jsonstring.go

  1package logfmt
  2
  3import (
  4	"bytes"
  5	"io"
  6	"strconv"
  7	"sync"
  8	"unicode"
  9	"unicode/utf16"
 10	"unicode/utf8"
 11)
 12
 13// Taken from Go's encoding/json and modified for use here.
 14
 15// Copyright 2010 The Go Authors. All rights reserved.
 16// Use of this source code is governed by a BSD-style
 17// license that can be found in the LICENSE file.
 18
 19var hex = "0123456789abcdef"
 20
 21var bufferPool = sync.Pool{
 22	New: func() interface{} {
 23		return &bytes.Buffer{}
 24	},
 25}
 26
 27func getBuffer() *bytes.Buffer {
 28	return bufferPool.Get().(*bytes.Buffer)
 29}
 30
 31func poolBuffer(buf *bytes.Buffer) {
 32	buf.Reset()
 33	bufferPool.Put(buf)
 34}
 35
 36// NOTE: keep in sync with writeQuotedBytes below.
 37func writeQuotedString(w io.Writer, s string) (int, error) {
 38	buf := getBuffer()
 39	buf.WriteByte('"')
 40	start := 0
 41	for i := 0; i < len(s); {
 42		if b := s[i]; b < utf8.RuneSelf {
 43			if 0x20 <= b && b != '\\' && b != '"' {
 44				i++
 45				continue
 46			}
 47			if start < i {
 48				buf.WriteString(s[start:i])
 49			}
 50			switch b {
 51			case '\\', '"':
 52				buf.WriteByte('\\')
 53				buf.WriteByte(b)
 54			case '\n':
 55				buf.WriteByte('\\')
 56				buf.WriteByte('n')
 57			case '\r':
 58				buf.WriteByte('\\')
 59				buf.WriteByte('r')
 60			case '\t':
 61				buf.WriteByte('\\')
 62				buf.WriteByte('t')
 63			default:
 64				// This encodes bytes < 0x20 except for \n, \r, and \t.
 65				buf.WriteString(`\u00`)
 66				buf.WriteByte(hex[b>>4])
 67				buf.WriteByte(hex[b&0xF])
 68			}
 69			i++
 70			start = i
 71			continue
 72		}
 73		c, size := utf8.DecodeRuneInString(s[i:])
 74		if c == utf8.RuneError {
 75			if start < i {
 76				buf.WriteString(s[start:i])
 77			}
 78			buf.WriteString(`\ufffd`)
 79			i += size
 80			start = i
 81			continue
 82		}
 83		i += size
 84	}
 85	if start < len(s) {
 86		buf.WriteString(s[start:])
 87	}
 88	buf.WriteByte('"')
 89	n, err := w.Write(buf.Bytes())
 90	poolBuffer(buf)
 91	return n, err
 92}
 93
 94// NOTE: keep in sync with writeQuoteString above.
 95func writeQuotedBytes(w io.Writer, s []byte) (int, error) {
 96	buf := getBuffer()
 97	buf.WriteByte('"')
 98	start := 0
 99	for i := 0; i < len(s); {
100		if b := s[i]; b < utf8.RuneSelf {
101			if 0x20 <= b && b != '\\' && b != '"' {
102				i++
103				continue
104			}
105			if start < i {
106				buf.Write(s[start:i])
107			}
108			switch b {
109			case '\\', '"':
110				buf.WriteByte('\\')
111				buf.WriteByte(b)
112			case '\n':
113				buf.WriteByte('\\')
114				buf.WriteByte('n')
115			case '\r':
116				buf.WriteByte('\\')
117				buf.WriteByte('r')
118			case '\t':
119				buf.WriteByte('\\')
120				buf.WriteByte('t')
121			default:
122				// This encodes bytes < 0x20 except for \n, \r, and \t.
123				buf.WriteString(`\u00`)
124				buf.WriteByte(hex[b>>4])
125				buf.WriteByte(hex[b&0xF])
126			}
127			i++
128			start = i
129			continue
130		}
131		c, size := utf8.DecodeRune(s[i:])
132		if c == utf8.RuneError {
133			if start < i {
134				buf.Write(s[start:i])
135			}
136			buf.WriteString(`\ufffd`)
137			i += size
138			start = i
139			continue
140		}
141		i += size
142	}
143	if start < len(s) {
144		buf.Write(s[start:])
145	}
146	buf.WriteByte('"')
147	n, err := w.Write(buf.Bytes())
148	poolBuffer(buf)
149	return n, err
150}
151
152// getu4 decodes \uXXXX from the beginning of s, returning the hex value,
153// or it returns -1.
154func getu4(s []byte) rune {
155	if len(s) < 6 || s[0] != '\\' || s[1] != 'u' {
156		return -1
157	}
158	r, err := strconv.ParseUint(string(s[2:6]), 16, 64)
159	if err != nil {
160		return -1
161	}
162	return rune(r)
163}
164
165func unquoteBytes(s []byte) (t []byte, ok bool) {
166	if len(s) < 2 || s[0] != '"' || s[len(s)-1] != '"' {
167		return
168	}
169	s = s[1 : len(s)-1]
170
171	// Check for unusual characters. If there are none,
172	// then no unquoting is needed, so return a slice of the
173	// original bytes.
174	r := 0
175	for r < len(s) {
176		c := s[r]
177		if c == '\\' || c == '"' || c < ' ' {
178			break
179		}
180		if c < utf8.RuneSelf {
181			r++
182			continue
183		}
184		rr, size := utf8.DecodeRune(s[r:])
185		if rr == utf8.RuneError {
186			break
187		}
188		r += size
189	}
190	if r == len(s) {
191		return s, true
192	}
193
194	b := make([]byte, len(s)+2*utf8.UTFMax)
195	w := copy(b, s[0:r])
196	for r < len(s) {
197		// Out of room?  Can only happen if s is full of
198		// malformed UTF-8 and we're replacing each
199		// byte with RuneError.
200		if w >= len(b)-2*utf8.UTFMax {
201			nb := make([]byte, (len(b)+utf8.UTFMax)*2)
202			copy(nb, b[0:w])
203			b = nb
204		}
205		switch c := s[r]; {
206		case c == '\\':
207			r++
208			if r >= len(s) {
209				return
210			}
211			switch s[r] {
212			default:
213				return
214			case '"', '\\', '/', '\'':
215				b[w] = s[r]
216				r++
217				w++
218			case 'b':
219				b[w] = '\b'
220				r++
221				w++
222			case 'f':
223				b[w] = '\f'
224				r++
225				w++
226			case 'n':
227				b[w] = '\n'
228				r++
229				w++
230			case 'r':
231				b[w] = '\r'
232				r++
233				w++
234			case 't':
235				b[w] = '\t'
236				r++
237				w++
238			case 'u':
239				r--
240				rr := getu4(s[r:])
241				if rr < 0 {
242					return
243				}
244				r += 6
245				if utf16.IsSurrogate(rr) {
246					rr1 := getu4(s[r:])
247					if dec := utf16.DecodeRune(rr, rr1); dec != unicode.ReplacementChar {
248						// A valid pair; consume.
249						r += 6
250						w += utf8.EncodeRune(b[w:], dec)
251						break
252					}
253					// Invalid surrogate; fall back to replacement rune.
254					rr = unicode.ReplacementChar
255				}
256				w += utf8.EncodeRune(b[w:], rr)
257			}
258
259		// Quote, control characters are invalid.
260		case c == '"', c < ' ':
261			return
262
263		// ASCII
264		case c < utf8.RuneSelf:
265			b[w] = c
266			r++
267			w++
268
269		// Coerce to well-formed UTF-8.
270		default:
271			rr, size := utf8.DecodeRune(s[r:])
272			r += size
273			w += utf8.EncodeRune(b[w:], rr)
274		}
275	}
276	return b[0:w], true
277}