brintos

brintos / llvm-project-archived public Read only

0
0
Text · 10.8 KiB · 3652e44 Raw
325 lines · c
1//===--- A platform independent file data structure -------------*- C++ -*-===//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#ifndef LLVM_LIBC_SRC___SUPPORT_FILE_FILE_H10#define LLVM_LIBC_SRC___SUPPORT_FILE_FILE_H11 12#include "hdr/stdint_proxy.h"13#include "hdr/stdio_macros.h"14#include "hdr/types/off_t.h"15#include "src/__support/CPP/new.h"16#include "src/__support/error_or.h"17#include "src/__support/macros/config.h"18#include "src/__support/macros/properties/architectures.h"19#include "src/__support/threads/mutex.h"20 21#include <stddef.h>22 23namespace LIBC_NAMESPACE_DECL {24 25struct FileIOResult {26  size_t value;27  int error;28 29  constexpr FileIOResult(size_t val) : value(val), error(0) {}30  constexpr FileIOResult(size_t val, int error) : value(val), error(error) {}31 32  constexpr bool has_error() { return error != 0; }33 34  constexpr operator size_t() { return value; }35};36 37// This a generic base class to encapsulate a platform independent file data38// structure. Platform specific specializations should create a subclass as39// suitable for their platform.40class File {41public:42  static constexpr size_t DEFAULT_BUFFER_SIZE = 1024;43 44  using LockFunc = void(File *);45  using UnlockFunc = void(File *);46 47  using WriteFunc = FileIOResult(File *, const void *, size_t);48  using ReadFunc = FileIOResult(File *, void *, size_t);49  // The SeekFunc is expected to return the current offset of the external50  // file position indicator.51  using SeekFunc = ErrorOr<off_t>(File *, off_t, int);52  using CloseFunc = int(File *);53 54  using ModeFlags = uint32_t;55 56  // The three different types of flags below are to be used with '|' operator.57  // Their values correspond to mutually exclusive bits in a 32-bit unsigned58  // integer value. A flag set can include both READ and WRITE if the file59  // is opened in update mode (ie. if the file was opened with a '+' the mode60  // string.)61  enum class OpenMode : ModeFlags {62    READ = 0x1,63    WRITE = 0x2,64    APPEND = 0x4,65    PLUS = 0x8,66  };67 68  // Denotes a file opened in binary mode (which is specified by including69  // the 'b' character in teh mode string.)70  enum class ContentType : ModeFlags {71    BINARY = 0x10,72  };73 74  // Denotes a file to be created for writing.75  enum class CreateType : ModeFlags {76    EXCLUSIVE = 0x100,77  };78 79private:80  enum class FileOp : uint8_t { NONE, READ, WRITE, SEEK };81 82  // Platform specific functions which create new file objects should initialize83  // these fields suitably via the constructor. Typically, they should be simple84  // syscall wrappers for the corresponding functionality.85  WriteFunc *platform_write;86  ReadFunc *platform_read;87  SeekFunc *platform_seek;88  CloseFunc *platform_close;89 90  Mutex mutex;91 92  // For files which are readable, we should be able to support one ungetc93  // operation even if |buf| is nullptr. So, in the constructor of File, we94  // set |buf| to point to this buffer character.95  uint8_t ungetc_buf;96 97  uint8_t *buf;   // Pointer to the stream buffer for buffered streams98  size_t bufsize; // Size of the buffer pointed to by |buf|.99 100  // Buffering mode to used to buffer.101  int bufmode;102 103  // If own_buf is true, the |buf| is owned by the stream and will be104  // free-ed when close method is called on the stream.105  bool own_buf;106 107  // The mode in which the file was opened.108  ModeFlags mode;109 110  // Current read or write pointer.111  size_t pos;112 113  // Represents the previous operation that was performed.114  FileOp prev_op;115 116  // When the buffer is used as a read buffer, read_limit is the upper limit117  // of the index to which the buffer can be read until.118  size_t read_limit;119 120  bool eof;121  bool err;122 123  // This is a convenience RAII class to lock and unlock file objects.124  class FileLock {125    File *file;126 127  public:128    explicit FileLock(File *f) : file(f) { file->lock(); }129 130    ~FileLock() { file->unlock(); }131 132    FileLock(const FileLock &) = delete;133    FileLock(FileLock &&) = delete;134  };135 136protected:137  constexpr bool write_allowed() const {138    return mode & (static_cast<ModeFlags>(OpenMode::WRITE) |139                   static_cast<ModeFlags>(OpenMode::APPEND) |140                   static_cast<ModeFlags>(OpenMode::PLUS));141  }142 143  constexpr bool read_allowed() const {144    return mode & (static_cast<ModeFlags>(OpenMode::READ) |145                   static_cast<ModeFlags>(OpenMode::PLUS));146  }147 148public:149  // We want this constructor to be constexpr so that global file objects150  // like stdout do not require invocation of the constructor which can151  // potentially lead to static initialization order fiasco. Consequently,152  // we will assume that the |buffer| and |buffer_size| argument are153  // meaningful - that is, |buffer| is nullptr if and only if |buffer_size|154  // is zero. This way, we will not have to employ the semantics of155  // the set_buffer method and allocate a buffer.156  constexpr File(WriteFunc *wf, ReadFunc *rf, SeekFunc *sf, CloseFunc *cf,157                 uint8_t *buffer, size_t buffer_size, int buffer_mode,158                 bool owned, ModeFlags modeflags)159      : platform_write(wf), platform_read(rf), platform_seek(sf),160        platform_close(cf), mutex(/*timed=*/false, /*recursive=*/false,161                                  /*robust=*/false, /*pshared=*/false),162        ungetc_buf(0), buf(buffer), bufsize(buffer_size), bufmode(buffer_mode),163        own_buf(owned), mode(modeflags), pos(0), prev_op(FileOp::NONE),164        read_limit(0), eof(false), err(false) {165    adjust_buf();166  }167 168  // Buffered write of |len| bytes from |data| without the file lock.169  FileIOResult write_unlocked(const void *data, size_t len);170 171  // Buffered write of |len| bytes from |data| under the file lock.172  FileIOResult write(const void *data, size_t len) {173    FileLock l(this);174    return write_unlocked(data, len);175  }176 177  // Buffered read of |len| bytes into |data| without the file lock.178  FileIOResult read_unlocked(void *data, size_t len);179 180  // Buffered read of |len| bytes into |data| under the file lock.181  FileIOResult read(void *data, size_t len) {182    FileLock l(this);183    return read_unlocked(data, len);184  }185 186  ErrorOr<int> seek(off_t offset, int whence);187 188  ErrorOr<off_t> tell();189 190  // If buffer has data written to it, flush it out. Does nothing if the191  // buffer is currently being used as a read buffer.192  int flush() {193    FileLock lock(this);194    return flush_unlocked();195  }196 197  int flush_unlocked();198 199  // Returns EOF on error and keeps the file unchanged.200  int ungetc_unlocked(int c);201 202  int ungetc(int c) {203    FileLock lock(this);204    return ungetc_unlocked(c);205  }206 207  // Does the following:208  // 1. If in write mode, Write out any data present in the buffer.209  // 2. Call platform_close.210  // platform_close is expected to cleanup the complete file object.211  int close() {212    {213      FileLock lock(this);214      if (prev_op == FileOp::WRITE && pos > 0) {215        auto buf_result = platform_write(this, buf, pos);216        if (buf_result.has_error() || buf_result.value < pos) {217          err = true;218          return buf_result.error;219        }220      }221    }222 223    // If we own the buffer, delete it before calling the platform close224    // implementation. The platform close should not need to access the buffer225    // and we need to clean it up before the entire structure is removed.226    if (own_buf)227      delete buf;228 229    // Platform close is expected to cleanup the file data structure which230    // includes the file mutex. Hence, we call platform_close after releasing231    // the file lock. Another thread doing file operations while a thread is232    // closing the file is undefined behavior as per POSIX.233    return platform_close(this);234  }235 236  // Sets the internal buffer to |buffer| with buffering mode |mode|.237  // |size| is the size of |buffer|. If |size| is non-zero, but |buffer|238  // is nullptr, then a buffer owned by this file will be allocated.239  // Else, |buffer| will not be owned by this file.240  //241  // Will return zero on success, or an error value on failure. Will fail242  // if:243  //   1. |buffer| is not a nullptr but |size| is zero.244  //   2. |buffer_mode| is not one of _IOLBF, IOFBF or _IONBF.245  //   3. If an allocation was required but the allocation failed.246  // For cases 1 and 2, the error returned in EINVAL. For case 3, error returned247  // is ENOMEM.248  int set_buffer(void *buffer, size_t size, int buffer_mode);249 250  void lock() { mutex.lock(); }251  void unlock() { mutex.unlock(); }252 253  bool error_unlocked() const { return err; }254 255  bool error() {256    FileLock l(this);257    return error_unlocked();258  }259 260  void clearerr_unlocked() { err = false; }261 262  void clearerr() {263    FileLock l(this);264    clearerr_unlocked();265  }266 267  bool iseof_unlocked() { return eof; }268 269  bool iseof() {270    FileLock l(this);271    return iseof_unlocked();272  }273 274  // Returns an bit map of flags corresponding to enumerations of275  // OpenMode, ContentType and CreateType.276  static ModeFlags mode_flags(const char *mode);277 278private:279  FileIOResult write_unlocked_lbf(const uint8_t *data, size_t len);280  FileIOResult write_unlocked_fbf(const uint8_t *data, size_t len);281  FileIOResult write_unlocked_nbf(const uint8_t *data, size_t len);282 283  FileIOResult read_unlocked_fbf(uint8_t *data, size_t len);284  FileIOResult read_unlocked_nbf(uint8_t *data, size_t len);285  size_t copy_data_from_buf(uint8_t *data, size_t len);286 287  constexpr void adjust_buf() {288    if (read_allowed() && (buf == nullptr || bufsize == 0)) {289      // We should allow atleast one ungetc operation.290      // This might give an impression that a buffer will be used even when291      // the user does not want a buffer. But, that will not be the case.292      // For reading, the buffering does not come into play. For writing, let293      // us take up the three different kinds of buffering separately:294      // 1. If user wants _IOFBF but gives a zero buffer, buffering still295      //    happens in the OS layer until the user flushes. So, from the user's296      //    point of view, this single byte buffer does not affect their297      //    experience.298      // 2. If user wants _IOLBF but gives a zero buffer, the reasoning is299      //    very similar to the _IOFBF case.300      // 3. If user wants _IONBF, then the buffer is ignored for writing.301      // So, all of the above cases, having a single ungetc buffer does not302      // affect the behavior experienced by the user.303      buf = &ungetc_buf;304      bufsize = 1;305      own_buf = false; // We shouldn't call free on |buf| when closing the file.306    }307  }308};309 310// The implementaiton of this function is provided by the platform_file311// library.312ErrorOr<File *> openfile(const char *path, const char *mode);313 314// The platform_file library should implement it if it relevant for that315// platform.316int get_fileno(File *f);317 318extern File *stdin;319extern File *stdout;320extern File *stderr;321 322} // namespace LIBC_NAMESPACE_DECL323 324#endif // LLVM_LIBC_SRC___SUPPORT_FILE_FILE_H325