1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
|
// *****************************************************************************
// * This file is part of the FreeFileSync project. It is distributed under *
// * GNU General Public License: https://www.gnu.org/licenses/gpl-3.0 *
// * Copyright (C) Zenju (zenju AT freefilesync DOT org) - All Rights Reserved *
// *****************************************************************************
#ifndef FILE_IO_H_89578342758342572345
#define FILE_IO_H_89578342758342572345
#include "file_error.h"
#include "serialize.h"
namespace zen
{
const char LINE_BREAK[] = "\n"; //since OS X Apple uses newline, too
/*
OS-buffered file IO optimized for
- sequential read/write accesses
- better error reporting
- long path support
- follows symlinks
*/
class FileBase
{
public:
const Zstring& getFilePath() const { return filePath_; }
using FileHandle = int;
FileHandle getHandle() { return fileHandle_; }
//Windows: use 64kB ?? https://technet.microsoft.com/en-us/library/cc938632
//Linux: use st_blksize?
//macOS: use f_iosize?
static size_t getBlockSize() { return 128 * 1024; };
protected:
FileBase(FileHandle handle, const Zstring& filePath) : fileHandle_(handle), filePath_(filePath) {}
~FileBase();
void close(); //throw FileError -> optional, but good place to catch errors when closing stream!
static const FileHandle invalidHandleValue;
private:
FileBase (const FileBase&) = delete;
FileBase& operator=(const FileBase&) = delete;
FileHandle fileHandle_ = invalidHandleValue;
const Zstring filePath_;
};
//-----------------------------------------------------------------------------------------------
class FileInput : public FileBase
{
public:
FileInput(const Zstring& filePath, const IOCallback& notifyUnbufferedIO); //throw FileError, ErrorFileLocked
FileInput(FileHandle handle, const Zstring& filePath, const IOCallback& notifyUnbufferedIO); //takes ownership!
size_t read(void* buffer, size_t bytesToRead); //throw FileError, ErrorFileLocked, X; return "bytesToRead" bytes unless end of stream!
private:
size_t tryRead(void* buffer, size_t bytesToRead); //throw FileError, ErrorFileLocked; may return short, only 0 means EOF! => CONTRACT: bytesToRead > 0!
const IOCallback notifyUnbufferedIO_; //throw X
std::vector<std::byte> memBuf_ = std::vector<std::byte>(getBlockSize());
size_t bufPos_ = 0;
size_t bufPosEnd_= 0;
};
class FileOutput : public FileBase
{
public:
enum AccessFlag
{
ACC_OVERWRITE,
ACC_CREATE_NEW
};
FileOutput(const Zstring& filePath, AccessFlag access, const IOCallback& notifyUnbufferedIO); //throw FileError, ErrorTargetExisting
FileOutput(FileHandle handle, const Zstring& filePath, const IOCallback& notifyUnbufferedIO); //takes ownership!
~FileOutput();
void preAllocateSpaceBestEffort(uint64_t expectedSize); //throw FileError
void write(const void* buffer, size_t bytesToWrite); //throw FileError, X
void flushBuffers(); //throw FileError, X
void finalize(); /*= flushBuffers() + close()*/ //throw FileError, X
private:
size_t tryWrite(const void* buffer, size_t bytesToWrite); //throw FileError; may return short! CONTRACT: bytesToWrite > 0
IOCallback notifyUnbufferedIO_; //throw X
std::vector<std::byte> memBuf_ = std::vector<std::byte>(getBlockSize());
size_t bufPos_ = 0;
size_t bufPosEnd_ = 0;
};
//-----------------------------------------------------------------------------------------------
//native stream I/O convenience functions:
template <class BinContainer> inline
BinContainer loadBinContainer(const Zstring& filePath, //throw FileError
const IOCallback& notifyUnbufferedIO)
{
FileInput streamIn(filePath, notifyUnbufferedIO); //throw FileError, ErrorFileLocked
return bufferedLoad<BinContainer>(streamIn); //throw FileError, X;
}
template <class BinContainer> inline
void saveBinContainer(const Zstring& filePath, const BinContainer& buffer, //throw FileError
const IOCallback& notifyUnbufferedIO)
{
FileOutput fileOut(filePath, FileOutput::ACC_OVERWRITE, notifyUnbufferedIO); //throw FileError, (ErrorTargetExisting)
if (!buffer.empty())
{
/*snake oil?*/ fileOut.preAllocateSpaceBestEffort(buffer.size()); //throw FileError
fileOut.write(&*buffer.begin(), buffer.size()); //throw FileError, X
}
fileOut.finalize(); //throw FileError, X
}
}
#endif //FILE_IO_H_89578342758342572345
|