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