| ... | ... | @@ -97,7 +97,7 @@ fn decodeLength(self: *Decompress, code: u8) !u16 { |
| 97 | 97 | return if (ml.extra_bits == 0) // 0 - 5 extra bits |
| 98 | 98 | ml.base |
| 99 | 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 | 103 | fn decodeDistance(self: *Decompress, code: u8) !u16 { |
| ... | ... | @@ -106,7 +106,7 @@ fn decodeDistance(self: *Decompress, code: u8) !u16 { |
| 106 | 106 | return if (md.extra_bits == 0) // 0 - 13 extra bits |
| 107 | 107 | md.base |
| 108 | 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 | 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 | 293 | // Length code is followed by 5 bits of distance code. |
| 294 | 294 | const length = try d.decodeLength(@intCast(code - 257)); |
| 295 | 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 | 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 | 318 | const length = try d.decodeLength(sym.symbol); |
| 318 | 319 | const dsm = try d.decodeSymbol(&d.dst_dec); |
| 319 | 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 | 324 | .end_of_block => { |
| 323 | 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 | 352 | |
| 351 | 353 | /// Write match (back-reference to the same data slice) starting at `distance` |
| 352 | 354 | /// back from current write position, and `length` of bytes. |
| 353 | | fn writeMatch(w: *Writer, length: u16, distance: u16, remaining: usize) !usize { |
| 354 | | _ = w; |
| 355 | | _ = length; |
| 356 | | _ = distance; |
| 357 | | _ = remaining; |
| 358 | | @panic("TODO"); |
| 355 | fn writeMatch(w: *Writer, length: u16, distance: u16) !void { |
| 356 | if (w.end < length) return error.InvalidMatch; |
| 357 | if (length < Token.base_length) return error.InvalidMatch; |
| 358 | if (length > Token.max_length) return error.InvalidMatch; |
| 359 | if (distance < Token.min_distance) return error.InvalidMatch; |
| 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 | 370 | fn takeBits(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 { |
| 421 | | const U = @Type(.{ .int = .{ .signedness = .unsigned, .bits = @bitSizeOf(T) } }); |
| 429 | fn peekBits(d: *Decompress, comptime U: type) !U { |
| 422 | 430 | const remaining_bits = d.remaining_bits; |
| 423 | 431 | const next_bits = d.next_bits; |
| 424 | | if (remaining_bits >= @bitSizeOf(T)) { |
| 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 | if (remaining_bits >= @bitSizeOf(U)) return @truncate(next_bits); |
| 432 | 433 | const in = d.input; |
| 433 | 434 | const next_int = in.peekInt(usize, .little) catch |err| switch (err) { |
| 434 | 435 | error.ReadFailed => return error.ReadFailed, |
| 435 | | error.EndOfStream => return peekBitsEnding(d, T), |
| 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), |
| 436 | error.EndOfStream => return peekBitsEnding(d, 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 { |
| 447 | | _ = d; |
| 448 | | @panic("TODO"); |
| 442 | fn peekBitsEnding(d: *Decompress, comptime U: type) !U { |
| 443 | const remaining_bits = d.remaining_bits; |
| 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 | 473 | fn tossBits(d: *Decompress, n: u6) !void { |
| ... | ... | @@ -472,10 +494,12 @@ fn tossBitsEnding(d: *Decompress, n: u6) !void { |
| 472 | 494 | @panic("TODO"); |
| 473 | 495 | } |
| 474 | 496 | |
| 475 | | fn takeNBitsBuffered(d: *Decompress, n: u4) !u16 { |
| 476 | | _ = d; |
| 477 | | _ = n; |
| 478 | | @panic("TODO"); |
| 497 | fn takeBitsRuntime(d: *Decompress, n: u4) !u16 { |
| 498 | const x = try peekBits(d, u16); |
| 499 | const mask: u16 = (@as(u16, 1) << n) - 1; |
| 500 | const u: u16 = @as(u16, @truncate(x)) & mask; |
| 501 | try tossBits(d, n); |
| 502 | return u; |
| 479 | 503 | } |
| 480 | 504 | |
| 481 | 505 | fn alignBitsToByte(d: *Decompress) void { |