#ifndef __XMP_LibUtils_hpp__ #define __XMP_LibUtils_hpp__ 1 // ================================================================================================= // Copyright 2009 Adobe Systems Incorporated // All Rights Reserved. // // NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms // of the Adobe license agreement accompanying it. // ================================================================================================= #include "public/include/XMP_Environment.h" // ! Must be the first include. #include "public/include/XMP_Const.h" #include #include #include #if XMP_DebugBuild #include #endif #if XMP_WinBuild #ifndef snprintf #define snprintf _snprintf #endif #endif // ================================================================================================= // Basic types, constants // ====================== #define kTab ((char)0x09) #define kLF ((char)0x0A) #define kCR ((char)0x0D) #if XMP_WinBuild #define kDirChar '\\' #else #define kDirChar '/' #endif typedef std::string XMP_VarString; #define EliminateGlobal(g) delete ( g ); g = 0 extern "C" bool Initialize_LibUtils(); extern "C" void Terminate_LibUtils(); #define IgnoreParam(p) (void)p // The builtin offsetof macro sometimes violates C++ data member rules. #define XMP_OffsetOf(struct,field) ( (char*)(&((struct*)0x100)->field) - (char*)0x100 ) // ================================================================================================= // Support for exceptions and asserts // ================================== #define AnnounceThrow(msg) /* Do nothing. */ #define AnnounceCatch(msg) /* Do nothing. */ #define XMP_Throw(msg,id) { AnnounceThrow ( msg ); throw XMP_Error ( id, msg ); } #if XMP_DebugBuild #define XMP_Throw_Verbose(msg,e,id) \ { \ char tmpMsg[255]; \ snprintf(tmpMsg, sizeof(tmpMsg), #msg "( %d )", e); \ XMP_Throw( tmpMsg, id); \ } #else #define XMP_Throw_Verbose(msg,e,id) XMP_Throw(msg, id) #endif class GenericErrorCallback { public: // Abstract base class for XMPCore and XMPFiles internal error notification support. Needed so // that the XMLParserAdapter (used by both XMPCore and XMPFiles) can send error notifications, // and so that utility parts of just XMPCore or XMPFiles can avoid dependence on XMPCore.hpp or // XMPFiles.hpp if that is appropriate. XMP_Uns32 limit; mutable XMP_Uns32 notifications; mutable XMP_ErrorSeverity topSeverity; GenericErrorCallback() : notifications(0), limit(1), topSeverity(kXMPErrSev_Recoverable) {}; virtual ~GenericErrorCallback() {}; void Clear() { this->notifications = 0; this->limit = 1; this->topSeverity = kXMPErrSev_Recoverable; }; bool CheckLimitAndSeverity (XMP_ErrorSeverity severity ) const; // Const so they can be used with const XMPMeta and XMPFiles objects. void NotifyClient ( XMP_ErrorSeverity severity, XMP_Error & error, XMP_StringPtr filePath = 0 ) const; virtual bool CanNotify ( ) const = 0; virtual bool ClientCallbackWrapper ( XMP_StringPtr filePath, XMP_ErrorSeverity severity, XMP_Int32 cause, XMP_StringPtr messsage ) const = 0; }; #define XMP_Error_Throw(error) { AnnounceThrow (error.GetErrMsg()); throw error; } // ------------------------------------------------------------------------------------------------- #define _MakeStr(p) #p #define _NotifyMsg(n,c,f,l) #n " failed: " #c " in " f " at line " _MakeStr(l) #define _ExplicitMsg(msg,c,e) #e " " #msg ": " #c #define XMP_Validate(c,msg,e) \ if ( ! (c) ) { \ const char * validate_msg = _ExplicitMsg ( msg, c, e ); \ XMP_Throw ( validate_msg, e ); \ } // This statement is needed in XMP_Assert definition to reduce warnings from // static analysis tool in Visual Studio. Defined here, as platform fork not // possible within macro definition below #if XMP_WinBuild #define analysis_assume(c) __analysis_assume( c ); #else #define analysis_assume(c) ((void) 0) #endif #if ! XMP_DebugBuild #define XMP_Assert(c) ((void) 0) #else #define XMP_Assert(c) assert ( c ) #endif #define XMP_Enforce(c) \ if ( ! (c) ) { \ const char * assert_msg = _NotifyMsg ( XMP_Enforce, (c), __FILE__, __LINE__ ); \ XMP_Throw ( assert_msg , kXMPErr_EnforceFailure ); \ } // ================================================================================================= // Thread synchronization locks // ============================ // About XMP and thread synchronization // // A variety of choices are provided for thread synchronization. Exactly one method must be chosen // by defining the appropriate symbol to 1. // // * UseNoLock - This choice turns the synchronization functions into no-ops. It must only be used // by single threaded clients, or clients providing their own control at a higher level. // // * UseGlobalLibraryLock - This choice uses a single per-library lock. The result is thread safe // but unfriendly behavior, no true concurrency. This should only be used as a debugging fallback. // // * UseBoostLock - This choice uses the Boost shared_mutex mechanism. It has the advantage of being // robust and being available on pretty much all platforms. It has the disadvantage of requiring // the developer to download, integrate, and build the Boost thread library. // // * UsePThreadLock - This choice uses the POSIX pthread rwlock mechanism. It has the advantage of // being robust and being available on any modern UNIX platform, including Mac OS X. // // * UseWinSlimLock - This choice uses the Windows slim reader/writer mechanism. It is robust but // only available on Vista and newer versions of Windows, it is not available on XP. // // * UseHomeGrownLock - This choice uses local code plus lower level synchronization primitives. It // has the advantage of being usable on all platforms, and having exposed and tunable policy. It // has the disadvantage of possibly being less robust than Boost or the O/S provided mechanisms. // The lower level synchronization primitives are pthread mutex and condition for UNIX (including // Mac OS X). For Windows there is a choice of critical section and condition variable for Vista // and newer; or critical section, event, and semaphore for XP and newer. #define UseNoLock 1 // ------------------------------------------------------------------------------------------------- // A basic exclusive access mutex and atomic increment/decrement operations. #if XMP_WinBuild #include #define HaveAtomicIncrDecr 1 typedef LONG XMP_AtomicCounter; #define XMP_AtomicIncrement(x) InterlockedIncrement ( &(x) ) #define XMP_AtomicDecrement(x) InterlockedDecrement ( &(x) ) typedef CRITICAL_SECTION XMP_BasicMutex; #define InitializeBasicMutex(mutex) { InitializeCriticalSection ( &mutex ); } #define TerminateBasicMutex(mutex) { DeleteCriticalSection ( &mutex ); } #define AcquireBasicMutex(mutex) { EnterCriticalSection ( &mutex ); } #define ReleaseBasicMutex(mutex) { LeaveCriticalSection ( &mutex ); } #elif XMP_MacBuild | XMP_iOSBuild #include #include #define HaveAtomicIncrDecr 1 typedef int32_t XMP_AtomicCounter; #define XMP_AtomicIncrement(x) OSAtomicIncrement32 ( &(x) ) #define XMP_AtomicDecrement(x) OSAtomicDecrement32 ( &(x) ) typedef pthread_mutex_t XMP_BasicMutex; #define InitializeBasicMutex(mutex) { int err = pthread_mutex_init ( &mutex, 0 ); XMP_Enforce ( err == 0 ); } #define TerminateBasicMutex(mutex) { int err = pthread_mutex_destroy ( &mutex ); XMP_Enforce ( err == 0 ); } #define AcquireBasicMutex(mutex) { int err = pthread_mutex_lock ( &mutex ); XMP_Enforce ( err == 0 ); } #define ReleaseBasicMutex(mutex) { int err = pthread_mutex_unlock ( &mutex ); XMP_Enforce ( err == 0 ); } #elif XMP_UNIXBuild #include // Atomic increment/decrement intrinsics should be in gcc 4.1, but Solaris seems to lack them. #ifndef HaveAtomicIncrDecr #define HaveAtomicIncrDecr 1 #endif #if HaveAtomicIncrDecr typedef XMP_Uns32 XMP_AtomicCounter; #define XMP_AtomicIncrement(x) __sync_add_and_fetch ( &(x), 1 ) #define XMP_AtomicDecrement(x) __sync_sub_and_fetch ( &(x), 1 ) #endif typedef pthread_mutex_t XMP_BasicMutex; #define InitializeBasicMutex(mutex) { int err = pthread_mutex_init ( &mutex, 0 ); XMP_Enforce ( err == 0 ); } #define TerminateBasicMutex(mutex) { int err = pthread_mutex_destroy ( &mutex ); XMP_Enforce ( err == 0 ); } #define AcquireBasicMutex(mutex) { int err = pthread_mutex_lock ( &mutex ); XMP_Enforce ( err == 0 ); } #define ReleaseBasicMutex(mutex) { int err = pthread_mutex_unlock ( &mutex ); XMP_Enforce ( err == 0 ); } #elif XMP_BanzaiBuild #include // Atomic increment/decrement intrinsics should be in gcc 4.1, but Solaris seems to lack them. #ifndef HaveAtomicIncrDecr #define HaveAtomicIncrDecr 1 #endif #if HaveAtomicIncrDecr typedef XMP_Uns32 XMP_AtomicCounter; #define XMP_AtomicIncrement(x) __sync_add_and_fetch ( &(x), 1 ) #define XMP_AtomicDecrement(x) __sync_sub_and_fetch ( &(x), 1 ) #endif typedef rtos_mutex_t XMP_BasicMutex; #define InitializeBasicMutex(mutex) { int err = rtos_mutex_create ( &mutex, "mutex"); XMP_Enforce ( err == 0 ); } #define TerminateBasicMutex(mutex) { int err = rtos_mutex_delete ( &mutex ); XMP_Enforce ( err == 0 ); } #define AcquireBasicMutex(mutex) { int err = rtos_mutex_acquire ( &mutex, RTOS_WAIT_FOREVER ); XMP_Enforce ( err == 0 ); } #define ReleaseBasicMutex(mutex) { int err = rtos_mutex_release ( &mutex ); XMP_Enforce ( err == 0 ); } #endif class XMP_AutoMutex { public: XMP_AutoMutex ( XMP_BasicMutex * _mutex ) : mutex(_mutex) { AcquireBasicMutex ( *this->mutex ); } ~XMP_AutoMutex() { this->Release(); } void Release() { if ( this->mutex != 0 ) ReleaseBasicMutex ( *this->mutex ); this->mutex = 0; } private: XMP_BasicMutex * mutex; XMP_AutoMutex() {}; // ! Must not be used. }; // ------------------------------------------------------------------------------------------------- // Details for the various locking mechanisms. #if UseNoLock typedef void* XMP_BasicRWLock; // For single threaded clients that want maximum performance. #define XMP_BasicRWLock_Initialize(lck) /* Do nothing. */ #define XMP_BasicRWLock_Terminate(lck) /* Do nothing. */ #define XMP_BasicRWLock_AcquireForRead(lck) /* Do nothing. */ #define XMP_BasicRWLock_AcquireForWrite(lck) /* Do nothing. */ #define XMP_BasicRWLock_ReleaseFromRead(lck) /* Do nothing. */ #define XMP_BasicRWLock_ReleaseFromWrite(lck) /* Do nothing. */ #elif UseGlobalLibraryLock extern XMP_BasicMutex sLibraryLock; typedef void* XMP_BasicRWLock; // Use the old thread-unfriendly per-DLL mutex. #define XMP_BasicRWLock_Initialize(lck) /* Do nothing. */ #define XMP_BasicRWLock_Terminate(lck) /* Do nothing. */ #define XMP_BasicRWLock_AcquireForRead(lck) /* Do nothing. */ #define XMP_BasicRWLock_AcquireForWrite(lck) /* Do nothing. */ #define XMP_BasicRWLock_ReleaseFromRead(lck) /* Do nothing. */ #define XMP_BasicRWLock_ReleaseFromWrite(lck) /* Do nothing. */ #elif UseBoostLock #include typedef boost::shared_mutex XMP_BasicRWLock; #define XMP_BasicRWLock_Initialize(lck) /* Do nothing. */ #define XMP_BasicRWLock_Terminate(lck) /* Do nothing. */ #define XMP_BasicRWLock_AcquireForRead(lck) lck.lock_shared() #define XMP_BasicRWLock_AcquireForWrite(lck) lck.lock() #define XMP_BasicRWLock_ReleaseFromRead(lck) lck.unlock_shared() #define XMP_BasicRWLock_ReleaseFromWrite(lck) lck.unlock() #elif UsePThreadLock #include typedef pthread_rwlock_t XMP_BasicRWLock; #define XMP_BasicRWLock_Initialize(lck) \ { int err = pthread_rwlock_init ( &lck, 0 ); \ if ( err != 0 ) XMP_Throw ( "Initialize pthread rwlock failed", kXMPErr_ExternalFailure ); } #define XMP_BasicRWLock_Terminate(lck) \ { int err = pthread_rwlock_destroy ( &lck ); XMP_Assert ( err == 0 ); } #define XMP_BasicRWLock_AcquireForRead(lck) \ { int err = pthread_rwlock_rdlock ( &lck ); \ if ( err != 0 ) XMP_Throw ( "Acquire pthread read lock failed", kXMPErr_ExternalFailure ); } #define XMP_BasicRWLock_AcquireForWrite(lck) \ { int err = pthread_rwlock_wrlock ( &lck ); \ if ( err != 0 ) XMP_Throw ( "Acquire pthread write lock failed", kXMPErr_ExternalFailure ); } #define XMP_BasicRWLock_ReleaseFromRead(lck) \ { int err = pthread_rwlock_unlock ( &lck ); \ if ( err != 0 ) XMP_Throw ( "Release pthread read lock failed", kXMPErr_ExternalFailure ); } #define XMP_BasicRWLock_ReleaseFromWrite(lck) \ { int err = pthread_rwlock_unlock ( &lck ); \ if ( err != 0 ) XMP_Throw ( "Release pthread write lock failed", kXMPErr_ExternalFailure ); } #elif UseWinSlimLock #include typedef SRWLOCK XMP_BasicRWLock; #define XMP_BasicRWLock_Initialize(lck) InitializeSRWLock ( &lck ) #define XMP_BasicRWLock_Terminate(lck) /* Do nothing. */ #define XMP_BasicRWLock_AcquireForRead(lck) AcquireSRWLockShared ( &lck ) #define XMP_BasicRWLock_AcquireForWrite(lck) AcquireSRWLockExclusive ( &lck ) #define XMP_BasicRWLock_ReleaseFromRead(lck) ReleaseSRWLockShared ( &lck ) #define XMP_BasicRWLock_ReleaseFromWrite(lck) ReleaseSRWLockExclusive ( &lck ) #elif UseHomeGrownLock class XMP_HomeGrownLock; typedef XMP_HomeGrownLock XMP_BasicRWLock; #define XMP_BasicRWLock_Initialize(lck) /* Do nothing. */ #define XMP_BasicRWLock_Terminate(lck) /* Do nothing. */ #define XMP_BasicRWLock_AcquireForRead(lck) lck.AcquireForRead() #define XMP_BasicRWLock_AcquireForWrite(lck) lck.AcquireForWrite() #define XMP_BasicRWLock_ReleaseFromRead(lck) lck.ReleaseFromRead() #define XMP_BasicRWLock_ReleaseFromWrite(lck) lck.ReleaseFromWrite() #if XMP_MacBuild | XMP_UNIXBuild | XMP_iOSBuild #include typedef pthread_cond_t XMP_BasicQueue; #elif XMP_WinBuild #include #ifndef BuildLocksForWinXP #define BuildLocksForWinXP 1 #endif #if ! BuildLocksForWinXP typedef CONDITION_VARIABLE XMP_BasicQueue; // ! Requires Vista or newer. #else class XMP_WinXP_HGQueue { public: XMP_WinXP_HGQueue(); ~XMP_WinXP_HGQueue(); void Wait ( XMP_BasicMutex & queueMutex ); void ReleaseOne(); void ReleaseAll(); private: HANDLE queueEvent; volatile XMP_Uns32 waitCount; // ! Does not need to be XMP_AtomicCounter. volatile bool releaseAll; }; typedef XMP_WinXP_HGQueue XMP_BasicQueue; #endif #endif class XMP_HomeGrownLock { public: XMP_HomeGrownLock(); ~XMP_HomeGrownLock(); void AcquireForRead(); void AcquireForWrite(); void ReleaseFromRead(); void ReleaseFromWrite(); private: XMP_BasicMutex queueMutex; // Used to protect queueing operations. XMP_BasicQueue readerQueue, writerQueue; volatile XMP_Uns32 lockCount, readersWaiting, writersWaiting; // ! Does not need to be XMP_AtomicCounter. volatile bool beingWritten; }; #else #error "No locking mechanism chosen" #endif class XMP_ReadWriteLock { // For the lock objects, use XMP_AutoLock to do the locking. public: XMP_ReadWriteLock(); ~XMP_ReadWriteLock(); void Acquire ( bool forWriting ); void Release(); private: XMP_BasicRWLock lock; #if XMP_DebugBuild && HaveAtomicIncrDecr volatile XMP_AtomicCounter lockCount; // ! Only for debug checks, must be XMP_AtomicCounter. #endif volatile bool beingWritten; }; #define kXMP_ReadLock false #define kXMP_WriteLock true class XMP_AutoLock { public: XMP_AutoLock ( const XMP_ReadWriteLock * _lock, bool forWriting, bool cond = true ) : lock(0) { if ( cond ) { // The cast below is needed because the _lock parameter might come from something // like "const XMPMeta &", which would make the lock itself const. But we need to // modify the lock (to acquire and release) even if the owning object is const. this->lock = (XMP_ReadWriteLock*)_lock; this->lock->Acquire ( forWriting ); } } ~XMP_AutoLock() { this->Release(); } void Release() { if ( this->lock != 0 ) this->lock->Release(); this->lock = 0; } private: XMP_ReadWriteLock * lock; XMP_AutoLock() {}; // ! Must not be used. }; // ================================================================================================= // Support for wrappers // ==================== #define AnnounceStaticEntry(proc) /* Do nothing. */ #define AnnounceObjectEntry(proc,rwMode) /* Do nothing. */ #define AnnounceExit() /* Do nothing. */ // ------------------------------------------------------------------------------------------------- #if UseGlobalLibraryLock #define AcquireLibraryLock(lck) XMP_AutoMutex libLock ( &lck ) #else #define AcquireLibraryLock(lck) /* nothing */ #endif #define XMP_ENTER_NoLock(Proc) \ AnnounceStaticEntry ( Proc ); \ try { \ wResult->errMessage = 0; #define XMP_ENTER_Static(Proc) \ AnnounceStaticEntry ( Proc ); \ AcquireLibraryLock ( sLibraryLock ); \ try { \ wResult->errMessage = 0; #define XMP_ENTER_ObjRead(XMPClass,Proc) \ AnnounceObjectEntry ( Proc, "reader" ); \ AcquireLibraryLock ( sLibraryLock ); \ const XMPClass & thiz = *((XMPClass*)xmpObjRef); \ XMP_AutoLock objLock ( &thiz.lock, kXMP_ReadLock ); \ try { \ wResult->errMessage = 0; #define XMP_ENTER_ObjWrite(XMPClass,Proc) \ AnnounceObjectEntry ( Proc, "writer" ); \ AcquireLibraryLock ( sLibraryLock ); \ XMPClass * thiz = (XMPClass*)xmpObjRef; \ XMP_AutoLock objLock ( &thiz->lock, kXMP_WriteLock ); \ try { \ wResult->errMessage = 0; #define XMP_EXIT \ XMP_CATCH_EXCEPTIONS \ AnnounceExit(); #define XMP_EXIT_NoThrow \ } catch ( ... ) { \ AnnounceCatch ( "no-throw catch-all" ); \ /* Do nothing. */ \ } \ AnnounceExit(); #define XMP_CATCH_EXCEPTIONS \ } catch ( XMP_Error & xmpErr ) { \ wResult->int32Result = xmpErr.GetID(); \ wResult->ptrResult = (void*)"XMP"; \ wResult->errMessage = xmpErr.GetErrMsg(); \ if ( wResult->errMessage == 0 ) wResult->errMessage = ""; \ AnnounceCatch ( wResult->errMessage ); \ } catch ( std::exception & stdErr ) { \ wResult->int32Result = kXMPErr_StdException; \ wResult->errMessage = stdErr.what(); \ if ( wResult->errMessage == 0 ) wResult->errMessage = ""; \ AnnounceCatch ( wResult->errMessage ); \ } catch ( ... ) { \ wResult->int32Result = kXMPErr_UnknownException; \ wResult->errMessage = "Caught unknown exception"; \ AnnounceCatch ( wResult->errMessage ); \ } #if XMP_DebugBuild #define RELEASE_NO_THROW /* empty */ #else #define RELEASE_NO_THROW throw() #endif // ================================================================================================= // Data structure dumping utilities // ================================ #define IsHexDigit(ch) ( (('0' <= (ch)) && ((ch) <= '9')) || (('A' <= (ch)) && ((ch) <= 'F')) ) #define HexDigitValue(ch) ( (((ch) - '0') < 10) ? ((ch) - '0') : ((ch) - 'A' + 10) ) static const char * kTenSpaces = " "; #define OutProcPadding(pad) { size_t padLen = (pad); \ for ( ; padLen >= 10; padLen -= 10 ) OutProcNChars ( kTenSpaces, 10 ); \ for ( ; padLen > 0; padLen -= 1 ) OutProcNChars ( " ", 1 ); } #define OutProcNewline() { XMP_Status status = (*outProc) ( refCon, "\n", 1 ); if ( status != 0 ) return; } #define OutProcNChars(p,n) { XMP_Status status = (*outProc) ( refCon, (p), (n) ); if ( status != 0 ) return; } #define OutProcLiteral(lit) { XMP_Status _status = (*outProc) ( refCon, (lit), (XMP_StringLen)strlen(lit) ); if ( _status != 0 ) return; } #define OutProcString(str) { XMP_Status _status = (*outProc) ( refCon, (str).c_str(), (XMP_StringLen)(str).size() ); if ( _status != 0 ) return; } #define OutProcDecInt(num) { snprintf ( buffer, sizeof(buffer), "%ld", (long)(num) ); /* AUDIT: Using sizeof for snprintf length is safe */ \ buffer[sizeof(buffer) -1] = 0; /* AUDIT warning C6053: Make sure buffer is terminated */ \ XMP_Status _status = (*outProc) ( refCon, buffer, (XMP_StringLen)strlen(buffer) ); if ( _status != 0 ) return; } #define OutProcHexInt(num) { snprintf ( buffer, sizeof(buffer), "%lX", (long)(num) ); /* AUDIT: Using sizeof for snprintf length is safe */ \ buffer[sizeof(buffer) -1] = 0; /* AUDIT warning C6053: Make sure buffer is terminated */ \ XMP_Status _status = (*outProc) ( refCon, buffer, (XMP_StringLen)strlen(buffer) ); if ( _status != 0 ) return; } #define OutProcHexByte(num) { snprintf ( buffer, sizeof(buffer), "%.2X", (unsigned char)(num) ); /* AUDIT: Using sizeof for snprintf length is safe */ \ XMP_Status _status = (*outProc) ( refCon, buffer, (XMP_StringLen)strlen(buffer) ); if ( _status != 0 ) return; } static const char * kIndent = " "; #define OutProcIndent(lev) { for ( size_t i = 0; i < (lev); ++i ) OutProcNChars ( kIndent, 3 ); } void DumpClearString ( const XMP_VarString & value, XMP_TextOutputProc outProc, void * refCon ); // ================================================================================================= // Namespace Tables // ================ typedef std::vector XMP_StringVector; typedef XMP_StringVector::iterator XMP_StringVectorPos; typedef XMP_StringVector::const_iterator XMP_StringVectorCPos; typedef std::pair < XMP_VarString, XMP_VarString > XMP_StringPair; typedef std::map < XMP_VarString, XMP_VarString > XMP_StringMap; typedef XMP_StringMap::iterator XMP_StringMapPos; typedef XMP_StringMap::const_iterator XMP_cStringMapPos; class XMP_NamespaceTable { public: XMP_NamespaceTable() {}; XMP_NamespaceTable ( const XMP_NamespaceTable & presets ); virtual ~XMP_NamespaceTable() {}; bool Define ( XMP_StringPtr uri, XMP_StringPtr suggPrefix, XMP_StringPtr * prefixPtr, XMP_StringLen * prefixLen); bool GetPrefix ( XMP_StringPtr uri, XMP_StringPtr * prefixPtr, XMP_StringLen * prefixLen ) const; bool GetURI ( XMP_StringPtr prefix, XMP_StringPtr * uriPtr, XMP_StringLen * uriLen ) const; void Dump ( XMP_TextOutputProc outProc, void * refCon ) const; private: XMP_ReadWriteLock lock; XMP_StringMap uriToPrefixMap, prefixToURIMap; }; // Right now it supports only ^, $ and \d, in future we should use it as a wrapper over // regex object once mac and Linux compilers start supporting them. class XMP_RegExp { public: XMP_RegExp ( XMP_StringPtr regExp ) { if ( regExp ) regExpStr = regExp; } XMP_Bool Match ( XMP_StringPtr s ); private: XMP_VarString regExpStr; }; // ================================================================================================= #endif // __XMP_LibUtils_hpp__