github.com/m3db/m3@v1.5.1-0.20231129193456-75a402aa583b/src/dbnode/encoding/m3tsz/timestamp_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  	"encoding/binary"
    25  	"errors"
    26  	"time"
    27  
    28  	"github.com/m3db/m3/src/dbnode/encoding"
    29  	"github.com/m3db/m3/src/dbnode/ts"
    30  	xtime "github.com/m3db/m3/src/x/time"
    31  )
    32  
    33  var (
    34  	errNoTimeSchemaForUnit        = errors.New("time encoding scheme doesn't exist for unit")
    35  	errUnexpectedAnnotationLength = errors.New("expected annotation length to be >= 0")
    36  	errAnnotationTooFewBytes      = errors.New("expected to read annotation bytes, but got end of stream")
    37  )
    38  
    39  // TimestampIterator encapsulates all the state required for iterating over
    40  // delta-of-delta compressed timestamps.
    41  type TimestampIterator struct {
    42  	PrevTime      xtime.UnixNano
    43  	PrevTimeDelta time.Duration
    44  	PrevAnt       ts.Annotation
    45  	prevAntBytes  [ts.OptimizedAnnotationLen]byte
    46  
    47  	TimeUnit        xtime.Unit
    48  	defaultTimeUnit xtime.Unit
    49  
    50  	markerEncodingScheme *encoding.MarkerEncodingScheme
    51  	timeEncodingSchemes  encoding.TimeEncodingSchemes
    52  	timeEncodingScheme   *encoding.TimeEncodingScheme
    53  
    54  	TimeUnitChanged bool
    55  	Done            bool
    56  
    57  	// Controls whether the iterator will "look ahead" for marker encoding
    58  	// schemes. Setting SkipMarkers to true disables the look ahead behavior
    59  	// for situations where looking ahead is not safe.
    60  	SkipMarkers bool
    61  
    62  	numValueBits uint8
    63  	numBits      uint8
    64  }
    65  
    66  // NewTimestampIterator creates a new TimestampIterator.
    67  func NewTimestampIterator(opts encoding.Options, skipMarkers bool) TimestampIterator {
    68  	mes := opts.MarkerEncodingScheme()
    69  	return TimestampIterator{
    70  		defaultTimeUnit:      opts.DefaultTimeUnit(),
    71  		SkipMarkers:          skipMarkers,
    72  		numValueBits:         uint8(mes.NumValueBits()),
    73  		numBits:              uint8(mes.NumOpcodeBits() + mes.NumValueBits()),
    74  		markerEncodingScheme: mes,
    75  		timeEncodingSchemes:  opts.TimeEncodingSchemes(),
    76  	}
    77  }
    78  
    79  // ReadTimestamp reads the first or next timestamp.
    80  func (it *TimestampIterator) ReadTimestamp(stream *encoding.IStream) (bool, bool, error) {
    81  	it.PrevAnt = nil
    82  
    83  	var (
    84  		first = false
    85  		dod   time.Duration
    86  		err   error
    87  	)
    88  
    89  	if it.PrevTime != 0 {
    90  		// inlined readNextTimestamp
    91  		dod, err = it.readMarkerOrDeltaOfDelta(stream)
    92  		if err == nil {
    93  			it.PrevTimeDelta += dod
    94  			it.PrevTime += xtime.UnixNano(it.PrevTimeDelta)
    95  		}
    96  	} else {
    97  		first = true
    98  		err = it.readFirstTimestamp(stream)
    99  	}
   100  
   101  	if err != nil {
   102  		return false, false, err
   103  	}
   104  
   105  	// NB(xichen): reset time delta to 0 when there is a time unit change to be
   106  	// consistent with the encoder.
   107  	if it.TimeUnitChanged {
   108  		it.PrevTimeDelta = 0
   109  		it.TimeUnitChanged = false
   110  	}
   111  
   112  	return first, it.Done, nil
   113  }
   114  
   115  // ReadTimeUnit reads an encoded time unit and updates the iterator's state
   116  // accordingly. It is exposed as a public method so that callers can control
   117  // the encoding / decoding of the time unit on their own if they choose.
   118  func (it *TimestampIterator) ReadTimeUnit(stream *encoding.IStream) error {
   119  	tuBits, err := stream.ReadBits(8)
   120  	if err != nil {
   121  		return err
   122  	}
   123  
   124  	tu := xtime.Unit(tuBits)
   125  	if tu.IsValid() && tu != it.TimeUnit {
   126  		it.TimeUnitChanged = true
   127  		tes, ok := it.timeEncodingSchemes.SchemeForUnit(tu)
   128  		if ok {
   129  			it.timeEncodingScheme = tes
   130  		}
   131  	}
   132  	it.TimeUnit = tu
   133  
   134  	return nil
   135  }
   136  
   137  func (it *TimestampIterator) readFirstTimestamp(stream *encoding.IStream) error {
   138  	ntBits, err := stream.ReadBits(64)
   139  	if err != nil {
   140  		return err
   141  	}
   142  
   143  	// NB(xichen): first time stamp is always normalized to nanoseconds.
   144  	nt := xtime.UnixNano(ntBits)
   145  	if it.TimeUnit == xtime.None {
   146  		it.TimeUnit = initialTimeUnit(nt, it.defaultTimeUnit)
   147  	}
   148  
   149  	tes, ok := it.timeEncodingSchemes.SchemeForUnit(it.TimeUnit)
   150  	if ok {
   151  		it.timeEncodingScheme = tes
   152  	}
   153  
   154  	err = it.readNextTimestamp(stream)
   155  	if err != nil {
   156  		return err
   157  	}
   158  
   159  	it.PrevTime = nt + xtime.UnixNano(it.PrevTimeDelta)
   160  	return nil
   161  }
   162  
   163  func (it *TimestampIterator) readNextTimestamp(stream *encoding.IStream) error {
   164  	dod, err := it.readMarkerOrDeltaOfDelta(stream)
   165  	if err != nil {
   166  		return err
   167  	}
   168  
   169  	it.PrevTimeDelta += dod
   170  	it.PrevTime += xtime.UnixNano(it.PrevTimeDelta)
   171  	return nil
   172  }
   173  
   174  // nolint: gocyclo
   175  func (it *TimestampIterator) tryReadMarker(stream *encoding.IStream) (time.Duration, bool, error) {
   176  	var (
   177  		numBits             = it.numBits
   178  		numValueBits        = it.numValueBits
   179  		opcodeAndValue, err = stream.PeekBits(numBits)
   180  	)
   181  
   182  	if err != nil {
   183  		return 0, false, nil
   184  	}
   185  
   186  	opcode := opcodeAndValue >> numValueBits
   187  	if opcode != it.markerEncodingScheme.Opcode() {
   188  		return 0, false, nil
   189  	}
   190  
   191  	var (
   192  		valueMask   = (1 << numValueBits) - 1
   193  		markerValue = encoding.Marker(opcodeAndValue & uint64(valueMask))
   194  	)
   195  
   196  	switch markerValue {
   197  	case it.markerEncodingScheme.EndOfStream():
   198  		_, err := stream.ReadBits(numBits)
   199  		if err != nil {
   200  			return 0, false, err
   201  		}
   202  		it.Done = true
   203  		return 0, true, nil
   204  	case it.markerEncodingScheme.Annotation():
   205  		_, err := stream.ReadBits(numBits)
   206  		if err != nil {
   207  			return 0, false, err
   208  		}
   209  		err = it.readAnnotation(stream)
   210  		if err != nil {
   211  			return 0, false, err
   212  		}
   213  		markerOrDOD, err := it.readMarkerOrDeltaOfDelta(stream)
   214  		if err != nil {
   215  			return 0, false, err
   216  		}
   217  		return markerOrDOD, true, nil
   218  	case it.markerEncodingScheme.TimeUnit():
   219  		_, err := stream.ReadBits(numBits)
   220  		if err != nil {
   221  			return 0, false, err
   222  		}
   223  		err = it.ReadTimeUnit(stream)
   224  		if err != nil {
   225  			return 0, false, err
   226  		}
   227  		markerOrDOD, err := it.readMarkerOrDeltaOfDelta(stream)
   228  		if err != nil {
   229  			return 0, false, err
   230  		}
   231  		return markerOrDOD, true, nil
   232  	default:
   233  		return 0, false, nil
   234  	}
   235  }
   236  
   237  func (it *TimestampIterator) readMarkerOrDeltaOfDelta(
   238  	stream *encoding.IStream,
   239  ) (time.Duration, error) {
   240  	if !it.SkipMarkers {
   241  		dod, success, err := it.tryReadMarker(stream)
   242  		if success || err != nil || it.Done {
   243  			return dod, err
   244  		}
   245  	}
   246  
   247  	return it.readDeltaOfDelta(stream)
   248  }
   249  
   250  func (it *TimestampIterator) readDeltaOfDelta(
   251  	stream *encoding.IStream,
   252  ) (time.Duration, error) {
   253  	if it.TimeUnitChanged {
   254  		return it.readFullTimestamp(stream)
   255  	} else if it.timeEncodingScheme == nil {
   256  		return 0, errNoTimeSchemaForUnit
   257  	}
   258  
   259  	cb, err := stream.ReadBits(1)
   260  	if err != nil {
   261  		return 0, err
   262  	}
   263  
   264  	tes := it.timeEncodingScheme
   265  	if cb == tes.ZeroBucket().Opcode() {
   266  		return 0, nil
   267  	}
   268  
   269  	buckets := tes.Buckets()
   270  	for i := 0; i < len(buckets); i++ {
   271  		nextCB, err := stream.ReadBits(1)
   272  		if err != nil {
   273  			return 0, nil
   274  		}
   275  
   276  		cb = (cb << 1) | nextCB
   277  		if cb == buckets[i].Opcode() {
   278  			dodBits, err := stream.ReadBits(uint8(buckets[i].NumValueBits()))
   279  			if err != nil {
   280  				return 0, err
   281  			}
   282  
   283  			dod := encoding.SignExtend(dodBits, uint8(buckets[i].NumValueBits()))
   284  			timeUnit, err := it.TimeUnit.Value()
   285  			if err != nil {
   286  				return 0, nil
   287  			}
   288  
   289  			return xtime.FromNormalizedDuration(dod, timeUnit), nil
   290  		}
   291  	}
   292  
   293  	numValueBits := uint8(tes.DefaultBucket().NumValueBits())
   294  	dodBits, err := stream.ReadBits(numValueBits)
   295  	if err != nil {
   296  		return 0, err
   297  	}
   298  	dod := encoding.SignExtend(dodBits, numValueBits)
   299  	timeUnit, err := it.TimeUnit.Value()
   300  	if err != nil {
   301  		return 0, nil
   302  	}
   303  
   304  	return xtime.FromNormalizedDuration(dod, timeUnit), nil
   305  }
   306  
   307  func (it *TimestampIterator) readFullTimestamp(
   308  	stream *encoding.IStream,
   309  ) (time.Duration, error) {
   310  	tes, exists := it.timeEncodingSchemes.SchemeForUnit(it.TimeUnit)
   311  	if !exists {
   312  		return 0, errNoTimeSchemaForUnit
   313  	}
   314  	it.timeEncodingScheme = tes
   315  	// NB(xichen): if the time unit has changed, always read 64 bits as normalized
   316  	// dod in nanoseconds.
   317  	dodBits, err := stream.ReadBits(64)
   318  	if err != nil {
   319  		return 0, err
   320  	}
   321  
   322  	dod := encoding.SignExtend(dodBits, 64)
   323  
   324  	return time.Duration(dod), nil
   325  }
   326  
   327  func (it *TimestampIterator) readAnnotation(stream *encoding.IStream) error {
   328  	antLen, err := it.readVarint(stream)
   329  	if err != nil {
   330  		return err
   331  	}
   332  
   333  	// NB: we add 1 here to offset the 1 we subtracted during encoding.
   334  	antLen = antLen + 1
   335  	if antLen <= 0 {
   336  		return errUnexpectedAnnotationLength
   337  	}
   338  
   339  	var buf []byte
   340  	if antLen <= len(it.prevAntBytes) {
   341  		buf = it.prevAntBytes[:antLen]
   342  	} else {
   343  		buf = make([]byte, antLen)
   344  	}
   345  
   346  	n, err := stream.Read(buf)
   347  	if err != nil {
   348  		return err
   349  	}
   350  	if n != antLen {
   351  		return errAnnotationTooFewBytes
   352  	}
   353  	it.PrevAnt = buf
   354  
   355  	return nil
   356  }
   357  
   358  func (it *TimestampIterator) readVarint(stream *encoding.IStream) (int, error) {
   359  	res, err := binary.ReadVarint(stream)
   360  	return int(res), err
   361  }