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