//# MultiFileBase.cc: Class to combine multiple files in a single one //# Copyright (C) 2014 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library is distributed in the hope that it will be useful, but WITHOUT //# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or //# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: RegularFileIO.h 20551 2009-03-25 00:11:33Z Malte.Marquarding $ //# Includes #include #include #include #include #include #include // for fileSTAT #include // needed for stat or stat64 #include #include // for posix_memalign namespace casacore { //# NAMESPACE CASACORE - BEGIN void operator<< (ostream& ios, const MultiFileInfo& info) { ios << info.name << ' ' << info.blockNrs << ' ' << info.fsize << ' ' << info.curBlock << ' ' << info.dirty << endl; } void operator<< (AipsIO& ios, const MultiFileInfo& info) { ios << info.name << info.blockNrs << info.fsize; } void operator>> (AipsIO& ios, MultiFileInfo& info) { ios >> info.name >> info.blockNrs >> info.fsize; } MultiFileBase::MultiFileBase (const String& name, Int blockSize, Bool useODirect) : itsBlockSize (blockSize), itsNrBlock (0), itsHdrCounter (0), itsUseODirect (useODirect), itsWritable (False), // usually reset by derived class itsChanged (False) { // Unset itsUseODirect if the OS does not support it. #ifndef HAVE_O_DIRECT itsUseODirect = False; #endif itsName = Path(name).expandedName(); } void MultiFileBase::setNewFile() { // New file. itsChanged = True; // Use file system block size, but not less than given size. if (itsBlockSize <= 0) { struct fileSTAT sfs; fileSTAT (itsName.c_str(), &sfs); Int64 blksz = sfs.st_blksize; itsBlockSize = std::max (-itsBlockSize, blksz); } AlwaysAssert (itsBlockSize > 0, AipsError); } MultiFileBase::~MultiFileBase() { itsInfo.clear(); } uInt MultiFileBase::nfile() const { Int nf = 0; for (vector::const_iterator iter=itsInfo.begin(); iter!=itsInfo.end(); ++iter) { if (! iter->name.empty()) { nf++; } } return nf; } void MultiFileBase::flush() { // Flush all buffers if needed. for (vector::iterator iter=itsInfo.begin(); iter!=itsInfo.end(); ++iter) { if (iter->dirty) { writeDirty (*iter); } } // Header only needs to be written if blocks were added since last flush. if (itsChanged) { writeHeader(); itsChanged = False; } flushFile(); } Int64 MultiFileBase::read (Int fileId, void* buf, Int64 size, Int64 offset) { if (fileId >= Int(itsInfo.size()) || itsInfo[fileId].name.empty()) { throw AipsError ("MultiFileBase::read - invalid fileId given"); } char* buffer = static_cast(buf); MultiFileInfo& info = itsInfo[fileId]; char* infoBuffer = info.buffer->data; // Determine the logical block to read and the start offset in that block. Int64 nrblk = (info.fsize + itsBlockSize - 1) / itsBlockSize; Int64 blknr = offset/itsBlockSize; Int64 start = offset - blknr*itsBlockSize; Int64 done = 0; Int64 szdo = std::min(size, info.fsize - offset); // not past EOF // Read until done. while (done < szdo) { AlwaysAssert (blknr < nrblk, AipsError); Int64 todo = std::min(szdo-done, itsBlockSize-start); // If already in buffer, copy from there. if (blknr == info.curBlock) { memcpy (buffer, infoBuffer+start, todo); } else { // Read directly into buffer if it fits exactly and no O_DIRECT. if (todo == itsBlockSize && !itsUseODirect) { readBlock (info, blknr, buffer); } else { if (info.dirty) { writeDirty (info); } // Read into file buffer and copy correct part. readBlock (info, blknr, infoBuffer); info.curBlock = blknr; memcpy (buffer, infoBuffer+start, todo); } } // Increment counters. done += todo; buffer += todo; blknr++; start = 0; } return done; } Int64 MultiFileBase::write (Int fileId, const void* buf, Int64 size, Int64 offset) { if (fileId >= Int(itsInfo.size()) || itsInfo[fileId].name.empty()) { throw AipsError ("MultiFileBase::write - invalid fileId given"); } const char* buffer = static_cast(buf); AlwaysAssert (itsWritable, AipsError); MultiFileInfo& info = itsInfo[fileId]; char* infoBuffer = info.buffer->data; // Determine the logical block to write and the start offset in that block. Int64 blknr = offset/itsBlockSize; Int64 start = offset - blknr*itsBlockSize; Int64 done = 0; // If beyond EOF, add blocks as needed. Int64 lastblk = blknr + (start+size+itsBlockSize-1) / itsBlockSize; Int64 curnrb = (info.fsize+itsBlockSize-1) / itsBlockSize; if (lastblk >= curnrb) { extend (info, lastblk); itsChanged = True; } // Write until all done. while (done < size) { Int64 todo = std::min(size-done, itsBlockSize-start); // Favor sequential writing, thus write current buffer first. if (blknr == info.curBlock) { memcpy (infoBuffer+start, buffer, todo); info.dirty = True; if (done+todo > size) { writeDirty (info); } } else if (todo == itsBlockSize && !itsUseODirect) { // Write directly from buffer if it fits exactly and no O_DIRECT. writeBlock (info, blknr, buffer); } else { // Write into temporary buffer and copy correct part. // First write possibly dirty buffer. if (info.dirty) { writeDirty (info); } if (blknr >= curnrb) { memset (infoBuffer, 0, itsBlockSize); } else { readBlock (info, blknr, infoBuffer); } info.curBlock = blknr; memcpy (infoBuffer+start, buffer, todo); info.dirty = True; } done += todo; buffer += todo; blknr++; start = 0; } if (offset+size > info.fsize) { info.fsize = offset+size; } return done; } void MultiFileBase::resync() { AlwaysAssert (!itsChanged, AipsError); // Clear all blocknrs. for (vector::iterator iter=itsInfo.begin(); iter!=itsInfo.end(); ++iter) { AlwaysAssert (!iter->dirty, AipsError); iter->curBlock = -1; } readHeader(); } Int MultiFileBase::addFile (const String& fname) { if (fname.empty()) { throw AipsError("MultiFileBase::addFile - empty file name given"); } // Only use the basename part (to avoid directory rename problems). String bname = Path(fname).baseName(); // Check that file name is not used yet. // Also determine (last) free file slot. uInt inx = itsInfo.size(); uInt i = 0; for (vector::iterator iter=itsInfo.begin(); iter!=itsInfo.end(); ++iter, ++i) { if (iter->name.empty()) { inx = i; // free file slot } else if (bname == iter->name) { throw AipsError ("MultiFileBase::addFile - file name " + bname + " already in use"); } } // Add a new file entry if needed. if (inx == itsInfo.size()) { itsInfo.resize (inx+1); } itsInfo[inx] = MultiFileInfo(itsBlockSize, itsUseODirect); itsInfo[inx].name = bname; doAddFile (itsInfo[inx]); itsChanged = True; return inx; } Int MultiFileBase::fileId (const String& fname, Bool throwExcp) const { // Only use the basename part (to avoid directory rename problems). String bname = Path(fname).baseName(); for (size_t i=0; i= Int(itsInfo.size()) || itsInfo[fileId].name.empty()) { throw AipsError ("MultiFileBase::deleteFile - invalid fileId given"); } MultiFileInfo& info = itsInfo[fileId]; doDeleteFile (info); // Clear this slot. info = MultiFileInfo(); itsChanged = True; } MultiFileInfo::MultiFileInfo (Int64 bufSize, Bool useODirect) : curBlock (-1), fsize (0), dirty (False), buffer (new MultiFileBuffer (bufSize, useODirect)) {} MultiFileBuffer::MultiFileBuffer (size_t bufSize, Bool useODirect) : data (0) { const size_t align = 4096; // Free buffer (in case used). if (data) { free (data); data = 0; } if (bufSize > 0) { if (useODirect && bufSize%align != 0) { throw AipsError("MultiFile bufsize " + String::toString(bufSize) + " must be a multiple of 4096 when using O_DIRECT"); } // Note that the error messages do a malloc as well, but small // compared to the requested malloc, so they'll probably succeed. void* ptr; if (useODirect) { if (posix_memalign (&ptr, align, bufSize) != 0) { throw AllocError("MultiFileBuffer: failed to allocate aligned buffer", bufSize); } } else { ptr = malloc (bufSize); if (!ptr) { throw AllocError("MultiFileBuffer: failed to allocate buffer", bufSize); } } data = static_cast(ptr); } } } //# NAMESPACE CASACORE - END