J. King
6 years ago
9 changed files with 256 additions and 33 deletions
@ -0,0 +1,91 @@ |
|||
<?php |
|||
/** @license MIT |
|||
* Copyright 2018 J. King et al. |
|||
* See LICENSE and AUTHORS files for details */ |
|||
|
|||
declare(strict_types=1); |
|||
namespace MensBeam\Intl\Encoding; |
|||
|
|||
class XUserDefined implements Encoding { |
|||
use GenericEncoding; |
|||
|
|||
/** Retrieve the next character in the string, in UTF-8 encoding |
|||
* |
|||
* The returned character may be a replacement character, or the empty string if the end of the string has been reached |
|||
*/ |
|||
public function nextChar(): string { |
|||
// get the byte at the current position |
|||
$b = @$this->string[$this->posChar]; |
|||
if ($b === "") { |
|||
return ""; |
|||
} |
|||
$this->posChar++; |
|||
$p = ord($b); |
|||
if ($p < 0x80) { |
|||
// if the byte is an ASCII character or end of input, simply return it |
|||
return $b; |
|||
} else { |
|||
return UTF8::encode(0xF700 + $p); |
|||
} |
|||
} |
|||
|
|||
/** Decodes the next character from the string and returns its code point number |
|||
* |
|||
* If the end of the string has been reached, false is returned |
|||
* |
|||
* @return int|bool |
|||
*/ |
|||
public function nextCode() { |
|||
// get the byte at the current position |
|||
$b = @$this->string[$this->posChar]; |
|||
if ($b === "") { |
|||
return false; |
|||
} |
|||
$this->posChar++; |
|||
$p = ord($b); |
|||
if ($p < 0x80) { |
|||
// if the byte is an ASCII character or end of input, simply return it |
|||
return $p; |
|||
} else { |
|||
return 0xF700 + $p; |
|||
} |
|||
} |
|||
|
|||
/** Advance $distance characters through the string |
|||
* |
|||
* If $distance is negative, the operation will be performed in reverse |
|||
* |
|||
* If the end (or beginning) of the string was reached before the end of the operation, the remaining number of requested characters is returned |
|||
*/ |
|||
public function seek(int $distance): int { |
|||
if ($distance > 0) { |
|||
while ($this->posChar < $this->lenByte && $distance > 0) { |
|||
$this->nextCode(); |
|||
$distance--; |
|||
} |
|||
return $distance; |
|||
} elseif ($distance < 0) { |
|||
$distance = abs($distance); |
|||
while ($this->posChar > 0 && $distance > 0) { |
|||
$this->posChar--; |
|||
$distance--; |
|||
} |
|||
return $distance; |
|||
} else { |
|||
return 0; |
|||
} |
|||
} |
|||
|
|||
/** Returns the current byte position of the decoder */ |
|||
public function posByte(): int { |
|||
return $this->posChar; |
|||
} |
|||
|
|||
/** Calculates the length of the string in code points |
|||
* |
|||
* Note that this may involve processing to the end of the string |
|||
*/ |
|||
public function len(): int { |
|||
return $this->lenByte; |
|||
} |
|||
} |
@ -0,0 +1,132 @@ |
|||
<?php |
|||
/** @license MIT |
|||
* Copyright 2018 J. King et al. |
|||
* See LICENSE and AUTHORS files for details */ |
|||
|
|||
declare(strict_types=1); |
|||
namespace MensBeam\Intl\TestCase\Encoding; |
|||
|
|||
use MensBeam\Intl\Encoding\XUserDefined; |
|||
|
|||
class TestXUserDefined extends \MensBeam\Intl\Test\DecoderTest { |
|||
protected $testedClass = XUserDefined::class; |
|||
/* X-user-defined doesn't have complex seeking, so this string is generic */ |
|||
protected $seekString = "30 31 32 33 34 35 36"; |
|||
protected $seekCodes = [0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36]; |
|||
protected $seekOffsets = [0, 1, 2, 3, 4, 5, 6, 7]; |
|||
/* This string is supposed to contain an invalid character sequence sandwiched between two null characters, but x-user-defined has no invalid characters */ |
|||
protected $brokenChar = ""; |
|||
protected $lowerA = "a"; |
|||
|
|||
/** |
|||
* @dataProvider provideStrings |
|||
* @covers MensBeam\Intl\Encoding\XUserDefined::__construct |
|||
* @covers MensBeam\Intl\Encoding\XUserDefined::nextCode |
|||
*/ |
|||
public function testDecodeMultipleCharactersAsCodePoints(string $input, array $exp) { |
|||
return parent::testDecodeMultipleCharactersAsCodePoints($input, $exp); |
|||
} |
|||
|
|||
/** |
|||
* @dataProvider provideStrings |
|||
* @covers MensBeam\Intl\Encoding\XUserDefined::__construct |
|||
* @covers MensBeam\Intl\Encoding\XUserDefined::nextChar |
|||
*/ |
|||
public function testDecodeMultipleCharactersAsStrings(string $input, array $exp) { |
|||
return parent::testDecodeMultipleCharactersAsStrings($input, $exp); |
|||
} |
|||
|
|||
/** |
|||
* @dataProvider provideStrings |
|||
* @coversNothing |
|||
*/ |
|||
public function testSTepBackThroughAString(string $input, array $exp) { |
|||
// this test has no meaning for x-user-defined |
|||
return parent::testSTepBackThroughAString($input, $exp); |
|||
} |
|||
|
|||
/** |
|||
* @covers MensBeam\Intl\Encoding\XUserDefined::seek |
|||
* @covers MensBeam\Intl\Encoding\XUserDefined::posChar |
|||
* @covers MensBeam\Intl\Encoding\XUserDefined::posByte |
|||
* @covers MensBeam\Intl\Encoding\XUserDefined::rewind |
|||
*/ |
|||
public function testSeekThroughAString() { |
|||
return parent::testSeekThroughAString(); |
|||
} |
|||
|
|||
/** |
|||
* @covers MensBeam\Intl\Encoding\XUserDefined::posChar |
|||
* @covers MensBeam\Intl\Encoding\XUserDefined::posByte |
|||
*/ |
|||
public function testTraversePastTheEndOfAString() { |
|||
return parent::testTraversePastTheEndOfAString(); |
|||
} |
|||
|
|||
/** |
|||
* @covers MensBeam\Intl\Encoding\XUserDefined::peekChar |
|||
* @covers MensBeam\Intl\Encoding\XUserDefined::stateSave |
|||
* @covers MensBeam\Intl\Encoding\XUserDefined::stateApply |
|||
*/ |
|||
public function testPeekAtCharacters() { |
|||
return parent::testPeekAtCharacters(); |
|||
} |
|||
|
|||
/** |
|||
* @covers MensBeam\Intl\Encoding\XUserDefined::peekCode |
|||
* @covers MensBeam\Intl\Encoding\XUserDefined::stateSave |
|||
* @covers MensBeam\Intl\Encoding\XUserDefined::stateApply |
|||
*/ |
|||
public function testPeekAtCodePoints() { |
|||
return parent::testPeekAtCodePoints(); |
|||
} |
|||
|
|||
/** |
|||
* @dataProvider provideStrings |
|||
* @covers MensBeam\Intl\Encoding\XUserDefined::len |
|||
* @covers MensBeam\Intl\Encoding\XUserDefined::stateSave |
|||
* @covers MensBeam\Intl\Encoding\XUserDefined::stateApply |
|||
*/ |
|||
public function testGetStringLength(string $input, array $points) { |
|||
return parent::testGetStringLength($input, $points); |
|||
} |
|||
|
|||
/** |
|||
* @coversNothing |
|||
*/ |
|||
public function testReplacementModes() { |
|||
$this->assertTrue(true); |
|||
} |
|||
|
|||
/** |
|||
* @dataProvider provideStrings |
|||
* @covers MensBeam\Intl\Encoding\XUserDefined::rewind |
|||
* @covers MensBeam\Intl\Encoding\XUserDefined::chars |
|||
* @covers MensBeam\Intl\Encoding\XUserDefined::codes |
|||
*/ |
|||
public function testIterateThroughAString(string $input, array $exp) { |
|||
return parent::testIterateThroughAString($input, $exp); |
|||
} |
|||
|
|||
public function provideStrings() { |
|||
$a_bytes = []; |
|||
$a_codes = []; |
|||
for ($a = 0; $a < 0x80; $a++) { |
|||
$a_bytes[] = strtoupper(bin2hex(chr($a))); |
|||
$a_codes[] = $a; |
|||
} |
|||
$p_bytes = []; |
|||
$p_codes = []; |
|||
for ($a = 0; $a < 0x80; $a++) { |
|||
$p_bytes[] = strtoupper(bin2hex(chr(0x80 + $a))); |
|||
$p_codes[] = 0xF780 + $a; |
|||
} |
|||
$a_bytes = implode(" ", $a_bytes); |
|||
$p_bytes = implode(" ", $p_bytes); |
|||
return [ |
|||
'empty string' => ["", []], |
|||
'ASCI bytes' => [$a_bytes, $a_codes], |
|||
'private-use bytes' => [$p_bytes, $p_codes], |
|||
]; |
|||
} |
|||
} |
@ -0,0 +1,23 @@ |
|||
<?php |
|||
/** @license MIT |
|||
* Copyright 2018 J. King et al. |
|||
* See LICENSE and AUTHORS files for details */ |
|||
|
|||
declare(strict_types=1); |
|||
namespace MensBeam\Intl\Test; |
|||
|
|||
use MensBeam\Intl\Encoding\EncoderException; |
|||
|
|||
abstract class CoderDecoderTest extends DecoderTest { |
|||
public function testEncodeCodePoints(bool $fatal, $input, $exp) { |
|||
$class = $this->testedClass; |
|||
if ($exp instanceof \Throwable) { |
|||
$this->expectException(get_class($exp)); |
|||
$this->expectExceptionCode($exp->getCode()); |
|||
} else { |
|||
$exp = strtolower(str_replace(" ", "", $exp)); |
|||
} |
|||
$out = $class::encode($input, $fatal); |
|||
$this->assertSame($exp, bin2hex($out)); |
|||
} |
|||
} |
Loading…
Reference in new issue