encoder/encoder.go
// Copyright (c) 2020-2023 Ozan Hacıbekiroğlu.
// Use of this source code is governed by a MIT License
// that can be found in the LICENSE file.
package encoder
import (
"bytes"
"encoding"
"encoding/binary"
"encoding/gob"
"errors"
"fmt"
"io"
"math"
"strconv"
"github.com/ozanh/ugo"
"github.com/ozanh/ugo/parser"
"github.com/ozanh/ugo/stdlib/json"
"github.com/ozanh/ugo/stdlib/time"
)
// Bytecode signature and version are written to the header of encoded Bytecode.
// Bytecode is encoded with current BytecodeVersion and its format.
const (
BytecodeSignature uint32 = 0x75474F
BytecodeVersion uint16 = 1
)
// Types implementing encoding.BinaryMarshaler encoding.BinaryUnmarshaler.
type (
Bytecode ugo.Bytecode
CompiledFunction ugo.CompiledFunction
BuiltinFunction ugo.BuiltinFunction
Function ugo.Function
UndefinedType ugo.UndefinedType
String ugo.String
Bytes ugo.Bytes
Array ugo.Array
Map ugo.Map
SyncMap ugo.SyncMap
Int ugo.Int
Uint ugo.Uint
Char ugo.Char
Float ugo.Float
Bool ugo.Bool
SourceFileSet parser.SourceFileSet
SourceFile parser.SourceFile
)
const (
binUndefinedV1 byte = iota
binTrueV1
binFalseV1
binIntV1
binUintV1
binCharV1
binFloatV1
binStringV1
binBytesV1
binArrayV1
binMapV1
binSyncMapV1
binCompiledFunctionV1
binFunctionV1
binBuiltinFunctionV1
binUnkownType byte = 255
)
var (
errVarintTooSmall = errors.New("read varint error: buf too small")
errVarintOverflow = errors.New("read varint error: value larger than 64 bits (overflow)")
)
func init() {
gob.Register(ugo.Undefined)
gob.Register(ugo.Bool(true))
gob.Register(ugo.Int(0))
gob.Register(ugo.Uint(0))
gob.Register(ugo.Char(0))
gob.Register(ugo.Float(0))
gob.Register(ugo.String(""))
gob.Register(ugo.Bytes(nil))
gob.Register(ugo.Array(nil))
gob.Register(ugo.Map(nil))
gob.Register((*ugo.Error)(nil))
gob.Register((*ugo.RuntimeError)(nil))
gob.Register((*ugo.SyncMap)(nil))
gob.Register((*ugo.ObjectPtr)(nil))
gob.Register((*time.Time)(nil))
gob.Register((*json.EncoderOptions)(nil))
gob.Register((*json.RawMessage)(nil))
}
// MarshalBinary implements encoding.BinaryMarshaler
func (bc *Bytecode) MarshalBinary() (data []byte, err error) {
switch BytecodeVersion {
case 1:
var buf bytes.Buffer
if err = bc.bytecodeV1Encoder(&buf); err != nil {
return nil, err
}
return buf.Bytes(), nil
default:
panic("invalid Bytecode version:" + strconv.Itoa(int(BytecodeVersion)))
}
}
// UnmarshalBinary implements encoding.BinaryUnmarshaler
// Do not use this method if builtin modules are used, instead use Decode method.
func (bc *Bytecode) UnmarshalBinary(data []byte) error {
if len(data) < 6 {
return &ugo.Error{
Name: "encoder.Bytecode.UnmarshalBinary",
Message: "invalid data",
}
}
sig := binary.BigEndian.Uint32(data[0:4])
if sig != BytecodeSignature {
return &ugo.Error{
Name: "encoder.Bytecode.UnmarshalBinary",
Message: "signature mismatch",
}
}
version := binary.BigEndian.Uint16(data[4:6])
switch version {
case BytecodeVersion:
buf := bytes.NewBuffer(data[6:])
err := bc.bytecodeV1Decoder(buf)
if err != nil {
return err
}
return nil
default:
return &ugo.Error{
Name: "encoder.Bytecode.UnmarshalBinary",
Message: "unsupported version:" + strconv.Itoa(int(version)),
}
}
}
func putBytecodeHeader(w io.Writer) (err error) {
sig := make([]byte, 4)
binary.BigEndian.PutUint32(sig, BytecodeSignature)
if _, err = io.Copy(w, bytes.NewReader(sig)); err != nil {
return
}
bcVersion := make([]byte, 2)
binary.BigEndian.PutUint16(bcVersion, BytecodeVersion)
if _, err = io.Copy(w, bytes.NewReader(bcVersion)); err != nil {
return
}
return nil
}
func (bc *Bytecode) bytecodeV1Encoder(w io.Writer) (err error) {
if err = putBytecodeHeader(w); err != nil {
return
}
// FileSet, field #0
if bc.FileSet != nil {
_ = writeByteTo(w, 0)
var data []byte
fs := (*SourceFileSet)(bc.FileSet)
if data, err = fs.MarshalBinary(); err != nil {
return
}
var sz []byte
if sz, err = Int(len(data)).MarshalBinary(); err != nil {
return
}
_, _ = w.Write(sz)
_, _ = w.Write(data)
}
// Main, field #1
if bc.Main != nil {
_ = writeByteTo(w, 1)
var data []byte
if data, err = (*CompiledFunction)(bc.Main).MarshalBinary(); err != nil {
return
}
if _, err = w.Write(data); err != nil {
return
}
}
// Constants, field #2
if bc.Constants != nil {
_ = writeByteTo(w, 2)
var data []byte
if data, err = Array(bc.Constants).MarshalBinary(); err != nil {
return
}
if _, err = w.Write(data); err != nil {
return
}
}
// NumModules, field #3
if bc.NumModules > 0 {
_ = writeByteTo(w, 3)
var data []byte
data, err = Int(bc.NumModules).MarshalBinary()
if err != nil {
return
}
if _, err = w.Write(data); err != nil {
return
}
}
return nil
}
func (bc *Bytecode) bytecodeV1Decoder(r *bytes.Buffer) error {
for {
field, err := r.ReadByte()
if err != nil {
if err == io.EOF {
return nil
}
return err
}
switch field {
case 0:
obj, err := DecodeObject(r)
if err != nil {
return err
}
sz := obj.(ugo.Int)
if sz <= 0 {
continue
}
data := make([]byte, sz)
if _, err = io.ReadFull(r, data); err != nil {
return err
}
var fs SourceFileSet
if err = fs.UnmarshalBinary(data); err != nil {
return err
}
bc.FileSet = (*parser.SourceFileSet)(&fs)
case 1:
f, err := DecodeObject(r)
if err != nil {
return err
}
bc.Main = f.(*ugo.CompiledFunction)
case 2:
obj, err := DecodeObject(r)
if err != nil {
return err
}
bc.Constants = obj.(ugo.Array)
case 3:
num, err := DecodeObject(r)
if err != nil {
return err
}
bc.NumModules = int(num.(ugo.Int))
default:
return errors.New("unknown field:" + strconv.Itoa(int(field)))
}
}
}
// DecodeObject decodes and returns Object from a io.Reader which is encoded with MarshalBinary.
func DecodeObject(r io.Reader) (ugo.Object, error) {
btype, err := readByteFrom(r)
if err != nil {
return nil, err
}
switch btype {
case binUndefinedV1:
return ugo.Undefined, nil
case binTrueV1:
return ugo.True, nil
case binFalseV1:
return ugo.False, nil
case binIntV1,
binUintV1,
binFloatV1,
binCharV1:
size, err := readByteFrom(r)
if err != nil {
return nil, err
}
buf := make([]byte, 2+size)
buf[0] = btype
buf[1] = size
if size > 0 {
if _, err = io.ReadFull(r, buf[2:]); err != nil {
return nil, err
}
}
switch btype {
case binIntV1:
var v Int
if err = v.UnmarshalBinary(buf); err != nil {
return nil, err
}
return ugo.Int(v), nil
case binUintV1:
var v Uint
if err = v.UnmarshalBinary(buf); err != nil {
return nil, err
}
return ugo.Uint(v), nil
case binFloatV1:
var v Float
if err = v.UnmarshalBinary(buf); err != nil {
return nil, err
}
return ugo.Float(v), nil
case binCharV1:
var v Char
if err = v.UnmarshalBinary(buf); err != nil {
return nil, err
}
return ugo.Char(v), nil
}
case binCompiledFunctionV1,
binArrayV1,
binBytesV1,
binStringV1,
binMapV1,
binSyncMapV1,
binFunctionV1,
binBuiltinFunctionV1:
var vi varintConv
value, readBytes, err := vi.readBytes(r)
if err != nil {
return nil, err
}
if value < 0 {
return nil, errors.New("negative value")
}
n := 1 + len(readBytes)
buf := make([]byte, n+int(value))
buf[0] = btype
copy(buf[1:], readBytes)
if value > 0 {
if _, err = io.ReadFull(r, buf[n:]); err != nil {
return nil, err
}
}
switch btype {
case binCompiledFunctionV1:
var v CompiledFunction
if err := v.UnmarshalBinary(buf); err != nil {
return nil, err
}
return (*ugo.CompiledFunction)(&v), nil
case binArrayV1:
var v = Array{}
if err := v.UnmarshalBinary(buf); err != nil {
return nil, err
}
return ugo.Array(v), nil
case binBytesV1:
var v = Bytes{}
if err := v.UnmarshalBinary(buf); err != nil {
return nil, err
}
return ugo.Bytes(v), nil
case binStringV1:
var v String
if err := v.UnmarshalBinary(buf); err != nil {
return nil, err
}
return ugo.String(v), nil
case binMapV1:
var v = Map{}
if err := v.UnmarshalBinary(buf); err != nil {
return nil, err
}
return ugo.Map(v), nil
case binSyncMapV1:
var v SyncMap
if err := v.UnmarshalBinary(buf); err != nil {
return nil, err
}
return (*ugo.SyncMap)(&v), nil
case binFunctionV1:
var v Function
if err := v.UnmarshalBinary(buf); err != nil {
return nil, err
}
return (*ugo.Function)(&v), nil
case binBuiltinFunctionV1:
var v BuiltinFunction
if err := v.UnmarshalBinary(buf); err != nil {
return nil, err
}
return (*ugo.BuiltinFunction)(&v), nil
}
case binUnkownType:
var v ugo.Object
if err := gob.NewDecoder(r).Decode(&v); err != nil {
return nil, err
}
return v, nil
}
return nil, errors.New(
"decode error: unknown encoding type:" + strconv.Itoa(int(btype)),
)
}
// MarshalBinary implements encoding.BinaryMarshaler
func (o *UndefinedType) MarshalBinary() ([]byte, error) {
return []byte{binUndefinedV1}, nil
}
// UnmarshalBinary implements encoding.BinaryUnmarshaler
func (o *UndefinedType) UnmarshalBinary(data []byte) error {
if len(data) < 1 || data[0] != binUndefinedV1 {
return errors.New("invalid ugo.Undefined data")
}
return nil
}
// MarshalBinary implements encoding.BinaryMarshaler
func (o Bool) MarshalBinary() ([]byte, error) {
if o {
return []byte{binTrueV1}, nil
}
return []byte{binFalseV1}, nil
}
// UnmarshalBinary implements encoding.BinaryUnmarshaler
func (o *Bool) UnmarshalBinary(data []byte) error {
if len(data) < 1 {
return errors.New("invalid ugo.Bool data")
}
if data[0] == binTrueV1 {
*o = true
return nil
}
if data[0] == binFalseV1 {
*o = false
return nil
}
return errors.New("invalid ugo.Bool data")
}
// MarshalBinary implements encoding.BinaryMarshaler
func (o Int) MarshalBinary() ([]byte, error) {
buf := make([]byte, 2+binary.MaxVarintLen64)
buf[0] = binIntV1
if o == 0 {
buf[1] = 0
return buf[:2], nil
}
n := binary.PutVarint(buf[2:], int64(o))
buf[1] = byte(n)
return buf[:2+n], nil
}
// UnmarshalBinary implements encoding.BinaryUnmarshaler
func (o *Int) UnmarshalBinary(data []byte) error {
if len(data) < 2 || data[0] != binIntV1 {
return errors.New("invalid ugo.Int data")
}
size := int(data[1])
if size <= 0 {
return nil
}
if len(data) < 2+size {
return errors.New("invalid ugo.Int data size")
}
v, n := binary.Varint(data[2:])
if n < 1 {
if n == 0 {
return errors.New("ugo.Int data buffer too small")
}
return errors.New("ugo.Int value larger than 64 bits")
}
*o = Int(v)
return nil
}
// MarshalBinary implements encoding.BinaryMarshaler
func (o Uint) MarshalBinary() ([]byte, error) {
buf := make([]byte, 2+binary.MaxVarintLen64)
buf[0] = binUintV1
if o == 0 {
buf[1] = 0
return buf[:2], nil
}
n := binary.PutUvarint(buf[2:], uint64(o))
buf[1] = byte(n)
return buf[:2+n], nil
}
// UnmarshalBinary implements encoding.BinaryUnmarshaler
func (o *Uint) UnmarshalBinary(data []byte) error {
if len(data) < 2 || data[0] != binUintV1 {
return errors.New("invalid ugo.Uint data")
}
size := int(data[1])
if size <= 0 {
return nil
}
if len(data) < 2+size {
return errors.New("invalid ugo.Uint data size")
}
v, n := binary.Uvarint(data[2:])
if n < 1 {
if n == 0 {
return errors.New("ugo.Uint data buffer too small")
}
return errors.New("ugo.Uint value larger than 64 bits")
}
*o = Uint(v)
return nil
}
// MarshalBinary implements encoding.BinaryMarshaler
func (o Char) MarshalBinary() ([]byte, error) {
buf := make([]byte, 2+binary.MaxVarintLen32)
buf[0] = binCharV1
if o == 0 {
buf[1] = 0
return buf[:2], nil
}
n := binary.PutVarint(buf[2:], int64(o))
buf[1] = byte(n)
return buf[:2+n], nil
}
// UnmarshalBinary implements encoding.BinaryUnmarshaler
func (o *Char) UnmarshalBinary(data []byte) error {
if len(data) < 2 || data[0] != binCharV1 {
return errors.New("invalid ugo.Char data")
}
size := int(data[1])
if size <= 0 {
return nil
}
if len(data) < 2+size {
return errors.New("invalid ugo.Char data size")
}
v, n := binary.Varint(data[2:])
if n < 1 {
if n == 0 {
return errors.New("ugo.Char data buffer too small")
}
return errors.New("ugo.Char value larger than 64 bits")
}
if int64(rune(v)) != v {
return errors.New("ugo.Char value larger than 32 bits")
}
*o = Char(v)
return nil
}
// MarshalBinary implements encoding.BinaryMarshaler
func (o Float) MarshalBinary() ([]byte, error) {
buf := make([]byte, 2+binary.MaxVarintLen64)
buf[0] = binFloatV1
if o == 0 {
buf[1] = 0
return buf[:2], nil
}
n := binary.PutUvarint(buf[2:], math.Float64bits(float64(o)))
buf[1] = byte(n)
return buf[:2+n], nil
}
// UnmarshalBinary implements encoding.BinaryUnmarshaler
func (o *Float) UnmarshalBinary(data []byte) error {
if len(data) < 2 || data[0] != binFloatV1 {
return errors.New("invalid ugo.Float data")
}
size := int(data[1])
if size <= 0 {
return nil
}
if len(data) < 2+size {
return errors.New("invalid ugo.Float data size")
}
v, n := binary.Uvarint(data[2:])
if n < 1 {
if n == 0 {
return errors.New("ugo.Float data buffer too small")
}
return errors.New("ugo.Float value larger than 64 bits")
}
*o = Float(math.Float64frombits(v))
return nil
}
// MarshalBinary implements encoding.BinaryMarshaler
func (o String) MarshalBinary() ([]byte, error) {
var buf bytes.Buffer
buf.WriteByte(binStringV1)
size := int64(len(o))
if size == 0 {
buf.WriteByte(0)
return buf.Bytes(), nil
}
var vi varintConv
b := vi.toBytes(size)
buf.Write(b)
buf.WriteString(string(o))
return buf.Bytes(), nil
}
// UnmarshalBinary implements encoding.BinaryUnmarshaler
func (o *String) UnmarshalBinary(data []byte) error {
if len(data) < 2 || data[0] != binStringV1 {
return errors.New("invalid ugo.String data")
}
size, offset, err := toVarint(data[1:])
if err != nil {
return err
}
if size <= 0 {
return nil
}
ub := 1 + offset + int(size)
if len(data) < ub {
return errors.New("invalid ugo.String data size")
}
*o = String(data[1+offset : ub])
return nil
}
// MarshalBinary implements encoding.BinaryMarshaler
func (o Bytes) MarshalBinary() ([]byte, error) {
var buf bytes.Buffer
buf.WriteByte(binBytesV1)
size := int64(len(o))
if size == 0 {
buf.WriteByte(0)
return buf.Bytes(), nil
}
var vi varintConv
b := vi.toBytes(size)
buf.Write(b)
buf.Write(o)
return buf.Bytes(), nil
}
// UnmarshalBinary implements encoding.BinaryUnmarshaler
func (o *Bytes) UnmarshalBinary(data []byte) error {
if len(data) < 2 || data[0] != binBytesV1 {
return errors.New("invalid ugo.Bytes data")
}
size, offset, err := toVarint(data[1:])
if err != nil {
return err
}
if size <= 0 {
return nil
}
ub := 1 + offset + int(size)
if len(data) < ub {
return errors.New("invalid ugo.Bytes data size")
}
*o = []byte(string(data[1+offset : ub]))
return nil
}
// MarshalBinary implements encoding.BinaryMarshaler
func (o Array) MarshalBinary() ([]byte, error) {
var buf bytes.Buffer
buf.WriteByte(binArrayV1)
if len(o) == 0 {
buf.WriteByte(0)
return buf.Bytes(), nil
}
var tmpBuf bytes.Buffer
var vi varintConv
b := vi.toBytes(int64(len(o)))
tmpBuf.Write(b)
for _, v := range o {
if m := marshaler(v); m != nil {
d, err := m.MarshalBinary()
if err != nil {
return nil, err
}
tmpBuf.Write(d)
} else {
tmpBuf.WriteByte(binUnkownType)
if err := gob.NewEncoder(&tmpBuf).Encode(&v); err != nil {
return nil, err
}
}
}
b = vi.toBytes(int64(tmpBuf.Len()))
buf.Write(b)
buf.Write(tmpBuf.Bytes())
return buf.Bytes(), nil
}
// UnmarshalBinary implements encoding.BinaryUnmarshaler
func (o *Array) UnmarshalBinary(data []byte) error {
if len(data) < 2 || data[0] != binArrayV1 {
return errors.New("invalid ugo.Array data")
}
size, offset, err := toVarint(data[1:])
if err != nil {
return err
}
if size <= 0 {
return nil
}
ub := 1 + offset + int(size)
if len(data) < ub {
return errors.New("invalid ugo.Array data size")
}
rd := bytes.NewReader(data[1+offset : ub])
var vi varintConv
vi.reader = rd
length, err := vi.read()
if err != nil {
return err
}
arr := make([]ugo.Object, 0, int(length))
for rd.Len() > 0 {
o, err := DecodeObject(rd)
if err != nil {
return err
}
arr = append(arr, o)
}
*o = arr
return nil
}
// MarshalBinary implements encoding.BinaryMarshaler
func (o Map) MarshalBinary() ([]byte, error) {
var buf bytes.Buffer
buf.WriteByte(binMapV1)
var tmpBuf bytes.Buffer
var vi varintConv
for k, v := range o {
b := vi.toBytes(int64(len(k)))
tmpBuf.Write(b)
tmpBuf.WriteString(k)
if m := marshaler(v); m != nil {
d, err := m.MarshalBinary()
if err != nil {
return nil, err
}
tmpBuf.Write(d)
} else {
tmpBuf.WriteByte(binUnkownType)
if err := gob.NewEncoder(&tmpBuf).Encode(&v); err != nil {
return nil, err
}
}
}
b := vi.toBytes(int64(tmpBuf.Len()))
buf.Write(b)
buf.Write(tmpBuf.Bytes())
return buf.Bytes(), nil
}
// UnmarshalBinary implements encoding.BinaryUnmarshaler
func (o *Map) UnmarshalBinary(data []byte) error {
if len(data) < 2 || data[0] != binMapV1 {
return errors.New("invalid ugo.Map data")
}
size, offset, err := toVarint(data[1:])
if err != nil {
return err
}
if size <= 0 {
return nil
}
if len(data) < 1+offset+int(size) {
return errors.New("invalid ugo.Map data size")
}
rd := bytes.NewReader(data[1+offset : 1+offset+int(size)])
strBuf := bytes.NewBuffer(nil)
var vi varintConv
vi.reader = rd
m := *o
for rd.Len() > 0 {
value, err := vi.read()
if err != nil {
return err
}
var k string
if value > 0 {
strBuf.Reset()
if _, err = io.CopyN(strBuf, rd, value); err != nil {
return err
}
k = strBuf.String()
}
o, err := DecodeObject(rd)
if err != nil {
return err
}
m[k] = o
}
return nil
}
// MarshalBinary implements encoding.BinaryMarshaler
func (o *SyncMap) MarshalBinary() ([]byte, error) {
(*ugo.SyncMap)(o).RLock()
defer (*ugo.SyncMap)(o).RUnlock()
var buf bytes.Buffer
if o.Value == nil {
buf.WriteByte(binSyncMapV1)
buf.WriteByte(0)
return buf.Bytes(), nil
}
b, err := Map(o.Value).MarshalBinary()
if err != nil {
return nil, err
}
if len(b) > 0 {
b[0] = binSyncMapV1
}
return b, nil
}
// UnmarshalBinary implements encoding.BinaryUnmarshaler
func (o *SyncMap) UnmarshalBinary(data []byte) error {
if len(data) < 2 || data[0] != binSyncMapV1 {
return errors.New("invalid ugo.SyncMap data")
}
if data[1] == 0 {
return nil
}
data[0] = binMapV1
m := Map{}
if err := m.UnmarshalBinary(data); err != nil {
data[0] = binSyncMapV1
return err
}
data[0] = binSyncMapV1
o.Value = (ugo.Map)(m)
return nil
}
// MarshalBinary implements encoding.BinaryMarshaler
func (o *CompiledFunction) MarshalBinary() ([]byte, error) {
var tmpBuf bytes.Buffer
var vi varintConv
if o.NumParams > 0 {
// NumParams field #0
tmpBuf.WriteByte(0)
b := vi.toBytes(int64(o.NumParams))
tmpBuf.Write(b)
}
if o.NumLocals > 0 {
// NumLocals field #1
tmpBuf.WriteByte(1)
b := vi.toBytes(int64(o.NumLocals))
tmpBuf.Write(b)
}
if o.Instructions != nil {
// Instructions field #2
tmpBuf.WriteByte(2)
data, err := Bytes(o.Instructions).MarshalBinary()
if err != nil {
return nil, err
}
tmpBuf.Write(data)
}
// Variadic field #3
if o.Variadic {
tmpBuf.WriteByte(3)
}
// Free field #4, ignore Free variables, doesn't make sense
if o.SourceMap != nil {
// SourceMap field #5
tmpBuf.WriteByte(5)
b := vi.toBytes(int64(len(o.SourceMap) * 2))
tmpBuf.Write(b)
for key, value := range o.SourceMap {
b = vi.toBytes(int64(key))
tmpBuf.Write(b)
b = vi.toBytes(int64(value))
tmpBuf.Write(b)
}
}
var buf bytes.Buffer
size := vi.toBytes(int64(tmpBuf.Len()))
buf.WriteByte(binCompiledFunctionV1)
buf.Write(size)
buf.Write(tmpBuf.Bytes())
return buf.Bytes(), nil
}
// UnmarshalBinary implements encoding.BinaryUnmarshaler
func (o *CompiledFunction) UnmarshalBinary(data []byte) error {
if len(data) < 2 || data[0] != binCompiledFunctionV1 {
return errors.New("invalid ugo.CompiledFunction data")
}
size, offset, err := toVarint(data[1:])
if err != nil {
return err
}
if size <= 0 {
return nil
}
rd := bytes.NewReader(data[1+offset : 1+offset+int(size)])
var vi varintConv
vi.reader = rd
for rd.Len() > 0 {
field, err := rd.ReadByte()
if err != nil {
return err
}
switch field {
case 0:
v, err := vi.read()
if err != nil {
return err
}
o.NumParams = int(v)
case 1:
v, err := vi.read()
if err != nil {
return err
}
o.NumLocals = int(v)
case 2:
obj, err := DecodeObject(rd)
if err != nil {
return err
}
o.Instructions = obj.(ugo.Bytes)
case 3:
o.Variadic = true
case 4:
return errors.New("unexpected field #4")
case 5:
length, err := vi.read()
if err != nil {
return err
}
sz := int(length / 2)
// always put size to the map to decode faster
o.SourceMap = make(map[int]int, sz)
for i := 0; i < sz; i++ {
key, err := vi.read()
if err != nil {
return err
}
value, err := vi.read()
if err != nil {
return err
}
o.SourceMap[int(key)] = int(value)
}
default:
return errors.New("unknown field:" + strconv.Itoa(int(field)))
}
}
return nil
}
// MarshalBinary implements encoding.BinaryMarshaler
func (o *BuiltinFunction) MarshalBinary() ([]byte, error) {
// Note: use string name instead of index of builtin
s, err := String(o.Name).MarshalBinary()
if err != nil {
return nil, err
}
var vi varintConv
b := vi.toBytes(int64(len(s)))
data := make([]byte, 0, 1+len(b)+len(s))
data = append(data, binBuiltinFunctionV1)
data = append(data, b...)
data = append(data, s...)
return data, nil
}
// UnmarshalBinary implements encoding.BinaryUnmarshaler
func (o *BuiltinFunction) UnmarshalBinary(data []byte) error {
if len(data) < 2 || data[0] != binBuiltinFunctionV1 {
return errors.New("invalid ugo.BuiltinFunction data")
}
size, offset, err := toVarint(data[1:])
if err != nil {
return err
}
if size <= 0 {
return errors.New("invalid ugo.BuiltinFunction data size")
}
var s String
if err := s.UnmarshalBinary(data[1+offset:]); err != nil {
return err
}
index, ok := ugo.BuiltinsMap[string(s)]
if !ok {
return fmt.Errorf("builtin '%s' not found", s)
}
obj := ugo.BuiltinObjects[index]
f, ok := obj.(*BuiltinFunction)
if ok {
*o = *f
return nil
}
return fmt.Errorf("builtin '%s' not a ugo.BuiltinFunction type", s)
}
// MarshalBinary implements encoding.BinaryMarshaler
func (o *Function) MarshalBinary() ([]byte, error) {
s, err := String(o.Name).MarshalBinary()
if err != nil {
return nil, err
}
var vi varintConv
b := vi.toBytes(int64(len(s)))
data := make([]byte, 0, 1+len(b)+len(s))
data = append(data, binFunctionV1)
data = append(data, b...)
data = append(data, s...)
return data, nil
}
// UnmarshalBinary implements encoding.BinaryUnmarshaler
func (o *Function) UnmarshalBinary(data []byte) error {
if len(data) < 2 || data[0] != binFunctionV1 {
return errors.New("invalid ugo.Function data")
}
size, offset, err := toVarint(data[1:])
if err != nil {
return err
}
if size <= 0 {
return errors.New("invalid ugo.Function data size")
}
var s String
if err := s.UnmarshalBinary(data[1+offset:]); err != nil {
return err
}
o.Name = string(s)
return nil
}
// MarshalBinary implements encoding.BinaryMarshaler
func (sf *SourceFile) MarshalBinary() ([]byte, error) {
var buf bytes.Buffer
d, err := String(sf.Name).MarshalBinary()
if err != nil {
return nil, err
}
buf.Write(d)
var vi varintConv
b := vi.toBytes(int64(sf.Base))
buf.Write(b)
b = vi.toBytes(int64(sf.Size))
buf.Write(b)
b = vi.toBytes(int64(len(sf.Lines)))
buf.Write(b)
for _, v := range sf.Lines {
b = vi.toBytes(int64(v))
buf.Write(b)
}
return buf.Bytes(), nil
}
// UnmarshalBinary implements encoding.BinaryUnmarshaler
func (sf *SourceFile) UnmarshalBinary(data []byte) error {
rd := bytes.NewReader(data)
obj, err := DecodeObject(rd)
if err != nil {
return err
}
sf.Name = obj.String()
var vi varintConv
vi.reader = rd
v, err := vi.read()
if err != nil {
return err
}
sf.Base = int(v)
v, err = vi.read()
if err != nil {
return err
}
sf.Size = int(v)
v, err = vi.read()
if err != nil {
return err
}
length := int(v)
lines := make([]int, length)
for i := 0; i < length; i++ {
v, err = vi.read()
if err != nil {
return err
}
lines[i] = int(v)
}
if rd.Len() > 0 {
return errors.New("unread bytes")
}
sf.Lines = lines
return nil
}
// MarshalBinary implements encoding.BinaryMarshaler
func (sfs *SourceFileSet) MarshalBinary() ([]byte, error) {
var buf bytes.Buffer
var vi varintConv
b := vi.toBytes(int64(sfs.Base))
buf.Write(b)
b = vi.toBytes(int64(len(sfs.Files)))
buf.Write(b)
for _, v := range sfs.Files {
if v == nil {
continue
}
d, err := (*SourceFile)(v).MarshalBinary()
if err != nil {
return nil, err
}
b := vi.toBytes(int64(len(d)))
buf.Write(b)
buf.Write(d)
}
return buf.Bytes(), nil
}
// UnmarshalBinary implements encoding.BinaryUnmarshaler
func (sfs *SourceFileSet) UnmarshalBinary(data []byte) error {
rd := bytes.NewReader(data)
var vi varintConv
vi.reader = rd
v, err := vi.read()
if err != nil {
return err
}
sfs.Base = int(v)
v, err = vi.read()
if err != nil {
return err
}
length := int(v)
files := make([]*parser.SourceFile, length)
for i := 0; i < length; i++ {
v, err = vi.read()
if err != nil {
return err
}
data := make([]byte, v)
if _, err = io.ReadFull(rd, data); err != nil {
return err
}
var file SourceFile
if err = file.UnmarshalBinary(data); err != nil {
return err
}
files[i] = (*parser.SourceFile)(&file)
}
if rd.Len() > 0 {
return errors.New("unread bytes")
}
sfs.Files = files
return nil
}
func readByteFrom(r io.Reader) (byte, error) {
if br, ok := r.(io.ByteReader); ok {
return br.ReadByte()
}
var one = []byte{0}
n, err := r.Read(one)
if err != nil {
if err == io.EOF {
if n == 1 {
return one[0], nil
}
}
return 0, err
}
if n == 1 {
return one[0], nil
}
return 0, errors.New("byte read error")
}
func writeByteTo(w io.Writer, b byte) error {
if bw, ok := w.(io.ByteWriter); ok {
return bw.WriteByte(b)
}
n, err := w.Write([]byte{b})
if err != nil {
return err
}
if n != 1 {
return errors.New("byte write error")
}
return nil
}
type varintConv struct {
buf [1 + binary.MaxVarintLen64]byte
reader *bytes.Reader
}
func (vi *varintConv) toBytes(v int64) []byte {
n := binary.PutVarint(vi.buf[1:], v)
vi.buf[0] = byte(n)
return vi.buf[:n+1]
}
func (vi *varintConv) read() (value int64, err error) {
var n byte
n, err = vi.reader.ReadByte()
if err != nil {
return
}
if int(n) > len(vi.buf) {
return 0, errVarintOverflow
}
data := vi.buf[:n]
if n == 0 {
return
}
if _, err = io.ReadFull(vi.reader, data); err != nil {
return
}
var offset int
value, offset = binary.Varint(data)
if offset < 1 {
if offset == 0 {
err = errVarintTooSmall
return
}
err = errVarintOverflow
return
}
return
}
func (vi *varintConv) readBytes(r io.Reader) (value int64, readBytes []byte, err error) {
var n byte
n, err = readByteFrom(r)
if err != nil {
return
}
if 1+int(n) > len(vi.buf) {
return 0, nil, errVarintOverflow
}
readBytes = vi.buf[:1+n]
readBytes[0] = n
if n == 0 {
return
}
if _, err = io.ReadFull(r, readBytes[1:]); err != nil {
return
}
var offset int
value, offset = binary.Varint(readBytes[1:])
if offset < 1 {
if offset == 0 {
err = errVarintTooSmall
return
}
err = errVarintOverflow
return
}
return
}
// toVarint converts a byte slice to int64. If length of slice is 0, it panics.
func toVarint(data []byte) (value int64, offset int, err error) {
size := int(data[0])
if size == 0 {
offset = 1
return
}
if len(data) < 1+size {
err = errVarintTooSmall
return
}
value, offset = binary.Varint(data[1:])
if offset < 1 {
if offset == 0 {
err = errVarintTooSmall
return
}
err = errVarintOverflow
return
}
offset++
return
}
func marshaler(o ugo.Object) encoding.BinaryMarshaler {
switch v := o.(type) {
case ugo.Bool:
return Bool(v)
case ugo.Int:
return Int(v)
case ugo.Uint:
return Uint(v)
case ugo.Char:
return Char(v)
case ugo.Float:
return Float(v)
case ugo.String:
return String(v)
case ugo.Bytes:
return Bytes(v)
case ugo.Array:
return Array(v)
case ugo.Map:
return Map(v)
case *ugo.SyncMap:
return (*SyncMap)(v)
case *ugo.CompiledFunction:
return (*CompiledFunction)(v)
case *ugo.Function:
return (*Function)(v)
case *ugo.BuiltinFunction:
return (*BuiltinFunction)(v)
case *ugo.UndefinedType:
return (*UndefinedType)(v)
default:
return nil
}
}