full bencode decoding is done now, later may need even more tests

This commit is contained in:
mykola2312 2024-10-15 09:02:36 +03:00
parent f5ab05acdc
commit c1dcb19f9d
3 changed files with 100 additions and 12 deletions

View file

@ -6,7 +6,8 @@ import java.util.Arrays;
import com.mykola2312.retracker.bencode.error.BDecodeError;
import com.mykola2312.retracker.bencode.error.BDecodeMalformed;
import com.mykola2312.retracker.bencode.error.BDecodeParseError;
import com.mykola2312.retracker.bencode.error.BDecodeUnknown;
import com.mykola2312.retracker.bencode.error.BErrorNoRoot;
import com.mykola2312.retracker.bencode.error.BErrorValueCast;
public class BTree {
private BValue root = null;
@ -24,12 +25,23 @@ public class BTree {
this.offset = offset;
}
private Long parseLong(byte[] bytes) throws BDecodeParseError {
// used to parse string lengths
private int parseInt(byte[] bytes) throws BDecodeParseError {
String strInt = new String(bytes, StandardCharsets.UTF_8);
try {
return Integer.parseInt(strInt);
} catch (NumberFormatException e) {
throw new BDecodeParseError(data, offset, e, strInt);
}
}
// used for BInteger
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);
throw new BDecodeParseError(data, offset, e, strInt);
}
}
@ -75,13 +87,55 @@ public class BTree {
// advance past terminator
offset++;
return list;
} else if (type == BDecoder.BE_DICT) {
BDict dict = new BDict();
// same business as BList, but we read pairs of values, key and value
while (offset < data.length && data[offset] != BDecoder.BE_END) {
BValue key = decode();
BValue value = decode();
dict.set(key, value);
}
// advance past terminator
offset++;
return dict;
} else { // string
// since string does not have leading type byte, move back offset
offset--;
// find string separator
int sep = offset;
while (sep < data.length && data[sep] != BDecoder.BE_STRING_SEP) {
sep++;
}
if (sep == data.length) {
throw new BDecodeMalformed(data, offset, "no string separator");
}
// everything before sep is integer of string length
int length = parseInt(Arrays.copyOfRange(data, offset, sep));
if (offset + length >= data.length) {
throw new BDecodeMalformed(data, offset, "string length is bigger than data");
}
BString string = new BString(Arrays.copyOfRange(data, sep + 1, sep + 1 + length));
offset = sep + 1 + length;
return string;
}
throw new BDecodeUnknown(data, offset, type);
}
}
public void decode(byte[] data) throws BDecodeError {
this.root = new BDecoder(data, 0).decode();
}
public BDict asDict() throws BErrorNoRoot, BErrorValueCast {
if (root == null) {
throw new BErrorNoRoot();
}
if (!root.getType().equals(BType.DICT)) {
throw new BErrorValueCast(root, "", BType.DICT, root.getType());
}
return (BDict)root;
}
}

View file

@ -3,8 +3,8 @@ 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));
public BDecodeParseError(byte[] data, int offset, Throwable cause, String offense) {
super(data, offset, String.format("Failed to parse into primitive type at offset %d: %s", offset, offense));
initCause(cause);
}
}

View file

@ -21,7 +21,7 @@ public class BTreeTest {
BValue root = tree.getRoot();
assertNotNull(root);
assertEquals(root, new BInteger(696969));
assertEquals(new BInteger(696969), root);
System.out.println("testParseInt: " + root.toString());
});
@ -47,7 +47,7 @@ public class BTreeTest {
BValue root = tree.getRoot();
assertNotNull(root);
assertEquals(((BList)root).getLength(), 0);
assertEquals(0, ((BList)root).getLength());
});
}
@ -70,16 +70,50 @@ public class BTreeTest {
}
}
assertDoesNotThrow(() -> {
assertEquals(list.get(BType.INTEGER, 0), new BInteger(1));
assertEquals(list.get(BType.INTEGER, 1), new BInteger(2345678));
assertEquals(new BInteger(1), list.get(BType.INTEGER, 0));
assertEquals(new BInteger(2345678), list.get(BType.INTEGER, 1));
BInteger value = list
.<BList>get(BType.LIST, 2)
.<BInteger>get(BType.INTEGER, 0);
assertNotNull(value);
assertEquals(value, new BInteger(69));
assertEquals(new BInteger(69), value);
System.out.println("value from nested list: " + value);
});
});
}
@Test
public void testString() throws BDecodeError {
final byte[] data = "11:testString!".getBytes();
BTree tree = new BTree();
assertDoesNotThrow(() -> {
tree.decode(data);
assertNotNull(tree.getRoot());
BString string = (BString)tree.getRoot();
System.out.println("string: " + string);
assertEquals(new BString("testString!"), string);
});
}
@Test
public void testDict() throws BDecodeError {
final byte[] data = "di0e5:first6:secondi69ee".getBytes();
BTree tree = new BTree();
assertDoesNotThrow(() -> {
tree.decode(data);
BDict root = tree.asDict();
// check keys
assertEquals(new BInteger(0), root.find(new BInteger(0)));
assertEquals(new BString("second"), root.find(new BString("second")));
// check values
assertEquals(new BString("first"), root.get(new BInteger(0)));
assertEquals(new BInteger(69), root.<BInteger>get(BType.INTEGER, "second"));
});
}
}