/* SPDX-License-Identifier: MIT * Copyright © 2020-2024 Charles Gunyon. */ #ifndef CMP_H_INCLUDED #define CMP_H_INCLUDED #include #include #include typedef struct cmp_ctx_s cmp_ctx_t; typedef bool cmp_reader(cmp_ctx_t *ctx, void *data, size_t limit); typedef bool cmp_skipper(cmp_ctx_t *ctx, size_t count); typedef size_t cmp_writer(cmp_ctx_t *ctx, const void *data, size_t count); enum { CMP_TYPE_POSITIVE_FIXNUM, /* 0 */ CMP_TYPE_FIXMAP, /* 1 */ CMP_TYPE_FIXARRAY, /* 2 */ CMP_TYPE_FIXSTR, /* 3 */ CMP_TYPE_NIL, /* 4 */ CMP_TYPE_BOOLEAN, /* 5 */ CMP_TYPE_BIN8, /* 6 */ CMP_TYPE_BIN16, /* 7 */ CMP_TYPE_BIN32, /* 8 */ CMP_TYPE_EXT8, /* 9 */ CMP_TYPE_EXT16, /* 10 */ CMP_TYPE_EXT32, /* 11 */ CMP_TYPE_FLOAT, /* 12 */ CMP_TYPE_DOUBLE, /* 13 */ CMP_TYPE_UINT8, /* 14 */ CMP_TYPE_UINT16, /* 15 */ CMP_TYPE_UINT32, /* 16 */ CMP_TYPE_UINT64, /* 17 */ CMP_TYPE_SINT8, /* 18 */ CMP_TYPE_SINT16, /* 19 */ CMP_TYPE_SINT32, /* 20 */ CMP_TYPE_SINT64, /* 21 */ CMP_TYPE_FIXEXT1, /* 22 */ CMP_TYPE_FIXEXT2, /* 23 */ CMP_TYPE_FIXEXT4, /* 24 */ CMP_TYPE_FIXEXT8, /* 25 */ CMP_TYPE_FIXEXT16, /* 26 */ CMP_TYPE_STR8, /* 27 */ CMP_TYPE_STR16, /* 28 */ CMP_TYPE_STR32, /* 29 */ CMP_TYPE_ARRAY16, /* 30 */ CMP_TYPE_ARRAY32, /* 31 */ CMP_TYPE_MAP16, /* 32 */ CMP_TYPE_MAP32, /* 33 */ CMP_TYPE_NEGATIVE_FIXNUM /* 34 */ }; typedef struct cmp_ext_s { int8_t type; uint32_t size; } cmp_ext_t; typedef union cmp_object_data_u { bool boolean; uint8_t u8; uint16_t u16; uint32_t u32; uint64_t u64; int8_t s8; int16_t s16; int32_t s32; int64_t s64; #ifndef CMP_NO_FLOAT float flt; double dbl; #endif /* CMP_NO_FLOAT */ uint32_t array_size; uint32_t map_size; uint32_t str_size; uint32_t bin_size; cmp_ext_t ext; } cmp_object_data_t; struct cmp_ctx_s { uint8_t error; void *buf; cmp_reader *read; cmp_skipper *skip; cmp_writer *write; }; typedef struct cmp_object_s { uint8_t type; cmp_object_data_t as; } cmp_object_t; #ifdef __cplusplus extern "C" { #endif /* * ============================================================================ * === Main API * ============================================================================ */ /* * Initializes a CMP context * * If you don't intend to read, `read` may be NULL, but calling `*read*` * functions will crash; there is no check. * * `skip` may be NULL, in which case skipping functions will use `read`. * * If you don't intend to write, `write` may be NULL, but calling `*write*` * functions will crash; there is no check. */ void cmp_init(cmp_ctx_t *ctx, void *buf, cmp_reader *read, cmp_skipper *skip, cmp_writer *write); /* Returns CMP's version */ uint32_t cmp_version(void); /* Returns the MessagePack version employed by CMP */ uint32_t cmp_mp_version(void); /* Returns a string description of a CMP context's error */ const char* cmp_strerror(const cmp_ctx_t *ctx); /* Writes a signed integer to the backend */ bool cmp_write_integer(cmp_ctx_t *ctx, int64_t d); /* Writes an unsigned integer to the backend */ bool cmp_write_uinteger(cmp_ctx_t *ctx, uint64_t u); /* * Writes a floating-point value (either single or double-precision) to the * backend */ #ifndef CMP_NO_FLOAT bool cmp_write_decimal(cmp_ctx_t *ctx, double d); #endif /* CMP_NO_FLOAT */ /* Writes NULL to the backend */ bool cmp_write_nil(cmp_ctx_t *ctx); /* Writes true to the backend */ bool cmp_write_true(cmp_ctx_t *ctx); /* Writes false to the backend */ bool cmp_write_false(cmp_ctx_t *ctx); /* Writes a boolean value to the backend */ bool cmp_write_bool(cmp_ctx_t *ctx, bool b); /* * Writes an unsigned char's value to the backend as a boolean. This is useful * if you are using a different boolean type in your application. */ bool cmp_write_u8_as_bool(cmp_ctx_t *ctx, uint8_t b); /* * Writes a string to the backend; according to the MessagePack spec, this must * be encoded using UTF-8, but CMP leaves that job up to the programmer. */ bool cmp_write_str(cmp_ctx_t *ctx, const char *data, uint32_t size); /* * Writes a string to the backend. This avoids using the STR8 marker, which * is unsupported by MessagePack v4, the version implemented by many other * MessagePack libraries. No encoding is assumed in this case, not that it * matters. */ bool cmp_write_str_v4(cmp_ctx_t *ctx, const char *data, uint32_t size); /* * Writes the string marker to the backend. This is useful if you are writing * data in chunks instead of a single shot. */ bool cmp_write_str_marker(cmp_ctx_t *ctx, uint32_t size); /* * Writes the string marker to the backend. This is useful if you are writing * data in chunks instead of a single shot. This avoids using the STR8 * marker, which is unsupported by MessagePack v4, the version implemented by * many other MessagePack libraries. No encoding is assumed in this case, not * that it matters. */ bool cmp_write_str_marker_v4(cmp_ctx_t *ctx, uint32_t size); /* Writes binary data to the backend */ bool cmp_write_bin(cmp_ctx_t *ctx, const void *data, uint32_t size); /* * Writes the binary data marker to the backend. This is useful if you are * writing data in chunks instead of a single shot. */ bool cmp_write_bin_marker(cmp_ctx_t *ctx, uint32_t size); /* Writes an array to the backend. */ bool cmp_write_array(cmp_ctx_t *ctx, uint32_t size); /* Writes a map to the backend. */ bool cmp_write_map(cmp_ctx_t *ctx, uint32_t size); /* Writes an extended type to the backend */ bool cmp_write_ext(cmp_ctx_t *ctx, int8_t type, uint32_t size, const void *data); /* * Writes the extended type marker to the backend. This is useful if you want * to write the type's data in chunks instead of a single shot. */ bool cmp_write_ext_marker(cmp_ctx_t *ctx, int8_t type, uint32_t size); /* Writes an object to the backend */ bool cmp_write_object(cmp_ctx_t *ctx, const cmp_object_t *obj); /* * Writes an object to the backend. This avoids using the STR8 marker, which * is unsupported by MessagePack v4, the version implemented by many other * MessagePack libraries. */ bool cmp_write_object_v4(cmp_ctx_t *ctx, const cmp_object_t *obj); /* Reads a signed integer that fits inside a signed char */ bool cmp_read_char(cmp_ctx_t *ctx, int8_t *c); /* Reads a signed integer that fits inside a signed short */ bool cmp_read_short(cmp_ctx_t *ctx, int16_t *s); /* Reads a signed integer that fits inside a signed int */ bool cmp_read_int(cmp_ctx_t *ctx, int32_t *i); /* Reads a signed integer that fits inside a signed long */ bool cmp_read_long(cmp_ctx_t *ctx, int64_t *d); /* Reads a signed integer */ bool cmp_read_integer(cmp_ctx_t *ctx, int64_t *d); /* Reads an unsigned integer that fits inside an unsigned char */ bool cmp_read_uchar(cmp_ctx_t *ctx, uint8_t *c); /* Reads an unsigned integer that fits inside an unsigned short */ bool cmp_read_ushort(cmp_ctx_t *ctx, uint16_t *s); /* Reads an unsigned integer that fits inside an unsigned int */ bool cmp_read_uint(cmp_ctx_t *ctx, uint32_t *i); /* Reads an unsigned integer that fits inside an unsigned long */ bool cmp_read_ulong(cmp_ctx_t *ctx, uint64_t *u); /* Reads an unsigned integer */ bool cmp_read_uinteger(cmp_ctx_t *ctx, uint64_t *u); /* * Reads a floating point value (either single or double-precision) from the * backend */ #ifndef CMP_NO_FLOAT bool cmp_read_decimal(cmp_ctx_t *ctx, double *d); #endif /* CMP_NO_FLOAT */ /* "Reads" (more like "skips") a NULL value from the backend */ bool cmp_read_nil(cmp_ctx_t *ctx); /* Reads a boolean from the backend */ bool cmp_read_bool(cmp_ctx_t *ctx, bool *b); /* * Reads a boolean as an unsigned char from the backend; this is useful if your * application uses a different boolean type. */ bool cmp_read_bool_as_u8(cmp_ctx_t *ctx, uint8_t *b); /* Reads a string's size from the backend */ bool cmp_read_str_size(cmp_ctx_t *ctx, uint32_t *size); /* * Reads a string from the backend; according to the spec, the string's data * ought to be encoded using UTF-8, but CMP leaves that job up to the programmer. */ bool cmp_read_str(cmp_ctx_t *ctx, char *data, uint32_t *size); /* Reads the size of packed binary data from the backend */ bool cmp_read_bin_size(cmp_ctx_t *ctx, uint32_t *size); /* Reads packed binary data from the backend */ bool cmp_read_bin(cmp_ctx_t *ctx, void *data, uint32_t *size); /* Reads an array from the backend */ bool cmp_read_array(cmp_ctx_t *ctx, uint32_t *size); /* Reads a map from the backend */ bool cmp_read_map(cmp_ctx_t *ctx, uint32_t *size); /* Reads the extended type's marker from the backend */ bool cmp_read_ext_marker(cmp_ctx_t *ctx, int8_t *type, uint32_t *size); /* Reads an extended type from the backend */ bool cmp_read_ext(cmp_ctx_t *ctx, int8_t *type, uint32_t *size, void *data); /* Reads an object from the backend */ bool cmp_read_object(cmp_ctx_t *ctx, cmp_object_t *obj); /* * Skips the next object from the backend. If that object is an array or map, * this function will: * - If `obj` is not `NULL`, fill in `obj` with that object * - Set `ctx->error` to `SKIP_DEPTH_LIMIT_EXCEEDED_ERROR` * - Return `false` * Otherwise: * - (Don't touch `obj`) * - Return `true` */ bool cmp_skip_object(cmp_ctx_t *ctx, cmp_object_t *obj); /* * This is similar to `cmp_skip_object`, except it tolerates flat arrays and * maps. If when skipping such an array or map this function encounters * another array/map, it will: * - If `obj` is not `NULL`, fill in `obj` with that (nested) object * - Set `ctx->error` to `SKIP_DEPTH_LIMIT_EXCEEDED_ERROR` * - Return `false` * Otherwise: * - (Don't touch `obj`) * - Return `true` * * WARNING: This can cause your application to spend an unbounded amount of * time reading nested data structures. Unless you completely trust * the data source, you should use `cmp_skip_object`. */ bool cmp_skip_object_flat(cmp_ctx_t *ctx, cmp_object_t *obj); /* * This is similar to `cmp_skip_object`, except it will continually skip * nested data structures. * * WARNING: This can cause your application to spend an unbounded amount of * time reading nested data structures. Unless you completely trust * the data source, you should use `cmp_skip_object`. */ bool cmp_skip_object_no_limit(cmp_ctx_t *ctx); /* * ============================================================================ * === Specific API * ============================================================================ */ bool cmp_write_pfix(cmp_ctx_t *ctx, uint8_t c); bool cmp_write_nfix(cmp_ctx_t *ctx, int8_t c); bool cmp_write_sfix(cmp_ctx_t *ctx, int8_t c); bool cmp_write_s8(cmp_ctx_t *ctx, int8_t c); bool cmp_write_s16(cmp_ctx_t *ctx, int16_t s); bool cmp_write_s32(cmp_ctx_t *ctx, int32_t i); bool cmp_write_s64(cmp_ctx_t *ctx, int64_t l); bool cmp_write_ufix(cmp_ctx_t *ctx, uint8_t c); bool cmp_write_u8(cmp_ctx_t *ctx, uint8_t c); bool cmp_write_u16(cmp_ctx_t *ctx, uint16_t s); bool cmp_write_u32(cmp_ctx_t *ctx, uint32_t i); bool cmp_write_u64(cmp_ctx_t *ctx, uint64_t l); #ifndef CMP_NO_FLOAT bool cmp_write_float(cmp_ctx_t *ctx, float f); bool cmp_write_double(cmp_ctx_t *ctx, double d); #endif /* CMP_NO_FLOAT */ bool cmp_write_fixstr_marker(cmp_ctx_t *ctx, uint8_t size); bool cmp_write_fixstr(cmp_ctx_t *ctx, const char *data, uint8_t size); bool cmp_write_str8_marker(cmp_ctx_t *ctx, uint8_t size); bool cmp_write_str8(cmp_ctx_t *ctx, const char *data, uint8_t size); bool cmp_write_str16_marker(cmp_ctx_t *ctx, uint16_t size); bool cmp_write_str16(cmp_ctx_t *ctx, const char *data, uint16_t size); bool cmp_write_str32_marker(cmp_ctx_t *ctx, uint32_t size); bool cmp_write_str32(cmp_ctx_t *ctx, const char *data, uint32_t size); bool cmp_write_bin8_marker(cmp_ctx_t *ctx, uint8_t size); bool cmp_write_bin8(cmp_ctx_t *ctx, const void *data, uint8_t size); bool cmp_write_bin16_marker(cmp_ctx_t *ctx, uint16_t size); bool cmp_write_bin16(cmp_ctx_t *ctx, const void *data, uint16_t size); bool cmp_write_bin32_marker(cmp_ctx_t *ctx, uint32_t size); bool cmp_write_bin32(cmp_ctx_t *ctx, const void *data, uint32_t size); bool cmp_write_fixarray(cmp_ctx_t *ctx, uint8_t size); bool cmp_write_array16(cmp_ctx_t *ctx, uint16_t size); bool cmp_write_array32(cmp_ctx_t *ctx, uint32_t size); bool cmp_write_fixmap(cmp_ctx_t *ctx, uint8_t size); bool cmp_write_map16(cmp_ctx_t *ctx, uint16_t size); bool cmp_write_map32(cmp_ctx_t *ctx, uint32_t size); bool cmp_write_fixext1_marker(cmp_ctx_t *ctx, int8_t type); bool cmp_write_fixext1(cmp_ctx_t *ctx, int8_t type, const void *data); bool cmp_write_fixext2_marker(cmp_ctx_t *ctx, int8_t type); bool cmp_write_fixext2(cmp_ctx_t *ctx, int8_t type, const void *data); bool cmp_write_fixext4_marker(cmp_ctx_t *ctx, int8_t type); bool cmp_write_fixext4(cmp_ctx_t *ctx, int8_t type, const void *data); bool cmp_write_fixext8_marker(cmp_ctx_t *ctx, int8_t type); bool cmp_write_fixext8(cmp_ctx_t *ctx, int8_t type, const void *data); bool cmp_write_fixext16_marker(cmp_ctx_t *ctx, int8_t type); bool cmp_write_fixext16(cmp_ctx_t *ctx, int8_t type, const void *data); bool cmp_write_ext8_marker(cmp_ctx_t *ctx, int8_t type, uint8_t size); bool cmp_write_ext8(cmp_ctx_t *ctx, int8_t type, uint8_t size, const void *data); bool cmp_write_ext16_marker(cmp_ctx_t *ctx, int8_t type, uint16_t size); bool cmp_write_ext16(cmp_ctx_t *ctx, int8_t type, uint16_t size, const void *data); bool cmp_write_ext32_marker(cmp_ctx_t *ctx, int8_t type, uint32_t size); bool cmp_write_ext32(cmp_ctx_t *ctx, int8_t type, uint32_t size, const void *data); bool cmp_read_pfix(cmp_ctx_t *ctx, uint8_t *c); bool cmp_read_nfix(cmp_ctx_t *ctx, int8_t *c); bool cmp_read_sfix(cmp_ctx_t *ctx, int8_t *c); bool cmp_read_s8(cmp_ctx_t *ctx, int8_t *c); bool cmp_read_s16(cmp_ctx_t *ctx, int16_t *s); bool cmp_read_s32(cmp_ctx_t *ctx, int32_t *i); bool cmp_read_s64(cmp_ctx_t *ctx, int64_t *l); bool cmp_read_ufix(cmp_ctx_t *ctx, uint8_t *c); bool cmp_read_u8(cmp_ctx_t *ctx, uint8_t *c); bool cmp_read_u16(cmp_ctx_t *ctx, uint16_t *s); bool cmp_read_u32(cmp_ctx_t *ctx, uint32_t *i); bool cmp_read_u64(cmp_ctx_t *ctx, uint64_t *l); #ifndef CMP_NO_FLOAT bool cmp_read_float(cmp_ctx_t *ctx, float *f); bool cmp_read_double(cmp_ctx_t *ctx, double *d); #endif /* CMP_NO_FLOAT */ bool cmp_read_fixext1_marker(cmp_ctx_t *ctx, int8_t *type); bool cmp_read_fixext1(cmp_ctx_t *ctx, int8_t *type, void *data); bool cmp_read_fixext2_marker(cmp_ctx_t *ctx, int8_t *type); bool cmp_read_fixext2(cmp_ctx_t *ctx, int8_t *type, void *data); bool cmp_read_fixext4_marker(cmp_ctx_t *ctx, int8_t *type); bool cmp_read_fixext4(cmp_ctx_t *ctx, int8_t *type, void *data); bool cmp_read_fixext8_marker(cmp_ctx_t *ctx, int8_t *type); bool cmp_read_fixext8(cmp_ctx_t *ctx, int8_t *type, void *data); bool cmp_read_fixext16_marker(cmp_ctx_t *ctx, int8_t *type); bool cmp_read_fixext16(cmp_ctx_t *ctx, int8_t *type, void *data); bool cmp_read_ext8_marker(cmp_ctx_t *ctx, int8_t *type, uint8_t *size); bool cmp_read_ext8(cmp_ctx_t *ctx, int8_t *type, uint8_t *size, void *data); bool cmp_read_ext16_marker(cmp_ctx_t *ctx, int8_t *type, uint16_t *size); bool cmp_read_ext16(cmp_ctx_t *ctx, int8_t *type, uint16_t *size, void *data); bool cmp_read_ext32_marker(cmp_ctx_t *ctx, int8_t *type, uint32_t *size); bool cmp_read_ext32(cmp_ctx_t *ctx, int8_t *type, uint32_t *size, void *data); /* * ============================================================================ * === Object API * ============================================================================ */ bool cmp_object_is_char(const cmp_object_t *obj); bool cmp_object_is_short(const cmp_object_t *obj); bool cmp_object_is_int(const cmp_object_t *obj); bool cmp_object_is_long(const cmp_object_t *obj); bool cmp_object_is_sinteger(const cmp_object_t *obj); bool cmp_object_is_uchar(const cmp_object_t *obj); bool cmp_object_is_ushort(const cmp_object_t *obj); bool cmp_object_is_uint(const cmp_object_t *obj); bool cmp_object_is_ulong(const cmp_object_t *obj); bool cmp_object_is_uinteger(const cmp_object_t *obj); bool cmp_object_is_float(const cmp_object_t *obj); bool cmp_object_is_double(const cmp_object_t *obj); bool cmp_object_is_nil(const cmp_object_t *obj); bool cmp_object_is_bool(const cmp_object_t *obj); bool cmp_object_is_str(const cmp_object_t *obj); bool cmp_object_is_bin(const cmp_object_t *obj); bool cmp_object_is_array(const cmp_object_t *obj); bool cmp_object_is_map(const cmp_object_t *obj); bool cmp_object_is_ext(const cmp_object_t *obj); bool cmp_object_as_char(const cmp_object_t *obj, int8_t *c); bool cmp_object_as_short(const cmp_object_t *obj, int16_t *s); bool cmp_object_as_int(const cmp_object_t *obj, int32_t *i); bool cmp_object_as_long(const cmp_object_t *obj, int64_t *d); bool cmp_object_as_sinteger(const cmp_object_t *obj, int64_t *d); bool cmp_object_as_uchar(const cmp_object_t *obj, uint8_t *c); bool cmp_object_as_ushort(const cmp_object_t *obj, uint16_t *s); bool cmp_object_as_uint(const cmp_object_t *obj, uint32_t *i); bool cmp_object_as_ulong(const cmp_object_t *obj, uint64_t *u); bool cmp_object_as_uinteger(const cmp_object_t *obj, uint64_t *u); #ifndef CMP_NO_FLOAT bool cmp_object_as_float(const cmp_object_t *obj, float *f); bool cmp_object_as_double(const cmp_object_t *obj, double *d); #endif /* CMP_NO_FLOAT */ bool cmp_object_as_bool(const cmp_object_t *obj, bool *b); bool cmp_object_as_str(const cmp_object_t *obj, uint32_t *size); bool cmp_object_as_bin(const cmp_object_t *obj, uint32_t *size); bool cmp_object_as_array(const cmp_object_t *obj, uint32_t *size); bool cmp_object_as_map(const cmp_object_t *obj, uint32_t *size); bool cmp_object_as_ext(const cmp_object_t *obj, int8_t *type, uint32_t *size); bool cmp_object_to_str(cmp_ctx_t *ctx, const cmp_object_t *obj, char *data, uint32_t buf_size); bool cmp_object_to_bin(cmp_ctx_t *ctx, const cmp_object_t *obj, void *data, uint32_t buf_size); #ifdef __cplusplus } /* extern "C" */ #endif #endif /* CMP_H_INCLUDED */ /* vi: set et ts=2 sw=2: */