frameenc.go 3.3 KB

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