<?php
 declare(strict_types=1); namespace SpomkyLabs\Pki\ASN1\Type\Constructed; use SpomkyLabs\Pki\ASN1\Component\Identifier; use SpomkyLabs\Pki\ASN1\Component\Length; use SpomkyLabs\Pki\ASN1\Element; use SpomkyLabs\Pki\ASN1\Exception\DecodeException; use SpomkyLabs\Pki\ASN1\Feature\ElementBase; use SpomkyLabs\Pki\ASN1\Type\Structure; final class Set extends Structure { public static function create(Element ...$elements): self { return new self(self::TYPE_SET, ...$elements); } public function sortedSet(): self { $obj = clone $this; usort( $obj->elements, function (Element $a, Element $b) { if ($a->typeClass() !== $b->typeClass()) { return $a->typeClass() < $b->typeClass() ? -1 : 1; } return $a->tag() <=> $b->tag(); } ); return $obj; } public function sortedSetOf(): self { $obj = clone $this; usort( $obj->elements, function (Element $a, Element $b) { $a_der = $a->toDER(); $b_der = $b->toDER(); return strcmp($a_der, $b_der); } ); return $obj; } protected static function decodeFromDER(Identifier $identifier, string $data, int &$offset): ElementBase { if (! $identifier->isConstructed()) { throw new DecodeException('Structured element must have constructed bit set.'); } $idx = $offset; $length = Length::expectFromDER($data, $idx); if ($length->isIndefinite()) { $type = self::decodeIndefiniteLength($data, $idx); } else { $type = self::decodeDefiniteLength($data, $idx, $length->intLength()); } $offset = $idx; return $type; } protected static function decodeDefiniteLength(string $data, int &$offset, int $length): ElementBase { $idx = $offset; $end = $idx + $length; $elements = []; while ($idx < $end) { $elements[] = Element::fromDER($data, $idx); if ($idx > $end) { throw new DecodeException("Structure's content overflows length."); } } $offset = $idx; return self::create(...$elements); } protected static function decodeIndefiniteLength(string $data, int &$offset): ElementBase { $idx = $offset; $elements = []; $end = mb_strlen($data, '8bit'); while (true) { if ($idx >= $end) { throw new DecodeException('Unexpected end of data while decoding indefinite length structure.'); } $el = Element::fromDER($data, $idx); if ($el->isType(self::TYPE_EOC)) { break; } $elements[] = $el; } $offset = $idx; $type = self::create(...$elements); $type->indefiniteLength = true; return $type; } } 