summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/ScopedStringChars.h51
-rw-r--r--luni/src/main/java/java/nio/charset/CharsetDecoder.java124
-rw-r--r--luni/src/main/java/java/nio/charset/CharsetEncoder.java125
-rw-r--r--luni/src/main/java/libcore/icu/CharsetDecoderICU.java9
-rw-r--r--luni/src/main/java/libcore/icu/CharsetEncoderICU.java15
-rw-r--r--luni/src/main/java/libcore/icu/NativeConverter.java4
-rw-r--r--luni/src/main/native/NativeConverter.cpp124
7 files changed, 210 insertions, 242 deletions
diff --git a/include/ScopedStringChars.h b/include/ScopedStringChars.h
new file mode 100644
index 0000000..9f543b7
--- /dev/null
+++ b/include/ScopedStringChars.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SCOPED_STRING_CHARS_H_included
+#define SCOPED_STRING_CHARS_H_included
+
+#include "JNIHelp.h"
+
+// A smart pointer that provides access to a jchar* given a JNI jstring.
+class ScopedStringChars {
+public:
+ ScopedStringChars(JNIEnv* env, jstring s) : mEnv(env), mString(s), mSize(0) {
+ mChars = env->GetStringChars(mString, NULL);
+ if (mChars != NULL) {
+ mSize = env->GetStringLength(mString);
+ }
+ }
+
+ ~ScopedStringChars() {
+ mEnv->ReleaseStringChars(mString, mChars);
+ }
+
+ const jchar* get() const { return mChars; }
+ const jchar& operator[](size_t n) const { return mChars[n]; }
+ size_t size() const { return mSize; }
+
+private:
+ JNIEnv* mEnv;
+ jstring mString;
+ const UChar* mChars;
+ size_t mSize;
+
+ // Disallow copy and assignment.
+ ScopedStringChars(const ScopedStringChars&);
+ void operator=(const ScopedStringChars&);
+};
+
+#endif // SCOPED_STRING_CHARS_H_included
diff --git a/luni/src/main/java/java/nio/charset/CharsetDecoder.java b/luni/src/main/java/java/nio/charset/CharsetDecoder.java
index df2bb6a..f67dbbc 100644
--- a/luni/src/main/java/java/nio/charset/CharsetDecoder.java
+++ b/luni/src/main/java/java/nio/charset/CharsetDecoder.java
@@ -82,37 +82,21 @@ import java.nio.CharBuffer;
* @see java.nio.charset.CharsetEncoder
*/
public abstract class CharsetDecoder {
-
- /*
- * internal status consts
- */
private static final int INIT = 0;
-
private static final int ONGOING = 1;
-
private static final int END = 2;
-
private static final int FLUSH = 3;
- // average number of chars for one byte
- private float averChars;
-
- // maximum number of chars for one byte
- private float maxChars;
-
- // charset for this decoder
- private Charset cs;
+ private final float averageCharsPerByte;
+ private final float maxCharsPerByte;
- // specify the action if malformed input error encountered
- private CodingErrorAction malformAction;
+ private final Charset cs;
- // specify the action if unmappable character error encountered
- private CodingErrorAction unmapAction;
+ private CodingErrorAction malformedInputAction;
+ private CodingErrorAction unmappableCharacterAction;
- // the replacement string
- private String replace;
+ private String replacementChars;
- // the current status
private int status;
/**
@@ -133,38 +117,32 @@ public abstract class CharsetDecoder {
* if <code>averageCharsPerByte</code> or
* <code>maxCharsPerByte</code> is negative.
*/
- protected CharsetDecoder(Charset charset, float averageCharsPerByte,
- float maxCharsPerByte) {
+ protected CharsetDecoder(Charset charset, float averageCharsPerByte, float maxCharsPerByte) {
if (averageCharsPerByte <= 0 || maxCharsPerByte <= 0) {
throw new IllegalArgumentException("averageCharsPerByte and maxCharsPerByte must be positive");
}
if (averageCharsPerByte > maxCharsPerByte) {
throw new IllegalArgumentException("averageCharsPerByte is greater than maxCharsPerByte");
}
- averChars = averageCharsPerByte;
- maxChars = maxCharsPerByte;
+ this.averageCharsPerByte = averageCharsPerByte;
+ this.maxCharsPerByte = maxCharsPerByte;
cs = charset;
status = INIT;
- malformAction = CodingErrorAction.REPORT;
- unmapAction = CodingErrorAction.REPORT;
- replace = "\ufffd";
+ malformedInputAction = CodingErrorAction.REPORT;
+ unmappableCharacterAction = CodingErrorAction.REPORT;
+ replacementChars = "\ufffd";
}
/**
- * Gets the average number of characters created by this decoder for a
+ * Returns the average number of characters created by this decoder for a
* single input byte.
- *
- * @return the average number of characters created by this decoder for a
- * single input byte.
*/
public final float averageCharsPerByte() {
- return averChars;
+ return averageCharsPerByte;
}
/**
- * Gets the <code>Charset</code> which this decoder uses.
- *
- * @return the <code>Charset</code> which this decoder uses.
+ * Returns the {@link Charset} which this decoder uses.
*/
public final Charset charset() {
return cs;
@@ -203,10 +181,9 @@ public abstract class CharsetDecoder {
* @throws CharacterCodingException
* if another exception happened during the decode operation.
*/
- public final CharBuffer decode(ByteBuffer in)
- throws CharacterCodingException {
+ public final CharBuffer decode(ByteBuffer in) throws CharacterCodingException {
reset();
- int length = (int) (in.remaining() * averChars);
+ int length = (int) (in.remaining() * averageCharsPerByte);
CharBuffer output = CharBuffer.allocate(length);
CoderResult result = null;
while (true) {
@@ -239,12 +216,10 @@ public abstract class CharsetDecoder {
/*
* checks the result whether it needs to throw CharacterCodingException.
*/
- private void checkCoderResult(CoderResult result)
- throws CharacterCodingException {
- if (result.isMalformed() && malformAction == CodingErrorAction.REPORT) {
+ private void checkCoderResult(CoderResult result) throws CharacterCodingException {
+ if (result.isMalformed() && malformedInputAction == CodingErrorAction.REPORT) {
throw new MalformedInputException(result.length());
- } else if (result.isUnmappable()
- && unmapAction == CodingErrorAction.REPORT) {
+ } else if (result.isUnmappable() && unmappableCharacterAction == CodingErrorAction.REPORT) {
throw new UnmappableCharacterException(result.length());
}
}
@@ -368,16 +343,16 @@ public abstract class CharsetDecoder {
return result;
}
// set coding error handle action
- action = malformAction;
+ action = malformedInputAction;
if (result.isUnmappable()) {
- action = unmapAction;
+ action = unmappableCharacterAction;
}
// If the action is IGNORE or REPLACE, we should continue decoding.
if (action == CodingErrorAction.REPLACE) {
- if (out.remaining() < replace.length()) {
+ if (out.remaining() < replacementChars.length()) {
return CoderResult.OVERFLOW;
}
- out.put(replace);
+ out.put(replacementChars);
} else {
if (action != CodingErrorAction.IGNORE)
return result;
@@ -585,25 +560,19 @@ public abstract class CharsetDecoder {
}
/**
- * Gets this decoder's <code>CodingErrorAction</code> when malformed input
+ * Returns this decoder's <code>CodingErrorAction</code> when malformed input
* occurred during the decoding process.
- *
- * @return this decoder's <code>CodingErrorAction</code> when malformed
- * input occurred during the decoding process.
*/
public CodingErrorAction malformedInputAction() {
- return malformAction;
+ return malformedInputAction;
}
/**
- * Gets the maximum number of characters which can be created by this
+ * Returns the maximum number of characters which can be created by this
* decoder for one input byte, must be positive.
- *
- * @return the maximum number of characters which can be created by this
- * decoder for one input byte, must be positive.
*/
public final float maxCharsPerByte() {
- return maxChars;
+ return maxCharsPerByte;
}
/**
@@ -623,7 +592,7 @@ public abstract class CharsetDecoder {
if (newAction == null) {
throw new IllegalArgumentException();
}
- malformAction = newAction;
+ malformedInputAction = newAction;
implOnMalformedInput(newAction);
return this;
}
@@ -645,18 +614,16 @@ public abstract class CharsetDecoder {
if (newAction == null) {
throw new IllegalArgumentException();
}
- unmapAction = newAction;
+ unmappableCharacterAction = newAction;
implOnUnmappableCharacter(newAction);
return this;
}
/**
- * Gets the replacement string, which is never null or empty.
- *
- * @return the replacement string, cannot be null or empty.
+ * Returns the replacement string, which is never null or empty.
*/
public final String replacement() {
- return replace;
+ return replacementChars;
}
/**
@@ -667,7 +634,7 @@ public abstract class CharsetDecoder {
* {@link #implReplaceWith(String) implReplaceWith} method with the given
* new replacement as argument.
*
- * @param newReplacement
+ * @param replacement
* the replacement string, cannot be null or empty. Its length
* cannot be larger than {@link #maxCharsPerByte()}.
* @return this decoder.
@@ -675,15 +642,19 @@ public abstract class CharsetDecoder {
* if the given replacement cannot satisfy the requirement
* mentioned above.
*/
- public final CharsetDecoder replaceWith(String newReplacement) {
- if (newReplacement == null || newReplacement.isEmpty()) {
- throw new IllegalArgumentException("Replacement string cannot be null or empty");
+ public final CharsetDecoder replaceWith(String replacement) {
+ if (replacement == null) {
+ throw new IllegalArgumentException("replacement == null");
}
- if (newReplacement.length() > maxChars) {
- throw new IllegalArgumentException("Replacement string cannot be longer than max characters per byte");
+ if (replacement.isEmpty()) {
+ throw new IllegalArgumentException("replacement.isEmpty()");
}
- replace = newReplacement;
- implReplaceWith(newReplacement);
+ if (replacement.length() > maxCharsPerByte()) {
+ throw new IllegalArgumentException("replacement length > maxCharsPerByte: " +
+ replacement.length() + " > " + maxCharsPerByte());
+ }
+ replacementChars = replacement;
+ implReplaceWith(replacement);
return this;
}
@@ -701,13 +672,10 @@ public abstract class CharsetDecoder {
}
/**
- * Gets this decoder's <code>CodingErrorAction</code> when an unmappable
+ * Returns this decoder's <code>CodingErrorAction</code> when an unmappable
* character error occurred during the decoding process.
- *
- * @return this decoder's <code>CodingErrorAction</code> when an
- * unmappable character error occurred during the decoding process.
*/
public CodingErrorAction unmappableCharacterAction() {
- return unmapAction;
+ return unmappableCharacterAction;
}
}
diff --git a/luni/src/main/java/java/nio/charset/CharsetEncoder.java b/luni/src/main/java/java/nio/charset/CharsetEncoder.java
index 8208a56..d2d7efb 100644
--- a/luni/src/main/java/java/nio/charset/CharsetEncoder.java
+++ b/luni/src/main/java/java/nio/charset/CharsetEncoder.java
@@ -77,42 +77,27 @@ import libcore.icu.CharsetEncoderICU;
* @see java.nio.charset.CharsetDecoder
*/
public abstract class CharsetEncoder {
-
- /*
- * internal status consts
- */
private static final int READY = 0;
private static final int ONGOING = 1;
private static final int END = 2;
private static final int FLUSH = 3;
private static final int INIT = 4;
- // the Charset which creates this encoder
- private Charset cs;
+ private final Charset cs;
- // average bytes per character created by this encoder
- private float averBytes;
+ private final float averageBytesPerChar;
+ private final float maxBytesPerChar;
- // maximum bytes per character can be created by this encoder
- private float maxBytes;
+ private byte[] replacementBytes;
- // replacement byte array
- private byte[] replace;
-
- // internal status
private int status;
-
// internal status indicates encode(CharBuffer) operation is finished
private boolean finished;
- // action for malformed input
- private CodingErrorAction malformAction;
+ private CodingErrorAction malformedInputAction;
+ private CodingErrorAction unmappableCharacterAction;
- // action for unmapped char input
- private CodingErrorAction unmapAction;
-
- // decoder instance for this encoder's charset, used for replacement value
- // checking
+ // decoder instance for this encoder's charset, used for replacement value checking
private CharsetDecoder decoder;
/**
@@ -153,29 +138,20 @@ public abstract class CharsetEncoder {
throw new IllegalArgumentException("averageBytesPerChar is greater than maxBytesPerChar");
}
this.cs = cs;
- averBytes = averageBytesPerChar;
- maxBytes = maxBytesPerChar;
+ this.averageBytesPerChar = averageBytesPerChar;
+ this.maxBytesPerChar = maxBytesPerChar;
status = INIT;
- malformAction = CodingErrorAction.REPORT;
- unmapAction = CodingErrorAction.REPORT;
- if (this instanceof CharsetEncoderICU) {
- // The RI enforces unnecessary restrictions on the replacement bytes. We trust ICU to
- // know what it's doing. This lets us support EUC-JP, SCSU, and Shift_JIS.
- uncheckedReplaceWith(replacement);
- } else {
- replaceWith(replacement);
- }
+ malformedInputAction = CodingErrorAction.REPORT;
+ unmappableCharacterAction = CodingErrorAction.REPORT;
+ replaceWith(replacement);
}
/**
- * Gets the average number of bytes created by this encoder for a single
+ * Returns the average number of bytes created by this encoder for a single
* input character.
- *
- * @return the average number of bytes created by this encoder for a single
- * input character.
*/
public final float averageBytesPerChar() {
- return averBytes;
+ return averageBytesPerChar;
}
/**
@@ -206,8 +182,8 @@ public abstract class CharsetEncoder {
if (status != READY) {
throw new IllegalStateException("encoding already in progress");
}
- CodingErrorAction malformBak = malformAction;
- CodingErrorAction unmapBak = unmapAction;
+ CodingErrorAction malformBak = malformedInputAction;
+ CodingErrorAction unmapBak = unmappableCharacterAction;
onMalformedInput(CodingErrorAction.REPORT);
onUnmappableCharacter(CodingErrorAction.REPORT);
boolean result = true;
@@ -250,9 +226,7 @@ public abstract class CharsetEncoder {
}
/**
- * Gets the <code>Charset</code> which this encoder uses.
- *
- * @return the <code>Charset</code> which this encoder uses.
+ * Returns the {@link Charset} which this encoder uses.
*/
public final Charset charset() {
return cs;
@@ -294,7 +268,7 @@ public abstract class CharsetEncoder {
return ByteBuffer.allocate(0);
}
reset();
- int length = (int) (in.remaining() * averBytes);
+ int length = (int) (in.remaining() * averageBytesPerChar);
ByteBuffer output = ByteBuffer.allocate(length);
CoderResult result = null;
while (true) {
@@ -337,9 +311,9 @@ public abstract class CharsetEncoder {
* checks the result whether it needs to throw CharacterCodingException.
*/
private void checkCoderResult(CoderResult result) throws CharacterCodingException {
- if (malformAction == CodingErrorAction.REPORT && result.isMalformed() ) {
+ if (malformedInputAction == CodingErrorAction.REPORT && result.isMalformed() ) {
throw new MalformedInputException(result.length());
- } else if (unmapAction == CodingErrorAction.REPORT && result.isUnmappable()) {
+ } else if (unmappableCharacterAction == CodingErrorAction.REPORT && result.isUnmappable()) {
throw new UnmappableCharacterException(result.length());
}
}
@@ -452,17 +426,17 @@ public abstract class CharsetEncoder {
status = endOfInput ? END : ONGOING;
return result;
}
- CodingErrorAction action = malformAction;
+ CodingErrorAction action = malformedInputAction;
if (result.isUnmappable()) {
- action = unmapAction;
+ action = unmappableCharacterAction;
}
// If the action is IGNORE or REPLACE, we should continue
// encoding.
if (action == CodingErrorAction.REPLACE) {
- if (out.remaining() < replace.length) {
+ if (out.remaining() < replacementBytes.length) {
return CoderResult.OVERFLOW;
}
- out.put(replace);
+ out.put(replacementBytes);
} else {
if (action != CodingErrorAction.IGNORE) {
return result;
@@ -632,25 +606,19 @@ public abstract class CharsetEncoder {
}
/**
- * Gets this encoder's <code>CodingErrorAction</code> when a malformed
+ * Returns this encoder's <code>CodingErrorAction</code> when a malformed
* input error occurred during the encoding process.
- *
- * @return this encoder's <code>CodingErrorAction</code> when a malformed
- * input error occurred during the encoding process.
*/
public CodingErrorAction malformedInputAction() {
- return malformAction;
+ return malformedInputAction;
}
/**
- * Gets the maximum number of bytes which can be created by this encoder for
+ * Returns the maximum number of bytes which can be created by this encoder for
* one input character, must be positive.
- *
- * @return the maximum number of bytes which can be created by this encoder
- * for one input character, must be positive.
*/
public final float maxBytesPerChar() {
- return maxBytes;
+ return maxBytesPerChar;
}
/**
@@ -670,7 +638,7 @@ public abstract class CharsetEncoder {
if (newAction == null) {
throw new IllegalArgumentException("newAction == null");
}
- malformAction = newAction;
+ malformedInputAction = newAction;
implOnMalformedInput(newAction);
return this;
}
@@ -692,18 +660,16 @@ public abstract class CharsetEncoder {
if (newAction == null) {
throw new IllegalArgumentException("newAction == null");
}
- unmapAction = newAction;
+ unmappableCharacterAction = newAction;
implOnUnmappableCharacter(newAction);
return this;
}
/**
- * Gets the replacement byte array, which is never null or empty.
- *
- * @return the replacement byte array, cannot be null or empty.
+ * Returns the replacement byte array, which is never null or empty.
*/
public final byte[] replacement() {
- return replace;
+ return replacementBytes;
}
/**
@@ -728,25 +694,29 @@ public abstract class CharsetEncoder {
if (replacement == null) {
throw new IllegalArgumentException("replacement == null");
}
- if (replacement.length == 0 || maxBytes < replacement.length) {
- throw new IllegalArgumentException("bad replacement length: " + replacement.length);
+ if (replacement.length == 0) {
+ throw new IllegalArgumentException("replacement.length == 0");
+ }
+ if (replacement.length > maxBytesPerChar()) {
+ throw new IllegalArgumentException("replacement length > maxBytesPerChar: " +
+ replacement.length + " > " + maxBytesPerChar());
}
if (!isLegalReplacement(replacement)) {
throw new IllegalArgumentException("bad replacement: " + Arrays.toString(replacement));
}
- uncheckedReplaceWith(replacement);
- return this;
+ return uncheckedReplaceWith(replacement);
}
- private final void uncheckedReplaceWith(byte[] replacement) {
+ public final CharsetEncoder uncheckedReplaceWith(byte[] replacement) {
// It seems like a bug, but the RI doesn't clone, and we have tests that check we don't.
- replace = replacement;
- implReplaceWith(replace);
+ this.replacementBytes = replacement;
+ implReplaceWith(replacementBytes);
+ return this;
}
/**
* Resets this encoder. This method will reset the internal status and then
- * calla <code>implReset()</code> to reset any status related to the
+ * calls <code>implReset()</code> to reset any status related to the
* specific charset.
*
* @return this encoder.
@@ -758,13 +728,10 @@ public abstract class CharsetEncoder {
}
/**
- * Gets this encoder's <code>CodingErrorAction</code> when unmappable
+ * Returns this encoder's <code>CodingErrorAction</code> when unmappable
* character occurred during encoding process.
- *
- * @return this encoder's <code>CodingErrorAction</code> when unmappable
- * character occurred during encoding process.
*/
public CodingErrorAction unmappableCharacterAction() {
- return unmapAction;
+ return unmappableCharacterAction;
}
}
diff --git a/luni/src/main/java/libcore/icu/CharsetDecoderICU.java b/luni/src/main/java/libcore/icu/CharsetDecoderICU.java
index d42368e..db7371b 100644
--- a/luni/src/main/java/libcore/icu/CharsetDecoderICU.java
+++ b/luni/src/main/java/libcore/icu/CharsetDecoderICU.java
@@ -74,14 +74,7 @@ public final class CharsetDecoderICU extends CharsetDecoder {
}
@Override protected void implReplaceWith(String newReplacement) {
- if (converterHandle > 0) {
- // TODO: is this the right test? we're providing characters.
- int max = NativeConverter.getMaxBytesPerChar(converterHandle);
- if (newReplacement.length() > max) {
- throw new IllegalArgumentException("replacement length (" + newReplacement.length() + ") > maximum (" + max + ")");
- }
- updateCallback();
- }
+ updateCallback();
}
@Override protected final void implOnMalformedInput(CodingErrorAction newAction) {
diff --git a/luni/src/main/java/libcore/icu/CharsetEncoderICU.java b/luni/src/main/java/libcore/icu/CharsetEncoderICU.java
index 15b2788..1ea261c 100644
--- a/luni/src/main/java/libcore/icu/CharsetEncoderICU.java
+++ b/luni/src/main/java/libcore/icu/CharsetEncoderICU.java
@@ -76,7 +76,6 @@ public final class CharsetEncoderICU extends CharsetEncoder {
byte[] replacement = makeReplacement(icuCanonicalName, address);
CharsetEncoderICU result = new CharsetEncoderICU(cs, averageBytesPerChar, maxBytesPerChar, replacement, address);
address = 0; // CharsetEncoderICU has taken ownership; its finalizer will do the free.
- result.updateCallback();
return result;
} finally {
if (address != 0) {
@@ -96,16 +95,20 @@ public final class CharsetEncoderICU extends CharsetEncoder {
}
private CharsetEncoderICU(Charset cs, float averageBytesPerChar, float maxBytesPerChar, byte[] replacement, long address) {
- super(cs, averageBytesPerChar, maxBytesPerChar, replacement);
+ // We lie to our superclass and let it think we're using the default replacement...
+ super(cs, averageBytesPerChar, maxBytesPerChar);
this.converterHandle = address;
+ // ...and then when our native peer is accessible, we tell it the truth.
+ // We still can't just call replaceWith because the RI enforces unnecessary restrictions
+ // on the replacement bytes. We trust ICU to know what it's doing. Doing so lets us support
+ // EUC-JP, SCSU, and Shift_JIS with ICU's suggested replacement bytes.
+ uncheckedReplaceWith(replacement);
}
@Override protected void implReplaceWith(byte[] newReplacement) {
+ // This hack is necessary because CharsetEncoder calls this abstract method from its
+ // constructor, and subclasses like CharsetEncoderICU aren't initialized at that point.
if (converterHandle != 0) {
- int max = NativeConverter.getMaxBytesPerChar(converterHandle);
- if (newReplacement.length > max) {
- throw new IllegalArgumentException("replacement length (" + newReplacement.length + ") > maximum (" + max + ")");
- }
updateCallback();
}
}
diff --git a/luni/src/main/java/libcore/icu/NativeConverter.java b/luni/src/main/java/libcore/icu/NativeConverter.java
index 8769032..2d8630c 100644
--- a/luni/src/main/java/libcore/icu/NativeConverter.java
+++ b/luni/src/main/java/libcore/icu/NativeConverter.java
@@ -58,9 +58,9 @@ public final class NativeConverter {
return setCallbackDecode(converterHandle,
translateCodingErrorAction(decoder.malformedInputAction()),
translateCodingErrorAction(decoder.unmappableCharacterAction()),
- decoder.replacement().toCharArray());
+ decoder.replacement());
}
- private static native int setCallbackDecode(long converterHandle, int onMalformedInput, int onUnmappableInput, char[] subChars);
+ private static native int setCallbackDecode(long converterHandle, int onMalformedInput, int onUnmappableInput, String subChars);
public static int setCallbackEncode(long converterHandle, CharsetEncoder encoder) {
return setCallbackEncode(converterHandle,
diff --git a/luni/src/main/native/NativeConverter.cpp b/luni/src/main/native/NativeConverter.cpp
index ff7b0d0..544bd99 100644
--- a/luni/src/main/native/NativeConverter.cpp
+++ b/luni/src/main/native/NativeConverter.cpp
@@ -20,6 +20,7 @@
#include "JniConstants.h"
#include "ScopedLocalRef.h"
#include "ScopedPrimitiveArray.h"
+#include "ScopedStringChars.h"
#include "ScopedUtfChars.h"
#include "UniquePtr.h"
#include "cutils/log.h"
@@ -35,16 +36,18 @@
#define NativeConverter_IGNORE 1
#define NativeConverter_REPLACE 2
+#define MAX_REPLACEMENT_LENGTH 32 // equivalent to UCNV_ERROR_BUFFER_LENGTH
+
struct DecoderCallbackContext {
- int length;
- UChar subUChars[256];
+ UChar replacementChars[MAX_REPLACEMENT_LENGTH];
+ size_t replacementCharCount;
UConverterToUCallback onUnmappableInput;
UConverterToUCallback onMalformedInput;
};
struct EncoderCallbackContext {
- int length;
- char subBytes[256];
+ char replacementBytes[MAX_REPLACEMENT_LENGTH];
+ size_t replacementByteCount;
UConverterFromUCallback onUnmappableInput;
UConverterFromUCallback onMalformedInput;
};
@@ -367,7 +370,7 @@ static void encoderReplaceCallback(const void* rawContext,
}
const EncoderCallbackContext* context = reinterpret_cast<const EncoderCallbackContext*>(rawContext);
*err = U_ZERO_ERROR;
- ucnv_cbFromUWriteBytes(fromArgs, context->subBytes, context->length, 0, err);
+ ucnv_cbFromUWriteBytes(fromArgs, context->replacementBytes, context->replacementByteCount, 0, err);
}
static UConverterFromUCallback getFromUCallback(int32_t mode) {
@@ -380,42 +383,34 @@ static UConverterFromUCallback getFromUCallback(int32_t mode) {
}
static jint NativeConverter_setCallbackEncode(JNIEnv* env, jclass, jlong address,
- jint onMalformedInput, jint onUnmappableInput, jbyteArray subBytes) {
+ jint onMalformedInput, jint onUnmappableInput, jbyteArray javaReplacement) {
UConverter* cnv = toUConverter(address);
if (!cnv) {
return U_ILLEGAL_ARGUMENT_ERROR;
}
- UConverterFromUCallback fromUOldAction = NULL;
- const void* fromUOldContext = NULL;
- ucnv_getFromUCallBack(cnv, &fromUOldAction, const_cast<const void**>(&fromUOldContext));
-
- /* fromUOldContext can only be EncoderCallbackContext since
- * the converter created is private data for the decoder
- * and callbacks can only be set via this method!
- */
- EncoderCallbackContext* fromUNewContext = NULL;
- UConverterFromUCallback fromUNewAction = NULL;
- if (fromUOldContext == NULL) {
- fromUNewContext = new EncoderCallbackContext;
- fromUNewAction = CHARSET_ENCODER_CALLBACK;
- } else {
- fromUNewContext = const_cast<EncoderCallbackContext*>(
- reinterpret_cast<const EncoderCallbackContext*>(fromUOldContext));
- fromUNewAction = fromUOldAction;
- fromUOldAction = NULL;
- fromUOldContext = NULL;
- }
- fromUNewContext->onMalformedInput = getFromUCallback(onMalformedInput);
- fromUNewContext->onUnmappableInput = getFromUCallback(onUnmappableInput);
- ScopedByteArrayRO sub(env, subBytes);
- if (sub.get() == NULL) {
+
+ UConverterFromUCallback oldCallback = NULL;
+ const void* oldCallbackContext = NULL;
+ ucnv_getFromUCallBack(cnv, &oldCallback, const_cast<const void**>(&oldCallbackContext));
+
+ EncoderCallbackContext* callbackContext = const_cast<EncoderCallbackContext*>(
+ reinterpret_cast<const EncoderCallbackContext*>(oldCallbackContext));
+ if (callbackContext == NULL) {
+ callbackContext = new EncoderCallbackContext;
+ }
+
+ callbackContext->onMalformedInput = getFromUCallback(onMalformedInput);
+ callbackContext->onUnmappableInput = getFromUCallback(onUnmappableInput);
+
+ ScopedByteArrayRO replacementBytes(env, javaReplacement);
+ if (replacementBytes.get() == NULL) {
return U_ILLEGAL_ARGUMENT_ERROR;
}
- fromUNewContext->length = sub.size();
- memcpy(fromUNewContext->subBytes, sub.get(), sub.size());
+ memcpy(callbackContext->replacementBytes, replacementBytes.get(), replacementBytes.size());
+ callbackContext->replacementByteCount = replacementBytes.size();
+
UErrorCode errorCode = U_ZERO_ERROR;
- ucnv_setFromUCallBack(cnv, fromUNewAction, fromUNewContext, &fromUOldAction, &fromUOldContext,
- &errorCode);
+ ucnv_setFromUCallBack(cnv, CHARSET_ENCODER_CALLBACK, callbackContext, NULL, NULL, &errorCode);
return errorCode;
}
@@ -433,7 +428,7 @@ static void decoderReplaceCallback(const void* rawContext,
}
const DecoderCallbackContext* context = reinterpret_cast<const DecoderCallbackContext*>(rawContext);
*err = U_ZERO_ERROR;
- ucnv_cbToUWriteUChars(toArgs,context->subUChars, context->length, 0, err);
+ ucnv_cbToUWriteUChars(toArgs,context->replacementChars, context->replacementCharCount, 0, err);
}
static UConverterToUCallback getToUCallback(int32_t mode) {
@@ -470,43 +465,34 @@ static void CHARSET_DECODER_CALLBACK(const void* rawContext, UConverterToUnicode
}
static jint NativeConverter_setCallbackDecode(JNIEnv* env, jclass, jlong address,
- jint onMalformedInput, jint onUnmappableInput, jcharArray subChars) {
+ jint onMalformedInput, jint onUnmappableInput, jstring javaReplacement) {
UConverter* cnv = toUConverter(address);
if (cnv == NULL) {
return U_ILLEGAL_ARGUMENT_ERROR;
}
- UConverterToUCallback toUOldAction;
- const void* toUOldContext;
- ucnv_getToUCallBack(cnv, &toUOldAction, &toUOldContext);
-
- /* toUOldContext can only be DecoderCallbackContext since
- * the converter created is private data for the decoder
- * and callbacks can only be set via this method!
- */
- DecoderCallbackContext* toUNewContext = NULL;
- UConverterToUCallback toUNewAction = NULL;
- if (toUOldContext == NULL) {
- toUNewContext = new DecoderCallbackContext;
- toUNewAction = CHARSET_DECODER_CALLBACK;
- } else {
- toUNewContext = const_cast<DecoderCallbackContext*>(
- reinterpret_cast<const DecoderCallbackContext*>(toUOldContext));
- toUNewAction = toUOldAction;
- toUOldAction = NULL;
- toUOldContext = NULL;
- }
- toUNewContext->onMalformedInput = getToUCallback(onMalformedInput);
- toUNewContext->onUnmappableInput = getToUCallback(onUnmappableInput);
- ScopedCharArrayRO sub(env, subChars);
- if (sub.get() == NULL) {
+ UConverterToUCallback oldCallback;
+ const void* oldCallbackContext;
+ ucnv_getToUCallBack(cnv, &oldCallback, &oldCallbackContext);
+
+ DecoderCallbackContext* callbackContext = const_cast<DecoderCallbackContext*>(
+ reinterpret_cast<const DecoderCallbackContext*>(oldCallbackContext));
+ if (callbackContext == NULL) {
+ callbackContext = new DecoderCallbackContext;
+ }
+
+ callbackContext->onMalformedInput = getToUCallback(onMalformedInput);
+ callbackContext->onUnmappableInput = getToUCallback(onUnmappableInput);
+
+ ScopedStringChars replacement(env, javaReplacement);
+ if (replacement.get() == NULL) {
return U_ILLEGAL_ARGUMENT_ERROR;
}
- toUNewContext->length = sub.size();
- u_strncpy(toUNewContext->subUChars, sub.get(), sub.size());
+ u_strncpy(callbackContext->replacementChars, replacement.get(), replacement.size());
+ callbackContext->replacementCharCount = replacement.size();
+
UErrorCode errorCode = U_ZERO_ERROR;
- ucnv_setToUCallBack(cnv, toUNewAction, toUNewContext, &toUOldAction, &toUOldContext,
- &errorCode);
+ ucnv_setToUCallBack(cnv, CHARSET_DECODER_CALLBACK, callbackContext, NULL, NULL, &errorCode);
return errorCode;
}
@@ -520,9 +506,9 @@ static jbyteArray NativeConverter_getSubstitutionBytes(JNIEnv* env, jclass, jlon
return NULL;
}
UErrorCode status = U_ZERO_ERROR;
- char subBytes[10];
- int8_t len = sizeof(subBytes);
- ucnv_getSubstChars(cnv, subBytes, &len, &status);
+ char replacementBytes[MAX_REPLACEMENT_LENGTH];
+ int8_t len = sizeof(replacementBytes);
+ ucnv_getSubstChars(cnv, replacementBytes, &len, &status);
if (!U_SUCCESS(status)) {
return env->NewByteArray(0);
}
@@ -530,7 +516,7 @@ static jbyteArray NativeConverter_getSubstitutionBytes(JNIEnv* env, jclass, jlon
if (result == NULL) {
return NULL;
}
- env->SetByteArrayRegion(result, 0, len, reinterpret_cast<jbyte*>(subBytes));
+ env->SetByteArrayRegion(result, 0, len, reinterpret_cast<jbyte*>(replacementBytes));
return result;
}
@@ -614,7 +600,7 @@ static JNINativeMethod gMethods[] = {
NATIVE_METHOD(NativeConverter, openConverter, "(Ljava/lang/String;)J"),
NATIVE_METHOD(NativeConverter, resetByteToChar, "(J)V"),
NATIVE_METHOD(NativeConverter, resetCharToByte, "(J)V"),
- NATIVE_METHOD(NativeConverter, setCallbackDecode, "(JII[C)I"),
+ NATIVE_METHOD(NativeConverter, setCallbackDecode, "(JIILjava/lang/String;)I"),
NATIVE_METHOD(NativeConverter, setCallbackEncode, "(JII[B)I"),
};
int register_libcore_icu_NativeConverter(JNIEnv* env) {