Merge branch 'develop'
This commit is contained in:
commit
0acec78044
19 changed files with 1312 additions and 860 deletions
|
@ -6,7 +6,7 @@ copyright 2011, 2012 alisdair sullivan
|
||||||
|
|
||||||
jsx is released under the terms of the [MIT][MIT] license
|
jsx is released under the terms of the [MIT][MIT] license
|
||||||
|
|
||||||
jsx uses [rebar][rebar] for it's build chain and [meck][meck] for it's test suite
|
jsx uses [rebar][rebar] for it's build chain
|
||||||
|
|
||||||
[](http://travis-ci.org/talentdeficit/jsx)
|
[](http://travis-ci.org/talentdeficit/jsx)
|
||||||
|
|
||||||
|
@ -60,7 +60,7 @@ to minify a json string: `jsx:format(JSON)`
|
||||||
|
|
||||||
json must be a binary encoded in `utf8`. if it's invalid `utf8` or invalid json, it probably won't parse without errors. there are a few non-standard extensions to the parser available that may change that, they are detailed in the options section below
|
json must be a binary encoded in `utf8`. if it's invalid `utf8` or invalid json, it probably won't parse without errors. there are a few non-standard extensions to the parser available that may change that, they are detailed in the options section below
|
||||||
|
|
||||||
jsx also supports json fragments; valid json values that are not complete json. that means jsx will parse things like `<<"1">`, `<<"true">>` and `<<"\"hello world\"">>` without problems
|
jsx also supports json fragments; valid json values that are not complete json. that means jsx will parse things like `<<"1">>`, `<<"true">>` and `<<"\"hello world\"">>` without complaint
|
||||||
|
|
||||||
#### erlang ####
|
#### erlang ####
|
||||||
|
|
||||||
|
@ -74,7 +74,7 @@ when converting from erlang to json, numbers are represented with their shortest
|
||||||
|
|
||||||
#### strings ####
|
#### strings ####
|
||||||
|
|
||||||
the [json spec][rfc4627] is frustratingly vague on the exact details of json strings. json must be unicode, but no encoding is specified. javascript explicitly allows strings containing codepoints explicitly disallowed by unicode. json allows implementations to set limits on the content of strings and other implementations attempt to resolve this in various ways. this implementation, in default operation, only accepts strings that meet the constraints set out in the json spec (properly escaped control characters, `"` and the escape character, `\`) and that are encoded in `utf8`
|
the [json spec][rfc4627] is frustratingly vague on the exact details of json strings. json must be unicode, but no encoding is specified. javascript explicitly allows strings containing codepoints explicitly disallowed by unicode. json allows implementations to set limits on the content of strings and other implementations attempt to resolve this in various ways. this implementation, in default operation, only accepts strings that meet the constraints set out in the json spec (strings are sequences of unicode codepoints deliminated by `"` (`u+0022`) that may not contain control codes unless properly escaped with `\` (`u+005c`)) and that are encoded in `utf8`
|
||||||
|
|
||||||
the utf8 restriction means improperly paired surrogates are explicitly disallowed. `u+d800` to `u+dfff` are allowed, but only when they form valid surrogate pairs. surrogates that appear otherwise are an error
|
the utf8 restriction means improperly paired surrogates are explicitly disallowed. `u+d800` to `u+dfff` are allowed, but only when they form valid surrogate pairs. surrogates that appear otherwise are an error
|
||||||
|
|
||||||
|
@ -82,7 +82,7 @@ json string escapes of the form `\uXXXX` will be converted to their equivalent c
|
||||||
|
|
||||||
in the interests of pragmatism, there is an option for looser parsing, see options below
|
in the interests of pragmatism, there is an option for looser parsing, see options below
|
||||||
|
|
||||||
all erlang strings are represented by *valid* `utf8` encoded binaries. the encoder will check strings for conformance. the same restrictions apply as for strings encountered within json texts. that means no unpaired surrogates
|
all erlang strings are represented by *valid* `utf8` encoded binaries. the encoder will check strings for conformance. noncharacters (like `u+ffff`) are allowed in erlang utf8 encoded binaries, but not in strings passed to the encoder (although see options below)
|
||||||
|
|
||||||
this implementation performs no normalization on strings beyond that detailed here. be careful when comparing strings as equivalent strings may have different `utf8` encodings
|
this implementation performs no normalization on strings beyond that detailed here. be careful when comparing strings as equivalent strings may have different `utf8` encodings
|
||||||
|
|
||||||
|
@ -101,47 +101,57 @@ json objects are represented by erlang proplists. the empty object has the speci
|
||||||
|
|
||||||
### <a name="options">options</a> ###
|
### <a name="options">options</a> ###
|
||||||
|
|
||||||
jsx functions all take a common set of options. not all flags have meaning in all contexts, but they are always valid options. flags are always atoms and have no value. functions may have additional options beyond these, see individual function documentation for details
|
jsx functions all take a common set of options. not all flags have meaning in all contexts, but they are always valid options. flags are always atoms or {atom, Term} tuples. functions may have additional options beyond these, see individual function documentation for details
|
||||||
|
|
||||||
#### `loose_unicode` ####
|
#### `replaced_bad_utf8` ####
|
||||||
|
|
||||||
json text input and json strings SHOULD be utf8 encoded binaries, appropriately escaped as per the json spec. if this option is present attempts are made to replace invalid codepoints with `u+FFFD` as per the unicode spec. this applies both to malformed unicode and disallowed codepoints
|
json text input and json strings SHOULD be utf8 encoded binaries, appropriately escaped as per the json spec. if this option is present attempts are made to replace invalid codepoints with `u+FFFD` as per the unicode spec. this applies both to malformed unicode and disallowed codepoints
|
||||||
|
|
||||||
#### `escape_forward_slash` ####
|
#### `escaped_forward_slashes` ####
|
||||||
|
|
||||||
json strings are escaped according to the json spec. this means forward slashes are never escaped. unfortunately, a microsoft implementation of json uses escaped forward slashes in json formatted date strings. without this option it is impossible to get date strings that some microsoft tools understand
|
json strings are escaped according to the json spec. this means forward slashes are never escaped. unfortunately, a microsoft implementation of json uses escaped forward slashes in json formatted date strings. without this option it is impossible to get date strings that some microsoft tools understand
|
||||||
|
|
||||||
#### `explicit_end` ####
|
#### `single_quoted_strings` ####
|
||||||
|
|
||||||
this option treats all exhausted inputs as incomplete, as explained below. the parser will not attempt to return a final state until the function is called with the value `end_stream`
|
|
||||||
|
|
||||||
#### `single_quotes` ####
|
|
||||||
|
|
||||||
some parsers allow double quotes (`u+0022`) to be replaced by single quotes (`u+0027`) to deliminate keys and strings. this option allows json containing single quotes as structural (deliminator) characters to be parsed without errors. note that the parser expects strings to be terminated by the same quote type that opened it and that single quotes must, obviously, be escaped within strings deliminated by single quotes. the parser will never emit json with keys or strings deliminated by single quotes
|
some parsers allow double quotes (`u+0022`) to be replaced by single quotes (`u+0027`) to deliminate keys and strings. this option allows json containing single quotes as structural (deliminator) characters to be parsed without errors. note that the parser expects strings to be terminated by the same quote type that opened it and that single quotes must, obviously, be escaped within strings deliminated by single quotes
|
||||||
|
|
||||||
#### `no_jsonp_escapes` ####
|
double quotes must ALWAYS be escaped, regardless of what kind of quotes deliminate the string they are found in
|
||||||
|
|
||||||
javascript interpreters treat the codepoints `u+2028` and `u+2029` as significant whitespace. json strings that contain either of these codepoints will be parsed incorrectly by some javascript interpreters. by default, these codepoints are escaped (to `"\u2028"` and `\u2029`, respectively) to retain compatibility. this option simply removes that escaping if, for some reason, you object to this
|
the parser will never emit json with keys or strings deliminated by single quotes
|
||||||
|
|
||||||
|
#### `unescaped_jsonp` ####
|
||||||
|
|
||||||
|
javascript interpreters treat the codepoints `u+2028` and `u+2029` as significant whitespace. json strings that contain either of these codepoints will be parsed incorrectly by some javascript interpreters. by default, these codepoints are escaped (to `\u2028` and `\u2029`, respectively) to retain compatibility. this option simply removes that escaping if, for some reason, you object to this
|
||||||
|
|
||||||
#### `comments` ####
|
#### `comments` ####
|
||||||
|
|
||||||
json has no official comments but some parsers allow c style comments. this flag allows comments (both `// ...` and `/* ... */` style) anywhere whitespace is allowed
|
json has no official comments but some parsers allow c style comments. this flag allows comments (both `// ...` and `/* ... */` style) anywhere whitespace is allowed
|
||||||
|
|
||||||
#### `json_escape` ####
|
#### `escaped_strings` ####
|
||||||
|
|
||||||
by default, both the encoder and decoder return strings as utf8 binaries appropriate for use in erlang. escape sequences that were present in decoded terms are converted into the appropriate codepoint and encoded terms are unaltered. this flag escapes strings for output in json, removing control codes and replacing them with the appropriate escapes
|
by default, both the encoder and decoder return strings as utf8 binaries appropriate for use in erlang. escape sequences that were present in decoded terms are converted into the appropriate codepoint and encoded terms are unaltered. this flag escapes strings as if for output in json, removing control codes and problematic codepoints and replacing them with the appropriate escapes
|
||||||
|
|
||||||
#### `dirty_strings` ####
|
#### `dirty_strings` ####
|
||||||
|
|
||||||
json escaping is lossy, it mutates the json string and repeated application can result in unwanted behaviour. if your strings are already escaped (or you'd like to force invalid strings into "json") use this flag to bypass escaping
|
json escaping is lossy, it mutates the json string and repeated application can result in unwanted behaviour. if your strings are already escaped (or you'd like to force invalid strings into "json") use this flag to bypass escaping
|
||||||
|
|
||||||
#### `ignore_bad_escapes` ####
|
#### `ignored_bad_escapes` ####
|
||||||
|
|
||||||
during decoding, ignore unrecognized escape sequences and leave them as is in the stream
|
during decoding, ignore unrecognized escape sequences and leave them as is in the stream. note that if you combine this option with `escaped_strings` the escape character itself will be escaped
|
||||||
|
|
||||||
|
#### `explicit_end` ####
|
||||||
|
|
||||||
|
this option treats all exhausted inputs as incomplete, as explained below. the parser will not attempt to return a final state until the function is called with the value `end_stream`
|
||||||
|
|
||||||
#### `relax` ####
|
#### `relax` ####
|
||||||
|
|
||||||
relax is a synonym for `[loose_unicode, single_quotes, comments, ignore_bad_escapes]`
|
relax is a synonym for `[replaced_bad_utf8, single_quoted_strings, comments, ignored_bad_escapes]` for when you don't care how janky and awful your json input is, you just want the parser to do the best it can
|
||||||
|
|
||||||
|
#### `{pre_encode, F}` ####
|
||||||
|
|
||||||
|
`F` is a function of arity 1 that pre-process input to the encoder. only input evaluated in a *value* context is pre-processed in this manner (so keys are not pre-processed, but objects and arrays are). if more than one pre encoder is declared, a `badarg` exception will occur
|
||||||
|
|
||||||
|
input can be any term, but output from the function must be a valid type for input
|
||||||
|
|
||||||
|
|
||||||
### <a name="incompletes">incomplete input</a> ###
|
### <a name="incompletes">incomplete input</a> ###
|
||||||
|
@ -229,9 +239,20 @@ types:
|
||||||
* `binary`
|
* `binary`
|
||||||
* `atom`
|
* `atom`
|
||||||
* `existing_atom`
|
* `existing_atom`
|
||||||
|
- `{post_decode, F}`
|
||||||
|
|
||||||
the option `labels` controls how keys are converted from json to erlang terms. `binary` does no conversion beyond normal escaping. `atom` converts keys to erlang atoms, and results in a badarg error if keys fall outside the range of erlang atoms. `existing_atom` is identical to `atom`, except it will not add new atoms to the atom table
|
the option `labels` controls how keys are converted from json to erlang terms. `binary` does no conversion beyond normal escaping. `atom` converts keys to erlang atoms, and results in a badarg error if keys fall outside the range of erlang atoms. `existing_atom` is identical to `atom`, except it will not add new atoms to the atom table
|
||||||
|
|
||||||
|
`{post_decode, F}` is a user defined function of arity 1 that is called on each output value (objects, arrays, strings, numbers and literals). it may return any value to be substituted in the returned term. for example:
|
||||||
|
|
||||||
|
```erlang
|
||||||
|
1> F = fun(V) when is_list(V) -> V; (V) -> false end.
|
||||||
|
2> jsx:to_term(<<"{\"a list\": [true, \"a string\", 1]}">>, [{post_decode, F}]).
|
||||||
|
[{<<"a list">>, [false, false, false]}]
|
||||||
|
```
|
||||||
|
|
||||||
|
if more than one decoder is declared a badarg exception will result
|
||||||
|
|
||||||
#### converting erlang terms to json ####
|
#### converting erlang terms to json ####
|
||||||
|
|
||||||
`to_json` parses an erlang term and produces a JSON text (see json <-> erlang mapping details below)
|
`to_json` parses an erlang term and produces a JSON text (see json <-> erlang mapping details below)
|
||||||
|
|
185
bin/jsx_bench.escript
Executable file
185
bin/jsx_bench.escript
Executable file
|
@ -0,0 +1,185 @@
|
||||||
|
#!/usr/bin/env escript
|
||||||
|
|
||||||
|
%% The MIT License
|
||||||
|
|
||||||
|
%% Copyright (c) 2012 Alisdair Sullivan <alisdairsullivan@yahoo.ca>
|
||||||
|
|
||||||
|
%% Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
%% of this software and associated documentation files (the "Software"), to deal
|
||||||
|
%% in the Software without restriction, including without limitation the rights
|
||||||
|
%% to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
%% copies of the Software, and to permit persons to whom the Software is
|
||||||
|
%% furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
%% The above copyright notice and this permission notice shall be included in
|
||||||
|
%% all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
%% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
%% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
%% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
%% AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
%% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
%% OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
%% THE SOFTWARE.
|
||||||
|
|
||||||
|
-mode(compile).
|
||||||
|
|
||||||
|
|
||||||
|
-define(averageN(Test, N),
|
||||||
|
{average, {repeat, N, Test}}
|
||||||
|
).
|
||||||
|
|
||||||
|
|
||||||
|
main([]) ->
|
||||||
|
%% preload jsx mods
|
||||||
|
jsx:to_term(<<"{}">>),
|
||||||
|
format(frequency:profile({"empty object to term", ?averageN({jsx, to_term, [<<"{}">>]}, 1000)})),
|
||||||
|
format(frequency:profile({"empty object to json", ?averageN({jsx, to_json, [[{}]]}, 1000)})),
|
||||||
|
format(frequency:profile({"empty array to term", ?averageN({jsx, to_term, [<<"[]">>]}, 1000)})),
|
||||||
|
format(frequency:profile({"empty array to json", ?averageN({jsx, to_json, [[]]}, 1000)})),
|
||||||
|
format(frequency:profile({"sample tweet to term", ?averageN({jsx, to_term, [sample_tweet()]}, 1000)})),
|
||||||
|
format(frequency:profile({"sample tweet to json", ?averageN({jsx, to_json, [jsx:to_term(sample_tweet())]}, 1000)})),
|
||||||
|
format(frequency:profile({"sample github user to term", ?averageN({jsx, to_term, [sample_github_user()]}, 1000)})),
|
||||||
|
format(frequency:profile({"sample github user to json", ?averageN({jsx, to_json, [jsx:to_term(sample_github_user())]}, 1000)})).
|
||||||
|
|
||||||
|
|
||||||
|
format([]) -> ok;
|
||||||
|
format([{name, Name}|Rest]) ->
|
||||||
|
io:format("name : ~p~n", [Name]),
|
||||||
|
format(Rest);
|
||||||
|
format([{time, Time}|Rest]) ->
|
||||||
|
io:format("time : ~p~n", [Time]),
|
||||||
|
format(Rest);
|
||||||
|
format([{error, Error}|Rest]) ->
|
||||||
|
io:format("error : ~p~n", [Error]),
|
||||||
|
format(Rest);
|
||||||
|
format([Result|Rest]) when is_list(Result) -> format(Result), format(Rest);
|
||||||
|
format([_|Rest]) -> format(Rest).
|
||||||
|
|
||||||
|
|
||||||
|
sample_tweet() ->
|
||||||
|
<<"{
|
||||||
|
\"coordinates\": null,
|
||||||
|
\"created_at\": \"Sat Sep 10 22:23:38 +0000 2011\",
|
||||||
|
\"truncated\": false,
|
||||||
|
\"favorited\": false,
|
||||||
|
\"id_str\": \"112652479837110273\",
|
||||||
|
\"entities\": {
|
||||||
|
\"urls\": [{
|
||||||
|
\"expanded_url\": \"http://instagr.am/p/MuW67/\",
|
||||||
|
\"url\": \"http://t.co/6J2EgYM\",
|
||||||
|
\"indices\": [67, 86],
|
||||||
|
\"display_url\": \"instagr.am/p/MuW67/\"
|
||||||
|
}],
|
||||||
|
\"hashtags\": [{
|
||||||
|
\"text\": \"tcdisrupt\",
|
||||||
|
\"indices\": [32,42]
|
||||||
|
}],
|
||||||
|
\"user_mentions\": [
|
||||||
|
{
|
||||||
|
\"name\": \"Twitter\",
|
||||||
|
\"id_str\": \"783214\",
|
||||||
|
\"id\": 783214,
|
||||||
|
\"indices\": [0, 8],
|
||||||
|
\"screen_name\": \"twitter\"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
\"name\": \"Picture.ly\",
|
||||||
|
\"id_str\": \"334715534\",
|
||||||
|
\"id\": 334715534,
|
||||||
|
\"indices\": [15, 28],
|
||||||
|
\"screen_name\": \"SeePicturely\"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
\"name\": \"Bosco So\",
|
||||||
|
\"id_str\": \"14792670\",
|
||||||
|
\"id\": 14792670,
|
||||||
|
\"indices\": [46, 58],
|
||||||
|
\"screen_name\": \"boscomonkey\"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
\"name\": \"Taylor Singletary\",
|
||||||
|
\"id_str\": \"819797\",
|
||||||
|
\"id\": 819797,
|
||||||
|
\"indices\": [59, 66],
|
||||||
|
\"screen_name\": \"episod\"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
\"in_reply_to_user_id_str\": \"783214\",
|
||||||
|
\"text\": \"@twitter meets @seepicturely at #tcdisrupt cc.@boscomonkey @episod http://t.co/6J2EgYM\",
|
||||||
|
\"contributors\": null,
|
||||||
|
\"id\": 112652479837110273,
|
||||||
|
\"retweet_count\": 0,
|
||||||
|
\"in_reply_to_status_id_str\": null,
|
||||||
|
\"geo\": null,
|
||||||
|
\"retweeted\": false,
|
||||||
|
\"possibly_sensitive\": false,
|
||||||
|
\"in_reply_to_user_id\": 783214,
|
||||||
|
\"place\": null,
|
||||||
|
\"source\": \"<a href=\\\"http://instagr.am\\\" rel=\\\"nofollow\\\">Instagram</a>\",
|
||||||
|
\"user\": {
|
||||||
|
\"profile_sidebar_border_color\": \"eeeeee\",
|
||||||
|
\"profile_background_tile\": true,
|
||||||
|
\"profile_sidebar_fill_color\": \"efefef\",
|
||||||
|
\"name\": \"Eoin McMillan \",
|
||||||
|
\"profile_image_url\": \"http://a1.twimg.com/profile_images/1380912173/Screen_shot_2011-06-03_at_7.35.36_PM_normal.png\",
|
||||||
|
\"created_at\": \"Mon May 16 20:07:59 +0000 2011\",
|
||||||
|
\"location\": \"Twitter\",
|
||||||
|
\"profile_link_color\": \"009999\",
|
||||||
|
\"follow_request_sent\": null,
|
||||||
|
\"is_translator\": false,
|
||||||
|
\"id_str\": \"299862462\",
|
||||||
|
\"favourites_count\": 0,
|
||||||
|
\"default_profile\": false,
|
||||||
|
\"url\": \"http://www.eoin.me\",
|
||||||
|
\"contributors_enabled\": false,
|
||||||
|
\"id\": 299862462,
|
||||||
|
\"utc_offset\": null,
|
||||||
|
\"profile_image_url_https\": \"https://si0.twimg.com/profile_images/1380912173/Screen_shot_2011-06-03_at_7.35.36_PM_normal.png\",
|
||||||
|
\"profile_use_background_image\": true,
|
||||||
|
\"listed_count\": 0,
|
||||||
|
\"followers_count\": 9,
|
||||||
|
\"lang\": \"en\",
|
||||||
|
\"profile_text_color\": \"333333\",
|
||||||
|
\"protected\": false,
|
||||||
|
\"profile_background_image_url_https\": \"https://si0.twimg.com/images/themes/theme14/bg.gif\",
|
||||||
|
\"description\": \"Eoin's photography account. See @mceoin for tweets.\",
|
||||||
|
\"geo_enabled\": false,
|
||||||
|
\"verified\": false,
|
||||||
|
\"profile_background_color\": \"131516\",
|
||||||
|
\"time_zone\": null,
|
||||||
|
\"notifications\": null,
|
||||||
|
\"statuses_count\": 255,
|
||||||
|
\"friends_count\": 0,
|
||||||
|
\"default_profile_image\": false,
|
||||||
|
\"profile_background_image_url\": \"http://a1.twimg.com/images/themes/theme14/bg.gif\",
|
||||||
|
\"screen_name\": \"imeoin\",
|
||||||
|
\"following\": null,
|
||||||
|
\"show_all_inline_media\": false
|
||||||
|
},
|
||||||
|
\"in_reply_to_screen_name\": \"twitter\",
|
||||||
|
\"in_reply_to_status_id\": null
|
||||||
|
}">>.
|
||||||
|
|
||||||
|
|
||||||
|
sample_github_user() ->
|
||||||
|
<<"{
|
||||||
|
\"user\": {
|
||||||
|
\"gravatar_id\": \"b8dbb1987e8e5318584865f880036796\",
|
||||||
|
\"company\": \"GitHub\",
|
||||||
|
\"name\": \"Chris Wanstrath\",
|
||||||
|
\"created_at\": \"2007/10/19 22:24:19 -0700\",
|
||||||
|
\"location\": \"San Francisco, CA\",
|
||||||
|
\"public_repo_count\": 98,
|
||||||
|
\"public_gist_count\": 270,
|
||||||
|
\"blog\": \"http://chriswanstrath.com/\",
|
||||||
|
\"following_count\": 196,
|
||||||
|
\"id\": 2,
|
||||||
|
\"type\": \"User\",
|
||||||
|
\"permission\": null,
|
||||||
|
\"followers_count\": 1692,
|
||||||
|
\"login\": \"defunkt\",
|
||||||
|
\"email\": \"chris@wanstrath.com\"
|
||||||
|
}
|
||||||
|
}">>.
|
|
@ -1,4 +1,4 @@
|
||||||
{name, "bad_low_surrogate_replaced"}.
|
{name, "bad_low_surrogate_replaced"}.
|
||||||
{jsx, [start_array,{string, <<16#fffd/utf8, 16#fffd/utf8>>},end_array,end_json]}.
|
{jsx, [start_array,{string, <<16#fffd/utf8, 16#fffd/utf8>>},end_array,end_json]}.
|
||||||
{json, "bad_low_surrogate_replaced.json"}.
|
{json, "bad_low_surrogate_replaced.json"}.
|
||||||
{jsx_flags, [loose_unicode]}.
|
{jsx_flags, [replaced_bad_utf8]}.
|
||||||
|
|
1
priv/test_cases/bom.json
Normal file
1
priv/test_cases/bom.json
Normal file
|
@ -0,0 +1 @@
|
||||||
|
[]
|
3
priv/test_cases/bom.test
Normal file
3
priv/test_cases/bom.test
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
{name, "byte order mark"}.
|
||||||
|
{jsx, [start_array, end_array, end_json]}.
|
||||||
|
{json, "bom.json"}.
|
10
priv/test_cases/comment_style_a.json
Normal file
10
priv/test_cases/comment_style_a.json
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
// comment
|
||||||
|
{ // comment
|
||||||
|
"key" // comment
|
||||||
|
: // comment
|
||||||
|
[ // comment
|
||||||
|
true // comment
|
||||||
|
, // comment
|
||||||
|
false // comment
|
||||||
|
] // comment
|
||||||
|
} // comment
|
4
priv/test_cases/comment_style_a.test
Normal file
4
priv/test_cases/comment_style_a.test
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
{name, "comment_style_a"}.
|
||||||
|
{jsx, [start_object,{key, <<"key">>}, start_array, {literal, true}, {literal, false}, end_array, end_object,end_json]}.
|
||||||
|
{json, "comment_style_a.json"}.
|
||||||
|
{jsx_flags, [comments]}.
|
1
priv/test_cases/comment_style_b.json
Normal file
1
priv/test_cases/comment_style_b.json
Normal file
|
@ -0,0 +1 @@
|
||||||
|
/* comment */ { /* comment */ "key" /* comment */ : /* comment */ [ /* comment */ true /* comment */ , /* comment */ false /* comment */ ] /* comment */ } /* comment */
|
4
priv/test_cases/comment_style_b.test
Normal file
4
priv/test_cases/comment_style_b.test
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
{name, "comment_style_a"}.
|
||||||
|
{jsx, [start_object,{key, <<"key">>}, start_array, {literal, true}, {literal, false}, end_array, end_object,end_json]}.
|
||||||
|
{json, "comment_style_a.json"}.
|
||||||
|
{jsx_flags, [comments]}.
|
|
@ -1,4 +1,4 @@
|
||||||
{name, "unpaired surrogate replaced"}.
|
{name, "unpaired surrogate replaced"}.
|
||||||
{jsx, [start_array,{string,<<65533/utf8,$b,$l,$a,$h>>},end_array,end_json]}.
|
{jsx, [start_array,{string,<<65533/utf8,$b,$l,$a,$h>>},end_array,end_json]}.
|
||||||
{json, "unpaired_surrogate_replaced.json"}.
|
{json, "unpaired_surrogate_replaced.json"}.
|
||||||
{jsx_flags, [loose_unicode]}.
|
{jsx_flags, [replaced_bad_utf8]}.
|
||||||
|
|
13
rebar.config
13
rebar.config
|
@ -28,16 +28,3 @@
|
||||||
{xref_checks, [undefined_function_calls]}.
|
{xref_checks, [undefined_function_calls]}.
|
||||||
|
|
||||||
{cover_enabled, true}.
|
{cover_enabled, true}.
|
||||||
|
|
||||||
{deps, [
|
|
||||||
{
|
|
||||||
'nicedecimal',
|
|
||||||
".*",
|
|
||||||
{git, "git://github.com/talentdeficit/nicedecimal.git", {branch, "master"}}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'meck',
|
|
||||||
".*",
|
|
||||||
{git, "git://github.com/eproxus/meck.git", {branch, "master"}}
|
|
||||||
}
|
|
||||||
]}.
|
|
||||||
|
|
|
@ -14,8 +14,7 @@
|
||||||
{registered, []},
|
{registered, []},
|
||||||
{applications, [
|
{applications, [
|
||||||
kernel,
|
kernel,
|
||||||
stdlib,
|
stdlib
|
||||||
nicedecimal
|
|
||||||
]},
|
]},
|
||||||
{env, []}
|
{env, []}
|
||||||
]}.
|
]}.
|
||||||
|
|
16
src/jsx.erl
16
src/jsx.erl
|
@ -134,41 +134,41 @@ encoder_decoder_equiv_test_() ->
|
||||||
].
|
].
|
||||||
|
|
||||||
|
|
||||||
single_quotes_test_() ->
|
single_quoted_strings_test_() ->
|
||||||
[
|
[
|
||||||
{"single quoted keys",
|
{"single quoted keys",
|
||||||
?_assertEqual(
|
?_assertEqual(
|
||||||
to_term(<<"{'key':true}">>, [single_quotes]),
|
to_term(<<"{'key':true}">>, [single_quoted_strings]),
|
||||||
[{<<"key">>, true}]
|
[{<<"key">>, true}]
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
{"multiple single quoted keys",
|
{"multiple single quoted keys",
|
||||||
?_assertEqual(
|
?_assertEqual(
|
||||||
to_term(<<"{'key':true, 'another key':true}">>, [single_quotes]),
|
to_term(<<"{'key':true, 'another key':true}">>, [single_quoted_strings]),
|
||||||
[{<<"key">>, true}, {<<"another key">>, true}]
|
[{<<"key">>, true}, {<<"another key">>, true}]
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
{"nested single quoted keys",
|
{"nested single quoted keys",
|
||||||
?_assertEqual(
|
?_assertEqual(
|
||||||
to_term(<<"{'key': {'key':true, 'another key':true}}">>, [single_quotes]),
|
to_term(<<"{'key': {'key':true, 'another key':true}}">>, [single_quoted_strings]),
|
||||||
[{<<"key">>, [{<<"key">>, true}, {<<"another key">>, true}]}]
|
[{<<"key">>, [{<<"key">>, true}, {<<"another key">>, true}]}]
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
{"single quoted string",
|
{"single quoted string",
|
||||||
?_assertEqual(
|
?_assertEqual(
|
||||||
to_term(<<"['string']">>, [single_quotes]),
|
to_term(<<"['string']">>, [single_quoted_strings]),
|
||||||
[<<"string">>]
|
[<<"string">>]
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
{"single quote in double quoted string",
|
{"single quote in double quoted string",
|
||||||
?_assertEqual(
|
?_assertEqual(
|
||||||
to_term(<<"[\"a single quote: '\"]">>, [single_quotes]),
|
to_term(<<"[\"a single quote: '\"]">>, [single_quoted_strings]),
|
||||||
[<<"a single quote: '">>]
|
[<<"a single quote: '">>]
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
{"escaped single quote in single quoted string",
|
{"escaped single quote in single quoted string",
|
||||||
?_assertEqual(
|
?_assertEqual(
|
||||||
to_term(<<"['a single quote: \\'']">>, [single_quotes]),
|
to_term(<<"['a single quote: \\'']">>, [single_quoted_strings]),
|
||||||
[<<"a single quote: '">>]
|
[<<"a single quote: '">>]
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
@ -181,7 +181,7 @@ single_quotes_test_() ->
|
||||||
{"mismatched quotes",
|
{"mismatched quotes",
|
||||||
?_assertError(
|
?_assertError(
|
||||||
badarg,
|
badarg,
|
||||||
to_term(<<"['mismatched\"]">>, [single_quotes])
|
to_term(<<"['mismatched\"]">>, [single_quoted_strings])
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
].
|
].
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -49,7 +49,7 @@ encoder(Handler, State, Opts) ->
|
||||||
|
|
||||||
|
|
||||||
start(Term, {Handler, State}, Opts) ->
|
start(Term, {Handler, State}, Opts) ->
|
||||||
Handler:handle_event(end_json, value(Term, {Handler, State}, Opts)).
|
Handler:handle_event(end_json, value(pre_encode(Term, Opts), {Handler, State}, Opts)).
|
||||||
|
|
||||||
|
|
||||||
value(String, {Handler, State}, Opts) when is_binary(String) ->
|
value(String, {Handler, State}, Opts) when is_binary(String) ->
|
||||||
|
@ -82,7 +82,7 @@ object([{Key, Value}|Rest], {Handler, State}, Opts) ->
|
||||||
{
|
{
|
||||||
Handler,
|
Handler,
|
||||||
value(
|
value(
|
||||||
Value,
|
pre_encode(Value, Opts),
|
||||||
{Handler, Handler:handle_event({key, clean_string(fix_key(Key), Opts)}, State)},
|
{Handler, Handler:handle_event({key, clean_string(fix_key(Key), Opts)}, State)},
|
||||||
Opts
|
Opts
|
||||||
)
|
)
|
||||||
|
@ -94,112 +94,408 @@ object(Term, Handler, Opts) -> ?error([Term, Handler, Opts]).
|
||||||
|
|
||||||
|
|
||||||
list([Value|Rest], {Handler, State}, Opts) ->
|
list([Value|Rest], {Handler, State}, Opts) ->
|
||||||
list(Rest, {Handler, value(Value, {Handler, State}, Opts)}, Opts);
|
list(Rest, {Handler, value(pre_encode(Value, Opts), {Handler, State}, Opts)}, Opts);
|
||||||
list([], {Handler, State}, _Opts) -> Handler:handle_event(end_array, State);
|
list([], {Handler, State}, _Opts) -> Handler:handle_event(end_array, State);
|
||||||
list(Term, Handler, Opts) -> ?error([Term, Handler, Opts]).
|
list(Term, Handler, Opts) -> ?error([Term, Handler, Opts]).
|
||||||
|
|
||||||
|
|
||||||
|
pre_encode(Value, #opts{pre_encode=false}) -> Value;
|
||||||
|
pre_encode(Value, Opts) -> (Opts#opts.pre_encode)(Value).
|
||||||
|
|
||||||
|
|
||||||
fix_key(Key) when is_atom(Key) -> fix_key(atom_to_binary(Key, utf8));
|
fix_key(Key) when is_atom(Key) -> fix_key(atom_to_binary(Key, utf8));
|
||||||
fix_key(Key) when is_binary(Key) -> Key.
|
fix_key(Key) when is_binary(Key) -> Key.
|
||||||
|
|
||||||
|
|
||||||
clean_string(Bin, Opts) ->
|
clean_string(Bin, Opts) ->
|
||||||
case Opts#opts.loose_unicode of
|
case Opts#opts.replaced_bad_utf8 orelse Opts#opts.escaped_strings of
|
||||||
true -> maybe_escape(clean_string(Bin, 0, size(Bin), Opts), Opts)
|
true -> clean(Bin, [], Opts)
|
||||||
; false ->
|
; false -> ensure_clean(Bin), Bin
|
||||||
case is_clean(Bin) of
|
|
||||||
true -> maybe_escape(Bin, Opts)
|
|
||||||
; false -> erlang:error(badarg, [Bin, Opts])
|
|
||||||
end
|
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
|
||||||
maybe_escape(Bin, Opts=#opts{json_escape=true}) -> jsx_utils:json_escape(Bin, Opts);
|
%% fast path for no escaping and no correcting, throws error if string is 'bad'
|
||||||
maybe_escape(Bin, _) -> Bin.
|
ensure_clean(<<>>) -> ok;
|
||||||
|
ensure_clean(<<0, Rest/binary>>) -> ensure_clean(Rest);
|
||||||
|
ensure_clean(<<1, Rest/binary>>) -> ensure_clean(Rest);
|
||||||
|
ensure_clean(<<2, Rest/binary>>) -> ensure_clean(Rest);
|
||||||
|
ensure_clean(<<3, Rest/binary>>) -> ensure_clean(Rest);
|
||||||
|
ensure_clean(<<4, Rest/binary>>) -> ensure_clean(Rest);
|
||||||
|
ensure_clean(<<5, Rest/binary>>) -> ensure_clean(Rest);
|
||||||
|
ensure_clean(<<6, Rest/binary>>) -> ensure_clean(Rest);
|
||||||
|
ensure_clean(<<7, Rest/binary>>) -> ensure_clean(Rest);
|
||||||
|
ensure_clean(<<8, Rest/binary>>) -> ensure_clean(Rest);
|
||||||
|
ensure_clean(<<9, Rest/binary>>) -> ensure_clean(Rest);
|
||||||
|
ensure_clean(<<10, Rest/binary>>) -> ensure_clean(Rest);
|
||||||
|
ensure_clean(<<11, Rest/binary>>) -> ensure_clean(Rest);
|
||||||
|
ensure_clean(<<12, Rest/binary>>) -> ensure_clean(Rest);
|
||||||
|
ensure_clean(<<13, Rest/binary>>) -> ensure_clean(Rest);
|
||||||
|
ensure_clean(<<14, Rest/binary>>) -> ensure_clean(Rest);
|
||||||
|
ensure_clean(<<15, Rest/binary>>) -> ensure_clean(Rest);
|
||||||
|
ensure_clean(<<16, Rest/binary>>) -> ensure_clean(Rest);
|
||||||
|
ensure_clean(<<17, Rest/binary>>) -> ensure_clean(Rest);
|
||||||
|
ensure_clean(<<18, Rest/binary>>) -> ensure_clean(Rest);
|
||||||
|
ensure_clean(<<19, Rest/binary>>) -> ensure_clean(Rest);
|
||||||
|
ensure_clean(<<20, Rest/binary>>) -> ensure_clean(Rest);
|
||||||
|
ensure_clean(<<21, Rest/binary>>) -> ensure_clean(Rest);
|
||||||
|
ensure_clean(<<22, Rest/binary>>) -> ensure_clean(Rest);
|
||||||
|
ensure_clean(<<23, Rest/binary>>) -> ensure_clean(Rest);
|
||||||
|
ensure_clean(<<24, Rest/binary>>) -> ensure_clean(Rest);
|
||||||
|
ensure_clean(<<25, Rest/binary>>) -> ensure_clean(Rest);
|
||||||
|
ensure_clean(<<26, Rest/binary>>) -> ensure_clean(Rest);
|
||||||
|
ensure_clean(<<27, Rest/binary>>) -> ensure_clean(Rest);
|
||||||
|
ensure_clean(<<28, Rest/binary>>) -> ensure_clean(Rest);
|
||||||
|
ensure_clean(<<29, Rest/binary>>) -> ensure_clean(Rest);
|
||||||
|
ensure_clean(<<30, Rest/binary>>) -> ensure_clean(Rest);
|
||||||
|
ensure_clean(<<31, Rest/binary>>) -> ensure_clean(Rest);
|
||||||
|
ensure_clean(<<32, Rest/binary>>) -> ensure_clean(Rest);
|
||||||
|
ensure_clean(<<33, Rest/binary>>) -> ensure_clean(Rest);
|
||||||
|
ensure_clean(<<34, Rest/binary>>) -> ensure_clean(Rest);
|
||||||
|
ensure_clean(<<35, Rest/binary>>) -> ensure_clean(Rest);
|
||||||
|
ensure_clean(<<36, Rest/binary>>) -> ensure_clean(Rest);
|
||||||
|
ensure_clean(<<37, Rest/binary>>) -> ensure_clean(Rest);
|
||||||
|
ensure_clean(<<38, Rest/binary>>) -> ensure_clean(Rest);
|
||||||
|
ensure_clean(<<39, Rest/binary>>) -> ensure_clean(Rest);
|
||||||
|
ensure_clean(<<40, Rest/binary>>) -> ensure_clean(Rest);
|
||||||
|
ensure_clean(<<41, Rest/binary>>) -> ensure_clean(Rest);
|
||||||
|
ensure_clean(<<42, Rest/binary>>) -> ensure_clean(Rest);
|
||||||
|
ensure_clean(<<43, Rest/binary>>) -> ensure_clean(Rest);
|
||||||
|
ensure_clean(<<44, Rest/binary>>) -> ensure_clean(Rest);
|
||||||
|
ensure_clean(<<45, Rest/binary>>) -> ensure_clean(Rest);
|
||||||
|
ensure_clean(<<46, Rest/binary>>) -> ensure_clean(Rest);
|
||||||
|
ensure_clean(<<47, Rest/binary>>) -> ensure_clean(Rest);
|
||||||
|
ensure_clean(<<48, Rest/binary>>) -> ensure_clean(Rest);
|
||||||
|
ensure_clean(<<49, Rest/binary>>) -> ensure_clean(Rest);
|
||||||
|
ensure_clean(<<50, Rest/binary>>) -> ensure_clean(Rest);
|
||||||
|
ensure_clean(<<51, Rest/binary>>) -> ensure_clean(Rest);
|
||||||
|
ensure_clean(<<52, Rest/binary>>) -> ensure_clean(Rest);
|
||||||
|
ensure_clean(<<53, Rest/binary>>) -> ensure_clean(Rest);
|
||||||
|
ensure_clean(<<54, Rest/binary>>) -> ensure_clean(Rest);
|
||||||
|
ensure_clean(<<55, Rest/binary>>) -> ensure_clean(Rest);
|
||||||
|
ensure_clean(<<56, Rest/binary>>) -> ensure_clean(Rest);
|
||||||
|
ensure_clean(<<57, Rest/binary>>) -> ensure_clean(Rest);
|
||||||
|
ensure_clean(<<58, Rest/binary>>) -> ensure_clean(Rest);
|
||||||
|
ensure_clean(<<59, Rest/binary>>) -> ensure_clean(Rest);
|
||||||
|
ensure_clean(<<60, Rest/binary>>) -> ensure_clean(Rest);
|
||||||
|
ensure_clean(<<61, Rest/binary>>) -> ensure_clean(Rest);
|
||||||
|
ensure_clean(<<62, Rest/binary>>) -> ensure_clean(Rest);
|
||||||
|
ensure_clean(<<63, Rest/binary>>) -> ensure_clean(Rest);
|
||||||
|
ensure_clean(<<64, Rest/binary>>) -> ensure_clean(Rest);
|
||||||
|
ensure_clean(<<65, Rest/binary>>) -> ensure_clean(Rest);
|
||||||
|
ensure_clean(<<66, Rest/binary>>) -> ensure_clean(Rest);
|
||||||
|
ensure_clean(<<67, Rest/binary>>) -> ensure_clean(Rest);
|
||||||
|
ensure_clean(<<68, Rest/binary>>) -> ensure_clean(Rest);
|
||||||
|
ensure_clean(<<69, Rest/binary>>) -> ensure_clean(Rest);
|
||||||
|
ensure_clean(<<70, Rest/binary>>) -> ensure_clean(Rest);
|
||||||
|
ensure_clean(<<71, Rest/binary>>) -> ensure_clean(Rest);
|
||||||
|
ensure_clean(<<72, Rest/binary>>) -> ensure_clean(Rest);
|
||||||
|
ensure_clean(<<73, Rest/binary>>) -> ensure_clean(Rest);
|
||||||
|
ensure_clean(<<74, Rest/binary>>) -> ensure_clean(Rest);
|
||||||
|
ensure_clean(<<75, Rest/binary>>) -> ensure_clean(Rest);
|
||||||
|
ensure_clean(<<76, Rest/binary>>) -> ensure_clean(Rest);
|
||||||
|
ensure_clean(<<77, Rest/binary>>) -> ensure_clean(Rest);
|
||||||
|
ensure_clean(<<78, Rest/binary>>) -> ensure_clean(Rest);
|
||||||
|
ensure_clean(<<79, Rest/binary>>) -> ensure_clean(Rest);
|
||||||
|
ensure_clean(<<80, Rest/binary>>) -> ensure_clean(Rest);
|
||||||
|
ensure_clean(<<81, Rest/binary>>) -> ensure_clean(Rest);
|
||||||
|
ensure_clean(<<82, Rest/binary>>) -> ensure_clean(Rest);
|
||||||
|
ensure_clean(<<83, Rest/binary>>) -> ensure_clean(Rest);
|
||||||
|
ensure_clean(<<84, Rest/binary>>) -> ensure_clean(Rest);
|
||||||
|
ensure_clean(<<85, Rest/binary>>) -> ensure_clean(Rest);
|
||||||
|
ensure_clean(<<86, Rest/binary>>) -> ensure_clean(Rest);
|
||||||
|
ensure_clean(<<87, Rest/binary>>) -> ensure_clean(Rest);
|
||||||
|
ensure_clean(<<88, Rest/binary>>) -> ensure_clean(Rest);
|
||||||
|
ensure_clean(<<89, Rest/binary>>) -> ensure_clean(Rest);
|
||||||
|
ensure_clean(<<90, Rest/binary>>) -> ensure_clean(Rest);
|
||||||
|
ensure_clean(<<91, Rest/binary>>) -> ensure_clean(Rest);
|
||||||
|
ensure_clean(<<92, Rest/binary>>) -> ensure_clean(Rest);
|
||||||
|
ensure_clean(<<93, Rest/binary>>) -> ensure_clean(Rest);
|
||||||
|
ensure_clean(<<94, Rest/binary>>) -> ensure_clean(Rest);
|
||||||
|
ensure_clean(<<95, Rest/binary>>) -> ensure_clean(Rest);
|
||||||
|
ensure_clean(<<96, Rest/binary>>) -> ensure_clean(Rest);
|
||||||
|
ensure_clean(<<97, Rest/binary>>) -> ensure_clean(Rest);
|
||||||
|
ensure_clean(<<98, Rest/binary>>) -> ensure_clean(Rest);
|
||||||
|
ensure_clean(<<99, Rest/binary>>) -> ensure_clean(Rest);
|
||||||
|
ensure_clean(<<100, Rest/binary>>) -> ensure_clean(Rest);
|
||||||
|
ensure_clean(<<101, Rest/binary>>) -> ensure_clean(Rest);
|
||||||
|
ensure_clean(<<102, Rest/binary>>) -> ensure_clean(Rest);
|
||||||
|
ensure_clean(<<103, Rest/binary>>) -> ensure_clean(Rest);
|
||||||
|
ensure_clean(<<104, Rest/binary>>) -> ensure_clean(Rest);
|
||||||
|
ensure_clean(<<105, Rest/binary>>) -> ensure_clean(Rest);
|
||||||
|
ensure_clean(<<106, Rest/binary>>) -> ensure_clean(Rest);
|
||||||
|
ensure_clean(<<107, Rest/binary>>) -> ensure_clean(Rest);
|
||||||
|
ensure_clean(<<108, Rest/binary>>) -> ensure_clean(Rest);
|
||||||
|
ensure_clean(<<109, Rest/binary>>) -> ensure_clean(Rest);
|
||||||
|
ensure_clean(<<110, Rest/binary>>) -> ensure_clean(Rest);
|
||||||
|
ensure_clean(<<111, Rest/binary>>) -> ensure_clean(Rest);
|
||||||
|
ensure_clean(<<112, Rest/binary>>) -> ensure_clean(Rest);
|
||||||
|
ensure_clean(<<113, Rest/binary>>) -> ensure_clean(Rest);
|
||||||
|
ensure_clean(<<114, Rest/binary>>) -> ensure_clean(Rest);
|
||||||
|
ensure_clean(<<115, Rest/binary>>) -> ensure_clean(Rest);
|
||||||
|
ensure_clean(<<116, Rest/binary>>) -> ensure_clean(Rest);
|
||||||
|
ensure_clean(<<117, Rest/binary>>) -> ensure_clean(Rest);
|
||||||
|
ensure_clean(<<118, Rest/binary>>) -> ensure_clean(Rest);
|
||||||
|
ensure_clean(<<119, Rest/binary>>) -> ensure_clean(Rest);
|
||||||
|
ensure_clean(<<120, Rest/binary>>) -> ensure_clean(Rest);
|
||||||
|
ensure_clean(<<121, Rest/binary>>) -> ensure_clean(Rest);
|
||||||
|
ensure_clean(<<122, Rest/binary>>) -> ensure_clean(Rest);
|
||||||
|
ensure_clean(<<123, Rest/binary>>) -> ensure_clean(Rest);
|
||||||
|
ensure_clean(<<124, Rest/binary>>) -> ensure_clean(Rest);
|
||||||
|
ensure_clean(<<125, Rest/binary>>) -> ensure_clean(Rest);
|
||||||
|
ensure_clean(<<126, Rest/binary>>) -> ensure_clean(Rest);
|
||||||
|
ensure_clean(<<127, Rest/binary>>) -> ensure_clean(Rest);
|
||||||
|
ensure_clean(<<X/utf8, Rest/binary>>) when X < 16#800 -> ensure_clean(Rest);
|
||||||
|
ensure_clean(<<X/utf8, Rest/binary>>) when X < 16#dcff -> ensure_clean(Rest);
|
||||||
|
ensure_clean(<<X/utf8, Rest/binary>>) when X > 16#dfff, X < 16#fdd0 -> ensure_clean(Rest);
|
||||||
|
ensure_clean(<<X/utf8, Rest/binary>>) when X > 16#fdef, X < 16#fffe -> ensure_clean(Rest);
|
||||||
|
ensure_clean(<<X/utf8, Rest/binary>>) when X >= 16#10000, X < 16#1fffe -> ensure_clean(Rest);
|
||||||
|
ensure_clean(<<X/utf8, Rest/binary>>) when X >= 16#20000, X < 16#2fffe -> ensure_clean(Rest);
|
||||||
|
ensure_clean(<<X/utf8, Rest/binary>>) when X >= 16#30000, X < 16#3fffe -> ensure_clean(Rest);
|
||||||
|
ensure_clean(<<X/utf8, Rest/binary>>) when X >= 16#40000, X < 16#4fffe -> ensure_clean(Rest);
|
||||||
|
ensure_clean(<<X/utf8, Rest/binary>>) when X >= 16#50000, X < 16#5fffe -> ensure_clean(Rest);
|
||||||
|
ensure_clean(<<X/utf8, Rest/binary>>) when X >= 16#60000, X < 16#6fffe -> ensure_clean(Rest);
|
||||||
|
ensure_clean(<<X/utf8, Rest/binary>>) when X >= 16#70000, X < 16#7fffe -> ensure_clean(Rest);
|
||||||
|
ensure_clean(<<X/utf8, Rest/binary>>) when X >= 16#80000, X < 16#8fffe -> ensure_clean(Rest);
|
||||||
|
ensure_clean(<<X/utf8, Rest/binary>>) when X >= 16#90000, X < 16#9fffe -> ensure_clean(Rest);
|
||||||
|
ensure_clean(<<X/utf8, Rest/binary>>) when X >= 16#a0000, X < 16#afffe -> ensure_clean(Rest);
|
||||||
|
ensure_clean(<<X/utf8, Rest/binary>>) when X >= 16#b0000, X < 16#bfffe -> ensure_clean(Rest);
|
||||||
|
ensure_clean(<<X/utf8, Rest/binary>>) when X >= 16#c0000, X < 16#cfffe -> ensure_clean(Rest);
|
||||||
|
ensure_clean(<<X/utf8, Rest/binary>>) when X >= 16#d0000, X < 16#dfffe -> ensure_clean(Rest);
|
||||||
|
ensure_clean(<<X/utf8, Rest/binary>>) when X >= 16#e0000, X < 16#efffe -> ensure_clean(Rest);
|
||||||
|
ensure_clean(<<X/utf8, Rest/binary>>) when X >= 16#f0000, X < 16#ffffe -> ensure_clean(Rest);
|
||||||
|
ensure_clean(<<X/utf8, Rest/binary>>) when X >= 16#100000, X < 16#10fffe -> ensure_clean(Rest);
|
||||||
|
ensure_clean(Bin) -> erlang:error(badarg, [Bin]).
|
||||||
|
|
||||||
|
|
||||||
is_clean(<<>>) -> true;
|
%% escape and/or replace bad codepoints if requested
|
||||||
is_clean(<<X/utf8, Rest/binary>>) when X < 16#80 -> is_clean(Rest);
|
clean(<<>>, Acc, _Opts) -> unicode:characters_to_binary(lists:reverse(Acc));
|
||||||
is_clean(<<X/utf8, Rest/binary>>) when X < 16#800 -> is_clean(Rest);
|
clean(<<0, Rest/binary>>, Acc, Opts) -> clean(Rest, maybe_replace(0, Opts) ++ Acc, Opts);
|
||||||
is_clean(<<X/utf8, Rest/binary>>) when X < 16#dcff -> is_clean(Rest);
|
clean(<<1, Rest/binary>>, Acc, Opts) -> clean(Rest, maybe_replace(1, Opts) ++ Acc, Opts);
|
||||||
is_clean(<<X/utf8, Rest/binary>>) when X > 16#dfff, X < 16#fdd0 -> is_clean(Rest);
|
clean(<<2, Rest/binary>>, Acc, Opts) -> clean(Rest, maybe_replace(2, Opts) ++ Acc, Opts);
|
||||||
is_clean(<<X/utf8, Rest/binary>>) when X > 16#fdef, X < 16#fffe -> is_clean(Rest);
|
clean(<<3, Rest/binary>>, Acc, Opts) -> clean(Rest, maybe_replace(3, Opts) ++ Acc, Opts);
|
||||||
is_clean(<<X/utf8, Rest/binary>>) when X >= 16#10000, X < 16#1fffe -> is_clean(Rest);
|
clean(<<4, Rest/binary>>, Acc, Opts) -> clean(Rest, maybe_replace(4, Opts) ++ Acc, Opts);
|
||||||
is_clean(<<X/utf8, Rest/binary>>) when X >= 16#20000, X < 16#2fffe -> is_clean(Rest);
|
clean(<<5, Rest/binary>>, Acc, Opts) -> clean(Rest, maybe_replace(5, Opts) ++ Acc, Opts);
|
||||||
is_clean(<<X/utf8, Rest/binary>>) when X >= 16#30000, X < 16#3fffe -> is_clean(Rest);
|
clean(<<6, Rest/binary>>, Acc, Opts) -> clean(Rest, maybe_replace(6, Opts) ++ Acc, Opts);
|
||||||
is_clean(<<X/utf8, Rest/binary>>) when X >= 16#40000, X < 16#4fffe -> is_clean(Rest);
|
clean(<<7, Rest/binary>>, Acc, Opts) -> clean(Rest, maybe_replace(7, Opts) ++ Acc, Opts);
|
||||||
is_clean(<<X/utf8, Rest/binary>>) when X >= 16#50000, X < 16#5fffe -> is_clean(Rest);
|
clean(<<8, Rest/binary>>, Acc, Opts) -> clean(Rest, maybe_replace(8, Opts) ++ Acc, Opts);
|
||||||
is_clean(<<X/utf8, Rest/binary>>) when X >= 16#60000, X < 16#6fffe -> is_clean(Rest);
|
clean(<<9, Rest/binary>>, Acc, Opts) -> clean(Rest, maybe_replace(9, Opts) ++ Acc, Opts);
|
||||||
is_clean(<<X/utf8, Rest/binary>>) when X >= 16#70000, X < 16#7fffe -> is_clean(Rest);
|
clean(<<10, Rest/binary>>, Acc, Opts) -> clean(Rest, maybe_replace(10, Opts) ++ Acc, Opts);
|
||||||
is_clean(<<X/utf8, Rest/binary>>) when X >= 16#80000, X < 16#8fffe -> is_clean(Rest);
|
clean(<<11, Rest/binary>>, Acc, Opts) -> clean(Rest, maybe_replace(11, Opts) ++ Acc, Opts);
|
||||||
is_clean(<<X/utf8, Rest/binary>>) when X >= 16#90000, X < 16#9fffe -> is_clean(Rest);
|
clean(<<12, Rest/binary>>, Acc, Opts) -> clean(Rest, maybe_replace(12, Opts) ++ Acc, Opts);
|
||||||
is_clean(<<X/utf8, Rest/binary>>) when X >= 16#a0000, X < 16#afffe -> is_clean(Rest);
|
clean(<<13, Rest/binary>>, Acc, Opts) -> clean(Rest, maybe_replace(13, Opts) ++ Acc, Opts);
|
||||||
is_clean(<<X/utf8, Rest/binary>>) when X >= 16#b0000, X < 16#bfffe -> is_clean(Rest);
|
clean(<<14, Rest/binary>>, Acc, Opts) -> clean(Rest, maybe_replace(14, Opts) ++ Acc, Opts);
|
||||||
is_clean(<<X/utf8, Rest/binary>>) when X >= 16#c0000, X < 16#cfffe -> is_clean(Rest);
|
clean(<<15, Rest/binary>>, Acc, Opts) -> clean(Rest, maybe_replace(15, Opts) ++ Acc, Opts);
|
||||||
is_clean(<<X/utf8, Rest/binary>>) when X >= 16#d0000, X < 16#dfffe -> is_clean(Rest);
|
clean(<<16, Rest/binary>>, Acc, Opts) -> clean(Rest, maybe_replace(16, Opts) ++ Acc, Opts);
|
||||||
is_clean(<<X/utf8, Rest/binary>>) when X >= 16#e0000, X < 16#efffe -> is_clean(Rest);
|
clean(<<17, Rest/binary>>, Acc, Opts) -> clean(Rest, maybe_replace(17, Opts) ++ Acc, Opts);
|
||||||
is_clean(<<X/utf8, Rest/binary>>) when X >= 16#f0000, X < 16#ffffe -> is_clean(Rest);
|
clean(<<18, Rest/binary>>, Acc, Opts) -> clean(Rest, maybe_replace(18, Opts) ++ Acc, Opts);
|
||||||
is_clean(<<X/utf8, Rest/binary>>) when X >= 16#100000, X < 16#10fffe -> is_clean(Rest);
|
clean(<<19, Rest/binary>>, Acc, Opts) -> clean(Rest, maybe_replace(19, Opts) ++ Acc, Opts);
|
||||||
is_clean(Bin) -> erlang:error(badarg, [Bin]).
|
clean(<<20, Rest/binary>>, Acc, Opts) -> clean(Rest, maybe_replace(20, Opts) ++ Acc, Opts);
|
||||||
|
clean(<<21, Rest/binary>>, Acc, Opts) -> clean(Rest, maybe_replace(21, Opts) ++ Acc, Opts);
|
||||||
|
clean(<<22, Rest/binary>>, Acc, Opts) -> clean(Rest, maybe_replace(22, Opts) ++ Acc, Opts);
|
||||||
|
clean(<<23, Rest/binary>>, Acc, Opts) -> clean(Rest, maybe_replace(23, Opts) ++ Acc, Opts);
|
||||||
|
clean(<<24, Rest/binary>>, Acc, Opts) -> clean(Rest, maybe_replace(24, Opts) ++ Acc, Opts);
|
||||||
|
clean(<<25, Rest/binary>>, Acc, Opts) -> clean(Rest, maybe_replace(25, Opts) ++ Acc, Opts);
|
||||||
|
clean(<<26, Rest/binary>>, Acc, Opts) -> clean(Rest, maybe_replace(26, Opts) ++ Acc, Opts);
|
||||||
|
clean(<<27, Rest/binary>>, Acc, Opts) -> clean(Rest, maybe_replace(27, Opts) ++ Acc, Opts);
|
||||||
|
clean(<<28, Rest/binary>>, Acc, Opts) -> clean(Rest, maybe_replace(28, Opts) ++ Acc, Opts);
|
||||||
|
clean(<<29, Rest/binary>>, Acc, Opts) -> clean(Rest, maybe_replace(29, Opts) ++ Acc, Opts);
|
||||||
|
clean(<<30, Rest/binary>>, Acc, Opts) -> clean(Rest, maybe_replace(30, Opts) ++ Acc, Opts);
|
||||||
|
clean(<<31, Rest/binary>>, Acc, Opts) -> clean(Rest, maybe_replace(31, Opts) ++ Acc, Opts);
|
||||||
|
clean(<<32, Rest/binary>>, Acc, Opts) -> clean(Rest, [32] ++ Acc, Opts);
|
||||||
|
clean(<<33, Rest/binary>>, Acc, Opts) -> clean(Rest, [33] ++ Acc, Opts);
|
||||||
|
clean(<<34, Rest/binary>>, Acc, Opts) -> clean(Rest, maybe_replace(34, Opts) ++ Acc, Opts);
|
||||||
|
clean(<<35, Rest/binary>>, Acc, Opts) -> clean(Rest, [35] ++ Acc, Opts);
|
||||||
|
clean(<<36, Rest/binary>>, Acc, Opts) -> clean(Rest, [36] ++ Acc, Opts);
|
||||||
|
clean(<<37, Rest/binary>>, Acc, Opts) -> clean(Rest, [37] ++ Acc, Opts);
|
||||||
|
clean(<<38, Rest/binary>>, Acc, Opts) -> clean(Rest, [38] ++ Acc, Opts);
|
||||||
|
clean(<<39, Rest/binary>>, Acc, Opts) -> clean(Rest, maybe_replace(39, Opts) ++ Acc, Opts);
|
||||||
|
clean(<<40, Rest/binary>>, Acc, Opts) -> clean(Rest, [40] ++ Acc, Opts);
|
||||||
|
clean(<<41, Rest/binary>>, Acc, Opts) -> clean(Rest, [41] ++ Acc, Opts);
|
||||||
|
clean(<<42, Rest/binary>>, Acc, Opts) -> clean(Rest, [42] ++ Acc, Opts);
|
||||||
|
clean(<<43, Rest/binary>>, Acc, Opts) -> clean(Rest, [43] ++ Acc, Opts);
|
||||||
|
clean(<<44, Rest/binary>>, Acc, Opts) -> clean(Rest, [44] ++ Acc, Opts);
|
||||||
|
clean(<<45, Rest/binary>>, Acc, Opts) -> clean(Rest, [45] ++ Acc, Opts);
|
||||||
|
clean(<<46, Rest/binary>>, Acc, Opts) -> clean(Rest, [46] ++ Acc, Opts);
|
||||||
|
clean(<<47, Rest/binary>>, Acc, Opts) -> clean(Rest, maybe_replace(47, Opts) ++ Acc, Opts);
|
||||||
|
clean(<<48, Rest/binary>>, Acc, Opts) -> clean(Rest, [48] ++ Acc, Opts);
|
||||||
|
clean(<<49, Rest/binary>>, Acc, Opts) -> clean(Rest, [49] ++ Acc, Opts);
|
||||||
|
clean(<<50, Rest/binary>>, Acc, Opts) -> clean(Rest, [50] ++ Acc, Opts);
|
||||||
|
clean(<<51, Rest/binary>>, Acc, Opts) -> clean(Rest, [51] ++ Acc, Opts);
|
||||||
|
clean(<<52, Rest/binary>>, Acc, Opts) -> clean(Rest, [52] ++ Acc, Opts);
|
||||||
|
clean(<<53, Rest/binary>>, Acc, Opts) -> clean(Rest, [53] ++ Acc, Opts);
|
||||||
|
clean(<<54, Rest/binary>>, Acc, Opts) -> clean(Rest, [54] ++ Acc, Opts);
|
||||||
|
clean(<<55, Rest/binary>>, Acc, Opts) -> clean(Rest, [55] ++ Acc, Opts);
|
||||||
|
clean(<<56, Rest/binary>>, Acc, Opts) -> clean(Rest, [56] ++ Acc, Opts);
|
||||||
|
clean(<<57, Rest/binary>>, Acc, Opts) -> clean(Rest, [57] ++ Acc, Opts);
|
||||||
|
clean(<<58, Rest/binary>>, Acc, Opts) -> clean(Rest, [58] ++ Acc, Opts);
|
||||||
|
clean(<<59, Rest/binary>>, Acc, Opts) -> clean(Rest, [59] ++ Acc, Opts);
|
||||||
|
clean(<<60, Rest/binary>>, Acc, Opts) -> clean(Rest, [60] ++ Acc, Opts);
|
||||||
|
clean(<<61, Rest/binary>>, Acc, Opts) -> clean(Rest, [61] ++ Acc, Opts);
|
||||||
|
clean(<<62, Rest/binary>>, Acc, Opts) -> clean(Rest, [62] ++ Acc, Opts);
|
||||||
|
clean(<<63, Rest/binary>>, Acc, Opts) -> clean(Rest, [63] ++ Acc, Opts);
|
||||||
|
clean(<<64, Rest/binary>>, Acc, Opts) -> clean(Rest, [64] ++ Acc, Opts);
|
||||||
|
clean(<<65, Rest/binary>>, Acc, Opts) -> clean(Rest, [65] ++ Acc, Opts);
|
||||||
|
clean(<<66, Rest/binary>>, Acc, Opts) -> clean(Rest, [66] ++ Acc, Opts);
|
||||||
|
clean(<<67, Rest/binary>>, Acc, Opts) -> clean(Rest, [67] ++ Acc, Opts);
|
||||||
|
clean(<<68, Rest/binary>>, Acc, Opts) -> clean(Rest, [68] ++ Acc, Opts);
|
||||||
|
clean(<<69, Rest/binary>>, Acc, Opts) -> clean(Rest, [69] ++ Acc, Opts);
|
||||||
|
clean(<<70, Rest/binary>>, Acc, Opts) -> clean(Rest, [70] ++ Acc, Opts);
|
||||||
|
clean(<<71, Rest/binary>>, Acc, Opts) -> clean(Rest, [71] ++ Acc, Opts);
|
||||||
|
clean(<<72, Rest/binary>>, Acc, Opts) -> clean(Rest, [72] ++ Acc, Opts);
|
||||||
|
clean(<<73, Rest/binary>>, Acc, Opts) -> clean(Rest, [73] ++ Acc, Opts);
|
||||||
|
clean(<<74, Rest/binary>>, Acc, Opts) -> clean(Rest, [74] ++ Acc, Opts);
|
||||||
|
clean(<<75, Rest/binary>>, Acc, Opts) -> clean(Rest, [75] ++ Acc, Opts);
|
||||||
|
clean(<<76, Rest/binary>>, Acc, Opts) -> clean(Rest, [76] ++ Acc, Opts);
|
||||||
|
clean(<<77, Rest/binary>>, Acc, Opts) -> clean(Rest, [77] ++ Acc, Opts);
|
||||||
|
clean(<<78, Rest/binary>>, Acc, Opts) -> clean(Rest, [78] ++ Acc, Opts);
|
||||||
|
clean(<<79, Rest/binary>>, Acc, Opts) -> clean(Rest, [79] ++ Acc, Opts);
|
||||||
|
clean(<<80, Rest/binary>>, Acc, Opts) -> clean(Rest, [80] ++ Acc, Opts);
|
||||||
|
clean(<<81, Rest/binary>>, Acc, Opts) -> clean(Rest, [81] ++ Acc, Opts);
|
||||||
|
clean(<<82, Rest/binary>>, Acc, Opts) -> clean(Rest, [82] ++ Acc, Opts);
|
||||||
|
clean(<<83, Rest/binary>>, Acc, Opts) -> clean(Rest, [83] ++ Acc, Opts);
|
||||||
|
clean(<<84, Rest/binary>>, Acc, Opts) -> clean(Rest, [84] ++ Acc, Opts);
|
||||||
|
clean(<<85, Rest/binary>>, Acc, Opts) -> clean(Rest, [85] ++ Acc, Opts);
|
||||||
|
clean(<<86, Rest/binary>>, Acc, Opts) -> clean(Rest, [86] ++ Acc, Opts);
|
||||||
|
clean(<<87, Rest/binary>>, Acc, Opts) -> clean(Rest, [87] ++ Acc, Opts);
|
||||||
|
clean(<<88, Rest/binary>>, Acc, Opts) -> clean(Rest, [88] ++ Acc, Opts);
|
||||||
|
clean(<<89, Rest/binary>>, Acc, Opts) -> clean(Rest, [89] ++ Acc, Opts);
|
||||||
|
clean(<<90, Rest/binary>>, Acc, Opts) -> clean(Rest, [90] ++ Acc, Opts);
|
||||||
|
clean(<<91, Rest/binary>>, Acc, Opts) -> clean(Rest, [91] ++ Acc, Opts);
|
||||||
|
clean(<<92, Rest/binary>>, Acc, Opts) -> clean(Rest, maybe_replace(92, Opts) ++ Acc, Opts);
|
||||||
|
clean(<<93, Rest/binary>>, Acc, Opts) -> clean(Rest, [93] ++ Acc, Opts);
|
||||||
|
clean(<<94, Rest/binary>>, Acc, Opts) -> clean(Rest, [94] ++ Acc, Opts);
|
||||||
|
clean(<<95, Rest/binary>>, Acc, Opts) -> clean(Rest, [95] ++ Acc, Opts);
|
||||||
|
clean(<<96, Rest/binary>>, Acc, Opts) -> clean(Rest, [96] ++ Acc, Opts);
|
||||||
|
clean(<<97, Rest/binary>>, Acc, Opts) -> clean(Rest, [97] ++ Acc, Opts);
|
||||||
|
clean(<<98, Rest/binary>>, Acc, Opts) -> clean(Rest, [98] ++ Acc, Opts);
|
||||||
|
clean(<<99, Rest/binary>>, Acc, Opts) -> clean(Rest, [99] ++ Acc, Opts);
|
||||||
|
clean(<<100, Rest/binary>>, Acc, Opts) -> clean(Rest, [100] ++ Acc, Opts);
|
||||||
|
clean(<<101, Rest/binary>>, Acc, Opts) -> clean(Rest, [101] ++ Acc, Opts);
|
||||||
|
clean(<<102, Rest/binary>>, Acc, Opts) -> clean(Rest, [102] ++ Acc, Opts);
|
||||||
|
clean(<<103, Rest/binary>>, Acc, Opts) -> clean(Rest, [103] ++ Acc, Opts);
|
||||||
|
clean(<<104, Rest/binary>>, Acc, Opts) -> clean(Rest, [104] ++ Acc, Opts);
|
||||||
|
clean(<<105, Rest/binary>>, Acc, Opts) -> clean(Rest, [105] ++ Acc, Opts);
|
||||||
|
clean(<<106, Rest/binary>>, Acc, Opts) -> clean(Rest, [106] ++ Acc, Opts);
|
||||||
|
clean(<<107, Rest/binary>>, Acc, Opts) -> clean(Rest, [107] ++ Acc, Opts);
|
||||||
|
clean(<<108, Rest/binary>>, Acc, Opts) -> clean(Rest, [108] ++ Acc, Opts);
|
||||||
|
clean(<<109, Rest/binary>>, Acc, Opts) -> clean(Rest, [109] ++ Acc, Opts);
|
||||||
|
clean(<<110, Rest/binary>>, Acc, Opts) -> clean(Rest, [110] ++ Acc, Opts);
|
||||||
|
clean(<<111, Rest/binary>>, Acc, Opts) -> clean(Rest, [111] ++ Acc, Opts);
|
||||||
|
clean(<<112, Rest/binary>>, Acc, Opts) -> clean(Rest, [112] ++ Acc, Opts);
|
||||||
|
clean(<<113, Rest/binary>>, Acc, Opts) -> clean(Rest, [113] ++ Acc, Opts);
|
||||||
|
clean(<<114, Rest/binary>>, Acc, Opts) -> clean(Rest, [114] ++ Acc, Opts);
|
||||||
|
clean(<<115, Rest/binary>>, Acc, Opts) -> clean(Rest, [115] ++ Acc, Opts);
|
||||||
|
clean(<<116, Rest/binary>>, Acc, Opts) -> clean(Rest, [116] ++ Acc, Opts);
|
||||||
|
clean(<<117, Rest/binary>>, Acc, Opts) -> clean(Rest, [117] ++ Acc, Opts);
|
||||||
|
clean(<<118, Rest/binary>>, Acc, Opts) -> clean(Rest, [118] ++ Acc, Opts);
|
||||||
|
clean(<<119, Rest/binary>>, Acc, Opts) -> clean(Rest, [119] ++ Acc, Opts);
|
||||||
|
clean(<<120, Rest/binary>>, Acc, Opts) -> clean(Rest, [120] ++ Acc, Opts);
|
||||||
|
clean(<<121, Rest/binary>>, Acc, Opts) -> clean(Rest, [121] ++ Acc, Opts);
|
||||||
|
clean(<<122, Rest/binary>>, Acc, Opts) -> clean(Rest, [122] ++ Acc, Opts);
|
||||||
|
clean(<<123, Rest/binary>>, Acc, Opts) -> clean(Rest, [123] ++ Acc, Opts);
|
||||||
|
clean(<<124, Rest/binary>>, Acc, Opts) -> clean(Rest, [124] ++ Acc, Opts);
|
||||||
|
clean(<<125, Rest/binary>>, Acc, Opts) -> clean(Rest, [125] ++ Acc, Opts);
|
||||||
|
clean(<<126, Rest/binary>>, Acc, Opts) -> clean(Rest, [126] ++ Acc, Opts);
|
||||||
|
clean(<<127, Rest/binary>>, Acc, Opts) -> clean(Rest, [127] ++ Acc, Opts);
|
||||||
|
clean(<<X/utf8, Rest/binary>>, Acc, Opts) when X < 16#800 ->
|
||||||
|
clean(Rest, [X] ++ Acc, Opts);
|
||||||
|
clean(<<X/utf8, Rest/binary>>, Acc, Opts) when X == 16#2028; X == 16#2029 ->
|
||||||
|
clean(Rest, maybe_replace(X, Opts) ++ Acc, Opts);
|
||||||
|
clean(<<X/utf8, Rest/binary>>, Acc, Opts) when X < 16#dcff ->
|
||||||
|
clean(Rest, [X] ++ Acc, Opts);
|
||||||
|
clean(<<X/utf8, Rest/binary>>, Acc, Opts) when X > 16#dfff, X < 16#fdd0 ->
|
||||||
|
clean(Rest, [X] ++ Acc, Opts);
|
||||||
|
clean(<<X/utf8, Rest/binary>>, Acc, Opts) when X > 16#fdef, X < 16#fffe ->
|
||||||
|
clean(Rest, [X] ++ Acc, Opts);
|
||||||
|
clean(<<X/utf8, Rest/binary>>, Acc, Opts) when X >= 16#10000, X < 16#1fffe ->
|
||||||
|
clean(Rest, [X] ++ Acc, Opts);
|
||||||
|
clean(<<X/utf8, Rest/binary>>, Acc, Opts) when X >= 16#20000, X < 16#2fffe ->
|
||||||
|
clean(Rest, [X] ++ Acc, Opts);
|
||||||
|
clean(<<X/utf8, Rest/binary>>, Acc, Opts) when X >= 16#30000, X < 16#3fffe ->
|
||||||
|
clean(Rest, [X] ++ Acc, Opts);
|
||||||
|
clean(<<X/utf8, Rest/binary>>, Acc, Opts) when X >= 16#40000, X < 16#4fffe ->
|
||||||
|
clean(Rest, [X] ++ Acc, Opts);
|
||||||
|
clean(<<X/utf8, Rest/binary>>, Acc, Opts) when X >= 16#50000, X < 16#5fffe ->
|
||||||
|
clean(Rest, [X] ++ Acc, Opts);
|
||||||
|
clean(<<X/utf8, Rest/binary>>, Acc, Opts) when X >= 16#60000, X < 16#6fffe ->
|
||||||
|
clean(Rest, [X] ++ Acc, Opts);
|
||||||
|
clean(<<X/utf8, Rest/binary>>, Acc, Opts) when X >= 16#70000, X < 16#7fffe ->
|
||||||
|
clean(Rest, [X] ++ Acc, Opts);
|
||||||
|
clean(<<X/utf8, Rest/binary>>, Acc, Opts) when X >= 16#80000, X < 16#8fffe ->
|
||||||
|
clean(Rest, [X] ++ Acc, Opts);
|
||||||
|
clean(<<X/utf8, Rest/binary>>, Acc, Opts) when X >= 16#90000, X < 16#9fffe ->
|
||||||
|
clean(Rest, [X] ++ Acc, Opts);
|
||||||
|
clean(<<X/utf8, Rest/binary>>, Acc, Opts) when X >= 16#a0000, X < 16#afffe ->
|
||||||
|
clean(Rest, [X] ++ Acc, Opts);
|
||||||
|
clean(<<X/utf8, Rest/binary>>, Acc, Opts) when X >= 16#b0000, X < 16#bfffe ->
|
||||||
|
clean(Rest, [X] ++ Acc, Opts);
|
||||||
|
clean(<<X/utf8, Rest/binary>>, Acc, Opts) when X >= 16#c0000, X < 16#cfffe ->
|
||||||
|
clean(Rest, [X] ++ Acc, Opts);
|
||||||
|
clean(<<X/utf8, Rest/binary>>, Acc, Opts) when X >= 16#d0000, X < 16#dfffe ->
|
||||||
|
clean(Rest, [X] ++ Acc, Opts);
|
||||||
|
clean(<<X/utf8, Rest/binary>>, Acc, Opts) when X >= 16#e0000, X < 16#efffe ->
|
||||||
|
clean(Rest, [X] ++ Acc, Opts);
|
||||||
|
clean(<<X/utf8, Rest/binary>>, Acc, Opts) when X >= 16#f0000, X < 16#ffffe ->
|
||||||
|
clean(Rest, [X] ++ Acc, Opts);
|
||||||
|
clean(<<X/utf8, Rest/binary>>, Acc, Opts) when X >= 16#100000, X < 16#10fffe ->
|
||||||
|
clean(Rest, [X] ++ Acc, Opts);
|
||||||
|
%% noncharacters
|
||||||
|
clean(<<_/utf8, Rest/binary>>, Acc, Opts) ->
|
||||||
|
clean(Rest, maybe_replace(noncharacter, Opts) ++ Acc, Opts);
|
||||||
|
%% surrogates
|
||||||
|
clean(<<237, X, _, Rest/binary>>, Acc, Opts) when X >= 160 ->
|
||||||
|
clean(Rest, maybe_replace(surrogate, Opts) ++ Acc, Opts);
|
||||||
|
%% u+fffe and u+ffff for R14BXX
|
||||||
|
clean(<<239, 191, X, Rest/binary>>, Acc, Opts) when X == 190; X == 191 ->
|
||||||
|
clean(Rest, maybe_replace(noncharacter, Opts) ++ Acc, Opts);
|
||||||
|
%% overlong encodings and missing continuations of a 2 byte sequence
|
||||||
|
clean(<<X, Rest/binary>>, Acc, Opts) when X >= 192, X =< 223 ->
|
||||||
|
clean(strip_continuations(Rest, 1), maybe_replace(badutf, Opts) ++ Acc, Opts);
|
||||||
|
%% overlong encodings and missing continuations of a 3 byte sequence
|
||||||
|
clean(<<X, Rest/binary>>, Acc, Opts) when X >= 224, X =< 239 ->
|
||||||
|
clean(strip_continuations(Rest, 2), maybe_replace(badutf, Opts) ++ Acc, Opts);
|
||||||
|
%% overlong encodings and missing continuations of a 4 byte sequence
|
||||||
|
clean(<<X, Rest/binary>>, Acc, Opts) when X >= 240, X =< 247 ->
|
||||||
|
clean(strip_continuations(Rest, 3), maybe_replace(badutf, Opts) ++ Acc, Opts);
|
||||||
|
clean(<<_, Rest/binary>>, Acc, Opts) ->
|
||||||
|
clean(Rest, maybe_replace(badutf, Opts) ++ Acc, Opts).
|
||||||
|
|
||||||
|
|
||||||
clean_string(Str, Len, Len, _Opts) -> Str;
|
strip_continuations(Bin, 0) -> Bin;
|
||||||
clean_string(Str, L, Len, Opts) ->
|
strip_continuations(<<X, Rest/binary>>, N) when X >= 128, X =< 191 ->
|
||||||
case Str of
|
strip_continuations(Rest, N - 1);
|
||||||
<<_:L/binary, X/utf8, _/binary>> when X < 16#80 -> clean_string(Str, L + 1, Len, Opts)
|
|
||||||
; <<_:L/binary, X/utf8, _/binary>> when X < 16#800 -> clean_string(Str, L + 2, Len, Opts)
|
|
||||||
; <<_:L/binary, X/utf8, _/binary>> when X < 16#dcff -> clean_string(Str, L + 3, Len, Opts)
|
|
||||||
; <<_:L/binary, X/utf8, _/binary>> when X > 16#dfff, X < 16#fdd0 -> clean_string(Str, L + 3, Len, Opts)
|
|
||||||
; <<_:L/binary, X/utf8, _/binary>> when X > 16#fdef, X < 16#fffe -> clean_string(Str, L + 3, Len, Opts)
|
|
||||||
; <<_:L/binary, X/utf8, _/binary>> when X >= 16#10000, X < 16#1fffe -> clean_string(Str, L + 4, Len, Opts)
|
|
||||||
; <<_:L/binary, X/utf8, _/binary>> when X >= 16#20000, X < 16#2fffe -> clean_string(Str, L + 4, Len, Opts)
|
|
||||||
; <<_:L/binary, X/utf8, _/binary>> when X >= 16#30000, X < 16#3fffe -> clean_string(Str, L + 4, Len, Opts)
|
|
||||||
; <<_:L/binary, X/utf8, _/binary>> when X >= 16#40000, X < 16#4fffe -> clean_string(Str, L + 4, Len, Opts)
|
|
||||||
; <<_:L/binary, X/utf8, _/binary>> when X >= 16#50000, X < 16#5fffe -> clean_string(Str, L + 4, Len, Opts)
|
|
||||||
; <<_:L/binary, X/utf8, _/binary>> when X >= 16#60000, X < 16#6fffe -> clean_string(Str, L + 4, Len, Opts)
|
|
||||||
; <<_:L/binary, X/utf8, _/binary>> when X >= 16#70000, X < 16#7fffe -> clean_string(Str, L + 4, Len, Opts)
|
|
||||||
; <<_:L/binary, X/utf8, _/binary>> when X >= 16#80000, X < 16#8fffe -> clean_string(Str, L + 4, Len, Opts)
|
|
||||||
; <<_:L/binary, X/utf8, _/binary>> when X >= 16#90000, X < 16#9fffe -> clean_string(Str, L + 4, Len, Opts)
|
|
||||||
; <<_:L/binary, X/utf8, _/binary>> when X >= 16#a0000, X < 16#afffe -> clean_string(Str, L + 4, Len, Opts)
|
|
||||||
; <<_:L/binary, X/utf8, _/binary>> when X >= 16#b0000, X < 16#bfffe -> clean_string(Str, L + 4, Len, Opts)
|
|
||||||
; <<_:L/binary, X/utf8, _/binary>> when X >= 16#c0000, X < 16#cfffe -> clean_string(Str, L + 4, Len, Opts)
|
|
||||||
; <<_:L/binary, X/utf8, _/binary>> when X >= 16#d0000, X < 16#dfffe -> clean_string(Str, L + 4, Len, Opts)
|
|
||||||
; <<_:L/binary, X/utf8, _/binary>> when X >= 16#e0000, X < 16#efffe -> clean_string(Str, L + 4, Len, Opts)
|
|
||||||
; <<_:L/binary, X/utf8, _/binary>> when X >= 16#f0000, X < 16#ffffe -> clean_string(Str, L + 4, Len, Opts)
|
|
||||||
; <<_:L/binary, X/utf8, _/binary>> when X >= 16#100000, X < 16#10fffe -> clean_string(Str, L + 4, Len, Opts)
|
|
||||||
%% noncharacters
|
|
||||||
; <<H:L/binary, X/utf8, T/binary>> when X < 16#10000 ->
|
|
||||||
clean_string(<<H:L/binary, 16#fffd/utf8, T/binary>>, L + 3, Len, Opts)
|
|
||||||
; <<H:L/binary, _/utf8, T/binary>> ->
|
|
||||||
clean_string(<<H:L/binary, 16#fffd/utf8, T/binary>>, L + 4, Len, Opts)
|
|
||||||
%% surrogates
|
|
||||||
; <<H:L/binary, 237, X, _, T/binary>> when X >= 160 ->
|
|
||||||
clean_string(<<H:L/binary, 16#fffd/utf8, T/binary>>, L + 3, Len, Opts)
|
|
||||||
%% u+fffe and u+ffff for R14BXX
|
|
||||||
; <<H:L/binary, 239, 191, X, T/binary>> when X == 190; X == 191 ->
|
|
||||||
clean_string(<<H:L/binary, 16#fffd/utf8, T/binary>>, L + 3, Len, Opts)
|
|
||||||
%% overlong encodings and missing continuations of a 2 byte sequence
|
|
||||||
; <<H:L/binary, X, T/binary>> when X >= 192, X =< 223 ->
|
|
||||||
{Tail, Stripped} = strip_continuations(T, 1, 0),
|
|
||||||
clean_string(<<H:L/binary, 16#fffd/utf8, Tail/binary>>, L + 3, Len + 2 - Stripped, Opts)
|
|
||||||
%% overlong encodings and missing continuations of a 3 byte sequence
|
|
||||||
; <<H:L/binary, X, T/binary>> when X >= 224, X =< 239 ->
|
|
||||||
{Tail, Stripped} = strip_continuations(T, 2, 0),
|
|
||||||
clean_string(<<H:L/binary, 16#fffd/utf8, Tail/binary>>, L + 3, Len + 2 - Stripped, Opts)
|
|
||||||
%% overlong encodings and missing continuations of a 4 byte sequence
|
|
||||||
; <<H:L/binary, X, T/binary>> when X >= 240, X =< 247 ->
|
|
||||||
{Tail, Stripped} = strip_continuations(T, 3, 0),
|
|
||||||
clean_string(<<H:L/binary, 16#fffd/utf8, Tail/binary>>, L + 3, Len + 2 - Stripped, Opts)
|
|
||||||
; <<H:L/binary, _, T/binary>> ->
|
|
||||||
clean_string(<<H:L/binary, 16#fffd/utf8, T/binary>>, L + 3, Len + 2, Opts)
|
|
||||||
end.
|
|
||||||
|
|
||||||
|
|
||||||
strip_continuations(Bin, 0, N) -> {Bin, N};
|
|
||||||
strip_continuations(<<X, Rest/binary>>, N, M) when X >= 128, X =< 191 ->
|
|
||||||
strip_continuations(Rest, N - 1, M + 1);
|
|
||||||
%% not a continuation byte
|
%% not a continuation byte
|
||||||
strip_continuations(Bin, _, N) -> {Bin, N}.
|
strip_continuations(Bin, _) -> Bin.
|
||||||
|
|
||||||
|
|
||||||
|
maybe_replace(X, #opts{dirty_strings=true}) when is_integer(X) -> [X];
|
||||||
|
maybe_replace($\b, #opts{escaped_strings=true}) -> [$b, $\\];
|
||||||
|
maybe_replace($\t, #opts{escaped_strings=true}) -> [$t, $\\];
|
||||||
|
maybe_replace($\n, #opts{escaped_strings=true}) -> [$n, $\\];
|
||||||
|
maybe_replace($\f, #opts{escaped_strings=true}) -> [$f, $\\];
|
||||||
|
maybe_replace($\r, #opts{escaped_strings=true}) -> [$r, $\\];
|
||||||
|
maybe_replace($\", #opts{escaped_strings=true}) -> [$\", $\\];
|
||||||
|
maybe_replace($', Opts=#opts{escaped_strings=true}) ->
|
||||||
|
case Opts#opts.single_quoted_strings of
|
||||||
|
true -> [$', $\\]
|
||||||
|
; false -> [$']
|
||||||
|
end;
|
||||||
|
maybe_replace($/, Opts=#opts{escaped_strings=true}) ->
|
||||||
|
case Opts#opts.escaped_forward_slashes of
|
||||||
|
true -> [$/, $\\]
|
||||||
|
; false -> [$/]
|
||||||
|
end;
|
||||||
|
maybe_replace($\\, #opts{escaped_strings=true}) -> [$\\, $\\];
|
||||||
|
maybe_replace(X, Opts=#opts{escaped_strings=true}) when X == 16#2028; X == 16#2029 ->
|
||||||
|
case Opts#opts.unescaped_jsonp of
|
||||||
|
true -> [X]
|
||||||
|
; false -> lists:reverse(jsx_utils:json_escape_sequence(X))
|
||||||
|
end;
|
||||||
|
maybe_replace(X, #opts{escaped_strings=true}) when X < 32 ->
|
||||||
|
lists:reverse(jsx_utils:json_escape_sequence(X));
|
||||||
|
maybe_replace(noncharacter, #opts{replaced_bad_utf8=true}) -> [16#fffd];
|
||||||
|
maybe_replace(surrogate, #opts{replaced_bad_utf8=true}) -> [16#fffd];
|
||||||
|
maybe_replace(badutf, #opts{replaced_bad_utf8=true}) -> [16#fffd].
|
||||||
|
|
||||||
|
|
||||||
-ifdef(TEST).
|
-ifdef(TEST).
|
||||||
|
@ -208,7 +504,7 @@ strip_continuations(Bin, _, N) -> {Bin, N}.
|
||||||
|
|
||||||
xcode(Bin) -> xcode(Bin, #opts{}).
|
xcode(Bin) -> xcode(Bin, #opts{}).
|
||||||
|
|
||||||
xcode(Bin, [loose_unicode]) -> xcode(Bin, #opts{loose_unicode=true});
|
xcode(Bin, [replaced_bad_utf8]) -> xcode(Bin, #opts{replaced_bad_utf8=true});
|
||||||
xcode(Bin, Opts) ->
|
xcode(Bin, Opts) ->
|
||||||
try clean_string(Bin, Opts)
|
try clean_string(Bin, Opts)
|
||||||
catch error:badarg -> {error, badarg}
|
catch error:badarg -> {error, badarg}
|
||||||
|
@ -225,20 +521,20 @@ bad_utf8_test_() ->
|
||||||
?_assert(is_bad(xcode(<<16#0080>>)))
|
?_assert(is_bad(xcode(<<16#0080>>)))
|
||||||
},
|
},
|
||||||
{"orphan continuation byte u+0080 replaced",
|
{"orphan continuation byte u+0080 replaced",
|
||||||
?_assertEqual(xcode(<<16#0080>>, [loose_unicode]), <<16#fffd/utf8>>)
|
?_assertEqual(xcode(<<16#0080>>, [replaced_bad_utf8]), <<16#fffd/utf8>>)
|
||||||
},
|
},
|
||||||
{"orphan continuation byte u+00bf",
|
{"orphan continuation byte u+00bf",
|
||||||
?_assert(is_bad(xcode(<<16#00bf>>)))
|
?_assert(is_bad(xcode(<<16#00bf>>)))
|
||||||
},
|
},
|
||||||
{"orphan continuation byte u+00bf replaced",
|
{"orphan continuation byte u+00bf replaced",
|
||||||
?_assertEqual(xcode(<<16#00bf>>, [loose_unicode]), <<16#fffd/utf8>>)
|
?_assertEqual(xcode(<<16#00bf>>, [replaced_bad_utf8]), <<16#fffd/utf8>>)
|
||||||
},
|
},
|
||||||
{"2 continuation bytes",
|
{"2 continuation bytes",
|
||||||
?_assert(is_bad(xcode(<<(binary:copy(<<16#0080>>, 2))/binary>>)))
|
?_assert(is_bad(xcode(<<(binary:copy(<<16#0080>>, 2))/binary>>)))
|
||||||
},
|
},
|
||||||
{"2 continuation bytes replaced",
|
{"2 continuation bytes replaced",
|
||||||
?_assertEqual(
|
?_assertEqual(
|
||||||
xcode(<<(binary:copy(<<16#0080>>, 2))/binary>>, [loose_unicode]),
|
xcode(<<(binary:copy(<<16#0080>>, 2))/binary>>, [replaced_bad_utf8]),
|
||||||
binary:copy(<<16#fffd/utf8>>, 2)
|
binary:copy(<<16#fffd/utf8>>, 2)
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
@ -247,7 +543,7 @@ bad_utf8_test_() ->
|
||||||
},
|
},
|
||||||
{"3 continuation bytes replaced",
|
{"3 continuation bytes replaced",
|
||||||
?_assertEqual(
|
?_assertEqual(
|
||||||
xcode(<<(binary:copy(<<16#0080>>, 3))/binary>>, [loose_unicode]),
|
xcode(<<(binary:copy(<<16#0080>>, 3))/binary>>, [replaced_bad_utf8]),
|
||||||
binary:copy(<<16#fffd/utf8>>, 3)
|
binary:copy(<<16#fffd/utf8>>, 3)
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
@ -256,7 +552,7 @@ bad_utf8_test_() ->
|
||||||
},
|
},
|
||||||
{"4 continuation bytes replaced",
|
{"4 continuation bytes replaced",
|
||||||
?_assertEqual(
|
?_assertEqual(
|
||||||
xcode(<<(binary:copy(<<16#0080>>, 4))/binary>>, [loose_unicode]),
|
xcode(<<(binary:copy(<<16#0080>>, 4))/binary>>, [replaced_bad_utf8]),
|
||||||
binary:copy(<<16#fffd/utf8>>, 4)
|
binary:copy(<<16#fffd/utf8>>, 4)
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
@ -265,7 +561,7 @@ bad_utf8_test_() ->
|
||||||
},
|
},
|
||||||
{"5 continuation bytes replaced",
|
{"5 continuation bytes replaced",
|
||||||
?_assertEqual(
|
?_assertEqual(
|
||||||
xcode(<<(binary:copy(<<16#0080>>, 5))/binary>>, [loose_unicode]),
|
xcode(<<(binary:copy(<<16#0080>>, 5))/binary>>, [replaced_bad_utf8]),
|
||||||
binary:copy(<<16#fffd/utf8>>, 5)
|
binary:copy(<<16#fffd/utf8>>, 5)
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
@ -274,7 +570,7 @@ bad_utf8_test_() ->
|
||||||
},
|
},
|
||||||
{"6 continuation bytes replaced",
|
{"6 continuation bytes replaced",
|
||||||
?_assertEqual(
|
?_assertEqual(
|
||||||
xcode(<<(binary:copy(<<16#0080>>, 6))/binary>>, [loose_unicode]),
|
xcode(<<(binary:copy(<<16#0080>>, 6))/binary>>, [replaced_bad_utf8]),
|
||||||
binary:copy(<<16#fffd/utf8>>, 6)
|
binary:copy(<<16#fffd/utf8>>, 6)
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
@ -283,7 +579,7 @@ bad_utf8_test_() ->
|
||||||
},
|
},
|
||||||
{"all continuation bytes replaced",
|
{"all continuation bytes replaced",
|
||||||
?_assertEqual(
|
?_assertEqual(
|
||||||
xcode(<<(list_to_binary(lists:seq(16#0080, 16#00bf)))/binary>>, [loose_unicode]),
|
xcode(<<(list_to_binary(lists:seq(16#0080, 16#00bf)))/binary>>, [replaced_bad_utf8]),
|
||||||
binary:copy(<<16#fffd/utf8>>, length(lists:seq(16#0080, 16#00bf)))
|
binary:copy(<<16#fffd/utf8>>, length(lists:seq(16#0080, 16#00bf)))
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
@ -292,7 +588,7 @@ bad_utf8_test_() ->
|
||||||
},
|
},
|
||||||
{"lonely start byte replaced",
|
{"lonely start byte replaced",
|
||||||
?_assertEqual(
|
?_assertEqual(
|
||||||
xcode(<<16#00c0>>, [loose_unicode]),
|
xcode(<<16#00c0>>, [replaced_bad_utf8]),
|
||||||
<<16#fffd/utf8>>
|
<<16#fffd/utf8>>
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
@ -301,7 +597,7 @@ bad_utf8_test_() ->
|
||||||
},
|
},
|
||||||
{"lonely start bytes (2 byte) replaced",
|
{"lonely start bytes (2 byte) replaced",
|
||||||
?_assertEqual(
|
?_assertEqual(
|
||||||
xcode(<<16#00c0, 32, 16#00df>>, [loose_unicode]),
|
xcode(<<16#00c0, 32, 16#00df>>, [replaced_bad_utf8]),
|
||||||
<<16#fffd/utf8, 32, 16#fffd/utf8>>
|
<<16#fffd/utf8, 32, 16#fffd/utf8>>
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
@ -310,7 +606,7 @@ bad_utf8_test_() ->
|
||||||
},
|
},
|
||||||
{"lonely start bytes (3 byte) replaced",
|
{"lonely start bytes (3 byte) replaced",
|
||||||
?_assertEqual(
|
?_assertEqual(
|
||||||
xcode(<<16#00e0, 32, 16#00ef>>, [loose_unicode]),
|
xcode(<<16#00e0, 32, 16#00ef>>, [replaced_bad_utf8]),
|
||||||
<<16#fffd/utf8, 32, 16#fffd/utf8>>
|
<<16#fffd/utf8, 32, 16#fffd/utf8>>
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
@ -319,7 +615,7 @@ bad_utf8_test_() ->
|
||||||
},
|
},
|
||||||
{"lonely start bytes (4 byte) replaced",
|
{"lonely start bytes (4 byte) replaced",
|
||||||
?_assertEqual(
|
?_assertEqual(
|
||||||
xcode(<<16#00f0, 32, 16#00f7>>, [loose_unicode]),
|
xcode(<<16#00f0, 32, 16#00f7>>, [replaced_bad_utf8]),
|
||||||
<<16#fffd/utf8, 32, 16#fffd/utf8>>
|
<<16#fffd/utf8, 32, 16#fffd/utf8>>
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
@ -328,25 +624,25 @@ bad_utf8_test_() ->
|
||||||
},
|
},
|
||||||
{"missing continuation byte (3 byte) replaced",
|
{"missing continuation byte (3 byte) replaced",
|
||||||
?_assertEqual(
|
?_assertEqual(
|
||||||
xcode(<<224, 160, 32>>, [loose_unicode]),
|
xcode(<<224, 160, 32>>, [replaced_bad_utf8]),
|
||||||
<<16#fffd/utf8, 32>>
|
<<16#fffd/utf8, 32>>
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
{"missing continuation byte (4 byte missing one)",
|
{"missing continuation byte (4 byte missing one)",
|
||||||
?_assert(is_bad(xcode(<<240, 144, 128, 32>>)))
|
?_assert(is_bad(xcode(<<240, 144, 128, 32>>)))
|
||||||
},
|
},
|
||||||
{"missing continuation byte2 (4 byte missing one) replaced",
|
{"missing continuation byte (4 byte missing one) replaced",
|
||||||
?_assertEqual(
|
?_assertEqual(
|
||||||
xcode(<<240, 144, 128, 32>>, [loose_unicode]),
|
xcode(<<240, 144, 128, 32>>, [replaced_bad_utf8]),
|
||||||
<<16#fffd/utf8, 32>>
|
<<16#fffd/utf8, 32>>
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
{"missing continuation byte (4 byte missing two)",
|
{"missing continuation byte (4 byte missing two)",
|
||||||
?_assert(is_bad(xcode(<<240, 144, 32>>)))
|
?_assert(is_bad(xcode(<<240, 144, 32>>)))
|
||||||
},
|
},
|
||||||
{"missing continuation byte2 (4 byte missing two) replaced",
|
{"missing continuation byte (4 byte missing two) replaced",
|
||||||
?_assertEqual(
|
?_assertEqual(
|
||||||
xcode(<<240, 144, 32>>, [loose_unicode]),
|
xcode(<<240, 144, 32>>, [replaced_bad_utf8]),
|
||||||
<<16#fffd/utf8, 32>>
|
<<16#fffd/utf8, 32>>
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
@ -355,7 +651,7 @@ bad_utf8_test_() ->
|
||||||
},
|
},
|
||||||
{"overlong encoding of u+002f (2 byte) replaced",
|
{"overlong encoding of u+002f (2 byte) replaced",
|
||||||
?_assertEqual(
|
?_assertEqual(
|
||||||
xcode(<<16#c0, 16#af, 32>>, [loose_unicode]),
|
xcode(<<16#c0, 16#af, 32>>, [replaced_bad_utf8]),
|
||||||
<<16#fffd/utf8, 32>>
|
<<16#fffd/utf8, 32>>
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
@ -364,7 +660,7 @@ bad_utf8_test_() ->
|
||||||
},
|
},
|
||||||
{"overlong encoding of u+002f (3 byte) replaced",
|
{"overlong encoding of u+002f (3 byte) replaced",
|
||||||
?_assertEqual(
|
?_assertEqual(
|
||||||
xcode(<<16#e0, 16#80, 16#af, 32>>, [loose_unicode]),
|
xcode(<<16#e0, 16#80, 16#af, 32>>, [replaced_bad_utf8]),
|
||||||
<<16#fffd/utf8, 32>>
|
<<16#fffd/utf8, 32>>
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
@ -373,7 +669,7 @@ bad_utf8_test_() ->
|
||||||
},
|
},
|
||||||
{"overlong encoding of u+002f (4 byte) replaced",
|
{"overlong encoding of u+002f (4 byte) replaced",
|
||||||
?_assertEqual(
|
?_assertEqual(
|
||||||
xcode(<<16#f0, 16#80, 16#80, 16#af, 32>>, [loose_unicode]),
|
xcode(<<16#f0, 16#80, 16#80, 16#af, 32>>, [replaced_bad_utf8]),
|
||||||
<<16#fffd/utf8, 32>>
|
<<16#fffd/utf8, 32>>
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
@ -382,7 +678,7 @@ bad_utf8_test_() ->
|
||||||
},
|
},
|
||||||
{"highest overlong 2 byte sequence replaced",
|
{"highest overlong 2 byte sequence replaced",
|
||||||
?_assertEqual(
|
?_assertEqual(
|
||||||
xcode(<<16#c1, 16#bf, 32>>, [loose_unicode]),
|
xcode(<<16#c1, 16#bf, 32>>, [replaced_bad_utf8]),
|
||||||
<<16#fffd/utf8, 32>>
|
<<16#fffd/utf8, 32>>
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
@ -391,7 +687,7 @@ bad_utf8_test_() ->
|
||||||
},
|
},
|
||||||
{"highest overlong 3 byte sequence replaced",
|
{"highest overlong 3 byte sequence replaced",
|
||||||
?_assertEqual(
|
?_assertEqual(
|
||||||
xcode(<<16#e0, 16#9f, 16#bf, 32>>, [loose_unicode]),
|
xcode(<<16#e0, 16#9f, 16#bf, 32>>, [replaced_bad_utf8]),
|
||||||
<<16#fffd/utf8, 32>>
|
<<16#fffd/utf8, 32>>
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
@ -400,7 +696,7 @@ bad_utf8_test_() ->
|
||||||
},
|
},
|
||||||
{"highest overlong 4 byte sequence replaced",
|
{"highest overlong 4 byte sequence replaced",
|
||||||
?_assertEqual(
|
?_assertEqual(
|
||||||
xcode(<<16#f0, 16#8f, 16#bf, 16#bf, 32>>, [loose_unicode]),
|
xcode(<<16#f0, 16#8f, 16#bf, 16#bf, 32>>, [replaced_bad_utf8]),
|
||||||
<<16#fffd/utf8, 32>>
|
<<16#fffd/utf8, 32>>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -418,7 +714,7 @@ encode(Term, Opts) ->
|
||||||
encode_test_() ->
|
encode_test_() ->
|
||||||
[
|
[
|
||||||
{"naked string", ?_assertEqual(encode(<<"a string\n">>), [{string, <<"a string\n">>}, end_json])},
|
{"naked string", ?_assertEqual(encode(<<"a string\n">>), [{string, <<"a string\n">>}, end_json])},
|
||||||
{"escaped naked string", ?_assertEqual(encode(<<"a string\n">>, [json_escape]), [{string, <<"a string\\n">>}, end_json])},
|
{"escaped naked string", ?_assertEqual(encode(<<"a string\n">>, [escaped_strings]), [{string, <<"a string\\n">>}, end_json])},
|
||||||
{"naked integer", ?_assertEqual(encode(123), [{integer, 123}, end_json])},
|
{"naked integer", ?_assertEqual(encode(123), [{integer, 123}, end_json])},
|
||||||
{"naked float", ?_assertEqual(encode(1.23), [{float, 1.23}, end_json])},
|
{"naked float", ?_assertEqual(encode(1.23), [{float, 1.23}, end_json])},
|
||||||
{"naked literal", ?_assertEqual(encode(null), [{literal, null}, end_json])},
|
{"naked literal", ?_assertEqual(encode(null), [{literal, null}, end_json])},
|
||||||
|
@ -488,13 +784,130 @@ encode_test_() ->
|
||||||
].
|
].
|
||||||
|
|
||||||
|
|
||||||
|
pre_encoders_test_() ->
|
||||||
|
Term = [
|
||||||
|
{<<"object">>, [
|
||||||
|
{<<"literals">>, [true, false, null]},
|
||||||
|
{<<"strings">>, [<<"foo">>, <<"bar">>, <<"baz">>]},
|
||||||
|
{<<"numbers">>, [1, 1.0, 1.0e0]}
|
||||||
|
]}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{"no pre encode", ?_assertEqual(
|
||||||
|
encode(Term, []),
|
||||||
|
[
|
||||||
|
start_object,
|
||||||
|
{key, <<"object">>}, start_object,
|
||||||
|
{key, <<"literals">>}, start_array,
|
||||||
|
{literal, true}, {literal, false}, {literal, null},
|
||||||
|
end_array,
|
||||||
|
{key, <<"strings">>}, start_array,
|
||||||
|
{string, <<"foo">>}, {string, <<"bar">>}, {string, <<"baz">>},
|
||||||
|
end_array,
|
||||||
|
{key, <<"numbers">>}, start_array,
|
||||||
|
{integer, 1}, {float, 1.0}, {float, 1.0},
|
||||||
|
end_array,
|
||||||
|
end_object,
|
||||||
|
end_object,
|
||||||
|
end_json
|
||||||
|
]
|
||||||
|
)},
|
||||||
|
{"replace lists with empty lists", ?_assertEqual(
|
||||||
|
encode(Term, [{pre_encode, fun(V) -> case V of [{_,_}|_] -> V; [{}] -> V; V when is_list(V) -> []; _ -> V end end}]),
|
||||||
|
[
|
||||||
|
start_object,
|
||||||
|
{key, <<"object">>}, start_object,
|
||||||
|
{key, <<"literals">>}, start_array, end_array,
|
||||||
|
{key, <<"strings">>}, start_array, end_array,
|
||||||
|
{key, <<"numbers">>}, start_array, end_array,
|
||||||
|
end_object,
|
||||||
|
end_object,
|
||||||
|
end_json
|
||||||
|
]
|
||||||
|
)},
|
||||||
|
{"replace objects with empty objects", ?_assertEqual(
|
||||||
|
encode(Term, [{pre_encode, fun(V) -> case V of [{_,_}|_] -> [{}]; _ -> V end end}]),
|
||||||
|
[
|
||||||
|
start_object,
|
||||||
|
end_object,
|
||||||
|
end_json
|
||||||
|
]
|
||||||
|
)},
|
||||||
|
{"replace all non-list values with false", ?_assertEqual(
|
||||||
|
encode(Term, [{pre_encode, fun(V) when is_list(V) -> V; (_) -> false end}]),
|
||||||
|
[
|
||||||
|
start_object,
|
||||||
|
{key, <<"object">>}, start_object,
|
||||||
|
{key, <<"literals">>}, start_array,
|
||||||
|
{literal, false}, {literal, false}, {literal, false},
|
||||||
|
end_array,
|
||||||
|
{key, <<"strings">>}, start_array,
|
||||||
|
{literal, false}, {literal, false}, {literal, false},
|
||||||
|
end_array,
|
||||||
|
{key, <<"numbers">>}, start_array,
|
||||||
|
{literal, false}, {literal, false}, {literal, false},
|
||||||
|
end_array,
|
||||||
|
end_object,
|
||||||
|
end_object,
|
||||||
|
end_json
|
||||||
|
]
|
||||||
|
)},
|
||||||
|
{"replace all atoms with atom_to_list", ?_assertEqual(
|
||||||
|
encode(Term, [{pre_encode, fun(V) when is_atom(V) -> unicode:characters_to_binary(atom_to_list(V)); (V) -> V end}]),
|
||||||
|
[
|
||||||
|
start_object,
|
||||||
|
{key, <<"object">>}, start_object,
|
||||||
|
{key, <<"literals">>}, start_array,
|
||||||
|
{string, <<"true">>}, {string, <<"false">>}, {string, <<"null">>},
|
||||||
|
end_array,
|
||||||
|
{key, <<"strings">>}, start_array,
|
||||||
|
{string, <<"foo">>}, {string, <<"bar">>}, {string, <<"baz">>},
|
||||||
|
end_array,
|
||||||
|
{key, <<"numbers">>}, start_array,
|
||||||
|
{integer, 1}, {float, 1.0}, {float, 1.0},
|
||||||
|
end_array,
|
||||||
|
end_object,
|
||||||
|
end_object,
|
||||||
|
end_json
|
||||||
|
]
|
||||||
|
)}
|
||||||
|
].
|
||||||
|
|
||||||
|
|
||||||
|
escapes_test_() ->
|
||||||
|
[
|
||||||
|
{"backspace escape", ?_assertEqual(encode(<<"\b">>, [escaped_strings]), [{string, <<"\\b">>}, end_json])},
|
||||||
|
{"formfeed escape", ?_assertEqual(encode(<<"\f">>, [escaped_strings]), [{string, <<"\\f">>}, end_json])},
|
||||||
|
{"newline escape", ?_assertEqual(encode(<<"\n">>, [escaped_strings]), [{string, <<"\\n">>}, end_json])},
|
||||||
|
{"carriage return escape", ?_assertEqual(encode(<<"\r">>, [escaped_strings]), [{string, <<"\\r">>}, end_json])},
|
||||||
|
{"tab escape", ?_assertEqual(encode(<<"\t">>, [escaped_strings]), [{string, <<"\\t">>}, end_json])},
|
||||||
|
{"quote escape", ?_assertEqual(encode(<<"\"">>, [escaped_strings]), [{string, <<"\\\"">>}, end_json])},
|
||||||
|
{"single quote escape", ?_assertEqual(encode(<<"'">>, [escaped_strings, single_quoted_strings]), [{string, <<"\\'">>}, end_json])},
|
||||||
|
{"no single quote escape", ?_assertEqual(encode(<<"'">>, [escaped_strings]), [{string, <<"'">>}, end_json])},
|
||||||
|
{"forward slash escape", ?_assertEqual(encode(<<"/">>, [escaped_strings, escaped_forward_slashes]), [{string, <<"\\/">>}, end_json])},
|
||||||
|
{"no forward slash escape", ?_assertEqual(encode(<<"/">>, [escaped_strings]), [{string, <<"/">>}, end_json])},
|
||||||
|
{"back slash escape", ?_assertEqual(encode(<<"\\">>, [escaped_strings]), [{string, <<"\\\\">>}, end_json])},
|
||||||
|
{"jsonp escape", ?_assertEqual(
|
||||||
|
encode(<<16#2028/utf8, 16#2029/utf8>>, [escaped_strings]),
|
||||||
|
[{string, <<"\\u2028\\u2029">>}, end_json]
|
||||||
|
)},
|
||||||
|
{"no jsonp escape", ?_assertEqual(
|
||||||
|
encode(<<16#2028/utf8, 16#2029/utf8>>, [escaped_strings, unescaped_jsonp]),
|
||||||
|
[{string, <<16#2028/utf8, 16#2029/utf8>>}, end_json]
|
||||||
|
)},
|
||||||
|
{"control escape", ?_assertEqual(encode(<<0>>, [escaped_strings]), [{string, <<"\\u0000">>}, end_json])},
|
||||||
|
{"dirty strings", ?_assertEqual(encode(<<"\n">>, [escaped_strings, dirty_strings]), [{string, <<"\n">>}, end_json])},
|
||||||
|
{"ignore bad escapes", ?_assertEqual(encode(<<"\\x25">>, [escaped_strings, ignored_bad_escapes]), [{string, <<"\\\\x25">>}, end_json])}
|
||||||
|
].
|
||||||
|
|
||||||
|
|
||||||
surrogates_test_() ->
|
surrogates_test_() ->
|
||||||
[
|
[
|
||||||
{"surrogates - badjson",
|
{"surrogates - badjson",
|
||||||
?_assertEqual(check_bad(surrogates()), [])
|
?_assert(check_bad(surrogates()))
|
||||||
},
|
},
|
||||||
{"surrogates - replaced",
|
{"surrogates - replaced",
|
||||||
?_assertEqual(check_replaced(surrogates()), [])
|
?_assert(check_replaced(surrogates()))
|
||||||
}
|
}
|
||||||
].
|
].
|
||||||
|
|
||||||
|
@ -502,10 +915,25 @@ surrogates_test_() ->
|
||||||
good_characters_test_() ->
|
good_characters_test_() ->
|
||||||
[
|
[
|
||||||
{"acceptable codepoints",
|
{"acceptable codepoints",
|
||||||
?_assertEqual(check_good(good()), [])
|
?_assert(check_good(good()))
|
||||||
|
},
|
||||||
|
{"acceptable codepoints - escaped_strings",
|
||||||
|
?_assert(check_good(good(), [escaped_strings]))
|
||||||
|
},
|
||||||
|
{"acceptable codepoints - replaced_bad_utf8",
|
||||||
|
?_assert(check_good(good(), [escaped_strings]))
|
||||||
|
},
|
||||||
|
{"acceptable codepoints - escaped_strings + replaced_bad_utf8",
|
||||||
|
?_assert(check_good(good(), [escaped_strings, replaced_bad_utf8]))
|
||||||
},
|
},
|
||||||
{"acceptable extended",
|
{"acceptable extended",
|
||||||
?_assertEqual(check_good(good_extended()), [])
|
?_assert(check_good(good_extended()))
|
||||||
|
},
|
||||||
|
{"acceptable extended - escaped_strings",
|
||||||
|
?_assert(check_good(good_extended(), [escaped_strings]))
|
||||||
|
},
|
||||||
|
{"acceptable extended - escaped_strings",
|
||||||
|
?_assert(check_good(good_extended(), [replaced_bad_utf8]))
|
||||||
}
|
}
|
||||||
].
|
].
|
||||||
|
|
||||||
|
@ -513,10 +941,10 @@ good_characters_test_() ->
|
||||||
reserved_test_() ->
|
reserved_test_() ->
|
||||||
[
|
[
|
||||||
{"reserved noncharacters - badjson",
|
{"reserved noncharacters - badjson",
|
||||||
?_assertEqual(check_bad(reserved_space()), [])
|
?_assert(check_bad(reserved_space()))
|
||||||
},
|
},
|
||||||
{"reserved noncharacters - replaced",
|
{"reserved noncharacters - replaced",
|
||||||
?_assertEqual(check_replaced(reserved_space()), [])
|
?_assert(check_replaced(reserved_space()))
|
||||||
}
|
}
|
||||||
].
|
].
|
||||||
|
|
||||||
|
@ -524,10 +952,10 @@ reserved_test_() ->
|
||||||
noncharacters_test_() ->
|
noncharacters_test_() ->
|
||||||
[
|
[
|
||||||
{"noncharacters - badjson",
|
{"noncharacters - badjson",
|
||||||
?_assertEqual(check_bad(noncharacters()), [])
|
?_assert(check_bad(noncharacters()))
|
||||||
},
|
},
|
||||||
{"noncharacters - replaced",
|
{"noncharacters - replaced",
|
||||||
?_assertEqual(check_replaced(noncharacters()), [])
|
?_assert(check_replaced(noncharacters()))
|
||||||
}
|
}
|
||||||
].
|
].
|
||||||
|
|
||||||
|
@ -535,31 +963,32 @@ noncharacters_test_() ->
|
||||||
extended_noncharacters_test_() ->
|
extended_noncharacters_test_() ->
|
||||||
[
|
[
|
||||||
{"extended noncharacters - badjson",
|
{"extended noncharacters - badjson",
|
||||||
?_assertEqual(check_bad(extended_noncharacters()), [])
|
?_assert(check_bad(extended_noncharacters()))
|
||||||
},
|
},
|
||||||
{"extended noncharacters - replaced",
|
{"extended noncharacters - replaced",
|
||||||
?_assertEqual(check_replaced(extended_noncharacters()), [])
|
?_assert(check_replaced(extended_noncharacters()))
|
||||||
}
|
}
|
||||||
].
|
].
|
||||||
|
|
||||||
|
|
||||||
check_bad(List) ->
|
check_bad(List) ->
|
||||||
lists:dropwhile(fun({_, {error, badjson}}) -> true ; (_) -> false end,
|
[] == lists:dropwhile(fun({_, {error, badjson}}) -> true ; (_) -> false end,
|
||||||
check(List, [], [])
|
check(List, [], [])
|
||||||
).
|
).
|
||||||
|
|
||||||
|
|
||||||
check_replaced(List) ->
|
check_replaced(List) ->
|
||||||
lists:dropwhile(fun({_, [{string, <<16#fffd/utf8>>}|_]}) -> true
|
[] == lists:dropwhile(fun({_, [{string, <<16#fffd/utf8>>}|_]}) -> true ; (_) -> false
|
||||||
; (_) -> false
|
|
||||||
end,
|
end,
|
||||||
check(List, [loose_unicode], [])
|
check(List, [replaced_bad_utf8], [])
|
||||||
).
|
).
|
||||||
|
|
||||||
|
|
||||||
check_good(List) ->
|
check_good(List) -> check_good(List, []).
|
||||||
lists:dropwhile(fun({_, [{string, _}|_]}) -> true ; (_) -> false end,
|
|
||||||
check(List, [], [])
|
check_good(List, Opts) ->
|
||||||
|
[] == lists:dropwhile(fun({_, [{string, _}|_]}) -> true ; (_) -> false end,
|
||||||
|
check(List, Opts, [])
|
||||||
).
|
).
|
||||||
|
|
||||||
|
|
||||||
|
@ -571,7 +1000,6 @@ check([H|T], Opts, Acc) ->
|
||||||
|
|
||||||
noncharacters() -> lists:seq(16#fffe, 16#ffff).
|
noncharacters() -> lists:seq(16#fffe, 16#ffff).
|
||||||
|
|
||||||
|
|
||||||
extended_noncharacters() ->
|
extended_noncharacters() ->
|
||||||
[16#1fffe, 16#1ffff, 16#2fffe, 16#2ffff]
|
[16#1fffe, 16#1ffff, 16#2fffe, 16#2ffff]
|
||||||
++ [16#3fffe, 16#3ffff, 16#4fffe, 16#4ffff]
|
++ [16#3fffe, 16#3ffff, 16#4fffe, 16#4ffff]
|
||||||
|
@ -582,16 +1010,12 @@ extended_noncharacters() ->
|
||||||
++ [16#dfffe, 16#dffff, 16#efffe, 16#effff]
|
++ [16#dfffe, 16#dffff, 16#efffe, 16#effff]
|
||||||
++ [16#ffffe, 16#fffff, 16#10fffe, 16#10ffff].
|
++ [16#ffffe, 16#fffff, 16#10fffe, 16#10ffff].
|
||||||
|
|
||||||
|
|
||||||
surrogates() -> lists:seq(16#d800, 16#dfff).
|
surrogates() -> lists:seq(16#d800, 16#dfff).
|
||||||
|
|
||||||
|
|
||||||
reserved_space() -> lists:seq(16#fdd0, 16#fdef).
|
reserved_space() -> lists:seq(16#fdd0, 16#fdef).
|
||||||
|
|
||||||
|
|
||||||
good() -> lists:seq(16#0000, 16#d7ff) ++ lists:seq(16#e000, 16#fdcf) ++ lists:seq(16#fdf0, 16#fffd).
|
good() -> lists:seq(16#0000, 16#d7ff) ++ lists:seq(16#e000, 16#fdcf) ++ lists:seq(16#fdf0, 16#fffd).
|
||||||
|
|
||||||
|
|
||||||
good_extended() -> [16#10000, 16#20000, 16#30000, 16#40000, 16#50000,
|
good_extended() -> [16#10000, 16#20000, 16#30000, 16#40000, 16#50000,
|
||||||
16#60000, 16#70000, 16#80000, 16#90000, 16#a0000,
|
16#60000, 16#70000, 16#80000, 16#90000, 16#a0000,
|
||||||
16#b0000, 16#c0000, 16#d0000, 16#e0000, 16#f0000
|
16#b0000, 16#c0000, 16#d0000, 16#e0000, 16#f0000
|
||||||
|
|
|
@ -1,11 +1,12 @@
|
||||||
-record(opts, {
|
-record(opts, {
|
||||||
loose_unicode = false,
|
replaced_bad_utf8 = false,
|
||||||
escape_forward_slash = false,
|
escaped_forward_slashes = false,
|
||||||
explicit_end = false,
|
single_quoted_strings = false,
|
||||||
single_quotes = false,
|
unescaped_jsonp = false,
|
||||||
no_jsonp_escapes = false,
|
|
||||||
comments = false,
|
comments = false,
|
||||||
json_escape = false,
|
escaped_strings = false,
|
||||||
dirty_strings = false,
|
dirty_strings = false,
|
||||||
ignore_bad_escapes = false
|
ignored_bad_escapes = false,
|
||||||
|
explicit_end = false,
|
||||||
|
pre_encode = false
|
||||||
}).
|
}).
|
|
@ -39,13 +39,13 @@
|
||||||
-spec to_json(Source::any(), Opts::opts()) -> binary().
|
-spec to_json(Source::any(), Opts::opts()) -> binary().
|
||||||
|
|
||||||
to_json(Source, Opts) when is_list(Opts) ->
|
to_json(Source, Opts) when is_list(Opts) ->
|
||||||
(jsx:encoder(?MODULE, Opts, jsx_utils:extract_opts([json_escape] ++ Opts)))(Source).
|
(jsx:encoder(?MODULE, Opts, jsx_utils:extract_opts(Opts ++ [escaped_strings])))(Source).
|
||||||
|
|
||||||
|
|
||||||
-spec format(Source::binary(), Opts::opts()) -> binary().
|
-spec format(Source::binary(), Opts::opts()) -> binary().
|
||||||
|
|
||||||
format(Source, Opts) when is_binary(Source) andalso is_list(Opts) ->
|
format(Source, Opts) when is_binary(Source) andalso is_list(Opts) ->
|
||||||
(jsx:decoder(?MODULE, Opts, jsx_utils:extract_opts([json_escape] ++ Opts)))(Source).
|
(jsx:decoder(?MODULE, Opts, jsx_utils:extract_opts(Opts ++ [escaped_strings])))(Source).
|
||||||
|
|
||||||
|
|
||||||
parse_opts(Opts) -> parse_opts(Opts, #opts{}).
|
parse_opts(Opts) -> parse_opts(Opts, #opts{}).
|
||||||
|
@ -142,7 +142,7 @@ encode(literal, Literal, _Opts) ->
|
||||||
encode(integer, Integer, _Opts) ->
|
encode(integer, Integer, _Opts) ->
|
||||||
erlang:integer_to_list(Integer);
|
erlang:integer_to_list(Integer);
|
||||||
encode(float, Float, _Opts) ->
|
encode(float, Float, _Opts) ->
|
||||||
nicedecimal:format(Float).
|
[Output] = io_lib:format("~p", [Float]), Output.
|
||||||
|
|
||||||
|
|
||||||
space(Opts) ->
|
space(Opts) ->
|
||||||
|
@ -176,24 +176,12 @@ indent_or_space(Opts) ->
|
||||||
-ifdef(TEST).
|
-ifdef(TEST).
|
||||||
-include_lib("eunit/include/eunit.hrl").
|
-include_lib("eunit/include/eunit.hrl").
|
||||||
|
|
||||||
setup_nicedecimal_meck(Return) ->
|
|
||||||
ok = meck:new(nicedecimal),
|
|
||||||
ok = meck:expect(nicedecimal, format, fun(1.23) -> Return end).
|
|
||||||
|
|
||||||
teardown_nicedecimal_meck(_) ->
|
|
||||||
?assert(meck:validate(nicedecimal)),
|
|
||||||
ok = meck:unload(nicedecimal).
|
|
||||||
|
|
||||||
basic_format_test_() ->
|
basic_format_test_() ->
|
||||||
[
|
[
|
||||||
{"empty object", ?_assertEqual(format(<<"{}">>, []), <<"{}">>)},
|
{"empty object", ?_assertEqual(format(<<"{}">>, []), <<"{}">>)},
|
||||||
{"empty array", ?_assertEqual(format(<<"[]">>, []), <<"[]">>)},
|
{"empty array", ?_assertEqual(format(<<"[]">>, []), <<"[]">>)},
|
||||||
{"naked integer", ?_assertEqual(format(<<"123">>, []), <<"123">>)},
|
{"naked integer", ?_assertEqual(format(<<"123">>, []), <<"123">>)},
|
||||||
{foreach,
|
{"naked float", ?_assertEqual(format(<<"1.23">>, []), <<"1.23">>)},
|
||||||
fun() -> setup_nicedecimal_meck(<<"1.23">>) end,
|
|
||||||
fun(R) -> teardown_nicedecimal_meck(R) end,
|
|
||||||
[{"naked float", ?_assertEqual(format(<<"1.23">>, []), <<"1.23">>)}]
|
|
||||||
},
|
|
||||||
{"naked string", ?_assertEqual(format(<<"\"hi\"">>, []), <<"\"hi\"">>)},
|
{"naked string", ?_assertEqual(format(<<"\"hi\"">>, []), <<"\"hi\"">>)},
|
||||||
{"naked string with control character", ?_assertEqual(
|
{"naked string with control character", ?_assertEqual(
|
||||||
format(<<"\"hi\\n\"">>, []), <<"\"hi\\n\"">>
|
format(<<"\"hi\\n\"">>, []), <<"\"hi\\n\"">>
|
||||||
|
@ -238,11 +226,7 @@ basic_to_json_test_() ->
|
||||||
{"empty object", ?_assertEqual(to_json([{}], []), <<"{}">>)},
|
{"empty object", ?_assertEqual(to_json([{}], []), <<"{}">>)},
|
||||||
{"empty array", ?_assertEqual(to_json([], []), <<"[]">>)},
|
{"empty array", ?_assertEqual(to_json([], []), <<"[]">>)},
|
||||||
{"naked integer", ?_assertEqual(to_json(123, []), <<"123">>)},
|
{"naked integer", ?_assertEqual(to_json(123, []), <<"123">>)},
|
||||||
{foreach,
|
{"naked float", ?_assertEqual(to_json(1.23, []) , <<"1.23">>)},
|
||||||
fun() -> setup_nicedecimal_meck(<<"1.23">>) end,
|
|
||||||
fun(R) -> teardown_nicedecimal_meck(R) end,
|
|
||||||
[{"naked float", ?_assertEqual(to_json(1.23, []) , <<"1.23">>)}]
|
|
||||||
},
|
|
||||||
{"naked string", ?_assertEqual(to_json(<<"hi">>, []), <<"\"hi\"">>)},
|
{"naked string", ?_assertEqual(to_json(<<"hi">>, []), <<"\"hi\"">>)},
|
||||||
{"naked string with control character", ?_assertEqual(
|
{"naked string with control character", ?_assertEqual(
|
||||||
to_json(<<"hi\n">>, []), <<"\"hi\\n\"">>
|
to_json(<<"hi\n">>, []), <<"\"hi\\n\"">>
|
||||||
|
@ -316,14 +300,10 @@ opts_test_() ->
|
||||||
format(<<"{\"a\":true,\"b\":true,\"c\":true}">>, [{space, 2}]),
|
format(<<"{\"a\":true,\"b\":true,\"c\":true}">>, [{space, 2}]),
|
||||||
<<"{\"a\": true, \"b\": true, \"c\": true}">>
|
<<"{\"a\": true, \"b\": true, \"c\": true}">>
|
||||||
)},
|
)},
|
||||||
{foreach,
|
{"array indent", ?_assertEqual(
|
||||||
fun() -> setup_nicedecimal_meck(<<"1.23">>) end,
|
format(<<"[1.23, 1.23, 1.23]">>, [{indent, 2}]),
|
||||||
fun(R) -> teardown_nicedecimal_meck(R) end,
|
<<"[\n 1.23,\n 1.23,\n 1.23\n]">>
|
||||||
[{"array indent", ?_assertEqual(
|
)},
|
||||||
format(<<"[1.23, 1.23, 1.23]">>, [{indent, 2}]),
|
|
||||||
<<"[\n 1.23,\n 1.23,\n 1.23\n]">>
|
|
||||||
)}]
|
|
||||||
},
|
|
||||||
{"object indent", ?_assertEqual(
|
{"object indent", ?_assertEqual(
|
||||||
format(<<"{\"a\":true,\"b\":true,\"c\":true}">>, [{indent, 2}]),
|
format(<<"{\"a\":true,\"b\":true,\"c\":true}">>, [{indent, 2}]),
|
||||||
<<"{\n \"a\":true,\n \"b\":true,\n \"c\":true\n}">>
|
<<"{\n \"a\":true,\n \"b\":true,\n \"c\":true\n}">>
|
||||||
|
|
|
@ -28,7 +28,8 @@
|
||||||
|
|
||||||
|
|
||||||
-record(opts, {
|
-record(opts, {
|
||||||
labels = binary
|
labels = binary,
|
||||||
|
post_decode = false
|
||||||
}).
|
}).
|
||||||
|
|
||||||
-type opts() :: list().
|
-type opts() :: list().
|
||||||
|
@ -49,7 +50,6 @@ to_term(Source, Opts) when is_list(Opts) ->
|
||||||
(jsx:decoder(?MODULE, Opts, jsx_utils:extract_opts(Opts)))(Source).
|
(jsx:decoder(?MODULE, Opts, jsx_utils:extract_opts(Opts)))(Source).
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
parse_opts(Opts) -> parse_opts(Opts, #opts{}).
|
parse_opts(Opts) -> parse_opts(Opts, #opts{}).
|
||||||
|
|
||||||
parse_opts([{labels, Val}|Rest], Opts)
|
parse_opts([{labels, Val}|Rest], Opts)
|
||||||
|
@ -57,42 +57,43 @@ parse_opts([{labels, Val}|Rest], Opts)
|
||||||
parse_opts(Rest, Opts#opts{labels = Val});
|
parse_opts(Rest, Opts#opts{labels = Val});
|
||||||
parse_opts([labels|Rest], Opts) ->
|
parse_opts([labels|Rest], Opts) ->
|
||||||
parse_opts(Rest, Opts#opts{labels = binary});
|
parse_opts(Rest, Opts#opts{labels = binary});
|
||||||
|
parse_opts([{post_decode, F}|Rest], Opts=#opts{post_decode=false}) when is_function(F, 1) ->
|
||||||
|
parse_opts(Rest, Opts#opts{post_decode=F});
|
||||||
|
parse_opts([{post_decode, _}|_] = Options, Opts) ->
|
||||||
|
erlang:error(badarg, [Options, Opts]);
|
||||||
parse_opts([_|Rest], Opts) ->
|
parse_opts([_|Rest], Opts) ->
|
||||||
parse_opts(Rest, Opts);
|
parse_opts(Rest, Opts);
|
||||||
parse_opts([], Opts) ->
|
parse_opts([], Opts) ->
|
||||||
Opts.
|
Opts.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
init(Opts) -> {[[]], parse_opts(Opts)}.
|
init(Opts) -> {[[]], parse_opts(Opts)}.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
handle_event(end_json, {[[Terms]], _Opts}) -> Terms;
|
handle_event(end_json, {[[Terms]], _Opts}) -> Terms;
|
||||||
|
|
||||||
handle_event(start_object, {Terms, Opts}) -> {[[]|Terms], Opts};
|
handle_event(start_object, {Terms, Opts}) -> {[[]|Terms], Opts};
|
||||||
handle_event(end_object, {[[], {key, Key}, Last|Terms], Opts}) ->
|
handle_event(end_object, {[[], {key, Key}, Last|Terms], Opts}) ->
|
||||||
{[[{Key, [{}]}] ++ Last] ++ Terms, Opts};
|
{[[{Key, post_decode([{}], Opts)}] ++ Last] ++ Terms, Opts};
|
||||||
handle_event(end_object, {[Object, {key, Key}, Last|Terms], Opts}) ->
|
handle_event(end_object, {[Object, {key, Key}, Last|Terms], Opts}) ->
|
||||||
{[[{Key, lists:reverse(Object)}] ++ Last] ++ Terms, Opts};
|
{[[{Key, post_decode(lists:reverse(Object), Opts)}] ++ Last] ++ Terms, Opts};
|
||||||
handle_event(end_object, {[[], Last|Terms], Opts}) ->
|
handle_event(end_object, {[[], Last|Terms], Opts}) ->
|
||||||
{[[[{}]] ++ Last] ++ Terms, Opts};
|
{[[post_decode([{}], Opts)] ++ Last] ++ Terms, Opts};
|
||||||
handle_event(end_object, {[Object, Last|Terms], Opts}) ->
|
handle_event(end_object, {[Object, Last|Terms], Opts}) ->
|
||||||
{[[lists:reverse(Object)] ++ Last] ++ Terms, Opts};
|
{[[post_decode(lists:reverse(Object), Opts)] ++ Last] ++ Terms, Opts};
|
||||||
|
|
||||||
handle_event(start_array, {Terms, Opts}) -> {[[]|Terms], Opts};
|
handle_event(start_array, {Terms, Opts}) -> {[[]|Terms], Opts};
|
||||||
handle_event(end_array, {[List, {key, Key}, Last|Terms], Opts}) ->
|
handle_event(end_array, {[List, {key, Key}, Last|Terms], Opts}) ->
|
||||||
{[[{Key, lists:reverse(List)}] ++ Last] ++ Terms, Opts};
|
{[[{Key, post_decode(lists:reverse(List), Opts)}] ++ Last] ++ Terms, Opts};
|
||||||
handle_event(end_array, {[Current, Last|Terms], Opts}) ->
|
handle_event(end_array, {[Current, Last|Terms], Opts}) ->
|
||||||
{[[lists:reverse(Current)] ++ Last] ++ Terms, Opts};
|
{[[post_decode(lists:reverse(Current), Opts)] ++ Last] ++ Terms, Opts};
|
||||||
|
|
||||||
handle_event({key, Key}, {Terms, Opts}) -> {[{key, format_key(Key, Opts)}] ++ Terms, Opts};
|
handle_event({key, Key}, {Terms, Opts}) -> {[{key, format_key(Key, Opts)}] ++ Terms, Opts};
|
||||||
|
|
||||||
handle_event({_, Event}, {[{key, Key}, Last|Terms], Opts}) ->
|
handle_event({_, Event}, {[{key, Key}, Last|Terms], Opts}) ->
|
||||||
{[[{Key, Event}] ++ Last] ++ Terms, Opts};
|
{[[{Key, post_decode(Event, Opts)}] ++ Last] ++ Terms, Opts};
|
||||||
handle_event({_, Event}, {[Last|Terms], Opts}) ->
|
handle_event({_, Event}, {[Last|Terms], Opts}) ->
|
||||||
{[[Event] ++ Last] ++ Terms, Opts}.
|
{[[post_decode(Event, Opts)] ++ Last] ++ Terms, Opts}.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
format_key(Key, Opts) ->
|
format_key(Key, Opts) ->
|
||||||
|
@ -103,6 +104,9 @@ format_key(Key, Opts) ->
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
|
||||||
|
post_decode(Value, #opts{post_decode=false}) -> Value;
|
||||||
|
post_decode(Value, Opts) -> (Opts#opts.post_decode)(Value).
|
||||||
|
|
||||||
|
|
||||||
%% eunit tests
|
%% eunit tests
|
||||||
|
|
||||||
|
@ -181,5 +185,46 @@ naked_test_() ->
|
||||||
{"naked literal", ?_assertEqual(to_term(<<"true">>, []), true)},
|
{"naked literal", ?_assertEqual(to_term(<<"true">>, []), true)},
|
||||||
{"naked string", ?_assertEqual(to_term(<<"\"string\"">>, []), <<"string">>)}
|
{"naked string", ?_assertEqual(to_term(<<"\"string\"">>, []), <<"string">>)}
|
||||||
].
|
].
|
||||||
|
|
||||||
|
post_decoders_test_() ->
|
||||||
|
JSON = <<"{\"object\": {
|
||||||
|
\"literals\": [true, false, null],
|
||||||
|
\"strings\": [\"foo\", \"bar\", \"baz\"],
|
||||||
|
\"numbers\": [1, 1.0, 1e0]
|
||||||
|
}}">>,
|
||||||
|
[
|
||||||
|
{"no post_decode", ?_assertEqual(
|
||||||
|
to_term(JSON, []),
|
||||||
|
[{<<"object">>, [
|
||||||
|
{<<"literals">>, [true, false, null]},
|
||||||
|
{<<"strings">>, [<<"foo">>, <<"bar">>, <<"baz">>]},
|
||||||
|
{<<"numbers">>, [1, 1.0, 1.0]}
|
||||||
|
]}]
|
||||||
|
)},
|
||||||
|
{"replace arrays with empty arrays", ?_assertEqual(
|
||||||
|
to_term(JSON, [{post_decode, fun([T|_] = V) when is_tuple(T) -> V; (V) when is_list(V) -> []; (V) -> V end}]),
|
||||||
|
[{<<"object">>, [{<<"literals">>, []}, {<<"strings">>, []}, {<<"numbers">>, []}]}]
|
||||||
|
)},
|
||||||
|
{"replace objects with empty objects", ?_assertEqual(
|
||||||
|
to_term(JSON, [{post_decode, fun(V) when is_list(V) -> [{}]; (V) -> V end}]),
|
||||||
|
[{}]
|
||||||
|
)},
|
||||||
|
{"replace all non-list values with false", ?_assertEqual(
|
||||||
|
to_term(JSON, [{post_decode, fun(V) when is_list(V) -> V; (_) -> false end}]),
|
||||||
|
[{<<"object">>, [
|
||||||
|
{<<"literals">>, [false, false, false]},
|
||||||
|
{<<"strings">>, [false, false, false]},
|
||||||
|
{<<"numbers">>, [false, false, false]}
|
||||||
|
]}]
|
||||||
|
)},
|
||||||
|
{"atoms_to_strings", ?_assertEqual(
|
||||||
|
to_term(JSON, [{post_decode, fun(V) when is_atom(V) -> unicode:characters_to_binary(atom_to_list(V)); (V) -> V end}]),
|
||||||
|
[{<<"object">>, [
|
||||||
|
{<<"literals">>, [<<"true">>, <<"false">>, <<"null">>]},
|
||||||
|
{<<"strings">>, [<<"foo">>, <<"bar">>, <<"baz">>]},
|
||||||
|
{<<"numbers">>, [1, 1.0, 1.0]}
|
||||||
|
]}]
|
||||||
|
)}
|
||||||
|
].
|
||||||
|
|
||||||
-endif.
|
-endif.
|
||||||
|
|
|
@ -25,61 +25,92 @@
|
||||||
|
|
||||||
-export([parse_opts/1]).
|
-export([parse_opts/1]).
|
||||||
-export([extract_opts/1]).
|
-export([extract_opts/1]).
|
||||||
-export([json_escape/2]).
|
-export([json_escape_sequence/1]).
|
||||||
|
|
||||||
-include("jsx_opts.hrl").
|
-include("jsx_opts.hrl").
|
||||||
|
|
||||||
|
|
||||||
%% parsing of jsx opts
|
%% parsing of jsx opts
|
||||||
|
|
||||||
parse_opts(Opts) ->
|
parse_opts(Opts) ->
|
||||||
parse_opts(Opts, #opts{}).
|
parse_opts(Opts, #opts{}).
|
||||||
|
|
||||||
parse_opts([], Opts) ->
|
parse_opts([], Opts) ->
|
||||||
Opts;
|
Opts;
|
||||||
parse_opts([loose_unicode|Rest], Opts) ->
|
parse_opts([replaced_bad_utf8|Rest], Opts) ->
|
||||||
parse_opts(Rest, Opts#opts{loose_unicode=true});
|
parse_opts(Rest, Opts#opts{replaced_bad_utf8=true});
|
||||||
parse_opts([escape_forward_slash|Rest], Opts) ->
|
parse_opts([escaped_forward_slashes|Rest], Opts) ->
|
||||||
parse_opts(Rest, Opts#opts{escape_forward_slash=true});
|
parse_opts(Rest, Opts#opts{escaped_forward_slashes=true});
|
||||||
parse_opts([explicit_end|Rest], Opts) ->
|
parse_opts([explicit_end|Rest], Opts) ->
|
||||||
parse_opts(Rest, Opts#opts{explicit_end=true});
|
parse_opts(Rest, Opts#opts{explicit_end=true});
|
||||||
parse_opts([single_quotes|Rest], Opts) ->
|
parse_opts([single_quoted_strings|Rest], Opts) ->
|
||||||
parse_opts(Rest, Opts#opts{single_quotes=true});
|
parse_opts(Rest, Opts#opts{single_quoted_strings=true});
|
||||||
parse_opts([no_jsonp_escapes|Rest], Opts) ->
|
parse_opts([unescaped_jsonp|Rest], Opts) ->
|
||||||
parse_opts(Rest, Opts#opts{no_jsonp_escapes=true});
|
parse_opts(Rest, Opts#opts{unescaped_jsonp=true});
|
||||||
parse_opts([comments|Rest], Opts) ->
|
parse_opts([comments|Rest], Opts) ->
|
||||||
parse_opts(Rest, Opts#opts{comments=true});
|
parse_opts(Rest, Opts#opts{comments=true});
|
||||||
parse_opts([json_escape|Rest], Opts) ->
|
parse_opts([escaped_strings|Rest], Opts) ->
|
||||||
parse_opts(Rest, Opts#opts{json_escape=true});
|
parse_opts(Rest, Opts#opts{escaped_strings=true});
|
||||||
parse_opts([dirty_strings|Rest], Opts) ->
|
parse_opts([dirty_strings|Rest], Opts) ->
|
||||||
parse_opts(Rest, Opts#opts{dirty_strings=true});
|
parse_opts(Rest, Opts#opts{dirty_strings=true});
|
||||||
parse_opts([ignore_bad_escapes|Rest], Opts) ->
|
parse_opts([ignored_bad_escapes|Rest], Opts) ->
|
||||||
parse_opts(Rest, Opts#opts{ignore_bad_escapes=true});
|
parse_opts(Rest, Opts#opts{ignored_bad_escapes=true});
|
||||||
parse_opts([relax|Rest], Opts) ->
|
parse_opts([relax|Rest], Opts) ->
|
||||||
parse_opts(Rest, Opts#opts{
|
parse_opts(Rest, Opts#opts{
|
||||||
loose_unicode = true,
|
replaced_bad_utf8 = true,
|
||||||
single_quotes = true,
|
single_quoted_strings = true,
|
||||||
comments = true,
|
comments = true,
|
||||||
ignore_bad_escapes = true
|
ignored_bad_escapes = true
|
||||||
});
|
});
|
||||||
parse_opts(_, _) ->
|
parse_opts([{pre_encode, Encoder}|Rest] = Options, Opts) when is_function(Encoder, 1) ->
|
||||||
{error, badarg}.
|
case Opts#opts.pre_encode of
|
||||||
|
false -> parse_opts(Rest, Opts#opts{pre_encode=Encoder})
|
||||||
|
; _ -> erlang:error(badarg, [Options, Opts])
|
||||||
|
end;
|
||||||
|
%% deprecated flags
|
||||||
|
parse_opts([{pre_encoder, Encoder}|Rest] = Options, Opts) when is_function(Encoder, 1) ->
|
||||||
|
case Opts#opts.pre_encode of
|
||||||
|
false -> parse_opts(Rest, Opts#opts{pre_encode=Encoder})
|
||||||
|
; _ -> erlang:error(badarg, [Options, Opts])
|
||||||
|
end;
|
||||||
|
parse_opts([loose_unicode|Rest], Opts) ->
|
||||||
|
parse_opts(Rest, Opts#opts{replaced_bad_utf8=true});
|
||||||
|
parse_opts([escape_forward_slash|Rest], Opts) ->
|
||||||
|
parse_opts(Rest, Opts#opts{escaped_forward_slashes=true});
|
||||||
|
parse_opts([single_quotes|Rest], Opts) ->
|
||||||
|
parse_opts(Rest, Opts#opts{single_quoted_strings=true});
|
||||||
|
parse_opts([no_jsonp_escapes|Rest], Opts) ->
|
||||||
|
parse_opts(Rest, Opts#opts{unescaped_jsonp=true});
|
||||||
|
parse_opts([json_escape|Rest], Opts) ->
|
||||||
|
parse_opts(Rest, Opts#opts{escaped_strings=true});
|
||||||
|
parse_opts([ignore_bad_escapes|Rest], Opts) ->
|
||||||
|
parse_opts(Rest, Opts#opts{ignored_bad_escapes=true});
|
||||||
|
parse_opts(Options, Opts) ->
|
||||||
|
erlang:error(badarg, [Options, Opts]).
|
||||||
|
|
||||||
|
|
||||||
valid_flags() ->
|
valid_flags() ->
|
||||||
[
|
[
|
||||||
loose_unicode,
|
replaced_bad_utf8,
|
||||||
escape_forward_slash,
|
escaped_forward_slashes,
|
||||||
explicit_end,
|
single_quoted_strings,
|
||||||
single_quotes,
|
unescaped_jsonp,
|
||||||
no_jsonp_escapes,
|
|
||||||
comments,
|
comments,
|
||||||
json_escape,
|
escaped_strings,
|
||||||
dirty_strings,
|
dirty_strings,
|
||||||
ignore_bad_escapes,
|
ignored_bad_escapes,
|
||||||
relax
|
explicit_end,
|
||||||
|
relax,
|
||||||
|
pre_encode,
|
||||||
|
%% deprecated flags
|
||||||
|
pre_encoder, %% pre_encode
|
||||||
|
loose_unicode, %% replaced_bad_utf8
|
||||||
|
escape_forward_slash, %% escaped_forward_slashes
|
||||||
|
single_quotes, %% single_quotes_strings
|
||||||
|
no_jsonp_escapes, %% unescaped_jsonp
|
||||||
|
json_escape, %% escaped_strings
|
||||||
|
ignore_bad_escapes %% ignored_bad_escapes
|
||||||
].
|
].
|
||||||
|
|
||||||
|
|
||||||
extract_opts(Opts) ->
|
extract_opts(Opts) ->
|
||||||
extract_parser_opts(Opts, []).
|
extract_parser_opts(Opts, []).
|
||||||
|
@ -97,231 +128,10 @@ extract_parser_opts([K|Rest], Acc) ->
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
|
||||||
%% json string escaping, for utf8 binaries. escape the json control sequences to
|
|
||||||
%% their json equivalent, escape other control characters to \uXXXX sequences,
|
|
||||||
%% everything else should be a legal json string component
|
|
||||||
|
|
||||||
json_escape(String, Opts) when is_binary(String) ->
|
|
||||||
case Opts#opts.dirty_strings of
|
|
||||||
true -> String
|
|
||||||
; false -> json_escape(String, Opts, 0, size(String))
|
|
||||||
end.
|
|
||||||
|
|
||||||
|
|
||||||
-define(control_character(X),
|
|
||||||
<<H:L/binary, X, T/binary>> ->
|
|
||||||
json_escape(
|
|
||||||
<<H/binary, (unicode:characters_to_binary(json_escape_sequence(X)))/binary, T/binary>>,
|
|
||||||
Opts,
|
|
||||||
L + 6,
|
|
||||||
Len + 5
|
|
||||||
)
|
|
||||||
).
|
|
||||||
|
|
||||||
json_escape(Str, Opts, L, Len) when L < Len ->
|
|
||||||
case Str of
|
|
||||||
?control_character(0);
|
|
||||||
?control_character(1);
|
|
||||||
?control_character(2);
|
|
||||||
?control_character(3);
|
|
||||||
?control_character(4);
|
|
||||||
?control_character(5);
|
|
||||||
?control_character(6);
|
|
||||||
?control_character(7);
|
|
||||||
<<H:L/binary, $\b, T/binary>> -> json_escape(<<H/binary, $\\, $b, T/binary>>, Opts, L + 2, Len + 1);
|
|
||||||
<<H:L/binary, $\t, T/binary>> -> json_escape(<<H/binary, $\\, $t, T/binary>>, Opts, L + 2, Len + 1);
|
|
||||||
<<H:L/binary, $\n, T/binary>> -> json_escape(<<H/binary, $\\, $n, T/binary>>, Opts, L + 2, Len + 1);
|
|
||||||
?control_character(11);
|
|
||||||
<<H:L/binary, $\f, T/binary>> -> json_escape(<<H/binary, $\\, $f, T/binary>>, Opts, L + 2, Len + 1);
|
|
||||||
<<H:L/binary, $\r, T/binary>> -> json_escape(<<H/binary, $\\, $r, T/binary>>, Opts, L + 2, Len + 1);
|
|
||||||
?control_character(14);
|
|
||||||
?control_character(15);
|
|
||||||
?control_character(16);
|
|
||||||
?control_character(17);
|
|
||||||
?control_character(18);
|
|
||||||
?control_character(19);
|
|
||||||
?control_character(20);
|
|
||||||
?control_character(21);
|
|
||||||
?control_character(22);
|
|
||||||
?control_character(23);
|
|
||||||
?control_character(24);
|
|
||||||
?control_character(25);
|
|
||||||
?control_character(26);
|
|
||||||
?control_character(27);
|
|
||||||
?control_character(28);
|
|
||||||
?control_character(29);
|
|
||||||
?control_character(30);
|
|
||||||
?control_character(31);
|
|
||||||
<<_:L/binary, 32, _/binary>> -> json_escape(Str, Opts, L + 1, Len);
|
|
||||||
<<_:L/binary, 33, _/binary>> -> json_escape(Str, Opts, L + 1, Len);
|
|
||||||
<<H:L/binary, $\", T/binary>> -> json_escape(<<H/binary, $\\, $", T/binary>>, Opts, L + 2, Len + 1);
|
|
||||||
<<_:L/binary, 35, _/binary>> -> json_escape(Str, Opts, L + 1, Len);
|
|
||||||
<<_:L/binary, 36, _/binary>> -> json_escape(Str, Opts, L + 1, Len);
|
|
||||||
<<_:L/binary, 37, _/binary>> -> json_escape(Str, Opts, L + 1, Len);
|
|
||||||
<<_:L/binary, 38, _/binary>> -> json_escape(Str, Opts, L + 1, Len);
|
|
||||||
<<_:L/binary, 39, _/binary>> -> json_escape(Str, Opts, L + 1, Len);
|
|
||||||
<<_:L/binary, 40, _/binary>> -> json_escape(Str, Opts, L + 1, Len);
|
|
||||||
<<_:L/binary, 41, _/binary>> -> json_escape(Str, Opts, L + 1, Len);
|
|
||||||
<<_:L/binary, 42, _/binary>> -> json_escape(Str, Opts, L + 1, Len);
|
|
||||||
<<_:L/binary, 43, _/binary>> -> json_escape(Str, Opts, L + 1, Len);
|
|
||||||
<<_:L/binary, 44, _/binary>> -> json_escape(Str, Opts, L + 1, Len);
|
|
||||||
<<_:L/binary, 45, _/binary>> -> json_escape(Str, Opts, L + 1, Len);
|
|
||||||
<<_:L/binary, 46, _/binary>> -> json_escape(Str, Opts, L + 1, Len);
|
|
||||||
<<H:L/binary, $/, T/binary>> ->
|
|
||||||
case Opts#opts.escape_forward_slash of
|
|
||||||
true ->
|
|
||||||
json_escape(<<H/binary, $\\, $/, T/binary>>, Opts, L + 2, Len + 1);
|
|
||||||
false ->
|
|
||||||
json_escape(<<H/binary, $/, T/binary>>, Opts, L + 1, Len)
|
|
||||||
end;
|
|
||||||
<<_:L/binary, 48, _/binary>> -> json_escape(Str, Opts, L + 1, Len);
|
|
||||||
<<_:L/binary, 49, _/binary>> -> json_escape(Str, Opts, L + 1, Len);
|
|
||||||
<<_:L/binary, 50, _/binary>> -> json_escape(Str, Opts, L + 1, Len);
|
|
||||||
<<_:L/binary, 51, _/binary>> -> json_escape(Str, Opts, L + 1, Len);
|
|
||||||
<<_:L/binary, 52, _/binary>> -> json_escape(Str, Opts, L + 1, Len);
|
|
||||||
<<_:L/binary, 53, _/binary>> -> json_escape(Str, Opts, L + 1, Len);
|
|
||||||
<<_:L/binary, 54, _/binary>> -> json_escape(Str, Opts, L + 1, Len);
|
|
||||||
<<_:L/binary, 55, _/binary>> -> json_escape(Str, Opts, L + 1, Len);
|
|
||||||
<<_:L/binary, 56, _/binary>> -> json_escape(Str, Opts, L + 1, Len);
|
|
||||||
<<_:L/binary, 57, _/binary>> -> json_escape(Str, Opts, L + 1, Len);
|
|
||||||
<<_:L/binary, 58, _/binary>> -> json_escape(Str, Opts, L + 1, Len);
|
|
||||||
<<_:L/binary, 59, _/binary>> -> json_escape(Str, Opts, L + 1, Len);
|
|
||||||
<<_:L/binary, 60, _/binary>> -> json_escape(Str, Opts, L + 1, Len);
|
|
||||||
<<_:L/binary, 61, _/binary>> -> json_escape(Str, Opts, L + 1, Len);
|
|
||||||
<<_:L/binary, 62, _/binary>> -> json_escape(Str, Opts, L + 1, Len);
|
|
||||||
<<_:L/binary, 63, _/binary>> -> json_escape(Str, Opts, L + 1, Len);
|
|
||||||
<<_:L/binary, 64, _/binary>> -> json_escape(Str, Opts, L + 1, Len);
|
|
||||||
<<_:L/binary, 65, _/binary>> -> json_escape(Str, Opts, L + 1, Len);
|
|
||||||
<<_:L/binary, 66, _/binary>> -> json_escape(Str, Opts, L + 1, Len);
|
|
||||||
<<_:L/binary, 67, _/binary>> -> json_escape(Str, Opts, L + 1, Len);
|
|
||||||
<<_:L/binary, 68, _/binary>> -> json_escape(Str, Opts, L + 1, Len);
|
|
||||||
<<_:L/binary, 69, _/binary>> -> json_escape(Str, Opts, L + 1, Len);
|
|
||||||
<<_:L/binary, 70, _/binary>> -> json_escape(Str, Opts, L + 1, Len);
|
|
||||||
<<_:L/binary, 71, _/binary>> -> json_escape(Str, Opts, L + 1, Len);
|
|
||||||
<<_:L/binary, 72, _/binary>> -> json_escape(Str, Opts, L + 1, Len);
|
|
||||||
<<_:L/binary, 73, _/binary>> -> json_escape(Str, Opts, L + 1, Len);
|
|
||||||
<<_:L/binary, 74, _/binary>> -> json_escape(Str, Opts, L + 1, Len);
|
|
||||||
<<_:L/binary, 75, _/binary>> -> json_escape(Str, Opts, L + 1, Len);
|
|
||||||
<<_:L/binary, 76, _/binary>> -> json_escape(Str, Opts, L + 1, Len);
|
|
||||||
<<_:L/binary, 77, _/binary>> -> json_escape(Str, Opts, L + 1, Len);
|
|
||||||
<<_:L/binary, 78, _/binary>> -> json_escape(Str, Opts, L + 1, Len);
|
|
||||||
<<_:L/binary, 79, _/binary>> -> json_escape(Str, Opts, L + 1, Len);
|
|
||||||
<<_:L/binary, 80, _/binary>> -> json_escape(Str, Opts, L + 1, Len);
|
|
||||||
<<_:L/binary, 81, _/binary>> -> json_escape(Str, Opts, L + 1, Len);
|
|
||||||
<<_:L/binary, 82, _/binary>> -> json_escape(Str, Opts, L + 1, Len);
|
|
||||||
<<_:L/binary, 83, _/binary>> -> json_escape(Str, Opts, L + 1, Len);
|
|
||||||
<<_:L/binary, 84, _/binary>> -> json_escape(Str, Opts, L + 1, Len);
|
|
||||||
<<_:L/binary, 85, _/binary>> -> json_escape(Str, Opts, L + 1, Len);
|
|
||||||
<<_:L/binary, 86, _/binary>> -> json_escape(Str, Opts, L + 1, Len);
|
|
||||||
<<_:L/binary, 87, _/binary>> -> json_escape(Str, Opts, L + 1, Len);
|
|
||||||
<<_:L/binary, 88, _/binary>> -> json_escape(Str, Opts, L + 1, Len);
|
|
||||||
<<_:L/binary, 89, _/binary>> -> json_escape(Str, Opts, L + 1, Len);
|
|
||||||
<<_:L/binary, 90, _/binary>> -> json_escape(Str, Opts, L + 1, Len);
|
|
||||||
<<_:L/binary, 91, _/binary>> -> json_escape(Str, Opts, L + 1, Len);
|
|
||||||
<<H:L/binary, $\\, T/binary>> -> json_escape(<<H/binary, $\\, $\\, T/binary>>, Opts, L + 2, Len + 1);
|
|
||||||
<<_:L/binary, 93, _/binary>> -> json_escape(Str, Opts, L + 1, Len);
|
|
||||||
<<_:L/binary, 94, _/binary>> -> json_escape(Str, Opts, L + 1, Len);
|
|
||||||
<<_:L/binary, 95, _/binary>> -> json_escape(Str, Opts, L + 1, Len);
|
|
||||||
<<_:L/binary, 96, _/binary>> -> json_escape(Str, Opts, L + 1, Len);
|
|
||||||
<<_:L/binary, 97, _/binary>> -> json_escape(Str, Opts, L + 1, Len);
|
|
||||||
<<_:L/binary, 98, _/binary>> -> json_escape(Str, Opts, L + 1, Len);
|
|
||||||
<<_:L/binary, 99, _/binary>> -> json_escape(Str, Opts, L + 1, Len);
|
|
||||||
<<_:L/binary, 100, _/binary>> -> json_escape(Str, Opts, L + 1, Len);
|
|
||||||
<<_:L/binary, 101, _/binary>> -> json_escape(Str, Opts, L + 1, Len);
|
|
||||||
<<_:L/binary, 102, _/binary>> -> json_escape(Str, Opts, L + 1, Len);
|
|
||||||
<<_:L/binary, 103, _/binary>> -> json_escape(Str, Opts, L + 1, Len);
|
|
||||||
<<_:L/binary, 104, _/binary>> -> json_escape(Str, Opts, L + 1, Len);
|
|
||||||
<<_:L/binary, 105, _/binary>> -> json_escape(Str, Opts, L + 1, Len);
|
|
||||||
<<_:L/binary, 106, _/binary>> -> json_escape(Str, Opts, L + 1, Len);
|
|
||||||
<<_:L/binary, 107, _/binary>> -> json_escape(Str, Opts, L + 1, Len);
|
|
||||||
<<_:L/binary, 108, _/binary>> -> json_escape(Str, Opts, L + 1, Len);
|
|
||||||
<<_:L/binary, 109, _/binary>> -> json_escape(Str, Opts, L + 1, Len);
|
|
||||||
<<_:L/binary, 110, _/binary>> -> json_escape(Str, Opts, L + 1, Len);
|
|
||||||
<<_:L/binary, 111, _/binary>> -> json_escape(Str, Opts, L + 1, Len);
|
|
||||||
<<_:L/binary, 112, _/binary>> -> json_escape(Str, Opts, L + 1, Len);
|
|
||||||
<<_:L/binary, 113, _/binary>> -> json_escape(Str, Opts, L + 1, Len);
|
|
||||||
<<_:L/binary, 114, _/binary>> -> json_escape(Str, Opts, L + 1, Len);
|
|
||||||
<<_:L/binary, 115, _/binary>> -> json_escape(Str, Opts, L + 1, Len);
|
|
||||||
<<_:L/binary, 116, _/binary>> -> json_escape(Str, Opts, L + 1, Len);
|
|
||||||
<<_:L/binary, 117, _/binary>> -> json_escape(Str, Opts, L + 1, Len);
|
|
||||||
<<_:L/binary, 118, _/binary>> -> json_escape(Str, Opts, L + 1, Len);
|
|
||||||
<<_:L/binary, 119, _/binary>> -> json_escape(Str, Opts, L + 1, Len);
|
|
||||||
<<_:L/binary, 120, _/binary>> -> json_escape(Str, Opts, L + 1, Len);
|
|
||||||
<<_:L/binary, 121, _/binary>> -> json_escape(Str, Opts, L + 1, Len);
|
|
||||||
<<_:L/binary, 122, _/binary>> -> json_escape(Str, Opts, L + 1, Len);
|
|
||||||
<<_:L/binary, 123, _/binary>> -> json_escape(Str, Opts, L + 1, Len);
|
|
||||||
<<_:L/binary, 124, _/binary>> -> json_escape(Str, Opts, L + 1, Len);
|
|
||||||
<<_:L/binary, 125, _/binary>> -> json_escape(Str, Opts, L + 1, Len);
|
|
||||||
<<_:L/binary, 126, _/binary>> -> json_escape(Str, Opts, L + 1, Len);
|
|
||||||
<<_:L/binary, 127, _/binary>> -> json_escape(Str, Opts, L + 1, Len);
|
|
||||||
<<H:L/binary, 16#2028/utf8, T/binary>> ->
|
|
||||||
case Opts#opts.no_jsonp_escapes of
|
|
||||||
true ->
|
|
||||||
json_escape(<<H/binary, 16#2028/utf8, T/binary>>, Opts, L + 3, Len);
|
|
||||||
false ->
|
|
||||||
B = unicode:characters_to_binary(json_escape_sequence(16#2028)),
|
|
||||||
json_escape(<<H/binary, B/binary, T/binary>>, Opts, L + 6, Len + 3)
|
|
||||||
end;
|
|
||||||
<<H:L/binary, 16#2029/utf8, T/binary>> ->
|
|
||||||
case Opts#opts.no_jsonp_escapes of
|
|
||||||
true ->
|
|
||||||
json_escape(<<H/binary, 16#2029/utf8, T/binary>>, Opts, L + 3, Len);
|
|
||||||
false ->
|
|
||||||
B = unicode:characters_to_binary(json_escape_sequence(16#2029)),
|
|
||||||
json_escape(<<H/binary, B/binary, T/binary>>, Opts, L + 6, Len + 3)
|
|
||||||
end;
|
|
||||||
<<_:L/binary, X/utf8, _/binary>> when X < 16#0080 ->
|
|
||||||
json_escape(Str, Opts, L + 1, Len);
|
|
||||||
<<_:L/binary, X/utf8, _/binary>> when X < 16#0800 ->
|
|
||||||
json_escape(Str, Opts, L + 2, Len);
|
|
||||||
<<_:L/binary, X/utf8, _/binary>> when X < 16#dcff ->
|
|
||||||
json_escape(Str, Opts, L + 3, Len);
|
|
||||||
<<_:L/binary, X/utf8, _/binary>> when X > 16#dfff, X < 16#fdd0 ->
|
|
||||||
json_escape(Str, Opts, L + 3, Len);
|
|
||||||
<<_:L/binary, X/utf8, _/binary>> when X > 16#fdef, X < 16#fffe ->
|
|
||||||
json_escape(Str, Opts, L + 3, Len);
|
|
||||||
<<_:L/binary, X/utf8, _/binary>> when X >= 16#10000, X < 16#1fffe ->
|
|
||||||
json_escape(Str, Opts, L + 4, Len);
|
|
||||||
<<_:L/binary, X/utf8, _/binary>> when X >= 16#20000, X < 16#2fffe ->
|
|
||||||
json_escape(Str, Opts, L + 4, Len);
|
|
||||||
<<_:L/binary, X/utf8, _/binary>> when X >= 16#30000, X < 16#3fffe ->
|
|
||||||
json_escape(Str, Opts, L + 4, Len);
|
|
||||||
<<_:L/binary, X/utf8, _/binary>> when X >= 16#40000, X < 16#4fffe ->
|
|
||||||
json_escape(Str, Opts, L + 4, Len);
|
|
||||||
<<_:L/binary, X/utf8, _/binary>> when X >= 16#50000, X < 16#5fffe ->
|
|
||||||
json_escape(Str, Opts, L + 4, Len);
|
|
||||||
<<_:L/binary, X/utf8, _/binary>> when X >= 16#60000, X < 16#6fffe ->
|
|
||||||
json_escape(Str, Opts, L + 4, Len);
|
|
||||||
<<_:L/binary, X/utf8, _/binary>> when X >= 16#70000, X < 16#7fffe ->
|
|
||||||
json_escape(Str, Opts, L + 4, Len);
|
|
||||||
<<_:L/binary, X/utf8, _/binary>> when X >= 16#80000, X < 16#8fffe ->
|
|
||||||
json_escape(Str, Opts, L + 4, Len);
|
|
||||||
<<_:L/binary, X/utf8, _/binary>> when X >= 16#90000, X < 16#9fffe ->
|
|
||||||
json_escape(Str, Opts, L + 4, Len);
|
|
||||||
<<_:L/binary, X/utf8, _/binary>> when X >= 16#a0000, X < 16#afffe ->
|
|
||||||
json_escape(Str, Opts, L + 4, Len);
|
|
||||||
<<_:L/binary, X/utf8, _/binary>> when X >= 16#b0000, X < 16#bfffe ->
|
|
||||||
json_escape(Str, Opts, L + 4, Len);
|
|
||||||
<<_:L/binary, X/utf8, _/binary>> when X >= 16#c0000, X < 16#cfffe ->
|
|
||||||
json_escape(Str, Opts, L + 4, Len);
|
|
||||||
<<_:L/binary, X/utf8, _/binary>> when X >= 16#d0000, X < 16#dfffe ->
|
|
||||||
json_escape(Str, Opts, L + 4, Len);
|
|
||||||
<<_:L/binary, X/utf8, _/binary>> when X >= 16#e0000, X < 16#efffe ->
|
|
||||||
json_escape(Str, Opts, L + 4, Len);
|
|
||||||
<<_:L/binary, X/utf8, _/binary>> when X >= 16#f0000, X < 16#ffffe ->
|
|
||||||
json_escape(Str, Opts, L + 4, Len);
|
|
||||||
<<_:L/binary, X/utf8, _/binary>> when X >= 16#100000, X < 16#10fffe ->
|
|
||||||
json_escape(Str, Opts, L + 4, Len);
|
|
||||||
_ -> erlang:error(badarg, [Str, Opts])
|
|
||||||
end;
|
|
||||||
json_escape(Str, _, L, Len) when L =:= Len ->
|
|
||||||
Str.
|
|
||||||
|
|
||||||
|
|
||||||
%% convert a codepoint to it's \uXXXX equiv.
|
%% convert a codepoint to it's \uXXXX equiv.
|
||||||
json_escape_sequence(X) ->
|
json_escape_sequence(X) ->
|
||||||
<<A:4, B:4, C:4, D:4>> = <<X:16>>,
|
<<A:4, B:4, C:4, D:4>> = <<X:16>>,
|
||||||
unicode:characters_to_binary([$\\, $u, (to_hex(A)), (to_hex(B)), (to_hex(C)), (to_hex(D))]).
|
[$\\, $u, (to_hex(A)), (to_hex(B)), (to_hex(C)), (to_hex(D))].
|
||||||
|
|
||||||
|
|
||||||
to_hex(10) -> $a;
|
to_hex(10) -> $a;
|
||||||
|
@ -338,70 +148,36 @@ to_hex(X) -> X + 48. %% ascii "1" is [49], "2" is [50], etc...
|
||||||
-include_lib("eunit/include/eunit.hrl").
|
-include_lib("eunit/include/eunit.hrl").
|
||||||
|
|
||||||
|
|
||||||
binary_escape_test_() ->
|
json_escape_sequence_test_() ->
|
||||||
[
|
[
|
||||||
{"json string escaping",
|
{"json escape sequence test - 16#0000", ?_assertEqual(json_escape_sequence(16#0000), "\\u0000")},
|
||||||
?_assertEqual(
|
{"json escape sequence test - 16#abc", ?_assertEqual(json_escape_sequence(16#abc), "\\u0abc")},
|
||||||
json_escape(<<"\"\\\b\f\n\r\t">>, #opts{}),
|
{"json escape sequence test - 16#def", ?_assertEqual(json_escape_sequence(16#def), "\\u0def")}
|
||||||
<<"\\\"\\\\\\b\\f\\n\\r\\t">>
|
|
||||||
)
|
|
||||||
},
|
|
||||||
{"json string hex escape",
|
|
||||||
?_assertEqual(
|
|
||||||
json_escape(<<0, 1, 2, 3, 11, 26, 30, 31>>, #opts{}),
|
|
||||||
<<"\\u0000\\u0001\\u0002\\u0003\\u000b\\u001a\\u001e\\u001f">>
|
|
||||||
)
|
|
||||||
},
|
|
||||||
{"jsonp protection",
|
|
||||||
?_assertEqual(
|
|
||||||
json_escape(<<226, 128, 168, 226, 128, 169>>, #opts{}),
|
|
||||||
<<"\\u2028\\u2029">>
|
|
||||||
)
|
|
||||||
},
|
|
||||||
{"no jsonp escapes",
|
|
||||||
?_assertEqual(
|
|
||||||
json_escape(<<226, 128, 168, 226, 128, 169>>, #opts{no_jsonp_escapes=true}),
|
|
||||||
<<226, 128, 168, 226, 128, 169>>
|
|
||||||
)
|
|
||||||
},
|
|
||||||
{"microsoft i hate your date format",
|
|
||||||
?_assertEqual(
|
|
||||||
json_escape(<<"/Date(1303502009425)/">>, #opts{escape_forward_slash=true}),
|
|
||||||
<<"\\/Date(1303502009425)\\/">>
|
|
||||||
)
|
|
||||||
},
|
|
||||||
{"dirty strings",
|
|
||||||
?_assertEqual(
|
|
||||||
json_escape(<<"\\x25\\uffff">>, #opts{dirty_strings=true}),
|
|
||||||
<<"\\x25\\uffff">>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
].
|
].
|
||||||
|
|
||||||
|
|
||||||
opts_test_() ->
|
opts_test_() ->
|
||||||
[
|
[
|
||||||
{"all flags",
|
{"all flags",
|
||||||
?_assertEqual(
|
?_assertEqual(
|
||||||
parse_opts([
|
parse_opts([
|
||||||
loose_unicode,
|
replaced_bad_utf8,
|
||||||
escape_forward_slash,
|
escaped_forward_slashes,
|
||||||
explicit_end,
|
explicit_end,
|
||||||
single_quotes,
|
single_quoted_strings,
|
||||||
no_jsonp_escapes,
|
unescaped_jsonp,
|
||||||
comments,
|
comments,
|
||||||
dirty_strings,
|
dirty_strings,
|
||||||
ignore_bad_escapes
|
ignored_bad_escapes
|
||||||
]),
|
]),
|
||||||
#opts{
|
#opts{
|
||||||
loose_unicode=true,
|
replaced_bad_utf8=true,
|
||||||
escape_forward_slash=true,
|
escaped_forward_slashes=true,
|
||||||
explicit_end=true,
|
explicit_end=true,
|
||||||
single_quotes=true,
|
single_quoted_strings=true,
|
||||||
no_jsonp_escapes=true,
|
unescaped_jsonp=true,
|
||||||
comments=true,
|
comments=true,
|
||||||
dirty_strings=true,
|
dirty_strings=true,
|
||||||
ignore_bad_escapes=true
|
ignored_bad_escapes=true
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
@ -409,10 +185,10 @@ opts_test_() ->
|
||||||
?_assertEqual(
|
?_assertEqual(
|
||||||
parse_opts([relax]),
|
parse_opts([relax]),
|
||||||
#opts{
|
#opts{
|
||||||
loose_unicode=true,
|
replaced_bad_utf8=true,
|
||||||
single_quotes=true,
|
single_quoted_strings=true,
|
||||||
comments=true,
|
comments=true,
|
||||||
ignore_bad_escapes=true
|
ignored_bad_escapes=true
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue