Add a cross-platform file mmap class and use it to access the timezone file, removing the need to keep the database in memory.

This commit is contained in:
Erik Hofman
2021-03-15 10:58:17 +01:00
parent 508a511070
commit f00a825e2d
2 changed files with 447 additions and 0 deletions

325
simgear/io/sg_mmap.cxx Normal file
View File

@@ -0,0 +1,325 @@
// sg_mmap.cxx -- File I/O routines
//
// Written by Curtis Olson, started November 1999.
//
// Copyright (C) 1999 Curtis L. Olson - http://www.flightgear.org/~curt
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
// $Id$
#include <simgear_config.h>
#include <simgear/compiler.h>
#include <string>
#ifdef _WIN32
# include <io.h>
#else
# include <sys/mman.h>
#endif
#include <cstring>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#if !defined(_MSC_VER)
# include <unistd.h>
#endif
#include <simgear/misc/stdint.hxx>
#include <simgear/debug/logstream.hxx>
#include "sg_mmap.hxx"
#ifdef WIN32
/*
* map 'filename' and return a pointer to it.
*/
static void *simple_mmap(int, size_t, SIMPLE_UNMMAP *);
static void simple_unmmap(void*, size_t, SIMPLE_UNMMAP *);
#else
# 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
SGMMapFile::SGMMapFile( )
{
set_type( sgFileType );
}
SGMMapFile::SGMMapFile(const SGPath &file, int repeat_, int extraoflags_ )
: file_name(file), repeat(repeat_), extraoflags(extraoflags_)
{
set_type( sgFileType );
}
SGMMapFile::SGMMapFile( int existingFd ) :
fp(existingFd)
{
set_type( sgFileType );
}
SGMMapFile::~SGMMapFile() {
close();
}
// open the file based on specified direction
bool SGMMapFile::open( const SGPath& file, const SGProtocolDir d ) {
file_name = file;
return open(d);
}
// open the file based on specified direction
bool SGMMapFile::open( const SGProtocolDir d ) {
set_dir( d );
#if defined(SG_WINDOWS)
std::wstring n = file_name.wstr();
#else
std::string n = file_name.utf8Str();
#endif
if ( get_dir() == SG_IO_OUT ) {
#if defined(SG_WINDOWS)
int mode = _S_IREAD | _S_IWRITE;
fp = ::_wopen(n.c_str(), O_WRONLY | O_CREAT | O_TRUNC | extraoflags, mode);
#else
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 );
#endif
} else if ( get_dir() == SG_IO_IN ) {
#if defined(SG_WINDOWS)
fp = ::_wopen( n.c_str(), O_RDONLY | extraoflags );
#else
fp = ::open( n.c_str(), O_RDONLY | extraoflags );
#endif
} else {
SG_LOG( SG_IO, SG_ALERT,
"Error: bidirection mode not available for files." );
return false;
}
if ( fp == -1 ) {
SG_LOG( SG_IO, SG_ALERT, "Error opening file: " << file_name );
return false;
}
// mmap
struct stat statbuf;
fstat(fp, &statbuf);
size = (size_t)statbuf.st_size;
buffer = (char*)simple_mmap(fp, size, &un);
if (buffer == (char*)-1)
{
SG_LOG( SG_IO, SG_ALERT, "Error mmapping file: " << file_name );
return false;
}
eof_flag = false;
return true;
}
// read a block of data of specified size
int SGMMapFile::read( char *buf, int length ) {
size_t read_size = length;
size_t result = length;
size_t pos = offset;
if (read_size > size - offset) {
read_size = size - offset;
result = 0; // eof
}
// read a chunk
memcpy(buf, buffer+offset, read_size);
offset += read_size;
if ( length > 0 && result == 0 ) {
if (repeat < 0 || iteration < repeat - 1) {
iteration++;
// loop reading the file, unless it is empty
off_t fileLen = pos;
if (fileLen == 0) {
eof_flag = true;
return 0;
} else {
offset = 0;
if (read_size > size) {
read_size = size;
result = 0; // eof
}
memcpy(buf, buffer, read_size);
offset += read_size;
return result;
}
} else {
eof_flag = true;
}
}
return result;
}
// read a line of data, length is max size of input buffer
int SGMMapFile::readline( char *buf, int length ) {
size_t read_size = length;
size_t result = length;
size_t pos = offset;
if (read_size > size - offset) {
read_size = size - offset;
result = 0; // eof
}
// read a chunk
memcpy(buf, buffer+offset, read_size);
offset += read_size;
if ( length > 0 && result == 0 ) {
if ((repeat < 0 || iteration < repeat - 1) && pos != 0) {
iteration++;
pos = 0;
result = length;
read_size = length;
if (read_size > size) {
read_size = size;
result = 0; // eof
}
memcpy(buf, buffer, read_size);
offset += read_size;
} else {
eof_flag = true;
}
}
// find the end of line and reset position
size_t i;
for ( i = 0; i < result && buf[i] != '\n'; ++i );
if ( buf[i] == '\n' ) {
result = i + 1;
} else {
result = i;
}
offset = pos + result;
// just in case ...
buf[ result ] = '\0';
return result;
}
std::string SGMMapFile::read_all()
{
return std::string(buffer, size);
}
// write data to a file
int SGMMapFile::write( const char *buf, const int length ) {
size_t write_size = length;
if (write_size > size - offset) {
SG_LOG( SG_IO, SG_ALERT, "Attempting to write beyond the mmap buffer size: " << file_name );
write_size = size - offset;
}
memcpy(buffer+offset, buf, write_size);
offset += write_size;
return write_size;
}
// write null terminated string to a file
int SGMMapFile::writestring( const char *str ) {
size_t write_size = std::strlen( str );
if (write_size > size - offset) {
SG_LOG( SG_IO, SG_ALERT, "Attempting to write beyond the mmap buffer size: " << file_name );
write_size = size - offset;
}
memcpy(buffer+offset, str, write_size);
offset += write_size;
return write_size;
}
// close the port
bool SGMMapFile::close() {
if (fp != -1 ) {
simple_unmmap(buffer, size, &un);
if ( ::close( fp ) == -1 ) {
return false;
}
eof_flag = true;
return true;
}
return false;
}
#ifdef WIN32
/* Source:
* https://mollyrocket.com/forums/viewtopic.php?p=2529
*/
void *
simple_mmap(int fd, size_t length, SIMPLE_UNMMAP *un)
{
HANDLE f;
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
simple_unmmap(void *addr, size_t len, SIMPLE_UNMMAP *un)
{
UnmapViewOfFile(un->p);
CloseHandle(un->m);
}
#endif

122
simgear/io/sg_mmap.hxx Normal file
View File

@@ -0,0 +1,122 @@
///@mmap
/// File I/O routines.
//
// Written by Curtis Olson, started November 1999.
//
// Copyright (C) 1999 Curtis L. Olson - http://www.flightgear.org/~curt
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Library General Public License for more details.
//
// You should have received a copy of the GNU Library General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
#ifndef _SG_MMAP_HXX
#define _SG_MMAP_HXX
#include <simgear/compiler.h>
#include <simgear/misc/sg_path.hxx>
#include "iochannel.hxx"
#include <string>
#ifdef WIN32
# define WIN32_LEAN_AND_MEAN
# include <windows.h>
#endif
/**
* A file I/O class based on 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;
size_t offset = 0;
size_t size = 0;
#ifdef WIN32
typedef struct
{
HANDLE m;
void *p;
} SIMPLE_UNMMAP;
SIMPLE_UNMMAP un;
#else
int un; // referenced but not used
#endif
public:
SGMMapFile();
/*
* Create an instance of SGMMapFile.
* When calling the constructor you need to provide a file
* name. This file is not opened immediately, but instead will be
* opened when the open() method is called.
* @param file name of file to open
* @param repeat On eof restart at the beginning of the file
*/
SGMMapFile( const SGPath& file, int repeat_ = 1, int extraoflags = 0);
/**
* Create an SGMMapFile from an existing, open file-descriptor
*/
SGMMapFile( int existingFd );
/** Destructor */
virtual ~SGMMapFile();
// open the file based on specified direction
bool open( const SGPath& file, const SGProtocolDir dir );
bool open( const SGProtocolDir dir );
// read a block of data of specified size
int read( char *buf, int length );
// read a line of data, length is max size of input buffer
int readline( char *buf, int length );
// reads the whole file into a buffer
// note: this really defeats the purpose of mmapping a file
std::string read_all();
inline const char* get() { return buffer; }
inline size_t get_size() { return size; }
// write data to a file
int write( const char *buf, const int length );
// write null terminated string to a file
int writestring( const char *str );
// close file
bool close();
/** @return the name of the file being manipulated. */
std::string get_file_name() const { return file_name.utf8Str(); }
/** @return true of eof conditions exists */
virtual bool eof() const { return eof_flag; };
};
#endif // _SG_MMAP_HXX