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