diff --git a/src/main/java/com/mykola2312/retracker/bencode/BTree.java b/src/main/java/com/mykola2312/retracker/bencode/BTree.java index d24b544..71937bf 100644 --- a/src/main/java/com/mykola2312/retracker/bencode/BTree.java +++ b/src/main/java/com/mykola2312/retracker/bencode/BTree.java @@ -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; + } } diff --git a/src/main/java/com/mykola2312/retracker/bencode/error/BDecodeParseError.java b/src/main/java/com/mykola2312/retracker/bencode/error/BDecodeParseError.java index 3ceedad..27712f0 100644 --- a/src/main/java/com/mykola2312/retracker/bencode/error/BDecodeParseError.java +++ b/src/main/java/com/mykola2312/retracker/bencode/error/BDecodeParseError.java @@ -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); } } diff --git a/src/test/java/com/mykola2312/retracker/bencode/BTreeTest.java b/src/test/java/com/mykola2312/retracker/bencode/BTreeTest.java index 3914595..721493e 100644 --- a/src/test/java/com/mykola2312/retracker/bencode/BTreeTest.java +++ b/src/test/java/com/mykola2312/retracker/bencode/BTreeTest.java @@ -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 .get(BType.LIST, 2) .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.get(BType.INTEGER, "second")); + }); + } }