1// Copyright 2019+ Klaus Post. All rights reserved. 2// License information can be found in the LICENSE file. 3// Based on work by Yann Collet, released under BSD License. 4 5package zstd 6 7import ( 8 "errors" 9 "fmt" 10 "io" 11 "math" 12 "math/bits" 13) 14 15type frameHeader struct { 16 ContentSize uint64 17 WindowSize uint32 18 SingleSegment bool 19 Checksum bool 20 DictID uint32 // Not stored. 21} 22 23const maxHeaderSize = 14 24 25func (f frameHeader) appendTo(dst []byte) ([]byte, error) { 26 dst = append(dst, frameMagic...) 27 var fhd uint8 28 if f.Checksum { 29 fhd |= 1 << 2 30 } 31 if f.SingleSegment { 32 fhd |= 1 << 5 33 } 34 var fcs uint8 35 if f.ContentSize >= 256 { 36 fcs++ 37 } 38 if f.ContentSize >= 65536+256 { 39 fcs++ 40 } 41 if f.ContentSize >= 0xffffffff { 42 fcs++ 43 } 44 fhd |= fcs << 6 45 46 dst = append(dst, fhd) 47 if !f.SingleSegment { 48 const winLogMin = 10 49 windowLog := (bits.Len32(f.WindowSize-1) - winLogMin) << 3 50 dst = append(dst, uint8(windowLog)) 51 } 52 if f.SingleSegment && f.ContentSize == 0 { 53 return nil, errors.New("single segment, but no size set") 54 } 55 switch fcs { 56 case 0: 57 if f.SingleSegment { 58 dst = append(dst, uint8(f.ContentSize)) 59 } 60 // Unless SingleSegment is set, framessizes < 256 are nto stored. 61 case 1: 62 f.ContentSize -= 256 63 dst = append(dst, uint8(f.ContentSize), uint8(f.ContentSize>>8)) 64 case 2: 65 dst = append(dst, uint8(f.ContentSize), uint8(f.ContentSize>>8), uint8(f.ContentSize>>16), uint8(f.ContentSize>>24)) 66 case 3: 67 dst = append(dst, uint8(f.ContentSize), uint8(f.ContentSize>>8), uint8(f.ContentSize>>16), uint8(f.ContentSize>>24), 68 uint8(f.ContentSize>>32), uint8(f.ContentSize>>40), uint8(f.ContentSize>>48), uint8(f.ContentSize>>56)) 69 default: 70 panic("invalid fcs") 71 } 72 return dst, nil 73} 74 75const skippableFrameHeader = 4 + 4 76 77// calcSkippableFrame will return a total size to be added for written 78// to be divisible by multiple. 79// The value will always be > skippableFrameHeader. 80// The function will panic if written < 0 or wantMultiple <= 0. 81func calcSkippableFrame(written, wantMultiple int64) int { 82 if wantMultiple <= 0 { 83 panic("wantMultiple <= 0") 84 } 85 if written < 0 { 86 panic("written < 0") 87 } 88 leftOver := written % wantMultiple 89 if leftOver == 0 { 90 return 0 91 } 92 toAdd := wantMultiple - leftOver 93 for toAdd < skippableFrameHeader { 94 toAdd += wantMultiple 95 } 96 return int(toAdd) 97} 98 99// skippableFrame will add a skippable frame with a total size of bytes. 100// total should be >= skippableFrameHeader and < math.MaxUint32. 101func skippableFrame(dst []byte, total int, r io.Reader) ([]byte, error) { 102 if total == 0 { 103 return dst, nil 104 } 105 if total < skippableFrameHeader { 106 return dst, fmt.Errorf("requested skippable frame (%d) < 8", total) 107 } 108 if int64(total) > math.MaxUint32 { 109 return dst, fmt.Errorf("requested skippable frame (%d) > max uint32", total) 110 } 111 dst = append(dst, 0x50, 0x2a, 0x4d, 0x18) 112 f := uint32(total - skippableFrameHeader) 113 dst = append(dst, uint8(f), uint8(f>>8), uint8(f>>16), uint8(f>>24)) 114 start := len(dst) 115 dst = append(dst, make([]byte, f)...) 116 _, err := io.ReadFull(r, dst[start:]) 117 return dst, err 118} 119