...

Source file src/github.com/go-logfmt/logfmt/jsonstring.go

Documentation: github.com/go-logfmt/logfmt

     1  package logfmt
     2  
     3  import (
     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  
    19  var hex = "0123456789abcdef"
    20  
    21  var bufferPool = sync.Pool{
    22  	New: func() interface{} {
    23  		return &bytes.Buffer{}
    24  	},
    25  }
    26  
    27  func getBuffer() *bytes.Buffer {
    28  	return bufferPool.Get().(*bytes.Buffer)
    29  }
    30  
    31  func poolBuffer(buf *bytes.Buffer) {
    32  	buf.Reset()
    33  	bufferPool.Put(buf)
    34  }
    35  
    36  // NOTE: keep in sync with writeQuotedBytes below.
    37  func 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.
    95  func 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.
   154  func 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  
   165  func 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  }
   278  

View as plain text