1 // Protocol Buffers - Google's data interchange format 2 // Copyright 2008 Google Inc. All rights reserved. 3 // 4 // Use of this source code is governed by a BSD-style 5 // license that can be found in the LICENSE file or at 6 // https://developers.google.com/open-source/licenses/bsd 7 8 package com.google.protobuf; 9 10 /** 11 * Provide text format escaping of proto instances. These ASCII characters are escaped: 12 * 13 * ASCII #7 (bell) --> \a 14 * ASCII #8 (backspace) --> \b 15 * ASCII #9 (horizontal tab) --> \t 16 * ASCII #10 (linefeed) --> \n 17 * ASCII #11 (vertical tab) --> \v 18 * ASCII #13 (carriage return) --> \r 19 * ASCII #12 (formfeed) --> \f 20 * ASCII #34 (apostrophe) --> \' 21 * ASCII #39 (straight double quote) --> \" 22 * ASCII #92 (backslash) --> \\ 23 * 24 * Other printable ASCII characters between 32 and 127 inclusive are output as is, unescaped. 25 * Other ASCII characters less than 32 and all Unicode characters 128 or greater are 26 * first encoded as UTF-8, then each byte is escaped individually as a 3-digit octal escape. 27 */ 28 final class TextFormatEscaper { TextFormatEscaper()29 private TextFormatEscaper() {} 30 31 private interface ByteSequence { size()32 int size(); 33 byteAt(int offset)34 byte byteAt(int offset); 35 } 36 37 /** 38 * Backslash escapes bytes in the format used in protocol buffer text format. 39 */ escapeBytes(ByteSequence input)40 static String escapeBytes(ByteSequence input) { 41 final StringBuilder builder = new StringBuilder(input.size()); 42 for (int i = 0; i < input.size(); i++) { 43 byte b = input.byteAt(i); 44 switch (b) { 45 case 0x07: 46 builder.append("\\a"); 47 break; 48 case '\b': 49 builder.append("\\b"); 50 break; 51 case '\f': 52 builder.append("\\f"); 53 break; 54 case '\n': 55 builder.append("\\n"); 56 break; 57 case '\r': 58 builder.append("\\r"); 59 break; 60 case '\t': 61 builder.append("\\t"); 62 break; 63 case 0x0b: 64 builder.append("\\v"); 65 break; 66 case '\\': 67 builder.append("\\\\"); 68 break; 69 case '\'': 70 builder.append("\\\'"); 71 break; 72 case '"': 73 builder.append("\\\""); 74 break; 75 default: 76 // Only ASCII characters between 0x20 (space) and 0x7e (tilde) are 77 // printable. Other byte values must be escaped. 78 if (b >= 0x20 && b <= 0x7e) { 79 builder.append((char) b); 80 } else { 81 builder.append('\\'); 82 builder.append((char) ('0' + ((b >>> 6) & 3))); 83 builder.append((char) ('0' + ((b >>> 3) & 7))); 84 builder.append((char) ('0' + (b & 7))); 85 } 86 break; 87 } 88 } 89 return builder.toString(); 90 } 91 92 /** 93 * Backslash escapes bytes in the format used in protocol buffer text format. 94 */ escapeBytes(final ByteString input)95 static String escapeBytes(final ByteString input) { 96 return escapeBytes( 97 new ByteSequence() { 98 @Override 99 public int size() { 100 return input.size(); 101 } 102 103 @Override 104 public byte byteAt(int offset) { 105 return input.byteAt(offset); 106 } 107 }); 108 } 109 110 /** Like {@link #escapeBytes(ByteString)}, but used for byte array. */ 111 static String escapeBytes(final byte[] input) { 112 return escapeBytes( 113 new ByteSequence() { 114 @Override 115 public int size() { 116 return input.length; 117 } 118 119 @Override 120 public byte byteAt(int offset) { 121 return input[offset]; 122 } 123 }); 124 } 125 126 /** 127 * Like {@link #escapeBytes(ByteString)}, but escapes a text string. 128 */ 129 static String escapeText(String input) { 130 return escapeBytes(ByteString.copyFromUtf8(input)); 131 } 132 133 /** Escape double quotes and backslashes in a String for unicode output of a message. */ 134 static String escapeDoubleQuotesAndBackslashes(String input) { 135 return input.replace("\\", "\\\\").replace("\"", "\\\""); 136 } 137 } 138