mirror of
https://github.com/Eaglercraft-TeaVM-Fork/eagler-teavm.git
synced 2024-12-22 08:14:09 -08:00
UTF-8 support test passes
This commit is contained in:
parent
0be769f74d
commit
cc5225a2a6
|
@ -15,12 +15,10 @@
|
|||
*/
|
||||
package org.teavm.classlib.java.nio.charset;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.*;
|
||||
import org.teavm.classlib.java.nio.TByteBuffer;
|
||||
import org.teavm.classlib.java.nio.TCharBuffer;
|
||||
import org.teavm.classlib.java.nio.charset.impl.UTF8Charset;
|
||||
import org.teavm.classlib.java.nio.charset.impl.TUTF8Charset;
|
||||
|
||||
/**
|
||||
*
|
||||
|
@ -30,6 +28,11 @@ public abstract class TCharset implements Comparable<TCharset> {
|
|||
private String canonicalName;
|
||||
private String[] aliases;
|
||||
private Set<String> aliasSet;
|
||||
private static final Map<String, TCharset> charsets = new HashMap<>();
|
||||
|
||||
static {
|
||||
charsets.put("UTF-8", new TUTF8Charset());
|
||||
}
|
||||
|
||||
protected TCharset(String canonicalName, String[] aliases) {
|
||||
checkCanonicalName(canonicalName);
|
||||
|
@ -74,12 +77,11 @@ public abstract class TCharset implements Comparable<TCharset> {
|
|||
throw new IllegalArgumentException("charsetName is null");
|
||||
}
|
||||
checkCanonicalName(charsetName);
|
||||
switch (charsetName.toUpperCase()) {
|
||||
case "UTF-8":
|
||||
return new UTF8Charset();
|
||||
default:
|
||||
throw new TUnsupportedCharsetException(charsetName);
|
||||
TCharset charset = charsets.get(charsetName.toUpperCase());
|
||||
if (charset == null) {
|
||||
throw new TUnsupportedCharsetException(charsetName);
|
||||
}
|
||||
return charset;
|
||||
}
|
||||
|
||||
public final String name() {
|
||||
|
|
|
@ -27,7 +27,7 @@ public abstract class TCharsetDecoder {
|
|||
private static final int INIT = 0;
|
||||
private static final int IN_PROGRESS = 1;
|
||||
private static final int END = 2;
|
||||
private static final int FLUSH = 2;
|
||||
private static final int FLUSH = 3;
|
||||
private TCharset charset;
|
||||
private float averageCharsPerByte;
|
||||
private float maxCharsPerByte;
|
||||
|
@ -125,7 +125,7 @@ public abstract class TCharsetDecoder {
|
|||
if (result.isOverflow()) {
|
||||
return result;
|
||||
} else if (result.isUnderflow()) {
|
||||
if (endOfInput) {
|
||||
if (endOfInput && in.hasRemaining()) {
|
||||
state = END;
|
||||
return TCoderResult.malformedForLength(in.remaining());
|
||||
}
|
||||
|
|
|
@ -78,4 +78,20 @@ public class TCoderResult {
|
|||
throw new TUnmappableCharacterException(length);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
switch (kind) {
|
||||
case 0:
|
||||
return "UNDERFLOW";
|
||||
case 1:
|
||||
return "OVERFLOW";
|
||||
case 2:
|
||||
return "MALFORMED " + length;
|
||||
case 3:
|
||||
return "UNMAPPABLE " + length;
|
||||
default:
|
||||
throw new AssertionError();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,124 @@
|
|||
/*
|
||||
* Copyright 2015 Alexey Andreev.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
package org.teavm.classlib.java.nio.charset.impl;
|
||||
|
||||
import org.teavm.classlib.java.nio.TByteBuffer;
|
||||
import org.teavm.classlib.java.nio.TCharBuffer;
|
||||
import org.teavm.classlib.java.nio.charset.TCharset;
|
||||
import org.teavm.classlib.java.nio.charset.TCharsetDecoder;
|
||||
import org.teavm.classlib.java.nio.charset.TCoderResult;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Alexey Andreev
|
||||
*/
|
||||
public abstract class TBufferedDecoder extends TCharsetDecoder {
|
||||
public TBufferedDecoder(TCharset cs, float averageCharsPerByte, float maxCharsPerByte) {
|
||||
super(cs, averageCharsPerByte, maxCharsPerByte);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected TCoderResult decodeLoop(TByteBuffer in, TCharBuffer out) {
|
||||
// Use intermediate array to batch buffer operations
|
||||
int outPos = 0;
|
||||
byte[] inArray = new byte[Math.min(in.remaining(), 512)];
|
||||
int inPos = 0;
|
||||
int inSize = 0;
|
||||
char[] outArray = new char[Math.min(out.remaining(), 512)];
|
||||
TCoderResult result = null;
|
||||
|
||||
while (true) {
|
||||
// If there were remaining bytes in input buffer, copy them to the beginning of input array
|
||||
// so the next iteration will process these bytes again
|
||||
if (inPos + 32 > inSize && in.hasRemaining()) {
|
||||
for (int i = inPos; i < inSize; ++i) {
|
||||
inArray[i - inPos] = inArray[i];
|
||||
}
|
||||
inPos = inSize - inPos;
|
||||
inSize = Math.min(in.remaining() + inPos, inArray.length);
|
||||
in.get(inArray, inPos, inSize - inPos);
|
||||
inPos = 0;
|
||||
}
|
||||
|
||||
if (!out.hasRemaining()) {
|
||||
result = !in.hasRemaining() && inPos >= inSize ? TCoderResult.UNDERFLOW : TCoderResult.OVERFLOW;
|
||||
break;
|
||||
}
|
||||
|
||||
// Perform iteration
|
||||
outPos = 0;
|
||||
int outSize = Math.min(out.remaining(), outArray.length);
|
||||
Controller controller = new Controller(in, out);
|
||||
result = arrayDecode(inArray, inPos, inSize, outArray, outPos, outSize, controller);
|
||||
inPos = controller.inPosition;
|
||||
if (result == null && outPos == controller.outPosition) {
|
||||
result = TCoderResult.UNDERFLOW;
|
||||
}
|
||||
outPos = controller.outPosition;
|
||||
|
||||
// Write any output characters to out buffer
|
||||
out.put(outArray, 0, outPos);
|
||||
if (result != null) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
in.position(in.position() - (inSize - inPos));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
protected abstract TCoderResult arrayDecode(byte[] inArray, int inPos, int inSize,
|
||||
char[] outArray, int outPos, int outSize,
|
||||
Controller controller);
|
||||
|
||||
public static class Controller {
|
||||
private TByteBuffer in;
|
||||
private TCharBuffer out;
|
||||
int inPosition;
|
||||
int outPosition;
|
||||
|
||||
Controller(TByteBuffer in, TCharBuffer out) {
|
||||
super();
|
||||
this.in = in;
|
||||
this.out = out;
|
||||
}
|
||||
|
||||
public boolean hasMoreInput() {
|
||||
return in.hasRemaining();
|
||||
}
|
||||
|
||||
public boolean hasMoreInput(int sz) {
|
||||
return in.remaining() >= sz;
|
||||
}
|
||||
|
||||
public boolean hasMoreOutput() {
|
||||
return out.hasRemaining();
|
||||
}
|
||||
|
||||
public boolean hasMoreOutput(int sz) {
|
||||
return out.remaining() >= sz;
|
||||
}
|
||||
|
||||
public void setInPosition(int inPosition) {
|
||||
this.inPosition = inPosition;
|
||||
}
|
||||
|
||||
public void setOutPosition(int outPosition) {
|
||||
this.outPosition = outPosition;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,128 @@
|
|||
/*
|
||||
* Copyright 2015 Alexey Andreev.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
package org.teavm.classlib.java.nio.charset.impl;
|
||||
|
||||
import org.teavm.classlib.java.nio.TByteBuffer;
|
||||
import org.teavm.classlib.java.nio.TCharBuffer;
|
||||
import org.teavm.classlib.java.nio.charset.TCharset;
|
||||
import org.teavm.classlib.java.nio.charset.TCharsetEncoder;
|
||||
import org.teavm.classlib.java.nio.charset.TCoderResult;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Alexey Andreev
|
||||
*/
|
||||
public abstract class TBufferedEncoder extends TCharsetEncoder {
|
||||
public TBufferedEncoder(TCharset cs, float averageBytesPerChar, float maxBytesPerChar, byte[] replacement) {
|
||||
super(cs, averageBytesPerChar, maxBytesPerChar, replacement);
|
||||
}
|
||||
|
||||
public TBufferedEncoder(TCharset cs, float averageBytesPerChar, float maxBytesPerChar) {
|
||||
super(cs, averageBytesPerChar, maxBytesPerChar);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected TCoderResult encodeLoop(TCharBuffer in, TByteBuffer out) {
|
||||
// Use intermediate array to batch buffer operations
|
||||
int outPos = 0;
|
||||
char[] inArray = new char[Math.min(in.remaining(), 512)];
|
||||
int inPos = 0;
|
||||
int inSize = 0;
|
||||
byte[] outArray = new byte[Math.min(out.remaining(), 512)];
|
||||
TCoderResult result = null;
|
||||
|
||||
while (true) {
|
||||
// If there were remaining bytes in input buffer, copy them to the beginning of input array
|
||||
// so the next iteration will process these bytes again
|
||||
if (inPos + 32 > inSize && in.hasRemaining()) {
|
||||
for (int i = inPos; i < inSize; ++i) {
|
||||
inArray[i - inPos] = inArray[i];
|
||||
}
|
||||
inPos = inSize - inPos;
|
||||
inSize = Math.min(in.remaining() + inPos, inArray.length);
|
||||
in.get(inArray, inPos, inSize - inPos);
|
||||
inPos = 0;
|
||||
}
|
||||
|
||||
if (!out.hasRemaining()) {
|
||||
result = !in.hasRemaining() && inPos >= inSize ? TCoderResult.UNDERFLOW : TCoderResult.OVERFLOW;
|
||||
break;
|
||||
}
|
||||
|
||||
// Perform iteration
|
||||
outPos = 0;
|
||||
int outSize = Math.min(out.remaining(), outArray.length);
|
||||
Controller controller = new Controller(in, out);
|
||||
result = arrayEncode(inArray, inPos, inSize, outArray, outPos, outSize, controller);
|
||||
inPos = controller.inPosition;
|
||||
if (result == null && outPos == controller.outPosition) {
|
||||
result = TCoderResult.UNDERFLOW;
|
||||
}
|
||||
outPos = controller.outPosition;
|
||||
|
||||
// Write any output characters to out buffer
|
||||
out.put(outArray, 0, outPos);
|
||||
if (result != null) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
in.position(in.position() - (inSize - inPos));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
protected abstract TCoderResult arrayEncode(char[] inArray, int inPos, int inSize,
|
||||
byte[] outArray, int outPos, int outSize,
|
||||
Controller controller);
|
||||
|
||||
public static class Controller {
|
||||
private TCharBuffer in;
|
||||
private TByteBuffer out;
|
||||
int inPosition;
|
||||
int outPosition;
|
||||
|
||||
Controller(TCharBuffer in, TByteBuffer out) {
|
||||
super();
|
||||
this.in = in;
|
||||
this.out = out;
|
||||
}
|
||||
|
||||
public boolean hasMoreInput() {
|
||||
return in.hasRemaining();
|
||||
}
|
||||
|
||||
public boolean hasMoreInput(int sz) {
|
||||
return in.remaining() >= sz;
|
||||
}
|
||||
|
||||
public boolean hasMoreOutput() {
|
||||
return out.hasRemaining();
|
||||
}
|
||||
|
||||
public boolean hasMoreOutput(int sz) {
|
||||
return out.remaining() >= sz;
|
||||
}
|
||||
|
||||
public void setInPosition(int inPosition) {
|
||||
this.inPosition = inPosition;
|
||||
}
|
||||
|
||||
public void setOutPosition(int outPosition) {
|
||||
this.outPosition = outPosition;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -23,8 +23,8 @@ import org.teavm.classlib.java.nio.charset.TCharsetEncoder;
|
|||
*
|
||||
* @author Alexey Andreev
|
||||
*/
|
||||
public class UTF8Charset extends TCharset {
|
||||
public UTF8Charset() {
|
||||
public class TUTF8Charset extends TCharset {
|
||||
public TUTF8Charset() {
|
||||
super("UTF-8", new String[0]);
|
||||
}
|
||||
|
||||
|
@ -35,11 +35,11 @@ public class UTF8Charset extends TCharset {
|
|||
|
||||
@Override
|
||||
public TCharsetDecoder newDecoder() {
|
||||
return new UTF8Decoder(this);
|
||||
return new TUTF8Decoder(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TCharsetEncoder newEncoder() {
|
||||
return null;
|
||||
return new TUTF8Encoder(this);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,93 @@
|
|||
/*
|
||||
* Copyright 2015 Alexey Andreev.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
package org.teavm.classlib.java.nio.charset.impl;
|
||||
|
||||
import org.teavm.classlib.impl.charset.UTF16Helper;
|
||||
import org.teavm.classlib.java.nio.charset.TCharset;
|
||||
import org.teavm.classlib.java.nio.charset.TCoderResult;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Alexey Andreev
|
||||
*/
|
||||
public class TUTF8Decoder extends TBufferedDecoder {
|
||||
public TUTF8Decoder(TCharset cs) {
|
||||
super(cs, 1f / 3, 0.5f);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected TCoderResult arrayDecode(byte[] inArray, int inPos, int inSize, char[] outArray, int outPos, int outSize,
|
||||
Controller controller) {
|
||||
TCoderResult result = null;
|
||||
while (inPos < inSize && outPos < outSize) {
|
||||
int b = inArray[inPos++] & 0xFF;
|
||||
if ((b & 0x80) == 0) {
|
||||
outArray[outPos++] = (char)b;
|
||||
} else if ((b & 0xE0) == 0xC0) {
|
||||
if (inPos >= inSize) {
|
||||
--inPos;
|
||||
if (!controller.hasMoreInput()) {
|
||||
result = TCoderResult.UNDERFLOW;
|
||||
}
|
||||
break;
|
||||
}
|
||||
outArray[outPos++] = (char)(((b & 0x1F) << 6) | (inArray[inPos++] & 0x3F));
|
||||
} else if ((b & 0xF0) == 0xE0) {
|
||||
if (inPos + 2 > inSize) {
|
||||
--inPos;
|
||||
if (!controller.hasMoreInput()) {
|
||||
result = TCoderResult.UNDERFLOW;
|
||||
}
|
||||
break;
|
||||
}
|
||||
byte b2 = inArray[inPos++];
|
||||
byte b3 = inArray[inPos++];
|
||||
char c = (char)(((b & 0x0F) << 12) | ((b2 & 0x3f) << 6) | (b3 & 0x3F));
|
||||
if (Character.isSurrogate(c)) {
|
||||
inPos -= 3;
|
||||
result = TCoderResult.malformedForLength(3);
|
||||
break;
|
||||
}
|
||||
outArray[outPos++] = c;
|
||||
} else if ((b & 0xF8) == 0xF0) {
|
||||
if (inPos + 3 > inSize) {
|
||||
--inPos;
|
||||
if (!controller.hasMoreInput()) {
|
||||
result = TCoderResult.UNDERFLOW;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (outPos + 2 > outSize) {
|
||||
--inPos;
|
||||
if (!controller.hasMoreOutput()) {
|
||||
result = TCoderResult.OVERFLOW;
|
||||
}
|
||||
break;
|
||||
}
|
||||
byte b2 = inArray[inPos++];
|
||||
byte b3 = inArray[inPos++];
|
||||
byte b4 = inArray[inPos++];
|
||||
int code = ((b & 0x07) << 18) | ((b2 & 0x3f) << 12) | ((b3 & 0x3F) << 6) | (b4 & 0x3F);
|
||||
outArray[outPos++] = UTF16Helper.highSurrogate(code);
|
||||
outArray[outPos++] = UTF16Helper.lowSurrogate(code);
|
||||
}
|
||||
}
|
||||
|
||||
controller.setInPosition(inPos);
|
||||
controller.setOutPosition(outPos);
|
||||
return result;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,95 @@
|
|||
/*
|
||||
* Copyright 2015 Alexey Andreev.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
package org.teavm.classlib.java.nio.charset.impl;
|
||||
|
||||
import org.teavm.classlib.impl.charset.UTF16Helper;
|
||||
import org.teavm.classlib.java.nio.charset.TCharset;
|
||||
import org.teavm.classlib.java.nio.charset.TCoderResult;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Alexey Andreev
|
||||
*/
|
||||
public class TUTF8Encoder extends TBufferedEncoder {
|
||||
TUTF8Encoder(TCharset cs) {
|
||||
super(cs, 2, 4);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected TCoderResult arrayEncode(char[] inArray, int inPos, int inSize, byte[] outArray, int outPos, int outSize,
|
||||
Controller controller) {
|
||||
TCoderResult result = null;
|
||||
while (inPos < inSize && outPos < outSize) {
|
||||
char ch = inArray[inPos++];
|
||||
if (ch < 0x80) {
|
||||
outArray[outPos++] = (byte)ch;
|
||||
} else if (ch < 0x800) {
|
||||
if (outPos + 2 > outSize) {
|
||||
--inPos;
|
||||
if (!controller.hasMoreOutput(2)) {
|
||||
result = TCoderResult.OVERFLOW;
|
||||
}
|
||||
break;
|
||||
}
|
||||
outArray[outPos++] = (byte)(0xC0 | (ch >> 6));
|
||||
outArray[outPos++] = (byte)(0x80 | (ch & 0x3F));
|
||||
} else if (!Character.isSurrogate(ch)) {
|
||||
if (outPos + 3 > outSize) {
|
||||
--inPos;
|
||||
if (!controller.hasMoreOutput(3)) {
|
||||
result = TCoderResult.OVERFLOW;
|
||||
}
|
||||
break;
|
||||
}
|
||||
outArray[outPos++] = (byte)(0xE0 | (ch >> 12));
|
||||
outArray[outPos++] = (byte)(0x80 | ((ch >> 6) & 0x3F));
|
||||
outArray[outPos++] = (byte)(0x80 | (ch & 0x3F));
|
||||
} else if (UTF16Helper.isHighSurrogate(ch)) {
|
||||
if (inPos >= inSize) {
|
||||
if (!controller.hasMoreInput()) {
|
||||
result = TCoderResult.UNDERFLOW;
|
||||
}
|
||||
break;
|
||||
}
|
||||
char low = inArray[inPos++];
|
||||
if (!UTF16Helper.isLowSurrogate(low)) {
|
||||
inPos -= 2;
|
||||
result = TCoderResult.malformedForLength(2);
|
||||
break;
|
||||
}
|
||||
if (outPos + 4 > outSize) {
|
||||
inPos -= 2;
|
||||
if (!controller.hasMoreOutput(4)) {
|
||||
result = TCoderResult.OVERFLOW;
|
||||
}
|
||||
break;
|
||||
}
|
||||
int codePoint = UTF16Helper.buildCodePoint(ch, low);
|
||||
outArray[outPos++] = (byte)(0xF0 | (codePoint >> 18));
|
||||
outArray[outPos++] = (byte)(0x80 | ((codePoint >> 12) & 0x3F));
|
||||
outArray[outPos++] = (byte)(0x80 | ((codePoint >> 6) & 0x3F));
|
||||
outArray[outPos++] = (byte)(0x80 | (codePoint & 0x3F));
|
||||
} else {
|
||||
result = TCoderResult.malformedForLength(1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
controller.setInPosition(inPos);
|
||||
controller.setOutPosition(outPos);
|
||||
return result;
|
||||
}
|
||||
}
|
|
@ -1,83 +0,0 @@
|
|||
/*
|
||||
* Copyright 2015 Alexey Andreev.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
package org.teavm.classlib.java.nio.charset.impl;
|
||||
|
||||
import org.teavm.classlib.impl.charset.UTF16Helper;
|
||||
import org.teavm.classlib.java.nio.TByteBuffer;
|
||||
import org.teavm.classlib.java.nio.TCharBuffer;
|
||||
import org.teavm.classlib.java.nio.charset.TCharset;
|
||||
import org.teavm.classlib.java.nio.charset.TCharsetDecoder;
|
||||
import org.teavm.classlib.java.nio.charset.TCoderResult;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Alexey Andreev
|
||||
*/
|
||||
public class UTF8Decoder extends TCharsetDecoder {
|
||||
public UTF8Decoder(TCharset cs) {
|
||||
super(cs, 1f / 3, 0.5f);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected TCoderResult decodeLoop(TByteBuffer in, TCharBuffer out) {
|
||||
while (true) {
|
||||
if (in.remaining() < 4) {
|
||||
return TCoderResult.UNDERFLOW;
|
||||
}
|
||||
if (!out.hasRemaining()) {
|
||||
return TCoderResult.OVERFLOW;
|
||||
}
|
||||
int b = in.get() & 0xFF;
|
||||
if ((b & 0x80) == 0) {
|
||||
out.put((char)b);
|
||||
} else if ((b & 0xE0) == 0xC0) {
|
||||
if (!in.hasRemaining()) {
|
||||
in.position(in.position() - 1);
|
||||
return TCoderResult.UNDERFLOW;
|
||||
}
|
||||
out.put((char)(((b & 0x1F) << 6) | (in.get() & 0x3F)));
|
||||
} else if ((b & 0xF0) == 0xE0) {
|
||||
if (in.remaining() < 2) {
|
||||
in.position(in.position() - 1);
|
||||
return TCoderResult.UNDERFLOW;
|
||||
}
|
||||
byte b2 = in.get();
|
||||
byte b3 = in.get();
|
||||
char c = (char)(((b & 0x0F) << 12) | ((b2 & 0x3f) << 6) | (b3 & 0x3F));
|
||||
if (Character.isSurrogate(c)) {
|
||||
in.position(in.position() - 2);
|
||||
return TCoderResult.malformedForLength(3);
|
||||
}
|
||||
out.put(c);
|
||||
} else if ((b & 0xF8) == 0xF0) {
|
||||
if (in.remaining() < 3) {
|
||||
in.position(in.position() - 1);
|
||||
return TCoderResult.UNDERFLOW;
|
||||
}
|
||||
if (out.remaining() < 3) {
|
||||
in.position(in.position() - 1);
|
||||
return TCoderResult.OVERFLOW;
|
||||
}
|
||||
byte b2 = in.get();
|
||||
byte b3 = in.get();
|
||||
byte b4 = in.get();
|
||||
int code = ((b & 0x07) << 18) | ((b2 & 0x3f) << 12) | ((b3 & 0x3F) << 6) | (b4 & 0x3F);
|
||||
out.put(UTF16Helper.highSurrogate(code));
|
||||
out.put(UTF16Helper.lowSurrogate(code));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,274 @@
|
|||
package org.teavm.classlib.java.nio.charset;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.CharBuffer;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.charset.CharsetDecoder;
|
||||
import java.nio.charset.CharsetEncoder;
|
||||
import java.nio.charset.CoderResult;
|
||||
import java.util.Arrays;
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Alexey Andreev
|
||||
*/
|
||||
public class UTF8Test {
|
||||
@Test
|
||||
public void encode1() {
|
||||
runEncode(600, 600);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void encode2() {
|
||||
runEncode(600, 100);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void encode3() {
|
||||
runEncode(100, 600);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void decode1() {
|
||||
runDecode(600, 600);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void decode2() {
|
||||
runDecode(600, 100);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void decode3() {
|
||||
runDecode(100, 600);
|
||||
}
|
||||
|
||||
private void runEncode(int inSize, int outSize) {
|
||||
char[] input = text.toCharArray();
|
||||
byte[] output = new byte[16384];
|
||||
int inPos = 0;
|
||||
int outPos = 0;
|
||||
CharsetEncoder encoder = Charset.forName("UTF-8").newEncoder();
|
||||
CoderResult result = CoderResult.UNDERFLOW;
|
||||
|
||||
while (true) {
|
||||
int inLen = Math.min(inSize, input.length - inPos);
|
||||
CharBuffer in = CharBuffer.wrap(input, inPos, inLen);
|
||||
int outLen = Math.min(outSize, output.length - outPos);
|
||||
ByteBuffer out = ByteBuffer.wrap(output, outPos, outLen);
|
||||
result = encoder.encode(in, out, inPos + inLen >= input.length);
|
||||
inPos = in.position();
|
||||
outPos = out.position();
|
||||
if (result.isError() || inPos >= input.length) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
assertTrue("Should be UNDERFLOW after encoding", result.isUnderflow());
|
||||
|
||||
while (true) {
|
||||
int outLen = Math.min(outSize, output.length - outPos);
|
||||
ByteBuffer out = ByteBuffer.wrap(output, outPos, outLen);
|
||||
result = encoder.flush(out);
|
||||
outPos = out.position();
|
||||
if (result.isUnderflow()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
assertTrue("Should be UNDERFLOW after flushing", result.isUnderflow());
|
||||
output = Arrays.copyOf(output, outPos);
|
||||
assertEquals(hex, bytesToHex(output));
|
||||
}
|
||||
|
||||
private void runDecode(int inSize, int outSize) {
|
||||
byte[] input = hexToBytes(hex);
|
||||
char[] output = new char[16384];
|
||||
int inPos = 0;
|
||||
int outPos = 0;
|
||||
CharsetDecoder decoder = Charset.forName("UTF-8").newDecoder();
|
||||
CoderResult result = CoderResult.UNDERFLOW;
|
||||
|
||||
while (true) {
|
||||
int inLen = Math.min(inSize, input.length - inPos);
|
||||
ByteBuffer in = ByteBuffer.wrap(input, inPos, inLen);
|
||||
int outLen = Math.min(outSize, output.length - outPos);
|
||||
CharBuffer out = CharBuffer.wrap(output, outPos, outLen);
|
||||
result = decoder.decode(in, out, inPos + inLen >= input.length);
|
||||
inPos = in.position();
|
||||
outPos = out.position();
|
||||
if (result.isError() || inPos >= input.length) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
assertTrue("Should be UNDERFLOW after encoding", result.isUnderflow());
|
||||
|
||||
while (true) {
|
||||
int outLen = Math.min(outSize, output.length - outPos);
|
||||
CharBuffer out = CharBuffer.wrap(output, outPos, outLen);
|
||||
result = decoder.flush(out);
|
||||
outPos = out.position();
|
||||
if (result.isUnderflow()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
assertTrue("Should be UNDERFLOW after flushing", result.isUnderflow());
|
||||
output = Arrays.copyOf(output, outPos);
|
||||
assertEquals(text, new String(output));
|
||||
}
|
||||
|
||||
private String bytesToHex(byte[] bytes) {
|
||||
char[] result = new char[bytes.length * 2];
|
||||
int j = 0;
|
||||
for (int i = 0; i < bytes.length; ++i) {
|
||||
int b = bytes[i] & 0xFF;
|
||||
result[j++] = hexDigits[b >> 4];
|
||||
result[j++] = hexDigits[b & 0xF];
|
||||
}
|
||||
return new String(result);
|
||||
}
|
||||
|
||||
private byte[] hexToBytes(String hex) {
|
||||
char[] chars = hex.toCharArray();
|
||||
byte[] result = new byte[chars.length / 2];
|
||||
int j = 0;
|
||||
for (int i = 0; i < chars.length; i += 2) {
|
||||
char hi = chars[i];
|
||||
char lo = chars[i + 1];
|
||||
result[j++] = (byte)((digit(hi) << 4) | digit(lo));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private static int digit(char c) {
|
||||
if (c >= '0' && c <= '9') {
|
||||
return c - '0';
|
||||
}
|
||||
return c - 'A' + 10;
|
||||
}
|
||||
|
||||
private static char[] hexDigits = "0123456789ABCDEF".toCharArray();
|
||||
|
||||
// Fragment from "The Idiot" by F. Dostoevsky
|
||||
private String text =
|
||||
"Здесь в моем объяснении я отмечаю все эти цифры и числа. Мне, конечно, всё равно будет, но теперь " +
|
||||
"(и, может быть, только в эту минуту) я желаю, чтобы те, которые будут судить мой поступок, могли ясно " +
|
||||
"видеть, из какой логической цепи выводов вышло мое „последнее убеждение“. Я написал сейчас выше, что " +
|
||||
"окончательная решимость, которой недоставало мне для исполнения моего „последнего убеждения“, произошла " +
|
||||
"во мне, кажется, вовсе не из логического вывода, а от какого-то странного толчка, от одного странного " +
|
||||
"обстоятельства, может быть вовсе не связанного ничем с ходом дела. Дней десять назад зашел ко мне Рогожин, " +
|
||||
"по одному своему делу, о котором здесь лишнее распространяться. Я никогда не видал Рогожина прежде, " +
|
||||
"но слышал о нем очень многое. Я дал ему все нужные справки, и он скоро ушел, а так как он и приходил " +
|
||||
"только за справками, то тем бы дело между нами и кончилось. Но он слишком заинтересовал меня, " +
|
||||
"и весь этот день я был под влиянием странных мыслей, так что решился пойти к нему на другой день сам, " +
|
||||
"отдать визит. Рогожин был мне очевидно не рад и даже „деликатно“ намекнул, что нам нечего продолжать " +
|
||||
"знакомство; но все-таки я провел очень любопытный час, как, вероятно, и он. Между нами был такой контраст, " +
|
||||
"который не мог не сказаться нам обоим, особенно мне: я был человек, уже сосчитавший дни свои, а он - " +
|
||||
"живущий самою полною, непосредственною жизнью, настоящею минутой, без всякой заботы о „последних“ " +
|
||||
"выводах, цифрах или о чем бы то ни было, не касающемся того, на чем... на чем... ну хоть на чем он " +
|
||||
"помешан; пусть простит мне это выражение господин Рогожин, пожалуй хоть как плохому литератору, не " +
|
||||
"умевшему выразить свою мысль. Несмотря на всю его нелюбезность, мне показалось, что он человек с умом и " +
|
||||
"может многое понимать, хотя его мало что интересует из постороннего. Я не намекал ему о моем „последнем " +
|
||||
"убеждении“, но мне почему-то показалось, что он, слушая меня, угадал его. Он промолчал, он ужасно молчалив. " +
|
||||
"Я намекнул ему, уходя, что, несмотря на всю между нами разницу и на все противоположности, - " +
|
||||
"les extrémités se touchent 1 (я растолковал ему это по-русски), так что, может быть, он и сам вовсе не " +
|
||||
"так далек от моего „последнего убеждения“, как кажется. На это он ответил мне очень угрюмою и кислою " +
|
||||
"гримасой, встал, сам сыскал мне мою фуражку, сделав вид, будто бы я сам ухожу, и просто-запросто вывел " +
|
||||
"меня из своего мрачного дома под видом того, что провожает меня из учтивости. Дом его поразил меня; " +
|
||||
"похож на кладбище, а ему, кажется, нравится, что, впрочем, понятно: такая полная, непосредственная " +
|
||||
"жизнь, которою он живет, слишком полна сама по себе, чтобы нуждаться в обстановке.";
|
||||
private String hex =
|
||||
"D097D0B4D0B5D181D18C20D0B220D0BCD0BED0B5D0BC20D0BED0B1D18AD18FD181D0BDD0B5D0BDD0B8D0B820D18F20D0BED182D0BCD" +
|
||||
"0B5D187D0B0D18E20D0B2D181D0B520D18DD182D0B820D186D0B8D184D180D18B20D0B820D187D0B8D181D0BBD0B02E20D09CD0BDD0" +
|
||||
"B52C20D0BAD0BED0BDD0B5D187D0BDD0BE2C20D0B2D181D19120D180D0B0D0B2D0BDD0BE20D0B1D183D0B4D0B5D1822C20D0BDD0BE2" +
|
||||
"0D182D0B5D0BFD0B5D180D18C2028D0B82C20D0BCD0BED0B6D0B5D18220D0B1D18BD182D18C2C20D182D0BED0BBD18CD0BAD0BE20D0" +
|
||||
"B220D18DD182D18320D0BCD0B8D0BDD183D182D1832920D18F20D0B6D0B5D0BBD0B0D18E2C20D187D182D0BED0B1D18B20D182D0B52" +
|
||||
"C20D0BAD0BED182D0BED180D18BD0B520D0B1D183D0B4D183D18220D181D183D0B4D0B8D182D18C20D0BCD0BED0B920D0BFD0BED181" +
|
||||
"D182D183D0BFD0BED0BA2C20D0BCD0BED0B3D0BBD0B820D18FD181D0BDD0BE20D0B2D0B8D0B4D0B5D182D18C2C20D0B8D0B720D0BAD" +
|
||||
"0B0D0BAD0BED0B920D0BBD0BED0B3D0B8D187D0B5D181D0BAD0BED0B920D186D0B5D0BFD0B820D0B2D18BD0B2D0BED0B4D0BED0B220" +
|
||||
"D0B2D18BD188D0BBD0BE20D0BCD0BED0B520E2809ED0BFD0BED181D0BBD0B5D0B4D0BDD0B5D0B520D183D0B1D0B5D0B6D0B4D0B5D0B" +
|
||||
"DD0B8D0B5E2809C2E20D0AF20D0BDD0B0D0BFD0B8D181D0B0D0BB20D181D0B5D0B9D187D0B0D18120D0B2D18BD188D0B52C20D187D1" +
|
||||
"82D0BE20D0BED0BAD0BED0BDD187D0B0D182D0B5D0BBD18CD0BDD0B0D18F20D180D0B5D188D0B8D0BCD0BED181D182D18C2C20D0BAD" +
|
||||
"0BED182D0BED180D0BED0B920D0BDD0B5D0B4D0BED181D182D0B0D0B2D0B0D0BBD0BE20D0BCD0BDD0B520D0B4D0BBD18F20D0B8D181" +
|
||||
"D0BFD0BED0BBD0BDD0B5D0BDD0B8D18F20D0BCD0BED0B5D0B3D0BE20E2809ED0BFD0BED181D0BBD0B5D0B4D0BDD0B5D0B3D0BE20D18" +
|
||||
"3D0B1D0B5D0B6D0B4D0B5D0BDD0B8D18FE2809C2C20D0BFD180D0BED0B8D0B7D0BED188D0BBD0B020D0B2D0BE20D0BCD0BDD0B52C20" +
|
||||
"D0BAD0B0D0B6D0B5D182D181D18F2C20D0B2D0BED0B2D181D0B520D0BDD0B520D0B8D0B720D0BBD0BED0B3D0B8D187D0B5D181D0BAD" +
|
||||
"0BED0B3D0BE20D0B2D18BD0B2D0BED0B4D0B02C20D0B020D0BED18220D0BAD0B0D0BAD0BED0B3D0BE2DD182D0BE20D181D182D180D0" +
|
||||
"B0D0BDD0BDD0BED0B3D0BE20D182D0BED0BBD187D0BAD0B02C20D0BED18220D0BED0B4D0BDD0BED0B3D0BE20D181D182D180D0B0D0B" +
|
||||
"DD0BDD0BED0B3D0BE20D0BED0B1D181D182D0BED18FD182D0B5D0BBD18CD181D182D0B2D0B02C20D0BCD0BED0B6D0B5D18220D0B1D1" +
|
||||
"8BD182D18C20D0B2D0BED0B2D181D0B520D0BDD0B520D181D0B2D18FD0B7D0B0D0BDD0BDD0BED0B3D0BE20D0BDD0B8D187D0B5D0BC2" +
|
||||
"0D18120D185D0BED0B4D0BED0BC20D0B4D0B5D0BBD0B02E20D094D0BDD0B5D0B920D0B4D0B5D181D18FD182D18C20D0BDD0B0D0B7D0" +
|
||||
"B0D0B420D0B7D0B0D188D0B5D0BB20D0BAD0BE20D0BCD0BDD0B520D0A0D0BED0B3D0BED0B6D0B8D0BD2C20D0BFD0BE20D0BED0B4D0B" +
|
||||
"DD0BED0BCD18320D181D0B2D0BED0B5D0BCD18320D0B4D0B5D0BBD1832C20D0BE20D0BAD0BED182D0BED180D0BED0BC20D0B7D0B4D0" +
|
||||
"B5D181D18C20D0BBD0B8D188D0BDD0B5D0B520D180D0B0D181D0BFD180D0BED181D182D180D0B0D0BDD18FD182D18CD181D18F2E20D" +
|
||||
"0AF20D0BDD0B8D0BAD0BED0B3D0B4D0B020D0BDD0B520D0B2D0B8D0B4D0B0D0BB20D0A0D0BED0B3D0BED0B6D0B8D0BDD0B020D0BFD1" +
|
||||
"80D0B5D0B6D0B4D0B52C20D0BDD0BE20D181D0BBD18BD188D0B0D0BB20D0BE20D0BDD0B5D0BC20D0BED187D0B5D0BDD18C20D0BCD0B" +
|
||||
"DD0BED0B3D0BED0B52E20D0AF20D0B4D0B0D0BB20D0B5D0BCD18320D0B2D181D0B520D0BDD183D0B6D0BDD18BD0B520D181D0BFD180" +
|
||||
"D0B0D0B2D0BAD0B82C20D0B820D0BED0BD20D181D0BAD0BED180D0BE20D183D188D0B5D0BB2C20D0B020D182D0B0D0BA20D0BAD0B0D" +
|
||||
"0BA20D0BED0BD20D0B820D0BFD180D0B8D185D0BED0B4D0B8D0BB20D182D0BED0BBD18CD0BAD0BE20D0B7D0B020D181D0BFD180D0B0" +
|
||||
"D0B2D0BAD0B0D0BCD0B82C20D182D0BE20D182D0B5D0BC20D0B1D18B20D0B4D0B5D0BBD0BE20D0BCD0B5D0B6D0B4D18320D0BDD0B0D" +
|
||||
"0BCD0B820D0B820D0BAD0BED0BDD187D0B8D0BBD0BED181D18C2E20D09DD0BE20D0BED0BD20D181D0BBD0B8D188D0BAD0BED0BC20D0" +
|
||||
"B7D0B0D0B8D0BDD182D0B5D180D0B5D181D0BED0B2D0B0D0BB20D0BCD0B5D0BDD18F2C20D0B820D0B2D0B5D181D18C20D18DD182D0B" +
|
||||
"ED18220D0B4D0B5D0BDD18C20D18F20D0B1D18BD0BB20D0BFD0BED0B420D0B2D0BBD0B8D18FD0BDD0B8D0B5D0BC20D181D182D180D0" +
|
||||
"B0D0BDD0BDD18BD18520D0BCD18BD181D0BBD0B5D0B92C20D182D0B0D0BA20D187D182D0BE20D180D0B5D188D0B8D0BBD181D18F20D" +
|
||||
"0BFD0BED0B9D182D0B820D0BA20D0BDD0B5D0BCD18320D0BDD0B020D0B4D180D183D0B3D0BED0B920D0B4D0B5D0BDD18C20D181D0B0" +
|
||||
"D0BC2C20D0BED182D0B4D0B0D182D18C20D0B2D0B8D0B7D0B8D1822E20D0A0D0BED0B3D0BED0B6D0B8D0BD20D0B1D18BD0BB20D0BCD" +
|
||||
"0BDD0B520D0BED187D0B5D0B2D0B8D0B4D0BDD0BE20D0BDD0B520D180D0B0D0B420D0B820D0B4D0B0D0B6D0B520E2809ED0B4D0B5D0" +
|
||||
"BBD0B8D0BAD0B0D182D0BDD0BEE2809C20D0BDD0B0D0BCD0B5D0BAD0BDD183D0BB2C20D187D182D0BE20D0BDD0B0D0BC20D0BDD0B5D" +
|
||||
"187D0B5D0B3D0BE20D0BFD180D0BED0B4D0BED0BBD0B6D0B0D182D18C20D0B7D0BDD0B0D0BAD0BED0BCD181D182D0B2D0BE3B20D0BD" +
|
||||
"D0BE20D0B2D181D0B52DD182D0B0D0BAD0B820D18F20D0BFD180D0BED0B2D0B5D0BB20D0BED187D0B5D0BDD18C20D0BBD18ED0B1D0B" +
|
||||
"ED0BFD18BD182D0BDD18BD0B920D187D0B0D1812C20D0BAD0B0D0BA2C20D0B2D0B5D180D0BED18FD182D0BDD0BE2C20D0B820D0BED0" +
|
||||
"BD2E20D09CD0B5D0B6D0B4D18320D0BDD0B0D0BCD0B820D0B1D18BD0BB20D182D0B0D0BAD0BED0B920D0BAD0BED0BDD182D180D0B0D" +
|
||||
"181D1822C20D0BAD0BED182D0BED180D18BD0B920D0BDD0B520D0BCD0BED0B320D0BDD0B520D181D0BAD0B0D0B7D0B0D182D18CD181" +
|
||||
"D18F20D0BDD0B0D0BC20D0BED0B1D0BED0B8D0BC2C20D0BED181D0BED0B1D0B5D0BDD0BDD0BE20D0BCD0BDD0B53A20D18F20D0B1D18" +
|
||||
"BD0BB20D187D0B5D0BBD0BED0B2D0B5D0BA2C20D183D0B6D0B520D181D0BED181D187D0B8D182D0B0D0B2D188D0B8D0B920D0B4D0BD" +
|
||||
"D0B820D181D0B2D0BED0B82C20D0B020D0BED0BD202D20D0B6D0B8D0B2D183D189D0B8D0B920D181D0B0D0BCD0BED18E20D0BFD0BED" +
|
||||
"0BBD0BDD0BED18E2C20D0BDD0B5D0BFD0BED181D180D0B5D0B4D181D182D0B2D0B5D0BDD0BDD0BED18E20D0B6D0B8D0B7D0BDD18CD1" +
|
||||
"8E2C20D0BDD0B0D181D182D0BED18FD189D0B5D18E20D0BCD0B8D0BDD183D182D0BED0B92C20D0B1D0B5D0B720D0B2D181D18FD0BAD" +
|
||||
"0BED0B920D0B7D0B0D0B1D0BED182D18B20D0BE20E2809ED0BFD0BED181D0BBD0B5D0B4D0BDD0B8D185E2809C20D0B2D18BD0B2D0BE" +
|
||||
"D0B4D0B0D1852C20D186D0B8D184D180D0B0D18520D0B8D0BBD0B820D0BE20D187D0B5D0BC20D0B1D18B20D182D0BE20D0BDD0B820D" +
|
||||
"0B1D18BD0BBD0BE2C20D0BDD0B520D0BAD0B0D181D0B0D18ED189D0B5D0BCD181D18F20D182D0BED0B3D0BE2C20D0BDD0B020D187D0" +
|
||||
"B5D0BC2E2E2E20D0BDD0B020D187D0B5D0BC2E2E2E20D0BDD18320D185D0BED182D18C20D0BDD0B020D187D0B5D0BC20D0BED0BD20D" +
|
||||
"0BFD0BED0BCD0B5D188D0B0D0BD3B20D0BFD183D181D182D18C20D0BFD180D0BED181D182D0B8D18220D0BCD0BDD0B520D18DD182D0" +
|
||||
"BE20D0B2D18BD180D0B0D0B6D0B5D0BDD0B8D0B520D0B3D0BED181D0BFD0BED0B4D0B8D0BD20D0A0D0BED0B3D0BED0B6D0B8D0BD2C2" +
|
||||
"0D0BFD0BED0B6D0B0D0BBD183D0B920D185D0BED182D18C20D0BAD0B0D0BA20D0BFD0BBD0BED185D0BED0BCD18320D0BBD0B8D182D0" +
|
||||
"B5D180D0B0D182D0BED180D1832C20D0BDD0B520D183D0BCD0B5D0B2D188D0B5D0BCD18320D0B2D18BD180D0B0D0B7D0B8D182D18C2" +
|
||||
"0D181D0B2D0BED18E20D0BCD18BD181D0BBD18C2E20D09DD0B5D181D0BCD0BED182D180D18F20D0BDD0B020D0B2D181D18E20D0B5D0" +
|
||||
"B3D0BE20D0BDD0B5D0BBD18ED0B1D0B5D0B7D0BDD0BED181D182D18C2C20D0BCD0BDD0B520D0BFD0BED0BAD0B0D0B7D0B0D0BBD0BED" +
|
||||
"181D18C2C20D187D182D0BE20D0BED0BD20D187D0B5D0BBD0BED0B2D0B5D0BA20D18120D183D0BCD0BED0BC20D0B820D0BCD0BED0B6" +
|
||||
"D0B5D18220D0BCD0BDD0BED0B3D0BED0B520D0BFD0BED0BDD0B8D0BCD0B0D182D18C2C20D185D0BED182D18F20D0B5D0B3D0BE20D0B" +
|
||||
"CD0B0D0BBD0BE20D187D182D0BE20D0B8D0BDD182D0B5D180D0B5D181D183D0B5D18220D0B8D0B720D0BFD0BED181D182D0BED180D0" +
|
||||
"BED0BDD0BDD0B5D0B3D0BE2E20D0AF20D0BDD0B520D0BDD0B0D0BCD0B5D0BAD0B0D0BB20D0B5D0BCD18320D0BE20D0BCD0BED0B5D0B" +
|
||||
"C20E2809ED0BFD0BED181D0BBD0B5D0B4D0BDD0B5D0BC20D183D0B1D0B5D0B6D0B4D0B5D0BDD0B8D0B8E2809C2C20D0BDD0BE20D0BC" +
|
||||
"D0BDD0B520D0BFD0BED187D0B5D0BCD1832DD182D0BE20D0BFD0BED0BAD0B0D0B7D0B0D0BBD0BED181D18C2C20D187D182D0BE20D0B" +
|
||||
"ED0BD2C20D181D0BBD183D188D0B0D18F20D0BCD0B5D0BDD18F2C20D183D0B3D0B0D0B4D0B0D0BB20D0B5D0B3D0BE2E20D09ED0BD20" +
|
||||
"D0BFD180D0BED0BCD0BED0BBD187D0B0D0BB2C20D0BED0BD20D183D0B6D0B0D181D0BDD0BE20D0BCD0BED0BBD187D0B0D0BBD0B8D0B" +
|
||||
"22E20D0AF20D0BDD0B0D0BCD0B5D0BAD0BDD183D0BB20D0B5D0BCD1832C20D183D185D0BED0B4D18F2C20D187D182D0BE2C20D0BDD0" +
|
||||
"B5D181D0BCD0BED182D180D18F20D0BDD0B020D0B2D181D18E20D0BCD0B5D0B6D0B4D18320D0BDD0B0D0BCD0B820D180D0B0D0B7D0B" +
|
||||
"DD0B8D186D18320D0B820D0BDD0B020D0B2D181D0B520D0BFD180D0BED182D0B8D0B2D0BED0BFD0BED0BBD0BED0B6D0BDD0BED181D1" +
|
||||
"82D0B82C202D206C65732065787472C3A96D6974C3A97320736520746F756368656E7420312028D18F20D180D0B0D181D182D0BED0B" +
|
||||
"BD0BAD0BED0B2D0B0D0BB20D0B5D0BCD18320D18DD182D0BE20D0BFD0BE2DD180D183D181D181D0BAD0B8292C20D182D0B0D0BA20D1" +
|
||||
"87D182D0BE2C20D0BCD0BED0B6D0B5D18220D0B1D18BD182D18C2C20D0BED0BD20D0B820D181D0B0D0BC20D0B2D0BED0B2D181D0B52" +
|
||||
"0D0BDD0B520D182D0B0D0BA20D0B4D0B0D0BBD0B5D0BA20D0BED18220D0BCD0BED0B5D0B3D0BE20E2809ED0BFD0BED181D0BBD0B5D0" +
|
||||
"B4D0BDD0B5D0B3D0BE20D183D0B1D0B5D0B6D0B4D0B5D0BDD0B8D18FE2809C2C20D0BAD0B0D0BA20D0BAD0B0D0B6D0B5D182D181D18" +
|
||||
"F2E20D09DD0B020D18DD182D0BE20D0BED0BD20D0BED182D0B2D0B5D182D0B8D0BB20D0BCD0BDD0B520D0BED187D0B5D0BDD18C20D1" +
|
||||
"83D0B3D180D18ED0BCD0BED18E20D0B820D0BAD0B8D181D0BBD0BED18E20D0B3D180D0B8D0BCD0B0D181D0BED0B92C20D0B2D181D18" +
|
||||
"2D0B0D0BB2C20D181D0B0D0BC20D181D18BD181D0BAD0B0D0BB20D0BCD0BDD0B520D0BCD0BED18E20D184D183D180D0B0D0B6D0BAD1" +
|
||||
"832C20D181D0B4D0B5D0BBD0B0D0B220D0B2D0B8D0B42C20D0B1D183D0B4D182D0BE20D0B1D18B20D18F20D181D0B0D0BC20D183D18" +
|
||||
"5D0BED0B6D1832C20D0B820D0BFD180D0BED181D182D0BE2DD0B7D0B0D0BFD180D0BED181D182D0BE20D0B2D18BD0B2D0B5D0BB20D0" +
|
||||
"BCD0B5D0BDD18F20D0B8D0B720D181D0B2D0BED0B5D0B3D0BE20D0BCD180D0B0D187D0BDD0BED0B3D0BE20D0B4D0BED0BCD0B020D0B" +
|
||||
"FD0BED0B420D0B2D0B8D0B4D0BED0BC20D182D0BED0B3D0BE2C20D187D182D0BE20D0BFD180D0BED0B2D0BED0B6D0B0D0B5D18220D0" +
|
||||
"BCD0B5D0BDD18F20D0B8D0B720D183D187D182D0B8D0B2D0BED181D182D0B82E20D094D0BED0BC20D0B5D0B3D0BE20D0BFD0BED180D" +
|
||||
"0B0D0B7D0B8D0BB20D0BCD0B5D0BDD18F3B20D0BFD0BED185D0BED0B620D0BDD0B020D0BAD0BBD0B0D0B4D0B1D0B8D189D0B52C20D0" +
|
||||
"B020D0B5D0BCD1832C20D0BAD0B0D0B6D0B5D182D181D18F2C20D0BDD180D0B0D0B2D0B8D182D181D18F2C20D187D182D0BE2C20D0B" +
|
||||
"2D0BFD180D0BED187D0B5D0BC2C20D0BFD0BED0BDD18FD182D0BDD0BE3A20D182D0B0D0BAD0B0D18F20D0BFD0BED0BBD0BDD0B0D18F" +
|
||||
"2C20D0BDD0B5D0BFD0BED181D180D0B5D0B4D181D182D0B2D0B5D0BDD0BDD0B0D18F20D0B6D0B8D0B7D0BDD18C2C20D0BAD0BED182D" +
|
||||
"0BED180D0BED18E20D0BED0BD20D0B6D0B8D0B2D0B5D1822C20D181D0BBD0B8D188D0BAD0BED0BC20D0BFD0BED0BBD0BDD0B020D181" +
|
||||
"D0B0D0BCD0B020D0BFD0BE20D181D0B5D0B1D0B52C20D187D182D0BED0B1D18B20D0BDD183D0B6D0B4D0B0D182D18CD181D18F20D0B" +
|
||||
"220D0BED0B1D181D182D0B0D0BDD0BED0B2D0BAD0B52E";
|
||||
}
|
Loading…
Reference in New Issue
Block a user