123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137 |
- // Copyright 2019+ Klaus Post. All rights reserved.
- // License information can be found in the LICENSE file.
- // Based on work by Yann Collet, released under BSD License.
- package zstd
- import (
- "encoding/binary"
- "fmt"
- "io"
- "math"
- "math/bits"
- )
- type frameHeader struct {
- ContentSize uint64
- WindowSize uint32
- SingleSegment bool
- Checksum bool
- DictID uint32
- }
- const maxHeaderSize = 14
- func (f frameHeader) appendTo(dst []byte) []byte {
- dst = append(dst, frameMagic...)
- var fhd uint8
- if f.Checksum {
- fhd |= 1 << 2
- }
- if f.SingleSegment {
- fhd |= 1 << 5
- }
- var dictIDContent []byte
- if f.DictID > 0 {
- var tmp [4]byte
- if f.DictID < 256 {
- fhd |= 1
- tmp[0] = uint8(f.DictID)
- dictIDContent = tmp[:1]
- } else if f.DictID < 1<<16 {
- fhd |= 2
- binary.LittleEndian.PutUint16(tmp[:2], uint16(f.DictID))
- dictIDContent = tmp[:2]
- } else {
- fhd |= 3
- binary.LittleEndian.PutUint32(tmp[:4], f.DictID)
- dictIDContent = tmp[:4]
- }
- }
- var fcs uint8
- if f.ContentSize >= 256 {
- fcs++
- }
- if f.ContentSize >= 65536+256 {
- fcs++
- }
- if f.ContentSize >= 0xffffffff {
- fcs++
- }
- fhd |= fcs << 6
- dst = append(dst, fhd)
- if !f.SingleSegment {
- const winLogMin = 10
- windowLog := (bits.Len32(f.WindowSize-1) - winLogMin) << 3
- dst = append(dst, uint8(windowLog))
- }
- if f.DictID > 0 {
- dst = append(dst, dictIDContent...)
- }
- switch fcs {
- case 0:
- if f.SingleSegment {
- dst = append(dst, uint8(f.ContentSize))
- }
- // Unless SingleSegment is set, framessizes < 256 are not stored.
- case 1:
- f.ContentSize -= 256
- dst = append(dst, uint8(f.ContentSize), uint8(f.ContentSize>>8))
- case 2:
- dst = append(dst, uint8(f.ContentSize), uint8(f.ContentSize>>8), uint8(f.ContentSize>>16), uint8(f.ContentSize>>24))
- case 3:
- dst = append(dst, uint8(f.ContentSize), uint8(f.ContentSize>>8), uint8(f.ContentSize>>16), uint8(f.ContentSize>>24),
- uint8(f.ContentSize>>32), uint8(f.ContentSize>>40), uint8(f.ContentSize>>48), uint8(f.ContentSize>>56))
- default:
- panic("invalid fcs")
- }
- return dst
- }
- const skippableFrameHeader = 4 + 4
- // calcSkippableFrame will return a total size to be added for written
- // to be divisible by multiple.
- // The value will always be > skippableFrameHeader.
- // The function will panic if written < 0 or wantMultiple <= 0.
- func calcSkippableFrame(written, wantMultiple int64) int {
- if wantMultiple <= 0 {
- panic("wantMultiple <= 0")
- }
- if written < 0 {
- panic("written < 0")
- }
- leftOver := written % wantMultiple
- if leftOver == 0 {
- return 0
- }
- toAdd := wantMultiple - leftOver
- for toAdd < skippableFrameHeader {
- toAdd += wantMultiple
- }
- return int(toAdd)
- }
- // skippableFrame will add a skippable frame with a total size of bytes.
- // total should be >= skippableFrameHeader and < math.MaxUint32.
- func skippableFrame(dst []byte, total int, r io.Reader) ([]byte, error) {
- if total == 0 {
- return dst, nil
- }
- if total < skippableFrameHeader {
- return dst, fmt.Errorf("requested skippable frame (%d) < 8", total)
- }
- if int64(total) > math.MaxUint32 {
- return dst, fmt.Errorf("requested skippable frame (%d) > max uint32", total)
- }
- dst = append(dst, 0x50, 0x2a, 0x4d, 0x18)
- f := uint32(total - skippableFrameHeader)
- dst = append(dst, uint8(f), uint8(f>>8), uint8(f>>16), uint8(f>>24))
- start := len(dst)
- dst = append(dst, make([]byte, f)...)
- _, err := io.ReadFull(r, dst[start:])
- return dst, err
- }
|