1 //===--- Implementation of a platform independent file data structure -----===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8
9 #include "file.h"
10
11 #include "src/__support/CPP/ArrayRef.h"
12
13 #include <errno.h>
14 #include <stdio.h>
15 #include <stdlib.h>
16
17 namespace __llvm_libc {
18
write_unlocked(const void * data,size_t len)19 size_t File::write_unlocked(const void *data, size_t len) {
20 if (!write_allowed()) {
21 errno = EBADF;
22 err = true;
23 return 0;
24 }
25
26 prev_op = FileOp::WRITE;
27
28 if (bufmode == _IOFBF) { // fully buffered
29 return write_unlocked_fbf(data, len);
30 } else if (bufmode == _IOLBF) { // line buffered
31 return write_unlocked_lbf(data, len);
32 } else /*if (bufmode == _IONBF) */ { // unbuffered
33 size_t ret_val = write_unlocked_nbf(data, len);
34 flush_unlocked();
35 return ret_val;
36 }
37 }
38
write_unlocked_nbf(const void * data,size_t len)39 size_t File::write_unlocked_nbf(const void *data, size_t len) {
40 if (pos > 0) { // If the buffer is not empty
41 // Flush the buffer
42 const size_t write_size = pos;
43 size_t bytes_written = platform_write(this, buf, write_size);
44 pos = 0; // Buffer is now empty so reset pos to the beginning.
45 // If less bytes were written than expected, then an error occurred.
46 if (bytes_written < write_size) {
47 err = true;
48 return 0; // No bytes from data were written, so return 0.
49 }
50 }
51
52 size_t written = platform_write(this, data, len);
53 if (written < len)
54 err = true;
55 return written;
56 }
57
write_unlocked_fbf(const void * data,size_t len)58 size_t File::write_unlocked_fbf(const void *data, size_t len) {
59 const size_t init_pos = pos;
60 const size_t bufspace = bufsize - pos;
61
62 // If data is too large to be buffered at all, then just write it unbuffered.
63 if (len > bufspace + bufsize)
64 return write_unlocked_nbf(data, len);
65
66 // we split |data| (conceptually) using the split point. Then we handle the
67 // two pieces separately.
68 const size_t split_point = len < bufspace ? len : bufspace;
69
70 // The primary piece is the piece of |data| we want to write to the buffer
71 // before flushing. It will always fit into the buffer, since the split point
72 // is defined as being min(len, bufspace), and it will always exist if len is
73 // non-zero.
74 cpp::ArrayRef<uint8_t> primary(data, split_point);
75
76 // The second piece is the remainder of |data|. It is written to the buffer if
77 // it fits, or written directly to the output if it doesn't. If the primary
78 // piece fits entirely in the buffer, the remainder may be nothing.
79 cpp::ArrayRef<uint8_t> remainder(
80 static_cast<const uint8_t *>(data) + split_point, len - split_point);
81
82 cpp::MutableArrayRef<uint8_t> bufref(buf, bufsize);
83
84 // Copy the first piece into the buffer.
85 // TODO: Replace the for loop below with a call to internal memcpy.
86 for (size_t i = 0; i < primary.size(); ++i)
87 bufref[pos + i] = primary[i];
88 pos += primary.size();
89
90 // If there is no remainder, we can return early, since the first piece has
91 // fit completely into the buffer.
92 if (remainder.size() == 0)
93 return len;
94
95 // We need to flush the buffer now, since there is still data and the buffer
96 // is full.
97 const size_t write_size = pos;
98 size_t bytes_written = platform_write(this, buf, write_size);
99 pos = 0; // Buffer is now empty so reset pos to the beginning.
100 // If less bytes were written than expected, then an error occurred. Return
101 // the number of bytes that have been written from |data|.
102 if (bytes_written < write_size) {
103 err = true;
104 return bytes_written <= init_pos ? 0 : bytes_written - init_pos;
105 }
106
107 // The second piece is handled basically the same as the first, although we
108 // know that if the second piece has data in it then the buffer has been
109 // flushed, meaning that pos is always 0.
110 if (remainder.size() < bufsize) {
111 // TODO: Replace the for loop below with a call to internal memcpy.
112 for (size_t i = 0; i < remainder.size(); ++i)
113 bufref[i] = remainder[i];
114 pos = remainder.size();
115 } else {
116 size_t bytes_written =
117 platform_write(this, remainder.data(), remainder.size());
118
119 // If less bytes were written than expected, then an error occurred. Return
120 // the number of bytes that have been written from |data|.
121 if (bytes_written < remainder.size()) {
122 err = true;
123 return primary.size() + bytes_written;
124 }
125 }
126
127 return len;
128 }
129
write_unlocked_lbf(const void * data,size_t len)130 size_t File::write_unlocked_lbf(const void *data, size_t len) {
131 constexpr char NEWLINE_CHAR = '\n';
132 size_t last_newline = len;
133 for (size_t i = len; i > 1; --i) {
134 if (static_cast<const char *>(data)[i - 1] == NEWLINE_CHAR) {
135 last_newline = i - 1;
136 break;
137 }
138 }
139
140 // If there is no newline, treat this as fully buffered.
141 if (last_newline == len) {
142 return write_unlocked_fbf(data, len);
143 }
144
145 // we split |data| (conceptually) using the split point. Then we handle the
146 // two pieces separately.
147 const size_t split_point = last_newline + 1;
148
149 // The primary piece is everything in |data| up to the newline. It's written
150 // unbuffered to the output.
151 cpp::ArrayRef<uint8_t> primary(data, split_point);
152
153 // The second piece is the remainder of |data|. It is written fully buffered,
154 // meaning it may stay in the buffer if it fits.
155 cpp::ArrayRef<uint8_t> remainder(
156 static_cast<const uint8_t *>(data) + split_point, len - split_point);
157
158 size_t written = 0;
159
160 written = write_unlocked_nbf(primary.data(), primary.size());
161 if (written < primary.size()) {
162 err = true;
163 return written;
164 }
165
166 flush_unlocked();
167
168 written += write_unlocked_fbf(remainder.data(), remainder.size());
169 if (written < len) {
170 err = true;
171 return written;
172 }
173
174 return len;
175 }
176
read_unlocked(void * data,size_t len)177 size_t File::read_unlocked(void *data, size_t len) {
178 if (!read_allowed()) {
179 errno = EBADF;
180 err = true;
181 return 0;
182 }
183
184 prev_op = FileOp::READ;
185
186 cpp::MutableArrayRef<uint8_t> bufref(buf, bufsize);
187 cpp::MutableArrayRef<uint8_t> dataref(data, len);
188
189 // Because read_limit is always greater than equal to pos,
190 // available_data is never a wrapped around value.
191 size_t available_data = read_limit - pos;
192 if (len <= available_data) {
193 // TODO: Replace the for loop below with a call to internal memcpy.
194 for (size_t i = 0; i < len; ++i)
195 dataref[i] = bufref[i + pos];
196 pos += len;
197 return len;
198 }
199
200 // Copy all of the available data.
201 // TODO: Replace the for loop with a call to internal memcpy.
202 for (size_t i = 0; i < available_data; ++i)
203 dataref[i] = bufref[i + pos];
204 read_limit = pos = 0; // Reset the pointers.
205
206 size_t to_fetch = len - available_data;
207 if (to_fetch > bufsize) {
208 size_t fetched_size = platform_read(this, data, to_fetch);
209 if (fetched_size < to_fetch) {
210 if (errno == 0)
211 eof = true;
212 else
213 err = true;
214 return available_data + fetched_size;
215 }
216 return len;
217 }
218
219 // Fetch and buffer another buffer worth of data.
220 size_t fetched_size = platform_read(this, buf, bufsize);
221 read_limit += fetched_size;
222 size_t transfer_size = fetched_size >= to_fetch ? to_fetch : fetched_size;
223 for (size_t i = 0; i < transfer_size; ++i)
224 dataref[i] = bufref[i];
225 pos += transfer_size;
226 if (fetched_size < to_fetch) {
227 if (errno == 0)
228 eof = true;
229 else
230 err = true;
231 }
232 return transfer_size + available_data;
233 }
234
seek(long offset,int whence)235 int File::seek(long offset, int whence) {
236 FileLock lock(this);
237 if (prev_op == FileOp::WRITE && pos > 0) {
238 size_t transferred_size = platform_write(this, buf, pos);
239 if (transferred_size < pos) {
240 err = true;
241 return -1;
242 }
243 } else if (prev_op == FileOp::READ && whence == SEEK_CUR) {
244 // More data could have been read out from the platform file than was
245 // required. So, we have to adjust the offset we pass to platform seek
246 // function. Note that read_limit >= pos is always true.
247 offset -= (read_limit - pos);
248 }
249 pos = read_limit = 0;
250 prev_op = FileOp::SEEK;
251 // Reset the eof flag as a seek might move the file positon to some place
252 // readable.
253 eof = false;
254 return platform_seek(this, offset, whence);
255 }
256
flush_unlocked()257 int File::flush_unlocked() {
258 if (prev_op == FileOp::WRITE && pos > 0) {
259 size_t transferred_size = platform_write(this, buf, pos);
260 if (transferred_size < pos) {
261 err = true;
262 return -1;
263 }
264 pos = 0;
265 return platform_flush(this);
266 }
267 // TODO: Add POSIX behavior for input streams.
268 return 0;
269 }
270
close()271 int File::close() {
272 {
273 FileLock lock(this);
274 if (prev_op == FileOp::WRITE && pos > 0) {
275 size_t transferred_size = platform_write(this, buf, pos);
276 if (transferred_size < pos) {
277 err = true;
278 return -1;
279 }
280 }
281 if (platform_close(this) != 0)
282 return -1;
283 if (own_buf)
284 free(buf);
285 }
286 free(this);
287 return 0;
288 }
289
set_buffer(void * buffer,size_t size,bool owned)290 void File::set_buffer(void *buffer, size_t size, bool owned) {
291 if (own_buf)
292 free(buf);
293 buf = buffer;
294 bufsize = size;
295 own_buf = owned;
296 }
297
mode_flags(const char * mode)298 File::ModeFlags File::mode_flags(const char *mode) {
299 // First character in |mode| should be 'a', 'r' or 'w'.
300 if (*mode != 'a' && *mode != 'r' && *mode != 'w')
301 return 0;
302
303 // There should be exaclty one main mode ('a', 'r' or 'w') character.
304 // If there are more than one main mode characters listed, then
305 // we will consider |mode| as incorrect and return 0;
306 int main_mode_count = 0;
307
308 ModeFlags flags = 0;
309 for (; *mode != '\0'; ++mode) {
310 switch (*mode) {
311 case 'r':
312 flags |= static_cast<ModeFlags>(OpenMode::READ);
313 ++main_mode_count;
314 break;
315 case 'w':
316 flags |= static_cast<ModeFlags>(OpenMode::WRITE);
317 ++main_mode_count;
318 break;
319 case '+':
320 flags |= static_cast<ModeFlags>(OpenMode::PLUS);
321 break;
322 case 'b':
323 flags |= static_cast<ModeFlags>(ContentType::BINARY);
324 break;
325 case 'a':
326 flags |= static_cast<ModeFlags>(OpenMode::APPEND);
327 ++main_mode_count;
328 break;
329 case 'x':
330 flags |= static_cast<ModeFlags>(CreateType::EXCLUSIVE);
331 break;
332 default:
333 return 0;
334 }
335 }
336
337 if (main_mode_count != 1)
338 return 0;
339
340 return flags;
341 }
342
343 } // namespace __llvm_libc
344