| ... | ... | @@ -1,5 +1,6 @@ |
| 1 | 1 | const std = @import("std"); |
| 2 | 2 | const assert = std.debug.assert; |
| 3 | const Allocator = std.mem.Allocator; |
| 3 | 4 | |
| 4 | 5 | const types = @import("types.zig"); |
| 5 | 6 | const frame = types.frame; |
| ... | ... | @@ -32,6 +33,14 @@ pub fn isSkippableMagic(magic: u32) bool { |
| 32 | 33 | /// - `error.EndOfStream` if `source` contains fewer than 4 bytes |
| 33 | 34 | pub fn decodeFrameType(source: anytype) error{ BadMagic, EndOfStream }!frame.Kind { |
| 34 | 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 | 44 | return if (magic == frame.ZStandard.magic_number) |
| 36 | 45 | .zstandard |
| 37 | 46 | else if (isSkippableMagic(magic)) |
| ... | ... | @@ -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 | 140 | /// Returns the frame checksum corresponding to the data fed into `hasher` |
| 82 | 141 | pub fn computeChecksum(hasher: *std.hash.XxHash64) u32 { |
| 83 | 142 | const hash = hasher.final(); |
| ... | ... | @@ -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 |
| 185 | | /// `decodeZStandardFrame()`. `allocator` is used to allocate both the returned |
| 186 | | /// slice and internal buffers used during decoding. The first four bytes of |
| 187 | | /// `src` must be the magic number for a Zstandard frame. |
| 243 | /// Decode a Zstandard from from `src` and return the decompressed bytes and the |
| 244 | /// number of bytes read; see `decodeZStandardFrame()`. `allocator` is used to |
| 245 | /// allocate both the returned slice and internal buffers used during decoding. |
| 246 | /// The first four bytes of `src` must be the magic number for a Zstandard |
| 247 | /// frame. |
| 188 | 248 | /// |
| 189 | 249 | /// Errors returned: |
| 190 | 250 | /// - `error.WindowSizeUnknown` if the frame does not have a valid window size |
| ... | ... | @@ -200,11 +260,11 @@ pub const FrameContext = struct { |
| 200 | 260 | /// - `error.OutOfMemory` if `allocator` cannot allocate enough memory |
| 201 | 261 | /// - an error in `block.Error` if there are errors decoding a block |
| 202 | 262 | pub fn decodeZStandardFrameAlloc( |
| 203 | | allocator: std.mem.Allocator, |
| 263 | allocator: Allocator, |
| 204 | 264 | src: []const u8, |
| 205 | 265 | verify_checksum: bool, |
| 206 | 266 | window_size_max: usize, |
| 207 | | ) (error{OutOfMemory} || FrameContext.Error || FrameError)![]u8 { |
| 267 | ) (error{OutOfMemory} || FrameContext.Error || FrameError)!DecodeResult { |
| 208 | 268 | var result = std.ArrayList(u8).init(allocator); |
| 209 | 269 | assert(readInt(u32, src[0..4]) == frame.ZStandard.magic_number); |
| 210 | 270 | var consumed_count: usize = 4; |
| ... | ... | @@ -258,7 +318,7 @@ pub fn decodeZStandardFrameAlloc( |
| 258 | 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 | 324 | /// Convenience wrapper for decoding all blocks in a frame; see `decodeBlock()`. |