package bytream

  1. Overview
  2. Docs
A streaming bytes and crunching them library

Install

dune-project
 Dependency

Authors

Maintainers

Sources

0.2.tar.gz
md5=86080ca17c645960050642fe02d03e41
sha512=f6bc5dfb5c5a9cc2c719bbff4eb66e587e0cbafd1b45a6b7c207df4f810f46466a93508339fd54e5f1c8ee0691e270060bf7535996b1d84d6724b5494de9de1f

doc/src/bytream/in.ml.html

Source file in.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
type t = {
  mutable reader : unit -> chunk;
  (* Current chunk *)
  mutable buffer : buffer;
  mutable offset : int;  (** Buffer's offset *)
  mutable length : int;  (** Buffer's length *)
  (* *)
  mutable total_offset : int;  (** Total read bytes from some source *)
  (* *)
  overlap_buffer : buffer;
      (** A small buffer to resolve the data gap situation between chunks *)
}

and buffer =
  (char, Bigarray.int8_unsigned_elt, Bigarray.c_layout) Bigarray.Array1.t

and chunk = buffer:buffer * offset:int * length:int

(* ===================================================================
    CONSTRUCTORS
   =================================================================== *)

let make' ?(overlap_size = 0xFF) reader =
  {
    reader;
    buffer = Bstr.empty;
    offset = 0;
    length = 0;
    total_offset = 0;
    overlap_buffer = Bstr.create overlap_size;
  }

let make ?overlap_size reader =
  let reader () =
    let buffer = reader () in
    (~buffer, ~offset:0, ~length:Bstr.(length buffer))
  in

  make' ?overlap_size reader

let of_buffer buffer =
  {
    reader = (fun () -> raise End_of_file);
    buffer;
    offset = 0;
    length = Bstr.length buffer;
    total_offset = 0;
    overlap_buffer = Bstr.empty;
  }

let of_string s = Bstr.of_string s |> of_buffer

let of_channel ?(io_buffer_size = 4096) ic =
  let buffer = Bstr.create io_buffer_size in

  let reader () =
    match In_channel.input_bigarray ic buffer 0 io_buffer_size with
    | 0 -> raise End_of_file
    | length -> (~buffer, ~offset:0, ~length)
  in

  make' reader

(* ===================================================================
    BUFFER MANIPULATION UTILITY FUNCTIONS
   =================================================================== *)

let[@inline] available_to_read in_stream = in_stream.length

let advance_offset in_stream n =
  (* assert (in_stream.length - n >= 0); *)
  in_stream.offset <- in_stream.offset + n;
  in_stream.length <- in_stream.length - n;
  in_stream.total_offset <- in_stream.total_offset + n

let[@inline] acquire_chunk in_stream = in_stream.reader ()

let set_chunk in_stream ((~buffer, ~offset, ~length) : chunk) =
  in_stream.buffer <- buffer;
  in_stream.offset <- offset;
  in_stream.length <- length

let get_chunk in_stream =
  (~buffer:in_stream.buffer, ~offset:in_stream.offset, ~length:in_stream.length)

let[@inline] position in_stream = in_stream.total_offset

let rec consume_bytes in_stream len =
  if len <> 0 then begin
    let available_bytes = available_to_read in_stream in
    let available_to_consume = min available_bytes len in

    advance_offset in_stream available_to_consume;

    if available_to_consume < len then begin
      set_chunk in_stream (acquire_chunk in_stream);
      consume_bytes in_stream (len - available_to_consume)
    end
  end

(* ===================================================================
    INPUTS
   =================================================================== *)

let gen_input
    ~(blit : Bstr.t -> src_off:int -> 'buf -> dst_off:int -> len:int -> unit)
    in_stream buffer off len =
  assert (len > 0);

  let available_bytes = available_to_read in_stream in

  if available_bytes = 0 then begin
    set_chunk in_stream (acquire_chunk in_stream)
  end;

  let batched_bytes = min len available_bytes in

  blit in_stream.buffer ~src_off:in_stream.offset buffer ~dst_off:off
    ~len:batched_bytes;

  advance_offset in_stream batched_bytes;

  batched_bytes

let rec gen_really_input ~blit in_stream buffer off len =
  if len > 0 then
    let batched_bytes = gen_input ~blit in_stream buffer off len in
    gen_really_input ~blit in_stream buffer (off + batched_bytes)
      (len - batched_bytes)

let[@inline] input in_stream buffer off len =
  gen_input ~blit:Bstr.blit in_stream buffer off len

and[@inline] really_input in_stream buffer off len =
  gen_really_input ~blit:Bstr.blit in_stream buffer off len

let[@inline] input_bytes in_stream bytes off len =
  gen_input ~blit:Bstr.blit_to_bytes in_stream bytes off len

and[@inline] really_input_bytes in_stream bytes off len =
  gen_really_input ~blit:Bstr.blit_to_bytes in_stream bytes off len

(* ===================================================================
    ENSURE MECHANISM WITH OVERLAPPING
   =================================================================== *)

let push_back in_stream chunk =
  let acquire_chunk' = in_stream.reader in

  in_stream.reader <-
    (fun () ->
      in_stream.reader <- acquire_chunk';
      chunk)

let[@inline] ensure_bytes in_stream length =
  (* assert (length <= Bstr.length in_stream.overlap_buffer); *)
  let aux in_stream available_bytes length =
    if available_bytes <> 0 then (
      really_input in_stream in_stream.overlap_buffer 0 length;
      push_back in_stream @@ get_chunk in_stream;

      set_chunk in_stream (~buffer:in_stream.overlap_buffer, ~offset:0, ~length))
    else set_chunk in_stream (acquire_chunk in_stream)
  in

  let available_bytes = (available_to_read [@inlined]) in_stream in
  if available_bytes < length then aux in_stream available_bytes length

let[@inline] ensure_bytes_at in_stream len =
  (ensure_bytes [@inlined]) in_stream len;
  let offset = in_stream.offset in
  (advance_offset [@inlined]) in_stream len;
  offset

let ensure_chunk in_stream length =
  let offset = ensure_bytes_at in_stream length in
  (~buffer:in_stream.buffer, ~offset, ~length)

let ensure_buffer in_stream len =
  let off = ensure_bytes_at in_stream len in
  Bstr.sub in_stream.buffer ~off ~len

(* ===================================================================
    COMBINATORS
   =================================================================== *)

let take n input_value in_stream = List.init n @@ fun _ -> input_value in_stream

let with_size f in_stream =
  let off = position in_stream in
  let result = f in_stream in
  let off' = position in_stream in

  (result, off' - off)

let[@tail_mod_cons] rec many input_value in_stream =
  match input_value in_stream with
  | exception End_of_file -> []
  | value -> value :: many input_value in_stream

(* ===================================================================
    INPUT INTEGER VALUES
   =================================================================== *)

let[@inline] gen_get in_stream f n =
  let off = (ensure_bytes_at [@inlined]) in_stream n in
  f in_stream.buffer off

let[@inline] input_char in_stream = gen_get in_stream Bstr.get 1
let[@inline] input_int8 in_stream = gen_get in_stream Bstr.get_int8 1
let[@inline] input_uint8 in_stream = gen_get in_stream Bstr.get_uint8 1
let[@inline] input_byte in_stream = gen_get in_stream Bstr.get_uint8 1

(* int16 *)
let[@inline] input_int16_be in_stream = gen_get in_stream Bstr.get_int16_be 2
let[@inline] input_int16_ne in_stream = gen_get in_stream Bstr.get_int16_ne 2
let[@inline] input_int16_le in_stream = gen_get in_stream Bstr.get_int16_le 2
let[@inline] input_uint16_be in_stream = gen_get in_stream Bstr.get_uint16_be 2
let[@inline] input_uint16_ne in_stream = gen_get in_stream Bstr.get_uint16_ne 2
let[@inline] input_uint16_le in_stream = gen_get in_stream Bstr.get_uint16_le 2

(* int32 *)
let[@inline] input_int32_be in_stream = gen_get in_stream Bstr.get_int32_be 4
let[@inline] input_int32_ne in_stream = gen_get in_stream Bstr.get_int32_ne 4
let[@inline] input_int32_le in_stream = gen_get in_stream Bstr.get_int32_le 4

(* int64 *)
let[@inline] input_int64_be in_stream = gen_get in_stream Bstr.get_int64_be 4
let[@inline] input_int64_ne in_stream = gen_get in_stream Bstr.get_int64_ne 4
let[@inline] input_int64_le in_stream = gen_get in_stream Bstr.get_int64_le 4

(* ===================================================================
    OTHER
   =================================================================== *)

let input_string in_stream len =
  let bytes = Bytes.create len in
  (really_input_bytes [@inlined]) in_stream bytes 0 len;
  Bytes.unsafe_to_string bytes

let input_while_into_buffer ~max_len p in_stream buffer =
  let rec aux count =
    try
      if count <= max_len then begin
        (ensure_bytes [@inlined]) in_stream 1;
        let ch = Bstr.get in_stream.buffer in_stream.offset in

        if p ch then begin
          (advance_offset [@inlined]) in_stream 1;
          Buffer.add_char buffer ch;
          aux (succ count)
        end
      end
    with _ -> ()
  in

  aux 0

let check_on_end_of_file in_steam =
  try
    let new_chunk = acquire_chunk in_steam in
    push_back in_steam new_chunk;
    false
  with End_of_file -> true

let input_while ?(max_len = Int.max_int) p in_stream =
  let[@inline] input_while_buffered max_len p in_stream =
    let[@local] buffer =
      Buffer.create (if max_len <> Int.max_int then max_len else 60)
    in

    input_while_into_buffer ~max_len p in_stream buffer;
    Buffer.contents buffer
  in

  let rec try_count_in_buffer max_len p in_stream length =
    let remaining_bytes_in_buffer = available_to_read in_stream - length in

    if remaining_bytes_in_buffer > 0 then begin
      let ch = Bstr.get in_stream.buffer (in_stream.offset + length) in
      if length <= max_len && p ch then
        try_count_in_buffer max_len p in_stream (succ length)
      else input_string in_stream length
    end
    else if check_on_end_of_file in_stream then input_string in_stream length
    else (input_while_buffered [@inlined]) max_len p in_stream
  in

  try_count_in_buffer max_len p in_stream 0

let input_while' ~max_len p in_stream =
  let string = input_while ~max_len p in_stream in
  consume_bytes in_stream (max_len - String.length string);
  string

let input_line in_stream =
  let string = input_while (( <> ) '\n') in_stream in
  match input_char in_stream with
  | exception End_of_file when "" = string -> raise End_of_file
  | (exception End_of_file) | '\n' -> string
  | _ -> failwith "impossible state of input_line"

(* ===================================================================
    TRANSFORMING
   =================================================================== *)

(* ... *)