summaryrefslogtreecommitdiffstats
path: root/vendor/clue/utf8-react
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/clue/utf8-react')
-rw-r--r--vendor/clue/utf8-react/LICENSE21
-rw-r--r--vendor/clue/utf8-react/composer.json27
-rw-r--r--vendor/clue/utf8-react/src/Sequencer.php174
3 files changed, 222 insertions, 0 deletions
diff --git a/vendor/clue/utf8-react/LICENSE b/vendor/clue/utf8-react/LICENSE
new file mode 100644
index 0000000..7baae8e
--- /dev/null
+++ b/vendor/clue/utf8-react/LICENSE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2016 Christian Lück
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is furnished
+to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/vendor/clue/utf8-react/composer.json b/vendor/clue/utf8-react/composer.json
new file mode 100644
index 0000000..931708f
--- /dev/null
+++ b/vendor/clue/utf8-react/composer.json
@@ -0,0 +1,27 @@
+{
+ "name": "clue/utf8-react",
+ "description": "Streaming UTF-8 parser, built on top of ReactPHP.",
+ "keywords": ["UTF-8", "utf8", "unicode", "streaming", "ReactPHP"],
+ "homepage": "https://github.com/clue/reactphp-utf8",
+ "license": "MIT",
+ "authors": [
+ {
+ "name": "Christian Lück",
+ "email": "christian@clue.engineering"
+ }
+ ],
+ "autoload": {
+ "psr-4": { "Clue\\React\\Utf8\\": "src/" }
+ },
+ "autoload-dev": {
+ "psr-4": { "Clue\\Tests\\React\\Utf8\\": "tests/" }
+ },
+ "require": {
+ "php": ">=5.3",
+ "react/stream": "^1.0 || ^0.7 || ^0.6 || ^0.5 || ^0.4 || ^0.3"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^9.3 ||^5.7 || ^4.8",
+ "react/stream": "^1.0 || ^0.7"
+ }
+}
diff --git a/vendor/clue/utf8-react/src/Sequencer.php b/vendor/clue/utf8-react/src/Sequencer.php
new file mode 100644
index 0000000..e9bf433
--- /dev/null
+++ b/vendor/clue/utf8-react/src/Sequencer.php
@@ -0,0 +1,174 @@
+<?php
+
+namespace Clue\React\Utf8;
+
+use Evenement\EventEmitter;
+use React\Stream\ReadableStreamInterface;
+use React\Stream\WritableStreamInterface;
+use React\Stream\Util;
+
+/**
+ * forwards only complete UTF-8 sequences
+ */
+class Sequencer extends EventEmitter implements ReadableStreamInterface
+{
+ private $input;
+ private $invalid;
+
+ private $buffer = '';
+ private $closed = false;
+
+ public function __construct(ReadableStreamInterface $input, $replacementCharacter = '?')
+ {
+ $this->input = $input;
+ $this->invalid = $replacementCharacter;
+
+ if (!$input->isReadable()) {
+ return $this->close();
+ }
+
+ $this->input->on('data', array($this, 'handleData'));
+ $this->input->on('end', array($this, 'handleEnd'));
+ $this->input->on('error', array($this, 'handleError'));
+ $this->input->on('close', array($this, 'close'));
+ }
+
+ /** @internal */
+ public function handleData($data)
+ {
+ $this->buffer .= $data;
+ $len = strlen($this->buffer);
+
+ $sequence = '';
+ $expect = 0;
+ $out = '';
+
+ for ($i = 0; $i < $len; ++$i) {
+ $char = $this->buffer[$i];
+ $code = ord($char);
+
+ if ($code & 128) {
+ // multi-byte sequence
+ if ($code & 64) {
+ // this is the start of a sequence
+
+ // unexpected start of sequence because already within sequence
+ if ($expect !== 0) {
+ $out .= str_repeat($this->invalid, strlen($sequence));
+ $sequence = '';
+ }
+
+ $sequence = $char;
+ $expect = 2;
+
+ if ($code & 32) {
+ ++$expect;
+ if ($code & 16) {
+ ++$expect;
+
+ if ($code & 8) {
+ // invalid sequence start length
+ $out .= $this->invalid;
+ $sequence = '';
+ $expect = 0;
+ }
+ }
+ }
+ } else {
+ // this is a follow-up byte in a sequence
+ if ($expect === 0) {
+ // we're not within a sequence in first place
+ $out .= $this->invalid;
+ } else {
+ // valid following byte in sequence
+ $sequence .= $char;
+
+ // sequence reached expected length => add to output
+ if (strlen($sequence) === $expect) {
+ $out .= $sequence;
+ $sequence = '';
+ $expect = 0;
+ }
+ }
+ }
+ } else {
+ // simple ASCII character found
+
+ // unexpected because already within sequence
+ if ($expect !== 0) {
+ $out .= str_repeat($this->invalid, strlen($sequence));
+ $sequence = '';
+ $expect = 0;
+ }
+
+ $out .= $char;
+ }
+ }
+
+ if ($out !== '') {
+ $this->buffer = substr($this->buffer, strlen($out));
+
+ $this->emit('data', array($out));
+ }
+ }
+
+ /** @internal */
+ public function handleEnd()
+ {
+ if ($this->buffer !== '' && $this->invalid !== '') {
+ $data = str_repeat($this->invalid, strlen($this->buffer));
+ $this->buffer = '';
+
+ $this->emit('data', array($data));
+ }
+
+ if (!$this->closed) {
+ $this->emit('end');
+ $this->close();
+ }
+ }
+
+ /** @internal */
+ public function handleError(\Exception $error)
+ {
+ $this->emit('error', array($error));
+ $this->close();
+ }
+
+ public function isReadable()
+ {
+ return !$this->closed && $this->input->isReadable();
+ }
+
+ public function close()
+ {
+ if ($this->closed) {
+ return;
+ }
+
+ $this->closed = true;
+ $this->buffer = '';
+
+ $this->input->close();
+
+ $this->emit('close');
+ $this->removeAllListeners();
+ }
+
+ public function pause()
+ {
+ $this->input->pause();
+ }
+
+ public function resume()
+ {
+ $this->input->resume();
+ }
+
+ public function pipe(WritableStreamInterface $dest, array $options = array())
+ {
+ Util::pipe($this, $dest, $options);
+
+ return $dest;
+ }
+}