diff --git a/.gitignore b/.gitignore index febba79..1f9a826 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ +.phpunit.result.cache +/.vscode /.idea /vendor /build diff --git a/CHANGELOG.md b/CHANGELOG.md deleted file mode 100644 index 828ee34..0000000 --- a/CHANGELOG.md +++ /dev/null @@ -1,22 +0,0 @@ -# Changelog - -All Notable changes to `Buffer` will be documented in this file. - -Updates should follow the [Keep a CHANGELOG](http://keepachangelog.com/) principles. - -## NEXT - YYYY-MM-DD - -### Added -- Nothing - -### Deprecated -- Nothing - -### Fixed -- Nothing - -### Removed -- Nothing - -### Security -- Nothing diff --git a/README.md b/README.md index bfb962c..a3f4b9b 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ $ composer require phpinnacle/buffer ```php =5.3.3" + "php": "^7.1" + }, + "require-dev": { + "phpunit/phpunit": "^7.1" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.4.x-dev" + "dev-master": "2.0.x-dev" } }, "autoload": { @@ -524,7 +527,7 @@ "authors": [ { "name": "Sebastian Bergmann", - "email": "sb@sebastian-bergmann.de", + "email": "sebastian@phpunit.de", "role": "lead" } ], @@ -534,7 +537,7 @@ "filesystem", "iterator" ], - "time": "2017-11-27T13:52:08+00:00" + "time": "2018-09-13T20:33:42+00:00" }, { "name": "phpunit/php-text-template", @@ -579,28 +582,28 @@ }, { "name": "phpunit/php-timer", - "version": "1.0.9", + "version": "2.1.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-timer.git", - "reference": "3dcf38ca72b158baf0bc245e9184d3fdffa9c46f" + "reference": "8b389aebe1b8b0578430bda0c7c95a829608e059" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/3dcf38ca72b158baf0bc245e9184d3fdffa9c46f", - "reference": "3dcf38ca72b158baf0bc245e9184d3fdffa9c46f", + "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/8b389aebe1b8b0578430bda0c7c95a829608e059", + "reference": "8b389aebe1b8b0578430bda0c7c95a829608e059", "shasum": "" }, "require": { - "php": "^5.3.3 || ^7.0" + "php": "^7.1" }, "require-dev": { - "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.0" + "phpunit/phpunit": "^7.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0-dev" + "dev-master": "2.1-dev" } }, "autoload": { @@ -615,7 +618,7 @@ "authors": [ { "name": "Sebastian Bergmann", - "email": "sb@sebastian-bergmann.de", + "email": "sebastian@phpunit.de", "role": "lead" } ], @@ -624,33 +627,33 @@ "keywords": [ "timer" ], - "time": "2017-02-26T11:10:40+00:00" + "time": "2019-02-20T10:12:59+00:00" }, { "name": "phpunit/php-token-stream", - "version": "2.0.2", + "version": "3.0.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-token-stream.git", - "reference": "791198a2c6254db10131eecfe8c06670700904db" + "reference": "c99e3be9d3e85f60646f152f9002d46ed7770d18" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/791198a2c6254db10131eecfe8c06670700904db", - "reference": "791198a2c6254db10131eecfe8c06670700904db", + "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/c99e3be9d3e85f60646f152f9002d46ed7770d18", + "reference": "c99e3be9d3e85f60646f152f9002d46ed7770d18", "shasum": "" }, "require": { "ext-tokenizer": "*", - "php": "^7.0" + "php": "^7.1" }, "require-dev": { - "phpunit/phpunit": "^6.2.4" + "phpunit/phpunit": "^7.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.0-dev" + "dev-master": "3.0-dev" } }, "autoload": { @@ -673,57 +676,55 @@ "keywords": [ "tokenizer" ], - "time": "2017-11-27T05:48:46+00:00" + "time": "2018-10-30T05:52:18+00:00" }, { "name": "phpunit/phpunit", - "version": "6.5.13", + "version": "8.0.4", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "0973426fb012359b2f18d3bd1e90ef1172839693" + "reference": "a7af0201285445c9c73c4bdf869c486e36b41604" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/0973426fb012359b2f18d3bd1e90ef1172839693", - "reference": "0973426fb012359b2f18d3bd1e90ef1172839693", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/a7af0201285445c9c73c4bdf869c486e36b41604", + "reference": "a7af0201285445c9c73c4bdf869c486e36b41604", "shasum": "" }, "require": { + "doctrine/instantiator": "^1.1", "ext-dom": "*", "ext-json": "*", "ext-libxml": "*", "ext-mbstring": "*", "ext-xml": "*", - "myclabs/deep-copy": "^1.6.1", - "phar-io/manifest": "^1.0.1", - "phar-io/version": "^1.0", - "php": "^7.0", + "ext-xmlwriter": "*", + "myclabs/deep-copy": "^1.7", + "phar-io/manifest": "^1.0.2", + "phar-io/version": "^2.0", + "php": "^7.2", "phpspec/prophecy": "^1.7", - "phpunit/php-code-coverage": "^5.3", - "phpunit/php-file-iterator": "^1.4.3", + "phpunit/php-code-coverage": "^7.0", + "phpunit/php-file-iterator": "^2.0.1", "phpunit/php-text-template": "^1.2.1", - "phpunit/php-timer": "^1.0.9", - "phpunit/phpunit-mock-objects": "^5.0.9", - "sebastian/comparator": "^2.1", - "sebastian/diff": "^2.0", - "sebastian/environment": "^3.1", + "phpunit/php-timer": "^2.0", + "sebastian/comparator": "^3.0", + "sebastian/diff": "^3.0", + "sebastian/environment": "^4.1", "sebastian/exporter": "^3.1", - "sebastian/global-state": "^2.0", + "sebastian/global-state": "^3.0", "sebastian/object-enumerator": "^3.0.3", - "sebastian/resource-operations": "^1.0", + "sebastian/resource-operations": "^2.0", "sebastian/version": "^2.0.1" }, - "conflict": { - "phpdocumentor/reflection-docblock": "3.0.2", - "phpunit/dbunit": "<3.0" - }, "require-dev": { "ext-pdo": "*" }, "suggest": { + "ext-soap": "*", "ext-xdebug": "*", - "phpunit/php-invoker": "^1.1" + "phpunit/php-invoker": "^2.0" }, "bin": [ "phpunit" @@ -731,7 +732,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "6.5.x-dev" + "dev-master": "8.0-dev" } }, "autoload": { @@ -757,66 +758,7 @@ "testing", "xunit" ], - "time": "2018-09-08T15:10:43+00:00" - }, - { - "name": "phpunit/phpunit-mock-objects", - "version": "5.0.10", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/phpunit-mock-objects.git", - "reference": "cd1cf05c553ecfec36b170070573e540b67d3f1f" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/cd1cf05c553ecfec36b170070573e540b67d3f1f", - "reference": "cd1cf05c553ecfec36b170070573e540b67d3f1f", - "shasum": "" - }, - "require": { - "doctrine/instantiator": "^1.0.5", - "php": "^7.0", - "phpunit/php-text-template": "^1.2.1", - "sebastian/exporter": "^3.1" - }, - "conflict": { - "phpunit/phpunit": "<6.0" - }, - "require-dev": { - "phpunit/phpunit": "^6.5.11" - }, - "suggest": { - "ext-soap": "*" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "5.0.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Mock Object library for PHPUnit", - "homepage": "https://github.com/sebastianbergmann/phpunit-mock-objects/", - "keywords": [ - "mock", - "xunit" - ], - "time": "2018-08-09T05:50:03+00:00" + "time": "2019-02-18T09:23:05+00:00" }, { "name": "sebastian/code-unit-reverse-lookup", @@ -865,30 +807,30 @@ }, { "name": "sebastian/comparator", - "version": "2.1.3", + "version": "3.0.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "34369daee48eafb2651bea869b4b15d75ccc35f9" + "reference": "5de4fc177adf9bce8df98d8d141a7559d7ccf6da" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/34369daee48eafb2651bea869b4b15d75ccc35f9", - "reference": "34369daee48eafb2651bea869b4b15d75ccc35f9", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/5de4fc177adf9bce8df98d8d141a7559d7ccf6da", + "reference": "5de4fc177adf9bce8df98d8d141a7559d7ccf6da", "shasum": "" }, "require": { - "php": "^7.0", - "sebastian/diff": "^2.0 || ^3.0", + "php": "^7.1", + "sebastian/diff": "^3.0", "sebastian/exporter": "^3.1" }, "require-dev": { - "phpunit/phpunit": "^6.4" + "phpunit/phpunit": "^7.1" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.1.x-dev" + "dev-master": "3.0-dev" } }, "autoload": { @@ -925,32 +867,33 @@ "compare", "equality" ], - "time": "2018-02-01T13:46:46+00:00" + "time": "2018-07-12T15:12:46+00:00" }, { "name": "sebastian/diff", - "version": "2.0.1", + "version": "3.0.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/diff.git", - "reference": "347c1d8b49c5c3ee30c7040ea6fc446790e6bddd" + "reference": "720fcc7e9b5cf384ea68d9d930d480907a0c1a29" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/347c1d8b49c5c3ee30c7040ea6fc446790e6bddd", - "reference": "347c1d8b49c5c3ee30c7040ea6fc446790e6bddd", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/720fcc7e9b5cf384ea68d9d930d480907a0c1a29", + "reference": "720fcc7e9b5cf384ea68d9d930d480907a0c1a29", "shasum": "" }, "require": { - "php": "^7.0" + "php": "^7.1" }, "require-dev": { - "phpunit/phpunit": "^6.2" + "phpunit/phpunit": "^7.5 || ^8.0", + "symfony/process": "^2 || ^3.3 || ^4" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.0-dev" + "dev-master": "3.0-dev" } }, "autoload": { @@ -975,34 +918,40 @@ "description": "Diff implementation", "homepage": "https://github.com/sebastianbergmann/diff", "keywords": [ - "diff" + "diff", + "udiff", + "unidiff", + "unified diff" ], - "time": "2017-08-03T08:09:46+00:00" + "time": "2019-02-04T06:01:07+00:00" }, { "name": "sebastian/environment", - "version": "3.1.0", + "version": "4.1.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/environment.git", - "reference": "cd0871b3975fb7fc44d11314fd1ee20925fce4f5" + "reference": "6fda8ce1974b62b14935adc02a9ed38252eca656" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/cd0871b3975fb7fc44d11314fd1ee20925fce4f5", - "reference": "cd0871b3975fb7fc44d11314fd1ee20925fce4f5", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/6fda8ce1974b62b14935adc02a9ed38252eca656", + "reference": "6fda8ce1974b62b14935adc02a9ed38252eca656", "shasum": "" }, "require": { - "php": "^7.0" + "php": "^7.1" }, "require-dev": { - "phpunit/phpunit": "^6.1" + "phpunit/phpunit": "^7.5" + }, + "suggest": { + "ext-posix": "*" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.1.x-dev" + "dev-master": "4.1-dev" } }, "autoload": { @@ -1027,7 +976,7 @@ "environment", "hhvm" ], - "time": "2017-07-01T08:51:00+00:00" + "time": "2019-02-01T05:27:49+00:00" }, { "name": "sebastian/exporter", @@ -1098,23 +1047,26 @@ }, { "name": "sebastian/global-state", - "version": "2.0.0", + "version": "3.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/global-state.git", - "reference": "e8ba02eed7bbbb9e59e43dedd3dddeff4a56b0c4" + "reference": "edf8a461cf1d4005f19fb0b6b8b95a9f7fa0adc4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/e8ba02eed7bbbb9e59e43dedd3dddeff4a56b0c4", - "reference": "e8ba02eed7bbbb9e59e43dedd3dddeff4a56b0c4", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/edf8a461cf1d4005f19fb0b6b8b95a9f7fa0adc4", + "reference": "edf8a461cf1d4005f19fb0b6b8b95a9f7fa0adc4", "shasum": "" }, "require": { - "php": "^7.0" + "php": "^7.2", + "sebastian/object-reflector": "^1.1.1", + "sebastian/recursion-context": "^3.0" }, "require-dev": { - "phpunit/phpunit": "^6.0" + "ext-dom": "*", + "phpunit/phpunit": "^8.0" }, "suggest": { "ext-uopz": "*" @@ -1122,7 +1074,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "2.0-dev" + "dev-master": "3.0-dev" } }, "autoload": { @@ -1145,7 +1097,7 @@ "keywords": [ "global state" ], - "time": "2017-04-27T15:39:26+00:00" + "time": "2019-02-01T05:30:01+00:00" }, { "name": "sebastian/object-enumerator", @@ -1294,25 +1246,25 @@ }, { "name": "sebastian/resource-operations", - "version": "1.0.0", + "version": "2.0.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/resource-operations.git", - "reference": "ce990bb21759f94aeafd30209e8cfcdfa8bc3f52" + "reference": "4d7a795d35b889bf80a0cc04e08d77cedfa917a9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/ce990bb21759f94aeafd30209e8cfcdfa8bc3f52", - "reference": "ce990bb21759f94aeafd30209e8cfcdfa8bc3f52", + "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/4d7a795d35b889bf80a0cc04e08d77cedfa917a9", + "reference": "4d7a795d35b889bf80a0cc04e08d77cedfa917a9", "shasum": "" }, "require": { - "php": ">=5.6.0" + "php": "^7.1" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0.x-dev" + "dev-master": "2.0-dev" } }, "autoload": { @@ -1332,7 +1284,7 @@ ], "description": "Provides a list of PHP built-in functions that operate on resources", "homepage": "https://www.github.com/sebastianbergmann/resource-operations", - "time": "2015-07-28T20:34:47+00:00" + "time": "2018-10-04T04:07:39+00:00" }, { "name": "sebastian/version", diff --git a/ext/.gitignore b/ext/.gitignore new file mode 100644 index 0000000..896efc4 --- /dev/null +++ b/ext/.gitignore @@ -0,0 +1,28 @@ +Makefile* +*.la +*.lo +acinclude.m4 +aclocal.m4 +config.guess +config.h* +config.sub +config.log +config.nice +config.status +configure* +install-sh +libtool +ltmain.sh +missing +mkinstalldirs +.deps +.libs/ +autom4te.cache/ +modules/ +build/ +run-tests.php +.phpenv-version +extras/ +php.sh +ltmain.sh.backup +tmp-php.ini diff --git a/ext/Buffer.cpp b/ext/Buffer.cpp new file mode 100644 index 0000000..96d23b4 --- /dev/null +++ b/ext/Buffer.cpp @@ -0,0 +1,253 @@ +/** + * This file is part of PHPinnacle/Buffer. + * + * (c) PHPinnacle Team + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +#include "Buffer.hpp" + +/************************* COMMON *************************/ +Buffer::Buffer() noexcept { +} + +Buffer::Buffer(const std::vector &_buffer) noexcept: + buffer(_buffer) { +} + +void Buffer::clear() noexcept { + buffer.clear(); +} + +void Buffer::discard(unsigned long long n) { + if (buffer.size() < n) + throw BufferOverflow(); + + buffer.erase(buffer.begin(), buffer.begin() + n); +} + +unsigned long long Buffer::size() const noexcept { + return buffer.size(); +} + +bool Buffer::empty() const noexcept { + return buffer.size() == 0; +} + +void Buffer::merge(const Buffer &other) noexcept { + buffer.insert(buffer.end(), other.buffer.begin(), other.buffer.end()); +} + +std::string Buffer::bytes() const noexcept { + std::stringstream stream; + + unsigned long long size = buffer.size(); + for (unsigned long long i = 0; i < size; ++i) + stream << buffer[i]; + + return stream.str(); +} + +std::string Buffer::flush() noexcept { + std::string out = bytes(); + + clear(); + + return out; +} + +/************************* WRITING *************************/ +template inline void Buffer::append(const T &val) { + unsigned int size = sizeof(T); + unsigned const char *array = reinterpret_cast(&val); + + for (unsigned int i = 0; i < size; ++i) + buffer.push_back(array[size - i - 1]); +} + +void Buffer::appendBoolean(bool val) noexcept { + append(val); +} + +void Buffer::appendString(const std::string &str) noexcept { + for (const unsigned char &s : str) appendInt8(s); +} + +void Buffer::appendInt8(char val) noexcept { + append(val); +} +void Buffer::appendUInt8(unsigned char val) noexcept { + append(val); +} + +void Buffer::appendInt16(short val) noexcept { + append(val); +} +void Buffer::appendUInt16(unsigned short val) noexcept { + append(val); +} + +void Buffer::appendInt32(int val) noexcept { + append(val); +} +void Buffer::appendUInt32(unsigned int val) noexcept { + append(val); +} + +void Buffer::appendInt64(long long val) noexcept { + append(val); +} +void Buffer::appendUInt64(unsigned long long val) noexcept { + append(val); +} + +void Buffer::appendFloat(float val) noexcept { + union { float fnum; unsigned long inum; } u; + u.fnum = val; + appendUInt32(u.inum); +} +void Buffer::appendDouble(double val) noexcept { + union { double fnum; unsigned long long inum; } u; + u.fnum = val; + appendUInt64(u.inum); +} + +/************************* READING *************************/ +template inline T Buffer::read(unsigned long long offset) { + T result = 0; + unsigned int size = sizeof(T); + + if (offset + size > buffer.size()) + throw BufferOverflow(); + + char *dst = (char*) &result; + char *src = (char*) &buffer[offset]; + + for (unsigned int i = 0; i < size; ++i) + dst[i] = src[size - i - 1]; + + return result; +} + +bool Buffer::readBoolean(unsigned long long offset) { + return read(offset); +} + +std::string Buffer::readString(unsigned long long size, unsigned long long offset) { + if (offset + size > buffer.size()) + throw BufferOverflow(); + + std::string result(buffer.begin() + offset, buffer.begin() + offset + size); + + return result; +} + +char Buffer::readInt8(unsigned long long offset) { + return read(offset); +} +unsigned char Buffer::readUInt8(unsigned long long offset) { + return read(offset); +} + +short Buffer::readInt16(unsigned long long offset) { + return read(offset); +} +unsigned short Buffer::readUInt16(unsigned long long offset) { + return read(offset); +} + +int Buffer::readInt32(unsigned long long offset) { + return read(offset); +} +unsigned int Buffer::readUInt32(unsigned long long offset) { + return read(offset); +} + +long long Buffer::readInt64(unsigned long long offset) { + return read(offset); +} +unsigned long long Buffer::readUInt64(unsigned long long offset) { + return read(offset); +} + +float Buffer::readFloat(unsigned long long offset) { + return read(offset); +} +double Buffer::readDouble(unsigned long long offset) { + return read(offset); +} + +/************************* CONSUMING *************************/ +template inline T Buffer::consume() { + T result = 0; + unsigned int size = sizeof(T); + + if (size > buffer.size()) + throw BufferOverflow(); + + char *dst = (char*) &result; + char *src = (char*) &buffer[0]; + + for (unsigned int i = 0; i < size; ++i) + dst[i] = src[size - i - 1]; + + buffer.erase(buffer.begin(), buffer.begin() + size); + + return result; +} + +bool Buffer::consumeBool() { + return consume(); +} + +std::string Buffer::consumeString(unsigned long long size) { + if (size > buffer.size()) + throw BufferOverflow(); + + std::string result(buffer.begin(), buffer.begin() + size); + + buffer.erase(buffer.begin(), buffer.begin() + size); + + return result; +} + +char Buffer::consumeInt8() { + return consume(); +} +unsigned char Buffer::consumeUInt8() { + return consume(); +} + +short Buffer::consumeInt16() { + return consume(); +} +unsigned short Buffer::consumeUInt16() { + return consume(); +} + +int Buffer::consumeInt32() { + return consume(); +} +unsigned int Buffer::consumeUInt32() { + return consume(); +} + +long long Buffer::consumeInt64() { + return consume(); +} +unsigned long long Buffer::consumeUInt64() { + return consume(); +} + +float Buffer::consumeFloat() { + return consume(); +} +double Buffer::consumeDouble() { + return consume(); +} + +Buffer::~Buffer() { + clear(); +} diff --git a/ext/Buffer.hpp b/ext/Buffer.hpp new file mode 100644 index 0000000..99ce821 --- /dev/null +++ b/ext/Buffer.hpp @@ -0,0 +1,105 @@ +/** + * This file is part of PHPinnacle/Buffer. + * + * (c) PHPinnacle Team + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +#pragma once +#include +#include +#include + +struct BufferOverflow : public std::exception {}; + +class Buffer { +public: + Buffer() noexcept; + Buffer(const std::vector&) noexcept; + + void clear() noexcept; + void discard(unsigned long long n); + + unsigned long long size() const noexcept; + bool empty() const noexcept; + + void merge(const Buffer&) noexcept; + + std::string flush() noexcept; + std::string bytes() const noexcept; + + /************************** Writing ***************************/ + + template inline void append(const T &val); + + void appendString(const std::string&) noexcept; + + void appendBoolean(bool) noexcept; + + void appendInt8(char) noexcept; + void appendUInt8(unsigned char) noexcept; + + void appendInt16(short) noexcept; + void appendUInt16(unsigned short) noexcept; + + void appendInt32(int) noexcept; + void appendUInt32(unsigned int) noexcept; + + void appendInt64(long long) noexcept; + void appendUInt64(unsigned long long) noexcept; + + void appendFloat(float) noexcept; + void appendDouble(double) noexcept; + + /************************** Reading ***************************/ + + template inline T read(unsigned long long offset); + + std::string readString(unsigned long long size, unsigned long long offset); + + bool readBoolean(unsigned long long offset); + + char readInt8(unsigned long long offset); + unsigned char readUInt8(unsigned long long offset); + + short readInt16(unsigned long long offset); + unsigned short readUInt16(unsigned long long offset); + + int readInt32(unsigned long long offset); + unsigned int readUInt32(unsigned long long offset); + + long long readInt64(unsigned long long offset); + unsigned long long readUInt64(unsigned long long offset); + + float readFloat(unsigned long long offset); + double readDouble(unsigned long long offset); + + /************************** Consuming ***************************/ + + template inline T consume(); + + std::string consumeString(unsigned long long size); + + bool consumeBool(); + + char consumeInt8(); + unsigned char consumeUInt8(); + + short consumeInt16(); + unsigned short consumeUInt16(); + + int consumeInt32(); + unsigned int consumeUInt32(); + + long long consumeInt64(); + unsigned long long consumeUInt64(); + + float consumeFloat(); + double consumeDouble(); + + ~Buffer(); +private: + std::vector buffer; +}; diff --git a/ext/buffer.cc b/ext/buffer.cc new file mode 100644 index 0000000..af7167a --- /dev/null +++ b/ext/buffer.cc @@ -0,0 +1,823 @@ +/** + * This file is part of PHPinnacle/Buffer. + * + * (c) PHPinnacle Team + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +#include "php_buffer.h" +#include "Buffer.hpp" + +#define GUARD_SIZE(b, s) { if (guard_data_size(b, s) == false) return; } + +typedef struct _buffer_object { + Buffer *data; + zend_object std; +} buffer_object; + +static zend_class_entry *buffer_object_ce = NULL; +static zend_class_entry *buffer_exception_ce = NULL; +static zend_object_handlers buffer_object_handlers; + +static void copy_zend_string_to_buffer(zend_string *str, Buffer *data) +{ + char *input = ZSTR_VAL(str); + zend_long input_len = ZSTR_LEN(str); + + for (int i = 0; i < input_len; i++) + data->appendInt8(input[i]); +} + +static zend_object* buffer_object_to_zend_object(buffer_object *objval) +{ + return ((zend_object*)(objval + 1)) - 1; +} + +static buffer_object* buffer_object_from_zend_object(zend_object *objval) +{ + return ((buffer_object*)(objval + 1)) - 1; +} + +static bool guard_data_size(buffer_object * buffer, unsigned int size) +{ + if (size > buffer->data->size()) { + zend_throw_exception(buffer_exception_ce, "Buffer overflow.", 0); + + return false; + } + + return true; +} + +PHP_METHOD(ByteBuffer, __construct) +{ + buffer_object *objval = buffer_object_from_zend_object(Z_OBJ_P(getThis())); + + objval->data = new Buffer(); + + if (ZEND_NUM_ARGS() == 0) { + return; + } + + zval *val; + + if (zend_parse_parameters_throw(ZEND_NUM_ARGS(), "z", &val) == FAILURE) { + return; + } + + if (Z_TYPE_P(val) == IS_STRING) { + copy_zend_string_to_buffer(Z_STR_P(val), objval->data); + } else if (Z_TYPE_P(val) == IS_OBJECT && instanceof_function(Z_OBJCE_P(val), buffer_object_ce) != 0) { + buffer_object *appval = buffer_object_from_zend_object(Z_OBJ_P(val)); + + objval->data->merge(*appval->data); + } else { + zend_type_error("Invalid ByteBuffer constructor arguments."); + + return; + } +} + +PHP_METHOD(ByteBuffer, __toString) +{ + buffer_object *objval = buffer_object_from_zend_object(Z_OBJ_P(getThis())); + + std::string s = objval->data->bytes(); + + RETURN_NEW_STR(zend_string_init(s.data(), s.size(), 0)); +} + +PHP_METHOD(ByteBuffer, bytes) +{ + buffer_object *objval = buffer_object_from_zend_object(Z_OBJ_P(getThis())); + + std::string s = objval->data->bytes(); + + RETURN_NEW_STR(zend_string_init(s.data(), s.size(), 0)); +} + +PHP_METHOD(ByteBuffer, size) +{ + buffer_object *objval = buffer_object_from_zend_object(Z_OBJ_P(getThis())); + + RETURN_LONG(objval->data->size()); +} + +PHP_METHOD(ByteBuffer, empty) +{ + buffer_object *objval = buffer_object_from_zend_object(Z_OBJ_P(getThis())); + + RETURN_BOOL(objval->data->empty()); +} + +PHP_METHOD(ByteBuffer, append) +{ + if (ZEND_NUM_ARGS() == 0) { + return; + } + + buffer_object *objval = buffer_object_from_zend_object(Z_OBJ_P(getThis())); + + zval *val; + + if (zend_parse_parameters_throw(ZEND_NUM_ARGS(), "z", &val) == FAILURE) { + return; + } + + if (Z_TYPE_P(val) == IS_STRING) { + copy_zend_string_to_buffer(Z_STR_P(val), objval->data); + } else if (Z_TYPE_P(val) == IS_OBJECT && instanceof_function(Z_OBJCE_P(val), buffer_object_ce) != 0) { + buffer_object *appval = buffer_object_from_zend_object(Z_OBJ_P(val)); + + objval->data->merge(*appval->data); + } else { + zend_type_error("Invalid argument for append."); + return; + } + + RETURN_ZVAL(getThis(), 1, 0); +} + +PHP_METHOD(ByteBuffer, read) +{ + buffer_object *objval = buffer_object_from_zend_object(Z_OBJ_P(getThis())); + + zend_long size = 0; + zend_long offset = 0; + + if (zend_parse_parameters_throw(ZEND_NUM_ARGS(), "l|l", &size, &offset) == FAILURE) { + return; + } + + GUARD_SIZE(objval, offset + size); + + std::string s = objval->data->readString(size, offset); + + RETURN_NEW_STR(zend_string_init(s.data(), s.size(), 0)); +} + +PHP_METHOD(ByteBuffer, consume) +{ + buffer_object *objval = buffer_object_from_zend_object(Z_OBJ_P(getThis())); + + zend_long size = 0; + + if (zend_parse_parameters_throw(ZEND_NUM_ARGS(), "l", &size) == FAILURE) { + return; + } + + GUARD_SIZE(objval, size); + + std::string s = objval->data->consumeString(size); + + RETURN_NEW_STR(zend_string_init(s.data(), s.size(), 0)); +} + +PHP_METHOD(ByteBuffer, discard) +{ + buffer_object *objval = buffer_object_from_zend_object(Z_OBJ_P(getThis())); + + zend_long size = 0; + + if (zend_parse_parameters_throw(ZEND_NUM_ARGS(), "l", &size) == FAILURE) { + return; + } + + GUARD_SIZE(objval, size); + + objval->data->discard(size); +} + +PHP_METHOD(ByteBuffer, slice) +{ + buffer_object *objval = buffer_object_from_zend_object(Z_OBJ_P(getThis())); + + zend_long size = 0; + zend_long offset = 0; + + if (zend_parse_parameters_throw(ZEND_NUM_ARGS(), "l|l", &size, &offset) == FAILURE) { + return; + } + + GUARD_SIZE(objval, offset + size); + + buffer_object *new_obj = NULL; + + object_init_ex(return_value, objval->std.ce); + + new_obj = buffer_object_from_zend_object(Z_OBJ_P(return_value)); + new_obj->data = new Buffer(); + new_obj->data->appendString(objval->data->readString(size, offset)); +} + +PHP_METHOD(ByteBuffer, shift) +{ + buffer_object *objval = buffer_object_from_zend_object(Z_OBJ_P(getThis())); + + zend_long size = 0; + + if (zend_parse_parameters_throw(ZEND_NUM_ARGS(), "l", &size) == FAILURE) { + return; + } + + GUARD_SIZE(objval, size); + + buffer_object *new_obj = NULL; + + object_init_ex(return_value, objval->std.ce); + + new_obj = buffer_object_from_zend_object(Z_OBJ_P(return_value)); + new_obj->data = new Buffer(); + new_obj->data->appendString(objval->data->consumeString(size)); +} + +PHP_METHOD(ByteBuffer, flush) +{ + buffer_object *objval = buffer_object_from_zend_object(Z_OBJ_P(getThis())); + + std::string s = objval->data->flush(); + + RETURN_NEW_STR(zend_string_init(s.data(), s.size(), 0)); +} + +PHP_METHOD(ByteBuffer, appendBool) +{ + buffer_object *objval = buffer_object_from_zend_object(Z_OBJ_P(getThis())); + + zend_bool value; + + if (zend_parse_parameters_throw(ZEND_NUM_ARGS(), "b", &value) == FAILURE) { + return; + } + + objval->data->appendBoolean(value); + + RETURN_ZVAL(getThis(), 1, 0); +} + +PHP_METHOD(ByteBuffer, readBool) +{ + buffer_object *objval = buffer_object_from_zend_object(Z_OBJ_P(getThis())); + + zend_long offset = 0; + + if (zend_parse_parameters_throw(ZEND_NUM_ARGS(), "|l", &offset) == FAILURE) { + return; + } + + GUARD_SIZE(objval, offset + 1); + + RETURN_BOOL(objval->data->readBoolean(offset)); +} + +PHP_METHOD(ByteBuffer, consumeBool) +{ + buffer_object *objval = buffer_object_from_zend_object(Z_OBJ_P(getThis())); + + GUARD_SIZE(objval, 1); + + RETURN_BOOL(objval->data->consumeBool()); +} + +PHP_METHOD(ByteBuffer, appendInt8) +{ + buffer_object *objval = buffer_object_from_zend_object(Z_OBJ_P(getThis())); + + zend_long value; + + if (zend_parse_parameters_throw(ZEND_NUM_ARGS(), "l", &value) == FAILURE) { + return; + } + + objval->data->appendInt8(value); + + RETURN_ZVAL(getThis(), 1, 0); +} + +PHP_METHOD(ByteBuffer, readInt8) +{ + buffer_object *objval = buffer_object_from_zend_object(Z_OBJ_P(getThis())); + + zend_long offset = 0; + + if (zend_parse_parameters_throw(ZEND_NUM_ARGS(), "|l", &offset) == FAILURE) { + return; + } + + GUARD_SIZE(objval, offset + 1); + + RETURN_LONG(objval->data->readInt8(offset)); +} + +PHP_METHOD(ByteBuffer, consumeInt8) +{ + buffer_object *objval = buffer_object_from_zend_object(Z_OBJ_P(getThis())); + + GUARD_SIZE(objval, 1); + + RETURN_LONG(objval->data->consumeInt8()); +} + +PHP_METHOD(ByteBuffer, appendUInt8) +{ + buffer_object *objval = buffer_object_from_zend_object(Z_OBJ_P(getThis())); + + zend_long value; + + if (zend_parse_parameters_throw(ZEND_NUM_ARGS(), "l", &value) == FAILURE) { + return; + } + + objval->data->appendUInt8(value); + + RETURN_ZVAL(getThis(), 1, 0); +} + +PHP_METHOD(ByteBuffer, readUInt8) +{ + buffer_object *objval = buffer_object_from_zend_object(Z_OBJ_P(getThis())); + + zend_long offset = 0; + + if (zend_parse_parameters_throw(ZEND_NUM_ARGS(), "|l", &offset) == FAILURE) { + return; + } + + GUARD_SIZE(objval, offset + 1); + + RETURN_LONG(objval->data->readUInt8(offset)); +} + +PHP_METHOD(ByteBuffer, consumeUInt8) +{ + buffer_object *objval = buffer_object_from_zend_object(Z_OBJ_P(getThis())); + + GUARD_SIZE(objval, 1); + + RETURN_LONG(objval->data->consumeUInt8()); +} + +PHP_METHOD(ByteBuffer, appendInt16) +{ + buffer_object *objval = buffer_object_from_zend_object(Z_OBJ_P(getThis())); + + zend_long value; + + if (zend_parse_parameters_throw(ZEND_NUM_ARGS(), "l", &value) == FAILURE) { + return; + } + + objval->data->appendInt16(value); + + RETURN_ZVAL(getThis(), 1, 0); +} + +PHP_METHOD(ByteBuffer, readInt16) +{ + buffer_object *objval = buffer_object_from_zend_object(Z_OBJ_P(getThis())); + + zend_long offset = 0; + + if (zend_parse_parameters_throw(ZEND_NUM_ARGS(), "|l", &offset) == FAILURE) { + return; + } + + GUARD_SIZE(objval, offset + 2); + + RETURN_LONG(objval->data->readInt16(offset)); +} + +PHP_METHOD(ByteBuffer, consumeInt16) +{ + buffer_object *objval = buffer_object_from_zend_object(Z_OBJ_P(getThis())); + + GUARD_SIZE(objval, 2); + + RETURN_LONG(objval->data->consumeInt16()); +} + +PHP_METHOD(ByteBuffer, appendUInt16) +{ + buffer_object *objval = buffer_object_from_zend_object(Z_OBJ_P(getThis())); + + zend_long value; + + if (zend_parse_parameters_throw(ZEND_NUM_ARGS(), "l", &value) == FAILURE) { + return; + } + + objval->data->appendUInt16(value); + + RETURN_ZVAL(getThis(), 1, 0); +} + +PHP_METHOD(ByteBuffer, readUInt16) +{ + buffer_object *objval = buffer_object_from_zend_object(Z_OBJ_P(getThis())); + + zend_long offset = 0; + + if (zend_parse_parameters_throw(ZEND_NUM_ARGS(), "|l", &offset) == FAILURE) { + return; + } + + GUARD_SIZE(objval, offset + 2); + + RETURN_LONG(objval->data->readUInt16(offset)); +} + +PHP_METHOD(ByteBuffer, consumeUInt16) +{ + buffer_object *objval = buffer_object_from_zend_object(Z_OBJ_P(getThis())); + + GUARD_SIZE(objval, 2); + + RETURN_LONG(objval->data->consumeUInt16()); +} + +PHP_METHOD(ByteBuffer, appendInt32) +{ + buffer_object *objval = buffer_object_from_zend_object(Z_OBJ_P(getThis())); + + zend_long value; + + if (zend_parse_parameters_throw(ZEND_NUM_ARGS(), "l", &value) == FAILURE) { + return; + } + + objval->data->appendInt32(value); + + RETURN_ZVAL(getThis(), 1, 0); +} + +PHP_METHOD(ByteBuffer, readInt32) +{ + buffer_object *objval = buffer_object_from_zend_object(Z_OBJ_P(getThis())); + + zend_long offset = 0; + + if (zend_parse_parameters_throw(ZEND_NUM_ARGS(), "|l", &offset) == FAILURE) { + return; + } + + GUARD_SIZE(objval, offset + 4); + + RETURN_LONG(objval->data->readInt32(offset)); +} + +PHP_METHOD(ByteBuffer, consumeInt32) +{ + buffer_object *objval = buffer_object_from_zend_object(Z_OBJ_P(getThis())); + + GUARD_SIZE(objval, 4); + + RETURN_LONG(objval->data->consumeInt32()); +} + +PHP_METHOD(ByteBuffer, appendUInt32) +{ + buffer_object *objval = buffer_object_from_zend_object(Z_OBJ_P(getThis())); + + zend_long value; + + if (zend_parse_parameters_throw(ZEND_NUM_ARGS(), "l", &value) == FAILURE) { + return; + } + + objval->data->appendUInt32(value); + + RETURN_ZVAL(getThis(), 1, 0); +} + +PHP_METHOD(ByteBuffer, readUInt32) +{ + buffer_object *objval = buffer_object_from_zend_object(Z_OBJ_P(getThis())); + + zend_long offset = 0; + + if (zend_parse_parameters_throw(ZEND_NUM_ARGS(), "|l", &offset) == FAILURE) { + return; + } + + GUARD_SIZE(objval, offset + 4); + + RETURN_LONG(objval->data->readUInt32(offset)); +} + +PHP_METHOD(ByteBuffer, consumeUInt32) +{ + buffer_object *objval = buffer_object_from_zend_object(Z_OBJ_P(getThis())); + + GUARD_SIZE(objval, 4); + + RETURN_LONG(objval->data->consumeUInt32()); +} + +PHP_METHOD(ByteBuffer, appendInt64) +{ + buffer_object *objval = buffer_object_from_zend_object(Z_OBJ_P(getThis())); + + zend_long value; + + if (zend_parse_parameters_throw(ZEND_NUM_ARGS(), "l", &value) == FAILURE) { + return; + } + + objval->data->appendInt64(value); + + RETURN_ZVAL(getThis(), 1, 0); +} + +PHP_METHOD(ByteBuffer, readInt64) +{ + buffer_object *objval = buffer_object_from_zend_object(Z_OBJ_P(getThis())); + + zend_long offset = 0; + + if (zend_parse_parameters_throw(ZEND_NUM_ARGS(), "|l", &offset) == FAILURE) { + return; + } + + GUARD_SIZE(objval, offset + 8); + + RETURN_LONG(objval->data->readInt64(offset)); +} + +PHP_METHOD(ByteBuffer, consumeInt64) +{ + buffer_object *objval = buffer_object_from_zend_object(Z_OBJ_P(getThis())); + + GUARD_SIZE(objval, 8); + + RETURN_LONG(objval->data->consumeInt64()); +} + +PHP_METHOD(ByteBuffer, appendUInt64) +{ + buffer_object *objval = buffer_object_from_zend_object(Z_OBJ_P(getThis())); + + zend_long value; + + if (zend_parse_parameters_throw(ZEND_NUM_ARGS(), "l", &value) == FAILURE) { + return; + } + + objval->data->appendUInt64(value); + + RETURN_ZVAL(getThis(), 1, 0); +} + +PHP_METHOD(ByteBuffer, readUInt64) +{ + buffer_object *objval = buffer_object_from_zend_object(Z_OBJ_P(getThis())); + + zend_long offset = 0; + + if (zend_parse_parameters_throw(ZEND_NUM_ARGS(), "|l", &offset) == FAILURE) { + return; + } + + GUARD_SIZE(objval, offset + 8); + + RETURN_LONG(objval->data->readUInt64(offset)); +} + +PHP_METHOD(ByteBuffer, consumeUInt64) +{ + buffer_object *objval = buffer_object_from_zend_object(Z_OBJ_P(getThis())); + + GUARD_SIZE(objval, 8); + + RETURN_LONG(objval->data->consumeUInt64()); +} + +PHP_METHOD(ByteBuffer, appendFloat) +{ + buffer_object *objval = buffer_object_from_zend_object(Z_OBJ_P(getThis())); + + double value; + + if (zend_parse_parameters_throw(ZEND_NUM_ARGS(), "d", &value) == FAILURE) { + return; + } + + objval->data->appendFloat(value); + + RETURN_ZVAL(getThis(), 1, 0); +} + +PHP_METHOD(ByteBuffer, readFloat) +{ + buffer_object *objval = buffer_object_from_zend_object(Z_OBJ_P(getThis())); + + zend_long offset = 0; + + if (zend_parse_parameters_throw(ZEND_NUM_ARGS(), "|l", &offset) == FAILURE) { + return; + } + + GUARD_SIZE(objval, offset + 4); + + RETURN_DOUBLE(objval->data->readFloat(offset)); +} + +PHP_METHOD(ByteBuffer, consumeFloat) +{ + buffer_object *objval = buffer_object_from_zend_object(Z_OBJ_P(getThis())); + + GUARD_SIZE(objval, 4); + + RETURN_DOUBLE(objval->data->consumeFloat()); +} + +PHP_METHOD(ByteBuffer, appendDouble) +{ + buffer_object *objval = buffer_object_from_zend_object(Z_OBJ_P(getThis())); + + double value; + + if (zend_parse_parameters_throw(ZEND_NUM_ARGS(), "d", &value) == FAILURE) { + return; + } + + objval->data->appendDouble(value); + + RETURN_ZVAL(getThis(), 1, 0); +} + +PHP_METHOD(ByteBuffer, readDouble) +{ + buffer_object *objval = buffer_object_from_zend_object(Z_OBJ_P(getThis())); + + zend_long offset = 0; + + if (zend_parse_parameters_throw(ZEND_NUM_ARGS(), "|l", &offset) == FAILURE) { + return; + } + + GUARD_SIZE(objval, offset + 8); + + RETURN_DOUBLE(objval->data->readDouble(offset)); +} + +PHP_METHOD(ByteBuffer, consumeDouble) +{ + buffer_object *objval = buffer_object_from_zend_object(Z_OBJ_P(getThis())); + + GUARD_SIZE(objval, 8); + + RETURN_DOUBLE(objval->data->consumeDouble()); +} + +static zend_function_entry buffer_object_methods[] = { + PHP_ME(ByteBuffer, __construct, NULL, ZEND_ACC_CTOR | ZEND_ACC_PUBLIC) + PHP_ME(ByteBuffer, __toString, NULL, ZEND_ACC_PUBLIC) + PHP_ME(ByteBuffer, bytes, NULL, ZEND_ACC_PUBLIC) + PHP_ME(ByteBuffer, size, NULL, ZEND_ACC_PUBLIC) + PHP_ME(ByteBuffer, empty, NULL, ZEND_ACC_PUBLIC) + PHP_ME(ByteBuffer, append, NULL, ZEND_ACC_PUBLIC) + PHP_ME(ByteBuffer, read, NULL, ZEND_ACC_PUBLIC) + PHP_ME(ByteBuffer, consume, NULL, ZEND_ACC_PUBLIC) + PHP_ME(ByteBuffer, discard, NULL, ZEND_ACC_PUBLIC) + PHP_ME(ByteBuffer, slice, NULL, ZEND_ACC_PUBLIC) + PHP_ME(ByteBuffer, shift, NULL, ZEND_ACC_PUBLIC) + PHP_ME(ByteBuffer, flush, NULL, ZEND_ACC_PUBLIC) + // BOOL + PHP_ME(ByteBuffer, appendBool, NULL, ZEND_ACC_PUBLIC) + PHP_ME(ByteBuffer, readBool, NULL, ZEND_ACC_PUBLIC) + PHP_ME(ByteBuffer, consumeBool, NULL, ZEND_ACC_PUBLIC) + // INT 8 + PHP_ME(ByteBuffer, appendInt8, NULL, ZEND_ACC_PUBLIC) + PHP_ME(ByteBuffer, readInt8, NULL, ZEND_ACC_PUBLIC) + PHP_ME(ByteBuffer, consumeInt8, NULL, ZEND_ACC_PUBLIC) + PHP_ME(ByteBuffer, appendUInt8, NULL, ZEND_ACC_PUBLIC) + PHP_ME(ByteBuffer, readUInt8, NULL, ZEND_ACC_PUBLIC) + PHP_ME(ByteBuffer, consumeUInt8, NULL, ZEND_ACC_PUBLIC) + // INT 16 + PHP_ME(ByteBuffer, appendInt16, NULL, ZEND_ACC_PUBLIC) + PHP_ME(ByteBuffer, readInt16, NULL, ZEND_ACC_PUBLIC) + PHP_ME(ByteBuffer, consumeInt16, NULL, ZEND_ACC_PUBLIC) + PHP_ME(ByteBuffer, appendUInt16, NULL, ZEND_ACC_PUBLIC) + PHP_ME(ByteBuffer, readUInt16, NULL, ZEND_ACC_PUBLIC) + PHP_ME(ByteBuffer, consumeUInt16, NULL, ZEND_ACC_PUBLIC) + // INT 32 + PHP_ME(ByteBuffer, appendInt32, NULL, ZEND_ACC_PUBLIC) + PHP_ME(ByteBuffer, readInt32, NULL, ZEND_ACC_PUBLIC) + PHP_ME(ByteBuffer, consumeInt32, NULL, ZEND_ACC_PUBLIC) + PHP_ME(ByteBuffer, appendUInt32, NULL, ZEND_ACC_PUBLIC) + PHP_ME(ByteBuffer, readUInt32, NULL, ZEND_ACC_PUBLIC) + PHP_ME(ByteBuffer, consumeUInt32, NULL, ZEND_ACC_PUBLIC) + // INT 64 + PHP_ME(ByteBuffer, appendInt64, NULL, ZEND_ACC_PUBLIC) + PHP_ME(ByteBuffer, readInt64, NULL, ZEND_ACC_PUBLIC) + PHP_ME(ByteBuffer, consumeInt64, NULL, ZEND_ACC_PUBLIC) + PHP_ME(ByteBuffer, appendUInt64, NULL, ZEND_ACC_PUBLIC) + PHP_ME(ByteBuffer, readUInt64, NULL, ZEND_ACC_PUBLIC) + PHP_ME(ByteBuffer, consumeUInt64, NULL, ZEND_ACC_PUBLIC) + // FLOAT + PHP_ME(ByteBuffer, appendFloat, NULL, ZEND_ACC_PUBLIC) + PHP_ME(ByteBuffer, readFloat, NULL, ZEND_ACC_PUBLIC) + PHP_ME(ByteBuffer, consumeFloat, NULL, ZEND_ACC_PUBLIC) + // DOUBLE + PHP_ME(ByteBuffer, appendDouble, NULL, ZEND_ACC_PUBLIC) + PHP_ME(ByteBuffer, readDouble, NULL, ZEND_ACC_PUBLIC) + PHP_ME(ByteBuffer, consumeDouble, NULL, ZEND_ACC_PUBLIC) + + PHP_FE_END +}; + +static zend_object* buffer_object_create(zend_class_entry *ce) { + buffer_object *objval = (buffer_object*) ecalloc(1, sizeof(buffer_object) + zend_object_properties_size(ce)); + + zend_object* ret = buffer_object_to_zend_object(objval); + + zend_object_std_init(ret, ce); + object_properties_init(ret, ce); + + ret->handlers = &buffer_object_handlers; + + return ret; +} + +static zend_object *buffer_object_clone(zval *object) +{ + buffer_object *old_obj = buffer_object_from_zend_object(Z_OBJ_P(object)); + buffer_object *new_obj = buffer_object_from_zend_object(buffer_object_create(old_obj->std.ce)); + + zend_objects_clone_members(&new_obj->std, &old_obj->std); + + new_obj->data = new Buffer(); + new_obj->data->merge(*old_obj->data); + + return &new_obj->std; +} + +static void buffer_object_free(zend_object *zobj) +{ + buffer_object *obj = buffer_object_from_zend_object(zobj); + + delete obj->data; + + zend_object_std_dtor(zobj); +} + +static void buffer_object_destroy(zend_object *object) +{ + zend_objects_destroy_object(object); +} + +static PHP_MINIT_FUNCTION(buffer) +{ + zend_class_entry bce; + zend_class_entry ece; + + INIT_CLASS_ENTRY(bce, "PHPinnacle\\Buffer\\ByteBuffer", buffer_object_methods); + INIT_CLASS_ENTRY(ece, "PHPinnacle\\Buffer\\BufferOverflow", NULL); + + buffer_exception_ce = zend_register_internal_class_ex(&ece, zend_ce_exception); + + buffer_object_ce = zend_register_internal_class(&bce); + buffer_object_ce->create_object = buffer_object_create; + + memcpy(&buffer_object_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers)); + + buffer_object_handlers.offset = XtOffsetOf(buffer_object, std); + buffer_object_handlers.clone_obj = buffer_object_clone; + buffer_object_handlers.free_obj = buffer_object_free; + buffer_object_handlers.dtor_obj = buffer_object_destroy; + + return SUCCESS; +} + +static PHP_MSHUTDOWN_FUNCTION(buffer) +{ + return SUCCESS; +} + +static PHP_MINFO_FUNCTION(buffer) +{ + php_info_print_table_start(); + php_info_print_table_header(2, "PHPinnacle ByteBuffer support", "enabled"); + php_info_print_table_end(); +} + +zend_module_entry buffer_module_entry = { + STANDARD_MODULE_HEADER, + "buffer", + NULL, + PHP_MINIT(buffer), + PHP_MSHUTDOWN(buffer), + NULL, + NULL, + PHP_MINFO(buffer), + PHP_BUFFER_VERSION, + STANDARD_MODULE_PROPERTIES +}; + +#ifdef COMPILE_DL_BUFFER +extern "C" { + ZEND_GET_MODULE(buffer) +} +#endif diff --git a/ext/config.m4 b/ext/config.m4 new file mode 100644 index 0000000..cbc44a5 --- /dev/null +++ b/ext/config.m4 @@ -0,0 +1,82 @@ +dnl config.m4 for extension buffer + +dnl Comments in this file start with the string 'dnl'. +dnl Remove where necessary. + +dnl If your extension references something external, use with: + +dnl PHP_ARG_WITH(buffer, for buffer support, +dnl Make sure that the comment is aligned: +dnl [ --with-buffer Include buffer support]) + +dnl Otherwise use enable: + +PHP_ARG_ENABLE(buffer, whether to enable buffer support, +dnl Make sure that the comment is aligned: +[ --enable-buffer Enable byte buffer support], no) + +if test "$PHP_BUFFER" != "no"; then + dnl Write more examples of tests here... + + dnl # get library FOO build options from pkg-config output + dnl AC_PATH_PROG(PKG_CONFIG, pkg-config, no) + dnl AC_MSG_CHECKING(for libfoo) + dnl if test -x "$PKG_CONFIG" && $PKG_CONFIG --exists foo; then + dnl if $PKG_CONFIG foo --atleast-version 1.2.3; then + dnl LIBFOO_CFLAGS=\`$PKG_CONFIG foo --cflags\` + dnl LIBFOO_LIBDIR=\`$PKG_CONFIG foo --libs\` + dnl LIBFOO_VERSON=\`$PKG_CONFIG foo --modversion\` + dnl AC_MSG_RESULT(from pkgconfig: version $LIBFOO_VERSON) + dnl else + dnl AC_MSG_ERROR(system libfoo is too old: version 1.2.3 required) + dnl fi + dnl else + dnl AC_MSG_ERROR(pkg-config not found) + dnl fi + dnl PHP_EVAL_LIBLINE($LIBFOO_LIBDIR, BUFFER_SHARED_LIBADD) + dnl PHP_EVAL_INCLINE($LIBFOO_CFLAGS) + + dnl # --with-buffer -> check with-path + dnl SEARCH_PATH="/usr/local /usr" # you might want to change this + dnl SEARCH_FOR="/include/buffer.h" # you most likely want to change this + dnl if test -r $PHP_BUFFER/$SEARCH_FOR; then # path given as parameter + dnl BUFFER_DIR=$PHP_BUFFER + dnl else # search default path list + dnl AC_MSG_CHECKING([for buffer files in default path]) + dnl for i in $SEARCH_PATH ; do + dnl if test -r $i/$SEARCH_FOR; then + dnl BUFFER_DIR=$i + dnl AC_MSG_RESULT(found in $i) + dnl fi + dnl done + dnl fi + dnl + dnl if test -z "$BUFFER_DIR"; then + dnl AC_MSG_RESULT([not found]) + dnl AC_MSG_ERROR([Please reinstall the buffer distribution]) + dnl fi + + dnl # --with-buffer -> add include path + dnl PHP_ADD_INCLUDE($BUFFER_DIR/include) + + dnl # --with-buffer -> check for lib and symbol presence + dnl LIBNAME=BUFFER # you may want to change this + dnl LIBSYMBOL=BUFFER # you most likely want to change this + + dnl PHP_CHECK_LIBRARY($LIBNAME,$LIBSYMBOL, + dnl [ + dnl PHP_ADD_LIBRARY_WITH_PATH($LIBNAME, $BUFFER_DIR/$PHP_LIBDIR, BUFFER_SHARED_LIBADD) + dnl AC_DEFINE(HAVE_BUFFERLIB,1,[ ]) + dnl ],[ + dnl AC_MSG_ERROR([wrong buffer lib version or lib not found]) + dnl ],[ + dnl -L$BUFFER_DIR/$PHP_LIBDIR -lm + dnl ]) + dnl + dnl PHP_SUBST(BUFFER_SHARED_LIBADD) + + PHP_REQUIRE_CXX() + PHP_SUBST(BUFFER_SHARED_LIBADD) + PHP_ADD_LIBRARY(stdc++, 1, BUFFER_SHARED_LIBADD) + PHP_NEW_EXTENSION(buffer, buffer.cc Buffer.cpp, $ext_shared) +fi diff --git a/ext/etc/buffer.php b/ext/etc/buffer.php new file mode 100644 index 0000000..49d27a1 --- /dev/null +++ b/ext/etc/buffer.php @@ -0,0 +1,39 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +use PHPinnacle\Buffer\ByteBuffer; + +$buffer = new ByteBuffer; + +$buffer->append('abcd'); +$buffer->read(4); + +$buffer->append(new ByteBuffer('zz')); +$buffer->consume(6); + +$buffer->appendUint8(1); +$buffer->appendInt8(1); +$slice = $buffer->slice(2); +$slice->readUint8(); +$slice->readInt8(1); +$buffer->consumeUint8(); +$buffer->consumeInt8(); + +$buffer->appendUint16(1); +$buffer->appendInt16(1); +$shift = $buffer->shift(4); +$shift->consumeUint16(); +$shift->consumeInt16(); + +$clone = clone $buffer; +$clone->appendUint32(1); + +echo $buffer->flush(); +echo (string) $clone; diff --git a/ext/php_buffer.h b/ext/php_buffer.h new file mode 100644 index 0000000..d6c4a7d --- /dev/null +++ b/ext/php_buffer.h @@ -0,0 +1,30 @@ +/** + * This file is part of PHPinnacle/Buffer. + * + * (c) PHPinnacle Team + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +#ifndef PHP_BUFFER_H +#define PHP_BUFFER_H + +#define PHP_BUFFER_EXTNAME "buffer" +#define PHP_BUFFER_VERSION "0.1.0" + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +extern "C" { +#include "php.h" +#include "ext/standard/info.h" +#include "zend_exceptions.h" +} + +extern zend_module_entry buffer_module_entry; +#define buffer_module_ptr &buffer_module_entry +#define phpext_buffer_ptr buffer_module_ptr + +#endif diff --git a/ext/tests/001.phpt b/ext/tests/001.phpt new file mode 100644 index 0000000..4c0c78a --- /dev/null +++ b/ext/tests/001.phpt @@ -0,0 +1,14 @@ +--TEST-- +Check if buffer is loaded +--SKIPIF-- + +--FILE-- + +--EXPECT-- +The extension "buffer" is available \ No newline at end of file diff --git a/ext/tests/002.phpt b/ext/tests/002.phpt new file mode 100644 index 0000000..155df1b --- /dev/null +++ b/ext/tests/002.phpt @@ -0,0 +1,40 @@ +--TEST-- +Buffer size test +--SKIPIF-- + +--FILE-- +size()}\n"; + +$buffer->append('a'); +echo "size: {$buffer->size()}\n"; + +$buffer->append('a'); +echo "size: {$buffer->size()}\n"; + +$buffer->read(1); +echo "size: {$buffer->size()}\n"; + +$buffer->read(2); +echo "size: {$buffer->size()}\n"; + +$buffer->consume(1); +echo "size: {$buffer->size()}\n"; + +$buffer->consume(1); +echo "size: {$buffer->size()}\n"; +?> +--EXPECT-- +size: 0 +size: 1 +size: 2 +size: 2 +size: 2 +size: 1 +size: 0 diff --git a/ext/tests/003.phpt b/ext/tests/003.phpt new file mode 100644 index 0000000..c5d8025 --- /dev/null +++ b/ext/tests/003.phpt @@ -0,0 +1,25 @@ +--TEST-- +Buffer empty test +--SKIPIF-- + +--FILE-- +empty()) { + echo "Empty\n"; +} + +$buffer->append('a'); + +if (!$buffer->empty()) { + echo "Not empty\n"; +} +?> +--EXPECT-- +Empty +Not empty \ No newline at end of file diff --git a/ext/tests/004.phpt b/ext/tests/004.phpt new file mode 100644 index 0000000..ef8febe --- /dev/null +++ b/ext/tests/004.phpt @@ -0,0 +1,18 @@ +--TEST-- +Buffer flush test +--SKIPIF-- + +--FILE-- +flush() . \PHP_EOL; +echo $buffer->empty() ? 'Empty' : 'Not empty'; +?> +--EXPECT-- +abcd +Empty diff --git a/ext/tests/005.phpt b/ext/tests/005.phpt new file mode 100644 index 0000000..46b912c --- /dev/null +++ b/ext/tests/005.phpt @@ -0,0 +1,18 @@ +--TEST-- +Buffer to string test +--SKIPIF-- + +--FILE-- +__toString() . \PHP_EOL; +?> +--EXPECT-- +abcd +abcd diff --git a/ext/tests/006.phpt b/ext/tests/006.phpt new file mode 100644 index 0000000..9d72dcc --- /dev/null +++ b/ext/tests/006.phpt @@ -0,0 +1,35 @@ +--TEST-- +Buffer read test +--SKIPIF-- + +--FILE-- +read(1) . \PHP_EOL; +echo $buffer->read(2) . \PHP_EOL; +echo $buffer->read(3) . \PHP_EOL; +echo $buffer->read(4) . \PHP_EOL; +echo $buffer->read(1, 1) . \PHP_EOL; +echo $buffer->read(3, 1) . \PHP_EOL; +echo "size: " . $buffer->size() . \PHP_EOL; + +try { + $buffer->read(5); +} catch (\PHPinnacle\Buffer\BufferOverflow $e) { + echo $e->getMessage(); +} +?> +--EXPECT-- +a +ab +abc +abcd +b +bcd +size: 4 +Buffer overflow. diff --git a/ext/tests/007.phpt b/ext/tests/007.phpt new file mode 100644 index 0000000..f49e571 --- /dev/null +++ b/ext/tests/007.phpt @@ -0,0 +1,29 @@ +--TEST-- +Buffer consume test +--SKIPIF-- + +--FILE-- +consume(1) . \PHP_EOL; +echo $buffer->consume(2) . \PHP_EOL; +echo $buffer->consume(1) . \PHP_EOL; +echo "size: " . $buffer->size() . \PHP_EOL; + +try { + $buffer->consume(1); +} catch (\PHPinnacle\Buffer\BufferOverflow $e) { + echo $e->getMessage(); +} +?> +--EXPECT-- +a +bc +d +size: 0 +Buffer overflow. \ No newline at end of file diff --git a/ext/tests/008.phpt b/ext/tests/008.phpt new file mode 100644 index 0000000..a9f0989 --- /dev/null +++ b/ext/tests/008.phpt @@ -0,0 +1,32 @@ +--TEST-- +Buffer discard test +--SKIPIF-- + +--FILE-- +discard(1); + +echo $buffer->read(3) . \PHP_EOL; + +$buffer->discard(2); + +echo $buffer->read(1) . \PHP_EOL; +echo "size: " . $buffer->size() . \PHP_EOL; + +try { + $buffer->discard(2); +} catch (\PHPinnacle\Buffer\BufferOverflow $e) { + echo $e->getMessage(); +} +?> +--EXPECT-- +bcd +d +size: 1 +Buffer overflow. \ No newline at end of file diff --git a/ext/tests/009.phpt b/ext/tests/009.phpt new file mode 100644 index 0000000..f0ae45f --- /dev/null +++ b/ext/tests/009.phpt @@ -0,0 +1,27 @@ +--TEST-- +Buffer slice test +--SKIPIF-- + +--FILE-- +slice(3); + +echo $slice3->read(3) . \PHP_EOL; +echo "size: " . $buffer->size() . \PHP_EOL; + +try { + $buffer->slice(5); +} catch (\PHPinnacle\Buffer\BufferOverflow $e) { + echo $e->getMessage(); +} +?> +--EXPECT-- +abc +size: 4 +Buffer overflow. \ No newline at end of file diff --git a/ext/tests/010.phpt b/ext/tests/010.phpt new file mode 100644 index 0000000..d3603de --- /dev/null +++ b/ext/tests/010.phpt @@ -0,0 +1,29 @@ +--TEST-- +Buffer shift test +--SKIPIF-- + +--FILE-- +shift(3); + +echo $shift3->read(3) . \PHP_EOL; +echo $buffer->read(1) . \PHP_EOL; +echo "size: " . $buffer->size() . \PHP_EOL; + +try { + $buffer->shift(2); +} catch (\PHPinnacle\Buffer\BufferOverflow $e) { + echo $e->getMessage(); +} +?> +--EXPECT-- +abc +d +size: 1 +Buffer overflow. \ No newline at end of file diff --git a/ext/tests/011.phpt b/ext/tests/011.phpt new file mode 100644 index 0000000..a95059c --- /dev/null +++ b/ext/tests/011.phpt @@ -0,0 +1,37 @@ +--TEST-- +Buffer append test +--SKIPIF-- + +--FILE-- +size() . \PHP_EOL; + +$buffer->append('abcd'); + +echo $buffer->read(4) . \PHP_EOL; +echo "size: " . $buffer->size() . \PHP_EOL; + +$buffer->append('efgh'); + +echo $buffer->read(8) . \PHP_EOL; +echo "size: " . $buffer->size() . \PHP_EOL; + +$buffer->append(new PHPinnacle\Buffer\ByteBuffer('zz')); + +echo $buffer->read(10) . \PHP_EOL; +echo "size: " . $buffer->size() . \PHP_EOL; +?> +--EXPECT-- +size: 0 +abcd +size: 4 +abcdefgh +size: 8 +abcdefghzz +size: 10 \ No newline at end of file diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 059b35d..09bb3af 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -22,7 +22,7 @@ - + diff --git a/src/Binary.php b/src/Binary.php deleted file mode 100644 index 933041f..0000000 --- a/src/Binary.php +++ /dev/null @@ -1,589 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -declare(strict_types = 1); - -namespace PHPinnacle\Buffer; - -class Binary -{ - /** - * @var bool - */ - private static $isLittleEndian; - - /** - * @var bool - */ - private static $native64BitPack; - - /** - * @var string - */ - private $data; - - /** - * @var int - */ - private $size; - - /** - * @param string $buffer - */ - public function __construct(string $buffer = '') - { - $this->data = $buffer; - $this->size = \strlen($this->data); - - if (!isset(self::$native64BitPack)) { - self::$native64BitPack = PHP_INT_SIZE === 8; - self::$isLittleEndian = \unpack("S", "\x01\x00")[1] === 1; - } - } - - /** - * @param string|static $value - * - * @return static - */ - public function append($value): self - { - if ($value instanceof Binary) { - $value = $value->data; - } - - $this->data .= $value; - $this->size += \strlen($value); - - return $this; - } - - /** - * @param int $n - * @param int $offset - * - * @return string - */ - public function read(int $n, int $offset = 0): string - { - if ($this->size < $offset + $n) { - throw new Exception\BufferUnderflow; - } - - if ($offset === 0 && $this->size === $offset + $n) { - return $this->data; - } - - return \substr($this->data, $offset, $n); - } - - /** - * @param int $n - * - * @return string - */ - public function consume(int $n): string - { - if ($this->size < $n) { - throw new Exception\BufferUnderflow; - } - - if ($this->size === $n) { - $buffer = $this->data; - - $this->data = ''; - $this->size = 0; - } else { - $buffer = \substr($this->data, 0, $n); - - $this->data = \substr($this->data, $n); - $this->size -= $n; - } - - return $buffer; - } - - /** - * @param int $n - * - * @return static - */ - public function discard(int $n): self - { - if ($this->size < $n) { - throw new Exception\BufferUnderflow; - } - - if ($this->size === $n) { - $this->data = ''; - $this->size = 0; - } else { - $this->data = \substr($this->data, $n); - $this->size -= $n; - } - - return $this; - } - - /** - * @param int $n - * - * @return static - */ - public function slice(int $n): self - { - if ($this->size < $n) { - throw new Exception\BufferUnderflow; - } - - return $this->size === $n ? new static($this->data) : new static(\substr($this->data, 0, $n)); - } - - /** - * @param int $n - * - * @return static - */ - public function shift(int $n): self - { - if ($this->size < $n) { - throw new Exception\BufferUnderflow; - } - - if ($this->size === $n) { - $buffer = $this->data; - - $this->data = ''; - $this->size = 0; - } else { - $buffer = \substr($this->data, 0, $n); - - $this->data = \substr($this->data, $n); - $this->size -= $n; - } - - return new static($buffer); - } - - /** - * @return string - */ - public function flush(): string - { - $data = $this->data; - - $this->data = ''; - $this->size = 0; - - return $data; - } - - /** - * @return int - */ - public function size(): int - { - return $this->size; - } - - /** - * @return boolean - */ - public function empty(): bool - { - return $this->size === 0; - } - - /** - * @param int $value - * - * @return static - */ - public function appendInt8(int $value): self - { - return $this->append(\pack("c", $value)); - } - - /** - * @param int $offset - * - * @return int - */ - public function readInt8(int $offset = 0): int - { - return \unpack("c", $this->read(1, $offset))[1]; - } - - /** - * @return int - */ - public function consumeInt8(): int - { - return \unpack("c", $this->consume(1))[1]; - } - - /** - * @param int $value - * - * @return static - */ - public function appendInt16(int $value): self - { - return $this->append(self::swapEndian16(\pack("s", $value))); - } - - /** - * @param int $offset - * - * @return int - */ - public function readInt16(int $offset = 0): int - { - return \unpack("s", self::swapEndian16($this->read(2, $offset)))[1]; - } - - /** - * @return int - */ - public function consumeInt16(): int - { - return \unpack("s", self::swapEndian16($this->consume(2)))[1]; - } - - /** - * @param int $value - * - * @return static - */ - public function appendInt32(int $value): self - { - return $this->append(self::swapEndian32(\pack("l", $value))); - } - - /** - * @param int $offset - * - * @return int - */ - public function readInt32(int $offset = 0): int - { - return \unpack("l", self::swapEndian32($this->read(4, $offset)))[1]; - } - - /** - * @return int - */ - public function consumeInt32(): int - { - return \unpack("l", self::swapEndian32($this->consume(4)))[1]; - } - - /** - * @param int $value - * - * @return static - */ - public function appendInt64(int $value): self - { - if (self::$native64BitPack) { - $s = self::swapEndian64(\pack("q", $value)); - } else { - $s = \pack("LL", ($value & 0xffffffff00000000) >> 32, $value & 0x00000000ffffffff); - $s = self::swapHalvedEndian64($s); - } - - return $this->append($s); - } - - /** - * @param int $offset - * - * @return int - */ - public function readInt64(int $offset = 0): int - { - $s = $this->read(8, $offset); - - if (self::$native64BitPack) { - return \unpack("q", self::swapEndian64($s))[1]; - } - - $d = \unpack("Lh/Ll", self::swapHalvedEndian64($s)); - - return $d["h"] << 32 | $d["l"]; - } - - /** - * @return int - */ - public function consumeInt64(): int - { - $s = $this->consume(8); - - if (self::$native64BitPack) { - return \unpack("q", self::swapEndian64($s))[1]; - } - - $d = \unpack("Lh/Ll", self::swapHalvedEndian64($s)); - - return $d["h"] << 32 | $d["l"]; - } - - /** - * @param int $value - * - * @return static - */ - public function appendUint8(int $value): self - { - return $this->append(\pack("C", $value)); - } - - /** - * @param int $offset - * - * @return int - */ - public function readUint8(int $offset = 0): int - { - return \unpack("C", $this->read(1, $offset))[1]; - } - - /** - * @return int - */ - public function consumeUint8(): int - { - $r = \unpack("C", $this->data)[1]; - - $this->discard(1); - - return $r; - } - - /** - * @param int $value - * - * @return static - */ - public function appendUint16(int $value): self - { - return $this->append(\pack("n", $value)); - } - - /** - * @param int $offset - * - * @return int - */ - public function readUint16(int $offset = 0): int - { - return \unpack("n", $this->read(2, $offset))[1]; - } - - /** - * @return int - */ - public function consumeUint16(): int - { - $r = \unpack("n", $this->data)[1]; - - $this->discard(2); - - return $r; - } - - /** - * @param int $value - * - * @return static - */ - public function appendUint32(int $value): self - { - return $this->append(\pack("N", $value)); - } - - /** - * @param int $offset - * - * @return int - */ - public function readUint32(int $offset = 0): int - { - return \unpack("N", $this->read(4, $offset))[1]; - } - - /** - * @return int - */ - public function consumeUint32(): int - { - $r = unpack("N", $this->data)[1]; - - $this->discard(4); - - return $r; - } - - /** - * @param int $value - * - * @return static - */ - public function appendUint64(int $value): self - { - if (self::$native64BitPack) { - $s = self::swapEndian64(\pack("Q", $value)); - } else { - $s = \pack("LL", ($value & 0xffffffff00000000) >> 32, $value & 0x00000000ffffffff); - $s = self::swapHalvedEndian64($s); - } - - return $this->append($s); - } - - /** - * @param int $offset - * - * @return int - */ - public function readUint64(int $offset = 0): int - { - $s = $this->read(8, $offset); - - if (self::$native64BitPack) { - return \unpack("Q", self::swapEndian64($s))[1]; - } - - $d = \unpack("Lh/Ll", self::swapHalvedEndian64($s)); - - return $d["h"] << 32 | $d["l"]; - } - - /** - * @return int - */ - public function consumeUint64(): int - { - $s = $this->consume(8); - - if (self::$native64BitPack) { - return \unpack("Q", self::swapEndian64($s))[1]; - } - - $d = \unpack("Lh/Ll", self::swapHalvedEndian64($s)); - - return $d["h"] << 32 | $d["l"]; - } - - /** - * @param float $value - * - * @return static - */ - public function appendFloat(float $value): self - { - return $this->append(self::swapEndian32(\pack("f", $value))); - } - - /** - * @param int $offset - * - * @return float - */ - public function readFloat(int $offset = 0): float - { - return \unpack("f", self::swapEndian32($this->read(4, $offset)))[1]; - } - - /** - * @return float - */ - public function consumeFloat(): float - { - return \unpack("f", self::swapEndian32($this->consume(4)))[1]; - } - - /** - * @param float $value - * - * @return static - */ - public function appendDouble($value): self - { - return $this->append(self::swapEndian64(\pack("d", $value))); - } - - /** - * @param int $offset - * - * @return float - */ - public function readDouble(int $offset = 0): float - { - return \unpack("d", self::swapEndian64($this->read(8, $offset)))[1]; - } - - /** - * @return float - */ - public function consumeDouble(): float - { - return \unpack("d", self::swapEndian64($this->consume(8)))[1]; - } - - /** - * @return string - */ - public function __toString(): string - { - return $this->data; - } - - /** - * @param string $s - * - * @return string - */ - private static function swapEndian16(string $s): string - { - return self::$isLittleEndian ? $s[1] . $s[0] : $s; - } - - /** - * @param string $s - * - * @return string - */ - private static function swapEndian32(string $s): string - { - return self::$isLittleEndian ? $s[3] . $s[2] . $s[1] . $s[0] : $s; - } - - /** - * @param string $s - * - * @return string - */ - private static function swapEndian64(string $s): string - { - return self::$isLittleEndian ? $s[7] . $s[6] . $s[5] . $s[4] . $s[3] . $s[2] . $s[1] . $s[0] : $s; - } - - /** - * @param string $s - * - * @return string - */ - private static function swapHalvedEndian64(string $s): string - { - return self::$isLittleEndian ? $s[3] . $s[2] . $s[1] . $s[0] . $s[7] . $s[6] . $s[5] . $s[4] : $s; - } -} diff --git a/src/Exception/BufferException.php b/src/BufferOverflow.php similarity index 64% rename from src/Exception/BufferException.php rename to src/BufferOverflow.php index aa4f103..aa55b58 100644 --- a/src/Exception/BufferException.php +++ b/src/BufferOverflow.php @@ -10,8 +10,11 @@ declare(strict_types = 1); -namespace PHPinnacle\Buffer\Exception; +namespace PHPinnacle\Buffer; -abstract class BufferException extends \RuntimeException +if (!\class_exists('\PHPinnacle\Buffer\BufferOverflow')) { + final class BufferOverflow extends \Exception + { + } } diff --git a/src/ByteBuffer.php b/src/ByteBuffer.php new file mode 100644 index 0000000..1a5e4a9 --- /dev/null +++ b/src/ByteBuffer.php @@ -0,0 +1,539 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types = 1); + +namespace PHPinnacle\Buffer; + +if (!\class_exists('\PHPinnacle\Buffer\ByteBuffer')) +{ + class ByteBuffer + { + /** + * @var bool + */ + private static $isLittleEndian; + + /** + * @var string + */ + private $data; + + /** + * @var int + */ + private $size; + + /** + * @param string $buffer + */ + public function __construct(string $buffer = '') + { + $this->data = $buffer; + $this->size = \strlen($this->data); + + if (!isset(self::$isLittleEndian)) { + self::$isLittleEndian = \unpack("S", "\x01\x00")[1] === 1; + } + } + + /** + * @param string|static $value + * + * @return static + */ + public function append($value): self + { + if ($value instanceof self) { + $value = $value->data; + } + + if (!\is_string($value)) { + throw new \TypeError; + } + + $this->data .= $value; + $this->size += \strlen($value); + + return $this; + } + + /** + * @param int $n + * @param int $offset + * + * @return string + * @throws BufferOverflow + */ + public function read(int $n, int $offset = 0): string + { + if ($this->size < $offset + $n) { + throw new BufferOverflow; + } + + if ($offset === 0 && $this->size === $offset + $n) { + return $this->data; + } + + return \substr($this->data, $offset, $n); + } + + /** + * @param int $n + * + * @return string + * @throws BufferOverflow + */ + public function consume(int $n): string + { + if ($this->size < $n) { + throw new BufferOverflow; + } + + if ($this->size === $n) { + $buffer = $this->data; + + $this->data = ''; + $this->size = 0; + } else { + $buffer = \substr($this->data, 0, $n); + + $this->data = \substr($this->data, $n); + $this->size -= $n; + } + + return $buffer; + } + + /** + * @param int $n + * + * @return static + * @throws BufferOverflow + */ + public function discard(int $n): self + { + if ($this->size < $n) { + throw new BufferOverflow; + } + + if ($this->size === $n) { + $this->data = ''; + $this->size = 0; + } else { + $this->data = \substr($this->data, $n); + $this->size -= $n; + } + + return $this; + } + + /** + * @param int $n + * + * @return static + * @throws BufferOverflow + */ + public function slice(int $n): self + { + if ($this->size < $n) { + throw new BufferOverflow; + } + + return $this->size === $n ? new static($this->data) : new static(\substr($this->data, 0, $n)); + } + + /** + * @param int $n + * + * @return static + * @throws BufferOverflow + */ + public function shift(int $n): self + { + if ($this->size < $n) { + throw new BufferOverflow; + } + + if ($this->size === $n) { + $buffer = $this->data; + + $this->data = ''; + $this->size = 0; + } else { + $buffer = \substr($this->data, 0, $n); + + $this->data = \substr($this->data, $n); + $this->size -= $n; + } + + return new static($buffer); + } + + /** + * @return string + */ + public function flush(): string + { + $data = $this->data; + + $this->data = ''; + $this->size = 0; + + return $data; + } + + /** + * @return int + */ + public function size(): int + { + return $this->size; + } + + /** + * @return boolean + */ + public function empty(): bool + { + return $this->size === 0; + } + + /** + * @param int $value + * + * @return static + */ + public function appendInt8(int $value): self + { + return $this->append(\pack("c", $value)); + } + + /** + * @param int $offset + * + * @return int + */ + public function readInt8(int $offset = 0): int + { + return \unpack("c", $this->read(1, $offset))[1]; + } + + /** + * @return int + */ + public function consumeInt8(): int + { + return \unpack("c", $this->consume(1))[1]; + } + + /** + * @param int $value + * + * @return static + */ + public function appendInt16(int $value): self + { + return $this->append(self::swapEndian16(\pack("s", $value))); + } + + /** + * @param int $offset + * + * @return int + */ + public function readInt16(int $offset = 0): int + { + return \unpack("s", self::swapEndian16($this->read(2, $offset)))[1]; + } + + /** + * @return int + */ + public function consumeInt16(): int + { + return \unpack("s", self::swapEndian16($this->consume(2)))[1]; + } + + /** + * @param int $value + * + * @return static + */ + public function appendInt32(int $value): self + { + return $this->append(self::swapEndian32(\pack("l", $value))); + } + + /** + * @param int $offset + * + * @return int + */ + public function readInt32(int $offset = 0): int + { + return \unpack("l", self::swapEndian32($this->read(4, $offset)))[1]; + } + + /** + * @return int + */ + public function consumeInt32(): int + { + return \unpack("l", self::swapEndian32($this->consume(4)))[1]; + } + + /** + * @param int $value + * + * @return static + */ + public function appendInt64(int $value): self + { + return $this->append(self::swapEndian64(\pack("q", $value))); + } + + /** + * @param int $offset + * + * @return int + */ + public function readInt64(int $offset = 0): int + { + return \unpack("q", self::swapEndian64($this->read(8, $offset)))[1]; + } + + /** + * @return int + */ + public function consumeInt64(): int + { + return \unpack("q", self::swapEndian64($this->consume(8)))[1]; + } + + /** + * @param int $value + * + * @return static + */ + public function appendUint8(int $value): self + { + return $this->append(\pack("C", $value)); + } + + /** + * @param int $offset + * + * @return int + */ + public function readUint8(int $offset = 0): int + { + return \unpack("C", $this->read(1, $offset))[1]; + } + + /** + * @return int + */ + public function consumeUint8(): int + { + $r = \unpack("C", $this->data)[1]; + + $this->discard(1); + + return $r; + } + + /** + * @param int $value + * + * @return static + */ + public function appendUint16(int $value): self + { + return $this->append(\pack("n", $value)); + } + + /** + * @param int $offset + * + * @return int + */ + public function readUint16(int $offset = 0): int + { + return \unpack("n", $this->read(2, $offset))[1]; + } + + /** + * @return int + */ + public function consumeUint16(): int + { + $r = \unpack("n", $this->data)[1]; + + $this->discard(2); + + return $r; + } + + /** + * @param int $value + * + * @return static + */ + public function appendUint32(int $value): self + { + return $this->append(\pack("N", $value)); + } + + /** + * @param int $offset + * + * @return int + */ + public function readUint32(int $offset = 0): int + { + return \unpack("N", $this->read(4, $offset))[1]; + } + + /** + * @return int + */ + public function consumeUint32(): int + { + $r = \unpack("N", $this->data)[1]; + + $this->discard(4); + + return $r; + } + + /** + * @param int $value + * + * @return static + */ + public function appendUint64(int $value): self + { + return $this->append(self::swapEndian64(\pack("Q", $value))); + } + + /** + * @param int $offset + * + * @return int + */ + public function readUint64(int $offset = 0): int + { + return \unpack("Q", self::swapEndian64($this->read(8, $offset)))[1]; + } + + /** + * @return int + */ + public function consumeUint64(): int + { + return \unpack("Q", self::swapEndian64($this->consume(8)))[1]; + } + + /** + * @param float $value + * + * @return static + */ + public function appendFloat(float $value): self + { + return $this->append(self::swapEndian32(\pack("f", $value))); + } + + /** + * @param int $offset + * + * @return float + */ + public function readFloat(int $offset = 0): float + { + return \unpack("f", self::swapEndian32($this->read(4, $offset)))[1]; + } + + /** + * @return float + */ + public function consumeFloat(): float + { + return \unpack("f", self::swapEndian32($this->consume(4)))[1]; + } + + /** + * @param float $value + * + * @return static + */ + public function appendDouble($value): self + { + return $this->append(self::swapEndian64(\pack("d", $value))); + } + + /** + * @param int $offset + * + * @return float + */ + public function readDouble(int $offset = 0): float + { + return \unpack("d", self::swapEndian64($this->read(8, $offset)))[1]; + } + + /** + * @return float + */ + public function consumeDouble(): float + { + return \unpack("d", self::swapEndian64($this->consume(8)))[1]; + } + + /** + * @return string + */ + public function __toString(): string + { + return $this->data; + } + + /** + * @param string $s + * + * @return string + */ + private static function swapEndian16(string $s): string + { + return self::$isLittleEndian ? $s[1] . $s[0] : $s; + } + + /** + * @param string $s + * + * @return string + */ + private static function swapEndian32(string $s): string + { + return self::$isLittleEndian ? $s[3] . $s[2] . $s[1] . $s[0] : $s; + } + + /** + * @param string $s + * + * @return string + */ + private static function swapEndian64(string $s): string + { + return self::$isLittleEndian ? $s[7] . $s[6] . $s[5] . $s[4] . $s[3] . $s[2] . $s[1] . $s[0] : $s; + } + } +} diff --git a/src/Exception/BufferUnderflow.php b/src/Exception/BufferUnderflow.php deleted file mode 100644 index e538bef..0000000 --- a/src/Exception/BufferUnderflow.php +++ /dev/null @@ -1,17 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -declare(strict_types = 1); - -namespace PHPinnacle\Buffer\Exception; - -final class BufferUnderflow extends BufferException -{ -} diff --git a/tests/BinaryTest.php b/tests/BinaryTest.php deleted file mode 100644 index ae90d59..0000000 --- a/tests/BinaryTest.php +++ /dev/null @@ -1,459 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace PHPinnacle\Buffer\Tests; - -use PHPinnacle\Buffer\Binary as Buffer; -use PHPUnit\Framework\TestCase; - -class BinaryTest extends TestCase -{ - public function testSize() - { - $buffer = new Buffer; - $this->assertEquals(0, $buffer->size()); - - $buffer->append('a'); - $this->assertEquals(1, $buffer->size()); - - $buffer->append('a'); - $this->assertEquals(2, $buffer->size()); - - $buffer->read(1); - $this->assertEquals(2, $buffer->size()); - - $buffer->read(2); - $this->assertEquals(2, $buffer->size()); - - $buffer->consume(1); - $this->assertEquals(1, $buffer->size()); - - $buffer->consume(1); - $this->assertEquals(0, $buffer->size()); - } - - public function testEmpty() - { - $buffer = new Buffer; - $this->assertTrue($buffer->empty()); - - $buffer->append('a'); - $this->assertFalse($buffer->empty()); - } - - public function testFlush() - { - $buffer = new Buffer('abcd'); - - $this->assertEquals('abcd', $buffer->flush()); - $this->assertTrue($buffer->empty()); - } - - public function testToString() - { - $buffer = new Buffer('abcd'); - - $this->assertEquals('abcd', (string) $buffer); - $this->assertEquals('abcd', $buffer->__toString()); - } - - public function testRead() - { - $buffer = new Buffer('abcd'); - - $this->assertEquals('a', $buffer->read(1)); - $this->assertEquals(4, $buffer->size()); - - $this->assertEquals('ab', $buffer->read(2)); - $this->assertEquals(4, $buffer->size()); - - $this->assertEquals('abc', $buffer->read(3)); - $this->assertEquals(4, $buffer->size()); - - $this->assertEquals('abcd', $buffer->read(4)); - $this->assertEquals(4, $buffer->size()); - } - - public function testReadOffset() - { - $buffer = new Buffer('abcd'); - - $this->assertEquals('a', $buffer->read(1, 0)); - $this->assertEquals(4, $buffer->size()); - - $this->assertEquals('b', $buffer->read(1, 1)); - $this->assertEquals(4, $buffer->size()); - - $this->assertEquals('c', $buffer->read(1, 2)); - $this->assertEquals(4, $buffer->size()); - - $this->assertEquals('d', $buffer->read(1, 3)); - $this->assertEquals(4, $buffer->size()); - } - - /** - * @expectedException \PHPinnacle\Buffer\Exception\BufferUnderflow - */ - public function testReadThrows() - { - $buffer = new Buffer; - $buffer->read(1); - } - - public function testConsume() - { - $buffer = new Buffer('abcd'); - - $this->assertEquals('a', $buffer->consume(1)); - $this->assertEquals(3, $buffer->size()); - - $this->assertEquals('bc', $buffer->consume(2)); - $this->assertEquals(1, $buffer->size()); - - $this->assertEquals('d', $buffer->consume(1)); - $this->assertEquals(0, $buffer->size()); - } - - /** - * @expectedException \PHPinnacle\Buffer\Exception\BufferUnderflow - */ - public function testConsumeThrows() - { - $buffer = new Buffer; - $buffer->consume(1); - } - - public function testDiscard() - { - $buffer = new Buffer('abcd'); - - $buffer->discard(1); - $this->assertEquals('bcd', $buffer->read($buffer->size())); - $this->assertEquals(3, $buffer->size()); - - $buffer->discard(2); - $this->assertEquals('d', $buffer->read($buffer->size())); - $this->assertEquals(1, $buffer->size()); - - $buffer->discard(1); - $this->assertEquals(0, $buffer->size()); - $this->assertTrue($buffer->empty()); - } - - /** - * @expectedException \PHPinnacle\Buffer\Exception\BufferUnderflow - */ - public function testDiscardThrows() - { - $buffer = new Buffer; - $buffer->discard(1); - } - - public function testSlice() - { - $buffer = new Buffer('abcd'); - - $slice1 = $buffer->slice(1); - $this->assertEquals('a', $slice1->read($slice1->size())); - $this->assertEquals(4, $buffer->size()); - - $slice2 = $buffer->slice(2); - $this->assertEquals('ab', $slice2->read($slice2->size())); - $this->assertEquals(4, $buffer->size()); - - $slice3 = $buffer->slice(3); - $this->assertEquals('abc', $slice3->read($slice3->size())); - $this->assertEquals(4, $buffer->size()); - - $slice4 = $buffer->slice(4); - $this->assertEquals('abcd', $slice4->read($slice4->size())); - $this->assertEquals(4, $buffer->size()); - } - - /** - * @expectedException \PHPinnacle\Buffer\Exception\BufferUnderflow - */ - public function testSliceThrows() - { - $buffer = new Buffer; - $buffer->slice(1); - } - - public function testShift() - { - $buffer = new Buffer('abcdef'); - - $slice1 = $buffer->shift(1); - $this->assertEquals('a', $slice1->read($slice1->size())); - $this->assertEquals(5, $buffer->size()); - - $slice2 = $buffer->shift(2); - $this->assertEquals('bc', $slice2->read($slice2->size())); - $this->assertEquals(3, $buffer->size()); - - $slice3 = $buffer->shift(3); - $this->assertEquals('def', $slice3->read($slice3->size())); - $this->assertEquals(0, $buffer->size()); - } - - /** - * @expectedException \PHPinnacle\Buffer\Exception\BufferUnderflow - */ - public function testShiftThrows() - { - $buffer = new Buffer; - $buffer->shift(1); - } - - public function testAppend() - { - $buffer = new Buffer; - $this->assertEquals(0, $buffer->size()); - - $buffer->append('abcd'); - $this->assertEquals(4, $buffer->size()); - $this->assertEquals('abcd', $buffer->read(4)); - - $buffer->append('efgh'); - $this->assertEquals(8, $buffer->size()); - $this->assertEquals('abcdefgh', $buffer->read(8)); - } - - public function testAppendBuffer() - { - $buffer = new Buffer; - $this->assertEquals(0, $buffer->size()); - - $buffer->append(new Buffer('ab')); - $this->assertEquals(2, $buffer->size()); - $this->assertEquals('ab', $buffer->read(2)); - - $buffer->append('cd'); - $this->assertEquals(4, $buffer->size()); - $this->assertEquals('abcd', $buffer->read(4)); - - $buffer->append(new Buffer('ef')); - $this->assertEquals(6, $buffer->size()); - $this->assertEquals('abcdef', $buffer->read(6)); - } - - // 8-bit integer functions - - public function testReadUint8() - { - $this->assertEquals(0xA9, (new Buffer("\xA9"))->readUint8()); - } - - public function testReadInt8() - { - $this->assertEquals(0xA9 - 0x100, (new Buffer("\xA9"))->readInt8()); - } - - public function testConsumeUint8() - { - $this->assertEquals(0xA9, (new Buffer("\xA9"))->consumeUint8()); - } - - public function testConsumeInt8() - { - $this->assertEquals(0xA9 - 0x100, (new Buffer("\xA9"))->consumeInt8()); - } - - public function testAppendUint8() - { - $this->assertEquals("\xA9", (new Buffer)->appendUint8(0xA9)->read(1)); - } - - public function testAppendInt8() - { - $this->assertEquals("\xA9", (new Buffer)->appendInt8(0xA9 - 0x100)->read(1)); - } - - // 16-bit integer functions - - public function testReadUint16() - { - $this->assertEquals(0xA978, (new Buffer("\xA9\x78"))->readUint16()); - } - - public function testReadInt16() - { - $this->assertEquals(0xA978 - 0x10000, (new Buffer("\xA9\x78"))->readInt16()); - } - - public function testConsumeUint16() - { - $this->assertEquals(0xA978, (new Buffer("\xA9\x78"))->consumeUint16()); - } - - public function testConsumeInt16() - { - $this->assertEquals(0xA978 - 0x10000, (new Buffer("\xA9\x78"))->consumeInt16()); - } - - public function testAppendUint16() - { - $this->assertEquals("\xA9\x78", (new Buffer)->appendUint16(0xA978)->read(2)); - } - - public function testAppendInt16() - { - $this->assertEquals("\xA9\x78", (new Buffer)->appendInt16(0xA978)->read(2)); - } - - // 32-bit integer functions - - public function testReadUint32() - { - $this->assertEquals(0xA9782361, (new Buffer("\xA9\x78\x23\x61"))->readUint32()); - } - - public function testReadInt32() - { - $this->assertEquals(0xA9782361 - 0x100000000, (new Buffer("\xA9\x78\x23\x61"))->readInt32()); - } - - public function testConsumeUint32() - { - $this->assertEquals(0xA9782361, (new Buffer("\xA9\x78\x23\x61"))->consumeUint32()); - } - - public function testConsumeInt32() - { - $this->assertEquals(0xA9782361 - 0x100000000, (new Buffer("\xA9\x78\x23\x61"))->consumeInt32()); - } - - public function testAppendUint32() - { - $this->assertEquals("\xA9\x78\x23\x61", (new Buffer)->appendUint32(0xA9782361)->read(4)); - } - - public function testAppendInt32() - { - $this->assertEquals("\xA9\x78\x23\x61", (new Buffer)->appendInt32(0xA9782361)->read(4)); - } - - // 64-bit integer functions - - public function testReadUint64() - { - $this->assertEquals(0x1978236134738525, (new Buffer("\x19\x78\x23\x61\x34\x73\x85\x25"))->readUint64()); - } - - public function testReadInt64() - { - $this->assertEquals(-2, (new Buffer("\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFE"))->readInt64()); - } - - public function testConsumeUint64() - { - $this->assertEquals(0x1978236134738525, (new Buffer("\x19\x78\x23\x61\x34\x73\x85\x25"))->consumeUint64()); - } - - public function testConsumeInt64() - { - $this->assertEquals(-2, (new Buffer("\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFE"))->consumeInt64()); - } - - public function testAppendUint64() - { - $this->assertEquals("\x19\x78\x23\x61\x34\x73\x85\x25", (new Buffer)->appendUint64(0x1978236134738525)->read(8)); - } - - public function testAppendInt64() - { - $this->assertEquals("\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFE", (new Buffer)->appendInt64(-2)->read(8)); - } - - // 64-bit integer functions - - public function testNotNativeReadUint64() - { - self::enableNotNative64Int(); - - $this->assertEquals(0x1978236134738525, (new Buffer("\x19\x78\x23\x61\x34\x73\x85\x25"))->readUint64()); - } - - public function testNotNativeReadInt64() - { - self::enableNotNative64Int(); - - $this->assertEquals(-2, (new Buffer("\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFE"))->readInt64()); - } - - public function testNotNativeConsumeUint64() - { - self::enableNotNative64Int(); - - $this->assertEquals(0x1978236134738525, (new Buffer("\x19\x78\x23\x61\x34\x73\x85\x25"))->consumeUint64()); - } - - public function testNotNativeConsumeInt64() - { - self::enableNotNative64Int(); - - $this->assertEquals(-2, (new Buffer("\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFE"))->consumeInt64()); - } - - public function testNotNativeAppendUint64() - { - self::enableNotNative64Int(); - - $this->assertEquals("\x19\x78\x23\x61\x34\x73\x85\x25", (new Buffer)->appendUint64(0x1978236134738525)->read(8)); - } - - public function testNotNativeAppendInt64() - { - self::enableNotNative64Int(); - - $this->assertEquals("\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFE", (new Buffer)->appendInt64(-2)->read(8)); - } - - // float - - public function testReadFloat() - { - $this->assertEquals(1.5, (new Buffer("\x3F\xC0\x00\x00"))->readFloat()); - } - - public function testConsumeFloat() - { - $this->assertEquals(1.5, (new Buffer("\x3F\xC0\x00\x00"))->consumeFloat()); - } - - public function testAppendFloat() - { - $this->assertEquals("\x3F\xC0\x00\x00", (new Buffer)->appendFloat(1.5)->read(4)); - } - - // double - - public function testReadDouble() - { - $this->assertEquals(1.5, (new Buffer("\x3F\xF8\x00\x00\x00\x00\x00\x00"))->readDouble()); - } - - public function testConsumeDouble() - { - $this->assertEquals(1.5, (new Buffer("\x3F\xF8\x00\x00\x00\x00\x00\x00"))->consumeDouble()); - } - - public function testAppendDouble() - { - $this->assertEquals("\x3F\xF8\x00\x00\x00\x00\x00\x00", (new Buffer)->appendDouble(1.5)->read(8)); - } - - private static function enableNotNative64Int() - { - $property = new \ReflectionProperty(Buffer::class, 'native64BitPack'); - $property->setAccessible(true); - $property->setValue(false); - } -} diff --git a/tests/ByteBufferTest.php b/tests/ByteBufferTest.php new file mode 100644 index 0000000..2c5004e --- /dev/null +++ b/tests/ByteBufferTest.php @@ -0,0 +1,418 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace PHPinnacle\Buffer\Tests; + +use PHPinnacle\Buffer\ByteBuffer as Buffer; +use PHPinnacle\Buffer\BufferOverflow; +use PHPUnit\Framework\TestCase; + +class ByteBufferTest extends TestCase +{ + public function testSize() + { + $buffer = new Buffer(); + self::assertEquals(0, $buffer->size()); + + $buffer->append('a'); + self::assertEquals(1, $buffer->size()); + + $buffer->append('a'); + self::assertEquals(2, $buffer->size()); + + $buffer->read(1); + self::assertEquals(2, $buffer->size()); + + $buffer->read(2); + self::assertEquals(2, $buffer->size()); + + $buffer->consume(1); + self::assertEquals(1, $buffer->size()); + + $buffer->consume(1); + self::assertEquals(0, $buffer->size()); + } + + public function testEmpty() + { + $buffer = new Buffer; + self::assertTrue($buffer->empty()); + + $buffer->append('a'); + self::assertFalse($buffer->empty()); + } + + public function testFlush() + { + $buffer = new Buffer('abcd'); + + self::assertEquals('abcd', $buffer->flush()); + self::assertTrue($buffer->empty()); + } + + public function testToString() + { + $buffer = new Buffer('abcd'); + + self::assertEquals('abcd', (string) $buffer); + self::assertEquals('abcd', $buffer->__toString()); + } + + public function testRead() + { + $buffer = new Buffer('abcd'); + + self::assertEquals('a', $buffer->read(1)); + self::assertEquals(4, $buffer->size()); + + self::assertEquals('ab', $buffer->read(2)); + self::assertEquals(4, $buffer->size()); + + self::assertEquals('abc', $buffer->read(3)); + self::assertEquals(4, $buffer->size()); + + self::assertEquals('abcd', $buffer->read(4)); + self::assertEquals(4, $buffer->size()); + } + + public function testReadOffset() + { + $buffer = new Buffer('abcd'); + + self::assertEquals('a', $buffer->read(1, 0)); + self::assertEquals(4, $buffer->size()); + + self::assertEquals('b', $buffer->read(1, 1)); + self::assertEquals(4, $buffer->size()); + + self::assertEquals('c', $buffer->read(1, 2)); + self::assertEquals(4, $buffer->size()); + + self::assertEquals('d', $buffer->read(1, 3)); + self::assertEquals(4, $buffer->size()); + } + + public function testReadThrows() + { + self::expectException(BufferOverflow::class); + + $buffer = new Buffer; + $buffer->read(1); + } + + public function testConsume() + { + $buffer = new Buffer('abcd'); + + self::assertEquals('a', $buffer->consume(1)); + self::assertEquals(3, $buffer->size()); + + self::assertEquals('bc', $buffer->consume(2)); + self::assertEquals(1, $buffer->size()); + + self::assertEquals('d', $buffer->consume(1)); + self::assertEquals(0, $buffer->size()); + } + + public function testConsumeThrows() + { + self::expectException(BufferOverflow::class); + + $buffer = new Buffer; + $buffer->consume(1); + } + + public function testDiscard() + { + $buffer = new Buffer('abcd'); + + $buffer->discard(1); + self::assertEquals('bcd', $buffer->read($buffer->size())); + self::assertEquals(3, $buffer->size()); + + $buffer->discard(2); + self::assertEquals('d', $buffer->read($buffer->size())); + self::assertEquals(1, $buffer->size()); + + $buffer->discard(1); + self::assertEquals(0, $buffer->size()); + self::assertTrue($buffer->empty()); + } + + public function testDiscardThrows() + { + self::expectException(BufferOverflow::class); + + $buffer = new Buffer; + $buffer->discard(1); + } + + public function testSlice() + { + $buffer = new Buffer('abcd'); + + $slice1 = $buffer->slice(1); + self::assertEquals('a', $slice1->read($slice1->size())); + self::assertEquals(4, $buffer->size()); + + $slice2 = $buffer->slice(2); + self::assertEquals('ab', $slice2->read($slice2->size())); + self::assertEquals(4, $buffer->size()); + + $slice3 = $buffer->slice(3); + self::assertEquals('abc', $slice3->read($slice3->size())); + self::assertEquals(4, $buffer->size()); + + $slice4 = $buffer->slice(4); + self::assertEquals('abcd', $slice4->read($slice4->size())); + self::assertEquals(4, $buffer->size()); + } + + public function testSliceThrows() + { + self::expectException(BufferOverflow::class); + + $buffer = new Buffer; + $buffer->slice(1); + } + + public function testShift() + { + $buffer = new Buffer('abcdef'); + + $slice1 = $buffer->shift(1); + self::assertEquals('a', $slice1->read($slice1->size())); + self::assertEquals(5, $buffer->size()); + + $slice2 = $buffer->shift(2); + self::assertEquals('bc', $slice2->read($slice2->size())); + self::assertEquals(3, $buffer->size()); + + $slice3 = $buffer->shift(3); + self::assertEquals('def', $slice3->read($slice3->size())); + self::assertEquals(0, $buffer->size()); + } + + public function testShiftThrows() + { + self::expectException(BufferOverflow::class); + + $buffer = new Buffer; + $buffer->shift(1); + } + + public function testAppend() + { + $buffer = new Buffer; + self::assertEquals(0, $buffer->size()); + + $buffer->append('abcd'); + self::assertEquals(4, $buffer->size()); + self::assertEquals('abcd', $buffer->read(4)); + + $buffer->append('efgh'); + self::assertEquals(8, $buffer->size()); + self::assertEquals('abcdefgh', $buffer->read(8)); + } + + public function testAppendBuffer() + { + $buffer = new Buffer; + self::assertEquals(0, $buffer->size()); + + $buffer->append(new Buffer('ab')); + self::assertEquals(2, $buffer->size()); + self::assertEquals('ab', $buffer->read(2)); + + $buffer->append('cd'); + self::assertEquals(4, $buffer->size()); + self::assertEquals('abcd', $buffer->read(4)); + + $buffer->append(new Buffer('ef')); + self::assertEquals(6, $buffer->size()); + self::assertEquals('abcdef', $buffer->read(6)); + } + + public function testAppendThrows() + { + self::expectException(\TypeError::class); + + $buffer = new Buffer; + $buffer->append(new \stdClass); + } + + // // 8-bit integer functions + + public function testReadUint8() + { + self::assertEquals(0xA9, (new Buffer("\xA9"))->readUint8()); + } + + public function testReadInt8() + { + self::assertEquals(0xA9 - 0x100, (new Buffer("\xA9"))->readInt8()); + } + + public function testConsumeUint8() + { + self::assertEquals(0xA9, (new Buffer("\xA9"))->consumeUint8()); + } + + public function testConsumeInt8() + { + self::assertEquals(0xA9 - 0x100, (new Buffer("\xA9"))->consumeInt8()); + } + + public function testAppendUint8() + { + self::assertEquals("\xA9", (new Buffer)->appendUint8(0xA9)->read(1)); + } + + public function testAppendInt8() + { + self::assertEquals("\xA9", (new Buffer)->appendInt8(0xA9 - 0x100)->read(1)); + } + + // 16-bit integer functions + + public function testReadUint16() + { + self::assertEquals(0xA978, (new Buffer("\xA9\x78"))->readUint16()); + } + + public function testReadInt16() + { + self::assertEquals(0xA978 - 0x10000, (new Buffer("\xA9\x78"))->readInt16()); + } + + public function testConsumeUint16() + { + self::assertEquals(0xA978, (new Buffer("\xA9\x78"))->consumeUint16()); + } + + public function testConsumeInt16() + { + self::assertEquals(0xA978 - 0x10000, (new Buffer("\xA9\x78"))->consumeInt16()); + } + + public function testAppendUint16() + { + self::assertEquals("\xA9\x78", (new Buffer)->appendUint16(0xA978)->read(2)); + } + + public function testAppendInt16() + { + self::assertEquals("\xA9\x78", (new Buffer)->appendInt16(0xA978)->read(2)); + } + + // 32-bit integer functions + + public function testReadUint32() + { + self::assertEquals(0xA9782361, (new Buffer("\xA9\x78\x23\x61"))->readUint32()); + } + + public function testReadInt32() + { + self::assertEquals(0xA9782361 - 0x100000000, (new Buffer("\xA9\x78\x23\x61"))->readInt32()); + } + + public function testConsumeUint32() + { + self::assertEquals(0xA9782361, (new Buffer("\xA9\x78\x23\x61"))->consumeUint32()); + } + + public function testConsumeInt32() + { + self::assertEquals(0xA9782361 - 0x100000000, (new Buffer("\xA9\x78\x23\x61"))->consumeInt32()); + } + + public function testAppendUint32() + { + self::assertEquals("\xA9\x78\x23\x61", (new Buffer)->appendUint32(0xA9782361)->read(4)); + } + + public function testAppendInt32() + { + self::assertEquals("\xA9\x78\x23\x61", (new Buffer)->appendInt32(0xA9782361)->read(4)); + } + + // 64-bit integer functions + + public function testReadUint64() + { + self::assertEquals(0x1978236134738525, (new Buffer("\x19\x78\x23\x61\x34\x73\x85\x25"))->readUint64()); + } + + public function testReadInt64() + { + self::assertEquals(-2, (new Buffer("\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFE"))->readInt64()); + } + + public function testConsumeUint64() + { + self::assertEquals(0x1978236134738525, (new Buffer("\x19\x78\x23\x61\x34\x73\x85\x25"))->consumeUint64()); + } + + public function testConsumeInt64() + { + self::assertEquals(-2, (new Buffer("\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFE"))->consumeInt64()); + } + + public function testAppendUint64() + { + self::assertEquals("\x19\x78\x23\x61\x34\x73\x85\x25", (new Buffer)->appendUint64(0x1978236134738525)->read(8)); + } + + public function testAppendInt64() + { + self::assertEquals("\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFE", (new Buffer)->appendInt64(-2)->read(8)); + } + + // Float + + public function testReadFloat() + { + $buffer = new Buffer("\x3F\xC0\x00\x00"); + + self::assertEquals(4, $buffer->size()); + self::assertEquals(1.5, $buffer->readFloat()); + } + + public function testConsumeFloat() + { + self::assertEquals(1.5, (new Buffer("\x3F\xC0\x00\x00"))->consumeFloat()); + } + + public function testAppendFloat() + { + self::assertEquals("\x3F\xC0\x00\x00", (new Buffer)->appendFloat(1.5)->read(4)); + } + + // Double + + public function testReadDouble() + { + $buffer = new Buffer("\x3F\xF8\x00\x00\x00\x00\x00\x00"); + + self::assertEquals(8, $buffer->size()); + self::assertEquals(1.5, $buffer->readDouble()); + } + + public function testConsumeDouble() + { + self::assertEquals(1.5, (new Buffer("\x3F\xF8\x00\x00\x00\x00\x00\x00"))->consumeDouble()); + } + + public function testAppendDouble() + { + self::assertEquals("\x3F\xF8\x00\x00\x00\x00\x00\x00", (new Buffer)->appendDouble(1.5)->read(8)); + } +}