# BinData turns binary format layouts into Ruby classes

> BinData is a Ruby gem that lets you describe a binary file layout as a class and then read or write it, replacing hand written pack and unpack code. The gem is small, permissive and still worked on recently, but its documentation lives mostly in a wiki.

**dmendel/bindata** — BinData - Reading and Writing Binary Data in Ruby

- Repository: https://github.com/dmendel/bindata
- Stars: 653 · Forks: 58
- Language: Ruby
- License: BSD-2-Clause
- Published: 2026-10-21 · Updated: 2026-10-21 · Language: en
- Canonical page: https://hysenlabs.com/projects/dmendel-bindata

## The unpack script that BinData is designed to replace

BinData starts from a familiar complaint. Hand parsing a binary file in Ruby means reading a fixed number of bytes, unpacking them, and then trusting that every later read starts in the right place. The project opens with exactly that shape of code: open a file, read two bytes and unpack them, read that many bytes as a name, read eight more bytes and unpack them as a pair of unsigned integers. The project's own comment calls it ugly and says it violates DRY, and the pain is specific rather than cosmetic. Every field is described by an offset and a byte count that has to be kept in sync by hand, and a format with a variable length field anywhere in the middle makes the arithmetic worse.

The audience is therefore Ruby developers who already understand binary formats: protocol parsers, file format readers, packet decoders. The gem is not aimed at people learning about binary data, and it does not try to reverse engineer an unknown format for you. It takes a format you already understand and gives you a declarative way to write it down once.

## A Rectangle class is the whole idea in eight lines

The rewritten version in the project description is the clearest evidence of the design. The same rectangle that took four hand written reads now comes from a class declaration:

```ruby
class Rectangle < BinData::Record
  endian :little
  uint16 :len
  string :name, :read_length => :len
  uint32 :width
  uint32 :height
end
```

The class inherits from BinData::Record, and each method call in the body names one field of the format. The programmer states what the format is, and BinData works out how to read and write data in that format, which is the phrase the project uses to describe its declarative approach. The comparison the project draws is with Ruby's pack and unpack methods, and the argument is readability, not raw speed.

Reading then looks like a single call, Rectangle.read(io), and field access becomes ordinary attribute access on the result, so r.name, r.width and r.height replace the intermediate local variables. The same class is used for writing, since a record that can be parsed can be serialised, which is the point of describing the layout rather than the parsing routine.

## endian and read_length cover the two fields that break naive parsers

Two details in the Rectangle class carry most of the practical weight. The first is the endian directive, which is set to :little at the top of the class and therefore applies to the integer fields that follow. Byte order is a property of the format, not of a single field, and setting it once at record level keeps uint16 and uint32 consistent with each other instead of asking the developer to remember it at every call site.

The second is the length dependent field. The name is not a fixed width string: its size comes from the value of the len field read immediately before it, expressed as string :name, :read_length => :len. Support for dependent and variable length fields is built in, and this line is the mechanism. A parser that reads a length and then uses it has to track two pieces of state at once, and hand written code tends to get that wrong on truncated or short input.

That is also where the failure mode sits. Because a field's length is computed from a value read from the stream, a corrupt or truncated file can ask the reader to consume a number of bytes that the file does not have. The project description does not spell out how such a case is reported, so if your formats come from untrusted sources you would need to confirm the error behaviour yourself before relying on it.

## gem install bindata is the entire setup

Installation is one command, taken from the project's Installation section:

```bash
gem install bindata
```

There is no build step, no system dependency and no configuration file in the repository layout to edit. The top level entries are the usual Ruby gem furniture: bindata.gemspec, Gemfile, Rakefile, INSTALL, LICENSE, NEWS.rdoc, ChangeLog.rdoc, README.md, plus the lib/ and test/ directories and a .github/ directory. The .github/ entry is where the CI workflow badge in the project description points, and the coverage badge points at a Coveralls service, so both test running and coverage measurement are wired into the project already.

The first real use after that is to define a record class of your own and read a file with it, exactly as the Rectangle example does. Since the gem ships as a plain gem, you can add it to a Gemfile and lock it there, though the project does not publish GitHub releases, so there is no tag to pin against and no published changelog entry to review at release time.

## The examples directory points at the formats people use it for

The repository includes an examples/ directory with a small set of files whose names suggest the intended use cases: examples/nbt.rb, examples/NBT.txt, examples/gzip.rb, examples/ip_address.rb, examples/tcp_ip.rb and examples/list.rb. The file names alone tell you that the author expected people to describe network addresses, a TCP header, a compressed archive container and a nested container format, but the repository does not label the formats each file targets beyond its name, so treat the list as a map of interest rather than as documented scope.

What is notable is the shape of the set. There is one file per format, each presumably a self contained declaration of a layout, which is the argument for a declarative library in one line: a format description is short enough to read in a minute, and each new format means a new small file rather than a new branch of parsing code.

The library itself lives in lib/, and the test suite in test/. There is no separate documentation directory in the top level entries, which is the single largest structural difference between this project and the many gems that ship their manual next to the code.

## Documentation sits in a wiki, and the repository keeps no homepage

The Documentation section of the project description points to the BinData manual at the GitHub wiki of the same account, and that wiki is where the API reference, the type list and the usage guidance live. Nothing in the repository itself documents the supported primitive types, the error classes or how to extend the library with new data types, even though creating new data types is one of the stated capabilities. That is a genuine constraint for a library that others will extend.

It also means the docs and the code can drift apart without a version marker telling you which release a page describes. The repository has no homepage field and no GitHub releases, so there is no published site that pins documentation to a gem version, and no release announcement that says what changed. The maintenance metadata does give one solid fact: the last push was on 2026-10-11, and the repository is not archived, so the code is being worked on.

The licence is BSD-2-Clause, the two clause permissive variant, which is generally easy to incorporate into commercial and closed source products. Licence obligations and compatibility with your own licence terms are a matter for your legal team, but the licence itself places no copyleft requirement on code that links against the gem.

## Where BinData is the wrong choice

The clearest wrong choice is a project that needs a stable public API contract. A gem maintained largely through a wiki, with no tagged releases and no homepage, gives downstream users no versioned reference to pin behaviour to, and any breaking change would have to be discovered by reading the ChangeLog.rdoc and NEWS.rdoc files in the repository.

The second wrong choice is a high throughput pipeline where per field overhead matters. The approach replaces arithmetic with object graph construction, so each parsed field becomes an object with accessors rather than a local variable, and a file with millions of records pays that cost repeatedly. The project presents itself as an easier and more readable alternative to pack and unpack, and it never claims to be the fastest option, so a streaming parser tuned for one fixed layout remains a legitimate alternative.

A third wrong choice is a language other than Ruby. There is no port worth counting on, and the whole model, from the class based declaration to the endian directive, is idiomatic Ruby rather than a portable format description.

## What to check before you depend on the gem

Two checks carry most of the weight. First, install the gem in a scratch project with gem install bindata and read the wiki manual against your own format, because the repository itself will not tell you which primitives exist or how dependent fields behave when the stream ends early. Second, look through the test/ directory to see which formats the library is actually exercised against, since that directory is a better guide to real coverage than the badge in the project description.

If both pass, the upgrade story is simple and cheap. There is no server, no database and no migration to run, and a gem version bump is a change to one line in a Gemfile lock. The risk that remains is organisational rather than technical: a single named maintainer, a wiki outside the repository and no release tags mean that a sudden discontinuation would leave you with a working but unmaintained dependency, so pinning the version and keeping the format declarations inside your own repository is the sensible precaution.

## Conclusion

Adopt BinData if your team maintains Ruby code that hand parses binary files and can live with a single maintainer and a wiki outside the repository. Skip it if you need versioned releases, an API contract in the repository, or documentation you can read offline. Before committing, check the wiki manual against your actual format, confirm the gem still installs on your Ruby version with gem install bindata, and look at the example files under examples/ to see whether the style matches the code you want to write.

## FAQ

### What is BinData used for in Ruby?

BinData is a Ruby gem for reading and writing structured binary data from a declarative description of the format. The project positions it as an easier and more readable alternative to Ruby's pack and unpack methods, and it supports the common primitive types plus dependent and variable length fields.

### How do I install BinData?

The project gives a single command, gem install bindata. There is no build step and no configuration file, and the gem can also be added to a Gemfile and locked there.

### Where is the BinData documentation?

The manual is the BinData wiki hosted in the GitHub wiki of the project account, linked from the Documentation section of the project description. The repository itself has no documentation directory and no homepage.

### Is BinData still maintained?

The repository is not archived and the last push was on 2026-10-11, so work on the code was recent at the time of writing. The repository has no GitHub releases, so there is no published version history beyond the ChangeLog.rdoc and NEWS.rdoc files it contains.

### What licence does BinData use?

BinData is distributed under BSD-2-Clause, the two clause permissive variant, with the licence text in the LICENSE file at the top level of the repository. It carries no copyleft requirement for code that uses the gem.

## Sources

- [dmendel/bindata on GitHub](https://github.com/dmendel/bindata)
- [Issues](https://github.com/dmendel/bindata/issues)
- [License: BSD-2-Clause](https://github.com/dmendel/bindata/blob/master/LICENSE)
- [README](https://github.com/dmendel/bindata/blob/master/README.md)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/dmendel-bindata
