| @ -1,4 +0,0 @@ | |||
| [submodule "chck"] | |||
| path = lib/chck | |||
| url = git://github.com/Cloudef/chck.git | |||
| branch = master | |||
| @ -1,3 +0,0 @@ | |||
| set(CHCK_BUILD_STATIC ON CACHE STRING "Build chck statically if not found systemwide" FORCE) | |||
| set(CHCK_BUILD_TESTS OFF CACHE STRING "Do not build chck tests" FORCE) | |||
| add_subproject(chck Chck) | |||
| @ -0,0 +1,568 @@ | |||
| #include "buffer.h" | |||
| #include <chck/overflow/overflow.h> | |||
| #include <stdlib.h> | |||
| #include <unistd.h> | |||
| #include <stdarg.h> | |||
| #if HAS_ZLIB | |||
| # include <zlib.h> | |||
| #elif !defined(HAS_ZLIB) | |||
| # define HAS_ZLIB 0 | |||
| #endif | |||
| struct chck_variant { | |||
| union { | |||
| uint64_t u64; | |||
| uint32_t u32; | |||
| uint16_t u16; | |||
| uint8_t u8; | |||
| uint8_t b[sizeof(uint64_t)]; | |||
| }; | |||
| enum chck_bits bits; | |||
| }; | |||
| static inline bool | |||
| valid_bits(enum chck_bits bits) | |||
| { | |||
| return (bits == CHCK_BUFFER_B8 || | |||
| bits == CHCK_BUFFER_B16 || | |||
| bits == CHCK_BUFFER_B32 || | |||
| bits == CHCK_BUFFER_B64); | |||
| } | |||
| static inline enum chck_bits | |||
| smallest_bits_for_value(uintmax_t v) | |||
| { | |||
| static const struct { | |||
| uintmax_t off; | |||
| enum chck_bits bits; | |||
| } map[3] = { | |||
| { ~(uint32_t)0, CHCK_BUFFER_B64 }, | |||
| { ~(uint16_t)0, CHCK_BUFFER_B32 }, | |||
| { ~(uint8_t)0, CHCK_BUFFER_B16 }, | |||
| }; | |||
| for (size_t i = 0; i < sizeof(map) / sizeof(map[0]); ++i) { | |||
| if (v <= map[i].off) | |||
| continue; | |||
| return map[i].bits; | |||
| } | |||
| return CHCK_BUFFER_B8; | |||
| } | |||
| static inline uintmax_t | |||
| variant_get_value(struct chck_variant v) | |||
| { | |||
| switch (v.bits) { | |||
| case CHCK_BUFFER_B8: | |||
| return v.u8; | |||
| case CHCK_BUFFER_B16: | |||
| return v.u16; | |||
| case CHCK_BUFFER_B32: | |||
| return v.u32; | |||
| case CHCK_BUFFER_B64: | |||
| return v.u64; | |||
| } | |||
| assert(0 && "should not happen"); | |||
| return 0; | |||
| } | |||
| void | |||
| chck_buffer_flush(struct chck_buffer *buf) | |||
| { | |||
| assert(buf); | |||
| if (buf->copied) | |||
| free(buf->buffer); | |||
| buf->copied = false; | |||
| buf->curpos = buf->buffer = NULL; | |||
| } | |||
| void | |||
| chck_buffer_release(struct chck_buffer *buf) | |||
| { | |||
| if (!buf) | |||
| return; | |||
| chck_buffer_flush(buf); | |||
| *buf = (struct chck_buffer){0}; | |||
| } | |||
| bool | |||
| chck_buffer_from_pointer(struct chck_buffer *buf, void *ptr, size_t size, enum chck_endianess endianess) | |||
| { | |||
| assert(buf); | |||
| *buf = (struct chck_buffer){ .step = 32 }; | |||
| chck_buffer_set_pointer(buf, ptr, size, endianess); | |||
| return true; | |||
| } | |||
| bool | |||
| chck_buffer(struct chck_buffer *buf, size_t size, enum chck_endianess endianess) | |||
| { | |||
| assert(buf); | |||
| void *data = NULL; | |||
| if (size > 0 && !(data = malloc(size))) | |||
| return false; | |||
| if (unlikely(!chck_buffer_from_pointer(buf, data, size, endianess))) | |||
| goto fail; | |||
| buf->copied = true; | |||
| return true; | |||
| fail: | |||
| free(data); | |||
| return false; | |||
| } | |||
| void | |||
| chck_buffer_set_pointer(struct chck_buffer *buf, void *ptr, size_t size, enum chck_endianess endianess) | |||
| { | |||
| assert(buf); | |||
| if (buf->copied) { | |||
| free(buf->buffer); | |||
| buf->buffer = NULL; | |||
| } | |||
| if (endianess == CHCK_ENDIANESS_NATIVE) { | |||
| buf->endianess = chck_endianess(); | |||
| } else { | |||
| buf->endianess = endianess; | |||
| } | |||
| buf->size = size; | |||
| buf->buffer = buf->curpos = ptr; | |||
| buf->copied = false; | |||
| } | |||
| bool | |||
| chck_buffer_resize(struct chck_buffer *buf, size_t size) | |||
| { | |||
| assert(buf); | |||
| if (unlikely(size == buf->size)) | |||
| return true; | |||
| if (unlikely(size == 0)) { | |||
| chck_buffer_flush(buf); | |||
| return true; | |||
| } | |||
| uint8_t *tmp; | |||
| if (!(tmp = realloc((buf->copied ? buf->buffer : NULL), size))) | |||
| return false; | |||
| /* set new buffer position */ | |||
| if (buf->curpos - buf->buffer > (ptrdiff_t)size) { | |||
| buf->curpos = tmp + (buf->size - size); | |||
| } else { | |||
| buf->curpos = tmp + (buf->curpos - buf->buffer); | |||
| } | |||
| buf->size = size; | |||
| buf->buffer = tmp; | |||
| buf->copied = true; | |||
| return true; | |||
| } | |||
| ptrdiff_t | |||
| chck_buffer_seek(struct chck_buffer *buf, long offset, int whence) | |||
| { | |||
| assert(buf); | |||
| assert(whence == SEEK_SET || whence == SEEK_END || whence == SEEK_CUR); | |||
| switch (whence) { | |||
| case SEEK_SET: | |||
| if (buf->buffer + offset > buf->buffer + buf->size) { | |||
| buf->curpos = buf->buffer + buf->size; | |||
| } else if (offset >= 0) { | |||
| buf->curpos = buf->buffer + offset; | |||
| } | |||
| break; | |||
| case SEEK_CUR: | |||
| if (buf->curpos + offset > buf->buffer + buf->size) { | |||
| buf->curpos = buf->curpos + buf->size; | |||
| } else if (buf->curpos + offset < buf->buffer) { | |||
| buf->curpos = buf->buffer; | |||
| } else { | |||
| buf->curpos = buf->curpos + offset; | |||
| } | |||
| break; | |||
| case SEEK_END: | |||
| buf->curpos = buf->buffer + buf->size; | |||
| break; | |||
| default:break; | |||
| } | |||
| return buf->curpos - buf->buffer; | |||
| } | |||
| static bool | |||
| bounds_check(struct chck_buffer *buf, size_t size, size_t memb) | |||
| { | |||
| size_t nsz; | |||
| if (unlikely(chck_mul_ofsz(size, memb, &nsz))) | |||
| return false; | |||
| const size_t sz = buf->size - (buf->curpos - buf->buffer); | |||
| if (nsz > sz) { | |||
| /* buf->size + size * memb + buf->step */ | |||
| if (unlikely(chck_add_ofsz(buf->size, nsz, &nsz)) || unlikely(chck_add_ofsz(buf->step, nsz, &nsz))) | |||
| return false; | |||
| if (!chck_buffer_resize(buf, nsz)) | |||
| return false; | |||
| } | |||
| return true; | |||
| } | |||
| size_t | |||
| chck_buffer_fill(const void *src, size_t size, size_t memb, struct chck_buffer *buf) | |||
| { | |||
| assert(src && buf); | |||
| if (!bounds_check(buf, size, memb) || !buf->curpos || !src) | |||
| return 0; | |||
| memcpy(buf->curpos, src, size * memb); | |||
| return memb; | |||
| } | |||
| size_t | |||
| chck_buffer_fill_from_file(FILE *src, size_t size, size_t memb, struct chck_buffer *buf) | |||
| { | |||
| assert(src && buf); | |||
| if (!bounds_check(buf, size, memb) || !buf->curpos || !src) | |||
| return 0; | |||
| return fread(buf->curpos, size, memb, src); | |||
| } | |||
| size_t | |||
| chck_buffer_fill_from_fd(int fd, size_t size, size_t memb, struct chck_buffer *buf) | |||
| { | |||
| assert(fd && buf); | |||
| if (!bounds_check(buf, size, memb) || !buf->curpos) | |||
| return 0; | |||
| const ssize_t ret = read(fd, buf->curpos, size * memb); | |||
| if (unlikely(ret == -1 || ret == 0)) | |||
| return 0; | |||
| return (size_t)ret / size; | |||
| } | |||
| size_t | |||
| chck_buffer_read(void *dst, size_t size, size_t memb, struct chck_buffer *buf) | |||
| { | |||
| assert(dst && buf); | |||
| size_t nsz; | |||
| if (unlikely(chck_mul_ofsz(size, memb, &nsz))) | |||
| return 0; | |||
| if (unlikely(nsz > buf->size - (buf->curpos - buf->buffer))) { | |||
| assert(size != 0); // should never happen | |||
| // read as much as we can | |||
| memb = (buf->size - (buf->curpos - buf->buffer)) / size; | |||
| } | |||
| memcpy(dst, buf->curpos, size * memb); | |||
| buf->curpos += size * memb; | |||
| return memb; | |||
| } | |||
| bool | |||
| chck_buffer_read_int(void *i, enum chck_bits bits, struct chck_buffer *buf) | |||
| { | |||
| assert(i && buf); | |||
| if (!valid_bits(bits)) | |||
| return false; | |||
| if (unlikely(chck_buffer_read(i, bits, 1, buf) != 1)) | |||
| return false; | |||
| if (!chck_buffer_native_endianess(buf)) | |||
| chck_bswap_single(i, bits); | |||
| return true; | |||
| } | |||
| bool | |||
| chck_buffer_read_string_of_type(char **str, size_t *out_len, enum chck_bits bits, struct chck_buffer *buf) | |||
| { | |||
| assert(buf && str); | |||
| *str = NULL; | |||
| if (out_len) | |||
| *out_len = 0; | |||
| struct chck_variant v = { .bits = bits }; | |||
| if (unlikely(!chck_buffer_read_int(v.b, bits, buf))) | |||
| return false; | |||
| const size_t len = variant_get_value(v); | |||
| if (out_len) | |||
| *out_len = len; | |||
| if (len <= 0) | |||
| return true; | |||
| if (!(*str = chck_calloc_add_of(len, 1))) | |||
| return false; | |||
| if (unlikely(chck_buffer_read(*str, 1, len, buf) != len)) { | |||
| free(*str); | |||
| return false; | |||
| } | |||
| return true; | |||
| } | |||
| bool | |||
| chck_buffer_read_string(char **str, size_t *len, struct chck_buffer *buf) | |||
| { | |||
| assert(str && buf); | |||
| *str = NULL; | |||
| if (len) | |||
| *len = 0; | |||
| uint8_t bits; | |||
| if (unlikely(!chck_buffer_read_int(&bits, sizeof(bits), buf))) | |||
| return false; | |||
| return likely(chck_buffer_read_string_of_type(str, len, bits, buf)); | |||
| } | |||
| size_t | |||
| chck_buffer_write(const void *src, size_t size, size_t memb, struct chck_buffer *buf) | |||
| { | |||
| memb = chck_buffer_fill(src, size, memb, buf); | |||
| buf->curpos += size * memb; | |||
| return memb; | |||
| } | |||
| size_t | |||
| chck_buffer_write_from_file(FILE *src, size_t size, size_t memb, struct chck_buffer *buf) | |||
| { | |||
| memb = chck_buffer_fill_from_file(src, size, memb, buf); | |||
| buf->curpos += size * memb; | |||
| return memb; | |||
| } | |||
| size_t | |||
| chck_buffer_write_from_fd(int fd, size_t size, size_t memb, struct chck_buffer *buf) | |||
| { | |||
| memb = chck_buffer_fill_from_fd(fd, size, memb, buf); | |||
| buf->curpos += size * memb; | |||
| return memb; | |||
| } | |||
| bool | |||
| chck_buffer_write_int(const void *i, enum chck_bits bits, struct chck_buffer *buf) | |||
| { | |||
| assert(buf); | |||
| if (!valid_bits(bits)) | |||
| return false; | |||
| bool ret; | |||
| if (!chck_buffer_native_endianess(buf)) { | |||
| uint8_t b[sizeof(uint64_t)]; | |||
| memcpy(b, i, bits); | |||
| chck_bswap_single(b, bits); | |||
| ret = (chck_buffer_write(b, bits, 1, buf) == 1); | |||
| } else { | |||
| ret = (chck_buffer_write(i, bits, 1, buf) == 1); | |||
| } | |||
| return ret; | |||
| } | |||
| bool | |||
| chck_buffer_write_string_of_type(const char *str, size_t len, enum chck_bits bits, struct chck_buffer *buf) | |||
| { | |||
| assert(buf); | |||
| bool ret = false; | |||
| switch (bits) { | |||
| case CHCK_BUFFER_B8: | |||
| ret = chck_buffer_write_int((uint8_t[]){len}, bits, buf); | |||
| break; | |||
| case CHCK_BUFFER_B16: | |||
| ret = chck_buffer_write_int((uint16_t[]){len}, bits, buf); | |||
| break; | |||
| case CHCK_BUFFER_B32: | |||
| ret = chck_buffer_write_int((uint32_t[]){len}, bits, buf); | |||
| break; | |||
| case CHCK_BUFFER_B64: | |||
| ret = chck_buffer_write_int((uint64_t[]){len}, bits, buf); | |||
| break; | |||
| } | |||
| if (unlikely(!ret)) | |||
| return false; | |||
| return likely(chck_buffer_write(str, 1, len, buf) == len); | |||
| } | |||
| bool | |||
| chck_buffer_write_string(const char *str, size_t len, struct chck_buffer *buf) | |||
| { | |||
| assert(buf); | |||
| const uint8_t bits = smallest_bits_for_value(len); | |||
| if (unlikely(!chck_buffer_write_int(&bits, sizeof(bits), buf))) | |||
| return false; | |||
| return likely(chck_buffer_write_string_of_type(str, len, bits, buf)); | |||
| } | |||
| #pragma GCC diagnostic push | |||
| #pragma GCC diagnostic ignored "-Wformat-nonliteral" | |||
| size_t | |||
| chck_buffer_write_format(struct chck_buffer *buf, const char *fmt, ...) | |||
| { | |||
| va_list argp; | |||
| va_start(argp, fmt); | |||
| const size_t wrote = chck_buffer_write_varg(buf, fmt, argp); | |||
| va_end(argp); | |||
| return wrote; | |||
| } | |||
| size_t | |||
| chck_buffer_write_varg(struct chck_buffer *buf, const char *fmt, va_list args) | |||
| { | |||
| va_list cpy; | |||
| va_copy(cpy, args); | |||
| char *str = NULL; | |||
| const size_t len = vsnprintf(NULL, 0, fmt, args); | |||
| if (len > 0 && !(str = chck_malloc_add_of(len, 1))) { | |||
| va_end(cpy); | |||
| return false; | |||
| } | |||
| vsnprintf(str, len + 1, fmt, cpy); | |||
| va_end(cpy); | |||
| const size_t wrote = chck_buffer_write(str, 1, len, buf); | |||
| free(str); | |||
| return wrote; | |||
| } | |||
| #pragma GCC diagnostic pop | |||
| bool | |||
| chck_buffer_has_zlib(void) | |||
| { | |||
| return HAS_ZLIB; | |||
| } | |||
| #pragma GCC diagnostic ignored "-Wsuggest-attribute=const" | |||
| bool | |||
| chck_buffer_compress_zlib(struct chck_buffer *buf) | |||
| { | |||
| #if HAS_ZLIB | |||
| uLongf dsize, bsize; | |||
| dsize = bsize = compressBound(buf->size); | |||
| void *compressed; | |||
| if (!(compressed = malloc(dsize))) | |||
| return false; | |||
| int ret; | |||
| while ((ret = compress(compressed, &dsize, buf->buffer, buf->size)) == Z_BUF_ERROR) { | |||
| void *tmp; | |||
| if (!(tmp = chck_realloc_mul_of(compressed, bsize, 2))) | |||
| goto fail; | |||
| compressed = tmp; | |||
| dsize = (bsize *= 2); | |||
| } | |||
| if (unlikely(ret != Z_OK)) | |||
| goto fail; | |||
| chck_buffer_set_pointer(buf, compressed, bsize, buf->endianess); | |||
| buf->copied = true; | |||
| if (buf->size > dsize) | |||
| chck_buffer_resize(buf, dsize); | |||
| return true; | |||
| fail: | |||
| free(compressed); | |||
| return false; | |||
| #else | |||
| (void)buf; | |||
| return false; | |||
| #endif | |||
| } | |||
| bool | |||
| chck_buffer_decompress_zlib(struct chck_buffer *buf) | |||
| { | |||
| #if HAS_ZLIB | |||
| uLongf dsize, bsize; | |||
| { | |||
| size_t sz; | |||
| if (unlikely(chck_mul_ofsz(buf->size, 2, &sz)) || (uLongf)sz < sz) | |||
| return false; | |||
| dsize = bsize = sz; | |||
| } | |||
| if (!dsize) | |||
| return false; | |||
| void *decompressed; | |||
| if (!(decompressed = malloc(dsize))) | |||
| return false; | |||
| int ret; | |||
| while ((ret = uncompress(decompressed, &dsize, buf->buffer, buf->size)) == Z_BUF_ERROR) { | |||
| void *tmp; | |||
| if (!(tmp = chck_realloc_mul_of(decompressed, bsize, 2))) | |||
| goto fail; | |||
| decompressed = tmp; | |||
| dsize = (bsize *= 2); | |||
| } | |||
| if (unlikely(ret != Z_OK)) | |||
| goto fail; | |||
| chck_buffer_set_pointer(buf, decompressed, bsize, buf->endianess); | |||
| buf->copied = true; | |||
| if (bsize > dsize) | |||
| chck_buffer_resize(buf, dsize); | |||
| return true; | |||
| fail: | |||
| free(decompressed); | |||
| return false; | |||
| #else | |||
| (void)buf; | |||
| return false; | |||
| #endif | |||
| } | |||
| @ -0,0 +1,75 @@ | |||
| #ifndef __chck_buffer__ | |||
| #define __chck_buffer__ | |||
| #include <stddef.h> | |||
| #include <stdbool.h> | |||
| #include <stdio.h> | |||
| #include <stdarg.h> | |||
| #include "endianess.h" | |||
| enum chck_bits { | |||
| CHCK_BUFFER_B8 = sizeof(int8_t), | |||
| CHCK_BUFFER_B16 = sizeof(int16_t), | |||
| CHCK_BUFFER_B32 = sizeof(int32_t), | |||
| CHCK_BUFFER_B64 = sizeof(int64_t), | |||
| }; | |||
| struct chck_buffer { | |||
| // pointer to current buffer and the current position | |||
| uint8_t *buffer, *curpos; | |||
| // size of the buffer | |||
| size_t size; | |||
| // growth step for the buffer incase writing to full buffer | |||
| size_t step; | |||
| // endianess true == big, false == little | |||
| bool endianess; | |||
| // copied == true, means that buffer is owned by this struct and will be freed on chck_buffer_release | |||
| bool copied; | |||
| }; | |||
| static inline bool | |||
| chck_buffer_native_endianess(const struct chck_buffer *buf) | |||
| { | |||
| return (chck_endianess() == buf->endianess); | |||
| } | |||
| void chck_buffer_release(struct chck_buffer *buf); | |||
| void chck_buffer_flush(struct chck_buffer *buf); | |||
| bool chck_buffer_from_pointer(struct chck_buffer *buf, void *ptr, size_t size, enum chck_endianess endianess); | |||
| bool chck_buffer(struct chck_buffer *buf, size_t size, enum chck_endianess endianess); | |||
| void chck_buffer_set_pointer(struct chck_buffer *buf, void *ptr, size_t size, enum chck_endianess endianess); | |||
| size_t chck_buffer_fill(const void *src, size_t size, size_t memb, struct chck_buffer *buf); | |||
| size_t chck_buffer_fill_from_file(FILE *src, size_t size, size_t memb, struct chck_buffer *buf); | |||
| size_t chck_buffer_fill_from_fd(int fd, size_t size, size_t memb, struct chck_buffer *buf); | |||
| size_t chck_buffer_write(const void *src, size_t size, size_t nmemb, struct chck_buffer *buf); | |||
| size_t chck_buffer_write_from_file(FILE *src, size_t size, size_t nmemb, struct chck_buffer *buf); | |||
| size_t chck_buffer_write_from_fd(int fd, size_t size, size_t nmemb, struct chck_buffer *buf); | |||
| size_t chck_buffer_read(void *dst, size_t size, size_t memb, struct chck_buffer *buf); | |||
| bool chck_buffer_read_int(void *i, enum chck_bits bits, struct chck_buffer *buf); | |||
| bool chck_buffer_read_string(char **str, size_t *len, struct chck_buffer *buf); | |||
| bool chck_buffer_read_string_of_type(char **str, size_t *len, enum chck_bits bits, struct chck_buffer *buf); | |||
| bool chck_buffer_write_int(const void *i, enum chck_bits bits, struct chck_buffer *buf); | |||
| bool chck_buffer_write_string(const char *str, size_t len, struct chck_buffer *buf); | |||
| bool chck_buffer_write_string_of_type(const char *str, size_t len, enum chck_bits bits, struct chck_buffer *buf); | |||
| CHCK_FORMAT(printf, 2, 3) size_t chck_buffer_write_format(struct chck_buffer *buf, const char *fmt, ...); | |||
| size_t chck_buffer_write_varg(struct chck_buffer *buf, const char *fmt, va_list args); | |||
| ptrdiff_t chck_buffer_seek(struct chck_buffer *buf, long offset, int whence); | |||
| bool chck_buffer_resize(struct chck_buffer *buf, size_t size); | |||
| /* -DHAS_ZLIB=1 -lz */ | |||
| bool chck_buffer_has_zlib(void); | |||
| bool chck_buffer_compress_zlib(struct chck_buffer *buf); | |||
| bool chck_buffer_decompress_zlib(struct chck_buffer *buf); | |||
| #endif /* __chck_buffer__ */ | |||
| @ -0,0 +1,133 @@ | |||
| #ifndef __chck_endianess__ | |||
| #define __chck_endianess__ | |||
| #include <chck/macros.h> | |||
| #include <stdint.h> | |||
| #include <assert.h> | |||
| #include <string.h> | |||
| enum chck_endianess { | |||
| CHCK_ENDIANESS_LITTLE = 0, | |||
| CHCK_ENDIANESS_BIG = 1, | |||
| CHCK_ENDIANESS_NATIVE = 2, | |||
| }; | |||
| #if defined(__clang__) || (__GNUC__ >= 4 && __GNUC_MINOR__ >= 3 && !defined(__MINGW32__) && !defined(__MINGW64__)) | |||
| # define bswap16 __builtin_bswap16 | |||
| # define bswap32 __builtin_bswap32 | |||
| # define bswap64 __builtin_bswap64 | |||
| # define HAS_BYTESWAP 1 | |||
| #elif defined(__GLIBC__) | |||
| # include <byteswap.h> | |||
| # define bswap16 __bswap_16 | |||
| # define bswap32 __bswap_32 | |||
| # define bswap64 __bswap_64 | |||
| # define HAS_BYTESWAP 1 | |||
| #elif defined(__NetBSD__) | |||
| # include <sys/types.h> | |||
| # include <machine/bswap.h> /* already named bswap16/32/64 */ | |||
| # define HAS_BYTESWAP 1 | |||
| #elif defined(_MSC_VER) | |||
| # define bswap16 _byteswap_ushort | |||
| # define bswap32 _byteswap_ulong | |||
| # define bswap64 _byteswap_uint64 | |||
| # define HAS_BYTESWAP 1 | |||
| #else | |||
| # define HAS_BYTESWAP 0 | |||
| #endif | |||
| #if (defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) || \ | |||
| (defined(__BYTE_ORDER) && __BYTE_ORDER == __BIG_ENDIAN) || \ | |||
| defined(__BIG_ENDIAN__) || \ | |||
| defined(__ARMEB__) || \ | |||
| defined(__THUMBEB__) || \ | |||
| defined(__AARCH64EB__) || \ | |||
| defined(_MIBSEB) || defined(__MIBSEB) || defined(__MIBSEB__) | |||
| // compiletime big endian | |||
| # define chck_endianess() CHCK_ENDIANESS_BIG | |||
| #elif (defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) || \ | |||
| (defined(__BYTE_ORDER) && __BYTE_ORDER == __LITTLE_ENDIAN) || \ | |||
| defined(__LITTLE_ENDIAN__) || \ | |||
| defined(__ARMEL__) || \ | |||
| defined(__THUMBEL__) || \ | |||
| defined(__AARCH64EL__) || \ | |||
| defined(_MIPSEL) || defined(__MIPSEL) || defined(__MIPSEL__) | |||
| // compiletime little endian | |||
| # define chck_endianess() CHCK_ENDIANESS_LITTLE | |||
| #else | |||
| // runtime endianess check | |||
| static inline enum | |||
| chck_endianess chck_endianess(void) | |||
| { | |||
| union { | |||
| uint32_t i; | |||
| char c[4]; | |||
| } bint = { 0x01020304 }; | |||
| return (bint.c[0] == 1 ? CHCK_ENDIANESS_BIG : CHCK_ENDIANESS_LITTLE); | |||
| }; | |||
| #endif | |||
| static inline void | |||
| chck_bswap_generic(void *p, size_t size) | |||
| { | |||
| if (size <= sizeof(uint8_t)) | |||
| return; | |||
| assert(size <= sizeof(intmax_t)); | |||
| uint8_t b[sizeof(intmax_t)]; | |||
| memcpy(b, p, size); | |||
| for (size_t s = 0; s < size; ++s) | |||
| memset((uint8_t*)p + s, b[size - s - 1], 1); | |||
| } | |||
| static inline void | |||
| chck_bswap_single(void *p, size_t size) | |||
| { | |||
| #if HAS_BYTESWAP | |||
| assert(p); | |||
| switch (size) { | |||
| case sizeof(uint32_t): | |||
| *((uint32_t*)p) = bswap32(*((uint32_t*)p)); | |||
| break; | |||
| case sizeof(uint16_t): | |||
| *((uint16_t*)p) = bswap16(*((uint16_t*)p)); | |||
| break; | |||
| case sizeof(uint64_t): | |||
| *((uint64_t*)p) = bswap64(*((uint64_t*)p)); | |||
| break; | |||
| default: | |||
| chck_bswap_generic(p, size); | |||
| break; | |||
| } | |||
| #else | |||
| chck_bswap_generic(p, size); | |||
| #endif | |||
| } | |||
| static inline void | |||
| chck_bswap(void *v, size_t size, size_t memb) | |||
| { | |||
| assert(v); | |||
| for (uint8_t *p = v; size > sizeof(uint8_t) && p < (uint8_t*)v + (memb * size); p += size) | |||
| chck_bswap_single(p, size); | |||
| } | |||
| /** define chck_bswap{16,32,64} for use **/ | |||
| #if HAS_BYTESWAP | |||
| # define generic_swap(T, n) \ | |||
| static inline T chck_bswap##n(T v) { return bswap##n(v); } | |||
| #else | |||
| # define generic_swap(T, n) \ | |||
| static inline T chck_bswap##n(T v) { chck_bswap_generic(&v, sizeof(v)); return v; } | |||
| #endif | |||
| generic_swap(uint16_t, 16) | |||
| generic_swap(uint32_t, 32) | |||
| generic_swap(uint64_t, 64) | |||
| #undef generic_swap | |||
| #endif /* __chck_endianess__ */ | |||
| @ -0,0 +1,159 @@ | |||
| #ifndef __chck_overflow_h__ | |||
| #define __chck_overflow_h__ | |||
| #include <chck/macros.h> | |||
| #include <stddef.h> | |||
| #include <stdint.h> | |||
| #include <stdbool.h> | |||
| #include <stdlib.h> | |||
| #include <assert.h> | |||
| /** clang feature detection. */ | |||
| #ifndef __has_builtin | |||
| # define __has_builtin(x) 0 | |||
| #endif | |||
| #if __GNUC__ >= 5 || __has_builtin(__builtin_add_overflow) | |||
| /** assume clang and gcc (>=5) to only have builtins for now. */ | |||
| # define add_of(a, b, r) __builtin_add_overflow(a, b, r) | |||
| # define sub_of(a, b, r) __builtin_sub_overflow(a, b, r) | |||
| # define mul_of(a, b, r) __builtin_mul_overflow(a, b, r) | |||
| # define of_attr | |||
| #else | |||
| /** else use these generics, note behaviour of these is not strictly defined. */ | |||
| # define add_of(a, b, r) ((*(r) = ((a) + (b))) < (a)) | |||
| # define sub_of(a, b, r) ((*(r) = ((a) - (b))) > (a)) | |||
| # define mul_of(a, b, r) (((*(r) = ((a) * (b))) || *(r) == 0) && ((a) != 0 && (b) > *(r) / (a))) | |||
| # if __clang__ | |||
| # define of_attr __attribute__((optnone)) // Do not optimize above checks, in most systems this will work, but not defined. | |||
| # warning "Using non compiler builtins for overflow checks, this will be undefined for signed integers" | |||
| # elif __GNUC__ | |||
| # define of_attr __attribute__((optimize("wrapv"))) // in older GCC we can make this behavior defined | |||
| # else | |||
| # warning "Using non compiler builtins for overflow checks, this will be undefined for signed integers" | |||
| # endif | |||
| #endif | |||
| /** declare overflow functions */ | |||
| // T = type name, n = function suffix, s = is type signed? | |||
| #define decl_generics_for_type(T, n, s) \ | |||
| of_attr static inline bool chck_add_of##n(T a, T b, T *r) { assert((!s || b >= 0) && r); return add_of(a, b, r); } \ | |||
| of_attr static inline bool chck_sub_of##n(T a, T b, T *r) { assert((!s || b >= 0) && r); return sub_of(a, b, r); } \ | |||
| of_attr static inline bool chck_mul_of##n(T a, T b, T *r) { assert(r); return mul_of(a, b, r); } | |||
| // UT = unsigned type, un = unsigned function suffix | |||
| // T = signed type, n = signed function suffix | |||
| #define decl_generics(UT, un, T, n) \ | |||
| decl_generics_for_type(UT, un, false) \ | |||
| decl_generics_for_type(T, n, true) | |||
| #pragma GCC diagnostic push | |||
| #pragma GCC diagnostic ignored "-Wtype-limits" | |||
| decl_generics_for_type(size_t, sz, false) | |||
| decl_generics(uint64_t, u64, int64_t, 64) | |||
| decl_generics(uint32_t, u32, int32_t, 32) | |||
| decl_generics(uint16_t, u16, int16_t, 16) | |||
| decl_generics(uint8_t, u8, int8_t, 8) | |||
| #pragma GCC diagnostic pop | |||
| #undef decl_generics | |||
| #undef decl_generics_for_type | |||
| #undef add_of | |||
| #undef sub_of | |||
| #undef mul_of | |||
| #undef of_attr | |||
| CHCK_MALLOC static inline void* | |||
| chck_malloc_add_of(size_t size, size_t add) | |||
| { | |||
| size_t r; | |||
| if (unlikely(chck_add_ofsz(size, add, &r)) || !r) | |||
| return NULL; | |||
| return malloc(r); | |||
| } | |||
| CHCK_MALLOC static inline void* | |||
| chck_malloc_sub_of(size_t size, size_t sub) | |||
| { | |||
| size_t r; | |||
| if (unlikely(chck_sub_ofsz(size, sub, &r)) || !r) | |||
| return NULL; | |||
| return malloc(r); | |||
| } | |||
| CHCK_MALLOC static inline void* | |||
| chck_malloc_mul_of(size_t size, size_t mul) | |||
| { | |||
| size_t r; | |||
| if (unlikely(chck_mul_ofsz(size, mul, &r)) || !r) | |||
| return NULL; | |||
| return malloc(r); | |||
| } | |||
| CHCK_MALLOC static inline void* | |||
| chck_calloc_of(size_t nmemb, size_t size) | |||
| { | |||
| size_t r; | |||
| if (unlikely(chck_mul_ofsz(nmemb, size, &r)) || !r) | |||
| return NULL; | |||
| return calloc(nmemb, size); | |||
| } | |||
| CHCK_MALLOC static inline void* | |||
| chck_calloc_add_of(size_t size, size_t add) | |||
| { | |||
| size_t r; | |||
| if (unlikely(chck_add_ofsz(size, add, &r)) || !r) | |||
| return NULL; | |||
| return calloc(1, r); | |||
| } | |||
| CHCK_MALLOC static inline void* | |||
| chck_calloc_sub_of(size_t size, size_t sub) | |||
| { | |||
| size_t r; | |||
| if (unlikely(chck_sub_ofsz(size, sub, &r)) || !r) | |||
| return NULL; | |||
| return calloc(1, r); | |||
| } | |||
| static inline void* | |||
| chck_realloc_add_of(void *ptr, size_t size, size_t add) | |||
| { | |||
| size_t r; | |||
| if (unlikely(chck_add_ofsz(size, add, &r)) || !r) | |||
| return NULL; | |||
| return realloc(ptr, r); | |||
| } | |||
| static inline void* | |||
| chck_realloc_sub_of(void *ptr, size_t size, size_t sub) | |||
| { | |||
| size_t r; | |||
| if (unlikely(chck_sub_ofsz(size, sub, &r)) || !r) | |||
| return NULL; | |||
| return realloc(ptr, r); | |||
| } | |||
| static inline void* | |||
| chck_realloc_mul_of(void *ptr, size_t size, size_t mul) | |||
| { | |||
| size_t r; | |||
| if (unlikely(chck_mul_ofsz(size, mul, &r)) || !r) | |||
| return NULL; | |||
| return realloc(ptr, r); | |||
| } | |||
| #endif /* __chck_overflow_h__ */ | |||