Skip to content

Commit

Permalink
Add pread and pwrite (no windows support)
Browse files Browse the repository at this point in the history
  • Loading branch information
patricoferris committed Feb 2, 2023
1 parent 471ca03 commit 4fd8e39
Show file tree
Hide file tree
Showing 8 changed files with 156 additions and 1 deletion.
1 change: 1 addition & 0 deletions cstruct-unix.opam
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ build: [
depends: [
"ocaml" {>= "4.06.0"}
"dune" {>= "2.0.0"}
"optint"
"base-unix"
"cstruct" {=version}
]
Expand Down
2 changes: 1 addition & 1 deletion unix/dune
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,4 @@
(language c)
(names read_stubs write_stubs writev_stubs send_stubs recv_stubs
recvfrom_stubs sendto_stubs))
(libraries cstruct unix))
(libraries cstruct optint unix))
53 changes: 53 additions & 0 deletions unix/read_stubs.c
Original file line number Diff line number Diff line change
Expand Up @@ -74,3 +74,56 @@ CAMLprim value stub_cstruct_read(value val_fd, value val_c)
#endif
CAMLreturn(Val_int(n));
}

CAMLprim value stub_cstruct_pread(value val_fd, value v_foff, value val_c)
{
CAMLparam3(val_fd, v_foff, val_c);
CAMLlocal3(val_buf, val_ofs, val_len);
uint8_t *buf;
size_t len;
ssize_t n = 0;
#ifdef WIN32
int win32err = 0;
SOCKET s;
HANDLE h;
DWORD numread;
int ok;
#endif
val_buf = Field(val_c, 0);
val_ofs = Field(val_c, 1);
val_len = Field(val_c, 2);

buf = (uint8_t *)Caml_ba_data_val(val_buf) + Long_val(val_ofs);
len = (size_t)Long_val(val_len);

#ifdef WIN32
switch (Descr_kind_val(val_fd))
{
case KIND_SOCKET:
uerror("stub_cstruct_pread on socket", Nothing)
case KIND_HANDLE:
h = Handle_val(val_fd);
caml_release_runtime_system();
ok = ReadFile(h, buf, len, &numread, NULL);
win32err = GetLastError();
n = numread;
caml_acquire_runtime_system();

if (!ok)
{
win32_maperr(win32err);
uerror("stub_cstruct_read", Nothing);
}
break;
default:
caml_failwith("unknown Descr_kind_val");
}
#else
caml_release_runtime_system();
n = pread(Int_val(val_fd), buf, len, Int_val(v_foff));
caml_acquire_runtime_system();
if (n < 0)
uerror("stub_cstruct_read", Nothing);
#endif
CAMLreturn(Val_int(n));
}
13 changes: 13 additions & 0 deletions unix/unix_cstruct.ml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*)

type offset = Optint.Int63.t

let of_fd fd =
let buffer = Bigarray.(array1_of_genarray (Unix.map_file fd char c_layout false [|-1|])) in
Cstruct.of_bigarray buffer
Expand All @@ -23,6 +25,7 @@ external stub_iov_max: unit -> int = "stub_cstruct_iov_max"

external stub_write: Unix.file_descr -> Cstruct.t -> int = "stub_cstruct_write"

external stub_pwrite: Unix.file_descr -> Cstruct.t -> offset -> int = "stub_cstruct_pwrite"
external stub_writev: Unix.file_descr -> Cstruct.t list -> int = "stub_cstruct_writev"

let iov_max = stub_iov_max ()
Expand All @@ -49,6 +52,12 @@ let rec write fd buf =
write fd @@ Cstruct.shift buf n
end

let rec pwrite ~off fd buf =
if Cstruct.length buf > 0 then begin
let n = stub_pwrite fd buf off in
pwrite fd ~off:(Optint.Int63.add off (Optint.Int63.of_int n)) @@ Cstruct.shift buf n
end

let writev fd bufs =
let rec use_writev = function
| [] -> ()
Expand All @@ -67,6 +76,10 @@ external recv: Unix.file_descr -> Cstruct.t -> Unix.msg_flag list -> int = "stub

external read: Unix.file_descr -> Cstruct.t -> int = "stub_cstruct_read"

external stub_pread: Unix.file_descr -> offset -> Cstruct.t -> int = "stub_cstruct_pread"

let pread ~off fd buf = stub_pread fd off buf

external recvfrom : Unix.file_descr -> Cstruct.t -> Unix.msg_flag list -> int * Unix.sockaddr = "stub_cstruct_recvfrom"

external sendto : Unix.file_descr -> Cstruct.t -> Unix.msg_flag list -> Unix.sockaddr -> int = "stub_cstruct_sendto"
7 changes: 7 additions & 0 deletions unix/unix_cstruct.mli
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,20 @@ val read: Unix.file_descr -> Cstruct.t -> int
(** [read fd cs] reads from the file descriptor into the buffer, returning the number of bytes read.
Like {! Unix.read}, but for Cstruct. *)

val pread: off:Optint.Int63.t ->Unix.file_descr -> Cstruct.t -> int
(** [pread ~off fd cs] reads from the file descriptor at position [off] into the buffer,
returning the number of bytes. *)

val write: Unix.file_descr -> Cstruct.t -> unit
(** [write fd cs] writes the whole Cstruct to the file descriptor. Like {! Unix.write}, but for Cstruct. *)

val writev: Unix.file_descr -> Cstruct.t list -> unit
(** [writev fd cs] writes the whole list of Cstructs to the file descriptor.
Like {! Unix.write}, but for a list of Cstructs. *)

val pwrite: off:Optint.Int63.t -> Unix.file_descr -> Cstruct.t -> unit
(** [pwrite ~off fd cs] writes all of [cs] to the file descriptor at position [off]. *)

val send: Unix.file_descr -> Cstruct.t -> Unix.msg_flag list -> int
(** [send fd c flags] sends the Cstruct to the file descriptor, returning the number of bytes written.
Like {! Unix.send}, but for Cstruct. *)
Expand Down
58 changes: 58 additions & 0 deletions unix/write_stubs.c
Original file line number Diff line number Diff line change
Expand Up @@ -67,3 +67,61 @@ CAMLprim value stub_cstruct_write(value val_fd, value val_c)
#endif
CAMLreturn(Val_int(n));
}

CAMLprim value stub_cstruct_pwrite(value val_fd, value val_c, value v_foff)
{
CAMLparam2(val_fd, val_c);
CAMLlocal3(val_buf, val_ofs, val_len);
val_buf = Field(val_c, 0);
val_ofs = Field(val_c, 1);
val_len = Field(val_c, 2);
void *buf = (char *)Caml_ba_data_val(val_buf) + Long_val(val_ofs);
size_t len = Long_val(val_len);
ssize_t n = 0;

#ifdef _WIN32
int win32err = 0;
switch (Descr_kind_val(val_fd))
{
case KIND_SOCKET:
SOCKET s = Socket_val(val_fd);

caml_release_runtime_system();
n = send(s, buf, len, 0);
win32err = WSAGetLastError();
caml_acquire_runtime_system();

if (n == SOCKET_ERROR)
{
win32_maperr(win32err);
unix_error(errno, "stub_cstruct_write", Nothing);
}
break;
case KIND_HANDLE:
HANDLE h = Handle_val(val_fd);
DWORD numwritten;
caml_release_runtime_system();
int ok = WriteFile(h, buf, len, &numwritten, NULL);
win32err = GetLastError();

n = numwritten;
caml_acquire_runtime_system();

if (!ok)
{
win32_maperr(win32err);
uerror("stub_cstruct_write", Nothing);
}
break;
default:
caml_failwith("unknown Descr_kind_val");
}
#else
caml_release_runtime_system();
n = pwrite(Int_val(val_fd), buf, len, Int_val(v_foff));
caml_acquire_runtime_system();
if (n < 0)
uerror("stub_cstruct_write", Nothing);
#endif
CAMLreturn(Val_int(n));
}
2 changes: 2 additions & 0 deletions unix/writev_stubs.c
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
#include <errno.h>
#ifndef _WIN32
#include <sys/uio.h>
#include <sys/types.h>
#include <unistd.h>
#include <limits.h>
#endif

Expand Down
21 changes: 21 additions & 0 deletions unix_test/tests.ml
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,24 @@ let read_and_check fd sent_message =
let actual = Cstruct.(to_string @@ sub buf 0 !read) in
Alcotest.(check string) "read contents" expected actual

let test_pwrite_file () =
let msg = Cstruct.of_string "mirage" in
let change = Cstruct.of_string "culously" in
with_tmp_dir
(fun dir ->
let test = Filename.concat dir "test" in
let fd = Unix.openfile test [ Unix.O_CREAT; Unix.O_RDWR ] 0o644 in
finally (fun () ->
Unix_cstruct.pwrite ~off:Optint.Int63.zero fd msg;
let buf = Cstruct.create 128 in
let got = Unix_cstruct.read fd buf in
Alcotest.(check string) "same string" (Cstruct.to_string msg) (Cstruct.(to_string (sub buf 0 got)));
Unix_cstruct.pwrite ~off:(Optint.Int63.of_int 4) fd change;
let got = Unix_cstruct.pread ~off:Optint.Int63.zero fd buf in
Alcotest.(check string) "same string" "miraculously" (Cstruct.(to_string (sub buf 0 got)));
) (fun () -> Unix.close fd)
)

let test_writev_file () =
with_tmp_dir
(fun dir ->
Expand Down Expand Up @@ -163,6 +181,9 @@ let suite = [
"test read and writev via a file", `Quick, test_writev_file;
"test read and writev via a socket", `Quick, test_writev_socket;
];
"pwrite", [
"test read and pwrite via a file", `Quick, test_pwrite_file;
];
"send recv", [
"test send and recv", `Quick, test_send_recv;
];
Expand Down

0 comments on commit 4fd8e39

Please sign in to comment.