MINOR: buffer: add functions to read/write varints from/to buffers

The new functions are :
  __b_put_varint() : inserts a varint when it's known that it fits
  b_put_varint()   : tries to insert a varint at the tail
  b_get_varint()   : tries to get a varint from the head
  b_peek_varint()  : tries to peek a varint at a specific offset

Wrapping is supported so that they are expected to be safe to use to
manipulate varints with buffers anywhere.
This commit is contained in:
Willy Tarreau 2019-08-23 17:06:12 +02:00
parent 4d589e719b
commit b88d231773

View File

@ -674,6 +674,164 @@ static inline int b_rep_blk(struct buffer *b, char *pos, char *end, const char *
}
/* __b_put_varint(): encode 64-bit value <v> as a varint into buffer <b>. The
* caller must have checked that the encoded value fits in the buffer so that
* there are no length checks. Wrapping is supported. You don't want to use
* this function but b_put_varint() instead.
*/
static inline void __b_put_varint(struct buffer *b, uint64_t v)
{
size_t data = b->data;
size_t size = b_size(b);
char *wrap = b_wrap(b);
char *tail = b_tail(b);
if (v >= 0xF0) {
/* more than one byte, first write the 4 least significant
* bits, then follow with 7 bits per byte.
*/
*tail = v | 0xF0;
v = (v - 0xF0) >> 4;
while (1) {
if (tail++ == wrap)
tail -= size;
data++;
if (v < 0x80)
break;
*tail = v | 0x80;
v = (v - 0x80) >> 7;
}
}
/* last byte */
*tail = v;
data++;
b->data = data;
}
/* b_put_varint(): try to encode value <v> as a varint into buffer <b>. Returns
* the number of bytes written in case of success, or 0 if there is not enough
* room. Wrapping is supported. No partial writes will be performed.
*/
static inline int b_put_varint(struct buffer *b, uint64_t v)
{
size_t data = b->data;
size_t size = b_size(b);
char *wrap = b_wrap(b);
char *tail = b_tail(b);
if (data != size && v >= 0xF0) {
/* more than one byte, first write the 4 least significant
* bits, then follow with 7 bits per byte.
*/
*tail = v | 0xF0;
v = (v - 0xF0) >> 4;
while (1) {
if (tail++ == wrap)
tail -= size;
data++;
if (data == size || v < 0x80)
break;
*tail = v | 0x80;
v = (v - 0x80) >> 7;
}
}
/* last byte */
if (data == size)
return 0;
*tail = v;
data++;
size = data - b->data;
b->data = data;
return size;
}
/* b_get_varint(): try to decode a varint from buffer <b> into value <vptr>.
* Returns the number of bytes read in case of success, or 0 if there were not
* enough bytes. Wrapping is supported. No partial reads will be performed.
*/
static inline int b_get_varint(struct buffer *b, uint64_t *vptr)
{
const uint8_t *head = (const uint8_t *)b_head(b);
const uint8_t *wrap = (const uint8_t *)b_wrap(b);
size_t data = b->data;
size_t size = b_size(b);
uint64_t v = 0;
int bits = 0;
if (data != 0 && (*head >= 0xF0)) {
v = *head;
bits += 4;
while (1) {
if (head++ == wrap)
head -= size;
data--;
if (!data || !(*head & 0x80))
break;
v += (uint64_t)*head << bits;
bits += 7;
}
}
/* last byte */
if (!data)
return 0;
v += (uint64_t)*head << bits;
*vptr = v;
data--;
size = b->data - data;
b_del(b, size);
return size;
}
/* b_peek_varint(): try to decode a varint from buffer <b> at offset <ofs>
* relative to head, into value <vptr>. Returns the number of bytes parsed in
* case of success, or 0 if there were not enough bytes, in which case the
* contents of <vptr> are not updated. Wrapping is supported. The buffer's head
* will NOT be updated. It is illegal to call this function with <ofs> greater
* than b->data.
*/
static inline int b_peek_varint(struct buffer *b, size_t ofs, uint64_t *vptr)
{
const uint8_t *head = (const uint8_t *)b_peek(b, ofs);
const uint8_t *wrap = (const uint8_t *)b_wrap(b);
size_t data = b_data(b) - ofs;
size_t size = b_size(b);
uint64_t v = 0;
int bits = 0;
if (data != 0 && (*head >= 0xF0)) {
v = *head;
bits += 4;
while (1) {
if (head++ == wrap)
head -= size;
data--;
if (!data || !(*head & 0x80))
break;
v += (uint64_t)*head << bits;
bits += 7;
}
}
/* last byte */
if (!data)
return 0;
v += (uint64_t)*head << bits;
*vptr = v;
data--;
size = b->data - ofs - data;
return size;
}
/*
* Buffer ring management.
*