MINOR: ncbuf: complete API and define block interal abstraction
Implement two new functions to report the total data stored accross the whole buffer and the data stored at a specific offset until the next gap or the buffer end. To facilitate implementation of these new functions and also future add/delete operations, a new abstraction is introduced : ncb_blk. This structure represents a block of either data or gap in the buffer. It simplifies operation when moving forward in the buffer. The first buffer block can be retrieved via ncb_blk_first(buf). The block at a specific offset is accessed via ncb_blk_find(buf, off). This abstraction is purely used in functions but not stored in the ncbuf structure per-se. This is necessary to keep the minimal memory footprint.
This commit is contained in:
parent
1b5f77fc18
commit
d5d2ed90f0
@ -26,6 +26,36 @@
|
||||
* where the number of formed gaps is kept minimal and evenly spread.
|
||||
*/
|
||||
|
||||
/* **** internal documentation ****
|
||||
*
|
||||
* This section is useful to users who need to understand how ncbuf are
|
||||
* implemented.
|
||||
*
|
||||
* Public and internal functions all shared a common abstraction of the buffer.
|
||||
* The buffer content is represented as a list of blocks, alternating between
|
||||
* DATA and GAP blocks. This simplifies the buffer examination loop and
|
||||
* insertion/deletion. Note that this list of blocks is not stored in the
|
||||
* buffer structure.
|
||||
*
|
||||
* The buffer is considered to always start with a DATA block. The size of this
|
||||
* block is stored just before <head> which is the pointer for offset 0. This
|
||||
* space will always be reserved for this usage. It can be accessed through
|
||||
* ncb_int_head(buf). If the buffer has no data at head, the reserved space
|
||||
* will simply contains the value 0, and will be follow by a gap.
|
||||
*
|
||||
* A gap always contains the size of the gap itself and the size of the next
|
||||
* data block. Here is a small representation of a gap stored at offset <x>
|
||||
* before a data block at offset <y>.
|
||||
*
|
||||
* x y
|
||||
* ------------------------------------------------------------
|
||||
* xxxxxx| GAP-SZ | DATA-SZ | | xxxxxxxxxxxxx...
|
||||
* ------------------------------------------------------------
|
||||
* | -------- GAP-SZ -------------- > | --- DATA-SZ --->
|
||||
*
|
||||
* This means that a gap must be at least big enough to store two sizes.
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/* ncb_sz_t is the basic type used in ncbuf to represent data and gap sizes.
|
||||
|
@ -12,7 +12,10 @@ char *ncb_head(const struct ncbuf *buf);
|
||||
char *ncb_wrap(const struct ncbuf *buf);
|
||||
|
||||
ncb_sz_t ncb_size(const struct ncbuf *buf);
|
||||
ncb_sz_t ncb_total_data(const struct ncbuf *buf);
|
||||
int ncb_is_empty(const struct ncbuf *buf);
|
||||
int ncb_is_full(const struct ncbuf *buf);
|
||||
|
||||
ncb_sz_t ncb_data(const struct ncbuf *buf, ncb_sz_t offset);
|
||||
|
||||
#endif /* _HAPROXY_NCBUF_H */
|
||||
|
147
src/ncbuf.c
147
src/ncbuf.c
@ -15,6 +15,21 @@
|
||||
|
||||
/* ******** internal API ******** */
|
||||
|
||||
#define NCB_BLK_NULL ((struct ncb_blk){ .st = NULL })
|
||||
|
||||
#define NCB_BK_F_GAP 0x01 /* block represents a gap */
|
||||
struct ncb_blk {
|
||||
char *st; /* first byte of the block */
|
||||
char *end; /* first byte after this block */
|
||||
|
||||
char *sz_ptr; /* pointer to size element */
|
||||
ncb_sz_t sz; /* size of the block */
|
||||
ncb_sz_t sz_data; /* size of the data following the block - only valid for GAP */
|
||||
ncb_sz_t off; /* offset of block in buffer */
|
||||
|
||||
char flag;
|
||||
};
|
||||
|
||||
/* Return pointer to <off> relative to <buf> head. Support buffer wrapping. */
|
||||
static char *ncb_peek(const struct ncbuf *buf, ncb_sz_t off)
|
||||
{
|
||||
@ -65,6 +80,100 @@ static ncb_sz_t ncb_read_off(const struct ncbuf *buf, char *st)
|
||||
return off;
|
||||
}
|
||||
|
||||
/* Returns true if <blk> is the special NULL block. */
|
||||
static int ncb_blk_is_null(const struct ncb_blk blk)
|
||||
{
|
||||
return !blk.st;
|
||||
}
|
||||
|
||||
/* Returns true if <blk> is the last block of <buf>. */
|
||||
static int ncb_blk_is_last(const struct ncbuf *buf, const struct ncb_blk blk)
|
||||
{
|
||||
BUG_ON_HOT(blk.off + blk.sz > ncb_size(buf));
|
||||
return blk.off + blk.sz == ncb_size(buf);
|
||||
}
|
||||
|
||||
/* Returns the first block of <buf> which is always a DATA. */
|
||||
static struct ncb_blk ncb_blk_first(const struct ncbuf *buf)
|
||||
{
|
||||
struct ncb_blk blk;
|
||||
|
||||
blk.st = ncb_head(buf);
|
||||
|
||||
blk.sz_ptr = ncb_reserved(buf);
|
||||
blk.sz = ncb_read_off(buf, ncb_reserved(buf));
|
||||
BUG_ON_HOT(blk.sz > ncb_size(buf));
|
||||
|
||||
blk.end = ncb_peek(buf, blk.sz);
|
||||
blk.off = 0;
|
||||
blk.flag = 0;
|
||||
|
||||
return blk;
|
||||
}
|
||||
|
||||
/* Returns the block following <prev> in the buffer <buf>. */
|
||||
static struct ncb_blk ncb_blk_next(const struct ncbuf *buf,
|
||||
const struct ncb_blk prev)
|
||||
{
|
||||
struct ncb_blk blk;
|
||||
|
||||
BUG_ON_HOT(ncb_blk_is_null(prev));
|
||||
|
||||
if (ncb_blk_is_last(buf, prev))
|
||||
return NCB_BLK_NULL;
|
||||
|
||||
blk.st = prev.end;
|
||||
blk.off = prev.off + prev.sz;
|
||||
blk.flag = ~prev.flag & NCB_BK_F_GAP;
|
||||
|
||||
if (blk.flag & NCB_BK_F_GAP) {
|
||||
blk.sz_ptr = ncb_peek(buf, blk.off + NCB_GAP_SZ_OFF);
|
||||
blk.sz = ncb_read_off(buf, blk.sz_ptr);
|
||||
BUG_ON_HOT(blk.sz < NCB_GAP_MIN_SZ);
|
||||
blk.sz_data = ncb_read_off(buf, ncb_peek(buf, blk.off + NCB_GAP_SZ_DATA_OFF));
|
||||
}
|
||||
else {
|
||||
blk.sz_ptr = ncb_peek(buf, prev.off + NCB_GAP_SZ_DATA_OFF);
|
||||
blk.sz = prev.sz_data;
|
||||
blk.sz_data = 0;
|
||||
|
||||
/* only first DATA block can be empty. If this happens, a GAP
|
||||
* merge should have been realized.
|
||||
*/
|
||||
BUG_ON_HOT(!blk.sz);
|
||||
}
|
||||
|
||||
BUG_ON_HOT(blk.off + blk.sz > ncb_size(buf));
|
||||
blk.end = ncb_peek(buf, blk.off + blk.sz);
|
||||
|
||||
return blk;
|
||||
}
|
||||
|
||||
/* Returns the block containing offset <off>. Note that if <off> is at the
|
||||
* frontier between two blocks, this function will return the preceding one.
|
||||
* This is done to easily merge blocks on insertion/deletion.
|
||||
*/
|
||||
static struct ncb_blk ncb_blk_find(const struct ncbuf *buf, ncb_sz_t off)
|
||||
{
|
||||
struct ncb_blk blk;
|
||||
|
||||
BUG_ON_HOT(off >= ncb_size(buf));
|
||||
|
||||
for (blk = ncb_blk_first(buf); off > blk.off + blk.sz;
|
||||
blk = ncb_blk_next(buf, blk)) {
|
||||
}
|
||||
|
||||
return blk;
|
||||
}
|
||||
|
||||
/* Transform absolute offset <off> to a relative one from <blk> start. */
|
||||
static ncb_sz_t ncb_blk_off(const struct ncb_blk blk, ncb_sz_t off)
|
||||
{
|
||||
BUG_ON_HOT(off < blk.off || off > blk.off + blk.sz);
|
||||
BUG_ON_HOT(off - blk.off > blk.sz);
|
||||
return off - blk.off;
|
||||
}
|
||||
|
||||
/* ******** public API ******** */
|
||||
|
||||
int ncb_is_null(const struct ncbuf *buf)
|
||||
@ -128,6 +237,20 @@ ncb_sz_t ncb_size(const struct ncbuf *buf)
|
||||
return buf->size - NCB_RESERVED_SZ;
|
||||
}
|
||||
|
||||
/* Returns the total number of bytes stored in whole <buf>. */
|
||||
ncb_sz_t ncb_total_data(const struct ncbuf *buf)
|
||||
{
|
||||
struct ncb_blk blk;
|
||||
int total = 0;
|
||||
|
||||
for (blk = ncb_blk_first(buf); !ncb_blk_is_null(blk); blk = ncb_blk_next(buf, blk)) {
|
||||
if (!(blk.flag & NCB_BK_F_GAP))
|
||||
total += blk.sz;
|
||||
}
|
||||
|
||||
return total;
|
||||
}
|
||||
|
||||
/* Returns true if there is no data anywhere in <buf>. */
|
||||
int ncb_is_empty(const struct ncbuf *buf)
|
||||
{
|
||||
@ -141,3 +264,27 @@ int ncb_is_full(const struct ncbuf *buf)
|
||||
BUG_ON_HOT(ncb_read_off(buf, ncb_reserved(buf)) > ncb_size(buf));
|
||||
return ncb_read_off(buf, ncb_reserved(buf)) == ncb_size(buf);
|
||||
}
|
||||
|
||||
/* Returns the number of bytes of data avaiable in <buf> starting at offset
|
||||
* <off> until the next gap or the buffer end. The counted data may wrapped if
|
||||
* the buffer storage is not aligned.
|
||||
*/
|
||||
ncb_sz_t ncb_data(const struct ncbuf *buf, ncb_sz_t off)
|
||||
{
|
||||
struct ncb_blk blk = ncb_blk_find(buf, off);
|
||||
ncb_sz_t off_blk = ncb_blk_off(blk, off);
|
||||
|
||||
/* if <off> at the frontier between two and <blk> is gap, retrieve the
|
||||
* next data block.
|
||||
*/
|
||||
if (blk.flag & NCB_BK_F_GAP && off_blk == blk.sz &&
|
||||
!ncb_blk_is_last(buf, blk)) {
|
||||
blk = ncb_blk_next(buf, blk);
|
||||
off_blk = ncb_blk_off(blk, off);
|
||||
}
|
||||
|
||||
if (blk.flag & NCB_BK_F_GAP)
|
||||
return 0;
|
||||
|
||||
return blk.sz - off_blk;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user