Linux kernel mirror (for testing)
git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel
os
linux
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
15struct 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
30static 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. */
42static 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 */
68static 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 */
97static 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. */
118static 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;
155err_out:
156 free(line);
157 return -ENOMEM;
158}
159
160#endif /* __API_IO__ */