-
Notifications
You must be signed in to change notification settings - Fork 0
/
varint.ex
68 lines (60 loc) · 1.72 KB
/
varint.ex
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
defmodule ProtoDef.Gen.Elixir.Impl.Varint do
use Bitwise
def decode_varint_signed!(data) do
{:ok, ret} = decode_varint_signed(data)
ret
end
def decode_varint_signed(data) do
case decode_varint(data) do
{:ok, {num, rest}} ->
num = if (num &&& (1 <<< 31)) == 0 do
num
else
num - (1 <<< 32)
end
{:ok, {num, rest}}
err -> err
end
end
def decode_varint!(data) do
{:ok, ret} = decode_varint(data)
ret
end
def decode_varint(data) do
inner_decode_varint(data, 0, 0)
end
defp inner_decode_varint(<<1::1, curr::7, rest::binary>>, num, acc) when num < (64-7) do
inner_decode_varint(rest, num+7, (curr <<< num) + acc)
end
defp inner_decode_varint(<<0::1, curr::7, rest::binary>>, num, acc) do
{:ok, {(curr <<< num) + acc, rest}}
end
defp inner_decode_varint(_, num, _) when num >= (64-7), do: :too_big
defp inner_decode_varint("", _, _), do: :incomplete
defp inner_decode_varint(_, _, _), do: :error
def encode_varint_signed(data) do
if data < 0 do
encode_varint(data + (1 <<< 32))
else
encode_varint(data)
end
end
def encode_varint(num) when num <= 127, do: <<num>>
def encode_varint(num) when num >= 128 do
<<1::1, band(num, 127)::7, encode_varint(num >>> 7)::binary>>
end
end
defimpl ProtoDef.Gen.Elixir.Protocol, for: ProtoDef.Type.Varint do
@data_var ProtoDef.Type.data_var
@input_var ProtoDef.Type.input_var
def decoder(_descr, _ctx) do
quote do
ProtoDef.Gen.Elixir.Impl.Varint.decode_varint_signed!(unquote(@data_var))
end
end
def encoder(_descr, _ctx) do
quote do
ProtoDef.Gen.Elixir.Impl.Varint.encode_varint_signed(unquote(@input_var))
end
end
end