Initial commit

This commit is contained in:
Tha_14
2024-02-22 21:43:11 +02:00
commit 1b96a031d2
1108 changed files with 157706 additions and 0 deletions

32
local_pod_repo/cmp/.gitignore vendored Normal file
View File

@ -0,0 +1,32 @@
# OS X
.DS_Store
# Xcode
build/
*.pbxuser
!default.pbxuser
*.mode1v3
!default.mode1v3
*.mode2v3
!default.mode2v3
*.perspectivev3
!default.perspectivev3
xcuserdata
*.xccheckout
profile
*.moved-aside
DerivedData
*.hmap
*.ipa
# Bundler
.bundle
# We recommend against adding the Pods directory to your .gitignore. However
# you should judge for yourself, the pros and cons are mentioned at:
# http://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control
#
# Note: if you ignore the Pods directory, make sure to uncomment
# `pod install` in .travis.yml
#
# Pods/

View File

@ -0,0 +1,22 @@
The MIT License (MIT)
Copyright (c) 2020 Charles Gunyon
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View File

@ -0,0 +1,30 @@
#
# Be sure to run `pod lib lint toxcore.podspec' to ensure this is a
# valid spec and remove all comments before submitting the spec.
#
# Any lines starting with a # are optional, but encouraged
#
# To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html
#
Pod::Spec.new do |s|
s.name = "cmp"
s.version = "20.0.0"
s.summary = "Cocoapods wrapper for cmp"
s.homepage = "https://github.com/camgunz/cmp"
s.license = 'MIT'
s.author = "Zoff"
s.source = {
:git => "https://github.com/camgunz/cmp.git",
:tag => s.version.to_s,
:submodules => true
}
s.pod_target_xcconfig = { 'ENABLE_BITCODE' => 'NO', 'OTHER_LDFLAGS' => '-read_only_relocs suppress' }
s.ios.deployment_target = '8.0'
s.requires_arc = true
s.source_files = 'cmp/*h', 'cmp/*.c'
end

54
local_pod_repo/cmp/cmp/.gitignore vendored Normal file
View File

@ -0,0 +1,54 @@
# Object files
*.o
*.ko
*.obj
*.elf
# Libraries
*.lib
*.a
# Shared objects (inc. Windows DLLs)
*.dll
*.so
*.so.*
*.dylib
# Executables
*.exe
*.out
*.app
*.i*86
*.x86_64
*.hex
# Vim swap files
*.swp
*.swo
# Coverage files
*.gcda
*.gcno
coverage/
# Profiling files
*.prof
# Testing executable
test/test-cmp
cmptest
cmptest2
cmpaddrtest
cmpmemtest
cmpnofloattest
cmpprof
cmpubtest
# Example executables
examples/example1
examples/example2
example1
example2
# Test data
cmp_data.dat

View File

@ -0,0 +1,4 @@
# Edit following two lines to set component requirements (see docs)
set(COMPONENT_SRCS "cmp.c" )
set(COMPONENT_ADD_INCLUDEDIRS ".")
register_component()

View File

@ -0,0 +1,50 @@
# Contributor Code of Conduct
As contributors and maintainers of this project, and in the interest of
fostering an open and welcoming community, we pledge to respect all people who
contribute through reporting issues, posting feature requests, updating
documentation, submitting pull requests or patches, and other activities.
We are committed to making participation in this project a harassment-free
experience for everyone, regardless of level of experience, gender, gender
identity and expression, sexual orientation, disability, personal appearance,
body size, race, ethnicity, age, religion, or nationality.
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery
* Personal attacks
* Trolling or insulting/derogatory comments
* Public or private harassment
* Publishing other's private information, such as physical or electronic
addresses, without explicit permission
* Other unethical or unprofessional conduct
Project maintainers have the right and responsibility to remove, edit, or
reject comments, commits, code, wiki edits, issues, and other contributions
that are not aligned to this Code of Conduct, or to ban temporarily or
permanently any contributor for other behaviors that they deem inappropriate,
threatening, offensive, or harmful.
By adopting this Code of Conduct, project maintainers commit themselves to
fairly and consistently applying these principles to every aspect of managing
this project. Project maintainers who do not follow or enforce the Code of
Conduct may be permanently removed from the project team.
This Code of Conduct applies both within project spaces and in public spaces
when an individual is representing the project or its community.
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported by contacting a project maintainer at charles.gunyon@gmail.com. All
complaints will be reviewed and investigated and will result in a response that
is deemed necessary and appropriate to the circumstances. Maintainers are
obligated to maintain confidentiality with regard to the reporter of an
incident.
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
version 1.3.0, available at
[http://contributor-covenant.org/version/1/3/0/][version]
[homepage]: http://contributor-covenant.org
[version]: http://contributor-covenant.org/version/1/3/0/

View File

@ -0,0 +1,22 @@
The MIT License (MIT)
Copyright (c) 2020 Charles Gunyon
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View File

@ -0,0 +1,123 @@
CC ?= gcc
CLANG ?= clang
EXTRA_CFLAGS ?= -Werror -Wall -Wextra -funsigned-char -fwrapv -Wconversion \
-Wno-sign-conversion -Wmissing-format-attribute \
-Wpointer-arith -Wformat-nonliteral -Winit-self \
-Wwrite-strings -Wshadow -Wenum-compare -Wempty-body \
-Wparentheses -Wcast-align -Wstrict-aliasing --pedantic-errors
CMPCFLAGS ?= -std=c89 -Wno-c99-extensions
TESTCFLAGS ?= -std=c99 -Wno-error=deprecated-declarations \
-Wno-deprecated-declarations -O0
NOFPUTESTCFLAGS ?= $(TESTCFLAGS) -DCMP_NO_FLOAT
ADDRCFLAGS ?= -fsanitize=address
MEMCFLAGS ?= -fsanitize=memory -fno-omit-frame-pointer \
-fno-optimize-sibling-calls
UBCFLAGS ?= -fsanitize=undefined
.PHONY: all clean test coverage
all: cmpunittest example1 example2
profile: cmpprof
@env LD_PRELOAD=/usr/lib/libprofiler.so CPUPROFILE=cmp.prof \
CPUPROFILE_FREQUENCY=1000 ./cmpprof
@pprof --web ./cmpprof cmp.prof
test: addrtest memtest nofloattest ubtest unittest
testprogs: cmpaddrtest cmpmemtest cmpnofloattest cmpubtest cmpunittest
addrtest: cmpaddrtest
@./cmpaddrtest
memtest: cmpmemtest
@./cmpmemtest
nofloattest: cmpnofloattest
@./cmpnofloattest
@rm -f *.gcno *.gcda *.info
ubtest: cmpubtest
@./cmpubtest
unittest: cmpunittest
@./cmpunittest
cmp.o: cmp.c
$(CC) $(CFLAGS) $(EXTRA_CFLAGS) $(CMPCFLAGS) \
-fprofile-arcs -ftest-coverage -g -I. -c cmp.c
cmpprof: cmp.o
$(CC) $(CFLAGS) $(EXTRA_CFLAGS) $(TESTCFLAGS) $(LDFLAGS) \
-fprofile-arcs -I. \
-o cmpprof cmp.o test/profile.c test/tests.c test/buf.c test/utils.c \
-lcmocka -lprofiler
cmpunittest: cmp.o
$(CC) $(CFLAGS) $(EXTRA_CFLAGS) $(TESTCFLAGS) $(LDFLAGS) \
-fprofile-arcs -ftest-coverage -g -I. \
-o cmpunittest cmp.o test/test.c test/tests.c test/buf.c test/utils.c \
-lcmocka
cmpnofloattest: cmp.o
$(CC) $(CFLAGS) $(EXTRA_CFLAGS) $(NOFPUTESTCFLAGS) $(LDFLAGS) \
-fprofile-arcs -ftest-coverage -g -I. \
-o cmpnofloattest cmp.o test/test.c test/tests.c test/buf.c \
test/utils.c \
-lcmocka
clangcmp.o: cmp.c
$(CLANG) $(CFLAGS) $(EXTRA_CFLAGS) $(CMPCFLAGS) \
-fprofile-arcs -ftest-coverage -g -I. -c cmp.c -o clangcmp.o
cmpaddrtest: clangcmp.o clean
$(CLANG) $(CFLAGS) $(EXTRA_CFLAGS) $(TESTCFLAGS) $(ADDRCFLAGS) $(LDFLAGS) \
-I. -o cmpaddrtest \
cmp.c test/test.c test/tests.c test/buf.c test/utils.c \
-lcmocka
cmpmemtest: clangcmp.o clean
$(CLANG) $(CFLAGS) $(EXTRA_CFLAGS) $(TESTCFLAGS) $(MEMCFLAGS) $(LDFLAGS) \
-I. -o cmpmemtest \
cmp.c test/test.c test/tests.c test/buf.c test/utils.c \
-lcmocka
cmpubtest: clangcmp.o clean
$(CLANG) $(CFLAGS) $(EXTRA_CFLAGS) $(TESTCFLAGS) $(UBCFLAGS) $(LDFLAGS) \
-I. -o cmpubtest \
cmp.c test/test.c test/tests.c test/buf.c test/utils.c \
-lcmocka
example1:
$(CC) $(CFLAGS) $(EXTRA_CFLAGS) --std=c89 -O3 -I. -o example1 \
cmp.c examples/example1.c
example2:
$(CC) $(CFLAGS) $(EXTRA_CFLAGS) --std=c89 -O3 -I. -o example2 \
cmp.c examples/example2.c
coverage:
@rm -f base_coverage.info test_coverage.info total_coverage.info
@rm -rf coverage
@lcov -q -c -i -d . -o base_coverage.info
@lcov -q -c -d . -o test_coverage.info
@lcov -q -a base_coverage.info -a test_coverage.info -o total_coverage.info
@lcov -q --summary total_coverage.info
@mkdir coverage
@genhtml -q -o coverage total_coverage.info
clean:
@rm -f cmp.prof
@rm -f cmpunittest
@rm -f cmpaddrtest
@rm -f cmpmemtest
@rm -f cmpubtest
@rm -f cmpnofloattest
@rm -f cmpprof
@rm -f example1
@rm -f example2
@rm -f *.o
@rm -f *.gcno *.gcda *.info
@rm -f cmp_data.dat

View File

@ -0,0 +1,223 @@
# CMP
[![Build Status](https://travis-ci.org/camgunz/cmp.svg?branch=master)](https://travis-ci.org/camgunz/cmp) [![Coverage Status](https://coveralls.io/repos/github/camgunz/cmp/badge.svg?branch=develop)](https://coveralls.io/github/camgunz/cmp?branch=develop)
CMP is a C implementation of the MessagePack serialization format. It
currently implements version 5 of the [MessagePack
Spec](http://github.com/msgpack/msgpack/blob/master/spec.md).
CMP's goal is to be lightweight and straightforward, forcing nothing on the
programmer.
## License
While I'm a big believer in the GPL, I license CMP under the MIT license.
## Example Usage
The following examples use a file as the backend, and are modeled after the
examples included with the msgpack-c project.
```C
#include <stdio.h>
#include <stdlib.h>
#include "cmp.h"
static bool read_bytes(void *data, size_t sz, FILE *fh) {
return fread(data, sizeof(uint8_t), sz, fh) == (sz * sizeof(uint8_t));
}
static bool file_reader(cmp_ctx_t *ctx, void *data, size_t limit) {
return read_bytes(data, limit, (FILE *)ctx->buf);
}
static bool file_skipper(cmp_ctx_t *ctx, size_t count) {
return fseek((FILE *)ctx->buf, count, SEEK_CUR);
}
static size_t file_writer(cmp_ctx_t *ctx, const void *data, size_t count) {
return fwrite(data, sizeof(uint8_t), count, (FILE *)ctx->buf);
}
static void error_and_exit(const char *msg) {
fprintf(stderr, "%s\n\n", msg);
exit(EXIT_FAILURE);
}
int main(void) {
FILE *fh = NULL;
cmp_ctx_t cmp = {0};
uint32_t array_size = 0;
uint32_t str_size = 0;
char hello[6] = {0};
char message_pack[12] = {0};
fh = fopen("cmp_data.dat", "w+b");
if (fh == NULL) {
error_and_exit("Error opening data.dat");
}
cmp_init(&cmp, fh, file_reader, file_skipper, file_writer);
if (!cmp_write_array(&cmp, 2)) {
error_and_exit(cmp_strerror(&cmp));
}
if (!cmp_write_str(&cmp, "Hello", 5)) {
error_and_exit(cmp_strerror(&cmp));
}
if (!cmp_write_str(&cmp, "MessagePack", 11)) {
error_and_exit(cmp_strerror(&cmp));
}
rewind(fh);
if (!cmp_read_array(&cmp, &array_size)) {
error_and_exit(cmp_strerror(&cmp));
}
/* You can read the str byte size and then read str bytes... */
if (!cmp_read_str_size(&cmp, &str_size)) {
error_and_exit(cmp_strerror(&cmp));
}
if (str_size > (sizeof(hello) - 1)) {
error_and_exit("Packed 'hello' length too long\n");
}
if (!read_bytes(hello, str_size, fh)) {
error_and_exit(cmp_strerror(&cmp));
}
/*
* ...or you can set the maximum number of bytes to read and do it all in
* one call
*/
str_size = sizeof(message_pack);
if (!cmp_read_str(&cmp, message_pack, &str_size)) {
error_and_exit(cmp_strerror(&cmp));
}
printf("Array Length: %u.\n", array_size);
printf("[\"%s\", \"%s\"]\n", hello, message_pack);
fclose(fh);
return EXIT_SUCCESS;
}
```
## Advanced Usage
See the `examples` folder.
## Fast, Lightweight, Flexible, and Robust
CMP uses no internal buffers; conversions, encoding and decoding are done on
the fly.
CMP's source and header file together are ~4k LOC.
CMP makes no heap allocations.
CMP uses standardized types rather than declaring its own, and it depends only
on `stdbool.h`, `stdint.h` and `string.h`.
CMP is written using C89 (ANSI C), aside, of course, from its use of
fixed-width integer types and `bool`.
On the other hand, CMP's test suite requires C99.
CMP only requires the programmer supply a read function, a write function, and
an optional skip function. In this way, the programmer can use CMP on memory,
files, sockets, etc.
CMP is portable. It uses fixed-width integer types, and checks the endianness
of the machine at runtime before swapping bytes (MessagePack is big-endian).
CMP provides a fairly comprehensive error reporting mechanism modeled after
`errno` and `strerror`.
CMP is thread aware; while contexts cannot be shared between threads, each
thread may use its own context freely.
CMP is tested using the MessagePack test suite as well as a large set of custom
test cases. Its small test program is compiled with clang using `-Wall -Werror
-Wextra ...` along with several other flags, and generates no compilation
errors in either clang or GCC.
CMP's source is written as readably as possible, using explicit, descriptive
variable names and a consistent, clear style.
CMP's source is written to be as secure as possible. Its testing suite checks
for invalid values, and data is always treated as suspect before it passes
validation.
CMP's API is designed to be clear, convenient and unsurprising. Strings are
null-terminated, binary data is not, error codes are clear, and so on.
CMP provides optional backwards compatibility for use with other MessagePack
implementations that only implement version 4 of the spec.
## Building
There is no build system for CMP. The programmer can drop `cmp.c` and `cmp.h`
in their source tree and modify as necessary. No special compiler settings are
required to build it, and it generates no compilation errors in either clang or
gcc.
## Versioning
CMP's versions are single integers. I don't use semantic versioning because
I don't guarantee that any version is completely compatible with any other. In
general, semantic versioning provides a false sense of security. You should be
evaluating compatibility yourself, not relying on some stranger's versioning
convention.
## Stability
I only guarantee stability for versions released on
[the releases page](../../releases). While rare, both `master` and `develop`
branches may have errors or mismatched versions.
## Backwards Compatibility
Version 4 of the MessagePack spec has no `BIN` type, and provides no `STR8`
marker. In order to remain backwards compatible with version 4 of MessagePack,
do the following:
Avoid these functions:
- `cmp_write_bin`
- `cmp_write_bin_marker`
- `cmp_write_str8_marker`
- `cmp_write_str8`
- `cmp_write_bin8_marker`
- `cmp_write_bin8`
- `cmp_write_bin16_marker`
- `cmp_write_bin16`
- `cmp_write_bin32_marker`
- `cmp_write_bin32`
Use these functions in lieu of their v5 counterparts:
- `cmp_write_str_marker_v4` instead of `cmp_write_str_marker`
- `cmp_write_str_v4` instead of `cmp_write_str`
- `cmp_write_object_v4` instead of `cmp_write_object`
## Disabling Floating Point Operations
Thanks to [tdragon](https://github.com/tdragon) it's possible to disable
floating point operations in CMP by defining `CMP_NO_FLOAT`. No floating point
functionality will be included. Fair warning: this changes the ABI.
## Setting Endianness at Compile Time
CMP will honor `WORDS_BIGENDIAN`. If defined to `0` it will convert data
to/from little-endian format when writing/reading. If defined to `1` it won't.
If not defined, CMP will check at runtime.

View File

@ -0,0 +1,14 @@
# To Do
- Work on fixing double-copy issue
- Essentially everything is written to a `cmp_object_t` before it's written
out to the caller, which is inefficient. The reasoning for this is to not
pollute the caller's environment should an error occur, but in practice
it's probably better to say, "you can't trust the contents of output
arguments you pass to CMP if the call fails".
- Build real docs
- Probably still just a Markdown file, but still, things have gotten complex
enough that `cmp.h` and `README.md` don't really cover it anymore.
- Prevent users from using extended types < 0 (reserved by MessagePack)

3561
local_pod_repo/cmp/cmp/cmp.c Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,572 @@
/*
The MIT License (MIT)
Copyright (c) 2020 Charles Gunyon
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#ifndef CMP_H_INCLUDED
#define CMP_H_INCLUDED
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
struct cmp_ctx_s;
typedef bool (*cmp_reader)(struct cmp_ctx_s *ctx, void *data, size_t limit);
typedef bool (*cmp_skipper)(struct cmp_ctx_s *ctx, size_t count);
typedef size_t (*cmp_writer)(struct cmp_ctx_s *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;
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;
};
typedef struct cmp_ctx_s {
uint8_t error;
void *buf;
cmp_reader read;
cmp_skipper skip;
cmp_writer write;
} cmp_ctx_t;
typedef struct cmp_object_s {
uint8_t type;
union cmp_object_data_u 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(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);
/*
* WARNING: THIS FUNCTION IS DEPRECATED AND WILL BE REMOVED IN A FUTURE RELEASE
*
* There is no way to track depths across elements without allocation. For
* example, an array constructed as: `[ [] [] [] [] [] [] [] [] [] [] ]`
* should be able to be skipped with `cmp_skip_object_limit(&cmp, &obj, 2)`.
* However, because we cannot track depth across the elements, there's no way
* to reset it after descending down into each element.
*
* This is similar to `cmp_skip_object`, except it tolerates up to `limit`
* levels of nesting. For example, in order to skip an array that contains a
* map, call `cmp_skip_object_limit(ctx, &obj, 2)`. Or in other words,
* `cmp_skip_object(ctx, &obj)` acts similarly to `cmp_skip_object_limit(ctx,
* &obj, 0)`
*
* Specifically, `limit` refers to depth, not breadth. So in order to skip an
* array that contains two arrays that each contain 3 strings, you would call
* `cmp_skip_object_limit(ctx, &obj, 2). In order to skip an array that
* contains 4 arrays that each contain 1 string, you would still call
* `cmp_skip_object_limit(ctx, &obj, 2).
*/
bool cmp_skip_object_limit(cmp_ctx_t *ctx, cmp_object_t *obj, uint32_t limit)
#ifdef __GNUC__
__attribute__((deprecated))
#endif
;
#ifdef _MSC_VER
#pragma deprecated(cmp_skip_object_limit)
#endif
/*
* ============================================================================
* === 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);
bool cmp_object_as_float(const cmp_object_t *obj, float *f);
bool cmp_object_as_double(const cmp_object_t *obj, double *d);
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
/*
* ============================================================================
* === Backwards compatibility defines
* ============================================================================
*/
#define cmp_write_int cmp_write_integer
#define cmp_write_sint cmp_write_integer
#define cmp_write_sinteger cmp_write_integer
#define cmp_write_uint cmp_write_uinteger
#define cmp_read_sinteger cmp_read_integer
#endif /* CMP_H_INCLUDED */
/* vi: set et ts=2 sw=2: */

View File

@ -0,0 +1,116 @@
/*
The MIT License (MIT)
Copyright (c) 2020 Charles Gunyon
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#include <stdio.h>
#include <stdlib.h>
#include "cmp.h"
static bool read_bytes(void *data, size_t sz, FILE *fh) {
return fread(data, sizeof(uint8_t), sz, fh) == (sz * sizeof(uint8_t));
}
static bool file_reader(cmp_ctx_t *ctx, void *data, size_t limit) {
return read_bytes(data, limit, (FILE *)ctx->buf);
}
static bool file_skipper(cmp_ctx_t *ctx, size_t count) {
return fseek((FILE *)ctx->buf, count, SEEK_CUR);
}
static size_t file_writer(cmp_ctx_t *ctx, const void *data, size_t count) {
return fwrite(data, sizeof(uint8_t), count, (FILE *)ctx->buf);
}
static void error_and_exit(const char *msg) {
fprintf(stderr, "%s\n\n", msg);
exit(EXIT_FAILURE);
}
int main(void) {
FILE *fh = NULL;
cmp_ctx_t cmp = {0};
uint32_t array_size = 0;
uint32_t str_size = 0;
char hello[6] = {0};
char message_pack[12] = {0};
fh = fopen("cmp_data.dat", "w+b");
if (fh == NULL) {
error_and_exit("Error opening data.dat");
}
cmp_init(&cmp, fh, file_reader, file_skipper, file_writer);
if (!cmp_write_array(&cmp, 2)) {
error_and_exit(cmp_strerror(&cmp));
}
if (!cmp_write_str(&cmp, "Hello", 5)) {
error_and_exit(cmp_strerror(&cmp));
}
if (!cmp_write_str(&cmp, "MessagePack", 11)) {
error_and_exit(cmp_strerror(&cmp));
}
rewind(fh);
if (!cmp_read_array(&cmp, &array_size)) {
error_and_exit(cmp_strerror(&cmp));
}
/* You can read the str byte size and then read str bytes... */
if (!cmp_read_str_size(&cmp, &str_size)) {
error_and_exit(cmp_strerror(&cmp));
}
if (str_size > (sizeof(hello) - 1)) {
error_and_exit("Packed 'hello' length too long\n");
}
if (!read_bytes(hello, str_size, fh)) {
error_and_exit(cmp_strerror(&cmp));
}
/*
* ...or you can set the maximum number of bytes to read and do it all in
* one call
*/
str_size = sizeof(message_pack);
if (!cmp_read_str(&cmp, message_pack, &str_size)) {
error_and_exit(cmp_strerror(&cmp));
}
printf("Array Length: %u.\n", array_size);
printf("[\"%s\", \"%s\"]\n", hello, message_pack);
fclose(fh);
return EXIT_SUCCESS;
}

View File

@ -0,0 +1,531 @@
/*
The MIT License (MIT)
Copyright (c) 2020 Charles Gunyon
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#include <errno.h>
#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "cmp.h"
static bool read_bytes(void *data, size_t sz, FILE *fh) {
return fread(data, sizeof(uint8_t), sz, fh) == (sz * sizeof(uint8_t));
}
static bool file_reader(cmp_ctx_t *ctx, void *data, size_t limit) {
return read_bytes(data, limit, (FILE *)ctx->buf);
}
static bool file_skipper(cmp_ctx_t *ctx, size_t count) {
return fseek((FILE *)ctx->buf, count, SEEK_CUR);
}
static size_t file_writer(cmp_ctx_t *ctx, const void *data, size_t count) {
return fwrite(data, sizeof(uint8_t), count, (FILE *)ctx->buf);
}
static void error_and_exit(const char *msg) {
fprintf(stderr, "%s\n\n", msg);
exit(EXIT_FAILURE);
}
int main(void) {
FILE *fh = {0};
cmp_ctx_t cmp = {0};
uint16_t year = 1983;
uint8_t month = 5;
uint8_t day = 28;
int64_t sint = 0;
uint64_t uint = 0;
float flt = 0.0f;
double dbl = 0.0;
bool boolean = false;
uint8_t fake_bool = 0;
uint32_t string_size = 0;
uint32_t array_size = 0;
uint32_t binary_size = 0;
uint32_t map_size = 0;
int8_t ext_type = 0;
uint32_t ext_size = 0;
char sbuf[12] = {0};
fh = fopen("cmp_data.dat", "w+b");
if (fh == NULL) {
error_and_exit("Error opening data.dat");
}
cmp_init(&cmp, fh, file_reader, file_skipper, file_writer);
/*
* When you write an array, you first specify the number of array
* elements, then you write that many elements.
*/
if (!cmp_write_array(&cmp, 9)) {
error_and_exit(cmp_strerror(&cmp));
}
if (!cmp_write_sint(&cmp, -14)) {
error_and_exit(cmp_strerror(&cmp));
}
if (!cmp_write_uint(&cmp, 38)) {
error_and_exit(cmp_strerror(&cmp));
}
if (!cmp_write_float(&cmp, 1.8f)) {
error_and_exit(cmp_strerror(&cmp));
}
if (!cmp_write_double(&cmp, 300.4)) {
error_and_exit(cmp_strerror(&cmp));
}
if (!cmp_write_nil(&cmp)) {
error_and_exit(cmp_strerror(&cmp));
}
if (!cmp_write_true(&cmp)) {
error_and_exit(cmp_strerror(&cmp));
}
if (!cmp_write_false(&cmp)) {
error_and_exit(cmp_strerror(&cmp));
}
if (!cmp_write_bool(&cmp, false)) {
error_and_exit(cmp_strerror(&cmp));
}
if (!cmp_write_u8_as_bool(&cmp, 1)) {
error_and_exit(cmp_strerror(&cmp));
}
/* Array full */
/*
* Maps work similar to arrays, but the length is in "pairs", so this
* writes 2 pairs to the map. Subsequently, pairs are written in key,
* value order.
*/
if (!cmp_write_map(&cmp, 2)) {
error_and_exit(cmp_strerror(&cmp));
}
/* You can write string data all at once... */
if (!cmp_write_str(&cmp, "Greeting", 8)) {
error_and_exit(cmp_strerror(&cmp));
}
if (!cmp_write_str(&cmp, "Hello", 5)) {
error_and_exit(cmp_strerror(&cmp));
}
if (!cmp_write_str(&cmp, "Name", 4)) {
error_and_exit(cmp_strerror(&cmp));
}
/* ...or in chunks */
if (!cmp_write_str_marker(&cmp, 5)) {
error_and_exit(cmp_strerror(&cmp));
}
if (file_writer(&cmp, "Li", 2) != 2) {
error_and_exit(strerror(errno));
}
if (file_writer(&cmp, "nus", 3) != 3) {
error_and_exit(strerror(errno));
}
/* Map full */
/* Binary data functions the same as string data */
if (!cmp_write_bin(&cmp, "MessagePack", 11)) {
error_and_exit(cmp_strerror(&cmp));
}
if (!cmp_write_bin_marker(&cmp, 8)) {
error_and_exit(cmp_strerror(&cmp));
}
if (file_writer(&cmp, "is ", 3) != 3) {
error_and_exit(strerror(errno));
}
if (file_writer(&cmp, "great", 5) != 5) {
error_and_exit(strerror(errno));
}
/*
* With extended types, you can create your own custom types. Here we
* create a simple date type.
*/
/* cmp_write_ext_marker(type, size) */
if (!cmp_write_ext_marker(&cmp, 1, 4)) {
error_and_exit(cmp_strerror(&cmp));
}
file_writer(&cmp, &year, sizeof(uint16_t));
file_writer(&cmp, &month, sizeof(uint8_t));
file_writer(&cmp, &day, sizeof(uint8_t));
/* Now we can read the data back just as easily */
rewind(fh);
if (!cmp_read_array(&cmp, &array_size)) {
error_and_exit(cmp_strerror(&cmp));
}
if (array_size != 9) {
error_and_exit("Array size was not 9");
}
if (!cmp_read_sinteger(&cmp, &sint)) {
error_and_exit(cmp_strerror(&cmp));
}
if (sint != -14) {
error_and_exit("Signed int was not -14");
}
if (!cmp_read_uinteger(&cmp, &uint)) {
error_and_exit(cmp_strerror(&cmp));
}
if (uint != 38) {
error_and_exit("Unsigned int was not 38");
}
if (!cmp_read_float(&cmp, &flt)) {
error_and_exit(cmp_strerror(&cmp));
}
if (flt != 1.8f) {
error_and_exit("Float was not 1.8f");
}
if (!cmp_read_double(&cmp, &dbl)) {
error_and_exit(cmp_strerror(&cmp));
}
if (dbl != 300.4) {
error_and_exit("Double was not 300.f");
}
if (!cmp_read_nil(&cmp)) {
error_and_exit(cmp_strerror(&cmp));
}
if (!cmp_read_bool(&cmp, &boolean)) {
error_and_exit(cmp_strerror(&cmp));
}
if (boolean != true) {
error_and_exit("First boolean was not true");
}
if (!cmp_read_bool(&cmp, &boolean)) {
error_and_exit(cmp_strerror(&cmp));
}
if (boolean != false) {
error_and_exit("Second boolean was not false");
}
if (!cmp_read_bool(&cmp, &boolean)) {
error_and_exit(cmp_strerror(&cmp));
}
if (boolean != false) {
error_and_exit("Third boolean was not false");
}
if (!cmp_read_bool_as_u8(&cmp, &fake_bool)) {
error_and_exit(cmp_strerror(&cmp));
}
if (fake_bool != 1) {
fprintf(stderr, "%u.\n", fake_bool);
error_and_exit("Third boolean (u8) was not 1");
}
if (!cmp_read_map(&cmp, &map_size)) {
error_and_exit(cmp_strerror(&cmp));
}
if (map_size != 2) {
error_and_exit("Map size was not 2");
}
/*
* String reading here. Note that normally strings are encoded using
* UTF-8. I have cleverly restricted this example to ASCII, which overlaps
* UTF-8 encoding, but this must not be assumed in real-world code.
*
* You can read strings in two ways. Either you can read the string's size
* in bytes and then read the bytes manually...
*/
if (!cmp_read_str_size(&cmp, &string_size)) {
error_and_exit(cmp_strerror(&cmp));
}
if (string_size != 8) {
error_and_exit("Greeting string key size was not 8");
}
if (!read_bytes(sbuf, 8, fh)) {
error_and_exit(strerror(errno));
}
sbuf[string_size] = 0;
if (strncmp(sbuf, "Greeting", 8) != 0) {
error_and_exit("Greeting string key name was not 'Greeting'");
}
/*
* ...or you can set the maximum number of bytes to read and do it all in
* one call. cmp_read_str will write no more than "size" bytes, including
* the terminating NULL, to the passed buffer. If the string's size
* exceeds the passed buffer size, the "size" input is set to the number of
* bytes necessary, not including the terminating NULL. Otherwise, the
* "size" input is set to the number of bytes written, not including the
* terminating NULL.
*/
string_size = sizeof(sbuf);
if (!cmp_read_str(&cmp, sbuf, &string_size)) {
error_and_exit(cmp_strerror(&cmp));
}
if (strncmp(sbuf, "Hello", 5) != 0) {
error_and_exit("Greeting string value was not 'Hello'");
}
string_size = sizeof(sbuf);
if (!cmp_read_str(&cmp, sbuf, &string_size)) {
error_and_exit(cmp_strerror(&cmp));
}
if (strncmp(sbuf, "Name", 4) != 0) {
error_and_exit("Name key name was not 'Name'");
}
string_size = sizeof(sbuf);
if (!cmp_read_str(&cmp, sbuf, &string_size)) {
error_and_exit(cmp_strerror(&cmp));
}
if (strncmp(sbuf, "Linus", 5) != 0) {
error_and_exit("Name key value was not 'Linus'");
}
memset(sbuf, 0, sizeof(sbuf));
binary_size = sizeof(sbuf);
if (!cmp_read_bin(&cmp, sbuf, &binary_size)) {
error_and_exit(cmp_strerror(&cmp));
}
if (memcmp(sbuf, "MessagePack", 11) != 0) {
error_and_exit("1st binary value was not 'MessagePack'");
}
memset(sbuf, 0, sizeof(sbuf));
binary_size = sizeof(sbuf);
if (!cmp_read_bin(&cmp, sbuf, &binary_size)) {
error_and_exit(cmp_strerror(&cmp));
}
if (memcmp(sbuf, "is great", 8) != 0) {
error_and_exit("2nd binary value was not 'is great'");
}
if (!cmp_read_ext_marker(&cmp, &ext_type, &ext_size)) {
error_and_exit(cmp_strerror(&cmp));
}
if (!read_bytes(&year, sizeof(uint16_t), fh)) {
error_and_exit(strerror(errno));
}
if (!read_bytes(&month, sizeof(uint8_t), fh)) {
error_and_exit(strerror(errno));
}
if (!read_bytes(&day, sizeof(uint8_t), fh)) {
error_and_exit(strerror(errno));
}
if (year != 1983) {
error_and_exit("Year was not 1983");
}
if (month != 5) {
error_and_exit("Month was not 5");
}
if (day != 28) {
error_and_exit("Day was not 28");
}
rewind(fh);
/* Alternately, you can read objects until the stream is empty */
while (1) {
cmp_object_t obj;
if (!cmp_read_object(&cmp, &obj)) {
if (feof(fh)) {
break;
}
error_and_exit(cmp_strerror(&cmp));
}
switch (obj.type) {
case CMP_TYPE_POSITIVE_FIXNUM:
case CMP_TYPE_UINT8:
printf("Unsigned Integer: %u\n", obj.as.u8);
break;
case CMP_TYPE_FIXMAP:
case CMP_TYPE_MAP16:
case CMP_TYPE_MAP32:
printf("Map: %u\n", obj.as.map_size);
break;
case CMP_TYPE_FIXARRAY:
case CMP_TYPE_ARRAY16:
case CMP_TYPE_ARRAY32:
printf("Array: %u\n", obj.as.array_size);
break;
case CMP_TYPE_FIXSTR:
case CMP_TYPE_STR8:
case CMP_TYPE_STR16:
case CMP_TYPE_STR32:
if (!read_bytes(sbuf, obj.as.str_size, fh)) {
error_and_exit(strerror(errno));
}
sbuf[obj.as.str_size] = 0;
printf("String: %s\n", sbuf);
break;
case CMP_TYPE_BIN8:
case CMP_TYPE_BIN16:
case CMP_TYPE_BIN32:
memset(sbuf, 0, sizeof(sbuf));
if (!read_bytes(sbuf, obj.as.bin_size, fh)) {
error_and_exit(strerror(errno));
}
printf("Binary: %s\n", sbuf);
break;
case CMP_TYPE_NIL:
printf("NULL\n");
break;
case CMP_TYPE_BOOLEAN:
if (obj.as.boolean) {
printf("Boolean: true\n");
}
else {
printf("Boolean: false\n");
}
break;
case CMP_TYPE_EXT8:
case CMP_TYPE_EXT16:
case CMP_TYPE_EXT32:
case CMP_TYPE_FIXEXT1:
case CMP_TYPE_FIXEXT2:
case CMP_TYPE_FIXEXT4:
case CMP_TYPE_FIXEXT8:
case CMP_TYPE_FIXEXT16:
if (obj.as.ext.type == 1) { /* Date object */
if (!read_bytes(&year, sizeof(uint16_t), fh)) {
error_and_exit(strerror(errno));
}
if (!read_bytes(&month, sizeof(uint8_t), fh)) {
error_and_exit(strerror(errno));
}
if (!read_bytes(&day, sizeof(uint8_t), fh)) {
error_and_exit(strerror(errno));
}
printf("Date: %u/%u/%u\n", year, month, day);
}
else {
printf("Extended type {%d, %u}: ",
obj.as.ext.type, obj.as.ext.size
);
while (obj.as.ext.size--) {
read_bytes(sbuf, sizeof(uint8_t), fh);
printf("%02x ", sbuf[0]);
}
printf("\n");
}
break;
case CMP_TYPE_FLOAT:
printf("Float: %f\n", obj.as.flt);
break;
case CMP_TYPE_DOUBLE:
printf("Double: %f\n", obj.as.dbl);
break;
case CMP_TYPE_UINT16:
printf("Unsigned Integer: %u\n", obj.as.u16);
break;
case CMP_TYPE_UINT32:
printf("Unsigned Integer: %u\n", obj.as.u32);
break;
case CMP_TYPE_UINT64:
printf("Unsigned Integer: %" PRIu64 "\n", obj.as.u64);
break;
case CMP_TYPE_NEGATIVE_FIXNUM:
case CMP_TYPE_SINT8:
printf("Signed Integer: %d\n", obj.as.s8);
break;
case CMP_TYPE_SINT16:
printf("Signed Integer: %d\n", obj.as.s16);
break;
case CMP_TYPE_SINT32:
printf("Signed Integer: %d\n", obj.as.s32);
break;
case CMP_TYPE_SINT64:
printf("Signed Integer: %" PRId64 "\n", obj.as.s64);
break;
default:
printf("Unrecognized object type %u\n", obj.type);
break;
}
}
fclose(fh);
return EXIT_SUCCESS;
}

View File

@ -0,0 +1,569 @@
/*
The MIT License (MIT)
Copyright (c) 2020 Charles Gunyon
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#include <inttypes.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "utils.h"
#include "buf.h"
static void check_cursor(buf_t *buf) {
if (buf->cursor > buf->size)
buf->size = buf->cursor;
}
buf_t* M_BufferNew(void) {
buf_t *buf = calloc(1, sizeof(buf_t));
if (buf == NULL)
error_and_exit("M_BufferNew: Calloc returned NULL.");
M_BufferInit(buf);
return buf;
}
buf_t* M_BufferNewWithCapacity(size_t capacity) {
buf_t *buf = calloc(1, sizeof(buf_t));
if (buf == NULL)
error_and_exit("M_BufferNew: calloc returned NULL");
M_BufferInitWithCapacity(buf, capacity);
return buf;
}
void M_BufferInit(buf_t *buf) {
buf->size = 0;
buf->capacity = 0;
buf->cursor = 0;
buf->data = NULL;
}
void M_BufferInitWithCapacity(buf_t *buf, size_t capacity) {
M_BufferInit(buf);
M_BufferEnsureTotalCapacity(buf, capacity);
}
size_t M_BufferGetCapacity(const buf_t *buf) {
return buf->capacity;
}
size_t M_BufferGetSize(const buf_t *buf) {
return buf->size;
}
size_t M_BufferGetCursor(const buf_t *buf) {
return buf->cursor;
}
char* M_BufferGetData(const buf_t *buf) {
return buf->data;
}
char* M_BufferGetDataAtCursor(const buf_t *buf) {
return buf->data + buf->cursor;
}
void M_BufferEnsureCapacity(buf_t *buf, size_t capacity) {
size_t needed_capacity = buf->cursor + capacity;
if (buf->capacity < needed_capacity) {
buf->data = realloc(buf->data, needed_capacity * sizeof(uint8_t));
if (buf->data == NULL) {
error_and_exit(
"M_BufferEnsureCapacity: Reallocating buffer data failed"
);
}
memset(buf->data + buf->capacity, 0, needed_capacity - buf->capacity);
buf->capacity = needed_capacity;
}
}
void M_BufferEnsureTotalCapacity(buf_t *buf, size_t capacity) {
if (buf->capacity < capacity) {
size_t old_capacity = buf->capacity;
buf->capacity = capacity;
buf->data = realloc(buf->data, buf->capacity * sizeof(uint8_t));
if (buf->data == NULL)
error_and_exit("M_BufferEnsureCapacity: Allocating buffer data failed");
memset(buf->data + old_capacity, 0, buf->capacity - old_capacity);
}
}
void M_BufferCopy(buf_t *dst, const buf_t *src) {
M_BufferSetData(dst, M_BufferGetData(src), M_BufferGetSize(src));
}
void M_BufferCursorCopy(buf_t *dst, const buf_t *src) {
M_BufferWrite(
dst,
M_BufferGetDataAtCursor(src),
M_BufferGetSize(src) - (M_BufferGetCursor(src) - 1)
);
}
bool M_BufferMove(buf_t *buf, size_t dpos, size_t spos, size_t count) {
if ((spos + count) > M_BufferGetSize(buf))
return false;
M_BufferEnsureTotalCapacity(buf, dpos + count);
memmove(M_BufferGetData(buf) + dpos, M_BufferGetData(buf) + spos, count);
return true;
}
void M_BufferSetData(buf_t *buf, const void *data, size_t size) {
M_BufferClear(buf);
M_BufferEnsureTotalCapacity(buf, size);
M_BufferWrite(buf, data, size);
}
void M_BufferSetString(buf_t *buf, const char *data, size_t length) {
M_BufferClear(buf);
M_BufferWriteString(buf, data, length);
}
bool M_BufferSetFile(buf_t *buf, const char *filename) {
FILE *fp;
size_t length;
bool out = false;
if ((fp = fopen(filename, "rb")) == NULL)
return false;
fseek(fp, 0, SEEK_END);
length = ftell(fp);
fseek(fp, 0, SEEK_SET);
M_BufferClear(buf);
M_BufferEnsureTotalCapacity(buf, length);
if (fread(buf->data, sizeof(uint8_t), length, fp) == length) {
buf->cursor = length;
buf->size = length;
out = true;
}
else {
M_BufferClear(buf);
out = false;
}
fclose(fp);
return out;
}
bool M_BufferSeek(buf_t *buf, size_t pos) {
if (pos > buf->size)
return false;
buf->cursor = pos;
return true;
}
bool M_BufferSeekBackward(buf_t *buf, size_t count) {
if (count > buf->cursor)
return false;
buf->cursor -= count;
return true;
}
bool M_BufferSeekForward(buf_t *buf, size_t count) {
if (buf->cursor + count > buf->size)
return false;
buf->cursor += count;
return true;
}
uint8_t M_BufferPeek(const buf_t *buf) {
return *(buf->data + buf->cursor);
}
void M_BufferWrite(buf_t *buf, const void *data, size_t size) {
M_BufferEnsureCapacity(buf, size);
memcpy(buf->data + buf->cursor, data, size);
buf->cursor += size;
check_cursor(buf);
}
void M_BufferWriteBool(buf_t *buf, bool b) {
M_BufferWriteBools(buf, &b, 1);
}
void M_BufferWriteBools(buf_t *buf, const bool *bools, size_t count) {
M_BufferEnsureCapacity(buf, count * sizeof(bool));
M_BufferWriteChars(buf, (char *)bools, count * sizeof(bool));
}
void M_BufferWriteChar(buf_t *buf, char c) {
M_BufferWriteChars(buf, &c, 1);
}
void M_BufferWriteChars(buf_t *buf, const char *chars, size_t count) {
M_BufferWrite(buf, chars, count * sizeof(char));
}
void M_BufferWriteUChar(buf_t *buf, unsigned char c) {
M_BufferWriteUChars(buf, &c, 1);
}
void M_BufferWriteUChars(buf_t *buf, const unsigned char *uchars, size_t count) {
M_BufferWrite(buf, uchars, count * sizeof(unsigned char));
}
void M_BufferWriteShort(buf_t *buf, short s) {
M_BufferWriteShorts(buf, &s, 1);
}
void M_BufferWriteShorts(buf_t *buf, const short *shorts, size_t count) {
M_BufferEnsureCapacity(buf, count * sizeof(short));
M_BufferWriteChars(buf, (char *)shorts, count * sizeof(short));
}
void M_BufferWriteUShort(buf_t *buf, unsigned short s) {
M_BufferWriteUShorts(buf, &s, 1);
}
void M_BufferWriteUShorts(buf_t *buf, const unsigned short *ushorts,
size_t count) {
M_BufferEnsureCapacity(buf, count * sizeof(unsigned short));
M_BufferWriteChars(buf, (char *)ushorts, count * sizeof(unsigned short));
}
void M_BufferWriteInt(buf_t *buf, int i) {
M_BufferWriteInts(buf, &i, 1);
}
void M_BufferWriteInts(buf_t *buf, const int *ints, size_t count) {
M_BufferEnsureCapacity(buf, count * sizeof(int));
M_BufferWriteChars(buf, (char *)ints, count * sizeof(int));
}
void M_BufferWriteUInt(buf_t *buf, unsigned int s) {
M_BufferWriteUInts(buf, &s, 1);
}
void M_BufferWriteUInts(buf_t *buf, const unsigned int *uints, size_t count) {
M_BufferEnsureCapacity(buf, count * sizeof(unsigned int));
M_BufferWriteChars(buf, (char *)uints, count * sizeof(unsigned int));
}
void M_BufferWriteLong(buf_t *buf, int64_t l) {
M_BufferWriteLongs(buf, &l, 1);
}
void M_BufferWriteLongs(buf_t *buf, const int64_t *longs, size_t count) {
M_BufferEnsureCapacity(buf, count * sizeof(int64_t));
M_BufferWriteChars(buf, (char *)longs, count * sizeof(int64_t));
}
void M_BufferWriteULong(buf_t *buf, uint64_t l) {
M_BufferWriteULongs(buf, &l, 1);
}
void M_BufferWriteULongs(buf_t *buf, const uint64_t *ulongs, size_t count) {
M_BufferEnsureCapacity(buf, count * sizeof(int64_t));
M_BufferWriteChars(buf, (char *)ulongs, count * sizeof(uint64_t));
}
#ifndef CMP_NO_FLOAT
void M_BufferWriteFloat(buf_t *buf, float f) {
M_BufferWriteFloats(buf, &f, 1);
}
void M_BufferWriteFloats(buf_t *buf, const float *floats, size_t count) {
M_BufferEnsureCapacity(buf, count * sizeof(float));
M_BufferWriteChars(buf, (char *)floats, count * sizeof(floats));
}
void M_BufferWriteDouble(buf_t *buf, double d) {
M_BufferWriteDoubles(buf, &d, 1);
}
void M_BufferWriteDoubles(buf_t *buf, const double *doubles, size_t count) {
M_BufferEnsureCapacity(buf, count * sizeof(double));
M_BufferWriteChars(buf, (char *)doubles, count * sizeof(doubles));
}
#endif
void M_BufferWriteString(buf_t *buf, const char *string, size_t length) {
M_BufferEnsureCapacity(buf, length + 1);
strncpy(buf->data + buf->cursor, string, length + 1);
buf->cursor += (length + 1);
check_cursor(buf);
}
void M_BufferWriteZeros(buf_t *buf, size_t count) {
M_BufferEnsureCapacity(buf, count);
for (size_t i = 0; i < count; i++)
buf->data[buf->cursor++] = 0;
check_cursor(buf);
}
bool M_BufferEqualsString(const buf_t *buf, const char *s) {
if (strncmp(buf->data + buf->cursor, s, buf->size - buf->cursor) == 0)
return true;
return false;
}
bool M_BufferEqualsData(const buf_t *buf, const void *d, size_t size) {
if (buf->cursor + size > buf->size)
return false;
if (memcmp(buf->data + buf->cursor, d, size) == 0)
return true;
return false;
}
bool M_BufferRead(buf_t *buf, void *data, size_t size) {
if (buf->cursor + size > buf->size)
return false;
if (size == 1)
*((char *)data) = *(buf->data + buf->cursor);
else
memcpy(data, buf->data + buf->cursor, size);
buf->cursor += size;
return true;
}
bool M_BufferReadBool(buf_t *buf, bool *b) {
return M_BufferReadBools(buf, b, 1);
}
bool M_BufferReadBools(buf_t *buf, bool *b, size_t count) {
return M_BufferRead(buf, b, count * sizeof(bool));
}
bool M_BufferReadChar(buf_t *buf, char *c) {
return M_BufferReadChars(buf, c, 1);
}
bool M_BufferReadChars(buf_t *buf, char *c, size_t count) {
return M_BufferRead(buf, c, count * sizeof(char));
}
bool M_BufferReadUChar(buf_t *buf, unsigned char *c) {
return M_BufferReadUChars(buf, c, 1);
}
bool M_BufferReadUChars(buf_t *buf, unsigned char *c, size_t count) {
return M_BufferRead(buf, c, count * sizeof(unsigned char));
}
bool M_BufferReadShort(buf_t *buf, short *s) {
return M_BufferReadShorts(buf, s, 1);
}
bool M_BufferReadShorts(buf_t *buf, short *s, size_t count) {
return M_BufferRead(buf, s, count * sizeof(short));
}
bool M_BufferReadUShort(buf_t *buf, unsigned short *s) {
return M_BufferReadUShorts(buf, s, 1);
}
bool M_BufferReadUShorts(buf_t *buf, unsigned short *s, size_t count) {
return M_BufferRead(buf, s, count * sizeof(unsigned short));
}
bool M_BufferReadInt(buf_t *buf, int *i) {
return M_BufferReadInts(buf, i, 1);
}
bool M_BufferReadInts(buf_t *buf, int *i, size_t count) {
return M_BufferRead(buf, i, count * sizeof(int));
}
bool M_BufferReadUInt(buf_t *buf, unsigned int *i) {
return M_BufferReadUInts(buf, i, 1);
}
bool M_BufferReadUInts(buf_t *buf, unsigned int *i, size_t count) {
return M_BufferRead(buf, i, count * sizeof(unsigned int));
}
bool M_BufferReadLong(buf_t *buf, int64_t *l) {
return M_BufferReadLongs(buf, l, 1);
}
bool M_BufferReadLongs(buf_t *buf, int64_t *l, size_t count) {
return M_BufferRead(buf, l, count * sizeof(int64_t));
}
bool M_BufferReadULong(buf_t *buf, uint64_t *l) {
return M_BufferReadULongs(buf, l, 1);
}
bool M_BufferReadULongs(buf_t *buf, uint64_t *l, size_t count) {
return M_BufferRead(buf, l, count * sizeof(uint64_t));
}
#ifndef CMP_NO_FLOAT
bool M_BufferReadFloat(buf_t *buf, float *f) {
return M_BufferReadFloats(buf, f, 1);
}
bool M_BufferReadFloats(buf_t *buf, float *f, size_t count) {
return M_BufferRead(buf, f, count * sizeof(float));
}
bool M_BufferReadDouble(buf_t *buf, double *d) {
return M_BufferReadDoubles(buf, d, 1);
}
bool M_BufferReadDoubles(buf_t *buf, double *d, size_t count) {
return M_BufferRead(buf, d, count * sizeof(double));
}
#endif
bool M_BufferReadString(buf_t *buf, char *s, size_t length) {
return M_BufferRead(buf, s, length);
}
bool M_BufferReadStringDup(const buf_t *buf, char **s) {
char *d = buf->data + buf->cursor;
size_t length = strlen(d);
if (buf->cursor + length > buf->size)
return false;
(*s) = strdup(buf->data + buf->cursor);
return true;
}
bool M_BufferCopyString(buf_t *dst, const buf_t *src) {
char *s = src->data + src->cursor;
size_t length = strlen(s);
if (src->cursor + length >= src->size)
return false;
M_BufferWriteString(dst, s, length);
return true;
}
void M_BufferCompact(buf_t *buf) {
if (buf->size < buf->capacity) {
char *new_buf = calloc(buf->size, sizeof(uint8_t));
if (buf->data == NULL)
error_and_exit("M_BufferCompact: Allocating new buffer data failed");
memcpy(new_buf, buf->data, buf->size);
free(buf->data);
buf->data = new_buf;
buf->capacity = buf->size;
if (buf->cursor > buf->size)
buf->cursor = buf->size;
}
}
void M_BufferTruncate(buf_t *buf, size_t new_size) {
size_t old_size = buf->size;
if (new_size >= buf->size)
errorf_and_exit("M_BufferTruncate: %zu >= %zu.", new_size, buf->size);
memset(buf->data + new_size, 0, old_size - new_size);
buf->size = new_size;
if (buf->cursor >= buf->size)
buf->cursor = buf->size - 1;
}
void M_BufferZero(const buf_t *buf) {
memset(buf->data, 0, buf->capacity);
}
void M_BufferClear(buf_t *buf) {
buf->size = 0;
buf->cursor = 0;
M_BufferZero(buf);
}
void M_BufferFree(buf_t *buf) {
free(buf->data);
memset(buf, 0, sizeof(buf_t));
buf->data = NULL;
}
void M_BufferPrint(const buf_t *buf) {
printf("Buffer capacity, size and cursor: [%zu, %zu, %zu].\n",
buf->capacity,
buf->size,
buf->cursor
);
for (size_t i = 0; i < MIN(64, buf->size); i++) {
printf("%02X ", (unsigned char)buf->data[i]);
if ((i > 0) && (((i + 1) % 25) == 0))
printf("\n");
}
printf("\n");
}
void M_BufferPrintAll(const buf_t *buf) {
printf("Buffer capacity, size and cursor: [%zu, %zu, %zu].\n",
buf->capacity,
buf->size,
buf->cursor
);
for (size_t i = 0; i < buf->size; i++) {
printf("%02X ", (unsigned char)buf->data[i]);
if ((i > 0) && (((i + 1) % 25) == 0))
printf("\n");
}
printf("\n");
}
/* vi: set et ts=2 sw=2: */

View File

@ -0,0 +1,138 @@
/*
The MIT License (MIT)
Copyright (c) 2020 Charles Gunyon
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#ifndef M_BUF_H__
#define M_BUF_H__
typedef struct buf_s {
size_t capacity;
size_t size;
size_t cursor;
char *data;
} buf_t;
buf_t* M_BufferNew(void);
buf_t* M_BufferNewWithCapacity(size_t capacity);
void M_BufferInit(buf_t *buf);
void M_BufferInitWithCapacity(buf_t *buf, size_t capacity);
size_t M_BufferGetCapacity(const buf_t *buf);
size_t M_BufferGetSize(const buf_t *buf);
size_t M_BufferGetCursor(const buf_t *buf);
char* M_BufferGetData(const buf_t *buf);
char* M_BufferGetDataAtCursor(const buf_t *buf);
void M_BufferEnsureCapacity(buf_t *buf, size_t capacity);
void M_BufferEnsureTotalCapacity(buf_t *buf, size_t capacity);
void M_BufferCopy(buf_t *dst, const buf_t *src);
void M_BufferCursorCopy(buf_t *dst, const buf_t *src);
bool M_BufferMove(buf_t *buf, size_t dpos, size_t spos, size_t count);
void M_BufferSetData(buf_t *buf, const void *data, size_t size);
void M_BufferSetString(buf_t *buf, const char *data, size_t length);
bool M_BufferSetFile(buf_t *buf, const char *filename);
bool M_BufferSeek(buf_t *buf, size_t pos);
bool M_BufferSeekBackward(buf_t *buf, size_t count);
bool M_BufferSeekForward(buf_t *buf, size_t count);
uint8_t M_BufferPeek(const buf_t *buf);
void M_BufferWrite(buf_t *buf, const void *data, size_t size);
void M_BufferWriteBool(buf_t *buf, bool b);
void M_BufferWriteBools(buf_t *buf, const bool *bools, size_t count);
void M_BufferWriteChar(buf_t *buf, char c);
void M_BufferWriteChars(buf_t *buf, const char *chars, size_t count);
void M_BufferWriteUChar(buf_t *buf, unsigned char c);
void M_BufferWriteUChars(buf_t *buf, const unsigned char *uchars,
size_t count);
void M_BufferWriteShort(buf_t *buf, short s);
void M_BufferWriteShorts(buf_t *buf, const short *shorts, size_t count);
void M_BufferWriteUShort(buf_t *buf, unsigned short s);
void M_BufferWriteUShorts(buf_t *buf, const unsigned short *ushorts,
size_t count);
void M_BufferWriteInt(buf_t *buf, int i);
void M_BufferWriteInts(buf_t *buf, const int *ints, size_t count);
void M_BufferWriteUInt(buf_t *buf, unsigned int i);
void M_BufferWriteUInts(buf_t *buf, const unsigned int *ints,
size_t count);
void M_BufferWriteLong(buf_t *buf, int64_t l);
void M_BufferWriteLongs(buf_t *buf, const int64_t *longs, size_t count);
void M_BufferWriteULong(buf_t *buf, uint64_t l);
void M_BufferWriteULongs(buf_t *buf, const uint64_t *longs, size_t count);
#ifndef CMP_NO_FLOAT
void M_BufferWriteFloat(buf_t *buf, float f);
void M_BufferWriteFloats(buf_t *buf, const float *floats, size_t count);
void M_BufferWriteDouble(buf_t *buf, double d);
void M_BufferWriteDoubles(buf_t *buf, const double *doubles, size_t count);
#endif
void M_BufferWriteString(buf_t *buf, const char *string, size_t length);
void M_BufferWriteZeros(buf_t *buf, size_t count);
bool M_BufferEqualsString(const buf_t *buf, const char *s);
bool M_BufferEqualsData(const buf_t *buf, const void *d, size_t size);
bool M_BufferRead(buf_t *buf, void *data, size_t size);
bool M_BufferReadBool(buf_t *buf, bool *b);
bool M_BufferReadBools(buf_t *buf, bool *b, size_t count);
bool M_BufferReadChar(buf_t *buf, char *c);
bool M_BufferReadChars(buf_t *buf, char *c, size_t count);
bool M_BufferReadUChar(buf_t *buf, unsigned char *c);
bool M_BufferReadUChars(buf_t *buf, unsigned char *c, size_t count);
bool M_BufferReadShort(buf_t *buf, short *s);
bool M_BufferReadShorts(buf_t *buf, short *shorts, size_t count);
bool M_BufferReadUShort(buf_t *buf, unsigned short *s);
bool M_BufferReadUShorts(buf_t *buf, unsigned short *s, size_t count);
bool M_BufferReadInt(buf_t *buf, int *i);
bool M_BufferReadInts(buf_t *buf, int *i, size_t count);
bool M_BufferReadUInt(buf_t *buf, unsigned int *i);
bool M_BufferReadUInts(buf_t *buf, unsigned int *i, size_t count);
bool M_BufferReadLong(buf_t *buf, int64_t *l);
bool M_BufferReadLongs(buf_t *buf, int64_t *l, size_t count);
bool M_BufferReadULong(buf_t *buf, uint64_t *l);
bool M_BufferReadULongs(buf_t *buf, uint64_t *l, size_t count);
#ifndef CMP_NO_FLOAT
bool M_BufferReadFloat(buf_t *buf, float *f);
bool M_BufferReadFloats(buf_t *buf, float *f, size_t count);
bool M_BufferReadDouble(buf_t *buf, double *d);
bool M_BufferReadDoubles(buf_t *buf, double *d, size_t count);
#endif
bool M_BufferReadString(buf_t *buf, char *s, size_t length);
bool M_BufferReadStringDup(const buf_t *buf, char **s);
bool M_BufferCopyString(buf_t *dst, const buf_t *src);
void M_BufferCompact(buf_t *buf);
void M_BufferTruncate(buf_t *buf, size_t new_size);
void M_BufferZero(const buf_t *buf);
void M_BufferClear(buf_t *buf);
void M_BufferFree(buf_t *buf);
void M_BufferPrint(const buf_t *buf);
void M_BufferPrintAll(const buf_t *buf);
#endif
/* vi: set et ts=2 sw=2: */

Binary file not shown.

View File

@ -0,0 +1,55 @@
/*
The MIT License (MIT)
Copyright (c) 2020 Charles Gunyon
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#include <stdlib.h>
#include "tests.h"
int main(void) {
test_msgpack(NULL);
test_fixedint(NULL);
test_numbers(NULL);
test_nil(NULL);
test_boolean(NULL);
test_bin(NULL);
test_string(NULL);
test_array(NULL);
test_map(NULL);
test_ext(NULL);
test_obj(NULL);
#ifndef CMP_NO_FLOAT
test_float_flip(NULL);
#endif
test_skipping(NULL);
test_deprecated_limited_skipping(NULL);
test_errors(NULL);
test_version(NULL);
test_conversions(NULL);
return EXIT_SUCCESS;
}
/* vi: set et ts=2 sw=2: */

View File

@ -0,0 +1,67 @@
/*
The MIT License (MIT)
Copyright (c) 2020 Charles Gunyon
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#include <setjmp.h>
#include <stdarg.h>
#include <stddef.h>
#include <stdlib.h>
#include <cmocka.h>
#include "tests.h"
int main(void) {
/* Use the old CMocka API because Travis' latest Ubuntu is Trusty */
const UnitTest tests[17] = {
unit_test(test_msgpack),
unit_test(test_fixedint),
unit_test(test_numbers),
unit_test(test_nil),
unit_test(test_boolean),
unit_test(test_bin),
unit_test(test_string),
unit_test(test_array),
unit_test(test_map),
unit_test(test_ext),
unit_test(test_obj),
#ifndef CMP_NO_FLOAT
unit_test(test_float_flip),
#endif
unit_test(test_skipping),
unit_test(test_deprecated_limited_skipping),
unit_test(test_errors),
unit_test(test_version),
unit_test(test_conversions),
};
if (run_tests(tests)) {
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
/* vi: set et ts=2 sw=2: */

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,45 @@
/*
The MIT License (MIT)
Copyright (c) 2020 Charles Gunyon
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
void test_msgpack(void **state);
void test_fixedint(void **state);
void test_numbers(void **state);
void test_conversions(void **state);
void test_nil(void **state);
void test_boolean(void **state);
void test_bin(void **state);
void test_string(void **state);
void test_array(void **state);
void test_map(void **state);
void test_ext(void **state);
void test_obj(void **state);
#ifndef CMP_NO_FLOAT
void test_float_flip(void **state);
#endif
void test_skipping(void **state);
void test_deprecated_limited_skipping(void **state);
void test_errors(void **state);
void test_version(void **state);
/* vi: set et ts=2 sw=2: */

View File

@ -0,0 +1,58 @@
/*
The MIT License (MIT)
Copyright (c) 2020 Charles Gunyon
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#include <inttypes.h>
#include <stdarg.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "utils.h"
void error_and_exit(const char *msg) {
fprintf(stderr, "%s\n", msg);
exit(EXIT_FAILURE);
}
void errorf_and_exit(const char *msg, ...) {
va_list args;
va_start(args, msg);
vfprintf(stderr, msg, args);
va_end(args);
exit(EXIT_FAILURE);
}
char* _strdup(const char *s) {
char *out = calloc(strlen(s) + 1, sizeof(char));
strcpy(out, s);
return out;
}
/* vi: set et ts=2 sw=2: */

View File

@ -0,0 +1,52 @@
/*
The MIT License (MIT)
Copyright (c) 2020 Charles Gunyon
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#ifndef UTILS_H__
#define UTILS_H__
void error_and_exit(const char *msg);
#ifdef __GNUC__
void errorf_and_exit(const char *msg, ...)
__attribute__ ((format (printf, 1, 2)));
#else
void errorf_and_exit(const char *msg, ...);
#endif
char* _strdup(const char *s);
#ifndef strdup
#define strdup _strdup
#endif
#ifndef MAX
#define MAX(a,b) ((a)>(b)?(a):(b))
#endif
#ifndef MIN
#define MIN(a,b) ((a)<(b)?(a):(b))
#endif
#endif