1 /** 2 ******************************************************************************* 3 * Copyright (C) 1996-2006, International Business Machines Corporation and * 4 * others. All Rights Reserved. * 5 ******************************************************************************* 6 * 7 ******************************************************************************* 8 */ 9 /** 10 * A JNI interface for ICU converters. 11 * 12 * 13 * @author Ram Viswanadha, IBM 14 */ 15 package java.nio.charset; 16 17 import java.nio.ByteBuffer; 18 import java.nio.CharBuffer; 19 import libcore.icu.ICU; 20 import libcore.icu.NativeConverter; 21 import libcore.util.EmptyArray; 22 23 final class CharsetDecoderICU extends CharsetDecoder { 24 private static final int MAX_CHARS_PER_BYTE = 2; 25 26 private static final int INPUT_OFFSET = 0; 27 private static final int OUTPUT_OFFSET = 1; 28 private static final int INVALID_BYTE_COUNT = 2; 29 /* 30 * data[INPUT_OFFSET] = on input contains the start of input and on output the number of input bytes consumed 31 * data[OUTPUT_OFFSET] = on input contains the start of output and on output the number of output chars written 32 * data[INVALID_BYTE_COUNT] = number of invalid bytes 33 */ 34 private final int[] data = new int[3]; 35 36 /* handle to the ICU converter that is opened */ 37 private long converterHandle = 0; 38 39 private byte[] input = null; 40 private char[] output= null; 41 42 private byte[] allocatedInput = null; 43 private char[] allocatedOutput = null; 44 45 // These instance variables are always assigned in the methods before being used. This class 46 // is inherently thread-unsafe so we don't have to worry about synchronization. 47 private int inEnd; 48 private int outEnd; 49 newInstance(Charset cs, String icuCanonicalName)50 public static CharsetDecoderICU newInstance(Charset cs, String icuCanonicalName) { 51 // This complexity is necessary to ensure that even if the constructor, superclass 52 // constructor, or call to updateCallback throw, we still free the native peer. 53 long address = 0; 54 try { 55 address = NativeConverter.openConverter(icuCanonicalName); 56 float averageCharsPerByte = NativeConverter.getAveCharsPerByte(address); 57 CharsetDecoderICU result = new CharsetDecoderICU(cs, averageCharsPerByte, address); 58 address = 0; // CharsetDecoderICU has taken ownership; its finalizer will do the free. 59 result.updateCallback(); 60 return result; 61 } finally { 62 if (address != 0) { 63 NativeConverter.closeConverter(address); 64 } 65 } 66 } 67 CharsetDecoderICU(Charset cs, float averageCharsPerByte, long address)68 private CharsetDecoderICU(Charset cs, float averageCharsPerByte, long address) { 69 super(cs, averageCharsPerByte, MAX_CHARS_PER_BYTE); 70 this.converterHandle = address; 71 } 72 implReplaceWith(String newReplacement)73 @Override protected void implReplaceWith(String newReplacement) { 74 updateCallback(); 75 } 76 implOnMalformedInput(CodingErrorAction newAction)77 @Override protected final void implOnMalformedInput(CodingErrorAction newAction) { 78 updateCallback(); 79 } 80 implOnUnmappableCharacter(CodingErrorAction newAction)81 @Override protected final void implOnUnmappableCharacter(CodingErrorAction newAction) { 82 updateCallback(); 83 } 84 updateCallback()85 private void updateCallback() { 86 NativeConverter.setCallbackDecode(converterHandle, this); 87 } 88 implReset()89 @Override protected void implReset() { 90 NativeConverter.resetByteToChar(converterHandle); 91 data[INPUT_OFFSET] = 0; 92 data[OUTPUT_OFFSET] = 0; 93 data[INVALID_BYTE_COUNT] = 0; 94 output = null; 95 input = null; 96 allocatedInput = null; 97 allocatedOutput = null; 98 inEnd = 0; 99 outEnd = 0; 100 } 101 implFlush(CharBuffer out)102 @Override protected final CoderResult implFlush(CharBuffer out) { 103 try { 104 // ICU needs to see an empty input. 105 input = EmptyArray.BYTE; 106 inEnd = 0; 107 data[INPUT_OFFSET] = 0; 108 109 data[OUTPUT_OFFSET] = getArray(out); 110 data[INVALID_BYTE_COUNT] = 0; // Make sure we don't see earlier errors. 111 112 int error = NativeConverter.decode(converterHandle, input, inEnd, output, outEnd, data, true); 113 if (ICU.U_FAILURE(error)) { 114 if (error == ICU.U_BUFFER_OVERFLOW_ERROR) { 115 return CoderResult.OVERFLOW; 116 } else if (error == ICU.U_TRUNCATED_CHAR_FOUND) { 117 if (data[INVALID_BYTE_COUNT] > 0) { 118 return CoderResult.malformedForLength(data[INVALID_BYTE_COUNT]); 119 } 120 } 121 } 122 return CoderResult.UNDERFLOW; 123 } finally { 124 setPosition(out); 125 implReset(); 126 } 127 } 128 decodeLoop(ByteBuffer in, CharBuffer out)129 @Override protected CoderResult decodeLoop(ByteBuffer in, CharBuffer out) { 130 if (!in.hasRemaining()) { 131 return CoderResult.UNDERFLOW; 132 } 133 134 data[INPUT_OFFSET] = getArray(in); 135 data[OUTPUT_OFFSET]= getArray(out); 136 137 try { 138 int error = NativeConverter.decode(converterHandle, input, inEnd, output, outEnd, data, false); 139 if (ICU.U_FAILURE(error)) { 140 if (error == ICU.U_BUFFER_OVERFLOW_ERROR) { 141 return CoderResult.OVERFLOW; 142 } else if (error == ICU.U_INVALID_CHAR_FOUND) { 143 return CoderResult.unmappableForLength(data[INVALID_BYTE_COUNT]); 144 } else if (error == ICU.U_ILLEGAL_CHAR_FOUND) { 145 return CoderResult.malformedForLength(data[INVALID_BYTE_COUNT]); 146 } else { 147 throw new AssertionError(error); 148 } 149 } 150 // Decoding succeeded: give us more data. 151 return CoderResult.UNDERFLOW; 152 } finally { 153 setPosition(in); 154 setPosition(out); 155 } 156 } 157 finalize()158 @Override protected void finalize() throws Throwable { 159 try { 160 NativeConverter.closeConverter(converterHandle); 161 converterHandle = 0; 162 } finally { 163 super.finalize(); 164 } 165 } 166 getArray(CharBuffer out)167 private int getArray(CharBuffer out) { 168 if (out.hasArray()) { 169 output = out.array(); 170 outEnd = out.arrayOffset() + out.limit(); 171 return out.arrayOffset() + out.position(); 172 } else { 173 outEnd = out.remaining(); 174 if (allocatedOutput == null || outEnd > allocatedOutput.length) { 175 allocatedOutput = new char[outEnd]; 176 } 177 // The array's start position is 0. 178 output = allocatedOutput; 179 return 0; 180 } 181 } 182 getArray(ByteBuffer in)183 private int getArray(ByteBuffer in) { 184 if (in.hasArray()) { 185 input = in.array(); 186 inEnd = in.arrayOffset() + in.limit(); 187 return in.arrayOffset() + in.position(); 188 } else { 189 inEnd = in.remaining(); 190 if (allocatedInput == null || inEnd > allocatedInput.length) { 191 allocatedInput = new byte[inEnd]; 192 } 193 // Copy the input buffer into the allocated array. 194 int pos = in.position(); 195 in.get(allocatedInput, 0, inEnd); 196 in.position(pos); 197 // The array's start position is 0. 198 input = allocatedInput; 199 return 0; 200 } 201 } 202 setPosition(CharBuffer out)203 private void setPosition(CharBuffer out) { 204 if (out.hasArray()) { 205 out.position(out.position() + data[OUTPUT_OFFSET] - out.arrayOffset()); 206 } else { 207 out.put(output, 0, data[OUTPUT_OFFSET]); 208 } 209 // release reference to output array, which may not be ours 210 output = null; 211 } 212 setPosition(ByteBuffer in)213 private void setPosition(ByteBuffer in) { 214 in.position(in.position() + data[INPUT_OFFSET]); 215 // release reference to input array, which may not be ours 216 input = null; 217 } 218 } 219