diff options
author | Ben Wilson <benwilson512@gmail.com> | 2017-05-23 07:00:44 -0400 |
---|---|---|
committer | José Valim <jose.valim@gmail.com> | 2017-05-23 13:00:44 +0200 |
commit | d0af4d7909f2ff6c9fb0ac9aedac61d1cea632ec (patch) | |
tree | 2a7d3e33a8307867762bcb1b919002f4f98ebf27 | |
parent | ad167e2e955af8c7abfd42c2388013b9eb682e77 (diff) | |
download | elixir-d0af4d7909f2ff6c9fb0ac9aedac61d1cea632ec.tar.gz |
Provide inspect opts printable_limit (#5822)
It avoids performance issues found when inspecting large printable strings
-rw-r--r-- | lib/elixir/lib/inspect.ex | 5 | ||||
-rw-r--r-- | lib/elixir/lib/inspect/algebra.ex | 5 | ||||
-rw-r--r-- | lib/elixir/lib/string.ex | 44 | ||||
-rw-r--r-- | lib/elixir/test/elixir/inspect_test.exs | 9 | ||||
-rw-r--r-- | lib/iex/test/iex/autocomplete_test.exs | 4 |
5 files changed, 46 insertions, 21 deletions
diff --git a/lib/elixir/lib/inspect.ex b/lib/elixir/lib/inspect.ex index e661874a8..46a9b6ffe 100644 --- a/lib/elixir/lib/inspect.ex +++ b/lib/elixir/lib/inspect.ex @@ -99,8 +99,9 @@ defimpl Inspect, for: Atom do 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 + def inspect(term, %Inspect.Opts{binaries: bins, base: base, printable_limit: printable_limit} = opts) when is_binary(term) do + if base == :decimal and (bins == :as_strings or (bins == :infer and String.printable?(term, printable_limit))) do + term = String.slice(term, 0..printable_limit) inspected = IO.iodata_to_binary([?", escape(term, ?"), ?"]) color(inspected, :string, opts) else diff --git a/lib/elixir/lib/inspect/algebra.ex b/lib/elixir/lib/inspect/algebra.ex index 46aa89412..d57e56245 100644 --- a/lib/elixir/lib/inspect/algebra.ex +++ b/lib/elixir/lib/inspect/algebra.ex @@ -27,6 +27,9 @@ defmodule Inspect.Opts do bitstrings, maps, lists and any other collection of items. It does not apply to strings nor charlists and defaults to 50. + * `:printable_limit` - limits the number of bytes that are printed for strings + and char lists. Defaults to 1024. + * `:pretty` - if set to `true` enables pretty printing, defaults to `false`. * `:width` - defaults to 80 characters, used when pretty is `true` or when @@ -55,6 +58,7 @@ defmodule Inspect.Opts do charlists: :infer, char_lists: :infer, limit: 50, + printable_limit: 1024, width: 80, base: :decimal, pretty: false, @@ -70,6 +74,7 @@ defmodule Inspect.Opts do charlists: :infer | :as_lists | :as_charlists, char_lists: :infer | :as_lists | :as_char_lists, limit: pos_integer | :infinity, + printable_limit: pos_integer | :infinity, width: pos_integer | :infinity, base: :decimal | :binary | :hex | :octal, pretty: boolean, diff --git a/lib/elixir/lib/string.ex b/lib/elixir/lib/string.ex index c7ffa6b00..2d96ed999 100644 --- a/lib/elixir/lib/string.ex +++ b/lib/elixir/lib/string.ex @@ -208,39 +208,49 @@ defmodule String do @doc """ Checks if a string contains only printable characters. + Takes an optional `limit` as a second argument. `printable?/2` only + checks the printability of the string up to the `limit`. + ## Examples iex> String.printable?("abc") true + iex> String.printable?("abc" <> <<0>>) + false + + iex> String.printable?("abc" <> <<0>>, 2) + true + """ @spec printable?(t) :: boolean - def printable?(string) + @spec printable?(t, non_neg_integer | :infinity) :: boolean + def printable?(string, counter \\ :infinity) + + def printable?(<<>>, _), do: true + def printable?(_, 0), do: true for char <- 0x20..0x7E do - def printable?(<<unquote(char), rest::binary>>) do - printable?(rest) + def printable?(<<unquote(char), rest::binary>>, counter) do + printable?(rest, decrement(counter)) + end + end + for char <- '\n\r\t\v\b\f\e\d\a' do + def printable?(<<unquote(char), rest::binary>>, counter) do + printable?(rest, decrement(counter)) end end - def printable?(<<?\n, rest::binary>>), do: printable?(rest) - def printable?(<<?\r, rest::binary>>), do: printable?(rest) - def printable?(<<?\t, rest::binary>>), do: printable?(rest) - def printable?(<<?\v, rest::binary>>), do: printable?(rest) - def printable?(<<?\b, rest::binary>>), do: printable?(rest) - def printable?(<<?\f, rest::binary>>), do: printable?(rest) - def printable?(<<?\e, rest::binary>>), do: printable?(rest) - def printable?(<<?\d, rest::binary>>), do: printable?(rest) - def printable?(<<?\a, rest::binary>>), do: printable?(rest) - - def printable?(<<char::utf8, rest::binary>>) + def printable?(<<char::utf8, rest::binary>>, counter) when char in 0xA0..0xD7FF when char in 0xE000..0xFFFD when char in 0x10000..0x10FFFF do - printable?(rest) + printable?(rest, decrement(counter)) end - def printable?(<<>>), do: true - def printable?(binary) when is_binary(binary), do: false + def printable?(binary, _) when is_binary(binary), do: false + + defp decrement(:infinity), do: :infinity + defp decrement(counter), do: counter - 1 @doc ~S""" Divides a string into substrings at each Unicode whitespace diff --git a/lib/elixir/test/elixir/inspect_test.exs b/lib/elixir/test/elixir/inspect_test.exs index 968bf0349..1dadd44f1 100644 --- a/lib/elixir/test/elixir/inspect_test.exs +++ b/lib/elixir/test/elixir/inspect_test.exs @@ -141,6 +141,15 @@ defmodule Inspect.BitStringTest do test "unprintable with opts" do assert inspect(<<193, 193, 193, 193>>, limit: 3) == "<<193, 193, 193, ...>>" end + + test "string with limits" do + assert inspect("hello world", printable_limit: 4) == ~s("hello") + # non printable characters after the limit don't matter + assert inspect("hello world" <> <<0>>, printable_limit: 4) == ~s("hello") + # non printable strings aren't affected by printable limit + assert inspect(<<0,1,2,3,4>>, printable_limit: 3) == ~s(<<0, 1, 2, 3, 4>>) + + end end defmodule Inspect.NumberTest do diff --git a/lib/iex/test/iex/autocomplete_test.exs b/lib/iex/test/iex/autocomplete_test.exs index 28f53a314..e2a4bf674 100644 --- a/lib/iex/test/iex/autocomplete_test.exs +++ b/lib/iex/test/iex/autocomplete_test.exs @@ -160,8 +160,8 @@ defmodule IEx.AutocompleteTest do end test "function completion with arity" do - assert expand('String.printable?') == {:yes, '', ['printable?/1']} - assert expand('String.printable?/') == {:yes, '', ['printable?/1']} + assert expand('String.printable?') == {:yes, '', ['printable?/1', 'printable?/2']} + assert expand('String.printable?/') == {:yes, '', ['printable?/1', 'printable?/2']} end test "function completion using a variable bound to a module" do |