github.com/v2pro/plz@v0.0.0-20221028024117-e5f9aec5b631/msgfmt/jsonfmt/encoder_str.go (about)

     1  package jsonfmt
     2  
     3  import (
     4  	"unsafe"
     5  	"unicode/utf8"
     6  	"context"
     7  )
     8  
     9  // safeSet holds the value true if the ASCII character with the given array
    10  // position can be represented inside a JSON string without any further
    11  // escaping.
    12  //
    13  // All values are true except for the ASCII control characters (0-31), the
    14  // double quote ("), and the backslash character ("\").
    15  var safeSet = [utf8.RuneSelf]bool{
    16  	' ':      true,
    17  	'!':      true,
    18  	'"':      false,
    19  	'#':      true,
    20  	'$':      true,
    21  	'%':      true,
    22  	'&':      true,
    23  	'\'':     true,
    24  	'(':      true,
    25  	')':      true,
    26  	'*':      true,
    27  	'+':      true,
    28  	',':      true,
    29  	'-':      true,
    30  	'.':      true,
    31  	'/':      true,
    32  	'0':      true,
    33  	'1':      true,
    34  	'2':      true,
    35  	'3':      true,
    36  	'4':      true,
    37  	'5':      true,
    38  	'6':      true,
    39  	'7':      true,
    40  	'8':      true,
    41  	'9':      true,
    42  	':':      true,
    43  	';':      true,
    44  	'<':      true,
    45  	'=':      true,
    46  	'>':      true,
    47  	'?':      true,
    48  	'@':      true,
    49  	'A':      true,
    50  	'B':      true,
    51  	'C':      true,
    52  	'D':      true,
    53  	'E':      true,
    54  	'F':      true,
    55  	'G':      true,
    56  	'H':      true,
    57  	'I':      true,
    58  	'J':      true,
    59  	'K':      true,
    60  	'L':      true,
    61  	'M':      true,
    62  	'N':      true,
    63  	'O':      true,
    64  	'P':      true,
    65  	'Q':      true,
    66  	'R':      true,
    67  	'S':      true,
    68  	'T':      true,
    69  	'U':      true,
    70  	'V':      true,
    71  	'W':      true,
    72  	'X':      true,
    73  	'Y':      true,
    74  	'Z':      true,
    75  	'[':      true,
    76  	'\\':     false,
    77  	']':      true,
    78  	'^':      true,
    79  	'_':      true,
    80  	'`':      true,
    81  	'a':      true,
    82  	'b':      true,
    83  	'c':      true,
    84  	'd':      true,
    85  	'e':      true,
    86  	'f':      true,
    87  	'g':      true,
    88  	'h':      true,
    89  	'i':      true,
    90  	'j':      true,
    91  	'k':      true,
    92  	'l':      true,
    93  	'm':      true,
    94  	'n':      true,
    95  	'o':      true,
    96  	'p':      true,
    97  	'q':      true,
    98  	'r':      true,
    99  	's':      true,
   100  	't':      true,
   101  	'u':      true,
   102  	'v':      true,
   103  	'w':      true,
   104  	'x':      true,
   105  	'y':      true,
   106  	'z':      true,
   107  	'{':      true,
   108  	'|':      true,
   109  	'}':      true,
   110  	'~':      true,
   111  	'\u007f': true,
   112  }
   113  
   114  var hex = "0123456789abcdef"
   115  
   116  type stringEncoder struct {
   117  }
   118  
   119  func (encoder *stringEncoder) Encode(ctx context.Context, space []byte, ptr unsafe.Pointer) []byte {
   120  	return WriteString(space, *(*string)(ptr))
   121  }
   122  
   123  func WriteString(space []byte, str string) []byte {
   124  	space = append(space, '"')
   125  	// write string, the fast path, without utf8 and escape support
   126  	var i int
   127  	var c byte
   128  	for i, c = range []byte(str) {
   129  		if c > 31 && c != '"' && c != '\\' {
   130  			space = append(space, c)
   131  		} else {
   132  			break
   133  		}
   134  	}
   135  	if i == len(str)-1 {
   136  		space = append(space, '"')
   137  		return space
   138  	}
   139  	return writeStringSlowPath(space, []byte(str[i:]))
   140  }
   141  
   142  func writeStringSlowPath(space []byte, s []byte) []byte {
   143  	start := 0
   144  	// for the remaining parts, we process them char by char
   145  	var i int
   146  	var b byte
   147  	for i, b = range s {
   148  		if b >= utf8.RuneSelf {
   149  			continue
   150  		}
   151  		if safeSet[b] {
   152  			continue
   153  		}
   154  		if start < i {
   155  			space = append(space, s[start:i]...)
   156  		}
   157  		switch b {
   158  		case '\\', '"':
   159  			space = append(space, '\\', b)
   160  		case '\n':
   161  			space = append(space, '\\', 'n')
   162  		case '\r':
   163  			space = append(space, '\\', 'r')
   164  		case '\t':
   165  			space = append(space, '\\', 't')
   166  		default:
   167  			// This encodes bytes < 0x20 except for \t, \n and \r.
   168  			space = append(space, '\\', 'u', '0', '0', hex[b>>4], hex[b&0xF])
   169  		}
   170  		start = i + 1
   171  	}
   172  	if start < len(s) {
   173  		space = append(space, s[start:]...)
   174  	}
   175  	return append(space, '"')
   176  }