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 +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
|