X-Git-Url: https://git.mxchange.org/?p=core.git;a=blobdiff_plain;f=framework%2Fmain%2Fclasses%2Ffile_directories%2Fbinary%2Fclass_BaseBinaryFile.php;h=d7aa398bbfcd133f1a1af7d3bfc9810426407268;hp=238c3930fb4ec99361514a9a2ec22f92369a6fde;hb=HEAD;hpb=78c80d535ab3e108f8c89f2aae9ebe02270e4d63 diff --git a/framework/main/classes/file_directories/binary/class_BaseBinaryFile.php b/framework/main/classes/file_directories/binary/class_BaseBinaryFile.php index 238c3930..d7aa398b 100644 --- a/framework/main/classes/file_directories/binary/class_BaseBinaryFile.php +++ b/framework/main/classes/file_directories/binary/class_BaseBinaryFile.php @@ -4,20 +4,29 @@ namespace Org\Mxchange\CoreFramework\Filesystem\File; // Import framework stuff use Org\Mxchange\CoreFramework\Bootstrap\FrameworkBootstrap; -use Org\Mxchange\CoreFramework\Factory\ObjectFactory; -use Org\Mxchange\CoreFramework\Filesystem\Block; -use Org\Mxchange\CoreFramework\Filesystem\Block\CalculatableBlock; +use Org\Mxchange\CoreFramework\EntryPoint\ApplicationEntryPoint; +use Org\Mxchange\CoreFramework\Factory\Object\ObjectFactory; use Org\Mxchange\CoreFramework\Filesystem\File\BaseAbstractFile; +use Org\Mxchange\CoreFramework\Filesystem\FilePointer; +use Org\Mxchange\CoreFramework\Generic\FrameworkInterface; +use Org\Mxchange\CoreFramework\Middleware\Debug\DebugMiddleware; +use Org\Mxchange\CoreFramework\Traits\Index\IndexableTrait; +use Org\Mxchange\CoreFramework\Traits\Stack\StackableTrait; // Import SPL stuff +use \BadMethodCallException; +use \InvalidArgumentException; +use \LogicException; +use \OutOfBoundsException; use \SplFileInfo; +use \UnexpectedValueException; /** * A general binary file class * * @author Roland Haeder * @version 0.0.0 - * @copyright Copyright (c) 2007, 2008 Roland Haeder, 2009 - 2020 Core Developer Team + * @copyright Copyright (c) 2007, 2008 Roland Haeder, 2009 - 2023 Core Developer Team * @license GNU GPL 3.0 or any newer version * @link http://www.ship-simu.org * @@ -34,68 +43,15 @@ use \SplFileInfo; * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -abstract class BaseBinaryFile extends BaseAbstractFile { - /** - * Separator for header data - */ - const SEPARATOR_HEADER_DATA = 0x01; - - /** - * Separator header->entries - */ - const SEPARATOR_HEADER_ENTRIES = 0x02; - - /** - * Separator group->hash - */ - const SEPARATOR_GROUP_HASH = 0x03; - - /** - * Separator hash->value - */ - const SEPARATOR_HASH_VALUE = 0x04; - - /** - * Separator entry->entry - */ - const SEPARATOR_ENTRIES = 0x05; - - /** - * Separator type->position - */ - const SEPARATOR_TYPE_POSITION = 0x06; - - /** - * Length of count - */ - const LENGTH_COUNT = 20; - - /** - * Length of position - */ - const LENGTH_POSITION = 20; - - /** - * Length of group - */ - const LENGTH_GROUP = 10; +abstract class BaseBinaryFile extends BaseAbstractFile implements BinaryFile { + // Load traits + use StackableTrait; + use IndexableTrait; /** - * Maximum length of entry type + * Configuration cache */ - const LENGTH_TYPE = 20; - - //***** Array elements for 'gaps' array ***** - - /** - * Start of gap - */ - const GAPS_INDEX_START = 'start'; - - /** - * End of gap - */ - const GAPS_INDEX_END = 'end'; + private static $configCache = []; /** * Current seek position @@ -110,17 +66,17 @@ abstract class BaseBinaryFile extends BaseAbstractFile { /** * File header */ - private $header = array(); + private $header = []; /** * Seek positions for gaps ("fragmentation") */ - private $gaps = array(); + private $gaps = []; /** * Seek positions for damaged entries (e.g. mismatching hash sum, ...) */ - private $damagedEntries = array(); + private $damagedEntries = []; /** * Back-buffer @@ -138,62 +94,55 @@ abstract class BaseBinaryFile extends BaseAbstractFile { * @param $className Name of the class * @return void */ - protected function __construct ($className) { + protected function __construct (string $className) { // Call parent constructor + //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage(sprintf('BASE-BINARY-FILE: className=%s - CALLED!', $className)); parent::__construct($className); - // Init counters and gaps array - $this->initCountersGapsArray(); + // Trace message + //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage('FRAMEWORK-DIRECTORY-POINTER: EXIT!'); } /** - * Checks whether the abstracted file only contains gaps by counting all - * gaps' bytes together and compare it to total length. + * Setter for backBuffer field * - * @return $isGapsOnly Whether the abstracted file only contains gaps + * @param $backBuffer Characters to "store" in back-buffer + * @return void */ - private function isFileOnlyGaps () { - // First/last gap found? - /* Only for debugging - if (isset($this->gaps[0])) { - // Output first and last gap - self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('[%s:%d:] this->gaps[0]=%s,this->gaps[%s]=%s', __METHOD__, __LINE__, print_r($this->gaps[0], true), (count($this->gaps) - 1), print_r($this->gaps[count($this->gaps) - 1], true))); - } // END - if - */ - - // Now count every gap - $gapsSize = 0; - foreach ($this->gaps as $gap) { - // Calculate size of found gap: end-start including both - $gapsSize += ($gap[self::GAPS_INDEX_END] - $gap[self::GAPS_INDEX_START]); - } // END - if - - // Debug output - //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('[%s:%d:] gapsSize=%s,this->headerSize=%s', __METHOD__, __LINE__, $gapsSize, $this->getHeaderSize())); - - // Total gap size + header size must be same as file size - $isGapsOnly = (($this->getHeaderSize() + $gapsSize) == $this->getFileSize()); + private function setBackBuffer (string $backBuffer) { + //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage(sprintf('BASE-BINARY-FILE: Setting backBuffer(%d)=%s - CALLED!', strlen($backBuffer), $backBuffer)); + $this->backBuffer = $backBuffer; + } - // Return status - return $isGapsOnly; + /** + * Getter for backBuffer field + * + * @return $backBuffer Characters "stored" in back-buffer + */ + private function getBackBuffer () { + //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage(sprintf('BASE-BINARY-FILE: Getting this->backBuffer(%d)=%s - CALLED!', strlen($this->backBuffer), $this->backBuffer)); + return $this->backBuffer; } /** - * Initializes counter for valid entries, arrays for damaged entries and - * an array for gap seek positions. If you call this method on your own, - * please re-analyze the file structure. So you are better to call - * analyzeFile() instead of this method. + * Setter for current field * + * @param $current Characters to set a currently loaded block * @return void */ - public function initCountersGapsArray () { - // Init counter and seek position - $this->setCounter(0); - $this->setSeekPosition(0); + private function setCurrentBlock (string $currentBlock) { + //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage(sprintf('BASE-BINARY-FILE: Setting currentBlock(%d)=%s - CALLED!', strlen($currentBlock), $currentBlock)); + $this->currentBlock = $currentBlock; + } - // Init arrays - $this->gaps = array(); - $this->damagedEntries = array(); + /** + * Gets currently read data + * + * @return $current Currently read data + */ + public function getCurrentBlock () { + //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage(sprintf('BASE-BINARY-FILE: Getting this->currentBlock(%d)=%s - CALLED!', strlen($this->currentBlock), $this->currentBlock)); + return $this->currentBlock; } /** @@ -202,7 +151,7 @@ abstract class BaseBinaryFile extends BaseAbstractFile { * @return $totalEntries Size of file header */ public final function getHeaderSize () { - // Get it + //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage(sprintf('BASE-BINARY-FILE: Getting this->headerSize=%d - CALLED!', $this->headerSize)); return $this->headerSize; } @@ -212,8 +161,8 @@ abstract class BaseBinaryFile extends BaseAbstractFile { * @param $headerSize Size of file header * @return void */ - public final function setHeaderSize ($headerSize) { - // Set it + public final function setHeaderSize (int $headerSize) { + //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage(sprintf('BASE-BINARY-FILE: Setting headerSize=%d - CALLED!', $headerSize)); $this->headerSize = $headerSize; } @@ -244,7 +193,7 @@ abstract class BaseBinaryFile extends BaseAbstractFile { * @return $seekPosition Current seek position (stored here in object) */ public final function getSeekPosition () { - // Get it + //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage(sprintf('BASE-BINARY-FILE: Getting this->seekPosition=%d - CALLED!', $this->seekPosition)); return $this->seekPosition; } @@ -254,51 +203,140 @@ abstract class BaseBinaryFile extends BaseAbstractFile { * @param $seekPosition Current seek position (stored here in object) * @return void */ - protected final function setSeekPosition ($seekPosition) { - // And set it + protected final function setSeekPosition (int $seekPosition) { + //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage(sprintf('BASE-BINARY-FILE: Setting seekPosition=%d - CALLED!', $seekPosition)); $this->seekPosition = $seekPosition; } /** - * Updates seekPosition attribute from file to avoid to much access on file. + * Checks whether the abstracted file only contains gaps by counting all + * gaps' bytes together and compare it to total length. * + * @return $isGapsOnly Whether the abstracted file only contains gaps + * @throws OutOfBoundsException If calculated file size is larger than actual + */ + public function isFileGapsOnly () { + // Count every gap + //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage('BASE-BINARY-FILE: CALLED!'); + $gapsSize = 0; + //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugMessage(sprintf('BASE-BINARY-FILE: this->gaps()=%d', count($this->gaps))); + foreach ($this->gaps as $gap) { + // Calculate size of found gap: end-start including both + //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugMessage(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])); + $gapsSize += ($gap[BinaryFile::GAPS_INDEX_END] - $gap[BinaryFile::GAPS_INDEX_START]); + + // Debug message + //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugMessage(sprintf('BASE-BINARY-FILE: gapsSize=%d', $gapsSize)); + } + + // Total gap size + header size + 1 must be same as file size + //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugMessage(sprintf('BASE-BINARY-FILE: gapsSize=%d,this->headerSize=%d,this->fileSize=%d', $gapsSize, $this->getHeaderSize(), $this->getFileSize())); + $determinedFileSize = ($gapsSize + $this->getHeaderSize() + 1); + + // Should not be more! + //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugMessage(sprintf('BASE-BINARY-FILE: determinedFileSize=%d,this->fileSize=%d', $determinedFileSize, $this->getFileSize())); + if ($determinedFileSize > $this->getFileSize()) { + // Should not happen + throw new OutOfBoundsException(sprintf('determinedFileSize=%d is larger than this->fileSize=%d', $determinedFileSize, $this->getFileSize())); + } + + // Is it same? + $isGapsOnly = ($determinedFileSize == $this->getFileSize()); + + // Return flag + //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage(sprintf('BASE-BINARY-FILE: isGapsOnly=%d - EXIT!', intval($isGapsOnly))); + return $isGapsOnly; + } + + /** + * Marks whole file as gaps-only (freshly created file + * + * @param $type Type of file + * @param $minimumBlockLength Minimum block length * @return void */ - public function updateSeekPosition () { - //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('[%s:%d:] CALLED!', __METHOD__, __LINE__)); + private function markFileGapsOnly (string $type, int $minimumBlockLength) { + // Is config cache there? + //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage(sprintf('BASE-BINARY-FILE: type=%s,minimumBlockLength=%d - CALLED!', $type, $minimumBlockLength)); + if (!isset(self::$configCache[$type . '_pre_allocate_count'])) { + // Then set it + self::$configCache[$type . '_pre_allocate_count'] = FrameworkBootstrap::getConfigurationInstance()->getConfigEntry($type . '_pre_allocate_count'); + } - // Get key (= seek position) - $seekPosition = $this->key(); - //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('[%s:%d:] Setting seekPosition=%s', __METHOD__, __LINE__, $seekPosition)); + // Very simple to do ... + //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugMessage(sprintf('BASE-BINARY-FILE: self:configCache[%s_pre_allocate_count]=%d', $type, self::$configCache[$type . '_pre_allocate_count'])); + for ($idx = 0; $idx < self::$configCache[$type . '_pre_allocate_count']; $idx++) { + // Calculate start/end positions + $startPosition = $idx * $minimumBlockLength; + $endPosition = $idx * $minimumBlockLength + $minimumBlockLength; - // And set it here - $this->setSeekPosition($seekPosition); + // Mark start and end position as gap + //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage(sprintf('BASE-BINARY-FILE: Invoking this->addGap(%d, %d) ...', $startPosition, $endPosition)); + $this->addGap($startPosition, $endPosition); + } - //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('[%s:%d:] EXIT!', __METHOD__, __LINE__)); + // Trace message + //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage('BASE-BINARY-FILE: EXIT!'); } /** - * Seeks to beginning of file, updates seek position in this object and - * flushes the header. + * Adds a gap for given start and end position * + * @param $startPosition Start seek position + * @param $endPosition End seek position * @return void */ - protected function rewindUpdateSeekPosition () { - //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('[%s:%d:] CALLED!', __METHOD__, __LINE__)); + private function addGap(int $startPosition, int $endPosition) { + // Push to gaps array + //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage(sprintf('BASE-BINARY-FILE: startPosition=%d,endPosition=%d - CALLED!', $startPosition, $endPosition)); + array_push($this->gaps, [ + BinaryFile::GAPS_INDEX_START => $startPosition, + BinaryFile::GAPS_INDEX_END => $endPosition, + ]); + + // Trace message + //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage('BASE-BINARY-FILE: EXIT!'); + } - // flushFileHeader must be callable - assert(is_callable(array($this, 'flushFileHeader'))); + /** + * Initializes the back-buffer by setting it to an empty string. + * + * @return void + */ + private function initBackBuffer () { + // Simply call the setter + //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage('BASE-BINARY-FILE: CALLED!'); + $this->setBackBuffer(''); + + // Trace message + //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage('BASE-BINARY-FILE: EXIT!'); + } + /** + * Seeks to beginning of file, updates seek position in this object and + * flushes the header. + * + * @param $flushHeader Wether the file's header should be flushed (default: false) + * @return void + */ + protected function rewindUpdateSeekPosition (bool $flushHeader = false) { // Seek to beginning of file + //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage(sprintf('BASE-BINARY-FILE: flushHeader=%d - CALLED!', intval($flushHeader))); $this->rewind(); // And update seek position ... + //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage('BASE-BINARY-FILE: Invoking this->updateSeekPosition() ...'); $this->updateSeekPosition(); - // ... to write it back into the file - $this->flushFileHeader(); + // Flush headers? + if ($flushHeader) { + // ... to write it back into the file + //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage('BASE-BINARY-FILE: Invoking this->flushFileHeader() ...'); + $this->flushFileHeader(); + } - //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('[%s:%d:] EXIT!', __METHOD__, __LINE__)); + // Trace message + //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage('BASE-BINARY-FILE: EXIT!'); } /** @@ -307,97 +345,134 @@ abstract class BaseBinaryFile extends BaseAbstractFile { * @return void */ protected function seekToOldPosition () { - //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('[%s:%d:] CALLED!', __METHOD__, __LINE__)); - // Seek to currently ("old") saved position - $this->seek($this->getSeekPosition()); + //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage('BASE-BINARY-FILE: CALLED!'); + $this->seek($this->determineSeekPosition()); - //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('[%s:%d:] EXIT!', __METHOD__, __LINE__)); + // Trace message + //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage('BASE-BINARY-FILE: EXIT!'); } /** - * Checks whether the block separator has been found + * Initializes this file class * - * @param $str String to look in - * @return $isFound Whether the block separator has been found + * @param $fileInfoInstance An instance of a SplFileInfo class + * @return void */ - public static function isBlockSeparatorFound ($str) { - // Determine it - $isFound = (strpos($str, chr(self::SEPARATOR_ENTRIES)) !== false); + protected function initFile (SplFileInfo $fileInfoInstance) { + // Get a file i/o pointer instance + //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage(sprintf('BASE-BINARY-FILE: fileInfoInstance[%s]=%s - CALLED!', get_class($fileInfoInstance), $fileInfoInstance)); + $pointerInstance = ObjectFactory::createObjectByConfiguredName('file_raw_input_output_class', array($fileInfoInstance)); - // Return result - return $isFound; + // ... and set it here + //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugMessage(sprintf('BASE-BINARY-FILE: Setting pointerInstance=%s ...', $pointerInstance->__toString())); + $this->setPointerInstance($pointerInstance); + + // Trace message + //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage('BASE-BINARY-FILE: EXIT!'); } /** - * Initializes the back-buffer by setting it to an empty string. + * Marks the currently loaded block as empty (with length of the block) * + * @param $length Length of the block * @return void + * @throws InvalidArgumentException If a parameter is invalid */ - private function initBackBuffer () { - // Simply call the setter - $this->setBackBuffer(''); + protected function markCurrentBlockAsEmpty (int $length) { + // Validate parameter + //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage(sprintf('BASE-BINARY-FILE: length=%d - CALLED!', $length)); + if ($length < 1) { + // Length cannot below one + throw new InvalidArgumentException(sprintf('length=%d is not valid', $length), FrameworkInterface::EXCEPTION_INVALID_ARGUMENT); + } + + // Get current seek position + $currentPosition = $this->determineSeekPosition(); + + // Now add it as gap entry + //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage(sprintf('BASE-BINARY-FILE: Invoking this->addGap(%d, %d) ..', ($currentPosition - $length), $currentPosition)); + $this->addGap(($currentPosition - $length), $currentPosition); + + // Trace message + //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage('BASE-BINARY-FILE: EXIT!'); } /** - * Setter for backBuffer field + * Initializes counter for valid entries, arrays for damaged entries and + * an array for gap seek positions. If you call this method on your own, + * please re-analyze the file structure. So you are better to call + * analyzeFileStructure() instead of this method. * - * @param $backBuffer Characters to "store" in back-buffer * @return void */ - private function setBackBuffer ($backBuffer) { - // Cast to string (so no arrays or objects) - $backBuffer = (string) $backBuffer; + public function initCountersGapsArray () { + // Init counter and seek position to header size + //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage('BASE-BINARY-FILE: Invoking this->determineSeekPosition() - CALLED!'); + $seekPosition = $this->getSeekPosition(); - // ... and set it - $this->backBuffer = $backBuffer; - } + // Set counter + //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugMessage(sprintf('BASE-BINARY-FILE: seekPosition=%d', $seekPosition)); + $this->setCounter(0); - /** - * Getter for backBuffer field - * - * @return $backBuffer Characters "stored" in back-buffer - */ - private function getBackBuffer () { - return $this->backBuffer; + // Get header size + $headerSize = $this->getHeaderSize(); + + // Set it + //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugMessage(sprintf('BASE-BINARY-FILE: Setting this->seekPosition=%d ...', $headerSize)); + $this->setSeekPosition($headerSize); + + // Init arrays + $this->gaps = []; + $this->damagedEntries = []; + + // Seek back to old position + //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage(sprintf('BASE-BINARY-FILE: Invoking this->seek(%d) ...', $seekPosition)); + $this->seek($seekPosition); + + // Trace message + //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage('BASE-BINARY-FILE: EXIT!'); } /** - * Setter for currentBlock field + * Updates seekPosition attribute from file to avoid to much access on file. * - * @param $currentBlock Characters to set a currently loaded block * @return void */ - private function setCurrentBlock ($currentBlock) { - // Cast to string (so no arrays or objects) - $currentBlock = (string) $currentBlock; + public function updateSeekPosition () { + // Get key (= seek position) + //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage('BASE-BINARY-FILE: CALLED!'); + $seekPosition = $this->determineSeekPosition(); - // ... and set it - $this->currentBlock = $currentBlock; - } + // And set it here + //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugMessage(sprintf('BASE-BINARY-FILE: seekPosition=%d', $seekPosition)); + $this->setSeekPosition($seekPosition); - /** - * Gets currently read data - * - * @return $current Currently read data - */ - public function getCurrentBlock () { - // Return it - return $this->currentBlock; + // Trace message + //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage('BASE-BINARY-FILE: EXIT!'); } /** - * Initializes this file class + * Checks whether the block separator has been found * - * @param $infoInstance An instance of a SplFileInfo class - * @return void + * @param $str String to look in + * @return $isFound Whether the block separator has been found + * @throws InvalidArgumentException If a parameter is not valid */ - protected function initFile (SplFileInfo $infoInstance) { - // Get a file i/o pointer instance - $pointerInstance = ObjectFactory::createObjectByConfiguredName('file_raw_input_output_class', array($infoInstance)); + public static function isBlockSeparatorFound (string $str) { + // Validate parameter + //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage(sprintf('BASE-BINARY-FILE: str=%s - CALLED!', $str)); + if (empty($str)) { + // Throw IAE + throw new InvalidArgumentException('Parameter "str" is empty', FrameworkInterface::EXCEPTION_INVALID_ARGUMENT); + } - // ... and set it here - $this->setPointerInstance($pointerInstance); + // Determine it + $isFound = (strpos($str, chr(BinaryFile::SEPARATOR_ENTRIES)) !== false); + + // Return result + //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage(sprintf('BASE-BINARY-FILE: isFound=%d - EXIT!', intval($isFound))); + return $isFound; } /** @@ -407,46 +482,75 @@ abstract class BaseBinaryFile extends BaseAbstractFile { * @param $data Data to be written * @param $flushHeader Whether to flush the header (default: flush) * @return void + * @throws OutOfBoundsException If the position is not seekable + * @throws InvalidArgumentException If a parameter is invalid */ - public function writeData ($seekPosition, $data, $flushHeader = true) { - //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('[%s:%d:] seekPosition=%s,data()=%d - CALLED!', __METHOD__, __LINE__, $seekPosition, strlen($data))); + public function writeData (int $seekPosition, string $data, bool $flushHeader = true) { + // Validate parameter + //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage(sprintf('BASE-BINARY-FILE: seekPosition=%s,data()=%d,flushHeader=%d - CALLED!', $seekPosition, strlen($data), intval($flushHeader))); + if ($seekPosition < 0) { + // Invalid seek position + throw new OutOfBoundsException(sprintf('seekPosition=%d is not valid', $seekPosition)); + } elseif (empty($data)) { + // Empty data is invalid, too + throw new InvalidArgumentException('Parameter "data" is empty', FrameworkInterface::EXCEPTION_INVALID_ARGUMENT); + } // Write data at given position - $this->getPointerInstance()->writeAtPosition($seekPosition, $data); + //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage(sprintf('BASE-BINARY-FILE: Invoking this->writeAtPosition(%d,%s) ...', $seekPosition, $data)); + $this->writeAtPosition($seekPosition, $data); // Increment counter + //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage('BASE-BINARY-FILE: Invoking this->incrementCounter() ...'); $this->incrementCounter(); // Update seek position + //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage('BASE-BINARY-FILE: Invoking this->updateSeekPosition() ...'); $this->updateSeekPosition(); // Flush the header? + //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugMessage(sprintf('BASE-BINARY-FILE: flushHeader=%d', intval($flushHeader))); if ($flushHeader === true) { // Flush header + //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage('BASE-BINARY-FILE: Invoking this->flushFileHeader() ...'); $this->flushFileHeader(); // Seek to old position + //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage('BASE-BINARY-FILE: Invoking this->seekToOldPosition() ...'); $this->seekToOldPosition(); - } // END - if + } - //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('[%s:%d:] EXIT!', __METHOD__, __LINE__)); + // Trace message + //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage('BASE-BINARY-FILE: EXIT!'); } /** - * Marks the currently loaded block as empty (with length of the block) + * Writes at given position by seeking to it. * - * @param $length Length of the block - * @return void + * @param $seekPosition Seek position in file + * @param $dataStream Data to be written + * @return mixed Number of writes bytes or false on error + * @throws OutOfBoundsException If the position is not seekable + * @throws InvalidArgumentException If a parameter is not valid */ - protected function markCurrentBlockAsEmpty ($length) { - // Get current seek position - $currentPosition = $this->key(); + public function writeAtPosition (int $seekPosition, string $dataStream) { + // Validate parameter + //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage(sprintf('BASE-BINARY-FILE: seekPosition=%d,dataStream(%d)=%s - CALLED!', $seekPosition, strlen($dataStream), $dataStream)); + if ($seekPosition < 0) { + // Invalid seek position + throw new OutOfBoundsException(sprintf('seekPosition=%d is not valid.', $seekPosition)); + } elseif (empty($dataStream)) { + // Empty dataStream + throw new InvalidArgumentException('Parameter "dataStream" is empty', FrameworkInterface::EXCEPTION_INVALID_ARGUMENT); + } - // Now add it as gap entry - array_push($this->gaps, array( - self::GAPS_INDEX_START => ($currentPosition - $length), - self::GAPS_INDEX_END => $currentPosition, - )); + // Call pointer's method + //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage(sprintf('BASE-BINARY-FILE: Invoking this->pointerInstance->writeAtPosition(%d, %s) ...', $seekPosition, $dataStream)); + $status = $this->getPointerInstance()->writeAtPosition($seekPosition, $dataStream); + + // Return status + //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage(sprintf('BASE-BINARY-FILE: status[%s]=%d - EXIT!', gettype($status), $status)); + return $status; } /** @@ -455,32 +559,29 @@ abstract class BaseBinaryFile extends BaseAbstractFile { * @return $isInitialized Whether the file header is initialized */ public function isFileHeaderInitialized () { - //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('[%s:%d:] CALLED!', __METHOD__, __LINE__)); - // Default is not initialized + //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage('BASE-BINARY-FILE: CALLED!'); $isInitialized = false; // Is the file initialized? if ($this->isFileInitialized()) { // Some bytes has been written, so rewind to start of it. - $rewindStatus = $this->rewind(); - //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('[%s:%d:] rewindStatus=%s', __METHOD__, __LINE__, $rewindStatus)); - - // Is the rewind() call successfull? - if ($rewindStatus != 1) { - // Something bad happened - self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('[%s:%d:] Could not rewind().', __METHOD__, __LINE__)); - } // END - if + $this->rewind(); // Read file header + //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage('BASE-BINARY-FILE: Invoking this->readFileHeader() ...'); $this->readFileHeader(); + // Get header count + $headerCount = count($this->getHeader()); + // The above method does already check the header - $isInitialized = true; - } // END - if + //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugMessage(sprintf('BASE-BINARY-FILE: headerCount=%d', $headerCount)); + $isInitialized = ($headerCount > 0); + } // Return result - //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('[%s:%d:] isInitialized=%d - EXIT!', __METHOD__, __LINE__, intval($isInitialized))); + //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage(sprintf('BASE-BINARY-FILE: isInitialized=%d - EXIT!', intval($isInitialized))); return $isInitialized; } @@ -488,25 +589,28 @@ abstract class BaseBinaryFile extends BaseAbstractFile { * Checks whether the assigned file has been initialized * * @return $isInitialized Whether the file's size is zero + * @throws UnexpectedValueException If an unexpected value was returned */ public function isFileInitialized () { - //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('[%s:%d:] CALLED!', __METHOD__, __LINE__)); - // Get it from iterator which holds the pointer instance. If false is returned + //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage('BASE-BINARY-FILE: CALLED!'); $fileSize = $this->size(); - //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('[%s:%d:] fileSize=%s', __METHOD__, __LINE__, $fileSize)); /* * The returned file size should not be false or NULL as this means * that the pointer class does not work correctly. */ - assert(is_int($fileSize)); + //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugMessage(sprintf('BASE-BINARY-FILE: fileSize[%s]=%d', gettype($fileSize), $fileSize)); + if (!is_int($fileSize)) { + // Bad file? + throw new UnexpectedValueException(sprintf('fileSize[]=%s is unexpected', gettype($fileSize)), FrameworkInterface::EXCEPTION_UNEXPECTED_VALUE); + } // Is more than 0 returned? $isInitialized = ($fileSize > 0); // Return result - //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('[%s:%d:] isInitialized=%d - EXIT!', __METHOD__, __LINE__, intval($isInitialized))); + //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage(sprintf('BASE-BINARY-FILE: isInitialized=%d - EXIT!', intval($isInitialized))); return $isInitialized; } @@ -514,58 +618,27 @@ abstract class BaseBinaryFile extends BaseAbstractFile { * Creates the assigned file * * @return void + * @throws BadMethodCallException If this file's header is already initialized */ public function createFileHeader () { - //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('[%s:%d:] CALLED!', __METHOD__, __LINE__)); - // The file's header should not be initialized here - assert(!$this->isFileHeaderInitialized()); + //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage('BASE-BINARY-FILE: CALLED!'); + if ($this->isFileHeaderInitialized()) { + // Bad method call + //* DEBUG-DIE: */ ApplicationEntryPoint::exitApplication(sprintf('[%s:%d]: this=%s', __METHOD__, __LINE__, print_r($this, TRUE))); + throw new BadMethodCallException('File header is already initialized but method called', FrameworkInterface::EXCEPTION_BAD_METHOD_CALL); + } // Simple flush file header which will create it. + //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage('BASE-BINARY-FILE: Invoking this->flushFileHeader() ...'); $this->flushFileHeader(); // Rewind seek position (to beginning of file) and update/flush file header + //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage('BASE-BINARY-FILE: Invoking this->rewindUpdateSeekPosition() ...'); $this->rewindUpdateSeekPosition(); - //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('[%s:%d:] EXIT!', __METHOD__, __LINE__)); - } - - /** - * Pre-allocates file (if enabled) with some space for later faster write access. - * - * @param $type Type of the file - * @return void - */ - public function preAllocateFile ($type) { - //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('[%s:%d:] CALLED!', __METHOD__, __LINE__)); - - // Is it enabled? - if (FrameworkBootstrap::getConfigurationInstance()->getConfigEntry($type . '_pre_allocate_enabled') != 'Y') { - // Not enabled - self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('[%s:%d:] Not pre-allocating file.', __METHOD__, __LINE__)); - - // Don't continue here. - return; - } // END - if - - // Message to user - self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('[%s:%d:] Pre-allocating file ...', __METHOD__, __LINE__)); - - // Calculate minimum length for one entry - $minLengthEntry = $this->getBlockInstance()->calculateMinimumBlockLength(); - //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('[%s:%d:] minLengthEntry=%s', __METHOD__, __LINE__, $minLengthEntry)); - - // Calulcate seek position - $seekPosition = $minLengthEntry * FrameworkBootstrap::getConfigurationInstance()->getConfigEntry($type . '_pre_allocate_count'); - //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('[%s:%d:] seekPosition=%s', __METHOD__, __LINE__, $seekPosition)); - - // Now simply write a NUL there. This will pre-allocate the file. - $this->writeData($seekPosition, chr(0)); - - // Rewind seek position (to beginning of file) and update/flush file header - $this->rewindUpdateSeekPosition(); - - //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('[%s:%d:] EXIT!', __METHOD__, __LINE__)); + // Trace message + //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage('BASE-BINARY-FILE: EXIT!'); } /** @@ -575,7 +648,12 @@ abstract class BaseBinaryFile extends BaseAbstractFile { */ public function determineSeekPosition () { // Call pointer instance - return $this->getPointerInstance()->determineSeekPosition(); + //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage('BASE-BINARY-FILE: CALLED!'); + $seekPosition = $this->getPointerInstance()->determineSeekPosition(); + + // Return position + //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage(sprintf('BASE-BINARY-FILE: seekPosition=%d - EXIT!', $seekPosition)); + return $seekPosition; } /** @@ -584,10 +662,22 @@ abstract class BaseBinaryFile extends BaseAbstractFile { * @param $offset Offset to seek to (or used as "base" for other seeks) * @param $whence Added to offset (default: only use offset to seek to) * @return $status Status of file seek: 0 = success, -1 = failed + * @throws OutOfBoundsException If the position is not seekable */ - public function seek ($offset, $whence = SEEK_SET) { + public function seek (int $offset, int $whence = SEEK_SET) { + // Validate parameter + //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage(sprintf('BASE-BINARY-FILE: offset=%d,whence=%d - CALLED!', $offset, $whence)); + if ($offset < 0) { + // No offset is smaller than zero + throw new OutOfBoundsException(sprintf('offset=%d is not valid', $offset)); + } + // Call pointer instance - return $this->getPointerInstance()->seek($offset, $whence); + $status = $this->getPointerInstance()->seek($offset, $whence); + + // Return status + //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage(sprintf('BASE-BINARY-FILE: status[%s]=%d - EXIT!', gettype($status), $status)); + return $status; } /** @@ -595,23 +685,41 @@ abstract class BaseBinaryFile extends BaseAbstractFile { * * @param $bytes Amount of bytes to read * @return $data Data read from file + * @throws OutOfBoundsException If the position is not seekable */ - public function read ($bytes = NULL) { - // $bytes shall be integer - assert(is_int($bytes)); + public function read (int $bytes = 0) { + // Validate parameter + //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage(sprintf('BASE-BINARY-FILE: bytes=%d - CALLED!', $bytes)); + if ($bytes < 0) { + // Throw exception + throw new OutOfBoundsException(sprintf('bytes=%d is not valid', $bytes)); + } // Call pointer instance - return $this->getPointerInstance()->read($bytes); + //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage(sprintf('BASE-BINARY-FILE: Invoking this->pointerInstance->read(%d) ...', $bytes)); + $data = $this->getPointerInstance()->read($bytes); + + // Update seek position + //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage('BASE-BINARY-FILE: Invoking this->updateSeekPosition() ...'); + $this->updateSeekPosition(); + + // Return data + //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage(sprintf('BASE-BINARY-FILE: data[%s]=%s - EXIT!', gettype($data), $data)); + return $data; } /** * Rewinds to the beginning of the file * - * @return $status Status of this operation + * @return void */ public function rewind () { // Call pointer instance - return $this->getPointerInstance()->rewind(); + //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage('BASE-BINARY-FILE: CALLED!'); + $this->getPointerInstance()->rewind(); + + // Trace message + //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage('BASE-BINARY-FILE: EXIT!'); } /** @@ -620,92 +728,99 @@ abstract class BaseBinaryFile extends BaseAbstractFile { * only gaps are found, the file is considered as "virgin" (no entries). * * @return void + * @throws BadMethodCallException If this method is called but file is not initialized */ - public function analyzeFile () { - //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('[%s:%d:] CALLED!', __METHOD__, __LINE__)); - + public function analyzeFileStructure () { // Make sure the file is initialized - assert($this->isFileInitialized()); + //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage('BASE-BINARY-FILE: CALLED!'); + if (!$this->isFileInitialized()) { + // Bad method call + throw new BadMethodCallException('Method called but file is not initialized.', FrameworkInterface::EXCEPTION_BAD_METHOD_CALL); + } // Init counters and gaps array + //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage('BASE-BINARY-FILE: Invoking this->initCounterGapsArrays() ...'); $this->initCountersGapsArray(); // Output message (as this may take some time) - self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('[%s:%d:] Analyzing file structure ... (this may take some time)', __METHOD__, __LINE__)); + self::createDebugInstance(__CLASS__, __LINE__)->debugMessage(sprintf('Analyzing file structure ... (this may take some time)')); - // First rewind to the begining - $this->rewind(); + // First Seek to right after file header + //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage(sprintf('BASE-BINARY-FILE: Invoking this->seek(%d) ...', $this->getHeaderSize() + 1)); + $this->seek($this->getHeaderSize() + 1); // Then try to load all entries - while ($this->valid()) { - // Go to next entry - $this->next(); - + //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugMessage(sprintf('BASE-BINARY-FILE: this->seekPosition=%d, looping through file ...', $this->getSeekPosition())); + while ($this->isValid()) { // Get current entry $current = $this->getCurrentBlock(); + // Go to next entry + //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage(sprintf('BASE-BINARY-FILE: current=%s, Invoking this->readNextBlock() ...', $current)); + $this->readNextBlock(); + /* * If the block is empty, maybe the whole file is? This could mean * that the file has been pre-allocated. */ - if (empty($current)) { + //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugMessage(sprintf('BASE-BINARY-FILE: current(%d)[]=%s', strlen($current), gettype($current))); + if (empty(trim($current, chr(0)))) { // Then skip this part + //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugMessage(sprintf('BASE-BINARY-FILE: current[]=%s is empty - CONTINUE!', gettype($current))); continue; - } // END - if + } - // Debug message - /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('[%s:%d:] current()=%d', __METHOD__, __LINE__, strlen($current))); - } // END - while + // Handle current record + //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugMessage(sprintf('BASE-BINARY-FILE: current(%d)[%s]=%s', strlen($current), gettype($current), $current)); + } // If the last read block is empty, check gaps - if (empty($current)) { + //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugMessage(sprintf('BASE-BINARY-FILE: current()=%d', strlen($current))); + if (empty(trim($current, chr(0)))) { // Output message - self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('[%s:%d:] Found a total of %s gaps.', __METHOD__, __LINE__, count($this->gaps))); + self::createDebugInstance(__CLASS__, __LINE__)->debugMessage(sprintf('BASE-BINARY-FILE: Found a total of %d gaps.', count($this->gaps))); // Check gaps, if the whole file is empty. - if ($this->isFileOnlyGaps()) { + //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage('BASE-BINARY-FILE: Invoking this->isFileGapsOnly() ...'); + if ($this->isFileGapsOnly()) { // Only gaps, so don't continue here. + //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugMessage('BASE-BINARY-FILE: File is gaps-only - EXIT!'); return; - } // END - if + } + } - /* - * The above call has calculated a total size of all gaps. If the - * percentage of gaps passes a "soft" limit and last - * defragmentation is to far in the past, or if a "hard" limit has - * reached, run defragmentation. - */ - if ($this->isDefragmentationNeeded()) { - // Run "defragmentation" - $this->doRunDefragmentation(); - } // END - if - } // END - if - //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('[%s:%d:] EXIT!', __METHOD__, __LINE__)); + /* + * The above call has calculated a total size of all gaps. If the + * percentage of gaps passes a "soft" limit and last + * defragmentation is to far in the past, or if a "hard" limit has + * reached, run defragmentation. + */ + //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage('BASE-BINARY-FILE: Invoking this->isDefragmentationNeeded() ...'); + if ($this->isDefragmentationNeeded()) { + // Run "defragmentation" + //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage('BASE-BINARY-FILE: Invoking this->doRunDefragmentation() ...'); + $this->doRunDefragmentation(); + } + + // Trace message + //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage('BASE-BINARY-FILE: EXIT!'); } /** - * Advances to next "block" of bytes + * Reads next "block" of given bytes into $currentBlock field. THis method + * loads the whole file into memory when the file is just freshly + * initialized (only zeros in it). * * @return void + * @throws InvalidArgumentException If a parameter is not valid */ - public function next () { - // Is there nothing to read? - if (!$this->valid()) { - // Nothing to read - return; - } // END - if - - // Debug message - //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('[%s:%d] key()=%d', __FUNCTION__, __LINE__, $this->key())); - - // Make sure the block instance is set - assert($this->getBlockInstance() instanceof CalculatableBlock); - - // First calculate minimum block length - $length = $this->getBlockInstance()->calculateMinimumBlockLength(); - //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('[%s:%d] length=%s', __FUNCTION__, __LINE__, $length)); - - // Short be more than zero! - assert($length > 0); + protected function readNextBlockByLength (int $length) { + // Validate parameter + //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage(sprintf('BASE-BINARY-FILE: this->seekPosition=%d,length=%d - CALLED!', $this->getSeekPosition(), $length)); + if ($length < 1) { + // Throw IAE + throw new InvalidArgumentException(sprintf('length=%d is not valid', $length), FrameworkInterface::EXCEPTION_INVALID_ARGUMENT); + } // Read possibly back-buffered bytes from previous call of next(). $data = $this->getBackBuffer(); @@ -715,56 +830,117 @@ abstract class BaseBinaryFile extends BaseAbstractFile { * "block" may not fit, so this loop will continue until the EOB or EOF * has been reached whatever comes first. */ - while ((!$this->isEndOfFileReached()) && (!self::isBlockSeparatorFound($data))) { + //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugMessage(sprintf('BASE-BINARY-FILE: this->seekPosition=%d,data()=%d', $this->getSeekPosition(), strlen($data))); + while ((!$this->isEndOfFileReached()) && (empty($data) || !self::isBlockSeparatorFound($data))) { // Then read the next possible block + //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugMessage(sprintf('BASE-BINARY-FILE: this->seekPosition=%d, Invoking this->read(%d) ...', $this->getSeekPosition(), $length)); $block = $this->read($length); - // Debug message - //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('[%s:%d] block()=%d,length=%s', __FUNCTION__, __LINE__, strlen($block), $length)); - - // Is it all empty? - if (strlen(trim($block)) == 0) { - // Mark this block as empty - $this->markCurrentBlockAsEmpty(strlen($block)); - - // Skip to next block - continue; - } // END - if + // Is the block empty? + //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugMessage(sprintf('BASE-BINARY-FILE: block()=%d,length=%d', strlen($block), $length)); + if (strlen($block) == 0) { + // Read empty block, maybe EOF + //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugMessage(sprintf('BASE-BINARY-FILE: this->seekPosition=%d, block is empty, maybe EOF reached - BREAK!', $this->getSeekPosition())); + break; + } elseif (empty(trim($block, chr(0)))) { + // Mark it as empty + //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugMessage(sprintf('BASE-BINARY-FILE: this->seekPosition=%d, Invoking this->markCurrentBlockAsEmpty(%d) ...', $this->getSeekPosition(), $length)); + $this->markCurrentBlockAsEmpty($length); + } // At this block then $data .= $block; - // A debug message - //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('[%s:%d] data()=%d', __FUNCTION__, __LINE__, strlen($data))); - } // END - while - - // EOF reached? - if ($this->isEndOfFileReached()) { - // Set whole data as current read block - //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('Calling setCurrentBlock(' . strlen($data) . ') ...'); - $this->setCurrentBlock($data); - - // Then abort here silently - //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('EOF reached.'); - return; - } // END - if + // Debug message + //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugMessage(sprintf('BASE-BINARY-FILE: this->seekPosition=%d,data()=%d', $this->getSeekPosition(), strlen($data))); + } /* * Init back-buffer which is the data that has been found beyond the * separator. */ + //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage('BASE-BINARY-FILE: Invoking this->initBackBuffer(), clearing this->currentBlock ...'); $this->initBackBuffer(); + $this->setCurrentBlock(''); - // Separate data - $dataArray = explode(chr(self::SEPARATOR_ENTRIES), $data); + // Is $data empty? + //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugMessage(sprintf('BASE-BINARY-FILE: data(%d)=%s', strlen($data), $data)); + if (empty(trim($data, chr(0)))) { + // Yes, maybe whole file was ... + //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugMessage(sprintf('BASE-BINARY-FILE: this->seekPosition=%d, maybe empty file found - EXIT!', $this->getSeekPosition())); + return; + } - // This array must contain two elements - //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('dataArray=' . print_r($dataArray, true)); - assert(count($dataArray) == 2); + // Separate data + $dataArray = explode(chr(BinaryFile::SEPARATOR_ENTRIES), $data); - // Left part is the actual block, right one the back-buffer data + // Left part is the actual block, right one the back-buffer data, if found + //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugMessage(sprintf('BASE-BINARY-FILE: dataArray()=%d', count($dataArray))); + //* PRINTR-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugMessage(sprintf('BASE-BINARY-FILE: dataArray=%s', print_r($dataArray, true))); $this->setCurrentBlock($dataArray[0]); - $this->setBackBuffer($dataArray[1]); + + // Is back buffere data found? + if (isset($dataArray[1]) && !empty(trim($dataArray[1], chr(0)))) { + // Set back buffer + //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugMessage(sprintf('BASE-BINARY-FILE: Setting this->backBuffer=%s ...', $dataArray[1])); + $this->setBackBuffer($dataArray[1]); + } + + // Trace message + //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage('BASE-BINARY-FILE: EXIT!'); + } + + /** + * Pre-allocates file (if enabled) with some space for later faster write access. + * + * @param $type Type of the file + * @return void + * @throws InvalidArgumentException If a parameter is empty + */ + protected function preAllocateFileByTypeLength (string $type, int $minimumBlockLength) { + // Is it enabled? + //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage(sprintf('BASE-BINARY-FILE: type=%s,minimumBlockLength=%d - CALLED!', $type, $minimumBlockLength)); + if (empty($type)) { + // Empty type + throw new InvalidArgumentException('Parameter "type" is empty', FrameworkInterface::EXCEPTION_INVALID_ARGUMENT); + } elseif ($minimumBlockLength < 1) { + // Invalid block length + throw new InvalidArgumentException(sprintf('Parameter minimumBlockLength=%d is not valid', $minimumBlockLength), FrameworkInterface::EXCEPTION_INVALID_ARGUMENT); + } elseif (!FrameworkBootstrap::getConfigurationInstance()->isEnabled($type . '_pre_allocate')) { + // Don't continue here. + self::createDebugInstance(__CLASS__, __LINE__)->traceMessage('BASE-BINARY-FILE: Not pre-allocating file. - EXIT!'); + return; + } + + // Get file size + $fileSize = $this->getFileSize(); + + // Calulcate seek position + //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugMessage(sprintf('BASE-BINARY-FILE: minimumBlockLength=%d,fileSize=%d', $minimumBlockLength, $fileSize)); + $seekPosition = $this->getHeaderSize() + $minimumBlockLength * FrameworkBootstrap::getConfigurationInstance()->getConfigEntry($type . '_pre_allocate_count') + $fileSize ; + + // Now simply write a NUL there. This will pre-allocate the file. + //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage(sprintf('BASE-BINARY-FILE: Invoking this->writeAtPosition(%d,NUL) ...', $seekPosition)); + $this->writeAtPosition($seekPosition, chr(0)); + + // Is the seek position zero? + //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugMessage(sprintf('BASE-BINARY-FILE: fileSize=%d', $fileSize)); + if ($fileSize == 0) { + // Mark file as gaps-only + //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage(sprintf('BASE-BINARY-FILE: Invoking this->markGapsOnly(%s,%d) ...', $type, $minimumBlockLength)); + $this->markFileGapsOnly($type, $minimumBlockLength); + } else { + // Analyze file structure + //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage('BASE-BINARY-FILE: Invoking this->analyzeFileStructure() ...'); + $this->analyzeFileStructure(); + } + + // Rewind seek position + //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage('BASE-BINARY-FILE: Invoking this->rewind() ...'); + $this->rewind(); + + // Trace message + //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage('BASE-BINARY-FILE: EXIT!'); } /** @@ -772,77 +948,62 @@ abstract class BaseBinaryFile extends BaseAbstractFile { * This method will return true if an emptied (nulled) entry has been found. * * @return $isValid Whether the next entry is valid + * @throws InvalidArgumentException If a parameter is not valid */ - public function valid () { - // Make sure the block instance is set - assert($this->getBlockInstance() instanceof Block); - - // First calculate minimum block length - $length = $this->getBlockInstance()->calculateMinimumBlockLength(); - - // Short be more than zero! - assert($length > 0); + protected function isValidByLength (int $length) { + // Validate parameter + //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage(sprintf('BASE-BINARY-FILE: length=%d - CALLED!', $length)); + if ($length < 1) { + // Throw IAE + throw new InvalidArgumentException(sprintf('Parameter length=%d is not valid', $length), FrameworkInterface::EXCEPTION_INVALID_ARGUMENT); + } // Get current seek position - $seekPosition = $this->key(); + $seekPosition = $this->determineSeekPosition(); // Then try to read it + //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugMessage(sprintf('BASE-BINARY-FILE: seekPosition=%d', $seekPosition)); $data = $this->read($length); // If some bytes could be read, all is fine + //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugMessage(sprintf('BASE-BINARY-FILE: data[%s]()=%d', gettype($data), strlen($data))); $isValid = ((is_string($data)) && (strlen($data) > 0)); // Get header size + //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugMessage(sprintf('BASE-BINARY-FILE: isValid=%d', intval($isValid))); $headerSize = $this->getHeaderSize(); // Is the seek position at or beyond the header? + //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugMessage(sprintf('BASE-BINARY-FILE: seekPosition=%d,headerSize=%d', $seekPosition, $headerSize)); if ($seekPosition >= $headerSize) { // Seek back to old position - $this->seek($seekPosition); + $isValid = ($isValid && $this->seek($seekPosition) === 0); } else { // Seek directly behind the header - $this->seek($headerSize); + $isValid = ($isValid && $this->seek($headerSize) === 0); } // Return result + //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage(sprintf('BASE-BINARY-FILE: isValid=%d - EXIT!', intval($isValid))); return $isValid; } /** - * Gets current seek position ("key"). - * - * @return $key Current key in iteration - */ - public function key () { - // Call pointer instance - return $this->getPointerInstance()->determineSeekPosition(); - } - - /** - * Reads the file header + * Reads next "block" of bytes into $currentBlock field. THis method loads + * the whole file into memory when the file is just freshly initialized + * (only zeros in it). * * @return void */ - public function readFileHeader () { - // Make sure the block instance is set - assert($this->getBlockInstance() instanceof Block); - - // Call block instance - $this->getBlockInstance()->readFileHeader(); - } + protected abstract function readNextBlock (); /** - * Flushes the file header + * Reads the file header * * @return void + * @throws LogicException If both instances are not set */ - public function flushFileHeader () { - // Make sure the block instance is set - assert($this->getBlockInstance() instanceof Block); - - // Call block instance - $this->getBlockInstance()->flushFileHeader(); - } + public abstract function readFileHeader (); /** * Searches for next suitable gap the given length of data can fit in @@ -850,16 +1011,28 @@ abstract class BaseBinaryFile extends BaseAbstractFile { * * @param $length Length of raw data * @return $seekPosition Found next gap's seek position + * @throws InvalidArgumentException If the parameter is not valid */ - public function searchNextGap ($length) { + public function searchNextGap (int $length) { // If the file is only gaps, no need to seek - if ($this->isFileOnlyGaps()) { - // The first empty block is the first one right after the header - return ($this->getHeaderSize() + 1); - } // END - if + //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage(sprintf('BASE-BINARY-FILE: length=%d - CALLED!', $length)); + if ($length <= 0) { + // Throw IAE + throw new InvalidArgumentException(sprintf('length=%d is not valid', $length), FrameworkInterface::EXCEPTION_INVALID_ARGUMENT); + } elseif ($this->isFileGapsOnly()) { + /* + * The first empty block is the 2nd one right after the header, so + * one byte gap to the header. + */ + $seekPosition = ($this->getHeaderSize() + 2); + + // Return position + //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage(sprintf('BASE-BINARY-FILE: seekPosition=%d - EXIT!', $seekPosition)); + return $seekPosition; + } // @TODO Unfinished - $this->partialStub('length=' . $length); + DebugMiddleware::getSelfInstance()->partialStub('length=' . $length); } }