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