|
- 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
- }
|