]>
Commit | Line | Data |
---|---|---|
1 | ||
2 | linebuf - a dbuf replacement for the New World Order(tm) | |
3 | ||
4 | By Adrian Chadd <adrian@creative.net.au> | |
5 | ||
6 | $Id: linebuf.txt 6 2005-09-10 01:02:21Z nenolod $ | |
7 | ||
8 | ||
9 | History | |
10 | ------- | |
11 | ||
12 | I could probably learn the dbuf history, but basically its evil. The | |
13 | general idea is that a dbuf holds incoming and outgoing data streams. | |
14 | The trouble is that well.. it was evil. You can check it out by getting | |
15 | the old src/dbuf.c and include/dbuf.h files if you really want. | |
16 | ||
17 | ||
18 | Replacement | |
19 | ----------- | |
20 | ||
21 | The linebuf system is a replacement for the dbuf code. The general idea here | |
22 | is that the data should be buffered in "lines" rather than just linearly | |
23 | like in the dbuf code. This lends to easier manipulation at a later date | |
24 | (think flushing data lines to a socket, and even "sharing" linebufs to | |
25 | reduce the copying required for one to many delivery.) | |
26 | ||
27 | The linebuf system is broken into two structures, the buf_head and buf_line . | |
28 | buf_head contains the buffer information (queue head/tail, length, allocated | |
29 | length and the write offset for flushing), and buf_line contains the | |
30 | line buffer information (buffer and various flags.) | |
31 | ||
32 | linebuf->terminated is *only* set when a CR/LF combination is received. | |
33 | ||
34 | linebuf->overflow is set if we get more data than we should, and we simply | |
35 | truncate the incoming data. | |
36 | ||
37 | linebuf->flushing is set when we are currently writing the buffer. We should | |
38 | _NEVER_ be appending to a buffer which we're flushing! | |
39 | ||
40 | When you get a buffer through linebuf_get() or write one through | |
41 | linebuf_flush(), it will *always* be terminated with a CR/LF (and a NUL if | |
42 | its a linebuf_get()). | |
43 | ||
44 | ||
45 | Linebuf manipulation | |
46 | -------------------- | |
47 | ||
48 | To use a linebuf, you simply stick a buf_head_t in your structure somewhere. | |
49 | You then use the following routines: | |
50 | ||
51 | int | |
52 | linebuf_parse(buf_head_t *bufhead, char *buf, int len) | |
53 | ||
54 | Parse the given buf. This routine does some complex manipulation: | |
55 | ||
56 | - if there is an incomplete buffer at the tail, buf is parsed to try and | |
57 | fill that incomplete buffer | |
58 | - a buffer is completed by a CR/LF/CRLF/LFCR. It accepts everything purely | |
59 | because I wanted to be "liberal in what you accept" .. | |
60 | - If a buffer is terminated, the linebuf is flagged terminated | |
61 | - If more data is trying to be squeezed into the buffer than space LEFT | |
62 | in the buffer, we skip to the next "CRLF", and tag the buffer terminated | |
63 | _and_ overflowed. | |
64 | - We treat multiple runs of CR/LF/CRLF/LFCR as a single CRLF. This is just | |
65 | a little extra goody to stop people sending hundreds of "CRLF"s and creating | |
66 | unnecessary buffers. | |
67 | - The number of lines parsed is returned (so you can implement per-line flood | |
68 | protection ..) | |
69 | ||
70 | ||
71 | void | |
72 | linebuf_put(buf_head_t *bufhead, char *buf, int len) | |
73 | ||
74 | Parse the given buf, ASSUMING it is a single buffer line. This is useful | |
75 | for server-generated messages where you know you have a single line, and | |
76 | you don't want to go through the overhead of parsing the data just for | |
77 | this. | |
78 | ||
79 | ||
80 | int | |
81 | linebuf_get(buf_head_t *bufhead, char *buf, int maxlen) | |
82 | ||
83 | Get a single line from the buffer. This removes data from the head of the | |
84 | buffer. If the first buffer is empty or is not terminated, 0 is returned | |
85 | which indicates that there is no data to parse. Terminated buffers are | |
86 | returned (CR/LF/NUL), and the length INCLUDING the CR/LF/NUL is returned. | |
87 | The buffer is copied and the linebuf is then deallocated. | |
88 | ||
89 | ||
90 | int | |
91 | linebuf_flush(int fd, buf_head_t *bufhead) | |
92 | ||
93 | Attempt to flush some data to the given socket. bufhead->writeofs tracks | |
94 | where in the head buffer we currently are. If the buffer is not terminated, | |
95 | -1 is returned with errno == EWOULDBLOCK to simulate a "retry me" condition. | |
96 | (See TODO..) | |
97 | ||
98 | linebuf_flush() returns whatever write() returns, and sets (ie doesn't touch | |
99 | after write()) errno accordingly. | |
100 | ||
101 | ||
102 | int | |
103 | linebuf_len(buf_head_t *bufhead) | |
104 | ||
105 | Return the length of the buffer, in bytes. This should be used when calculating | |
106 | how big a buffer is for statistics. | |
107 | ||
108 | ||
109 | int | |
110 | linebuf_alloclen(buf_head_t *bufhead) | |
111 | ||
112 | Return how big the *allocated* space is. This is much more suitable for | |
113 | anti-flood checking, as someone might be sending a whole bunch of 1-byte | |
114 | linebufs which might not trigger a recvq / sendq limit but might chew up | |
115 | way too much memory. | |
116 | ||
117 | ||
118 | ||
119 | Notes | |
120 | ----- | |
121 | ||
122 | * Remember that the trailing NUL isn't covered in the string length. | |
123 | ||
124 | ||
125 | Limitations | |
126 | ----------- | |
127 | ||
128 | * all the buffers are a fixed size - here they are current 513 bytes | |
129 | (510 bytes + CR/LF/NUL) | |
130 | ||
131 | ||
132 | TODO | |
133 | ---- | |
134 | ||
135 | * linebuf_flush() should be changed a little so if the buffer isn't | |
136 | terminated, we *dont* retry flushing a buffer until we get more data. | |
137 | ||
138 | * Implement a reference-friendly linebuf to reduce copies .. | |
139 |