| ... | ... | @@ -6,6 +6,8 @@ const types = @import("types.zig"); |
| 6 | 6 | const frame = types.frame; |
| 7 | 7 | const LiteralsSection = types.compressed_block.LiteralsSection; |
| 8 | 8 | const SequencesSection = types.compressed_block.SequencesSection; |
| 9 | const SkippableHeader = types.frame.Skippable.Header; |
| 10 | const ZstandardHeader = types.frame.Zstandard.Header; |
| 9 | 11 | const Table = types.compressed_block.Table; |
| 10 | 12 | |
| 11 | 13 | pub const block = @import("decode/block.zig"); |
| ... | ... | @@ -16,15 +18,13 @@ const readers = @import("readers.zig"); |
| 16 | 18 | |
| 17 | 19 | const readInt = std.mem.readIntLittle; |
| 18 | 20 | const readIntSlice = std.mem.readIntSliceLittle; |
| 19 | | fn readVarInt(comptime T: type, bytes: []const u8) T { |
| 20 | | return std.mem.readVarInt(T, bytes, .Little); |
| 21 | | } |
| 22 | 21 | |
| 22 | /// Returns `true` is `magic` is a valid magic number for a skippable frame |
| 23 | 23 | pub fn isSkippableMagic(magic: u32) bool { |
| 24 | 24 | return frame.Skippable.magic_number_min <= magic and magic <= frame.Skippable.magic_number_max; |
| 25 | 25 | } |
| 26 | 26 | |
| 27 | | /// Returns the kind of frame at the beginning of `src`. |
| 27 | /// Returns the kind of frame at the beginning of `source`. |
| 28 | 28 | /// |
| 29 | 29 | /// Errors returned: |
| 30 | 30 | /// - `error.BadMagic` if `source` begins with bytes not equal to the |
| ... | ... | @@ -50,11 +50,22 @@ pub fn frameType(magic: u32) error{BadMagic}!frame.Kind { |
| 50 | 50 | } |
| 51 | 51 | |
| 52 | 52 | pub const FrameHeader = union(enum) { |
| 53 | | zstandard: types.frame.Zstandard.Header, |
| 54 | | skippable: types.frame.Skippable.Header, |
| 53 | zstandard: ZstandardHeader, |
| 54 | skippable: SkippableHeader, |
| 55 | 55 | }; |
| 56 | 56 | |
| 57 | | pub fn decodeFrameHeader(source: anytype) error{ BadMagic, EndOfStream, ReservedBitSet }!FrameHeader { |
| 57 | pub const HeaderError = error{ BadMagic, EndOfStream, ReservedBitSet }; |
| 58 | |
| 59 | /// Returns the header of the frame at the beginning of `source`. |
| 60 | /// |
| 61 | /// Errors returned: |
| 62 | /// - `error.BadMagic` if `source` begins with bytes not equal to the |
| 63 | /// Zstandard frame magic number, or outside the range of magic numbers for |
| 64 | /// skippable frames. |
| 65 | /// - `error.EndOfStream` if `source` contains fewer than 4 bytes |
| 66 | /// - `error.ReservedBitSet` if the frame is a Zstandard frame and any of the |
| 67 | /// reserved bits are set |
| 68 | pub fn decodeFrameHeader(source: anytype) HeaderError!FrameHeader { |
| 58 | 69 | const magic = try source.readIntLittle(u32); |
| 59 | 70 | const frame_type = try frameType(magic); |
| 60 | 71 | switch (frame_type) { |
| ... | ... | @@ -68,41 +79,74 @@ pub fn decodeFrameHeader(source: anytype) error{ BadMagic, EndOfStream, Reserved |
| 68 | 79 | } |
| 69 | 80 | } |
| 70 | 81 | |
| 71 | | const ReadWriteCount = struct { |
| 82 | pub const ReadWriteCount = struct { |
| 72 | 83 | read_count: usize, |
| 73 | 84 | write_count: usize, |
| 74 | 85 | }; |
| 75 | 86 | |
| 76 | | /// Decodes frames from `src` into `dest`; see `decodeFrame()`. |
| 77 | | pub fn decode(dest: []u8, src: []const u8, verify_checksum: bool) !usize { |
| 87 | /// Decodes frames from `src` into `dest`; returns the length of the result. |
| 88 | /// The stream should not have extra trailing bytes - either all bytes in `src` |
| 89 | /// will be decoded, or an error will be returned. An error will be returned if |
| 90 | /// a Zstandard frame in `src` does not declare its content size. |
| 91 | /// |
| 92 | /// Errors returned: |
| 93 | /// - `error.DictionaryIdFlagUnsupported` if a `src` contains a frame that |
| 94 | /// uses a dictionary |
| 95 | /// - `error.MalformedFrame` if a frame in `src` is invalid |
| 96 | /// - `error.UnknownContentSizeUnsupported` if a frame in `src` does not |
| 97 | /// declare its content size |
| 98 | pub fn decode(dest: []u8, src: []const u8, verify_checksum: bool) error{ |
| 99 | MalformedFrame, |
| 100 | UnknownContentSizeUnsupported, |
| 101 | DictionaryIdFlagUnsupported, |
| 102 | }!usize { |
| 78 | 103 | var write_count: usize = 0; |
| 79 | 104 | var read_count: usize = 0; |
| 80 | 105 | while (read_count < src.len) { |
| 81 | | const counts = try decodeFrame(dest, src[read_count..], verify_checksum); |
| 106 | const counts = decodeFrame(dest, src[read_count..], verify_checksum) catch |err| { |
| 107 | switch (err) { |
| 108 | error.UnknownContentSizeUnsupported => return error.UnknownContentSizeUnsupported, |
| 109 | error.DictionaryIdFlagUnsupported => return error.DictionaryIdFlagUnsupported, |
| 110 | else => return error.MalformedFrame, |
| 111 | } |
| 112 | }; |
| 82 | 113 | read_count += counts.read_count; |
| 83 | 114 | write_count += counts.write_count; |
| 84 | 115 | } |
| 85 | 116 | return write_count; |
| 86 | 117 | } |
| 87 | 118 | |
| 119 | /// Decodes a stream of frames from `src`; returns the decoded bytes. The stream |
| 120 | /// should not have extra trailing bytes - either all bytes in `src` will be |
| 121 | /// decoded, or an error will be returned. |
| 122 | /// |
| 123 | /// Errors returned: |
| 124 | /// - `error.DictionaryIdFlagUnsupported` if a `src` contains a frame that |
| 125 | /// uses a dictionary |
| 126 | /// - `error.MalformedFrame` if a frame in `src` is invalid |
| 127 | /// - `error.OutOfMemory` if `allocator` cannot allocate enough memory |
| 88 | 128 | pub fn decodeAlloc( |
| 89 | 129 | allocator: Allocator, |
| 90 | 130 | src: []const u8, |
| 91 | 131 | verify_checksum: bool, |
| 92 | 132 | window_size_max: usize, |
| 93 | | ) ![]u8 { |
| 133 | ) error{ DictionaryIdFlagUnsupported, MalformedFrame, OutOfMemory }![]u8 { |
| 94 | 134 | var result = std.ArrayList(u8).init(allocator); |
| 95 | 135 | errdefer result.deinit(); |
| 96 | 136 | |
| 97 | 137 | var read_count: usize = 0; |
| 98 | 138 | while (read_count < src.len) { |
| 99 | | read_count += try decodeFrameArrayList( |
| 139 | read_count += decodeFrameArrayList( |
| 100 | 140 | allocator, |
| 101 | 141 | &result, |
| 102 | 142 | src[read_count..], |
| 103 | 143 | verify_checksum, |
| 104 | 144 | window_size_max, |
| 105 | | ); |
| 145 | ) catch |err| switch (err) { |
| 146 | error.OutOfMemory => return error.OutOfMemory, |
| 147 | error.DictionaryIdFlagUnsupported => return error.DictionaryIdFlagUnsupported, |
| 148 | else => return error.MalformedFrame, |
| 149 | }; |
| 106 | 150 | } |
| 107 | 151 | return result.toOwnedSlice(); |
| 108 | 152 | } |
| ... | ... | @@ -112,18 +156,24 @@ pub fn decodeAlloc( |
| 112 | 156 | /// frames that declare the decompressed content size. |
| 113 | 157 | /// |
| 114 | 158 | /// Errors returned: |
| 159 | /// - `error.BadMagic` if the first 4 bytes of `src` is not a valid magic |
| 160 | /// number for a Zstandard or skippable frame |
| 115 | 161 | /// - `error.UnknownContentSizeUnsupported` if the frame does not declare the |
| 116 | 162 | /// uncompressed content size |
| 163 | /// - `error.WindowSizeUnknown` if the frame does not have a valid window size |
| 117 | 164 | /// - `error.ContentTooLarge` if `dest` is smaller than the uncompressed data |
| 118 | 165 | /// size declared by the frame header |
| 119 | | /// - `error.BadMagic` if the first 4 bytes of `src` is not a valid magic |
| 120 | | /// number for a Zstandard or Skippable frame |
| 166 | /// - `error.ContentSizeTooLarge` if the frame header indicates a content size |
| 167 | /// that is larger than `std.math.maxInt(usize)` |
| 121 | 168 | /// - `error.DictionaryIdFlagUnsupported` if the frame uses a dictionary |
| 122 | 169 | /// - `error.ChecksumFailure` if `verify_checksum` is true and the frame |
| 123 | 170 | /// contains a checksum that does not match the checksum of the decompressed |
| 124 | 171 | /// data |
| 125 | | /// - `error.ReservedBitSet` if the reserved bit of the frame header is set |
| 172 | /// - `error.ReservedBitSet` if any of the reserved bits of the frame header |
| 173 | /// are set |
| 126 | 174 | /// - `error.EndOfStream` if `src` does not contain a complete frame |
| 175 | /// - `error.BadContentSize` if the content size declared by the frame does |
| 176 | /// not equal the actual size of decompressed data |
| 127 | 177 | /// - an error in `block.Error` if there are errors decoding a block |
| 128 | 178 | /// - `error.SkippableSizeTooLarge` if the frame is skippable and reports a |
| 129 | 179 | /// size greater than `src.len` |
| ... | ... | @@ -131,7 +181,15 @@ pub fn decodeFrame( |
| 131 | 181 | dest: []u8, |
| 132 | 182 | src: []const u8, |
| 133 | 183 | verify_checksum: bool, |
| 134 | | ) !ReadWriteCount { |
| 184 | ) (error{ |
| 185 | BadMagic, |
| 186 | UnknownContentSizeUnsupported, |
| 187 | ContentTooLarge, |
| 188 | ContentSizeTooLarge, |
| 189 | WindowSizeUnknown, |
| 190 | DictionaryIdFlagUnsupported, |
| 191 | SkippableSizeTooLarge, |
| 192 | } || FrameError)!ReadWriteCount { |
| 135 | 193 | var fbs = std.io.fixedBufferStream(src); |
| 136 | 194 | switch (try decodeFrameType(fbs.reader())) { |
| 137 | 195 | .zstandard => return decodeZstandardFrame(dest, src, verify_checksum), |
| ... | ... | @@ -153,16 +211,21 @@ pub fn decodeFrame( |
| 153 | 211 | /// |
| 154 | 212 | /// Errors returned: |
| 155 | 213 | /// - `error.BadMagic` if the first 4 bytes of `src` is not a valid magic |
| 156 | | /// number for a Zstandard or Skippable frame |
| 214 | /// number for a Zstandard or skippable frame |
| 157 | 215 | /// - `error.WindowSizeUnknown` if the frame does not have a valid window size |
| 158 | 216 | /// - `error.WindowTooLarge` if the window size is larger than |
| 159 | 217 | /// `window_size_max` |
| 218 | /// - `error.ContentSizeTooLarge` if the frame header indicates a content size |
| 219 | /// that is larger than `std.math.maxInt(usize)` |
| 160 | 220 | /// - `error.DictionaryIdFlagUnsupported` if the frame uses a dictionary |
| 161 | 221 | /// - `error.ChecksumFailure` if `verify_checksum` is true and the frame |
| 162 | 222 | /// contains a checksum that does not match the checksum of the decompressed |
| 163 | 223 | /// data |
| 164 | | /// - `error.ReservedBitSet` if the reserved bit of the frame header is set |
| 224 | /// - `error.ReservedBitSet` if any of the reserved bits of the frame header |
| 225 | /// are set |
| 165 | 226 | /// - `error.EndOfStream` if `src` does not contain a complete frame |
| 227 | /// - `error.BadContentSize` if the content size declared by the frame does |
| 228 | /// not equal the actual size of decompressed data |
| 166 | 229 | /// - `error.OutOfMemory` if `allocator` cannot allocate enough memory |
| 167 | 230 | /// - an error in `block.Error` if there are errors decoding a block |
| 168 | 231 | /// - `error.SkippableSizeTooLarge` if the frame is skippable and reports a |
| ... | ... | @@ -173,12 +236,18 @@ pub fn decodeFrameArrayList( |
| 173 | 236 | src: []const u8, |
| 174 | 237 | verify_checksum: bool, |
| 175 | 238 | window_size_max: usize, |
| 176 | | ) !usize { |
| 239 | ) (error{ BadMagic, OutOfMemory, SkippableSizeTooLarge } || FrameContext.Error || FrameError)!usize { |
| 177 | 240 | var fbs = std.io.fixedBufferStream(src); |
| 178 | 241 | const reader = fbs.reader(); |
| 179 | 242 | const magic = try reader.readIntLittle(u32); |
| 180 | 243 | switch (try frameType(magic)) { |
| 181 | | .zstandard => return decodeZstandardFrameArrayList(allocator, dest, src, verify_checksum, window_size_max), |
| 244 | .zstandard => return decodeZstandardFrameArrayList( |
| 245 | allocator, |
| 246 | dest, |
| 247 | src, |
| 248 | verify_checksum, |
| 249 | window_size_max, |
| 250 | ), |
| 182 | 251 | .skippable => { |
| 183 | 252 | const content_size = try fbs.reader().readIntLittle(u32); |
| 184 | 253 | if (content_size > std.math.maxInt(usize) - 8) return error.SkippableSizeTooLarge; |
| ... | ... | @@ -211,7 +280,10 @@ const FrameError = error{ |
| 211 | 280 | /// uncompressed content size |
| 212 | 281 | /// - `error.ContentTooLarge` if `dest` is smaller than the uncompressed data |
| 213 | 282 | /// size declared by the frame header |
| 283 | /// - `error.WindowSizeUnknown` if the frame does not have a valid window size |
| 214 | 284 | /// - `error.DictionaryIdFlagUnsupported` if the frame uses a dictionary |
| 285 | /// - `error.ContentSizeTooLarge` if the frame header indicates a content size |
| 286 | /// that is larger than `std.math.maxInt(usize)` |
| 215 | 287 | /// - `error.ChecksumFailure` if `verify_checksum` is true and the frame |
| 216 | 288 | /// contains a checksum that does not match the checksum of the decompressed |
| 217 | 289 | /// data |
| ... | ... | @@ -239,7 +311,11 @@ pub fn decodeZstandardFrame( |
| 239 | 311 | var source = fbs.reader(); |
| 240 | 312 | const frame_header = try decodeZstandardHeader(source); |
| 241 | 313 | consumed_count += fbs.pos; |
| 242 | | break :context FrameContext.init(frame_header, std.math.maxInt(usize), verify_checksum) catch |err| switch (err) { |
| 314 | break :context FrameContext.init( |
| 315 | frame_header, |
| 316 | std.math.maxInt(usize), |
| 317 | verify_checksum, |
| 318 | ) catch |err| switch (err) { |
| 243 | 319 | error.WindowTooLarge => unreachable, |
| 244 | 320 | inline else => |e| return e, |
| 245 | 321 | }; |
| ... | ... | @@ -260,7 +336,8 @@ pub fn decodeZStandardFrameBlocks( |
| 260 | 336 | src: []const u8, |
| 261 | 337 | frame_context: *FrameContext, |
| 262 | 338 | ) (error{ ContentTooLarge, UnknownContentSizeUnsupported } || FrameError)!ReadWriteCount { |
| 263 | | const content_size = frame_context.content_size orelse return error.UnknownContentSizeUnsupported; |
| 339 | const content_size = frame_context.content_size orelse |
| 340 | return error.UnknownContentSizeUnsupported; |
| 264 | 341 | if (dest.len < content_size) return error.ContentTooLarge; |
| 265 | 342 | |
| 266 | 343 | var consumed_count: usize = 0; |
| ... | ... | @@ -304,14 +381,19 @@ pub const FrameContext = struct { |
| 304 | 381 | /// |
| 305 | 382 | /// Errors returned: |
| 306 | 383 | /// - `error.DictionaryIdFlagUnsupported` if the frame uses a dictionary |
| 307 | | /// - `error.WindowSizeUnknown` if the frame does not have a valid window size |
| 308 | | /// - `error.WindowTooLarge` if the window size is larger than `window_size_max` |
| 384 | /// - `error.WindowSizeUnknown` if the frame does not have a valid window |
| 385 | /// size |
| 386 | /// - `error.WindowTooLarge` if the window size is larger than |
| 387 | /// `window_size_max` |
| 388 | /// - `error.ContentSizeTooLarge` if the frame header indicates a content |
| 389 | /// size larger than `std.math.maxInt(usize)` |
| 309 | 390 | pub fn init( |
| 310 | | frame_header: frame.Zstandard.Header, |
| 391 | frame_header: ZstandardHeader, |
| 311 | 392 | window_size_max: usize, |
| 312 | 393 | verify_checksum: bool, |
| 313 | 394 | ) Error!FrameContext { |
| 314 | | if (frame_header.descriptor.dictionary_id_flag != 0) return error.DictionaryIdFlagUnsupported; |
| 395 | if (frame_header.descriptor.dictionary_id_flag != 0) |
| 396 | return error.DictionaryIdFlagUnsupported; |
| 315 | 397 | |
| 316 | 398 | const window_size_raw = frameWindowSize(frame_header) orelse return error.WindowSizeUnknown; |
| 317 | 399 | const window_size = if (window_size_raw > window_size_max) |
| ... | ... | @@ -319,7 +401,8 @@ pub const FrameContext = struct { |
| 319 | 401 | else |
| 320 | 402 | @intCast(usize, window_size_raw); |
| 321 | 403 | |
| 322 | | const should_compute_checksum = frame_header.descriptor.content_checksum_flag and verify_checksum; |
| 404 | const should_compute_checksum = |
| 405 | frame_header.descriptor.content_checksum_flag and verify_checksum; |
| 323 | 406 | |
| 324 | 407 | const content_size = if (frame_header.content_size) |size| |
| 325 | 408 | std.math.cast(usize, size) orelse return error.ContentSizeTooLarge |
| ... | ... | @@ -345,6 +428,8 @@ pub const FrameContext = struct { |
| 345 | 428 | /// - `error.WindowTooLarge` if the window size is larger than |
| 346 | 429 | /// `window_size_max` |
| 347 | 430 | /// - `error.DictionaryIdFlagUnsupported` if the frame uses a dictionary |
| 431 | /// - `error.ContentSizeTooLarge` if the frame header indicates a content size |
| 432 | /// that is larger than `std.math.maxInt(usize)` |
| 348 | 433 | /// - `error.ChecksumFailure` if `verify_checksum` is true and the frame |
| 349 | 434 | /// contains a checksum that does not match the checksum of the decompressed |
| 350 | 435 | /// data |
| ... | ... | @@ -441,8 +526,6 @@ pub fn decodeZstandardFrameBlocksArrayList( |
| 441 | 526 | return consumed_count; |
| 442 | 527 | } |
| 443 | 528 | |
| 444 | | /// Convenience wrapper for decoding all blocks in a frame; see |
| 445 | | /// `decodeZStandardFrameBlocks()`. |
| 446 | 529 | fn decodeFrameBlocksInner( |
| 447 | 530 | dest: []u8, |
| 448 | 531 | src: []const u8, |
| ... | ... | @@ -459,7 +542,7 @@ fn decodeFrameBlocksInner( |
| 459 | 542 | var bytes_read: usize = 3; |
| 460 | 543 | defer consumed_count.* += bytes_read; |
| 461 | 544 | var decode_state = block.DecodeState.init(&literal_fse_data, &match_fse_data, &offset_fse_data); |
| 462 | | var written_count: usize = 0; |
| 545 | var count: usize = 0; |
| 463 | 546 | while (true) : ({ |
| 464 | 547 | block_header = try block.decodeBlockHeaderSlice(src[bytes_read..]); |
| 465 | 548 | bytes_read += 3; |
| ... | ... | @@ -471,18 +554,18 @@ fn decodeFrameBlocksInner( |
| 471 | 554 | &decode_state, |
| 472 | 555 | &bytes_read, |
| 473 | 556 | block_size_max, |
| 474 | | written_count, |
| 557 | count, |
| 475 | 558 | ); |
| 476 | | if (hash) |hash_state| hash_state.update(dest[written_count .. written_count + written_size]); |
| 477 | | written_count += written_size; |
| 559 | if (hash) |hash_state| hash_state.update(dest[count .. count + written_size]); |
| 560 | count += written_size; |
| 478 | 561 | if (block_header.last_block) break; |
| 479 | 562 | } |
| 480 | | return written_count; |
| 563 | return count; |
| 481 | 564 | } |
| 482 | 565 | |
| 483 | 566 | /// Decode the header of a skippable frame. The first four bytes of `src` must |
| 484 | | /// be a valid magic number for a Skippable frame. |
| 485 | | pub fn decodeSkippableHeader(src: *const [8]u8) frame.Skippable.Header { |
| 567 | /// be a valid magic number for a skippable frame. |
| 568 | pub fn decodeSkippableHeader(src: *const [8]u8) SkippableHeader { |
| 486 | 569 | const magic = readInt(u32, src[0..4]); |
| 487 | 570 | assert(isSkippableMagic(magic)); |
| 488 | 571 | const frame_size = readInt(u32, src[4..8]); |
| ... | ... | @@ -494,7 +577,7 @@ pub fn decodeSkippableHeader(src: *const [8]u8) frame.Skippable.Header { |
| 494 | 577 | |
| 495 | 578 | /// Returns the window size required to decompress a frame, or `null` if it |
| 496 | 579 | /// cannot be determined (which indicates a malformed frame header). |
| 497 | | pub fn frameWindowSize(header: frame.Zstandard.Header) ?u64 { |
| 580 | pub fn frameWindowSize(header: ZstandardHeader) ?u64 { |
| 498 | 581 | if (header.window_descriptor) |descriptor| { |
| 499 | 582 | const exponent = (descriptor & 0b11111000) >> 3; |
| 500 | 583 | const mantissa = descriptor & 0b00000111; |
| ... | ... | @@ -508,10 +591,10 @@ pub fn frameWindowSize(header: frame.Zstandard.Header) ?u64 { |
| 508 | 591 | /// Decode the header of a Zstandard frame. |
| 509 | 592 | /// |
| 510 | 593 | /// Errors returned: |
| 511 | | /// - `error.ReservedBitSet` if the reserved bits of the header are set |
| 594 | /// - `error.ReservedBitSet` if any of the reserved bits of the header are set |
| 512 | 595 | /// - `error.EndOfStream` if `source` does not contain a complete header |
| 513 | | pub fn decodeZstandardHeader(source: anytype) error{ EndOfStream, ReservedBitSet }!frame.Zstandard.Header { |
| 514 | | const descriptor = @bitCast(frame.Zstandard.Header.Descriptor, try source.readByte()); |
| 596 | pub fn decodeZstandardHeader(source: anytype) error{ EndOfStream, ReservedBitSet }!ZstandardHeader { |
| 597 | const descriptor = @bitCast(ZstandardHeader.Descriptor, try source.readByte()); |
| 515 | 598 | |
| 516 | 599 | if (descriptor.reserved) return error.ReservedBitSet; |
| 517 | 600 | |
| ... | ... | @@ -534,7 +617,7 @@ pub fn decodeZstandardHeader(source: anytype) error{ EndOfStream, ReservedBitSet |
| 534 | 617 | if (field_size == 2) content_size.? += 256; |
| 535 | 618 | } |
| 536 | 619 | |
| 537 | | const header = frame.Zstandard.Header{ |
| 620 | const header = ZstandardHeader{ |
| 538 | 621 | .descriptor = descriptor, |
| 539 | 622 | .window_descriptor = window_descriptor, |
| 540 | 623 | .dictionary_id = dictionary_id, |