Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Newer
Older
100644 486 lines (440 sloc) 18.808 kB
5bc97a9 @Vagabond Forgot to add trunc_io
Vagabond authored
1 %% ``The contents of this file are subject to the Erlang Public License,
2 %% Version 1.1, (the "License"); you may not use this file except in
3 %% compliance with the License. You should have received a copy of the
4 %% Erlang Public License along with your Erlang distribution. If not, it can be
5 %% retrieved via the world wide web at http://www.erlang.org/.
6 %%
7 %% Software distributed under the License is distributed on an "AS IS"
8 %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
9 %% the License for the specific language governing rights and limitations
10 %% under the License.
11 %%
12 %% The Initial Developer of the Original Code is Corelatus AB.
13 %% Portions created by Corelatus are Copyright 2003, Corelatus
14 %% AB. All Rights Reserved.''
15 %%
16 %% @doc Module to print out terms for logging. Limits by length rather than depth.
17 %%
18 %% The resulting string may be slightly larger than the limit; the intention
19 %% is to provide predictable CPU and memory consumption for formatting
20 %% terms, not produce precise string lengths.
21 %%
22 %% Typical use:
23 %%
24 %% trunc_io:print(Term, 500).
25 %%
26 %% Source license: Erlang Public License.
27 %% Original author: Matthias Lang, <tt>matthias@corelatus.se</tt>
4ac0137 @Vagabond Rename trunc_io to lager_trunc_io to prevent clashes
Vagabond authored
28 %%
29 %% Various changes to this module, most notably the format/3 implementation
6a8403f @Vagabond Fix edoc generation
Vagabond authored
30 %% were added by Andrew Thompson `<andrew@basho.com>'. The module has been renamed
4ac0137 @Vagabond Rename trunc_io to lager_trunc_io to prevent clashes
Vagabond authored
31 %% to avoid conflicts with the vanilla module.
5bc97a9 @Vagabond Forgot to add trunc_io
Vagabond authored
32
4ac0137 @Vagabond Rename trunc_io to lager_trunc_io to prevent clashes
Vagabond authored
33 -module(lager_trunc_io).
5bc97a9 @Vagabond Forgot to add trunc_io
Vagabond authored
34 -author('matthias@corelatus.se').
35 %% And thanks to Chris Newcombe for a bug fix
01dc584 @Vagabond Add truc_io:format and use it in the crash logger
Vagabond authored
36 -export([format/3, print/2, fprint/2, safe/2]). % interface functions
5bc97a9 @Vagabond Forgot to add trunc_io
Vagabond authored
37 -version("$Id: trunc_io.erl,v 1.11 2009-02-23 12:01:06 matthias Exp $").
38
03d3fa1 @Vagabond Fixes and tests for deep-list handling in trunc_io:format
Vagabond authored
39 -ifdef(TEST).
40 -export([perf/0, perf/3, perf1/0, test/0, test/2]). % testing functions
41 -include_lib("eunit/include/eunit.hrl").
42 -endif.
43
01dc584 @Vagabond Add truc_io:format and use it in the crash logger
Vagabond authored
44 format(String, Args, Max) ->
45 Parts = re:split(String,
d230cba @Vagabond More fixes found by QC and some QC improvements
Vagabond authored
46 "(~(?:-??\\d+\\.|\\*\\.|\\.|)(?:-??\\d+\\.|\\*\\.|\\.|)(?:-??\\d+|\\*|)(?:t|)(?:[cfegswpWPBX#bx+ni~]))",
01dc584 @Vagabond Add truc_io:format and use it in the crash logger
Vagabond authored
47 [{return, list}, trim]),
48 Maxlen = Max - length(String),
49 format(Parts, Args, Maxlen, [], []).
50
37f190f @Vagabond Try to give more equal allocations to large terms
Vagabond authored
51 format([], _Args, Max, Acc, ArgAcc) ->
52 FmtArgs = resolve_futures(Max, ArgAcc),
53 io_lib:format(lists:flatten(lists:reverse(Acc)), lists:reverse(FmtArgs));
01dc584 @Vagabond Add truc_io:format and use it in the crash logger
Vagabond authored
54 format([[] | T], Args, Max, Acc, ArgAcc) ->
55 % discard the null list generated by split
56 format(T, Args, Max, Acc, ArgAcc);
d230cba @Vagabond More fixes found by QC and some QC improvements
Vagabond authored
57 format(["~~" | T], Args, Max, Acc, ArgAcc) ->
58 format(T, Args, Max+1, ["~~" | Acc], ArgAcc);
01dc584 @Vagabond Add truc_io:format and use it in the crash logger
Vagabond authored
59 format(["~n" | T], Args, Max, Acc, ArgAcc) ->
60 % ignore newlines for the purposes of argument indexing
61 format(T, Args, Max+1, ["~n" | Acc], ArgAcc);
62 format(["~i" | T], [AH | AT], Max, Acc, ArgAcc) ->
63 % ~i means ignore this argument, but we'll just pass it through
64 format(T, AT, Max+2, ["~i" | Acc], [AH | ArgAcc]);
d230cba @Vagabond More fixes found by QC and some QC improvements
Vagabond authored
65 format([[$~|H]| T], [AH1, AH2 | AT], Max, Acc, ArgAcc) when H == "X"; H == "x" ->
66 %% ~X consumes 2 arguments. It only prints integers so we can leave it alone
67 format(T, AT, Max, ["~X" | Acc], [AH2, AH1 | ArgAcc]);
68 format([[$~|H]| T], [AH1, _AH2 | AT], Max, Acc, ArgAcc) when H == "W"; H == "P" ->
69 %% ~P and ~W consume 2 arguments, the second one being a depth limiter.
70 %% trunc_io isn't (yet) depth aware, so we can't honor this format string
71 %% safely at the moment, so just treat it like a regular ~p
72 %% TODO support for depth limiting
03d3fa1 @Vagabond Fixes and tests for deep-list handling in trunc_io:format
Vagabond authored
73 Input = case H == "P" andalso lager_stdlib:string_p(AH1) of
74 true ->
75 lists:flatten(AH1);
76 _ -> AH1
77 end,
78 case print(Input, Max + 2) of
d230cba @Vagabond More fixes found by QC and some QC improvements
Vagabond authored
79 {_Res, Max} ->
80 % this isn't the last argument, but it consumed all available space
81 % delay calculating the print size until the end
03d3fa1 @Vagabond Fixes and tests for deep-list handling in trunc_io:format
Vagabond authored
82 format(T, AT, Max + 2, ["~s" | Acc], [{future, Input} | ArgAcc]);
d230cba @Vagabond More fixes found by QC and some QC improvements
Vagabond authored
83 {String, Length} ->
84 format(T, AT, Max + 2 - Length, ["~s" | Acc], [String | ArgAcc])
85 end;
01dc584 @Vagabond Add truc_io:format and use it in the crash logger
Vagabond authored
86 format([[$~|H]| T], [AH | AT], Max, Acc, ArgAcc) when length(H) == 1 ->
87 % single character format specifier, relatively simple
88 case H of
89 _ when H == "p"; H == "w"; H == "s" ->
03d3fa1 @Vagabond Fixes and tests for deep-list handling in trunc_io:format
Vagabond authored
90 Input = case (H == "s" orelse H == "p") andalso lager_stdlib:string_p(AH) of
91 true ->
92 lists:flatten(AH);
93 _ -> AH
94 end,
01dc584 @Vagabond Add truc_io:format and use it in the crash logger
Vagabond authored
95 %okay, these are prime candidates for rewriting
03d3fa1 @Vagabond Fixes and tests for deep-list handling in trunc_io:format
Vagabond authored
96 case print(Input, Max + 2) of
d230cba @Vagabond More fixes found by QC and some QC improvements
Vagabond authored
97 {_Res, Max} ->
37f190f @Vagabond Try to give more equal allocations to large terms
Vagabond authored
98 % this isn't the last argument, but it consumed all available space
99 % delay calculating the print size until the end
03d3fa1 @Vagabond Fixes and tests for deep-list handling in trunc_io:format
Vagabond authored
100 format(T, AT, Max + 2, ["~s" | Acc], [{future, Input} | ArgAcc]);
37f190f @Vagabond Try to give more equal allocations to large terms
Vagabond authored
101 {String, Length} ->
102 {Value, RealLen} = case H of
103 "s" ->
a0440a9 @Vagabond Smarter doublequote stripping when printing strings with ~s
Vagabond authored
104 % strip off the doublequotes, if applicable
7e9da64 @Vagabond Several fixes for printing binaries, improve quote stripping
Vagabond authored
105 Trimmed = unquote_string(lists:flatten(String)),
a0440a9 @Vagabond Smarter doublequote stripping when printing strings with ~s
Vagabond authored
106 {Trimmed, length(Trimmed)};
37f190f @Vagabond Try to give more equal allocations to large terms
Vagabond authored
107 _ ->
108 {String, Length}
109 end,
95f87e5 @Vagabond Remove unecessary and pointless call to lists:flatten
Vagabond authored
110 format(T, AT, Max + 2 - RealLen, ["~s" | Acc], [Value | ArgAcc])
37f190f @Vagabond Try to give more equal allocations to large terms
Vagabond authored
111 end;
01dc584 @Vagabond Add truc_io:format and use it in the crash logger
Vagabond authored
112 _ ->
113 % whatever, just pass them on through
d230cba @Vagabond More fixes found by QC and some QC improvements
Vagabond authored
114 format(T, AT, Max, [[$~ | H] | Acc], [AH | ArgAcc])
01dc584 @Vagabond Add truc_io:format and use it in the crash logger
Vagabond authored
115 end;
116 format([[$~|H]| T], [AH | AT], Max, Acc, ArgAcc) ->
9cbb72c @Vagabond Cleanup some commented out code
Vagabond authored
117 %% its actually simplest to just look at the last character in the string
d230cba @Vagabond More fixes found by QC and some QC improvements
Vagabond authored
118 case lists:nth(length(H), H) of
119 C when C == $p; C == $w; C == $s ->
01dc584 @Vagabond Add truc_io:format and use it in the crash logger
Vagabond authored
120 %okay, these are prime candidates for rewriting
03d3fa1 @Vagabond Fixes and tests for deep-list handling in trunc_io:format
Vagabond authored
121 Input = case (C == $s orelse C == $p) andalso lager_stdlib:string_p(AH) of
122 true ->
123 lists:flatten(AH);
124 _ -> AH
125 end,
126 case print(Input, Max + length(H) + 1) of
d230cba @Vagabond More fixes found by QC and some QC improvements
Vagabond authored
127 {_Res, Max} ->
37f190f @Vagabond Try to give more equal allocations to large terms
Vagabond authored
128 % this isn't the last argument, but it consumed all available space
129 % delay calculating the print size until the end
03d3fa1 @Vagabond Fixes and tests for deep-list handling in trunc_io:format
Vagabond authored
130 format(T, AT, Max + length(H) + 1, ["~s" | Acc], [{future, Input} | ArgAcc]);
37f190f @Vagabond Try to give more equal allocations to large terms
Vagabond authored
131 {String, Length} ->
03d3fa1 @Vagabond Fixes and tests for deep-list handling in trunc_io:format
Vagabond authored
132 {Value, RealLen} = case C of
133 $s ->
7e9da64 @Vagabond Several fixes for printing binaries, improve quote stripping
Vagabond authored
134 % strip off the doublequotes, if applicable
135 Trimmed = unquote_string(lists:flatten(String)),
136 {Trimmed, length(Trimmed)};
37f190f @Vagabond Try to give more equal allocations to large terms
Vagabond authored
137 _ ->
138 {String, Length}
139 end,
140 format(T, AT, Max + length(H) + 1 - RealLen, ["~s" | Acc], [Value | ArgAcc])
141 end;
d230cba @Vagabond More fixes found by QC and some QC improvements
Vagabond authored
142 C when C == $P; C == $W ->
143 %% ~P and ~W consume 2 arguments, the second one being a depth limiter.
144 %% trunc_io isn't (yet) depth aware, so we can't honor this format string
145 %% safely at the moment, so just treat it like a regular ~p
146 %% TODO support for depth limiting
147 [_ | AT2] = AT,
03d3fa1 @Vagabond Fixes and tests for deep-list handling in trunc_io:format
Vagabond authored
148 Input = case C == $P andalso lager_stdlib:string_p(AH) of
149 true ->
150 lists:flatten(AH);
151 _ -> AH
152 end,
153 case print(Input, Max + 2) of
d230cba @Vagabond More fixes found by QC and some QC improvements
Vagabond authored
154 {_Res, Max} ->
155 % this isn't the last argument, but it consumed all available space
156 % delay calculating the print size until the end
03d3fa1 @Vagabond Fixes and tests for deep-list handling in trunc_io:format
Vagabond authored
157 format(T, AT2, Max + 2, ["~s" | Acc], [{future, Input} | ArgAcc]);
d230cba @Vagabond More fixes found by QC and some QC improvements
Vagabond authored
158 {String, Length} ->
159 format(T, AT2, Max + 2 - Length, ["~s" | Acc], [String | ArgAcc])
160 end;
161 C when C == $X; C == $x ->
162 %% ~X consumes 2 arguments. It only prints integers so we can leave it alone
01dc584 @Vagabond Add truc_io:format and use it in the crash logger
Vagabond authored
163 [AH2 | AT2] = AT,
164 format(T, AT2, Max, [[$~|H]|Acc], [AH2, AH |ArgAcc]);
d230cba @Vagabond More fixes found by QC and some QC improvements
Vagabond authored
165 _ ->
166 format(T, AT, Max, [[$~|H] | Acc], [AH|ArgAcc])
01dc584 @Vagabond Add truc_io:format and use it in the crash logger
Vagabond authored
167 end;
168 format([H | T], Args, Max, Acc, ArgAcc) ->
169 format(T, Args, Max, [H | Acc], ArgAcc).
5bc97a9 @Vagabond Forgot to add trunc_io
Vagabond authored
170
37f190f @Vagabond Try to give more equal allocations to large terms
Vagabond authored
171 %% for all the really big terms encountered in a format/3 call, try to give each of them an equal share
172 resolve_futures(Max, Args) ->
173 Count = length(lists:filter(fun({future, _}) -> true; (_) -> false end, Args)),
174 case Count of
175 0 ->
176 Args;
177 _ ->
178 SingleFmt = Max div Count,
179 lists:map(fun({future, Value}) -> element(1, print(Value, SingleFmt)); (X) -> X end, Args)
180 end.
181
5bc97a9 @Vagabond Forgot to add trunc_io
Vagabond authored
182 %% @doc Returns an flattened list containing the ASCII representation of the given
183 %% term.
184 -spec fprint(term(), pos_integer()) -> string().
185 fprint(T, Max) ->
186 {L, _} = print(T, Max),
187 lists:flatten(L).
188
189 %% @doc Same as print, but never crashes.
190 %%
191 %% This is a tradeoff. Print might conceivably crash if it's asked to
192 %% print something it doesn't understand, for example some new data
193 %% type in a future version of Erlang. If print crashes, we fall back
194 %% to io_lib to format the term, but then the formatting is
195 %% depth-limited instead of length limited, so you might run out
196 %% memory printing it. Out of the frying pan and into the fire.
197 %%
198 -spec safe(term(), pos_integer()) -> {string(), pos_integer()} | {string()}.
199 safe(What, Len) ->
200 case catch print(What, Len) of
201 {L, Used} when is_list(L) -> {L, Used};
202 _ -> {"unable to print" ++ io_lib:write(What, 99)}
203 end.
204
205 %% @doc Returns {List, Length}
206 -spec print(term(), pos_integer()) -> {iolist(), pos_integer()}.
207 print(_, Max) when Max < 0 -> {"...", 3};
208 print(Tuple, Max) when is_tuple(Tuple) ->
209 {TC, Len} = tuple_contents(Tuple, Max-2),
210 {[${, TC, $}], Len + 2};
211
212 %% @doc We assume atoms, floats, funs, integers, PIDs, ports and refs never need
213 %% to be truncated. This isn't strictly true, someone could make an
214 %% arbitrarily long bignum. Let's assume that won't happen unless someone
215 %% is being malicious.
216 %%
217 print(Atom, _Max) when is_atom(Atom) ->
218 L = atom_to_list(Atom),
3babca1 @Vagabond Make sure atoms needing quoting are quoted.
Vagabond authored
219 R = case atom_needs_quoting_start(L) of
220 true -> lists:flatten([$', L, $']);
221 false -> L
222 end,
223 {R, length(R)};
5bc97a9 @Vagabond Forgot to add trunc_io
Vagabond authored
224
225 print(<<>>, _Max) ->
226 {"<<>>", 4};
227
71fae1b @Vagabond Truncator for funs; special case binary truncation when max is 0
Vagabond authored
228 print(Binary, 0) when is_binary(Binary) ->
229 {"<<..>>", 6};
230
5bc97a9 @Vagabond Forgot to add trunc_io
Vagabond authored
231 print(Binary, Max) when is_binary(Binary) ->
232 B = binary_to_list(Binary, 1, lists:min([Max, size(Binary)])),
233 {L, Len} = alist_start(B, Max-4),
7e9da64 @Vagabond Several fixes for printing binaries, improve quote stripping
Vagabond authored
234 {Res, Length} = case L of
235 [91, X, 93] ->
236 {X, Len - 2};
237 X ->
238 {X, Len}
239 end,
240 {["<<", Res, ">>"], Length+4};
5bc97a9 @Vagabond Forgot to add trunc_io
Vagabond authored
241
242 print(Float, _Max) when is_float(Float) ->
9a577df @Vagabond Use the io_lib_format:fwrite_g function to print floats
Vagabond authored
243 %% use the same function io_lib:format uses to print floats
244 %% float_to_list is way too verbose.
245 L = io_lib_format:fwrite_g(Float),
5bc97a9 @Vagabond Forgot to add trunc_io
Vagabond authored
246 {L, length(L)};
247
71fae1b @Vagabond Truncator for funs; special case binary truncation when max is 0
Vagabond authored
248 print(Fun, Max) when is_function(Fun) ->
5bc97a9 @Vagabond Forgot to add trunc_io
Vagabond authored
249 L = erlang:fun_to_list(Fun),
71fae1b @Vagabond Truncator for funs; special case binary truncation when max is 0
Vagabond authored
250 case length(L) > Max of
251 true ->
252 S = erlang:max(5, Max),
253 Res = string:substr(L, 1, S) ++ "..>",
254 {Res, length(Res)};
255 _ ->
256 {L, length(L)}
257 end;
5bc97a9 @Vagabond Forgot to add trunc_io
Vagabond authored
258
259 print(Integer, _Max) when is_integer(Integer) ->
260 L = integer_to_list(Integer),
261 {L, length(L)};
262
263 print(Pid, _Max) when is_pid(Pid) ->
264 L = pid_to_list(Pid),
265 {L, length(L)};
266
267 print(Ref, _Max) when is_reference(Ref) ->
268 L = erlang:ref_to_list(Ref),
269 {L, length(L)};
270
271 print(Port, _Max) when is_port(Port) ->
272 L = erlang:port_to_list(Port),
273 {L, length(L)};
274
275 print(List, Max) when is_list(List) ->
276 alist_start(List, Max).
277
278 %% Returns {List, Length}
279 tuple_contents(Tuple, Max) ->
280 L = tuple_to_list(Tuple),
281 list_body(L, Max).
282
283 %% Format the inside of a list, i.e. do not add a leading [ or trailing ].
284 %% Returns {List, Length}
285 list_body([], _) -> {[], 0};
286 list_body(_, Max) when Max < 4 -> {"...", 3};
287 list_body([H|T], Max) ->
288 {List, Len} = print(H, Max),
289 {Final, FLen} = list_bodyc(T, Max - Len),
290 {[List|Final], FLen + Len};
291 list_body(X, Max) -> %% improper list
292 {List, Len} = print(X, Max - 1),
293 {[$|,List], Len + 1}.
294
295 list_bodyc([], _) -> {[], 0};
296 list_bodyc(_, Max) when Max < 4 -> {"...", 3};
297 list_bodyc([H|T], Max) ->
298 {List, Len} = print(H, Max),
299 {Final, FLen} = list_bodyc(T, Max - Len - 1),
300 {[$,, List|Final], FLen + Len + 1};
301 list_bodyc(X,Max) -> %% improper list
302 {List, Len} = print(X, Max - 1),
303 {[$|,List], Len + 1}.
304
305 %% The head of a list we hope is ascii. Examples:
306 %%
307 %% [65,66,67] -> "ABC"
308 %% [65,0,67] -> "A"[0,67]
309 %% [0,65,66] -> [0,65,66]
310 %% [65,b,66] -> "A"[b,66]
311 %%
312 alist_start([], _) -> {"[]", 2};
313 alist_start(_, Max) when Max < 4 -> {"...", 3};
af4ea7f @Vagabond Don't treat floats in a list as printable characters Reported by @bry…
Vagabond authored
314 alist_start([H|T], Max) when is_integer(H), H >= 16#20, H =< 16#7e -> % definitely printable
5bc97a9 @Vagabond Forgot to add trunc_io
Vagabond authored
315 {L, Len} = alist([H|T], Max-1),
01dc584 @Vagabond Add truc_io:format and use it in the crash logger
Vagabond authored
316 {[$"|L], Len + 1};
af4ea7f @Vagabond Don't treat floats in a list as printable characters Reported by @bry…
Vagabond authored
317 alist_start([H|T], Max) when H =:= 9; H =:= 10; H =:= 13 -> % show as space
5bc97a9 @Vagabond Forgot to add trunc_io
Vagabond authored
318 {L, Len} = alist(T, Max-1),
319 {[$ |L], Len + 1};
320 alist_start(L, Max) ->
321 {R, Len} = list_body(L, Max-2),
322 {[$[, R, $]], Len + 2}.
323
324 alist([], _) -> {"\"", 1};
325 alist(_, Max) when Max < 5 -> {"...\"", 4};
af4ea7f @Vagabond Don't treat floats in a list as printable characters Reported by @bry…
Vagabond authored
326 alist([H|T], Max) when is_integer(H), H >= 16#20, H =< 16#7e -> % definitely printable
5bc97a9 @Vagabond Forgot to add trunc_io
Vagabond authored
327 {L, Len} = alist(T, Max-1),
328 {[H|L], Len + 1};
af4ea7f @Vagabond Don't treat floats in a list as printable characters Reported by @bry…
Vagabond authored
329 alist([H|T], Max) when H =:= 9; H =:= 10; H =:= 13 -> % show as space
5bc97a9 @Vagabond Forgot to add trunc_io
Vagabond authored
330 {L, Len} = alist(T, Max-1),
331 {[$ |L], Len + 1};
332 alist(L, Max) ->
333 {R, Len} = list_body(L, Max-3),
01dc584 @Vagabond Add truc_io:format and use it in the crash logger
Vagabond authored
334 {[$", $[, R, $]], Len + 3}.
5bc97a9 @Vagabond Forgot to add trunc_io
Vagabond authored
335
3babca1 @Vagabond Make sure atoms needing quoting are quoted.
Vagabond authored
336 %% is the first character in the atom alphabetic & lowercase?
337 atom_needs_quoting_start([H|T]) when H >= $a, H =< $z ->
338 atom_needs_quoting(T);
339 atom_needs_quoting_start(_) ->
340 true.
341
342 atom_needs_quoting([]) ->
343 false;
344 atom_needs_quoting([H|T]) when (H >= $a andalso H =< $z);
345 (H >= $A andalso H =< $Z);
346 H == $@; H == $_ ->
347 atom_needs_quoting(T);
348 atom_needs_quoting(_) ->
349 true.
5bc97a9 @Vagabond Forgot to add trunc_io
Vagabond authored
350
7e9da64 @Vagabond Several fixes for printing binaries, improve quote stripping
Vagabond authored
351 unquote_string([$<, $<, $"|T] = Str) ->
352 case string:substr(T, length(T) - 2) of
353 "\">>" ->
354 string:substr(T, 1, length(T) - 3);
355 _ ->
356 Str
357 end;
358 unquote_string([$"|_] = Str) ->
359 case lists:last(Str) == $" of
360 true ->
361 string:strip(Str, both, $");
362 _ ->
363 Str
364 end;
365 unquote_string([$'|_] = Str) ->
366 case lists:last(Str) == $' of
367 true ->
368 string:strip(Str, both, $');
369 _ ->
370 Str
371 end;
372 unquote_string(S) ->
373 S.
374
03d3fa1 @Vagabond Fixes and tests for deep-list handling in trunc_io:format
Vagabond authored
375 -ifdef(TEST).
5bc97a9 @Vagabond Forgot to add trunc_io
Vagabond authored
376 %%--------------------
377 %% The start of a test suite. So far, it only checks for not crashing.
378 -spec test() -> ok.
379 test() ->
380 test(trunc_io, print).
381
382 -spec test(atom(), atom()) -> ok.
383 test(Mod, Func) ->
384 Simple_items = [atom, 1234, 1234.0, {tuple}, [], [list], "string", self(),
385 <<1,2,3>>, make_ref(), fun() -> ok end],
386 F = fun(A) ->
387 Mod:Func(A, 100),
388 Mod:Func(A, 2),
389 Mod:Func(A, 20)
390 end,
391
392 G = fun(A) ->
393 case catch F(A) of
394 {'EXIT', _} -> exit({failed, A});
395 _ -> ok
396 end
397 end,
398
399 lists:foreach(G, Simple_items),
400
401 Tuples = [ {1,2,3,a,b,c}, {"abc", def, 1234},
402 {{{{a},b,c,{d},e}},f}],
403
404 Lists = [ [1,2,3,4,5,6,7], lists:seq(1,1000),
405 [{a}, {a,b}, {a, [b,c]}, "def"], [a|b], [$a|$b] ],
406
407
408 lists:foreach(G, Tuples),
409 lists:foreach(G, Lists).
410
411 -spec perf() -> ok.
412 perf() ->
413 {New, _} = timer:tc(trunc_io, perf, [trunc_io, print, 1000]),
414 {Old, _} = timer:tc(trunc_io, perf, [io_lib, write, 1000]),
415 io:fwrite("New code took ~p us, old code ~p\n", [New, Old]).
416
417 -spec perf(atom(), atom(), integer()) -> done.
418 perf(M, F, Reps) when Reps > 0 ->
419 test(M,F),
420 perf(M,F,Reps-1);
421 perf(_,_,_) ->
422 done.
423
424 %% Performance test. Needs a particularly large term I saved as a binary...
425 -spec perf1() -> {non_neg_integer(), non_neg_integer()}.
426 perf1() ->
427 {ok, Bin} = file:read_file("bin"),
428 A = binary_to_term(Bin),
429 {N, _} = timer:tc(trunc_io, print, [A, 1500]),
430 {M, _} = timer:tc(io_lib, write, [A]),
431 {N, M}.
03d3fa1 @Vagabond Fixes and tests for deep-list handling in trunc_io:format
Vagabond authored
432
433 format_test() ->
434 %% simple format strings
435 ?assertEqual("foobar", lists:flatten(format("~s", [["foo", $b, $a, $r]], 50))),
436 ?assertEqual("\"foobar\"", lists:flatten(format("~p", [["foo", $b, $a, $r]], 50))),
437 ?assertEqual("\"foobar\"", lists:flatten(format("~P", [["foo", $b, $a, $r], 10], 50))),
438 ?assertEqual("[\"foo\",98,97,114]", lists:flatten(format("~w", [["foo", $b, $a, $r], 10], 50))),
439
440 %% complex ones
441 ?assertEqual("foobar", lists:flatten(format("~10s", [["foo", $b, $a, $r]], 50))),
442 ?assertEqual("\"foobar\"", lists:flatten(format("~10p", [["foo", $b, $a, $r]], 50))),
443 ?assertEqual("\"foobar\"", lists:flatten(format("~10P", [["foo", $b, $a, $r], 10], 50))),
444 ?assertEqual("[\"foo\",98,97,114]", lists:flatten(format("~10W", [["foo", $b, $a, $r], 10], 50))),
445 ok.
446
9a577df @Vagabond Use the io_lib_format:fwrite_g function to print floats
Vagabond authored
447 atom_quoting_test() ->
448 ?assertEqual("hello", lists:flatten(format("~p", [hello], 50))),
449 ?assertEqual("'hello world'", lists:flatten(format("~p", ['hello world'], 50))),
450 ?assertEqual("hello_world", lists:flatten(format("~p", ['hello_world'], 50))),
451 ?assertEqual("'node@127.0.0.1'", lists:flatten(format("~p", ['node@127.0.0.1'], 50))),
452 ?assertEqual("node@nohost", lists:flatten(format("~p", [node@nohost], 50))),
453 ok.
454
455 sane_float_printing_test() ->
456 ?assertEqual("1.0", lists:flatten(format("~p", [1.0], 50))),
457 ?assertEqual("1.23456789", lists:flatten(format("~p", [1.23456789], 50))),
458 ?assertEqual("1.23456789", lists:flatten(format("~p", [1.234567890], 50))),
459 ?assertEqual("0.3333333333333333", lists:flatten(format("~p", [1/3], 50))),
460 ?assertEqual("0.1234567", lists:flatten(format("~p", [0.1234567], 50))),
461 ok.
462
af4ea7f @Vagabond Don't treat floats in a list as printable characters Reported by @bry…
Vagabond authored
463 float_inside_list_test() ->
464 ?assertEqual("\"a\"[38.233913133184835,99]", lists:flatten(format("~p", [[$a, 38.233913133184835, $c]], 50))),
465 ?assertEqual("\"a\"[38.233913133184835,99]", lists:flatten(format("~s", [[$a, 38.233913133184835, $c]], 50))),
466 ok.
467
a0440a9 @Vagabond Smarter doublequote stripping when printing strings with ~s
Vagabond authored
468 quote_strip_test() ->
469 ?assertEqual("\"hello\"", lists:flatten(format("~p", ["hello"], 50))),
470 ?assertEqual("hello", lists:flatten(format("~s", ["hello"], 50))),
471 ?assertEqual("hello", lists:flatten(format("~s", [hello], 50))),
472 ?assertEqual("hello", lists:flatten(format("~p", [hello], 50))),
7e9da64 @Vagabond Several fixes for printing binaries, improve quote stripping
Vagabond authored
473 ?assertEqual("'hello world'", lists:flatten(format("~p", ['hello world'], 50))),
474 ?assertEqual("hello world", lists:flatten(format("~s", ['hello world'], 50))),
475 ok.
476
477 binary_printing_test() ->
478 ?assertEqual("<<\"hello\">>", lists:flatten(format("~p", [<<$h, $e, $l, $l, $o>>], 50))),
479 ?assertEqual("<<\"hello\">>", lists:flatten(format("~p", [<<"hello">>], 50))),
480 ?assertEqual("<<1,2,3,4>>", lists:flatten(format("~p", [<<1, 2, 3, 4>>], 50))),
481 ?assertEqual("<<1,2,3,4>>", lists:flatten(format("~s", [<<1, 2, 3, 4>>], 50))),
482 ?assertEqual("hello", lists:flatten(format("~s", [<<"hello">>], 50))),
483 ?assertEqual("hello", lists:flatten(format("~10s", [<<"hello">>], 50))),
a0440a9 @Vagabond Smarter doublequote stripping when printing strings with ~s
Vagabond authored
484 ok.
03d3fa1 @Vagabond Fixes and tests for deep-list handling in trunc_io:format
Vagabond authored
485 -endif.
Something went wrong with that request. Please try again.