683 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			Go
		
	
	
			
		
		
	
	
			683 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			Go
		
	
	
package pretty
 | 
						|
 | 
						|
import (
 | 
						|
	"bytes"
 | 
						|
	"encoding/json"
 | 
						|
	"sort"
 | 
						|
	"strconv"
 | 
						|
)
 | 
						|
 | 
						|
// Options is Pretty options
 | 
						|
type Options struct {
 | 
						|
	// Width is an max column width for single line arrays
 | 
						|
	// Default is 80
 | 
						|
	Width int
 | 
						|
	// Prefix is a prefix for all lines
 | 
						|
	// Default is an empty string
 | 
						|
	Prefix string
 | 
						|
	// Indent is the nested indentation
 | 
						|
	// Default is two spaces
 | 
						|
	Indent string
 | 
						|
	// SortKeys will sort the keys alphabetically
 | 
						|
	// Default is false
 | 
						|
	SortKeys bool
 | 
						|
}
 | 
						|
 | 
						|
// DefaultOptions is the default options for pretty formats.
 | 
						|
var DefaultOptions = &Options{Width: 80, Prefix: "", Indent: "  ", SortKeys: false}
 | 
						|
 | 
						|
// Pretty converts the input json into a more human readable format where each
 | 
						|
// element is on it's own line with clear indentation.
 | 
						|
func Pretty(json []byte) []byte { return PrettyOptions(json, nil) }
 | 
						|
 | 
						|
// PrettyOptions is like Pretty but with customized options.
 | 
						|
func PrettyOptions(json []byte, opts *Options) []byte {
 | 
						|
	if opts == nil {
 | 
						|
		opts = DefaultOptions
 | 
						|
	}
 | 
						|
	buf := make([]byte, 0, len(json))
 | 
						|
	if len(opts.Prefix) != 0 {
 | 
						|
		buf = append(buf, opts.Prefix...)
 | 
						|
	}
 | 
						|
	buf, _, _, _ = appendPrettyAny(buf, json, 0, true,
 | 
						|
		opts.Width, opts.Prefix, opts.Indent, opts.SortKeys,
 | 
						|
		0, 0, -1)
 | 
						|
	if len(buf) > 0 {
 | 
						|
		buf = append(buf, '\n')
 | 
						|
	}
 | 
						|
	return buf
 | 
						|
}
 | 
						|
 | 
						|
// Ugly removes insignificant space characters from the input json byte slice
 | 
						|
// and returns the compacted result.
 | 
						|
func Ugly(json []byte) []byte {
 | 
						|
	buf := make([]byte, 0, len(json))
 | 
						|
	return ugly(buf, json)
 | 
						|
}
 | 
						|
 | 
						|
// UglyInPlace removes insignificant space characters from the input json
 | 
						|
// byte slice and returns the compacted result. This method reuses the
 | 
						|
// input json buffer to avoid allocations. Do not use the original bytes
 | 
						|
// slice upon return.
 | 
						|
func UglyInPlace(json []byte) []byte { return ugly(json, json) }
 | 
						|
 | 
						|
func ugly(dst, src []byte) []byte {
 | 
						|
	dst = dst[:0]
 | 
						|
	for i := 0; i < len(src); i++ {
 | 
						|
		if src[i] > ' ' {
 | 
						|
			dst = append(dst, src[i])
 | 
						|
			if src[i] == '"' {
 | 
						|
				for i = i + 1; i < len(src); i++ {
 | 
						|
					dst = append(dst, src[i])
 | 
						|
					if src[i] == '"' {
 | 
						|
						j := i - 1
 | 
						|
						for ; ; j-- {
 | 
						|
							if src[j] != '\\' {
 | 
						|
								break
 | 
						|
							}
 | 
						|
						}
 | 
						|
						if (j-i)%2 != 0 {
 | 
						|
							break
 | 
						|
						}
 | 
						|
					}
 | 
						|
				}
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return dst
 | 
						|
}
 | 
						|
 | 
						|
func isNaNOrInf(src []byte) bool {
 | 
						|
	return src[0] == 'i' || //Inf
 | 
						|
		src[0] == 'I' || // inf
 | 
						|
		src[0] == '+' || // +Inf
 | 
						|
		src[0] == 'N' || // Nan
 | 
						|
		(src[0] == 'n' && len(src) > 1 && src[1] != 'u') // nan
 | 
						|
}
 | 
						|
 | 
						|
func appendPrettyAny(buf, json []byte, i int, pretty bool, width int, prefix, indent string, sortkeys bool, tabs, nl, max int) ([]byte, int, int, bool) {
 | 
						|
	for ; i < len(json); i++ {
 | 
						|
		if json[i] <= ' ' {
 | 
						|
			continue
 | 
						|
		}
 | 
						|
		if json[i] == '"' {
 | 
						|
			return appendPrettyString(buf, json, i, nl)
 | 
						|
		}
 | 
						|
 | 
						|
		if (json[i] >= '0' && json[i] <= '9') || json[i] == '-' || isNaNOrInf(json[i:]) {
 | 
						|
			return appendPrettyNumber(buf, json, i, nl)
 | 
						|
		}
 | 
						|
		if json[i] == '{' {
 | 
						|
			return appendPrettyObject(buf, json, i, '{', '}', pretty, width, prefix, indent, sortkeys, tabs, nl, max)
 | 
						|
		}
 | 
						|
		if json[i] == '[' {
 | 
						|
			return appendPrettyObject(buf, json, i, '[', ']', pretty, width, prefix, indent, sortkeys, tabs, nl, max)
 | 
						|
		}
 | 
						|
		switch json[i] {
 | 
						|
		case 't':
 | 
						|
			return append(buf, 't', 'r', 'u', 'e'), i + 4, nl, true
 | 
						|
		case 'f':
 | 
						|
			return append(buf, 'f', 'a', 'l', 's', 'e'), i + 5, nl, true
 | 
						|
		case 'n':
 | 
						|
			return append(buf, 'n', 'u', 'l', 'l'), i + 4, nl, true
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return buf, i, nl, true
 | 
						|
}
 | 
						|
 | 
						|
type pair struct {
 | 
						|
	kstart, kend int
 | 
						|
	vstart, vend int
 | 
						|
}
 | 
						|
 | 
						|
type byKeyVal struct {
 | 
						|
	sorted bool
 | 
						|
	json   []byte
 | 
						|
	buf    []byte
 | 
						|
	pairs  []pair
 | 
						|
}
 | 
						|
 | 
						|
func (arr *byKeyVal) Len() int {
 | 
						|
	return len(arr.pairs)
 | 
						|
}
 | 
						|
func (arr *byKeyVal) Less(i, j int) bool {
 | 
						|
	if arr.isLess(i, j, byKey) {
 | 
						|
		return true
 | 
						|
	}
 | 
						|
	if arr.isLess(j, i, byKey) {
 | 
						|
		return false
 | 
						|
	}
 | 
						|
	return arr.isLess(i, j, byVal)
 | 
						|
}
 | 
						|
func (arr *byKeyVal) Swap(i, j int) {
 | 
						|
	arr.pairs[i], arr.pairs[j] = arr.pairs[j], arr.pairs[i]
 | 
						|
	arr.sorted = true
 | 
						|
}
 | 
						|
 | 
						|
type byKind int
 | 
						|
 | 
						|
const (
 | 
						|
	byKey byKind = 0
 | 
						|
	byVal byKind = 1
 | 
						|
)
 | 
						|
 | 
						|
type jtype int
 | 
						|
 | 
						|
const (
 | 
						|
	jnull jtype = iota
 | 
						|
	jfalse
 | 
						|
	jnumber
 | 
						|
	jstring
 | 
						|
	jtrue
 | 
						|
	jjson
 | 
						|
)
 | 
						|
 | 
						|
func getjtype(v []byte) jtype {
 | 
						|
	if len(v) == 0 {
 | 
						|
		return jnull
 | 
						|
	}
 | 
						|
	switch v[0] {
 | 
						|
	case '"':
 | 
						|
		return jstring
 | 
						|
	case 'f':
 | 
						|
		return jfalse
 | 
						|
	case 't':
 | 
						|
		return jtrue
 | 
						|
	case 'n':
 | 
						|
		return jnull
 | 
						|
	case '[', '{':
 | 
						|
		return jjson
 | 
						|
	default:
 | 
						|
		return jnumber
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func (arr *byKeyVal) isLess(i, j int, kind byKind) bool {
 | 
						|
	k1 := arr.json[arr.pairs[i].kstart:arr.pairs[i].kend]
 | 
						|
	k2 := arr.json[arr.pairs[j].kstart:arr.pairs[j].kend]
 | 
						|
	var v1, v2 []byte
 | 
						|
	if kind == byKey {
 | 
						|
		v1 = k1
 | 
						|
		v2 = k2
 | 
						|
	} else {
 | 
						|
		v1 = bytes.TrimSpace(arr.buf[arr.pairs[i].vstart:arr.pairs[i].vend])
 | 
						|
		v2 = bytes.TrimSpace(arr.buf[arr.pairs[j].vstart:arr.pairs[j].vend])
 | 
						|
		if len(v1) >= len(k1)+1 {
 | 
						|
			v1 = bytes.TrimSpace(v1[len(k1)+1:])
 | 
						|
		}
 | 
						|
		if len(v2) >= len(k2)+1 {
 | 
						|
			v2 = bytes.TrimSpace(v2[len(k2)+1:])
 | 
						|
		}
 | 
						|
	}
 | 
						|
	t1 := getjtype(v1)
 | 
						|
	t2 := getjtype(v2)
 | 
						|
	if t1 < t2 {
 | 
						|
		return true
 | 
						|
	}
 | 
						|
	if t1 > t2 {
 | 
						|
		return false
 | 
						|
	}
 | 
						|
	if t1 == jstring {
 | 
						|
		s1 := parsestr(v1)
 | 
						|
		s2 := parsestr(v2)
 | 
						|
		return string(s1) < string(s2)
 | 
						|
	}
 | 
						|
	if t1 == jnumber {
 | 
						|
		n1, _ := strconv.ParseFloat(string(v1), 64)
 | 
						|
		n2, _ := strconv.ParseFloat(string(v2), 64)
 | 
						|
		return n1 < n2
 | 
						|
	}
 | 
						|
	return string(v1) < string(v2)
 | 
						|
 | 
						|
}
 | 
						|
 | 
						|
func parsestr(s []byte) []byte {
 | 
						|
	for i := 1; i < len(s); i++ {
 | 
						|
		if s[i] == '\\' {
 | 
						|
			var str string
 | 
						|
			json.Unmarshal(s, &str)
 | 
						|
			return []byte(str)
 | 
						|
		}
 | 
						|
		if s[i] == '"' {
 | 
						|
			return s[1:i]
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return nil
 | 
						|
}
 | 
						|
 | 
						|
func appendPrettyObject(buf, json []byte, i int, open, close byte, pretty bool, width int, prefix, indent string, sortkeys bool, tabs, nl, max int) ([]byte, int, int, bool) {
 | 
						|
	var ok bool
 | 
						|
	if width > 0 {
 | 
						|
		if pretty && open == '[' && max == -1 {
 | 
						|
			// here we try to create a single line array
 | 
						|
			max := width - (len(buf) - nl)
 | 
						|
			if max > 3 {
 | 
						|
				s1, s2 := len(buf), i
 | 
						|
				buf, i, _, ok = appendPrettyObject(buf, json, i, '[', ']', false, width, prefix, "", sortkeys, 0, 0, max)
 | 
						|
				if ok && len(buf)-s1 <= max {
 | 
						|
					return buf, i, nl, true
 | 
						|
				}
 | 
						|
				buf = buf[:s1]
 | 
						|
				i = s2
 | 
						|
			}
 | 
						|
		} else if max != -1 && open == '{' {
 | 
						|
			return buf, i, nl, false
 | 
						|
		}
 | 
						|
	}
 | 
						|
	buf = append(buf, open)
 | 
						|
	i++
 | 
						|
	var pairs []pair
 | 
						|
	if open == '{' && sortkeys {
 | 
						|
		pairs = make([]pair, 0, 8)
 | 
						|
	}
 | 
						|
	var n int
 | 
						|
	for ; i < len(json); i++ {
 | 
						|
		if json[i] <= ' ' {
 | 
						|
			continue
 | 
						|
		}
 | 
						|
		if json[i] == close {
 | 
						|
			if pretty {
 | 
						|
				if open == '{' && sortkeys {
 | 
						|
					buf = sortPairs(json, buf, pairs)
 | 
						|
				}
 | 
						|
				if n > 0 {
 | 
						|
					nl = len(buf)
 | 
						|
					if buf[nl-1] == ' ' {
 | 
						|
						buf[nl-1] = '\n'
 | 
						|
					} else {
 | 
						|
						buf = append(buf, '\n')
 | 
						|
					}
 | 
						|
				}
 | 
						|
				if buf[len(buf)-1] != open {
 | 
						|
					buf = appendTabs(buf, prefix, indent, tabs)
 | 
						|
				}
 | 
						|
			}
 | 
						|
			buf = append(buf, close)
 | 
						|
			return buf, i + 1, nl, open != '{'
 | 
						|
		}
 | 
						|
		if open == '[' || json[i] == '"' {
 | 
						|
			if n > 0 {
 | 
						|
				buf = append(buf, ',')
 | 
						|
				if width != -1 && open == '[' {
 | 
						|
					buf = append(buf, ' ')
 | 
						|
				}
 | 
						|
			}
 | 
						|
			var p pair
 | 
						|
			if pretty {
 | 
						|
				nl = len(buf)
 | 
						|
				if buf[nl-1] == ' ' {
 | 
						|
					buf[nl-1] = '\n'
 | 
						|
				} else {
 | 
						|
					buf = append(buf, '\n')
 | 
						|
				}
 | 
						|
				if open == '{' && sortkeys {
 | 
						|
					p.kstart = i
 | 
						|
					p.vstart = len(buf)
 | 
						|
				}
 | 
						|
				buf = appendTabs(buf, prefix, indent, tabs+1)
 | 
						|
			}
 | 
						|
			if open == '{' {
 | 
						|
				buf, i, nl, _ = appendPrettyString(buf, json, i, nl)
 | 
						|
				if sortkeys {
 | 
						|
					p.kend = i
 | 
						|
				}
 | 
						|
				buf = append(buf, ':')
 | 
						|
				if pretty {
 | 
						|
					buf = append(buf, ' ')
 | 
						|
				}
 | 
						|
			}
 | 
						|
			buf, i, nl, ok = appendPrettyAny(buf, json, i, pretty, width, prefix, indent, sortkeys, tabs+1, nl, max)
 | 
						|
			if max != -1 && !ok {
 | 
						|
				return buf, i, nl, false
 | 
						|
			}
 | 
						|
			if pretty && open == '{' && sortkeys {
 | 
						|
				p.vend = len(buf)
 | 
						|
				if p.kstart > p.kend || p.vstart > p.vend {
 | 
						|
					// bad data. disable sorting
 | 
						|
					sortkeys = false
 | 
						|
				} else {
 | 
						|
					pairs = append(pairs, p)
 | 
						|
				}
 | 
						|
			}
 | 
						|
			i--
 | 
						|
			n++
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return buf, i, nl, open != '{'
 | 
						|
}
 | 
						|
func sortPairs(json, buf []byte, pairs []pair) []byte {
 | 
						|
	if len(pairs) == 0 {
 | 
						|
		return buf
 | 
						|
	}
 | 
						|
	vstart := pairs[0].vstart
 | 
						|
	vend := pairs[len(pairs)-1].vend
 | 
						|
	arr := byKeyVal{false, json, buf, pairs}
 | 
						|
	sort.Stable(&arr)
 | 
						|
	if !arr.sorted {
 | 
						|
		return buf
 | 
						|
	}
 | 
						|
	nbuf := make([]byte, 0, vend-vstart)
 | 
						|
	for i, p := range pairs {
 | 
						|
		nbuf = append(nbuf, buf[p.vstart:p.vend]...)
 | 
						|
		if i < len(pairs)-1 {
 | 
						|
			nbuf = append(nbuf, ',')
 | 
						|
			nbuf = append(nbuf, '\n')
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return append(buf[:vstart], nbuf...)
 | 
						|
}
 | 
						|
 | 
						|
func appendPrettyString(buf, json []byte, i, nl int) ([]byte, int, int, bool) {
 | 
						|
	s := i
 | 
						|
	i++
 | 
						|
	for ; i < len(json); i++ {
 | 
						|
		if json[i] == '"' {
 | 
						|
			var sc int
 | 
						|
			for j := i - 1; j > s; j-- {
 | 
						|
				if json[j] == '\\' {
 | 
						|
					sc++
 | 
						|
				} else {
 | 
						|
					break
 | 
						|
				}
 | 
						|
			}
 | 
						|
			if sc%2 == 1 {
 | 
						|
				continue
 | 
						|
			}
 | 
						|
			i++
 | 
						|
			break
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return append(buf, json[s:i]...), i, nl, true
 | 
						|
}
 | 
						|
 | 
						|
func appendPrettyNumber(buf, json []byte, i, nl int) ([]byte, int, int, bool) {
 | 
						|
	s := i
 | 
						|
	i++
 | 
						|
	for ; i < len(json); i++ {
 | 
						|
		if json[i] <= ' ' || json[i] == ',' || json[i] == ':' || json[i] == ']' || json[i] == '}' {
 | 
						|
			break
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return append(buf, json[s:i]...), i, nl, true
 | 
						|
}
 | 
						|
 | 
						|
func appendTabs(buf []byte, prefix, indent string, tabs int) []byte {
 | 
						|
	if len(prefix) != 0 {
 | 
						|
		buf = append(buf, prefix...)
 | 
						|
	}
 | 
						|
	if len(indent) == 2 && indent[0] == ' ' && indent[1] == ' ' {
 | 
						|
		for i := 0; i < tabs; i++ {
 | 
						|
			buf = append(buf, ' ', ' ')
 | 
						|
		}
 | 
						|
	} else {
 | 
						|
		for i := 0; i < tabs; i++ {
 | 
						|
			buf = append(buf, indent...)
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return buf
 | 
						|
}
 | 
						|
 | 
						|
// Style is the color style
 | 
						|
type Style struct {
 | 
						|
	Key, String, Number [2]string
 | 
						|
	True, False, Null   [2]string
 | 
						|
	Escape              [2]string
 | 
						|
	Brackets            [2]string
 | 
						|
	Append              func(dst []byte, c byte) []byte
 | 
						|
}
 | 
						|
 | 
						|
func hexp(p byte) byte {
 | 
						|
	switch {
 | 
						|
	case p < 10:
 | 
						|
		return p + '0'
 | 
						|
	default:
 | 
						|
		return (p - 10) + 'a'
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// TerminalStyle is for terminals
 | 
						|
var TerminalStyle *Style
 | 
						|
 | 
						|
func init() {
 | 
						|
	TerminalStyle = &Style{
 | 
						|
		Key:      [2]string{"\x1B[1m\x1B[94m", "\x1B[0m"},
 | 
						|
		String:   [2]string{"\x1B[32m", "\x1B[0m"},
 | 
						|
		Number:   [2]string{"\x1B[33m", "\x1B[0m"},
 | 
						|
		True:     [2]string{"\x1B[36m", "\x1B[0m"},
 | 
						|
		False:    [2]string{"\x1B[36m", "\x1B[0m"},
 | 
						|
		Null:     [2]string{"\x1B[2m", "\x1B[0m"},
 | 
						|
		Escape:   [2]string{"\x1B[35m", "\x1B[0m"},
 | 
						|
		Brackets: [2]string{"\x1B[1m", "\x1B[0m"},
 | 
						|
		Append: func(dst []byte, c byte) []byte {
 | 
						|
			if c < ' ' && (c != '\r' && c != '\n' && c != '\t' && c != '\v') {
 | 
						|
				dst = append(dst, "\\u00"...)
 | 
						|
				dst = append(dst, hexp((c>>4)&0xF))
 | 
						|
				return append(dst, hexp((c)&0xF))
 | 
						|
			}
 | 
						|
			return append(dst, c)
 | 
						|
		},
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// Color will colorize the json. The style parma is used for customizing
 | 
						|
// the colors. Passing nil to the style param will use the default
 | 
						|
// TerminalStyle.
 | 
						|
func Color(src []byte, style *Style) []byte {
 | 
						|
	if style == nil {
 | 
						|
		style = TerminalStyle
 | 
						|
	}
 | 
						|
	apnd := style.Append
 | 
						|
	if apnd == nil {
 | 
						|
		apnd = func(dst []byte, c byte) []byte {
 | 
						|
			return append(dst, c)
 | 
						|
		}
 | 
						|
	}
 | 
						|
	type stackt struct {
 | 
						|
		kind byte
 | 
						|
		key  bool
 | 
						|
	}
 | 
						|
	var dst []byte
 | 
						|
	var stack []stackt
 | 
						|
	for i := 0; i < len(src); i++ {
 | 
						|
		if src[i] == '"' {
 | 
						|
			key := len(stack) > 0 && stack[len(stack)-1].key
 | 
						|
			if key {
 | 
						|
				dst = append(dst, style.Key[0]...)
 | 
						|
			} else {
 | 
						|
				dst = append(dst, style.String[0]...)
 | 
						|
			}
 | 
						|
			dst = apnd(dst, '"')
 | 
						|
			esc := false
 | 
						|
			uesc := 0
 | 
						|
			for i = i + 1; i < len(src); i++ {
 | 
						|
				if src[i] == '\\' {
 | 
						|
					if key {
 | 
						|
						dst = append(dst, style.Key[1]...)
 | 
						|
					} else {
 | 
						|
						dst = append(dst, style.String[1]...)
 | 
						|
					}
 | 
						|
					dst = append(dst, style.Escape[0]...)
 | 
						|
					dst = apnd(dst, src[i])
 | 
						|
					esc = true
 | 
						|
					if i+1 < len(src) && src[i+1] == 'u' {
 | 
						|
						uesc = 5
 | 
						|
					} else {
 | 
						|
						uesc = 1
 | 
						|
					}
 | 
						|
				} else if esc {
 | 
						|
					dst = apnd(dst, src[i])
 | 
						|
					if uesc == 1 {
 | 
						|
						esc = false
 | 
						|
						dst = append(dst, style.Escape[1]...)
 | 
						|
						if key {
 | 
						|
							dst = append(dst, style.Key[0]...)
 | 
						|
						} else {
 | 
						|
							dst = append(dst, style.String[0]...)
 | 
						|
						}
 | 
						|
					} else {
 | 
						|
						uesc--
 | 
						|
					}
 | 
						|
				} else {
 | 
						|
					dst = apnd(dst, src[i])
 | 
						|
				}
 | 
						|
				if src[i] == '"' {
 | 
						|
					j := i - 1
 | 
						|
					for ; ; j-- {
 | 
						|
						if src[j] != '\\' {
 | 
						|
							break
 | 
						|
						}
 | 
						|
					}
 | 
						|
					if (j-i)%2 != 0 {
 | 
						|
						break
 | 
						|
					}
 | 
						|
				}
 | 
						|
			}
 | 
						|
			if esc {
 | 
						|
				dst = append(dst, style.Escape[1]...)
 | 
						|
			} else if key {
 | 
						|
				dst = append(dst, style.Key[1]...)
 | 
						|
			} else {
 | 
						|
				dst = append(dst, style.String[1]...)
 | 
						|
			}
 | 
						|
		} else if src[i] == '{' || src[i] == '[' {
 | 
						|
			stack = append(stack, stackt{src[i], src[i] == '{'})
 | 
						|
			dst = append(dst, style.Brackets[0]...)
 | 
						|
			dst = apnd(dst, src[i])
 | 
						|
			dst = append(dst, style.Brackets[1]...)
 | 
						|
		} else if (src[i] == '}' || src[i] == ']') && len(stack) > 0 {
 | 
						|
			stack = stack[:len(stack)-1]
 | 
						|
			dst = append(dst, style.Brackets[0]...)
 | 
						|
			dst = apnd(dst, src[i])
 | 
						|
			dst = append(dst, style.Brackets[1]...)
 | 
						|
		} else if (src[i] == ':' || src[i] == ',') && len(stack) > 0 && stack[len(stack)-1].kind == '{' {
 | 
						|
			stack[len(stack)-1].key = !stack[len(stack)-1].key
 | 
						|
			dst = append(dst, style.Brackets[0]...)
 | 
						|
			dst = apnd(dst, src[i])
 | 
						|
			dst = append(dst, style.Brackets[1]...)
 | 
						|
		} else {
 | 
						|
			var kind byte
 | 
						|
			if (src[i] >= '0' && src[i] <= '9') || src[i] == '-' || isNaNOrInf(src[i:]) {
 | 
						|
				kind = '0'
 | 
						|
				dst = append(dst, style.Number[0]...)
 | 
						|
			} else if src[i] == 't' {
 | 
						|
				kind = 't'
 | 
						|
				dst = append(dst, style.True[0]...)
 | 
						|
			} else if src[i] == 'f' {
 | 
						|
				kind = 'f'
 | 
						|
				dst = append(dst, style.False[0]...)
 | 
						|
			} else if src[i] == 'n' {
 | 
						|
				kind = 'n'
 | 
						|
				dst = append(dst, style.Null[0]...)
 | 
						|
			} else {
 | 
						|
				dst = apnd(dst, src[i])
 | 
						|
			}
 | 
						|
			if kind != 0 {
 | 
						|
				for ; i < len(src); i++ {
 | 
						|
					if src[i] <= ' ' || src[i] == ',' || src[i] == ':' || src[i] == ']' || src[i] == '}' {
 | 
						|
						i--
 | 
						|
						break
 | 
						|
					}
 | 
						|
					dst = apnd(dst, src[i])
 | 
						|
				}
 | 
						|
				if kind == '0' {
 | 
						|
					dst = append(dst, style.Number[1]...)
 | 
						|
				} else if kind == 't' {
 | 
						|
					dst = append(dst, style.True[1]...)
 | 
						|
				} else if kind == 'f' {
 | 
						|
					dst = append(dst, style.False[1]...)
 | 
						|
				} else if kind == 'n' {
 | 
						|
					dst = append(dst, style.Null[1]...)
 | 
						|
				}
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return dst
 | 
						|
}
 | 
						|
 | 
						|
// Spec strips out comments and trailing commas and convert the input to a
 | 
						|
// valid JSON per the official spec: https://tools.ietf.org/html/rfc8259
 | 
						|
//
 | 
						|
// The resulting JSON will always be the same length as the input and it will
 | 
						|
// include all of the same line breaks at matching offsets. This is to ensure
 | 
						|
// the result can be later processed by a external parser and that that
 | 
						|
// parser will report messages or errors with the correct offsets.
 | 
						|
func Spec(src []byte) []byte {
 | 
						|
	return spec(src, nil)
 | 
						|
}
 | 
						|
 | 
						|
// SpecInPlace is the same as Spec, but this method reuses the input json
 | 
						|
// buffer to avoid allocations. Do not use the original bytes slice upon return.
 | 
						|
func SpecInPlace(src []byte) []byte {
 | 
						|
	return spec(src, src)
 | 
						|
}
 | 
						|
 | 
						|
func spec(src, dst []byte) []byte {
 | 
						|
	dst = dst[:0]
 | 
						|
	for i := 0; i < len(src); i++ {
 | 
						|
		if src[i] == '/' {
 | 
						|
			if i < len(src)-1 {
 | 
						|
				if src[i+1] == '/' {
 | 
						|
					dst = append(dst, ' ', ' ')
 | 
						|
					i += 2
 | 
						|
					for ; i < len(src); i++ {
 | 
						|
						if src[i] == '\n' {
 | 
						|
							dst = append(dst, '\n')
 | 
						|
							break
 | 
						|
						} else if src[i] == '\t' || src[i] == '\r' {
 | 
						|
							dst = append(dst, src[i])
 | 
						|
						} else {
 | 
						|
							dst = append(dst, ' ')
 | 
						|
						}
 | 
						|
					}
 | 
						|
					continue
 | 
						|
				}
 | 
						|
				if src[i+1] == '*' {
 | 
						|
					dst = append(dst, ' ', ' ')
 | 
						|
					i += 2
 | 
						|
					for ; i < len(src)-1; i++ {
 | 
						|
						if src[i] == '*' && src[i+1] == '/' {
 | 
						|
							dst = append(dst, ' ', ' ')
 | 
						|
							i++
 | 
						|
							break
 | 
						|
						} else if src[i] == '\n' || src[i] == '\t' ||
 | 
						|
							src[i] == '\r' {
 | 
						|
							dst = append(dst, src[i])
 | 
						|
						} else {
 | 
						|
							dst = append(dst, ' ')
 | 
						|
						}
 | 
						|
					}
 | 
						|
					continue
 | 
						|
				}
 | 
						|
			}
 | 
						|
		}
 | 
						|
		dst = append(dst, src[i])
 | 
						|
		if src[i] == '"' {
 | 
						|
			for i = i + 1; i < len(src); i++ {
 | 
						|
				dst = append(dst, src[i])
 | 
						|
				if src[i] == '"' {
 | 
						|
					j := i - 1
 | 
						|
					for ; ; j-- {
 | 
						|
						if src[j] != '\\' {
 | 
						|
							break
 | 
						|
						}
 | 
						|
					}
 | 
						|
					if (j-i)%2 != 0 {
 | 
						|
						break
 | 
						|
					}
 | 
						|
				}
 | 
						|
			}
 | 
						|
		} else if src[i] == '}' || src[i] == ']' {
 | 
						|
			for j := len(dst) - 2; j >= 0; j-- {
 | 
						|
				if dst[j] <= ' ' {
 | 
						|
					continue
 | 
						|
				}
 | 
						|
				if dst[j] == ',' {
 | 
						|
					dst[j] = ' '
 | 
						|
				}
 | 
						|
				break
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return dst
 | 
						|
}
 |