1
// Copyright 2020+ Klaus Post. All rights reserved.
2
// License information can be found in the LICENSE file.
13
// HeaderMaxSize is the maximum size of a Frame and Block Header.
14
// If less is sent to Header.Decode it *may* still contain enough information.
15
const HeaderMaxSize = 14 + 3
17
// Header contains information about the first frame and block within that.
19
// SingleSegment specifies whether the data is to be decompressed into a
20
// single contiguous memory segment.
21
// It implies that WindowSize is invalid and that FrameContentSize is valid.
24
// WindowSize is the window of data to keep while decoding.
25
// Will only be set if SingleSegment is false.
29
// If 0, no dictionary.
32
// HasFCS specifies whether FrameContentSize has a valid value.
35
// FrameContentSize is the expected uncompressed size of the entire frame.
36
FrameContentSize uint64
38
// Skippable will be true if the frame is meant to be skipped.
39
// This implies that FirstBlock.OK is false.
42
// SkippableID is the user-specific ID for the skippable frame.
43
// Valid values are between 0 to 15, inclusive.
46
// SkippableSize is the length of the user data to skip following
50
// HeaderSize is the raw size of the frame header.
52
// For normal frames, it includes the size of the magic number and
53
// the size of the header (per section 3.1.1.1).
54
// It does not include the size for any data blocks (section 3.1.1.2) nor
55
// the size for the trailing content checksum.
57
// For skippable frames, this counts the size of the magic number
58
// along with the size of the size field of the payload.
59
// It does not include the size of the skippable payload itself.
60
// The total frame size is the HeaderSize plus the SkippableSize.
63
// First block information.
65
// OK will be set if first block could be decoded.
68
// Is this the last block of a frame?
71
// Is the data compressed?
72
// If true CompressedSize will be populated.
73
// Unfortunately DecompressedSize cannot be determined
74
// without decoding the blocks.
77
// DecompressedSize is the expected decompressed size of the block.
78
// Will be 0 if it cannot be determined.
81
// CompressedSize of the data in the block.
82
// Does not include the block header.
83
// Will be equal to DecompressedSize if not Compressed.
87
// If set there is a checksum present for the block content.
88
// The checksum field at the end is always 4 bytes long.
92
// Decode the header from the beginning of the stream.
93
// This will decode the frame header and the first block header if enough bytes are provided.
94
// It is recommended to provide at least HeaderMaxSize bytes.
95
// If the frame header cannot be read an error will be returned.
96
// If there isn't enough input, io.ErrUnexpectedEOF is returned.
97
// The FirstBlock.OK will indicate if enough information was available to decode the first block header.
98
func (h *Header) Decode(in []byte) error {
101
return io.ErrUnexpectedEOF
104
b, in := in[:4], in[4:]
105
if !bytes.Equal(b, frameMagic) {
106
if !bytes.Equal(b[1:4], skippableFrameMagic) || b[0]&0xf0 != 0x50 {
107
return ErrMagicMismatch
110
return io.ErrUnexpectedEOF
114
h.SkippableID = int(b[0] & 0xf)
115
h.SkippableSize = binary.LittleEndian.Uint32(in)
119
// Read Window_Descriptor
120
// https://github.com/facebook/zstd/blob/dev/doc/zstd_compression_format.md#window_descriptor
122
return io.ErrUnexpectedEOF
124
fhd, in := in[0], in[1:]
126
h.SingleSegment = fhd&(1<<5) != 0
127
h.HasCheckSum = fhd&(1<<2) != 0
129
return errors.New("reserved bit set on frame header")
132
if !h.SingleSegment {
134
return io.ErrUnexpectedEOF
137
wd, in = in[0], in[1:]
139
windowLog := 10 + (wd >> 3)
140
windowBase := uint64(1) << windowLog
141
windowAdd := (windowBase / 8) * uint64(wd&0x7)
142
h.WindowSize = windowBase + windowAdd
145
// Read Dictionary_ID
146
// https://github.com/facebook/zstd/blob/dev/doc/zstd_compression_format.md#dictionary_id
147
if size := fhd & 3; size != 0 {
151
if len(in) < int(size) {
152
return io.ErrUnexpectedEOF
154
b, in = in[:size], in[size:]
155
h.HeaderSize += int(size)
158
h.DictionaryID = uint32(b[0])
160
h.DictionaryID = uint32(b[0]) | (uint32(b[1]) << 8)
162
h.DictionaryID = uint32(b[0]) | (uint32(b[1]) << 8) | (uint32(b[2]) << 16) | (uint32(b[3]) << 24)
166
// Read Frame_Content_Size
167
// https://github.com/facebook/zstd/blob/dev/doc/zstd_compression_format.md#frame_content_size
181
if len(in) < fcsSize {
182
return io.ErrUnexpectedEOF
184
b, in = in[:fcsSize], in[fcsSize:]
185
h.HeaderSize += int(fcsSize)
188
h.FrameContentSize = uint64(b[0])
190
// When FCS_Field_Size is 2, the offset of 256 is added.
191
h.FrameContentSize = uint64(b[0]) | (uint64(b[1]) << 8) + 256
193
h.FrameContentSize = uint64(b[0]) | (uint64(b[1]) << 8) | (uint64(b[2]) << 16) | (uint64(b[3]) << 24)
195
d1 := uint32(b[0]) | (uint32(b[1]) << 8) | (uint32(b[2]) << 16) | (uint32(b[3]) << 24)
196
d2 := uint32(b[4]) | (uint32(b[5]) << 8) | (uint32(b[6]) << 16) | (uint32(b[7]) << 24)
197
h.FrameContentSize = uint64(d1) | (uint64(d2) << 32)
201
// Frame Header done, we will not fail from now on.
206
bh := uint32(tmp[0]) | (uint32(tmp[1]) << 8) | (uint32(tmp[2]) << 16)
207
h.FirstBlock.Last = bh&1 != 0
208
blockType := blockType((bh >> 1) & 3)
210
cSize := int(bh >> 3)
212
case blockTypeReserved:
215
h.FirstBlock.Compressed = true
216
h.FirstBlock.DecompressedSize = cSize
217
h.FirstBlock.CompressedSize = 1
218
case blockTypeCompressed:
219
h.FirstBlock.Compressed = true
220
h.FirstBlock.CompressedSize = cSize
222
h.FirstBlock.DecompressedSize = cSize
223
h.FirstBlock.CompressedSize = cSize
225
panic("Invalid block type")
228
h.FirstBlock.OK = true