Implemented rudely reading file header + commented out a lot noisy debug lines.
[core.git] / inc / classes / main / stacker / file / class_BaseFileStack.php
index 21315929272717a46af5bb5dae3c5a56e0b1feb8..94410d80385331dad1f1783be47a6efec92db347 100644 (file)
@@ -28,29 +28,34 @@ class BaseFileStack extends BaseStacker {
        const STACK_MAGIC = 'STACKv0.1';
 
        /**
-        * Separator magic->count
+        * Separator for header data
         */
-       const SEPARATOR_MAGIC_COUNT = 0x00;
+       const SEPARATOR_HEADER_DATA = 0x01;
 
        /**
-        * Separator position->entries
+        * Separator header->entries
         */
-       const SEPARATOR_SEEK_POS_ENTRIES = 0xff;
+       const SEPARATOR_HEADER_ENTRIES = 0x02;
 
        /**
         * Separator hash->name
         */
-       const SEPARATOR_HASH_NAME = 0x05;
+       const SEPARATOR_HASH_NAME = 0x03;
+
+       /**
+        * Length of name
+        */
+       const LENGTH_NAME = 10;
 
        /**
         * Length of count
         */
-       const COUNT_LENGTH = 20;
+       const LENGTH_COUNT = 20;
 
        /**
         * Length of position
         */
-       const COUNT_POSITION = 20;
+       const LENGTH_POSITION = 20;
 
        /**
         * Counter for total entries
@@ -62,6 +67,16 @@ class BaseFileStack extends BaseStacker {
         */
        private $seekPosition = 0;
 
+       /**
+        * Size of header
+        */
+       private $headerSize = 0;
+
+       /**
+        * File header
+        */
+       private $header = array();
+
        /**
         * Protected constructor
         *
@@ -71,6 +86,16 @@ class BaseFileStack extends BaseStacker {
        protected function __construct ($className) {
                // Call parent constructor
                parent::__construct($className);
+
+               // Calculate header size
+               $this->headerSize = (
+                       strlen(self::STACK_MAGIC) +
+                       strlen(self::SEPARATOR_HEADER_DATA) +
+                       self::LENGTH_COUNT +
+                       strlen(self::SEPARATOR_HEADER_DATA) +
+                       self::LENGTH_POSITION +
+                       strlen(self::SEPARATOR_HEADER_ENTRIES)
+               );
        }
 
        /**
@@ -120,11 +145,77 @@ class BaseFileStack extends BaseStacker {
         * @return      void
         */
        private function updateSeekPosition () {
+               //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput(sprintf('[%s:%d:] CALLED!', __METHOD__, __LINE__));
+
                // Get key (= seek position)
                $seekPosition = $this->getIteratorInstance()->key();
+               //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput(sprintf('[%s:%d:] Setting seekPosition=%s', __METHOD__, __LINE__, $seekPosition));
 
                // And set it here
                $this->setSeekPosition($seekPosition);
+
+               //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput(sprintf('[%s:%d:] EXIT!', __METHOD__, __LINE__));
+       }
+
+       /**
+        * Reads the file header
+        *
+        * @return      void
+        */
+       private function readFileHeader () {
+               //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput(sprintf('[%s:%d:] CALLED!', __METHOD__, __LINE__));
+
+               // First rewind to beginning as the header sits at the beginning ...
+               $this->getIteratorInstance()->rewind();
+
+               // Then read it (see constructor for calculation)
+               $data = $this->getIteratorInstance()->read($this->headerSize);
+               //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput(sprintf('[%s:%d:] Read %d bytes (%d wanted).', __METHOD__, __LINE__, strlen($data), $this->headerSize));
+
+               // Have all requested bytes been read?
+               assert(strlen($data) == $this->headerSize);
+               //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput(sprintf('[%s:%d:] Passed assert().', __METHOD__, __LINE__));
+
+               // Last character must be the separator
+               //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput(sprintf('[%s:%d:] data(-1)=%s', __METHOD__, __LINE__, dechex(ord(substr($data, -1, 1)))));
+               assert(substr($data, -1, 1) == chr(self::SEPARATOR_HEADER_ENTRIES));
+               //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput(sprintf('[%s:%d:] Passed assert().', __METHOD__, __LINE__));
+
+               // Okay, then remove it
+               $data = substr($data, 0, -1);
+
+               // And update seek position
+               $this->updateSeekPosition();
+
+               /*
+                * Now split it:
+                *
+                * 0 => Magic
+                * 1 => Total entries
+                * 2 => Current seek position
+                */
+               $this->header = explode(chr(self::SEPARATOR_HEADER_DATA), $data);
+
+               // Check if the array has only 3 elements
+               //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput(sprintf('[%s:%d:] header(%d)=%s', __METHOD__, __LINE__, count($this->header), print_r($this->header, TRUE)));
+               assert(count($this->header) == 3);
+               //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput(sprintf('[%s:%d:] Passed assert().', __METHOD__, __LINE__));
+
+               // Check magic
+               assert($this->header[0] == self::STACK_MAGIC);
+               //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput(sprintf('[%s:%d:] Passed assert().', __METHOD__, __LINE__));
+
+               // Check length of count and seek position
+               assert(strlen($this->header[1]) == self::LENGTH_COUNT);
+               //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput(sprintf('[%s:%d:] Passed assert().', __METHOD__, __LINE__));
+               assert(strlen($this->header[2]) == self::LENGTH_POSITION);
+               //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput(sprintf('[%s:%d:] Passed assert().', __METHOD__, __LINE__));
+
+               // Decode count and seek position
+               $this->header[1] = hex2bin($this->header[1]);
+               $this->header[2] = hex2bin($this->header[2]);
+
+               //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput(sprintf('[%s:%d:] EXIT!', __METHOD__, __LINE__));
        }
 
        /**
@@ -133,19 +224,31 @@ class BaseFileStack extends BaseStacker {
         * @return      $isInitialized  Whether the file header is initialized
         */
        private function isFileHeaderInitialized () {
+               //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput(sprintf('[%s:%d:] CALLED!', __METHOD__, __LINE__));
                // Default is not initialized
                $isInitialized = FALSE;
 
                // Is the file initialized?
                if ($this->isFileInitialized()) {
                        // Some bytes has been written, so rewind to start of it.
-                       $this->getIteratorInstance()->rewind();
+                       $rewindStatus = $this->getIteratorInstance()->rewind();
+                       //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput(sprintf('[%s:%d:] rewindStatus=%s', __METHOD__, __LINE__, $rewindStatus));
+
+                       // Is the rewind() call successfull?
+                       if ($rewindStatus != 1) {
+                               // Something bad happened
+                               self::createDebugInstance(__CLASS__)->debugOutput(sprintf('[%s:%d:] Could not rewind().', __METHOD__, __LINE__));
+                       } // END - if
 
                        // Read file header
                        $this->readFileHeader();
+
+                       // The above method does already check the header
+                       $isInitialized = TRUE;
                } // END - if
 
                // Return result
+               //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput(sprintf('[%s:%d:] isInitialized=%d - EXIT!', __METHOD__, __LINE__, intval($isInitialized)));
                return $isInitialized;
        }
 
@@ -155,8 +258,11 @@ class BaseFileStack extends BaseStacker {
         * @return      $isInitialized          Whether the file's size is zero
         */
        private function isFileInitialized () {
+               //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput(sprintf('[%s:%d:] CALLED!', __METHOD__, __LINE__));
+
                // Get it from iterator which holds the pointer instance. If FALSE is returned
                $fileSize = $this->getIteratorInstance()->size();
+               //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput(sprintf('[%s:%d:] fileSize=%s', __METHOD__, __LINE__, $fileSize));
 
                /*
                 * The returned file size should not be FALSE or NULL as this means
@@ -168,6 +274,7 @@ class BaseFileStack extends BaseStacker {
                $isInitialized = ($fileSize > 0);
 
                // Return result
+               //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput(sprintf('[%s:%d:] isInitialized=%d - EXIT!', __METHOD__, __LINE__, intval($isInitialized)));
                return $isInitialized;
        }
 
@@ -177,11 +284,14 @@ class BaseFileStack extends BaseStacker {
         * @return      void
         */
        private function createFileHeader () {
+               //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput(sprintf('[%s:%d:] CALLED!', __METHOD__, __LINE__));
                // The file's header should not be initialized here
                assert(!$this->isFileHeaderInitialized());
 
-               // Flush file header
+               // Simple flush file header which will create it.
                $this->flushFileHeader();
+
+               //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput(sprintf('[%s:%d:] EXIT!!', __METHOD__, __LINE__));
        }
 
        /**
@@ -190,29 +300,89 @@ class BaseFileStack extends BaseStacker {
         * @return      void
         */
        private function flushFileHeader () {
+               //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput(sprintf('[%s:%d:] CALLED!', __METHOD__, __LINE__));
+
                // Put all informations together
-               $header = sprintf('%s%s%s%s%s',
+               $header = sprintf('%s%s%s%s%s%s',
                        // Magic
                        self::STACK_MAGIC,
 
                        // Separator magic<->count
-                       chr(self::SEPARATOR_MAGIC_COUNT),
+                       chr(self::SEPARATOR_HEADER_DATA),
 
                        // Total entries (will be zero) and pad it to 20 chars
-                       str_pad($this->dec2hex($this->getCounter()), self::COUNT_LENGTH, '0', STR_PAD_LEFT),
+                       str_pad($this->dec2hex($this->getCounter()), self::LENGTH_COUNT, '0', STR_PAD_LEFT),
+
+                       // Separator count<->seek position
+                       chr(self::SEPARATOR_HEADER_DATA),
 
                        // Position (will be zero)
-                       str_pad($this->dec2hex(0, 2), self::COUNT_POSITION, '0', STR_PAD_LEFT),
+                       str_pad($this->dec2hex($this->getSeekPosition(), 2), self::LENGTH_POSITION, '0', STR_PAD_LEFT),
 
                        // Separator position<->entries
-                       chr(self::SEPARATOR_SEEK_POS_ENTRIES)
+                       chr(self::SEPARATOR_HEADER_ENTRIES)
                );
 
                // Write it to disk (header is always at seek position 0)
-               $this->getIteratorInstance()->writeAtPosition(0, $header);
+               $this->writeData(0, $header);
+
+               //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput(sprintf('[%s:%d:] EXIT!', __METHOD__, __LINE__));
+       }
+
+       /**
+        * Writes data at given position
+        *
+        * @param       $seekPosition   Seek position
+        * @param       $data                   Data to be written
+        * @return      void
+        */
+       private function writeData ($seekPosition, $data) {
+               //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput(sprintf('[%s:%d:] seekPosition=%s,data()=%s - CALLED!', __METHOD__, __LINE__, $seekPosition, strlen($data)));
+
+               // Write data at given position
+               $this->getIteratorInstance()->writeAtPosition($seekPosition, $data);
 
                // Update seek position
                $this->updateSeekPosition();
+
+               //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput(sprintf('[%s:%d:] EXIT!', __METHOD__, __LINE__));
+       }
+
+       /**
+        * Pre-allocates file (if enabled) with some space for later faster write access.
+        *
+        * @return      void
+        */
+       private function preAllocateFile () {
+               //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput(sprintf('[%s:%d:] CALLED!', __METHOD__, __LINE__));
+
+               // Is it enabled?
+               if ($this->getConfigInstance()->getConfigEntry('file_stack_pre_allocate_enabled') != 'Y') {
+                       // Not enabled
+                       self::createDebugInstance(__CLASS__)->debugOutput(sprintf('[%s:%d:] Not pre-allocating stack file.', __METHOD__, __LINE__));
+
+                       // Don't continue here.
+                       return;
+               } // END - if
+
+               // Message to user
+               self::createDebugInstance(__CLASS__)->debugOutput(sprintf('[%s:%d:] Pre-allocating stack file ...', __METHOD__, __LINE__));
+
+               /*
+                * Calculate minimum length for one entry:
+                * minimum length = hash length + separator + name + minimum entry size = ?? + 1 + 10 + 1 = ??
+                */
+               $minLengthEntry = self::getHashLength() + strlen(self::SEPARATOR_HASH_NAME) + self::LENGTH_NAME + 1;
+               //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput(sprintf('[%s:%d:] minLengthEntry=%s', __METHOD__, __LINE__, $minLengthEntry));
+
+               // Calulcate seek position
+               $seekPosition = $minLengthEntry * $this->getConfigInstance()->getConfigEntry('file_stack_pre_allocate_count');
+               //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->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));
+
+               //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput(sprintf('[%s:%d:] EXIT!', __METHOD__, __LINE__));
        }
 
        /**