blob: 438caf5392c509999249780d712078a1d14ebd99 [file] [log] [blame]
package msgp
import (
"bufio"
"encoding/base64"
"encoding/json"
"io"
"strconv"
"time"
)
var unfuns [_maxtype]func(jsWriter, []byte, []byte) ([]byte, []byte, error)
func init() {
// NOTE(pmh): this is best expressed as a jump table,
// but gc doesn't do that yet. revisit post-go1.5.
unfuns = [_maxtype]func(jsWriter, []byte, []byte) ([]byte, []byte, error){
StrType: rwStringBytes,
BinType: rwBytesBytes,
MapType: rwMapBytes,
ArrayType: rwArrayBytes,
Float64Type: rwFloat64Bytes,
Float32Type: rwFloat32Bytes,
BoolType: rwBoolBytes,
IntType: rwIntBytes,
UintType: rwUintBytes,
NilType: rwNullBytes,
ExtensionType: rwExtensionBytes,
Complex64Type: rwExtensionBytes,
Complex128Type: rwExtensionBytes,
TimeType: rwTimeBytes,
}
}
// UnmarshalAsJSON takes raw messagepack and writes
// it as JSON to 'w'. If an error is returned, the
// bytes not translated will also be returned. If
// no errors are encountered, the length of the returned
// slice will be zero.
func UnmarshalAsJSON(w io.Writer, msg []byte) ([]byte, error) {
var (
scratch []byte
cast bool
dst jsWriter
err error
)
if jsw, ok := w.(jsWriter); ok {
dst = jsw
cast = true
} else {
dst = bufio.NewWriterSize(w, 512)
}
for len(msg) > 0 && err == nil {
msg, scratch, err = writeNext(dst, msg, scratch)
}
if !cast && err == nil {
err = dst.(*bufio.Writer).Flush()
}
return msg, err
}
func writeNext(w jsWriter, msg []byte, scratch []byte) ([]byte, []byte, error) {
if len(msg) < 1 {
return msg, scratch, ErrShortBytes
}
t := getType(msg[0])
if t == InvalidType {
return msg, scratch, InvalidPrefixError(msg[0])
}
if t == ExtensionType {
et, err := peekExtension(msg)
if err != nil {
return nil, scratch, err
}
if et == TimeExtension {
t = TimeType
}
}
return unfuns[t](w, msg, scratch)
}
func rwArrayBytes(w jsWriter, msg []byte, scratch []byte) ([]byte, []byte, error) {
sz, msg, err := ReadArrayHeaderBytes(msg)
if err != nil {
return msg, scratch, err
}
err = w.WriteByte('[')
if err != nil {
return msg, scratch, err
}
for i := uint32(0); i < sz; i++ {
if i != 0 {
err = w.WriteByte(',')
if err != nil {
return msg, scratch, err
}
}
msg, scratch, err = writeNext(w, msg, scratch)
if err != nil {
return msg, scratch, err
}
}
err = w.WriteByte(']')
return msg, scratch, err
}
func rwMapBytes(w jsWriter, msg []byte, scratch []byte) ([]byte, []byte, error) {
sz, msg, err := ReadMapHeaderBytes(msg)
if err != nil {
return msg, scratch, err
}
err = w.WriteByte('{')
if err != nil {
return msg, scratch, err
}
for i := uint32(0); i < sz; i++ {
if i != 0 {
err = w.WriteByte(',')
if err != nil {
return msg, scratch, err
}
}
msg, scratch, err = rwMapKeyBytes(w, msg, scratch)
if err != nil {
return msg, scratch, err
}
err = w.WriteByte(':')
if err != nil {
return msg, scratch, err
}
msg, scratch, err = writeNext(w, msg, scratch)
if err != nil {
return msg, scratch, err
}
}
err = w.WriteByte('}')
return msg, scratch, err
}
func rwMapKeyBytes(w jsWriter, msg []byte, scratch []byte) ([]byte, []byte, error) {
msg, scratch, err := rwStringBytes(w, msg, scratch)
if err != nil {
if tperr, ok := err.(TypeError); ok && tperr.Encoded == BinType {
return rwBytesBytes(w, msg, scratch)
}
}
return msg, scratch, err
}
func rwStringBytes(w jsWriter, msg []byte, scratch []byte) ([]byte, []byte, error) {
str, msg, err := ReadStringZC(msg)
if err != nil {
return msg, scratch, err
}
_, err = rwquoted(w, str)
return msg, scratch, err
}
func rwBytesBytes(w jsWriter, msg []byte, scratch []byte) ([]byte, []byte, error) {
bts, msg, err := ReadBytesZC(msg)
if err != nil {
return msg, scratch, err
}
l := base64.StdEncoding.EncodedLen(len(bts))
if cap(scratch) >= l {
scratch = scratch[0:l]
} else {
scratch = make([]byte, l)
}
base64.StdEncoding.Encode(scratch, bts)
err = w.WriteByte('"')
if err != nil {
return msg, scratch, err
}
_, err = w.Write(scratch)
if err != nil {
return msg, scratch, err
}
err = w.WriteByte('"')
return msg, scratch, err
}
func rwNullBytes(w jsWriter, msg []byte, scratch []byte) ([]byte, []byte, error) {
msg, err := ReadNilBytes(msg)
if err != nil {
return msg, scratch, err
}
_, err = w.Write(null)
return msg, scratch, err
}
func rwBoolBytes(w jsWriter, msg []byte, scratch []byte) ([]byte, []byte, error) {
b, msg, err := ReadBoolBytes(msg)
if err != nil {
return msg, scratch, err
}
if b {
_, err = w.WriteString("true")
return msg, scratch, err
}
_, err = w.WriteString("false")
return msg, scratch, err
}
func rwIntBytes(w jsWriter, msg []byte, scratch []byte) ([]byte, []byte, error) {
i, msg, err := ReadInt64Bytes(msg)
if err != nil {
return msg, scratch, err
}
scratch = strconv.AppendInt(scratch[0:0], i, 10)
_, err = w.Write(scratch)
return msg, scratch, err
}
func rwUintBytes(w jsWriter, msg []byte, scratch []byte) ([]byte, []byte, error) {
u, msg, err := ReadUint64Bytes(msg)
if err != nil {
return msg, scratch, err
}
scratch = strconv.AppendUint(scratch[0:0], u, 10)
_, err = w.Write(scratch)
return msg, scratch, err
}
func rwFloatBytes(w jsWriter, msg []byte, f64 bool, scratch []byte) ([]byte, []byte, error) {
var f float64
var err error
var sz int
if f64 {
sz = 64
f, msg, err = ReadFloat64Bytes(msg)
} else {
sz = 32
var v float32
v, msg, err = ReadFloat32Bytes(msg)
f = float64(v)
}
if err != nil {
return msg, scratch, err
}
scratch = strconv.AppendFloat(scratch, f, 'f', -1, sz)
_, err = w.Write(scratch)
return msg, scratch, err
}
func rwFloat32Bytes(w jsWriter, msg []byte, scratch []byte) ([]byte, []byte, error) {
var f float32
var err error
f, msg, err = ReadFloat32Bytes(msg)
if err != nil {
return msg, scratch, err
}
scratch = strconv.AppendFloat(scratch[:0], float64(f), 'f', -1, 32)
_, err = w.Write(scratch)
return msg, scratch, err
}
func rwFloat64Bytes(w jsWriter, msg []byte, scratch []byte) ([]byte, []byte, error) {
var f float64
var err error
f, msg, err = ReadFloat64Bytes(msg)
if err != nil {
return msg, scratch, err
}
scratch = strconv.AppendFloat(scratch[:0], f, 'f', -1, 64)
_, err = w.Write(scratch)
return msg, scratch, err
}
func rwTimeBytes(w jsWriter, msg []byte, scratch []byte) ([]byte, []byte, error) {
var t time.Time
var err error
t, msg, err = ReadTimeBytes(msg)
if err != nil {
return msg, scratch, err
}
bts, err := t.MarshalJSON()
if err != nil {
return msg, scratch, err
}
_, err = w.Write(bts)
return msg, scratch, err
}
func rwExtensionBytes(w jsWriter, msg []byte, scratch []byte) ([]byte, []byte, error) {
var err error
var et int8
et, err = peekExtension(msg)
if err != nil {
return msg, scratch, err
}
// if it's time.Time
if et == TimeExtension {
var tm time.Time
tm, msg, err = ReadTimeBytes(msg)
if err != nil {
return msg, scratch, err
}
bts, err := tm.MarshalJSON()
if err != nil {
return msg, scratch, err
}
_, err = w.Write(bts)
return msg, scratch, err
}
// if the extension is registered,
// use its canonical JSON form
if f, ok := extensionReg[et]; ok {
e := f()
msg, err = ReadExtensionBytes(msg, e)
if err != nil {
return msg, scratch, err
}
bts, err := json.Marshal(e)
if err != nil {
return msg, scratch, err
}
_, err = w.Write(bts)
return msg, scratch, err
}
// otherwise, write `{"type": <num>, "data": "<base64data>"}`
r := RawExtension{}
r.Type = et
msg, err = ReadExtensionBytes(msg, &r)
if err != nil {
return msg, scratch, err
}
scratch, err = writeExt(w, r, scratch)
return msg, scratch, err
}
func writeExt(w jsWriter, r RawExtension, scratch []byte) ([]byte, error) {
_, err := w.WriteString(`{"type":`)
if err != nil {
return scratch, err
}
scratch = strconv.AppendInt(scratch[0:0], int64(r.Type), 10)
_, err = w.Write(scratch)
if err != nil {
return scratch, err
}
_, err = w.WriteString(`,"data":"`)
if err != nil {
return scratch, err
}
l := base64.StdEncoding.EncodedLen(len(r.Data))
if cap(scratch) >= l {
scratch = scratch[0:l]
} else {
scratch = make([]byte, l)
}
base64.StdEncoding.Encode(scratch, r.Data)
_, err = w.Write(scratch)
if err != nil {
return scratch, err
}
_, err = w.WriteString(`"}`)
return scratch, err
}