Skip to content

Commit 1b02bfb

Browse files
parsers + file: Buf migration, _Generic type safety, encapsulation cleanup
Big refactor that lands several related cleanups in one commit: 1. Parser structs migrate from `Allocator * + u8 *data + size data_size` to a single `Buf data` field. ElfFile, MachoFile, PeFile, PdbFile all now own a Buf and free it through `BufDeinit`. The L-form `*_open_from_memory` signature drops to `(<Foo>File *out, Buf *in)`: parser snapshots the Buf and MemSets the caller's view to zero, so any post-call use is a fresh empty Buf instead of a stale alias. No data copy in the L-form (only the 4 struct fields move). The R-form `_copy` still does a MemCopy through a fresh Buf, no change to its contract. 2. `FileRead(File *, Buf *)` overload added next to the existing Str form. Five parser callers (Elf/MachO/Pe/Pdb/Http) slurp into Buf now -- semantically correct for binary inputs, and the cast + manual-disassembly dance is gone. `BufFmt` declarations moved out of Buf.h into Io.h (they're an I/O concern; Buf.h was pulling Io.h transitively which formed a File.h <-> Buf.h cycle). 3. `_Generic` type safety: 14 path-API macros (FileOpen, FileGetSize, File/DirRemove, DirCreate/CreateAll/RemoveAll, DirGetContents, and the four parser `_Open` overloads) previously had a `default:` arm that silently cast any input to `const char *` -- passing an `int *` or struct pointer compiled clean. Replaced with explicit `char *:` and `const char *:` associations so unsupported types now fail to compile (no matching `_Generic` association). `IOFMT` and `ARG_TARGET` keep their `default:` arms intentionally -- both route to runtime LOG_FATAL sentinels (`UnsupportedType` / `ARG_KIND_INVALID`), documented in their headers. 4. `BufAllocator(b)` accessor macro added; ~60 sites of direct `b->length` / `b->data` / `b->allocator` access in parsers, Http, KvConfig, MachoCache replaced with `BufLength` / `BufData` / `BufAllocator` / `StrLen` / `StrBegin` / `StrCharAt`. Container fields are accessed only inside the container's own `.c`; outside code goes through the public macro surface. 5. `FileOpenTemp` drops its `prefix` argument -- the name is now just `Prng64()` rendered as 16-hex, file lands in CWD. Removes the `_Generic`-on-path complexity entirely (no string-shaped input to dispatch on). Iter test files include Buf.h directly now that Buf.h doesn't transitively pull it through Iter.h's chain. Verified: Linux sanitised: 104/104 OK Mac sanitised: 99/99 OK in 40s wall Mac freestanding: 99/99 OK in 19s wall Mac libc-diet: beam + resolve dyld-only (no libSystem refs)
1 parent bcd6f72 commit 1b02bfb

22 files changed

Lines changed: 451 additions & 477 deletions

File tree

Include/Misra/Parsers/Elf.h

Lines changed: 27 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#define MISRA_PARSERS_ELF_H
1919

2020
#include <Misra/Std/Allocator.h>
21+
#include <Misra/Std/Container/Buf.h>
2122
#include <Misra/Std/Container/Str.h>
2223
#include <Misra/Std/Container/Vec.h>
2324
#include <Misra/Types.h>
@@ -174,9 +175,7 @@ typedef Vec(ElfSymbol) ElfSymbols;
174175
/// (validated by the resolver before use).
175176
///
176177
typedef struct ElfFile {
177-
Allocator *allocator;
178-
u8 *data;
179-
size data_size;
178+
Buf data;
180179
ElfHeader header;
181180
ElfSections sections;
182181
ElfSymbols symbols;
@@ -209,56 +208,49 @@ bool elf_file_open(ElfFile *out, const char *path, Allocator *alloc);
209208
(path), \
210209
Str *: elf_file_open((out), ((Str *)(path))->data, MisraScope), \
211210
const Str *: elf_file_open((out), ((const Str *)(path))->data, MisraScope), \
212-
default: elf_file_open((out), (const char *)(path), MisraScope) \
211+
char *: elf_file_open((out), (const char *)(path), MisraScope), \
212+
const char *: elf_file_open((out), (const char *)(path), MisraScope) \
213213
)
214214
#define ElfFileOpen_3(out, path, alloc) \
215215
_Generic( \
216216
(path), \
217217
Str *: elf_file_open((out), ((Str *)(path))->data, ALLOCATOR_OF(alloc)), \
218218
const Str *: elf_file_open((out), ((const Str *)(path))->data, ALLOCATOR_OF(alloc)), \
219-
default: elf_file_open((out), (const char *)(path), ALLOCATOR_OF(alloc)) \
219+
char *: elf_file_open((out), (const char *)(path), ALLOCATOR_OF(alloc)), \
220+
const char *: elf_file_open((out), (const char *)(path), ALLOCATOR_OF(alloc)) \
220221
)
221222

222223
///
223224
/// Parse an ELF object from an in-memory byte range -- **L-value /
224225
/// ownership-transfer form** (mirrors `VecInsertL`).
225226
///
226-
/// The `data` parameter is `u8 **` -- a pointer to the caller's
227-
/// pointer -- precisely because ownership is moving. After the call
228-
/// returns, `*data == NULL`: the caller's variable has been zeroed
229-
/// to make use-after-transfer impossible. The parser holds the
230-
/// original pointer internally and frees it through `alloc` on
231-
/// `ElfFileDeinit`. This invariant is the same on success and failure
232-
/// -- on parse failure the parser still frees the bytes and still
233-
/// nulls `*data`.
227+
/// Takes the caller's `Buf` by pointer. The parser snapshots the Buf
228+
/// internally and zeroes the caller's `*in` so any post-call use sees
229+
/// an empty Buf instead of a stale alias. The parser then owns the
230+
/// bytes and the buffer's allocator; both are released by
231+
/// `ElfFileDeinit`. The zero-on-take invariant holds on both success
232+
/// and failure -- on parse failure the parser still consumed the Buf,
233+
/// just frees it through the carried allocator before returning.
234234
///
235235
/// USAGE:
236-
/// u8 *buf = my_buffer; // buffer allocated through `alloc`
237-
/// ElfFileOpenFromMemory(&elf, &buf, n, &alloc);
238-
/// // buf == NULL here -- compiler can't enforce, but a follow-up
239-
/// // dereference will SIGSEGV instead of using freed memory.
236+
/// Buf buf = BufInit(&alloc);
237+
/// FileRead(&f, &buf);
238+
/// ElfFileOpenFromMemory(&elf, &buf);
239+
/// // buf is now {NULL, 0, 0, NULL} -- safe to drop on stack.
240240
///
241-
/// out[out] : Populated on success.
242-
/// data[in,out] : Address of the caller's `u8 *` holding the bytes.
243-
/// `*data` MUST be non-NULL and MUST have been
244-
/// produced by `alloc`. After the call, `*data` is
245-
/// NULL (success or failure).
246-
/// data_size[in] : Length of the buffer pointed to by `*data`.
247-
/// alloc[in] : Allocator that produced `*data` and that will back
248-
/// the section / symbol vectors.
249-
///
250-
/// SUCCESS : Returns true; `out` owns the bytes; `*data == NULL`.
251-
/// FAILURE : Returns false; the bytes have been freed through `alloc`;
252-
/// `*data == NULL`; `out` is left zeroed.
241+
/// out[out] : Populated on success.
242+
/// in[in,out] : Pointer to the caller's `Buf`. `in->data` must be
243+
/// non-NULL and `in->allocator` must be set. After the
244+
/// call, `*in` is zeroed (success or failure).
245+
///
246+
/// SUCCESS : Returns true; `out` owns the bytes; `*in` is zeroed.
247+
/// FAILURE : Returns false; the bytes have been freed; `*in` is
248+
/// zeroed; `out` is left zeroed.
253249
///
254250
/// TAGS: Parser, ELF, Memory, Ownership
255251
///
256-
bool elf_file_open_from_memory(ElfFile *out, u8 **data, size data_size, Allocator *alloc);
257-
#define ElfFileOpenFromMemory(...) MISRA_OVERLOAD(ElfFileOpenFromMemory, __VA_ARGS__)
258-
#define ElfFileOpenFromMemory_3(out, dataref, data_size) \
259-
elf_file_open_from_memory((out), (dataref), (data_size), MisraScope)
260-
#define ElfFileOpenFromMemory_4(out, dataref, data_size, alloc) \
261-
elf_file_open_from_memory((out), (dataref), (data_size), ALLOCATOR_OF(alloc))
252+
bool elf_file_open_from_memory(ElfFile *out, Buf *in);
253+
#define ElfFileOpenFromMemory(out, in) elf_file_open_from_memory((out), (in))
262254

263255
///
264256
/// Parse an ELF object from an in-memory byte range -- **R-value /

Include/Misra/Parsers/MachO.h

Lines changed: 20 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
#define MISRA_PARSERS_MACHO_H
2424

2525
#include <Misra/Std/Allocator.h>
26+
#include <Misra/Std/Container/Buf.h>
2627
#include <Misra/Std/Container/Vec.h>
2728
#include <Misra/Types.h>
2829

@@ -99,9 +100,7 @@ typedef Vec(MachoSymbol) MachoSymbols;
99100
/// - symbols : Entries from LC_SYMTAB; may be empty if stripped.
100101
///
101102
typedef struct MachoFile {
102-
Allocator *allocator;
103-
u8 *data;
104-
size data_size;
103+
Buf data;
105104
u32 cputype;
106105
MachoFileType filetype;
107106
u8 uuid[16];
@@ -128,40 +127,41 @@ bool macho_file_open(MachoFile *out, const char *path, Allocator *alloc);
128127
(path), \
129128
Str *: macho_file_open((out), ((Str *)(path))->data, MisraScope), \
130129
const Str *: macho_file_open((out), ((const Str *)(path))->data, MisraScope), \
131-
default: macho_file_open((out), (const char *)(path), MisraScope) \
130+
char *: macho_file_open((out), (const char *)(path), MisraScope), \
131+
const char *: macho_file_open((out), (const char *)(path), MisraScope) \
132132
)
133133
#define MachoFileOpen_3(out, path, alloc) \
134134
_Generic( \
135135
(path), \
136136
Str *: macho_file_open((out), ((Str *)(path))->data, ALLOCATOR_OF(alloc)), \
137137
const Str *: macho_file_open((out), ((const Str *)(path))->data, ALLOCATOR_OF(alloc)), \
138-
default: macho_file_open((out), (const char *)(path), ALLOCATOR_OF(alloc)) \
138+
char *: macho_file_open((out), (const char *)(path), ALLOCATOR_OF(alloc)), \
139+
const char *: macho_file_open((out), (const char *)(path), ALLOCATOR_OF(alloc)) \
139140
)
140141

141142
///
142143
/// Parse a Mach-O image from an in-memory byte range -- **L-value /
143144
/// ownership-transfer form** (mirrors `VecInsertL`).
144145
///
145-
/// `data` is `u8 **`: ownership is moving from caller to parser. On
146-
/// entry `*data` is the caller's buffer (allocated through `alloc`);
147-
/// on exit (success OR failure) `*data == NULL`. Calling code:
146+
/// Takes the caller's `Buf` by pointer. The parser snapshots the Buf
147+
/// and zeroes the caller's `*in` so any post-call use is an empty Buf
148+
/// rather than a stale alias. Allocator comes from the Buf. The
149+
/// zero-on-take invariant holds on success and failure.
148150
///
149-
/// u8 *buf = my_buffer;
150-
/// MachoFileOpenFromMemory(&m, &buf, n, &alloc);
151-
/// // buf == NULL afterwards.
151+
/// USAGE:
152+
/// Buf buf = BufInit(&alloc);
153+
/// FileRead(&f, &buf);
154+
/// MachoFileOpenFromMemory(&m, &buf);
155+
/// // buf is now zeroed.
152156
///
153-
/// SUCCESS : Returns true; `out` owns the bytes; `*data == NULL`.
154-
/// FAILURE : Returns false; the bytes have been freed through `alloc`;
155-
/// `*data == NULL`; `out` is left zeroed.
157+
/// SUCCESS : Returns true; `out` owns the bytes; `*in` is zeroed.
158+
/// FAILURE : Returns false; the bytes have been freed; `*in` is zeroed;
159+
/// `out` is left zeroed.
156160
///
157161
/// TAGS: Parser, MachO, Memory, Ownership
158162
///
159-
bool macho_file_open_from_memory(MachoFile *out, u8 **data, size data_size, Allocator *alloc);
160-
#define MachoFileOpenFromMemory(...) MISRA_OVERLOAD(MachoFileOpenFromMemory, __VA_ARGS__)
161-
#define MachoFileOpenFromMemory_3(out, dataref, data_size) \
162-
macho_file_open_from_memory((out), (dataref), (data_size), MisraScope)
163-
#define MachoFileOpenFromMemory_4(out, dataref, data_size, alloc) \
164-
macho_file_open_from_memory((out), (dataref), (data_size), ALLOCATOR_OF(alloc))
163+
bool macho_file_open_from_memory(MachoFile *out, Buf *in);
164+
#define MachoFileOpenFromMemory(out, in) macho_file_open_from_memory((out), (in))
165165

166166
///
167167
/// Parse a Mach-O image from an in-memory byte range -- **R-value /

Include/Misra/Parsers/Pdb.h

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
#define MISRA_PARSERS_PDB_H
2727

2828
#include <Misra/Std/Allocator.h>
29+
#include <Misra/Std/Container/Buf.h>
2930
#include <Misra/Std/Container/Vec.h>
3031
#include <Misra/Types.h>
3132

@@ -81,9 +82,7 @@ typedef struct PdbInfo {
8182
/// `PdbFileOpen[FromMemory]`.
8283
///
8384
typedef struct PdbFile {
84-
Allocator *allocator;
85-
u8 *data;
86-
size data_size;
85+
Buf data;
8786
u32 block_size;
8887
u32 num_streams;
8988
PdbInfo info;
@@ -123,14 +122,16 @@ bool pdb_file_open(PdbFile *out, const char *path, Allocator *alloc);
123122
(path), \
124123
Str *: pdb_file_open((out), ((Str *)(path))->data, MisraScope), \
125124
const Str *: pdb_file_open((out), ((const Str *)(path))->data, MisraScope), \
126-
default: pdb_file_open((out), (const char *)(path), MisraScope) \
125+
char *: pdb_file_open((out), (const char *)(path), MisraScope), \
126+
const char *: pdb_file_open((out), (const char *)(path), MisraScope) \
127127
)
128128
#define PdbFileOpen_3(out, path, alloc) \
129129
_Generic( \
130130
(path), \
131131
Str *: pdb_file_open((out), ((Str *)(path))->data, ALLOCATOR_OF(alloc)), \
132132
const Str *: pdb_file_open((out), ((const Str *)(path))->data, ALLOCATOR_OF(alloc)), \
133-
default: pdb_file_open((out), (const char *)(path), ALLOCATOR_OF(alloc)) \
133+
char *: pdb_file_open((out), (const char *)(path), ALLOCATOR_OF(alloc)), \
134+
const char *: pdb_file_open((out), (const char *)(path), ALLOCATOR_OF(alloc)) \
134135
)
135136

136137
///
@@ -151,12 +152,8 @@ bool pdb_file_open(PdbFile *out, const char *path, Allocator *alloc);
151152
///
152153
/// TAGS: Parser, PDB, Memory, Ownership
153154
///
154-
bool pdb_file_open_from_memory(PdbFile *out, u8 **data, size data_size, Allocator *alloc);
155-
#define PdbFileOpenFromMemory(...) MISRA_OVERLOAD(PdbFileOpenFromMemory, __VA_ARGS__)
156-
#define PdbFileOpenFromMemory_3(out, dataref, data_size) \
157-
pdb_file_open_from_memory((out), (dataref), (data_size), MisraScope)
158-
#define PdbFileOpenFromMemory_4(out, dataref, data_size, alloc) \
159-
pdb_file_open_from_memory((out), (dataref), (data_size), ALLOCATOR_OF(alloc))
155+
bool pdb_file_open_from_memory(PdbFile *out, Buf *in);
156+
#define PdbFileOpenFromMemory(out, in) pdb_file_open_from_memory((out), (in))
160157

161158
///
162159
/// Open and parse a PDB from an in-memory byte range -- **R-value /

Include/Misra/Parsers/Pe.h

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#define MISRA_PARSERS_PE_H
1818

1919
#include <Misra/Std/Allocator.h>
20+
#include <Misra/Std/Container/Buf.h>
2021
#include <Misra/Std/Container/Vec.h>
2122
#include <Misra/Types.h>
2223

@@ -94,9 +95,7 @@ typedef struct PeCodeViewInfo {
9495
/// - codeview : CodeView debug record, if present.
9596
///
9697
typedef struct PeFile {
97-
Allocator *allocator;
98-
u8 *data;
99-
size data_size;
98+
Buf data;
10099
PeMachine machine;
101100
bool is_pe32_plus;
102101
u64 image_base;
@@ -125,14 +124,16 @@ bool pe_file_open(PeFile *out, const char *path, Allocator *alloc);
125124
(path), \
126125
Str *: pe_file_open((out), ((Str *)(path))->data, MisraScope), \
127126
const Str *: pe_file_open((out), ((const Str *)(path))->data, MisraScope), \
128-
default: pe_file_open((out), (const char *)(path), MisraScope) \
127+
char *: pe_file_open((out), (const char *)(path), MisraScope), \
128+
const char *: pe_file_open((out), (const char *)(path), MisraScope) \
129129
)
130130
#define PeFileOpen_3(out, path, alloc) \
131131
_Generic( \
132132
(path), \
133133
Str *: pe_file_open((out), ((Str *)(path))->data, ALLOCATOR_OF(alloc)), \
134134
const Str *: pe_file_open((out), ((const Str *)(path))->data, ALLOCATOR_OF(alloc)), \
135-
default: pe_file_open((out), (const char *)(path), ALLOCATOR_OF(alloc)) \
135+
char *: pe_file_open((out), (const char *)(path), ALLOCATOR_OF(alloc)), \
136+
const char *: pe_file_open((out), (const char *)(path), ALLOCATOR_OF(alloc)) \
136137
)
137138

138139
///
@@ -153,12 +154,8 @@ bool pe_file_open(PeFile *out, const char *path, Allocator *alloc);
153154
///
154155
/// TAGS: Parser, PE, Memory, Ownership
155156
///
156-
bool pe_file_open_from_memory(PeFile *out, u8 **data, size data_size, Allocator *alloc);
157-
#define PeFileOpenFromMemory(...) MISRA_OVERLOAD(PeFileOpenFromMemory, __VA_ARGS__)
158-
#define PeFileOpenFromMemory_3(out, dataref, data_size) \
159-
pe_file_open_from_memory((out), (dataref), (data_size), MisraScope)
160-
#define PeFileOpenFromMemory_4(out, dataref, data_size, alloc) \
161-
pe_file_open_from_memory((out), (dataref), (data_size), ALLOCATOR_OF(alloc))
157+
bool pe_file_open_from_memory(PeFile *out, Buf *in);
158+
#define PeFileOpenFromMemory(out, in) pe_file_open_from_memory((out), (in))
162159

163160
///
164161
/// Parse a PE image from an in-memory byte range -- **R-value /

0 commit comments

Comments
 (0)