Browse Source

Add rencode-java as local library because repo is gone

pull/594/head
Taco 3 years ago
parent
commit
27f74fe2c2
  1. 1
      app/build.gradle
  2. 32
      app/src/main/java/se/dimovski/rencode/Rencode.java
  3. 494
      app/src/main/java/se/dimovski/rencode/RencodeInputStream.java
  4. 404
      app/src/main/java/se/dimovski/rencode/RencodeOutputStream.java
  5. 47
      app/src/main/java/se/dimovski/rencode/TypeCode.java
  6. 74
      app/src/main/java/se/dimovski/rencode/Utils.java

1
app/build.gradle

@ -90,7 +90,6 @@ dependencies { @@ -90,7 +90,6 @@ dependencies {
implementation 'com.nostra13.universalimageloader:universal-image-loader:1.9.5'
implementation 'com.getbase:floatingactionbutton:1.10.1'
implementation 'com.nispok:snackbar:2.11.0'
implementation 'com.github.aegnor:rencode-java:cb628e824e'
implementation 'org.apache.openjpa:openjpa-lib:3.1.1'
implementation 'net.iharder:base64:2.3.9'
implementation('com.github.afollestad.material-dialogs:core:0.9.6.0@aar') {

32
app/src/main/java/se/dimovski/rencode/Rencode.java

@ -0,0 +1,32 @@ @@ -0,0 +1,32 @@
package se.dimovski.rencode;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
public class Rencode
{
public static Object decode(byte[] data) throws IOException
{
final InputStream is = new ByteArrayInputStream(data);
final RencodeInputStream inputStream = new RencodeInputStream(is);
final Object decoded = inputStream.readObject();
inputStream.close();
return decoded;
}
public static byte[] encode(Object obj) throws IOException
{
final ByteArrayOutputStream baos = new ByteArrayOutputStream();
final RencodeOutputStream output = new RencodeOutputStream(baos);
output.writeObject(obj);
final byte[] encoded = baos.toByteArray();
output.close();
return encoded;
}
}

494
app/src/main/java/se/dimovski/rencode/RencodeInputStream.java

@ -0,0 +1,494 @@ @@ -0,0 +1,494 @@
package se.dimovski.rencode;
import java.io.DataInput;
import java.io.EOFException;
import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
public class RencodeInputStream extends FilterInputStream implements DataInput
{
/**
* The charset that is being used for {@link String}s.
*/
private final String charset;
/**
* Whether or not all byte-Arrays should be decoded as {@link String}s.
*/
private final boolean decodeAsString;
/**
* Creates a {@link RencodeInputStream} with the default encoding.
*/
public RencodeInputStream(InputStream in)
{
this(in, Utils.UTF_8, false);
}
/**
* Creates a {@link RencodeInputStream} with the given encoding.
*/
public RencodeInputStream(InputStream in, String charset)
{
this(in, charset, false);
}
/**
* Creates a {@link RencodeInputStream} with the default encoding.
*/
public RencodeInputStream(InputStream in, boolean decodeAsString)
{
this(in, Utils.UTF_8, decodeAsString);
}
/**
* Creates a {@link RencodeInputStream} with the given encoding.
*/
public RencodeInputStream(InputStream in, String charset, boolean decodeAsString)
{
super(in);
if (charset == null)
{
throw new IllegalArgumentException("charset is null");
}
this.charset = charset;
this.decodeAsString = decodeAsString;
}
/**
* Returns the charset that is used to decode {@link String}s. The default
* value is UTF-8.
*/
public String getCharset()
{
return charset;
}
/**
* Returns true if all byte-Arrays are being turned into {@link String}s.
*/
public boolean isDecodeAsString()
{
return decodeAsString;
}
/**
* Reads and returns an {@link Object}.
*/
public Object readObject() throws IOException
{
int token = readToken();
return readObject(token);
}
/**
* Reads and returns an {@link Object}.
*/
protected Object readObject(int token) throws IOException
{
if (token == TypeCode.DICTIONARY)
{
return readMap0(Object.class);
}
else if (Utils.isFixedDictionary(token))
{
return readMap0(Object.class, token);
}
else if (token == TypeCode.LIST)
{
return readList0(Object.class);
}
else if (Utils.isFixedList(token))
{
return readList0(Object.class, token);
}
else if (Utils.isNumber(token))
{
return readNumber0(token);
}
else if (token == TypeCode.FALSE || token == TypeCode.TRUE)
{
return readBoolean0(token);
}
else if (token == TypeCode.NULL)
{
return null;
}
else if (Utils.isDigit(token) || Utils.isFixedString(token))
{
return readString(token, charset);
}
throw new IOException("Not implemented: " + token);
}
/**
* Reads and returns a {@link Map}.
*/
public Map<String, ?> readMap() throws IOException
{
return readMap(Object.class);
}
/**
* Reads and returns a {@link Map}.
*/
public <T> Map<String, T> readMap(Class<T> clazz) throws IOException
{
int token = readToken();
if (token != TypeCode.DICTIONARY)
{
throw new IOException();
}
return readMap0(clazz);
}
private <T> Map<String, T> readMap0(Class<T> clazz) throws IOException
{
Map<String, T> map = new TreeMap<String, T>();
int token = -1;
while ((token = readToken()) != TypeCode.END)
{
readMapItem(clazz, token, map);
}
return map;
}
private <T> Map<String, T> readMap0(Class<T> clazz, int token) throws IOException
{
Map<String, T> map = new TreeMap<String, T>();
int count = token - TypeCode.EMBEDDED.DICT_START;
for (int i = 0; i < count; i++)
{
readMapItem(clazz, readToken(), map);
}
return map;
}
private <T> void readMapItem(Class<T> clazz, int token, Map<String, T> map) throws UnsupportedEncodingException,
IOException
{
String key = readString(token, charset);
T value = clazz.cast(readObject());
map.put(key, value);
}
public int readToken() throws IOException
{
int token = super.read();
if (token == -1)
{
throw new EOFException();
}
return token;
}
/**
* Reads and returns a {@link List}.
*/
public List<?> readList() throws IOException
{
return readList(Object.class);
}
/**
* Reads and returns a {@link List}.
*/
public <T> List<T> readList(Class<T> clazz) throws IOException
{
int token = readToken();
if (token != TypeCode.LIST)
{
throw new IOException();
}
return readList0(clazz);
}
private <T> List<T> readList0(Class<T> clazz) throws IOException
{
List<T> list = new ArrayList<T>();
int token = -1;
while ((token = readToken()) != TypeCode.END)
{
list.add(clazz.cast(readObject(token)));
}
return list;
}
private <T> List<T> readList0(Class<T> clazz, int token) throws IOException
{
List<T> list = new ArrayList<T>();
int length = token - TypeCode.EMBEDDED.LIST_START;
for (int i = 0; i < length; i++)
{
list.add(clazz.cast(readObject()));
}
return list;
}
public boolean readBoolean() throws IOException
{
return readBoolean0(readToken());
}
public boolean readBoolean0(int token) throws IOException
{
if (token == TypeCode.FALSE)
{
return false;
}
else if (token == TypeCode.TRUE)
{
return true;
}
throw new IOException();
}
public byte readByte() throws IOException
{
return (byte) readToken();
}
public char readChar() throws IOException
{
return (char) readToken();
}
public double readDouble() throws IOException
{
return readNumber().doubleValue();
}
public float readFloat() throws IOException
{
return readNumber().floatValue();
}
public void readFully(byte[] dst) throws IOException
{
readFully(dst, 0, dst.length);
}
public void readFully(byte[] dst, int off, int len) throws IOException
{
int total = 0;
while (total < len)
{
int r = read(dst, total, len - total);
if (r == -1)
{
throw new EOFException();
}
total += r;
}
}
public int readInt() throws IOException
{
return readNumber().intValue();
}
public String readLine() throws IOException
{
return readString();
}
public long readLong() throws IOException
{
return readNumber().longValue();
}
public short readShort() throws IOException
{
return readNumber().shortValue();
}
public String readUTF() throws IOException
{
return readString(Utils.UTF_8);
}
public int readUnsignedByte() throws IOException
{
return readByte() & 0xFF;
}
public int readUnsignedShort() throws IOException
{
return readShort() & 0xFFFF;
}
/**
* Reads and returns a {@link Number}.
*/
public Number readNumber() throws IOException
{
int token = readToken();
if (!Utils.isNumber(token))
{
throw new IOException();
}
return readNumber0(token);
}
private Number readNumber0(int token) throws IOException
{
switch (token)
{
case TypeCode.BYTE:
return (int) readToBuffer(1).get();
case TypeCode.SHORT:
return (int) readToBuffer(2).getShort();
case TypeCode.INT:
return readToBuffer(4).getInt();
case TypeCode.LONG:
return readToBuffer(8).getLong();
case TypeCode.FLOAT:
return readToBuffer(4).getFloat();
case TypeCode.DOUBLE:
return readToBuffer(8).getDouble();
case TypeCode.NUMBER:
return readNumber0();
}
if (Utils.isNegativeFixedNumber(token))
{
return TypeCode.EMBEDDED.INT_NEG_START - 1 - token;
}
else if (Utils.isPositiveFixedNumber(token))
{
return TypeCode.EMBEDDED.INT_POS_START + token;
}
throw new IOException("Unknown number. TypeCode: " + token);
}
private ByteBuffer readToBuffer(int count) throws IOException
{
return ByteBuffer.wrap(readBytesFixed(count));
}
private Number readNumber0() throws IOException
{
StringBuilder buffer = new StringBuilder();
boolean decimal = false;
int token = -1;
while ((token = readToken()) != TypeCode.END)
{
if (token == '.')
{
decimal = true;
}
buffer.append((char) token);
}
try
{
if (decimal)
{
return new BigDecimal(buffer.toString());
}
else
{
return new BigInteger(buffer.toString());
}
}
catch (NumberFormatException err)
{
throw new IOException("NumberFormatException", err);
}
}
public int skipBytes(int n) throws IOException
{
return (int) skip(n);
}
/**
* Reads and returns a byte-Array.
*/
public byte[] readBytes() throws IOException
{
int token = readToken();
return readBytes(token);
}
/**
* Reads and returns a {@link String}.
*/
public String readString() throws IOException
{
return readString(charset);
}
private String readString(String encoding) throws IOException
{
return readString(readToken(), encoding);
}
private String readString(int token, String charset) throws IOException
{
if (Utils.isFixedString(token))
{
int length = token - TypeCode.EMBEDDED.STR_START;
return new String(readBytesFixed(length), charset);
}
return new String(readBytes(token), charset);
}
private byte[] readBytes(int token) throws IOException
{
int length = readLength(token);
return readBytesFixed(length);
}
private byte[] readBytesFixed(int count) throws IOException
{
byte[] data = new byte[count];
readFully(data);
return data;
}
private int readLength(int token) throws IOException
{
StringBuilder buffer = new StringBuilder();
buffer.append((char) token);
while ((token = readToken()) != TypeCode.LENGTH_DELIM)
{
buffer.append((char) token);
}
return Integer.parseInt(buffer.toString());
}
}

404
app/src/main/java/se/dimovski/rencode/RencodeOutputStream.java

@ -0,0 +1,404 @@ @@ -0,0 +1,404 @@
package se.dimovski.rencode;
import java.io.DataOutput;
import java.io.FilterOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.lang.reflect.Array;
import java.nio.ByteBuffer;
import java.util.Collection;
import java.util.Map;
import java.util.SortedMap;
import java.util.TreeMap;
public class RencodeOutputStream extends FilterOutputStream implements DataOutput
{
/**
* The {@link String} charset.
*/
private final String charset;
/**
* Creates a {@link RencodeOutputStream} with the default charset.
*/
public RencodeOutputStream(OutputStream out)
{
this(out, Utils.UTF_8);
}
/**
* Creates a {@link RencodeOutputStream} with the given encoding.
*/
public RencodeOutputStream(OutputStream out, String charset)
{
super(out);
if (charset == null)
{
throw new NullPointerException("charset");
}
this.charset = charset;
}
/**
* Returns the charset that is used to encode {@link String}s. The default
* value is UTF-8.
*/
public String getCharset()
{
return charset;
}
/**
* Writes an {@link Object}.
*/
public void writeObject(Object value) throws IOException
{
if (value == null)
{
writeNull();
}
else if (value instanceof byte[])
{
writeBytes((byte[]) value);
}
else if (value instanceof Boolean)
{
writeBoolean((Boolean) value);
}
else if (value instanceof Character)
{
writeChar((Character) value);
}
else if (value instanceof Number)
{
writeNumber((Number) value);
}
else if (value instanceof String)
{
writeString((String) value);
}
else if (value instanceof Collection<?>)
{
writeCollection((Collection<?>) value);
}
else if (value instanceof Map<?, ?>)
{
writeMap((Map<?, ?>) value);
}
else if (value instanceof Enum<?>)
{
writeEnum((Enum<?>) value);
}
else if (value.getClass().isArray())
{
writeArray(value);
}
else
{
writeCustom(value);
}
}
/**
* Writes a null value
*/
public void writeNull() throws IOException
{
write(TypeCode.NULL);
}
/**
* Overwrite this method to write custom objects. The default implementation
* throws an {@link IOException}.
*/
protected void writeCustom(Object value) throws IOException
{
throw new IOException("Cannot encode " + value);
}
/**
* Writes the given byte-Array
*/
public void writeBytes(byte[] value) throws IOException
{
writeBytes(value, 0, value.length);
}
/**
* Writes the given byte-Array
*/
public void writeBytes(byte[] value, int offset, int length) throws IOException
{
write(value, offset, length);
}
/**
* Writes a boolean
*/
public void writeBoolean(boolean value) throws IOException
{
write(value ? TypeCode.TRUE : TypeCode.FALSE);
}
/**
* Writes a char
*/
public void writeChar(int value) throws IOException
{
writeByte(value);
}
/**
* Writes a byte
*/
public void writeByte(int value) throws IOException
{
write(TypeCode.BYTE);
write(value);
}
/**
* Writes a short
*/
public void writeShort(int value) throws IOException
{
write(TypeCode.SHORT);
ByteBuffer buffer = ByteBuffer.allocate(Utils.SHORT_BYTES).putShort((short) value);
write(buffer.array());
}
/**
* Writes an int
*/
public void writeInt(int value) throws IOException
{
write(TypeCode.INT);
ByteBuffer buffer = ByteBuffer.allocate(Utils.INTEGER_BYTES).putInt(value);
write(buffer.array());
}
/**
* Writes a long
*/
public void writeLong(long value) throws IOException
{
write(TypeCode.LONG);
ByteBuffer buffer = ByteBuffer.allocate(Utils.LONG_BYTES).putLong(value);
write(buffer.array());
}
/**
* Writes a float
*/
public void writeFloat(float value) throws IOException
{
write(TypeCode.FLOAT);
ByteBuffer buffer = ByteBuffer.allocate(Utils.FLOAT_BYTES).putFloat(value);
write(buffer.array());
}
/**
* Writes a double
*/
public void writeDouble(double value) throws IOException
{
write(TypeCode.DOUBLE);
ByteBuffer buffer = ByteBuffer.allocate(Utils.DOUBLE_BYTES).putDouble(value);
write(buffer.array());
}
/**
* Writes a {@link Number}
*/
public void writeNumber(Number num) throws IOException
{
if (num instanceof Float)
{
writeFloat(num.floatValue());
}
else if (num instanceof Double)
{
writeDouble(num.doubleValue());
}
if (0 <= num.intValue() && num.intValue() < TypeCode.EMBEDDED.INT_POS_COUNT)
{
write(TypeCode.EMBEDDED.INT_POS_START + num.intValue());
}
else if (-TypeCode.EMBEDDED.INT_NEG_COUNT <= num.intValue() && num.intValue() < 0)
{
write(TypeCode.EMBEDDED.INT_NEG_START - 1 - num.intValue());
}
else if (Byte.MIN_VALUE <= num.intValue() && num.intValue() < Byte.MAX_VALUE)
{
writeByte(num.byteValue());
}
else if (Short.MIN_VALUE <= num.intValue() && num.intValue() < Short.MAX_VALUE)
{
writeShort(num.shortValue());
}
else if (Integer.MIN_VALUE <= num.longValue() && num.longValue() < Integer.MAX_VALUE)
{
writeInt(num.intValue());
}
else if (Long.MIN_VALUE <= num.longValue() && num.longValue() < Long.MAX_VALUE)
{
writeLong(num.longValue());
}
else
{
String number = num.toString();
write(TypeCode.NUMBER);
write(number.getBytes(charset));
write(TypeCode.END);
}
}
/**
* Writes a {@link String}
*/
public void writeString(String value) throws IOException
{
int len = value.length();
if (len < TypeCode.EMBEDDED.STR_COUNT)
{
write(TypeCode.EMBEDDED.STR_START + len);
}
else
{
String lenString = Integer.toString(len);
writeBytes(lenString.getBytes(charset));
write(TypeCode.LENGTH_DELIM);
}
writeBytes(value.getBytes(charset));
}
/**
* Writes a {@link Collection}.
*/
public void writeCollection(Collection<?> value) throws IOException
{
boolean useEndToken = value.size() >= TypeCode.EMBEDDED.LIST_COUNT;
if (useEndToken)
{
write(TypeCode.LIST);
}
else
{
write(TypeCode.EMBEDDED.LIST_START + value.size());
}
for (Object element : value)
{
writeObject(element);
}
if (useEndToken)
{
write(TypeCode.END);
}
}
/**
* Writes a {@link Map}.
*/
public void writeMap(Map<?, ?> map) throws IOException
{
if (!(map instanceof SortedMap<?, ?>))
{
map = new TreeMap<Object, Object>(map);
}
boolean untilEnd = map.size() >= TypeCode.EMBEDDED.DICT_COUNT;
if (untilEnd)
{
write(TypeCode.DICTIONARY);
}
else
{
write(TypeCode.EMBEDDED.DICT_START + map.size());
}
for (Map.Entry<?, ?> entry : map.entrySet())
{
writeObject(entry.getKey());
writeObject(entry.getValue());
}
if (untilEnd)
{
write(TypeCode.END);
}
}
/**
* Writes an {@link Enum}.
*/
public void writeEnum(Enum<?> value) throws IOException
{
writeString(value.name());
}
/**
* Writes an array
*/
public void writeArray(Object value) throws IOException
{
int length = Array.getLength(value);
boolean useEndToken = length >= TypeCode.EMBEDDED.LIST_COUNT;
if (useEndToken)
{
write(TypeCode.LIST);
}
else
{
write(TypeCode.EMBEDDED.LIST_START + length);
}
for (int i = 0; i < length; i++)
{
writeObject(Array.get(value, i));
}
if (useEndToken)
{
write(TypeCode.END);
}
}
/**
* Writes the given {@link String}
*/
public void writeBytes(String value) throws IOException
{
writeString(value);
}
/**
* Writes the given {@link String}
*/
public void writeChars(String value) throws IOException
{
writeString(value);
}
/**
* Writes an UTF encoded {@link String}
*/
public void writeUTF(String value) throws IOException
{
writeBytes(value.getBytes(Utils.UTF_8));
}
}

47
app/src/main/java/se/dimovski/rencode/TypeCode.java

@ -0,0 +1,47 @@ @@ -0,0 +1,47 @@
package se.dimovski.rencode;
public class TypeCode
{
// The bencode 'typecodes' such as i, d, etc have been
// extended and relocated on the base-256 character set.
public static final char LIST = 59;
public static final char DICTIONARY = 60;
public static final char NUMBER = 61;
public static final char BYTE = 62;
public static final char SHORT = 63;
public static final char INT = 64;
public static final char LONG = 65;
public static final char FLOAT = 66;
public static final char DOUBLE = 44;
public static final char TRUE = 67;
public static final char FALSE = 68;
public static final char NULL = 69;
public static final char END = 127;
public static final char LENGTH_DELIM = ':';
/*
* TypeCodes with embedded values/lengths
*/
public static class EMBEDDED
{
// Positive integers
public static final int INT_POS_START = 0;
public static final int INT_POS_COUNT = 44;
// Negative integers
public static final int INT_NEG_START = 70;
public static final int INT_NEG_COUNT = 32;
// Dictionaries
public static final int DICT_START = 102;
public static final int DICT_COUNT = 25;
// Strings
public static final int STR_START = 128;
public static final int STR_COUNT = 64;
// Lists
public static final int LIST_START = STR_START + STR_COUNT;
public static final int LIST_COUNT = 64;
}
}

74
app/src/main/java/se/dimovski/rencode/Utils.java

@ -0,0 +1,74 @@ @@ -0,0 +1,74 @@
package se.dimovski.rencode;
public class Utils
{
// Character Encodings
public final static String UTF_8 = "UTF-8";
public final static String ISO_8859 = "ISO-8859-1";
// Byte-lengths for types
public static final int SHORT_BYTES = Short.SIZE / Byte.SIZE;
public static final int INTEGER_BYTES = Integer.SIZE / Byte.SIZE;
public static final int LONG_BYTES = Long.SIZE / Byte.SIZE;
public static final int FLOAT_BYTES = Float.SIZE / Byte.SIZE;
public static final int DOUBLE_BYTES = Double.SIZE / Byte.SIZE;
// Maximum length of integer when written as base 10 string.
public static final int MAX_INT_LENGTH = 64;
private static boolean tokenInRange(int token, int start, int count)
{
return start <= token && token < (start + count);
}
public static boolean isNumber(int token)
{
switch (token)
{
case TypeCode.NUMBER:
case TypeCode.BYTE:
case TypeCode.SHORT:
case TypeCode.INT:
case TypeCode.LONG:
case TypeCode.FLOAT:
case TypeCode.DOUBLE:
return true;
}
return isFixedNumber(token);
}
public static boolean isFixedNumber(int token)
{
return isPositiveFixedNumber(token) || isNegativeFixedNumber(token);
}
public static boolean isPositiveFixedNumber(int token)
{
return tokenInRange(token, TypeCode.EMBEDDED.INT_POS_START, TypeCode.EMBEDDED.INT_POS_COUNT);
}
public static boolean isNegativeFixedNumber(int token)
{
return tokenInRange(token, TypeCode.EMBEDDED.INT_NEG_START, TypeCode.EMBEDDED.INT_NEG_COUNT);
}
public static boolean isFixedList(int token)
{
return tokenInRange(token, TypeCode.EMBEDDED.LIST_START, TypeCode.EMBEDDED.LIST_COUNT);
}
public static boolean isFixedDictionary(int token)
{
return tokenInRange(token, TypeCode.EMBEDDED.DICT_START, TypeCode.EMBEDDED.DICT_COUNT);
}
public static boolean isFixedString(int token)
{
return tokenInRange(token, TypeCode.EMBEDDED.STR_START, TypeCode.EMBEDDED.STR_COUNT);
}
public static boolean isDigit(int token)
{
return '0' <= token && token <= '9';
}
}
Loading…
Cancel
Save