| ... | ... | @@ -6,6 +6,7 @@ const frame = types.frame; |
| 6 | 6 | const Literals = types.compressed_block.Literals; |
| 7 | 7 | const Sequences = types.compressed_block.Sequences; |
| 8 | 8 | const Table = types.compressed_block.Table; |
| 9 | const RingBuffer = @import("RingBuffer.zig"); |
| 9 | 10 | |
| 10 | 11 | const readInt = std.mem.readIntLittle; |
| 11 | 12 | const readIntSlice = std.mem.readIntSliceLittle; |
| ... | ... | @@ -214,7 +215,7 @@ const DecodeState = struct { |
| 214 | 215 | } |
| 215 | 216 | |
| 216 | 217 | fn executeSequenceSlice(self: *DecodeState, dest: []u8, write_pos: usize, literals: Literals, sequence: Sequence) !void { |
| 217 | | try self.decodeLiteralsInto(dest[write_pos..], literals, sequence.literal_length); |
| 218 | try self.decodeLiteralsSlice(dest[write_pos..], literals, sequence.literal_length); |
| 218 | 219 | |
| 219 | 220 | // TODO: should we validate offset against max_window_size? |
| 220 | 221 | assert(sequence.offset <= write_pos + sequence.literal_length); |
| ... | ... | @@ -225,6 +226,15 @@ const DecodeState = struct { |
| 225 | 226 | std.mem.copy(u8, dest[write_pos + sequence.literal_length ..], dest[copy_start..copy_end]); |
| 226 | 227 | } |
| 227 | 228 | |
| 229 | fn executeSequenceRingBuffer(self: *DecodeState, dest: *RingBuffer, literals: Literals, sequence: Sequence) !void { |
| 230 | try self.decodeLiteralsRingBuffer(dest, literals, sequence.literal_length); |
| 231 | // TODO: check that ring buffer window is full enough for match copies |
| 232 | const copy_slice = dest.sliceAt(dest.write_index + dest.data.len - sequence.offset, sequence.match_length); |
| 233 | // TODO: would std.mem.copy and figuring out dest slice be better/faster? |
| 234 | for (copy_slice.first) |b| dest.writeAssumeCapacity(b); |
| 235 | for (copy_slice.second) |b| dest.writeAssumeCapacity(b); |
| 236 | } |
| 237 | |
| 228 | 238 | fn decodeSequenceSlice( |
| 229 | 239 | self: *DecodeState, |
| 230 | 240 | dest: []u8, |
| ... | ... | @@ -246,6 +256,31 @@ const DecodeState = struct { |
| 246 | 256 | return sequence.match_length + sequence.literal_length; |
| 247 | 257 | } |
| 248 | 258 | |
| 259 | fn decodeSequenceRingBuffer( |
| 260 | self: *DecodeState, |
| 261 | dest: *RingBuffer, |
| 262 | literals: Literals, |
| 263 | bit_reader: anytype, |
| 264 | last_sequence: bool, |
| 265 | ) !usize { |
| 266 | const sequence = try self.nextSequence(bit_reader); |
| 267 | try self.executeSequenceRingBuffer(dest, literals, sequence); |
| 268 | if (std.options.log_level == .debug) { |
| 269 | const sequence_length = sequence.literal_length + sequence.match_length; |
| 270 | const written_slice = dest.sliceLast(sequence_length); |
| 271 | log.debug("sequence decompressed into '{x}{x}'", .{ |
| 272 | std.fmt.fmtSliceHexUpper(written_slice.first), |
| 273 | std.fmt.fmtSliceHexUpper(written_slice.second), |
| 274 | }); |
| 275 | } |
| 276 | if (!last_sequence) { |
| 277 | try self.updateState(.literal, bit_reader); |
| 278 | try self.updateState(.match, bit_reader); |
| 279 | try self.updateState(.offset, bit_reader); |
| 280 | } |
| 281 | return sequence.match_length + sequence.literal_length; |
| 282 | } |
| 283 | |
| 249 | 284 | fn nextLiteralMultiStream(self: *DecodeState, literals: Literals) !void { |
| 250 | 285 | self.literal_stream_index += 1; |
| 251 | 286 | try self.initLiteralStream(literals.streams.four[self.literal_stream_index]); |
| ... | ... | @@ -258,7 +293,7 @@ const DecodeState = struct { |
| 258 | 293 | while (0 == try self.literal_stream_reader.readBitsNoEof(u1, 1)) {} |
| 259 | 294 | } |
| 260 | 295 | |
| 261 | | fn decodeLiteralsInto(self: *DecodeState, dest: []u8, literals: Literals, len: usize) !void { |
| 296 | fn decodeLiteralsSlice(self: *DecodeState, dest: []u8, literals: Literals, len: usize) !void { |
| 262 | 297 | if (self.literal_written_count + len > literals.header.regenerated_size) return error.MalformedLiteralsLength; |
| 263 | 298 | switch (literals.header.block_type) { |
| 264 | 299 | .raw => { |
| ... | ... | @@ -327,6 +362,74 @@ const DecodeState = struct { |
| 327 | 362 | } |
| 328 | 363 | } |
| 329 | 364 | |
| 365 | fn decodeLiteralsRingBuffer(self: *DecodeState, dest: *RingBuffer, literals: Literals, len: usize) !void { |
| 366 | if (self.literal_written_count + len > literals.header.regenerated_size) return error.MalformedLiteralsLength; |
| 367 | switch (literals.header.block_type) { |
| 368 | .raw => { |
| 369 | const literal_data = literals.streams.one[self.literal_written_count .. self.literal_written_count + len]; |
| 370 | dest.writeSliceAssumeCapacity(literal_data); |
| 371 | self.literal_written_count += len; |
| 372 | }, |
| 373 | .rle => { |
| 374 | var i: usize = 0; |
| 375 | while (i < len) : (i += 1) { |
| 376 | dest.writeAssumeCapacity(literals.streams.one[0]); |
| 377 | } |
| 378 | self.literal_written_count += len; |
| 379 | }, |
| 380 | .compressed, .treeless => { |
| 381 | // const written_bytes_per_stream = (literals.header.regenerated_size + 3) / 4; |
| 382 | const huffman_tree = self.huffman_tree orelse unreachable; |
| 383 | const max_bit_count = huffman_tree.max_bit_count; |
| 384 | const starting_bit_count = Literals.HuffmanTree.weightToBitCount( |
| 385 | huffman_tree.nodes[huffman_tree.symbol_count_minus_one].weight, |
| 386 | max_bit_count, |
| 387 | ); |
| 388 | var bits_read: u4 = 0; |
| 389 | var huffman_tree_index: usize = huffman_tree.symbol_count_minus_one; |
| 390 | var bit_count_to_read: u4 = starting_bit_count; |
| 391 | var i: usize = 0; |
| 392 | while (i < len) : (i += 1) { |
| 393 | var prefix: u16 = 0; |
| 394 | while (true) { |
| 395 | const new_bits = self.literal_stream_reader.readBitsNoEof(u16, bit_count_to_read) catch |err| |
| 396 | switch (err) { |
| 397 | error.EndOfStream => if (literals.streams == .four and self.literal_stream_index < 3) bits: { |
| 398 | try self.nextLiteralMultiStream(literals); |
| 399 | break :bits try self.literal_stream_reader.readBitsNoEof(u16, bit_count_to_read); |
| 400 | } else { |
| 401 | return error.UnexpectedEndOfLiteralStream; |
| 402 | }, |
| 403 | }; |
| 404 | prefix <<= bit_count_to_read; |
| 405 | prefix |= new_bits; |
| 406 | bits_read += bit_count_to_read; |
| 407 | const result = try huffman_tree.query(huffman_tree_index, prefix); |
| 408 | |
| 409 | switch (result) { |
| 410 | .symbol => |sym| { |
| 411 | dest.writeAssumeCapacity(sym); |
| 412 | bit_count_to_read = starting_bit_count; |
| 413 | bits_read = 0; |
| 414 | huffman_tree_index = huffman_tree.symbol_count_minus_one; |
| 415 | break; |
| 416 | }, |
| 417 | .index => |index| { |
| 418 | huffman_tree_index = index; |
| 419 | const bit_count = Literals.HuffmanTree.weightToBitCount( |
| 420 | huffman_tree.nodes[index].weight, |
| 421 | max_bit_count, |
| 422 | ); |
| 423 | bit_count_to_read = bit_count - bits_read; |
| 424 | }, |
| 425 | } |
| 426 | } |
| 427 | } |
| 428 | self.literal_written_count += len; |
| 429 | }, |
| 430 | } |
| 431 | } |
| 432 | |
| 330 | 433 | fn getCode(self: *DecodeState, comptime choice: DataType) u32 { |
| 331 | 434 | return switch (@field(self, @tagName(choice)).table) { |
| 332 | 435 | .rle => |value| value, |
| ... | ... | @@ -437,6 +540,14 @@ fn decodeRawBlock(dest: []u8, src: []const u8, block_size: u21, consumed_count: |
| 437 | 540 | return block_size; |
| 438 | 541 | } |
| 439 | 542 | |
| 543 | fn decodeRawBlockRingBuffer(dest: *RingBuffer, src: []const u8, block_size: u21, consumed_count: *usize) usize { |
| 544 | log.debug("writing raw block - size {d}", .{block_size}); |
| 545 | const data = src[0..block_size]; |
| 546 | dest.writeSliceAssumeCapacity(data); |
| 547 | consumed_count.* += block_size; |
| 548 | return block_size; |
| 549 | } |
| 550 | |
| 440 | 551 | fn decodeRleBlock(dest: []u8, src: []const u8, block_size: u21, consumed_count: *usize) usize { |
| 441 | 552 | log.debug("writing rle block - '{x}'x{d}", .{ src[0], block_size }); |
| 442 | 553 | var write_pos: usize = 0; |
| ... | ... | @@ -447,6 +558,16 @@ fn decodeRleBlock(dest: []u8, src: []const u8, block_size: u21, consumed_count: |
| 447 | 558 | return block_size; |
| 448 | 559 | } |
| 449 | 560 | |
| 561 | fn decodeRleBlockRingBuffer(dest: *RingBuffer, src: []const u8, block_size: u21, consumed_count: *usize) usize { |
| 562 | log.debug("writing rle block - '{x}'x{d}", .{ src[0], block_size }); |
| 563 | var write_pos: usize = 0; |
| 564 | while (write_pos < block_size) : (write_pos += 1) { |
| 565 | dest.writeAssumeCapacity(src[0]); |
| 566 | } |
| 567 | consumed_count.* += 1; |
| 568 | return block_size; |
| 569 | } |
| 570 | |
| 450 | 571 | fn prepareDecodeState( |
| 451 | 572 | decode_state: *DecodeState, |
| 452 | 573 | src: []const u8, |
| ... | ... | @@ -545,7 +666,7 @@ pub fn decodeBlock( |
| 545 | 666 | if (decode_state.literal_written_count < literals.header.regenerated_size) { |
| 546 | 667 | log.debug("decoding remaining literals", .{}); |
| 547 | 668 | const len = literals.header.regenerated_size - decode_state.literal_written_count; |
| 548 | | try decode_state.decodeLiteralsInto(dest[written_count + bytes_written ..], literals, len); |
| 669 | try decode_state.decodeLiteralsSlice(dest[written_count + bytes_written ..], literals, len); |
| 549 | 670 | log.debug("remaining decoded literals at {d}: {}", .{ |
| 550 | 671 | written_count, |
| 551 | 672 | std.fmt.fmtSliceHexUpper(dest[written_count .. written_count + len]), |
| ... | ... | @@ -562,6 +683,73 @@ pub fn decodeBlock( |
| 562 | 683 | } |
| 563 | 684 | } |
| 564 | 685 | |
| 686 | pub fn decodeBlockRingBuffer( |
| 687 | dest: *RingBuffer, |
| 688 | src: []const u8, |
| 689 | block_header: frame.ZStandard.Block.Header, |
| 690 | decode_state: *DecodeState, |
| 691 | consumed_count: *usize, |
| 692 | block_size_maximum: usize, |
| 693 | ) !usize { |
| 694 | const block_size = block_header.block_size; |
| 695 | if (block_size_maximum < block_size) return error.BlockSizeOverMaximum; |
| 696 | // TODO: we probably want to enable safety for release-fast and release-small (or insert custom checks) |
| 697 | switch (block_header.block_type) { |
| 698 | .raw => return decodeRawBlockRingBuffer(dest, src, block_size, consumed_count), |
| 699 | .rle => return decodeRleBlockRingBuffer(dest, src, block_size, consumed_count), |
| 700 | .compressed => { |
| 701 | var bytes_read: usize = 0; |
| 702 | const literals = try decodeLiteralsSection(src, &bytes_read); |
| 703 | const sequences_header = try decodeSequencesHeader(src[bytes_read..], &bytes_read); |
| 704 | |
| 705 | bytes_read += try prepareDecodeState(decode_state, src[bytes_read..], literals, sequences_header); |
| 706 | |
| 707 | var bytes_written: usize = 0; |
| 708 | if (sequences_header.sequence_count > 0) { |
| 709 | const bit_stream_bytes = src[bytes_read..block_size]; |
| 710 | var reverse_byte_reader = reversedByteReader(bit_stream_bytes); |
| 711 | var bit_stream = reverseBitReader(reverse_byte_reader.reader()); |
| 712 | |
| 713 | while (0 == try bit_stream.readBitsNoEof(u1, 1)) {} |
| 714 | try decode_state.readInitialState(&bit_stream); |
| 715 | |
| 716 | var i: usize = 0; |
| 717 | while (i < sequences_header.sequence_count) : (i += 1) { |
| 718 | log.debug("decoding sequence {d}", .{i}); |
| 719 | const decompressed_size = try decode_state.decodeSequenceRingBuffer( |
| 720 | dest, |
| 721 | literals, |
| 722 | &bit_stream, |
| 723 | i == sequences_header.sequence_count - 1, |
| 724 | ); |
| 725 | bytes_written += decompressed_size; |
| 726 | } |
| 727 | |
| 728 | bytes_read += bit_stream_bytes.len; |
| 729 | } |
| 730 | |
| 731 | if (decode_state.literal_written_count < literals.header.regenerated_size) { |
| 732 | log.debug("decoding remaining literals", .{}); |
| 733 | const len = literals.header.regenerated_size - decode_state.literal_written_count; |
| 734 | try decode_state.decodeLiteralsRingBuffer(dest, literals, len); |
| 735 | const written_slice = dest.sliceLast(len); |
| 736 | log.debug("remaining decoded literals at {d}: {}{}", .{ |
| 737 | bytes_written, |
| 738 | std.fmt.fmtSliceHexUpper(written_slice.first), |
| 739 | std.fmt.fmtSliceHexUpper(written_slice.second), |
| 740 | }); |
| 741 | bytes_written += len; |
| 742 | } |
| 743 | |
| 744 | decode_state.literal_written_count = 0; |
| 745 | assert(bytes_read == block_header.block_size); |
| 746 | consumed_count.* += bytes_read; |
| 747 | return bytes_written; |
| 748 | }, |
| 749 | .reserved => return error.FrameContainsReservedBlock, |
| 750 | } |
| 751 | } |
| 752 | |
| 565 | 753 | pub fn decodeSkippableHeader(src: *const [8]u8) frame.Skippable.Header { |
| 566 | 754 | const magic = readInt(u32, src[0..4]); |
| 567 | 755 | assert(isSkippableMagic(magic)); |