Line data Source code
1 : #ifndef HEADER_fd_src_util_io_fd_io_h
2 : #define HEADER_fd_src_util_io_fd_io_h
3 :
4 : /* API for platform agnostic high performance stream I/O. Summary:
5 :
6 : Read at least min bytes directly from stream fd into size max buffer
7 : buf (1<=min<=max):
8 :
9 : ulong rsz; int err = fd_io_read( fd, buf, min, max, &rsz );
10 : if ( FD_LIKELY( err==0 ) ) ... success, rsz in [min,max], buf updated
11 : else if( FD_LIKELY( err< 0 ) ) ... EOF encountered, rsz is [0,min), buf updated
12 : else ... I/O error, rsz is zero, buf clobbered
13 : ... err is strerror compat, err is neither EAGAIN nor EWOULDBLOCK
14 : ... fd should be considered failed
15 :
16 : This usage will block (even if the stream is non-blocking) until min
17 : bytes are read, EOF is encountered or there is an I/O error. As
18 : such, the min==1 case behaves like a POSIX read on a blocking stream.
19 : The min==max case will read an exact number of bytes from the stream
20 : and return an error if this is not possible. The 1<min<max case is
21 : useful for implementing buffered I/O.
22 :
23 : A non-blocking read on a non-blocking stream is possible by setting
24 : min==0:
25 :
26 : ulong rsz; int err = fd_io_read( fd, buf, 0UL, max, &rsz );
27 : if ( FD_LIKELY( err==0 ) ) ... success, rsz in [1,max], buf updated
28 : else if( FD_LIKELY( err==EAGAIN ) ) ... try again later, rsz is zero, buf unchanged
29 : else if( FD_LIKELY( err< 0 ) ) ... EOF encountered, rsz is zero, buf unchanged
30 : else ... I/O error, rsz is zero, buf clobbered
31 : ... err is strerror compat, err is neither EAGAIN nor EWOULDBLOCK
32 : ... fd should be considered failed
33 :
34 : (If min==0 and stream fd is blocking, the EAGAIN case will never
35 : occur.)
36 :
37 : Write is nearly symmetrical to read.
38 :
39 : Write at least min byte and at most max bytes from buf into stream fd
40 : (1<=min<=max):
41 :
42 : ulong wsz; int err = fd_io_write( fd, buf, min, max, &wsz );
43 : if( FD_LIKELY( err==0 ) ) ... success, wsz in [min,max]
44 : else ... I/O error, wsz is zero, how much of buf was streamed before the error is unknown
45 : ... err is strerror compat, err is neither EAGAIN nor EWOULDBLOCK
46 : ... fd should be considered failed
47 :
48 : This usage will block (even if the stream is non-blocking) until min
49 : bytes are written or there is an I/O error. As such, the min==1 case
50 : behaves like a POSIX write on a stream. The min==max case will write
51 : an exact number of bytes to fd and give an error if this is not
52 : possible. The 1<min<max case is useful for implementing buffered
53 : I/O.
54 :
55 : A non-blocking write on a non-blocking stream is possible by setting
56 : min==0:
57 :
58 : ulong wsz; int err = fd_io_write( fd, buf, 0UL, max, &wsz );
59 : if ( FD_LIKELY( err==0 ) ) ... success, wsz in [1,max]
60 : else if( FD_LIKELY( err==EAGAIN ) ) ... try again later, wsz is zero
61 : else ... I/O error, wsz is zero, how much of buf was streamed before the error is unknown
62 : ... err is strerror compat, err is neither EAGAIN nor EWOULDBLOCK
63 : ... fd should be considered failed
64 :
65 : (If min==0 and stream fd is blocking, the EAGAIN case will never
66 : occur.)
67 :
68 : Each call above typically requires at least one system call. This
69 : can be inefficient if doing lots of tiny reads and writes. For
70 : higher performance usage in cases like this, buffered I/O APIs are
71 : also provided.
72 :
73 : Buffered reads:
74 :
75 : ... setup buffered reads from stream fd using the rbuf_sz size
76 : ... buffer rbuf (rbuf_sz>0) as the read buffer
77 :
78 : fd_io_buffered_istream_t in[1];
79 : fd_io_buffered_istream_init( in, fd, rbuf, rbuf_sz );
80 : ... in is initialized and has ownership of fd and rbuf
81 :
82 : ... accessors (these return the values used to init in)
83 :
84 : int fd = fd_io_buffered_istream_fd ( in );
85 : void * rbuf = fd_io_buffered_istream_rbuf ( in );
86 : ulong rbuf_sz = fd_io_buffered_istream_rbuf_sz( in );
87 :
88 : ... read sz bytes from a buffered stream
89 :
90 : int err = fd_io_buffered_istream_read( in, buf, sz );
91 : if ( FD_LIKELY( err==0 ) ) ... success, buf holds the next sz bytes of stream
92 : else if( FD_LIKELY( err< 0 ) ) ... EOF before sz bytes could be read, buf clobbered
93 : else ... I/O error, buf clobbered
94 : ... err is strerror compat, err is neither EAGAIN nor EWOULDBLOCK
95 : ... in and fd should be considered failed
96 :
97 : ... skip sz bytes in a buffered stream
98 :
99 : int err = fd_io_buffered_istream_skip( in, sz );
100 : if ( FD_LIKELY( err==0 ) ) ... success, sz bytes skipped
101 : else if( FD_LIKELY( err< 0 ) ) ... EOF before sz bytes could be skipped
102 : else ... I/O error
103 : ... err is strerror compat, err is neither EAGAIN nor EWOULDBLOCK
104 : ... in and fd should be considered failed
105 :
106 : ... zero-copy read bytes from a buffered stream
107 :
108 : ulong peek_sz = fd_io_buffered_istream_peek_sz( in ); ... returns number of bytes currently buffered
109 : void const * peek = fd_io_buffered_istream_peek ( in ); ... returns location of current buffered bytes
110 :
111 : fd_io_buffered_istream_seek( in, sz ); ... consume sz currently buffered bytes, sz in [0,peek_sz]
112 :
113 : ... read buffering control
114 :
115 : int err = fd_io_buffered_istream_fetch( in );
116 : if ( FD_LIKELY( err==0 ) ) ... success, peek_sz updated to at most rbuf_sz
117 : else if( FD_LIKELY( err< 0 ) ) ... end-of-file, peek_sz updated to unconsumed bytes remaining (at most rbuf_sz)
118 : else if( FD_LIKELY( err==EAGAIN ) ) ... try again later, peek_sz unchanged
119 : else ... I/O error, peek_sz unchanged
120 : ... err is strerror compat, err is neither EAGAIN nor EWOULDBLOCK
121 : ... in and fd should be considered failed
122 :
123 : (The EAGAIN case only applies for a non-blocking stream.)
124 :
125 : TODO: consider option to do block until a min level?
126 :
127 : ... finish using buffered stream
128 :
129 : fd_io_buffered_istream_fini( in );
130 : ... in is not in use and no longer has ownership of fd and rbuf.
131 : ... IMPORTANT! buffering might have pushed fd's file offset beyond
132 : ... the bytes the user has actually consumed. It is the user's
133 : ... responsibility for handling this. (Usually nothing needs to be
134 : ... or can be done as either the next operation is to close fd or
135 : ... the fd does not support seeking.)
136 :
137 : There are nearly symmetric APIs for buffered writes.
138 :
139 : ... start buffered writing to stream fd using the wbuf_sz size
140 : ... buffer wbuf (wbuf_sz>0) as the write buffer
141 :
142 : fd_io_buffered_ostream_t out[1];
143 : fd_io_buffered_ostream_init( out, fd, wbuf, wbuf_sz );
144 : ... out is initialized and has ownership of fd and wbuf
145 :
146 : ... accessors (these return the values used to init in)
147 :
148 : int fd = fd_io_buffered_ostream_fd ( out );
149 : void * wbuf = fd_io_buffered_ostream_wbuf ( out );
150 : ulong wbuf_sz = fd_io_buffered_ostream_wbuf_sz( out );
151 :
152 : ... write sz bytes to a buffered stream
153 :
154 : int err = fd_io_buffered_ostream_write( out, buf, sz );
155 : if( FD_LIKELY( err==0 ) ) ... success, sz bytes from have been written from the caller's POV
156 : else ... I/O error, err is strerror compat, err is neither EAGAIN nor EWOULDBLOCK
157 : ... out and fd should be considered failed
158 :
159 : ... zero-copy write bytes to a buffered stream
160 :
161 : ulong peek_sz = fd_io_buffered_ostream_peek_sz( out ); ... returns amount of unused write buffer space, in [0,wbuf_sz]
162 : void * peek = fd_io_buffered_ostream_peek ( out ); ... returns location of unused write buffer space
163 :
164 : fd_io_buffered_ostream_seek( in, sz ); ... commit sz unused bytes of write buffer, sz in [0,peek_sz]
165 :
166 : ... write buffer control
167 :
168 : int err = fd_io_buffered_ostream_flush( out );
169 : if( FD_LIKELY( err==0 ) ) ... success, all buffered bytes have been drained to fd
170 : else ... I/O error, err is strerror compat, err is neither EAGAIN nor EWOULDBLOCK
171 : ... out and fd should be considered failed
172 :
173 : (This will block even for a non-blocking stream.)
174 :
175 : ... finish using buffered stream
176 :
177 : fd_io_buffered_ostream_fini( out );
178 : ... out is not in use and no longer has ownership of fd and wbuf.
179 : ... IMPORTANT! fini does not do any flushing of buffered writes (as
180 : ... such fini is guaranteed to always succeed, can be applied to
181 : ... out's that have failed, etc). It is the users responsibility
182 : ... to do any final flush before calling fini.
183 :
184 : More details below. */
185 :
186 : #include "../bits/fd_bits.h"
187 :
188 : /* FD_IO_SEEK_TYPE_{SET,CUR,END} give supported seek types. They have
189 : the same meaning as the corresponding lseek SEEK_{SET,CUR_END}. */
190 :
191 3009003 : #define FD_IO_SEEK_TYPE_SET (0)
192 2021187 : #define FD_IO_SEEK_TYPE_CUR (1)
193 : #define FD_IO_SEEK_TYPE_END (2)
194 :
195 : /* fd_io_buffered_{istream,ostream}_t is an opaque handle of an
196 : {input,output} stream with buffered {reads,writes}. The internals
197 : are visible here to facilitate inlining various operations. This is
198 : declaration friendly (e.g. usually should just do
199 : "fd_io_buffered_istream_t in[1];" on the stack to get a suitable
200 : memory region for the stream state). These are not meant to be
201 : persistent or shared IPC. */
202 :
203 : struct fd_io_buffered_istream_private {
204 : int fd; /* Open normal-ish file descriptor of stream */
205 : uchar * rbuf; /* Read buffer, non-NULL, indexed [0,rbuf_sz), arb alignment */
206 : ulong rbuf_sz; /* Read buffer size, positive */
207 : ulong rbuf_lo; /* Buf bytes [0,rbuf_lo) have already been consumed */
208 : ulong rbuf_ready; /* Number of buffered byte that haven't been consumed, 0<=rbuf_lo<=(rbuf_lo+rbuf_ready)<=rbuf_sz */
209 : };
210 :
211 : typedef struct fd_io_buffered_istream_private fd_io_buffered_istream_t;
212 :
213 : struct fd_io_buffered_ostream_private {
214 : int fd; /* Open normal-ish file descriptor of stream */
215 : uchar * wbuf; /* Write buffer, non-NULL, indexed [0,wbuf_sz), arb alignment */
216 : ulong wbuf_sz; /* Write buffer size, positive */
217 : ulong wbuf_used; /* Number buffered bytes that haven't been written to fd, in [0,wbuf_sz] */
218 : };
219 :
220 : typedef struct fd_io_buffered_ostream_private fd_io_buffered_ostream_t;
221 :
222 : /* FD_IO_MMIO_MODE_* give supported modes for memory mapped I/O */
223 :
224 63 : #define FD_IO_MMIO_MODE_READ_ONLY (0)
225 12354 : #define FD_IO_MMIO_MODE_READ_WRITE (1)
226 :
227 : FD_PROTOTYPES_BEGIN
228 :
229 : /* fd_io_read streams at least dst_min bytes from the given file
230 : descriptor into the given memory region. fd should be an open
231 : normal-ish file descriptor (it is okay for fd to be non-blocking).
232 : dst points in the caller's address space with arbitrary alignment to
233 : the first byte of the dst_max byte memory region to use (assumes dst
234 : non-NULL, and dst_min is at most dst_max). The caller should not
235 : read or write this region during the call and no interest in dst is
236 : retained on return. If dst_min is 0, this will try to read dst_max
237 : from the stream exactly once. If dst_max is 0, is a no-op.
238 :
239 : Returns 0 on success. On success, *_dst_sz will be the number of
240 : bytes read into dst. Will be in [dst_min,dst_max].
241 :
242 : Returns a negative number if end-of-file was encountered before
243 : reading dst_min bytes. *_dst_sz will be the number bytes read into
244 : dst when the end-of-file was encountered. Will be in [0,dst_min).
245 :
246 : Returns an errno compatible error code on failure (note that all
247 : errnos are positive). If errno is anything other than EAGAIN, the
248 : underlying fd should be considered to be in a failed state such that
249 : the only valid operation on fd is to close it. *_dst_sz will be zero
250 : and the contents of dst will be undefined. This API fixes up the
251 : POSIX glitches around EWOULDBLOCK / EAGAIN: if the underlying target
252 : has EWOULDBLOCK different from EAGAIN and read uses EWOULDBLOCK
253 : instead of EAGAIN, this will still just return EAGAIN. EAGAIN will
254 : only be returned if the underlying fd is non-blocking and dst_min is
255 : zero.
256 :
257 : TL;DR
258 :
259 : - dst_min is positive:
260 :
261 : ulong dst_sz; int err = fd_io_read( fd, dst, dst_min, dst_max, &dst_sz );
262 : if ( FD_LIKELY( err==0 ) ) ... success, dst_sz in [dst_min,dst_max], dst updated
263 : else if( FD_LIKELY( err< 0 ) ) ... EOF, dst_sz in [0,dst_min), dst updated
264 : else ... I/O error, dst_sz is zero, dst clobbered
265 : ... err is strerror compat, err is neither EAGAIN nor EWOULDBLOCK
266 : ... fd should be considered failed
267 :
268 : This is equivalent to looping over reads of up to dst_max in size
269 : from fd until at least dst_min bytes are read. It does not matter
270 : if fd is blocking or not.
271 :
272 : - dst_min is zero and fd is blocking:
273 :
274 : ulong dst_sz; int err = fd_io_read( fd, dst, dst_min, dst_max, &dst_sz );
275 : if ( FD_LIKELY( err==0 ) ) ... success, dst_sz in [1,dst_max], dst updated
276 : else if( FD_LIKELY( err< 0 ) ) ... EOF, dst_sz is zero, dst unchanged
277 : else ... I/O error, dst_sz is zero, dst clobbered
278 : ... err is strerror compat, err is neither EAGAIN nor EWOULDBLOCK
279 : ... fd should be considered failed
280 :
281 : This is equivalent to a single read of dst_max size on a blocking
282 : fd.
283 :
284 : - dst_min is zero and fd is non-blocking:
285 :
286 : ulong dst_sz; int err = fd_io_read( fd, dst, dst_min, dst_max, &dst_sz );
287 : if ( FD_LIKELY( err==0 ) ) ... success, dst_sz in [1,dst_max], dst updated
288 : else if( FD_LIKELY( err==EAGAIN ) ) ... no data available now, try again later, dst_sz is zero, dst unchanged
289 : else if( FD_LIKELY( err< 0 ) ) ... EOF, dst_sz is zero, dst unchanged
290 : else ... I/O error, dst_sz is zero, dst clobbered
291 : ... err is strerror compat, err is neither EAGAIN nor EWOULDBLOCK
292 : ... fd should be considered failed
293 :
294 : This is equivalent to a single read of dst_max size on a
295 : non-blocking fd (with the POSIX glitches around EAGAIN /
296 : EWOULDBLOCK cleaned up). */
297 :
298 : int
299 : fd_io_read( int fd,
300 : void * dst,
301 : ulong dst_min,
302 : ulong dst_max,
303 : ulong * _dst_sz );
304 :
305 : /* fd_io_write behaves virtually identical to fd_io_read but the
306 : direction of the transfer is from memory to the stream and there is
307 : no notion of EOF handling. Assumes src is non-NULL,
308 : src_min<=src_max, src_sz is non-NULL and non-overlapping with src.
309 : If src_max is 0, is a no-op. Summarizing:
310 :
311 : - src_min is positive:
312 :
313 : ulong src_sz; int err = fd_io_write( fd, src, src_min, src_max, &src_sz );
314 : if( FD_LIKELY( err==0 ) ) ... success, src_sz in [src_min,src_max]
315 : else ... I/O error, src_sz is zero
316 : ... err is strerror compat, err is neither EAGAIN nor EWOULDBLOCK
317 : ... fd should be considered failed
318 :
319 : This is equivalent to looping over writes of up to src_max in size
320 : to fd until at least src_min bytes are written. It does not matter
321 : if fd is blocking or not.
322 :
323 : - src_min is zero and fd is blocking:
324 :
325 : ulong src_sz; int err = fd_io_write( fd, src, src_min, src_max, &src_sz );
326 : if( FD_LIKELY( err==0 ) ) ... success, src_sz in [1,src_max]
327 : else ... I/O error, src_sz is zero
328 : ... err is strerror compat, err is neither EAGAIN nor EWOULDBLOCK
329 : ... fd should be considered failed
330 :
331 : This is equivalent to a single write of src_max size on a blocking
332 : fd.
333 :
334 : - src_min is zero and fd is non-blocking:
335 :
336 : ulong src_sz; int err = fd_io_write( fd, src, src_min, src_max, &src_sz );
337 : if ( FD_LIKELY( err==0 ) ) ... success, src_sz in [1,src_max]
338 : else if( FD_LIKELY( err==EAGAIN ) ) ... no bytes written, try again later, src_sz is zero
339 : else ... I/O error, src_sz is zero
340 : ... err is strerror compat, err is neither EAGAIN nor EWOULDBLOCK
341 : ... fd should be considered failed
342 :
343 : This is equivalent to a single write of src_max size on a
344 : non-blocking fd (with the POSIX glitches around EAGAIN /
345 : EWOULDBLOCK fixed up). */
346 :
347 : int
348 : fd_io_write( int fd,
349 : void const * src,
350 : ulong src_min,
351 : ulong src_max,
352 : ulong * _src_sz );
353 :
354 : /* fd_io_sz returns the current byte size of the file underlying the
355 : given file descriptor. Note that writing to a file descriptor may
356 : increase the file size. On success, returns 0 and *_sz will contain
357 : the current size (in [0,LONG_MAX], not ULONG_MAX for fd_io_seek
358 : friendliness). On failure, returns a strerror compatible error code
359 : and *_sz will be 0. Reasons for failure include the usual fstat
360 : reasons. */
361 :
362 : int
363 : fd_io_sz( int fd,
364 : ulong * _sz );
365 :
366 : /* fd_io_truncate truncates the file underlying the given file descriptor
367 : to be sz bytes long. If sz is larger than the current size, the file
368 : will be zero padded to sz. If smaller, the trailing size bytes will
369 : be discarded. On success, returns 0. On failure, returns a strerror
370 : compatible error code. Reasons for failure include sz is too large /
371 : incompatible with ftruncate and the usual ftruncate reasons.
372 : Truncating a file to a size that is smaller than the file offset of
373 : any open file descriptor (file system wide) on that file has
374 : undefined behavior. (POSIX has some conventions here but portable
375 : code should not rely on them.) */
376 :
377 : int
378 : fd_io_truncate( int fd,
379 : ulong sz );
380 :
381 : /* fd_io_seek seeks the byte index of the given file descriptor
382 : according to rel_off and type. Seeking to indices outside [0,sz]
383 : (i.e. before the SOF / first byte of the file and strictly after EOF
384 : / one past the last byte of the file) is not supported (POSIX has
385 : defined behaviors but portable code should not assume them).
386 :
387 : type==FD_IO_SEEK_TYPE_SET: the index will be seeked rel_off bytes
388 : from SOF / the first byte of the file. Supported rel_off are in
389 : [0,sz].
390 :
391 : type==FD_IO_SEEK_TYPE_CUR: the index will be seeked rel_off bytes
392 : from the current byte index. Supported rel_off are in
393 : [-idx,sz-idx].
394 :
395 : type==FD_IO_SEEK_TYPE_END: the index will be seeked rel_off bytes
396 : from EOF / one past the last byte of the file. Supported rel_off
397 : are in [-sz,0].
398 :
399 : On success, returns 0 and *_idx will contain the new byte index (will
400 : be in [0,sz] for supported rel_off and in [0,LONG_MAX] generally). On
401 : failure, returns a strerror compatible error code and *_idx will be 0.
402 : Reasons for failure include unsupported type, rel_off is not lseek
403 : compatible, and the usual lseek reasons (e.g. fd is not seekable ...
404 : a pipe / socket / etc).
405 :
406 : Note that, if the file descriptor supports it:
407 :
408 : fd_io_seek( fd, 0L, FD_IO_SEEK_TYPE_CUR, &idx );
409 :
410 : will return fd's current byte index (in [0,sz]) in idx. */
411 :
412 : int
413 : fd_io_seek( int fd,
414 : long rel_off,
415 : int type,
416 : ulong * _idx );
417 :
418 : /* fd_io_buffered_read is like fd_io_read but can consolidate many
419 : tiny reads into a larger fd_io_read via the given buffer. Unlike
420 : fd_io_read, dst NULL is okay if dst_sz is 0 (dst_sz 0 is a no-op that
421 : immediately returns success). Will block the caller until the read
422 : is complete or an end-of-file was encountered.
423 :
424 : rbuf points to the first byte of a rbuf_sz size memory region in the
425 : caller's address space used for read buffering (assumes rbuf is
426 : non-NULL with arbitrary alignment and rbuf_sz is positive). On entry
427 : *_rbuf_lo is where the first byte of unconsumed buffered reads are
428 : located and *_rbuf_ready is number of unconsumed buffered bytes.
429 : Assumes 0<=*_rbuf_lo<=(*_rbuf_lo+*_rbuf_ready)<=rbuf_sz.
430 :
431 : Returns 0 on success. dst will hold dst_sz bytes from the stream.
432 : *_rbuf_lo and *_rbuf_ready will be updated and the above invariant on
433 : *_rbuf_lo and *_rbuf_ready will still hold.
434 :
435 : Returns non-zero on failure. Failure indicates that dst_sz bytes
436 : could not be read because of an I/O error (return will be a positive
437 : errno compatible error code) or an end-of-file was encountered
438 : (return will be a negative number). dst should be assumed to have
439 : been clobbered, *_rbuf_lo and *_rbuf_ready will be zero. If an I/O
440 : error, the fd should be considered to be in a failed state such that
441 : the only valid operation on it is to close it.
442 :
443 : IMPORTANT! This function will only read from fd in multiples of
444 : rbuf_sz (except for a potentially last incomplete block before an
445 : end-of-file). This can be useful for various ultra high performance
446 : contexts.
447 :
448 : This API usually should not be used directly. It is mostly useful
449 : for implementing higher level APIs like fd_io_buffered_istream below. */
450 :
451 : int
452 : fd_io_buffered_read( int fd,
453 : void * dst,
454 : ulong dst_sz,
455 : void * rbuf,
456 : ulong rbuf_sz,
457 : ulong * _rbuf_lo,
458 : ulong * _rbuf_ready );
459 :
460 : /* fd_io_buffered_skip is like fd_io_buffered_read but will skip over
461 : skip_sz bytes of the stream without copying them into a user buffer.
462 : If stream fd is seekable (e.g. a normal file), this should be O(1).
463 : If not (e.g. fd is pipe / socket / stdin / etc), this will block the
464 : caller until skip_sz bytes have been skipped or an I/O error occurs.
465 :
466 : IMPORTANT! If stream fd is seekable, POSIX behaviors allow seeking
467 : past end-of-file (apparently even if fd is read only). Whether or
468 : not this is a good idea is debatable. The result though is this API
469 : will usually not return an error if skip_sz moves past the
470 : end-of-file (however, if skip_sz is so large that it causes the file
471 : offset to overflow, this will return EOVERFLOW). In particular, this
472 : API cannot be used to detect end-of-file.
473 :
474 : IMPORTANT! This function makes no effort to skip in multiples of
475 : rbuf_sz. Such is up to the caller to do if such is desirable.
476 :
477 : This API usually should not be used directly. It is mostly useful
478 : for implementing higher level APIs like fd_io_buffered_istream below. */
479 :
480 : int
481 : fd_io_buffered_skip( int fd,
482 : ulong skip_sz,
483 : void * rbuf,
484 : ulong rbuf_sz,
485 : ulong * _rbuf_lo,
486 : ulong * _rbuf_ready );
487 :
488 : /* fd_io_buffered_write is like fd_io_write but can consolidate many
489 : tiny writes into a larger fd_io_write via the given buffer. Unlike
490 : fd_io_write, src NULL is okay if src_sz is 0 (src_sz 0 is a no-op
491 : that immediately returns success). Will block the caller until the
492 : write is complete or an end-of-file was encountered.
493 :
494 : wbuf points to the first byte of a wbuf_sz size memory region in the
495 : caller's address space (assumes wbuf is non-NULL with arbitrary
496 : alignment and wbuf_sz is positive). On entry *_wbuf_used is the
497 : number of bytes in wbuf from previous buffered writes that have not
498 : yet been streamed out. Assumes *_wbuf_used is in [0,wbuf_sz].
499 :
500 : Returns 0 on success. wbuf will hold *_wbuf_used bytes not yet
501 : written to fd by write and/or previous buffered writes. The above
502 : invariant on *_wbuf_used will still hold.
503 :
504 : Returns non-zero on failure. Failure indicates that src_sz bytes
505 : could not be written because of an I/O error (return will be a
506 : positive errno compatible error code). fd should be considered to be
507 : in a failed state such that the only valid operation on it is to
508 : close it. *_wbuf_used will be 0 and the contents of wbuf will be
509 : undefined. Zero or more bytes of previously buffered writes and/or
510 : src might have been written before the failure.
511 :
512 : IMPORTANT! This function will only write to fd in multiples of
513 : wbuf_sz. This can be useful for various ultra high performance
514 : contexts.
515 :
516 : This API usually should not be used directly. It is mostly useful
517 : for implementing higher level APIs like fd_io_buffered_ostream below. */
518 :
519 : int
520 : fd_io_buffered_write( int fd,
521 : void const * src,
522 : ulong src_sz,
523 : void * wbuf,
524 : ulong wbuf_sz,
525 : ulong * _wbuf_used );
526 :
527 : /* fd_io_buffered_istream_init initializes in to do buffered reads from
528 : the given file descriptor. in is an unused location that should hold
529 : the buffering state, fd is an open normal-ish file descriptor, rbuf
530 : points to the first byte in the caller's address space to an unused
531 : rbuf_sz size memory region to use for read buffering (assumes rbuf is
532 : non-NULL with arbitrary alignment and rbuf_sz is positive). Returns
533 : in and on return in will be initialized. in will have ownership of
534 : fd and rbuf while initialized. */
535 :
536 : static inline fd_io_buffered_istream_t *
537 : fd_io_buffered_istream_init( fd_io_buffered_istream_t * in,
538 : int fd,
539 : void * rbuf,
540 45 : ulong rbuf_sz ) {
541 45 : in->fd = fd;
542 45 : in->rbuf = (uchar *)rbuf;
543 45 : in->rbuf_sz = rbuf_sz;
544 45 : in->rbuf_lo = 0UL;
545 45 : in->rbuf_ready = 0UL;
546 45 : return in;
547 45 : }
548 :
549 : /* fd_io_buffered_istream_{fd,rbuf,rbuf_sz} return the corresponding
550 : value used to initialize in. Assumes in is initialized. */
551 :
552 3 : FD_FN_PURE static inline int fd_io_buffered_istream_fd ( fd_io_buffered_istream_t const * in ) { return in->fd; }
553 3 : FD_FN_PURE static inline void * fd_io_buffered_istream_rbuf ( fd_io_buffered_istream_t const * in ) { return in->rbuf; }
554 3 : FD_FN_PURE static inline ulong fd_io_buffered_istream_rbuf_sz( fd_io_buffered_istream_t const * in ) { return in->rbuf_sz; }
555 :
556 : /* fd_io_buffered_istream_fini finalizes a buffered input stream.
557 : Assumes in is initialized. On return in will no longer be
558 : initialized and ownership the underlying fd and rbuf will return to
559 : the caller.
560 :
561 : IMPORTANT! THIS WILL NOT REPOSITION THE UNDERLYING FD FILE OFFSET
562 : (SUCH MIGHT NOT EVEN BE POSSIBLE) TO "UNREAD" ANY UNCONSUMED BUFFERED
563 : DATA. */
564 :
565 : static inline void
566 45 : fd_io_buffered_istream_fini( fd_io_buffered_istream_t * in ) {
567 45 : (void)in;
568 45 : }
569 :
570 : /* fd_io_buffered_istream_read reads dst_sz bytes from in to dst,
571 : reading ahead as convenient. Assumes in is initialized. dst /
572 : dst_sz have the same meaning / restrictions as fd_io_buffered_read.
573 : Returns 0 on success and non-zero on failure. Failure interpretation
574 : is the same as fd_io_buffered_read. On failure, in and the
575 : underlying file descriptor should be considered to be in a failed
576 : state (e.g. the only valid thing to do to in is fini and the only
577 : valid thing to do to fd is close).
578 :
579 : IMPORTANT! If fd_io_buffered_istream_{fetch,skip} below are never
580 : used (or only used to skip in multiplies of rbuf_sz), all the reads
581 : from the underlying stream will always be at multiples of rbuf_sz
582 : from the file offset when the in was initialized and a multiple of
583 : rbuf_sz in size (except possibly a final read to the end-of-file).
584 : This can be beneficial in various high performance I/O regimes. */
585 :
586 : FD_FN_UNUSED static int /* Work around -Winline */
587 : fd_io_buffered_istream_read( fd_io_buffered_istream_t * in,
588 : void * dst,
589 1767 : ulong dst_sz ) {
590 : /* We destructure in to avoid pointer escapes that might inhibit
591 : optimizations of other in inlines. */
592 1767 : ulong rbuf_lo = in->rbuf_lo;
593 1767 : ulong rbuf_ready = in->rbuf_ready;
594 1767 : int err = fd_io_buffered_read( in->fd, dst, dst_sz, in->rbuf, in->rbuf_sz, &rbuf_lo, &rbuf_ready );
595 1767 : in->rbuf_lo = rbuf_lo;
596 1767 : in->rbuf_ready = rbuf_ready;
597 1767 : return err;
598 1767 : }
599 :
600 : /* fd_io_buffered_istream_skip skips skip_sz bytes from in. Assumes in
601 : is initialized. Returns 0 on success and non-zero on failure.
602 : Failure interpretation is the same as fd_io_buffered_read. On a
603 : failure, in and the underlying file descriptor should be considered
604 : to be in a failed state (e.g. the only valid thing to do to in is
605 : fini and the only valid thing to do to fd is close).
606 :
607 : If the fd underlying in is seekable (e.g. a file), this will be very
608 : fast. If not (e.g. fd is pipe / socket / etc), this can block the
609 : caller until skip_sz bytes have arrived or an I/O error is detected.
610 :
611 : IMPORTANT! See note in fd_io_buffered_istream_read above about the
612 : impact of this on file pointer alignment. */
613 :
614 : static inline int
615 : fd_io_buffered_istream_skip( fd_io_buffered_istream_t * in,
616 1242 : ulong skip_sz ) {
617 : /* We destructure in to avoid pointer escapes that might inhibit
618 : optimizations of other in inlines. */
619 1242 : ulong rbuf_lo = in->rbuf_lo;
620 1242 : ulong rbuf_ready = in->rbuf_ready;
621 1242 : int err = fd_io_buffered_skip( in->fd, skip_sz, in->rbuf, in->rbuf_sz, &rbuf_lo, &rbuf_ready );
622 1242 : in->rbuf_lo = rbuf_lo;
623 1242 : in->rbuf_ready = rbuf_ready;
624 1242 : return err;
625 1242 : }
626 :
627 : /* fd_io_buffered_istream_peek returns a pointer in the caller's address
628 : space to the first byte that has been read but not yet consumed.
629 : Assumes in is initialized. The returned pointer can have arbitrary
630 : alignment and the returned pointer lifetime is until the next read,
631 : fetch, or fini. */
632 :
633 : FD_FN_PURE static inline void const *
634 7653 : fd_io_buffered_istream_peek( fd_io_buffered_istream_t * in ) {
635 7653 : return in->rbuf + in->rbuf_lo;
636 7653 : }
637 :
638 : /* fd_io_buffered_istream_peek_sz returns the number of bytes that have
639 : been read but not yet consumed. Assumes in is initialized. Returned
640 : value will be in [0,rbuf_sz] and will be valid until the next read,
641 : fetch, seek or fini. */
642 :
643 : FD_FN_PURE static inline ulong
644 11100 : fd_io_buffered_istream_peek_sz( fd_io_buffered_istream_t * in ) {
645 11100 : return in->rbuf_ready;
646 11100 : }
647 :
648 : /* fd_io_buffered_istream_seek consumes sz buffered bytes from in.
649 : Assumes in is initialized and that sz is at most peek_sz. */
650 :
651 : static inline void
652 : fd_io_buffered_istream_seek( fd_io_buffered_istream_t * in,
653 7653 : ulong sz ) {
654 7653 : in->rbuf_lo += sz;
655 7653 : in->rbuf_ready -= sz;
656 7653 : }
657 :
658 : /* fd_io_buffered_istream_fetch tries to fill up the stream's read
659 : buffer with as many unconsumed bytes as possible. Assumes in is
660 : initialized. Returns 0 on success (rbuf is filled to rbuf_sz with
661 : unconsumed data) and non-zero on failure (see below for
662 : interpretation). On failure, in and the underlying file descriptor
663 : should be considered to be in a failed state (e.g. the only valid
664 : thing to do out on is fini and the only valid thing to do on fd is
665 : close). That is:
666 :
667 : int err = fd_io_buffered_istream_fetch( in );
668 : if( FD_LIKELY( err==0 ) ) ... success, peek_sz() updated to at most rbuf_sz
669 : else if( FD_LIKELY( err< 0 ) ) ... end-of-file, peek_sz() updated to at most rbuf_sz and is num unconsumed bytes to EOF
670 : else if( FD_LIKELY( err==EAGAIN ) ) ... try again, peek_sz() unchanged, only possible if fd is non-blocking
671 : else ... I/O error, peek_sz() unchanged,
672 : ... err is strerror compat, err is neither EAGAIN nor EWOULDBLOCK
673 : ... in and fd should be considered failed
674 :
675 : IMPORTANT! See note in fd_io_buffered_istream_read above about the
676 : impact of fetch on file pointer alignment. */
677 :
678 : FD_FN_UNUSED static int /* Work around -Winline */
679 3456 : fd_io_buffered_istream_fetch( fd_io_buffered_istream_t * in ) {
680 3456 : uchar * rbuf = in->rbuf;
681 3456 : ulong rbuf_sz = in->rbuf_sz;
682 3456 : ulong rbuf_lo = in->rbuf_lo;
683 3456 : ulong rbuf_ready = in->rbuf_ready;
684 3456 : if( FD_UNLIKELY( rbuf_ready>=rbuf_sz ) ) return 0; /* buffer already full */
685 3453 : if( FD_LIKELY( (!!rbuf_ready) & (!!rbuf_lo) ) ) memmove( rbuf, rbuf+rbuf_lo, rbuf_ready ); /* Move unconsumed to beginning */
686 3453 : ulong rsz;
687 3453 : int err = fd_io_read( in->fd, rbuf+rbuf_ready, 0UL, rbuf_sz-rbuf_ready, &rsz );
688 3453 : in->rbuf_lo = 0UL;
689 3453 : in->rbuf_ready = rbuf_ready + rsz;
690 3453 : return err;
691 3456 : }
692 :
693 : /* fd_io_buffered_ostream_init initializes out to do buffered writes to
694 : the given file descriptor. out is an unused location that should
695 : hold the stream state, fd is an open normal-ish file descriptor to
696 : buffer, wbuf points to the first byte in the caller's address space
697 : to an unused wbuf_sz size memory region to use for the buffering
698 : (assumes wbuf is non-NULL with arbitrary alignment and wbuf_sz is
699 : positive). Returns out and on return out will be initialized. On
700 : return out will have ownership of fd and wbuf. */
701 :
702 : static inline fd_io_buffered_ostream_t *
703 : fd_io_buffered_ostream_init( fd_io_buffered_ostream_t * out,
704 : int fd,
705 : void * wbuf,
706 12 : ulong wbuf_sz ) {
707 12 : out->fd = fd;
708 12 : out->wbuf = (uchar *)wbuf;
709 12 : out->wbuf_sz = wbuf_sz;
710 12 : out->wbuf_used = 0UL;
711 12 : return out;
712 12 : }
713 :
714 : /* fd_io_buffered_ostream_{fd,wbuf,wbuf_sz} return the corresponding
715 : value used to initialize out. Assumes out is initialized. */
716 :
717 3 : FD_FN_PURE static inline int fd_io_buffered_ostream_fd ( fd_io_buffered_ostream_t const * out ) { return out->fd; }
718 3 : FD_FN_PURE static inline void * fd_io_buffered_ostream_wbuf ( fd_io_buffered_ostream_t const * out ) { return out->wbuf; }
719 3 : FD_FN_PURE static inline ulong fd_io_buffered_ostream_wbuf_sz( fd_io_buffered_ostream_t const * out ) { return out->wbuf_sz; }
720 :
721 : /* fd_io_buffered_ostream_fini finalizes a buffered output stream.
722 : Assumes out is initialized. On return out will no longer be
723 : initialized and the caller will have ownership of the underlying fd
724 : and wbuf.
725 :
726 : IMPORTANT! THIS WILL NOT DO ANY FINAL FLUSH OF BUFFERED BYTES. IT
727 : IS THE CALLER'S RESPONSIBILITY TO DO THIS IN THE NORMAL FINI CASE. */
728 :
729 : static inline void
730 12 : fd_io_buffered_ostream_fini( fd_io_buffered_ostream_t * out ) {
731 12 : (void)out;
732 12 : }
733 :
734 : /* fd_io_buffered_ostream_write writes src_sz bytes from src to the
735 : stream, temporarily buffering zero or more bytes as convenient.
736 : Assume out is initialized. src / src_sz have the same meaning /
737 : restrictions as fd_io_buffered_write. Returns 0 on success and
738 : non-zero on failure. Failure interpretation is the same as
739 : fd_io_buffered_write. On failure, out and the underlying file
740 : descriptor should be considered to be in a failed state (e.g. the
741 : only valid thing to do to out is fini and the only valid thing to do
742 : to fd is close).
743 :
744 : IMPORTANT! If fd_io_buffered_ostream_flush is only used to do a
745 : final flush before fini, all the writes to the underlying stream will
746 : always be at multiples of wbuf_sz offset from the initial file offset
747 : when the out was initialized and all the write sizes (except
748 : potentially the final flush) will be a multiple of wbuf_sz in size.
749 : This can be beneficial in various high performance I/O regimes. */
750 :
751 : static inline int
752 : fd_io_buffered_ostream_write( fd_io_buffered_ostream_t * out,
753 : void const * src,
754 561 : ulong src_sz ) {
755 : /* We destructure out to avoid pointer escapes that might inhibit
756 : optimizations of other inlines that operate on out. */
757 561 : ulong wsz = out->wbuf_used;
758 561 : int err = fd_io_buffered_write( out->fd, src, src_sz, out->wbuf, out->wbuf_sz, &wsz );
759 561 : out->wbuf_used = wsz;
760 561 : return err;
761 561 : }
762 :
763 : /* fd_io_buffered_ostream_peek returns a pointer in the caller's address
764 : space where the caller can prepare bytes to be streamed out. Assumes
765 : out is initialized. The returned pointer can have arbitrary
766 : alignment and the returned pointer lifetime is until the next write,
767 : flush, or fini. */
768 :
769 : FD_FN_PURE static inline void *
770 3471 : fd_io_buffered_ostream_peek( fd_io_buffered_ostream_t * out ) {
771 3471 : return out->wbuf + out->wbuf_used;
772 3471 : }
773 :
774 : /* fd_io_buffered_istream_peek_sz returns the number of bytes available
775 : at the peek location. Assumes out is initialized. Returned value
776 : will be in [0,wbuf_sz] and will be valid until the next write, fetch,
777 : seek or fini. */
778 :
779 : FD_FN_PURE static inline ulong
780 5184 : fd_io_buffered_ostream_peek_sz( fd_io_buffered_ostream_t * out ) {
781 5184 : return out->wbuf_sz - out->wbuf_used;
782 5184 : }
783 :
784 : /* fd_io_buffered_istream_seek commits the next sz unused write buffer
785 : bytes to be streamed out. Assumes out is initialized and that sz is
786 : at most peek_sz. */
787 :
788 : static inline void
789 : fd_io_buffered_ostream_seek( fd_io_buffered_ostream_t * out,
790 3444 : ulong sz ) {
791 3444 : out->wbuf_used += sz;
792 3444 : }
793 :
794 : /* fd_io_buffered_ostream_flush writes any buffered bytes in the stream's
795 : write buffer to the underlying file descriptor. Assume out is
796 : initialized. Returns 0 on success (all buffered bytes written to fd)
797 : and non-zero on failure (see below for interpretation). In both
798 : cases, the write buffer will be empty on return. On failure, out and
799 : the underlying file descriptor should be considered to be in a failed
800 : state (e.g. the only valid thing to do to out is fini and the only
801 : valid thing to do to fd is close).
802 :
803 : int err = fd_io_buffered_ostream_flush( out );
804 : if( FD_LIKELY( err==0 ) ) ... success, write buffer empty
805 : else ... I/O error, write buffer empty
806 : ... err is strerror compat, err is neither EAGAIN nor EWOULDBLOCK
807 : ... in and fd should be considered failed
808 :
809 : IMPORTANT! See note in fd_io_buffered_ostream_write below about the
810 : impact of doing this outside a final flush. */
811 :
812 : FD_FN_UNUSED static int /* Work around -Winline */
813 1779 : fd_io_buffered_ostream_flush( fd_io_buffered_ostream_t * out ) {
814 1779 : ulong wbuf_used = out->wbuf_used;
815 1779 : if( FD_UNLIKELY( !wbuf_used ) ) return 0; /* optimize for lots of tiny writes */
816 1779 : out->wbuf_used = 0UL;
817 1779 : ulong wsz;
818 1779 : return fd_io_write( out->fd, out->wbuf, wbuf_used, wbuf_used, &wsz );
819 1779 : }
820 :
821 : /* Memory mapped I/O APIs */
822 :
823 : /* fd_io_mmio_init starts memory mapped I/O on the file underlying
824 : the given file descriptor. Specifically, it maps the file into the
825 : caller's address space according to the given FD_IO_MMIO_MODE.
826 :
827 : On success, returns 0. On return, *(caller_mmio_t *)_mmio will point
828 : to the first byte in the caller's address space where the file was
829 : mapped (will be aligned at least 4096 currently) and *_mmio_sz will
830 : contain the region's byte size (which is the same as the file's
831 : size). The mapping's lifetime will be until the corresponding fini
832 : or the process/thread group terminates (normally or not). If the
833 : file has zero size, the region will be NULL and non-NULL otherwise.
834 : The usual POSIX semantics for fd and the underlying file apply. In
835 : particular, fd can be closed and/or the file deleted during memory
836 : mapped I/O without issue. Retains no interest in mmio or mmio_sz.
837 :
838 : On failure, returns a non-zero strerror compatible error code. On
839 : return, the region will be an NULL with zero length. Reasons for
840 : failure include all usual the fd_io_sz reasons and mmap reasons (e.g.
841 : fd is a file descriptor of a memory mappable file). Retains no
842 : interest in mmio or mmio_sz.
843 :
844 : IMPORTANT SAFETY TIP! Changes to the file via the region are not
845 : guaranteed visible to others until the corresponding fini.
846 : Conversely, changes by others to the file during memory mapped I/O
847 : may not become visible during memory mapped I/O. Lastly, concurrent
848 : memory mapped I/O to the same file (whether via the same file
849 : descriptor or not and/or within the same thread group or not) is
850 : supported currently. E.g. a slow but portable init/fini
851 : implementation under the hood could, on init, allocate a suitably
852 : aligned memory region, read the entire file into that region and
853 : return that region and, on fini, write the entire region back to the
854 : file (if applicable) and free it while an optimized implementation
855 : could use target specific virtual memory APIs to eliminate excess
856 : alloc/free/read/write operations. */
857 :
858 : int
859 : fd_io_mmio_init( int fd,
860 : int mode,
861 : void * _mmio, /* Psychologically, a "caller_mmio_t **" */
862 : ulong * _mmio_sz );
863 :
864 : /* fd_io_mmio_fini finishes memory mapped I/O on a file. mmio and
865 : mmio_sz should be region where the memory mapped I/O is in progress.
866 : Memory mapped I/O will not be in progress on return. Guaranteed not
867 : to fail from the caller's point of view. See fd_shmem_mmio_init for
868 : more details. */
869 :
870 : void
871 : fd_io_mmio_fini( void const * mmio,
872 : ulong mmio_sz );
873 :
874 : /* TODO: consider a fd_io_mmio_sync API to allow changes to an
875 : in-progress mmio to be made visible? */
876 :
877 : /* Misc APIs */
878 :
879 : /* fd_io_strerror converts an fd_io error code (i.e. negative ->
880 : end-of-file, 0 -> success, positive -> strerror compatible) into a
881 : human readable cstr. Unlike strerror, the lifetime of the returned
882 : pointer is infinite and the call itself is thread safe. The
883 : returned pointer is always to a non-NULL cstr. */
884 :
885 : FD_FN_CONST char const *
886 : fd_io_strerror( int err );
887 :
888 : /* fd_io_strsignal converts a signal code (like returned by WTERMSIG)
889 : into a human readable cstr. Unlike strsignal, the lifetime of the
890 : returned pointer is infinite and the call itself is thread safe.
891 : Unlike the glibc strsignal implementation in particular, it does
892 : not call `brk(3)` or `futex(2)` internally. The returned pointer
893 : is always to a non-NULL cstr. */
894 : FD_FN_CONST char const *
895 : fd_io_strsignal( int err );
896 :
897 : /* TODO: ASYNC IO APIS */
898 :
899 : FD_PROTOTYPES_END
900 :
901 : #endif /* HEADER_fd_src_util_io_fd_io_h */
|