brintos

brintos / llvm-project-archived public Read only

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