Fixed: Fatal error: Access to undeclared static property: BaseFrameworkSystem::$hashL...
[core.git] / inc / classes / main / file_directories / class_BaseFile.php
index c596fcb4a31f4ce8891b9cb9cc9b12b00fae5e29..806745d11c2a8716d35a7cda0ac08ec80005baa8 100644 (file)
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 class BaseFile extends BaseFrameworkSystem {
+       /**
+        * Separator for header data
+        */
+       const SEPARATOR_HEADER_DATA = 0x01;
+
+       /**
+        * Separator header->entries
+        */
+       const SEPARATOR_HEADER_ENTRIES = 0x02;
+
+       /**
+        * Separator hash->name
+        */
+       const SEPARATOR_HASH_NAME = 0x03;
+
+       /**
+        * Separator entry->entry
+        */
+       const SEPARATOR_ENTRIES = 0x04;
+
+       /**
+        * Separator type->position
+        */
+       const SEPARATOR_TYPE_POSITION = 0x05;
+
+       /**
+        * Length of count
+        */
+       const LENGTH_COUNT = 20;
+
+       /**
+        * Length of position
+        */
+       const LENGTH_POSITION = 20;
+
+       /**
+        * Length of name
+        */
+       const LENGTH_NAME = 10;
+
+       /**
+        * Maximum length of entry type
+        */
+       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';
+
+       /**
+        * Counter for total entries
+        */
+       private $totalEntries = 0;
+
+       /**
+        * Current seek position
+        */
+       private $seekPosition = 0;
+
+       /**
+        * Size of header
+        */
+       private $headerSize = 0;
+
+       /**
+        * File header
+        */
+       private $header = array();
+
+       /**
+        * Seek positions for gaps ("fragmentation")
+        */
+       private $gaps = array();
+
+       /**
+        * Seek positions for damaged entries (e.g. mismatching hash sum, ...)
+        */
+       private $damagedEntries = array();
+
        /**
         * The current file we are working in
         */
@@ -64,6 +151,188 @@ y   * @return      void
                parent::__destruct();
        }
 
+       /**
+        * 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.
+        *
+        * @return      void
+        */
+       public function initCountersGapsArray () {
+               // Init counter and seek position
+               $this->setCounter(0);
+               $this->setSeekPosition(0);
+
+               // Init arrays
+               $this->gaps = array();
+               $this->damagedEntries = array();
+       }
+
+       /**
+        * Getter for total entries
+        *
+        * @return      $totalEntries   Total entries in this file
+        */
+       protected final function getCounter () {
+               // Get it
+               return $this->totalEntries;
+       }
+
+       /**
+        * Setter for total entries
+        *
+        * @param       $totalEntries   Total entries in this file
+        * @return      void
+        */
+       protected final function setCounter ($counter) {
+               // Set it
+               $this->totalEntries = $counter;
+       }
+
+       /**
+        * Increment counter
+        *
+        * @return      void
+        */
+       protected final function incrementCounter () {
+               // Get it
+               $this->totalEntries++;
+       }
+
+       /**
+        * Getter for header size
+        *
+        * @return      $totalEntries   Size of file header
+        */
+       public final function getHeaderSize () {
+               // Get it
+               return $this->headerSize;
+       }
+
+       /**
+        * Setter for header size
+        *
+        * @param       $headerSize             Size of file header
+        * @return      void
+        */
+       public final function setHeaderSize ($headerSize) {
+               // Set it
+               $this->headerSize = $headerSize;
+       }
+
+       /**
+        * Getter for header array
+        *
+        * @return      $totalEntries   Size of file header
+        */
+       public final function getHeader () {
+               // Get it
+               return $this->header;
+       }
+
+       /**
+        * Setter for header
+        *
+        * @param       $header         Array for a file header
+        * @return      void
+        */
+       public final function setHeader (array $header) {
+               // Set it
+               $this->header = $header;
+       }
+
+       /**
+        * Getter for seek position
+        *
+        * @return      $seekPosition   Current seek position (stored here in object)
+        */
+       protected final function getSeekPosition () {
+               // Get it
+               return $this->seekPosition;
+       }
+
+       /**
+        * Setter for seek position
+        *
+        * @param       $seekPosition   Current seek position (stored here in object)
+        * @return      void
+        */
+       protected final function setSeekPosition ($seekPosition) {
+               // And set it
+               $this->seekPosition = $seekPosition;
+       }
+
+       /**
+        * Updates seekPosition attribute from file to avoid to much access on file.
+        *
+        * @return      void
+        */
+       public function updateSeekPosition () {
+               //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput(sprintf('[%s:%d:] CALLED!', __METHOD__, __LINE__));
+
+               // Get key (= seek position)
+               $seekPosition = $this->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__));
+       }
+
+       /**
+        * Seeks to beginning of file, updates seek position in this object and
+        * flushes the header.
+        *
+        * @return      void
+        */
+       protected function rewindUpdateSeekPosition () {
+               //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput(sprintf('[%s:%d:] CALLED!', __METHOD__, __LINE__));
+
+               // flushFileHeader must be callable
+               assert(is_callable(array($this, 'flushFileHeader')));
+
+               // Seek to beginning of file
+               $this->rewind();
+
+               // And update seek position ...
+               $this->updateSeekPosition();
+
+               // ... to write it back into the file
+               $this->flushFileHeader();
+
+               //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput(sprintf('[%s:%d:] EXIT!!', __METHOD__, __LINE__));
+       }
+
+       /**
+        * Seeks to old position
+        *
+        * @return      void
+        */
+       protected function seekToOldPosition () {
+               //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput(sprintf('[%s:%d:] CALLED!', __METHOD__, __LINE__));
+
+               // Seek to currently ("old") saved position
+               $this->seek($this->getSeekPosition());
+
+               //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput(sprintf('[%s:%d:] EXIT!!', __METHOD__, __LINE__));
+       }
+
+       /**
+        * Checks whether the block separator has been found
+        *
+        * @param       $str            String to look in
+        * @return      $isFound        Whether the block separator has been found
+        */
+       public static function isBlockSeparatorFound ($str) {
+               // Determine it
+               $isFound = (strpos($str, chr(self::SEPARATOR_ENTRIES)) !== FALSE);
+
+               // Return result
+               return $isFound;
+       }
+
        /**
         * Getter for the file pointer
         *
@@ -194,6 +463,23 @@ y   * @return      void
                //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput(sprintf('[%s:%d:] EXIT!', __METHOD__, __LINE__));
        }
 
+       /**
+        * Marks the currently loaded block as empty (with length of the block)
+        *
+        * @param       $length         Length of the block
+        * @return      void
+        */
+       protected function markCurrentBlockAsEmpty ($length) {
+               // Get current seek position
+               $currentPosition = $this->key();
+
+               // Now add it as gap entry
+               array_push($this->gaps, array(
+                       self::GAPS_INDEX_START  => ($currentPosition - $length),
+                       self::GAPS_INDEX_END    => $currentPosition,
+               ));
+       }
+
        /**
         * Checks whether the file header is initialized
         *
@@ -208,7 +494,7 @@ y    * @return      void
                // Is the file initialized?
                if ($this->isFileInitialized()) {
                        // Some bytes has been written, so rewind to start of it.
-                       $rewindStatus = $this->getIteratorInstance()->rewind();
+                       $rewindStatus = $this->rewind();
                        //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput(sprintf('[%s:%d:] rewindStatus=%s', __METHOD__, __LINE__, $rewindStatus));
 
                        // Is the rewind() call successfull?
@@ -218,7 +504,7 @@ y    * @return      void
                        } // END - if
 
                        // Read file header
-                       $this->getBlockInstance()->readFileHeader();
+                       $this->readFileHeader();
 
                        // The above method does already check the header
                        $isInitialized = TRUE;
@@ -267,10 +553,10 @@ y  * @return      void
                assert(!$this->isFileHeaderInitialized());
 
                // Simple flush file header which will create it.
-               $this->getBlockInstance()->flushFileHeader();
+               $this->flushFileHeader();
 
                // Rewind seek position (to beginning of file) and update/flush file header
-               $this->rewineUpdateSeekPosition();
+               $this->rewindUpdateSeekPosition();
 
                //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput(sprintf('[%s:%d:] EXIT!!', __METHOD__, __LINE__));
        }
@@ -308,7 +594,7 @@ y    * @return      void
                $this->writeData($seekPosition, chr(0));
 
                // Rewind seek position (to beginning of file) and update/flush file header
-               $this->rewineUpdateSeekPosition();
+               $this->rewindUpdateSeekPosition();
 
                //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput(sprintf('[%s:%d:] EXIT!', __METHOD__, __LINE__));
        }
@@ -460,7 +746,6 @@ y    * @return      void
         * Advances to next "block" of bytes
         *
         * @return      void
-        * @todo        This method will load large but empty files in a whole
         */
        public function next () {
                // Is there nothing to read?
@@ -478,17 +763,37 @@ y  * @return      void
                // Short be more than zero!
                assert($length > 0);
 
-               // Wait until a entry/block separator has been found
+               // Read possibly back-buffered bytes from previous call of next().
                $data = $this->getBackBuffer();
+
+               /*
+                * Read until a entry/block separator has been found. The next read
+                * "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))) {
-                       // Then read the block
-                       $data .= $this->read($length);
-                       //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('data()=' . strlen($data));
+                       // Then read the next possible block
+                       $block = $this->read($length);
+
+                       // Is it all empty?
+                       if (strlen(trim($block)) == 0) {
+                               // Mark this block as empty
+                               $this->markCurrentBlockAsEmpty($length);
+
+                               // Skip to next block
+                               continue;
+                       } // END - if
+
+                       // At this block then
+                       $data .= $block;
+
+                       // A debug message
+                       //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput(sprintf('[%s:%d] data()=%s', __FUNCTION__, __LINE__, strlen($data)));
                } // END - if
 
                // EOF reached?
                if ($this->isEndOfFileReached()) {
-                       // Set whole data as current block
+                       // Set whole data as current read block
                        //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('Calling setCurrentBlock(' . strlen($data) . ') ...');
                        $this->setCurrentBlock($data);
 
@@ -504,7 +809,7 @@ y    * @return      void
                $this->initBackBuffer();
 
                // Separate data
-               $dataArray = explode(self::getBlockSeparator(), $data);
+               $dataArray = explode(chr(self::SEPARATOR_ENTRIES), $data);
 
                // This array must contain two elements
                //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('dataArray=' . print_r($dataArray, TRUE));
@@ -523,7 +828,7 @@ y    * @return      void
         */
        public function valid () {
                // Make sure the block instance is set
-               assert($this->getBlockInstance() instanceof CalculatableBlock);
+               assert($this->getBlockInstance() instanceof Block);
 
                // First calculate minimum block length
                $length = $this->getBlockInstance()->calculateMinimumBlockLength();
@@ -565,6 +870,32 @@ y   * @return      void
                // Call pointer instance
                return $this->getPointerInstance()->determineSeekPosition();
        }
+
+       /**
+        * Reads the file header
+        *
+        * @return      void
+        */
+       public function readFileHeader () {
+               // Make sure the block instance is set
+               assert($this->getBlockInstance() instanceof Block);
+
+               // Call block instance
+               $this->getBlockInstance()->readFileHeader();
+       }
+
+       /**
+        * Flushes the file header
+        *
+        * @return      void
+        */
+       public function flushFileHeader () {
+               // Make sure the block instance is set
+               assert($this->getBlockInstance() instanceof Block);
+
+               // Call block instance
+               $this->getBlockInstance()->flushFileHeader();
+       }
 }
 
 // [EOF]