| 570 | } |
| 571 | |
| 572 | private static final class StatusMessageMarshaller implements TrustedAsciiMarshaller<String> { |
| 573 | |
| 574 | private static final byte[] HEX = |
| 575 | {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'}; |
| 576 | |
| 577 | @Override |
| 578 | public byte[] toAsciiString(String value) { |
| 579 | byte[] valueBytes = value.getBytes(UTF_8); |
| 580 | for (int i = 0; i < valueBytes.length; i++) { |
| 581 | byte b = valueBytes[i]; |
| 582 | // If there are only non escaping characters, skip the slow path. |
| 583 | if (isEscapingChar(b)) { |
| 584 | return toAsciiStringSlow(valueBytes, i); |
| 585 | } |
| 586 | } |
| 587 | return valueBytes; |
| 588 | } |
| 589 | |
| 590 | private static boolean isEscapingChar(byte b) { |
| 591 | return b < ' ' || b >= '~' || b == '%'; |
| 592 | } |
| 593 | |
| 594 | /** |
| 595 | * Percent encode bytes to make them ASCII. |
| 596 | * |
| 597 | * @param valueBytes the UTF-8 bytes |
| 598 | * @param ri The reader index, pointed at the first byte that needs escaping. |
| 599 | */ |
| 600 | private static byte[] toAsciiStringSlow(byte[] valueBytes, int ri) { |
| 601 | byte[] escapedBytes = new byte[ri + (valueBytes.length - ri) * 3]; |
| 602 | // copy over the good bytes |
| 603 | if (ri != 0) { |
| 604 | System.arraycopy(valueBytes, 0, escapedBytes, 0, ri); |
| 605 | } |
| 606 | int wi = ri; |
| 607 | for (; ri < valueBytes.length; ri++) { |
| 608 | byte b = valueBytes[ri]; |
| 609 | // Manually implement URL encoding, per the gRPC spec. |
| 610 | if (isEscapingChar(b)) { |
| 611 | escapedBytes[wi] = '%'; |
| 612 | escapedBytes[wi + 1] = HEX[(b >> 4) & 0xF]; |
| 613 | escapedBytes[wi + 2] = HEX[b & 0xF]; |
| 614 | wi += 3; |
| 615 | continue; |
| 616 | } |
| 617 | escapedBytes[wi++] = b; |
| 618 | } |
| 619 | return Arrays.copyOf(escapedBytes, wi); |
| 620 | } |
| 621 | |
| 622 | @SuppressWarnings("deprecation") // Use fast but deprecated String ctor |
| 623 | @Override |
| 624 | public String parseAsciiString(byte[] value) { |
| 625 | for (int i = 0; i < value.length; i++) { |
| 626 | byte b = value[i]; |
| 627 | if (b < ' ' || b >= '~' || (b == '%' && i + 2 < value.length)) { |
| 628 | return parseAsciiStringSlow(value); |
| 629 | } |
nothing calls this directly
no outgoing calls
no test coverage detected