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
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
|
-module(dlink_data_rvi).
-compile(export_all).
-record(dlink_data_rvi, {need, buf}).
-define(MAX_LINE, 79).
init(_Opts) ->
undefined.
port_options() ->
[].
encode(Elems, St) ->
Bin = encode_(Elems, <<>>),
Sz = byte_size(Bin),
{ok, <<"&RVI|",
(integer_to_binary(Sz, 16))/binary, "\n",
Bin/binary>>, St}.
encode_([{Key, Val}|T], Acc) ->
{Type, ValBin} = encode_val(Val),
Bin = encode_elem(to_bin(Key), Type, ValBin),
encode_(T, <<Acc/binary, Bin/binary>>);
encode_([], Acc) ->
Acc.
encode_val(V) when is_binary(V) -> {$B, V};
encode_val(V) when is_integer(V) -> {$i, integer_to_binary(V,16)};
encode_val(V) when is_atom(V) -> {$a, atom_to_binary(V, latin1)};
encode_val(V) when is_float(V) ->
Bin = <<V/float>>,
{$f, Bin};
encode_val({T,_} = J) when T==array; T==struct ->
JSON = exo_json:encode(J),
{$J, iolist_to_binary(JSON)};
encode_val([T|_] = L) when is_tuple(T) ->
{$L, encode_(L, <<>>)}.
decode_value($B, Bin) -> Bin;
decode_value($i, Bin) -> binary_to_integer(Bin, 16);
decode_value($f, <<F/float>>) -> F;
decode_value($a, Bin) -> binary_to_existing_atom(Bin, latin1);
decode_value($J, Bin) ->
{ok, Obj} = exo_json:decode_string(binary_to_list(Bin)),
Obj;
decode_value($L, Bin) ->
decode_packet(Bin).
encode_elem(Key, Type, Bin) ->
BSz = byte_size(Bin),
case byte_size(Key) + BSz of
Sz when Sz =< 78, Type >= $a, Type =< $z ->
<<Key/binary, "|", Type, ":", Bin/binary, "\n">>;
_ ->
<<Key/binary, "|", Type, "|",
(integer_to_binary(BSz+1,16))/binary, "\n",
Bin/binary, "\n">>
end.
decode(<<"&RVI|", Rest/binary>>, undefined) ->
case erlang:decode_packet(line, Rest, [{line_length, 79}]) of
{more, _} ->
{more, Rest};
{ok, Ln, Rest1} ->
LSz = byte_size(Ln),
LSz1 = LSz-1,
<<Size:LSz1/binary, "\n">> = Ln,
Bytes = binary_to_integer(Size, 16),
case Rest1 of
<<Pkt:Bytes/binary, Tail/binary>> ->
{ok, decode_packet(Pkt), Tail};
_ ->
{more, #dlink_data_rvi{need = Bytes, buf = Rest1}}
end
end;
decode(Data, #dlink_data_rvi{need = Bytes, buf = Buf} = St) ->
case <<Buf/binary, Data/binary>> of
<<Pkt:Bytes/binary, Tail/binary>> ->
{ok, decode_packet(Pkt), Tail};
Buf1 ->
{more, St#dlink_data_rvi{buf = Buf1}}
end;
decode(_, _St) ->
{error, unknown, undefined}.
decode_packet(<<>>) ->
[];
decode_packet(P) ->
{ok, L, Rest} = erlang:decode_packet(line, P, [{line_length, ?MAX_LINE}]),
case split_line(L) of
{Key, Type, simple, Data} ->
[{Key, decode_value(Type, Data)}|decode_packet(Rest)];
{Key, Type, Size} ->
Size1 = Size-1,
<<VBin:Size1/binary, "\n", Rest1/binary>> = Rest,
[{Key, decode_value(Type, VBin)}|decode_packet(Rest1)]
end.
to_bin(V) when is_atom(V) -> atom_to_binary(V, latin1);
to_bin(V) when is_binary(V) -> V;
to_bin(V) when is_list(V) -> iolist_to_binary(V).
split_line(L) ->
split_line(L, <<>>).
split_line(<<"\\", $|, Rest/binary>>, Acc) ->
split_line(Rest, <<Acc/binary, $|>>);
split_line(<<"|", T, ":", Rest/binary>>, Acc) ->
{Acc, T, simple, remove_nl(Rest)};
split_line(<<"|", T, "|", Rest/binary>>, Acc) ->
SzBin = remove_nl(Rest),
{Acc, T, binary_to_integer(SzBin, 16)};
split_line(<<H, T/binary>>, Acc) ->
split_line(T, <<Acc/binary, H>>).
remove_nl(B) ->
Sz = byte_size(B),
Sz1 = Sz-1,
<<V:Sz1/binary, "\n">> = B,
V.
|