snappy.go 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132
  1. package snappy
  2. import (
  3. "bytes"
  4. "encoding/binary"
  5. "errors"
  6. master "github.com/golang/snappy"
  7. )
  8. const (
  9. sizeOffset = 16
  10. sizeBytes = 4
  11. )
  12. var (
  13. xerialHeader = []byte{130, 83, 78, 65, 80, 80, 89, 0}
  14. // This is xerial version 1 and minimally compatible with version 1
  15. xerialVersionInfo = []byte{0, 0, 0, 1, 0, 0, 0, 1}
  16. // ErrMalformed is returned by the decoder when the xerial framing
  17. // is malformed
  18. ErrMalformed = errors.New("malformed xerial framing")
  19. )
  20. func min(x, y int) int {
  21. if x < y {
  22. return x
  23. }
  24. return y
  25. }
  26. // Encode encodes data as snappy with no framing header.
  27. func Encode(src []byte) []byte {
  28. return master.Encode(nil, src)
  29. }
  30. // EncodeStream *appends* to the specified 'dst' the compressed
  31. // 'src' in xerial framing format. If 'dst' does not have enough
  32. // capacity, then a new slice will be allocated. If 'dst' has
  33. // non-zero length, then if *must* have been built using this function.
  34. func EncodeStream(dst, src []byte) []byte {
  35. if len(dst) == 0 {
  36. dst = append(dst, xerialHeader...)
  37. dst = append(dst, xerialVersionInfo...)
  38. }
  39. // Snappy encode in blocks of maximum 32KB
  40. var (
  41. max = len(src)
  42. blockSize = 32 * 1024
  43. pos = 0
  44. chunk []byte
  45. )
  46. for pos < max {
  47. newPos := min(pos + blockSize, max)
  48. chunk = master.Encode(chunk[:cap(chunk)], src[pos:newPos])
  49. // First encode the compressed size (big-endian)
  50. // Put* panics if the buffer is too small, so pad 4 bytes first
  51. origLen := len(dst)
  52. dst = append(dst, dst[0:4]...)
  53. binary.BigEndian.PutUint32(dst[origLen:], uint32(len(chunk)))
  54. // And now the compressed data
  55. dst = append(dst, chunk...)
  56. pos = newPos
  57. }
  58. workspacePool.Put(chunk)
  59. return dst
  60. }
  61. // Decode decodes snappy data whether it is traditional unframed
  62. // or includes the xerial framing format.
  63. func Decode(src []byte) ([]byte, error) {
  64. return DecodeInto(nil, src)
  65. }
  66. // DecodeInto decodes snappy data whether it is traditional unframed
  67. // or includes the xerial framing format into the specified `dst`.
  68. // It is assumed that the entirety of `dst` including all capacity is available
  69. // for use by this function. If `dst` is nil *or* insufficiently large to hold
  70. // the decoded `src`, new space will be allocated.
  71. func DecodeInto(dst, src []byte) ([]byte, error) {
  72. var max = len(src)
  73. if max < len(xerialHeader) {
  74. return nil, ErrMalformed
  75. }
  76. if !bytes.Equal(src[:8], xerialHeader) {
  77. return master.Decode(dst[:cap(dst)], src)
  78. }
  79. if max < sizeOffset+sizeBytes {
  80. return nil, ErrMalformed
  81. }
  82. if dst == nil {
  83. dst = make([]byte, 0, len(src))
  84. }
  85. dst = dst[:0]
  86. var (
  87. pos = sizeOffset
  88. chunk []byte
  89. err error
  90. )
  91. for pos+sizeBytes <= max {
  92. size := int(binary.BigEndian.Uint32(src[pos : pos+sizeBytes]))
  93. pos += sizeBytes
  94. nextPos := pos + size
  95. // On architectures where int is 32-bytes wide size + pos could
  96. // overflow so we need to check the low bound as well as the
  97. // high
  98. if nextPos < pos || nextPos > max {
  99. return nil, ErrMalformed
  100. }
  101. chunk, err = master.Decode(chunk[:cap(chunk)], src[pos:nextPos])
  102. if err != nil {
  103. return nil, err
  104. }
  105. pos = nextPos
  106. dst = append(dst, chunk...)
  107. }
  108. return dst, nil
  109. }