* Also an "end-of-package" marker is being added as the last chunk to mark the
* end of of the whole package submission.
*
- * @author Roland Haeder <webmaster@ship-simu.org>
+ * @author Roland Haeder <webmaster@shipsimu.org>
* @version 0.0.0
- * @copyright Copyright (c) 2007, 2008 Roland Haeder, 2009 - 2011 Hub Developer Team
+ * @copyright Copyright (c) 2007, 2008 Roland Haeder, 2009 - 2012 Hub Developer Team
* @license GNU GPL 3.0 or any newer version
- * @link http://www.ship-simu.org
+ * @link http://www.shipsimu.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
* 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, Registerable {
+class PackageFragmenter extends BaseHubSystem implements Fragmentable, Registerable {
/**
* Cached chunk size in bits
*/
private $processedPackages = array();
/**
- * Serial number
+ * Serial numbers (array key is final hash)
*/
- private $serialNumber = 0x00000000;
+ private $serialNumber = array();
/**
- * Maximum possible serial number
+ * Maximum possible serial number, "cache" for speeding up things
*/
private $maxSerialNumber = 0;
// Get new instance
$fragmenterInstance = new PackageFragmenter();
- // And also a crypto instance (for our encrypted messages)
+ // Get a crypto instance and set it here
$cryptoInstance = ObjectFactory::createObjectByConfiguredName('crypto_class');
$fragmenterInstance->setCryptoInstance($cryptoInstance);
}
/**
- * Checks wether the given package data is already processed by this fragmenter
+ * Checks whether the given package data is already processed by this fragmenter
*
* @param $packageData Raw package data array
- * @return $isProcessed Wether the package has been fragmented
+ * @return $isProcessed Whether the package has been fragmented
*/
private function isPackageProcessed (array $packageData) {
// Get array index
$isProcessed = (
(isset($this->processedPackages[$index]))
&&
- ($this->processedPackages[$index] === true)
+ ($this->processedPackages[$index] === TRUE)
);
// Return it
*/
private function markPackageDataProcessed (array $packageData) {
// Remember it (until we may remove it)
- $this->processedPackages[$this->getProcessedPackagesIndex($packageData)] = true;
+ $this->processedPackages[$this->getProcessedPackagesIndex($packageData)] = TRUE;
}
/**
assert(strlen($finalHash) > 0);
// Is the pointer already initialized?
- //* NOISY-DEBUG: */ $this->debugOutput('FRAGMENTER: finalHash=' . $finalHash);
+ //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('PACKAGE-FRAGMENTER[' . __METHOD__ . ':' . __LINE__ . ']: finalHash[' . gettype($finalHash) . ']=' . $finalHash);
assert(isset($this->chunkPointers[$finalHash]));
// Return it
assert(isset($this->chunkPointers[$finalHash]));
// Count one up
+ //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('PACKAGE-FRAGMENTER[' . __METHOD__ . ':' . __LINE__ . ']: finalHash[' . gettype($finalHash) . ']=' . $finalHash);
$this->chunkPointers[$finalHash]++;
}
* Get the crypto instance and hash the data with no extra salt because
* the other peer doesn't have *this* peer's salt.
*/
- $hash = $this->getCryptoInstance()->hashString($rawData, '', false);
+ $hash = $this->getCryptoInstance()->hashString($rawData, '', FALSE);
// 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, self::MAX_SERIAL_LENGTH);
-
- // Count one up
- $this->serialNumber++;
-
- // Return the encoded serial number
- return $encodedSerialNumber;
- }
-
/**
* Appends an end-of-package chunk to the chunk list for given chunk and
* final hash. As of 23-March-2012 the format of this chunk will be as any
* regular one to keep things easy (KISS) in ChunkHandler class.
*
- * @param $chunkHash Last chunk's hash
+ * @param $lastChunk Last chunk raw data
* @param $finalHash Final hash for raw (unencoded) data
* @return void
*/
- private function appendEndOfPackageChunk ($chunkHash, $finalHash) {
+ private function appendEndOfPackageChunk ($lastChunk, $finalHash) {
// Generate end-of-package marker
$chunkData =
self::END_OF_PACKAGE_IDENTIFIER .
$finalHash . self::CHUNK_HASH_SEPARATOR .
- $chunkHash . self::CHUNK_SEPARATOR;
+ $this->generateHashFromRawData($lastChunk);
+
+ // Debug message
+ //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('PACKAGE-FRAGMENTER[' . __METHOD__ . ':' . __LINE__ . ']: Adding EOP chunk with size of ' . strlen($chunkData) . ',finalHash=' . $finalHash . ' ...');
// Add it as regular chunk
- $this->addChunkData($chunkData);
+ $this->addChunkData($finalHash, $chunkData);
}
/**
// Calculate real (data) chunk size
$dataChunkSize = $this->getDataChunkSizeFromHash($finalHash);
- //* NOISY-DEBUG: */ $this->debugOutput('FRAGMENTER: dataChunkSize=' . $dataChunkSize);
+ //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('PACKAGE-FRAGMENTER[' . __METHOD__ . ':' . __LINE__ . ']: dataChunkSize=' . $dataChunkSize);
// Init variables
$chunkHash = '';
+ $chunkData = '';
// Now split it up
for ($idx = 0; $idx < strlen($rawData); $idx += $dataChunkSize) {
$chunkData = substr($rawData, $idx, $dataChunkSize);
// Add the chunk to the propper array and do all the stuff there
- $this->addChunkData($chunkData);
+ $this->addChunkData($finalHash, $chunkData);
} // END - for
// Debug output
- //* NOISY-DEBUG: */ $this->debugOutput('FRAGMENTER: Raw data of ' . strlen($rawData) . ' bytes has been fragmented into ' . count($this->chunks[$finalHash]) . ' chunk(s).');
+ //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('PACKAGE-FRAGMENTER[' . __METHOD__ . ':' . __LINE__ . ']: Raw data of ' . strlen($rawData) . ' bytes has been fragmented into ' . count($this->chunks[$finalHash]) . ' chunk(s).');
// Add end-of-package chunk
- $this->appendEndOfPackageChunk($chunkHash, $finalHash);
+ $this->appendEndOfPackageChunk($chunkData, $finalHash);
}
/**
* Adds the given chunk (raw data) to the proper array and hashes it for
* later verfication.
*
+ * @param $finalHash Final hash for faster processing
* @param $chunkData Raw chunk data
+ * @param $prepend Whether append (default) or prepend the chunk
* @return void
*/
- private function addChunkData ($chunkData) {
+ private function addChunkData ($finalHash, $chunkData, $prepend = FALSE) {
// Hash it
- $rawDataHash = $this->getCryptoInstance()->hashString($chunkData, '', false);
+ $rawDataHash = $this->getCryptoInstance()->hashString($chunkData, '', FALSE);
// Prepend the hash to the chunk
$rawData = (
$rawDataHash . self::CHUNK_DATA_HASH_SEPARATOR .
- $this->getNextHexSerialNumber() . self::CHUNK_DATA_HASH_SEPARATOR .
- $rawData . self::CHUNK_SEPARATOR
+ $this->getNextHexSerialNumber($finalHash) . self::CHUNK_DATA_HASH_SEPARATOR .
+ $chunkData . self::CHUNK_SEPARATOR
);
// Make sure the chunk is not larger than a TCP package can hold
- assert(strlen($rawData) <= NetworkPackage::TCP_PACKAGE_SIZE);
+ //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('PACKAGE-FRAGMENTER[' . __METHOD__ . ':' . __LINE__ . ']: assert: ' . strlen($rawData) . '/' . NetworkPackage::TCP_PACKAGE_SIZE . ' ...');
+ // @TODO This assert broke packages where the hash chunk was very large: assert(strlen($rawData) <= NetworkPackage::TCP_PACKAGE_SIZE);
// Add it to the array
- //* NOISY-DEBUG: */ $this->debugOutput('FRAGMENTER: Adding ' . strlen($rawData) . ' bytes of a chunk.');
- $this->chunks[$finalHash][] = $rawData;
- $this->chunkHashes[$finalHash][] = $rawDataHash;
+ if ($prepend === TRUE) {
+ // Debug message
+ //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('PACKAGE-FRAGMENTER[' . __METHOD__ . ':' . __LINE__ . ']: Prepending ' . strlen($rawData) . ' bytes of a chunk, finalHash=' . $finalHash . ' ...');
+ array_unshift($this->chunkHashes[$finalHash], $rawDataHash);
+ array_unshift($this->chunks[$finalHash] , $rawData);
+ } else {
+ // Debug message
+ //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('PACKAGE-FRAGMENTER[' . __METHOD__ . ':' . __LINE__ . ']: Appending ' . strlen($rawData) . ' bytes of a chunk, finalHash=' . $finalHash . ' ...');
+
+ // Is the array there?
+ if (!isset($this->chunks[$finalHash])) {
+ // Then initialize it
+ $this->chunks[$finalHash] = array();
+ $this->chunkHashes[$finalHash] = array();
+ } // END - if
+
+ // Add both
+ array_push($this->chunks[$finalHash] , $rawData);
+ array_push($this->chunkHashes[$finalHash], $rawDataHash);
+ }
}
/**
* Prepends a chunk (or more) with all hashes from all chunks + final chunk.
*
- * @param $rawData Raw data string
* @param $finalHash Final hash from the raw data
* @return void
*/
- private function prependHashChunk ($rawData, $finalHash) {
+ private function prependHashChunk ($finalHash) {
// "Implode" the whole array of hashes into one string
$rawData = self::HASH_CHUNK_IDENTIFIER . implode(self::CHUNK_HASH_SEPARATOR, $this->chunkHashes[$finalHash]);
- // Also get a hash from it
- $chunkHash = $this->generateHashFromRawData($rawData);
-
- // Calulcate chunk size
- $dataChunkSize = $this->getDataChunkSizeFromHash($chunkHash);
-
- // Now array_unshift() it to the two chunk arrays
- for ($idx = 0; $idx < strlen($rawData); $idx += $dataChunkSize) {
- // Get the next chunk
- $chunk = substr($rawData, $idx, $dataChunkSize);
+ // Prepend chunk
+ $this->addChunkData($finalHash, $rawData, TRUE);
+ }
- // Hash it and remember it in seperate array
- $chunkHash = $this->getCryptoInstance()->hashString($chunk, '', false);
- array_unshift($this->chunkHashes[$finalHash], $chunkHash);
+ /**
+ * "Getter" for the next hexadecimal-encoded serial number
+ *
+ * @param $finalHash Final hash
+ * @return $encodedSerialNumber The next hexadecimal-encoded serial number
+ */
+ public function getNextHexSerialNumber ($finalHash) {
+ // Assert on maximum serial number length
+ assert($this->serialNumber[$finalHash] <= $this->maxSerialNumber);
- // Prepend the hash to the chunk
- $chunk =
- $chunkHash . self::CHUNK_DATA_HASH_SEPARATOR .
- $this->getNextHexSerialNumber() . self::CHUNK_DATA_HASH_SEPARATOR .
- $chunk . self::CHUNK_SEPARATOR
- ;
+ // Encode the current serial number
+ /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('PACKAGE-FRAGMENTER[' . __METHOD__ . ':' . __LINE__ . ']: serialNumber[' . $finalHash . ']=' . $this->serialNumber[$finalHash]);
+ $encodedSerialNumber = $this->dec2Hex($this->serialNumber[$finalHash], self::MAX_SERIAL_LENGTH);
- // Make sure the chunk is not larger than a TCP package can hold
- assert(strlen($chunk) <= NetworkPackage::TCP_PACKAGE_SIZE);
+ // Count one up
+ $this->serialNumber[$finalHash]++;
- // Add it to the array
- //* NOISY-DEBUG: */ $this->debugOutput('FRAGMENTER: Adding ' . strlen($chunk) . ' bytes of a chunk.');
- array_unshift($this->chunks[$finalHash], $chunk);
- } // END - for
+ // Return the encoded serial number
+ /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('PACKAGE-FRAGMENTER[' . __METHOD__ . ':' . __LINE__ . ']: encodedSerialNumber=' . $encodedSerialNumber);
+ return $encodedSerialNumber;
}
/**
public function fragmentPackageArray (array $packageData, ConnectionHelper $helperInstance) {
// Is this package already fragmented?
if (!$this->isPackageProcessed($packageData)) {
- // Remove package status, the recipient doesn't need this
+ // Remove package status and protocol, the recipient doesn't need this
unset($packageData[NetworkPackage::PACKAGE_DATA_STATUS]);
+ unset($packageData[NetworkPackage::PACKAGE_DATA_PROTOCOL]);
// First we need to "implode" the array
$rawData = implode(NetworkPackage::PACKAGE_DATA_SEPARATOR, $packageData);
// Remember it
$this->processedPackages[$this->getProcessedPackagesIndex($packageData)] = $finalHash;
- // Init pointer
+ // Init pointer and reset serial number
$this->initPointer($finalHash);
+ $this->resetSerialNumber($finalHash);
// Split the encoded data into smaller chunks
$this->splitEncodedDataIntoChunks($rawData, $finalHash);
// Prepend a chunk with all hashes together
- $this->prependHashChunk($rawData, $finalHash);
+ $this->prependHashChunk($finalHash);
// Mark the package as fragmented
$this->markPackageDataProcessed($packageData);
}
// Return final hash
- //* NOISY-DEBUG: */ $this->debugOutput('FRAGMENTER: finalHash[' . gettype($finalHash) . ']=' . $finalHash);
+ //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('PACKAGE-FRAGMENTER[' . __METHOD__ . ':' . __LINE__ . ']: finalHash[' . gettype($finalHash) . ']=' . $finalHash);
return $finalHash;
}
*
* @param $finalHash Final hash for faster lookup
* @return $rawDataChunk Raw package data chunk
- * @throws AssertionException If $finalHash was not 'true'
+ * @throws AssertionException If $finalHash was not 'TRUE'
*/
public function getNextRawDataChunk ($finalHash) {
+ // Debug message
+ //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('PACKAGE-FRAGMENTER[' . __METHOD__ . ':' . __LINE__ . ']: finalHash[' . gettype($finalHash) . ']=' . $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) {
+ // This may happen when the final hash is TRUE
+ if ($finalHash === TRUE) {
// Set current to null
$current = NULL;
} else {
// 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
+ //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput(__METHOD__. ': finalHash=' . $finalHash . ',current=' . $current . ' - No more entries found!');
return array();
} // END - if
+ // Debug message
+ //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput(__METHOD__. ': finalHash=' . $finalHash . ',current=' . $current . ',chunkHashes()=' . count($this->chunkHashes[$finalHash]) .' - Entry choosen ...');
+
// Generate the array
$rawDataChunk = array(
$this->chunkHashes[$finalHash][$current] => $this->chunks[$finalHash][$current]
// Return the chunk array
return $rawDataChunk;
}
+
+ /**
+ * Resets the serial number to zero for given final hash
+ *
+ * @param $finalHash Final hash to reset counter for
+ * @return void
+ */
+ public function resetSerialNumber ($finalHash) {
+ // Final hash must be set
+ assert((is_string($finalHash)) && (!empty($finalHash)));
+
+ // Reset serial number
+ $this->serialNumber[$finalHash] = 0;
+ }
}
// [EOF]