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.

intcoder.go 4.4KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172
  1. // Copyright (c) 2017 Couchbase, Inc.
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. package zap
  15. import (
  16. "bytes"
  17. "encoding/binary"
  18. "io"
  19. )
  20. type chunkedIntCoder struct {
  21. final []byte
  22. chunkSize uint64
  23. chunkBuf bytes.Buffer
  24. chunkLens []uint64
  25. currChunk uint64
  26. buf []byte
  27. }
  28. // newChunkedIntCoder returns a new chunk int coder which packs data into
  29. // chunks based on the provided chunkSize and supports up to the specified
  30. // maxDocNum
  31. func newChunkedIntCoder(chunkSize uint64, maxDocNum uint64) *chunkedIntCoder {
  32. total := maxDocNum/chunkSize + 1
  33. rv := &chunkedIntCoder{
  34. chunkSize: chunkSize,
  35. chunkLens: make([]uint64, total),
  36. final: make([]byte, 0, 64),
  37. }
  38. return rv
  39. }
  40. // Reset lets you reuse this chunked int coder. buffers are reset and reused
  41. // from previous use. you cannot change the chunk size or max doc num.
  42. func (c *chunkedIntCoder) Reset() {
  43. c.final = c.final[:0]
  44. c.chunkBuf.Reset()
  45. c.currChunk = 0
  46. for i := range c.chunkLens {
  47. c.chunkLens[i] = 0
  48. }
  49. }
  50. // Add encodes the provided integers into the correct chunk for the provided
  51. // doc num. You MUST call Add() with increasing docNums.
  52. func (c *chunkedIntCoder) Add(docNum uint64, vals ...uint64) error {
  53. chunk := docNum / c.chunkSize
  54. if chunk != c.currChunk {
  55. // starting a new chunk
  56. c.Close()
  57. c.chunkBuf.Reset()
  58. c.currChunk = chunk
  59. }
  60. if len(c.buf) < binary.MaxVarintLen64 {
  61. c.buf = make([]byte, binary.MaxVarintLen64)
  62. }
  63. for _, val := range vals {
  64. wb := binary.PutUvarint(c.buf, val)
  65. _, err := c.chunkBuf.Write(c.buf[:wb])
  66. if err != nil {
  67. return err
  68. }
  69. }
  70. return nil
  71. }
  72. func (c *chunkedIntCoder) AddBytes(docNum uint64, buf []byte) error {
  73. chunk := docNum / c.chunkSize
  74. if chunk != c.currChunk {
  75. // starting a new chunk
  76. c.Close()
  77. c.chunkBuf.Reset()
  78. c.currChunk = chunk
  79. }
  80. _, err := c.chunkBuf.Write(buf)
  81. return err
  82. }
  83. // Close indicates you are done calling Add() this allows the final chunk
  84. // to be encoded.
  85. func (c *chunkedIntCoder) Close() {
  86. encodingBytes := c.chunkBuf.Bytes()
  87. c.chunkLens[c.currChunk] = uint64(len(encodingBytes))
  88. c.final = append(c.final, encodingBytes...)
  89. c.currChunk = uint64(cap(c.chunkLens)) // sentinel to detect double close
  90. }
  91. // Write commits all the encoded chunked integers to the provided writer.
  92. func (c *chunkedIntCoder) Write(w io.Writer) (int, error) {
  93. bufNeeded := binary.MaxVarintLen64 * (1 + len(c.chunkLens))
  94. if len(c.buf) < bufNeeded {
  95. c.buf = make([]byte, bufNeeded)
  96. }
  97. buf := c.buf
  98. // convert the chunk lengths into chunk offsets
  99. chunkOffsets := modifyLengthsToEndOffsets(c.chunkLens)
  100. // write out the number of chunks & each chunk offsets
  101. n := binary.PutUvarint(buf, uint64(len(chunkOffsets)))
  102. for _, chunkOffset := range chunkOffsets {
  103. n += binary.PutUvarint(buf[n:], chunkOffset)
  104. }
  105. tw, err := w.Write(buf[:n])
  106. if err != nil {
  107. return tw, err
  108. }
  109. // write out the data
  110. nw, err := w.Write(c.final)
  111. tw += nw
  112. if err != nil {
  113. return tw, err
  114. }
  115. return tw, nil
  116. }
  117. func (c *chunkedIntCoder) FinalSize() int {
  118. return len(c.final)
  119. }
  120. // modifyLengthsToEndOffsets converts the chunk length array
  121. // to a chunk offset array. The readChunkBoundary
  122. // will figure out the start and end of every chunk from
  123. // these offsets. Starting offset of i'th index is stored
  124. // in i-1'th position except for 0'th index and ending offset
  125. // is stored at i'th index position.
  126. // For 0'th element, starting position is always zero.
  127. // eg:
  128. // Lens -> 5 5 5 5 => 5 10 15 20
  129. // Lens -> 0 5 0 5 => 0 5 5 10
  130. // Lens -> 0 0 0 5 => 0 0 0 5
  131. // Lens -> 5 0 0 0 => 5 5 5 5
  132. // Lens -> 0 5 0 0 => 0 5 5 5
  133. // Lens -> 0 0 5 0 => 0 0 5 5
  134. func modifyLengthsToEndOffsets(lengths []uint64) []uint64 {
  135. var runningOffset uint64
  136. var index, i int
  137. for i = 1; i <= len(lengths); i++ {
  138. runningOffset += lengths[i-1]
  139. lengths[index] = runningOffset
  140. index++
  141. }
  142. return lengths
  143. }
  144. func readChunkBoundary(chunk int, offsets []uint64) (uint64, uint64) {
  145. var start uint64
  146. if chunk > 0 {
  147. start = offsets[chunk-1]
  148. }
  149. return start, offsets[chunk]
  150. }