github.com/sagernet/sing@v0.4.0-beta.19.0.20240518125136-f67a0988a636/common/json/internal/contextjson_120/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 ( 8 "bytes" 9 ) 10 11 // Compact appends to dst the JSON-encoded src with 12 // insignificant space characters elided. 13 func Compact(dst *bytes.Buffer, src []byte) error { 14 return compact(dst, src, false) 15 } 16 17 func compact(dst *bytes.Buffer, src []byte, escape bool) error { 18 origLen := dst.Len() 19 scan := newScanner() 20 defer freeScanner(scan) 21 start := 0 22 for i, c := range src { 23 if escape && (c == '<' || c == '>' || c == '&') { 24 if start < i { 25 dst.Write(src[start:i]) 26 } 27 dst.WriteString(`\u00`) 28 dst.WriteByte(hex[c>>4]) 29 dst.WriteByte(hex[c&0xF]) 30 start = i + 1 31 } 32 // Convert U+2028 and U+2029 (E2 80 A8 and E2 80 A9). 33 if escape && c == 0xE2 && i+2 < len(src) && src[i+1] == 0x80 && src[i+2]&^1 == 0xA8 { 34 if start < i { 35 dst.Write(src[start:i]) 36 } 37 dst.WriteString(`\u202`) 38 dst.WriteByte(hex[src[i+2]&0xF]) 39 start = i + 3 40 } 41 v := scan.step(scan, c) 42 if v >= scanSkipSpace { 43 if v == scanError { 44 break 45 } 46 if start < i { 47 dst.Write(src[start:i]) 48 } 49 start = i + 1 50 } 51 } 52 if scan.eof() == scanError { 53 dst.Truncate(origLen) 54 return scan.err 55 } 56 if start < len(src) { 57 dst.Write(src[start:]) 58 } 59 return nil 60 } 61 62 func newline(dst *bytes.Buffer, prefix, indent string, depth int) { 63 dst.WriteByte('\n') 64 dst.WriteString(prefix) 65 for i := 0; i < depth; i++ { 66 dst.WriteString(indent) 67 } 68 } 69 70 // Indent appends to dst an indented form of the JSON-encoded src. 71 // Each element in a JSON object or array begins on a new, 72 // indented line beginning with prefix followed by one or more 73 // copies of indent according to the indentation nesting. 74 // The data appended to dst does not begin with the prefix nor 75 // any indentation, to make it easier to embed inside other formatted JSON data. 76 // Although leading space characters (space, tab, carriage return, newline) 77 // at the beginning of src are dropped, trailing space characters 78 // at the end of src are preserved and copied to dst. 79 // For example, if src has no trailing spaces, neither will dst; 80 // if src ends in a trailing newline, so will dst. 81 func Indent(dst *bytes.Buffer, src []byte, prefix, indent string) error { 82 origLen := dst.Len() 83 scan := newScanner() 84 defer freeScanner(scan) 85 needIndent := false 86 depth := 0 87 for _, c := range src { 88 scan.bytes++ 89 v := scan.step(scan, c) 90 if v == scanSkipSpace { 91 continue 92 } 93 if v == scanError { 94 break 95 } 96 if needIndent && v != scanEndObject && v != scanEndArray { 97 needIndent = false 98 depth++ 99 newline(dst, prefix, indent, depth) 100 } 101 102 // Emit semantically uninteresting bytes 103 // (in particular, punctuation in strings) unmodified. 104 if v == scanContinue { 105 dst.WriteByte(c) 106 continue 107 } 108 109 // Add spacing around real punctuation. 110 switch c { 111 case '{', '[': 112 // delay indent so that empty object and array are formatted as {} and []. 113 needIndent = true 114 dst.WriteByte(c) 115 116 case ',': 117 dst.WriteByte(c) 118 newline(dst, prefix, indent, depth) 119 120 case ':': 121 dst.WriteByte(c) 122 dst.WriteByte(' ') 123 124 case '}', ']': 125 if needIndent { 126 // suppress indent in empty object/array 127 needIndent = false 128 } else { 129 depth-- 130 newline(dst, prefix, indent, depth) 131 } 132 dst.WriteByte(c) 133 134 default: 135 dst.WriteByte(c) 136 } 137 } 138 if scan.eof() == scanError { 139 dst.Truncate(origLen) 140 return scan.err 141 } 142 return nil 143 }