🛀 data mode rework

This commit is contained in:
codemasher
2020-11-21 18:40:53 +01:00
parent 9f5d0bc060
commit 8bd573ddfd
7 changed files with 64 additions and 74 deletions
+13 -8
View File
@@ -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);
}
}
+12 -8
View File
@@ -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++;
}
+13 -11
View File
@@ -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){
+19 -12
View File
@@ -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);
}
}
+2 -2
View File
@@ -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
+2 -32
View File
@@ -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))
;
}
}
+3 -1
View File
@@ -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;
}