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