• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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