1 // © 2016 and later: Unicode, Inc. and others. 2 // License & terms of use: http://www.unicode.org/copyright.html 3 /** 4 ******************************************************************************* 5 * Copyright (C) 2006-2014, International Business Machines Corporation and * 6 * others. All Rights Reserved. * 7 ******************************************************************************* 8 * 9 ******************************************************************************* 10 */ 11 12 package com.ibm.icu.charset; 13 14 import java.nio.ByteBuffer; 15 import java.nio.CharBuffer; 16 import java.nio.IntBuffer; 17 import java.nio.charset.CharsetDecoder; 18 import java.nio.charset.CoderResult; 19 import java.nio.charset.CodingErrorAction; 20 21 import com.ibm.icu.impl.Assert; 22 23 /** 24 * An abstract class that provides framework methods of decoding operations for concrete 25 * subclasses. 26 * In the future this class will contain API that will implement converter semantics of ICU4C. 27 * @stable ICU 3.6 28 */ 29 public abstract class CharsetDecoderICU extends CharsetDecoder{ 30 31 int toUnicodeStatus; 32 byte[] toUBytesArray = new byte[128]; 33 int toUBytesBegin = 0; 34 int toULength; 35 char[] charErrorBufferArray = new char[128]; 36 int charErrorBufferLength; 37 int charErrorBufferBegin; 38 char[] invalidCharBuffer = new char[128]; 39 int invalidCharLength; 40 41 /** 42 * Maximum number of indexed bytes 43 * @internal 44 * @deprecated This API is ICU internal only. 45 */ 46 @Deprecated 47 protected static final int EXT_MAX_BYTES = 0x1f; 48 49 /* store previous UChars/chars to continue partial matches */ 50 byte[] preToUArray = new byte[EXT_MAX_BYTES]; 51 int preToUBegin; 52 int preToULength; /* negative: replay */ 53 int preToUFirstLength; /* length of first character */ 54 int mode; 55 56 Object toUContext = null; 57 private CharsetCallback.Decoder onUnmappableCharacter = CharsetCallback.TO_U_CALLBACK_STOP; 58 private CharsetCallback.Decoder onMalformedInput = CharsetCallback.TO_U_CALLBACK_STOP; 59 CharsetCallback.Decoder toCharErrorBehaviour = new CharsetCallback.Decoder() { 60 @Override 61 public CoderResult call(CharsetDecoderICU decoder, Object context, ByteBuffer source, 62 CharBuffer target, IntBuffer offsets, char[] buffer, int length, CoderResult cr) { 63 if (cr.isUnmappable()) { 64 return onUnmappableCharacter.call(decoder, context, source, target, offsets, buffer, 65 length, cr); 66 } else /* if (cr.isMalformed()) */ { 67 return onMalformedInput.call(decoder, context, source, target, offsets, buffer, 68 length, cr); 69 } 70 // return CharsetCallback.TO_U_CALLBACK_STOP.call(decoder, context, source, target, offsets, buffer, length, cr); 71 } 72 }; 73 74 // exist to keep implOnMalformedInput and implOnUnmappableInput from being too recursive 75 private boolean malformedInputCalled = false; 76 private boolean unmappableCharacterCalled = false; 77 78 /* 79 * Construct a CharsetDecorderICU based on the information provided from a CharsetICU object. 80 * 81 * @param cs The CharsetICU object containing information about how to charset to decode. 82 */ CharsetDecoderICU(CharsetICU cs)83 CharsetDecoderICU(CharsetICU cs) { 84 super(cs, (1/cs.maxCharsPerByte), cs.maxCharsPerByte); 85 } 86 87 /* 88 * Is this Decoder allowed to use fallbacks? A fallback mapping is a mapping 89 * that will convert a byte sequence to a Unicode codepoint sequence, but 90 * the encoded Unicode codepoint sequence will round trip convert to a different 91 * byte sequence. In ICU, this is can be called a reverse fallback. 92 * @return A boolean 93 */ isFallbackUsed()94 final boolean isFallbackUsed() { 95 return true; 96 } 97 98 /** 99 * Fallback is currently always used by icu4j decoders. 100 */ isToUUseFallback()101 static final boolean isToUUseFallback() { 102 return isToUUseFallback(true); 103 } 104 105 /** 106 * Fallback is currently always used by icu4j decoders. 107 */ isToUUseFallback(boolean iUseFallback)108 static final boolean isToUUseFallback(boolean iUseFallback) { 109 return true; 110 } 111 112 /** 113 * Sets the action to be taken if an illegal sequence is encountered 114 * 115 * @param newAction action to be taken 116 * @exception IllegalArgumentException 117 * @stable ICU 3.6 118 */ 119 @Override implOnMalformedInput(CodingErrorAction newAction)120 protected final void implOnMalformedInput(CodingErrorAction newAction) { 121 // don't run infinitely 122 if (malformedInputCalled) 123 return; 124 125 // if we get a replace, do not let the nio replace 126 if (newAction == CodingErrorAction.REPLACE) { 127 malformedInputCalled = true; 128 super.onMalformedInput(CodingErrorAction.IGNORE); 129 malformedInputCalled = false; 130 } 131 132 onMalformedInput = getCallback(newAction); 133 } 134 135 /** 136 * Sets the action to be taken if an illegal sequence is encountered 137 * 138 * @param newAction action to be taken 139 * @exception IllegalArgumentException 140 * @stable ICU 3.6 141 */ 142 @Override implOnUnmappableCharacter(CodingErrorAction newAction)143 protected final void implOnUnmappableCharacter(CodingErrorAction newAction) { 144 // don't run infinitely 145 if (unmappableCharacterCalled) 146 return; 147 148 // if we get a replace, do not let the nio replace 149 if (newAction == CodingErrorAction.REPLACE) { 150 unmappableCharacterCalled = true; 151 super.onUnmappableCharacter(CodingErrorAction.IGNORE); 152 unmappableCharacterCalled = false; 153 } 154 155 onUnmappableCharacter = getCallback(newAction); 156 } 157 158 /** 159 * Sets the callback encoder method and context to be used if an illegal sequence is encountered. 160 * You would normally call this twice to set both the malform and unmappable error. In this case, 161 * newContext should remain the same since using a different newContext each time will negate the last 162 * one used. 163 * @param err CoderResult 164 * @param newCallback CharsetCallback.Encoder 165 * @param newContext Object 166 * @stable ICU 4.0 167 */ setToUCallback(CoderResult err, CharsetCallback.Decoder newCallback, Object newContext)168 public final void setToUCallback(CoderResult err, CharsetCallback.Decoder newCallback, Object newContext) { 169 if (err.isMalformed()) { 170 onMalformedInput = newCallback; 171 } else if (err.isUnmappable()) { 172 onUnmappableCharacter = newCallback; 173 } else { 174 /* Error: Only malformed and unmappable are handled. */ 175 } 176 177 if (toUContext == null || !toUContext.equals(newContext)) { 178 toUContext = newContext; 179 } 180 } 181 getCallback(CodingErrorAction action)182 private static CharsetCallback.Decoder getCallback(CodingErrorAction action){ 183 if(action==CodingErrorAction.REPLACE){ 184 return CharsetCallback.TO_U_CALLBACK_SUBSTITUTE; 185 }else if(action==CodingErrorAction.IGNORE){ 186 return CharsetCallback.TO_U_CALLBACK_SKIP; 187 }else /* if(action==CodingErrorAction.REPORT) */ { 188 return CharsetCallback.TO_U_CALLBACK_STOP; 189 } 190 } 191 private final ByteBuffer EMPTY = ByteBuffer.allocate(0); 192 /** 193 * Flushes any characters saved in the converter's internal buffer and 194 * resets the converter. 195 * @param out action to be taken 196 * @return result of flushing action and completes the decoding all input. 197 * Returns CoderResult.UNDERFLOW if the action succeeds. 198 * @stable ICU 3.6 199 */ 200 @Override implFlush(CharBuffer out)201 protected final CoderResult implFlush(CharBuffer out) { 202 return decode(EMPTY, out, null, true); 203 } 204 205 /** 206 * Resets the to Unicode mode of converter 207 * @stable ICU 3.6 208 */ 209 @Override implReset()210 protected void implReset() { 211 toUnicodeStatus = 0 ; 212 toULength = 0; 213 charErrorBufferLength = 0; 214 charErrorBufferBegin = 0; 215 216 /* store previous UChars/chars to continue partial matches */ 217 preToUBegin = 0; 218 preToULength = 0; /* negative: replay */ 219 preToUFirstLength = 0; 220 221 mode = 0; 222 } 223 224 /** 225 * Decodes one or more bytes. The default behaviour of the converter 226 * is stop and report if an error in input stream is encountered. 227 * To set different behaviour use @see CharsetDecoder.onMalformedInput() 228 * This method allows a buffer by buffer conversion of a data stream. 229 * The state of the conversion is saved between calls to convert. 230 * Among other things, this means multibyte input sequences can be 231 * split between calls. If a call to convert results in an Error, the 232 * conversion may be continued by calling convert again with suitably 233 * modified parameters.All conversions should be finished with a call to 234 * the flush method. 235 * @param in buffer to decode 236 * @param out buffer to populate with decoded result 237 * @return Result of decoding action. Returns CoderResult.UNDERFLOW if the decoding 238 * action succeeds or more input is needed for completing the decoding action. 239 * @stable ICU 3.6 240 */ 241 @Override decodeLoop(ByteBuffer in,CharBuffer out)242 protected CoderResult decodeLoop(ByteBuffer in,CharBuffer out){ 243 if(in.remaining() < toUCountPending()){ 244 return CoderResult.UNDERFLOW; 245 } 246 // if (!in.hasRemaining()) { 247 // toULength = 0; 248 // return CoderResult.UNDERFLOW; 249 // } 250 251 in.position(in.position() + toUCountPending()); 252 253 /* do the conversion */ 254 CoderResult ret = decode(in, out, null, false); 255 256 // ok was there input held in the previous invocation of decodeLoop 257 // that resulted in output in this invocation? 258 in.position(in.position() - toUCountPending()); 259 260 return ret; 261 } 262 263 /* 264 * Implements the ICU semantic for decode operation 265 * @param in The input byte buffer 266 * @param out The output character buffer 267 * @return Result of decoding action. Returns CoderResult.UNDERFLOW if the decoding 268 * action succeeds or more input is needed for completing the decoding action. 269 */ decodeLoop(ByteBuffer in, CharBuffer out, IntBuffer offsets, boolean flush)270 abstract CoderResult decodeLoop(ByteBuffer in, CharBuffer out, IntBuffer offsets, boolean flush); 271 272 /* 273 * Implements the ICU semantic for decode operation 274 * @param source The input byte buffer 275 * @param target The output character buffer 276 * @param offsets 277 * @param flush true if, and only if, the invoker can provide no 278 * additional input bytes beyond those in the given buffer. 279 * @return Result of decoding action. Returns CoderResult.UNDERFLOW if the decoding 280 * action succeeds or more input is needed for completing the decoding action. 281 */ decode(ByteBuffer source, CharBuffer target, IntBuffer offsets, boolean flush)282 final CoderResult decode(ByteBuffer source, CharBuffer target, IntBuffer offsets, boolean flush) { 283 284 /* check parameters */ 285 if (target == null || source == null) { 286 throw new IllegalArgumentException(); 287 } 288 289 /* 290 * Make sure that the buffer sizes do not exceed the number range for 291 * int32_t because some functions use the size (in units or bytes) 292 * rather than comparing pointers, and because offsets are int32_t values. 293 * 294 * size_t is guaranteed to be unsigned and large enough for the job. 295 * 296 * Return with an error instead of adjusting the limits because we would 297 * not be able to maintain the semantics that either the source must be 298 * consumed or the target filled (unless an error occurs). 299 * An adjustment would be sourceLimit=t+0x7fffffff; for example. 300 */ 301 /*agljport:fix 302 if( 303 ((size_t)(sourceLimit-s)>(size_t)0x7fffffff && sourceLimit>s) || 304 ((size_t)(targetLimit-t)>(size_t)0x3fffffff && targetLimit>t) 305 ) { 306 *err=U_ILLEGAL_ARGUMENT_ERROR; 307 return; 308 } 309 */ 310 311 /* flush the target overflow buffer */ 312 if (charErrorBufferLength > 0) { 313 int i = 0; 314 do { 315 if (!target.hasRemaining()) { 316 /* the overflow buffer contains too much, keep the rest */ 317 int j = 0; 318 319 do { 320 charErrorBufferArray[j++] = charErrorBufferArray[i++]; 321 } while (i < charErrorBufferLength); 322 323 charErrorBufferLength = (byte) j; 324 return CoderResult.OVERFLOW; 325 } 326 327 /* copy the overflow contents to the target */ 328 target.put(charErrorBufferArray[i++]); 329 if (offsets != null) { 330 offsets.put(-1); /* no source index available for old output */ 331 } 332 } while (i < charErrorBufferLength); 333 334 /* the overflow buffer is completely copied to the target */ 335 charErrorBufferLength = 0; 336 } 337 338 if (!flush && !source.hasRemaining() && toULength == 0 && preToULength >= 0) { 339 /* the overflow buffer is emptied and there is no new input: we are done */ 340 return CoderResult.UNDERFLOW; 341 } 342 343 /* 344 * Do not simply return with a buffer overflow error if 345 * !flush && t==targetLimit 346 * because it is possible that the source will not generate any output. 347 * For example, the skip callback may be called; 348 * it does not output anything. 349 */ 350 351 return toUnicodeWithCallback(source, target, offsets, flush); 352 } 353 354 /* Currently, we are not using offsets in ICU4J. */ 355 /* private void updateOffsets(IntBuffer offsets,int length, int sourceIndex, int errorInputLength) { 356 int limit; 357 int delta, offset; 358 359 if(sourceIndex>=0) { 360 /* 361 * adjust each offset by adding the previous sourceIndex 362 * minus the length of the input sequence that caused an 363 * error, if any 364 */ 365 /* delta=sourceIndex-errorInputLength; 366 } else { 367 /* 368 * set each offset to -1 because this conversion function 369 * does not handle offsets 370 */ 371 /* delta=-1; 372 } 373 limit=offsets.position()+length; 374 if(delta==0) { 375 /* most common case, nothing to do */ 376 /* } else if(delta>0) { 377 /* add the delta to each offset (but not if the offset is <0) */ 378 /* while(offsets.position()<limit) { 379 offset=offsets.get(offsets.position()); 380 if(offset>=0) { 381 offsets.put(offset+delta); 382 } 383 //FIXME: ++offsets; 384 } 385 } else /* delta<0 */ /* { 386 /* 387 * set each offset to -1 because this conversion function 388 * does not handle offsets 389 * or the error input sequence started in a previous buffer 390 */ 391 /* while(offsets.position()<limit) { 392 offsets.put(-1); 393 } 394 } 395 } */ toUnicodeWithCallback(ByteBuffer source, CharBuffer target, IntBuffer offsets, boolean flush)396 final CoderResult toUnicodeWithCallback(ByteBuffer source, CharBuffer target, IntBuffer offsets, boolean flush){ 397 398 int sourceIndex; 399 int errorInputLength; 400 boolean converterSawEndOfInput, calledCallback; 401 //int t=target.position(); 402 int s=source.position(); 403 /* variables for m:n conversion */ 404 ByteBuffer replayArray = ByteBuffer.allocate(EXT_MAX_BYTES); 405 int replayArrayIndex = 0; 406 407 ByteBuffer realSource=null; 408 boolean realFlush=false; 409 int realSourceIndex=0; 410 411 412 CoderResult cr = CoderResult.UNDERFLOW; 413 414 /* get the converter implementation function */ 415 sourceIndex=0; 416 417 if(preToULength>=0) { 418 /* normal mode */ 419 } else { 420 /* 421 * Previous m:n conversion stored source units from a partial match 422 * and failed to consume all of them. 423 * We need to "replay" them from a temporary buffer and convert them first. 424 */ 425 realSource=source; 426 realFlush=flush; 427 realSourceIndex=sourceIndex; 428 //UConverterUtility.uprv_memcpy(replayArray, replayBegin, preToUArray, preToUBegin, -preToULength); 429 replayArray.put(preToUArray,0, -preToULength); 430 source=replayArray; 431 source.position(0); 432 source.limit(replayArrayIndex-preToULength); 433 flush=false; 434 sourceIndex=-1; 435 preToULength=0; 436 } 437 438 /* 439 * loop for conversion and error handling 440 * 441 * loop { 442 * convert 443 * loop { 444 * update offsets 445 * handle end of input 446 * handle errors/call callback 447 * } 448 * } 449 */ 450 for(;;) { 451 452 /* convert */ 453 cr = decodeLoop(source, target, offsets, flush); 454 455 /* 456 * set a flag for whether the converter 457 * successfully processed the end of the input 458 * 459 * need not check cnv->preToULength==0 because a replay (<0) will cause 460 * s<sourceLimit before converterSawEndOfInput is checked 461 */ 462 converterSawEndOfInput= (cr.isUnderflow() && flush && source.remaining()==0 && toULength == 0); 463 464 /* no callback called yet for this iteration */ 465 calledCallback=false; 466 467 /* no sourceIndex adjustment for conversion, only for callback output */ 468 errorInputLength=0; 469 470 /* 471 * loop for offsets and error handling 472 * 473 * iterates at most 3 times: 474 * 1. to clean up after the conversion function 475 * 2. after the callback 476 * 3. after the callback again if there was truncated input 477 */ 478 for(;;) { 479 /* update offsets if we write any */ 480 /* Currently offsets are not being used in ICU4J */ 481 /* if(offsets!=null) { 482 483 int length=(target.position()-t); 484 if(length>0) { 485 updateOffsets(offsets, length, sourceIndex, errorInputLength); 486 487 488 /* 489 * if a converter handles offsets and updates the offsets 490 * pointer at the end, then pArgs->offset should not change 491 * here; 492 * however, some converters do not handle offsets at all 493 * (sourceIndex<0) or may not update the offsets pointer 494 */ 495 //TODO: pArgs->offsets=offsets+=length; 496 /* } 497 498 if(sourceIndex>=0) { 499 sourceIndex+=(source.position()-s); 500 } 501 502 } */ 503 504 if(preToULength<0) { 505 /* 506 * switch the source to new replay units (cannot occur while replaying) 507 * after offset handling and before end-of-input and callback handling 508 */ 509 if(realSource==null) 510 { 511 realSource=source; 512 realFlush=flush; 513 realSourceIndex=sourceIndex; 514 515 //UConverterUtility.uprv_memcpy(replayArray, replayBegin, preToUArray, preToUBegin, -preToULength); 516 replayArray.put(preToUArray,0, -preToULength); 517 // reset position 518 replayArray.position(0); 519 520 source=replayArray; 521 source.limit(replayArrayIndex-preToULength); 522 flush=false; 523 if((sourceIndex+=preToULength)<0) { 524 sourceIndex=-1; 525 } 526 527 preToULength=0; 528 } else { 529 /* see implementation note before _fromUnicodeWithCallback() */ 530 //agljport:todo U_ASSERT(realSource==NULL); 531 Assert.assrt(realSource==null); 532 } 533 } 534 535 /* update pointers */ 536 s=source.position(); 537 //t=target.position(); 538 539 if(cr.isUnderflow()) { 540 if(s<source.limit()) 541 { 542 /* 543 * continue with the conversion loop while there is still input left 544 * (continue converting by breaking out of only the inner loop) 545 */ 546 break; 547 } else if(realSource!=null) { 548 /* switch back from replaying to the real source and continue */ 549 source = realSource; 550 flush=realFlush; 551 sourceIndex=realSourceIndex; 552 realSource=null; 553 break; 554 } else if(flush && toULength>0) { 555 /* 556 * the entire input stream is consumed 557 * and there is a partial, truncated input sequence left 558 */ 559 560 /* inject an error and continue with callback handling */ 561 cr = CoderResult.malformedForLength(toULength); 562 calledCallback=false; /* new error condition */ 563 } else { 564 /* input consumed */ 565 if(flush) { 566 /* 567 * return to the conversion loop once more if the flush 568 * flag is set and the conversion function has not 569 * successfully processed the end of the input yet 570 * 571 * (continue converting by breaking out of only the inner loop) 572 */ 573 if(!converterSawEndOfInput) { 574 break; 575 } 576 577 /* reset the converter without calling the callback function */ 578 implReset(); 579 } 580 581 /* done successfully */ 582 return cr; 583 } 584 } 585 586 /* U_FAILURE(*err) */ 587 { 588 589 if( calledCallback || cr.isOverflow() || 590 (cr.isMalformed() && cr.isUnmappable()) 591 ) { 592 /* 593 * the callback did not or cannot resolve the error: 594 * set output pointers and return 595 * 596 * the check for buffer overflow is redundant but it is 597 * a high-runner case and hopefully documents the intent 598 * well 599 * 600 * if we were replaying, then the replay buffer must be 601 * copied back into the UConverter 602 * and the real arguments must be restored 603 */ 604 if(realSource!=null) { 605 int length; 606 Assert.assrt(preToULength==0); 607 length = source.limit() - source.position(); 608 if(length>0) { 609 //UConverterUtility.uprv_memcpy(preToUArray, preToUBegin, pArgs.sourceArray, pArgs.sourceBegin, length); 610 source.get(preToUArray, preToUBegin, length); 611 preToULength=(byte)-length; 612 } 613 } 614 return cr; 615 } 616 } 617 618 /* copy toUBytes[] to invalidCharBuffer[] */ 619 errorInputLength=invalidCharLength=toULength; 620 if(errorInputLength>0) { 621 copy(toUBytesArray, 0, invalidCharBuffer, 0, errorInputLength); 622 } 623 624 /* set the converter state to deal with the next character */ 625 toULength=0; 626 627 /* call the callback function */ 628 cr = toCharErrorBehaviour.call(this, toUContext, source, target, offsets, invalidCharBuffer, errorInputLength, cr); 629 /* 630 * loop back to the offset handling 631 * 632 * this flag will indicate after offset handling 633 * that a callback was called; 634 * if the callback did not resolve the error, then we return 635 */ 636 calledCallback=true; 637 } 638 } 639 } 640 641 /* 642 * Returns the number of chars held in the converter's internal state 643 * because more input is needed for completing the conversion. This function is 644 * useful for mapping semantics of ICU's converter interface to those of iconv, 645 * and this information is not needed for normal conversion. 646 * @return The number of chars in the state. -1 if an error is encountered. 647 */ toUCountPending()648 /*public*/ int toUCountPending() { 649 if(preToULength > 0){ 650 return preToULength ; 651 } else if(preToULength < 0){ 652 return -preToULength; 653 } else if(toULength > 0){ 654 return toULength; 655 } else { 656 return 0; 657 } 658 } 659 660 copy(byte[] src, int srcOffset, char[] dst, int dstOffset, int length)661 private void copy(byte[] src, int srcOffset, char[] dst, int dstOffset, int length) { 662 for(int i=srcOffset; i<length; i++){ 663 dst[dstOffset++]=(char)(src[srcOffset++] & UConverterConstants.UNSIGNED_BYTE_MASK); 664 } 665 } 666 /* 667 * ONLY used by ToU callback functions. 668 * This function will write out the specified characters to the target 669 * character buffer. 670 * @return A CoderResult object that contains the error result when an error occurs. 671 */ toUWriteUChars( CharsetDecoderICU cnv, char[] ucharsArray, int ucharsBegin, int length, CharBuffer target, IntBuffer offsets, int sourceIndex)672 static final CoderResult toUWriteUChars( CharsetDecoderICU cnv, 673 char[] ucharsArray, int ucharsBegin, int length, 674 CharBuffer target, IntBuffer offsets, int sourceIndex) { 675 676 CoderResult cr = CoderResult.UNDERFLOW; 677 678 /* write UChars */ 679 if(offsets==null) { 680 while(length>0 && target.hasRemaining()) { 681 target.put(ucharsArray[ucharsBegin++]); 682 --length; 683 } 684 685 } else { 686 /* output with offsets */ 687 while(length>0 && target.hasRemaining()) { 688 target.put(ucharsArray[ucharsBegin++]); 689 offsets.put(sourceIndex); 690 --length; 691 } 692 } 693 /* write overflow */ 694 if(length>0) { 695 cnv.charErrorBufferLength= 0; 696 cr = CoderResult.OVERFLOW; 697 do { 698 cnv.charErrorBufferArray[cnv.charErrorBufferLength++]=ucharsArray[ucharsBegin++]; 699 } while(--length>0); 700 } 701 return cr; 702 } 703 /* 704 * This function will write out the Unicode substitution character to the 705 * target character buffer. 706 * Sub classes to override this method if required 707 * @param decoder 708 * @param source 709 * @param target 710 * @param offsets 711 * @return A CoderResult object that contains the error result when an error occurs. 712 */ 713 /* Note: Currently, this method is not being used because the callback method calls toUWriteUChars with 714 * the substitution characters. Will leave in here for the time being. To be removed later. (4.0) 715 */ 716 /*CoderResult cbToUWriteSub(CharsetDecoderICU decoder, 717 ByteBuffer source, CharBuffer target, 718 IntBuffer offsets){ 719 String sub = decoder.replacement(); 720 CharsetICU cs = (CharsetICU) decoder.charset(); 721 if (decoder.invalidCharLength==1 && cs.subChar1 != 0x00) { 722 char[] subArr = new char[] { 0x1a }; 723 return CharsetDecoderICU.toUWriteUChars(decoder, subArr, 0, sub 724 .length(), target, offsets, source.position()); 725 } else { 726 return CharsetDecoderICU.toUWriteUChars(decoder, sub.toCharArray(), 727 0, sub.length(), target, offsets, source.position()); 728 729 } 730 }*/ 731 732 /** 733 * Returns the maxBytesPerChar value for the Charset that created this decoder. 734 * @return maxBytesPerChar 735 * @stable ICU 4.8 736 */ maxBytesPerChar()737 public final float maxBytesPerChar() { 738 return ((CharsetICU)(this.charset())).maxBytesPerChar; 739 } 740 } 741