]> git.mxchange.org Git - hub.git/commitdiff
Continued with package fragmenter (a lot new, but unfinished code)
authorRoland Häder <roland@mxchange.org>
Sun, 17 Apr 2011 22:08:24 +0000 (22:08 +0000)
committerRoland Häder <roland@mxchange.org>
Sun, 17 Apr 2011 22:08:24 +0000 (22:08 +0000)
12 files changed:
.gitattributes
application/hub/config.php
application/hub/interfaces/package/fragmenter/class_Fragmentable.php
application/hub/main/database/wrapper/states/class_PeerStateLookupDatabaseWrapper.php
application/hub/main/helper/connection/class_BaseConnectionHelper.php
application/hub/main/package/class_NetworkPackage.php
application/hub/main/package/fragmenter/class_PackageFragmenter.php
application/hub/main/streams/.htaccess [new file with mode: 0644]
application/hub/main/streams/package/.htaccess [new file with mode: 0644]
application/hub/main/streams/package/input/.htaccess [new file with mode: 0644]
application/hub/main/streams/package/output/.htaccess [new file with mode: 0644]
application/hub/main/streams/package/output/class_PackageOutputStream.php [new file with mode: 0644]

index c046a648d4f50846d3c98cf24847bef0586f6575..7327360bcc98e1c146179b8ae24b55956fcd01b7 100644 (file)
@@ -459,6 +459,11 @@ application/hub/main/states/peer/class_ -text svneol=unset#text/plain
 application/hub/main/states/peer/class_BasePeerState.php -text svneol=unset#text/plain
 application/hub/main/states/peer/new/.htaccess -text svneol=unset#text/plain
 application/hub/main/states/peer/new/class_NewConnectionPeerState.php svneol=native#text/plain
+application/hub/main/streams/.htaccess svneol=native#text/plain
+application/hub/main/streams/package/.htaccess svneol=native#text/plain
+application/hub/main/streams/package/input/.htaccess svneol=native#text/plain
+application/hub/main/streams/package/output/.htaccess svneol=native#text/plain
+application/hub/main/streams/package/output/class_PackageOutputStream.php svneol=native#text/plain
 application/hub/main/tags/.htaccess -text svneol=unset#text/plain
 application/hub/main/tags/class_ -text svneol=unset#text/plain
 application/hub/main/tags/class_BaseTags.php svneol=native#text/plain
index 450c7916708d61a4e2d1dc7abb13ec895261ea2a..738f88b9e46c8fe2711a4f649ab7b19975691972 100644 (file)
@@ -552,6 +552,12 @@ $cfg->setConfigEntry('external_ip', '');
 // CFG: PACKAGE-FRAGMENTER-CLASS
 $cfg->setConfigEntry('package_fragmenter_class', 'PackageFragmenter');
 
+// CFG: NODE-RAW-PACKAGE-OUTPUT-STREAM
+$cfg->setConfigEntry('node_raw_package_output_stream', 'PackageOutputStream');
+
+// CFG: PACKAGE-CHUNK-SIZE
+$cfg->setConfigEntry('package_chunk_size', 8*512);
+
 // CFG: CRUNCHER-TEST-UNITS-ENABLED
 $cfg->setConfigEntry('cruncher_test_units_enabled', 'Y');
 
index b511efd6647ed5ca9462c018b975bde53d76c271..5a3ebe80d54633187bf96c9e05bb66ff933f808a 100644 (file)
@@ -25,22 +25,24 @@ interface Fragmentable extends FrameworkInterface {
        /**
         * This method does "implode" the given package data array into one long
         * string, splits it into small chunks, adds a serial number and checksum
-        * to all chunks and prepends a final hashsum chunk.
+        * to all chunks and prepends a final hashsum chunk. It will return the
+        * final hash for faster processing of packages.
         *
         * @param       $packageData                    Raw package data array
         * @param       $connectionInstance             A helper instance for connections
-        * @return      void
+        * @return      $finalHash                              Final hash for faster processing
         */
        function fragmentPackageArray (array $packageData, BaseConnectionHelper $connectionInstance);
 
        /**
         * This method gets the next chunk from the internal FIFO which should be
-        * sent to the given recipient.
+        * sent to the given recipient. It will return an associative array where
+        * the key is the chunk hash and value the raw chunk data.
         *
-        * @param       $packageData    Raw package data array
+        * @param       $finalHash              Final hash for faster lookup
         * @return      $rawDataChunk   Raw package data chunk
         */
-       function getNextRawDataChunk (array $packageData);
+       function getNextRawDataChunk ($finalHash);
 }
 
 // [EOF]
index 86d4528671a9732f4c7d7428c81737950c2f8e89..7450d228f5f2b3d45a7af414f84fe945d91bf60f 100644 (file)
@@ -81,7 +81,7 @@ class PeerStateLookupDatabaseWrapper extends BaseDatabaseWrapper {
                // Is the package valid?
                if (!isset($packageData[NetworkPackage::INDEX_PACKAGE_SENDER])) {
                        // Invalid package found, please report this
-                       die('packageData='.print_r($packageData, true));
+                       die(__METHOD__ . ': packageData=' . print_r($packageData, true));
                } // END - if
 
                // Remove session id > IP:port
index 0f2ffcc01fd6f9dd05d03d67a858efeb1f385fbd..4c68a02512540e21ae54a98b593cdadce9136a74 100644 (file)
@@ -57,6 +57,16 @@ class BaseConnectionHelper extends BaseHubHelper implements Registerable, Protoc
         */
        private $shuttedDown = false;
 
+       /**
+        * Currently sent chunks
+        */
+       private $sentChunks = array();
+
+       /**
+        * Current final hash
+        */
+       private $currentFinalHash = '';
+
        /**
         * Protected constructor
         *
@@ -143,31 +153,38 @@ class BaseConnectionHelper extends BaseHubHelper implements Registerable, Protoc
         * "Getter" for raw data from a package array. A fragmenter is used which
         * will returns us only so many raw data which fits into the back buffer.
         * The rest is being held in a back-buffer and waits there for the next
-        * cycle and while be then sent. This is done by a FIFO.
+        * cycle and while be then sent.
         *
         * This method does 4 simple steps:
         * 1) Aquire fragmenter object instance from the factory
         * 2) Handle over the package data array to the fragmenter
-        * 3) Request a chunk (which "pops" the chunk from the fragmenter's FIFO)
-        * 4) Finally return the chunk to the caller
+        * 3) Request a chunk
+        * 4) Finally return the chunk (array) to the caller
         *
         * @param       $packageData    Raw package data array
-        * @return      $rawData                Raw package data bytes
+        * @return      $rawDataChunk   An array with the raw data as value and chunk hash as key
         */
        private function getRawDataFromPackageArray (array $packageData) {
-               // Get the fragmenter instance
-               $fragmenterInstance = ObjectFactory::createObjectByConfiguredName('package_fragmenter_class');
+               // If there is no fragmenter?
+               if (!Registry::getRegistry()->instanceExists('package_fragmenter')) {
+                       // Get the fragmenter instance
+                       $fragmenterInstance = ObjectFactory::createObjectByConfiguredName('package_fragmenter_class');
+
+                       // Add it to the registry
+                       Registry::getRegistry()->addInstance('package_fragmenter', $fragmenterInstance);
+               } else {
+                       // Get fragmenter from registry
+                       $fragmenterInstance = Registry::getRegistry()->getInstance('package_fragmenter');
+               }
 
-               // Implode the package data array and fragement the resulting string
-               $fragmenterInstance->fragmentPackageArray($packageData, $this);
+               // Implode the package data array and fragement the resulting string, returns the final hash
+               $this->currentFinalHash = $fragmenterInstance->fragmentPackageArray($packageData, $this);
 
-               // Get the next raw data chunk from the fragmenter's FIFO
-               $rawData = $fragmenterInstance->getNextRawDataChunk($packageData);
-               /* DEBUG: */ $this->debugOutput('rawData['.strlen($rawData).']='.$rawData);
-               /* DEBUG: */ die();
+               // Get the next raw data chunk from the fragmenter
+               $rawDataChunk = $fragmenterInstance->getNextRawDataChunk($this->currentFinalHash);
 
                // Return it
-               return $rawData;
+               return $rawDataChunk;
        }
 
        /**
@@ -179,7 +196,7 @@ class BaseConnectionHelper extends BaseHubHelper implements Registerable, Protoc
         */
        public function sendRawPackageData (array $packageData) {
                // Convert the package data array to a raw data stream
-               $rawData = $this->getRawDataFromPackageArray($packageData);
+               $rawDataChunk = $this->getRawDataFromPackageArray($packageData);
 
                // Get socket resource
                $socketResource = $this->getSocketResource();
index ddc95fe874eb8336a00ac4f3a535b2c74cdc6933..64d3a8f57bffd7fd4f8369aa2ee5a8ac19a12882 100644 (file)
@@ -105,6 +105,11 @@ class NetworkPackage extends BaseFrameworkSystem implements Deliverable, Registe
         */
        const NETWORK_TARGET_SELF = 'self';
 
+       /**
+        * TCP package size
+        */
+       const TCP_PACKAGE_SIZE = 512;
+
        /**
         * Protected constructor
         *
index 6d69683c2ed4216485db9674389e3a0fbc6dd2d1..e7d2349ff1ac9fbb61754b27057187a4fb0ca7e9 100644 (file)
  * You should have received a copy of the GNU General Public License
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
-class PackageFragmenter extends BaseFrameworkSystem implements Fragmentable {
+class PackageFragmenter extends BaseFrameworkSystem implements Fragmentable, Registerable {
+       /**
+        * Cached chunk size in bits
+        */
+       private $chunkSize = 0;
+
+       /**
+        * Array for chunks
+        */
+       private $chunks = array();
+
+       /**
+        * Array for chunk hashes
+        */
+       private $chunkHashes = array();
+
+       /**
+        * Array for chunk pointers
+        */
+       private $chunkPointers = array();
+
+       /**
+        * Array for processed packages
+        */
+       private $processedPackages = array();
+
+       /**
+        * Serial number
+        */
+       private $serialNumber = 0x00000000;
+
+       /**
+        * Length of largest possible serial number
+        */
+       private $maxSerialLength = 8;
+
+       /**
+        * Maximum possible serial number
+        */
+       private $maxSerialNumber  = 0;
+       /**
+        * Seperator between chunk data, serial number and chunk hash
+        */
+       const CHUNK_DATA_HASH_SEPERATOR = '@';
+
+       /**
+        * Seperator for all chunk hashes
+        */
+       const CHUNK_HASH_SEPERATOR = ';';
+
+       /**
+        * Identifier for hash chunk
+        */
+       const HASH_CHUNK_IDENTIFIER = 'HASH-CHUNK:';
+
        /**
         * Protected constructor
         *
@@ -33,6 +87,9 @@ class PackageFragmenter extends BaseFrameworkSystem implements Fragmentable {
        protected function __construct () {
                // Call parent constructor
                parent::__construct(__CLASS__);
+
+               // Init this fragmenter
+               $this->initFragmenter();
        }
 
        /**
@@ -44,30 +101,356 @@ class PackageFragmenter extends BaseFrameworkSystem implements Fragmentable {
                // Get new instance
                $fragmenterInstance = new PackageFragmenter();
 
+               // Get an output stream for all out-going packages
+               $streamInstance = ObjectFactory::createObjectByConfiguredName('node_raw_package_output_stream');
+
+               // And set it in this fragmenter
+               $fragmenterInstance->setOutputStreamInstance($streamInstance);
+
+               // And also a crypto instance (for our encrypted messages)
+               $cryptoInstance = ObjectFactory::createObjectByConfiguredName('crypto_class');
+               $fragmenterInstance->setCryptoInstance($cryptoInstance);
+
                // Return the prepared instance
                return $fragmenterInstance;
        }
 
+       /**
+        * Initializes this fragmenter
+        *
+        * @return      void
+        */
+       private function initFragmenter () {
+               // Load some configuration entries and "cache" them:
+               // - Chunk size in bits
+               $this->chunkSize = $this->getConfigInstance()->getConfigEntry('package_chunk_size');
+
+               // - Maximum serial number
+               $this->maxSerialNumber = $this->hex2dec(str_repeat('f', $this->maxSerialLength));
+       }
+
+       /**
+        * Initializes the pointer for given final hash
+        *
+        * @param       $finalHash      Final hash to initialize pointer for
+        * @return      void
+        */
+       private function initPointer ($finalHash) {
+               $this->chunkPointers[$finalHash] = 0;
+       }
+
+       /**
+        * "Getter" for processedPackages array index
+        *
+        * @param       $packageData    Raw package data array
+        * @return      $index                  Array index for processedPackages
+        */
+       private function getProcessedPackagesIndex (array $packageData) {
+               return (
+                       $packageData['sender']    . NetworkPackage::PACKAGE_DATA_SEPERATOR .
+                       $packageData['recipient'] . NetworkPackage::PACKAGE_DATA_SEPERATOR .
+                       $packageData['content']   . NetworkPackage::PACKAGE_DATA_SEPERATOR
+               );
+       }
+
+       /**
+        * Checks wether the given package data is already processed by this fragmenter
+        *
+        * @param       $packageData    Raw package data array
+        * @return      $isProcessed    Wether the package has been fragmented
+        */
+       private function isPackageProcessed (array $packageData) {
+               // Get array index
+               $index = $this->getProcessedPackagesIndex($packageData);
+
+               // Is the array index there?
+               $isProcessed = (
+                       (isset($this->processedPackages[$index])) &&
+                       ($this->processedPackages[$index] === true)
+               );
+
+               // Return it
+               return $isProcessed;
+       }
+
+       /**
+        * Marks the given package data as processed by this fragmenter
+        *
+        * @param       $packageData    Raw package data array
+        * @return      void
+        */
+       private function markPackageDataProcessed (array $packageData) {
+               // Remember it (until we may remove it)
+               $this->processedPackages[$this->getProcessedPackagesIndex($packageData)] = true;
+       }
+
+       /**
+        * Getter for final hash from given package data
+        *
+        * @param       $packageData    Raw package data array
+        * @return      $finalHash              Final hash for package data
+        */
+       private function getFinalHashFromPackageData (array $packageData) {
+               // Make sure it is there
+               assert(isset($this->processedPackages[$this->getProcessedPackagesIndex($packageData)]));
+
+               // Return it
+               return $this->processedPackages[$this->getProcessedPackagesIndex($packageData)];
+       }
+
+       /**
+        * Get next chunk pointer for given final hash
+        *
+        * @param       $finalHash      Final hash to get current pointer for
+        */
+       private function getCurrentChunkPointer ($finalHash) {
+               // Is the final hash valid?
+               assert(strlen($finalHash) > 0);
+
+               // Is the pointer already initialized?
+               //* NOISY-DEBUG: */ $this->debugOutput('FRAGMENTER: finalHash=' . $finalHash);
+               assert(isset($this->chunkPointers[$finalHash]));
+
+               // Return it
+               return $this->chunkPointers[$finalHash];
+       }
+
+       /**
+        * Advance the chunk pointer for given final hash
+        *
+        * @param       $finalHash      Final hash to advance the pointer for
+        */
+       private function nextChunkPointer ($finalHash) {
+               // Is the pointer already initialized?
+               assert(isset($this->chunkPointers[$finalHash]));
+
+               // Count one up
+               $this->chunkPointers[$finalHash]++;
+       }
+
+       /**
+        * "Getter" for data chunk size of given hash.
+        *
+        * @param       $hash                   Hash to substract it's length
+        * @return      $dataChunkSize  The chunk size
+        */
+       private function getDataChunkSizeFromHash ($hash) {
+               // Calculate real (data) chunk size
+               $dataChunkSize = (
+                       // Real chunk size
+                       ($this->chunkSize / 8) -
+                       // Hash size
+                       strlen($hash) -
+                       // Length of sperators
+                       (strlen(self::CHUNK_DATA_HASH_SEPERATOR) * 2) -
+                       // Length of max serial number
+                       $this->maxSerialLength
+               );
+
+               // This should be larger than zero bytes
+               assert($dataChunkSize > 0);
+
+               // Return it
+               return $dataChunkSize;
+       }
+
+       /**
+        * Generates a hash from raw data
+        *
+        * @param       $rawData        Raw data bytes to hash
+        * @return      $hash           Hash from the raw data
+        */
+       private function generateHashFromRawData ($rawData) {
+               // Get the crypto instance and hash the data
+               $hash = $this->getCryptoInstance()->hashString($rawData);
+
+               // Return it
+               return $hash;
+       }
+
+       /**
+        * "Getter" for the next hexadecimal-encoded serial number
+        *
+        * @return      $encodedSerialNumber    The next hexadecimal-encoded serial number
+        */
+       private function getNextHexSerialNumber () {
+               // Assert on maximum serial number length
+               assert($this->serialNumber <= $this->maxSerialNumber);
+
+               // Encode the current serial number
+               $encodedSerialNumber = $this->dec2Hex($this->serialNumber, $this->maxSerialLength);
+
+               // Count one up
+               $this->serialNumber++;
+
+               // Return the encoded serial number
+               return $encodedSerialNumber;
+       }
+
+       /**
+        * Splits the given encoded data into smaller chunks, the size of the final
+        * and the seperator is being subtracted from chunk size to fit it into a
+        * TCP package (512 bytes).
+        *
+        * @param       $encodedData    Encoded data string
+        * @param       $finalHash              Final hash from the raw data
+        * @return      void
+        */
+       private function splitEncodedDataIntoChunks ($encodedData, $finalHash) {
+               // Make sure final hashes with at least 32 bytes can pass
+               assert(strlen($finalHash) >= 32);
+
+               // Calculate real (data) chunk size
+               $dataChunkSize = $this->getDataChunkSizeFromHash($finalHash);
+
+               // Now split it up
+               for ($idx = 0; $idx < strlen($encodedData); $idx += $dataChunkSize) {
+                       // Get the next chunk
+                       $chunk = substr($encodedData, $idx, $dataChunkSize);
+
+                       // Hash it and remember it in seperate array
+                       $chunkHash = $this->getCryptoInstance()->hashString($chunk);
+                       $this->chunkHashes[$finalHash][] = $chunkHash;
+
+                       // Prepend the hash to the chunk
+                       $chunk = $chunkHash . self::CHUNK_DATA_HASH_SEPERATOR . $this->getNextHexSerialNumber() . self::CHUNK_DATA_HASH_SEPERATOR . $chunk;
+
+                       // Make sure the chunk is not larger than a TCP package can hold
+                       assert(strlen($chunk) <= NetworkPackage::TCP_PACKAGE_SIZE);
+
+                       // Add it to the array
+                       $this->chunks[$finalHash][] = $chunk;
+               } // END - for
+
+               // Debug output
+               $this->debugOutput('FRAGMENTER: Encoded data of ' . strlen($encodedData) . ' bytes has been fragmented into ' . count($this->chunks[$finalHash]) . ' chunk(s).');
+       }
+
+       /**
+        * Prepends a chunk (or more) with all hashes from all chunks + final chunk.
+        *
+        * @param       $encodedData    Encoded data string
+        * @param       $finalHash              Final hash from the raw data
+        * @return      void
+        */
+       private function prependHashChunk ($encodedData, $finalHash) {
+               // "Implode" the whole array of hashes into one string
+               $rawData = self::HASH_CHUNK_IDENTIFIER . implode(self::CHUNK_HASH_SEPERATOR, $this->chunkHashes[$finalHash]);
+
+               // Also get a hash from it
+               $chunkHash = $this->generateHashFromRawData($rawData);
+
+               // Also encode this one
+               $encodedData = $this->getOutputStreamInstance()->streamData($rawData);
+
+               // Calulcate chunk size
+               $dataChunkSize = $this->getDataChunkSizeFromHash($chunkHash);
+
+               // Now array_unshift() it to the two chunk arrays
+               for ($idx = 0; $idx < strlen($encodedData); $idx += $dataChunkSize) {
+                       // Get the next chunk
+                       $chunk = substr($encodedData, $idx, $dataChunkSize);
+
+                       // Hash it and remember it in seperate array
+                       $chunkHash = $this->getCryptoInstance()->hashString($chunk);
+                       array_unshift($this->chunkHashes[$finalHash], $chunkHash);
+
+                       // Prepend the hash to the chunk
+                       $chunk = $chunkHash . self::CHUNK_DATA_HASH_SEPERATOR . $this->getNextHexSerialNumber() . self::CHUNK_DATA_HASH_SEPERATOR . $chunk;
+
+                       // Make sure the chunk is not larger than a TCP package can hold
+                       assert(strlen($chunk) <= NetworkPackage::TCP_PACKAGE_SIZE);
+
+                       // Add it to the array
+                       array_unshift($this->chunks[$finalHash], $chunk);
+               } // END - for
+       }
+
        /**
         * This method does "implode" the given package data array into one long
         * string, splits it into small chunks, adds a serial number and checksum
-        * to all chunks and prepends a final hashsum chunk.
+        * to all chunks and prepends a chunk with all hashes only in it. It will
+        * return the final hash for faster processing of packages.
         *
         * @param       $packageData                    Raw package data array
         * @param       $connectionInstance             A helper instance for connections
-        * @return      void
+        * @return      $finalHash                              Final hash for faster processing
+        * @todo        $connectionInstance is unused
         */
        public function fragmentPackageArray (array $packageData, BaseConnectionHelper $connectionInstance) {
+               // Is this package already fragmented?
+               if (!$this->isPackageProcessed($packageData)) {
+                       // First we need to "implode" the array
+                       $rawData = implode(NetworkPackage::PACKAGE_DATA_SEPERATOR, $packageData);
+
+                       // Generate the final hash from the raw data (not encoded!)
+                       $finalHash = $this->generateHashFromRawData($rawData);
+
+                       // Remember it
+                       $this->processedPackages[$this->getProcessedPackagesIndex($packageData)] = $finalHash;
+
+                       // Init pointer
+                       $this->initPointer($finalHash);
+
+                       // Encode the package for delivery
+                       $encodedData = $this->getOutputStreamInstance()->streamData($rawData);
+
+                       // Split the encoded data into smaller chunks
+                       $this->splitEncodedDataIntoChunks($encodedData, $finalHash);
+
+                       // Prepend a chunk with all hashes together
+                       $this->prependHashChunk($encodedData, $finalHash);
+
+                       // Mark the package as fragmented
+                       $this->markPackageDataProcessed($packageData);
+               } else {
+                       // Get the final hash from the package data
+                       $finalHash = $this->getFinalHashFromPackageData($packageData);
+               }
+
+               // Return final hash
+               return $finalHash;
        }
 
        /**
         * This method gets the next chunk from the internal FIFO which should be
-        * sent to the given recipient.
+        * sent to the given recipient. It will return an associative array where
+        * the key is the chunk hash and value the raw chunk data.
         *
-        * @param       $packageData    Raw package data array
+        * @param       $finalHash              Final hash for faster lookup
         * @return      $rawDataChunk   Raw package data chunk
         */
-       public function getNextRawDataChunk (array $packageData) {
+       public function getNextRawDataChunk ($finalHash) {
+               try {
+                       // Get current chunk index
+                       $current = $this->getCurrentChunkPointer($finalHash);
+               } catch (AssertionException $e) {
+                       // This may happen when the final hash is true
+                       if ($finalHash === true) {
+                               // Set current to null
+                               $current = null;
+                       } else {
+                               // Throw the exception
+                               throw $e;
+                       }
+               }
+
+               // If there is no entry left, return an empty array
+               if ((!isset($this->chunkHashes[$finalHash][$current])) || (!isset($this->chunks[$finalHash][$current]))) {
+                       // No more entries found
+                       return array();
+               } // END - if
+
+               // Generate the array
+               $rawDataChunk = array(
+                       $this->chunkHashes[$finalHash][$current] => $this->chunks[$finalHash][$current]
+               );
+
+               // Count one index up
+               $this->nextChunkPointer($finalHash);
+
+               // Return the chunk array
+               return $rawDataChunk;
        }
 }
 
diff --git a/application/hub/main/streams/.htaccess b/application/hub/main/streams/.htaccess
new file mode 100644 (file)
index 0000000..3a42882
--- /dev/null
@@ -0,0 +1 @@
+Deny from all
diff --git a/application/hub/main/streams/package/.htaccess b/application/hub/main/streams/package/.htaccess
new file mode 100644 (file)
index 0000000..3a42882
--- /dev/null
@@ -0,0 +1 @@
+Deny from all
diff --git a/application/hub/main/streams/package/input/.htaccess b/application/hub/main/streams/package/input/.htaccess
new file mode 100644 (file)
index 0000000..3a42882
--- /dev/null
@@ -0,0 +1 @@
+Deny from all
diff --git a/application/hub/main/streams/package/output/.htaccess b/application/hub/main/streams/package/output/.htaccess
new file mode 100644 (file)
index 0000000..3a42882
--- /dev/null
@@ -0,0 +1 @@
+Deny from all
diff --git a/application/hub/main/streams/package/output/class_PackageOutputStream.php b/application/hub/main/streams/package/output/class_PackageOutputStream.php
new file mode 100644 (file)
index 0000000..af98e5e
--- /dev/null
@@ -0,0 +1,65 @@
+<?php
+/**
+ * A PackageOutputStream class
+ *
+ * @author             Roland Haeder <webmaster@ship-simu.org>
+ * @version            0.0.0
+ * @copyright  Copyright (c) 2007, 2008 Roland Haeder, 2009  Developer Team
+ * @license            GNU GPL 3.0 or any newer version
+ * @link               http://www.ship-simu.org
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+class PackageOutputStream extends BaseStream implements OutputStreamable {
+       /**
+        * Protected constructor
+        *
+        * @return      void
+        */
+       protected function __construct () {
+               // Call parent constructor
+               parent::__construct(__CLASS__);
+       }
+
+       /**
+        * Creates an instance of this node class
+        *
+        * @return      $streamInstance         An instance of this node class
+        */
+       public final static function createPackageOutputStream () {
+               // Get a new instance
+               $streamInstance = new PackageOutputStream();
+
+               // Return the instance
+               return $streamInstance;
+       }
+
+       /**
+        * Streams the data and maybe does something to it
+        *
+        * @param       $data   The data (string mostly) to "stream"
+        * @return      $data   The data (string mostly) to "stream"
+        * @throws      UnsupportedOperationException   If this method is called
+        */
+       public function streamData ($data) {
+               // Encode the data with BASE64 encoding
+               $data = base64_encode($data);
+
+               // Return it
+               return $data;
+       }
+}
+
+// [EOF]
+?>