
io.airlift.jcodings.transcode.EConv Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of joni Show documentation
Show all versions of joni Show documentation
Java port of Oniguruma: http://www.geocities.jp/kosako3/oniguruma
that uses byte arrays directly instead of java Strings and chars
/*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
* of the Software, and to permit persons to whom the Software is furnished to do
* so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package io.airlift.jcodings.transcode;
import static io.airlift.jcodings.util.CaseInsensitiveBytesHash.caseInsensitiveEquals;
import io.airlift.jcodings.Encoding;
import io.airlift.jcodings.Ptr;
import io.airlift.jcodings.exception.InternalException;
import io.airlift.jcodings.specific.UTF32BEEncoding;
import java.util.Arrays;
public final class EConv implements EConvFlags {
int flags;
public byte[] source, destination; // source/destination encoding names
boolean started = false;
public byte[] replacementString;
public int replacementLength;
public byte[] replacementEncoding;
Buffer inBuf = new Buffer();
public EConvElement[] elements;
public int numTranscoders;
int numFinished;
public Transcoding lastTranscoding;
public final LastError lastError = new LastError();
public Encoding sourceEncoding, destinationEncoding;
@Override
public String toString() {
return new String(source) + " => " + new String(destination);
}
EConv(int nHint) {
if (nHint <= 0) nHint = 1;
elements = new EConvElement[nHint];
lastError.result = EConvResult.SourceBufferEmpty;
}
public static final class EConvElement extends Buffer {
EConvElement(Transcoding transcoding) {
this.transcoding = transcoding;
lastResult = EConvResult.SourceBufferEmpty;
}
public final Transcoding transcoding;
EConvResult lastResult;
@Override
public String toString() {
String s = "EConv " + transcoding.toString() + "\n";
s += " last result: " + lastResult;
return s;
}
}
public static final class LastError {
EConvResult result;
Transcoding errorTranscoding;
byte[] source, destination;
byte[] errorBytes;
int errorBytesP, errorBytesLength;
int readAgainLength;
void reset() {
result = null;
errorTranscoding = null;
source = destination = null;
errorBytes = null;
errorBytesP = errorBytesLength = 0;
readAgainLength = 0;
}
public EConvResult getResult() {
return result;
}
public Transcoding getErrorTranscoding() {
return errorTranscoding;
}
public byte[] getSource() {
return source;
}
public byte[] getDestination() {
return destination;
}
public byte[] getErrorBytes() {
return errorBytes;
}
public int getErrorBytesP() {
return errorBytesP;
}
public int getErrorBytesLength() {
return errorBytesLength;
}
public int getReadAgainLength() {
return readAgainLength;
}
@Override
public String toString() {
String s = "Last Error " + (source == null ? "null" : new String(source)) + " => " + (destination == null ? "null" : new String(destination))
+ "\n";
s += " result: " + result.toString() + "\n";
s += " error bytes: " + (errorBytes == null ? "null" : new String(errorBytes, errorBytesP, errorBytesP + errorBytesLength)) + "\n";
s += " read again length: " + readAgainLength;
return s;
}
}
static final byte[] NULL_STRING = new byte[0];
static final int[] NULL_POINTER = new int[0];
static boolean decorator(byte[] source, byte[] destination) {
return source.length == 0;
}
/* rb_econv_add_transcoder_at */
void addTranscoderAt(Transcoder transcoder, int i) {
if (numTranscoders == elements.length) {
EConvElement[] tmp = new EConvElement[elements.length * 2];
System.arraycopy(elements, 0, tmp, 0, i);
System.arraycopy(elements, i, tmp, i + 1, elements.length - i);
elements = tmp;
} else {
System.arraycopy(elements, i, elements, i + 1, elements.length - i - 1);
}
elements[i] = new EConvElement(transcoder.transcoding(0));
elements[i].allocate(4096);
numTranscoders++;
if (!decorator(transcoder.source, transcoder.destination)) {
for (int j = numTranscoders - 1; i <= j; j--) {
Transcoding tc = elements[j].transcoding;
Transcoder tr = tc.transcoder;
if (!decorator(tr.source, tr.destination)) {
lastTranscoding = tc;
break;
}
}
}
}
/* trans_sweep */
private int transSweep(byte[] in, Ptr inPtr, int inStop, byte[] out, Ptr outPtr, int outStop, int flags, int start) {
boolean try_ = true;
Ptr ipp = null;
Ptr opp = null;
while (try_) {
try_ = false;
for (int i = start; i < numTranscoders; i++) {
EConvElement te = elements[i];
final int is, os;
final byte[] ibytes, obytes;
EConvElement previousTE = null;
boolean ippIsStart = false;
boolean oppIsEnd = false;
if (i == 0) {
ipp = inPtr;
is = inStop;
ibytes = in;
} else {
previousTE = elements[i - 1];
ipp = new Ptr(previousTE.dataStart);
ippIsStart = true;
is = previousTE.dataEnd;
ibytes = previousTE.bytes;
}
if (i == numTranscoders - 1) {
opp = outPtr;
os = outStop;
obytes = out;
} else {
if (te.bufStart != te.dataStart) {
int len = te.dataEnd - te.dataStart;
int off = te.dataStart - te.bufStart;
System.arraycopy(te.bytes, te.dataStart, te.bytes, te.bufStart, len);
te.dataStart = te.bufStart;
te.dataEnd -= off;
}
opp = new Ptr(te.dataEnd);
oppIsEnd = true;
os = te.bufEnd;
obytes = te.bytes;
}
int f = flags;
if (numFinished != i) f |= PARTIAL_INPUT;
if (i == 0 && (flags & AFTER_OUTPUT) != 0) {
start = 1;
flags &= ~AFTER_OUTPUT;
}
if (i != 0) f &= ~AFTER_OUTPUT;
int iold = ipp.p;
int oold = opp.p;
EConvResult res;
te.lastResult = res = te.transcoding.convert(ibytes, ipp, is, obytes, opp, os, f);
if (ippIsStart) previousTE.dataStart = ipp.p;
if (oppIsEnd) te.dataEnd = opp.p;
if (iold != ipp.p || oold != opp.p) try_ = true;
switch (res) {
case InvalidByteSequence:
case IncompleteInput:
case UndefinedConversion:
case AfterOutput:
return i;
case DestinationBufferFull:
case SourceBufferEmpty:
break;
case Finished:
numFinished = i + 1;
break;
}
}
}
return -1;
}
/* rb_trans_conv */
private EConvResult transConv(byte[] in, Ptr inPtr, int inStop, byte[] out, Ptr outPtr, int outStop, int flags, Ptr resultPositionPtr) {
// null check
if (elements[0].lastResult == EConvResult.AfterOutput) elements[0].lastResult = EConvResult.SourceBufferEmpty;
for (int i = numTranscoders - 1; 0 <= i; i--) {
switch (elements[i].lastResult) {
case InvalidByteSequence:
case IncompleteInput:
case UndefinedConversion:
case AfterOutput:
case Finished:
return transConvNeedReport(in, inPtr, inStop, out, outPtr, outStop, flags, resultPositionPtr, i + 1, i);
case DestinationBufferFull:
case SourceBufferEmpty:
break;
default:
throw new InternalException("unexpected transcode last result");
}
}
/* /^[sd]+$/ is confirmed. but actually /^s*d*$/. */
if (elements[numTranscoders - 1].lastResult == EConvResult.DestinationBufferFull && (flags & AFTER_OUTPUT) != 0) {
EConvResult res = transConv(NULL_STRING, Ptr.NULL, 0, out, outPtr, outStop, (flags & ~AFTER_OUTPUT) | PARTIAL_INPUT, resultPositionPtr);
return res.isSourceBufferEmpty() ? EConvResult.AfterOutput : res;
}
return transConvNeedReport(in, inPtr, inStop, out, outPtr, outStop, flags, resultPositionPtr, 0, -1);
}
private EConvResult transConvNeedReport(byte[] in, Ptr inPtr, int inStop, byte[] out, Ptr outPtr, int outStop, int flags, Ptr resultPositionPtr,
int sweepStart, int needReportIndex) {
do {
needReportIndex = transSweep(in, inPtr, inStop, out, outPtr, outStop, flags, sweepStart);
sweepStart = needReportIndex + 1;
} while (needReportIndex != -1 && needReportIndex != numTranscoders - 1);
for (int i = numTranscoders - 1; i >= 0; i--) {
if (elements[i].lastResult != EConvResult.SourceBufferEmpty) {
EConvResult res = elements[i].lastResult;
switch (res) {
case InvalidByteSequence:
case IncompleteInput:
case UndefinedConversion:
case AfterOutput:
elements[i].lastResult = EConvResult.SourceBufferEmpty;
}
if (resultPositionPtr != null) resultPositionPtr.p = i;
return res;
}
}
if (resultPositionPtr != null) resultPositionPtr.p = -1;
return EConvResult.SourceBufferEmpty;
}
/* rb_econv_convert0 */
private EConvResult convertInternal(byte[] in, Ptr inPtr, int inStop, byte[] out, Ptr outPtr, int outStop, int flags) {
lastError.reset();
EConvResult res;
int len;
if (numTranscoders == 0) {
if (inBuf.bytes != null && inBuf.dataStart != inBuf.dataEnd) {
if (outStop - outPtr.p < inBuf.dataEnd - inBuf.dataStart) {
len = outStop - outPtr.p;
System.arraycopy(inBuf, inBuf.dataStart, out, outPtr.p, len);
outPtr.p = outStop;
inBuf.dataStart += len;
return convertInternalResult(EConvResult.DestinationBufferFull, null);
}
len = inBuf.dataEnd - inBuf.dataStart;
System.arraycopy(inBuf.bytes, inBuf.dataStart, out, outPtr.p, len);
outPtr.p += len;
inBuf.dataStart = inBuf.dataEnd = inBuf.bufStart;
if ((flags & AFTER_OUTPUT) != 0) return convertInternalResult(EConvResult.AfterOutput, null);
}
if (outStop - outPtr.p < inStop - inPtr.p) {
len = outStop - outPtr.p;
} else {
len = inStop - inPtr.p;
}
if (len > 0 && (flags & AFTER_OUTPUT) != 0) {
out[outPtr.p++] = in[inPtr.p++];
return convertInternalResult(EConvResult.AfterOutput, null);
}
System.arraycopy(in, inPtr.p, out, outPtr.p, len);
outPtr.p += len;
inPtr.p += len;
if (inPtr.p != inStop) {
res = EConvResult.DestinationBufferFull;
} else if ((flags & PARTIAL_INPUT) != 0) {
res = EConvResult.SourceBufferEmpty;
} else {
res = EConvResult.Finished;
}
return convertInternalResult(res, null);
}
boolean hasOutput = false;
EConvElement elem = elements[numTranscoders - 1];
if (elem.bytes != null) {
int dataStart = elem.dataStart;
int dataEnd = elem.dataEnd;
byte[] data = elem.bytes;
if (dataStart != dataEnd) {
if (outStop - outPtr.p < dataEnd - dataStart) {
len = outStop - outPtr.p;
System.arraycopy(data, dataStart, out, outPtr.p, len);
outPtr.p = outStop;
elem.dataStart += len;
return convertInternalResult(EConvResult.DestinationBufferFull, null);
}
len = dataEnd - dataStart;
System.arraycopy(data, dataStart, out, outPtr.p, len);
outPtr.p += len;
elem.dataStart = elem.dataEnd = elem.bufStart;
hasOutput = true;
}
}
Ptr resultPosition = new Ptr(0);
if (inBuf != null && inBuf.dataStart != inBuf.dataEnd) {
Ptr inDataStartPtr = new Ptr(inBuf.dataStart);
res = transConv(inBuf.bytes, inDataStartPtr, inBuf.dataEnd, out, outPtr, outStop, (flags & ~AFTER_OUTPUT) | PARTIAL_INPUT, resultPosition);
inBuf.dataStart = inDataStartPtr.p;
if (!res.isSourceBufferEmpty()) return convertInternalResult(EConvResult.SourceBufferEmpty, resultPosition);
}
if (hasOutput && (flags & AFTER_OUTPUT) != 0 && inPtr.p != inStop) {
inStop = inPtr.p;
res = transConv(in, inPtr, inStop, out, outPtr, outStop, flags, resultPosition);
if (res.isSourceBufferEmpty()) res = EConvResult.AfterOutput;
} else if ((flags & AFTER_OUTPUT) != 0 || numTranscoders == 1) {
res = transConv(in, inPtr, inStop, out, outPtr, outStop, flags, resultPosition);
} else {
flags |= AFTER_OUTPUT;
do {
res = transConv(in, inPtr, inStop, out, outPtr, outStop, flags, resultPosition);
} while (res.isAfterOutput());
}
return convertInternalResult(res, resultPosition);
}
private EConvResult convertInternalResult(EConvResult res, Ptr resultPosition) {
lastError.result = res;
switch (res) {
case InvalidByteSequence:
case IncompleteInput:
case UndefinedConversion:
Transcoding errorTranscoding = elements[resultPosition.p].transcoding;
lastError.errorTranscoding = errorTranscoding;
lastError.source = errorTranscoding.transcoder.source;
lastError.destination = errorTranscoding.transcoder.destination;
lastError.errorBytes = errorTranscoding.readBuf;
lastError.errorBytesP = 0;
lastError.errorBytesLength = errorTranscoding.recognizedLength;
lastError.readAgainLength = errorTranscoding.readAgainLength;
}
return res;
}
/* rb_econv_convert */
public EConvResult convert(byte[] in, Ptr inPtr, int inStop, byte[] out, Ptr outPtr, int outStop, int flags) {
started = true;
if (in == null || inPtr == null) {
in = NULL_STRING;
inPtr = Ptr.NULL;
inStop = 0;
}
if (out == null || outPtr == null) {
out = NULL_STRING;
outPtr = Ptr.NULL;
outStop = 0;
}
resume: while (true) {
EConvResult ret = convertInternal(in, inPtr, inStop, out, outPtr, outStop, flags);
if (ret.isInvalidByteSequence() || ret.isIncompleteInput()) {
switch (this.flags & INVALID_MASK) {
case INVALID_REPLACE:
if (outputReplacementCharacter() == 0) continue resume;
}
}
if (ret.isUndefinedConversion()) {
switch (this.flags & UNDEF_MASK) {
case UNDEF_REPLACE:
if (outputReplacementCharacter() == 0) continue resume;
break;
case UNDEF_HEX_CHARREF:
if (outputHexCharref() == 0) continue resume;
break;
}
}
return ret;
}
}
/* output_hex_charref */
private int outputHexCharref() {
final byte[] utfBytes;
final int utfP;
int utfLen;
if (caseInsensitiveEquals(lastError.source, "UTF-32BE".getBytes())) {
utfBytes = lastError.errorBytes;
utfP = lastError.errorBytesP;
utfLen = lastError.errorBytesLength;
} else {
Ptr utfLenA = new Ptr();
// TODO: better calculation?
byte[] utfBuf = new byte[lastError.errorBytesLength * UTF32BEEncoding.INSTANCE.maxLength()];
utfBytes = allocateConvertedString(lastError.source, "UTF-32BE".getBytes(), lastError.errorBytes, lastError.errorBytesP, lastError.errorBytesLength, utfBuf, utfLenA);
if (utfBytes == null) return -1;
utfP = 0;
utfLen = utfLenA.p;
}
if (utfLen % 4 != 0) return -1;
int p = utfP;
while (4 <= utfLen) {
int u = 0; // long ??
u += (utfBytes[p] & 0xff) << 24;
u += (utfBytes[p + 1] & 0xff) << 16;
u += (utfBytes[p + 2] & 0xff) << 8;
u += (utfBytes[p + 3] & 0xff);
byte[] charrefbuf = String.format("%X;", u).getBytes(); // FIXME: use faster sprintf ??
if (insertOutput(charrefbuf, 0, charrefbuf.length, "US-ASCII".getBytes()) == -1) return -1;
p += 4;
utfLen -= 4;
}
return 0;
}
/* rb_econv_encoding_to_insert_output */
public byte[] encodingToInsertOutput() {
Transcoding transcoding = lastTranscoding;
if (transcoding == null) return NULL_STRING;
Transcoder transcoder = transcoding.transcoder;
return transcoder.compatibility.isEncoder() ? transcoder.source : transcoder.destination;
}
/* allocate_converted_string */
private static byte[] allocateConvertedString(byte[] source, byte[] destination, byte[] str, int strP, int strLen, byte[] callerDstBuf, Ptr dstLenPtr) {
int dstBufSize;
if (callerDstBuf != null) {
dstBufSize = callerDstBuf.length;
} else if (strLen == 0) {
dstBufSize = 1; // ??
} else {
dstBufSize = strLen;
}
EConv ec = TranscoderDB.open(source, destination, 0);
if (ec == null) return null;
byte[] dstStr;
if (callerDstBuf != null) {
dstStr = callerDstBuf;
} else {
dstStr = new byte[dstBufSize];
}
int dstLen = 0;
Ptr sp = new Ptr(strP);
Ptr dp = new Ptr(dstLen);
EConvResult res = ec.convert(str, sp, strP + strLen, dstStr, dp, dstBufSize, 0);
dstLen = dp.p;
while (res.isDestinationBufferFull()) {
dstBufSize *= 2;
byte[] tmp = new byte[dstBufSize];
System.arraycopy(dstStr, 0, tmp, 0, dstBufSize / 2);
dstStr = tmp;
dp.p = dstLen; // ??
res = ec.convert(str, sp, strP + strLen, dstStr, dp, dstBufSize, 0);
dstLen = dp.p;
}
if (!res.isFinished()) return null;
ec.close();
dstLenPtr.p = dstLen;
return dstStr;
}
/* rb_econv_insert_output */
public int insertOutput(byte[] str, int strP, int strLen, byte[] strEncoding) {
byte[] insertEncoding = encodingToInsertOutput();
byte[] insertBuf = null;
started = true;
if (strLen == 0) return 0;
final byte[] insertStr;
final int insertP;
final int insertLen;
if (caseInsensitiveEquals(insertEncoding, strEncoding)) {
insertStr = str;
insertP = 0;
insertLen = strLen;
} else {
Ptr insertLenP = new Ptr();
insertBuf = new byte[4096]; // FIXME: wasteful
insertStr = allocateConvertedString(strEncoding, insertEncoding, str, strP, strLen, insertBuf, insertLenP);
insertLen = insertLenP.p;
insertP = insertStr == str ? strP : 0;
if (insertStr == null) return -1;
}
int need = insertLen;
final int lastTranscodingIndex = numTranscoders - 1;
final Transcoding transcoding;
Buffer buf;
if (numTranscoders == 0) {
transcoding = null;
buf = inBuf;
} else if (elements[lastTranscodingIndex].transcoding.transcoder.compatibility.isEncoder()) {
transcoding = elements[lastTranscodingIndex].transcoding;
need += transcoding.readAgainLength;
if (need < insertLen) return -1;
if (lastTranscodingIndex == 0) {
buf = inBuf;
} else {
buf = elements[lastTranscodingIndex - 1];
}
} else {
transcoding = elements[lastTranscodingIndex].transcoding;
buf = elements[lastTranscodingIndex];
}
if (buf == null) {
buf = new Buffer();
buf.allocate(need);
} else if (buf.bytes == null) {
buf.bytes = new byte[need];
buf.dataStart = 0;
buf.dataEnd = 0;
buf.bufEnd = need;
} else if ((buf.bufEnd - buf.dataEnd) < need) {
// try to compact buffer by moving data portion back to bufStart
System.arraycopy(buf.bytes, buf.dataStart, buf.bytes, buf.bufStart, buf.dataEnd - buf.dataStart);
buf.dataEnd = buf.bufStart + (buf.dataEnd - buf.dataStart);
buf.dataStart = buf.bufStart;
if ((buf.bufEnd - buf.dataEnd) < need) {
// still not enough room; use a separate buffer
int s = (buf.dataEnd - buf.bufStart) + need;
if (s < need) return -1;
Buffer buf2 = buf = new Buffer();
buf2.allocate(s);
System.arraycopy(buf.bytes, buf.bufStart, buf2.bytes, 0, s); // ??
buf2.dataStart = 0;
buf2.dataEnd = buf.dataEnd - buf.bufStart;
}
}
System.arraycopy(insertStr, insertP, buf.bytes, buf.dataEnd, insertLen);
buf.dataEnd += insertLen;
if (transcoding != null && transcoding.transcoder.compatibility.isEncoder()) {
System.arraycopy(transcoding.readBuf, transcoding.recognizedLength, buf.bytes, buf.dataEnd, transcoding.readAgainLength);
buf.dataEnd += transcoding.readAgainLength;
transcoding.readAgainLength = 0;
}
return 0;
}
/* rb_econv_close */
public void close() {
for (int i = 0; i < numTranscoders; i++) {
elements[i].transcoding.close();
}
}
/* rb_econv_putbackable */
public int putbackable() {
return numTranscoders == 0 ? 0 : elements[0].transcoding.readAgainLength;
}
/* rb_econv_putback */
public void putback(byte[] bytes, int p, int n) {
if (numTranscoders == 0 || n == 0) return;
Transcoding transcoding = elements[0].transcoding;
System.arraycopy(transcoding.readBuf, transcoding.recognizedLength + transcoding.readAgainLength - n, bytes, p, n);
transcoding.readAgainLength -= n;
}
/* rb_econv_add_converter */
public boolean addConverter(byte[] source, byte[] destination, int n) {
if (started) return false;
TranscoderDB.Entry entry = TranscoderDB.getEntry(source, destination);
if (entry == null) return false;
Transcoder transcoder = entry.getTranscoder();
if (transcoder == null) return false;
addTranscoderAt(transcoder, n);
return true;
}
/* rb_econv_decorate_at */
boolean decorateAt(byte[] decorator, int n) {
return addConverter(NULL_STRING, decorator, n);
}
/* rb_econv_decorate_at_first */
// NOTE: Unused in MRI proper; for C exts, presumably
boolean decorateAtFirst(byte[] decorator) {
if (numTranscoders == 0) return decorateAt(decorator, 0);
Transcoder transcoder = elements[0].transcoding.transcoder;
if (!decorator(transcoder.source, transcoder.destination) && transcoder.compatibility.isDecoder()) {
return decorateAt(decorator, 1);
}
return decorateAt(decorator, 0);
}
/* rb_econv_decorate_at_last */
boolean decorateAtLast(byte[] decorator) {
if (numTranscoders == 0) return decorateAt(decorator, 0);
Transcoder transcoder = elements[numTranscoders - 1].transcoding.transcoder;
if (!decorator(transcoder.source, transcoder.destination) && transcoder.compatibility.isEncoder()) {
return decorateAt(decorator, numTranscoders - 1);
}
return decorateAt(decorator, numTranscoders);
}
/* rb_econv_binmode */
public void binmode() {
Transcoder[] transcoders = new Transcoder[3];
int n = 0;
if ((flags & UNIVERSAL_NEWLINE_DECORATOR) != 0) {
TranscoderDB.Entry entry = TranscoderDB.getEntry(NULL_STRING, "universal_newline".getBytes());
if (entry.getTranscoder() != null) transcoders[n++] = entry.getTranscoder();
}
if ((flags & CRLF_NEWLINE_DECORATOR) != 0) {
TranscoderDB.Entry entry = TranscoderDB.getEntry(NULL_STRING, "crlf_newline".getBytes());
if (entry.getTranscoder() != null) transcoders[n++] = entry.getTranscoder();
}
if ((flags & CR_NEWLINE_DECORATOR) != 0) {
TranscoderDB.Entry entry = TranscoderDB.getEntry(NULL_STRING, "cr_newline".getBytes());
if (entry.getTranscoder() != null) transcoders[n++] = entry.getTranscoder();
}
int nTrans = numTranscoders;
int j = 0;
for (int i = 0; i < nTrans; i++) {
int k;
for (k = 0; k < n; k++) {
if (transcoders[k] == elements[i].transcoding.transcoder) break;
}
if (k == n) {
elements[j] = elements[i];
j++;
} else {
elements[i].transcoding.close();
numTranscoders--;
}
}
flags &= ~NEWLINE_DECORATOR_MASK;
}
/* econv_description, rb_econv_open_exc, make_econv_exception */
/* more_output_buffer */
/* make_replacement */
public int makeReplacement() {
if (replacementString != null) return 0;
byte[] insEnc = encodingToInsertOutput();
final byte[] replEnc;
final int len;
final byte[] replacement;
if (insEnc.length != 0) {
// Transcoding transcoding = lastTranscoding;
// Transcoder transcoder = transcoding.transcoder;
// Encoding enc = EncodingDB.getEncodings().get(transcoder.destination).getEncoding();
// get_replacement_character
if (caseInsensitiveEquals(insEnc, "UTF-8".getBytes())) {
len = 3;
replEnc = "UTF-8".getBytes();
replacement = new byte[] { (byte) 0xEF, (byte) 0xBF, (byte) 0xBD };
} else {
len = 1;
replEnc = "US-ASCII".getBytes();
replacement = new byte[] { '?' };
}
} else {
len = 1;
replEnc = NULL_STRING;
replacement = new byte[] { '?' };
}
replacementString = replacement;
replacementLength = len;
replacementEncoding = replEnc;
return 0;
}
/* rb_econv_set_replacement */
public int setReplacement(byte[] str, int p, int len, byte[] encname) {
byte[] encname2 = encodingToInsertOutput();
final byte[] str2;
final int p2 = 0;
final int len2;
if (caseInsensitiveEquals(encname, encname2)) {
str2 = new byte[len];
System.arraycopy(str, p, str2, 0, len); // ??
len2 = len;
encname2 = encname;
} else {
Ptr len2p = new Ptr();
str2 = allocateConvertedString(encname, encname2, str, p, len, null, len2p);
if (str2 == null) return -1;
len2 = len2p.p;
}
replacementString = str2;
replacementLength = len2;
replacementEncoding = encname2;
return 0;
}
/* output_replacement_character */
int outputReplacementCharacter() {
if (makeReplacement() == -1) return -1;
if (insertOutput(replacementString, 0, replacementLength, replacementEncoding) == -1) return -1;
return 0;
}
public String toStringFull() {
String s = "EConv " + new String(source) + " => " + new String(destination) + "\n";
s += " started: " + started + "\n";
s += " replacement string: " + (replacementString == null ? "null" : new String(replacementString, 0, replacementLength)) + "\n";
s += " replacement encoding: " + (replacementEncoding == null ? "null" : new String(replacementEncoding)) + "\n";
s += "\n";
for (int i = 0; i < numTranscoders; i++) {
s += " element " + i + ": " + elements[i].toString() + "\n";
}
s += "\n";
s += " lastTranscoding: " + lastTranscoding + "\n";
s += " last error: " + (lastError == null ? "null" : lastError.toString());
return s;
}
// econv_equal
@Override
public boolean equals(Object other) {
if (!(other instanceof EConv)) return false;
EConv ec1 = this;
EConv ec2 = (EConv)other;
int i;
if (ec2 == null) return false;
if (ec1.source != ec2.source &&
!Arrays.equals(ec1.source, ec2.source))
return false;
if (ec1.destination != ec2.destination &&
!Arrays.equals(ec1.destination, ec2.destination))
return false;
if (ec1.flags != ec2.flags) return false;
if (ec1.replacementEncoding != ec2.replacementEncoding &&
!Arrays.equals(ec1.replacementEncoding, ec2.replacementEncoding))
return false;
if (ec1.replacementLength != ec2.replacementLength) return false;
if (ec1.replacementString != ec2.replacementString &&
!memcmp(ec1.replacementString, ec2.replacementString, ec2.replacementLength))
return false;
if (ec1.numTranscoders != ec2.numTranscoders) return false;
for (i = 0; i < ec1.numTranscoders; i++) {
if (ec1.elements[i].transcoding.transcoder != ec2.elements[i].transcoding.transcoder)
return false;
}
return true;
}
private static boolean memcmp(byte[] a, byte[] b, int len) {
for (int i = 0; i < len; i++) {
if (a[i] != b[i]) return false;
}
return true;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy