bytebuf.h (3251B)
1 #ifndef KIT_OBJ_BYTEBUF_H 2 #define KIT_OBJ_BYTEBUF_H 3 4 /* Heap-backed growable byte buffer — the canonical staging buffer for object 5 * writers that assemble a blob (a linkedit table, a .dynstr, a chained-fixups 6 * stream) before handing it to a Writer. The ELF and Mach-O linkers both grew 7 * their own near-identical copies; this is the shared one. 8 * 9 * Offsets returned by the append family are byte offsets into the buffer at 10 * the time of the call, which callers patch up later via the data/len fields. 11 * Allocation failure is fatal inside VEC_GROW (it panics), so callers do not 12 * need to check append return values for OOM. */ 13 14 #include <string.h> 15 16 #include "core/bytes.h" 17 #include "core/core.h" 18 #include "core/util.h" 19 #include "core/vec.h" 20 21 typedef struct ObjByteBuf { 22 Heap* heap; 23 u8* data; 24 u32 len; 25 u32 cap; 26 } ObjByteBuf; 27 28 static inline void objbb_init(ObjByteBuf* b, Heap* h) { 29 b->heap = h; 30 b->data = NULL; 31 b->len = 0; 32 b->cap = 0; 33 } 34 35 static inline void objbb_fini(ObjByteBuf* b) { 36 if (b->data) b->heap->free(b->heap, b->data, b->cap); 37 b->data = NULL; 38 b->cap = b->len = 0; 39 } 40 41 static inline void objbb_reserve(ObjByteBuf* b, u32 need) { 42 if (need <= b->cap) return; 43 (void)VEC_GROW(b->heap, b->data, b->cap, need); 44 } 45 46 /* Zero-fill up to the next `a`-aligned length; returns the new (aligned) 47 * length so callers can record an aligned offset in one step. */ 48 static inline u32 objbb_align(ObjByteBuf* b, u32 a) { 49 u32 n = (u32)ALIGN_UP((u64)b->len, (u64)a); 50 if (n > b->len) { 51 objbb_reserve(b, n); 52 memset(b->data + b->len, 0, n - b->len); 53 b->len = n; 54 } 55 return b->len; 56 } 57 58 static inline u32 objbb_append(ObjByteBuf* b, const void* src, u32 n) { 59 u32 off = b->len; 60 objbb_reserve(b, b->len + n); 61 if (n) memcpy(b->data + b->len, src, n); 62 b->len += n; 63 return off; 64 } 65 66 static inline u32 objbb_u8(ObjByteBuf* b, u8 v) { 67 return objbb_append(b, &v, 1); 68 } 69 70 static inline u32 objbb_u16(ObjByteBuf* b, u16 v) { 71 u8 t[2]; 72 wr_u16_le(t, v); 73 return objbb_append(b, t, 2); 74 } 75 76 static inline u32 objbb_u32(ObjByteBuf* b, u32 v) { 77 u8 t[4]; 78 wr_u32_le(t, v); 79 return objbb_append(b, t, 4); 80 } 81 82 static inline u32 objbb_u64(ObjByteBuf* b, u64 v) { 83 u8 t[8]; 84 wr_u64_le(t, v); 85 return objbb_append(b, t, 8); 86 } 87 88 /* Append n bytes plus a NUL terminator (no dedup). */ 89 static inline u32 objbb_str(ObjByteBuf* b, const char* s, u32 n) { 90 u32 off = b->len; 91 objbb_reserve(b, b->len + n + 1u); 92 if (n) memcpy(b->data + b->len, s, n); 93 b->data[b->len + n] = 0; 94 b->len += n + 1u; 95 return off; 96 } 97 98 /* Append a NUL-terminated string with linear dedup over what we've appended 99 * so far. Strtabs (.dynstr) are small, so the scan stays cheap. An empty 100 * string maps to offset 0 (the leading NUL the caller is expected to have 101 * placed). */ 102 static inline u32 objbb_append_str(ObjByteBuf* b, const char* s, u32 n) { 103 if (n == 0) return 0; 104 if (b->len > n) { 105 u32 i; 106 for (i = 0; i + n < b->len; ++i) { 107 if (b->data[i + n] == 0 && memcmp(b->data + i, s, n) == 0) return i; 108 } 109 } 110 return objbb_str(b, s, n); 111 } 112 113 /* objbb_append_str for a NUL-terminated C string. */ 114 static inline u32 objbb_append_cstr(ObjByteBuf* b, const char* s) { 115 return objbb_append_str(b, s, (u32)strlen(s)); 116 } 117 118 #endif