summaryrefslogtreecommitdiff
path: root/source/common/XMP_LibUtils.hpp
diff options
context:
space:
mode:
Diffstat (limited to 'source/common/XMP_LibUtils.hpp')
-rw-r--r--source/common/XMP_LibUtils.hpp520
1 files changed, 520 insertions, 0 deletions
diff --git a/source/common/XMP_LibUtils.hpp b/source/common/XMP_LibUtils.hpp
new file mode 100644
index 0000000..7697de3
--- /dev/null
+++ b/source/common/XMP_LibUtils.hpp
@@ -0,0 +1,520 @@
+#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 "XMP_Environment.h" // ! Must be the first include.
+#include "XMP_Const.h"
+
+#include <map>
+#include <string>
+
+#if XMP_DebugBuild
+ #include <cassert>
+#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)
+
+typedef std::string XMP_VarString;
+
+#define EliminateGlobal(g) delete ( g ); g = 0
+
+extern "C" bool Initialize_LibUtils();
+extern "C" void Terminate_LibUtils();
+
+static void * ignorePtr = 0;
+#define IgnoreParam(p) ignorePtr = (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 ); }
+
+// -------------------------------------------------------------------------------------------------
+
+#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 ); \
+ }
+
+#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 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 UseHomeGrownLock 1
+
+// -------------------------------------------------------------------------------------------------
+// A basic exclusive access mutex and atomic increment/decrement operations.
+
+#if XMP_WinBuild
+
+ #include <Windows.h>
+
+ #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
+
+ #include <pthread.h>
+ #include <libkern/OSAtomic.h>
+
+ #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 <pthread.h>
+
+ // 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 ); }
+
+#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 <boost/thread/shared_mutex.hpp>
+ 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 <pthread.h>
+ 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 <Windows.h>
+ 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
+
+ #include <pthread.h>
+
+ typedef pthread_cond_t XMP_BasicQueue;
+
+ #elif XMP_WinBuild
+
+ #include <Windows.h>
+ #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 */ \
+ 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 */ \
+ XMP_Status status = (*outProc) ( refCon, buffer, (XMP_StringLen)strlen(buffer) ); if ( status != 0 ) return; }
+
+#define OutProcHexByte(num) { snprintf ( buffer, sizeof(buffer), "%.2X", (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::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;
+
+};
+
+// =================================================================================================
+
+#endif // __XMP_LibUtils_hpp__