1 #ifndef __XMPFiles_Impl_hpp__
2 #define __XMPFiles_Impl_hpp__	1
3 
4 // =================================================================================================
5 // ADOBE SYSTEMS INCORPORATED
6 // Copyright 2004-2008 Adobe Systems Incorporated
7 // All Rights Reserved
8 //
9 // NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms
10 // of the Adobe license agreement accompanying it.
11 // =================================================================================================
12 
13 #include "XMP_Environment.h"	// ! Must be the first #include!
14 #include "XMP_Const.h"
15 #include "XMP_BuildInfo.h"
16 #include "EndianUtils.hpp"
17 
18 #include <string>
19 #define TXMP_STRING_TYPE std::string
20 #define XMP_INCLUDE_XMPFILES 1
21 #include "XMP.hpp"
22 
23 #include "XMPFiles.hpp"
24 #include "LargeFileAccess.hpp"
25 
26 #include <vector>
27 #include <string>
28 #include <map>
29 #include <cstring>
30 
31 #include <cassert>
32 
33 #if XMP_WinBuild
34 	#include <Windows.h>
35 	#define snprintf _snprintf
36 #else
37 	#if XMP_MacBuild
38 		#include <Files.h>
39 	#endif
40 	// POSIX headers for both Mac and generic UNIX.
41 	#include <pthread.h>
42 	#include <fcntl.h>
43 	#include <unistd.h>
44 	#include <dirent.h>
45 	#include <sys/stat.h>
46 	#include <sys/types.h>
47 #endif
48 
49 // =================================================================================================
50 // General global variables and macros
51 
52 extern long sXMPFilesInitCount;
53 
54 #ifndef GatherPerformanceData
55 	#define GatherPerformanceData 0
56 #endif
57 
58 #if ! GatherPerformanceData
59 
60 	#define StartPerfCheck(proc,info)	/* do nothing */
61 	#define EndPerfCheck(proc)	/* do nothing */
62 
63 #else
64 
65 	#include "PerfUtils.hpp"
66 
67 	enum {
68 		kAPIPerf_OpenFile,
69 		kAPIPerf_CloseFile,
70 		kAPIPerf_GetXMP,
71 		kAPIPerf_GetThumbnail,
72 		kAPIPerf_PutXMP,
73 		kAPIPerf_CanPutXMP,
74 		kAPIPerfProcCount	// Last, count of the procs.
75 	};
76 
77 	static const char* kAPIPerfNames[] =
78 		{ "OpenFile", "CloseFile", "GetXMP", "GetThumbnail", "PutXMP", "CanPutXMP", 0 };
79 
80 	struct APIPerfItem {
81 		XMP_Uns8    whichProc;
82 		double      elapsedTime;
83 		XMPFilesRef xmpFilesRef;
84 		std::string extraInfo;
APIPerfItemAPIPerfItem85 		APIPerfItem ( XMP_Uns8 proc, double time, XMPFilesRef ref, const char * info )
86 			: whichProc(proc), elapsedTime(time), xmpFilesRef(ref), extraInfo(info) {};
87 	};
88 
89 	typedef std::vector<APIPerfItem> APIPerfCollection;
90 
91 	extern APIPerfCollection* sAPIPerf;
92 
93 	#define StartPerfCheck(proc,info)	\
94 		sAPIPerf->push_back ( APIPerfItem ( proc, 0.0, xmpFilesRef, info ) );	\
95 		APIPerfItem & thisPerf = sAPIPerf->back();								\
96 		PerfUtils::MomentValue startTime, endTime;								\
97 		try {																	\
98 			startTime = PerfUtils::NoteThisMoment();
99 
100 	#define EndPerfCheck(proc)	\
101 			endTime = PerfUtils::NoteThisMoment();										\
102 			thisPerf.elapsedTime = PerfUtils::GetElapsedSeconds ( startTime, endTime );	\
103 		} catch ( ... ) {																\
104 			endTime = PerfUtils::NoteThisMoment();										\
105 			thisPerf.elapsedTime = PerfUtils::GetElapsedSeconds ( startTime, endTime );	\
106 			thisPerf.extraInfo += "  ** THROW **";										\
107 			throw;																		\
108 		}
109 
110 #endif
111 
112 extern XMP_FileFormat voidFileFormat;	// Used as sink for unwanted output parameters.
113 extern XMP_PacketInfo voidPacketInfo;
114 extern void *         voidVoidPtr;
115 extern XMP_StringPtr  voidStringPtr;
116 extern XMP_StringLen  voidStringLen;
117 extern XMP_OptionBits voidOptionBits;
118 
119 static const XMP_Uns8 * kUTF8_PacketStart = (const XMP_Uns8 *) "<?xpacket begin=";
120 static const XMP_Uns8 * kUTF8_PacketID    = (const XMP_Uns8 *) "W5M0MpCehiHzreSzNTczkc9d";
121 static const size_t     kUTF8_PacketHeaderLen = 51;	// ! strlen ( "<?xpacket begin='xxx' id='W5M0MpCehiHzreSzNTczkc9d'" )
122 
123 static const XMP_Uns8 * kUTF8_PacketTrailer    = (const XMP_Uns8 *) "<?xpacket end=\"w\"?>";
124 static const size_t     kUTF8_PacketTrailerLen = 19;	// ! strlen ( kUTF8_PacketTrailer )
125 
126 struct FileExtMapping {
127 	XMP_StringPtr  ext;
128 	XMP_FileFormat format;
129 };
130 
131 extern const FileExtMapping kFileExtMap[];
132 extern const char * kKnownScannedFiles[];
133 extern const char * kKnownRejectedFiles[];
134 
135 #define Uns8Ptr(p) ((XMP_Uns8 *) (p))
136 
137 #define kTab ((char)0x09)
138 #define kLF ((char)0x0A)
139 #define kCR ((char)0x0D)
140 
141 #define IsNewline( ch )    ( ((ch) == kLF) || ((ch) == kCR) )
142 #define IsSpaceOrTab( ch ) ( ((ch) == ' ') || ((ch) == kTab) )
143 #define IsWhitespace( ch ) ( IsSpaceOrTab ( ch ) || IsNewline ( ch ) )
144 
MakeLowerCase(std::string * str)145 static inline void MakeLowerCase ( std::string * str )
146 {
147 	for ( size_t i = 0, limit = str->size(); i < limit; ++i ) {
148 		char ch = (*str)[i];
149 		if ( ('A' <= ch) && (ch <= 'Z') ) (*str)[i] += 0x20;
150 	}
151 }
152 
MakeUpperCase(std::string * str)153 static inline void MakeUpperCase ( std::string * str )
154 {
155 	for ( size_t i = 0, limit = str->size(); i < limit; ++i ) {
156 		char ch = (*str)[i];
157 		if ( ('a' <= ch) && (ch <= 'z') ) (*str)[i] -= 0x20;
158 	}
159 }
160 
161 #define XMP_LitMatch(s,l)		(strcmp((s),(l)) == 0)
162 #define XMP_LitNMatch(s,l,n)	(strncmp((s),(l),(n)) == 0)
163 
164 #define IgnoreParam(p)	voidVoidPtr = (void*)&p
165 
166 // =================================================================================================
167 // Support for asserts
168 
169 #define _MakeStr(p)			#p
170 #define _NotifyMsg(n,c,f,l)	#n " failed: " #c " in " f " at line " _MakeStr(l)
171 #define _NotifyMsg2(msg,c,e) #e " " #msg ": " #c
172 
173 #if ! XMP_DebugBuild
174 	#define XMP_Assert(c)	((void) 0)
175 #else
176 		#define XMP_Assert(c)	assert ( c )
177 #endif
178 
179 	#define XMP_Enforce(c)																			\
180 		if ( ! (c) ) {																				\
181 			const char * assert_msg = _NotifyMsg ( XMP_Enforce, (c), __FILE__, __LINE__ );			\
182 			XMP_Throw ( assert_msg , kXMPErr_EnforceFailure );										\
183 		}
184 #define XMP_Validate(c,msg,e)																	\
185 	if ( ! (c) ) {																				\
186 		const char * enforce_msg = _NotifyMsg2(msg,c,e);										\
187 		XMP_Throw ( enforce_msg , e );															\
188 	}
189 
190 // =================================================================================================
191 // Support for memory leak tracking
192 
193 #ifndef TrackMallocAndFree
194 	#define TrackMallocAndFree 0
195 #endif
196 
197 #if TrackMallocAndFree
198 
ChattyMalloc(size_t size)199 	static void* ChattyMalloc ( size_t size )
200 	{
201 		void* ptr = malloc ( size );
202 		fprintf ( stderr, "Malloc %d bytes @ %.8X\n", size, ptr );
203 		return ptr;
204 	}
205 
ChattyFree(void * ptr)206 	static void ChattyFree ( void* ptr )
207 	{
208 		fprintf ( stderr, "Free @ %.8X\n", ptr );
209 		free ( ptr );
210 	}
211 
212 	#define malloc(s) ChattyMalloc ( s )
213 	#define free(p) ChattyFree ( p )
214 
215 #endif
216 
217 // =================================================================================================
218 // Support for exceptions and thread locking
219 
220 // *** Local copies of threading and exception macros from XMP_Impl.hpp. XMPFiles needs to use a
221 // *** separate thread lock from XMPCore. Eventually this could benefit from being recast into an
222 // *** XMPToolkit_Impl that supports separate locks.
223 
224 typedef std::string	XMP_VarString;
225 
226 #ifndef TraceXMPCalls
227 	#define TraceXMPCalls	0
228 #endif
229 
230 #if ! TraceXMPCalls
231 
232 	#define AnnounceThrow(msg)		/* Do nothing. */
233 	#define AnnounceCatch(msg)		/* Do nothing. */
234 
235 	#define AnnounceEntry(proc)		/* Do nothing. */
236 	#define AnnounceNoLock(proc)	/* Do nothing. */
237 	#define AnnounceExit()			/* Do nothing. */
238 
239 	#define ReportLock()			++sXMPFilesLockCount
240 	#define ReportUnlock()			--sXMPFilesLockCount
241 	#define ReportKeepLock()		/* Do nothing. */
242 
243 #else
244 
245 	extern FILE * xmpFilesOut;
246 
247 	#define AnnounceThrow(msg)	\
248 		fprintf ( xmpFilesOut, "XMP_Throw: %s\n", msg ); fflush ( xmpFilesOut )
249 	#define AnnounceCatch(msg)	\
250 		fprintf ( xmpFilesOut, "Catch in %s: %s\n", procName, msg ); fflush ( xmpFilesOut )
251 
252 	#define AnnounceEntry(proc)			\
253 		const char * procName = proc;	\
254 		fprintf ( xmpFilesOut, "Entering %s\n", procName ); fflush ( xmpFilesOut )
255 	#define AnnounceNoLock(proc)		\
256 		const char * procName = proc;	\
257 		fprintf ( xmpFilesOut, "Entering %s (no lock)\n", procName ); fflush ( xmpFilesOut )
258 	#define AnnounceExit()	\
259 		fprintf ( xmpFilesOut, "Exiting %s\n", procName ); fflush ( xmpFilesOut )
260 
261 	#define ReportLock()	\
262 		++sXMPFilesLockCount; fprintf ( xmpFilesOut, "  Auto lock, count = %d\n", sXMPFilesLockCount ); fflush ( xmpFilesOut )
263 	#define ReportUnlock()	\
264 		--sXMPFilesLockCount; fprintf ( xmpFilesOut, "  Auto unlock, count = %d\n", sXMPFilesLockCount ); fflush ( xmpFilesOut )
265 	#define ReportKeepLock()	\
266 		fprintf ( xmpFilesOut, "  Keeping lock, count = %d\n", sXMPFilesLockCount ); fflush ( xmpFilesOut )
267 
268 #endif
269 
270 #define XMP_Throw(msg,id)	{ AnnounceThrow ( msg ); throw XMP_Error ( id, msg ); }
271 
272 // -------------------------------------------------------------------------------------------------
273 
274 #if XMP_WinBuild
275 	typedef CRITICAL_SECTION XMP_Mutex;
276 #else
277 	// Use pthread for both Mac and generic UNIX.
278 	typedef pthread_mutex_t XMP_Mutex;
279 #endif
280 
281 extern XMP_Mutex sXMPFilesLock;
282 extern int sXMPFilesLockCount;	// Keep signed to catch unlock errors.
283 extern XMP_VarString * sXMPFilesExceptionMessage;
284 
285 extern bool XMP_InitMutex ( XMP_Mutex * mutex );
286 extern void XMP_TermMutex ( XMP_Mutex & mutex );
287 
288 extern void XMP_EnterCriticalRegion ( XMP_Mutex & mutex );
289 extern void XMP_ExitCriticalRegion ( XMP_Mutex & mutex );
290 
291 class XMPFiles_AutoMutex {
292 public:
XMPFiles_AutoMutex()293 	XMPFiles_AutoMutex() : mutex(&sXMPFilesLock) { XMP_EnterCriticalRegion ( *mutex ); ReportLock(); };
~XMPFiles_AutoMutex()294 	~XMPFiles_AutoMutex() { if ( mutex != 0 ) { ReportUnlock(); XMP_ExitCriticalRegion ( *mutex ); mutex = 0; } };
KeepLock()295 	void KeepLock() { ReportKeepLock(); mutex = 0; };
296 private:
297 	XMP_Mutex * mutex;
298 };
299 
300 // *** Switch to XMPEnterObjectWrapper & XMPEnterStaticWrapper, to allow for per-object locks.
301 
302 // ! Don't do the initialization check (sXMP_InitCount > 0) for the no-lock case. That macro is used
303 // ! by WXMPMeta_Initialize_1.
304 
305 #define XMP_ENTER_WRAPPER_NO_LOCK(proc)						\
306 	AnnounceNoLock ( proc );								\
307 	XMP_Assert ( (0 <= sXMPFilesLockCount) && (sXMPFilesLockCount <= 1) );	\
308 	try {													\
309 		wResult->errMessage = 0;
310 
311 #define XMP_ENTER_WRAPPER(proc)								\
312 	AnnounceEntry ( proc );									\
313 	XMP_Assert ( sXMPFilesInitCount > 0 );					\
314 	XMP_Assert ( (0 <= sXMPFilesLockCount) && (sXMPFilesLockCount <= 1) );	\
315 	try {													\
316 		XMPFiles_AutoMutex mutex;							\
317 		wResult->errMessage = 0;
318 
319 #define XMP_EXIT_WRAPPER	\
320 	XMP_CATCH_EXCEPTIONS	\
321 	AnnounceExit();
322 
323 #define XMP_EXIT_WRAPPER_KEEP_LOCK(keep)	\
324 		if ( keep ) mutex.KeepLock();		\
325 	XMP_CATCH_EXCEPTIONS					\
326 	AnnounceExit();
327 
328 #define XMP_EXIT_WRAPPER_NO_THROW				\
329 	} catch ( ... )	{							\
330 		AnnounceCatch ( "no-throw catch-all" );	\
331 		/* Do nothing. */						\
332 	}											\
333 	AnnounceExit();
334 
335 #define XMP_CATCH_EXCEPTIONS										\
336 	} catch ( XMP_Error & xmpErr ) {								\
337 		wResult->int32Result = xmpErr.GetID(); 						\
338 		wResult->ptrResult   = (void*)"XMP";						\
339 		wResult->errMessage  = xmpErr.GetErrMsg();					\
340 		if ( wResult->errMessage == 0 ) wResult->errMessage = "";	\
341 		AnnounceCatch ( wResult->errMessage );						\
342 	} catch ( std::exception & stdErr ) {							\
343 		wResult->int32Result = kXMPErr_StdException; 				\
344 		wResult->errMessage  = stdErr.what(); 						\
345 		if ( wResult->errMessage == 0 ) wResult->errMessage = "";	\
346 		AnnounceCatch ( wResult->errMessage );						\
347 	} catch ( ... ) {												\
348 		wResult->int32Result = kXMPErr_UnknownException; 			\
349 		wResult->errMessage  = "Caught unknown exception";			\
350 		AnnounceCatch ( wResult->errMessage );						\
351 	}
352 
353 #if XMP_DebugBuild
354 	#define RELEASE_NO_THROW	/* empty */
355 #else
356 	#define RELEASE_NO_THROW	throw()
357 #endif
358 
359 // =================================================================================================
360 // FileHandler declarations
361 
362 extern void ReadXMPPacket ( XMPFileHandler * handler );
363 
364 extern void FillPacketInfo ( const XMP_VarString & packet, XMP_PacketInfo * info );
365 
366 class XMPFileHandler {	// See XMPFiles.hpp for usage notes.
367 public:
368 
369     #define DefaultCTorPresets							\
370     	handlerFlags(0), stdCharForm(kXMP_CharUnknown),	\
371     	containsTNail(false), processedTNail(false),	\
372     	containsXMP(false), processedXMP(false), needsUpdate(false)
373 
374     XMPFileHandler() : parent(0), DefaultCTorPresets {};
375     XMPFileHandler (XMPFiles * _parent) : parent(_parent), DefaultCTorPresets {};
376 
~XMPFileHandler()377 	virtual ~XMPFileHandler() {};	// ! The specific handler is responsible for tnailInfo.tnailImage.
378 
379 	virtual void CacheFileData() = 0;
380 	virtual void ProcessTNail();	// The default implementation just sets processedTNail to true.
381 	virtual void ProcessXMP();		// The default implementation just parses the XMP.
382 
383 	virtual XMP_OptionBits GetSerializeOptions();	// The default is compact.
384 
385 	virtual void UpdateFile ( bool doSafeUpdate ) = 0;
386     virtual void WriteFile ( LFA_FileRef sourceRef, const std::string & sourcePath ) = 0;
387 
388 	// ! Leave the data members public so common code can see them.
389 
390 	XMPFiles *     parent;			// Let's the handler see the file info.
391 	XMP_OptionBits handlerFlags;	// Capabilities of this handler.
392 	XMP_Uns8       stdCharForm;		// The standard character form for output.
393 
394 	bool containsTNail;		// True if the file has a native thumbnail.
395 	bool processedTNail;	// True if the cached thumbnail data has been processed.
396 	bool containsXMP;		// True if the file has XMP or PutXMP has been called.
397 	bool processedXMP;		// True if the XMP is parsed and reconciled.
398 	bool needsUpdate;		// True if the file needs to be updated.
399 
400 	XMP_PacketInfo packetInfo;	// ! This is always info about the packet in the file, if any!
401 	std::string    xmpPacket;	// ! This is the current XMP, updated by XMPFiles::PutXMP.
402 	SXMPMeta       xmpObj;
403 
404 	XMP_ThumbnailInfo tnailInfo;
405 
406 };	// XMPFileHandler
407 
408 typedef XMPFileHandler * (* XMPFileHandlerCTor) ( XMPFiles * parent );
409 
410 typedef bool (* CheckFileFormatProc ) ( XMP_FileFormat format,
411 										XMP_StringPtr  filePath,
412 										LFA_FileRef    fileRef,
413 										XMPFiles *     parent );
414 
415 typedef bool (*CheckFolderFormatProc ) ( XMP_FileFormat format,
416 										 const std::string & rootPath,
417 										 const std::string & gpName,
418 										 const std::string & parentName,
419 										 const std::string & leafName,
420 										 XMPFiles * parent );
421 
422 // =================================================================================================
423 
424 #if XMP_MacBuild
425 	extern LFA_FileRef LFA_OpenRsrc ( const char * fileName, char openMode );	// Open the Mac resource fork.
426 #endif
427 
428 extern void CreateTempFile ( const std::string & origPath, std::string * tempPath, bool copyMacRsrc = false );
429 enum { kCopyMacRsrc = true };
430 
431 struct AutoFile {	// Provides auto close of files on exit or exception.
432 	LFA_FileRef fileRef;
AutoFileAutoFile433 	AutoFile() : fileRef(0) {};
~AutoFileAutoFile434 	~AutoFile() { if ( fileRef != 0 ) LFA_Close ( fileRef ); };
435 };
436 
437 enum { kFMode_DoesNotExist, kFMode_IsFile, kFMode_IsFolder, kFMode_IsOther };
438 typedef XMP_Uns8 FileMode;
439 
440 #if XMP_WinBuild
441 	#define kDirChar '\\'
442 #else
443 	#define kDirChar '/'
444 #endif
445 
446 class XMP_FolderInfo {
447 public:
448 
XMP_FolderInfo()449 	XMP_FolderInfo() : dirRef(0) {};
~XMP_FolderInfo()450 	~XMP_FolderInfo() { if ( this->dirRef != 0 ) this->Close(); };
451 
452 	void Open ( const char * folderPath );
453 	void Close();
454 
455 	bool GetFolderPath ( XMP_VarString * folderPath );
456 	bool GetNextChild ( XMP_VarString * childName );
457 
458 private:
459 
460 	std::string folderPath;
461 
462 	#if XMP_WinBuild
463 		HANDLE dirRef;	// Windows uses FindFirstFile and FindNextFile.
464 	#else
465 		DIR * dirRef;	// Mac and UNIX use the POSIX opendir/readdir/closedir functions.
466 	#endif
467 
468 };
469 
470 extern FileMode GetFileMode ( const char * path );
471 
GetChildMode(std::string & path,XMP_StringPtr childName)472 static inline FileMode GetChildMode ( std::string & path, XMP_StringPtr childName )
473 {
474 	size_t pathLen = path.size();
475 	path += kDirChar;
476 	path += childName;
477 	FileMode mode = GetFileMode ( path.c_str() );
478 	path.erase ( pathLen );
479 	return mode;
480 }
481 
SplitLeafName(std::string * path,std::string * leafName)482 static inline void SplitLeafName ( std::string * path, std::string * leafName )
483 {
484 	size_t dirPos = path->size();
485 	if ( dirPos == 0 ) {
486 		leafName->erase();
487 		return;
488 	}
489 
490 	for ( --dirPos; dirPos > 0; --dirPos ) {
491 		#if XMP_WinBuild
492 			if ( (*path)[dirPos] == '/' ) (*path)[dirPos] = kDirChar;	// Tolerate both '\' and '/'.
493 		#endif
494 		if ( (*path)[dirPos] == kDirChar ) break;
495 	}
496 	if ( (*path)[dirPos] == kDirChar ) {
497 		leafName->assign ( &(*path)[dirPos+1] );
498 		path->erase ( dirPos );
499 	} else if ( dirPos == 0 ) {
500 		leafName->erase();
501 		leafName->swap ( *path );
502 	}
503 
504 }
505 
506 // -------------------------------------------------------------------------------------------------
507 
CheckBytes(const void * left,const void * right,size_t length)508 static inline bool CheckBytes ( const void * left, const void * right, size_t length )
509 {
510 	return (std::memcmp ( left, right, length ) == 0);
511 }
512 
513 // -------------------------------------------------------------------------------------------------
514 
CheckCString(const void * left,const void * right)515 static inline bool CheckCString ( const void * left, const void * right )
516 {
517 	return (strcmp ( (char*)left, (char*)right ) == 0);
518 }
519 
520 // -------------------------------------------------------------------------------------------------
521 // CheckFileSpace and RefillBuffer
522 // -------------------------------
523 //
524 // There is always a problem in file scanning of managing what you want to check against what is
525 // available in a buffer, trying to keep the logic understandable and minimize data movement. The
526 // CheckFileSpace and RefillBuffer functions are used here for a standard scanning model.
527 //
528 // The format scanning routines have an outer, "infinite" loop that looks for file markers. There
529 // is a local (on stack) buffer, a pointer to the current position in the buffer, and a pointer for
530 // the end of the buffer. The end pointer is just past the end of the buffer, "bufPtr == bufLimit"
531 // means you are out of data. The outer loop ends when the necessary markers are found or we reach
532 // the end of the file.
533 //
534 // The filePos is the file offset of the start of the current buffer. This is maintained so that
535 // we can tell where the packet is in the file, part of the info returned to the client.
536 //
537 // At each check CheckFileSpace is used to make sure there is enough data in the buffer for the
538 // check to be made. It refills the buffer if necessary, preserving the unprocessed data, setting
539 // bufPtr and bufLimit appropriately. If we are too close to the end of the file to make the check
540 // a failure status is returned.
541 
542 enum { kIOBufferSize = 128*1024 };
543 
544 struct IOBuffer {
545 	XMP_Int64 filePos;
546 	XMP_Uns8* ptr;
547 	XMP_Uns8* limit;
548 	size_t    len;
549 	XMP_Uns8  data [kIOBufferSize];
IOBufferIOBuffer550 	IOBuffer() : filePos(0), ptr(&data[0]), limit(ptr), len(0) {};
551 };
552 
FillBuffer(LFA_FileRef fileRef,XMP_Int64 fileOffset,IOBuffer * ioBuf)553 static inline void FillBuffer ( LFA_FileRef fileRef, XMP_Int64 fileOffset, IOBuffer* ioBuf )
554 {
555 	ioBuf->filePos = LFA_Seek ( fileRef, fileOffset, SEEK_SET );
556 	if ( ioBuf->filePos != fileOffset ) XMP_Throw ( "Seek failure in FillBuffer", kXMPErr_ExternalFailure );
557 	ioBuf->len = LFA_Read ( fileRef, &ioBuf->data[0], kIOBufferSize );
558 	ioBuf->ptr = &ioBuf->data[0];
559 	ioBuf->limit = ioBuf->ptr + ioBuf->len;
560 }
561 
MoveToOffset(LFA_FileRef fileRef,XMP_Int64 fileOffset,IOBuffer * ioBuf)562 static inline void MoveToOffset ( LFA_FileRef fileRef, XMP_Int64 fileOffset, IOBuffer* ioBuf )
563 {
564 	if ( (ioBuf->filePos <= fileOffset) && (fileOffset < (XMP_Int64)(ioBuf->filePos + ioBuf->len)) ) {
565 		size_t bufOffset = (size_t)(fileOffset - ioBuf->filePos);
566 		ioBuf->ptr = &ioBuf->data[bufOffset];
567 	} else {
568 		FillBuffer ( fileRef, fileOffset, ioBuf );
569 	}
570 }
571 
RefillBuffer(LFA_FileRef fileRef,IOBuffer * ioBuf)572 static inline void RefillBuffer ( LFA_FileRef fileRef, IOBuffer* ioBuf )
573 {
574 	ioBuf->filePos += (ioBuf->ptr - &ioBuf->data[0]);	// ! Increment before the read.
575 	size_t bufTail = ioBuf->limit - ioBuf->ptr;	// We'll re-read the tail portion of the buffer.
576 	if ( bufTail > 0 ) ioBuf->filePos = LFA_Seek ( fileRef, -((XMP_Int64)bufTail), SEEK_CUR );
577 	ioBuf->len = LFA_Read ( fileRef, &ioBuf->data[0], kIOBufferSize );
578 	ioBuf->ptr = &ioBuf->data[0];
579 	ioBuf->limit = ioBuf->ptr + ioBuf->len;
580 }
581 
CheckFileSpace(LFA_FileRef fileRef,IOBuffer * ioBuf,size_t neededLen)582 static inline bool CheckFileSpace ( LFA_FileRef fileRef, IOBuffer* ioBuf, size_t neededLen )
583 {
584 	if ( size_t(ioBuf->limit - ioBuf->ptr) < size_t(neededLen) ) {	// ! Avoid VS.Net compare warnings.
585 		RefillBuffer ( fileRef, ioBuf );
586 	}
587 	return (size_t(ioBuf->limit - ioBuf->ptr) >= size_t(neededLen));
588 }
589 
590 #endif /* __XMPFiles_Impl_hpp__ */
591