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