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