#579: Implemented mmap for Windows directly in FileIo and made that class use the Pimpl idiom (#510) to de-clutter the interface. Unrelated: Added EXV_MIN/MAX macros, allow iconv config in commercial version.

v0.27.3
Andreas Huggel 16 years ago
parent db0a5b8338
commit 83cd2efab9

@ -25,11 +25,11 @@
native language is requested. */
#undef ENABLE_NLS
#endif /* !EXV_COMMERCIAL_VERSION */
/* Define to 1 if you have the `iconv' function. */
#undef HAVE_ICONV
#endif /* !EXV_COMMERCIAL_VERSION */
/* Define to `const' or to empty, depending on the second argument of `iconv'. */
#undef ICONV_CONST

@ -573,21 +573,6 @@ copy/y ..\..\..\zlib-1.2.3\projects\visualc6\Win32_DLL_Release\zlib1.dll $(OutDi
<File
RelativePath="..\include\exv_msvc.h">
</File>
<File
RelativePath="..\src\mmap.cpp">
</File>
<File
RelativePath="..\include\sys\mman.h">
</File>
<File
RelativePath="..\include\sys\socket.h">
</File>
<File
RelativePath="..\include\sys\types.h">
</File>
<File
RelativePath="..\include\winposix_export.h">
</File>
</Filter>
</Files>
<Globals>

@ -43,14 +43,11 @@ typedef int pid_t;
native language is requested. */
# undef EXV_ENABLE_NLS
#endif /* !EXV_COMMERCIAL_VERSION */
/* Define to 1 if you have the `iconv' function. */
# undef EXV_HAVE_ICONV
/* Define to 1 to enable conversion of UCS2 encoded Windows tags to UTF-8. */
# undef EXV_HAVE_PRINTUCS2
#endif /* !EXV_COMMERCIAL_VERSION */
/* Define as 1 if you have the `zlib' library. (0 to omit zlib) [png support] */
#define HAVE_LIBZ 1
@ -72,16 +69,11 @@ typedef int pid_t;
/* Windows unicode path support */
#define EXV_UNICODE_PATH
/* Define to 1 if you have the "sys/mman.h header file (and supporting code of course) */
/* At this time (between 0.18.1 and 0.19) this is used by TIFF files to avoid reading */
/* the total file into memory returning in a 6x improvement in exiv2 on 2mb tiff files */
#define EXV_HAVE_SYS_MMAN_H 1
#ifdef EXV_HAVE_SYS_MMAN_H
#if EXV_HAVE_SYS_MMAN_H
#define EXV_HAVE_MMAP 1
#define EXV_HAVE_MUNMAP 1
#endif
#endif
/* Define to 1 if you have the `mmap' function. */
/* #undef EXV_HAVE_MMAP */
/* Define to 1 if you have the `munmap' function. */
/* #undef EXV_HAVE_MUNMAP */
/* Shared library support */
#ifdef EXV_HAVE_DLL

@ -1,57 +0,0 @@
/*
This file is part of the KDE libraries
Copyright (C) 2004 Jaroslaw Staniek <js@iidea.pl>
These sources are based on ftp://g.oswego.edu/pub/misc/malloc.c
file by Doug Lea, released to the public domain.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License version 2 as published by the Free Software Foundation.
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; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#ifndef KDEWIN_SYS_MMAN_H
#define KDEWIN_SYS_MMAN_H
// include everywhere
#include <sys/types.h>
#ifdef __cplusplus
extern "C" {
#endif
#define PROT_NONE 0
#define PROT_READ 1
#define PROT_WRITE 2
#define PROT_EXEC 4
/* These values don't really matter in windows mmap emulation */
#define MAP_FILE 0
#define MAP_SHARED 1
#define MAP_PRIVATE 2
#define MAP_TYPE 0xF
#define MAP_FIXED 0x10
#define MAP_ANONYMOUS 0x20
#define MAP_ANON MAP_ANONYMOUS
#define MAP_FAILED ((void *)-1)
KDEWIN32_EXPORT void *mmap(void *start, size_t length, int prot , int flags, int fd, off_t offset);
KDEWIN32_EXPORT int munmap(void *start, size_t length);
#ifdef __cplusplus
}
#endif
#endif // KDEWIN_SYS_MMAN_H

@ -1,7 +0,0 @@
#ifndef _sys_socket_h_
#define _sys_socket_h_
// rmills
// minimum to enable mmap.cpp to compile without change
#endif

@ -1,8 +0,0 @@
#ifndef _types_h_
#define _types_h_
// rmills
// minimum required by exiv2
//
#include "winposix_export.h"
#endif

@ -1,39 +0,0 @@
#ifndef _WINPOSIX_EXPORT_H_
#define _WINPOSIX_EXPORT_H_
// rmills
// msvc/include/winposix_export.h
//
// I'm very appreciative and respectful of the work of Jaroslaw Staniek <js@iidea.pl>
// in the KDE libraries where I found msvc/src/mmap.cpp and msvc/mman.h
//
// http://websvn.kde.org/trunk/KDE/kdelibs/win/include/msvc/sys/mman.h?revision=517357&view=markup&pathrev=519502
// http://websvn.kde.org/trunk/KDE/kdelibs/win/src/mmap.c?revision=519502&view=markup&pathrev=519502
// I have commented 2 essential tiny changes in mmap.cpp to make it compile with MSVC
// I've also added a dummy sys/socket.h and sys/types.h file to keep everybody happy!
//
// the contents of this file are the minimum required to enable exiv2 to compile link and execute the mmap.cpp code
//
#ifndef ENOTSUP
#define ENOTSUP 911
#endif
#ifndef off_t
#define off_t size_t
#endif
#ifndef KDEWIN32_EXPORT
#define KDEWIN32_EXPORT
#endif
// give MSVC 7.1 (VS 2003 .Net) encouragement to ignore _set_errno !
#if _MSC_VER < 1400
#ifndef _set_errno
#define _set_errno(x)
#endif
#endif
#endif

@ -1,196 +0,0 @@
/*
This file is part of the KDE libraries
Copyright (c) 2006 Christian Ehrlicher <ch.ehrlicher@gmx.de>
These sources are based on ftp://g.oswego.edu/pub/misc/malloc.c
file by Doug Lea, released to the public domain.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License version 2 as published by the Free Software Foundation.
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; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#include <winposix_export.h>
#include <windows.h>
#include <assert.h>
#include <stdlib.h>
#include <errno.h>
#include <io.h>
#include <sys/mman.h>
#include <sys/socket.h>
#ifndef SECTION_MAP_EXECUTE_EXPLICIT
//not defined in the February 2003 version of the Platform SDK
#define SECTION_MAP_EXECUTE_EXPLICIT 0x0020
#endif
#ifndef FILE_MAP_EXECUTE
//not defined in the February 2003 version of the Platform SDK
#define FILE_MAP_EXECUTE SECTION_MAP_EXECUTE_EXPLICIT
#endif
#define MUNMAP_FAILURE (-1)
#define USE_MALLOC_LOCK 1
struct mmapInfos {
HANDLE hFile; // the duplicated fd
HANDLE hMap; // handle returned by CreateFileMapping
void* start; // ptr returned by MapViewOfFile
};
CRITICAL_SECTION cs;
// rmills - only change is to add long to the following 2 lines
static long g_curMMapInfos = 0;
static long g_maxMMapInfos = -1;
static struct mmapInfos *g_mmapInfos = NULL;
#define NEW_MMAP_STRUCT_CNT 10
static int mapProtFlags(int flags, DWORD *dwAccess)
{
if ( ( flags & PROT_READ ) == PROT_READ ) {
if ( ( flags & PROT_WRITE ) == PROT_WRITE ) {
*dwAccess = FILE_MAP_WRITE;
if ( ( flags & PROT_EXEC ) == PROT_EXEC ) {
return PAGE_EXECUTE_READWRITE;
}
return PAGE_READWRITE;
}
if ( ( flags & PROT_EXEC ) == PROT_EXEC ) {
*dwAccess = FILE_MAP_EXECUTE;
return PAGE_EXECUTE_READ;
}
*dwAccess = FILE_MAP_READ;
return PAGE_READONLY;
}
if ( ( flags & PROT_WRITE ) == PROT_WRITE ) {
*dwAccess = FILE_MAP_COPY;
return PAGE_WRITECOPY;
}
if ( ( flags & PROT_EXEC ) == PROT_EXEC ) {
*dwAccess = FILE_MAP_EXECUTE;
return PAGE_EXECUTE_READ;
}
*dwAccess = 0;
return 0;
}
void *mmap(void *start, size_t length, int prot , int flags, int fd, off_t offset)
{
struct mmapInfos mmi;
DWORD dwAccess;
DWORD flProtect;
HANDLE hfd;
if ( g_maxMMapInfos == -1 ) {
g_maxMMapInfos = 0;
InitializeCriticalSection( &cs );
}
flProtect = mapProtFlags( flags, &dwAccess );
if ( flProtect == 0 ) {
_set_errno( EINVAL );
return MAP_FAILED;
}
// we don't support this atm
if ( prot == MAP_FIXED ) {
_set_errno( ENOTSUP );
return MAP_FAILED;
}
if ( fd == -1 ) {
_set_errno( EBADF );
return MAP_FAILED;
}
hfd = (HANDLE)_get_osfhandle( fd );
if ( hfd == INVALID_HANDLE_VALUE )
return MAP_FAILED;
if ( !DuplicateHandle( GetCurrentProcess(), hfd, GetCurrentProcess(),
&mmi.hFile, 0, FALSE, DUPLICATE_SAME_ACCESS ) ) {
#ifdef _DEBUG
DWORD dwLastErr = GetLastError();
#endif
return MAP_FAILED;
}
mmi.hMap = CreateFileMapping( mmi.hFile, NULL, flProtect,
0, length, NULL );
if ( mmi.hMap == 0 ) {
_set_errno( EACCES );
return MAP_FAILED;
}
mmi.start = MapViewOfFile( mmi.hMap, dwAccess, 0, offset, 0 );
if ( mmi.start == 0 ) {
DWORD dwLastErr = GetLastError();
if ( dwLastErr == ERROR_MAPPED_ALIGNMENT )
_set_errno( EINVAL );
else
_set_errno( EACCES );
return MAP_FAILED;
}
EnterCriticalSection( &cs );
if ( g_mmapInfos == NULL ) {
g_maxMMapInfos = NEW_MMAP_STRUCT_CNT;
g_mmapInfos = ( struct mmapInfos* )calloc( g_maxMMapInfos,
sizeof( struct mmapInfos ) );
}
if( g_curMMapInfos == g_maxMMapInfos) {
g_maxMMapInfos += NEW_MMAP_STRUCT_CNT;
g_mmapInfos = ( struct mmapInfos* )realloc( g_mmapInfos,
g_maxMMapInfos * sizeof( struct mmapInfos ) );
}
memcpy( &g_mmapInfos[g_curMMapInfos], &mmi, sizeof( struct mmapInfos) );
g_curMMapInfos++;
LeaveCriticalSection( &cs );
return mmi.start;
}
int munmap(void *start, size_t length)
{
int i, j;
for( i = 0; i < g_curMMapInfos; i++ ) {
if( g_mmapInfos[i].start == start )
break;
}
if( i == g_curMMapInfos ) {
_set_errno( EINVAL );
return -1;
}
UnmapViewOfFile( g_mmapInfos[i].start );
CloseHandle( g_mmapInfos[i].hMap );
CloseHandle( g_mmapInfos[i].hFile );
EnterCriticalSection( &cs );
for( j = i + 1; j < g_curMMapInfos; j++ ) {
memcpy( &g_mmapInfos[ j - 1 ], &g_mmapInfos[ j ],
sizeof( struct mmapInfos ) );
}
g_curMMapInfos--;
if( g_curMMapInfos == 0 ) {
free( g_mmapInfos );
g_mmapInfos = NULL;
g_maxMMapInfos = 0;
}
LeaveCriticalSection( &cs );
return 0;
}

@ -25,7 +25,7 @@ try {
throw Error(10, path, "rb", strError());
}
// Map it to memory
const byte* pData = file.mmap();
const Exiv2::byte* pData = file.mmap();
long size = file.size();
DataBuf buf(size);
// Read from the memory mapped region

@ -61,7 +61,13 @@ EXIV2_RCSID("@(#) $Id$")
# include <unistd.h> // for getpid, stat
#endif
// MSVC doesn't provide mode_t
#ifdef _MSC_VER
typedef unsigned short mode_t;
#endif
#if defined WIN32 && !defined __CYGWIN__
# include <windows.h>
# include <io.h>
#endif
@ -73,137 +79,298 @@ namespace Exiv2 {
{
}
FileIo::FileIo(const std::string& path)
//! Internal Pimpl structure of class FileIo.
class FileIo::Impl {
public:
Impl(const std::string& path); //!< Constructor
#ifdef EXV_UNICODE_PATH
Impl(const std::wstring& wpath); //!< Constructor
#endif
// Enumeration
enum OpMode { opRead, opWrite, opSeek };
#ifdef EXV_UNICODE_PATH
enum WpMode { wpStandard, wpUnicode };
#endif
// DATA
std::string path_;
#ifdef EXV_UNICODE_PATH
std::wstring wpath_;
WpMode wpMode_;
#endif
std::string openMode_;
FILE *fp_;
OpMode opMode_;
#if defined WIN32 && !defined __CYGWIN__
HANDLE hFile_; // Duplicated fd
HANDLE hMap_; // Handle from CreateFileMapping
#endif
byte* pMappedArea_;
size_t mappedLength_;
bool isMalloced_; //!< Is the mapped area allocated?
bool isWriteable_; //!< Can the mapped area be written to?
// TYPES
//! Simple struct stat wrapper for internal use
struct StructStat {
StructStat() : st_mode(0), st_size(0) {}
mode_t st_mode; //!< Permissions
off_t st_size; //!< Size
};
// METHODS
/*!
@brief Switch to a new access mode, reopening the file if needed.
Optimized to only reopen the file when it is really necessary.
@param opMode The mode to switch to.
@return 0 if successful
*/
int switchMode(OpMode opMode);
//! stat wrapper for internal use
int stat(StructStat& buf) const;
private:
// NOT IMPLEMENTED
Impl(const Impl& rhs); //!< Copy constructor
Impl& operator=(const Impl& rhs); //!< Assignment
}; // class FileIo::Impl
FileIo::Impl::Impl(const std::string& path)
: path_(path),
#ifdef EXV_UNICODE_PATH
wpMode_(wpStandard),
#endif
fp_(0), opMode_(opSeek),
#if defined WIN32 && !defined __CYGWIN__
hFile_(0), hMap_(0),
#endif
pMappedArea_(0), mappedLength_(0), isMalloced_(false), isWriteable_(false)
{
}
#ifdef EXV_UNICODE_PATH
FileIo::FileIo(const std::wstring& wpath)
FileIo::Impl::Impl(const std::wstring& wpath)
: wpath_(wpath),
wpMode_(wpUnicode),
fp_(0), opMode_(opSeek),
#if defined WIN32 && !defined __CYGWIN__
hFile_(0), hMap_(0),
#endif
pMappedArea_(0), mappedLength_(0), isMalloced_(false), isWriteable_(false)
{
}
#endif
int FileIo::Impl::switchMode(OpMode opMode)
{
assert(fp_ != 0);
if (opMode_ == opMode) return 0;
OpMode oldOpMode = opMode_;
opMode_ = opMode;
bool reopen = true;
switch(opMode) {
case opRead:
// Flush if current mode allows reading, else reopen (in mode "r+b"
// as in this case we know that we can write to the file)
if (openMode_[0] == 'r' || openMode_[1] == '+') reopen = false;
break;
case opWrite:
// Flush if current mode allows writing, else reopen
if (openMode_[0] != 'r' || openMode_[1] == '+') reopen = false;
break;
case opSeek:
reopen = false;
break;
}
if (!reopen) {
// Don't do anything when switching _from_ opSeek mode; we
// flush when switching _to_ opSeek.
if (oldOpMode == opSeek) return 0;
// Flush. On msvcrt fflush does not do the job
std::fseek(fp_, 0, SEEK_CUR);
return 0;
}
// Reopen the file
long offset = std::ftell(fp_);
if (offset == -1) return -1;
// 'Manual' open("r+b") to avoid munmap()
if (fp_ != 0) {
std::fclose(fp_);
fp_= 0;
}
openMode_ = "r+b";
opMode_ = opSeek;
fp_ = std::fopen(path_.c_str(), openMode_.c_str());
if (!fp_) return 1;
return std::fseek(fp_, offset, SEEK_SET);
} // FileIo::Impl::switchMode
int FileIo::Impl::stat(StructStat& buf) const
{
int ret = 0;
#ifdef EXV_UNICODE_PATH
if (wpMode_ == wpUnicode) {
struct _stat st;
ret = ::_wstat(wpath_.c_str(), &st);
if (0 == ret) {
buf.st_size = st.st_size;
buf.st_mode = st.st_mode;
}
}
else
#endif
{
struct stat st;
ret = ::stat(path_.c_str(), &st);
if (0 == ret) {
buf.st_size = st.st_size;
buf.st_mode = st.st_mode;
}
}
return ret;
} // FileIo::Impl::stat
FileIo::FileIo(const std::string& path)
: p_(new Impl(path))
{
}
#ifdef EXV_UNICODE_PATH
FileIo::FileIo(const std::wstring& wpath)
: p_(new Impl(wpath))
{
}
#endif
FileIo::~FileIo()
{
close();
delete p_;
}
int FileIo::munmap()
{
int rc = 0;
if (pMappedArea_ != 0) {
if (p_->pMappedArea_ != 0) {
#if defined EXV_HAVE_MMAP && defined EXV_HAVE_MUNMAP
if (::munmap(pMappedArea_, mappedLength_) != 0) {
if (::munmap(p_->pMappedArea_, p_->mappedLength_) != 0) {
rc = 1;
}
#elif defined WIN32 && !defined __CYGWIN__
UnmapViewOfFile(p_->pMappedArea_);
CloseHandle(p_->hMap_);
p_->hMap_ = 0;
CloseHandle(p_->hFile_);
p_->hFile_ = 0;
#else
if (isWriteable_) {
write(pMappedArea_, mappedLength_);
if (p_->isWriteable_) {
write(p_->pMappedArea_, p_->mappedLength_);
}
if (isMalloced_) {
delete[] pMappedArea_;
isMalloced_ = false;
if (p_->isMalloced_) {
delete[] p_->pMappedArea_;
p_->isMalloced_ = false;
}
#endif
}
if (isWriteable_) {
if (fp_ != 0) switchMode(opRead);
isWriteable_ = false;
if (p_->isWriteable_) {
if (p_->fp_ != 0) p_->switchMode(Impl::opRead);
p_->isWriteable_ = false;
}
pMappedArea_ = 0;
mappedLength_ = 0;
p_->pMappedArea_ = 0;
p_->mappedLength_ = 0;
return rc;
}
byte* FileIo::mmap(bool isWriteable)
{
assert(fp_ != 0);
assert(p_->fp_ != 0);
if (munmap() != 0) {
throw Error(2, path_, strError(), "munmap");
throw Error(2, p_->path_, strError(), "munmap");
}
mappedLength_ = size();
isWriteable_ = isWriteable;
p_->mappedLength_ = size();
p_->isWriteable_ = isWriteable;
if (p_->isWriteable_ && p_->switchMode(Impl::opWrite) != 0) return 0;
#if defined EXV_HAVE_MMAP && defined EXV_HAVE_MUNMAP
int prot = PROT_READ;
if (isWriteable_) {
if (p_->isWriteable_) {
prot |= PROT_WRITE;
if (switchMode(opWrite) != 0) return 0;
}
void* rc = ::mmap(0, mappedLength_, prot, MAP_SHARED, fileno(fp_), 0);
void* rc = ::mmap(0, p_->mappedLength_, prot, MAP_SHARED, fileno(p_->fp_), 0);
if (MAP_FAILED == rc) {
throw Error(2, path_, strError(), "mmap");
throw Error(2, p_->path_, strError(), "mmap");
}
p_->pMappedArea_ = static_cast<byte*>(rc);
#elif defined WIN32 && !defined __CYGWIN__
// Windows implementation
// TODO: An attempt to map a file with a length of 0 (zero) fails with
// an error code of ERROR_FILE_INVALID.
// Applications should test for files with a length of 0 (zero) and
// reject those files.
DWORD dwAccess = FILE_MAP_READ;
DWORD flProtect = PAGE_READONLY;
if (isWriteable) {
dwAccess = FILE_MAP_WRITE;
flProtect = PAGE_READWRITE;
}
HANDLE hPh = GetCurrentProcess();
HANDLE hFd = (HANDLE)_get_osfhandle(fileno(p_->fp_));
if (hFd == INVALID_HANDLE_VALUE) {
throw Error(2, p_->path_, "MSG1", "_get_osfhandle");
}
pMappedArea_ = static_cast<byte*>(rc);
if (!DuplicateHandle(hPh, hFd, hPh, &p_->hFile_, 0, false, DUPLICATE_SAME_ACCESS)) {
throw Error(2, p_->path_, "MSG2", "DuplicateHandle");
}
p_->hMap_ = CreateFileMapping(p_->hFile_, 0, flProtect, 0, p_->mappedLength_, 0);
if (p_->hMap_ == 0 ) {
throw Error(2, p_->path_, "MSG3", "CreateFileMapping");
}
void* rc = MapViewOfFile(p_->hMap_, dwAccess, 0, 0, 0);
if (rc == 0) {
throw Error(2, p_->path_, "MSG4", "CreateFileMapping");
}
p_->pMappedArea_ = static_cast<byte*>(rc);
#else
// Workaround for platforms without mmap: Read the file into memory
DataBuf buf(static_cast<long>(mappedLength_));
DataBuf buf(static_cast<long>(p_->mappedLength_));
read(buf.pData_, buf.size_);
if (error() || eof()) throw Error(2, path_, strError(), "FileIo::mmap");
pMappedArea_ = buf.release().first;
isMalloced_ = true;
#endif
return pMappedArea_;
}
int FileIo::stat(StructStat& buf) const
{
int ret = 0;
#ifdef EXV_UNICODE_PATH
if (wpMode_ == wpUnicode) {
struct _stat st;
ret = ::_wstat(wpath_.c_str(), &st);
if (0 == ret) {
buf.st_size = st.st_size;
buf.st_mode = st.st_mode;
}
}
else
if (error() || eof()) throw Error(2, p_->path_, strError(), "FileIo::mmap");
p_->pMappedArea_ = buf.release().first;
p_->isMalloced_ = true;
#endif
{
struct stat st;
ret = ::stat(path_.c_str(), &st);
if (0 == ret) {
buf.st_size = st.st_size;
buf.st_mode = st.st_mode;
}
}
return ret;
return p_->pMappedArea_;
}
BasicIo::AutoPtr FileIo::temporary() const
{
BasicIo::AutoPtr basicIo;
StructStat buf;
int ret = stat(buf);
Impl::StructStat buf;
int ret = p_->stat(buf);
// If file is > 1MB then use a file, otherwise use memory buffer
if (ret != 0 || buf.st_size > 1048576) {
pid_t pid = ::getpid();
std::auto_ptr<FileIo> fileIo;
#ifdef EXV_UNICODE_PATH
if (wpMode_ == wpUnicode) {
std::wstring tmpname = wpath_ + s2ws(toString(pid));
if (p_->wpMode_ == Impl::wpUnicode) {
std::wstring tmpname = p_->wpath_ + s2ws(toString(pid));
fileIo = std::auto_ptr<FileIo>(new FileIo(tmpname));
}
else
#endif
{
std::string tmpname = path_ + toString(pid);
std::string tmpname = p_->path_ + toString(pid);
fileIo = std::auto_ptr<FileIo>(new FileIo(tmpname));
}
if (fileIo->open("w+b") != 0) {
throw Error(10, path_, "w+b", strError());
throw Error(10, p_->path_, "w+b", strError());
}
basicIo = fileIo;
}
@ -214,74 +381,26 @@ namespace Exiv2 {
return basicIo;
}
int FileIo::switchMode(OpMode opMode)
{
assert(fp_ != 0);
if (opMode_ == opMode) return 0;
OpMode oldOpMode = opMode_;
opMode_ = opMode;
bool reopen = true;
switch(opMode) {
case opRead:
// Flush if current mode allows reading, else reopen (in mode "r+b"
// as in this case we know that we can write to the file)
if (openMode_[0] == 'r' || openMode_[1] == '+') reopen = false;
break;
case opWrite:
// Flush if current mode allows writing, else reopen
if (openMode_[0] != 'r' || openMode_[1] == '+') reopen = false;
break;
case opSeek:
reopen = false;
break;
}
if (!reopen) {
// Don't do anything when switching _from_ opSeek mode; we
// flush when switching _to_ opSeek.
if (oldOpMode == opSeek) return 0;
// Flush. On msvcrt fflush does not do the job
std::fseek(fp_, 0, SEEK_CUR);
return 0;
}
// Reopen the file
long offset = std::ftell(fp_);
if (offset == -1) return -1;
// 'Manual' open("r+b") to avoid munmap()
if (fp_ != 0) {
std::fclose(fp_);
fp_= 0;
}
openMode_ = "r+b";
opMode_ = opSeek;
fp_ = std::fopen(path_.c_str(), openMode_.c_str());
if (!fp_) return 1;
return std::fseek(fp_, offset, SEEK_SET);
}
long FileIo::write(const byte* data, long wcount)
{
assert(fp_ != 0);
if (switchMode(opWrite) != 0) return 0;
return (long)std::fwrite(data, 1, wcount, fp_);
assert(p_->fp_ != 0);
if (p_->switchMode(Impl::opWrite) != 0) return 0;
return (long)std::fwrite(data, 1, wcount, p_->fp_);
}
long FileIo::write(BasicIo& src)
{
assert(fp_ != 0);
assert(p_->fp_ != 0);
if (static_cast<BasicIo*>(this) == &src) return 0;
if (!src.isopen()) return 0;
if (switchMode(opWrite) != 0) return 0;
if (p_->switchMode(Impl::opWrite) != 0) return 0;
byte buf[4096];
long readCount = 0;
long writeCount = 0;
long writeTotal = 0;
while ((readCount = src.read(buf, sizeof(buf)))) {
writeTotal += writeCount = (long)std::fwrite(buf, 1, readCount, fp_);
writeTotal += writeCount = (long)std::fwrite(buf, 1, readCount, p_->fp_);
if (writeCount != readCount) {
// try to reset back to where write stopped
src.seek(writeCount-readCount, BasicIo::cur);
@ -294,8 +413,8 @@ namespace Exiv2 {
void FileIo::transfer(BasicIo& src)
{
const bool wasOpen = (fp_ != 0);
const std::string lastMode(openMode_);
const bool wasOpen = (p_->fp_ != 0);
const std::string lastMode(p_->openMode_);
FileIo *fileIo = dynamic_cast<FileIo*>(&src);
if (fileIo) {
@ -305,15 +424,15 @@ namespace Exiv2 {
if (open("w+b") != 0) {
// Remove the (temporary) file
#ifdef EXV_UNICODE_PATH
if (fileIo->wpMode_ == wpUnicode) {
::_wremove(fileIo->wpath_.c_str());
if (fileIo->p_->wpMode_ == Impl::wpUnicode) {
::_wremove(fileIo->p_->wpath_.c_str());
}
else
#endif
{
::remove(fileIo->path_.c_str());
::remove(fileIo->p_->path_.c_str());
}
throw Error(10, path_, "w+b", strError());
throw Error(10, p_->path_, "w+b", strError());
}
close();
@ -322,13 +441,13 @@ namespace Exiv2 {
char* pf = 0;
#ifdef EXV_UNICODE_PATH
wchar_t* wpf = 0;
if (wpMode_ == wpUnicode) {
wpf = const_cast<wchar_t*>(wpath_.c_str());
if (p_->wpMode_ == Impl::wpUnicode) {
wpf = const_cast<wchar_t*>(p_->wpath_.c_str());
}
else
#endif
{
pf = const_cast<char*>(path_.c_str());
pf = const_cast<char*>(p_->path_.c_str());
}
// Get the permissions of the file, or linked-to file, on platforms which have lstat
@ -346,13 +465,13 @@ namespace Exiv2 {
}
origStMode = buf1.st_mode;
DataBuf lbuf; // So that the allocated memory is freed. Must have same scope as pf
// In case path_ is a symlink, get the path of the linked-to file
// In case p_->path_ is a symlink, get the path of the linked-to file
if (statOk && S_ISLNK(buf1.st_mode)) {
lbuf.alloc(buf1.st_size + 1);
memset(lbuf.pData_, 0x0, lbuf.size_);
pf = reinterpret_cast<char*>(lbuf.pData_);
if (::readlink(path_.c_str(), pf, lbuf.size_ - 1) == -1) {
throw Error(2, path_, strError(), "readlink");
if (::readlink(p_->path_.c_str(), pf, lbuf.size_ - 1) == -1) {
throw Error(2, p_->path_, strError(), "readlink");
}
// We need the permissions of the file, not the symlink
if (::stat(pf, &buf1) == -1) {
@ -364,8 +483,8 @@ namespace Exiv2 {
origStMode = buf1.st_mode;
}
#else // EXV_HAVE_LSTAT
StructStat buf1;
if (stat(buf1) == -1) {
Impl::StructStat buf1;
if (p_->stat(buf1) == -1) {
statOk = false;
}
origStMode = buf1.st_mode;
@ -373,14 +492,14 @@ namespace Exiv2 {
// MSVCRT rename that does not overwrite existing files
#ifdef EXV_UNICODE_PATH
if (wpMode_ == wpUnicode) {
if (p_->wpMode_ == Impl::wpUnicode) {
if (fileExists(wpf) && ::_wremove(wpf) != 0) {
throw Error(2, wpf, strError(), "::_wremove");
}
if (::_wrename(fileIo->wpath_.c_str(), wpf) == -1) {
throw Error(17, ws2s(fileIo->wpath_), wpf, strError());
if (::_wrename(fileIo->p_->wpath_.c_str(), wpf) == -1) {
throw Error(17, ws2s(fileIo->p_->wpath_), wpf, strError());
}
::_wremove(fileIo->wpath_.c_str());
::_wremove(fileIo->p_->wpath_.c_str());
// Check permissions of new file
struct _stat buf2;
if (statOk && ::_wstat(wpf, &buf2) == -1) {
@ -397,17 +516,17 @@ namespace Exiv2 {
#endif
}
}
} // if (wpMode_ == wpUnicode)
} // if (p_->wpMode_ == Impl::wpUnicode)
else
#endif // EXV_UNICODE_PATH
{
if (fileExists(pf) && ::remove(pf) != 0) {
throw Error(2, pf, strError(), "::remove");
}
if (::rename(fileIo->path_.c_str(), pf) == -1) {
throw Error(17, fileIo->path_, pf, strError());
if (::rename(fileIo->p_->path_.c_str(), pf) == -1) {
throw Error(17, fileIo->p_->path_, pf, strError());
}
::remove(fileIo->path_.c_str());
::remove(fileIo->p_->path_.c_str());
// Check permissions of new file
struct stat buf2;
if (statOk && ::stat(pf, &buf2) == -1) {
@ -429,7 +548,7 @@ namespace Exiv2 {
else {
// Generic handling, reopen both to reset to start
if (open("w+b") != 0) {
throw Error(10, path_, "w+b", strError());
throw Error(10, p_->path_, "w+b", strError());
}
if (src.open() != 0) {
throw Error(9, src.path(), strError());
@ -440,24 +559,24 @@ namespace Exiv2 {
if (wasOpen) {
if (open(lastMode) != 0) {
throw Error(10, path_, lastMode, strError());
throw Error(10, p_->path_, lastMode, strError());
}
}
else close();
if (error() || src.error()) throw Error(18, path_, strError());
if (error() || src.error()) throw Error(18, p_->path_, strError());
}
int FileIo::putb(byte data)
{
assert(fp_ != 0);
if (switchMode(opWrite) != 0) return EOF;
return putc(data, fp_);
assert(p_->fp_ != 0);
if (p_->switchMode(Impl::opWrite) != 0) return EOF;
return putc(data, p_->fp_);
}
int FileIo::seek(long offset, Position pos)
{
assert(fp_ != 0);
assert(p_->fp_ != 0);
int fileSeek = 0;
switch (pos) {
@ -466,30 +585,29 @@ namespace Exiv2 {
case BasicIo::end: fileSeek = SEEK_END; break;
}
if (switchMode(opSeek) != 0) return 1;
return std::fseek(fp_, offset, fileSeek);
if (p_->switchMode(Impl::opSeek) != 0) return 1;
return std::fseek(p_->fp_, offset, fileSeek);
}
long FileIo::tell() const
{
assert(fp_ != 0);
return std::ftell(fp_);
assert(p_->fp_ != 0);
return std::ftell(p_->fp_);
}
long FileIo::size() const
{
// Flush and commit only if the file is open for writing
if (fp_ != 0 && (openMode_[0] != 'r' || openMode_[1] == '+')) {
std::fflush(fp_);
if (p_->fp_ != 0 && (p_->openMode_[0] != 'r' || p_->openMode_[1] == '+')) {
std::fflush(p_->fp_);
#if defined WIN32 && !defined __CYGWIN__
// This is required on msvcrt before stat after writing to a file
_commit(_fileno(fp_));
_commit(_fileno(p_->fp_));
#endif
}
StructStat buf;
int ret = stat(buf);
Impl::StructStat buf;
int ret = p_->stat(buf);
if (ret != 0) return -1;
return buf.st_size;
@ -504,40 +622,40 @@ namespace Exiv2 {
int FileIo::open(const std::string& mode)
{
close();
openMode_ = mode;
opMode_ = opSeek;
p_->openMode_ = mode;
p_->opMode_ = Impl::opSeek;
#ifdef EXV_UNICODE_PATH
if (wpMode_ == wpUnicode) {
fp_ = ::_wfopen(wpath_.c_str(), s2ws(mode).c_str());
if (p_->wpMode_ == Impl::wpUnicode) {
p_->fp_ = ::_wfopen(p_->wpath_.c_str(), s2ws(mode).c_str());
}
else
#endif
{
fp_ = ::fopen(path_.c_str(), mode.c_str());
p_->fp_ = ::fopen(p_->path_.c_str(), mode.c_str());
}
if (!fp_) return 1;
if (!p_->fp_) return 1;
return 0;
}
bool FileIo::isopen() const
{
return fp_ != 0;
return p_->fp_ != 0;
}
int FileIo::close()
{
int rc = 0;
if (munmap() != 0) rc = 2;
if (fp_ != 0) {
if (std::fclose(fp_) != 0) rc |= 1;
fp_= 0;
if (p_->fp_ != 0) {
if (std::fclose(p_->fp_) != 0) rc |= 1;
p_->fp_= 0;
}
return rc;
}
DataBuf FileIo::read(long rcount)
{
assert(fp_ != 0);
assert(p_->fp_ != 0);
DataBuf buf(rcount);
long readCount = read(buf.pData_, buf.size_);
buf.size_ = readCount;
@ -546,46 +664,46 @@ namespace Exiv2 {
long FileIo::read(byte* buf, long rcount)
{
assert(fp_ != 0);
if (switchMode(opRead) != 0) return 0;
return (long)std::fread(buf, 1, rcount, fp_);
assert(p_->fp_ != 0);
if (p_->switchMode(Impl::opRead) != 0) return 0;
return (long)std::fread(buf, 1, rcount, p_->fp_);
}
int FileIo::getb()
{
assert(fp_ != 0);
if (switchMode(opRead) != 0) return EOF;
return getc(fp_);
assert(p_->fp_ != 0);
if (p_->switchMode(Impl::opRead) != 0) return EOF;
return getc(p_->fp_);
}
int FileIo::error() const
{
return fp_ != 0 ? ferror(fp_) : 0;
return p_->fp_ != 0 ? ferror(p_->fp_) : 0;
}
bool FileIo::eof() const
{
assert(fp_ != 0);
return feof(fp_) != 0;
assert(p_->fp_ != 0);
return feof(p_->fp_) != 0;
}
std::string FileIo::path() const
{
#ifdef EXV_UNICODE_PATH
if (wpMode_ == wpUnicode) {
return ws2s(wpath_);
if (p_->wpMode_ == Impl::wpUnicode) {
return ws2s(p_->wpath_);
}
#endif
return path_;
return p_->path_;
}
#ifdef EXV_UNICODE_PATH
std::wstring FileIo::wpath() const
{
if (wpMode_ == wpStandard) {
return s2ws(path_);
if (p_->wpMode_ == Impl::wpStandard) {
return s2ws(p_->path_);
}
return wpath_;
return p_->wpath_;
}
#endif
@ -627,7 +745,7 @@ namespace Exiv2 {
if (!isMalloced_) {
// Minimum size for 1st block is 32kB
long size = std::max(32768 * (1 + need / 32768), size_);
long size = EXV_MAX(32768 * (1 + need / 32768), size_);
byte* data = (byte*)std::malloc(size);
std::memcpy(data, data_, size_);
data_ = data;
@ -773,7 +891,7 @@ namespace Exiv2 {
long MemIo::read(byte* buf, long rcount)
{
long avail = size_ - idx_;
long allow = std::min(rcount, avail);
long allow = EXV_MIN(rcount, avail);
std::memcpy(buf, &data_[idx_], allow);
idx_ += allow;
if (rcount > avail) eof_ = true;

@ -35,15 +35,6 @@
// + standard includes
#include <string>
#include <memory>
#include <cstdio>
#include <sys/types.h>
#include <sys/stat.h>
// MSVC doesn't provide mode_t
#ifdef _MSC_VER
typedef unsigned short mode_t;
#endif
// *****************************************************************************
// namespace extensions
@ -505,44 +496,9 @@ namespace Exiv2 {
//! Assignment operator
FileIo& operator=(const FileIo& rhs);
// Enumeration
enum OpMode { opRead, opWrite, opSeek };
#ifdef EXV_UNICODE_PATH
enum WpMode { wpStandard, wpUnicode };
#endif
// DATA
std::string path_;
#ifdef EXV_UNICODE_PATH
std::wstring wpath_;
WpMode wpMode_;
#endif
std::string openMode_;
FILE *fp_;
OpMode opMode_;
byte* pMappedArea_;
size_t mappedLength_;
bool isMalloced_; //!< Is the mapped area allocated?
bool isWriteable_; //!< Can the mapped area be written to?
// TYPES
//! Simple struct stat wrapper for internal use
struct StructStat {
StructStat() : st_mode(0), st_size(0) {}
mode_t st_mode; //!< Permissions
off_t st_size; //!< Size
};
// METHODS
/*!
@brief Switch to a new access mode, reopening the file if needed.
Optimized to only reopen the file when it is really necessary.
@param opMode The mode to switch to.
@return 0 if successful
*/
EXV_DLLLOCAL int switchMode(OpMode opMode);
//! stat wrapper for internal use
EXV_DLLLOCAL int stat(StructStat& buf) const;
// Pimpl idiom
class Impl;
Impl* p_;
}; // class FileIo

@ -635,7 +635,7 @@ namespace Exiv2 {
assert(def != 0);
uint16_t tag = static_cast<uint16_t>(idx / cfg()->tagStep());
int32_t sz = std::min(def->size(tag, cfg()->group_), TiffEntryBase::doSize() - idx);
int32_t sz = EXV_MIN(def->size(tag, cfg()->group_), TiffEntryBase::doSize() - idx);
TiffComponent::AutoPtr tc = TiffCreator::create(tag, cfg()->group_);
TiffBinaryElement* tp = dynamic_cast<TiffBinaryElement*>(tc.get());
// The assertion typically fails if a component is not configured in
@ -1674,13 +1674,13 @@ namespace Exiv2 {
uint32_t idx = 0;
for (Components::const_iterator i = elements_.begin(); i != elements_.end(); ++i) {
idx = std::max(idx, (*i)->tag() * cfg()->tagStep());
idx = EXV_MAX(idx, (*i)->tag() * cfg()->tagStep());
idx += (*i)->size();
}
if (cfg()->hasFillers_ && def()) {
const ArrayDef* lastDef = def() + defSize() - 1;
uint16_t lastTag = static_cast<uint16_t>(lastDef->idx_ / cfg()->tagStep());
idx = std::max(idx, lastDef->idx_ + lastDef->size(lastTag, cfg()->group_));
idx = EXV_MAX(idx, lastDef->idx_ + lastDef->size(lastTag, cfg()->group_));
}
return idx;

@ -73,6 +73,10 @@ typedef __int32 int32_t;
*/
#define EXV_CALL_MEMBER_FN(object,ptrToMember) ((object).*(ptrToMember))
// Simple min and max macros
#define EXV_MIN(a,b) ((a) < (b) ? (a) : (b))
#define EXV_MAX(a,b) ((a) > (b) ? (a) : (b))
// *****************************************************************************
// forward declarations
struct tm;

Loading…
Cancel
Save