Passed
Push — v5 ( 92f563...26536d )
by smiley
02:19
created

ReedSolomon::interleaveEcBytes()   B

Complexity

Conditions 7
Paths 26

Size

Total Lines 63
Code Lines 40

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 7
eloc 40
c 1
b 0
f 0
nc 26
nop 1
dl 0
loc 63
rs 8.3466

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/**
3
 * Class ReedSolomon
4
 *
5
 * @filesource   ReedSolomon.php
6
 * @created      07.01.2021
7
 * @package      chillerlan\QRCode\Common
8
 * @author       smiley <[email protected]>
9
 * @copyright    2021 smiley
10
 * @license      MIT
11
 */
12
13
namespace chillerlan\QRCode\Common;
14
15
use chillerlan\QRCode\Helpers\BitBuffer;
16
use chillerlan\QRCode\Helpers\Polynomial;
17
18
use SplFixedArray;
19
use function array_fill, array_merge, count, max;
20
21
/**
22
 * ISO/IEC 18004:2000 Section 8.5 ff
23
 *
24
 * @see http://www.thonky.com/qr-code-tutorial/error-correction-coding
25
 */
26
final class ReedSolomon{
27
28
	private Version       $version;
29
	private EccLevel      $eccLevel;
30
31
	private SplFixedArray $interleavedData;
32
	private int           $interleavedDataIndex;
33
34
	/**
35
	 * ReedSolomon constructor.
36
	 */
37
	public function __construct(Version $version, EccLevel $eccLevel){
38
		$this->version  = $version;
39
		$this->eccLevel = $eccLevel;
40
	}
41
42
	/**
43
	 * ECC interleaving
44
	 *
45
	 * @return \SplFixedArray<int>
46
	 */
47
	public function interleaveEcBytes(BitBuffer $bitBuffer):SplFixedArray{
48
		[$l1, $l2, $b1, $b2] = $this->version->getRSBlocks($this->eccLevel->getLevel());
49
50
		$numRsBlocks = $l1 + $l2;
51
		$ecBytes     = new SplFixedArray($numRsBlocks);
52
		$rsBlocks    = array_fill(0, $l1, [$b1, $b2]);
53
54
		if($l2 > 0){
55
			$rsBlocks = array_merge($rsBlocks, array_fill(0, $l2, [$b1 + 1, $b2 + 1]));
56
		}
57
58
		$dataBytes      = SplFixedArray::fromArray($rsBlocks);
59
		$maxDataBytes   = 0;
60
		$maxEcBytes     = 0;
61
		$dataByteOffset = 0;
62
		$bitBufferData  = $bitBuffer->getBuffer();
63
64
		foreach($rsBlocks as $key => $block){
65
			[$rsBlockTotal, $dataByteCount] = $block;
66
67
			$ecByteCount     = $rsBlockTotal - $dataByteCount;
68
			$maxDataBytes    = max($maxDataBytes, $dataByteCount);
69
			$maxEcBytes      = max($maxEcBytes, $ecByteCount);
70
			$dataBytes[$key] = new SplFixedArray($dataByteCount);
71
72
			foreach($dataBytes[$key] as $i => $_){
73
				$dataBytes[$key][$i] = $bitBufferData[$i + $dataByteOffset] & 0xff;
74
			}
75
76
			$rsPoly  = new Polynomial;
77
			$modPoly = new Polynomial;
78
79
			for($i = 0; $i < $ecByteCount; $i++){
80
				$modPoly->setNum([1, $modPoly->gexp($i)]);
81
				$rsPoly->multiply($modPoly->getNum());
82
			}
83
84
			$rsPolyCount = count($rsPoly->getNum()) - 1;
85
86
			$modPoly
87
				->setNum($dataBytes[$key]->toArray(), $rsPolyCount)
88
				->mod($rsPoly->getNum())
89
			;
90
91
			$ecBytes[$key] = new SplFixedArray($rsPolyCount);
92
			$num           = $modPoly->getNum();
93
			$count         = count($num) - count($ecBytes[$key]);
94
95
			foreach($ecBytes[$key] as $i => $_){
96
				$modIndex          = $i + $count;
97
				$ecBytes[$key][$i] = $modIndex >= 0 ? $num[$modIndex] : 0;
98
			}
99
100
			$dataByteOffset += $dataByteCount;
101
		}
102
103
		$this->interleavedData      = new SplFixedArray($this->version->getTotalCodewords());
104
		$this->interleavedDataIndex = 0;
105
106
		$this->interleave($dataBytes, $maxDataBytes, $numRsBlocks);
107
		$this->interleave($ecBytes, $maxEcBytes, $numRsBlocks);
108
109
		return $this->interleavedData;
110
	}
111
112
	/**
113
	 *
114
	 */
115
	private function interleave(SplFixedArray $byteArray, int $maxBytes, int $numRsBlocks):void{
116
		for($x = 0; $x < $maxBytes; $x++){
117
			for($y = 0; $y < $numRsBlocks; $y++){
118
				if($x < count($byteArray[$y])){
119
					$this->interleavedData[$this->interleavedDataIndex++] = $byteArray[$y][$x];
120
				}
121
			}
122
		}
123
	}
124
125
}
126