From a650c6b475bb194331ba105844a340427c3674cc Mon Sep 17 00:00:00 2001 From: Timm Friebe Date: Tue, 5 Aug 2025 20:43:52 +0200 Subject: [PATCH 1/4] Use compression library, adding support for Snappy --- composer.json | 1 + .../php/com/mongodb/io/Compression.class.php | 18 ++++++++++++-- .../php/com/mongodb/io/Compressor.class.php | 21 ++++++++++++---- src/main/php/com/mongodb/io/Zlib.class.php | 24 ------------------- src/main/php/com/mongodb/io/Zstd.class.php | 24 ------------------- .../unittest/CompressionTest.class.php | 19 +++++++-------- .../mongodb/unittest/ConnectionTest.class.php | 5 ++-- 7 files changed, 44 insertions(+), 68 deletions(-) delete mode 100755 src/main/php/com/mongodb/io/Zlib.class.php delete mode 100755 src/main/php/com/mongodb/io/Zstd.class.php diff --git a/composer.json b/composer.json index 2f47ae5..5ddcb14 100755 --- a/composer.json +++ b/composer.json @@ -9,6 +9,7 @@ "xp-framework/core": "^12.0 | ^11.0 | ^10.0", "xp-framework/networking": "^10.0", "xp-framework/math": "^9.1", + "xp-forge/compression": "^2.0", "php" : ">=7.4.0" }, "require-dev" : { diff --git a/src/main/php/com/mongodb/io/Compression.class.php b/src/main/php/com/mongodb/io/Compression.class.php index a6cd008..e50a1a3 100755 --- a/src/main/php/com/mongodb/io/Compression.class.php +++ b/src/main/php/com/mongodb/io/Compression.class.php @@ -1,5 +1,6 @@ new Zlib($options['zlibCompressionLevel'] ?? -1); - extension_loaded('zstd') && self::$negotiable['zstd']= fn($options) => new Zstd($options['zstdCompressionLevel'] ?? -1); + self::$negotiable['snappy']= fn($options) => new Compressor( + 1, + new Snappy(), + null + ); + extension_loaded('zlib') && self::$negotiable['zlib']= fn($options) => new Compressor( + 2, + new Gzip(), + $options['zlibCompressionLevel'] ?? -1 + ); + extension_loaded('zstd') && self::$negotiable['zlib']= fn($options) => new Compressor( + 3, + new ZStandard(), + $options['zstdCompressionLevel'] ?? -1 + ); } /** @param [:com.mongodb.io.Compressor] $compressors */ diff --git a/src/main/php/com/mongodb/io/Compressor.class.php b/src/main/php/com/mongodb/io/Compressor.class.php index f77e01d..dbd08d4 100755 --- a/src/main/php/com/mongodb/io/Compressor.class.php +++ b/src/main/php/com/mongodb/io/Compressor.class.php @@ -1,15 +1,26 @@ id= $id; + $this->algorithm= $algorithm; + $this->options= $options; + } - public abstract function decompress($compressed); + public function compress($data) { + return $this->algorithm->compress($data, $this->options); + } - public function toString() { return nameof($this).'(id: '.$this->id.')'; } + public function decompress($compressed) { + return $this->algorithm->decompress($compressed); + } + + public function toString() { return nameof($this).'(id: '.$this->id.', options: '.$this->options.')'; } public function hashCode() { return 'C'.$this->id; } diff --git a/src/main/php/com/mongodb/io/Zlib.class.php b/src/main/php/com/mongodb/io/Zlib.class.php deleted file mode 100755 index a195e3a..0000000 --- a/src/main/php/com/mongodb/io/Zlib.class.php +++ /dev/null @@ -1,24 +0,0 @@ -level= $level; - } - - public function compress($data) { - return gzcompress($data, $this->level); - } - - public function decompress($compressed) { - return gzuncompress($compressed); - } - - /** @return string */ - public function toString() { - return nameof($this).'(id: '.$this->id.', level: '.$this->level.')'; - } -} \ No newline at end of file diff --git a/src/main/php/com/mongodb/io/Zstd.class.php b/src/main/php/com/mongodb/io/Zstd.class.php deleted file mode 100755 index 0c30cea..0000000 --- a/src/main/php/com/mongodb/io/Zstd.class.php +++ /dev/null @@ -1,24 +0,0 @@ -level= $level; - } - - public function compress($data) { - return zstd_compress($data); - } - - public function decompress($compressed) { - return zstd_uncompress($compressed); - } - - /** @return string */ - public function toString() { - return nameof($this).'(id: '.$this->id.', level: '.$this->level.')'; - } -} \ No newline at end of file diff --git a/src/test/php/com/mongodb/unittest/CompressionTest.class.php b/src/test/php/com/mongodb/unittest/CompressionTest.class.php index 213a37d..1ed7c18 100755 --- a/src/test/php/com/mongodb/unittest/CompressionTest.class.php +++ b/src/test/php/com/mongodb/unittest/CompressionTest.class.php @@ -1,6 +1,7 @@ compressor= new class() extends Compressor { - public $id= 9; - public function compress($data) { /** Not implemented */ } - public function decompress($compressed) { /** Not implemented */ } - }; + $this->compressor= new Compressor(0, new None()); } @@ -59,27 +56,27 @@ public function negotiate_unsupported() { #[Test, Runtime(extensions: ['zlib'])] public function negotiate_zlib() { - Assert::instance(Zlib::class, Compression::negotiate(['unsupported', 'zlib'])->select(2)); + Assert::instance(Gzip::class, Compression::negotiate(['unsupported', 'zlib'])->select(2)->algorithm); } #[Test, Runtime(extensions: ['zlib']), Values([[[], -1], [['zlibCompressionLevel' => 6], 6]])] public function negotiate_zlib_with($options, $level) { $compressor= Compression::negotiate(['zlib'], $options)->select(2); - Assert::instance(Zlib::class, $compressor); - Assert::equals($level, $compressor->level); + Assert::instance(Gzip::class, $compressor->algorithm); + Assert::equals($level, $compressor->options); } #[Test, Runtime(extensions: ['zstd'])] public function negotiate_zstd() { - Assert::instance(Zstd::class, Compression::negotiate(['unsupported', 'zstd'])->select(3)); + Assert::instance(ZStandard::class, Compression::negotiate(['unsupported', 'zstd'])->select(3)->algorithm); } #[Test, Runtime(extensions: ['zstd']), Values([[[], -1], [['zstdCompressionLevel' => 6], 6]])] public function negotiate_zstd_with($options, $level) { $compressor= Compression::negotiate(['zstd'], $options)->select(3); - Assert::instance(Zstd::class, $compressor); + Assert::instance(ZStandard::class, $compressor->algorithm); Assert::equals($level, $compressor->level); } } \ No newline at end of file diff --git a/src/test/php/com/mongodb/unittest/ConnectionTest.class.php b/src/test/php/com/mongodb/unittest/ConnectionTest.class.php index e44948d..654374d 100755 --- a/src/test/php/com/mongodb/unittest/ConnectionTest.class.php +++ b/src/test/php/com/mongodb/unittest/ConnectionTest.class.php @@ -1,7 +1,8 @@ 'one']]; $c= new Connection(new TestingSocket([ ...$this->reply(['ok' => 1.0, 'compression' => ['zlib']]), - ...$this->compressed(new Zlib(), [ + ...$this->compressed(new Compressor(2, new Gzip()), [ 'cursor' => ['firstBatch' => $documents, 'id' => new Int64(0), 'ns' => 'test.entries'], 'ok' => 1, ]), From cbbbb5b347e6c9201c82b243c7d589edc6f93fc6 Mon Sep 17 00:00:00 2001 From: Timm Friebe Date: Sat, 16 Aug 2025 20:31:36 +0200 Subject: [PATCH 2/4] Fix ZStandard support --- src/main/php/com/mongodb/io/Compression.class.php | 2 +- src/test/php/com/mongodb/unittest/CompressionTest.class.php | 4 ++-- src/test/php/com/mongodb/unittest/ConnectionTest.class.php | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main/php/com/mongodb/io/Compression.class.php b/src/main/php/com/mongodb/io/Compression.class.php index e50a1a3..082ea5b 100755 --- a/src/main/php/com/mongodb/io/Compression.class.php +++ b/src/main/php/com/mongodb/io/Compression.class.php @@ -27,7 +27,7 @@ static function __static() { new Gzip(), $options['zlibCompressionLevel'] ?? -1 ); - extension_loaded('zstd') && self::$negotiable['zlib']= fn($options) => new Compressor( + extension_loaded('zstd') && self::$negotiable['zstd']= fn($options) => new Compressor( 3, new ZStandard(), $options['zstdCompressionLevel'] ?? -1 diff --git a/src/test/php/com/mongodb/unittest/CompressionTest.class.php b/src/test/php/com/mongodb/unittest/CompressionTest.class.php index 1ed7c18..6224881 100755 --- a/src/test/php/com/mongodb/unittest/CompressionTest.class.php +++ b/src/test/php/com/mongodb/unittest/CompressionTest.class.php @@ -1,7 +1,7 @@ select(3); Assert::instance(ZStandard::class, $compressor->algorithm); - Assert::equals($level, $compressor->level); + Assert::equals($level, $compressor->options); } } \ No newline at end of file diff --git a/src/test/php/com/mongodb/unittest/ConnectionTest.class.php b/src/test/php/com/mongodb/unittest/ConnectionTest.class.php index 654374d..b275743 100755 --- a/src/test/php/com/mongodb/unittest/ConnectionTest.class.php +++ b/src/test/php/com/mongodb/unittest/ConnectionTest.class.php @@ -2,7 +2,7 @@ use com\mongodb\Int64; use com\mongodb\io\{BSON, Connection, Compression, Compressor}; -use io\streams\compress\Gzip; +use io\streams\compress\{Gzip, ZStandard}; use peer\ConnectException; use test\verify\Runtime; use test\{Assert, Before, Expect, Test, Values}; @@ -164,7 +164,7 @@ public function send_and_receive_zstd() { $documents= [['_id' => 'one']]; $c= new Connection(new TestingSocket([ ...$this->reply(['ok' => 1.0, 'compression' => ['zstd']]), - ...$this->compressed(new Zstd(), [ + ...$this->compressed(new Compressor(3, new ZStandard()), [ 'cursor' => ['firstBatch' => $documents, 'id' => new Int64(0), 'ns' => 'test.entries'], 'ok' => 1, ]), From 8f5a6f8ea9e9037e264578538629d3ca1b0f5880 Mon Sep 17 00:00:00 2001 From: Timm Friebe Date: Sat, 16 Aug 2025 20:32:50 +0200 Subject: [PATCH 3/4] Negotiate snappy compression --- .../php/com/mongodb/unittest/CompressionTest.class.php | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/test/php/com/mongodb/unittest/CompressionTest.class.php b/src/test/php/com/mongodb/unittest/CompressionTest.class.php index 6224881..d13a419 100755 --- a/src/test/php/com/mongodb/unittest/CompressionTest.class.php +++ b/src/test/php/com/mongodb/unittest/CompressionTest.class.php @@ -1,7 +1,7 @@ select(1)->algorithm); + } + #[Test, Runtime(extensions: ['zlib'])] public function negotiate_zlib() { Assert::instance(Gzip::class, Compression::negotiate(['unsupported', 'zlib'])->select(2)->algorithm); From 76abd90289e2644a875a79fad8821e780a9616fb Mon Sep 17 00:00:00 2001 From: Timm Friebe Date: Sat, 16 Aug 2025 20:38:26 +0200 Subject: [PATCH 4/4] Inline compression and decompression instead of delegating --- src/main/php/com/mongodb/io/Compressor.class.php | 8 -------- src/main/php/com/mongodb/io/Connection.class.php | 4 ++-- .../php/com/mongodb/unittest/ConnectionTest.class.php | 2 +- 3 files changed, 3 insertions(+), 11 deletions(-) diff --git a/src/main/php/com/mongodb/io/Compressor.class.php b/src/main/php/com/mongodb/io/Compressor.class.php index dbd08d4..72e1fb8 100755 --- a/src/main/php/com/mongodb/io/Compressor.class.php +++ b/src/main/php/com/mongodb/io/Compressor.class.php @@ -12,14 +12,6 @@ public function __construct(int $id, Algorithm $algorithm, $options= null) { $this->options= $options; } - public function compress($data) { - return $this->algorithm->compress($data, $this->options); - } - - public function decompress($compressed) { - return $this->algorithm->decompress($compressed); - } - public function toString() { return nameof($this).'(id: '.$this->id.', options: '.$this->options.')'; } public function hashCode() { return 'C'.$this->id; } diff --git a/src/main/php/com/mongodb/io/Connection.class.php b/src/main/php/com/mongodb/io/Connection.class.php index 36c1062..100bb9f 100755 --- a/src/main/php/com/mongodb/io/Connection.class.php +++ b/src/main/php/com/mongodb/io/Connection.class.php @@ -239,7 +239,7 @@ public function send($operation, $header, $sections, $readPreference= null) { $length= strlen($body); if ($this->compression && $compressor= $this->compression->for($sections, $length)) { - $compressed= $compressor->compress($body); + $compressed= $compressor->algorithm->compress($body, $compressor->options); $this->socket->write(pack( 'VVVVVVCa*', strlen($compressed) + 25, @@ -284,7 +284,7 @@ public function send($operation, $header, $sections, $readPreference= null) { $compressed= unpack('VoriginalOpcode/VuncompressedSize/CcompressorId', $response); if ($this->compression && $compressor= $this->compression->select($compressed['compressorId'] ?? null)) { - $response= $compressor->decompress(substr($response, 9)); + $response= $compressor->algorithm->decompress(substr($response, 9)); $meta['opCode']= $compressed['originalOpcode']; goto opcode; } diff --git a/src/test/php/com/mongodb/unittest/ConnectionTest.class.php b/src/test/php/com/mongodb/unittest/ConnectionTest.class.php index b275743..ff6f3a1 100755 --- a/src/test/php/com/mongodb/unittest/ConnectionTest.class.php +++ b/src/test/php/com/mongodb/unittest/ConnectionTest.class.php @@ -32,7 +32,7 @@ private function msg(array $document): array { /** Creates an OP_COMPRESSED message with an embedded OP_MSG opcode */ private function compressed(Compressor $compressor, array $document): array { $payload= pack('VC', 0, 0).$this->bson->sections($document); - $compressed= $compressor->compress($payload); + $compressed= $compressor->algorithm->compress($payload, $compressor->options); return [ pack('VVVV', strlen($compressed) + 25, 0, 0, Connection::OP_COMPRESSED), pack('VVC', Connection::OP_MSG, strlen($payload), $compressor->id).$compressed