#pragma once // External #include #include // Internal #include "BufferedFrame2D.hpp" #include "DEN/DenFileInfo.hpp" #include "Frame2DI.hpp" #include "Frame2DReaderI.hpp" namespace CTL { namespace io { /** * Implementation of the ProjectionReader for projections and projection matrices stored in the * den files. */ template class DenFrame2DReader : virtual public Frame2DReaderI // Frame2DReaderI will be only once in the family tree { public: /**Constructs DenFrame2DReader from file name. * * * @param denFile * @param denFile File in a DEN format to read by frames. * @param additionalBufferNum Number of additional buffers to allocate. Defaults to 0 for 1 * default buffer. */ DenFrame2DReader(std::string denFile, uint32_t additionalBufferNum = 0); /// Destructor ~DenFrame2DReader(); /// Copy constructor DenFrame2DReader(const DenFrame2DReader& b); // Copy assignment with copy and swap DenFrame2DReader& operator=(DenFrame2DReader b); static void swap(DenFrame2DReader& a, DenFrame2DReader& b); // Move constructor DenFrame2DReader(DenFrame2DReader&& b); // Move assignment DenFrame2DReader& operator=(DenFrame2DReader&& other); std::shared_ptr> readFrame(unsigned int i) override; std::shared_ptr> readBufferedFrame(unsigned int i); void readFrameIntoBuffer(unsigned int frameID, T* outside_buffer); uint32_t dimx() const override; uint32_t dimy() const override; uint32_t dimz() const override; std::string getFileName() const; /**Returns file name of the underlying DEN file.**/ protected: std::string denFile; uint64_t offset; // protected: // Visible in inheritance structure uint32_t sizex, sizey, sizez; DenSupportedType dataType; int elementByteSize; private: mutable std::mutex* consistencyMutexes; uint8_t** buffers; T** buffer_copys; uint32_t additionalBufferNum; }; template DenFrame2DReader::DenFrame2DReader(std::string denFile, uint32_t additionalBufferNum) : denFile(denFile) , additionalBufferNum(additionalBufferNum) { DenFileInfo pi = DenFileInfo(this->denFile); this->offset = pi.getOffset(); this->sizex = pi.dimx(); this->sizey = pi.dimy(); this->sizez = pi.dimz(); this->dataType = pi.getDataType(); this->elementByteSize = pi.elementByteSize(); this->consistencyMutexes = new std::mutex[1 + additionalBufferNum]; this->buffers = new uint8_t*[1 + additionalBufferNum]; this->buffer_copys = new T*[1 + additionalBufferNum]; for(uint32_t i = 0; i != 1 + additionalBufferNum; i++) { this->buffers[i] = new uint8_t[elementByteSize * sizex * sizey]; this->buffer_copys[i] = new T[sizex * sizey]; } // Buffers are used for the alocation of new frames. Since this class uses the instance that // copies memory, this memory might me reused. } // This mumbo jumbo is for correctly deleting object after move assignment operation was // performed to be able to delete such object with stealed internals where its buffers are // nullptr. template DenFrame2DReader::~DenFrame2DReader() { if(consistencyMutexes != nullptr) { delete[] consistencyMutexes; } consistencyMutexes = nullptr; if(buffers != nullptr) { for(uint32_t i = 0; i != 1 + additionalBufferNum; i++) { if(buffers[i] != nullptr) { delete buffers[i]; } buffers[i] = nullptr; } delete[] buffers; } buffers = nullptr; if(buffer_copys != nullptr) { for(uint32_t i = 0; i != 1 + additionalBufferNum; i++) { if(buffer_copys[i] != nullptr) { delete buffer_copys[i]; } buffer_copys[i] = nullptr; } delete[] buffer_copys; } buffer_copys = nullptr; } /**Copy constructor of DenFrame2DReader from another element. *Basically contructs everything from scratch by probing file on the disk. */ template DenFrame2DReader::DenFrame2DReader(const DenFrame2DReader& b) : DenFrame2DReader::DenFrame2DReader(b.denFile, b.additionalBufferNum) { } template void DenFrame2DReader::swap(DenFrame2DReader& a, DenFrame2DReader& b) { std::swap(a.denFile, b.denFile); std::swap(a.offset, b.offset); std::swap(a.sizex, b.sizex); std::swap(a.sizey, b.sizey); std::swap(a.sizez, b.sizez); std::swap(a.dataType, b.dataType); std::swap(a.elementByteSize, b.elementByteSize); std::swap(a.additionalBufferNum, b.additionalBufferNum); // It will probably work just to swap pointers to the starts of respective arrays std::swap(a.buffers, b.buffers); std::swap(a.buffer_copys, b.buffer_coppys); std::swap(a.consistencyMutexes, b.consistencyMutexes); } /**Copy assignment * */ template DenFrame2DReader& DenFrame2DReader::operator=(DenFrame2DReader b) { swap(*this, b); return *this; } template DenFrame2DReader::DenFrame2DReader(DenFrame2DReader&& b) { this->denFile = b.denFile; this->offset = b.offset; this->sizex = b.sizex; this->sizey = b.sizey; this->sizez = b.sizez; this->dataType = b.dataType; this->elementByteSize = b.elementByteSize; this->additionalBufferNum = b.additionalBufferNum; this->consistencyMutexes = std::exchange(b.consistencyMutexes, nullptr); this->buffers = std::exchange(b.buffers, nullptr); this->buffer_copys = std::exchange(b.buffer_copys, nullptr); } // Move constructor to steal resources from another object template DenFrame2DReader& DenFrame2DReader::operator=(DenFrame2DReader&& b) { if(&b != this) // To elegantly solve situation when assigning to itself { this->denFile = b.denFile; this->offset = b.offset; this->sizex = b.sizex; this->sizey = b.sizey; this->sizez = b.sizez; this->dataType = b.dataType; this->elementByteSize = b.elementByteSize; this->additionalBufferNum = b.additionalBufferNum; if(consistencyMutexes != nullptr) { delete[] consistencyMutexes; } consistencyMutexes = nullptr; if(buffers != nullptr) { for(uint32_t i = 0; i != 1 + additionalBufferNum; i++) { if(buffers[i] != nullptr) { delete buffers[i]; } buffers[i] = nullptr; } delete[] buffers; } buffers = nullptr; if(buffer_copys != nullptr) { for(uint32_t i = 0; i != 1 + additionalBufferNum; i++) { if(buffer_copys[i] != nullptr) { delete buffer_copys[i]; } buffer_copys[i] = nullptr; } delete[] buffer_copys; } buffer_copys = nullptr; this->consistencyMutexes = std::exchange(b.consistencyMutexes, nullptr); this->buffers = std::exchange(b.buffers, nullptr); this->buffer_copys = std::exchange(b.buffer_copys, nullptr); } return *this; } // Move assignment template std::string DenFrame2DReader::getFileName() const { return this->denFile; } template uint32_t DenFrame2DReader::dimx() const { return sizex; } template uint32_t DenFrame2DReader::dimy() const { return sizey; } template uint32_t DenFrame2DReader::dimz() const { return sizez; } template std::shared_ptr> DenFrame2DReader::readFrame(unsigned int sliceNum) { std::shared_ptr> ps = readBufferedFrame(sliceNum); return ps; } template std::shared_ptr> DenFrame2DReader::readBufferedFrame(unsigned int sliceNum) { std::unique_lock l; bool locked = false; uint32_t mutexnum = 0; for(uint32_t i = 0; i != 1 + additionalBufferNum; i++) { l = std::unique_lock(consistencyMutexes[i], std::try_to_lock); if(l.owns_lock()) { locked = true; mutexnum = i; break; } } if(!locked) { l = std::unique_lock(consistencyMutexes[0]); mutexnum = 0; } // Mutex will be released as this goes out of scope. // To protect calling this method from another thread using the same block of memory uint8_t* buffer = buffers[mutexnum]; T* buffer_copy = buffer_copys[mutexnum]; uint32_t elmCount = sizex * sizey; uint64_t position = this->offset + uint64_t(sliceNum) * elementByteSize * elmCount; io::readBytesFrom(this->denFile, position, buffer, elementByteSize * elmCount); for(uint32_t a = 0; a != elmCount; a++) { buffer_copy[a] = util::getNextElement(&buffer[a * elementByteSize], dataType); } std::shared_ptr> ps = std::make_shared>(buffer_copy, sizex, sizey); return ps; } template void DenFrame2DReader::readFrameIntoBuffer(unsigned int frameID, T* outside_buffer) { std::unique_lock l; bool locked = false; uint32_t mutexnum = 0; for(uint32_t i = 0; i != 1 + additionalBufferNum; i++) { l = std::unique_lock(consistencyMutexes[i], std::try_to_lock); if(l.owns_lock()) { locked = true; mutexnum = i; break; } } if(!locked) { l = std::unique_lock(consistencyMutexes[0]); mutexnum = 0; } // Mutex will be released as this goes out of scope. // To protect calling this method from another thread using the same block of memory uint8_t* buffer = buffers[mutexnum]; uint32_t elmCount = sizex * sizey; uint64_t position = this->offset + uint64_t(frameID) * elementByteSize * elmCount; io::readBytesFrom(this->denFile, position, buffer, elementByteSize * elmCount); for(uint32_t a = 0; a != elmCount; a++) { outside_buffer[a] = util::getNextElement(&buffer[a * elementByteSize], dataType); } } } // namespace io } // namespace CTL