Платформа ЦРНП "Мирокод" для разработки проектов
https://git.mirocod.ru
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
115 lines
2.9 KiB
115 lines
2.9 KiB
// 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 ( |
|
"fmt" |
|
"io" |
|
"math" |
|
"math/bits" |
|
) |
|
|
|
type frameHeader struct { |
|
ContentSize uint64 |
|
WindowSize uint32 |
|
SingleSegment bool |
|
Checksum bool |
|
DictID uint32 // Not stored. |
|
} |
|
|
|
const maxHeaderSize = 14 |
|
|
|
func (f frameHeader) appendTo(dst []byte) ([]byte, error) { |
|
dst = append(dst, frameMagic...) |
|
var fhd uint8 |
|
if f.Checksum { |
|
fhd |= 1 << 2 |
|
} |
|
if f.SingleSegment { |
|
fhd |= 1 << 5 |
|
} |
|
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)) |
|
} |
|
|
|
switch fcs { |
|
case 0: |
|
if f.SingleSegment { |
|
dst = append(dst, uint8(f.ContentSize)) |
|
} |
|
// Unless SingleSegment is set, framessizes < 256 are nto 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, nil |
|
} |
|
|
|
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 |
|
}
|
|
|