blob: 4479cfe18b25513511cc1b098adc9c2f24d54ea6 [file] [log] [blame]
Scott Bakered4efab2020-01-13 19:12:25 -08001// 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}