MMap : improved error reporting, Win32 compat

Use strutils::sterror to print errno; convert to pimpl idiom to
avoid pulling Windows.h into public view.
This commit is contained in:
James Turner
2021-03-16 15:49:58 +00:00
parent 0c72b5e622
commit 522aed9b73
2 changed files with 196 additions and 179 deletions
Regular → Executable
+173 -119
View File
@@ -24,27 +24,51 @@
#include <simgear/compiler.h> #include <simgear/compiler.h>
#include <string> #include <string>
#ifdef _WIN32
# include <io.h>
#else
# include <sys/mman.h>
#endif
#include <cstring> #include <cstring>
#include <sys/types.h> #include <sys/types.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <fcntl.h> #include <fcntl.h>
#if !defined(_MSC_VER) #include <simgear/misc/sg_path.hxx>
#include <simgear/misc/stdint.hxx>
#include <simgear/debug/logstream.hxx>
#include <simgear/misc/strutils.hxx>
#include "sg_mmap.hxx"
#if defined(SG_WINDOWS)
# define WIN32_LEAN_AND_MEAN
# include <windows.h>
# include <io.h>
#else
# include <sys/mman.h>
# include <unistd.h> # include <unistd.h>
#endif #endif
#include <simgear/misc/stdint.hxx> class SGMMapFile::SGMMapFilePrivate
#include <simgear/debug/logstream.hxx> {
public:
SGPath file_name;
int fp = -1;
bool eof_flag = true;
// Number of repetitions to play. -1 means loop infinitely.
int repeat = 1;
int iteration = 0; // number of current repetition,
// starting at 0
int extraoflags = 0;
#include "sg_mmap.hxx" char *buffer = nullptr;
off_t offset = 0;
size_t size = 0;
#if defined(SG_WINDOWS)
HANDLE _nativeHandle = NULL;
#endif
ssize_t mmap_read(void *buf, size_t count);
ssize_t mmap_write(const void *buf, size_t count);
off_t forward(off_t amount);
};
SGMMapFile::SGMMapFile( ) SGMMapFile::SGMMapFile( )
{ {
@@ -52,51 +76,58 @@ SGMMapFile::SGMMapFile( )
} }
SGMMapFile::SGMMapFile(const SGPath &file, int repeat_, int extraoflags_ ) SGMMapFile::SGMMapFile(const SGPath &file, int repeat_, int extraoflags_ )
: file_name(file), repeat(repeat_), extraoflags(extraoflags_) : d(new SGMMapFilePrivate)
{ {
d->file_name = file;
d->repeat = repeat_;
d->extraoflags = extraoflags_;
set_type( sgFileType ); set_type( sgFileType );
} }
SGMMapFile::SGMMapFile( int existingFd ) : SGMMapFile::SGMMapFile( int existingFd )
fp(existingFd) : d(new SGMMapFilePrivate)
{ {
d->fp = existingFd;
set_type( sgFileType ); set_type( sgFileType );
} }
SGMMapFile::~SGMMapFile() { SGMMapFile::~SGMMapFile()
{
close(); close();
} }
// open the file based on specified direction // open the file based on specified direction
bool SGMMapFile::open( const SGPath& file, const SGProtocolDir d ) { bool SGMMapFile::open( const SGPath& file, const SGProtocolDir dir )
file_name = file; {
return open(d); d->file_name = file;
return open(dir);
} }
// open the file based on specified direction // open the file based on specified direction
bool SGMMapFile::open( const SGProtocolDir d ) { bool SGMMapFile::open( const SGProtocolDir dir )
set_dir( d ); {
set_dir( dir );
#if defined(SG_WINDOWS) #if defined(SG_WINDOWS)
std::wstring n = file_name.wstr(); const std::wstring n = d->file_name.wstr();
#else #else
std::string n = file_name.utf8Str(); const std::string n = d->file_name.utf8Str();
#endif #endif
if ( get_dir() == SG_IO_OUT ) { if ( get_dir() == SG_IO_OUT ) {
#if defined(SG_WINDOWS) #if defined(SG_WINDOWS)
int mode = _S_IREAD | _S_IWRITE; int mode = _S_IREAD | _S_IWRITE;
fp = ::_wopen(n.c_str(), O_WRONLY | O_CREAT | O_TRUNC | extraoflags, mode); d->fp = ::_wopen(n.c_str(), O_WRONLY | O_CREAT | O_TRUNC | d->extraoflags, mode);
#else #else
mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH; mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
fp = ::open( n.c_str(), O_WRONLY | O_CREAT | O_TRUNC | extraoflags, mode ); d->fp = ::open( n.c_str(), O_WRONLY | O_CREAT | O_TRUNC | d->extraoflags, mode );
#endif #endif
} else if ( get_dir() == SG_IO_IN ) { } else if ( get_dir() == SG_IO_IN ) {
#if defined(SG_WINDOWS) #if defined(SG_WINDOWS)
fp = ::_wopen( n.c_str(), O_RDONLY | extraoflags ); d->fp = ::_wopen( n.c_str(), O_RDONLY | d->extraoflags );
#else #else
fp = ::open( n.c_str(), O_RDONLY | extraoflags ); d->fp = ::open( n.c_str(), O_RDONLY | d->extraoflags );
#endif #endif
} else { } else {
SG_LOG( SG_IO, SG_ALERT, SG_LOG( SG_IO, SG_ALERT,
@@ -104,30 +135,54 @@ bool SGMMapFile::open( const SGProtocolDir d ) {
return false; return false;
} }
if ( fp == -1 ) { if ( d->fp == -1 ) {
SG_LOG( SG_IO, SG_ALERT, "Error opening file: " << file_name ); SG_LOG( SG_IO, SG_ALERT, "Error opening file: " << d->file_name
<< "\n\t" << simgear::strutils::error_string(errno));
return false; return false;
} }
// mmap // mmap
struct stat statbuf; struct stat statbuf;
fstat(fp, &statbuf); fstat(d->fp, &statbuf);
size = (size_t)statbuf.st_size; d->size = (size_t) statbuf.st_size;
buffer = (char*)simple_mmap(fp, size, &un); #if defined(SG_WINDOWS)
if (buffer == (char*)-1) HANDLE osfHandle = (HANDLE)_get_osfhandle(d->fp);
{ if (!osfHandle) {
SG_LOG( SG_IO, SG_ALERT, "Error mmapping file: " << file_name ); SG_LOG( SG_IO, SG_ALERT, "Error mmapping file: _get_osfhandle failed:" << d->file_name
<< "\n\t" << simgear::strutils::error_string(errno));
return false; return false;
} }
eof_flag = false; d->_nativeHandle = CreateFileMapping(osfHandle, NULL, PAGE_READONLY, 0, 0, NULL);
if (!d->_nativeHandle) {
SG_LOG( SG_IO, SG_ALERT, "Error mmapping file: CreateFileMapping failed:" << d->file_name
<< "\n\t" << simgear::strutils::error_string(errno));
return false;
}
d->buffer = reinterpret_cast<char*>(MapViewOfFile(d->_nativeHandle, FILE_MAP_READ, 0, 0, 0));
if (!d->buffer) {
CloseHandle(d->_nativeHandle);
SG_LOG( SG_IO, SG_ALERT, "Error mmapping file: MapViewOfFile failed:" << d->file_name
<< "\n\t" << simgear::strutils::error_string(errno));
return false;
}
#else
d->buffer = (char*) mmap(0, d->size, PROT_READ, MAP_PRIVATE, d->fp, 0L);
if (d->buffer == MAP_FAILED) {
SG_LOG( SG_IO, SG_ALERT, "Error mmapping file: " << d->file_name
<< "\n\t" << simgear::strutils::error_string(errno));
return false;
}
#endif
d-> eof_flag = false;
return true; return true;
} }
off_t SGMMapFile::forward(off_t amount) { off_t SGMMapFile::SGMMapFilePrivate::forward(off_t amount)
if ((size_t)amount > size - offset) { {
if ((size_t) amount > size - offset) {
amount = size - offset; amount = size - offset;
} }
offset += amount; offset += amount;
@@ -138,78 +193,80 @@ off_t SGMMapFile::forward(off_t amount) {
return amount; return amount;
} }
const char* SGMMapFile::advance(off_t amount) { const char* SGMMapFile::advance(off_t amount)
const char *ptr = buffer + offset; {
const char *ptr = d->buffer + d->offset;
off_t advanced = forward(amount); off_t advanced = d->forward(amount);
if (advanced != amount) { if (advanced != amount) {
eof_flag = true; d->eof_flag = true;
return nullptr; return nullptr;
} }
return ptr; return ptr;
} }
ssize_t SGMMapFile::mmap_read(void *buf, size_t count) { ssize_t SGMMapFile::SGMMapFilePrivate::mmap_read(void *buf, size_t count)
{
const char *ptr = buffer + offset; const char *ptr = buffer + offset;
size_t result = forward(count); size_t result = forward(count);
memcpy(buf, ptr, result); memcpy(buf, ptr, result);
return result; return result;
} }
ssize_t SGMMapFile::mmap_write(const void *buf, size_t count) { ssize_t SGMMapFile::SGMMapFilePrivate::mmap_write(const void *buf, size_t count)
{
char *ptr = buffer + offset; char *ptr = buffer + offset;
size_t result = forward(count); size_t result = forward(count);
memcpy(ptr, buf, result); memcpy(ptr, buf, result);
return result; return result;
} }
// read a block of data of specified size // read a block of data of specified size
int SGMMapFile::read( char *buf, int length ) { int SGMMapFile::read( char *buf, int length )
{
// read a chunk // read a chunk
ssize_t result = mmap_read(buf, length); ssize_t result = d->mmap_read(buf, length);
if ( length > 0 && result == 0 ) { if ( length > 0 && result == 0 ) {
if (repeat < 0 || iteration < repeat - 1) { if (d->repeat < 0 || d->iteration < d->repeat - 1) {
iteration++; d->iteration++;
// loop reading the file, unless it is empty // loop reading the file, unless it is empty
off_t fileLen = offset; // lseek(0, SEEK_CUR) off_t fileLen = d->offset; // lseek(0, SEEK_CUR)
if (fileLen == 0) { if (fileLen == 0) {
eof_flag = true; d->eof_flag = true;
return 0; return 0;
} else { } else {
offset = 0; // lseek(0, SEEK_SET) d->offset = 0; // lseek(0, SEEK_SET)
return mmap_read(buf, length); return d->mmap_read(buf, length);
} }
} else { } else {
eof_flag = true; d->eof_flag = true;
} }
} }
return result; return result;
} }
int SGMMapFile::read( char *buf, int length, int num ) { int SGMMapFile::read( char *buf, int length, int num )
return mmap_read(buf, num*length)/length; {
return d->mmap_read(buf, num*length)/length;
} }
// read a line of data, length is max size of input buffer // read a line of data, length is max size of input buffer
int SGMMapFile::readline( char *buf, int length ) { int SGMMapFile::readline( char *buf, int length )
int pos = offset; // pos = lseek(0, SEEK_CUR) {
int pos = d->offset; // pos = lseek(0, SEEK_CUR)
// read a chunk // read a chunk
ssize_t result = mmap_read(buf, length); ssize_t result = d->mmap_read(buf, length);
if ( length > 0 && result == 0 ) { if ( length > 0 && result == 0 ) {
if ((repeat < 0 || iteration < repeat - 1) && pos != 0) { if ((d->repeat < 0 || d->iteration < d->repeat - 1) && pos != 0) {
iteration++; d->iteration++;
pos = offset = 0; // pos = lseek(0, SEEK_SET) pos = d->offset = 0; // pos = lseek(0, SEEK_SET)
result = mmap_read(buf, length); result = d->mmap_read(buf, length);
} else { } else {
eof_flag = true; d->eof_flag = true;
} }
} }
@@ -217,28 +274,30 @@ int SGMMapFile::readline( char *buf, int length ) {
int i; int i;
for ( i = 0; i < result && buf[i] != '\n'; ++i ); for ( i = 0; i < result && buf[i] != '\n'; ++i );
if ( buf[i] == '\n' ) { if ( buf[i] == '\n' ) {
result = i + 1; result = i + 1;
} else { } else {
result = i; result = i;
} }
offset = pos + result; // lseek(pos+result, SEEK_SET) d->offset = pos + result; // lseek(pos+result, SEEK_SET)
// just in case ... // just in case ...
buf[ result ] = '\0'; buf[ result ] = '\0';
return result; return result;
} }
std::string SGMMapFile::read_all() { std::string SGMMapFile::read_all()
return std::string(buffer, size); {
return std::string(d->buffer, d->size);
} }
// write data to a file // write data to a file
int SGMMapFile::write( const char *buf, const int length ) { int SGMMapFile::write( const char *buf, const int length )
int result = mmap_write(buf, length); {
int result = d->mmap_write(buf, length);
if ( result != length ) { if ( result != length ) {
SG_LOG( SG_IO, SG_ALERT, "Error writing data: " << file_name ); SG_LOG( SG_IO, SG_ALERT, "Error writing data to mmaped-: " << d->file_name
<< "\n\t" << simgear::strutils::error_string(errno));
} }
return result; return result;
@@ -246,65 +305,60 @@ int SGMMapFile::write( const char *buf, const int length ) {
// write null terminated string to a file // write null terminated string to a file
int SGMMapFile::writestring( const char *str ) { int SGMMapFile::writestring( const char *str )
{
int length = std::strlen( str ); int length = std::strlen( str );
return write( str, length ); return write( str, length );
} }
// close the port // close the port
bool SGMMapFile::close() { bool SGMMapFile::close()
if (fp != -1 ) { {
simple_unmmap(buffer, size, &un); if (d->fp != -1 ) {
if ( ::close( fp ) == -1 ) { #if defined(SG_WINDOWS)
UnmapViewOfFile(d->buffer);
CloseHandle(d->_nativeHandle);
d->_nativeHandle = NULL;
#else
if (munmap(d->buffer, d->size) != 0) {
SG_LOG( SG_IO, SG_ALERT, "Error un-mapping mmaped-file: " << d->file_name
<< "\n\t" << simgear::strutils::error_string(errno));
}
#endif
if ( ::close( d->fp ) == -1 ) {
SG_LOG( SG_IO, SG_ALERT, "Error clossing mmaped-file: " << d->file_name
<< "\n\t" << simgear::strutils::error_string(errno));
return false; return false;
} }
eof_flag = true; d->eof_flag = true;
d->fp = -1;
return true; return true;
} }
return false; return false;
} }
#ifdef WIN32 const char* SGMMapFile::get() const
/* Source:
* https://mollyrocket.com/forums/viewtopic.php?p=2529
*/
void *
SGMMapFile::simple_mmap(int fd, size_t length, SIMPLE_UNMMAP *un)
{ {
HANDLE f; return d->buffer;
HANDLE m;
void *p;
f = (HANDLE)_get_osfhandle(fd);
if (!f) return (void *)-1;
m = CreateFileMapping(f, NULL, PAGE_READONLY, 0, 0, NULL);
if (!m) return (void *)-1;
p = MapViewOfFile(m, FILE_MAP_READ, 0, 0, 0);
if (!p)
{
CloseHandle(m);
return (void *)-1;
}
if (un)
{
un->m = m;
un->p = p;
}
return p;
} }
void const char* SGMMapFile::ptr() const
SGMMapFile::simple_unmmap(void *addr, size_t len, SIMPLE_UNMMAP *un)
{ {
UnmapViewOfFile(un->p); return d->buffer + d->offset;
CloseHandle(un->m);
} }
#endif
size_t SGMMapFile::get_size() const
{
return d->size;
}
bool SGMMapFile::eof() const
{
return d->eof_flag;
}
off_t SGMMapFile::forward(off_t amount)
{
return d->forward(amount);
}
+23 -60
View File
@@ -19,60 +19,21 @@
// License along with this library; if not, write to the Free Software // License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
#ifndef _SG_MMAP_HXX #pragma once
#define _SG_MMAP_HXX
#include <simgear/compiler.h> #include <memory>
#include <string>
#include <simgear/misc/sg_path.hxx>
#include "iochannel.hxx" #include "iochannel.hxx"
#include <string> // forward decls
class SGPath;
#ifdef WIN32
# define WIN32_LEAN_AND_MEAN
# include <windows.h>
#endif
/** /**
* A file I/O class based on SGIOChannel. * A file I/O class based on SGIOChannel.
*/ */
class SGMMapFile : public SGIOChannel { class SGMMapFile : public SGIOChannel
{
SGPath file_name;
int fp = -1;
bool eof_flag = true;
// Number of repetitions to play. -1 means loop infinitely.
const int repeat = 1;
int iteration = 0; // number of current repetition,
// starting at 0
int extraoflags = 0;
char *buffer = nullptr;
off_t offset = 0;
size_t size = 0;
#ifdef WIN32
typedef struct
{
HANDLE m;
void *p;
} SIMPLE_UNMMAP;
SIMPLE_UNMMAP un;
// map the file descriptor and return a pointer to the buffer.
static void *simple_mmap(int, size_t, SIMPLE_UNMMAP *);
static void simple_unmmap(void*, size_t, SIMPLE_UNMMAP *);
#else
int un; // referenced but not used
# define simple_mmap(a, b, c) mmap(0, (b), PROT_READ, MAP_PRIVATE, (a), 0L)
# define simple_unmmap(a, b, c) munmap((a), (b))
#endif
ssize_t mmap_read(void *buf, size_t count);
ssize_t mmap_write(const void *buf, size_t count);
public: public:
SGMMapFile(); SGMMapFile();
@@ -97,26 +58,26 @@ public:
// open the file based on specified direction // open the file based on specified direction
bool open( const SGPath& file, const SGProtocolDir dir ); bool open( const SGPath& file, const SGProtocolDir dir );
bool open( const SGProtocolDir dir ); bool open(const SGProtocolDir dir) override;
// read a block of data of specified size // read a block of data of specified size
int read( char *buf, int length ); int read(char* buf, int length) override;
// read a block of data of specified size // read a block of data of specified size
int read( char *buf, int length, int num ); int read( char *buf, int length, int num );
// read a line of data, length is max size of input buffer // read a line of data, length is max size of input buffer
int readline( char *buf, int length ); int readline(char* buf, int length) override;
// reads the whole file into a buffer // reads the whole file into a buffer
// note: this really defeats the purpose of mmapping a file // note: this really defeats the purpose of mmapping a file
std::string read_all(); std::string read_all();
// get the pointer to the start of the buffer // get the pointer to the start of the buffer
inline const char *get() { return buffer; } const char* get() const;
// get the pointer to the current offset of the buffer // get the pointer to the current offset of the buffer
inline const char *ptr() { return buffer + offset; } const char* ptr() const;
// get the pointer at the current offset and increase the offset by amount // get the pointer at the current offset and increase the offset by amount
// returns nullptr if the offset pointer would end up beyond the mmap // returns nullptr if the offset pointer would end up beyond the mmap
@@ -124,7 +85,7 @@ public:
const char* advance(off_t amount); const char* advance(off_t amount);
// return the size of the mmaped area // return the size of the mmaped area
inline size_t get_size() { return size; } size_t get_size() const;
// forward by amount, returns the amount which could be forwarded // forward by amount, returns the amount which could be forwarded
// without the offset getting beyond the mmap buffer size. // without the offset getting beyond the mmap buffer size.
@@ -132,22 +93,24 @@ public:
off_t forward(off_t amount); off_t forward(off_t amount);
// rewind the offset pointer // rewind the offset pointer
inline void rewind() { offset = 0; } void rewind();
// write data to a file // write data to a file
int write( const char *buf, const int length ); int write(const char* buf, const int length) override;
// write null terminated string to a file // write null terminated string to a file
int writestring( const char *str ); int writestring(const char* str) override;
// close file // close file
bool close(); bool close() override;
/** @return the name of the file being manipulated. */ /** @return the name of the file being manipulated. */
std::string get_file_name() const { return file_name.utf8Str(); } std::string get_file_name() const;
/** @return true of eof conditions exists */ /** @return true of eof conditions exists */
virtual bool eof() const { return eof_flag; }; bool eof() const override;
};
#endif // _SG_MMAP_HXX private:
class SGMMapFilePrivate;
std::unique_ptr<SGMMapFilePrivate> d;
};