diff --git a/.gitignore b/.gitignore index 065769eeba111c28e7250e496aec936c81b7db4f..633a7d72b4f6a96980eec0d1d508467a17ddfa92 100644 --- a/.gitignore +++ b/.gitignore @@ -204,3 +204,4 @@ hello-*.tar.gz /src/plugins/gui/gtk_compat.ml /src/plugins/gui/GSourceView.ml /src/plugins/gui/GSourceView.mli +/tests/crowbar/integer_bb_pretty diff --git a/src/libraries/stdlib/integer.ml b/src/libraries/stdlib/integer.ml index 55d36122770094da3949f3540941b9fdc0bdccbd..1a7e593f79aede19b4ecddaf53d51964fa7020ce 100644 --- a/src/libraries/stdlib/integer.ml +++ b/src/libraries/stdlib/integer.ml @@ -109,17 +109,85 @@ let popcount = Z.popcount raises multiple [Failure _] exceptions *) failwith "Integer.of_string" - let max_int64 = of_int64 Int64.max_int let min_int64 = of_int64 Int64.min_int - let to_string = Z.to_string let to_float = Z.to_float let of_float z = try Z.of_float z with Z.Overflow -> raise Too_big + let bdigits = [| + "0000" ; (* 0 *) + "0001" ; (* 1 *) + "0010" ; (* 2 *) + "0011" ; (* 3 *) + "0100" ; (* 4 *) + "0101" ; (* 5 *) + "0110" ; (* 6 *) + "0111" ; (* 7 *) + "1000" ; (* 8 *) + "1001" ; (* 9 *) + "1010" ; (* 10 *) + "1011" ; (* 11 *) + "1100" ; (* 12 *) + "1101" ; (* 13 *) + "1110" ; (* 14 *) + "1111" ; (* 15 *) + |] + + let pp_bin_pos fmt r = Format.pp_print_string fmt bdigits.(r) + let pp_bin_neg fmt r = Format.pp_print_string fmt bdigits.(15-r) + + let pp_hex_pos fmt r = Format.fprintf fmt "%04X" r + let pp_hex_neg fmt r = Format.fprintf fmt "%04X" (0xFFFF-r) + + let bmask_bin = Z.of_int 0xF (* 4 bits mask *) + let bmask_hex = Z.of_int 0xFFFF (* 64 bits mask *) + + type digits = { + nbits : int ; (* max number of bits *) + bsize : int ; (* bits in each bloc *) + bmask : Z.t ; (* block mask, must be (1 << bsize) - 1 *) + sep : string ; + pp : Format.formatter -> int -> unit ; (* print one block *) + } + + let rec pp_digits d fmt n v = + if gt v zero || n < d.nbits then + begin + let r = Z.to_int (Z.logand v d.bmask) in + let k = d.bsize in + pp_digits d fmt (n + k) (Z.shift_right_trunc v k) ; + if gt v d.bmask || (n + k) < d.nbits + then Format.pp_print_string fmt d.sep ; + d.pp fmt r ; + end + + let pp_bin ?(nbits=1) ?(sep="") fmt v = + let nbits = if nbits <= 0 then 1 else nbits in + if le zero v then + ( Format.pp_print_string fmt "0b" ; + pp_digits { nbits ; sep ; bsize=4 ; + bmask = bmask_bin ; pp = pp_bin_pos } fmt 0 v ) + else + ( Format.pp_print_string fmt "1b" ; + pp_digits { nbits ; sep ; bsize=4 ; + bmask = bmask_bin ; pp = pp_bin_neg } fmt 0 (Z.lognot v) ) + + let pp_hex ?(nbits=1) ?(sep="") fmt v = + let nbits = if nbits <= 0 then 1 else nbits in + if le zero v then + ( Format.pp_print_string fmt "0x" ; + pp_digits { nbits ; sep ; bsize=16 ; + bmask = bmask_hex ; pp = pp_hex_pos } fmt 0 v ) + + else + ( Format.pp_print_string fmt "1x" ; + pp_digits { nbits ; sep ; bsize=16 ; + bmask = bmask_hex ; pp = pp_hex_neg } fmt 0 (Z.lognot v) ) + let pretty ?(hexa=false) fmt v = let rec aux v = if gt v two_power_60 then diff --git a/src/libraries/stdlib/integer.mli b/src/libraries/stdlib/integer.mli index 4e939382bf131017b673f534505420f04c0df94c..3ed0f1297471b8c393056437955eec4085c79363 100644 --- a/src/libraries/stdlib/integer.mli +++ b/src/libraries/stdlib/integer.mli @@ -150,8 +150,23 @@ val popcount: t -> int val pretty : ?hexa:bool -> t Pretty_utils.formatter - +val pp_bin : ?nbits:int -> ?sep:string -> t Pretty_utils.formatter +(** Print binary format. Digits are output by blocs of 4 bits + separated by [~sep] with at least [~nbits] total bits. If [nbits] is + non positive, it will be ignored. + + Positive values are prefixed with ["0b"] and negative values + are printed as their 2-complement ([lnot]) with prefix ["1b"]. *) + +val pp_hex : ?nbits:int -> ?sep:string -> t Pretty_utils.formatter +(** Print hexadecimal format. Digits are output by blocs of 16 bits + (4 hex digits) separated by [~sep] with at least [~nbits] total bits. + If [nbits] is non positive, it will be ignored. + + Positive values are preffixed with ["0x"] and negative values + are printed as their 2-complement ([lnot]) with prefix ["1x"]. *) (* + Local Variables: compile-command: "make -C ../../.." End: diff --git a/tests/crowbar/integer_bb_pretty.ml b/tests/crowbar/integer_bb_pretty.ml new file mode 100644 index 0000000000000000000000000000000000000000..2c6ec812e2968f282f1efb3e3cb19a7c45c9ef0a --- /dev/null +++ b/tests/crowbar/integer_bb_pretty.ml @@ -0,0 +1,59 @@ +open Crowbar + +let reparse v s = + let failure info = + Crowbar.fail + ("Pretty-printing '" ^ (Z.to_string v) ^ "' returns '" ^ s ^ "'" ^ info) + in + if String.length s <= 2 then failure ""; + let is_neg = s.[0] = '1' in + let is_hex = s.[1] = 'x' in + let s = String.(concat "" (split_on_char '_' s)) in + let v' = + if is_neg then begin + let chr = if is_hex then 'F' else '1' in + let module M = struct exception Found of int end in + let check i c = if i > 1 && c <> chr then raise (M.Found i) in + try String.iteri check s; Z.minus_one + with M.Found idx -> + let len, v' = + if is_hex then begin + let remains = String.sub s idx (String.length s - idx) in + let v' = Z.of_string ("0x" ^ remains) in + 4 * (String.length remains), v' + end else begin + let remains = String.sub s idx (String.length s - idx) in + let v' = Z.of_string ("0b" ^ remains) in + String.length remains, v' + end + in + let m = Z.(one lsl len) in + let m = Z.pred m in + let v' = Z.logxor m v' in + Z.pred (Z.lognot (Z.pred v')) + end else + Z.of_string s + in + if not (Z.equal v v') then + failure (" reparsed as '" ^ Z.format "%b" v' ^ "' (" ^ Z.to_string v' ^ ")") + +let test z is_hex nbits has_sep = + guard (nbits >= 0 && nbits <= 1024); + let sep = if has_sep then Some "_" else None in + let pp z = if is_hex then + Integer.pp_hex ~nbits ?sep z + else + Integer.pp_bin ~nbits ?sep z + in + let s = Format.asprintf "%a" pp z in + reparse z s + +let zarith = + let open Crowbar in + fix (fun zarith -> + choose + [ map [int64] Z.of_int64; + map [zarith; int64] (fun z i -> Z.((z lsl 64) + of_int64 i)) ]) + +let () = Crowbar.add_test ~name:"pp_bin_hex" + [ zarith; Crowbar.bool; Crowbar.int; Crowbar.bool ] test diff --git a/tests/misc/oracle/pp_bin_hex.res.oracle b/tests/misc/oracle/pp_bin_hex.res.oracle new file mode 100644 index 0000000000000000000000000000000000000000..4791a522a343769216bd7ce7e105707e34f739e4 --- /dev/null +++ b/tests/misc/oracle/pp_bin_hex.res.oracle @@ -0,0 +1,86 @@ +-------------------------------------------------- +Dec. 0 +Hex. 0x0000 +Bin. 0b0000_0000 +-------------------------------------------------- +Dec. 1 +Hex. 0x0001 +Bin. 0b0000_0001 +-------------------------------------------------- +Dec. -1 +Hex. 1xFFFF +Bin. 1b1111_1111 +-------------------------------------------------- +Dec. 2 +Hex. 0x0002 +Bin. 0b0000_0010 +-------------------------------------------------- +Dec. -2 +Hex. 1xFFFE +Bin. 1b1111_1110 +-------------------------------------------------- +Dec. 5 +Hex. 0x0005 +Bin. 0b0000_0101 +-------------------------------------------------- +Dec. -5 +Hex. 1xFFFB +Bin. 1b1111_1011 +-------------------------------------------------- +Dec. 9 +Hex. 0x0009 +Bin. 0b0000_1001 +-------------------------------------------------- +Dec. -9 +Hex. 1xFFF7 +Bin. 1b1111_0111 +-------------------------------------------------- +Dec. 16 +Hex. 0x0010 +Bin. 0b0001_0000 +-------------------------------------------------- +Dec. -16 +Hex. 1xFFF0 +Bin. 1b1111_0000 +-------------------------------------------------- +Dec. 127 +Hex. 0x007F +Bin. 0b0111_1111 +-------------------------------------------------- +Dec. -127 +Hex. 1xFF81 +Bin. 1b1000_0001 +-------------------------------------------------- +Dec. 128 +Hex. 0x0080 +Bin. 0b1000_0000 +-------------------------------------------------- +Dec. -128 +Hex. 1xFF80 +Bin. 1b1000_0000 +-------------------------------------------------- +Dec. 255 +Hex. 0x00FF +Bin. 0b1111_1111 +-------------------------------------------------- +Dec. -255 +Hex. 1xFF01 +Bin. 1b0000_0001 +-------------------------------------------------- +Dec. 4279173135 +Hex. 0xFF0F_000F +Bin. 0b1111_1111_0000_1111_0000_0000_0000_1111 +-------------------------------------------------- +Dec. -4279173135 +Hex. 1x00F0_FFF1 +Bin. 1b0000_0000_1111_0000_1111_1111_1111_0001 +-------------------------------------------------- +Dec. 386334727 +Hex. 0x1707_0007 +Bin. 0b0001_0111_0000_0111_0000_0000_0000_0111 +-------------------------------------------------- +Dec. -386334727 +Hex. 1xE8F8_FFF9 +Bin. 1b1110_1000_1111_1000_1111_1111_1111_1001 +-------------------------------------------------- +[kernel] Parsing tests/misc/pp_bin_hex.i (no preprocessing) diff --git a/tests/misc/pp_bin_hex.i b/tests/misc/pp_bin_hex.i new file mode 100644 index 0000000000000000000000000000000000000000..a53499be69922b6d91cd0d4926968829ccf22a93 --- /dev/null +++ b/tests/misc/pp_bin_hex.i @@ -0,0 +1,3 @@ +/* run.config + OPT: -no-autoload-plugins -load-script tests/misc/pp_bin_hex.ml +*/ diff --git a/tests/misc/pp_bin_hex.ml b/tests/misc/pp_bin_hex.ml new file mode 100644 index 0000000000000000000000000000000000000000..41ab2e6ab92ee1beef120983f3e307461ada4811 --- /dev/null +++ b/tests/misc/pp_bin_hex.ml @@ -0,0 +1,37 @@ +let pp_dec fmt z = Integer.pretty ~hexa:false fmt z +let pp_hex fmt z = Integer.pp_hex ~nbits:16 ~sep:"_" fmt z +let pp_bin fmt z = Integer.pp_bin ~nbits:8 ~sep:"_" fmt z + +let hrule () = + Format.printf "--------------------------------------------------@." + +let testcase z = + begin + hrule () ; + Format.printf "Dec. %a@." pp_dec z ; + Format.printf "Hex. %a@." pp_hex z ; + Format.printf "Bin. %a@." pp_bin z ; + end + +let () = + begin + List.iter + (fun z -> + testcase z ; + if not (Integer.equal z Integer.zero) then + testcase (Integer.neg z) + ) [ + Integer.of_string "0" ; + Integer.of_string "1" ; + Integer.of_string "2" ; + Integer.of_string "5" ; + Integer.of_string "9" ; + Integer.of_string "16" ; + Integer.of_string "127" ; + Integer.of_string "128" ; + Integer.of_string "0xFF" ; + Integer.of_string "0xFF0F000F" ; + Integer.of_string "0x17070007" ; + ] ; + hrule () ; + end diff --git a/tests/misc/print_machdep.i b/tests/misc/print_machdep.i index 80afbbd20f335c72d597dbc6c45cf6e37d304c37..0762669acd646cdbd6257081b52f420d15c7e2b2 100644 --- a/tests/misc/print_machdep.i +++ b/tests/misc/print_machdep.i @@ -1,4 +1,3 @@ /* run.config - CMD: @frama-c@ -no-autoload-plugins - OPT: -print-machdep + OPT: -no-autoload-plugins -print-machdep */