diff --git a/4dev/tests/Create/CoreLibsCreateCrc64Test.php b/4dev/tests/Create/CoreLibsCreateCrc64Test.php new file mode 100644 index 00000000..dffa02a7 --- /dev/null +++ b/4dev/tests/Create/CoreLibsCreateCrc64Test.php @@ -0,0 +1,73 @@ + [ + 'file' => __DIR__ . DIRECTORY_SEPARATOR . 'files' . DIRECTORY_SEPARATOR . 'random_10KB.bin', + 'crc64_base64' => 'oMgkUkGrDME=', + 'crc64_hex' => 'a0c8245241ab0cc1', + ], + '100kb' => [ + 'file' => __DIR__ . DIRECTORY_SEPARATOR . 'files' . DIRECTORY_SEPARATOR . 'random_100KB.bin', + 'crc64_base64' => 'i9Lpf3Y3D3M=', + 'crc64_hex' => '8bd2e97f76370f73', + ], + '1mb' => [ + 'file' => __DIR__ . DIRECTORY_SEPARATOR . 'files' . DIRECTORY_SEPARATOR . 'random_1MB.bin', + 'crc64_base64' => 'WqYAirRJ0q8=', + 'crc64_hex' => '5aa6008ab449d2af', + ], + '5mb' => [ + 'file' => __DIR__ . DIRECTORY_SEPARATOR . 'files' . DIRECTORY_SEPARATOR . 'random_5MB.bin', + 'crc64_base64' => '9hS5kUxbgR0=', + 'crc64_hex' => 'f614b9914c5b811d', + ], + '10mb' => [ + 'file' => __DIR__ . DIRECTORY_SEPARATOR . 'files' . DIRECTORY_SEPARATOR . 'random_10MB.bin', + 'crc64_base64' => 'xDmtkk1JhjM=', + 'crc64_hex' => 'c439ad924d498633', + ], + // everything above 10MB is too slow, sow do not check this + ]; + } + + /** + * Undocumented function + * + * @covers ::crc64 + * @covers ::crc64hex + * @dataProvider providerFiles + * @testdox generate crc64 for $file and expecting $crc64_base64 and $crc64_hex [$_dataName] + * + * @return void + */ + public function testCrc64(string $file, string $crc64_base64, string $crc64_hex): void + { + $this->assertSame( + $crc64_base64, + \CoreLibs\Create\Crc64::crc64($file), + "Failed asserting base64 CRC64 for $file" + ); + $this->assertSame( + $crc64_hex, + \CoreLibs\Create\Crc64::crc64hex($file), + "Failed asserting hex CRC64 for $file" + ); + } +} + +// __END__ diff --git a/4dev/tests/Create/files/random_100KB.bin b/4dev/tests/Create/files/random_100KB.bin new file mode 100644 index 00000000..c9ab5eab Binary files /dev/null and b/4dev/tests/Create/files/random_100KB.bin differ diff --git a/4dev/tests/Create/files/random_10KB.bin b/4dev/tests/Create/files/random_10KB.bin new file mode 100644 index 00000000..c6d3b8c4 Binary files /dev/null and b/4dev/tests/Create/files/random_10KB.bin differ diff --git a/4dev/tests/Create/files/random_10MB.bin b/4dev/tests/Create/files/random_10MB.bin new file mode 100644 index 00000000..81f205dc Binary files /dev/null and b/4dev/tests/Create/files/random_10MB.bin differ diff --git a/4dev/tests/Create/files/random_1MB.bin b/4dev/tests/Create/files/random_1MB.bin new file mode 100644 index 00000000..0002c672 Binary files /dev/null and b/4dev/tests/Create/files/random_1MB.bin differ diff --git a/4dev/tests/Create/files/random_5MB.bin b/4dev/tests/Create/files/random_5MB.bin new file mode 100644 index 00000000..796bfda0 Binary files /dev/null and b/4dev/tests/Create/files/random_5MB.bin differ diff --git a/www/admin/class_test.crc64.php b/www/admin/class_test.crc64.php new file mode 100644 index 00000000..9d389dc6 --- /dev/null +++ b/www/admin/class_test.crc64.php @@ -0,0 +1,77 @@ + BASE . LOG, + 'log_file_id' => $LOG_FILE_ID, + 'log_per_date' => true, +]); + +$PAGE_NAME = 'TEST CLASS: CRC64'; +print ""; +print "" . $PAGE_NAME . ""; +print ""; +print '
Class Test Master
'; +print '

' . $PAGE_NAME . '

'; + + +$file = getcwd() . DIRECTORY_SEPARATOR . 'class_test.file.php'; +print "CRC64: $file: " . Crc64::crc64($file) . "
"; +print "CRC64HEX: $file: " . Crc64::crc64hex($file) . "
"; + +// big file check including speed +$base_path = __DIR__ . DIRECTORY_SEPARATOR + . '..' . DIRECTORY_SEPARATOR + . '..' . DIRECTORY_SEPARATOR + . '4dev' . DIRECTORY_SEPARATOR . 'tests' . DIRECTORY_SEPARATOR . + 'Create' . DIRECTORY_SEPARATOR . 'files' . DIRECTORY_SEPARATOR; +$max_size = 1024 * 1024 * 10; +# open the base path and loop over each file in there +$files = glob($base_path . '*'); +if (!empty($files)) { + foreach ($files as $file) { + if (!is_file($file)) { + continue; + } + if (filesize($file) > $max_size) { + print "Skipping large file: $file
"; + continue; + } + RunningTime::runningTime(); + print "CRC64: $file: " . Crc64::crc64($file) . "
"; + RunningTime::runningTime(); + print "Running Time: $file: " . RunningTime::runningTimeString() . "
"; + print "Running Time: $file: " . RunningTime::hrRunningTime() . "
"; + print "CRC64HEX: $file: " . Crc64::crc64hex($file) . "
"; + RunningTime::runningTime(); + print "Running Time: $file: " . RunningTime::runningTimeString() . "
"; + print "Running Time: $file: " . RunningTime::hrRunningTime() . "
"; + RunningTime::hrRunningTimeReset(); + } +} else { + print "No files found in base path: $base_path
"; +} + +print ""; + +// __END__ diff --git a/www/admin/class_test.php b/www/admin/class_test.php index 0e2d6ad4..2ae5c7ac 100644 --- a/www/admin/class_test.php +++ b/www/admin/class_test.php @@ -120,6 +120,7 @@ $test_files = [ 'class_test.datetime.php' => 'Class Test: DATE/TIME', 'class_test.array.php' => 'Class Test: ARRAY HANDLER', 'class_test.file.php' => 'Class Test: FILE', + 'class_test.crc64.php' => 'Class Test: CRC64', 'class_test.randomkey.php' => 'Class Test: RANDOM KEY', 'class_test.system.php' => 'Class Test: SYSTEM', 'class_test.readenvfile.php' => 'Class Test: READ ENV FILE', diff --git a/www/lib/CoreLibs/Create/Crc64.php b/www/lib/CoreLibs/Create/Crc64.php new file mode 100644 index 00000000..eab5ac45 --- /dev/null +++ b/www/lib/CoreLibs/Create/Crc64.php @@ -0,0 +1,106 @@ +> is arithmetic, so mask off the sign bit + $c = ($c & 1) + ? ((($c >> 1) & PHP_INT_MAX) ^ $poly) + : (($c >> 1) & PHP_INT_MAX); + } + $table[$i] = $c; + } + } + $crc = ~$crc; // xor 0xFFFFFFFFFFFFFFFF + foreach (unpack('C*', $data) ?: [] as $b) { + $crc = $table[($crc ^ $b) & 0xFF] ^ (($crc >> 8) & 0x00FFFFFFFFFFFFFF); + } + return ~$crc; + } + + /** + * Returns int value of the CRC64 checksum for the given file. + * + * @param string $path + * @param int<1,16777216> $chunkSize + * @return int + */ + private function crc64CalcFile(string $path, int $chunkSize = 8 * 1024 * 1024): int + { + $fh = fopen($path, 'rb'); + if ($fh === false) { + throw new \RuntimeException("Cannot open $path"); + } + $crc = 0; + try { + while (!feof($fh)) { + $chunk = fread($fh, $chunkSize); + if ($chunk === false) { + throw new \RuntimeException("Read error on $path"); + } + $crc = $this->crc64Calc($chunk, $crc); + } + } finally { + fclose($fh); + } + return $crc; + } + + /** + * Returns the Base64-encoded CRC64 checksum for the given file. + * + * @param string $path + * @return string + */ + public static function crc64(string $path): string + { + return base64_encode(pack('J', (new self())->crc64CalcFile($path))); // J = uint64 big-endian + } + + /** + * Returns the 16-character lowercase hexadecimal CRC64 checksum for the given file. + * Same as base64.b64decode(...).hex() + * + * @param string $path + * @return string + */ + public static function crc64hex(string $path): string + { + return sprintf('%016x', (new self())->crc64CalcFile($path)); + } +} + +// __END__