Current section
Files
Jump to
Current section
Files
lib/rns/hdlc.ex
defmodule RNS.HDLC do
@moduledoc """
HDLC framing.
This seems to be based on [HDLC](https://en.wikipedia.org/wiki/HDLC) but does not have addresses or anything, just the flag and escaping...
"""
@doc """
Frame raw data into HDLC.
Escapes the data and puts it between two HDLC flags.
The code might be a bit ugly, so let me explain it:
It returns a binary, and you can see that it ends and starts with `0x7E`, which is the flag that show the start and end of the HDLC data.
The complicated part is with the `String.replace/4`, it replaces the characters `0x7E`(the flag) and `0x7D`(the escape character) by `0x7D`(the escape character)
and the character that is getting replaced xored with `0x20`. There is also `String.to_charlist/1` and `hd/1`, that's just to convert the binary(`String`) to an `Integer`.
Basically, `<<0x7E>>` is replaced by `<<0x7D, 0x5E>>` and `<<0x7D>>` by `<<0x7D, 0x5D>>`.
"""
@spec frame(binary()) :: binary()
def frame(raw) do
<<0x7E,
String.replace(raw, [<<0x7E>>, <<0x7D>>], fn char ->
int =
char
|> String.to_charlist()
|> hd
<<0x7D, Bitwise.bxor(int, 0x20)>>
end)::binary, 0x7E>>
end
@doc """
Unframes HDLC data.
Removes HDLC flags or gives an error if they are not present, and unescapes the characters.
"""
@spec unframe(binary()) :: {:ok, binary()} | {:error, :no_hldc_flags}
def unframe(raw) do
case raw do
<<0x7E, unframed::binary-size(byte_size(raw) - 2), 0x7E>> ->
escaped =
String.replace(unframed, [<<0x7D, 0x5E>>, <<0x7D, 0x5D>>], fn string ->
int =
String.at(string, 1)
|> String.to_charlist()
|> hd
Bitwise.bxor(int, 0x20)
end)
{:ok, escaped}
_ ->
{:error, :no_hdlc_flags}
end
end
end