1adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project/**
2adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project*******************************************************************************
3adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project* Copyright (C) 1996-2006, International Business Machines Corporation and    *
4adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project* others. All Rights Reserved.                                                  *
5adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project*******************************************************************************
6adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project*
7adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project*******************************************************************************
8adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project*/
9f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes/**
10adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * A JNI interface for ICU converters.
11adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project *
12f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes *
13adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * @author Ram Viswanadha, IBM
14adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project */
153664d8839f0ba794f428119ee7f7304a66861da5Elliott Hughespackage java.nio.charset;
16adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
17e31b37859051d3902e06e4ba384995df7188917fHans Boehmimport dalvik.annotation.optimization.ReachabilitySensitive;
18adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Projectimport java.nio.ByteBuffer;
19adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Projectimport java.nio.CharBuffer;
207d52d787302b862019da41aa753646d88d43fd61Elliott Hughesimport java.util.HashMap;
217d52d787302b862019da41aa753646d88d43fd61Elliott Hughesimport java.util.Map;
225ec69b20ab9b3e2dcbe225d548168b09afbbbac2Elliott Hughesimport libcore.icu.ICU;
233664d8839f0ba794f428119ee7f7304a66861da5Elliott Hughesimport libcore.icu.NativeConverter;
2426ce8fbd8fe488cc969b08f64c56525662763dc4Jesse Wilsonimport libcore.util.EmptyArray;
25adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
264fd8ac297725190c6d81e9538b1faf7cfb0f5bb8Elliott Hughesfinal class CharsetEncoderICU extends CharsetEncoder {
277d52d787302b862019da41aa753646d88d43fd61Elliott Hughes    private static final Map<String, byte[]> DEFAULT_REPLACEMENTS = new HashMap<String, byte[]>();
287d52d787302b862019da41aa753646d88d43fd61Elliott Hughes    static {
2957a151cc43ef3dfc1040fad5581836c66c8e53f3Elliott Hughes        // ICU has different default replacements to the RI in some cases. There are many
3057a151cc43ef3dfc1040fad5581836c66c8e53f3Elliott Hughes        // additional cases, but this covers all the charsets that Java guarantees will be
3157a151cc43ef3dfc1040fad5581836c66c8e53f3Elliott Hughes        // available, which is where compatibility seems most important. (The RI even uses
3257a151cc43ef3dfc1040fad5581836c66c8e53f3Elliott Hughes        // the byte corresponding to '?' in ASCII as the replacement byte for charsets where that
3357a151cc43ef3dfc1040fad5581836c66c8e53f3Elliott Hughes        // byte corresponds to an entirely different character.)
34d6db7651bf7868ff780d284eb1bfd6ad860ddc1fElliott Hughes        // It's odd that UTF-8 doesn't use U+FFFD, given that (unlike ISO-8859-1 and US-ASCII) it
35d6db7651bf7868ff780d284eb1bfd6ad860ddc1fElliott Hughes        // can represent it, but this is what the RI does...
367d52d787302b862019da41aa753646d88d43fd61Elliott Hughes        byte[] questionMark = new byte[] { (byte) '?' };
377d52d787302b862019da41aa753646d88d43fd61Elliott Hughes        DEFAULT_REPLACEMENTS.put("UTF-8",      questionMark);
387d52d787302b862019da41aa753646d88d43fd61Elliott Hughes        DEFAULT_REPLACEMENTS.put("ISO-8859-1", questionMark);
397d52d787302b862019da41aa753646d88d43fd61Elliott Hughes        DEFAULT_REPLACEMENTS.put("US-ASCII",   questionMark);
407d52d787302b862019da41aa753646d88d43fd61Elliott Hughes    }
41adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
42c60bc1815dca549f3fb4e572f6aac749d7fa9fc6Elliott Hughes    private static final int INPUT_OFFSET = 0;
43c60bc1815dca549f3fb4e572f6aac749d7fa9fc6Elliott Hughes    private static final int OUTPUT_OFFSET = 1;
44f15b280ac1c2e32f7becdb314440c2999dc8d204Narayan Kamath    private static final int INVALID_CHAR_COUNT = 2;
45c60bc1815dca549f3fb4e572f6aac749d7fa9fc6Elliott Hughes    /*
46adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * data[INPUT_OFFSET]   = on input contains the start of input and on output the number of input chars consumed
47adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * data[OUTPUT_OFFSET]  = on input contains the start of output and on output the number of output bytes written
48adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * data[INVALID_CHARS]  = number of invalid chars
49adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     */
5033604713c5c70f9e6cad61dee6eb628db666bb22Elliott Hughes    private int[] data = new int[3];
512981b5e8cf7c19dfd85b2088b18b7a6146825317Elliott Hughes
52adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    /* handle to the ICU converter that is opened */
53e31b37859051d3902e06e4ba384995df7188917fHans Boehm    @ReachabilitySensitive
541bdc6bc1c72b033ed860360e69fc9f1f1121579bNarayan Kamath    private final long converterHandle;
55adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
56adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    private char[] input = null;
57adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    private byte[] output = null;
58adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
599932735d0306721523082f77b0a9ba4aa4db8cdbMihai Preda    private char[] allocatedInput = null;
609932735d0306721523082f77b0a9ba4aa4db8cdbMihai Preda    private byte[] allocatedOutput = null;
619932735d0306721523082f77b0a9ba4aa4db8cdbMihai Preda
62866e7ae17a3da81a02b0b144e0c9c2b3196d293aElliott Hughes    // These instance variables are always assigned in the methods before being used. This class
63866e7ae17a3da81a02b0b144e0c9c2b3196d293aElliott Hughes    // is inherently thread-unsafe so we don't have to worry about synchronization.
64adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    private int inEnd;
65adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    private int outEnd;
66adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
677d52d787302b862019da41aa753646d88d43fd61Elliott Hughes    public static CharsetEncoderICU newInstance(Charset cs, String icuCanonicalName) {
687d52d787302b862019da41aa753646d88d43fd61Elliott Hughes        // This complexity is necessary to ensure that even if the constructor, superclass
697d52d787302b862019da41aa753646d88d43fd61Elliott Hughes        // constructor, or call to updateCallback throw, we still free the native peer.
707d52d787302b862019da41aa753646d88d43fd61Elliott Hughes        long address = 0;
7159b140db4c4bd7617792256cd5d52713409137bcHans Boehm        CharsetEncoderICU result;
727d52d787302b862019da41aa753646d88d43fd61Elliott Hughes        try {
737d52d787302b862019da41aa753646d88d43fd61Elliott Hughes            address = NativeConverter.openConverter(icuCanonicalName);
747d52d787302b862019da41aa753646d88d43fd61Elliott Hughes            float averageBytesPerChar = NativeConverter.getAveBytesPerChar(address);
757d52d787302b862019da41aa753646d88d43fd61Elliott Hughes            float maxBytesPerChar = NativeConverter.getMaxBytesPerChar(address);
767d52d787302b862019da41aa753646d88d43fd61Elliott Hughes            byte[] replacement = makeReplacement(icuCanonicalName, address);
7759b140db4c4bd7617792256cd5d52713409137bcHans Boehm            result = new CharsetEncoderICU(cs, averageBytesPerChar, maxBytesPerChar, replacement, address);
7859b140db4c4bd7617792256cd5d52713409137bcHans Boehm        } catch (Throwable t) {
797d52d787302b862019da41aa753646d88d43fd61Elliott Hughes            if (address != 0) {
807d52d787302b862019da41aa753646d88d43fd61Elliott Hughes                NativeConverter.closeConverter(address);
817d52d787302b862019da41aa753646d88d43fd61Elliott Hughes            }
8259b140db4c4bd7617792256cd5d52713409137bcHans Boehm            throw t;
837d52d787302b862019da41aa753646d88d43fd61Elliott Hughes        }
8459b140db4c4bd7617792256cd5d52713409137bcHans Boehm        // An exception in registerConverter() will deallocate address:
8559b140db4c4bd7617792256cd5d52713409137bcHans Boehm        NativeConverter.registerConverter(result, address);
8659b140db4c4bd7617792256cd5d52713409137bcHans Boehm        result.updateCallback();
8759b140db4c4bd7617792256cd5d52713409137bcHans Boehm        return result;
887d52d787302b862019da41aa753646d88d43fd61Elliott Hughes    }
897d52d787302b862019da41aa753646d88d43fd61Elliott Hughes
907d52d787302b862019da41aa753646d88d43fd61Elliott Hughes    private static byte[] makeReplacement(String icuCanonicalName, long address) {
917d52d787302b862019da41aa753646d88d43fd61Elliott Hughes        // We have our own map of RI-compatible default replacements (where ICU disagrees)...
927d52d787302b862019da41aa753646d88d43fd61Elliott Hughes        byte[] replacement = DEFAULT_REPLACEMENTS.get(icuCanonicalName);
937d52d787302b862019da41aa753646d88d43fd61Elliott Hughes        if (replacement != null) {
947d52d787302b862019da41aa753646d88d43fd61Elliott Hughes            return replacement.clone();
957d52d787302b862019da41aa753646d88d43fd61Elliott Hughes        }
967d52d787302b862019da41aa753646d88d43fd61Elliott Hughes        // ...but fall back to asking ICU.
977d52d787302b862019da41aa753646d88d43fd61Elliott Hughes        return NativeConverter.getSubstitutionBytes(address);
987d52d787302b862019da41aa753646d88d43fd61Elliott Hughes    }
997d52d787302b862019da41aa753646d88d43fd61Elliott Hughes
1007d52d787302b862019da41aa753646d88d43fd61Elliott Hughes    private CharsetEncoderICU(Charset cs, float averageBytesPerChar, float maxBytesPerChar, byte[] replacement, long address) {
1013664d8839f0ba794f428119ee7f7304a66861da5Elliott Hughes        super(cs, averageBytesPerChar, maxBytesPerChar, replacement, true);
1023664d8839f0ba794f428119ee7f7304a66861da5Elliott Hughes        // Our native peer needs to know what just happened...
1037d52d787302b862019da41aa753646d88d43fd61Elliott Hughes        this.converterHandle = address;
104adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
105adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
10633604713c5c70f9e6cad61dee6eb628db666bb22Elliott Hughes    @Override protected void implReplaceWith(byte[] newReplacement) {
1073664d8839f0ba794f428119ee7f7304a66861da5Elliott Hughes        updateCallback();
108adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
109adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
11033604713c5c70f9e6cad61dee6eb628db666bb22Elliott Hughes    @Override protected void implOnMalformedInput(CodingErrorAction newAction) {
111cf6c3a752da274cc5025191d3bcd62e6222f4a4cElliott Hughes        updateCallback();
112adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
113adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
11433604713c5c70f9e6cad61dee6eb628db666bb22Elliott Hughes    @Override protected void implOnUnmappableCharacter(CodingErrorAction newAction) {
115cf6c3a752da274cc5025191d3bcd62e6222f4a4cElliott Hughes        updateCallback();
116cf6c3a752da274cc5025191d3bcd62e6222f4a4cElliott Hughes    }
117adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
118cf6c3a752da274cc5025191d3bcd62e6222f4a4cElliott Hughes    private void updateCallback() {
1195ec69b20ab9b3e2dcbe225d548168b09afbbbac2Elliott Hughes        NativeConverter.setCallbackEncode(converterHandle, this);
120adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
121adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
12233604713c5c70f9e6cad61dee6eb628db666bb22Elliott Hughes    @Override protected void implReset() {
12333604713c5c70f9e6cad61dee6eb628db666bb22Elliott Hughes        NativeConverter.resetCharToByte(converterHandle);
12433604713c5c70f9e6cad61dee6eb628db666bb22Elliott Hughes        data[INPUT_OFFSET] = 0;
12533604713c5c70f9e6cad61dee6eb628db666bb22Elliott Hughes        data[OUTPUT_OFFSET] = 0;
126f15b280ac1c2e32f7becdb314440c2999dc8d204Narayan Kamath        data[INVALID_CHAR_COUNT] = 0;
1272981b5e8cf7c19dfd85b2088b18b7a6146825317Elliott Hughes        output = null;
1282981b5e8cf7c19dfd85b2088b18b7a6146825317Elliott Hughes        input = null;
1292981b5e8cf7c19dfd85b2088b18b7a6146825317Elliott Hughes        allocatedInput = null;
1302981b5e8cf7c19dfd85b2088b18b7a6146825317Elliott Hughes        allocatedOutput = null;
1312981b5e8cf7c19dfd85b2088b18b7a6146825317Elliott Hughes        inEnd = 0;
1322981b5e8cf7c19dfd85b2088b18b7a6146825317Elliott Hughes        outEnd = 0;
13333604713c5c70f9e6cad61dee6eb628db666bb22Elliott Hughes    }
13433604713c5c70f9e6cad61dee6eb628db666bb22Elliott Hughes
13533604713c5c70f9e6cad61dee6eb628db666bb22Elliott Hughes    @Override protected CoderResult implFlush(ByteBuffer out) {
136adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        try {
13733604713c5c70f9e6cad61dee6eb628db666bb22Elliott Hughes            // ICU needs to see an empty input.
13833604713c5c70f9e6cad61dee6eb628db666bb22Elliott Hughes            input = EmptyArray.CHAR;
139dbf863488607fbb16a3d28c09f772d9581bd64adElliott Hughes            inEnd = 0;
14033604713c5c70f9e6cad61dee6eb628db666bb22Elliott Hughes            data[INPUT_OFFSET] = 0;
14133604713c5c70f9e6cad61dee6eb628db666bb22Elliott Hughes
142adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            data[OUTPUT_OFFSET] = getArray(out);
143f15b280ac1c2e32f7becdb314440c2999dc8d204Narayan Kamath            data[INVALID_CHAR_COUNT] = 0; // Make sure we don't see earlier errors.
144adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
1455ec69b20ab9b3e2dcbe225d548168b09afbbbac2Elliott Hughes            int error = NativeConverter.encode(converterHandle, input, inEnd, output, outEnd, data, true);
1465ec69b20ab9b3e2dcbe225d548168b09afbbbac2Elliott Hughes            if (ICU.U_FAILURE(error)) {
1475ec69b20ab9b3e2dcbe225d548168b09afbbbac2Elliott Hughes                if (error == ICU.U_BUFFER_OVERFLOW_ERROR) {
148adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                    return CoderResult.OVERFLOW;
1495ec69b20ab9b3e2dcbe225d548168b09afbbbac2Elliott Hughes                } else if (error == ICU.U_TRUNCATED_CHAR_FOUND) {
150f15b280ac1c2e32f7becdb314440c2999dc8d204Narayan Kamath                    if (data[INVALID_CHAR_COUNT] > 0) {
151f15b280ac1c2e32f7becdb314440c2999dc8d204Narayan Kamath                        return CoderResult.malformedForLength(data[INVALID_CHAR_COUNT]);
152adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                    }
153adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                }
154adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            }
155adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            return CoderResult.UNDERFLOW;
156adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        } finally {
157adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            setPosition(out);
158adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            implReset();
159adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
160adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
161adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
16233604713c5c70f9e6cad61dee6eb628db666bb22Elliott Hughes    @Override protected CoderResult encodeLoop(CharBuffer in, ByteBuffer out) {
163adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        if (!in.hasRemaining()) {
164adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            return CoderResult.UNDERFLOW;
165adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
166adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
167adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        data[INPUT_OFFSET] = getArray(in);
168adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        data[OUTPUT_OFFSET]= getArray(out);
169f15b280ac1c2e32f7becdb314440c2999dc8d204Narayan Kamath        data[INVALID_CHAR_COUNT] = 0; // Make sure we don't see earlier errors.
170f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes
171adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        try {
1725ec69b20ab9b3e2dcbe225d548168b09afbbbac2Elliott Hughes            int error = NativeConverter.encode(converterHandle, input, inEnd, output, outEnd, data, false);
1735ec69b20ab9b3e2dcbe225d548168b09afbbbac2Elliott Hughes            if (ICU.U_FAILURE(error)) {
1745ec69b20ab9b3e2dcbe225d548168b09afbbbac2Elliott Hughes                if (error == ICU.U_BUFFER_OVERFLOW_ERROR) {
175adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                    return CoderResult.OVERFLOW;
1765ec69b20ab9b3e2dcbe225d548168b09afbbbac2Elliott Hughes                } else if (error == ICU.U_INVALID_CHAR_FOUND) {
177f15b280ac1c2e32f7becdb314440c2999dc8d204Narayan Kamath                    return CoderResult.unmappableForLength(data[INVALID_CHAR_COUNT]);
1785ec69b20ab9b3e2dcbe225d548168b09afbbbac2Elliott Hughes                } else if (error == ICU.U_ILLEGAL_CHAR_FOUND) {
179f15b280ac1c2e32f7becdb314440c2999dc8d204Narayan Kamath                    return CoderResult.malformedForLength(data[INVALID_CHAR_COUNT]);
18033604713c5c70f9e6cad61dee6eb628db666bb22Elliott Hughes                } else {
1815ec69b20ab9b3e2dcbe225d548168b09afbbbac2Elliott Hughes                    throw new AssertionError(error);
182adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                }
183adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            }
1842981b5e8cf7c19dfd85b2088b18b7a6146825317Elliott Hughes            // Decoding succeeded: give us more data.
185adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            return CoderResult.UNDERFLOW;
186adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        } finally {
187adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            setPosition(in);
188adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            setPosition(out);
189adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
190adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
191adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
192866e7ae17a3da81a02b0b144e0c9c2b3196d293aElliott Hughes    private int getArray(ByteBuffer out) {
193866e7ae17a3da81a02b0b144e0c9c2b3196d293aElliott Hughes        if (out.hasArray()) {
194adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            output = out.array();
195b7bfb47e9720ecc6e10f43878f27e40542a9c800Elliott Hughes            outEnd = out.arrayOffset() + out.limit();
196b7bfb47e9720ecc6e10f43878f27e40542a9c800Elliott Hughes            return out.arrayOffset() + out.position();
197866e7ae17a3da81a02b0b144e0c9c2b3196d293aElliott Hughes        } else {
198adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            outEnd = out.remaining();
1992981b5e8cf7c19dfd85b2088b18b7a6146825317Elliott Hughes            if (allocatedOutput == null || outEnd > allocatedOutput.length) {
2009932735d0306721523082f77b0a9ba4aa4db8cdbMihai Preda                allocatedOutput = new byte[outEnd];
201adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            }
20233604713c5c70f9e6cad61dee6eb628db666bb22Elliott Hughes            // The array's start position is 0
2039932735d0306721523082f77b0a9ba4aa4db8cdbMihai Preda            output = allocatedOutput;
204adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            return 0;
205adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
206adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
207adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
208866e7ae17a3da81a02b0b144e0c9c2b3196d293aElliott Hughes    private int getArray(CharBuffer in) {
209866e7ae17a3da81a02b0b144e0c9c2b3196d293aElliott Hughes        if (in.hasArray()) {
210adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            input = in.array();
211b7bfb47e9720ecc6e10f43878f27e40542a9c800Elliott Hughes            inEnd = in.arrayOffset() + in.limit();
21233604713c5c70f9e6cad61dee6eb628db666bb22Elliott Hughes            return in.arrayOffset() + in.position();
213866e7ae17a3da81a02b0b144e0c9c2b3196d293aElliott Hughes        } else {
214adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            inEnd = in.remaining();
21533604713c5c70f9e6cad61dee6eb628db666bb22Elliott Hughes            if (allocatedInput == null || inEnd > allocatedInput.length) {
2169932735d0306721523082f77b0a9ba4aa4db8cdbMihai Preda                allocatedInput = new char[inEnd];
217adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            }
21833604713c5c70f9e6cad61dee6eb628db666bb22Elliott Hughes            // Copy the input buffer into the allocated array.
219adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            int pos = in.position();
22033604713c5c70f9e6cad61dee6eb628db666bb22Elliott Hughes            in.get(allocatedInput, 0, inEnd);
221adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            in.position(pos);
22233604713c5c70f9e6cad61dee6eb628db666bb22Elliott Hughes            // The array's start position is 0
22333604713c5c70f9e6cad61dee6eb628db666bb22Elliott Hughes            input = allocatedInput;
22433604713c5c70f9e6cad61dee6eb628db666bb22Elliott Hughes            return 0;
225adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
226adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
227f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes
22833604713c5c70f9e6cad61dee6eb628db666bb22Elliott Hughes    private void setPosition(ByteBuffer out) {
229adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        if (out.hasArray()) {
2304e4b93ffe988b0a597835d852489573b57e0f3feNarayan Kamath            out.position(data[OUTPUT_OFFSET] - out.arrayOffset());
231adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        } else {
232adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            out.put(output, 0, data[OUTPUT_OFFSET]);
233adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
2349932735d0306721523082f77b0a9ba4aa4db8cdbMihai Preda        // release reference to output array, which may not be ours
2359932735d0306721523082f77b0a9ba4aa4db8cdbMihai Preda        output = null;
236adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
237adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
23833604713c5c70f9e6cad61dee6eb628db666bb22Elliott Hughes    private void setPosition(CharBuffer in) {
23991bb19d2ef9822cfb50fd1191cdb3ee06e2939b9Narayan Kamath        int position = in.position() + data[INPUT_OFFSET] - data[INVALID_CHAR_COUNT];
24091bb19d2ef9822cfb50fd1191cdb3ee06e2939b9Narayan Kamath        if (position < 0) {
24191bb19d2ef9822cfb50fd1191cdb3ee06e2939b9Narayan Kamath            // The calculated position might be negative if we encountered an
24291bb19d2ef9822cfb50fd1191cdb3ee06e2939b9Narayan Kamath            // invalid char that spanned input buffers. We adjust it to 0 in this case.
24391bb19d2ef9822cfb50fd1191cdb3ee06e2939b9Narayan Kamath            //
24491bb19d2ef9822cfb50fd1191cdb3ee06e2939b9Narayan Kamath            // NOTE: The API doesn't allow us to adjust the position of the previous
24591bb19d2ef9822cfb50fd1191cdb3ee06e2939b9Narayan Kamath            // input buffer. (Doing that wouldn't serve any useful purpose anyway.)
24691bb19d2ef9822cfb50fd1191cdb3ee06e2939b9Narayan Kamath            position = 0;
24791bb19d2ef9822cfb50fd1191cdb3ee06e2939b9Narayan Kamath        }
24891bb19d2ef9822cfb50fd1191cdb3ee06e2939b9Narayan Kamath
24991bb19d2ef9822cfb50fd1191cdb3ee06e2939b9Narayan Kamath        in.position(position);
2509932735d0306721523082f77b0a9ba4aa4db8cdbMihai Preda        // release reference to input array, which may not be ours
2519932735d0306721523082f77b0a9ba4aa4db8cdbMihai Preda        input = null;
252adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
253adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project}
254