Bytesrw adapter for Eio
ocaml
codec
1(*---------------------------------------------------------------------------
2 Copyright (c) 2025 Anil Madhavapeddy <anil@recoil.org>. All rights reserved.
3 SPDX-License-Identifier: ISC
4 ---------------------------------------------------------------------------*)
5
6(** Bytesrw adapters for Eio
7
8 This module provides adapters to create {!Bytesrw.Bytes.Reader.t} and
9 {!Bytesrw.Bytes.Writer.t} from Eio flows, mirroring the API of
10 {!Bytesrw_unix} for Eio's effect-based I/O. *)
11
12open Bytesrw
13
14(** Create a [Bytes.Reader.t] from an Eio source flow.
15
16 Reads directly from the flow without intermediate buffering.
17
18 @param slice_length
19 Maximum bytes per slice (default: 65536, which is
20 {!Bytes.Slice.unix_io_buffer_size}) *)
21let bytes_reader_of_flow ?(slice_length = Bytes.Slice.unix_io_buffer_size)
22 (flow : _ Eio.Flow.source) : Bytes.Reader.t =
23 let buf_size = Bytes.Slice.check_length slice_length in
24 let read () =
25 let cstruct = Cstruct.create buf_size in
26 match Eio.Flow.single_read flow cstruct with
27 | 0 -> Bytes.Slice.eod
28 | count ->
29 let data_cs = Cstruct.sub cstruct 0 count in
30 let buf = Cstruct.to_bytes data_cs in
31 Bytes.Slice.make buf ~first:0 ~length:count
32 | exception End_of_file -> Bytes.Slice.eod
33 in
34 Bytes.Reader.make ~slice_length read
35
36(** Create a [Bytes.Reader.t] from an Eio file at [offset].
37
38 Uses pread for random-access reading without loading the whole file.
39
40 @param slice_length Maximum bytes per slice (default: 65536).
41 @param length Number of bytes to read (default: to end of file). *)
42let pread_reader ?(slice_length = Bytes.Slice.unix_io_buffer_size) ?length
43 ~offset (file : _ Eio.File.ro) : Bytes.Reader.t =
44 let stat = Eio.File.stat file in
45 let file_length = Optint.Int63.to_int stat.size in
46 let total = match length with Some n -> n | None -> file_length - offset in
47 let pos = ref 0 in
48 let buf_size = Bytes.Slice.check_length slice_length in
49 let buf = Bytes.create buf_size in
50 let read () =
51 if !pos >= total then Bytes.Slice.eod
52 else
53 let len = min buf_size (total - !pos) in
54 let cs = Cstruct.create len in
55 Eio.File.pread_exact file
56 ~file_offset:(Optint.Int63.of_int (offset + !pos))
57 [ cs ];
58 Cstruct.blit_to_bytes cs 0 buf 0 len;
59 pos := !pos + len;
60 Bytes.Slice.make buf ~first:0 ~length:len
61 in
62 Bytes.Reader.make ~slice_length read
63
64(** Create a [Bytes.Writer.t] from an Eio sink flow.
65
66 Writes directly to the flow without intermediate buffering.
67
68 @param slice_length
69 Suggested slice length for upstream (default: 65536, which is
70 {!Bytes.Slice.unix_io_buffer_size}) *)
71let bytes_writer_of_flow ?(slice_length = Bytes.Slice.unix_io_buffer_size)
72 (flow : _ Eio.Flow.sink) : Bytes.Writer.t =
73 let rec write slice =
74 if Bytes.Slice.is_eod slice then ()
75 else begin
76 let bytes = Bytes.Slice.bytes slice in
77 let first = Bytes.Slice.first slice in
78 let length = Bytes.Slice.length slice in
79 let cstruct = Cstruct.of_bytes ~off:first ~len:length bytes in
80 match Eio.Flow.single_write flow [ cstruct ] with
81 | count when count = length -> ()
82 | count -> write (Option.get (Bytes.Slice.drop count slice))
83 end
84 in
85 Bytes.Writer.make ~slice_length write