| ... | @@ -136,51 +136,90 @@ pub const Header = struct { | ... | @@ -136,51 +136,90 @@ pub const Header = struct { |
| 136 | } | 136 | } |
| 137 | }; | 137 | }; |
| 138 | | 138 | |
| 139 | const Buffer = struct { | 139 | fn BufferedReader(comptime ReaderType: type) type { |
| 140 | buffer: [512 * 8]u8 = undefined, | 140 | return struct { |
| 141 | start: usize = 0, | 141 | unbuffered_reader: ReaderType, |
| 142 | end: usize = 0, | 142 | buffer: [512 * 8]u8 = undefined, |
| | 143 | start: usize = 0, |
| | 144 | end: usize = 0, |
| 143 | | 145 | |
| 144 | pub fn readChunk(b: *Buffer, reader: anytype, count: usize) ![]const u8 { | 146 | const Self = @This(); |
| 145 | b.ensureCapacity(1024); | | |
| 146 | | 147 | |
| 147 | const ask = @min(b.buffer.len - b.end, count -| (b.end - b.start)); | 148 | pub fn readChunk(self: *Self, count: usize) ![]const u8 { |
| 148 | b.end += try reader.readAtLeast(b.buffer[b.end..], ask); | 149 | self.ensureCapacity(1024); |
| 149 | | 150 | |
| 150 | return b.buffer[b.start..b.end]; | 151 | const ask = @min(self.buffer.len - self.end, count -| (self.end - self.start)); |
| 151 | } | 152 | self.end += try self.unbuffered_reader.readAtLeast(self.buffer[self.end..], ask); |
| 152 | | 153 | |
| 153 | pub fn advance(b: *Buffer, count: usize) void { | 154 | return self.buffer[self.start..self.end]; |
| 154 | b.start += count; | 155 | } |
| 155 | assert(b.start <= b.end); | | |
| 156 | } | | |
| 157 | | 156 | |
| 158 | pub fn skip(b: *Buffer, reader: anytype, count: usize) !void { | 157 | pub fn advance(self: *Self, count: usize) void { |
| 159 | if (b.start + count > b.end) { | 158 | self.start += count; |
| 160 | try reader.skipBytes(b.start + count - b.end, .{}); | 159 | assert(self.start <= self.end); |
| 161 | b.start = b.end; | 160 | } |
| 162 | } else { | 161 | |
| 163 | b.advance(count); | 162 | pub fn skip(self: *Self, count: usize) !void { |
| | 163 | if (self.start + count > self.end) { |
| | 164 | try self.unbuffered_reader.skipBytes(self.start + count - self.end, .{}); |
| | 165 | self.start = self.end; |
| | 166 | } else { |
| | 167 | self.advance(count); |
| | 168 | } |
| 164 | } | 169 | } |
| 165 | } | | |
| 166 | | 170 | |
| 167 | inline fn ensureCapacity(b: *Buffer, count: usize) void { | 171 | inline fn ensureCapacity(self: *Self, count: usize) void { |
| 168 | if (b.buffer.len - b.start < count) { | 172 | if (self.buffer.len - self.start < count) { |
| 169 | const dest_end = b.end - b.start; | 173 | const dest_end = self.end - self.start; |
| 170 | @memcpy(b.buffer[0..dest_end], b.buffer[b.start..b.end]); | 174 | @memcpy(self.buffer[0..dest_end], self.buffer[self.start..self.end]); |
| 171 | b.end = dest_end; | 175 | self.end = dest_end; |
| 172 | b.start = 0; | 176 | self.start = 0; |
| | 177 | } |
| 173 | } | 178 | } |
| 174 | } | 179 | |
| 175 | }; | 180 | pub fn write(self: *Self, writer: anytype, size: usize) !void { |
| | 181 | const rounded_file_size = std.mem.alignForward(usize, size, 512); |
| | 182 | const chunk_size = rounded_file_size + 512; |
| | 183 | const pad_len: usize = rounded_file_size - size; |
| | 184 | |
| | 185 | var file_off: usize = 0; |
| | 186 | while (true) { |
| | 187 | const temp = try self.readChunk(chunk_size - file_off); |
| | 188 | if (temp.len == 0) return error.UnexpectedEndOfStream; |
| | 189 | const slice = temp[0..@min(size - file_off, temp.len)]; |
| | 190 | try writer.writeAll(slice); |
| | 191 | |
| | 192 | file_off += slice.len; |
| | 193 | self.advance(slice.len); |
| | 194 | if (file_off >= size) { |
| | 195 | self.advance(pad_len); |
| | 196 | return; |
| | 197 | } |
| | 198 | } |
| | 199 | } |
| | 200 | |
| | 201 | pub fn copy(self: *Self, dst_buffer: []u8, size: usize) !void { |
| | 202 | const rounded_file_size = std.mem.alignForward(usize, size, 512); |
| | 203 | const chunk_size = rounded_file_size + 512; |
| | 204 | |
| | 205 | var i: usize = 0; |
| | 206 | while (i < size) { |
| | 207 | const slice = try self.readChunk(chunk_size - i); |
| | 208 | if (slice.len == 0) return error.UnexpectedEndOfStream; |
| | 209 | const copy_size: usize = @min(size - i, slice.len); |
| | 210 | @memcpy(dst_buffer[i .. i + copy_size], slice[0..copy_size]); |
| | 211 | self.advance(copy_size); |
| | 212 | i += copy_size; |
| | 213 | } |
| | 214 | } |
| | 215 | }; |
| | 216 | } |
| 176 | | 217 | |
| 177 | fn Iterator(comptime ReaderType: type) type { | 218 | fn Iterator(comptime ReaderType: type) type { |
| 178 | return struct { | 219 | return struct { |
| 179 | file_name_buffer: [std.fs.MAX_PATH_BYTES]u8 = undefined, | 220 | file_name_buffer: [std.fs.MAX_PATH_BYTES]u8 = undefined, |
| 180 | file_name_len: usize = 0, | 221 | file_name_len: usize = 0, |
| 181 | buffer: Buffer = .{}, | 222 | reader: BufferedReader(ReaderType), |
| 182 | reader: ReaderType, | | |
| 183 | pad_len: usize = 0, | | |
| 184 | diagnostics: ?*Options.Diagnostics, | 223 | diagnostics: ?*Options.Diagnostics, |
| 185 | | 224 | |
| 186 | const Self = @This(); | 225 | const Self = @This(); |
| ... | @@ -193,50 +232,32 @@ fn Iterator(comptime ReaderType: type) type { | ... | @@ -193,50 +232,32 @@ fn Iterator(comptime ReaderType: type) type { |
| 193 | iter: *Self, | 232 | iter: *Self, |
| 194 | | 233 | |
| 195 | pub fn write(self: File, writer: anytype) !void { | 234 | pub fn write(self: File, writer: anytype) !void { |
| 196 | const rounded_file_size = std.mem.alignForward(u64, self.size, 512); | 235 | try self.iter.reader.write(writer, self.size); |
| 197 | var file_off: usize = 0; | | |
| 198 | while (true) { | | |
| 199 | const temp = try self.iter.buffer.readChunk(self.iter.reader, @intCast(rounded_file_size + 512 - file_off)); | | |
| 200 | if (temp.len == 0) return error.UnexpectedEndOfStream; | | |
| 201 | const slice = temp[0..@intCast(@min(self.size - file_off, temp.len))]; | | |
| 202 | try writer.writeAll(slice); | | |
| 203 | | | |
| 204 | file_off += slice.len; | | |
| 205 | self.iter.buffer.advance(slice.len); | | |
| 206 | if (file_off >= self.size) { | | |
| 207 | return; | | |
| 208 | // self.iter.buffer.advance(pad_len); | | |
| 209 | // continue :header; | | |
| 210 | } | | |
| 211 | } | | |
| 212 | } | 236 | } |
| 213 | | 237 | |
| 214 | pub fn skip(self: File) void { | 238 | pub fn skip(self: File) !void { |
| 215 | _ = self; | 239 | const rounded_file_size = std.mem.alignForward(usize, self.size, 512); |
| 216 | unreachable; | 240 | try self.iter.reader.skip(rounded_file_size); |
| 217 | } | 241 | } |
| 218 | }; | 242 | }; |
| 219 | | 243 | |
| 220 | pub fn next(self: *Self) !?File { | 244 | pub fn next(self: *Self) !?File { |
| 221 | self.buffer.advance(self.pad_len); | | |
| 222 | self.pad_len = 0; | | |
| 223 | self.file_name_len = 0; | 245 | self.file_name_len = 0; |
| 224 | | | |
| 225 | while (true) { | 246 | while (true) { |
| 226 | const chunk = try self.buffer.readChunk(self.reader, 1024); | 247 | const chunk = try self.reader.readChunk(1024); |
| 227 | switch (chunk.len) { | 248 | switch (chunk.len) { |
| 228 | 0 => return null, | 249 | 0 => return null, |
| 229 | 1...511 => return error.UnexpectedEndOfStream, | 250 | 1...511 => return error.UnexpectedEndOfStream, |
| 230 | else => {}, | 251 | else => {}, |
| 231 | } | 252 | } |
| 232 | self.buffer.advance(512); | 253 | self.reader.advance(512); |
| 233 | | 254 | |
| 234 | const header: Header = .{ .bytes = chunk[0..512] }; | 255 | const header: Header = .{ .bytes = chunk[0..512] }; |
| 235 | const file_size = try header.fileSize(); | 256 | const file_size = try header.fileSize(); |
| 236 | const file_type = header.fileType(); | 257 | const file_type = header.fileType(); |
| 237 | const link_name = header.linkName(); | 258 | const link_name = header.linkName(); |
| 238 | const rounded_file_size = std.mem.alignForward(u64, file_size, 512); | 259 | const rounded_file_size: usize = std.mem.alignForward(usize, file_size, 512); |
| 239 | self.pad_len = @intCast(rounded_file_size - file_size); | 260 | |
| 240 | const file_name = if (self.file_name_len == 0) | 261 | const file_name = if (self.file_name_len == 0) |
| 241 | try header.fullFileName(&self.file_name_buffer) | 262 | try header.fullFileName(&self.file_name_buffer) |
| 242 | else | 263 | else |
| ... | @@ -253,44 +274,33 @@ fn Iterator(comptime ReaderType: type) type { | ... | @@ -253,44 +274,33 @@ fn Iterator(comptime ReaderType: type) type { |
| 253 | }; | 274 | }; |
| 254 | }, | 275 | }, |
| 255 | .global_extended_header => { | 276 | .global_extended_header => { |
| 256 | self.buffer.skip(self.reader, @intCast(rounded_file_size)) catch return error.TarHeadersTooBig; | 277 | self.reader.skip(rounded_file_size) catch return error.TarHeadersTooBig; |
| 257 | }, | 278 | }, |
| 258 | .extended_header => { | 279 | .extended_header => { |
| 259 | if (file_size == 0) { | 280 | if (file_size == 0) continue; |
| 260 | self.buffer.advance(@intCast(rounded_file_size)); | | |
| 261 | continue; | | |
| 262 | } | | |
| 263 | | 281 | |
| 264 | const chunk_size: usize = @intCast(rounded_file_size + 512); | 282 | const chunk_size: usize = rounded_file_size + 512; |
| 265 | var data_off: usize = 0; | 283 | var data_off: usize = 0; |
| 266 | const file_name_override_len = while (data_off < file_size) { | 284 | const file_name_override_len = while (data_off < file_size) { |
| 267 | const slice = try self.buffer.readChunk(self.reader, chunk_size - data_off); | 285 | const slice = try self.reader.readChunk(chunk_size - data_off); |
| 268 | if (slice.len == 0) return error.UnexpectedEndOfStream; | 286 | if (slice.len == 0) return error.UnexpectedEndOfStream; |
| 269 | const remaining_size: usize = @intCast(file_size - data_off); | 287 | const remaining_size: usize = file_size - data_off; |
| 270 | const attr_info = try parsePaxAttribute(slice[0..@min(remaining_size, slice.len)], remaining_size); | 288 | const attr_info = try parsePaxAttribute(slice[0..@min(remaining_size, slice.len)], remaining_size); |
| 271 | | 289 | |
| 272 | if (std.mem.eql(u8, attr_info.key, "path")) { | 290 | if (std.mem.eql(u8, attr_info.key, "path")) { |
| 273 | if (attr_info.value_len > self.file_name_buffer.len) return error.NameTooLong; | 291 | if (attr_info.value_len > self.file_name_buffer.len) return error.NameTooLong; |
| 274 | self.buffer.advance(attr_info.value_off); | 292 | self.reader.advance(attr_info.value_off); |
| 275 | data_off += attr_info.value_off; | 293 | data_off += attr_info.value_off; |
| 276 | break attr_info.value_len; | 294 | break attr_info.value_len; |
| 277 | } | 295 | } |
| 278 | | 296 | |
| 279 | try self.buffer.skip(self.reader, attr_info.size); | 297 | try self.reader.skip(attr_info.size); |
| 280 | data_off += attr_info.size; | 298 | data_off += attr_info.size; |
| 281 | } else 0; | 299 | } else 0; |
| 282 | | 300 | |
| 283 | var i: usize = 0; | 301 | try self.reader.copy(&self.file_name_buffer, file_name_override_len); |
| 284 | while (i < file_name_override_len) { | | |
| 285 | const slice = try self.buffer.readChunk(self.reader, chunk_size - data_off - i); | | |
| 286 | if (slice.len == 0) return error.UnexpectedEndOfStream; | | |
| 287 | const copy_size: usize = @intCast(@min(file_name_override_len - i, slice.len)); | | |
| 288 | @memcpy(self.file_name_buffer[i .. i + copy_size], slice[0..copy_size]); | | |
| 289 | self.buffer.advance(copy_size); | | |
| 290 | i += copy_size; | | |
| 291 | } | | |
| 292 | | 302 | |
| 293 | try self.buffer.skip(self.reader, @intCast(rounded_file_size - data_off - file_name_override_len)); | 303 | try self.reader.skip(rounded_file_size - data_off - file_name_override_len); |
| 294 | self.file_name_len = file_name_override_len; | 304 | self.file_name_len = file_name_override_len; |
| 295 | continue; | 305 | continue; |
| 296 | }, | 306 | }, |
| ... | @@ -309,7 +319,11 @@ fn Iterator(comptime ReaderType: type) type { | ... | @@ -309,7 +319,11 @@ fn Iterator(comptime ReaderType: type) type { |
| 309 | } | 319 | } |
| 310 | | 320 | |
| 311 | pub fn iterator(reader: anytype, diagnostics: ?*Options.Diagnostics) Iterator(@TypeOf(reader)) { | 321 | pub fn iterator(reader: anytype, diagnostics: ?*Options.Diagnostics) Iterator(@TypeOf(reader)) { |
| 312 | return .{ .reader = reader, .diagnostics = diagnostics }; | 322 | const ReaderType = @TypeOf(reader); |
| | 323 | return .{ |
| | 324 | .reader = BufferedReader(ReaderType){ .unbuffered_reader = reader }, |
| | 325 | .diagnostics = diagnostics, |
| | 326 | }; |
| 313 | } | 327 | } |
| 314 | | 328 | |
| 315 | pub fn pipeToFileSystem(dir: std.fs.Dir, reader: anytype, options: Options) !void { | 329 | pub fn pipeToFileSystem(dir: std.fs.Dir, reader: anytype, options: Options) !void { |
| ... | @@ -364,7 +378,7 @@ pub fn pipeToFileSystem(dir: std.fs.Dir, reader: anytype, options: Options) !voi | ... | @@ -364,7 +378,7 @@ pub fn pipeToFileSystem(dir: std.fs.Dir, reader: anytype, options: Options) !voi |
| 364 | if (file) |f| { | 378 | if (file) |f| { |
| 365 | try iter_file.write(f); | 379 | try iter_file.write(f); |
| 366 | } else { | 380 | } else { |
| 367 | iter_file.skip(); | 381 | try iter_file.skip(); |
| 368 | } | 382 | } |
| 369 | }, | 383 | }, |
| 370 | .symbolic_link => { | 384 | .symbolic_link => { |