blob: a96cf598ed7d2fa5ecde2f1ba4e1a362d3dca26d [file] [log] [blame]
Amaury Denoyelle1b5f77f2022-05-09 09:37:27 +02001#include <haproxy/ncbuf.h>
2
Amaury Denoyelle077e0962022-05-09 09:43:11 +02003#include <string.h>
4
5#ifndef MIN
6#define MIN(a, b) (((a) < (b)) ? (a) : (b))
7#endif
8
Amaury Denoyelleeeeeed42022-05-04 16:51:19 +02009#ifdef STANDALONE
10#include <stdarg.h>
11#include <stdlib.h>
12#include <stdio.h>
13#include <unistd.h>
14
15#include <haproxy/list.h>
16#endif /* STANDALONE */
17
Amaury Denoyelle2526a6a2022-11-28 15:10:30 +010018#ifdef DEBUG_STRICT
Amaury Denoyelle1b5f77f2022-05-09 09:37:27 +020019# include <haproxy/bug.h>
20#else
21# include <stdio.h>
22# include <stdlib.h>
23
24# undef BUG_ON
25# define BUG_ON(x) if (x) { fprintf(stderr, "CRASH ON %s:%d\n", __func__, __LINE__); abort(); }
26
27# undef BUG_ON_HOT
28# define BUG_ON_HOT(x) if (x) { fprintf(stderr, "CRASH ON %s:%d\n", __func__, __LINE__); abort(); }
29#endif /* DEBUG_DEV */
30
Amaury Denoyelled64a26f2022-11-28 15:08:14 +010031#include <haproxy/compiler.h>
32
Amaury Denoyelle1b5f77f2022-05-09 09:37:27 +020033/* ******** internal API ******** */
34
Amaury Denoyelled5d2ed92022-05-09 09:38:45 +020035#define NCB_BLK_NULL ((struct ncb_blk){ .st = NULL })
36
37#define NCB_BK_F_GAP 0x01 /* block represents a gap */
Amaury Denoyelleedeb0a62022-05-04 16:16:39 +020038#define NCB_BK_F_FIN 0x02 /* special reduced gap present at the end of the buffer */
Amaury Denoyelled5d2ed92022-05-09 09:38:45 +020039struct ncb_blk {
40 char *st; /* first byte of the block */
41 char *end; /* first byte after this block */
42
Amaury Denoyelleedeb0a62022-05-04 16:16:39 +020043 char *sz_ptr; /* pointer to size element - NULL for reduced gap */
Amaury Denoyelled5d2ed92022-05-09 09:38:45 +020044 ncb_sz_t sz; /* size of the block */
Amaury Denoyelleedeb0a62022-05-04 16:16:39 +020045 ncb_sz_t sz_data; /* size of the data following the block - invalid for reduced GAP */
Amaury Denoyelled5d2ed92022-05-09 09:38:45 +020046 ncb_sz_t off; /* offset of block in buffer */
47
48 char flag;
49};
50
Amaury Denoyelle1b5f77f2022-05-09 09:37:27 +020051/* Return pointer to <off> relative to <buf> head. Support buffer wrapping. */
52static char *ncb_peek(const struct ncbuf *buf, ncb_sz_t off)
53{
54 char *ptr = ncb_head(buf) + off;
55 if (ptr >= buf->area + buf->size)
56 ptr -= buf->size;
57 return ptr;
58}
59
60/* Returns the reserved space of <buf> which contains the size of the first
61 * data block.
62 */
63static char *ncb_reserved(const struct ncbuf *buf)
64{
65 return ncb_peek(buf, buf->size - NCB_RESERVED_SZ);
66}
67
68/* Encode <off> at <st> position in <buf>. Support wrapping. */
Amaury Denoyelled64a26f2022-11-28 15:08:14 +010069static forceinline void ncb_write_off(const struct ncbuf *buf, char *st, ncb_sz_t off)
Amaury Denoyelle1b5f77f2022-05-09 09:37:27 +020070{
71 int i;
72
73 BUG_ON_HOT(st >= buf->area + buf->size);
74
75 for (i = 0; i < sizeof(ncb_sz_t); ++i) {
76 (*st) = off >> (8 * i) & 0xff;
77
78 if ((++st) == ncb_wrap(buf))
79 st = ncb_orig(buf);
80 }
81}
82
83/* Decode offset stored at <st> position in <buf>. Support wrapping. */
Amaury Denoyelled64a26f2022-11-28 15:08:14 +010084static forceinline ncb_sz_t ncb_read_off(const struct ncbuf *buf, char *st)
Amaury Denoyelle1b5f77f2022-05-09 09:37:27 +020085{
86 int i;
87 ncb_sz_t off = 0;
88
89 BUG_ON_HOT(st >= buf->area + buf->size);
90
91 for (i = 0; i < sizeof(ncb_sz_t); ++i) {
92 off |= (unsigned char )(*st) << (8 * i);
93
94 if ((++st) == ncb_wrap(buf))
95 st = ncb_orig(buf);
96 }
97
98 return off;
99}
100
Amaury Denoyelle077e0962022-05-09 09:43:11 +0200101/* Add <off> to the offset stored at <st> in <buf>. Support wrapping. */
Amaury Denoyelled64a26f2022-11-28 15:08:14 +0100102static forceinline void ncb_inc_off(const struct ncbuf *buf, char *st, ncb_sz_t off)
Amaury Denoyelle077e0962022-05-09 09:43:11 +0200103{
104 const ncb_sz_t old = ncb_read_off(buf, st);
105 ncb_write_off(buf, st, old + off);
106}
107
Amaury Denoyelleedeb0a62022-05-04 16:16:39 +0200108/* Returns true if a gap cannot be inserted at <off> : a reduced gap must be used. */
Amaury Denoyelled64a26f2022-11-28 15:08:14 +0100109static forceinline int ncb_off_reduced(const struct ncbuf *b, ncb_sz_t off)
Amaury Denoyelleedeb0a62022-05-04 16:16:39 +0200110{
111 return off + NCB_GAP_MIN_SZ > ncb_size(b);
112}
113
Amaury Denoyelled5d2ed92022-05-09 09:38:45 +0200114/* Returns true if <blk> is the special NULL block. */
Amaury Denoyelled64a26f2022-11-28 15:08:14 +0100115static forceinline int ncb_blk_is_null(const struct ncb_blk *blk)
Amaury Denoyelled5d2ed92022-05-09 09:38:45 +0200116{
Amaury Denoyelle17e20e82022-11-28 11:06:42 +0100117 return !blk->st;
Amaury Denoyelled5d2ed92022-05-09 09:38:45 +0200118}
119
120/* Returns true if <blk> is the last block of <buf>. */
Amaury Denoyelled64a26f2022-11-28 15:08:14 +0100121static forceinline int ncb_blk_is_last(const struct ncbuf *buf, const struct ncb_blk *blk)
Amaury Denoyelled5d2ed92022-05-09 09:38:45 +0200122{
Amaury Denoyelle17e20e82022-11-28 11:06:42 +0100123 BUG_ON_HOT(blk->off + blk->sz > ncb_size(buf));
124 return blk->off + blk->sz == ncb_size(buf);
Amaury Denoyelled5d2ed92022-05-09 09:38:45 +0200125}
126
127/* Returns the first block of <buf> which is always a DATA. */
128static struct ncb_blk ncb_blk_first(const struct ncbuf *buf)
129{
130 struct ncb_blk blk;
131
Amaury Denoyelle48fbad42022-05-16 11:09:29 +0200132 if (ncb_is_null(buf))
133 return NCB_BLK_NULL;
134
Amaury Denoyelled5d2ed92022-05-09 09:38:45 +0200135 blk.st = ncb_head(buf);
136
137 blk.sz_ptr = ncb_reserved(buf);
138 blk.sz = ncb_read_off(buf, ncb_reserved(buf));
Amaury Denoyelleb5ca9432022-05-13 15:33:50 +0200139 blk.sz_data = 0;
Amaury Denoyelled5d2ed92022-05-09 09:38:45 +0200140 BUG_ON_HOT(blk.sz > ncb_size(buf));
141
142 blk.end = ncb_peek(buf, blk.sz);
143 blk.off = 0;
144 blk.flag = 0;
145
146 return blk;
147}
148
149/* Returns the block following <prev> in the buffer <buf>. */
150static struct ncb_blk ncb_blk_next(const struct ncbuf *buf,
Amaury Denoyelle17e20e82022-11-28 11:06:42 +0100151 const struct ncb_blk *prev)
Amaury Denoyelled5d2ed92022-05-09 09:38:45 +0200152{
153 struct ncb_blk blk;
154
155 BUG_ON_HOT(ncb_blk_is_null(prev));
156
157 if (ncb_blk_is_last(buf, prev))
158 return NCB_BLK_NULL;
159
Amaury Denoyelle17e20e82022-11-28 11:06:42 +0100160 blk.st = prev->end;
161 blk.off = prev->off + prev->sz;
162 blk.flag = ~prev->flag & NCB_BK_F_GAP;
Amaury Denoyelled5d2ed92022-05-09 09:38:45 +0200163
164 if (blk.flag & NCB_BK_F_GAP) {
Amaury Denoyelleedeb0a62022-05-04 16:16:39 +0200165 if (ncb_off_reduced(buf, blk.off)) {
166 blk.flag |= NCB_BK_F_FIN;
167 blk.sz_ptr = NULL;
168 blk.sz = ncb_size(buf) - blk.off;
169 blk.sz_data = 0;
170
171 /* A reduced gap can only be the last block. */
Amaury Denoyelle17e20e82022-11-28 11:06:42 +0100172 BUG_ON_HOT(!ncb_blk_is_last(buf, &blk));
Amaury Denoyelleedeb0a62022-05-04 16:16:39 +0200173 }
174 else {
175 blk.sz_ptr = ncb_peek(buf, blk.off + NCB_GAP_SZ_OFF);
176 blk.sz = ncb_read_off(buf, blk.sz_ptr);
177 blk.sz_data = ncb_read_off(buf, ncb_peek(buf, blk.off + NCB_GAP_SZ_DATA_OFF));
178 BUG_ON_HOT(blk.sz < NCB_GAP_MIN_SZ);
179 }
Amaury Denoyelled5d2ed92022-05-09 09:38:45 +0200180 }
181 else {
Amaury Denoyelle17e20e82022-11-28 11:06:42 +0100182 blk.sz_ptr = ncb_peek(buf, prev->off + NCB_GAP_SZ_DATA_OFF);
183 blk.sz = prev->sz_data;
Amaury Denoyelled5d2ed92022-05-09 09:38:45 +0200184 blk.sz_data = 0;
185
186 /* only first DATA block can be empty. If this happens, a GAP
187 * merge should have been realized.
188 */
189 BUG_ON_HOT(!blk.sz);
190 }
191
192 BUG_ON_HOT(blk.off + blk.sz > ncb_size(buf));
193 blk.end = ncb_peek(buf, blk.off + blk.sz);
194
195 return blk;
196}
197
198/* Returns the block containing offset <off>. Note that if <off> is at the
199 * frontier between two blocks, this function will return the preceding one.
200 * This is done to easily merge blocks on insertion/deletion.
201 */
202static struct ncb_blk ncb_blk_find(const struct ncbuf *buf, ncb_sz_t off)
203{
204 struct ncb_blk blk;
205
Amaury Denoyelle48fbad42022-05-16 11:09:29 +0200206 if (ncb_is_null(buf))
207 return NCB_BLK_NULL;
208
Amaury Denoyelled5d2ed92022-05-09 09:38:45 +0200209 BUG_ON_HOT(off >= ncb_size(buf));
210
211 for (blk = ncb_blk_first(buf); off > blk.off + blk.sz;
Amaury Denoyelle17e20e82022-11-28 11:06:42 +0100212 blk = ncb_blk_next(buf, &blk)) {
Amaury Denoyelled5d2ed92022-05-09 09:38:45 +0200213 }
214
215 return blk;
216}
217
218/* Transform absolute offset <off> to a relative one from <blk> start. */
Amaury Denoyelled64a26f2022-11-28 15:08:14 +0100219static forceinline ncb_sz_t ncb_blk_off(const struct ncb_blk *blk, ncb_sz_t off)
Amaury Denoyelled5d2ed92022-05-09 09:38:45 +0200220{
Amaury Denoyelle17e20e82022-11-28 11:06:42 +0100221 BUG_ON_HOT(off < blk->off || off > blk->off + blk->sz);
222 BUG_ON_HOT(off - blk->off > blk->sz);
223 return off - blk->off;
Amaury Denoyelled5d2ed92022-05-09 09:38:45 +0200224}
225
Amaury Denoyelle077e0962022-05-09 09:43:11 +0200226/* Simulate insertion in <buf> of <data> of length <len> at offset <off>. This
227 * ensures that minimal block size are respected for newly formed gaps. <blk>
Amaury Denoyelleb830f0d2022-05-09 11:59:15 +0200228 * must be the block where the insert operation begins. If <mode> is
229 * NCB_ADD_COMPARE, old and new overlapped data are compared to validate the
230 * insertion.
Amaury Denoyelle077e0962022-05-09 09:43:11 +0200231 *
232 * Returns NCB_RET_OK if insertion can proceed.
233 */
234static enum ncb_ret ncb_check_insert(const struct ncbuf *buf,
Amaury Denoyelle17e20e82022-11-28 11:06:42 +0100235 const struct ncb_blk *blk, ncb_sz_t off,
Amaury Denoyelleb830f0d2022-05-09 11:59:15 +0200236 const char *data, ncb_sz_t len,
237 enum ncb_add_mode mode)
Amaury Denoyelle077e0962022-05-09 09:43:11 +0200238{
Amaury Denoyelle17e20e82022-11-28 11:06:42 +0100239 struct ncb_blk next;
Amaury Denoyelle077e0962022-05-09 09:43:11 +0200240 ncb_sz_t off_blk = ncb_blk_off(blk, off);
241 ncb_sz_t to_copy;
242 ncb_sz_t left = len;
243
244 /* If insertion starts in a gap, it must leave enough space to keep the
245 * gap header.
246 */
Amaury Denoyelle17e20e82022-11-28 11:06:42 +0100247 if (left && (blk->flag & NCB_BK_F_GAP)) {
Amaury Denoyelle077e0962022-05-09 09:43:11 +0200248 if (off_blk < NCB_GAP_MIN_SZ)
249 return NCB_RET_GAP_SIZE;
250 }
251
Amaury Denoyelle17e20e82022-11-28 11:06:42 +0100252 next = *blk;
Amaury Denoyelle077e0962022-05-09 09:43:11 +0200253 while (left) {
Amaury Denoyelle17e20e82022-11-28 11:06:42 +0100254 off_blk = ncb_blk_off(&next, off);
255 to_copy = MIN(left, next.sz - off_blk);
Amaury Denoyelle077e0962022-05-09 09:43:11 +0200256
Amaury Denoyelle17e20e82022-11-28 11:06:42 +0100257 if (next.flag & NCB_BK_F_GAP && off_blk + to_copy < next.sz) {
Amaury Denoyelle077e0962022-05-09 09:43:11 +0200258 /* Insertion must leave enough space for a new gap
259 * header if stopped in a middle of a gap.
260 */
Amaury Denoyelle17e20e82022-11-28 11:06:42 +0100261 const ncb_sz_t gap_sz = next.sz - (off_blk + to_copy);
262 if (gap_sz < NCB_GAP_MIN_SZ && !ncb_blk_is_last(buf, &next))
Amaury Denoyelle077e0962022-05-09 09:43:11 +0200263 return NCB_RET_GAP_SIZE;
264 }
Amaury Denoyelle17e20e82022-11-28 11:06:42 +0100265 else if (!(next.flag & NCB_BK_F_GAP) && mode == NCB_ADD_COMPARE) {
Amaury Denoyelleb830f0d2022-05-09 11:59:15 +0200266 /* Compare memory of data block in NCB_ADD_COMPARE mode. */
Amaury Denoyelle17e20e82022-11-28 11:06:42 +0100267 const ncb_sz_t off_blk = ncb_blk_off(&next, off);
Amaury Denoyelleb830f0d2022-05-09 11:59:15 +0200268 char *st = ncb_peek(buf, off);
269
Amaury Denoyelle17e20e82022-11-28 11:06:42 +0100270 to_copy = MIN(left, next.sz - off_blk);
Amaury Denoyelleb830f0d2022-05-09 11:59:15 +0200271 if (st + to_copy > ncb_wrap(buf)) {
272 const ncb_sz_t sz1 = ncb_wrap(buf) - st;
273 if (memcmp(st, data, sz1))
274 return NCB_RET_DATA_REJ;
275 if (memcmp(ncb_orig(buf), data + sz1, to_copy - sz1))
276 return NCB_RET_DATA_REJ;
277 }
278 else {
279 if (memcmp(st, data, to_copy))
280 return NCB_RET_DATA_REJ;
281 }
282 }
Amaury Denoyelle077e0962022-05-09 09:43:11 +0200283
284 left -= to_copy;
285 data += to_copy;
286 off += to_copy;
287
Amaury Denoyelle17e20e82022-11-28 11:06:42 +0100288 next = ncb_blk_next(buf, &next);
Amaury Denoyelle077e0962022-05-09 09:43:11 +0200289 }
290
291 return NCB_RET_OK;
292}
293
294/* Fill new <data> of length <len> inside an already existing data <blk> at
295 * offset <off>. Offset is relative to <blk> so it cannot be greater than the
Amaury Denoyelleb830f0d2022-05-09 11:59:15 +0200296 * block size. <mode> specifies if old data are preserved or overwritten.
Amaury Denoyelle077e0962022-05-09 09:43:11 +0200297 */
298static ncb_sz_t ncb_fill_data_blk(const struct ncbuf *buf,
Amaury Denoyelle17e20e82022-11-28 11:06:42 +0100299 const struct ncb_blk *blk, ncb_sz_t off,
Amaury Denoyelleb830f0d2022-05-09 11:59:15 +0200300 const char *data, ncb_sz_t len,
301 enum ncb_add_mode mode)
Amaury Denoyelle077e0962022-05-09 09:43:11 +0200302{
Amaury Denoyelle17e20e82022-11-28 11:06:42 +0100303 const ncb_sz_t to_copy = MIN(len, blk->sz - off);
Amaury Denoyelleb830f0d2022-05-09 11:59:15 +0200304 char *ptr = NULL;
Amaury Denoyelle077e0962022-05-09 09:43:11 +0200305
Amaury Denoyelle17e20e82022-11-28 11:06:42 +0100306 BUG_ON_HOT(off > blk->sz);
Amaury Denoyelle077e0962022-05-09 09:43:11 +0200307 /* This can happens due to previous ncb_blk_find() usage. In this
308 * case the current fill is a noop.
309 */
Amaury Denoyelle17e20e82022-11-28 11:06:42 +0100310 if (off == blk->sz)
Amaury Denoyelle077e0962022-05-09 09:43:11 +0200311 return 0;
312
Amaury Denoyelleb830f0d2022-05-09 11:59:15 +0200313 if (mode == NCB_ADD_OVERWRT) {
Amaury Denoyelle17e20e82022-11-28 11:06:42 +0100314 ptr = ncb_peek(buf, blk->off + off);
Amaury Denoyelleb830f0d2022-05-09 11:59:15 +0200315
316 if (ptr + to_copy >= ncb_wrap(buf)) {
317 const ncb_sz_t sz1 = ncb_wrap(buf) - ptr;
318 memcpy(ptr, data, sz1);
319 memcpy(ncb_orig(buf), data + sz1, to_copy - sz1);
320 }
321 else {
322 memcpy(ptr, data, to_copy);
323 }
324 }
325
Amaury Denoyelle077e0962022-05-09 09:43:11 +0200326 return to_copy;
327}
328
329/* Fill the gap <blk> starting at <off> with new <data> of length <len>. <off>
330 * is relative to <blk> so it cannot be greater than the block size.
331 */
332static ncb_sz_t ncb_fill_gap_blk(const struct ncbuf *buf,
Amaury Denoyelle17e20e82022-11-28 11:06:42 +0100333 const struct ncb_blk *blk, ncb_sz_t off,
Amaury Denoyelle077e0962022-05-09 09:43:11 +0200334 const char *data, ncb_sz_t len)
335{
Amaury Denoyelle17e20e82022-11-28 11:06:42 +0100336 const ncb_sz_t to_copy = MIN(len, blk->sz - off);
Amaury Denoyelle077e0962022-05-09 09:43:11 +0200337 char *ptr;
338
Amaury Denoyelle17e20e82022-11-28 11:06:42 +0100339 BUG_ON_HOT(off > blk->sz);
Amaury Denoyelle077e0962022-05-09 09:43:11 +0200340 /* This can happens due to previous ncb_blk_find() usage. In this
341 * case the current fill is a noop.
342 */
Amaury Denoyelle17e20e82022-11-28 11:06:42 +0100343 if (off == blk->sz)
Amaury Denoyelle077e0962022-05-09 09:43:11 +0200344 return 0;
345
346 /* A new gap must be created if insertion stopped before gap end. */
Amaury Denoyelle17e20e82022-11-28 11:06:42 +0100347 if (off + to_copy < blk->sz) {
348 const ncb_sz_t gap_off = blk->off + off + to_copy;
349 const ncb_sz_t gap_sz = blk->sz - off - to_copy;
Amaury Denoyelle077e0962022-05-09 09:43:11 +0200350
351 BUG_ON_HOT(!ncb_off_reduced(buf, gap_off) &&
Amaury Denoyelle17e20e82022-11-28 11:06:42 +0100352 blk->off + blk->sz - gap_off < NCB_GAP_MIN_SZ);
Amaury Denoyelle077e0962022-05-09 09:43:11 +0200353
354 /* write the new gap header unless this is a reduced gap. */
355 if (!ncb_off_reduced(buf, gap_off)) {
356 char *gap_ptr = ncb_peek(buf, gap_off + NCB_GAP_SZ_OFF);
357 char *gap_data_ptr = ncb_peek(buf, gap_off + NCB_GAP_SZ_DATA_OFF);
358
359 ncb_write_off(buf, gap_ptr, gap_sz);
Amaury Denoyelle17e20e82022-11-28 11:06:42 +0100360 ncb_write_off(buf, gap_data_ptr, blk->sz_data);
Amaury Denoyelle077e0962022-05-09 09:43:11 +0200361 }
362 }
363
364 /* fill the gap with new data */
Amaury Denoyelle17e20e82022-11-28 11:06:42 +0100365 ptr = ncb_peek(buf, blk->off + off);
Amaury Denoyelle077e0962022-05-09 09:43:11 +0200366 if (ptr + to_copy >= ncb_wrap(buf)) {
367 ncb_sz_t sz1 = ncb_wrap(buf) - ptr;
368 memcpy(ptr, data, sz1);
369 memcpy(ncb_orig(buf), data + sz1, to_copy - sz1);
370 }
371 else {
372 memcpy(ptr, data, to_copy);
373 }
374
375 return to_copy;
376}
377
Amaury Denoyelle1b5f77f2022-05-09 09:37:27 +0200378/* ******** public API ******** */
379
Amaury Denoyelle1b5f77f2022-05-09 09:37:27 +0200380/* Initialize or reset <buf> by clearing all data. Its size is untouched.
Amaury Denoyelle48fbad42022-05-16 11:09:29 +0200381 * Buffer is positioned to <head> offset. Use 0 to realign it. <buf> must not
382 * be NCBUF_NULL.
Amaury Denoyelle1b5f77f2022-05-09 09:37:27 +0200383 */
384void ncb_init(struct ncbuf *buf, ncb_sz_t head)
385{
Amaury Denoyelle48fbad42022-05-16 11:09:29 +0200386 BUG_ON_HOT(ncb_is_null(buf));
387
Amaury Denoyelle1b5f77f2022-05-09 09:37:27 +0200388 BUG_ON_HOT(head >= buf->size);
389 buf->head = head;
390
391 ncb_write_off(buf, ncb_reserved(buf), 0);
392 ncb_write_off(buf, ncb_head(buf), ncb_size(buf));
393 ncb_write_off(buf, ncb_peek(buf, sizeof(ncb_sz_t)), 0);
394}
395
396/* Construct a ncbuf with all its parameters. */
397struct ncbuf ncb_make(char *area, ncb_sz_t size, ncb_sz_t head)
398{
399 struct ncbuf buf;
400
401 /* Ensure that there is enough space for the reserved space and data.
402 * This is the minimal value to not crash later.
403 */
404 BUG_ON_HOT(size <= NCB_RESERVED_SZ);
405
406 buf.area = area;
407 buf.size = size;
408 buf.head = head;
409
410 return buf;
411}
412
Amaury Denoyelled5d2ed92022-05-09 09:38:45 +0200413/* Returns the total number of bytes stored in whole <buf>. */
414ncb_sz_t ncb_total_data(const struct ncbuf *buf)
415{
416 struct ncb_blk blk;
417 int total = 0;
418
Amaury Denoyelle17e20e82022-11-28 11:06:42 +0100419 for (blk = ncb_blk_first(buf); !ncb_blk_is_null(&blk); blk = ncb_blk_next(buf, &blk)) {
Amaury Denoyelled5d2ed92022-05-09 09:38:45 +0200420 if (!(blk.flag & NCB_BK_F_GAP))
421 total += blk.sz;
422 }
423
424 return total;
425}
426
Amaury Denoyelle1b5f77f2022-05-09 09:37:27 +0200427/* Returns true if there is no data anywhere in <buf>. */
428int ncb_is_empty(const struct ncbuf *buf)
429{
Amaury Denoyellef6dbdc12022-05-17 18:52:22 +0200430 int first_data, first_gap;
431
Amaury Denoyelle48fbad42022-05-16 11:09:29 +0200432 if (ncb_is_null(buf))
433 return 1;
434
Amaury Denoyellef6dbdc12022-05-17 18:52:22 +0200435 first_data = ncb_read_off(buf, ncb_reserved(buf));
436 BUG_ON_HOT(first_data > ncb_size(buf));
437 /* Buffer is not empty if first data block is not nul. */
438 if (first_data)
439 return 0;
440
441 /* Head contains the first gap size if first data block is empty. */
442 first_gap = ncb_read_off(buf, ncb_head(buf));
443 BUG_ON_HOT(first_gap > ncb_size(buf));
444 return first_gap == ncb_size(buf);
Amaury Denoyelle1b5f77f2022-05-09 09:37:27 +0200445}
446
447/* Returns true if no more data can be inserted in <buf>. */
448int ncb_is_full(const struct ncbuf *buf)
449{
Amaury Denoyellef6dbdc12022-05-17 18:52:22 +0200450 int first_data;
451
Amaury Denoyelle48fbad42022-05-16 11:09:29 +0200452 if (ncb_is_null(buf))
453 return 0;
454
Amaury Denoyellef6dbdc12022-05-17 18:52:22 +0200455 /* First data block must cover whole buffer if full. */
456 first_data = ncb_read_off(buf, ncb_reserved(buf));
457 BUG_ON_HOT(first_data > ncb_size(buf));
458 return first_data == ncb_size(buf);
Amaury Denoyelle1b5f77f2022-05-09 09:37:27 +0200459}
Amaury Denoyelled5d2ed92022-05-09 09:38:45 +0200460
Amaury Denoyellee0a92a72022-07-01 14:45:41 +0200461/* Returns true if <buf> contains data fragmented by gaps. */
462int ncb_is_fragmented(const struct ncbuf *buf)
463{
464 struct ncb_blk data, gap;
465
466 if (ncb_is_null(buf))
467 return 0;
468
469 /* check if buffer is empty or full */
470 if (ncb_is_empty(buf) || ncb_is_full(buf))
471 return 0;
472
473 /* check that following gap is the last block */
474 data = ncb_blk_first(buf);
Amaury Denoyelle17e20e82022-11-28 11:06:42 +0100475 gap = ncb_blk_next(buf, &data);
476 return !ncb_blk_is_last(buf, &gap);
Amaury Denoyellee0a92a72022-07-01 14:45:41 +0200477}
478
Ilya Shipitsin3b64a282022-07-29 22:26:53 +0500479/* Returns the number of bytes of data available in <buf> starting at offset
Amaury Denoyelled5d2ed92022-05-09 09:38:45 +0200480 * <off> until the next gap or the buffer end. The counted data may wrapped if
481 * the buffer storage is not aligned.
482 */
483ncb_sz_t ncb_data(const struct ncbuf *buf, ncb_sz_t off)
484{
Amaury Denoyelle1194db22022-05-31 11:44:25 +0200485 struct ncb_blk blk;
486 ncb_sz_t off_blk;
Amaury Denoyelled5d2ed92022-05-09 09:38:45 +0200487
Amaury Denoyelle1194db22022-05-31 11:44:25 +0200488 if (ncb_is_null(buf))
Amaury Denoyelle48fbad42022-05-16 11:09:29 +0200489 return 0;
490
Amaury Denoyelle1194db22022-05-31 11:44:25 +0200491 blk = ncb_blk_find(buf, off);
Amaury Denoyelle17e20e82022-11-28 11:06:42 +0100492 off_blk = ncb_blk_off(&blk, off);
Amaury Denoyelle1194db22022-05-31 11:44:25 +0200493
Amaury Denoyelled5d2ed92022-05-09 09:38:45 +0200494 /* if <off> at the frontier between two and <blk> is gap, retrieve the
495 * next data block.
496 */
497 if (blk.flag & NCB_BK_F_GAP && off_blk == blk.sz &&
Amaury Denoyelle17e20e82022-11-28 11:06:42 +0100498 !ncb_blk_is_last(buf, &blk)) {
499 blk = ncb_blk_next(buf, &blk);
500 off_blk = ncb_blk_off(&blk, off);
Amaury Denoyelled5d2ed92022-05-09 09:38:45 +0200501 }
502
503 if (blk.flag & NCB_BK_F_GAP)
504 return 0;
505
506 return blk.sz - off_blk;
507}
Amaury Denoyelle077e0962022-05-09 09:43:11 +0200508
509/* Add a new block at <data> of size <len> in <buf> at offset <off>.
510 *
511 * Returns NCB_RET_OK on success. On error the following codes are returned :
512 * - NCB_RET_GAP_SIZE : cannot add data because the gap formed is too small
Amaury Denoyelleb830f0d2022-05-09 11:59:15 +0200513 * - NCB_RET_DATA_REJ : old data would be overwritten by different ones in
514 * NCB_ADD_COMPARE mode.
Amaury Denoyelle077e0962022-05-09 09:43:11 +0200515 */
516enum ncb_ret ncb_add(struct ncbuf *buf, ncb_sz_t off,
Amaury Denoyelleb830f0d2022-05-09 11:59:15 +0200517 const char *data, ncb_sz_t len, enum ncb_add_mode mode)
Amaury Denoyelle077e0962022-05-09 09:43:11 +0200518{
519 struct ncb_blk blk;
520 ncb_sz_t left = len;
521 enum ncb_ret ret;
522 char *new_sz;
523
524 if (!len)
525 return NCB_RET_OK;
526
527 BUG_ON_HOT(off + len > ncb_size(buf));
528
529 /* Get block where insertion begins. */
530 blk = ncb_blk_find(buf, off);
531
532 /* Check if insertion is possible. */
Amaury Denoyelle17e20e82022-11-28 11:06:42 +0100533 ret = ncb_check_insert(buf, &blk, off, data, len, mode);
Amaury Denoyelle077e0962022-05-09 09:43:11 +0200534 if (ret != NCB_RET_OK)
535 return ret;
536
537 if (blk.flag & NCB_BK_F_GAP) {
538 /* Reduce gap size if insertion begins in a gap. Gap data size
539 * is reset and will be recalculated during insertion.
540 */
541 const ncb_sz_t gap_sz = off - blk.off;
542 BUG_ON_HOT(gap_sz < NCB_GAP_MIN_SZ);
543
544 /* pointer to data size to increase. */
545 new_sz = ncb_peek(buf, blk.off + NCB_GAP_SZ_DATA_OFF);
546
547 ncb_write_off(buf, blk.sz_ptr, gap_sz);
548 ncb_write_off(buf, new_sz, 0);
549 }
550 else {
551 /* pointer to data size to increase. */
552 new_sz = blk.sz_ptr;
553 }
554
555 /* insert data */
556 while (left) {
557 struct ncb_blk next;
Amaury Denoyelle17e20e82022-11-28 11:06:42 +0100558 const ncb_sz_t off_blk = ncb_blk_off(&blk, off);
Amaury Denoyelle077e0962022-05-09 09:43:11 +0200559 ncb_sz_t done;
560
561 /* retrieve the next block. This is necessary to do this
Ilya Shipitsin3b64a282022-07-29 22:26:53 +0500562 * before overwriting a gap.
Amaury Denoyelle077e0962022-05-09 09:43:11 +0200563 */
Amaury Denoyelle17e20e82022-11-28 11:06:42 +0100564 next = ncb_blk_next(buf, &blk);
Amaury Denoyelle077e0962022-05-09 09:43:11 +0200565
566 if (blk.flag & NCB_BK_F_GAP) {
Amaury Denoyelle17e20e82022-11-28 11:06:42 +0100567 done = ncb_fill_gap_blk(buf, &blk, off_blk, data, left);
Amaury Denoyelle077e0962022-05-09 09:43:11 +0200568
569 /* update the inserted data block size */
570 if (off + done == blk.off + blk.sz) {
571 /* merge next data block if insertion reached gap end */
572 ncb_inc_off(buf, new_sz, done + blk.sz_data);
573 }
574 else {
575 /* insertion stopped before gap end */
576 ncb_inc_off(buf, new_sz, done);
577 }
578 }
579 else {
Amaury Denoyelle17e20e82022-11-28 11:06:42 +0100580 done = ncb_fill_data_blk(buf, &blk, off_blk, data, left, mode);
Amaury Denoyelle077e0962022-05-09 09:43:11 +0200581 }
582
583 BUG_ON_HOT(done > blk.sz || done > left);
584 left -= done;
585 data += done;
586 off += done;
587
588 blk = next;
589 }
590
591 return NCB_RET_OK;
592}
Amaury Denoyelledf25acf2022-05-04 16:47:09 +0200593
Amaury Denoyelleca21c762022-05-17 18:52:39 +0200594/* Advance the head of <buf> to the offset <adv>. Data at the start of buffer
Amaury Denoyelledf25acf2022-05-04 16:47:09 +0200595 * will be lost while some space will be formed at the end to be able to insert
596 * new data.
597 *
Amaury Denoyelle7f0295f2022-11-18 15:54:40 +0100598 * Returns NCB_RET_OK on success. It may return NCB_RET_GAP_SIZE if operation
599 * is rejected due to the formation of a too small gap in front. If advance is
600 * done only inside a data block it is guaranteed to succeed.
Amaury Denoyelledf25acf2022-05-04 16:47:09 +0200601 */
Amaury Denoyelleca21c762022-05-17 18:52:39 +0200602enum ncb_ret ncb_advance(struct ncbuf *buf, ncb_sz_t adv)
Amaury Denoyelledf25acf2022-05-04 16:47:09 +0200603{
Amaury Denoyelleca21c762022-05-17 18:52:39 +0200604 struct ncb_blk start, last;
Amaury Denoyelledf25acf2022-05-04 16:47:09 +0200605 ncb_sz_t off_blk;
606 ncb_sz_t first_data_sz;
607
Amaury Denoyelleca21c762022-05-17 18:52:39 +0200608 BUG_ON_HOT(adv > ncb_size(buf));
609 if (!adv)
Amaury Denoyelledf25acf2022-05-04 16:47:09 +0200610 return NCB_RET_OK;
611
Amaury Denoyelleca21c762022-05-17 18:52:39 +0200612 /* Special case if adv is full size. This is equivalent to a reset. */
613 if (adv == ncb_size(buf)) {
Amaury Denoyelledf25acf2022-05-04 16:47:09 +0200614 ncb_init(buf, buf->head);
615 return NCB_RET_OK;
616 }
617
Amaury Denoyelleca21c762022-05-17 18:52:39 +0200618 start = ncb_blk_find(buf, adv);
619
620 /* Special case if advance until the last block which is a GAP. The
621 * buffer will be left empty and is thus equivalent to a reset.
622 */
Amaury Denoyelle17e20e82022-11-28 11:06:42 +0100623 if (ncb_blk_is_last(buf, &start) && (start.flag & NCB_BK_F_GAP)) {
Amaury Denoyelleca21c762022-05-17 18:52:39 +0200624 ncb_sz_t new_head = buf->head + adv;
625 if (new_head >= buf->size)
626 new_head -= buf->size;
627
628 ncb_init(buf, new_head);
629 return NCB_RET_OK;
630 }
631
632 last = start;
Amaury Denoyelle17e20e82022-11-28 11:06:42 +0100633 while (!ncb_blk_is_last(buf, &last))
634 last = ncb_blk_next(buf, &last);
Amaury Denoyelledf25acf2022-05-04 16:47:09 +0200635
Amaury Denoyelle17e20e82022-11-28 11:06:42 +0100636 off_blk = ncb_blk_off(&start, adv);
Amaury Denoyelledf25acf2022-05-04 16:47:09 +0200637
Amaury Denoyelleca21c762022-05-17 18:52:39 +0200638 if (start.flag & NCB_BK_F_GAP) {
639 /* If advance in a GAP, its new size must be big enough. */
640 if (start.sz == off_blk) {
641 /* GAP removed. Buffer will start with following DATA block. */
642 first_data_sz = start.sz_data;
Amaury Denoyelledf25acf2022-05-04 16:47:09 +0200643 }
Amaury Denoyelleca21c762022-05-17 18:52:39 +0200644 else if (start.sz - off_blk < NCB_GAP_MIN_SZ) {
Amaury Denoyelledf25acf2022-05-04 16:47:09 +0200645 return NCB_RET_GAP_SIZE;
646 }
647 else {
Amaury Denoyelleca21c762022-05-17 18:52:39 +0200648 /* Buffer will start with this GAP block. */
Amaury Denoyelledf25acf2022-05-04 16:47:09 +0200649 first_data_sz = 0;
650 }
651 }
652 else {
Amaury Denoyelleca21c762022-05-17 18:52:39 +0200653 /* If off_blk less than start.sz, the data block will becomes the
Amaury Denoyelledf25acf2022-05-04 16:47:09 +0200654 * first block. If equal, the data block is completely removed
655 * and thus the following GAP will be the first block.
656 */
Amaury Denoyelleca21c762022-05-17 18:52:39 +0200657 first_data_sz = start.sz - off_blk;
Amaury Denoyelledf25acf2022-05-04 16:47:09 +0200658 }
659
Amaury Denoyelleca21c762022-05-17 18:52:39 +0200660 if (last.flag & NCB_BK_F_GAP) {
661 /* Extend last GAP unless this is a reduced gap. */
662 if (!(last.flag & NCB_BK_F_FIN) || last.sz + adv >= NCB_GAP_MIN_SZ) {
Amaury Denoyelledf25acf2022-05-04 16:47:09 +0200663 /* use .st instead of .sz_ptr which can be NULL if reduced gap */
Amaury Denoyelleca21c762022-05-17 18:52:39 +0200664 ncb_write_off(buf, last.st, last.sz + adv);
Amaury Denoyelledf25acf2022-05-04 16:47:09 +0200665 ncb_write_off(buf, ncb_peek(buf, last.off + NCB_GAP_SZ_DATA_OFF), 0);
666 }
667 }
Amaury Denoyelleca21c762022-05-17 18:52:39 +0200668 else {
669 /* Insert a GAP after the last DATA block. */
670 if (adv >= NCB_GAP_MIN_SZ) {
671 ncb_write_off(buf, ncb_peek(buf, last.off + last.sz + NCB_GAP_SZ_OFF), adv);
Amaury Denoyelledf25acf2022-05-04 16:47:09 +0200672 ncb_write_off(buf, ncb_peek(buf, last.off + last.sz + NCB_GAP_SZ_DATA_OFF), 0);
673 }
674 }
675
Amaury Denoyelleca21c762022-05-17 18:52:39 +0200676 /* Advance head and update reserved header with new first data size. */
677 buf->head += adv;
Amaury Denoyelledf25acf2022-05-04 16:47:09 +0200678 if (buf->head >= buf->size)
679 buf->head -= buf->size;
680 ncb_write_off(buf, ncb_reserved(buf), first_data_sz);
681
Amaury Denoyelleca21c762022-05-17 18:52:39 +0200682 /* If advance in a GAP, reduce its size. */
683 if (start.flag & NCB_BK_F_GAP && !first_data_sz) {
684 ncb_write_off(buf, ncb_head(buf), start.sz - off_blk);
Amaury Denoyelledf25acf2022-05-04 16:47:09 +0200685 /* Recopy the block sz_data at the new position. */
Amaury Denoyelleca21c762022-05-17 18:52:39 +0200686 ncb_write_off(buf, ncb_peek(buf, NCB_GAP_SZ_DATA_OFF), start.sz_data);
Amaury Denoyelledf25acf2022-05-04 16:47:09 +0200687 }
688
689 return NCB_RET_OK;
690}
Amaury Denoyelleeeeeed42022-05-04 16:51:19 +0200691
692/* ******** testing API ******** */
693/* To build it :
Amaury Denoyellef46393a2022-05-16 11:09:05 +0200694 * gcc -Wall -DSTANDALONE -lasan -I./include -o ncbuf src/ncbuf.c
Amaury Denoyelleeeeeed42022-05-04 16:51:19 +0200695 */
696#ifdef STANDALONE
697
698int ncb_print = 0;
699
700static void ncbuf_printf(char *str, ...)
701{
702 va_list args;
703
704 va_start(args, str);
705 if (ncb_print)
706 vfprintf(stderr, str, args);
707 va_end(args);
708}
709
710struct rand_off {
711 struct list el;
712 ncb_sz_t off;
713 ncb_sz_t len;
714};
715
716static struct rand_off *ncb_generate_rand_off(const struct ncbuf *buf)
717{
718 struct rand_off *roff;
Tim Duesterhus9fb57e82022-06-01 21:58:37 +0200719 roff = calloc(1, sizeof(*roff));
Amaury Denoyelleeeeeed42022-05-04 16:51:19 +0200720 BUG_ON(!roff);
721
722 roff->off = rand() % (ncb_size(buf));
723 if (roff->off > 0 && roff->off < NCB_GAP_MIN_SZ)
724 roff->off = 0;
725
726 roff->len = rand() % (ncb_size(buf) - roff->off + 1);
727
728 return roff;
729}
730
Amaury Denoyelle17e20e82022-11-28 11:06:42 +0100731static void ncb_print_blk(const struct ncb_blk *blk)
Amaury Denoyelleeeeeed42022-05-04 16:51:19 +0200732{
733 if (ncb_print) {
Amaury Denoyellef46393a2022-05-16 11:09:05 +0200734 fprintf(stderr, "%s(%s): %2u/%u.\n",
Amaury Denoyelle17e20e82022-11-28 11:06:42 +0100735 blk->flag & NCB_BK_F_GAP ? "GAP " : "DATA",
736 blk->flag & NCB_BK_F_FIN ? "F" : "-", blk->off, blk->sz);
Amaury Denoyelleeeeeed42022-05-04 16:51:19 +0200737 }
738}
739
Amaury Denoyelle17e20e82022-11-28 11:06:42 +0100740static int ncb_is_null_blk(const struct ncb_blk *blk)
Amaury Denoyelleeeeeed42022-05-04 16:51:19 +0200741{
Amaury Denoyelle17e20e82022-11-28 11:06:42 +0100742 return !blk->st;
Amaury Denoyelleeeeeed42022-05-04 16:51:19 +0200743}
744
745static void ncb_loop(const struct ncbuf *buf)
746{
747 struct ncb_blk blk;
748
749 blk = ncb_blk_first(buf);
750 do {
Amaury Denoyelle17e20e82022-11-28 11:06:42 +0100751 ncb_print_blk(&blk);
752 blk = ncb_blk_next(buf, &blk);
753 } while (!ncb_is_null_blk(&blk));
Amaury Denoyelleeeeeed42022-05-04 16:51:19 +0200754
755 ncbuf_printf("\n");
756}
757
758static void ncbuf_print_buf(struct ncbuf *b, ncb_sz_t len,
759 unsigned char *area, int line)
760{
761 int i;
762
763 ncbuf_printf("buffer status at line %d\n", line);
764 for (i = 0; i < len; ++i) {
765 ncbuf_printf("%02x.", area[i]);
766 if (i && i % 32 == 31) ncbuf_printf("\n");
767 else if (i && i % 8 == 7) ncbuf_printf(" ");
768 }
769 ncbuf_printf("\n");
770
771 ncb_loop(b);
772
773 if (ncb_print)
774 getchar();
775}
776
777static struct ncbuf b;
778static unsigned char *bufarea = NULL;
779static ncb_sz_t bufsize = 16384;
780static ncb_sz_t bufhead = 15;
781
782#define NCB_INIT(buf) \
783 if ((reset)) { memset(bufarea, 0xaa, bufsize); } \
784 ncb_init(buf, bufhead); \
785 ncbuf_print_buf(&b, bufsize, bufarea, __LINE__);
786
787#define NCB_ADD_EQ(buf, off, data, sz, mode, ret) \
788 BUG_ON(ncb_add((buf), (off), (data), (sz), (mode)) != (ret)); \
789 ncbuf_print_buf(buf, bufsize, bufarea, __LINE__);
790
791#define NCB_ADD_NEQ(buf, off, data, sz, mode, ret) \
792 BUG_ON(ncb_add((buf), (off), (data), (sz), (mode)) == (ret)); \
793 ncbuf_print_buf(buf, bufsize, bufarea, __LINE__);
794
795#define NCB_ADVANCE_EQ(buf, off, ret) \
796 BUG_ON(ncb_advance((buf), (off)) != (ret)); \
797 ncbuf_print_buf(buf, bufsize, bufarea, __LINE__);
798
799#define NCB_TOTAL_DATA_EQ(buf, data) \
800 BUG_ON(ncb_total_data((buf)) != (data));
801
802#define NCB_DATA_EQ(buf, off, data) \
803 BUG_ON(ncb_data((buf), (off)) != (data));
804
805static int ncbuf_test(ncb_sz_t head, int reset, int print_delay)
806{
807 char *data0, data1[] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f };
808 struct list list = LIST_HEAD_INIT(list);
809 struct rand_off *roff, *roff_tmp;
810 enum ncb_ret ret;
811
812 data0 = malloc(bufsize);
813 memset(data0, 0xff, bufsize);
814
815 bufarea = malloc(bufsize);
816
Amaury Denoyelle48fbad42022-05-16 11:09:29 +0200817 fprintf(stderr, "running unit tests\n");
818
819 b = NCBUF_NULL;
820 BUG_ON(!ncb_is_null(&b));
821 NCB_DATA_EQ(&b, 0, 0);
822 NCB_TOTAL_DATA_EQ(&b, 0);
823 BUG_ON(ncb_size(&b) != 0);
824 BUG_ON(!ncb_is_empty(&b));
825 BUG_ON(ncb_is_full(&b));
Amaury Denoyellee0a92a72022-07-01 14:45:41 +0200826 BUG_ON(ncb_is_fragmented(&b));
Amaury Denoyelle48fbad42022-05-16 11:09:29 +0200827
Amaury Denoyellef46393a2022-05-16 11:09:05 +0200828 b.area = (char *)bufarea;
Amaury Denoyelleeeeeed42022-05-04 16:51:19 +0200829 b.size = bufsize;
830 b.head = head;
831 NCB_INIT(&b);
832
Amaury Denoyelleeeeeed42022-05-04 16:51:19 +0200833 /* insertion test suite */
834 NCB_INIT(&b);
835 NCB_DATA_EQ(&b, 0, 0); NCB_DATA_EQ(&b, bufsize - NCB_RESERVED_SZ - 1, 0); /* first and last offset */
836 NCB_ADD_EQ(&b, 24, data0, 9, NCB_ADD_PRESERVE, NCB_RET_OK); NCB_DATA_EQ(&b, 24, 9);
837 /* insert new data at the same offset as old */
838 NCB_ADD_EQ(&b, 24, data0, 16, NCB_ADD_PRESERVE, NCB_RET_OK); NCB_DATA_EQ(&b, 24, 16);
839
840 NCB_INIT(&b); NCB_DATA_EQ(&b, 0, 0);
841 NCB_ADD_EQ(&b, 0, data0, 16, NCB_ADD_PRESERVE, NCB_RET_OK); NCB_DATA_EQ(&b, 0, 16);
Amaury Denoyellee0a92a72022-07-01 14:45:41 +0200842 BUG_ON(ncb_is_fragmented(&b));
Amaury Denoyelleeeeeed42022-05-04 16:51:19 +0200843 NCB_ADD_EQ(&b, 24, data0, 16, NCB_ADD_PRESERVE, NCB_RET_OK); NCB_DATA_EQ(&b, 0, 16);
Amaury Denoyellee0a92a72022-07-01 14:45:41 +0200844 BUG_ON(!ncb_is_fragmented(&b));
Amaury Denoyelleeeeeed42022-05-04 16:51:19 +0200845 /* insert data overlapping two data blocks and a gap */
846 NCB_ADD_EQ(&b, 12, data0, 16, NCB_ADD_PRESERVE, NCB_RET_OK); NCB_DATA_EQ(&b, 0, 40);
Amaury Denoyellee0a92a72022-07-01 14:45:41 +0200847 BUG_ON(ncb_is_fragmented(&b));
Amaury Denoyelleeeeeed42022-05-04 16:51:19 +0200848
849 NCB_INIT(&b);
850 NCB_ADD_EQ(&b, 32, data0, 16, NCB_ADD_PRESERVE, NCB_RET_OK); NCB_DATA_EQ(&b, 0, 0); NCB_DATA_EQ(&b, 16, 0); NCB_DATA_EQ(&b, 32, 16);
Amaury Denoyellee0a92a72022-07-01 14:45:41 +0200851 BUG_ON(!ncb_is_fragmented(&b));
Amaury Denoyelleeeeeed42022-05-04 16:51:19 +0200852 NCB_ADD_EQ(&b, 0, data0, 16, NCB_ADD_PRESERVE, NCB_RET_OK); NCB_DATA_EQ(&b, 0, 16); NCB_DATA_EQ(&b, 16, 0); NCB_DATA_EQ(&b, 32, 16);
Amaury Denoyellee0a92a72022-07-01 14:45:41 +0200853 BUG_ON(!ncb_is_fragmented(&b));
Amaury Denoyelleeeeeed42022-05-04 16:51:19 +0200854 /* insert data to exactly cover a gap between two data blocks */
855 NCB_ADD_EQ(&b, 16, data0, 16, NCB_ADD_PRESERVE, NCB_RET_OK); NCB_DATA_EQ(&b, 0, 48); NCB_DATA_EQ(&b, 16, 32); NCB_DATA_EQ(&b, 32, 16);
Amaury Denoyellee0a92a72022-07-01 14:45:41 +0200856 BUG_ON(ncb_is_fragmented(&b));
Amaury Denoyelleeeeeed42022-05-04 16:51:19 +0200857
858 NCB_INIT(&b);
859 NCB_ADD_EQ(&b, 0, data0, 8, NCB_ADD_PRESERVE, NCB_RET_OK);
860 /* this insertion must be rejected because of minimal gap size */
861 NCB_ADD_EQ(&b, 10, data0, 8, NCB_ADD_PRESERVE, NCB_RET_GAP_SIZE);
862
863 /* Test reduced gap support */
864 NCB_INIT(&b);
865 /* this insertion will form a reduced gap */
866 NCB_ADD_EQ(&b, 0, data0, bufsize - (NCB_GAP_MIN_SZ - 1), NCB_ADD_COMPARE, NCB_RET_OK);
867
868 /* Test the various insertion mode */
869 NCB_INIT(&b);
870 NCB_ADD_EQ(&b, 10, data1, 16, NCB_ADD_PRESERVE, NCB_RET_OK);
871 NCB_ADD_EQ(&b, 12, data1, 16, NCB_ADD_COMPARE, NCB_RET_DATA_REJ);
872 NCB_ADD_EQ(&b, 12, data1, 16, NCB_ADD_PRESERVE, NCB_RET_OK); BUG_ON(*ncb_peek(&b, 12) != data1[2]);
873 NCB_ADD_EQ(&b, 12, data1, 16, NCB_ADD_OVERWRT, NCB_RET_OK); BUG_ON(*ncb_peek(&b, 12) == data1[2]);
874
875 /* advance test suite */
876 NCB_INIT(&b);
877 NCB_ADVANCE_EQ(&b, 10, NCB_RET_OK); /* advance in an empty buffer; this ensures we do not leave an empty DATA in the middle of the buffer */
878 NCB_ADVANCE_EQ(&b, ncb_size(&b) - 2, NCB_RET_OK);
879
880 NCB_INIT(&b);
881 /* first fill the buffer */
882 NCB_ADD_EQ(&b, 0, data0, bufsize - NCB_RESERVED_SZ, NCB_ADD_COMPARE, NCB_RET_OK);
883 /* delete 2 bytes : a reduced gap must be created */
884 NCB_ADVANCE_EQ(&b, 2, NCB_RET_OK); NCB_TOTAL_DATA_EQ(&b, ncb_size(&b) - 2);
885 /* delete 1 byte : extend the reduced gap */
886 NCB_ADVANCE_EQ(&b, 1, NCB_RET_OK); NCB_TOTAL_DATA_EQ(&b, ncb_size(&b) - 3);
887 /* delete 5 bytes : a full gap must be present */
888 NCB_ADVANCE_EQ(&b, 5, NCB_RET_OK); NCB_TOTAL_DATA_EQ(&b, ncb_size(&b) - 8);
889 /* completely clear the buffer */
890 NCB_ADVANCE_EQ(&b, bufsize - NCB_RESERVED_SZ, NCB_RET_OK); NCB_TOTAL_DATA_EQ(&b, 0);
891
892
893 NCB_INIT(&b);
894 NCB_ADD_EQ(&b, 10, data0, 10, NCB_ADD_PRESERVE, NCB_RET_OK);
895 NCB_ADVANCE_EQ(&b, 2, NCB_RET_OK); /* reduce a gap in front of the buffer */
896 NCB_ADVANCE_EQ(&b, 1, NCB_RET_GAP_SIZE); /* reject */
897 NCB_ADVANCE_EQ(&b, 8, NCB_RET_OK); /* remove completely the gap */
898 NCB_ADVANCE_EQ(&b, 8, NCB_RET_OK); /* remove inside the data */
899 NCB_ADVANCE_EQ(&b, 10, NCB_RET_OK); /* remove completely the data */
900
901 fprintf(stderr, "first random pass\n");
902 NCB_INIT(&b);
903
904 /* generate randon data offsets until the buffer is full */
905 while (!ncb_is_full(&b)) {
906 roff = ncb_generate_rand_off(&b);
907 LIST_INSERT(&list, &roff->el);
908
909 ret = ncb_add(&b, roff->off, data0, roff->len, NCB_ADD_COMPARE);
910 BUG_ON(ret == NCB_RET_DATA_REJ);
911 ncbuf_print_buf(&b, bufsize, bufarea, __LINE__);
912 usleep(print_delay);
913 }
914
915 fprintf(stderr, "buf full, prepare for reverse random\n");
916 ncbuf_print_buf(&b, bufsize, bufarea, __LINE__);
917
918 /* insert the previously generated random offsets in the reverse order.
919 * At the end, the buffer should be full.
920 */
921 NCB_INIT(&b);
922 list_for_each_entry_safe(roff, roff_tmp, &list, el) {
923 int full = ncb_is_full(&b);
924 if (!full) {
925 ret = ncb_add(&b, roff->off, data0, roff->len, NCB_ADD_COMPARE);
926 BUG_ON(ret == NCB_RET_DATA_REJ);
927 ncbuf_print_buf(&b, bufsize, bufarea, __LINE__);
928 usleep(print_delay);
929 }
930
931 LIST_DELETE(&roff->el);
932 free(roff);
933 }
934
935 if (!ncb_is_full(&b))
936 abort();
937
938 fprintf(stderr, "done\n");
939
940 free(bufarea);
941 free(data0);
942
943 return 1;
944}
945
946int main(int argc, char **argv)
947{
948 int reset = 0;
949 int print_delay = 100000;
950 char c;
951
952 opterr = 0;
953 while ((c = getopt(argc, argv, "h:s:rp::")) != -1) {
954 switch (c) {
955 case 'h':
956 bufhead = atoi(optarg);
957 break;
958 case 's':
959 bufsize = atoi(optarg);
960 if (bufsize < 64) {
961 fprintf(stderr, "bufsize should be at least 64 bytes for unit test suite\n");
962 exit(127);
963 }
964 break;
965 case 'r':
966 reset = 1;
967 break;
968 case 'p':
969 if (optarg)
970 print_delay = atoi(optarg);
971 ncb_print = 1;
972 break;
973 case '?':
974 default:
975 fprintf(stderr, "usage: %s [-r] [-s bufsize] [-h bufhead] [-p <delay_msec>]\n", argv[0]);
976 exit(127);
977 }
978 }
979
980 ncbuf_test(bufhead, reset, print_delay);
981 return EXIT_SUCCESS;
982}
983
984#endif /* STANDALONE */