| ... | @@ -1,5 +1,6 @@ | ... | @@ -1,5 +1,6 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | const assert = std.debug.assert; | 2 | const assert = std.debug.assert; |
| | 3 | const Allocator = std.mem.Allocator; |
| 3 | | 4 | |
| 4 | const types = @import("types.zig"); | 5 | const types = @import("types.zig"); |
| 5 | const frame = types.frame; | 6 | const frame = types.frame; |
| ... | @@ -32,6 +33,14 @@ pub fn isSkippableMagic(magic: u32) bool { | ... | @@ -32,6 +33,14 @@ pub fn isSkippableMagic(magic: u32) bool { |
| 32 | /// - `error.EndOfStream` if `source` contains fewer than 4 bytes | 33 | /// - `error.EndOfStream` if `source` contains fewer than 4 bytes |
| 33 | pub fn decodeFrameType(source: anytype) error{ BadMagic, EndOfStream }!frame.Kind { | 34 | pub fn decodeFrameType(source: anytype) error{ BadMagic, EndOfStream }!frame.Kind { |
| 34 | const magic = try source.readIntLittle(u32); | 35 | const magic = try source.readIntLittle(u32); |
| | 36 | return frameType(magic); |
| | 37 | } |
| | 38 | |
| | 39 | /// Returns the kind of frame associated to `magic`. |
| | 40 | /// |
| | 41 | /// Errors returned: |
| | 42 | /// - `error.BadMagic` if `magic` is not a valid magic number. |
| | 43 | pub fn frameType(magic: u32) error{BadMagic}!frame.Kind { |
| 35 | return if (magic == frame.ZStandard.magic_number) | 44 | return if (magic == frame.ZStandard.magic_number) |
| 36 | .zstandard | 45 | .zstandard |
| 37 | else if (isSkippableMagic(magic)) | 46 | else if (isSkippableMagic(magic)) |
| ... | @@ -78,6 +87,56 @@ pub fn decodeFrame( | ... | @@ -78,6 +87,56 @@ pub fn decodeFrame( |
| 78 | }; | 87 | }; |
| 79 | } | 88 | } |
| 80 | | 89 | |
| | 90 | pub const DecodeResult = struct { |
| | 91 | bytes: []u8, |
| | 92 | read_count: usize, |
| | 93 | }; |
| | 94 | pub const DecodedFrame = union(enum) { |
| | 95 | zstandard: DecodeResult, |
| | 96 | skippable: frame.Skippable.Header, |
| | 97 | }; |
| | 98 | |
| | 99 | /// Decodes the frame at the start of `src` into `dest`. Returns the number of |
| | 100 | /// bytes read from `src` and the decoded bytes for a Zstandard frame, or the |
| | 101 | /// frame header for a Skippable frame. |
| | 102 | /// |
| | 103 | /// Errors returned: |
| | 104 | /// - `error.BadMagic` if the first 4 bytes of `src` is not a valid magic |
| | 105 | /// number for a Zstandard or Skippable frame |
| | 106 | /// - `error.WindowSizeUnknown` if the frame does not have a valid window size |
| | 107 | /// - `error.WindowTooLarge` if the window size is larger than |
| | 108 | /// `window_size_max` |
| | 109 | /// - `error.DictionaryIdFlagUnsupported` if the frame uses a dictionary |
| | 110 | /// - `error.ChecksumFailure` if `verify_checksum` is true and the frame |
| | 111 | /// contains a checksum that does not match the checksum of the decompressed |
| | 112 | /// data |
| | 113 | /// - `error.ReservedBitSet` if the reserved bit of the frame header is set |
| | 114 | /// - `error.UnusedBitSet` if the unused bit of the frame header is set |
| | 115 | /// - `error.EndOfStream` if `src` does not contain a complete frame |
| | 116 | /// - `error.OutOfMemory` if `allocator` cannot allocate enough memory |
| | 117 | /// - an error in `block.Error` if there are errors decoding a block |
| | 118 | pub fn decodeFrameAlloc( |
| | 119 | allocator: Allocator, |
| | 120 | src: []const u8, |
| | 121 | verify_checksum: bool, |
| | 122 | window_size_max: usize, |
| | 123 | ) !DecodedFrame { |
| | 124 | var fbs = std.io.fixedBufferStream(src); |
| | 125 | const reader = fbs.reader(); |
| | 126 | const magic = try reader.readIntLittle(u32); |
| | 127 | return switch (try frameType(magic)) { |
| | 128 | .zstandard => .{ |
| | 129 | .zstandard = try decodeZStandardFrameAlloc(allocator, src, verify_checksum, window_size_max), |
| | 130 | }, |
| | 131 | .skippable => .{ |
| | 132 | .skippable = .{ |
| | 133 | .magic_number = magic, |
| | 134 | .frame_size = try reader.readIntLittle(u32), |
| | 135 | }, |
| | 136 | }, |
| | 137 | }; |
| | 138 | } |
| | 139 | |
| 81 | /// Returns the frame checksum corresponding to the data fed into `hasher` | 140 | /// Returns the frame checksum corresponding to the data fed into `hasher` |
| 82 | pub fn computeChecksum(hasher: *std.hash.XxHash64) u32 { | 141 | pub fn computeChecksum(hasher: *std.hash.XxHash64) u32 { |
| 83 | const hash = hasher.final(); | 142 | const hash = hasher.final(); |
| ... | @@ -181,10 +240,11 @@ pub const FrameContext = struct { | ... | @@ -181,10 +240,11 @@ pub const FrameContext = struct { |
| 181 | } | 240 | } |
| 182 | }; | 241 | }; |
| 183 | | 242 | |
| 184 | /// Decode a Zstandard from from `src` and return the decompressed bytes; see | 243 | /// Decode a Zstandard from from `src` and return the decompressed bytes and the |
| 185 | /// `decodeZStandardFrame()`. `allocator` is used to allocate both the returned | 244 | /// number of bytes read; see `decodeZStandardFrame()`. `allocator` is used to |
| 186 | /// slice and internal buffers used during decoding. The first four bytes of | 245 | /// allocate both the returned slice and internal buffers used during decoding. |
| 187 | /// `src` must be the magic number for a Zstandard frame. | 246 | /// The first four bytes of `src` must be the magic number for a Zstandard |
| | 247 | /// frame. |
| 188 | /// | 248 | /// |
| 189 | /// Errors returned: | 249 | /// Errors returned: |
| 190 | /// - `error.WindowSizeUnknown` if the frame does not have a valid window size | 250 | /// - `error.WindowSizeUnknown` if the frame does not have a valid window size |
| ... | @@ -200,11 +260,11 @@ pub const FrameContext = struct { | ... | @@ -200,11 +260,11 @@ pub const FrameContext = struct { |
| 200 | /// - `error.OutOfMemory` if `allocator` cannot allocate enough memory | 260 | /// - `error.OutOfMemory` if `allocator` cannot allocate enough memory |
| 201 | /// - an error in `block.Error` if there are errors decoding a block | 261 | /// - an error in `block.Error` if there are errors decoding a block |
| 202 | pub fn decodeZStandardFrameAlloc( | 262 | pub fn decodeZStandardFrameAlloc( |
| 203 | allocator: std.mem.Allocator, | 263 | allocator: Allocator, |
| 204 | src: []const u8, | 264 | src: []const u8, |
| 205 | verify_checksum: bool, | 265 | verify_checksum: bool, |
| 206 | window_size_max: usize, | 266 | window_size_max: usize, |
| 207 | ) (error{OutOfMemory} || FrameContext.Error || FrameError)![]u8 { | 267 | ) (error{OutOfMemory} || FrameContext.Error || FrameError)!DecodeResult { |
| 208 | var result = std.ArrayList(u8).init(allocator); | 268 | var result = std.ArrayList(u8).init(allocator); |
| 209 | assert(readInt(u32, src[0..4]) == frame.ZStandard.magic_number); | 269 | assert(readInt(u32, src[0..4]) == frame.ZStandard.magic_number); |
| 210 | var consumed_count: usize = 4; | 270 | var consumed_count: usize = 4; |
| ... | @@ -258,7 +318,7 @@ pub fn decodeZStandardFrameAlloc( | ... | @@ -258,7 +318,7 @@ pub fn decodeZStandardFrameAlloc( |
| 258 | if (checksum != computeChecksum(hasher)) return error.ChecksumFailure; | 318 | if (checksum != computeChecksum(hasher)) return error.ChecksumFailure; |
| 259 | } | 319 | } |
| 260 | } | 320 | } |
| 261 | return result.toOwnedSlice(); | 321 | return DecodeResult{ .bytes = try result.toOwnedSlice(), .read_count = consumed_count }; |
| 262 | } | 322 | } |
| 263 | | 323 | |
| 264 | /// Convenience wrapper for decoding all blocks in a frame; see `decodeBlock()`. | 324 | /// Convenience wrapper for decoding all blocks in a frame; see `decodeBlock()`. |