Skip to content

refactor(mask): simplify MaskUtil::getDataMaskBit implementation#226

Merged
DASPRiD merged 7 commits intoBacon:mainfrom
vlakoff:refactor/mask
Mar 16, 2026
Merged

refactor(mask): simplify MaskUtil::getDataMaskBit implementation#226
DASPRiD merged 7 commits intoBacon:mainfrom
vlakoff:refactor/mask

Conversation

@vlakoff
Copy link
Contributor

@vlakoff vlakoff commented Mar 16, 2026

Several simplifications in the MaskUtil::getDataMaskBit implementation, the most significant ones being:

  • replacing BitUtils::unsignedRightShift with straightforward integer division
  • using a PHP match expression

@codecov
Copy link

codecov bot commented Mar 16, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 71.81%. Comparing base (3feed0e) to head (b07cd58).
⚠️ Report is 2 commits behind head on main.

Additional details and impacted files
@@             Coverage Diff              @@
##               main     #226      +/-   ##
============================================
- Coverage     71.95%   71.81%   -0.14%     
+ Complexity      996      995       -1     
============================================
  Files            49       49              
  Lines          3170     3151      -19     
============================================
- Hits           2281     2263      -18     
+ Misses          889      888       -1     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@vlakoff
Copy link
Contributor Author

vlakoff commented Mar 16, 2026

About replacing BitUtils::unsignedRightShift with straightforward integer division:

Quick benchmark code - unsignedRightShift method call vs integer division
class BitUtils {
    public static function unsignedRightShift(int $a, int $b): int
    {
        return (
            $a >= 0
            ? $a >> $b
            : (($a & 0x7fffffff) >> $b) | (0x40000000 >> ($b - 1))
        );
    }
}

function bench(callable $fn, int $iterations = 1_000_000): string {
    $start = microtime(true);
    $sum = 0;

    for ($i = 0; $i < $iterations; ++$i) {
        $y = ($i & 0xFFFF) - 32768; // includes negatives
        $sum += $fn($y);
    }

    return number_format(microtime(true) - $start, 3);
}

$timeStaticMethod = bench(function($y) {
    return BitUtils::unsignedRightShift($y, 1);
});

$timeInline = bench(function($y) {
    return ($y >= 0)
        ? ($y >> 1)
        : (($y & 0x7fffffff) >> 1) | 0x40000000;
});

$timeOptimized = bench(function($y) {
    return ($y >> 1) & 0x7fffffff;
});

$timeDivision = bench(function($y) {
    return (int) ($y / 2);
});

$timeIntdiv = bench(function($y) {
    return intdiv($y, 2);
});

echo "Static method shift:      {$timeStaticMethod} sec\n";
echo "Inline unsigned shift:    {$timeInline} sec\n";
echo "Optimized unsigned shift: {$timeOptimized} sec\n";
echo "Integer division:         {$timeDivision} sec\n";
echo "intdiv():                 {$timeIntdiv} sec\n";

Results:

== https://onlinephp.io/ - 1M iterations ==

PHP 8.2.29:
Static method shift:      0.295 sec
Inline unsigned shift:    0.100 sec
Optimized unsigned shift: 0.167 sec
Integer division:         0.137 sec
intdiv():                 0.212 sec

PHP 8.3.21:
Static method shift:      0.183 sec
Inline unsigned shift:    0.217 sec
Optimized unsigned shift: 0.188 sec
Integer division:         0.199 sec
intdiv():                 0.137 sec

PHP 8.4.10:
Static method shift:      0.216 sec
Inline unsigned shift:    0.162 sec
Optimized unsigned shift: 0.108 sec
Integer division:         0.203 sec
intdiv():                 0.201 sec

== local - 5M iterations ==

PHP 8.5.4:
Static method shift:      0.396 sec
Inline unsigned shift:    0.261 sec
Optimized unsigned shift: 0.229 sec
Integer division:         0.261 sec
intdiv():                 0.278 sec

intdiv() is the cleanest method, and as a bonus it is marginally faster than calling BitUtils::unsignedRightShift.

vlakoff added 7 commits March 16, 2026 13:59
Simpler and saves the method call overhead.
As there is now a dedicated function intdiv() for this operation, let's use it.
The bitmask is marginally faster, but modulo makes the intent clearer.
…atchError

The PHP match expression throws an UnhandledMatchError when no arm matches.

Removing the custom exception simplifies the code and aligns with the behavior discussed in Bacon#200.
This was the only remaining loose comparison in the entire repository.
@DASPRiD
Copy link
Member

DASPRiD commented Mar 16, 2026

I assume other places which use unsignedRightShift cannot be modified the same way?

@vlakoff
Copy link
Contributor Author

vlakoff commented Mar 16, 2026

This simplification only works when the value being shifted is guaranteed to be non‑negative.

For non‑negative inputs, unsignedRightShift($x, n) behaves the same as an arithmetic operation like intdiv($x, 2**n).

But if the input can be negative, logical right shift and arithmetic right shift diverge, so those cases can’t be simplified in the same straightforward way.

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Refactors MaskUtil::getDataMaskBit() to simplify mask-bit computation logic in the QR encoder, replacing the prior switch/bitwise implementation with a match expression and integer division.

Changes:

  • Replaced switch-based mask selection with a PHP match expression.
  • Removed dependency on BitUtils::unsignedRightShift() in favor of intdiv().
  • Dropped InvalidArgumentException handling/documentation for invalid mask patterns.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@DASPRiD DASPRiD merged commit c6e0271 into Bacon:main Mar 16, 2026
13 checks passed
@DASPRiD
Copy link
Member

DASPRiD commented Mar 16, 2026

thanks!

@vlakoff vlakoff deleted the refactor/mask branch March 16, 2026 18:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants