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