package hex

  1. Overview
  2. Docs
Legend:
Page
Library
Module
Module type
Parameter
Class
Class type
Source

Source file hex.ml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
(*
 * Copyright (c) 2015 Trevor Summers Smith <trevorsummerssmith@gmail.com>
 * Copyright (c) 2014 Thomas Gazagnaire <thomas@gazagnaire.org>
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 *)

type t = [`Hex of string]

let invalid_arg fmt =
  Printf.ksprintf (fun str -> raise (Invalid_argument str)) fmt

let hexa = "0123456789abcdef"
and hexa1 =
  "0000000000000000111111111111111122222222222222223333333333333333\
   4444444444444444555555555555555566666666666666667777777777777777\
   88888888888888889999999999999999aaaaaaaaaaaaaaaabbbbbbbbbbbbbbbb\
   ccccccccccccccccddddddddddddddddeeeeeeeeeeeeeeeeffffffffffffffff"
and hexa2 =
  "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef\
   0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef\
   0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef\
   0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"

let char_is_printable chr =
  chr >= ' ' && chr <= '~'

let of_char c =
  let x = Char.code c in
  hexa.[x lsr 4], hexa.[x land 0xf]

let to_char x y =
  let code c = match c with
    | '0'..'9' -> Char.code c - 48 (* Char.code '0' *)
    | 'A'..'F' -> Char.code c - 55 (* Char.code 'A' + 10 *)
    | 'a'..'f' -> Char.code c - 87 (* Char.code 'a' + 10 *)
    | _ -> invalid_arg "Hex.to_char: %d is an invalid char" (Char.code c)
  in
  Char.chr (code x lsl 4 + code y)

let of_string_fast s =
  let len = String.length s in
  let buf = Bytes.create (len * 2) in
  for i = 0 to len - 1 do
    Bytes.unsafe_set buf (i * 2)
      (String.unsafe_get hexa1 (Char.code (String.unsafe_get s i)));
    Bytes.unsafe_set buf (succ (i * 2))
      (String.unsafe_get hexa2 (Char.code (String.unsafe_get s i)));
  done;
  `Hex (Bytes.to_string buf)

let of_helper ~ignore (next : int -> char) len =
  let buf = Buffer.create len in
  for i = 0 to len - 1 do
    let c = next i in
    if List.mem c ignore then ()
    else
      let x,y = of_char c in
      Buffer.add_char buf x;
      Buffer.add_char buf y;
  done;
  `Hex (Buffer.contents buf)

let of_string ?(ignore = []) s =
  match ignore with
  | [] -> of_string_fast s
  | ignore -> of_helper ~ignore (fun i -> s.[i]) (String.length s)

let of_bytes ?ignore b =
  of_string ?ignore (Bytes.to_string b)

let to_helper ~empty_return ~create ~set (`Hex s) =
  if s = "" then empty_return
  else
    let n = String.length s in
    let buf = create (n/2) in
    let rec aux i j =
      if i >= n then ()
      else if j >= n then invalid_arg "Hex conversion: Hex string cannot have an odd number of characters."
      else (
        set buf (i/2) (to_char s.[i] s.[j]);
        aux (j+1) (j+2)
      )
    in
    aux 0 1;
    buf

let to_bytes hex =
  to_helper ~empty_return:Bytes.empty ~create:Bytes.create ~set:Bytes.set hex

let to_string hex = Bytes.to_string @@ to_bytes hex

let of_cstruct ?(ignore=[]) cs =
  let open Cstruct in
  of_helper
    ~ignore
    (fun i -> Bigarray.Array1.get cs.buffer (cs.off+i))
    cs.len

(* Allocate just once for to_cstruct *)
let empty_cstruct = Cstruct.of_string ""

let to_cstruct hex =
  to_helper
    ~empty_return:empty_cstruct ~create:Cstruct.create ~set:Cstruct.set_char hex

let of_bigstring ?(ignore=[]) buf =
  of_helper
    ~ignore
    (Bigarray.Array1.get buf)
    (Bigarray.Array1.dim buf)

let to_bigstring hex =
  to_helper
    ~empty_return:empty_cstruct.buffer
    ~create:Bigarray.(Array1.create char c_layout)
    ~set:Bigarray.Array1.set hex

let hexdump_s ?(print_row_numbers=true) ?(print_chars=true) (`Hex s) =
  let char_len = 16 in (* row width in # chars *)
  let hex_len = char_len * 2 in (* row width in # hex chars *)
  (* Buf length is roughly 4... could put this in exactly but very brittle *)
  let buf = Buffer.create ((String.length s) * 4) in
  let ( <= ) buf s = Buffer.add_string buf s in
  (* Create three columns -- row #, hex and ascii chars*)
  let n = String.length s in
  let rows = (n / hex_len) + (if n mod hex_len = 0 then 0 else 1) in
  for row = 0 to rows-1 do
    let last_row = row = rows-1 in
    (* First column is row number *)
    if print_row_numbers then
      buf <= Printf.sprintf "%.8d: " row;
    (* Row length is hex_length, unless we are on the last row and we
       have less than hex_length left *)
    let row_len = if last_row then
        (let rem = n mod hex_len in
         if rem = 0 then hex_len else rem)
      else hex_len in
    for i = 0 to row_len-1 do
      (* Second column is the hex *)
      if i mod 4 = 0 && i <> 0 then buf <= Printf.sprintf " ";
      let i = i + (row * hex_len) in
      buf <= Printf.sprintf "%c" (String.get s i)
    done;
    (* This is only needed for the last row -- pad if less than len *)
    if last_row then
      let missed_chars = hex_len - row_len in
      let pad = missed_chars in
      (* Every four chars add spacing *)
      let pad = pad + (missed_chars / 4) in
      buf <= Printf.sprintf "%s" (String.make pad ' ')
    else ();
    (* Third column is ascii *)
    if print_chars then begin
      buf <= "  ";
      let rec aux i j =
        if i > row_len - 2 then ()
        else begin
          let pos = i + (row * hex_len) in
          let pos' = pos + 1 in
          let c = to_char (String.get s pos) (String.get s pos') in
          if char_is_printable c then
            buf <= Printf.sprintf "%c" c
          else
            buf <= ".";
          aux (j+1) (j+2)
        end
      in
      aux 0 1;
    end;
    buf <= "\n";
  done;
  Buffer.contents buf

let hexdump ?print_row_numbers ?print_chars hex =
  Printf.printf "%s" (hexdump_s ?print_row_numbers ?print_chars hex)

let pp ppf (`Hex hex) =
  Format.pp_print_string ppf hex

let show (`Hex hex) = hex
OCaml

Innovation. Community. Security.