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