implement BList decoding, fix numerous bugs in BList

This commit is contained in:
mykola2312 2024-10-15 04:06:26 +03:00
parent 695d21f851
commit 24e9568f89
4 changed files with 71 additions and 12 deletions

View file

@ -36,6 +36,12 @@ public class BList extends BValue {
// builder
public BList append(BValue item) {
// do not allow any nulls pass into list, because it fucks up everything
if (item == null) {
// fail earlier than fail horribly
throw new NullPointerException();
}
BValue first = getFirst();
BValue last = getLast();
if (first == null) {

View file

@ -6,6 +6,7 @@ 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;
public class BTree {
private BValue root = null;
@ -39,8 +40,7 @@ public class BTree {
private static final byte BE_END = (byte)'e';
public BValue decode() throws BDecodeError {
BType type;
if (data.length < 2) {
if (data.length - offset < 2) {
/* no bencode data can be less than 2 bytes: for integer must be 'i0e' atleast,
* strings must be at least '0:' and lists and dicts can be empty
*/
@ -48,15 +48,9 @@ public class BTree {
}
// 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: type = BType.STRING;
}
offset++;
byte type = data[offset++];
if (type.equals(BType.INTEGER)) {
if (type == BDecoder.BE_INTEGER) {
// advance until we hit end marker
int end = offset;
while (end < data.length && data[end] != BDecoder.BE_END) {
@ -67,10 +61,23 @@ public class BTree {
}
// convert bytes to string and string to integer
byte[] bytes = Arrays.copyOfRange(data, offset, end);
// after parsing integer set new offset and advance past terminator
offset = end + 1;
return new BInteger(parseLong(bytes));
} else if (type == BDecoder.BE_LIST) {
BList list = new BList();
// we're going to use recursion to read elements until we hit end
while (offset < data.length && data[offset] != BDecoder.BE_END) {
BValue item = decode();
list.append(item);
}
// advance past terminator
offset++;
return list;
}
return null;
throw new BDecodeUnknown(data, offset, type);
}
}

View file

@ -6,7 +6,7 @@ public class BDecodeUnknown extends BDecodeError {
byte unknown;
public BDecodeUnknown(byte[] data, int offset, byte unknown) {
super(data, offset, String.format("Unknown symbol 0x%x at offset %d", unknown));
super(data, offset, String.format("Unknown symbol 0x%x at offset %d", unknown, offset));
this.unknown = unknown;
}
}

View file

@ -36,4 +36,50 @@ public class BTreeTest {
tree.decode(data);
});
}
@Test
public void testEmptyList() throws BDecodeError {
final byte[] data = "le".getBytes();
BTree tree = new BTree();
assertDoesNotThrow(() -> {
tree.decode(data);
BValue root = tree.getRoot();
assertNotNull(root);
assertEquals(((BList)root).getLength(), 0);
});
}
@Test
public void testList() throws BDecodeError {
final byte[] data = "li1ei2345678eli69eee".getBytes();
BTree tree = new BTree();
assertDoesNotThrow(() -> {
tree.decode(data);
assertNotNull(tree.getRoot());
BList list = (BList)tree.getRoot();
{
System.out.println(String.format("BDecode'd list size: %d", list.getLength()));
System.out.println("BDecode'd list contents:");
for (BValue item : list) {
System.out.println(item.toString());
}
}
assertDoesNotThrow(() -> {
assertEquals(list.get(BType.INTEGER, 0), new BInteger(1));
assertEquals(list.get(BType.INTEGER, 1), new BInteger(2345678));
BInteger value = list
.<BList>get(BType.LIST, 2)
.<BInteger>get(BType.INTEGER, 0);
assertNotNull(value);
assertEquals(value, new BInteger(69));
System.out.println("value from nested list: " + value);
});
});
}
}