xref: /llvm-project/libc/src/__support/File/file.cpp (revision c6b7bd42d58c166ed37cdd51bd46eaf2564b1f96)
14ef02da0SSiva Chandra Reddy //===--- Implementation of a platform independent file data structure -----===//
24ef02da0SSiva Chandra Reddy //
34ef02da0SSiva Chandra Reddy // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
44ef02da0SSiva Chandra Reddy // See https://llvm.org/LICENSE.txt for license information.
54ef02da0SSiva Chandra Reddy // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
64ef02da0SSiva Chandra Reddy //
74ef02da0SSiva Chandra Reddy //===----------------------------------------------------------------------===//
84ef02da0SSiva Chandra Reddy 
94ef02da0SSiva Chandra Reddy #include "file.h"
104ef02da0SSiva Chandra Reddy 
119cfe3028SJob Henandez Lara #include "hdr/func/realloc.h"
125aed6d67SMichael Jones #include "hdr/stdio_macros.h"
135aed6d67SMichael Jones #include "hdr/types/off_t.h"
14d06308dfSSiva Chandra Reddy #include "src/__support/CPP/new.h"
154d231912SGuillaume Chatelet #include "src/__support/CPP/span.h"
165ff3ff33SPetr Hosek #include "src/__support/macros/config.h"
1704a9c625SMichael Jones #include "src/errno/libc_errno.h" // For error macros
184ef02da0SSiva Chandra Reddy 
195ff3ff33SPetr Hosek namespace LIBC_NAMESPACE_DECL {
204ef02da0SSiva Chandra Reddy 
219beb8d11SMichael Jones FileIOResult File::write_unlocked(const void *data, size_t len) {
224ef02da0SSiva Chandra Reddy   if (!write_allowed()) {
234ef02da0SSiva Chandra Reddy     err = true;
249beb8d11SMichael Jones     return {0, EBADF};
254ef02da0SSiva Chandra Reddy   }
264ef02da0SSiva Chandra Reddy 
274ef02da0SSiva Chandra Reddy   prev_op = FileOp::WRITE;
284ef02da0SSiva Chandra Reddy 
29d04494ccSJoseph Huber   if (bufmode == _IONBF) { // unbuffered.
304d231912SGuillaume Chatelet     size_t ret_val =
314d231912SGuillaume Chatelet         write_unlocked_nbf(static_cast<const uint8_t *>(data), len);
326ce490e5SMichael Jones     flush_unlocked();
336ce490e5SMichael Jones     return ret_val;
34e6c401b5SJoseph Huber   } else if (bufmode == _IOFBF) { // fully buffered
35e6c401b5SJoseph Huber     return write_unlocked_fbf(static_cast<const uint8_t *>(data), len);
36e6c401b5SJoseph Huber   } else /*if (bufmode == _IOLBF) */ { // line buffered
37e6c401b5SJoseph Huber     return write_unlocked_lbf(static_cast<const uint8_t *>(data), len);
386ce490e5SMichael Jones   }
396ce490e5SMichael Jones }
406ce490e5SMichael Jones 
419beb8d11SMichael Jones FileIOResult File::write_unlocked_nbf(const uint8_t *data, size_t len) {
42d04494ccSJoseph Huber   if (pos > 0) { // If the buffer is not empty
436ce490e5SMichael Jones     // Flush the buffer
446ce490e5SMichael Jones     const size_t write_size = pos;
45*c6b7bd42SJack Huang     FileIOResult write_result = platform_write(this, buf, write_size);
466ce490e5SMichael Jones     pos = 0; // Buffer is now empty so reset pos to the beginning.
476ce490e5SMichael Jones     // If less bytes were written than expected, then an error occurred.
489beb8d11SMichael Jones     if (write_result < write_size) {
496ce490e5SMichael Jones       err = true;
509beb8d11SMichael Jones       // No bytes from data were written, so return 0.
519beb8d11SMichael Jones       return {0, write_result.error};
526ce490e5SMichael Jones     }
536ce490e5SMichael Jones   }
546ce490e5SMichael Jones 
55*c6b7bd42SJack Huang   FileIOResult write_result = platform_write(this, data, len);
569beb8d11SMichael Jones   if (write_result < len)
576ce490e5SMichael Jones     err = true;
589beb8d11SMichael Jones   return write_result;
596ce490e5SMichael Jones }
606ce490e5SMichael Jones 
619beb8d11SMichael Jones FileIOResult File::write_unlocked_fbf(const uint8_t *data, size_t len) {
626ce490e5SMichael Jones   const size_t init_pos = pos;
636ce490e5SMichael Jones   const size_t bufspace = bufsize - pos;
646ce490e5SMichael Jones 
656ce490e5SMichael Jones   // If data is too large to be buffered at all, then just write it unbuffered.
666ce490e5SMichael Jones   if (len > bufspace + bufsize)
676ce490e5SMichael Jones     return write_unlocked_nbf(data, len);
686ce490e5SMichael Jones 
696ce490e5SMichael Jones   // we split |data| (conceptually) using the split point. Then we handle the
706ce490e5SMichael Jones   // two pieces separately.
716ce490e5SMichael Jones   const size_t split_point = len < bufspace ? len : bufspace;
726ce490e5SMichael Jones 
736ce490e5SMichael Jones   // The primary piece is the piece of |data| we want to write to the buffer
746ce490e5SMichael Jones   // before flushing. It will always fit into the buffer, since the split point
756ce490e5SMichael Jones   // is defined as being min(len, bufspace), and it will always exist if len is
766ce490e5SMichael Jones   // non-zero.
774d231912SGuillaume Chatelet   cpp::span<const uint8_t> primary(data, split_point);
786ce490e5SMichael Jones 
796ce490e5SMichael Jones   // The second piece is the remainder of |data|. It is written to the buffer if
806ce490e5SMichael Jones   // it fits, or written directly to the output if it doesn't. If the primary
816ce490e5SMichael Jones   // piece fits entirely in the buffer, the remainder may be nothing.
824d231912SGuillaume Chatelet   cpp::span<const uint8_t> remainder(
836ce490e5SMichael Jones       static_cast<const uint8_t *>(data) + split_point, len - split_point);
846ce490e5SMichael Jones 
854d231912SGuillaume Chatelet   cpp::span<uint8_t> bufref(static_cast<uint8_t *>(buf), bufsize);
864ef02da0SSiva Chandra Reddy 
876ce490e5SMichael Jones   // Copy the first piece into the buffer.
884ef02da0SSiva Chandra Reddy   // TODO: Replace the for loop below with a call to internal memcpy.
896ce490e5SMichael Jones   for (size_t i = 0; i < primary.size(); ++i)
906ce490e5SMichael Jones     bufref[pos + i] = primary[i];
916ce490e5SMichael Jones   pos += primary.size();
926ce490e5SMichael Jones 
936ce490e5SMichael Jones   // If there is no remainder, we can return early, since the first piece has
946ce490e5SMichael Jones   // fit completely into the buffer.
956ce490e5SMichael Jones   if (remainder.size() == 0)
964ef02da0SSiva Chandra Reddy     return len;
974ef02da0SSiva Chandra Reddy 
986ce490e5SMichael Jones   // We need to flush the buffer now, since there is still data and the buffer
996ce490e5SMichael Jones   // is full.
1006ce490e5SMichael Jones   const size_t write_size = pos;
1019beb8d11SMichael Jones 
102*c6b7bd42SJack Huang   FileIOResult buf_result = platform_write(this, buf, write_size);
1039beb8d11SMichael Jones   size_t bytes_written = buf_result.value;
1049beb8d11SMichael Jones 
1054ef02da0SSiva Chandra Reddy   pos = 0; // Buffer is now empty so reset pos to the beginning.
1066ce490e5SMichael Jones   // If less bytes were written than expected, then an error occurred. Return
1076ce490e5SMichael Jones   // the number of bytes that have been written from |data|.
1089beb8d11SMichael Jones   if (buf_result.has_error() || bytes_written < write_size) {
1094ef02da0SSiva Chandra Reddy     err = true;
1109beb8d11SMichael Jones     return {bytes_written <= init_pos ? 0 : bytes_written - init_pos,
1119beb8d11SMichael Jones             buf_result.error};
1124ef02da0SSiva Chandra Reddy   }
1134ef02da0SSiva Chandra Reddy 
1146ce490e5SMichael Jones   // The second piece is handled basically the same as the first, although we
1156ce490e5SMichael Jones   // know that if the second piece has data in it then the buffer has been
1166ce490e5SMichael Jones   // flushed, meaning that pos is always 0.
1176ce490e5SMichael Jones   if (remainder.size() < bufsize) {
1184ef02da0SSiva Chandra Reddy     // TODO: Replace the for loop below with a call to internal memcpy.
1196ce490e5SMichael Jones     for (size_t i = 0; i < remainder.size(); ++i)
1206ce490e5SMichael Jones       bufref[i] = remainder[i];
1216ce490e5SMichael Jones     pos = remainder.size();
1226ce490e5SMichael Jones   } else {
1239beb8d11SMichael Jones 
124*c6b7bd42SJack Huang     FileIOResult result =
125*c6b7bd42SJack Huang         platform_write(this, remainder.data(), remainder.size());
1269beb8d11SMichael Jones     size_t bytes_written = buf_result.value;
1276ce490e5SMichael Jones 
1286ce490e5SMichael Jones     // If less bytes were written than expected, then an error occurred. Return
1296ce490e5SMichael Jones     // the number of bytes that have been written from |data|.
1309beb8d11SMichael Jones     if (result.has_error() || bytes_written < remainder.size()) {
1316ce490e5SMichael Jones       err = true;
1329beb8d11SMichael Jones       return {primary.size() + bytes_written, result.error};
1336ce490e5SMichael Jones     }
1346ce490e5SMichael Jones   }
1356ce490e5SMichael Jones 
1364ef02da0SSiva Chandra Reddy   return len;
1374ef02da0SSiva Chandra Reddy }
1384ef02da0SSiva Chandra Reddy 
1399beb8d11SMichael Jones FileIOResult File::write_unlocked_lbf(const uint8_t *data, size_t len) {
1404d231912SGuillaume Chatelet   constexpr uint8_t NEWLINE_CHAR = '\n';
1416ce490e5SMichael Jones   size_t last_newline = len;
142a9e0dbefSMichael Jones   for (size_t i = len; i >= 1; --i) {
1434d231912SGuillaume Chatelet     if (data[i - 1] == NEWLINE_CHAR) {
1445bcda1d3SMichael Jones       last_newline = i - 1;
1456ce490e5SMichael Jones       break;
1464ef02da0SSiva Chandra Reddy     }
1476ce490e5SMichael Jones   }
1486ce490e5SMichael Jones 
1496ce490e5SMichael Jones   // If there is no newline, treat this as fully buffered.
1506ce490e5SMichael Jones   if (last_newline == len) {
1516ce490e5SMichael Jones     return write_unlocked_fbf(data, len);
1526ce490e5SMichael Jones   }
1536ce490e5SMichael Jones 
1546ce490e5SMichael Jones   // we split |data| (conceptually) using the split point. Then we handle the
1556ce490e5SMichael Jones   // two pieces separately.
1566ce490e5SMichael Jones   const size_t split_point = last_newline + 1;
1576ce490e5SMichael Jones 
1586ce490e5SMichael Jones   // The primary piece is everything in |data| up to the newline. It's written
1596ce490e5SMichael Jones   // unbuffered to the output.
1604d231912SGuillaume Chatelet   cpp::span<const uint8_t> primary(data, split_point);
1616ce490e5SMichael Jones 
1626ce490e5SMichael Jones   // The second piece is the remainder of |data|. It is written fully buffered,
1636ce490e5SMichael Jones   // meaning it may stay in the buffer if it fits.
1644d231912SGuillaume Chatelet   cpp::span<const uint8_t> remainder(
1656ce490e5SMichael Jones       static_cast<const uint8_t *>(data) + split_point, len - split_point);
1666ce490e5SMichael Jones 
1676ce490e5SMichael Jones   size_t written = 0;
1686ce490e5SMichael Jones 
1696ce490e5SMichael Jones   written = write_unlocked_nbf(primary.data(), primary.size());
1706ce490e5SMichael Jones   if (written < primary.size()) {
1716ce490e5SMichael Jones     err = true;
1726ce490e5SMichael Jones     return written;
1736ce490e5SMichael Jones   }
1746ce490e5SMichael Jones 
1756ce490e5SMichael Jones   flush_unlocked();
1766ce490e5SMichael Jones 
1776ce490e5SMichael Jones   written += write_unlocked_fbf(remainder.data(), remainder.size());
1780f72a0d2SSiva Chandra Reddy   if (written < len) {
1796ce490e5SMichael Jones     err = true;
1806ce490e5SMichael Jones     return written;
1816ce490e5SMichael Jones   }
1826ce490e5SMichael Jones 
1834ef02da0SSiva Chandra Reddy   return len;
1844ef02da0SSiva Chandra Reddy }
1854ef02da0SSiva Chandra Reddy 
1869beb8d11SMichael Jones FileIOResult File::read_unlocked(void *data, size_t len) {
1874ef02da0SSiva Chandra Reddy   if (!read_allowed()) {
1884ef02da0SSiva Chandra Reddy     err = true;
1899beb8d11SMichael Jones     return {0, EBADF};
1904ef02da0SSiva Chandra Reddy   }
1914ef02da0SSiva Chandra Reddy 
1924ef02da0SSiva Chandra Reddy   prev_op = FileOp::READ;
1934ef02da0SSiva Chandra Reddy 
194*c6b7bd42SJack Huang   if (bufmode == _IONBF) { // unbuffered.
195*c6b7bd42SJack Huang     return read_unlocked_nbf(static_cast<uint8_t *>(data), len);
196*c6b7bd42SJack Huang   } else if (bufmode == _IOFBF) { // fully buffered
197*c6b7bd42SJack Huang     return read_unlocked_fbf(static_cast<uint8_t *>(data), len);
198*c6b7bd42SJack Huang   } else /*if (bufmode == _IOLBF) */ { // line buffered
199*c6b7bd42SJack Huang     // There is no line buffered mode for read. Use fully buffered instead.
200*c6b7bd42SJack Huang     return read_unlocked_fbf(static_cast<uint8_t *>(data), len);
201*c6b7bd42SJack Huang   }
202*c6b7bd42SJack Huang }
203*c6b7bd42SJack Huang 
204*c6b7bd42SJack Huang size_t File::copy_data_from_buf(uint8_t *data, size_t len) {
2054d231912SGuillaume Chatelet   cpp::span<uint8_t> bufref(static_cast<uint8_t *>(buf), bufsize);
2064d231912SGuillaume Chatelet   cpp::span<uint8_t> dataref(static_cast<uint8_t *>(data), len);
2074ef02da0SSiva Chandra Reddy 
2084ef02da0SSiva Chandra Reddy   // Because read_limit is always greater than equal to pos,
2094ef02da0SSiva Chandra Reddy   // available_data is never a wrapped around value.
2104ef02da0SSiva Chandra Reddy   size_t available_data = read_limit - pos;
2114ef02da0SSiva Chandra Reddy   if (len <= available_data) {
2124ef02da0SSiva Chandra Reddy     // TODO: Replace the for loop below with a call to internal memcpy.
2134ef02da0SSiva Chandra Reddy     for (size_t i = 0; i < len; ++i)
2144ef02da0SSiva Chandra Reddy       dataref[i] = bufref[i + pos];
2154ef02da0SSiva Chandra Reddy     pos += len;
2164ef02da0SSiva Chandra Reddy     return len;
2174ef02da0SSiva Chandra Reddy   }
2184ef02da0SSiva Chandra Reddy 
2194ef02da0SSiva Chandra Reddy   // Copy all of the available data.
2204ef02da0SSiva Chandra Reddy   // TODO: Replace the for loop with a call to internal memcpy.
2214ef02da0SSiva Chandra Reddy   for (size_t i = 0; i < available_data; ++i)
2224ef02da0SSiva Chandra Reddy     dataref[i] = bufref[i + pos];
2234ef02da0SSiva Chandra Reddy   read_limit = pos = 0; // Reset the pointers.
224*c6b7bd42SJack Huang 
225*c6b7bd42SJack Huang   return available_data;
226*c6b7bd42SJack Huang }
227*c6b7bd42SJack Huang 
228*c6b7bd42SJack Huang FileIOResult File::read_unlocked_fbf(uint8_t *data, size_t len) {
229*c6b7bd42SJack Huang   // Read data from the buffer first.
230*c6b7bd42SJack Huang   size_t available_data = copy_data_from_buf(data, len);
231*c6b7bd42SJack Huang   if (available_data == len)
232*c6b7bd42SJack Huang     return available_data;
233*c6b7bd42SJack Huang 
2341ceafe5eSSiva Chandra Reddy   // Update the dataref to reflect that fact that we have already
2351ceafe5eSSiva Chandra Reddy   // copied |available_data| into |data|.
2364ef02da0SSiva Chandra Reddy   size_t to_fetch = len - available_data;
237*c6b7bd42SJack Huang   cpp::span<uint8_t> dataref(static_cast<uint8_t *>(data) + available_data,
238*c6b7bd42SJack Huang                              to_fetch);
239*c6b7bd42SJack Huang 
2404ef02da0SSiva Chandra Reddy   if (to_fetch > bufsize) {
241*c6b7bd42SJack Huang     FileIOResult result = platform_read(this, dataref.data(), to_fetch);
2429beb8d11SMichael Jones     size_t fetched_size = result.value;
2439beb8d11SMichael Jones     if (result.has_error() || fetched_size < to_fetch) {
2449beb8d11SMichael Jones       if (!result.has_error())
2454ef02da0SSiva Chandra Reddy         eof = true;
2464ef02da0SSiva Chandra Reddy       else
2474ef02da0SSiva Chandra Reddy         err = true;
248*c6b7bd42SJack Huang       return {available_data + fetched_size, result.error};
2494ef02da0SSiva Chandra Reddy     }
2504ef02da0SSiva Chandra Reddy     return len;
2514ef02da0SSiva Chandra Reddy   }
2524ef02da0SSiva Chandra Reddy 
2534ef02da0SSiva Chandra Reddy   // Fetch and buffer another buffer worth of data.
254*c6b7bd42SJack Huang   FileIOResult result = platform_read(this, buf, bufsize);
2559beb8d11SMichael Jones   size_t fetched_size = result.value;
2564ef02da0SSiva Chandra Reddy   read_limit += fetched_size;
2574ef02da0SSiva Chandra Reddy   size_t transfer_size = fetched_size >= to_fetch ? to_fetch : fetched_size;
2584ef02da0SSiva Chandra Reddy   for (size_t i = 0; i < transfer_size; ++i)
259*c6b7bd42SJack Huang     dataref[i] = buf[i];
2604ef02da0SSiva Chandra Reddy   pos += transfer_size;
2619beb8d11SMichael Jones   if (result.has_error() || fetched_size < to_fetch) {
2629beb8d11SMichael Jones     if (!result.has_error())
2634ef02da0SSiva Chandra Reddy       eof = true;
2644ef02da0SSiva Chandra Reddy     else
2654ef02da0SSiva Chandra Reddy       err = true;
2664ef02da0SSiva Chandra Reddy   }
2679beb8d11SMichael Jones   return {transfer_size + available_data, result.error};
2684ef02da0SSiva Chandra Reddy }
2694ef02da0SSiva Chandra Reddy 
270*c6b7bd42SJack Huang FileIOResult File::read_unlocked_nbf(uint8_t *data, size_t len) {
271*c6b7bd42SJack Huang   // Check whether there is a character in the ungetc buffer.
272*c6b7bd42SJack Huang   size_t available_data = copy_data_from_buf(data, len);
273*c6b7bd42SJack Huang   if (available_data == len)
274*c6b7bd42SJack Huang     return available_data;
275*c6b7bd42SJack Huang 
276*c6b7bd42SJack Huang   // Directly copy the data into |data|.
277*c6b7bd42SJack Huang   cpp::span<uint8_t> dataref(static_cast<uint8_t *>(data) + available_data,
278*c6b7bd42SJack Huang                              len - available_data);
279*c6b7bd42SJack Huang   FileIOResult result = platform_read(this, dataref.data(), dataref.size());
280*c6b7bd42SJack Huang 
281*c6b7bd42SJack Huang   if (result.has_error() || result < dataref.size()) {
282*c6b7bd42SJack Huang     if (!result.has_error())
283*c6b7bd42SJack Huang       eof = true;
284*c6b7bd42SJack Huang     else
285*c6b7bd42SJack Huang       err = true;
286*c6b7bd42SJack Huang   }
287*c6b7bd42SJack Huang   return {result + available_data, result.error};
288*c6b7bd42SJack Huang }
289*c6b7bd42SJack Huang 
2901ceafe5eSSiva Chandra Reddy int File::ungetc_unlocked(int c) {
2911ceafe5eSSiva Chandra Reddy   // There is no meaning to unget if:
2921ceafe5eSSiva Chandra Reddy   // 1. You are trying to push back EOF.
2931ceafe5eSSiva Chandra Reddy   // 2. Read operations are not allowed on this file.
2941ceafe5eSSiva Chandra Reddy   // 3. The previous operation was a write operation.
2951ceafe5eSSiva Chandra Reddy   if (c == EOF || !read_allowed() || (prev_op == FileOp::WRITE))
2961ceafe5eSSiva Chandra Reddy     return EOF;
2971ceafe5eSSiva Chandra Reddy 
2981ceafe5eSSiva Chandra Reddy   cpp::span<uint8_t> bufref(static_cast<uint8_t *>(buf), bufsize);
2991ceafe5eSSiva Chandra Reddy   if (read_limit == 0) {
3001ceafe5eSSiva Chandra Reddy     // If |read_limit| is zero, it can mean three things:
3011ceafe5eSSiva Chandra Reddy     //   a. This file was just created.
3021ceafe5eSSiva Chandra Reddy     //   b. The previous operation was a seek operation.
3031ceafe5eSSiva Chandra Reddy     //   c. The previous operation was a read operation which emptied
3041ceafe5eSSiva Chandra Reddy     //      the buffer.
3051ceafe5eSSiva Chandra Reddy     // For all the above cases, we simply write |c| at the beginning
3061ceafe5eSSiva Chandra Reddy     // of the buffer and bump |read_limit|. Note that |pos| will also
3071ceafe5eSSiva Chandra Reddy     // be zero in this case, so we don't need to adjust it.
3081ceafe5eSSiva Chandra Reddy     bufref[0] = static_cast<unsigned char>(c);
3091ceafe5eSSiva Chandra Reddy     ++read_limit;
3101ceafe5eSSiva Chandra Reddy   } else {
3111ceafe5eSSiva Chandra Reddy     // If |read_limit| is non-zero, it means that there is data in the buffer
3121ceafe5eSSiva Chandra Reddy     // from a previous read operation. Which would also mean that |pos| is not
3131ceafe5eSSiva Chandra Reddy     // zero. So, we decrement |pos| and write |c| in to the buffer at the new
3141ceafe5eSSiva Chandra Reddy     // |pos|. If too many ungetc operations are performed without reads, it
3151ceafe5eSSiva Chandra Reddy     // can lead to (pos == 0 but read_limit != 0). We will just error out in
3161ceafe5eSSiva Chandra Reddy     // such a case.
3171ceafe5eSSiva Chandra Reddy     if (pos == 0)
3181ceafe5eSSiva Chandra Reddy       return EOF;
3191ceafe5eSSiva Chandra Reddy     --pos;
3201ceafe5eSSiva Chandra Reddy     bufref[pos] = static_cast<unsigned char>(c);
3211ceafe5eSSiva Chandra Reddy   }
3221ceafe5eSSiva Chandra Reddy 
3231ceafe5eSSiva Chandra Reddy   eof = false; // There is atleast one character that can be read now.
3241ceafe5eSSiva Chandra Reddy   err = false; // This operation was a success.
3251ceafe5eSSiva Chandra Reddy   return c;
3261ceafe5eSSiva Chandra Reddy }
3271ceafe5eSSiva Chandra Reddy 
3282308c7f5SMikhail R. Gadelha ErrorOr<int> File::seek(off_t offset, int whence) {
3294ef02da0SSiva Chandra Reddy   FileLock lock(this);
3304ef02da0SSiva Chandra Reddy   if (prev_op == FileOp::WRITE && pos > 0) {
3319beb8d11SMichael Jones 
332*c6b7bd42SJack Huang     FileIOResult buf_result = platform_write(this, buf, pos);
3339beb8d11SMichael Jones     if (buf_result.has_error() || buf_result.value < pos) {
3344ef02da0SSiva Chandra Reddy       err = true;
3359beb8d11SMichael Jones       return Error(buf_result.error);
3364ef02da0SSiva Chandra Reddy     }
337a0f6d12cSSiva Chandra Reddy   } else if (prev_op == FileOp::READ && whence == SEEK_CUR) {
338a0f6d12cSSiva Chandra Reddy     // More data could have been read out from the platform file than was
339a0f6d12cSSiva Chandra Reddy     // required. So, we have to adjust the offset we pass to platform seek
340a0f6d12cSSiva Chandra Reddy     // function. Note that read_limit >= pos is always true.
341a0f6d12cSSiva Chandra Reddy     offset -= (read_limit - pos);
3424ef02da0SSiva Chandra Reddy   }
3434ef02da0SSiva Chandra Reddy   pos = read_limit = 0;
3444ef02da0SSiva Chandra Reddy   prev_op = FileOp::SEEK;
3454ef02da0SSiva Chandra Reddy   // Reset the eof flag as a seek might move the file positon to some place
3464ef02da0SSiva Chandra Reddy   // readable.
3474ef02da0SSiva Chandra Reddy   eof = false;
3489beb8d11SMichael Jones   auto result = platform_seek(this, offset, whence);
3499beb8d11SMichael Jones   if (!result.has_value())
3509beb8d11SMichael Jones     return Error(result.error());
3519beb8d11SMichael Jones   return 0;
35243e52ad5SSiva Chandra Reddy }
35343e52ad5SSiva Chandra Reddy 
3547776fba4SMikhail R. Gadelha ErrorOr<off_t> File::tell() {
35543e52ad5SSiva Chandra Reddy   FileLock lock(this);
3569beb8d11SMichael Jones   auto seek_target = eof ? SEEK_END : SEEK_CUR;
3579beb8d11SMichael Jones   auto result = platform_seek(this, 0, seek_target);
3589beb8d11SMichael Jones   if (!result.has_value() || result.value() < 0)
3599beb8d11SMichael Jones     return Error(result.error());
3607776fba4SMikhail R. Gadelha   off_t platform_offset = result.value();
36143e52ad5SSiva Chandra Reddy   if (prev_op == FileOp::READ)
36243e52ad5SSiva Chandra Reddy     return platform_offset - (read_limit - pos);
3637776fba4SMikhail R. Gadelha   if (prev_op == FileOp::WRITE)
36443e52ad5SSiva Chandra Reddy     return platform_offset + pos;
36543e52ad5SSiva Chandra Reddy   return platform_offset;
3664ef02da0SSiva Chandra Reddy }
3674ef02da0SSiva Chandra Reddy 
3686ce490e5SMichael Jones int File::flush_unlocked() {
3694ef02da0SSiva Chandra Reddy   if (prev_op == FileOp::WRITE && pos > 0) {
370*c6b7bd42SJack Huang     FileIOResult buf_result = platform_write(this, buf, pos);
3719beb8d11SMichael Jones     if (buf_result.has_error() || buf_result.value < pos) {
3724ef02da0SSiva Chandra Reddy       err = true;
3739beb8d11SMichael Jones       return buf_result.error;
3744ef02da0SSiva Chandra Reddy     }
3754ef02da0SSiva Chandra Reddy     pos = 0;
3764ef02da0SSiva Chandra Reddy   }
37722f9dca1SSiva Chandra Reddy   // TODO: Add POSIX behavior for input streams.
3784ef02da0SSiva Chandra Reddy   return 0;
3794ef02da0SSiva Chandra Reddy }
3804ef02da0SSiva Chandra Reddy 
3814eea8849SSiva Chandra Reddy int File::set_buffer(void *buffer, size_t size, int buffer_mode) {
3824eea8849SSiva Chandra Reddy   // We do not need to lock the file as this method should be called before
3834eea8849SSiva Chandra Reddy   // other operations are performed on the file.
3844eea8849SSiva Chandra Reddy   if (buffer != nullptr && size == 0)
3854eea8849SSiva Chandra Reddy     return EINVAL;
3864eea8849SSiva Chandra Reddy 
3874eea8849SSiva Chandra Reddy   switch (buffer_mode) {
3884eea8849SSiva Chandra Reddy   case _IOFBF:
3894eea8849SSiva Chandra Reddy   case _IOLBF:
3904eea8849SSiva Chandra Reddy   case _IONBF:
3914eea8849SSiva Chandra Reddy     break;
3924eea8849SSiva Chandra Reddy   default:
3934eea8849SSiva Chandra Reddy     return EINVAL;
3944eea8849SSiva Chandra Reddy   }
3954eea8849SSiva Chandra Reddy 
3964eea8849SSiva Chandra Reddy   if (buffer == nullptr && size != 0 && buffer_mode != _IONBF) {
3974eea8849SSiva Chandra Reddy     // We exclude the case of buffer_mode == _IONBF in this branch
3984eea8849SSiva Chandra Reddy     // because we don't need to allocate buffer in such a case.
3994eea8849SSiva Chandra Reddy     if (own_buf) {
400d06308dfSSiva Chandra Reddy       // This is one of the places where use a C allocation functon
401d06308dfSSiva Chandra Reddy       // as C++ does not have an equivalent of realloc.
402d06308dfSSiva Chandra Reddy       buf = reinterpret_cast<uint8_t *>(realloc(buf, size));
403d06308dfSSiva Chandra Reddy       if (buf == nullptr)
404d06308dfSSiva Chandra Reddy         return ENOMEM;
4054eea8849SSiva Chandra Reddy     } else {
406d06308dfSSiva Chandra Reddy       AllocChecker ac;
407d06308dfSSiva Chandra Reddy       buf = new (ac) uint8_t[size];
408d06308dfSSiva Chandra Reddy       if (!ac)
409d06308dfSSiva Chandra Reddy         return ENOMEM;
4104eea8849SSiva Chandra Reddy       own_buf = true;
4114eea8849SSiva Chandra Reddy     }
4124eea8849SSiva Chandra Reddy     bufsize = size;
4134eea8849SSiva Chandra Reddy     // TODO: Handle allocation failures.
4144eea8849SSiva Chandra Reddy   } else {
4154ef02da0SSiva Chandra Reddy     if (own_buf)
416d06308dfSSiva Chandra Reddy       delete buf;
4174eea8849SSiva Chandra Reddy     if (buffer_mode != _IONBF) {
4184d231912SGuillaume Chatelet       buf = static_cast<uint8_t *>(buffer);
4194ef02da0SSiva Chandra Reddy       bufsize = size;
4204eea8849SSiva Chandra Reddy     } else {
4214eea8849SSiva Chandra Reddy       // We don't need any buffer.
4224eea8849SSiva Chandra Reddy       buf = nullptr;
4234eea8849SSiva Chandra Reddy       bufsize = 0;
4244eea8849SSiva Chandra Reddy     }
4254eea8849SSiva Chandra Reddy     own_buf = false;
4264eea8849SSiva Chandra Reddy   }
4274eea8849SSiva Chandra Reddy   bufmode = buffer_mode;
4284eea8849SSiva Chandra Reddy   adjust_buf();
4294eea8849SSiva Chandra Reddy   return 0;
4304ef02da0SSiva Chandra Reddy }
4314ef02da0SSiva Chandra Reddy 
4324ef02da0SSiva Chandra Reddy File::ModeFlags File::mode_flags(const char *mode) {
4334ef02da0SSiva Chandra Reddy   // First character in |mode| should be 'a', 'r' or 'w'.
4344ef02da0SSiva Chandra Reddy   if (*mode != 'a' && *mode != 'r' && *mode != 'w')
4354ef02da0SSiva Chandra Reddy     return 0;
4364ef02da0SSiva Chandra Reddy 
4374ef02da0SSiva Chandra Reddy   // There should be exaclty one main mode ('a', 'r' or 'w') character.
4384ef02da0SSiva Chandra Reddy   // If there are more than one main mode characters listed, then
4394ef02da0SSiva Chandra Reddy   // we will consider |mode| as incorrect and return 0;
4404ef02da0SSiva Chandra Reddy   int main_mode_count = 0;
4414ef02da0SSiva Chandra Reddy 
4424ef02da0SSiva Chandra Reddy   ModeFlags flags = 0;
4434ef02da0SSiva Chandra Reddy   for (; *mode != '\0'; ++mode) {
4444ef02da0SSiva Chandra Reddy     switch (*mode) {
4454ef02da0SSiva Chandra Reddy     case 'r':
4464ef02da0SSiva Chandra Reddy       flags |= static_cast<ModeFlags>(OpenMode::READ);
4474ef02da0SSiva Chandra Reddy       ++main_mode_count;
4484ef02da0SSiva Chandra Reddy       break;
4494ef02da0SSiva Chandra Reddy     case 'w':
4504ef02da0SSiva Chandra Reddy       flags |= static_cast<ModeFlags>(OpenMode::WRITE);
4514ef02da0SSiva Chandra Reddy       ++main_mode_count;
4524ef02da0SSiva Chandra Reddy       break;
4534ef02da0SSiva Chandra Reddy     case '+':
45429a631a2SSiva Chandra Reddy       flags |= static_cast<ModeFlags>(OpenMode::PLUS);
4554ef02da0SSiva Chandra Reddy       break;
4564ef02da0SSiva Chandra Reddy     case 'b':
4574ef02da0SSiva Chandra Reddy       flags |= static_cast<ModeFlags>(ContentType::BINARY);
4584ef02da0SSiva Chandra Reddy       break;
4594ef02da0SSiva Chandra Reddy     case 'a':
4604ef02da0SSiva Chandra Reddy       flags |= static_cast<ModeFlags>(OpenMode::APPEND);
4614ef02da0SSiva Chandra Reddy       ++main_mode_count;
4624ef02da0SSiva Chandra Reddy       break;
4634ef02da0SSiva Chandra Reddy     case 'x':
4644ef02da0SSiva Chandra Reddy       flags |= static_cast<ModeFlags>(CreateType::EXCLUSIVE);
4654ef02da0SSiva Chandra Reddy       break;
4664ef02da0SSiva Chandra Reddy     default:
4674ef02da0SSiva Chandra Reddy       return 0;
4684ef02da0SSiva Chandra Reddy     }
4694ef02da0SSiva Chandra Reddy   }
4704ef02da0SSiva Chandra Reddy 
4714ef02da0SSiva Chandra Reddy   if (main_mode_count != 1)
4724ef02da0SSiva Chandra Reddy     return 0;
4734ef02da0SSiva Chandra Reddy 
4744ef02da0SSiva Chandra Reddy   return flags;
4754ef02da0SSiva Chandra Reddy }
4764ef02da0SSiva Chandra Reddy 
4775ff3ff33SPetr Hosek } // namespace LIBC_NAMESPACE_DECL
478