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 NativeConverter.registerConverter(this, converterHandle); 72 } 73 implReplaceWith(String newReplacement)74 @Override protected void implReplaceWith(String newReplacement) { 75 updateCallback(); 76 } 77 implOnMalformedInput(CodingErrorAction newAction)78 @Override protected final void implOnMalformedInput(CodingErrorAction newAction) { 79 updateCallback(); 80 } 81 implOnUnmappableCharacter(CodingErrorAction newAction)82 @Override protected final void implOnUnmappableCharacter(CodingErrorAction newAction) { 83 updateCallback(); 84 } 85 updateCallback()86 private void updateCallback() { 87 NativeConverter.setCallbackDecode(converterHandle, this); 88 } 89 implReset()90 @Override protected void implReset() { 91 NativeConverter.resetByteToChar(converterHandle); 92 data[INPUT_OFFSET] = 0; 93 data[OUTPUT_OFFSET] = 0; 94 data[INVALID_BYTE_COUNT] = 0; 95 output = null; 96 input = null; 97 allocatedInput = null; 98 allocatedOutput = null; 99 inEnd = 0; 100 outEnd = 0; 101 } 102 implFlush(CharBuffer out)103 @Override protected final CoderResult implFlush(CharBuffer out) { 104 try { 105 // ICU needs to see an empty input. 106 input = EmptyArray.BYTE; 107 inEnd = 0; 108 data[INPUT_OFFSET] = 0; 109 110 data[OUTPUT_OFFSET] = getArray(out); 111 data[INVALID_BYTE_COUNT] = 0; // Make sure we don't see earlier errors. 112 113 int error = NativeConverter.decode(converterHandle, input, inEnd, output, outEnd, data, true); 114 if (ICU.U_FAILURE(error)) { 115 if (error == ICU.U_BUFFER_OVERFLOW_ERROR) { 116 return CoderResult.OVERFLOW; 117 } else if (error == ICU.U_TRUNCATED_CHAR_FOUND) { 118 if (data[INVALID_BYTE_COUNT] > 0) { 119 return CoderResult.malformedForLength(data[INVALID_BYTE_COUNT]); 120 } 121 } 122 } 123 return CoderResult.UNDERFLOW; 124 } finally { 125 setPosition(out); 126 implReset(); 127 } 128 } 129 decodeLoop(ByteBuffer in, CharBuffer out)130 @Override protected CoderResult decodeLoop(ByteBuffer in, CharBuffer out) { 131 if (!in.hasRemaining()) { 132 return CoderResult.UNDERFLOW; 133 } 134 135 data[INPUT_OFFSET] = getArray(in); 136 data[OUTPUT_OFFSET]= getArray(out); 137 138 try { 139 int error = NativeConverter.decode(converterHandle, input, inEnd, output, outEnd, data, false); 140 if (ICU.U_FAILURE(error)) { 141 if (error == ICU.U_BUFFER_OVERFLOW_ERROR) { 142 return CoderResult.OVERFLOW; 143 } else if (error == ICU.U_INVALID_CHAR_FOUND) { 144 return CoderResult.unmappableForLength(data[INVALID_BYTE_COUNT]); 145 } else if (error == ICU.U_ILLEGAL_CHAR_FOUND) { 146 return CoderResult.malformedForLength(data[INVALID_BYTE_COUNT]); 147 } else { 148 throw new AssertionError(error); 149 } 150 } 151 // Decoding succeeded: give us more data. 152 return CoderResult.UNDERFLOW; 153 } finally { 154 setPosition(in); 155 setPosition(out); 156 } 157 } 158 159 getArray(CharBuffer out)160 private int getArray(CharBuffer out) { 161 if (out.hasArray()) { 162 output = out.array(); 163 outEnd = out.arrayOffset() + out.limit(); 164 return out.arrayOffset() + out.position(); 165 } else { 166 outEnd = out.remaining(); 167 if (allocatedOutput == null || outEnd > allocatedOutput.length) { 168 allocatedOutput = new char[outEnd]; 169 } 170 // The array's start position is 0. 171 output = allocatedOutput; 172 return 0; 173 } 174 } 175 getArray(ByteBuffer in)176 private int getArray(ByteBuffer in) { 177 if (in.hasArray()) { 178 input = in.array(); 179 inEnd = in.arrayOffset() + in.limit(); 180 return in.arrayOffset() + in.position(); 181 } else { 182 inEnd = in.remaining(); 183 if (allocatedInput == null || inEnd > allocatedInput.length) { 184 allocatedInput = new byte[inEnd]; 185 } 186 // Copy the input buffer into the allocated array. 187 int pos = in.position(); 188 in.get(allocatedInput, 0, inEnd); 189 in.position(pos); 190 // The array's start position is 0. 191 input = allocatedInput; 192 return 0; 193 } 194 } 195 setPosition(CharBuffer out)196 private void setPosition(CharBuffer out) { 197 if (out.hasArray()) { 198 out.position(out.position() + data[OUTPUT_OFFSET]); 199 } else { 200 out.put(output, 0, data[OUTPUT_OFFSET]); 201 } 202 // release reference to output array, which may not be ours 203 output = null; 204 } 205 setPosition(ByteBuffer in)206 private void setPosition(ByteBuffer in) { 207 in.position(in.position() + data[INPUT_OFFSET]); 208 // release reference to input array, which may not be ours 209 input = null; 210 } 211 } 212