From 92325c05b1c3fff6f2fae3357886c26c1d804ecb Mon Sep 17 00:00:00 2001 From: Kodi Craft Date: Sun, 7 Jul 2024 18:23:14 +0200 Subject: [PATCH] Implement deserialization for varints and varlongs --- lib/data.ex | 40 +++++++++++++++++++++++++++++++++++++++- test/data_test.exs | 41 ++++++++++++++++++++++++++++++++++++++++- 2 files changed, 79 insertions(+), 2 deletions(-) diff --git a/lib/data.ex b/lib/data.ex index ac87ee1..a084805 100644 --- a/lib/data.ex +++ b/lib/data.ex @@ -77,7 +77,7 @@ defmodule Amethyst.Minecraft.Read do {[], binary} end - def bool({acc, <>}) do + def bool({acc, <>}) do {[value != 0 | acc], rest} end @@ -112,4 +112,42 @@ defmodule Amethyst.Minecraft.Read do def double({acc, <>}) do {[value | acc], rest} end + + def varint(tuple, read \\ 0, nacc \\ 0) + def varint({acc, <<1::1, value::7, rest::binary>>}, read, nacc) when read < 5 do + varint({acc, rest}, read + 1, nacc + (value <<< (7 * read))) + end + def varint({acc, <<0::1, value::7, rest::binary>>}, read, nacc) do + total = nacc + (value <<< (7 * read)) + <> = <> + {[value | acc], rest} + end + def varint(_, read, _) when read >= 5 do + raise RuntimeError, "Got a varint which is too big!" + end + def varint({_, ""}, _, _) do + raise RuntimeError, "Got an incomplete varint!" + end + + def varlong(tuple, read \\ 0, nacc \\ 0) + def varlong({acc, <<1::1, value::7, rest::binary>>}, read, nacc) when read < 10 do + varlong({acc, rest}, read + 1, nacc + (value <<< (7 * read))) + end + def varlong({acc, <<0::1, value::7, rest::binary>>}, read, nacc) do + total = nacc + (value <<< (7 * read)) + <> = <> + {[value | acc], rest} + end + def varlong(_, read, _) when read >= 10 do + raise RuntimeError, "Got a varint which is too big!" + end + def varlong({_, ""}, _, _) do + raise RuntimeError, "Got an incomplete varint!" + end + + def string({acc, data}) do + {[length], rest} = start(data) |> varint() + <> = rest + {[value | acc], rest} + end end diff --git a/test/data_test.exs b/test/data_test.exs index b3fd4c7..51a19b9 100644 --- a/test/data_test.exs +++ b/test/data_test.exs @@ -1,7 +1,11 @@ defmodule WriteTest do use ExUnit.Case + + @moduledoc """ + This module contains tests for the Amethyst.Minecraft.Write module. + """ + doctest Amethyst.Minecraft.Write - doctest Amethyst.Minecraft.Read test "writing a varint" do assert Amethyst.Minecraft.Write.varint(0) == <<0x00>> @@ -28,3 +32,38 @@ defmodule WriteTest do assert Amethyst.Minecraft.Write.string("") == <<0x00>> end end + +defmodule ReadTest do + use ExUnit.Case + + @moduledoc """ + This module contains tests for the Amethyst.Minecraft.Read module. + """ + + doctest Amethyst.Minecraft.Read + + test "reading a varint" do + assert Amethyst.Minecraft.Read.start(<<0x00>>) |> Amethyst.Minecraft.Read.varint() == {[0], ""} + assert Amethyst.Minecraft.Read.start(<<0x7F>>) |> Amethyst.Minecraft.Read.varint() == {[127], ""} + assert Amethyst.Minecraft.Read.start(<<0x80, 0x01>>) |> Amethyst.Minecraft.Read.varint() == {[128], ""} + assert Amethyst.Minecraft.Read.start(<<0xFF, 0x01>>) |> Amethyst.Minecraft.Read.varint() == {[255], ""} + assert Amethyst.Minecraft.Read.start(<<0xFF, 0xFF, 0xFF, 0xFF, 0x07>>) |> Amethyst.Minecraft.Read.varint() == {[2_147_483_647], ""} + assert Amethyst.Minecraft.Read.start(<<0xFF, 0xFF, 0xFF, 0xFF, 0x0F>>) |> Amethyst.Minecraft.Read.varint() == {[-1], ""} + assert Amethyst.Minecraft.Read.start(<<0x80, 0x80, 0x80, 0x80, 0x08>>) |> Amethyst.Minecraft.Read.varint() == {[-2_147_483_648], ""} + end + + test "reading a varlong" do + assert Amethyst.Minecraft.Read.start(<<0x00>>) |> Amethyst.Minecraft.Read.varlong() == {[0], ""} + assert Amethyst.Minecraft.Read.start(<<0x7F>>) |> Amethyst.Minecraft.Read.varlong() == {[127], ""} + assert Amethyst.Minecraft.Read.start(<<0x80, 0x01>>) |> Amethyst.Minecraft.Read.varlong() == {[128], ""} + assert Amethyst.Minecraft.Read.start(<<0xFF, 0x01>>) |> Amethyst.Minecraft.Read.varlong() == {[255], ""} + assert Amethyst.Minecraft.Read.start(<<0xFF, 0xFF, 0xFF, 0xFF, 0x07>>) |> Amethyst.Minecraft.Read.varlong() == {[2_147_483_647], ""} + assert Amethyst.Minecraft.Read.start(<<0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F>>) |> Amethyst.Minecraft.Read.varlong() == {[9_223_372_036_854_775_807], ""} + assert Amethyst.Minecraft.Read.start(<<0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x01>>) |> Amethyst.Minecraft.Read.varlong() == {[-1], ""} + end + + test "reading a string" do + assert Amethyst.Minecraft.Read.start(<<0x0D, "Hello, world!">>) |> Amethyst.Minecraft.Read.string() == {["Hello, world!"], ""} + assert Amethyst.Minecraft.Read.start(<<0x00>>) |> Amethyst.Minecraft.Read.string() == {[""], ""} + end +end