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