You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
213 lines
4.9 KiB
PHP
213 lines
4.9 KiB
PHP
<?php
|
|
|
|
|
|
namespace Cijber\OpenSSL;
|
|
|
|
use ArrayAccess;
|
|
use Cijber\OpenSSL;
|
|
use Cijber\OpenSSL\C\CBackedObject;
|
|
use Cijber\OpenSSL\C\CBackedObjectWithOwner;
|
|
use Countable;
|
|
use FFI;
|
|
use FFI\CData;
|
|
use InvalidArgumentException;
|
|
use IteratorAggregate;
|
|
use RuntimeException;
|
|
use Traversable;
|
|
|
|
/**
|
|
* Class Stack
|
|
* @package Cijber\OpenSSL
|
|
*/
|
|
abstract class Stack extends CBackedObjectWithOwner implements Countable, ArrayAccess, Traversable, IteratorAggregate
|
|
{
|
|
const CLASSNAME = CBackedObjectWithOwner::class;
|
|
const TYPE = "struct stack_st*";
|
|
|
|
abstract protected function spawn(CData $cData);
|
|
|
|
protected $owner;
|
|
|
|
protected function __construct(FFI $ffi, CData $cObj, $owner = null)
|
|
{
|
|
parent::__construct($ffi, $ffi->cast(self::TYPE, $cObj));
|
|
$this->owner = $owner;
|
|
}
|
|
|
|
public static function new()
|
|
{
|
|
$ffi = OpenSSL::getFFI();
|
|
$cObj = $ffi->sk_new_null();
|
|
return new static($ffi, $cObj);
|
|
}
|
|
|
|
protected function freeObject()
|
|
{
|
|
$this->ffi->sk_free($this->cObj);
|
|
}
|
|
|
|
public function count(): int
|
|
{
|
|
return $this->ffi->sk_num($this->cObj);
|
|
}
|
|
|
|
public function get(int $offset)
|
|
{
|
|
$res = $this->ffi->sk_value($this->cObj, $offset);
|
|
|
|
if ($res === null) {
|
|
throw new RuntimeException("Failed to retrieve item from stack");
|
|
}
|
|
|
|
return $this->spawn($res);
|
|
}
|
|
|
|
public function set(int $offset, CBackedObject $object): void
|
|
{
|
|
/***
|
|
* Make sure ref count is updated for old object
|
|
*/
|
|
/** @var CBackedObjectWithOwner $obj */
|
|
$obj = $this->get($offset);
|
|
$res = $this->ffi->sk_set($this->cObj, $offset, $object->cObj);
|
|
|
|
if ($res === null) {
|
|
throw new RuntimeException("Failed to set item on stack");
|
|
}
|
|
|
|
$obj->decreaseRefCount();
|
|
}
|
|
|
|
public function shift()
|
|
{
|
|
$cObj = $this->ffi->sk_shift($this->cObj);
|
|
return $this->handleResult($cObj);
|
|
}
|
|
|
|
protected function handleResult(?CData $cObj)
|
|
{
|
|
if ($cObj === null) {
|
|
return null;
|
|
}
|
|
|
|
/** @var CBackedObjectWithOwner $obj */
|
|
$obj = $this->spawn($cObj);
|
|
$obj->decreaseRefCount();
|
|
return $obj;
|
|
}
|
|
|
|
public function pop()
|
|
{
|
|
$cObj = $this->ffi->sk_pop($this->cObj);
|
|
return $this->handleResult($cObj);
|
|
}
|
|
|
|
public function push(CBackedObject $object): int
|
|
{
|
|
$this->ensureCorrect($object);
|
|
$idx = $this->ffi->sk_push($this->cObj, $object->cObj);
|
|
if ($idx === 0) {
|
|
throw new RuntimeException("Failed to insert element");
|
|
}
|
|
|
|
return $idx;
|
|
}
|
|
|
|
private function ensureCorrect($object)
|
|
{
|
|
$expectedClassName = static::CLASSNAME;
|
|
|
|
if (!($object instanceof $expectedClassName)) {
|
|
throw new InvalidArgumentException("Expected object of class $expectedClassName got object of class " . get_class($object));
|
|
}
|
|
}
|
|
|
|
public function unshift(CBackedObject $object): int
|
|
{
|
|
$this->ensureCorrect($object);
|
|
|
|
$idx = $this->ffi->sk_unshift($this->cObj, $object->cObj);
|
|
if ($idx === 0) {
|
|
throw new RuntimeException("Failed to insert element");
|
|
}
|
|
|
|
$object->pushRefCount();
|
|
|
|
return $idx;
|
|
}
|
|
|
|
public function freeAll()
|
|
{
|
|
$this->ffi->sk_pop_free($this->cObj, function (CData $cObj) {
|
|
/** @var CBackedObject $cObj */
|
|
$cObj = $this->spawn($cObj);
|
|
$cObj->decreaseRefCount();
|
|
$cObj->free();
|
|
});
|
|
|
|
$this->freed();
|
|
}
|
|
|
|
public function offsetExists($offset)
|
|
{
|
|
return $offset >= 0 && $offset < $this->count();
|
|
}
|
|
|
|
public function offsetGet($offset)
|
|
{
|
|
return $this->get($offset);
|
|
}
|
|
|
|
public function offsetSet($offset, $value)
|
|
{
|
|
if ($offset === null) {
|
|
$this->push($value);
|
|
return;
|
|
}
|
|
|
|
$this->set($offset, $value);
|
|
}
|
|
|
|
public function offsetUnset($offset)
|
|
{
|
|
return $this->delete($offset);
|
|
}
|
|
|
|
public function delete($offset)
|
|
{
|
|
$obj = $this->ffi->sk_delete($this->cObj, $offset);
|
|
if ($obj === null) {
|
|
throw new RuntimeException("Failed to delete element $offset from stack");
|
|
}
|
|
|
|
/** @var CBackedObjectWithOwner $phpObj */
|
|
$phpObj = $this->spawn($obj);
|
|
$phpObj->decreaseRefCount();
|
|
|
|
return $phpObj;
|
|
}
|
|
|
|
public function getIterator()
|
|
{
|
|
for ($i = 0; $i < $this->count(); $i++) {
|
|
yield $this[$i];
|
|
}
|
|
}
|
|
|
|
public function __clone()
|
|
{
|
|
$cObj = $this->ffi->sk_dup($this->cObj);
|
|
|
|
if ($cObj === null) {
|
|
throw new RuntimeException("Failed to clone stack");
|
|
}
|
|
|
|
/** @var CBackedObjectWithOwner $obj */
|
|
foreach ($this as $obj) {
|
|
$obj->pushRefCount();
|
|
}
|
|
|
|
return new static($this->ffi, $cObj);
|
|
}
|
|
}
|