Skip to content

Commit a77e6f2

Browse files
authored
Add Lwt_unix.pread and pwrite (#768)
Resolves #767.
1 parent f4557e8 commit a77e6f2

File tree

13 files changed

+577
-2
lines changed

13 files changed

+577
-2
lines changed

src/unix/dune

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,11 +45,15 @@
4545
windows_get_page_size
4646
unix_mincore
4747
unix_read
48+
unix_pread
4849
windows_read
50+
windows_pread
4951
unix_bytes_read
5052
windows_bytes_read
5153
unix_write
54+
unix_pwrite
5255
windows_write
56+
windows_pwrite
5357
unix_bytes_write
5458
windows_bytes_write
5559
unix_readv_writev_utils
@@ -84,11 +88,15 @@
8488
unix_wait_mincore_job
8589
unix_open_job
8690
unix_read_job
91+
unix_pread_job
8792
windows_read_job
93+
windows_pread_job
8894
unix_bytes_read_job
8995
windows_bytes_read_job
9096
unix_write_job
9197
windows_write_job
98+
unix_pwrite_job
99+
windows_pwrite_job
92100
unix_bytes_write_job
93101
windows_bytes_write_job
94102
unix_stat_job_utils

src/unix/lwt_unix.cppo.ml

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -631,6 +631,12 @@ let wait_read ch =
631631

632632
external stub_read : Unix.file_descr -> Bytes.t -> int -> int -> int = "lwt_unix_read"
633633
external read_job : Unix.file_descr -> Bytes.t -> int -> int -> int job = "lwt_unix_read_job"
634+
external stub_pread :
635+
Unix.file_descr -> Bytes.t -> file_offset:int -> int -> int -> int =
636+
"lwt_unix_pread"
637+
external pread_job :
638+
Unix.file_descr -> Bytes.t -> file_offset:int -> int -> int -> int job =
639+
"lwt_unix_pread_job"
634640

635641
let read ch buf pos len =
636642
if pos < 0 || len < 0 || pos > Bytes.length buf - len then
@@ -643,6 +649,17 @@ let read ch buf pos len =
643649
| false ->
644650
wrap_syscall Read ch (fun () -> stub_read ch.fd buf pos len)
645651

652+
let pread ch buf ~file_offset pos len =
653+
if pos < 0 || len < 0 || pos > Bytes.length buf - len then
654+
invalid_arg "Lwt_unix.pread"
655+
else
656+
Lazy.force ch.blocking >>= function
657+
| true ->
658+
wait_read ch >>= fun () ->
659+
run_job (pread_job ch.fd buf ~file_offset pos len)
660+
| false ->
661+
wrap_syscall Read ch (fun () -> stub_pread ch.fd buf ~file_offset pos len)
662+
646663
external stub_read_bigarray :
647664
Unix.file_descr -> bigarray -> int -> int -> int = "lwt_unix_bytes_read"
648665
external read_bigarray_job :
@@ -672,6 +689,12 @@ let wait_write ch =
672689

673690
external stub_write : Unix.file_descr -> Bytes.t -> int -> int -> int = "lwt_unix_write"
674691
external write_job : Unix.file_descr -> Bytes.t -> int -> int -> int job = "lwt_unix_write_job"
692+
external stub_pwrite :
693+
Unix.file_descr -> Bytes.t -> file_offset:int -> int -> int -> int =
694+
"lwt_unix_pwrite"
695+
external pwrite_job :
696+
Unix.file_descr -> Bytes.t -> file_offset:int -> int -> int -> int job =
697+
"lwt_unix_pwrite_job"
675698

676699
let write ch buf pos len =
677700
if pos < 0 || len < 0 || pos > Bytes.length buf - len then
@@ -684,10 +707,25 @@ let write ch buf pos len =
684707
| false ->
685708
wrap_syscall Write ch (fun () -> stub_write ch.fd buf pos len)
686709

710+
let pwrite ch buf ~file_offset pos len =
711+
if pos < 0 || len < 0 || pos > Bytes.length buf - len then
712+
invalid_arg "Lwt_unix.pwrite"
713+
else
714+
Lazy.force ch.blocking >>= function
715+
| true ->
716+
wait_write ch >>= fun () ->
717+
run_job (pwrite_job ch.fd buf ~file_offset pos len)
718+
| false ->
719+
wrap_syscall Write ch (fun () -> stub_pwrite ch.fd buf ~file_offset pos len)
720+
687721
let write_string ch buf pos len =
688722
let buf = Bytes.unsafe_of_string buf in
689723
write ch buf pos len
690724

725+
let pwrite_string ch buf ~file_offset pos len =
726+
let buf = Bytes.unsafe_of_string buf in
727+
pwrite ch buf ~file_offset pos len
728+
691729
external stub_write_bigarray :
692730
Unix.file_descr -> bigarray -> int -> int -> int = "lwt_unix_bytes_write"
693731
external write_bigarray_job :

src/unix/lwt_unix.cppo.mli

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -298,6 +298,17 @@ val read : file_descr -> bytes -> int -> int -> int Lwt.t
298298
except [Unix.Unix_error Unix.EAGAIN], [Unix.Unix_error Unix.EWOULDBLOCK] or
299299
[Unix.Unix_error Unix.EINTR]. *)
300300

301+
val pread : file_descr -> bytes -> file_offset:int -> int -> int -> int Lwt.t
302+
(** [pread fd buf ~file_offset ofs len] on file descriptors allowing seek,
303+
reads up to [len] bytes from [fd] at offset [file_offset] from the
304+
beginning of the file, and writes them to [buf], starting at offset [ofs].
305+
306+
On Unix systems, the file descriptor position is unaffected. On Windows
307+
it is changed to be just after the last read position.
308+
309+
The thread can fail with any exception that can be raised by [read] or
310+
[lseek]. *)
311+
301312
val write : file_descr -> bytes -> int -> int -> int Lwt.t
302313
(** [write fd buf ofs len] writes up to [len] bytes to [fd] from [buf], starting
303314
at buffer offset [ofs]. The function immediately evaluates to an Lwt thread,
@@ -315,9 +326,25 @@ val write : file_descr -> bytes -> int -> int -> int Lwt.t
315326
[Unix.single_write], except [Unix.Unix_error Unix.EAGAIN],
316327
[Unix.Unix_error Unix.EWOULDBLOCK] or [Unix.Unix_error Unix.EINTR]. *)
317328

329+
val pwrite : file_descr -> bytes -> file_offset:int -> int -> int -> int Lwt.t
330+
(** [pwrite fd buf ~file_offset ofs len] on file descriptors allowing seek,
331+
writes up to [len] bytes to [fd] from [buf], starting at buffer offset
332+
[ofs]. The data is written at offset [file_offset] from the beginning
333+
of [fd].
334+
335+
On Unix systems, the file descriptor position is unaffected. On Windows
336+
it is changed to be just after the last written position.
337+
338+
The thread can fail with any exception that can be raised by [write] or
339+
[lseek]. *)
340+
318341
val write_string : file_descr -> string -> int -> int -> int Lwt.t
319342
(** See {!write}. *)
320343

344+
val pwrite_string :
345+
file_descr -> string -> file_offset:int -> int -> int -> int Lwt.t
346+
(** See {!pwrite}. *)
347+
321348
(** Sequences of buffer slices for {!writev}. *)
322349
module IO_vectors :
323350
sig

src/unix/unix_c/unix_pread.c

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
/* This file is part of Lwt, released under the MIT license. See LICENSE.md for
2+
details, or visit https://github.com/ocsigen/lwt/blob/master/LICENSE.md. */
3+
4+
5+
6+
#include "lwt_config.h"
7+
8+
#if !defined(LWT_ON_WINDOWS)
9+
10+
#include <caml/mlvalues.h>
11+
#include <caml/unixsupport.h>
12+
13+
CAMLprim value lwt_unix_pread(value val_fd, value val_buf, value val_file_ofs,
14+
value val_ofs, value val_len)
15+
{
16+
long ret;
17+
ret = pread(Int_val(val_fd), &Byte(String_val(val_buf), Long_val(val_ofs)),
18+
Long_val(val_len), Long_val(val_file_ofs));
19+
if (ret == -1) uerror("pread", Nothing);
20+
return Val_long(ret);
21+
}
22+
#endif

src/unix/unix_c/unix_pread_job.c

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
/* This file is part of Lwt, released under the MIT license. See LICENSE.md for
2+
details, or visit https://github.com/ocsigen/lwt/blob/master/LICENSE.md. */
3+
4+
5+
6+
#include "lwt_config.h"
7+
8+
#if !defined(LWT_ON_WINDOWS)
9+
10+
#include <caml/alloc.h>
11+
#include <caml/memory.h>
12+
#include <caml/mlvalues.h>
13+
#include <caml/unixsupport.h>
14+
#include <caml/version.h>
15+
#include <errno.h>
16+
#include <string.h>
17+
18+
#include "lwt_unix.h"
19+
20+
#if OCAML_VERSION < 40600
21+
#define Bytes_val(x) String_val(x)
22+
#endif
23+
24+
struct job_pread {
25+
struct lwt_unix_job job;
26+
/* The file descriptor. */
27+
int fd;
28+
/* The amount of data to read. */
29+
long length;
30+
/* The offset in the file */
31+
off_t file_offset;
32+
/* The OCaml string. */
33+
value string;
34+
/* The offset in the string. */
35+
long offset;
36+
/* The result of the pread syscall. */
37+
long result;
38+
/* The value of errno. */
39+
int error_code;
40+
/* The temporary buffer. */
41+
char buffer[];
42+
};
43+
44+
static void worker_pread(struct job_pread *job)
45+
{
46+
job->result = pread(job->fd, job->buffer, job->length, job->file_offset);
47+
job->error_code = errno;
48+
}
49+
50+
static value result_pread(struct job_pread *job)
51+
{
52+
long result = job->result;
53+
if (result < 0) {
54+
int error_code = job->error_code;
55+
caml_remove_generational_global_root(&(job->string));
56+
lwt_unix_free_job(&job->job);
57+
unix_error(error_code, "pread", Nothing);
58+
} else {
59+
memcpy(Bytes_val(job->string) + job->offset, job->buffer, result);
60+
caml_remove_generational_global_root(&(job->string));
61+
lwt_unix_free_job(&job->job);
62+
return Val_long(result);
63+
}
64+
}
65+
66+
CAMLprim value lwt_unix_pread_job(value val_fd, value val_buffer,
67+
value val_file_offset, value val_offset,
68+
value val_length)
69+
{
70+
long length = Long_val(val_length);
71+
LWT_UNIX_INIT_JOB(job, pread, length);
72+
job->fd = Int_val(val_fd);
73+
job->length = length;
74+
job->file_offset = Long_val(val_file_offset);
75+
job->string = val_buffer;
76+
job->offset = Long_val(val_offset);
77+
caml_register_generational_global_root(&(job->string));
78+
return lwt_unix_alloc_job(&(job->job));
79+
}
80+
#endif

src/unix/unix_c/unix_pwrite.c

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
/* This file is part of Lwt, released under the MIT license. See LICENSE.md for
2+
details, or visit https://github.com/ocsigen/lwt/blob/master/LICENSE.md. */
3+
4+
5+
6+
#include "lwt_config.h"
7+
8+
#if !defined(LWT_ON_WINDOWS)
9+
10+
#include <caml/mlvalues.h>
11+
#include <caml/unixsupport.h>
12+
#include <unistd.h>
13+
14+
CAMLprim value lwt_unix_pwrite(value val_fd, value val_buf, value val_file_ofs,
15+
value val_ofs, value val_len)
16+
{
17+
long ret;
18+
ret = pwrite(Int_val(val_fd), &Byte(String_val(val_buf), Long_val(val_ofs)),
19+
Long_val(val_len), Long_val(val_file_ofs));
20+
if (ret == -1) uerror("pwrite", Nothing);
21+
return Val_long(ret);
22+
}
23+
#endif

src/unix/unix_c/unix_pwrite_job.c

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
/* This file is part of Lwt, released under the MIT license. See LICENSE.md for
2+
details, or visit https://github.com/ocsigen/lwt/blob/master/LICENSE.md. */
3+
4+
5+
6+
#include "lwt_config.h"
7+
8+
#if !defined(LWT_ON_WINDOWS)
9+
10+
#include <caml/mlvalues.h>
11+
#include <caml/unixsupport.h>
12+
#include <errno.h>
13+
#include <string.h>
14+
15+
#include "lwt_unix.h"
16+
17+
struct job_pwrite {
18+
struct lwt_unix_job job;
19+
int fd;
20+
long length;
21+
off_t file_offset;
22+
long result;
23+
int error_code;
24+
char buffer[];
25+
};
26+
27+
static void worker_pwrite(struct job_pwrite *job)
28+
{
29+
job->result = pwrite(job->fd, job->buffer, job->length, job->file_offset);
30+
job->error_code = errno;
31+
}
32+
33+
static value result_pwrite(struct job_pwrite *job)
34+
{
35+
long result = job->result;
36+
LWT_UNIX_CHECK_JOB(job, result < 0, "pwrite");
37+
lwt_unix_free_job(&job->job);
38+
return Val_long(result);
39+
}
40+
41+
CAMLprim value lwt_unix_pwrite_job(value val_fd, value val_string,
42+
value val_file_offset, value val_offset,
43+
value val_length)
44+
{
45+
long length = Long_val(val_length);
46+
LWT_UNIX_INIT_JOB(job, pwrite, length);
47+
job->fd = Int_val(val_fd);
48+
job->length = length;
49+
job->file_offset = Long_val(val_file_offset);
50+
memcpy(job->buffer, String_val(val_string) + Long_val(val_offset), length);
51+
return lwt_unix_alloc_job(&(job->job));
52+
}
53+
#endif

src/unix/windows_c/windows_pread.c

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
/* This file is part of Lwt, released under the MIT license. See LICENSE.md for
2+
details, or visit https://github.com/ocsigen/lwt/blob/master/LICENSE.md. */
3+
4+
5+
6+
#include "lwt_config.h"
7+
8+
#if defined(LWT_ON_WINDOWS)
9+
10+
#include <caml/fail.h>
11+
#include <caml/memory.h>
12+
#include <caml/mlvalues.h>
13+
#include <caml/unixsupport.h>
14+
15+
CAMLprim value lwt_unix_pread(value fd, value buf, value vfile_offset,
16+
value vofs, value vlen)
17+
{
18+
intnat ofs, len, file_offset, written;
19+
DWORD numbytes, numwritten;
20+
DWORD err = 0;
21+
22+
Begin_root(buf);
23+
ofs = Long_val(vofs);
24+
len = Long_val(vlen);
25+
file_offset = Long_val(vfile_offset);
26+
written = 0;
27+
if (len > 0) {
28+
numbytes = len;
29+
if (Descr_kind_val(fd) == KIND_SOCKET) {
30+
caml_invalid_argument("Lwt_unix.pread");
31+
} else {
32+
HANDLE h = Handle_val(fd);
33+
OVERLAPPED overlapped;
34+
memset( &overlapped, 0, sizeof(overlapped));
35+
overlapped.OffsetHigh = (DWORD)(file_offset >> 32);
36+
overlapped.Offset = (DWORD)(file_offset & 0xFFFFFFFFLL);
37+
if (!ReadFile(h, &Byte(buf, ofs), numbytes, &numwritten,
38+
&overlapped))
39+
err = GetLastError();
40+
}
41+
if (err) {
42+
win32_maperr(err);
43+
uerror("pread", Nothing);
44+
}
45+
written = numwritten;
46+
}
47+
End_roots();
48+
return Val_long(written);
49+
}
50+
#endif

0 commit comments

Comments
 (0)