From 8bd573ddfdb6b65c5a5ecf9bba4cae77d4ee7d8e Mon Sep 17 00:00:00 2001 From: codemasher Date: Sat, 21 Nov 2020 18:40:53 +0100 Subject: [PATCH] :bath: data mode rework --- src/Data/AlphaNum.php | 21 ++++++++++++-------- src/Data/Byte.php | 20 +++++++++++-------- src/Data/Kanji.php | 24 +++++++++++----------- src/Data/Number.php | 31 ++++++++++++++++++----------- src/Data/QRData.php | 4 ++-- src/Data/QRDataModeAbstract.php | 34 ++------------------------------ src/Data/QRDataModeInterface.php | 4 +++- 7 files changed, 64 insertions(+), 74 deletions(-) diff --git a/src/Data/AlphaNum.php b/src/Data/AlphaNum.php index fd16000c6..a78d8fec3 100644 --- a/src/Data/AlphaNum.php +++ b/src/Data/AlphaNum.php @@ -12,6 +12,7 @@ namespace chillerlan\QRCode\Data; +use chillerlan\QRCode\Helpers\BitBuffer; use chillerlan\QRCode\QRCode; use function ceil, ord, sprintf, str_split; @@ -38,15 +39,13 @@ final class AlphaNum extends QRDataModeAbstract{ '+' => 40, '-' => 41, '.' => 42, '/' => 43, ':' => 44, ]; - protected int $datamode = QRCode::DATA_ALPHANUM; - protected array $lengthBits = [9, 11, 13]; /** * @inheritdoc */ public function getLengthInBits():int{ - return (int)ceil($this->getLength() * (11 / 2)); + return (int)ceil($this->getCharCount() * (11 / 2)); } /** @@ -66,16 +65,22 @@ final class AlphaNum extends QRDataModeAbstract{ /** * @inheritdoc */ - public function write(int $version):void{ - $this->writeSegmentHeader($version); - $len = $this->getLength(); + public function write(BitBuffer $bitBuffer, int $version):void{ + $len = $this->getCharCount(); + $bitBuffer + ->put(QRCode::DATA_ALPHANUM, 4) + ->put($len, $this->getLengthBitsForVersion($version)) + ; + + // encode 2 characters in 11 bits for($i = 0; $i + 1 < $len; $i += 2){ - $this->bitBuffer->put($this->getCharCode($this->data[$i]) * 45 + $this->getCharCode($this->data[$i + 1]), 11); + $bitBuffer->put($this->getCharCode($this->data[$i]) * 45 + $this->getCharCode($this->data[$i + 1]), 11); } + // encode a remaining character in 6 bits if($i < $len){ - $this->bitBuffer->put($this->getCharCode($this->data[$i]), 6); + $bitBuffer->put($this->getCharCode($this->data[$i]), 6); } } diff --git a/src/Data/Byte.php b/src/Data/Byte.php index c93a7f40e..738ec7d32 100644 --- a/src/Data/Byte.php +++ b/src/Data/Byte.php @@ -12,6 +12,7 @@ namespace chillerlan\QRCode\Data; +use chillerlan\QRCode\Helpers\BitBuffer; use chillerlan\QRCode\QRCode; use function ord; @@ -24,15 +25,13 @@ use function ord; */ final class Byte extends QRDataModeAbstract{ - protected int $datamode = QRCode::DATA_BYTE; - protected array $lengthBits = [8, 16, 16]; /** * @inheritdoc */ public function getLengthInBits():int{ - return $this->getLength() * 8; + return $this->getCharCount() * 8; } /** @@ -45,13 +44,18 @@ final class Byte extends QRDataModeAbstract{ /** * @inheritdoc */ - public function write(int $version):void{ - $this->writeSegmentHeader($version); - $len = $this->getLength(); - $i = 0; + public function write(BitBuffer $bitBuffer, int $version):void{ + $len = $this->getCharCount(); + + $bitBuffer + ->put(QRCode::DATA_BYTE, 4) + ->put($len, $this->getLengthBitsForVersion($version)) + ; + + $i = 0; while($i < $len){ - $this->bitBuffer->put(ord($this->data[$i]), 8); + $bitBuffer->put(ord($this->data[$i]), 8); $i++; } diff --git a/src/Data/Kanji.php b/src/Data/Kanji.php index 91cd85175..676fe2a93 100644 --- a/src/Data/Kanji.php +++ b/src/Data/Kanji.php @@ -25,12 +25,10 @@ use function mb_convert_encoding, mb_detect_encoding, mb_strlen, ord, sprintf, s */ final class Kanji extends QRDataModeAbstract{ - protected int $datamode = QRCode::DATA_KANJI; - protected array $lengthBits = [8, 10, 12]; - public function __construct(BitBuffer $bitBuffer, string $data){ - parent::__construct($bitBuffer, $data); + public function __construct(string $data){ + parent::__construct($data); /** @noinspection PhpFieldAssignmentTypeMismatchInspection */ $this->data = mb_convert_encoding($this->data, 'SJIS', mb_detect_encoding($this->data)); @@ -39,7 +37,7 @@ final class Kanji extends QRDataModeAbstract{ /** * @inheritdoc */ - protected function getLength():int{ + protected function getCharCount():int{ return mb_strlen($this->data, 'SJIS'); } @@ -47,7 +45,7 @@ final class Kanji extends QRDataModeAbstract{ * @inheritdoc */ public function getLengthInBits():int{ - return $this->getLength() * 13; + return $this->getCharCount() * 13; } /** @@ -75,9 +73,14 @@ final class Kanji extends QRDataModeAbstract{ * * @throws \chillerlan\QRCode\Data\QRCodeDataException on an illegal character occurence */ - public function write(int $version):void{ - $this->writeSegmentHeader($version); - $len = strlen($this->data); // not self::getLength() - we need 8-bit length + public function write(BitBuffer $bitBuffer, int $version):void{ + + $bitBuffer + ->put(QRCode::DATA_KANJI, 4) + ->put($this->getCharCount(), $this->getLengthBitsForVersion($version)) + ; + + $len = strlen($this->data); for($i = 0; $i + 1 < $len; $i += 2){ $c = ((0xff & ord($this->data[$i])) << 8) | (0xff & ord($this->data[$i + 1])); @@ -92,8 +95,7 @@ final class Kanji extends QRDataModeAbstract{ throw new QRCodeDataException(sprintf('illegal char at %d [%d]', $i + 1, $c)); } - $this->bitBuffer->put(((($c >> 8) & 0xff) * 0xC0) + ($c & 0xff), 13); - + $bitBuffer->put(((($c >> 8) & 0xff) * 0xC0) + ($c & 0xff), 13); } if($i < $len){ diff --git a/src/Data/Number.php b/src/Data/Number.php index 90f7d7632..0fe616ff9 100644 --- a/src/Data/Number.php +++ b/src/Data/Number.php @@ -12,6 +12,7 @@ namespace chillerlan\QRCode\Data; +use chillerlan\QRCode\Helpers\BitBuffer; use chillerlan\QRCode\QRCode; use function ceil, ord, sprintf, str_split, substr; @@ -31,15 +32,13 @@ final class Number extends QRDataModeAbstract{ '0' => 0, '1' => 1, '2' => 2, '3' => 3, '4' => 4, '5' => 5, '6' => 6, '7' => 7, '8' => 8, '9' => 9, ]; - protected int $datamode = QRCode::DATA_NUMBER; - protected array $lengthBits = [10, 12, 14]; /** * @inheritdoc */ public function getLengthInBits():int{ - return (int)ceil($this->getLength() * (10 / 3)); + return (int)ceil($this->getCharCount() * (10 / 3)); } /** @@ -59,23 +58,31 @@ final class Number extends QRDataModeAbstract{ /** * @inheritdoc */ - public function write(int $version):void{ - $this->writeSegmentHeader($version); - $len = $this->getLength(); - $i = 0; + public function write(BitBuffer $bitBuffer, int $version):void{ + $len = $this->getCharCount(); + $bitBuffer + ->put(QRCode::DATA_NUMBER, 4) + ->put($len, $this->getLengthBitsForVersion($version)) + ; + + $i = 0; + + // encode numeric triplets in 10 bits while($i + 2 < $len){ - $this->bitBuffer->put($this->parseInt(substr($this->data, $i, 3)), 10); + $bitBuffer->put($this->parseInt(substr($this->data, $i, 3)), 10); $i += 3; } if($i < $len){ - if($len - $i === 1){ - $this->bitBuffer->put($this->parseInt(substr($this->data, $i, $i + 1)), 4); + // encode 2 remaining numbers in 7 bits + if($len - $i === 2){ + $bitBuffer->put($this->parseInt(substr($this->data, $i, 2)), 7); } - elseif($len - $i === 2){ - $this->bitBuffer->put($this->parseInt(substr($this->data, $i, $i + 2)), 7); + // encode one remaining number in 4 bits + elseif($len - $i === 1){ + $bitBuffer->put($this->parseInt(substr($this->data, $i, 1)), 4); } } diff --git a/src/Data/QRData.php b/src/Data/QRData.php index aea59ae3f..856901963 100644 --- a/src/Data/QRData.php +++ b/src/Data/QRData.php @@ -188,7 +188,7 @@ class QRData{ foreach($dataSegments as $segment){ [$class, $data] = $segment; - $this->dataSegments[] = new $class($this->bitBuffer, $data); + $this->dataSegments[] = new $class($data); } $this->version = $this->options->version === QRCode::VERSION_AUTO @@ -271,7 +271,7 @@ class QRData{ $MAX_BITS = $this->maxBitsForEcc[$this->version]; foreach($this->dataSegments as $segment){ - $segment->write($this->version); + $segment->write($this->bitBuffer, $this->version); } // overflow, likely caused due to invalid version setting diff --git a/src/Data/QRDataModeAbstract.php b/src/Data/QRDataModeAbstract.php index 8b652a72e..dadfb52a2 100644 --- a/src/Data/QRDataModeAbstract.php +++ b/src/Data/QRDataModeAbstract.php @@ -18,11 +18,6 @@ use chillerlan\QRCode\Helpers\BitBuffer; */ abstract class QRDataModeAbstract implements QRDataModeInterface{ - /** - * the current data mode: Num, Alphanum, Kanji, Byte - */ - protected int $datamode; - /** * mode length bits for the version breakpoints 1-9, 10-26 and 27-40 * @@ -35,30 +30,17 @@ abstract class QRDataModeAbstract implements QRDataModeInterface{ */ protected string $data; - /** - * a BitBuffer instance - */ - protected BitBuffer $bitBuffer; - /** * QRDataModeAbstract constructor. - * - * @throws \chillerlan\QRCode\Data\QRCodeDataException */ - public function __construct(BitBuffer $bitBuffer, string $data){ - // do we need this here? we check during write anyways. -# if(!static::validateString($data)){ -# throw new QRCodeDataException('invalid data string'); -# } - - $this->bitBuffer = $bitBuffer; + public function __construct(string $data){ $this->data = $data; } /** * returns the character count of the $data string */ - protected function getLength():int{ + protected function getCharCount():int{ return strlen($this->data); } @@ -86,16 +68,4 @@ abstract class QRDataModeAbstract implements QRDataModeInterface{ throw new QRCodeDataException(sprintf('invalid version number: %d', $version)); } - /** - * - */ - protected function writeSegmentHeader(int $version):void{ - - $this->bitBuffer - ->put($this->datamode, 4) - ->put($this->getLength(), $this->getLengthBitsForVersion($version)) - ; - - } - } diff --git a/src/Data/QRDataModeInterface.php b/src/Data/QRDataModeInterface.php index d254b18cc..e24886ca4 100644 --- a/src/Data/QRDataModeInterface.php +++ b/src/Data/QRDataModeInterface.php @@ -12,6 +12,8 @@ namespace chillerlan\QRCode\Data; +use chillerlan\QRCode\Helpers\BitBuffer; + /** * Specifies the methods reqired for the data modules (Number, Alphanum, Byte and Kanji) * and holds version information in several constants @@ -38,6 +40,6 @@ interface QRDataModeInterface{ * * @see \chillerlan\QRCode\Data\QRData::writeBitBuffer() */ - public function write(int $version):void; + public function write(BitBuffer $bitBuffer, int $version):void; }