Home | History | Annotate | Download | only in charset
      1 //  2016 and later: Unicode, Inc. and others.
      2 // License & terms of use: http://www.unicode.org/copyright.html#License
      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 sematics 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      */
     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      */
     94     final boolean isFallbackUsed() {
     95         return true;
     96     }
     97 
     98     /**
     99      * Fallback is currently always used by icu4j decoders.
    100      */
    101     static final boolean isToUUseFallback() {
    102         return isToUUseFallback(true);
    103     }
    104 
    105     /**
    106      * Fallback is currently always used by icu4j decoders.
    107      */
    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
    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
    143     protected final void implOnUnmappableCharacter(CodingErrorAction newAction) {
    144         // dont 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 encounterd.
    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      */
    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 
    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
    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
    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
    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      */
    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      */
    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     } */
    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      */
    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 
    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      */
    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      */
    737     public final float maxBytesPerChar() {
    738         return ((CharsetICU)(this.charset())).maxBytesPerChar;
    739     }
    740 }
    741