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