github.com/cockroachdb/cockroachdb-parser@v0.23.3-0.20240213214944-911057d40c9a/pkg/util/json/jentry.go (about)

     1  // Copyright 2017 The Cockroach Authors.
     2  //
     3  // Use of this software is governed by the Business Source License
     4  // included in the file licenses/BSL.txt.
     5  //
     6  // As of the Change Date specified in that file, in accordance with
     7  // the Business Source License, use of this software will be governed
     8  // by the Apache License, Version 2.0, included in the file
     9  // licenses/APL.txt.
    10  
    11  package json
    12  
    13  import "github.com/cockroachdb/cockroachdb-parser/pkg/util/encoding"
    14  
    15  const nullTag = 0x00000000
    16  const stringTag = 0x10000000
    17  const numberTag = 0x20000000
    18  const falseTag = 0x30000000
    19  const trueTag = 0x40000000
    20  const containerTag = 0x50000000
    21  
    22  const jEntryIsOffFlag = 0x80000000
    23  const jEntryTypeMask = 0x70000000
    24  const jEntryOffLenMask = 0x0FFFFFFF
    25  
    26  // jEntry is a header for a particular JSON value. See the JSONB encoding RFC
    27  // for an explanation of its purpose and format:
    28  // https://github.com/cockroachdb/cockroachdb-parser/blob/master/docs/RFCS/20171005_jsonb_encoding.md
    29  type jEntry struct {
    30  	typCode uint32
    31  	length  uint32
    32  }
    33  
    34  type encodingType int
    35  
    36  const (
    37  	lengthEncoding encodingType = iota
    38  	offsetEncoding
    39  )
    40  
    41  // encodingMode specifies the context in which a JEntry is to be encoded.
    42  type encodingMode struct {
    43  	typ encodingType
    44  	// offset is the offset in the current container which we will be encoding
    45  	// this JEntry. Only relevant when typ == offsetEncoding.
    46  	offset uint32
    47  }
    48  
    49  var lengthMode = encodingMode{typ: lengthEncoding}
    50  
    51  func offsetEncode(offset uint32) encodingMode {
    52  	return encodingMode{typ: offsetEncoding, offset: offset}
    53  }
    54  
    55  var nullJEntry = jEntry{nullTag, 0}
    56  var trueJEntry = jEntry{trueTag, 0}
    57  var falseJEntry = jEntry{falseTag, 0}
    58  
    59  func makeStringJEntry(length int) jEntry {
    60  	return jEntry{stringTag, uint32(length)}
    61  }
    62  
    63  func makeNumberJEntry(length int) jEntry {
    64  	return jEntry{numberTag, uint32(length)}
    65  }
    66  
    67  func makeContainerJEntry(length int) jEntry {
    68  	return jEntry{containerTag, uint32(length)}
    69  }
    70  
    71  // encoded returns the encoded form of the jEntry.
    72  func (e jEntry) encoded(mode encodingMode) uint32 {
    73  	switch mode.typ {
    74  	case lengthEncoding:
    75  		return e.typCode | e.length
    76  	case offsetEncoding:
    77  		return jEntryIsOffFlag | e.typCode | mode.offset
    78  	}
    79  	return 0
    80  }
    81  
    82  func getJEntryAt(b []byte, idx int, offset int) (jEntry, error) {
    83  	enc, err := getUint32At(b, idx)
    84  	if err != nil {
    85  		return jEntry{}, err
    86  	}
    87  	length := enc & jEntryOffLenMask
    88  	if (enc & jEntryIsOffFlag) != 0 {
    89  		length -= uint32(offset)
    90  	}
    91  	return jEntry{
    92  		length:  length,
    93  		typCode: enc & jEntryTypeMask,
    94  	}, nil
    95  }
    96  
    97  // decodeJEntry decodes a 4-byte JEntry from a buffer. The current offset is
    98  // required because a JEntry can either encode a length or an offset, and while
    99  // a length can be interpreted locally, the current decoding offset is required
   100  // in order to interpret the encoded offset.
   101  func decodeJEntry(b []byte, offset uint32) ([]byte, jEntry, error) {
   102  	b, encoded, err := encoding.DecodeUint32Ascending(b)
   103  	if err != nil {
   104  		return b, jEntry{}, err
   105  	}
   106  
   107  	length := encoded & jEntryOffLenMask
   108  
   109  	isOff := (encoded & jEntryIsOffFlag) != 0
   110  	if isOff {
   111  		length -= offset
   112  	}
   113  
   114  	return b, jEntry{
   115  		typCode: encoded & jEntryTypeMask,
   116  		length:  length,
   117  	}, nil
   118  }