2 *******************************************************************************
3 * Copyright (C) 2006-2011, International Business Machines Corporation and *
4 * others. All Rights Reserved. *
5 *******************************************************************************
7 *******************************************************************************
10 package com.ibm.icu.charset;
12 import java.nio.BufferOverflowException;
13 import java.nio.ByteBuffer;
14 import java.nio.CharBuffer;
15 import java.nio.IntBuffer;
16 import java.nio.charset.CharsetEncoder;
17 import java.nio.charset.CoderResult;
18 import java.nio.charset.CodingErrorAction;
20 import com.ibm.icu.impl.Assert;
21 import com.ibm.icu.lang.UCharacter;
22 import com.ibm.icu.text.UTF16;
25 * An abstract class that provides framework methods of decoding operations for concrete
27 * In the future this class will contain API that will implement converter semantics of ICU4C.
30 public abstract class CharsetEncoderICU extends CharsetEncoder {
32 /* this is used in fromUnicode DBCS tables as an "unassigned" marker */
33 static final char MISSING_CHAR_MARKER = '\uFFFF';
35 byte[] errorBuffer = new byte[30];
37 int errorBufferLength = 0;
39 /** these are for encodeLoopICU */
40 int fromUnicodeStatus;
48 /* maximum number of indexed UChars */
49 static final int EXT_MAX_UCHARS = 19;
51 /* store previous UChars/chars to continue partial matches */
52 int preFromUFirstCP; /* >=0: partial match */
54 char[] preFromUArray = new char[EXT_MAX_UCHARS];
58 int preFromULength; /* negative: replay */
60 char[] invalidUCharBuffer = new char[2];
62 int invalidUCharLength;
66 private CharsetCallback.Encoder onUnmappableInput = CharsetCallback.FROM_U_CALLBACK_STOP;
68 private CharsetCallback.Encoder onMalformedInput = CharsetCallback.FROM_U_CALLBACK_STOP;
70 CharsetCallback.Encoder fromCharErrorBehaviour = new CharsetCallback.Encoder() {
71 public CoderResult call(CharsetEncoderICU encoder, Object context,
72 CharBuffer source, ByteBuffer target, IntBuffer offsets,
73 char[] buffer, int length, int cp, CoderResult cr) {
74 if (cr.isUnmappable()) {
75 return onUnmappableInput.call(encoder, context, source, target,
76 offsets, buffer, length, cp, cr);
77 } else /* if (cr.isMalformed()) */ {
78 return onMalformedInput.call(encoder, context, source, target,
79 offsets, buffer, length, cp, cr);
81 // return CharsetCallback.FROM_U_CALLBACK_STOP.call(encoder, context, source, target, offsets, buffer, length, cp, cr);
87 * Construcs a new encoder for the given charset
90 * for which the decoder is created
92 * the substitution bytes
94 CharsetEncoderICU(CharsetICU cs, byte[] replacement) {
95 super(cs, (cs.minBytesPerChar + cs.maxBytesPerChar) / 2,
96 cs.maxBytesPerChar, replacement);
100 * Is this Encoder allowed to use fallbacks? A fallback mapping is a mapping
101 * that will convert a Unicode codepoint sequence to a byte sequence, but
102 * the encoded byte sequence will round trip convert to a different
103 * Unicode codepoint sequence.
104 * @return true if the converter uses fallback, false otherwise.
107 public boolean isFallbackUsed() {
112 * Sets whether this Encoder can use fallbacks?
113 * @param usesFallback true if the user wants the converter to take
114 * advantage of the fallback mapping, false otherwise.
117 public void setFallbackUsed(boolean usesFallback) {
118 useFallback = usesFallback;
122 * Use fallbacks from Unicode to codepage when useFallback or for private-use code points
123 * @param c A codepoint
125 final boolean isFromUUseFallback(int c) {
127 || (UCharacter.getType(c) == UCharacter.PRIVATE_USE);
131 * Use fallbacks from Unicode to codepage when useFallback or for private-use code points
133 static final boolean isFromUUseFallback(boolean iUseFallback, int c) {
134 return (iUseFallback)
135 || (UCharacter.getType(c) == UCharacter.PRIVATE_USE);
139 * Sets the action to be taken if an illegal sequence is encountered
143 * @exception IllegalArgumentException
146 protected void implOnMalformedInput(CodingErrorAction newAction) {
147 onMalformedInput = getCallback(newAction);
151 * Sets the action to be taken if an illegal sequence is encountered
155 * @exception IllegalArgumentException
158 protected void implOnUnmappableCharacter(CodingErrorAction newAction) {
159 onUnmappableInput = getCallback(newAction);
163 * Sets the callback encoder method and context to be used if an illegal sequence is encountered.
164 * You would normally call this twice to set both the malform and unmappable error. In this case,
165 * newContext should remain the same since using a different newContext each time will negate the last
167 * @param err CoderResult
168 * @param newCallback CharsetCallback.Encoder
169 * @param newContext Object
172 public final void setFromUCallback(CoderResult err, CharsetCallback.Encoder newCallback, Object newContext) {
173 if (err.isMalformed()) {
174 onMalformedInput = newCallback;
175 } else if (err.isUnmappable()) {
176 onUnmappableInput = newCallback;
178 /* Error: Only malformed and unmappable are handled. */
181 if (fromUContext == null || !fromUContext.equals(newContext)) {
182 setFromUContext(newContext);
187 * Sets fromUContext used in callbacks.
189 * @param newContext Object
190 * @exception IllegalArgumentException The object is an illegal argument for UContext.
193 public final void setFromUContext(Object newContext) {
194 fromUContext = newContext;
197 private static CharsetCallback.Encoder getCallback(CodingErrorAction action) {
198 if (action == CodingErrorAction.REPLACE) {
199 return CharsetCallback.FROM_U_CALLBACK_SUBSTITUTE;
200 } else if (action == CodingErrorAction.IGNORE) {
201 return CharsetCallback.FROM_U_CALLBACK_SKIP;
202 } else /* if (action == CodingErrorAction.REPORT) */ {
203 return CharsetCallback.FROM_U_CALLBACK_STOP;
207 private static final CharBuffer EMPTY = CharBuffer.allocate(0);
210 * Flushes any characters saved in the converter's internal buffer and
211 * resets the converter.
212 * @param out action to be taken
213 * @return result of flushing action and completes the decoding all input.
214 * Returns CoderResult.UNDERFLOW if the action succeeds.
217 protected CoderResult implFlush(ByteBuffer out) {
218 return encode(EMPTY, out, null, true);
222 * Resets the from Unicode mode of converter
225 protected void implReset() {
226 errorBufferLength = 0;
227 fromUnicodeStatus = 0;
232 private void fromUnicodeReset() {
234 preFromUFirstCP = UConverterConstants.U_SENTINEL;
239 * Encodes one or more chars. The default behaviour of the
240 * converter is stop and report if an error in input stream is encountered.
241 * To set different behaviour use @see CharsetEncoder.onMalformedInput()
242 * @param in buffer to decode
243 * @param out buffer to populate with decoded result
244 * @return result of decoding action. Returns CoderResult.UNDERFLOW if the decoding
245 * action succeeds or more input is needed for completing the decoding action.
248 protected CoderResult encodeLoop(CharBuffer in, ByteBuffer out) {
249 if (!in.hasRemaining() && this.errorBufferLength == 0) { // make sure the errorBuffer is empty
250 // The Java framework should have already substituted what was left.
252 //fromUnicodeReset();
253 return CoderResult.UNDERFLOW;
255 in.position(in.position() + fromUCountPending());
256 /* do the conversion */
257 CoderResult ret = encode(in, out, null, false);
258 setSourcePosition(in);
259 /* No need to reset to keep the proper state of the encoder.
260 if (ret.isUnderflow() && in.hasRemaining()) {
261 // The Java framework is going to substitute what is left.
262 //fromUnicodeReset();
268 * Implements ICU semantics of buffer management
272 * @return A CoderResult object that contains the error result when an error occurs.
274 abstract CoderResult encodeLoop(CharBuffer source, ByteBuffer target,
275 IntBuffer offsets, boolean flush);
278 * Implements ICU semantics for encoding the buffer
279 * @param source The input character buffer
280 * @param target The output byte buffer
282 * @param flush true if, and only if, the invoker can provide no
283 * additional input bytes beyond those in the given buffer.
284 * @return A CoderResult object that contains the error result when an error occurs.
286 final CoderResult encode(CharBuffer source, ByteBuffer target,
287 IntBuffer offsets, boolean flush) {
289 /* check parameters */
290 if (target == null || source == null) {
291 throw new IllegalArgumentException();
295 * Make sure that the buffer sizes do not exceed the number range for
296 * int32_t because some functions use the size (in units or bytes)
297 * rather than comparing pointers, and because offsets are int32_t values.
299 * size_t is guaranteed to be unsigned and large enough for the job.
301 * Return with an error instead of adjusting the limits because we would
302 * not be able to maintain the semantics that either the source must be
303 * consumed or the target filled (unless an error occurs).
304 * An adjustment would be targetLimit=t+0x7fffffff; for example.
307 /* flush the target overflow buffer */
308 if (errorBufferLength > 0) {
309 byte[] overflowArray;
312 overflowArray = errorBuffer;
313 length = errorBufferLength;
316 if (target.remaining() == 0) {
317 /* the overflow buffer contains too much, keep the rest */
321 overflowArray[j++] = overflowArray[i++];
322 } while (i < length);
324 errorBufferLength = (byte) j;
325 return CoderResult.OVERFLOW;
328 /* copy the overflow contents to the target */
329 target.put(overflowArray[i++]);
330 if (offsets != null) {
331 offsets.put(-1); /* no source index available for old output */
333 } while (i < length);
335 /* the overflow buffer is completely copied to the target */
336 errorBufferLength = 0;
339 if (!flush && source.remaining() == 0 && preFromULength >= 0) {
340 /* the overflow buffer is emptied and there is no new input: we are done */
341 return CoderResult.UNDERFLOW;
345 * Do not simply return with a buffer overflow error if
346 * !flush && t==targetLimit
347 * because it is possible that the source will not generate any output.
348 * For example, the skip callback may be called;
349 * it does not output anything.
352 return fromUnicodeWithCallback(source, target, offsets, flush);
357 * Implementation note for m:n conversions
359 * While collecting source units to find the longest match for m:n conversion,
360 * some source units may need to be stored for a partial match.
361 * When a second buffer does not yield a match on all of the previously stored
362 * source units, then they must be "replayed", i.e., fed back into the converter.
364 * The code relies on the fact that replaying will not nest -
365 * converting a replay buffer will not result in a replay.
366 * This is because a replay is necessary only after the _continuation_ of a
367 * partial match failed, but a replay buffer is converted as a whole.
368 * It may result in some of its units being stored again for a partial match,
369 * but there will not be a continuation _during_ the replay which could fail.
371 * It is conceivable that a callback function could call the converter
372 * recursively in a way that causes another replay to be stored, but that
373 * would be an error in the callback function.
374 * Such violations will cause assertion failures in a debug build,
375 * and wrong output, but they will not cause a crash.
377 final CoderResult fromUnicodeWithCallback(CharBuffer source,
378 ByteBuffer target, IntBuffer offsets, boolean flush) {
381 int errorInputLength;
382 boolean converterSawEndOfInput, calledCallback;
384 /* variables for m:n conversion */
385 CharBuffer replayArray = CharBuffer.allocate(EXT_MAX_UCHARS);
386 int replayArrayIndex = 0;
387 CharBuffer realSource;
390 CoderResult cr = CoderResult.UNDERFLOW;
392 /* get the converter implementation function */
395 if (preFromULength >= 0) {
401 * Previous m:n conversion stored source units from a partial match
402 * and failed to consume all of them.
403 * We need to "replay" them from a temporary buffer and convert them first.
408 //UConverterUtility.uprv_memcpy(replayArray, replayArrayIndex, preFromUArray, 0, -preFromULength*UMachine.U_SIZEOF_UCHAR);
409 replayArray.put(preFromUArray, 0, -preFromULength);
410 source = replayArray;
411 source.position(replayArrayIndex);
412 source.limit(replayArrayIndex - preFromULength); //preFromULength is negative, see declaration
419 * loop for conversion and error handling
425 * handle end of input
426 * handle errors/call callback
432 cr = encodeLoop(source, target, offsets, flush);
434 * set a flag for whether the converter
435 * successfully processed the end of the input
437 * need not check cnv.preFromULength==0 because a replay (<0) will cause
438 * s<sourceLimit before converterSawEndOfInput is checked
440 converterSawEndOfInput = (cr.isUnderflow() && flush
441 && source.remaining() == 0 && fromUChar32 == 0);
443 /* no callback called yet for this iteration */
444 calledCallback = false;
446 /* no sourceIndex adjustment for conversion, only for callback output */
447 errorInputLength = 0;
450 * loop for offsets and error handling
452 * iterates at most 3 times:
453 * 1. to clean up after the conversion function
454 * 2. after the callback
455 * 3. after the callback again if there was truncated input
458 /* update offsets if we write any */
459 /* Currently offsets are not being used in ICU4J */
460 /* if (offsets != null) {
461 int length = target.remaining();
465 * if a converter handles offsets and updates the offsets
466 * pointer at the end, then offset should not change
468 * however, some converters do not handle offsets at all
469 * (sourceIndex<0) or may not update the offsets pointer
471 /* offsets.position(offsets.position() + length);
474 if (sourceIndex >= 0) {
475 sourceIndex += (int) (source.position());
479 if (preFromULength < 0) {
481 * switch the source to new replay units (cannot occur while replaying)
482 * after offset handling and before end-of-input and callback handling
484 if (realSource == null) {
488 //UConverterUtility.uprv_memcpy(replayArray, replayArrayIndex, preFromUArray, 0, -preFromULength*UMachine.U_SIZEOF_UCHAR);
489 replayArray.put(preFromUArray, 0, -preFromULength);
491 source = replayArray;
492 source.position(replayArrayIndex);
493 source.limit(replayArrayIndex - preFromULength);
495 if ((sourceIndex += preFromULength) < 0) {
501 /* see implementation note before _fromUnicodeWithCallback() */
502 //agljport:todo U_ASSERT(realSource==NULL);
503 Assert.assrt(realSource == null);
507 /* update pointers */
508 sBufferIndex = source.position();
509 if (cr.isUnderflow()) {
510 if (sBufferIndex < source.limit()) {
512 * continue with the conversion loop while there is still input left
513 * (continue converting by breaking out of only the inner loop)
516 } else if (realSource != null) {
517 /* switch back from replaying to the real source and continue */
520 sourceIndex = source.position();
523 } else if (flush && fromUChar32 != 0) {
525 * the entire input stream is consumed
526 * and there is a partial, truncated input sequence left
529 /* inject an error and continue with callback handling */
530 //err[0]=ErrorCode.U_TRUNCATED_CHAR_FOUND;
531 cr = CoderResult.malformedForLength(1);
532 calledCallback = false; /* new error condition */
537 * return to the conversion loop once more if the flush
538 * flag is set and the conversion function has not
539 * successfully processed the end of the input yet
541 * (continue converting by breaking out of only the inner loop)
543 if (!converterSawEndOfInput) {
547 /* reset the converter without calling the callback function */
551 /* done successfully */
559 if (calledCallback || cr.isOverflow()
560 || (!cr.isMalformed() && !cr.isUnmappable())) {
562 * the callback did not or cannot resolve the error:
563 * set output pointers and return
565 * the check for buffer overflow is redundant but it is
566 * a high-runner case and hopefully documents the intent
569 * if we were replaying, then the replay buffer must be
570 * copied back into the UConverter
571 * and the real arguments must be restored
573 if (realSource != null) {
576 //agljport:todo U_ASSERT(cnv.preFromULength==0);
578 length = source.remaining();
580 //UConverterUtility.uprv_memcpy(preFromUArray, 0, sourceArray, pArgs.sourceBegin, length*UMachine.U_SIZEOF_UCHAR);
581 source.get(preFromUArray, 0, length);
582 preFromULength = (byte) -length;
591 /* callback handling */
595 /* get and write the code point */
596 codePoint = fromUChar32;
597 errorInputLength = UTF16.append(invalidUCharBuffer, 0,
599 invalidUCharLength = errorInputLength;
601 /* set the converter state to deal with the next character */
604 /* call the callback function */
605 cr = fromCharErrorBehaviour.call(this, fromUContext,
606 source, target, offsets, invalidUCharBuffer,
607 invalidUCharLength, codePoint, cr);
611 * loop back to the offset handling
613 * this flag will indicate after offset handling
614 * that a callback was called;
615 * if the callback did not resolve the error, then we return
617 calledCallback = true;
623 * Ascertains if a given Unicode code point (32bit value for handling surrogates)
624 * can be converted to the target encoding. If the caller wants to test if a
625 * surrogate pair can be converted to target encoding then the
626 * responsibility of assembling the int value lies with the caller.
627 * For assembling a code point the caller can use UTF16 class of ICU4J and do something like:
629 * while(i<mySource.length){
630 * if(UTF16.isLeadSurrogate(mySource[i])&& i+1< mySource.length){
631 * if(UTF16.isTrailSurrogate(mySource[i+1])){
632 * int temp = UTF16.charAt(mySource,i,i+1,0);
633 * if(!((CharsetEncoderICU) myConv).canEncode(temp)){
644 * String src = new String(mySource);
646 * boolean passed = false;
647 * while(i<src.length()){
648 * codepoint = UTF16.charAt(src,i);
649 * i+= (codepoint>0xfff)? 2:1;
650 * if(!(CharsetEncoderICU) myConv).canEncode(codepoint)){
656 * @param codepoint Unicode code point as int value
657 * @return true if a character can be converted
659 /* TODO This is different from Java's canEncode(char) API.
660 * ICU's API should implement getUnicodeSet,
661 * and override canEncode(char) which queries getUnicodeSet.
662 * The getUnicodeSet should return a frozen UnicodeSet or use a fillin parameter, like ICU4C.
664 /*public boolean canEncode(int codepoint) {
668 * Overrides super class method
671 public boolean isLegalReplacement(byte[] repl) {
676 * Writes out the specified output bytes to the target byte buffer or to converter internal buffers.
684 * @return A CoderResult object that contains the error result when an error occurs.
686 static final CoderResult fromUWriteBytes(CharsetEncoderICU cnv,
687 byte[] bytesArray, int bytesBegin, int bytesLength, ByteBuffer out,
688 IntBuffer offsets, int sourceIndex) {
691 int obl = bytesLength;
692 CoderResult cr = CoderResult.UNDERFLOW;
693 int bytesLimit = bytesBegin + bytesLength;
695 for (; bytesBegin < bytesLimit;) {
696 out.put(bytesArray[bytesBegin]);
701 } catch (BufferOverflowException ex) {
702 cr = CoderResult.OVERFLOW;
705 if (offsets != null) {
706 while (obl > bytesLength) {
707 offsets.put(sourceIndex);
712 cnv.errorBufferLength = bytesLimit - bytesBegin;
713 if (cnv.errorBufferLength > 0) {
715 while (bytesBegin < bytesLimit) {
716 cnv.errorBuffer[index++] = bytesArray[bytesBegin++];
718 cr = CoderResult.OVERFLOW;
724 * Returns the number of chars held in the converter's internal state
725 * because more input is needed for completing the conversion. This function is
726 * useful for mapping semantics of ICU's converter interface to those of iconv,
727 * and this information is not needed for normal conversion.
728 * @return The number of chars in the state. -1 if an error is encountered.
730 /*public*/int fromUCountPending() {
731 if (preFromULength > 0) {
732 return UTF16.getCharCount(preFromUFirstCP) + preFromULength;
733 } else if (preFromULength < 0) {
734 return -preFromULength;
735 } else if (fromUChar32 > 0) {
737 } else if (preFromUFirstCP > 0) {
738 return UTF16.getCharCount(preFromUFirstCP);
747 private final void setSourcePosition(CharBuffer source) {
749 // ok was there input held in the previous invocation of encodeLoop
750 // that resulted in output in this invocation?
751 source.position(source.position() - fromUCountPending());
755 * Write the codepage substitution character.
756 * Subclasses to override this method.
757 * For stateful converters, it is typically necessary to handle this
758 * specificially for the converter in order to properly maintain the state.
759 * @param source The input character buffer
760 * @param target The output byte buffer
762 * @return A CoderResult object that contains the error result when an error occurs.
764 CoderResult cbFromUWriteSub(CharsetEncoderICU encoder, CharBuffer source,
765 ByteBuffer target, IntBuffer offsets) {
766 CharsetICU cs = (CharsetICU) encoder.charset();
767 byte[] sub = encoder.replacement();
768 if (cs.subChar1 != 0 && encoder.invalidUCharBuffer[0] <= 0xff) {
769 return CharsetEncoderICU.fromUWriteBytes(encoder,
770 new byte[] { cs.subChar1 }, 0, 1, target, offsets, source
773 return CharsetEncoderICU.fromUWriteBytes(encoder, sub, 0,
774 sub.length, target, offsets, source.position());
779 * Write the characters to target.
780 * @param source The input character buffer
781 * @param target The output byte buffer
783 * @return A CoderResult object that contains the error result when an error occurs.
785 CoderResult cbFromUWriteUChars(CharsetEncoderICU encoder,
786 CharBuffer source, ByteBuffer target, IntBuffer offsets) {
787 CoderResult cr = CoderResult.UNDERFLOW;
789 /* This is a fun one. Recursion can occur - we're basically going to
790 * just retry shoving data through the same converter. Note, if you got
791 * here through some kind of invalid sequence, you maybe should emit a
792 * reset sequence of some kind. Since this IS an actual conversion,
793 * take care that you've changed the callback or the data, or you'll
794 * get an infinite loop.
797 int oldTargetPosition = target.position();
798 int offsetIndex = source.position();
800 cr = encoder.encode(source, target, null, false); /* no offsets and no flush */
802 if (offsets != null) {
803 while (target.position() != oldTargetPosition) {
804 offsets.put(offsetIndex);
809 /* Note, if you did something like used a stop subcallback, things would get interesting.
810 * In fact, here's where we want to return the partially consumed in-source!
812 if (cr.isOverflow()) {
813 /* Overflowed target. Now, we'll write into the charErrorBuffer.
814 * It's a fixed size. If we overflow it...Hm
817 /* start the new target at the first free slot in the error buffer */
818 int errBuffLen = encoder.errorBufferLength;
819 ByteBuffer newTarget = ByteBuffer.wrap(encoder.errorBuffer);
820 newTarget.position(errBuffLen); /* set the position at the end of the error buffer */
821 encoder.errorBufferLength = 0;
823 encoder.encode(source, newTarget, null, false);
825 encoder.errorBuffer = newTarget.array();
826 encoder.errorBufferLength = newTarget.position();
834 * Handles a common situation where a character has been read and it may be
835 * a lead surrogate followed by a trail surrogate. This method can change
836 * the source position and will modify fromUChar32.
840 * If <code>null</code> is returned, then there was success in reading a
841 * surrogate pair, the codepoint is stored in <code>fromUChar32</code> and
842 * <code>fromUChar32</code> should be reset (to 0) after being read.
846 * The encoding source.
848 * A character that may be the first in a surrogate pair.
849 * @return <code>CoderResult.malformedForLength(1)</code> or
850 * <code>CoderResult.UNDERFLOW</code> if there is a problem, or
851 * <code>null</code> if there isn't.
852 * @see #handleSurrogates(CharBuffer, char)
853 * @see #handleSurrogates(CharBuffer, int, char)
854 * @see #handleSurrogates(char[], int, int, char)
856 final CoderResult handleSurrogates(CharBuffer source, char lead) {
857 if (!UTF16.isLeadSurrogate(lead)) {
859 return CoderResult.malformedForLength(1);
862 if (!source.hasRemaining()) {
864 return CoderResult.UNDERFLOW;
867 char trail = source.get();
869 if (!UTF16.isTrailSurrogate(trail)) {
871 source.position(source.position() - 1);
872 return CoderResult.malformedForLength(1);
875 fromUChar32 = UCharacter.getCodePoint(lead, trail);
881 * Same as <code>handleSurrogates(CharBuffer, char)</code>, but with arrays. As an added
882 * requirement, the calling method must also increment the index if this method returns
888 * The encoding source.
890 * A character that may be the first in a surrogate pair.
891 * @return <code>CoderResult.malformedForLength(1)</code> or
892 * <code>CoderResult.UNDERFLOW</code> if there is a problem, or <code>null</code> if
894 * @see #handleSurrogates(CharBuffer, char)
895 * @see #handleSurrogates(CharBuffer, int, char)
896 * @see #handleSurrogates(char[], int, int, char)
898 final CoderResult handleSurrogates(char[] sourceArray, int sourceIndex,
899 int sourceLimit, char lead) {
900 if (!UTF16.isLeadSurrogate(lead)) {
902 return CoderResult.malformedForLength(1);
905 if (sourceIndex >= sourceLimit) {
907 return CoderResult.UNDERFLOW;
910 char trail = sourceArray[sourceIndex];
912 if (!UTF16.isTrailSurrogate(trail)) {
914 return CoderResult.malformedForLength(1);
917 fromUChar32 = UCharacter.getCodePoint(lead, trail);
922 * Returns the maxCharsPerByte value for the Charset that created this encoder.
923 * @return maxCharsPerByte
925 * @provisional This API might change or be removed in a future release.
927 public final float maxCharsPerByte() {
928 return ((CharsetICU)(this.charset())).maxCharsPerByte;