From 9750786a5c03b5ce3ea22b240d1b3cd34990856b Mon Sep 17 00:00:00 2001 From: Li Jin Date: Wed, 9 Apr 2025 17:40:13 +0800 Subject: Updated efsw. Fixed issue #204. --- src/3rdParty/efsw/Atomic.hpp | 18 - src/3rdParty/efsw/Debug.cpp | 0 src/3rdParty/efsw/Debug.hpp | 6 +- src/3rdParty/efsw/DirWatcherGeneric.cpp | 0 src/3rdParty/efsw/DirWatcherGeneric.hpp | 0 src/3rdParty/efsw/DirectorySnapshot.cpp | 4 +- src/3rdParty/efsw/DirectorySnapshot.hpp | 0 src/3rdParty/efsw/DirectorySnapshotDiff.cpp | 0 src/3rdParty/efsw/DirectorySnapshotDiff.hpp | 0 src/3rdParty/efsw/FileInfo.cpp | 14 +- src/3rdParty/efsw/FileInfo.hpp | 10 +- src/3rdParty/efsw/FileSystem.cpp | 59 +- src/3rdParty/efsw/FileSystem.hpp | 4 +- src/3rdParty/efsw/FileWatcher.cpp | 239 ++-- src/3rdParty/efsw/FileWatcherCWrapper.cpp | 26 +- src/3rdParty/efsw/FileWatcherFSEvents.cpp | 114 +- src/3rdParty/efsw/FileWatcherFSEvents.hpp | 49 +- src/3rdParty/efsw/FileWatcherGeneric.cpp | 8 +- src/3rdParty/efsw/FileWatcherGeneric.hpp | 19 +- src/3rdParty/efsw/FileWatcherImpl.cpp | 57 +- src/3rdParty/efsw/FileWatcherImpl.hpp | 121 +- src/3rdParty/efsw/FileWatcherInotify.cpp | 140 +- src/3rdParty/efsw/FileWatcherInotify.hpp | 22 +- src/3rdParty/efsw/FileWatcherKqueue.cpp | 8 +- src/3rdParty/efsw/FileWatcherKqueue.hpp | 17 +- src/3rdParty/efsw/FileWatcherWin32.cpp | 524 +++---- src/3rdParty/efsw/FileWatcherWin32.hpp | 141 +- src/3rdParty/efsw/LICENSE | 0 src/3rdParty/efsw/Lock.hpp | 0 src/3rdParty/efsw/Log.cpp | 19 +- src/3rdParty/efsw/Mutex.cpp | 0 src/3rdParty/efsw/Mutex.hpp | 0 src/3rdParty/efsw/String.cpp | 0 src/3rdParty/efsw/String.hpp | 3 +- src/3rdParty/efsw/System.cpp | 0 src/3rdParty/efsw/System.hpp | 0 src/3rdParty/efsw/Thread.cpp | 3 +- src/3rdParty/efsw/Thread.hpp | 0 src/3rdParty/efsw/Utf.hpp | 1442 ++++++++++---------- src/3rdParty/efsw/Utf.inl | 1152 ++++++++-------- src/3rdParty/efsw/Watcher.cpp | 0 src/3rdParty/efsw/Watcher.hpp | 0 src/3rdParty/efsw/WatcherFSEvents.cpp | 80 +- src/3rdParty/efsw/WatcherFSEvents.hpp | 47 +- src/3rdParty/efsw/WatcherGeneric.cpp | 0 src/3rdParty/efsw/WatcherGeneric.hpp | 2 +- src/3rdParty/efsw/WatcherInotify.cpp | 4 - src/3rdParty/efsw/WatcherInotify.hpp | 4 +- src/3rdParty/efsw/WatcherKqueue.cpp | 15 +- src/3rdParty/efsw/WatcherKqueue.hpp | 2 +- src/3rdParty/efsw/WatcherWin32.cpp | 214 ++- src/3rdParty/efsw/WatcherWin32.hpp | 25 +- src/3rdParty/efsw/base.hpp | 0 src/3rdParty/efsw/efsw.h | 175 ++- src/3rdParty/efsw/efsw.hpp | 456 ++++--- src/3rdParty/efsw/inotify-nosys.h | 0 src/3rdParty/efsw/platform/platformimpl.hpp | 0 .../efsw/platform/posix/FileSystemImpl.cpp | 0 .../efsw/platform/posix/FileSystemImpl.hpp | 0 src/3rdParty/efsw/platform/posix/MutexImpl.cpp | 0 src/3rdParty/efsw/platform/posix/MutexImpl.hpp | 0 src/3rdParty/efsw/platform/posix/SystemImpl.cpp | 0 src/3rdParty/efsw/platform/posix/SystemImpl.hpp | 0 src/3rdParty/efsw/platform/posix/ThreadImpl.cpp | 17 +- src/3rdParty/efsw/platform/posix/ThreadImpl.hpp | 7 +- src/3rdParty/efsw/platform/win/FileSystemImpl.cpp | 0 src/3rdParty/efsw/platform/win/FileSystemImpl.hpp | 0 src/3rdParty/efsw/platform/win/MutexImpl.cpp | 0 src/3rdParty/efsw/platform/win/MutexImpl.hpp | 0 src/3rdParty/efsw/platform/win/SystemImpl.cpp | 0 src/3rdParty/efsw/platform/win/SystemImpl.hpp | 0 src/3rdParty/efsw/platform/win/ThreadImpl.cpp | 2 +- src/3rdParty/efsw/platform/win/ThreadImpl.hpp | 2 +- src/3rdParty/efsw/sophist.h | 294 ++-- 74 files changed, 2952 insertions(+), 2613 deletions(-) mode change 100755 => 100644 src/3rdParty/efsw/Atomic.hpp mode change 100755 => 100644 src/3rdParty/efsw/Debug.cpp mode change 100755 => 100644 src/3rdParty/efsw/Debug.hpp mode change 100755 => 100644 src/3rdParty/efsw/DirWatcherGeneric.cpp mode change 100755 => 100644 src/3rdParty/efsw/DirWatcherGeneric.hpp mode change 100755 => 100644 src/3rdParty/efsw/DirectorySnapshot.cpp mode change 100755 => 100644 src/3rdParty/efsw/DirectorySnapshot.hpp mode change 100755 => 100644 src/3rdParty/efsw/DirectorySnapshotDiff.cpp mode change 100755 => 100644 src/3rdParty/efsw/DirectorySnapshotDiff.hpp mode change 100755 => 100644 src/3rdParty/efsw/FileInfo.cpp mode change 100755 => 100644 src/3rdParty/efsw/FileInfo.hpp mode change 100755 => 100644 src/3rdParty/efsw/FileSystem.cpp mode change 100755 => 100644 src/3rdParty/efsw/FileSystem.hpp mode change 100755 => 100644 src/3rdParty/efsw/FileWatcher.cpp mode change 100755 => 100644 src/3rdParty/efsw/FileWatcherCWrapper.cpp mode change 100755 => 100644 src/3rdParty/efsw/FileWatcherFSEvents.cpp mode change 100755 => 100644 src/3rdParty/efsw/FileWatcherFSEvents.hpp mode change 100755 => 100644 src/3rdParty/efsw/FileWatcherGeneric.cpp mode change 100755 => 100644 src/3rdParty/efsw/FileWatcherGeneric.hpp mode change 100755 => 100644 src/3rdParty/efsw/FileWatcherImpl.cpp mode change 100755 => 100644 src/3rdParty/efsw/FileWatcherImpl.hpp mode change 100755 => 100644 src/3rdParty/efsw/FileWatcherInotify.cpp mode change 100755 => 100644 src/3rdParty/efsw/FileWatcherInotify.hpp mode change 100755 => 100644 src/3rdParty/efsw/FileWatcherKqueue.cpp mode change 100755 => 100644 src/3rdParty/efsw/FileWatcherKqueue.hpp mode change 100755 => 100644 src/3rdParty/efsw/FileWatcherWin32.cpp mode change 100755 => 100644 src/3rdParty/efsw/FileWatcherWin32.hpp mode change 100755 => 100644 src/3rdParty/efsw/LICENSE mode change 100755 => 100644 src/3rdParty/efsw/Lock.hpp mode change 100755 => 100644 src/3rdParty/efsw/Log.cpp mode change 100755 => 100644 src/3rdParty/efsw/Mutex.cpp mode change 100755 => 100644 src/3rdParty/efsw/Mutex.hpp mode change 100755 => 100644 src/3rdParty/efsw/String.cpp mode change 100755 => 100644 src/3rdParty/efsw/String.hpp mode change 100755 => 100644 src/3rdParty/efsw/System.cpp mode change 100755 => 100644 src/3rdParty/efsw/System.hpp mode change 100755 => 100644 src/3rdParty/efsw/Thread.cpp mode change 100755 => 100644 src/3rdParty/efsw/Thread.hpp mode change 100755 => 100644 src/3rdParty/efsw/Utf.hpp mode change 100755 => 100644 src/3rdParty/efsw/Utf.inl mode change 100755 => 100644 src/3rdParty/efsw/Watcher.cpp mode change 100755 => 100644 src/3rdParty/efsw/Watcher.hpp mode change 100755 => 100644 src/3rdParty/efsw/WatcherFSEvents.cpp mode change 100755 => 100644 src/3rdParty/efsw/WatcherFSEvents.hpp mode change 100755 => 100644 src/3rdParty/efsw/WatcherGeneric.cpp mode change 100755 => 100644 src/3rdParty/efsw/WatcherGeneric.hpp mode change 100755 => 100644 src/3rdParty/efsw/WatcherInotify.cpp mode change 100755 => 100644 src/3rdParty/efsw/WatcherInotify.hpp mode change 100755 => 100644 src/3rdParty/efsw/WatcherKqueue.cpp mode change 100755 => 100644 src/3rdParty/efsw/WatcherKqueue.hpp mode change 100755 => 100644 src/3rdParty/efsw/WatcherWin32.cpp mode change 100755 => 100644 src/3rdParty/efsw/WatcherWin32.hpp mode change 100755 => 100644 src/3rdParty/efsw/base.hpp mode change 100755 => 100644 src/3rdParty/efsw/efsw.h mode change 100755 => 100644 src/3rdParty/efsw/efsw.hpp mode change 100755 => 100644 src/3rdParty/efsw/inotify-nosys.h mode change 100755 => 100644 src/3rdParty/efsw/platform/platformimpl.hpp mode change 100755 => 100644 src/3rdParty/efsw/platform/posix/FileSystemImpl.cpp mode change 100755 => 100644 src/3rdParty/efsw/platform/posix/FileSystemImpl.hpp mode change 100755 => 100644 src/3rdParty/efsw/platform/posix/MutexImpl.cpp mode change 100755 => 100644 src/3rdParty/efsw/platform/posix/MutexImpl.hpp mode change 100755 => 100644 src/3rdParty/efsw/platform/posix/SystemImpl.cpp mode change 100755 => 100644 src/3rdParty/efsw/platform/posix/SystemImpl.hpp mode change 100755 => 100644 src/3rdParty/efsw/platform/posix/ThreadImpl.cpp mode change 100755 => 100644 src/3rdParty/efsw/platform/posix/ThreadImpl.hpp mode change 100755 => 100644 src/3rdParty/efsw/platform/win/FileSystemImpl.cpp mode change 100755 => 100644 src/3rdParty/efsw/platform/win/FileSystemImpl.hpp mode change 100755 => 100644 src/3rdParty/efsw/platform/win/MutexImpl.cpp mode change 100755 => 100644 src/3rdParty/efsw/platform/win/MutexImpl.hpp mode change 100755 => 100644 src/3rdParty/efsw/platform/win/SystemImpl.cpp mode change 100755 => 100644 src/3rdParty/efsw/platform/win/SystemImpl.hpp mode change 100755 => 100644 src/3rdParty/efsw/platform/win/ThreadImpl.cpp mode change 100755 => 100644 src/3rdParty/efsw/platform/win/ThreadImpl.hpp mode change 100755 => 100644 src/3rdParty/efsw/sophist.h (limited to 'src') diff --git a/src/3rdParty/efsw/Atomic.hpp b/src/3rdParty/efsw/Atomic.hpp old mode 100755 new mode 100644 index 4008dfc..9015c60 --- a/src/3rdParty/efsw/Atomic.hpp +++ b/src/3rdParty/efsw/Atomic.hpp @@ -3,9 +3,7 @@ #include -#ifdef EFSW_USE_CXX11 #include -#endif namespace efsw { @@ -14,36 +12,20 @@ template class Atomic { explicit Atomic( T set = false ) : set_( set ) {} Atomic& operator=( T set ) { -#ifdef EFSW_USE_CXX11 set_.store( set, std::memory_order_release ); -#else - set_ = set; -#endif return *this; } explicit operator T() const { -#ifdef EFSW_USE_CXX11 return set_.load( std::memory_order_acquire ); -#else - return set_; -#endif } T load() const { -#ifdef EFSW_USE_CXX11 return set_.load( std::memory_order_acquire ); -#else - return set_; -#endif } private: -#ifdef EFSW_USE_CXX11 std::atomic set_; -#else - volatile T set_; -#endif }; } // namespace efsw diff --git a/src/3rdParty/efsw/Debug.cpp b/src/3rdParty/efsw/Debug.cpp old mode 100755 new mode 100644 diff --git a/src/3rdParty/efsw/Debug.hpp b/src/3rdParty/efsw/Debug.hpp old mode 100755 new mode 100644 index 78d3557..fefaec4 --- a/src/3rdParty/efsw/Debug.hpp +++ b/src/3rdParty/efsw/Debug.hpp @@ -49,8 +49,10 @@ void efPRINTC( unsigned int cond, const char* format, ... ); #define efDEBUGC( cond, format, args... ) \ {} #else -#define efDEBUG -#define efDEBUGC +#define efDEBUG( ... ) \ + {} +#define efDEBUGC( ... ) \ + {} #endif #endif diff --git a/src/3rdParty/efsw/DirWatcherGeneric.cpp b/src/3rdParty/efsw/DirWatcherGeneric.cpp old mode 100755 new mode 100644 diff --git a/src/3rdParty/efsw/DirWatcherGeneric.hpp b/src/3rdParty/efsw/DirWatcherGeneric.hpp old mode 100755 new mode 100644 diff --git a/src/3rdParty/efsw/DirectorySnapshot.cpp b/src/3rdParty/efsw/DirectorySnapshot.cpp old mode 100755 new mode 100644 index 6049e4a..f78475f --- a/src/3rdParty/efsw/DirectorySnapshot.cpp +++ b/src/3rdParty/efsw/DirectorySnapshot.cpp @@ -44,7 +44,7 @@ void DirectorySnapshot::initFiles() { Files = FileSystem::filesInfoFromPath( DirectoryInfo.Filepath ); FileInfoMap::iterator it = Files.begin(); - std::list eraseFiles; + std::vector eraseFiles; /// Remove all non regular files and non directories for ( ; it != Files.end(); it++ ) { @@ -53,7 +53,7 @@ void DirectorySnapshot::initFiles() { } } - for ( std::list::iterator eit = eraseFiles.begin(); eit != eraseFiles.end(); + for ( std::vector::iterator eit = eraseFiles.begin(); eit != eraseFiles.end(); eit++ ) { Files.erase( *eit ); } diff --git a/src/3rdParty/efsw/DirectorySnapshot.hpp b/src/3rdParty/efsw/DirectorySnapshot.hpp old mode 100755 new mode 100644 diff --git a/src/3rdParty/efsw/DirectorySnapshotDiff.cpp b/src/3rdParty/efsw/DirectorySnapshotDiff.cpp old mode 100755 new mode 100644 diff --git a/src/3rdParty/efsw/DirectorySnapshotDiff.hpp b/src/3rdParty/efsw/DirectorySnapshotDiff.hpp old mode 100755 new mode 100644 diff --git a/src/3rdParty/efsw/FileInfo.cpp b/src/3rdParty/efsw/FileInfo.cpp old mode 100755 new mode 100644 index 072cd6d..707f617 --- a/src/3rdParty/efsw/FileInfo.cpp +++ b/src/3rdParty/efsw/FileInfo.cpp @@ -35,10 +35,6 @@ #endif #endif -#if EFSW_PLATFORM != EFSW_PLATFORM_WIN32 -#include -#endif - namespace efsw { bool FileInfo::exists( const std::string& filePath ) { @@ -186,12 +182,14 @@ bool FileInfo::isLink() const { std::string FileInfo::linksTo() { #if EFSW_PLATFORM != EFSW_PLATFORM_WIN32 if ( isLink() ) { - std::error_code ec; - auto ch = std::filesystem::canonical( Filepath, ec ); + char* ch = realpath( Filepath.c_str(), NULL ); + + if ( NULL != ch ) { + std::string tstr( ch ); - if ( !ec ) { + free( ch ); - return ch.string(); + return tstr; } } #endif diff --git a/src/3rdParty/efsw/FileInfo.hpp b/src/3rdParty/efsw/FileInfo.hpp old mode 100755 new mode 100644 index f1bcf4b..1b55c35 --- a/src/3rdParty/efsw/FileInfo.hpp +++ b/src/3rdParty/efsw/FileInfo.hpp @@ -2,7 +2,7 @@ #define EFSW_FILEINFO_HPP #include -#include +#include #include #include @@ -18,11 +18,11 @@ class FileInfo { FileInfo(); - explicit FileInfo( const std::string& filepath ); + FileInfo( const std::string& filepath ); FileInfo( const std::string& filepath, bool linkInfo ); - FileInfo( const FileInfo& ) = default; + FileInfo(const FileInfo&) = default; bool operator==( const FileInfo& Other ) const; @@ -58,8 +58,8 @@ class FileInfo { }; typedef std::map FileInfoMap; -typedef std::list FileInfoList; -typedef std::list> MovedList; +typedef std::vector FileInfoList; +typedef std::vector> MovedList; } // namespace efsw diff --git a/src/3rdParty/efsw/FileSystem.cpp b/src/3rdParty/efsw/FileSystem.cpp old mode 100755 new mode 100644 index 867f120..1ed346c --- a/src/3rdParty/efsw/FileSystem.cpp +++ b/src/3rdParty/efsw/FileSystem.cpp @@ -1,10 +1,19 @@ +#include #include #include +#include #if EFSW_OS == EFSW_OS_MACOSX #include #endif +#if EFSW_OS == EFSW_OS_WIN +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif +#include +#endif + namespace efsw { bool FileSystem::isDirectory( const std::string& path ) { @@ -26,13 +35,13 @@ bool FileSystem::slashAtEnd( std::string& dir ) { } void FileSystem::dirAddSlashAtEnd( std::string& dir ) { - if ( dir.size() > 1 && dir[dir.size() - 1] != getOSSlash() ) { + if ( dir.size() >= 1 && dir[dir.size() - 1] != getOSSlash() ) { dir.push_back( getOSSlash() ); } } void FileSystem::dirRemoveSlashAtEnd( std::string& dir ) { - if ( dir.size() > 1 && dir[dir.size() - 1] == getOSSlash() ) { + if ( dir.size() >= 1 && dir[dir.size() - 1] == getOSSlash() ) { dir.erase( dir.size() - 1 ); } } @@ -91,13 +100,30 @@ std::string FileSystem::precomposeFileName( const std::string& name ) { CFStringNormalize( cfMutable, kCFStringNormalizationFormC ); - char c_str[255 + 1]; - CFStringGetCString( cfMutable, c_str, sizeof( c_str ) - 1, kCFStringEncodingUTF8 ); - - CFRelease( cfStringRef ); - CFRelease( cfMutable ); + const char* c_str = CFStringGetCStringPtr( cfMutable, kCFStringEncodingUTF8 ); + if ( c_str != NULL ) { + std::string result( c_str ); + CFRelease( cfStringRef ); + CFRelease( cfMutable ); + return result; + } + CFIndex length = CFStringGetLength( cfMutable ); + CFIndex maxSize = CFStringGetMaximumSizeForEncoding( length, kCFStringEncodingUTF8 ); + if ( maxSize == kCFNotFound ) { + CFRelease( cfStringRef ); + CFRelease( cfMutable ); + return std::string(); + } - return std::string( c_str ); + std::string result( maxSize + 1, '\0' ); + if ( CFStringGetCString( cfMutable, &result[0], result.size(), kCFStringEncodingUTF8 ) ) { + result.resize( std::strlen( result.c_str() ) ); + CFRelease( cfStringRef ); + CFRelease( cfMutable ); + } else { + result.clear(); + } + return result; #else return name; #endif @@ -115,4 +141,21 @@ std::string FileSystem::getCurrentWorkingDirectory() { return Platform::FileSystem::getCurrentWorkingDirectory(); } +std::string FileSystem::getRealPath( const std::string& path ) { + std::string realPath; +#if defined( EFSW_PLATFORM_POSIX ) + char dir[PATH_MAX]; + realpath( path.c_str(), &dir[0] ); + realPath = std::string( dir ); +#elif EFSW_OS == EFSW_OS_WIN + wchar_t dir[_MAX_PATH + 1]; + GetFullPathNameW( String::fromUtf8( path ).toWideString().c_str(), _MAX_PATH, &dir[0], + nullptr ); + realPath = String( dir ).toUtf8(); +#else +#warning FileSystem::getRealPath() not implemented on this platform. +#endif + return realPath; +} + } // namespace efsw diff --git a/src/3rdParty/efsw/FileSystem.hpp b/src/3rdParty/efsw/FileSystem.hpp old mode 100755 new mode 100644 index 6c24386..1d66ece --- a/src/3rdParty/efsw/FileSystem.hpp +++ b/src/3rdParty/efsw/FileSystem.hpp @@ -3,7 +3,6 @@ #include #include -#include namespace efsw { @@ -34,6 +33,9 @@ class FileSystem { static bool changeWorkingDirectory( const std::string& path ); static std::string getCurrentWorkingDirectory(); + + static std::string getRealPath( const std::string& path ); + }; } // namespace efsw diff --git a/src/3rdParty/efsw/FileWatcher.cpp b/src/3rdParty/efsw/FileWatcher.cpp old mode 100755 new mode 100644 index 696a46f..f45b243 --- a/src/3rdParty/efsw/FileWatcher.cpp +++ b/src/3rdParty/efsw/FileWatcher.cpp @@ -1,119 +1,120 @@ -#include -#include -#include -#include - -#if EFSW_PLATFORM == EFSW_PLATFORM_WIN32 -#include -#define FILEWATCHER_IMPL FileWatcherWin32 -#define BACKEND_NAME "Win32" -#elif EFSW_PLATFORM == EFSW_PLATFORM_INOTIFY -#include -#define FILEWATCHER_IMPL FileWatcherInotify -#define BACKEND_NAME "Inotify" -#elif EFSW_PLATFORM == EFSW_PLATFORM_KQUEUE -#include -#define FILEWATCHER_IMPL FileWatcherKqueue -#define BACKEND_NAME "Kqueue" -#elif EFSW_PLATFORM == EFSW_PLATFORM_FSEVENTS -#include -#define FILEWATCHER_IMPL FileWatcherFSEvents -#define BACKEND_NAME "FSEvents" -#else -#define FILEWATCHER_IMPL FileWatcherGeneric -#define BACKEND_NAME "Generic" -#endif - -#include - -namespace efsw { - -FileWatcher::FileWatcher() : mFollowSymlinks( false ), mOutOfScopeLinks( false ) { - efDEBUG( "Using backend: %s\n", BACKEND_NAME ); - - mImpl = new FILEWATCHER_IMPL( this ); - - if ( !mImpl->initOK() ) { - efSAFE_DELETE( mImpl ); - - efDEBUG( "Falled back to backend: %s\n", BACKEND_NAME ); - - mImpl = new FileWatcherGeneric( this ); - } -} - -FileWatcher::FileWatcher( bool useGenericFileWatcher ) : - mFollowSymlinks( false ), mOutOfScopeLinks( false ) { - if ( useGenericFileWatcher ) { - efDEBUG( "Using backend: Generic\n" ); - - mImpl = new FileWatcherGeneric( this ); - } else { - efDEBUG( "Using backend: %s\n", BACKEND_NAME ); - - mImpl = new FILEWATCHER_IMPL( this ); - - if ( !mImpl->initOK() ) { - efSAFE_DELETE( mImpl ); - - efDEBUG( "Falled back to backend: %s\n", BACKEND_NAME ); - - mImpl = new FileWatcherGeneric( this ); - } - } -} - -FileWatcher::~FileWatcher() { - efSAFE_DELETE( mImpl ); -} - -WatchID FileWatcher::addWatch( const std::string& directory, FileWatchListener* watcher ) { - if ( mImpl->mIsGeneric || !FileSystem::isRemoteFS( directory ) ) { - return mImpl->addWatch( directory, watcher, false ); - } else { - return Errors::Log::createLastError( Errors::FileRemote, directory ); - } -} - -WatchID FileWatcher::addWatch( const std::string& directory, FileWatchListener* watcher, - bool recursive ) { - if ( mImpl->mIsGeneric || !FileSystem::isRemoteFS( directory ) ) { - return mImpl->addWatch( directory, watcher, recursive ); - } else { - return Errors::Log::createLastError( Errors::FileRemote, directory ); - } -} - -void FileWatcher::removeWatch( const std::string& directory ) { - mImpl->removeWatch( directory ); -} - -void FileWatcher::removeWatch( WatchID watchid ) { - mImpl->removeWatch( watchid ); -} - -void FileWatcher::watch() { - mImpl->watch(); -} - -std::list FileWatcher::directories() { - return mImpl->directories(); -} - -void FileWatcher::followSymlinks( bool follow ) { - mFollowSymlinks = follow; -} - -const bool& FileWatcher::followSymlinks() const { - return mFollowSymlinks; -} - -void FileWatcher::allowOutOfScopeLinks( bool allow ) { - mOutOfScopeLinks = allow; -} - -const bool& FileWatcher::allowOutOfScopeLinks() const { - return mOutOfScopeLinks; -} - -} // namespace efsw +#include +#include +#include +#include + +#if EFSW_PLATFORM == EFSW_PLATFORM_WIN32 +#include +#define FILEWATCHER_IMPL FileWatcherWin32 +#define BACKEND_NAME "Win32" +#elif EFSW_PLATFORM == EFSW_PLATFORM_INOTIFY +#include +#define FILEWATCHER_IMPL FileWatcherInotify +#define BACKEND_NAME "Inotify" +#elif EFSW_PLATFORM == EFSW_PLATFORM_KQUEUE +#include +#define FILEWATCHER_IMPL FileWatcherKqueue +#define BACKEND_NAME "Kqueue" +#elif EFSW_PLATFORM == EFSW_PLATFORM_FSEVENTS +#include +#define FILEWATCHER_IMPL FileWatcherFSEvents +#define BACKEND_NAME "FSEvents" +#else +#define FILEWATCHER_IMPL FileWatcherGeneric +#define BACKEND_NAME "Generic" +#endif + +#include + +namespace efsw { + +FileWatcher::FileWatcher() : mFollowSymlinks( false ), mOutOfScopeLinks( false ) { + efDEBUG( "Using backend: %s\n", BACKEND_NAME ); + + mImpl = new FILEWATCHER_IMPL( this ); + + if ( !mImpl->initOK() ) { + efSAFE_DELETE( mImpl ); + + efDEBUG( "Falled back to backend: %s\n", BACKEND_NAME ); + + mImpl = new FileWatcherGeneric( this ); + } +} + +FileWatcher::FileWatcher( bool useGenericFileWatcher ) : + mFollowSymlinks( false ), mOutOfScopeLinks( false ) { + if ( useGenericFileWatcher ) { + efDEBUG( "Using backend: Generic\n" ); + + mImpl = new FileWatcherGeneric( this ); + } else { + efDEBUG( "Using backend: %s\n", BACKEND_NAME ); + + mImpl = new FILEWATCHER_IMPL( this ); + + if ( !mImpl->initOK() ) { + efSAFE_DELETE( mImpl ); + + efDEBUG( "Falled back to backend: %s\n", BACKEND_NAME ); + + mImpl = new FileWatcherGeneric( this ); + } + } +} + +FileWatcher::~FileWatcher() { + efSAFE_DELETE( mImpl ); +} + +WatchID FileWatcher::addWatch( const std::string& directory, FileWatchListener* watcher ) { + return addWatch( directory, watcher, false, {} ); +} + +WatchID FileWatcher::addWatch( const std::string& directory, FileWatchListener* watcher, + bool recursive ) { + return addWatch( directory, watcher, recursive, {} ); +} + +WatchID FileWatcher::addWatch( const std::string& directory, FileWatchListener* watcher, + bool recursive, const std::vector& options ) { + if ( mImpl->mIsGeneric || !FileSystem::isRemoteFS( directory ) ) { + return mImpl->addWatch( directory, watcher, recursive, options ); + } else { + return Errors::Log::createLastError( Errors::FileRemote, directory ); + } +} + +void FileWatcher::removeWatch( const std::string& directory ) { + mImpl->removeWatch( directory ); +} + +void FileWatcher::removeWatch( WatchID watchid ) { + mImpl->removeWatch( watchid ); +} + +void FileWatcher::watch() { + mImpl->watch(); +} + +std::vector FileWatcher::directories() { + return mImpl->directories(); +} + +void FileWatcher::followSymlinks( bool follow ) { + mFollowSymlinks = follow; +} + +const bool& FileWatcher::followSymlinks() const { + return mFollowSymlinks; +} + +void FileWatcher::allowOutOfScopeLinks( bool allow ) { + mOutOfScopeLinks = allow; +} + +const bool& FileWatcher::allowOutOfScopeLinks() const { + return mOutOfScopeLinks; +} + +} // namespace efsw diff --git a/src/3rdParty/efsw/FileWatcherCWrapper.cpp b/src/3rdParty/efsw/FileWatcherCWrapper.cpp old mode 100755 new mode 100644 index 5c49a66..8712d6e --- a/src/3rdParty/efsw/FileWatcherCWrapper.cpp +++ b/src/3rdParty/efsw/FileWatcherCWrapper.cpp @@ -28,12 +28,12 @@ class Watcher_CAPI : public efsw::FileWatchListener { */ static std::vector g_callbacks; -Watcher_CAPI* find_callback( efsw_watcher watcher, efsw_pfn_fileaction_callback fn ) { +Watcher_CAPI* find_callback( efsw_watcher watcher, efsw_pfn_fileaction_callback fn, void* param ) { for ( std::vector::iterator i = g_callbacks.begin(); i != g_callbacks.end(); ++i ) { Watcher_CAPI* callback = *i; - if ( callback->mFn == fn && callback->mWatcher == watcher ) + if ( callback->mFn == fn && callback->mWatcher == watcher && callback->mParam == param ) return *i; } @@ -71,17 +71,35 @@ const char* efsw_getlasterror() { return log_str.c_str(); } +EFSW_API void efsw_clearlasterror() { + efsw::Errors::Log::clearLastError(); +} + efsw_watchid efsw_addwatch( efsw_watcher watcher, const char* directory, efsw_pfn_fileaction_callback callback_fn, int recursive, void* param ) { - Watcher_CAPI* callback = find_callback( watcher, callback_fn ); + return efsw_addwatch_withoptions( watcher, directory, callback_fn, recursive, 0, 0, param ); +} + +efsw_watchid efsw_addwatch_withoptions(efsw_watcher watcher, const char* directory, + efsw_pfn_fileaction_callback callback_fn, int recursive, + efsw_watcher_option *options, int options_number, + void* param) { + Watcher_CAPI* callback = find_callback( watcher, callback_fn, param ); if ( callback == NULL ) { callback = new Watcher_CAPI( watcher, callback_fn, param ); g_callbacks.push_back( callback ); } + std::vector watcher_options{}; + for ( int i = 0; i < options_number; i++ ) { + efsw_watcher_option* option = &options[i]; + watcher_options.emplace_back( efsw::WatcherOption{ + static_cast(option->option), option->value } ); + } + return ( (efsw::FileWatcher*)watcher ) - ->addWatch( std::string( directory ), callback, TOBOOL( recursive ) ); + ->addWatch( std::string( directory ), callback, TOBOOL( recursive ), watcher_options ); } void efsw_removewatch( efsw_watcher watcher, const char* directory ) { diff --git a/src/3rdParty/efsw/FileWatcherFSEvents.cpp b/src/3rdParty/efsw/FileWatcherFSEvents.cpp old mode 100755 new mode 100644 index bcfdbe6..70ec2b1 --- a/src/3rdParty/efsw/FileWatcherFSEvents.cpp +++ b/src/3rdParty/efsw/FileWatcherFSEvents.cpp @@ -41,6 +41,32 @@ bool FileWatcherFSEvents::isGranular() { return getOSXReleaseNumber() >= 11; } +static std::string convertCFStringToStdString( CFStringRef cfString ) { + // Try to get the C string pointer directly + const char* cStr = CFStringGetCStringPtr( cfString, kCFStringEncodingUTF8 ); + + if ( cStr ) { + // If the pointer is valid, directly return a std::string from it + return std::string( cStr ); + } else { + // If not, manually convert it + CFIndex length = CFStringGetLength( cfString ); + CFIndex maxSize = CFStringGetMaximumSizeForEncoding( length, kCFStringEncodingUTF8 ) + + 1; // +1 for null terminator + + char* buffer = new char[maxSize]; + + if ( CFStringGetCString( cfString, buffer, maxSize, kCFStringEncodingUTF8 ) ) { + std::string result( buffer ); + delete[] buffer; + return result; + } else { + delete[] buffer; + return ""; + } + } +} + void FileWatcherFSEvents::FSEventCallback( ConstFSEventStreamRef /*streamRef*/, void* userData, size_t numEvents, void* eventPaths, const FSEventStreamEventFlags eventFlags[], @@ -51,8 +77,24 @@ void FileWatcherFSEvents::FSEventCallback( ConstFSEventStreamRef /*streamRef*/, events.reserve( numEvents ); for ( size_t i = 0; i < numEvents; i++ ) { - events.push_back( FSEvent( std::string( ( (char**)eventPaths )[i] ), (long)eventFlags[i], - (Uint64)eventIds[i] ) ); + if ( isGranular() ) { + CFDictionaryRef pathInfoDict = + static_cast( CFArrayGetValueAtIndex( (CFArrayRef)eventPaths, i ) ); + CFStringRef path = static_cast( + CFDictionaryGetValue( pathInfoDict, kFSEventStreamEventExtendedDataPathKey ) ); + CFNumberRef cfInode = static_cast( + CFDictionaryGetValue( pathInfoDict, kFSEventStreamEventExtendedFileIDKey ) ); + + if ( cfInode ) { + unsigned long inode = 0; + CFNumberGetValue( cfInode, kCFNumberLongType, &inode ); + events.push_back( FSEvent( convertCFStringToStdString( path ), (long)eventFlags[i], + (Uint64)eventIds[i], inode ) ); + } + } else { + events.push_back( FSEvent( std::string( ( (char**)eventPaths )[i] ), + (long)eventFlags[i], (Uint64)eventIds[i] ) ); + } } watcher->handleActions( events ); @@ -63,7 +105,7 @@ void FileWatcherFSEvents::FSEventCallback( ConstFSEventStreamRef /*streamRef*/, } FileWatcherFSEvents::FileWatcherFSEvents( FileWatcher* parent ) : - FileWatcherImpl( parent ), mRunLoopRef( NULL ), mLastWatchID( 0 ), mThread( NULL ) { + FileWatcherImpl( parent ), mLastWatchID( 0 ) { mInitOK = true; watch(); @@ -72,10 +114,7 @@ FileWatcherFSEvents::FileWatcherFSEvents( FileWatcher* parent ) : FileWatcherFSEvents::~FileWatcherFSEvents() { mInitOK = false; - if ( mRunLoopRef.load() ) - CFRunLoopStop( mRunLoopRef.load() ); - - efSAFE_DELETE( mThread ); + mWatchCond.notify_all(); WatchMap::iterator iter = mWatches.begin(); @@ -84,18 +123,11 @@ FileWatcherFSEvents::~FileWatcherFSEvents() { efSAFE_DELETE( watch ); } - - mWatches.clear(); } WatchID FileWatcherFSEvents::addWatch( const std::string& directory, FileWatchListener* watcher, - bool recursive ) { - /// Wait to the RunLoopRef to be ready - while ( NULL == mRunLoopRef.load() ) { - System::sleep( 1 ); - } - - std::string dir( directory ); + bool recursive, const std::vector& options ) { + std::string dir( FileSystem::getRealPath( directory ) ); FileInfo fi( dir ); @@ -135,12 +167,18 @@ WatchID FileWatcherFSEvents::addWatch( const std::string& directory, FileWatchLi pWatch->Directory = dir; pWatch->Recursive = recursive; pWatch->FWatcher = this; + pWatch->ModifiedFlags = + getOptionValue( options, Option::MacModifiedFilter, efswFSEventsModified ); + pWatch->SanitizeEvents = getOptionValue( options, Option::MacSanitizeEvents, 0 ) != 0; pWatch->init(); - Lock lock( mWatchesLock ); - mWatches.insert( std::make_pair( mLastWatchID, pWatch ) ); + { + Lock lock( mWatchesLock ); + mWatches.insert( std::make_pair( mLastWatchID, pWatch ) ); + } + mWatchCond.notify_all(); return pWatch->ID; } @@ -174,50 +212,20 @@ void FileWatcherFSEvents::removeWatch( WatchID watchid ) { efSAFE_DELETE( watch ); } -void FileWatcherFSEvents::watch() { - if ( NULL == mThread ) { - mThread = new Thread( &FileWatcherFSEvents::run, this ); - mThread->launch(); - } -} - -void FileWatcherFSEvents::run() { - mRunLoopRef = CFRunLoopGetCurrent(); - - while ( mInitOK ) { - mNeedInitMutex.lock(); - - if ( !mNeedInit.empty() ) { - for ( std::vector::iterator it = mNeedInit.begin(); - it != mNeedInit.end(); ++it ) { - ( *it )->initAsync(); - } - - mNeedInit.clear(); - } - - mNeedInitMutex.unlock(); - - if ( mWatches.empty() ) { - System::sleep( 100 ); - } else { - CFRunLoopRunInMode( kCFRunLoopDefaultMode, 0.5, kCFRunLoopRunTimedOut ); - } - } - - mRunLoopRef = NULL; -} +void FileWatcherFSEvents::watch() {} void FileWatcherFSEvents::handleAction( Watcher* /*watch*/, const std::string& /*filename*/, unsigned long /*action*/, std::string /*oldFilename*/ ) { /// Not used } -std::list FileWatcherFSEvents::directories() { - std::list dirs; +std::vector FileWatcherFSEvents::directories() { + std::vector dirs; Lock lock( mWatchesLock ); + dirs.reserve( mWatches.size() ); + for ( WatchMap::iterator it = mWatches.begin(); it != mWatches.end(); ++it ) { dirs.push_back( std::string( it->second->Directory ) ); } diff --git a/src/3rdParty/efsw/FileWatcherFSEvents.hpp b/src/3rdParty/efsw/FileWatcherFSEvents.hpp old mode 100755 new mode 100644 index 5279847..daa538c --- a/src/3rdParty/efsw/FileWatcherFSEvents.hpp +++ b/src/3rdParty/efsw/FileWatcherFSEvents.hpp @@ -7,33 +7,15 @@ #include #include +#include #include -#include #include #include +#include +#include namespace efsw { -/* OSX < 10.7 has no file events */ -/* So i declare the events constants */ -enum FSEventEvents { - efswFSEventStreamCreateFlagFileEvents = 0x00000010, - efswFSEventStreamEventFlagItemCreated = 0x00000100, - efswFSEventStreamEventFlagItemRemoved = 0x00000200, - efswFSEventStreamEventFlagItemInodeMetaMod = 0x00000400, - efswFSEventStreamEventFlagItemRenamed = 0x00000800, - efswFSEventStreamEventFlagItemModified = 0x00001000, - efswFSEventStreamEventFlagItemFinderInfoMod = 0x00002000, - efswFSEventStreamEventFlagItemChangeOwner = 0x00004000, - efswFSEventStreamEventFlagItemXattrMod = 0x00008000, - efswFSEventStreamEventFlagItemIsFile = 0x00010000, - efswFSEventStreamEventFlagItemIsDir = 0x00020000, - efswFSEventStreamEventFlagItemIsSymlink = 0x00040000, - efswFSEventsModified = efswFSEventStreamEventFlagItemFinderInfoMod | - efswFSEventStreamEventFlagItemModified | - efswFSEventStreamEventFlagItemInodeMetaMod -}; - /// Implementation for Win32 based on ReadDirectoryChangesW. /// @class FileWatcherFSEvents class FileWatcherFSEvents : public FileWatcherImpl { @@ -52,48 +34,43 @@ class FileWatcherFSEvents : public FileWatcherImpl { /// Add a directory watch /// On error returns WatchID with Error type. - WatchID addWatch( const std::string& directory, FileWatchListener* watcher, bool recursive ); + WatchID addWatch( const std::string& directory, FileWatchListener* watcher, bool recursive, + const std::vector &options ) override; /// Remove a directory watch. This is a brute force lazy search O(nlogn). - void removeWatch( const std::string& directory ); + void removeWatch( const std::string& directory ) override; /// Remove a directory watch. This is a map lookup O(logn). - void removeWatch( WatchID watchid ); + void removeWatch( WatchID watchid ) override; /// Updates the watcher. Must be called often. - void watch(); + void watch() override; /// Handles the action void handleAction( Watcher* watch, const std::string& filename, unsigned long action, - std::string oldFilename = "" ); + std::string oldFilename = "" ) override; /// @return Returns a list of the directories that are being watched - std::list directories(); + std::vector directories() override; protected: static void FSEventCallback( ConstFSEventStreamRef streamRef, void* userData, size_t numEvents, void* eventPaths, const FSEventStreamEventFlags eventFlags[], const FSEventStreamEventId eventIds[] ); - Atomic mRunLoopRef; - /// Vector of WatcherWin32 pointers WatchMap mWatches; /// The last watchid WatchID mLastWatchID; - Thread* mThread; - Mutex mWatchesLock; - bool pathInWatches( const std::string& path ); + bool pathInWatches( const std::string& path ) override; - std::vector mNeedInit; - Mutex mNeedInitMutex; + std::mutex mWatchesMutex; + std::condition_variable mWatchCond; - private: - void run(); }; } // namespace efsw diff --git a/src/3rdParty/efsw/FileWatcherGeneric.cpp b/src/3rdParty/efsw/FileWatcherGeneric.cpp old mode 100755 new mode 100644 index 074cff1..3f3c52e --- a/src/3rdParty/efsw/FileWatcherGeneric.cpp +++ b/src/3rdParty/efsw/FileWatcherGeneric.cpp @@ -25,7 +25,7 @@ FileWatcherGeneric::~FileWatcherGeneric() { } WatchID FileWatcherGeneric::addWatch( const std::string& directory, FileWatchListener* watcher, - bool recursive ) { + bool recursive, const std::vector& options ) { std::string dir( directory ); FileSystem::dirAddSlashAtEnd( dir ); @@ -127,11 +127,13 @@ void FileWatcherGeneric::handleAction( Watcher*, const std::string&, unsigned lo /// Not used } -std::list FileWatcherGeneric::directories() { - std::list dirs; +std::vector FileWatcherGeneric::directories() { + std::vector dirs; Lock lock( mWatchesLock ); + dirs.reserve( mWatches.size() ); + WatchList::iterator it = mWatches.begin(); for ( ; it != mWatches.end(); ++it ) { diff --git a/src/3rdParty/efsw/FileWatcherGeneric.hpp b/src/3rdParty/efsw/FileWatcherGeneric.hpp old mode 100755 new mode 100644 index 4cb0b67..47f7e04 --- a/src/3rdParty/efsw/FileWatcherGeneric.hpp +++ b/src/3rdParty/efsw/FileWatcherGeneric.hpp @@ -4,7 +4,7 @@ #include #include #include -#include +#include namespace efsw { @@ -12,7 +12,7 @@ namespace efsw { /// @class FileWatcherGeneric class FileWatcherGeneric : public FileWatcherImpl { public: - typedef std::list WatchList; + typedef std::vector WatchList; FileWatcherGeneric( FileWatcher* parent ); @@ -20,23 +20,24 @@ class FileWatcherGeneric : public FileWatcherImpl { /// Add a directory watch /// On error returns WatchID with Error type. - WatchID addWatch( const std::string& directory, FileWatchListener* watcher, bool recursive ); + WatchID addWatch( const std::string& directory, FileWatchListener* watcher, bool recursive, + const std::vector &options ) override; /// Remove a directory watch. This is a brute force lazy search O(nlogn). - void removeWatch( const std::string& directory ); + void removeWatch( const std::string& directory ) override; /// Remove a directory watch. This is a map lookup O(logn). - void removeWatch( WatchID watchid ); + void removeWatch( WatchID watchid ) override; /// Updates the watcher. Must be called often. - void watch(); + void watch() override; /// Handles the action void handleAction( Watcher* watch, const std::string& filename, unsigned long action, - std::string oldFilename = "" ); + std::string oldFilename = "" ) override; /// @return Returns a list of the directories that are being watched - std::list directories(); + std::vector directories() override; protected: Thread* mThread; @@ -49,7 +50,7 @@ class FileWatcherGeneric : public FileWatcherImpl { Mutex mWatchesLock; - bool pathInWatches( const std::string& path ); + bool pathInWatches( const std::string& path ) override; private: void run(); diff --git a/src/3rdParty/efsw/FileWatcherImpl.cpp b/src/3rdParty/efsw/FileWatcherImpl.cpp old mode 100755 new mode 100644 index f6b86a5..bf69a45 --- a/src/3rdParty/efsw/FileWatcherImpl.cpp +++ b/src/3rdParty/efsw/FileWatcherImpl.cpp @@ -1,23 +1,34 @@ -#include -#include -#include - -namespace efsw { - -FileWatcherImpl::FileWatcherImpl( FileWatcher* parent ) : - mFileWatcher( parent ), mInitOK( false ), mIsGeneric( false ) { - System::maxFD(); -} - -FileWatcherImpl::~FileWatcherImpl() {} - -bool FileWatcherImpl::initOK() { - return static_cast( mInitOK ); -} - -bool FileWatcherImpl::linkAllowed( const std::string& curPath, const std::string& link ) { - return ( mFileWatcher->followSymlinks() && mFileWatcher->allowOutOfScopeLinks() ) || - -1 != String::strStartsWith( curPath, link ); -} - -} // namespace efsw +#include +#include +#include + +namespace efsw { + +FileWatcherImpl::FileWatcherImpl( FileWatcher* parent ) : + mFileWatcher( parent ), mInitOK( false ), mIsGeneric( false ) { + System::maxFD(); +} + +FileWatcherImpl::~FileWatcherImpl() {} + +bool FileWatcherImpl::initOK() { + return static_cast( mInitOK ); +} + +bool FileWatcherImpl::linkAllowed( const std::string& curPath, const std::string& link ) { + return ( mFileWatcher->followSymlinks() && mFileWatcher->allowOutOfScopeLinks() ) || + -1 != String::strStartsWith( curPath, link ); +} + +int FileWatcherImpl::getOptionValue( const std::vector& options, Option option, + int defaultValue ) { + for ( size_t i = 0; i < options.size(); i++ ) { + if ( options[i].mOption == option ) { + return options[i].mValue; + } + } + + return defaultValue; +} + +} // namespace efsw diff --git a/src/3rdParty/efsw/FileWatcherImpl.hpp b/src/3rdParty/efsw/FileWatcherImpl.hpp old mode 100755 new mode 100644 index ea1beb8..a6ec472 --- a/src/3rdParty/efsw/FileWatcherImpl.hpp +++ b/src/3rdParty/efsw/FileWatcherImpl.hpp @@ -1,57 +1,64 @@ -#ifndef EFSW_FILEWATCHERIMPL_HPP -#define EFSW_FILEWATCHERIMPL_HPP - -#include -#include -#include -#include -#include -#include - -namespace efsw { - -class FileWatcherImpl { - public: - FileWatcherImpl( FileWatcher* parent ); - - virtual ~FileWatcherImpl(); - - /// Add a directory watch - /// On error returns WatchID with Error type. - virtual WatchID addWatch( const std::string& directory, FileWatchListener* watcher, - bool recursive ) = 0; - - /// Remove a directory watch. This is a brute force lazy search O(nlogn). - virtual void removeWatch( const std::string& directory ) = 0; - - /// Remove a directory watch. This is a map lookup O(logn). - virtual void removeWatch( WatchID watchid ) = 0; - - /// Updates the watcher. Must be called often. - virtual void watch() = 0; - - /// Handles the action - virtual void handleAction( Watcher* watch, const std::string& filename, unsigned long action, - std::string oldFilename = "" ) = 0; - - /// @return Returns a list of the directories that are being watched - virtual std::list directories() = 0; - - /// @return true if the backend init successfully - virtual bool initOK(); - - /// @return If the link is allowed according to the current path and the state of out scope - /// links - virtual bool linkAllowed( const std::string& curPath, const std::string& link ); - - /// Search if a directory already exists in the watches - virtual bool pathInWatches( const std::string& path ) = 0; - - FileWatcher* mFileWatcher; - Atomic mInitOK; - bool mIsGeneric; -}; - -} // namespace efsw - -#endif +#ifndef EFSW_FILEWATCHERIMPL_HPP +#define EFSW_FILEWATCHERIMPL_HPP + +#include +#include +#include +#include +#include +#include + +namespace efsw { + +class FileWatcherImpl { + public: + FileWatcherImpl( FileWatcher* parent ); + + virtual ~FileWatcherImpl(); + + /// Add a directory watch + /// On error returns WatchID with Error type. + virtual WatchID addWatch( const std::string& directory, FileWatchListener* watcher, + bool recursive, const std::vector& options = {} ) = 0; + + /// Remove a directory watch. This is a brute force lazy search O(nlogn). + virtual void removeWatch( const std::string& directory ) = 0; + + /// Remove a directory watch. This is a map lookup O(logn). + virtual void removeWatch( WatchID watchid ) = 0; + + /// Updates the watcher. Must be called often. + virtual void watch() = 0; + + /// Handles the action + virtual void handleAction( Watcher* watch, const std::string& filename, unsigned long action, + std::string oldFilename = "" ) = 0; + + /// @return Returns a list of the directories that are being watched + virtual std::vector directories() = 0; + + /// @return true if the backend init successfully + virtual bool initOK(); + + /// @return If the link is allowed according to the current path and the state of out scope + /// links + virtual bool linkAllowed( const std::string& curPath, const std::string& link ); + + /// Search if a directory already exists in the watches + virtual bool pathInWatches( const std::string& path ) = 0; + + protected: + friend class FileWatcher; + friend class DirWatcherGeneric; + + FileWatcher* mFileWatcher; + Atomic mInitOK; + bool mIsGeneric; + + int getOptionValue( const std::vector& options, Option option, + int defaultValue ); +}; + +} // namespace efsw + +#endif diff --git a/src/3rdParty/efsw/FileWatcherInotify.cpp b/src/3rdParty/efsw/FileWatcherInotify.cpp old mode 100755 new mode 100644 index e0da76b..1ec3d48 --- a/src/3rdParty/efsw/FileWatcherInotify.cpp +++ b/src/3rdParty/efsw/FileWatcherInotify.cpp @@ -1,3 +1,4 @@ +#include #include #if EFSW_PLATFORM == EFSW_PLATFORM_INOTIFY @@ -26,7 +27,7 @@ namespace efsw { FileWatcherInotify::FileWatcherInotify( FileWatcher* parent ) : - FileWatcherImpl( parent ), mFD( -1 ), mThread( NULL ) { + FileWatcherImpl( parent ), mFD( -1 ), mThread( NULL ), mIsTakingAction( false ) { mFD = inotify_init(); if ( mFD < 0 ) { @@ -38,13 +39,20 @@ FileWatcherInotify::FileWatcherInotify( FileWatcher* parent ) : FileWatcherInotify::~FileWatcherInotify() { mInitOK = false; - + // There is deadlock when release FileWatcherInotify instance since its handAction + // function is still running and hangs in requiring lock without init lock captured. + while ( mIsTakingAction ) { + // It'd use condition-wait instead of sleep. Actually efsw has no such + // implementation so we just skip and sleep while for that to avoid deadlock. + usleep( 1000 ); + }; Lock initLock( mInitLock ); efSAFE_DELETE( mThread ); Lock l( mWatchesLock ); Lock l2( mRealWatchesLock ); + WatchMap::iterator iter = mWatches.begin(); WatchMap::iterator end = mWatches.end(); @@ -61,15 +69,17 @@ FileWatcherInotify::~FileWatcherInotify() { } WatchID FileWatcherInotify::addWatch( const std::string& directory, FileWatchListener* watcher, - bool recursive ) { + bool recursive, const std::vector& options ) { if ( !mInitOK ) return Errors::Log::createLastError( Errors::Unspecified, directory ); Lock initLock( mInitLock ); - return addWatch( directory, watcher, recursive, NULL ); + bool syntheticEvents = getOptionValue( options, Options::LinuxProduceSyntheticEvents, 0 ) != 0; + return addWatch( directory, watcher, recursive, syntheticEvents, NULL ); } WatchID FileWatcherInotify::addWatch( const std::string& directory, FileWatchListener* watcher, - bool recursive, WatcherInotify* parent ) { + bool recursive, bool syntheticEvents, + WatcherInotify* parent, bool fromInternalEvent ) { std::string dir( directory ); FileSystem::dirAddSlashAtEnd( dir ); @@ -133,6 +143,7 @@ WatchID FileWatcherInotify::addWatch( const std::string& directory, FileWatchLis { Lock lock( mWatchesLock ); mWatches.insert( std::make_pair( wd, pWatch ) ); + mWatchesRef[pWatch->Directory] = wd; } if ( NULL == pWatch->Parent ) { @@ -151,7 +162,17 @@ WatchID FileWatcherInotify::addWatch( const std::string& directory, FileWatchLis const FileInfo& cfi = it->second; if ( cfi.isDirectory() && cfi.isReadable() ) { - addWatch( cfi.Filepath, watcher, recursive, pWatch ); + addWatch( cfi.Filepath, watcher, recursive, syntheticEvents, pWatch ); + } + } + + if ( fromInternalEvent && parent != NULL && syntheticEvents ) { + for ( const auto& file : files ) { + if ( file.second.isRegularFile() ) { + pWatch->Listener->handleFileAction( + pWatch->ID, pWatch->Directory, + FileSystem::fileNameFromPath( file.second.Filepath ), Actions::Add ); + } } } } @@ -161,6 +182,8 @@ WatchID FileWatcherInotify::addWatch( const std::string& directory, FileWatchLis void FileWatcherInotify::removeWatchLocked( WatchID watchid ) { WatchMap::iterator iter = mWatches.find( watchid ); + if ( iter == mWatches.end() ) + return; WatcherInotify* watch = iter->second; @@ -173,22 +196,21 @@ void FileWatcherInotify::removeWatchLocked( WatchID watchid ) { } } - if ( watch->Recursive ) { + if ( watch->Recursive && NULL == watch->Parent ) { WatchMap::iterator it = mWatches.begin(); - std::list eraseWatches; + std::vector eraseWatches; - for ( ; it != mWatches.end(); ++it ) { - if ( it->second != watch && it->second->inParentTree( watch ) ) { + for ( ; it != mWatches.end(); ++it ) + if ( it->second != watch && it->second->inParentTree( watch ) ) eraseWatches.push_back( it->second->InotifyID ); - } - } - for ( std::list::iterator eit = eraseWatches.begin(); eit != eraseWatches.end(); + for ( std::vector::iterator eit = eraseWatches.begin(); eit != eraseWatches.end(); ++eit ) { removeWatch( *eit ); } } + mWatchesRef.erase( watch->Directory ); mWatches.erase( iter ); if ( NULL == watch->Parent ) { @@ -217,52 +239,11 @@ void FileWatcherInotify::removeWatch( const std::string& directory ) { Lock lock( mWatchesLock ); Lock l( mRealWatchesLock ); - WatchMap::iterator iter = mWatches.begin(); - - for ( ; iter != mWatches.end(); ++iter ) { - if ( directory == iter->second->Directory ) { - WatcherInotify* watch = iter->second; - - if ( watch->Recursive ) { - WatchMap::iterator it = mWatches.begin(); - std::list eraseWatches; - - for ( ; it != mWatches.end(); ++it ) { - if ( it->second->inParentTree( watch ) ) { - eraseWatches.push_back( it->second->InotifyID ); - } - } - - for ( std::list::iterator eit = eraseWatches.begin(); - eit != eraseWatches.end(); ++eit ) { - removeWatchLocked( *eit ); - } - } - - mWatches.erase( iter ); - - if ( NULL == watch->Parent ) { - WatchMap::iterator eraseit = mRealWatches.find( watch->InotifyID ); - - if ( eraseit != mRealWatches.end() ) { - mRealWatches.erase( eraseit ); - } - } - - int err = inotify_rm_watch( mFD, watch->InotifyID ); - - if ( err < 0 ) { - efDEBUG( "Error removing watch %d: %s\n", watch->InotifyID, strerror( errno ) ); - } else { - efDEBUG( "Removed watch %s with id: %d\n", watch->Directory.c_str(), - watch->InotifyID ); - } - - efSAFE_DELETE( watch ); + std::unordered_map::iterator ref = mWatchesRef.find( directory ); + if ( ref == mWatchesRef.end() ) + return; - break; - } - } + removeWatchLocked( ref->second ); } void FileWatcherInotify::removeWatch( WatchID watchid ) { @@ -270,13 +251,6 @@ void FileWatcherInotify::removeWatch( WatchID watchid ) { return; Lock initLock( mInitLock ); Lock lock( mWatchesLock ); - - WatchMap::iterator iter = mWatches.find( watchid ); - - if ( iter == mWatches.end() ) { - return; - } - removeWatchLocked( watchid ); } @@ -295,10 +269,8 @@ Watcher* FileWatcherInotify::watcherContainsDirectory( std::string dir ) { for ( WatchMap::iterator it = mWatches.begin(); it != mWatches.end(); ++it ) { Watcher* watcher = it->second; - - if ( watcher->Directory == watcherPath ) { + if ( watcher->Directory == watcherPath ) return watcher; - } } return NULL; @@ -422,7 +394,7 @@ void FileWatcherInotify::run() { const std::string& oldFileName = ( *it ).second; /// Check if the file move was a folder already being watched - std::list eraseWatches; + std::vector eraseWatches; { Lock lock( mWatchesLock ); @@ -439,12 +411,15 @@ void FileWatcherInotify::run() { } /// Remove invalid watches - eraseWatches.sort(); + std::stable_sort( eraseWatches.begin(), eraseWatches.end(), + []( const Watcher* left, const Watcher* right ) { + return left->Directory < right->Directory; + } ); if ( eraseWatches.empty() ) { handleAction( watch, oldFileName, IN_DELETE ); } else { - for ( std::list::reverse_iterator eit = eraseWatches.rbegin(); + for ( std::vector::reverse_iterator eit = eraseWatches.rbegin(); eit != eraseWatches.rend(); ++eit ) { Watcher* rmWatch = *eit; @@ -485,8 +460,9 @@ void FileWatcherInotify::checkForNewWatcher( Watcher* watch, std::string fpath ) } if ( !found ) { - addWatch( fpath, watch->Listener, watch->Recursive, - static_cast( watch ) ); + WatcherInotify* iWatch = static_cast( watch ); + addWatch( fpath, watch->Listener, watch->Recursive, iWatch->syntheticEvents, + static_cast( watch ), true ); } } } @@ -496,7 +472,7 @@ void FileWatcherInotify::handleAction( Watcher* watch, const std::string& filena if ( !watch || !watch->Listener || !mInitOK ) { return; } - + mIsTakingAction = true; Lock initLock( mInitLock ); std::string fpath( watch->Directory + filename ); @@ -563,18 +539,20 @@ void FileWatcherInotify::handleAction( Watcher* watch, const std::string& filena } } } + mIsTakingAction = false; } -std::list FileWatcherInotify::directories() { - std::list dirs; +std::vector FileWatcherInotify::directories() { + std::vector dirs; Lock l( mRealWatchesLock ); + dirs.reserve( mRealWatches.size() ); + WatchMap::iterator it = mRealWatches.begin(); - for ( ; it != mRealWatches.end(); ++it ) { + for ( ; it != mRealWatches.end(); ++it ) dirs.push_back( it->second->Directory ); - } return dirs; } @@ -585,11 +563,9 @@ bool FileWatcherInotify::pathInWatches( const std::string& path ) { /// Search in the real watches, since it must allow adding a watch already watched as a subdir WatchMap::iterator it = mRealWatches.begin(); - for ( ; it != mRealWatches.end(); ++it ) { - if ( it->second->Directory == path ) { + for ( ; it != mRealWatches.end(); ++it ) + if ( it->second->Directory == path ) return true; - } - } return false; } diff --git a/src/3rdParty/efsw/FileWatcherInotify.hpp b/src/3rdParty/efsw/FileWatcherInotify.hpp old mode 100755 new mode 100644 index dc922ac..26d2c0b --- a/src/3rdParty/efsw/FileWatcherInotify.hpp +++ b/src/3rdParty/efsw/FileWatcherInotify.hpp @@ -7,6 +7,7 @@ #include #include +#include #include namespace efsw { @@ -24,23 +25,24 @@ class FileWatcherInotify : public FileWatcherImpl { /// Add a directory watch /// On error returns WatchID with Error type. - WatchID addWatch( const std::string& directory, FileWatchListener* watcher, bool recursive ); + WatchID addWatch( const std::string& directory, FileWatchListener* watcher, bool recursive, + const std::vector& options ) override; /// Remove a directory watch. This is a brute force lazy search O(nlogn). - void removeWatch( const std::string& directory ); + void removeWatch( const std::string& directory ) override; /// Remove a directory watch. This is a map lookup O(logn). - void removeWatch( WatchID watchid ); + void removeWatch( WatchID watchid ) override; /// Updates the watcher. Must be called often. - void watch(); + void watch() override; /// Handles the action void handleAction( Watcher* watch, const std::string& filename, unsigned long action, - std::string oldFilename = "" ); + std::string oldFilename = "" ) override; /// @return Returns a list of the directories that are being watched - std::list directories(); + std::vector directories() override; protected: /// Map of WatchID to WatchStruct pointers @@ -49,6 +51,8 @@ class FileWatcherInotify : public FileWatcherImpl { /// User added watches WatchMap mRealWatches; + std::unordered_map mWatchesRef; + /// inotify file descriptor int mFD; @@ -57,12 +61,14 @@ class FileWatcherInotify : public FileWatcherImpl { Mutex mWatchesLock; Mutex mRealWatchesLock; Mutex mInitLock; + bool mIsTakingAction; std::vector> mMovedOutsideWatches; WatchID addWatch( const std::string& directory, FileWatchListener* watcher, bool recursive, - WatcherInotify* parent = NULL ); + bool syntheticEvents, WatcherInotify* parent = NULL, + bool fromInternalEvent = false ); - bool pathInWatches( const std::string& path ); + bool pathInWatches( const std::string& path ) override; private: void run(); diff --git a/src/3rdParty/efsw/FileWatcherKqueue.cpp b/src/3rdParty/efsw/FileWatcherKqueue.cpp old mode 100755 new mode 100644 index 38ffad9..ad03036 --- a/src/3rdParty/efsw/FileWatcherKqueue.cpp +++ b/src/3rdParty/efsw/FileWatcherKqueue.cpp @@ -45,7 +45,7 @@ FileWatcherKqueue::~FileWatcherKqueue() { } WatchID FileWatcherKqueue::addWatch( const std::string& directory, FileWatchListener* watcher, - bool recursive ) { + bool recursive, const std::vector& /*options*/ ) { static bool s_ug = false; std::string dir( directory ); @@ -184,11 +184,13 @@ void FileWatcherKqueue::run() { void FileWatcherKqueue::handleAction( Watcher* /*watch*/, const std::string& /*filename*/, unsigned long /*action*/, std::string /*oldFilename*/ ) {} -std::list FileWatcherKqueue::directories() { - std::list dirs; +std::vector FileWatcherKqueue::directories() { + std::vector dirs; Lock lock( mWatchesLock ); + dirs.reserve( mWatches.size() ); + WatchMap::iterator it = mWatches.begin(); for ( ; it != mWatches.end(); ++it ) { diff --git a/src/3rdParty/efsw/FileWatcherKqueue.hpp b/src/3rdParty/efsw/FileWatcherKqueue.hpp old mode 100755 new mode 100644 index 1bf3755..ff5327b --- a/src/3rdParty/efsw/FileWatcherKqueue.hpp +++ b/src/3rdParty/efsw/FileWatcherKqueue.hpp @@ -21,23 +21,24 @@ class FileWatcherKqueue : public FileWatcherImpl { /// Add a directory watch /// On error returns WatchID with Error type. - WatchID addWatch( const std::string& directory, FileWatchListener* watcher, bool recursive ); + WatchID addWatch( const std::string& directory, FileWatchListener* watcher, bool recursive, + const std::vector &options ) override; /// Remove a directory watch. This is a brute force lazy search O(nlogn). - void removeWatch( const std::string& directory ); + void removeWatch( const std::string& directory ) override; /// Remove a directory watch. This is a map lookup O(logn). - void removeWatch( WatchID watchid ); + void removeWatch( WatchID watchid ) override; /// Updates the watcher. Must be called often. - void watch(); + void watch() override; /// Handles the action void handleAction( Watcher* watch, const std::string& filename, unsigned long action, - std::string oldFilename = "" ); + std::string oldFilename = "" ) override; /// @return Returns a list of the directories that are being watched - std::list directories(); + std::vector directories() override; protected: /// Map of WatchID to WatchStruct pointers @@ -53,7 +54,7 @@ class FileWatcherKqueue : public FileWatcherImpl { Mutex mWatchesLock; - std::list mRemoveList; + std::vector mRemoveList; long mFileDescriptorCount; @@ -61,7 +62,7 @@ class FileWatcherKqueue : public FileWatcherImpl { bool isAddingWatcher() const; - bool pathInWatches( const std::string& path ); + bool pathInWatches( const std::string& path ) override; void addFD(); diff --git a/src/3rdParty/efsw/FileWatcherWin32.cpp b/src/3rdParty/efsw/FileWatcherWin32.cpp old mode 100755 new mode 100644 index 963dc98..19b71d7 --- a/src/3rdParty/efsw/FileWatcherWin32.cpp +++ b/src/3rdParty/efsw/FileWatcherWin32.cpp @@ -1,257 +1,267 @@ -#include -#include -#include -#include -#include - -#if EFSW_PLATFORM == EFSW_PLATFORM_WIN32 - -namespace efsw { - -FileWatcherWin32::FileWatcherWin32( FileWatcher* parent ) : - FileWatcherImpl( parent ), mLastWatchID( 0 ), mThread( NULL ) { - mIOCP = CreateIoCompletionPort( INVALID_HANDLE_VALUE, NULL, 0, 1 ); - if ( mIOCP && mIOCP != INVALID_HANDLE_VALUE ) - mInitOK = true; -} - -FileWatcherWin32::~FileWatcherWin32() { - mInitOK = false; - - if ( mIOCP && mIOCP != INVALID_HANDLE_VALUE ) { - PostQueuedCompletionStatus( mIOCP, 0, reinterpret_cast( this ), NULL ); - } - - efSAFE_DELETE( mThread ); - - removeAllWatches(); - - CloseHandle( mIOCP ); -} - -WatchID FileWatcherWin32::addWatch( const std::string& directory, FileWatchListener* watcher, - bool recursive ) { - std::string dir( directory ); - - FileInfo fi( dir ); - - if ( !fi.isDirectory() ) { - return Errors::Log::createLastError( Errors::FileNotFound, dir ); - } else if ( !fi.isReadable() ) { - return Errors::Log::createLastError( Errors::FileNotReadable, dir ); - } - - FileSystem::dirAddSlashAtEnd( dir ); - - Lock lock( mWatchesLock ); - - if ( pathInWatches( dir ) ) { - return Errors::Log::createLastError( Errors::FileRepeated, dir ); - } - - WatchID watchid = ++mLastWatchID; - - WatcherStructWin32* watch = CreateWatch( - String::fromUtf8( dir ).toWideString().c_str(), recursive, - FILE_NOTIFY_CHANGE_CREATION | FILE_NOTIFY_CHANGE_LAST_WRITE | FILE_NOTIFY_CHANGE_FILE_NAME | - FILE_NOTIFY_CHANGE_DIR_NAME | FILE_NOTIFY_CHANGE_SIZE, - mIOCP ); - - if ( NULL == watch ) { - return Errors::Log::createLastError( Errors::FileNotFound, dir ); - } - - // Add the handle to the handles vector - watch->Watch->ID = watchid; - watch->Watch->Watch = this; - watch->Watch->Listener = watcher; - watch->Watch->DirName = new char[dir.length() + 1]; - strcpy( watch->Watch->DirName, dir.c_str() ); - - mWatches.insert( watch ); - - return watchid; -} - -void FileWatcherWin32::removeWatch( const std::string& directory ) { - Lock lock( mWatchesLock ); - - Watches::iterator iter = mWatches.begin(); - - for ( ; iter != mWatches.end(); ++iter ) { - if ( directory == ( *iter )->Watch->DirName ) { - removeWatch( *iter ); - break; - } - } -} - -void FileWatcherWin32::removeWatch( WatchID watchid ) { - Lock lock( mWatchesLock ); - - Watches::iterator iter = mWatches.begin(); - - for ( ; iter != mWatches.end(); ++iter ) { - // Find the watch ID - if ( ( *iter )->Watch->ID == watchid ) { - removeWatch( *iter ); - return; - } - } -} - -void FileWatcherWin32::removeWatch( WatcherStructWin32* watch ) { - Lock lock( mWatchesLock ); - - DestroyWatch( watch ); - mWatches.erase( watch ); -} - -void FileWatcherWin32::watch() { - if ( NULL == mThread ) { - mThread = new Thread( &FileWatcherWin32::run, this ); - mThread->launch(); - } -} - -void FileWatcherWin32::removeAllWatches() { - Lock lock( mWatchesLock ); - - Watches::iterator iter = mWatches.begin(); - - for ( ; iter != mWatches.end(); ++iter ) { - DestroyWatch( ( *iter ) ); - } - - mWatches.clear(); -} - -void FileWatcherWin32::run() { - do { - if ( mInitOK && !mWatches.empty() ) { - DWORD numOfBytes = 0; - OVERLAPPED* ov = NULL; - ULONG_PTR compKey = 0; - BOOL res = FALSE; - - while ( ( res = GetQueuedCompletionStatus( mIOCP, &numOfBytes, &compKey, &ov, - INFINITE ) ) != FALSE ) { - if ( compKey != 0 && compKey == reinterpret_cast( this ) ) { - break; - } else { - Lock lock( mWatchesLock ); - WatchCallback( numOfBytes, ov ); - } - } - } else { - System::sleep( 10 ); - } - } while ( mInitOK ); - - removeAllWatches(); -} - -void FileWatcherWin32::handleAction( Watcher* watch, const std::string& filename, - unsigned long action, std::string /*oldFilename*/ ) { - Action fwAction; - - switch ( action ) { - case FILE_ACTION_RENAMED_OLD_NAME: - watch->OldFileName = filename; - return; - case FILE_ACTION_ADDED: - fwAction = Actions::Add; - break; - case FILE_ACTION_RENAMED_NEW_NAME: { - fwAction = Actions::Moved; - - std::string fpath( watch->Directory + filename ); - - // Update the directory path - if ( watch->Recursive && FileSystem::isDirectory( fpath ) ) { - // Update the new directory path - std::string opath( watch->Directory + watch->OldFileName ); - FileSystem::dirAddSlashAtEnd( opath ); - FileSystem::dirAddSlashAtEnd( fpath ); - - for ( Watches::iterator it = mWatches.begin(); it != mWatches.end(); ++it ) { - if ( ( *it )->Watch->Directory == opath ) { - ( *it )->Watch->Directory = fpath; - - break; - } - } - } - - std::string folderPath( static_cast( watch )->DirName ); - std::string realFilename = filename; - std::size_t sepPos = filename.find_last_of( "/\\" ); - std::string oldFolderPath = - static_cast( watch )->DirName + - watch->OldFileName.substr( 0, watch->OldFileName.find_last_of( "/\\" ) ); - - if ( sepPos != std::string::npos ) { - folderPath += filename.substr( 0, sepPos ); - realFilename = filename.substr( sepPos + 1 ); - } - - if ( folderPath == oldFolderPath ) { - watch->Listener->handleFileAction( - watch->ID, folderPath, realFilename, fwAction, - FileSystem::fileNameFromPath( watch->OldFileName ) ); - } else { - watch->Listener->handleFileAction( watch->ID, - static_cast( watch )->DirName, - filename, fwAction, watch->OldFileName ); - } - return; - } - case FILE_ACTION_REMOVED: - fwAction = Actions::Delete; - break; - case FILE_ACTION_MODIFIED: - fwAction = Actions::Modified; - break; - default: - return; - }; - - std::string folderPath( static_cast( watch )->DirName ); - std::string realFilename = filename; - std::size_t sepPos = filename.find_last_of( "/\\" ); - - if ( sepPos != std::string::npos ) { - folderPath += filename.substr( 0, sepPos ); - realFilename = filename.substr( sepPos + 1 ); - } - - watch->Listener->handleFileAction( watch->ID, folderPath, realFilename, fwAction ); -} - -std::list FileWatcherWin32::directories() { - std::list dirs; - - Lock lock( mWatchesLock ); - - for ( Watches::iterator it = mWatches.begin(); it != mWatches.end(); ++it ) { - dirs.push_back( std::string( ( *it )->Watch->DirName ) ); - } - - return dirs; -} - -bool FileWatcherWin32::pathInWatches( const std::string& path ) { - Lock lock( mWatchesLock ); - - for ( Watches::iterator it = mWatches.begin(); it != mWatches.end(); ++it ) { - if ( ( *it )->Watch->DirName == path ) { - return true; - } - } - - return false; -} - -} // namespace efsw - -#endif +#include +#include +#include +#include +#include + +#if EFSW_PLATFORM == EFSW_PLATFORM_WIN32 + +namespace efsw { + +FileWatcherWin32::FileWatcherWin32( FileWatcher* parent ) : + FileWatcherImpl( parent ), mLastWatchID( 0 ), mThread( NULL ) { + mIOCP = CreateIoCompletionPort( INVALID_HANDLE_VALUE, NULL, 0, 1 ); + if ( mIOCP && mIOCP != INVALID_HANDLE_VALUE ) + mInitOK = true; +} + +FileWatcherWin32::~FileWatcherWin32() { + mInitOK = false; + + if ( mIOCP && mIOCP != INVALID_HANDLE_VALUE ) { + PostQueuedCompletionStatus( mIOCP, 0, reinterpret_cast( this ), NULL ); + } + + efSAFE_DELETE( mThread ); + + removeAllWatches(); + + if ( mIOCP ) + CloseHandle( mIOCP ); +} + +WatchID FileWatcherWin32::addWatch( const std::string& directory, FileWatchListener* watcher, + bool recursive, const std::vector &options ) { + std::string dir( directory ); + + FileInfo fi( dir ); + + if ( !fi.isDirectory() ) { + return Errors::Log::createLastError( Errors::FileNotFound, dir ); + } else if ( !fi.isReadable() ) { + return Errors::Log::createLastError( Errors::FileNotReadable, dir ); + } + + FileSystem::dirAddSlashAtEnd( dir ); + + Lock lock( mWatchesLock ); + + if ( pathInWatches( dir ) ) { + return Errors::Log::createLastError( Errors::FileRepeated, dir ); + } + + WatchID watchid = ++mLastWatchID; + + DWORD bufferSize = static_cast( getOptionValue(options, Option::WinBufferSize, 63 * 1024) ); + DWORD notifyFilter = static_cast( getOptionValue(options, Option::WinNotifyFilter, + FILE_NOTIFY_CHANGE_CREATION | FILE_NOTIFY_CHANGE_LAST_WRITE | + FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_DIR_NAME | + FILE_NOTIFY_CHANGE_SIZE) ); + + WatcherStructWin32* watch = CreateWatch( String::fromUtf8( dir ).toWideString().c_str(), + recursive, bufferSize, notifyFilter, mIOCP ); + + if ( NULL == watch ) { + return Errors::Log::createLastError( Errors::FileNotFound, dir ); + } + + // Add the handle to the handles vector + watch->Watch->ID = watchid; + watch->Watch->Watch = this; + watch->Watch->Listener = watcher; + watch->Watch->DirName = new char[dir.length() + 1]; + strcpy( watch->Watch->DirName, dir.c_str() ); + + mWatches.insert( watch ); + + return watchid; +} + +void FileWatcherWin32::removeWatch( const std::string& directory ) { + Lock lock( mWatchesLock ); + + Watches::iterator iter = mWatches.begin(); + + for ( ; iter != mWatches.end(); ++iter ) { + if ( directory == ( *iter )->Watch->DirName ) { + removeWatch( *iter ); + break; + } + } +} + +void FileWatcherWin32::removeWatch( WatchID watchid ) { + Lock lock( mWatchesLock ); + + Watches::iterator iter = mWatches.begin(); + + for ( ; iter != mWatches.end(); ++iter ) { + // Find the watch ID + if ( ( *iter )->Watch->ID == watchid ) { + removeWatch( *iter ); + return; + } + } +} + +void FileWatcherWin32::removeWatch( WatcherStructWin32* watch ) { + Lock lock( mWatchesLock ); + + DestroyWatch( watch ); + mWatches.erase( watch ); +} + +void FileWatcherWin32::watch() { + if ( NULL == mThread ) { + mThread = new Thread( &FileWatcherWin32::run, this ); + mThread->launch(); + } +} + +void FileWatcherWin32::removeAllWatches() { + Lock lock( mWatchesLock ); + + Watches::iterator iter = mWatches.begin(); + + for ( ; iter != mWatches.end(); ++iter ) { + DestroyWatch( ( *iter ) ); + } + + mWatches.clear(); +} + +void FileWatcherWin32::run() { + do { + if ( mInitOK && !mWatches.empty() ) { + DWORD numOfBytes = 0; + OVERLAPPED* ov = NULL; + ULONG_PTR compKey = 0; + BOOL res = FALSE; + + while ( ( res = GetQueuedCompletionStatus( mIOCP, &numOfBytes, &compKey, &ov, + INFINITE ) ) != FALSE ) { + if ( compKey != 0 && compKey == reinterpret_cast( this ) ) { + break; + } else { + Lock lock( mWatchesLock ); + if (mWatches.find( (WatcherStructWin32*)ov ) != mWatches.end()) + WatchCallback( numOfBytes, ov ); + } + } + } else { + System::sleep( 10 ); + } + } while ( mInitOK ); + + removeAllWatches(); +} + +void FileWatcherWin32::handleAction( Watcher* watch, const std::string& filename, + unsigned long action, std::string /*oldFilename*/ ) { + Action fwAction; + + switch ( action ) { + case FILE_ACTION_RENAMED_OLD_NAME: + watch->OldFileName = filename; + return; + case FILE_ACTION_ADDED: + fwAction = Actions::Add; + break; + case FILE_ACTION_RENAMED_NEW_NAME: { + fwAction = Actions::Moved; + + std::string fpath( watch->Directory + filename ); + + // Update the directory path + if ( watch->Recursive && FileSystem::isDirectory( fpath ) ) { + // Update the new directory path + std::string opath( watch->Directory + watch->OldFileName ); + FileSystem::dirAddSlashAtEnd( opath ); + FileSystem::dirAddSlashAtEnd( fpath ); + + for ( Watches::iterator it = mWatches.begin(); it != mWatches.end(); ++it ) { + if ( ( *it )->Watch->Directory == opath ) { + ( *it )->Watch->Directory = fpath; + + break; + } + } + } + + std::string folderPath( static_cast( watch )->DirName ); + std::string realFilename = filename; + std::size_t sepPos = filename.find_last_of( "/\\" ); + std::string oldFolderPath = + static_cast( watch )->DirName + + watch->OldFileName.substr( 0, watch->OldFileName.find_last_of( "/\\" ) ); + + if ( sepPos != std::string::npos ) { + folderPath += + filename.substr( 0, sepPos + 1 < filename.size() ? sepPos + 1 : sepPos ); + realFilename = filename.substr( sepPos + 1 ); + } + + if ( folderPath == oldFolderPath ) { + watch->Listener->handleFileAction( + watch->ID, folderPath, realFilename, fwAction, + FileSystem::fileNameFromPath( watch->OldFileName ) ); + } else { + watch->Listener->handleFileAction( watch->ID, + static_cast( watch )->DirName, + filename, fwAction, watch->OldFileName ); + } + return; + } + case FILE_ACTION_REMOVED: + fwAction = Actions::Delete; + break; + case FILE_ACTION_MODIFIED: + fwAction = Actions::Modified; + break; + default: + return; + }; + + std::string folderPath( static_cast( watch )->DirName ); + std::string realFilename = filename; + std::size_t sepPos = filename.find_last_of( "/\\" ); + + if ( sepPos != std::string::npos ) { + folderPath += filename.substr( 0, sepPos + 1 < filename.size() ? sepPos + 1 : sepPos ); + realFilename = filename.substr( sepPos + 1 ); + } + + FileSystem::dirAddSlashAtEnd( folderPath ); + + watch->Listener->handleFileAction( watch->ID, folderPath, realFilename, fwAction ); +} + +std::vector FileWatcherWin32::directories() { + std::vector dirs; + + Lock lock( mWatchesLock ); + + dirs.reserve( mWatches.size() ); + + for ( Watches::iterator it = mWatches.begin(); it != mWatches.end(); ++it ) { + dirs.push_back( std::string( ( *it )->Watch->DirName ) ); + } + + return dirs; +} + +bool FileWatcherWin32::pathInWatches( const std::string& path ) { + Lock lock( mWatchesLock ); + + for ( Watches::iterator it = mWatches.begin(); it != mWatches.end(); ++it ) { + if ( ( *it )->Watch->DirName == path ) { + return true; + } + } + + return false; +} + +} // namespace efsw + +#endif diff --git a/src/3rdParty/efsw/FileWatcherWin32.hpp b/src/3rdParty/efsw/FileWatcherWin32.hpp old mode 100755 new mode 100644 index 94439cf..3016aac --- a/src/3rdParty/efsw/FileWatcherWin32.hpp +++ b/src/3rdParty/efsw/FileWatcherWin32.hpp @@ -1,70 +1,71 @@ -#ifndef EFSW_FILEWATCHERWIN32_HPP -#define EFSW_FILEWATCHERWIN32_HPP - -#include - -#if EFSW_PLATFORM == EFSW_PLATFORM_WIN32 - -#include -#include -#include -#include - -namespace efsw { - -/// Implementation for Win32 based on ReadDirectoryChangesW. -/// @class FileWatcherWin32 -class FileWatcherWin32 : public FileWatcherImpl { - public: - /// type for a map from WatchID to WatcherWin32 pointer - typedef std::set Watches; - - FileWatcherWin32( FileWatcher* parent ); - - virtual ~FileWatcherWin32(); - - /// Add a directory watch - /// On error returns WatchID with Error type. - WatchID addWatch( const std::string& directory, FileWatchListener* watcher, bool recursive ); - - /// Remove a directory watch. This is a brute force lazy search O(nlogn). - void removeWatch( const std::string& directory ); - - /// Remove a directory watch. This is a map lookup O(logn). - void removeWatch( WatchID watchid ); - - /// Updates the watcher. Must be called often. - void watch(); - - /// Handles the action - void handleAction( Watcher* watch, const std::string& filename, unsigned long action, - std::string oldFilename = "" ); - - /// @return Returns a list of the directories that are being watched - std::list directories(); - - protected: - HANDLE mIOCP; - Watches mWatches; - - /// The last watchid - WatchID mLastWatchID; - Thread* mThread; - Mutex mWatchesLock; - - bool pathInWatches( const std::string& path ); - - /// Remove all directory watches. - void removeAllWatches(); - - void removeWatch( WatcherStructWin32* watch ); - - private: - void run(); -}; - -} // namespace efsw - -#endif - -#endif +#ifndef EFSW_FILEWATCHERWIN32_HPP +#define EFSW_FILEWATCHERWIN32_HPP + +#include + +#if EFSW_PLATFORM == EFSW_PLATFORM_WIN32 + +#include +#include +#include +#include + +namespace efsw { + +/// Implementation for Win32 based on ReadDirectoryChangesW. +/// @class FileWatcherWin32 +class FileWatcherWin32 : public FileWatcherImpl { + public: + /// type for a map from WatchID to WatcherWin32 pointer + typedef std::unordered_set Watches; + + FileWatcherWin32( FileWatcher* parent ); + + virtual ~FileWatcherWin32(); + + /// Add a directory watch + /// On error returns WatchID with Error type. + WatchID addWatch( const std::string& directory, FileWatchListener* watcher, bool recursive, + const std::vector &options ) override; + + /// Remove a directory watch. This is a brute force lazy search O(nlogn). + void removeWatch( const std::string& directory ) override; + + /// Remove a directory watch. This is a map lookup O(logn). + void removeWatch( WatchID watchid ) override; + + /// Updates the watcher. Must be called often. + void watch() override; + + /// Handles the action + void handleAction( Watcher* watch, const std::string& filename, unsigned long action, + std::string oldFilename = "" ) override; + + /// @return Returns a list of the directories that are being watched + std::vector directories() override; + + protected: + HANDLE mIOCP; + Watches mWatches; + + /// The last watchid + WatchID mLastWatchID; + Thread* mThread; + Mutex mWatchesLock; + + bool pathInWatches( const std::string& path ) override; + + /// Remove all directory watches. + void removeAllWatches(); + + void removeWatch( WatcherStructWin32* watch ); + + private: + void run(); +}; + +} // namespace efsw + +#endif + +#endif diff --git a/src/3rdParty/efsw/LICENSE b/src/3rdParty/efsw/LICENSE old mode 100755 new mode 100644 diff --git a/src/3rdParty/efsw/Lock.hpp b/src/3rdParty/efsw/Lock.hpp old mode 100755 new mode 100644 diff --git a/src/3rdParty/efsw/Log.cpp b/src/3rdParty/efsw/Log.cpp old mode 100755 new mode 100644 index ddf7a62..6f32df7 --- a/src/3rdParty/efsw/Log.cpp +++ b/src/3rdParty/efsw/Log.cpp @@ -1,20 +1,31 @@ #include +#include namespace efsw { namespace Errors { -static std::string LastError; +static std::string LastError = ""; +static Error LastErrorCode = NoError; std::string Log::getLastErrorLog() { return LastError; } +Error Log::getLastErrorCode() { + return LastErrorCode; +} + +void Log::clearLastError() { + LastErrorCode = NoError; + LastError = ""; +} + Error Log::createLastError( Error err, std::string log ) { switch ( err ) { case FileNotFound: LastError = "File not found ( " + log + " )"; break; case FileRepeated: - LastError = "File reapeated in watches ( " + log + " )"; + LastError = "File repeated in watches ( " + log + " )"; break; case FileOutOfScope: LastError = "Symlink file out of scope ( " + log + " )"; @@ -23,11 +34,15 @@ Error Log::createLastError( Error err, std::string log ) { LastError = "File is located in a remote file system, use a generic watcher. ( " + log + " )"; break; + case WatcherFailed: + LastError = "File system watcher failed ( " + log + " )"; + break; case Unspecified: default: LastError = log; } + efDEBUG( "%s\n", LastError.c_str() ); return err; } diff --git a/src/3rdParty/efsw/Mutex.cpp b/src/3rdParty/efsw/Mutex.cpp old mode 100755 new mode 100644 diff --git a/src/3rdParty/efsw/Mutex.hpp b/src/3rdParty/efsw/Mutex.hpp old mode 100755 new mode 100644 diff --git a/src/3rdParty/efsw/String.cpp b/src/3rdParty/efsw/String.cpp old mode 100755 new mode 100644 diff --git a/src/3rdParty/efsw/String.hpp b/src/3rdParty/efsw/String.hpp old mode 100755 new mode 100644 index 65bce33..b42b945 --- a/src/3rdParty/efsw/String.hpp +++ b/src/3rdParty/efsw/String.hpp @@ -11,7 +11,6 @@ #include #include #include -#include #include #include #include @@ -24,7 +23,7 @@ namespace efsw { * **/ class String { public: - typedef Uint32 StringBaseType; + typedef char32_t StringBaseType; typedef std::basic_string StringType; typedef StringType::iterator Iterator; //! Iterator type typedef StringType::const_iterator ConstIterator; //! Constant iterator type diff --git a/src/3rdParty/efsw/System.cpp b/src/3rdParty/efsw/System.cpp old mode 100755 new mode 100644 diff --git a/src/3rdParty/efsw/System.hpp b/src/3rdParty/efsw/System.hpp old mode 100755 new mode 100644 diff --git a/src/3rdParty/efsw/Thread.cpp b/src/3rdParty/efsw/Thread.cpp old mode 100755 new mode 100644 index e3f0fa0..cfa88b4 --- a/src/3rdParty/efsw/Thread.cpp +++ b/src/3rdParty/efsw/Thread.cpp @@ -34,7 +34,8 @@ void Thread::terminate() { } void Thread::run() { - mEntryPoint->run(); + if ( mEntryPoint ) + mEntryPoint->run(); } } // namespace efsw diff --git a/src/3rdParty/efsw/Thread.hpp b/src/3rdParty/efsw/Thread.hpp old mode 100755 new mode 100644 diff --git a/src/3rdParty/efsw/Utf.hpp b/src/3rdParty/efsw/Utf.hpp old mode 100755 new mode 100644 index 6e9ea71..1b042cd --- a/src/3rdParty/efsw/Utf.hpp +++ b/src/3rdParty/efsw/Utf.hpp @@ -1,721 +1,721 @@ -/** NOTE: - * This code is based on the Utf implementation from SFML2. License zlib/png ( - *http://www.sfml-dev.org/license.php ) The class was modified to fit efsw own needs. This is not - *the original implementation from SFML2. - * */ - -#ifndef EFSW_UTF_HPP -#define EFSW_UTF_HPP - -//////////////////////////////////////////////////////////// -// Headers -//////////////////////////////////////////////////////////// -#include -#include -#include -#include - -namespace efsw { - -template class Utf; - -//////////////////////////////////////////////////////////// -/// \brief Specialization of the Utf template for UTF-8 -/// -//////////////////////////////////////////////////////////// -template <> class Utf<8> { - public: - //////////////////////////////////////////////////////////// - /// \brief Decode a single UTF-8 character - /// - /// Decoding a character means finding its unique 32-bits - /// code (called the codepoint) in the Unicode standard. - /// - /// \param begin Iterator pointing to the beginning of the input sequence - /// \param end Iterator pointing to the end of the input sequence - /// \param output Codepoint of the decoded UTF-8 character - /// \param replacement Replacement character to use in case the UTF-8 sequence is invalid - /// - /// \return Iterator pointing to one past the last read element of the input sequence - /// - //////////////////////////////////////////////////////////// - template - static In Decode( In begin, In end, Uint32& output, Uint32 replacement = 0 ); - - //////////////////////////////////////////////////////////// - /// \brief Encode a single UTF-8 character - /// - /// Encoding a character means converting a unique 32-bits - /// code (called the codepoint) in the target encoding, UTF-8. - /// - /// \param input Codepoint to encode as UTF-8 - /// \param output Iterator pointing to the beginning of the output sequence - /// \param replacement Replacement for characters not convertible to UTF-8 (use 0 to skip them) - /// - /// \return Iterator to the end of the output sequence which has been written - /// - //////////////////////////////////////////////////////////// - template static Out Encode( Uint32 input, Out output, Uint8 replacement = 0 ); - - //////////////////////////////////////////////////////////// - /// \brief Advance to the next UTF-8 character - /// - /// This function is necessary for multi-elements encodings, as - /// a single character may use more than 1 storage element. - /// - /// \param begin Iterator pointing to the beginning of the input sequence - /// \param end Iterator pointing to the end of the input sequence - /// - /// \return Iterator pointing to one past the last read element of the input sequence - /// - //////////////////////////////////////////////////////////// - template static In Next( In begin, In end ); - - //////////////////////////////////////////////////////////// - /// \brief Count the number of characters of a UTF-8 sequence - /// - /// This function is necessary for multi-elements encodings, as - /// a single character may use more than 1 storage element, thus the - /// total size can be different from (begin - end). - /// - /// \param begin Iterator pointing to the beginning of the input sequence - /// \param end Iterator pointing to the end of the input sequence - /// - /// \return Iterator pointing to one past the last read element of the input sequence - /// - //////////////////////////////////////////////////////////// - template static std::size_t Count( In begin, In end ); - - //////////////////////////////////////////////////////////// - /// \brief Convert an ANSI characters range to UTF-8 - /// - /// The current global locale will be used by default, unless you - /// pass a custom one in the \a locale parameter. - /// - /// \param begin Iterator pointing to the beginning of the input sequence - /// \param end Iterator pointing to the end of the input sequence - /// \param output Iterator pointing to the beginning of the output sequence - /// \param locale Locale to use for conversion - /// - /// \return Iterator to the end of the output sequence which has been written - /// - //////////////////////////////////////////////////////////// - template - static Out FromAnsi( In begin, In end, Out output, const std::locale& locale = std::locale() ); - - //////////////////////////////////////////////////////////// - /// \brief Convert a wide characters range to UTF-8 - /// - /// \param begin Iterator pointing to the beginning of the input sequence - /// \param end Iterator pointing to the end of the input sequence - /// \param output Iterator pointing to the beginning of the output sequence - /// - /// \return Iterator to the end of the output sequence which has been written - /// - //////////////////////////////////////////////////////////// - template static Out FromWide( In begin, In end, Out output ); - - //////////////////////////////////////////////////////////// - /// \brief Convert a latin-1 (ISO-5589-1) characters range to UTF-8 - /// - /// \param begin Iterator pointing to the beginning of the input sequence - /// \param end Iterator pointing to the end of the input sequence - /// \param output Iterator pointing to the beginning of the output sequence - /// \param locale Locale to use for conversion - /// - /// \return Iterator to the end of the output sequence which has been written - /// - //////////////////////////////////////////////////////////// - template static Out FromLatin1( In begin, In end, Out output ); - - //////////////////////////////////////////////////////////// - /// \brief Convert an UTF-8 characters range to ANSI characters - /// - /// The current global locale will be used by default, unless you - /// pass a custom one in the \a locale parameter. - /// - /// \param begin Iterator pointing to the beginning of the input sequence - /// \param end Iterator pointing to the end of the input sequence - /// \param output Iterator pointing to the beginning of the output sequence - /// \param replacement Replacement for characters not convertible to ANSI (use 0 to skip them) - /// \param locale Locale to use for conversion - /// - /// \return Iterator to the end of the output sequence which has been written - /// - //////////////////////////////////////////////////////////// - template - static Out ToAnsi( In begin, In end, Out output, char replacement = 0, - const std::locale& locale = std::locale() ); - -#ifndef EFSW_NO_WIDECHAR - //////////////////////////////////////////////////////////// - /// \brief Convert an UTF-8 characters range to wide characters - /// - /// \param begin Iterator pointing to the beginning of the input sequence - /// \param end Iterator pointing to the end of the input sequence - /// \param output Iterator pointing to the beginning of the output sequence - /// \param replacement Replacement for characters not convertible to wide (use 0 to skip them) - /// - /// \return Iterator to the end of the output sequence which has been written - /// - //////////////////////////////////////////////////////////// - template - static Out ToWide( In begin, In end, Out output, wchar_t replacement = 0 ); -#endif - - //////////////////////////////////////////////////////////// - /// \brief Convert an UTF-8 characters range to latin-1 (ISO-5589-1) characters - /// - /// \param begin Iterator pointing to the beginning of the input sequence - /// \param end Iterator pointing to the end of the input sequence - /// \param output Iterator pointing to the beginning of the output sequence - /// \param replacement Replacement for characters not convertible to wide (use 0 to skip them) - /// - /// \return Iterator to the end of the output sequence which has been written - /// - //////////////////////////////////////////////////////////// - template - static Out ToLatin1( In begin, In end, Out output, char replacement = 0 ); - - //////////////////////////////////////////////////////////// - /// \brief Convert a UTF-8 characters range to UTF-8 - /// - /// This functions does nothing more than a direct copy; - /// it is defined only to provide the same interface as other - /// specializations of the efsw::Utf<> template, and allow - /// generic code to be written on top of it. - /// - /// \param begin Iterator pointing to the beginning of the input sequence - /// \param end Iterator pointing to the end of the input sequence - /// \param output Iterator pointing to the beginning of the output sequence - /// - /// \return Iterator to the end of the output sequence which has been written - /// - //////////////////////////////////////////////////////////// - template static Out toUtf8( In begin, In end, Out output ); - - //////////////////////////////////////////////////////////// - /// \brief Convert a UTF-8 characters range to UTF-16 - /// - /// \param begin Iterator pointing to the beginning of the input sequence - /// \param end Iterator pointing to the end of the input sequence - /// \param output Iterator pointing to the beginning of the output sequence - /// - /// \return Iterator to the end of the output sequence which has been written - /// - //////////////////////////////////////////////////////////// - template static Out ToUtf16( In begin, In end, Out output ); - - //////////////////////////////////////////////////////////// - /// \brief Convert a UTF-8 characters range to UTF-32 - /// - /// \param begin Iterator pointing to the beginning of the input sequence - /// \param end Iterator pointing to the end of the input sequence - /// \param output Iterator pointing to the beginning of the output sequence - /// - /// \return Iterator to the end of the output sequence which has been written - /// - //////////////////////////////////////////////////////////// - template static Out ToUtf32( In begin, In end, Out output ); -}; - -//////////////////////////////////////////////////////////// -/// \brief Specialization of the Utf template for UTF-16 -/// -//////////////////////////////////////////////////////////// -template <> class Utf<16> { - public: - //////////////////////////////////////////////////////////// - /// \brief Decode a single UTF-16 character - /// - /// Decoding a character means finding its unique 32-bits - /// code (called the codepoint) in the Unicode standard. - /// - /// \param begin Iterator pointing to the beginning of the input sequence - /// \param end Iterator pointing to the end of the input sequence - /// \param output Codepoint of the decoded UTF-16 character - /// \param replacement Replacement character to use in case the UTF-8 sequence is invalid - /// - /// \return Iterator pointing to one past the last read element of the input sequence - /// - //////////////////////////////////////////////////////////// - template - static In Decode( In begin, In end, Uint32& output, Uint32 replacement = 0 ); - - //////////////////////////////////////////////////////////// - /// \brief Encode a single UTF-16 character - /// - /// Encoding a character means converting a unique 32-bits - /// code (called the codepoint) in the target encoding, UTF-16. - /// - /// \param input Codepoint to encode as UTF-16 - /// \param output Iterator pointing to the beginning of the output sequence - /// \param replacement Replacement for characters not convertible to UTF-16 (use 0 to skip them) - /// - /// \return Iterator to the end of the output sequence which has been written - /// - //////////////////////////////////////////////////////////// - template static Out Encode( Uint32 input, Out output, Uint16 replacement = 0 ); - - //////////////////////////////////////////////////////////// - /// \brief Advance to the next UTF-16 character - /// - /// This function is necessary for multi-elements encodings, as - /// a single character may use more than 1 storage element. - /// - /// \param begin Iterator pointing to the beginning of the input sequence - /// \param end Iterator pointing to the end of the input sequence - /// - /// \return Iterator pointing to one past the last read element of the input sequence - /// - //////////////////////////////////////////////////////////// - template static In Next( In begin, In end ); - - //////////////////////////////////////////////////////////// - /// \brief Count the number of characters of a UTF-16 sequence - /// - /// This function is necessary for multi-elements encodings, as - /// a single character may use more than 1 storage element, thus the - /// total size can be different from (begin - end). - /// - /// \param begin Iterator pointing to the beginning of the input sequence - /// \param end Iterator pointing to the end of the input sequence - /// - /// \return Iterator pointing to one past the last read element of the input sequence - /// - //////////////////////////////////////////////////////////// - template static std::size_t Count( In begin, In end ); - - //////////////////////////////////////////////////////////// - /// \brief Convert an ANSI characters range to UTF-16 - /// - /// The current global locale will be used by default, unless you - /// pass a custom one in the \a locale parameter. - /// - /// \param begin Iterator pointing to the beginning of the input sequence - /// \param end Iterator pointing to the end of the input sequence - /// \param output Iterator pointing to the beginning of the output sequence - /// \param locale Locale to use for conversion - /// - /// \return Iterator to the end of the output sequence which has been written - /// - //////////////////////////////////////////////////////////// - template - static Out FromAnsi( In begin, In end, Out output, const std::locale& locale = std::locale() ); - - //////////////////////////////////////////////////////////// - /// \brief Convert a wide characters range to UTF-16 - /// - /// \param begin Iterator pointing to the beginning of the input sequence - /// \param end Iterator pointing to the end of the input sequence - /// \param output Iterator pointing to the beginning of the output sequence - /// - /// \return Iterator to the end of the output sequence which has been written - /// - //////////////////////////////////////////////////////////// - template static Out FromWide( In begin, In end, Out output ); - - //////////////////////////////////////////////////////////// - /// \brief Convert a latin-1 (ISO-5589-1) characters range to UTF-16 - /// - /// \param begin Iterator pointing to the beginning of the input sequence - /// \param end Iterator pointing to the end of the input sequence - /// \param output Iterator pointing to the beginning of the output sequence - /// \param locale Locale to use for conversion - /// - /// \return Iterator to the end of the output sequence which has been written - /// - //////////////////////////////////////////////////////////// - template static Out FromLatin1( In begin, In end, Out output ); - - //////////////////////////////////////////////////////////// - /// \brief Convert an UTF-16 characters range to ANSI characters - /// - /// The current global locale will be used by default, unless you - /// pass a custom one in the \a locale parameter. - /// - /// \param begin Iterator pointing to the beginning of the input sequence - /// \param end Iterator pointing to the end of the input sequence - /// \param output Iterator pointing to the beginning of the output sequence - /// \param replacement Replacement for characters not convertible to ANSI (use 0 to skip them) - /// \param locale Locale to use for conversion - /// - /// \return Iterator to the end of the output sequence which has been written - /// - //////////////////////////////////////////////////////////// - template - static Out ToAnsi( In begin, In end, Out output, char replacement = 0, - const std::locale& locale = std::locale() ); - -#ifndef EFSW_NO_WIDECHAR - //////////////////////////////////////////////////////////// - /// \brief Convert an UTF-16 characters range to wide characters - /// - /// \param begin Iterator pointing to the beginning of the input sequence - /// \param end Iterator pointing to the end of the input sequence - /// \param output Iterator pointing to the beginning of the output sequence - /// \param replacement Replacement for characters not convertible to wide (use 0 to skip them) - /// - /// \return Iterator to the end of the output sequence which has been written - /// - //////////////////////////////////////////////////////////// - template - static Out ToWide( In begin, In end, Out output, wchar_t replacement = 0 ); -#endif - - //////////////////////////////////////////////////////////// - /// \brief Convert an UTF-16 characters range to latin-1 (ISO-5589-1) characters - /// - /// \param begin Iterator pointing to the beginning of the input sequence - /// \param end Iterator pointing to the end of the input sequence - /// \param output Iterator pointing to the beginning of the output sequence - /// \param replacement Replacement for characters not convertible to wide (use 0 to skip them) - /// - /// \return Iterator to the end of the output sequence which has been written - /// - //////////////////////////////////////////////////////////// - template - static Out ToLatin1( In begin, In end, Out output, char replacement = 0 ); - - //////////////////////////////////////////////////////////// - /// \brief Convert a UTF-16 characters range to UTF-8 - /// - /// \param begin Iterator pointing to the beginning of the input sequence - /// \param end Iterator pointing to the end of the input sequence - /// \param output Iterator pointing to the beginning of the output sequence - /// - /// \return Iterator to the end of the output sequence which has been written - /// - //////////////////////////////////////////////////////////// - template static Out toUtf8( In begin, In end, Out output ); - - //////////////////////////////////////////////////////////// - /// \brief Convert a UTF-16 characters range to UTF-16 - /// - /// This functions does nothing more than a direct copy; - /// it is defined only to provide the same interface as other - /// specializations of the efsw::Utf<> template, and allow - /// generic code to be written on top of it. - /// - /// \param begin Iterator pointing to the beginning of the input sequence - /// \param end Iterator pointing to the end of the input sequence - /// \param output Iterator pointing to the beginning of the output sequence - /// - /// \return Iterator to the end of the output sequence which has been written - /// - //////////////////////////////////////////////////////////// - template static Out ToUtf16( In begin, In end, Out output ); - - //////////////////////////////////////////////////////////// - /// \brief Convert a UTF-16 characters range to UTF-32 - /// - /// \param begin Iterator pointing to the beginning of the input sequence - /// \param end Iterator pointing to the end of the input sequence - /// \param output Iterator pointing to the beginning of the output sequence - /// - /// \return Iterator to the end of the output sequence which has been written - /// - //////////////////////////////////////////////////////////// - template static Out ToUtf32( In begin, In end, Out output ); -}; - -//////////////////////////////////////////////////////////// -/// \brief Specialization of the Utf template for UTF-32 -/// -//////////////////////////////////////////////////////////// -template <> class Utf<32> { - public: - //////////////////////////////////////////////////////////// - /// \brief Decode a single UTF-32 character - /// - /// Decoding a character means finding its unique 32-bits - /// code (called the codepoint) in the Unicode standard. - /// For UTF-32, the character value is the same as the codepoint. - /// - /// \param begin Iterator pointing to the beginning of the input sequence - /// \param end Iterator pointing to the end of the input sequence - /// \param output Codepoint of the decoded UTF-32 character - /// \param replacement Replacement character to use in case the UTF-8 sequence is invalid - /// - /// \return Iterator pointing to one past the last read element of the input sequence - /// - //////////////////////////////////////////////////////////// - template - static In Decode( In begin, In end, Uint32& output, Uint32 replacement = 0 ); - - //////////////////////////////////////////////////////////// - /// \brief Encode a single UTF-32 character - /// - /// Encoding a character means converting a unique 32-bits - /// code (called the codepoint) in the target encoding, UTF-32. - /// For UTF-32, the codepoint is the same as the character value. - /// - /// \param input Codepoint to encode as UTF-32 - /// \param output Iterator pointing to the beginning of the output sequence - /// \param replacement Replacement for characters not convertible to UTF-32 (use 0 to skip them) - /// - /// \return Iterator to the end of the output sequence which has been written - /// - //////////////////////////////////////////////////////////// - template static Out Encode( Uint32 input, Out output, Uint32 replacement = 0 ); - - //////////////////////////////////////////////////////////// - /// \brief Advance to the next UTF-32 character - /// - /// This function is trivial for UTF-32, which can store - /// every character in a single storage element. - /// - /// \param begin Iterator pointing to the beginning of the input sequence - /// \param end Iterator pointing to the end of the input sequence - /// - /// \return Iterator pointing to one past the last read element of the input sequence - /// - //////////////////////////////////////////////////////////// - template static In Next( In begin, In end ); - - //////////////////////////////////////////////////////////// - /// \brief Count the number of characters of a UTF-32 sequence - /// - /// This function is trivial for UTF-32, which can store - /// every character in a single storage element. - /// - /// \param begin Iterator pointing to the beginning of the input sequence - /// \param end Iterator pointing to the end of the input sequence - /// - /// \return Iterator pointing to one past the last read element of the input sequence - /// - //////////////////////////////////////////////////////////// - template static std::size_t Count( In begin, In end ); - - //////////////////////////////////////////////////////////// - /// \brief Convert an ANSI characters range to UTF-32 - /// - /// The current global locale will be used by default, unless you - /// pass a custom one in the \a locale parameter. - /// - /// \param begin Iterator pointing to the beginning of the input sequence - /// \param end Iterator pointing to the end of the input sequence - /// \param output Iterator pointing to the beginning of the output sequence - /// \param locale Locale to use for conversion - /// - /// \return Iterator to the end of the output sequence which has been written - /// - //////////////////////////////////////////////////////////// - template - static Out FromAnsi( In begin, In end, Out output, const std::locale& locale = std::locale() ); - - //////////////////////////////////////////////////////////// - /// \brief Convert a wide characters range to UTF-32 - /// - /// \param begin Iterator pointing to the beginning of the input sequence - /// \param end Iterator pointing to the end of the input sequence - /// \param output Iterator pointing to the beginning of the output sequence - /// - /// \return Iterator to the end of the output sequence which has been written - /// - //////////////////////////////////////////////////////////// - template static Out FromWide( In begin, In end, Out output ); - - //////////////////////////////////////////////////////////// - /// \brief Convert a latin-1 (ISO-5589-1) characters range to UTF-32 - /// - /// \param begin Iterator pointing to the beginning of the input sequence - /// \param end Iterator pointing to the end of the input sequence - /// \param output Iterator pointing to the beginning of the output sequence - /// \param locale Locale to use for conversion - /// - /// \return Iterator to the end of the output sequence which has been written - /// - //////////////////////////////////////////////////////////// - template static Out FromLatin1( In begin, In end, Out output ); - - //////////////////////////////////////////////////////////// - /// \brief Convert an UTF-32 characters range to ANSI characters - /// - /// The current global locale will be used by default, unless you - /// pass a custom one in the \a locale parameter. - /// - /// \param begin Iterator pointing to the beginning of the input sequence - /// \param end Iterator pointing to the end of the input sequence - /// \param output Iterator pointing to the beginning of the output sequence - /// \param replacement Replacement for characters not convertible to ANSI (use 0 to skip them) - /// \param locale Locale to use for conversion - /// - /// \return Iterator to the end of the output sequence which has been written - /// - //////////////////////////////////////////////////////////// - template - static Out ToAnsi( In begin, In end, Out output, char replacement = 0, - const std::locale& locale = std::locale() ); - -#ifndef EFSW_NO_WIDECHAR - //////////////////////////////////////////////////////////// - /// \brief Convert an UTF-32 characters range to wide characters - /// - /// \param begin Iterator pointing to the beginning of the input sequence - /// \param end Iterator pointing to the end of the input sequence - /// \param output Iterator pointing to the beginning of the output sequence - /// \param replacement Replacement for characters not convertible to wide (use 0 to skip them) - /// - /// \return Iterator to the end of the output sequence which has been written - /// - //////////////////////////////////////////////////////////// - template - static Out ToWide( In begin, In end, Out output, wchar_t replacement = 0 ); -#endif - - //////////////////////////////////////////////////////////// - /// \brief Convert an UTF-16 characters range to latin-1 (ISO-5589-1) characters - /// - /// \param begin Iterator pointing to the beginning of the input sequence - /// \param end Iterator pointing to the end of the input sequence - /// \param output Iterator pointing to the beginning of the output sequence - /// \param replacement Replacement for characters not convertible to wide (use 0 to skip them) - /// - /// \return Iterator to the end of the output sequence which has been written - /// - //////////////////////////////////////////////////////////// - template - static Out ToLatin1( In begin, In end, Out output, char replacement = 0 ); - - //////////////////////////////////////////////////////////// - /// \brief Convert a UTF-32 characters range to UTF-8 - /// - /// \param begin Iterator pointing to the beginning of the input sequence - /// \param end Iterator pointing to the end of the input sequence - /// \param output Iterator pointing to the beginning of the output sequence - /// - /// \return Iterator to the end of the output sequence which has been written - /// - //////////////////////////////////////////////////////////// - template static Out toUtf8( In begin, In end, Out output ); - - //////////////////////////////////////////////////////////// - /// \brief Convert a UTF-32 characters range to UTF-16 - /// - /// \param begin Iterator pointing to the beginning of the input sequence - /// \param end Iterator pointing to the end of the input sequence - /// \param output Iterator pointing to the beginning of the output sequence - /// - /// \return Iterator to the end of the output sequence which has been written - /// - //////////////////////////////////////////////////////////// - template static Out ToUtf16( In begin, In end, Out output ); - - //////////////////////////////////////////////////////////// - /// \brief Convert a UTF-32 characters range to UTF-32 - /// - /// This functions does nothing more than a direct copy; - /// it is defined only to provide the same interface as other - /// specializations of the efsw::Utf<> template, and allow - /// generic code to be written on top of it. - /// - /// \param begin Iterator pointing to the beginning of the input sequence - /// \param end Iterator pointing to the end of the input sequence - /// \param output Iterator pointing to the beginning of the output sequence - /// - /// \return Iterator to the end of the output sequence which has been written - /// - //////////////////////////////////////////////////////////// - template static Out ToUtf32( In begin, In end, Out output ); - - //////////////////////////////////////////////////////////// - /// \brief Decode a single ANSI character to UTF-32 - /// - /// This function does not exist in other specializations - /// of efsw::Utf<>, it is defined for convenience (it is used by - /// several other conversion functions). - /// - /// \param input Input ANSI character - /// \param locale Locale to use for conversion - /// - /// \return Converted character - /// - //////////////////////////////////////////////////////////// - template - static Uint32 DecodeAnsi( In input, const std::locale& locale = std::locale() ); - - //////////////////////////////////////////////////////////// - /// \brief Decode a single wide character to UTF-32 - /// - /// This function does not exist in other specializations - /// of efsw::Utf<>, it is defined for convenience (it is used by - /// several other conversion functions). - /// - /// \param input Input wide character - /// - /// \return Converted character - /// - //////////////////////////////////////////////////////////// - template static Uint32 DecodeWide( In input ); - - //////////////////////////////////////////////////////////// - /// \brief Encode a single UTF-32 character to ANSI - /// - /// This function does not exist in other specializations - /// of efsw::Utf<>, it is defined for convenience (it is used by - /// several other conversion functions). - /// - /// \param codepoint Iterator pointing to the beginning of the input sequence - /// \param output Iterator pointing to the beginning of the output sequence - /// \param replacement Replacement if the input character is not convertible to ANSI (use 0 to - /// skip it) \param locale Locale to use for conversion - /// - /// \return Iterator to the end of the output sequence which has been written - /// - //////////////////////////////////////////////////////////// - template - static Out EncodeAnsi( Uint32 codepoint, Out output, char replacement = 0, - const std::locale& locale = std::locale() ); - -#ifndef EFSW_NO_WIDECHAR - //////////////////////////////////////////////////////////// - /// \brief Encode a single UTF-32 character to wide - /// - /// This function does not exist in other specializations - /// of efsw::Utf<>, it is defined for convenience (it is used by - /// several other conversion functions). - /// - /// \param codepoint Iterator pointing to the beginning of the input sequence - /// \param output Iterator pointing to the beginning of the output sequence - /// \param replacement Replacement if the input character is not convertible to wide (use 0 to - /// skip it) - /// - /// \return Iterator to the end of the output sequence which has been written - /// - //////////////////////////////////////////////////////////// - template - static Out EncodeWide( Uint32 codepoint, Out output, wchar_t replacement = 0 ); -#endif -}; - -#include "Utf.inl" - -// Make typedefs to get rid of the template syntax -typedef Utf<8> Utf8; -typedef Utf<16> Utf16; -typedef Utf<32> Utf32; - -} // namespace efsw -#endif - -//////////////////////////////////////////////////////////// -/// \class efsw::Utf -/// \ingroup system -/// -/// Utility class providing generic functions for UTF conversions. -/// -/// efsw::Utf is a low-level, generic interface for counting, iterating, -/// encoding and decoding Unicode characters and strings. It is able -/// to handle ANSI, wide, UTF-8, UTF-16 and UTF-32 encodings. -/// -/// efsw::Utf functions are all static, these classes are not meant to -/// be instanciated. All the functions are template, so that you -/// can use any character / string type for a given encoding. -/// -/// It has 3 specializations: -/// \li efsw::Utf<8> (typedef'd to efsw::Utf8) -/// \li efsw::Utf<16> (typedef'd to efsw::Utf16) -/// \li efsw::Utf<32> (typedef'd to efsw::Utf32) -/// -//////////////////////////////////////////////////////////// +/** NOTE: + * This code is based on the Utf implementation from SFML2. License zlib/png ( + *http://www.sfml-dev.org/license.php ) The class was modified to fit efsw own needs. This is not + *the original implementation from SFML2. + * */ + +#ifndef EFSW_UTF_HPP +#define EFSW_UTF_HPP + +//////////////////////////////////////////////////////////// +// Headers +//////////////////////////////////////////////////////////// +#include +#include +#include +#include + +namespace efsw { + +template class Utf; + +//////////////////////////////////////////////////////////// +/// \brief Specialization of the Utf template for UTF-8 +/// +//////////////////////////////////////////////////////////// +template <> class Utf<8> { + public: + //////////////////////////////////////////////////////////// + /// \brief Decode a single UTF-8 character + /// + /// Decoding a character means finding its unique 32-bits + /// code (called the codepoint) in the Unicode standard. + /// + /// \param begin Iterator pointing to the beginning of the input sequence + /// \param end Iterator pointing to the end of the input sequence + /// \param output Codepoint of the decoded UTF-8 character + /// \param replacement Replacement character to use in case the UTF-8 sequence is invalid + /// + /// \return Iterator pointing to one past the last read element of the input sequence + /// + //////////////////////////////////////////////////////////// + template + static In Decode( In begin, In end, Uint32& output, Uint32 replacement = 0 ); + + //////////////////////////////////////////////////////////// + /// \brief Encode a single UTF-8 character + /// + /// Encoding a character means converting a unique 32-bits + /// code (called the codepoint) in the target encoding, UTF-8. + /// + /// \param input Codepoint to encode as UTF-8 + /// \param output Iterator pointing to the beginning of the output sequence + /// \param replacement Replacement for characters not convertible to UTF-8 (use 0 to skip them) + /// + /// \return Iterator to the end of the output sequence which has been written + /// + //////////////////////////////////////////////////////////// + template static Out Encode( Uint32 input, Out output, Uint8 replacement = 0 ); + + //////////////////////////////////////////////////////////// + /// \brief Advance to the next UTF-8 character + /// + /// This function is necessary for multi-elements encodings, as + /// a single character may use more than 1 storage element. + /// + /// \param begin Iterator pointing to the beginning of the input sequence + /// \param end Iterator pointing to the end of the input sequence + /// + /// \return Iterator pointing to one past the last read element of the input sequence + /// + //////////////////////////////////////////////////////////// + template static In Next( In begin, In end ); + + //////////////////////////////////////////////////////////// + /// \brief Count the number of characters of a UTF-8 sequence + /// + /// This function is necessary for multi-elements encodings, as + /// a single character may use more than 1 storage element, thus the + /// total size can be different from (begin - end). + /// + /// \param begin Iterator pointing to the beginning of the input sequence + /// \param end Iterator pointing to the end of the input sequence + /// + /// \return Iterator pointing to one past the last read element of the input sequence + /// + //////////////////////////////////////////////////////////// + template static std::size_t Count( In begin, In end ); + + //////////////////////////////////////////////////////////// + /// \brief Convert an ANSI characters range to UTF-8 + /// + /// The current global locale will be used by default, unless you + /// pass a custom one in the \a locale parameter. + /// + /// \param begin Iterator pointing to the beginning of the input sequence + /// \param end Iterator pointing to the end of the input sequence + /// \param output Iterator pointing to the beginning of the output sequence + /// \param locale Locale to use for conversion + /// + /// \return Iterator to the end of the output sequence which has been written + /// + //////////////////////////////////////////////////////////// + template + static Out FromAnsi( In begin, In end, Out output, const std::locale& locale = std::locale() ); + + //////////////////////////////////////////////////////////// + /// \brief Convert a wide characters range to UTF-8 + /// + /// \param begin Iterator pointing to the beginning of the input sequence + /// \param end Iterator pointing to the end of the input sequence + /// \param output Iterator pointing to the beginning of the output sequence + /// + /// \return Iterator to the end of the output sequence which has been written + /// + //////////////////////////////////////////////////////////// + template static Out FromWide( In begin, In end, Out output ); + + //////////////////////////////////////////////////////////// + /// \brief Convert a latin-1 (ISO-5589-1) characters range to UTF-8 + /// + /// \param begin Iterator pointing to the beginning of the input sequence + /// \param end Iterator pointing to the end of the input sequence + /// \param output Iterator pointing to the beginning of the output sequence + /// \param locale Locale to use for conversion + /// + /// \return Iterator to the end of the output sequence which has been written + /// + //////////////////////////////////////////////////////////// + template static Out FromLatin1( In begin, In end, Out output ); + + //////////////////////////////////////////////////////////// + /// \brief Convert an UTF-8 characters range to ANSI characters + /// + /// The current global locale will be used by default, unless you + /// pass a custom one in the \a locale parameter. + /// + /// \param begin Iterator pointing to the beginning of the input sequence + /// \param end Iterator pointing to the end of the input sequence + /// \param output Iterator pointing to the beginning of the output sequence + /// \param replacement Replacement for characters not convertible to ANSI (use 0 to skip them) + /// \param locale Locale to use for conversion + /// + /// \return Iterator to the end of the output sequence which has been written + /// + //////////////////////////////////////////////////////////// + template + static Out ToAnsi( In begin, In end, Out output, char replacement = 0, + const std::locale& locale = std::locale() ); + +#ifndef EFSW_NO_WIDECHAR + //////////////////////////////////////////////////////////// + /// \brief Convert an UTF-8 characters range to wide characters + /// + /// \param begin Iterator pointing to the beginning of the input sequence + /// \param end Iterator pointing to the end of the input sequence + /// \param output Iterator pointing to the beginning of the output sequence + /// \param replacement Replacement for characters not convertible to wide (use 0 to skip them) + /// + /// \return Iterator to the end of the output sequence which has been written + /// + //////////////////////////////////////////////////////////// + template + static Out ToWide( In begin, In end, Out output, wchar_t replacement = 0 ); +#endif + + //////////////////////////////////////////////////////////// + /// \brief Convert an UTF-8 characters range to latin-1 (ISO-5589-1) characters + /// + /// \param begin Iterator pointing to the beginning of the input sequence + /// \param end Iterator pointing to the end of the input sequence + /// \param output Iterator pointing to the beginning of the output sequence + /// \param replacement Replacement for characters not convertible to wide (use 0 to skip them) + /// + /// \return Iterator to the end of the output sequence which has been written + /// + //////////////////////////////////////////////////////////// + template + static Out ToLatin1( In begin, In end, Out output, char replacement = 0 ); + + //////////////////////////////////////////////////////////// + /// \brief Convert a UTF-8 characters range to UTF-8 + /// + /// This functions does nothing more than a direct copy; + /// it is defined only to provide the same interface as other + /// specializations of the efsw::Utf<> template, and allow + /// generic code to be written on top of it. + /// + /// \param begin Iterator pointing to the beginning of the input sequence + /// \param end Iterator pointing to the end of the input sequence + /// \param output Iterator pointing to the beginning of the output sequence + /// + /// \return Iterator to the end of the output sequence which has been written + /// + //////////////////////////////////////////////////////////// + template static Out toUtf8( In begin, In end, Out output ); + + //////////////////////////////////////////////////////////// + /// \brief Convert a UTF-8 characters range to UTF-16 + /// + /// \param begin Iterator pointing to the beginning of the input sequence + /// \param end Iterator pointing to the end of the input sequence + /// \param output Iterator pointing to the beginning of the output sequence + /// + /// \return Iterator to the end of the output sequence which has been written + /// + //////////////////////////////////////////////////////////// + template static Out ToUtf16( In begin, In end, Out output ); + + //////////////////////////////////////////////////////////// + /// \brief Convert a UTF-8 characters range to UTF-32 + /// + /// \param begin Iterator pointing to the beginning of the input sequence + /// \param end Iterator pointing to the end of the input sequence + /// \param output Iterator pointing to the beginning of the output sequence + /// + /// \return Iterator to the end of the output sequence which has been written + /// + //////////////////////////////////////////////////////////// + template static Out ToUtf32( In begin, In end, Out output ); +}; + +//////////////////////////////////////////////////////////// +/// \brief Specialization of the Utf template for UTF-16 +/// +//////////////////////////////////////////////////////////// +template <> class Utf<16> { + public: + //////////////////////////////////////////////////////////// + /// \brief Decode a single UTF-16 character + /// + /// Decoding a character means finding its unique 32-bits + /// code (called the codepoint) in the Unicode standard. + /// + /// \param begin Iterator pointing to the beginning of the input sequence + /// \param end Iterator pointing to the end of the input sequence + /// \param output Codepoint of the decoded UTF-16 character + /// \param replacement Replacement character to use in case the UTF-8 sequence is invalid + /// + /// \return Iterator pointing to one past the last read element of the input sequence + /// + //////////////////////////////////////////////////////////// + template + static In Decode( In begin, In end, Uint32& output, Uint32 replacement = 0 ); + + //////////////////////////////////////////////////////////// + /// \brief Encode a single UTF-16 character + /// + /// Encoding a character means converting a unique 32-bits + /// code (called the codepoint) in the target encoding, UTF-16. + /// + /// \param input Codepoint to encode as UTF-16 + /// \param output Iterator pointing to the beginning of the output sequence + /// \param replacement Replacement for characters not convertible to UTF-16 (use 0 to skip them) + /// + /// \return Iterator to the end of the output sequence which has been written + /// + //////////////////////////////////////////////////////////// + template static Out Encode( Uint32 input, Out output, Uint16 replacement = 0 ); + + //////////////////////////////////////////////////////////// + /// \brief Advance to the next UTF-16 character + /// + /// This function is necessary for multi-elements encodings, as + /// a single character may use more than 1 storage element. + /// + /// \param begin Iterator pointing to the beginning of the input sequence + /// \param end Iterator pointing to the end of the input sequence + /// + /// \return Iterator pointing to one past the last read element of the input sequence + /// + //////////////////////////////////////////////////////////// + template static In Next( In begin, In end ); + + //////////////////////////////////////////////////////////// + /// \brief Count the number of characters of a UTF-16 sequence + /// + /// This function is necessary for multi-elements encodings, as + /// a single character may use more than 1 storage element, thus the + /// total size can be different from (begin - end). + /// + /// \param begin Iterator pointing to the beginning of the input sequence + /// \param end Iterator pointing to the end of the input sequence + /// + /// \return Iterator pointing to one past the last read element of the input sequence + /// + //////////////////////////////////////////////////////////// + template static std::size_t Count( In begin, In end ); + + //////////////////////////////////////////////////////////// + /// \brief Convert an ANSI characters range to UTF-16 + /// + /// The current global locale will be used by default, unless you + /// pass a custom one in the \a locale parameter. + /// + /// \param begin Iterator pointing to the beginning of the input sequence + /// \param end Iterator pointing to the end of the input sequence + /// \param output Iterator pointing to the beginning of the output sequence + /// \param locale Locale to use for conversion + /// + /// \return Iterator to the end of the output sequence which has been written + /// + //////////////////////////////////////////////////////////// + template + static Out FromAnsi( In begin, In end, Out output, const std::locale& locale = std::locale() ); + + //////////////////////////////////////////////////////////// + /// \brief Convert a wide characters range to UTF-16 + /// + /// \param begin Iterator pointing to the beginning of the input sequence + /// \param end Iterator pointing to the end of the input sequence + /// \param output Iterator pointing to the beginning of the output sequence + /// + /// \return Iterator to the end of the output sequence which has been written + /// + //////////////////////////////////////////////////////////// + template static Out FromWide( In begin, In end, Out output ); + + //////////////////////////////////////////////////////////// + /// \brief Convert a latin-1 (ISO-5589-1) characters range to UTF-16 + /// + /// \param begin Iterator pointing to the beginning of the input sequence + /// \param end Iterator pointing to the end of the input sequence + /// \param output Iterator pointing to the beginning of the output sequence + /// \param locale Locale to use for conversion + /// + /// \return Iterator to the end of the output sequence which has been written + /// + //////////////////////////////////////////////////////////// + template static Out FromLatin1( In begin, In end, Out output ); + + //////////////////////////////////////////////////////////// + /// \brief Convert an UTF-16 characters range to ANSI characters + /// + /// The current global locale will be used by default, unless you + /// pass a custom one in the \a locale parameter. + /// + /// \param begin Iterator pointing to the beginning of the input sequence + /// \param end Iterator pointing to the end of the input sequence + /// \param output Iterator pointing to the beginning of the output sequence + /// \param replacement Replacement for characters not convertible to ANSI (use 0 to skip them) + /// \param locale Locale to use for conversion + /// + /// \return Iterator to the end of the output sequence which has been written + /// + //////////////////////////////////////////////////////////// + template + static Out ToAnsi( In begin, In end, Out output, char replacement = 0, + const std::locale& locale = std::locale() ); + +#ifndef EFSW_NO_WIDECHAR + //////////////////////////////////////////////////////////// + /// \brief Convert an UTF-16 characters range to wide characters + /// + /// \param begin Iterator pointing to the beginning of the input sequence + /// \param end Iterator pointing to the end of the input sequence + /// \param output Iterator pointing to the beginning of the output sequence + /// \param replacement Replacement for characters not convertible to wide (use 0 to skip them) + /// + /// \return Iterator to the end of the output sequence which has been written + /// + //////////////////////////////////////////////////////////// + template + static Out ToWide( In begin, In end, Out output, wchar_t replacement = 0 ); +#endif + + //////////////////////////////////////////////////////////// + /// \brief Convert an UTF-16 characters range to latin-1 (ISO-5589-1) characters + /// + /// \param begin Iterator pointing to the beginning of the input sequence + /// \param end Iterator pointing to the end of the input sequence + /// \param output Iterator pointing to the beginning of the output sequence + /// \param replacement Replacement for characters not convertible to wide (use 0 to skip them) + /// + /// \return Iterator to the end of the output sequence which has been written + /// + //////////////////////////////////////////////////////////// + template + static Out ToLatin1( In begin, In end, Out output, char replacement = 0 ); + + //////////////////////////////////////////////////////////// + /// \brief Convert a UTF-16 characters range to UTF-8 + /// + /// \param begin Iterator pointing to the beginning of the input sequence + /// \param end Iterator pointing to the end of the input sequence + /// \param output Iterator pointing to the beginning of the output sequence + /// + /// \return Iterator to the end of the output sequence which has been written + /// + //////////////////////////////////////////////////////////// + template static Out toUtf8( In begin, In end, Out output ); + + //////////////////////////////////////////////////////////// + /// \brief Convert a UTF-16 characters range to UTF-16 + /// + /// This functions does nothing more than a direct copy; + /// it is defined only to provide the same interface as other + /// specializations of the efsw::Utf<> template, and allow + /// generic code to be written on top of it. + /// + /// \param begin Iterator pointing to the beginning of the input sequence + /// \param end Iterator pointing to the end of the input sequence + /// \param output Iterator pointing to the beginning of the output sequence + /// + /// \return Iterator to the end of the output sequence which has been written + /// + //////////////////////////////////////////////////////////// + template static Out ToUtf16( In begin, In end, Out output ); + + //////////////////////////////////////////////////////////// + /// \brief Convert a UTF-16 characters range to UTF-32 + /// + /// \param begin Iterator pointing to the beginning of the input sequence + /// \param end Iterator pointing to the end of the input sequence + /// \param output Iterator pointing to the beginning of the output sequence + /// + /// \return Iterator to the end of the output sequence which has been written + /// + //////////////////////////////////////////////////////////// + template static Out ToUtf32( In begin, In end, Out output ); +}; + +//////////////////////////////////////////////////////////// +/// \brief Specialization of the Utf template for UTF-32 +/// +//////////////////////////////////////////////////////////// +template <> class Utf<32> { + public: + //////////////////////////////////////////////////////////// + /// \brief Decode a single UTF-32 character + /// + /// Decoding a character means finding its unique 32-bits + /// code (called the codepoint) in the Unicode standard. + /// For UTF-32, the character value is the same as the codepoint. + /// + /// \param begin Iterator pointing to the beginning of the input sequence + /// \param end Iterator pointing to the end of the input sequence + /// \param output Codepoint of the decoded UTF-32 character + /// \param replacement Replacement character to use in case the UTF-8 sequence is invalid + /// + /// \return Iterator pointing to one past the last read element of the input sequence + /// + //////////////////////////////////////////////////////////// + template + static In Decode( In begin, In end, Uint32& output, Uint32 replacement = 0 ); + + //////////////////////////////////////////////////////////// + /// \brief Encode a single UTF-32 character + /// + /// Encoding a character means converting a unique 32-bits + /// code (called the codepoint) in the target encoding, UTF-32. + /// For UTF-32, the codepoint is the same as the character value. + /// + /// \param input Codepoint to encode as UTF-32 + /// \param output Iterator pointing to the beginning of the output sequence + /// \param replacement Replacement for characters not convertible to UTF-32 (use 0 to skip them) + /// + /// \return Iterator to the end of the output sequence which has been written + /// + //////////////////////////////////////////////////////////// + template static Out Encode( Uint32 input, Out output, Uint32 replacement = 0 ); + + //////////////////////////////////////////////////////////// + /// \brief Advance to the next UTF-32 character + /// + /// This function is trivial for UTF-32, which can store + /// every character in a single storage element. + /// + /// \param begin Iterator pointing to the beginning of the input sequence + /// \param end Iterator pointing to the end of the input sequence + /// + /// \return Iterator pointing to one past the last read element of the input sequence + /// + //////////////////////////////////////////////////////////// + template static In Next( In begin, In end ); + + //////////////////////////////////////////////////////////// + /// \brief Count the number of characters of a UTF-32 sequence + /// + /// This function is trivial for UTF-32, which can store + /// every character in a single storage element. + /// + /// \param begin Iterator pointing to the beginning of the input sequence + /// \param end Iterator pointing to the end of the input sequence + /// + /// \return Iterator pointing to one past the last read element of the input sequence + /// + //////////////////////////////////////////////////////////// + template static std::size_t Count( In begin, In end ); + + //////////////////////////////////////////////////////////// + /// \brief Convert an ANSI characters range to UTF-32 + /// + /// The current global locale will be used by default, unless you + /// pass a custom one in the \a locale parameter. + /// + /// \param begin Iterator pointing to the beginning of the input sequence + /// \param end Iterator pointing to the end of the input sequence + /// \param output Iterator pointing to the beginning of the output sequence + /// \param locale Locale to use for conversion + /// + /// \return Iterator to the end of the output sequence which has been written + /// + //////////////////////////////////////////////////////////// + template + static Out FromAnsi( In begin, In end, Out output, const std::locale& locale = std::locale() ); + + //////////////////////////////////////////////////////////// + /// \brief Convert a wide characters range to UTF-32 + /// + /// \param begin Iterator pointing to the beginning of the input sequence + /// \param end Iterator pointing to the end of the input sequence + /// \param output Iterator pointing to the beginning of the output sequence + /// + /// \return Iterator to the end of the output sequence which has been written + /// + //////////////////////////////////////////////////////////// + template static Out FromWide( In begin, In end, Out output ); + + //////////////////////////////////////////////////////////// + /// \brief Convert a latin-1 (ISO-5589-1) characters range to UTF-32 + /// + /// \param begin Iterator pointing to the beginning of the input sequence + /// \param end Iterator pointing to the end of the input sequence + /// \param output Iterator pointing to the beginning of the output sequence + /// \param locale Locale to use for conversion + /// + /// \return Iterator to the end of the output sequence which has been written + /// + //////////////////////////////////////////////////////////// + template static Out FromLatin1( In begin, In end, Out output ); + + //////////////////////////////////////////////////////////// + /// \brief Convert an UTF-32 characters range to ANSI characters + /// + /// The current global locale will be used by default, unless you + /// pass a custom one in the \a locale parameter. + /// + /// \param begin Iterator pointing to the beginning of the input sequence + /// \param end Iterator pointing to the end of the input sequence + /// \param output Iterator pointing to the beginning of the output sequence + /// \param replacement Replacement for characters not convertible to ANSI (use 0 to skip them) + /// \param locale Locale to use for conversion + /// + /// \return Iterator to the end of the output sequence which has been written + /// + //////////////////////////////////////////////////////////// + template + static Out ToAnsi( In begin, In end, Out output, char replacement = 0, + const std::locale& locale = std::locale() ); + +#ifndef EFSW_NO_WIDECHAR + //////////////////////////////////////////////////////////// + /// \brief Convert an UTF-32 characters range to wide characters + /// + /// \param begin Iterator pointing to the beginning of the input sequence + /// \param end Iterator pointing to the end of the input sequence + /// \param output Iterator pointing to the beginning of the output sequence + /// \param replacement Replacement for characters not convertible to wide (use 0 to skip them) + /// + /// \return Iterator to the end of the output sequence which has been written + /// + //////////////////////////////////////////////////////////// + template + static Out ToWide( In begin, In end, Out output, wchar_t replacement = 0 ); +#endif + + //////////////////////////////////////////////////////////// + /// \brief Convert an UTF-16 characters range to latin-1 (ISO-5589-1) characters + /// + /// \param begin Iterator pointing to the beginning of the input sequence + /// \param end Iterator pointing to the end of the input sequence + /// \param output Iterator pointing to the beginning of the output sequence + /// \param replacement Replacement for characters not convertible to wide (use 0 to skip them) + /// + /// \return Iterator to the end of the output sequence which has been written + /// + //////////////////////////////////////////////////////////// + template + static Out ToLatin1( In begin, In end, Out output, char replacement = 0 ); + + //////////////////////////////////////////////////////////// + /// \brief Convert a UTF-32 characters range to UTF-8 + /// + /// \param begin Iterator pointing to the beginning of the input sequence + /// \param end Iterator pointing to the end of the input sequence + /// \param output Iterator pointing to the beginning of the output sequence + /// + /// \return Iterator to the end of the output sequence which has been written + /// + //////////////////////////////////////////////////////////// + template static Out toUtf8( In begin, In end, Out output ); + + //////////////////////////////////////////////////////////// + /// \brief Convert a UTF-32 characters range to UTF-16 + /// + /// \param begin Iterator pointing to the beginning of the input sequence + /// \param end Iterator pointing to the end of the input sequence + /// \param output Iterator pointing to the beginning of the output sequence + /// + /// \return Iterator to the end of the output sequence which has been written + /// + //////////////////////////////////////////////////////////// + template static Out ToUtf16( In begin, In end, Out output ); + + //////////////////////////////////////////////////////////// + /// \brief Convert a UTF-32 characters range to UTF-32 + /// + /// This functions does nothing more than a direct copy; + /// it is defined only to provide the same interface as other + /// specializations of the efsw::Utf<> template, and allow + /// generic code to be written on top of it. + /// + /// \param begin Iterator pointing to the beginning of the input sequence + /// \param end Iterator pointing to the end of the input sequence + /// \param output Iterator pointing to the beginning of the output sequence + /// + /// \return Iterator to the end of the output sequence which has been written + /// + //////////////////////////////////////////////////////////// + template static Out ToUtf32( In begin, In end, Out output ); + + //////////////////////////////////////////////////////////// + /// \brief Decode a single ANSI character to UTF-32 + /// + /// This function does not exist in other specializations + /// of efsw::Utf<>, it is defined for convenience (it is used by + /// several other conversion functions). + /// + /// \param input Input ANSI character + /// \param locale Locale to use for conversion + /// + /// \return Converted character + /// + //////////////////////////////////////////////////////////// + template + static Uint32 DecodeAnsi( In input, const std::locale& locale = std::locale() ); + + //////////////////////////////////////////////////////////// + /// \brief Decode a single wide character to UTF-32 + /// + /// This function does not exist in other specializations + /// of efsw::Utf<>, it is defined for convenience (it is used by + /// several other conversion functions). + /// + /// \param input Input wide character + /// + /// \return Converted character + /// + //////////////////////////////////////////////////////////// + template static Uint32 DecodeWide( In input ); + + //////////////////////////////////////////////////////////// + /// \brief Encode a single UTF-32 character to ANSI + /// + /// This function does not exist in other specializations + /// of efsw::Utf<>, it is defined for convenience (it is used by + /// several other conversion functions). + /// + /// \param codepoint Iterator pointing to the beginning of the input sequence + /// \param output Iterator pointing to the beginning of the output sequence + /// \param replacement Replacement if the input character is not convertible to ANSI (use 0 to + /// skip it) \param locale Locale to use for conversion + /// + /// \return Iterator to the end of the output sequence which has been written + /// + //////////////////////////////////////////////////////////// + template + static Out EncodeAnsi( Uint32 codepoint, Out output, char replacement = 0, + const std::locale& locale = std::locale() ); + +#ifndef EFSW_NO_WIDECHAR + //////////////////////////////////////////////////////////// + /// \brief Encode a single UTF-32 character to wide + /// + /// This function does not exist in other specializations + /// of efsw::Utf<>, it is defined for convenience (it is used by + /// several other conversion functions). + /// + /// \param codepoint Iterator pointing to the beginning of the input sequence + /// \param output Iterator pointing to the beginning of the output sequence + /// \param replacement Replacement if the input character is not convertible to wide (use 0 to + /// skip it) + /// + /// \return Iterator to the end of the output sequence which has been written + /// + //////////////////////////////////////////////////////////// + template + static Out EncodeWide( Uint32 codepoint, Out output, wchar_t replacement = 0 ); +#endif +}; + +#include "Utf.inl" + +// Make typedefs to get rid of the template syntax +typedef Utf<8> Utf8; +typedef Utf<16> Utf16; +typedef Utf<32> Utf32; + +} // namespace efsw +#endif + +//////////////////////////////////////////////////////////// +/// \class efsw::Utf +/// \ingroup system +/// +/// Utility class providing generic functions for UTF conversions. +/// +/// efsw::Utf is a low-level, generic interface for counting, iterating, +/// encoding and decoding Unicode characters and strings. It is able +/// to handle ANSI, wide, UTF-8, UTF-16 and UTF-32 encodings. +/// +/// efsw::Utf functions are all static, these classes are not meant to +/// be instanciated. All the functions are template, so that you +/// can use any character / string type for a given encoding. +/// +/// It has 3 specializations: +/// \li efsw::Utf<8> (typedef'd to efsw::Utf8) +/// \li efsw::Utf<16> (typedef'd to efsw::Utf16) +/// \li efsw::Utf<32> (typedef'd to efsw::Utf32) +/// +//////////////////////////////////////////////////////////// diff --git a/src/3rdParty/efsw/Utf.inl b/src/3rdParty/efsw/Utf.inl old mode 100755 new mode 100644 index 5b6c2e0..5c9d7a3 --- a/src/3rdParty/efsw/Utf.inl +++ b/src/3rdParty/efsw/Utf.inl @@ -1,576 +1,576 @@ -// References : -// http://www.unicode.org/ -// http://www.unicode.org/Public/PROGRAMS/CVTUTF/ConvertUTF.c -// http://www.unicode.org/Public/PROGRAMS/CVTUTF/ConvertUTF.h -// http://people.w3.org/rishida/scripts/uniview/conversion -//////////////////////////////////////////////////////////// - -template In Utf<8>::Decode( In begin, In end, Uint32& output, Uint32 replacement ) { - // Some useful precomputed data - static const int trailing[256] = { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5 }; - static const Uint32 offsets[6] = { 0x00000000, 0x00003080, 0x000E2080, - 0x03C82080, 0xFA082080, 0x82082080 }; - - // Decode the character - int trailingBytes = trailing[static_cast( *begin )]; - if ( begin + trailingBytes < end ) { - output = 0; - switch ( trailingBytes ) { - case 5: - output += static_cast( *begin++ ); - output <<= 6; - case 4: - output += static_cast( *begin++ ); - output <<= 6; - case 3: - output += static_cast( *begin++ ); - output <<= 6; - case 2: - output += static_cast( *begin++ ); - output <<= 6; - case 1: - output += static_cast( *begin++ ); - output <<= 6; - case 0: - output += static_cast( *begin++ ); - } - output -= offsets[trailingBytes]; - } else { - // Incomplete character - begin = end; - output = replacement; - } - - return begin; -} - -template Out Utf<8>::Encode( Uint32 input, Out output, Uint8 replacement ) { - // Some useful precomputed data - static const Uint8 firstBytes[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC }; - - // Encode the character - if ( ( input > 0x0010FFFF ) || ( ( input >= 0xD800 ) && ( input <= 0xDBFF ) ) ) { - // Invalid character - if ( replacement ) - *output++ = replacement; - } else { - // Valid character - - // Get the number of bytes to write - int bytesToWrite = 1; - if ( input < 0x80 ) - bytesToWrite = 1; - else if ( input < 0x800 ) - bytesToWrite = 2; - else if ( input < 0x10000 ) - bytesToWrite = 3; - else if ( input <= 0x0010FFFF ) - bytesToWrite = 4; - - // Extract the bytes to write - Uint8 bytes[4]; - switch ( bytesToWrite ) { - case 4: - bytes[3] = static_cast( ( input | 0x80 ) & 0xBF ); - input >>= 6; - case 3: - bytes[2] = static_cast( ( input | 0x80 ) & 0xBF ); - input >>= 6; - case 2: - bytes[1] = static_cast( ( input | 0x80 ) & 0xBF ); - input >>= 6; - case 1: - bytes[0] = static_cast( input | firstBytes[bytesToWrite] ); - } - - // Add them to the output - const Uint8* currentByte = bytes; - switch ( bytesToWrite ) { - case 4: - *output++ = *currentByte++; - case 3: - *output++ = *currentByte++; - case 2: - *output++ = *currentByte++; - case 1: - *output++ = *currentByte++; - } - } - - return output; -} - -template In Utf<8>::Next( In begin, In end ) { - Uint32 codepoint; - return Decode( begin, end, codepoint ); -} - -template std::size_t Utf<8>::Count( In begin, In end ) { - std::size_t length = 0; - while ( begin < end ) { - begin = Next( begin, end ); - ++length; - } - - return length; -} - -template -Out Utf<8>::FromAnsi( In begin, In end, Out output, const std::locale& locale ) { - while ( begin < end ) { - Uint32 codepoint = Utf<32>::DecodeAnsi( *begin++, locale ); - output = Encode( codepoint, output ); - } - - return output; -} - -template Out Utf<8>::FromWide( In begin, In end, Out output ) { - while ( begin < end ) { - Uint32 codepoint = Utf<32>::DecodeWide( *begin++ ); - output = Encode( codepoint, output ); - } - - return output; -} - -template Out Utf<8>::FromLatin1( In begin, In end, Out output ) { - // Latin-1 is directly compatible with Unicode encodings, - // and can thus be treated as (a sub-range of) UTF-32 - while ( begin < end ) - output = Encode( *begin++, output ); - - return output; -} - -template -Out Utf<8>::ToAnsi( In begin, In end, Out output, char replacement, const std::locale& locale ) { - while ( begin < end ) { - Uint32 codepoint; - begin = Decode( begin, end, codepoint ); - output = Utf<32>::EncodeAnsi( codepoint, output, replacement, locale ); - } - - return output; -} - -#ifndef EFSW_NO_WIDECHAR -template -Out Utf<8>::ToWide( In begin, In end, Out output, wchar_t replacement ) { - while ( begin < end ) { - Uint32 codepoint; - begin = Decode( begin, end, codepoint ); - output = Utf<32>::EncodeWide( codepoint, output, replacement ); - } - - return output; -} -#endif - -template -Out Utf<8>::ToLatin1( In begin, In end, Out output, char replacement ) { - // Latin-1 is directly compatible with Unicode encodings, - // and can thus be treated as (a sub-range of) UTF-32 - while ( begin < end ) { - Uint32 codepoint; - begin = Decode( begin, end, codepoint ); - *output++ = codepoint < 256 ? static_cast( codepoint ) : replacement; - } - - return output; -} - -template Out Utf<8>::toUtf8( In begin, In end, Out output ) { - while ( begin < end ) - *output++ = *begin++; - - return output; -} - -template Out Utf<8>::ToUtf16( In begin, In end, Out output ) { - while ( begin < end ) { - Uint32 codepoint; - begin = Decode( begin, end, codepoint ); - output = Utf<16>::Encode( codepoint, output ); - } - - return output; -} - -template Out Utf<8>::ToUtf32( In begin, In end, Out output ) { - while ( begin < end ) { - Uint32 codepoint; - begin = Decode( begin, end, codepoint ); - *output++ = codepoint; - } - - return output; -} - -template In Utf<16>::Decode( In begin, In end, Uint32& output, Uint32 replacement ) { - Uint16 first = *begin++; - - // If it's a surrogate pair, first convert to a single UTF-32 character - if ( ( first >= 0xD800 ) && ( first <= 0xDBFF ) ) { - if ( begin < end ) { - Uint32 second = *begin++; - if ( ( second >= 0xDC00 ) && ( second <= 0xDFFF ) ) { - // The second element is valid: convert the two elements to a UTF-32 character - output = static_cast( ( ( first - 0xD800 ) << 10 ) + ( second - 0xDC00 ) + - 0x0010000 ); - } else { - // Invalid character - output = replacement; - } - } else { - // Invalid character - begin = end; - output = replacement; - } - } else { - // We can make a direct copy - output = first; - } - - return begin; -} - -template Out Utf<16>::Encode( Uint32 input, Out output, Uint16 replacement ) { - if ( input < 0xFFFF ) { - // The character can be copied directly, we just need to check if it's in the valid range - if ( ( input >= 0xD800 ) && ( input <= 0xDFFF ) ) { - // Invalid character (this range is reserved) - if ( replacement ) - *output++ = replacement; - } else { - // Valid character directly convertible to a single UTF-16 character - *output++ = static_cast( input ); - } - } else if ( input > 0x0010FFFF ) { - // Invalid character (greater than the maximum unicode value) - if ( replacement ) - *output++ = replacement; - } else { - // The input character will be converted to two UTF-16 elements - input -= 0x0010000; - *output++ = static_cast( ( input >> 10 ) + 0xD800 ); - *output++ = static_cast( ( input & 0x3FFUL ) + 0xDC00 ); - } - - return output; -} - -template In Utf<16>::Next( In begin, In end ) { - Uint32 codepoint; - return Decode( begin, end, codepoint ); -} - -template std::size_t Utf<16>::Count( In begin, In end ) { - std::size_t length = 0; - while ( begin < end ) { - begin = Next( begin, end ); - ++length; - } - - return length; -} - -template -Out Utf<16>::FromAnsi( In begin, In end, Out output, const std::locale& locale ) { - while ( begin < end ) { - Uint32 codepoint = Utf<32>::DecodeAnsi( *begin++, locale ); - output = Encode( codepoint, output ); - } - - return output; -} - -template Out Utf<16>::FromWide( In begin, In end, Out output ) { - while ( begin < end ) { - Uint32 codepoint = Utf<32>::DecodeWide( *begin++ ); - output = Encode( codepoint, output ); - } - - return output; -} - -template Out Utf<16>::FromLatin1( In begin, In end, Out output ) { - // Latin-1 is directly compatible with Unicode encodings, - // and can thus be treated as (a sub-range of) UTF-32 - while ( begin < end ) - *output++ = *begin++; - - return output; -} - -template -Out Utf<16>::ToAnsi( In begin, In end, Out output, char replacement, const std::locale& locale ) { - while ( begin < end ) { - Uint32 codepoint; - begin = Decode( begin, end, codepoint ); - output = Utf<32>::EncodeAnsi( codepoint, output, replacement, locale ); - } - - return output; -} - -#ifndef EFSW_NO_WIDECHAR -template -Out Utf<16>::ToWide( In begin, In end, Out output, wchar_t replacement ) { - while ( begin < end ) { - Uint32 codepoint; - begin = Decode( begin, end, codepoint ); - output = Utf<32>::EncodeWide( codepoint, output, replacement ); - } - - return output; -} -#endif - -template -Out Utf<16>::ToLatin1( In begin, In end, Out output, char replacement ) { - // Latin-1 is directly compatible with Unicode encodings, - // and can thus be treated as (a sub-range of) UTF-32 - while ( begin < end ) { - *output++ = *begin < 256 ? static_cast( *begin ) : replacement; - begin++; - } - - return output; -} - -template Out Utf<16>::toUtf8( In begin, In end, Out output ) { - while ( begin < end ) { - Uint32 codepoint; - begin = Decode( begin, end, codepoint ); - output = Utf<8>::Encode( codepoint, output ); - } - - return output; -} - -template Out Utf<16>::ToUtf16( In begin, In end, Out output ) { - while ( begin < end ) - *output++ = *begin++; - - return output; -} - -template Out Utf<16>::ToUtf32( In begin, In end, Out output ) { - while ( begin < end ) { - Uint32 codepoint; - begin = Decode( begin, end, codepoint ); - *output++ = codepoint; - } - - return output; -} - -template In Utf<32>::Decode( In begin, In /*end*/, Uint32& output, Uint32 ) { - output = *begin++; - return begin; -} - -template Out Utf<32>::Encode( Uint32 input, Out output, Uint32 /*replacement*/ ) { - *output++ = input; - return output; -} - -template In Utf<32>::Next( In begin, In /*end*/ ) { - return ++begin; -} - -template std::size_t Utf<32>::Count( In begin, In end ) { - return begin - end; -} - -template -Out Utf<32>::FromAnsi( In begin, In end, Out output, const std::locale& locale ) { - while ( begin < end ) - *output++ = DecodeAnsi( *begin++, locale ); - - return output; -} - -template Out Utf<32>::FromWide( In begin, In end, Out output ) { - while ( begin < end ) - *output++ = DecodeWide( *begin++ ); - - return output; -} - -template Out Utf<32>::FromLatin1( In begin, In end, Out output ) { - // Latin-1 is directly compatible with Unicode encodings, - // and can thus be treated as (a sub-range of) UTF-32 - while ( begin < end ) - *output++ = *begin++; - - return output; -} - -template -Out Utf<32>::ToAnsi( In begin, In end, Out output, char replacement, const std::locale& locale ) { - while ( begin < end ) - output = EncodeAnsi( *begin++, output, replacement, locale ); - - return output; -} - -#ifndef EFSW_NO_WIDECHAR -template -Out Utf<32>::ToWide( In begin, In end, Out output, wchar_t replacement ) { - while ( begin < end ) - output = EncodeWide( *begin++, output, replacement ); - - return output; -} -#endif - -template -Out Utf<32>::ToLatin1( In begin, In end, Out output, char replacement ) { - // Latin-1 is directly compatible with Unicode encodings, - // and can thus be treated as (a sub-range of) UTF-32 - while ( begin < end ) { - *output++ = *begin < 256 ? static_cast( *begin ) : replacement; - begin++; - } - - return output; -} - -template Out Utf<32>::toUtf8( In begin, In end, Out output ) { - while ( begin < end ) - output = Utf<8>::Encode( *begin++, output ); - - return output; -} - -template Out Utf<32>::ToUtf16( In begin, In end, Out output ) { - while ( begin < end ) - output = Utf<16>::Encode( *begin++, output ); - - return output; -} - -template Out Utf<32>::ToUtf32( In begin, In end, Out output ) { - while ( begin < end ) - *output++ = *begin++; - - return output; -} - -template Uint32 Utf<32>::DecodeAnsi( In input, const std::locale& locale ) { - // On Windows, gcc's standard library (glibc++) has almost - // no support for Unicode stuff. As a consequence, in this - // context we can only use the default locale and ignore - // the one passed as parameter. - -#if EFSW_PLATFORM == EFSW_PLATFORM_WIN && /* if Windows ... */ \ - ( defined( __GLIBCPP__ ) || \ - defined( __GLIBCXX__ ) ) && /* ... and standard library is glibc++ ... */ \ - !( defined( __SGI_STL_PORT ) || \ - defined( _STLPORT_VERSION ) ) /* ... and STLPort is not used on top of it */ - - wchar_t character = 0; - mbtowc( &character, &input, 1 ); - return static_cast( character ); - -#else -// Get the facet of the locale which deals with character conversion -#ifndef EFSW_NO_WIDECHAR - const std::ctype& facet = std::use_facet>( locale ); -#else - const std::ctype& facet = std::use_facet>( locale ); -#endif - - // Use the facet to convert each character of the input string - return static_cast( facet.widen( input ) ); - -#endif -} - -template Uint32 Utf<32>::DecodeWide( In input ) { - // The encoding of wide characters is not well defined and is left to the system; - // however we can safely assume that it is UCS-2 on Windows and - // UCS-4 on Unix systems. - // In both cases, a simple copy is enough (UCS-2 is a subset of UCS-4, - // and UCS-4 *is* UTF-32). - - return input; -} - -template -Out Utf<32>::EncodeAnsi( Uint32 codepoint, Out output, char replacement, - const std::locale& locale ) { - // On Windows, gcc's standard library (glibc++) has almost - // no support for Unicode stuff. As a consequence, in this - // context we can only use the default locale and ignore - // the one passed as parameter. - -#if EFSW_PLATFORM == EFSW_PLATFORM_WIN && /* if Windows ... */ \ - ( defined( __GLIBCPP__ ) || \ - defined( __GLIBCXX__ ) ) && /* ... and standard library is glibc++ ... */ \ - !( defined( __SGI_STL_PORT ) || \ - defined( _STLPORT_VERSION ) ) /* ... and STLPort is not used on top of it */ - - char character = 0; - if ( wctomb( &character, static_cast( codepoint ) ) >= 0 ) - *output++ = character; - else if ( replacement ) - *output++ = replacement; - - return output; - -#else -// Get the facet of the locale which deals with character conversion -#ifndef EFSW_NO_WIDECHAR - const std::ctype& facet = std::use_facet>( locale ); -#else - const std::ctype& facet = std::use_facet>( locale ); -#endif - - // Use the facet to convert each character of the input string - *output++ = facet.narrow( static_cast( codepoint ), replacement ); - - return output; - -#endif -} - -#ifndef EFSW_NO_WIDECHAR -template -Out Utf<32>::EncodeWide( Uint32 codepoint, Out output, wchar_t replacement ) { - // The encoding of wide characters is not well defined and is left to the system; - // however we can safely assume that it is UCS-2 on Windows and - // UCS-4 on Unix systems. - // For UCS-2 we need to check if the source characters fits in (UCS-2 is a subset of UCS-4). - // For UCS-4 we can do a direct copy (UCS-4 *is* UTF-32). - - switch ( sizeof( wchar_t ) ) { - case 4: { - *output++ = static_cast( codepoint ); - break; - } - - default: { - if ( ( codepoint <= 0xFFFF ) && ( ( codepoint < 0xD800 ) || ( codepoint > 0xDFFF ) ) ) { - *output++ = static_cast( codepoint ); - } else if ( replacement ) { - *output++ = replacement; - } - break; - } - } - - return output; -} -#endif +// References : +// http://www.unicode.org/ +// http://www.unicode.org/Public/PROGRAMS/CVTUTF/ConvertUTF.c +// http://www.unicode.org/Public/PROGRAMS/CVTUTF/ConvertUTF.h +// http://people.w3.org/rishida/scripts/uniview/conversion +//////////////////////////////////////////////////////////// + +template In Utf<8>::Decode( In begin, In end, Uint32& output, Uint32 replacement ) { + // Some useful precomputed data + static const int trailing[256] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5 }; + static const Uint32 offsets[6] = { 0x00000000, 0x00003080, 0x000E2080, + 0x03C82080, 0xFA082080, 0x82082080 }; + + // Decode the character + int trailingBytes = trailing[static_cast( *begin )]; + if ( begin + trailingBytes < end ) { + output = 0; + switch ( trailingBytes ) { + case 5: + output += static_cast( *begin++ ); + output <<= 6; + case 4: + output += static_cast( *begin++ ); + output <<= 6; + case 3: + output += static_cast( *begin++ ); + output <<= 6; + case 2: + output += static_cast( *begin++ ); + output <<= 6; + case 1: + output += static_cast( *begin++ ); + output <<= 6; + case 0: + output += static_cast( *begin++ ); + } + output -= offsets[trailingBytes]; + } else { + // Incomplete character + begin = end; + output = replacement; + } + + return begin; +} + +template Out Utf<8>::Encode( Uint32 input, Out output, Uint8 replacement ) { + // Some useful precomputed data + static const Uint8 firstBytes[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC }; + + // Encode the character + if ( ( input > 0x0010FFFF ) || ( ( input >= 0xD800 ) && ( input <= 0xDBFF ) ) ) { + // Invalid character + if ( replacement ) + *output++ = replacement; + } else { + // Valid character + + // Get the number of bytes to write + int bytesToWrite = 1; + if ( input < 0x80 ) + bytesToWrite = 1; + else if ( input < 0x800 ) + bytesToWrite = 2; + else if ( input < 0x10000 ) + bytesToWrite = 3; + else if ( input <= 0x0010FFFF ) + bytesToWrite = 4; + + // Extract the bytes to write + Uint8 bytes[4]; + switch ( bytesToWrite ) { + case 4: + bytes[3] = static_cast( ( input | 0x80 ) & 0xBF ); + input >>= 6; + case 3: + bytes[2] = static_cast( ( input | 0x80 ) & 0xBF ); + input >>= 6; + case 2: + bytes[1] = static_cast( ( input | 0x80 ) & 0xBF ); + input >>= 6; + case 1: + bytes[0] = static_cast( input | firstBytes[bytesToWrite] ); + } + + // Add them to the output + const Uint8* currentByte = bytes; + switch ( bytesToWrite ) { + case 4: + *output++ = *currentByte++; + case 3: + *output++ = *currentByte++; + case 2: + *output++ = *currentByte++; + case 1: + *output++ = *currentByte++; + } + } + + return output; +} + +template In Utf<8>::Next( In begin, In end ) { + Uint32 codepoint; + return Decode( begin, end, codepoint ); +} + +template std::size_t Utf<8>::Count( In begin, In end ) { + std::size_t length = 0; + while ( begin < end ) { + begin = Next( begin, end ); + ++length; + } + + return length; +} + +template +Out Utf<8>::FromAnsi( In begin, In end, Out output, const std::locale& locale ) { + while ( begin < end ) { + Uint32 codepoint = Utf<32>::DecodeAnsi( *begin++, locale ); + output = Encode( codepoint, output ); + } + + return output; +} + +template Out Utf<8>::FromWide( In begin, In end, Out output ) { + while ( begin < end ) { + Uint32 codepoint = Utf<32>::DecodeWide( *begin++ ); + output = Encode( codepoint, output ); + } + + return output; +} + +template Out Utf<8>::FromLatin1( In begin, In end, Out output ) { + // Latin-1 is directly compatible with Unicode encodings, + // and can thus be treated as (a sub-range of) UTF-32 + while ( begin < end ) + output = Encode( *begin++, output ); + + return output; +} + +template +Out Utf<8>::ToAnsi( In begin, In end, Out output, char replacement, const std::locale& locale ) { + while ( begin < end ) { + Uint32 codepoint; + begin = Decode( begin, end, codepoint ); + output = Utf<32>::EncodeAnsi( codepoint, output, replacement, locale ); + } + + return output; +} + +#ifndef EFSW_NO_WIDECHAR +template +Out Utf<8>::ToWide( In begin, In end, Out output, wchar_t replacement ) { + while ( begin < end ) { + Uint32 codepoint; + begin = Decode( begin, end, codepoint ); + output = Utf<32>::EncodeWide( codepoint, output, replacement ); + } + + return output; +} +#endif + +template +Out Utf<8>::ToLatin1( In begin, In end, Out output, char replacement ) { + // Latin-1 is directly compatible with Unicode encodings, + // and can thus be treated as (a sub-range of) UTF-32 + while ( begin < end ) { + Uint32 codepoint; + begin = Decode( begin, end, codepoint ); + *output++ = codepoint < 256 ? static_cast( codepoint ) : replacement; + } + + return output; +} + +template Out Utf<8>::toUtf8( In begin, In end, Out output ) { + while ( begin < end ) + *output++ = *begin++; + + return output; +} + +template Out Utf<8>::ToUtf16( In begin, In end, Out output ) { + while ( begin < end ) { + Uint32 codepoint; + begin = Decode( begin, end, codepoint ); + output = Utf<16>::Encode( codepoint, output ); + } + + return output; +} + +template Out Utf<8>::ToUtf32( In begin, In end, Out output ) { + while ( begin < end ) { + Uint32 codepoint; + begin = Decode( begin, end, codepoint ); + *output++ = codepoint; + } + + return output; +} + +template In Utf<16>::Decode( In begin, In end, Uint32& output, Uint32 replacement ) { + Uint16 first = *begin++; + + // If it's a surrogate pair, first convert to a single UTF-32 character + if ( ( first >= 0xD800 ) && ( first <= 0xDBFF ) ) { + if ( begin < end ) { + Uint32 second = *begin++; + if ( ( second >= 0xDC00 ) && ( second <= 0xDFFF ) ) { + // The second element is valid: convert the two elements to a UTF-32 character + output = static_cast( ( ( first - 0xD800 ) << 10 ) + ( second - 0xDC00 ) + + 0x0010000 ); + } else { + // Invalid character + output = replacement; + } + } else { + // Invalid character + begin = end; + output = replacement; + } + } else { + // We can make a direct copy + output = first; + } + + return begin; +} + +template Out Utf<16>::Encode( Uint32 input, Out output, Uint16 replacement ) { + if ( input < 0xFFFF ) { + // The character can be copied directly, we just need to check if it's in the valid range + if ( ( input >= 0xD800 ) && ( input <= 0xDFFF ) ) { + // Invalid character (this range is reserved) + if ( replacement ) + *output++ = replacement; + } else { + // Valid character directly convertible to a single UTF-16 character + *output++ = static_cast( input ); + } + } else if ( input > 0x0010FFFF ) { + // Invalid character (greater than the maximum unicode value) + if ( replacement ) + *output++ = replacement; + } else { + // The input character will be converted to two UTF-16 elements + input -= 0x0010000; + *output++ = static_cast( ( input >> 10 ) + 0xD800 ); + *output++ = static_cast( ( input & 0x3FFUL ) + 0xDC00 ); + } + + return output; +} + +template In Utf<16>::Next( In begin, In end ) { + Uint32 codepoint; + return Decode( begin, end, codepoint ); +} + +template std::size_t Utf<16>::Count( In begin, In end ) { + std::size_t length = 0; + while ( begin < end ) { + begin = Next( begin, end ); + ++length; + } + + return length; +} + +template +Out Utf<16>::FromAnsi( In begin, In end, Out output, const std::locale& locale ) { + while ( begin < end ) { + Uint32 codepoint = Utf<32>::DecodeAnsi( *begin++, locale ); + output = Encode( codepoint, output ); + } + + return output; +} + +template Out Utf<16>::FromWide( In begin, In end, Out output ) { + while ( begin < end ) { + Uint32 codepoint = Utf<32>::DecodeWide( *begin++ ); + output = Encode( codepoint, output ); + } + + return output; +} + +template Out Utf<16>::FromLatin1( In begin, In end, Out output ) { + // Latin-1 is directly compatible with Unicode encodings, + // and can thus be treated as (a sub-range of) UTF-32 + while ( begin < end ) + *output++ = *begin++; + + return output; +} + +template +Out Utf<16>::ToAnsi( In begin, In end, Out output, char replacement, const std::locale& locale ) { + while ( begin < end ) { + Uint32 codepoint; + begin = Decode( begin, end, codepoint ); + output = Utf<32>::EncodeAnsi( codepoint, output, replacement, locale ); + } + + return output; +} + +#ifndef EFSW_NO_WIDECHAR +template +Out Utf<16>::ToWide( In begin, In end, Out output, wchar_t replacement ) { + while ( begin < end ) { + Uint32 codepoint; + begin = Decode( begin, end, codepoint ); + output = Utf<32>::EncodeWide( codepoint, output, replacement ); + } + + return output; +} +#endif + +template +Out Utf<16>::ToLatin1( In begin, In end, Out output, char replacement ) { + // Latin-1 is directly compatible with Unicode encodings, + // and can thus be treated as (a sub-range of) UTF-32 + while ( begin < end ) { + *output++ = *begin < 256 ? static_cast( *begin ) : replacement; + begin++; + } + + return output; +} + +template Out Utf<16>::toUtf8( In begin, In end, Out output ) { + while ( begin < end ) { + Uint32 codepoint; + begin = Decode( begin, end, codepoint ); + output = Utf<8>::Encode( codepoint, output ); + } + + return output; +} + +template Out Utf<16>::ToUtf16( In begin, In end, Out output ) { + while ( begin < end ) + *output++ = *begin++; + + return output; +} + +template Out Utf<16>::ToUtf32( In begin, In end, Out output ) { + while ( begin < end ) { + Uint32 codepoint; + begin = Decode( begin, end, codepoint ); + *output++ = codepoint; + } + + return output; +} + +template In Utf<32>::Decode( In begin, In /*end*/, Uint32& output, Uint32 ) { + output = *begin++; + return begin; +} + +template Out Utf<32>::Encode( Uint32 input, Out output, Uint32 /*replacement*/ ) { + *output++ = input; + return output; +} + +template In Utf<32>::Next( In begin, In /*end*/ ) { + return ++begin; +} + +template std::size_t Utf<32>::Count( In begin, In end ) { + return begin - end; +} + +template +Out Utf<32>::FromAnsi( In begin, In end, Out output, const std::locale& locale ) { + while ( begin < end ) + *output++ = DecodeAnsi( *begin++, locale ); + + return output; +} + +template Out Utf<32>::FromWide( In begin, In end, Out output ) { + while ( begin < end ) + *output++ = DecodeWide( *begin++ ); + + return output; +} + +template Out Utf<32>::FromLatin1( In begin, In end, Out output ) { + // Latin-1 is directly compatible with Unicode encodings, + // and can thus be treated as (a sub-range of) UTF-32 + while ( begin < end ) + *output++ = *begin++; + + return output; +} + +template +Out Utf<32>::ToAnsi( In begin, In end, Out output, char replacement, const std::locale& locale ) { + while ( begin < end ) + output = EncodeAnsi( *begin++, output, replacement, locale ); + + return output; +} + +#ifndef EFSW_NO_WIDECHAR +template +Out Utf<32>::ToWide( In begin, In end, Out output, wchar_t replacement ) { + while ( begin < end ) + output = EncodeWide( *begin++, output, replacement ); + + return output; +} +#endif + +template +Out Utf<32>::ToLatin1( In begin, In end, Out output, char replacement ) { + // Latin-1 is directly compatible with Unicode encodings, + // and can thus be treated as (a sub-range of) UTF-32 + while ( begin < end ) { + *output++ = *begin < 256 ? static_cast( *begin ) : replacement; + begin++; + } + + return output; +} + +template Out Utf<32>::toUtf8( In begin, In end, Out output ) { + while ( begin < end ) + output = Utf<8>::Encode( *begin++, output ); + + return output; +} + +template Out Utf<32>::ToUtf16( In begin, In end, Out output ) { + while ( begin < end ) + output = Utf<16>::Encode( *begin++, output ); + + return output; +} + +template Out Utf<32>::ToUtf32( In begin, In end, Out output ) { + while ( begin < end ) + *output++ = *begin++; + + return output; +} + +template Uint32 Utf<32>::DecodeAnsi( In input, const std::locale& locale ) { + // On Windows, gcc's standard library (glibc++) has almost + // no support for Unicode stuff. As a consequence, in this + // context we can only use the default locale and ignore + // the one passed as parameter. + +#if EFSW_PLATFORM == EFSW_PLATFORM_WIN && /* if Windows ... */ \ + ( defined( __GLIBCPP__ ) || \ + defined( __GLIBCXX__ ) ) && /* ... and standard library is glibc++ ... */ \ + !( defined( __SGI_STL_PORT ) || \ + defined( _STLPORT_VERSION ) ) /* ... and STLPort is not used on top of it */ + + wchar_t character = 0; + mbtowc( &character, &input, 1 ); + return static_cast( character ); + +#else +// Get the facet of the locale which deals with character conversion +#ifndef EFSW_NO_WIDECHAR + const std::ctype& facet = std::use_facet>( locale ); +#else + const std::ctype& facet = std::use_facet>( locale ); +#endif + + // Use the facet to convert each character of the input string + return static_cast( facet.widen( input ) ); + +#endif +} + +template Uint32 Utf<32>::DecodeWide( In input ) { + // The encoding of wide characters is not well defined and is left to the system; + // however we can safely assume that it is UCS-2 on Windows and + // UCS-4 on Unix systems. + // In both cases, a simple copy is enough (UCS-2 is a subset of UCS-4, + // and UCS-4 *is* UTF-32). + + return input; +} + +template +Out Utf<32>::EncodeAnsi( Uint32 codepoint, Out output, char replacement, + const std::locale& locale ) { + // On Windows, gcc's standard library (glibc++) has almost + // no support for Unicode stuff. As a consequence, in this + // context we can only use the default locale and ignore + // the one passed as parameter. + +#if EFSW_PLATFORM == EFSW_PLATFORM_WIN && /* if Windows ... */ \ + ( defined( __GLIBCPP__ ) || \ + defined( __GLIBCXX__ ) ) && /* ... and standard library is glibc++ ... */ \ + !( defined( __SGI_STL_PORT ) || \ + defined( _STLPORT_VERSION ) ) /* ... and STLPort is not used on top of it */ + + char character = 0; + if ( wctomb( &character, static_cast( codepoint ) ) >= 0 ) + *output++ = character; + else if ( replacement ) + *output++ = replacement; + + return output; + +#else +// Get the facet of the locale which deals with character conversion +#ifndef EFSW_NO_WIDECHAR + const std::ctype& facet = std::use_facet>( locale ); +#else + const std::ctype& facet = std::use_facet>( locale ); +#endif + + // Use the facet to convert each character of the input string + *output++ = facet.narrow( static_cast( codepoint ), replacement ); + + return output; + +#endif +} + +#ifndef EFSW_NO_WIDECHAR +template +Out Utf<32>::EncodeWide( Uint32 codepoint, Out output, wchar_t replacement ) { + // The encoding of wide characters is not well defined and is left to the system; + // however we can safely assume that it is UCS-2 on Windows and + // UCS-4 on Unix systems. + // For UCS-2 we need to check if the source characters fits in (UCS-2 is a subset of UCS-4). + // For UCS-4 we can do a direct copy (UCS-4 *is* UTF-32). + + switch ( sizeof( wchar_t ) ) { + case 4: { + *output++ = static_cast( codepoint ); + break; + } + + default: { + if ( ( codepoint <= 0xFFFF ) && ( ( codepoint < 0xD800 ) || ( codepoint > 0xDFFF ) ) ) { + *output++ = static_cast( codepoint ); + } else if ( replacement ) { + *output++ = replacement; + } + break; + } + } + + return output; +} +#endif diff --git a/src/3rdParty/efsw/Watcher.cpp b/src/3rdParty/efsw/Watcher.cpp old mode 100755 new mode 100644 diff --git a/src/3rdParty/efsw/Watcher.hpp b/src/3rdParty/efsw/Watcher.hpp old mode 100755 new mode 100644 diff --git a/src/3rdParty/efsw/WatcherFSEvents.cpp b/src/3rdParty/efsw/WatcherFSEvents.cpp old mode 100755 new mode 100644 index b562d3c..f963374 --- a/src/3rdParty/efsw/WatcherFSEvents.cpp +++ b/src/3rdParty/efsw/WatcherFSEvents.cpp @@ -8,22 +8,11 @@ namespace efsw { WatcherFSEvents::WatcherFSEvents() : - Watcher(), FWatcher( NULL ), FSStream( NULL ), WatcherGen( NULL ), initializedAsync( false ) {} - -WatcherFSEvents::WatcherFSEvents( WatchID id, std::string directory, FileWatchListener* listener, - bool recursive, WatcherFSEvents* /*parent*/ ) : - Watcher( id, directory, listener, recursive ), - FWatcher( NULL ), - FSStream( NULL ), - WatcherGen( NULL ), - initializedAsync( false ) {} + Watcher(), FWatcher( NULL ), FSStream( NULL ), WatcherGen( NULL ) {} WatcherFSEvents::~WatcherFSEvents() { if ( NULL != FSStream ) { - if ( initializedAsync ) { - FSEventStreamStop( FSStream ); - } - + FSEventStreamStop( FSStream ); FSEventStreamInvalidate( FSStream ); FSEventStreamRelease( FSStream ); } @@ -39,7 +28,9 @@ void WatcherFSEvents::init() { Uint32 streamFlags = kFSEventStreamCreateFlagNone; if ( FileWatcherFSEvents::isGranular() ) { - streamFlags = efswFSEventStreamCreateFlagFileEvents; + streamFlags = efswFSEventStreamCreateFlagFileEvents | efswFSEventStreamCreateFlagNoDefer | + efswFSEventStreamCreateFlagUseExtendedData | + efswFSEventStreamCreateFlagUseCFTypes; } else { WatcherGen = new WatcherGeneric( ID, Directory, Listener, FWatcher.load(), Recursive ); } @@ -52,49 +43,49 @@ void WatcherFSEvents::init() { ctx.release = NULL; ctx.copyDescription = NULL; + dispatch_queue_t queue = dispatch_queue_create( NULL, NULL ); + FSStream = FSEventStreamCreate( kCFAllocatorDefault, &FileWatcherFSEvents::FSEventCallback, &ctx, - CFDirectoryArray, kFSEventStreamEventIdSinceNow, 0.25, streamFlags ); - FWatcher.load()->mNeedInitMutex.lock(); - FWatcher.load()->mNeedInit.push_back( this ); - FWatcher.load()->mNeedInitMutex.unlock(); + CFDirectoryArray, kFSEventStreamEventIdSinceNow, 0., streamFlags ); - CFRelease( CFDirectoryArray ); - CFRelease( CFDirectory ); -} + FSEventStreamSetDispatchQueue( FSStream, queue ); -void WatcherFSEvents::initAsync() { - FSEventStreamScheduleWithRunLoop( FSStream, FWatcher.load()->mRunLoopRef.load(), - kCFRunLoopDefaultMode ); FSEventStreamStart( FSStream ); - initializedAsync = true; + + CFRelease( CFDirectoryArray ); + CFRelease( CFDirectory ); } void WatcherFSEvents::sendFileAction( WatchID watchid, const std::string& dir, const std::string& filename, Action action, std::string oldFilename ) { Listener->handleFileAction( watchid, FileSystem::precomposeFileName( dir ), - FileSystem::precomposeFileName( filename ), action, oldFilename ); + FileSystem::precomposeFileName( filename ), action, + FileSystem::precomposeFileName( oldFilename ) ); } void WatcherFSEvents::handleAddModDel( const Uint32& flags, const std::string& path, - std::string& dirPath, std::string& filePath ) { - if ( flags & efswFSEventStreamEventFlagItemCreated ) { - if ( FileInfo::exists( path ) ) { - sendFileAction( ID, dirPath, filePath, Actions::Add ); - } + std::string& dirPath, std::string& filePath, Uint64 inode ) { + if ( ( flags & efswFSEventStreamEventFlagItemCreated ) && FileInfo::exists( path ) && + ( !SanitizeEvents || FilesAdded.find( inode ) != FilesAdded.end() ) ) { + sendFileAction( ID, dirPath, filePath, Actions::Add ); + + if ( SanitizeEvents ) + FilesAdded.insert( inode ); } - if ( flags & efswFSEventsModified ) { + if ( flags & ModifiedFlags ) { sendFileAction( ID, dirPath, filePath, Actions::Modified ); } - if ( flags & efswFSEventStreamEventFlagItemRemoved ) { + if ( ( flags & efswFSEventStreamEventFlagItemRemoved ) && !FileInfo::exists( path ) ) { // Since i don't know the order, at least i try to keep the data consistent with the real // state - if ( !FileInfo::exists( path ) ) { - sendFileAction( ID, dirPath, filePath, Actions::Delete ); - } + sendFileAction( ID, dirPath, filePath, Actions::Delete ); + + if ( SanitizeEvents ) + FilesAdded.erase( inode ); } } @@ -136,19 +127,20 @@ void WatcherFSEvents::handleActions( std::vector& events ) { // been added modified and erased, but i can't know if first was erased and then added // and modified, or added, then modified and then erased. I don't know what they were // thinking by doing this... - efDEBUG( "Event in: %s - flags: %ld\n", event.Path.c_str(), event.Flags ); + efDEBUG( "Event in: %s - flags: 0x%x\n", event.Path.c_str(), event.Flags ); if ( event.Flags & efswFSEventStreamEventFlagItemRenamed ) { if ( ( i + 1 < esize ) && ( events[i + 1].Flags & efswFSEventStreamEventFlagItemRenamed ) && - ( events[i + 1].Id == event.Id + 1 ) ) { + ( events[i + 1].inode == event.inode ) ) { FSEvent& nEvent = events[i + 1]; std::string newDir( FileSystem::pathRemoveFileName( nEvent.Path ) ); std::string newFilepath( FileSystem::fileNameFromPath( nEvent.Path ) ); if ( event.Path != nEvent.Path ) { if ( dirPath == newDir ) { - if ( !FileInfo::exists( event.Path ) ) { + if ( !FileInfo::exists( event.Path ) || + 0 == strcasecmp( event.Path.c_str(), nEvent.Path.c_str() ) ) { sendFileAction( ID, dirPath, newFilepath, Actions::Moved, filePath ); } else { @@ -159,12 +151,12 @@ void WatcherFSEvents::handleActions( std::vector& events ) { sendFileAction( ID, dirPath, filePath, Actions::Delete ); sendFileAction( ID, newDir, newFilepath, Actions::Add ); - if ( nEvent.Flags & efswFSEventsModified ) { + if ( nEvent.Flags & ModifiedFlags ) { sendFileAction( ID, newDir, newFilepath, Actions::Modified ); } } } else { - handleAddModDel( nEvent.Flags, nEvent.Path, dirPath, filePath ); + handleAddModDel( nEvent.Flags, nEvent.Path, dirPath, filePath, event.inode ); } if ( nEvent.Flags & ( efswFSEventStreamEventFlagItemCreated | @@ -180,14 +172,14 @@ void WatcherFSEvents::handleActions( std::vector& events ) { } else if ( FileInfo::exists( event.Path ) ) { sendFileAction( ID, dirPath, filePath, Actions::Add ); - if ( event.Flags & efswFSEventsModified ) { + if ( event.Flags & ModifiedFlags ) { sendFileAction( ID, dirPath, filePath, Actions::Modified ); } } else { sendFileAction( ID, dirPath, filePath, Actions::Delete ); } } else { - handleAddModDel( event.Flags, event.Path, dirPath, filePath ); + handleAddModDel( event.Flags, event.Path, dirPath, filePath, event.inode ); } } else { efDEBUG( "Directory: %s changed\n", event.Path.c_str() ); @@ -197,7 +189,7 @@ void WatcherFSEvents::handleActions( std::vector& events ) { } void WatcherFSEvents::process() { - std::set::iterator it = DirsChanged.begin(); + std::unordered_set::iterator it = DirsChanged.begin(); for ( ; it != DirsChanged.end(); it++ ) { if ( !FileWatcherFSEvents::isGranular() ) { diff --git a/src/3rdParty/efsw/WatcherFSEvents.hpp b/src/3rdParty/efsw/WatcherFSEvents.hpp old mode 100755 new mode 100644 index 4dbb231..f05b094 --- a/src/3rdParty/efsw/WatcherFSEvents.hpp +++ b/src/3rdParty/efsw/WatcherFSEvents.hpp @@ -9,51 +9,72 @@ #include #include #include -#include +#include #include namespace efsw { +/* OSX < 10.7 has no file events */ +/* So i declare the events constants */ +enum FSEventEvents { + efswFSEventStreamCreateFlagUseCFTypes = 0x00000001, + efswFSEventStreamCreateFlagNoDefer = 0x00000002, + efswFSEventStreamCreateFlagFileEvents = 0x00000010, + efswFSEventStreamCreateFlagUseExtendedData = 0x00000040, + efswFSEventStreamEventFlagItemCreated = 0x00000100, + efswFSEventStreamEventFlagItemRemoved = 0x00000200, + efswFSEventStreamEventFlagItemInodeMetaMod = 0x00000400, + efswFSEventStreamEventFlagItemRenamed = 0x00000800, + efswFSEventStreamEventFlagItemModified = 0x00001000, + efswFSEventStreamEventFlagItemFinderInfoMod = 0x00002000, + efswFSEventStreamEventFlagItemChangeOwner = 0x00004000, + efswFSEventStreamEventFlagItemXattrMod = 0x00008000, + efswFSEventStreamEventFlagItemIsFile = 0x00010000, + efswFSEventStreamEventFlagItemIsDir = 0x00020000, + efswFSEventStreamEventFlagItemIsSymlink = 0x00040000, + efswFSEventsModified = efswFSEventStreamEventFlagItemFinderInfoMod | + efswFSEventStreamEventFlagItemModified | + efswFSEventStreamEventFlagItemInodeMetaMod +}; + class FileWatcherFSEvents; class FSEvent { public: - FSEvent( std::string path, long flags, Uint64 id ) : Path( path ), Flags( flags ), Id( id ) {} + FSEvent( std::string path, long flags, Uint64 id, Uint64 inode = 0 ) : + Path( path ), Flags( flags ), Id( id ), inode( inode ) {} std::string Path; - long Flags; - Uint64 Id; + long Flags{ 0 }; + Uint64 Id{ 0 }; + Uint64 inode{ 0 }; }; class WatcherFSEvents : public Watcher { public: WatcherFSEvents(); - WatcherFSEvents( WatchID id, std::string directory, FileWatchListener* listener, bool recursive, - WatcherFSEvents* parent = NULL ); - ~WatcherFSEvents(); void init(); - void initAsync(); - void handleActions( std::vector& events ); void process(); Atomic FWatcher; FSEventStreamRef FSStream; + Uint64 ModifiedFlags{ efswFSEventsModified }; + bool SanitizeEvents{ false }; protected: void handleAddModDel( const Uint32& flags, const std::string& path, std::string& dirPath, - std::string& filePath ); + std::string& filePath, Uint64 inode ); WatcherGeneric* WatcherGen; - Atomic initializedAsync; - - std::set DirsChanged; + std::unordered_set DirsChanged; + std::unordered_set FilesAdded; void sendFileAction( WatchID watchid, const std::string& dir, const std::string& filename, Action action, std::string oldFilename = "" ); diff --git a/src/3rdParty/efsw/WatcherGeneric.cpp b/src/3rdParty/efsw/WatcherGeneric.cpp old mode 100755 new mode 100644 diff --git a/src/3rdParty/efsw/WatcherGeneric.hpp b/src/3rdParty/efsw/WatcherGeneric.hpp old mode 100755 new mode 100644 index 9cf8365..d11ec20 --- a/src/3rdParty/efsw/WatcherGeneric.hpp +++ b/src/3rdParty/efsw/WatcherGeneric.hpp @@ -17,7 +17,7 @@ class WatcherGeneric : public Watcher { ~WatcherGeneric(); - void watch(); + void watch() override; void watchDir( std::string dir ); diff --git a/src/3rdParty/efsw/WatcherInotify.cpp b/src/3rdParty/efsw/WatcherInotify.cpp old mode 100755 new mode 100644 index 7259bb1..812ddae --- a/src/3rdParty/efsw/WatcherInotify.cpp +++ b/src/3rdParty/efsw/WatcherInotify.cpp @@ -4,10 +4,6 @@ namespace efsw { WatcherInotify::WatcherInotify() : Watcher(), Parent( NULL ) {} -WatcherInotify::WatcherInotify( WatchID id, std::string directory, FileWatchListener* listener, - bool recursive, WatcherInotify* parent ) : - Watcher( id, directory, listener, recursive ), Parent( parent ), DirInfo( directory ) {} - bool WatcherInotify::inParentTree( WatcherInotify* parent ) { WatcherInotify* tNext = Parent; diff --git a/src/3rdParty/efsw/WatcherInotify.hpp b/src/3rdParty/efsw/WatcherInotify.hpp old mode 100755 new mode 100644 index bf2ff5e..ec55ed0 --- a/src/3rdParty/efsw/WatcherInotify.hpp +++ b/src/3rdParty/efsw/WatcherInotify.hpp @@ -10,15 +10,13 @@ class WatcherInotify : public Watcher { public: WatcherInotify(); - WatcherInotify( WatchID id, std::string directory, FileWatchListener* listener, bool recursive, - WatcherInotify* parent = NULL ); - bool inParentTree( WatcherInotify* parent ); WatcherInotify* Parent; WatchID InotifyID; FileInfo DirInfo; + bool syntheticEvents{ false }; }; } // namespace efsw diff --git a/src/3rdParty/efsw/WatcherKqueue.cpp b/src/3rdParty/efsw/WatcherKqueue.cpp old mode 100755 new mode 100644 index 441948a..424b989 --- a/src/3rdParty/efsw/WatcherKqueue.cpp +++ b/src/3rdParty/efsw/WatcherKqueue.cpp @@ -139,7 +139,7 @@ void WatcherKqueue::addAll() { void WatcherKqueue::removeAll() { efDEBUG( "removeAll(): Removing all child watchers\n" ); - std::list erase; + std::vector erase; for ( WatchMap::iterator it = mWatches.begin(); it != mWatches.end(); it++ ) { efDEBUG( "removeAll(): Removed child watcher %s\n", it->second->Directory.c_str() ); @@ -147,7 +147,7 @@ void WatcherKqueue::removeAll() { erase.push_back( it->second->ID ); } - for ( std::list::iterator eit = erase.begin(); eit != erase.end(); eit++ ) { + for ( std::vector::iterator eit = erase.begin(); eit != erase.end(); eit++ ) { removeWatch( *eit ); } } @@ -354,7 +354,8 @@ void WatcherKqueue::watch() { bool needScan = false; // Then we get the the events of the current folder - while ( ( nev = kevent( mKqueue, &mChangeList[0], mChangeListCount + 1, &event, 1, + while ( !mChangeList.empty() && + ( nev = kevent( mKqueue, mChangeList.data(), mChangeListCount + 1, &event, 1, &mWatcher->mTimeOut ) ) != 0 ) { // An error ocurred? if ( nev == -1 ) { @@ -436,7 +437,6 @@ void WatcherKqueue::moveDirectory( std::string oldPath, std::string newPath, boo WatchID WatcherKqueue::addWatch( const std::string& directory, FileWatchListener* watcher, bool recursive, WatcherKqueue* parent ) { - static long s_fc = 0; static bool s_ug = false; std::string dir( directory ); @@ -478,8 +478,6 @@ WatchID WatcherKqueue::addWatch( const std::string& directory, FileWatchListener watch->addAll(); - s_fc++; - // if failed to open the directory... erase the watcher if ( !watch->initOK() ) { int le = watch->lastErrno(); @@ -502,9 +500,8 @@ WatchID WatcherKqueue::addWatch( const std::string& directory, FileWatchListener } } else { if ( !s_ug ) { - efDEBUG( "Started using WatcherGeneric, reached file descriptors limit: %ld. Folders " - "added: %ld\n", - mWatcher->mFileDescriptorCount, s_fc ); + efDEBUG( "Started using WatcherGeneric, reached file descriptors limit: %ld.\n", + mWatcher->mFileDescriptorCount ); s_ug = true; } diff --git a/src/3rdParty/efsw/WatcherKqueue.hpp b/src/3rdParty/efsw/WatcherKqueue.hpp old mode 100755 new mode 100644 index 87d898c..75c0f62 --- a/src/3rdParty/efsw/WatcherKqueue.hpp +++ b/src/3rdParty/efsw/WatcherKqueue.hpp @@ -49,7 +49,7 @@ class WatcherKqueue : public Watcher { WatchID watchingDirectory( std::string dir ); - void watch(); + void watch() override; WatchID addWatch( const std::string& directory, FileWatchListener* watcher, bool recursive, WatcherKqueue* parent ); diff --git a/src/3rdParty/efsw/WatcherWin32.cpp b/src/3rdParty/efsw/WatcherWin32.cpp old mode 100755 new mode 100644 index 3e8bcc7..712419e --- a/src/3rdParty/efsw/WatcherWin32.cpp +++ b/src/3rdParty/efsw/WatcherWin32.cpp @@ -1,34 +1,114 @@ +#include #include #include #if EFSW_PLATFORM == EFSW_PLATFORM_WIN32 +#include + namespace efsw { -/// Unpacks events and passes them to a user defined callback. -void CALLBACK WatchCallback( DWORD dwNumberOfBytesTransfered, LPOVERLAPPED lpOverlapped ) { - if ( dwNumberOfBytesTransfered == 0 || NULL == lpOverlapped ) { - return; +struct EFSW_FILE_NOTIFY_EXTENDED_INFORMATION_EX { + DWORD NextEntryOffset; + DWORD Action; + LARGE_INTEGER CreationTime; + LARGE_INTEGER LastModificationTime; + LARGE_INTEGER LastChangeTime; + LARGE_INTEGER LastAccessTime; + LARGE_INTEGER AllocatedLength; + LARGE_INTEGER FileSize; + DWORD FileAttributes; + DWORD ReparsePointTag; + LARGE_INTEGER FileId; + LARGE_INTEGER ParentFileId; + DWORD FileNameLength; + WCHAR FileName[1]; +}; + +typedef EFSW_FILE_NOTIFY_EXTENDED_INFORMATION_EX* EFSW_PFILE_NOTIFY_EXTENDED_INFORMATION_EX; + +typedef BOOL( WINAPI* EFSW_LPREADDIRECTORYCHANGESEXW )( HANDLE hDirectory, LPVOID lpBuffer, + DWORD nBufferLength, BOOL bWatchSubtree, + DWORD dwNotifyFilter, LPDWORD lpBytesReturned, + LPOVERLAPPED lpOverlapped, LPOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine, + DWORD ReadDirectoryNotifyInformationClass ); + +static EFSW_LPREADDIRECTORYCHANGESEXW pReadDirectoryChangesExW = NULL; + +#define EFSW_ReadDirectoryNotifyExtendedInformation 2 + +static void initReadDirectoryChangesEx() { + static bool hasInit = false; + if ( !hasInit ) { + hasInit = true; + + HMODULE hModule = GetModuleHandleW( L"Kernel32.dll" ); + if ( !hModule ) + return; + + pReadDirectoryChangesExW = + (EFSW_LPREADDIRECTORYCHANGESEXW)GetProcAddress( hModule, "ReadDirectoryChangesExW" ); } +} - char szFile[MAX_PATH]; +void WatchCallbackOld( WatcherWin32* pWatch ) { PFILE_NOTIFY_INFORMATION pNotify; - WatcherStructWin32* tWatch = (WatcherStructWin32*)lpOverlapped; - WatcherWin32* pWatch = tWatch->Watch; size_t offset = 0; - do { bool skip = false; pNotify = (PFILE_NOTIFY_INFORMATION)&pWatch->Buffer[offset]; offset += pNotify->NextEntryOffset; + int count = + WideCharToMultiByte( CP_UTF8, 0, pNotify->FileName, + pNotify->FileNameLength / sizeof( WCHAR ), NULL, 0, NULL, NULL ); + if ( count == 0 ) + continue; + + std::string nfile( count, '\0' ); + + count = WideCharToMultiByte( CP_UTF8, 0, pNotify->FileName, + pNotify->FileNameLength / sizeof( WCHAR ), &nfile[0], count, + NULL, NULL ); + + if ( FILE_ACTION_MODIFIED == pNotify->Action ) { + FileInfo fifile( std::string( pWatch->DirName ) + nfile ); + + if ( pWatch->LastModifiedEvent.file.ModificationTime == fifile.ModificationTime && + pWatch->LastModifiedEvent.file.Size == fifile.Size && + pWatch->LastModifiedEvent.fileName == nfile ) { + skip = true; + } + + pWatch->LastModifiedEvent.fileName = nfile; + pWatch->LastModifiedEvent.file = fifile; + } + + if ( !skip ) { + pWatch->Watch->handleAction( pWatch, nfile, pNotify->Action ); + } + } while ( pNotify->NextEntryOffset != 0 ); +} + +void WatchCallbackEx( WatcherWin32* pWatch ) { + EFSW_PFILE_NOTIFY_EXTENDED_INFORMATION_EX pNotify; + size_t offset = 0; + do { + bool skip = false; + + pNotify = (EFSW_PFILE_NOTIFY_EXTENDED_INFORMATION_EX)&pWatch->Buffer[offset]; + offset += pNotify->NextEntryOffset; + int count = + WideCharToMultiByte( CP_UTF8, 0, pNotify->FileName, + pNotify->FileNameLength / sizeof( WCHAR ), NULL, 0, NULL, NULL ); + if ( count == 0 ) + continue; - int count = WideCharToMultiByte( CP_UTF8, 0, pNotify->FileName, - pNotify->FileNameLength / sizeof( WCHAR ), szFile, - MAX_PATH - 1, NULL, NULL ); - szFile[count] = TEXT( '\0' ); + std::string nfile( count, '\0' ); - std::string nfile( szFile ); + count = WideCharToMultiByte( CP_UTF8, 0, pNotify->FileName, + pNotify->FileNameLength / sizeof( WCHAR ), &nfile[0], count, + NULL, NULL ); if ( FILE_ACTION_MODIFIED == pNotify->Action ) { FileInfo fifile( std::string( pWatch->DirName ) + nfile ); @@ -41,12 +121,59 @@ void CALLBACK WatchCallback( DWORD dwNumberOfBytesTransfered, LPOVERLAPPED lpOve pWatch->LastModifiedEvent.fileName = nfile; pWatch->LastModifiedEvent.file = fifile; + } else if ( FILE_ACTION_RENAMED_OLD_NAME == pNotify->Action ) { + pWatch->OldFiles.emplace_back( nfile, pNotify->FileId ); + skip = true; + } else if ( FILE_ACTION_RENAMED_NEW_NAME == pNotify->Action ) { + std::string oldFile; + LARGE_INTEGER oldFileId{}; + + for ( auto it = pWatch->OldFiles.begin(); it != pWatch->OldFiles.end(); ++it ) { + if ( it->second.QuadPart == pNotify->FileId.QuadPart ) { + oldFile = it->first; + oldFileId = it->second; + it = pWatch->OldFiles.erase( it ); + break; + } + } + + if ( oldFile.empty() ) { + pWatch->Watch->handleAction( pWatch, nfile, FILE_ACTION_ADDED ); + skip = true; + } else { + pWatch->Watch->handleAction( pWatch, oldFile, FILE_ACTION_RENAMED_OLD_NAME ); + } } if ( !skip ) { pWatch->Watch->handleAction( pWatch, nfile, pNotify->Action ); } } while ( pNotify->NextEntryOffset != 0 ); +} + +/// Unpacks events and passes them to a user defined callback. +void CALLBACK WatchCallback( DWORD dwNumberOfBytesTransfered, LPOVERLAPPED lpOverlapped ) { + if ( NULL == lpOverlapped ) { + return; + } + + WatcherStructWin32* tWatch = (WatcherStructWin32*)lpOverlapped; + WatcherWin32* pWatch = tWatch->Watch; + + if ( dwNumberOfBytesTransfered == 0 ) { + if ( nullptr != pWatch && !pWatch->StopNow ) { + RefreshWatch( tWatch ); + } else { + return; + } + } + + // Fork watch depending on the Windows API supported + if ( pWatch->Extended ) { + WatchCallbackEx( pWatch ); + } else { + WatchCallbackOld( pWatch ); + } if ( !pWatch->StopNow ) { RefreshWatch( tWatch ); @@ -54,11 +181,40 @@ void CALLBACK WatchCallback( DWORD dwNumberOfBytesTransfered, LPOVERLAPPED lpOve } /// Refreshes the directory monitoring. -bool RefreshWatch( WatcherStructWin32* pWatch ) { - return ReadDirectoryChangesW( pWatch->Watch->DirHandle, pWatch->Watch->Buffer, - sizeof( pWatch->Watch->Buffer ), pWatch->Watch->Recursive, - pWatch->Watch->NotifyFilter, NULL, &pWatch->Overlapped, - NULL ) != 0; +RefreshResult RefreshWatch( WatcherStructWin32* pWatch ) { + initReadDirectoryChangesEx(); + + bool bRet = false; + RefreshResult ret = RefreshResult::Failed; + pWatch->Watch->Extended = false; + + if ( pReadDirectoryChangesExW ) { + bRet = pReadDirectoryChangesExW( pWatch->Watch->DirHandle, pWatch->Watch->Buffer.data(), + (DWORD)pWatch->Watch->Buffer.size(), pWatch->Watch->Recursive, + pWatch->Watch->NotifyFilter, NULL, &pWatch->Overlapped, + NULL, EFSW_ReadDirectoryNotifyExtendedInformation ) != 0; + if ( bRet ) { + ret = RefreshResult::SucessEx; + pWatch->Watch->Extended = true; + } + } + + if ( !bRet ) { + bRet = ReadDirectoryChangesW( pWatch->Watch->DirHandle, pWatch->Watch->Buffer.data(), + (DWORD)pWatch->Watch->Buffer.size(), pWatch->Watch->Recursive, + pWatch->Watch->NotifyFilter, NULL, &pWatch->Overlapped, + NULL ) != 0; + + if ( bRet ) + ret = RefreshResult::Success; + } + + if ( !bRet ) { + std::string error = std::to_string( GetLastError() ); + Errors::Log::createLastError( Errors::WatcherFailed, error ); + } + + return ret; } /// Stops monitoring a directory. @@ -70,19 +226,17 @@ void DestroyWatch( WatcherStructWin32* pWatch ) { CloseHandle( pWatch->Watch->DirHandle ); efSAFE_DELETE_ARRAY( pWatch->Watch->DirName ); efSAFE_DELETE( pWatch->Watch ); + efSAFE_DELETE( pWatch ); } } /// Starts monitoring a directory. -WatcherStructWin32* CreateWatch( LPCWSTR szDirectory, bool recursive, DWORD NotifyFilter, - HANDLE iocp ) { - WatcherStructWin32* tWatch; - size_t ptrsize = sizeof( *tWatch ); - tWatch = static_cast( - HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, ptrsize ) ); - - WatcherWin32* pWatch = new WatcherWin32(); - tWatch->Watch = pWatch; +WatcherStructWin32* CreateWatch( LPCWSTR szDirectory, bool recursive, + DWORD bufferSize, DWORD notifyFilter, HANDLE iocp ) { + WatcherStructWin32* tWatch = new WatcherStructWin32(); + WatcherWin32* pWatch = new WatcherWin32(bufferSize); + if (tWatch) + tWatch->Watch = pWatch; pWatch->DirHandle = CreateFileW( szDirectory, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, @@ -90,17 +244,17 @@ WatcherStructWin32* CreateWatch( LPCWSTR szDirectory, bool recursive, DWORD Noti if ( pWatch->DirHandle != INVALID_HANDLE_VALUE && CreateIoCompletionPort( pWatch->DirHandle, iocp, 0, 1 ) ) { - pWatch->NotifyFilter = NotifyFilter; + pWatch->NotifyFilter = notifyFilter; pWatch->Recursive = recursive; - if ( RefreshWatch( tWatch ) ) { + if ( RefreshResult::Failed != RefreshWatch( tWatch ) ) { return tWatch; } } CloseHandle( pWatch->DirHandle ); efSAFE_DELETE( pWatch->Watch ); - HeapFree( GetProcessHeap(), 0, tWatch ); + efSAFE_DELETE( tWatch ); return NULL; } diff --git a/src/3rdParty/efsw/WatcherWin32.hpp b/src/3rdParty/efsw/WatcherWin32.hpp old mode 100755 new mode 100644 index 71e13be..ea1e8e4 --- a/src/3rdParty/efsw/WatcherWin32.hpp +++ b/src/3rdParty/efsw/WatcherWin32.hpp @@ -3,6 +3,7 @@ #include #include +#include #if EFSW_PLATFORM == EFSW_PLATFORM_WIN32 @@ -21,6 +22,8 @@ namespace efsw { class WatcherWin32; +enum RefreshResult { Failed, Success, SucessEx }; + /// Internal watch data struct WatcherStructWin32 { OVERLAPPED Overlapped; @@ -32,39 +35,41 @@ struct sLastModifiedEvent { std::string fileName; }; -bool RefreshWatch( WatcherStructWin32* pWatch ); +RefreshResult RefreshWatch( WatcherStructWin32* pWatch ); void CALLBACK WatchCallback( DWORD dwNumberOfBytesTransfered, LPOVERLAPPED lpOverlapped ); void DestroyWatch( WatcherStructWin32* pWatch ); -WatcherStructWin32* CreateWatch( LPCWSTR szDirectory, bool recursive, DWORD NotifyFilter, - HANDLE iocp ); +WatcherStructWin32* CreateWatch( LPCWSTR szDirectory, bool recursive, + DWORD bufferSize, DWORD notifyFilter, HANDLE iocp ); class WatcherWin32 : public Watcher { public: - WatcherWin32() : + WatcherWin32(DWORD dwBufferSize) : Struct( NULL ), DirHandle( NULL ), + Buffer(), lParam( 0 ), NotifyFilter( 0 ), StopNow( false ), + Extended( false ), Watch( NULL ), - DirName( NULL ) {} + DirName( NULL ) { + Buffer.resize(dwBufferSize); + } WatcherStructWin32* Struct; HANDLE DirHandle; - BYTE Buffer - [63 * - 1024]; // do NOT make this bigger than 64K because it will fail if the folder being watched - // is on the network! (see - // http://msdn.microsoft.com/en-us/library/windows/desktop/aa365465(v=vs.85).aspx) + std::vector Buffer; LPARAM lParam; DWORD NotifyFilter; bool StopNow; + bool Extended; FileWatcherImpl* Watch; char* DirName; sLastModifiedEvent LastModifiedEvent; + std::vector> OldFiles; }; } // namespace efsw diff --git a/src/3rdParty/efsw/base.hpp b/src/3rdParty/efsw/base.hpp old mode 100755 new mode 100644 diff --git a/src/3rdParty/efsw/efsw.h b/src/3rdParty/efsw/efsw.h old mode 100755 new mode 100644 index 28e63e2..30cf595 --- a/src/3rdParty/efsw/efsw.h +++ b/src/3rdParty/efsw/efsw.h @@ -1,7 +1,7 @@ /** @author Sepul Sepehr Taghdisian - Copyright (c) 2013 Martin Lucas Golini + Copyright (c) 2024 Martín Lucas Golini Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -32,31 +32,31 @@ extern "C" { #endif -#if defined(_WIN32) - #ifdef EFSW_DYNAMIC - // Windows platforms - #ifdef EFSW_EXPORTS - // From DLL side, we must export - #define EFSW_API __declspec(dllexport) - #else - // From client application side, we must import - #define EFSW_API __declspec(dllimport) - #endif - #else - // No specific directive needed for static build - #ifndef EFSW_API - #define EFSW_API - #endif - #endif +#if defined( _WIN32 ) +#ifdef EFSW_DYNAMIC +// Windows platforms +#ifdef EFSW_EXPORTS +// From DLL side, we must export +#define EFSW_API __declspec( dllexport ) #else - #if ( __GNUC__ >= 4 ) && defined( EFSW_EXPORTS ) - #define EFSW_API __attribute__ ((visibility("default"))) - #endif - - // Other platforms don't need to define anything - #ifndef EFSW_API - #define EFSW_API - #endif +// From client application side, we must import +#define EFSW_API __declspec( dllimport ) +#endif +#else +// No specific directive needed for static build +#ifndef EFSW_API +#define EFSW_API +#endif +#endif +#else +#if ( __GNUC__ >= 4 ) && defined( EFSW_EXPORTS ) +#define EFSW_API __attribute__( ( visibility( "default" ) ) ) +#endif + +// Other platforms don't need to define anything +#ifndef EFSW_API +#define EFSW_API +#endif #endif /// Type for a watch id @@ -65,84 +65,127 @@ typedef long efsw_watchid; /// Type for watcher typedef void* efsw_watcher; -enum efsw_action -{ - EFSW_ADD = 1, /// Sent when a file is created or renamed - EFSW_DELETE = 2, /// Sent when a file is deleted or renamed - EFSW_MODIFIED = 3, /// Sent when a file is modified - EFSW_MOVED = 4 /// Sent when a file is moved +enum efsw_action { + EFSW_ADD = 1, /// Sent when a file is created or renamed + EFSW_DELETE = 2, /// Sent when a file is deleted or renamed + EFSW_MODIFIED = 3, /// Sent when a file is modified + EFSW_MOVED = 4 /// Sent when a file is moved +}; + +enum efsw_error { + EFSW_NOTFOUND = -1, + EFSW_REPEATED = -2, + EFSW_OUTOFSCOPE = -3, + EFSW_NOTREADABLE = -4, + EFSW_REMOTE = -5, + EFSW_WATCHER_FAILED = -6, + EFSW_UNSPECIFIED = -7 }; -enum efsw_error -{ - EFSW_NOTFOUND = -1, - EFSW_REPEATED = -2, - EFSW_OUTOFSCOPE = -3, - EFSW_NOTREADABLE = -4, - EFSW_REMOTE = -5, - EFSW_UNSPECIFIED = -6 +enum efsw_option { + /// For Windows, the default buffer size of 63*1024 bytes sometimes is not enough and + /// file system events may be dropped. For that, using a different (bigger) buffer size + /// can be defined here, but note that this does not work for network drives, + /// because a buffer larger than 64K will fail the folder being watched, see + /// http://msdn.microsoft.com/en-us/library/windows/desktop/aa365465(v=vs.85).aspx) + EFSW_OPT_WIN_BUFFER_SIZE = 1, + /// For Windows, per default all events are captured but we might only be interested + /// in a subset; the value of the option should be set to a bitwise or'ed set of + /// FILE_NOTIFY_CHANGE_* flags. + EFSW_OPT_WIN_NOTIFY_FILTER = 2, + /// For macOS (FSEvents backend), per default all modified event types are capture but we might + // only be interested in a subset; the value of the option should be set to a set of bitwise + // from: + // kFSEventStreamEventFlagItemFinderInfoMod + // kFSEventStreamEventFlagItemModified + // kFSEventStreamEventFlagItemInodeMetaMod + // Default configuration will set the 3 flags + EFSW_OPT_MAC_MODIFIED_FILTER = 3, + /// macOS sometimes informs incorrect or old file states that may confuse the consumer + /// The events sanitizer will try to sanitize incorrectly reported events in favor of reducing + /// the number of events reported. This will have an small performance and memory impact as a + /// consequence. + EFSW_OPT_MAC_SANITIZE_EVENTS = 4, + /// Linux does not support natively recursive watchers. This means that when using recursive + /// watches efsw registers new watchers for each directory. If new file are created between + /// the time efsw takes to register the new directory those events might be missed. To avoid + /// missing new file notifications efsw will trigger synthetic new file events for existing + /// files in the new directroy watched. This might have the unintended consequence of sending + /// duplicated created events due to the system also emitting this event. + LINUX_PRODUCE_SYNTHETIC_EVENTS = 5, }; /// Basic interface for listening for file events. -typedef void (*efsw_pfn_fileaction_callback) ( - efsw_watcher watcher, - efsw_watchid watchid, - const char* dir, - const char* filename, - enum efsw_action action, - const char* old_filename, - void* param -); +typedef void ( *efsw_pfn_fileaction_callback )( efsw_watcher watcher, efsw_watchid watchid, + const char* dir, const char* filename, + enum efsw_action action, const char* old_filename, + void* param ); + +typedef struct { + enum efsw_option option; + int value; +} efsw_watcher_option; /** * Creates a new file-watcher * @param generic_mode Force the use of the Generic file watcher */ -efsw_watcher EFSW_API efsw_create(int generic_mode); +efsw_watcher EFSW_API efsw_create( int generic_mode ); /// Release the file-watcher and unwatch any directories -void EFSW_API efsw_release(efsw_watcher watcher); +void EFSW_API efsw_release( efsw_watcher watcher ); -/// Retreive last error occured by file-watcher +/// Retrieve last error occured by file-watcher EFSW_API const char* efsw_getlasterror(); -/// Add a directory watch. Same as the other addWatch, but doesn't have recursive option. -/// For backwards compatibility. +/// Reset file-watcher last error +EFSW_API void efsw_clearlasterror(); + +/// Add a directory watch /// On error returns WatchID with Error type. -efsw_watchid EFSW_API efsw_addwatch(efsw_watcher watcher, const char* directory, - efsw_pfn_fileaction_callback callback_fn, int recursive, void* param); +efsw_watchid EFSW_API efsw_addwatch( efsw_watcher watcher, const char* directory, + efsw_pfn_fileaction_callback callback_fn, int recursive, + void* param ); + +/// Add a directory watch, specifying options +/// @param options Pointer to an array of watcher options +/// @param nr_options Number of options referenced by \p options +efsw_watchid EFSW_API efsw_addwatch_withoptions( efsw_watcher watcher, const char* directory, + efsw_pfn_fileaction_callback callback_fn, + int recursive, efsw_watcher_option* options, + int options_number, void* param ); /// Remove a directory watch. This is a brute force search O(nlogn). -void EFSW_API efsw_removewatch(efsw_watcher watcher, const char* directory); +void EFSW_API efsw_removewatch( efsw_watcher watcher, const char* directory ); /// Remove a directory watch. This is a map lookup O(logn). -void EFSW_API efsw_removewatch_byid(efsw_watcher watcher, efsw_watchid watchid); +void EFSW_API efsw_removewatch_byid( efsw_watcher watcher, efsw_watchid watchid ); /// Starts watching ( in other thread ) -void EFSW_API efsw_watch(efsw_watcher watcher); +void EFSW_API efsw_watch( efsw_watcher watcher ); /** * Allow recursive watchers to follow symbolic links to other directories * followSymlinks is disabled by default */ -void EFSW_API efsw_follow_symlinks(efsw_watcher watcher, int enable); +void EFSW_API efsw_follow_symlinks( efsw_watcher watcher, int enable ); /** @return If can follow symbolic links to directorioes */ -int EFSW_API efsw_follow_symlinks_isenabled(efsw_watcher watcher); +int EFSW_API efsw_follow_symlinks_isenabled( efsw_watcher watcher ); /** * When enable this it will allow symlinks to watch recursively out of the pointed directory. * follorSymlinks must be enabled to this work. - * For example, added symlink to /home/folder, and the symlink points to /, this by default is not allowed, - * it's only allowed to symlink anything from /home/ and deeper. This is to avoid great levels of recursion. - * Enabling this could lead in infinite recursion, and crash the watcher ( it will try not to avoid this ). - * Buy enabling out of scope links, it will allow this behavior. + * For example, added symlink to /home/folder, and the symlink points to /, this by default is not + * allowed, it's only allowed to symlink anything from /home/ and deeper. This is to avoid great + * levels of recursion. Enabling this could lead in infinite recursion, and crash the watcher ( it + * will try not to avoid this ). Buy enabling out of scope links, it will allow this behavior. * allowOutOfScopeLinks are disabled by default. */ -void EFSW_API efsw_allow_outofscopelinks(efsw_watcher watcher, int allow); +void EFSW_API efsw_allow_outofscopelinks( efsw_watcher watcher, int allow ); /// @return Returns if out of scope links are allowed -int EFSW_API efsw_outofscopelinks_isallowed(efsw_watcher watcher); +int EFSW_API efsw_outofscopelinks_isallowed( efsw_watcher watcher ); #ifdef __cplusplus } diff --git a/src/3rdParty/efsw/efsw.hpp b/src/3rdParty/efsw/efsw.hpp old mode 100755 new mode 100644 index 12af116..11a5dec --- a/src/3rdParty/efsw/efsw.hpp +++ b/src/3rdParty/efsw/efsw.hpp @@ -1,195 +1,261 @@ -/** - @author Martín Lucas Golini - - Copyright (c) 2013 Martín Lucas Golini - - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - THE SOFTWARE. - - This software is a fork of the "simplefilewatcher" by James Wynn (james@jameswynn.com) - http://code.google.com/p/simplefilewatcher/ also MIT licensed. -*/ - -#ifndef ESFW_HPP -#define ESFW_HPP - -#include -#include - -#if defined( _WIN32 ) -#ifdef EFSW_DYNAMIC -// Windows platforms -#ifdef EFSW_EXPORTS -// From DLL side, we must export -#define EFSW_API __declspec( dllexport ) -#else -// From client application side, we must import -#define EFSW_API __declspec( dllimport ) -#endif -#else -// No specific directive needed for static build -#ifndef EFSW_API -#define EFSW_API -#endif -#endif -#else -#if ( __GNUC__ >= 4 ) && defined( EFSW_EXPORTS ) -#ifndef EFSW_API -#define EFSW_API __attribute__( ( visibility( "default" ) ) ) -#endif -#endif - -// Other platforms don't need to define anything -#ifndef EFSW_API -#define EFSW_API -#endif -#endif - -namespace efsw { - -/// Type for a watch id -typedef long WatchID; - -// forward declarations -class FileWatcherImpl; -class FileWatchListener; - -/// Actions to listen for. Rename will send two events, one for -/// the deletion of the old file, and one for the creation of the -/// new file. -namespace Actions { -enum Action { - /// Sent when a file is created or renamed - Add = 1, - /// Sent when a file is deleted or renamed - Delete = 2, - /// Sent when a file is modified - Modified = 3, - /// Sent when a file is moved - Moved = 4 -}; -} -typedef Actions::Action Action; - -/// Errors log namespace -namespace Errors { - -enum Error { - FileNotFound = -1, - FileRepeated = -2, - FileOutOfScope = -3, - FileNotReadable = -4, - FileRemote = -5, /** Directory in remote file system ( create a generic FileWatcher instance to - watch this directory ). */ - Unspecified = -6 -}; - -class EFSW_API Log { - public: - /// @return The last error logged - static std::string getLastErrorLog(); - - /// Creates an error of the type specified - static Error createLastError( Error err, std::string log ); -}; - -} // namespace Errors -typedef Errors::Error Error; - -/// Listens to files and directories and dispatches events -/// to notify the listener of files and directories changes. -/// @class FileWatcher -class EFSW_API FileWatcher { - public: - /// Default constructor, will use the default platform file watcher - FileWatcher(); - - /// Constructor that lets you force the use of the Generic File Watcher - explicit FileWatcher( bool useGenericFileWatcher ); - - virtual ~FileWatcher(); - - /// Add a directory watch. Same as the other addWatch, but doesn't have recursive option. - /// For backwards compatibility. - /// On error returns WatchID with Error type. - WatchID addWatch( const std::string& directory, FileWatchListener* watcher ); - - /// Add a directory watch - /// On error returns WatchID with Error type. - WatchID addWatch( const std::string& directory, FileWatchListener* watcher, bool recursive ); - - /// Remove a directory watch. This is a brute force search O(nlogn). - void removeWatch( const std::string& directory ); - - /// Remove a directory watch. This is a map lookup O(logn). - void removeWatch( WatchID watchid ); - - /// Starts watching ( in other thread ) - void watch(); - - /// @return Returns a list of the directories that are being watched - std::list directories(); - - /** Allow recursive watchers to follow symbolic links to other directories - * followSymlinks is disabled by default - */ - void followSymlinks( bool follow ); - - /** @return If can follow symbolic links to directorioes */ - const bool& followSymlinks() const; - - /** When enable this it will allow symlinks to watch recursively out of the pointed directory. - * follorSymlinks must be enabled to this work. - * For example, added symlink to /home/folder, and the symlink points to /, this by default is - * not allowed, it's only allowed to symlink anything from /home/ and deeper. This is to avoid - * great levels of recursion. Enabling this could lead in infinite recursion, and crash the - * watcher ( it will try not to avoid this ). Buy enabling out of scope links, it will allow - * this behavior. allowOutOfScopeLinks are disabled by default. - */ - void allowOutOfScopeLinks( bool allow ); - - /// @return Returns if out of scope links are allowed - const bool& allowOutOfScopeLinks() const; - - private: - /// The implementation - FileWatcherImpl* mImpl; - bool mFollowSymlinks; - bool mOutOfScopeLinks; -}; - -/// Basic interface for listening for file events. -/// @class FileWatchListener -class FileWatchListener { - public: - virtual ~FileWatchListener() {} - - /// Handles the action file action - /// @param watchid The watch id for the directory - /// @param dir The directory - /// @param filename The filename that was accessed (not full path) - /// @param action Action that was performed - /// @param oldFilename The name of the file or directory moved - virtual void handleFileAction( WatchID watchid, const std::string& dir, - const std::string& filename, Action action, - std::string oldFilename = "" ) = 0; -}; - -} // namespace efsw - -#endif +/** + @author Martín Lucas Golini + + Copyright (c) 2024 Martín Lucas Golini + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. + + This software is a fork of the "simplefilewatcher" by James Wynn (james@jameswynn.com) + http://code.google.com/p/simplefilewatcher/ also MIT licensed. +*/ + +#ifndef ESFW_HPP +#define ESFW_HPP + +#include +#include + +#if defined( _WIN32 ) +#ifdef EFSW_DYNAMIC +// Windows platforms +#ifdef EFSW_EXPORTS +// From DLL side, we must export +#define EFSW_API __declspec( dllexport ) +#else +// From client application side, we must import +#define EFSW_API __declspec( dllimport ) +#endif +#else +// No specific directive needed for static build +#ifndef EFSW_API +#define EFSW_API +#endif +#endif +#else +#if ( __GNUC__ >= 4 ) && defined( EFSW_EXPORTS ) +#ifndef EFSW_API +#define EFSW_API __attribute__( ( visibility( "default" ) ) ) +#endif +#endif + +// Other platforms don't need to define anything +#ifndef EFSW_API +#define EFSW_API +#endif +#endif + +namespace efsw { + +/// Type for a watch id +typedef long WatchID; + +// forward declarations +class FileWatcherImpl; +class FileWatchListener; +class WatcherOption; + +/// Actions to listen for. Rename will send two events, one for +/// the deletion of the old file, and one for the creation of the +/// new file. +namespace Actions { +enum Action { + /// Sent when a file is created or renamed + Add = 1, + /// Sent when a file is deleted or renamed + Delete = 2, + /// Sent when a file is modified + Modified = 3, + /// Sent when a file is moved + Moved = 4 +}; +} +typedef Actions::Action Action; + +/// Errors log namespace +namespace Errors { + +enum Error { + NoError = 0, + FileNotFound = -1, + FileRepeated = -2, + FileOutOfScope = -3, + FileNotReadable = -4, + /// Directory in remote file system + /// ( create a generic FileWatcher instance to watch this directory ). + FileRemote = -5, + /// File system watcher failed to watch for changes. + WatcherFailed = -6, + Unspecified = -7 +}; + +class EFSW_API Log { + public: + /// @return The last error logged + static std::string getLastErrorLog(); + + /// @return The code of the last error logged + static Error getLastErrorCode(); + + /// Reset last error + static void clearLastError(); + + /// Creates an error of the type specified + static Error createLastError( Error err, std::string log ); +}; + +} // namespace Errors +typedef Errors::Error Error; + +/// Optional file watcher settings. +namespace Options { +enum Option { + /// For Windows, the default buffer size of 63*1024 bytes sometimes is not enough and + /// file system events may be dropped. For that, using a different (bigger) buffer size + /// can be defined here, but note that this does not work for network drives, + /// because a buffer larger than 64K will fail the folder being watched, see + /// http://msdn.microsoft.com/en-us/library/windows/desktop/aa365465(v=vs.85).aspx) + WinBufferSize = 1, + /// For Windows, per default all events are captured but we might only be interested + /// in a subset; the value of the option should be set to a bitwise or'ed set of + /// FILE_NOTIFY_CHANGE_* flags. + WinNotifyFilter = 2, + /// For macOS (FSEvents backend), per default all modified event types are capture but we might + /// only be interested in a subset; the value of the option should be set to a set of bitwise + /// from: + /// kFSEventStreamEventFlagItemFinderInfoMod + /// kFSEventStreamEventFlagItemModified + /// kFSEventStreamEventFlagItemInodeMetaMod + /// Default configuration will set the 3 flags + MacModifiedFilter = 3, + /// macOS sometimes informs incorrect or old file states that may confuse the consumer + /// The events sanitizer will try to sanitize incorrectly reported events in favor of reducing + /// the number of events reported. This will have an small performance and memory impact as a + /// consequence. + MacSanitizeEvents = 4, + /// Linux does not support natively recursive watchers. This means that when using recursive + /// watches efsw registers new watchers for each directory. If new file are created between + /// the time efsw takes to register the new directory those events might be missed. To avoid + /// missing new file notifications efsw will trigger synthetic created file events for existing + /// files in the new directroy watched. This might have the unintended consequence of sending + /// duplicated created events due to the system also emitting this event. + LinuxProduceSyntheticEvents = 5, +}; +} +typedef Options::Option Option; + +/// Listens to files and directories and dispatches events +/// to notify the listener of files and directories changes. +/// @class FileWatcher +class EFSW_API FileWatcher { + public: + /// Default constructor, will use the default platform file watcher + FileWatcher(); + + /// Constructor that lets you force the use of the Generic File Watcher + explicit FileWatcher( bool useGenericFileWatcher ); + + virtual ~FileWatcher(); + + /// Add a directory watch. Same as the other addWatch, but doesn't have recursive option. + /// For backwards compatibility. + /// On error returns WatchID with Error type. + WatchID addWatch( const std::string& directory, FileWatchListener* watcher ); + + /// Add a directory watch + /// On error returns WatchID with Error type. + WatchID addWatch( const std::string& directory, FileWatchListener* watcher, bool recursive ); + + /// Add a directory watch, allowing customization with options + /// @param directory The folder to be watched + /// @param watcher The listener to receive events + /// @param recursive Set this to true to include subdirectories + /// @param options Allows customization of a watcher + /// @return Returns the watch id for the directory or, on error, a WatchID with Error type. + WatchID addWatch( const std::string& directory, FileWatchListener* watcher, bool recursive, + const std::vector& options ); + + /// Remove a directory watch. This is a brute force search O(nlogn). + void removeWatch( const std::string& directory ); + + /// Remove a directory watch. This is a map lookup O(logn). + void removeWatch( WatchID watchid ); + + /// Starts watching ( in other thread ) + void watch(); + + /// @return Returns a list of the directories that are being watched + std::vector directories(); + + /** Allow recursive watchers to follow symbolic links to other directories + * followSymlinks is disabled by default + */ + void followSymlinks( bool follow ); + + /** @return If can follow symbolic links to directorioes */ + const bool& followSymlinks() const; + + /** When enable this it will allow symlinks to watch recursively out of the pointed directory. + * follorSymlinks must be enabled to this work. + * For example, added symlink to /home/folder, and the symlink points to /, this by default is + * not allowed, it's only allowed to symlink anything from /home/ and deeper. This is to avoid + * great levels of recursion. Enabling this could lead in infinite recursion, and crash the + * watcher ( it will try not to avoid this ). Buy enabling out of scope links, it will allow + * this behavior. allowOutOfScopeLinks are disabled by default. + */ + void allowOutOfScopeLinks( bool allow ); + + /// @return Returns if out of scope links are allowed + const bool& allowOutOfScopeLinks() const; + + private: + /// The implementation + FileWatcherImpl* mImpl; + bool mFollowSymlinks; + bool mOutOfScopeLinks; +}; + +/// Basic interface for listening for file events. +/// @class FileWatchListener +class FileWatchListener { + public: + virtual ~FileWatchListener() {} + + /// Handles the action file action + /// @param watchid The watch id for the directory + /// @param dir The directory + /// @param filename The filename that was accessed (not full path) + /// @param action Action that was performed + /// @param oldFilename The name of the file or directory moved + virtual void handleFileAction( WatchID watchid, const std::string& dir, + const std::string& filename, Action action, + std::string oldFilename = "" ) = 0; +}; + +/// Optional, typically platform specific parameter for customization of a watcher. +/// @class WatcherOption +class WatcherOption { + public: + WatcherOption( Option option, int value ) : mOption( option ), mValue( value ){}; + Option mOption; + int mValue; +}; + +} // namespace efsw + +#endif diff --git a/src/3rdParty/efsw/inotify-nosys.h b/src/3rdParty/efsw/inotify-nosys.h old mode 100755 new mode 100644 diff --git a/src/3rdParty/efsw/platform/platformimpl.hpp b/src/3rdParty/efsw/platform/platformimpl.hpp old mode 100755 new mode 100644 diff --git a/src/3rdParty/efsw/platform/posix/FileSystemImpl.cpp b/src/3rdParty/efsw/platform/posix/FileSystemImpl.cpp old mode 100755 new mode 100644 diff --git a/src/3rdParty/efsw/platform/posix/FileSystemImpl.hpp b/src/3rdParty/efsw/platform/posix/FileSystemImpl.hpp old mode 100755 new mode 100644 diff --git a/src/3rdParty/efsw/platform/posix/MutexImpl.cpp b/src/3rdParty/efsw/platform/posix/MutexImpl.cpp old mode 100755 new mode 100644 diff --git a/src/3rdParty/efsw/platform/posix/MutexImpl.hpp b/src/3rdParty/efsw/platform/posix/MutexImpl.hpp old mode 100755 new mode 100644 diff --git a/src/3rdParty/efsw/platform/posix/SystemImpl.cpp b/src/3rdParty/efsw/platform/posix/SystemImpl.cpp old mode 100755 new mode 100644 diff --git a/src/3rdParty/efsw/platform/posix/SystemImpl.hpp b/src/3rdParty/efsw/platform/posix/SystemImpl.hpp old mode 100755 new mode 100644 diff --git a/src/3rdParty/efsw/platform/posix/ThreadImpl.cpp b/src/3rdParty/efsw/platform/posix/ThreadImpl.cpp old mode 100755 new mode 100644 index e0ae84f..772fbc9 --- a/src/3rdParty/efsw/platform/posix/ThreadImpl.cpp +++ b/src/3rdParty/efsw/platform/posix/ThreadImpl.cpp @@ -5,11 +5,10 @@ #include #include -#include namespace efsw { namespace Platform { -ThreadImpl::ThreadImpl( Thread* owner ) : mIsActive( false ) { +ThreadImpl::ThreadImpl( efsw::Thread* owner ) : mIsActive( false ) { mIsActive = pthread_create( &mThread, NULL, &ThreadImpl::entryPoint, owner ) == 0; if ( !mIsActive ) { @@ -17,14 +16,16 @@ ThreadImpl::ThreadImpl( Thread* owner ) : mIsActive( false ) { } } +ThreadImpl::~ThreadImpl() { + terminate(); +} + void ThreadImpl::wait() { // Wait for the thread to finish, no timeout if ( mIsActive ) { assert( pthread_equal( pthread_self(), mThread ) == 0 ); - pthread_join( mThread, NULL ); - - mIsActive = false; // Reset the thread state + mIsActive = pthread_join( mThread, NULL ) != 0; } } @@ -41,14 +42,14 @@ void ThreadImpl::terminate() { } void* ThreadImpl::entryPoint( void* userData ) { - // The Thread instance is stored in the user data - Thread* owner = static_cast( userData ); - // Tell the thread to handle cancel requests immediatly #ifdef PTHREAD_CANCEL_ASYNCHRONOUS pthread_setcanceltype( PTHREAD_CANCEL_ASYNCHRONOUS, NULL ); #endif + // The Thread instance is stored in the user data + Thread* owner = static_cast( userData ); + // Forward to the owner owner->run(); diff --git a/src/3rdParty/efsw/platform/posix/ThreadImpl.hpp b/src/3rdParty/efsw/platform/posix/ThreadImpl.hpp old mode 100755 new mode 100644 index ffc6da0..2e02f9a --- a/src/3rdParty/efsw/platform/posix/ThreadImpl.hpp +++ b/src/3rdParty/efsw/platform/posix/ThreadImpl.hpp @@ -5,6 +5,7 @@ #if defined( EFSW_PLATFORM_POSIX ) +#include #include namespace efsw { @@ -15,7 +16,9 @@ namespace Platform { class ThreadImpl { public: - ThreadImpl( Thread* owner ); + explicit ThreadImpl( efsw::Thread* owner ); + + ~ThreadImpl(); void wait(); @@ -25,7 +28,7 @@ class ThreadImpl { static void* entryPoint( void* userData ); pthread_t mThread; - bool mIsActive; + Atomic mIsActive; }; } // namespace Platform diff --git a/src/3rdParty/efsw/platform/win/FileSystemImpl.cpp b/src/3rdParty/efsw/platform/win/FileSystemImpl.cpp old mode 100755 new mode 100644 diff --git a/src/3rdParty/efsw/platform/win/FileSystemImpl.hpp b/src/3rdParty/efsw/platform/win/FileSystemImpl.hpp old mode 100755 new mode 100644 diff --git a/src/3rdParty/efsw/platform/win/MutexImpl.cpp b/src/3rdParty/efsw/platform/win/MutexImpl.cpp old mode 100755 new mode 100644 diff --git a/src/3rdParty/efsw/platform/win/MutexImpl.hpp b/src/3rdParty/efsw/platform/win/MutexImpl.hpp old mode 100755 new mode 100644 diff --git a/src/3rdParty/efsw/platform/win/SystemImpl.cpp b/src/3rdParty/efsw/platform/win/SystemImpl.cpp old mode 100755 new mode 100644 diff --git a/src/3rdParty/efsw/platform/win/SystemImpl.hpp b/src/3rdParty/efsw/platform/win/SystemImpl.hpp old mode 100755 new mode 100644 diff --git a/src/3rdParty/efsw/platform/win/ThreadImpl.cpp b/src/3rdParty/efsw/platform/win/ThreadImpl.cpp old mode 100755 new mode 100644 index d0fde8b..463934c --- a/src/3rdParty/efsw/platform/win/ThreadImpl.cpp +++ b/src/3rdParty/efsw/platform/win/ThreadImpl.cpp @@ -8,7 +8,7 @@ namespace efsw { namespace Platform { -ThreadImpl::ThreadImpl( Thread* owner ) { +ThreadImpl::ThreadImpl( efsw::Thread* owner ) { mThread = reinterpret_cast( _beginthreadex( NULL, 0, &ThreadImpl::entryPoint, owner, 0, &mThreadId ) ); diff --git a/src/3rdParty/efsw/platform/win/ThreadImpl.hpp b/src/3rdParty/efsw/platform/win/ThreadImpl.hpp old mode 100755 new mode 100644 index 1afb593..455f24c --- a/src/3rdParty/efsw/platform/win/ThreadImpl.hpp +++ b/src/3rdParty/efsw/platform/win/ThreadImpl.hpp @@ -19,7 +19,7 @@ namespace Platform { class ThreadImpl { public: - ThreadImpl( Thread* owner ); + explicit ThreadImpl( efsw::Thread* owner ); ~ThreadImpl(); diff --git a/src/3rdParty/efsw/sophist.h b/src/3rdParty/efsw/sophist.h old mode 100755 new mode 100644 index 3a64504..82e5c36 --- a/src/3rdParty/efsw/sophist.h +++ b/src/3rdParty/efsw/sophist.h @@ -1,147 +1,147 @@ -/* sophist.h - 0.3 - public domain - Sean Barrett 2010 -** Knowledge drawn from Brian Hook's posh.h and http://predef.sourceforge.net -** Sophist provides portable types; you typedef/#define them to your own names -** -** defines: -** - SOPHIST_endian - either SOPHIST_little_endian or SOPHIST_big_endian -** - SOPHIST_has_64 - either 0 or 1; if 0, int64 types aren't defined -** - SOPHIST_pointer64 - either 0 or 1; if 1, pointer is 64-bit -** -** - SOPHIST_intptr, SOPHIST_uintptr - integer same size as pointer -** - SOPHIST_int8, SOPHIST_uint8, SOPHIST_int16, SOPHIST_uint16 -** - SOPHIST_int32, SOPHIST_uint32, SOPHIST_int64, SOPHIST_uint64 -** - SOPHIST_int64_constant(number) - macros for creating 64-bit -** - SOPHIST_uint64_constant(number) integer constants -** - SOPHIST_printf_format64 - string for printf format for int64 -*/ - -#ifndef __INCLUDE_SOPHIST_H__ -#define __INCLUDE_SOPHIST_H__ - -#define SOPHIST_compiletime_assert(name,val) \ - typedef int SOPHIST__assert##name[(val) ? 1 : -1] - -/* define a couple synthetic rules to make code more readable */ -#if (defined(__sparc__) || defined(__sparc)) && \ - (defined(__arch64__) || defined(__sparcv9) || defined(__sparc_v9__)) - #define SOPHIST_sparc64 -#endif - -#if (defined(linux) || defined(__linux__)) && \ - (defined(__alpha)||defined(__alpha__)||defined(__x86_64__)||defined(_M_X64)) - #define SOPHIST_linux64 -#endif - -/* basic types */ -typedef signed char SOPHIST_int8; -typedef unsigned char SOPHIST_uint8; - -typedef signed short SOPHIST_int16; -typedef unsigned short SOPHIST_uint16; - -#ifdef __palmos__ - typedef signed long SOPHIST_int32; - typedef unsigned long SOPHIST_uint32; -#else - typedef signed int SOPHIST_int32; - typedef unsigned int SOPHIST_uint32; -#endif - -#ifndef SOPHIST_NO_64 - #if defined(_MSC_VER) || defined(__WATCOMC__) || defined(__BORLANDC__) \ - || (defined(__alpha) && defined(__DECC)) - - typedef signed __int64 SOPHIST_int64; - typedef unsigned __int64 SOPHIST_uint64; - #define SOPHIST_has_64 1 - #define SOPHIST_int64_constant(x) (x##i64) - #define SOPHIST_uint64_constant(x) (x##ui64) - #define SOPHIST_printf_format64 "I64" - - #elif defined(__LP64__) || defined(__powerpc64__) || defined(SOPHIST_sparc64) - - typedef signed long SOPHIST_int64; - typedef unsigned long SOPHIST_uint64; - - #define SOPHIST_has_64 1 - #define SOPHIST_int64_constant(x) ((SOPHIST_int64) x) - #define SOPHIST_uint64_constant(x) ((SOPHIST_uint64) x) - #define SOPHIST_printf_format64 "l" - - #elif defined(_LONG_LONG) || defined(__SUNPRO_C) || defined(__SUNPRO_CC) \ - || defined(__GNUC__) || defined(__MWERKS__) || defined(__APPLE_CC__) \ - || defined(sgi) || defined (__sgi) || defined(__sgi__) \ - || defined(_CRAYC) - - typedef signed long long SOPHIST_int64; - typedef unsigned long long SOPHIST_uint64; - - #define SOPHIST_has_64 1 - #define SOPHIST_int64_constant(x) (x##LL) - #define SOPHIST_uint64_constant(x) (x##ULL) - #define SOPHIST_printf_format64 "ll" - #endif -#endif - -#ifndef SOPHIST_has_64 -#define SOPHIST_has_64 0 -#endif - -SOPHIST_compiletime_assert( int8 , sizeof(SOPHIST_int8 ) == 1); -SOPHIST_compiletime_assert(uint16, sizeof(SOPHIST_int16) == 2); -SOPHIST_compiletime_assert( int32, sizeof(SOPHIST_int32 ) == 4); -SOPHIST_compiletime_assert(uint32, sizeof(SOPHIST_uint32) == 4); - -#if SOPHIST_has_64 - SOPHIST_compiletime_assert( int64, sizeof(SOPHIST_int64 ) == 8); - SOPHIST_compiletime_assert(uint64, sizeof(SOPHIST_uint64) == 8); -#endif - -/* determine whether pointers are 64-bit */ - -#if defined(SOPHIST_linux64) || defined(SOPHIST_sparc64) \ - || defined(__osf__) || (defined(_WIN64) && !defined(_XBOX)) \ - || defined(__64BIT__) \ - || defined(__LP64) || defined(__LP64__) || defined(_LP64) \ - || defined(_ADDR64) || defined(_CRAYC) \ - - #define SOPHIST_pointer64 1 - - SOPHIST_compiletime_assert(pointer64, sizeof(void*) == 8); - - typedef SOPHIST_int64 SOPHIST_intptr; - typedef SOPHIST_uint64 SOPHIST_uintptr; -#else - - #define SOPHIST_pointer64 0 - - SOPHIST_compiletime_assert(pointer64, sizeof(void*) <= 4); - - /* do we care about pointers that are only 16-bit? */ - typedef SOPHIST_int32 SOPHIST_intptr; - typedef SOPHIST_uint32 SOPHIST_uintptr; - -#endif - -SOPHIST_compiletime_assert(intptr, sizeof(SOPHIST_intptr) == sizeof(char *)); - -/* enumerate known little endian cases; fallback to big-endian */ - -#define SOPHIST_little_endian 1 -#define SOPHIST_big_endian 2 - -#if defined(__386__) || defined(i386) || defined(__i386__) \ - || defined(__X86) || defined(_M_IX86) \ - || defined(_M_X64) || defined(__x86_64__) \ - || defined(alpha) || defined(__alpha) || defined(__alpha__) \ - || defined(_M_ALPHA) \ - || defined(ARM) || defined(_ARM) || defined(__arm__) \ - || defined(WIN32) || defined(_WIN32) || defined(__WIN32__) \ - || defined(_WIN32_WCE) || defined(__NT__) \ - || defined(__MIPSEL__) - #define SOPHIST_endian SOPHIST_little_endian -#else - #define SOPHIST_endian SOPHIST_big_endian -#endif - -#endif /* __INCLUDE_SOPHIST_H__ */ +/* sophist.h - 0.3 - public domain - Sean Barrett 2010 +** Knowledge drawn from Brian Hook's posh.h and http://predef.sourceforge.net +** Sophist provides portable types; you typedef/#define them to your own names +** +** defines: +** - SOPHIST_endian - either SOPHIST_little_endian or SOPHIST_big_endian +** - SOPHIST_has_64 - either 0 or 1; if 0, int64 types aren't defined +** - SOPHIST_pointer64 - either 0 or 1; if 1, pointer is 64-bit +** +** - SOPHIST_intptr, SOPHIST_uintptr - integer same size as pointer +** - SOPHIST_int8, SOPHIST_uint8, SOPHIST_int16, SOPHIST_uint16 +** - SOPHIST_int32, SOPHIST_uint32, SOPHIST_int64, SOPHIST_uint64 +** - SOPHIST_int64_constant(number) - macros for creating 64-bit +** - SOPHIST_uint64_constant(number) integer constants +** - SOPHIST_printf_format64 - string for printf format for int64 +*/ + +#ifndef __INCLUDE_SOPHIST_H__ +#define __INCLUDE_SOPHIST_H__ + +#define SOPHIST_compiletime_assert(name,val) \ + typedef int SOPHIST__assert##name[(val) ? 1 : -1] + +/* define a couple synthetic rules to make code more readable */ +#if (defined(__sparc__) || defined(__sparc)) && \ + (defined(__arch64__) || defined(__sparcv9) || defined(__sparc_v9__)) + #define SOPHIST_sparc64 +#endif + +#if (defined(linux) || defined(__linux__)) && \ + (defined(__alpha)||defined(__alpha__)||defined(__x86_64__)||defined(_M_X64)) + #define SOPHIST_linux64 +#endif + +/* basic types */ +typedef signed char SOPHIST_int8; +typedef unsigned char SOPHIST_uint8; + +typedef signed short SOPHIST_int16; +typedef unsigned short SOPHIST_uint16; + +#ifdef __palmos__ + typedef signed long SOPHIST_int32; + typedef unsigned long SOPHIST_uint32; +#else + typedef signed int SOPHIST_int32; + typedef unsigned int SOPHIST_uint32; +#endif + +#ifndef SOPHIST_NO_64 + #if defined(_MSC_VER) || defined(__WATCOMC__) || defined(__BORLANDC__) \ + || (defined(__alpha) && defined(__DECC)) + + typedef signed __int64 SOPHIST_int64; + typedef unsigned __int64 SOPHIST_uint64; + #define SOPHIST_has_64 1 + #define SOPHIST_int64_constant(x) (x##i64) + #define SOPHIST_uint64_constant(x) (x##ui64) + #define SOPHIST_printf_format64 "I64" + + #elif defined(__LP64__) || defined(__powerpc64__) || defined(SOPHIST_sparc64) + + typedef signed long SOPHIST_int64; + typedef unsigned long SOPHIST_uint64; + + #define SOPHIST_has_64 1 + #define SOPHIST_int64_constant(x) ((SOPHIST_int64) x) + #define SOPHIST_uint64_constant(x) ((SOPHIST_uint64) x) + #define SOPHIST_printf_format64 "l" + + #elif defined(_LONG_LONG) || defined(__SUNPRO_C) || defined(__SUNPRO_CC) \ + || defined(__GNUC__) || defined(__MWERKS__) || defined(__APPLE_CC__) \ + || defined(sgi) || defined (__sgi) || defined(__sgi__) \ + || defined(_CRAYC) + + typedef signed long long SOPHIST_int64; + typedef unsigned long long SOPHIST_uint64; + + #define SOPHIST_has_64 1 + #define SOPHIST_int64_constant(x) (x##LL) + #define SOPHIST_uint64_constant(x) (x##ULL) + #define SOPHIST_printf_format64 "ll" + #endif +#endif + +#ifndef SOPHIST_has_64 +#define SOPHIST_has_64 0 +#endif + +SOPHIST_compiletime_assert( int8 , sizeof(SOPHIST_int8 ) == 1); +SOPHIST_compiletime_assert(uint16, sizeof(SOPHIST_int16) == 2); +SOPHIST_compiletime_assert( int32, sizeof(SOPHIST_int32 ) == 4); +SOPHIST_compiletime_assert(uint32, sizeof(SOPHIST_uint32) == 4); + +#if SOPHIST_has_64 + SOPHIST_compiletime_assert( int64, sizeof(SOPHIST_int64 ) == 8); + SOPHIST_compiletime_assert(uint64, sizeof(SOPHIST_uint64) == 8); +#endif + +/* determine whether pointers are 64-bit */ + +#if defined(SOPHIST_linux64) || defined(SOPHIST_sparc64) \ + || defined(__osf__) || (defined(_WIN64) && !defined(_XBOX)) \ + || defined(__64BIT__) \ + || defined(__LP64) || defined(__LP64__) || defined(_LP64) \ + || defined(_ADDR64) || defined(_CRAYC) \ + + #define SOPHIST_pointer64 1 + + SOPHIST_compiletime_assert(pointer64, sizeof(void*) == 8); + + typedef SOPHIST_int64 SOPHIST_intptr; + typedef SOPHIST_uint64 SOPHIST_uintptr; +#else + + #define SOPHIST_pointer64 0 + + SOPHIST_compiletime_assert(pointer64, sizeof(void*) <= 4); + + /* do we care about pointers that are only 16-bit? */ + typedef SOPHIST_int32 SOPHIST_intptr; + typedef SOPHIST_uint32 SOPHIST_uintptr; + +#endif + +SOPHIST_compiletime_assert(intptr, sizeof(SOPHIST_intptr) == sizeof(char *)); + +/* enumerate known little endian cases; fallback to big-endian */ + +#define SOPHIST_little_endian 1 +#define SOPHIST_big_endian 2 + +#if defined(__386__) || defined(i386) || defined(__i386__) \ + || defined(__X86) || defined(_M_IX86) \ + || defined(_M_X64) || defined(__x86_64__) \ + || defined(alpha) || defined(__alpha) || defined(__alpha__) \ + || defined(_M_ALPHA) \ + || defined(ARM) || defined(_ARM) || defined(__arm__) \ + || defined(WIN32) || defined(_WIN32) || defined(__WIN32__) \ + || defined(_WIN32_WCE) || defined(__NT__) \ + || defined(__MIPSEL__) + #define SOPHIST_endian SOPHIST_little_endian +#else + #define SOPHIST_endian SOPHIST_big_endian +#endif + +#endif /* __INCLUDE_SOPHIST_H__ */ -- cgit v1.2.3-55-g6feb