├── .gitignore ├── .travis.yml ├── LICENSE ├── Makefile ├── README.md ├── doc └── overview.edoc ├── rebar.config ├── rebar.lock └── src ├── binpp.app.src └── binpp.erl /.gitignore: -------------------------------------------------------------------------------- 1 | .eunit 2 | deps 3 | priv 4 | *.o 5 | *.beam 6 | rebar 7 | _build 8 | *.xml 9 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: erlang 2 | 3 | otp_release: 4 | - R15B 5 | - R16B03 6 | - 17.0 7 | - 18.0 8 | - 19.0 9 | 10 | install: wget https://s3.amazonaws.com/rebar3/rebar3 && chmod 755 rebar3 11 | 12 | script: ./rebar3 eunit 13 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE 3 | Version 2, December 2004 4 | 5 | Copyright (C) 2011 Adam Rutkowski 6 | 7 | Everyone is permitted to copy and distribute verbatim or modified 8 | copies of this license document, and changing it is allowed as long 9 | as the name is changed. 10 | 11 | DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE 12 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 13 | 14 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | REBAR=rebar 2 | 3 | all: compile 4 | 5 | compile: 6 | $(REBAR) compile 7 | 8 | clean: 9 | $(REBAR) clean 10 | 11 | test: clean compile 12 | $(REBAR) eunit skip_deps=true 13 | 14 | docs: 15 | $(REBAR) doc skip_deps=true 16 | 17 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | binpp - Erlang Binary Pretty Printer 2 | =================================== 3 | 4 | Hex.pm: https://hex.pm/packages/binpp 5 | 6 | [![Build Status](https://secure.travis-ci.org/jtendo/binpp.png)](http://travis-ci.org/jtendo/binpp) 7 | 8 | Example usage: 9 | 10 | 1> Bin. 11 | <<12,242,207,49,82,69,45,130,212,69,80,88,8,81,23,36,86,7, 12 | 68,19,133,97,51,216,56,145,88,8,81,...>> 13 | 2> binpp:pprint(Bin). 14 | 0000 0C F2 CF 31 52 45 2D 82 D4 45 50 58 08 51 17 24 .òÏ1RE-‚ÔEPX.Q.$ 15 | 0001 56 07 44 13 85 61 33 D8 38 91 58 08 51 17 24 56 V.D.…a3Ø8‘X.Q.$V 16 | 0002 0A 14 20 4E 24 16 09 60 F4 0A 15 11 01 30 13 89 .. N$..`ô....0.‰ 17 | 0003 05 81 0F 09 15 C5 61 33 D8 54 91 52 5D 81 17 24 ....Åa3ØT‘R].$ 18 | 0004 11 14 60 23 D1 3D 55 80 ..`#Ñ=U€ 19 | ok 20 | 21 | Binpp will use io:format to output the formatted binary by default, however 22 | there are options making pprint functions return formatted data instead 23 | of performing direct IO write: 24 | 25 | 1> Bin2 = <<"foo bar baz">>. 26 | <<"foo bar baz">> 27 | 2> binpp:pprint(Bin2, [{return, iolist}]). 28 | [["0000",32,"66 6F 6F 20 62 61 72 20 62 61 7A ",32, 29 | "foo bar baz","\n"]] 30 | 3> binpp:pprint(Bin2, [{return, binary}]). 31 | <<"0000 66 6F 6F 20 62 61 72 20 62 61 7A foo bar baz\n">> 32 | 33 | You may use a custom printer function as well: 34 | 35 | 4> binpp:pprint(Bin2, [{printer, fun(O) -> io:format("~s~n", [O]) end}]). 36 | 0000 66 6F 6F 20 62 61 72 20 62 61 7A foo bar baz 37 | 38 | ok 39 | 40 | An additional API is provided for printing binary fragments: 41 | 42 | 5> binpp:pprint(Bin2, {0, 3}, []). 43 | 0000 66 6F 6F foo 44 | 45 | Also, binary byte-to-byte comparsion: 46 | 47 | 6> binpp:compare(<<1,2,1024:512,3,4>>, <<1,3,1024:512,5,6>>). 48 | -- 02 -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 03 -- -- -- -- -- -- -- -- -- -- -- -- -- -- 49 | -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 50 | -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 51 | -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 52 | -- -- 03 04 -- -- 05 06 53 | ok 54 | 55 | Plus a handy little helper: 56 | 57 | 7> binpp:compare(<<1,2,255,3,1024:512>>, binpp:from_str("01 02 FF CC")). 58 | -- -- -- 03 00 00 00 00 00 00 00 00 00 00 00 00 -- -- -- CC ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? 59 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? 60 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? 61 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? 62 | 00 00 04 00 ?? ?? ?? ?? 63 | ok 64 | 65 | 66 | Edoc available at http://jtendo.github.com/binpp/doc/. 67 | 68 | That's all folks! 69 | -------------------------------------------------------------------------------- /doc/overview.edoc: -------------------------------------------------------------------------------- 1 | @title binpp - Erlang Binary Pretty Printer 2 | @copyright 2012 (c) Adam Rutkowski 3 | @version 1.1 4 | 5 | @doc This lilbrary provides additional utility functions for working with Erlang 6 | binary() and bitstring() types. 7 | 8 | You might find it useful if you are going to: 9 | 14 | 15 |

Installation

16 | 17 |
    18 | 19 |
  1. binpp comes with a Makefile that requires rebar build tool. 20 | 21 | If you don't have rebar installed, you may get it from Rebar Github repository 22 |
  2. 23 | 24 |
  3. 25 | Download the sources from binpp Github repository. 26 | 27 |
      28 |
    • To build the library run make.
    • 29 |
    • To run the tests run make test.
    • 30 |
    • To generate documentation run make docs.
    • 31 |
    32 |
  4. 33 |
34 | 35 | 36 | 37 |

Example usage

38 | 39 | ``` 40 | 1> Bin. 41 | <<12,242,207,49,82,69,45,130,212,69,80,88,8,81,23,36,86,7, 42 | 68,19,133,97,51,216,56,145,88,8,81,...>> 43 | 2> binpp:pprint(Bin). 44 | 0C F2 CF 31 52 45 2D 82 D4 45 50 58 08 51 17 24 .òÏ1RE-‚ÔEPX.Q.$ 45 | 56 07 44 13 85 61 33 D8 38 91 58 08 51 17 24 56 V.D.…a3Ø8‘X.Q.$V 46 | 0A 14 20 4E 24 16 09 60 F4 0A 15 11 01 30 13 89 .. N$..`ô....0.‰ 47 | 05 81 0F 09 15 C5 61 33 D8 54 91 52 5D 81 17 24 ....Åa3ØT‘R].$ 48 | 11 14 60 23 D1 3D 55 80 ..`#Ñ=U€ 49 | ok 50 | ''' 51 | 52 | Binpp will use io:format to output the formatted binary by default, however 53 | there are options making pprint functions return formatted data instead 54 | of performing direct IO write: 55 | 56 | ``` 57 | 1> Bin2 = <<"foo bar baz">>. 58 | <<"foo bar baz">> 59 | 2> binpp:pprint(Bin2, [{return, iolist}]). 60 | [["66 6F 6F 20 62 61 72 20 62 61 7A ",32, 61 | "foo bar baz","\n"]] 62 | 3> binpp:pprint(Bin2, [{return, binary}]). 63 | <<"66 6F 6F 20 62 61 72 20 62 61 7A foo bar baz\n">> 64 | ''' 65 | 66 | You may use a custom printer function as well: 67 | 68 | ``` 69 | 4> binpp:pprint(Bin2, [{printer, fun(O) -> io:format("~s~n", [O]) end}]). 70 | 66 6F 6F 20 62 61 72 20 62 61 7A foo bar baz 71 | 72 | ok 73 | ''' 74 | 75 | An additional API is provided for printing binary fragments: 76 | 77 | ``` 78 | 5> binpp:pprint(Bin2, {0, 3}, []). 79 | 66 6F 6F foo 80 | ''' 81 | 82 | Also, binary byte-to-byte comparsion: 83 | 84 | ``` 85 | 6> binpp:compare(<<1,2,1024:512,3,4>>, <<1,3,1024:512,5,6>>). 86 | -- 02 -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 03 -- -- -- -- -- -- -- -- -- -- -- -- -- -- 87 | -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 88 | -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 89 | -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 90 | -- -- 03 04 -- -- 05 06 91 | ok 92 | ''' 93 | 94 | Plus a handy little helper: 95 | 96 | ``` 97 | 7> binpp:compare(<<1,2,255,3,1024:512>>, binpp:from_str("01 02 FF CC")). 98 | -- -- -- 03 00 00 00 00 00 00 00 00 00 00 00 00 -- -- -- CC ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? 99 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? 100 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? 101 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? 102 | 00 00 04 00 ?? ?? ?? ?? 103 | ok 104 | ''' 105 | 106 | That's all folks! 107 | 108 |

Get involved

109 | 110 | Bug reports can be submitted to: binpp issues @ Github. 111 | 112 | Patches and feature requests are always welcome! 113 | 114 |

Module index:

115 |
116 |
{@link binpp}
117 |
Erlang Binary Pretty Printer
118 |
119 | -------------------------------------------------------------------------------- /rebar.config: -------------------------------------------------------------------------------- 1 | {erl_opts,[warnings_as_errors, 2 | {platform_define, "^(19|2)", rand_only} 3 | ]}. 4 | 5 | {cover_enabled, true}. 6 | {cover_print_enabled, true}. 7 | {eunit_opts, [verbose, 8 | {report, {eunit_surefire, [{dir, "."}]}}]}. 9 | 10 | {plugins, [rebar3_hex]}. 11 | -------------------------------------------------------------------------------- /rebar.lock: -------------------------------------------------------------------------------- 1 | []. 2 | -------------------------------------------------------------------------------- /src/binpp.app.src: -------------------------------------------------------------------------------- 1 | {application, binpp, [ 2 | {description, "Erlang Binary Pretty Printer"}, 3 | {vsn, "1.1.1"}, 4 | {registered, []}, 5 | {applications, [kernel, stdlib]}, 6 | {env, []}, 7 | {modules, []}, 8 | {maintainers, ["Adam Rutkowski"]}, 9 | {licenses, ["WTFPL"]}, 10 | {links, [{"Github", "https://github.com/jtendo/binpp"}]} 11 | ]}. 12 | -------------------------------------------------------------------------------- /src/binpp.erl: -------------------------------------------------------------------------------- 1 | %% @doc Pretty printer for Erlang binaries. 2 | -module(binpp). 3 | -author('Adam Rutkowski hq@mtod.org'). 4 | 5 | -export([pprint/1, pprint/2, pprint/3]). 6 | -export([compare/2]). 7 | -export([from_str/1]). 8 | -export([convert/1, convert/2]). 9 | 10 | -export_type([opt/0, opts/0]). 11 | 12 | -opaque opt() :: {return, iolist} | {return, binary} | {printer, function()}. 13 | -opaque opts() :: list(opt()). 14 | 15 | %% Printer constants 16 | -define(SPACE, $ ). 17 | -define(SPECIAL, $.). 18 | -define(FILL, $0). 19 | 20 | %% Comparison glyphs 21 | -define(MISSING, "??"). 22 | -define(EQUAL, "--"). 23 | 24 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 25 | % API % 26 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 27 | 28 | %% @doc Pretty print a binary to stdout using io:format/2 29 | %% Note: io:format/2 is a blocking call. 30 | -spec pprint(binary() | bitstring()) -> ok. 31 | pprint(Bin) -> 32 | pprint(Bin, []). 33 | 34 | %% @doc Print a binary using custom function or return the formatted result. 35 | %% Valid option is one of: 36 | %% 41 | %% 42 | %% Custom printers should be of arity 1, accepting the prettified result as 43 | %% an iolist() input. Returned value can be any(). 44 | -spec pprint(binary() | bitstring(), opts()) -> ok | any(). 45 | pprint(Bin, Opts) when is_list(Opts) -> 46 | {ok, Octets} = convert(Bin, hex), 47 | Buckets = buckets(16, Octets), 48 | Printed = print_buckets(Buckets), 49 | apply_opts(Printed, Opts). 50 | 51 | %% @doc Pretty print a slice of binary. 52 | -spec pprint(binary() | bitstring(), {non_neg_integer(), non_neg_integer()}, 53 | opts()) -> ok | any(). 54 | pprint(Bin, {Pos, Len}, Opts) when Len =< byte_size(Bin), (Pos+Len) =< byte_size(Bin) -> 55 | pprint(binary:part(Bin, Pos, Len), Opts); 56 | pprint(Bin, {Pos, _}, Opts) -> 57 | pprint(binary:part(Bin, Pos, byte_size(Bin)-Pos), Opts). 58 | 59 | %% @doc Pretty print byte-to-byte comparsion of two binaries to stdout. 60 | -spec compare(binary() | bitstring(), binary() | bitstring()) -> ok. 61 | compare(Bin1, Bin2) when is_binary(Bin1) orelse is_bitstring(Bin1), 62 | is_binary(Bin2) orelse is_bitstring(Bin2) -> 63 | {ok, Octets1} = convert(Bin1, hex), 64 | {ok, Octets2} = convert(Bin2, hex), 65 | {ok, {D1, D2}} = diff(Octets1, Octets2), 66 | print_comparison(buckets(16, D1), buckets(16, D2)). 67 | 68 | %% @doc Construct binary from hex string. 69 | %% Hex string octets can be optionally separated with spaces. 70 | %% Valid hex strings: 71 | %% 75 | -spec from_str(string()) -> binary(). 76 | from_str(Str) when is_list(Str) -> 77 | Bytes = case lists:member(?SPACE, Str) of 78 | true -> 79 | string:tokens(Str, [?SPACE]); 80 | false when length(Str) rem 2 =:= 0 -> 81 | buckets(2, Str) 82 | end, 83 | list_to_binary([list_to_integer(B, 16) || B <- Bytes]). 84 | 85 | %% @doc Convert binary to hex string. 86 | -spec convert(binary() | bitstring()) -> {ok, list()}. 87 | convert(Bin) when is_binary(Bin) orelse is_bitstring(Bin) -> 88 | convert(Bin, hex). 89 | 90 | %% @doc Convert binary to hex string or binary (base-2) string. 91 | -spec convert(binary() | bitstring(), hex | bin) -> {ok, list()}. 92 | convert(Bin, hex) when is_binary(Bin) orelse is_bitstring(Bin) -> 93 | convert(Bin, [], fun byte_to_hexstr/1); 94 | convert(Bin, bin) when is_binary(Bin) orelse is_bitstring(Bin) -> 95 | convert(Bin, [], fun byte_to_binstr/1). 96 | 97 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 98 | % Internals % 99 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 100 | 101 | -spec apply_opts(iolist(), opts()) -> ok | iolist() | binary(). 102 | apply_opts(IoList, []) -> 103 | io:format("~s~n", [IoList]); 104 | apply_opts(IoList, [{return, iolist}]) -> 105 | IoList; 106 | apply_opts(IoList, [{return, binary}]) -> 107 | iolist_to_binary(IoList); 108 | apply_opts(IoList, [{printer, Fun}]) when is_function(Fun) -> 109 | Fun(IoList); 110 | apply_opts(_, _) -> erlang:error(badarg). 111 | 112 | -spec convert(binary() | bitstring(), list(), function()) -> {ok, string()}. 113 | convert(<<>>, Acc, _) -> 114 | {ok, lists:reverse(Acc)}; 115 | convert(Bin, [], FormatFun) when is_bitstring(Bin), not is_binary(Bin) -> 116 | %% byte align bistring() to make a complementary binary() 117 | Align = (8 - (bit_size(Bin) rem 8)), 118 | error_logger:info_msg("Aligned bitstring with ~.10B bit(s).~n", [Align]), 119 | convert(<>, [], FormatFun); 120 | convert(<>, SoFar, FormatFun) -> 121 | convert(Rest, [FormatFun(Bin)|SoFar], FormatFun). 122 | 123 | print_buckets(Buckets) -> 124 | {Printed, _} = lists:mapfoldl(fun(Bucket, Offset) -> 125 | B = print_bucket(Bucket), 126 | Annotated = io_lib:format("~4.16.0B ~s", [Offset, B]), 127 | {Annotated, Offset+1} 128 | end, 0, Buckets), 129 | Printed. 130 | 131 | print_bucket(Bucket) -> 132 | OctetLine = string:join(Bucket, [?SPACE]), 133 | OctetRepr = lists:map( 134 | fun(B) -> 135 | case list_to_integer(B, 16) of 136 | Code when Code >= ?SPACE -> Code; 137 | _ -> ?SPECIAL 138 | end 139 | end, 140 | Bucket), 141 | io_lib:format("~s ~s~n", [string:left(OctetLine, 16*2 + 16, ?SPACE), OctetRepr]). 142 | 143 | -spec print_comparison(list(), list()) -> ok. 144 | print_comparison([], []) -> 145 | ok; 146 | print_comparison([L|LRest], [R|RRest]) -> 147 | Zfill = fun(Line) -> string:left(Line, 16*2 + 16, ?SPACE) end, 148 | DiffL = Zfill(string:join(L, [?SPACE])), 149 | DiffR = Zfill(string:join(R, [?SPACE])), 150 | io:format("~s ~s~n", [DiffL, DiffR]), 151 | print_comparison(LRest, RRest). 152 | 153 | -spec diff(list(), list()) -> {ok, {list(), list()}}. 154 | diff(L1, L2) when is_list(L1), is_list(L2) -> 155 | diff(L1, L2, [], []). 156 | 157 | -spec diff(list(), list(), list(), list()) -> {ok, {list(), list()}}. 158 | diff([], [], LD, RD) -> 159 | {ok, {lists:reverse(LD), lists:reverse(RD)}}; 160 | diff([], [H2|R2], LD, RD) -> 161 | diff([], R2, [?MISSING|LD], [H2|RD]); 162 | diff([H1|R1], [], LD, RD) -> 163 | diff(R1, [], [H1|LD], [?MISSING|RD]); 164 | diff([H1|R1], [H1|R2], LD, RD) -> %% H1 =:= H2 165 | diff(R1, R2, [?EQUAL|LD], [?EQUAL|RD]); 166 | diff([H1|R1], [H2|R2], LD, RD) -> 167 | diff(R1, R2, [H1|LD], [H2|RD]). 168 | 169 | -spec byte_to_hexstr(byte()) -> string(). 170 | byte_to_hexstr(B) when B >= 0, B =< 255 -> 171 | to_hexstr(B, 16, 2). 172 | 173 | -spec byte_to_binstr(byte()) -> string(). 174 | byte_to_binstr(B) when B >= 0, B =< 255 -> 175 | to_hexstr(B, 2, 8). 176 | 177 | -spec to_hexstr(byte(), non_neg_integer(), non_neg_integer()) -> string(). 178 | to_hexstr(B, Base, Len) -> 179 | string:right(integer_to_list(B, Base), Len, ?FILL). 180 | 181 | %% @doc Divide list L into X lists of size N 182 | %% courtesy of MononcQc 183 | -spec buckets(non_neg_integer(), list()) -> list(list()). 184 | buckets(N, L) -> 185 | buckets(1, N, length(L) div N, L, [[]]). 186 | buckets(_, _, 0, [], [[]|Acc]) -> 187 | lists:reverse(Acc); 188 | buckets(_, _, 0, Rest, [[]|Acc]) -> 189 | lists:reverse([Rest|Acc]); 190 | buckets(N, N, M, [H|T], [A|Acc]) -> 191 | buckets(1, N, M-1, T, [[], lists:reverse([H|A]) | Acc]); 192 | buckets(X, N, M, [H|T], [A|Acc]) -> 193 | buckets(X+1, N, M, T, [[H|A]|Acc]). 194 | 195 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 196 | % Tests % 197 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 198 | 199 | -ifdef(TEST). 200 | -include_lib("eunit/include/eunit.hrl"). 201 | 202 | -define(MAX_BIN_SIZE, 2048). 203 | -define(RUNS, 100). 204 | 205 | -ifdef(rand_only). 206 | -define(random, rand). 207 | -else. 208 | -define(random, random). 209 | -endif. 210 | 211 | -ifdef(rand_only). 212 | random_seed() -> 213 | %% the rand module self-seeds 214 | ok. 215 | -else. 216 | random_seed() -> 217 | <> = crypto:rand_bytes(12), 218 | random:seed({A,B,C}). 219 | -endif. 220 | 221 | setup_random() -> 222 | _ = random_seed(), 223 | ok. 224 | 225 | binpp_random_test_() -> 226 | {setup, fun setup_random/0, [ 227 | {generator, fun rand_pprint/0}, 228 | {generator, fun rand_pprint_bitstring/0}, 229 | {generator, fun rand_compare/0}, 230 | {generator, fun rand_pprint_opts/0}, 231 | {generator, fun rand_pprint_slice/0}, 232 | {generator, fun rand_from_str/0} 233 | ]}. 234 | 235 | buckets_test_() -> 236 | F = fun buckets/2, 237 | Tests = [ 238 | { {1, [1,2,3,4]}, [ [1], [2], [3], [4] ] }, 239 | { {2, [1,2,3,4]}, [ [1,2], [3,4] ] }, 240 | { {3, [1,2,3,4]}, [ [1,2,3], [4] ] }, 241 | { {4, [1,2,3,4]}, [ [1,2,3,4] ] }, 242 | { {5, [1,2,3,4]}, [ [1,2,3,4] ] } 243 | ], 244 | [ { <<"Buckets">>, fun() -> ?assertEqual(R, F(I1, I2)) end } 245 | || { {I1, I2}, R } <- Tests ]. 246 | 247 | byte_to_binstr_test_() -> 248 | F = fun byte_to_binstr/1, 249 | Tests = [ 250 | { 0, "00000000" }, 251 | { 1, "00000001" }, 252 | { 255, "11111111" } 253 | ], 254 | [ { iolist_to_binary(["Convert ", integer_to_list(I)]), 255 | fun() -> ?assertEqual(R, F(I)) end } 256 | || { I, R } <- Tests ]. 257 | 258 | byte_to_hexstr_test_() -> 259 | F = fun byte_to_hexstr/1, 260 | Tests = [ 261 | { 0, "00" }, 262 | { 1, "01" }, 263 | { 255, "FF" } 264 | ], 265 | [ { iolist_to_binary(["Convert ", integer_to_list(I)]), 266 | fun() -> ?assertEqual(R, F(I)) end } 267 | || { I, R } <- Tests ]. 268 | 269 | diff_test_() -> 270 | F = fun diff/2, 271 | Tests = [ 272 | { { [], [] }, { [], []} }, 273 | { { [1, 2], [1, 1] }, { [?EQUAL, 2], [?EQUAL, 1]} }, 274 | { { [1], [1, 1] }, { [?EQUAL, ?MISSING], [?EQUAL, 1]} }, 275 | { { [1, 2, 3], [2, 1] }, { [1, 2, 3], [2, 1, ?MISSING]} }, 276 | { { [], [2, 1] }, { [?MISSING, ?MISSING], [2, 1]} } 277 | ], 278 | [ { <<"Diff">>, fun() -> ?assertEqual({ok, R}, F(I1, I2)) end } 279 | || { {I1, I2}, R } <- Tests ]. 280 | 281 | print_bucket_test_() -> 282 | F = fun print_bucket/1, 283 | Tests = [ 284 | { ["00", "FF"], 285 | ["00 FF ", ?SPACE, [?SPECIAL, 255], "\n"] }, 286 | 287 | { ["41" || _ <- lists:seq(1, 16) ], 288 | ["41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 ", ?SPACE, [$A || _ <- lists:seq(1, 16)], "\n"] } 289 | ], 290 | [ { iolist_to_binary(["Print ", I]), fun() -> ?assertEqual(R, F(I)) end } 291 | || { I, R } <- Tests ]. 292 | 293 | print_buckets_test_() -> 294 | F = fun print_buckets/1, 295 | Tests = [ 296 | { 297 | [ ["00", "FF"] || _ <- lists:seq(0, 16) ], 298 | lists:flatten([ 299 | "0000 00 FF .",255,"\n", 300 | "0001 00 FF .",255,"\n", 301 | "0002 00 FF .",255,"\n", 302 | "0003 00 FF .",255,"\n", 303 | "0004 00 FF .",255,"\n", 304 | "0005 00 FF .",255,"\n", 305 | "0006 00 FF .",255,"\n", 306 | "0007 00 FF .",255,"\n", 307 | "0008 00 FF .",255,"\n", 308 | "0009 00 FF .",255,"\n", 309 | "000A 00 FF .",255,"\n", 310 | "000B 00 FF .",255,"\n", 311 | "000C 00 FF .",255,"\n", 312 | "000D 00 FF .",255,"\n", 313 | "000E 00 FF .",255,"\n", 314 | "000F 00 FF .",255,"\n", 315 | "0010 00 FF .",255,"\n"]) 316 | } 317 | ], 318 | [ { iolist_to_binary(["Print buckets ", I]), 319 | fun() -> 320 | B = F(I), 321 | ?assertEqual(R, lists:flatten(B)) 322 | end } || { I, R } <- Tests ]. 323 | 324 | 325 | convert_hex_test_() -> 326 | F = fun convert/1, 327 | Tests = [ 328 | { <<1,2,3>>, ["01", "02", "03"] }, 329 | { <<256:32>>, ["00", "00", "01", "00"] }, 330 | { <<"AAA">>, ["41", "41", "41"] }, 331 | { <<256:7>>, ["00"] }, 332 | { <<256:9>>, ["80", "00"] } 333 | ], 334 | [ { <<"Convert">>, fun() -> ?assertEqual({ok, R}, F(I)) end } || { I, R } <- Tests ]. 335 | 336 | convert_bin_test_() -> 337 | F = fun convert/2, 338 | Tests = [ 339 | { <<1,2,3>>, ["00000001","00000010","00000011"] }, 340 | { <<256:32>>, ["00000000","00000000","00000001", "00000000"] }, 341 | { <<"AAA">>, ["01000001","01000001","01000001"] }, 342 | { <<256:7>>, ["00000000"] }, 343 | { <<256:9>>, ["10000000","00000000"] } 344 | ], 345 | [ { <<"Convert">>, fun() -> ?assertEqual({ok, R}, F(I, bin)) end } || { I, R } <- Tests ]. 346 | 347 | rand_pprint() -> 348 | F = fun pprint/1, 349 | Tests = [ { crypto:strong_rand_bytes(?random:uniform(?MAX_BIN_SIZE)), ok } || _ <- lists:seq(1, ?RUNS) ], 350 | [ { <<"Random pprint">>, fun() -> ?assertEqual(R, F(I)) end } 351 | || { I, R } <- Tests ]. 352 | 353 | rand_pprint_bitstring() -> 354 | F = fun pprint/1, 355 | Tests = [ { << 356 | (crypto:strong_rand_bytes(?random:uniform(?MAX_BIN_SIZE)))/binary, 357 | 0:(?random:uniform(7))>>, ok } 358 | || _ <- lists:seq(1, ?RUNS) ], 359 | [ { <<"Random pprint (bitstring)">>, fun() -> ?assertEqual(R, F(I)) end } 360 | || { I, R } <- Tests ]. 361 | 362 | rand_compare() -> 363 | F = fun compare/2, 364 | Rand = fun() -> crypto:strong_rand_bytes(?random:uniform(?MAX_BIN_SIZE)) end, 365 | Tests = [ { { Rand(), Rand() }, ok } || _ <- lists:seq(1, ?RUNS) ], 366 | [ { <<"Random compare">>, fun() -> ?assertEqual(R, F(I1, I2)) end } 367 | || { {I1, I2}, R } <- Tests ]. 368 | 369 | rand_pprint_opts() -> 370 | F = fun pprint/2, 371 | CustomPrinter = fun(B) when is_list(B) -> works end, 372 | OptsMap = [ 373 | %% Option %% Predicate 374 | { {return, binary}, fun erlang:is_binary/1 }, 375 | { {return, iolist}, fun erlang:is_list/1 }, 376 | { {printer, CustomPrinter}, fun(works) -> true; (_) -> false end }, 377 | { {invalid, option}, fun({'EXIT', {badarg, _}}) -> true; (O) -> O end } 378 | ], 379 | Range = length(OptsMap), 380 | Rand = fun() -> 381 | Input = crypto:strong_rand_bytes(?random:uniform(?MAX_BIN_SIZE)), 382 | {Opt, Predicate} = lists:nth(?random:uniform(Range), OptsMap), 383 | {Input, Opt, Predicate} 384 | end, 385 | Tests = [ Rand() || _ <- lists:seq(1, ?RUNS) ], 386 | Title = fun(Opt) -> 387 | iolist_to_binary([ "Random pprint w/ opt: ", io_lib:format("~p", [Opt]) ]) end, 388 | [ { Title(Opt), fun() -> ?assertEqual(true, Pred( catch( F(I, [Opt]) ) )) end } 389 | || {I, Opt, Pred} <- Tests ]. 390 | 391 | rand_pprint_slice() -> 392 | F = fun pprint/3, 393 | Rand = fun() -> 394 | Bytes = crypto:strong_rand_bytes(?random:uniform(?MAX_BIN_SIZE)), 395 | Pos = ?random:uniform(byte_size(Bytes)), 396 | Len = ?random:uniform(byte_size(Bytes)), 397 | {Bytes, Pos, Len} 398 | end, 399 | Tests = [ Rand() || _ <- lists:seq(1, ?RUNS) ], 400 | Title = fun(Size, Slice) -> 401 | iolist_to_binary(io_lib:format("Random pprint w/ slice: (~p) ~p", [Size, Slice])) 402 | end, 403 | [ { Title(byte_size(Bytes), {Pos, Len}), fun() -> ?assertEqual(ok, F(Bytes, {Pos, Len}, [])) end } 404 | || { Bytes, Pos, Len } <- Tests ]. 405 | 406 | rand_from_str() -> 407 | F = fun from_str/1, 408 | Rand = fun() -> 409 | Bytes = crypto:strong_rand_bytes(?random:uniform(?MAX_BIN_SIZE)), 410 | {ok, Converted} = convert(Bytes), 411 | case ?random:uniform(2) of 412 | 1 -> {lists:flatten(Converted), Bytes}; 413 | 2 -> {string:join(Converted, " "), Bytes} 414 | end 415 | end, 416 | Tests = [ Rand() || _ <- lists:seq(1, ?RUNS) ], 417 | [ { <<"Random from_str">>, fun() -> ?assertEqual(R, F(I)) end } 418 | || { I, R } <- Tests ]. 419 | 420 | -endif. 421 | --------------------------------------------------------------------------------