• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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      */
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         // 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      */
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