/*
 *
 * bucket.c -- Traffic Squeezer Payload Coalescing bucket management code.
 *
 * Author: Kiran K (2006-2008)
 *         kiran_cyberpro at yahoo dot com
 *
 *
 * Traffic Squeezer is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the
 * Free Software Foundation; either version 2, or (at your option) any
 * later version.
 *
 * Traffic Squeezer is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with GNU Zebra; see the file COPYING.  If not, write to the Free
 * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
 * 02111-1307, USA.
 */

#include <stdio.h>
#include <malloc.h>
#include <string.h>
#include <sys/types.h>

#include "bucket.h"
#include "common.h"


#define BUCKET_ERROR_OVERFLOW 0x1
extern BYTEx2 bucket_size;

/* adds a fresh PDU into the new/existing bucket (used: Ingress Cycle) */
int add_bucket_PDU(IN unsigned char *in_buff, IN size_t in_buff_size, OUT ts_bucket_t *bucket)
{
	if( (bucket->size + in_buff_size) > bucket_size) // New Bucket is larger than MTU
	{
		return FALSE;
	}
	else{
		bucket->pdu_cnt++;
		if((bucket->pdu_cnt-1)==0)
			memcpy( bucket->payload, in_buff, in_buff_size);
		else
			memcpy( (bucket->payload + bucket->size), in_buff, in_buff_size);
		bucket->size += in_buff_size;
		bucket->pdu_size[bucket->pdu_cnt-1] = in_buff_size; //Copy the curr payload size
	}
        return TRUE;
}

/* gets the final Coalesced PDU from the bucket for Tx (used: Ingress Cycle) */
int get_bucket_PDU(IN ts_bucket_t *bucket, OUT unsigned char *out_buff, OUT size_t *out_buff_size)
{	
	memcpy(out_buff, bucket->payload, (size_t)bucket->size); //Dump the whole bucket in out_buff
	*out_buff_size = (size_t)bucket->size;
	


	{ int i, j;
	  for(i=(bucket->pdu_cnt-1), j=0; i>=0; i--, j+=2)
	  {	BYTEx2 temp_pdu_size = htons(bucket->pdu_size[i]);
		//now construct the bucket header at the end of the frame
		  //Here is the structure
		  // [2 bytes -> pdu-n offset]... [2 bytes -> pdu-2 offset][2 bytes -> pdu-1 offset] [1 byte -> pdu count]	
			memcpy((bucket->ts_bucket_hdr+j), &temp_pdu_size, 2); /* add each packet pos to the header starting from the last packet */
 			   /* as shown in the above template */	
	  }
	  bucket->ts_bucket_hdr[j] = (BYTE)bucket->pdu_cnt;	/* add the count to the end of the header */
	  memcpy( (out_buff+(*out_buff_size)), (bucket->ts_bucket_hdr), j+1 ); 	/* Add this bucket header to the newly constructed PDU */
	  *out_buff_size += j+1;
    	}

	return TRUE;
}

/* gets the pre-coalesced PDU and extracts multiple PDUs in a bucket (used: Egress Cycle) */
int get_bucket_PDUs(IN unsigned char *in_buff, IN size_t in_buff_size, OUT ts_bucket_t *bucket)
{	
       int in_buff_cur_pos = (in_buff_size-1); //Pointing to the last byte of the input buffer

        bucket->pdu_cnt = (BYTE)(*(in_buff+in_buff_cur_pos)); //Last byte of the input buffer is pdu_cnt
        in_buff_cur_pos--;

        //Extract the PDU lengths of each packet
        {       int i;
                for(i=0; i<(bucket->pdu_cnt) ; i++, in_buff_cur_pos-=2)
                {
                        bucket->pdu_size[i] = ((BYTEx2)( *(in_buff+in_buff_cur_pos)));
                }
        }
        bucket->size = in_buff_cur_pos+2; //Last pos of the input buffer cur pos is the size of the payload alone
        memcpy( bucket->payload, in_buff, bucket->size);


        return TRUE;
}

/* cleans the whole bucket contents */
int clean_bucket(INOUT ts_bucket_t *bucket)
{
	
	if(bucket==NULL)
		return FALSE;
	bucket->pdu_cnt=ZERO;              /* Count of number of PDUs in the bucket */
	bucket->size=ZERO;                 /* Bucket size */
	{  int i;
	   for(i=0;i<MAX_BUCKET_PDU;i++)
		bucket->pdu_size[i] = ZERO;
	}
        memset(bucket->payload, ZERO, ETH2_MAX_MTU);
	memset(bucket->ts_bucket_hdr, ZERO, MAX_BUCKET_HDR);
	return TRUE;
}




