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