xref: /linux/tools/lib/api/io.h (revision 2363088eba2ecccfb643725e4864af73c4226a04)
1 /* SPDX-License-Identifier: GPL-2.0 */
2 /*
3  * Lightweight buffered reading library.
4  *
5  * Copyright 2019 Google LLC.
6  */
7 #ifndef __API_IO__
8 #define __API_IO__
9 
10 #include <errno.h>
11 #include <poll.h>
12 #include <stdlib.h>
13 #include <string.h>
14 #include <unistd.h>
15 
16 struct io {
17 	/* File descriptor being read/ */
18 	int fd;
19 	/* Size of the read buffer. */
20 	unsigned int buf_len;
21 	/* Pointer to storage for buffering read. */
22 	char *buf;
23 	/* End of the storage. */
24 	char *end;
25 	/* Currently accessed data pointer. */
26 	char *data;
27 	/* Read timeout, 0 implies no timeout. */
28 	int timeout_ms;
29 	/* Set true on when the end of file on read error. */
30 	bool eof;
31 };
32 
33 static inline void io__init(struct io *io, int fd,
34 			    char *buf, unsigned int buf_len)
35 {
36 	io->fd = fd;
37 	io->buf_len = buf_len;
38 	io->buf = buf;
39 	io->end = buf;
40 	io->data = buf;
41 	io->timeout_ms = 0;
42 	io->eof = false;
43 }
44 
45 /* Reads one character from the "io" file with similar semantics to fgetc. */
46 static inline int io__get_char(struct io *io)
47 {
48 	char *ptr = io->data;
49 
50 	if (io->eof)
51 		return -1;
52 
53 	if (ptr == io->end) {
54 		ssize_t n;
55 
56 		if (io->timeout_ms != 0) {
57 			struct pollfd pfds[] = {
58 				{
59 					.fd = io->fd,
60 					.events = POLLIN,
61 				},
62 			};
63 
64 			n = poll(pfds, 1, io->timeout_ms);
65 			if (n == 0)
66 				errno = ETIMEDOUT;
67 			if (n > 0 && !(pfds[0].revents & POLLIN)) {
68 				errno = EIO;
69 				n = -1;
70 			}
71 			if (n <= 0) {
72 				io->eof = true;
73 				return -1;
74 			}
75 		}
76 		n = read(io->fd, io->buf, io->buf_len);
77 
78 		if (n <= 0) {
79 			io->eof = true;
80 			return -1;
81 		}
82 		ptr = &io->buf[0];
83 		io->end = &io->buf[n];
84 	}
85 	io->data = ptr + 1;
86 	return *ptr;
87 }
88 
89 /* Read a hexadecimal value with no 0x prefix into the out argument hex. If the
90  * first character isn't hexadecimal returns -2, io->eof returns -1, otherwise
91  * returns the character after the hexadecimal value which may be -1 for eof.
92  * If the read value is larger than a u64 the high-order bits will be dropped.
93  */
94 static inline int io__get_hex(struct io *io, __u64 *hex)
95 {
96 	bool first_read = true;
97 
98 	*hex = 0;
99 	while (true) {
100 		int ch = io__get_char(io);
101 
102 		if (ch < 0)
103 			return ch;
104 		if (ch >= '0' && ch <= '9')
105 			*hex = (*hex << 4) | (ch - '0');
106 		else if (ch >= 'a' && ch <= 'f')
107 			*hex = (*hex << 4) | (ch - 'a' + 10);
108 		else if (ch >= 'A' && ch <= 'F')
109 			*hex = (*hex << 4) | (ch - 'A' + 10);
110 		else if (first_read)
111 			return -2;
112 		else
113 			return ch;
114 		first_read = false;
115 	}
116 }
117 
118 /* Read a positive decimal value with out argument dec. If the first character
119  * isn't a decimal returns -2, io->eof returns -1, otherwise returns the
120  * character after the decimal value which may be -1 for eof. If the read value
121  * is larger than a u64 the high-order bits will be dropped.
122  */
123 static inline int io__get_dec(struct io *io, __u64 *dec)
124 {
125 	bool first_read = true;
126 
127 	*dec = 0;
128 	while (true) {
129 		int ch = io__get_char(io);
130 
131 		if (ch < 0)
132 			return ch;
133 		if (ch >= '0' && ch <= '9')
134 			*dec = (*dec * 10) + ch - '0';
135 		else if (first_read)
136 			return -2;
137 		else
138 			return ch;
139 		first_read = false;
140 	}
141 }
142 
143 /* Read up to and including the first newline following the pattern of getline. */
144 static inline ssize_t io__getline(struct io *io, char **line_out, size_t *line_len_out)
145 {
146 	char buf[128];
147 	int buf_pos = 0;
148 	char *line = NULL, *temp;
149 	size_t line_len = 0;
150 	int ch = 0;
151 
152 	/* TODO: reuse previously allocated memory. */
153 	free(*line_out);
154 	while (ch != '\n') {
155 		ch = io__get_char(io);
156 
157 		if (ch < 0)
158 			break;
159 
160 		if (buf_pos == sizeof(buf)) {
161 			temp = realloc(line, line_len + sizeof(buf));
162 			if (!temp)
163 				goto err_out;
164 			line = temp;
165 			memcpy(&line[line_len], buf, sizeof(buf));
166 			line_len += sizeof(buf);
167 			buf_pos = 0;
168 		}
169 		buf[buf_pos++] = (char)ch;
170 	}
171 	temp = realloc(line, line_len + buf_pos + 1);
172 	if (!temp)
173 		goto err_out;
174 	line = temp;
175 	memcpy(&line[line_len], buf, buf_pos);
176 	line[line_len + buf_pos] = '\0';
177 	line_len += buf_pos;
178 	*line_out = line;
179 	*line_len_out = line_len;
180 	return line_len;
181 err_out:
182 	free(line);
183 	return -ENOMEM;
184 }
185 
186 #endif /* __API_IO__ */
187