/*
 * sb.c
 *
 * Derived from:
 *
 * Copyright (c) 1982, 1986 Regents of the University of California.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms are permitted
 * provided that this notice is preserved and that due credit is given
 * to the University of California at Berkeley. The name of the University
 * may not be used to endorse or promote products derived from this
 * software without specific prior written permission. This software
 * is provided ``as is'' without express or implied warranty.
 *
 * Modified for x-kernel v3.3
 * Modifications Copyright (c) 1991,1996  Arizona Board of Regents
 *
 * $Revision: 1.3 $
 * $Date: 1996/02/01 15:40:00 $
 */

/*
 * sb = send buffer ??
 */

#include "xkernel.h"
#include "insque.h"
#include "tcp_trace.h"
#include "sb.h"

extern int tracetcpp;
struct sb_i *sbifreelist = 0;


static void
print_sb(sb, m)
    struct sb *sb;
    char *m;
{
    struct sb_i *s;
    printf("%s sbLen == %d:", m, sb->len);
    for (s = sb->next; s != (struct sb_i *)sb; s = s->next) {
	printf("(%d)", msgLength(&s->m));
    }
    printf("\n");
}


void
sbappend(sb, m)
    struct sb *sb;
    Msg *m;
{
    struct sb_i *nsb;
    xTrace3(tcpp, 4, "sbappend on %lx olen %d msglen %d",
	    (u_long)sb, sb->len, msgLength(m));
    if (msgLength(m) == 0) {
	return;
    }
    sbinew(nsb);
    msgConstructCopy(&nsb->m, m);
    if (sb->len==0) {
	sb->next = nsb;
	nsb->prev = (struct sb_i *)sb;
    }
    else {
	nsb->prev = sb->prev;
	nsb->prev->next = nsb;
    }
    nsb->next = (struct sb_i *)sb;
    sb->prev = nsb;
    
    sb->len += msgLength(m);
    /*  insque(nsb, sb->next); */
    xIfTrace(tcpp, 4) print_sb(sb, "append");
}


/*
 * collect 'len' bytes at offset 'off' from send buffer 'sb' and put
 * them in msg 'm'.  'm' is assumed to be uninitialized.
 */
void
sbcollect(sb, m, off, len, delete)
    struct sb *sb;
    Msg *m;
    int off, len, delete;
{
    struct sb_i *s, *next;
    int time;
    
    xTrace5(tcpp, 4, "sbcollect on %lx olen %d len %d off %d %s",
	    (u_long)sb, sb->len, len, off, delete ? "delete" : "");
    xAssert(!delete || off == 0);
    if (len == 0) {
	msgConstructEmpty(m);
	return;
    }
    xIfTrace(tcpp, 5) print_sb(sb, "collect");
    for (s = sb->next;
	 s != (struct sb_i *)sb && off >= msgLength(&s->m);
	 s = s->next) 
      off -= msgLength(&s->m);
    xAssert(s != (struct sb_i *)sb);
    if (off > 0 && off < msgLength(&s->m)) {
	struct sb_i *ns;
	sbinew(ns);
	msgConstructEmpty(&ns->m);
	xTrace2(tcpp, 5, "sbcollect: split0 msg size %d at %d",
		msgLength(&s->m), off);
	msgBreak(&s->m, &ns->m, off);
        ns->sendTime = s->sendTime;
        ns->transmits = s->transmits;
	insque(ns, s);
    }
    if (msgLength(&s->m) > len) {
	struct sb_i *ns;
	sbinew(ns);
	msgConstructEmpty(&ns->m);
	xTrace2(tcpp, 5, "sbcollect: split1 msg size %d at %d",
		msgLength(&s->m), len);
	msgBreak(&s->m, &ns->m, len);
	insque(ns, s);
	s = ns;
    }
    /*
     * We now have s pointing to the first msg, collect the rest
     */
    
    time = tcpGetTime();
    s->sendTime = time;
    s->transmits++;
    
    xTrace1(tcpp, 5, "sbcollect: first piece has size %d", msgLength(&s->m));
    /* msg_save(new, s->m); */
    msgConstructCopy(m, &s->m);
    len -= msgLength(m);
    s = s->next;
    while (len > 0) {
	xAssert(s != (struct sb_i *) sb);
	next = s->next;
	if (msgLength(&s->m) > len) {
	    sbinew(next);
	    msgConstructCopy(&next->m, &s->m);
	    xTrace2(tcpp, 5, "sbcollect: split2 msg size %d at %d",
		    msgLength(&s->m), len);
	    msgBreak(&next->m, &s->m, len);
            next->sendTime = s->sendTime;
            next->transmits = s->transmits;
	    insque(next, s->next);
	}
	/* msg_save(s->m, s->m); */
	msgJoin(m, m, &s->m);
        s->sendTime = time; 
        s->transmits++; 
	len -= msgLength(&s->m);
	s = next;
    }
}


void
sbflush(sb)
    struct sb *sb;
{
    struct sb_i *s = sb->next, *next;
    
    xTrace2(tcpp, 4, "sbflush on %lx olen %d", (u_long)sb, sb->len);
    while (s != (struct sb_i *)sb) {
	next = s->next;
	xTrace1(tcpp, 5, "sbflush: freeing msg len %d", msgLength(&s->m));
	msgDestroy(&s->m);
	sbifree(s);
	s = next;
    }
    sb->len = 0;
}


int              
sbexpire(sb, time, maxseg, transmits, stime)
    struct sb *sb;
    int time, *transmits, *stime;
    int maxseg;
{       
    int len = 0;
    struct sb_i *s = sb->next, *next;
    
    xTrace1(tcpp, 4, "sbexpire  time %d", time);
                 
    if (s != (struct sb_i *)sb) {
        next = s->next;
        *transmits = s->transmits;
	*stime = s->sendTime;
        if ((s->sendTime) && (s->sendTime < time)) {
/* bmod          *stime = s->sendTime; */
          len = msgLength(&s->m);
          s = next;
          while ((s != (struct sb_i *)sb) &&
                 (len < maxseg) &&
                 (s->sendTime < time)) {
                next = s->next;
                len += msgLength(&s->m);
                s = next;
          }
        }
    }
    return len;
}   
    
    
void
sbdrop(sb, len, timestamp, transmits)
    struct sb *sb;
    int len;
    int *timestamp; 
    int *transmits;
{
  struct sb_i *s, *next;

  s = sb->next;
  *timestamp = 0;
  *transmits = 0;
  xTrace3(tcpp, 4, "sbdrop on %lx olen %d len %d", (u_long)sb, sb->len, len);
  xIfTrace(tcpp, 5) print_sb(sb, "drop before");
  while (s != (struct sb_i *)sb && len > 0) {
    if (len < msgLength(&s->m)) {
      struct sb_i *ns;
      sbinew(ns);
      msgConstructEmpty(&ns->m);
      ns->sendTime = s->sendTime;
      ns->transmits = s->transmits;
      xTrace2(tcpp, 5, "sbdrop: split msg size %d at %d", msgLength(&s->m),len);
      msgBreak(&s->m, &ns->m, len);
      insque(ns, s->next);
    }
    len -= msgLength(&s->m);
    sb->len -= msgLength(&s->m);
    next = s->next;
    if (s->sendTime > *timestamp ) *timestamp = s->sendTime;
    if (s->transmits > *transmits ) *transmits = s->transmits;
    remque(s);
    xTrace1(tcpp, 5, "sbdrop: freeing msg len %d", msgLength(&s->m));
    msgDestroy(&s->m);
    sbifree(s);
    s = next;
  }
  xIfTrace(tcpp, 5) print_sb(sb, "drop after");
}


void
sbdelete(sb)
    struct sb *sb;
{
  xTrace1(tcpp, 3, "sbdelete on %lx", (u_long)sb);
  sbflush(sb);
  xFree((char *)sb);
}
