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
114ef02da0SSiva Chandra Reddy #include "src/__support/CPP/ArrayRef.h"
124ef02da0SSiva Chandra Reddy
134ef02da0SSiva Chandra Reddy #include <errno.h>
14a0f6d12cSSiva Chandra Reddy #include <stdio.h>
154ef02da0SSiva Chandra Reddy #include <stdlib.h>
164ef02da0SSiva Chandra Reddy
174ef02da0SSiva Chandra Reddy namespace __llvm_libc {
184ef02da0SSiva Chandra Reddy
write_unlocked(const void * data,size_t len)19945e0220SSiva Chandra Reddy size_t File::write_unlocked(const void *data, size_t len) {
204ef02da0SSiva Chandra Reddy if (!write_allowed()) {
214ef02da0SSiva Chandra Reddy errno = EBADF;
224ef02da0SSiva Chandra Reddy err = true;
234ef02da0SSiva Chandra Reddy return 0;
244ef02da0SSiva Chandra Reddy }
254ef02da0SSiva Chandra Reddy
264ef02da0SSiva Chandra Reddy prev_op = FileOp::WRITE;
274ef02da0SSiva Chandra Reddy
286ce490e5SMichael Jones if (bufmode == _IOFBF) { // fully buffered
296ce490e5SMichael Jones return write_unlocked_fbf(data, len);
306ce490e5SMichael Jones } else if (bufmode == _IOLBF) { // line buffered
316ce490e5SMichael Jones return write_unlocked_lbf(data, len);
326ce490e5SMichael Jones } else /*if (bufmode == _IONBF) */ { // unbuffered
336ce490e5SMichael Jones size_t ret_val = write_unlocked_nbf(data, len);
346ce490e5SMichael Jones flush_unlocked();
356ce490e5SMichael Jones return ret_val;
366ce490e5SMichael Jones }
376ce490e5SMichael Jones }
386ce490e5SMichael Jones
write_unlocked_nbf(const void * data,size_t len)396ce490e5SMichael Jones size_t File::write_unlocked_nbf(const void *data, size_t len) {
406ce490e5SMichael Jones if (pos > 0) { // If the buffer is not empty
416ce490e5SMichael Jones // Flush the buffer
426ce490e5SMichael Jones const size_t write_size = pos;
436ce490e5SMichael Jones size_t bytes_written = platform_write(this, buf, write_size);
446ce490e5SMichael Jones pos = 0; // Buffer is now empty so reset pos to the beginning.
456ce490e5SMichael Jones // If less bytes were written than expected, then an error occurred.
466ce490e5SMichael Jones if (bytes_written < write_size) {
476ce490e5SMichael Jones err = true;
486ce490e5SMichael Jones return 0; // No bytes from data were written, so return 0.
496ce490e5SMichael Jones }
506ce490e5SMichael Jones }
516ce490e5SMichael Jones
526ce490e5SMichael Jones size_t written = platform_write(this, data, len);
536ce490e5SMichael Jones if (written < len)
546ce490e5SMichael Jones err = true;
556ce490e5SMichael Jones return written;
566ce490e5SMichael Jones }
576ce490e5SMichael Jones
write_unlocked_fbf(const void * data,size_t len)586ce490e5SMichael Jones size_t File::write_unlocked_fbf(const void *data, size_t len) {
596ce490e5SMichael Jones const size_t init_pos = pos;
606ce490e5SMichael Jones const size_t bufspace = bufsize - pos;
616ce490e5SMichael Jones
626ce490e5SMichael Jones // If data is too large to be buffered at all, then just write it unbuffered.
636ce490e5SMichael Jones if (len > bufspace + bufsize)
646ce490e5SMichael Jones return write_unlocked_nbf(data, len);
656ce490e5SMichael Jones
666ce490e5SMichael Jones // we split |data| (conceptually) using the split point. Then we handle the
676ce490e5SMichael Jones // two pieces separately.
686ce490e5SMichael Jones const size_t split_point = len < bufspace ? len : bufspace;
696ce490e5SMichael Jones
706ce490e5SMichael Jones // The primary piece is the piece of |data| we want to write to the buffer
716ce490e5SMichael Jones // before flushing. It will always fit into the buffer, since the split point
726ce490e5SMichael Jones // is defined as being min(len, bufspace), and it will always exist if len is
736ce490e5SMichael Jones // non-zero.
746ce490e5SMichael Jones cpp::ArrayRef<uint8_t> primary(data, split_point);
756ce490e5SMichael Jones
766ce490e5SMichael Jones // The second piece is the remainder of |data|. It is written to the buffer if
776ce490e5SMichael Jones // it fits, or written directly to the output if it doesn't. If the primary
786ce490e5SMichael Jones // piece fits entirely in the buffer, the remainder may be nothing.
796ce490e5SMichael Jones cpp::ArrayRef<uint8_t> remainder(
806ce490e5SMichael Jones static_cast<const uint8_t *>(data) + split_point, len - split_point);
816ce490e5SMichael Jones
824ef02da0SSiva Chandra Reddy cpp::MutableArrayRef<uint8_t> bufref(buf, bufsize);
834ef02da0SSiva Chandra Reddy
846ce490e5SMichael Jones // Copy the first piece into the buffer.
854ef02da0SSiva Chandra Reddy // TODO: Replace the for loop below with a call to internal memcpy.
866ce490e5SMichael Jones for (size_t i = 0; i < primary.size(); ++i)
876ce490e5SMichael Jones bufref[pos + i] = primary[i];
886ce490e5SMichael Jones pos += primary.size();
896ce490e5SMichael Jones
906ce490e5SMichael Jones // If there is no remainder, we can return early, since the first piece has
916ce490e5SMichael Jones // fit completely into the buffer.
926ce490e5SMichael Jones if (remainder.size() == 0)
934ef02da0SSiva Chandra Reddy return len;
944ef02da0SSiva Chandra Reddy
956ce490e5SMichael Jones // We need to flush the buffer now, since there is still data and the buffer
966ce490e5SMichael Jones // is full.
976ce490e5SMichael Jones const size_t write_size = pos;
986ce490e5SMichael Jones size_t bytes_written = platform_write(this, buf, write_size);
994ef02da0SSiva Chandra Reddy pos = 0; // Buffer is now empty so reset pos to the beginning.
1006ce490e5SMichael Jones // If less bytes were written than expected, then an error occurred. Return
1016ce490e5SMichael Jones // the number of bytes that have been written from |data|.
1026ce490e5SMichael Jones if (bytes_written < write_size) {
1034ef02da0SSiva Chandra Reddy err = true;
1046ce490e5SMichael Jones return bytes_written <= init_pos ? 0 : bytes_written - init_pos;
1054ef02da0SSiva Chandra Reddy }
1064ef02da0SSiva Chandra Reddy
1076ce490e5SMichael Jones // The second piece is handled basically the same as the first, although we
1086ce490e5SMichael Jones // know that if the second piece has data in it then the buffer has been
1096ce490e5SMichael Jones // flushed, meaning that pos is always 0.
1106ce490e5SMichael Jones if (remainder.size() < bufsize) {
1114ef02da0SSiva Chandra Reddy // TODO: Replace the for loop below with a call to internal memcpy.
1126ce490e5SMichael Jones for (size_t i = 0; i < remainder.size(); ++i)
1136ce490e5SMichael Jones bufref[i] = remainder[i];
1146ce490e5SMichael Jones pos = remainder.size();
1156ce490e5SMichael Jones } else {
1166ce490e5SMichael Jones size_t bytes_written =
1176ce490e5SMichael Jones platform_write(this, remainder.data(), remainder.size());
1186ce490e5SMichael Jones
1196ce490e5SMichael Jones // If less bytes were written than expected, then an error occurred. Return
1206ce490e5SMichael Jones // the number of bytes that have been written from |data|.
1216ce490e5SMichael Jones if (bytes_written < remainder.size()) {
1226ce490e5SMichael Jones err = true;
1236ce490e5SMichael Jones return primary.size() + bytes_written;
1246ce490e5SMichael Jones }
1256ce490e5SMichael Jones }
1266ce490e5SMichael Jones
1274ef02da0SSiva Chandra Reddy return len;
1284ef02da0SSiva Chandra Reddy }
1294ef02da0SSiva Chandra Reddy
write_unlocked_lbf(const void * data,size_t len)1306ce490e5SMichael Jones size_t File::write_unlocked_lbf(const void *data, size_t len) {
1316ce490e5SMichael Jones constexpr char NEWLINE_CHAR = '\n';
1326ce490e5SMichael Jones size_t last_newline = len;
133*5bcda1d3SMichael Jones for (size_t i = len; i > 1; --i) {
134*5bcda1d3SMichael Jones if (static_cast<const char *>(data)[i - 1] == NEWLINE_CHAR) {
135*5bcda1d3SMichael Jones last_newline = i - 1;
1366ce490e5SMichael Jones break;
1374ef02da0SSiva Chandra Reddy }
1386ce490e5SMichael Jones }
1396ce490e5SMichael Jones
1406ce490e5SMichael Jones // If there is no newline, treat this as fully buffered.
1416ce490e5SMichael Jones if (last_newline == len) {
1426ce490e5SMichael Jones return write_unlocked_fbf(data, len);
1436ce490e5SMichael Jones }
1446ce490e5SMichael Jones
1456ce490e5SMichael Jones // we split |data| (conceptually) using the split point. Then we handle the
1466ce490e5SMichael Jones // two pieces separately.
1476ce490e5SMichael Jones const size_t split_point = last_newline + 1;
1486ce490e5SMichael Jones
1496ce490e5SMichael Jones // The primary piece is everything in |data| up to the newline. It's written
1506ce490e5SMichael Jones // unbuffered to the output.
1516ce490e5SMichael Jones cpp::ArrayRef<uint8_t> primary(data, split_point);
1526ce490e5SMichael Jones
1536ce490e5SMichael Jones // The second piece is the remainder of |data|. It is written fully buffered,
1546ce490e5SMichael Jones // meaning it may stay in the buffer if it fits.
1556ce490e5SMichael Jones cpp::ArrayRef<uint8_t> remainder(
1566ce490e5SMichael Jones static_cast<const uint8_t *>(data) + split_point, len - split_point);
1576ce490e5SMichael Jones
1586ce490e5SMichael Jones size_t written = 0;
1596ce490e5SMichael Jones
1606ce490e5SMichael Jones written = write_unlocked_nbf(primary.data(), primary.size());
1616ce490e5SMichael Jones if (written < primary.size()) {
1626ce490e5SMichael Jones err = true;
1636ce490e5SMichael Jones return written;
1646ce490e5SMichael Jones }
1656ce490e5SMichael Jones
1666ce490e5SMichael Jones flush_unlocked();
1676ce490e5SMichael Jones
1686ce490e5SMichael Jones written += write_unlocked_fbf(remainder.data(), remainder.size());
1690f72a0d2SSiva Chandra Reddy if (written < len) {
1706ce490e5SMichael Jones err = true;
1716ce490e5SMichael Jones return written;
1726ce490e5SMichael Jones }
1736ce490e5SMichael Jones
1744ef02da0SSiva Chandra Reddy return len;
1754ef02da0SSiva Chandra Reddy }
1764ef02da0SSiva Chandra Reddy
read_unlocked(void * data,size_t len)177945e0220SSiva Chandra Reddy size_t File::read_unlocked(void *data, size_t len) {
1784ef02da0SSiva Chandra Reddy if (!read_allowed()) {
1794ef02da0SSiva Chandra Reddy errno = EBADF;
1804ef02da0SSiva Chandra Reddy err = true;
1814ef02da0SSiva Chandra Reddy return 0;
1824ef02da0SSiva Chandra Reddy }
1834ef02da0SSiva Chandra Reddy
1844ef02da0SSiva Chandra Reddy prev_op = FileOp::READ;
1854ef02da0SSiva Chandra Reddy
1864ef02da0SSiva Chandra Reddy cpp::MutableArrayRef<uint8_t> bufref(buf, bufsize);
1874ef02da0SSiva Chandra Reddy cpp::MutableArrayRef<uint8_t> dataref(data, len);
1884ef02da0SSiva Chandra Reddy
1894ef02da0SSiva Chandra Reddy // Because read_limit is always greater than equal to pos,
1904ef02da0SSiva Chandra Reddy // available_data is never a wrapped around value.
1914ef02da0SSiva Chandra Reddy size_t available_data = read_limit - pos;
1924ef02da0SSiva Chandra Reddy if (len <= available_data) {
1934ef02da0SSiva Chandra Reddy // TODO: Replace the for loop below with a call to internal memcpy.
1944ef02da0SSiva Chandra Reddy for (size_t i = 0; i < len; ++i)
1954ef02da0SSiva Chandra Reddy dataref[i] = bufref[i + pos];
1964ef02da0SSiva Chandra Reddy pos += len;
1974ef02da0SSiva Chandra Reddy return len;
1984ef02da0SSiva Chandra Reddy }
1994ef02da0SSiva Chandra Reddy
2004ef02da0SSiva Chandra Reddy // Copy all of the available data.
2014ef02da0SSiva Chandra Reddy // TODO: Replace the for loop with a call to internal memcpy.
2024ef02da0SSiva Chandra Reddy for (size_t i = 0; i < available_data; ++i)
2034ef02da0SSiva Chandra Reddy dataref[i] = bufref[i + pos];
2044ef02da0SSiva Chandra Reddy read_limit = pos = 0; // Reset the pointers.
2054ef02da0SSiva Chandra Reddy
2064ef02da0SSiva Chandra Reddy size_t to_fetch = len - available_data;
2074ef02da0SSiva Chandra Reddy if (to_fetch > bufsize) {
2084ef02da0SSiva Chandra Reddy size_t fetched_size = platform_read(this, data, to_fetch);
2094ef02da0SSiva Chandra Reddy if (fetched_size < to_fetch) {
2104ef02da0SSiva Chandra Reddy if (errno == 0)
2114ef02da0SSiva Chandra Reddy eof = true;
2124ef02da0SSiva Chandra Reddy else
2134ef02da0SSiva Chandra Reddy err = true;
2144ef02da0SSiva Chandra Reddy return available_data + fetched_size;
2154ef02da0SSiva Chandra Reddy }
2164ef02da0SSiva Chandra Reddy return len;
2174ef02da0SSiva Chandra Reddy }
2184ef02da0SSiva Chandra Reddy
2194ef02da0SSiva Chandra Reddy // Fetch and buffer another buffer worth of data.
2204ef02da0SSiva Chandra Reddy size_t fetched_size = platform_read(this, buf, bufsize);
2214ef02da0SSiva Chandra Reddy read_limit += fetched_size;
2224ef02da0SSiva Chandra Reddy size_t transfer_size = fetched_size >= to_fetch ? to_fetch : fetched_size;
2234ef02da0SSiva Chandra Reddy for (size_t i = 0; i < transfer_size; ++i)
2244ef02da0SSiva Chandra Reddy dataref[i] = bufref[i];
2254ef02da0SSiva Chandra Reddy pos += transfer_size;
2264ef02da0SSiva Chandra Reddy if (fetched_size < to_fetch) {
2274ef02da0SSiva Chandra Reddy if (errno == 0)
2284ef02da0SSiva Chandra Reddy eof = true;
2294ef02da0SSiva Chandra Reddy else
2304ef02da0SSiva Chandra Reddy err = true;
2314ef02da0SSiva Chandra Reddy }
2324ef02da0SSiva Chandra Reddy return transfer_size + available_data;
2334ef02da0SSiva Chandra Reddy }
2344ef02da0SSiva Chandra Reddy
seek(long offset,int whence)2354ef02da0SSiva Chandra Reddy int File::seek(long offset, int whence) {
2364ef02da0SSiva Chandra Reddy FileLock lock(this);
2374ef02da0SSiva Chandra Reddy if (prev_op == FileOp::WRITE && pos > 0) {
2384ef02da0SSiva Chandra Reddy size_t transferred_size = platform_write(this, buf, pos);
2394ef02da0SSiva Chandra Reddy if (transferred_size < pos) {
2404ef02da0SSiva Chandra Reddy err = true;
2414ef02da0SSiva Chandra Reddy return -1;
2424ef02da0SSiva Chandra Reddy }
243a0f6d12cSSiva Chandra Reddy } else if (prev_op == FileOp::READ && whence == SEEK_CUR) {
244a0f6d12cSSiva Chandra Reddy // More data could have been read out from the platform file than was
245a0f6d12cSSiva Chandra Reddy // required. So, we have to adjust the offset we pass to platform seek
246a0f6d12cSSiva Chandra Reddy // function. Note that read_limit >= pos is always true.
247a0f6d12cSSiva Chandra Reddy offset -= (read_limit - pos);
2484ef02da0SSiva Chandra Reddy }
2494ef02da0SSiva Chandra Reddy pos = read_limit = 0;
2504ef02da0SSiva Chandra Reddy prev_op = FileOp::SEEK;
2514ef02da0SSiva Chandra Reddy // Reset the eof flag as a seek might move the file positon to some place
2524ef02da0SSiva Chandra Reddy // readable.
2534ef02da0SSiva Chandra Reddy eof = false;
2544ef02da0SSiva Chandra Reddy return platform_seek(this, offset, whence);
2554ef02da0SSiva Chandra Reddy }
2564ef02da0SSiva Chandra Reddy
flush_unlocked()2576ce490e5SMichael Jones int File::flush_unlocked() {
2584ef02da0SSiva Chandra Reddy if (prev_op == FileOp::WRITE && pos > 0) {
2594ef02da0SSiva Chandra Reddy size_t transferred_size = platform_write(this, buf, pos);
2604ef02da0SSiva Chandra Reddy if (transferred_size < pos) {
2614ef02da0SSiva Chandra Reddy err = true;
2624ef02da0SSiva Chandra Reddy return -1;
2634ef02da0SSiva Chandra Reddy }
2644ef02da0SSiva Chandra Reddy pos = 0;
2654ef02da0SSiva Chandra Reddy return platform_flush(this);
2664ef02da0SSiva Chandra Reddy }
26722f9dca1SSiva Chandra Reddy // TODO: Add POSIX behavior for input streams.
2684ef02da0SSiva Chandra Reddy return 0;
2694ef02da0SSiva Chandra Reddy }
2704ef02da0SSiva Chandra Reddy
close()2714ef02da0SSiva Chandra Reddy int File::close() {
2724ef02da0SSiva Chandra Reddy {
2734ef02da0SSiva Chandra Reddy FileLock lock(this);
2744ef02da0SSiva Chandra Reddy if (prev_op == FileOp::WRITE && pos > 0) {
2754ef02da0SSiva Chandra Reddy size_t transferred_size = platform_write(this, buf, pos);
2764ef02da0SSiva Chandra Reddy if (transferred_size < pos) {
2774ef02da0SSiva Chandra Reddy err = true;
2784ef02da0SSiva Chandra Reddy return -1;
2794ef02da0SSiva Chandra Reddy }
2804ef02da0SSiva Chandra Reddy }
2814ef02da0SSiva Chandra Reddy if (platform_close(this) != 0)
2824ef02da0SSiva Chandra Reddy return -1;
2834ef02da0SSiva Chandra Reddy if (own_buf)
2844ef02da0SSiva Chandra Reddy free(buf);
2854ef02da0SSiva Chandra Reddy }
2864ef02da0SSiva Chandra Reddy free(this);
2874ef02da0SSiva Chandra Reddy return 0;
2884ef02da0SSiva Chandra Reddy }
2894ef02da0SSiva Chandra Reddy
set_buffer(void * buffer,size_t size,bool owned)2904ef02da0SSiva Chandra Reddy void File::set_buffer(void *buffer, size_t size, bool owned) {
2914ef02da0SSiva Chandra Reddy if (own_buf)
2924ef02da0SSiva Chandra Reddy free(buf);
2934ef02da0SSiva Chandra Reddy buf = buffer;
2944ef02da0SSiva Chandra Reddy bufsize = size;
2954ef02da0SSiva Chandra Reddy own_buf = owned;
2964ef02da0SSiva Chandra Reddy }
2974ef02da0SSiva Chandra Reddy
mode_flags(const char * mode)2984ef02da0SSiva Chandra Reddy File::ModeFlags File::mode_flags(const char *mode) {
2994ef02da0SSiva Chandra Reddy // First character in |mode| should be 'a', 'r' or 'w'.
3004ef02da0SSiva Chandra Reddy if (*mode != 'a' && *mode != 'r' && *mode != 'w')
3014ef02da0SSiva Chandra Reddy return 0;
3024ef02da0SSiva Chandra Reddy
3034ef02da0SSiva Chandra Reddy // There should be exaclty one main mode ('a', 'r' or 'w') character.
3044ef02da0SSiva Chandra Reddy // If there are more than one main mode characters listed, then
3054ef02da0SSiva Chandra Reddy // we will consider |mode| as incorrect and return 0;
3064ef02da0SSiva Chandra Reddy int main_mode_count = 0;
3074ef02da0SSiva Chandra Reddy
3084ef02da0SSiva Chandra Reddy ModeFlags flags = 0;
3094ef02da0SSiva Chandra Reddy for (; *mode != '\0'; ++mode) {
3104ef02da0SSiva Chandra Reddy switch (*mode) {
3114ef02da0SSiva Chandra Reddy case 'r':
3124ef02da0SSiva Chandra Reddy flags |= static_cast<ModeFlags>(OpenMode::READ);
3134ef02da0SSiva Chandra Reddy ++main_mode_count;
3144ef02da0SSiva Chandra Reddy break;
3154ef02da0SSiva Chandra Reddy case 'w':
3164ef02da0SSiva Chandra Reddy flags |= static_cast<ModeFlags>(OpenMode::WRITE);
3174ef02da0SSiva Chandra Reddy ++main_mode_count;
3184ef02da0SSiva Chandra Reddy break;
3194ef02da0SSiva Chandra Reddy case '+':
32029a631a2SSiva Chandra Reddy flags |= static_cast<ModeFlags>(OpenMode::PLUS);
3214ef02da0SSiva Chandra Reddy break;
3224ef02da0SSiva Chandra Reddy case 'b':
3234ef02da0SSiva Chandra Reddy flags |= static_cast<ModeFlags>(ContentType::BINARY);
3244ef02da0SSiva Chandra Reddy break;
3254ef02da0SSiva Chandra Reddy case 'a':
3264ef02da0SSiva Chandra Reddy flags |= static_cast<ModeFlags>(OpenMode::APPEND);
3274ef02da0SSiva Chandra Reddy ++main_mode_count;
3284ef02da0SSiva Chandra Reddy break;
3294ef02da0SSiva Chandra Reddy case 'x':
3304ef02da0SSiva Chandra Reddy flags |= static_cast<ModeFlags>(CreateType::EXCLUSIVE);
3314ef02da0SSiva Chandra Reddy break;
3324ef02da0SSiva Chandra Reddy default:
3334ef02da0SSiva Chandra Reddy return 0;
3344ef02da0SSiva Chandra Reddy }
3354ef02da0SSiva Chandra Reddy }
3364ef02da0SSiva Chandra Reddy
3374ef02da0SSiva Chandra Reddy if (main_mode_count != 1)
3384ef02da0SSiva Chandra Reddy return 0;
3394ef02da0SSiva Chandra Reddy
3404ef02da0SSiva Chandra Reddy return flags;
3414ef02da0SSiva Chandra Reddy }
3424ef02da0SSiva Chandra Reddy
3434ef02da0SSiva Chandra Reddy } // namespace __llvm_libc
344