diff --git a/bench/encode.exs b/bench/encode.exs index 3ec2797..f5daffe 100644 --- a/bench/encode.exs +++ b/bench/encode.exs @@ -1,12 +1,13 @@ encode_jobs = %{ - "Jason" => &Jason.encode_to_iodata!/1, - "Jason strict" => &Jason.encode_to_iodata!(&1, maps: :strict), - "Poison" => &Poison.encode_to_iodata!/1, - "JSX" => &JSX.encode!/1, - "Tiny" => &Tiny.encode!/1, - "jsone" => &:jsone.encode/1, + "Jason" => &Jason.encode_to_iodata!(&1, escape: :elixir_json), + "Jason native" => &Jason.encode_to_iodata!(&1, escape: :native_json), + # "Jason strict" => &Jason.encode_to_iodata!(&1, maps: :strict, escape: :elixir_json), + "Poison" => &Poison.encode!/1, + # "JSX" => &JSX.encode!/1, + # "Tiny" => &Tiny.encode!/1, + # "jsone" => &:jsone.encode/1, "jiffy" => &:jiffy.encode/1, - "JSON" => &JSON.encode!/1, + # "JSON" => &JSON.encode!/1, # "term_to_binary" => &:erlang.term_to_binary/1, } diff --git a/bench/mix.exs b/bench/mix.exs index 649fe7c..624b2ca 100644 --- a/bench/mix.exs +++ b/bench/mix.exs @@ -23,6 +23,7 @@ defmodule JasonBench.MixProject do defp deps do [ {:jason, "~> 1.0", path: "../", override: true}, + {:jason_native, ">= 0.0.0", path: "../../jason_native"}, {:benchee, "~> 1.0"}, {:benchee_html, "~> 1.0"}, {:poison, "~> 5.0"}, diff --git a/bench/mix.lock b/bench/mix.lock index 22356ba..47b9f1a 100644 --- a/bench/mix.lock +++ b/bench/mix.lock @@ -3,8 +3,8 @@ "benchee_html": {:hex, :benchee_html, "1.0.0", "5b4d24effebd060f466fb460ec06576e7b34a00fc26b234fe4f12c4f05c95947", [:mix], [{:benchee, ">= 0.99.0 and < 2.0.0", [hex: :benchee, repo: "hexpm", optional: false]}, {:benchee_json, "~> 1.0", [hex: :benchee_json, repo: "hexpm", optional: false]}], "hexpm", "5280af9aac432ff5ca4216d03e8a93f32209510e925b60e7f27c33796f69e699"}, "benchee_json": {:hex, :benchee_json, "1.0.0", "cc661f4454d5995c08fe10dd1f2f72f229c8f0fb1c96f6b327a8c8fc96a91fe5", [:mix], [{:benchee, ">= 0.99.0 and < 2.0.0", [hex: :benchee, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "da05d813f9123505f870344d68fb7c86a4f0f9074df7d7b7e2bb011a63ec231c"}, "deep_merge": {:hex, :deep_merge, "1.0.0", "b4aa1a0d1acac393bdf38b2291af38cb1d4a52806cf7a4906f718e1feb5ee961", [:mix], [], "hexpm", "ce708e5f094b9cd4e8f2be4f00d2f4250c4095be93f8cd6d018c753894885430"}, + "elixir_make": {:hex, :elixir_make, "0.6.3", "bc07d53221216838d79e03a8019d0839786703129599e9619f4ab74c8c096eac", [:mix], [], "hexpm", "f5cbd651c5678bcaabdbb7857658ee106b12509cd976c2c2fca99688e1daf716"}, "exjsx": {:hex, :exjsx, "4.0.0", "60548841e0212df401e38e63c0078ec57b33e7ea49b032c796ccad8cde794b5c", [:mix], [{:jsx, "~> 2.8.0", [hex: :jsx, repo: "hexpm", optional: false]}], "hexpm", "32e95820a97cffea67830e91514a2ad53b888850442d6d395f53a1ac60c82e07"}, - "jason": {:hex, :jason, "1.1.2", "b03dedea67a99223a2eaf9f1264ce37154564de899fd3d8b9a21b1a6fd64afe7", [], [{:decimal, "~> 1.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm"}, "jiffy": {:hex, :jiffy, "1.1.1", "aca10f47aa91697bf24ab9582c74e00e8e95474c7ef9f76d4f1a338d0f5de21b", [:rebar3], [], "hexpm", "62e1f0581c3c19c33a725c781dfa88410d8bff1bbafc3885a2552286b4785c4c"}, "json": {:hex, :json, "1.4.1", "8648f04a9439765ad449bc56a3ff7d8b11dd44ff08ffcdefc4329f7c93843dfa", [:mix], [], "hexpm", "9abf218dbe4ea4fcb875e087d5f904ef263d012ee5ed21d46e9dbca63f053d16"}, "jsone": {:hex, :jsone, "1.7.0", "1e3bd7d5dd44bb2eb0797dddea1cbf2ddab8d9f29e499a467ca171c23f5984ea", [:rebar3], [], "hexpm", "a3a33712ee6bc8be10cfa21c7c425a299de4c5a8533f9f931e577a6d0e8f5dbd"}, diff --git a/lib/encode.ex b/lib/encode.ex index 553a5ef..ae7baac 100644 --- a/lib/encode.ex +++ b/lib/encode.ex @@ -38,6 +38,8 @@ defmodule Jason.Encode do catch :throw, %EncodeError{} = e -> {:error, e} + :error, {:invalid_byte, _, _} = e -> + {:error, EncodeError.new(e)} :error, %Protocol.UndefinedError{protocol: Jason.Encoder} = e -> {:error, e} end @@ -50,15 +52,33 @@ defmodule Jason.Encode do end end - defp escape_function(%{escape: escape}) do - case escape do - :json -> &escape_json/1 - :html_safe -> &escape_html/1 - :unicode_safe -> &escape_unicode/1 - :javascript_safe -> &escape_javascript/1 - # Keep for compatibility with Poison - :javascript -> &escape_javascript/1 - :unicode -> &escape_unicode/1 + if Code.ensure_loaded?(Jason.Native) do + defp escape_function(%{escape: escape}) do + case escape do + :json -> &Jason.Native.escape_json/1 + :native_json -> &Jason.Native.escape_json/1 + :elixir_json -> &escape_json/1 + :html_safe -> &escape_html/1 + :unicode_safe -> &escape_unicode/1 + :javascript_safe -> &escape_javascript/1 + # Keep for compatibility with Poison + :javascript -> &escape_javascript/1 + :unicode -> &escape_unicode/1 + end + end + else + defp escape_function(%{escape: escape}) do + case escape do + :json -> &escape_json/1 + :native_json -> raise ArgumentError, "jason_native not found, :native_ options not available" + :elixir_json -> &escape_json/1 + :html_safe -> &escape_html/1 + :unicode_safe -> &escape_unicode/1 + :javascript_safe -> &escape_javascript/1 + # Keep for compatibility with Poison + :javascript -> &escape_javascript/1 + :unicode -> &escape_unicode/1 + end end end diff --git a/mix.exs b/mix.exs index 5d1baf1..3b1fdf5 100644 --- a/mix.exs +++ b/mix.exs @@ -31,6 +31,7 @@ defmodule Jason.Mixfile do {:decimal, "~> 1.0 or ~> 2.0", optional: true}, {:dialyxir, "~> 1.0", only: [:dev, :test], runtime: false}, {:ex_doc, ">= 0.0.0", only: :dev, runtime: false}, + {:jason_native, ">= 0.0.0", optional: true, path: "../jason_native"} ] ++ maybe_stream_data() end diff --git a/mix.lock b/mix.lock index f55d0ce..8344074 100644 --- a/mix.lock +++ b/mix.lock @@ -2,8 +2,10 @@ "decimal": {:hex, :decimal, "2.0.0", "a78296e617b0f5dd4c6caf57c714431347912ffb1d0842e998e9792b5642d697", [:mix], [], "hexpm", "34666e9c55dea81013e77d9d87370fe6cb6291d1ef32f46a1600230b1d44f577"}, "dialyxir": {:hex, :dialyxir, "1.2.0", "58344b3e87c2e7095304c81a9ae65cb68b613e28340690dfe1a5597fd08dec37", [:mix], [{:erlex, ">= 0.2.6", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm", "61072136427a851674cab81762be4dbeae7679f85b1272b6d25c3a839aff8463"}, "earmark_parser": {:hex, :earmark_parser, "1.4.26", "f4291134583f373c7d8755566122908eb9662df4c4b63caa66a0eabe06569b0a", [:mix], [], "hexpm", "48d460899f8a0c52c5470676611c01f64f3337bad0b26ddab43648428d94aabc"}, + "elixir_make": {:hex, :elixir_make, "0.6.3", "bc07d53221216838d79e03a8019d0839786703129599e9619f4ab74c8c096eac", [:mix], [], "hexpm", "f5cbd651c5678bcaabdbb7857658ee106b12509cd976c2c2fca99688e1daf716"}, "erlex": {:hex, :erlex, "0.2.6", "c7987d15e899c7a2f34f5420d2a2ea0d659682c06ac607572df55a43753aa12e", [:mix], [], "hexpm", "2ed2e25711feb44d52b17d2780eabf998452f6efda104877a3881c2f8c0c0c75"}, "ex_doc": {:hex, :ex_doc, "0.28.5", "3e52a6d2130ce74d096859e477b97080c156d0926701c13870a4e1f752363279", [:mix], [{:earmark_parser, "~> 1.4.19", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1", [hex: :makeup_erlang, repo: "hexpm", optional: false]}], "hexpm", "d2c4b07133113e9aa3e9ba27efb9088ba900e9e51caa383919676afdf09ab181"}, + "jason_native": {:git, "https://github.com/spawnfest/json_native.git", "d3d752b1baa81eae6beb053e48604edcf3b16149", []}, "makeup": {:hex, :makeup, "1.1.0", "6b67c8bc2882a6b6a445859952a602afc1a41c2e08379ca057c0f525366fc3ca", [:mix], [{:nimble_parsec, "~> 1.2.2 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "0a45ed501f4a8897f580eabf99a2e5234ea3e75a4373c8a52824f6e873be57a6"}, "makeup_elixir": {:hex, :makeup_elixir, "0.16.0", "f8c570a0d33f8039513fbccaf7108c5d750f47d8defd44088371191b76492b0b", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.2.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "28b2cbdc13960a46ae9a8858c4bebdec3c9a6d7b4b9e7f4ed1502f8159f338e7"}, "makeup_erlang": {:hex, :makeup_erlang, "0.1.1", "3fcb7f09eb9d98dc4d208f49cc955a34218fc41ff6b84df7c75b3e6e533cc65f", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "174d0809e98a4ef0b3309256cbf97101c6ec01c4ab0b23e926a9e17df2077cbb"}, diff --git a/test/encode_test.exs b/test/encode_test.exs index 4f5d33c..bdef42c 100644 --- a/test/encode_test.exs +++ b/test/encode_test.exs @@ -201,11 +201,19 @@ defmodule Jason.EncoderTest do end assert_raise EncodeError, "invalid byte 0x80 in <<128>>", fn -> - assert to_json(<<0x80>>) + assert to_json(<<0x80>>, escape: :native_json) + end + + assert_raise EncodeError, "invalid byte 0x80 in <<128>>", fn -> + assert to_json(<<0x80>>, escape: :elixir_json) end assert_raise EncodeError, fn -> - assert to_json(<>) + assert to_json(<>, escape: :native_json) + end + + assert_raise EncodeError, fn -> + assert to_json(<>, escape: :elixir_json) end end @@ -221,8 +229,14 @@ defmodule Jason.EncoderTest do assert to_json(%{a: 3.14159, b: 1}, pretty: false) == ~s|{"a":3.14159,"b":1}| end - defp to_json(value, opts \\ []) do - Jason.encode!(value, opts) + defp to_json(value) do + native = Jason.encode!(value, escape: :native_json) + elixir = Jason.encode!(value, escape: :elixir_json) + assert native == elixir + native end + defp to_json(value, opts) do + Jason.encode!(value, opts) + end end