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/new.h"
12#include "src/__support/CPP/span.h"
13#include "src/errno/libc_errno.h" // For error macros
14
15#include <stdio.h>
16#include <stdlib.h>
17
18namespace LIBC_NAMESPACE {
19
20FileIOResult File::write_unlocked(const void *data, size_t len) {
21 if (!write_allowed()) {
22 err = true;
23 return {0, EBADF};
24 }
25
26 prev_op = FileOp::WRITE;
27
28 if (bufmode == _IONBF) { // unbuffered.
29 size_t ret_val =
30 write_unlocked_nbf(data: static_cast<const uint8_t *>(data), len);
31 flush_unlocked();
32 return ret_val;
33 } else if (bufmode == _IOFBF) { // fully buffered
34 return write_unlocked_fbf(data: static_cast<const uint8_t *>(data), len);
35 } else /*if (bufmode == _IOLBF) */ { // line buffered
36 return write_unlocked_lbf(data: static_cast<const uint8_t *>(data), len);
37 }
38}
39
40FileIOResult File::write_unlocked_nbf(const uint8_t *data, size_t len) {
41 if (pos > 0) { // If the buffer is not empty
42 // Flush the buffer
43 const size_t write_size = pos;
44 auto write_result = platform_write(this, buf, write_size);
45 pos = 0; // Buffer is now empty so reset pos to the beginning.
46 // If less bytes were written than expected, then an error occurred.
47 if (write_result < write_size) {
48 err = true;
49 // No bytes from data were written, so return 0.
50 return {0, write_result.error};
51 }
52 }
53
54 auto write_result = platform_write(this, data, len);
55 if (write_result < len)
56 err = true;
57 return write_result;
58}
59
60FileIOResult File::write_unlocked_fbf(const uint8_t *data, size_t len) {
61 const size_t init_pos = pos;
62 const size_t bufspace = bufsize - pos;
63
64 // If data is too large to be buffered at all, then just write it unbuffered.
65 if (len > bufspace + bufsize)
66 return write_unlocked_nbf(data, len);
67
68 // we split |data| (conceptually) using the split point. Then we handle the
69 // two pieces separately.
70 const size_t split_point = len < bufspace ? len : bufspace;
71
72 // The primary piece is the piece of |data| we want to write to the buffer
73 // before flushing. It will always fit into the buffer, since the split point
74 // is defined as being min(len, bufspace), and it will always exist if len is
75 // non-zero.
76 cpp::span<const uint8_t> primary(data, split_point);
77
78 // The second piece is the remainder of |data|. It is written to the buffer if
79 // it fits, or written directly to the output if it doesn't. If the primary
80 // piece fits entirely in the buffer, the remainder may be nothing.
81 cpp::span<const uint8_t> remainder(
82 static_cast<const uint8_t *>(data) + split_point, len - split_point);
83
84 cpp::span<uint8_t> bufref(static_cast<uint8_t *>(buf), bufsize);
85
86 // Copy the first piece into the buffer.
87 // TODO: Replace the for loop below with a call to internal memcpy.
88 for (size_t i = 0; i < primary.size(); ++i)
89 bufref[pos + i] = primary[i];
90 pos += primary.size();
91
92 // If there is no remainder, we can return early, since the first piece has
93 // fit completely into the buffer.
94 if (remainder.size() == 0)
95 return len;
96
97 // We need to flush the buffer now, since there is still data and the buffer
98 // is full.
99 const size_t write_size = pos;
100
101 auto buf_result = platform_write(this, buf, write_size);
102 size_t bytes_written = buf_result.value;
103
104 pos = 0; // Buffer is now empty so reset pos to the beginning.
105 // If less bytes were written than expected, then an error occurred. Return
106 // the number of bytes that have been written from |data|.
107 if (buf_result.has_error() || bytes_written < write_size) {
108 err = true;
109 return {bytes_written <= init_pos ? 0 : bytes_written - init_pos,
110 buf_result.error};
111 }
112
113 // The second piece is handled basically the same as the first, although we
114 // know that if the second piece has data in it then the buffer has been
115 // flushed, meaning that pos is always 0.
116 if (remainder.size() < bufsize) {
117 // TODO: Replace the for loop below with a call to internal memcpy.
118 for (size_t i = 0; i < remainder.size(); ++i)
119 bufref[i] = remainder[i];
120 pos = remainder.size();
121 } else {
122
123 auto result = platform_write(this, remainder.data(), remainder.size());
124 size_t bytes_written = buf_result.value;
125
126 // If less bytes were written than expected, then an error occurred. Return
127 // the number of bytes that have been written from |data|.
128 if (result.has_error() || bytes_written < remainder.size()) {
129 err = true;
130 return {primary.size() + bytes_written, result.error};
131 }
132 }
133
134 return len;
135}
136
137FileIOResult File::write_unlocked_lbf(const uint8_t *data, size_t len) {
138 constexpr uint8_t NEWLINE_CHAR = '\n';
139 size_t last_newline = len;
140 for (size_t i = len; i >= 1; --i) {
141 if (data[i - 1] == NEWLINE_CHAR) {
142 last_newline = i - 1;
143 break;
144 }
145 }
146
147 // If there is no newline, treat this as fully buffered.
148 if (last_newline == len) {
149 return write_unlocked_fbf(data, len);
150 }
151
152 // we split |data| (conceptually) using the split point. Then we handle the
153 // two pieces separately.
154 const size_t split_point = last_newline + 1;
155
156 // The primary piece is everything in |data| up to the newline. It's written
157 // unbuffered to the output.
158 cpp::span<const uint8_t> primary(data, split_point);
159
160 // The second piece is the remainder of |data|. It is written fully buffered,
161 // meaning it may stay in the buffer if it fits.
162 cpp::span<const uint8_t> remainder(
163 static_cast<const uint8_t *>(data) + split_point, len - split_point);
164
165 size_t written = 0;
166
167 written = write_unlocked_nbf(data: primary.data(), len: primary.size());
168 if (written < primary.size()) {
169 err = true;
170 return written;
171 }
172
173 flush_unlocked();
174
175 written += write_unlocked_fbf(data: remainder.data(), len: remainder.size());
176 if (written < len) {
177 err = true;
178 return written;
179 }
180
181 return len;
182}
183
184FileIOResult File::read_unlocked(void *data, size_t len) {
185 if (!read_allowed()) {
186 err = true;
187 return {0, EBADF};
188 }
189
190 prev_op = FileOp::READ;
191
192 cpp::span<uint8_t> bufref(static_cast<uint8_t *>(buf), bufsize);
193 cpp::span<uint8_t> dataref(static_cast<uint8_t *>(data), len);
194
195 // Because read_limit is always greater than equal to pos,
196 // available_data is never a wrapped around value.
197 size_t available_data = read_limit - pos;
198 if (len <= available_data) {
199 // TODO: Replace the for loop below with a call to internal memcpy.
200 for (size_t i = 0; i < len; ++i)
201 dataref[i] = bufref[i + pos];
202 pos += len;
203 return len;
204 }
205
206 // Copy all of the available data.
207 // TODO: Replace the for loop with a call to internal memcpy.
208 for (size_t i = 0; i < available_data; ++i)
209 dataref[i] = bufref[i + pos];
210 read_limit = pos = 0; // Reset the pointers.
211 // Update the dataref to reflect that fact that we have already
212 // copied |available_data| into |data|.
213 dataref = cpp::span<uint8_t>(dataref.data() + available_data,
214 dataref.size() - available_data);
215
216 size_t to_fetch = len - available_data;
217 if (to_fetch > bufsize) {
218 auto result = platform_read(this, dataref.data(), to_fetch);
219 size_t fetched_size = result.value;
220 if (result.has_error() || fetched_size < to_fetch) {
221 if (!result.has_error())
222 eof = true;
223 else
224 err = true;
225 return {available_data + fetched_size, result.has_error()};
226 }
227 return len;
228 }
229
230 // Fetch and buffer another buffer worth of data.
231 auto result = platform_read(this, buf, bufsize);
232 size_t fetched_size = result.value;
233 read_limit += fetched_size;
234 size_t transfer_size = fetched_size >= to_fetch ? to_fetch : fetched_size;
235 for (size_t i = 0; i < transfer_size; ++i)
236 dataref[i] = bufref[i];
237 pos += transfer_size;
238 if (result.has_error() || fetched_size < to_fetch) {
239 if (!result.has_error())
240 eof = true;
241 else
242 err = true;
243 }
244 return {transfer_size + available_data, result.error};
245}
246
247int File::ungetc_unlocked(int c) {
248 // There is no meaning to unget if:
249 // 1. You are trying to push back EOF.
250 // 2. Read operations are not allowed on this file.
251 // 3. The previous operation was a write operation.
252 if (c == EOF || !read_allowed() || (prev_op == FileOp::WRITE))
253 return EOF;
254
255 cpp::span<uint8_t> bufref(static_cast<uint8_t *>(buf), bufsize);
256 if (read_limit == 0) {
257 // If |read_limit| is zero, it can mean three things:
258 // a. This file was just created.
259 // b. The previous operation was a seek operation.
260 // c. The previous operation was a read operation which emptied
261 // the buffer.
262 // For all the above cases, we simply write |c| at the beginning
263 // of the buffer and bump |read_limit|. Note that |pos| will also
264 // be zero in this case, so we don't need to adjust it.
265 bufref[0] = static_cast<unsigned char>(c);
266 ++read_limit;
267 } else {
268 // If |read_limit| is non-zero, it means that there is data in the buffer
269 // from a previous read operation. Which would also mean that |pos| is not
270 // zero. So, we decrement |pos| and write |c| in to the buffer at the new
271 // |pos|. If too many ungetc operations are performed without reads, it
272 // can lead to (pos == 0 but read_limit != 0). We will just error out in
273 // such a case.
274 if (pos == 0)
275 return EOF;
276 --pos;
277 bufref[pos] = static_cast<unsigned char>(c);
278 }
279
280 eof = false; // There is atleast one character that can be read now.
281 err = false; // This operation was a success.
282 return c;
283}
284
285ErrorOr<int> File::seek(long offset, int whence) {
286 FileLock lock(this);
287 if (prev_op == FileOp::WRITE && pos > 0) {
288
289 auto buf_result = platform_write(this, buf, pos);
290 if (buf_result.has_error() || buf_result.value < pos) {
291 err = true;
292 return Error(buf_result.error);
293 }
294 } else if (prev_op == FileOp::READ && whence == SEEK_CUR) {
295 // More data could have been read out from the platform file than was
296 // required. So, we have to adjust the offset we pass to platform seek
297 // function. Note that read_limit >= pos is always true.
298 offset -= (read_limit - pos);
299 }
300 pos = read_limit = 0;
301 prev_op = FileOp::SEEK;
302 // Reset the eof flag as a seek might move the file positon to some place
303 // readable.
304 eof = false;
305 auto result = platform_seek(this, offset, whence);
306 if (!result.has_value())
307 return Error(result.error());
308 else
309 return 0;
310}
311
312ErrorOr<long> File::tell() {
313 FileLock lock(this);
314 auto seek_target = eof ? SEEK_END : SEEK_CUR;
315 auto result = platform_seek(this, 0, seek_target);
316 if (!result.has_value() || result.value() < 0)
317 return Error(result.error());
318 long platform_offset = result.value();
319 if (prev_op == FileOp::READ)
320 return platform_offset - (read_limit - pos);
321 else if (prev_op == FileOp::WRITE)
322 return platform_offset + pos;
323 else
324 return platform_offset;
325}
326
327int File::flush_unlocked() {
328 if (prev_op == FileOp::WRITE && pos > 0) {
329 auto buf_result = platform_write(this, buf, pos);
330 if (buf_result.has_error() || buf_result.value < pos) {
331 err = true;
332 return buf_result.error;
333 }
334 pos = 0;
335 }
336 // TODO: Add POSIX behavior for input streams.
337 return 0;
338}
339
340int File::set_buffer(void *buffer, size_t size, int buffer_mode) {
341 // We do not need to lock the file as this method should be called before
342 // other operations are performed on the file.
343 if (buffer != nullptr && size == 0)
344 return EINVAL;
345
346 switch (buffer_mode) {
347 case _IOFBF:
348 case _IOLBF:
349 case _IONBF:
350 break;
351 default:
352 return EINVAL;
353 }
354
355 if (buffer == nullptr && size != 0 && buffer_mode != _IONBF) {
356 // We exclude the case of buffer_mode == _IONBF in this branch
357 // because we don't need to allocate buffer in such a case.
358 if (own_buf) {
359 // This is one of the places where use a C allocation functon
360 // as C++ does not have an equivalent of realloc.
361 buf = reinterpret_cast<uint8_t *>(realloc(ptr: buf, size: size));
362 if (buf == nullptr)
363 return ENOMEM;
364 } else {
365 AllocChecker ac;
366 buf = new (ac) uint8_t[size];
367 if (!ac)
368 return ENOMEM;
369 own_buf = true;
370 }
371 bufsize = size;
372 // TODO: Handle allocation failures.
373 } else {
374 if (own_buf)
375 delete buf;
376 if (buffer_mode != _IONBF) {
377 buf = static_cast<uint8_t *>(buffer);
378 bufsize = size;
379 } else {
380 // We don't need any buffer.
381 buf = nullptr;
382 bufsize = 0;
383 }
384 own_buf = false;
385 }
386 bufmode = buffer_mode;
387 adjust_buf();
388 return 0;
389}
390
391File::ModeFlags File::mode_flags(const char *mode) {
392 // First character in |mode| should be 'a', 'r' or 'w'.
393 if (*mode != 'a' && *mode != 'r' && *mode != 'w')
394 return 0;
395
396 // There should be exaclty one main mode ('a', 'r' or 'w') character.
397 // If there are more than one main mode characters listed, then
398 // we will consider |mode| as incorrect and return 0;
399 int main_mode_count = 0;
400
401 ModeFlags flags = 0;
402 for (; *mode != '\0'; ++mode) {
403 switch (*mode) {
404 case 'r':
405 flags |= static_cast<ModeFlags>(OpenMode::READ);
406 ++main_mode_count;
407 break;
408 case 'w':
409 flags |= static_cast<ModeFlags>(OpenMode::WRITE);
410 ++main_mode_count;
411 break;
412 case '+':
413 flags |= static_cast<ModeFlags>(OpenMode::PLUS);
414 break;
415 case 'b':
416 flags |= static_cast<ModeFlags>(ContentType::BINARY);
417 break;
418 case 'a':
419 flags |= static_cast<ModeFlags>(OpenMode::APPEND);
420 ++main_mode_count;
421 break;
422 case 'x':
423 flags |= static_cast<ModeFlags>(CreateType::EXCLUSIVE);
424 break;
425 default:
426 return 0;
427 }
428 }
429
430 if (main_mode_count != 1)
431 return 0;
432
433 return flags;
434}
435
436} // namespace LIBC_NAMESPACE
437

source code of libc/src/__support/File/file.cpp