<?php declare(strict_types=1); /** * @author Pavel Djundik * * @see https://xpaw.me * @see https://github.com/xPaw/PHP-Source-Query * * @license GNU Lesser General Public License, version 2.1 * * @internal */ namespace xPaw\SourceQuery; use xPaw\SourceQuery\Exception\InvalidPacketException; final class Buffer { /** * Buffer. */ private string $buffer = ''; /** * Buffer length. */ private int $length = 0; /** * Current position in buffer. */ private int $position = 0; /** * Sets buffer. */ public function set(string $buffer): void { $this->buffer = $buffer; $this->length = strlen($buffer); $this->position = 0; } /** * Get remaining bytes. * * @return int Remaining bytes in buffer */ public function remaining(): int { return $this->length - $this->position; } public function isEmpty(): bool { return $this->remaining() <= 0; } /** * Gets data from buffer. * * @param int $length Bytes to read */ public function get(int $length = -1): string { if (0 === $length) { return ''; } $remaining = $this->remaining(); if (-1 === $length) { $length = $remaining; } elseif ($length > $remaining) { return ''; } $data = substr($this->buffer, $this->position, $length); $this->position += $length; return $data; } /** * Get byte from buffer. */ public function getByte(): int { return ord($this->get(1)); } /** * Get byte (boolean) from buffer. */ public function getBool(): bool { return 1 === $this->getByte(); } /** * Get byte (character) from buffer. */ public function getChar(): string { return chr($this->getByte()); } /** * Get short from buffer. * * @throws InvalidPacketException */ public function getShort(): int { if ($this->remaining() < 2) { throw new InvalidPacketException('Not enough data to unpack a short.', InvalidPacketException::BUFFER_EMPTY); } $data = unpack('v', $this->get(2)); if (!$data) { throw new InvalidPacketException('Empty data from packet.'); } return (int) $data[1]; } /** * Get long from buffer. * * @throws InvalidPacketException */ public function getLong(): int { if ($this->remaining() < 4) { throw new InvalidPacketException('Not enough data to unpack a long.', InvalidPacketException::BUFFER_EMPTY); } $data = unpack('l', $this->get(4)); if (!$data) { throw new InvalidPacketException('Empty data from packet.'); } return (int) $data[1]; } /** * Get float from buffer. * * @throws InvalidPacketException */ public function getFloat(): float { if ($this->remaining() < 4) { throw new InvalidPacketException('Not enough data to unpack a float.', InvalidPacketException::BUFFER_EMPTY); } $data = unpack('f', $this->get(4)); if (!$data) { throw new InvalidPacketException('Empty data from packet.'); } return (float) $data[1]; } /** * Get unsigned long from buffer. * * @throws InvalidPacketException */ public function getUnsignedLong(): int { if ($this->remaining() < 4) { throw new InvalidPacketException('Not enough data to unpack an usigned long.', InvalidPacketException::BUFFER_EMPTY); } $data = unpack('V', $this->get(4)); if (!$data) { throw new InvalidPacketException('Empty data from packet.'); } return (int) $data[1]; } /** * Read one string from buffer ending with null byte. */ public function getString(): string { $zeroBytePosition = strpos($this->buffer, "\0", $this->position); if (false === $zeroBytePosition) { return ''; } $string = $this->get($zeroBytePosition - $this->position); ++$this->position; return $string; } }