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  }