diff --git a/jspb/constants.go b/jspb/constants.go new file mode 100644 index 00000000..02fe3edd --- /dev/null +++ b/jspb/constants.go @@ -0,0 +1,6 @@ +package jspb + +const ( + // InvalidFieldNumber is a Flag to indicate a missing field. + InvalidFieldNumber = -1 +) diff --git a/jspb/errors.go b/jspb/errors.go new file mode 100644 index 00000000..30ed39e3 --- /dev/null +++ b/jspb/errors.go @@ -0,0 +1,19 @@ +package jspb + +import "github.com/gopherjs/gopherjs/js" + +// catchException recovers any JS exceptions and +// stores the error in the parameter +func catchException(err *error) { + e := recover() + + if e == nil { + return + } + + if e, ok := e.(*js.Error); ok { + *err = e + } else { + panic(e) + } +} diff --git a/jspb/reader.go b/jspb/reader.go new file mode 100644 index 00000000..4d05a20d --- /dev/null +++ b/jspb/reader.go @@ -0,0 +1,120 @@ +package jspb + +import "github.com/gopherjs/gopherjs/js" + +// Reader encapsulates the jspb.BinaryReader +type Reader interface { + Next() bool + Err() error + + GetFieldNumber() int + SkipField() + + // Scalars + ReadInt32() int32 + ReadUint32() uint32 + ReadString() string + ReadBool() bool + + // Slices + ReadUint32Slice() []uint32 + + // Specials + ReadMessage(func()) + ReadEnum() int +} + +// NewReader returns a new Reader ready for writing +func NewReader(data []byte) Reader { + return &reader{ + Object: js.Global.Get("BinaryReader").New(data), + } +} + +// reader implements the Reader interface +type reader struct { + *js.Object + err error +} + +// Reads the next field header in the stream if there is one, returns true if +// we saw a valid field header or false if we've read the whole stream. +// Sets err if we encountered a deprecated START_GROUP/END_GROUP field. +func (r *reader) Next() bool { + defer catchException(&r.err) + return r.err == nil && r.Call("nextField").Bool() && !r.Call("isEndGroup").Bool() +} + +// Err returns the error state of the Reader. +func (r reader) Err() error { + return r.err +} + +// The field number of the next field in the buffer, or +// InvalidFieldNumber if there is no next field. +func (r reader) GetFieldNumber() int { + return r.Call("getFieldNumber").Int() +} + +// Skips over the next field in the binary stream. +func (r reader) SkipField() { + r.Call("skipField") +} + +// ReadInt32 reads a signed 32-bit integer field from the binary +// stream, sets err if the next field in the +// stream is not of the correct wire type. +func (r *reader) ReadInt32() int32 { + defer catchException(&r.err) + return int32(r.Call("readInt32").Int()) +} + +// ReadUit32 reads an unsigned 32-bit integer field from the binary +// stream, sets err if the next field in the +// stream is not of the correct wire type. +func (r *reader) ReadUint32() uint32 { + defer catchException(&r.err) + return uint32(r.Call("readUint32").Int()) +} + +// ReadString reads a string field from the binary stream, sets err +// if the next field in the stream is not of the correct wire type. +func (r *reader) ReadString() string { + defer catchException(&r.err) + return r.Call("readString").String() +} + +// ReadBool reads a bool field from the binary stream, sets err +// if the next field in the stream is not of the correct wire type. +func (r *reader) ReadBool() bool { + defer catchException(&r.err) + return r.Call("readBool").Bool() +} + +// ReadUint32Slice reads a packed 32-bit unsigned integer field +// from the binary stream, sets err if the next field +// in the stream is not of the correct wire type. +func (r *reader) ReadUint32Slice() (ret []uint32) { + defer catchException(&r.err) + values := r.Call("readPackedUint32").Interface().([]interface{}) + for _, value := range values { + ret = append(ret, uint32(value.(float64))) + } + + return ret +} + +// ReadMessage deserializes a proto using +// the provided reader function. +func (r *reader) ReadMessage(readFunc func()) { + defer catchException(&r.err) + r.Call("readMessage", js.Undefined /* Unused */, readFunc) +} + +// ReadEnum reads an enum field from the binary stream, +// sets err if the next field in the stream +// is not of the correct wire type. +func (r *reader) ReadEnum() int { + defer catchException(&r.err) + return r.Call("readEnum").Int() +} diff --git a/jspb/writer.go b/jspb/writer.go new file mode 100644 index 00000000..03d96837 --- /dev/null +++ b/jspb/writer.go @@ -0,0 +1,73 @@ +package jspb + +import "github.com/gopherjs/gopherjs/js" + +// Writer encapsulates the jspb.BinaryWriter. +type Writer interface { + GetResult() []byte + + // Scalars + WriteInt32(int, int32) + WriteUint32(int, uint32) + WriteString(int, string) + WriteBool(int, bool) + + // Slices + WriteUint32Slice(int, []uint32) + + // Specials + WriteMessage(int, func()) + WriteEnum(int, int) +} + +// NewWriter returns a new Writer ready for writing. +func NewWriter() Writer { + return &writer{ + Object: js.Global.Get("BinaryWriter").New(), + } +} + +// writer implements the Writer interface. +type writer struct { + *js.Object +} + +// GetResult returns the contents of the buffer as a byte slice. +func (w writer) GetResult() []byte { + return w.Call("getResultBuffer").Interface().([]byte) +} + +// WriteInt32 writes an int32 field to the buffer. +func (w writer) WriteInt32(field int, value int32) { + w.Call("writeInt32", field, value) +} + +// WriteInt32 writes a uint32 field to the buffer. +func (w writer) WriteUint32(field int, value uint32) { + w.Call("writeUint32", field, value) +} + +// WriteString writes a string field to the buffer +func (w writer) WriteString(field int, value string) { + w.Call("writeString", field, value) +} + +// WriteBool writes a string field to the buffer +func (w writer) WriteBool(field int, value bool) { + w.Call("writeBool", field, value) +} + +// WriteUint32Slice writes a uint32 slice field to the buffer +func (w writer) WriteUint32Slice(field int, value []uint32) { + w.Call("writePackedUint32", field, value) +} + +// WriteMessage writes a message to the buffer using writeFunc +func (w writer) WriteMessage(field int, writeFunc func()) { + w.Call("writeMessage", field, 0 /* Unused */, writeFunc) +} + +// WriteEnum writes an enum (as an int) to the buffer +func (w writer) WriteEnum(field int, value int) { + w.Call("writeEnum", field, value) +}