blob: 69736e8d4bb8c0ac04027f46feb7ed9fef5f4300 [file] [log] [blame]
khenaidoo106c61a2021-08-11 18:05:46 -04001// Copyright 2020+ Klaus Post. All rights reserved.
2// License information can be found in the LICENSE file.
3
4package zstd
5
6import (
7 "bytes"
8 "errors"
9 "io"
10)
11
12// HeaderMaxSize is the maximum size of a Frame and Block Header.
13// If less is sent to Header.Decode it *may* still contain enough information.
14const HeaderMaxSize = 14 + 3
15
16// Header contains information about the first frame and block within that.
17type Header struct {
18 // Window Size the window of data to keep while decoding.
19 // Will only be set if HasFCS is false.
20 WindowSize uint64
21
22 // Frame content size.
23 // Expected size of the entire frame.
24 FrameContentSize uint64
25
26 // Dictionary ID.
27 // If 0, no dictionary.
28 DictionaryID uint32
29
30 // First block information.
31 FirstBlock struct {
32 // OK will be set if first block could be decoded.
33 OK bool
34
35 // Is this the last block of a frame?
36 Last bool
37
38 // Is the data compressed?
39 // If true CompressedSize will be populated.
40 // Unfortunately DecompressedSize cannot be determined
41 // without decoding the blocks.
42 Compressed bool
43
44 // DecompressedSize is the expected decompressed size of the block.
45 // Will be 0 if it cannot be determined.
46 DecompressedSize int
47
48 // CompressedSize of the data in the block.
49 // Does not include the block header.
50 // Will be equal to DecompressedSize if not Compressed.
51 CompressedSize int
52 }
53
54 // Skippable will be true if the frame is meant to be skipped.
55 // No other information will be populated.
56 Skippable bool
57
58 // If set there is a checksum present for the block content.
59 HasCheckSum bool
60
61 // If this is true FrameContentSize will have a valid value
62 HasFCS bool
63
64 SingleSegment bool
65}
66
67// Decode the header from the beginning of the stream.
68// This will decode the frame header and the first block header if enough bytes are provided.
69// It is recommended to provide at least HeaderMaxSize bytes.
70// If the frame header cannot be read an error will be returned.
71// If there isn't enough input, io.ErrUnexpectedEOF is returned.
72// The FirstBlock.OK will indicate if enough information was available to decode the first block header.
73func (h *Header) Decode(in []byte) error {
74 if len(in) < 4 {
75 return io.ErrUnexpectedEOF
76 }
77 b, in := in[:4], in[4:]
78 if !bytes.Equal(b, frameMagic) {
79 if !bytes.Equal(b[1:4], skippableFrameMagic) || b[0]&0xf0 != 0x50 {
80 return ErrMagicMismatch
81 }
82 *h = Header{Skippable: true}
83 return nil
84 }
85 if len(in) < 1 {
86 return io.ErrUnexpectedEOF
87 }
88
89 // Clear output
90 *h = Header{}
91 fhd, in := in[0], in[1:]
92 h.SingleSegment = fhd&(1<<5) != 0
93 h.HasCheckSum = fhd&(1<<2) != 0
94
95 if fhd&(1<<3) != 0 {
96 return errors.New("reserved bit set on frame header")
97 }
98
99 // Read Window_Descriptor
100 // https://github.com/facebook/zstd/blob/dev/doc/zstd_compression_format.md#window_descriptor
101 if !h.SingleSegment {
102 if len(in) < 1 {
103 return io.ErrUnexpectedEOF
104 }
105 var wd byte
106 wd, in = in[0], in[1:]
107 windowLog := 10 + (wd >> 3)
108 windowBase := uint64(1) << windowLog
109 windowAdd := (windowBase / 8) * uint64(wd&0x7)
110 h.WindowSize = windowBase + windowAdd
111 }
112
113 // Read Dictionary_ID
114 // https://github.com/facebook/zstd/blob/dev/doc/zstd_compression_format.md#dictionary_id
115 if size := fhd & 3; size != 0 {
116 if size == 3 {
117 size = 4
118 }
119 if len(in) < int(size) {
120 return io.ErrUnexpectedEOF
121 }
122 b, in = in[:size], in[size:]
123 if b == nil {
124 return io.ErrUnexpectedEOF
125 }
126 switch size {
127 case 1:
128 h.DictionaryID = uint32(b[0])
129 case 2:
130 h.DictionaryID = uint32(b[0]) | (uint32(b[1]) << 8)
131 case 4:
132 h.DictionaryID = uint32(b[0]) | (uint32(b[1]) << 8) | (uint32(b[2]) << 16) | (uint32(b[3]) << 24)
133 }
134 }
135
136 // Read Frame_Content_Size
137 // https://github.com/facebook/zstd/blob/dev/doc/zstd_compression_format.md#frame_content_size
138 var fcsSize int
139 v := fhd >> 6
140 switch v {
141 case 0:
142 if h.SingleSegment {
143 fcsSize = 1
144 }
145 default:
146 fcsSize = 1 << v
147 }
148
149 if fcsSize > 0 {
150 h.HasFCS = true
151 if len(in) < fcsSize {
152 return io.ErrUnexpectedEOF
153 }
154 b, in = in[:fcsSize], in[fcsSize:]
155 if b == nil {
156 return io.ErrUnexpectedEOF
157 }
158 switch fcsSize {
159 case 1:
160 h.FrameContentSize = uint64(b[0])
161 case 2:
162 // When FCS_Field_Size is 2, the offset of 256 is added.
163 h.FrameContentSize = uint64(b[0]) | (uint64(b[1]) << 8) + 256
164 case 4:
165 h.FrameContentSize = uint64(b[0]) | (uint64(b[1]) << 8) | (uint64(b[2]) << 16) | (uint64(b[3]) << 24)
166 case 8:
167 d1 := uint32(b[0]) | (uint32(b[1]) << 8) | (uint32(b[2]) << 16) | (uint32(b[3]) << 24)
168 d2 := uint32(b[4]) | (uint32(b[5]) << 8) | (uint32(b[6]) << 16) | (uint32(b[7]) << 24)
169 h.FrameContentSize = uint64(d1) | (uint64(d2) << 32)
170 }
171 }
172
173 // Frame Header done, we will not fail from now on.
174 if len(in) < 3 {
175 return nil
176 }
177 tmp := in[:3]
178 bh := uint32(tmp[0]) | (uint32(tmp[1]) << 8) | (uint32(tmp[2]) << 16)
179 h.FirstBlock.Last = bh&1 != 0
180 blockType := blockType((bh >> 1) & 3)
181 // find size.
182 cSize := int(bh >> 3)
183 switch blockType {
184 case blockTypeReserved:
185 return nil
186 case blockTypeRLE:
187 h.FirstBlock.Compressed = true
188 h.FirstBlock.DecompressedSize = cSize
189 h.FirstBlock.CompressedSize = 1
190 case blockTypeCompressed:
191 h.FirstBlock.Compressed = true
192 h.FirstBlock.CompressedSize = cSize
193 case blockTypeRaw:
194 h.FirstBlock.DecompressedSize = cSize
195 h.FirstBlock.CompressedSize = cSize
196 default:
197 panic("Invalid block type")
198 }
199
200 h.FirstBlock.OK = true
201 return nil
202}