| ... | @@ -37,6 +37,8 @@ const State = union(enum) { | ... | @@ -37,6 +37,8 @@ const State = union(enum) { |
| 37 | stored_block: u16, | 37 | stored_block: u16, |
| 38 | fixed_block, | 38 | fixed_block, |
| 39 | dynamic_block, | 39 | dynamic_block, |
| | 40 | dynamic_block_literal: u8, |
| | 41 | dynamic_block_match: u16, |
| 40 | protocol_footer, | 42 | protocol_footer, |
| 41 | end, | 43 | end, |
| 42 | }; | 44 | }; |
| ... | @@ -63,7 +65,7 @@ const direct_vtable: Reader.VTable = .{ | ... | @@ -63,7 +65,7 @@ const direct_vtable: Reader.VTable = .{ |
| 63 | const indirect_vtable: Reader.VTable = .{ | 65 | const indirect_vtable: Reader.VTable = .{ |
| 64 | .stream = streamIndirect, | 66 | .stream = streamIndirect, |
| 65 | .rebase = rebaseFallible, | 67 | .rebase = rebaseFallible, |
| 66 | .discard = discard, | 68 | .discard = discardIndirect, |
| 67 | .readVec = readVec, | 69 | .readVec = readVec, |
| 68 | }; | 70 | }; |
| 69 | | 71 | |
| ... | @@ -128,6 +130,26 @@ fn discard(r: *Reader, limit: std.Io.Limit) Reader.Error!usize { | ... | @@ -128,6 +130,26 @@ fn discard(r: *Reader, limit: std.Io.Limit) Reader.Error!usize { |
| 128 | return n; | 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 | fn readVec(r: *Reader, data: [][]u8) Reader.Error!usize { | 153 | fn readVec(r: *Reader, data: [][]u8) Reader.Error!usize { |
| 132 | _ = data; | 154 | _ = data; |
| 133 | const d: *Decompress = @alignCast(@fieldParentPtr("reader", r)); | 155 | const d: *Decompress = @alignCast(@fieldParentPtr("reader", r)); |
| ... | @@ -140,7 +162,7 @@ fn streamIndirectInner(d: *Decompress) Reader.Error!usize { | ... | @@ -140,7 +162,7 @@ fn streamIndirectInner(d: *Decompress) Reader.Error!usize { |
| 140 | var writer: Writer = .{ | 162 | var writer: Writer = .{ |
| 141 | .buffer = r.buffer, | 163 | .buffer = r.buffer, |
| 142 | .end = r.end, | 164 | .end = r.end, |
| 143 | .vtable = &.{ .drain = Writer.fixedDrain }, | 165 | .vtable = &.{ .drain = Writer.unreachableDrain }, |
| 144 | }; | 166 | }; |
| 145 | defer r.end = writer.end; | 167 | defer r.end = writer.end; |
| 146 | _ = streamFallible(d, &writer, .limited(writer.buffer.len - writer.end)) catch |err| switch (err) { | 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,30 +401,49 @@ fn streamInner(d: *Decompress, w: *Writer, limit: std.Io.Limit) (Error || Reader |
| 379 | .dynamic_block => { | 401 | .dynamic_block => { |
| 380 | // In larger archives most blocks are usually dynamic, so | 402 | // In larger archives most blocks are usually dynamic, so |
| 381 | // decompression performance depends on this logic. | 403 | // decompression performance depends on this logic. |
| 382 | while (remaining > 0) { | 404 | var sym = try d.decodeSymbol(&d.lit_dec); |
| 383 | const sym = try d.decodeSymbol(&d.lit_dec); | 405 | sym: switch (sym.kind) { |
| 384 | | 406 | .literal => { |
| 385 | switch (sym.kind) { | 407 | if (remaining != 0) { |
| 386 | .literal => { | 408 | @branchHint(.likely); |
| 387 | try w.writeBytePreserve(flate.history_len, sym.symbol); | | |
| 388 | remaining -= 1; | 409 | remaining -= 1; |
| 389 | }, | 410 | try w.writeBytePreserve(flate.history_len, sym.symbol); |
| 390 | .match => { | 411 | sym = try d.decodeSymbol(&d.lit_dec); |
| 391 | // Decode match backreference <length, distance> | 412 | continue :sym sym.kind; |
| 392 | const length = try d.decodeLength(sym.symbol); | 413 | } else { |
| 393 | const dsm = try d.decodeSymbol(&d.dst_dec); | 414 | d.state = .{ .dynamic_block_literal = sym.symbol }; |
| 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; | | |
| 400 | return @intFromEnum(limit) - remaining; | 415 | return @intFromEnum(limit) - remaining; |
| 401 | }, | 416 | } |
| 402 | } | 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 | .protocol_footer => { | 448 | .protocol_footer => { |
| 408 | switch (d.container_metadata) { | 449 | switch (d.container_metadata) { |
| ... | @@ -424,7 +465,7 @@ fn streamInner(d: *Decompress, w: *Writer, limit: std.Io.Limit) (Error || Reader | ... | @@ -424,7 +465,7 @@ fn streamInner(d: *Decompress, w: *Writer, limit: std.Io.Limit) (Error || Reader |
| 424 | }, | 465 | }, |
| 425 | } | 466 | } |
| 426 | d.state = .end; | 467 | d.state = .end; |
| 427 | return 0; | 468 | return @intFromEnum(limit) - remaining; |
| 428 | }, | 469 | }, |
| 429 | .end => return error.EndOfStream, | 470 | .end => return error.EndOfStream, |
| 430 | } | 471 | } |