github.com/m3db/m3@v1.5.0/src/dbnode/encoding/m3tsz/m3tsz.go (about)

     1  // Copyright (c) 2016 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  	"errors"
    25  	"math"
    26  )
    27  
    28  const (
    29  	// DefaultIntOptimizationEnabled is the default switch for m3tsz int optimization
    30  	DefaultIntOptimizationEnabled = true
    31  
    32  	// OpcodeZeroSig indicates that there were zero significant digits.
    33  	OpcodeZeroSig = 0x0
    34  	// OpcodeNonZeroSig indicates that there were a non-zero number of significant digits.
    35  	OpcodeNonZeroSig = 0x1
    36  
    37  	// NumSigBits is the number of bits required to encode the maximum possible value
    38  	// of significant digits.
    39  	NumSigBits = 6
    40  
    41  	opcodeZeroValueXOR        = 0x0
    42  	opcodeContainedValueXOR   = 0x2
    43  	opcodeUncontainedValueXOR = 0x3
    44  	opcodeNoUpdateSig         = 0x0
    45  	opcodeUpdateSig           = 0x1
    46  	opcodeUpdate              = 0x0
    47  	opcodeNoUpdate            = 0x1
    48  	opcodeUpdateMult          = 0x1
    49  	opcodeNoUpdateMult        = 0x0
    50  	opcodePositive            = 0x0
    51  	opcodeNegative            = 0x1
    52  	opcodeRepeat              = 0x1
    53  	opcodeNoRepeat            = 0x0
    54  	opcodeFloatMode           = 0x1
    55  	opcodeIntMode             = 0x0
    56  
    57  	sigDiffThreshold   = uint8(3)
    58  	sigRepeatThreshold = uint8(5)
    59  
    60  	maxMult     = uint8(6)
    61  	numMultBits = 3
    62  )
    63  
    64  var (
    65  	maxInt               = float64(math.MaxInt64)
    66  	minInt               = float64(math.MinInt64)
    67  	maxOptInt            = math.Pow(10.0, 13) // Max int for int optimization
    68  	multipliers          = createMultipliers()
    69  	errInvalidMultiplier = errors.New("supplied multiplier is invalid")
    70  )
    71  
    72  // convertToIntFloat takes a float64 val and the current max multiplier
    73  // and attempts to transform the float into an int with multiplier. There
    74  // is potential for a small accuracy loss for float values that are very
    75  // close to ints eg. 46.000000000000001 would be returned as 46. This only
    76  // applies to values where the next possible smaller or larger float changes
    77  // the integer component of the float
    78  func convertToIntFloat(v float64, curMaxMult uint8) (float64, uint8, bool, error) {
    79  	if curMaxMult == 0 && v < maxInt {
    80  		// Quick check for vals that are already ints
    81  		i, r := math.Modf(v)
    82  		if r == 0 {
    83  			return i, 0, false, nil
    84  		}
    85  	}
    86  
    87  	if curMaxMult > maxMult {
    88  		return 0.0, 0, false, errInvalidMultiplier
    89  	}
    90  
    91  	sign := 1.0
    92  	if v < 0 {
    93  		sign = -1.0
    94  	}
    95  
    96  	for mult := curMaxMult; mult <= maxMult; mult++ {
    97  		val := v * multipliers[int(mult)] * sign
    98  		if val >= maxOptInt {
    99  			break
   100  		}
   101  		i, r := math.Modf(val)
   102  		if r == 0 {
   103  			return sign * i, mult, false, nil
   104  		} else if r < 0.1 {
   105  			// Round down and check
   106  			if math.Nextafter(val, 0) <= i {
   107  				return sign * i, mult, false, nil
   108  			}
   109  		} else if r > 0.9 {
   110  			// Round up and check
   111  			next := i + 1
   112  			if math.Nextafter(val, next) >= next {
   113  				return sign * next, mult, false, nil
   114  			}
   115  		}
   116  	}
   117  
   118  	return v, 0, true, nil
   119  }
   120  
   121  func convertFromIntFloat(val float64, mult uint8) float64 {
   122  	if mult == 0 {
   123  		return val
   124  	}
   125  
   126  	return val / multipliers[int(mult)]
   127  }
   128  
   129  // createMultipliers creates all the multipliers up to maxMult
   130  // and places them into a slice
   131  func createMultipliers() []float64 {
   132  	multipliers := make([]float64, maxMult+1)
   133  	base := 1.0
   134  	for i := 0; i <= int(maxMult); i++ {
   135  		multipliers[i] = base
   136  		base = base * 10.0
   137  	}
   138  
   139  	return multipliers
   140  }