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