|
1 | 1 | package com.jsoniter; |
2 | 2 |
|
3 | | -import java.io.ByteArrayOutputStream; |
4 | 3 | import java.io.Closeable; |
5 | 4 | import java.io.IOException; |
6 | 5 | import java.io.InputStream; |
|
11 | 10 | import java.util.HashMap; |
12 | 11 | import java.util.Map; |
13 | 12 |
|
14 | | -import static java.lang.Character.*; |
15 | | - |
16 | 13 | public class Jsoniter implements Closeable { |
17 | 14 |
|
18 | 15 | private static final boolean[] breaks = new boolean[256]; |
19 | 16 | final static ValueType[] valueTypes = new ValueType[256]; |
20 | | - int[] base64Tbl = { |
21 | | - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, |
22 | | - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, |
23 | | - -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, 52, 53, 54, |
24 | | - 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, |
25 | | - 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, |
26 | | - 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, -1, 26, 27, 28, 29, 30, |
27 | | - 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, |
28 | | - 48, 49, 50, 51, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, |
29 | | - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, |
30 | | - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, |
31 | | - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, |
32 | | - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, |
33 | | - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, |
34 | | - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, |
35 | | - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}; |
36 | 17 | InputStream in; |
37 | 18 | byte[] buf; |
38 | 19 | int head; |
39 | 20 | int tail; |
40 | 21 | boolean eof; |
41 | | - private final Slice reusableSlice = new Slice(null, 0, 0); |
| 22 | + final Slice reusableSlice = new Slice(null, 0, 0); |
42 | 23 | char[] reusableChars = new char[32]; |
43 | 24 |
|
44 | 25 | static { |
@@ -252,214 +233,12 @@ final byte nextToken() throws IOException { |
252 | 233 | } |
253 | 234 | } |
254 | 235 |
|
255 | | - final Slice readSlice() throws IOException { |
256 | | - int end = findSliceEnd(); |
257 | | - if (end != -1) { |
258 | | - // reuse current buffer |
259 | | - reusableSlice.data = buf; |
260 | | - reusableSlice.head = head; |
261 | | - reusableSlice.len = end - head - 1; |
262 | | - head = end; |
263 | | - return reusableSlice; |
264 | | - } |
265 | | - byte[] part1 = new byte[tail - head]; |
266 | | - System.arraycopy(buf, head, part1, 0, part1.length); |
267 | | - for (; ; ) { |
268 | | - if (!loadMore()) { |
269 | | - throw reportError("readSlice", "unmatched quote"); |
270 | | - } |
271 | | - end = findSliceEnd(); |
272 | | - if (end == -1) { |
273 | | - byte[] part2 = new byte[part1.length + buf.length]; |
274 | | - System.arraycopy(part1, 0, part2, 0, part1.length); |
275 | | - System.arraycopy(buf, 0, part2, part1.length, buf.length); |
276 | | - part1 = part2; |
277 | | - } else { |
278 | | - byte[] part2 = new byte[part1.length + end - 1]; |
279 | | - System.arraycopy(part1, 0, part2, 0, part1.length); |
280 | | - System.arraycopy(buf, 0, part2, part1.length, end - 1); |
281 | | - head = end; |
282 | | - reusableSlice.data = part2; |
283 | | - reusableSlice.head = 0; |
284 | | - reusableSlice.len = part2.length; |
285 | | - return reusableSlice; |
286 | | - } |
287 | | - } |
288 | | - } |
289 | | - |
290 | | - public final byte[] readBase64() throws IOException { |
291 | | - // from https://gist.github.com/EmilHernvall/953733 |
292 | | - if (nextToken() != '"') { |
293 | | - throw reportError("readBase64", "expect \" for base64"); |
294 | | - } |
295 | | - Slice slice = readSlice(); |
296 | | - if (slice == null) { |
297 | | - return null; |
298 | | - } |
299 | | - ByteArrayOutputStream buffer = new ByteArrayOutputStream(); |
300 | | - int end = slice.head + slice.len; |
301 | | - for (int i = slice.head; i < end; i++) { |
302 | | - int b = 0; |
303 | | - if (base64Tbl[slice.data[i]] != -1) { |
304 | | - b = (base64Tbl[slice.data[i]] & 0xFF) << 18; |
305 | | - } |
306 | | - // skip unknown characters |
307 | | - else { |
308 | | - i++; |
309 | | - continue; |
310 | | - } |
311 | | - |
312 | | - int num = 0; |
313 | | - if (i + 1 < end && base64Tbl[slice.data[i + 1]] != -1) { |
314 | | - b = b | ((base64Tbl[slice.data[i + 1]] & 0xFF) << 12); |
315 | | - num++; |
316 | | - } |
317 | | - if (i + 2 < end && base64Tbl[slice.data[i + 2]] != -1) { |
318 | | - b = b | ((base64Tbl[slice.data[i + 2]] & 0xFF) << 6); |
319 | | - num++; |
320 | | - } |
321 | | - if (i + 3 < end && base64Tbl[slice.data[i + 3]] != -1) { |
322 | | - b = b | (base64Tbl[slice.data[i + 3]] & 0xFF); |
323 | | - num++; |
324 | | - } |
325 | | - |
326 | | - while (num > 0) { |
327 | | - int c = (b & 0xFF0000) >> 16; |
328 | | - buffer.write((char) c); |
329 | | - b <<= 8; |
330 | | - num--; |
331 | | - } |
332 | | - i += 4; |
333 | | - } |
334 | | - return buffer.toByteArray(); |
335 | | - } |
336 | | - |
337 | 236 | public final String readString() throws IOException { |
338 | | - byte c = nextToken(); |
339 | | - if (c == 'n') { |
340 | | - skipUntilBreak(); |
341 | | - return null; |
342 | | - } |
343 | | - if (c != '"') { |
344 | | - throw reportError("readString", "expect n or \""); |
345 | | - } |
346 | | - // try fast path first |
347 | | - for (int i = head, j = 0; i < tail && j < reusableChars.length; i++, j++) { |
348 | | - c = buf[i]; |
349 | | - if (c == '"') { |
350 | | - head = i + 1; |
351 | | - return new String(reusableChars, 0, j); |
352 | | - } |
353 | | - // If we encounter a backslash, which is a beginning of an escape sequence |
354 | | - // or a high bit was set - indicating an UTF-8 encoded multibyte character, |
355 | | - // there is no chance that we can decode the string without instantiating |
356 | | - // a temporary buffer, so quit this loop |
357 | | - if ((c ^ '\\') < 1) break; |
358 | | - reusableChars[j] = (char) c; |
359 | | - } |
360 | | - return readStringSlowPath(); |
361 | | - } |
362 | | - |
363 | | - final String readStringSlowPath() throws IOException { |
364 | | - // http://grepcode.com/file_/repository.grepcode.com/java/root/jdk/openjdk/8u40-b25/sun/nio/cs/UTF_8.java/?v=source |
365 | | - // byte => char with support of escape in one pass |
366 | | - int j = 0; |
367 | | - int minimumCapacity = reusableChars.length - 2; |
368 | | - for (; ; ) { |
369 | | - if (j == minimumCapacity) { |
370 | | - char[] newBuf = new char[reusableChars.length * 2]; |
371 | | - System.arraycopy(reusableChars, 0, newBuf, 0, reusableChars.length); |
372 | | - reusableChars = newBuf; |
373 | | - minimumCapacity = reusableChars.length - 2; |
374 | | - } |
375 | | - int b1 = readByte(); |
376 | | - if (b1 >= 0) { |
377 | | - if (b1 == '"') { |
378 | | - return new String(reusableChars, 0, j); |
379 | | - } else if (b1 == '\\') { |
380 | | - int b2 = readByte(); |
381 | | - switch (b2) { |
382 | | - case '"': |
383 | | - reusableChars[j++] = '"'; |
384 | | - break; |
385 | | - case '\\': |
386 | | - reusableChars[j++] = '\\'; |
387 | | - break; |
388 | | - case '/': |
389 | | - reusableChars[j++] = '/'; |
390 | | - break; |
391 | | - case 'b': |
392 | | - reusableChars[j++] = '\b'; |
393 | | - break; |
394 | | - case 'f': |
395 | | - reusableChars[j++] = '\f'; |
396 | | - break; |
397 | | - case 'n': |
398 | | - reusableChars[j++] = '\n'; |
399 | | - break; |
400 | | - case 'r': |
401 | | - reusableChars[j++] = '\r'; |
402 | | - break; |
403 | | - case 't': |
404 | | - reusableChars[j++] = '\t'; |
405 | | - break; |
406 | | - case 'u': |
407 | | - reusableChars[j++] = NumberReader.readU4(this); |
408 | | - break; |
409 | | - default: |
410 | | - throw new RuntimeException("unexpected escape char: " + b2); |
411 | | - } |
412 | | - } else { |
413 | | - // 1 byte, 7 bits: 0xxxxxxx |
414 | | - reusableChars[j++] = (char) b1; |
415 | | - } |
416 | | - } else if ((b1 >> 5) == -2 && (b1 & 0x1e) != 0) { |
417 | | - // 2 bytes, 11 bits: 110xxxxx 10xxxxxx |
418 | | - int b2 = readByte(); |
419 | | - reusableChars[j++] = (char) (((b1 << 6) ^ b2) |
420 | | - ^ |
421 | | - (((byte) 0xC0 << 6) ^ |
422 | | - ((byte) 0x80 << 0))); |
423 | | - } else if ((b1 >> 4) == -2) { |
424 | | - // 3 bytes, 16 bits: 1110xxxx 10xxxxxx 10xxxxxx |
425 | | - int b2 = readByte(); |
426 | | - int b3 = readByte(); |
427 | | - char c = (char) |
428 | | - ((b1 << 12) ^ |
429 | | - (b2 << 6) ^ |
430 | | - (b3 ^ |
431 | | - (((byte) 0xE0 << 12) ^ |
432 | | - ((byte) 0x80 << 6) ^ |
433 | | - ((byte) 0x80 << 0)))); |
434 | | - reusableChars[j++] = c; |
435 | | - } else if ((b1 >> 3) == -2) { |
436 | | - // 4 bytes, 21 bits: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx |
437 | | - int b2 = readByte(); |
438 | | - int b3 = readByte(); |
439 | | - int b4 = readByte(); |
440 | | - int uc = ((b1 << 18) ^ |
441 | | - (b2 << 12) ^ |
442 | | - (b3 << 6) ^ |
443 | | - (b4 ^ |
444 | | - (((byte) 0xF0 << 18) ^ |
445 | | - ((byte) 0x80 << 12) ^ |
446 | | - ((byte) 0x80 << 6) ^ |
447 | | - ((byte) 0x80 << 0)))); |
448 | | - reusableChars[j++] = highSurrogate(uc); |
449 | | - reusableChars[j++] = lowSurrogate(uc); |
450 | | - } else { |
451 | | - throw new RuntimeException("unexpected input"); |
452 | | - } |
453 | | - } |
454 | | - } |
455 | | - |
456 | | - private static char highSurrogate(int codePoint) { |
457 | | - return (char) ((codePoint >>> 10) |
458 | | - + (MIN_HIGH_SURROGATE - (MIN_SUPPLEMENTARY_CODE_POINT >>> 10))); |
| 237 | + return StringReader.readString(this); |
459 | 238 | } |
460 | 239 |
|
461 | | - private static char lowSurrogate(int codePoint) { |
462 | | - return (char) ((codePoint & 0x3ff) + MIN_LOW_SURROGATE); |
| 240 | + public final byte[] readBase64() throws IOException { |
| 241 | + return StringReader.readBase64(this); |
463 | 242 | } |
464 | 243 |
|
465 | 244 | public final String readObject() throws IOException { |
@@ -731,18 +510,6 @@ final int findStringEnd() { |
731 | 510 | return -1; |
732 | 511 | } |
733 | 512 |
|
734 | | - // slice does not allow escape |
735 | | - final int findSliceEnd() { |
736 | | - for (int i = head; i < tail; i++) { |
737 | | - byte c = buf[i]; |
738 | | - if (c == '"') { |
739 | | - return i + 1; |
740 | | - } else if (c == '\\') { |
741 | | - throw reportError("findSliceEnd", "slice does not support escape char"); |
742 | | - } |
743 | | - } |
744 | | - return -1; |
745 | | - } |
746 | 513 |
|
747 | 514 | public ValueType whatIsNext() throws IOException { |
748 | 515 | ValueType valueType = valueTypes[nextToken()]; |
|
0 commit comments