//===--- A platform independent file data structure -------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #ifndef LLVM_LIBC_SRC_SUPPORT_OSUTIL_FILE_H #define LLVM_LIBC_SRC_SUPPORT_OSUTIL_FILE_H #include "src/__support/threads/mutex.h" #include #include namespace __llvm_libc { // This a generic base class to encapsulate a platform independent file data // structure. Platform specific specializations should create a subclass as // suitable for their platform. class File { public: static constexpr size_t DEFAULT_BUFFER_SIZE = 1024; using LockFunc = void(File *); using UnlockFunc = void(File *); using WriteFunc = size_t(File *, const void *, size_t); using ReadFunc = size_t(File *, void *, size_t); using SeekFunc = int(File *, long, int); using CloseFunc = int(File *); using FlushFunc = int(File *); using ModeFlags = uint32_t; // The three different types of flags below are to be used with '|' operator. // Their values correspond to mutually exclusive bits in a 32-bit unsigned // integer value. A flag set can include both READ and WRITE if the file // is opened in update mode (ie. if the file was opened with a '+' the mode // string.) enum class OpenMode : ModeFlags { READ = 0x1, WRITE = 0x2, APPEND = 0x4, PLUS = 0x8, }; // Denotes a file opened in binary mode (which is specified by including // the 'b' character in teh mode string.) enum class ContentType : ModeFlags { BINARY = 0x10, }; // Denotes a file to be created for writing. enum class CreateType : ModeFlags { EXCLUSIVE = 0x100, }; private: enum class FileOp : uint8_t { NONE, READ, WRITE, SEEK }; // Platfrom specific functions which create new file objects should initialize // these fields suitably via the constructor. Typically, they should be simple // syscall wrappers for the corresponding functionality. WriteFunc *platform_write; ReadFunc *platform_read; SeekFunc *platform_seek; CloseFunc *platform_close; FlushFunc *platform_flush; Mutex mutex; void *buf; // Pointer to the stream buffer for buffered streams size_t bufsize; // Size of the buffer pointed to by |buf|. // Buffering mode to used to buffer. int bufmode; // If own_buf is true, the |buf| is owned by the stream and will be // free-ed when close method is called on the stream. bool own_buf; // The mode in which the file was opened. ModeFlags mode; // Current read or write pointer. size_t pos; // Represents the previous operation that was performed. FileOp prev_op; // When the buffer is used as a read buffer, read_limit is the upper limit // of the index to which the buffer can be read until. size_t read_limit; bool eof; bool err; // This is a convenience RAII class to lock and unlock file objects. class FileLock { File *file; public: explicit FileLock(File *f) : file(f) { file->lock(); } ~FileLock() { file->unlock(); } FileLock(const FileLock &) = delete; FileLock(FileLock &&) = delete; }; protected: bool write_allowed() const { return mode & (static_cast(OpenMode::WRITE) | static_cast(OpenMode::APPEND) | static_cast(OpenMode::PLUS)); } bool read_allowed() const { return mode & (static_cast(OpenMode::READ) | static_cast(OpenMode::PLUS)); } public: // We want this constructor to be constexpr so that global file objects // like stdout do not require invocation of the constructor which can // potentially lead to static initialization order fiasco. constexpr File(WriteFunc *wf, ReadFunc *rf, SeekFunc *sf, CloseFunc *cf, FlushFunc *ff, void *buffer, size_t buffer_size, int buffer_mode, bool owned, ModeFlags modeflags) : platform_write(wf), platform_read(rf), platform_seek(sf), platform_close(cf), platform_flush(ff), mutex(false, false, false), buf(buffer), bufsize(buffer_size), bufmode(buffer_mode), own_buf(owned), mode(modeflags), pos(0), prev_op(FileOp::NONE), read_limit(0), eof(false), err(false) {} // This function helps initialize the various fields of the File data // structure after a allocating memory for it via a call to malloc. static void init(File *f, WriteFunc *wf, ReadFunc *rf, SeekFunc *sf, CloseFunc *cf, FlushFunc *ff, void *buffer, size_t buffer_size, int buffer_mode, bool owned, ModeFlags modeflags) { Mutex::init(&f->mutex, false, false, false); f->platform_write = wf; f->platform_read = rf; f->platform_seek = sf; f->platform_close = cf; f->platform_flush = ff; f->buf = reinterpret_cast(buffer); f->bufsize = buffer_size; f->bufmode = buffer_mode; f->own_buf = owned; f->mode = modeflags; f->prev_op = FileOp::NONE; f->read_limit = f->pos = 0; f->eof = f->err = false; } // Buffered write of |len| bytes from |data| without the file lock. size_t write_unlocked(const void *data, size_t len); // Buffered write of |len| bytes from |data| under the file lock. size_t write(const void *data, size_t len) { FileLock l(this); return write_unlocked(data, len); } // Buffered read of |len| bytes into |data| without the file lock. size_t read_unlocked(void *data, size_t len); // Buffered read of |len| bytes into |data| under the file lock. size_t read(void *data, size_t len) { FileLock l(this); return read_unlocked(data, len); } int seek(long offset, int whence); // If buffer has data written to it, flush it out. Does nothing if the // buffer is currently being used as a read buffer. int flush() { FileLock lock(this); return flush_unlocked(); } int flush_unlocked(); // Sets the internal buffer to |buffer| with buffering mode |mode|. // |size| is the size of |buffer|. This new |buffer| is owned by the // stream only if |owned| is true. void set_buffer(void *buffer, size_t size, bool owned); // Closes the file stream and frees up all resources owned by it. int close(); void lock() { mutex.lock(); } void unlock() { mutex.unlock(); } bool error_unlocked() const { return err; } bool error() { FileLock l(this); return error_unlocked(); } void clearerr_unlocked() { err = false; } void clearerr() { FileLock l(this); clearerr_unlocked(); } bool iseof_unlocked() { return eof; } bool iseof() { FileLock l(this); return iseof_unlocked(); } // Returns an bit map of flags corresponding to enumerations of // OpenMode, ContentType and CreateType. static ModeFlags mode_flags(const char *mode); private: size_t write_unlocked_lbf(const void *data, size_t len); size_t write_unlocked_fbf(const void *data, size_t len); size_t write_unlocked_nbf(const void *data, size_t len); }; // The implementaiton of this function is provided by the platfrom_file // library. File *openfile(const char *path, const char *mode); extern File *stdout; extern File *stderr; } // namespace __llvm_libc #endif // LLVM_LIBC_SRC_SUPPORT_OSUTIL_FILE_H