Get rid of Base64 class in TeaVM compiler

This commit is contained in:
Alexey Andreev 2017-11-26 19:56:23 +03:00
parent 8fbf62ebac
commit a39e6eb47e
5 changed files with 209 additions and 93 deletions

View File

@ -1,87 +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.impl;
import java.util.Arrays;
public final class Base64 {
private static char[] alphabet = new char[64];
private static int[] reverse = new int[256];
static {
int i = 0;
for (char c = 'A'; c <= 'Z'; ++c) {
alphabet[i++] = c;
}
for (char c = 'a'; c <= 'z'; ++c) {
alphabet[i++] = c;
}
for (char c = '0'; c <= '9'; ++c) {
alphabet[i++] = c;
}
alphabet[i++] = '+';
alphabet[i++] = '/';
Arrays.fill(reverse, -1);
for (i = 0; i < alphabet.length; ++i) {
reverse[alphabet[i]] = i;
}
}
private Base64() {
}
public static byte[] decode(String text) {
int outputSize = ((text.length() - 1) / 4 + 1) * 3;
int i;
int j;
for (i = text.length() - 1; i >= 0 && text.charAt(i) == '='; --i) {
--outputSize;
}
byte[] output = new byte[outputSize];
int triples = (outputSize / 3) * 3;
i = 0;
for (j = 0; j < triples;) {
int a = decode(text.charAt(i++));
int b = decode(text.charAt(i++));
int c = decode(text.charAt(i++));
int d = decode(text.charAt(i++));
int out = (a << 18) | (b << 12) | (c << 6) | d;
output[j++] = (byte) (out >>> 16);
output[j++] = (byte) (out >>> 8);
output[j++] = (byte) out;
}
int rem = output.length - j;
if (rem == 1) {
int a = decode(text.charAt(i));
int b = decode(text.charAt(i + 1));
output[j] = (byte) ((a << 2) | (b >>> 4));
} else if (rem == 2) {
int a = decode(text.charAt(i));
int b = decode(text.charAt(i + 1));
int c = decode(text.charAt(i + 2));
output[j] = (byte) ((a << 2) | (b >>> 4));
output[j + 1] = (byte) ((b << 4) | (c >>> 2));
}
return output;
}
private static int decode(char c) {
return c < 256 ? reverse[c] : -1;
}
}

View File

@ -0,0 +1,153 @@
/*
* 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.impl;
import java.util.Arrays;
public final class Base64Impl {
private static byte[] alphabet = new byte[64];
private static int[] reverse = new int[256];
static {
int i = 0;
for (char c = 'A'; c <= 'Z'; ++c) {
alphabet[i++] = (byte) c;
}
for (char c = 'a'; c <= 'z'; ++c) {
alphabet[i++] = (byte) c;
}
for (char c = '0'; c <= '9'; ++c) {
alphabet[i++] = (byte) c;
}
alphabet[i++] = '+';
alphabet[i++] = '/';
Arrays.fill(reverse, -1);
for (i = 0; i < alphabet.length; ++i) {
reverse[alphabet[i]] = i;
}
}
private Base64Impl() {
}
public static byte[] decode(byte[] text) {
int outputSize = (text.length / 4) * 3;
int rem = text.length % 4;
if (rem == 2 || rem == 3) {
outputSize += rem - 1;
}
int i;
for (i = text.length - 1; i >= 0 && text[i] == '='; --i) {
--outputSize;
}
byte[] output = new byte[outputSize];
decode(text, output);
return output;
}
public static void decode(byte[] text, byte[] output) {
int inputSize = text.length;
int i;
for (i = text.length - 1; i >= 0 && text[i] == '='; --i) {
--inputSize;
}
int triples = (inputSize / 4) * 4;
i = 0;
int j = 0;
while (i < triples) {
int a = decode(text[i++]);
int b = decode(text[i++]);
int c = decode(text[i++]);
int d = decode(text[i++]);
int out = (a << 18) | (b << 12) | (c << 6) | d;
output[j++] = (byte) (out >>> 16);
output[j++] = (byte) (out >>> 8);
output[j++] = (byte) out;
}
int rem = inputSize - i;
if (rem == 2) {
int a = decode(text[i]);
int b = decode(text[i + 1]);
output[j] = (byte) ((a << 2) | (b >>> 4));
} else if (rem == 3) {
int a = decode(text[i]);
int b = decode(text[i + 1]);
int c = decode(text[i + 2]);
output[j] = (byte) ((a << 2) | (b >>> 4));
output[j + 1] = (byte) ((b << 4) | (c >>> 2));
}
}
private static int decode(byte c) {
return reverse[c];
}
public static byte[] encode(byte[] data, boolean pad) {
int outputSize = ((data.length + 2) / 3) * 4;
if (!pad) {
int rem = data.length % 3;
if (rem != 0) {
outputSize -= 3 - rem;
}
}
byte[] output = new byte[outputSize];
encode(data, output, pad);
return output;
}
public static int encode(byte[] data, byte[] output, boolean pad) {
int triples = (data.length / 3) * 3;
int i = 0;
int j;
for (j = 0; j < triples;) {
output[i++] = encode((byte) (data[j] >>> 2));
output[i++] = encode((byte) ((data[j] << 4) | (data[j + 1] >>> 4)));
++j;
output[i++] = encode((byte) ((data[j] << 2) | (data[j + 1] >>> 6)));
++j;
output[i++] = encode(data[j]);
++j;
}
int rem = data.length - j;
if (rem == 1) {
output[i++] = encode((byte) (data[j] >>> 2));
output[i++] = encode((byte) (data[j] << 4));
if (pad) {
output[i++] = '=';
output[i++] = '=';
}
} else if (rem == 2) {
output[i++] = encode((byte) (data[j] >>> 2));
output[i++] = encode((byte) ((data[j] << 4) | (data[j + 1] >>> 4)));
++j;
output[i++] = encode((byte) (data[j] << 2));
if (pad) {
output[i++] = '=';
}
}
return i;
}
private static byte encode(byte b) {
return alphabet[b & 63];
}
}

View File

@ -19,7 +19,6 @@ import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
import java.util.Base64;
import java.util.HashSet;
import java.util.Properties;
import java.util.ServiceLoader;
@ -31,6 +30,7 @@ import org.teavm.backend.javascript.spi.Injector;
import org.teavm.backend.javascript.spi.InjectorContext;
import org.teavm.classlib.ResourceSupplier;
import org.teavm.classlib.ResourceSupplierContext;
import org.teavm.classlib.impl.Base64Impl;
import org.teavm.model.ListableClassReaderSource;
import org.teavm.model.MethodReference;
@ -69,10 +69,14 @@ public class ClassLoaderNativeGenerator implements Injector {
}
first = false;
writer.newLine();
String data = Base64.getEncoder().encodeToString(IOUtils.toByteArray(new BufferedInputStream(input)));
byte[] dataBytes = Base64Impl.encode(IOUtils.toByteArray(new BufferedInputStream(input)), true);
char[] dataChars = new char[dataBytes.length];
for (int i = 0; i < dataBytes.length; ++i) {
dataChars[i] = (char) dataBytes[i];
}
writer.append("\"").append(RenderingUtil.escapeString(resource)).append("\"");
writer.ws().append(':').ws();
writer.append("\"").append(data).append("\"");
writer.append("\"").append(new String(dataChars)).append("\"");
}
}

View File

@ -18,7 +18,7 @@ package org.teavm.classlib.java.lang;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import org.teavm.backend.javascript.spi.InjectedBy;
import org.teavm.classlib.impl.Base64;
import org.teavm.classlib.impl.Base64Impl;
import org.teavm.jso.JSBody;
import org.teavm.jso.JSIndexer;
import org.teavm.jso.JSObject;
@ -50,7 +50,11 @@ public abstract class TClassLoader extends TObject {
}
JSObject data = resources.getResource(name);
String dataString = resourceToString(data);
return dataString == null ? null : new ByteArrayInputStream(Base64.decode(dataString));
byte[] bytes = new byte[dataString.length()];
for (int i = 0; i < bytes.length; ++i) {
bytes[i] = (byte) dataString.charAt(i);
}
return dataString == null ? null : new ByteArrayInputStream(Base64Impl.decode(bytes));
}
public static InputStream getSystemResourceAsStream(String name) {

View File

@ -23,17 +23,59 @@ public class Base64Test {
@Test
public void decoderWorks() {
assertEquals("q", decode("cQ=="));
assertEquals("q", decode("cQ"));
assertEquals("qw", decode("cXc="));
assertEquals("qw", decode("cXc"));
assertEquals("qwe", decode("cXdl"));
assertEquals("qwer", decode("cXdlcg=="));
assertEquals("qwer", decode("cXdlcg"));
assertEquals("qwert", decode("cXdlcnQ="));
assertEquals("qwerty", decode("cXdlcnR5"));
assertEquals("qwertyu", decode("cXdlcnR5dQ=="));
assertEquals("qwertyu", decode("cXdlcnR5dQ"));
}
@Test
public void encoderWorks() {
assertEquals("cQ==", encode("q"));
assertEquals("cXc=", encode("qw"));
assertEquals("cXdl", encode("qwe"));
assertEquals("cXdlcg==", encode("qwer"));
assertEquals("cXdlcnQ=", encode("qwert"));
assertEquals("cXdlcnR5", encode("qwerty"));
assertEquals("cXdlcnR5dQ==", encode("qwertyu"));
}
@Test
public void encoderNoPadWorks() {
assertEquals("cQ", encodeNoPad("q"));
assertEquals("cXc", encodeNoPad("qw"));
assertEquals("cXdl", encodeNoPad("qwe"));
assertEquals("cXdlcg", encodeNoPad("qwer"));
assertEquals("cXdlcnQ", encodeNoPad("qwert"));
assertEquals("cXdlcnR5", encodeNoPad("qwerty"));
assertEquals("cXdlcnR5dQ", encodeNoPad("qwertyu"));
}
private String decode(String text) {
try {
return new String(Base64.decode(text), "UTF-8");
return new String(Base64Impl.decode(text.getBytes("UTF-8")), "UTF-8");
} catch (UnsupportedEncodingException e) {
return "";
}
}
private String encode(String text) {
try {
return new String(Base64Impl.encode(text.getBytes("UTF-8"), true), "UTF-8");
} catch (UnsupportedEncodingException e) {
return "";
}
}
private String encodeNoPad(String text) {
try {
return new String(Base64Impl.encode(text.getBytes("UTF-8"), false), "UTF-8");
} catch (UnsupportedEncodingException e) {
return "";
}