Skip to content

Commit c76c70b

Browse files
committed
:octocat: simplify Number and AlphaNum classes (backport from dev-main)
1 parent bbb2414 commit c76c70b

File tree

2 files changed

+57
-83
lines changed

2 files changed

+57
-83
lines changed

src/Data/AlphaNum.php

Lines changed: 39 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
namespace chillerlan\QRCode\Data;
1212

1313
use chillerlan\QRCode\Common\{BitBuffer, Mode};
14-
use function array_flip, ceil, intdiv, str_split;
14+
use function ceil, intdiv, preg_match, strpos;
1515

1616
/**
1717
* Alphanumeric mode: 0 to 9, A to Z, space, $ % * + - . / :
@@ -24,18 +24,9 @@ final class AlphaNum extends QRDataModeAbstract{
2424
/**
2525
* ISO/IEC 18004:2000 Table 5
2626
*
27-
* @var int[]
27+
* @var string
2828
*/
29-
private const CHAR_TO_ORD = [
30-
// phpcs:ignore
31-
'0' => 0, '1' => 1, '2' => 2, '3' => 3, '4' => 4, '5' => 5, '6' => 6, '7' => 7,
32-
// phpcs:ignore
33-
'8' => 8, '9' => 9, 'A' => 10, 'B' => 11, 'C' => 12, 'D' => 13, 'E' => 14, 'F' => 15,
34-
'G' => 16, 'H' => 17, 'I' => 18, 'J' => 19, 'K' => 20, 'L' => 21, 'M' => 22, 'N' => 23,
35-
'O' => 24, 'P' => 25, 'Q' => 26, 'R' => 27, 'S' => 28, 'T' => 29, 'U' => 30, 'V' => 31,
36-
'W' => 32, 'X' => 33, 'Y' => 34, 'Z' => 35, ' ' => 36, '$' => 37, '%' => 38, '*' => 39,
37-
'+' => 40, '-' => 41, '.' => 42, '/' => 43, ':' => 44,
38-
];
29+
private const CHAR_MAP = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:';
3930

4031
/**
4132
* @inheritDoc
@@ -53,18 +44,7 @@ public function getLengthInBits():int{
5344
* @inheritDoc
5445
*/
5546
public static function validateString(string $string):bool{
56-
57-
if($string === ''){
58-
return false;
59-
}
60-
61-
foreach(str_split($string) as $chr){
62-
if(!isset(self::CHAR_TO_ORD[$chr])){
63-
return false;
64-
}
65-
}
66-
67-
return true;
47+
return (bool)preg_match('/^[A-Z\d %$*+-.:\/]+$/', $string);
6848
}
6949

7050
/**
@@ -80,12 +60,15 @@ public function write(BitBuffer $bitBuffer, int $versionNumber):QRDataModeInterf
8060

8161
// encode 2 characters in 11 bits
8262
for($i = 0; ($i + 1) < $len; $i += 2){
83-
$bitBuffer->put((self::CHAR_TO_ORD[$this->data[$i]] * 45 + self::CHAR_TO_ORD[$this->data[($i + 1)]]), 11);
63+
$bitBuffer->put(
64+
($this->ord($this->data[$i]) * 45 + $this->ord($this->data[($i + 1)])),
65+
11,
66+
);
8467
}
8568

8669
// encode a remaining character in 6 bits
8770
if($i < $len){
88-
$bitBuffer->put(self::CHAR_TO_ORD[$this->data[$i]], 6);
71+
$bitBuffer->put($this->ord($this->data[$i]), 6);
8972
}
9073

9174
return $this;
@@ -97,19 +80,7 @@ public function write(BitBuffer $bitBuffer, int $versionNumber):QRDataModeInterf
9780
* @throws \chillerlan\QRCode\Data\QRCodeDataException
9881
*/
9982
public static function decodeSegment(BitBuffer $bitBuffer, int $versionNumber):string{
100-
$length = $bitBuffer->read(self::getLengthBits($versionNumber));
101-
$charmap = array_flip(self::CHAR_TO_ORD);
102-
103-
// @todo
104-
$toAlphaNumericChar = function(int $ord) use ($charmap):string{
105-
106-
if(isset($charmap[$ord])){
107-
return $charmap[$ord];
108-
}
109-
110-
throw new QRCodeDataException('invalid character value: '.$ord);
111-
};
112-
83+
$length = $bitBuffer->read(self::getLengthBits($versionNumber));
11384
$result = '';
11485
// Read two characters at a time
11586
while($length > 1){
@@ -118,9 +89,9 @@ public static function decodeSegment(BitBuffer $bitBuffer, int $versionNumber):s
11889
throw new QRCodeDataException('not enough bits available'); // @codeCoverageIgnore
11990
}
12091

121-
$nextTwoCharsBits = $bitBuffer->read(11);
122-
$result .= $toAlphaNumericChar(intdiv($nextTwoCharsBits, 45));
123-
$result .= $toAlphaNumericChar($nextTwoCharsBits % 45);
92+
$nextTwoCharsBits = $bitBuffer->read(11);
93+
$result .= self::chr(intdiv($nextTwoCharsBits, 45));
94+
$result .= self::chr($nextTwoCharsBits % 45);
12495
$length -= 2;
12596
}
12697

@@ -130,10 +101,35 @@ public static function decodeSegment(BitBuffer $bitBuffer, int $versionNumber):s
130101
throw new QRCodeDataException('not enough bits available'); // @codeCoverageIgnore
131102
}
132103

133-
$result .= $toAlphaNumericChar($bitBuffer->read(6));
104+
$result .= self::chr($bitBuffer->read(6));
134105
}
135106

136107
return $result;
137108
}
138109

110+
/**
111+
* @throws \chillerlan\QRCode\Data\QRCodeDataException
112+
*/
113+
private function ord(string $chr):int{
114+
$ord = strpos(self::CHAR_MAP, $chr);
115+
116+
if($ord === false){
117+
throw new QRCodeDataException('invalid character'); // @codeCoverageIgnore
118+
}
119+
120+
return $ord;
121+
}
122+
123+
/**
124+
* @throws \chillerlan\QRCode\Data\QRCodeDataException
125+
*/
126+
private static function chr(int $ord):string{
127+
128+
if($ord < 0 || $ord > 44){
129+
throw new QRCodeDataException('invalid character code'); // @codeCoverageIgnore
130+
}
131+
132+
return self::CHAR_MAP[$ord];
133+
}
134+
139135
}

src/Data/Number.php

Lines changed: 18 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,6 @@
2121
*/
2222
final class Number extends QRDataModeAbstract{
2323

24-
/**
25-
* @var int[]
26-
*/
27-
private const NUMBER_TO_ORD = [
28-
'0' => 0, '1' => 1, '2' => 2, '3' => 3, '4' => 4, '5' => 5, '6' => 6, '7' => 7, '8' => 8, '9' => 9,
29-
];
30-
3124
/**
3225
* @inheritDoc
3326
*/
@@ -44,18 +37,7 @@ public function getLengthInBits():int{
4437
* @inheritDoc
4538
*/
4639
public static function validateString(string $string):bool{
47-
48-
if($string === ''){
49-
return false;
50-
}
51-
52-
foreach(str_split($string) as $chr){
53-
if(!isset(self::NUMBER_TO_ORD[$chr])){
54-
return false;
55-
}
56-
}
57-
58-
return true;
40+
return (bool)preg_match('/^\d+$/', $string);
5941
}
6042

6143
/**
@@ -95,12 +77,20 @@ public function write(BitBuffer $bitBuffer, int $versionNumber):QRDataModeInterf
9577

9678
/**
9779
* get the code for the given numeric string
80+
*
81+
* @throws \chillerlan\QRCode\Data\QRCodeDataException
9882
*/
9983
private function parseInt(string $string):int{
10084
$num = 0;
10185

102-
foreach(unpack('C*', $string) as $chr){
103-
$num = ($num * 10 + $chr - 48);
86+
$ords = unpack('C*', $string);
87+
88+
if($ords === false){
89+
throw new QRCodeDataException('unpack() error');
90+
}
91+
92+
foreach($ords as $ord){
93+
$num = ($num * 10 + $ord - 48);
10494
}
10595

10696
return $num;
@@ -112,19 +102,7 @@ private function parseInt(string $string):int{
112102
* @throws \chillerlan\QRCode\Data\QRCodeDataException
113103
*/
114104
public static function decodeSegment(BitBuffer $bitBuffer, int $versionNumber):string{
115-
$length = $bitBuffer->read(self::getLengthBits($versionNumber));
116-
$charmap = array_flip(self::NUMBER_TO_ORD);
117-
118-
// @todo
119-
$toNumericChar = function(int $ord) use ($charmap):string{
120-
121-
if(isset($charmap[$ord])){
122-
return $charmap[$ord];
123-
}
124-
125-
throw new QRCodeDataException('invalid character value: '.$ord);
126-
};
127-
105+
$length = $bitBuffer->read(self::getLengthBits($versionNumber));
128106
$result = '';
129107
// Read three digits at a time
130108
while($length >= 3){
@@ -139,9 +117,9 @@ public static function decodeSegment(BitBuffer $bitBuffer, int $versionNumber):s
139117
throw new QRCodeDataException('error decoding numeric value');
140118
}
141119

142-
$result .= $toNumericChar(intdiv($threeDigitsBits, 100));
143-
$result .= $toNumericChar(intdiv($threeDigitsBits, 10) % 10);
144-
$result .= $toNumericChar($threeDigitsBits % 10);
120+
$result .= intdiv($threeDigitsBits, 100);
121+
$result .= (intdiv($threeDigitsBits, 10) % 10);
122+
$result .= ($threeDigitsBits % 10);
145123

146124
$length -= 3;
147125
}
@@ -158,8 +136,8 @@ public static function decodeSegment(BitBuffer $bitBuffer, int $versionNumber):s
158136
throw new QRCodeDataException('error decoding numeric value');
159137
}
160138

161-
$result .= $toNumericChar(intdiv($twoDigitsBits, 10));
162-
$result .= $toNumericChar($twoDigitsBits % 10);
139+
$result .= intdiv($twoDigitsBits, 10);
140+
$result .= ($twoDigitsBits % 10);
163141
}
164142
elseif($length === 1){
165143
// One digit left over to read
@@ -173,7 +151,7 @@ public static function decodeSegment(BitBuffer $bitBuffer, int $versionNumber):s
173151
throw new QRCodeDataException('error decoding numeric value');
174152
}
175153

176-
$result .= $toNumericChar($digitBits);
154+
$result .= $digitBits;
177155
}
178156

179157
return $result;

0 commit comments

Comments
 (0)