How to write bitstring as read such as 001 1101 on Elixir
Tags: elixir
Published: 2019-05-03 15:05:40

---
title: How to write bitstring as read such as 001 1101 on Elixir
published: true
description: How to write bitstring as read such as 001 1101 on Elixir
tags: elixir
---

UPDATE: Added binary handling with using `0b` and [`:binary.encode_unsigned/1`](http://erlang.org/doc/man/binary.html#encode_unsigned-1). Thanks for [Elixir Forum](https://elixirforum.com/t/elixir-blog-posts/150/475?u=niku).

# My motivation

I want to write bits like ` 010 1100  000 0010` (from [protocol buffer's encoding sample](https://developers.google.com/protocol-buffers/docs/encoding)) on Elixir particular for testing.

Please note, most of cases we have to handle on erlang or elixir is [binary which means bitstring to be divisible by 8](http://erlang.org/doc/programming_examples/bit_syntax.html). If you wanted to write binary as you read, you may use integer literal starting `0b` and [`:binary.encode_unsigned/1`](http://erlang.org/doc/man/binary.html#encode_unsigned-1) such as:

```elixir
:binary.encode_unsigned(0b1010_1100_1111_1111)
# => <<172, 255>>
```

However, I want to handle bitstring which is not divisible by 8, for example 3bits, 7bits, 17bits and so on. `binary.encode_unsigned/1` aligns to binary.

```elixir
bit_size(:binary.encode_unsigned(0b101))
# => 8
```

In above case, I wanted to get 3 as bit size but 8. So I can't use `0b` and `:binary.encode_unsigned/1` for "writing what I read".

I know writing bitstring as I read has already be able to do following like:

```elixir
<<0::1, 1::1, 0::1, 1::1, 1::1, 0::1, 0::1, 0::1, 0::1, 0::1, 0::1, 0::1, 1::1, 0::1>>
# => <<88, 2::size(6)>>
```

It is correct. Because, in Integer, a representation of 8bits ` 010 1100  0` is `<<88>>` (you can check `Integer.digits(88, 2) # => [1, 0, 1, 1, 0, 0, 0]`, please note heading `0`s are dropped) and the rest `00 0010` is `<<2::size(6)>>`. 

However, I feel the former `<<0::1, 1::1, ...>>` is quite verbose and the latter `<<88, 2::size(6)>>` is not intuitive. Don't you feel so?

I want to write bits as read such as:

```elixir
010 1100 000 0010 
# => <<88, 2::size(6)>>
```

# My solution

[sigil](https://elixir-lang.org/getting-started/sigils.html) is

> one of the mechanisms provided by the language for working with textual representations.

That's I want.

First time, I implement as function following like:

```elixir
# You can check the code on iex
defmodule BitsSigils do
  require Logger

  def sigil_b(term, _modifiers) do
    for <<c <- term>>,
      into: <<>> do
        case c do
          ^c when c in [?0, ?1] ->
            # It's a valid character as an element.
            <<c::1>>
          ^c when c in [?_, ?\s] ->
            # It's a valid character as a just placeholder.
            <<>>
          _ ->
            Logger.warn("Given unexpected sigil_b character, just ignored. The character was \"#{:binary.encode_unsigned(c)}\", its integer value was #{c}.")
            <<>>
        end
    end
  end
end

import BitsSigils
~b(_010 1100 _000 0010)
# => <<88, 2::size(6)>>
```

Yeah, It seems to work fine. But it doesn't work as a left side on a pattern matching.

```elixir
case <<88, 2::size(6)>> do
  ~b(_010 1100 _000 0010) ->
    true
  _ ->
    false
end

# ** (CompileError) iex:16: cannot invoke remote function BitsSigils.sigil_b/2 inside a match
#     (stdlib) lists.erl:1354: :lists.mapfoldl/3
#     (stdlib) lists.erl:1354: :lists.mapfoldl/3
```

So finally, I implement `sigil_b` as a macro following like: 

```elixir
# You can check the code on iex
defmodule BitsSigils do
  require Logger

  defmacro sigil_b({:<<>>, meta, [arg]}, modifiers) do
    result = do_sigil_b(arg, modifiers)
    {:<<>>, meta, [Macro.escape(result)]}
  end

  def do_sigil_b(term, _modifiers) do
    for <<c <- term>>,
      into: <<>> do
        case c do
          ^c when c in [?0, ?1] ->
            # It's a valid character as an element.
            <<c::1>>
          ^c when c in [?_, ?\s] ->
            # It's a valid character as a just placeholder.
            <<>>
          _ ->
            Logger.warn("Given unexpected sigil_b character, just ignored. The character was \"#{:binary.encode_unsigned(c)}\", its integer value was #{c}.")
            <<>>
        end
    end
  end
end

import BitsSigils
case <<88, 2::size(6)>> do
  ~b(_010 1100 _000 0010) ->
    true
  _ ->
    false
end
# => true
```

# Conclusion

- Most of cases you have to handle is **binary** which is special case of bitstring. In such cases, You may use `0b` and [`:binary.encode_unsigned/1`](http://erlang.org/doc/man/binary.html#encode_unsigned-1)
- If you want to get something textual representation, you could use [sigil](https://elixir-lang.org/getting-started/sigils.html).
- If you want to use sigil at left hand side on an evaluation, you had to use macro instead of function.