| ... | @@ -97,7 +97,7 @@ fn decodeLength(self: *Decompress, code: u8) !u16 { | ... | @@ -97,7 +97,7 @@ fn decodeLength(self: *Decompress, code: u8) !u16 { |
| 97 | return if (ml.extra_bits == 0) // 0 - 5 extra bits | 97 | return if (ml.extra_bits == 0) // 0 - 5 extra bits |
| 98 | ml.base | 98 | ml.base |
| 99 | else | 99 | else |
| 100 | ml.base + try self.takeNBitsBuffered(ml.extra_bits); | 100 | ml.base + try self.takeBitsRuntime(ml.extra_bits); |
| 101 | } | 101 | } |
| 102 | | 102 | |
| 103 | fn decodeDistance(self: *Decompress, code: u8) !u16 { | 103 | fn decodeDistance(self: *Decompress, code: u8) !u16 { |
| ... | @@ -106,7 +106,7 @@ fn decodeDistance(self: *Decompress, code: u8) !u16 { | ... | @@ -106,7 +106,7 @@ fn decodeDistance(self: *Decompress, code: u8) !u16 { |
| 106 | return if (md.extra_bits == 0) // 0 - 13 extra bits | 106 | return if (md.extra_bits == 0) // 0 - 13 extra bits |
| 107 | md.base | 107 | md.base |
| 108 | else | 108 | else |
| 109 | md.base + try self.takeNBitsBuffered(md.extra_bits); | 109 | md.base + try self.takeBitsRuntime(md.extra_bits); |
| 110 | } | 110 | } |
| 111 | | 111 | |
| 112 | // Decode code length symbol to code length. Writes decoded length into | 112 | // Decode code length symbol to code length. Writes decoded length into |
| ... | @@ -293,7 +293,8 @@ fn readInner(d: *Decompress, w: *Writer, limit: std.Io.Limit) (Error || Reader.S | ... | @@ -293,7 +293,8 @@ fn readInner(d: *Decompress, w: *Writer, limit: std.Io.Limit) (Error || Reader.S |
| 293 | // Length code is followed by 5 bits of distance code. | 293 | // Length code is followed by 5 bits of distance code. |
| 294 | const length = try d.decodeLength(@intCast(code - 257)); | 294 | const length = try d.decodeLength(@intCast(code - 257)); |
| 295 | const distance = try d.decodeDistance(@bitReverse(try d.takeBits(u5))); | 295 | const distance = try d.decodeDistance(@bitReverse(try d.takeBits(u5))); |
| 296 | remaining = try writeMatch(w, length, distance, remaining); | 296 | try writeMatch(w, length, distance); |
| | 297 | remaining -= length; |
| 297 | }, | 298 | }, |
| 298 | else => return error.InvalidCode, | 299 | else => return error.InvalidCode, |
| 299 | } | 300 | } |
| ... | @@ -317,7 +318,8 @@ fn readInner(d: *Decompress, w: *Writer, limit: std.Io.Limit) (Error || Reader.S | ... | @@ -317,7 +318,8 @@ fn readInner(d: *Decompress, w: *Writer, limit: std.Io.Limit) (Error || Reader.S |
| 317 | const length = try d.decodeLength(sym.symbol); | 318 | const length = try d.decodeLength(sym.symbol); |
| 318 | const dsm = try d.decodeSymbol(&d.dst_dec); | 319 | const dsm = try d.decodeSymbol(&d.dst_dec); |
| 319 | const distance = try d.decodeDistance(dsm.symbol); | 320 | const distance = try d.decodeDistance(dsm.symbol); |
| 320 | remaining = try writeMatch(w, length, distance, remaining); | 321 | try writeMatch(w, length, distance); |
| | 322 | remaining -= length; |
| 321 | }, | 323 | }, |
| 322 | .end_of_block => { | 324 | .end_of_block => { |
| 323 | d.state = if (d.final_block) .protocol_footer else .block_header; | 325 | d.state = if (d.final_block) .protocol_footer else .block_header; |
| ... | @@ -350,12 +352,19 @@ fn readInner(d: *Decompress, w: *Writer, limit: std.Io.Limit) (Error || Reader.S | ... | @@ -350,12 +352,19 @@ fn readInner(d: *Decompress, w: *Writer, limit: std.Io.Limit) (Error || Reader.S |
| 350 | | 352 | |
| 351 | /// Write match (back-reference to the same data slice) starting at `distance` | 353 | /// Write match (back-reference to the same data slice) starting at `distance` |
| 352 | /// back from current write position, and `length` of bytes. | 354 | /// back from current write position, and `length` of bytes. |
| 353 | fn writeMatch(w: *Writer, length: u16, distance: u16, remaining: usize) !usize { | 355 | fn writeMatch(w: *Writer, length: u16, distance: u16) !void { |
| 354 | _ = w; | 356 | if (w.end < length) return error.InvalidMatch; |
| 355 | _ = length; | 357 | if (length < Token.base_length) return error.InvalidMatch; |
| 356 | _ = distance; | 358 | if (length > Token.max_length) return error.InvalidMatch; |
| 357 | _ = remaining; | 359 | if (distance < Token.min_distance) return error.InvalidMatch; |
| 358 | @panic("TODO"); | 360 | if (distance > Token.max_distance) return error.InvalidMatch; |
| | 361 | |
| | 362 | // This is not a @memmove; it intentionally repeats patterns caused by |
| | 363 | // iterating one byte at a time. |
| | 364 | const dest = try w.writableSlicePreserve(flate.history_len, length); |
| | 365 | const end = dest.ptr - w.buffer.ptr; |
| | 366 | const src = w.buffer[end - distance ..][0..length]; |
| | 367 | for (dest, src) |*d, s| d.* = s; |
| 359 | } | 368 | } |
| 360 | | 369 | |
| 361 | fn takeBits(d: *Decompress, comptime T: type) !T { | 370 | fn takeBits(d: *Decompress, comptime T: type) !T { |
| ... | @@ -417,35 +426,48 @@ fn takeBitsEnding(d: *Decompress, comptime T: type) !T { | ... | @@ -417,35 +426,48 @@ fn takeBitsEnding(d: *Decompress, comptime T: type) !T { |
| 417 | }; | 426 | }; |
| 418 | } | 427 | } |
| 419 | | 428 | |
| 420 | fn peekBits(d: *Decompress, comptime T: type) !T { | 429 | fn peekBits(d: *Decompress, comptime U: type) !U { |
| 421 | const U = @Type(.{ .int = .{ .signedness = .unsigned, .bits = @bitSizeOf(T) } }); | | |
| 422 | const remaining_bits = d.remaining_bits; | 430 | const remaining_bits = d.remaining_bits; |
| 423 | const next_bits = d.next_bits; | 431 | const next_bits = d.next_bits; |
| 424 | if (remaining_bits >= @bitSizeOf(T)) { | 432 | if (remaining_bits >= @bitSizeOf(U)) return @truncate(next_bits); |
| 425 | const u: U = @truncate(next_bits); | | |
| 426 | return switch (@typeInfo(T)) { | | |
| 427 | .int => u, | | |
| 428 | .@"enum" => @enumFromInt(u), | | |
| 429 | else => @bitCast(u), | | |
| 430 | }; | | |
| 431 | } | | |
| 432 | const in = d.input; | 433 | const in = d.input; |
| 433 | const next_int = in.peekInt(usize, .little) catch |err| switch (err) { | 434 | const next_int = in.peekInt(usize, .little) catch |err| switch (err) { |
| 434 | error.ReadFailed => return error.ReadFailed, | 435 | error.ReadFailed => return error.ReadFailed, |
| 435 | error.EndOfStream => return peekBitsEnding(d, T), | 436 | error.EndOfStream => return peekBitsEnding(d, U), |
| 436 | }; | | |
| 437 | const needed_bits = @bitSizeOf(T) - remaining_bits; | | |
| 438 | const u: U = @intCast(((next_int & ((@as(usize, 1) << needed_bits) - 1)) << remaining_bits) | next_bits); | | |
| 439 | return switch (@typeInfo(T)) { | | |
| 440 | .int => u, | | |
| 441 | .@"enum" => @enumFromInt(u), | | |
| 442 | else => @bitCast(u), | | |
| 443 | }; | 437 | }; |
| | 438 | const needed_bits = @bitSizeOf(U) - remaining_bits; |
| | 439 | return @intCast(((next_int & ((@as(usize, 1) << needed_bits) - 1)) << remaining_bits) | next_bits); |
| 444 | } | 440 | } |
| 445 | | 441 | |
| 446 | fn peekBitsEnding(d: *Decompress, comptime T: type) !T { | 442 | fn peekBitsEnding(d: *Decompress, comptime U: type) !U { |
| 447 | _ = d; | 443 | const remaining_bits = d.remaining_bits; |
| 448 | @panic("TODO"); | 444 | const next_bits = d.next_bits; |
| | 445 | const in = d.input; |
| | 446 | var u: U = 0; |
| | 447 | var remaining_needed_bits = @bitSizeOf(U) - remaining_bits; |
| | 448 | var peek_len: usize = 0; |
| | 449 | while (@bitSizeOf(U) >= 8 and remaining_needed_bits >= 8) { |
| | 450 | peek_len += 1; |
| | 451 | const byte = try specialPeek(in, next_bits, peek_len); |
| | 452 | u = (u << 8) | byte; |
| | 453 | remaining_needed_bits -= 8; |
| | 454 | } |
| | 455 | if (remaining_needed_bits != 0) { |
| | 456 | peek_len += 1; |
| | 457 | const byte = try specialPeek(in, next_bits, peek_len); |
| | 458 | u = @intCast((@as(usize, u) << remaining_needed_bits) | (byte & ((@as(usize, 1) << remaining_needed_bits) - 1))); |
| | 459 | } |
| | 460 | return @intCast((@as(usize, u) << remaining_bits) | next_bits); |
| | 461 | } |
| | 462 | |
| | 463 | /// If there is any unconsumed data, handles EndOfStream by pretending there |
| | 464 | /// are zeroes afterwards. |
| | 465 | fn specialPeek(in: *Reader, next_bits: usize, n: usize) Reader.Error!u8 { |
| | 466 | const peeked = in.peek(n) catch |err| switch (err) { |
| | 467 | error.ReadFailed => return error.ReadFailed, |
| | 468 | error.EndOfStream => if (next_bits == 0 and n == 0) return error.EndOfStream else return 0, |
| | 469 | }; |
| | 470 | return peeked[n - 1]; |
| 449 | } | 471 | } |
| 450 | | 472 | |
| 451 | fn tossBits(d: *Decompress, n: u6) !void { | 473 | fn tossBits(d: *Decompress, n: u6) !void { |
| ... | @@ -472,10 +494,12 @@ fn tossBitsEnding(d: *Decompress, n: u6) !void { | ... | @@ -472,10 +494,12 @@ fn tossBitsEnding(d: *Decompress, n: u6) !void { |
| 472 | @panic("TODO"); | 494 | @panic("TODO"); |
| 473 | } | 495 | } |
| 474 | | 496 | |
| 475 | fn takeNBitsBuffered(d: *Decompress, n: u4) !u16 { | 497 | fn takeBitsRuntime(d: *Decompress, n: u4) !u16 { |
| 476 | _ = d; | 498 | const x = try peekBits(d, u16); |
| 477 | _ = n; | 499 | const mask: u16 = (@as(u16, 1) << n) - 1; |
| 478 | @panic("TODO"); | 500 | const u: u16 = @as(u16, @truncate(x)) & mask; |
| | 501 | try tossBits(d, n); |
| | 502 | return u; |
| 479 | } | 503 | } |
| 480 | | 504 | |
| 481 | fn alignBitsToByte(d: *Decompress) void { | 505 | fn alignBitsToByte(d: *Decompress) void { |