Skip to content

Commit

Permalink
Program support gob encoding
Browse files Browse the repository at this point in the history
  • Loading branch information
saibing committed Oct 31, 2023
1 parent 993fce7 commit 6998f41
Show file tree
Hide file tree
Showing 4 changed files with 260 additions and 1 deletion.
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module github.com/dop251/goja

go 1.16
go 1.21.3

require (
github.com/dlclark/regexp2 v1.7.0
Expand Down
145 changes: 145 additions & 0 deletions gob.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
package goja

import (
"bytes"
"encoding/binary"
"encoding/gob"
"fmt"

"github.com/dop251/goja/unistring"
)

func init() {
gob.Register(unistring.String(""))
gob.Register([]srcMapItem{})
gob.Register(srcMapItem{})
}

// GobEncode implements the gob.GobEncoder interface for gob serialization.
func (p *Program) GobEncode() ([]byte, error) {
return p.MarshalBinary()
}

func (p *Program) MarshalBinary() ([]byte, error) {
codeSize := uint32(len(p.code))
valueSize := uint32(len(p.values))
srcMapSize := uint32(len(p.srcMap))
data := make([]byte, 0, 4+codeSize*16+4+valueSize*16+4+srcMapSize*16)

data = binary.BigEndian.AppendUint32(data, codeSize)
for _, ins := range p.code {
v, err := encodeInstruction(ins)
if err != nil {
return nil, err
}
data = append(data, v...)
}
data = binary.BigEndian.AppendUint32(data, valueSize)
for _, value := range p.values {
v, err := encodeValue(value)
if err != nil {
return nil, err
}
data = append(data, v...)
}

buf := bytes.NewBuffer(make([]byte, len(p.funcName)))
enc := gob.NewEncoder(buf)
if err := enc.Encode(p.funcName); err != nil {
return nil, err
}
data = append(data, buf.Bytes()...)

srcBytes, err := p.src.GobEncode()

Check failure on line 53 in gob.go

View workflow job for this annotation

GitHub Actions / test (1.x, ubuntu-latest)

p.src.GobEncode undefined (type *file.File has no field or method GobEncode)

Check failure on line 53 in gob.go

View workflow job for this annotation

GitHub Actions / test (1.x, ubuntu-latest, 386)

p.src.GobEncode undefined (type *file.File has no field or method GobEncode)
if err != nil {
return nil, err
}
data = binary.BigEndian.AppendUint32(data, uint32(len(srcBytes)))
data = append(data, srcBytes...)

buf = bytes.NewBuffer(make([]byte, len(p.srcMap)*16+128))
enc = gob.NewEncoder(buf)
if err = enc.Encode(p.srcMap); err != nil {
return nil, err
}

data = binary.BigEndian.AppendUint32(data, uint32(len(buf.Bytes())))
data = append(data, buf.Bytes()...)

return data, nil
}

// GobDecode implements the gob.GobDecoder interface for gob serialization.
func (p *Program) GobDecode(data []byte) error {
return p.UnmarshalBinary(data)
}

func (p *Program) UnmarshalBinary(data []byte) error {
codeSize := int(binary.BigEndian.Uint32(data[:4]))
p.code = make([]instruction, 0, codeSize)
for i := 0; i < codeSize; i++ {
ins, rest, err := decodeInstruction(data)
if err != nil {
return err
}
p.code = append(p.code, ins)
data = rest
}

valueSize := int(binary.BigEndian.Uint32(data[:4]))
p.values = make([]Value, 0, valueSize)
for i := 0; i < valueSize; i++ {
value, rest, err := decodeValue(data)
if err != nil {
return err
}
p.values = append(p.values, value)
data = rest
}

buf := bytes.NewBuffer(data)
dec := gob.NewDecoder(buf)
if err := dec.Decode(&p.funcName); err != nil {
return err
}
data = data[len(p.funcName):]

srcSize := binary.BigEndian.Uint32(data[:4])
data = data[4:]
buf = bytes.NewBuffer(data[:srcSize])
dec = gob.NewDecoder(buf)
if err := dec.Decode(&p.src); err != nil {
return err
}
data = data[srcSize:]

srcMapSize := binary.BigEndian.Uint32(data[:4])
data = data[4:]
if len(data) != int(srcMapSize) {
return fmt.Errorf("the decode src map buffer size %d is wrong", srcMapSize)
}

buf = bytes.NewBuffer(data[:srcMapSize])
dec = gob.NewDecoder(buf)
if err := dec.Decode(&p.srcMap); err != nil {
return err
}

return nil
}

func (m *srcMapItem) GobEncode() ([]byte, error) {
data := make([]byte, 16)
binary.BigEndian.PutUint64(data, uint64(m.pc))
binary.BigEndian.PutUint64(data[8:], uint64(m.srcPos))
return data, nil
}

func (m *srcMapItem) GobDecode(data []byte) error {
if len(data) < 16 {
return fmt.Errorf("error decoding binary %v: expected at least 4 bytes, got %d", data, len(data))
}
m.pc = int(binary.BigEndian.Uint64(data[:8]))
m.srcPos = int(binary.BigEndian.Uint64(data[8:]))
return nil
}
9 changes: 9 additions & 0 deletions gob_instruction.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package goja

func encodeInstruction(ins instruction) ([]byte, error) {
return nil, nil
}

func decodeInstruction(data []byte) (instruction, []byte, error) {
return nil, nil, nil
}
105 changes: 105 additions & 0 deletions gob_value.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
package goja

import (
"encoding/binary"
"fmt"
"math"
"unsafe"
)

type ValueType = uint8

const (
ValueInt ValueType = iota
ValueFloat
ValueBool
ValueNull
AsciiString
UnicodeString
)

func encodeValue(value Value) (data []byte, err error) {
switch v := value.(type) {
case valueInt:
data = make([]byte, 9)
data[0] = ValueInt
binary.BigEndian.PutUint64(data[1:9], uint64(v))
case valueFloat:
data = make([]byte, 9)
data[0] = ValueFloat
binary.BigEndian.PutUint64(data[1:9], math.Float64bits(float64(v)))
case valueBool:
data = make([]byte, 2)
data[0] = ValueBool
b := uint8(0)
if v {
b = 1
}
data[1] = b
case valueNull:
data = make([]byte, 1)
data[0] = ValueNull
case asciiString:
size := uint32(len(v))
data = make([]byte, 1+4+size)
data[0] = AsciiString
binary.BigEndian.PutUint32(data[1:5], size)
if size > 0 {
ptr := unsafe.StringData(string(v))
copy(data[5:], unsafe.Slice(ptr, size))
}
case unicodeString:
size := uint32(len(v))
data = make([]byte, 1+4+size*2)
data[0] = UnicodeString
binary.BigEndian.PutUint32(data[1:5], size)
for i := 0; i < int(size); i++ {
begin := 5 + i*2
binary.BigEndian.PutUint16(data[begin:begin+2], v[i])
}
default:
err = fmt.Errorf("unsupport encode value type %T", value)
}

return
}

func decodeValue(data []byte) (value Value, rest []byte, err error) {
valueType := data[0]
switch valueType {
case ValueInt:
val := binary.BigEndian.Uint64(data[1:9])
value = valueInt(val)
rest = data[9:]
case ValueFloat:
val := binary.BigEndian.Uint64(data[1:9])
value = valueFloat(math.Float64frombits(val))
rest = data[9:]
case ValueBool:
val := data[1]
value = valueBool(val == 1)
rest = data[2:]
case ValueNull:
value = valueNull{}
rest = data[1:]
case AsciiString:
size := binary.BigEndian.Uint32(data[1:5])
buf := make([]byte, size)
copy(buf, data[5:5+size])
value = asciiString(buf)
rest = data[5+size:]
case UnicodeString:
size := binary.BigEndian.Uint32(data[1:5])
buf := make([]uint16, size)
for i := 0; i < int(size); i++ {
begin := 5 + i*2
buf[i] = binary.BigEndian.Uint16(data[begin : begin+2])
}
value = unicodeString(buf)
rest = data[5+size*2:]
default:
err = fmt.Errorf("unsupport decode value type %d", valueType)
}

return
}

0 comments on commit 6998f41

Please sign in to comment.