A simple library for parsing and packing fundamental types for building network protocols. Licensed under the permissive ISC license. See the LICENSE file for details. Comments and improvements are welcome.
NOTE: The tests supplied in tests do not exhaustively test the contracts of all of the functions, but they do check for many different errors.
- C99 compiler
- stdlib: malloc, realloc, free, memmove, memcpy, strlen
- 8-bit char type
- Signed integers are represented using two's complement
- Floats are IEEE 754 32 bit floats in host endian
- Doubles are IEEE 754 64 bit floats in host endian
Parsing a structure containing a varint and a short:
struct mcp_parse buf = MCP_START_INITIALIZER(base, size);
mcp_varint_t a = mcp_varint(&buf);
uint16_t b = mcp_ushort(&buf);
if (!mcp_ok(&buf))
/* There was an error! */
/* Otherwise, everything is fine. */
Packing the same struct back into a fbuf:
struct fbuf buf = FBUF_INITIALIZER;
int ret = 0;
ret |= mcg_varint(buf, a);
ret |= mcg_ushort(buf, b);
if (ret)
/* There was an error! */
/* Otherwise, everything is fine. */
Reading from a unix socket into a fbuf:
void *ptr = fbuf_wptr(&buf, BLOCK_SIZE);
if (ptr == NULL)
/* Error: Buffer full. */
ssize_t ret = read(fd, ptr, fbuf_wavail(&buf));
if (ret <= 0)
/* Error: see man read (2) */
fbuf_produce(&buf, ret);
/* report that we have read ret bytes from fd */
Writing to a unix socket from a fbuf:
if (fbuf_avail(&buf) == 0)
/* Error: No data in buffer to write. */
ssize_t ret = write(fd, fbuf_ptr(&buf), fbuf_avail(&buf));
if (ret < 0)
/* Error: see man write (2) */
fbuf_consume(&buf, ret);
/* report that we have written ret bytes to fd */
The default and maximum value of max_size; the absolute maximum size of a fbuf.
Equivalent to calling fbuf_init
with FBUF_MAX
.
Sets up an buf
for first use. Limiting the maximum size
of the buffer to max
bytes.
Only call this function once per fbuf object.
Clears any data waiting in the buf
, but keeps the memory block.
Resets buf
as if it were just initialized with fbuf_init
.
Frees any block of memory owned by fbuf. Does not free buf
, i.e. does not call free(buf)
.
Returns a pointer to the beginning of the data waiting in the buffer.
Use fbuf_avail
to get the size of the block of data returned.
The pointers returned by fbuf_wptr
and fbuf_ptr
are invalidated by all fbuf_
calls to the same fbuf object except fbuf_wptr
with a require
argument of zero, fbuf_wavail
, fbuf_ptr
, and fbuf_avail
.
Returns the size of data waiting in the fbuf.
Returns a pointer to use when writing data into buf
.
Expands the buffer to guarantee that the pointer returned
will be at least require
bytes long. If the memory allocation
failed, fbuf_wptr
will return NULL.
The pointers returned by fbuf_wptr
and fbuf_ptr
are invalidated by all fbuf_
calls to the same fbuf object except fbuf_wptr
with a require
argument of zero, fbuf_wavail
, fbuf_ptr
, and fbuf_avail
.
Returns the maximum size that wptr can satisfy without expanding
the buf
.
Deletes sz
bytes from the end of the buffer.
Commits sz
bytes to the buffer that have been written to the
pointer returned by the most recent call to fbuf_wptr
.
Removed sz
bytes from buf
starting from the pointer returned by
the most recent call to fbuf_ptr
.
Resizes buf
so it can hold at least requested_size
more bytes in addition to the
data waiting in the buffer. fbuf_expand
returns the the same value as fbuf_wavail
Rotates the buffer data so that the buffer starts at the data waiting in the buffer, making future writes are more efficient. If you are doing many repeated reads and writes, you should call this function to prevent the buffer from much larger than the size of the data waiting in the buffer.
Changes the max size of the buf
to new_max. Returns 0
if fbuf_shrink
succeeds without error, or 1
if new_max is too low and would truncate
data waiting in buf
.
Copies size
bytes from src
into dest
, expanding the buffer if necessary.
If the copy succeeds without error, fbuf_copy
returns 0
.
Otherwise, it returns 1
on error.
mcp type | c type | mcp type | ctype |
---|---|---|---|
raw |
void * |
ubyte |
uint8_t |
bytes |
void * with size_t |
short |
uint16_t |
string |
utf-8 char * NUL -terminated |
uint |
uint32_t |
bool |
int as 1 or 0 |
ulong |
uint64_t |
varint |
uint32_t |
byte |
int8_t |
varlong |
uint64_t |
short |
int16_t |
svarint |
int32_t |
int |
int32_t |
svarlong |
int64_t |
long |
int16_t |
float |
float |
double |
double |
The maximum accepted size of a bytes object. Useful for compatibility with implementations that use varint28 as a length prefix.
No error, the parser will continue to parse data from the buffer.
An overrun occurred, there was not enough data in the buffer to completely read the type requested. Retry again later with more data.
There was not enough memory to complete the operation.
An overflow was encountered when trying to parse the data in the buffer.
An invalid value was encountered when trying to parse the data in the buffer.
NOTE: on error, the parser is left in an undefined state. You can copy the parser to save it's state if you need to.
Returns 1
if there are no errors asserted on buf
. 0
otherwise.
Returns the error code asserted on buf
.
Initializes the parser, buf
, for use with size
bytes starting at base
.
Returns 1
if the parser has reached the end of the buffer. 0
otherwise.
Returns the number of bytes waiting to be processed in buf
.
If dest
, which is max_size
bytes long, is large enough to
hold the object in buf
, then mcp_copy_*type*
copies the
object into dest
and consumes it from buf
and returns
the size in bytes of the resultant object in dest, if applicable,
including the NUL
-terminator. If dest
is too small to hold the object
MCP_EOVERFLOW
is not asserted, and is not copied to dest
and the
object is not consumed from buf
. If there was an error decoding
the object from buf
the error is asserted on buf
and zero is returned and
the parser is left in an undefined state.
NOTE: For an object to be consumed, the parser simply advances the pointer past the object.
Decodes and and consumes returns the decoded object from buf
. If there was
an error in decoding the object, an error is asserted on buf
and a place-holder value is returned (typically zero) and the parser
is left an an undefined state.
WARNING: Pointers returned by this function with the types raw
and bytes
are only valid as long as base is valid. mcp_copy_type does not have this
restriction, but is slightly less efficient due to a copy.
NOTE: For an object to be consumed, the parser simply advances the pointer past the object.
Packs an value
into buf
, expanding buf
if necessary.
Returns 0
if successful and 1
if there was an error.
On error, partial values may be written. If you need an
all-or-nothing call, store the old buffer size and uncommit
any partial writes.
An example implementation of this is:
size_t old_avail = fbuf_avail(buf);
int ret = 0;
/* call generators */
ret |= mcg_varint_(buf, a);
ret |= mcg_ushort(buf, b);
/* ... */
if (ret) {
/* rollback writes */
fbuf_unproduce(old_avail - fbuf_avail(buf));
/* handle error */
}
/* generator succeeded */