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 }