|
- package msgp
-
- import (
- "bufio"
- "encoding/base64"
- "encoding/json"
- "io"
- "strconv"
- "unicode/utf8"
- )
-
- var (
- null = []byte("null")
- hex = []byte("0123456789abcdef")
- )
-
- var defuns [_maxtype]func(jsWriter, *Reader) (int, error)
-
- // note: there is an initialization loop if
- // this isn't set up during init()
- func init() {
- // since none of these functions are inline-able,
- // there is not much of a penalty to the indirect
- // call. however, this is best expressed as a jump-table...
- defuns = [_maxtype]func(jsWriter, *Reader) (int, error){
- StrType: rwString,
- BinType: rwBytes,
- MapType: rwMap,
- ArrayType: rwArray,
- Float64Type: rwFloat64,
- Float32Type: rwFloat32,
- BoolType: rwBool,
- IntType: rwInt,
- UintType: rwUint,
- NilType: rwNil,
- ExtensionType: rwExtension,
- Complex64Type: rwExtension,
- Complex128Type: rwExtension,
- TimeType: rwTime,
- }
- }
-
- // this is the interface
- // used to write json
- type jsWriter interface {
- io.Writer
- io.ByteWriter
- WriteString(string) (int, error)
- }
-
- // CopyToJSON reads MessagePack from 'src' and copies it
- // as JSON to 'dst' until EOF.
- func CopyToJSON(dst io.Writer, src io.Reader) (n int64, err error) {
- r := NewReader(src)
- n, err = r.WriteToJSON(dst)
- freeR(r)
- return
- }
-
- // WriteToJSON translates MessagePack from 'r' and writes it as
- // JSON to 'w' until the underlying reader returns io.EOF. It returns
- // the number of bytes written, and an error if it stopped before EOF.
- func (r *Reader) WriteToJSON(w io.Writer) (n int64, err error) {
- var j jsWriter
- var bf *bufio.Writer
- if jsw, ok := w.(jsWriter); ok {
- j = jsw
- } else {
- bf = bufio.NewWriter(w)
- j = bf
- }
- var nn int
- for err == nil {
- nn, err = rwNext(j, r)
- n += int64(nn)
- }
- if err != io.EOF {
- if bf != nil {
- bf.Flush()
- }
- return
- }
- err = nil
- if bf != nil {
- err = bf.Flush()
- }
- return
- }
-
- func rwNext(w jsWriter, src *Reader) (int, error) {
- t, err := src.NextType()
- if err != nil {
- return 0, err
- }
- return defuns[t](w, src)
- }
-
- func rwMap(dst jsWriter, src *Reader) (n int, err error) {
- var comma bool
- var sz uint32
- var field []byte
-
- sz, err = src.ReadMapHeader()
- if err != nil {
- return
- }
-
- if sz == 0 {
- return dst.WriteString("{}")
- }
-
- err = dst.WriteByte('{')
- if err != nil {
- return
- }
- n++
- var nn int
- for i := uint32(0); i < sz; i++ {
- if comma {
- err = dst.WriteByte(',')
- if err != nil {
- return
- }
- n++
- }
-
- field, err = src.ReadMapKeyPtr()
- if err != nil {
- return
- }
- nn, err = rwquoted(dst, field)
- n += nn
- if err != nil {
- return
- }
-
- err = dst.WriteByte(':')
- if err != nil {
- return
- }
- n++
- nn, err = rwNext(dst, src)
- n += nn
- if err != nil {
- return
- }
- if !comma {
- comma = true
- }
- }
-
- err = dst.WriteByte('}')
- if err != nil {
- return
- }
- n++
- return
- }
-
- func rwArray(dst jsWriter, src *Reader) (n int, err error) {
- err = dst.WriteByte('[')
- if err != nil {
- return
- }
- var sz uint32
- var nn int
- sz, err = src.ReadArrayHeader()
- if err != nil {
- return
- }
- comma := false
- for i := uint32(0); i < sz; i++ {
- if comma {
- err = dst.WriteByte(',')
- if err != nil {
- return
- }
- n++
- }
- nn, err = rwNext(dst, src)
- n += nn
- if err != nil {
- return
- }
- comma = true
- }
-
- err = dst.WriteByte(']')
- if err != nil {
- return
- }
- n++
- return
- }
-
- func rwNil(dst jsWriter, src *Reader) (int, error) {
- err := src.ReadNil()
- if err != nil {
- return 0, err
- }
- return dst.Write(null)
- }
-
- func rwFloat32(dst jsWriter, src *Reader) (int, error) {
- f, err := src.ReadFloat32()
- if err != nil {
- return 0, err
- }
- src.scratch = strconv.AppendFloat(src.scratch[:0], float64(f), 'f', -1, 64)
- return dst.Write(src.scratch)
- }
-
- func rwFloat64(dst jsWriter, src *Reader) (int, error) {
- f, err := src.ReadFloat64()
- if err != nil {
- return 0, err
- }
- src.scratch = strconv.AppendFloat(src.scratch[:0], f, 'f', -1, 32)
- return dst.Write(src.scratch)
- }
-
- func rwInt(dst jsWriter, src *Reader) (int, error) {
- i, err := src.ReadInt64()
- if err != nil {
- return 0, err
- }
- src.scratch = strconv.AppendInt(src.scratch[:0], i, 10)
- return dst.Write(src.scratch)
- }
-
- func rwUint(dst jsWriter, src *Reader) (int, error) {
- u, err := src.ReadUint64()
- if err != nil {
- return 0, err
- }
- src.scratch = strconv.AppendUint(src.scratch[:0], u, 10)
- return dst.Write(src.scratch)
- }
-
- func rwBool(dst jsWriter, src *Reader) (int, error) {
- b, err := src.ReadBool()
- if err != nil {
- return 0, err
- }
- if b {
- return dst.WriteString("true")
- }
- return dst.WriteString("false")
- }
-
- func rwTime(dst jsWriter, src *Reader) (int, error) {
- t, err := src.ReadTime()
- if err != nil {
- return 0, err
- }
- bts, err := t.MarshalJSON()
- if err != nil {
- return 0, err
- }
- return dst.Write(bts)
- }
-
- func rwExtension(dst jsWriter, src *Reader) (n int, err error) {
- et, err := src.peekExtensionType()
- if err != nil {
- return 0, err
- }
-
- // registered extensions can override
- // the JSON encoding
- if j, ok := extensionReg[et]; ok {
- var bts []byte
- e := j()
- err = src.ReadExtension(e)
- if err != nil {
- return
- }
- bts, err = json.Marshal(e)
- if err != nil {
- return
- }
- return dst.Write(bts)
- }
-
- e := RawExtension{}
- e.Type = et
- err = src.ReadExtension(&e)
- if err != nil {
- return
- }
-
- var nn int
- err = dst.WriteByte('{')
- if err != nil {
- return
- }
- n++
-
- nn, err = dst.WriteString(`"type:"`)
- n += nn
- if err != nil {
- return
- }
-
- src.scratch = strconv.AppendInt(src.scratch[0:0], int64(e.Type), 10)
- nn, err = dst.Write(src.scratch)
- n += nn
- if err != nil {
- return
- }
-
- nn, err = dst.WriteString(`,"data":"`)
- n += nn
- if err != nil {
- return
- }
-
- enc := base64.NewEncoder(base64.StdEncoding, dst)
-
- nn, err = enc.Write(e.Data)
- n += nn
- if err != nil {
- return
- }
- err = enc.Close()
- if err != nil {
- return
- }
- nn, err = dst.WriteString(`"}`)
- n += nn
- return
- }
-
- func rwString(dst jsWriter, src *Reader) (n int, err error) {
- var p []byte
- p, err = src.R.Peek(1)
- if err != nil {
- return
- }
- lead := p[0]
- var read int
-
- if isfixstr(lead) {
- read = int(rfixstr(lead))
- src.R.Skip(1)
- goto write
- }
-
- switch lead {
- case mstr8:
- p, err = src.R.Next(2)
- if err != nil {
- return
- }
- read = int(uint8(p[1]))
- case mstr16:
- p, err = src.R.Next(3)
- if err != nil {
- return
- }
- read = int(big.Uint16(p[1:]))
- case mstr32:
- p, err = src.R.Next(5)
- if err != nil {
- return
- }
- read = int(big.Uint32(p[1:]))
- default:
- err = badPrefix(StrType, lead)
- return
- }
- write:
- p, err = src.R.Next(read)
- if err != nil {
- return
- }
- n, err = rwquoted(dst, p)
- return
- }
-
- func rwBytes(dst jsWriter, src *Reader) (n int, err error) {
- var nn int
- err = dst.WriteByte('"')
- if err != nil {
- return
- }
- n++
- src.scratch, err = src.ReadBytes(src.scratch[:0])
- if err != nil {
- return
- }
- enc := base64.NewEncoder(base64.StdEncoding, dst)
- nn, err = enc.Write(src.scratch)
- n += nn
- if err != nil {
- return
- }
- err = enc.Close()
- if err != nil {
- return
- }
- err = dst.WriteByte('"')
- if err != nil {
- return
- }
- n++
- return
- }
-
- // Below (c) The Go Authors, 2009-2014
- // Subject to the BSD-style license found at http://golang.org
- //
- // see: encoding/json/encode.go:(*encodeState).stringbytes()
- func rwquoted(dst jsWriter, s []byte) (n int, err error) {
- var nn int
- err = dst.WriteByte('"')
- if err != nil {
- return
- }
- n++
- start := 0
- for i := 0; i < len(s); {
- if b := s[i]; b < utf8.RuneSelf {
- if 0x20 <= b && b != '\\' && b != '"' && b != '<' && b != '>' && b != '&' {
- i++
- continue
- }
- if start < i {
- nn, err = dst.Write(s[start:i])
- n += nn
- if err != nil {
- return
- }
- }
- switch b {
- case '\\', '"':
- err = dst.WriteByte('\\')
- if err != nil {
- return
- }
- n++
- err = dst.WriteByte(b)
- if err != nil {
- return
- }
- n++
- case '\n':
- err = dst.WriteByte('\\')
- if err != nil {
- return
- }
- n++
- err = dst.WriteByte('n')
- if err != nil {
- return
- }
- n++
- case '\r':
- err = dst.WriteByte('\\')
- if err != nil {
- return
- }
- n++
- err = dst.WriteByte('r')
- if err != nil {
- return
- }
- n++
- case '\t':
- err = dst.WriteByte('\\')
- if err != nil {
- return
- }
- n++
- err = dst.WriteByte('t')
- if err != nil {
- return
- }
- n++
- default:
- // This encodes bytes < 0x20 except for \t, \n and \r.
- // It also escapes <, >, and &
- // because they can lead to security holes when
- // user-controlled strings are rendered into JSON
- // and served to some browsers.
- nn, err = dst.WriteString(`\u00`)
- n += nn
- if err != nil {
- return
- }
- err = dst.WriteByte(hex[b>>4])
- if err != nil {
- return
- }
- n++
- err = dst.WriteByte(hex[b&0xF])
- if err != nil {
- return
- }
- n++
- }
- i++
- start = i
- continue
- }
- c, size := utf8.DecodeRune(s[i:])
- if c == utf8.RuneError && size == 1 {
- if start < i {
- nn, err = dst.Write(s[start:i])
- n += nn
- if err != nil {
- return
- }
- }
- nn, err = dst.WriteString(`\ufffd`)
- n += nn
- if err != nil {
- return
- }
- i += size
- start = i
- continue
- }
- // U+2028 is LINE SEPARATOR.
- // U+2029 is PARAGRAPH SEPARATOR.
- // They are both technically valid characters in JSON strings,
- // but don't work in JSONP, which has to be evaluated as JavaScript,
- // and can lead to security holes there. It is valid JSON to
- // escape them, so we do so unconditionally.
- // See http://timelessrepo.com/json-isnt-a-javascript-subset for discussion.
- if c == '\u2028' || c == '\u2029' {
- if start < i {
- nn, err = dst.Write(s[start:i])
- n += nn
- if err != nil {
- return
- }
- }
- nn, err = dst.WriteString(`\u202`)
- n += nn
- if err != nil {
- return
- }
- err = dst.WriteByte(hex[c&0xF])
- if err != nil {
- return
- }
- n++
- i += size
- start = i
- continue
- }
- i += size
- }
- if start < len(s) {
- nn, err = dst.Write(s[start:])
- n += nn
- if err != nil {
- return
- }
- }
- err = dst.WriteByte('"')
- if err != nil {
- return
- }
- n++
- return
- }
|