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