USGS

Isis 3.0 Object Programmers' Reference

Home

CubeIoHandler.cpp
Go to the documentation of this file.
1 
24 #include "IsisDebug.h"
25 #include "CubeIoHandler.h"
26 
27 #include <algorithm>
28 #include <cmath>
29 #include <iomanip>
30 
31 #include <QDebug>
32 #include <QFile>
33 #include <QList>
34 #include <QListIterator>
35 #include <QMapIterator>
36 #include <QMutex>
37 #include <QPair>
38 #include <QRect>
39 #include <QTime>
40 
41 #include "Area3D.h"
42 #include "Brick.h"
43 #include "CubeCachingAlgorithm.h"
44 #include "Displacement.h"
45 #include "Distance.h"
46 #include "Endian.h"
47 #include "EndianSwapper.h"
48 #include "IException.h"
49 #include "IString.h"
50 #include "PixelType.h"
51 #include "Preference.h"
52 #include "Pvl.h"
53 #include "PvlGroup.h"
54 #include "PvlObject.h"
55 #include "RawCubeChunk.h"
57 #include "SpecialPixel.h"
58 #include "Statistics.h"
59 
60 using namespace std;
61 
62 namespace Isis {
79  CubeIoHandler::CubeIoHandler(QFile * dataFile,
80  const QList<int> *virtualBandList, const Pvl &label, bool alreadyOnDisk) {
81  m_byteSwapper = NULL;
82  m_cachingAlgorithms = NULL;
83  m_dataIsOnDiskMap = NULL;
84  m_rawData = NULL;
85  m_virtualBands = NULL;
86  m_nullChunkData = NULL;
87  m_lastProcessByLineChunks = NULL;
88  m_writeCache = NULL;
89  m_ioThreadPool = NULL;
90  m_writeThreadMutex = NULL;
91 
92  try {
93  if (!dataFile) {
94  IString msg = "Cannot create a CubeIoHandler with a NULL data file";
95  throw IException(IException::Programmer, msg, _FILEINFO_);
96  }
97 
98  m_cachingAlgorithms = new QList<CubeCachingAlgorithm *>;
99 
100  PvlGroup &performancePrefs =
101  Preference::Preferences().findGroup("Performance");
102  IString cubeWritePerfOpt = performancePrefs["CubeWriteThread"][0];
103  m_useOptimizedCubeWrite = (cubeWritePerfOpt.DownCase() == "optimized");
104  if ((m_useOptimizedCubeWrite && !alreadyOnDisk) ||
105  cubeWritePerfOpt.DownCase() == "always") {
106  m_ioThreadPool = new QThreadPool;
107  m_ioThreadPool->setMaxThreadCount(1);
108  }
109 
110  m_consecutiveOverflowCount = 0;
111  m_lastOperationWasWrite = false;
112  m_rawData = new QMap<int, RawCubeChunk *>;
113  m_writeCache = new QPair< QMutex *, QList<Buffer *> >;
114  m_writeCache->first = new QMutex;
115  m_writeThreadMutex = new QMutex;
116 
117  m_idealFlushSize = 32;
118 
119  m_cachingAlgorithms->append(new RegionalCachingAlgorithm);
120 
121  m_dataFile = dataFile;
122 
123  const PvlObject &core = label.findObject("IsisCube").findObject("Core");
124  const PvlGroup &pixelGroup = core.findGroup("Pixels");
125 
126  QString byteOrderStr = pixelGroup.findKeyword("ByteOrder")[0];
127  m_byteSwapper = new EndianSwapper(
128  byteOrderStr.toUpper());
129  m_base = pixelGroup.findKeyword("Base");
130  m_multiplier = pixelGroup.findKeyword("Multiplier");
131  m_pixelType = PixelTypeEnumeration(pixelGroup.findKeyword("Type"));
132 
133  // If the byte swapper isn't going to do anything, then get rid of it
134  // because it's quicker to check for a NULL byte swapper member than to
135  // call a swap that won't do anything.
136  if(!m_byteSwapper->willSwap()) {
137  delete m_byteSwapper;
138  m_byteSwapper = NULL;
139  }
140 
141  const PvlGroup &dimensions = core.findGroup("Dimensions");
142  m_numSamples = dimensions.findKeyword("Samples");
143  m_numLines = dimensions.findKeyword("Lines");
144  m_numBands = dimensions.findKeyword("Bands");
145 
146  m_startByte = (int)core.findKeyword("StartByte") - 1;
147 
148  m_samplesInChunk = -1;
149  m_linesInChunk = -1;
150  m_bandsInChunk = -1;
151 
152  if(!alreadyOnDisk) {
153  m_dataIsOnDiskMap = new QMap<int, bool>;
154  }
155 
156  setVirtualBands(virtualBandList);
157  }
158  catch(IException &e) {
159  IString msg = "Constructing CubeIoHandler failed";
160  throw IException(e, IException::Programmer, msg, _FILEINFO_);
161  }
162  catch(...) {
163  IString msg = "Constructing CubeIoHandler failed";
164  throw IException(IException::Programmer, msg, _FILEINFO_);
165  }
166  }
167 
168 
173  CubeIoHandler::~CubeIoHandler() {
174  ASSERT( m_rawData ? m_rawData->size() == 0 : 1 );
175 
176  if (m_ioThreadPool)
177  m_ioThreadPool->waitForDone();
178 
179  delete m_ioThreadPool;
180  m_ioThreadPool = NULL;
181 
182  delete m_dataIsOnDiskMap;
183  m_dataIsOnDiskMap = NULL;
184 
185  if (m_cachingAlgorithms) {
186  QListIterator<CubeCachingAlgorithm *> it(*m_cachingAlgorithms);
187  while (it.hasNext()) {
188  delete it.next();
189  }
190  delete m_cachingAlgorithms;
191  m_cachingAlgorithms = NULL;
192  }
193 
194  if (m_rawData) {
195  QMapIterator<int, RawCubeChunk *> it(*m_rawData);
196  while (it.hasNext()) {
197  // Unwritten data here means it cannot be written :(
198  ASSERT(0);
199  it.next();
200 
201  if(it.value())
202  delete it.value();
203  }
204  delete m_rawData;
205  m_rawData = NULL;
206  }
207 
208  if (m_writeCache) {
209  delete m_writeCache->first;
210  m_writeCache->first = NULL;
211 
212  for (int i = 0; i < m_writeCache->second.size(); i++) {
213  delete m_writeCache->second[i];
214  }
215  m_writeCache->second.clear();
216 
217  delete m_writeCache;
218  m_writeCache = NULL;
219  }
220 
221  delete m_byteSwapper;
222  m_byteSwapper = NULL;
223 
224  delete m_virtualBands;
225  m_virtualBands = NULL;
226 
227  delete m_nullChunkData;
228  m_nullChunkData = NULL;
229 
230  delete m_lastProcessByLineChunks;
231  m_lastProcessByLineChunks = NULL;
232 
233  delete m_writeThreadMutex;
234  m_writeThreadMutex = NULL;
235  }
236 
237 
246  void CubeIoHandler::read(Buffer &bufferToFill) const {
247  // We need to record the current chunk count size so we can use
248  // it to evaluate if the cache should be minimized
249  int lastChunkCount = m_rawData->size();
250 
251  if (m_lastOperationWasWrite) {
252  // Do the remaining writes
253  flushWriteCache(true);
254 
255  m_lastOperationWasWrite = false;
256 
257  // Stop backgrounding writes now, we don't want to keep incurring this
258  // penalty.
259  if (m_useOptimizedCubeWrite) {
260  delete m_ioThreadPool;
261  m_ioThreadPool = NULL;
262  }
263  }
264 
265  QMutexLocker lock(m_writeThreadMutex);
266 
267  // NON-THREADED CUBE READ
268  QList<RawCubeChunk *> cubeChunks;
269  QList<int > chunkBands;
270 
271  int bufferSampleCount = bufferToFill.SampleDimension();
272  int bufferLineCount = bufferToFill.LineDimension();
273  int bufferBandCount = bufferToFill.BandDimension();
274 
275  // our chunk dimensions are same as buffer shape dimensions
276  if (bufferSampleCount == m_samplesInChunk &&
277  bufferLineCount == m_linesInChunk &&
278  bufferBandCount == m_bandsInChunk) {
279  int bufferStartSample = bufferToFill.Sample();
280  int bufferStartLine = bufferToFill.Line();
281  int bufferStartBand = bufferToFill.Band();
282 
283  int bufferEndSample = bufferStartSample + bufferSampleCount - 1;
284  int bufferEndLine = bufferStartLine + bufferLineCount - 1;
285  int bufferEndBand = bufferStartBand + bufferBandCount - 1;
286 
287  // make sure we access the correct band
288  int startBand = bufferStartBand - 1;
289  if (m_virtualBands)
290  startBand = m_virtualBands->at(bufferStartBand - 1);
291 
292  int expectedChunkIndex =
293  ((bufferStartSample - 1) / getSampleCountInChunk()) +
294  ((bufferStartLine - 1) / getLineCountInChunk()) *
295  getChunkCountInSampleDimension() +
296  ((startBand - 1) / getBandCountInChunk()) *
297  getChunkCountInSampleDimension() *
298  getChunkCountInLineDimension();
299 
300  int chunkStartSample, chunkStartLine, chunkStartBand,
301  chunkEndSample, chunkEndLine, chunkEndBand;
302 
303  getChunkPlacement(expectedChunkIndex,
304  chunkStartSample, chunkStartLine, chunkStartBand,
305  chunkEndSample, chunkEndLine, chunkEndBand);
306 
307  if (chunkStartSample == bufferStartSample &&
308  chunkStartLine == bufferStartLine &&
309  chunkStartBand == bufferStartBand &&
310  chunkEndSample == bufferEndSample &&
311  chunkEndLine == bufferEndLine &&
312  chunkEndBand == bufferEndBand) {
313  cubeChunks.append(getChunk(expectedChunkIndex, true));
314  chunkBands.append(cubeChunks.last()->getStartBand());
315  }
316  }
317 
318  if (cubeChunks.empty()) {
319  // We can't guarantee our cube chunks will encompass the buffer
320  // if the buffer goes beyond the cube bounds.
321  for(int i = 0; i < bufferToFill.size(); i++) {
322  bufferToFill[i] = Null;
323  }
324 
326  chunkInfo = findCubeChunks(
327  bufferToFill.Sample(), bufferToFill.SampleDimension(),
328  bufferToFill.Line(), bufferToFill.LineDimension(),
329  bufferToFill.Band(), bufferToFill.BandDimension());
330  cubeChunks = chunkInfo.first;
331  chunkBands = chunkInfo.second;
332  }
333 
334  for (int i = 0; i < cubeChunks.size(); i++) {
335  writeIntoDouble(*cubeChunks[i], bufferToFill, chunkBands[i]);
336  }
337 
338  // Minimize the cache if it changed in size
339  if (lastChunkCount != m_rawData->size()) {
340  minimizeCache(cubeChunks, bufferToFill);
341  }
342  }
343 
344 
354  void CubeIoHandler::write(const Buffer &bufferToWrite) {
355  m_lastOperationWasWrite = true;
356 
357  if (m_ioThreadPool) {
358  // THREADED CUBE WRITE
359  Buffer * copy = new Buffer(bufferToWrite);
360  {
361  QMutexLocker locker(m_writeCache->first);
362  m_writeCache->second.append(copy);
363  }
364 
365  flushWriteCache();
366  }
367  else {
368  QMutexLocker lock(m_writeThreadMutex);
369  // NON-THREADED CUBE WRITE
370  synchronousWrite(bufferToWrite);
371  }
372  }
373 
374 
384  void CubeIoHandler::addCachingAlgorithm(CubeCachingAlgorithm *algorithm) {
385  m_cachingAlgorithms->prepend(algorithm);
386  }
387 
388 
399  void CubeIoHandler::clearCache(bool blockForWriteCache) const {
400  if (blockForWriteCache) {
401  // Start the rest of the writes
402  flushWriteCache(true);
403  }
404 
405  // If this map is allocated, then this is a brand new cube and we need to
406  // make sure it's filled with data or NULLs.
407  if(m_dataIsOnDiskMap) {
408  writeNullDataToDisk();
409  }
410 
411  // This should be allocated. This is a list of the cached cube data.
412  // Write it all to disk.
413  if (m_rawData) {
414  QMapIterator<int, RawCubeChunk *> it(*m_rawData);
415  while (it.hasNext()) {
416  it.next();
417 
418  if(it.value()) {
419  if(it.value()->isDirty()) {
420  (const_cast<CubeIoHandler *>(this))->writeRaw(*it.value());
421  }
422 
423  delete it.value();
424  }
425  }
426 
427  m_rawData->clear();
428  }
429 
430  if(m_lastProcessByLineChunks) {
431  delete m_lastProcessByLineChunks;
432  m_lastProcessByLineChunks = NULL;
433  }
434  }
435 
436 
441  BigInt CubeIoHandler::getDataSize() const {
442  return (BigInt)getChunkCountInSampleDimension() *
443  (BigInt)getChunkCountInLineDimension() *
444  (BigInt)getChunkCountInBandDimension() *
445  (BigInt)getBytesPerChunk();
446  }
447 
448 
458  void CubeIoHandler::setVirtualBands(const QList<int> *virtualBandList) {
459  if(m_virtualBands) {
460  delete m_virtualBands;
461  m_virtualBands = NULL;
462  }
463 
464  if(virtualBandList && !virtualBandList->empty())
465  m_virtualBands = new QList<int>(*virtualBandList);
466  }
467 
468 
478  void CubeIoHandler::updateLabels(Pvl &labels) {
479  }
480 
481 
489  QMutex *CubeIoHandler::dataFileMutex() {
490  return m_writeThreadMutex;
491  }
492 
496  int CubeIoHandler::bandCount() const {
497  return m_numBands;
498  }
499 
500 
504  int CubeIoHandler::getBandCountInChunk() const {
505  return m_bandsInChunk;
506  }
507 
508 
516  BigInt CubeIoHandler::getBytesPerChunk() const {
517  return m_samplesInChunk * m_linesInChunk * m_bandsInChunk *
518  SizeOf(m_pixelType);
519  }
520 
521 
526  int CubeIoHandler::getChunkCountInBandDimension() const {
527  return (int)ceil((double)m_numBands / (double)m_bandsInChunk);
528  }
529 
530 
535  int CubeIoHandler::getChunkCountInLineDimension() const {
536  return (int)ceil((double)m_numLines / (double)m_linesInChunk);
537  }
538 
539 
544  int CubeIoHandler::getChunkCountInSampleDimension() const {
545  return (int)ceil((double)m_numSamples / (double)m_samplesInChunk);
546  }
547 
548 
560  int CubeIoHandler::getChunkIndex(const RawCubeChunk &chunk) const {
561 // ASSERT(chunk.getStartSample() <= sampleCount());
562 // ASSERT(chunk.getStartLine() <= lineCount());
563 // ASSERT(chunk.getStartBand() <= bandCount());
564 
565  int sampleIndex = (chunk.getStartSample() - 1) / getSampleCountInChunk();
566  int lineIndex = (chunk.getStartLine() - 1) / getLineCountInChunk();
567  int bandIndex = (chunk.getStartBand() - 1) / getBandCountInChunk();
568 
569  int indexInBand =
570  sampleIndex + lineIndex * getChunkCountInSampleDimension();
571  int indexOffsetToBand = bandIndex * getChunkCountInSampleDimension() *
572  getChunkCountInLineDimension();
573 
574  return indexOffsetToBand + indexInBand;
575  }
576 
577 
581  BigInt CubeIoHandler::getDataStartByte() const {
582  return m_startByte;
583  }
584 
585 
592  QFile * CubeIoHandler::getDataFile() {
593  return m_dataFile;
594  }
595 
596 
601  int CubeIoHandler::lineCount() const {
602  return m_numLines;
603  }
604 
605 
609  int CubeIoHandler::getLineCountInChunk() const {
610  return m_linesInChunk;
611  }
612 
613 
617  PixelType CubeIoHandler::pixelType() const {
618  return m_pixelType;
619  }
620 
621 
626  int CubeIoHandler::sampleCount() const {
627  return m_numSamples;
628  }
629 
630 
634  int CubeIoHandler::getSampleCountInChunk() const {
635  return m_samplesInChunk;
636  }
637 
638 
651  void CubeIoHandler::setChunkSizes(
652  int numSamples, int numLines, int numBands) {
653  bool success = false;
654  IString msg;
655 
656  if(m_samplesInChunk != -1 || m_linesInChunk != -1 || m_bandsInChunk != -1) {
657  IString msg = "You cannot change the chunk sizes once set";
658  }
659  else if(numSamples < 1) {
660  msg = "Negative and zero chunk sizes are not supported, samples per chunk"
661  " cannot be [" + IString(numSamples) + "]";
662  }
663  else if(numLines < 1) {
664  msg = "Negative and zero chunk sizes are not supported, lines per chunk "
665  "cannot be [" + IString(numLines) + "]";
666  }
667  else if(numBands < 1) {
668  msg = "Negative and zero chunk sizes are not supported, lines per chunk "
669  "cannot be [" + IString(numBands) + "]";
670  }
671  else {
672  success = true;
673  }
674 
675  if(success) {
676  m_samplesInChunk = numSamples;
677  m_linesInChunk = numLines;
678  m_bandsInChunk = numBands;
679 
680  if(m_dataIsOnDiskMap) {
681  m_dataFile->resize(getDataStartByte() + getDataSize());
682  }
683  else if(m_dataFile->size() < getDataStartByte() + getDataSize()) {
684  success = false;
685  msg = "File size [" + IString((BigInt)m_dataFile->size()) +
686  " bytes] not big enough to hold data [" +
687  IString(getDataStartByte() + getDataSize()) + " bytes] where the "
688  "offset to the cube data is [" + IString(getDataStartByte()) +
689  " bytes]";
690  }
691  }
692  else {
693  throw IException(IException::Programmer, msg, _FILEINFO_);
694  }
695  }
696 
697 
704  void CubeIoHandler::blockUntilThreadPoolEmpty() const {
705  if (m_ioThreadPool) {
706  QMutexLocker lock(m_writeThreadMutex);
707  }
708  }
709 
710 
718  bool CubeIoHandler::bufferLessThan(Buffer * const &lhs, Buffer * const &rhs) {
719  bool lessThan = false;
720 
721  // If there is any overlap then we need to return false due to it being
722  // ambiguous.
723  Area3D lhsArea(
724  Displacement(lhs->Sample(), Displacement::Pixels),
725  Displacement(lhs->Line(), Displacement::Pixels),
726  Displacement(lhs->Band(), Displacement::Pixels),
727  Distance(lhs->SampleDimension() - 1, Distance::Pixels),
728  Distance(lhs->LineDimension() - 1, Distance::Pixels),
729  Distance(lhs->BandDimension() - 1, Distance::Pixels));
730  Area3D rhsArea(
731  Displacement(rhs->Sample(), Displacement::Pixels),
732  Displacement(rhs->Line(), Displacement::Pixels),
733  Displacement(rhs->Band(), Displacement::Pixels),
734  Distance(rhs->SampleDimension() - 1, Distance::Pixels),
735  Distance(rhs->LineDimension() - 1, Distance::Pixels),
736  Distance(rhs->BandDimension() - 1, Distance::Pixels));
737 
738  if (!lhsArea.intersect(rhsArea).isValid()) {
739  if (lhs->Band() != rhs->Band()) {
740  lessThan = lhs->Band() < rhs->Band();
741  }
742  else if (lhs->Line() != rhs->Line()) {
743  lessThan = lhs->Line() < rhs->Line();
744  }
745  else if (lhs->Sample() != rhs->Sample()) {
746  lessThan = lhs->Sample() < rhs->Sample();
747  }
748  }
749 
750  return lessThan;
751  }
752 
753 
767  QPair< QList<RawCubeChunk *>, QList<int> > CubeIoHandler::findCubeChunks(int startSample,
768  int numSamples, int startLine, int numLines, int startBand,
769  int numBands) const {
770  QList<RawCubeChunk *> results;
771  QList<int> resultBands;
772 /************************************************************************CHANGED THIS!!!!!!!!******/
773  int lastBand = startBand + numBands - 1;
774 // int lastBand = min(startBand + numBands - 1,
775 // bandCount());
776 /************************************************************************CHANGED THIS!!!!!!!!******/
777 
778  QRect areaInBand(
779  QPoint(max(startSample, 1),
780  max(startLine, 1)),
781  QPoint(min(startSample + numSamples - 1,
782  sampleCount()),
783  min(startLine + numLines - 1,
784  lineCount())));
785 
786  // We are considering only 1 band at a time.. we can't use m_bandsInChunk
787  // because of virtual bands, but every extra loop should not need extra
788  // IO.
789  for(int band = startBand; band <= lastBand; band ++) {
790  // This is the user-requested area in this band
791  QRect areaLeftInBand(areaInBand);
792 
793  int actualBand = band;
794 
795  if(m_virtualBands) {
796  if (band < 1 || band > m_virtualBands->size())
797  actualBand = 0;
798  else
799  actualBand = (m_virtualBands->at(band - 1) - 1) / m_bandsInChunk + 1;
800  }
801  // We will be consuming areaLeftInBand until we've got all of the area
802  // requested.
803  while(!areaLeftInBand.isEmpty()) {
851  int areaStartLine = areaLeftInBand.top();
852  int areaStartSample = areaLeftInBand.left();
853 
854  int initialChunkXPos = (areaStartSample - 1) / m_samplesInChunk;
855  int initialChunkYPos = (areaStartLine - 1) / m_linesInChunk;
856  int initialChunkZPos = (actualBand - 1) / m_bandsInChunk;
857  int initialChunkBand = initialChunkZPos * m_bandsInChunk + 1;
858 
859 
860  QRect chunkRect(initialChunkXPos * m_samplesInChunk + 1,
861  initialChunkYPos * m_linesInChunk + 1,
862  m_samplesInChunk, m_linesInChunk);
863 
864  // The chunk rectangle must intersect the remaining area that is in the
865  // current band, and the chunk's initial band must be between 1 and the
866  // number of physical bands in the cube (inclusive).
867  while(chunkRect.intersects(areaLeftInBand) &&
868  (initialChunkBand >= 1 && initialChunkBand <= bandCount())) {
869  int chunkXPos = (chunkRect.left() - 1) / m_samplesInChunk;
870  int chunkYPos = (chunkRect.top() - 1) / m_linesInChunk;
871  int chunkZPos = initialChunkZPos;
872 
873  // We now have an X,Y,Z position for the chunk. What's its index?
874  int chunkIndex = chunkXPos +
875  (chunkYPos * getChunkCountInSampleDimension()) +
876  (chunkZPos * getChunkCountInSampleDimension() *
877  getChunkCountInLineDimension());
878 
879  RawCubeChunk * newChunk = getChunk(chunkIndex, true);
880 
881  results.append(newChunk);
882  resultBands.append(band);
883 
884  chunkRect.moveLeft(chunkRect.right() + 1);
885  }
886 
887  areaLeftInBand.setTop(chunkRect.bottom() + 1);
888  }
889  }
890 
891  return QPair< QList<RawCubeChunk *>, QList<int> >(results, resultBands);
892  }
893 
894 
912  void CubeIoHandler::findIntersection(
913  const RawCubeChunk &cube1, const Buffer &cube2,
914  int &startX, int &startY, int &startZ,
915  int &endX, int &endY, int &endZ) const {
916  // So we have 2 3D "cubes" (not Cube cubes but 3d areas) we need to
917  // intersect in order to figure out what chunk data goes into the output
918  // buffer.
919 
920  // To find the band range we actually have to map all of the bands from
921  // virtual bands to physical bands
922  int startVBand = cube2.Band();
923  int endVBand = startVBand + cube2.BandDimension() - 1;
924 
925  int startPhysicalBand = 0;
926  int endPhysicalBand = 0;
927 
928  bool startVBandFound = false;
929  for(int virtualBand = startVBand; virtualBand <= endVBand; virtualBand ++) {
930  int physicalBand = virtualBand;
931 
932  bool bandExists = true;
933  if(m_virtualBands) {
934  if (virtualBand < 1 || virtualBand > m_virtualBands->size())
935  bandExists = false;
936  else {
937  physicalBand = m_virtualBands->at(virtualBand - 1);
938  }
939  }
940 
941  if (bandExists) {
942  if(!startVBandFound) {
943  startPhysicalBand = physicalBand;
944  endPhysicalBand = physicalBand;
945  startVBandFound = true;
946  }
947  else {
948  if(physicalBand < startPhysicalBand)
949  startPhysicalBand = physicalBand;
950 
951  if(physicalBand > endPhysicalBand)
952  endPhysicalBand = physicalBand;
953  }
954  }
955  }
956 
957  startX = max(cube1.getStartSample(), cube2.Sample());
958  startY = max(cube1.getStartLine(), cube2.Line());
959  startZ = max(cube1.getStartBand(), startPhysicalBand);
960  endX = min(cube1.getStartSample() + cube1.sampleCount() - 1,
961  cube2.Sample() + cube2.SampleDimension() - 1);
962  endY = min(cube1.getStartLine() + cube1.lineCount() - 1,
963  cube2.Line() + cube2.LineDimension() - 1);
964  endZ = min(cube1.getStartBand() + cube1.bandCount() - 1,
965  endPhysicalBand);
966  }
967 
968 
979  void CubeIoHandler::flushWriteCache(bool force) const {
980  if (m_ioThreadPool) {
981  bool shouldFlush = m_writeCache->second.size() >= m_idealFlushSize ||
982  force;
983  bool cacheOverflowing =
984  (m_writeCache->second.size() > m_idealFlushSize * 10);
985  bool shouldAndCanFlush = false;
986  bool forceStart = force;
987 
988  if (shouldFlush) {
989  shouldAndCanFlush = m_writeThreadMutex->tryLock();
990  if (shouldAndCanFlush) {
991  m_writeThreadMutex->unlock();
992  }
993  }
994 
995  if (cacheOverflowing && !shouldAndCanFlush) {
996  forceStart = true;
997  m_consecutiveOverflowCount++;
998  }
999 
1000  if (forceStart) {
1001  blockUntilThreadPoolEmpty();
1002 
1003  if (m_writeCache->second.size() != 0) {
1004  m_idealFlushSize = m_writeCache->second.size();
1005  shouldFlush = true;
1006  shouldAndCanFlush = true;
1007  }
1008  }
1009  else if (!cacheOverflowing && shouldAndCanFlush) {
1010  m_consecutiveOverflowCount = 0;
1011  }
1012 
1013  if (cacheOverflowing && m_useOptimizedCubeWrite) {
1014  blockUntilThreadPoolEmpty();
1015 
1016  // If the process is very I/O bound, then write caching isn't helping
1017  // anything. In fact, it hurts, so turn it off.
1018  if (m_consecutiveOverflowCount > 10) {
1019  delete m_ioThreadPool;
1020  m_ioThreadPool = NULL;
1021  }
1022 
1023  // Write it all synchronously.
1024  foreach (Buffer *bufferToWrite, m_writeCache->second) {
1025  const_cast<CubeIoHandler *>(this)->synchronousWrite(*bufferToWrite);
1026  delete bufferToWrite;
1027  }
1028 
1029  m_writeCache->second.clear();
1030  }
1031 
1032  if (shouldAndCanFlush && m_ioThreadPool) {
1033  QMutexLocker locker(m_writeCache->first);
1034  QRunnable *writer = new BufferToChunkWriter(
1035  const_cast<CubeIoHandler *>(this), m_writeCache->second);
1036 
1037  m_ioThreadPool->start(writer);
1038 
1039  m_writeCache->second.clear();
1040  m_lastOperationWasWrite = true;
1041  }
1042 
1043  if (force) {
1044  blockUntilThreadPoolEmpty();
1045  }
1046  }
1047  }
1048 
1049 
1056  void CubeIoHandler::freeChunk(RawCubeChunk *chunkToFree) const {
1057  if(chunkToFree && m_rawData) {
1058  int chunkIndex = getChunkIndex(*chunkToFree);
1059 
1060  m_rawData->erase(m_rawData->find(chunkIndex));
1061 
1062  if(chunkToFree->isDirty())
1063  (const_cast<CubeIoHandler *>(this))->writeRaw(*chunkToFree);
1064 
1065  delete chunkToFree;
1066 
1067  if(m_lastProcessByLineChunks) {
1068  delete m_lastProcessByLineChunks;
1069  m_lastProcessByLineChunks = NULL;
1070  }
1071  }
1072  }
1073 
1074 
1084  RawCubeChunk *CubeIoHandler::getChunk(int chunkIndex,
1085  bool allocateIfNecessary) const {
1086  RawCubeChunk *chunk = NULL;
1087 
1088  if(m_rawData) {
1089  chunk = m_rawData->value(chunkIndex);
1090  }
1091 
1092  if(allocateIfNecessary && !chunk) {
1093  if(m_dataIsOnDiskMap && !(*m_dataIsOnDiskMap)[chunkIndex]) {
1094  chunk = getNullChunk(chunkIndex);
1095  (*m_dataIsOnDiskMap)[chunkIndex] = true;
1096  }
1097  else {
1098  int startSample;
1099  int startLine;
1100  int startBand;
1101  int endSample;
1102  int endLine;
1103  int endBand;
1104  getChunkPlacement(chunkIndex, startSample, startLine, startBand,
1105  endSample, endLine, endBand);
1106  chunk = new RawCubeChunk(startSample, startLine, startBand,
1107  endSample, endLine, endBand,
1108  getBytesPerChunk());
1109 
1110  (const_cast<CubeIoHandler *>(this))->readRaw(*chunk);
1111  chunk->setDirty(false);
1112  }
1113 
1114  (*m_rawData)[chunkIndex] = chunk;
1115  }
1116 
1117  return chunk;
1118  }
1119 
1120 
1125  int CubeIoHandler::getChunkCount() const {
1126  return getChunkCountInSampleDimension() *
1127  getChunkCountInLineDimension() *
1128  getChunkCountInBandDimension();
1129  }
1130 
1131 
1143  void CubeIoHandler::getChunkPlacement(int chunkIndex,
1144  int &startSample, int &startLine, int &startBand,
1145  int &endSample, int &endLine, int &endBand) const {
1146  int chunkSampleIndex = chunkIndex % getChunkCountInSampleDimension();
1147 
1148  chunkIndex =
1149  (chunkIndex - chunkSampleIndex) / getChunkCountInSampleDimension();
1150 
1151  int chunkLineIndex = chunkIndex % getChunkCountInLineDimension();
1152  chunkIndex = (chunkIndex - chunkLineIndex) / getChunkCountInLineDimension();
1153 
1154  int chunkBandIndex = chunkIndex;
1155 
1156  startSample = chunkSampleIndex * getSampleCountInChunk() + 1;
1157  endSample = startSample + getSampleCountInChunk() - 1;
1158  startLine = chunkLineIndex * getLineCountInChunk() + 1;
1159  endLine = startLine + getLineCountInChunk() - 1;
1160  startBand = chunkBandIndex * getBandCountInChunk() + 1;
1161  endBand = startBand + getBandCountInChunk() - 1;
1162  }
1163 
1164 
1175  RawCubeChunk *CubeIoHandler::getNullChunk(int chunkIndex) const {
1176  // Shouldn't ask for null chunks when the area has already been allocated
1177 // ASSERT(getChunk(chunkIndex) == NULL);
1178 
1179  int startSample = 0;
1180  int startLine = 0;
1181  int startBand = 0;
1182 
1183  int endSample = 0;
1184  int endLine = 0;
1185  int endBand = 0;
1186 
1187  getChunkPlacement(chunkIndex, startSample, startLine, startBand,
1188  endSample, endLine, endBand);
1189 
1190  RawCubeChunk *result = new RawCubeChunk(startSample, startLine, startBand,
1191  endSample, endLine, endBand, getBytesPerChunk());
1192 
1193  if(!m_nullChunkData) {
1194  // the pixel type doesn't really matter, so pick something small
1195  Brick nullBuffer(result->sampleCount(),
1196  result->lineCount(),
1197  result->bandCount(),
1198  UnsignedByte);
1199 
1200  nullBuffer.SetBasePosition(result->getStartSample(),
1201  result->getStartLine(),
1202  result->getStartBand());
1203  for(int i = 0; i < nullBuffer.size(); i++) {
1204  nullBuffer[i] = Null;
1205  }
1206 
1207  writeIntoRaw(nullBuffer, *result, result->getStartBand());
1208  m_nullChunkData = new QByteArray(result->getRawData());
1209  }
1210  else {
1211  result->setRawData(*m_nullChunkData);
1212  }
1213 
1214  result->setDirty(true);
1215  return result;
1216  }
1217 
1218 
1228  void CubeIoHandler::minimizeCache(const QList<RawCubeChunk *> &justUsed,
1229  const Buffer &justRequested) const {
1230  // Since we have a lock on the cache, no newly created threads can utilize
1231  // or access any cache data until we're done.
1232  if (m_rawData->size() * getBytesPerChunk() > 1 * 1024 * 1024 ||
1233  m_cachingAlgorithms->size() > 1) {
1234  bool algorithmAccepted = false;
1235 
1236  int algorithmIndex = 0;
1237  while(!algorithmAccepted &&
1238  algorithmIndex < m_cachingAlgorithms->size()) {
1239  CubeCachingAlgorithm *algorithm = (*m_cachingAlgorithms)[algorithmIndex];
1240 
1242  algorithm->recommendChunksToFree(m_rawData->values(), justUsed,
1243  justRequested);
1244 
1245  algorithmAccepted = result.algorithmUnderstoodData();
1246 
1247  if(algorithmAccepted) {
1248  QList<RawCubeChunk *> chunksToFree = result.getChunksToFree();
1249 
1250  RawCubeChunk *chunkToFree;
1251  foreach(chunkToFree, chunksToFree) {
1252  freeChunk(chunkToFree);
1253  }
1254  }
1255 
1256  algorithmIndex ++;
1257  }
1258 
1259  // Fall back - no algorithms liked us :(
1260  if(!algorithmAccepted && m_rawData->size() > 100) {
1261  // This (minimizeCache()) is typically executed in the Runnable thread.
1262  // We don't want to wait for ourselves.
1263  clearCache(false);
1264  }
1265  }
1266  }
1267 
1268 
1277  void CubeIoHandler::synchronousWrite(const Buffer &bufferToWrite) {
1278  QList<RawCubeChunk *> cubeChunks;
1279  QList<int> cubeChunkBands;
1280 
1281  int bufferSampleCount = bufferToWrite.SampleDimension();
1282  int bufferLineCount = bufferToWrite.LineDimension();
1283  int bufferBandCount = bufferToWrite.BandDimension();
1284 
1285  // process by line optimization
1286  if(m_lastProcessByLineChunks &&
1287  m_lastProcessByLineChunks->size()) {
1288  // Not optimized yet, let's see if we can optimize
1289  if(bufferToWrite.Sample() == 1 &&
1290  bufferSampleCount == sampleCount() &&
1291  bufferLineCount == 1 &&
1292  bufferBandCount == 1) {
1293  // We look like a process by line, are we using the same chunks as
1294  // before?
1295  int bufferLine = bufferToWrite.Line();
1296  int chunkStartLine =
1297  (*m_lastProcessByLineChunks)[0]->getStartLine();
1298  int chunkLines =
1299  (*m_lastProcessByLineChunks)[0]->lineCount();
1300  int bufferBand = bufferToWrite.Band();
1301  int chunkStartBand =
1302  (*m_lastProcessByLineChunks)[0]->getStartBand();
1303  int chunkBands =
1304  (*m_lastProcessByLineChunks)[0]->bandCount();
1305 
1306  if(bufferLine >= chunkStartLine &&
1307  bufferLine <= chunkStartLine + chunkLines - 1 &&
1308  bufferBand >= chunkStartBand &&
1309  bufferBand <= chunkStartBand + chunkBands - 1) {
1310  cubeChunks = *m_lastProcessByLineChunks;
1311  for (int i = 0; i < cubeChunks.size(); i++) {
1312  cubeChunkBands.append( cubeChunks[i]->getStartBand() );
1313  }
1314  }
1315  }
1316  }
1317  // Processing by chunk size
1318  else if (bufferSampleCount == m_samplesInChunk &&
1319  bufferLineCount == m_linesInChunk &&
1320  bufferBandCount == m_bandsInChunk) {
1321  int bufferStartSample = bufferToWrite.Sample();
1322  int bufferStartLine = bufferToWrite.Line();
1323  int bufferStartBand = bufferToWrite.Band();
1324 
1325  int bufferEndSample = bufferStartSample + bufferSampleCount - 1;
1326  int bufferEndLine = bufferStartLine + bufferLineCount - 1;
1327  int bufferEndBand = bufferStartBand + bufferBandCount - 1;
1328 
1329  int expectedChunkIndex =
1330  ((bufferStartSample - 1) / getSampleCountInChunk()) +
1331  ((bufferStartLine - 1) / getLineCountInChunk()) *
1332  getChunkCountInSampleDimension() +
1333  ((bufferStartBand - 1) / getBandCountInChunk()) *
1334  getChunkCountInSampleDimension() *
1335  getChunkCountInLineDimension();
1336 
1337  int chunkStartSample, chunkStartLine, chunkStartBand,
1338  chunkEndSample, chunkEndLine, chunkEndBand;
1339 
1340  getChunkPlacement(expectedChunkIndex,
1341  chunkStartSample, chunkStartLine, chunkStartBand,
1342  chunkEndSample, chunkEndLine, chunkEndBand);
1343 
1344  if (chunkStartSample == bufferStartSample &&
1345  chunkStartLine == bufferStartLine &&
1346  chunkStartBand == bufferStartBand &&
1347  chunkEndSample == bufferEndSample &&
1348  chunkEndLine == bufferEndLine &&
1349  chunkEndBand == bufferEndBand) {
1350  cubeChunks.append(getChunk(expectedChunkIndex, true));
1351  cubeChunkBands.append(cubeChunks.last()->getStartBand());
1352  }
1353  }
1354 
1356  if(cubeChunks.empty()) {
1357  chunkInfo = findCubeChunks(
1358  bufferToWrite.Sample(), bufferSampleCount,
1359  bufferToWrite.Line(), bufferLineCount,
1360  bufferToWrite.Band(), bufferBandCount);
1361  cubeChunks = chunkInfo.first;
1362  cubeChunkBands = chunkInfo.second;
1363  }
1364 
1365  // process by line optimization
1366  if(bufferToWrite.Sample() == 1 &&
1367  bufferSampleCount == sampleCount() &&
1368  bufferLineCount == 1 &&
1369  bufferBandCount == 1) {
1370  if(!m_lastProcessByLineChunks)
1371  m_lastProcessByLineChunks =
1372  new QList<RawCubeChunk *>(cubeChunks);
1373  else
1374  *m_lastProcessByLineChunks = cubeChunks;
1375  }
1376 
1377  for(int i = 0; i < cubeChunks.size(); i++) {
1378  writeIntoRaw(bufferToWrite, *cubeChunks[i], cubeChunkBands[i]);
1379  }
1380 
1381  minimizeCache(cubeChunks, bufferToWrite);
1382  }
1383 
1384 
1391  void CubeIoHandler::writeIntoDouble(const RawCubeChunk &chunk,
1392  Buffer &output, int index) const {
1393  // The code in this method is highly optimized. Even the order of the if
1394  // statements will have a significant impact on performance if changed.
1395  // Also, there is a lot of duplicate code in both writeIntoDouble(...) and
1396  // writeIntoRaw(...). This is needed for performance gain. Any function
1397  // or method calls from within the x loop cause significant performance
1398  // decreases.
1399  int startX = 0;
1400  int startY = 0;
1401  int startZ = 0;
1402 
1403  int endX = 0;
1404  int endY = 0;
1405  int endZ = 0;
1406 
1407  findIntersection(chunk, output, startX, startY, startZ, endX, endY, endZ);
1408 
1409  int bufferBand = output.Band();
1410  int bufferBands = output.BandDimension();
1411  int chunkStartSample = chunk.getStartSample();
1412  int chunkStartLine = chunk.getStartLine();
1413  int chunkStartBand = chunk.getStartBand();
1414  int chunkLineSize = chunk.sampleCount();
1415  int chunkBandSize = chunkLineSize * chunk.lineCount();
1416  //double *buffersDoubleBuf = output.p_buf;
1417  double *buffersDoubleBuf = output.DoubleBuffer();
1418  const char *chunkBuf = chunk.getRawData().data();
1419  char *buffersRawBuf = (char *)output.RawBuffer();
1420 
1421  for(int z = startZ; z <= endZ; z++) {
1422  const int &bandIntoChunk = z - chunkStartBand;
1423  int virtualBand = z;
1424 
1425  virtualBand = index;
1426 
1427  if(virtualBand != 0 && virtualBand >= bufferBand &&
1428  virtualBand <= bufferBand + bufferBands - 1) {
1429 
1430  for(int y = startY; y <= endY; y++) {
1431  const int &lineIntoChunk = y - chunkStartLine;
1432  int bufferIndex = output.Index(startX, y, virtualBand);
1433 
1434  for(int x = startX; x <= endX; x++) {
1435  const int &sampleIntoChunk = x - chunkStartSample;
1436 
1437  const int &chunkIndex = sampleIntoChunk +
1438  (chunkLineSize * lineIntoChunk) +
1439  (chunkBandSize * bandIntoChunk);
1440 
1441  double &bufferVal = buffersDoubleBuf[bufferIndex];
1442 
1443  if(m_pixelType == Real) {
1444  float raw = ((float *)chunkBuf)[chunkIndex];
1445  if(m_byteSwapper)
1446  raw = m_byteSwapper->Float(&raw);
1447 
1448  if(raw >= VALID_MIN4) {
1449  bufferVal = (double) raw;
1450  }
1451  else {
1452  if(raw == NULL4)
1453  bufferVal = NULL8;
1454  else if(raw == LOW_INSTR_SAT4)
1455  bufferVal = LOW_INSTR_SAT8;
1456  else if(raw == LOW_REPR_SAT4)
1457  bufferVal = LOW_REPR_SAT8;
1458  else if(raw == HIGH_INSTR_SAT4)
1459  bufferVal = HIGH_INSTR_SAT8;
1460  else if(raw == HIGH_REPR_SAT4)
1461  bufferVal = HIGH_REPR_SAT8;
1462  else
1463  bufferVal = LOW_REPR_SAT8;
1464  }
1465 
1466  ((float *)buffersRawBuf)[bufferIndex] = raw;
1467  }
1468  else if(m_pixelType == SignedWord) {
1469  short raw = ((short *)chunkBuf)[chunkIndex];
1470  if(m_byteSwapper)
1471  raw = m_byteSwapper->ShortInt(&raw);
1472 
1473  if(raw >= VALID_MIN2) {
1474  bufferVal = (double) raw * m_multiplier + m_base;
1475  }
1476  else {
1477  if(raw == NULL2)
1478  bufferVal = NULL8;
1479  else if(raw == LOW_INSTR_SAT2)
1480  bufferVal = LOW_INSTR_SAT8;
1481  else if(raw == LOW_REPR_SAT2)
1482  bufferVal = LOW_REPR_SAT8;
1483  else if(raw == HIGH_INSTR_SAT2)
1484  bufferVal = HIGH_INSTR_SAT8;
1485  else if(raw == HIGH_REPR_SAT2)
1486  bufferVal = HIGH_REPR_SAT8;
1487  else
1488  bufferVal = LOW_REPR_SAT8;
1489  }
1490 
1491  ((short *)buffersRawBuf)[bufferIndex] = raw;
1492  }
1493  else if(m_pixelType == UnsignedByte) {
1494  unsigned char raw = ((unsigned char *)chunkBuf)[chunkIndex];
1495 
1496  if(raw == NULL1) {
1497  bufferVal = NULL8;
1498  }
1499  else if(raw == HIGH_REPR_SAT1) {
1500  bufferVal = HIGH_REPR_SAT8;
1501  }
1502  else {
1503  bufferVal = (double) raw * m_multiplier + m_base;
1504  }
1505 
1506  ((unsigned char *)buffersRawBuf)[bufferIndex] = raw;
1507  }
1508 
1509  bufferIndex ++;
1510  }
1511  }
1512  }
1513  }
1514  }
1515 
1516 
1523  void CubeIoHandler::writeIntoRaw(const Buffer &buffer, RawCubeChunk &output, int index)
1524  const {
1525  // The code in this method is highly optimized. Even the order of the if
1526  // statements will have a significant impact on performance if changed.
1527  // Also, there is a lot of duplicate code in both writeIntoDouble(...) and
1528  // writeIntoRaw(...). This is needed for performance gain. Any function
1529  // or method calls from within the x loop cause significant performance
1530  // decreases.
1531  int startX = 0;
1532  int startY = 0;
1533  int startZ = 0;
1534 
1535  int endX = 0;
1536  int endY = 0;
1537  int endZ = 0;
1538 
1539  output.setDirty(true);
1540  findIntersection(output, buffer, startX, startY, startZ, endX, endY, endZ);
1541 
1542  int bufferBand = buffer.Band();
1543  int bufferBands = buffer.BandDimension();
1544  int outputStartSample = output.getStartSample();
1545  int outputStartLine = output.getStartLine();
1546  int outputStartBand = output.getStartBand();
1547  int lineSize = output.sampleCount();
1548  int bandSize = lineSize * output.lineCount();
1549  double *buffersDoubleBuf = buffer.DoubleBuffer();
1550  char *chunkBuf = output.getRawData().data();
1551 
1552  for(int z = startZ; z <= endZ; z++) {
1553  const int &bandIntoChunk = z - outputStartBand;
1554  int virtualBand = index;
1555 
1556 
1557  if(m_virtualBands) {
1558  virtualBand = m_virtualBands->indexOf(virtualBand) + 1;
1559  }
1560 
1561  if(virtualBand != 0 && virtualBand >= bufferBand &&
1562  virtualBand <= bufferBand + bufferBands - 1) {
1563 
1564  for(int y = startY; y <= endY; y++) {
1565  const int &lineIntoChunk = y - outputStartLine;
1566  int bufferIndex = buffer.Index(startX, y, virtualBand);
1567 
1568  for(int x = startX; x <= endX; x++) {
1569  const int &sampleIntoChunk = x - outputStartSample;
1570 
1571  const int &chunkIndex = sampleIntoChunk +
1572  (lineSize * lineIntoChunk) + (bandSize * bandIntoChunk);
1573 
1574  double bufferVal = buffersDoubleBuf[bufferIndex];
1575 
1576  if(m_pixelType == Real) {
1577  float raw = 0;
1578 
1579  if(bufferVal >= VALID_MIN8) {
1580  double filePixelValueDbl = (bufferVal - m_base) /
1581  m_multiplier;
1582 
1583  if(filePixelValueDbl < (double) VALID_MIN4) {
1584  raw = LOW_REPR_SAT4;
1585  }
1586  else if(filePixelValueDbl > (double) VALID_MAX4) {
1587  raw = HIGH_REPR_SAT4;
1588  }
1589  else {
1590  raw = (float) filePixelValueDbl;
1591  }
1592  }
1593  else {
1594  if(bufferVal == NULL8)
1595  raw = NULL4;
1596  else if(bufferVal == LOW_INSTR_SAT8)
1597  raw = LOW_INSTR_SAT4;
1598  else if(bufferVal == LOW_REPR_SAT8)
1599  raw = LOW_REPR_SAT4;
1600  else if(bufferVal == HIGH_INSTR_SAT8)
1601  raw = HIGH_INSTR_SAT4;
1602  else if(bufferVal == HIGH_REPR_SAT8)
1603  raw = HIGH_REPR_SAT4;
1604  else
1605  raw = LOW_REPR_SAT4;
1606  }
1607  ((float *)chunkBuf)[chunkIndex] =
1608  m_byteSwapper ? m_byteSwapper->Float(&raw) : raw;
1609  }
1610  else if(m_pixelType == SignedWord) {
1611  short raw;
1612 
1613  if(bufferVal >= VALID_MIN8) {
1614  double filePixelValueDbl = (bufferVal - m_base) /
1615  m_multiplier;
1616  if(filePixelValueDbl < VALID_MIN2 - 0.5) {
1617  raw = LOW_REPR_SAT2;
1618  }
1619  if(filePixelValueDbl > VALID_MAX2 + 0.5) {
1620  raw = HIGH_REPR_SAT2;
1621  }
1622  else {
1623  int filePixelValue = (int)round(filePixelValueDbl);
1624 
1625  if(filePixelValue < VALID_MIN2) {
1626  raw = LOW_REPR_SAT2;
1627  }
1628  else if(filePixelValue > VALID_MAX2) {
1629  raw = HIGH_REPR_SAT2;
1630  }
1631  else {
1632  raw = filePixelValue;
1633  }
1634  }
1635  }
1636  else {
1637  if(bufferVal == NULL8)
1638  raw = NULL2;
1639  else if(bufferVal == LOW_INSTR_SAT8)
1640  raw = LOW_INSTR_SAT2;
1641  else if(bufferVal == LOW_REPR_SAT8)
1642  raw = LOW_REPR_SAT2;
1643  else if(bufferVal == HIGH_INSTR_SAT8)
1644  raw = HIGH_INSTR_SAT2;
1645  else if(bufferVal == HIGH_REPR_SAT8)
1646  raw = HIGH_REPR_SAT2;
1647  else
1648  raw = LOW_REPR_SAT2;
1649  }
1650 
1651  ((short *)chunkBuf)[chunkIndex] =
1652  m_byteSwapper ? m_byteSwapper->ShortInt(&raw) : raw;
1653  }
1654  else if(m_pixelType == UnsignedByte) {
1655  unsigned char raw;
1656 
1657  if(bufferVal >= VALID_MIN8) {
1658  double filePixelValueDbl = (bufferVal - m_base) /
1659  m_multiplier;
1660  if(filePixelValueDbl < VALID_MIN1 - 0.5) {
1661  raw = LOW_REPR_SAT1;
1662  }
1663  else if(filePixelValueDbl > VALID_MAX1 + 0.5) {
1664  raw = HIGH_REPR_SAT1;
1665  }
1666  else {
1667  int filePixelValue = (int)(filePixelValueDbl + 0.5);
1668  if(filePixelValue < VALID_MIN1) {
1669  raw = LOW_REPR_SAT1;
1670  }
1671  else if(filePixelValue > VALID_MAX1) {
1672  raw = HIGH_REPR_SAT1;
1673  }
1674  else {
1675  raw = (unsigned char)(filePixelValue);
1676  }
1677  }
1678  }
1679  else {
1680  if(bufferVal == NULL8)
1681  raw = NULL1;
1682  else if(bufferVal == LOW_INSTR_SAT8)
1683  raw = LOW_INSTR_SAT1;
1684  else if(bufferVal == LOW_REPR_SAT8)
1685  raw = LOW_REPR_SAT1;
1686  else if(bufferVal == HIGH_INSTR_SAT8)
1687  raw = HIGH_INSTR_SAT1;
1688  else if(bufferVal == HIGH_REPR_SAT8)
1689  raw = HIGH_REPR_SAT1;
1690  else
1691  raw = LOW_REPR_SAT1;
1692  }
1693 
1694  ((unsigned char *)chunkBuf)[chunkIndex] = raw;
1695  }
1696 
1697  bufferIndex ++;
1698  }
1699  }
1700  }
1701  }
1702  }
1703 
1704 
1708  void CubeIoHandler::writeNullDataToDisk() const {
1709  if(!m_dataIsOnDiskMap) {
1710  IString msg = "Cannot call CubeIoHandler::writeNullDataToDisk unless "
1711  "data is not already on disk (Cube::Create was called)";
1712  throw IException(IException::Programmer, msg, _FILEINFO_);
1713  }
1714 
1715  int numChunks = getChunkCount();
1716  for(int i = 0; i < numChunks; i++) {
1717  if(!(*m_dataIsOnDiskMap)[i]) {
1718  RawCubeChunk *nullChunk = getNullChunk(i);
1719  (const_cast<CubeIoHandler *>(this))->writeRaw(*nullChunk);
1720  (*m_dataIsOnDiskMap)[i] = true;
1721 
1722  delete nullChunk;
1723  nullChunk = NULL;
1724  }
1725  }
1726  }
1727 
1728 
1742  CubeIoHandler::BufferToChunkWriter::BufferToChunkWriter(
1743  CubeIoHandler * ioHandler, QList<Buffer *> buffersToWrite) {
1744  m_ioHandler = ioHandler;
1745  m_buffersToWrite = new QList<Buffer *>(buffersToWrite);
1746  m_timer = new QTime;
1747  m_timer->start();
1748 
1749  m_ioHandler->m_writeThreadMutex->lock();
1750  }
1751 
1752 
1763  CubeIoHandler::BufferToChunkWriter::~BufferToChunkWriter() {
1764  int elapsedMs = m_timer->elapsed();
1765  int idealFlushElapsedTime = 100; // ms
1766 
1767  // We want to aim our flush size at 100ms, so adjust accordingly to aim at
1768  // our target. This method seems to be extremely effective because
1769  // we maximize our I/O calls when caching is interfering and normalize
1770  // them otherwise.
1771  int msOffIdeal = elapsedMs - idealFlushElapsedTime;
1772  double percentOffIdeal = msOffIdeal / (double)idealFlushElapsedTime;
1773 
1774  // flush size is bounded to [32, 5000]
1775  int currentCacheSize = m_ioHandler->m_idealFlushSize;
1776  int desiredAdjustment = -1 * currentCacheSize * percentOffIdeal;
1777  int desiredCacheSize = (int)(currentCacheSize + desiredAdjustment);
1778  m_ioHandler->m_idealFlushSize = (int)(qMin(5000,
1779  qMax(32, desiredCacheSize)));
1780 
1781  delete m_timer;
1782  m_timer = NULL;
1783 
1784  m_ioHandler->m_writeThreadMutex->unlock();
1785  m_ioHandler = NULL;
1786 
1787  ASSERT(m_buffersToWrite->isEmpty());
1788  delete m_buffersToWrite;
1789  m_buffersToWrite = NULL;
1790  }
1791 
1792 
1798  void CubeIoHandler::BufferToChunkWriter::run() {
1799  // Sorting the buffers didn't seem to have a large positive impact on speed,
1800  // but does increase complexity so it's disabled.
1801 // QList<Buffer * > buffersToWrite(*m_buffersToWrite);
1802 // qSort(buffersToWrite.begin(), buffersToWrite.end(), bufferLessThan);
1803 
1804  // If the buffers have any overlap at all then we can't sort them and still
1805  // guarantee the last write() call makes it into the correct place. The
1806  // bufferLessThan is guaranteed to return false if there is overlap.
1807 // bool sortable = true;
1808 // for (int i = 1; sortable && i < buffersToWrite.size(); i++) {
1809 // if (!bufferLessThan(buffersToWrite[i - 1], buffersToWrite[i])) {
1810 // sortable = false;
1811 // }
1812 // }
1813 
1814 // if (!sortable) {
1815 // buffersToWrite = *m_buffersToWrite;
1816 // }
1817 
1818  foreach (Buffer * bufferToWrite, *m_buffersToWrite) {
1819  m_ioHandler->synchronousWrite(*bufferToWrite);
1820  delete bufferToWrite;
1821  }
1822 
1823  m_buffersToWrite->clear();
1824  m_ioHandler->m_dataFile->flush();
1825  }
1826 }
1827