github.com/bir3/gocompiler@v0.9.2202/extra/compress/zstd/decodeheader.go (about)

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