@ -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__ */ |