safeAppendStringLike is a generic implementation of safeAddString and safeAddByteString. It appends a string or byte slice to the buffer, escaping all special characters.
( // appendTo appends this string-like object to the buffer. appendTo func(*buffer.Buffer, S), // decodeRune decodes the next rune from the string-like object // and returns its value and width in bytes. decodeRune func(S) (rune, int), buf *buffer.Buffer, s S, )
| 507 | // safeAppendStringLike is a generic implementation of safeAddString and safeAddByteString. |
| 508 | // It appends a string or byte slice to the buffer, escaping all special characters. |
| 509 | func safeAppendStringLike[S []byte | string]( |
| 510 | // appendTo appends this string-like object to the buffer. |
| 511 | appendTo func(*buffer.Buffer, S), |
| 512 | // decodeRune decodes the next rune from the string-like object |
| 513 | // and returns its value and width in bytes. |
| 514 | decodeRune func(S) (rune, int), |
| 515 | buf *buffer.Buffer, |
| 516 | s S, |
| 517 | ) { |
| 518 | // The encoding logic below works by skipping over characters |
| 519 | // that can be safely copied as-is, |
| 520 | // until a character is found that needs special handling. |
| 521 | // At that point, we copy everything we've seen so far, |
| 522 | // and then handle that special character. |
| 523 | // |
| 524 | // last is the index of the last byte that was copied to the buffer. |
| 525 | last := 0 |
| 526 | for i := 0; i < len(s); { |
| 527 | if s[i] >= utf8.RuneSelf { |
| 528 | // Character >= RuneSelf may be part of a multi-byte rune. |
| 529 | // They need to be decoded before we can decide how to handle them. |
| 530 | r, size := decodeRune(s[i:]) |
| 531 | if r != utf8.RuneError || size != 1 { |
| 532 | // No special handling required. |
| 533 | // Skip over this rune and continue. |
| 534 | i += size |
| 535 | continue |
| 536 | } |
| 537 | |
| 538 | // Invalid UTF-8 sequence. |
| 539 | // Replace it with the Unicode replacement character. |
| 540 | appendTo(buf, s[last:i]) |
| 541 | buf.AppendString(`\ufffd`) |
| 542 | |
| 543 | i++ |
| 544 | last = i |
| 545 | } else { |
| 546 | // Character < RuneSelf is a single-byte UTF-8 rune. |
| 547 | if s[i] >= 0x20 && s[i] != '\\' && s[i] != '"' { |
| 548 | // No escaping necessary. |
| 549 | // Skip over this character and continue. |
| 550 | i++ |
| 551 | continue |
| 552 | } |
| 553 | |
| 554 | // This character needs to be escaped. |
| 555 | appendTo(buf, s[last:i]) |
| 556 | switch s[i] { |
| 557 | case '\\', '"': |
| 558 | buf.AppendByte('\\') |
| 559 | buf.AppendByte(s[i]) |
| 560 | case '\n': |
| 561 | buf.AppendByte('\\') |
| 562 | buf.AppendByte('n') |
| 563 | case '\r': |
| 564 | buf.AppendByte('\\') |
| 565 | buf.AppendByte('r') |
| 566 | case '\t': |