306 lines
8.2 KiB
C
306 lines
8.2 KiB
C
// No copyright
|
|
#pragma once
|
|
|
|
#include <assert.h>
|
|
#include <stdbool.h>
|
|
#include <stdint.h>
|
|
#include <string.h>
|
|
#include <time.h>
|
|
|
|
|
|
// ---- macro utilities
|
|
#define ACG_UTIL_STR(v) #v
|
|
#define ACG_UTIL_STR2(v) ACG_UTIL_STR(v)
|
|
|
|
|
|
// ---- variable points
|
|
#if !defined(ACG_NOW)
|
|
# define ACG_NOW ((uint64_t) ((double) clock()/CLOCKS_PER_SEC*1000000U))
|
|
#endif
|
|
#if !defined(ACG_TID)
|
|
# define ACG_TID (0x700FU) // you should replace ACG_TID to log an actual TID
|
|
#endif
|
|
#if !defined(ACG_LOC_FULL)
|
|
# define ACG_LOC_FULL (__FILE__ ":" ACG_UTIL_STR2(__LINE__))
|
|
#endif
|
|
#if !defined(ACG_LOC_SHORT)
|
|
# define ACG_LOC_SHORT ACG_UTIL_STR2(__LINE__)
|
|
#endif
|
|
#if !defined(ACG_FLUSH)
|
|
# define ACG_FLUSH() (true)
|
|
#endif
|
|
|
|
|
|
// ---- sugar syntax for logging
|
|
#define ACG_BLOB_LITERAL(s, data) ( \
|
|
acg_stream_write_chunk_blob((s), sizeof((data)), (const uint8_t*) (data)) && \
|
|
ACG_FLUSH((s)) \
|
|
)
|
|
#define ACG_BLOB(s, size, data) ( \
|
|
acg_stream_write_chunk_blob((s), (size), (const uint8_t*) (data)) && \
|
|
ACG_FLUSH((s)) \
|
|
)
|
|
#define ACG_BEGIN(s) ( \
|
|
acg_stream_write_chunk_begin((s)) && \
|
|
acg_stream_write_chunk_loc((s), sizeof(ACG_LOC_FULL)-1U, (const uint8_t*) ACG_LOC_FULL) && \
|
|
acg_stream_write_chunk_ctx((s), ACG_NOW, ACG_TID) && \
|
|
ACG_FLUSH((s)) \
|
|
)
|
|
#define ACG_END(s) ( \
|
|
acg_stream_write_chunk_end((s)) && \
|
|
acg_stream_write_chunk_loc((s), sizeof(ACG_LOC_SHORT)-1U,(const uint8_t*) ACG_LOC_SHORT) && \
|
|
acg_stream_write_chunk_ctx((s), ACG_NOW, ACG_TID) && \
|
|
ACG_FLUSH((s)) \
|
|
)
|
|
#define ACG_CHECK(s) ( \
|
|
acg_stream_write_chunk_check((s)) && \
|
|
acg_stream_write_chunk_loc((s), sizeof(ACG_LOC_SHORT)-1U,(const uint8_t*) ACG_LOC_SHORT) && \
|
|
acg_stream_write_chunk_ctx((s), ACG_NOW, ACG_TID) && \
|
|
ACG_FLUSH((s)) \
|
|
)
|
|
|
|
|
|
// ---- constants for binary format
|
|
#define ACG_CHUNK_PAD 0x00U
|
|
#define ACG_CHUNK_BLOB 0x01U
|
|
#define ACG_CHUNK_CTX 0x02U
|
|
#define ACG_CHUNK_LOC 0x03U
|
|
#define ACG_CHUNK_BEGIN 0x04U
|
|
#define ACG_CHUNK_END 0x05U
|
|
#define ACG_CHUNK_CHECK 0x06U
|
|
|
|
#define ACG_UNITBITS_CHUNK_ID 4U
|
|
|
|
|
|
// ---- utility functions
|
|
static inline uint64_t acg_util_fullbits(uint8_t bits) {
|
|
return (((uint64_t) 1U) << bits) - 1U;
|
|
}
|
|
static inline uint64_t acg_util_umin(uint64_t a, uint64_t b) {
|
|
return a > b? b: a;
|
|
}
|
|
|
|
|
|
// ---- stream type definition and its functions
|
|
struct acg_stream {
|
|
uint8_t* buffer;
|
|
uint64_t size_bytes;
|
|
uint64_t cursor_bits;
|
|
void* udata;
|
|
};
|
|
|
|
|
|
#if !defined(ACG_NO_WRITE_FUNCTIONS)
|
|
static inline bool acg_stream_write_pad(struct acg_stream* s) {
|
|
assert(NULL != s);
|
|
|
|
s->cursor_bits = (s->cursor_bits+7U)/8U*8U;
|
|
return true;
|
|
}
|
|
static inline bool acg_stream_write_bool(struct acg_stream* s, bool v) {
|
|
assert(NULL != s);
|
|
|
|
if (s->size_bytes <= s->cursor_bits) {
|
|
return false;
|
|
}
|
|
uint64_t* const c = &s->cursor_bits;
|
|
s->buffer[*c/8U] |= (!!v) << (*c%8U);
|
|
++*c;
|
|
return true;
|
|
}
|
|
static inline bool acg_stream_write_uint(struct acg_stream* s, uint64_t v, uint8_t unit_bits) {
|
|
assert(NULL != s);
|
|
assert(2U <= unit_bits);
|
|
assert(65U >= unit_bits);
|
|
|
|
uint8_t* const buf = s->buffer;
|
|
uint64_t* const c = &s->cursor_bits;
|
|
|
|
const uint8_t unit_data_bits = unit_bits - 1U;
|
|
do {
|
|
if (s->size_bytes*8U - *c < unit_bits) {
|
|
return false;
|
|
}
|
|
|
|
const uint8_t frag_bits = acg_util_umin(unit_data_bits, (8U - *c%8U)%8U);
|
|
if (frag_bits > 0U) {
|
|
buf[*c/8U] |= (v & acg_util_fullbits(frag_bits)) << (*c%8U);
|
|
*c += frag_bits;
|
|
}
|
|
|
|
const uint8_t remain_bits = unit_data_bits - frag_bits;
|
|
if (remain_bits > 0U) {
|
|
assert(*c%8U == 0U);
|
|
|
|
const uint64_t v_part = (v >> frag_bits) & acg_util_fullbits(remain_bits);
|
|
memcpy(&buf[*c/8U], &v_part, (remain_bits + 7U) / 8U);
|
|
*c += remain_bits;
|
|
}
|
|
|
|
v >>= unit_data_bits;;
|
|
acg_stream_write_bool(s, !!v);
|
|
} while (v);
|
|
return true;
|
|
}
|
|
static inline bool acg_stream_write_blob(struct acg_stream* s, uint64_t size_bytes, const uint8_t* buf) {
|
|
assert(NULL != s);
|
|
|
|
if (!acg_stream_write_uint(s, size_bytes, 8U)) {
|
|
return false;
|
|
}
|
|
if (!acg_stream_write_pad(s)) {
|
|
return false;
|
|
}
|
|
|
|
uint64_t* const c = &s->cursor_bits;
|
|
|
|
const uint64_t avail_bytes = s->size_bytes - *c/8U;
|
|
if (avail_bytes < size_bytes) {
|
|
return false;
|
|
}
|
|
memcpy(&s->buffer[*c/8], buf, size_bytes);
|
|
|
|
const uint64_t size_bits = size_bytes * 8U;
|
|
*c += size_bits;
|
|
return true;
|
|
}
|
|
|
|
static inline bool acg_stream_write_chunk_pad(struct acg_stream* s) {
|
|
assert(NULL != s);
|
|
|
|
if (s->cursor_bits%8U == 0U) {
|
|
return true;
|
|
}
|
|
if (s->size_bytes <= s->cursor_bits/8U) {
|
|
return false;
|
|
}
|
|
return
|
|
acg_stream_write_uint(s, ACG_CHUNK_PAD, ACG_UNITBITS_CHUNK_ID) &&
|
|
acg_stream_write_pad(s);
|
|
}
|
|
static inline bool acg_stream_write_chunk_blob(struct acg_stream* s, uint64_t size_bytes, const uint8_t* buf) {
|
|
assert(NULL != s);
|
|
return
|
|
acg_stream_write_uint(s, ACG_CHUNK_BLOB, ACG_UNITBITS_CHUNK_ID) &&
|
|
acg_stream_write_blob(s, size_bytes, buf);
|
|
}
|
|
static inline bool acg_stream_write_chunk_ctx(struct acg_stream* s, uint64_t time, uint64_t tid) {
|
|
assert(NULL != s);
|
|
return
|
|
acg_stream_write_uint(s, ACG_CHUNK_CTX, ACG_UNITBITS_CHUNK_ID) &&
|
|
acg_stream_write_uint(s, time, 32U) &&
|
|
acg_stream_write_uint(s, tid, 16U);
|
|
}
|
|
static inline bool acg_stream_write_chunk_loc(struct acg_stream* s, uint64_t size_bytes, const uint8_t* buf) {
|
|
assert(NULL != s);
|
|
return
|
|
acg_stream_write_uint(s, ACG_CHUNK_LOC, ACG_UNITBITS_CHUNK_ID) &&
|
|
acg_stream_write_blob(s, size_bytes, buf);
|
|
}
|
|
static inline bool acg_stream_write_chunk_begin(struct acg_stream* s) {
|
|
assert(NULL != s);
|
|
return acg_stream_write_uint(s, ACG_CHUNK_BEGIN, ACG_UNITBITS_CHUNK_ID);
|
|
}
|
|
static inline bool acg_stream_write_chunk_end(struct acg_stream* s) {
|
|
assert(NULL != s);
|
|
return acg_stream_write_uint(s, ACG_CHUNK_END, ACG_UNITBITS_CHUNK_ID);
|
|
}
|
|
static inline bool acg_stream_write_chunk_check(struct acg_stream* s) {
|
|
assert(NULL != s);
|
|
return acg_stream_write_uint(s, ACG_CHUNK_CHECK, ACG_UNITBITS_CHUNK_ID);
|
|
}
|
|
#endif // !defined(ACG_NO_WRITE_FUNCTIONS)
|
|
|
|
|
|
#if !defined(ACG_NO_READ_FUNCTIONS)
|
|
static inline bool acg_stream_read_pad(struct acg_stream* s) {
|
|
assert(NULL != s);
|
|
|
|
s->cursor_bits = (s->cursor_bits + 7U)/8U*8U;
|
|
return true;
|
|
}
|
|
static inline bool acg_stream_read_bool(struct acg_stream* s, bool* v) {
|
|
assert(NULL != s);
|
|
assert(NULL != v);
|
|
|
|
if (s->size_bytes*8U <= s->cursor_bits) {
|
|
return false;
|
|
}
|
|
uint64_t* const c = &s->cursor_bits;
|
|
*v = (s->buffer[*c/8U] >> (*c%8U)) & 1U;
|
|
++*c;
|
|
return true;
|
|
}
|
|
static inline bool acg_stream_read_uint(struct acg_stream* s, uint64_t* v, uint8_t unit_bits) {
|
|
assert(NULL != s);
|
|
assert(NULL != v);
|
|
assert(2U <= unit_bits);
|
|
assert(65U >= unit_bits);
|
|
|
|
*v = 0U;
|
|
|
|
const uint8_t unit_data_bits = unit_bits - 1U;
|
|
|
|
const uint8_t* const buf = s->buffer;
|
|
uint64_t* const c = &s->cursor_bits;
|
|
|
|
uint8_t v_offset_bits = 0U;
|
|
bool cont_flag = true;
|
|
|
|
while (cont_flag) {
|
|
if (s->size_bytes*8U < *c+unit_data_bits) {
|
|
return false;
|
|
}
|
|
|
|
const uint8_t frag_bits = acg_util_umin((8U - *c%8U)%8U, unit_data_bits);
|
|
if (frag_bits > 0U) {
|
|
const uint64_t v_part =
|
|
(buf[*c/8U] >> (*c%8U)) & acg_util_fullbits(frag_bits);
|
|
*v |= v_part << v_offset_bits;
|
|
*c += frag_bits;
|
|
v_offset_bits += frag_bits;
|
|
}
|
|
|
|
const uint8_t remain_bits = unit_data_bits - frag_bits;
|
|
if (remain_bits > 0U) {
|
|
assert(*c%8U == 0U);
|
|
|
|
uint64_t v_part = 0U;
|
|
memcpy(&v_part, &buf[*c/8U], (remain_bits+7U)/8U);
|
|
v_part &= acg_util_fullbits(remain_bits);
|
|
|
|
*v |= v_part << v_offset_bits;
|
|
*c += remain_bits;
|
|
v_offset_bits += remain_bits;
|
|
}
|
|
|
|
if (!acg_stream_read_bool(s, &cont_flag)) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
static inline bool acg_stream_read_blob(struct acg_stream* s, uint64_t* size, uint8_t** buf) {
|
|
assert(NULL != s);
|
|
assert(NULL != size);
|
|
assert(NULL != buf);
|
|
|
|
if (!acg_stream_read_uint(s, size, 8U)) {
|
|
return false;
|
|
}
|
|
if (!acg_stream_read_pad(s)) {
|
|
return false;
|
|
}
|
|
|
|
uint64_t* const c = &s->cursor_bits;
|
|
|
|
const uint64_t offset = *c/8U;
|
|
if (s->size_bytes < offset + *size) {
|
|
return false;
|
|
}
|
|
*buf = &s->buffer[offset];
|
|
*c += *size *8U;
|
|
return true;
|
|
}
|
|
#endif // !defined(ACG_NO_READ_FUNCTIONS)
|