| ... | @@ -34,6 +34,7 @@ const std = @import("std"); | ... | @@ -34,6 +34,7 @@ const std = @import("std"); |
| 34 | const mem = std.mem; | 34 | const mem = std.mem; |
| 35 | const leb = std.debug.leb; | 35 | const leb = std.debug.leb; |
| 36 | const log = std.log.scoped(.link); | 36 | const log = std.log.scoped(.link); |
| | 37 | const testing = std.testing; |
| 37 | const Allocator = mem.Allocator; | 38 | const Allocator = mem.Allocator; |
| 38 | | 39 | |
| 39 | pub const Symbol = struct { | 40 | pub const Symbol = struct { |
| ... | @@ -67,48 +68,33 @@ const Node = struct { | ... | @@ -67,48 +68,33 @@ const Node = struct { |
| 67 | self.edges.deinit(alloc); | 68 | self.edges.deinit(alloc); |
| 68 | } | 69 | } |
| 69 | | 70 | |
| 70 | fn put(self: *Node, alloc: *Allocator, fromEdge: ?*Edge, prefix: usize, label: []const u8) !*Node { | 71 | fn put(self: *Node, alloc: *Allocator, label: []const u8) !*Node { |
| 71 | // Traverse all edges. | 72 | // Check for match with edges from this node. |
| 72 | for (self.edges.items) |*edge| { | 73 | for (self.edges.items) |*edge| { |
| 73 | const match = mem.indexOfDiff(u8, edge.label, label) orelse return self; // Got a full match, don't do anything. | 74 | const match = mem.indexOfDiff(u8, edge.label, label) orelse return edge.to; |
| 74 | if (match - prefix > 0) { | 75 | if (match == 0) continue; |
| 75 | // If we match, we advance further down the trie. | 76 | if (match == edge.label.len) return edge.to.put(alloc, label[match..]); |
| 76 | return edge.to.put(alloc, edge, match, label); | 77 | |
| 77 | } | 78 | // Found a match, need to splice up nodes. |
| 78 | } | 79 | // From: A -> B |
| 79 | | 80 | // To: A -> C -> B |
| 80 | if (fromEdge) |from| { | 81 | const mid = try alloc.create(Node); |
| 81 | if (mem.eql(u8, from.label, label[0..prefix])) { | 82 | mid.* = .{}; |
| 82 | if (prefix == label.len) return self; | 83 | const to_label = edge.label; |
| | 84 | const to_node = edge.to; |
| | 85 | edge.to = mid; |
| | 86 | edge.label = label[0..match]; |
| | 87 | |
| | 88 | try mid.edges.append(alloc, .{ |
| | 89 | .from = mid, |
| | 90 | .to = to_node, |
| | 91 | .label = to_label[match..], |
| | 92 | }); |
| | 93 | |
| | 94 | if (match == label.len) { |
| | 95 | return to_node; |
| 83 | } else { | 96 | } else { |
| 84 | // Fixup nodes. We need to insert an intermediate node between | 97 | return mid.put(alloc, label[match..]); |
| 85 | // from.to and self. | | |
| 86 | // Is: A -> B | | |
| 87 | // Should be: A -> C -> B | | |
| 88 | const mid = try alloc.create(Node); | | |
| 89 | mid.* = .{}; | | |
| 90 | const to_label = from.label; | | |
| 91 | from.to = mid; | | |
| 92 | from.label = label[0..prefix]; | | |
| 93 | | | |
| 94 | try mid.edges.append(alloc, .{ | | |
| 95 | .from = mid, | | |
| 96 | .to = self, | | |
| 97 | .label = to_label, | | |
| 98 | }); | | |
| 99 | | | |
| 100 | if (prefix == label.len) return self; // We're done. | | |
| 101 | | | |
| 102 | const new_node = try alloc.create(Node); | | |
| 103 | new_node.* = .{}; | | |
| 104 | | | |
| 105 | try mid.edges.append(alloc, .{ | | |
| 106 | .from = mid, | | |
| 107 | .to = new_node, | | |
| 108 | .label = label, | | |
| 109 | }); | | |
| 110 | | | |
| 111 | return new_node; | | |
| 112 | } | 98 | } |
| 113 | } | 99 | } |
| 114 | | 100 | |
| ... | @@ -148,7 +134,7 @@ const Node = struct { | ... | @@ -148,7 +134,7 @@ const Node = struct { |
| 148 | // Write number of edges (max legal number of edges is 256). | 134 | // Write number of edges (max legal number of edges is 256). |
| 149 | try buffer.append(alloc, @intCast(u8, self.edges.items.len)); | 135 | try buffer.append(alloc, @intCast(u8, self.edges.items.len)); |
| 150 | | 136 | |
| 151 | var node_offset_info: [@sizeOf(u8)]u64 = undefined; | 137 | var node_offset_info: [std.math.maxInt(u8)]u64 = undefined; |
| 152 | for (self.edges.items) |edge, i| { | 138 | for (self.edges.items) |edge, i| { |
| 153 | // Write edges labels leaving out space in-between to later populate | 139 | // Write edges labels leaving out space in-between to later populate |
| 154 | // with offsets to each node. | 140 | // with offsets to each node. |
| ... | @@ -185,7 +171,7 @@ root: Node, | ... | @@ -185,7 +171,7 @@ root: Node, |
| 185 | /// This operation may change the layout of the trie by splicing edges in | 171 | /// This operation may change the layout of the trie by splicing edges in |
| 186 | /// certain circumstances. | 172 | /// certain circumstances. |
| 187 | pub fn put(self: *Trie, alloc: *Allocator, symbol: Symbol) !void { | 173 | pub fn put(self: *Trie, alloc: *Allocator, symbol: Symbol) !void { |
| 188 | const node = try self.root.put(alloc, null, 0, symbol.name); | 174 | const node = try self.root.put(alloc, symbol.name); |
| 189 | node.offset = symbol.offset; | 175 | node.offset = symbol.offset; |
| 190 | node.export_flags = symbol.export_flags; | 176 | node.export_flags = symbol.export_flags; |
| 191 | } | 177 | } |
| ... | @@ -202,9 +188,7 @@ pub fn deinit(self: *Trie, alloc: *Allocator) void { | ... | @@ -202,9 +188,7 @@ pub fn deinit(self: *Trie, alloc: *Allocator) void { |
| 202 | } | 188 | } |
| 203 | | 189 | |
| 204 | test "Trie basic" { | 190 | test "Trie basic" { |
| 205 | const testing = @import("std").testing; | | |
| 206 | var gpa = testing.allocator; | 191 | var gpa = testing.allocator; |
| 207 | | | |
| 208 | var trie: Trie = .{ | 192 | var trie: Trie = .{ |
| 209 | .root = .{}, | 193 | .root = .{}, |
| 210 | }; | 194 | }; |
| ... | @@ -223,7 +207,7 @@ test "Trie basic" { | ... | @@ -223,7 +207,7 @@ test "Trie basic" { |
| 223 | testing.expect(mem.eql(u8, trie.root.edges.items[0].label, "_st")); | 207 | testing.expect(mem.eql(u8, trie.root.edges.items[0].label, "_st")); |
| 224 | | 208 | |
| 225 | { | 209 | { |
| 226 | // root --- _st ---> node --- _start ---> node | 210 | // root --- _st ---> node --- art ---> node |
| 227 | try trie.put(gpa, .{ | 211 | try trie.put(gpa, .{ |
| 228 | .name = "_start", | 212 | .name = "_start", |
| 229 | .offset = 0, | 213 | .offset = 0, |
| ... | @@ -234,12 +218,12 @@ test "Trie basic" { | ... | @@ -234,12 +218,12 @@ test "Trie basic" { |
| 234 | const nextEdge = &trie.root.edges.items[0]; | 218 | const nextEdge = &trie.root.edges.items[0]; |
| 235 | testing.expect(mem.eql(u8, nextEdge.label, "_st")); | 219 | testing.expect(mem.eql(u8, nextEdge.label, "_st")); |
| 236 | testing.expect(nextEdge.to.edges.items.len == 1); | 220 | testing.expect(nextEdge.to.edges.items.len == 1); |
| 237 | testing.expect(mem.eql(u8, nextEdge.to.edges.items[0].label, "_start")); | 221 | testing.expect(mem.eql(u8, nextEdge.to.edges.items[0].label, "art")); |
| 238 | } | 222 | } |
| 239 | { | 223 | { |
| 240 | // root --- _ ---> node --- _st ---> node --- _start ---> node | 224 | // root --- _ ---> node --- st ---> node --- art ---> node |
| 241 | // | | 225 | // | |
| 242 | // | --- _main ---> node | 226 | // | --- main ---> node |
| 243 | try trie.put(gpa, .{ | 227 | try trie.put(gpa, .{ |
| 244 | .name = "_main", | 228 | .name = "_main", |
| 245 | .offset = 0, | 229 | .offset = 0, |
| ... | @@ -250,10 +234,103 @@ test "Trie basic" { | ... | @@ -250,10 +234,103 @@ test "Trie basic" { |
| 250 | const nextEdge = &trie.root.edges.items[0]; | 234 | const nextEdge = &trie.root.edges.items[0]; |
| 251 | testing.expect(mem.eql(u8, nextEdge.label, "_")); | 235 | testing.expect(mem.eql(u8, nextEdge.label, "_")); |
| 252 | testing.expect(nextEdge.to.edges.items.len == 2); | 236 | testing.expect(nextEdge.to.edges.items.len == 2); |
| 253 | testing.expect(mem.eql(u8, nextEdge.to.edges.items[0].label, "_st")); | 237 | testing.expect(mem.eql(u8, nextEdge.to.edges.items[0].label, "st")); |
| 254 | testing.expect(mem.eql(u8, nextEdge.to.edges.items[1].label, "_main")); | 238 | testing.expect(mem.eql(u8, nextEdge.to.edges.items[1].label, "main")); |
| 255 | | 239 | |
| 256 | const nextNextEdge = &nextEdge.to.edges.items[0]; | 240 | const nextNextEdge = &nextEdge.to.edges.items[0]; |
| 257 | testing.expect(mem.eql(u8, nextNextEdge.to.edges.items[0].label, "_start")); | 241 | testing.expect(mem.eql(u8, nextNextEdge.to.edges.items[0].label, "art")); |
| 258 | } | 242 | } |
| 259 | } | 243 | } |
| | 244 | |
| | 245 | test "Trie.writeULEB128Mem" { |
| | 246 | var gpa = testing.allocator; |
| | 247 | var trie: Trie = .{ |
| | 248 | .root = .{}, |
| | 249 | }; |
| | 250 | defer trie.deinit(gpa); |
| | 251 | |
| | 252 | try trie.put(gpa, .{ |
| | 253 | .name = "__mh_execute_header", |
| | 254 | .offset = 0, |
| | 255 | .export_flags = 0, |
| | 256 | }); |
| | 257 | try trie.put(gpa, .{ |
| | 258 | .name = "_main", |
| | 259 | .offset = 0x1000, |
| | 260 | .export_flags = 0, |
| | 261 | }); |
| | 262 | |
| | 263 | var buffer: std.ArrayListUnmanaged(u8) = .{}; |
| | 264 | defer buffer.deinit(gpa); |
| | 265 | |
| | 266 | try trie.writeULEB128Mem(gpa, &buffer); |
| | 267 | |
| | 268 | const exp_buffer = [_]u8{ |
| | 269 | 0x0, |
| | 270 | 0x1, |
| | 271 | 0x5f, |
| | 272 | 0x0, |
| | 273 | 0xc, |
| | 274 | 0x0, |
| | 275 | 0x0, |
| | 276 | 0x0, |
| | 277 | 0x0, |
| | 278 | 0x0, |
| | 279 | 0x0, |
| | 280 | 0x0, |
| | 281 | 0x0, |
| | 282 | 0x2, |
| | 283 | 0x5f, |
| | 284 | 0x6d, |
| | 285 | 0x68, |
| | 286 | 0x5f, |
| | 287 | 0x65, |
| | 288 | 0x78, |
| | 289 | 0x65, |
| | 290 | 0x63, |
| | 291 | 0x75, |
| | 292 | 0x74, |
| | 293 | 0x65, |
| | 294 | 0x5f, |
| | 295 | 0x68, |
| | 296 | 0x65, |
| | 297 | 0x61, |
| | 298 | 0x64, |
| | 299 | 0x65, |
| | 300 | 0x72, |
| | 301 | 0x0, |
| | 302 | 0x36, |
| | 303 | 0x0, |
| | 304 | 0x0, |
| | 305 | 0x0, |
| | 306 | 0x0, |
| | 307 | 0x0, |
| | 308 | 0x0, |
| | 309 | 0x0, |
| | 310 | 0x6d, |
| | 311 | 0x61, |
| | 312 | 0x69, |
| | 313 | 0x6e, |
| | 314 | 0x0, |
| | 315 | 0x3a, |
| | 316 | 0x0, |
| | 317 | 0x0, |
| | 318 | 0x0, |
| | 319 | 0x0, |
| | 320 | 0x0, |
| | 321 | 0x0, |
| | 322 | 0x0, |
| | 323 | 0x2, |
| | 324 | 0x0, |
| | 325 | 0x0, |
| | 326 | 0x0, |
| | 327 | 0x3, |
| | 328 | 0x0, |
| | 329 | 0x80, |
| | 330 | 0x20, |
| | 331 | 0x0, |
| | 332 | }; |
| | 333 | |
| | 334 | testing.expect(buffer.items.len == exp_buffer.len); |
| | 335 | testing.expect(mem.eql(u8, buffer.items, exp_buffer[0..])); |
| | 336 | } |