Class BDecodeNode
BDecodeNode provides structured access to bencoded data, the serialization format
used by BitTorrent for .torrent files and DHT messages. Bencode is a simple encoding that
supports integers, byte strings, lists, and dictionaries. BDecodeNode allows navigating
this hierarchical structure efficiently without parsing bencoded strings manually.
Understanding Bencode Format:
Integers: i42e → represents integer 42
Strings: 4:spam → represents "spam" (4-byte string)
Lists: l4:spami42ee → represents ["spam", 42]
Dicts: d3:agei42ee → represents {"age": 42}
Bencoding used extensively in BitTorrent:
- .torrent file metadata (announce, info hash, file list)
- DHT protocol messages (finds, nodes)
- Tracker responses (peer lists)
- Magnet link extensions
Parsing Torrent Files:
// Decode bencoded torrent file data
byte[] torrentData = Files.readAllBytes(Paths.get("download.torrent"));
BDecodeNode root = BDecodeNode.bdecode(torrentData);
// Navigate the dictionary structure
String announce = root.getString("announce"); // Tracker URL
long creationDate = root.getInt("creation date");
// Access nested structures
BDecodeNode info = root.getDict("info"); // The info dict
long pieceLength = info.getInt("piece length");
String pieces = info.getString("pieces"); // Raw piece hashes
// Access lists
BDecodeNode files = info.getList("files"); // Only in multi-file torrents
Typical Torrent File Structure (bencoded):
{
"announce": "http://tracker.example.com/announce",
"creation date": 1234567890,
"comment": "Optional comment",
"info": {
"piece length": 16384, // Typical: 16 KB
"pieces": "...20 bytes... → hex string of SHA-1 hashes (concat)",
"name": "filename.iso", // Single file torrent
"length": 1073741824 // File size in bytes
}
}
OR Multi-file structure:
{
"info": {
"piece length": 16384,
"pieces": "...",
"name": "directory",
"files": [
{"length": 100, "path": ["dir1", "file1.txt"]},
{"length": 200, "path": ["dir1", "file2.txt"]},
{"length": 300, "path": ["dir2", "file3.txt"]}
]
}
}
Key Methods for Dictionary Navigation:
BDecodeNode node = ...;
// Check if key exists and what type it is
if (node.hasString("name")) {
String name = node.getString("name");
}
if (node.hasInt("length")) {
long length = node.getInt("length");
}
if (node.hasDict("info")) {
BDecodeNode info = node.getDict("info");
}
if (node.hasList("files")) {
BDecodeNode files = node.getList("files");
}
// Type checking is important - wrong type returns null or 0
// Example: trying to getInt() on a string field returns 0, not error
Parsing DHT Messages:
// DHT messages are also bencoded
byte[] dhtResponse = receiveDhtMessage();
BDecodeNode msg = BDecodeNode.bdecode(dhtResponse);
// DHT message structure
String type = msg.getString("y"); // "r" for response, "q" for query
BDecodeNode response = msg.getDict("r");
if (response != null) {
// Extract nodes list: "nodes" is a string of packed node info
String nodesData = response.getString("nodes"); // 26 bytes per node (20 hash + 6 addr)
BDecodeNode values = response.getList("values"); // Peer list
}
Accessing List Elements:
BDecodeNode files = torrentInfo.getList("files");
// Iterate through files in multi-file torrent
// Note: Limitation - no direct iteration in current implementation
// Use with TorrentInfo or FileStorage for better file handling
// Direct file access is typically done through TorrentInfo
TorrentInfo ti = new TorrentInfo(torrentData);
FileStorage fs = ti.files();
for (int i = 0; i < fs.numFiles(); i++) {
String path = fs.filePath(i);
long size = fs.fileSize(i);
}
Error Handling:
try {
BDecodeNode root = BDecodeNode.bdecode(torrentData);
// Successfully parsed
} catch (IllegalArgumentException e) {
// Parsing failed - invalid bencoding format
System.err.println("Invalid torrent file: " + e.getMessage());
}
// Safe null checks for missing fields
BDecodeNode info = root.getDict("info");
if (info == null) {
throw new IllegalArgumentException("Missing 'info' dict in torrent");
}
Type Safety Pattern:
// Always use has* methods before accessing fields
if (node.hasDict("info")) {
BDecodeNode info = node.getDict("info"); // Safe - already checked
// Now process info...
}
// Avoid this:
BDecodeNode info = node.getDict("typo"); // Returns null - no error thrown
long x = info.getInt("field"); // NPE - null reference!
// Better:
if (node.hasDict("info")) {
info = node.getDict("info");
long x = info.getInt("field"); // Safe
}
Performance and Memory:
- Parsing does NOT copy the bencoded data - it creates views over the original bytes
- BDecodeNode holds a reference to the underlying byte buffer to prevent GC
- Navigation is O(1) for direct dict lookups, O(n) for list access
- Best for one-time parsing of static metadata (not streaming)
Common Torrent Fields Reference:
| Field | Type | Description |
|---|---|---|
| announce | String | Primary tracker URL |
| announce-list | List | List of tracker tiers (backup trackers) |
| creation date | Int | Unix timestamp when created |
| comment | String | Optional torrent description |
| created by | String | Client that created torrent |
| info | Dict | File metadata and hashes |
| info.name | String | File or directory name |
| info.piece length | Int | Size of each piece (16384 common) |
| info.pieces | String | Concatenated SHA-1 hashes (20 bytes each) |
| info.length | Int | File size (single-file torrents) |
| info.files | List | File entries (multi-file torrents) |
- See Also:
-
Constructor Summary
ConstructorsConstructorDescriptionBDecodeNode(com.frostwire.jlibtorrent.swig.bdecode_node n) Creates a BDecodeNode wrapping a native bdecode_node object.BDecodeNode(com.frostwire.jlibtorrent.swig.bdecode_node n, com.frostwire.jlibtorrent.swig.byte_vector buffer) Creates a BDecodeNode with reference to backing buffer for memory safety. -
Method Summary
Modifier and TypeMethodDescriptionstatic BDecodeNodebdecode(byte[] data) com.frostwire.jlibtorrent.swig.byte_vectorbuffer()Returns the buffer backing this node's data.longbooleanbooleanbooleanbooleancom.frostwire.jlibtorrent.swig.bdecode_nodeswig()Returns the underlying native SWIG object.toString()Returns a JSON-like string representation of this node and its contents.
-
Constructor Details
-
BDecodeNode
public BDecodeNode(com.frostwire.jlibtorrent.swig.bdecode_node n) Creates a BDecodeNode wrapping a native bdecode_node object.- Parameters:
n- the native bdecode_node object (usually from bdecode() or dict/list navigation)
-
BDecodeNode
public BDecodeNode(com.frostwire.jlibtorrent.swig.bdecode_node n, com.frostwire.jlibtorrent.swig.byte_vector buffer) Creates a BDecodeNode with reference to backing buffer for memory safety.The buffer reference prevents premature garbage collection of the bencoded data that this node references. This is critical when the node was created by bdecode() - without the buffer reference, the underlying bytes could be GC'd while the node is still in use, causing crashes or data corruption.
- Parameters:
n- the native bdecode_node objectbuffer- the byte buffer containing the original bencoded data
-
-
Method Details
-
swig
public com.frostwire.jlibtorrent.swig.bdecode_node swig()Returns the underlying native SWIG object.- Returns:
- the bdecode_node for direct native API access
-
buffer
public com.frostwire.jlibtorrent.swig.byte_vector buffer()Returns the buffer backing this node's data.This method returns the byte buffer used during parsing if this node was created via bdecode(), or null if constructed directly. The buffer is kept as a reference to prevent garbage collection of the data this node navigates.
- Returns:
- the pinned byte_vector buffer, or null if not created via bdecode()
-
toString
Returns a JSON-like string representation of this node and its contents. -
hasList
-
hasDict
-
hasString
-
hasInt
-
getList
-
getDict
-
getString
-
getInt
-
bdecode
-