suricata
util-streaming-buffer.c
Go to the documentation of this file.
1/* Copyright (C) 2015-2023 Open Information Security Foundation
2 *
3 * You can copy, redistribute or modify this Program under the terms of
4 * the GNU General Public License version 2 as published by the Free
5 * Software Foundation.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * version 2 along with this program; if not, write to the Free Software
14 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
15 * 02110-1301, USA.
16 */
17
18#include "suricata-common.h"
20#include "util-unittest.h"
21#include "util-print.h"
22#include "util-validate.h"
23#include "util-debug.h"
24#include "util-error.h"
25
26#include "app-layer-htp-mem.h"
27#include "conf-yaml-loader.h"
28#include "app-layer-htp.h"
29
30static void ListRegions(StreamingBuffer *sb);
31
32#define DUMP_REGIONS 0 // set to 1 to dump a visual representation of the regions list and sbb tree.
33
34/**
35 * \file
36 *
37 * \author Victor Julien <victor@inliniac.net>
38 *
39 * \brief Streaming Buffer API
40 */
41
42static void *ReallocFunc(void *ptr, const size_t size)
43{
44 void *ptrmem = SCRealloc(ptr, size);
45 if (unlikely(ptrmem == NULL)) {
47 }
48 return ptrmem;
49}
50
51static void *CallocFunc(const size_t nm, const size_t sz)
52{
53 void *ptrmem = SCCalloc(nm, sz);
54 if (unlikely(ptrmem == NULL)) {
56 }
57 return ptrmem;
58}
59
60/* memory handling wrappers. If config doesn't define it's own set of
61 * functions, use the defaults */
62// TODO the default allocators don't set `sc_errno` yet.
63#define CALLOC(cfg, n, s) (cfg)->Calloc ? (cfg)->Calloc((n), (s)) : CallocFunc((n), (s))
64#define REALLOC(cfg, ptr, orig_s, s) \
65 (cfg)->Realloc ? (cfg)->Realloc((ptr), (orig_s), (s)) : ReallocFunc((ptr), (s))
66#define FREE(cfg, ptr, s) \
67 (cfg)->Free ? (cfg)->Free((ptr), (s)) : SCFree((ptr))
68
69static void SBBFree(StreamingBuffer *sb, const StreamingBufferConfig *cfg);
70
72
74{
75 SCLogDebug("a %" PRIu64 " len %u, b %" PRIu64 " len %u", a->offset, a->len, b->offset, b->len);
76
77 if (a->offset > b->offset)
78 SCReturnInt(1);
79 else if (a->offset < b->offset)
80 SCReturnInt(-1);
81 else {
82 if (a->len == 0 || b->len == 0 || a->len == b->len)
83 SCReturnInt(0);
84 else if (a->len > b->len)
85 SCReturnInt(1);
86 else
87 SCReturnInt(-1);
88 }
89}
90
91/* inclusive compare function that also considers the right edge,
92 * not just the offset. */
93static inline int InclusiveCompare(StreamingBufferBlock *lookup, StreamingBufferBlock *intree) {
94 const uint64_t lre = lookup->offset + lookup->len;
95 const uint64_t tre = intree->offset + intree->len;
96 if (lre <= intree->offset) // entirely before
97 return -1;
98 else if (lookup->offset < tre && lre <= tre) // (some) overlap
99 return 0;
100 else
101 return 1; // entirely after
102}
103
105{
106 SCLogDebug("looking up %" PRIu64, elm->offset);
107
108 struct StreamingBufferBlock *tmp = RB_ROOT(head);
109 struct StreamingBufferBlock *res = NULL;
110 while (tmp) {
111 SCLogDebug("compare with %" PRIu64 "/%u", tmp->offset, tmp->len);
112 const int comp = InclusiveCompare(elm, tmp);
113 SCLogDebug("compare result: %d", comp);
114 if (comp < 0) {
115 res = tmp;
116 tmp = RB_LEFT(tmp, rb);
117 } else if (comp > 0) {
118 tmp = RB_RIGHT(tmp, rb);
119 } else {
120 return tmp;
121 }
122 }
123 return res;
124}
125
126/** \interal
127 * \brief does data region intersect with list region 'r'
128 * Takes the max gap into account.
129 */
130static inline bool RegionsIntersect(const StreamingBufferConfig *cfg,
131 const StreamingBufferRegion *r, const uint64_t offset, const uint64_t re)
132{
133 /* create the data range for the region, adding the max gap */
134 const uint64_t reg_o =
135 r->stream_offset > cfg->region_gap ? (r->stream_offset - cfg->region_gap) : 0;
136 const uint64_t reg_re = r->stream_offset + r->buf_size + cfg->region_gap;
137 SCLogDebug("r %p: %" PRIu64 "/%" PRIu64 " - adjusted %" PRIu64 "/%" PRIu64, r, r->stream_offset,
138 r->stream_offset + r->buf_size, reg_o, reg_re);
139 /* check if data range intersects with region range */
140 /* [offset:re] and [reg_o:reg_re] do not intersect if and only if
141 * re < reg_o or if reg_re < offset (one segment is strictly before the other)
142 * trusting that offset<=re and reg_o<=reg_re
143 */
144 if (re < reg_o || reg_re < offset) {
145 return false;
146 }
147 return true;
148}
149
150/** \internal
151 * \brief find the first region for merging.
152 */
153static StreamingBufferRegion *FindFirstRegionForOffset(const StreamingBufferConfig *cfg,
154 StreamingBufferRegion *r, const uint64_t offset, const uint32_t len,
156{
157 const uint64_t data_re = offset + len;
158 SCLogDebug("looking for first region matching %" PRIu64 "/%" PRIu64, offset, data_re);
159
160 StreamingBufferRegion *p = NULL;
161 for (; r != NULL; r = r->next) {
162 if (RegionsIntersect(cfg, r, offset, data_re)) {
163 *prev = p;
164 return r;
165 }
166 p = r;
167 }
168 *prev = NULL;
169 return NULL;
170}
171
172static StreamingBufferRegion *FindLargestRegionForOffset(const StreamingBufferConfig *cfg,
173 StreamingBufferRegion *r, const uint64_t offset, const uint32_t len)
174{
175 const uint64_t data_re = offset + len;
176 SCLogDebug("starting at %p/%" PRIu64 ", offset %" PRIu64 ", data_re %" PRIu64, r,
177 r->stream_offset, offset, data_re);
178 StreamingBufferRegion *candidate = r;
179 for (; r != NULL; r = r->next) {
180#ifdef DEBUG
181 const uint64_t reg_re = r->stream_offset + r->buf_size;
182 SCLogDebug("checking: %p/%" PRIu64 "/%" PRIu64 ", offset %" PRIu64 "/%" PRIu64, r,
183 r->stream_offset, reg_re, offset, data_re);
184#endif
185 if (!RegionsIntersect(cfg, r, offset, data_re))
186 return candidate;
187
188 if (r->buf_size > candidate->buf_size) {
189 SCLogDebug("candidate %p as size %u > %u", candidate, r->buf_size, candidate->buf_size);
190 candidate = r;
191 }
192 }
193 return candidate;
194}
195
196static StreamingBufferRegion *FindRightEdge(const StreamingBufferConfig *cfg,
197 StreamingBufferRegion *r, const uint64_t offset, const uint32_t len)
198{
199 const uint64_t data_re = offset + len;
200 StreamingBufferRegion *candidate = r;
201 for (; r != NULL; r = r->next) {
202 if (!RegionsIntersect(cfg, r, offset, data_re)) {
204 "r %p is out of scope: %" PRIu64 "/%u/%" PRIu64, r, offset, len, offset + len);
205 return candidate;
206 }
207 candidate = r;
208 }
209 return candidate;
210}
211
212/** \note uses sc_errno to give error details */
213static inline StreamingBufferRegion *InitBufferRegion(
214 StreamingBuffer *sb, const StreamingBufferConfig *cfg, const uint32_t min_size)
215{
216 if ((cfg->max_regions != 0 && sb->regions >= cfg->max_regions) || (sb->regions == UINT16_MAX)) {
217 SCLogDebug("max regions reached");
219 return NULL;
220 }
221
222 StreamingBufferRegion *aux_r = CALLOC(cfg, 1, sizeof(*aux_r));
223 if (aux_r == NULL) {
224 return NULL;
225 }
226
227 aux_r->buf = CALLOC(cfg, 1, MAX(cfg->buf_size, min_size));
228 if (aux_r->buf == NULL) {
229 FREE(cfg, aux_r, sizeof(*aux_r));
230 return NULL;
231 }
232 aux_r->buf_size = MAX(cfg->buf_size, min_size);
233 sb->regions++;
234 sb->max_regions = MAX(sb->regions, sb->max_regions);
235 return aux_r;
236}
237
238static inline int InitBuffer(StreamingBuffer *sb, const StreamingBufferConfig *cfg)
239{
240 sb->region.buf = CALLOC(cfg, 1, cfg->buf_size);
241 if (sb->region.buf == NULL) {
242 return sc_errno;
243 }
244 sb->region.buf_size = cfg->buf_size;
245 return SC_OK;
246}
247
249{
250 StreamingBuffer *sb = CALLOC(cfg, 1, sizeof(StreamingBuffer));
251 if (sb == NULL) {
252 return NULL;
253 }
254
255 sb->region.buf_size = cfg->buf_size;
256 sb->regions = sb->max_regions = 1;
257
258 if (cfg->buf_size == 0) {
259 return sb;
260 }
261
262 int r = InitBuffer(sb, cfg);
263 if (r != SC_OK) {
264 FREE(cfg, sb, sizeof(StreamingBuffer));
265 sc_errno = r;
266 return NULL;
267 }
268 return sb;
269}
270
272{
273 if (sb != NULL) {
274 SCLogDebug("sb->region.buf_size %u max %u", sb->region.buf_size, sb->buf_size_max);
275
276 SBBFree(sb, cfg);
277 ListRegions(sb);
278 if (sb->region.buf != NULL) {
279 FREE(cfg, sb->region.buf, sb->region.buf_size);
280 sb->region.buf = NULL;
281 }
282
283 for (StreamingBufferRegion *r = sb->region.next; r != NULL;) {
285 FREE(cfg, r->buf, r->buf_size);
286 FREE(cfg, r, sizeof(*r));
287 r = next;
288 }
289 sb->region.next = NULL;
290 sb->regions = sb->max_regions = 1;
291 }
292}
293
295{
296 if (sb != NULL) {
297 StreamingBufferClear(sb, cfg);
298 FREE(cfg, sb, sizeof(StreamingBuffer));
299 }
300}
301
302#ifdef DEBUG
303static void SBBPrintList(StreamingBuffer *sb)
304{
305 StreamingBufferBlock *sbb = NULL;
306 RB_FOREACH(sbb, SBB, &sb->sbb_tree) {
307 SCLogDebug("sbb: offset %" PRIu64 ", len %u", sbb->offset, sbb->len);
308 StreamingBufferBlock *next = SBB_RB_NEXT(sbb);
309 if (next) {
310 if ((sbb->offset + sbb->len) != next->offset) {
311 SCLogDebug("gap: offset %" PRIu64 ", len %" PRIu64, (sbb->offset + sbb->len),
312 next->offset - (sbb->offset + sbb->len));
313 }
314 }
315 }
316}
317#endif
318
319/* setup with gap between 2 blocks
320 *
321 * [block][gap][block]
322 **/
323static int WARN_UNUSED SBBInit(StreamingBuffer *sb, const StreamingBufferConfig *cfg,
324 StreamingBufferRegion *region, uint32_t rel_offset, uint32_t data_len)
325{
327 DEBUG_VALIDATE_BUG_ON(region->buf_offset > region->stream_offset + rel_offset);
328
329 /* need to set up 2: existing data block and new data block */
330 StreamingBufferBlock *sbb = CALLOC(cfg, 1, sizeof(*sbb));
331 if (sbb == NULL) {
332 return sc_errno;
333 }
334 sbb->offset = sb->region.stream_offset;
335 sbb->len = sb->region.buf_offset;
336 (void)SBB_RB_INSERT(&sb->sbb_tree, sbb);
337 sb->head = sbb;
338 sb->sbb_size = sbb->len;
339
340 StreamingBufferBlock *sbb2 = CALLOC(cfg, 1, sizeof(*sbb2));
341 if (sbb2 == NULL) {
342 return sc_errno;
343 }
344 sbb2->offset = region->stream_offset + rel_offset;
345 sbb2->len = data_len;
346 DEBUG_VALIDATE_BUG_ON(sbb2->offset < sbb->len);
347 SCLogDebug("sbb1 %" PRIu64 ", len %u, sbb2 %" PRIu64 ", len %u", sbb->offset, sbb->len,
348 sbb2->offset, sbb2->len);
349
350 sb->sbb_size += sbb2->len;
351 if (SBB_RB_INSERT(&sb->sbb_tree, sbb2) != NULL) {
352 FREE(cfg, sbb2, sizeof(*sbb2));
353 return SC_EINVAL;
354 }
355
356#ifdef DEBUG
357 SBBPrintList(sb);
358#endif
359 return SC_OK;
360}
361
362/* setup with leading gap
363 *
364 * [gap][block]
365 **/
366static int WARN_UNUSED SBBInitLeadingGap(
367 StreamingBuffer *sb, const StreamingBufferConfig *cfg, uint64_t offset, uint32_t data_len)
368{
370
371 StreamingBufferBlock *sbb = CALLOC(cfg, 1, sizeof(*sbb));
372 if (sbb == NULL) {
373 return sc_errno;
374 }
375 sbb->offset = offset;
376 sbb->len = data_len;
377
378 sb->head = sbb;
379 sb->sbb_size = sbb->len;
380 (void)SBB_RB_INSERT(&sb->sbb_tree, sbb);
381
382 SCLogDebug("sbb %" PRIu64 ", len %u", sbb->offset, sbb->len);
383#ifdef DEBUG
384 SBBPrintList(sb);
385#endif
386 return SC_OK;
387}
388
389static inline void ConsolidateFwd(StreamingBuffer *sb, const StreamingBufferConfig *cfg,
390 StreamingBufferRegion *region, struct SBB *tree, StreamingBufferBlock *sa)
391{
392 uint64_t sa_re = sa->offset + sa->len;
393 StreamingBufferBlock *tr, *s = sa;
394 RB_FOREACH_FROM(tr, SBB, s) {
395 if (sa == tr)
396 continue;
397
398 const uint64_t tr_re = tr->offset + tr->len;
399 SCLogDebug("-> (fwd) tr %p %" PRIu64 "/%u re %" PRIu64, tr, tr->offset, tr->len, tr_re);
400
401 if (sa_re < tr->offset) {
402 SCLogDebug("entirely before: %" PRIu64 " < %" PRIu64, sa_re, tr->offset);
403 break; // entirely before
404 }
405
406 /* new block (sa) entirely eclipsed by in tree block (tr)
407 sa: [ ]
408 tr: [ ]
409 sa: [ ]
410 tr: [ ]
411 sa: [ ]
412 tr: [ ]
413 */
414 if (sa->offset >= tr->offset && sa_re <= tr_re) {
415 sb->sbb_size -= sa->len;
416 sa->len = tr->len;
417 sa->offset = tr->offset;
418 sa_re = sa->offset + sa->len;
419 SCLogDebug("-> (fwd) tr %p %" PRIu64 "/%u REMOVED ECLIPSED (sa overlapped by tr)", tr,
420 tr->offset, tr->len);
421 SBB_RB_REMOVE(tree, tr);
422 FREE(cfg, tr, sizeof(StreamingBufferBlock));
423 /* new block (sa) entire eclipses in tree block (tr)
424 sa: [ ]
425 tr: [ ]
426 sa: [ ]
427 tr: [ ]
428 sa: [ ]
429 tr: [ ]
430 */
431 } else if (sa->offset <= tr->offset && sa_re >= tr_re) {
432 SCLogDebug("-> (fwd) tr %p %" PRIu64 "/%u REMOVED ECLIPSED (tr overlapped by sa)", tr,
433 tr->offset, tr->len);
434 SBB_RB_REMOVE(tree, tr);
435 sb->sbb_size -= tr->len;
436 FREE(cfg, tr, sizeof(StreamingBufferBlock));
437
438 SCLogDebug("-> (fwd) tr %p %" PRIu64 "/%u region %p so %" PRIu64 " bo %u sz %u", sa,
439 sa->offset, sa->len, region, region->stream_offset, region->buf_offset,
440 region->buf_size);
441 if (sa->offset == region->stream_offset &&
442 sa_re > (region->stream_offset + region->buf_offset)) {
443 DEBUG_VALIDATE_BUG_ON(sa_re < region->stream_offset);
444 DEBUG_VALIDATE_BUG_ON(sa_re - region->stream_offset > UINT32_MAX);
445 region->buf_offset = (uint32_t)(sa_re - region->stream_offset);
446 SCLogDebug("-> (fwd) tr %p %" PRIu64 "/%u region %p so %" PRIu64
447 " bo %u sz %u BUF_OFFSET UPDATED",
448 sa, sa->offset, sa->len, region, region->stream_offset, region->buf_offset,
449 region->buf_size);
450 }
451 /* new block (sa) extended by in tree block (tr)
452 sa: [ ]
453 tr: [ ]
454 sa: [ ]
455 tr: [ ]
456 */
457 } else if (sa->offset < tr->offset && // starts before
458 sa_re >= tr->offset && sa_re < tr_re) // ends inside
459 {
460 // merge. sb->sbb_size includes both so we need to adjust that too.
461 DEBUG_VALIDATE_BUG_ON(sa->len + tr->len > UINT32_MAX);
462 uint32_t combined_len = (uint32_t)(sa->len + tr->len);
463 DEBUG_VALIDATE_BUG_ON(tr_re - sa->offset > UINT32_MAX);
464 sa->len = (uint32_t)(tr_re - sa->offset);
465 sa_re = sa->offset + sa->len;
466 SCLogDebug("-> (fwd) tr %p %" PRIu64 "/%u REMOVED MERGED", tr, tr->offset, tr->len);
467 SBB_RB_REMOVE(tree, tr);
468 sb->sbb_size -= (combined_len - sa->len); // remove what we added twice
469 FREE(cfg, tr, sizeof(StreamingBufferBlock));
470 SCLogDebug("-> (fwd) tr %p %" PRIu64 "/%u RESULT", sa, sa->offset, sa->len);
471 SCLogDebug("-> (fwd) tr %p %" PRIu64 "/%u region %p so %" PRIu64 " bo %u sz %u", sa,
472 sa->offset, sa->len, region, region->stream_offset, region->buf_offset,
473 region->buf_size);
474 if (sa->offset == region->stream_offset &&
475 sa_re > (region->stream_offset + region->buf_offset)) {
476 DEBUG_VALIDATE_BUG_ON(sa_re < region->stream_offset);
477 DEBUG_VALIDATE_BUG_ON(sa_re - region->stream_offset > UINT32_MAX);
478 region->buf_offset = (uint32_t)(sa_re - region->stream_offset);
479 SCLogDebug("-> (fwd) tr %p %" PRIu64 "/%u region %p so %" PRIu64
480 " bo %u sz %u BUF_OFFSET UPDATED",
481 sa, sa->offset, sa->len, region, region->stream_offset, region->buf_offset,
482 region->buf_size);
483 }
484 }
485 }
486}
487
488static inline void ConsolidateBackward(StreamingBuffer *sb, const StreamingBufferConfig *cfg,
489 StreamingBufferRegion *region, struct SBB *tree, StreamingBufferBlock *sa)
490{
491 uint64_t sa_re = sa->offset + sa->len;
492 StreamingBufferBlock *tr, *s = sa;
493 RB_FOREACH_REVERSE_FROM(tr, SBB, s) {
494 if (sa == tr)
495 continue;
496 const uint64_t tr_re = tr->offset + tr->len;
497 SCLogDebug("-> (bwd) tr %p %" PRIu64 "/%u", tr, tr->offset, tr->len);
498
499 if (sa->offset > tr_re)
500 break; // entirely after
501
502 /* new block (sa) entirely eclipsed by in tree block (tr)
503 sa: [ ]
504 tr: [ ]
505 sa: [ ]
506 tr: [ ]
507 sa: [ ]
508 tr: [ ]
509 */
510 if (sa->offset >= tr->offset && sa_re <= tr_re) {
511 sb->sbb_size -= sa->len; // sa entirely eclipsed so remove double accounting
512 sa->len = tr->len;
513 sa->offset = tr->offset;
514 sa_re = sa->offset + sa->len;
515 SCLogDebug("-> (bwd) tr %p %" PRIu64 "/%u REMOVED ECLIPSED (sa overlapped by tr)", tr,
516 tr->offset, tr->len);
517 if (sb->head == tr)
518 sb->head = sa;
519 SBB_RB_REMOVE(tree, tr);
520 FREE(cfg, tr, sizeof(StreamingBufferBlock));
521 /* new block (sa) entire eclipses in tree block (tr)
522 sa: [ ]
523 tr: [ ]
524 sa: [ ]
525 tr: [ ]
526 sa: [ ]
527 tr: [ ]
528 */
529 } else if (sa->offset <= tr->offset && sa_re >= tr_re) {
530 SCLogDebug("-> (bwd) tr %p %" PRIu64 "/%u REMOVED ECLIPSED (tr overlapped by sa)", tr,
531 tr->offset, tr->len);
532 if (sb->head == tr)
533 sb->head = sa;
534 SBB_RB_REMOVE(tree, tr);
535 sb->sbb_size -= tr->len; // tr entirely eclipsed so remove double accounting
536 FREE(cfg, tr, sizeof(StreamingBufferBlock));
537
538 SCLogDebug("-> (bwd) tr %p %" PRIu64 "/%u region %p so %" PRIu64 " bo %u sz %u", sa,
539 sa->offset, sa->len, region, region->stream_offset, region->buf_offset,
540 region->buf_size);
541
542 if (sa->offset == region->stream_offset &&
543 sa_re > (region->stream_offset + region->buf_offset)) {
544 DEBUG_VALIDATE_BUG_ON(sa_re < region->stream_offset);
545 DEBUG_VALIDATE_BUG_ON(sa_re - region->stream_offset > UINT32_MAX);
546 region->buf_offset = (uint32_t)(sa_re - region->stream_offset);
547 SCLogDebug("-> (bwd) tr %p %" PRIu64 "/%u region %p so %" PRIu64
548 " bo %u sz %u BUF_OFFSET UPDATED",
549 sa, sa->offset, sa->len, region, region->stream_offset, region->buf_offset,
550 region->buf_size);
551 }
552
553 /* new block (sa) extends in tree block (tr)
554 sa: [ ]
555 tr: [ ]
556 sa: [ ]
557 tr: [ ]
558 */
559 } else if (sa->offset > tr->offset && sa_re > tr_re && sa->offset <= tr_re) {
560 // merge. sb->sbb_size includes both so we need to adjust that too.
561 DEBUG_VALIDATE_BUG_ON(sa->len + tr->len > UINT32_MAX);
562 uint32_t combined_len = (uint32_t)(sa->len + tr->len);
563 DEBUG_VALIDATE_BUG_ON(sa_re - tr->offset > UINT32_MAX);
564 sa->len = (uint32_t)(sa_re - tr->offset);
565 sa->offset = tr->offset;
566 sa_re = sa->offset + sa->len;
567 SCLogDebug("-> (bwd) tr %p %" PRIu64 "/%u REMOVED MERGED", tr, tr->offset, tr->len);
568 if (sb->head == tr)
569 sb->head = sa;
570 SBB_RB_REMOVE(tree, tr);
571 sb->sbb_size -= (combined_len - sa->len); // remove what we added twice
572 FREE(cfg, tr, sizeof(StreamingBufferBlock));
573
574 SCLogDebug("-> (bwd) tr %p %" PRIu64 "/%u region %p so %" PRIu64 " bo %u sz %u", sa,
575 sa->offset, sa->len, region, region->stream_offset, region->buf_offset,
576 region->buf_size);
577 if (sa->offset == region->stream_offset &&
578 sa_re > (region->stream_offset + region->buf_offset)) {
579 DEBUG_VALIDATE_BUG_ON(sa_re < region->stream_offset);
580 DEBUG_VALIDATE_BUG_ON(sa_re - region->stream_offset > UINT32_MAX);
581 region->buf_offset = (uint32_t)(sa_re - region->stream_offset);
582 SCLogDebug("-> (bwd) tr %p %" PRIu64 "/%u region %p so %" PRIu64
583 " bo %u sz %u BUF_OFFSET UPDATED",
584 sa, sa->offset, sa->len, region, region->stream_offset, region->buf_offset,
585 region->buf_size);
586 }
587 }
588 }
589}
590
591/** \internal
592 * \param region the region that holds the new data
593 */
594static int SBBUpdate(StreamingBuffer *sb, const StreamingBufferConfig *cfg,
595 StreamingBufferRegion *region, uint32_t rel_offset, uint32_t len)
596{
597 struct SBB *tree = &sb->sbb_tree;
598 SCLogDebug("* inserting: %u/%u", rel_offset, len);
599
600 StreamingBufferBlock *sbb = CALLOC(cfg, 1, sizeof(*sbb));
601 if (sbb == NULL) {
602 return sc_errno;
603 }
604 sbb->offset = region->stream_offset + rel_offset;
605 sbb->len = len;
606
607 StreamingBufferBlock *res = SBB_RB_INSERT(tree, sbb);
608 if (res) {
609 // exact overlap
610 SCLogDebug("* insert failed: exact match in tree with %p %" PRIu64 "/%u", res, res->offset,
611 res->len);
612 FREE(cfg, sbb, sizeof(StreamingBufferBlock));
613 return SC_OK;
614 }
615 sb->sbb_size += len; // may adjust based on consolidation below
616
617 /* handle backwards and forwards overlaps */
618 if (SBB_RB_PREV(sbb) == NULL) {
619 sb->head = sbb;
620 } else {
621 ConsolidateBackward(sb, cfg, region, tree, sbb);
622 }
623 ConsolidateFwd(sb, cfg, region, tree, sbb);
624#ifdef DEBUG
625 SBBPrintList(sb);
626#endif
627 if (sbb->offset == region->stream_offset) {
628 SCLogDebug("insert at region head");
629 region->buf_offset = sbb->len;
630 }
631 return SC_OK;
632}
633
634static void SBBFree(StreamingBuffer *sb, const StreamingBufferConfig *cfg)
635{
636 StreamingBufferBlock *sbb = NULL, *safe = NULL;
637 RB_FOREACH_SAFE(sbb, SBB, &sb->sbb_tree, safe) {
638 SBB_RB_REMOVE(&sb->sbb_tree, sbb);
639 sb->sbb_size -= sbb->len;
640 FREE(cfg, sbb, sizeof(StreamingBufferBlock));
641 }
642 sb->head = NULL;
643}
644
645static void SBBPrune(StreamingBuffer *sb, const StreamingBufferConfig *cfg)
646{
647 SCLogDebug("pruning %p to %" PRIu64, sb, sb->region.stream_offset);
648 StreamingBufferBlock *sbb = NULL, *safe = NULL;
649 RB_FOREACH_SAFE(sbb, SBB, &sb->sbb_tree, safe) {
650 /* completely beyond window, we're done */
651 if (sbb->offset >= sb->region.stream_offset) {
652 sb->head = sbb;
653 if (sbb->offset == sb->region.stream_offset) {
654 SCLogDebug("set buf_offset to first sbb len %u", sbb->len);
656 sb->region.buf_offset = sbb->len;
657 }
658 break;
659 }
660
661 /* partly before, partly beyond. Adjust */
662 if (sbb->offset < sb->region.stream_offset &&
663 sbb->offset + sbb->len > sb->region.stream_offset) {
664 DEBUG_VALIDATE_BUG_ON(sb->region.stream_offset - sbb->offset > UINT32_MAX);
665 uint32_t shrink_by = (uint32_t)(sb->region.stream_offset - sbb->offset);
666 DEBUG_VALIDATE_BUG_ON(shrink_by > sbb->len);
667 if (sbb->len >= shrink_by) {
668 sbb->len -= shrink_by;
669 sbb->offset += shrink_by;
670 sb->sbb_size -= shrink_by;
671 SCLogDebug("shrunk by %u", shrink_by);
673 }
674 sb->head = sbb;
675 if (sbb->offset == sb->region.stream_offset) {
676 SCLogDebug("set buf_offset to first sbb len %u", sbb->len);
678 sb->region.buf_offset = sbb->len;
679 }
680 break;
681 }
682
683 SBB_RB_REMOVE(&sb->sbb_tree, sbb);
684 /* either we set it again for the next sbb, or there isn't any */
685 sb->head = NULL;
686 sb->sbb_size -= sbb->len;
687 SCLogDebug("sb %p removed %p %" PRIu64 ", %u", sb, sbb, sbb->offset, sbb->len);
688 FREE(cfg, sbb, sizeof(StreamingBufferBlock));
689 }
690#ifdef DEBUG
691 SBBPrintList(sb);
692#endif
693}
694
695static inline uint32_t ToNextMultipleOf(const uint32_t in, const uint32_t m)
696{
697 uint32_t r = in;
698 if (m > 0) {
699 const uint32_t x = in % m;
700 if (x != 0) {
701 r = (in - x) + m;
702 }
703 }
704 return r;
705}
706
707static thread_local bool g2s_warn_once = false;
708
709static inline int WARN_UNUSED GrowRegionToSize(StreamingBuffer *sb,
710 const StreamingBufferConfig *cfg, StreamingBufferRegion *region, const uint32_t size)
711{
713 if (size > BIT_U32(30)) { // 1GiB
714 if (!g2s_warn_once) {
715 SCLogWarning("StreamingBuffer::GrowRegionToSize() tried to alloc %u bytes, exceeds "
716 "limit of %" PRIu32,
717 size, BIT_U32(30));
718 g2s_warn_once = true;
719 }
720 return SC_ELIMIT;
721 }
722
723 /* try to grow in multiples of cfg->buf_size */
724 const uint32_t grow = ToNextMultipleOf(size, cfg->buf_size);
725 SCLogDebug("grow %u", grow);
726 if (grow <= region->buf_size) {
727 // do not try to shrink, and do not memset with diff having unsigned underflow
728 return SC_OK;
729 }
730
731 void *ptr = REALLOC(cfg, region->buf, region->buf_size, grow);
732 if (ptr == NULL) {
733 if (sc_errno == SC_OK)
735 return sc_errno;
736 }
737 /* for safe printing and general caution, lets memset the
738 * new data to 0 */
739 size_t diff = grow - region->buf_size;
740 void *new_mem = ((char *)ptr) + region->buf_size;
741 memset(new_mem, 0, diff);
742
743 region->buf = ptr;
744 region->buf_size = grow;
745 SCLogDebug("grown buffer to %u", grow);
746#ifdef DEBUG
747 if (region->buf_size > sb->buf_size_max) {
748 sb->buf_size_max = region->buf_size;
749 }
750#endif
751 return SC_OK;
752}
753
754static int WARN_UNUSED GrowToSize(
755 StreamingBuffer *sb, const StreamingBufferConfig *cfg, uint32_t size)
756{
757 return GrowRegionToSize(sb, cfg, &sb->region, size);
758}
759
760static inline bool RegionBeforeOffset(const StreamingBufferRegion *r, const uint64_t o)
761{
762 return (r->stream_offset + r->buf_size <= o);
763}
764
765static inline bool RegionContainsOffset(const StreamingBufferRegion *r, const uint64_t o)
766{
767 return (o >= r->stream_offset && (r->stream_offset + r->buf_size) >= o);
768}
769
770/** \internal
771 * \brief slide to offset for regions
772 *
773 *
774 * [ main ] [ gap ] [ aux ] [ gap ] [ aux ]
775 * ^
776 * - main reset to 0
777 *
778 *
779 * [ main ] [ gap ] [ aux ] [ gap ] [ aux ]
780 * ^
781 * - main shift
782 *
783 * [ main ] [ gap ] [ aux ] [ gap ] [ aux ]
784 * ^
785 * - main reset
786 * - move aux into main
787 * - free aux
788 * - shift
789 *
790 * [ main ] [ gap ] [ aux ] [ gap ] [ aux ]
791 * ^
792 * - main reset
793 * - move aux into main
794 * - free aux
795 * - no shift
796 */
797static inline void StreamingBufferSlideToOffsetWithRegions(
798 StreamingBuffer *sb, const StreamingBufferConfig *cfg, const uint64_t slide_offset)
799{
800 ListRegions(sb);
801 DEBUG_VALIDATE_BUG_ON(slide_offset == sb->region.stream_offset);
802
803 SCLogDebug("slide_offset %" PRIu64, slide_offset);
804 SCLogDebug("main: offset %" PRIu64 " buf %p size %u offset %u", sb->region.stream_offset,
805 sb->region.buf, sb->region.buf_size, sb->region.buf_offset);
806
807 StreamingBufferRegion *to_shift = NULL;
808 const bool main_is_oow = RegionBeforeOffset(&sb->region, slide_offset);
809 if (main_is_oow) {
810 SCLogDebug("main_is_oow");
811 if (sb->region.buf != NULL) {
812 SCLogDebug("clearing main");
813 FREE(cfg, sb->region.buf, sb->region.buf_size);
814 sb->region.buf = NULL;
815 sb->region.buf_size = 0;
816 sb->region.buf_offset = 0;
817 sb->region.stream_offset = slide_offset;
818 } else {
819 sb->region.stream_offset = slide_offset;
820 }
821
822 /* remove regions that are out of window & select the region to
823 * become the new main */
824 StreamingBufferRegion *prev = &sb->region;
825 for (StreamingBufferRegion *r = sb->region.next; r != NULL;) {
826 SCLogDebug("r %p so %" PRIu64 ", re %" PRIu64, r, r->stream_offset,
827 r->stream_offset + r->buf_offset);
829 if (RegionBeforeOffset(r, slide_offset)) {
830 SCLogDebug("r %p so %" PRIu64 ", re %" PRIu64 " -> before", r, r->stream_offset,
831 r->stream_offset + r->buf_offset);
832 DEBUG_VALIDATE_BUG_ON(r == &sb->region);
833 prev->next = next;
834
835 FREE(cfg, r->buf, r->buf_size);
836 FREE(cfg, r, sizeof(*r));
837 sb->regions--;
839 } else if (RegionContainsOffset(r, slide_offset)) {
840 SCLogDebug("r %p so %" PRIu64 ", re %" PRIu64 " -> within", r, r->stream_offset,
841 r->stream_offset + r->buf_offset);
842 /* remove from list, we will xfer contents to main below */
843 prev->next = next;
844 to_shift = r;
845 break;
846 } else {
847 SCLogDebug("r %p so %" PRIu64 ", re %" PRIu64 " -> post", r, r->stream_offset,
848 r->stream_offset + r->buf_offset);
849 /* implied beyond slide offset */
850 DEBUG_VALIDATE_BUG_ON(r->stream_offset < slide_offset);
851 break;
852 }
853 r = next;
854 }
855 SCLogDebug("to_shift %p", to_shift);
856
857 // this region is main, or will xfer its buffer to main
858 if (to_shift && to_shift != &sb->region) {
859 SCLogDebug("main: offset %" PRIu64 " buf %p size %u offset %u", to_shift->stream_offset,
860 to_shift->buf, to_shift->buf_size, to_shift->buf_offset);
861 DEBUG_VALIDATE_BUG_ON(sb->region.buf != NULL);
862
863 sb->region.buf = to_shift->buf;
864 sb->region.stream_offset = to_shift->stream_offset;
865 sb->region.buf_offset = to_shift->buf_offset;
866 sb->region.buf_size = to_shift->buf_size;
867 sb->region.next = to_shift->next;
868
869 BUG_ON(to_shift == &sb->region);
870 FREE(cfg, to_shift, sizeof(*to_shift));
871 to_shift = &sb->region;
872 sb->regions--;
874 }
875
876 } else {
877 to_shift = &sb->region;
878 SCLogDebug("shift start region %p", to_shift);
879 }
880
881 // this region is main, or will xfer its buffer to main
882 if (to_shift) {
883 // Do the shift. If new region is exactly at the slide offset we can skip this.
884 DEBUG_VALIDATE_BUG_ON(to_shift->stream_offset > slide_offset);
885 DEBUG_VALIDATE_BUG_ON(slide_offset - to_shift->stream_offset > UINT32_MAX);
886 const uint32_t s = (uint32_t)(slide_offset - to_shift->stream_offset);
887 if (s > 0) {
888 const uint32_t new_data_size = to_shift->buf_size - s;
889 uint32_t new_mem_size = ToNextMultipleOf(new_data_size, cfg->buf_size);
890
891 /* see if after the slide we'd overlap with the next region. If so, we need
892 * to consolidate them into one. Error handling is a bit peculiar. We need
893 * to grow a region, move data from another region into it, then free the
894 * other region. This could fail if we're close to the memcap. In this case
895 * we relax our rounding up logic so we only shrink and don't merge the 2
896 * regions after all. */
897 if (to_shift->next && slide_offset + new_mem_size >= to_shift->next->stream_offset) {
898 StreamingBufferRegion *start = to_shift;
900 const uint64_t next_re = next->stream_offset + next->buf_size;
901 DEBUG_VALIDATE_BUG_ON(next_re - slide_offset > UINT32_MAX);
902 const uint32_t mem_size =
903 ToNextMultipleOf((uint32_t)(next_re - slide_offset), cfg->buf_size);
904
905 /* using next as the new main */
906 if (start->buf_size < next->buf_size) {
907 SCLogDebug("replace main with the next bigger region");
908
909 DEBUG_VALIDATE_BUG_ON(next->stream_offset - slide_offset > UINT32_MAX);
910 const uint32_t next_data_offset =
911 (uint32_t)(next->stream_offset - slide_offset);
912 const uint32_t prev_buf_size = next->buf_size;
913 DEBUG_VALIDATE_BUG_ON(slide_offset - start->stream_offset > UINT32_MAX);
914 const uint32_t start_data_offset =
915 (uint32_t)(slide_offset - start->stream_offset);
916 DEBUG_VALIDATE_BUG_ON(start_data_offset > start->buf_size);
917 if (start_data_offset > start->buf_size) {
918 new_mem_size = new_data_size;
919 goto just_main;
920 }
921 /* expand "next" to include relevant part of "start" */
922 if (GrowRegionToSize(sb, cfg, next, mem_size) != 0) {
923 new_mem_size = new_data_size;
924 goto just_main;
925 }
926 SCLogDebug("region now sized %u", mem_size);
927
928 // slide "next":
929 // pre-grow: [nextnextnext]
930 // post-grow [nextnextnextXXX]
931 // post-move [XXXnextnextnext]
932 memmove(next->buf + next_data_offset, next->buf, prev_buf_size);
933
934 // move portion of "start" into "next"
935 //
936 // start: [ooooookkkkk] (o: old, k: keep)
937 // pre-next [xxxxxnextnextnext]
938 // post-next [kkkkknextnextnext]
939 const uint32_t start_data_size = start->buf_size - start_data_offset;
940 memcpy(next->buf, start->buf + start_data_offset, start_data_size);
941
942 // free "start"s buffer, we will use the one from "next"
943 FREE(cfg, start->buf, start->buf_size);
944
945 // update "main" to use "next"
946 start->stream_offset = slide_offset;
947 start->buf = next->buf;
948 start->buf_size = next->buf_size;
949 start->next = next->next;
950
951 // free "next"
952 FREE(cfg, next, sizeof(*next));
953 sb->regions--;
955 goto done;
956 } else {
957 /* using "main", expand to include "next" */
958 if (mem_size > start->buf_size) {
959 // Check that start->buf_size is actually not big enough
960 // As mem_size computation and earlier checks do not make it clear.
961 if (GrowRegionToSize(sb, cfg, start, mem_size) != 0) {
962 new_mem_size = new_data_size;
963 goto just_main;
964 }
965 }
966 SCLogDebug("start->buf now size %u", mem_size);
967
968 // slide "start"
969 // pre: [xxxxxxxAAA]
970 // post: [AAAxxxxxxx]
971 SCLogDebug("s %u new_data_size %u", s, new_data_size);
972 memmove(start->buf, start->buf + s, new_data_size);
973
974 // copy in "next"
975 // pre: [AAAxxxxxxx]
976 // [BBBBBBB]
977 // post: [AAABBBBBBB]
978 SCLogDebug("copy next->buf %p/%u to start->buf offset %u", next->buf,
979 next->buf_size, new_data_size);
980 memcpy(start->buf + new_data_size, next->buf, next->buf_size);
981
982 start->stream_offset = slide_offset;
983 start->next = next->next;
984
985 // free "next"
986 FREE(cfg, next->buf, next->buf_size);
987 FREE(cfg, next, sizeof(*next));
988 sb->regions--;
990 goto done;
991 }
992 }
993
994 just_main:
995 SCLogDebug("s %u new_data_size %u", s, new_data_size);
996 memmove(to_shift->buf, to_shift->buf + s, new_data_size);
997 /* shrink memory region. If this fails we keep the old */
998 void *ptr = REALLOC(cfg, to_shift->buf, to_shift->buf_size, new_mem_size);
999 if (ptr != NULL) {
1000 to_shift->buf = ptr;
1001 to_shift->buf_size = new_mem_size;
1002 SCLogDebug("new buf_size %u", to_shift->buf_size);
1003 }
1004 if (s < to_shift->buf_offset)
1005 to_shift->buf_offset -= s;
1006 else
1007 to_shift->buf_offset = 0;
1008 to_shift->stream_offset = slide_offset;
1009 }
1010 }
1011
1012done:
1013 SCLogDebug("main: offset %" PRIu64 " buf %p size %u offset %u", sb->region.stream_offset,
1014 sb->region.buf, sb->region.buf_size, sb->region.buf_offset);
1015 SCLogDebug("end of slide");
1016}
1017
1018/**
1019 * \brief slide to absolute offset
1020 * \todo if sliding beyond window, we could perhaps reset?
1021 */
1023 StreamingBuffer *sb, const StreamingBufferConfig *cfg, uint64_t offset)
1024{
1025 SCLogDebug("sliding to offset %" PRIu64, offset);
1026 ListRegions(sb);
1027#ifdef DEBUG
1028 SBBPrintList(sb);
1029#endif
1030
1031 if (sb->region.next) {
1032 StreamingBufferSlideToOffsetWithRegions(sb, cfg, offset);
1033 SBBPrune(sb, cfg);
1034 SCLogDebug("post SBBPrune");
1035 ListRegions(sb);
1036#ifdef DEBUG
1037 SBBPrintList(sb);
1038#endif
1039 DEBUG_VALIDATE_BUG_ON(sb->region.buf != NULL && sb->region.buf_size == 0);
1043 sb->head->len > sb->region.buf_offset);
1045 return;
1046 }
1047
1048 if (offset > sb->region.stream_offset) {
1050 const uint32_t slide = (uint32_t)(offset - sb->region.stream_offset);
1051 if (sb->head != NULL) {
1052 /* have sbb's, so can't rely on buf_offset for the slide */
1053 if (slide < sb->region.buf_size) {
1054 const uint32_t size = sb->region.buf_size - slide;
1055 SCLogDebug("sliding %u forward, size of original buffer left after slide %u", slide,
1056 size);
1057 memmove(sb->region.buf, sb->region.buf + slide, size);
1058 if (sb->region.buf_offset > slide) {
1059 sb->region.buf_offset -= slide;
1060 } else {
1061 sb->region.buf_offset = 0;
1062 }
1063 } else {
1064 sb->region.buf_offset = 0;
1065 }
1067 } else {
1068 /* no sbb's, so we can use buf_offset */
1069 if (offset <= sb->region.stream_offset + sb->region.buf_offset) {
1070 const uint32_t size = sb->region.buf_offset - slide;
1071 SCLogDebug("sliding %u forward, size of original buffer left after slide %u", slide,
1072 size);
1073 memmove(sb->region.buf, sb->region.buf + slide, size);
1075 sb->region.buf_offset = size;
1076 } else {
1077 /* moved past all data */
1079 sb->region.buf_offset = 0;
1080 }
1081 }
1082 SBBPrune(sb, cfg);
1083 }
1084#ifdef DEBUG
1085 SBBPrintList(sb);
1086#endif
1088}
1089
1090static int DataFits(const StreamingBuffer *sb, const uint32_t len)
1091{
1092 uint64_t buf_offset64 = sb->region.buf_offset;
1093 uint64_t len64 = len;
1094 if (len64 + buf_offset64 > UINT32_MAX) {
1095 return -1;
1096 }
1097 return sb->region.buf_offset + len <= sb->region.buf_size;
1098}
1099
1101 StreamingBufferSegment *seg, const uint8_t *data, uint32_t data_len)
1102{
1103 DEBUG_VALIDATE_BUG_ON(seg == NULL);
1104 DEBUG_VALIDATE_BUG_ON(data_len > BIT_U32(27)); // 128MiB is excessive already
1105
1106 if (sb->region.buf == NULL) {
1107 if (InitBuffer(sb, cfg) == -1)
1108 return -1;
1109 }
1110
1111 int r = DataFits(sb, data_len);
1112 if (r < 0) {
1114 return -1;
1115 } else if (r == 0) {
1116 if (sb->region.buf_size == 0) {
1117 if (GrowToSize(sb, cfg, data_len) != SC_OK)
1118 return -1;
1119 } else {
1120 if (GrowToSize(sb, cfg, sb->region.buf_offset + data_len) != SC_OK)
1121 return -1;
1122 }
1123 }
1124 DEBUG_VALIDATE_BUG_ON(DataFits(sb, data_len) != 1);
1125 if (DataFits(sb, data_len) != 1)
1126 return -1;
1127
1128 memcpy(sb->region.buf + sb->region.buf_offset, data, data_len);
1129 seg->stream_offset = sb->region.stream_offset + sb->region.buf_offset;
1130 seg->segment_len = data_len;
1131 uint32_t rel_offset = sb->region.buf_offset;
1132 sb->region.buf_offset += data_len;
1133
1134 if (!RB_EMPTY(&sb->sbb_tree)) {
1135 return SBBUpdate(sb, cfg, &sb->region, rel_offset, data_len);
1136 } else {
1137 return 0;
1138 }
1139}
1140
1141/**
1142 * \brief add data w/o tracking a segment
1143 */
1145 const uint8_t *data, uint32_t data_len)
1146{
1147 DEBUG_VALIDATE_BUG_ON(data_len > BIT_U32(27)); // 128MiB is excessive already
1148
1149 if (sb->region.buf == NULL) {
1150 if (InitBuffer(sb, cfg) == -1)
1151 return -1;
1152 }
1153
1154 int r = DataFits(sb, data_len);
1155 if (r < 0) {
1157 return -1;
1158 } else if (r == 0) {
1159 if (sb->region.buf_size == 0) {
1160 if (GrowToSize(sb, cfg, data_len) != SC_OK)
1161 return -1;
1162 } else {
1163 if (GrowToSize(sb, cfg, sb->region.buf_offset + data_len) != SC_OK)
1164 return -1;
1165 }
1166 }
1167 DEBUG_VALIDATE_BUG_ON(DataFits(sb, data_len) != 1);
1168
1169 memcpy(sb->region.buf + sb->region.buf_offset, data, data_len);
1170 uint32_t rel_offset = sb->region.buf_offset;
1171 sb->region.buf_offset += data_len;
1172
1173 if (!RB_EMPTY(&sb->sbb_tree)) {
1174 return SBBUpdate(sb, cfg, &sb->region, rel_offset, data_len);
1175 } else {
1176 return 0;
1177 }
1178}
1179
1180static int DataFitsAtOffset(
1181 const StreamingBufferRegion *region, const uint32_t len, const uint32_t offset)
1182{
1183 const uint64_t offset64 = offset;
1184 const uint64_t len64 = len;
1185 if (offset64 + len64 > UINT32_MAX)
1186 return -1;
1187 return (offset + len <= region->buf_size);
1188}
1189
1190#if defined(DEBUG) || defined(DEBUG_VALIDATION)
1191static void Validate(const StreamingBuffer *sb)
1192{
1193 bool bail = false;
1194 uint32_t cnt = 0;
1195 for (const StreamingBufferRegion *r = &sb->region; r != NULL; r = r->next) {
1196 cnt++;
1197 if (r->next) {
1198 bail |= ((r->stream_offset + r->buf_size) > r->next->stream_offset);
1199 }
1200
1201 bail |= (r->buf != NULL && r->buf_size == 0);
1202 bail |= (r->buf_offset > r->buf_size);
1203 }
1204 // leading gap, so buf_offset should be 0?
1205 if (sb->head && sb->head->offset > sb->region.stream_offset) {
1206 SCLogDebug("leading gap of size %" PRIu64, sb->head->offset - sb->region.stream_offset);
1207 BUG_ON(sb->region.buf_offset != 0);
1208 }
1209
1210 if (sb->head && sb->head->offset == sb->region.stream_offset) {
1211 BUG_ON(sb->head->len > sb->region.buf_offset);
1212 BUG_ON(sb->head->len < sb->region.buf_offset);
1213 }
1214 BUG_ON(sb->regions != cnt);
1215 BUG_ON(bail);
1216}
1217#endif
1218
1219static void ListRegions(StreamingBuffer *sb)
1220{
1221 if (sb->region.buf == NULL && sb->region.buf_offset == 0 && sb->region.next == NULL)
1222 return;
1223#if defined(DEBUG) && DUMP_REGIONS == 1
1224 uint32_t cnt = 0;
1225 for (StreamingBufferRegion *r = &sb->region; r != NULL; r = r->next) {
1226 cnt++;
1227 char gap[64] = "";
1228 if (r->next) {
1229 snprintf(gap, sizeof(gap), "[ gap:%" PRIu64 " ]",
1230 r->next->stream_offset - (r->stream_offset + r->buf_size));
1231 }
1232
1233 printf("[ %s offset:%" PRIu64 " size:%u offset:%u ]%s", r == &sb->region ? "main" : "aux",
1234 r->stream_offset, r->buf_size, r->buf_offset, gap);
1235 }
1236 printf("(max %u, cnt %u, sb->regions %u)\n", sb->max_regions, cnt, sb->regions);
1237 bool at_least_one = false;
1238 uint64_t last_re = sb->region.stream_offset;
1239 StreamingBufferBlock *sbb = NULL;
1240 RB_FOREACH(sbb, SBB, &sb->sbb_tree)
1241 {
1242 if (last_re != sbb->offset) {
1243 printf("[ gap:%" PRIu64 " ]", sbb->offset - last_re);
1244 }
1245 printf("[ sbb offset:%" PRIu64 " len:%u ]", sbb->offset, sbb->len);
1246 at_least_one = true;
1247 last_re = sbb->offset + sbb->len;
1248 }
1249 if (at_least_one)
1250 printf("\n");
1251#endif
1252#if defined(DEBUG) || defined(DEBUG_VALIDATION)
1253 StreamingBufferBlock *sbb2 = NULL;
1254 RB_FOREACH(sbb2, SBB, &sb->sbb_tree)
1255 {
1256 const uint8_t *_data = NULL;
1257 uint32_t _data_len = 0;
1258 (void)StreamingBufferSBBGetData(sb, sbb2, &_data, &_data_len);
1259 }
1260 Validate(sb);
1261#endif
1262}
1263
1264/** \internal
1265 * \brief process insert by consolidating the affected regions into one
1266 * \note sets sc_errno
1267 */
1268static StreamingBufferRegion *BufferInsertAtRegionConsolidate(StreamingBuffer *sb,
1270 StreamingBufferRegion *src_start, StreamingBufferRegion *src_end,
1271 const uint64_t data_offset, const uint32_t data_len, StreamingBufferRegion *prev,
1272 uint32_t dst_buf_size)
1273{
1274 int retval;
1275#ifdef DEBUG
1276 const uint64_t data_re = data_offset + data_len;
1277 SCLogDebug("sb %p dst %p src_start %p src_end %p data_offset %" PRIu64
1278 "/data_len %u/data_re %" PRIu64,
1279 sb, dst, src_start, src_end, data_offset, data_len, data_re);
1280#endif
1281
1282 // 1. determine size and offset for dst.
1283 const uint64_t dst_offset = MIN(src_start->stream_offset, data_offset);
1284 DEBUG_VALIDATE_BUG_ON(dst_offset < sb->region.stream_offset);
1285 const uint32_t dst_size = dst_buf_size;
1286 SCLogDebug("dst_size %u", dst_size);
1287
1288 // 2. resize dst
1289 const uint32_t old_size = dst->buf_size;
1290 DEBUG_VALIDATE_BUG_ON(dst->stream_offset - dst_offset > UINT32_MAX);
1291 const uint32_t dst_copy_offset = (uint32_t)(dst->stream_offset - dst_offset);
1292#ifdef DEBUG
1293 const uint32_t old_offset = dst->buf_offset;
1294 SCLogDebug("old_size %u, old_offset %u, dst_copy_offset %u", old_size, old_offset,
1295 dst_copy_offset);
1296#endif
1297 if ((retval = GrowRegionToSize(sb, cfg, dst, dst_size)) != SC_OK) {
1298 sc_errno = retval;
1299 return NULL;
1300 }
1301 SCLogDebug("resized to %u -> %u", dst_size, dst->buf_size);
1302 /* validate that the size is exactly what we asked for */
1303 DEBUG_VALIDATE_BUG_ON(dst_size != dst->buf_size);
1304 if (dst_copy_offset != 0)
1305 memmove(dst->buf + dst_copy_offset, dst->buf, old_size);
1306 if (dst_offset != dst->stream_offset) {
1307 dst->stream_offset = dst_offset;
1308 // buf_offset no longer valid, reset.
1309 dst->buf_offset = 0;
1310 }
1311
1312 uint32_t new_offset = src_start->buf_offset;
1313 if (data_offset == src_start->stream_offset + src_start->buf_offset) {
1314 new_offset += data_len;
1315 }
1316
1317 bool start_is_main = false;
1318 if (src_start == &sb->region) {
1319 DEBUG_VALIDATE_BUG_ON(src_start->stream_offset != dst_offset);
1320
1321 start_is_main = true;
1322 SCLogDebug("src_start is main region");
1323 if (src_start != dst)
1324 memcpy(dst->buf, src_start->buf, src_start->buf_offset);
1325 if (src_start == src_end) {
1326 SCLogDebug("src_start == src_end == main, we're done");
1327 DEBUG_VALIDATE_BUG_ON(src_start != dst);
1328 return src_start;
1329 }
1330 prev = src_start;
1331 src_start = src_start->next; // skip in the loop below
1332 }
1333
1334 // 3. copy all regions from src_start to dst_start into the new region
1335 for (StreamingBufferRegion *r = src_start; r != NULL;) {
1336 SCLogDebug("r %p %" PRIu64 ", offset %u, len %u, %s, last %s", r, r->stream_offset,
1337 r->buf_offset, r->buf_size, r == &sb->region ? "main" : "aux",
1338 BOOL2STR(r == src_end));
1339 // skip dst
1340 if (r == dst) {
1341 SCLogDebug("skipping r %p as it is 'dst'", r);
1342 if (r == src_end)
1343 break;
1344 prev = r;
1345 r = r->next;
1346 continue;
1347 }
1348 DEBUG_VALIDATE_BUG_ON(r->stream_offset - dst_offset > UINT32_MAX);
1349 const uint32_t target_offset = (uint32_t)(r->stream_offset - dst_offset);
1350 SCLogDebug("r %p: target_offset %u", r, target_offset);
1351 DEBUG_VALIDATE_BUG_ON(target_offset > dst->buf_size);
1352 DEBUG_VALIDATE_BUG_ON(target_offset + r->buf_size > dst->buf_size);
1353 memcpy(dst->buf + target_offset, r->buf, r->buf_size);
1354
1356 FREE(cfg, r->buf, r->buf_size);
1357 FREE(cfg, r, sizeof(*r));
1358 sb->regions--;
1360
1361 DEBUG_VALIDATE_BUG_ON(prev == NULL && src_start != &sb->region);
1362 if (prev != NULL) {
1363 SCLogDebug("setting prev %p next to %p (was %p)", prev, next, prev->next);
1364 prev->next = next;
1365 } else {
1366 SCLogDebug("no prev yet");
1367 }
1368
1369 if (r == src_end)
1370 break;
1371 r = next;
1372 }
1373
1374 /* special handling of main region being the start, but not the
1375 * region we expand. In this case we'll have main and dst. We will
1376 * move the buffer from dst into main and free dst. */
1377 if (start_is_main && dst != &sb->region) {
1379 SCLogDebug("start_is_main && dst != main region");
1380 FREE(cfg, sb->region.buf, sb->region.buf_size);
1381 sb->region.buf = dst->buf;
1382 sb->region.buf_size = dst->buf_size;
1383 sb->region.buf_offset = new_offset;
1384 SCLogDebug("sb->region.buf_offset set to %u", sb->region.buf_offset);
1385 sb->region.next = dst->next;
1386 FREE(cfg, dst, sizeof(*dst));
1387 dst = &sb->region;
1388 sb->regions--;
1390 } else {
1391 SCLogDebug("dst: %p next %p", dst, dst->next);
1392 }
1393
1394 SCLogDebug("returning dst %p stream_offset %" PRIu64 " buf_offset %u buf_size %u", dst,
1395 dst->stream_offset, dst->buf_offset, dst->buf_size);
1396 return dst;
1397}
1398
1399/** \note sets sc_errno */
1400static StreamingBufferRegion *BufferInsertAtRegionDo(StreamingBuffer *sb,
1401 const StreamingBufferConfig *cfg, const uint64_t offset, const uint32_t len)
1402{
1403 SCLogDebug("offset %" PRIu64 ", len %u", offset, len);
1404 StreamingBufferRegion *start_prev = NULL;
1405 StreamingBufferRegion *start =
1406 FindFirstRegionForOffset(cfg, &sb->region, offset, len, &start_prev);
1407 if (start) {
1408 const uint64_t insert_re = offset + len;
1409 const uint64_t insert_start_offset = MIN(start->stream_offset, offset);
1410 uint64_t insert_adjusted_re = insert_re;
1411
1412 SCLogDebug("start region %p/%" PRIu64 "/%u", start, start->stream_offset, start->buf_size);
1413 StreamingBufferRegion *big = FindLargestRegionForOffset(cfg, start, offset, len);
1414 DEBUG_VALIDATE_BUG_ON(big == NULL);
1415 if (big == NULL) {
1417 return NULL;
1418 }
1419 SCLogDebug("big region %p/%" PRIu64 "/%u", big, big->stream_offset, big->buf_size);
1420 StreamingBufferRegion *end = FindRightEdge(cfg, big, offset, len);
1421 DEBUG_VALIDATE_BUG_ON(end == NULL);
1422 if (end == NULL) {
1424 return NULL;
1425 }
1426 insert_adjusted_re = MAX(insert_adjusted_re, (end->stream_offset + end->buf_size));
1427
1428 DEBUG_VALIDATE_BUG_ON(insert_adjusted_re - insert_start_offset > UINT32_MAX);
1429 uint32_t new_buf_size = ToNextMultipleOf(
1430 (uint32_t)(insert_adjusted_re - insert_start_offset), cfg->buf_size);
1431 SCLogDebug("new_buf_size %u", new_buf_size);
1432
1433 /* see if our new buf size + cfg->buf_size margin leads to an overlap with the next region.
1434 * If so, include that in the consolidation. */
1435 if (end->next != NULL && new_buf_size + insert_start_offset > end->next->stream_offset) {
1436 SCLogDebug("adjusted end from %p to %p", end, end->next);
1437 end = end->next;
1438 insert_adjusted_re = MAX(insert_adjusted_re, (end->stream_offset + end->buf_size));
1439 DEBUG_VALIDATE_BUG_ON(insert_adjusted_re - insert_start_offset > UINT32_MAX);
1440 new_buf_size = ToNextMultipleOf(
1441 (uint32_t)(insert_adjusted_re - insert_start_offset), cfg->buf_size);
1442 SCLogDebug("new_buf_size %u", new_buf_size);
1443 }
1444
1445 SCLogDebug("end region %p/%" PRIu64 "/%u", end, end->stream_offset, end->buf_size);
1446 /* sets sc_errno */
1447 StreamingBufferRegion *ret = BufferInsertAtRegionConsolidate(
1448 sb, cfg, big, start, end, offset, len, start_prev, new_buf_size);
1449 return ret;
1450 } else {
1451 /* if there was no region we can use we add a new region and insert it */
1452 StreamingBufferRegion *append = &sb->region;
1453 for (StreamingBufferRegion *r = append; r != NULL; r = r->next) {
1454 if (r->stream_offset > offset) {
1455 break;
1456 } else {
1457 append = r;
1458 }
1459 }
1460
1461 SCLogDebug("no matching region found, append to %p (%s)", append,
1462 append == &sb->region ? "main" : "aux");
1463 /* sets sc_errno */
1464 StreamingBufferRegion *add = InitBufferRegion(sb, cfg, len);
1465 if (add == NULL)
1466 return NULL;
1467 add->stream_offset = offset;
1468 add->next = append->next;
1469 append->next = add;
1470 SCLogDebug("new region %p offset %" PRIu64, add, add->stream_offset);
1471 return add;
1472 }
1473}
1474
1475/** \internal
1476 * \brief return the region to put the new data in
1477 *
1478 * Will find an existing region, expand it if needed. If no existing region exists or is
1479 * a good fit, it will try to set up a new region. If the region then overlaps or gets
1480 * too close to the next, merge them.
1481 *
1482 * \note sets sc_errno
1483 */
1484static StreamingBufferRegion *BufferInsertAtRegion(StreamingBuffer *sb,
1485 const StreamingBufferConfig *cfg, const uint32_t data_len, const uint64_t data_offset)
1486{
1487 SCLogDebug("data_offset %" PRIu64 ", data_len %u, re %" PRIu64, data_offset, data_len,
1488 data_offset + data_len);
1489 ListRegions(sb);
1490
1491 if (RegionsIntersect(cfg, &sb->region, data_offset, data_offset + data_len)) {
1492 SCLogDebug("data_offset %" PRIu64 ", data_len %u intersects with main region (next %p)",
1493 data_offset, data_len, sb->region.next);
1494 if (sb->region.next == NULL ||
1495 !RegionsIntersect(cfg, sb->region.next, data_offset, data_offset + data_len)) {
1496 SCLogDebug(
1497 "data_offset %" PRIu64
1498 ", data_len %u intersects with main region, no next or way before next region",
1499 data_offset, data_len);
1500 if (sb->region.buf == NULL) {
1501 int r;
1502 if ((r = InitBuffer(sb, cfg)) != SC_OK) { // TODO init with size
1503 sc_errno = r;
1504 return NULL;
1505 }
1506 }
1507 return &sb->region;
1508 }
1509 } else if (sb->region.next == NULL) {
1510 /* InitBufferRegion sets sc_errno */
1511 StreamingBufferRegion *aux_r = sb->region.next = InitBufferRegion(sb, cfg, data_len);
1512 if (aux_r == NULL)
1513 return NULL;
1514 aux_r->stream_offset = data_offset;
1515 DEBUG_VALIDATE_BUG_ON(data_len > aux_r->buf_size);
1516 SCLogDebug("created new region %p with offset %" PRIu64 ", size %u", aux_r,
1517 aux_r->stream_offset, aux_r->buf_size);
1518 return aux_r;
1519 }
1520 /* BufferInsertAtRegionDo sets sc_errno */
1521 StreamingBufferRegion *blob = BufferInsertAtRegionDo(sb, cfg, data_offset, data_len);
1522 SCLogDebug("blob %p (%s)", blob, blob == &sb->region ? "main" : "aux");
1523 return blob;
1524}
1525
1526/**
1527 * \param offset offset relative to StreamingBuffer::stream_offset
1528 *
1529 * \return SC_OK in case of success
1530 * \return SC_E* errors otherwise
1531 */
1533 StreamingBufferSegment *seg, const uint8_t *data, uint32_t data_len, uint64_t offset)
1534{
1535 DEBUG_VALIDATE_BUG_ON(seg == NULL);
1536 DEBUG_VALIDATE_BUG_ON(data_len > BIT_U32(27)); // 128MiB is excessive already
1537 DEBUG_VALIDATE_BUG_ON(offset < sb->region.stream_offset);
1538 if (offset < sb->region.stream_offset) {
1539 return SC_EINVAL;
1540 }
1541
1542 StreamingBufferRegion *region = BufferInsertAtRegion(sb, cfg, data_len, offset);
1543 if (region == NULL) {
1544 return sc_errno;
1545 }
1546
1547 const bool region_is_main = region == &sb->region;
1548
1549 SCLogDebug("inserting %" PRIu64 "/%u using %s region %p", offset, data_len,
1550 region == &sb->region ? "main" : "aux", region);
1551
1552 DEBUG_VALIDATE_BUG_ON(offset - region->stream_offset > UINT32_MAX);
1553 uint32_t rel_offset = (uint32_t)(offset - region->stream_offset);
1554 int r = DataFitsAtOffset(region, data_len, rel_offset);
1555 if (r < 0) {
1557 return SC_ELIMIT;
1558 } else if (r == 0) {
1559 if ((r = GrowToSize(sb, cfg, (rel_offset + data_len))) != SC_OK)
1560 return r;
1561 }
1562 DEBUG_VALIDATE_BUG_ON(DataFitsAtOffset(region, data_len, rel_offset) != 1);
1563
1564 SCLogDebug("offset %" PRIu64 " data_len %u, rel_offset %u into region offset %" PRIu64
1565 ", buf_offset %u, buf_size %u",
1566 offset, data_len, rel_offset, region->stream_offset, region->buf_offset,
1567 region->buf_size);
1568 memcpy(region->buf + rel_offset, data, data_len);
1569 seg->stream_offset = offset;
1570 seg->segment_len = data_len;
1571
1572 SCLogDebug("rel_offset %u region->stream_offset %" PRIu64 ", buf_offset %u", rel_offset,
1573 region->stream_offset, region->buf_offset);
1574
1575 if (RB_EMPTY(&sb->sbb_tree)) {
1576 SCLogDebug("empty sbb list");
1577
1578 if (region_is_main) {
1579 if (sb->region.stream_offset == offset) {
1580 SCLogDebug("empty sbb list: block exactly what was expected, fall through");
1581 /* empty list, data is exactly what is expected (append),
1582 * so do nothing.
1583 * Update buf_offset if needed, but in case of overlaps it might be beyond us. */
1584 sb->region.buf_offset = MAX(sb->region.buf_offset, rel_offset + data_len);
1585 } else if ((rel_offset + data_len) <= sb->region.buf_offset) {
1586 SCLogDebug("empty sbb list: block is within existing main data region");
1587 } else {
1588 if (sb->region.buf_offset && rel_offset == sb->region.buf_offset) {
1589 SCLogDebug("exactly at expected offset");
1590 // nothing to do
1591 sb->region.buf_offset = rel_offset + data_len;
1592
1593 } else if (rel_offset < sb->region.buf_offset) {
1594 // nothing to do
1595
1596 SCLogDebug("before expected offset: %u < sb->region.buf_offset %u", rel_offset,
1597 sb->region.buf_offset);
1598 if (rel_offset + data_len > sb->region.buf_offset) {
1599 SCLogDebug("before expected offset, ends after: %u < sb->region.buf_offset "
1600 "%u, %u > %u",
1601 rel_offset, sb->region.buf_offset, rel_offset + data_len,
1602 sb->region.buf_offset);
1603 sb->region.buf_offset = rel_offset + data_len;
1604 }
1605
1606 } else if (sb->region.buf_offset) {
1607 SCLogDebug("beyond expected offset: SBBInit");
1608 /* existing data, but there is a gap between us */
1609 if ((r = SBBInit(sb, cfg, region, rel_offset, data_len)) != SC_OK)
1610 return r;
1611 } else {
1612 /* gap before data in empty list */
1613 SCLogDebug("empty sbb list: invoking SBBInitLeadingGap");
1614 if ((r = SBBInitLeadingGap(sb, cfg, offset, data_len)) != SC_OK)
1615 return r;
1616 }
1617 }
1618 } else {
1619 if (sb->region.buf_offset) {
1620 /* existing data, but there is a gap between us */
1621 SCLogDebug("empty sbb list, no data in main: use SBBInit");
1622 if ((r = SBBInit(sb, cfg, region, rel_offset, data_len)) != SC_OK)
1623 return r;
1624 } else {
1625 /* gap before data in empty list */
1626 SCLogDebug("empty sbb list: invoking SBBInitLeadingGap");
1627 if ((r = SBBInitLeadingGap(sb, cfg, offset, data_len)) != SC_OK)
1628 return r;
1629 }
1630 if (rel_offset == region->buf_offset) {
1631 SCLogDebug("pre region->buf_offset %u", region->buf_offset);
1632 region->buf_offset = rel_offset + data_len;
1633 SCLogDebug("post region->buf_offset %u", region->buf_offset);
1634 }
1635 }
1636 } else {
1637 SCLogDebug("updating sbb tree");
1638 /* already have blocks, so append new block based on new data */
1639 if ((r = SBBUpdate(sb, cfg, region, rel_offset, data_len)) != SC_OK)
1640 return r;
1641 }
1642 DEBUG_VALIDATE_BUG_ON(!region_is_main && sb->head == NULL);
1643
1644 ListRegions(sb);
1645 if (RB_EMPTY(&sb->sbb_tree)) {
1647 }
1648
1649 return SC_OK;
1650}
1651
1653 const StreamingBufferSegment *seg)
1654{
1655 if (seg->stream_offset < sb->region.stream_offset) {
1656 if (seg->stream_offset + seg->segment_len <= sb->region.stream_offset) {
1657 return 1;
1658 }
1659 }
1660 return 0;
1661}
1662
1663static inline const StreamingBufferRegion *GetRegionForOffset(
1664 const StreamingBuffer *sb, const uint64_t offset)
1665{
1666 if (sb == NULL)
1667 return NULL;
1668 if (sb->region.next == NULL) {
1669 return &sb->region;
1670 }
1671 if (offset >= sb->region.stream_offset &&
1672 offset < (sb->region.stream_offset + sb->region.buf_size)) {
1673 return &sb->region;
1674 }
1675 for (const StreamingBufferRegion *r = sb->region.next; r != NULL; r = r->next) {
1676 if (offset >= r->stream_offset && offset < (r->stream_offset + r->buf_size)) {
1677 return r;
1678 }
1679 }
1680 return NULL;
1681}
1682
1683/** \brief get the data for one SBB */
1685 const StreamingBufferBlock *sbb,
1686 const uint8_t **data, uint32_t *data_len)
1687{
1688 const StreamingBufferRegion *region = GetRegionForOffset(sb, sbb->offset);
1689 SCLogDebug("first find our region (offset %" PRIu64 ") -> %p", sbb->offset, region);
1690 if (region) {
1691 SCLogDebug("region %p found %" PRIu64 "/%u/%u", region, region->stream_offset,
1692 region->buf_size, region->buf_offset);
1694 region->stream_offset == sbb->offset && region->buf_offset > sbb->len);
1695 // buf_offset should match first sbb len if it has the same offset
1696
1697 if (sbb->offset >= region->stream_offset) {
1698 SCLogDebug("1");
1699 uint64_t offset = sbb->offset - region->stream_offset;
1700 *data = region->buf + offset;
1701 DEBUG_VALIDATE_BUG_ON(offset + sbb->len > region->buf_size);
1702 *data_len = sbb->len;
1703 return;
1704 } else {
1705 SCLogDebug("2");
1706 uint32_t offset = (uint32_t)(region->stream_offset - sbb->offset);
1707 if (offset < sbb->len) {
1708 *data = region->buf;
1709 *data_len = sbb->len - offset;
1710 return;
1711 }
1712 SCLogDebug("3");
1713 }
1714 }
1715 *data = NULL;
1716 *data_len = 0;
1717}
1718
1719/** \brief get the data for one SBB */
1721 const StreamingBufferBlock *sbb,
1722 const uint8_t **data, uint32_t *data_len,
1723 uint64_t offset)
1724{
1725 /* validate that we are looking for a offset within the sbb */
1726 DEBUG_VALIDATE_BUG_ON(!(offset >= sbb->offset && offset < (sbb->offset + sbb->len)));
1727 if (!(offset >= sbb->offset && offset < (sbb->offset + sbb->len))) {
1728 *data = NULL;
1729 *data_len = 0;
1730 return;
1731 }
1732
1733 const StreamingBufferRegion *region = GetRegionForOffset(sb, offset);
1734 if (region) {
1735 DEBUG_VALIDATE_BUG_ON(sbb->len - (offset - sbb->offset) > UINT32_MAX);
1736 uint32_t sbblen = (uint32_t)(sbb->len - (offset - sbb->offset));
1737
1738 if (offset >= region->stream_offset) {
1739 uint32_t data_offset = (uint32_t)(offset - region->stream_offset);
1740 *data = region->buf + data_offset;
1741 if (data_offset + sbblen > region->buf_size) {
1742 *data_len = region->buf_size - data_offset;
1743 } else {
1744 *data_len = sbblen;
1745 }
1746 DEBUG_VALIDATE_BUG_ON(*data_len > sbblen);
1747 return;
1748 } else {
1749 uint32_t data_offset = (uint32_t)(region->stream_offset - sbb->offset);
1750 if (data_offset < sbblen) {
1751 *data = region->buf;
1752 *data_len = sbblen - data_offset;
1753 DEBUG_VALIDATE_BUG_ON(*data_len > sbblen);
1754 return;
1755 }
1756 }
1757 }
1758
1759 *data = NULL;
1760 *data_len = 0;
1761}
1762
1764 const StreamingBufferSegment *seg,
1765 const uint8_t **data, uint32_t *data_len)
1766{
1767 const StreamingBufferRegion *region = GetRegionForOffset(sb, seg->stream_offset);
1768 if (region) {
1769 if (seg->stream_offset >= region->stream_offset) {
1770 uint32_t offset = (uint32_t)(seg->stream_offset - region->stream_offset);
1771 *data = region->buf + offset;
1772 if (offset + seg->segment_len > region->buf_size) {
1773 *data_len = region->buf_size - offset;
1774 } else {
1775 *data_len = seg->segment_len;
1776 }
1777 SCLogDebug("*data_len %u", *data_len);
1778 return;
1779 } else {
1780 uint32_t offset = (uint32_t)(region->stream_offset - seg->stream_offset);
1781 if (offset < seg->segment_len) {
1782 *data = region->buf;
1783 *data_len = seg->segment_len - offset;
1784 SCLogDebug("*data_len %u", *data_len);
1785 return;
1786 }
1787 }
1788 }
1789 *data = NULL;
1790 *data_len = 0;
1791}
1792
1793/**
1794 * \retval 1 data is the same
1795 * \retval 0 data is different
1796 */
1798 const StreamingBufferSegment *seg,
1799 const uint8_t *rawdata, uint32_t rawdata_len)
1800{
1801 const uint8_t *segdata = NULL;
1802 uint32_t segdata_len = 0;
1803 StreamingBufferSegmentGetData(sb, seg, &segdata, &segdata_len);
1804 if (segdata && segdata_len &&
1805 segdata_len == rawdata_len &&
1806 memcmp(segdata, rawdata, segdata_len) == 0)
1807 {
1808 return 1;
1809 }
1810 return 0;
1811}
1812
1814 const uint8_t **data, uint32_t *data_len,
1815 uint64_t *stream_offset)
1816{
1817 if (sb != NULL && sb->region.buf != NULL) {
1818 *data = sb->region.buf;
1819 *data_len = sb->region.buf_offset;
1821 return 1;
1822 } else {
1823 *data = NULL;
1824 *data_len = 0;
1825 *stream_offset = 0;
1826 return 0;
1827 }
1828}
1829
1831 const uint8_t **data, uint32_t *data_len,
1832 uint64_t offset)
1833{
1834 const StreamingBufferRegion *region = GetRegionForOffset(sb, offset);
1835 if (region != NULL && region->buf != NULL && offset >= region->stream_offset &&
1836 offset < (region->stream_offset + region->buf_offset)) {
1837 DEBUG_VALIDATE_BUG_ON(offset - region->stream_offset > UINT32_MAX);
1838 uint32_t skip = (uint32_t)(offset - region->stream_offset);
1839 *data = region->buf + skip;
1840 *data_len = region->buf_offset - skip;
1841 return 1;
1842 } else {
1843 *data = NULL;
1844 *data_len = 0;
1845 return 0;
1846 }
1847}
1848
1849/**
1850 * \retval 1 data is the same
1851 * \retval 0 data is different
1852 */
1854 const uint8_t *rawdata, uint32_t rawdata_len)
1855{
1856 const uint8_t *sbdata = NULL;
1857 uint32_t sbdata_len = 0;
1858 uint64_t offset = 0;
1859 StreamingBufferGetData(sb, &sbdata, &sbdata_len, &offset);
1860 if (offset == 0 &&
1861 sbdata && sbdata_len &&
1862 sbdata_len == rawdata_len &&
1863 memcmp(sbdata, rawdata, sbdata_len) == 0)
1864 {
1865 return 1;
1866 }
1867 SCLogDebug("sbdata_len %u, offset %" PRIu64, sbdata_len, offset);
1868 printf("got:\n");
1869 PrintRawDataFp(stdout, sbdata,sbdata_len);
1870 printf("wanted:\n");
1871 PrintRawDataFp(stdout, rawdata,rawdata_len);
1872 return 0;
1873}
1874
1875#ifdef UNITTESTS
1876static void Dump(StreamingBuffer *sb)
1877{
1878 PrintRawDataFp(stdout, sb->region.buf, sb->region.buf_offset);
1879}
1880
1881static void DumpSegment(StreamingBuffer *sb, StreamingBufferSegment *seg)
1882{
1883 const uint8_t *data = NULL;
1884 uint32_t data_len = 0;
1885 StreamingBufferSegmentGetData(sb, seg, &data, &data_len);
1886 if (data && data_len) {
1887 PrintRawDataFp(stdout, data, data_len);
1888 }
1889}
1890
1891static int StreamingBufferTest02(void)
1892{
1893 StreamingBufferConfig cfg = { 24, 1, STREAMING_BUFFER_REGION_GAP_DEFAULT, NULL, NULL, NULL };
1895 FAIL_IF(sb == NULL);
1896
1897 StreamingBufferSegment seg1;
1898 FAIL_IF(StreamingBufferAppend(sb, &cfg, &seg1, (const uint8_t *)"ABCDEFGH", 8) != 0);
1899 StreamingBufferSegment seg2;
1900 FAIL_IF(StreamingBufferAppend(sb, &cfg, &seg2, (const uint8_t *)"01234567", 8) != 0);
1901 FAIL_IF(sb->region.stream_offset != 0);
1902 FAIL_IF(sb->region.buf_offset != 16);
1903 FAIL_IF(seg1.stream_offset != 0);
1904 FAIL_IF(seg2.stream_offset != 8);
1907 Dump(sb);
1908 DumpSegment(sb, &seg1);
1909 DumpSegment(sb, &seg2);
1911 FAIL_IF_NOT(sb->head == RB_MIN(SBB, &sb->sbb_tree));
1912
1913 StreamingBufferSlideToOffset(sb, &cfg, 6);
1915 FAIL_IF_NOT(sb->head == RB_MIN(SBB, &sb->sbb_tree));
1916
1917 StreamingBufferSegment seg3;
1918 FAIL_IF(StreamingBufferAppend(sb, &cfg, &seg3, (const uint8_t *)"QWERTY", 6) != 0);
1919 FAIL_IF(sb->region.stream_offset != 6);
1920 FAIL_IF(sb->region.buf_offset != 16);
1921 FAIL_IF(seg3.stream_offset != 16);
1925 Dump(sb);
1926 DumpSegment(sb, &seg1);
1927 DumpSegment(sb, &seg2);
1928 DumpSegment(sb, &seg3);
1930 FAIL_IF_NOT(sb->head == RB_MIN(SBB, &sb->sbb_tree));
1931
1932 StreamingBufferSlideToOffset(sb, &cfg, 12);
1936 Dump(sb);
1937 DumpSegment(sb, &seg1);
1938 DumpSegment(sb, &seg2);
1939 DumpSegment(sb, &seg3);
1941 FAIL_IF_NOT(sb->head == RB_MIN(SBB, &sb->sbb_tree));
1942
1943 StreamingBufferFree(sb, &cfg);
1944 PASS;
1945}
1946
1947static int StreamingBufferTest03(void)
1948{
1949 StreamingBufferConfig cfg = { 24, 1, STREAMING_BUFFER_REGION_GAP_DEFAULT, NULL, NULL, NULL };
1951 FAIL_IF(sb == NULL);
1952
1953 StreamingBufferSegment seg1;
1954 FAIL_IF(StreamingBufferAppend(sb, &cfg, &seg1, (const uint8_t *)"ABCDEFGH", 8) != 0);
1955 StreamingBufferSegment seg2;
1956 FAIL_IF(StreamingBufferInsertAt(sb, &cfg, &seg2, (const uint8_t *)"01234567", 8, 14) != 0);
1957 FAIL_IF(sb->region.stream_offset != 0);
1958 FAIL_IF(sb->region.buf_offset != 8);
1959 FAIL_IF(seg1.stream_offset != 0);
1960 FAIL_IF(seg2.stream_offset != 14);
1963 Dump(sb);
1964 DumpSegment(sb, &seg1);
1965 DumpSegment(sb, &seg2);
1966 FAIL_IF_NULL(sb->head);
1967 FAIL_IF_NOT(sb->sbb_size == 16);
1968 FAIL_IF_NOT(sb->head == RB_MIN(SBB, &sb->sbb_tree));
1969
1970 StreamingBufferSegment seg3;
1971 FAIL_IF(StreamingBufferInsertAt(sb, &cfg, &seg3, (const uint8_t *)"QWERTY", 6, 8) != 0);
1972 FAIL_IF(sb->region.stream_offset != 0);
1973 FAIL_IF(sb->region.buf_offset != 22);
1974 FAIL_IF(seg3.stream_offset != 8);
1978 Dump(sb);
1979 DumpSegment(sb, &seg1);
1980 DumpSegment(sb, &seg2);
1981 DumpSegment(sb, &seg3);
1982 FAIL_IF_NULL(sb->head);
1983 FAIL_IF_NOT(sb->sbb_size == 22);
1984 FAIL_IF_NOT(sb->head == RB_MIN(SBB, &sb->sbb_tree));
1985
1986 StreamingBufferSlideToOffset(sb, &cfg, 10);
1990 Dump(sb);
1991 DumpSegment(sb, &seg1);
1992 DumpSegment(sb, &seg2);
1993 DumpSegment(sb, &seg3);
1994 FAIL_IF_NULL(sb->head);
1995 FAIL_IF_NOT(sb->sbb_size == 12);
1996 FAIL_IF_NOT(sb->head == RB_MIN(SBB, &sb->sbb_tree));
1997
1998 StreamingBufferFree(sb, &cfg);
1999 PASS;
2000}
2001
2002static int StreamingBufferTest04(void)
2003{
2004 StreamingBufferConfig cfg = { 16, 1, STREAMING_BUFFER_REGION_GAP_DEFAULT, NULL, NULL, NULL };
2006 FAIL_IF(sb == NULL);
2007
2008 StreamingBufferSegment seg1;
2009 FAIL_IF(StreamingBufferAppend(sb, &cfg, &seg1, (const uint8_t *)"ABCDEFGH", 8) != 0);
2010 FAIL_IF(!RB_EMPTY(&sb->sbb_tree));
2011 StreamingBufferSegment seg2;
2012 FAIL_IF(StreamingBufferInsertAt(sb, &cfg, &seg2, (const uint8_t *)"01234567", 8, 14) != 0);
2013 FAIL_IF(sb->region.stream_offset != 0);
2014 FAIL_IF(sb->region.buf_offset != 8);
2015 FAIL_IF(seg1.stream_offset != 0);
2016 FAIL_IF(seg2.stream_offset != 14);
2019 FAIL_IF(RB_EMPTY(&sb->sbb_tree));
2020 StreamingBufferBlock *sbb1 = RB_MIN(SBB, &sb->sbb_tree);
2021 FAIL_IF(sbb1 != sb->head);
2022 FAIL_IF_NULL(sbb1);
2023 FAIL_IF(sbb1->offset != 0);
2024 FAIL_IF(sbb1->len != 8);
2025 StreamingBufferBlock *sbb2 = SBB_RB_NEXT(sbb1);
2026 FAIL_IF_NULL(sbb2);
2027 FAIL_IF(sbb2 == sb->head);
2028 FAIL_IF(sbb2->offset != 14);
2029 FAIL_IF(sbb2->len != 8);
2030 Dump(sb);
2031 DumpSegment(sb, &seg1);
2032 DumpSegment(sb, &seg2);
2033 FAIL_IF_NOT(sb->head == RB_MIN(SBB, &sb->sbb_tree));
2034
2035 StreamingBufferSegment seg3;
2036 FAIL_IF(StreamingBufferInsertAt(sb, &cfg, &seg3, (const uint8_t *)"QWERTY", 6, 8) != 0);
2037 FAIL_IF(sb->region.stream_offset != 0);
2038 FAIL_IF(sb->region.buf_offset != 22);
2039 FAIL_IF(seg3.stream_offset != 8);
2043 sbb1 = RB_MIN(SBB, &sb->sbb_tree);
2044 FAIL_IF_NULL(sbb1);
2045 FAIL_IF(sbb1 != sb->head);
2046 FAIL_IF(sbb1->offset != 0);
2047 FAIL_IF(sbb1->len != 22);
2048 FAIL_IF(SBB_RB_NEXT(sbb1));
2049 Dump(sb);
2050 DumpSegment(sb, &seg1);
2051 DumpSegment(sb, &seg2);
2052 DumpSegment(sb, &seg3);
2053 FAIL_IF_NULL(sb->head);
2054 FAIL_IF_NOT(sb->sbb_size == 22);
2055 FAIL_IF_NOT(sb->head == RB_MIN(SBB, &sb->sbb_tree));
2056
2057 /* far ahead of curve: */
2058 StreamingBufferSegment seg4;
2059 FAIL_IF(StreamingBufferInsertAt(sb, &cfg, &seg4, (const uint8_t *)"XYZ", 3, 124) != 0);
2060 FAIL_IF(sb->region.stream_offset != 0);
2061 FAIL_IF(sb->region.buf_offset != 22);
2062 FAIL_IF(sb->region.buf_size != 128);
2063 FAIL_IF(seg4.stream_offset != 124);
2068 sbb1 = RB_MIN(SBB, &sb->sbb_tree);
2069 FAIL_IF_NULL(sbb1);
2070 FAIL_IF(sbb1 != sb->head);
2071 FAIL_IF(sbb1->offset != 0);
2072 FAIL_IF(sbb1->len != 22);
2073 FAIL_IF(!SBB_RB_NEXT(sbb1));
2074 Dump(sb);
2075 DumpSegment(sb, &seg1);
2076 DumpSegment(sb, &seg2);
2077 DumpSegment(sb, &seg3);
2078 DumpSegment(sb, &seg4);
2079 FAIL_IF_NULL(sb->head);
2080 FAIL_IF_NOT(sb->sbb_size == 25);
2081 FAIL_IF_NOT(sb->head == RB_MIN(SBB, &sb->sbb_tree));
2082
2083 FAIL_IF(!StreamingBufferSegmentCompareRawData(sb,&seg1,(const uint8_t *)"ABCDEFGH", 8));
2084 FAIL_IF(!StreamingBufferSegmentCompareRawData(sb,&seg2,(const uint8_t *)"01234567", 8));
2085 FAIL_IF(!StreamingBufferSegmentCompareRawData(sb,&seg3,(const uint8_t *)"QWERTY", 6));
2086 FAIL_IF(!StreamingBufferSegmentCompareRawData(sb,&seg4,(const uint8_t *)"XYZ", 3));
2087
2088 StreamingBufferFree(sb, &cfg);
2089 PASS;
2090}
2091
2092/** \test lots of gaps in block list */
2093static int StreamingBufferTest06(void)
2094{
2095 StreamingBufferConfig cfg = { 16, 1, STREAMING_BUFFER_REGION_GAP_DEFAULT, NULL, NULL, NULL };
2097 FAIL_IF(sb == NULL);
2098
2099 StreamingBufferSegment seg1;
2100 FAIL_IF(StreamingBufferAppend(sb, &cfg, &seg1, (const uint8_t *)"A", 1) != 0);
2101 StreamingBufferSegment seg2;
2102 FAIL_IF(StreamingBufferInsertAt(sb, &cfg, &seg2, (const uint8_t *)"C", 1, 2) != 0);
2103 Dump(sb);
2104 FAIL_IF_NULL(sb->head);
2105 FAIL_IF_NOT(sb->sbb_size == 2);
2106 FAIL_IF_NOT(sb->head == RB_MIN(SBB, &sb->sbb_tree));
2107
2108 StreamingBufferSegment seg3;
2109 FAIL_IF(StreamingBufferInsertAt(sb, &cfg, &seg3, (const uint8_t *)"F", 1, 5) != 0);
2110 Dump(sb);
2111 FAIL_IF_NULL(sb->head);
2112 FAIL_IF_NOT(sb->sbb_size == 3);
2113 FAIL_IF_NOT(sb->head == RB_MIN(SBB, &sb->sbb_tree));
2114
2115 StreamingBufferSegment seg4;
2116 FAIL_IF(StreamingBufferInsertAt(sb, &cfg, &seg4, (const uint8_t *)"H", 1, 7) != 0);
2117 Dump(sb);
2118 FAIL_IF_NULL(sb->head);
2119 FAIL_IF_NOT(sb->sbb_size == 4);
2120 FAIL_IF_NOT(sb->head == RB_MIN(SBB, &sb->sbb_tree));
2121
2122 StreamingBufferSegment seg5;
2123 FAIL_IF(StreamingBufferInsertAt(sb, &cfg, &seg5, (const uint8_t *)"ABCDEFGHIJ", 10, 0) != 0);
2124 Dump(sb);
2125 StreamingBufferBlock *sbb1 = RB_MIN(SBB, &sb->sbb_tree);
2126 FAIL_IF_NULL(sbb1);
2127 FAIL_IF(sbb1->offset != 0);
2128 FAIL_IF(sbb1->len != 10);
2129 FAIL_IF(SBB_RB_NEXT(sbb1));
2130 FAIL_IF_NULL(sb->head);
2131 FAIL_IF_NOT(sb->sbb_size == 10);
2132 FAIL_IF_NOT(sb->head == RB_MIN(SBB, &sb->sbb_tree));
2133
2134 StreamingBufferSegment seg6;
2135 FAIL_IF(StreamingBufferInsertAt(sb, &cfg, &seg6, (const uint8_t *)"abcdefghij", 10, 0) != 0);
2136 Dump(sb);
2137 sbb1 = RB_MIN(SBB, &sb->sbb_tree);
2138 FAIL_IF_NULL(sbb1);
2139 FAIL_IF(sbb1->offset != 0);
2140 FAIL_IF(sbb1->len != 10);
2141 FAIL_IF(SBB_RB_NEXT(sbb1));
2142 FAIL_IF_NULL(sb->head);
2143 FAIL_IF_NOT(sb->sbb_size == 10);
2144 FAIL_IF_NOT(sb->head == RB_MIN(SBB, &sb->sbb_tree));
2145
2146 StreamingBufferFree(sb, &cfg);
2147 PASS;
2148}
2149
2150/** \test lots of gaps in block list */
2151static int StreamingBufferTest07(void)
2152{
2153 StreamingBufferConfig cfg = { 16, 1, STREAMING_BUFFER_REGION_GAP_DEFAULT, NULL, NULL, NULL };
2155 FAIL_IF(sb == NULL);
2156
2157 StreamingBufferSegment seg1;
2158 FAIL_IF(StreamingBufferInsertAt(sb, &cfg, &seg1, (const uint8_t *)"B", 1, 1) != 0);
2159 StreamingBufferSegment seg2;
2160 FAIL_IF(StreamingBufferInsertAt(sb, &cfg, &seg2, (const uint8_t *)"D", 1, 3) != 0);
2161 Dump(sb);
2162 FAIL_IF_NULL(sb->head);
2163 FAIL_IF_NOT(sb->sbb_size == 2);
2164 FAIL_IF_NOT(sb->head == RB_MIN(SBB, &sb->sbb_tree));
2165
2166 StreamingBufferSegment seg3;
2167 FAIL_IF(StreamingBufferInsertAt(sb, &cfg, &seg3, (const uint8_t *)"F", 1, 5) != 0);
2168 Dump(sb);
2169 FAIL_IF_NULL(sb->head);
2170 FAIL_IF_NOT(sb->sbb_size == 3);
2171 FAIL_IF_NOT(sb->head == RB_MIN(SBB, &sb->sbb_tree));
2172
2173 StreamingBufferSegment seg4;
2174 FAIL_IF(StreamingBufferInsertAt(sb, &cfg, &seg4, (const uint8_t *)"H", 1, 7) != 0);
2175 Dump(sb);
2176 FAIL_IF_NULL(sb->head);
2177 FAIL_IF_NOT(sb->sbb_size == 4);
2178 FAIL_IF_NOT(sb->head == RB_MIN(SBB, &sb->sbb_tree));
2179
2180 StreamingBufferSegment seg5;
2181 FAIL_IF(StreamingBufferInsertAt(sb, &cfg, &seg5, (const uint8_t *)"ABCDEFGHIJ", 10, 0) != 0);
2182 Dump(sb);
2183 StreamingBufferBlock *sbb1 = RB_MIN(SBB, &sb->sbb_tree);
2184 FAIL_IF_NULL(sbb1);
2185 FAIL_IF(sbb1->offset != 0);
2186 FAIL_IF(sbb1->len != 10);
2187 FAIL_IF(SBB_RB_NEXT(sbb1));
2188 FAIL_IF_NULL(sb->head);
2189 FAIL_IF_NOT(sb->sbb_size == 10);
2190 FAIL_IF_NOT(sb->head == RB_MIN(SBB, &sb->sbb_tree));
2191
2192 StreamingBufferSegment seg6;
2193 FAIL_IF(StreamingBufferInsertAt(sb, &cfg, &seg6, (const uint8_t *)"abcdefghij", 10, 0) != 0);
2194 Dump(sb);
2195 sbb1 = RB_MIN(SBB, &sb->sbb_tree);
2196 FAIL_IF_NULL(sbb1);
2197 FAIL_IF(sbb1->offset != 0);
2198 FAIL_IF(sbb1->len != 10);
2199 FAIL_IF(SBB_RB_NEXT(sbb1));
2200 FAIL_IF_NULL(sb->head);
2201 FAIL_IF_NOT(sb->sbb_size == 10);
2202 FAIL_IF_NOT(sb->head == RB_MIN(SBB, &sb->sbb_tree));
2203
2204 StreamingBufferFree(sb, &cfg);
2205 PASS;
2206}
2207
2208/** \test lots of gaps in block list */
2209static int StreamingBufferTest08(void)
2210{
2211 StreamingBufferConfig cfg = { 16, 1, STREAMING_BUFFER_REGION_GAP_DEFAULT, NULL, NULL, NULL };
2213 FAIL_IF(sb == NULL);
2214
2215 StreamingBufferSegment seg1;
2216 FAIL_IF(StreamingBufferInsertAt(sb, &cfg, &seg1, (const uint8_t *)"B", 1, 1) != 0);
2217 StreamingBufferSegment seg2;
2218 FAIL_IF(StreamingBufferInsertAt(sb, &cfg, &seg2, (const uint8_t *)"D", 1, 3) != 0);
2219 Dump(sb);
2220 FAIL_IF_NULL(sb->head);
2221 FAIL_IF_NOT(sb->sbb_size == 2);
2222 FAIL_IF_NOT(sb->head == RB_MIN(SBB, &sb->sbb_tree));
2223
2224 StreamingBufferSegment seg3;
2225 FAIL_IF(StreamingBufferInsertAt(sb, &cfg, &seg3, (const uint8_t *)"F", 1, 5) != 0);
2226 Dump(sb);
2227 FAIL_IF_NULL(sb->head);
2228 FAIL_IF_NOT(sb->sbb_size == 3);
2229 FAIL_IF_NOT(sb->head == RB_MIN(SBB, &sb->sbb_tree));
2230
2231 StreamingBufferSegment seg4;
2232 FAIL_IF(StreamingBufferInsertAt(sb, &cfg, &seg4, (const uint8_t *)"H", 1, 7) != 0);
2233 Dump(sb);
2234 FAIL_IF_NULL(sb->head);
2235 FAIL_IF_NOT(sb->sbb_size == 4);
2236 FAIL_IF_NOT(sb->head == RB_MIN(SBB, &sb->sbb_tree));
2237
2238 StreamingBufferSegment seg5;
2239 FAIL_IF(StreamingBufferInsertAt(sb, &cfg, &seg5, (const uint8_t *)"ABCDEFGHIJ", 10, 0) != 0);
2240 Dump(sb);
2241 StreamingBufferBlock *sbb1 = RB_MIN(SBB, &sb->sbb_tree);
2242 FAIL_IF_NULL(sbb1);
2243 FAIL_IF(sbb1->offset != 0);
2244 FAIL_IF(sbb1->len != 10);
2245 FAIL_IF(SBB_RB_NEXT(sbb1));
2246 FAIL_IF_NULL(sb->head);
2247 FAIL_IF_NOT(sb->sbb_size == 10);
2248 FAIL_IF_NOT(sb->head == RB_MIN(SBB, &sb->sbb_tree));
2249
2250 StreamingBufferSegment seg6;
2251 FAIL_IF(StreamingBufferAppend(sb, &cfg, &seg6, (const uint8_t *)"abcdefghij", 10) != 0);
2252 Dump(sb);
2253 sbb1 = RB_MIN(SBB, &sb->sbb_tree);
2254 FAIL_IF_NULL(sbb1);
2255 FAIL_IF(sbb1->offset != 0);
2256 FAIL_IF(sbb1->len != 20);
2257 FAIL_IF(SBB_RB_NEXT(sbb1));
2258 FAIL_IF_NULL(sb->head);
2259 FAIL_IF_NOT(sb->sbb_size == 20);
2260 FAIL_IF_NOT(sb->head == RB_MIN(SBB, &sb->sbb_tree));
2261
2262 StreamingBufferFree(sb, &cfg);
2263 PASS;
2264}
2265
2266/** \test lots of gaps in block list */
2267static int StreamingBufferTest09(void)
2268{
2269 StreamingBufferConfig cfg = { 16, 1, STREAMING_BUFFER_REGION_GAP_DEFAULT, NULL, NULL, NULL };
2271 FAIL_IF(sb == NULL);
2272
2273 StreamingBufferSegment seg1;
2274 FAIL_IF(StreamingBufferInsertAt(sb, &cfg, &seg1, (const uint8_t *)"B", 1, 1) != 0);
2275 StreamingBufferSegment seg2;
2276 FAIL_IF(StreamingBufferInsertAt(sb, &cfg, &seg2, (const uint8_t *)"D", 1, 3) != 0);
2277 Dump(sb);
2278 FAIL_IF_NULL(sb->head);
2279 FAIL_IF_NOT(sb->sbb_size == 2);
2280 FAIL_IF_NOT(sb->head == RB_MIN(SBB, &sb->sbb_tree));
2281
2282 StreamingBufferSegment seg3;
2283 FAIL_IF(StreamingBufferInsertAt(sb, &cfg, &seg3, (const uint8_t *)"H", 1, 7) != 0);
2284 Dump(sb);
2285 FAIL_IF_NULL(sb->head);
2286 FAIL_IF_NOT(sb->sbb_size == 3);
2287 FAIL_IF_NOT(sb->head == RB_MIN(SBB, &sb->sbb_tree));
2288
2289 StreamingBufferSegment seg4;
2290 FAIL_IF(StreamingBufferInsertAt(sb, &cfg, &seg4, (const uint8_t *)"F", 1, 5) != 0);
2291 Dump(sb);
2292 FAIL_IF_NULL(sb->head);
2293 FAIL_IF_NOT(sb->sbb_size == 4);
2294 FAIL_IF_NOT(sb->head == RB_MIN(SBB, &sb->sbb_tree));
2295
2296 StreamingBufferSegment seg5;
2297 FAIL_IF(StreamingBufferInsertAt(sb, &cfg, &seg5, (const uint8_t *)"ABCDEFGHIJ", 10, 0) != 0);
2298 Dump(sb);
2299 StreamingBufferBlock *sbb1 = RB_MIN(SBB, &sb->sbb_tree);
2300 FAIL_IF_NULL(sbb1);
2301 FAIL_IF(sbb1->offset != 0);
2302 FAIL_IF(sbb1->len != 10);
2303 FAIL_IF(SBB_RB_NEXT(sbb1));
2304 FAIL_IF_NULL(sb->head);
2305 FAIL_IF_NOT(sb->sbb_size == 10);
2306 FAIL_IF_NOT(sb->head == RB_MIN(SBB, &sb->sbb_tree));
2307
2308 StreamingBufferSegment seg6;
2309 FAIL_IF(StreamingBufferInsertAt(sb, &cfg, &seg6, (const uint8_t *)"abcdefghij", 10, 0) != 0);
2310 Dump(sb);
2311 sbb1 = RB_MIN(SBB, &sb->sbb_tree);
2312 FAIL_IF_NULL(sbb1);
2313 FAIL_IF(sbb1->offset != 0);
2314 FAIL_IF(sbb1->len != 10);
2315 FAIL_IF(SBB_RB_NEXT(sbb1));
2316 FAIL_IF_NULL(sb->head);
2317 FAIL_IF_NOT(sb->sbb_size == 10);
2318 FAIL_IF_NOT(sb->head == RB_MIN(SBB, &sb->sbb_tree));
2319
2320 StreamingBufferFree(sb, &cfg);
2321 PASS;
2322}
2323
2324/** \test lots of gaps in block list */
2325static int StreamingBufferTest10(void)
2326{
2327 StreamingBufferConfig cfg = { 16, 1, STREAMING_BUFFER_REGION_GAP_DEFAULT, NULL, NULL, NULL };
2329 FAIL_IF(sb == NULL);
2330
2331 StreamingBufferSegment seg1;
2332 FAIL_IF(StreamingBufferInsertAt(sb, &cfg, &seg1, (const uint8_t *)"A", 1, 0) != 0);
2333 Dump(sb);
2334 StreamingBufferSegment seg2;
2335 FAIL_IF(StreamingBufferInsertAt(sb, &cfg, &seg2, (const uint8_t *)"D", 1, 3) != 0);
2336 Dump(sb);
2337 StreamingBufferSegment seg3;
2338 FAIL_IF(StreamingBufferInsertAt(sb, &cfg, &seg3, (const uint8_t *)"H", 1, 7) != 0);
2339 Dump(sb);
2340 FAIL_IF_NULL(sb->head);
2341 FAIL_IF_NOT(sb->sbb_size == 3);
2342
2343 StreamingBufferSegment seg4;
2344 FAIL_IF(StreamingBufferInsertAt(sb, &cfg, &seg4, (const uint8_t *)"B", 1, 1) != 0);
2345 Dump(sb);
2346 StreamingBufferSegment seg5;
2347 FAIL_IF(StreamingBufferInsertAt(sb, &cfg, &seg5, (const uint8_t *)"C", 1, 2) != 0);
2348 Dump(sb);
2349 StreamingBufferSegment seg6;
2350 FAIL_IF(StreamingBufferInsertAt(sb, &cfg, &seg6, (const uint8_t *)"G", 1, 6) != 0);
2351 Dump(sb);
2352 FAIL_IF_NOT(sb->head == RB_MIN(SBB, &sb->sbb_tree));
2353 FAIL_IF_NULL(sb->head);
2354 FAIL_IF_NOT(sb->sbb_size == 6);
2355
2356 StreamingBufferSegment seg7;
2357 FAIL_IF(StreamingBufferInsertAt(sb, &cfg, &seg7, (const uint8_t *)"ABCDEFGHIJ", 10, 0) != 0);
2358 Dump(sb);
2359 StreamingBufferBlock *sbb1 = RB_MIN(SBB, &sb->sbb_tree);
2360 FAIL_IF_NULL(sbb1);
2361 FAIL_IF(sbb1->offset != 0);
2362 FAIL_IF(sbb1->len != 10);
2363 FAIL_IF(SBB_RB_NEXT(sbb1));
2364 FAIL_IF_NULL(sb->head);
2365 FAIL_IF_NOT(sb->sbb_size == 10);
2366
2367 StreamingBufferSegment seg8;
2368 FAIL_IF(StreamingBufferInsertAt(sb, &cfg, &seg8, (const uint8_t *)"abcdefghij", 10, 0) != 0);
2369 Dump(sb);
2370 sbb1 = RB_MIN(SBB, &sb->sbb_tree);
2371 FAIL_IF_NOT(sb->head == RB_MIN(SBB, &sb->sbb_tree));
2372 FAIL_IF_NULL(sbb1);
2373 FAIL_IF(sbb1->offset != 0);
2374 FAIL_IF(sbb1->len != 10);
2375 FAIL_IF(SBB_RB_NEXT(sbb1));
2376 FAIL_IF_NULL(sb->head);
2377 FAIL_IF_NOT(sb->sbb_size == 10);
2378
2379 StreamingBufferFree(sb, &cfg);
2380 PASS;
2381}
2382
2383static int StreamingBufferTest11(void)
2384{
2385 StreamingBufferConfig cfg = { 24, 1, STREAMING_BUFFER_REGION_GAP_DEFAULT, NULL, NULL, NULL };
2387 FAIL_IF(sb == NULL);
2388
2389 StreamingBufferSegment seg1;
2390 FAIL_IF(StreamingBufferAppend(sb, &cfg, &seg1, (const uint8_t *)"ABCDEFGH", 8) != 0);
2391 StreamingBufferSegment seg2;
2392 unsigned int data_len = 0xffffffff;
2393 FAIL_IF(StreamingBufferAppend(sb, &cfg, &seg2, (const uint8_t *)"unused", data_len) != -1);
2395 sb, &cfg, &seg2, (const uint8_t *)"abcdefghij", data_len, 100000) != SC_ELIMIT);
2396 StreamingBufferFree(sb, &cfg);
2397 PASS;
2398}
2399
2400static const char *dummy_conf_string = "%YAML 1.1\n"
2401 "---\n"
2402 "\n"
2403 "app-layer:\n"
2404 " protocols:\n"
2405 " http:\n"
2406 " enabled: yes\n"
2407 " memcap: 88\n"
2408 "\n";
2409
2410static int StreamingBufferTest12(void)
2411{
2413 SCConfInit();
2415 SCConfYamlLoadString((const char *)dummy_conf_string, strlen(dummy_conf_string));
2416 HTPConfigure();
2417
2419 HTPFree };
2421 FAIL_IF(sb == NULL);
2422
2423 StreamingBufferSegment seg1;
2424 FAIL_IF(StreamingBufferAppend(sb, &cfg, &seg1, (const uint8_t *)"ABCDEFGHIJKLMNOP", 16) != 0);
2425
2426 StreamingBufferSegment seg2;
2427 FAIL_IF(StreamingBufferAppend(sb, &cfg, &seg2,
2428 (const uint8_t *)"ABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZ",
2429 52) != -1);
2431
2432 StreamingBufferFree(sb, &cfg);
2435
2436 PASS;
2437}
2438#endif
2439
2441{
2442#ifdef UNITTESTS
2443 UtRegisterTest("StreamingBufferTest02", StreamingBufferTest02);
2444 UtRegisterTest("StreamingBufferTest03", StreamingBufferTest03);
2445 UtRegisterTest("StreamingBufferTest04", StreamingBufferTest04);
2446 UtRegisterTest("StreamingBufferTest06", StreamingBufferTest06);
2447 UtRegisterTest("StreamingBufferTest07", StreamingBufferTest07);
2448 UtRegisterTest("StreamingBufferTest08", StreamingBufferTest08);
2449 UtRegisterTest("StreamingBufferTest09", StreamingBufferTest09);
2450 UtRegisterTest("StreamingBufferTest10", StreamingBufferTest10);
2451 UtRegisterTest("StreamingBufferTest11 Bug 6903", StreamingBufferTest11);
2452 UtRegisterTest("StreamingBufferTest12 Bug 6782", StreamingBufferTest12);
2453#endif
2454}
uint8_t len
uint16_t dst
void * HTPCalloc(size_t n, size_t size)
void * HTPRealloc(void *ptr, size_t orig_size, size_t size)
void HTPFree(void *ptr, size_t size)
struct HtpBodyChunk_ * next
#define WARN_UNUSED
Definition bindgen.h:32
int SCConfYamlLoadString(const char *string, size_t len)
Load configuration from a YAML string.
void SCConfInit(void)
Initialize the configuration system.
Definition conf.c:120
void SCConfCreateContextBackup(void)
Creates a backup of the conf_hash hash_table used by the conf API.
Definition conf.c:684
void SCConfRestoreContextBackup(void)
Restores the backup of the hash_table present in backup_conf_hash back to conf_hash.
Definition conf.c:694
Flow * head
Definition flow-hash.h:1
SCMutex m
Definition flow-hash.h:6
#define FAIL_IF_NULL(expr)
Fail a test if expression evaluates to NULL.
void UtRegisterTest(const char *name, int(*TestFn)(void))
Register unit test.
#define FAIL_IF_NOT(expr)
Fail a test if expression evaluates to false.
#define PASS
Pass the test.
#define FAIL_IF(expr)
Fail a test if expression evaluates to true.
#define FAIL_IF_NOT_NULL(expr)
Fail a test if expression evaluates to non-NULL.
void HtpConfigRestoreBackup(void)
void HtpConfigCreateBackup(void)
void HTPConfigure(void)
struct HtpBodyChunk_ * next
block of continues data
struct StreamingBufferRegion_ * next
StreamingBufferRegion region
StreamingBufferBlock * head
#define BUG_ON(x)
#define MIN(x, y)
#define BIT_U32(n)
#define MAX(x, y)
uint32_t cnt
#define RB_MIN(name, x)
Definition tree.h:778
#define RB_EMPTY(head)
Definition tree.h:327
#define RB_FOREACH(x, name, head)
Definition tree.h:781
#define RB_FOREACH_FROM(x, name, y)
Definition tree.h:786
#define RB_LEFT(elm, field)
Definition tree.h:322
#define RB_RIGHT(elm, field)
Definition tree.h:323
#define RB_FOREACH_SAFE(x, name, head, y)
Definition tree.h:791
#define RB_FOREACH_REVERSE_FROM(x, name, y)
Definition tree.h:801
#define RB_ROOT(head)
Definition tree.h:326
#define RB_GENERATE(name, type, field, cmp)
Definition tree.h:421
#define BOOL2STR(b)
Definition util-debug.h:535
#define SCLogDebug(...)
Definition util-debug.h:275
#define SCReturnInt(x)
Definition util-debug.h:281
#define SCLogWarning(...)
Macro used to log WARNING messages.
Definition util-debug.h:255
thread_local SCError sc_errno
Definition util-error.c:31
@ SC_ELIMIT
Definition util-error.h:31
@ SC_EINVAL
Definition util-error.h:30
@ SC_ENOMEM
Definition util-error.h:29
@ SC_OK
Definition util-error.h:27
#define SCRealloc(ptr, sz)
Definition util-mem.h:50
#define SCCalloc(nm, sz)
Definition util-mem.h:53
#define unlikely(expr)
void PrintRawDataFp(FILE *fp, const uint8_t *buf, uint32_t buflen)
Definition util-print.c:112
void StreamingBufferSegmentGetData(const StreamingBuffer *sb, const StreamingBufferSegment *seg, const uint8_t **data, uint32_t *data_len)
void StreamingBufferSBBGetDataAtOffset(const StreamingBuffer *sb, const StreamingBufferBlock *sbb, const uint8_t **data, uint32_t *data_len, uint64_t offset)
get the data for one SBB
int StreamingBufferAppend(StreamingBuffer *sb, const StreamingBufferConfig *cfg, StreamingBufferSegment *seg, const uint8_t *data, uint32_t data_len)
int StreamingBufferSegmentCompareRawData(const StreamingBuffer *sb, const StreamingBufferSegment *seg, const uint8_t *rawdata, uint32_t rawdata_len)
#define FREE(cfg, ptr, s)
StreamingBuffer * StreamingBufferInit(const StreamingBufferConfig *cfg)
int StreamingBufferGetDataAtOffset(const StreamingBuffer *sb, const uint8_t **data, uint32_t *data_len, uint64_t offset)
int StreamingBufferInsertAt(StreamingBuffer *sb, const StreamingBufferConfig *cfg, StreamingBufferSegment *seg, const uint8_t *data, uint32_t data_len, uint64_t offset)
void StreamingBufferSlideToOffset(StreamingBuffer *sb, const StreamingBufferConfig *cfg, uint64_t offset)
slide to absolute offset
StreamingBufferBlock * SBB_RB_FIND_INCLUSIVE(struct SBB *head, StreamingBufferBlock *elm)
void StreamingBufferRegisterTests(void)
int StreamingBufferCompareRawData(const StreamingBuffer *sb, const uint8_t *rawdata, uint32_t rawdata_len)
void StreamingBufferFree(StreamingBuffer *sb, const StreamingBufferConfig *cfg)
#define REALLOC(cfg, ptr, orig_s, s)
#define CALLOC(cfg, n, s)
void StreamingBufferSBBGetData(const StreamingBuffer *sb, const StreamingBufferBlock *sbb, const uint8_t **data, uint32_t *data_len)
get the data for one SBB
void StreamingBufferClear(StreamingBuffer *sb, const StreamingBufferConfig *cfg)
int SBBCompare(struct StreamingBufferBlock *a, struct StreamingBufferBlock *b)
int StreamingBufferAppendNoTrack(StreamingBuffer *sb, const StreamingBufferConfig *cfg, const uint8_t *data, uint32_t data_len)
add data w/o tracking a segment
int StreamingBufferSegmentIsBeforeWindow(const StreamingBuffer *sb, const StreamingBufferSegment *seg)
int StreamingBufferGetData(const StreamingBuffer *sb, const uint8_t **data, uint32_t *data_len, uint64_t *stream_offset)
uint64_t stream_offset
uint32_t segment_len
uint64_t offset
#define STREAMING_BUFFER_REGION_GAP_DEFAULT
#define DEBUG_VALIDATE_BUG_ON(exp)