Increase performance of Inspect.BitString.escape/2

This commit is contained in:
José Valim
2016-10-24 14:07:10 +02:00
parent bbd8622218
commit c8a2a9f4e2
3 changed files with 65 additions and 76 deletions
+62 -73
View File
@@ -88,7 +88,7 @@ defimpl Inspect, for: Atom do
atom in Macro.binary_ops or atom in Macro.unary_ops ->
":" <> binary
true ->
<<?:, ?", Inspect.BitString.escape(binary, ?")::binary, ?">>
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
<<?", escape(term, ?")::binary, ?">>
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, t::binary>>, char, binary) do
escape(t, char, <<binary::binary, ?\\, char>>)
defp escape(<<char, t::binary>>, char, acc) do
escape(t, char, [char, ?\\ | acc])
end
defp escape(<<?#, ?{, t::binary>>, char, binary) do
escape(t, char, <<binary::binary, ?\\, ?#, ?{>>)
defp escape(<<?#, ?{, t::binary>>, char, acc) do
escape(t, char, [?{, ?#, ?\\ | acc])
end
defp escape(<<?\a, t::binary>>, char, binary) do
escape(t, char, <<binary::binary, ?\\, ?a>>)
defp escape(<<?\a, t::binary>>, char, acc) do
escape(t, char, [?a, ?\\ | acc])
end
defp escape(<<?\b, t::binary>>, char, binary) do
escape(t, char, <<binary::binary, ?\\, ?b>>)
defp escape(<<?\b, t::binary>>, char, acc) do
escape(t, char, [?b, ?\\ | acc])
end
defp escape(<<?\d, t::binary>>, char, binary) do
escape(t, char, <<binary::binary, ?\\, ?d>>)
defp escape(<<?\d, t::binary>>, char, acc) do
escape(t, char, [?d, ?\\ | acc])
end
defp escape(<<?\e, t::binary>>, char, binary) do
escape(t, char, <<binary::binary, ?\\, ?e>>)
defp escape(<<?\e, t::binary>>, char, acc) do
escape(t, char, [?e, ?\\ | acc])
end
defp escape(<<?\f, t::binary>>, char, binary) do
escape(t, char, <<binary::binary, ?\\, ?f>>)
defp escape(<<?\f, t::binary>>, char, acc) do
escape(t, char, [?f, ?\\ | acc])
end
defp escape(<<?\n, t::binary>>, char, binary) do
escape(t, char, <<binary::binary, ?\\, ?n>>)
defp escape(<<?\n, t::binary>>, char, acc) do
escape(t, char, [?n, ?\\ | acc])
end
defp escape(<<?\r, t::binary>>, char, binary) do
escape(t, char, <<binary::binary, ?\\, ?r>>)
defp escape(<<?\r, t::binary>>, char, acc) do
escape(t, char, [?r, ?\\ | acc])
end
defp escape(<<?\\, t::binary>>, char, binary) do
escape(t, char, <<binary::binary, ?\\, ?\\>>)
defp escape(<<?\\, t::binary>>, char, acc) do
escape(t, char, [?\\, ?\\ | acc])
end
defp escape(<<?\t, t::binary>>, char, binary) do
escape(t, char, <<binary::binary, ?\\, ?t>>)
defp escape(<<?\t, t::binary>>, char, acc) do
escape(t, char, [?t, ?\\ | acc])
end
defp escape(<<?\v, t::binary>>, char, binary) do
escape(t, char, <<binary::binary, ?\\, ?v>>)
defp escape(<<?\v, t::binary>>, char, acc) do
escape(t, char, [?v, ?\\ | acc])
end
defp escape(<<h::utf8, t::binary>>, char, binary) do
head = <<h::utf8>>
if String.printable?(head) do
escape(t, char, append(head, binary))
else
<<byte::8, h::binary>> = head
t = <<h::binary, t::binary>>
escape(t, char, <<binary::binary, escape_char(byte)::binary>>)
end
defp escape(<<h::utf8, t::binary>>, 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, [<<h::utf8>> | acc])
end
defp escape(<<h, t::binary>>, char, binary) do
escape(t, char, <<binary::binary, escape_char(h)::binary>>)
defp escape(<<h, t::binary>>, 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>>
[?\\, ?0]
end
def escape_char(char) when char < 0x100 do
<<a::4, b::4>> = <<char::8>>
<<?\\, ?x, to_hex(a), to_hex(b)>>
[?\\, ?x, to_hex(a), to_hex(b)]
end
def escape_char(char) when char < 0x10000 do
<<a::4, b::4, c::4, d::4>> = <<char::16>>
<<?\\, ?x, ?{, to_hex(a), to_hex(b), to_hex(c), to_hex(d), ?}>>
[?\\, ?x, ?{, to_hex(a), to_hex(b), to_hex(c), to_hex(d), ?}]
end
def escape_char(char) when char < 0x1000000 do
<<a::4, b::4, c::4, d::4, e::4, f::4>> = <<char::24>>
<<?\\, ?x, ?{, to_hex(a), to_hex(b), to_hex(c),
to_hex(d), to_hex(e), to_hex(f), ?}>>
[?\\, ?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(<<h, t::binary>>, binary), do: append(t, <<binary::binary, h>>)
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)) ->
<<?', Inspect.BitString.escape(IO.chardata_to_string(term), ?')::binary, ?'>>
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",
<<delim, escape(regex.source, delim)::binary, delim>>,
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(<<?\\, term>> <> rest, buf, term),
do: escape(rest, buf <> <<?\\, term>>, term)
do: escape(rest, [term , ?\\ | buf], term)
defp escape(<<term>> <> rest, buf, term),
do: escape(rest, buf <> <<?\\, term>>, 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(<<?\n>> <> rest, buf, term),
do: escape(rest, <<buf::binary, ?\\, ?n>>, term)
do: escape(rest, [?n, ?\\ | buf], term)
defp escape(<<?\r>> <> rest, buf, term),
do: escape(rest, <<buf::binary, ?\\, ?r>>, term)
do: escape(rest, [?r, ?\\ | buf], term)
defp escape(<<?\t>> <> rest, buf, term),
do: escape(rest, <<buf::binary, ?\\, ?t>>, term)
do: escape(rest, [?t, ?\\ | buf], term)
defp escape(<<?\v>> <> rest, buf, term),
do: escape(rest, <<buf::binary, ?\\, ?v>>, term)
do: escape(rest, [?v, ?\\ | buf], term)
defp escape(<<?\f>> <> rest, buf, term),
do: escape(rest, <<buf::binary, ?\\, ?f>>, term)
do: escape(rest, [?f, ?\\ | buf], term)
defp escape(<<?\a>> <> rest, buf, term),
do: escape(rest, <<buf::binary, ?\\, ?a>>, term)
do: escape(rest, [?a, ?\\ | buf], term)
defp escape(<<c::utf8>> <> rest, buf, term) do
charstr = <<c::utf8>>
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(<<c::utf8, rest::binary>>, 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, [<<c::utf8>> | buf], term)
defp escape(<<c>> <> rest, buf, term),
do: escape(rest, <<buf::binary, Inspect.BitString.escape_char(c)>>, term)
defp escape(<<c, rest::binary>>, 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
+1 -1
View File
@@ -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 ->
+2 -2
View File
@@ -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, <<token>>}, script_string(string1, string2), {:eq, <<token>>}]
end
end