@@ -31,6 +31,8 @@ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
3131 */
3232package com .jsoniter .output ;
3333
34+ import com .jsoniter .spi .JsonException ;
35+
3436import java .io .IOException ;
3537
3638class StreamImplString {
@@ -39,6 +41,10 @@ class StreamImplString {
3941 '0' , '1' , '2' , '3' , '4' , '5' , '6' , '7' , '8' , '9' ,
4042 'a' , 'b' , 'c' , 'd' , 'e' , 'f' };
4143 private static final boolean [] CAN_DIRECT_WRITE = new boolean [128 ];
44+ private final static int SURR1_FIRST = 0xD800 ;
45+ private final static int SURR1_LAST = 0xDBFF ;
46+ private final static int SURR2_FIRST = 0xDC00 ;
47+ private final static int SURR2_LAST = 0xDFFF ;
4248
4349 static {
4450 for (int i = 0 ; i < CAN_DIRECT_WRITE .length ; i ++) {
@@ -122,41 +128,102 @@ public static final void writeStringWithoutQuote(final JsonStream stream, final
122128 }
123129
124130 private static void writeStringSlowPath (JsonStream stream , String val , int i , int valLen ) throws IOException {
131+ boolean escapeUnicode = stream .currentConfig ().escapeUnicode ();
132+ if (escapeUnicode ) {
133+ for (; i < valLen ; i ++) {
134+ int c = val .charAt (i );
135+ if (c > 125 ) {
136+ byte b4 = (byte ) (c & 0xf );
137+ byte b3 = (byte ) (c >> 4 & 0xf );
138+ byte b2 = (byte ) (c >> 8 & 0xf );
139+ byte b1 = (byte ) (c >> 12 & 0xf );
140+ stream .write ((byte ) '\\' , (byte ) 'u' , ITOA [b1 ], ITOA [b2 ], ITOA [b3 ], ITOA [b4 ]);
141+ } else {
142+ writeAsciiChar (stream , c );
143+ }
144+ }
145+ } else {
146+ writeStringSlowPathWithoutEscapeUnicode (stream , val , i , valLen );
147+ }
148+ }
149+
150+ private static void writeStringSlowPathWithoutEscapeUnicode (JsonStream stream , String val , int i , int valLen ) throws IOException {
151+ int _surrogate ;
125152 for (; i < valLen ; i ++) {
126153 int c = val .charAt (i );
127154 if (c > 125 ) {
128- byte b4 = (byte ) (c & 0xf );
129- byte b3 = (byte ) (c >> 4 & 0xf );
130- byte b2 = (byte ) (c >> 8 & 0xf );
131- byte b1 = (byte ) (c >> 12 & 0xf );
132- stream .write ((byte ) '\\' , (byte ) 'u' , ITOA [b1 ], ITOA [b2 ], ITOA [b3 ], ITOA [b4 ]);
133- } else {
134- switch (c ) {
135- case '"' :
136- stream .write ((byte ) '\\' , (byte ) '"' );
137- break ;
138- case '\\' :
139- stream .write ((byte ) '\\' , (byte ) '\\' );
140- break ;
141- case '\b' :
142- stream .write ((byte ) '\\' , (byte ) 'b' );
143- break ;
144- case '\f' :
145- stream .write ((byte ) '\\' , (byte ) 'f' );
146- break ;
147- case '\n' :
148- stream .write ((byte ) '\\' , (byte ) 'n' );
149- break ;
150- case '\r' :
151- stream .write ((byte ) '\\' , (byte ) 'r' );
155+ if (c < 0x800 ) { // 2-byte
156+ stream .write (
157+ (byte ) (0xc0 | (c >> 6 )),
158+ (byte ) (0x80 | (c & 0x3f ))
159+ );
160+ } else { // 3 or 4 bytes
161+ // Surrogates?
162+ if (c < SURR1_FIRST || c > SURR2_LAST ) {
163+ stream .write (
164+ (byte ) (0xe0 | (c >> 12 )),
165+ (byte ) (0x80 | ((c >> 6 ) & 0x3f )),
166+ (byte ) (0x80 | (c & 0x3f ))
167+ );
168+ continue ;
169+ }
170+ // Yup, a surrogate:
171+ if (c > SURR1_LAST ) { // must be from first range
172+ throw new JsonException ("illegalSurrogate" );
173+ }
174+ _surrogate = c ;
175+ // and if so, followed by another from next range
176+ if (i >= valLen ) { // unless we hit the end?
152177 break ;
153- case '\t' :
154- stream .write ((byte ) '\\' , (byte ) 't' );
155- break ;
156- default :
157- stream .write (c );
178+ }
179+ int firstPart = _surrogate ;
180+ _surrogate = 0 ;
181+ // Ok, then, is the second part valid?
182+ if (c < SURR2_FIRST || c > SURR2_LAST ) {
183+ throw new JsonException ("Broken surrogate pair: first char 0x" + Integer .toHexString (firstPart ) + ", second 0x" + Integer .toHexString (c ) + "; illegal combination" );
184+ }
185+ c = 0x10000 + ((firstPart - SURR1_FIRST ) << 10 ) + (c - SURR2_FIRST );
186+ if (c > 0x10FFFF ) { // illegal in JSON as well as in XML
187+ throw new JsonException ("illegalSurrogate" );
188+ }
189+ stream .write (
190+ (byte ) (0xf0 | (c >> 18 )),
191+ (byte ) (0x80 | ((c >> 12 ) & 0x3f )),
192+ (byte ) (0x80 | ((c >> 6 ) & 0x3f )),
193+ (byte ) (0x80 | (c & 0x3f ))
194+ );
158195 }
196+ } else {
197+ writeAsciiChar (stream , c );
159198 }
160199 }
161200 }
201+
202+ private static void writeAsciiChar (JsonStream stream , int c ) throws IOException {
203+ switch (c ) {
204+ case '"' :
205+ stream .write ((byte ) '\\' , (byte ) '"' );
206+ break ;
207+ case '\\' :
208+ stream .write ((byte ) '\\' , (byte ) '\\' );
209+ break ;
210+ case '\b' :
211+ stream .write ((byte ) '\\' , (byte ) 'b' );
212+ break ;
213+ case '\f' :
214+ stream .write ((byte ) '\\' , (byte ) 'f' );
215+ break ;
216+ case '\n' :
217+ stream .write ((byte ) '\\' , (byte ) 'n' );
218+ break ;
219+ case '\r' :
220+ stream .write ((byte ) '\\' , (byte ) 'r' );
221+ break ;
222+ case '\t' :
223+ stream .write ((byte ) '\\' , (byte ) 't' );
224+ break ;
225+ default :
226+ stream .write (c );
227+ }
228+ }
162229}
0 commit comments