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_OSUTIL_FILE_H
10 #define LLVM_LIBC_SRC_SUPPORT_OSUTIL_FILE_H
11 
12 #include <stddef.h>
13 #include <stdint.h>
14 
15 namespace __llvm_libc {
16 
17 // This a generic base class to encapsulate a platform independent file data
18 // structure. Platform specific specializations should create a subclass as
19 // suitable for their platform.
20 class File {
21 public:
22   using LockFunc = void(File *);
23   using UnlockFunc = void(File *);
24 
25   using WriteFunc = size_t(File *, const void *, size_t);
26   using ReadFunc = size_t(File *, void *, size_t);
27   using SeekFunc = int(File *, long, int);
28   using CloseFunc = int(File *);
29   using FlushFunc = int(File *);
30 
31   using ModeFlags = uint32_t;
32 
33   // The three different types of flags below are to be used with '|' operator.
34   // Their values correspond to mutually exclusive bits in a 32-bit unsigned
35   // integer value. A flag set can include both READ and WRITE if the file
36   // is opened in update mode (ie. if the file was opened with a '+' the mode
37   // string.)
38   enum class OpenMode : ModeFlags {
39     READ = 0x1,
40     WRITE = 0x2,
41     APPEND = 0x4,
42   };
43 
44   // Denotes a file opened in binary mode (which is specified by including
45   // the 'b' character in teh mode string.)
46   enum class ContentType : ModeFlags {
47     BINARY = 0x10,
48   };
49 
50   // Denotes a file to be created for writing.
51   enum class CreateType : ModeFlags {
52     EXCLUSIVE = 0x100,
53   };
54 
55 private:
56   enum class FileOp : uint8_t { NONE, READ, WRITE, SEEK };
57 
58   // Platfrom specific functions which create new file objects should initialize
59   // these fields suitably via the constructor. Typically, they should be simple
60   // syscall wrappers for the corresponding functionality.
61   WriteFunc *platform_write;
62   ReadFunc *platform_read;
63   SeekFunc *platform_seek;
64   CloseFunc *platform_close;
65   FlushFunc *platform_flush;
66 
67   // Platform specific functions to lock and unlock file for mutually exclusive
68   // access from threads in a multi-threaded application.
69   LockFunc *platform_lock;
70   UnlockFunc *platform_unlock;
71 
72   void *buf;      // Pointer to the stream buffer for buffered streams
73   size_t bufsize; // Size of the buffer pointed to by |buf|.
74 
75   // Buffering mode to used to buffer.
76   int bufmode;
77 
78   // If own_buf is true, the |buf| is owned by the stream and will be
79   // free-ed when close method is called on the stream.
80   bool own_buf;
81 
82   // The mode in which the file was opened.
83   ModeFlags mode;
84 
85   // Current read or write pointer.
86   size_t pos;
87 
88   // Represents the previous operation that was performed.
89   FileOp prev_op;
90 
91   // When the buffer is used as a read buffer, read_limit is the upper limit
92   // of the index to which the buffer can be read until.
93   size_t read_limit;
94 
95   bool eof;
96   bool err;
97 
98 protected:
99   bool write_allowed() const {
100     return mode & (static_cast<ModeFlags>(OpenMode::WRITE) |
101                    static_cast<ModeFlags>(OpenMode::APPEND));
102   }
103 
104   bool read_allowed() const {
105     return mode & static_cast<ModeFlags>(OpenMode::READ);
106   }
107 
108 public:
109   // We want this constructor to be constexpr so that global file objects
110   // like stdout do not require invocation of the constructor which can
111   // potentially lead to static initialization order fiasco.
112   constexpr File(WriteFunc *wf, ReadFunc *rf, SeekFunc *sf, CloseFunc *cf,
113                  FlushFunc *ff, LockFunc *lf, UnlockFunc *ulf, void *buffer,
114                  size_t buffer_size, int buffer_mode, bool owned,
115                  ModeFlags modeflags)
116       : platform_write(wf), platform_read(rf), platform_seek(sf),
117         platform_close(cf), platform_flush(ff), platform_lock(lf),
118         platform_unlock(ulf), buf(buffer), bufsize(buffer_size),
119         bufmode(buffer_mode), own_buf(owned), mode(modeflags), pos(0),
120         prev_op(FileOp::NONE), read_limit(0), eof(false), err(false) {}
121 
122   // This function helps initialize the various fields of the File data
123   // structure after a allocating memory for it via a call to malloc.
124   static void init(File *f, WriteFunc *wf, ReadFunc *rf, SeekFunc *sf,
125                    CloseFunc *cf, FlushFunc *ff, LockFunc *lf, UnlockFunc *ulf,
126                    void *buffer, size_t buffer_size, int buffer_mode,
127                    bool owned, ModeFlags modeflags) {
128     f->platform_write = wf;
129     f->platform_read = rf;
130     f->platform_seek = sf;
131     f->platform_close = cf;
132     f->platform_flush = ff;
133     f->platform_lock = lf;
134     f->platform_unlock = ulf;
135     f->buf = reinterpret_cast<uint8_t *>(buffer);
136     f->bufsize = buffer_size;
137     f->bufmode = buffer_mode;
138     f->own_buf = owned;
139     f->mode = modeflags;
140 
141     f->prev_op = FileOp::NONE;
142     f->read_limit = f->pos = 0;
143     f->eof = f->err = false;
144   }
145 
146   // Buffered write of |len| bytes from |data|.
147   size_t write(const void *data, size_t len);
148 
149   // Buffered read of |len| bytes into |data|.
150   size_t read(void *data, size_t len);
151 
152   int seek(long offset, int whence);
153 
154   // If buffer has data written to it, flush it out. Does nothing if the
155   // buffer is currently being used as a read buffer.
156   int flush();
157 
158   // Sets the internal buffer to |buffer| with buffering mode |mode|.
159   // |size| is the size of |buffer|. This new |buffer| is owned by the
160   // stream only if |owned| is true.
161   void set_buffer(void *buffer, size_t size, bool owned);
162 
163   // Closes the file stream and frees up all resources owned by it.
164   int close();
165 
166   void lock() { platform_lock(this); }
167   void unlock() { platform_unlock(this); }
168 
169   bool error() const { return err; }
170   void clearerr() { err = false; }
171   bool iseof() const { return eof; }
172 
173   // Returns an bit map of flags corresponding to enumerations of
174   // OpenMode, ContentType and CreateType.
175   static ModeFlags mode_flags(const char *mode);
176 };
177 
178 // This is a convenience RAII class to lock and unlock file objects.
179 class FileLock {
180   File *file;
181 
182 public:
183   explicit FileLock(File *f) : file(f) { file->lock(); }
184 
185   ~FileLock() { file->unlock(); }
186 
187   FileLock(const FileLock &) = delete;
188   FileLock(FileLock &&) = delete;
189 };
190 
191 } // namespace __llvm_libc
192 
193 #endif // LLVM_LIBC_SRC_SUPPORT_OSUTIL_FILE_H
194