#ifndef AASPI_FILE_IO_H #define AASPI_FILE_IO_H //! \file //! \brief Interface to low level I/O routines #include "aaspi_types.h" #ifdef _WIN32 #include #include #include #include #define AASPI_OS_OPEN _open #define AASPI_OS_CLOSE _close #define AASPI_OS_LSEEK _lseeki64 #define AASPI_OS_READ _read #define AASPI_OS_WRITE _write #define AASPI_OS_MAXREAD 16777216 #define AASPI_OPENFLAGS O_BINARY; #define AASPI_DEF_PMODE ( _S_IREAD | _S_IWRITE) #define PATHSEP '\\' #else #include #include #include #include #include #include #define AASPI_OS_OPEN open #define AASPI_OS_CLOSE close #define AASPI_OS_LSEEK lseek #define AASPI_OS_READ read #define AASPI_OS_WRITE write #define AASPI_OS_MAXREAD SSIZE_MAX #define AASPI_OPENFLAGS O_LARGEFILE #define AASPI_DEF_PMODE (S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP| S_IROTH | S_IWOTH) #define PATHSEP '/' #endif #include #include #ifdef _WIN32 #include "aaspi_zgy.h" // for ZGY io. For now, this is exclusive on Windows. #include "aaspi_path.h" // to get file extension easily #define ZGYAPI_ASSERT(statement) \ if (!(statement)) { \ std::cout << "error in ZGY-API: " << #statement << ": " << error.getFullMsg() << std::endl; \ return -1; \ } using namespace Slb::Salmon::Zgy; #endif using namespace std; //! \brief Binary file access class //! //! This class provides access to the lower level OS device I/O functions. //! The intent is to provide an OS independent class to support 64bit //! low level I/O class AASPI_BinFile { public: enum OpenFlags { NotOpen = 0x00, ReadOnly = 0x01, WriteOnly = 0x02, ReadWrite = 0x03, Append = 0x04, Truncate = 0x08, Exclusive = 0x10 }; AASPI_BinFile() { init(); } ~AASPI_BinFile() { Close(); } bool Open(const string &fname, int flags=ReadOnly); /*!< Open file */ bool Close(); /*!< Close file */ aaspi_ssize_t Read(char *dst, size_t len); aaspi_ssize_t Write(const char *src, size_t len); bool Seek(aaspi_off_t pos, int whence=SEEK_SET); aaspi_off_t Tell(); /*!< Report current file position */ aaspi_off_t Size(); /*!< Report current file size */ const string &Filename(); /*!< Return name of open file */ bool isOpen(); /*!< Check to see if file is open */ bool atEOF(); /*!< Check to see if file is at end */ string errorMsg(); /*!< Return last error message */ int lastError(); /*!< Return last error code */ void clearError(); /*!< Clear last error */ #ifdef _WIN32 AASPI_ZGY_MetaData zgy_meta; // This has to be public so that aaspi_file.cpp can update it on time. float type_min, type_max, type_range; // min, max, and range of integral data type float data_min, data_max, data_range; // min, max, and range of actual data bool OpenZGY(const string &fname, int flags = ReadOnly); /*!< Open file */ bool CloseZGY(); /*!< Close file */ aaspi_ssize_t ReadZGY(char *dst, size_t len); aaspi_ssize_t WriteZGY(const char *src, size_t len); int initZGYWriter(); bool SeekZGY(aaspi_off_t pos, int whence = SEEK_SET); aaspi_off_t TellZGY(); /*!< Report current file position */ aaspi_off_t SizeZGY(); /*!< Report current file size */ void volume_correction(int*, int*, int*); #endif protected: string file_name; //!< File name int fd; //!< File descriptor int open_flags; //!< Keep track of open flags bool at_eof; //!< Check for eof on read int last_error; //!< Last error number from errno #ifdef _WIN32 // ZGY stuff ZgyApi::ReaderFactory* reader_factory; ZgyApi::ReaderPtr reader_accessor; ZgyApi::WriterFactory* writer_factory; ZgyApi::WriterPtr writer_accessor; ZgyApi::Error error; int seek_inline, seek_xline, seek_sample; // zero-based "seek" simulation for ZGY file size_t current_seek_byte; // current seeking point in byte int n_inline, n_xline, n_sample; // number of inline, xline, and sample to be read/write size_t max_trace_size, max_line_size, max_volume_size; // some zgy-specific variables to reduce calculations size_t size_of_n_inline, size_of_n_xline, size_of_n_sample; #endif void init(); }; inline void AASPI_BinFile::init() { fd= -1; open_flags=NotOpen; at_eof=false; file_name.clear(); last_error=0; #ifdef _WIN32 seek_inline = 0; // the starting point of seek is always (0,0,0) seek_xline = 0; seek_sample = 0; #endif } //! Opens binary file for input, output or both based on flags //! Sets filename only on successful open. //! \returns Returns true on successful open inline bool AASPI_BinFile::Open(const string &fname,int flags) { if (open_flags != NotOpen) { // Should we close the old file? NOPE, when file is opened, users can fire up as many read() commands as they want, each will call to this. return false; } int openFlags=AASPI_OPENFLAGS; // Local OS dependent flags if ((flags & ReadWrite) == ReadWrite) { openFlags |= O_RDWR; } else if (flags & WriteOnly) { openFlags |= (O_WRONLY | O_CREAT); } else { // ReadOnly is the default openFlags |= O_RDONLY; } if (flags & Append) openFlags |= O_APPEND; if (flags & Truncate) openFlags |= O_TRUNC; if (flags & Exclusive) openFlags |= O_EXCL; int rv = AASPI_OS_OPEN(fname.c_str(), openFlags, AASPI_DEF_PMODE); if (rv >= 0) { fd = rv; file_name = fname; open_flags = flags; at_eof = false; } else { init(); last_error = errno; return false; // Failed to open } return true; } inline bool AASPI_BinFile::Close() { int rv= -1; // Fall through value if file is not open if (open_flags != NotOpen) { rv = AASPI_OS_CLOSE(fd); init(); // reset file } if (rv > 0) { last_error=errno; return false; } return true; } //! Reads len bytes from a file into dst //! inline aaspi_ssize_t AASPI_BinFile::Read(char *dst, size_t len) { if (open_flags == NotOpen) return -1; if ((open_flags & ReadWrite) == WriteOnly) return -1; char *inptr = dst; while (len > 0) { size_t rsz = len; if (rsz > AASPI_OS_MAXREAD) rsz = AASPI_OS_MAXREAD; aaspi_ssize_t nb = AASPI_OS_READ(fd, inptr, rsz); if (nb < 0) { last_error = errno; return -1; } else if (nb == 0) { at_eof = true; break; } len -= nb; inptr += nb; } return (inptr - dst); } //! Writes len bytes from src to a file //! inline aaspi_ssize_t AASPI_BinFile::Write(const char *src, size_t len) { if (open_flags == NotOpen) return 0; if ((open_flags & ReadWrite) == ReadOnly) return 0; aaspi_ssize_t nb = AASPI_OS_WRITE(fd, src, len); if (nb < aaspi_ssize_t(len)) last_error = errno; return nb; } //! Seeks to the position pos relative to whence. //! whence may be SEEK_SET,SEEK_CUR oer SEEK_END //! \returns Returns true if successful inline bool AASPI_BinFile::Seek(aaspi_off_t pos, int whence) { if (open_flags == NotOpen) return false; at_eof=false; aaspi_off_t cp = AASPI_OS_LSEEK(fd, pos, whence); if (cp != (aaspi_off_t)-1) return true; last_error = errno; return false; } //! Report the current byte position of the file pointer //! \returns Returns -1 on failure inline aaspi_off_t AASPI_BinFile::Tell() { if (open_flags == NotOpen) return (aaspi_off_t) -1; aaspi_off_t cp=AASPI_OS_LSEEK(fd,(aaspi_off_t) 0,SEEK_CUR); if (cp == (aaspi_off_t)-1) last_error=errno; return cp; } //! Report the current file size in bytes //! \returns Returns -1 on failure inline aaspi_off_t AASPI_BinFile::Size() { if (open_flags == NotOpen) return (aaspi_off_t) -1; aaspi_off_t cp=AASPI_OS_LSEEK(fd,(aaspi_off_t) 0,SEEK_CUR); aaspi_off_t sz=AASPI_OS_LSEEK(fd,(aaspi_off_t) 0,SEEK_END); aaspi_off_t lv=AASPI_OS_LSEEK(fd,cp,SEEK_SET); if (cp != lv) { last_error=errno; sz= -1; } return sz; } #ifdef _WIN32 //------------------------------------------------------------ // ZGY-equivalent functions //------------------------------------------------------------ // Open ZGY binary inline bool AASPI_BinFile::OpenZGY(const string &fname, int flags) { if (open_flags != NotOpen) { // Should we close the old file? NOPE, when file is opened, users can fire up as many read() commands as they want, each will call to this. return false; } int openFlags = AASPI_OPENFLAGS; // Local OS dependent flags if ((flags & ReadWrite) == ReadWrite) { openFlags |= O_RDWR; } else if (flags & WriteOnly) { openFlags |= (O_WRONLY | O_CREAT); } else { // ReadOnly is the default openFlags |= O_RDONLY; } if (flags & Append) openFlags |= O_APPEND; if (flags & Truncate) openFlags |= O_TRUNC; if (flags & Exclusive) openFlags |= O_EXCL; if (flags & ReadOnly) { ZgyApi::ReaderFactory* reader_factory(ZgyApi::getReaderFactory()); ZGYAPI_ASSERT(reader_factory->create(ZgyApi::SimpleString(fname.c_str()), &reader_accessor, &error)); ZGYAPI_ASSERT(reader_accessor->getMetaData(&zgy_meta, &error)); // Needed for the true inline, xline, and sample limit of the binary volume max_trace_size = sizeof(float) * zgy_meta.size[2]; max_line_size = max_trace_size * zgy_meta.size[1]; max_volume_size = max_line_size * zgy_meta.size[0]; } // At this point, zgy reader is set up correctly. Assume successful operation file_name = fname; open_flags = flags; seek_inline = 0; // First time opening (even after a close). Reinitialize Seek simulation. seek_xline = 0; seek_sample = 0; at_eof = false; return true; } // Close ZGY binary inline bool AASPI_BinFile::CloseZGY() { int rv = -1; // Fall through value if file is not open if (open_flags != NotOpen) { // Check if we are writing to a ZGY binary // If so, finalize it if (open_flags & WriteOnly) { ZGYAPI_ASSERT(writer_accessor->finalize(&error)); // close ZGY file } init(); // reset file } if (rv > 0) { last_error = errno; return false; } return true; } // Read ZGY binary inline aaspi_ssize_t AASPI_BinFile::ReadZGY(char *dst, size_t len) { if (open_flags == NotOpen) return -1; if ((open_flags & ReadWrite) == WriteOnly) return -1; // Fool proof current_seek_byte = max_line_size*seek_inline + max_trace_size*seek_xline + sizeof(float)*seek_sample; if ((current_seek_byte + len) > max_volume_size) len = max_volume_size - current_seek_byte; // First, we need to convert len to sample, xline, then inline n_inline = len / (max_line_size); size_of_n_inline = max_line_size*n_inline; n_xline = (len - size_of_n_inline) / max_trace_size; size_of_n_xline = max_trace_size*n_xline; n_sample = (len - size_of_n_inline - size_of_n_xline) / sizeof(float); // I expect this to be zero in most cases size_of_n_sample = sizeof(float)*n_sample; // Now convert dst to a local float pointer. float* databuff = (float*)dst; // Then simulate sequential read per each axis. Remember, the size of n0, n1, n2 in ZGYAPI cannot be 0. The size of databuff = n0*n1*n2!!! if (n_sample > 0) { ZGYAPI_ASSERT(reader_accessor->read(seek_inline, seek_xline, seek_sample, 1, 1, n_sample, databuff, &error)); seek_sample += n_sample; // don't forget to simulate seek right after a read databuff += size_of_n_sample; // move the pointer forward to the end of n_sample read in volume_correction(&seek_inline, &seek_xline, &seek_sample); // make sure to update seek position in case seek_sample goes beyond limit } if (n_xline > 0) { ZGYAPI_ASSERT(reader_accessor->read(seek_inline, seek_xline, seek_sample, 1, n_xline, zgy_meta.size[2], databuff, &error)); seek_xline += n_xline; // don't forget to simulate seek right after a read databuff += size_of_n_xline; // move the pointer forward to the end of n_xline read in volume_correction(&seek_inline, &seek_xline, &seek_sample); // make sure to update seek position in case seek_xline goes beyond limit } if (n_inline > 0) { ZGYAPI_ASSERT(reader_accessor->read(seek_inline, seek_xline, seek_sample, n_inline, zgy_meta.size[1], zgy_meta.size[2], databuff, &error)); seek_inline += n_inline; // don't forget to simulate seek right after a read databuff += size_of_n_inline; // move the pointer forward to the end of n_inline read in } // Modify databuff to account for data range if data type is not float32 if (zgy_meta.datatype != ZgyApi::DataType::Float32) { size_t current_length = 0; databuff = (float*)dst; // reset pointer databuff to the beginning of output array while (current_length < len) { // rescale read data into the correct range (converting integral storage to float) *databuff = data_min + (*databuff - type_min) / type_range * data_range; databuff++; current_length += sizeof(float); } } if (seek_inline > (zgy_meta.size[0] - 1)) at_eof = true; // most likely when all data were read return (databuff - (float*)dst); } // Write to ZGY binary inline aaspi_ssize_t AASPI_BinFile::WriteZGY(const char *src, size_t len) { if (open_flags == NotOpen) return 0; if ((open_flags & ReadWrite) == ReadOnly) return 0; // Fool proof current_seek_byte = max_line_size*seek_inline + max_trace_size*seek_xline + sizeof(float)*seek_sample; if ((current_seek_byte + len) > max_volume_size) len = max_volume_size - current_seek_byte; // First, we need to convert len to sample, xline, then inline n_inline = len / (max_line_size); size_of_n_inline = max_line_size*n_inline; n_xline = (len - size_of_n_inline) / max_trace_size; size_of_n_xline = max_trace_size*n_xline; n_sample = (len - size_of_n_inline - size_of_n_xline) / sizeof(float); // I expect this to be zero in most cases size_of_n_sample = sizeof(float)*n_sample; // Define data buffer pointer float* databuff; // Depend on data type, either simply take src or convert integral (signed int 8-bit and 16-bit) to float if (zgy_meta.datatype != ZgyApi::DataType::Float32) { // Now convert src to a local float pointer. int databuff_length = len / sizeof(float); databuff = new float[databuff_length]; // Need to take care of scaling int count; float* current_pointer = (float*)src; for (count = 0; count < databuff_length; ++count) { databuff[count] = *current_pointer; databuff[count] = type_min + (databuff[count] - data_min) / data_range * type_range; current_pointer++; } } else { // float32. databuff = (float*)src; } // Then simulate sequential write per each axis. Remember, the size of n0, n1, n2 in ZGYAPI cannot be 0. The size of databuff = n0*n1*n2!!! if (n_sample > 0) { ZGYAPI_ASSERT(writer_accessor->write(seek_inline, seek_xline, seek_sample, 1, 1, n_sample, databuff, &error)); seek_sample += n_sample; // don't forget to simulate seek right after a read databuff += size_of_n_sample; // move the pointer forward to the end of n_sample read in volume_correction(&seek_inline, &seek_xline, &seek_sample); // make sure to update seek position in case seek_sample goes beyond limit } if (n_xline > 0) { ZGYAPI_ASSERT(writer_accessor->write(seek_inline, seek_xline, seek_sample, 1, n_xline, zgy_meta.size[2], databuff, &error)); seek_xline += n_xline; // don't forget to simulate seek right after a read databuff += size_of_n_xline; // move the pointer forward to the end of n_xline read in volume_correction(&seek_inline, &seek_xline, &seek_sample); // make sure to update seek position in case seek_xline goes beyond limit } if (n_inline > 0) { ZGYAPI_ASSERT(writer_accessor->write(seek_inline, seek_xline, seek_sample, n_inline, zgy_meta.size[1], zgy_meta.size[2], databuff, &error)); seek_inline += n_inline; // don't forget to simulate seek right after a read databuff += size_of_n_inline; // move the pointer forward to the end of n_inline read in } return len; } // Initialize ZGY binary for writing inline int AASPI_BinFile::initZGYWriter() { ZgyApi::WriterFactory* writer_factory(ZgyApi::getWriterFactory()); ZGYAPI_ASSERT(writer_factory->create(ZgyApi::SimpleString(file_name.c_str()), zgy_meta, &writer_accessor, &error)); // shit, this is bad, we need to find a way to redefine metadata later max_trace_size = sizeof(float) * zgy_meta.size[2]; max_line_size = max_trace_size * zgy_meta.size[1]; max_volume_size = max_line_size * zgy_meta.size[0]; return 0; } // Seek within ZGY binary inline bool AASPI_BinFile::SeekZGY(aaspi_off_t pos, int whence) { if (open_flags == NotOpen) return false; at_eof = false; // Simulate seek based on whence and pos (offset) value // whence can be SEEK_SET, SEEK_CUR, or SEEK_END. Each case has different implementation. if (whence == SEEK_SET) { // most common situation: seek from the beginning of the file. // Fool proof if (pos > max_volume_size) pos = max_volume_size; } else if (whence == SEEK_CUR) { // less common: seek from current position // Fool proof current_seek_byte = max_line_size*seek_inline + max_trace_size*seek_xline + sizeof(float)*seek_sample; // need this to add on top of pos if ((current_seek_byte + pos) > max_volume_size) pos = max_volume_size - current_seek_byte; } else { // very rare situation: seek from the end of the file. pos better be negative // Fool proof current_seek_byte = max_volume_size + sizeof(float); // the end of the last data point, which is the begin of "out-side-of-file" if ((current_seek_byte + pos) > max_volume_size) pos = max_volume_size - current_seek_byte; } // pos is now the total number of bytes to seek through from the beginning. n_inline = pos / (max_line_size); size_of_n_inline = max_line_size*n_inline; n_xline = (pos - size_of_n_inline) / max_trace_size; size_of_n_xline = max_trace_size*n_xline; n_sample = (pos - size_of_n_inline - size_of_n_xline) / sizeof(float); // I expect this to be zero in most cases seek_inline = n_inline; seek_xline = n_xline; seek_sample = n_sample; return true; // assume I have fool-proof enough } // Tell the current "simulated" seek position of ZGY file inline aaspi_off_t AASPI_BinFile::TellZGY() { if (open_flags == NotOpen) return (aaspi_off_t)-1; aaspi_off_t cp = max_line_size*seek_inline + max_trace_size*seek_xline + sizeof(float)*seek_sample; return cp; } // Simulate size of ZGY file as if it's AASPI binary inline aaspi_off_t AASPI_BinFile::SizeZGY() { if (open_flags == NotOpen) return (aaspi_off_t)-1; aaspi_off_t sz = max_volume_size; return sz; } // A procedure to correct seek info for zgy file in case seek value is greater than the limit of an axis (e.g. seek_sample > (zgy_meta.size[2]-1)) inline void AASPI_BinFile::volume_correction(int* seek_inline, int* seek_xline, int* seek_sample) { while (*seek_sample > (zgy_meta.size[2] - 1)) { // if seek_sample is greater than limit, then we need to reset it down, and increase xline count *seek_sample -= zgy_meta.size[2]; *seek_xline += 1; } while (*seek_xline > (zgy_meta.size[1] - 1)) { *seek_xline -= zgy_meta.size[1]; *seek_inline += 1; } // Assume user is not stupid enough to seek beyond the file limit. } #endif inline const string &AASPI_BinFile::Filename() { return file_name; } inline bool AASPI_BinFile::isOpen() { return open_flags != NotOpen; } inline bool AASPI_BinFile::atEOF() { return at_eof; } inline string AASPI_BinFile::errorMsg() { return string(strerror(last_error)); } inline int AASPI_BinFile::lastError() { return last_error; } inline void AASPI_BinFile::clearError() { last_error=0; } #endif