sbuf.c 3.99 KB
Newer Older
bellard's avatar
bellard committed
1 2
/*
 * Copyright (c) 1995 Danny Gasparovski.
3 4
 *
 * Please read the file COPYRIGHT for the
bellard's avatar
bellard committed
5 6 7 8
 * terms and conditions of the copyright.
 */

#include <slirp.h>
9
#include <main-loop.h>
bellard's avatar
bellard committed
10

11 12
static void sbappendsb(struct sbuf *sb, struct mbuf *m);

bellard's avatar
bellard committed
13
void
14
sbfree(struct sbuf *sb)
bellard's avatar
bellard committed
15 16 17 18 19
{
	free(sb->sb_data);
}

void
20
sbdrop(struct sbuf *sb, int num)
bellard's avatar
bellard committed
21
{
22 23
    int limit = sb->sb_datalen / 2;

24
	/*
bellard's avatar
bellard committed
25
	 * We can only drop how much we have
26
	 * This should never succeed
bellard's avatar
bellard committed
27 28 29 30 31 32 33
	 */
	if(num > sb->sb_cc)
		num = sb->sb_cc;
	sb->sb_cc -= num;
	sb->sb_rptr += num;
	if(sb->sb_rptr >= sb->sb_data + sb->sb_datalen)
		sb->sb_rptr -= sb->sb_datalen;
34

35 36 37
    if (sb->sb_cc < limit && sb->sb_cc + num >= limit) {
        qemu_notify_event();
    }
bellard's avatar
bellard committed
38 39 40
}

void
41
sbreserve(struct sbuf *sb, int size)
bellard's avatar
bellard committed
42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69
{
	if (sb->sb_data) {
		/* Already alloced, realloc if necessary */
		if (sb->sb_datalen != size) {
			sb->sb_wptr = sb->sb_rptr = sb->sb_data = (char *)realloc(sb->sb_data, size);
			sb->sb_cc = 0;
			if (sb->sb_wptr)
			   sb->sb_datalen = size;
			else
			   sb->sb_datalen = 0;
		}
	} else {
		sb->sb_wptr = sb->sb_rptr = sb->sb_data = (char *)malloc(size);
		sb->sb_cc = 0;
		if (sb->sb_wptr)
		   sb->sb_datalen = size;
		else
		   sb->sb_datalen = 0;
	}
}

/*
 * Try and write() to the socket, whatever doesn't get written
 * append to the buffer... for a host with a fast net connection,
 * this prevents an unnecessary copy of the data
 * (the socket is non-blocking, so we won't hang)
 */
void
70
sbappend(struct socket *so, struct mbuf *m)
bellard's avatar
bellard committed
71 72
{
	int ret = 0;
73

bellard's avatar
bellard committed
74 75 76 77
	DEBUG_CALL("sbappend");
	DEBUG_ARG("so = %lx", (long)so);
	DEBUG_ARG("m = %lx", (long)m);
	DEBUG_ARG("m->m_len = %d", m->m_len);
78

bellard's avatar
bellard committed
79 80 81 82 83
	/* Shouldn't happen, but...  e.g. foreign host closes connection */
	if (m->m_len <= 0) {
		m_free(m);
		return;
	}
84

bellard's avatar
bellard committed
85 86 87 88 89 90 91 92 93 94 95
	/*
	 * If there is urgent data, call sosendoob
	 * if not all was sent, sowrite will take care of the rest
	 * (The rest of this function is just an optimisation)
	 */
	if (so->so_urgc) {
		sbappendsb(&so->so_rcv, m);
		m_free(m);
		sosendoob(so);
		return;
	}
96

bellard's avatar
bellard committed
97 98 99 100 101
	/*
	 * We only write if there's nothing in the buffer,
	 * ottherwise it'll arrive out of order, and hence corrupt
	 */
	if (!so->so_rcv.sb_cc)
102
	   ret = slirp_send(so, m->m_data, m->m_len, 0);
103

bellard's avatar
bellard committed
104
	if (ret <= 0) {
105
		/*
bellard's avatar
bellard committed
106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128
		 * Nothing was written
		 * It's possible that the socket has closed, but
		 * we don't need to check because if it has closed,
		 * it will be detected in the normal way by soread()
		 */
		sbappendsb(&so->so_rcv, m);
	} else if (ret != m->m_len) {
		/*
		 * Something was written, but not everything..
		 * sbappendsb the rest
		 */
		m->m_len -= ret;
		m->m_data += ret;
		sbappendsb(&so->so_rcv, m);
	} /* else */
	/* Whatever happened, we free the mbuf */
	m_free(m);
}

/*
 * Copy the data from m into sb
 * The caller is responsible to make sure there's enough room
 */
129 130
static void
sbappendsb(struct sbuf *sb, struct mbuf *m)
bellard's avatar
bellard committed
131 132
{
	int len, n,  nn;
133

bellard's avatar
bellard committed
134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166
	len = m->m_len;

	if (sb->sb_wptr < sb->sb_rptr) {
		n = sb->sb_rptr - sb->sb_wptr;
		if (n > len) n = len;
		memcpy(sb->sb_wptr, m->m_data, n);
	} else {
		/* Do the right edge first */
		n = sb->sb_data + sb->sb_datalen - sb->sb_wptr;
		if (n > len) n = len;
		memcpy(sb->sb_wptr, m->m_data, n);
		len -= n;
		if (len) {
			/* Now the left edge */
			nn = sb->sb_rptr - sb->sb_data;
			if (nn > len) nn = len;
			memcpy(sb->sb_data,m->m_data+n,nn);
			n += nn;
		}
	}

	sb->sb_cc += n;
	sb->sb_wptr += n;
	if (sb->sb_wptr >= sb->sb_data + sb->sb_datalen)
		sb->sb_wptr -= sb->sb_datalen;
}

/*
 * Copy data from sbuf to a normal, straight buffer
 * Don't update the sbuf rptr, this will be
 * done in sbdrop when the data is acked
 */
void
167
sbcopy(struct sbuf *sb, int off, int len, char *to)
bellard's avatar
bellard committed
168 169
{
	char *from;
170

bellard's avatar
bellard committed
171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187
	from = sb->sb_rptr + off;
	if (from >= sb->sb_data + sb->sb_datalen)
		from -= sb->sb_datalen;

	if (from < sb->sb_wptr) {
		if (len > sb->sb_cc) len = sb->sb_cc;
		memcpy(to,from,len);
	} else {
		/* re-use off */
		off = (sb->sb_data + sb->sb_datalen) - from;
		if (off > len) off = len;
		memcpy(to,from,off);
		len -= off;
		if (len)
		   memcpy(to+off,sb->sb_data,len);
	}
}