/*
    Copyright 2000 (c) by David Schweikert,
    Swiss Federal Institute of Technology,
    Computer Engineering and Networks Laboratory.
 
    TOPSY -- A Teachable Operating System.
             Implementation of a tiny and simple
             micro kernel for teaching purposes.
 
    For further information, please visit http://www.tik.ee.ethz.ch/~topsy
 
    This software is provided under the terms of the GNU General Public 
    Licence. A full copy of the GNU GPL is provided in the file COPYING 
    found in the development root of Topsy.
*/
/*
    File:                  $Source: /home/ruf/cvs_Topsy/cvs/Topsy/Net/NetBuf.c,v $
    Affiliation:           ETH Zuerich, TIK
    Version:               $Revision: 2936 $
    Last Date of Change:   $Date: 2007-03-21 15:23:42 +0100 (Mit, 21 Mär 2007) $  by: $Author: baderma $
 
*/
#include <Limits.h>
#include <Topsy.h>
#include <Lock.h>
#include <NetBuf.h>
#include <NetSyscall.h>
#include <NetDebug.h>
#include <UserSupport.h>


#define NETBUF_BUFSIZE(s)        (stats.sizes[s])
#define NETBUF_POOLFULLMASK(s) (ULONG_MAX >> (NETBUF_MAXBUFS(0)-NETBUF_MAXBUFS(s)))

static LockDesc netbuflock;
static NetBufStats stats;
static NetBufsPool freepools[NETBUF_SIZES];

NetBufStats *netbufStats = &stats;

/** netbufInit: Initialize netbuf manager.
 */
void netbufInit()
{
  int i = 0;

  lockInit(&netbuflock);
  for(i=0; i<NETBUF_SIZES; i++)
  {
    freepools[i] = 0;
    stats.sizes[i] = (NETBUF_POOLSIZE - sizeof(NetBufsPoolHdr)) /
                     (1 << (NETBUF_SIZES-1-i));
    stats.sizes[i] -= stats.sizes[i]%ALIGN;
    stats.pools[i] = 0;
    stats.usage[i] = 0;
  }

  netdbgDisplay(NETDEBUG_NETBUFS2, "NetBufs initialized.\n");
}

inline static int netbufSizeNr(unsigned int size)
{
  int i = 0;

  for(i=0; i<NETBUF_SIZES; i++)
  {
    if(NETBUF_BUFSIZE(i) >= size)
      return i;
  }

  return NETBUF_SIZES-1;
}


/** netbufAlloc: Allocates a new netbuf of at least payload-size 'size'.
 *               If it is not possible, a linked list of smaller netbufs 
 *               is returned.
 */
int netbufAlloc(NetBuf *bufPtr, unsigned int size)
{
  int s = netbufSizeNr(size + sizeof(NetBufHdr));
  NetBufsPool pool;
  NetBuf buf;
  unsigned long u,totalsize;
  unsigned short bufnr, datasize;

  netdbgPrintf(NETDEBUG_NETBUFS2, "NetBuf: New buffer of size %d requested.\n",
               NETBUF_BUFSIZE(s));

  /* GET LOCK */
  lock(&netbuflock);

  netdbgDisplay(NETDEBUG_NETBUFS2, "NetBuf: got lock.\n");

  /* search for a free buf */
  pool = freepools[s];

  if(!pool)
  { /* no free buf in pool found... */
    netdbgDisplay(NETDEBUG_NETBUFS2, "NetBuf: sending VM_ALLOC.\n");
    /* alloc a new pool */
    if(netAlloc((Address *) &pool, NETBUF_POOLSIZE) != VM_ALLOCOK)
    {
      unlock(&netbuflock);
      netdbgDisplay(NETDEBUG_GENERIC,
                    "NetBuf: Couldn't get new pool!!\n");
      return 0;
    }
    netdbgPrintf(NETDEBUG_NETBUFS2, "NetBuf: new pool at %p\n", pool);

    /* init pool */
    pool->next = 0;
    pool->prev = 0;
    pool->used = 0;
    pool->sizenr = s;
    freepools[s] = pool;
    stats.pools[s]++;

    netdbgPrintf(NETDEBUG_NETBUFS2,
                 "NetBuf: New pool allocated for size %d (%p)\n",
                 NETBUF_BUFSIZE(s), pool);
  }

  /* get first unused buf */
  for(u=pool->used, bufnr=0; u % 2; u >>= 1)
    bufnr++;

  /* mark as used */
  pool->used |= 1UL << bufnr;

  /* if full remove from freepools */
  if(pool->used == NETBUF_POOLFULLMASK(s))
  {
    netdbgPrintf(NETDEBUG_NETBUFS2,
                 "NetBuf: Pool %p is full. Removing from freepools.\n",pool);
    if(pool->prev)
      pool->prev->next = pool->next;
    else
      freepools[s]     = pool->next;
    if(pool->next)
      pool->next->prev = pool->prev;
  }

  /* increment statistics counter */
  stats.usage[s]++;

  /* FREE LOCK */
  unlock(&netbuflock);
  netdbgDisplay(NETDEBUG_NETBUFS2, "NetBuf: lock freed.\n");

  /* Warning: pointer arithmetic! */
  buf = (NetBuf) ((char *) pool + sizeof(NetBufsPoolHdr)+
                  bufnr*NETBUF_BUFSIZE(s));
  *bufPtr = buf;
  datasize = NETBUF_BUFSIZE(s) - sizeof(NetBufHdr);

  /* init buf */
  buf->pool = pool;
  buf->bufnr  = bufnr;
  buf->size   = datasize;
  buf->start  = sizeof(NetBufHdr);
  buf->end    = sizeof(NetBufHdr);

  netdbgPrintf(NETDEBUG_NETBUFS2,
               "NetBuf: New buffer (%p): pool=%p, bufnr=%d, size=%d, start=%p, end=%p\n",
               buf, buf->pool, buf->bufnr, buf->size, buf->start, buf->end);

  totalsize=datasize;
  /* alloc further NetBufs if needed */
  if(datasize < size)
  {
    if(!(datasize=netbufAlloc(&(buf->next), size-datasize)))
      return 0;
    totalsize+=datasize;
  }
  else
  {
    buf->next = 0;
  }

  return totalsize;
}


static void netbufFreeBuf(NetBuf buf) /* no lock !!! Use netbufFree */
{
  unsigned short s;
  int wasfull=0;
  NetBufsPool pool = buf->pool;

  netdbgPrintf(NETDEBUG_NETBUFS2,
               "NetBuf: Freeing buffer %p in pool %p (prev=%p, next=%p).\n",
               buf, pool,pool->prev,pool->next);

  s = pool->sizenr;

  lock(&netbuflock);

  if(pool->used == NETBUF_POOLFULLMASK(s))
    wasfull=1;

  if(!(pool->used & (1UL<<buf->bufnr)))
  {
    netdbgPrintf(NETDEBUG_GENERIC, "NetBuf: Freeing already freed buffer!\n");
  }

  //pool->used &= ~(1UL << (buf->bufnr));
  pool->used ^= 1UL << buf->bufnr;
  stats.usage[s]--;

  if(pool->used == 0 && freepools[s] && freepools[s]->next)
  { /* threshold of 1 */
    if(!wasfull)
    {
      /* remove from freepools */
      if(pool->prev)
        pool->prev->next = pool->next;
      else
        freepools[s]     = pool->next;
      if(pool->next)
        pool->next->prev = pool->prev;
    }

    netdbgPrintf(NETDEBUG_NETBUFS2, "NetBuf: Freeing pool %p.\n", pool);

    netFree(pool);
    stats.pools[s]--;
  }
  else
  {
    if(wasfull)
    {
      /* add to freepools */
      if(freepools[s])
        freepools[s]->prev=pool;
      pool->next=freepools[s];
      pool->prev=0;
      freepools[s]=pool;
    }
  }

  unlock(&netbuflock);
}


/** netbufFreeBuf for the whole linked list
 */
void netbufFree(NetBuf buf)
{
  NetBuf next;

  while(buf)
  {
    next=buf->next;
    netbufFreeBuf(buf);
    buf=next;
  }
}



/*************** UTILITIES ******************/


/** netbufAddHeadSpace: Controls if there is enough space left before
 *                 buf->start (mininum: len) and if not, adds another 
 *                 netbuf to the netbuf-list (to the head) of size len.
 *                 If there is enough space, the buf->start index is decreased
 *                 accordingly.
 */
int  netbufAddHeadSpace(NetBuf *bufPtr, unsigned int len)
{
  NetBuf newbuf;

  if (! *bufPtr )
  {
    netdbgDisplay(NETDEBUG_NETBUFS,
                  "NetBuf: netbufAddHeadSpace: Allocating new NetBuf\n");
    return netbufAlloc(bufPtr, len)?1:0;
  }
  /* if there isn't enough space, add a netbuf to the head */
  if(NETBUF_HEADSPACE(*bufPtr) < len)
  {
    if(!netbufAlloc(&newbuf, len))
      return 0;

    newbuf->next  = *bufPtr;
    newbuf->end   = NETBUF_HEADERSIZE+newbuf->size;
    newbuf->start = newbuf->end-len;
    *bufPtr = newbuf;

    netdbgDisplay(NETDEBUG_NETBUFS,
                  "NetBuf: netbufAddHeadSpace: Added new buf at head of list.\n");
    /* netdbgPrintf(NETDEBUG_GENERIC,
     *            "NetBuf: *bufPtr=%p, next=%p, end=%d, start=%d\n", 
     *            *bufPtr, newbuf->next, newbuf->end, newbuf->start);
     */
  }
  else
  {
    netdbgDisplay(NETDEBUG_NETBUFS,
                  "NetBuf: netbufAddHeadSpace: resizing head space\n");
    /* netdbgPrintf(NETDEBUG_GENERIC, "Adding header: start (before) = %d", (*bufPtr)->start); */
    (*bufPtr)->start -= len;
    /* netdbgPrintf(NETDEBUG_GENERIC, "Adding header: start (after)  = %d", (*bufPtr)->start); */
  }

  return 1;
}


/** netbufAddTailSpace: Controls if there is enough space at the end of bufPtr
 *                 (mininum: len) and if not, adds another netbuf to the 
 *                 netbuf-list (to the end) of size len.
 *                 If there is enough space, the buf->end index is increased
 *                 accordingly.
 */
int  netbufAddTailSpace(NetBuf *bufPtr, unsigned int len)
{
  NetBuf newbuf, buf = *bufPtr;

  if (! *bufPtr )
  {
    netdbgDisplay(NETDEBUG_NETBUFS,
                  "NetBuf: netbufAddTailSpace: Allocating new NetBuf\n");
    return netbufAlloc(bufPtr, len)?1:0;
  }

  for(;buf->next;buf=buf->next)
    ;
  if( NETBUF_TAILSPACE(buf) < len )
  {
    if(!netbufAlloc(&newbuf, len))
      return 0;

    buf->next     = newbuf;
    for(;newbuf->next != NULL; newbuf=newbuf->next)
    {
      newbuf->end = NETBUF_TAILSPACE(newbuf);
      len -= NETBUF_LEN(newbuf);
    }
    newbuf->end   = newbuf->start + len;
    netdbgDisplay(NETDEBUG_NETBUFS,
                  "NetBuf: netbufAddTailSpace: Added new buf at end of list.\n");
  }
  else
  {
    netdbgPrintf(NETDEBUG_NETBUFS,
                 "NetBuf: netbufAddTailSpace: resizing tail space (%i / %i)\n",
                 NETBUF_TAILSPACE(buf), len);
    buf->end += len;
  }

  return 1;
}


/** netbufAddHead: add NetBuf buf to the head of Netbuf bufPtr
 */
int netbufAddHead(NetBuf *bufPtr, NetBuf buf)
{
  NetBuf head = buf;

  for(;buf->next;buf=buf->next)
    ;
  buf->next = *bufPtr;
  *bufPtr = head;

  return 1;
}


/** netbufAddTail: add NetBuf buf to the tail of Netbuf bufPtr
 */
int netbufAddTail(NetBuf *bufPtr, NetBuf buf)
{
  NetBuf tail = *bufPtr;

  if ( tail == NULL )
  {
    *bufPtr = buf;
    return 1;
  }
  for(;tail->next;tail=tail->next)
    ;
  tail->next = buf;

  return 1;
}


/** netbufLen: Returns the total length of the netbuf-list
 */
unsigned int netbufLen(NetBuf buf)
{
  unsigned int len=0;
  for(;buf;buf=buf->next)
    len+=NETBUF_LEN(buf);
  return len;
}


/** netbufTrim: If total length of the netbuf-list is bigger than 'len',
 *              removes bytes at the end, so that they are equal.
 */
void netbufTrim(NetBuf buf, unsigned int len)
{
  unsigned int tlen = 0, blen = 0;

  for(;buf;buf=buf->next)
  {
    blen=NETBUF_LEN(buf);

    if(tlen+blen>len)
    {
      buf->end = buf->start + (len - tlen);
      if(buf->next)
      {
        netbufFree(buf->next);
        buf->next=0;
      }
      return;
    }

    tlen+=blen;
  }
  return;
}


/** netbufPosition: 'position' refers to the absolute byte position in a
 *               virtual big buffer which is in reality a linked list of 
 *               netbufs. After the function-call, buf points to the netbuf
 *               which contains that byte, and position is adjusted to the
 *               offset in that netbuf. Returns 1 on success.
 */
int netbufPosition(NetBuf *buf, unsigned int *position)
{
  for(;*buf;*buf=(*buf)->next)
  {
    if(NETBUF_LEN(*buf)>*position)
      return 1;
    *position -= NETBUF_LEN(*buf);
  }
  netdbgPrintf(NETDEBUG_NETBUFS, "NetBuf: netbufPosition: failed\n");
  return 0;
}


/** netbufClone: returns an exact copy of buf.
 */
int netbufClone(NetBuf *dest, NetBuf src)
{
  NetBuf tmp;

  if(!src)
    return 0;

  /* Copy the first */
  if(!netbufAlloc(dest,NETBUF_SIZE(src)))
    return 0;
  tmp = *dest;
  tmp->start=src->start;
  tmp->end  =src->end;
  byteCopy(NETBUF_DATA(tmp),NETBUF_DATA(src),NETBUF_LEN(src));
  src = src->next;

  /* Copy the following */
  while(src)
  {
    if(!netbufAlloc(&(tmp->next),NETBUF_SIZE(src)))
    {
      netbufFree(*dest);
      return 0;
    }
    tmp = tmp->next;
    tmp->start=src->start;
    tmp->end  =src->end;
    byteCopy(NETBUF_DATA(tmp),NETBUF_DATA(src),NETBUF_LEN(src));

    src=src->next;
  }

  return 1;
}


/** netbufCopy: returns a copy of buf from pos to pos+len
 */
int netbufCopy(NetBuf *dest, NetBuf src, int pos, int len)
{
  NetBuf dst;
  int totalsize = 0, dstpos = 0;

  /* Alloc buffer */
  totalsize = netbufAlloc(dest,len);
  if(!totalsize)
    return 0;

  dst = *dest;

  /* Leave space at the beginning */
  dst->start = NETBUF_HEADERSIZE+(totalsize-len);

  /* Search for pos in the src-linkedlist */
  if(!netbufPosition(&src,&pos))
    return 0;

  while(1)
  {
    int clen = 0,tmp = 0,whatnext = 0;

    /* minimize between NETBUF_LEN(src), len and
     * NETBUF_SIZE(dst) */
    clen = NETBUF_LEN(src)-pos;
    if(clen>=len)
    {
      clen=len;
      whatnext=0; /* finished copying */
    }
    else
      whatnext=1; /* next src */

    tmp = NETBUF_SIZE(src)-dstpos;
    if(clen>tmp)
    {
      clen=tmp;
      whatnext=2; /* next dst */
    }

    if(tmp<0)
    {
      /* we have a problem... */
      goto error;
    }

    /* Set dst->end */
    dst->end = dst->start+dstpos+clen;

    /* Do the copy */
    byteCopy(NETBUF_DATA(dst)+dstpos, NETBUF_DATA(src)+pos, clen);

    switch(whatnext)
    {
    case 0:
      return 1;
      break;
    case 1:
      if(!src->next)
        goto error;
      src = src->next;
      dstpos = clen;
      break;
    case 2:
      if(!dst->next)
        goto error;
      dst = dst->next;
      dst->start = NETBUF_HEADERSIZE;
      pos = clen;
      break;
    }
  }

error:
  netbufFree(*dest);
  return 0;
}


/** netbufChop: remove len bytes from dest. If necessary free empty NetBufs.
 *              Via options you can choose where to remove the data.
 */
int netbufChop(NetBuf *dest, unsigned int len, int options)
{
  NetBuf buf = *dest, prev;
  unsigned int size = 0;

  switch (options)
  {
  case FROM_END:
    size = netbufLen(buf);
    if ( len > size )
      len = size;
    size -= len;
    if (! netbufPosition(&buf, &size) )
      return 0;
    if ( size == 0 )
    {
      netbufFree(buf);
      buf = NULL;
      return 1;
    }
    netbufFree(buf->next); /* remove the rest */
    buf->next = 0;
    buf->end = buf->start + size;
    return 1;
  case FROM_START:
    size = 0;
    prev = buf;
    while ( buf && NETBUF_LEN(buf) <= len )
    {
      prev = buf;
      len -= NETBUF_LEN(buf);
      buf = buf->next;
    }
    if ( buf )
      buf->start += len;
    prev->next = 0;
    netbufFree(*dest);
    *dest = buf;
    return 1;
  default:
    return 0;
  }
}

/** netbufZero:  fill NetBuf with len bytes of zeros form offset.
 *               offset may not be greater then netbufLen(*dest)
 */
int netbufZero(NetBuf *dest, unsigned int len, unsigned int offset)
{
  NetBuf buf = *dest;
  unsigned int t = 0;

  netdbgPrintf(NETDEBUG_NETBUFS,
               "NetBuf: netbufZero len %i offset %i\n",
               len, offset);
  if (! netbufPosition(&buf, &offset) )
    return 0; /* XXX offset > size */
  while ( len > 0 )
  {
    t = NETBUF_LEN(buf)-offset;
    if ( t > len )
      t = len;
    netdbgPrintf(NETDEBUG_NETBUFS,
                 "NetBuf: netbufZero wiping form %x[%i]=%x size %i\n",
                 NETBUF_DATA(buf), offset, NETBUF_DATA(buf) + offset, t);
    zeroOut(NETBUF_DATA(buf) + offset , t);
    len -= t;
    offset = 0; /* only the first time we have an offset */
    if (len != 0 && buf->next != NULL )
    {
      if(! netbufAlloc(&(buf->next), len) )
      {
        /* some data may have been written */
        return 0;
      }
    }
    buf = buf->next;
  }
  return 1;
}


/** netBufWrite: write len bytes of linear data from databuf into NetBuf at
 *               position offset. If necessary add more NetBufs. 
 *               Via whence its possible to change the use of offset.
 *               If whence is SEEK_SET, the offset is set to offset bytes.
 *               If whence is SEEK_END, the offset is set to the size of the
 *               NetBuf plus offset bytes
 *               offset can be negative. If necessary pad zeros will be added.
 */
int netbufWrite(NetBuf *dest, char *databuf, unsigned int len,
                int offset, int whence)
{
  NetBuf buf;
  unsigned int size = 0;

  size = netbufLen(*dest);
  switch ( whence )
  {
  case SEEK_SET:
    /* first seek */
    netdbgPrintf(NETDEBUG_NETBUFS,
                 "NetBuf: netbufWrite SEEK_SET offset %i len %i size %i\n",
                 offset, len, size);
    if ( size == 0 )
    {
      if (! netbufAlloc(dest, len) )
      {
        return 0;
      }
      netdbgPrintf(NETDEBUG_NETBUFS,
                   "NetBuf: netbufAlloc new buf at %p len %i\n",
                   *dest, len);
    }
    if ( offset < 0 )
    {
      if (! netbufAddHeadSpace(dest, -offset) )
        return 0;
      offset = 0;
    }
    else if ( offset > size )
    {
      netdbgPrintf(NETDEBUG_NETBUFS,
                   "NetBuf: netbufWrite zeroOut offset %i size %i\n",
                   offset, size);
      if (! netbufZero(dest, offset-size, size) )
        return 0;
    }
    buf = *dest;
    netdbgPrintf(NETDEBUG_NETBUFS,
                 "NetBuf: netbufWrite SEEK_SET to offset %i size %i\n",
                 offset, size);

    if ( size != 0 )
      if (! netbufPosition(&buf, &offset) )
        return 0;
    break;
  case SEEK_END:
    /* first seek */
    netdbgPrintf(NETDEBUG_NETBUFS,
                 "NetBuf: netbufWrite SEEK_END offset %i len %i size %i\n",
                 offset, len, size);
    if ( offset > 0 )
    { /* add at end */
      /* pad with zeros */
      netdbgPrintf(NETDEBUG_NETBUFS,
                   "NetBuf: netbufWrite zeroOut offset %i size %i\n",
                   offset, size);
      if ( netbufZero(dest, offset, size) )
        return 0;
      size += offset;
    }
    else if ( size < -offset )
    { /* add at head */
      if (! netbufAddHeadSpace(dest, offset-size) )
        return 0;
      offset = 0;
    }
    else
    { /* add somewhere between */
      if ( size == 0 )
      {
        if (! netbufAlloc(dest, len) )
        {
          return 0;
        }
      }
      else
        size -= offset;
    }
    netdbgPrintf(NETDEBUG_NETBUFS,
                 "NetBuf: netbufWrite SEEK_END to offset %i\n",
                 size);
    if ( size != 0 )
      if (!netbufPosition(&buf, &size) )
        return 0;
    offset = size;
    break;
  default:
    return 0;
  }
  /* then write the data */

  /*-CJ	{
  		unsigned int i = len, j = 0;
  		unsigned char* c = databuf;
  		
  		netdbgPrintf(0, "NetBuf: netbufWrite databuf size %i\n", len);
  		while ( i ) {
  			netdbgPrintf(0, "%02x ", (int) *c);
  			if (! (++j % 8) ) netdbgPrintf(0, "\n");
  			i--;
  			c++;
  		}
  		netdbgPrintf(0, "\n");
  	}
  */
  while ( len > 0 )
  {
    size = NETBUF_LEN(buf)?NETBUF_LEN(buf)-offset:NETBUF_SIZE(buf);
    if ( size > len )
      size = len;
    byteCopy(NETBUF_DATA(buf) + offset, databuf, size);
    len -= size;
    databuf += size; /* seek a bit */
    NETBUF_END(buf) = NETBUF_START(buf) + offset + size;

    /*-CJ		netdbgPrintf(0,
    				"NetBuf: netbufWrite data written to %p len %i\n",
    				NETBUF_DATA(buf) + offset, size);
    		netdbgDumpBuf(NETDEBUG_GENERIC, buf);
    */
    offset = 0; /* only the first time we have an offset */
    if (! buf->next && len > 0 )
    {
      if(! netbufAlloc(&(buf->next), len) )
      {
        /* some data may have been written */
        return 0;
      }
    }
    buf = buf->next;
  }

  return 1;
}


/** netBufRead:  read len bytes of data from NetBuf at position offset and
 *               write it into the linear buffer databuf
 *               Via options its possible to change the use of offset.
 *               If whence is SEEK_SET, the offset is set to offset bytes.
 *               If whence is SEEK_END, the offset is set to the size of the
 *               NetBuf minus offset bytes
 */
int netbufRead(NetBuf *src, char *databuf, unsigned int *len,
               unsigned int offset, int whence)
{
  NetBuf buf;
  unsigned int size = 0, t = 0;

  if (len == NULL)
  {
    return 1;
  }
  size = netbufLen(*src);
  switch (whence)
  {
  case SEEK_SET:
    netdbgPrintf(NETDEBUG_NETBUFS,
                 "NetBuf: netbufRead SEEK_SET offset %i len %u\n",
                 offset, *len);
    buf = *src;
    if (! netbufPosition(&buf, &offset) )
      return 0;
    break;
  case SEEK_END:
    netdbgPrintf(NETDEBUG_NETBUFS,
                 "NetBuf: netbufRead SEEK_END offset %i len %i size %i\n",
                 offset, *len, size);
    if ( size < offset )
      return 0;
    offset = size - offset;
    if (! netbufPosition(&buf, &offset) )
      return 0;
    break;
  default:
    return 0;
  }
  /*	netdbgPrintf(0,
  			"NetBuf: netbufRead data offset %i len %i copying to %p\n",
  			offset, size, databuf);
  	netdbgDumpBuf(NETDEBUG_GENERIC, buf);
  */
  /* read the data */
  t = *len;
  *len = 0;
  while ( t > 0 )
  {
    size = NETBUF_LEN(buf)-offset;
    if ( size > t )
      size = t;

    byteCopy(databuf, NETBUF_DATA(buf) + offset, size);
    t -= size;
    databuf += size;
    *len += size; /* seek a bit */
    offset = 0; /* only the first time we have an offset */
    /*	{
    		unsigned int i = *len, j = 0;
    		unsigned char* c = databuf - size;
    		
    		netdbgPrintf(0, "NetBuf: netbufRead databuf size %i\n", i);
    		while ( i ) {
    			netdbgPrintf(0, "%02x ", (int) *c);
    			if (! (++j % 8) ) netdbgPrintf(0, "\n");
    			i--;
    			c++;
    		}
    		netdbgPrintf(0, "\n");
    	} */
    if (! buf->next )
    {
      /* no more data */
      /* this is not an error so let's return 1 */
      return 1;
    }
    buf = buf->next;
  }
  return 1;

}


/** netbufStatistics: displays internal info about netbufs
 */
void netbufStatistics()
{
  int	n;
  netdbgPrintf(NETDEBUG_TCP,"%s","size\t\tpools\tused\tfree\tmemory\n");
  for(n=0; n<NETBUF_SIZES; n++)
  {
    netdbgPrintf(NETDEBUG_TCP, " %d\t\t%d\t\t%d\t\t%d\t\t%dk\n",
                 netbufStats->sizes[n],
                 netbufStats->pools[n],
                 netbufStats->usage[n],
                 netbufStats->pools[n]*NETBUF_MAXBUFS(n) - netbufStats->usage[n],
                 NETBUF_POOLSIZE*netbufStats->pools[n]/1024);
  }
}
