decodeheader.go 5.3 KB

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