#ifndef BLOB_H #define BLOB_H #include #include #include #include // ----------------------------------------------------------------------------- // 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 bit_length and capacity in bits. // ----------------------------------------------------------------------------- typedef struct blob { // The actual buffer holding the bits (in multiples of 8 bits). uint8_t *data; // The total number of bits currently in use (the "length" of the blob). size_t bit_length; // The total capacity in bits that 'data' can currently hold without realloc. size_t bit_capacity; // 0 = antestone (mutable) // 1 = stone (immutable) int is_stone; } blob; // Initialize a new blob void blob_init(blob *b); // Create a new blob with specified bit capacity blob *blob_new(size_t bit_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_fit(blob *b, int64_t fit, int length); int blob_write_kim(blob *b, int64_t value); int blob_write_pad(blob *b, int block_size); int blob_write_text(blob *b, const char *text); int blob_write_bytes(blob *b, void *data, size_t length); // 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_fit(const blob *b, size_t from, int length, int64_t *out_value); int blob_read_kim(const blob *b, size_t from, int64_t *out_value, size_t *bits_read); 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->bit_length + new_bits; if (need_bits <= b->bit_capacity) return 0; // Increase capacity (in multiples of bytes). // We can pick a growth strategy. For demonstration, double it: size_t new_capacity = b->bit_capacity == 0 ? 64 : b->bit_capacity * 2; while (new_capacity < need_bits) new_capacity *= 2; // Round up new_capacity to a 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; // out of memory } // zero-fill the new area (only beyond the old capacity) size_t old_size_bytes = b->bit_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->bit_capacity = new_capacity; return 0; } void blob_init(blob *b) { b->data = NULL; b->bit_length = 0; b->bit_capacity = 0; b->is_stone = 0; } blob *blob_new(size_t bit_capacity) { blob *b = calloc(1, sizeof(blob)); if (!b) return NULL; blob_init(b); if (bit_capacity > 0) { // Round up to multiple of 8 if (bit_capacity % 8) { bit_capacity += 8 - (bit_capacity % 8); } b->bit_capacity = bit_capacity; size_t bytes = bit_capacity / 8; b->data = calloc(bytes, 1); if (!b->data) { 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->bit_length) to = src->bit_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->bit_length = copy_len; // Copy bits for (size_t i = 0; i < copy_len; i++) { size_t src_bit_index = from + i; size_t src_byte = src_bit_index >> 3; size_t src_off = src_bit_index & 7; int bit_val = (src->data[src_byte] >> src_off) & 1; size_t dst_byte = i >> 3; size_t dst_off = i & 7; if (bit_val) { b->data[dst_byte] |= (1 << dst_off); } } 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->bit_length = length_bits; if (logical_value) { // Fill with 1s size_t bytes = b->bit_capacity / 8; memset(b->data, 0xff, bytes); // Clear unused bits in last byte size_t used_bits_in_last_byte = length_bits & 7; if (used_bits_in_last_byte && bytes > 0) { uint8_t mask = (1 << used_bits_in_last_byte) - 1; b->data[bytes - 1] &= mask; } } return b; } void blob_destroy(blob *b) { if (b) { if (b && b->data) { free(b->data); b->data = NULL; b->bit_length = 0; b->bit_capacity = 0; } free(b); } } void blob_make_stone(blob *b) { if (!b) return; b->is_stone = 1; // Optionally shrink the buffer to exactly bit_length in size if (b->bit_capacity > b->bit_length) { size_t size_in_bytes = (b->bit_length + 7) >> 3; // round up to full bytes uint8_t *new_ptr = NULL; if (size_in_bytes) { new_ptr = realloc(b->data, size_in_bytes); if (new_ptr) { b->data = new_ptr; } } else { // zero length free(b->data); b->data = NULL; } b->bit_capacity = b->bit_length; // capacity in bits now matches 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->bit_length; size_t byte_index = bit_index >> 3; size_t offset_in_byte = bit_index & 7; // set or clear bit if (bit_val) b->data[byte_index] |= (1 << offset_in_byte); else b->data[byte_index] &= ~(1 << offset_in_byte); b->bit_length++; return 0; } int blob_write_blob(blob *b, const blob *src) { if (!b || !src || b->is_stone) return -1; // Append all bits from src blob for (size_t i = 0; i < src->bit_length; i++) { size_t byte_idx = i / 8; size_t bit_idx = i % 8; int bit = (src->data[byte_idx] >> bit_idx) & 1; if (blob_write_bit(b, bit) < 0) { return -1; } } return 0; } int blob_write_dec64(blob *b, double d) { if (!b || b->is_stone) return -1; // Simple DEC64 encoding: store as IEEE 754 double (64 bits) uint64_t bits; memcpy(&bits, &d, sizeof(bits)); // Write 64 bits for (int i = 0; i < 64; i++) { if (blob_write_bit(b, (bits >> i) & 1) < 0) { return -1; } } return 0; } int blob_write_fit(blob *b, int64_t fit, int length) { if (!b || b->is_stone) return -1; if (length < 0 || length > 64) return -1; // Check if fit requires more bits than allowed if (length < 64) { int64_t max = (1LL << length) - 1; if (fit < 0 || fit > max) { return -1; } } // Write the bits for (int i = 0; i < length; i++) { if (blob_write_bit(b, (fit >> i) & 1) < 0) { return -1; } } return 0; } // Calculate the kim length in bits for a fit (int64) value int kim_length_for_fit(int64_t value) { if (value >= -1 && value <= 127) return 8; if (value >= -127 && value <= 16383) return 16; if (value >= -16383 && value <= 2097151) return 24; if (value >= -2097151 && value <= 268435455) return 32; if (value >= -268435455 && value <= 34359738367LL) return 40; if (value >= -34359738367LL && value <= 4398046511103LL) return 48; if (value >= -4398046511103LL && value <= 562949953421311LL) return 56; if (value >= -562949953421311LL && value <= 36028797018963967LL) return 64; if (value >= -36028797018963967LL) return 72; return 80; // Maximum kim length } int blob_write_kim(blob *b, int64_t value) { if (!b || b->is_stone) return -1; int bits = kim_length_for_fit(value); int bytes = bits / 8; uint8_t kim_bytes[10] = {0}; // Max 10 bytes for kim if (value < 0) { // Negative number: first byte is 0x80, then encode -value-1 kim_bytes[0] = 0x80; value = -value - 1; // Encode remaining bytes for (int i = bytes - 1; i > 0; i--) { kim_bytes[i] = value & 0x7F; value >>= 7; if (i > 1) kim_bytes[i] |= 0x80; // Set continuation bit } } else { // Positive number for (int i = bytes - 1; i >= 0; i--) { kim_bytes[i] = value & 0x7F; value >>= 7; if (i > 0) kim_bytes[i] |= 0x80; // Set continuation bit } } // Write the kim bytes as bits for (int i = 0; i < bytes; i++) { for (int j = 0; j < 8; j++) { if (blob_write_bit(b, (kim_bytes[i] >> j) & 1) < 0) return -1; } } return 0; } int blob_write_pad(blob *b, int block_size) { if (!b || b->is_stone) return -1; if (block_size <= 0) return -1; // Write a 1 bit if (blob_write_bit(b, 1) < 0) { return -1; } // Calculate how many 0 bits to add size_t current_len = b->bit_length; size_t remainder = current_len % block_size; if (remainder > 0) { size_t zeros_needed = block_size - remainder; for (size_t i = 0; i < zeros_needed; 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); // Write kim-encoded length if (blob_write_kim(b, len) < 0) { return -1; } // Write each character as kim-encoded UTF-32 // For simplicity, assuming ASCII for (size_t i = 0; i < len; i++) { if (blob_write_kim(b, (unsigned char)text[i]) < 0) { return -1; } } return 0; } int blob_write_bytes(blob *b, void *data, size_t length) { if (!b || !data || b->is_stone) return -1; // Write each byte as 8 bits uint8_t *bytes = (uint8_t *)data; for (size_t i = 0; i < length; i++) { for (int j = 0; j < 8; j++) { if (blob_write_bit(b, (bytes[i] >> j) & 1) < 0) { return -1; } } } 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->bit_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->bit_length) return -1; // Read 64 bits uint64_t bits = 0; for (int i = 0; i < 64; i++) { int bit; blob_read_bit(b, from + i, &bit); if (bit) bits |= (1ULL << i); } // Convert to double memcpy(out_value, &bits, sizeof(*out_value)); return 0; } int blob_read_fit(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 + length > b->bit_length) return -1; // Read bits int64_t value = 0; for (int i = 0; i < length; i++) { int bit; blob_read_bit(b, from + i, &bit); if (bit) value |= (1LL << i); } *out_value = value; return 0; } int blob_read_kim(const blob *b, size_t from, int64_t *out_value, size_t *bits_read) { if (!b || !b->is_stone || !out_value || !bits_read) return -1; size_t pos = from; uint8_t bytes[10]; int byte_count = 0; // Read bytes until we find one without continuation bit while (byte_count < 10) { if (pos + 8 > b->bit_length) return -1; uint8_t byte = 0; for (int i = 0; i < 8; i++) { int bit; if (blob_read_bit(b, pos + i, &bit) < 0) return -1; if (bit) byte |= (1 << i); } bytes[byte_count++] = byte; pos += 8; if (!(byte & 0x80)) break; // No continuation bit } if (byte_count == 0) return -1; // Decode the kim value int64_t value = 0; int is_negative = (bytes[0] == 0x80); if (is_negative) { // Skip the 0x80 byte and decode remaining for (int i = 1; i < byte_count; i++) { value = (value << 7) | (bytes[i] & 0x7F); } value = -value - 1; } else { for (int i = 0; i < byte_count; i++) { value = (value << 7) | (bytes[i] & 0x7F); } } *out_value = value; *bits_read = byte_count * 8; 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; if (from >= b->bit_length) return -1; // Read kim-encoded length int64_t length; size_t len_bits; if (blob_read_kim(b, from, &length, &len_bits) < 0) { return -1; } if (length < 0) return -1; size_t pos = from + len_bits; // Read characters char *str = malloc(length + 1); if (!str) return -1; for (int64_t i = 0; i < length; i++) { int64_t ch; size_t ch_bits; if (blob_read_kim(b, pos, &ch, &ch_bits) < 0) { free(str); return -1; } // For simplicity, assuming ASCII str[i] = (char)(ch & 0xFF); pos += ch_bits; } str[length] = '\0'; *out_text = str; *bits_read = pos - from; 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; // Check if blob length is multiple of block_size if (b->bit_length % block_size != 0) { return 0; } // Check if difference between length and from is <= block_size int64_t diff = b->bit_length - from; if (diff <= 0 || diff > block_size) { return 0; } // Check if bit at from is 1 int bit; if (blob_read_bit(b, from, &bit) < 0 || bit != 1) { return 0; } // Check if remaining bits are 0 for (size_t i = from + 1; i < b->bit_length; i++) { if (blob_read_bit(b, i, &bit) < 0 || bit != 0) { return 0; } } return 1; } #endif /* BLOB_IMPLEMENTATION */ #endif /* BLOB_H */