decodeheader.go 6.5 KB

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