From c8a2a9f4e2226d2c2fffeac05b38d24d12e9f006 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Mon, 24 Oct 2016 13:57:44 +0200 Subject: [PATCH] Increase performance of Inspect.BitString.escape/2 --- lib/elixir/lib/inspect.ex | 135 +++++++++++++++----------------- lib/elixir/lib/macro.ex | 2 +- lib/ex_unit/lib/ex_unit/diff.ex | 4 +- 3 files changed, 65 insertions(+), 76 deletions(-) diff --git a/lib/elixir/lib/inspect.ex b/lib/elixir/lib/inspect.ex index 6662394e35..6df7e6029a 100644 --- a/lib/elixir/lib/inspect.ex +++ b/lib/elixir/lib/inspect.ex @@ -88,7 +88,7 @@ defimpl Inspect, for: Atom do atom in Macro.binary_ops or atom in Macro.unary_ops -> ":" <> binary true -> - <> + IO.iodata_to_binary [?:, ?", Inspect.BitString.escape(binary, ?"), ?"] end end @@ -143,7 +143,7 @@ end defimpl Inspect, for: BitString do def inspect(term, %Inspect.Opts{binaries: bins, base: base} = opts) when is_binary(term) do if base == :decimal and (bins == :as_strings or (bins == :infer and String.printable?(term))) do - <> + IO.iodata_to_binary([?", escape(term, ?"), ?"]) else inspect_bitstring(term, opts) end @@ -157,88 +157,82 @@ defimpl Inspect, for: BitString do @doc false def escape(other, char) do - escape(other, char, <<>>) + escape(other, char, []) end - defp escape(<>, char, binary) do - escape(t, char, <>) + defp escape(<>, char, acc) do + escape(t, char, [char, ?\\ | acc]) end - defp escape(<>, char, binary) do - escape(t, char, <>) + defp escape(<>, char, acc) do + escape(t, char, [?{, ?#, ?\\ | acc]) end - defp escape(<>, char, binary) do - escape(t, char, <>) + defp escape(<>, char, acc) do + escape(t, char, [?a, ?\\ | acc]) end - defp escape(<>, char, binary) do - escape(t, char, <>) + defp escape(<>, char, acc) do + escape(t, char, [?b, ?\\ | acc]) end - defp escape(<>, char, binary) do - escape(t, char, <>) + defp escape(<>, char, acc) do + escape(t, char, [?d, ?\\ | acc]) end - defp escape(<>, char, binary) do - escape(t, char, <>) + defp escape(<>, char, acc) do + escape(t, char, [?e, ?\\ | acc]) end - defp escape(<>, char, binary) do - escape(t, char, <>) + defp escape(<>, char, acc) do + escape(t, char, [?f, ?\\ | acc]) end - defp escape(<>, char, binary) do - escape(t, char, <>) + defp escape(<>, char, acc) do + escape(t, char, [?n, ?\\ | acc]) end - defp escape(<>, char, binary) do - escape(t, char, <>) + defp escape(<>, char, acc) do + escape(t, char, [?r, ?\\ | acc]) end - defp escape(<>, char, binary) do - escape(t, char, <>) + defp escape(<>, char, acc) do + escape(t, char, [?\\, ?\\ | acc]) end - defp escape(<>, char, binary) do - escape(t, char, <>) + defp escape(<>, char, acc) do + escape(t, char, [?t, ?\\ | acc]) end - defp escape(<>, char, binary) do - escape(t, char, <>) + defp escape(<>, char, acc) do + escape(t, char, [?v, ?\\ | acc]) end - defp escape(<>, char, binary) do - head = <> - if String.printable?(head) do - escape(t, char, append(head, binary)) - else - <> = head - t = <> - escape(t, char, <>) - end + defp escape(<>, char, acc) + when h in 0x20..0x7E + when h in 0xA0..0xD7FF + when h in 0xE000..0xFFFD + when h in 0x10000..0x10FFFF do + escape(t, char, [<> | acc]) end - defp escape(<>, char, binary) do - escape(t, char, <>) + defp escape(<>, char, acc) do + escape(t, char, [escape_char(h) | acc]) end - defp escape(<<>>, _char, binary), do: binary + defp escape(<<>>, _char, acc), do: :lists.reverse(acc) @doc false # Also used by Regex def escape_char(0) do - <> + [?\\, ?0] end def escape_char(char) when char < 0x100 do <> = <> - <> + [?\\, ?x, to_hex(a), to_hex(b)] end def escape_char(char) when char < 0x10000 do <> = <> - <> + [?\\, ?x, ?{, to_hex(a), to_hex(b), to_hex(c), to_hex(d), ?}] end def escape_char(char) when char < 0x1000000 do <> = <> - <> + [?\\, ?x, ?{, to_hex(a), to_hex(b), to_hex(c), + to_hex(d), to_hex(e), to_hex(f), ?}] end defp to_hex(c) when c in 0..9, do: ?0+c defp to_hex(c) when c in 10..15, do: ?A+c-10 - defp append(<>, binary), do: append(t, <>) - defp append(<<>>, binary), do: binary - ## Bitstrings defp inspect_bitstring("", _opts) do @@ -295,7 +289,7 @@ defimpl Inspect, for: List do cond do lists == :as_charlists or (lists == :infer and printable?(term)) -> - <> + IO.iodata_to_binary [?', Inspect.BitString.escape(IO.chardata_to_string(term), ?'), ?'] keyword?(term) -> surround_many("[", term, "]", opts, &keyword/2) true -> @@ -413,55 +407,50 @@ end defimpl Inspect, for: Regex do def inspect(regex, _opts) do - delim = ?/ - concat ["~r", - <>, - regex.opts] + IO.iodata_to_binary ["~r", ?/, escape(regex.source, ?/), ?/, regex.opts] end defp escape(bin, term), - do: escape(bin, <<>>, term) + do: escape(bin, [], term) defp escape(<> <> rest, buf, term), - do: escape(rest, buf <> <>, term) + do: escape(rest, [term , ?\\ | buf], term) defp escape(<> <> rest, buf, term), - do: escape(rest, buf <> <>, term) + do: escape(rest, [term, ?\\ | buf], term) - # the list of characters is from "String.printable?" impl + # The list of characters is from "String.printable?" implementation # minus characters treated specially by regex: \s, \d, \b, \e defp escape(<> <> rest, buf, term), - do: escape(rest, <>, term) + do: escape(rest, [?n, ?\\ | buf], term) defp escape(<> <> rest, buf, term), - do: escape(rest, <>, term) + do: escape(rest, [?r, ?\\ | buf], term) defp escape(<> <> rest, buf, term), - do: escape(rest, <>, term) + do: escape(rest, [?t, ?\\ | buf], term) defp escape(<> <> rest, buf, term), - do: escape(rest, <>, term) + do: escape(rest, [?v, ?\\ | buf], term) defp escape(<> <> rest, buf, term), - do: escape(rest, <>, term) + do: escape(rest, [?f, ?\\ | buf], term) defp escape(<> <> rest, buf, term), - do: escape(rest, <>, term) + do: escape(rest, [?a, ?\\ | buf], term) - defp escape(<> <> rest, buf, term) do - charstr = <> - if String.printable?(charstr) and not c in [?\d, ?\b, ?\e] do - escape(rest, buf <> charstr, term) - else - escape(rest, buf <> Inspect.BitString.escape_char(c), term) - end - end + defp escape(<>, buf, term) + when c in 0x20..0x7E + when c in 0xA0..0xD7FF + when c in 0xE000..0xFFFD + when c in 0x10000..0x10FFFF, + do: escape(rest, [<> | buf], term) - defp escape(<> <> rest, buf, term), - do: escape(rest, <>, term) + defp escape(<>, buf, term), + do: escape(rest, [Inspect.BitString.escape_char(c) | buf], term) - defp escape(<<>>, buf, _), do: buf + defp escape(<<>>, buf, _), do: :lists.reverse(buf) end defimpl Inspect, for: Function do diff --git a/lib/elixir/lib/macro.ex b/lib/elixir/lib/macro.ex index 7b23872e6d..2b1c65e516 100644 --- a/lib/elixir/lib/macro.ex +++ b/lib/elixir/lib/macro.ex @@ -740,7 +740,7 @@ defmodule Macro do list == [] -> "[]" :io_lib.printable_list(list) -> - "'" <> Inspect.BitString.escape(IO.chardata_to_string(list), ?') <> "'" + IO.iodata_to_binary [?', Inspect.BitString.escape(IO.chardata_to_string(list), ?'), ?'] Inspect.List.keyword?(list) -> "[" <> kw_list_to_string(list, fun) <> "]" true -> diff --git a/lib/ex_unit/lib/ex_unit/diff.ex b/lib/ex_unit/lib/ex_unit/diff.ex index 8c6e0d5b8c..300cba0671 100644 --- a/lib/ex_unit/lib/ex_unit/diff.ex +++ b/lib/ex_unit/lib/ex_unit/diff.ex @@ -69,8 +69,8 @@ defmodule ExUnit.Diff do length1 = String.length(string1) length2 = String.length(string2) if bag_distance(string1, string2) / max(length1, length2) <= 0.6 do - string1 = Inspect.BitString.escape(string1, token) - string2 = Inspect.BitString.escape(string2, token) + string1 = IO.iodata_to_binary Inspect.BitString.escape(string1, token) + string2 = IO.iodata_to_binary Inspect.BitString.escape(string2, token) [{:eq, <>}, script_string(string1, string2), {:eq, <>}] end end