dependabot-hex 0.118.13 → 0.119.0.beta1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 15b9133b51ceb0643d5be3bf66f1ef9c13c4d108692c2caee8004ff325ae65ef
4
- data.tar.gz: 56d1e51a741756ec873cce6d8b4c8e2f55e45bbcdc7a4e917e865d712be4c57f
3
+ metadata.gz: 3604a148074d38433c48025d48dbc44da12cd64132cfd55372445b47dd830864
4
+ data.tar.gz: db60087432ed4e23f7edc0014348bb47175ca81fa5cdd1b1811936c747c0f260
5
5
  SHA512:
6
- metadata.gz: 1d0aa7e6a6710d668a63e1f70aae63e0614e862ceaf285c8848f35c34d7542c3681a38b675d42dc7f199e602532d49fd10b1dbc9ac74053f3e9f4b5a91139aa9
7
- data.tar.gz: 03e049e53a668a29a8a680f9d8ddbd67f31bc74b1aa0b95ca397307fb122ce78a7fa6429bf796ee130588e1c27bca7f04f29ced069aae98ff0ce70a6cf960156
6
+ metadata.gz: 4cc9b3bafe4314ea6147a566e4c99ba1f5ef9d53d8428fa50cf0bdd70736dc996b7c3e16d4e7c5869224504632d850ebe69f85fa4037a4b961633d9b8a2b5f4d
7
+ data.tar.gz: 549c1b9f72347c16f9bfbde5137e1ddf92a163283f0c016ea331867efa41e0ec64a4669074a6c4fd872235063b8621958b68980c3d7686a0d7ca68a8e4b7fa2c
@@ -0,0 +1,88 @@
1
+ # Changelog
2
+
3
+ ## 1.2.1 (04.05.2020)
4
+
5
+ ### Security
6
+
7
+ * Fix `html_safe` escaping in `Jason.encode`
8
+
9
+ The `<!--` sequence of characters would not be escaped in `Jason.encode`
10
+ with`html_escape` mode, which could lead to DoS attacks when used for
11
+ embedding of arbitrary, user controlled strings into HTML through JSON
12
+ (e.g. inside of `<script>` tags).
13
+
14
+ If you were not using the `html_safe` option, you are not affected.
15
+
16
+ Affected versions: < 1.2.1
17
+ Patched versions: >= 1.2.1
18
+
19
+ ## 1.2.0 (17.03.2020)
20
+
21
+ ### Enhancements
22
+
23
+ * Add `Jason.Encode.keyword/2`
24
+ ([cb1f26a](https://github.com/michalmuskala/jason/commit/cb1f26a)).
25
+
26
+ ### Bug fixes
27
+
28
+ * Fix `Jason.Helpers.json_map/1` value expansion
29
+ ([70b046a](https://github.com/michalmuskala/jason/commit/70b046a)).
30
+
31
+ ## 1.1.2 (19.10.2018)
32
+
33
+ ### Bug fixes
34
+
35
+ * correctly handle the `pretty: false` option
36
+ ([ba318c8](https://github.com/michalmuskala/jason/commit/ba318c8)).
37
+
38
+ ## 1.1.1 (10.07.2018)
39
+
40
+ ### Bug fixes
41
+
42
+ * correctly handle escape sequences in strings when pretty printing
43
+ ([794bbe4](https://github.com/michalmuskala/jason/commit/794bbe4)).
44
+
45
+ ## 1.1.0 (02.07.2018)
46
+
47
+ ### Enhancements
48
+
49
+ * pretty-printing support through `Jason.Formatter` and `pretty: true` option
50
+ in `Jason.encode/2` ([d758e36](https://github.com/michalmuskala/jason/commit/d758e36)).
51
+
52
+ ### Bug fixes
53
+
54
+ * silence variable warnings for fields with underscores used during deriving
55
+ ([88dd85c](https://github.com/michalmuskala/jason/commit/88dd85c)).
56
+ * **potential incompatibility** don't raise `Protocol.UndefinedError` in non-bang functions
57
+ ([ad0f57b](https://github.com/michalmuskala/jason/commit/ad0f57b)).
58
+
59
+ ## 1.0.1 (02.07.2018)
60
+
61
+ ### Bug fixes
62
+
63
+ * fix `Jason.Encode.escape` type ([a57b430](https://github.com/michalmuskala/jason/commit/a57b430))
64
+ * multiple documentation improvements
65
+
66
+ ## 1.0.0 (26.01.2018)
67
+
68
+ No changes
69
+
70
+ ## 1.0.0-rc.3 (26.01.2018)
71
+
72
+ ### Changes
73
+
74
+ * update `escape` option of `Jason.encode/2` to take values:
75
+ `:json | :unicode_safe | :html_safe | :javascript_safe` for consistency. Old values of
76
+ `:unicode` and `:javascript` are still supported for compatibility with Poison.
77
+ ([f42dcbd](https://github.com/michalmuskala/jason/commit/f42dcbd))
78
+
79
+ ## 1.0.0-rc.2 (07.01.2018)
80
+
81
+ ### Bug fixes
82
+
83
+ * add type for `strings` option ([b459ee4](https://github.com/michalmuskala/jason/commit/b459ee4))
84
+ * support iodata in `decode!` ([a1f3456](https://github.com/michalmuskala/jason/commit/a1f3456))
85
+
86
+ ## 1.0.0-rc.1 (22.12.2017)
87
+
88
+ Initial release
@@ -0,0 +1,13 @@
1
+ Copyright 2017 Michał Muskała
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.
@@ -0,0 +1,141 @@
1
+ # Jason
2
+
3
+ A blazing fast JSON parser and generator in pure Elixir.
4
+
5
+ The parser and generator are at least twice as fast as other Elixir/Erlang libraries
6
+ (most notably `Poison`).
7
+ The performance is comparable to `jiffy`, which is implemented in C as a NIF.
8
+ Jason is usually only twice as slow.
9
+
10
+ Both parser and generator fully conform to
11
+ [RFC 8259](https://tools.ietf.org/html/rfc8259) and
12
+ [ECMA 404](http://www.ecma-international.org/publications/standards/Ecma-404.htm)
13
+ standards. The parser is tested using [JSONTestSuite](https://github.com/nst/JSONTestSuite).
14
+
15
+ ## Installation
16
+
17
+ The package can be installed by adding `jason` to your list of dependencies
18
+ in `mix.exs`:
19
+
20
+ ```elixir
21
+ def deps do
22
+ [{:jason, "~> 1.2"}]
23
+ end
24
+ ```
25
+
26
+ ## Basic Usage
27
+
28
+ ``` elixir
29
+ iex(1)> Jason.encode!(%{"age" => 44, "name" => "Steve Irwin", "nationality" => "Australian"})
30
+ "{\"age\":44,\"name\":\"Steve Irwin\",\"nationality\":\"Australian\"}"
31
+
32
+ iex(2)> Jason.decode!(~s({"age":44,"name":"Steve Irwin","nationality":"Australian"}))
33
+ %{"age" => 44, "name" => "Steve Irwin", "nationality" => "Australian"}
34
+ ```
35
+
36
+ Full documentation can be found at [https://hexdocs.pm/jason](https://hexdocs.pm/jason).
37
+
38
+ ## Use with other libraries
39
+
40
+ ### Postgrex
41
+
42
+ Versions starting at 0.14.0 use `Jason` by default. For earlier versions, please refer to
43
+ [previous versions of this document](https://github.com/michalmuskala/jason/tree/v1.1.2#postgrex).
44
+
45
+ ### Ecto
46
+
47
+ Versions starting at 3.0.0 use `Jason` by default. For earlier versions, please refer to
48
+ [previous versions of this document](https://github.com/michalmuskala/jason/tree/v1.1.2#ecto).
49
+
50
+ ### Plug (and Phoenix)
51
+
52
+ Phoenix starting at 1.4.0 uses `Jason` by default. For earlier versions, please refer to
53
+ [previous versions of this document](https://github.com/michalmuskala/jason/tree/v1.1.2#plug-and-phoenix).
54
+
55
+ ### Absinthe
56
+
57
+ You need to pass the `:json_codec` option to `Absinthe.Plug`
58
+
59
+ ```elixir
60
+ # When called directly:
61
+ plug Absinthe.Plug,
62
+ schema: MyApp.Schema,
63
+ json_codec: Jason
64
+
65
+ # When used in phoenix router:
66
+ forward "/api",
67
+ to: Absinthe.Plug,
68
+ init_opts: [schema: MyApp.Schema, json_codec: Jason]
69
+ ```
70
+
71
+ ## Benchmarks
72
+
73
+ Detailed benchmarks (including memory measurements):
74
+ https://gist.github.com/michalmuskala/4d64a5a7696ca84ac7c169a0206640d5
75
+
76
+ HTML reports for the benchmark (only performance measurements):
77
+ http://michal.muskala.eu/jason/decode.html and http://michal.muskala.eu/jason/encode.html
78
+
79
+ ### Running
80
+
81
+ Benchmarks against most popular Elixir & Erlang json libraries can be executed after
82
+ going into the `bench/` folder and then executing `mix bench.encode` and `mix bench.decode`.
83
+ A HTML report of the benchmarks (after their execution) can be found in
84
+ `bench/output/encode.html` and `bench/output/decode.html` respectively.
85
+
86
+ ## Differences to Poison
87
+
88
+ Jason has a couple feature differences compared to Poison.
89
+
90
+ * Jason follows the JSON spec more strictly, for example it does not allow
91
+ unescaped newline characters in JSON strings - e.g. `"\"\n\""` will
92
+ produce a decoding error.
93
+ * no support for decoding into data structures (the `as:` option).
94
+ * no built-in encoders for `MapSet`, `Range` and `Stream`.
95
+ * no support for encoding arbitrary structs - explicit implementation
96
+ of the `Jason.Encoder` protocol is always required.
97
+ * different pretty-printing customisation options (default `pretty: true` works the same)
98
+
99
+ If you require encoders for any of the unsupported collection types, I suggest
100
+ adding the needed implementations directly to your project:
101
+
102
+ ```elixir
103
+ defimpl Jason.Encoder, for: [MapSet, Range, Stream] do
104
+ def encode(struct, opts) do
105
+ Jason.Encode.list(Enum.to_list(struct), opts)
106
+ end
107
+ end
108
+ ```
109
+
110
+ If you need to encode some struct that does not implement the protocol,
111
+ if you own the struct, you can derive the implementation specifying
112
+ which fields should be encoded to JSON:
113
+
114
+ ```elixir
115
+ @derive {Jason.Encoder, only: [....]}
116
+ defstruct # ...
117
+ ```
118
+
119
+ It is also possible to encode all fields, although this should be
120
+ used carefully to avoid accidentally leaking private information
121
+ when new fields are added:
122
+
123
+ ```elixir
124
+ @derive Jason.Encoder
125
+ defstruct # ...
126
+ ```
127
+
128
+ Finally, if you don't own the struct you want to encode to JSON,
129
+ you may use `Protocol.derive/3` placed outside of any module:
130
+
131
+ ```elixir
132
+ Protocol.derive(Jason.Encoder, NameOfTheStruct, only: [...])
133
+ Protocol.derive(Jason.Encoder, NameOfTheStruct)
134
+ ```
135
+
136
+ ## License
137
+
138
+ Jason is released under the Apache License 2.0 - see the [LICENSE](LICENSE) file.
139
+
140
+ Some elements of tests and benchmarks have their origins in the
141
+ [Poison library](https://github.com/devinus/poison) and were initially licensed under [CC0-1.0](https://creativecommons.org/publicdomain/zero/1.0/).
@@ -0,0 +1,20 @@
1
+ {<<"app">>,<<"jason">>}.
2
+ {<<"build_tools">>,[<<"mix">>]}.
3
+ {<<"description">>,
4
+ <<"A blazing fast JSON parser and generator in pure Elixir.">>}.
5
+ {<<"elixir">>,<<"~> 1.4">>}.
6
+ {<<"files">>,
7
+ [<<"lib">>,<<"lib/jason.ex">>,<<"lib/encoder.ex">>,<<"lib/decoder.ex">>,
8
+ <<"lib/formatter.ex">>,<<"lib/encode.ex">>,<<"lib/codegen.ex">>,
9
+ <<"lib/helpers.ex">>,<<"lib/fragment.ex">>,<<"mix.exs">>,<<"README.md">>,
10
+ <<"LICENSE">>,<<"CHANGELOG.md">>]}.
11
+ {<<"licenses">>,[<<"Apache-2.0">>]}.
12
+ {<<"links">>,[{<<"GitHub">>,<<"https://github.com/michalmuskala/jason">>}]}.
13
+ {<<"name">>,<<"jason">>}.
14
+ {<<"requirements">>,
15
+ [[{<<"app">>,<<"decimal">>},
16
+ {<<"name">>,<<"decimal">>},
17
+ {<<"optional">>,true},
18
+ {<<"repository">>,<<"hexpm">>},
19
+ {<<"requirement">>,<<"~> 1.0">>}]]}.
20
+ {<<"version">>,<<"1.2.1">>}.
@@ -0,0 +1,138 @@
1
+ defmodule Jason.Codegen do
2
+ @moduledoc false
3
+
4
+ alias Jason.{Encode, EncodeError}
5
+
6
+ def jump_table(ranges, default) do
7
+ ranges
8
+ |> ranges_to_orddict()
9
+ |> :array.from_orddict(default)
10
+ |> :array.to_orddict()
11
+ end
12
+
13
+ def jump_table(ranges, default, max) do
14
+ ranges
15
+ |> ranges_to_orddict()
16
+ |> :array.from_orddict(default)
17
+ |> resize(max)
18
+ |> :array.to_orddict()
19
+ end
20
+
21
+ defmacro bytecase(var, do: clauses) do
22
+ {ranges, default, literals} = clauses_to_ranges(clauses, [])
23
+
24
+ jump_table = jump_table(ranges, default)
25
+
26
+ quote do
27
+ case unquote(var) do
28
+ unquote(jump_table_to_clauses(jump_table, literals))
29
+ end
30
+ end
31
+ end
32
+
33
+ defmacro bytecase(var, max, do: clauses) do
34
+ {ranges, default, empty} = clauses_to_ranges(clauses, [])
35
+
36
+ jump_table = jump_table(ranges, default, max)
37
+
38
+ quote do
39
+ case unquote(var) do
40
+ unquote(jump_table_to_clauses(jump_table, empty))
41
+ end
42
+ end
43
+ end
44
+
45
+ def build_kv_iodata(kv, encode_args) do
46
+ elements =
47
+ kv
48
+ |> Enum.map(&encode_pair(&1, encode_args))
49
+ |> Enum.intersperse(",")
50
+
51
+ collapse_static(List.flatten(["{", elements] ++ '}'))
52
+ end
53
+
54
+ defp clauses_to_ranges([{:->, _, [[{:in, _, [byte, range]}, rest], action]} | tail], acc) do
55
+ clauses_to_ranges(tail, [{range, {byte, rest, action}} | acc])
56
+ end
57
+
58
+ defp clauses_to_ranges([{:->, _, [[default, rest], action]} | tail], acc) do
59
+ {Enum.reverse(acc), {default, rest, action}, literal_clauses(tail)}
60
+ end
61
+
62
+ defp literal_clauses(clauses) do
63
+ Enum.map(clauses, fn {:->, _, [[literal], action]} ->
64
+ {literal, action}
65
+ end)
66
+ end
67
+
68
+ defp jump_table_to_clauses([{val, {{:_, _, _}, rest, action}} | tail], empty) do
69
+ quote do
70
+ <<unquote(val), unquote(rest)::bits>> ->
71
+ unquote(action)
72
+ end ++ jump_table_to_clauses(tail, empty)
73
+ end
74
+
75
+ defp jump_table_to_clauses([{val, {byte, rest, action}} | tail], empty) do
76
+ quote do
77
+ <<unquote(byte), unquote(rest)::bits>> when unquote(byte) === unquote(val) ->
78
+ unquote(action)
79
+ end ++ jump_table_to_clauses(tail, empty)
80
+ end
81
+
82
+ defp jump_table_to_clauses([], literals) do
83
+ Enum.flat_map(literals, fn {pattern, action} ->
84
+ quote do
85
+ unquote(pattern) ->
86
+ unquote(action)
87
+ end
88
+ end)
89
+ end
90
+
91
+ defp resize(array, size), do: :array.resize(size, array)
92
+
93
+ defp ranges_to_orddict(ranges) do
94
+ ranges
95
+ |> Enum.flat_map(fn
96
+ {int, value} when is_integer(int) ->
97
+ [{int, value}]
98
+
99
+ {enum, value} ->
100
+ Enum.map(enum, &{&1, value})
101
+ end)
102
+ |> :orddict.from_list()
103
+ end
104
+
105
+ defp encode_pair({key, value}, encode_args) do
106
+ key = IO.iodata_to_binary(Encode.key(key, &escape_key/3))
107
+ key = "\"" <> key <> "\":"
108
+ [key, quote(do: Encode.value(unquote(value), unquote_splicing(encode_args)))]
109
+ end
110
+
111
+ defp escape_key(binary, _original, _skip) do
112
+ check_safe_key!(binary)
113
+ binary
114
+ end
115
+
116
+ defp check_safe_key!(binary) do
117
+ for <<(<<byte>> <- binary)>> do
118
+ if byte > 0x7F or byte < 0x1F or byte in '"\\/' do
119
+ raise EncodeError,
120
+ "invalid byte #{inspect(byte, base: :hex)} in literal key: #{inspect(binary)}"
121
+ end
122
+ end
123
+
124
+ :ok
125
+ end
126
+
127
+ defp collapse_static([bin1, bin2 | rest]) when is_binary(bin1) and is_binary(bin2) do
128
+ collapse_static([bin1 <> bin2 | rest])
129
+ end
130
+
131
+ defp collapse_static([other | rest]) do
132
+ [other | collapse_static(rest)]
133
+ end
134
+
135
+ defp collapse_static([]) do
136
+ []
137
+ end
138
+ end
@@ -0,0 +1,657 @@
1
+ defmodule Jason.DecodeError do
2
+ @type t :: %__MODULE__{position: integer, data: String.t}
3
+
4
+ defexception [:position, :token, :data]
5
+
6
+ def message(%{position: position, token: token}) when is_binary(token) do
7
+ "unexpected sequence at position #{position}: #{inspect token}"
8
+ end
9
+ def message(%{position: position, data: data}) when position == byte_size(data) do
10
+ "unexpected end of input at position #{position}"
11
+ end
12
+ def message(%{position: position, data: data}) do
13
+ byte = :binary.at(data, position)
14
+ str = <<byte>>
15
+ if String.printable?(str) do
16
+ "unexpected byte at position #{position}: " <>
17
+ "#{inspect byte, base: :hex} ('#{str}')"
18
+ else
19
+ "unexpected byte at position #{position}: " <>
20
+ "#{inspect byte, base: :hex}"
21
+ end
22
+ end
23
+ end
24
+
25
+ defmodule Jason.Decoder do
26
+ @moduledoc false
27
+
28
+ import Bitwise
29
+
30
+ alias Jason.{DecodeError, Codegen}
31
+
32
+ import Codegen, only: [bytecase: 2, bytecase: 3]
33
+
34
+ # @compile :native
35
+
36
+ # We use integers instead of atoms to take advantage of the jump table
37
+ # optimization
38
+ @terminate 0
39
+ @array 1
40
+ @key 2
41
+ @object 3
42
+
43
+ def parse(data, opts) when is_binary(data) do
44
+ key_decode = key_decode_function(opts)
45
+ string_decode = string_decode_function(opts)
46
+ try do
47
+ value(data, data, 0, [@terminate], key_decode, string_decode)
48
+ catch
49
+ {:position, position} ->
50
+ {:error, %DecodeError{position: position, data: data}}
51
+ {:token, token, position} ->
52
+ {:error, %DecodeError{token: token, position: position, data: data}}
53
+ else
54
+ value ->
55
+ {:ok, value}
56
+ end
57
+ end
58
+
59
+ defp key_decode_function(%{keys: :atoms}), do: &String.to_atom/1
60
+ defp key_decode_function(%{keys: :atoms!}), do: &String.to_existing_atom/1
61
+ defp key_decode_function(%{keys: :strings}), do: &(&1)
62
+ defp key_decode_function(%{keys: fun}) when is_function(fun, 1), do: fun
63
+
64
+ defp string_decode_function(%{strings: :copy}), do: &:binary.copy/1
65
+ defp string_decode_function(%{strings: :reference}), do: &(&1)
66
+
67
+ defp value(data, original, skip, stack, key_decode, string_decode) do
68
+ bytecase data do
69
+ _ in '\s\n\t\r', rest ->
70
+ value(rest, original, skip + 1, stack, key_decode, string_decode)
71
+ _ in '0', rest ->
72
+ number_zero(rest, original, skip, stack, key_decode, string_decode, 1)
73
+ _ in '123456789', rest ->
74
+ number(rest, original, skip, stack, key_decode, string_decode, 1)
75
+ _ in '-', rest ->
76
+ number_minus(rest, original, skip, stack, key_decode, string_decode)
77
+ _ in '"', rest ->
78
+ string(rest, original, skip + 1, stack, key_decode, string_decode, 0)
79
+ _ in '[', rest ->
80
+ array(rest, original, skip + 1, stack, key_decode, string_decode)
81
+ _ in '{', rest ->
82
+ object(rest, original, skip + 1, stack, key_decode, string_decode)
83
+ _ in ']', rest ->
84
+ empty_array(rest, original, skip + 1, stack, key_decode, string_decode)
85
+ _ in 't', rest ->
86
+ case rest do
87
+ <<"rue", rest::bits>> ->
88
+ continue(rest, original, skip + 4, stack, key_decode, string_decode, true)
89
+ <<_::bits>> ->
90
+ error(original, skip)
91
+ end
92
+ _ in 'f', rest ->
93
+ case rest do
94
+ <<"alse", rest::bits>> ->
95
+ continue(rest, original, skip + 5, stack, key_decode, string_decode, false)
96
+ <<_::bits>> ->
97
+ error(original, skip)
98
+ end
99
+ _ in 'n', rest ->
100
+ case rest do
101
+ <<"ull", rest::bits>> ->
102
+ continue(rest, original, skip + 4, stack, key_decode, string_decode, nil)
103
+ <<_::bits>> ->
104
+ error(original, skip)
105
+ end
106
+ _, rest ->
107
+ error(rest, original, skip + 1, stack, key_decode, string_decode)
108
+ <<_::bits>> ->
109
+ error(original, skip)
110
+ end
111
+ end
112
+
113
+ defp number_minus(<<?0, rest::bits>>, original, skip, stack, key_decode, string_decode) do
114
+ number_zero(rest, original, skip, stack, key_decode, string_decode, 2)
115
+ end
116
+ defp number_minus(<<byte, rest::bits>>, original, skip, stack, key_decode, string_decode)
117
+ when byte in '123456789' do
118
+ number(rest, original, skip, stack, key_decode, string_decode, 2)
119
+ end
120
+ defp number_minus(<<_rest::bits>>, original, skip, _stack, _key_decode, _string_decode) do
121
+ error(original, skip + 1)
122
+ end
123
+
124
+ defp number(<<byte, rest::bits>>, original, skip, stack, key_decode, string_decode, len)
125
+ when byte in '0123456789' do
126
+ number(rest, original, skip, stack, key_decode, string_decode, len + 1)
127
+ end
128
+ defp number(<<?., rest::bits>>, original, skip, stack, key_decode, string_decode, len) do
129
+ number_frac(rest, original, skip, stack, key_decode, string_decode, len + 1)
130
+ end
131
+ defp number(<<e, rest::bits>>, original, skip, stack, key_decode, string_decode, len) when e in 'eE' do
132
+ prefix = binary_part(original, skip, len)
133
+ number_exp_copy(rest, original, skip + len + 1, stack, key_decode, string_decode, prefix)
134
+ end
135
+ defp number(<<rest::bits>>, original, skip, stack, key_decode, string_decode, len) do
136
+ int = String.to_integer(binary_part(original, skip, len))
137
+ continue(rest, original, skip + len, stack, key_decode, string_decode, int)
138
+ end
139
+
140
+ defp number_frac(<<byte, rest::bits>>, original, skip, stack, key_decode, string_decode, len)
141
+ when byte in '0123456789' do
142
+ number_frac_cont(rest, original, skip, stack, key_decode, string_decode, len + 1)
143
+ end
144
+ defp number_frac(<<_rest::bits>>, original, skip, _stack, _key_decode, _string_decode, len) do
145
+ error(original, skip + len)
146
+ end
147
+
148
+ defp number_frac_cont(<<byte, rest::bits>>, original, skip, stack, key_decode, string_decode, len)
149
+ when byte in '0123456789' do
150
+ number_frac_cont(rest, original, skip, stack, key_decode, string_decode, len + 1)
151
+ end
152
+ defp number_frac_cont(<<e, rest::bits>>, original, skip, stack, key_decode, string_decode, len)
153
+ when e in 'eE' do
154
+ number_exp(rest, original, skip, stack, key_decode, string_decode, len + 1)
155
+ end
156
+ defp number_frac_cont(<<rest::bits>>, original, skip, stack, key_decode, string_decode, len) do
157
+ token = binary_part(original, skip, len)
158
+ float = try_parse_float(token, token, skip)
159
+ continue(rest, original, skip + len, stack, key_decode, string_decode, float)
160
+ end
161
+
162
+ defp number_exp(<<byte, rest::bits>>, original, skip, stack, key_decode, string_decode, len)
163
+ when byte in '0123456789' do
164
+ number_exp_cont(rest, original, skip, stack, key_decode, string_decode, len + 1)
165
+ end
166
+ defp number_exp(<<byte, rest::bits>>, original, skip, stack, key_decode, string_decode, len)
167
+ when byte in '+-' do
168
+ number_exp_sign(rest, original, skip, stack, key_decode, string_decode, len + 1)
169
+ end
170
+ defp number_exp(<<_rest::bits>>, original, skip, _stack, _key_decode, _string_decode, len) do
171
+ error(original, skip + len)
172
+ end
173
+
174
+ defp number_exp_sign(<<byte, rest::bits>>, original, skip, stack, key_decode, string_decode, len)
175
+ when byte in '0123456789' do
176
+ number_exp_cont(rest, original, skip, stack, key_decode, string_decode, len + 1)
177
+ end
178
+ defp number_exp_sign(<<_rest::bits>>, original, skip, _stack, _key_decode, _string_decode, len) do
179
+ error(original, skip + len)
180
+ end
181
+
182
+ defp number_exp_cont(<<byte, rest::bits>>, original, skip, stack, key_decode, string_decode, len)
183
+ when byte in '0123456789' do
184
+ number_exp_cont(rest, original, skip, stack, key_decode, string_decode, len + 1)
185
+ end
186
+ defp number_exp_cont(<<rest::bits>>, original, skip, stack, key_decode, string_decode, len) do
187
+ token = binary_part(original, skip, len)
188
+ float = try_parse_float(token, token, skip)
189
+ continue(rest, original, skip + len, stack, key_decode, string_decode, float)
190
+ end
191
+
192
+ defp number_exp_copy(<<byte, rest::bits>>, original, skip, stack, key_decode, string_decode, prefix)
193
+ when byte in '0123456789' do
194
+ number_exp_cont(rest, original, skip, stack, key_decode, string_decode, prefix, 1)
195
+ end
196
+ defp number_exp_copy(<<byte, rest::bits>>, original, skip, stack, key_decode, string_decode, prefix)
197
+ when byte in '+-' do
198
+ number_exp_sign(rest, original, skip, stack, key_decode, string_decode, prefix, 1)
199
+ end
200
+ defp number_exp_copy(<<_rest::bits>>, original, skip, _stack, _key_decode, _string_decode, _prefix) do
201
+ error(original, skip)
202
+ end
203
+
204
+ defp number_exp_sign(<<byte, rest::bits>>, original, skip, stack, key_decode, string_decode, prefix, len)
205
+ when byte in '0123456789' do
206
+ number_exp_cont(rest, original, skip, stack, key_decode, string_decode, prefix, len + 1)
207
+ end
208
+ defp number_exp_sign(<<_rest::bits>>, original, skip, _stack, _key_decode, _string_decode, _prefix, len) do
209
+ error(original, skip + len)
210
+ end
211
+
212
+ defp number_exp_cont(<<byte, rest::bits>>, original, skip, stack, key_decode, string_decode, prefix, len)
213
+ when byte in '0123456789' do
214
+ number_exp_cont(rest, original, skip, stack, key_decode, string_decode, prefix, len + 1)
215
+ end
216
+ defp number_exp_cont(<<rest::bits>>, original, skip, stack, key_decode, string_decode, prefix, len) do
217
+ suffix = binary_part(original, skip, len)
218
+ string = prefix <> ".0e" <> suffix
219
+ prefix_size = byte_size(prefix)
220
+ initial_skip = skip - prefix_size - 1
221
+ final_skip = skip + len
222
+ token = binary_part(original, initial_skip, prefix_size + len + 1)
223
+ float = try_parse_float(string, token, initial_skip)
224
+ continue(rest, original, final_skip, stack, key_decode, string_decode, float)
225
+ end
226
+
227
+ defp number_zero(<<?., rest::bits>>, original, skip, stack, key_decode, string_decode, len) do
228
+ number_frac(rest, original, skip, stack, key_decode, string_decode, len + 1)
229
+ end
230
+ defp number_zero(<<e, rest::bits>>, original, skip, stack, key_decode, string_decode, len) when e in 'eE' do
231
+ number_exp_copy(rest, original, skip + len + 1, stack, key_decode, string_decode, "0")
232
+ end
233
+ defp number_zero(<<rest::bits>>, original, skip, stack, key_decode, string_decode, len) do
234
+ continue(rest, original, skip + len, stack, key_decode, string_decode, 0)
235
+ end
236
+
237
+ @compile {:inline, array: 6}
238
+
239
+ defp array(rest, original, skip, stack, key_decode, string_decode) do
240
+ value(rest, original, skip, [@array, [] | stack], key_decode, string_decode)
241
+ end
242
+
243
+ defp empty_array(<<rest::bits>>, original, skip, stack, key_decode, string_decode) do
244
+ case stack do
245
+ [@array, [] | stack] ->
246
+ continue(rest, original, skip, stack, key_decode, string_decode, [])
247
+ _ ->
248
+ error(original, skip - 1)
249
+ end
250
+ end
251
+
252
+ defp array(data, original, skip, stack, key_decode, string_decode, value) do
253
+ bytecase data do
254
+ _ in '\s\n\t\r', rest ->
255
+ array(rest, original, skip + 1, stack, key_decode, string_decode, value)
256
+ _ in ']', rest ->
257
+ [acc | stack] = stack
258
+ value = :lists.reverse(acc, [value])
259
+ continue(rest, original, skip + 1, stack, key_decode, string_decode, value)
260
+ _ in ',', rest ->
261
+ [acc | stack] = stack
262
+ value(rest, original, skip + 1, [@array, [value | acc] | stack], key_decode, string_decode)
263
+ _, _rest ->
264
+ error(original, skip)
265
+ <<_::bits>> ->
266
+ empty_error(original, skip)
267
+ end
268
+ end
269
+
270
+ @compile {:inline, object: 6}
271
+
272
+ defp object(rest, original, skip, stack, key_decode, string_decode) do
273
+ key(rest, original, skip, [[] | stack], key_decode, string_decode)
274
+ end
275
+
276
+ defp object(data, original, skip, stack, key_decode, string_decode, value) do
277
+ bytecase data do
278
+ _ in '\s\n\t\r', rest ->
279
+ object(rest, original, skip + 1, stack, key_decode, string_decode, value)
280
+ _ in '}', rest ->
281
+ skip = skip + 1
282
+ [key, acc | stack] = stack
283
+ final = [{key_decode.(key), value} | acc]
284
+ continue(rest, original, skip, stack, key_decode, string_decode, :maps.from_list(final))
285
+ _ in ',', rest ->
286
+ skip = skip + 1
287
+ [key, acc | stack] = stack
288
+ acc = [{key_decode.(key), value} | acc]
289
+ key(rest, original, skip, [acc | stack], key_decode, string_decode)
290
+ _, _rest ->
291
+ error(original, skip)
292
+ <<_::bits>> ->
293
+ empty_error(original, skip)
294
+ end
295
+ end
296
+
297
+ defp key(data, original, skip, stack, key_decode, string_decode) do
298
+ bytecase data do
299
+ _ in '\s\n\t\r', rest ->
300
+ key(rest, original, skip + 1, stack, key_decode, string_decode)
301
+ _ in '}', rest ->
302
+ case stack do
303
+ [[] | stack] ->
304
+ continue(rest, original, skip + 1, stack, key_decode, string_decode, %{})
305
+ _ ->
306
+ error(original, skip)
307
+ end
308
+ _ in '"', rest ->
309
+ string(rest, original, skip + 1, [@key | stack], key_decode, string_decode, 0)
310
+ _, _rest ->
311
+ error(original, skip)
312
+ <<_::bits>> ->
313
+ empty_error(original, skip)
314
+ end
315
+ end
316
+
317
+ defp key(data, original, skip, stack, key_decode, string_decode, value) do
318
+ bytecase data do
319
+ _ in '\s\n\t\r', rest ->
320
+ key(rest, original, skip + 1, stack, key_decode, string_decode, value)
321
+ _ in ':', rest ->
322
+ value(rest, original, skip + 1, [@object, value | stack], key_decode, string_decode)
323
+ _, _rest ->
324
+ error(original, skip)
325
+ <<_::bits>> ->
326
+ empty_error(original, skip)
327
+ end
328
+ end
329
+
330
+ # TODO: check if this approach would be faster:
331
+ # https://git.ninenines.eu/cowlib.git/tree/src/cow_ws.erl#n469
332
+ # http://bjoern.hoehrmann.de/utf-8/decoder/dfa/
333
+ defp string(data, original, skip, stack, key_decode, string_decode, len) do
334
+ bytecase data, 128 do
335
+ _ in '"', rest ->
336
+ string = string_decode.(binary_part(original, skip, len))
337
+ continue(rest, original, skip + len + 1, stack, key_decode, string_decode, string)
338
+ _ in '\\', rest ->
339
+ part = binary_part(original, skip, len)
340
+ escape(rest, original, skip + len, stack, key_decode, string_decode, part)
341
+ _ in unquote(0x00..0x1F), _rest ->
342
+ error(original, skip)
343
+ _, rest ->
344
+ string(rest, original, skip, stack, key_decode, string_decode, len + 1)
345
+ <<char::utf8, rest::bits>> when char <= 0x7FF ->
346
+ string(rest, original, skip, stack, key_decode, string_decode, len + 2)
347
+ <<char::utf8, rest::bits>> when char <= 0xFFFF ->
348
+ string(rest, original, skip, stack, key_decode, string_decode, len + 3)
349
+ <<_char::utf8, rest::bits>> ->
350
+ string(rest, original, skip, stack, key_decode, string_decode, len + 4)
351
+ <<_::bits>> ->
352
+ empty_error(original, skip + len)
353
+ end
354
+ end
355
+
356
+ defp string(data, original, skip, stack, key_decode, string_decode, acc, len) do
357
+ bytecase data, 128 do
358
+ _ in '"', rest ->
359
+ last = binary_part(original, skip, len)
360
+ string = IO.iodata_to_binary([acc | last])
361
+ continue(rest, original, skip + len + 1, stack, key_decode, string_decode, string)
362
+ _ in '\\', rest ->
363
+ part = binary_part(original, skip, len)
364
+ escape(rest, original, skip + len, stack, key_decode, string_decode, [acc | part])
365
+ _ in unquote(0x00..0x1F), _rest ->
366
+ error(original, skip)
367
+ _, rest ->
368
+ string(rest, original, skip, stack, key_decode, string_decode, acc, len + 1)
369
+ <<char::utf8, rest::bits>> when char <= 0x7FF ->
370
+ string(rest, original, skip, stack, key_decode, string_decode, acc, len + 2)
371
+ <<char::utf8, rest::bits>> when char <= 0xFFFF ->
372
+ string(rest, original, skip, stack, key_decode, string_decode, acc, len + 3)
373
+ <<_char::utf8, rest::bits>> ->
374
+ string(rest, original, skip, stack, key_decode, string_decode, acc, len + 4)
375
+ <<_::bits>> ->
376
+ empty_error(original, skip + len)
377
+ end
378
+ end
379
+
380
+ defp escape(data, original, skip, stack, key_decode, string_decode, acc) do
381
+ bytecase data do
382
+ _ in 'b', rest ->
383
+ string(rest, original, skip + 2, stack, key_decode, string_decode, [acc | '\b'], 0)
384
+ _ in 't', rest ->
385
+ string(rest, original, skip + 2, stack, key_decode, string_decode, [acc | '\t'], 0)
386
+ _ in 'n', rest ->
387
+ string(rest, original, skip + 2, stack, key_decode, string_decode, [acc | '\n'], 0)
388
+ _ in 'f', rest ->
389
+ string(rest, original, skip + 2, stack, key_decode, string_decode, [acc | '\f'], 0)
390
+ _ in 'r', rest ->
391
+ string(rest, original, skip + 2, stack, key_decode, string_decode, [acc | '\r'], 0)
392
+ _ in '"', rest ->
393
+ string(rest, original, skip + 2, stack, key_decode, string_decode, [acc | '\"'], 0)
394
+ _ in '/', rest ->
395
+ string(rest, original, skip + 2, stack, key_decode, string_decode, [acc | '/'], 0)
396
+ _ in '\\', rest ->
397
+ string(rest, original, skip + 2, stack, key_decode, string_decode, [acc | '\\'], 0)
398
+ _ in 'u', rest ->
399
+ escapeu(rest, original, skip, stack, key_decode, string_decode, acc)
400
+ _, _rest ->
401
+ error(original, skip + 1)
402
+ <<_::bits>> ->
403
+ empty_error(original, skip)
404
+ end
405
+ end
406
+
407
+ defmodule Unescape do
408
+ @moduledoc false
409
+
410
+ import Bitwise
411
+
412
+ @digits Enum.concat([?0..?9, ?A..?F, ?a..?f])
413
+
414
+ def unicode_escapes(chars1 \\ @digits, chars2 \\ @digits) do
415
+ for char1 <- chars1, char2 <- chars2 do
416
+ {(char1 <<< 8) + char2, integer8(char1, char2)}
417
+ end
418
+ end
419
+
420
+ defp integer8(char1, char2) do
421
+ (integer4(char1) <<< 4) + integer4(char2)
422
+ end
423
+
424
+ defp integer4(char) when char in ?0..?9, do: char - ?0
425
+ defp integer4(char) when char in ?A..?F, do: char - ?A + 10
426
+ defp integer4(char) when char in ?a..?f, do: char - ?a + 10
427
+
428
+ defp token_error_clause(original, skip, len) do
429
+ quote do
430
+ _ ->
431
+ token_error(unquote_splicing([original, skip, len]))
432
+ end
433
+ end
434
+
435
+ defmacro escapeu_first(int, last, rest, original, skip, stack, key_decode, string_decode, acc) do
436
+ clauses = escapeu_first_clauses(last, rest, original, skip, stack, key_decode, string_decode, acc)
437
+ quote location: :keep do
438
+ case unquote(int) do
439
+ unquote(clauses ++ token_error_clause(original, skip, 6))
440
+ end
441
+ end
442
+ end
443
+
444
+ defp escapeu_first_clauses(last, rest, original, skip, stack, key_decode, string_decode, acc) do
445
+ for {int, first} <- unicode_escapes(),
446
+ not (first in 0xDC..0xDF) do
447
+ escapeu_first_clause(int, first, last, rest, original, skip, stack, key_decode, string_decode, acc)
448
+ end
449
+ end
450
+
451
+ defp escapeu_first_clause(int, first, last, rest, original, skip, stack, key_decode, string_decode, acc)
452
+ when first in 0xD8..0xDB do
453
+ hi =
454
+ quote bind_quoted: [first: first, last: last] do
455
+ 0x10000 + ((((first &&& 0x03) <<< 8) + last) <<< 10)
456
+ end
457
+ args = [rest, original, skip, stack, key_decode, string_decode, acc, hi]
458
+ [clause] =
459
+ quote location: :keep do
460
+ unquote(int) -> escape_surrogate(unquote_splicing(args))
461
+ end
462
+ clause
463
+ end
464
+
465
+ defp escapeu_first_clause(int, first, last, rest, original, skip, stack, key_decode, string_decode, acc)
466
+ when first <= 0x00 do
467
+ skip = quote do: (unquote(skip) + 6)
468
+ acc =
469
+ quote bind_quoted: [acc: acc, first: first, last: last] do
470
+ if last <= 0x7F do
471
+ # 0?????
472
+ [acc, last]
473
+ else
474
+ # 110xxxx?? 10?????
475
+ byte1 = ((0b110 <<< 5) + (first <<< 2)) + (last >>> 6)
476
+ byte2 = (0b10 <<< 6) + (last &&& 0b111111)
477
+ [acc, byte1, byte2]
478
+ end
479
+ end
480
+ args = [rest, original, skip, stack, key_decode, string_decode, acc, 0]
481
+ [clause] =
482
+ quote location: :keep do
483
+ unquote(int) -> string(unquote_splicing(args))
484
+ end
485
+ clause
486
+ end
487
+
488
+ defp escapeu_first_clause(int, first, last, rest, original, skip, stack, key_decode, string_decode, acc)
489
+ when first <= 0x07 do
490
+ skip = quote do: (unquote(skip) + 6)
491
+ acc =
492
+ quote bind_quoted: [acc: acc, first: first, last: last] do
493
+ # 110xxx?? 10??????
494
+ byte1 = ((0b110 <<< 5) + (first <<< 2)) + (last >>> 6)
495
+ byte2 = (0b10 <<< 6) + (last &&& 0b111111)
496
+ [acc, byte1, byte2]
497
+ end
498
+ args = [rest, original, skip, stack, key_decode, string_decode, acc, 0]
499
+ [clause] =
500
+ quote location: :keep do
501
+ unquote(int) -> string(unquote_splicing(args))
502
+ end
503
+ clause
504
+ end
505
+
506
+ defp escapeu_first_clause(int, first, last, rest, original, skip, stack, key_decode, string_decode, acc)
507
+ when first <= 0xFF do
508
+ skip = quote do: (unquote(skip) + 6)
509
+ acc =
510
+ quote bind_quoted: [acc: acc, first: first, last: last] do
511
+ # 1110xxxx 10xxxx?? 10??????
512
+ byte1 = (0b1110 <<< 4) + (first >>> 4)
513
+ byte2 = ((0b10 <<< 6) + ((first &&& 0b1111) <<< 2)) + (last >>> 6)
514
+ byte3 = (0b10 <<< 6) + (last &&& 0b111111)
515
+ [acc, byte1, byte2, byte3]
516
+ end
517
+ args = [rest, original, skip, stack, key_decode, string_decode, acc, 0]
518
+ [clause] =
519
+ quote location: :keep do
520
+ unquote(int) -> string(unquote_splicing(args))
521
+ end
522
+ clause
523
+ end
524
+
525
+ defmacro escapeu_last(int, original, skip) do
526
+ clauses = escapeu_last_clauses()
527
+ quote location: :keep do
528
+ case unquote(int) do
529
+ unquote(clauses ++ token_error_clause(original, skip, 6))
530
+ end
531
+ end
532
+ end
533
+
534
+ defp escapeu_last_clauses() do
535
+ for {int, last} <- unicode_escapes() do
536
+ [clause] =
537
+ quote do
538
+ unquote(int) -> unquote(last)
539
+ end
540
+ clause
541
+ end
542
+ end
543
+
544
+ defmacro escapeu_surrogate(int, last, rest, original, skip, stack, key_decode, string_decode, acc,
545
+ hi) do
546
+ clauses = escapeu_surrogate_clauses(last, rest, original, skip, stack, key_decode, string_decode, acc, hi)
547
+ quote location: :keep do
548
+ case unquote(int) do
549
+ unquote(clauses ++ token_error_clause(original, skip, 12))
550
+ end
551
+ end
552
+ end
553
+
554
+ defp escapeu_surrogate_clauses(last, rest, original, skip, stack, key_decode, string_decode, acc, hi) do
555
+ digits1 = 'Dd'
556
+ digits2 = Stream.concat([?C..?F, ?c..?f])
557
+ for {int, first} <- unicode_escapes(digits1, digits2) do
558
+ escapeu_surrogate_clause(int, first, last, rest, original, skip, stack, key_decode, string_decode, acc, hi)
559
+ end
560
+ end
561
+
562
+ defp escapeu_surrogate_clause(int, first, last, rest, original, skip, stack, key_decode, string_decode, acc, hi) do
563
+ skip = quote do: unquote(skip) + 12
564
+ acc =
565
+ quote bind_quoted: [acc: acc, first: first, last: last, hi: hi] do
566
+ lo = ((first &&& 0x03) <<< 8) + last
567
+ [acc | <<(hi + lo)::utf8>>]
568
+ end
569
+ args = [rest, original, skip, stack, key_decode, string_decode, acc, 0]
570
+ [clause] =
571
+ quote do
572
+ unquote(int) ->
573
+ string(unquote_splicing(args))
574
+ end
575
+ clause
576
+ end
577
+ end
578
+
579
+ defp escapeu(<<int1::16, int2::16, rest::bits>>, original, skip, stack, key_decode, string_decode, acc) do
580
+ require Unescape
581
+ last = escapeu_last(int2, original, skip)
582
+ Unescape.escapeu_first(int1, last, rest, original, skip, stack, key_decode, string_decode, acc)
583
+ end
584
+ defp escapeu(<<_rest::bits>>, original, skip, _stack, _key_decode, _string_decode, _acc) do
585
+ empty_error(original, skip)
586
+ end
587
+
588
+ # @compile {:inline, escapeu_last: 3}
589
+
590
+ defp escapeu_last(int, original, skip) do
591
+ require Unescape
592
+ Unescape.escapeu_last(int, original, skip)
593
+ end
594
+
595
+ defp escape_surrogate(<<?\\, ?u, int1::16, int2::16, rest::bits>>, original,
596
+ skip, stack, key_decode, string_decode, acc, hi) do
597
+ require Unescape
598
+ last = escapeu_last(int2, original, skip + 6)
599
+ Unescape.escapeu_surrogate(int1, last, rest, original, skip, stack, key_decode, string_decode, acc, hi)
600
+ end
601
+ defp escape_surrogate(<<_rest::bits>>, original, skip, _stack, _key_decode, _string_decode, _acc, _hi) do
602
+ error(original, skip + 6)
603
+ end
604
+
605
+ defp try_parse_float(string, token, skip) do
606
+ :erlang.binary_to_float(string)
607
+ catch
608
+ :error, :badarg ->
609
+ token_error(token, skip)
610
+ end
611
+
612
+ defp error(<<_rest::bits>>, _original, skip, _stack, _key_decode, _string_decode) do
613
+ throw {:position, skip - 1}
614
+ end
615
+
616
+ defp empty_error(_original, skip) do
617
+ throw {:position, skip}
618
+ end
619
+
620
+ @compile {:inline, error: 2, token_error: 2, token_error: 3}
621
+ defp error(_original, skip) do
622
+ throw {:position, skip}
623
+ end
624
+
625
+ defp token_error(token, position) do
626
+ throw {:token, token, position}
627
+ end
628
+
629
+ defp token_error(token, position, len) do
630
+ throw {:token, binary_part(token, position, len), position}
631
+ end
632
+
633
+ @compile {:inline, continue: 7}
634
+ defp continue(rest, original, skip, stack, key_decode, string_decode, value) do
635
+ case stack do
636
+ [@terminate | stack] ->
637
+ terminate(rest, original, skip, stack, key_decode, string_decode, value)
638
+ [@array | stack] ->
639
+ array(rest, original, skip, stack, key_decode, string_decode, value)
640
+ [@key | stack] ->
641
+ key(rest, original, skip, stack, key_decode, string_decode, value)
642
+ [@object | stack] ->
643
+ object(rest, original, skip, stack, key_decode, string_decode, value)
644
+ end
645
+ end
646
+
647
+ defp terminate(<<byte, rest::bits>>, original, skip, stack, key_decode, string_decode, value)
648
+ when byte in '\s\n\r\t' do
649
+ terminate(rest, original, skip + 1, stack, key_decode, string_decode, value)
650
+ end
651
+ defp terminate(<<>>, _original, _skip, _stack, _key_decode, _string_decode, value) do
652
+ value
653
+ end
654
+ defp terminate(<<_rest::bits>>, original, skip, _stack, _key_decode, _string_decode, _value) do
655
+ error(original, skip)
656
+ end
657
+ end