]> git.mxchange.org Git - core.git/blob - framework/main/classes/file_directories/binary/class_BaseBinaryFile.php
Continued:
[core.git] / framework / main / classes / file_directories / binary / class_BaseBinaryFile.php
1 <?php
2 // Own namespace
3 namespace Org\Mxchange\CoreFramework\Filesystem\File;
4
5 // Import framework stuff
6 use Org\Mxchange\CoreFramework\Bootstrap\FrameworkBootstrap;
7 use Org\Mxchange\CoreFramework\EntryPoint\ApplicationEntryPoint;
8 use Org\Mxchange\CoreFramework\Factory\Object\ObjectFactory;
9 use Org\Mxchange\CoreFramework\Filesystem\File\BaseAbstractFile;
10 use Org\Mxchange\CoreFramework\Filesystem\FilePointer;
11 use Org\Mxchange\CoreFramework\Generic\FrameworkInterface;
12 use Org\Mxchange\CoreFramework\Middleware\Debug\DebugMiddleware;
13 use Org\Mxchange\CoreFramework\Traits\Index\IndexableTrait;
14 use Org\Mxchange\CoreFramework\Traits\Stack\StackableTrait;
15
16 // Import SPL stuff
17 use \BadMethodCallException;
18 use \InvalidArgumentException;
19 use \LogicException;
20 use \OutOfBoundsException;
21 use \SplFileInfo;
22 use \UnexpectedValueException;
23
24 /**
25  * A general binary file class
26  *
27  * @author              Roland Haeder <webmaster@ship-simu.org>
28  * @version             0.0.0
29  * @copyright   Copyright (c) 2007, 2008 Roland Haeder, 2009 - 2023 Core Developer Team
30  * @license             GNU GPL 3.0 or any newer version
31  * @link                http://www.ship-simu.org
32  *
33  * This program is free software: you can redistribute it and/or modify
34  * it under the terms of the GNU General Public License as published by
35  * the Free Software Foundation, either version 3 of the License, or
36  * (at your option) any later version.
37  *
38  * This program is distributed in the hope that it will be useful,
39  * but WITHOUT ANY WARRANTY; without even the implied warranty of
40  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
41  * GNU General Public License for more details.
42  *
43  * You should have received a copy of the GNU General Public License
44  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
45  */
46 abstract class BaseBinaryFile extends BaseAbstractFile implements BinaryFile {
47         // Load traits
48         use StackableTrait;
49         use IndexableTrait;
50
51         /**
52          * Configuration cache
53          */
54         private static $configCache = [];
55
56         /**
57          * Current seek position
58          */
59         private $seekPosition = 0;
60
61         /**
62          * Size of header
63          */
64         private $headerSize = 0;
65
66         /**
67          * File header
68          */
69         private $header = [];
70
71         /**
72          * Seek positions for gaps ("fragmentation")
73          */
74         private $gaps = [];
75
76         /**
77          * Seek positions for damaged entries (e.g. mismatching hash sum, ...)
78          */
79         private $damagedEntries = [];
80
81         /**
82          * Back-buffer
83          */
84         private $backBuffer = '';
85
86         /**
87          * Currently loaded block (will be returned by current())
88          */
89         private $currentBlock = '';
90
91         /**
92          * Protected constructor
93          *
94          * @param       $className      Name of the class
95          * @return      void
96          */
97         protected function __construct (string $className) {
98                 // Call parent constructor
99                 parent::__construct($className);
100         }
101
102         /**
103          * Setter for backBuffer field
104          *
105          * @param       $backBuffer             Characters to "store" in back-buffer
106          * @return      void
107          */
108         private function setBackBuffer (string $backBuffer) {
109                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-BINARY-FILE: Setting backBuffer(%d)=%s - CALLED!', strlen($backBuffer), $backBuffer));
110                 $this->backBuffer = $backBuffer;
111         }
112
113         /**
114          * Getter for backBuffer field
115          *
116          * @return      $backBuffer             Characters "stored" in back-buffer
117          */
118         private function getBackBuffer () {
119                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-BINARY-FILE: Getting this->backBuffer(%d)=%s - CALLED!', strlen($this->backBuffer), $this->backBuffer));
120                 return $this->backBuffer;
121         }
122
123         /**
124          * Setter for current field
125          *
126          * @param       $current        Characters to set a currently loaded block
127          * @return      void
128          */
129         private function setCurrentBlock (string $currentBlock) {
130                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-BINARY-FILE: Setting currentBlock(%d)=%s - CALLED!', strlen($currentBlock), $currentBlock));
131                 $this->currentBlock = $currentBlock;
132         }
133
134         /**
135          * Gets currently read data
136          *
137          * @return      $current        Currently read data
138          */
139         public function getCurrentBlock () {
140                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-BINARY-FILE: Getting this->currentBlock(%d)=%s - CALLED!', strlen($this->currentBlock), $this->currentBlock));
141                 return $this->currentBlock;
142         }
143
144         /**
145          * Getter for header size
146          *
147          * @return      $totalEntries   Size of file header
148          */
149         public final function getHeaderSize () {
150                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-BINARY-FILE: Getting this->headerSize=%d - CALLED!', $this->headerSize));
151                 return $this->headerSize;
152         }
153
154         /**
155          * Setter for header size
156          *
157          * @param       $headerSize             Size of file header
158          * @return      void
159          */
160         public final function setHeaderSize (int $headerSize) {
161                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-BINARY-FILE: Setting headerSize=%d - CALLED!', $headerSize));
162                 $this->headerSize = $headerSize;
163         }
164
165         /**
166          * Getter for header array
167          *
168          * @return      $totalEntries   Size of file header
169          */
170         public final function getHeader () {
171                 // Get it
172                 return $this->header;
173         }
174
175         /**
176          * Setter for header
177          *
178          * @param       $header         Array for a file header
179          * @return      void
180          */
181         public final function setHeader (array $header) {
182                 // Set it
183                 $this->header = $header;
184         }
185
186         /**
187          * Getter for seek position
188          *
189          * @return      $seekPosition   Current seek position (stored here in object)
190          */
191         public final function getSeekPosition () {
192                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-BINARY-FILE: Getting this->seekPosition=%d - CALLED!', $this->seekPosition));
193                 return $this->seekPosition;
194         }
195
196         /**
197          * Setter for seek position
198          *
199          * @param       $seekPosition   Current seek position (stored here in object)
200          * @return      void
201          */
202         protected final function setSeekPosition (int $seekPosition) {
203                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-BINARY-FILE: Setting seekPosition=%d - CALLED!', $seekPosition));
204                 $this->seekPosition = $seekPosition;
205         }
206
207         /**
208          * Checks whether the abstracted file only contains gaps by counting all
209          * gaps' bytes together and compare it to total length.
210          *
211          * @return      $isGapsOnly             Whether the abstracted file only contains gaps
212          * @throws      OutOfBoundsException    If calculated file size is larger than actual
213          */
214         public function isFileGapsOnly () {
215                 // Count every gap
216                 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('BASE-BINARY-FILE: CALLED!');
217                 $gapsSize = 0;
218                 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-BINARY-FILE: this->gaps()=%d', count($this->gaps)));
219                 foreach ($this->gaps as $gap) {
220                         // Calculate size of found gap: end-start including both
221                         //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-BINARY-FILE: gap[%s]=%d,ga[%s]=%d', BinaryFile::GAPS_INDEX_START, $gap[BinaryFile::GAPS_INDEX_START], BinaryFile::GAPS_INDEX_END, $gap[BinaryFile::GAPS_INDEX_END]));
222                         $gapsSize += ($gap[BinaryFile::GAPS_INDEX_END] - $gap[BinaryFile::GAPS_INDEX_START]);
223
224                         // Debug message
225                         //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-BINARY-FILE: gapsSize=%d', $gapsSize));
226                 }
227
228                 // Total gap size + header size + 1 must be same as file size
229                 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-BINARY-FILE: gapsSize=%d,this->headerSize=%d,this->fileSize=%d', $gapsSize, $this->getHeaderSize(), $this->getFileSize()));
230                 $determinedFileSize = ($gapsSize + $this->getHeaderSize() + 1);
231
232                 // Should not be more!
233                 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-BINARY-FILE: determinedFileSize=%d,this->fileSize=%d', $determinedFileSize, $this->getFileSize()));
234                 if ($determinedFileSize > $this->getFileSize()) {
235                         // Should not happen
236                         throw new OutOfBoundsException(sprintf('determinedFileSize=%d is larger than this->fileSize=%d', $determinedFileSize, $this->getFileSize()));
237                 }
238
239                 // Is it same?
240                 $isGapsOnly = ($determinedFileSize == $this->getFileSize());
241
242                 // Return flag
243                 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-BINARY-FILE: isGapsOnly=%d - EXIT!', intval($isGapsOnly)));
244                 return $isGapsOnly;
245         }
246
247         /**
248          * Marks whole file as gaps-only (freshly created file
249          *
250          * @param       $type   Type of file
251          * @param       $minimumBlockLength             Minimum block length
252          * @return      void
253          */
254         private function markFileGapsOnly (string $type, int $minimumBlockLength) {
255                 // Is config cache there?
256                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-BINARY-FILE: type=%s,minimumBlockLength=%d - CALLED!', $type, $minimumBlockLength));
257                 if (!isset(self::$configCache[$type . '_pre_allocate_count'])) {
258                         // Then set it
259                         self::$configCache[$type . '_pre_allocate_count'] = FrameworkBootstrap::getConfigurationInstance()->getConfigEntry($type . '_pre_allocate_count');
260                 }
261
262                 // Very simple to do ...
263                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-BINARY-FILE: self:configCache[%s_pre_allocate_count]()=%d', count(self::$configCache[$type . '_pre_allocate_count'])));
264                 for ($idx = 0; $idx < self::$configCache[$type . '_pre_allocate_count']; $idx++) {
265                         // Calculate start/end positions
266                         $startPosition = $idx * $minimumBlockLength;
267                         $endPosition = $idx * $minimumBlockLength + $minimumBlockLength;
268
269                         // Mark start and end position as gap
270                         //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-BINARY-FILE: Calling this->addGap(%d, %d) ...', $startPosition, $endPosition));
271                         $this->addGap($startPosition, $endPosition);
272                 }
273
274                 // Trace message
275                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('BASE-BINARY-FILE: EXIT!');
276         }
277
278         /**
279          * Adds a gap for given start and end position
280          *
281          * @param       $startPosition  Start seek position
282          * @param       $endPosition    End seek position
283          * @return      void
284          */
285         private function addGap(int $startPosition, int $endPosition) {
286                 // Push to gaps array
287                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-BINARY-FILE: startPosition=%d,endPosition=%d - CALLED!', $startPosition, $endPosition));
288                 array_push($this->gaps, [
289                         BinaryFile::GAPS_INDEX_START  => $startPosition,
290                         BinaryFile::GAPS_INDEX_END    => $endPosition,
291                 ]);
292
293                 // Trace message
294                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('BASE-BINARY-FILE: EXIT!');
295         }
296
297         /**
298          * Initializes the back-buffer by setting it to an empty string.
299          *
300          * @return      void
301          */
302         private function initBackBuffer () {
303                 // Simply call the setter
304                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('BASE-BINARY-FILE: CALLED!');
305                 $this->setBackBuffer('');
306
307                 // Trace message
308                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('BASE-BINARY-FILE: EXIT!');
309         }
310
311         /**
312          * Seeks to beginning of file, updates seek position in this object and
313          * flushes the header.
314          *
315          * @param       $flushHeader    Wether the file's header should be flushed (default: false)
316          * @return      void
317          */
318         protected function rewindUpdateSeekPosition (bool $flushHeader = false) {
319                 // Seek to beginning of file
320                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-BINARY-FILE: flushHeader=%d - CALLED!', intval($flushHeader)));
321                 $this->rewind();
322
323                 // And update seek position ...
324                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('BASE-BINARY-FILE: Calling this->updateSeekPosition() ...');
325                 $this->updateSeekPosition();
326
327                 // Flush headers?
328                 if ($flushHeader) {
329                         // ... to write it back into the file
330                         //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('BASE-BINARY-FILE: Calling this->flushFileHeader() ...');
331                         $this->flushFileHeader();
332                 }
333
334                 // Trace message
335                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('BASE-BINARY-FILE: EXIT!');
336         }
337
338         /**
339          * Seeks to old position
340          *
341          * @return      void
342          */
343         protected function seekToOldPosition () {
344                 // Seek to currently ("old") saved position
345                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('BASE-BINARY-FILE: CALLED!');
346                 $this->seek($this->determineSeekPosition());
347
348                 // Trace message
349                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('BASE-BINARY-FILE: EXIT!');
350         }
351
352         /**
353          * Initializes this file class
354          *
355          * @param       $fileInfoInstance       An instance of a SplFileInfo class
356          * @return      void
357          */
358         protected function initFile (SplFileInfo $fileInfoInstance) {
359                 // Get a file i/o pointer instance
360                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-BINARY-FILE: fileInfoInstance[%s]=%s - CALLED!', get_class($fileInfoInstance), $fileInfoInstance));
361                 $pointerInstance = ObjectFactory::createObjectByConfiguredName('file_raw_input_output_class', array($fileInfoInstance));
362
363                 // ... and set it here
364                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-BINARY-FILE: Setting pointerInstance=%s ...', $pointerInstance->__toString()));
365                 $this->setPointerInstance($pointerInstance);
366
367                 // Trace message
368                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('BASE-BINARY-FILE: EXIT!');
369         }
370
371         /**
372          * Marks the currently loaded block as empty (with length of the block)
373          *
374          * @param       $length         Length of the block
375          * @return      void
376          * @throws      InvalidArgumentException        If a parameter is invalid
377          */
378         protected function markCurrentBlockAsEmpty (int $length) {
379                 // Validate parameter
380                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-BINARY-FILE: length=%d - CALLED!', $length));
381                 if ($length < 1) {
382                         // Length cannot below one
383                         throw new InvalidArgumentException(sprintf('length=%d is not valid', $length));
384                 }
385
386                 // Get current seek position
387                 $currentPosition = $this->determineSeekPosition();
388
389                 // Now add it as gap entry
390                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-BINARY-FILE: Calling this->addGap(%d, %d) ..', ($currentPosition - $length), $currentPosition));
391                 $this->addGap(($currentPosition - $length), $currentPosition);
392
393                 // Trace message
394                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('BASE-BINARY-FILE: EXIT!');
395         }
396
397         /**
398          * Initializes counter for valid entries, arrays for damaged entries and
399          * an array for gap seek positions. If you call this method on your own,
400          * please re-analyze the file structure. So you are better to call
401          * analyzeFileStructure() instead of this method.
402          *
403          * @return      void
404          */
405         public function initCountersGapsArray () {
406                 // Init counter and seek position to header size
407                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('BASE-BINARY-FILE: Calling this->determineSeekPosition() - CALLED!');
408                 $seekPosition = $this->getSeekPosition();
409
410                 // Set counter
411                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-BINARY-FILE: seekPosition=%d', $seekPosition));
412                 $this->setCounter(0);
413
414                 // Get header size
415                 $headerSize = $this->getHeaderSize();
416
417                 // Set it
418                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-BINARY-FILE: Setting this->seekPosition=%d ...', $headerSize));
419                 $this->setSeekPosition($headerSize);
420
421                 // Init arrays
422                 $this->gaps = [];
423                 $this->damagedEntries = [];
424
425                 // Seek back to old position
426                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-BINARY-FILE: Calling this->seek(%d) ...', $seekPosition));
427                 $this->seek($seekPosition);
428
429                 // Trace message
430                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('BASE-BINARY-FILE: EXIT!');
431         }
432
433         /**
434          * Updates seekPosition attribute from file to avoid to much access on file.
435          *
436          * @return      void
437          */
438         public function updateSeekPosition () {
439                 // Get key (= seek position)
440                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('BASE-BINARY-FILE: CALLED!');
441                 $seekPosition = $this->determineSeekPosition();
442
443                 // And set it here
444                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-BINARY-FILE: seekPosition=%d', $seekPosition));
445                 $this->setSeekPosition($seekPosition);
446
447                 // Trace message
448                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('BASE-BINARY-FILE: EXIT!');
449         }
450
451         /**
452          * Checks whether the block separator has been found
453          *
454          * @param       $str            String to look in
455          * @return      $isFound        Whether the block separator has been found
456          * @throws      InvalidArgumentException        If a parameter is not valid
457          */
458         public static function isBlockSeparatorFound (string $str) {
459                 // Validate parameter
460                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-BINARY-FILE: str=%s - CALLED!', $str));
461                 if (empty($str)) {
462                         // Throw IAE
463                         throw new InvalidArgumentException('Parameter "str" is empty', FrameworkInterface::EXCEPTION_INVALID_ARGUMENT);
464                 }
465
466                 // Determine it
467                 $isFound = (strpos($str, chr(BinaryFile::SEPARATOR_ENTRIES)) !== false);
468
469                 // Return result
470                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-BINARY-FILE: isFound=%d - EXIT!', intval($isFound)));
471                 return $isFound;
472         }
473
474         /**
475          * Writes data at given position
476          *
477          * @param       $seekPosition   Seek position
478          * @param       $data                   Data to be written
479          * @param       $flushHeader    Whether to flush the header (default: flush)
480          * @return      void
481          * @throws      OutOfBoundsException    If the position is not seekable
482          * @throws      InvalidArgumentException        If a parameter is invalid
483          */
484         public function writeData (int $seekPosition, string $data, bool $flushHeader = true) {
485                 // Validate parameter
486                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-BINARY-FILE: seekPosition=%s,data()=%d,flushHeader=%d - CALLED!', $seekPosition, strlen($data), intval($flushHeader)));
487                 if ($seekPosition < 0) {
488                         // Invalid seek position
489                         throw new OutOfBoundsException(sprintf('seekPosition=%d is not valid', $seekPosition));
490                 } elseif (empty($data)) {
491                         // Empty data is invalid, too
492                         throw new InvalidArgumentException('Parameter "data" is empty', FrameworkInterface::EXCEPTION_INVALID_ARGUMENT);
493                 }
494
495                 // Write data at given position
496                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-BINARY-FILE: Calling this->writeAtPosition(%d,%s) ...', $seekPosition, $data));
497                 $this->writeAtPosition($seekPosition, $data);
498
499                 // Increment counter
500                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('BASE-BINARY-FILE: Calling this->incrementCounter() ...');
501                 $this->incrementCounter();
502
503                 // Update seek position
504                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('BASE-BINARY-FILE: Calling this->updateSeekPosition() ...');
505                 $this->updateSeekPosition();
506
507                 // Flush the header?
508                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-BINARY-FILE: flushHeader=%d', intval($flushHeader)));
509                 if ($flushHeader === true) {
510                         // Flush header
511                         //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('BASE-BINARY-FILE: Calling this->flushFileHeader() ...');
512                         $this->flushFileHeader();
513
514                         // Seek to old position
515                         //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('BASE-BINARY-FILE: Calling this->seekToOldPosition() ...');
516                         $this->seekToOldPosition();
517                 }
518
519                 // Trace message
520                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('BASE-BINARY-FILE: EXIT!');
521         }
522
523         /**
524          * Writes at given position by seeking to it.
525          *
526          * @param       $seekPosition   Seek position in file
527          * @param       $dataStream             Data to be written
528          * @return      mixed                   Number of writes bytes or false on error
529          * @throws      OutOfBoundsException    If the position is not seekable
530          * @throws      InvalidArgumentException        If a parameter is not valid
531          */
532         public function writeAtPosition (int $seekPosition, string $dataStream) {
533                 // Validate parameter
534                 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-BINARY-FILE: seekPosition=%d,dataStream(%d)=%s - CALLED!', $seekPosition, strlen($dataStream), $dataStream));
535                 if ($seekPosition < 0) {
536                         // Invalid seek position
537                         throw new OutOfBoundsException(sprintf('seekPosition=%d is not valid.', $seekPosition));
538                 } elseif (empty($dataStream)) {
539                         // Empty dataStream
540                         throw new InvalidArgumentException('Parameter "dataStream" is empty', FrameworkInterface::EXCEPTION_INVALID_ARGUMENT);
541                 }
542
543                 // Call pointer's method
544                 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-BINARY-FILE: Calling this->pointerInstance->writeAtPosition(%d, %s) ...', $seekPosition, $dataStream));
545                 $status = $this->getPointerInstance()->writeAtPosition($seekPosition, $dataStream);
546
547                 // Return status
548                 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-BINARY-FILE: status[%s]=%d - EXIT!', gettype($status), $status));
549                 return $status;
550         }
551
552         /**
553          * Checks whether the file header is initialized
554          *
555          * @return      $isInitialized  Whether the file header is initialized
556          */
557         public function isFileHeaderInitialized () {
558                 // Default is not initialized
559                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('BASE-BINARY-FILE: CALLED!');
560                 $isInitialized = false;
561
562                 // Is the file initialized?
563                 if ($this->isFileInitialized()) {
564                         // Some bytes has been written, so rewind to start of it.
565                         $this->rewind();
566
567                         // Read file header
568                         //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('BASE-BINARY-FILE: Calling this->readFileHeader() ...');
569                         $this->readFileHeader();
570
571                         // Get header count
572                         $headerCount = count($this->getHeader());
573
574                         // The above method does already check the header
575                         //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-BINARY-FILE: headerCount=%d', $headerCount));
576                         $isInitialized = ($headerCount > 0);
577                 }
578
579                 // Return result
580                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-BINARY-FILE: isInitialized=%d - EXIT!', intval($isInitialized)));
581                 return $isInitialized;
582         }
583
584         /**
585          * Checks whether the assigned file has been initialized
586          *
587          * @return      $isInitialized          Whether the file's size is zero
588          * @throws      UnexpectedValueException        If an unexpected value was returned
589          */
590         public function isFileInitialized () {
591                 // Get it from iterator which holds the pointer instance. If false is returned
592                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('BASE-BINARY-FILE: CALLED!');
593                 $fileSize = $this->size();
594
595                 /*
596                  * The returned file size should not be false or NULL as this means
597                  * that the pointer class does not work correctly.
598                  */
599                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-BINARY-FILE: fileSize[%s]=%d', gettype($fileSize), $fileSize));
600                 if (!is_int($fileSize)) {
601                         // Bad file?
602                         throw new UnexpectedValueException(sprintf('fileSize[]=%s is unexpected', gettype($fileSize)));
603                 }
604
605                 // Is more than 0 returned?
606                 $isInitialized = ($fileSize > 0);
607
608                 // Return result
609                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-BINARY-FILE: isInitialized=%d - EXIT!', intval($isInitialized)));
610                 return $isInitialized;
611         }
612
613         /**
614          * Creates the assigned file
615          *
616          * @return      void
617          * @throws      BadMethodCallException  If this file's header is already initialized
618          */
619         public function createFileHeader () {
620                 // The file's header should not be initialized here
621                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('BASE-BINARY-FILE: CALLED!');
622                 if ($this->isFileHeaderInitialized()) {
623                         // Bad method call
624                         //* DEBUG-DIE: */ ApplicationEntryPoint::exitApplication(sprintf('[%s:%d]: this=%s', __METHOD__, __LINE__, print_r($this, TRUE)));
625                         throw new BadMethodCallException('File header is already initialized but method called');
626                 }
627
628                 // Simple flush file header which will create it.
629                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('BASE-BINARY-FILE: Calling this->flushFileHeader() ...');
630                 $this->flushFileHeader();
631
632                 // Rewind seek position (to beginning of file) and update/flush file header
633                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('BASE-BINARY-FILE: Calling this->rewindUpdateSeekPosition() ...');
634                 $this->rewindUpdateSeekPosition();
635
636                 // Trace message
637                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('BASE-BINARY-FILE: EXIT!');
638         }
639
640         /**
641          * Determines seek position
642          *
643          * @return      $seekPosition   Current seek position
644          */
645         public function determineSeekPosition () {
646                 // Call pointer instance
647                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('BASE-BINARY-FILE: CALLED!');
648                 $seekPosition = $this->getPointerInstance()->determineSeekPosition();
649
650                 // Return position
651                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-BINARY-FILE: seekPosition=%d - EXIT!', $seekPosition));
652                 return $seekPosition;
653         }
654
655         /**
656          * Seek to given offset (default) or other possibilities as fseek() gives.
657          *
658          * @param       $offset         Offset to seek to (or used as "base" for other seeks)
659          * @param       $whence         Added to offset (default: only use offset to seek to)
660          * @return      $status         Status of file seek: 0 = success, -1 = failed
661          * @throws      OutOfBoundsException    If the position is not seekable
662          */
663         public function seek (int $offset, int $whence = SEEK_SET) {
664                 // Validate parameter
665                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-BINARY-FILE: offset=%d,whence=%d - CALLED!', $offset, $whence));
666                 if ($offset < 0) {
667                         // No offset is smaller than zero
668                         throw new OutOfBoundsException(sprintf('offset=%d is not valid', $offset));
669                 }
670
671                 // Call pointer instance
672                 $status = $this->getPointerInstance()->seek($offset, $whence);
673
674                 // Return status
675                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-BINARY-FILE: status[%s]=%d - EXIT!', gettype($status), $status));
676                 return $status;
677         }
678
679         /**
680          * Reads given amount of bytes from file.
681          *
682          * @param       $bytes  Amount of bytes to read
683          * @return      $data   Data read from file
684          * @throws      OutOfBoundsException    If the position is not seekable
685          */
686         public function read (int $bytes = 0) {
687                 // Validate parameter
688                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-BINARY-FILE: bytes=%d - CALLED!', $bytes));
689                 if ($bytes < 0) {
690                         // Throw exception
691                         throw new OutOfBoundsException(sprintf('bytes=%d is not valid', $bytes));
692                 }
693
694                 // Call pointer instance
695                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-BINARY-FILE: Calling this->pointerInstance->read(%d) ...', $bytes));
696                 $data = $this->getPointerInstance()->read($bytes);
697
698                 // Update seek position
699                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('BASE-BINARY-FILE: Calling this->updateSeekPosition() ...');
700                 $this->updateSeekPosition();
701
702                 // Return data
703                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-BINARY-FILE: data[%s]=%s - EXIT!', gettype($data), $data));
704                 return $data;
705         }
706
707         /**
708          * Rewinds to the beginning of the file
709          *
710          * @return      void
711          */
712         public function rewind () {
713                 // Call pointer instance
714                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('BASE-BINARY-FILE: CALLED!');
715                 $this->getPointerInstance()->rewind();
716
717                 // Trace message
718                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('BASE-BINARY-FILE: EXIT!');
719         }
720
721         /**
722          * Analyzes entries in index file. This will count all found (and valid)
723          * entries, mark invalid as damaged and count gaps ("fragmentation"). If
724          * only gaps are found, the file is considered as "virgin" (no entries).
725          *
726          * @return      void
727          * @throws      BadMethodCallException  If this method is called but file is not initialized
728          */
729         public function analyzeFileStructure () {
730                 // Make sure the file is initialized
731                 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('BASE-BINARY-FILE: CALLED!');
732                 if (!$this->isFileInitialized()) {
733                         // Bad method call
734                         throw new BadMethodCallException('Method called but file is not initialized.');
735                 }
736
737                 // Init counters and gaps array
738                 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('BASE-BINARY-FILE: Calling this->initCounterGapsArrays() ...');
739                 $this->initCountersGapsArray();
740
741                 // Output message (as this may take some time)
742                 self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('Analyzing file structure ... (this may take some time)'));
743
744                 // First Seek to right after file header
745                 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-BINARY-FILE: Calling this->seek(%d) ...', $this->getHeaderSize() + 1));
746                 $this->seek($this->getHeaderSize() + 1);
747
748                 // Then try to load all entries
749                 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-BINARY-FILE: this->seekPosition=%d, looping through file ...', $this->getSeekPosition()));
750                 while ($this->isValid()) {
751                         // Get current entry
752                         $current = $this->getCurrentBlock();
753
754                         // Go to next entry
755                         //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-BINARY-FILE: current=%s, calling this->readNextBlock() ...', $current));
756                         $this->readNextBlock();
757
758                         /*
759                          * If the block is empty, maybe the whole file is? This could mean
760                          * that the file has been pre-allocated.
761                          */
762                         //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-BINARY-FILE: current(%d)[]=%s', strlen($current), gettype($current)));
763                         if (empty(trim($current, chr(0)))) {
764                                 // Then skip this part
765                                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-BINARY-FILE: current[]=%s is empty - CONTINUE!', gettype($current)));
766                                 continue;
767                         }
768
769                         // Handle current record
770                         /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-BINARY-FILE: current(%d)[%s]=%s', strlen($current), gettype($current), $current));
771                 }
772
773                 // If the last read block is empty, check gaps
774                 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-BINARY-FILE: current()=%d', strlen($current)));
775                 if (empty(trim($current, chr(0)))) {
776                         // Output message
777                         self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-BINARY-FILE: Found a total of %d gaps.', count($this->gaps)));
778
779                         // Check gaps, if the whole file is empty.
780                         /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('BASE-BINARY-FILE: Calling this->isFileGapsOnly() ...');
781                         if ($this->isFileGapsOnly()) {
782                                 // Only gaps, so don't continue here.
783                                 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('BASE-BINARY-FILE: File is gaps-only - EXIT!');
784                                 return;
785                         }
786                 }
787
788                 /*
789                  * The above call has calculated a total size of all gaps. If the
790                  * percentage of gaps passes a "soft" limit and last
791                  * defragmentation is to far in the past, or if a "hard" limit has
792                  * reached, run defragmentation.
793                  */
794                 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('BASE-BINARY-FILE: Calling this->isDefragmentationNeeded() ...');
795                 if ($this->isDefragmentationNeeded()) {
796                         // Run "defragmentation"
797                         /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('BASE-BINARY-FILE: Calling this->doRunDefragmentation() ...');
798                         $this->doRunDefragmentation();
799                 }
800
801                 // Trace message
802                 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('BASE-BINARY-FILE: EXIT!');
803         }
804
805         /**
806          * Reads next "block" of given bytes into $currentBlock field. THis method
807          * loads the whole file into memory when the file is just freshly
808          * initialized (only zeros in it).
809          *
810          * @return      void
811          * @throws      InvalidArgumentException        If a parameter is not valid
812          */
813         protected function readNextBlockByLength (int $length) {
814                 // Validate parameter
815                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-BINARY-FILE: this->seekPosition=%d,length=%d - CALLED!', $this->getSeekPosition(), $length));
816                 if ($length < 1) {
817                         // Throw IAE
818                         throw new InvalidArgumentException(sprintf('length=%d is not valid', $length));
819                 }
820
821                 // Read possibly back-buffered bytes from previous call of next().
822                 $data = $this->getBackBuffer();
823
824                 /*
825                  * Read until a entry/block separator has been found. The next read
826                  * "block" may not fit, so this loop will continue until the EOB or EOF
827                  * has been reached whatever comes first.
828                  */
829                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-BINARY-FILE: this->seekPosition=%d,data()=%d', $this->getSeekPosition(), strlen($data)));
830                 while ((!$this->isEndOfFileReached()) && (empty($data) || !self::isBlockSeparatorFound($data))) {
831                         // Then read the next possible block
832                         //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-BINARY-FILE: this->seekPosition=%d, calling this->read(%d) ...', $this->getSeekPosition(), $length));
833                         $block = $this->read($length);
834
835                         // Is the block empty?
836                         //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-BINARY-FILE: block()=%d,length=%d', strlen($block), $length));
837                         if (strlen($block) == 0) {
838                                 // Read empty block, maybe EOF
839                                 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-BINARY-FILE: this->seekPosition=%d, block is empty, maybe EOF reached - BREAK!', $this->getSeekPosition()));
840                                 break;
841                         } elseif (empty(trim($block, chr(0)))) {
842                                 // Mark it as empty
843                                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-BINARY-FILE: this->seekPosition=%d, calling this->markCurrentBlockAsEmpty(%d) ...', $this->getSeekPosition(), $length));
844                                 $this->markCurrentBlockAsEmpty($length);
845                         }
846
847                         // At this block then
848                         $data .= $block;
849
850                         // Debug message
851                         //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-BINARY-FILE: this->seekPosition=%d,data()=%d', $this->getSeekPosition(), strlen($data)));
852                 }
853
854                 /*
855                  * Init back-buffer which is the data that has been found beyond the
856                  * separator.
857                  */
858                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('BASE-BINARY-FILE: Calling this->initBackBuffer(), clearing this->currentBlock ...');
859                 $this->initBackBuffer();
860                 $this->setCurrentBlock('');
861
862                 // Is $data empty?
863                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-BINARY-FILE: data(%d)=%s', strlen($data), $data));
864                 if (empty(trim($data, chr(0)))) {
865                         // Yes, maybe whole file was ...
866                         //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-BINARY-FILE: this->seekPosition=%d, maybe empty file found - EXIT!', $this->getSeekPosition()));
867                         return;
868                 }
869
870                 // Separate data
871                 $dataArray = explode(chr(BinaryFile::SEPARATOR_ENTRIES), $data);
872
873                 // Left part is the actual block, right one the back-buffer data, if found
874                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-BINARY-FILE: dataArray()=%d', count($dataArray)));
875                 //* PRINTR-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-BINARY-FILE: dataArray=%s', print_r($dataArray, true)));
876                 $this->setCurrentBlock($dataArray[0]);
877
878                 // Is back buffere data found?
879                 if (isset($dataArray[1]) && !empty(trim($dataArray[1], chr(0)))) {
880                         // Set back buffer
881                         //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-BINARY-FILE: Setting this->backBuffer=%s ...', $dataArray[1]));
882                         $this->setBackBuffer($dataArray[1]);
883                 }
884
885                 // Trace message
886                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('BASE-BINARY-FILE: EXIT!');
887         }
888
889         /**
890          * Pre-allocates file (if enabled) with some space for later faster write access.
891          *
892          * @param       $type   Type of the file
893          * @return      void
894          * @throws      InvalidArgumentException        If a parameter is empty
895          * @throws      BadMethodCallException  If this->stackInstance is not properly set
896          */
897         protected function preAllocateFileByTypeLength (string $type, int $minimumBlockLength) {
898                 // Is it enabled?
899                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-BINARY-FILE: type=%s,minimumBlockLength=%d - CALLED!', $type, $minimumBlockLength));
900                 if (empty($type)) {
901                         // Empty type
902                         throw new InvalidArgumentException('Parameter "type" is empty', FrameworkInterface::EXCEPTION_INVALID_ARGUMENT);
903                 } elseif ($minimumBlockLength < 1) {
904                         // Invalid block length
905                         throw new InvalidArgumentException(sprintf('Parameter minimumBlockLength=%d is not valid', $minimumBlockLength));
906                 } elseif (FrameworkBootstrap::getConfigurationInstance()->getConfigEntry($type . '_pre_allocate_enabled') != 'Y') {
907                         // Don't continue here.
908                         self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-BINARY-FILE: Not pre-allocating file.'));
909                         return;
910                 }
911
912                 // Get file size
913                 $fileSize = $this->getFileSize();
914
915                 // Calulcate seek position
916                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-BINARY-FILE: minimumBlockLength=%d,fileSize=%d', $minimumBlockLength, $fileSize));
917                 $seekPosition = $this->getHeaderSize() + $minimumBlockLength * FrameworkBootstrap::getConfigurationInstance()->getConfigEntry($type . '_pre_allocate_count') + $fileSize ;
918
919                 // Now simply write a NUL there. This will pre-allocate the file.
920                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-BINARY-FILE: Calling this->writeAtPosition(%d,NUL) ...', $seekPosition));
921                 $this->writeAtPosition($seekPosition, chr(0));
922
923                 // Is the seek position zero?
924                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-BINARY-FILE: fileSize=%d', $fileSize));
925                 if ($fileSize == 0) {
926                         // Mark file as gaps-only
927                         //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-BINARY-FILE: Calling this->markGapsOnly(%s,%d) ...', $type, $minimumBlockLength));
928                         $this->markFileGapsOnly($type, $minimumBlockLength);
929                 } else {
930                         // Analyze file structure
931                         //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('BASE-BINARY-FILE: Calling this->analyzeFileStructure() ...');
932                         $this->analyzeFileStructure();
933                 }
934
935                 // Rewind seek position
936                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('BASE-BINARY-FILE: Calling this->rewind() ...');
937                 $this->rewind();
938
939                 // Trace message
940                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('BASE-BINARY-FILE: EXIT!');
941         }
942
943         /**
944          * Checks wether the current entry is valid (not at the end of the file).
945          * This method will return true if an emptied (nulled) entry has been found.
946          *
947          * @return      $isValid        Whether the next entry is valid
948          * @throws      InvalidArgumentException        If a parameter is not valid
949          */
950         protected function isValidByLength (int $length) {
951                 // Validate parameter
952                 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-BINARY-FILE: length=%d - CALLED!', $length));
953                 if ($length < 1) {
954                         // Throw IAE
955                         throw new InvalidArgumentException(sprintf('Parameter length=%d is not valid', $length));
956                 }
957
958                 // Get current seek position
959                 $seekPosition = $this->determineSeekPosition();
960
961                 // Then try to read it
962                 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-BINARY-FILE: seekPosition=%d', $seekPosition));
963                 $data = $this->read($length);
964
965                 // If some bytes could be read, all is fine
966                 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-BINARY-FILE: data[%s]()=%d', gettype($data), strlen($data)));
967                 $isValid = ((is_string($data)) && (strlen($data) > 0));
968
969                 // Get header size
970                 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-BINARY-FILE: isValid=%d', intval($isValid)));
971                 $headerSize = $this->getHeaderSize();
972
973                 // Is the seek position at or beyond the header?
974                 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-BINARY-FILE: seekPosition=%d,headerSize=%d', $seekPosition, $headerSize));
975                 if ($seekPosition >= $headerSize) {
976                         // Seek back to old position
977                         $isValid = ($isValid && $this->seek($seekPosition) === 0);
978                 } else {
979                         // Seek directly behind the header
980                         $isValid = ($isValid && $this->seek($headerSize) === 0);
981                 }
982
983                 // Return result
984                 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-BINARY-FILE: isValid=%d - EXIT!', intval($isValid)));
985                 return $isValid;
986         }
987
988         /**
989          * Reads next "block" of bytes into $currentBlock field. THis method loads
990          * the whole file into memory when the file is just freshly initialized
991          * (only zeros in it).
992          *
993          * @return      void
994          */
995         protected abstract function readNextBlock ();
996
997         /**
998          * Reads the file header
999          *
1000          * @return      void
1001          * @throws      LogicException  If both instances are not set
1002          */
1003         public abstract function readFileHeader ();
1004
1005         /**
1006          * Searches for next suitable gap the given length of data can fit in
1007          * including padding bytes.
1008          *
1009          * @param       $length                 Length of raw data
1010          * @return      $seekPosition   Found next gap's seek position
1011          * @throws      InvalidArgumentException        If the parameter is not valid
1012          */
1013         public function searchNextGap (int $length) {
1014                 // If the file is only gaps, no need to seek
1015                 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-BINARY-FILE: length=%d - CALLED!', $length));
1016                 if ($length <= 0) {
1017                         // Throw IAE
1018                         throw new InvalidArgumentException(sprintf('length=%d is not valid', $length));
1019                 } elseif ($this->isFileGapsOnly()) {
1020                         /*
1021                          * The first empty block is the 2nd one right after the header, so
1022                          * one byte gap to the header.
1023                          */
1024                         $seekPosition = ($this->getHeaderSize() + 2);
1025
1026                         // Return position
1027                         /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-BINARY-FILE: seekPosition=%d - EXIT!', $seekPosition));
1028                         return $seekPosition;
1029                 }
1030
1031                 // @TODO Unfinished
1032                 DebugMiddleware::getSelfInstance()->partialStub('length=' . $length);
1033         }
1034
1035 }