initial commit
This commit is contained in:
commit
25372c91b4
8 changed files with 198 additions and 0 deletions
0
CHANGES.md
Normal file
0
CHANGES.md
Normal file
23
LICENSE.md
Normal file
23
LICENSE.md
Normal file
|
@ -0,0 +1,23 @@
|
|||
Copyright (c) 2017, 2018, Hannes Mehnert
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright notice, this
|
||||
list of conditions and the following disclaimer in the documentation and/or
|
||||
other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
0
README.md
Normal file
0
README.md
Normal file
4
dune
Normal file
4
dune
Normal file
|
@ -0,0 +1,4 @@
|
|||
(library
|
||||
(name ohex)
|
||||
(public_name ohex)
|
||||
(modules ohex))
|
3
dune-project
Normal file
3
dune-project
Normal file
|
@ -0,0 +1,3 @@
|
|||
(lang dune 2.7)
|
||||
(name ohex)
|
||||
(formatting disabled)
|
102
ohex.ml
Normal file
102
ohex.ml
Normal file
|
@ -0,0 +1,102 @@
|
|||
|
||||
let string_fold f acc str =
|
||||
let st = ref acc in
|
||||
String.iter (fun c -> st := f !st c) str;
|
||||
!st
|
||||
|
||||
let is_space = function
|
||||
| ' ' | '\n' | '\r' | '\t' -> true
|
||||
| _ -> false
|
||||
|
||||
let count_hex_chars ?(skip_whitespace = true) src =
|
||||
string_fold (fun r c ->
|
||||
if skip_whitespace && is_space c then
|
||||
r
|
||||
else
|
||||
succ r)
|
||||
0 src / 2
|
||||
|
||||
let decode_into ?(skip_whitespace = true) src tgt ?(off = 0) () =
|
||||
let fold f acc str =
|
||||
let st = ref acc in
|
||||
String.iter (fun c -> st := f !st c) str;
|
||||
!st
|
||||
and digit c =
|
||||
match c with
|
||||
| '0'..'9' -> int_of_char c - 0x30
|
||||
| 'A'..'F' -> int_of_char c - 0x41 + 10
|
||||
| 'a'..'f' -> int_of_char c - 0x61 + 10
|
||||
| _ -> invalid_arg "bad character"
|
||||
in
|
||||
let chars, leftover =
|
||||
fold (fun (chars, leftover) c ->
|
||||
if skip_whitespace && is_space c then
|
||||
chars, leftover
|
||||
else
|
||||
let c = digit c in
|
||||
match leftover with
|
||||
| None -> chars, Some (c lsl 4)
|
||||
| Some c' -> (c' lor c) :: chars, None)
|
||||
([], None) src
|
||||
in
|
||||
let chars = List.rev chars in
|
||||
if leftover <> None then
|
||||
invalid_arg "leftover byte in hex string";
|
||||
List.iteri (fun idx c -> Bytes.set_uint8 tgt (off + idx) c) chars
|
||||
|
||||
let decode ?(skip_whitespace = true) src =
|
||||
let len = count_hex_chars ~skip_whitespace src in
|
||||
let buf = Bytes.create len in
|
||||
decode_into ~skip_whitespace src buf ();
|
||||
Bytes.unsafe_to_string buf
|
||||
|
||||
let encode_into src tgt ?(off = 0) () =
|
||||
String.iteri (fun idx c ->
|
||||
let hi, lo =
|
||||
let i = int_of_char c in
|
||||
i lsr 4, i land 0xFF
|
||||
in
|
||||
Bytes.set_uint8 tgt (idx * 2 + off) hi;
|
||||
Bytes.set_uint8 tgt (idx * 2 + off + 1) lo)
|
||||
src
|
||||
|
||||
let encode src =
|
||||
let buf = Bytes.create (String.length src * 2) in
|
||||
encode_into src buf ();
|
||||
Bytes.unsafe_to_string buf
|
||||
|
||||
let printable_ascii c =
|
||||
let i = int_of_char c in
|
||||
not (i < 0x20 || i >= 0x7f)
|
||||
|
||||
let pp ?(row_numbers = true) ?(chars = true) () ppf s =
|
||||
String.iteri (fun idx c ->
|
||||
if idx mod 16 = 0 && row_numbers then
|
||||
Format.fprintf ppf "%06x " idx;
|
||||
Format.fprintf ppf "%02x" (int_of_char c);
|
||||
if idx mod 2 = 1 then
|
||||
Format.pp_print_string ppf " ";
|
||||
if idx mod 8 = 7 then
|
||||
Format.pp_print_string ppf " ";
|
||||
if idx mod 16 = 15 && chars then
|
||||
String.iter (fun c ->
|
||||
Format.pp_print_char ppf (if printable_ascii c then c else '.'))
|
||||
(String.sub s (idx - 15) 16);
|
||||
if idx mod 16 = 15 then
|
||||
Format.pp_print_string ppf "\n")
|
||||
s;
|
||||
(if chars then
|
||||
let last_n, pad =
|
||||
let l = String.length s in
|
||||
let pad = 16 - (l mod 16) in
|
||||
let pad = if pad = 16 then 0 else pad in
|
||||
String.sub s (l - (l mod 16)) (l mod 16),
|
||||
pad
|
||||
in
|
||||
let pad_chars = pad * 2 + (pad + 1) / 2 + (if pad > 8 then 1 else 0) + 1 in
|
||||
Format.pp_print_string ppf (String.make pad_chars ' ');
|
||||
String.iter (fun c ->
|
||||
Format.pp_print_char ppf (if printable_ascii c then c else '.'))
|
||||
last_n);
|
||||
if String.length s mod 16 <> 0 then
|
||||
Format.pp_print_string ppf "\n"
|
44
ohex.mli
Normal file
44
ohex.mli
Normal file
|
@ -0,0 +1,44 @@
|
|||
(** Convert from and to hex representation. *)
|
||||
|
||||
val count_hex_chars : ?skip_whitespace:bool -> string -> int
|
||||
(** [count_hex_chars ~skip_whitespace s] counts the amount of hex characters in
|
||||
the string [s]. The argument [skip_whitespace] defaults to [true], and skips
|
||||
any whitespace characters (' ', '\n', '\r', '\t'). This function is useful
|
||||
for estimating the space required for [decode_into]. *)
|
||||
|
||||
val decode : ?skip_whitespace:bool -> string -> string
|
||||
(** [decode ~skip_whitespace s] decodes a hex string [s] into a sequence of
|
||||
octets. The argument [skip_whitespace] defaults to [true], and skips any
|
||||
whitespace characters in [s] (' ', '\n', '\r', '\t'). An example:
|
||||
[decode "4142" = "AB"].
|
||||
|
||||
@raise Invalid_argument if any character in [s] is not a hex character, or
|
||||
an odd amount of characters are present. *)
|
||||
|
||||
val decode_into : ?skip_whitespace:bool -> string -> bytes -> ?off:int -> unit
|
||||
-> unit
|
||||
(** [decode_into ~skip_whitespace s dst ~off ()] decodes [s] into [dst]
|
||||
starting at [off] (defaults to 0). The argument [skip_whitespace] defaults
|
||||
to [true] and skips any whitespace characters.
|
||||
|
||||
@raise Invalid_argument if any character in [s] is not a hex character, an
|
||||
odd amount of characters are present, or [dst] does not contain enough
|
||||
space. *)
|
||||
|
||||
val encode : string -> string
|
||||
(** [encode s] encodes [s] into a freshly allocated string of double size, where
|
||||
each character in [s] is encoded as two hex digits in the returned string. *)
|
||||
|
||||
val encode_into : string -> bytes -> ?off:int -> unit -> unit
|
||||
(** [encode_into s dst ~off ()] encodes [s] into [dst] starting at [off]
|
||||
(defaults to 0). Each character is encoded as two hex digits in [dst].
|
||||
|
||||
@raise Invalid_argument if [dst] does not contain enough space. *)
|
||||
|
||||
val pp : ?row_numbers:bool -> ?chars:bool -> unit ->
|
||||
Format.formatter -> string -> unit
|
||||
(** [pp ~row_numbers ~chars () ppf s] pretty-prints the string [s] in
|
||||
hexadecimal (similar to [hexdump -C]). If [row_numbers] is provided
|
||||
(defaults to [true]), each output line is prefixed with the row number.
|
||||
If [chars] is provided (defaults to [true]), in the last column the ASCII
|
||||
string is printed (non-printable characters are printed as '.'). *)
|
22
ohex.opam
Normal file
22
ohex.opam
Normal file
|
@ -0,0 +1,22 @@
|
|||
opam-version: "2.0"
|
||||
maintainer: "Hannes Mehnert <hannes@mehnert.org>"
|
||||
authors: "Hannes Mehnert <hannes@mehnert.org>"
|
||||
license: "ISC"
|
||||
homepage: "https://git.robur.coop/robur/ohex"
|
||||
doc: "https://robur-coop.github.io/ohex/doc"
|
||||
bug-reports: "https://git.robur.coop/robur/ohex/issues"
|
||||
depends: [
|
||||
"ocaml" {>= "4.08.0"}
|
||||
"dune"
|
||||
"alcotest" {with-test}
|
||||
]
|
||||
build: [
|
||||
["dune" "subst"] {dev}
|
||||
["dune" "build" "-p" name "-j" jobs]
|
||||
["dune" "runtest" "-p" name "-j" jobs] {with-test}
|
||||
]
|
||||
dev-repo: "git+https://git.robur.coop/robur/ohex.git"
|
||||
synopsis: "Hexadecimal encoding and decoding"
|
||||
description: """
|
||||
A library to encode and decode hexadecimal byte sequences.
|
||||
"""
|
Loading…
Reference in a new issue