package hxd

  1. Overview
  2. Docs

Source file o.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
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
open S

module Option = struct
  let map_value ~default f = function
    | Some x -> f x
    | None -> default
end

type colorscheme = Fmt.style array

let colorscheme_of_array : Fmt.style array -> colorscheme = fun x ->
  if Array.length x <> 256 then Fmt.invalid_arg "Hxd.O.colorscheme_of_array: expect 256 elements" ; x

let lowercase x color =
  for i = 97 to 122 do x.(i) <- color done
let uppercase x color =
  for i = 65 to 90 do x.(i) <- color done
let digit x color =
  for i = 48 to 57 do x.(i) <- color done
let code x i color = x.(i) <- color

type payload = string

type xxd =
  { cols : int
  ; groupsize : int
  ; long : int option
  ; uppercase : bool
  ; i_buffer_size : int
  ; o_buffer_size : int
  ; colorscheme : colorscheme }

type caml =
  { kind : [ `List | `Array ]
  ; with_comments : bool
  ; cols : int
  ; long : int option
  ; i_buffer_size : int
  ; o_buffer_size : int
  ; uppercase : bool }

let fmt_of_byte uppercase
  : Format.formatter -> int -> unit
  = fun ppf x -> match uppercase with
  | true -> Format.fprintf ppf "%02X" x
  | false -> Format.fprintf ppf "%02x" x

let code_to_utf_8 ppf code =
  match (Char.chr (code land 0xff)) with
  | '\032' .. '\126' as chr -> Format.pp_print_char ppf chr
  | _ -> Format.pp_print_char ppf '.'

let pp_like_xxd (xxd:xxd) ~seek ppf chunk =
  assert (String.length chunk <= xxd.cols) ;

  Format.fprintf ppf "%08x: " seek ;

  let off = ref 0 in
  let len = String.length chunk in
  while !off < xxd.cols do
    if !off < len && Option.map_value ~default:true (fun long -> seek + !off < long) xxd.long
    then let code = Char.code (String.get chunk !off) in
         (Fmt.styled xxd.colorscheme.(code) (fmt_of_byte xxd.uppercase)) ppf code
    else Format.pp_print_string ppf "  " ;

    if xxd.groupsize <> 0
       && (!off + 1) mod xxd.groupsize = 0
    then Format.pp_print_string ppf " " ;

    incr off
  done ;

  Format.pp_print_string ppf "  " ;

  let off = ref 0 in
  while !off < xxd.cols do
    if !off < len && Option.map_value ~default:true (fun long -> seek + !off < long) xxd.long
    then let code = Char.code (String.get chunk !off) in
         (Fmt.styled xxd.colorscheme.(code) code_to_utf_8) ppf code
    else Format.pp_print_string ppf " " ;
    incr off
  done ;

  Format.fprintf ppf "@,"

type pp = seek:int -> Format.formatter -> payload -> unit

let pp_middle ppf = function
  | `List -> Format.pp_print_string ppf "; \""
  | `Array -> Format.pp_print_string ppf " ; \""

let pp_chunk ppf chunk =
  for i = 0 to String.length chunk - 1
  do match chunk.[i] with
    | '"' | '*' -> Format.pp_print_char ppf '.'
    | '\032' .. '\126' as chr -> Format.pp_print_char ppf chr
    | _ -> Format.pp_print_char ppf '.' done

let pp_like_caml caml ~seek:_ ppf chunk =
  assert (String.length chunk <= caml.cols) ;

  Format.fprintf ppf "%a" pp_middle caml.kind ;

  (* TODO: [long] *)
  for i = 0 to String.length chunk - 1
  do Format.fprintf ppf ("\\x" ^^ (if caml.uppercase then "%02X" else "%02x"))
      (Char.code (String.get chunk i)) done ;

  Format.pp_print_string ppf "\"" ;

  if String.length chunk < caml.cols
  then
    for _ = String.length chunk to caml.cols - 1
    do Format.pp_print_string ppf "    " done ;

  if caml.with_comments
  then ( Format.fprintf ppf " (* %a *)" pp_chunk chunk ) ;

  Format.fprintf ppf "@,"

type cfg =
  | Xxd of xxd
  | Caml of caml

let pp_of_cfg : cfg -> pp = function
  | Xxd xxd -> pp_like_xxd xxd
  | Caml caml -> pp_like_caml caml

let pp_begin_of_cfg : cfg -> Format.formatter -> unit -> unit = function
  | Xxd _ -> fun _ppf () -> ()
  | Caml caml -> match caml.kind with
    | `List -> fun ppf () ->
      Format.pp_print_string ppf "[\n"
    | `Array -> fun ppf () ->
      Format.pp_print_string ppf "[|\n"

let pp_end_of_cfg : cfg -> Format.formatter -> unit -> unit = function
  | Xxd _ -> fun _ppf () -> ()
  | Caml caml -> match caml.kind with
    | `List -> fun ppf () ->
      Format.pp_print_string ppf "]\n"
    | `Array -> fun ppf () ->
      Format.pp_print_string ppf " |"
    ; Format.pp_close_box ppf ()
    ; Format.pp_print_string ppf "]\n"

let default =
  Xxd { cols= 16
      ; groupsize= 2
      ; long= None
      ; uppercase= false
      ; i_buffer_size= 4096
      ; o_buffer_size= 4096
      ; colorscheme= Array.make 256 `None }

let xxd ?(cols= 16) ?(groupsize= 2) ?long
    ?buffer_size:((i_buffer_size, o_buffer_size)= 4096, 4096) ?(uppercase= false) colorscheme =
  Xxd { cols
      ; groupsize
      ; long
      ; uppercase
      ; i_buffer_size
      ; o_buffer_size
      ; colorscheme }

let caml
    ?(with_comments= false) ?(cols= 16) ?long
    ?buffer_size:((i_buffer_size, o_buffer_size)= 4096, 4096) ?(uppercase= false) kind =
  Caml { kind
       ; with_comments
       ; cols
       ; long
       ; i_buffer_size
       ; o_buffer_size
       ; uppercase }

let cols = function
  | Xxd xxd -> xxd.cols
  | Caml caml -> caml.cols

let long = function
  | Xxd xxd -> xxd.long
  | Caml caml -> caml.long

let i_buffer_size = function
  | Xxd xxd -> xxd.i_buffer_size
  | Caml caml -> caml.i_buffer_size

let o_buffer_size = function
  | Xxd xxd -> xxd.o_buffer_size
  | Caml caml -> caml.o_buffer_size

let o :
  type fi fo s e.
  cfg ->
  s scheduler ->
  (fi, bytes, s, e) iflow -> (fo, string, s, e) oflow ->
  fi -> fo ->
  (fi, s, e) seek ->
  [ `Absolute of int | `Relative of int ] ->
  Format.formatter ->
  ((unit, e) result, s) io =
  fun cfg s (module IFlow) (module OFlow) ic oc { lseek } seek ppf ->
  let ( >>= ) = s.bind in
  let return = s.return in

  let i_buffer_size = i_buffer_size cfg in
  let o_buffer_size = o_buffer_size cfg in
  let itmp = Bytes.create i_buffer_size in
  let otmp = Bytes.create o_buffer_size in

  let cur = Bytes.create (cols cfg) in

  let qui, _ = Ke.create ~capacity:(i_buffer_size * 2) Bigarray.Char in
  let quo, _ = Ke.create ~capacity:(o_buffer_size * 2) Bigarray.Char in

  let pp = pp_of_cfg cfg in
  let pp_begin = pp_begin_of_cfg cfg in
  let pp_end = pp_end_of_cfg cfg in

  let got = ref 0 in
  let input flow buf ~off ~len = match long cfg with
    | None -> IFlow.input flow buf ~off ~len
    | Some long ->
      let len = (min : int -> int -> int) (long - !got) len in
      IFlow.input flow buf ~off ~len >>= function
      | Ok len -> got := !got + len ; return (Ok len)
      | Error _ as err -> return err in

  let rec trailing_output seek =
    if Ke.is_empty quo then return (Ok ())
    else
      let len = (min : int -> int -> int) (Ke.length quo) o_buffer_size in
      Ke.keep_exn quo ~blit:B.blit_to_bytes ~length:Bytes.length ~off:0 ~len otmp ;
      OFlow.output oc (Bytes.unsafe_to_string otmp) ~off:0 ~len >>= function
      | Ok wrote -> Ke.shift_exn quo wrote ; trailing_output seek
      | Error _ as err -> return err

  and trailing_input seek =
    if Ke.is_empty qui
    then
      ( let res = Fmt.strf_like ppf "%a" pp_end () in
        pp_end ppf ()
      ; match Ke.push quo ~blit:B.blit_of_string ~length:String.length res with
      | Some _ -> trailing_output seek
      | None ->
        let len = (min : int -> int -> int) (Ke.length quo) o_buffer_size in
        Ke.keep_exn quo ~blit:B.blit_to_bytes ~length:Bytes.length ~off:0 ~len otmp ;
        OFlow.output oc (Bytes.unsafe_to_string otmp) ~off:0 ~len >>= function
        | Ok wrote -> Ke.shift_exn quo wrote ; trailing_output seek
        | Error _ as err -> return err )
    else
      let len = (min : int -> int -> int) (Ke.length qui) (cols cfg) in
      Ke.keep_exn qui ~blit:B.blit_to_bytes ~length:Bytes.length ~off:0 ~len cur ;

      let tmp = Bytes.sub_string cur 0 len in
      let res = Fmt.strf_like ppf "%a" (pp ~seek) tmp in

      pp ~seek ppf tmp ;

      match Ke.push quo ~blit:B.blit_of_string ~length:String.length res with
      | Some _ ->
        Ke.shift_exn qui len ;
        trailing_input (seek + len)
      | None ->
        let len = (min : int -> int -> int) (Ke.length quo) o_buffer_size in
        Ke.keep_exn quo ~blit:B.blit_to_bytes ~length:Bytes.length ~off:0 ~len otmp ;
        OFlow.output oc (Bytes.unsafe_to_string otmp) ~off:0 ~len >>= function
        | Ok wrote -> Ke.shift_exn quo wrote ; trailing_input seek
        | Error _ as err -> return err

  and flush_output seek =
    match Ke.keep qui ~blit:B.blit_to_bytes ~length:Bytes.length ~off:0 ~len:(cols cfg) cur with
    | None -> flush_input seek (* no enough *)
    | Some () ->
      let tmp = Bytes.sub_string cur 0 (cols cfg) in
      let res = Fmt.strf_like ppf "%a" (pp ~seek) tmp in

      pp ~seek ppf tmp ;

      match Ke.push quo ~blit:B.blit_of_string ~length:String.length res with
      | Some _ ->
        Ke.shift_exn qui (cols cfg) ;
        flush_output (seek + (cols cfg))
      | None ->
        let len = (min : int -> int -> int) (Ke.length quo) o_buffer_size in
        Ke.keep_exn quo ~blit:B.blit_to_bytes ~length:Bytes.length ~off:0 ~len otmp ;
        OFlow.output oc (Bytes.unsafe_to_string otmp) ~off:0 ~len >>= function
        | Ok wrote -> Ke.shift_exn quo wrote ; flush_input seek
        | Error _ as err -> return err

  and flush_input seek =
    if Ke.available qui = 0 then flush_output seek
    else
      let len = (min : int -> int -> int) i_buffer_size (Ke.available qui) in
      input ic itmp ~off:0 ~len >>= function
      | Error _ as err -> return err
      | Ok 0 ->
        trailing_input seek
      | Ok len ->
        let _ = Ke.push_exn qui ~blit:B.blit_of_bytes ~length:Bytes.length ~off:0 ~len itmp in
        flush_output seek in

  let res = Fmt.strf_like ppf "%a" pp_begin () in
  let _ = Ke.push_exn quo ~blit:B.blit_of_string ~length:String.length res in
  pp_begin ppf () ;

  match seek with
  | `Absolute position ->
    if position < 0
    then lseek ic position `END >>= function
      | Ok _ -> flush_input 0
      | Error _ as err -> return err
    else flush_input position
  | `Relative position ->
    (lseek ic position `CUR >>= function
      | Ok _ -> flush_input 0
      | Error _ as err -> return err)
OCaml

Innovation. Community. Security.