]> git.mxchange.org Git - core.git/blob - framework/main/classes/file_directories/binary/class_BaseBinaryFile.php
95c642eb066f72b4330566218d486794b351fdea
[core.git] / framework / main / classes / file_directories / binary / class_BaseBinaryFile.php
1 <?php
2 // Own namespace
3 namespace Org\Mxchange\CoreFramework\Filesystem\File;
4
5 // Import framework stuff
6 use Org\Mxchange\CoreFramework\Bootstrap\FrameworkBootstrap;
7 use Org\Mxchange\CoreFramework\EntryPoint\ApplicationEntryPoint;
8 use Org\Mxchange\CoreFramework\Factory\Object\ObjectFactory;
9 use Org\Mxchange\CoreFramework\Filesystem\File\BaseAbstractFile;
10 use Org\Mxchange\CoreFramework\Index\Indexable;
11 use Org\Mxchange\CoreFramework\Stack\File\StackableFile;
12 use Org\Mxchange\CoreFramework\Traits\Index\IndexableTrait;
13 use Org\Mxchange\CoreFramework\Traits\Stack\StackableTrait;
14
15 // Import SPL stuff
16 use \BadMethodCallException;
17 use \InvalidArgumentException;
18 use \LogicException;
19 use \OutOfBoundsException;
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 IndexableTrait;
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      OutOfBoundsException    If the position is not seekable
504          * @throws      InvalidArgumentException        If a parameter is invalid
505          */
506         public function writeData (int $seekPosition, string $data, bool $flushHeader = true) {
507                 // Validate parameter
508                 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-BINARY-FILE: seekPosition=%s,data()=%d,flushHeader=%d - CALLED!', $seekPosition, strlen($data), intval($flushHeader)));
509                 if ($seekPosition < 0) {
510                         // Invalid seek position
511                         throw new OutOfBoundsException(sprintf('seekPosition=%d is not valid', $seekPosition));
512                 } elseif (empty($data)) {
513                         // Empty data is invalid, too
514                         throw new InvalidArgumentException('Parameter "data" is empty');
515                 }
516
517                 // Write data at given position
518                 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-BINARY-FILE: Calling this->writeAtPosition(%d,%s) ...', $seekPosition, $data));
519                 $this->writeAtPosition($seekPosition, $data);
520
521                 // Increment counter
522                 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('BASE-BINARY-FILE: Calling this->incrementCounter() ...');
523                 $this->incrementCounter();
524
525                 // Update seek position
526                 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('BASE-BINARY-FILE: Calling this->updateSeekPosition() ...');
527                 $this->updateSeekPosition();
528
529                 // Flush the header?
530                 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-BINARY-FILE: flushHeader=%d', intval($flushHeader)));
531                 if ($flushHeader === true) {
532                         // Flush header
533                         /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('BASE-BINARY-FILE: Calling this->flushFileHeader() ...');
534                         $this->flushFileHeader();
535
536                         // Seek to old position
537                         /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('BASE-BINARY-FILE: Calling this->seekToOldPosition() ...');
538                         $this->seekToOldPosition();
539                 }
540
541                 // Trace message
542                 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('BASE-BINARY-FILE: EXIT!');
543         }
544
545         /**
546          * Writes at given position by seeking to it.
547          *
548          * @param       $seekPosition   Seek position in file
549          * @param       $dataStream             Data to be written
550          * @return      mixed                   Number of writes bytes or false on error
551          * @throws      OutOfBoundsException    If the position is not seekable
552          * @throws      InvalidArgumentException        If a parameter is not valid
553          */
554         public function writeAtPosition (int $seekPosition, string $dataStream) {
555                 // Validate parameter
556                 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-BINARY-FILE: seekPosition=%d,dataStream(%d)=%s - CALLED!', $seekPosition, strlen($dataStream), $dataStream));
557                 if ($seekPosition < 0) {
558                         // Invalid seek position
559                         throw new OutOfBoundsException(sprintf('seekPosition=%d is not valid.', $seekPosition));
560                 } elseif (empty($dataStream)) {
561                         // Empty dataStream
562                         throw new InvalidArgumentException('Parameter "dataStream" is empty');
563                 }
564
565                 // Call pointer's method
566                 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-BINARY-FILE: Calling this->pointerInstance->writeAtPosition(%d, %s) ...', $seekPosition, $dataStream));
567                 $status = $this->getPointerInstance()->writeAtPosition($seekPosition, $dataStream);
568
569                 // Return status
570                 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-BINARY-FILE: status[%s]=%d - EXIT!', gettype($status), $status));
571                 return $status;
572         }
573
574         /**
575          * Checks whether the file header is initialized
576          *
577          * @return      $isInitialized  Whether the file header is initialized
578          */
579         public function isFileHeaderInitialized () {
580                 // Default is not initialized
581                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('BASE-BINARY-FILE: CALLED!');
582                 $isInitialized = false;
583
584                 // Is the file initialized?
585                 if ($this->isFileInitialized()) {
586                         // Some bytes has been written, so rewind to start of it.
587                         $this->rewind();
588
589                         // Read file header
590                         //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('BASE-BINARY-FILE: Calling this->readFileHeader() ...');
591                         $this->readFileHeader();
592
593                         // Get header count
594                         $headerCount = count($this->getHeader());
595
596                         // The above method does already check the header
597                         //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-BINARY-FILE: headerCount=%d', $headerCount));
598                         $isInitialized = ($headerCount > 0);
599                 }
600
601                 // Return result
602                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-BINARY-FILE: isInitialized=%d - EXIT!', intval($isInitialized)));
603                 return $isInitialized;
604         }
605
606         /**
607          * Checks whether the assigned file has been initialized
608          *
609          * @return      $isInitialized          Whether the file's size is zero
610          * @throws      UnexpectedValueException        If an unexpected value was returned
611          */
612         public function isFileInitialized () {
613                 // Get it from iterator which holds the pointer instance. If false is returned
614                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('BASE-BINARY-FILE: CALLED!');
615                 $fileSize = $this->size();
616
617                 /*
618                  * The returned file size should not be false or NULL as this means
619                  * that the pointer class does not work correctly.
620                  */
621                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-BINARY-FILE: fileSize[%s]=%d', gettype($fileSize), $fileSize));
622                 if (!is_int($fileSize)) {
623                         // Bad file?
624                         throw new UnexpectedValueException(sprintf('fileSize[]=%s is unexpected', gettype($fileSize)));
625                 }
626
627                 // Is more than 0 returned?
628                 $isInitialized = ($fileSize > 0);
629
630                 // Return result
631                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-BINARY-FILE: isInitialized=%d - EXIT!', intval($isInitialized)));
632                 return $isInitialized;
633         }
634
635         /**
636          * Creates the assigned file
637          *
638          * @return      void
639          * @throws      BadMethodCallException  If this file's header is already initialized
640          */
641         public function createFileHeader () {
642                 // The file's header should not be initialized here
643                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('BASE-BINARY-FILE: CALLED!');
644                 if ($this->isFileHeaderInitialized()) {
645                         // Bad method call
646                         //* DEBUG-DIE: */ ApplicationEntryPoint::exitApplication(sprintf('[%s:%d]: this=%s', __METHOD__, __LINE__, print_r($this, TRUE)));
647                         throw new BadMethodCallException('File header is already initialized but method called');
648                 }
649
650                 // Simple flush file header which will create it.
651                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('BASE-BINARY-FILE: Calling this->flushFileHeader() ...');
652                 $this->flushFileHeader();
653
654                 // Rewind seek position (to beginning of file) and update/flush file header
655                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('BASE-BINARY-FILE: Calling this->rewindUpdateSeekPosition() ...');
656                 $this->rewindUpdateSeekPosition();
657
658                 // Trace message
659                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('BASE-BINARY-FILE: EXIT!');
660         }
661
662         /**
663          * Pre-allocates file (if enabled) with some space for later faster write access.
664          *
665          * @param       $type   Type of the file
666          * @return      void
667          * @throws      InvalidArgumentException        If a parameter is empty
668          */
669         public function preAllocateFile (string $type) {
670                 // Is it enabled?
671                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-BINARY-FILE: type=%s - CALLED!', $type));
672                 if (empty($type)) {
673                         // Empty type
674                         throw new InvalidArgumentException('Parameter "type" is empty');
675                 } elseif (FrameworkBootstrap::getConfigurationInstance()->getConfigEntry($type . '_pre_allocate_enabled') != 'Y') {
676                         // Don't continue here.
677                         self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-BINARY-FILE: Not pre-allocating file.'));
678                         return;
679                 }
680
681                 // Message to user
682                 self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('BASE-BINARY-FILE: Pre-allocating file ...');
683
684                 // Calculate minimum length for one entry and get file size
685                 $minimumBlockLength = $this->getIndexInstance()->calculateMinimumBlockLength();
686                 $fileSize = $this->getFileSize();
687
688                 // Calulcate seek position
689                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-BINARY-FILE: minimumBlockLength=%d,fileSize=%d', $minimumBlockLength, $fileSize));
690                 $seekPosition = $minimumBlockLength * FrameworkBootstrap::getConfigurationInstance()->getConfigEntry($type . '_pre_allocate_count') + $fileSize ;
691
692                 // Now simply write a NUL there. This will pre-allocate the file.
693                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-BINARY-FILE: Calling this->writeAtPosition(%d,NUL) ...', $seekPosition));
694                 $this->writeAtPosition($seekPosition, chr(0));
695
696                 // Is the seek position zero?
697                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-BINARY-FILE: fileSize=%d', $fileSize));
698                 if ($fileSize == 0) {
699                         // Mark file as gaps-only
700                         //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-BINARY-FILE: Calling this->markGapsOnly(%s,%d) ...', $type, $minimumBlockLength));
701                         $this->markFileGapsOnly($type, $minimumBlockLength);
702                 } else {
703                         // Analyze file structure
704                         //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('BASE-BINARY-FILE: Calling this->analyzeFileStructure() ...');
705                         $this->analyzeFileStructure();
706                 }
707
708                 // Rewind seek position
709                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('BASE-BINARY-FILE: Calling this->rewind() ...');
710                 $this->rewind();
711
712                 // Trace message
713                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('BASE-BINARY-FILE: EXIT!');
714         }
715
716         /**
717          * Determines seek position
718          *
719          * @return      $seekPosition   Current seek position
720          */
721         public function determineSeekPosition () {
722                 // Call pointer instance
723                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('BASE-BINARY-FILE: CALLED!');
724                 $seekPosition = $this->getPointerInstance()->determineSeekPosition();
725
726                 // Return position
727                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-BINARY-FILE: seekPosition=%d - EXIT!', $seekPosition));
728                 return $seekPosition;
729         }
730
731         /**
732          * Seek to given offset (default) or other possibilities as fseek() gives.
733          *
734          * @param       $offset         Offset to seek to (or used as "base" for other seeks)
735          * @param       $whence         Added to offset (default: only use offset to seek to)
736          * @return      $status         Status of file seek: 0 = success, -1 = failed
737          * @throws      OutOfBoundsException    If the position is not seekable
738          */
739         public function seek (int $offset, int $whence = SEEK_SET) {
740                 // Validate parameter
741                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-BINARY-FILE: offset=%d,whence=%d - CALLED!', $offset, $whence));
742                 if ($offset < 0) {
743                         // No offset is smaller than zero
744                         throw new OutOfBoundsException(sprintf('offset=%d is not valid', $offset));
745                 }
746
747                 // Call pointer instance
748                 $status = $this->getPointerInstance()->seek($offset, $whence);
749
750                 // Return status
751                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-BINARY-FILE: status[%s]=%d - EXIT!', gettype($status), $status));
752                 return $status;
753         }
754
755         /**
756          * Reads given amount of bytes from file.
757          *
758          * @param       $bytes  Amount of bytes to read
759          * @return      $data   Data read from file
760          * @throws      OutOfBoundsException    If the position is not seekable
761          */
762         public function read (int $bytes = 0) {
763                 // Validate parameter
764                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-BINARY-FILE: bytes=%d - CALLED!', $bytes));
765                 if ($bytes < 0) {
766                         // Throw exception
767                         throw new OutOfBoundsException(sprintf('bytes=%d is not valid', $bytes));
768                 }
769
770                 // Call pointer instance
771                 $data = $this->getPointerInstance()->read($bytes);
772
773                 // Return data
774                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-BINARY-FILE: data[%s]=%s - EXIT!', gettype($data), $data));
775                 return $data;
776         }
777
778         /**
779          * Rewinds to the beginning of the file
780          *
781          * @return      void
782          */
783         public function rewind () {
784                 // Call pointer instance
785                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('BASE-BINARY-FILE: CALLED!');
786                 $this->getPointerInstance()->rewind();
787
788                 // Trace message
789                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('BASE-BINARY-FILE: EXIT!');
790         }
791
792         /**
793          * Analyzes entries in index file. This will count all found (and valid)
794          * entries, mark invalid as damaged and count gaps ("fragmentation"). If
795          * only gaps are found, the file is considered as "virgin" (no entries).
796          *
797          * @return      void
798          * @throws      BadMethodCallException  If this method is called but file is not initialized
799          */
800         public function analyzeFileStructure () {
801                 // Make sure the file is initialized
802                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('BASE-BINARY-FILE: CALLED!');
803                 if (!$this->isFileInitialized()) {
804                         // Bad method call
805                         throw new BadMethodCallException('Method called but file is not initialized.');
806                 }
807
808                 // Init counters and gaps array
809                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('BASE-BINARY-FILE: Calling this->initCounterGapsArrays() ...');
810                 $this->initCountersGapsArray();
811
812                 // Output message (as this may take some time)
813                 self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('Analyzing file structure ... (this may take some time)'));
814
815                 // First rewind to the begining
816                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('BASE-BINARY-FILE: Calling this->rewind() ...');
817                 $this->rewind();
818
819                 // Then try to load all entries
820                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('BASE-BINARY-FILE: Looping through file');
821                 while ($this->isValid()) {
822                         // Get current entry
823                         $current = $this->getCurrentBlock();
824
825                         // Go to next entry
826                         //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('BASE-BINARY-FILE: Calling this->readNextBlock() ...');
827                         $this->readNextBlock();
828
829                         /*
830                          * If the block is empty, maybe the whole file is? This could mean
831                          * that the file has been pre-allocated.
832                          */
833                         //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-BINARY-FILE: current(%d)[]=%s', strlen($current), gettype($current)));
834                         if (empty($current)) {
835                                 // Then skip this part
836                                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-BINARY-FILE: current[]=%s is empty - CONTINUE!', gettype($current)));
837                                 continue;
838                         }
839
840                         // Handle current record
841                         //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-BINARY-FILE: current(%d)[]=%s', strlen($current), gettype($current)));
842                 }
843
844                 // If the last read block is empty, check gaps
845                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-BINARY-FILE: current()=%d', strlen($current)));
846                 if (empty($current)) {
847                         // Output message
848                         self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-BINARY-FILE: Found a total of %d gaps.', count($this->gaps)));
849
850                         // Check gaps, if the whole file is empty.
851                         //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('BASE-BINARY-FILE: Calling this->isFileGapsOnly() ...');
852                         if ($this->isFileGapsOnly()) {
853                                 // Only gaps, so don't continue here.
854                                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('BASE-BINARY-FILE: File is gaps-only - EXIT!');
855                                 return;
856                         }
857
858                         /*
859                          * The above call has calculated a total size of all gaps. If the
860                          * percentage of gaps passes a "soft" limit and last
861                          * defragmentation is to far in the past, or if a "hard" limit has
862                          * reached, run defragmentation.
863                          */
864                         //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('BASE-BINARY-FILE: Calling this->isDefragmentationNeeded() ...');
865                         if ($this->isDefragmentationNeeded()) {
866                                 // Run "defragmentation"
867                                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('BASE-BINARY-FILE: Calling this->doRunDefragmentation() ...');
868                                 $this->doRunDefragmentation();
869                         }
870                 }
871
872                 // Trace message
873                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('BASE-BINARY-FILE: EXIT!');
874         }
875
876         /**
877          * Reads next "block" of bytes into $currentBlock field
878          *
879          * @return      void
880          * @throws      BadMethodCallException  If this method was called without prior valid() call
881          */
882         public function readNextBlock () {
883                 // Is there nothing to read?
884                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('BASE-BINARY-FILE: CALLED!');
885                 if (!$this->isValid()) {
886                         // Nothing to read
887                         throw new BadMethodCallException('next() invoked but no valid current block (EOF?)');
888                 }
889
890                 // First calculate minimum block length
891                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-BINARY-FILE: this->seekPosition=%d', $this->determineSeekPosition()));
892                 $length = $this->getIndexInstance()->calculateMinimumBlockLength();
893                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-BINARY-FILE: length=%d', $length));
894
895                 // Read possibly back-buffered bytes from previous call of next().
896                 $data = $this->getBackBuffer();
897
898                 /*
899                  * Read until a entry/block separator has been found. The next read
900                  * "block" may not fit, so this loop will continue until the EOB or EOF
901                  * has been reached whatever comes first.
902                  */
903                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-BINARY-FILE: data()=%d', strlen($data)));
904                 while ((!$this->isEndOfFileReached()) && (empty($data) || !self::isBlockSeparatorFound($data))) {
905                         // Then read the next possible block
906                         //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-BINARY-FILE: Calling this->read(%d) ...', $length));
907                         $block = $this->read($length);
908
909                         // Is it all empty?
910                         //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-BINARY-FILE: block()=%d,length=%d', strlen($block), $length));
911                         if (strlen(trim($block, chr(0))) == 0) {
912                                 // Mark this block as empty
913                                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-BINARY-FILE: Calling this->markCurrentBlockAsEmpty(%d) ...', strlen($block)));
914                                 $this->markCurrentBlockAsEmpty(strlen($block));
915
916                                 // Exit look
917                                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('BASE-BINARY-FILE: Empty block found ... - BREAK!');
918                                 break;
919                         }
920
921                         // At this block then
922                         $data .= $block;
923
924                         // A debug message
925                         //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-BINARY-FILE: data(%d)=%s', strlen($data), $data));
926                 }
927
928                 /*
929                  * Init back-buffer which is the data that has been found beyond the
930                  * separator.
931                  */
932                 $this->initBackBuffer();
933
934                 // Is $data empty?
935                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-BINARY-FILE: data(%d)=%s', strlen($data), $data));
936                 if (empty($data)) {
937                         // Yes, maybe whole file was ...
938                         //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('BASE-BINARY-FILE: Maybe empty file found - EXIT!');
939                         return;
940                 }
941
942                 // Separate data
943                 $dataArray = explode(chr(self::SEPARATOR_ENTRIES), $data);
944
945                 // Left part is the actual block, right one the back-buffer data, if found
946                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-BINARY-FILE: dataArray()=%d', count($dataArray)));
947                 //* PRINTR-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-BINARY-FILE: dataArray=%s', print_r($dataArray, true)));
948                 $this->setCurrentBlock($dataArray[0]);
949
950                 // Is back buffere data found?
951                 if (isset($dataArray[1])) {
952                         // Set back buffer
953                         $this->setBackBuffer($dataArray[1]);
954                 }
955
956                 // Trace message
957                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('BASE-BINARY-FILE: EXIT!');
958         }
959
960         /**
961          * Checks wether the current entry is valid (not at the end of the file).
962          * This method will return true if an emptied (nulled) entry has been found.
963          *
964          * @return      $isValid        Whether the next entry is valid
965          * @throws      UnexpectedValueException        If some value is not expected
966          */
967         public function isValid () {
968                 // First calculate minimum block length
969                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('BASE-BINARY-FILE: CALLED!');
970                 $length = $this->getIndexInstance()->calculateMinimumBlockLength();
971
972                 // Short be more than zero!
973                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-BINARY-FILE: length=%d', $length));
974                 if ($length < 1) {
975                         // Throw UVE
976                         throw new UnexpectedValueException(sprintf('length=%d is not expected', $length));
977                 }
978
979                 // Get current seek position
980                 $seekPosition = $this->determineSeekPosition();
981
982                 // Then try to read it
983                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-BINARY-FILE: seekPosition=%d', $seekPosition));
984                 $data = $this->read($length);
985
986                 // If some bytes could be read, all is fine
987                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-BINARY-FILE: data[%s]()=%d', gettype($data), strlen($data)));
988                 $isValid = ((is_string($data)) && (strlen($data) > 0));
989
990                 // Get header size
991                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-BINARY-FILE: isValid=%d', intval($isValid)));
992                 $headerSize = $this->getHeaderSize();
993
994                 // Is the seek position at or beyond the header?
995                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-BINARY-FILE: seekPosition=%d,headerSize=%d', $seekPosition, $headerSize));
996                 if ($seekPosition >= $headerSize) {
997                         // Seek back to old position
998                         $isValid = ($isValid && $this->seek($seekPosition) === 0);
999                 } else {
1000                         // Seek directly behind the header
1001                         $isValid = ($isValid && $this->seek($headerSize) === 0);
1002                 }
1003
1004                 // Return result
1005                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-BINARY-FILE: isValid=%d - EXIT!', intval($isValid)));
1006                 return $isValid;
1007         }
1008
1009         /**
1010          * Reads the file header
1011          *
1012          * @return      void
1013          * @throws      LogicException  If both instances are not set
1014          */
1015         public function readFileHeader () {
1016                 // Is index set or stack?
1017                 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('BASE-BINARY-FILE: CALLED!');
1018                 if ($this->getIndexInstance() instanceof Indexable) {
1019                         // Call index instance
1020                         /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('BASE-BINARY-FILE: Calling this->indexInstance->readIndexHeader() ...');
1021                         $this->getIndexInstance()->readIndexHeader();
1022                 } elseif ($this->getStackInstance() instanceof StackableFile) {
1023                         // Call stacke instance
1024                         /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('BASE-BINARY-FILE: Calling this->stackInstance->readStackHeader() ...');
1025                         $this->getStackInstance()->readStackHeader();
1026                 } else {
1027                         // Bad logic?
1028                         throw new LogicException('Wether indexInstance nor stackInstance are set');
1029                 }
1030
1031                 // Trace message
1032                 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('BASE-BINARY-FILE: EXIT!');
1033         }
1034
1035         /**
1036          * Flushes the file header
1037          *
1038          * @return      void
1039          */
1040         public function flushFileHeader () {
1041                 // Call block instance
1042                 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('BASE-BINARY-FILE: CALLED!');
1043                 $this->getIndexInstance()->flushFileHeader();
1044
1045                 // Trace message
1046                 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('BASE-BINARY-FILE: EXIT!');
1047         }
1048
1049         /**
1050          * Searches for next suitable gap the given length of data can fit in
1051          * including padding bytes.
1052          *
1053          * @param       $length                 Length of raw data
1054          * @return      $seekPosition   Found next gap's seek position
1055          * @throws      InvalidArgumentException        If the parameter is not valid
1056          */
1057         public function searchNextGap (int $length) {
1058                 // If the file is only gaps, no need to seek
1059                 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-BINARY-FILE: length=%d - CALLED!', $length));
1060                 if ($length <= 0) {
1061                         // Throw IAE
1062                         throw new InvalidArgumentException(sprintf('length=%d is not valid', $length));
1063                 } elseif ($this->isFileGapsOnly()) {
1064                         /*
1065                          * The first empty block is the 2nd one right after the header, so
1066                          * one byte gap to the header.
1067                          */
1068                         $seekPosition = ($this->getHeaderSize() + 2);
1069
1070                         // Return position
1071                         /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-BINARY-FILE: seekPosition=%d - EXIT!', $seekPosition));
1072                         return $seekPosition;
1073                 }
1074
1075                 // @TODO Unfinished
1076                 $this->partialStub('length=' . $length);
1077         }
1078
1079 }