begin working on bencode decoding
This commit is contained in:
parent
43da5e8d45
commit
d40e1cbe4e
5 changed files with 140 additions and 0 deletions
71
src/main/java/com/mykola2312/retracker/bencode/BTree.java
Normal file
71
src/main/java/com/mykola2312/retracker/bencode/BTree.java
Normal file
|
|
@ -0,0 +1,71 @@
|
||||||
|
package com.mykola2312.retracker.bencode;
|
||||||
|
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
import com.mykola2312.retracker.bencode.error.BDecodeError;
|
||||||
|
import com.mykola2312.retracker.bencode.error.BDecodeParseError;
|
||||||
|
import com.mykola2312.retracker.bencode.error.BDecodeUnknown;
|
||||||
|
|
||||||
|
public class BTree {
|
||||||
|
private BValue root = null;
|
||||||
|
|
||||||
|
public BValue getRoot() {
|
||||||
|
return root;
|
||||||
|
}
|
||||||
|
|
||||||
|
class BDecoder {
|
||||||
|
private byte[] data;
|
||||||
|
private int offset = 0;
|
||||||
|
|
||||||
|
public BDecoder(byte[] data, int offset) {
|
||||||
|
this.data = data;
|
||||||
|
this.offset = offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Long parseLong(byte[] bytes) throws BDecodeParseError {
|
||||||
|
String strInt = new String(bytes, StandardCharsets.UTF_8);
|
||||||
|
try {
|
||||||
|
return Long.parseLong(strInt);
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
throw new BDecodeParseError(data, offset, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final byte BE_INTEGER = (byte)'i';
|
||||||
|
private static final byte BE_LIST = (byte)'l';
|
||||||
|
private static final byte BE_DICT = (byte)'d';
|
||||||
|
private static final byte BE_STRING_SEP = (byte)':';
|
||||||
|
private static final byte BE_END = (byte)'e';
|
||||||
|
|
||||||
|
public BValue decode() throws BDecodeError {
|
||||||
|
BType type;
|
||||||
|
|
||||||
|
// consume and determine type
|
||||||
|
switch (data[offset]) {
|
||||||
|
case BE_INTEGER: type = BType.INTEGER; break;
|
||||||
|
case BE_LIST: type = BType.LIST; break;
|
||||||
|
case BE_DICT: type = BType.DICT; break;
|
||||||
|
default: throw new BDecodeUnknown(data, offset, data[offset]);
|
||||||
|
}
|
||||||
|
offset++;
|
||||||
|
|
||||||
|
if (type.equals(BType.INTEGER)) {
|
||||||
|
// advance until we hit end marker
|
||||||
|
int end = offset;
|
||||||
|
while (data[end] != BDecoder.BE_END) {
|
||||||
|
end++;
|
||||||
|
}
|
||||||
|
// convert bytes to string and string to integer
|
||||||
|
byte[] bytes = Arrays.copyOfRange(data, offset, end);
|
||||||
|
return new BInteger(parseLong(bytes));
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void decode(byte[] data) throws BDecodeError {
|
||||||
|
this.root = new BDecoder(data, 0).decode();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
package com.mykola2312.retracker.bencode.error;
|
||||||
|
|
||||||
|
public class BDecodeError extends Exception {
|
||||||
|
private static final long serialVersionUID = 4282658520481186036L;
|
||||||
|
|
||||||
|
public byte[] data;
|
||||||
|
public int offset;
|
||||||
|
|
||||||
|
public BDecodeError(byte[] data, int offset, String error) {
|
||||||
|
super(error);
|
||||||
|
this.data = data;
|
||||||
|
this.offset = offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BDecodeError(byte[] data, int offset) {
|
||||||
|
super(String.format("Error parsing data at offset %d", offset));
|
||||||
|
this.data = data;
|
||||||
|
this.offset = offset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,10 @@
|
||||||
|
package com.mykola2312.retracker.bencode.error;
|
||||||
|
|
||||||
|
public class BDecodeParseError extends BDecodeError {
|
||||||
|
private static final long serialVersionUID = 8987586062482975916L;
|
||||||
|
|
||||||
|
public BDecodeParseError(byte[] data, int offset, Throwable cause) {
|
||||||
|
super(data, offset, String.format("Failed to parse into primitive type at offset %d", offset));
|
||||||
|
initCause(cause);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,12 @@
|
||||||
|
package com.mykola2312.retracker.bencode.error;
|
||||||
|
|
||||||
|
public class BDecodeUnknown extends BDecodeError {
|
||||||
|
private static final long serialVersionUID = -7027240782119100714L;
|
||||||
|
|
||||||
|
byte unknown;
|
||||||
|
|
||||||
|
public BDecodeUnknown(byte[] data, int offset, byte unknown) {
|
||||||
|
super(data, offset, String.format("Unknown symbol 0x%x at offset %d", unknown));
|
||||||
|
this.unknown = unknown;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,27 @@
|
||||||
|
package com.mykola2312.retracker.bencode;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import com.mykola2312.retracker.bencode.error.BDecodeError;
|
||||||
|
|
||||||
|
public class BTreeTest {
|
||||||
|
@Test
|
||||||
|
public void testParseInt() throws BDecodeError {
|
||||||
|
final byte[] data = "i696969e".getBytes();
|
||||||
|
|
||||||
|
BTree tree = new BTree();
|
||||||
|
assertDoesNotThrow(() -> {
|
||||||
|
tree.decode(data);
|
||||||
|
|
||||||
|
BValue root = tree.getRoot();
|
||||||
|
assertNotNull(root);
|
||||||
|
assertEquals(root, new BInteger(696969));
|
||||||
|
|
||||||
|
System.out.println("testParseInt: " + root.toString());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Reference in a new issue