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