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 +4 -4
- data/helpers/install-dir/hex/deps/jason/.fetch +0 -0
- data/helpers/install-dir/hex/deps/jason/.hex +0 -0
- data/helpers/install-dir/hex/deps/jason/CHANGELOG.md +88 -0
- data/helpers/install-dir/hex/deps/jason/LICENSE +13 -0
- data/helpers/install-dir/hex/deps/jason/README.md +141 -0
- data/helpers/install-dir/hex/deps/jason/hex_metadata.config +20 -0
- data/helpers/install-dir/hex/deps/jason/lib/codegen.ex +138 -0
- data/helpers/install-dir/hex/deps/jason/lib/decoder.ex +657 -0
- data/helpers/install-dir/hex/deps/jason/lib/encode.ex +634 -0
- data/helpers/install-dir/hex/deps/jason/lib/encoder.ex +216 -0
- data/helpers/install-dir/hex/deps/jason/lib/formatter.ex +253 -0
- data/helpers/install-dir/hex/deps/jason/lib/fragment.ex +11 -0
- data/helpers/install-dir/hex/deps/jason/lib/helpers.ex +98 -0
- data/helpers/install-dir/hex/deps/jason/lib/jason.ex +228 -0
- data/helpers/install-dir/hex/deps/jason/mix.exs +76 -0
- data/helpers/install-dir/hex/lib/check_update.exs +92 -0
- data/helpers/install-dir/hex/lib/do_update.exs +39 -0
- data/helpers/install-dir/hex/lib/parse_deps.exs +104 -0
- data/helpers/install-dir/hex/lib/run.exs +76 -0
- data/helpers/install-dir/hex/mix.exs +21 -0
- data/helpers/install-dir/hex/mix.lock +3 -0
- metadata +29 -8
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3604a148074d38433c48025d48dbc44da12cd64132cfd55372445b47dd830864
|
4
|
+
data.tar.gz: db60087432ed4e23f7edc0014348bb47175ca81fa5cdd1b1811936c747c0f260
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4cc9b3bafe4314ea6147a566e4c99ba1f5ef9d53d8428fa50cf0bdd70736dc996b7c3e16d4e7c5869224504632d850ebe69f85fa4037a4b961633d9b8a2b5f4d
|
7
|
+
data.tar.gz: 549c1b9f72347c16f9bfbde5137e1ddf92a163283f0c016ea331867efa41e0ec64a4669074a6c4fd872235063b8621958b68980c3d7686a0d7ca68a8e4b7fa2c
|
File without changes
|
Binary file
|
@@ -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
|