frameenc.go 2.9 KB

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