<?php
namespace BitWasp\Buffertools;
use Mdanter\Ecc\EccFactory;
use Mdanter\Ecc\Math\GmpMathInterface;
class Buffer implements BufferInterface
{
/**
* @var int
*/
protected $size;
/**
* @var string
*/
protected $buffer;
/**
* @var GmpMathInterface
*/
protected $math;
/**
* @param string $byteString
* @param null|integer $byteSize
* @param GmpMathInterface $math
* @throws \Exception
*/
public function __construct($byteString = '', $byteSize = null, GmpMathInterface $math = null)
{
$this->math = $math ?: EccFactory::getAdapter();
if ($byteSize !== null) {
// Check the integer doesn't overflow its supposed size
if (strlen($byteString) > $byteSize) {
throw new \Exception('Byte string exceeds maximum size');
}
} else {
$byteSize = strlen($byteString);
}
$this->size = $byteSize;
$this->buffer = $byteString;
}
/**
* Return a formatted version for var_dump
*/
public function __debugInfo()
{
return [
'size' => $this->size,
'buffer' => '0x' . unpack("H*", $this->buffer)[1],
];
}
/**
* Create a new buffer from a hex string
*
* @param string $hexString
* @param integer $byteSize
* @param GmpMathInterface $math
* @return Buffer
* @throws \Exception
*/
public static function hex($hexString = '', $byteSize = null, GmpMathInterface $math = null)
{
if (strlen($hexString) > 0 && !ctype_xdigit($hexString)) {
throw new \InvalidArgumentException('Buffer::hex: non-hex character passed');
}
$math = $math ?: EccFactory::getAdapter();
$binary = pack("H*", $hexString);
return new self($binary, $byteSize, $math);
}
/**
* @param int|string $integer
* @param null|int $byteSize
* @param GmpMathInterface|null $math
* @return Buffer
*/
public static function int($integer, $byteSize = null, GmpMathInterface $math = null)
{
if ($integer < 0) {
throw new \InvalidArgumentException('Negative integers not supported by Buffer::int. This could be an application error, or you should be using templates.');
}
$math = $math ?: EccFactory::getAdapter();
$binary = pack("H*", $math->decHex($integer));
return new self($binary, $byteSize, $math);
}
/**
* @param integer $start
* @param integer|null $end
* @return Buffer
* @throws \Exception
*/
public function slice($start, $end = null)
{
if ($start > $this->getSize()) {
throw new \Exception('Start exceeds buffer length');
}
if ($end === null) {
return new self(substr($this->getBinary(), $start));
}
if ($end > $this->getSize()) {
throw new \Exception('Length exceeds buffer length');
}
$string = substr($this->getBinary(), $start, $end);
if (!is_string($string)) {
throw new \RuntimeException('Failed to slice string of with requested start/end');
}
$length = strlen($string);
return new self($string, $length, $this->math);
}
/**
* Get the size of the buffer to be returned
*
* @return int
*/
public function getSize()
{
return $this->size;
}
/**
* Get the size of the value stored in the buffer
*
* @return int
*/
public function getInternalSize()
{
return strlen($this->buffer);
}
/**
* @return string
*/
public function getBinary()
{
// if a size is specified we'll make sure the value returned is that size
if ($this->size !== null) {
if (strlen($this->buffer) < $this->size) {
return str_pad($this->buffer, $this->size, chr(0), STR_PAD_LEFT);
} elseif (strlen($this->buffer) > $this->size) {
return substr($this->buffer, 0, $this->size);
}
}
return $this->buffer;
}
/**
* @return string
*/
public function getHex()
{
return unpack("H*", $this->getBinary())[1];
}
/**
* @return \GMP
*/
public function getGmp()
{
$gmp = gmp_init($this->getHex(), 16);
return $gmp;
}
/**
* @return int|string
*/
public function getInt()
{
return gmp_strval($this->getGmp(), 10);
}
/**
* @return BufferInterface
*/
public function flip()
{
return Buffertools::flipBytes($this);
}
/**
* @param BufferInterface $other
* @return bool
*/
public function equals(BufferInterface $other)
{
return ($other->getSize() === $this->getSize()
&& $other->getBinary() === $this->getBinary());
}
}