| ... | ... | @@ -37,6 +37,8 @@ const State = union(enum) { |
| 37 | 37 | stored_block: u16, |
| 38 | 38 | fixed_block, |
| 39 | 39 | dynamic_block, |
| 40 | dynamic_block_literal: u8, |
| 41 | dynamic_block_match: u16, |
| 40 | 42 | protocol_footer, |
| 41 | 43 | end, |
| 42 | 44 | }; |
| ... | ... | @@ -63,7 +65,7 @@ const direct_vtable: Reader.VTable = .{ |
| 63 | 65 | const indirect_vtable: Reader.VTable = .{ |
| 64 | 66 | .stream = streamIndirect, |
| 65 | 67 | .rebase = rebaseFallible, |
| 66 | | .discard = discard, |
| 68 | .discard = discardIndirect, |
| 67 | 69 | .readVec = readVec, |
| 68 | 70 | }; |
| 69 | 71 | |
| ... | ... | @@ -128,6 +130,26 @@ fn discard(r: *Reader, limit: std.Io.Limit) Reader.Error!usize { |
| 128 | 130 | return n; |
| 129 | 131 | } |
| 130 | 132 | |
| 133 | fn discardIndirect(r: *Reader, limit: std.Io.Limit) Reader.Error!usize { |
| 134 | const d: *Decompress = @alignCast(@fieldParentPtr("reader", r)); |
| 135 | if (r.end + flate.history_len > r.buffer.len) rebase(r, flate.history_len); |
| 136 | var writer: Writer = .{ |
| 137 | .buffer = r.buffer, |
| 138 | .end = r.end, |
| 139 | .vtable = &.{ .drain = Writer.unreachableDrain }, |
| 140 | }; |
| 141 | { |
| 142 | defer r.end = writer.end; |
| 143 | _ = streamFallible(d, &writer, .limited(writer.buffer.len - writer.end)) catch |err| switch (err) { |
| 144 | error.WriteFailed => unreachable, |
| 145 | else => |e| return e, |
| 146 | }; |
| 147 | } |
| 148 | const n = limit.minInt(r.end - r.seek); |
| 149 | r.seek += n; |
| 150 | return n; |
| 151 | } |
| 152 | |
| 131 | 153 | fn readVec(r: *Reader, data: [][]u8) Reader.Error!usize { |
| 132 | 154 | _ = data; |
| 133 | 155 | const d: *Decompress = @alignCast(@fieldParentPtr("reader", r)); |
| ... | ... | @@ -140,7 +162,7 @@ fn streamIndirectInner(d: *Decompress) Reader.Error!usize { |
| 140 | 162 | var writer: Writer = .{ |
| 141 | 163 | .buffer = r.buffer, |
| 142 | 164 | .end = r.end, |
| 143 | | .vtable = &.{ .drain = Writer.fixedDrain }, |
| 165 | .vtable = &.{ .drain = Writer.unreachableDrain }, |
| 144 | 166 | }; |
| 145 | 167 | defer r.end = writer.end; |
| 146 | 168 | _ = streamFallible(d, &writer, .limited(writer.buffer.len - writer.end)) catch |err| switch (err) { |
| ... | ... | @@ -379,30 +401,49 @@ fn streamInner(d: *Decompress, w: *Writer, limit: std.Io.Limit) (Error || Reader |
| 379 | 401 | .dynamic_block => { |
| 380 | 402 | // In larger archives most blocks are usually dynamic, so |
| 381 | 403 | // decompression performance depends on this logic. |
| 382 | | while (remaining > 0) { |
| 383 | | const sym = try d.decodeSymbol(&d.lit_dec); |
| 384 | | |
| 385 | | switch (sym.kind) { |
| 386 | | .literal => { |
| 387 | | try w.writeBytePreserve(flate.history_len, sym.symbol); |
| 404 | var sym = try d.decodeSymbol(&d.lit_dec); |
| 405 | sym: switch (sym.kind) { |
| 406 | .literal => { |
| 407 | if (remaining != 0) { |
| 408 | @branchHint(.likely); |
| 388 | 409 | remaining -= 1; |
| 389 | | }, |
| 390 | | .match => { |
| 391 | | // Decode match backreference <length, distance> |
| 392 | | const length = try d.decodeLength(sym.symbol); |
| 393 | | const dsm = try d.decodeSymbol(&d.dst_dec); |
| 394 | | const distance = try d.decodeDistance(dsm.symbol); |
| 395 | | try writeMatch(w, length, distance); |
| 396 | | remaining -= length; |
| 397 | | }, |
| 398 | | .end_of_block => { |
| 399 | | d.state = if (d.final_block) .protocol_footer else .block_header; |
| 410 | try w.writeBytePreserve(flate.history_len, sym.symbol); |
| 411 | sym = try d.decodeSymbol(&d.lit_dec); |
| 412 | continue :sym sym.kind; |
| 413 | } else { |
| 414 | d.state = .{ .dynamic_block_literal = sym.symbol }; |
| 400 | 415 | return @intFromEnum(limit) - remaining; |
| 401 | | }, |
| 402 | | } |
| 416 | } |
| 417 | }, |
| 418 | .match => { |
| 419 | // Decode match backreference <length, distance> |
| 420 | const length = try d.decodeLength(sym.symbol); |
| 421 | continue :sw .{ .dynamic_block_match = length }; |
| 422 | }, |
| 423 | .end_of_block => { |
| 424 | d.state = if (d.final_block) .protocol_footer else .block_header; |
| 425 | continue :sw d.state; |
| 426 | }, |
| 427 | } |
| 428 | }, |
| 429 | .dynamic_block_literal => |symbol| { |
| 430 | assert(remaining != 0); |
| 431 | remaining -= 1; |
| 432 | try w.writeBytePreserve(flate.history_len, symbol); |
| 433 | continue :sw .dynamic_block; |
| 434 | }, |
| 435 | .dynamic_block_match => |length| { |
| 436 | if (remaining >= length) { |
| 437 | @branchHint(.likely); |
| 438 | remaining -= length; |
| 439 | const dsm = try d.decodeSymbol(&d.dst_dec); |
| 440 | const distance = try d.decodeDistance(dsm.symbol); |
| 441 | try writeMatch(w, length, distance); |
| 442 | continue :sw .dynamic_block; |
| 443 | } else { |
| 444 | d.state = .{ .dynamic_block_match = length }; |
| 445 | return @intFromEnum(limit) - remaining; |
| 403 | 446 | } |
| 404 | | d.state = .dynamic_block; |
| 405 | | return @intFromEnum(limit) - remaining; |
| 406 | 447 | }, |
| 407 | 448 | .protocol_footer => { |
| 408 | 449 | switch (d.container_metadata) { |
| ... | ... | @@ -424,7 +465,7 @@ fn streamInner(d: *Decompress, w: *Writer, limit: std.Io.Limit) (Error || Reader |
| 424 | 465 | }, |
| 425 | 466 | } |
| 426 | 467 | d.state = .end; |
| 427 | | return 0; |
| 468 | return @intFromEnum(limit) - remaining; |
| 428 | 469 | }, |
| 429 | 470 | .end => return error.EndOfStream, |
| 430 | 471 | } |