File: io.c

package info (click to toggle)
bar 1.11.1-3
  • links: PTS, VCS
  • area: main
  • in suites: bullseye, buster, sid
  • size: 928 kB
  • sloc: ansic: 4,381; sh: 3,334; makefile: 264
file content (345 lines) | stat: -rw-r--r-- 8,764 bytes parent folder | download | duplicates (3)
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
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
#include "config.h"

#include "headers.h"
#include "error.h"
#include "fd.h"
#include "io.h"

/** I/O state data. */
IO io;

/** Initialize I/O state data.
 * \return 0 for success.  (This function does not fail.)
 */
int ioInit(void)
{
	/** Initlalize the input file path to null. */
	io.in_path = 0;
	/** Initialize the input file descriptor to standard input. */
	io.in = STDIN_FILENO;
	/** Initialize the output file descriptor to standard output. */
	io.out = STDOUT_FILENO;
	/** Set the initial end-of-file flags to false (or zero). */
	io.eof_in = 0;
	io.eof_out = 0;
	/** Initialize the ring buffer. */
	io.buffer_used = 0;
	io.buffer_head = 0;
	io.buffer_size = DEFAULT_BUFFER_SIZE;
	io.buffer = 0;
	/** Initialize the byte-counters. */
	io.last_read = 0;
	io.last_write = 0;
	io.total_size = 0;
	io.total_size_known = 0;
	io.total_read = 0;
	io.total_write = 0;
	io.continue_size = 0;
	/** Initialize the I/O timeout to 1 millisecond. */
	io.timeout = 1; /* 1/1000000 second */
	/** Initialize the current time and the throttle counts. */
	io.current_time = time(0);
	io.throttle_count = 1;
	io.throttle = MAX_UINT64;
	/** Initialize the default block size to 512 bytes. */
	io.block_size = 512; /* 0.5k default */
	return(0);
}

#ifdef HAVE_SIGNAL_H
/** A signal handler for control-C.
 *
 * \param signo The signal number.
 */
static void sig_int(int signo)
{
	/** Return I/O streams to a default state before exiting. */
	fdEnd(io.in);
	fdEnd(io.out);
	exit(1);
}
#endif

#ifdef HAVE_SYSCONF
#	if HAVE_DECL__SC_PAGE_SIZE == 1
#		define PAGESIZE _SC_PAGE_SIZE
#	else
#		if HAVE_DECL__SC_PAGESIZE == 1
#			define PAGESIZE _SC_PAGESIZE
#		endif
#	endif
#	ifndef PAGESIZE
#		warning I dont know how to retrieve the size of a page using sysconf.
#		warning Assuming page size is DEFAULT_PAGE_SIZE bytes.
#		undef HAVE_SYSCONF
#	endif
#endif

/** Prepare I/O
 *
 * \return 0 for success, non-zero for failure.
 */
int ioBegin(void)
{
	/** If memalign() or posix_memalign() is available, then allocate memory on
	 * a page boundary.  If sysconf() is available, find out what the page size
	 * is for this machine, otherwise assume that a page is 8192 bytes.
	 */
#if defined(USE_MEMALIGN) \
	&& (defined(HAVE_POSIX_MEMALIGN) || defined(HAVE_MEMALIGN))
	long page_size = 0;

#	ifdef HAVE_SYSCONF
	page_size = sysconf(PAGESIZE);
#	else
	page_size = DEFAULT_PAGE_SIZE;
#	endif
#endif

	/** Allocate memory for the ring buffer. */
#undef USED_MEMALIGN
#ifdef USE_MEMALIGN
#	if defined(HAVE_POSIX_MEMALIGN)
#		define USED_MEMALIGN
	if (
		/** Incorporating patch provided by Doncho N. Gunchev
		 * Closes bug: [ 1158420 ] glibc detects invalid free on exit (Fedora Core 3)
		 * */
		posix_memalign((void*)&io.buffer, page_size, sizeof(char) * io.buffer_size) != 0
		)
	{
		print_error(stderr, "Memory allocation failed");
		return(1);
	}
/*
#	elif defined(HAVE_MEMALIGN)
#		if defined(USE_MEMALIGN_BOUNDARY_SIZE)
#			define USED_MEMALIGN
			io.buffer = (char *)memalign(page_size, sizeof(char) * io.buffer_size);
#		elif defined(USE_MEMALIGN_SIZE_BOUNDARY)
#			define USED_MEMALIGN
			io.buffer = (char *)memalign(sizeof(char) * io.buffer_size, page_size);
#		endif
*/
#	endif
#endif
#ifndef USED_MEMALIGN
	io.buffer = (char *)malloc(sizeof(char) * io.buffer_size);
#endif
	if (io.buffer == 0) {
		print_error(stderr, "Memory allocation failed");
		return(1);
	}
	/** If we have signal(), then set up a signal handler to catch control-C's.
	 */
#ifdef HAVE_SIGNAL_H
	if (signal(SIGINT, sig_int) == SIG_ERR) {
		print_error(stderr, "Could not install SIGINT signal handler");
		print_error(stderr, "Control-C events will not reset non-blocking I/O");
	}
#endif
	/** Prepare the I/O file descriptors. */
	fdBegin(io.in);
	fdBegin(io.out);
	return(0);
}

/** Shut down I/O
 *
 * \return 0 for success.  (This function does not fail.)
 */
int ioEnd(void)
{
	/** Free the I/O buffer. */
	free(io.buffer);
	io.buffer = 0;
	io.buffer_used = 0;
	/** Return the file descriptors to their previous state. */
	fdEnd(io.in);
	fdEnd(io.out);
	return(0);
}

/** Check to see if I/O is ready. */
void ioCheck(void)
{
	static fd_set rset, wset;
	static struct timeval to;
	
	to.tv_sec = 0;
	/** Wait for up to io.timeout milliseconds for a change in I/O state. */
	to.tv_usec = io.timeout;
	io.in_ready = 1;
	io.out_ready = 1;
	FD_ZERO(&rset);
	FD_ZERO(&wset);
	if (io.buffer_used != io.buffer_size) FD_SET(io.in, &rset);
	if (io.buffer_used) FD_SET(io.out, &wset);
	/** Check to see if I/O is ready. */
	if (select( ((io.in<io.out)?io.out:io.in)+1, 
		&rset, &wset, 0, &to) == -1) {
		if (errno != EINTR) {
			print_error(stderr, "select() failed");
			return;
		}
	}
	/** Set the I/O ready flags in I/O state data. */
	if (!FD_ISSET(io.in, &rset))
		io.in_ready = 0;
	if (!FD_ISSET(io.out, &wset))
		io.out_ready = 0;
}

/** Read input
 *
 * \return 0 for success, non-zero for failure.
 */
int ioRead(void)
{
	int num_read = 0;
	int end = 0;
	int avail = 0;
#ifdef USE_IOVEC
	int num = 0;
	struct iovec vec[2] = { { 0 } };
#endif

	/** If we've reached out throttle count for this second, return. */
	if ((io.current_time == time(0))
		&& (io.throttle_count >= io.throttle)) {
		return(0);
	}
	if (io.current_time != time(0)) {
		io.throttle_count = 0;
		io.current_time = time(0);
	}
	/** If there is nothing to read yet, return. */
	if (!io.in_ready) return(0);
	io.last_read = 0;
	/** If we've reached the end of input, return. */
	if (io.eof_in) return(0);
	/** If the buffer is full, return. */
	if (io.buffer_used == io.buffer_size) return(0);
	/** Calculate the location and size of the free space in the buffer. */
	end = (io.buffer_used + io.buffer_head) % io.buffer_size;
	avail = io.buffer_size - io.buffer_used;
	/** If the free space in the buffer is greater than the number of bytes left
	 * to be read in this second, throttle the input.
	 */
	if ((io.throttle - io.throttle_count) < avail)
		avail = io.throttle - io.throttle_count;
#ifdef USE_IOVEC
	/** If using iovec I/O, set up vec[] with the proper information and then
	 * read using iovec I/O.
	 */
	vec[num  ].iov_base = io.buffer + end;
	if (end+avail <= io.buffer_size)
		vec[num++].iov_len  = avail;
	else {
		vec[num++].iov_len  = io.buffer_size - end;
		vec[num  ].iov_base = io.buffer;
		vec[num++].iov_len  = avail - (io.buffer_size - end);
	}
	num_read = readv(io.in, vec, num);
#else
	/** If not using iovec, then read input into the next available chunk of
	 * linear buffer space.
	 */
	if (end+avail <= io.buffer_size) {
		num_read = read(io.in,
			io.buffer + end,
			avail);
	}
	else {
		num_read = read(io.in,
			io.buffer + end,
			io.buffer_size - end);
	}
#endif
	/** Check for end of file. */
	if (num_read == 0) {
		io.eof_in = 1;
		return(0);
	}
	/** Check for an error. */
	if ((num_read < 0) && (errno == EAGAIN))
		num_read = 0;
	if (num_read < 0) return(num_read);
	/** Update I/O byte counters. */
	io.buffer_used += num_read;
	io.last_read = num_read;
	io.total_read += num_read;
	io.throttle_count += num_read;
	return(0);
}

/** Write data
 *
 * \return 0 for success, non-zero for failure.
 */
int ioWrite(void)
{
	int num_written = 0;
#ifdef USE_IOVEC
	int num = 0;
	struct iovec vec[2] = { { 0 } };
#endif

	/** If output is not ready, return. */
	if (!io.out_ready) return(0);
	io.last_write = 0;
#ifdef USE_IOVEC
	/** If using iovec I/O, set up vec[] with the correct information and then
	 * write.
	 */
	vec[num  ].iov_base = io.buffer + io.buffer_head;
	if (io.buffer_head + io.buffer_used <= io.buffer_size)
		vec[num++].iov_len  = io.buffer_used;
	else {
		vec[num++].iov_len  = io.buffer_size - io.buffer_head;
		vec[num  ].iov_base = io.buffer;
		vec[num++].iov_len  = io.buffer_used - (io.buffer_size - io.buffer_head);
	}
	num_written = writev(io.out, vec, num);
#else
	/** If not using iovec, then write the next chunk of linear data from the
	 * buffer.
	 */
	if (io.buffer_head + io.buffer_used <= io.buffer_size) {
		num_written = write(io.out,
			io.buffer + io.buffer_head,
			io.buffer_used);
	}
	else {
		num_written = write(io.out,
			io.buffer + io.buffer_head,
			io.buffer_size - io.buffer_head);
	}
#endif
	/** Check for end of file. */
	if (num_written == 0) {
		io.eof_out = 1;
		return(0);
	}
	/** Check for an error. */
	if ((num_written < 0) && (errno == EAGAIN))
		num_written = 0;
	if (num_written < 0) return(num_written);
	/** Update the I/O byte counters. */
	io.buffer_used -= num_written;
	io.buffer_head = (io.buffer_head + num_written) % io.buffer_size;
	io.last_write = num_written;
	io.total_write += num_written;
	return(0);
}

/** Check to see if we're done copying input to output
 *
 * \return 1 if done, 0 otherwise.
 */
int ioIsDone(void)
{
	if ((io.eof_in && (io.buffer_used == 0)) || io.eof_out) return(1);
	return(0);
}