Files
cell/source/blob.h

431 lines
13 KiB
C

#ifndef BLOB_H
#define BLOB_H
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
// -----------------------------------------------------------------------------
// A simple blob structure that can be in two states:
// - antestone (mutable): writing is allowed
// - stone (immutable): reading is allowed
//
// The blob is stored as an array of bits in memory, but for simplicity here,
// we store them in a dynamic byte array with a length and capacity in bits.
// -----------------------------------------------------------------------------
typedef struct blob {
uint8_t *data;
// The total number of bits currently in use (the "length" of the blob).
size_t length;
size_t capacity;
// 0 = antestone (mutable)
// 1 = stone (immutable)
int is_stone;
} blob;
// Create a new blob with specified bit capacity
blob *blob_new(size_t capacity);
// Create a blob from another blob (copy bits from 'from' to 'to')
blob *blob_new_from_blob(const blob *src, size_t from, size_t to);
// Create a blob with length bits, filled with logical value or random function
blob *blob_new_with_fill(size_t length_bits, int logical_value);
// Destroy blob and free memory
void blob_destroy(blob *b);
// Turn a blob into stone (immutable)
void blob_make_stone(blob *b);
// Write operations (only work on antestone blobs)
int blob_write_bit(blob *b, int bit_val);
int blob_write_blob(blob *b, const blob *src);
int blob_write_dec64(blob *b, double d);
int blob_write_int64(blob *b, int64_t i);
int blob_write_pad(blob *b, int block_size);
int blob_write_text(blob *b, const char *text);
// Read operations (only work on stone blobs)
int blob_read_bit(const blob *b, size_t pos, int *out_bit);
blob *blob_read_blob(const blob *b, size_t from, size_t to);
int blob_read_dec64(const blob *b, size_t from, double *out_value);
int blob_read_int64(const blob *b, size_t from, int length, int64_t *out_value);
int blob_read_text(const blob *b, size_t from, char **out_text, size_t *bits_read);
int blob_pad_check(const blob *b, size_t from, int block_size);
// Utility functions
int kim_length_for_fit(int64_t value);
#ifdef BLOB_IMPLEMENTATION
// Helper to ensure capacity for writing
static int blob_ensure_capacity(blob *b, size_t new_bits) {
size_t need_bits = b->length + new_bits;
if (need_bits <= b->capacity) return 0;
// Grow strategy: double until enough
size_t new_capacity = b->capacity == 0 ? 64 : b->capacity * 2;
while (new_capacity < need_bits) new_capacity *= 2;
// Round up to multiple of 8 bits
if (new_capacity % 8) new_capacity += 8 - (new_capacity % 8);
size_t new_size_bytes = new_capacity / 8;
uint8_t *new_ptr = realloc(b->data, new_size_bytes);
if (!new_ptr) return -1;
// Zero-fill newly allocated area
size_t old_size_bytes = b->capacity / 8;
if (new_size_bytes > old_size_bytes)
memset(new_ptr + old_size_bytes, 0, new_size_bytes - old_size_bytes);
b->data = new_ptr;
b->capacity = new_capacity;
return 0;
}
// A simple bit-level memcpy (may handle unaligned bits at both ends)
void bitcpy(unsigned char *dst, int dst_bit_offset,
unsigned char *src, int src_bit_offset, int bit_length)
{
int dst_byte_offset = dst_bit_offset / 8;
int src_byte_offset = src_bit_offset / 8;
int dst_bit = dst_bit_offset % 8;
int src_bit = src_bit_offset % 8;
int remaining_bits = bit_length;
if (dst_bit != 0 || src_bit != 0)
{
unsigned char mask = (1 << dst_bit) - 1;
unsigned char src_mask = (1 << (8 - src_bit)) - 1;
unsigned char src_byte = src[src_byte_offset] & (src_mask << src_bit);
dst[dst_byte_offset] = (dst[dst_byte_offset] & ~mask) |
(src_byte >> (src_bit - dst_bit));
remaining_bits -= (8 - dst_bit);
dst_byte_offset++;
src_byte_offset += (remaining_bits <= 0) ? 0 : 1;
}
int whole_bytes = remaining_bits / 8;
for (int i = 0; i < whole_bytes; i++)
dst[dst_byte_offset + i] = src[src_byte_offset + i];
remaining_bits %= 8;
dst_byte_offset += whole_bytes;
src_byte_offset += whole_bytes;
if (remaining_bits > 0)
{
unsigned char mask = (1 << remaining_bits) - 1;
unsigned char src_byte = src[src_byte_offset] & mask;
dst[dst_byte_offset] = (dst[dst_byte_offset] & ~mask) | src_byte;
}
}
// Fast bit-copy for arbitrary bit ranges (inclusive) from src → dest
void copy_bits_fast(const void *src, void *dest,
int n, /* start bit in src (inclusive) */
int m, /* end bit in src (inclusive) */
int x) /* start bit in dest */
{
const uint8_t *s = (const uint8_t*)src;
uint8_t *d = (uint8_t*)dest;
int total_bits = m - n + 1;
if (total_bits <= 0) return;
int src_bit = n;
int dst_bit = x;
int src_byte = src_bit >> 3;
int dst_byte = dst_bit >> 3;
int src_off = src_bit & 7;
int dst_off = dst_bit & 7;
// 1) Leading partial byte to align dest
if (dst_off != 0) {
int chunk = 8 - dst_off;
if (chunk > total_bits) chunk = total_bits;
uint8_t dst_mask = (((1u << chunk) - 1u) << dst_off);
uint16_t wb = (uint16_t)s[src_byte] | ((uint16_t)s[src_byte + 1] << 8);
uint8_t bits = (uint8_t)((wb >> src_off) & ((1u << chunk) - 1u));
bits <<= dst_off;
d[dst_byte] = (d[dst_byte] & ~dst_mask) | (bits & dst_mask);
total_bits -= chunk;
src_bit += chunk;
dst_bit += chunk;
src_byte = src_bit >> 3;
dst_byte = dst_bit >> 3;
src_off = src_bit & 7;
dst_off = dst_bit & 7; // now zero
}
// 2) Copy full bytes
if (total_bits >= 8) {
if (src_off == 0) {
int num_bytes = total_bits >> 3;
memcpy(&d[dst_byte], &s[src_byte], (size_t)num_bytes);
total_bits -= num_bytes << 3;
src_byte += num_bytes;
dst_byte += num_bytes;
} else {
int num_bytes = total_bits >> 3;
for (int i = 0; i < num_bytes; i++) {
uint16_t wb = (uint16_t)s[src_byte + i] |
((uint16_t)s[src_byte + i + 1] << 8);
d[dst_byte + i] = (uint8_t)((wb >> src_off) & 0xFFu);
}
total_bits -= num_bytes << 3;
src_byte += num_bytes;
dst_byte += num_bytes;
}
}
// 3) Trailing bits (< 8)
if (total_bits > 0) {
uint8_t dst_mask = (1u << total_bits) - 1u;
uint16_t wb = (uint16_t)s[src_byte] | ((uint16_t)s[src_byte + 1] << 8);
uint8_t bits = (uint8_t)((wb >> src_off) & dst_mask);
d[dst_byte] = (d[dst_byte] & ~dst_mask) | (bits & dst_mask);
}
}
// Copy bits [from..to) from src buffer into the blob at its current length
// (i.e. 'to' is exclusive). Advance blob->length by (to - from). Return 0.
static int blob_copy_bits(blob *dest, const void *src, size_t from, size_t to) {
size_t bits = to - from;
if (blob_ensure_capacity(dest, bits) < 0) return -1;
int n = (int)from;
int m = (int)(to - 1);
copy_bits_fast(src, dest->data, n, m, (int)dest->length);
dest->length += bits;
return 0;
}
blob *blob_new(size_t capacity) {
if (capacity < 0) capacity = 0;
blob *b = calloc(1, sizeof(blob));
if (!b) return NULL;
if (blob_ensure_capacity(b, capacity) < 0) {
free(b);
return NULL;
}
return b;
}
blob *blob_new_from_blob(const blob *src, size_t from, size_t to) {
if (!src) return NULL;
if (to > src->length) to = src->length;
if (from >= to) return blob_new(0);
size_t copy_len = to - from;
blob *b = blob_new(copy_len);
if (!b) return NULL;
b->length = 0; // will be set by blob_copy_bits
blob_copy_bits(b, src->data, from, to);
return b;
}
blob *blob_new_with_fill(size_t length_bits, int logical_value) {
blob *b = blob_new(length_bits);
if (!b) return NULL;
b->length = length_bits;
if (logical_value) {
size_t bytes = b->capacity / 8;
memset(b->data, 0xFF, bytes);
size_t used_bits = length_bits & 7;
if (used_bits && bytes > 0) {
uint8_t mask = (1 << used_bits) - 1;
b->data[bytes - 1] &= mask;
}
}
return b;
}
void blob_destroy(blob *b) {
if (!b) return;
if (b->data) {
free(b->data);
b->data = NULL;
b->length = 0;
b->capacity = 0;
}
free(b);
}
void blob_make_stone(blob *b) {
if (!b) return;
b->is_stone = 1;
// shrink buffer to exactly length bits
if (b->capacity > b->length) {
size_t size_bytes = (b->length + 7) >> 3;
if (size_bytes) {
uint8_t *new_ptr = realloc(b->data, size_bytes);
if (new_ptr) b->data = new_ptr;
} else {
free(b->data);
b->data = NULL;
}
b->capacity = b->length;
}
}
int blob_write_bit(blob *b, int bit_val) {
if (!b || b->is_stone) return -1;
if (blob_ensure_capacity(b, 1) < 0) return -1;
size_t bit_index = b->length;
size_t byte_index = bit_index >> 3;
size_t offset_in_byte = bit_index & 7;
if (bit_val)
b->data[byte_index] |= (1 << offset_in_byte);
else
b->data[byte_index] &= ~(1 << offset_in_byte);
b->length++;
return 0;
}
int blob_write_blob(blob *b, const blob *src) {
if (!b || !src || b->is_stone) return -1;
return blob_copy_bits(b, src->data, 0, src->length);
}
int blob_write_dec64(blob *b, double d) {
if (!b || b->is_stone) return -1;
if (blob_ensure_capacity(b, 64) < 0) return -1;
copy_bits_fast(&d, b->data, 0, 64 - 1, (int)b->length);
b->length += 64;
return 0;
}
int blob_write_int64(blob *b, int64_t i) {
if (!b || b->is_stone) return -1;
if (blob_ensure_capacity(b, 64) < 0) return -1;
copy_bits_fast(&i, b->data, 0, 64 - 1, (int)b->length);
b->length += 64;
return 0;
}
int blob_write_pad(blob *b, int block_size) {
if (!b || b->is_stone) return -1;
if (block_size <= 0) return -1;
if (blob_write_bit(b, 1) < 0) return -1;
size_t current = b->length;
size_t rem = current % block_size;
if (rem > 0) {
size_t zeros = block_size - rem;
for (size_t i = 0; i < zeros; i++)
if (blob_write_bit(b, 0) < 0) return -1;
}
return 0;
}
int blob_write_text(blob *b, const char *text) {
if (!b || !text || b->is_stone) return -1;
size_t len = strlen(text);
// Ensure capacity for 64-bit length + len*8 bits of text
if (blob_ensure_capacity(b, 64 + (len * 8)) < 0) return -1;
// 1) Write length prefix
if (blob_write_int64(b, (int64_t)len) < 0) return -1;
// 2) Write raw text bytes (len * 8 bits)
if (len > 0) {
copy_bits_fast(text, b->data, 0, (int)(len * 8 - 1), (int)b->length);
b->length += (len * 8);
}
return 0;
}
int blob_read_bit(const blob *b, size_t pos, int *out_bit) {
if (!b || !b->is_stone || !out_bit) return -1;
if (pos >= b->length) return -1;
size_t byte_index = pos >> 3;
size_t offset_in_byte = pos & 7;
*out_bit = (b->data[byte_index] & (1 << offset_in_byte)) ? 1 : 0;
return 0;
}
blob *blob_read_blob(const blob *b, size_t from, size_t to) {
if (!b || !b->is_stone) return NULL;
return blob_new_from_blob(b, from, to);
}
int blob_read_dec64(const blob *b, size_t from, double *out_value) {
if (!b || !b->is_stone || !out_value) return -1;
if (from + 64 > b->length) return -1;
if (from < 0) return -1;
copy_bits_fast(b->data, out_value, (int)from, (int)(from + 64 - 1), 0);
return 0;
}
int blob_read_int64(const blob *b, size_t from, int length, int64_t *out_value) {
if (!b || !b->is_stone || !out_value) return -1;
if (length < 0 || length > 64) return -1;
if (from + (size_t)length > b->length) return -1;
copy_bits_fast(b->data, out_value, (int)from, (int)(from + length - 1), 0);
return 0;
}
int blob_read_text(const blob *b, size_t from, char **out_text, size_t *bits_read) {
if (!b || !b->is_stone || !out_text || !bits_read) return -1;
// Need at least 64 bits for length prefix
if (from + 64 > b->length) return -1;
int64_t raw_len = 0;
// Read 64 bits of length
copy_bits_fast(b->data, &raw_len, (int)from, (int)(from + 64 - 1), 0);
if (raw_len < 0) return -1;
size_t len = (size_t)raw_len;
// Check that there are len bytes following
size_t after_len = from + 64;
size_t needed_end = after_len + (len * 8);
if (needed_end > b->length) return -1;
// Allocate (len + 1) bytes for C-string
char *str = malloc(len + 1);
if (!str) return -1;
// Copy exactly len*8 bits into str[0..len-1]
copy_bits_fast(b->data, str, (int)after_len, (int)(after_len + (len * 8) - 1), 0);
str[len] = '\0';
*out_text = str;
*bits_read = 64 + (len * 8);
return 0;
}
int blob_pad_check(const blob *b, size_t from, int block_size) {
if (!b || !b->is_stone) return 0;
if (block_size <= 0) return 0;
if (b->length % block_size != 0) return 0;
int64_t diff = (int64_t)b->length - (int64_t)from;
if (diff <= 0 || diff > block_size) return 0;
int bit;
if (blob_read_bit(b, from, &bit) < 0 || bit != 1) return 0;
for (size_t i = from + 1; i < b->length; i++) {
if (blob_read_bit(b, i, &bit) < 0 || bit != 0) return 0;
}
return 1;
}
#endif /* BLOB_IMPLEMENTATION */
#endif /* BLOB_H */