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  }