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 }