1 package com.fasterxml.jackson.core.json; 2 3 import java.io.*; 4 import java.math.BigDecimal; 5 import java.math.BigInteger; 6 7 import com.fasterxml.jackson.core.*; 8 import com.fasterxml.jackson.core.io.CharTypes; 9 import com.fasterxml.jackson.core.io.CharacterEscapes; 10 import com.fasterxml.jackson.core.io.IOContext; 11 import com.fasterxml.jackson.core.io.NumberOutput; 12 13 /** 14 * {@link JsonGenerator} that outputs JSON content using a {@link java.io.Writer} 15 * which handles character encoding. 16 */ 17 public class WriterBasedJsonGenerator 18 extends JsonGeneratorImpl 19 { 20 final protected static int SHORT_WRITE = 32; 21 22 final protected static char[] HEX_CHARS = CharTypes.copyHexChars(); 23 24 /* 25 /********************************************************** 26 /* Configuration 27 /********************************************************** 28 */ 29 30 final protected Writer _writer; 31 32 /** 33 * Character used for quoting JSON Object property names 34 * and String values. 35 */ 36 protected char _quoteChar; 37 38 /* 39 /********************************************************** 40 /* Output buffering 41 /********************************************************** 42 */ 43 44 /** 45 * Intermediate buffer in which contents are buffered before 46 * being written using {@link #_writer}. 47 */ 48 protected char[] _outputBuffer; 49 50 /** 51 * Pointer to the first buffered character to output 52 */ 53 protected int _outputHead; 54 55 /** 56 * Pointer to the position right beyond the last character to output 57 * (end marker; may point to position right beyond the end of the buffer) 58 */ 59 protected int _outputTail; 60 61 /** 62 * End marker of the output buffer; one past the last valid position 63 * within the buffer. 64 */ 65 protected int _outputEnd; 66 67 /** 68 * Short (14 char) temporary buffer allocated if needed, for constructing 69 * escape sequences 70 */ 71 protected char[] _entityBuffer; 72 73 /** 74 * When custom escapes are used, this member variable is used 75 * internally to hold a reference to currently used escape 76 */ 77 protected SerializableString _currentEscape; 78 79 /** 80 * Intermediate buffer in which characters of a String are copied 81 * before being encoded. 82 * 83 * @since 2.10 84 */ 85 protected char[] _copyBuffer; 86 87 /* 88 /********************************************************** 89 /* Life-cycle 90 /********************************************************** 91 */ 92 93 @Deprecated // since 2.10 WriterBasedJsonGenerator(IOContext ctxt, int features, ObjectCodec codec, Writer w)94 public WriterBasedJsonGenerator(IOContext ctxt, int features, 95 ObjectCodec codec, Writer w) { 96 this(ctxt, features, codec, w, JsonFactory.DEFAULT_QUOTE_CHAR); 97 } 98 99 /** 100 * @since 2.10 101 */ WriterBasedJsonGenerator(IOContext ctxt, int features, ObjectCodec codec, Writer w, char quoteChar)102 public WriterBasedJsonGenerator(IOContext ctxt, int features, 103 ObjectCodec codec, Writer w, 104 char quoteChar) 105 106 { 107 super(ctxt, features, codec); 108 _writer = w; 109 _outputBuffer = ctxt.allocConcatBuffer(); 110 _outputEnd = _outputBuffer.length; 111 _quoteChar = quoteChar; 112 if (quoteChar != '"') { // since 2.10 113 _outputEscapes = CharTypes.get7BitOutputEscapes(quoteChar); 114 } 115 } 116 117 /* 118 /********************************************************** 119 /* Overridden configuration, introspection methods 120 /********************************************************** 121 */ 122 123 @Override getOutputTarget()124 public Object getOutputTarget() { 125 return _writer; 126 } 127 128 @Override getOutputBuffered()129 public int getOutputBuffered() { 130 // Assuming tail and head are kept but... trust and verify: 131 int len = _outputTail - _outputHead; 132 return Math.max(0, len); 133 } 134 135 // json does allow this so 136 @Override canWriteFormattedNumbers()137 public boolean canWriteFormattedNumbers() { return true; } 138 139 /* 140 /********************************************************** 141 /* Overridden methods 142 /********************************************************** 143 */ 144 145 @Override writeFieldName(String name)146 public void writeFieldName(String name) throws IOException 147 { 148 int status = _writeContext.writeFieldName(name); 149 if (status == JsonWriteContext.STATUS_EXPECT_VALUE) { 150 _reportError("Can not write a field name, expecting a value"); 151 } 152 _writeFieldName(name, (status == JsonWriteContext.STATUS_OK_AFTER_COMMA)); 153 } 154 155 @Override writeFieldName(SerializableString name)156 public void writeFieldName(SerializableString name) throws IOException 157 { 158 // Object is a value, need to verify it's allowed 159 int status = _writeContext.writeFieldName(name.getValue()); 160 if (status == JsonWriteContext.STATUS_EXPECT_VALUE) { 161 _reportError("Can not write a field name, expecting a value"); 162 } 163 _writeFieldName(name, (status == JsonWriteContext.STATUS_OK_AFTER_COMMA)); 164 } 165 _writeFieldName(String name, boolean commaBefore)166 protected final void _writeFieldName(String name, boolean commaBefore) throws IOException 167 { 168 if (_cfgPrettyPrinter != null) { 169 _writePPFieldName(name, commaBefore); 170 return; 171 } 172 // for fast+std case, need to output up to 2 chars, comma, dquote 173 if ((_outputTail + 1) >= _outputEnd) { 174 _flushBuffer(); 175 } 176 if (commaBefore) { 177 _outputBuffer[_outputTail++] = ','; 178 } 179 // Alternate mode, in which quoting of field names disabled? 180 if (_cfgUnqNames) { 181 _writeString(name); 182 return; 183 } 184 // we know there's room for at least one more char 185 _outputBuffer[_outputTail++] = _quoteChar; 186 // The beef: 187 _writeString(name); 188 // and closing quotes; need room for one more char: 189 if (_outputTail >= _outputEnd) { 190 _flushBuffer(); 191 } 192 _outputBuffer[_outputTail++] = _quoteChar; 193 } 194 _writeFieldName(SerializableString name, boolean commaBefore)195 protected final void _writeFieldName(SerializableString name, boolean commaBefore) throws IOException 196 { 197 if (_cfgPrettyPrinter != null) { 198 _writePPFieldName(name, commaBefore); 199 return; 200 } 201 // for fast+std case, need to output up to 2 chars, comma, dquote 202 if ((_outputTail + 1) >= _outputEnd) { 203 _flushBuffer(); 204 } 205 if (commaBefore) { 206 _outputBuffer[_outputTail++] = ','; 207 } 208 // Alternate mode, in which quoting of field names disabled? 209 if (_cfgUnqNames) { 210 final char[] ch = name.asQuotedChars(); 211 writeRaw(ch, 0, ch.length); 212 return; 213 } 214 // we know there's room for at least one more char 215 _outputBuffer[_outputTail++] = _quoteChar; 216 // The beef: 217 218 int len = name.appendQuoted(_outputBuffer, _outputTail); 219 if (len < 0) { 220 _writeFieldNameTail(name); 221 return; 222 } 223 _outputTail += len; 224 if (_outputTail >= _outputEnd) { 225 _flushBuffer(); 226 } 227 _outputBuffer[_outputTail++] = _quoteChar; 228 } 229 _writeFieldNameTail(SerializableString name)230 private final void _writeFieldNameTail(SerializableString name) throws IOException 231 { 232 final char[] quoted = name.asQuotedChars(); 233 writeRaw(quoted, 0, quoted.length); 234 if (_outputTail >= _outputEnd) { 235 _flushBuffer(); 236 } 237 _outputBuffer[_outputTail++] = _quoteChar; 238 } 239 240 /* 241 /********************************************************** 242 /* Output method implementations, structural 243 /********************************************************** 244 */ 245 246 @Override writeStartArray()247 public void writeStartArray() throws IOException 248 { 249 _verifyValueWrite("start an array"); 250 _writeContext = _writeContext.createChildArrayContext(); 251 if (_cfgPrettyPrinter != null) { 252 _cfgPrettyPrinter.writeStartArray(this); 253 } else { 254 if (_outputTail >= _outputEnd) { 255 _flushBuffer(); 256 } 257 _outputBuffer[_outputTail++] = '['; 258 } 259 } 260 261 @Override // since 2.12 writeStartArray(Object currentValue)262 public void writeStartArray(Object currentValue) throws IOException 263 { 264 _verifyValueWrite("start an array"); 265 _writeContext = _writeContext.createChildArrayContext(currentValue); 266 if (_cfgPrettyPrinter != null) { 267 _cfgPrettyPrinter.writeStartArray(this); 268 } else { 269 if (_outputTail >= _outputEnd) { 270 _flushBuffer(); 271 } 272 _outputBuffer[_outputTail++] = '['; 273 } 274 } 275 276 @Override // since 2.12 writeStartArray(Object currentValue, int size)277 public void writeStartArray(Object currentValue, int size) throws IOException 278 { 279 _verifyValueWrite("start an array"); 280 _writeContext = _writeContext.createChildArrayContext(currentValue); 281 if (_cfgPrettyPrinter != null) { 282 _cfgPrettyPrinter.writeStartArray(this); 283 } else { 284 if (_outputTail >= _outputEnd) { 285 _flushBuffer(); 286 } 287 _outputBuffer[_outputTail++] = '['; 288 } 289 } 290 291 @Override writeEndArray()292 public void writeEndArray() throws IOException 293 { 294 if (!_writeContext.inArray()) { 295 _reportError("Current context not Array but "+_writeContext.typeDesc()); 296 } 297 if (_cfgPrettyPrinter != null) { 298 _cfgPrettyPrinter.writeEndArray(this, _writeContext.getEntryCount()); 299 } else { 300 if (_outputTail >= _outputEnd) { 301 _flushBuffer(); 302 } 303 _outputBuffer[_outputTail++] = ']'; 304 } 305 _writeContext = _writeContext.clearAndGetParent(); 306 } 307 308 @Override writeStartObject()309 public void writeStartObject() throws IOException 310 { 311 _verifyValueWrite("start an object"); 312 _writeContext = _writeContext.createChildObjectContext(); 313 if (_cfgPrettyPrinter != null) { 314 _cfgPrettyPrinter.writeStartObject(this); 315 } else { 316 if (_outputTail >= _outputEnd) { 317 _flushBuffer(); 318 } 319 _outputBuffer[_outputTail++] = '{'; 320 } 321 } 322 323 @Override // since 2.8 writeStartObject(Object forValue)324 public void writeStartObject(Object forValue) throws IOException 325 { 326 _verifyValueWrite("start an object"); 327 JsonWriteContext ctxt = _writeContext.createChildObjectContext(forValue); 328 _writeContext = ctxt; 329 if (_cfgPrettyPrinter != null) { 330 _cfgPrettyPrinter.writeStartObject(this); 331 } else { 332 if (_outputTail >= _outputEnd) { 333 _flushBuffer(); 334 } 335 _outputBuffer[_outputTail++] = '{'; 336 } 337 } 338 339 @Override writeEndObject()340 public void writeEndObject() throws IOException 341 { 342 if (!_writeContext.inObject()) { 343 _reportError("Current context not Object but "+_writeContext.typeDesc()); 344 } 345 if (_cfgPrettyPrinter != null) { 346 _cfgPrettyPrinter.writeEndObject(this, _writeContext.getEntryCount()); 347 } else { 348 if (_outputTail >= _outputEnd) { 349 _flushBuffer(); 350 } 351 _outputBuffer[_outputTail++] = '}'; 352 } 353 _writeContext = _writeContext.clearAndGetParent(); 354 } 355 356 /** 357 * Specialized version of <code>_writeFieldName</code>, off-lined 358 * to keep the "fast path" as simple (and hopefully fast) as possible. 359 */ _writePPFieldName(String name, boolean commaBefore)360 protected final void _writePPFieldName(String name, boolean commaBefore) throws IOException 361 { 362 if (commaBefore) { 363 _cfgPrettyPrinter.writeObjectEntrySeparator(this); 364 } else { 365 _cfgPrettyPrinter.beforeObjectEntries(this); 366 } 367 368 if (_cfgUnqNames) {// non-standard, omit quotes 369 _writeString(name); 370 } else { 371 if (_outputTail >= _outputEnd) { 372 _flushBuffer(); 373 } 374 _outputBuffer[_outputTail++] = _quoteChar; 375 _writeString(name); 376 if (_outputTail >= _outputEnd) { 377 _flushBuffer(); 378 } 379 _outputBuffer[_outputTail++] = _quoteChar; 380 } 381 } 382 _writePPFieldName(SerializableString name, boolean commaBefore)383 protected final void _writePPFieldName(SerializableString name, boolean commaBefore) throws IOException 384 { 385 if (commaBefore) { 386 _cfgPrettyPrinter.writeObjectEntrySeparator(this); 387 } else { 388 _cfgPrettyPrinter.beforeObjectEntries(this); 389 } 390 final char[] quoted = name.asQuotedChars(); 391 if (_cfgUnqNames) {// non-standard, omit quotes 392 writeRaw(quoted, 0, quoted.length); 393 } else { 394 if (_outputTail >= _outputEnd) { 395 _flushBuffer(); 396 } 397 _outputBuffer[_outputTail++] = _quoteChar; 398 writeRaw(quoted, 0, quoted.length); 399 if (_outputTail >= _outputEnd) { 400 _flushBuffer(); 401 } 402 _outputBuffer[_outputTail++] = _quoteChar; 403 } 404 } 405 406 /* 407 /********************************************************** 408 /* Output method implementations, textual 409 /********************************************************** 410 */ 411 412 @Override writeString(String text)413 public void writeString(String text) throws IOException 414 { 415 _verifyValueWrite(WRITE_STRING); 416 if (text == null) { 417 _writeNull(); 418 return; 419 } 420 if (_outputTail >= _outputEnd) { 421 _flushBuffer(); 422 } 423 _outputBuffer[_outputTail++] = _quoteChar; 424 _writeString(text); 425 // And finally, closing quotes 426 if (_outputTail >= _outputEnd) { 427 _flushBuffer(); 428 } 429 _outputBuffer[_outputTail++] = _quoteChar; 430 } 431 432 @Override writeString(Reader reader, int len)433 public void writeString(Reader reader, int len) throws IOException { 434 _verifyValueWrite(WRITE_STRING); 435 if (reader == null) { 436 _reportError("null reader"); 437 } 438 int toRead = (len >= 0) ? len : Integer.MAX_VALUE; 439 //Add leading quote 440 if ((_outputTail + len) >= _outputEnd) { 441 _flushBuffer(); 442 } 443 _outputBuffer[_outputTail++] = _quoteChar; 444 445 final char[] buf = _allocateCopyBuffer(); 446 //read 447 while (toRead > 0) { 448 int toReadNow = Math.min(toRead, buf.length); 449 int numRead = reader.read(buf, 0, toReadNow); 450 if (numRead <= 0) { 451 break; 452 } 453 if ((_outputTail + len) >= _outputEnd) { 454 _flushBuffer(); 455 } 456 _writeString(buf, 0, numRead); 457 458 //decrease tracker 459 toRead -= numRead; 460 } 461 //Add trailing quote 462 if ((_outputTail + len) >= _outputEnd) { 463 _flushBuffer(); 464 } 465 _outputBuffer[_outputTail++] = _quoteChar; 466 467 if (toRead > 0 && len >= 0) { 468 _reportError("Didn't read enough from reader"); 469 } 470 } 471 472 @Override writeString(char[] text, int offset, int len)473 public void writeString(char[] text, int offset, int len) throws IOException 474 { 475 _verifyValueWrite(WRITE_STRING); 476 if (_outputTail >= _outputEnd) { 477 _flushBuffer(); 478 } 479 _outputBuffer[_outputTail++] = _quoteChar; 480 _writeString(text, offset, len); 481 // And finally, closing quotes 482 if (_outputTail >= _outputEnd) { 483 _flushBuffer(); 484 } 485 _outputBuffer[_outputTail++] = _quoteChar; 486 } 487 488 @Override writeString(SerializableString sstr)489 public void writeString(SerializableString sstr) throws IOException 490 { 491 _verifyValueWrite(WRITE_STRING); 492 if (_outputTail >= _outputEnd) { 493 _flushBuffer(); 494 } 495 _outputBuffer[_outputTail++] = _quoteChar; 496 int len = sstr.appendQuoted(_outputBuffer, _outputTail); 497 if (len < 0) { 498 _writeString2(sstr); 499 return; 500 } 501 _outputTail += len; 502 if (_outputTail >= _outputEnd) { 503 _flushBuffer(); 504 } 505 _outputBuffer[_outputTail++] = _quoteChar; 506 } 507 _writeString2(SerializableString sstr)508 private void _writeString2(SerializableString sstr) throws IOException 509 { 510 // Note: copied from writeRaw: 511 char[] text = sstr.asQuotedChars(); 512 final int len = text.length; 513 if (len < SHORT_WRITE) { 514 int room = _outputEnd - _outputTail; 515 if (len > room) { 516 _flushBuffer(); 517 } 518 System.arraycopy(text, 0, _outputBuffer, _outputTail, len); 519 _outputTail += len; 520 } else { 521 _flushBuffer(); 522 _writer.write(text, 0, len); 523 } 524 if (_outputTail >= _outputEnd) { 525 _flushBuffer(); 526 } 527 _outputBuffer[_outputTail++] = _quoteChar; 528 } 529 530 @Override writeRawUTF8String(byte[] text, int offset, int length)531 public void writeRawUTF8String(byte[] text, int offset, int length) throws IOException { 532 // could add support for buffering if we really want it... 533 _reportUnsupportedOperation(); 534 } 535 536 @Override writeUTF8String(byte[] text, int offset, int length)537 public void writeUTF8String(byte[] text, int offset, int length) throws IOException { 538 // could add support for buffering if we really want it... 539 _reportUnsupportedOperation(); 540 } 541 542 /* 543 /********************************************************** 544 /* Output method implementations, unprocessed ("raw") 545 /********************************************************** 546 */ 547 548 @Override writeRaw(String text)549 public void writeRaw(String text) throws IOException 550 { 551 // Nothing to check, can just output as is 552 int len = text.length(); 553 int room = _outputEnd - _outputTail; 554 555 if (room == 0) { 556 _flushBuffer(); 557 room = _outputEnd - _outputTail; 558 } 559 // But would it nicely fit in? If yes, it's easy 560 if (room >= len) { 561 text.getChars(0, len, _outputBuffer, _outputTail); 562 _outputTail += len; 563 } else { 564 writeRawLong(text); 565 } 566 } 567 568 @Override writeRaw(String text, int start, int len)569 public void writeRaw(String text, int start, int len) throws IOException 570 { 571 // Nothing to check, can just output as is 572 int room = _outputEnd - _outputTail; 573 574 if (room < len) { 575 _flushBuffer(); 576 room = _outputEnd - _outputTail; 577 } 578 // But would it nicely fit in? If yes, it's easy 579 if (room >= len) { 580 text.getChars(start, start+len, _outputBuffer, _outputTail); 581 _outputTail += len; 582 } else { 583 writeRawLong(text.substring(start, start+len)); 584 } 585 } 586 587 // @since 2.1 588 @Override writeRaw(SerializableString text)589 public void writeRaw(SerializableString text) throws IOException { 590 int len = text.appendUnquoted(_outputBuffer, _outputTail); 591 if (len < 0) { 592 writeRaw(text.getValue()); 593 return; 594 } 595 _outputTail += len; 596 } 597 598 @Override writeRaw(char[] text, int offset, int len)599 public void writeRaw(char[] text, int offset, int len) throws IOException 600 { 601 // Only worth buffering if it's a short write? 602 if (len < SHORT_WRITE) { 603 int room = _outputEnd - _outputTail; 604 if (len > room) { 605 _flushBuffer(); 606 } 607 System.arraycopy(text, offset, _outputBuffer, _outputTail, len); 608 _outputTail += len; 609 return; 610 } 611 // Otherwise, better just pass through: 612 _flushBuffer(); 613 _writer.write(text, offset, len); 614 } 615 616 @Override writeRaw(char c)617 public void writeRaw(char c) throws IOException 618 { 619 if (_outputTail >= _outputEnd) { 620 _flushBuffer(); 621 } 622 _outputBuffer[_outputTail++] = c; 623 } 624 writeRawLong(String text)625 private void writeRawLong(String text) throws IOException 626 { 627 int room = _outputEnd - _outputTail; 628 // If not, need to do it by looping 629 text.getChars(0, room, _outputBuffer, _outputTail); 630 _outputTail += room; 631 _flushBuffer(); 632 int offset = room; 633 int len = text.length() - room; 634 635 while (len > _outputEnd) { 636 int amount = _outputEnd; 637 text.getChars(offset, offset+amount, _outputBuffer, 0); 638 _outputHead = 0; 639 _outputTail = amount; 640 _flushBuffer(); 641 offset += amount; 642 len -= amount; 643 } 644 // And last piece (at most length of buffer) 645 text.getChars(offset, offset+len, _outputBuffer, 0); 646 _outputHead = 0; 647 _outputTail = len; 648 } 649 650 /* 651 /********************************************************** 652 /* Output method implementations, base64-encoded binary 653 /********************************************************** 654 */ 655 656 @Override writeBinary(Base64Variant b64variant, byte[] data, int offset, int len)657 public void writeBinary(Base64Variant b64variant, byte[] data, int offset, int len) 658 throws IOException, JsonGenerationException 659 { 660 _verifyValueWrite(WRITE_BINARY); 661 // Starting quotes 662 if (_outputTail >= _outputEnd) { 663 _flushBuffer(); 664 } 665 _outputBuffer[_outputTail++] = _quoteChar; 666 _writeBinary(b64variant, data, offset, offset+len); 667 // and closing quotes 668 if (_outputTail >= _outputEnd) { 669 _flushBuffer(); 670 } 671 _outputBuffer[_outputTail++] = _quoteChar; 672 } 673 674 @Override writeBinary(Base64Variant b64variant, InputStream data, int dataLength)675 public int writeBinary(Base64Variant b64variant, 676 InputStream data, int dataLength) 677 throws IOException, JsonGenerationException 678 { 679 _verifyValueWrite(WRITE_BINARY); 680 // Starting quotes 681 if (_outputTail >= _outputEnd) { 682 _flushBuffer(); 683 } 684 _outputBuffer[_outputTail++] = _quoteChar; 685 byte[] encodingBuffer = _ioContext.allocBase64Buffer(); 686 int bytes; 687 try { 688 if (dataLength < 0) { // length unknown 689 bytes = _writeBinary(b64variant, data, encodingBuffer); 690 } else { 691 int missing = _writeBinary(b64variant, data, encodingBuffer, dataLength); 692 if (missing > 0) { 693 _reportError("Too few bytes available: missing "+missing+" bytes (out of "+dataLength+")"); 694 } 695 bytes = dataLength; 696 } 697 } finally { 698 _ioContext.releaseBase64Buffer(encodingBuffer); 699 } 700 // and closing quotes 701 if (_outputTail >= _outputEnd) { 702 _flushBuffer(); 703 } 704 _outputBuffer[_outputTail++] = _quoteChar; 705 return bytes; 706 } 707 708 /* 709 /********************************************************** 710 /* Output method implementations, primitive 711 /********************************************************** 712 */ 713 714 @Override writeNumber(short s)715 public void writeNumber(short s) throws IOException 716 { 717 _verifyValueWrite(WRITE_NUMBER); 718 if (_cfgNumbersAsStrings) { 719 _writeQuotedShort(s); 720 return; 721 } 722 // up to 5 digits and possible minus sign 723 if ((_outputTail + 6) >= _outputEnd) { 724 _flushBuffer(); 725 } 726 _outputTail = NumberOutput.outputInt(s, _outputBuffer, _outputTail); 727 } 728 _writeQuotedShort(short s)729 private void _writeQuotedShort(short s) throws IOException { 730 if ((_outputTail + 8) >= _outputEnd) { 731 _flushBuffer(); 732 } 733 _outputBuffer[_outputTail++] = _quoteChar; 734 _outputTail = NumberOutput.outputInt(s, _outputBuffer, _outputTail); 735 _outputBuffer[_outputTail++] = _quoteChar; 736 } 737 738 @Override writeNumber(int i)739 public void writeNumber(int i) throws IOException 740 { 741 _verifyValueWrite(WRITE_NUMBER); 742 if (_cfgNumbersAsStrings) { 743 _writeQuotedInt(i); 744 return; 745 } 746 // up to 10 digits and possible minus sign 747 if ((_outputTail + 11) >= _outputEnd) { 748 _flushBuffer(); 749 } 750 _outputTail = NumberOutput.outputInt(i, _outputBuffer, _outputTail); 751 } 752 _writeQuotedInt(int i)753 private void _writeQuotedInt(int i) throws IOException { 754 if ((_outputTail + 13) >= _outputEnd) { 755 _flushBuffer(); 756 } 757 _outputBuffer[_outputTail++] = _quoteChar; 758 _outputTail = NumberOutput.outputInt(i, _outputBuffer, _outputTail); 759 _outputBuffer[_outputTail++] = _quoteChar; 760 } 761 762 @Override writeNumber(long l)763 public void writeNumber(long l) throws IOException 764 { 765 _verifyValueWrite(WRITE_NUMBER); 766 if (_cfgNumbersAsStrings) { 767 _writeQuotedLong(l); 768 return; 769 } 770 if ((_outputTail + 21) >= _outputEnd) { 771 // up to 20 digits, minus sign 772 _flushBuffer(); 773 } 774 _outputTail = NumberOutput.outputLong(l, _outputBuffer, _outputTail); 775 } 776 _writeQuotedLong(long l)777 private void _writeQuotedLong(long l) throws IOException { 778 if ((_outputTail + 23) >= _outputEnd) { 779 _flushBuffer(); 780 } 781 _outputBuffer[_outputTail++] = _quoteChar; 782 _outputTail = NumberOutput.outputLong(l, _outputBuffer, _outputTail); 783 _outputBuffer[_outputTail++] = _quoteChar; 784 } 785 786 // !!! 05-Aug-2008, tatus: Any ways to optimize these? 787 788 @Override writeNumber(BigInteger value)789 public void writeNumber(BigInteger value) throws IOException 790 { 791 _verifyValueWrite(WRITE_NUMBER); 792 if (value == null) { 793 _writeNull(); 794 } else if (_cfgNumbersAsStrings) { 795 _writeQuotedRaw(value.toString()); 796 } else { 797 writeRaw(value.toString()); 798 } 799 } 800 801 @SuppressWarnings("deprecation") 802 @Override writeNumber(double d)803 public void writeNumber(double d) throws IOException 804 { 805 if (_cfgNumbersAsStrings || 806 (NumberOutput.notFinite(d) && isEnabled(Feature.QUOTE_NON_NUMERIC_NUMBERS))) { 807 writeString(String.valueOf(d)); 808 return; 809 } 810 // What is the max length for doubles? 40 chars? 811 _verifyValueWrite(WRITE_NUMBER); 812 writeRaw(String.valueOf(d)); 813 } 814 815 @SuppressWarnings("deprecation") 816 @Override writeNumber(float f)817 public void writeNumber(float f) throws IOException 818 { 819 if (_cfgNumbersAsStrings || 820 (NumberOutput.notFinite(f) && isEnabled(Feature.QUOTE_NON_NUMERIC_NUMBERS))) { 821 writeString(String.valueOf(f)); 822 return; 823 } 824 // What is the max length for floats? 825 _verifyValueWrite(WRITE_NUMBER); 826 writeRaw(String.valueOf(f)); 827 } 828 829 @Override writeNumber(BigDecimal value)830 public void writeNumber(BigDecimal value) throws IOException 831 { 832 // Don't really know max length for big decimal, no point checking 833 _verifyValueWrite(WRITE_NUMBER); 834 if (value == null) { 835 _writeNull(); 836 } else if (_cfgNumbersAsStrings) { 837 _writeQuotedRaw(_asString(value)); 838 } else { 839 writeRaw(_asString(value)); 840 } 841 } 842 843 @Override writeNumber(String encodedValue)844 public void writeNumber(String encodedValue) throws IOException 845 { 846 _verifyValueWrite(WRITE_NUMBER); 847 if (_cfgNumbersAsStrings) { 848 _writeQuotedRaw(encodedValue); 849 } else { 850 writeRaw(encodedValue); 851 } 852 } 853 854 @Override writeNumber(char[] encodedValueBuffer, int offset, int length)855 public void writeNumber(char[] encodedValueBuffer, int offset, int length) throws IOException { 856 _verifyValueWrite(WRITE_NUMBER); 857 if (_cfgNumbersAsStrings) { 858 _writeQuotedRaw(encodedValueBuffer, offset, length); 859 } else { 860 writeRaw(encodedValueBuffer, offset, length); 861 } 862 } 863 _writeQuotedRaw(String value)864 private void _writeQuotedRaw(String value) throws IOException 865 { 866 if (_outputTail >= _outputEnd) { 867 _flushBuffer(); 868 } 869 _outputBuffer[_outputTail++] = _quoteChar; 870 writeRaw(value); 871 if (_outputTail >= _outputEnd) { 872 _flushBuffer(); 873 } 874 _outputBuffer[_outputTail++] = _quoteChar; 875 } 876 _writeQuotedRaw(char[] text, int offset, int length)877 private void _writeQuotedRaw(char[] text, int offset, int length) throws IOException 878 { 879 if (_outputTail >= _outputEnd) { 880 _flushBuffer(); 881 } 882 _outputBuffer[_outputTail++] = _quoteChar; 883 writeRaw(text, offset, length); 884 if (_outputTail >= _outputEnd) { 885 _flushBuffer(); 886 } 887 _outputBuffer[_outputTail++] = _quoteChar; 888 } 889 890 @Override writeBoolean(boolean state)891 public void writeBoolean(boolean state) throws IOException 892 { 893 _verifyValueWrite(WRITE_BOOLEAN); 894 if ((_outputTail + 5) >= _outputEnd) { 895 _flushBuffer(); 896 } 897 int ptr = _outputTail; 898 char[] buf = _outputBuffer; 899 if (state) { 900 buf[ptr] = 't'; 901 buf[++ptr] = 'r'; 902 buf[++ptr] = 'u'; 903 buf[++ptr] = 'e'; 904 } else { 905 buf[ptr] = 'f'; 906 buf[++ptr] = 'a'; 907 buf[++ptr] = 'l'; 908 buf[++ptr] = 's'; 909 buf[++ptr] = 'e'; 910 } 911 _outputTail = ptr+1; 912 } 913 914 @Override writeNull()915 public void writeNull() throws IOException { 916 _verifyValueWrite(WRITE_NULL); 917 _writeNull(); 918 } 919 920 /* 921 /********************************************************** 922 /* Implementations for other methods 923 /********************************************************** 924 */ 925 926 @Override _verifyValueWrite(String typeMsg)927 protected final void _verifyValueWrite(String typeMsg) throws IOException 928 { 929 final int status = _writeContext.writeValue(); 930 if (_cfgPrettyPrinter != null) { 931 // Otherwise, pretty printer knows what to do... 932 _verifyPrettyValueWrite(typeMsg, status); 933 return; 934 } 935 char c; 936 switch (status) { 937 case JsonWriteContext.STATUS_OK_AS_IS: 938 default: 939 return; 940 case JsonWriteContext.STATUS_OK_AFTER_COMMA: 941 c = ','; 942 break; 943 case JsonWriteContext.STATUS_OK_AFTER_COLON: 944 c = ':'; 945 break; 946 case JsonWriteContext.STATUS_OK_AFTER_SPACE: // root-value separator 947 if (_rootValueSeparator != null) { 948 writeRaw(_rootValueSeparator.getValue()); 949 } 950 return; 951 case JsonWriteContext.STATUS_EXPECT_NAME: 952 _reportCantWriteValueExpectName(typeMsg); 953 return; 954 } 955 if (_outputTail >= _outputEnd) { 956 _flushBuffer(); 957 } 958 _outputBuffer[_outputTail++] = c; 959 } 960 961 /* 962 /********************************************************** 963 /* Low-level output handling 964 /********************************************************** 965 */ 966 967 @Override flush()968 public void flush() throws IOException 969 { 970 _flushBuffer(); 971 if (_writer != null) { 972 if (isEnabled(Feature.FLUSH_PASSED_TO_STREAM)) { 973 _writer.flush(); 974 } 975 } 976 } 977 978 @Override close()979 public void close() throws IOException 980 { 981 super.close(); 982 983 /* 05-Dec-2008, tatu: To add [JACKSON-27], need to close open 984 * scopes. 985 */ 986 // First: let's see that we still have buffers... 987 if (_outputBuffer != null 988 && isEnabled(Feature.AUTO_CLOSE_JSON_CONTENT)) { 989 while (true) { 990 JsonStreamContext ctxt = getOutputContext(); 991 if (ctxt.inArray()) { 992 writeEndArray(); 993 } else if (ctxt.inObject()) { 994 writeEndObject(); 995 } else { 996 break; 997 } 998 } 999 } 1000 _flushBuffer(); 1001 _outputHead = 0; 1002 _outputTail = 0; 1003 1004 /* 25-Nov-2008, tatus: As per [JACKSON-16] we are not to call close() 1005 * on the underlying Reader, unless we "own" it, or auto-closing 1006 * feature is enabled. 1007 * One downside: when using UTF8Writer, underlying buffer(s) 1008 * may not be properly recycled if we don't close the writer. 1009 */ 1010 if (_writer != null) { 1011 if (_ioContext.isResourceManaged() || isEnabled(Feature.AUTO_CLOSE_TARGET)) { 1012 _writer.close(); 1013 } else if (isEnabled(Feature.FLUSH_PASSED_TO_STREAM)) { 1014 // If we can't close it, we should at least flush 1015 _writer.flush(); 1016 } 1017 } 1018 // Internal buffer(s) generator has can now be released as well 1019 _releaseBuffers(); 1020 } 1021 1022 @Override _releaseBuffers()1023 protected void _releaseBuffers() 1024 { 1025 char[] buf = _outputBuffer; 1026 if (buf != null) { 1027 _outputBuffer = null; 1028 _ioContext.releaseConcatBuffer(buf); 1029 } 1030 buf = _copyBuffer; 1031 if (buf != null) { 1032 _copyBuffer = null; 1033 _ioContext.releaseNameCopyBuffer(buf); 1034 } 1035 } 1036 1037 /* 1038 /********************************************************** 1039 /* Internal methods, low-level writing; text, default 1040 /********************************************************** 1041 */ 1042 _writeString(String text)1043 private void _writeString(String text) throws IOException 1044 { 1045 /* One check first: if String won't fit in the buffer, let's 1046 * segment writes. No point in extending buffer to huge sizes 1047 * (like if someone wants to include multi-megabyte base64 1048 * encoded stuff or such) 1049 */ 1050 final int len = text.length(); 1051 if (len > _outputEnd) { // Let's reserve space for entity at begin/end 1052 _writeLongString(text); 1053 return; 1054 } 1055 1056 // Ok: we know String will fit in buffer ok 1057 // But do we need to flush first? 1058 if ((_outputTail + len) > _outputEnd) { 1059 _flushBuffer(); 1060 } 1061 text.getChars(0, len, _outputBuffer, _outputTail); 1062 1063 if (_characterEscapes != null) { 1064 _writeStringCustom(len); 1065 } else if (_maximumNonEscapedChar != 0) { 1066 _writeStringASCII(len, _maximumNonEscapedChar); 1067 } else { 1068 _writeString2(len); 1069 } 1070 } 1071 _writeString2(final int len)1072 private void _writeString2(final int len) throws IOException 1073 { 1074 // And then we'll need to verify need for escaping etc: 1075 final int end = _outputTail + len; 1076 final int[] escCodes = _outputEscapes; 1077 final int escLen = escCodes.length; 1078 1079 output_loop: 1080 while (_outputTail < end) { 1081 // Fast loop for chars not needing escaping 1082 escape_loop: 1083 while (true) { 1084 char c = _outputBuffer[_outputTail]; 1085 if (c < escLen && escCodes[c] != 0) { 1086 break escape_loop; 1087 } 1088 if (++_outputTail >= end) { 1089 break output_loop; 1090 } 1091 } 1092 1093 // Ok, bumped into something that needs escaping. 1094 /* First things first: need to flush the buffer. 1095 * Inlined, as we don't want to lose tail pointer 1096 */ 1097 int flushLen = (_outputTail - _outputHead); 1098 if (flushLen > 0) { 1099 _writer.write(_outputBuffer, _outputHead, flushLen); 1100 } 1101 /* In any case, tail will be the new start, so hopefully 1102 * we have room now. 1103 */ 1104 char c = _outputBuffer[_outputTail++]; 1105 _prependOrWriteCharacterEscape(c, escCodes[c]); 1106 } 1107 } 1108 1109 /** 1110 * Method called to write "long strings", strings whose length exceeds 1111 * output buffer length. 1112 */ _writeLongString(String text)1113 private void _writeLongString(String text) throws IOException 1114 { 1115 // First things first: let's flush the buffer to get some more room 1116 _flushBuffer(); 1117 1118 // Then we can write 1119 final int textLen = text.length(); 1120 int offset = 0; 1121 do { 1122 int max = _outputEnd; 1123 int segmentLen = ((offset + max) > textLen) 1124 ? (textLen - offset) : max; 1125 text.getChars(offset, offset+segmentLen, _outputBuffer, 0); 1126 if (_characterEscapes != null) { 1127 _writeSegmentCustom(segmentLen); 1128 } else if (_maximumNonEscapedChar != 0) { 1129 _writeSegmentASCII(segmentLen, _maximumNonEscapedChar); 1130 } else { 1131 _writeSegment(segmentLen); 1132 } 1133 offset += segmentLen; 1134 } while (offset < textLen); 1135 } 1136 1137 /** 1138 * Method called to output textual context which has been copied 1139 * to the output buffer prior to call. If any escaping is needed, 1140 * it will also be handled by the method. 1141 *<p> 1142 * Note: when called, textual content to write is within output 1143 * buffer, right after buffered content (if any). That's why only 1144 * length of that text is passed, as buffer and offset are implied. 1145 */ _writeSegment(int end)1146 private void _writeSegment(int end) throws IOException 1147 { 1148 final int[] escCodes = _outputEscapes; 1149 final int escLen = escCodes.length; 1150 1151 int ptr = 0; 1152 int start = ptr; 1153 1154 output_loop: 1155 while (ptr < end) { 1156 // Fast loop for chars not needing escaping 1157 char c; 1158 while (true) { 1159 c = _outputBuffer[ptr]; 1160 if (c < escLen && escCodes[c] != 0) { 1161 break; 1162 } 1163 if (++ptr >= end) { 1164 break; 1165 } 1166 } 1167 1168 // Ok, bumped into something that needs escaping. 1169 /* First things first: need to flush the buffer. 1170 * Inlined, as we don't want to lose tail pointer 1171 */ 1172 int flushLen = (ptr - start); 1173 if (flushLen > 0) { 1174 _writer.write(_outputBuffer, start, flushLen); 1175 if (ptr >= end) { 1176 break output_loop; 1177 } 1178 } 1179 ++ptr; 1180 // So; either try to prepend (most likely), or write directly: 1181 start = _prependOrWriteCharacterEscape(_outputBuffer, ptr, end, c, escCodes[c]); 1182 } 1183 } 1184 1185 /** 1186 * This method called when the string content is already in 1187 * a char buffer, and need not be copied for processing. 1188 */ _writeString(char[] text, int offset, int len)1189 private void _writeString(char[] text, int offset, int len) throws IOException 1190 { 1191 if (_characterEscapes != null) { 1192 _writeStringCustom(text, offset, len); 1193 return; 1194 } 1195 if (_maximumNonEscapedChar != 0) { 1196 _writeStringASCII(text, offset, len, _maximumNonEscapedChar); 1197 return; 1198 } 1199 1200 // Let's just find longest spans of non-escapable content, and for 1201 // each see if it makes sense to copy them, or write through 1202 1203 len += offset; // -> len marks the end from now on 1204 final int[] escCodes = _outputEscapes; 1205 final int escLen = escCodes.length; 1206 while (offset < len) { 1207 int start = offset; 1208 1209 while (true) { 1210 char c = text[offset]; 1211 if (c < escLen && escCodes[c] != 0) { 1212 break; 1213 } 1214 if (++offset >= len) { 1215 break; 1216 } 1217 } 1218 1219 // Short span? Better just copy it to buffer first: 1220 int newAmount = offset - start; 1221 if (newAmount < SHORT_WRITE) { 1222 // Note: let's reserve room for escaped char (up to 6 chars) 1223 if ((_outputTail + newAmount) > _outputEnd) { 1224 _flushBuffer(); 1225 } 1226 if (newAmount > 0) { 1227 System.arraycopy(text, start, _outputBuffer, _outputTail, newAmount); 1228 _outputTail += newAmount; 1229 } 1230 } else { // Nope: better just write through 1231 _flushBuffer(); 1232 _writer.write(text, start, newAmount); 1233 } 1234 // Was this the end? 1235 if (offset >= len) { // yup 1236 break; 1237 } 1238 // Nope, need to escape the char. 1239 char c = text[offset++]; 1240 _appendCharacterEscape(c, escCodes[c]); 1241 } 1242 } 1243 1244 /* 1245 /********************************************************** 1246 /* Internal methods, low-level writing, text segment 1247 /* with additional escaping (ASCII or such) 1248 /********************************************************** 1249 */ 1250 1251 /* Same as "_writeString2()", except needs additional escaping 1252 * for subset of characters 1253 */ _writeStringASCII(final int len, final int maxNonEscaped)1254 private void _writeStringASCII(final int len, final int maxNonEscaped) 1255 throws IOException, JsonGenerationException 1256 { 1257 // And then we'll need to verify need for escaping etc: 1258 int end = _outputTail + len; 1259 final int[] escCodes = _outputEscapes; 1260 final int escLimit = Math.min(escCodes.length, maxNonEscaped+1); 1261 int escCode = 0; 1262 1263 output_loop: 1264 while (_outputTail < end) { 1265 char c; 1266 // Fast loop for chars not needing escaping 1267 escape_loop: 1268 while (true) { 1269 c = _outputBuffer[_outputTail]; 1270 if (c < escLimit) { 1271 escCode = escCodes[c]; 1272 if (escCode != 0) { 1273 break escape_loop; 1274 } 1275 } else if (c > maxNonEscaped) { 1276 escCode = CharacterEscapes.ESCAPE_STANDARD; 1277 break escape_loop; 1278 } 1279 if (++_outputTail >= end) { 1280 break output_loop; 1281 } 1282 } 1283 int flushLen = (_outputTail - _outputHead); 1284 if (flushLen > 0) { 1285 _writer.write(_outputBuffer, _outputHead, flushLen); 1286 } 1287 ++_outputTail; 1288 _prependOrWriteCharacterEscape(c, escCode); 1289 } 1290 } 1291 _writeSegmentASCII(int end, final int maxNonEscaped)1292 private void _writeSegmentASCII(int end, final int maxNonEscaped) 1293 throws IOException, JsonGenerationException 1294 { 1295 final int[] escCodes = _outputEscapes; 1296 final int escLimit = Math.min(escCodes.length, maxNonEscaped+1); 1297 1298 int ptr = 0; 1299 int escCode = 0; 1300 int start = ptr; 1301 1302 output_loop: 1303 while (ptr < end) { 1304 // Fast loop for chars not needing escaping 1305 char c; 1306 while (true) { 1307 c = _outputBuffer[ptr]; 1308 if (c < escLimit) { 1309 escCode = escCodes[c]; 1310 if (escCode != 0) { 1311 break; 1312 } 1313 } else if (c > maxNonEscaped) { 1314 escCode = CharacterEscapes.ESCAPE_STANDARD; 1315 break; 1316 } 1317 if (++ptr >= end) { 1318 break; 1319 } 1320 } 1321 int flushLen = (ptr - start); 1322 if (flushLen > 0) { 1323 _writer.write(_outputBuffer, start, flushLen); 1324 if (ptr >= end) { 1325 break output_loop; 1326 } 1327 } 1328 ++ptr; 1329 start = _prependOrWriteCharacterEscape(_outputBuffer, ptr, end, c, escCode); 1330 } 1331 } 1332 _writeStringASCII(char[] text, int offset, int len, final int maxNonEscaped)1333 private void _writeStringASCII(char[] text, int offset, int len, 1334 final int maxNonEscaped) 1335 throws IOException, JsonGenerationException 1336 { 1337 len += offset; // -> len marks the end from now on 1338 final int[] escCodes = _outputEscapes; 1339 final int escLimit = Math.min(escCodes.length, maxNonEscaped+1); 1340 1341 int escCode = 0; 1342 1343 while (offset < len) { 1344 int start = offset; 1345 char c; 1346 1347 while (true) { 1348 c = text[offset]; 1349 if (c < escLimit) { 1350 escCode = escCodes[c]; 1351 if (escCode != 0) { 1352 break; 1353 } 1354 } else if (c > maxNonEscaped) { 1355 escCode = CharacterEscapes.ESCAPE_STANDARD; 1356 break; 1357 } 1358 if (++offset >= len) { 1359 break; 1360 } 1361 } 1362 1363 // Short span? Better just copy it to buffer first: 1364 int newAmount = offset - start; 1365 if (newAmount < SHORT_WRITE) { 1366 // Note: let's reserve room for escaped char (up to 6 chars) 1367 if ((_outputTail + newAmount) > _outputEnd) { 1368 _flushBuffer(); 1369 } 1370 if (newAmount > 0) { 1371 System.arraycopy(text, start, _outputBuffer, _outputTail, newAmount); 1372 _outputTail += newAmount; 1373 } 1374 } else { // Nope: better just write through 1375 _flushBuffer(); 1376 _writer.write(text, start, newAmount); 1377 } 1378 // Was this the end? 1379 if (offset >= len) { // yup 1380 break; 1381 } 1382 // Nope, need to escape the char. 1383 ++offset; 1384 _appendCharacterEscape(c, escCode); 1385 } 1386 } 1387 1388 /* 1389 /********************************************************** 1390 /* Internal methods, low-level writing, text segment 1391 /* with custom escaping (possibly coupling with ASCII limits) 1392 /********************************************************** 1393 */ 1394 1395 /* Same as "_writeString2()", except needs additional escaping 1396 * for subset of characters 1397 */ _writeStringCustom(final int len)1398 private void _writeStringCustom(final int len) 1399 throws IOException, JsonGenerationException 1400 { 1401 // And then we'll need to verify need for escaping etc: 1402 int end = _outputTail + len; 1403 final int[] escCodes = _outputEscapes; 1404 final int maxNonEscaped = (_maximumNonEscapedChar < 1) ? 0xFFFF : _maximumNonEscapedChar; 1405 final int escLimit = Math.min(escCodes.length, maxNonEscaped+1); 1406 int escCode = 0; 1407 final CharacterEscapes customEscapes = _characterEscapes; 1408 1409 output_loop: 1410 while (_outputTail < end) { 1411 char c; 1412 // Fast loop for chars not needing escaping 1413 escape_loop: 1414 while (true) { 1415 c = _outputBuffer[_outputTail]; 1416 if (c < escLimit) { 1417 escCode = escCodes[c]; 1418 if (escCode != 0) { 1419 break escape_loop; 1420 } 1421 } else if (c > maxNonEscaped) { 1422 escCode = CharacterEscapes.ESCAPE_STANDARD; 1423 break escape_loop; 1424 } else { 1425 if ((_currentEscape = customEscapes.getEscapeSequence(c)) != null) { 1426 escCode = CharacterEscapes.ESCAPE_CUSTOM; 1427 break escape_loop; 1428 } 1429 } 1430 if (++_outputTail >= end) { 1431 break output_loop; 1432 } 1433 } 1434 int flushLen = (_outputTail - _outputHead); 1435 if (flushLen > 0) { 1436 _writer.write(_outputBuffer, _outputHead, flushLen); 1437 } 1438 ++_outputTail; 1439 _prependOrWriteCharacterEscape(c, escCode); 1440 } 1441 } 1442 _writeSegmentCustom(int end)1443 private void _writeSegmentCustom(int end) 1444 throws IOException, JsonGenerationException 1445 { 1446 final int[] escCodes = _outputEscapes; 1447 final int maxNonEscaped = (_maximumNonEscapedChar < 1) ? 0xFFFF : _maximumNonEscapedChar; 1448 final int escLimit = Math.min(escCodes.length, maxNonEscaped+1); 1449 final CharacterEscapes customEscapes = _characterEscapes; 1450 1451 int ptr = 0; 1452 int escCode = 0; 1453 int start = ptr; 1454 1455 output_loop: 1456 while (ptr < end) { 1457 // Fast loop for chars not needing escaping 1458 char c; 1459 while (true) { 1460 c = _outputBuffer[ptr]; 1461 if (c < escLimit) { 1462 escCode = escCodes[c]; 1463 if (escCode != 0) { 1464 break; 1465 } 1466 } else if (c > maxNonEscaped) { 1467 escCode = CharacterEscapes.ESCAPE_STANDARD; 1468 break; 1469 } else { 1470 if ((_currentEscape = customEscapes.getEscapeSequence(c)) != null) { 1471 escCode = CharacterEscapes.ESCAPE_CUSTOM; 1472 break; 1473 } 1474 } 1475 if (++ptr >= end) { 1476 break; 1477 } 1478 } 1479 int flushLen = (ptr - start); 1480 if (flushLen > 0) { 1481 _writer.write(_outputBuffer, start, flushLen); 1482 if (ptr >= end) { 1483 break output_loop; 1484 } 1485 } 1486 ++ptr; 1487 start = _prependOrWriteCharacterEscape(_outputBuffer, ptr, end, c, escCode); 1488 } 1489 } 1490 _writeStringCustom(char[] text, int offset, int len)1491 private void _writeStringCustom(char[] text, int offset, int len) 1492 throws IOException, JsonGenerationException 1493 { 1494 len += offset; // -> len marks the end from now on 1495 final int[] escCodes = _outputEscapes; 1496 final int maxNonEscaped = (_maximumNonEscapedChar < 1) ? 0xFFFF : _maximumNonEscapedChar; 1497 final int escLimit = Math.min(escCodes.length, maxNonEscaped+1); 1498 final CharacterEscapes customEscapes = _characterEscapes; 1499 1500 int escCode = 0; 1501 1502 while (offset < len) { 1503 int start = offset; 1504 char c; 1505 1506 while (true) { 1507 c = text[offset]; 1508 if (c < escLimit) { 1509 escCode = escCodes[c]; 1510 if (escCode != 0) { 1511 break; 1512 } 1513 } else if (c > maxNonEscaped) { 1514 escCode = CharacterEscapes.ESCAPE_STANDARD; 1515 break; 1516 } else { 1517 if ((_currentEscape = customEscapes.getEscapeSequence(c)) != null) { 1518 escCode = CharacterEscapes.ESCAPE_CUSTOM; 1519 break; 1520 } 1521 } 1522 if (++offset >= len) { 1523 break; 1524 } 1525 } 1526 1527 // Short span? Better just copy it to buffer first: 1528 int newAmount = offset - start; 1529 if (newAmount < SHORT_WRITE) { 1530 // Note: let's reserve room for escaped char (up to 6 chars) 1531 if ((_outputTail + newAmount) > _outputEnd) { 1532 _flushBuffer(); 1533 } 1534 if (newAmount > 0) { 1535 System.arraycopy(text, start, _outputBuffer, _outputTail, newAmount); 1536 _outputTail += newAmount; 1537 } 1538 } else { // Nope: better just write through 1539 _flushBuffer(); 1540 _writer.write(text, start, newAmount); 1541 } 1542 // Was this the end? 1543 if (offset >= len) { // yup 1544 break; 1545 } 1546 // Nope, need to escape the char. 1547 ++offset; 1548 _appendCharacterEscape(c, escCode); 1549 } 1550 } 1551 1552 /* 1553 /********************************************************** 1554 /* Internal methods, low-level writing; binary 1555 /********************************************************** 1556 */ 1557 _writeBinary(Base64Variant b64variant, byte[] input, int inputPtr, final int inputEnd)1558 protected final void _writeBinary(Base64Variant b64variant, byte[] input, int inputPtr, final int inputEnd) 1559 throws IOException, JsonGenerationException 1560 { 1561 // Encoding is by chunks of 3 input, 4 output chars, so: 1562 int safeInputEnd = inputEnd - 3; 1563 // Let's also reserve room for possible (and quoted) lf char each round 1564 int safeOutputEnd = _outputEnd - 6; 1565 int chunksBeforeLF = b64variant.getMaxLineLength() >> 2; 1566 1567 // Ok, first we loop through all full triplets of data: 1568 while (inputPtr <= safeInputEnd) { 1569 if (_outputTail > safeOutputEnd) { // need to flush 1570 _flushBuffer(); 1571 } 1572 // First, mash 3 bytes into lsb of 32-bit int 1573 int b24 = ((int) input[inputPtr++]) << 8; 1574 b24 |= ((int) input[inputPtr++]) & 0xFF; 1575 b24 = (b24 << 8) | (((int) input[inputPtr++]) & 0xFF); 1576 _outputTail = b64variant.encodeBase64Chunk(b24, _outputBuffer, _outputTail); 1577 if (--chunksBeforeLF <= 0) { 1578 // note: must quote in JSON value 1579 _outputBuffer[_outputTail++] = '\\'; 1580 _outputBuffer[_outputTail++] = 'n'; 1581 chunksBeforeLF = b64variant.getMaxLineLength() >> 2; 1582 } 1583 } 1584 1585 // And then we may have 1 or 2 leftover bytes to encode 1586 int inputLeft = inputEnd - inputPtr; // 0, 1 or 2 1587 if (inputLeft > 0) { // yes, but do we have room for output? 1588 if (_outputTail > safeOutputEnd) { // don't really need 6 bytes but... 1589 _flushBuffer(); 1590 } 1591 int b24 = ((int) input[inputPtr++]) << 16; 1592 if (inputLeft == 2) { 1593 b24 |= (((int) input[inputPtr++]) & 0xFF) << 8; 1594 } 1595 _outputTail = b64variant.encodeBase64Partial(b24, inputLeft, _outputBuffer, _outputTail); 1596 } 1597 } 1598 1599 // write-method called when length is definitely known _writeBinary(Base64Variant b64variant, InputStream data, byte[] readBuffer, int bytesLeft)1600 protected final int _writeBinary(Base64Variant b64variant, 1601 InputStream data, byte[] readBuffer, int bytesLeft) 1602 throws IOException, JsonGenerationException 1603 { 1604 int inputPtr = 0; 1605 int inputEnd = 0; 1606 int lastFullOffset = -3; 1607 1608 // Let's also reserve room for possible (and quoted) lf char each round 1609 int safeOutputEnd = _outputEnd - 6; 1610 int chunksBeforeLF = b64variant.getMaxLineLength() >> 2; 1611 1612 while (bytesLeft > 2) { // main loop for full triplets 1613 if (inputPtr > lastFullOffset) { 1614 inputEnd = _readMore(data, readBuffer, inputPtr, inputEnd, bytesLeft); 1615 inputPtr = 0; 1616 if (inputEnd < 3) { // required to try to read to have at least 3 bytes 1617 break; 1618 } 1619 lastFullOffset = inputEnd-3; 1620 } 1621 if (_outputTail > safeOutputEnd) { // need to flush 1622 _flushBuffer(); 1623 } 1624 int b24 = ((int) readBuffer[inputPtr++]) << 8; 1625 b24 |= ((int) readBuffer[inputPtr++]) & 0xFF; 1626 b24 = (b24 << 8) | (((int) readBuffer[inputPtr++]) & 0xFF); 1627 bytesLeft -= 3; 1628 _outputTail = b64variant.encodeBase64Chunk(b24, _outputBuffer, _outputTail); 1629 if (--chunksBeforeLF <= 0) { 1630 _outputBuffer[_outputTail++] = '\\'; 1631 _outputBuffer[_outputTail++] = 'n'; 1632 chunksBeforeLF = b64variant.getMaxLineLength() >> 2; 1633 } 1634 } 1635 1636 // And then we may have 1 or 2 leftover bytes to encode 1637 if (bytesLeft > 0) { 1638 inputEnd = _readMore(data, readBuffer, inputPtr, inputEnd, bytesLeft); 1639 inputPtr = 0; 1640 if (inputEnd > 0) { // yes, but do we have room for output? 1641 if (_outputTail > safeOutputEnd) { // don't really need 6 bytes but... 1642 _flushBuffer(); 1643 } 1644 int b24 = ((int) readBuffer[inputPtr++]) << 16; 1645 int amount; 1646 if (inputPtr < inputEnd) { 1647 b24 |= (((int) readBuffer[inputPtr]) & 0xFF) << 8; 1648 amount = 2; 1649 } else { 1650 amount = 1; 1651 } 1652 _outputTail = b64variant.encodeBase64Partial(b24, amount, _outputBuffer, _outputTail); 1653 bytesLeft -= amount; 1654 } 1655 } 1656 return bytesLeft; 1657 } 1658 1659 // write method when length is unknown _writeBinary(Base64Variant b64variant, InputStream data, byte[] readBuffer)1660 protected final int _writeBinary(Base64Variant b64variant, 1661 InputStream data, byte[] readBuffer) 1662 throws IOException, JsonGenerationException 1663 { 1664 int inputPtr = 0; 1665 int inputEnd = 0; 1666 int lastFullOffset = -3; 1667 int bytesDone = 0; 1668 1669 // Let's also reserve room for possible (and quoted) LF char each round 1670 int safeOutputEnd = _outputEnd - 6; 1671 int chunksBeforeLF = b64variant.getMaxLineLength() >> 2; 1672 1673 // Ok, first we loop through all full triplets of data: 1674 while (true) { 1675 if (inputPtr > lastFullOffset) { // need to load more 1676 inputEnd = _readMore(data, readBuffer, inputPtr, inputEnd, readBuffer.length); 1677 inputPtr = 0; 1678 if (inputEnd < 3) { // required to try to read to have at least 3 bytes 1679 break; 1680 } 1681 lastFullOffset = inputEnd-3; 1682 } 1683 if (_outputTail > safeOutputEnd) { // need to flush 1684 _flushBuffer(); 1685 } 1686 // First, mash 3 bytes into lsb of 32-bit int 1687 int b24 = ((int) readBuffer[inputPtr++]) << 8; 1688 b24 |= ((int) readBuffer[inputPtr++]) & 0xFF; 1689 b24 = (b24 << 8) | (((int) readBuffer[inputPtr++]) & 0xFF); 1690 bytesDone += 3; 1691 _outputTail = b64variant.encodeBase64Chunk(b24, _outputBuffer, _outputTail); 1692 if (--chunksBeforeLF <= 0) { 1693 _outputBuffer[_outputTail++] = '\\'; 1694 _outputBuffer[_outputTail++] = 'n'; 1695 chunksBeforeLF = b64variant.getMaxLineLength() >> 2; 1696 } 1697 } 1698 1699 // And then we may have 1 or 2 leftover bytes to encode 1700 if (inputPtr < inputEnd) { // yes, but do we have room for output? 1701 if (_outputTail > safeOutputEnd) { // don't really need 6 bytes but... 1702 _flushBuffer(); 1703 } 1704 int b24 = ((int) readBuffer[inputPtr++]) << 16; 1705 int amount = 1; 1706 if (inputPtr < inputEnd) { 1707 b24 |= (((int) readBuffer[inputPtr]) & 0xFF) << 8; 1708 amount = 2; 1709 } 1710 bytesDone += amount; 1711 _outputTail = b64variant.encodeBase64Partial(b24, amount, _outputBuffer, _outputTail); 1712 } 1713 return bytesDone; 1714 } 1715 _readMore(InputStream in, byte[] readBuffer, int inputPtr, int inputEnd, int maxRead)1716 private int _readMore(InputStream in, 1717 byte[] readBuffer, int inputPtr, int inputEnd, 1718 int maxRead) throws IOException 1719 { 1720 // anything to shift to front? 1721 int i = 0; 1722 while (inputPtr < inputEnd) { 1723 readBuffer[i++] = readBuffer[inputPtr++]; 1724 } 1725 inputPtr = 0; 1726 inputEnd = i; 1727 maxRead = Math.min(maxRead, readBuffer.length); 1728 1729 do { 1730 int length = maxRead - inputEnd; 1731 if (length == 0) { 1732 break; 1733 } 1734 int count = in.read(readBuffer, inputEnd, length); 1735 if (count < 0) { 1736 return inputEnd; 1737 } 1738 inputEnd += count; 1739 } while (inputEnd < 3); 1740 return inputEnd; 1741 } 1742 1743 /* 1744 /********************************************************** 1745 /* Internal methods, low-level writing, other 1746 /********************************************************** 1747 */ 1748 _writeNull()1749 private final void _writeNull() throws IOException 1750 { 1751 if ((_outputTail + 4) >= _outputEnd) { 1752 _flushBuffer(); 1753 } 1754 int ptr = _outputTail; 1755 char[] buf = _outputBuffer; 1756 buf[ptr] = 'n'; 1757 buf[++ptr] = 'u'; 1758 buf[++ptr] = 'l'; 1759 buf[++ptr] = 'l'; 1760 _outputTail = ptr+1; 1761 } 1762 1763 /* 1764 /********************************************************** 1765 /* Internal methods, low-level writing, escapes 1766 /********************************************************** 1767 */ 1768 1769 /** 1770 * Method called to try to either prepend character escape at front of 1771 * given buffer; or if not possible, to write it out directly. 1772 * Uses head and tail pointers (and updates as necessary) 1773 */ _prependOrWriteCharacterEscape(char ch, int escCode)1774 private void _prependOrWriteCharacterEscape(char ch, int escCode) 1775 throws IOException, JsonGenerationException 1776 { 1777 if (escCode >= 0) { // \\N (2 char) 1778 if (_outputTail >= 2) { // fits, just prepend 1779 int ptr = _outputTail - 2; 1780 _outputHead = ptr; 1781 _outputBuffer[ptr++] = '\\'; 1782 _outputBuffer[ptr] = (char) escCode; 1783 return; 1784 } 1785 // won't fit, write 1786 char[] buf = _entityBuffer; 1787 if (buf == null) { 1788 buf = _allocateEntityBuffer(); 1789 } 1790 _outputHead = _outputTail; 1791 buf[1] = (char) escCode; 1792 _writer.write(buf, 0, 2); 1793 return; 1794 } 1795 if (escCode != CharacterEscapes.ESCAPE_CUSTOM) { // std, \\uXXXX 1796 if (_outputTail >= 6) { // fits, prepend to buffer 1797 char[] buf = _outputBuffer; 1798 int ptr = _outputTail - 6; 1799 _outputHead = ptr; 1800 buf[ptr] = '\\'; 1801 buf[++ptr] = 'u'; 1802 // We know it's a control char, so only the last 2 chars are non-0 1803 if (ch > 0xFF) { // beyond 8 bytes 1804 int hi = (ch >> 8) & 0xFF; 1805 buf[++ptr] = HEX_CHARS[hi >> 4]; 1806 buf[++ptr] = HEX_CHARS[hi & 0xF]; 1807 ch &= 0xFF; 1808 } else { 1809 buf[++ptr] = '0'; 1810 buf[++ptr] = '0'; 1811 } 1812 buf[++ptr] = HEX_CHARS[ch >> 4]; 1813 buf[++ptr] = HEX_CHARS[ch & 0xF]; 1814 return; 1815 } 1816 // won't fit, flush and write 1817 char[] buf = _entityBuffer; 1818 if (buf == null) { 1819 buf = _allocateEntityBuffer(); 1820 } 1821 _outputHead = _outputTail; 1822 if (ch > 0xFF) { // beyond 8 bytes 1823 int hi = (ch >> 8) & 0xFF; 1824 int lo = ch & 0xFF; 1825 buf[10] = HEX_CHARS[hi >> 4]; 1826 buf[11] = HEX_CHARS[hi & 0xF]; 1827 buf[12] = HEX_CHARS[lo >> 4]; 1828 buf[13] = HEX_CHARS[lo & 0xF]; 1829 _writer.write(buf, 8, 6); 1830 } else { // We know it's a control char, so only the last 2 chars are non-0 1831 buf[6] = HEX_CHARS[ch >> 4]; 1832 buf[7] = HEX_CHARS[ch & 0xF]; 1833 _writer.write(buf, 2, 6); 1834 } 1835 return; 1836 } 1837 String escape; 1838 1839 if (_currentEscape == null) { 1840 escape = _characterEscapes.getEscapeSequence(ch).getValue(); 1841 } else { 1842 escape = _currentEscape.getValue(); 1843 _currentEscape = null; 1844 } 1845 int len = escape.length(); 1846 if (_outputTail >= len) { // fits in, prepend 1847 int ptr = _outputTail - len; 1848 _outputHead = ptr; 1849 escape.getChars(0, len, _outputBuffer, ptr); 1850 return; 1851 } 1852 // won't fit, write separately 1853 _outputHead = _outputTail; 1854 _writer.write(escape); 1855 } 1856 1857 /** 1858 * Method called to try to either prepend character escape at front of 1859 * given buffer; or if not possible, to write it out directly. 1860 * 1861 * @return Pointer to start of prepended entity (if prepended); or 'ptr' 1862 * if not. 1863 */ _prependOrWriteCharacterEscape(char[] buffer, int ptr, int end, char ch, int escCode)1864 private int _prependOrWriteCharacterEscape(char[] buffer, int ptr, int end, 1865 char ch, int escCode) 1866 throws IOException, JsonGenerationException 1867 { 1868 if (escCode >= 0) { // \\N (2 char) 1869 if (ptr > 1 && ptr < end) { // fits, just prepend 1870 ptr -= 2; 1871 buffer[ptr] = '\\'; 1872 buffer[ptr+1] = (char) escCode; 1873 } else { // won't fit, write 1874 char[] ent = _entityBuffer; 1875 if (ent == null) { 1876 ent = _allocateEntityBuffer(); 1877 } 1878 ent[1] = (char) escCode; 1879 _writer.write(ent, 0, 2); 1880 } 1881 return ptr; 1882 } 1883 if (escCode != CharacterEscapes.ESCAPE_CUSTOM) { // std, \\uXXXX 1884 if (ptr > 5 && ptr < end) { // fits, prepend to buffer 1885 ptr -= 6; 1886 buffer[ptr++] = '\\'; 1887 buffer[ptr++] = 'u'; 1888 // We know it's a control char, so only the last 2 chars are non-0 1889 if (ch > 0xFF) { // beyond 8 bytes 1890 int hi = (ch >> 8) & 0xFF; 1891 buffer[ptr++] = HEX_CHARS[hi >> 4]; 1892 buffer[ptr++] = HEX_CHARS[hi & 0xF]; 1893 ch &= 0xFF; 1894 } else { 1895 buffer[ptr++] = '0'; 1896 buffer[ptr++] = '0'; 1897 } 1898 buffer[ptr++] = HEX_CHARS[ch >> 4]; 1899 buffer[ptr] = HEX_CHARS[ch & 0xF]; 1900 ptr -= 5; 1901 } else { 1902 // won't fit, flush and write 1903 char[] ent = _entityBuffer; 1904 if (ent == null) { 1905 ent = _allocateEntityBuffer(); 1906 } 1907 _outputHead = _outputTail; 1908 if (ch > 0xFF) { // beyond 8 bytes 1909 int hi = (ch >> 8) & 0xFF; 1910 int lo = ch & 0xFF; 1911 ent[10] = HEX_CHARS[hi >> 4]; 1912 ent[11] = HEX_CHARS[hi & 0xF]; 1913 ent[12] = HEX_CHARS[lo >> 4]; 1914 ent[13] = HEX_CHARS[lo & 0xF]; 1915 _writer.write(ent, 8, 6); 1916 } else { // We know it's a control char, so only the last 2 chars are non-0 1917 ent[6] = HEX_CHARS[ch >> 4]; 1918 ent[7] = HEX_CHARS[ch & 0xF]; 1919 _writer.write(ent, 2, 6); 1920 } 1921 } 1922 return ptr; 1923 } 1924 String escape; 1925 if (_currentEscape == null) { 1926 escape = _characterEscapes.getEscapeSequence(ch).getValue(); 1927 } else { 1928 escape = _currentEscape.getValue(); 1929 _currentEscape = null; 1930 } 1931 int len = escape.length(); 1932 if (ptr >= len && ptr < end) { // fits in, prepend 1933 ptr -= len; 1934 escape.getChars(0, len, buffer, ptr); 1935 } else { // won't fit, write separately 1936 _writer.write(escape); 1937 } 1938 return ptr; 1939 } 1940 1941 /** 1942 * Method called to append escape sequence for given character, at the 1943 * end of standard output buffer; or if not possible, write out directly. 1944 */ _appendCharacterEscape(char ch, int escCode)1945 private void _appendCharacterEscape(char ch, int escCode) 1946 throws IOException, JsonGenerationException 1947 { 1948 if (escCode >= 0) { // \\N (2 char) 1949 if ((_outputTail + 2) > _outputEnd) { 1950 _flushBuffer(); 1951 } 1952 _outputBuffer[_outputTail++] = '\\'; 1953 _outputBuffer[_outputTail++] = (char) escCode; 1954 return; 1955 } 1956 if (escCode != CharacterEscapes.ESCAPE_CUSTOM) { // std, \\uXXXX 1957 if ((_outputTail + 5) >= _outputEnd) { 1958 _flushBuffer(); 1959 } 1960 int ptr = _outputTail; 1961 char[] buf = _outputBuffer; 1962 buf[ptr++] = '\\'; 1963 buf[ptr++] = 'u'; 1964 // We know it's a control char, so only the last 2 chars are non-0 1965 if (ch > 0xFF) { // beyond 8 bytes 1966 int hi = (ch >> 8) & 0xFF; 1967 buf[ptr++] = HEX_CHARS[hi >> 4]; 1968 buf[ptr++] = HEX_CHARS[hi & 0xF]; 1969 ch &= 0xFF; 1970 } else { 1971 buf[ptr++] = '0'; 1972 buf[ptr++] = '0'; 1973 } 1974 buf[ptr++] = HEX_CHARS[ch >> 4]; 1975 buf[ptr++] = HEX_CHARS[ch & 0xF]; 1976 _outputTail = ptr; 1977 return; 1978 } 1979 String escape; 1980 if (_currentEscape == null) { 1981 escape = _characterEscapes.getEscapeSequence(ch).getValue(); 1982 } else { 1983 escape = _currentEscape.getValue(); 1984 _currentEscape = null; 1985 } 1986 int len = escape.length(); 1987 if ((_outputTail + len) > _outputEnd) { 1988 _flushBuffer(); 1989 if (len > _outputEnd) { // very very long escape; unlikely but theoretically possible 1990 _writer.write(escape); 1991 return; 1992 } 1993 } 1994 escape.getChars(0, len, _outputBuffer, _outputTail); 1995 _outputTail += len; 1996 } 1997 _allocateEntityBuffer()1998 private char[] _allocateEntityBuffer() 1999 { 2000 char[] buf = new char[14]; 2001 // first 2 chars, non-numeric escapes (like \n) 2002 buf[0] = '\\'; 2003 // next 6; 8-bit escapes (control chars mostly) 2004 buf[2] = '\\'; 2005 buf[3] = 'u'; 2006 buf[4] = '0'; 2007 buf[5] = '0'; 2008 // last 6, beyond 8 bits 2009 buf[8] = '\\'; 2010 buf[9] = 'u'; 2011 _entityBuffer = buf; 2012 return buf; 2013 } 2014 2015 /** 2016 * @since 2.9 2017 */ _allocateCopyBuffer()2018 private char[] _allocateCopyBuffer() { 2019 if (_copyBuffer == null) { 2020 _copyBuffer = _ioContext.allocNameCopyBuffer(2000); 2021 } 2022 return _copyBuffer; 2023 } 2024 _flushBuffer()2025 protected void _flushBuffer() throws IOException 2026 { 2027 int len = _outputTail - _outputHead; 2028 if (len > 0) { 2029 int offset = _outputHead; 2030 _outputTail = _outputHead = 0; 2031 _writer.write(_outputBuffer, offset, len); 2032 } 2033 } 2034 } 2035