dynamic_buffer.h v0.2.2
Reference-counted byte buffer library for efficient I/O operations
Loading...
Searching...
No Matches
dynamic_buffer.h
Go to the documentation of this file.
1
79#ifndef DYNAMIC_BUFFER_H
80#define DYNAMIC_BUFFER_H
81
82// Version information
83#define DB_VERSION_MAJOR 0
84#define DB_VERSION_MINOR 2
85#define DB_VERSION_PATCH 2
86#define DB_VERSION_STRING "0.2.2"
87
88#include <stddef.h>
89#include <stdint.h>
90#include <stdbool.h>
91#include <string.h>
92#include <stdio.h>
93
94// For ssize_t
95#ifdef _WIN32
96#include <io.h>
97typedef long ssize_t;
98#else
99#include <sys/types.h>
100#include <unistd.h>
101#endif
102
103// Configuration macros (user can override before including)
104#ifndef DB_MALLOC
105#include <stdlib.h>
106#define DB_MALLOC malloc
107#endif
108
109#ifndef DB_REALLOC
110#include <stdlib.h>
111#define DB_REALLOC realloc
112#endif
113
114#ifndef DB_FREE
115#include <stdlib.h>
116#define DB_FREE free
117#endif
118
119#ifndef DB_ASSERT
120#include <assert.h>
121#define DB_ASSERT assert
122#endif
123
124// Atomic reference counting support (requires C11)
125#ifndef DB_ATOMIC_REFCOUNT
126#define DB_ATOMIC_REFCOUNT 0
127#endif
128
129#if DB_ATOMIC_REFCOUNT
130#include <stdatomic.h>
131typedef _Atomic int db_refcount_t;
132#define DB_REFCOUNT_INIT(n) ATOMIC_VAR_INIT(n)
133#define DB_REFCOUNT_LOAD(ptr) atomic_load(ptr)
134#define DB_REFCOUNT_INCREMENT(ptr) (atomic_fetch_add(ptr, 1) + 1)
135#define DB_REFCOUNT_DECREMENT(ptr) (atomic_fetch_sub(ptr, 1) - 1)
136#else
137typedef int db_refcount_t;
138#define DB_REFCOUNT_INIT(n) (n)
139#define DB_REFCOUNT_LOAD(ptr) (*(ptr))
140#define DB_REFCOUNT_INCREMENT(ptr) (++(*(ptr)))
141#define DB_REFCOUNT_DECREMENT(ptr) (--(*(ptr)))
142#endif
143
144// Function visibility control
145#ifndef DB_DEF
146#ifdef DB_IMPLEMENTATION
147// When the including .c defines DB_IMPLEMENTATION,
148// we want *definitions* with external linkage.
149// For the declarations above the impl, make DB_DEF empty.
150#define DB_DEF
151#else
152// In all other TUs, make them extern declarations.
153#define DB_DEF extern
154#endif
155#endif
156
157// Forward declarations
158
173typedef char* db_buffer;
174
186DB_DEF db_buffer db_new(size_t capacity);
187
203DB_DEF db_buffer db_new_with_data(const void* data, size_t size);
204
213DB_DEF db_buffer db_new_from_owned_data(void* data, size_t size, size_t capacity);
214
221
227
243
250
257
264
281DB_DEF db_buffer db_slice(db_buffer buf, size_t offset, size_t length);
282
290
298
328DB_DEF db_buffer db_append(db_buffer buf, const void* data, size_t size);
329
330
346
353DB_DEF db_buffer db_concat_many(db_buffer* buffers, size_t count);
354
370
378
395DB_DEF ssize_t db_read_fd(db_buffer* buf_ptr, int fd, size_t max_bytes);
396
403DB_DEF ssize_t db_write_fd(db_buffer buf, int fd);
404
410DB_DEF db_buffer db_read_file(const char* filename);
411
418DB_DEF bool db_write_file(db_buffer buf, const char* filename);
419
434DB_DEF db_buffer db_to_hex(db_buffer buf, bool uppercase);
435
442DB_DEF db_buffer db_from_hex(const char* hex_string, size_t length);
443
449DB_DEF void db_debug_print(db_buffer buf, const char* label);
450
462typedef struct db_builder_internal* db_builder;
463
480DB_DEF db_builder db_builder_new(size_t initial_capacity);
481
488
495
501
508
515
522
528
535DB_DEF int db_builder_append_uint8(db_builder builder, uint8_t value);
536
543DB_DEF int db_builder_append_uint16_le(db_builder builder, uint16_t value);
544
551DB_DEF int db_builder_append_uint16_be(db_builder builder, uint16_t value);
552
559DB_DEF int db_builder_append_uint32_le(db_builder builder, uint32_t value);
560
567DB_DEF int db_builder_append_uint32_be(db_builder builder, uint32_t value);
568
575DB_DEF int db_builder_append_uint64_le(db_builder builder, uint64_t value);
576
583DB_DEF int db_builder_append_uint64_be(db_builder builder, uint64_t value);
584
592DB_DEF int db_builder_append(db_builder builder, const void* data, size_t size);
593
600DB_DEF int db_builder_append_cstring(db_builder builder, const char* str);
601
609
621typedef struct db_reader_internal* db_reader;
622
638
645
651
657
664
671
678DB_DEF bool db_reader_can_read(db_reader reader, size_t bytes);
679
685DB_DEF void db_reader_seek(db_reader reader, size_t position);
686
693
700
707
714
721
728
735
742DB_DEF void db_read_bytes(db_reader reader, void* data, size_t size);
743
746// Implementation section - only compiled when DB_IMPLEMENTATION is defined
747#ifdef DB_IMPLEMENTATION
748
756typedef struct db_internal {
757 db_refcount_t refcount;
758 size_t size;
759 size_t capacity;
760} db_internal;
761
767static inline db_internal* db_meta(db_buffer buf) {
768 return (db_internal*)((char*)(buf) - sizeof(db_internal));
769}
770
775static db_buffer db_alloc(size_t capacity) {
776 // Allocate: metadata + buffer data
777 size_t total_size = sizeof(db_internal) + capacity;
778
779 // Check for overflow
780 if (total_size < sizeof(db_internal) || total_size < capacity) {
781 return NULL;
782 }
783
784 void* block = DB_MALLOC(total_size);
785 DB_ASSERT(block && "db_alloc: memory allocation failed");
786
787 // Initialize metadata
788 db_internal* meta = (db_internal*)block;
789 meta->refcount = DB_REFCOUNT_INIT(1);
790 meta->size = 0;
791 meta->capacity = capacity;
792
793 // Return pointer to buffer data portion
794 return (db_buffer)((char*)block + sizeof(db_internal));
795}
796
801static void db_dealloc(db_buffer buf) {
802 if (!buf) return;
803
804 // Get original malloc pointer and free it
805 void* block = (char*)buf - sizeof(db_internal);
806 DB_FREE(block);
807}
808
809// Implementation of public functions
810
811db_buffer db_new(size_t capacity) {
812 return db_alloc(capacity);
813}
814
815db_buffer db_new_with_data(const void* data, size_t size) {
816 DB_ASSERT((data || size == 0) && "db_new_with_data: data cannot be NULL when size > 0");
817
818 db_buffer buf = db_alloc(size);
819 // db_alloc now asserts on allocation failure
820
821 if (size > 0) {
822 memcpy(buf, data, size);
823 db_meta(buf)->size = size;
824 }
825
826 return buf;
827}
828
829db_buffer db_new_from_owned_data(void* data, size_t size, size_t capacity) {
830 DB_ASSERT((data || (size == 0 && capacity == 0)) && "db_new_from_owned_data: data cannot be NULL with non-zero size/capacity");
831 DB_ASSERT(capacity >= size && "db_new_from_owned_data: capacity must be >= size");
832
833 // We can't use the negative offset trick here since we don't control the data allocation
834 // Instead, we'll copy the data to maintain design consistency
835 db_buffer buf = db_alloc(capacity);
836 // db_alloc now asserts on allocation failure
837
838 if (size > 0) {
839 memcpy(buf, data, size);
840 db_meta(buf)->size = size;
841 }
842
843 return buf;
844}
845
847 DB_ASSERT(buf && "db_retain: buf cannot be NULL");
848 DB_REFCOUNT_INCREMENT(&db_meta(buf)->refcount);
849 return buf;
850}
851
852void db_release(db_buffer* buf_ptr) {
853 DB_ASSERT(buf_ptr && "db_release: buf_ptr cannot be NULL");
854 if (!*buf_ptr) return;
855
856 db_buffer buf = *buf_ptr;
857 *buf_ptr = NULL;
858
859 if (DB_REFCOUNT_DECREMENT(&db_meta(buf)->refcount) == 0) {
860 // Reference count reached 0, free the buffer
861 db_dealloc(buf);
862 }
863}
864
865
866size_t db_size(db_buffer buf) {
867 DB_ASSERT(buf && "db_size: buf cannot be NULL");
868 return db_meta(buf)->size;
869}
870
871size_t db_capacity(db_buffer buf) {
872 DB_ASSERT(buf && "db_capacity: buf cannot be NULL");
873 return db_meta(buf)->capacity;
874}
875
876bool db_is_empty(db_buffer buf) {
877 DB_ASSERT(buf && "db_is_empty: buf cannot be NULL");
878 return db_meta(buf)->size == 0;
879}
880
881int db_refcount(db_buffer buf) {
882 DB_ASSERT(buf && "db_refcount: buf cannot be NULL");
883 return DB_REFCOUNT_LOAD(&db_meta(buf)->refcount);
884}
885
886db_buffer db_slice(db_buffer buf, size_t offset, size_t length) {
887 DB_ASSERT(buf && "db_slice: buf cannot be NULL");
888
889 size_t buf_size = db_meta(buf)->size;
890 if (offset > buf_size || offset + length > buf_size) {
891 return NULL; // Invalid bounds
892 }
893
894 // Create an independent copy of the slice data
895 db_buffer slice = db_alloc(length);
896 // db_alloc now asserts on allocation failure
897
898 // Copy the slice data directly from the source buffer
899 if (length > 0) {
900 memcpy(slice, buf + offset, length);
901 db_meta(slice)->size = length;
902 }
903
904 return slice;
905}
906
907db_buffer db_slice_from(db_buffer buf, size_t offset) {
908 DB_ASSERT(buf && "db_slice_from: buf cannot be NULL");
909 size_t buf_size = db_meta(buf)->size;
910 if (offset > buf_size) return NULL;
911 return db_slice(buf, offset, buf_size - offset);
912}
913
914db_buffer db_slice_to(db_buffer buf, size_t length) {
915 DB_ASSERT(buf && "db_slice_to: buf cannot be NULL");
916 if (length > db_meta(buf)->size) return NULL;
917 return db_slice(buf, 0, length);
918}
919
920
921
922db_buffer db_append(db_buffer buf, const void* data, size_t size) {
923 DB_ASSERT(buf && "db_append: buf cannot be NULL");
924 DB_ASSERT((data || size == 0) && "db_append: data cannot be NULL when size > 0");
925
926 if (size == 0) {
927 return db_retain(buf); // Return retained original if nothing to append
928 }
929
930 size_t old_size = db_meta(buf)->size;
931 size_t new_size = old_size + size;
932 db_buffer result = db_new(new_size);
933 // db_new now asserts on allocation failure
934
935 // Copy original buffer
936 if (old_size > 0) {
937 memcpy(result, buf, old_size);
938 }
939 // Append new data
940 memcpy(result + old_size, data, size);
941
942 // Set the final size
943 db_meta(result)->size = new_size;
944
945 return result;
946}
947
948// Internal helper functions for builder (similar to ds_stringbuilder helpers)
949
955static int db_internal_ensure_unique(db_buffer* builder_data) {
956 if (!*builder_data) return -1;
957
958 if (db_refcount(*builder_data) <= 1) {
959 return 0; // Already unique
960 }
961
962 // Need to create our own copy
963 size_t current_size = db_size(*builder_data);
964 db_buffer new_buf = db_new_with_data(*builder_data, current_size);
965 // db_new_with_data now asserts on allocation failure
966
967 db_release(builder_data);
968 *builder_data = new_buf;
969 return 0;
970}
971
979static int db_internal_ensure_capacity(db_buffer* builder_data, size_t* builder_capacity, size_t required_capacity) {
980 if (*builder_capacity >= required_capacity) {
981 return 0; // Already have enough capacity
982 }
983
984 size_t new_capacity = *builder_capacity;
985 if (new_capacity == 0) {
986 new_capacity = 16; // Initial capacity
987 }
988
989 while (new_capacity < required_capacity) {
990 new_capacity *= 2; // Double the capacity
991 }
992
993 // Use realloc for efficient buffer growth
994 db_internal* meta = db_meta(*builder_data);
995
996 // Calculate new block size (metadata + data)
997 size_t new_block_size = sizeof(db_internal) + new_capacity;
998
999 // Realloc the entire block (metadata + data)
1000 db_internal* new_meta = (db_internal*)DB_REALLOC(meta, new_block_size);
1001 DB_ASSERT(new_meta && "db_internal_ensure_capacity: memory reallocation failed");
1002
1003 // Update capacity (size remains the same)
1004 new_meta->capacity = new_capacity;
1005
1006 // Update the buffer pointer to point to data portion
1007 *builder_data = (db_buffer)((char*)new_meta + sizeof(db_internal));
1008 *builder_capacity = new_capacity;
1009 return 0;
1010}
1011
1020static int db_internal_append(db_buffer* builder_data, size_t* builder_capacity, const void* data, size_t size) {
1021 if (size == 0) return 0;
1022
1023 if (db_internal_ensure_unique(builder_data) != 0) {
1024 return -1;
1025 }
1026
1027 size_t current_size = db_size(*builder_data);
1028 size_t needed_capacity = current_size + size;
1029
1030 if (db_internal_ensure_capacity(builder_data, builder_capacity, needed_capacity) != 0) {
1031 return -1;
1032 }
1033
1034 // Append the data
1035 memcpy(*builder_data + current_size, data, size);
1036 db_meta(*builder_data)->size = current_size + size;
1037
1038 return 0;
1039}
1040
1042 DB_ASSERT(buf1 && "db_concat: buf1 cannot be NULL");
1043 DB_ASSERT(buf2 && "db_concat: buf2 cannot be NULL");
1044
1045 size_t size1 = db_meta(buf1)->size;
1046 size_t size2 = db_meta(buf2)->size;
1047 size_t total_size = size1 + size2;
1048
1049 if (total_size == 0) return db_new(0);
1050
1051 db_buffer result = db_new(total_size);
1052 // db_new now asserts on allocation failure
1053
1054 if (size1 > 0) {
1055 memcpy(result, buf1, size1);
1056 }
1057
1058 if (size2 > 0) {
1059 memcpy(result + size1, buf2, size2);
1060 }
1061
1062 db_meta(result)->size = total_size;
1063 return result;
1064}
1065
1066db_buffer db_concat_many(db_buffer* buffers, size_t count) {
1067 if (!buffers || count == 0) return db_new(0);
1068
1069 // Calculate total size
1070 size_t total_size = 0;
1071 for (size_t i = 0; i < count; i++) {
1072 if (buffers[i]) {
1073 total_size += db_meta(buffers[i])->size;
1074 }
1075 }
1076
1077 db_buffer result = db_new(total_size);
1078 // db_new now asserts on allocation failure
1079
1080 size_t offset = 0;
1081 for (size_t i = 0; i < count; i++) {
1082 if (buffers[i]) {
1083 size_t size = db_meta(buffers[i])->size;
1084 if (size > 0) {
1085 memcpy(result + offset, buffers[i], size);
1086 offset += size;
1087 }
1088 }
1089 }
1090
1091 db_meta(result)->size = total_size;
1092 return result;
1093}
1094
1095bool db_equals(db_buffer buf1, db_buffer buf2) {
1096 DB_ASSERT(buf1 && "db_equals: buf1 cannot be NULL");
1097 DB_ASSERT(buf2 && "db_equals: buf2 cannot be NULL");
1098
1099 if (buf1 == buf2) return true;
1100
1101 size_t size1 = db_meta(buf1)->size;
1102 size_t size2 = db_meta(buf2)->size;
1103 if (size1 != size2) return false;
1104
1105 return memcmp(buf1, buf2, size1) == 0;
1106}
1107
1108int db_compare(db_buffer buf1, db_buffer buf2) {
1109 DB_ASSERT(buf1 && "db_compare: buf1 cannot be NULL");
1110 DB_ASSERT(buf2 && "db_compare: buf2 cannot be NULL");
1111
1112 if (buf1 == buf2) return 0;
1113
1114 size_t size1 = db_meta(buf1)->size;
1115 size_t size2 = db_meta(buf2)->size;
1116 size_t min_size = size1 < size2 ? size1 : size2;
1117
1118 int result = memcmp(buf1, buf2, min_size);
1119
1120 if (result != 0) return result;
1121
1122 // Contents are equal up to min_size, compare sizes
1123 if (size1 < size2) return -1;
1124 if (size1 > size2) return 1;
1125 return 0;
1126}
1127
1128// I/O operations - basic implementation
1129#ifdef _WIN32
1130#include <io.h>
1131#define db_read read
1132#define db_write write
1133#else
1134#include <unistd.h>
1135#define db_read read
1136#define db_write write
1137#endif
1138
1139ssize_t db_read_fd(db_buffer* buf_ptr, int fd, size_t max_bytes) {
1140 DB_ASSERT(buf_ptr && *buf_ptr && "db_read_fd: buf_ptr and *buf_ptr cannot be NULL");
1141 DB_ASSERT(fd >= 0 && "db_read_fd: invalid file descriptor");
1142
1143 // Read data into a temporary buffer first
1144 size_t read_size = max_bytes == 0 ? 4096 : max_bytes;
1145 char* temp_buffer = (char*)DB_MALLOC(read_size);
1146 DB_ASSERT(temp_buffer && "db_read_fd: memory allocation failed");
1147
1148 ssize_t bytes_read = db_read(fd, temp_buffer, read_size);
1149 if (bytes_read > 0) {
1150 // Create new buffer with appended data (immutable operation)
1151 db_buffer new_buf = db_append(*buf_ptr, temp_buffer, bytes_read);
1152 if (new_buf) {
1153 db_release(buf_ptr); // Release old buffer
1154 *buf_ptr = new_buf; // Update with new buffer
1155 } else {
1156 bytes_read = -1; // Failed to append
1157 }
1158 }
1159
1160 DB_FREE(temp_buffer);
1161 return bytes_read;
1162}
1163
1164ssize_t db_write_fd(db_buffer buf, int fd) {
1165 DB_ASSERT(buf && "db_write_fd: buf cannot be NULL");
1166 DB_ASSERT(fd >= 0 && "db_write_fd: invalid file descriptor");
1167 size_t size = db_meta(buf)->size;
1168 if (size == 0) return 0;
1169
1170 return db_write(fd, buf, size);
1171}
1172
1173db_buffer db_read_file(const char* filename) {
1174 if (!filename) return NULL; // Allow NULL filename as runtime error
1175
1176 FILE* file = fopen(filename, "rb");
1177 if (!file) return NULL;
1178
1179 // Get file size
1180 fseek(file, 0, SEEK_END);
1181 long file_size = ftell(file);
1182 fseek(file, 0, SEEK_SET);
1183
1184 if (file_size < 0) {
1185 fclose(file);
1186 return NULL;
1187 }
1188
1189 db_buffer buf = db_new(file_size);
1190 if (!buf) {
1191 fclose(file);
1192 return NULL;
1193 }
1194
1195 size_t bytes_read = fread(buf, 1, file_size, file);
1196 fclose(file);
1197
1198 db_meta(buf)->size = bytes_read;
1199 return buf;
1200}
1201
1202bool db_write_file(db_buffer buf, const char* filename) {
1203 DB_ASSERT(buf && "db_write_file: buf cannot be NULL");
1204 if (!filename) return false;
1205
1206 FILE* file = fopen(filename, "wb");
1207 if (!file) return false;
1208
1209 bool success = true;
1210 size_t size = db_meta(buf)->size;
1211 if (size > 0) {
1212 size_t written = fwrite(buf, 1, size, file);
1213 success = (written == size);
1214 }
1215
1216 fclose(file);
1217 return success;
1218}
1219
1220// Utility functions
1221db_buffer db_to_hex(db_buffer buf, bool uppercase) {
1222 DB_ASSERT(buf && "db_to_hex: buf cannot be NULL");
1223
1224 size_t size = db_meta(buf)->size;
1225 if (size == 0) return db_new_with_data("", 0);
1226
1227 size_t hex_size = size * 2;
1228 db_buffer hex_buf = db_new(hex_size);
1229 // db_new now asserts on allocation failure
1230
1231 const char* hex_chars = uppercase ? "0123456789ABCDEF" : "0123456789abcdef";
1232 const uint8_t* data = (const uint8_t*)buf;
1233 char* hex_data = hex_buf;
1234
1235 for (size_t i = 0; i < size; i++) {
1236 hex_data[i * 2] = hex_chars[data[i] >> 4];
1237 hex_data[i * 2 + 1] = hex_chars[data[i] & 0x0F];
1238 }
1239
1240 db_meta(hex_buf)->size = hex_size;
1241 return hex_buf;
1242}
1243
1244static int hex_char_to_value(char c) {
1245 if (c >= '0' && c <= '9') return c - '0';
1246 if (c >= 'A' && c <= 'F') return c - 'A' + 10;
1247 if (c >= 'a' && c <= 'f') return c - 'a' + 10;
1248 return -1;
1249}
1250
1251db_buffer db_from_hex(const char* hex_string, size_t length) {
1252 if (!hex_string || length % 2 != 0) return NULL; // Runtime validation
1253
1254 size_t byte_length = length / 2;
1255 db_buffer buf = db_new(byte_length);
1256 // db_new now asserts on allocation failure
1257
1258 uint8_t* data = (uint8_t*)buf;
1259
1260 for (size_t i = 0; i < byte_length; i++) {
1261 int high = hex_char_to_value(hex_string[i * 2]);
1262 int low = hex_char_to_value(hex_string[i * 2 + 1]);
1263
1264 if (high < 0 || low < 0) {
1265 db_release(&buf);
1266 return NULL;
1267 }
1268
1269 data[i] = (uint8_t)((high << 4) | low);
1270 }
1271
1272 db_meta(buf)->size = byte_length;
1273 return buf;
1274}
1275
1276void db_debug_print(db_buffer buf, const char* label) {
1277 const char* name = label ? label : "buffer";
1278
1279 if (!buf) {
1280 printf("%s: NULL\n", name);
1281 return;
1282 }
1283
1284 db_internal* meta = db_meta(buf);
1285 size_t size = meta->size;
1286 size_t capacity = meta->capacity;
1287 int refcount = DB_REFCOUNT_LOAD(&meta->refcount);
1288
1289 printf("%s: size=%zu, capacity=%zu, refcount=%d\n",
1290 name, size, capacity, refcount);
1291
1292 // Print first few bytes as hex
1293 if (size > 0) {
1294 printf(" data: ");
1295 const uint8_t* data = (const uint8_t*)buf;
1296 size_t print_size = size < 16 ? size : 16;
1297
1298 for (size_t i = 0; i < print_size; i++) {
1299 printf("%02x ", data[i]);
1300 }
1301
1302 if (size > 16) {
1303 printf("... (%zu more bytes)", size - 16);
1304 }
1305 printf("\n");
1306 }
1307}
1308
1309// Builder and Reader Implementation
1310
1311struct db_builder_internal {
1312 db_refcount_t refcount; // Reference count for the builder itself
1313 db_buffer data; // Points to buffer data (same layout as db_buffer)
1314 size_t capacity; // Capacity for growth (size is in metadata)
1315};
1316
1317struct db_reader_internal {
1318 db_refcount_t refcount; // Reference count for the reader itself
1319 db_buffer buf; // Buffer being read (retained reference)
1320 size_t position; // Current read position
1321};
1322
1323// Builder implementation
1324
1325db_builder db_builder_new(size_t initial_capacity) {
1326 struct db_builder_internal* builder = (struct db_builder_internal*)DB_MALLOC(sizeof(struct db_builder_internal));
1327 DB_ASSERT(builder && "db_builder_new: memory allocation failed");
1328
1329 db_buffer buf = db_new(initial_capacity);
1330 // db_new asserts on allocation failure
1331
1332 builder->refcount = DB_REFCOUNT_INIT(1);
1333 builder->data = buf; // Take ownership of newly created buffer
1334 builder->capacity = initial_capacity;
1335
1336 return builder;
1337}
1338
1340 DB_ASSERT(buf && "db_builder_from_buffer: buf cannot be NULL");
1341
1342 struct db_builder_internal* builder = (struct db_builder_internal*)DB_MALLOC(sizeof(struct db_builder_internal));
1343 DB_ASSERT(builder && "db_builder_from_buffer: memory allocation failed");
1344
1345 // Make a copy of the buffer to preserve immutability
1346 builder->data = db_new_with_data(buf, db_size(buf));
1347 builder->refcount = DB_REFCOUNT_INIT(1);
1348 builder->capacity = db_capacity(buf);
1349
1350 return builder;
1351}
1352
1354 DB_ASSERT(builder && "db_builder_retain: builder cannot be NULL");
1355 DB_REFCOUNT_INCREMENT(&builder->refcount);
1356 return builder;
1357}
1358
1359void db_builder_release(db_builder* builder_ptr) {
1360 DB_ASSERT(builder_ptr && "db_builder_release: builder_ptr cannot be NULL");
1361 if (!*builder_ptr) return;
1362
1363 db_builder builder = *builder_ptr;
1364 *builder_ptr = NULL;
1365
1366 if (DB_REFCOUNT_DECREMENT(&builder->refcount) == 0) {
1367 // Reference count reached 0, free the builder and release buffer
1368 db_release(&builder->data);
1369 DB_FREE(builder);
1370 }
1371}
1372
1374 DB_ASSERT(builder_ptr && "db_builder_finish: builder_ptr cannot be NULL");
1375 DB_ASSERT(*builder_ptr && "db_builder_finish: builder cannot be NULL");
1376
1377 struct db_builder_internal* builder = *builder_ptr;
1378 db_buffer result = builder->data;
1379
1380 // Invalidate the builder - don't release the buffer, it's being returned
1381 builder->data = NULL;
1382 DB_FREE(builder);
1383 *builder_ptr = NULL;
1384
1385 return result;
1386}
1387
1388size_t db_builder_size(db_builder builder) {
1389 DB_ASSERT(builder && "db_builder_size: builder cannot be NULL");
1390 return db_size(builder->data);
1391}
1392
1393size_t db_builder_capacity(db_builder builder) {
1394 DB_ASSERT(builder && "db_builder_capacity: builder cannot be NULL");
1395 return builder->capacity;
1396}
1397
1398void db_builder_clear(db_builder builder) {
1399 DB_ASSERT(builder && "db_builder_clear: builder cannot be NULL");
1400
1401 // Always create a new empty buffer (since buffers are immutable)
1402 db_release(&builder->data);
1403 builder->data = db_new(builder->capacity);
1404}
1405
1406
1407
1408int db_builder_append_uint8(db_builder builder, uint8_t value) {
1409 DB_ASSERT(builder && "db_builder_append_uint8: builder cannot be NULL");
1410
1411 return db_internal_append(&builder->data, &builder->capacity, &value, 1);
1412}
1413
1414int db_builder_append_uint16_le(db_builder builder, uint16_t value) {
1415 DB_ASSERT(builder && "db_builder_append_uint16_le: builder cannot be NULL");
1416
1417 uint8_t bytes[2] = {
1418 (uint8_t)(value & 0xFF),
1419 (uint8_t)((value >> 8) & 0xFF)
1420 };
1421
1422 return db_internal_append(&builder->data, &builder->capacity, bytes, 2);
1423}
1424
1425int db_builder_append_uint16_be(db_builder builder, uint16_t value) {
1426 DB_ASSERT(builder && "db_builder_append_uint16_be: builder cannot be NULL");
1427
1428 uint8_t bytes[2] = {
1429 (uint8_t)((value >> 8) & 0xFF),
1430 (uint8_t)(value & 0xFF)
1431 };
1432
1433 return db_internal_append(&builder->data, &builder->capacity, bytes, 2);
1434}
1435
1436int db_builder_append_uint32_le(db_builder builder, uint32_t value) {
1437 DB_ASSERT(builder && "db_builder_append_uint32_le: builder cannot be NULL");
1438
1439 uint8_t bytes[4] = {
1440 (uint8_t)(value & 0xFF),
1441 (uint8_t)((value >> 8) & 0xFF),
1442 (uint8_t)((value >> 16) & 0xFF),
1443 (uint8_t)((value >> 24) & 0xFF)
1444 };
1445
1446 return db_internal_append(&builder->data, &builder->capacity, bytes, 4);
1447}
1448
1449int db_builder_append_uint32_be(db_builder builder, uint32_t value) {
1450 DB_ASSERT(builder && "db_builder_append_uint32_be: builder cannot be NULL");
1451
1452 uint8_t bytes[4] = {
1453 (uint8_t)((value >> 24) & 0xFF),
1454 (uint8_t)((value >> 16) & 0xFF),
1455 (uint8_t)((value >> 8) & 0xFF),
1456 (uint8_t)(value & 0xFF)
1457 };
1458
1459 return db_internal_append(&builder->data, &builder->capacity, bytes, 4);
1460}
1461
1462int db_builder_append_uint64_le(db_builder builder, uint64_t value) {
1463 DB_ASSERT(builder && "db_builder_append_uint64_le: builder cannot be NULL");
1464
1465 uint8_t bytes[8] = {
1466 (uint8_t)(value & 0xFF),
1467 (uint8_t)((value >> 8) & 0xFF),
1468 (uint8_t)((value >> 16) & 0xFF),
1469 (uint8_t)((value >> 24) & 0xFF),
1470 (uint8_t)((value >> 32) & 0xFF),
1471 (uint8_t)((value >> 40) & 0xFF),
1472 (uint8_t)((value >> 48) & 0xFF),
1473 (uint8_t)((value >> 56) & 0xFF)
1474 };
1475
1476 return db_internal_append(&builder->data, &builder->capacity, bytes, 8);
1477}
1478
1479int db_builder_append_uint64_be(db_builder builder, uint64_t value) {
1480 DB_ASSERT(builder && "db_builder_append_uint64_be: builder cannot be NULL");
1481
1482 uint8_t bytes[8] = {
1483 (uint8_t)((value >> 56) & 0xFF),
1484 (uint8_t)((value >> 48) & 0xFF),
1485 (uint8_t)((value >> 40) & 0xFF),
1486 (uint8_t)((value >> 32) & 0xFF),
1487 (uint8_t)((value >> 24) & 0xFF),
1488 (uint8_t)((value >> 16) & 0xFF),
1489 (uint8_t)((value >> 8) & 0xFF),
1490 (uint8_t)(value & 0xFF)
1491 };
1492
1493 return db_internal_append(&builder->data, &builder->capacity, bytes, 8);
1494}
1495
1496int db_builder_append(db_builder builder, const void* data, size_t size) {
1497 DB_ASSERT(builder && "db_builder_append: builder cannot be NULL");
1498 DB_ASSERT((data || size == 0) && "db_builder_append: data cannot be NULL when size > 0");
1499
1500 if (size == 0) return 0;
1501
1502 return db_internal_append(&builder->data, &builder->capacity, data, size);
1503}
1504
1505int db_builder_append_cstring(db_builder builder, const char* str) {
1506 DB_ASSERT(builder && "db_builder_append_cstring: builder cannot be NULL");
1507 DB_ASSERT(str && "db_builder_append_cstring: str cannot be NULL");
1508
1509 size_t len = strlen(str);
1510 return db_builder_append(builder, str, len);
1511}
1512
1514 DB_ASSERT(builder && "db_builder_append_buffer: builder cannot be NULL");
1515 DB_ASSERT(buf && "db_builder_append_buffer: buf cannot be NULL");
1516
1517 return db_builder_append(builder, buf, db_size(buf));
1518}
1519
1520// Reader implementation
1521
1523 DB_ASSERT(buf && "db_reader_new: buf cannot be NULL");
1524
1525 struct db_reader_internal* reader = (struct db_reader_internal*)DB_MALLOC(sizeof(struct db_reader_internal));
1526 DB_ASSERT(reader && "db_reader_new: memory allocation failed");
1527
1528 reader->refcount = DB_REFCOUNT_INIT(1);
1529 reader->buf = db_retain(buf); // Keep a reference to the buffer
1530 reader->position = 0;
1531
1532 return reader;
1533}
1534
1536 DB_ASSERT(reader && "db_reader_retain: reader cannot be NULL");
1537 DB_REFCOUNT_INCREMENT(&reader->refcount);
1538 return reader;
1539}
1540
1541void db_reader_release(db_reader* reader_ptr) {
1542 DB_ASSERT(reader_ptr && "db_reader_release: reader_ptr cannot be NULL");
1543 if (!*reader_ptr) return;
1544
1545 db_reader reader = *reader_ptr;
1546 *reader_ptr = NULL;
1547
1548 if (DB_REFCOUNT_DECREMENT(&reader->refcount) == 0) {
1549 // Reference count reached 0, free the reader and release buffer
1550 db_release(&reader->buf);
1551 DB_FREE(reader);
1552 }
1553}
1554
1555void db_reader_free(db_reader* reader_ptr) {
1556 // Legacy function - just call the new release function
1557 db_reader_release(reader_ptr);
1558}
1559
1560size_t db_reader_position(db_reader reader) {
1561 DB_ASSERT(reader && "reader cannot be NULL");
1562 return reader->position;
1563}
1564
1565size_t db_reader_remaining(db_reader reader) {
1566 DB_ASSERT(reader && "reader cannot be NULL");
1567 size_t buffer_size = db_meta(reader->buf)->size;
1568 return (reader->position < buffer_size) ? (buffer_size - reader->position) : 0;
1569}
1570
1571bool db_reader_can_read(db_reader reader, size_t bytes) {
1572 DB_ASSERT(reader && "reader cannot be NULL");
1573 return db_reader_remaining(reader) >= bytes;
1574}
1575
1576void db_reader_seek(db_reader reader, size_t position) {
1577 DB_ASSERT(reader && "reader cannot be NULL");
1578 size_t buffer_size = db_meta(reader->buf)->size;
1579 DB_ASSERT(position <= buffer_size && "db_reader_seek: cannot seek past buffer end");
1580 reader->position = position;
1581}
1582
1583uint8_t db_read_uint8(db_reader reader) {
1584 DB_ASSERT(reader && "reader cannot be NULL");
1585 DB_ASSERT(db_reader_can_read(reader, 1) && "db_read_uint8: insufficient data available");
1586
1587 uint8_t value = *(uint8_t*)(reader->buf + reader->position);
1588 reader->position += 1;
1589
1590 return value;
1591}
1592
1593uint16_t db_read_uint16_le(db_reader reader) {
1594 DB_ASSERT(reader && "reader cannot be NULL");
1595 DB_ASSERT(db_reader_can_read(reader, 2) && "insufficient data available");
1596
1597 const uint8_t* ptr = (const uint8_t*)(reader->buf + reader->position);
1598 uint16_t value = (uint16_t)ptr[0] | ((uint16_t)ptr[1] << 8);
1599 reader->position += 2;
1600
1601 return value;
1602}
1603
1604uint16_t db_read_uint16_be(db_reader reader) {
1605 DB_ASSERT(reader && "reader cannot be NULL");
1606 DB_ASSERT(db_reader_can_read(reader, 2) && "insufficient data available");
1607
1608 const uint8_t* ptr = (const uint8_t*)(reader->buf + reader->position);
1609 uint16_t value = ((uint16_t)ptr[0] << 8) | (uint16_t)ptr[1];
1610 reader->position += 2;
1611
1612 return value;
1613}
1614
1615uint32_t db_read_uint32_le(db_reader reader) {
1616 DB_ASSERT(reader && "reader cannot be NULL");
1617 DB_ASSERT(db_reader_can_read(reader, 4) && "insufficient data available");
1618
1619 const uint8_t* ptr = (const uint8_t*)(reader->buf + reader->position);
1620 uint32_t value = (uint32_t)ptr[0] |
1621 ((uint32_t)ptr[1] << 8) |
1622 ((uint32_t)ptr[2] << 16) |
1623 ((uint32_t)ptr[3] << 24);
1624 reader->position += 4;
1625
1626 return value;
1627}
1628
1629uint32_t db_read_uint32_be(db_reader reader) {
1630 DB_ASSERT(reader && "reader cannot be NULL");
1631 DB_ASSERT(db_reader_can_read(reader, 4) && "insufficient data available");
1632
1633 const uint8_t* ptr = (const uint8_t*)(reader->buf + reader->position);
1634 uint32_t value = ((uint32_t)ptr[0] << 24) |
1635 ((uint32_t)ptr[1] << 16) |
1636 ((uint32_t)ptr[2] << 8) |
1637 (uint32_t)ptr[3];
1638 reader->position += 4;
1639
1640 return value;
1641}
1642
1643uint64_t db_read_uint64_le(db_reader reader) {
1644 DB_ASSERT(reader && "reader cannot be NULL");
1645 DB_ASSERT(db_reader_can_read(reader, 8) && "insufficient data available");
1646
1647 const uint8_t* ptr = (const uint8_t*)(reader->buf + reader->position);
1648 uint64_t value = (uint64_t)ptr[0] |
1649 ((uint64_t)ptr[1] << 8) |
1650 ((uint64_t)ptr[2] << 16) |
1651 ((uint64_t)ptr[3] << 24) |
1652 ((uint64_t)ptr[4] << 32) |
1653 ((uint64_t)ptr[5] << 40) |
1654 ((uint64_t)ptr[6] << 48) |
1655 ((uint64_t)ptr[7] << 56);
1656 reader->position += 8;
1657
1658 return value;
1659}
1660
1661uint64_t db_read_uint64_be(db_reader reader) {
1662 DB_ASSERT(reader && "reader cannot be NULL");
1663 DB_ASSERT(db_reader_can_read(reader, 8) && "insufficient data available");
1664
1665 const uint8_t* ptr = (const uint8_t*)(reader->buf + reader->position);
1666 uint64_t value = ((uint64_t)ptr[0] << 56) |
1667 ((uint64_t)ptr[1] << 48) |
1668 ((uint64_t)ptr[2] << 40) |
1669 ((uint64_t)ptr[3] << 32) |
1670 ((uint64_t)ptr[4] << 24) |
1671 ((uint64_t)ptr[5] << 16) |
1672 ((uint64_t)ptr[6] << 8) |
1673 (uint64_t)ptr[7];
1674 reader->position += 8;
1675
1676 return value;
1677}
1678
1679void db_read_bytes(db_reader reader, void* data, size_t size) {
1680 DB_ASSERT(reader && "reader cannot be NULL");
1681 DB_ASSERT((data || size == 0) && "db_read_bytes: data cannot be NULL when size > 0");
1682 DB_ASSERT(db_reader_can_read(reader, size) && "db_read_bytes: insufficient data available");
1683
1684 if (size > 0) {
1685 memcpy(data, reader->buf + reader->position, size);
1686 reader->position += size;
1687 }
1688}
1689
1690#endif // DB_IMPLEMENTATION
1691
1692#endif // DYNAMIC_BUFFER_H
#define DB_REFCOUNT_LOAD(ptr)
Definition dynamic_buffer.h:139
#define DB_DEF
Definition dynamic_buffer.h:153
#define DB_REALLOC
Definition dynamic_buffer.h:111
#define DB_FREE
Definition dynamic_buffer.h:116
#define DB_REFCOUNT_INCREMENT(ptr)
Definition dynamic_buffer.h:140
int db_refcount_t
Definition dynamic_buffer.h:137
#define DB_ASSERT
Definition dynamic_buffer.h:121
#define DB_MALLOC
Definition dynamic_buffer.h:106
char * db_buffer
Buffer handle - points directly to buffer data.
Definition dynamic_buffer.h:173
#define DB_REFCOUNT_DECREMENT(ptr)
Definition dynamic_buffer.h:141
#define DB_REFCOUNT_INIT(n)
Definition dynamic_buffer.h:138
DB_DEF int db_refcount(db_buffer buf)
Get current reference count.
DB_DEF size_t db_capacity(db_buffer buf)
Get current capacity of buffer in bytes.
DB_DEF bool db_is_empty(db_buffer buf)
Check if buffer is empty.
DB_DEF size_t db_size(db_buffer buf)
Get current size of buffer in bytes.
DB_DEF int db_builder_append_uint32_le(db_builder builder, uint32_t value)
Write uint32 value in little-endian format.
DB_DEF int db_builder_append_uint64_be(db_builder builder, uint64_t value)
Write uint64 value in big-endian format.
DB_DEF db_buffer db_builder_finish(db_builder *builder_ptr)
Finalize builder and return the constructed buffer.
DB_DEF int db_builder_append_uint16_be(db_builder builder, uint16_t value)
Write uint16 value in big-endian format.
DB_DEF size_t db_builder_size(db_builder builder)
Get current write position in builder.
DB_DEF db_builder db_builder_new(size_t initial_capacity)
Create a new buffer builder.
DB_DEF int db_builder_append_uint8(db_builder builder, uint8_t value)
Write uint8 value.
DB_DEF void db_builder_clear(db_builder builder)
Clear builder contents.
struct db_builder_internal * db_builder
Opaque builder handle for constructing buffers efficiently.
Definition dynamic_buffer.h:462
DB_DEF db_builder db_builder_from_buffer(db_buffer buf)
Create builder from existing buffer (continues at end)
DB_DEF int db_builder_append(db_builder builder, const void *data, size_t size)
Write raw bytes.
DB_DEF int db_builder_append_buffer(db_builder builder, db_buffer buf)
Append buffer contents.
DB_DEF void db_builder_release(db_builder *builder_ptr)
Decrease builder reference count and potentially free builder.
DB_DEF int db_builder_append_uint16_le(db_builder builder, uint16_t value)
Write uint16 value in little-endian format.
DB_DEF db_builder db_builder_retain(db_builder builder)
Increase builder reference count (share ownership)
DB_DEF int db_builder_append_uint64_le(db_builder builder, uint64_t value)
Write uint64 value in little-endian format.
DB_DEF int db_builder_append_uint32_be(db_builder builder, uint32_t value)
Write uint32 value in big-endian format.
DB_DEF size_t db_builder_capacity(db_builder builder)
Get current capacity of builder.
DB_DEF int db_builder_append_cstring(db_builder builder, const char *str)
Write null-terminated string (without null terminator)
DB_DEF int db_compare(db_buffer buf1, db_buffer buf2)
Compare buffer contents lexicographically.
DB_DEF bool db_equals(db_buffer buf1, db_buffer buf2)
Compare two buffers for equality.
DB_DEF db_buffer db_concat(db_buffer buf1, db_buffer buf2)
Concatenate two buffers into a new buffer.
DB_DEF db_buffer db_concat_many(db_buffer *buffers, size_t count)
Concatenate multiple buffers into a new buffer.
DB_DEF ssize_t db_read_fd(db_buffer *buf_ptr, int fd, size_t max_bytes)
Read data from file descriptor into buffer.
DB_DEF db_buffer db_read_file(const char *filename)
Read entire file into a new buffer.
DB_DEF ssize_t db_write_fd(db_buffer buf, int fd)
Write buffer contents to file descriptor.
DB_DEF bool db_write_file(db_buffer buf, const char *filename)
Write buffer contents to file.
DB_DEF db_buffer db_retain(db_buffer buf)
Increase reference count (share ownership)
DB_DEF db_buffer db_new_with_data(const void *data, size_t size)
Create a new buffer initialized with data (copies the data)
DB_DEF db_buffer db_new_from_owned_data(void *data, size_t size, size_t capacity)
Create a new buffer by copying existing data.
DB_DEF void db_release(db_buffer *buf_ptr)
Decrease reference count and potentially free buffer.
DB_DEF db_buffer db_new(size_t capacity)
Create a new empty buffer with specified capacity.
DB_DEF db_buffer db_append(db_buffer buf, const void *data, size_t size)
Create new buffer with data appended.
DB_DEF uint64_t db_read_uint64_le(db_reader reader)
Read uint64 value in little-endian format.
DB_DEF size_t db_reader_remaining(db_reader reader)
Get number of bytes remaining.
DB_DEF db_reader db_reader_new(db_buffer buf)
Create a new buffer reader.
DB_DEF uint64_t db_read_uint64_be(db_reader reader)
Read uint64 value in big-endian format.
DB_DEF void db_read_bytes(db_reader reader, void *data, size_t size)
Read raw bytes.
DB_DEF void db_reader_release(db_reader *reader_ptr)
Decrease reader reference count and potentially free reader.
struct db_reader_internal * db_reader
Opaque reader handle for parsing buffers.
Definition dynamic_buffer.h:621
DB_DEF size_t db_reader_position(db_reader reader)
Get current read position.
DB_DEF uint16_t db_read_uint16_le(db_reader reader)
Read uint16 value in little-endian format.
DB_DEF uint8_t db_read_uint8(db_reader reader)
Read uint8 value.
DB_DEF void db_reader_free(db_reader *reader_ptr)
Free reader resources (legacy name, use db_reader_release instead)
DB_DEF db_reader db_reader_retain(db_reader reader)
Increase reader reference count (share ownership)
DB_DEF uint16_t db_read_uint16_be(db_reader reader)
Read uint16 value in big-endian format.
DB_DEF void db_reader_seek(db_reader reader, size_t position)
Seek to specific position.
DB_DEF uint32_t db_read_uint32_le(db_reader reader)
Read uint32 value in little-endian format.
DB_DEF bool db_reader_can_read(db_reader reader, size_t bytes)
Check if reader can read specified number of bytes.
DB_DEF uint32_t db_read_uint32_be(db_reader reader)
Read uint32 value in big-endian format.
DB_DEF db_buffer db_slice(db_buffer buf, size_t offset, size_t length)
Create a slice of the buffer (creates independent copy)
DB_DEF db_buffer db_slice_from(db_buffer buf, size_t offset)
Create a slice from offset to end of buffer.
DB_DEF db_buffer db_slice_to(db_buffer buf, size_t length)
Create a slice from start to specified length.
DB_DEF db_buffer db_to_hex(db_buffer buf, bool uppercase)
Create a hexadecimal representation of buffer contents.
DB_DEF db_buffer db_from_hex(const char *hex_string, size_t length)
Create buffer from hexadecimal string.
DB_DEF void db_debug_print(db_buffer buf, const char *label)
Print buffer information for debugging.