github.com/m3db/m3@v1.5.0/src/dbnode/encoding/m3tsz/float_encoder_iterator.go (about) 1 // Copyright (c) 2019 Uber Technologies, Inc. 2 // 3 // Permission is hereby granted, free of charge, to any person obtaining a copy 4 // of this software and associated documentation files (the "Software"), to deal 5 // in the Software without restriction, including without limitation the rights 6 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 // copies of the Software, and to permit persons to whom the Software is 8 // furnished to do so, subject to the following conditions: 9 // 10 // The above copyright notice and this permission notice shall be included in 11 // all copies or substantial portions of the Software. 12 // 13 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 // THE SOFTWARE. 20 21 package m3tsz 22 23 import ( 24 "math" 25 26 "github.com/m3db/m3/src/dbnode/encoding" 27 ) 28 29 const ( 30 bits12To6Mask = 4032 // 1111 1100 0000 31 bits6To0Mask = 63 // 0011 1111 32 ) 33 34 // FloatEncoderAndIterator encapsulates the state required for a logical stream of bits 35 // that represent a stream of float values compressed with XOR. 36 type FloatEncoderAndIterator struct { 37 PrevXOR uint64 38 PrevFloatBits uint64 39 40 // Only taken into account if using the WriteFloat() and ReadFloat() 41 // APIs. 42 NotFirst bool 43 } 44 45 // WriteFloat writes a float into the stream, writing the full value or a compressed 46 // XOR as appropriate. 47 func (eit *FloatEncoderAndIterator) WriteFloat(stream encoding.OStream, val float64) { 48 fb := math.Float64bits(val) 49 if eit.NotFirst { 50 eit.writeNextFloat(stream, fb) 51 } else { 52 eit.writeFullFloat(stream, fb) 53 eit.NotFirst = true 54 } 55 } 56 57 // ReadFloat reads a compressed float from the stream. 58 func (eit *FloatEncoderAndIterator) ReadFloat(stream *encoding.IStream) error { 59 if eit.NotFirst { 60 return eit.readNextFloat(stream) 61 } 62 63 err := eit.readFullFloat(stream) 64 eit.NotFirst = true 65 return err 66 67 } 68 69 func (eit *FloatEncoderAndIterator) writeFullFloat(stream encoding.OStream, val uint64) { 70 eit.PrevFloatBits = val 71 eit.PrevXOR = val 72 stream.WriteBits(val, 64) 73 } 74 75 func (eit *FloatEncoderAndIterator) writeNextFloat(stream encoding.OStream, val uint64) { 76 xor := eit.PrevFloatBits ^ val 77 eit.writeXOR(stream, xor) 78 eit.PrevXOR = xor 79 eit.PrevFloatBits = val 80 } 81 82 func (eit *FloatEncoderAndIterator) writeXOR(stream encoding.OStream, currXOR uint64) { 83 if currXOR == 0 { 84 stream.WriteBits(opcodeZeroValueXOR, 1) 85 return 86 } 87 88 // NB(xichen): can be further optimized by keeping track of leading and trailing zeros in eit. 89 prevLeading, prevTrailing := encoding.LeadingAndTrailingZeros(eit.PrevXOR) 90 curLeading, curTrailing := encoding.LeadingAndTrailingZeros(currXOR) 91 if curLeading >= prevLeading && curTrailing >= prevTrailing { 92 stream.WriteBits(opcodeContainedValueXOR, 2) 93 stream.WriteBits(currXOR>>uint(prevTrailing), 64-prevLeading-prevTrailing) 94 return 95 } 96 97 stream.WriteBits(opcodeUncontainedValueXOR, 2) 98 stream.WriteBits(uint64(curLeading), 6) 99 numMeaningfulBits := 64 - curLeading - curTrailing 100 // numMeaningfulBits is at least 1, so we can subtract 1 from it and encode it in 6 bits 101 stream.WriteBits(uint64(numMeaningfulBits-1), 6) 102 stream.WriteBits(currXOR>>uint(curTrailing), numMeaningfulBits) 103 } 104 105 func (eit *FloatEncoderAndIterator) readFullFloat(stream *encoding.IStream) error { 106 vb, err := stream.ReadBits(64) 107 if err != nil { 108 return err 109 } 110 111 eit.PrevFloatBits = vb 112 eit.PrevXOR = vb 113 114 return nil 115 } 116 117 func (eit *FloatEncoderAndIterator) readNextFloat(stream *encoding.IStream) error { 118 cb, err := stream.ReadBits(1) 119 if err != nil { 120 return err 121 } 122 123 if cb == opcodeZeroValueXOR { 124 eit.PrevXOR = 0 125 return nil 126 } 127 128 nextCB, err := stream.ReadBits(1) 129 if err != nil { 130 return err 131 } 132 133 cb = (cb << 1) | nextCB 134 if cb == opcodeContainedValueXOR { 135 previousLeading, previousTrailing := encoding.LeadingAndTrailingZeros(eit.PrevXOR) 136 numMeaningfulBits := uint8(64 - previousLeading - previousTrailing) 137 meaningfulBits, err := stream.ReadBits(numMeaningfulBits) 138 if err != nil { 139 return err 140 } 141 142 eit.PrevXOR = meaningfulBits << uint(previousTrailing) 143 eit.PrevFloatBits ^= eit.PrevXOR 144 return nil 145 } 146 147 numLeadingZeroesAndNumMeaningfulBits, err := stream.ReadBits(12) 148 if err != nil { 149 return err 150 } 151 152 numLeadingZeros := (numLeadingZeroesAndNumMeaningfulBits & bits12To6Mask) >> 6 153 numMeaningfulBits := (numLeadingZeroesAndNumMeaningfulBits & bits6To0Mask) + 1 154 155 meaningfulBits, err := stream.ReadBits(uint8(numMeaningfulBits)) 156 if err != nil { 157 return err 158 } 159 160 numTrailingZeros := 64 - numLeadingZeros - numMeaningfulBits 161 162 eit.PrevXOR = meaningfulBits << uint(numTrailingZeros) 163 eit.PrevFloatBits ^= eit.PrevXOR 164 return nil 165 }