1package msgp
2
3import (
4	"bufio"
5	"encoding/base64"
6	"encoding/json"
7	"io"
8	"strconv"
9	"time"
10)
11
12var unfuns [_maxtype]func(jsWriter, []byte, []byte) ([]byte, []byte, error)
13
14func init() {
15
16	// NOTE(pmh): this is best expressed as a jump table,
17	// but gc doesn't do that yet. revisit post-go1.5.
18	unfuns = [_maxtype]func(jsWriter, []byte, []byte) ([]byte, []byte, error){
19		StrType:        rwStringBytes,
20		BinType:        rwBytesBytes,
21		MapType:        rwMapBytes,
22		ArrayType:      rwArrayBytes,
23		Float64Type:    rwFloat64Bytes,
24		Float32Type:    rwFloat32Bytes,
25		BoolType:       rwBoolBytes,
26		IntType:        rwIntBytes,
27		UintType:       rwUintBytes,
28		NilType:        rwNullBytes,
29		ExtensionType:  rwExtensionBytes,
30		Complex64Type:  rwExtensionBytes,
31		Complex128Type: rwExtensionBytes,
32		TimeType:       rwTimeBytes,
33	}
34}
35
36// UnmarshalAsJSON takes raw messagepack and writes
37// it as JSON to 'w'. If an error is returned, the
38// bytes not translated will also be returned. If
39// no errors are encountered, the length of the returned
40// slice will be zero.
41func UnmarshalAsJSON(w io.Writer, msg []byte) ([]byte, error) {
42	var (
43		scratch []byte
44		cast    bool
45		dst     jsWriter
46		err     error
47	)
48	if jsw, ok := w.(jsWriter); ok {
49		dst = jsw
50		cast = true
51	} else {
52		dst = bufio.NewWriterSize(w, 512)
53	}
54	for len(msg) > 0 && err == nil {
55		msg, scratch, err = writeNext(dst, msg, scratch)
56	}
57	if !cast && err == nil {
58		err = dst.(*bufio.Writer).Flush()
59	}
60	return msg, err
61}
62
63func writeNext(w jsWriter, msg []byte, scratch []byte) ([]byte, []byte, error) {
64	if len(msg) < 1 {
65		return msg, scratch, ErrShortBytes
66	}
67	t := getType(msg[0])
68	if t == InvalidType {
69		return msg, scratch, InvalidPrefixError(msg[0])
70	}
71	if t == ExtensionType {
72		et, err := peekExtension(msg)
73		if err != nil {
74			return nil, scratch, err
75		}
76		if et == TimeExtension {
77			t = TimeType
78		}
79	}
80	return unfuns[t](w, msg, scratch)
81}
82
83func rwArrayBytes(w jsWriter, msg []byte, scratch []byte) ([]byte, []byte, error) {
84	sz, msg, err := ReadArrayHeaderBytes(msg)
85	if err != nil {
86		return msg, scratch, err
87	}
88	err = w.WriteByte('[')
89	if err != nil {
90		return msg, scratch, err
91	}
92	for i := uint32(0); i < sz; i++ {
93		if i != 0 {
94			err = w.WriteByte(',')
95			if err != nil {
96				return msg, scratch, err
97			}
98		}
99		msg, scratch, err = writeNext(w, msg, scratch)
100		if err != nil {
101			return msg, scratch, err
102		}
103	}
104	err = w.WriteByte(']')
105	return msg, scratch, err
106}
107
108func rwMapBytes(w jsWriter, msg []byte, scratch []byte) ([]byte, []byte, error) {
109	sz, msg, err := ReadMapHeaderBytes(msg)
110	if err != nil {
111		return msg, scratch, err
112	}
113	err = w.WriteByte('{')
114	if err != nil {
115		return msg, scratch, err
116	}
117	for i := uint32(0); i < sz; i++ {
118		if i != 0 {
119			err = w.WriteByte(',')
120			if err != nil {
121				return msg, scratch, err
122			}
123		}
124		msg, scratch, err = rwMapKeyBytes(w, msg, scratch)
125		if err != nil {
126			return msg, scratch, err
127		}
128		err = w.WriteByte(':')
129		if err != nil {
130			return msg, scratch, err
131		}
132		msg, scratch, err = writeNext(w, msg, scratch)
133		if err != nil {
134			return msg, scratch, err
135		}
136	}
137	err = w.WriteByte('}')
138	return msg, scratch, err
139}
140
141func rwMapKeyBytes(w jsWriter, msg []byte, scratch []byte) ([]byte, []byte, error) {
142	msg, scratch, err := rwStringBytes(w, msg, scratch)
143	if err != nil {
144		if tperr, ok := err.(TypeError); ok && tperr.Encoded == BinType {
145			return rwBytesBytes(w, msg, scratch)
146		}
147	}
148	return msg, scratch, err
149}
150
151func rwStringBytes(w jsWriter, msg []byte, scratch []byte) ([]byte, []byte, error) {
152	str, msg, err := ReadStringZC(msg)
153	if err != nil {
154		return msg, scratch, err
155	}
156	_, err = rwquoted(w, str)
157	return msg, scratch, err
158}
159
160func rwBytesBytes(w jsWriter, msg []byte, scratch []byte) ([]byte, []byte, error) {
161	bts, msg, err := ReadBytesZC(msg)
162	if err != nil {
163		return msg, scratch, err
164	}
165	l := base64.StdEncoding.EncodedLen(len(bts))
166	if cap(scratch) >= l {
167		scratch = scratch[0:l]
168	} else {
169		scratch = make([]byte, l)
170	}
171	base64.StdEncoding.Encode(scratch, bts)
172	err = w.WriteByte('"')
173	if err != nil {
174		return msg, scratch, err
175	}
176	_, err = w.Write(scratch)
177	if err != nil {
178		return msg, scratch, err
179	}
180	err = w.WriteByte('"')
181	return msg, scratch, err
182}
183
184func rwNullBytes(w jsWriter, msg []byte, scratch []byte) ([]byte, []byte, error) {
185	msg, err := ReadNilBytes(msg)
186	if err != nil {
187		return msg, scratch, err
188	}
189	_, err = w.Write(null)
190	return msg, scratch, err
191}
192
193func rwBoolBytes(w jsWriter, msg []byte, scratch []byte) ([]byte, []byte, error) {
194	b, msg, err := ReadBoolBytes(msg)
195	if err != nil {
196		return msg, scratch, err
197	}
198	if b {
199		_, err = w.WriteString("true")
200		return msg, scratch, err
201	}
202	_, err = w.WriteString("false")
203	return msg, scratch, err
204}
205
206func rwIntBytes(w jsWriter, msg []byte, scratch []byte) ([]byte, []byte, error) {
207	i, msg, err := ReadInt64Bytes(msg)
208	if err != nil {
209		return msg, scratch, err
210	}
211	scratch = strconv.AppendInt(scratch[0:0], i, 10)
212	_, err = w.Write(scratch)
213	return msg, scratch, err
214}
215
216func rwUintBytes(w jsWriter, msg []byte, scratch []byte) ([]byte, []byte, error) {
217	u, msg, err := ReadUint64Bytes(msg)
218	if err != nil {
219		return msg, scratch, err
220	}
221	scratch = strconv.AppendUint(scratch[0:0], u, 10)
222	_, err = w.Write(scratch)
223	return msg, scratch, err
224}
225
226func rwFloatBytes(w jsWriter, msg []byte, f64 bool, scratch []byte) ([]byte, []byte, error) {
227	var f float64
228	var err error
229	var sz int
230	if f64 {
231		sz = 64
232		f, msg, err = ReadFloat64Bytes(msg)
233	} else {
234		sz = 32
235		var v float32
236		v, msg, err = ReadFloat32Bytes(msg)
237		f = float64(v)
238	}
239	if err != nil {
240		return msg, scratch, err
241	}
242	scratch = strconv.AppendFloat(scratch, f, 'f', -1, sz)
243	_, err = w.Write(scratch)
244	return msg, scratch, err
245}
246
247func rwFloat32Bytes(w jsWriter, msg []byte, scratch []byte) ([]byte, []byte, error) {
248	var f float32
249	var err error
250	f, msg, err = ReadFloat32Bytes(msg)
251	if err != nil {
252		return msg, scratch, err
253	}
254	scratch = strconv.AppendFloat(scratch[:0], float64(f), 'f', -1, 32)
255	_, err = w.Write(scratch)
256	return msg, scratch, err
257}
258
259func rwFloat64Bytes(w jsWriter, msg []byte, scratch []byte) ([]byte, []byte, error) {
260	var f float64
261	var err error
262	f, msg, err = ReadFloat64Bytes(msg)
263	if err != nil {
264		return msg, scratch, err
265	}
266	scratch = strconv.AppendFloat(scratch[:0], f, 'f', -1, 64)
267	_, err = w.Write(scratch)
268	return msg, scratch, err
269}
270
271func rwTimeBytes(w jsWriter, msg []byte, scratch []byte) ([]byte, []byte, error) {
272	var t time.Time
273	var err error
274	t, msg, err = ReadTimeBytes(msg)
275	if err != nil {
276		return msg, scratch, err
277	}
278	bts, err := t.MarshalJSON()
279	if err != nil {
280		return msg, scratch, err
281	}
282	_, err = w.Write(bts)
283	return msg, scratch, err
284}
285
286func rwExtensionBytes(w jsWriter, msg []byte, scratch []byte) ([]byte, []byte, error) {
287	var err error
288	var et int8
289	et, err = peekExtension(msg)
290	if err != nil {
291		return msg, scratch, err
292	}
293
294	// if it's time.Time
295	if et == TimeExtension {
296		var tm time.Time
297		tm, msg, err = ReadTimeBytes(msg)
298		if err != nil {
299			return msg, scratch, err
300		}
301		bts, err := tm.MarshalJSON()
302		if err != nil {
303			return msg, scratch, err
304		}
305		_, err = w.Write(bts)
306		return msg, scratch, err
307	}
308
309	// if the extension is registered,
310	// use its canonical JSON form
311	if f, ok := extensionReg[et]; ok {
312		e := f()
313		msg, err = ReadExtensionBytes(msg, e)
314		if err != nil {
315			return msg, scratch, err
316		}
317		bts, err := json.Marshal(e)
318		if err != nil {
319			return msg, scratch, err
320		}
321		_, err = w.Write(bts)
322		return msg, scratch, err
323	}
324
325	// otherwise, write `{"type": <num>, "data": "<base64data>"}`
326	r := RawExtension{}
327	r.Type = et
328	msg, err = ReadExtensionBytes(msg, &r)
329	if err != nil {
330		return msg, scratch, err
331	}
332	scratch, err = writeExt(w, r, scratch)
333	return msg, scratch, err
334}
335
336func writeExt(w jsWriter, r RawExtension, scratch []byte) ([]byte, error) {
337	_, err := w.WriteString(`{"type":`)
338	if err != nil {
339		return scratch, err
340	}
341	scratch = strconv.AppendInt(scratch[0:0], int64(r.Type), 10)
342	_, err = w.Write(scratch)
343	if err != nil {
344		return scratch, err
345	}
346	_, err = w.WriteString(`,"data":"`)
347	if err != nil {
348		return scratch, err
349	}
350	l := base64.StdEncoding.EncodedLen(len(r.Data))
351	if cap(scratch) >= l {
352		scratch = scratch[0:l]
353	} else {
354		scratch = make([]byte, l)
355	}
356	base64.StdEncoding.Encode(scratch, r.Data)
357	_, err = w.Write(scratch)
358	if err != nil {
359		return scratch, err
360	}
361	_, err = w.WriteString(`"}`)
362	return scratch, err
363}
364