1#ifndef File_h
2#define File_h
3
4#ifndef Global_h
5#include "Global.h"
6#endif
7
8#include <unistd.h>
9#include <fcntl.h>
10#include <sys/stat.h>
11
12#ifndef _LARGEFILE64_SOURCE
13#define lseek64 ::lseek
14#define open64  ::open
15#endif
16
17
18//=================================================================================================
19// A buffered file abstraction with only 'putChar()' and 'getChar()'.
20
21
22#define File_BufSize 1024   // A small buffer seem to work just as fine as a big one (at least under Linux)
23
24enum FileMode { READ, WRITE };
25
26class Exception_EOF {};
27
28
29// WARNING! This code is not thoroughly tested. May contain bugs!
30
31class File {
32    int         fd;         // Underlying file descriptor.
33    FileMode    mode;       // Reading or writing.
34    uchar*      buf;        // Read or write buffer.
35    int         size;       // Size of buffer (at end of file, less than 'File_BufSize').
36    int         pos;        // Current position in buffer
37    bool        own_fd;     // Do we own the file descriptor? If so, will close file in destructor.
38
39public:
40    #define DEFAULTS fd(-1), mode(READ), buf(NULL), size(-1), pos(0), own_fd(true)
41    File(void) : DEFAULTS {}
42
43    File(int fd, FileMode mode, bool own_fd = true) : DEFAULTS  {
44        open(fd, mode, own_fd); }
45
46    File(cchar* name, cchar* mode) : DEFAULTS {
47        open(name, mode); }
48    #undef DEFAULTS
49
50   ~File(void) {
51        close(); }
52
53   // Low-level open. If 'own_fd' is FALSE, descriptor will not be closed by destructor.
54    void open(int fd, FileMode mode, bool own_fd = true);
55    void open(cchar* name, cchar* mode);                     // FILE* compatible interface.
56    void close(void);
57
58    bool null(void) {               // TRUE if no file is opened.
59        return fd == -1; }
60
61    // Don't run UNIX function 'close()' on descriptor in 'File's 'close()'.
62    int releaseDescriptor(void) {
63        if (mode == READ)
64            lseek64(fd, pos - size, SEEK_CUR);
65        own_fd = false;
66        return fd; }
67
68    FileMode getMode(void) {
69        return mode; }
70
71    void setMode(FileMode m) {
72        if (m == mode) return;
73        if (m == READ){
74            flush();
75            size = read(fd, buf, File_BufSize);
76        }else{
77            lseek64(fd, pos - size, SEEK_CUR);
78            size = -1; }
79        mode = m;
80        pos = 0; }
81
82    int getCharQ(void) { // Quick version with minimal overhead : don't call this in wrong mode!
83      #ifdef PARANOID
84        assert(mode == READ);
85      #endif
86        if (pos < size) return (uchar)buf[pos++];
87        if (size < File_BufSize) return EOF;
88        size = read(fd, buf, File_BufSize);
89        pos  = 0;
90        if (size == 0) return EOF;
91        return (uchar)buf[pos++]; }
92
93    int putCharQ(int chr) { // Quick version with minimal overhead : don't call this in wrong mode!
94      #ifdef PARANOID
95        assert(mode == WRITE);
96      #endif
97        if (pos == File_BufSize)
98            write(fd, buf, File_BufSize),
99            pos = 0;
100        return buf[pos++] = (uchar)chr; }
101
102    int getChar(void) {
103        if (mode == WRITE) setMode(READ);
104        return getCharQ(); }
105
106    int putChar(int chr) {
107        if (mode == READ) setMode(WRITE);
108        return putCharQ(chr); }
109
110    bool eof(void) {
111        assert(mode == READ);
112        if (pos < size) return false;
113        if (size < File_BufSize) return true;
114        size = read(fd, buf, File_BufSize);
115        pos  = 0;
116        if (size == 0) return true;
117        return false; }
118
119    void flush(void) {
120        assert(mode == WRITE);
121        write(fd, buf, pos);
122        pos = 0; }
123
124    void  seek(int64 pos, int whence = SEEK_SET);
125    int64 tell(void);
126};
127
128
129//=================================================================================================
130// Some nice helper functions:
131
132
133void                 putUInt (File& out, uint64 val);
134uint64               getUInt (File& in) throw(Exception_EOF);
135static inline uint64 encode64(int64  val)           { return (val >= 0) ? (uint64)val << 1 : (((uint64)(~val) << 1) | 1); }
136static inline int64  decode64(uint64 val)           { return ((val & 1) == 0) ? (int64)(val >> 1) : ~(int64)(val >> 1); }
137static inline void   putInt  (File& out, int64 val) { putUInt(out, encode64(val)); }
138static inline uint64 getInt  (File& in)             { return decode64(getUInt(in)); }
139
140
141//=================================================================================================
142#endif
143