[NFC][Py Reformat] Added more commits to .git-blame-ignore-revs
[llvm-project.git] / libc / src / __support / File / file.cpp
blob6a80ce0c8fb8952eb9afd270872618157acdea3f
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 //===----------------------------------------------------------------------===//
9 #include "file.h"
11 #include "src/__support/CPP/new.h"
12 #include "src/__support/CPP/span.h"
13 #include "src/errno/libc_errno.h" // For error macros
15 #include <stdio.h>
16 #include <stdlib.h>
18 namespace __llvm_libc {
20 FileIOResult File::write_unlocked(const void *data, size_t len) {
21 if (!write_allowed()) {
22 err = true;
23 return {0, EBADF};
26 prev_op = FileOp::WRITE;
28 if (bufmode == _IOFBF) { // fully buffered
29 return write_unlocked_fbf(static_cast<const uint8_t *>(data), len);
30 } else if (bufmode == _IOLBF) { // line buffered
31 return write_unlocked_lbf(static_cast<const uint8_t *>(data), len);
32 } else /*if (bufmode == _IONBF) */ { // unbuffered
33 size_t ret_val =
34 write_unlocked_nbf(static_cast<const uint8_t *>(data), len);
35 flush_unlocked();
36 return ret_val;
40 FileIOResult 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};
54 auto write_result = platform_write(this, data, len);
55 if (write_result < len)
56 err = true;
57 return write_result;
60 FileIOResult 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;
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);
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;
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);
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);
84 cpp::span<uint8_t> bufref(static_cast<uint8_t *>(buf), bufsize);
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();
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;
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;
101 auto buf_result = platform_write(this, buf, write_size);
102 size_t bytes_written = buf_result.value;
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};
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 {
123 auto result = platform_write(this, remainder.data(), remainder.size());
124 size_t bytes_written = buf_result.value;
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};
134 return len;
137 FileIOResult 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;
147 // If there is no newline, treat this as fully buffered.
148 if (last_newline == len) {
149 return write_unlocked_fbf(data, len);
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;
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);
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);
165 size_t written = 0;
167 written = write_unlocked_nbf(primary.data(), primary.size());
168 if (written < primary.size()) {
169 err = true;
170 return written;
173 flush_unlocked();
175 written += write_unlocked_fbf(remainder.data(), remainder.size());
176 if (written < len) {
177 err = true;
178 return written;
181 return len;
184 FileIOResult File::read_unlocked(void *data, size_t len) {
185 if (!read_allowed()) {
186 err = true;
187 return {0, EBADF};
190 prev_op = FileOp::READ;
192 cpp::span<uint8_t> bufref(static_cast<uint8_t *>(buf), bufsize);
193 cpp::span<uint8_t> dataref(static_cast<uint8_t *>(data), len);
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;
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);
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()};
227 return len;
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;
244 return {transfer_size + available_data, result.error};
247 int 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;
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);
280 eof = false; // There is atleast one character that can be read now.
281 err = false; // This operation was a success.
282 return c;
285 ErrorOr<int> File::seek(long offset, int whence) {
286 FileLock lock(this);
287 if (prev_op == FileOp::WRITE && pos > 0) {
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);
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);
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;
312 ErrorOr<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;
327 int 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;
334 pos = 0;
335 return platform_flush(this);
337 // TODO: Add POSIX behavior for input streams.
338 return 0;
341 int File::set_buffer(void *buffer, size_t size, int buffer_mode) {
342 // We do not need to lock the file as this method should be called before
343 // other operations are performed on the file.
345 if (buffer != nullptr && size == 0)
346 return EINVAL;
348 switch (buffer_mode) {
349 case _IOFBF:
350 case _IOLBF:
351 case _IONBF:
352 break;
353 default:
354 return EINVAL;
357 if (buffer == nullptr && size != 0 && buffer_mode != _IONBF) {
358 // We exclude the case of buffer_mode == _IONBF in this branch
359 // because we don't need to allocate buffer in such a case.
360 if (own_buf) {
361 // This is one of the places where use a C allocation functon
362 // as C++ does not have an equivalent of realloc.
363 buf = reinterpret_cast<uint8_t *>(realloc(buf, size));
364 if (buf == nullptr)
365 return ENOMEM;
366 } else {
367 AllocChecker ac;
368 buf = new (ac) uint8_t[size];
369 if (!ac)
370 return ENOMEM;
371 own_buf = true;
373 bufsize = size;
374 // TODO: Handle allocation failures.
375 } else {
376 if (own_buf)
377 delete buf;
378 if (buffer_mode != _IONBF) {
379 buf = static_cast<uint8_t *>(buffer);
380 bufsize = size;
381 } else {
382 // We don't need any buffer.
383 buf = nullptr;
384 bufsize = 0;
386 own_buf = false;
388 bufmode = buffer_mode;
389 adjust_buf();
390 return 0;
393 File::ModeFlags File::mode_flags(const char *mode) {
394 // First character in |mode| should be 'a', 'r' or 'w'.
395 if (*mode != 'a' && *mode != 'r' && *mode != 'w')
396 return 0;
398 // There should be exaclty one main mode ('a', 'r' or 'w') character.
399 // If there are more than one main mode characters listed, then
400 // we will consider |mode| as incorrect and return 0;
401 int main_mode_count = 0;
403 ModeFlags flags = 0;
404 for (; *mode != '\0'; ++mode) {
405 switch (*mode) {
406 case 'r':
407 flags |= static_cast<ModeFlags>(OpenMode::READ);
408 ++main_mode_count;
409 break;
410 case 'w':
411 flags |= static_cast<ModeFlags>(OpenMode::WRITE);
412 ++main_mode_count;
413 break;
414 case '+':
415 flags |= static_cast<ModeFlags>(OpenMode::PLUS);
416 break;
417 case 'b':
418 flags |= static_cast<ModeFlags>(ContentType::BINARY);
419 break;
420 case 'a':
421 flags |= static_cast<ModeFlags>(OpenMode::APPEND);
422 ++main_mode_count;
423 break;
424 case 'x':
425 flags |= static_cast<ModeFlags>(CreateType::EXCLUSIVE);
426 break;
427 default:
428 return 0;
432 if (main_mode_count != 1)
433 return 0;
435 return flags;
438 } // namespace __llvm_libc