bencode
Version, currently 0.1.03 versions
- 0.2.1latestJul 3, 2020
- 0.2.0not indexedJul 3, 2020
- 0.1.0not indexedJul 3, 2020
github.com/lbarasti/bencode
Serialize and deserialize Bencode-encoded strings.
Nothing has been indexed for 0.1.0 yet. The tag is recorded, its shard.yml has not been read, so the manifest and dependency list below are empty because they are unknown rather than because they are absent.
Installation
# Add this to your shard.yml
dependencies:
bencode:
github: lbarasti/bencode
version: ~> 0.1.0Then run:
shards installshard.yml
No shard.yml has been indexed for 0.1.0. You can read it on the repository.
Dependencies
Unknown: the shard.yml for this version has not been read yet.
README
This README is the one indexed from the repository at its latest ref, not from the tag for this version.
bencode
A Crystal shard providing serialization and deserialization utilities to parse and generate Bencode-encoded strings.
Installation
- Add the dependency to your
shard.yml:
dependencies:
bencode:
github: lbarasti/bencode
- Run
shards install
Usage
The Bencode module allows parsing and generating Bencode documents.
General type-safe interface
The general type-safe interface for parsing Bencode is to invoke T.from_bencode on a target type T and pass either a String or IO as an argument.
require "bencode"
bencode_text = "li1ei2ei3ee"
Array(Int64).from_bencode(bencode_text) # => [1, 2, 3]
bencode_text = "d1:xi1e1:yi2ee"
Hash(String, Int64).from_bencode(bencode_text) # => {"x" => 1, "y" => 2}
Serializing is achieved by invoking to_bencode, which returns a String, or to_bencode(io : IO), which will stream the Bencoding to an IO.
require "bencode"
[1, 2, 3].to_bencode # => "li1ei2ei3ee"
{"x" => 1, "y" => 2}.to_bencode # => "d1:xi1e1:yi2ee"
In line with the Bencode specification, the following types in the standard library have been extended to implement these methods.
- String
- Int64
- Array(T)
- Hash(String, T)
provided that T.from_bencode(String) and T#to_bencode(io) methods are defined for the type T. For user-defined types you can define these yourself - from_bencode for parsing and to_bencode for serializing - or you can include Bencode::Serializable in your struct or class.
Serializing custom types
The Bencode::Serializable module automatically generates methods for Bencode serialization when included.
To change how individual instance variables are parsed and serialized, the annotation Bencode::Field can be placed on the instance variable. Annotating property, getter and setter macros is also allowed.
require "bencode"
class Location
include Bencode::Serializable
@[Bencode::Field(key: "long")]
property lng : Int64
property lat : Int64
def initialize(@lat, @lng)
end
end
class House
include Bencode::Serializable
property address : String
property location : Location
def initialize(@address, @location)
end
end
house = House.from_bencode("d7:address17:Crystal Road 12348:locationd3:lati12e4:longi34eee")
house.address # => "Crystal Road 1234"
house.location # => #<Location:0x10cd93d80 @lat=12, @lng=34>
house.to_bencode # => "d7:address17:Crystal Road 12348:locationd3:lati12e4:longi34eee"
houses = Array(House).from_bencode("ld7:address17:Crystal Road 12348:locationd3:lati12e4:longi34eeee")
houses.size # => 1
houses.to_bencode # => "ld7:address17:Crystal Road 12348:locationd3:lati12e4:longi34eeee"
Parsing with Bencode.parse
Bencode.parse will return a Bencode::Type, which is an alias for the union of all possible Bencode types. This makes it necessary to cast the parsed object to the expected type, in order to traverse it.
require "bencode"
value = Bencode.parse("li1ei2ei3ee") # : Bencode::Type
value.as(Array)[0] # => 1
typeof(value.as(Array)[0]) # => Bencode::Type
value.as(Array)[0].as(Int) # => 1
typeof(value.as(Array)[0].as(Int)) # => Int64
value.as_a[0] + 1 # Error, because value[0] is Bencode::Type
value.as_a[0].as(Int) + 10 # => 11
Bencode.parse can read from an IO directly (such as a file) which saves allocating a string:
require "bencode"
bencode = File.open("path/to/file.torrent") do |file|
Bencode.parse(file)
end
Parsing with Bencode.parse is useful for dealing with a dynamic Bencode structure.
Custom parsing with _from_bencode
If Bencode::Serializable does not fit your needs, you can define a custom deserializer for an object of type T by defining a _from_bencode(Bencode::Type) : T class method on it. For example:
record A, a : String, b : Int64 do
def self._from_bencode(obj : Bencode::Type)
a, b = obj.as(Array)
new a.as(String), b.as(Int64)
end
end
a = A.from_bencode "l5:helloi-42ee" # => A(@a="hello", @b=-42)
Generating with to_bencode
to_bencode and to_bencode(IO) methods are provided for primitive types, but you need to define to_bencode(IO) for custom objects, either manually or using Bencode::Serializable.
Credits
- Hamdiakoguz's bencoding.cr has been a huge inspiration for the implementation of the parser. I would have used their shard directly, if it wasn't that
crystalhas evolved with some breaking API changes since the shard was last updated - I also used jackpal's bencode-go as a reference implementation
- This repo's readme, examples and API are inspired by Crystal's JSON package - JSON::Serializable, in particular.
Development
Just check out the repository and run crystal spec to run the tests.
Contributing
- Fork it (https://github.com/lbarasti/bencode/fork)
- Create your feature branch (
git checkout -b my-new-feature) - Commit your changes (
git commit -am 'Add some feature') - Push to the branch (
git push origin my-new-feature) - Create a new Pull Request
Contributors
- lbarasti - creator and maintainer
Documentation
Built from the current release. The first visit to a release nobody has asked for starts its build.
Links
This release
- Version
0.1.0- Tagged
- Jul 3, 2020
- Commit
753877188ea5- Indexed
- not yet
Dependents
Repository
github.com/lbarasti/bencode
Metadata
- Created
- Aug 12, 2026
- Updated
- Sep 25, 2026
- Synced
- Sep 25, 2026
- Versions
- 3