@@ -3,21 +3,14 @@ defmodule Msgpack.Decoder do
33 Handles the logic of decoding a MessagePack binary into an Elixir term.
44 """
55
6- @ default_max_depth 100
7- @ default_max_byte_size 10_000_000 # 10MB
8-
9- # The number of gregorian seconds from year 0 to the Unix epoch. This is a constant.
10- @ epoch_offset :calendar . datetime_to_gregorian_seconds ( { { 1970 , 1 , 1 } , { 0 , 0 , 0 } } )
6+ alias Msgpack.Decoder.Internal
117
128 @ spec decode ( binary ( ) , keyword ( ) ) :: { :ok , term ( ) } | { :error , term ( ) }
139 def decode ( binary , opts \\ [ ] ) do
14- merged_opts =
15- opts
16- |> Keyword . put_new ( :max_depth , @ default_max_depth )
17- |> Keyword . put_new ( :max_byte_size , @ default_max_byte_size )
10+ merged_opts = Keyword . merge ( default_opts ( ) , opts )
1811
1912 try do
20- case do_decode ( binary , merged_opts ) do
13+ case Internal . decode ( binary , merged_opts ) do
2114 { :ok , { term , << >> } } ->
2215 { :ok , term }
2316
@@ -33,241 +26,13 @@ defmodule Msgpack.Decoder do
3326 end
3427 end
3528
36- # ==== Nil ====
37- defp do_decode ( << 0xC0 , rest :: binary >> , _opts ) , do: { :ok , { nil , rest } }
38-
39- # ==== Boolean ====
40- defp do_decode ( << 0xC3 , rest :: binary >> , _opts ) , do: { :ok , { true , rest } }
41- defp do_decode ( << 0xC2 , rest :: binary >> , _opts ) , do: { :ok , { false , rest } }
42-
43- # ==== Integers ====
44- # ==== Positive Fixint ====
45- defp do_decode ( << int :: 8 , rest :: binary >> , _opts ) when int < 128 do
46- { :ok , { int , rest } }
47- end
48-
49- # ==== Negative Fixint ====
50- defp do_decode ( << int :: signed - 8 , rest :: binary >> , _opts ) when int >= - 32 and int < 0 do
51- { :ok , { int , rest } }
52- end
53-
54- # ==== Unsigned Integers ====
55- defp do_decode ( << 0xCC , int :: 8 , rest :: binary >> , _opts ) , do: { :ok , { int , rest } }
56- defp do_decode ( << 0xCD , int :: 16 , rest :: binary >> , _opts ) , do: { :ok , { int , rest } }
57- defp do_decode ( << 0xCE , int :: 32 , rest :: binary >> , _opts ) , do: { :ok , { int , rest } }
58- defp do_decode ( << 0xCF , int :: 64 , rest :: binary >> , _opts ) , do: { :ok , { int , rest } }
59-
60- # ==== Signed Integers ====
61- defp do_decode ( << 0xD0 , int :: signed - 8 , rest :: binary >> , _opts ) , do: { :ok , { int , rest } }
62- defp do_decode ( << 0xD1 , int :: signed - 16 , rest :: binary >> , _opts ) , do: { :ok , { int , rest } }
63- defp do_decode ( << 0xD2 , int :: signed - 32 , rest :: binary >> , _opts ) , do: { :ok , { int , rest } }
64- defp do_decode ( << 0xD3 , int :: signed - 64 , rest :: binary >> , _opts ) , do: { :ok , { int , rest } }
65-
66- # ==== Floats ====
67- defp do_decode ( << 0xCA , float :: float - 32 , rest :: binary >> , _opts ) , do: { :ok , { float , rest } }
68- defp do_decode ( << 0xCB , float :: float - 64 , rest :: binary >> , _opts ) , do: { :ok , { float , rest } }
69-
70- # ==== Strings ====
71- defp do_decode ( << prefix , rest :: binary >> , opts ) when prefix >= 0xA0 and prefix <= 0xBF do
72- size = prefix - 0xA0
73- decode_string ( rest , size , opts )
74- end
75-
76- defp do_decode ( << 0xD9 , size :: 8 , rest :: binary >> , opts ) , do: decode_string ( rest , size , opts )
77- defp do_decode ( << 0xDA , size :: 16 , rest :: binary >> , opts ) , do: decode_string ( rest , size , opts )
78- defp do_decode ( << 0xDB , size :: 32 , rest :: binary >> , opts ) , do: decode_string ( rest , size , opts )
79-
80- # ==== Raw Binary ====
81- defp do_decode ( << 0xC4 , size :: 8 , rest :: binary >> , opts ) , do: decode_binary ( rest , size , opts )
82- defp do_decode ( << 0xC5 , size :: 16 , rest :: binary >> , opts ) , do: decode_binary ( rest , size , opts )
83- defp do_decode ( << 0xC6 , size :: 32 , rest :: binary >> , opts ) , do: decode_binary ( rest , size , opts )
84-
85- # ==== Arrays ====
86- defp do_decode ( << prefix , rest :: binary >> , opts ) when prefix >= 0x90 and prefix <= 0x9F do
87- size = prefix - 0x90
88- decode_array ( rest , size , opts )
89- end
90-
91- defp do_decode ( << 0xDC , size :: 16 , rest :: binary >> , opts ) , do: decode_array ( rest , size , opts )
92- defp do_decode ( << 0xDD , size :: 32 , rest :: binary >> , opts ) , do: decode_array ( rest , size , opts )
93-
94- # ==== Maps ====
95- defp do_decode ( << prefix , rest :: binary >> , opts ) when prefix >= 0x80 and prefix <= 0x8F do
96- size = prefix - 0x80
97- decode_map ( rest , size , opts )
98- end
99-
100- defp do_decode ( << 0xDE , size :: 16 , rest :: binary >> , opts ) , do: decode_map ( rest , size , opts )
101- defp do_decode ( << 0xDF , size :: 32 , rest :: binary >> , opts ) , do: decode_map ( rest , size , opts )
102-
103- # ==== Extensions & Timestamps ====
104- # ==== Fixext ====
105- defp do_decode ( << 0xD4 , type :: signed - 8 , data :: binary - size ( 1 ) , rest :: binary >> , opts ) ,
106- do: decode_ext ( type , data , rest , opts )
107-
108- defp do_decode ( << 0xD5 , type :: signed - 8 , data :: binary - size ( 2 ) , rest :: binary >> , opts ) ,
109- do: decode_ext ( type , data , rest , opts )
110-
111- defp do_decode ( << 0xD6 , type :: signed - 8 , data :: binary - size ( 4 ) , rest :: binary >> , opts ) ,
112- do: decode_ext ( type , data , rest , opts )
113-
114- defp do_decode ( << 0xD7 , type :: signed - 8 , data :: binary - size ( 8 ) , rest :: binary >> , opts ) ,
115- do: decode_ext ( type , data , rest , opts )
116-
117- defp do_decode ( << 0xD8 , type :: signed - 8 , data :: binary - size ( 16 ) , rest :: binary >> , opts ) ,
118- do: decode_ext ( type , data , rest , opts )
119-
120- # ==== Ext ====
121- defp do_decode ( << 0xC7 , len :: 8 , type :: signed - 8 , data :: binary - size ( len ) , rest :: binary >> , opts ) ,
122- do: decode_ext ( type , data , rest , opts )
123-
124- defp do_decode ( << 0xC8 , len :: 16 , type :: signed - 8 , data :: binary - size ( len ) , rest :: binary >> , opts ) ,
125- do: decode_ext ( type , data , rest , opts )
126-
127- defp do_decode ( << 0xC9 , len :: 32 , type :: signed - 8 , data :: binary - size ( len ) , rest :: binary >> , opts ) ,
128- do: decode_ext ( type , data , rest , opts )
129-
130- # ==== Unknown types ====
131- defp do_decode ( << prefix , _rest :: binary >> , _opts ) do
132- { :error , { :unknown_prefix , prefix } }
133- end
134-
135- defp do_decode ( << >> , _opts ) do
136- { :error , :unexpected_eof }
137- end
138-
139- # ==== Helpers ====
140- defp decode_string ( binary , size , opts ) do
141- if max_size = opts [ :max_byte_size ] , do: check_byte_size ( size , max_size )
142-
143- case binary do
144- << string :: binary - size ( size ) , rest :: binary >> ->
145- { :ok , { string , rest } }
146-
147- _ ->
148- { :error , :unexpected_eof }
149- end
150- end
151-
152- defp decode_binary ( binary , size , opts ) do
153- if max_size = opts [ :max_byte_size ] , do: check_byte_size ( size , max_size )
154-
155- case binary do
156- << bin :: binary - size ( size ) , rest :: binary >> ->
157- { :ok , { bin , rest } }
158-
159- _ ->
160- { :error , :unexpected_eof }
161- end
162- end
163-
164- defp decode_array ( binary , size , opts ) do
165- depth = opts [ :depth ] || 0
166-
167- check_depth ( depth , opts [ :max_depth ] )
168- check_byte_size ( size , opts [ :max_byte_size ] )
169-
170- new_opts = Keyword . put ( opts , :depth , depth + 1 )
171-
172- decode_many ( binary , size , [ ] , new_opts )
173- end
174-
175- defp decode_map ( binary , size , opts ) do
176- depth = opts [ :depth ] || 0
177-
178- check_depth ( depth , opts [ :max_depth ] )
179- check_byte_size ( size * 2 , opts [ :max_byte_size ] )
180-
181- new_opts = Keyword . put ( opts , :depth , depth + 1 )
182-
183- with { :ok , { kv_pairs , rest } } <- decode_many ( binary , size * 2 , [ ] , new_opts ) do
184- map =
185- Enum . chunk_every ( kv_pairs , 2 )
186- |> Enum . map ( & List . to_tuple / 1 )
187- |> Enum . into ( % { } )
188-
189- { :ok , { map , rest } }
190- end
191- end
192-
193- # Recursively decodes `count` terms from the binary
194- defp decode_many ( binary , 0 , acc , _opts ) do
195- { :ok , { Enum . reverse ( acc ) , binary } }
196- end
197-
198- defp decode_many ( binary , count , acc , opts ) do
199- case do_decode ( binary , opts ) do
200- { :ok , { term , rest } } ->
201- decode_many ( rest , count - 1 , [ term | acc ] , opts )
202-
203- { :error , reason } ->
204- { :error , reason }
205- end
206- end
207-
208- defp decode_ext ( - 1 , data , rest , _opts ) do
209- { :ok , { decode_timestamp ( data ) , rest } }
210- end
211-
212- defp decode_ext ( type , data , rest , _opts ) do
213- { :ok , { % Msgpack.Ext { type: type , data: data } , rest } }
214- end
215-
216- # timestamp 32: 4 bytes (32-bit unsigned integer seconds)
217- defp decode_timestamp ( << unix_seconds :: unsigned - 32 >> ) do
218- gregorian_seconds = unix_seconds + @ epoch_offset
219- erlang_datetime = :calendar . gregorian_seconds_to_datetime ( gregorian_seconds )
220- NaiveDateTime . from_erl! ( erlang_datetime )
221- end
222-
223- # timestamp 64: 8 bytes (30-bit nanoseconds + 34-bit seconds)
224- defp decode_timestamp ( << data :: unsigned - 64 >> ) do
225- nanoseconds = :erlang . bsr ( data , 34 )
226-
227- if nanoseconds > 999_999_999 do
228- throw ( { :error , :invalid_timestamp } )
229- else
230- unix_seconds = :erlang . band ( data , 0x00000003_FFFFFFFF )
231- gregorian_seconds = unix_seconds + @ epoch_offset
232- erlang_datetime = :calendar . gregorian_seconds_to_datetime ( gregorian_seconds )
233- base_datetime = NaiveDateTime . from_erl! ( erlang_datetime )
234-
235- if nanoseconds > 0 do
236- microseconds = div ( nanoseconds , 1000 )
237- % { base_datetime | microsecond: { microseconds , 6 } }
238- else
239- base_datetime
240- end
241- end
242- end
243-
244- # timestamp 96: 12 bytes (32-bit nanoseconds + 64-bit seconds)
245- defp decode_timestamp ( << nanoseconds :: unsigned - 32 , unix_seconds :: signed - 64 >> ) do
246- if nanoseconds > 999_999_999 do
247- throw ( { :error , :invalid_timestamp } )
248- else
249- gregorian_seconds = unix_seconds + @ epoch_offset
250- erlang_datetime = :calendar . gregorian_seconds_to_datetime ( gregorian_seconds )
251- base_datetime = NaiveDateTime . from_erl! ( erlang_datetime )
252-
253- if nanoseconds > 0 do
254- microseconds = div ( nanoseconds , 1000 )
255- % { base_datetime | microsecond: { microseconds , 6 } }
256- else
257- base_datetime
258- end
259- end
260- end
261-
262- defp check_byte_size ( size , max_size ) when size > max_size do
263- throw ( { :error , { :max_byte_size_exceeded , max_size } } )
264- end
265-
266- defp check_byte_size ( _size , _max_size ) , do: :ok
267-
268- defp check_depth ( depth , max_depth ) when depth >= max_depth do
269- throw ( { :error , { :max_depth_reached , max_depth } } )
29+ @ doc """
30+ Returns a keyword list of the default options for the decoder.
31+ """
32+ def default_opts ( ) do
33+ [
34+ max_depth: 100 ,
35+ max_byte_size: 10_000_000 # 10MB
36+ ]
27037 end
271-
272- defp check_depth ( _depth , _max_depth ) , do: :ok
27338end
0 commit comments