#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. */ native language is requested. */
#undef ENABLE_NLS #undef ENABLE_NLS
#endif /* !EXV_COMMERCIAL_VERSION */
/* Define to 1 if you have the `iconv' function. */ /* Define to 1 if you have the `iconv' function. */
#undef HAVE_ICONV #undef HAVE_ICONV
#endif /* !EXV_COMMERCIAL_VERSION */
/* Define to `const' or to empty, depending on the second argument of `iconv'. */ /* Define to `const' or to empty, depending on the second argument of `iconv'. */
#undef ICONV_CONST #undef ICONV_CONST

@ -573,21 +573,6 @@ copy/y ..\..\..\zlib-1.2.3\projects\visualc6\Win32_DLL_Release\zlib1.dll $(OutDi
<File <File
RelativePath="..\include\exv_msvc.h"> RelativePath="..\include\exv_msvc.h">
</File> </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> </Filter>
</Files> </Files>
<Globals> <Globals>

@ -43,14 +43,11 @@ typedef int pid_t;
native language is requested. */ native language is requested. */
# undef EXV_ENABLE_NLS # undef EXV_ENABLE_NLS
#endif /* !EXV_COMMERCIAL_VERSION */
/* Define to 1 if you have the `iconv' function. */ /* Define to 1 if you have the `iconv' function. */
# undef EXV_HAVE_ICONV # 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 as 1 if you have the `zlib' library. (0 to omit zlib) [png support] */
#define HAVE_LIBZ 1 #define HAVE_LIBZ 1
@ -72,16 +69,11 @@ typedef int pid_t;
/* Windows unicode path support */ /* Windows unicode path support */
#define EXV_UNICODE_PATH #define EXV_UNICODE_PATH
/* Define to 1 if you have the "sys/mman.h header file (and supporting code of course) */ /* Define to 1 if you have the `mmap' function. */
/* At this time (between 0.18.1 and 0.19) this is used by TIFF files to avoid reading */ /* #undef EXV_HAVE_MMAP */
/* the total file into memory returning in a 6x improvement in exiv2 on 2mb tiff files */
#define EXV_HAVE_SYS_MMAN_H 1 /* Define to 1 if you have the `munmap' function. */
#ifdef EXV_HAVE_SYS_MMAN_H /* #undef EXV_HAVE_MUNMAP */
#if EXV_HAVE_SYS_MMAN_H
#define EXV_HAVE_MMAP 1
#define EXV_HAVE_MUNMAP 1
#endif
#endif
/* Shared library support */ /* Shared library support */
#ifdef EXV_HAVE_DLL #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()); throw Error(10, path, "rb", strError());
} }
// Map it to memory // Map it to memory
const byte* pData = file.mmap(); const Exiv2::byte* pData = file.mmap();
long size = file.size(); long size = file.size();
DataBuf buf(size); DataBuf buf(size);
// Read from the memory mapped region // Read from the memory mapped region

@ -61,7 +61,13 @@ EXIV2_RCSID("@(#) $Id$")
# include <unistd.h> // for getpid, stat # include <unistd.h> // for getpid, stat
#endif #endif
// MSVC doesn't provide mode_t
#ifdef _MSC_VER
typedef unsigned short mode_t;
#endif
#if defined WIN32 && !defined __CYGWIN__ #if defined WIN32 && !defined __CYGWIN__
# include <windows.h>
# include <io.h> # include <io.h>
#endif #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), : path_(path),
#ifdef EXV_UNICODE_PATH #ifdef EXV_UNICODE_PATH
wpMode_(wpStandard), wpMode_(wpStandard),
#endif #endif
fp_(0), opMode_(opSeek), fp_(0), opMode_(opSeek),
#if defined WIN32 && !defined __CYGWIN__
hFile_(0), hMap_(0),
#endif
pMappedArea_(0), mappedLength_(0), isMalloced_(false), isWriteable_(false) pMappedArea_(0), mappedLength_(0), isMalloced_(false), isWriteable_(false)
{ {
} }
#ifdef EXV_UNICODE_PATH #ifdef EXV_UNICODE_PATH
FileIo::FileIo(const std::wstring& wpath) FileIo::Impl::Impl(const std::wstring& wpath)
: wpath_(wpath), : wpath_(wpath),
wpMode_(wpUnicode), wpMode_(wpUnicode),
fp_(0), opMode_(opSeek), fp_(0), opMode_(opSeek),
#if defined WIN32 && !defined __CYGWIN__
hFile_(0), hMap_(0),
#endif
pMappedArea_(0), mappedLength_(0), isMalloced_(false), isWriteable_(false) 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 #endif
FileIo::~FileIo() FileIo::~FileIo()
{ {
close(); close();
delete p_;
} }
int FileIo::munmap() int FileIo::munmap()
{ {
int rc = 0; int rc = 0;
if (pMappedArea_ != 0) { if (p_->pMappedArea_ != 0) {
#if defined EXV_HAVE_MMAP && defined EXV_HAVE_MUNMAP #if defined EXV_HAVE_MMAP && defined EXV_HAVE_MUNMAP
if (::munmap(pMappedArea_, mappedLength_) != 0) { if (::munmap(p_->pMappedArea_, p_->mappedLength_) != 0) {
rc = 1; rc = 1;
} }
#elif defined WIN32 && !defined __CYGWIN__
UnmapViewOfFile(p_->pMappedArea_);
CloseHandle(p_->hMap_);
p_->hMap_ = 0;
CloseHandle(p_->hFile_);
p_->hFile_ = 0;
#else #else
if (isWriteable_) { if (p_->isWriteable_) {
write(pMappedArea_, mappedLength_); write(p_->pMappedArea_, p_->mappedLength_);
} }
if (isMalloced_) { if (p_->isMalloced_) {
delete[] pMappedArea_; delete[] p_->pMappedArea_;
isMalloced_ = false; p_->isMalloced_ = false;
} }
#endif #endif
} }
if (isWriteable_) { if (p_->isWriteable_) {
if (fp_ != 0) switchMode(opRead); if (p_->fp_ != 0) p_->switchMode(Impl::opRead);
isWriteable_ = false; p_->isWriteable_ = false;
} }
pMappedArea_ = 0; p_->pMappedArea_ = 0;
mappedLength_ = 0; p_->mappedLength_ = 0;
return rc; return rc;
} }
byte* FileIo::mmap(bool isWriteable) byte* FileIo::mmap(bool isWriteable)
{ {
assert(fp_ != 0); assert(p_->fp_ != 0);
if (munmap() != 0) { if (munmap() != 0) {
throw Error(2, path_, strError(), "munmap"); throw Error(2, p_->path_, strError(), "munmap");
} }
mappedLength_ = size(); p_->mappedLength_ = size();
isWriteable_ = isWriteable; p_->isWriteable_ = isWriteable;
if (p_->isWriteable_ && p_->switchMode(Impl::opWrite) != 0) return 0;
#if defined EXV_HAVE_MMAP && defined EXV_HAVE_MUNMAP #if defined EXV_HAVE_MMAP && defined EXV_HAVE_MUNMAP
int prot = PROT_READ; int prot = PROT_READ;
if (isWriteable_) { if (p_->isWriteable_) {
prot |= PROT_WRITE; 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) { 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 #else
// Workaround for platforms without mmap: Read the file into memory // 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_); read(buf.pData_, buf.size_);
if (error() || eof()) throw Error(2, path_, strError(), "FileIo::mmap"); if (error() || eof()) throw Error(2, p_->path_, strError(), "FileIo::mmap");
pMappedArea_ = buf.release().first; p_->pMappedArea_ = buf.release().first;
isMalloced_ = true; p_->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
#endif #endif
{ return p_->pMappedArea_;
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;
} }
BasicIo::AutoPtr FileIo::temporary() const BasicIo::AutoPtr FileIo::temporary() const
{ {
BasicIo::AutoPtr basicIo; BasicIo::AutoPtr basicIo;
StructStat buf; Impl::StructStat buf;
int ret = stat(buf); int ret = p_->stat(buf);
// If file is > 1MB then use a file, otherwise use memory buffer // If file is > 1MB then use a file, otherwise use memory buffer
if (ret != 0 || buf.st_size > 1048576) { if (ret != 0 || buf.st_size > 1048576) {
pid_t pid = ::getpid(); pid_t pid = ::getpid();
std::auto_ptr<FileIo> fileIo; std::auto_ptr<FileIo> fileIo;
#ifdef EXV_UNICODE_PATH #ifdef EXV_UNICODE_PATH
if (wpMode_ == wpUnicode) { if (p_->wpMode_ == Impl::wpUnicode) {
std::wstring tmpname = wpath_ + s2ws(toString(pid)); std::wstring tmpname = p_->wpath_ + s2ws(toString(pid));
fileIo = std::auto_ptr<FileIo>(new FileIo(tmpname)); fileIo = std::auto_ptr<FileIo>(new FileIo(tmpname));
} }
else else
#endif #endif
{ {
std::string tmpname = path_ + toString(pid); std::string tmpname = p_->path_ + toString(pid);
fileIo = std::auto_ptr<FileIo>(new FileIo(tmpname)); fileIo = std::auto_ptr<FileIo>(new FileIo(tmpname));
} }
if (fileIo->open("w+b") != 0) { if (fileIo->open("w+b") != 0) {
throw Error(10, path_, "w+b", strError()); throw Error(10, p_->path_, "w+b", strError());
} }
basicIo = fileIo; basicIo = fileIo;
} }
@ -214,74 +381,26 @@ namespace Exiv2 {
return basicIo; 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) long FileIo::write(const byte* data, long wcount)
{ {
assert(fp_ != 0); assert(p_->fp_ != 0);
if (switchMode(opWrite) != 0) return 0; if (p_->switchMode(Impl::opWrite) != 0) return 0;
return (long)std::fwrite(data, 1, wcount, fp_); return (long)std::fwrite(data, 1, wcount, p_->fp_);
} }
long FileIo::write(BasicIo& src) long FileIo::write(BasicIo& src)
{ {
assert(fp_ != 0); assert(p_->fp_ != 0);
if (static_cast<BasicIo*>(this) == &src) return 0; if (static_cast<BasicIo*>(this) == &src) return 0;
if (!src.isopen()) return 0; if (!src.isopen()) return 0;
if (switchMode(opWrite) != 0) return 0; if (p_->switchMode(Impl::opWrite) != 0) return 0;
byte buf[4096]; byte buf[4096];
long readCount = 0; long readCount = 0;
long writeCount = 0; long writeCount = 0;
long writeTotal = 0; long writeTotal = 0;
while ((readCount = src.read(buf, sizeof(buf)))) { 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) { if (writeCount != readCount) {
// try to reset back to where write stopped // try to reset back to where write stopped
src.seek(writeCount-readCount, BasicIo::cur); src.seek(writeCount-readCount, BasicIo::cur);
@ -294,8 +413,8 @@ namespace Exiv2 {
void FileIo::transfer(BasicIo& src) void FileIo::transfer(BasicIo& src)
{ {
const bool wasOpen = (fp_ != 0); const bool wasOpen = (p_->fp_ != 0);
const std::string lastMode(openMode_); const std::string lastMode(p_->openMode_);
FileIo *fileIo = dynamic_cast<FileIo*>(&src); FileIo *fileIo = dynamic_cast<FileIo*>(&src);
if (fileIo) { if (fileIo) {
@ -305,15 +424,15 @@ namespace Exiv2 {
if (open("w+b") != 0) { if (open("w+b") != 0) {
// Remove the (temporary) file // Remove the (temporary) file
#ifdef EXV_UNICODE_PATH #ifdef EXV_UNICODE_PATH
if (fileIo->wpMode_ == wpUnicode) { if (fileIo->p_->wpMode_ == Impl::wpUnicode) {
::_wremove(fileIo->wpath_.c_str()); ::_wremove(fileIo->p_->wpath_.c_str());
} }
else else
#endif #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(); close();
@ -322,13 +441,13 @@ namespace Exiv2 {
char* pf = 0; char* pf = 0;
#ifdef EXV_UNICODE_PATH #ifdef EXV_UNICODE_PATH
wchar_t* wpf = 0; wchar_t* wpf = 0;
if (wpMode_ == wpUnicode) { if (p_->wpMode_ == Impl::wpUnicode) {
wpf = const_cast<wchar_t*>(wpath_.c_str()); wpf = const_cast<wchar_t*>(p_->wpath_.c_str());
} }
else else
#endif #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 // 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; origStMode = buf1.st_mode;
DataBuf lbuf; // So that the allocated memory is freed. Must have same scope as pf 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)) { if (statOk && S_ISLNK(buf1.st_mode)) {
lbuf.alloc(buf1.st_size + 1); lbuf.alloc(buf1.st_size + 1);
memset(lbuf.pData_, 0x0, lbuf.size_); memset(lbuf.pData_, 0x0, lbuf.size_);
pf = reinterpret_cast<char*>(lbuf.pData_); pf = reinterpret_cast<char*>(lbuf.pData_);
if (::readlink(path_.c_str(), pf, lbuf.size_ - 1) == -1) { if (::readlink(p_->path_.c_str(), pf, lbuf.size_ - 1) == -1) {
throw Error(2, path_, strError(), "readlink"); throw Error(2, p_->path_, strError(), "readlink");
} }
// We need the permissions of the file, not the symlink // We need the permissions of the file, not the symlink
if (::stat(pf, &buf1) == -1) { if (::stat(pf, &buf1) == -1) {
@ -364,8 +483,8 @@ namespace Exiv2 {
origStMode = buf1.st_mode; origStMode = buf1.st_mode;
} }
#else // EXV_HAVE_LSTAT #else // EXV_HAVE_LSTAT
StructStat buf1; Impl::StructStat buf1;
if (stat(buf1) == -1) { if (p_->stat(buf1) == -1) {
statOk = false; statOk = false;
} }
origStMode = buf1.st_mode; origStMode = buf1.st_mode;
@ -373,14 +492,14 @@ namespace Exiv2 {
// MSVCRT rename that does not overwrite existing files // MSVCRT rename that does not overwrite existing files
#ifdef EXV_UNICODE_PATH #ifdef EXV_UNICODE_PATH
if (wpMode_ == wpUnicode) { if (p_->wpMode_ == Impl::wpUnicode) {
if (fileExists(wpf) && ::_wremove(wpf) != 0) { if (fileExists(wpf) && ::_wremove(wpf) != 0) {
throw Error(2, wpf, strError(), "::_wremove"); throw Error(2, wpf, strError(), "::_wremove");
} }
if (::_wrename(fileIo->wpath_.c_str(), wpf) == -1) { if (::_wrename(fileIo->p_->wpath_.c_str(), wpf) == -1) {
throw Error(17, ws2s(fileIo->wpath_), wpf, strError()); 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 // Check permissions of new file
struct _stat buf2; struct _stat buf2;
if (statOk && ::_wstat(wpf, &buf2) == -1) { if (statOk && ::_wstat(wpf, &buf2) == -1) {
@ -397,17 +516,17 @@ namespace Exiv2 {
#endif #endif
} }
} }
} // if (wpMode_ == wpUnicode) } // if (p_->wpMode_ == Impl::wpUnicode)
else else
#endif // EXV_UNICODE_PATH #endif // EXV_UNICODE_PATH
{ {
if (fileExists(pf) && ::remove(pf) != 0) { if (fileExists(pf) && ::remove(pf) != 0) {
throw Error(2, pf, strError(), "::remove"); throw Error(2, pf, strError(), "::remove");
} }
if (::rename(fileIo->path_.c_str(), pf) == -1) { if (::rename(fileIo->p_->path_.c_str(), pf) == -1) {
throw Error(17, fileIo->path_, pf, strError()); throw Error(17, fileIo->p_->path_, pf, strError());
} }
::remove(fileIo->path_.c_str()); ::remove(fileIo->p_->path_.c_str());
// Check permissions of new file // Check permissions of new file
struct stat buf2; struct stat buf2;
if (statOk && ::stat(pf, &buf2) == -1) { if (statOk && ::stat(pf, &buf2) == -1) {
@ -429,7 +548,7 @@ namespace Exiv2 {
else { else {
// Generic handling, reopen both to reset to start // Generic handling, reopen both to reset to start
if (open("w+b") != 0) { if (open("w+b") != 0) {
throw Error(10, path_, "w+b", strError()); throw Error(10, p_->path_, "w+b", strError());
} }
if (src.open() != 0) { if (src.open() != 0) {
throw Error(9, src.path(), strError()); throw Error(9, src.path(), strError());
@ -440,24 +559,24 @@ namespace Exiv2 {
if (wasOpen) { if (wasOpen) {
if (open(lastMode) != 0) { if (open(lastMode) != 0) {
throw Error(10, path_, lastMode, strError()); throw Error(10, p_->path_, lastMode, strError());
} }
} }
else close(); 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) int FileIo::putb(byte data)
{ {
assert(fp_ != 0); assert(p_->fp_ != 0);
if (switchMode(opWrite) != 0) return EOF; if (p_->switchMode(Impl::opWrite) != 0) return EOF;
return putc(data, fp_); return putc(data, p_->fp_);
} }
int FileIo::seek(long offset, Position pos) int FileIo::seek(long offset, Position pos)
{ {
assert(fp_ != 0); assert(p_->fp_ != 0);
int fileSeek = 0; int fileSeek = 0;
switch (pos) { switch (pos) {
@ -466,30 +585,29 @@ namespace Exiv2 {
case BasicIo::end: fileSeek = SEEK_END; break; case BasicIo::end: fileSeek = SEEK_END; break;
} }
if (switchMode(opSeek) != 0) return 1; if (p_->switchMode(Impl::opSeek) != 0) return 1;
return std::fseek(fp_, offset, fileSeek); return std::fseek(p_->fp_, offset, fileSeek);
} }
long FileIo::tell() const long FileIo::tell() const
{ {
assert(fp_ != 0); assert(p_->fp_ != 0);
return std::ftell(fp_); return std::ftell(p_->fp_);
} }
long FileIo::size() const long FileIo::size() const
{ {
// Flush and commit only if the file is open for writing // Flush and commit only if the file is open for writing
if (fp_ != 0 && (openMode_[0] != 'r' || openMode_[1] == '+')) { if (p_->fp_ != 0 && (p_->openMode_[0] != 'r' || p_->openMode_[1] == '+')) {
std::fflush(fp_); std::fflush(p_->fp_);
#if defined WIN32 && !defined __CYGWIN__ #if defined WIN32 && !defined __CYGWIN__
// This is required on msvcrt before stat after writing to a file // This is required on msvcrt before stat after writing to a file
_commit(_fileno(fp_)); _commit(_fileno(p_->fp_));
#endif #endif
} }
StructStat buf; Impl::StructStat buf;
int ret = stat(buf); int ret = p_->stat(buf);
if (ret != 0) return -1; if (ret != 0) return -1;
return buf.st_size; return buf.st_size;
@ -504,40 +622,40 @@ namespace Exiv2 {
int FileIo::open(const std::string& mode) int FileIo::open(const std::string& mode)
{ {
close(); close();
openMode_ = mode; p_->openMode_ = mode;
opMode_ = opSeek; p_->opMode_ = Impl::opSeek;
#ifdef EXV_UNICODE_PATH #ifdef EXV_UNICODE_PATH
if (wpMode_ == wpUnicode) { if (p_->wpMode_ == Impl::wpUnicode) {
fp_ = ::_wfopen(wpath_.c_str(), s2ws(mode).c_str()); p_->fp_ = ::_wfopen(p_->wpath_.c_str(), s2ws(mode).c_str());
} }
else else
#endif #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; return 0;
} }
bool FileIo::isopen() const bool FileIo::isopen() const
{ {
return fp_ != 0; return p_->fp_ != 0;
} }
int FileIo::close() int FileIo::close()
{ {
int rc = 0; int rc = 0;
if (munmap() != 0) rc = 2; if (munmap() != 0) rc = 2;
if (fp_ != 0) { if (p_->fp_ != 0) {
if (std::fclose(fp_) != 0) rc |= 1; if (std::fclose(p_->fp_) != 0) rc |= 1;
fp_= 0; p_->fp_= 0;
} }
return rc; return rc;
} }
DataBuf FileIo::read(long rcount) DataBuf FileIo::read(long rcount)
{ {
assert(fp_ != 0); assert(p_->fp_ != 0);
DataBuf buf(rcount); DataBuf buf(rcount);
long readCount = read(buf.pData_, buf.size_); long readCount = read(buf.pData_, buf.size_);
buf.size_ = readCount; buf.size_ = readCount;
@ -546,46 +664,46 @@ namespace Exiv2 {
long FileIo::read(byte* buf, long rcount) long FileIo::read(byte* buf, long rcount)
{ {
assert(fp_ != 0); assert(p_->fp_ != 0);
if (switchMode(opRead) != 0) return 0; if (p_->switchMode(Impl::opRead) != 0) return 0;
return (long)std::fread(buf, 1, rcount, fp_); return (long)std::fread(buf, 1, rcount, p_->fp_);
} }
int FileIo::getb() int FileIo::getb()
{ {
assert(fp_ != 0); assert(p_->fp_ != 0);
if (switchMode(opRead) != 0) return EOF; if (p_->switchMode(Impl::opRead) != 0) return EOF;
return getc(fp_); return getc(p_->fp_);
} }
int FileIo::error() const int FileIo::error() const
{ {
return fp_ != 0 ? ferror(fp_) : 0; return p_->fp_ != 0 ? ferror(p_->fp_) : 0;
} }
bool FileIo::eof() const bool FileIo::eof() const
{ {
assert(fp_ != 0); assert(p_->fp_ != 0);
return feof(fp_) != 0; return feof(p_->fp_) != 0;
} }
std::string FileIo::path() const std::string FileIo::path() const
{ {
#ifdef EXV_UNICODE_PATH #ifdef EXV_UNICODE_PATH
if (wpMode_ == wpUnicode) { if (p_->wpMode_ == Impl::wpUnicode) {
return ws2s(wpath_); return ws2s(p_->wpath_);
} }
#endif #endif
return path_; return p_->path_;
} }
#ifdef EXV_UNICODE_PATH #ifdef EXV_UNICODE_PATH
std::wstring FileIo::wpath() const std::wstring FileIo::wpath() const
{ {
if (wpMode_ == wpStandard) { if (p_->wpMode_ == Impl::wpStandard) {
return s2ws(path_); return s2ws(p_->path_);
} }
return wpath_; return p_->wpath_;
} }
#endif #endif
@ -627,7 +745,7 @@ namespace Exiv2 {
if (!isMalloced_) { if (!isMalloced_) {
// Minimum size for 1st block is 32kB // 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); byte* data = (byte*)std::malloc(size);
std::memcpy(data, data_, size_); std::memcpy(data, data_, size_);
data_ = data; data_ = data;
@ -773,7 +891,7 @@ namespace Exiv2 {
long MemIo::read(byte* buf, long rcount) long MemIo::read(byte* buf, long rcount)
{ {
long avail = size_ - idx_; long avail = size_ - idx_;
long allow = std::min(rcount, avail); long allow = EXV_MIN(rcount, avail);
std::memcpy(buf, &data_[idx_], allow); std::memcpy(buf, &data_[idx_], allow);
idx_ += allow; idx_ += allow;
if (rcount > avail) eof_ = true; if (rcount > avail) eof_ = true;

@ -35,15 +35,6 @@
// + standard includes // + standard includes
#include <string> #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 // namespace extensions
@ -505,44 +496,9 @@ namespace Exiv2 {
//! Assignment operator //! Assignment operator
FileIo& operator=(const FileIo& rhs); FileIo& operator=(const FileIo& rhs);
// Enumeration // Pimpl idiom
enum OpMode { opRead, opWrite, opSeek }; class Impl;
#ifdef EXV_UNICODE_PATH Impl* p_;
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;
}; // class FileIo }; // class FileIo

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

@ -73,6 +73,10 @@ typedef __int32 int32_t;
*/ */
#define EXV_CALL_MEMBER_FN(object,ptrToMember) ((object).*(ptrToMember)) #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 // forward declarations
struct tm; struct tm;

Loading…
Cancel
Save