From ca800443bfd116ca1b8d2e3d08de23572d699b0c Mon Sep 17 00:00:00 2001 From: SisMaker <1713699517@qq.com> Date: Sat, 20 Feb 2021 12:00:11 +0800 Subject: [PATCH] =?UTF-8?q?ft:=20=E4=BF=AE=E6=94=B9=E4=B8=BA=E4=BA=8C?= =?UTF-8?q?=E8=BF=9B=E5=88=B6=E5=8C=B9=E9=85=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- include/eFmt.hrl | 2 + src/eFmtFormat.erl | 285 ++++++++++++++++++++++++--------------------- 2 files changed, 152 insertions(+), 135 deletions(-) diff --git a/include/eFmt.hrl b/include/eFmt.hrl index bc0d79f..5f0cec2 100644 --- a/include/eFmt.hrl +++ b/include/eFmt.hrl @@ -1,4 +1,6 @@ -define(base(Precision), case Precision of none -> 10; _ -> Precision end). +%% 三元表达式 +-define(IIF(Cond, Ret1, Ret2), (case Cond of true -> Ret1; _ -> Ret2 end)). -record(fmtSpec, { ctlChar :: char() %% 控制序列的类型 $p $w diff --git a/src/eFmtFormat.erl b/src/eFmtFormat.erl index fbdda69..d7ea884 100644 --- a/src/eFmtFormat.erl +++ b/src/eFmtFormat.erl @@ -128,8 +128,8 @@ doCollPadChar(LPart, Args, Width, Adjust, Precision, Acc) -> doCollEncoding(LPart, Args, Width, Adjust, Precision, PadChar, Acc) -> case LPart of - <<"t", Char:8/integer, LeftLPart/binary>> -> - true = Char =/= $l, + <<"t", LeftLPart/binary>> -> + %true = Char =/= $l, doCollStrings(LeftLPart, Args, Width, Adjust, Precision, PadChar, unicode, Acc); _ -> doCollStrings(LPart, Args, Width, Adjust, Precision, PadChar, latin1, Acc) @@ -137,16 +137,16 @@ doCollEncoding(LPart, Args, Width, Adjust, Precision, PadChar, Acc) -> doCollStrings(LPart, Args, Width, Adjust, Precision, PadChar, Encoding, Acc) -> case LPart of - <<"l", Char:8/integer, LeftLPart/binary>> -> - true = Char =/= $t, + <<"l", LeftLPart/binary>> -> + %true = Char =/= $t, doCollCA(LeftLPart, Args, Width, Adjust, Precision, PadChar, Encoding, false, Acc); _ -> doCollCA(LPart, Args, Width, Adjust, Precision, PadChar, Encoding, true, Acc) end. doCollCA(LPart, Args, Width, Adjust, Precision, PadChar, Encoding, Strings, Acc) -> - <> = LPart, - case Char of + <> = LPart, + case CtlChar of $w -> [OneArgs | LeftArgs] = Args, As = OneArgs, NextArgs = LeftArgs; $p ->[OneArgs | LeftArgs] = Args, As = OneArgs, NextArgs = LeftArgs; $W ->[OneArgs | LeftArgs] = Args, [Depth | LastArgs] = LeftArgs, As = [OneArgs, Depth], NextArgs = LastArgs; @@ -166,7 +166,7 @@ doCollCA(LPart, Args, Width, Adjust, Precision, PadChar, Encoding, Strings, Acc) $n -> As = undefined, NextArgs = Args; $i ->[OneArgs | LeftArgs] = Args, As = OneArgs, NextArgs = LeftArgs end, - FmtSpec = #fmtSpec{ctlChar = Char, args = As, width = Width, adjust = Adjust, precision = Precision, padChar = PadChar, encoding = Encoding, strings = Strings}, + FmtSpec = #fmtSpec{ctlChar = CtlChar, args = As, width = Width, adjust = Adjust, precision = Precision, padChar = PadChar, encoding = Encoding, strings = Strings}, doCollect(LeftLPart, NextArgs, [FmtSpec | Acc]). %% Build the output text for a pre-parsed format list. @@ -177,14 +177,14 @@ build(Cs) -> -spec build(FormatList :: [char() | eFmt:fmtSpec()], Options :: [{'chars_limit', CharsLimit :: integer()}]) -> eFmt:chars(). build(Cs, Options) -> CharsLimit = getOpt(chars_limit, Options, -1), - Res1 = buildSmall(Cs, []), - {P, S, W, Other} = count_small(Res1, 0, 0, 0, 0), + ResList = buildSmall(Cs, []), + {P, S, W, Other} = cntSmall(ResList, 0, 0, 0, 0), case P + S + W of 0 -> - Res1; + ResList; NumOfLimited -> - RemainingChars = sub(CharsLimit, Other), - build_limited(Res1, P, NumOfLimited, RemainingChars, 0) + RemainChars = remainChars(CharsLimit, Other), + buildLimited(ResList, P, NumOfLimited, RemainChars, 0, []) end. %% build_small([Control]) -> eFmt:chars(). @@ -193,20 +193,19 @@ build(Cs, Options) -> %% Interpret the control structures. Count the number of print %% remaining and only calculate indentation when necessary. Must also %% be smart when calculating indentation for characters in format. - buildSmall([], Acc) -> Acc; buildSmall([OneCA | Cs], Acc) -> case OneCA of - #fmtSpec{ctlChar = C, args = Args, width = Width, adjust = Adjust, precision = Precision, padChar = PadChar, encoding = Encoding} -> - case ctlSmall(C, Args, Width, Adjust, Precision, PadChar, Encoding) of + #fmtSpec{ctlChar = CtlChar, args = Args, width = Width, adjust = Adjust, precision = Precision, padChar = PadChar, encoding = Encoding} -> + case ctlSmall(CtlChar, Args, Width, Adjust, Precision, PadChar, Encoding) of not_small -> buildSmall(Cs, [OneCA | Acc]); + ignore -> buildSmall(Cs, Acc); Str -> buildSmall(Cs, [Str | Acc]) end; _ -> buildSmall(Cs, [OneCA | Acc]) end. - %% control_small(FormatChar, [Argument], FieldWidth, Adjust, Precision, %% PadChar, Encoding) -> String %% control_limited(FormatChar, [Argument], FieldWidth, Adjust, Precision, @@ -245,63 +244,72 @@ ctlSmall($+, Args, Width, Adjust, Precision, PadChar, _Encoding) when is_integer ctlSmall($#, Args, Width, Adjust, Precision, PadChar, _Encoding) when is_integer(Args) -> Base = ?base(Precision), prefixedInt(Args, Width, Adjust, Base, PadChar, integer_to_binary(Base), $#, false); -ctlSmall($c,Args, Width, Adjust, Precision, PadChar, _Encoding) when is_integer(Args) -> - char(Args, Width, Adjust, Precision, PadChar); -ctlSmall($c, Args, Width, Adjust, Precision, PadChar, _Enc) when is_integer(Args) -> - char(Args band 255, Width, Adjust, Precision, PadChar); +ctlSmall($c,Args, Width, Adjust, Precision, PadChar, Encoding) when is_integer(Args) -> + case Encoding of + unicode -> + char(Args, Width, Adjust, Precision, PadChar); + _ -> + char(Args band 255, Width, Adjust, Precision, PadChar) + end; ctlSmall($~, _Args, Width, Adjust, Precision, PadChar, _Encoding) -> char($~, Width, Adjust, Precision, PadChar); ctlSmall($n, _Args, Width, Adjust, Precision, PadChar, _Encoding) -> newline(Width, Adjust, Precision, PadChar); -ctlSmall($i, _Args, _Width, _Adjust, _Precision, _PadChar, _Encoding) -> []; +ctlSmall($i, _Args, _Width, _Adjust, _Precision, _PadChar, _Encoding) -> ignore; ctlSmall(_C, _Args, _Width, _Adjust, _Precision, _PadChar, _Encoding) -> not_small. -%% count_small([ControlC])->计数。计算大(pPwWsS)打印请求的数量和其他打印(小)请求的字符数。 -count_small([#{control_char := $p} | Cs], P, S, W, Other) -> - count_small(Cs, P + 1, S, W, Other); -count_small([#{control_char := $P} | Cs], P, S, W, Other) -> - count_small(Cs, P + 1, S, W, Other); -count_small([#{control_char := $w} | Cs], P, S, W, Other) -> - count_small(Cs, P, S, W + 1, Other); -count_small([#{control_char := $W} | Cs], P, S, W, Other) -> - count_small(Cs, P, S, W + 1, Other); -count_small([#{control_char := $s} | Cs], P, S, W, Other) -> - count_small(Cs, P, S, W + 1, Other); -count_small([S | Cs], P, S, W, Other) when is_list(S);is_binary(S) -> - count_small(Cs, P, S, W, Other + eFmt:charsLen(S)); -count_small([C | Cs], P, S, W, Other) when is_integer(C) -> - count_small(Cs, P, S, W, Other + 1); -count_small([], P, S, W, Other) -> - {P, S, W, Other}. - - - - -build_limited([#{control_char := C, args := As, width := F, adjust := Ad, precision := P, pad_char := Pad, encoding := Enc, strings := Str} | Cs], NumOfPs0, Count0, MaxLen0, I) -> - MaxChars = if - MaxLen0 < 0 -> MaxLen0; - true -> MaxLen0 div Count0 - end, - S = control_limited(C, As, F, Ad, P, Pad, Enc, Str, MaxChars, I), - NumOfPs = decr_pc(C, NumOfPs0), - Count = Count0 - 1, - MaxLen = if - MaxLen0 < 0 -> % optimization - MaxLen0; - true -> - Len = eFmt:charsLen(S), - sub(MaxLen0, Len) - end, - if - NumOfPs > 0 -> [S | build_limited(Cs, NumOfPs, Count, - MaxLen, indentation(S, I))]; - true -> [S | build_limited(Cs, NumOfPs, Count, MaxLen, I)] - end; -build_limited([$\n | Cs], NumOfPs, Count, MaxLen, _I) -> - [$\n | build_limited(Cs, NumOfPs, Count, MaxLen, 0)]; -build_limited([$\t | Cs], NumOfPs, Count, MaxLen, I) -> - [$\t | build_limited(Cs, NumOfPs, Count, MaxLen, ((I + 8) div 8) * 8)]; -build_limited([C | Cs], NumOfPs, Count, MaxLen, I) -> - [C | build_limited(Cs, NumOfPs, Count, MaxLen, I + 1)]; -build_limited([], _, _, _, _) -> []. +cntSmall([], P, S, W, Other) -> + {P, S, W, Other}; +cntSmall([OneRes | Cs], P, S, W, Other) -> + case OneRes of + #fmtSpec{ctlChar = CtlChar} -> + case CtlChar of + $p -> + cntSmall(Cs, P + 1, S, W, Other); + $P -> + cntSmall(Cs, P + 1, S, W, Other); + $w -> + cntSmall(Cs, P, S, W + 1, Other); + $W -> + cntSmall(Cs, P, S, W + 1, Other); + $s -> + cntSmall(Cs, P, S, W + 1, Other); + _ -> + cntSmall(Cs, P, S, W, Other) + end; + _ -> + if + is_binary(OneRes) orelse is_list(OneRes) -> + cntSmall(Cs, P, S, W, Other + eFmt:charsLen(S)); + is_integer(OneRes) -> + cntSmall(Cs, P, S, W, Other + 1); + true -> + cntSmall(Cs, P, S, W, Other) + end + end. + +buildLimited([], _, _, _, _, Acc) -> Acc; +buildLimited([OneCA | Cs], NumOfPs, Count, MaxLen, I, Acc) -> + case OneCA of + #fmtSpec{ctlChar = CtlChar, args = Args, width = Width, adjust = Adjust, precision = Precision, padChar = PadChar, encoding = Encoding, strings = Strings} -> + MaxChars = if MaxLen < 0 -> MaxLen; true -> MaxLen div Count end, + S = ctlLimited(CtlChar, Args, Width, Adjust, Precision, PadChar, Encoding, Strings, MaxChars, I), + NewNumOfPs = decr_pc(CtlChar, NumOfPs), + NewCount = Count - 1, + MaxLen = if + MaxLen < 0 -> % optimization + MaxLen; + true -> + Len = eFmt:charsLen(S), + remainChars(MaxLen, Len) + end, + if + NewNumOfPs > 0 -> [S | buildLimited(Cs, NewNumOfPs, NewCount, + MaxLen, indentation(S, I))]; + true -> [S | buildLimited(Cs, NewNumOfPs, NewCount, MaxLen, I)] + end; + _ -> + buildLimited(Cs, NewNumOfPs, NewCount, MaxLen, I + 1, [OneCA | Acc]) + end. + decr_pc($p, Pc) -> Pc - 1; decr_pc($P, Pc) -> Pc - 1; @@ -323,25 +331,32 @@ indentation([C | Cs], I) -> indentation([], I) -> I. +%% (CtlChar, Args, Width, Adjust, Precision, PadChar, Encoding, Strings, MaxChars, I) +ctlLimited($s, Args, Width, Adjust, Precision, PadChar, Encoding, _Strings, CharsLimit, _I) -> + case Encoding of + latin1 -> + BinStr = erlang:iolist_to_binary(Args); -control_limited($s, [L0], F, Adj, P, Pad, latin1 = Enc, _Str, CL, _I) -> - L = iolist_to_chars(L0, F, CL), - string(L, limit_field(F, CL), Adj, P, Pad, Enc); -control_limited($s, [L0], F, Adj, P, Pad, unicode = Enc, _Str, CL, _I) -> - L = cdata_to_chars(L0, F, CL), - uniconv(string(L, limit_field(F, CL), Adj, P, Pad, Enc)); -control_limited($w, [A], F, Adj, P, Pad, Enc, _Str, CL, _I) -> - Chars = eFmt:write(A, [{depth, -1}, {encoding, Enc}, {chars_limit, CL}]), - term(Chars, F, Adj, P, Pad); -control_limited($p, [A], F, Adj, P, Pad, Enc, Str, CL, I) -> - print(A, -1, F, Adj, P, Pad, Enc, Str, CL, I); -control_limited($W, [A, Depth], F, Adj, P, Pad, Enc, _Str, CL, _I) + _ -> + BinStr = case catch unicode:characters_to_binary(Args, unicode) of + Str when is_binary(Str) -> Str; + _ -> toBinary(Args) + end + end, + TemBinStr = strToChars(BinStr, Width, CharsLimit), + string(TemBinStr, ?IIF(CharsLimit < 0 orelse Width =:= none, Width, max(3, min(Width, CharsLimit))), Adjust, Precision, PadChar, Encoding); +ctlLimited($w, Args, Width, Adjust, Precision, PadChar, Encoding, _Strings, CharsLimit, _I) -> + Chars = eFmt:write(Args, [{depth, -1}, {encoding, Encoding}, {chars_limit, CharsLimit}]), + term(Chars, Width, Adjust, Precision, PadChar); +ctlLimited($p, Args, Width, Adjust, Precision, PadChar, Encoding, Strings, CharsLimit, I) -> + print(Args, -1, Width, Adjust, Precision, PadChar, Encoding, Strings, CharsLimit, I); +ctlLimited($W, [Args, Depth], Width, Adjust, Precision, PadChar, Encoding, _Strings, CharsLimit, _I) when is_integer(Depth) -> - Chars = eFmt:write(A, [{depth, Depth}, {encoding, Enc}, {chars_limit, CL}]), - term(Chars, F, Adj, P, Pad); -control_limited($P, [A, Depth], F, Adj, P, Pad, Enc, Str, CL, I) + Chars = eFmt:write(Args, [{depth, Depth}, {encoding, Encoding}, {chars_limit, CharsLimit}]), + term(Chars, Width, Adjust, Precision, PadChar); +ctlLimited($P, [Args, Depth], Width, Adjust, Precision, PadChar, Encoding, Strings, CharsLimit, I) when is_integer(Depth) -> - print(A, Depth, F, Adj, P, Pad, Enc, Str, CL, I). + print(Args, Depth, Width, Adjust, Precision, PadChar, Encoding, Strings, CharsLimit, I). -ifdef(UNICODE_AS_BINARIES). uniconv(C) -> @@ -611,7 +626,7 @@ log2floor(Int, N) -> iolist_to_chars(Cs, F, CharsLimit) when CharsLimit < 0; CharsLimit >= F -> iolist_to_chars(Cs); iolist_to_chars(Cs, _, CharsLimit) -> - limit_iolist_to_chars(Cs, sub(CharsLimit, 3), [], normal). % three dots + limit_iolist_to_chars(Cs, remainChars(CharsLimit, 3), [], normal). % three dots iolist_to_chars([C | Cs]) when is_integer(C), C >= $\000, C =< $\377 -> [C | iolist_to_chars(Cs)]; @@ -622,6 +637,9 @@ iolist_to_chars([]) -> iolist_to_chars(B) when is_binary(B) -> binary_to_list(B). + + + limit_iolist_to_chars(Cs, 0, S, normal) -> L = limit_iolist_to_chars(Cs, 4, S, final), case iolist_size(L) of @@ -646,22 +664,26 @@ limit_iolist_to_chars(B, Limit, S, Mode) when is_binary(B) -> [binary_to_list(B) | limit_iolist_to_chars([], Limit - Sz, S, Mode)] end. +strToChars(BinStr, Width, CharsLimit) -> + ByteSize = byte_size(BinStr), + if + Width == none -> + case CharsLimit < 0 orelse CharsLimit >= ByteSize of + true -> + BinStr; + _ -> + <<(binary:part(BinStr, 0, CharsLimit))/binary, "...">> + end; + CharsLimit < 0 orelse CharsLimit >= Width -> + BinStr; + true -> + <<(binary:part(BinStr, 0, CharsLimit))/binary, "...">> + end. + cdata_to_chars(Cs, F, CharsLimit) when CharsLimit < 0; CharsLimit >= F -> cdata_to_chars(Cs); cdata_to_chars(Cs, _, CharsLimit) -> - limit_cdata_to_chars(Cs, sub(CharsLimit, 3), normal). % three dots - -cdata_to_chars([C | Cs]) when is_integer(C), C >= $\000 -> - [C | cdata_to_chars(Cs)]; -cdata_to_chars([I | Cs]) -> - [cdata_to_chars(I) | cdata_to_chars(Cs)]; -cdata_to_chars([]) -> - []; -cdata_to_chars(B) when is_binary(B) -> - case catch unicode:characters_to_list(B) of - L when is_list(L) -> L; - _ -> binary_to_list(B) - end. + limit_cdata_to_chars(Cs, remainChars(CharsLimit, 3), normal). % three dots limit_cdata_to_chars(Cs, 0, normal) -> L = limit_cdata_to_chars(Cs, 4, final), @@ -729,7 +751,7 @@ flatTrunc(List, Width, _Encoding) -> makePadChars(Char, Cnt, BinStr) -> case Cnt > 0 of true -> - makePadChars(Cnt - 1, Char, <>); + makePadChars(Cnt - 1, Char, <>); _ -> BinStr end. @@ -781,43 +803,36 @@ prefixedInt(Int, Width, Adjust, Base, PadChar, Prefix, Prefix2, Lowercase) -> end end. -%% char(Char, Field, Adjust, Precision, PadChar) -> chars(). - -char(C, none, _Adj, none, _Pad) -> [C]; -char(C, F, _Adj, none, _Pad) -> makePadChars(C, F, <<>>); -char(C, none, _Adj, P, _Pad) -> makePadChars(C, P, <<>>); -char(C, F, Adj, P, Pad) when F >= P -> - adjust(Adj, makePadChars(C, P, <<>>), makePadChars(Pad, F - P, <<>>)). - -%% newline(Field, Adjust, Precision, PadChar) -> [Char]. - -newline(none, _Adj, _P, _Pad) -> "\n"; -newline(F, right, _P, _Pad) -> makePadChars($\n, F, <<>>). - - - - -%chars(C, N, Tail) -> -% [chars(C, N)|Tail]. - -%% Lowercase conversion - -cond_lowercase(String, true) -> - lowercase(String); -cond_lowercase(String, false) -> - String. +char(Char, Width, Adjust, Precision, PadChar) -> + if + Width == none andalso Precision == none -> + integer_to_binary(Char); + Precision == none -> + makePadChars(Char, Width, <<>>); + Width == none -> + makePadChars(Char, Precision, <<>>); + true -> + adjust(Adjust, makePadChars(Char, Precision, <<>>), makePadChars(PadChar, Width - Precision, <<>>)) + end. -lowercase([H | T]) when is_integer(H), H >= $A, H =< $Z -> - [(H - $A + $a) | lowercase(T)]; -lowercase([H | T]) -> - [H | lowercase(T)]; -lowercase([]) -> - []. +newline(none, _Adjust, _Precision, _PadChar) -> <<"\n">>; +newline(Width, Adjust, _Precision, _PadChar) -> + case Adjust of + right -> + makePadChars($\n, Width, <<>>); + _ -> + <<"\n">> + end. -%% Make sure T does change sign. -sub(T, _) when T < 0 -> T; -sub(T, E) when T >= E -> T - E; -sub(_, _) -> 0. +remainChars(T, E) -> + if + T < 0 -> + T; + T >= E -> + T - E; + true -> + 0 + end. getOpt(Key, TupleList, Default) -> case lists:keyfind(Key, 1, TupleList) of