From 34f76e926c6f96f110412e085876df6490900285 Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Wed, 6 Apr 2022 10:53:26 -0400 Subject: [PATCH] add `CCString.{to_hex,of_hex}` --- src/core/CCString.ml | 43 +++++++++++++++++++++++++++++++++++++ src/core/CCString.mli | 13 +++++++++++ src/core/CCStringLabels.mli | 13 +++++++++++ 3 files changed, 69 insertions(+) diff --git a/src/core/CCString.ml b/src/core/CCString.ml index 84991e40..6c5ae1c5 100644 --- a/src/core/CCString.ml +++ b/src/core/CCString.ml @@ -1134,6 +1134,49 @@ let equal_caseless s1 s2: bool = Q.(printable_string) (fun s -> equal_caseless (uppercase_ascii s) s) *) +let to_hex (s:string) : string = + let i_to_hex (i:int) = + if i < 10 then Char.chr (i + Char.code '0') + else Char.chr (i - 10 + Char.code 'a') + in + + let res = Bytes.create (2 * length s) in + for i = 0 to length s-1 do + let n = Char.code (get s i) in + Bytes.set res (2 * i) (i_to_hex ((n land 0xf0) lsr 4)); + Bytes.set res (2 * i + 1) (i_to_hex (n land 0x0f)); + done; + Bytes.unsafe_to_string res + +let of_hex_exn (s:string) : string = + let n_of_c = function + | '0' .. '9' as c -> Char.code c - Char.code '0' + | 'a' .. 'f' as c -> 10 + Char.code c - Char.code 'a' + | _ -> invalid_arg "string: invalid hex" + in + if (String.length s mod 2 <> 0) then invalid_arg "string: hex sequence must be of even length"; + let res = Bytes.make (String.length s / 2) '\x00' in + for i=0 to String.length s/2-1 do + let n1 = n_of_c (String.get s (2*i)) in + let n2 = n_of_c (String.get s (2*i+1)) in + let n = (n1 lsl 4) lor n2 in + Bytes.set res i (Char.chr n) + done; + Bytes.unsafe_to_string res + +let of_hex s = try Some (of_hex_exn s) with Invalid_argument _ -> None + +(*$= & ~printer:(Printf.sprintf "%S") + "0068656c6c6f20776f726c64" (to_hex "\000hello world") + "" (to_hex "") + "\000hello world" (of_hex_exn "0068656c6c6f20776f726c64") +*) + +(*$Q + Q.(string) (fun s -> \ + of_hex_exn (to_hex s) = s) + *) + let pp_buf buf s = Buffer.add_char buf '"'; Buffer.add_string buf s; diff --git a/src/core/CCString.mli b/src/core/CCString.mli index f0e77b80..0ece1ff1 100644 --- a/src/core/CCString.mli +++ b/src/core/CCString.mli @@ -325,6 +325,19 @@ val equal_caseless : string -> string -> bool (** [equal_caseless s1 s2] compares [s1] and [s2] without respect to {b ascii} lowercase. @since 1.2 *) +val to_hex : string -> string +(** Convert a string with arbitrary content into a hexadecimal string. + @since NEXT_RELEASE *) + +val of_hex : string -> string option +(** Convert a string in hex into a string with arbitrary content. + @since NEXT_RELEASE *) + +val of_hex_exn : string -> string +(** Same as {!of_hex} but fails harder. + @raise Invalid_argument if the input is not valid hex. + @since NEXT_RELEASE *) + (** {2 Finding} A relatively efficient algorithm for finding sub-strings. diff --git a/src/core/CCStringLabels.mli b/src/core/CCStringLabels.mli index e3bf170e..866b9e5a 100644 --- a/src/core/CCStringLabels.mli +++ b/src/core/CCStringLabels.mli @@ -354,6 +354,19 @@ val equal_caseless : string -> string -> bool (** [equal_caseless s1 s2] compares [s1] and [s2] without respect to {b ascii} lowercase. @since 1.2 *) +val to_hex : string -> string +(** Convert a string with arbitrary content into a hexadecimal string. + @since NEXT_RELEASE *) + +val of_hex : string -> string option +(** Convert a string in hex into a string with arbitrary content. + @since NEXT_RELEASE *) + +val of_hex_exn : string -> string +(** Same as {!of_hex} but fails harder. + @raise Invalid_argument if the input is not valid hex. + @since NEXT_RELEASE *) + (** {2 Finding} A relatively efficient algorithm for finding sub-strings.