Jelajahi Sumber

:octocat: move deinterleaving to ReedSolomonDecoder

smiley 2 tahun lalu
induk
melakukan
8eb54d4eef
2 mengubah file dengan 94 tambahan dan 85 penghapusan
  1. 89 4
      src/Common/ReedSolomonDecoder.php
  2. 5 81
      src/Decoder/Decoder.php

+ 89 - 4
src/Common/ReedSolomonDecoder.php

@@ -34,11 +34,23 @@ use function array_fill, array_reverse, count;
  */
  */
 final class ReedSolomonDecoder{
 final class ReedSolomonDecoder{
 
 
+	private Version  $version;
+	private EccLevel $eccLevel;
+
+	/**
+	 * ReedSolomonDecoder constructor
+	 */
+	public function __construct(Version $version, EccLevel $eccLevel){
+		$this->version  = $version;
+		$this->eccLevel = $eccLevel;
+	}
+
 	/**
 	/**
 	 * Error-correct and copy data blocks together into a stream of bytes
 	 * Error-correct and copy data blocks together into a stream of bytes
 	 */
 	 */
-	public function decode(array $dataBlocks):array{
-		$resultBytes = [];
+	public function decode(array $rawCodewords):BitBuffer{
+		$dataBlocks  = $this->deinterleaveRawBytes($rawCodewords);
+		$dataBytes   = [];
 
 
 		foreach($dataBlocks as $dataBlock){
 		foreach($dataBlocks as $dataBlock){
 			[$numDataCodewords, $codewordBytes] = $dataBlock;
 			[$numDataCodewords, $codewordBytes] = $dataBlock;
@@ -46,11 +58,84 @@ final class ReedSolomonDecoder{
 			$corrected = $this->correctErrors($codewordBytes, $numDataCodewords);
 			$corrected = $this->correctErrors($codewordBytes, $numDataCodewords);
 
 
 			for($i = 0; $i < $numDataCodewords; $i++){
 			for($i = 0; $i < $numDataCodewords; $i++){
-				$resultBytes[] = $corrected[$i];
+				$dataBytes[] = $corrected[$i];
+			}
+		}
+
+		return new BitBuffer($dataBytes);
+	}
+
+	/**
+	 * When QR Codes use multiple data blocks, they are actually interleaved.
+	 * That is, the first byte of data block 1 to n is written, then the second bytes, and so on. This
+	 * method will separate the data into original blocks.
+	 *
+	 * @throws \chillerlan\QRCode\Decoder\QRCodeDecoderException
+	 */
+	private function deinterleaveRawBytes(array $rawCodewords):array{
+		// Figure out the number and size of data blocks used by this version and
+		// error correction level
+		[$numEccCodewords, $eccBlocks] = $this->version->getRSBlocks($this->eccLevel);
+
+		// Now establish DataBlocks of the appropriate size and number of data codewords
+		$result          = [];//new DataBlock[$totalBlocks];
+		$numResultBlocks = 0;
+
+		foreach($eccBlocks as $blockData){
+			[$numEccBlocks, $eccPerBlock] = $blockData;
+
+			for($i = 0; $i < $numEccBlocks; $i++, $numResultBlocks++){
+				$result[$numResultBlocks] = [$eccPerBlock, array_fill(0, $numEccCodewords + $eccPerBlock, 0)];
+			}
+		}
+
+		// All blocks have the same amount of data, except that the last n
+		// (where n may be 0) have 1 more byte. Figure out where these start.
+		/** @phan-suppress-next-line PhanTypePossiblyInvalidDimOffset */
+		$shorterBlocksTotalCodewords = count($result[0][1]);
+		$longerBlocksStartAt         = count($result) - 1;
+
+		while($longerBlocksStartAt >= 0){
+			$numCodewords = count($result[$longerBlocksStartAt][1]);
+
+			if($numCodewords == $shorterBlocksTotalCodewords){
+				break;
 			}
 			}
+
+			$longerBlocksStartAt--;
 		}
 		}
 
 
-		return $resultBytes;
+		$longerBlocksStartAt++;
+
+		$shorterBlocksNumDataCodewords = $shorterBlocksTotalCodewords - $numEccCodewords;
+		// The last elements of result may be 1 element longer;
+		// first fill out as many elements as all of them have
+		$rawCodewordsOffset = 0;
+
+		for($i = 0; $i < $shorterBlocksNumDataCodewords; $i++){
+			for($j = 0; $j < $numResultBlocks; $j++){
+				$result[$j][1][$i] = $rawCodewords[$rawCodewordsOffset++];
+			}
+		}
+
+		// Fill out the last data block in the longer ones
+		for($j = $longerBlocksStartAt; $j < $numResultBlocks; $j++){
+			$result[$j][1][$shorterBlocksNumDataCodewords] = $rawCodewords[$rawCodewordsOffset++];
+		}
+
+		// Now add in error correction blocks
+		/** @phan-suppress-next-line PhanTypePossiblyInvalidDimOffset */
+		$max = count($result[0][1]);
+
+		for($i = $shorterBlocksNumDataCodewords; $i < $max; $i++){
+			for($j = 0; $j < $numResultBlocks; $j++){
+				$iOffset                 = $j < $longerBlocksStartAt ? $i : $i + 1;
+				$result[$j][1][$iOffset] = $rawCodewords[$rawCodewordsOffset++];
+			}
+		}
+
+		// DataBlocks containing original bytes, "de-interleaved" from representation in the QR Code
+		return $result;
 	}
 	}
 
 
 	/**
 	/**

+ 5 - 81
src/Decoder/Decoder.php

@@ -78,96 +78,20 @@ final class Decoder{
 			throw new QRCodeDecoderException('unable to read version or format info'); // @codeCoverageIgnore
 			throw new QRCodeDecoderException('unable to read version or format info'); // @codeCoverageIgnore
 		}
 		}
 
 
-		$resultBytes = (new ReedSolomonDecoder)->decode($this->getDataBlocks($rawCodewords));
-		// Decode the contents of that stream of bytes
+		$resultBytes = (new ReedSolomonDecoder($this->version, $this->eccLevel))->decode($rawCodewords);
+
 		return $this->decodeBitStream($resultBytes);
 		return $this->decodeBitStream($resultBytes);
 	}
 	}
 
 
 	/**
 	/**
-	 * When QR Codes use multiple data blocks, they are actually interleaved.
-	 * That is, the first byte of data block 1 to n is written, then the second bytes, and so on. This
-	 * method will separate the data into original blocks.
-	 *
-	 * @param array $rawCodewords bytes as read directly from the QR Code
+	 * Decode the contents of that stream of bytes
 	 *
 	 *
-	 * @return array DataBlocks containing original bytes, "de-interleaved" from representation in the QR Code
-	 * @throws \chillerlan\QRCode\Decoder\QRCodeDecoderException
-	 */
-	private function getDataBlocks(array $rawCodewords):array{
-
-		// Figure out the number and size of data blocks used by this version and
-		// error correction level
-		/** @phan-suppress-next-line PhanTypeMismatchArgumentNullable */
-		[$numEccCodewords, $eccBlocks] = $this->version->getRSBlocks($this->eccLevel);
-
-		// Now establish DataBlocks of the appropriate size and number of data codewords
-		$result          = [];//new DataBlock[$totalBlocks];
-		$numResultBlocks = 0;
-
-		foreach($eccBlocks as $blockData){
-			[$numEccBlocks, $eccPerBlock] = $blockData;
-
-			for($i = 0; $i < $numEccBlocks; $i++, $numResultBlocks++){
-				$result[$numResultBlocks] = [$eccPerBlock, array_fill(0, $numEccCodewords + $eccPerBlock, 0)];
-			}
-		}
-
-		// All blocks have the same amount of data, except that the last n
-		// (where n may be 0) have 1 more byte. Figure out where these start.
-		/** @phan-suppress-next-line PhanTypePossiblyInvalidDimOffset */
-		$shorterBlocksTotalCodewords = count($result[0][1]);
-		$longerBlocksStartAt         = count($result) - 1;
-
-		while($longerBlocksStartAt >= 0){
-			$numCodewords = count($result[$longerBlocksStartAt][1]);
-
-			if($numCodewords == $shorterBlocksTotalCodewords){
-				break;
-			}
-
-			$longerBlocksStartAt--;
-		}
-
-		$longerBlocksStartAt++;
-
-		$shorterBlocksNumDataCodewords = $shorterBlocksTotalCodewords - $numEccCodewords;
-		// The last elements of result may be 1 element longer;
-		// first fill out as many elements as all of them have
-		$rawCodewordsOffset = 0;
-
-		for($i = 0; $i < $shorterBlocksNumDataCodewords; $i++){
-			for($j = 0; $j < $numResultBlocks; $j++){
-				$result[$j][1][$i] = $rawCodewords[$rawCodewordsOffset++];
-			}
-		}
-
-		// Fill out the last data block in the longer ones
-		for($j = $longerBlocksStartAt; $j < $numResultBlocks; $j++){
-			$result[$j][1][$shorterBlocksNumDataCodewords] = $rawCodewords[$rawCodewordsOffset++];
-		}
-
-		// Now add in error correction blocks
-		/** @phan-suppress-next-line PhanTypePossiblyInvalidDimOffset */
-		$max = count($result[0][1]);
-
-		for($i = $shorterBlocksNumDataCodewords; $i < $max; $i++){
-			for($j = 0; $j < $numResultBlocks; $j++){
-				$iOffset                 = $j < $longerBlocksStartAt ? $i : $i + 1;
-				$result[$j][1][$iOffset] = $rawCodewords[$rawCodewordsOffset++];
-			}
-		}
-
-		return $result;
-	}
-
-	/**
 	 * @throws \chillerlan\QRCode\Decoder\QRCodeDecoderException
 	 * @throws \chillerlan\QRCode\Decoder\QRCodeDecoderException
 	 */
 	 */
-	private function decodeBitStream(array $bytes):DecoderResult{
-		$bitBuffer      = new BitBuffer($bytes);
+	private function decodeBitStream(BitBuffer $bitBuffer):DecoderResult{
+		$versionNumber  = $this->version->getVersionNumber();
 		$symbolSequence = -1;
 		$symbolSequence = -1;
 		$parityData     = -1;
 		$parityData     = -1;
-		$versionNumber  = $this->version->getVersionNumber();
 		$eciCharset     = null;
 		$eciCharset     = null;
 		$fc1InEffect    = false;
 		$fc1InEffect    = false;
 		$result         = '';
 		$result         = '';