Amaury Denoyelle | 1b5f77f | 2022-05-09 09:37:27 +0200 | [diff] [blame^] | 1 | #include <haproxy/ncbuf.h> |
| 2 | |
| 3 | #ifdef DEBUG_DEV |
| 4 | # include <haproxy/bug.h> |
| 5 | #else |
| 6 | # include <stdio.h> |
| 7 | # include <stdlib.h> |
| 8 | |
| 9 | # undef BUG_ON |
| 10 | # define BUG_ON(x) if (x) { fprintf(stderr, "CRASH ON %s:%d\n", __func__, __LINE__); abort(); } |
| 11 | |
| 12 | # undef BUG_ON_HOT |
| 13 | # define BUG_ON_HOT(x) if (x) { fprintf(stderr, "CRASH ON %s:%d\n", __func__, __LINE__); abort(); } |
| 14 | #endif /* DEBUG_DEV */ |
| 15 | |
| 16 | /* ******** internal API ******** */ |
| 17 | |
| 18 | /* Return pointer to <off> relative to <buf> head. Support buffer wrapping. */ |
| 19 | static char *ncb_peek(const struct ncbuf *buf, ncb_sz_t off) |
| 20 | { |
| 21 | char *ptr = ncb_head(buf) + off; |
| 22 | if (ptr >= buf->area + buf->size) |
| 23 | ptr -= buf->size; |
| 24 | return ptr; |
| 25 | } |
| 26 | |
| 27 | /* Returns the reserved space of <buf> which contains the size of the first |
| 28 | * data block. |
| 29 | */ |
| 30 | static char *ncb_reserved(const struct ncbuf *buf) |
| 31 | { |
| 32 | return ncb_peek(buf, buf->size - NCB_RESERVED_SZ); |
| 33 | } |
| 34 | |
| 35 | /* Encode <off> at <st> position in <buf>. Support wrapping. */ |
| 36 | static void ncb_write_off(const struct ncbuf *buf, char *st, ncb_sz_t off) |
| 37 | { |
| 38 | int i; |
| 39 | |
| 40 | BUG_ON_HOT(st >= buf->area + buf->size); |
| 41 | |
| 42 | for (i = 0; i < sizeof(ncb_sz_t); ++i) { |
| 43 | (*st) = off >> (8 * i) & 0xff; |
| 44 | |
| 45 | if ((++st) == ncb_wrap(buf)) |
| 46 | st = ncb_orig(buf); |
| 47 | } |
| 48 | } |
| 49 | |
| 50 | /* Decode offset stored at <st> position in <buf>. Support wrapping. */ |
| 51 | static ncb_sz_t ncb_read_off(const struct ncbuf *buf, char *st) |
| 52 | { |
| 53 | int i; |
| 54 | ncb_sz_t off = 0; |
| 55 | |
| 56 | BUG_ON_HOT(st >= buf->area + buf->size); |
| 57 | |
| 58 | for (i = 0; i < sizeof(ncb_sz_t); ++i) { |
| 59 | off |= (unsigned char )(*st) << (8 * i); |
| 60 | |
| 61 | if ((++st) == ncb_wrap(buf)) |
| 62 | st = ncb_orig(buf); |
| 63 | } |
| 64 | |
| 65 | return off; |
| 66 | } |
| 67 | |
| 68 | /* ******** public API ******** */ |
| 69 | |
| 70 | int ncb_is_null(const struct ncbuf *buf) |
| 71 | { |
| 72 | return buf->size == 0; |
| 73 | } |
| 74 | |
| 75 | /* Initialize or reset <buf> by clearing all data. Its size is untouched. |
| 76 | * Buffer is positioned to <head> offset. Use 0 to realign it. |
| 77 | */ |
| 78 | void ncb_init(struct ncbuf *buf, ncb_sz_t head) |
| 79 | { |
| 80 | BUG_ON_HOT(head >= buf->size); |
| 81 | buf->head = head; |
| 82 | |
| 83 | ncb_write_off(buf, ncb_reserved(buf), 0); |
| 84 | ncb_write_off(buf, ncb_head(buf), ncb_size(buf)); |
| 85 | ncb_write_off(buf, ncb_peek(buf, sizeof(ncb_sz_t)), 0); |
| 86 | } |
| 87 | |
| 88 | /* Construct a ncbuf with all its parameters. */ |
| 89 | struct ncbuf ncb_make(char *area, ncb_sz_t size, ncb_sz_t head) |
| 90 | { |
| 91 | struct ncbuf buf; |
| 92 | |
| 93 | /* Ensure that there is enough space for the reserved space and data. |
| 94 | * This is the minimal value to not crash later. |
| 95 | */ |
| 96 | BUG_ON_HOT(size <= NCB_RESERVED_SZ); |
| 97 | |
| 98 | buf.area = area; |
| 99 | buf.size = size; |
| 100 | buf.head = head; |
| 101 | |
| 102 | return buf; |
| 103 | } |
| 104 | |
| 105 | /* Returns start of allocated buffer area. */ |
| 106 | char *ncb_orig(const struct ncbuf *buf) |
| 107 | { |
| 108 | return buf->area; |
| 109 | } |
| 110 | |
| 111 | /* Returns current head pointer into buffer area. */ |
| 112 | char *ncb_head(const struct ncbuf *buf) |
| 113 | { |
| 114 | return buf->area + buf->head; |
| 115 | } |
| 116 | |
| 117 | /* Returns the first byte after the allocated buffer area. */ |
| 118 | char *ncb_wrap(const struct ncbuf *buf) |
| 119 | { |
| 120 | return buf->area + buf->size; |
| 121 | } |
| 122 | |
| 123 | /* Returns the usable size of <buf> for data storage. This is the size of the |
| 124 | * allocated buffer without the reserved header space. |
| 125 | */ |
| 126 | ncb_sz_t ncb_size(const struct ncbuf *buf) |
| 127 | { |
| 128 | return buf->size - NCB_RESERVED_SZ; |
| 129 | } |
| 130 | |
| 131 | /* Returns true if there is no data anywhere in <buf>. */ |
| 132 | int ncb_is_empty(const struct ncbuf *buf) |
| 133 | { |
| 134 | BUG_ON_HOT(*ncb_reserved(buf) + *ncb_head(buf) > ncb_size(buf)); |
| 135 | return *ncb_reserved(buf) == 0 && *ncb_head(buf) == ncb_size(buf); |
| 136 | } |
| 137 | |
| 138 | /* Returns true if no more data can be inserted in <buf>. */ |
| 139 | int ncb_is_full(const struct ncbuf *buf) |
| 140 | { |
| 141 | BUG_ON_HOT(ncb_read_off(buf, ncb_reserved(buf)) > ncb_size(buf)); |
| 142 | return ncb_read_off(buf, ncb_reserved(buf)) == ncb_size(buf); |
| 143 | } |