C++ Utilities  4.16.0
Useful C++ classes and routines such as argument parser, IO and conversion utilities
nativefilestream.cpp
Go to the documentation of this file.
1 #include "./nativefilestream.h"
2 
3 #ifdef CPP_UTILITIES_USE_NATIVE_FILE_BUFFER
4 #include "./catchiofailure.h"
5 
6 #ifdef PLATFORM_WINDOWS
7 #include "../conversion/stringconversion.h"
8 #endif
9 
10 // include header files for file buffer implementation
11 #if defined(CPP_UTILITIES_USE_GNU_CXX_STDIO_FILEBUF)
12 #include <ext/stdio_filebuf.h>
13 #elif defined(CPP_UTILITIES_USE_BOOST_IOSTREAMS)
14 #include <boost/iostreams/device/file_descriptor.hpp>
15 #include <boost/iostreams/stream.hpp>
16 #else
17 #error "Configuration for NativeFileStream backend insufficient."
18 #endif
19 
20 // include platform specific header
21 #if defined(PLATFORM_UNIX)
22 #include <cstdio>
23 #include <fcntl.h>
24 #include <sys/stat.h>
25 #include <sys/types.h>
26 #elif defined(PLATFORM_WINDOWS)
27 #include <fcntl.h>
28 #include <io.h>
29 #include <sys/stat.h> // yes, this is needed under Windows (https://msdn.microsoft.com/en-US/library/5yhhz3y7.aspx)
30 #include <windows.h>
31 #endif
32 
33 #endif
34 
35 using namespace std;
36 
37 namespace IoUtilities {
38 
39 #ifdef CPP_UTILITIES_USE_NATIVE_FILE_BUFFER
40 
41 #ifdef CPP_UTILITIES_USE_GNU_CXX_STDIO_FILEBUF
42 using StreamBuffer = __gnu_cxx::stdio_filebuf<char>;
43 #else // CPP_UTILITIES_USE_BOOST_IOSTREAMS
44 using StreamBuffer = boost::iostreams::stream_buffer<boost::iostreams::file_descriptor>;
45 #endif
46 
47 struct NativeFileParams {
48 
49 #ifdef PLATFORM_WINDOWS
50  NativeFileParams(ios_base::openmode cppOpenMode)
51  : openMode(cppOpenMode & ios_base::binary ? _O_BINARY : 0)
52  , flags(cppOpenMode & ios_base::binary ? 0 : _O_TEXT)
53  , permissions(0)
54  , access(0)
55  , shareMode(0)
56  , creation(0)
57  {
58  if ((cppOpenMode & ios_base::out) && (cppOpenMode & ios_base::in)) {
59  openMode |= _O_RDWR;
60  access = GENERIC_READ | GENERIC_WRITE;
61  shareMode = FILE_SHARE_READ;
62  creation = OPEN_EXISTING;
63  } else if (cppOpenMode & ios_base::out) {
64  openMode |= _O_WRONLY | _O_CREAT;
65  permissions = _S_IREAD | _S_IWRITE;
66  access = GENERIC_WRITE;
67  creation = OPEN_ALWAYS;
68  } else if (cppOpenMode & ios_base::in) {
69  openMode |= _O_RDONLY;
70  flags |= _O_RDONLY;
71  access = GENERIC_READ;
72  shareMode = FILE_SHARE_READ;
73  creation = OPEN_EXISTING;
74  }
75  if (cppOpenMode & ios_base::app) {
76  openMode |= _O_APPEND;
77  flags |= _O_APPEND;
78  }
79  if (cppOpenMode & ios_base::trunc) {
80  openMode |= _O_TRUNC;
81  creation = (cppOpenMode & ios_base::in) ? TRUNCATE_EXISTING : CREATE_ALWAYS;
82  }
83  }
84 
85  int openMode;
86  int flags;
87  int permissions;
88  DWORD access;
89  DWORD shareMode;
90  DWORD creation;
91 #else
92  NativeFileParams(ios_base::openmode cppOpenMode)
93  : openFlags(0)
94  {
95  if ((cppOpenMode & ios_base::in) && (cppOpenMode & ios_base::out)) {
96  if (cppOpenMode & ios_base::app) {
97  openMode = "a+";
98  openFlags = O_RDWR | O_APPEND;
99  } else if (cppOpenMode & ios_base::trunc) {
100  openMode = "w+";
101  openFlags = O_RDWR | O_TRUNC;
102  } else {
103  openMode = "r+";
104  openFlags = O_RDWR;
105  }
106  } else if (cppOpenMode & ios_base::in) {
107  openMode = 'r';
108  openFlags = O_RDONLY;
109  } else if (cppOpenMode & ios_base::out) {
110  if (cppOpenMode & ios_base::app) {
111  openMode = 'a';
112  openFlags = O_WRONLY | O_APPEND;
113  } else if (cppOpenMode & ios_base::trunc) {
114  openMode = 'w';
115  openFlags = O_WRONLY | O_TRUNC | O_CREAT;
116  } else {
117  openMode = "w";
118  openFlags = O_WRONLY | O_CREAT;
119  }
120  }
121  if (cppOpenMode & ios_base::binary) {
122  openMode += 'b';
123  }
124  }
125 
126  std::string openMode;
127  int openFlags;
128 #endif
129 };
130 
135  : iostream(new StreamBuffer)
136  , m_filebuf(rdbuf())
137 {
138 }
139 
144  : iostream(other.m_filebuf.release())
145  , m_filebuf(rdbuf())
146  , m_fileHandle(other.m_fileHandle)
147 {
148 }
149 
153 NativeFileStream::~NativeFileStream()
154 {
155 }
156 
160 bool NativeFileStream::is_open() const
161 {
162  return m_filebuf && static_cast<const StreamBuffer *>(m_filebuf.get())->is_open();
163 }
164 
178 void NativeFileStream::open(const string &path, ios_base::openmode openMode)
179 {
180  setFileBuffer(makeFileBuffer(path, openMode));
181 }
182 
191 void NativeFileStream::openFromFileDescriptor(int fileDescriptor, ios_base::openmode openMode)
192 {
193  setFileBuffer(makeFileBuffer(fileDescriptor, openMode));
194 }
195 
199 void NativeFileStream::close()
200 {
201  if (m_filebuf) {
202  static_cast<StreamBuffer *>(m_filebuf.get())->close();
203  }
204 }
205 
209 void NativeFileStream::setFileBuffer(std::unique_ptr<std::basic_streambuf<char>> buffer)
210 {
211  rdbuf(buffer.get());
212  m_filebuf = std::move(buffer);
213 }
214 
218 std::unique_ptr<std::basic_streambuf<char>> NativeFileStream::makeFileBuffer(const string &path, ios_base::openmode openMode)
219 {
220 #ifdef PLATFORM_WINDOWS
221  // convert path to UTF-16
222  const auto widePath(makeWidePath(path));
223 #endif
224 
225  // compute native params
226  const NativeFileParams nativeParams(openMode);
227 
228 #ifdef CPP_UTILITIES_USE_GNU_CXX_STDIO_FILEBUF
229  // open file handle to initialize stdio_filebuf
230 #ifdef PLATFORM_WINDOWS
231  const int fileHandle = _wopen(widePath.get(), nativeParams.openMode, nativeParams.permissions);
232  if (fileHandle == -1) {
233  ::IoUtilities::throwIoFailure("_wopen failed");
234  }
235 #else
236  const auto fileHandle = fopen(path.data(), nativeParams.openMode.data());
237  if (!fileHandle) {
238  ::IoUtilities::throwIoFailure("fopen failed");
239  }
240 #endif
241  return make_unique<StreamBuffer>(fileHandle, openMode);
242 
243 #else // CPP_UTILITIES_USE_BOOST_IOSTREAMS
244  // create raw file descriptor to initialize boost::iostreams::file_descriptor
245 #ifdef PLATFORM_WINDOWS
246  const auto fileDescriptor
247  = CreateFileW(widePath.get(), nativeParams.access, nativeParams.shareMode, nullptr, nativeParams.creation, FILE_ATTRIBUTE_NORMAL);
248  if (fileDescriptor == INVALID_HANDLE_VALUE) {
249  ::IoUtilities::throwIoFailure("CreateFileW failed");
250  }
251 #else
252  const auto fileDescriptor = ::open(path.data(), nativeParams.openFlags, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
253  if (fileDescriptor == -1) {
254  ::IoUtilities::throwIoFailure("open failed");
255  }
256 #endif
257  return make_unique<StreamBuffer>(fileDescriptor, boost::iostreams::close_handle);
258 #endif
259 }
260 
264 std::unique_ptr<std::basic_streambuf<char>> NativeFileStream::makeFileBuffer(int fileDescriptor, ios_base::openmode openMode)
265 {
266  // compute native params
267  const NativeFileParams nativeParams(openMode);
268 
269 #ifdef CPP_UTILITIES_USE_GNU_CXX_STDIO_FILEBUF
270  // open file handle to initialize stdio_filebuf
271 #ifdef PLATFORM_WINDOWS
272  const auto fileHandle = _get_osfhandle(fileDescriptor);
273  if (fileHandle == -1) {
274  ::IoUtilities::throwIoFailure("_get_osfhandle failed");
275  }
276  const auto osFileHandle = _open_osfhandle(fileHandle, nativeParams.flags);
277  if (osFileHandle == -1) {
278  ::IoUtilities::throwIoFailure("_open_osfhandle failed");
279  }
280 #else
281  const auto fileHandle = fdopen(fileDescriptor, nativeParams.openMode.data());
282  if (!fileHandle) {
283  ::IoUtilities::throwIoFailure("fdopen failed");
284  }
285 #endif
286  return make_unique<StreamBuffer>(fileDescriptor, openMode);
287 
288 #else // CPP_UTILITIES_USE_BOOST_IOSTREAMS
289  // initialize boost::iostreams::file_descriptor from the specified fileDescriptor
290  return make_unique<StreamBuffer>(fileDescriptor, boost::iostreams::close_handle);
291 #endif
292 }
293 
294 #ifdef PLATFORM_WINDOWS
295 
299 std::unique_ptr<wchar_t[]> NativeFileStream::makeWidePath(const std::string &path)
300 {
301  auto widePath = ::ConversionUtilities::convertMultiByteToWide(path);
302  if (!widePath.first) {
303  ::IoUtilities::throwIoFailure("Unable to convert path to UTF-16");
304  }
305  return std::move(widePath.first);
306 }
307 #endif
308 
309 #else
310 
311 // std::fstream is used
312 
313 #endif
314 } // namespace IoUtilities
CPP_UTILITIES_EXPORT void throwIoFailure(const char *what)
Throws an std::ios_base::failure with the specified message.
STL namespace.
std::fstream NativeFileStream
Contains utility classes helping to read and write streams.
Definition: binaryreader.h:10