Return-Path: X-Original-To: apmail-couchdb-commits-archive@www.apache.org Delivered-To: apmail-couchdb-commits-archive@www.apache.org Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by minotaur.apache.org (Postfix) with SMTP id 4BF7A10FBA for ; Mon, 3 Feb 2014 22:13:22 +0000 (UTC) Received: (qmail 4296 invoked by uid 500); 3 Feb 2014 22:12:49 -0000 Delivered-To: apmail-couchdb-commits-archive@couchdb.apache.org Received: (qmail 3232 invoked by uid 500); 3 Feb 2014 22:12:24 -0000 Mailing-List: contact commits-help@couchdb.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@couchdb.apache.org Delivered-To: mailing list commits@couchdb.apache.org Received: (qmail 2835 invoked by uid 99); 3 Feb 2014 22:12:17 -0000 Received: from tyr.zones.apache.org (HELO tyr.zones.apache.org) (140.211.11.114) by apache.org (qpsmtpd/0.29) with ESMTP; Mon, 03 Feb 2014 22:12:17 +0000 Received: by tyr.zones.apache.org (Postfix, from userid 65534) id 2E6659189EB; Mon, 3 Feb 2014 22:12:16 +0000 (UTC) Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit From: davisp@apache.org To: commits@couchdb.apache.org Date: Mon, 03 Feb 2014 22:12:30 -0000 Message-Id: <0a0f984293ea414d83ce1a8156e88276@git.apache.org> In-Reply-To: References: X-Mailer: ASF-Git Admin Mailer Subject: [16/29] twig commit: updated refs/heads/import to 2d56280 Add trunc_io.erl v 1.11 from Matthias Lang http://www.corelatus.com/~matthias/trunc_io.erl Project: http://git-wip-us.apache.org/repos/asf/couchdb-twig/repo Commit: http://git-wip-us.apache.org/repos/asf/couchdb-twig/commit/c4d7baf1 Tree: http://git-wip-us.apache.org/repos/asf/couchdb-twig/tree/c4d7baf1 Diff: http://git-wip-us.apache.org/repos/asf/couchdb-twig/diff/c4d7baf1 Branch: refs/heads/import Commit: c4d7baf143748a30f1b0991429d760b134065c4c Parents: 263e017 Author: Adam Kocoloski Authored: Tue Mar 8 22:05:13 2011 -0500 Committer: Adam Kocoloski Committed: Tue Mar 8 22:05:13 2011 -0500 ---------------------------------------------------------------------- src/trunc_io.erl | 215 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 215 insertions(+) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/couchdb-twig/blob/c4d7baf1/src/trunc_io.erl ---------------------------------------------------------------------- diff --git a/src/trunc_io.erl b/src/trunc_io.erl new file mode 100644 index 0000000..d3d5bb4 --- /dev/null +++ b/src/trunc_io.erl @@ -0,0 +1,215 @@ +%% ``The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with your Erlang distribution. If not, it can be +%% retrieved via the world wide web at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% The Initial Developer of the Original Code is Corelatus AB. +%% Portions created by Corelatus are Copyright 2003, Corelatus +%% AB. All Rights Reserved.'' +%% +%% Module to print out terms for logging. Limits by length rather than depth. +%% +%% The resulting string may be slightly larger than the limit; the intention +%% is to provide predictable CPU and memory consumption for formatting +%% terms, not produce precise string lengths. +%% +%% Typical use: +%% +%% trunc_io:print(Term, 500). +%% +-module(trunc_io). +-author('matthias@corelatus.se'). +%% And thanks to Chris Newcombe for a bug fix +-export([print/2, fprint/2, safe/2]). % interface functions +-export([perf/0, perf/3, perf1/0, test/0, test/2]). % testing functions +-version("$Id: trunc_io.erl,v 1.11 2009-02-23 12:01:06 matthias Exp $"). + + +%% Returns an flattened list containing the ASCII representation of the given +%% term. +fprint(T, Max) -> + {L, _} = print(T, Max), + lists:flatten(L). + +%% Same as print, but never crashes. +%% +%% This is a tradeoff. Print might conceivably crash if it's asked to +%% print something it doesn't understand, for example some new data +%% type in a future version of Erlang. If print crashes, we fall back +%% to io_lib to format the term, but then the formatting is +%% depth-limited instead of length limited, so you might run out +%% memory printing it. Out of the frying pan and into the fire. +%% +safe(What, Len) -> + case catch print(What, Len) of + {L, Used} when list(L) -> {L, Used}; + _ -> {"unable to print" ++ io_lib:write(What, 99)} + end. + +%% Returns {List, Length} +print(_, Max) when Max < 0 -> {"...", 3}; +print(Tuple, Max) when tuple(Tuple) -> + {TC, Len} = tuple_contents(Tuple, Max-2), + {[${, TC, $}], Len + 2}; + +%% We assume atoms, floats, funs, integers, PIDs, ports and refs never need +%% to be truncated. This isn't strictly true, someone could make an +%% arbitrarily long bignum. Let's assume that won't happen unless someone +%% is being malicious. +%% +print(Atom, _Max) when atom(Atom) -> + L = atom_to_list(Atom), + {L, length(L)}; + +print(<<>>, _Max) -> + {"<<>>", 4}; + +print(Binary, Max) when binary(Binary) -> + B = binary_to_list(Binary, 1, lists:min([Max, size(Binary)])), + {L, Len} = alist_start(B, Max-4), + {["<<", L, ">>"], Len}; + +print(Float, _Max) when float(Float) -> + L = float_to_list(Float), + {L, length(L)}; + +print(Fun, _Max) when function(Fun) -> + L = erlang:fun_to_list(Fun), + {L, length(L)}; + +print(Integer, _Max) when integer(Integer) -> + L = integer_to_list(Integer), + {L, length(L)}; + +print(Pid, _Max) when pid(Pid) -> + L = pid_to_list(Pid), + {L, length(L)}; + +print(Ref, _Max) when reference(Ref) -> + L = erlang:ref_to_list(Ref), + {L, length(L)}; + +print(Port, _Max) when port(Port) -> + L = erlang:port_to_list(Port), + {L, length(L)}; + +print(List, Max) when list(List) -> + alist_start(List, Max). + +%% Returns {List, Length} +tuple_contents(Tuple, Max) -> + L = tuple_to_list(Tuple), + list_body(L, Max). + +%% Format the inside of a list, i.e. do not add a leading [ or trailing ]. +%% Returns {List, Length} +list_body([], _) -> {[], 0}; +list_body(_, Max) when Max < 4 -> {"...", 3}; +list_body([H|T], Max) -> + {List, Len} = print(H, Max), + {Final, FLen} = list_bodyc(T, Max - Len), + {[List|Final], FLen + Len}; +list_body(X, Max) -> %% improper list + {List, Len} = print(X, Max - 1), + {[$|,List], Len + 1}. + +list_bodyc([], _) -> {[], 0}; +list_bodyc(_, Max) when Max < 4 -> {"...", 3}; +list_bodyc([H|T], Max) -> + {List, Len} = print(H, Max), + {Final, FLen} = list_bodyc(T, Max - Len - 1), + {[$,, List|Final], FLen + Len + 1}; +list_bodyc(X,Max) -> %% improper list + {List, Len} = print(X, Max - 1), + {[$|,List], Len + 1}. + +%% The head of a list we hope is ascii. Examples: +%% +%% [65,66,67] -> "ABC" +%% [65,0,67] -> "A"[0,67] +%% [0,65,66] -> [0,65,66] +%% [65,b,66] -> "A"[b,66] +%% +alist_start([], _) -> {"[]", 2}; +alist_start(_, Max) when Max < 4 -> {"...", 3}; +alist_start([H|T], Max) when H >= 16#20, H =< 16#7e -> % definitely printable + {L, Len} = alist([H|T], Max-1), + {[$\"|L], Len + 1}; +alist_start([H|T], Max) when H == 9; H == 10; H == 13 -> % show as space + {L, Len} = alist(T, Max-1), + {[$ |L], Len + 1}; +alist_start(L, Max) -> + {R, Len} = list_body(L, Max-2), + {[$[, R, $]], Len + 2}. + +alist([], _) -> {"\"", 1}; +alist(_, Max) when Max < 5 -> {"...\"", 4}; +alist([H|T], Max) when H >= 16#20, H =< 16#7e -> % definitely printable + {L, Len} = alist(T, Max-1), + {[H|L], Len + 1}; +alist([H|T], Max) when H == 9; H == 10; H == 13 -> % show as space + {L, Len} = alist(T, Max-1), + {[$ |L], Len + 1}; +alist(L, Max) -> + {R, Len} = list_body(L, Max-3), + {[$\", $[, R, $]], Len + 3}. + + +%%-------------------- +%% The start of a test suite. So far, it only checks for not crashing. +test() -> + test(trunc_io, print). + +test(Mod, Func) -> + Simple_items = [atom, 1234, 1234.0, {tuple}, [], [list], "string", self(), + <<1,2,3>>, make_ref(), fun() -> ok end], + F = fun(A) -> + Mod:Func(A, 100), + Mod:Func(A, 2), + Mod:Func(A, 20) + end, + + G = fun(A) -> + case catch F(A) of + {'EXIT', _} -> exit({failed, A}); + _ -> ok + end + end, + + lists:foreach(G, Simple_items), + + Tuples = [ {1,2,3,a,b,c}, {"abc", def, 1234}, + {{{{a},b,c,{d},e}},f}], + + Lists = [ [1,2,3,4,5,6,7], lists:seq(1,1000), + [{a}, {a,b}, {a, [b,c]}, "def"], [a|b], [$a|$b] ], + + + lists:foreach(G, Tuples), + lists:foreach(G, Lists). + +perf() -> + {New, _} = timer:tc(trunc_io, perf, [trunc_io, print, 1000]), + {Old, _} = timer:tc(trunc_io, perf, [io_lib, write, 1000]), + io:fwrite("New code took ~p us, old code ~p\n", [New, Old]). + +perf(M, F, Reps) when Reps > 0 -> + test(M,F), + perf(M,F,Reps-1); +perf(_,_,_) -> + done. + +%% Performance test. Needs a particularly large term I saved as a binary... +perf1() -> + {ok, Bin} = file:read_file("bin"), + A = binary_to_term(Bin), + {N, _} = timer:tc(trunc_io, print, [A, 1500]), + {M, _} = timer:tc(io_lib, write, [A]), + {N, M}. +