• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

devinus / poison / 9a997cab15882bad4f8c7a362bd518e9acc3066c-PR-221

01 Jun 2024 12:02PM UTC coverage: 91.398%. First build
9a997cab15882bad4f8c7a362bd518e9acc3066c-PR-221

Pull #221

github

devinus
WIP
Pull Request #221: WIP

34 of 36 new or added lines in 3 files covered. (94.44%)

255 of 279 relevant lines covered (91.4%)

650693.68 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

94.16
/lib/poison/parser.ex
1
defmodule Poison.MissingDependencyError do
2
  @type t :: %__MODULE__{name: String.t()}
3

4
  defexception name: nil
5

6
  def message(%{name: name}) do
7
    "missing optional dependency: #{name}"
×
8
  end
9
end
10

11
defmodule Poison.ParseError do
12
  alias Code.Identifier
13
  alias Poison.Parser
14

15
  @type t :: %__MODULE__{data: String.t(), skip: non_neg_integer, value: Parser.t()}
16

17
  defexception data: "", skip: 0, value: nil
18

19
  def message(%{data: data, skip: skip, value: value}) when value != nil do
20
    <<head::binary-size(skip), _rest::bits>> = data
546✔
21
    pos = String.length(head)
546✔
22
    "cannot parse value at position #{pos}: #{inspect(value)}"
546✔
23
  end
24

25
  def message(%{data: data, skip: skip}) when is_bitstring(data) do
26
    <<head::binary-size(skip), rest::bits>> = data
5,313✔
27
    pos = String.length(head)
5,313✔
28

29
    case rest do
5,313✔
30
      <<>> ->
31
        "unexpected end of input at position #{pos}"
1,428✔
32

33
      <<token::utf8, _rest::bits>> ->
34
        "unexpected token at position #{pos}: #{escape(token)}"
3,675✔
35

36
      _rest ->
37
        "cannot parse value at position #{pos}: #{inspect(<<rest::bits>>)}"
210✔
38
    end
39
  end
40

41
  def message(%{data: data}) do
42
    "unsupported value: #{inspect(data)}"
×
43
  end
44

45
  defp escape(token) do
46
    {value, _} = Identifier.escape(<<token::utf8>>, ?\\)
3,675✔
47
    value
3,675✔
48
  end
49
end
50

51
defmodule Poison.Parser do
52
  @moduledoc """
53
  An RFC 7159 and ECMA 404 conforming JSON parser.
54

55
  See: https://tools.ietf.org/html/rfc7159
56
  See: http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf
57
  """
58

59
  @compile :inline
60
  @compile :inline_list_funcs
61

62
  import Bitwise
63

64
  alias Poison.{Decoder, ParseError}
65

66
  @typep value ::
67
           nil | true | false | float | integer | String.t() | [value] | %{String.t() => value}
68

69
  if Code.ensure_loaded?(Decimal) do
70
    @type t :: value | Decimal.t()
71
  else
72
    @type t :: value
73
  end
74

75
  whitespace = ~c"\s\t\n\r"
76
  digits = ?0..?9
77

78
  defmacrop syntax_error(skip) do
79
    quote do
80
      raise ParseError, skip: unquote(skip)
81
    end
82
  end
83

84
  @spec parse!(iodata | bitstring, Decoder.options()) :: t | no_return
85
  def parse!(value, options \\ %{})
15,834✔
86

87
  def parse!(data, options) when is_bitstring(data) do
22,470✔
88
    [value | skip] =
22,470✔
89
      value(data, data, :maps.get(:keys, options, nil), :maps.get(:decimal, options, nil), 0)
90

91
    <<_skip::binary-size(skip), rest::bits>> = data
18,228✔
92
    skip_whitespace(rest, skip, value)
18,228✔
93
  rescue
94
    exception in ParseError ->
4,620✔
95
      reraise ParseError,
4,620✔
96
              [data: data, skip: exception.skip, value: exception.value],
4,620✔
97
              __STACKTRACE__
98
  end
99

100
  def parse!(iodata, options) do
101
    iodata |> IO.iodata_to_binary() |> parse!(options)
×
102
  end
103

104
  @compile {:inline, value: 5}
105

106
  defp value(<<"null", _rest::bits>>, _data, _keys, _decimal, skip) do
81,969✔
107
    [nil | skip + 4]
7,679✔
108
  end
109

110
  defp value(<<"true", _rest::bits>>, _data, _keys, _decimal, skip) do
41,115✔
111
    [true | skip + 4]
3,758✔
112
  end
113

114
  defp value(<<"false", _rest::bits>>, _data, _keys, _decimal, skip) do
40,874✔
115
    [false | skip + 5]
3,962✔
116
  end
117

118
  defp value(<<?-, rest::bits>>, _data, _keys, decimal, skip) do
119
    number_neg(rest, decimal, skip + 1)
92,577✔
120
  end
121

122
  defp value(<<?0, rest::bits>>, _data, _keys, decimal, skip) do
123
    number_frac(rest, decimal, skip + 1, 1, 0, 0)
24,513✔
124
  end
125

126
  for digit <- ?1..?9 do
127
    coef = digit - ?0
128

129
    defp value(<<unquote(digit), rest::bits>>, _data, _keys, decimal, skip) do
130
      number_int(rest, decimal, skip + 1, 1, unquote(coef), 0)
15,944✔
131
    end
132
  end
133

134
  defp value(<<?", rest::bits>>, data, _keys, _decimal, skip) do
135
    string_continue(rest, data, skip + 1)
98,289✔
136
  end
137

138
  defp value(<<?[, rest::bits>>, data, keys, decimal, skip) do
139
    array_values(rest, data, keys, decimal, skip + 1, [])
3,451,087✔
140
  end
141

142
  defp value(<<?{, rest::bits>>, data, keys, decimal, skip) do
143
    object_pairs(rest, data, keys, decimal, skip + 1, [])
1,058,637✔
144
  end
145

146
  for char <- whitespace do
147
    defp value(<<unquote(char), rest::bits>>, data, keys, decimal, skip) do
148
      value(rest, data, keys, decimal, skip + 1)
1,239✔
149
    end
150
  end
151

152
  defp value(_rest, _data, _keys, _decimal, skip) do
153
    syntax_error(skip)
1,533✔
154
  end
155

156
  ## Objects
157

158
  defmacrop object_name(keys, skip, name) do
159
    quote bind_quoted: [keys: keys, skip: skip, name: name] do
160
      case keys do
161
        :atoms! ->
162
          try do
163
            String.to_existing_atom(name)
164
          rescue
165
            ArgumentError ->
166
              reraise ParseError, [skip: skip, value: name], __STACKTRACE__
167
          end
168

169
        :atoms ->
170
          # credo:disable-for-next-line Credo.Check.Warning.UnsafeToAtom
171
          String.to_atom(name)
172

173
        _keys ->
174
          name
175
      end
176
    end
177
  end
178

179
  @compile {:inline, object_pairs: 6}
180

181
  defp object_pairs(<<?", rest::bits>>, data, keys, decimal, skip, acc) do
182
    start = skip + 1
1,282,233✔
183
    [name | skip] = string_continue(rest, data, start)
1,282,233✔
184
    <<_skip::binary-size(skip), rest::bits>> = data
1,282,212✔
185

186
    [value | skip] = object_value(rest, data, keys, decimal, skip)
1,282,212✔
187
    <<_skip::binary-size(skip), rest::bits>> = data
231,981✔
188

189
    object_pairs_continue(rest, data, keys, decimal, skip, [
231,981✔
190
      {object_name(keys, start, name), value} | acc
231,981✔
191
    ])
192
  end
193

194
  defp object_pairs(<<?}, _rest::bits>>, _data, _keys, _decimal, skip, []) do
255✔
195
    [%{} | skip + 1]
31✔
196
  end
197

198
  for char <- whitespace do
199
    defp object_pairs(<<unquote(char), rest::bits>>, data, keys, decimal, skip, acc) do
200
      object_pairs(rest, data, keys, decimal, skip + 1, acc)
315✔
201
    end
202
  end
203

204
  defp object_pairs(_rest, _data, _keys, _decimal, skip, _acc) do
205
    syntax_error(skip)
483✔
206
  end
207

208
  @compile {:inline, object_pairs_continue: 6}
209

210
  defp object_pairs_continue(<<?,, rest::bits>>, data, keys, decimal, skip, acc) do
211
    object_pairs(rest, data, keys, decimal, skip + 1, acc)
224,365✔
212
  end
213

214
  defp object_pairs_continue(<<?}, _rest::bits>>, _data, _keys, _decimal, skip, acc) do
6,903✔
215
    [:maps.from_list(acc) | skip + 1]
650✔
216
  end
217

218
  for char <- whitespace do
219
    defp object_pairs_continue(<<unquote(char), rest::bits>>, data, keys, decimal, skip, acc) do
220
      object_pairs_continue(rest, data, keys, decimal, skip + 1, acc)
147✔
221
    end
222
  end
223

224
  defp object_pairs_continue(_rest, _data, _keys, _decimal, skip, _acc) do
225
    syntax_error(skip)
42✔
226
  end
227

228
  @compile {:inline, object_value: 5}
229

230
  defp object_value(<<?:, rest::bits>>, data, keys, decimal, skip) do
231
    value(rest, data, keys, decimal, skip + 1)
1,282,086✔
232
  end
233

234
  for char <- whitespace do
235
    defp object_value(<<unquote(char), rest::bits>>, data, keys, decimal, skip) do
236
      object_value(rest, data, keys, decimal, skip + 1)
168✔
237
    end
238
  end
239

240
  defp object_value(_rest, _data, _keys, _decimal, skip) do
241
    syntax_error(skip)
126✔
242
  end
243

244
  ## Arrays
245

246
  @compile {:inline, array_values: 6}
247

248
  defp array_values(<<?], _rest::bits>>, _data, _keys, _decimal, skip, acc) do
291✔
249
    [acc | skip + 1]
35✔
250
  end
251

252
  for char <- whitespace do
253
    defp array_values(<<unquote(char), rest::bits>>, data, keys, decimal, skip, acc) do
254
      array_values(rest, data, keys, decimal, skip + 1, acc)
252✔
255
    end
256
  end
257

258
  defp array_values(rest, data, keys, decimal, skip, acc) do
259
    [value | skip] = value(rest, data, keys, decimal, skip)
3,450,761✔
260
    <<_skip::binary-size(skip), rest::bits>> = data
298,871✔
261
    array_values_continue(rest, data, keys, decimal, skip, [value | acc])
298,871✔
262
  end
263

264
  @compile {:inline, array_values_continue: 6}
265

266
  defp array_values_continue(<<?,, rest::bits>>, data, keys, decimal, skip, acc) do
267
    [value | skip] = value(rest, data, keys, decimal, skip + 1)
221,579✔
268
    <<_skip::binary-size(skip), rest::bits>> = data
221,327✔
269
    array_values_continue(rest, data, keys, decimal, skip, [value | acc])
221,327✔
270
  end
271

272
  defp array_values_continue(<<?], _rest::bits>>, _data, _keys, _decimal, skip, acc) do
269,684✔
273
    [:lists.reverse(acc) | skip + 1]
28,389✔
274
  end
275

276
  for char <- whitespace do
277
    defp array_values_continue(<<unquote(char), rest::bits>>, data, keys, decimal, skip, acc) do
278
      array_values_continue(rest, data, keys, decimal, skip + 1, acc)
231✔
279
    end
280
  end
281

282
  defp array_values_continue(_rest, _data, _keys, _decimal, skip, _acc) do
283
    syntax_error(skip)
546✔
284
  end
285

286
  ## Numbers
287

288
  @compile {:inline, number_neg: 3}
289

290
  defp number_neg(<<?0, rest::bits>>, decimal, skip) do
291
    number_frac(rest, decimal, skip + 1, -1, 0, 0)
23,537✔
292
  end
293

294
  for char <- ?1..?9 do
295
    defp number_neg(<<unquote(char), rest::bits>>, decimal, skip) do
296
      number_int(rest, decimal, skip + 1, -1, unquote(char - ?0), 0)
14,558✔
297
    end
298
  end
299

300
  defp number_neg(_rest, _decimal, skip) do
301
    syntax_error(skip)
168✔
302
  end
303

304
  @compile {:inline, number_int: 6}
305

306
  for char <- digits do
307
    defp number_int(<<unquote(char), rest::bits>>, decimal, skip, sign, coef, exp) do
308
      number_int(rest, decimal, skip + 1, sign, coef * 10 + unquote(char - ?0), exp)
18,110✔
309
    end
310
  end
311

312
  defp number_int(rest, decimal, skip, sign, coef, exp) do
313
    number_frac(rest, decimal, skip, sign, coef, exp)
139,775✔
314
  end
315

316
  @compile {:inline, number_frac: 6}
317

318
  defp number_frac(<<?., rest::bits>>, decimal, skip, sign, coef, exp) do
319
    number_frac_continue(rest, decimal, skip + 1, sign, coef, exp)
94,292✔
320
  end
321

322
  defp number_frac(rest, decimal, skip, sign, coef, exp) do
323
    number_exp(rest, decimal, skip, sign, coef, exp)
93,533✔
324
  end
325

326
  @compile {:inline, number_frac_continue: 6}
327

328
  for char <- digits do
329
    defp number_frac_continue(<<unquote(char), rest::bits>>, decimal, skip, sign, coef, exp) do
330
      number_frac_continue(rest, decimal, skip + 1, sign, coef * 10 + unquote(char - ?0), exp - 1)
143,118✔
331
    end
332
  end
333

334
  defp number_frac_continue(_rest, _decimal, skip, _sign, _coef, 0) do
335
    syntax_error(skip)
168✔
336
  end
337

338
  defp number_frac_continue(rest, decimal, skip, sign, coef, exp) do
339
    number_exp(rest, decimal, skip, sign, coef, exp)
94,124✔
340
  end
341

342
  @compile {:inline, number_exp: 6}
343

344
  for e <- ~c"eE" do
345
    defp number_exp(<<unquote(e), rest::bits>>, decimal, skip, sign, coef, exp) do
346
      [value | skip] = number_exp_continue(rest, skip + 1)
39,134✔
347
      number_complete(decimal, skip, sign, coef, exp + value)
38,861✔
348
    end
349
  end
350

351
  defp number_exp(_rest, decimal, skip, sign, coef, exp) do
352
    number_complete(decimal, skip, sign, coef, exp)
148,376✔
353
  end
354

355
  @compile {:inline, number_exp_continue: 2}
356

357
  defp number_exp_continue(<<?-, rest::bits>>, skip) do
358
    [exp | skip] = number_exp_digits(rest, skip + 1)
122✔
359
    [-exp | skip]
12✔
360
  end
361

362
  defp number_exp_continue(<<?+, rest::bits>>, skip) do
363
    number_exp_digits(rest, skip + 1)
273✔
364
  end
365

366
  defp number_exp_continue(rest, skip) do
367
    number_exp_digits(rest, skip)
38,886✔
368
  end
369

370
  @compile {:inline, number_exp_digits: 2}
371

372
  defp number_exp_digits(<<rest::bits>>, skip) do
373
    case number_digits(rest, skip, 0) do
39,281✔
374
      [_exp | ^skip] ->
375
        syntax_error(skip)
315✔
376

377
      other ->
378
        other
38,966✔
379
    end
380
  end
381

382
  defp number_exp_digits(<<>>, skip), do: syntax_error(skip)
×
383

384
  @compile {:inline, number_digits: 3}
385

386
  for char <- digits do
387
    defp number_digits(<<unquote(char), rest::bits>>, skip, acc) do
388
      number_digits(rest, skip + 1, acc * 10 + unquote(char - ?0))
35,084✔
389
    end
390
  end
391

392
  defp number_digits(_rest, skip, acc) do
36,131✔
393
    [acc | skip]
3,150✔
394
  end
395

396
  @compile {:inline, number_complete: 5}
397

398
  if Code.ensure_loaded?(Decimal) do
399
    defp number_complete(true, skip, sign, coef, exp) do
2,071✔
400
      [%Decimal{sign: sign, coef: coef, exp: exp} | skip]
218✔
401
    end
402
  else
403
    defp number_complete(true, _skip, _sign, _coef, _exp) do
404
      raise Poison.MissingDependencyError, name: "Decimal"
405
    end
406
  end
407

408
  defp number_complete(_decimal, skip, sign, coef, 0) do
85,570✔
409
    [coef * sign | skip]
8,073✔
410
  end
411

412
  max_sig = 1 <<< 53
413

414
  # See: https://arxiv.org/pdf/2101.11408.pdf
415
  defp number_complete(_decimal, skip, sign, coef, exp)
416
       when exp in -10..10 and coef < unquote(max_sig) do
417
    if exp < 0 do
14,998✔
418
      [coef / pow10(-exp) * sign | skip]
385✔
419
    else
420
      [coef * pow10(exp) * sign | skip]
1,081✔
421
    end
422
  end
423

424
  defp number_complete(_decimal, skip, sign, coef, exp) do
76,412✔
425
    [
426
      String.to_float(
427
        <<Integer.to_string(coef * sign)::bits, ".0e"::bits, Integer.to_string(exp)::bits>>
428
      )
429
      | skip
6,382✔
430
    ]
431
  rescue
432
    ArithmeticError ->
433
      reraise ParseError, [skip: skip, value: "#{coef * sign}e#{exp}"], __STACKTRACE__
×
434
  end
435

436
  @compile {:inline, pow10: 1}
437

438
  for n <- 1..10 do
439
    defp pow10(unquote(n)), do: unquote(:math.pow(10, n))
2,707✔
440
  end
441

442
  defp pow10(n), do: 1.0e10 * pow10(n - 10)
×
443

444
  ## Strings
445

446
  defmacrop string_codepoint_size(codepoint) do
447
    quote bind_quoted: [codepoint: codepoint] do
448
      cond do
449
        codepoint <= 0x7FF -> 2
450
        codepoint <= 0xFFFF -> 3
451
        true -> 4
452
      end
453
    end
454
  end
455

456
  @compile {:inline, string_continue: 3}
457

458
  defp string_continue(<<?", _rest::bits>>, _data, skip) do
953,870✔
459
    ["" | skip + 1]
100,361✔
460
  end
461

462
  defp string_continue(rest, data, skip) do
463
    string_continue(rest, data, skip, false, 0, [])
326,291✔
464
  end
465

466
  @compile {:inline, string_continue: 6}
467

468
  defp string_continue(<<?", _rest::bits>>, data, skip, unicode, len, acc) do
469
    cond do
325,451✔
470
      acc == [] ->
471
        if len > 0 do
320,404✔
472
          [binary_part(data, skip, len) | skip + len + 1]
26,213✔
473
        else
474
          ["" | skip + 1]
475
        end
476

477
      unicode ->
5,047✔
478
        case :unicode.characters_to_binary([acc | binary_part(data, skip, len)], :utf8) do
4,851✔
479
          string when is_binary(string) ->
4,389✔
480
            [string | skip + len + 1]
462✔
481

482
          _other ->
NEW
483
            syntax_error(skip + len)
×
484
        end
485

486
      true ->
196✔
487
        [IO.iodata_to_binary([acc | binary_part(data, skip, len)]) | skip + len + 1]
21✔
488
    end
489
  end
490

491
  defp string_continue(<<?\\, rest::bits>>, data, skip, unicode, len, acc) do
492
    string_escape(rest, data, skip + len + 1, unicode, [acc | binary_part(data, skip, len)])
6,496✔
493
  end
494

495
  defp string_continue(<<char, rest::bits>>, data, skip, unicode, len, acc) when char >= 0x20 do
496
    string_continue(rest, data, skip, unicode, len + 1, acc)
47,608,919✔
497
  end
498

499
  defp string_continue(<<codepoint::utf8, rest::bits>>, data, skip, _unicode, len, acc)
500
       when codepoint > 0x80 do
501
    string_continue(rest, data, skip, true, len + string_codepoint_size(codepoint), acc)
×
502
  end
503

504
  defp string_continue(_other, _data, skip, _unicode, len, _acc) do
505
    syntax_error(skip + len)
336✔
506
  end
507

508
  @compile {:inline, string_escape: 5}
509

510
  defp string_escape(<<?u, rest::bits>>, data, skip, _unicode, acc) do
511
    string_escape_unicode(rest, data, skip, acc)
5,691✔
512
  end
513

514
  for {seq, char} <- Enum.zip(~C("\ntr/fb), ~c("\\\n\t\r/\f\b)) do
515
    defp string_escape(<<unquote(seq), rest::bits>>, data, skip, unicode, acc) do
516
      string_continue(rest, data, skip + 1, unicode, 0, [acc | [unquote(char)]])
157✔
517
    end
518
  end
519

520
  defp string_escape(_rest, _data, skip, _unicode, _acc), do: syntax_error(skip)
210✔
521

522
  # http://www.ietf.org/rfc/rfc2781.txt
523
  # http://perldoc.perl.org/Encode/Unicode.html#Surrogate-Pairs
524
  # http://mathiasbynens.be/notes/javascript-encoding#surrogate-pairs
525
  defguardp is_hi_surrogate(cp) when cp in 0xD800..0xDBFF
526
  defguardp is_lo_surrogate(cp) when cp in 0xDC00..0xDFFF
527

528
  defmacrop get_codepoint(seq, skip) do
529
    quote bind_quoted: [seq: seq, skip: skip] do
530
      try do
531
        String.to_integer(seq, 16)
532
      rescue
533
        ArgumentError ->
534
          reraise ParseError, [skip: skip, value: "\\u#{seq}"], __STACKTRACE__
535
      end
536
    end
537
  end
538

539
  @compile {:inline, string_escape_unicode: 4}
540

541
  defp string_escape_unicode(<<seq1::binary-size(4), rest::bits>>, data, skip, acc) do
542
    case get_codepoint(seq1, skip) do
5,670✔
543
      hi when is_hi_surrogate(hi) ->
544
        case rest do
2,520✔
545
          <<"\\u", seq2::binary-size(4), rest::bits>> ->
546
            case get_codepoint(seq2, skip + 6) do
2,436✔
547
              lo when is_lo_surrogate(lo) ->
548
                codepoint = 0x10000 + ((hi &&& 0x03FF) <<< 10) + (lo &&& 0x03FF)
2,331✔
549
                string_continue(rest, data, skip + 11, true, 0, [acc | [codepoint]])
2,331✔
550

551
              _other ->
552
                raise ParseError, skip: skip, value: "\\u#{seq1}\\u#{seq2}"
42✔
553
            end
554

555
          _rest ->
556
            raise ParseError, skip: skip, value: "\\u#{seq1}"
84✔
557
        end
558

559
      lo when is_lo_surrogate(lo) ->
560
        raise ParseError, skip: skip, value: "\\u#{seq1}"
21✔
561

562
      codepoint ->
563
        string_continue(rest, data, skip + 5, true, 0, [acc | [codepoint]])
3,066✔
564
    end
565
  end
566

567
  defp string_escape_unicode(_rest, _data, skip, _acc), do: syntax_error(skip + 1)
21✔
568

569
  ## Whitespace
570

571
  @compile {:inline, skip_whitespace: 3}
572

573
  defp skip_whitespace(<<>>, _skip, value) do
574
    value
17,850✔
575
  end
576

577
  for char <- whitespace do
578
    defp skip_whitespace(<<unquote(char), rest::bits>>, skip, value) do
579
      skip_whitespace(rest, skip + 1, value)
231✔
580
    end
581
  end
582

583
  defp skip_whitespace(_rest, skip, _value) do
584
    syntax_error(skip)
378✔
585
  end
586
end
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc