201 lines · c
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 17struct 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 34static 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. */47static 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. */86static 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 the98 * first character isn't hexadecimal returns -2, io->eof returns -1, otherwise99 * 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 */102static 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 else121 return ch;122 first_read = false;123 }124}125 126/* Read a positive decimal value with out argument dec. If the first character127 * isn't a decimal returns -2, io->eof returns -1, otherwise returns the128 * character after the decimal value which may be -1 for eof. If the read value129 * is larger than a u64 the high-order bits will be dropped.130 */131static 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 else146 return ch;147 first_read = false;148 }149}150 151/* Read up to and including the first delim. */152static 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;189err_out:190 free(line);191 *line_out = NULL;192 return -ENOMEM;193}194 195static 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