// fileio.cpp -- // $Id$ // This is part of Metakit, see http://www.equi4.com/metakit/ /** @file * Implementation of c4_FileStrategy and c4_FileStream */ #include "header.h" #include "mk4io.h" #if q4_WIN32 #if q4_MSVC && !q4_STRICT #pragma warning(disable: 4201) // nonstandard extension used : ... #endif #define WIN32_LEAN_AND_MEAN #include #if !defined (q4_WINCE) #include #include #include #endif #endif #if q4_UNIX && HAVE_MMAP #include #include #endif #if q4_UNIX #include #include #endif #if q4_WINCE #define _get_osfhandle(x) x #endif #ifndef _O_NOINHERIT #define _O_NOINHERIT 0 #endif ///////////////////////////////////////////////////////////////////////////// // // The "Carbon" version of a build on Macintosh supports running under // either MacOS 7..9 (which has no mmap), or MacOS X (which has mmap). // The logic below was adapted from a contribution by Paul Snively, it // decides at run time which case it is, and switches I/O calls to match. #if defined (q4_CARBON) && q4_CARBON //#if q4_MAC && !defined (__MACH__) && (!q4_MWCW || __MWERKS__ >= 0x3000) #undef HAVE_MMAP #define HAVE_MMAP 1 #include #include #define PROT_NONE 0x00 #define PROT_READ 0x01 #define PROT_WRITE 0x02 #define PROT_EXEC 0x04 #define MAP_SHARED 0x0001 #define MAP_PRIVATE 0x0002 #define MAP_FIXED 0x0010 #define MAP_RENAME 0x0020 #define MAP_NORESERVE 0x0040 #define MAP_INHERIT 0x0080 #define MAP_NOEXTEND 0x0100 #define MAP_HASSEMAPHORE 0x0200 typedef unsigned long t4_u32; static t4_u32 sfwRefCount = 0; static CFBundleRef systemFramework = NULL; static char* fake_mmap(char*, t4_u32, int, int, int, long long) { return (char*) -1L; } static int fake_munmap(char*, t4_u32) { return 0; } static FILE* (*my_fopen)(const char*,const char*) = fopen; static int (*my_fclose)(FILE*) = fclose; static long (*my_ftell)(FILE*) = ftell; static int (*my_fseek)(FILE*,long,int) = fseek; static t4_u32 (*my_fread)(void* ptr,t4_u32,t4_u32,FILE*) = fread; static t4_u32 (*my_fwrite)(const void* ptr,t4_u32,t4_u32,FILE*) = fwrite; static int (*my_ferror)(FILE*) = ferror; static int (*my_fflush)(FILE*) = fflush; static int (*my_fileno)(FILE*) = fileno; static char* (*my_mmap)(char*,t4_u32,int,int,int,long long) = fake_mmap; static int (*my_munmap)(char*,t4_u32) = fake_munmap; static void InitializeIO() { if (sfwRefCount++) return; // race condition, infinitesimal risk FSRef theRef; if (FSFindFolder(kOnAppropriateDisk, kFrameworksFolderType, false, &theRef) == noErr) { CFURLRef fw = CFURLCreateFromFSRef(kCFAllocatorSystemDefault, &theRef); if (fw) { CFURLRef bd = CFURLCreateCopyAppendingPathComponent(kCFAllocatorSystemDefault, fw, CFSTR("System.framework"), false); CFRelease(fw); if (bd) { systemFramework = CFBundleCreate(kCFAllocatorSystemDefault, bd); CFRelease(bd); } } if (!systemFramework || !CFBundleLoadExecutable(systemFramework)) return; #define F(x) CFBundleGetFunctionPointerForName(systemFramework, CFSTR(#x)) my_fopen = (FILE* (*)(const char*,const char*)) F(fopen); my_fclose = (int (*)(FILE*)) F(fclose); my_ftell = (long (*)(FILE*)) F(ftell); my_fseek = (int (*)(FILE*,long,int)) F(fseek); my_fread = (t4_u32 (*)(void* ptr,t4_u32,t4_u32,FILE*)) F(fread); my_fwrite = (t4_u32 (*)(const void* ptr,t4_u32,t4_u32,FILE*)) F(fwrite); my_ferror = (int (*)(FILE*)) F(ferror); my_fflush = (int (*)(FILE*)) F(fflush); my_fileno = (int (*)(FILE*)) F(fileno); my_mmap = (char* (*)(char*,t4_u32,int,int,int,long long)) F(mmap); my_munmap = (int (*)(char*,t4_u32)) F(munmap); #undef F d4_assert(my_fopen && my_fclose && my_ftell && my_fseek && my_fread && my_fwrite && my_ferror && my_fflush && my_fileno && my_mmap && my_munmap); } } static void FinalizeIO() { if (--sfwRefCount) return; // race condition, infinitesimal risk if (systemFramework) { CFBundleUnloadExecutable(systemFramework); CFRelease(systemFramework); systemFramework = 0; } } #define fopen my_fopen #define fclose my_fclose #define ftell my_ftell #define fseek my_fseek #define fread my_fread #define fwrite my_fwrite #define ferror my_ferror #define fflush my_fflush #define fileno my_fileno #define mmap my_mmap #define munmap my_munmap #else #define InitializeIO() #define FinalizeIO() #endif ///////////////////////////////////////////////////////////////////////////// #if q4_CHECK #include void f4_AssertionFailed(const char* cond_, const char* file_, int line_) { fprintf(stderr, "Assertion failed: %s (file %s, line %d)\n", cond_, file_, line_); abort(); } #endif //q4_CHECK ///////////////////////////////////////////////////////////////////////////// // c4_FileStream c4_FileStream::c4_FileStream (FILE* stream_, bool owned_) : _stream (stream_), _owned (owned_) { } c4_FileStream::~c4_FileStream () { if (_owned) fclose(_stream); } int c4_FileStream::Read(void* buffer_, int length_) { d4_assert(_stream != 0); return (int) fread(buffer_, 1, length_, _stream); } bool c4_FileStream::Write(const void* buffer_, int length_) { d4_assert(_stream != 0); return (int) fwrite(buffer_, 1, length_, _stream) == length_; } ///////////////////////////////////////////////////////////////////////////// // c4_FileStrategy c4_FileStrategy::c4_FileStrategy (FILE* file_) : _file (file_), _cleanup (0) { InitializeIO(); ResetFileMapping(); } c4_FileStrategy::~c4_FileStrategy () { _file = 0; ResetFileMapping(); if (_cleanup) fclose(_cleanup); d4_assert(_mapStart == 0); FinalizeIO(); } bool c4_FileStrategy::IsValid() const { return _file != 0; } t4_i32 c4_FileStrategy::FileSize() { d4_assert(_file != 0); long size = -1; long old = ftell(_file); if (old >= 0 && fseek(_file, 0, 2) == 0) { long pos = ftell(_file); if (fseek(_file, old, 0) == 0) size = pos; } if (size < 0) _failure = ferror(_file); return size; } t4_i32 c4_FileStrategy::FreshGeneration() { d4_assert(false); return 0; } void c4_FileStrategy::ResetFileMapping() { #if q4_WIN32 if (_mapStart != 0) { _mapStart -= _baseOffset; d4_dbgdef(BOOL g =) ::UnmapViewOfFile((char*) _mapStart); d4_assert(g); _mapStart = 0; _dataSize = 0; } if (_file != 0) { t4_i32 len = FileSize(); if (len > 0) { FlushFileBuffers((HANDLE) _get_osfhandle(_fileno(_file))); HANDLE h = ::CreateFileMapping((HANDLE) _get_osfhandle(_fileno(_file)), 0, PAGE_READONLY, 0, len, 0); d4_assert(h); // check for errors, but can continue without mapping if (h) { _mapStart = (t4_byte*) ::MapViewOfFile(h, FILE_MAP_READ, 0, 0, len); d4_assert(_mapStart != 0); if (_mapStart != 0) { _mapStart += _baseOffset; _dataSize = len - _baseOffset; } d4_dbgdef(BOOL f =) ::CloseHandle(h); d4_assert(f); } } } #elif HAVE_MMAP if (_mapStart != 0) { _mapStart -= _baseOffset; munmap((char*) _mapStart, _baseOffset + _dataSize); // also loses const _mapStart = 0; _dataSize = 0; } if (_file != 0) { t4_i32 len = FileSize(); if (len > 0) { _mapStart = (const t4_byte*) mmap(0, len, PROT_READ, MAP_SHARED, fileno(_file), 0); if (_mapStart != (void*) -1L) { _mapStart += _baseOffset; _dataSize = len - _baseOffset; } else _mapStart = 0; } } #endif } bool c4_FileStrategy::DataOpen(const char* fname_, int mode_) { d4_assert(!_file); #if q4_WIN32 && !q4_BORC && !q4_WINCE int flags = _O_BINARY | _O_NOINHERIT | (mode_ > 0 ? _O_RDWR : _O_RDONLY); int fd = _open(fname_, flags); if (fd != -1) _cleanup = _file = _fdopen(fd, mode_ > 0 ? "r+b" : "rb"); #else _cleanup = _file = fopen(fname_, mode_ > 0 ? "r+b" : "rb"); #if q4_UNIX if (_file != 0) fcntl(fileno(_file), F_SETFD, FD_CLOEXEC); #endif //q4_UNIX #endif //q4_WIN32 && !q4_BORC && !q4_WINCE if (_file != 0) { ResetFileMapping(); return true; } if (mode_ > 0) { #if q4_WIN32 && !q4_BORC && !q4_WINCE fd = _open(fname_, flags | _O_CREAT, _S_IREAD | _S_IWRITE); if (fd != -1) _cleanup = _file = _fdopen(fd, "w+b"); #else _cleanup = _file = fopen(fname_, "w+b"); #if q4_UNIX if (_file != 0) fcntl(fileno(_file), F_SETFD, FD_CLOEXEC); #endif //q4_UNIX #endif //q4_WIN32 && !q4_BORC && !q4_WINCE } //d4_assert(_file != 0); return false; } int c4_FileStrategy::DataRead(t4_i32 pos_, void* buf_, int len_) { d4_assert(_baseOffset + pos_ >= 0); d4_assert(_file != 0); //printf("DataRead at %d len %d\n", pos_, len_); return fseek(_file, _baseOffset + pos_, 0) != 0 ? -1 : (int) fread(buf_, 1, len_, _file); } void c4_FileStrategy::DataWrite(t4_i32 pos_, const void* buf_, int len_) { d4_assert(_baseOffset + pos_ >= 0); d4_assert(_file != 0); #if 0 if (_mapStart <= buf_ && buf_ < _mapStart + _dataSize) { printf("DataWrite %08x at %d len %d (map %d)\n", buf_, pos_, len_, (const t4_byte*) buf_ - _mapStart + _baseOffset); } else { printf("DataWrite %08x at %d len %d\n", buf_, pos_, len_); } fprintf(stderr, " _mapStart %08x _dataSize %d buf_ %08x len_ %d _baseOffset %d\n", _mapStart, _dataSize, buf_, len_, _baseOffset); printf(" _mapStart %08x _dataSize %d buf_ %08x len_ %d _baseOffset %d\n", _mapStart, _dataSize, buf_, len_, _baseOffset); fflush(stdout); #endif #if q4_WIN32 || __hpux || __MACH__ // if (buf_ >= _mapStart && buf_ <= _mapLimit - len_) // a horrendous hack to allow file mapping for Win95 on network drive // must use a temp buf to avoid write from mapped file to same file // // 6-Feb-1999 -- this workaround is not thread safe // 30-Nov-2001 -- changed to use the stack so now it is // 28-Oct-2002 -- added HP/UX to the mix, to avoid hard lockup char tempBuf [4096]; d4_assert(len_ <= sizeof tempBuf); buf_ = memcpy(tempBuf, buf_, len_); #endif if (fseek(_file, _baseOffset + pos_, 0) != 0 || (int) fwrite(buf_, 1, len_, _file) != len_) { _failure = ferror(_file); d4_assert(_failure != 0); d4_assert(true); // always force an assertion failure in debug mode } } void c4_FileStrategy::DataCommit(t4_i32 limit_) { d4_assert(_file != 0); if (fflush(_file) < 0) { _failure = ferror(_file); d4_assert(_failure != 0); d4_assert(true); // always force an assertion failure in debug mode return; } if (limit_ > 0) { #if 0 // can't truncate file in a portable way! // unmap the file first, WinNT is more picky about this than Win95 FILE* save = _file; _file = 0; ResetFileMapping(); _file = save; _file->SetLength(limit_); // now we can resize the file #endif ResetFileMapping(); // remap, since file length may have changed } } /////////////////////////////////////////////////////////////////////////////