diff --git a/include/haproxy/ncbmbuf.h b/include/haproxy/ncbmbuf.h index 711985023..f114898d0 100644 --- a/include/haproxy/ncbmbuf.h +++ b/include/haproxy/ncbmbuf.h @@ -11,6 +11,24 @@ static inline int ncbmb_is_null(const struct ncbmbuf *buf) void ncbmb_init(struct ncbmbuf *buf, ncb_sz_t head); struct ncbmbuf ncbmb_make(char *area, ncb_sz_t size, ncb_sz_t head); +/* Returns start of allocated buffer area. */ +static inline char *ncbmb_orig(const struct ncbmbuf *buf) +{ + return buf->area; +} + +/* Returns current head pointer into buffer area. */ +static inline char *ncbmb_head(const struct ncbmbuf *buf) +{ + return buf->area + buf->head; +} + +/* Returns the first byte after the allocated buffer area. */ +static inline char *ncbmb_wrap(const struct ncbmbuf *buf) +{ + return buf->area + buf->size; +} + /* Returns the usable size of for data storage. This is the size of the * allocated buffer without the bitmap space. */ @@ -24,4 +42,7 @@ static inline ncb_sz_t ncbmb_size(const struct ncbmbuf *buf) int ncbmb_is_empty(const struct ncbmbuf *buf); +enum ncb_ret ncbmb_add(struct ncbmbuf *buf, ncb_sz_t off, + const char *data, ncb_sz_t len, enum ncb_add_mode mode); + #endif /* _HAPROXY_NCBMBUF_H */ diff --git a/src/ncbmbuf.c b/src/ncbmbuf.c index c268d14ff..c26b25eca 100644 --- a/src/ncbmbuf.c +++ b/src/ncbmbuf.c @@ -15,6 +15,53 @@ # define BUG_ON_HOT(x) if (x) { fprintf(stderr, "CRASH ON %s:%d\n", __func__, __LINE__); abort(); } #endif /* DEBUG_STRICT */ +/* ******** internal API ******** */ + +static char *ncbmb_peek(const struct ncbmbuf *buf, ncb_sz_t off) +{ + char *ptr = ncbmb_head(buf) + off; + if (ptr >= buf->area + buf->size) + ptr -= buf->size; + return ptr; +} + +static void ncbmb_set_bitmap(struct ncbmbuf *buf, ncb_sz_t off, ncb_sz_t len) +{ + const ncb_sz_t sz = ncbmb_size(buf); + ncb_sz_t off_abs; + unsigned char mod; + unsigned char *b; + + off_abs = off < sz ? off : off - sz; + b = buf->bitmap + (off_abs / 8); + + mod = off_abs % 8; + if (mod) { + /* adjust first bitmap byte bit by bit if not aligned on 8 */ + unsigned char to_copy = len < 8 - mod ? len : 8 - mod; + *b |= (unsigned char)(0xff << (8 - to_copy)) >> mod; + len -= to_copy; + ++b; + } + + if (len) { + size_t to_copy = len / 8; + /* bulk set bitmap as many as possible */ + if (to_copy) { + memset(b, 0xff, to_copy); + len -= 8 * to_copy; + b += to_copy; + } + + if (len) { + /* adjust last bitmap byte shifted by remaining len */ + *b |= 0xff << (8 - len); + } + } +} + +/* ******** public API ******** */ + /* Initialize or reset by clearing all data. Its size is untouched. * Buffer is positioned to offset. Use 0 to realign it. must not * be NCBUF_NULL. @@ -87,10 +134,34 @@ ncb_sz_t ncbmb_data(const struct ncbmbuf *buf, ncb_sz_t off) return 0; } +/* Add a new block at of size in at offset . Note that + * currently only NCB_ADD_OVERWRT mode is supported. + * + * Always returns NCB_RET_OK as this operation cannot fail. + */ enum ncb_ret ncbmb_add(struct ncbmbuf *buf, ncb_sz_t off, const char *data, ncb_sz_t len, enum ncb_add_mode mode) { - /* TODO */ + char *ptr = ncbmb_peek(buf, off); + + BUG_ON_HOT(mode != NCB_ADD_OVERWRT); + + BUG_ON_HOT(off + len > buf->size); + + if (ptr + len >= ncbmb_wrap(buf)) { + ncb_sz_t sz1 = ncbmb_wrap(buf) - ptr; + + memcpy(ptr, data, sz1); + ncbmb_set_bitmap(buf, buf->head + off, sz1); + + memcpy(ncbmb_orig(buf), data + sz1, len - sz1); + ncbmb_set_bitmap(buf, 0, len - sz1); + } + else { + memcpy(ptr, data, len); + ncbmb_set_bitmap(buf, buf->head + off, len); + } + return NCB_RET_OK; }