diff --git a/src/jsx_decoder.erl b/src/jsx_decoder.erl index 7c5d295..4ef6ed1 100644 --- a/src/jsx_decoder.erl +++ b/src/jsx_decoder.erl @@ -1041,6 +1041,207 @@ done(Bin, Handler, Stack, Opts) -> ?error([Bin, Handler, Stack, Opts]). -include_lib("eunit/include/eunit.hrl"). +xcode(Bin) -> xcode(Bin, []). + +xcode(Bin, Opts) -> + Size = size(Bin), + try jsx:to_term(<<34, Bin:Size/binary, 34>>, Opts) + catch error:badarg -> {error, badarg} + end. + + +is_bad({error, badarg}) -> true; +is_bad(_) -> false. + + +bad_utf8_test_() -> + [ + {"orphan continuation byte u+0080", + ?_assert(is_bad(xcode(<<16#0080>>))) + }, + {"orphan continuation byte u+0080 replaced", + ?_assertEqual(xcode(<<16#0080>>, [loose_unicode]), <<16#fffd/utf8>>) + }, + {"orphan continuation byte u+00bf", + ?_assert(is_bad(xcode(<<16#00bf>>))) + }, + {"orphan continuation byte u+00bf replaced", + ?_assertEqual(xcode(<<16#00bf>>, [loose_unicode]), <<16#fffd/utf8>>) + }, + {"2 continuation bytes", + ?_assert(is_bad(xcode(<<(binary:copy(<<16#0080>>, 2))/binary>>))) + }, + {"2 continuation bytes replaced", + ?_assertEqual( + xcode(<<(binary:copy(<<16#0080>>, 2))/binary>>, [loose_unicode]), + binary:copy(<<16#fffd/utf8>>, 2) + ) + }, + {"3 continuation bytes", + ?_assert(is_bad(xcode(<<(binary:copy(<<16#0080>>, 3))/binary>>))) + }, + {"3 continuation bytes replaced", + ?_assertEqual( + xcode(<<(binary:copy(<<16#0080>>, 3))/binary>>, [loose_unicode]), + binary:copy(<<16#fffd/utf8>>, 3) + ) + }, + {"4 continuation bytes", + ?_assert(is_bad(xcode(<<(binary:copy(<<16#0080>>, 4))/binary>>))) + }, + {"4 continuation bytes replaced", + ?_assertEqual( + xcode(<<(binary:copy(<<16#0080>>, 4))/binary>>, [loose_unicode]), + binary:copy(<<16#fffd/utf8>>, 4) + ) + }, + {"5 continuation bytes", + ?_assert(is_bad(xcode(<<(binary:copy(<<16#0080>>, 5))/binary>>))) + }, + {"5 continuation bytes replaced", + ?_assertEqual( + xcode(<<(binary:copy(<<16#0080>>, 5))/binary>>, [loose_unicode]), + binary:copy(<<16#fffd/utf8>>, 5) + ) + }, + {"6 continuation bytes", + ?_assert(is_bad(xcode(<<(binary:copy(<<16#0080>>, 6))/binary>>))) + }, + {"6 continuation bytes replaced", + ?_assertEqual( + xcode(<<(binary:copy(<<16#0080>>, 6))/binary>>, [loose_unicode]), + binary:copy(<<16#fffd/utf8>>, 6) + ) + }, + {"all continuation bytes", + ?_assert(is_bad(xcode(<<(list_to_binary(lists:seq(16#0080, 16#00bf)))/binary>>))) + }, + {"all continuation bytes replaced", + ?_assertEqual( + xcode(<<(list_to_binary(lists:seq(16#0080, 16#00bf)))/binary>>, [loose_unicode]), + binary:copy(<<16#fffd/utf8>>, length(lists:seq(16#0080, 16#00bf))) + ) + }, + {"lonely start byte", + ?_assert(is_bad(xcode(<<16#00c0>>))) + }, + {"lonely start byte replaced", + ?_assertEqual( + xcode(<<16#00c0>>, [loose_unicode]), + <<16#fffd/utf8>> + ) + }, + {"lonely start bytes (2 byte)", + ?_assert(is_bad(xcode(<<16#00c0, 32, 16#00df>>))) + }, + {"lonely start bytes (2 byte) replaced", + ?_assertEqual( + xcode(<<16#00c0, 32, 16#00df>>, [loose_unicode]), + <<16#fffd/utf8, 32, 16#fffd/utf8>> + ) + }, + {"lonely start bytes (3 byte)", + ?_assert(is_bad(xcode(<<16#00e0, 32, 16#00ef>>))) + }, + {"lonely start bytes (3 byte) replaced", + ?_assertEqual( + xcode(<<16#00e0, 32, 16#00ef>>, [loose_unicode]), + <<16#fffd/utf8, 32, 16#fffd/utf8>> + ) + }, + {"lonely start bytes (4 byte)", + ?_assert(is_bad(xcode(<<16#00f0, 32, 16#00f7>>))) + }, + {"lonely start bytes (4 byte) replaced", + ?_assertEqual( + xcode(<<16#00f0, 32, 16#00f7>>, [loose_unicode]), + <<16#fffd/utf8, 32, 16#fffd/utf8>> + ) + }, + {"missing continuation byte (3 byte)", + ?_assert(is_bad(xcode(<<224, 160, 32>>))) + }, + {"missing continuation byte (3 byte) replaced", + ?_assertEqual( + xcode(<<224, 160, 32>>, [loose_unicode]), + <<16#fffd/utf8, 32>> + ) + }, + {"missing continuation byte (4 byte missing one)", + ?_assert(is_bad(xcode(<<240, 144, 128, 32>>))) + }, + {"missing continuation byte2 (4 byte missing one) replaced", + ?_assertEqual( + xcode(<<240, 144, 128, 32>>, [loose_unicode]), + <<16#fffd/utf8, 32>> + ) + }, + {"missing continuation byte (4 byte missing two)", + ?_assert(is_bad(xcode(<<240, 144, 32>>))) + }, + {"missing continuation byte2 (4 byte missing two) replaced", + ?_assertEqual( + xcode(<<240, 144, 32>>, [loose_unicode]), + <<16#fffd/utf8, 32>> + ) + }, + {"overlong encoding of u+002f (2 byte)", + ?_assert(is_bad(xcode(<<16#c0, 16#af, 32>>))) + }, + {"overlong encoding of u+002f (2 byte) replaced", + ?_assertEqual( + xcode(<<16#c0, 16#af, 32>>, [loose_unicode]), + <<16#fffd/utf8, 32>> + ) + }, + {"overlong encoding of u+002f (3 byte)", + ?_assert(is_bad(xcode(<<16#e0, 16#80, 16#af, 32>>))) + }, + {"overlong encoding of u+002f (3 byte) replaced", + ?_assertEqual( + xcode(<<16#e0, 16#80, 16#af, 32>>, [loose_unicode]), + <<16#fffd/utf8, 32>> + ) + }, + {"overlong encoding of u+002f (4 byte)", + ?_assert(is_bad(xcode(<<16#f0, 16#80, 16#80, 16#af, 32>>))) + }, + {"overlong encoding of u+002f (4 byte) replaced", + ?_assertEqual( + xcode(<<16#f0, 16#80, 16#80, 16#af, 32>>, [loose_unicode]), + <<16#fffd/utf8, 32>> + ) + }, + {"highest overlong 2 byte sequence", + ?_assert(is_bad(xcode(<<16#c1, 16#bf, 32>>))) + }, + {"highest overlong 2 byte sequence replaced", + ?_assertEqual( + xcode(<<16#c1, 16#bf, 32>>, [loose_unicode]), + <<16#fffd/utf8, 32>> + ) + }, + {"highest overlong 3 byte sequence", + ?_assert(is_bad(xcode(<<16#e0, 16#9f, 16#bf, 32>>))) + }, + {"highest overlong 3 byte sequence replaced", + ?_assertEqual( + xcode(<<16#e0, 16#9f, 16#bf, 32>>, [loose_unicode]), + <<16#fffd/utf8, 32>> + ) + }, + {"highest overlong 4 byte sequence", + ?_assert(is_bad(xcode(<<16#f0, 16#8f, 16#bf, 16#bf, 32>>))) + }, + {"highest overlong 4 byte sequence replaced", + ?_assertEqual( + xcode(<<16#f0, 16#8f, 16#bf, 16#bf, 32>>, [loose_unicode]), + <<16#fffd/utf8, 32>> + ) + } + ]. + + comments_test_() -> [ {"preceeding // comment", ?_assertEqual( diff --git a/src/jsx_encoder.erl b/src/jsx_encoder.erl index 573d870..079d454 100644 --- a/src/jsx_encoder.erl +++ b/src/jsx_encoder.erl @@ -174,6 +174,208 @@ strip_continuations(Bin, _, N) -> {Bin, N}. -ifdef(TEST). -include_lib("eunit/include/eunit.hrl"). + +xcode(Bin) -> xcode(Bin, #opts{}). + +xcode(Bin, [loose_unicode]) -> xcode(Bin, #opts{loose_unicode=true}); +xcode(Bin, Opts) -> + try clean_string(Bin, Opts) + catch error:badarg -> {error, badarg} + end. + + +is_bad({error, badarg}) -> true; +is_bad(_) -> false. + + +bad_utf8_test_() -> + [ + {"orphan continuation byte u+0080", + ?_assert(is_bad(xcode(<<16#0080>>))) + }, + {"orphan continuation byte u+0080 replaced", + ?_assertEqual(xcode(<<16#0080>>, [loose_unicode]), <<16#fffd/utf8>>) + }, + {"orphan continuation byte u+00bf", + ?_assert(is_bad(xcode(<<16#00bf>>))) + }, + {"orphan continuation byte u+00bf replaced", + ?_assertEqual(xcode(<<16#00bf>>, [loose_unicode]), <<16#fffd/utf8>>) + }, + {"2 continuation bytes", + ?_assert(is_bad(xcode(<<(binary:copy(<<16#0080>>, 2))/binary>>))) + }, + {"2 continuation bytes replaced", + ?_assertEqual( + xcode(<<(binary:copy(<<16#0080>>, 2))/binary>>, [loose_unicode]), + binary:copy(<<16#fffd/utf8>>, 2) + ) + }, + {"3 continuation bytes", + ?_assert(is_bad(xcode(<<(binary:copy(<<16#0080>>, 3))/binary>>))) + }, + {"3 continuation bytes replaced", + ?_assertEqual( + xcode(<<(binary:copy(<<16#0080>>, 3))/binary>>, [loose_unicode]), + binary:copy(<<16#fffd/utf8>>, 3) + ) + }, + {"4 continuation bytes", + ?_assert(is_bad(xcode(<<(binary:copy(<<16#0080>>, 4))/binary>>))) + }, + {"4 continuation bytes replaced", + ?_assertEqual( + xcode(<<(binary:copy(<<16#0080>>, 4))/binary>>, [loose_unicode]), + binary:copy(<<16#fffd/utf8>>, 4) + ) + }, + {"5 continuation bytes", + ?_assert(is_bad(xcode(<<(binary:copy(<<16#0080>>, 5))/binary>>))) + }, + {"5 continuation bytes replaced", + ?_assertEqual( + xcode(<<(binary:copy(<<16#0080>>, 5))/binary>>, [loose_unicode]), + binary:copy(<<16#fffd/utf8>>, 5) + ) + }, + {"6 continuation bytes", + ?_assert(is_bad(xcode(<<(binary:copy(<<16#0080>>, 6))/binary>>))) + }, + {"6 continuation bytes replaced", + ?_assertEqual( + xcode(<<(binary:copy(<<16#0080>>, 6))/binary>>, [loose_unicode]), + binary:copy(<<16#fffd/utf8>>, 6) + ) + }, + {"all continuation bytes", + ?_assert(is_bad(xcode(<<(list_to_binary(lists:seq(16#0080, 16#00bf)))/binary>>))) + }, + {"all continuation bytes replaced", + ?_assertEqual( + xcode(<<(list_to_binary(lists:seq(16#0080, 16#00bf)))/binary>>, [loose_unicode]), + binary:copy(<<16#fffd/utf8>>, length(lists:seq(16#0080, 16#00bf))) + ) + }, + {"lonely start byte", + ?_assert(is_bad(xcode(<<16#00c0>>))) + }, + {"lonely start byte replaced", + ?_assertEqual( + xcode(<<16#00c0>>, [loose_unicode]), + <<16#fffd/utf8>> + ) + }, + {"lonely start bytes (2 byte)", + ?_assert(is_bad(xcode(<<16#00c0, 32, 16#00df>>))) + }, + {"lonely start bytes (2 byte) replaced", + ?_assertEqual( + xcode(<<16#00c0, 32, 16#00df>>, [loose_unicode]), + <<16#fffd/utf8, 32, 16#fffd/utf8>> + ) + }, + {"lonely start bytes (3 byte)", + ?_assert(is_bad(xcode(<<16#00e0, 32, 16#00ef>>))) + }, + {"lonely start bytes (3 byte) replaced", + ?_assertEqual( + xcode(<<16#00e0, 32, 16#00ef>>, [loose_unicode]), + <<16#fffd/utf8, 32, 16#fffd/utf8>> + ) + }, + {"lonely start bytes (4 byte)", + ?_assert(is_bad(xcode(<<16#00f0, 32, 16#00f7>>))) + }, + {"lonely start bytes (4 byte) replaced", + ?_assertEqual( + xcode(<<16#00f0, 32, 16#00f7>>, [loose_unicode]), + <<16#fffd/utf8, 32, 16#fffd/utf8>> + ) + }, + {"missing continuation byte (3 byte)", + ?_assert(is_bad(xcode(<<224, 160, 32>>))) + }, + {"missing continuation byte (3 byte) replaced", + ?_assertEqual( + xcode(<<224, 160, 32>>, [loose_unicode]), + <<16#fffd/utf8, 32>> + ) + }, + {"missing continuation byte (4 byte missing one)", + ?_assert(is_bad(xcode(<<240, 144, 128, 32>>))) + }, + {"missing continuation byte2 (4 byte missing one) replaced", + ?_assertEqual( + xcode(<<240, 144, 128, 32>>, [loose_unicode]), + <<16#fffd/utf8, 32>> + ) + }, + {"missing continuation byte (4 byte missing two)", + ?_assert(is_bad(xcode(<<240, 144, 32>>))) + }, + {"missing continuation byte2 (4 byte missing two) replaced", + ?_assertEqual( + xcode(<<240, 144, 32>>, [loose_unicode]), + <<16#fffd/utf8, 32>> + ) + }, + {"overlong encoding of u+002f (2 byte)", + ?_assert(is_bad(xcode(<<16#c0, 16#af, 32>>))) + }, + {"overlong encoding of u+002f (2 byte) replaced", + ?_assertEqual( + xcode(<<16#c0, 16#af, 32>>, [loose_unicode]), + <<16#fffd/utf8, 32>> + ) + }, + {"overlong encoding of u+002f (3 byte)", + ?_assert(is_bad(xcode(<<16#e0, 16#80, 16#af, 32>>))) + }, + {"overlong encoding of u+002f (3 byte) replaced", + ?_assertEqual( + xcode(<<16#e0, 16#80, 16#af, 32>>, [loose_unicode]), + <<16#fffd/utf8, 32>> + ) + }, + {"overlong encoding of u+002f (4 byte)", + ?_assert(is_bad(xcode(<<16#f0, 16#80, 16#80, 16#af, 32>>))) + }, + {"overlong encoding of u+002f (4 byte) replaced", + ?_assertEqual( + xcode(<<16#f0, 16#80, 16#80, 16#af, 32>>, [loose_unicode]), + <<16#fffd/utf8, 32>> + ) + }, + {"highest overlong 2 byte sequence", + ?_assert(is_bad(xcode(<<16#c1, 16#bf, 32>>))) + }, + {"highest overlong 2 byte sequence replaced", + ?_assertEqual( + xcode(<<16#c1, 16#bf, 32>>, [loose_unicode]), + <<16#fffd/utf8, 32>> + ) + }, + {"highest overlong 3 byte sequence", + ?_assert(is_bad(xcode(<<16#e0, 16#9f, 16#bf, 32>>))) + }, + {"highest overlong 3 byte sequence replaced", + ?_assertEqual( + xcode(<<16#e0, 16#9f, 16#bf, 32>>, [loose_unicode]), + <<16#fffd/utf8, 32>> + ) + }, + {"highest overlong 4 byte sequence", + ?_assert(is_bad(xcode(<<16#f0, 16#8f, 16#bf, 16#bf, 32>>))) + }, + {"highest overlong 4 byte sequence replaced", + ?_assertEqual( + xcode(<<16#f0, 16#8f, 16#bf, 16#bf, 32>>, [loose_unicode]), + <<16#fffd/utf8, 32>> + ) + } + ]. + + encode(Term) -> (encoder(jsx, [], []))(Term). encode(Term, Opts) -> @@ -303,14 +505,6 @@ extended_noncharacters_test_() -> } ]. -malformed_test_() -> - [ - {"malformed codepoint with 1 byte", ?_assertError(badarg, encode(<<128>>))}, - {"malformed codepoint with 2 bytes", ?_assertError(badarg, encode(<<128, 192>>))}, - {"malformed codepoint with 3 bytes", ?_assertError(badarg, encode(<<128, 192, 192>>))}, - {"malformed codepoint with 4 bytes", ?_assertError(badarg, encode(<<128, 192, 192, 192>>))} - ]. - check_bad(List) -> lists:dropwhile(fun({_, {error, badjson}}) -> true ; (_) -> false end, diff --git a/src/jsx_utils.erl b/src/jsx_utils.erl index 96a9f53..aea689c 100644 --- a/src/jsx_utils.erl +++ b/src/jsx_utils.erl @@ -302,14 +302,26 @@ json_escape(Str, Opts, L, Len) when L < Len -> false -> erlang:error(badarg, [Str, Opts]) end; <> when X >= 192, X =< 223 -> - {Rest, Stripped} = strip_continuations(T, 1, 0), - json_escape(<>, Opts, L + 3, Len + 2 - Stripped); + case Opts#opts.loose_unicode of + true -> + {Rest, Stripped} = strip_continuations(T, 1, 0), + json_escape(<>, Opts, L + 3, Len + 2 - Stripped); + false -> erlang:error(badarg, [Str, Opts]) + end; <> when X >= 224, X =< 239 -> - {Rest, Stripped} = strip_continuations(T, 2, 0), - json_escape(<>, Opts, L + 3, Len + 2 - Stripped); + case Opts#opts.loose_unicode of + true -> + {Rest, Stripped} = strip_continuations(T, 2, 0), + json_escape(<>, Opts, L + 3, Len + 2 - Stripped); + false -> erlang:error(badarg, [Str, Opts]) + end; <> when X >= 240, X =< 247 -> - {Rest, Stripped} = strip_continuations(T, 3, 0), - json_escape(<>, Opts, L + 3, Len + 2 - Stripped); + case Opts#opts.loose_unicode of + true -> + {Rest, Stripped} = strip_continuations(T, 3, 0), + json_escape(<>, Opts, L + 3, Len + 2 - Stripped); + false -> erlang:error(badarg, [Str, Opts]) + end; <> -> case Opts#opts.loose_unicode of true -> json_escape(<>, Opts, L + 3, Len + 2); @@ -347,6 +359,207 @@ strip_continuations(Bin, _, N) -> {Bin, N}. -include_lib("eunit/include/eunit.hrl"). +xcode(Bin) -> xcode(Bin, #opts{}). + +xcode(Bin, [loose_unicode]) -> xcode(Bin, #opts{loose_unicode=true}); +xcode(Bin, Opts) -> + try json_escape(Bin, Opts) + catch error:badarg -> {error, badarg} + end. + + +is_bad({error, badarg}) -> true; +is_bad(_) -> false. + + +bad_utf8_test_() -> + [ + {"orphan continuation byte u+0080", + ?_assert(is_bad(xcode(<<16#0080>>))) + }, + {"orphan continuation byte u+0080 replaced", + ?_assertEqual(xcode(<<16#0080>>, [loose_unicode]), <<16#fffd/utf8>>) + }, + {"orphan continuation byte u+00bf", + ?_assert(is_bad(xcode(<<16#00bf>>))) + }, + {"orphan continuation byte u+00bf replaced", + ?_assertEqual(xcode(<<16#00bf>>, [loose_unicode]), <<16#fffd/utf8>>) + }, + {"2 continuation bytes", + ?_assert(is_bad(xcode(<<(binary:copy(<<16#0080>>, 2))/binary>>))) + }, + {"2 continuation bytes replaced", + ?_assertEqual( + xcode(<<(binary:copy(<<16#0080>>, 2))/binary>>, [loose_unicode]), + binary:copy(<<16#fffd/utf8>>, 2) + ) + }, + {"3 continuation bytes", + ?_assert(is_bad(xcode(<<(binary:copy(<<16#0080>>, 3))/binary>>))) + }, + {"3 continuation bytes replaced", + ?_assertEqual( + xcode(<<(binary:copy(<<16#0080>>, 3))/binary>>, [loose_unicode]), + binary:copy(<<16#fffd/utf8>>, 3) + ) + }, + {"4 continuation bytes", + ?_assert(is_bad(xcode(<<(binary:copy(<<16#0080>>, 4))/binary>>))) + }, + {"4 continuation bytes replaced", + ?_assertEqual( + xcode(<<(binary:copy(<<16#0080>>, 4))/binary>>, [loose_unicode]), + binary:copy(<<16#fffd/utf8>>, 4) + ) + }, + {"5 continuation bytes", + ?_assert(is_bad(xcode(<<(binary:copy(<<16#0080>>, 5))/binary>>))) + }, + {"5 continuation bytes replaced", + ?_assertEqual( + xcode(<<(binary:copy(<<16#0080>>, 5))/binary>>, [loose_unicode]), + binary:copy(<<16#fffd/utf8>>, 5) + ) + }, + {"6 continuation bytes", + ?_assert(is_bad(xcode(<<(binary:copy(<<16#0080>>, 6))/binary>>))) + }, + {"6 continuation bytes replaced", + ?_assertEqual( + xcode(<<(binary:copy(<<16#0080>>, 6))/binary>>, [loose_unicode]), + binary:copy(<<16#fffd/utf8>>, 6) + ) + }, + {"all continuation bytes", + ?_assert(is_bad(xcode(<<(list_to_binary(lists:seq(16#0080, 16#00bf)))/binary>>))) + }, + {"all continuation bytes replaced", + ?_assertEqual( + xcode(<<(list_to_binary(lists:seq(16#0080, 16#00bf)))/binary>>, [loose_unicode]), + binary:copy(<<16#fffd/utf8>>, length(lists:seq(16#0080, 16#00bf))) + ) + }, + {"lonely start byte", + ?_assert(is_bad(xcode(<<16#00c0>>))) + }, + {"lonely start byte replaced", + ?_assertEqual( + xcode(<<16#00c0>>, [loose_unicode]), + <<16#fffd/utf8>> + ) + }, + {"lonely start bytes (2 byte)", + ?_assert(is_bad(xcode(<<16#00c0, 32, 16#00df>>))) + }, + {"lonely start bytes (2 byte) replaced", + ?_assertEqual( + xcode(<<16#00c0, 32, 16#00df>>, [loose_unicode]), + <<16#fffd/utf8, 32, 16#fffd/utf8>> + ) + }, + {"lonely start bytes (3 byte)", + ?_assert(is_bad(xcode(<<16#00e0, 32, 16#00ef>>))) + }, + {"lonely start bytes (3 byte) replaced", + ?_assertEqual( + xcode(<<16#00e0, 32, 16#00ef>>, [loose_unicode]), + <<16#fffd/utf8, 32, 16#fffd/utf8>> + ) + }, + {"lonely start bytes (4 byte)", + ?_assert(is_bad(xcode(<<16#00f0, 32, 16#00f7>>))) + }, + {"lonely start bytes (4 byte) replaced", + ?_assertEqual( + xcode(<<16#00f0, 32, 16#00f7>>, [loose_unicode]), + <<16#fffd/utf8, 32, 16#fffd/utf8>> + ) + }, + {"missing continuation byte (3 byte)", + ?_assert(is_bad(xcode(<<224, 160, 32>>))) + }, + {"missing continuation byte (3 byte) replaced", + ?_assertEqual( + xcode(<<224, 160, 32>>, [loose_unicode]), + <<16#fffd/utf8, 32>> + ) + }, + {"missing continuation byte (4 byte missing one)", + ?_assert(is_bad(xcode(<<240, 144, 128, 32>>))) + }, + {"missing continuation byte2 (4 byte missing one) replaced", + ?_assertEqual( + xcode(<<240, 144, 128, 32>>, [loose_unicode]), + <<16#fffd/utf8, 32>> + ) + }, + {"missing continuation byte (4 byte missing two)", + ?_assert(is_bad(xcode(<<240, 144, 32>>))) + }, + {"missing continuation byte2 (4 byte missing two) replaced", + ?_assertEqual( + xcode(<<240, 144, 32>>, [loose_unicode]), + <<16#fffd/utf8, 32>> + ) + }, + {"overlong encoding of u+002f (2 byte)", + ?_assert(is_bad(xcode(<<16#c0, 16#af, 32>>))) + }, + {"overlong encoding of u+002f (2 byte) replaced", + ?_assertEqual( + xcode(<<16#c0, 16#af, 32>>, [loose_unicode]), + <<16#fffd/utf8, 32>> + ) + }, + {"overlong encoding of u+002f (3 byte)", + ?_assert(is_bad(xcode(<<16#e0, 16#80, 16#af, 32>>))) + }, + {"overlong encoding of u+002f (3 byte) replaced", + ?_assertEqual( + xcode(<<16#e0, 16#80, 16#af, 32>>, [loose_unicode]), + <<16#fffd/utf8, 32>> + ) + }, + {"overlong encoding of u+002f (4 byte)", + ?_assert(is_bad(xcode(<<16#f0, 16#80, 16#80, 16#af, 32>>))) + }, + {"overlong encoding of u+002f (4 byte) replaced", + ?_assertEqual( + xcode(<<16#f0, 16#80, 16#80, 16#af, 32>>, [loose_unicode]), + <<16#fffd/utf8, 32>> + ) + }, + {"highest overlong 2 byte sequence", + ?_assert(is_bad(xcode(<<16#c1, 16#bf, 32>>))) + }, + {"highest overlong 2 byte sequence replaced", + ?_assertEqual( + xcode(<<16#c1, 16#bf, 32>>, [loose_unicode]), + <<16#fffd/utf8, 32>> + ) + }, + {"highest overlong 3 byte sequence", + ?_assert(is_bad(xcode(<<16#e0, 16#9f, 16#bf, 32>>))) + }, + {"highest overlong 3 byte sequence replaced", + ?_assertEqual( + xcode(<<16#e0, 16#9f, 16#bf, 32>>, [loose_unicode]), + <<16#fffd/utf8, 32>> + ) + }, + {"highest overlong 4 byte sequence", + ?_assert(is_bad(xcode(<<16#f0, 16#8f, 16#bf, 16#bf, 32>>))) + }, + {"highest overlong 4 byte sequence replaced", + ?_assertEqual( + xcode(<<16#f0, 16#8f, 16#bf, 16#bf, 32>>, [loose_unicode]), + <<16#fffd/utf8, 32>> + ) + } + ]. + + binary_escape_test_() -> [ {"json string escaping",