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 /* Read from fd filling the buffer. Called when io->data == io->end. */ 47 static inline int io__fill_buffer(struct io *io) 48 { 49 ssize_t n; 50 51 if (io->eof) 52 return -1; 53 54 if (io->timeout_ms != 0) { 55 struct pollfd pfds[] = { 56 { 57 .fd = io->fd, 58 .events = POLLIN, 59 }, 60 }; 61 62 n = poll(pfds, 1, io->timeout_ms); 63 if (n == 0) 64 errno = ETIMEDOUT; 65 if (n > 0 && !(pfds[0].revents & POLLIN)) { 66 errno = EIO; 67 n = -1; 68 } 69 if (n <= 0) { 70 io->eof = true; 71 return -1; 72 } 73 } 74 n = read(io->fd, io->buf, io->buf_len); 75 76 if (n <= 0) { 77 io->eof = true; 78 return -1; 79 } 80 io->data = &io->buf[0]; 81 io->end = &io->buf[n]; 82 return 0; 83 } 84 85 /* Reads one character from the "io" file with similar semantics to fgetc. */ 86 static inline int io__get_char(struct io *io) 87 { 88 if (io->data == io->end) { 89 int ret = io__fill_buffer(io); 90 91 if (ret) 92 return ret; 93 } 94 return *io->data++; 95 } 96 97 /* Read a hexadecimal value with no 0x prefix into the out argument hex. If the 98 * first character isn't hexadecimal returns -2, io->eof returns -1, otherwise 99 * returns the character after the hexadecimal value which may be -1 for eof. 100 * If the read value is larger than a u64 the high-order bits will be dropped. 101 */ 102 static inline int io__get_hex(struct io *io, __u64 *hex) 103 { 104 bool first_read = true; 105 106 *hex = 0; 107 while (true) { 108 int ch = io__get_char(io); 109 110 if (ch < 0) 111 return ch; 112 if (ch >= '0' && ch <= '9') 113 *hex = (*hex << 4) | (ch - '0'); 114 else if (ch >= 'a' && ch <= 'f') 115 *hex = (*hex << 4) | (ch - 'a' + 10); 116 else if (ch >= 'A' && ch <= 'F') 117 *hex = (*hex << 4) | (ch - 'A' + 10); 118 else if (first_read) 119 return -2; 120 else 121 return ch; 122 first_read = false; 123 } 124 } 125 126 /* Read a positive decimal value with out argument dec. If the first character 127 * isn't a decimal returns -2, io->eof returns -1, otherwise returns the 128 * character after the decimal value which may be -1 for eof. If the read value 129 * is larger than a u64 the high-order bits will be dropped. 130 */ 131 static inline int io__get_dec(struct io *io, __u64 *dec) 132 { 133 bool first_read = true; 134 135 *dec = 0; 136 while (true) { 137 int ch = io__get_char(io); 138 139 if (ch < 0) 140 return ch; 141 if (ch >= '0' && ch <= '9') 142 *dec = (*dec * 10) + ch - '0'; 143 else if (first_read) 144 return -2; 145 else 146 return ch; 147 first_read = false; 148 } 149 } 150 151 /* Read up to and including the first delim. */ 152 static inline ssize_t io__getdelim(struct io *io, char **line_out, size_t *line_len_out, int delim) 153 { 154 char buf[128]; 155 int buf_pos = 0; 156 char *line = NULL, *temp; 157 size_t line_len = 0; 158 int ch = 0; 159 160 /* TODO: reuse previously allocated memory. */ 161 free(*line_out); 162 while (ch != delim) { 163 ch = io__get_char(io); 164 165 if (ch < 0) 166 break; 167 168 if (buf_pos == sizeof(buf)) { 169 temp = realloc(line, line_len + sizeof(buf)); 170 if (!temp) 171 goto err_out; 172 line = temp; 173 memcpy(&line[line_len], buf, sizeof(buf)); 174 line_len += sizeof(buf); 175 buf_pos = 0; 176 } 177 buf[buf_pos++] = (char)ch; 178 } 179 temp = realloc(line, line_len + buf_pos + 1); 180 if (!temp) 181 goto err_out; 182 line = temp; 183 memcpy(&line[line_len], buf, buf_pos); 184 line[line_len + buf_pos] = '\0'; 185 line_len += buf_pos; 186 *line_out = line; 187 *line_len_out = line_len; 188 return line_len; 189 err_out: 190 free(line); 191 *line_out = NULL; 192 return -ENOMEM; 193 } 194 195 static inline ssize_t io__getline(struct io *io, char **line_out, size_t *line_len_out) 196 { 197 return io__getdelim(io, line_out, line_len_out, /*delim=*/'\n'); 198 } 199 200 #endif /* __API_IO__ */ 201