github.com/m3db/m3@v1.5.0/src/x/serialize/unchecked_decoder.go (about)

     1  // Copyright (c) 2021 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 serialize
    22  
    23  import (
    24  	"fmt"
    25  )
    26  
    27  // uncheckedDecoder is a fast decoder that skips all safety checks (i.e. ref counts).
    28  // it is suitable to use this when you are confident the provided bytes to decode are not shared.
    29  type uncheckedDecoder struct {
    30  	// immutable state for the lifetime of the decoder
    31  	maxTags      uint16
    32  	maxTagLength uint16
    33  
    34  	// immutable state for the lifetime of a single use.
    35  	length int
    36  
    37  	// mutable state during a single use.
    38  	data            []byte
    39  	remaining       int
    40  	err             error
    41  	currentTagName  []byte
    42  	currentTagValue []byte
    43  }
    44  
    45  func newUncheckedTagDecoder(tagLimits TagSerializationLimits) *uncheckedDecoder {
    46  	return &uncheckedDecoder{
    47  		// inline the config values since this call is in the hotpath of the matcher. saved about 1% cpu.
    48  		maxTags:      tagLimits.MaxNumberTags(),
    49  		maxTagLength: tagLimits.MaxTagLiteralLength(),
    50  	}
    51  }
    52  
    53  func (d *uncheckedDecoder) reset(b []byte) {
    54  	d.data = b
    55  	var length uint16
    56  	if len(d.data) == 0 {
    57  		d.length = 0
    58  		d.remaining = 0
    59  	} else {
    60  		header, err := d.decodeUInt16()
    61  		if err != nil {
    62  			d.err = err
    63  			return
    64  		}
    65  
    66  		if header != HeaderMagicNumber {
    67  			d.err = ErrIncorrectHeader
    68  			return
    69  		}
    70  
    71  		length, err = d.decodeUInt16()
    72  		if err != nil {
    73  			d.err = err
    74  			return
    75  		}
    76  
    77  		if limit := d.maxTags; length > limit {
    78  			d.err = fmt.Errorf("too many tags [ limit = %d, observed = %d ]", limit, length)
    79  			return
    80  		}
    81  	}
    82  	d.length = int(length)
    83  	d.remaining = int(length)
    84  	d.err = nil
    85  	d.currentTagName = nil
    86  	d.currentTagValue = nil
    87  }
    88  
    89  func (d *uncheckedDecoder) next() bool {
    90  	if d.err != nil || d.remaining <= 0 {
    91  		return false
    92  	}
    93  
    94  	if err := d.decodeTag(); err != nil {
    95  		d.err = err
    96  		return false
    97  	}
    98  
    99  	d.remaining--
   100  	return true
   101  }
   102  
   103  func (d *uncheckedDecoder) current() ([]byte, []byte) {
   104  	return d.currentTagName, d.currentTagValue
   105  }
   106  
   107  func (d *uncheckedDecoder) decodeTag() error {
   108  	var err error
   109  	d.currentTagName, err = d.decodeID()
   110  	if err != nil {
   111  		return err
   112  	}
   113  	if len(d.currentTagName) == 0 {
   114  		return ErrEmptyTagNameLiteral
   115  	}
   116  
   117  	d.currentTagValue, err = d.decodeID()
   118  	if err != nil {
   119  		return err
   120  	}
   121  	return nil
   122  }
   123  
   124  func (d *uncheckedDecoder) decodeID() ([]byte, error) {
   125  	l, err := d.decodeUInt16()
   126  	if err != nil {
   127  		return nil, err
   128  	}
   129  
   130  	if limit := d.maxTagLength; l > limit {
   131  		return nil, fmt.Errorf("tag literal too long [ limit = %d, observed = %d ]", limit, int(l))
   132  	}
   133  
   134  	if len(d.data) < int(l) {
   135  		return nil, errInvalidByteStreamIDDecoding
   136  	}
   137  
   138  	id := d.data[:l]
   139  	d.data = d.data[l:]
   140  
   141  	return id, nil
   142  }
   143  
   144  func (d *uncheckedDecoder) decodeUInt16() (uint16, error) {
   145  	if len(d.data) < 2 {
   146  		return 0, errInvalidByteStreamUintDecoding
   147  	}
   148  
   149  	n := decodeUInt16(d.data)
   150  	d.data = d.data[2:]
   151  	return n, nil
   152  }