ubjson/visitor.go (828 lines of code) (raw):
// Licensed to Elasticsearch B.V. under one or more contributor
// license agreements. See the NOTICE file distributed with
// this work for additional information regarding copyright
// ownership. Elasticsearch B.V. licenses this file to you under
// the Apache License, Version 2.0 (the "License"); you may
// not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
package ubjson
import (
"encoding/binary"
"io"
"math"
"strconv"
structform "github.com/elastic/go-structform"
)
type Visitor struct {
w writer
length lengthStack
scratch [16]byte
}
type writer struct {
out io.Writer
}
var _ structform.ExtVisitor = &Visitor{}
const (
maxUint = ^uint(0)
maxInt = int(maxUint >> 1)
isUint64 = maxUint > math.MaxUint32
isInt64 = maxInt > math.MaxInt32
)
func (w writer) write(b []byte) error {
_, err := w.out.Write(b)
return err
}
func NewVisitor(out io.Writer) *Visitor {
v := &Visitor{w: writer{out}}
v.length.stack = v.length.stack0[:0]
return v
}
func (vs *Visitor) writeByte(b byte) error {
vs.scratch[0] = b
return vs.w.write(vs.scratch[:1])
}
func (vs *Visitor) optionalCount(l int) error {
vs.length.push(int64(l))
if l <= 0 {
// don't add size if array is empty or size is unknown
return nil
}
if err := vs.writeByte(countMarker); err != nil {
return err
}
return vs.writeLen(l)
}
func (vs *Visitor) OnObjectStart(l int, _ structform.BaseType) error {
// TODO: add typed object support in case of values being passed one by one
// if number of elements is known, add size
if err := vs.writeByte(objStartMarker); err != nil {
return err
}
return vs.optionalCount(l)
}
func (vs *Visitor) OnObjectFinished() error {
if vs.length.pop() <= 0 {
return vs.writeByte(objEndMarker)
}
return nil
}
func (vs *Visitor) OnKey(s string) error {
return vs.string(str2Bytes(s), false)
}
func (vs *Visitor) OnKeyRef(s []byte) error {
return vs.string(s, false)
}
func (vs *Visitor) OnArrayStart(l int, t structform.BaseType) error {
// TODO: optimize array by computing type tag
if err := vs.writeByte(arrStartMarker); err != nil {
return err
}
// if array size is known, add at least size
return vs.optionalCount(l)
}
func (vs *Visitor) OnArrayFinished() error {
if vs.length.pop() <= 0 {
return vs.writeByte(arrEndMarker)
}
return nil
}
func (vs *Visitor) writeLen(l int) error {
return vs.onInt(l, true)
}
func (vs *Visitor) OnStringRef(s []byte) error {
if len(s) == 0 {
return vs.string(nil, true)
}
return vs.string(s, true)
}
func (vs *Visitor) OnString(s string) error {
if len(s) == 0 {
return vs.string(nil, true)
}
return vs.string(str2Bytes(s), true)
}
func (vs *Visitor) string(s []byte, marker bool) error {
if marker {
if err := vs.writeByte(stringMarker); err != nil {
return err
}
}
L := len(s)
if err := vs.writeLen(L); err != nil {
return err
}
if L == 0 {
return nil
}
return vs.w.write(s)
}
func (vs *Visitor) OnBool(b bool) error {
if b {
return vs.writeByte(trueMarker)
}
return vs.writeByte(falseMarker)
}
func (vs *Visitor) OnNil() error {
return vs.writeByte(nullMarker)
}
// int
func (vs *Visitor) OnInt8(i int8) error {
return vs.int8(i, true)
}
func (vs *Visitor) int8(i int8, marker bool) error {
if marker {
if err := vs.writeByte(int8Marker); err != nil {
return err
}
}
return vs.writeByte(byte(i))
}
func (vs *Visitor) OnInt16(i int16) error {
if math.MinInt8 <= i && i <= math.MaxInt8 {
return vs.int8(int8(i), true)
}
return vs.int16(i, true)
}
func (vs *Visitor) int16(i int16, marker bool) error {
if marker {
if err := vs.writeByte(int16Marker); err != nil {
return err
}
}
binary.BigEndian.PutUint16(vs.scratch[:2], uint16(i))
return vs.w.write(vs.scratch[:2])
}
func (vs *Visitor) OnInt32(i int32) error {
if math.MinInt16 <= i && i <= math.MaxInt16 {
return vs.OnInt16(int16(i))
}
return vs.int32(i, true)
}
func (vs *Visitor) int32(i int32, marker bool) error {
if marker {
if err := vs.writeByte(int32Marker); err != nil {
return err
}
}
binary.BigEndian.PutUint32(vs.scratch[:4], uint32(i))
return vs.w.write(vs.scratch[:4])
}
func (vs *Visitor) OnInt64(i int64) error {
if math.MinInt32 <= i && i <= math.MaxInt32 {
return vs.OnInt32(int32(i))
}
return vs.int64(i, true)
}
func (vs *Visitor) int64(i int64, marker bool) error {
if marker {
if err := vs.writeByte(int64Marker); err != nil {
return err
}
}
binary.BigEndian.PutUint64(vs.scratch[:8], uint64(i))
return vs.w.write(vs.scratch[:8])
}
func (vs *Visitor) OnInt(i int) error {
return vs.onInt(i, true)
}
func (vs *Visitor) onInt(i int, marker bool) error {
switch {
case math.MinInt8 <= i && i <= math.MaxInt8:
return vs.int8(int8(i), marker)
case 0 <= i && i <= math.MaxUint8:
return vs.uint8(uint8(i), marker)
case math.MinInt16 <= i && i <= math.MaxInt16:
return vs.int16(int16(i), marker)
case math.MinInt32 <= i && i <= math.MaxInt32:
return vs.int32(int32(i), marker)
default:
return vs.int64(int64(i), marker)
}
}
func (vs *Visitor) OnByte(b byte) error {
vs.scratch[0] = charMarker
vs.scratch[1] = b
return vs.w.write(vs.scratch[:2])
}
// uint
func (vs *Visitor) OnUint8(u uint8) error {
return vs.uint8(u, true)
}
func (vs *Visitor) uint8(u uint8, marker bool) error {
if marker {
vs.scratch[0], vs.scratch[1] = uint8Marker, u
return vs.w.write(vs.scratch[:2])
}
return vs.writeByte(u)
}
func (vs *Visitor) OnUint16(u uint16) error {
return vs.OnUint64(uint64(u))
}
func (vs *Visitor) OnUint32(u uint32) error {
return vs.OnUint64(uint64(u))
}
func (vs *Visitor) OnUint64(u uint64) error {
return vs.uint64(u, uintType(u), true)
}
func (vs *Visitor) uint64(u uint64, t byte, marker bool) error {
switch t {
case int8Marker:
return vs.int8(int8(u), marker)
case uint8Marker:
return vs.uint8(uint8(u), marker)
case int16Marker:
return vs.int16(int16(u), marker)
case int32Marker:
return vs.int32(int32(u), marker)
case int64Marker:
return vs.int64(int64(u), marker)
default:
return vs.uint64HighPrec(u, marker)
}
}
func (vs *Visitor) uint64HighPrec(u uint64, marker bool) error {
if marker {
if err := vs.writeByte(highPrecMarker); err != nil {
return err
}
}
b := strconv.AppendUint(vs.scratch[:0], u, 10)
if err := vs.writeLen(len(b)); err != nil {
return err
}
return vs.w.write(b)
}
func (vs *Visitor) OnUint(u uint) error {
return vs.OnUint64(uint64(u))
}
// float
func (vs *Visitor) OnFloat32(f float32) error {
return vs.float32(f, true)
}
func (vs *Visitor) float32(f float32, marker bool) error {
if marker {
if err := vs.writeByte(float32Marker); err != nil {
return err
}
}
bits := math.Float32bits(f)
binary.BigEndian.PutUint32(vs.scratch[:4], bits)
return vs.w.write(vs.scratch[:4])
}
func (vs *Visitor) OnFloat64(f float64) error {
return vs.float64(f, true)
}
func (vs *Visitor) float64(f float64, marker bool) error {
if marker {
if err := vs.writeByte(float64Marker); err != nil {
return err
}
}
bits := math.Float64bits(f)
binary.BigEndian.PutUint64(vs.scratch[:8], bits)
return vs.w.write(vs.scratch[:8])
}
// specialize array encoders
func (vs *Visitor) onTypedStruct(s, t byte, count int) error {
vs.scratch[0] = s
vs.scratch[1] = typeMarker
vs.scratch[2] = t
vs.scratch[3] = countMarker
if err := vs.w.write(vs.scratch[:4]); err != nil {
return err
}
return vs.writeLen(count)
}
func (vs *Visitor) onArray(t byte, count int) error {
return vs.onTypedStruct(arrStartMarker, t, count)
}
func (vs *Visitor) OnStringArray(a []string) error {
if done, err := vs.onEmptyArray(len(a)); done {
return err
}
if err := vs.onArray(stringMarker, len(a)); err != nil {
return err
}
for _, v := range a {
if err := vs.string(str2Bytes(v), false); err != nil {
return err
}
}
return nil
}
func (vs *Visitor) OnBoolArray(a []bool) error {
// no special encoding for boolean arrays => fall back to per element encoding
if err := vs.OnArrayStart(len(a), structform.AnyType); err != nil {
return err
}
for _, b := range a {
if err := vs.OnBool(b); err != nil {
return err
}
}
return nil
}
func (vs *Visitor) OnInt8Array(a []int8) error {
if done, err := vs.onEmptyArray(len(a)); done {
return err
}
if err := vs.onArray(int8Marker, len(a)); err != nil {
return err
}
for _, v := range a {
if err := vs.int8(v, false); err != nil {
return err
}
}
return nil
}
func (vs *Visitor) OnInt16Array(a []int16) error {
if done, err := vs.onEmptyArray(len(a)); done {
return err
}
if err := vs.onArray(int16Marker, len(a)); err != nil {
return err
}
for _, v := range a {
if err := vs.int16(v, false); err != nil {
return err
}
}
return nil
}
func (vs *Visitor) OnInt32Array(a []int32) error {
if done, err := vs.onEmptyArray(len(a)); done {
return err
}
if err := vs.onArray(int32Marker, len(a)); err != nil {
return err
}
for _, v := range a {
if err := vs.int32(v, false); err != nil {
return err
}
}
return nil
}
func (vs *Visitor) OnInt64Array(a []int64) error {
if done, err := vs.onEmptyArray(len(a)); done {
return err
}
if err := vs.onArray(int64Marker, len(a)); err != nil {
return err
}
for _, v := range a {
if err := vs.int64(v, false); err != nil {
return err
}
}
return nil
}
func (vs *Visitor) OnIntArray(a []int) error {
if done, err := vs.onEmptyArray(len(a)); done {
return err
}
marker := int32Marker
if isInt64 {
marker = int64Marker
}
if err := vs.onArray(marker, len(a)); err != nil {
return err
}
for _, v := range a {
var err error
if isInt64 {
err = vs.int64(int64(v), false)
} else {
err = vs.int32(int32(v), false)
}
if err != nil {
return err
}
}
return nil
}
func (vs *Visitor) OnBytes(a []byte) error {
if done, err := vs.onEmptyArray(len(a)); done {
return err
}
if err := vs.onArray(uint8Marker, len(a)); err != nil {
return err
}
for _, v := range a {
if err := vs.uint8(v, false); err != nil {
return err
}
}
return nil
}
func (vs *Visitor) OnUint8Array(a []uint8) error {
if done, err := vs.onEmptyArray(len(a)); done {
return err
}
if err := vs.onArray(uint8Marker, len(a)); err != nil {
return err
}
for _, v := range a {
if err := vs.uint8(v, false); err != nil {
return err
}
}
return nil
}
func (vs *Visitor) OnUint16Array(a []uint16) error {
if done, err := vs.onEmptyArray(len(a)); done {
return err
}
// find type:
minT := int8Marker
for _, v := range a {
minT = maxNumType(minT, uintType(uint64(v)))
}
// serialize array
if err := vs.onArray(minT, len(a)); err != nil {
return err
}
for _, v := range a {
if err := vs.uint64(uint64(v), minT, false); err != nil {
return err
}
}
return nil
}
func (vs *Visitor) OnUint32Array(a []uint32) error {
if done, err := vs.onEmptyArray(len(a)); done {
return err
}
// find type:
minT := int8Marker
for _, v := range a {
minT = maxNumType(minT, uintType(uint64(v)))
}
// serialize array
if err := vs.onArray(minT, len(a)); err != nil {
return err
}
for _, v := range a {
if err := vs.uint64(uint64(v), minT, false); err != nil {
return err
}
}
return nil
}
func (vs *Visitor) OnUint64Array(a []uint64) error {
if done, err := vs.onEmptyArray(len(a)); done {
return err
}
// find type:
minT := int8Marker
for _, v := range a {
minT = maxNumType(minT, uintType(v))
}
// serialize array
if err := vs.onArray(minT, len(a)); err != nil {
return err
}
for _, v := range a {
if err := vs.uint64(v, minT, false); err != nil {
return err
}
}
return nil
}
func (vs *Visitor) OnUintArray(a []uint) error {
if done, err := vs.onEmptyArray(len(a)); done {
return err
}
// find type:
minT := int8Marker
for _, v := range a {
minT = maxNumType(minT, uintType(uint64(v)))
}
// serialize array
if err := vs.onArray(minT, len(a)); err != nil {
return err
}
for _, v := range a {
if err := vs.uint64(uint64(v), minT, false); err != nil {
return err
}
}
return nil
}
func (vs *Visitor) OnFloat32Array(a []float32) error {
if done, err := vs.onEmptyArray(len(a)); done {
return err
}
if err := vs.onArray(float32Marker, len(a)); err != nil {
return err
}
for _, v := range a {
if err := vs.float32(v, false); err != nil {
return err
}
}
return nil
}
func (vs *Visitor) OnFloat64Array(a []float64) error {
if done, err := vs.onEmptyArray(len(a)); done {
return err
}
if err := vs.onArray(float64Marker, len(a)); err != nil {
return err
}
for _, v := range a {
if err := vs.float64(v, false); err != nil {
return err
}
}
return nil
}
func (vs *Visitor) OnStringObject(m map[string]string) error {
if done, err := vs.onEmptyObject(len(m)); done {
return err
}
if err := vs.onObject(stringMarker, len(m)); err != nil {
return err
}
for k, v := range m {
if err := vs.string(str2Bytes(k), false); err != nil {
return err
}
if err := vs.string(str2Bytes(v), false); err != nil {
return err
}
}
return nil
}
func (vs *Visitor) OnBoolObject(m map[string]bool) error {
if done, err := vs.onEmptyObject(len(m)); done {
return err
}
if err := vs.optionalCount(len(m)); err != nil {
return err
}
for k, v := range m {
if err := vs.string(str2Bytes(k), false); err != nil {
return err
}
if err := vs.OnBool(v); err != nil {
return err
}
}
return nil
}
func (vs *Visitor) OnInt8Object(m map[string]int8) error {
if done, err := vs.onEmptyObject(len(m)); done {
return err
}
if err := vs.onObject(int8Marker, len(m)); err != nil {
return err
}
for k, v := range m {
if err := vs.string(str2Bytes(k), false); err != nil {
return err
}
if err := vs.int8(v, false); err != nil {
return err
}
}
return nil
}
func (vs *Visitor) OnInt16Object(m map[string]int16) error {
if done, err := vs.onEmptyObject(len(m)); done {
return err
}
if err := vs.onObject(int16Marker, len(m)); err != nil {
return err
}
for k, v := range m {
if err := vs.string(str2Bytes(k), false); err != nil {
return err
}
if err := vs.int16(v, false); err != nil {
return err
}
}
return nil
}
func (vs *Visitor) OnInt32Object(m map[string]int32) error {
if done, err := vs.onEmptyObject(len(m)); done {
return err
}
if err := vs.onObject(int32Marker, len(m)); err != nil {
return err
}
for k, v := range m {
if err := vs.string(str2Bytes(k), false); err != nil {
return err
}
if err := vs.int32(v, false); err != nil {
return err
}
}
return nil
}
func (vs *Visitor) OnInt64Object(m map[string]int64) error {
if done, err := vs.onEmptyObject(len(m)); done {
return err
}
if err := vs.onObject(int64Marker, len(m)); err != nil {
return err
}
for k, v := range m {
if err := vs.string(str2Bytes(k), false); err != nil {
return err
}
if err := vs.int64(v, false); err != nil {
return err
}
}
return nil
}
func (vs *Visitor) OnIntObject(m map[string]int) error {
if done, err := vs.onEmptyObject(len(m)); done {
return err
}
marker := int32Marker
if isInt64 {
marker = int64Marker
}
if err := vs.onObject(marker, len(m)); err != nil {
return err
}
for k, v := range m {
var err error
if err = vs.string(str2Bytes(k), false); err != nil {
return err
}
if isInt64 {
err = vs.int64(int64(v), false)
} else {
err = vs.int32(int32(v), false)
}
if err != nil {
return err
}
}
return nil
}
func (vs *Visitor) OnUint8Object(m map[string]uint8) error {
if done, err := vs.onEmptyObject(len(m)); done {
return err
}
if err := vs.onObject(uint8Marker, len(m)); err != nil {
return err
}
for k, v := range m {
if err := vs.string(str2Bytes(k), false); err != nil {
return err
}
if err := vs.uint8(v, false); err != nil {
return err
}
}
return nil
}
func (vs *Visitor) OnUint16Object(m map[string]uint16) error {
if done, err := vs.onEmptyObject(len(m)); done {
return err
}
// find type:
minT := int8Marker
for _, v := range m {
minT = maxNumType(minT, uintType(uint64(v)))
}
//serialize object
if err := vs.onObject(minT, len(m)); err != nil {
return err
}
for k, v := range m {
if err := vs.string(str2Bytes(k), false); err != nil {
return err
}
if err := vs.uint64(uint64(v), minT, false); err != nil {
return err
}
}
return nil
}
func (vs *Visitor) OnUint32Object(m map[string]uint32) error {
if done, err := vs.onEmptyObject(len(m)); done {
return err
}
// find type:
minT := int8Marker
for _, v := range m {
minT = maxNumType(minT, uintType(uint64(v)))
}
//serialize object
if err := vs.onObject(minT, len(m)); err != nil {
return err
}
for k, v := range m {
if err := vs.string(str2Bytes(k), false); err != nil {
return err
}
if err := vs.uint64(uint64(v), minT, false); err != nil {
return err
}
}
return nil
}
func (vs *Visitor) OnUint64Object(m map[string]uint64) error {
if done, err := vs.onEmptyObject(len(m)); done {
return err
}
// find type:
minT := int8Marker
for _, v := range m {
minT = maxNumType(minT, uintType(uint64(v)))
}
//serialize object
if err := vs.onObject(minT, len(m)); err != nil {
return err
}
for k, v := range m {
if err := vs.string(str2Bytes(k), false); err != nil {
return err
}
if err := vs.uint64(uint64(v), minT, false); err != nil {
return err
}
}
return nil
}
func (vs *Visitor) OnUintObject(m map[string]uint) error {
if done, err := vs.onEmptyObject(len(m)); done {
return err
}
// find type:
minT := int8Marker
for _, v := range m {
minT = maxNumType(minT, uintType(uint64(v)))
}
//serialize object
if err := vs.onObject(minT, len(m)); err != nil {
return err
}
for k, v := range m {
if err := vs.string(str2Bytes(k), false); err != nil {
return err
}
if err := vs.uint64(uint64(v), minT, false); err != nil {
return err
}
}
return nil
}
func (vs *Visitor) OnFloat32Object(m map[string]float32) error {
if done, err := vs.onEmptyObject(len(m)); done {
return err
}
if err := vs.onObject(float32Marker, len(m)); err != nil {
return err
}
for k, v := range m {
if err := vs.string(str2Bytes(k), false); err != nil {
return err
}
if err := vs.float32(v, false); err != nil {
return err
}
}
return nil
}
func (vs *Visitor) OnFloat64Object(m map[string]float64) error {
if done, err := vs.onEmptyObject(len(m)); done {
return err
}
if err := vs.onObject(float64Marker, len(m)); err != nil {
return err
}
for k, v := range m {
if err := vs.string(str2Bytes(k), false); err != nil {
return err
}
if err := vs.float64(v, false); err != nil {
return err
}
}
return nil
}
func (vs *Visitor) onEmptyArray(l int) (bool, error) {
if l > 0 {
return false, nil
}
vs.scratch[0], vs.scratch[1] = arrStartMarker, arrEndMarker
return true, vs.w.write(vs.scratch[:2])
}
func (vs *Visitor) onEmptyObject(l int) (bool, error) {
if l > 0 {
return false, nil
}
vs.scratch[0], vs.scratch[1] = objStartMarker, objEndMarker
return true, vs.w.write(vs.scratch[:2])
}
func (vs *Visitor) onObject(marker byte, count int) error {
return vs.onTypedStruct(objStartMarker, marker, count)
}
func maxNumType(a, b byte) byte {
switch {
case a == highPrecMarker || b == highPrecMarker:
return highPrecMarker
case a == int64Marker || b == int64Marker:
return int64Marker
case a == int32Marker || b == int32Marker:
return int32Marker
case a == int16Marker || b == int16Marker:
return int16Marker
case a == uint8Marker || b == uint8Marker:
return uint8Marker
default:
return int8Marker
}
}
func uintType(u uint64) byte {
switch {
case u <= math.MaxInt8:
return int8Marker
case u <= math.MaxUint8:
return uint8Marker
case u <= math.MaxInt16:
return int16Marker
case u <= math.MaxInt32:
return int32Marker
case u <= math.MaxInt64:
return int64Marker
default:
return highPrecMarker
}
}