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