authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2020-12-09 11:01:51+01:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2020-12-09 20:36:58+01:00
loga28340405392b4a0a687e668406a067be1ae5e3c
tree6cbe7cf493d3a33459ae65de815b92290b6fed7f
parenta579f8ae8d6009d95ef22879bc725a233f838d6f

macho: split writing Trie into finalize and const write


2 files changed, 234 insertions(+), 204 deletions(-)

src/link/MachO.zig+5-1
...@@ -1810,8 +1810,12 @@ fn writeExportTrie(self: *MachO) !void {...@@ -1810,8 +1810,12 @@ fn writeExportTrie(self: *MachO) !void {
1810 });1810 });
1811 }1811 }
18121812
1813 var buffer = try trie.writeULEB128Mem();1813 try trie.finalize();
1814 var buffer = try self.base.allocator.alloc(u8, trie.size);
1814 defer self.base.allocator.free(buffer);1815 defer self.base.allocator.free(buffer);
1816 var stream = std.io.fixedBufferStream(buffer);
1817 const nwritten = try trie.write(stream.writer());
1818 assert(nwritten == trie.size);
18151819
1816 const dyld_info = &self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly;1820 const dyld_info = &self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly;
1817 const export_size = @intCast(u32, mem.alignForward(buffer.len, @sizeOf(u64)));1821 const export_size = @intCast(u32, mem.alignForward(buffer.len, @sizeOf(u64)));
src/link/MachO/Trie.zig+229-203
...@@ -51,7 +51,7 @@ pub const Edge = struct {...@@ -51,7 +51,7 @@ pub const Edge = struct {
51 label: []u8,51 label: []u8,
5252
53 fn deinit(self: *Edge, allocator: *Allocator) void {53 fn deinit(self: *Edge, allocator: *Allocator) void {
54 self.to.deinit();54 self.to.deinit(allocator);
55 allocator.destroy(self.to);55 allocator.destroy(self.to);
56 allocator.free(self.label);56 allocator.free(self.label);
57 self.from = undefined;57 self.from = undefined;
...@@ -62,6 +62,7 @@ pub const Edge = struct {...@@ -62,6 +62,7 @@ pub const Edge = struct {
6262
63pub const Node = struct {63pub const Node = struct {
64 base: *Trie,64 base: *Trie,
65
65 /// Terminal info associated with this node.66 /// Terminal info associated with this node.
66 /// If this node is not a terminal node, info is null.67 /// If this node is not a terminal node, info is null.
67 terminal_info: ?struct {68 terminal_info: ?struct {
...@@ -70,82 +71,93 @@ pub const Node = struct {...@@ -70,82 +71,93 @@ pub const Node = struct {
70 /// VM address offset wrt to the section this symbol is defined against.71 /// VM address offset wrt to the section this symbol is defined against.
71 vmaddr_offset: u64,72 vmaddr_offset: u64,
72 } = null,73 } = null,
74
73 /// Offset of this node in the trie output byte stream.75 /// Offset of this node in the trie output byte stream.
74 trie_offset: ?usize = null,76 trie_offset: ?usize = null,
77
75 /// List of all edges originating from this node.78 /// List of all edges originating from this node.
76 edges: std.ArrayListUnmanaged(Edge) = .{},79 edges: std.ArrayListUnmanaged(Edge) = .{},
7780
78 fn deinit(self: *Node) void {81 node_dirty: bool = true,
82
83 fn deinit(self: *Node, allocator: *Allocator) void {
79 for (self.edges.items) |*edge| {84 for (self.edges.items) |*edge| {
80 edge.deinit(self.base.allocator);85 edge.deinit(allocator);
81 }86 }
82 self.edges.deinit(self.base.allocator);87 self.edges.deinit(allocator);
83 }88 }
8489
85 /// Inserts a new node starting from `self`.90 /// Inserts a new node starting from `self`.
86 fn put(self: *Node, label: []const u8) !*Node {91 fn put(self: *Node, allocator: *Allocator, label: []const u8) !*Node {
87 // Check for match with edges from this node.92 // Check for match with edges from this node.
88 for (self.edges.items) |*edge| {93 for (self.edges.items) |*edge| {
89 const match = mem.indexOfDiff(u8, edge.label, label) orelse return edge.to;94 const match = mem.indexOfDiff(u8, edge.label, label) orelse return edge.to;
90 if (match == 0) continue;95 if (match == 0) continue;
91 if (match == edge.label.len) return edge.to.put(label[match..]);96 if (match == edge.label.len) return edge.to.put(allocator, label[match..]);
9297
93 // Found a match, need to splice up nodes.98 // Found a match, need to splice up nodes.
94 // From: A -> B99 // From: A -> B
95 // To: A -> C -> B100 // To: A -> C -> B
96 const mid = try self.base.allocator.create(Node);101 const mid = try allocator.create(Node);
97 mid.* = .{ .base = self.base };102 mid.* = .{ .base = self.base };
98 var to_label = try self.base.allocator.dupe(u8, edge.label[match..]);103 var to_label = try allocator.dupe(u8, edge.label[match..]);
99 self.base.allocator.free(edge.label);104 allocator.free(edge.label);
100 const to_node = edge.to;105 const to_node = edge.to;
101 edge.to = mid;106 edge.to = mid;
102 edge.label = try self.base.allocator.dupe(u8, label[0..match]);107 edge.label = try allocator.dupe(u8, label[0..match]);
103 self.base.node_count += 1;108 self.base.node_count += 1;
104109
105 try mid.edges.append(self.base.allocator, .{110 try mid.edges.append(allocator, .{
106 .from = mid,111 .from = mid,
107 .to = to_node,112 .to = to_node,
108 .label = to_label,113 .label = to_label,
109 });114 });
110115
111 return if (match == label.len) to_node else mid.put(label[match..]);116 return if (match == label.len) to_node else mid.put(allocator, label[match..]);
112 }117 }
113118
114 // Add a new node.119 // Add a new node.
115 const node = try self.base.allocator.create(Node);120 const node = try allocator.create(Node);
116 node.* = .{ .base = self.base };121 node.* = .{ .base = self.base };
117 self.base.node_count += 1;122 self.base.node_count += 1;
118123
119 try self.edges.append(self.base.allocator, .{124 try self.edges.append(allocator, .{
120 .from = self,125 .from = self,
121 .to = node,126 .to = node,
122 .label = try self.base.allocator.dupe(u8, label),127 .label = try allocator.dupe(u8, label),
123 });128 });
124129
125 return node;130 return node;
126 }131 }
127132
128 fn fromByteStream(self: *Node, stream: anytype) Trie.FromByteStreamError!void {133 /// Recursively parses the node from the input byte stream.
129 self.trie_offset = try stream.getPos();134 fn read(self: *Node, allocator: *Allocator, reader: anytype) Trie.ReadError!void {
130 var reader = stream.reader();135 self.node_dirty = true;
136
137 self.trie_offset = try reader.context.getPos();
138
131 const node_size = try leb.readULEB128(u64, reader);139 const node_size = try leb.readULEB128(u64, reader);
132 if (node_size > 0) {140 if (node_size > 0) {
133 const export_flags = try leb.readULEB128(u64, reader);141 const export_flags = try leb.readULEB128(u64, reader);
134 // TODO Parse special flags.142 // TODO Parse special flags.
135 assert(export_flags & macho.EXPORT_SYMBOL_FLAGS_REEXPORT == 0 and143 assert(export_flags & macho.EXPORT_SYMBOL_FLAGS_REEXPORT == 0 and
136 export_flags & macho.EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER == 0);144 export_flags & macho.EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER == 0);
145
137 const vmaddr_offset = try leb.readULEB128(u64, reader);146 const vmaddr_offset = try leb.readULEB128(u64, reader);
147
138 self.terminal_info = .{148 self.terminal_info = .{
139 .export_flags = export_flags,149 .export_flags = export_flags,
140 .vmaddr_offset = vmaddr_offset,150 .vmaddr_offset = vmaddr_offset,
141 };151 };
142 }152 }
153
143 const nedges = try reader.readByte();154 const nedges = try reader.readByte();
144 self.base.node_count += nedges;155 self.base.node_count += nedges;
156
145 var i: usize = 0;157 var i: usize = 0;
146 while (i < nedges) : (i += 1) {158 while (i < nedges) : (i += 1) {
147 var label = blk: {159 var label = blk: {
148 var label_buf = std.ArrayList(u8).init(self.base.allocator);160 var label_buf = std.ArrayList(u8).init(allocator);
149 while (true) {161 while (true) {
150 const next = try reader.readByte();162 const next = try reader.readByte();
151 if (next == @as(u8, 0))163 if (next == @as(u8, 0))
...@@ -154,25 +166,32 @@ pub const Node = struct {...@@ -154,25 +166,32 @@ pub const Node = struct {
154 }166 }
155 break :blk label_buf.toOwnedSlice();167 break :blk label_buf.toOwnedSlice();
156 };168 };
169
157 const seek_to = try leb.readULEB128(u64, reader);170 const seek_to = try leb.readULEB128(u64, reader);
158 const cur_pos = try stream.getPos();171 const cur_pos = try reader.context.getPos();
159 try stream.seekTo(seek_to);172 try reader.context.seekTo(seek_to);
160 var node = try self.base.allocator.create(Node);173
174 const node = try allocator.create(Node);
161 node.* = .{ .base = self.base };175 node.* = .{ .base = self.base };
162 try node.fromByteStream(stream);176
163 try self.edges.append(self.base.allocator, .{177 try node.read(allocator, reader);
178 try self.edges.append(allocator, .{
164 .from = self,179 .from = self,
165 .to = node,180 .to = node,
166 .label = label,181 .label = label,
167 });182 });
168 try stream.seekTo(cur_pos);183 try reader.context.seekTo(cur_pos);
169 }184 }
170 }185 }
171186
172 /// This method should only be called *after* updateOffset has been called!187 /// Writes this node to a byte stream.
173 /// In case this is not upheld, this method will panic.188 /// The children of this node *are* not written to the byte stream
174 fn writeULEB128Mem(self: Node, buffer: *std.ArrayList(u8)) !void {189 /// recursively. To write all nodes to a byte stream in sequence,
175 assert(self.trie_offset != null); // You need to call updateOffset first.190 /// iterate over `Trie.ordered_nodes` and call this method on each node.
191 /// This is one of the requirements of the MachO.
192 /// Panics if `finalize` was not called before calling this method.
193 fn write(self: Node, writer: anytype) !void {
194 assert(!self.node_dirty);
176 if (self.terminal_info) |info| {195 if (self.terminal_info) |info| {
177 // Terminal node info: encode export flags and vmaddr offset of this symbol.196 // Terminal node info: encode export flags and vmaddr offset of this symbol.
178 var info_buf_len: usize = 0;197 var info_buf_len: usize = 0;
...@@ -189,38 +208,35 @@ pub const Node = struct {...@@ -189,38 +208,35 @@ pub const Node = struct {
189 var size_stream = std.io.fixedBufferStream(&size_buf);208 var size_stream = std.io.fixedBufferStream(&size_buf);
190 try leb.writeULEB128(size_stream.writer(), info_stream.pos);209 try leb.writeULEB128(size_stream.writer(), info_stream.pos);
191210
192 // Now, write them to the output buffer.211 // Now, write them to the output stream.
193 buffer.appendSliceAssumeCapacity(size_buf[0..size_stream.pos]);212 try writer.writeAll(size_buf[0..size_stream.pos]);
194 buffer.appendSliceAssumeCapacity(info_buf[0..info_stream.pos]);213 try writer.writeAll(info_buf[0..info_stream.pos]);
195 } else {214 } else {
196 // Non-terminal node is delimited by 0 byte.215 // Non-terminal node is delimited by 0 byte.
197 buffer.appendAssumeCapacity(0);216 try writer.writeByte(0);
198 }217 }
199 // Write number of edges (max legal number of edges is 256).218 // Write number of edges (max legal number of edges is 256).
200 buffer.appendAssumeCapacity(@intCast(u8, self.edges.items.len));219 try writer.writeByte(@intCast(u8, self.edges.items.len));
201220
202 for (self.edges.items) |edge| {221 for (self.edges.items) |edge| {
203 // Write edges labels.222 // Write edge label and offset to next node in trie.
204 buffer.appendSliceAssumeCapacity(edge.label);223 try writer.writeAll(edge.label);
205 buffer.appendAssumeCapacity(0);224 try writer.writeByte(0);
206225 try leb.writeULEB128(writer, edge.to.trie_offset.?);
207 var buf: [@sizeOf(u64)]u8 = undefined;
208 var buf_stream = std.io.fixedBufferStream(&buf);
209 try leb.writeULEB128(buf_stream.writer(), edge.to.trie_offset.?);
210 buffer.appendSliceAssumeCapacity(buf[0..buf_stream.pos]);
211 }226 }
212 }227 }
213228
214 const UpdateResult = struct {229 const FinalizeResult = struct {
215 /// Current size of this node in bytes.230 /// Current size of this node in bytes.
216 node_size: usize,231 node_size: usize,
232
217 /// True if the trie offset of this node in the output byte stream233 /// True if the trie offset of this node in the output byte stream
218 /// would need updating; false otherwise.234 /// would need updating; false otherwise.
219 updated: bool,235 updated: bool,
220 };236 };
221237
222 /// Updates offset of this node in the output byte stream.238 /// Updates offset of this node in the output byte stream.
223 fn updateOffset(self: *Node, offset: usize) UpdateResult {239 fn finalize(self: *Node, offset_in_trie: usize) FinalizeResult {
224 var node_size: usize = 0;240 var node_size: usize = 0;
225 if (self.terminal_info) |info| {241 if (self.terminal_info) |info| {
226 node_size += sizeULEB128Mem(info.export_flags);242 node_size += sizeULEB128Mem(info.export_flags);
...@@ -237,8 +253,9 @@ pub const Node = struct {...@@ -237,8 +253,9 @@ pub const Node = struct {
237 }253 }
238254
239 const trie_offset = self.trie_offset orelse 0;255 const trie_offset = self.trie_offset orelse 0;
240 const updated = offset != trie_offset;256 const updated = offset_in_trie != trie_offset;
241 self.trie_offset = offset;257 self.trie_offset = offset_in_trie;
258 self.node_dirty = false;
242259
243 return .{ .node_size = node_size, .updated = updated };260 return .{ .node_size = node_size, .updated = updated };
244 }261 }
...@@ -256,15 +273,30 @@ pub const Node = struct {...@@ -256,15 +273,30 @@ pub const Node = struct {
256 }273 }
257};274};
258275
259/// Count of nodes in the trie.
260/// The count is updated at every `put` call.
261/// The trie always consists of at least a root node, hence
262/// the count always starts at 1.
263node_count: usize = 1,
264/// The root node of the trie.276/// The root node of the trie.
265root: ?Node = null,277root: ?*Node = null,
278
266allocator: *Allocator,279allocator: *Allocator,
267280
281/// If you want to access nodes ordered in DFS fashion,
282/// you should call `finalize` first since the nodes
283/// in this container are not guaranteed to not be stale
284/// if more insertions took place after the last `finalize`
285/// call.
286ordered_nodes: std.ArrayListUnmanaged(*Node) = .{},
287
288/// The size of the trie in bytes.
289/// This value may be outdated if there were additional
290/// insertions performed after `finalize` was called.
291/// Call `finalize` before accessing this value to ensure
292/// it is up-to-date.
293size: usize = 0,
294
295/// Number of nodes currently in the trie.
296node_count: usize = 0,
297
298trie_dirty: bool = true,
299
268pub fn init(allocator: *Allocator) Trie {300pub fn init(allocator: *Allocator) Trie {
269 return .{ .allocator = allocator };301 return .{ .allocator = allocator };
270}302}
...@@ -273,76 +305,90 @@ pub fn init(allocator: *Allocator) Trie {...@@ -273,76 +305,90 @@ pub fn init(allocator: *Allocator) Trie {
273/// This operation may change the layout of the trie by splicing edges in305/// This operation may change the layout of the trie by splicing edges in
274/// certain circumstances.306/// certain circumstances.
275pub fn put(self: *Trie, symbol: Symbol) !void {307pub fn put(self: *Trie, symbol: Symbol) !void {
276 if (self.root == null) {308 try self.createRoot();
277 self.root = .{ .base = self };309 const node = try self.root.?.put(self.allocator, symbol.name);
278 }
279 const node = try self.root.?.put(symbol.name);
280 node.terminal_info = .{310 node.terminal_info = .{
281 .vmaddr_offset = symbol.vmaddr_offset,311 .vmaddr_offset = symbol.vmaddr_offset,
282 .export_flags = symbol.export_flags,312 .export_flags = symbol.export_flags,
283 };313 };
314 self.trie_dirty = true;
284}315}
285316
286const FromByteStreamError = error{317/// Finalizes this trie for writing to a byte stream.
287 OutOfMemory,318/// This step performs multiple passes through the trie ensuring
288 EndOfStream,319/// there are no gaps after every `Node` is ULEB128 encoded.
289 Overflow,320/// Call this method before trying to `write` the trie to a byte stream.
290};321pub fn finalize(self: *Trie) !void {
322 if (!self.trie_dirty) return;
291323
292/// Parse the trie from a byte stream.324 self.ordered_nodes.shrinkRetainingCapacity(0);
293pub fn fromByteStream(self: *Trie, stream: anytype) FromByteStreamError!void {325 try self.ordered_nodes.ensureCapacity(self.allocator, self.node_count);
294 if (self.root == null) {
295 self.root = .{ .base = self };
296 }
297 return self.root.?.fromByteStream(stream);
298}
299326
300/// Write the trie to a buffer ULEB128 encoded.327 comptime const Fifo = std.fifo.LinearFifo(*Node, .{ .Static = std.math.maxInt(u8) });
301/// Caller owns the memory and needs to free it.328 var fifo = Fifo.init();
302pub fn writeULEB128Mem(self: *Trie) ![]u8 {329 try fifo.writeItem(self.root.?);
303 var ordered_nodes = try self.nodes();330
304 defer self.allocator.free(ordered_nodes);331 while (fifo.readItem()) |next| {
332 for (next.edges.items) |*edge| {
333 try fifo.writeItem(edge.to);
334 }
335 self.ordered_nodes.appendAssumeCapacity(next);
336 }
305337
306 var offset: usize = 0;
307 var more: bool = true;338 var more: bool = true;
308 while (more) {339 while (more) {
309 offset = 0;340 self.size = 0;
310 more = false;341 more = false;
311 for (ordered_nodes) |node| {342 for (self.ordered_nodes.items) |node| {
312 const res = node.updateOffset(offset);343 const res = node.finalize(self.size);
313 offset += res.node_size;344 self.size += res.node_size;
314 if (res.updated) more = true;345 if (res.updated) more = true;
315 }346 }
316 }347 }
317348
318 var buffer = std.ArrayList(u8).init(self.allocator);349 self.trie_dirty = false;
319 try buffer.ensureCapacity(offset);
320 for (ordered_nodes) |node| {
321 try node.writeULEB128Mem(&buffer);
322 }
323 return buffer.toOwnedSlice();
324}350}
325351
326pub fn nodes(self: *Trie) ![]*Node {352const ReadError = error{
327 var ordered_nodes = std.ArrayList(*Node).init(self.allocator);353 OutOfMemory,
328 try ordered_nodes.ensureCapacity(self.node_count);354 EndOfStream,
355 Overflow,
356};
329357
330 comptime const Fifo = std.fifo.LinearFifo(*Node, .{ .Static = std.math.maxInt(u8) });358/// Parse the trie from a byte stream.
331 var fifo = Fifo.init();359pub fn read(self: *Trie, reader: anytype) ReadError!void {
332 try fifo.writeItem(&self.root.?);360 try self.createRoot();
361 return self.root.?.read(self.allocator, reader);
362}
333363
334 while (fifo.readItem()) |next| {364/// Write the trie to a byte stream.
335 for (next.edges.items) |*edge| {365/// Caller owns the memory and needs to free it.
336 try fifo.writeItem(edge.to);366/// Panics if the trie was not finalized using `finalize`
337 }367/// before calling this method.
338 ordered_nodes.appendAssumeCapacity(next);368pub fn write(self: Trie, writer: anytype) !usize {
369 assert(!self.trie_dirty);
370 var counting_writer = std.io.countingWriter(writer);
371 for (self.ordered_nodes.items) |node| {
372 try node.write(counting_writer.writer());
339 }373 }
340374 return counting_writer.bytes_written;
341 return ordered_nodes.toOwnedSlice();
342}375}
343376
344pub fn deinit(self: *Trie) void {377pub fn deinit(self: *Trie) void {
345 self.root.?.deinit();378 if (self.root) |root| {
379 root.deinit(self.allocator);
380 self.allocator.destroy(root);
381 }
382 self.ordered_nodes.deinit(self.allocator);
383}
384
385fn createRoot(self: *Trie) !void {
386 if (self.root == null) {
387 const root = try self.allocator.create(Node);
388 root.* = .{ .base = self };
389 self.root = root;
390 self.node_count += 1;
391 }
346}392}
347393
348test "Trie node count" {394test "Trie node count" {
...@@ -350,7 +396,8 @@ test "Trie node count" {...@@ -350,7 +396,8 @@ test "Trie node count" {
350 var trie = Trie.init(gpa);396 var trie = Trie.init(gpa);
351 defer trie.deinit();397 defer trie.deinit();
352398
353 testing.expectEqual(trie.node_count, 1);399 testing.expectEqual(trie.node_count, 0);
400 testing.expect(trie.root == null);
354401
355 try trie.put(.{402 try trie.put(.{
356 .name = "_main",403 .name = "_main",
...@@ -439,7 +486,7 @@ test "Trie basic" {...@@ -439,7 +486,7 @@ test "Trie basic" {
439 }486 }
440}487}
441488
442test "Trie.writeULEB128Mem" {489test "write Trie to a byte stream" {
443 var gpa = testing.allocator;490 var gpa = testing.allocator;
444 var trie = Trie.init(gpa);491 var trie = Trie.init(gpa);
445 defer trie.deinit();492 defer trie.deinit();
...@@ -455,112 +502,91 @@ test "Trie.writeULEB128Mem" {...@@ -455,112 +502,91 @@ test "Trie.writeULEB128Mem" {
455 .export_flags = 0,502 .export_flags = 0,
456 });503 });
457504
458 var buffer = try trie.writeULEB128Mem();505 try trie.finalize();
459 defer gpa.free(buffer);506 try trie.finalize(); // Finalizing mulitple times is a nop subsequently unless we add new nodes.
460507
461 const exp_buffer = [_]u8{508 const exp_buffer = [_]u8{
462 0x0,509 0x0, 0x1, // node root
463 0x1,510 0x5f, 0x0, 0x5, // edge '_'
464 0x5f,511 0x0, 0x2, // non-terminal node
465 0x0,512 0x5f, 0x6d, 0x68, 0x5f, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, // edge '_mh_execute_header'
466 0x5,513 0x65, 0x5f, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x0, 0x21, // edge '_mh_execute_header'
467 0x0,514 0x6d, 0x61, 0x69, 0x6e, 0x0, 0x25, // edge 'main'
468 0x2,515 0x2, 0x0, 0x0, 0x0, // terminal node
469 0x5f,516 0x3, 0x0, 0x80, 0x20, 0x0, // terminal node
470 0x6d,
471 0x68,
472 0x5f,
473 0x65,
474 0x78,
475 0x65,
476 0x63,
477 0x75,
478 0x74,
479 0x65,
480 0x5f,
481 0x68,
482 0x65,
483 0x61,
484 0x64,
485 0x65,
486 0x72,
487 0x0,
488 0x21,
489 0x6d,
490 0x61,
491 0x69,
492 0x6e,
493 0x0,
494 0x25,
495 0x2,
496 0x0,
497 0x0,
498 0x0,
499 0x3,
500 0x0,
501 0x80,
502 0x20,
503 0x0,
504 };517 };
505518
506 testing.expect(buffer.len == exp_buffer.len);519 var buffer = try gpa.alloc(u8, trie.size);
507 testing.expect(mem.eql(u8, buffer, exp_buffer[0..]));520 defer gpa.free(buffer);
521 var stream = std.io.fixedBufferStream(buffer);
522 {
523 const nwritten = try trie.write(stream.writer());
524 testing.expect(nwritten == trie.size);
525 testing.expect(mem.eql(u8, buffer, exp_buffer[0..]));
526 }
527 {
528 // Writing finalized trie again should yield the same result.
529 try stream.seekTo(0);
530 const nwritten = try trie.write(stream.writer());
531 testing.expect(nwritten == trie.size);
532 testing.expect(mem.eql(u8, buffer, exp_buffer[0..]));
533 }
508}534}
509535
510test "parse Trie from byte stream" {536// test "parse Trie from byte stream" {
511 var gpa = testing.allocator;537// var gpa = testing.allocator;
512538
513 const in_buffer = [_]u8{539// const in_buffer = [_]u8{
514 0x0,540// 0x0,
515 0x1,541// 0x1,
516 0x5f,542// 0x5f,
517 0x0,543// 0x0,
518 0x5,544// 0x5,
519 0x0,545// 0x0,
520 0x2,546// 0x2,
521 0x5f,547// 0x5f,
522 0x6d,548// 0x6d,
523 0x68,549// 0x68,
524 0x5f,550// 0x5f,
525 0x65,551// 0x65,
526 0x78,552// 0x78,
527 0x65,553// 0x65,
528 0x63,554// 0x63,
529 0x75,555// 0x75,
530 0x74,556// 0x74,
531 0x65,557// 0x65,
532 0x5f,558// 0x5f,
533 0x68,559// 0x68,
534 0x65,560// 0x65,
535 0x61,561// 0x61,
536 0x64,562// 0x64,
537 0x65,563// 0x65,
538 0x72,564// 0x72,
539 0x0,565// 0x0,
540 0x21,566// 0x21,
541 0x6d,567// 0x6d,
542 0x61,568// 0x61,
543 0x69,569// 0x69,
544 0x6e,570// 0x6e,
545 0x0,571// 0x0,
546 0x25,572// 0x25,
547 0x2,573// 0x2,
548 0x0,574// 0x0,
549 0x0,575// 0x0,
550 0x0,576// 0x0,
551 0x3,577// 0x3,
552 0x0,578// 0x0,
553 0x80,579// 0x80,
554 0x20,580// 0x20,
555 0x0,581// 0x0,
556 };582// };
557 var stream = std.io.fixedBufferStream(in_buffer[0..]);583// var stream = std.io.fixedBufferStream(in_buffer[0..]);
558 var trie = Trie.init(gpa);584// var trie = Trie.init(gpa);
559 defer trie.deinit();585// defer trie.deinit();
560 try trie.fromByteStream(&stream);586// try trie.fromByteStream(&stream);
561587
562 var out_buffer = try trie.writeULEB128Mem();588// var out_buffer = try trie.writeULEB128Mem();
563 defer gpa.free(out_buffer);589// defer gpa.free(out_buffer);
564590
565 testing.expect(mem.eql(u8, in_buffer[0..], out_buffer));591// testing.expect(mem.eql(u8, in_buffer[0..], out_buffer));
566}592// }