_site/cover/msmp_jsonb.COVER.html

1 %% Copyright (c) 2023 Peter Morgan <peter.james.morgan@gmail.com>
2 %%
3 %% Licensed under the Apache License, Version 2.0 (the "License");
4 %% you may not use this file except in compliance with the License.
5 %% You may obtain a copy of the License at
6 %%
7 %% http://www.apache.org/licenses/LICENSE-2.0
8 %%
9 %% Unless required by applicable law or agreed to in writing, software
10 %% distributed under the License is distributed on an "AS IS" BASIS,
11 %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 %% See the License for the specific language governing permissions and
13 %% limitations under the License.
14
15
16 -module(msmp_jsonb).
17
18
19 -feature(maybe_expr, enable).
20
21
22 -export([decode/1]).
23 -export([type/1]).
24 -import(scran_branch, [alt/1]).
25 -import(scran_bytes, [tag/1]).
26 -import(scran_bytes, [take/1]).
27 -import(scran_combinator, [map_parser/2]).
28 -import(scran_combinator, [map_result/2]).
29 -import(scran_combinator, [condition/2]).
30 -import(scran_multi, [count/2]).
31 -import(scran_result, [into_map/1]).
32 -import(scran_result, [into_tuple/1]).
33 -import(scran_result, [kv/2]).
34 -import(scran_sequence, [followed_with/2]).
35 -import(scran_sequence, [preceded/2]).
36 -import(scran_sequence, [sequence/1]).
37 -import(scran_sequence, [zip/2]).
38 -include_lib("kernel/include/logger.hrl").
39 -on_load(on_load/0).
40
41
42 decode(Metadata) ->
43 27 fun
44 (Input) ->
45 27 ?LOG_DEBUG(
46 #{metadata => Metadata,
47 27 input =>Input}),
48
49 27 (map_parser(
50 take(msmp_integer_fixed:decode(Metadata)),
51 alt([object(),
52
53 preceded(
54 type(string),
55 msmp_string_length_encoded:decode()),
56
57 numeric(),
58
59 literal(single),
60
61 array()])))(Input)
62 end.
63
64
65 numeric() ->
66 27 fun
67 (Input) ->
68 21 (alt([preceded(type(int16), scran_number:i(little, 16)),
69 preceded(type(uint16), scran_number:u(little, 16)),
70
71 preceded(type(int32), scran_number:i(little, 32)),
72 preceded(type(uint32), scran_number:u(little, 32)),
73
74 preceded(type(int64), scran_number:i(little, 64)),
75 preceded(type(uint64), scran_number:u(little, 64)),
76
77 preceded(type(double), scran_number:f(little, 64))]))(Input)
78 end.
79
80
81 array() ->
82 27 fun
83 (Input) ->
84 6 ?LOG_DEBUG(#{input => Input}),
85 6 (alt([array(small),
86 array(large)]))(Input)
87 end.
88
89
90 array(Size) ->
91 12 fun
92 (<<_:8, JSONB/bytes>> = Input) ->
93 6 ?LOG_DEBUG(#{size => Size, input => Input}),
94
95 6 (followed_with(
96 header(array, Size),
97 fun
98 (#{element_count := ElementCount}) ->
99 6 value_entries(ElementCount, Size, JSONB)
100 end))(Input)
101 end.
102
103
104 header(Type, Size) ->
105 56 fun
106 (Input) ->
107 56 ?LOG_DEBUG(#{size => Size, input => Input}),
108
109 56 (into_map(
110 sequence(
111 [kv(type, type(Type, Size)),
112 kv(element_count, element_count(Size)),
113 kv(size, sz(Size))])))(Input)
114 end.
115
116
117 value_entries(N, Size, JSONB) ->
118 13 fun
119 (Input) ->
120 13 ?LOG_DEBUG(#{n => N,
121 jsonb => JSONB,
122 13 input => Input}),
123
124 13 (count(N, value_entry(Size, JSONB)))(Input)
125 end.
126
127
128 value_entry(Size, JSONB) ->
129 13 fun
130 (Input) ->
131 25 (alt(
132 [preceded(
133 type(int16),
134 %% in a large array/object an int16 uses 4 bytes
135 msmp_integer_fixed:decode(bytes(Size))),
136
137 condition(
138 Size == large,
139 preceded(
140 type(int32),
141 msmp_integer_fixed:decode(4))),
142
143 literal(Size),
144
145 offset_value({array, small},
146 small,
147 offset_array(small),
148 JSONB),
149
150 offset_value(string,
151 Size,
152 msmp_string_length_encoded:decode(),
153 JSONB)]))(Input)
154 end.
155
156
157 offset_array(Size) ->
158 25 fun
159 (Input) ->
160 2 (followed_with(
161 into_map(
162 sequence(
163 [kv(element_count, element_count(Size)),
164 kv(size, sz(Size))])),
165 fun
166 (#{element_count := ElementCount}) ->
167 2 value_entries(ElementCount, Size, Input)
168 end))(Input)
169 end.
170
171
172 offset_value(Type, Size, ValueParser, JSONB) ->
173 50 fun
174 (Input) ->
175 30 maybe
176 30 {Remaining, #{offset := Offset}} ?= (into_map(
177 sequence(
178 [kv(type, type(Type)),
179 offset(Size)])))(Input),
180
181 16 ?LOG_DEBUG(#{type => Type,
182 size => Size,
183 value_parser => scran_debug:pp(ValueParser),
184 offset => Offset,
185 input => Input,
186 16 jsonb => JSONB}),
187
188 16 {_, Value} ?= (at_offset(ValueParser, Offset))(JSONB),
189
190 16 ?LOG_DEBUG(#{remaining => Remaining, value => Value}),
191
192 16 {Remaining, Value}
193 end
194 end.
195
196
197 at_offset(Parser, Offset) ->
198 16 fun
199 (Input) ->
200 16 ?LOG_DEBUG(#{parser => scran_debug:pp(Parser),
201 input => Input,
202 16 offset => Offset}),
203
204 16 (preceded(
205 take(Offset),
206 Parser))(Input)
207 end.
208
209
210 offset(Size) ->
211 30 fun
212 (Input) ->
213 16 (kv(offset, msmp_integer_fixed:decode(bytes(Size))))(Input)
214 end.
215
216
217 object() ->
218 27 fun
219 (Input) ->
220 27 ?LOG_DEBUG(#{input => Input}),
221
222 27 (alt([object(small),
223 object(large)]))(Input)
224 end.
225
226
227 object(Size) ->
228 54 fun
229 (<<_:8, JSONB/bytes>> = Input) ->
230 50 ?LOG_DEBUG(#{size => Size, input => Input}),
231
232 50 (followed_with(
233 header(object, Size),
234 fun
235 (#{element_count := ElementCount}) ->
236 5 object_kv(ElementCount, Size, JSONB)
237 end))(Input)
238 end.
239
240
241 sz(Size) ->
242 58 fun
243 (Input) ->
244 13 ?LOG_DEBUG(#{size => Size, input => Input}),
245 13 (msmp_integer_fixed:decode(bytes(Size)))(Input)
246 end.
247
248
249 element_count(Size) ->
250 58 fun
251 (Input) ->
252 13 ?LOG_DEBUG(#{size => Size, input => Input}),
253 13 (msmp_integer_fixed:decode(bytes(Size)))(Input)
254 end.
255
256
257 object_kv(N, Size, JSONB) ->
258 5 fun
259 (Input) ->
260 5 ?LOG_DEBUG(#{n => N,
261 jsonb => JSONB,
262 5 input => Input}),
263
264 5 (into_map(
265 zip(object_keys(N, Size, JSONB),
266 value_entries(N, Size, JSONB))))(Input)
267 end.
268
269
270 object_keys(N, Size, JSONB) ->
271 5 fun
272 (Input) ->
273 5 ?LOG_DEBUG(#{n => N,
274 size => Size,
275 jsonb => JSONB,
276 5 input => Input}),
277
278 5 (count(N, at_offset_with_length(key_entry(Size), JSONB)))(Input)
279 end.
280
281
282 key_entry(Size) ->
283 5 fun
284 (Input) ->
285 8 (into_tuple(
286 sequence(
287 [msmp_integer_fixed:decode(bytes(Size)),
288 %% key length is always 2 bytes independent of size
289 msmp_integer_fixed:decode(2)])))(Input)
290 end.
291
292
293 at_offset_with_length(Parser, JSONB) ->
294 5 fun
295 (Input) ->
296 8 (map_result(
297 Parser,
298 fun
299 (PosLen) ->
300 8 try
301 8 binary:part(JSONB, PosLen)
302
303 catch
304 error:badarg ->
305
:-(
nomatch
306 end
307 end))(Input)
308 end.
309
310
311 27 bytes(single) -> 1;
312 128 bytes(small) -> 2;
313 4 bytes(large) -> 4.
314
315
316 -spec literal(atom()) -> scran:parser().
317
318 literal(Size) ->
319 52 fun
320 (Input) ->
321 28 ?LOG_DEBUG(#{input => Input}),
322
323 28 (preceded(
324 type(literal),
325 alt([literal(null, Size),
326 literal(true, Size),
327 literal(false, Size)])))(Input)
328 end.
329
330
331 type(Type, Size) ->
332 56 ?FUNCTION_NAME({Type, Size}).
333
334
335 -spec type(atom() | tuple()) -> scran:parser().
336
337 type(Type) ->
338 341 Tag = <<(cache(?FUNCTION_NAME, Type)):8>>,
339 341 fun
340 (Input) ->
341 266 (scran_bytes:tag(Tag))(Input)
342 end.
343
344
345 -spec literal(atom(), atom()) -> scran:parser().
346
347 literal(Type, Size) ->
348 84 Tag = <<(cache(?FUNCTION_NAME, Type)):(bytes(Size))/little-unit:8>>,
349 84 fun
350 (Input) ->
351 12 ?LOG_DEBUG(#{type => Type, input => Input}),
352 12 (scran_combinator:value(Type, tag(Tag)))(Input)
353 end.
354
355
356 -spec cache(atom(), atom() | tuple()) -> integer() | no_return().
357
358 cache(Category, Type) ->
359 425 maps:get(Type, cache(Category)).
360
361
362 -spec cache(atom()) -> #{atom() => integer()} | no_return().
363
364 cache(Category) ->
365 425 maps:get(Category, persistent_term:get(?MODULE)).
366
367
368 on_load() ->
369 1 persistent_term:put(
370 ?MODULE,
371 #{type => msmp_enum:priv_consult("jsonb-type.terms"),
372 literal => msmp_enum:priv_consult("jsonb-literal.terms")}).
Line Hits Source