Class BDecodeNode

java.lang.Object
com.frostwire.jlibtorrent.BDecodeNode

public final class BDecodeNode extends Object
Parser and wrapper for bencoded data structures (BitTorrent metadata format).

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:

BDecode Node Data Types
FieldTypeDescription
announceStringPrimary tracker URL
announce-listListList of tracker tiers (backup trackers)
creation dateIntUnix timestamp when created
commentStringOptional torrent description
created byStringClient that created torrent
infoDictFile metadata and hashes
info.nameStringFile or directory name
info.piece lengthIntSize of each piece (16384 common)
info.piecesStringConcatenated SHA-1 hashes (20 bytes each)
info.lengthIntFile size (single-file torrents)
info.filesListFile entries (multi-file torrents)
See Also:
  • 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 object
      buffer - 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

      public String toString()
      Returns a JSON-like string representation of this node and its contents.
      Overrides:
      toString in class Object
      Returns:
      formatted string showing the structure and values
    • hasList

      public boolean hasList(String key)
    • hasDict

      public boolean hasDict(String key)
    • hasString

      public boolean hasString(String key)
    • hasInt

      public boolean hasInt(String key)
    • getList

      public BDecodeNode getList(String key)
    • getDict

      public BDecodeNode getDict(String key)
    • getString

      public String getString(String key)
    • getInt

      public long getInt(String key)
    • bdecode

      public static BDecodeNode bdecode(byte[] data)