3 namespace Org\Mxchange\CoreFramework\Stack\File;
5 // Import framework stuff
6 use Org\Mxchange\CoreFramework\Factory\Stack\File\FileStackIndexFactory;
7 use Org\Mxchange\CoreFramework\Factory\Object\ObjectFactory;
8 use Org\Mxchange\CoreFramework\Filesystem\File\BaseBinaryFile;
9 use Org\Mxchange\CoreFramework\Generic\UnsupportedOperationException;
10 use Org\Mxchange\CoreFramework\Iterator\Filesystem\SeekableWritableFileIterator;
11 use Org\Mxchange\CoreFramework\Stack\BaseStacker;
12 use Org\Mxchange\CoreFramework\Stack\File\InvalidMagicException;
13 use Org\Mxchange\CoreFramework\Stack\File\StackableFile;
14 use Org\Mxchange\CoreFramework\Traits\Index\IndexableTrait;
15 use Org\Mxchange\CoreFramework\Traits\Iterator\IteratorTrait;
16 use Org\Mxchange\CoreFramework\Utils\String\StringUtils;
19 use \InvalidArgumentException;
21 use \UnexpectedValueException;
24 * A general file-based stack class
26 * @author Roland Haeder <webmaster@ship-simu.org>
28 * @copyright Copyright (c) 2007, 2008 Roland Haeder, 2009 - 2020 Core Developer Team
29 * @license GNU GPL 3.0 or any newer version
30 * @link http://www.ship-simu.org
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.
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.
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/>.
45 abstract class BaseFileStack extends BaseStacker implements StackableFile {
51 const EXCEPTION_BAD_MAGIC = 0xe100;
54 * Minimum block length
56 private static $minimumBlockLength = 0;
59 * Protected constructor
61 * @param $className Name of the class
64 protected function __construct (string $className) {
65 // Call parent constructor
66 parent::__construct($className);
70 * Reads the file header
73 * @todo To hard assertions here, better rewrite them to exceptions
74 * @throws UnexpectedValueException If header is not proper length
75 * @throws InvalidMagicException If a bad magic was found
77 public function readFileHeader () {
78 // First rewind to beginning as the header sits at the beginning ...
79 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('BASE-FILE-STACK: CALLED!');
80 $this->getIteratorInstance()->rewind();
82 // Then read it (see constructor for calculation)
83 $data = $this->getIteratorInstance()->read($this->getIteratorInstance()->getHeaderSize());
85 // Have all requested bytes been read?
86 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-FILE-STACK: Read %d bytes (%d wanted).', strlen($data), $this->getIteratorInstance()->getHeaderSize()));
87 if (strlen($data) != $this->getIteratorInstance()->getHeaderSize()) {
89 throw new UnexpectedValueException(sprintf('data(%d)=%s does not match iteratorInstance->headerSize=%d',
92 $this->getIteratorInstance()->getHeaderSize()
94 } elseif (empty(trim($data, chr(0)))) {
95 // Empty header, file is freshly pre-allocated
96 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('BASE-FILE-STACK: Empty file header detected - EXIT!');
100 // Last character must be the separator
101 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-FILE-STACK: data(-1)=%s', dechex(ord(substr($data, -1, 1)))));
102 if (substr($data, -1, 1) !== chr(BaseBinaryFile::SEPARATOR_HEADER_ENTRIES)) {
103 // Not valid separator
104 throw new UnexpectedValueException(sprintf('data=%s does not have separator=%s at the end.',
106 BaseBinaryFile::SEPARATOR_HEADER_ENTRIES
110 // Okay, then remove it
111 $data = substr($data, 0, -1);
113 // And update seek position
114 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('BASE-FILE-STACK: Calling this->iteratorInstance->updateSeekPosition() ...');
115 $this->getIteratorInstance()->updateSeekPosition();
122 * 2 => current seek position
124 $header = explode(chr(BaseBinaryFile::SEPARATOR_HEADER_DATA), $data);
126 // Check if the array has only 3 elements
127 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-FILE-STACK: header(%d)=%s', count($header), print_r($header, true)));
128 if (count($header) != 3) {
129 // Header array count is not expected
130 throw new UnexpectedValueException(sprintf('data=%s has %d elements, expected 3',
134 } elseif ($header[0] != StackableFile::STACK_MAGIC) {
136 throw new InvalidMagicException($data, self::EXCEPTION_BAD_MAGIC);
139 // Check length of count and seek position
140 if (strlen($header[1]) != BaseBinaryFile::LENGTH_COUNT) {
141 // Count length not valid
142 throw new UnexpectedValueException(sprintf('header[1](%d)=%s is not expected %d length',
145 BaseBinaryFile::LENGTH_COUNT
147 } elseif (strlen($header[1]) != BaseBinaryFile::LENGTH_POSITION) {
148 // Position length not valid
149 throw new UnexpectedValueException(sprintf('header[2](%d)=%s is not expected %d length',
152 BaseBinaryFile::LENGTH_POSITION
156 // Decode count and seek position
157 $header[1] = hex2bin($header[1]);
158 $header[2] = hex2bin($header[2]);
161 $this->getIteratorInstance()->setHeader($header);
164 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('BASE-FILE-STACK: EXIT!');
168 * Flushes the file header
172 public function flushFileHeader () {
173 // Put all informations together
174 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('BASE-FILE-STACK: CALLED!');
175 $header = sprintf('%s%s%s%s%s%s',
177 StackableFile::STACK_MAGIC,
179 // Separator magic<->count
180 chr(BaseBinaryFile::SEPARATOR_HEADER_DATA),
182 // Padded total entries
183 str_pad(StringUtils::dec2hex($this->getIteratorInstance()->getCounter()), BaseBinaryFile::LENGTH_COUNT, '0', STR_PAD_LEFT),
185 // Separator count<->seek position
186 chr(BaseBinaryFile::SEPARATOR_HEADER_DATA),
188 // Padded seek position
189 str_pad(StringUtils::dec2hex($this->getIteratorInstance()->getSeekPosition(), 2), BaseBinaryFile::LENGTH_POSITION, '0', STR_PAD_LEFT),
191 // Separator position<->entries
192 chr(BaseBinaryFile::SEPARATOR_HEADER_ENTRIES)
195 // Write it to disk (header is always at seek position 0)
196 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-FILE-STACK: Calling this->iteratorInstance->writeAtPosition(0, header=%s) ...', $header));
197 $this->getIteratorInstance()->writeAtPosition(0, $header);
200 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('BASE-FILE-STACK: EXIT!');
204 * Initializes this file-based stack.
206 * @param $fileInfoInstance An instance of a SplFileInfo class
207 * @param $type Type of this stack (e.g. url_source for URL sources)
209 * @throws InvalidArgumentException If a parameter is invalid
210 * @todo Currently the stack file is not cached, please implement a memory-handling class and if enough RAM is found, cache the whole stack file.
212 protected function initFileStack (SplFileInfo $fileInfoInstance, string $type) {
213 // Validate parameter
214 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-FILE-STACK: fileInfoInstance[%s]=%s,type=%s - CALLED!', get_class($fileInfoInstance), $fileInfoInstance, $type));
217 throw new InvalidArgumentException('Parameter "type" is empty');
220 // Get a stack file instance
221 $stackInstance = ObjectFactory::createObjectByConfiguredName('stack_file_class', array($fileInfoInstance, $this));
223 // Get iterator instance
224 $iteratorInstance = ObjectFactory::createObjectByConfiguredName('file_iterator_class', array($stackInstance));
227 $this->setIteratorInstance($iteratorInstance);
229 // Calculate header size
231 strlen(StackableFile::STACK_MAGIC) +
232 strlen(chr(BaseBinaryFile::SEPARATOR_HEADER_DATA)) +
233 BaseBinaryFile::LENGTH_COUNT +
234 strlen(chr(BaseBinaryFile::SEPARATOR_HEADER_DATA)) +
235 BaseBinaryFile::LENGTH_POSITION +
236 strlen(chr(BaseBinaryFile::SEPARATOR_HEADER_ENTRIES))
240 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-FILE-STACK: Setting headerSize=%d ...', $headerSize));
241 $this->getIteratorInstance()->setHeaderSize($headerSize);
243 // Init counters and gaps array
244 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('BASE-FILE-STACK: Calling this->iteratorInstance->initCountersGapsArray() ...');
245 $this->getIteratorInstance()->initCountersGapsArray();
247 // Default is not created/already exists
250 // Is the file's header initialized?
251 if (!$this->getIteratorInstance()->isFileHeaderInitialized()) {
252 // First pre-allocate a bit
253 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('BASE-FILE-STACK: Calling this->iteratorInstance->preAllocateFile(file_stack) ...');
254 $this->getIteratorInstance()->preAllocateFile('file_stack');
256 // Then create file header
257 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('BASE-FILE-STACK: this->iteratorInstance->createFileHeader() ...');
258 $this->getIteratorInstance()->createFileHeader();
264 // Load the file header
265 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('BASE-FILE-STACK: Calling this->readFileHeader() ...');
266 $this->readFileHeader();
268 // Not created/already exists?
269 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-FILE-STACK: created=%d', intval($created)));
271 // Count all entries in file
272 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('BASE-FILE-STACK: Calling this->iteratorInstance->analyzeFileStructure() ...');
273 $this->getIteratorInstance()->analyzeFileStructure();
277 * Get stack index instance. This can be used for faster
278 * "defragmentation" and startup.
280 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-FILE-STACK: fileInfoInstance[%s]=%s,type=%s', get_class($fileInfoInstance), $fileInfoInstance, $type));
281 $indexInstance = FileStackIndexFactory::createFileStackIndexInstance($fileInfoInstance, $type);
284 $this->setIndexInstance($indexInstance);
287 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('BASE-FILE-STACK: EXIT!');
291 * Adds a value to given stack
293 * @param $stackerName Name of the stack
294 * @param $value Value to add to this stacker
296 * @throws FullStackerException If the stack is full
297 * @throws InvalidArgumentException If a parameter is not valid
298 * @throws InvalidArgumentException Not all variable types are wanted here
300 protected function addValueToStack (string $stackerName, $value) {
301 // Validate parameter
302 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-FILE-STACK: stackerName=%s,value[%s]=%s - CALLED!', $stackerName, gettype($value), print_r($value, true)));
303 if (empty($stackerName)) {
304 // No empty stack name
305 throw new InvalidArgumentException('Parameter "stackerName" is empty');
306 } elseif ($this->isStackFull($stackerName)) {
308 throw new FullStackerException([$this, $stackerName, $value], self::EXCEPTION_STACKER_IS_FULL);
309 } elseif (is_resource($value) || is_object($value)) {
311 throw new InvalidArgumentException(sprintf('value[]=%s is not supported', gettype($value)));
315 * Now add the value to the file stack which returns gap position, a
316 * hash and length of the raw data.
318 $data = $this->getIteratorInstance()->writeValueToFile($stackerName, $value);
320 // Add the hash and gap position to the index
321 $this->getIndexInstance()->addHashToIndex($stackerName, $data);
324 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('BASE-FILE-STACK: EXIT!');
328 * Get last value from named stacker
330 * @param $stackerName Name of the stack
331 * @return $value Value of last added value
332 * @throws InvalidArgumentException If a parameter is not valid
333 * @throws BadMethodCallException If the stack is empty
335 protected function getLastValue (string $stackerName) {
336 // Validate parameter
337 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-FILE-STACK: stackerName=%s - CALLED!', $stackerName));
338 if (empty($stackerName)) {
339 // No empty stack name
340 throw new InvalidArgumentException('Parameter "stackerName" is empty');
341 } elseif ($this->isStackEmpty($stackerName)) {
342 // Throw an exception
343 throw new BadMethodCallException([$this, $stackerName], self::EXCEPTION_STACKER_IS_EMPTY);
346 // Now get the last value
347 /* NOISY-DEBUG: */ $this->partialStub('[' . __METHOD__ . ':' . __LINE__ . '] stackerName=' . $stackerName);
351 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-FILE-STACK: value[]=%s - EXIT!', gettype($value)));
356 * Get first value from named stacker
358 * @param $stackerName Name of the stack
359 * @return $value Value of last added value
360 * @throws InvalidArgumentException If a parameter is not valid
361 * @throws BadMethodCallException If the stack is empty
363 protected function getFirstValue (string $stackerName) {
364 // Validate parameter
365 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-FILE-STACK: stackerName=%s - CALLED!', $stackerName));
366 if (empty($stackerName)) {
367 // No empty stack name
368 throw new InvalidArgumentException('Parameter "stackerName" is empty');
369 } elseif ($this->isStackEmpty($stackerName)) {
370 // Throw an exception
371 throw new BadMethodCallException([$this, $stackerName], self::EXCEPTION_STACKER_IS_EMPTY);
374 // Now get the first value
375 /* NOISY-DEBUG: */ $this->partialStub('[' . __METHOD__ . ':' . __LINE__ . '] stackerName=' . $stackerName);
379 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-FILE-STACK: value[]=%s - EXIT!', gettype($value)));
384 * "Pops" last entry from stack
386 * @param $stackerName Name of the stack
387 * @return $value Value "poped" from array
388 * @throws InvalidArgumentException If a parameter is not valid
389 * @throws BadMethodCallException If the stack is empty
391 protected function popLast (string $stackerName) {
392 // Validate parameter
393 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-FILE-STACK: stackerName=%s - CALLED!', $stackerName));
394 if (empty($stackerName)) {
395 // No empty stack name
396 throw new InvalidArgumentException('Parameter "stackerName" is empty');
397 } elseif ($this->isStackEmpty($stackerName)) {
398 // Throw an exception
399 throw new BadMethodCallException([$this, $stackerName], self::EXCEPTION_STACKER_IS_EMPTY);
402 // Now, remove the last entry, we don't care about the return value here, see elseif() block above
403 /* NOISY-DEBUG: */ $this->partialStub('[' . __METHOD__ . ':' . __LINE__ . '] stackerName=' . $stackerName);
408 * "Pops" first entry from stack
410 * @param $stackerName Name of the stack
411 * @return $value Value "shifted" from array
412 * @throws InvalidArgumentException If a parameter is not valid
413 * @throws BadMethodCallException If the named stacker is empty
415 protected function popFirst (string $stackerName) {
416 // Validate parameter
417 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-FILE-STACK: stackerName=%s - CALLED!', $stackerName));
418 if (empty($stackerName)) {
419 // No empty stack name
420 throw new InvalidArgumentException('Parameter "stackerName" is empty');
421 } elseif ($this->isStackEmpty($stackerName)) {
422 // Throw an exception
423 throw new BadMethodCallException([$this, $stackerName], self::EXCEPTION_STACKER_IS_EMPTY);
426 // Now, remove the last entry, we don't care about the return value here, see elseif() block above
427 /* NOISY-DEBUG: */ $this->partialStub('[' . __METHOD__ . ':' . __LINE__ . '] stackerName=' . $stackerName);
432 * Checks whether the given stack is full
434 * @param $stackerName Name of the stack
435 * @return $isFull Whether the stack is full
436 * @throws InvalidArgumentException If a parameter is not valid
438 protected function isStackFull (string $stackerName) {
439 // Validate parameter
440 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-FILE-STACK: stackerName=%s - CALLED!', $stackerName));
441 if (empty($stackerName)) {
442 // No empty stack name
443 throw new InvalidArgumentException('Parameter "stackerName" is empty');
446 // @TODO Please implement this, returning false
447 /* NOISY-DEBUG: */ $this->partialStub('[' . __METHOD__ . ':' . __LINE__ . '] stackerName=' . $stackerName);
451 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-FILE-STACK: isFull=%d - EXIT!', intval($isFull)));
456 * Checks whether the given stack is empty
458 * @param $stackerName Name of the stack
459 * @return $isEmpty Whether the stack is empty
460 * @throws InvalidArgumentException If a parameter is not valid
461 * @throws BadMethodCallException If given stack is missing
463 public function isStackEmpty (string $stackerName) {
464 // Validate parameter
465 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-FILE-STACK: stackerName=%s - CALLED!', $stackerName));
466 if (empty($stackerName)) {
467 // No empty stack name
468 throw new InvalidArgumentException('Parameter "stackerName" is empty');
471 // So, is the stack empty?
472 $isEmpty = (($this->getStackCount($stackerName)) == 0);
475 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-FILE-STACK: isEmpty=%d - EXIT!', intval($isEmpty)));
480 * Initializes given stacker
482 * @param $stackerName Name of the stack
483 * @param $forceReInit Force re-initialization
485 * @throws UnsupportedOperationException This method is not (and maybe never will be) supported
487 public function initStack (string $stackerName, bool $forceReInit = false) {
488 throw new UnsupportedOperationException(array($this, __FUNCTION__, $this->getIteratorInstance()->getPointerInstance()), self::EXCEPTION_UNSPPORTED_OPERATION);
492 * Initializes all stacks
495 * @throws UnsupportedOperationException This method is not (and maybe never will be) supported
497 public function initStacks (array $stacks, bool $forceReInit = false) {
498 throw new UnsupportedOperationException(array($this, __FUNCTION__, $this->getIteratorInstance()->getPointerInstance()), self::EXCEPTION_UNSPPORTED_OPERATION);
502 * Checks whether the given stack is initialized (set in array $stackers)
504 * @param $stackerName Name of the stack
505 * @return $isInitialized Whether the stack is initialized
506 * @throws UnsupportedOperationException This method is not (and maybe never will be) supported
508 public function isStackInitialized (string $stackerName) {
509 throw new UnsupportedOperationException(array($this, __FUNCTION__, $this->getIteratorInstance()->getPointerInstance()), self::EXCEPTION_UNSPPORTED_OPERATION);
513 * Determines whether the EOF has been reached
515 * @return $isEndOfFileReached Whether the EOF has been reached
516 * @throws UnsupportedOperationException This method is not (and maybe never will be) supported
518 public function isEndOfFileReached () {
519 throw new UnsupportedOperationException(array($this, __FUNCTION__, $this->getIteratorInstance()->getPointerInstance()), self::EXCEPTION_UNSPPORTED_OPERATION);
523 * Getter for size of given stack (array count)
525 * @param $stackerName Name of the stack
526 * @return $count Size of stack (array count)
528 public function getStackCount (string $stackerName) {
529 // Now, simply return the found count value, this must be up-to-date then!
530 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-FILE-STACK: stackerName=%s - CALLED!', $stackerName));
531 $count = $this->getIteratorInstance()->getCounter();
534 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-FILE-STACK: count=%d - EXIT!', $count));
539 * Calculates minimum length for one entry/block
541 * @return $length Minimum length for one entry/block
543 public function calculateMinimumBlockLength () {
544 // Is the value "cached"?
545 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('BASE-FILE-STACK: CALLED!');
546 if (self::$minimumBlockLength == 0) {
548 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('BASE-FILE-STACK: Calculating ...');
549 self::$minimumBlockLength =
550 // Length of entry group
551 BaseBinaryFile::LENGTH_GROUP + strlen(chr(BaseBinaryFile::SEPARATOR_GROUP_HASH)) +
553 self::getHashLength() + strlen(chr(BaseBinaryFile::SEPARATOR_HASH_VALUE)) + 1 +
555 strlen(chr(BaseBinaryFile::SEPARATOR_ENTRIES));
559 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-FILE-STACK: self::minimumBlockLength=%d - EXIT!', self::$minimumBlockLength));
560 return self::$minimumBlockLength;
564 * Initializes counter for valid entries, arrays for damaged entries and
565 * an array for gap seek positions. If you call this method on your own,
566 * please re-analyze the file structure. So you are better to call
567 * analyzeFileStructure() instead of this method.
570 * @throws UnsupportedOperationException This method is not (and maybe never will be) supported
572 public function initCountersGapsArray () {
573 throw new UnsupportedOperationException(array($this, __FUNCTION__, $this->getIteratorInstance()->getPointerInstance()), self::EXCEPTION_UNSPPORTED_OPERATION);
577 * Getter for header size
579 * @return $totalEntries Size of file header
580 * @throws UnsupportedOperationException This method is not (and maybe never will be) supported
582 public final function getHeaderSize () {
583 throw new UnsupportedOperationException(array($this, __FUNCTION__, $this->getIteratorInstance()->getPointerInstance()), self::EXCEPTION_UNSPPORTED_OPERATION);
587 * Setter for header size
589 * @param $headerSize Size of file header
591 * @throws UnsupportedOperationException This method is not (and maybe never will be) supported
593 public final function setHeaderSize (int $headerSize) {
594 throw new UnsupportedOperationException(array($this, __FUNCTION__, $this->getIteratorInstance()->getPointerInstance()), self::EXCEPTION_UNSPPORTED_OPERATION);
598 * Getter for header array
600 * @return $totalEntries Size of file header
601 * @throws UnsupportedOperationException This method is not (and maybe never will be) supported
603 public final function getHeader () {
604 throw new UnsupportedOperationException(array($this, __FUNCTION__, $this->getIteratorInstance()->getPointerInstance()), self::EXCEPTION_UNSPPORTED_OPERATION);
610 * @param $header Array for a file header
612 * @throws UnsupportedOperationException This method is not (and maybe never will be) supported
614 public final function setHeader (array $header) {
615 throw new UnsupportedOperationException(array($this, __FUNCTION__, $this->getIteratorInstance()->getPointerInstance()), self::EXCEPTION_UNSPPORTED_OPERATION);
619 * Updates seekPosition attribute from file to avoid to much access on file.
622 * @throws UnsupportedOperationException This method is not (and maybe never will be) supported
624 public function updateSeekPosition () {
625 throw new UnsupportedOperationException(array($this, __FUNCTION__, $this->getIteratorInstance()->getPointerInstance()), self::EXCEPTION_UNSPPORTED_OPERATION);
629 * Getter for total entries
631 * @return $totalEntries Total entries in this file
632 * @throws UnsupportedOperationException This method is not (and maybe never will be) supported
634 public final function getCounter () {
635 throw new UnsupportedOperationException(array($this, __FUNCTION__, $this->getIteratorInstance()->getPointerInstance()), self::EXCEPTION_UNSPPORTED_OPERATION);
639 * Writes data at given position
641 * @param $seekPosition Seek position
642 * @param $data Data to be written
643 * @param $flushHeader Whether to flush the header (default: flush)
645 * @throws UnsupportedOperationException This method is not (and maybe never will be) supported
647 public function writeData (int $seekPosition, string $data, bool $flushHeader = true) {
649 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-FILE-STACK: seekPosition=%s,data[]=%s,flushHeader=%d', $seekPosition, gettype($data), intval($flushHeader)));
650 throw new UnsupportedOperationException(array($this, __FUNCTION__, $this->getIteratorInstance()->getPointerInstance()), self::EXCEPTION_UNSPPORTED_OPERATION);
654 * Writes at given position by seeking to it.
656 * @param $seekPosition Seek position in file
657 * @param $dataStream Data to be written
658 * @return mixed Number of writes bytes or false on error
659 * @throws UnsupportedOperationException This method is not (and maybe never will be) supported
661 public function writeAtPosition (int $seekPosition, string $dataStream) {
663 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-FILE-STACK: seekPosition=%d,dataStream(%d)=%s - CALLED!', $seekPosition, strlen($dataStream), $dataStream));
664 throw new UnsupportedOperationException(array($this, __FUNCTION__, $this->getIteratorInstance()->getPointerInstance()), self::EXCEPTION_UNSPPORTED_OPERATION);
668 * Writes given value to the file and returns a hash and gap position for it
670 * @param $groupId Group identifier
671 * @param $value Value to be added to the stack
672 * @return $data Hash and gap position
673 * @throws UnsupportedOperationException This method is not (and maybe never will be) supported
675 public function writeValueToFile (string $groupId, $value) {
676 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-FILE-STACK: groupId=%s,value[%s]=%s', $groupId, gettype($value), print_r($value, true)));
677 throw new UnsupportedOperationException(array($this, __FUNCTION__, $this->getIteratorInstance()->getPointerInstance()), self::EXCEPTION_UNSPPORTED_OPERATION);
681 * Searches for next suitable gap the given length of data can fit in
682 * including padding bytes.
684 * @param $length Length of raw data
685 * @return $seekPosition Found next gap's seek position
686 * @throws UnsupportedOperationException This method is not (and maybe never will be) supported
688 public function searchNextGap (int $length) {
689 // Not supported here
690 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-FILE-STACK: length=%d - CALLED!', $length));
691 throw new UnsupportedOperationException(array($this, __FUNCTION__, $this->getIteratorInstance()->getPointerInstance()), self::EXCEPTION_UNSPPORTED_OPERATION);
695 * "Getter" for file size
697 * @return $fileSize Size of currently loaded file
699 public function getFileSize () {
700 // Call iterator's method
701 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('BASE-FILE-STACK: CALLED!');
702 $size = $this->getIteratorInstance()->getFileSize();
705 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-FILE-STACK: size=%d - EXIT!', $size));
710 * Writes given raw data to the file and returns a gap position and length
712 * @param $groupId Group identifier
713 * @param $hash Hash from encoded value
714 * @param $encoded Encoded value to be written to the file
715 * @return $data Gap position and length of the raw data
717 public function writeDataToFreeGap (string $groupId, string $hash, string $encoded) {
718 // Raw data been written to the file
719 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-FILE-STACK: groupId=%s,hash=%s,encoded()=%d - CALLED!', $groupId, $hash, strlen($encoded)));
720 $rawData = sprintf('%s%s%s%s%s',
722 BaseBinaryFile::SEPARATOR_GROUP_HASH,
724 BaseBinaryFile::SEPARATOR_HASH_VALUE,
728 // Search for next free gap
729 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-FILE-STACK: groupId=%s,hash=%s,rawData()=%d', $groupId, $hash, strlen($rawData)));
730 $gapPosition = $this->getIteratorInstance()->searchNextGap(strlen($rawData));
732 // Gap position cannot be smaller than header length + 1
733 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-FILE-STACK: gapPosition=%d', $gapPosition));
734 if ($gapPosition <= $this->getIteratorInstance()->getHeaderSize()) {
735 // Improper gap position
736 throw new UnexpectedValueException(sprintf('gapPosition[%s]=%d is not larger than headerSize=%d',
737 gettype($gapPosition),
739 $this->getIteratorInstance()->getHeaderSize()
743 // Then write the data at that gap
744 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-FILE-STACK: groupId=%s,hash=%s,gapPosition=%s', $groupId, $hash, $gapPosition));
745 $this->getIteratorInstance()->writeData($gapPosition, $rawData);
747 // Return gap position, hash and length of raw data
748 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-FILE-STACK: groupId=%s,hash=%s,rawData()=%d - EXIT!', $groupId, $hash, strlen($rawData)));
750 self::ARRAY_NAME_GAP_POSITION => $gapPosition,
751 self::ARRAY_NAME_HASH => $hash,
752 self::ARRAY_NAME_DATA_LENGTH => strlen($rawData),