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