| ... | ... | @@ -12,65 +12,54 @@ toc: Toc, |
| 12 | 12 | const Toc = std.StringArrayHashMapUnmanaged(std.ArrayListUnmanaged(u32)); |
| 13 | 13 | |
| 14 | 14 | // Archive files start with the ARMAG identifying string. Then follows a |
| 15 | | // `struct ar_hdr', and as many bytes of member file data as its `ar_size' |
| 15 | // `struct Header', and as many bytes of member file data as its `size' |
| 16 | 16 | // member indicates, for each member file. |
| 17 | 17 | /// String that begins an archive file. |
| 18 | 18 | const ARMAG: *const [SARMAG:0]u8 = "!<arch>\n"; |
| 19 | 19 | /// Size of that string. |
| 20 | 20 | const SARMAG: u4 = 8; |
| 21 | 21 | |
| 22 | | /// String in ar_fmag at the end of each header. |
| 22 | /// String in fmag at the end of each header. |
| 23 | 23 | const ARFMAG: *const [2:0]u8 = "`\n"; |
| 24 | 24 | |
| 25 | | const ar_hdr = extern struct { |
| 25 | const Header = extern struct { |
| 26 | 26 | /// Member file name, sometimes / terminated. |
| 27 | | ar_name: [16]u8, |
| 28 | | |
| 27 | name: [16]u8, |
| 29 | 28 | /// File date, decimal seconds since Epoch. |
| 30 | | ar_date: [12]u8, |
| 31 | | |
| 29 | date: [12]u8, |
| 32 | 30 | /// User ID, in ASCII format. |
| 33 | | ar_uid: [6]u8, |
| 34 | | |
| 31 | uid: [6]u8, |
| 35 | 32 | /// Group ID, in ASCII format. |
| 36 | | ar_gid: [6]u8, |
| 37 | | |
| 33 | gid: [6]u8, |
| 38 | 34 | /// File mode, in ASCII octal. |
| 39 | | ar_mode: [8]u8, |
| 40 | | |
| 35 | mode: [8]u8, |
| 41 | 36 | /// File size, in ASCII decimal. |
| 42 | | ar_size: [10]u8, |
| 43 | | |
| 37 | size: [10]u8, |
| 44 | 38 | /// Always contains ARFMAG. |
| 45 | | ar_fmag: [2]u8, |
| 39 | fmag: [2]u8, |
| 46 | 40 | |
| 47 | 41 | const NameOrIndex = union(enum) { |
| 48 | 42 | name: []const u8, |
| 49 | 43 | index: u32, |
| 50 | 44 | }; |
| 51 | 45 | |
| 52 | | fn nameOrIndex(archive: ar_hdr) !NameOrIndex { |
| 53 | | const value = getValue(&archive.ar_name); |
| 46 | fn nameOrIndex(archive: Header) !NameOrIndex { |
| 47 | const value = getValue(&archive.name); |
| 54 | 48 | const slash_index = mem.indexOfScalar(u8, value, '/') orelse return error.MalformedArchive; |
| 55 | 49 | const len = value.len; |
| 56 | 50 | if (slash_index == len - 1) { |
| 57 | 51 | // Name stored directly |
| 58 | | return NameOrIndex{ .name = value }; |
| 52 | return .{ .name = value }; |
| 59 | 53 | } else { |
| 60 | 54 | // Name follows the header directly and its length is encoded in |
| 61 | 55 | // the name field. |
| 62 | 56 | const index = try std.fmt.parseInt(u32, value[slash_index + 1 ..], 10); |
| 63 | | return NameOrIndex{ .index = index }; |
| 57 | return .{ .index = index }; |
| 64 | 58 | } |
| 65 | 59 | } |
| 66 | 60 | |
| 67 | | fn date(archive: ar_hdr) !u64 { |
| 68 | | const value = getValue(&archive.ar_date); |
| 69 | | return std.fmt.parseInt(u64, value, 10); |
| 70 | | } |
| 71 | | |
| 72 | | fn size(archive: ar_hdr) !u32 { |
| 73 | | const value = getValue(&archive.ar_size); |
| 61 | fn parsedSize(archive: Header) !u32 { |
| 62 | const value = getValue(&archive.size); |
| 74 | 63 | return std.fmt.parseInt(u32, value, 10); |
| 75 | 64 | } |
| 76 | 65 | |
| ... | ... | @@ -98,8 +87,8 @@ pub fn parse(gpa: Allocator, file_contents: []const u8) !Archive { |
| 98 | 87 | const magic = try reader.readBytesNoEof(SARMAG); |
| 99 | 88 | if (!mem.eql(u8, &magic, ARMAG)) return error.BadArchiveMagic; |
| 100 | 89 | |
| 101 | | const header = try reader.readStruct(ar_hdr); |
| 102 | | if (!mem.eql(u8, &header.ar_fmag, ARFMAG)) return error.BadHeaderDelimiter; |
| 90 | const header = try reader.readStruct(Header); |
| 91 | if (!mem.eql(u8, &header.fmag, ARFMAG)) return error.BadHeaderDelimiter; |
| 103 | 92 | |
| 104 | 93 | var toc = try parseTableOfContents(gpa, header, reader); |
| 105 | 94 | errdefer deinitToc(gpa, &toc); |
| ... | ... | @@ -113,7 +102,7 @@ pub fn parse(gpa: Allocator, file_contents: []const u8) !Archive { |
| 113 | 102 | }; |
| 114 | 103 | } |
| 115 | 104 | |
| 116 | | fn parseName(archive: *const Archive, header: ar_hdr) ![]const u8 { |
| 105 | fn parseName(archive: *const Archive, header: Header) ![]const u8 { |
| 117 | 106 | const name_or_index = try header.nameOrIndex(); |
| 118 | 107 | switch (name_or_index) { |
| 119 | 108 | .name => |name| return name, |
| ... | ... | @@ -124,10 +113,10 @@ fn parseName(archive: *const Archive, header: ar_hdr) ![]const u8 { |
| 124 | 113 | } |
| 125 | 114 | } |
| 126 | 115 | |
| 127 | | fn parseTableOfContents(gpa: Allocator, header: ar_hdr, reader: anytype) !Toc { |
| 116 | fn parseTableOfContents(gpa: Allocator, header: Header, reader: anytype) !Toc { |
| 128 | 117 | // size field can have extra spaces padded in front as well as the end, |
| 129 | 118 | // so we trim those first before parsing the ASCII value. |
| 130 | | const size_trimmed = mem.trim(u8, &header.ar_size, " "); |
| 119 | const size_trimmed = mem.trim(u8, &header.size, " "); |
| 131 | 120 | const sym_tab_size = try std.fmt.parseInt(u32, size_trimmed, 10); |
| 132 | 121 | |
| 133 | 122 | const num_symbols = try reader.readInt(u32, .big); |
| ... | ... | @@ -167,14 +156,14 @@ fn parseTableOfContents(gpa: Allocator, header: ar_hdr, reader: anytype) !Toc { |
| 167 | 156 | } |
| 168 | 157 | |
| 169 | 158 | fn parseNameTable(gpa: Allocator, reader: anytype) ![]const u8 { |
| 170 | | const header: ar_hdr = try reader.readStruct(ar_hdr); |
| 171 | | if (!mem.eql(u8, &header.ar_fmag, ARFMAG)) { |
| 159 | const header: Header = try reader.readStruct(Header); |
| 160 | if (!mem.eql(u8, &header.fmag, ARFMAG)) { |
| 172 | 161 | return error.InvalidHeaderDelimiter; |
| 173 | 162 | } |
| 174 | | if (!mem.eql(u8, header.ar_name[0..2], "//")) { |
| 163 | if (!mem.eql(u8, header.name[0..2], "//")) { |
| 175 | 164 | return error.MissingTableName; |
| 176 | 165 | } |
| 177 | | const table_size = try header.size(); |
| 166 | const table_size = try header.parsedSize(); |
| 178 | 167 | const long_file_names = try gpa.alloc(u8, table_size); |
| 179 | 168 | errdefer gpa.free(long_file_names); |
| 180 | 169 | try reader.readNoEof(long_file_names); |
| ... | ... | @@ -186,14 +175,14 @@ fn parseNameTable(gpa: Allocator, reader: anytype) ![]const u8 { |
| 186 | 175 | /// When found, parses the object file into an `Object` and returns it. |
| 187 | 176 | pub fn parseObject(archive: Archive, wasm: *const Wasm, file_contents: []const u8, path: Path) !Object { |
| 188 | 177 | var fbs = std.io.fixedBufferStream(file_contents); |
| 189 | | const header = try fbs.reader().readStruct(ar_hdr); |
| 178 | const header = try fbs.reader().readStruct(Header); |
| 190 | 179 | |
| 191 | | if (!mem.eql(u8, &header.ar_fmag, ARFMAG)) return error.BadArchiveHeaderDelimiter; |
| 180 | if (!mem.eql(u8, &header.fmag, ARFMAG)) return error.BadArchiveHeaderDelimiter; |
| 192 | 181 | |
| 193 | 182 | const object_name = try archive.parseName(header); |
| 194 | | const object_file_size = try header.size(); |
| 183 | const object_file_size = try header.parsedSize(); |
| 195 | 184 | |
| 196 | | return Object.create(wasm, file_contents[@sizeOf(ar_hdr)..][0..object_file_size], path, object_name); |
| 185 | return Object.create(wasm, file_contents[@sizeOf(Header)..][0..object_file_size], path, object_name); |
| 197 | 186 | } |
| 198 | 187 | |
| 199 | 188 | const std = @import("std"); |