1 #ifndef __XMP_LibUtils_hpp__
2 #define __XMP_LibUtils_hpp__ 1
3 
4 // =================================================================================================
5 // Copyright 2009 Adobe Systems Incorporated
6 // All Rights Reserved.
7 //
8 // NOTICE:  Adobe permits you to use, modify, and distribute this file in accordance with the terms
9 // of the Adobe license agreement accompanying it.
10 // =================================================================================================
11 
12 #include "public/include/XMP_Environment.h"	// ! Must be the first include.
13 #include "public/include/XMP_Const.h"
14 
15 #include <map>
16 #include <string>
17 #include <vector>
18 
19 #if XMP_DebugBuild
20 	#include <cassert>
21 #endif
22 
23 #if XMP_WinBuild
24 	#ifndef snprintf
25 		#define snprintf _snprintf
26 	#endif
27 #endif
28 
29 // =================================================================================================
30 // Basic types, constants
31 // ======================
32 
33 #define kTab ((char)0x09)
34 #define kLF ((char)0x0A)
35 #define kCR ((char)0x0D)
36 
37 #if XMP_WinBuild
38 	#define kDirChar '\\'
39 #else
40 	#define kDirChar '/'
41 #endif
42 
43 typedef std::string XMP_VarString;
44 
45 #define EliminateGlobal(g) delete ( g ); g = 0
46 
47 extern "C" bool Initialize_LibUtils();
48 extern "C" void Terminate_LibUtils();
49 
50 #define IgnoreParam(p)	(void)p
51 
52 // The builtin offsetof macro sometimes violates C++ data member rules.
53 #define XMP_OffsetOf(struct,field)	( (char*)(&((struct*)0x100)->field) - (char*)0x100 )
54 
55 // =================================================================================================
56 // Support for exceptions and asserts
57 // ==================================
58 
59 #define AnnounceThrow(msg)		/* Do nothing. */
60 #define AnnounceCatch(msg)		/* Do nothing. */
61 
62 #define XMP_Throw(msg,id)	{ AnnounceThrow ( msg ); throw XMP_Error ( id, msg ); }
63 
64 #if XMP_DebugBuild
65 #define XMP_Throw_Verbose(msg,e,id)						\
66 {														\
67 	char tmpMsg[255];									\
68 	snprintf(tmpMsg, sizeof(tmpMsg), #msg "( %d )", e);	\
69 	XMP_Throw( tmpMsg, id);								\
70 }
71 #else
72 	#define XMP_Throw_Verbose(msg,e,id) XMP_Throw(msg, id)
73 #endif
74 
75 #define XMP_Error_Throw(error)	{ AnnounceThrow (error.GetErrMsg()); throw error; }
76 
77 class GenericErrorCallback {
78 public:
79 	// Abstract base class for XMPCore and XMPFiles internal error notification support. Needed so
80 	// that the XMLParserAdapter (used by both XMPCore and XMPFiles) can send error notifications,
81 	// and so that utility parts of just XMPCore or XMPFiles can avoid dependence on XMPCore.hpp or
82 	// XMPFiles.hpp if that is appropriate.
83 
84 	XMP_Uns32					limit;
85 	mutable XMP_Uns32			notifications;
86 	mutable XMP_ErrorSeverity	topSeverity;
87 
GenericErrorCallback()88 	GenericErrorCallback() : limit(1), notifications(0), topSeverity(kXMPErrSev_Recoverable) {};
~GenericErrorCallback()89 	virtual ~GenericErrorCallback() {};
90 
Clear()91 	void Clear() { this->notifications = 0; this->limit = 1; this->topSeverity = kXMPErrSev_Recoverable; };
92 
CheckLimitAndSeverity(XMP_ErrorSeverity severity) const93 	bool CheckLimitAndSeverity (XMP_ErrorSeverity severity ) const
94 	{
95 
96 		if ( this->limit == 0 ) return true;	// Always notify if the limit is zero.
97 		if ( severity < this->topSeverity ) return false;	// Don't notify, don't count.
98 
99 		if ( severity > this->topSeverity ) {
100 			this->topSeverity = severity;
101 			this->notifications = 0;
102 		}
103 
104 		this->notifications += 1;
105 		return (this->notifications <= this->limit);
106 
107 	}	// GenericErrorCallback::CheckLimitAndSeverity
108 
109 	// Const so they can be used with const XMPMeta and XMPFiles objects.
NotifyClient(XMP_ErrorSeverity severity,XMP_Error & error,XMP_StringPtr filePath=0) const110 	void NotifyClient ( XMP_ErrorSeverity severity, XMP_Error & error, XMP_StringPtr filePath = 0 ) const
111 	{
112 
113 		bool notifyClient = CanNotify() && !error.IsNotified();
114 		bool returnAndRecover (severity == kXMPErrSev_Recoverable);
115 
116 		if ( notifyClient ) {
117 			error.SetNotified();
118 			notifyClient = CheckLimitAndSeverity ( severity );
119 			if ( notifyClient ) {
120 				returnAndRecover &= ClientCallbackWrapper( filePath, severity, error.GetID(), error.GetErrMsg() );
121 			}
122 		}
123 
124 		if ( ! returnAndRecover ) XMP_Error_Throw ( error );
125 
126 	}	// GenericErrorCallback::NotifyClient
127 
128 	virtual bool CanNotify ( ) const = 0;
129 	virtual bool ClientCallbackWrapper ( XMP_StringPtr filePath, XMP_ErrorSeverity severity, XMP_Int32 cause, XMP_StringPtr messsage ) const = 0;
130 
131 };
132 
133 // -------------------------------------------------------------------------------------------------
134 
135 struct ErrorCallbackBox
136 {
137 	XMPFiles_ErrorCallbackWrapper	wrapperProc;
138 	XMPFiles_ErrorCallbackProc	clientProc;
139 	void * context;
140 	XMP_Uns32 limit;
141 
ErrorCallbackBoxErrorCallbackBox142 	ErrorCallbackBox( XMPFiles_ErrorCallbackWrapper	wrapperProcedure,
143 	XMPFiles_ErrorCallbackProc	clientProcedure,
144 	void * contextPtr,
145 	XMP_Uns32 limit32 ): wrapperProc(wrapperProcedure), clientProc(clientProcedure), context(contextPtr), limit(limit32) { }
146 };
147 
148 // -------------------------------------------------------------------------------------------------
149 
150 #define _MakeStr(p) #p
151 #define _NotifyMsg(n,c,f,l)	#n " failed: " #c " in " f " at line " _MakeStr(l)
152 #define _ExplicitMsg(msg,c,e) #e " " #msg ": " #c
153 
154 #define XMP_Validate(c,msg,e)									\
155 	if ( ! (c) ) {												\
156 		const char * validate_msg = _ExplicitMsg ( msg, c, e );	\
157 		XMP_Throw ( validate_msg, e );							\
158 	}
159 
160 // This statement is needed in XMP_Assert definition to reduce warnings from
161 // static analysis tool in Visual Studio. Defined here, as platform fork not
162 // possible within macro definition below
163 #if XMP_WinBuild
164 	#define analysis_assume(c) __analysis_assume( c );
165 #else
166 	#define analysis_assume(c) ((void) 0)
167 #endif
168 
169 #if ! XMP_DebugBuild
170 	#define XMP_Assert(c)	((void) 0)
171 #else
172 		#define XMP_Assert(c)	assert ( c )
173 #endif
174 
175 	#define XMP_Enforce(c)																	\
176 		if ( ! (c) ) {																		\
177 			const char * assert_msg = _NotifyMsg ( XMP_Enforce, (c), __FILE__, __LINE__ );	\
178 			XMP_Throw ( assert_msg , kXMPErr_EnforceFailure );								\
179 		}
180 // =================================================================================================
181 // Thread synchronization locks
182 // ============================
183 
184 // About XMP and thread synchronization
185 //
186 // A variety of choices are provided for thread synchronization. Exactly one method must be chosen
187 // by defining the appropriate symbol to 1.
188 //
189 // * UseNoLock - This choice turns the synchronization functions into no-ops. It must only be used
190 //   by single threaded clients, or clients providing their own control at a higher level.
191 //
192 // * UseGlobalLibraryLock - This choice uses a single per-library lock. The result is thread safe
193 //   but unfriendly behavior, no true concurrency. This should only be used as a debugging fallback.
194 //
195 // * UseBoostLock - This choice uses the Boost shared_mutex mechanism. It has the advantage of being
196 //   robust and being available on pretty much all platforms. It has the disadvantage of requiring
197 //   the developer to download, integrate, and build the Boost thread library.
198 //
199 // * UsePThreadLock - This choice uses the POSIX pthread rwlock mechanism. It has the advantage of
200 //   being robust and being available on any modern UNIX platform, including Mac OS X.
201 //
202 // * UseWinSlimLock - This choice uses the Windows slim reader/writer mechanism. It is robust but
203 //   only available on Vista and newer versions of Windows, it is not available on XP.
204 //
205 // * UseHomeGrownLock - This choice uses local code plus lower level synchronization primitives. It
206 //   has the advantage of being usable on all platforms, and having exposed and tunable policy. It
207 //   has the disadvantage of possibly being less robust than Boost or the O/S provided mechanisms.
208 //   The lower level synchronization primitives are pthread mutex and condition for UNIX (including
209 //   Mac OS X). For Windows there is a choice of critical section and condition variable for Vista
210 //   and newer; or critical section, event, and semaphore for XP and newer.
211 
212 #define UseHomeGrownLock 1
213 
214 // -------------------------------------------------------------------------------------------------
215 // A basic exclusive access mutex and atomic increment/decrement operations.
216 
217 #if XMP_WinBuild
218 
219 	#include <Windows.h>
220 
221 	#define HaveAtomicIncrDecr 1
222 	typedef LONG XMP_AtomicCounter;
223 
224 	#define XMP_AtomicIncrement(x)	InterlockedIncrement ( &(x) )
225 	#define XMP_AtomicDecrement(x)	InterlockedDecrement ( &(x) )
226 
227 	typedef CRITICAL_SECTION XMP_BasicMutex;
228 
229 	#define InitializeBasicMutex(mutex)	{ InitializeCriticalSection ( &mutex ); }
230 	#define TerminateBasicMutex(mutex)	{ DeleteCriticalSection ( &mutex ); }
231 	#define AcquireBasicMutex(mutex)	{ EnterCriticalSection ( &mutex ); }
232 	#define ReleaseBasicMutex(mutex)	{ LeaveCriticalSection ( &mutex ); }
233 
234 #elif XMP_MacBuild | XMP_iOSBuild
235 
236 	#include <pthread.h>
237 	#include <libkern/OSAtomic.h>
238 
239 	#define HaveAtomicIncrDecr 1
240 	typedef int32_t XMP_AtomicCounter;
241 
242 	#define XMP_AtomicIncrement(x)	OSAtomicIncrement32Barrier ( &(x) )
243 	#define XMP_AtomicDecrement(x)	OSAtomicDecrement32Barrier ( &(x) )
244 
245 	typedef pthread_mutex_t XMP_BasicMutex;
246 
247 	#define InitializeBasicMutex(mutex)	{ int err = pthread_mutex_init ( &mutex, 0 ); XMP_Enforce ( err == 0 ); }
248 	#define TerminateBasicMutex(mutex)	{ int err = pthread_mutex_destroy ( &mutex ); XMP_Enforce ( err == 0 ); }
249 	#define AcquireBasicMutex(mutex)	{ int err = pthread_mutex_lock ( &mutex ); XMP_Enforce ( err == 0 ); }
250 	#define ReleaseBasicMutex(mutex)	{ int err = pthread_mutex_unlock ( &mutex ); XMP_Enforce ( err == 0 ); }
251 
252 #elif XMP_UNIXBuild
253 
254 	#include <pthread.h>
255 
256 	// Atomic increment/decrement intrinsics should be in gcc 4.1, but Solaris seems to lack them.
257 	#ifndef HaveAtomicIncrDecr
258 		#define HaveAtomicIncrDecr 1
259 	#endif
260 	#if HaveAtomicIncrDecr
261 		typedef XMP_Uns32 XMP_AtomicCounter;
262 		#define XMP_AtomicIncrement(x)	__sync_add_and_fetch ( &(x), 1 )
263 		#define XMP_AtomicDecrement(x)	__sync_sub_and_fetch ( &(x), 1 )
264 	#endif
265 
266 	typedef pthread_mutex_t XMP_BasicMutex;
267 
268 	#define InitializeBasicMutex(mutex)	{ int err = pthread_mutex_init ( &mutex, 0 ); XMP_Enforce ( err == 0 ); }
269 	#define TerminateBasicMutex(mutex)	{ int err = pthread_mutex_destroy ( &mutex ); XMP_Enforce ( err == 0 ); }
270 	#define AcquireBasicMutex(mutex)	{ int err = pthread_mutex_lock ( &mutex ); XMP_Enforce ( err == 0 ); }
271 	#define ReleaseBasicMutex(mutex)	{ int err = pthread_mutex_unlock ( &mutex ); XMP_Enforce ( err == 0 ); }
272 
273 #endif
274 
275 class XMP_AutoMutex {
276 public:
XMP_AutoMutex(XMP_BasicMutex * _mutex)277 	XMP_AutoMutex ( XMP_BasicMutex * _mutex ) : mutex(_mutex) { AcquireBasicMutex ( *this->mutex ); }
~XMP_AutoMutex()278 	~XMP_AutoMutex() { this->Release(); }
Release()279 	void Release() { if ( this->mutex != 0 ) ReleaseBasicMutex ( *this->mutex ); this->mutex = 0; }
280 private:
281 	XMP_BasicMutex * mutex;
XMP_AutoMutex()282 	XMP_AutoMutex() {};	// ! Must not be used.
283 };
284 
285 // -------------------------------------------------------------------------------------------------
286 // Details for the various locking mechanisms.
287 
288 #if UseNoLock
289 
290 	typedef void* XMP_BasicRWLock;	// For single threaded clients that want maximum performance.
291 
292 	#define XMP_BasicRWLock_Initialize(lck)			/* Do nothing. */
293 	#define XMP_BasicRWLock_Terminate(lck)			/* Do nothing. */
294 
295 	#define XMP_BasicRWLock_AcquireForRead(lck)		/* Do nothing. */
296 	#define XMP_BasicRWLock_AcquireForWrite(lck)	/* Do nothing. */
297 
298 	#define XMP_BasicRWLock_ReleaseFromRead(lck)	/* Do nothing. */
299 	#define XMP_BasicRWLock_ReleaseFromWrite(lck)	/* Do nothing. */
300 
301 #elif UseGlobalLibraryLock
302 
303 	extern XMP_BasicMutex sLibraryLock;
304 
305 	typedef void* XMP_BasicRWLock;	// Use the old thread-unfriendly per-DLL mutex.
306 
307 	#define XMP_BasicRWLock_Initialize(lck)			/* Do nothing. */
308 	#define XMP_BasicRWLock_Terminate(lck)			/* Do nothing. */
309 
310 	#define XMP_BasicRWLock_AcquireForRead(lck)		/* Do nothing. */
311 	#define XMP_BasicRWLock_AcquireForWrite(lck)	/* Do nothing. */
312 
313 	#define XMP_BasicRWLock_ReleaseFromRead(lck)	/* Do nothing. */
314 	#define XMP_BasicRWLock_ReleaseFromWrite(lck)	/* Do nothing. */
315 
316 #elif UseBoostLock
317 
318 	#include <boost/thread/shared_mutex.hpp>
319 	typedef boost::shared_mutex XMP_BasicRWLock;
320 
321 	#define XMP_BasicRWLock_Initialize(lck)			/* Do nothing. */
322 	#define XMP_BasicRWLock_Terminate(lck)			/* Do nothing. */
323 
324 	#define XMP_BasicRWLock_AcquireForRead(lck)		lck.lock_shared()
325 	#define XMP_BasicRWLock_AcquireForWrite(lck)	lck.lock()
326 
327 	#define XMP_BasicRWLock_ReleaseFromRead(lck)	lck.unlock_shared()
328 	#define XMP_BasicRWLock_ReleaseFromWrite(lck)	lck.unlock()
329 
330 #elif UsePThreadLock
331 
332 	#include <pthread.h>
333 	typedef pthread_rwlock_t XMP_BasicRWLock;
334 
335 	#define XMP_BasicRWLock_Initialize(lck)				\
336 		{ int err = pthread_rwlock_init ( &lck, 0 );	\
337 		  if ( err != 0 ) XMP_Throw ( "Initialize pthread rwlock failed", kXMPErr_ExternalFailure ); }
338 	#define XMP_BasicRWLock_Terminate(lck)	\
339 		{ int err = pthread_rwlock_destroy ( &lck ); XMP_Assert ( err == 0 ); }
340 
341 	#define XMP_BasicRWLock_AcquireForRead(lck)		\
342 		{ int err = pthread_rwlock_rdlock ( &lck );	\
343 		  if ( err != 0 ) XMP_Throw ( "Acquire pthread read lock failed", kXMPErr_ExternalFailure ); }
344 	#define XMP_BasicRWLock_AcquireForWrite(lck)	\
345 		{ int err = pthread_rwlock_wrlock ( &lck );	\
346 		  if ( err != 0 ) XMP_Throw ( "Acquire pthread write lock failed", kXMPErr_ExternalFailure ); }
347 
348 	#define XMP_BasicRWLock_ReleaseFromRead(lck)	\
349 		{ int err = pthread_rwlock_unlock ( &lck );	\
350 		  if ( err != 0 ) XMP_Throw ( "Release pthread read lock failed", kXMPErr_ExternalFailure ); }
351 	#define XMP_BasicRWLock_ReleaseFromWrite(lck)	\
352 		{ int err = pthread_rwlock_unlock ( &lck );	\
353 		  if ( err != 0 ) XMP_Throw ( "Release pthread write lock failed", kXMPErr_ExternalFailure ); }
354 
355 #elif UseWinSlimLock
356 
357 	#include <Windows.h>
358 	typedef SRWLOCK XMP_BasicRWLock;
359 
360 	#define XMP_BasicRWLock_Initialize(lck)			InitializeSRWLock ( &lck )
361 	#define XMP_BasicRWLock_Terminate(lck)			/* Do nothing. */
362 
363 	#define XMP_BasicRWLock_AcquireForRead(lck)		AcquireSRWLockShared ( &lck )
364 	#define XMP_BasicRWLock_AcquireForWrite(lck)	AcquireSRWLockExclusive ( &lck )
365 
366 	#define XMP_BasicRWLock_ReleaseFromRead(lck)	ReleaseSRWLockShared ( &lck )
367 	#define XMP_BasicRWLock_ReleaseFromWrite(lck)	ReleaseSRWLockExclusive ( &lck )
368 
369 #elif UseHomeGrownLock
370 
371 	class XMP_HomeGrownLock;
372 	typedef XMP_HomeGrownLock XMP_BasicRWLock;
373 
374 	#define XMP_BasicRWLock_Initialize(lck)			/* Do nothing. */
375 	#define XMP_BasicRWLock_Terminate(lck)			/* Do nothing. */
376 	#define XMP_BasicRWLock_AcquireForRead(lck)		lck.AcquireForRead()
377 	#define XMP_BasicRWLock_AcquireForWrite(lck)	lck.AcquireForWrite()
378 	#define XMP_BasicRWLock_ReleaseFromRead(lck)	lck.ReleaseFromRead()
379 	#define XMP_BasicRWLock_ReleaseFromWrite(lck)	lck.ReleaseFromWrite()
380 
381 	#if XMP_MacBuild | XMP_UNIXBuild | XMP_iOSBuild
382 
383 		#include <pthread.h>
384 
385 		typedef pthread_cond_t  XMP_BasicQueue;
386 
387 	#elif XMP_WinBuild
388 
389 		#include <Windows.h>
390 		#ifndef BuildLocksForWinXP
391 			#define BuildLocksForWinXP 1
392 		#endif
393 
394 		#if ! BuildLocksForWinXP
395 			typedef CONDITION_VARIABLE XMP_BasicQueue;	// ! Requires Vista or newer.
396 		#else
397 			class XMP_WinXP_HGQueue {
398 			public:
399 				XMP_WinXP_HGQueue();
400 				~XMP_WinXP_HGQueue();
401 				void Wait ( XMP_BasicMutex & queueMutex );
402 				void ReleaseOne();
403 				void ReleaseAll();
404 			private:
405 				HANDLE queueEvent;
406 				volatile XMP_Uns32 waitCount;	// ! Does not need to be XMP_AtomicCounter.
407 				volatile bool releaseAll;
408 			};
409 			typedef XMP_WinXP_HGQueue XMP_BasicQueue;
410 		#endif
411 
412 	#endif
413 
414 	class XMP_HomeGrownLock {
415 	public:
416 		XMP_HomeGrownLock();
417 		~XMP_HomeGrownLock() noexcept(false);
418 		void AcquireForRead();
419 		void AcquireForWrite();
420 		void ReleaseFromRead();
421 		void ReleaseFromWrite();
422 	private:
423 		XMP_BasicMutex queueMutex;	// Used to protect queueing operations.
424 		XMP_BasicQueue readerQueue, writerQueue;
425 		volatile XMP_Uns32 lockCount, readersWaiting, writersWaiting;	// ! Does not need to be XMP_AtomicCounter.
426 		volatile bool beingWritten;
427 	};
428 
429 #else
430 
431 	#error "No locking mechanism chosen"
432 
433 #endif
434 
435 class XMP_ReadWriteLock {	// For the lock objects, use XMP_AutoLock to do the locking.
436 public:
437 	XMP_ReadWriteLock();
438 	~XMP_ReadWriteLock();
439 	void Acquire ( bool forWriting );
440 	void Release();
441 private:
442 	XMP_BasicRWLock lock;
443 	#if XMP_DebugBuild && HaveAtomicIncrDecr
444 		volatile XMP_AtomicCounter lockCount;	// ! Only for debug checks, must be XMP_AtomicCounter.
445 	#endif
446 	volatile bool beingWritten;
447 };
448 
449 #define kXMP_ReadLock	false
450 #define kXMP_WriteLock	true
451 
452 class XMP_AutoLock {
453 public:
XMP_AutoLock(const XMP_ReadWriteLock * _lock,bool forWriting,bool cond=true)454 	XMP_AutoLock ( const XMP_ReadWriteLock * _lock, bool forWriting, bool cond = true ) : lock(0)
455 		{
456 			if ( cond ) {
457 				// The cast below is needed because the _lock parameter might come from something
458 				// like "const XMPMeta &", which would make the lock itself const. But we need to
459 				// modify the lock (to acquire and release) even if the owning object is const.
460 				this->lock = (XMP_ReadWriteLock*)_lock;
461 				this->lock->Acquire ( forWriting );
462 			}
463 		}
~XMP_AutoLock()464 	~XMP_AutoLock() { this->Release(); }
Release()465 	void Release() { if ( this->lock != 0 ) this->lock->Release(); this->lock = 0; }
466 private:
467 	XMP_ReadWriteLock * lock;
XMP_AutoLock()468 	XMP_AutoLock() {};	// ! Must not be used.
469 };
470 
471 // =================================================================================================
472 // Support for wrappers
473 // ====================
474 
475 #define AnnounceStaticEntry(proc)			/* Do nothing. */
476 #define AnnounceObjectEntry(proc,rwMode)	/* Do nothing. */
477 
478 #define AnnounceExit()	/* Do nothing. */
479 
480 // -------------------------------------------------------------------------------------------------
481 
482 #if UseGlobalLibraryLock
483 	#define AcquireLibraryLock(lck)	XMP_AutoMutex libLock ( &lck )
484 #else
485 	#define AcquireLibraryLock(lck)	/* nothing */
486 #endif
487 
488 #define XMP_ENTER_NoLock(Proc)		\
489 	AnnounceStaticEntry ( Proc );	\
490 	try {							\
491 		wResult->SetErrMessage(0);
492 
493 #define XMP_ENTER_Static(Proc)				\
494 	AnnounceStaticEntry ( Proc );			\
495 	AcquireLibraryLock ( sLibraryLock );	\
496 	try {									\
497 		wResult->SetErrMessage(0);
498 
499 #define XMP_ENTER_ObjRead(XMPClass,Proc)				\
500 	AnnounceObjectEntry ( Proc, "reader" );				\
501 	AcquireLibraryLock ( sLibraryLock );				\
502 	const XMPClass & thiz = *((XMPClass*)xmpObjRef);	\
503 	XMP_AutoLock objLock ( &thiz.lock, kXMP_ReadLock );	\
504 	try {												\
505 		wResult->SetErrMessage(0);
506 
507 #define XMP_ENTER_ObjWrite(XMPClass,Proc)					\
508 	AnnounceObjectEntry ( Proc, "writer" );					\
509 	AcquireLibraryLock ( sLibraryLock );					\
510 	XMPClass * thiz = (XMPClass*)xmpObjRef;					\
511 	XMP_AutoLock objLock ( &thiz->lock, kXMP_WriteLock );	\
512 	try {													\
513 		wResult->SetErrMessage(0);
514 
515 #define XMP_EXIT			\
516 	XMP_CATCH_EXCEPTIONS	\
517 	AnnounceExit();
518 
519 #define XMP_EXIT_NoThrow						\
520 	} catch ( ... )	{							\
521 		AnnounceCatch ( "no-throw catch-all" );	\
522 		/* Do nothing. */						\
523 	}											\
524 	AnnounceExit();
525 
526 #define XMP_CATCH_EXCEPTIONS										\
527 	} catch ( XMP_Error & xmpErr ) {								\
528 		wResult->int32Result = xmpErr.GetID(); 						\
529 		wResult->ptrResult   = (void*)"XMP";						\
530 		wResult->SetErrMessage(xmpErr.GetErrMsg());             \
531 		if ( wResult->GetErrMessage() == 0 ) wResult->SetErrMessage(""); \
532 		AnnounceCatch ( wResult->GetErrMessage() );             \
533 	} catch ( std::exception & stdErr ) {							\
534 		wResult->int32Result = kXMPErr_StdException; 				\
535 		wResult->SetErrMessage(stdErr.what());                  \
536 		if ( wResult->GetErrMessage() == 0 ) wResult->SetErrMessage(""); \
537 		AnnounceCatch ( wResult->GetErrMessage() );             \
538 	} catch ( ... ) {												\
539 		wResult->int32Result = kXMPErr_UnknownException; 			\
540 		wResult->SetErrMessage("Caught unknown exception");     \
541 		AnnounceCatch ( wResult->GetErrMessage() );                \
542 	}
543 
544 #if XMP_DebugBuild
545 	#define RELEASE_NO_THROW	/* empty */
546 #else
547 	#define RELEASE_NO_THROW	throw()
548 #endif
549 
550 // =================================================================================================
551 // Data structure dumping utilities
552 // ================================
553 
554 #define IsHexDigit(ch)		( (('0' <= (ch)) && ((ch) <= '9')) || (('A' <= (ch)) && ((ch) <= 'F')) )
555 #define HexDigitValue(ch)	( (((ch) - '0') < 10) ? ((ch) - '0') : ((ch) - 'A' + 10) )
556 
557 #define kTenSpaces "          "
558 #define OutProcPadding(pad)	{ size_t padLen = (pad); 												\
559 							  for ( ; padLen >= 10; padLen -= 10 ) OutProcNChars ( kTenSpaces, 10 );	\
560 							  for ( ; padLen > 0; padLen -= 1 ) OutProcNChars ( " ", 1 ); }
561 
562 
563 #define OutProcNewline()	{ XMP_Status status = (*outProc) ( refCon, "\n", 1 );  if ( status != 0 ) return; }
564 
565 #define OutProcNChars(p,n)	{ XMP_Status status = (*outProc) ( refCon, (p), (n) );  if ( status != 0 ) return; }
566 
567 #define OutProcLiteral(lit)	{ XMP_Status _status = (*outProc) ( refCon, (lit), (XMP_StringLen)strlen(lit) );  if ( _status != 0 ) return; }
568 
569 #define OutProcString(str)	{ XMP_Status _status = (*outProc) ( refCon, (str).c_str(), (XMP_StringLen)(str).size() );  if ( _status != 0 ) return; }
570 
571 #define OutProcDecInt(num)	{ snprintf ( buffer, sizeof(buffer), "%ld", (long)(num) ); /* AUDIT: Using sizeof for snprintf length is safe */	\
572 							  buffer[sizeof(buffer) -1] = 0; /* AUDIT warning C6053: Make sure buffer is terminated */							\
573 							  XMP_Status _status = (*outProc) ( refCon, buffer, (XMP_StringLen)strlen(buffer) );  if ( _status != 0 ) return; }
574 
575 #define OutProcHexInt(num)	{ snprintf ( buffer, sizeof(buffer), "%lX", (long)(num) ); /* AUDIT: Using sizeof for snprintf length is safe */	\
576 							  buffer[sizeof(buffer) -1] = 0; /* AUDIT warning C6053: Make sure buffer is terminated */							\
577 							  XMP_Status _status = (*outProc) ( refCon, buffer, (XMP_StringLen)strlen(buffer) );  if ( _status != 0 ) return; }
578 
579 #define OutProcHexByte(num)	{ snprintf ( buffer, sizeof(buffer), "%.2X", (unsigned char)(num) ); /* AUDIT: Using sizeof for snprintf length is safe */	\
580 							  XMP_Status _status = (*outProc) ( refCon, buffer, (XMP_StringLen)strlen(buffer) );  if ( _status != 0 ) return; }
581 
582 #define kIndent "   "
583 #define OutProcIndent(lev)	{ for ( size_t i = 0; i < (lev); ++i ) OutProcNChars ( kIndent, 3 ); }
584 
585 void DumpClearString ( const XMP_VarString & value, XMP_TextOutputProc outProc, void * refCon );
586 
587 // =================================================================================================
588 // Namespace Tables
589 // ================
590 typedef std::vector <XMP_VarString> XMP_StringVector;
591 typedef XMP_StringVector::iterator XMP_StringVectorPos;
592 typedef XMP_StringVector::const_iterator XMP_StringVectorCPos;
593 
594 typedef std::pair < XMP_VarString, XMP_VarString >	XMP_StringPair;
595 
596 typedef std::map < XMP_VarString, XMP_VarString > XMP_StringMap;
597 typedef XMP_StringMap::iterator       XMP_StringMapPos;
598 typedef XMP_StringMap::const_iterator XMP_cStringMapPos;
599 
600 class XMP_NamespaceTable {
601 public:
602 
XMP_NamespaceTable()603 	XMP_NamespaceTable() {};
604 	XMP_NamespaceTable ( const XMP_NamespaceTable & presets );
~XMP_NamespaceTable()605 	virtual ~XMP_NamespaceTable() {};
606 
607     bool Define ( XMP_StringPtr uri, XMP_StringPtr suggPrefix,
608     			  XMP_StringPtr * prefixPtr, XMP_StringLen * prefixLen);
609 
610     bool GetPrefix ( XMP_StringPtr uri, XMP_StringPtr * prefixPtr, XMP_StringLen * prefixLen ) const;
611     bool GetURI    ( XMP_StringPtr prefix, XMP_StringPtr * uriPtr, XMP_StringLen * uriLen ) const;
612 
613     void Dump ( XMP_TextOutputProc outProc, void * refCon ) const;
614 
615 private:
616 
617 	XMP_ReadWriteLock lock;
618 	XMP_StringMap uriToPrefixMap, prefixToURIMap;
619 
620 };
621 
622 
623 // Right now it supports only ^, $ and \d, in future we should use it as a wrapper over
624 // regex object once mac and Linux compilers start supporting them.
625 
626 class XMP_RegExp {
627 public:
XMP_RegExp(XMP_StringPtr regExp)628 	XMP_RegExp ( XMP_StringPtr regExp )
629 	{
630 		if ( regExp )
631 			regExpStr = regExp;
632 	}
633 
634 	XMP_Bool Match ( XMP_StringPtr s );
635 
636 private:
637 	XMP_VarString regExpStr;
638 };
639 
640 // =================================================================================================
641 
642 #endif	// __XMP_LibUtils_hpp__
643