github.com/qiniu/dyn@v1.3.0/text/internal/encoding/json/indent.go (about)

     1  // Copyright 2010 The Go Authors.  All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package json
     6  
     7  import "bytes"
     8  
     9  // Compact appends to dst the JSON-encoded src with
    10  // insignificant space characters elided.
    11  func Compact(dst *bytes.Buffer, src []byte) error {
    12  	return compact(dst, src, false)
    13  }
    14  
    15  func compact(dst *bytes.Buffer, src []byte, escape bool) error {
    16  	origLen := dst.Len()
    17  	var scan scanner
    18  	scan.reset()
    19  	start := 0
    20  	for i, c := range src {
    21  		if escape && (c == '<' || c == '>' || c == '&') {
    22  			if start < i {
    23  				dst.Write(src[start:i])
    24  			}
    25  			dst.WriteString(`\u00`)
    26  			dst.WriteByte(hex[c>>4])
    27  			dst.WriteByte(hex[c&0xF])
    28  			start = i + 1
    29  		}
    30  		v := scan.step(&scan, int(c))
    31  		if v >= scanSkipSpace {
    32  			if v == scanError {
    33  				break
    34  			}
    35  			if start < i {
    36  				dst.Write(src[start:i])
    37  			}
    38  			start = i + 1
    39  		}
    40  	}
    41  	if scan.eof() == scanError {
    42  		dst.Truncate(origLen)
    43  		return scan.err
    44  	}
    45  	if start < len(src) {
    46  		dst.Write(src[start:])
    47  	}
    48  	return nil
    49  }
    50  
    51  func newline(dst *bytes.Buffer, prefix, indent string, depth int) {
    52  	dst.WriteByte('\n')
    53  	dst.WriteString(prefix)
    54  	for i := 0; i < depth; i++ {
    55  		dst.WriteString(indent)
    56  	}
    57  }
    58  
    59  // Indent appends to dst an indented form of the JSON-encoded src.
    60  // Each element in a JSON object or array begins on a new,
    61  // indented line beginning with prefix followed by one or more
    62  // copies of indent according to the indentation nesting.
    63  // The data appended to dst has no trailing newline, to make it easier
    64  // to embed inside other formatted JSON data.
    65  func Indent(dst *bytes.Buffer, src []byte, prefix, indent string) error {
    66  	origLen := dst.Len()
    67  	var scan scanner
    68  	scan.reset()
    69  	needIndent := false
    70  	depth := 0
    71  	for _, c := range src {
    72  		scan.bytes++
    73  		v := scan.step(&scan, int(c))
    74  		if v == scanSkipSpace {
    75  			continue
    76  		}
    77  		if v == scanError {
    78  			break
    79  		}
    80  		if needIndent && v != scanEndObject && v != scanEndArray {
    81  			needIndent = false
    82  			depth++
    83  			newline(dst, prefix, indent, depth)
    84  		}
    85  
    86  		// Emit semantically uninteresting bytes
    87  		// (in particular, punctuation in strings) unmodified.
    88  		if v == scanContinue {
    89  			dst.WriteByte(c)
    90  			continue
    91  		}
    92  
    93  		// Add spacing around real punctuation.
    94  		switch c {
    95  		case '{', '[':
    96  			// delay indent so that empty object and array are formatted as {} and [].
    97  			needIndent = true
    98  			dst.WriteByte(c)
    99  
   100  		case ',':
   101  			dst.WriteByte(c)
   102  			newline(dst, prefix, indent, depth)
   103  
   104  		case ':':
   105  			dst.WriteByte(c)
   106  			dst.WriteByte(' ')
   107  
   108  		case '}', ']':
   109  			if needIndent {
   110  				// suppress indent in empty object/array
   111  				needIndent = false
   112  			} else {
   113  				depth--
   114  				newline(dst, prefix, indent, depth)
   115  			}
   116  			dst.WriteByte(c)
   117  
   118  		default:
   119  			dst.WriteByte(c)
   120  		}
   121  	}
   122  	if scan.eof() == scanError {
   123  		dst.Truncate(origLen)
   124  		return scan.err
   125  	}
   126  	return nil
   127  }