dependabot-hex 0.118.13 → 0.119.0.beta1

Sign up to get free protection for your applications and to get access to all the features.
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