Pragya Arya | 324337e | 2020-02-20 14:35:08 +0530 | [diff] [blame] | 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 | |
| 5 | package zstd |
| 6 | |
| 7 | import ( |
| 8 | "fmt" |
| 9 | "io" |
| 10 | "math" |
| 11 | "math/bits" |
| 12 | ) |
| 13 | |
| 14 | type frameHeader struct { |
| 15 | ContentSize uint64 |
| 16 | WindowSize uint32 |
| 17 | SingleSegment bool |
| 18 | Checksum bool |
| 19 | DictID uint32 // Not stored. |
| 20 | } |
| 21 | |
| 22 | const maxHeaderSize = 14 |
| 23 | |
| 24 | func (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 | |
| 72 | const 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. |
| 78 | func 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. |
| 98 | func 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 | } |