Просмотр исходного кода

:octocat: use ZXing classes for RS encoding, cleanup

codemasher 5 лет назад
Родитель
Сommit
4ce932aee3

+ 0 - 125
src/Common/ReedSolomon.php

@@ -1,125 +0,0 @@
-<?php
-/**
- * Class ReedSolomon
- *
- * @filesource   ReedSolomon.php
- * @created      07.01.2021
- * @package      chillerlan\QRCode\Common
- * @author       smiley <smiley@chillerlan.net>
- * @copyright    2021 smiley
- * @license      MIT
- */
-
-namespace chillerlan\QRCode\Common;
-
-use chillerlan\QRCode\Helpers\BitBuffer;
-use chillerlan\QRCode\Helpers\Polynomial;
-
-use SplFixedArray;
-use function array_fill, array_merge, count, max;
-
-/**
- * ISO/IEC 18004:2000 Section 8.5 ff
- *
- * @see http://www.thonky.com/qr-code-tutorial/error-correction-coding
- */
-final class ReedSolomon{
-
-	private Version       $version;
-	private EccLevel      $eccLevel;
-
-	private SplFixedArray $interleavedData;
-	private int           $interleavedDataIndex;
-
-	/**
-	 * ReedSolomon constructor.
-	 */
-	public function __construct(Version $version, EccLevel $eccLevel){
-		$this->version  = $version;
-		$this->eccLevel = $eccLevel;
-	}
-
-	/**
-	 * ECC interleaving
-	 *
-	 * @return \SplFixedArray<int>
-	 */
-	public function interleaveEcBytes(BitBuffer $bitBuffer):SplFixedArray{
-		[$numEccCodewords, [[$l1, $b1], [$l2, $b2]]] = $this->version->getRSBlocks($this->eccLevel);
-
-		$numRsBlocks = $l1 + $l2;
-		$ecBytes     = new SplFixedArray($numRsBlocks);
-		$rsBlocks    = array_fill(0, $l1, [$numEccCodewords + $b1, $b1]);
-
-		if($l2 > 0){
-			$rsBlocks = array_merge($rsBlocks, array_fill(0, $l2, [$numEccCodewords + $b2, $b2]));
-		}
-
-		$dataBytes      = SplFixedArray::fromArray($rsBlocks);
-		$maxDataBytes   = 0;
-		$maxEcBytes     = 0;
-		$dataByteOffset = 0;
-		$bitBufferData  = $bitBuffer->getBuffer();
-
-		foreach($rsBlocks as $key => $block){
-			[$rsBlockTotal, $dataByteCount] = $block;
-
-			$ecByteCount     = $rsBlockTotal - $dataByteCount;
-			$maxDataBytes    = max($maxDataBytes, $dataByteCount);
-			$maxEcBytes      = max($maxEcBytes, $ecByteCount);
-			$dataBytes[$key] = new SplFixedArray($dataByteCount);
-
-			foreach($dataBytes[$key] as $i => $_){
-				$dataBytes[$key][$i] = $bitBufferData[$i + $dataByteOffset] & 0xff;
-			}
-
-			$rsPoly  = new Polynomial;
-			$modPoly = new Polynomial;
-
-			for($i = 0; $i < $ecByteCount; $i++){
-				$modPoly->setNum([1, $modPoly->gexp($i)]);
-				$rsPoly->multiply($modPoly->getNum());
-			}
-
-			$rsPolyCount = count($rsPoly->getNum()) - 1;
-
-			$modPoly
-				->setNum($dataBytes[$key]->toArray(), $rsPolyCount)
-				->mod($rsPoly->getNum())
-			;
-
-			$ecBytes[$key] = new SplFixedArray($rsPolyCount);
-			$num           = $modPoly->getNum();
-			$count         = count($num) - count($ecBytes[$key]);
-
-			foreach($ecBytes[$key] as $i => $_){
-				$modIndex          = $i + $count;
-				$ecBytes[$key][$i] = $modIndex >= 0 ? $num[$modIndex] : 0;
-			}
-
-			$dataByteOffset += $dataByteCount;
-		}
-
-		$this->interleavedData      = new SplFixedArray($this->version->getTotalCodewords());
-		$this->interleavedDataIndex = 0;
-
-		$this->interleave($dataBytes, $maxDataBytes, $numRsBlocks);
-		$this->interleave($ecBytes, $maxEcBytes, $numRsBlocks);
-
-		return $this->interleavedData;
-	}
-
-	/**
-	 *
-	 */
-	private function interleave(SplFixedArray $byteArray, int $maxBytes, int $numRsBlocks):void{
-		for($x = 0; $x < $maxBytes; $x++){
-			for($y = 0; $y < $numRsBlocks; $y++){
-				if($x < count($byteArray[$y])){
-					$this->interleavedData[$this->interleavedDataIndex++] = $byteArray[$y][$x];
-				}
-			}
-		}
-	}
-
-}

+ 118 - 0
src/Common/ReedSolomonEncoder.php

@@ -0,0 +1,118 @@
+<?php
+/**
+ * Class ReedSolomonEncoder
+ *
+ * @filesource   ReedSolomonEncoder.php
+ * @created      07.01.2021
+ * @package      chillerlan\QRCode\Common
+ * @author       smiley <smiley@chillerlan.net>
+ * @copyright    2021 smiley
+ * @license      MIT
+ */
+
+namespace chillerlan\QRCode\Common;
+
+use chillerlan\QRCode\Helpers\BitBuffer;
+use SplFixedArray;
+
+use function array_fill, array_merge, count, max;
+
+/**
+ * ISO/IEC 18004:2000 Section 8.5 ff
+ *
+ * @see http://www.thonky.com/qr-code-tutorial/error-correction-coding
+ */
+final class ReedSolomonEncoder{
+
+	private SplFixedArray $interleavedData;
+	private int           $interleavedDataIndex;
+
+	/**
+	 * ECC interleaving
+	 *
+	 * @return \SplFixedArray<int>
+	 */
+	public function interleaveEcBytes(BitBuffer $bitBuffer, Version $version, EccLevel $eccLevel):SplFixedArray{
+		[$numEccCodewords, [[$l1, $b1], [$l2, $b2]]] = $version->getRSBlocks($eccLevel);
+
+		$rsBlocks = array_fill(0, $l1, [$numEccCodewords + $b1, $b1]);
+
+		if($l2 > 0){
+			$rsBlocks = array_merge($rsBlocks, array_fill(0, $l2, [$numEccCodewords + $b2, $b2]));
+		}
+
+		$bitBufferData  = $bitBuffer->getBuffer();
+		$dataBytes      = [];
+		$ecBytes        = [];
+		$maxDataBytes   = 0;
+		$maxEcBytes     = 0;
+		$dataByteOffset = 0;
+
+		foreach($rsBlocks as $key => $block){
+			[$rsBlockTotal, $dataByteCount] = $block;
+
+			$dataBytes[$key] = [];
+
+			for($i = 0; $i < $dataByteCount; $i++){
+				$dataBytes[$key][$i] = $bitBufferData[$i + $dataByteOffset] & 0xff;
+			}
+
+			$ecByteCount    = $rsBlockTotal - $dataByteCount;
+			$ecBytes[$key]  = $this->generateEcBytes($dataBytes[$key], $ecByteCount);
+			$maxDataBytes   = max($maxDataBytes, $dataByteCount);
+			$maxEcBytes     = max($maxEcBytes, $ecByteCount);
+			$dataByteOffset += $dataByteCount;
+		}
+
+		$this->interleavedData      = new SplFixedArray($version->getTotalCodewords());
+		$this->interleavedDataIndex = 0;
+		$numRsBlocks                = $l1 + $l2;
+
+		$this->interleave($dataBytes, $maxDataBytes, $numRsBlocks);
+		$this->interleave($ecBytes, $maxEcBytes, $numRsBlocks);
+
+		return $this->interleavedData;
+	}
+
+	/**
+	 *
+	 */
+	private function generateEcBytes(array $dataBytes, int $ecByteCount):array{
+		$rsPoly = new GenericGFPoly([1]);
+
+		for($i = 0; $i < $ecByteCount; $i++){
+			$rsPoly = $rsPoly->multiply(new GenericGFPoly([1, GF256::exp($i)]));
+		}
+
+		$rsPolyDegree = $rsPoly->getDegree();
+
+		$num = (new GenericGFPoly($dataBytes, $rsPolyDegree))
+			->mod($rsPoly)
+			->getCoefficients()
+		;
+
+		$ecBytes = array_fill(0, $rsPolyDegree, 0);
+		$count   = count($num) - count($ecBytes);
+
+		foreach($ecBytes as $i => &$val){
+			$modIndex = $i + $count;
+			$val      = $modIndex >= 0 ? $num[$modIndex] : 0;
+		}
+
+		return $ecBytes;
+	}
+
+	/**
+	 *
+	 */
+	private function interleave(array $byteArray, int $maxBytes, int $numRsBlocks):void{
+		for($x = 0; $x < $maxBytes; $x++){
+			for($y = 0; $y < $numRsBlocks; $y++){
+				if($x < count($byteArray[$y])){
+					$this->interleavedData[$this->interleavedDataIndex++] = $byteArray[$y][$x];
+				}
+			}
+		}
+	}
+
+}

+ 2 - 3
src/Data/QRData.php

@@ -12,7 +12,7 @@
 
 namespace chillerlan\QRCode\Data;
 
-use chillerlan\QRCode\Common\{EccLevel, Mode, ReedSolomon, Version};
+use chillerlan\QRCode\Common\{EccLevel, Mode, ReedSolomonEncoder, Version};
 use chillerlan\QRCode\QRCode;
 use chillerlan\QRCode\Helpers\BitBuffer;
 use chillerlan\Settings\SettingsContainerInterface;
@@ -99,8 +99,7 @@ class QRData{
 	 * returns a fresh matrix object with the data written for the given $maskPattern
 	 */
 	public function initMatrix(int $maskPattern, bool $test = null):QRMatrix{
-		$rs   = new ReedSolomon($this->version, $this->eccLevel);
-		$data = $rs->interleaveEcBytes($this->bitBuffer);
+		$data = (new ReedSolomonEncoder)->interleaveEcBytes($this->bitBuffer, $this->version, $this->eccLevel);
 
 		return (new QRMatrix($this->version, $this->eccLevel))
 			->init($maskPattern, $test)

+ 0 - 178
src/Helpers/Polynomial.php

@@ -1,178 +0,0 @@
-<?php
-/**
- * Class Polynomial
- *
- * @filesource   Polynomial.php
- * @created      25.11.2015
- * @package      chillerlan\QRCode\Helpers
- * @author       Smiley <smiley@chillerlan.net>
- * @copyright    2015 Smiley
- * @license      MIT
- */
-
-namespace chillerlan\QRCode\Helpers;
-
-use chillerlan\QRCode\QRCodeException;
-
-use function array_fill, count, sprintf;
-
-/**
- * Polynomial long division helpers
- *
- * @see http://www.thonky.com/qr-code-tutorial/error-correction-coding
- */
-final class Polynomial{
-
-	/**
-	 * @see http://www.thonky.com/qr-code-tutorial/log-antilog-table
-	 */
-	protected const table = [
-		[  1,   0], [  2,   0], [  4,   1], [  8,  25], [ 16,   2], [ 32,  50], [ 64,  26], [128, 198],
-		[ 29,   3], [ 58, 223], [116,  51], [232, 238], [205,  27], [135, 104], [ 19, 199], [ 38,  75],
-		[ 76,   4], [152, 100], [ 45, 224], [ 90,  14], [180,  52], [117, 141], [234, 239], [201, 129],
-		[143,  28], [  3, 193], [  6, 105], [ 12, 248], [ 24, 200], [ 48,   8], [ 96,  76], [192, 113],
-		[157,   5], [ 39, 138], [ 78, 101], [156,  47], [ 37, 225], [ 74,  36], [148,  15], [ 53,  33],
-		[106,  53], [212, 147], [181, 142], [119, 218], [238, 240], [193,  18], [159, 130], [ 35,  69],
-		[ 70,  29], [140, 181], [  5, 194], [ 10, 125], [ 20, 106], [ 40,  39], [ 80, 249], [160, 185],
-		[ 93, 201], [186, 154], [105,   9], [210, 120], [185,  77], [111, 228], [222, 114], [161, 166],
-		[ 95,   6], [190, 191], [ 97, 139], [194,  98], [153, 102], [ 47, 221], [ 94,  48], [188, 253],
-		[101, 226], [202, 152], [137,  37], [ 15, 179], [ 30,  16], [ 60, 145], [120,  34], [240, 136],
-		[253,  54], [231, 208], [211, 148], [187, 206], [107, 143], [214, 150], [177, 219], [127, 189],
-		[254, 241], [225, 210], [223,  19], [163,  92], [ 91, 131], [182,  56], [113,  70], [226,  64],
-		[217,  30], [175,  66], [ 67, 182], [134, 163], [ 17, 195], [ 34,  72], [ 68, 126], [136, 110],
-		[ 13, 107], [ 26,  58], [ 52,  40], [104,  84], [208, 250], [189, 133], [103, 186], [206,  61],
-		[129, 202], [ 31,  94], [ 62, 155], [124, 159], [248,  10], [237,  21], [199, 121], [147,  43],
-		[ 59,  78], [118, 212], [236, 229], [197, 172], [151, 115], [ 51, 243], [102, 167], [204,  87],
-		[133,   7], [ 23, 112], [ 46, 192], [ 92, 247], [184, 140], [109, 128], [218,  99], [169,  13],
-		[ 79, 103], [158,  74], [ 33, 222], [ 66, 237], [132,  49], [ 21, 197], [ 42, 254], [ 84,  24],
-		[168, 227], [ 77, 165], [154, 153], [ 41, 119], [ 82,  38], [164, 184], [ 85, 180], [170, 124],
-		[ 73,  17], [146,  68], [ 57, 146], [114, 217], [228,  35], [213,  32], [183, 137], [115,  46],
-		[230,  55], [209,  63], [191, 209], [ 99,  91], [198, 149], [145, 188], [ 63, 207], [126, 205],
-		[252, 144], [229, 135], [215, 151], [179, 178], [123, 220], [246, 252], [241, 190], [255,  97],
-		[227, 242], [219,  86], [171, 211], [ 75, 171], [150,  20], [ 49,  42], [ 98,  93], [196, 158],
-		[149, 132], [ 55,  60], [110,  57], [220,  83], [165,  71], [ 87, 109], [174,  65], [ 65, 162],
-		[130,  31], [ 25,  45], [ 50,  67], [100, 216], [200, 183], [141, 123], [  7, 164], [ 14, 118],
-		[ 28, 196], [ 56,  23], [112,  73], [224, 236], [221, 127], [167,  12], [ 83, 111], [166, 246],
-		[ 81, 108], [162, 161], [ 89,  59], [178,  82], [121,  41], [242, 157], [249,  85], [239, 170],
-		[195, 251], [155,  96], [ 43, 134], [ 86, 177], [172, 187], [ 69, 204], [138,  62], [  9,  90],
-		[ 18, 203], [ 36,  89], [ 72,  95], [144, 176], [ 61, 156], [122, 169], [244, 160], [245,  81],
-		[247,  11], [243, 245], [251,  22], [235, 235], [203, 122], [139, 117], [ 11,  44], [ 22, 215],
-		[ 44,  79], [ 88, 174], [176, 213], [125, 233], [250, 230], [233, 231], [207, 173], [131, 232],
-		[ 27, 116], [ 54, 214], [108, 244], [216, 234], [173, 168], [ 71,  80], [142,  88], [  1, 175],
-	];
-
-	/**
-	 * @var int[]
-	 */
-	protected array $num = [];
-
-	/**
-	 * Polynomial constructor.
-	 */
-	public function __construct(array $num = null, int $shift = null){
-		$this->setNum($num ?? [1], $shift);
-	}
-
-	/**
-	 *
-	 */
-	public function getNum():array{
-		return $this->num;
-	}
-
-	/**
-	 * @param int[]    $num
-	 * @param int|null $shift
-	 *
-	 * @return \chillerlan\QRCode\Helpers\Polynomial
-	 */
-	public function setNum(array $num, int $shift = null):Polynomial{
-		$offset = 0;
-		$numCount = count($num);
-
-		while($offset < $numCount && $num[$offset] === 0){
-			$offset++;
-		}
-
-		$this->num = array_fill(0, $numCount - $offset + ($shift ?? 0), 0);
-
-		for($i = 0; $i < $numCount - $offset; $i++){
-			$this->num[$i] = $num[$i + $offset];
-		}
-
-		return $this;
-	}
-
-	/**
-	 * @param int[] $e
-	 *
-	 * @return \chillerlan\QRCode\Helpers\Polynomial
-	 */
-	public function multiply(array $e):Polynomial{
-		$n = array_fill(0, count($this->num) + count($e) - 1, 0);
-
-		foreach($this->num as $i => $vi){
-			$vi = $this->glog($vi);
-
-			foreach($e as $j => $vj){
-				$n[$i + $j] ^= $this->gexp($vi + $this->glog($vj));
-			}
-
-		}
-
-		$this->setNum($n);
-
-		return $this;
-	}
-
-	/**
-	 * @param int[] $e
-	 *
-	 * @return \chillerlan\QRCode\Helpers\Polynomial
-	 */
-	public function mod(array $e):Polynomial{
-		$n = $this->num;
-
-		if(count($n) - count($e) < 0){
-			return $this;
-		}
-
-		$ratio = $this->glog($n[0]) - $this->glog($e[0]);
-
-		foreach($e as $i => $v){
-			$n[$i] ^= $this->gexp($this->glog($v) + $ratio);
-		}
-
-		$this->setNum($n)->mod($e);
-
-		return $this;
-	}
-
-	/**
-	 * @throws \chillerlan\QRCode\QRCodeException
-	 */
-	public function glog(int $n):int{
-
-		if($n < 1){
-			throw new QRCodeException(sprintf('log(%s)', $n));
-		}
-
-		return Polynomial::table[$n][1];
-	}
-
-	/**
-	 *
-	 */
-	public function gexp(int $n):int{
-
-		if($n < 0){
-			$n += 255;
-		}
-		elseif($n >= 256){
-			$n -= 255;
-		}
-
-		return Polynomial::table[$n][0];
-	}
-
-}

+ 0 - 42
tests/Helpers/PolynomialTest.php

@@ -1,42 +0,0 @@
-<?php
-/**
- * Class PolynomialTest
- *
- * @filesource   PolynomialTest.php
- * @created      09.02.2016
- * @package      chillerlan\QRCodeTest\Helpers
- * @author       Smiley <smiley@chillerlan.net>
- * @copyright    2015 Smiley
- * @license      MIT
- */
-
-namespace chillerlan\QRCodeTest\Helpers;
-
-use chillerlan\QRCode\Helpers\Polynomial;
-use chillerlan\QRCode\QRCodeException;
-use PHPUnit\Framework\TestCase;
-
-/**
- * Polynomial coverage test
- */
-final class PolynomialTest extends TestCase{
-
-	protected Polynomial $polynomial;
-
-	protected function setUp():void{
-		$this->polynomial = new Polynomial;
-	}
-
-	public function testGexp():void{
-		$this::assertSame(142, $this->polynomial->gexp(-1));
-		$this::assertSame(133, $this->polynomial->gexp(128));
-		$this::assertSame(2,   $this->polynomial->gexp(256));
-	}
-
-	public function testGlogException():void{
-		$this->expectException(QRCodeException::class);
-		$this->expectExceptionMessage('log(0)');
-
-		$this->polynomial->glog(0);
-	}
-}