<?php
/**
* SeekQuarry/Yioop --
* Open Source Pure PHP Search Engine, Crawler, and Indexer
*
* Copyright (C) 2009 - 2026 Chris Pollett chris@pollett.org
*
* LICENSE:
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* END LICENSE
*
* @author Chris Pollett chris@pollett.org
* @license https://www.gnu.org/licenses/ GPL3
* @link https://www.seekquarry.com/
* @copyright 2009 - 2026
* @filesource
*
* LowBitReader reads a string of bytes taking the lowest bit of a byte
* first, which is how Vorbis writes its numbers.
*/
namespace seekquarry\yioop\library\av_processing;
/**
* LowBitReader walks a string of bytes one bit at a time, taking the
* lowest bit of a byte before the higher ones. Vorbis writes its
* numbers that way round, where H.264 and AAC write the highest bit
* first, so a reader built for those formats would read a Vorbis field
* backwards.
*
* A caller makes one over the bytes of a packet and asks for as many
* bits at a time as the format says a field takes. The reader keeps its
* own place, so fields come back in the order they were written.
*
* @author Chris Pollett
*/
class LowBitReader
{
/**
* $bytes stores the bytes this reader was given. It walks them and
* never changes them.
* @var string
*/
public $bytes;
/**
* $position stores how many bits the reader has passed, counting
* from the start of the bytes.
* @var int
*/
public $position = 0;
/**
* $bit_count stores how many bits the bytes hold altogether, which
* is eight times their length.
* @var int
*/
public $bit_count = 0;
/**
* __construct makes a reader over the bytes a caller wants to read.
* Nothing is read until a field is asked for.
*
* @param string $data The bytes to read through.
*/
public function __construct($data)
{
$this->bytes = $data;
$this->bit_count = strlen($data) * 8;
}
/**
* bitsLeft says how many bits remain unread. A caller asks before
* reading a field whose width it knows, so that a packet which ends
* early is noticed rather than read past its end.
*
* @return int How many bits remain.
*/
public function bitsLeft()
{
return $this->bit_count - $this->position;
}
/**
* readBit reads one bit and moves the reader on by one. Reading
* past the end hands back zero, which is what the format asks for:
* a packet may end mid-field, and the reading stops there.
*
* @return int The bit read, zero or one.
*/
public function readBit()
{
if ($this->position >= $this->bit_count) {
$this->position++;
return 0;
}
$byte = ord($this->bytes[$this->position >> 3]);
$bit = ($byte >> ($this->position & 7)) & 1;
$this->position++;
return $bit;
}
/**
* readBits reads a field of a given width as one number, its lowest
* bit first. Most of what a Vorbis packet holds is written this
* way.
*
* @param int $count How many bits the field takes.
* @return int The number those bits spell out.
*/
public function readBits($count)
{
$value = 0;
for ($at = 0; $at < $count; $at++) {
$value |= $this->readBit() << $at;
}
return $value;
}
/**
* readFloat reads one of the numbers a code book stores as a
* fraction. Vorbis writes such a number in a shape of its own,
* older than the one machines use: a sign, a size, and how far the
* point has moved.
*
* @return float The number those bits spell out.
*/
public function readFloat()
{
$held = $this->readBits(32);
$size = $held & 0x1FFFFF;
$sign = $held & 0x80000000;
$moved = (($held & 0x7FE00000) >> 21) - 788;
if ($sign) {
$size = -$size;
}
return $size * pow(2.0, $moved);
}
/**
* bitsFor says how many bits a number takes to write, which the
* format uses to size several of its fields. The answer for zero is
* zero, and for any other number it is one more than the place of
* its highest bit.
*
* @param int $value The number to measure.
* @return int How many bits it takes.
*/
public static function bitsFor($value)
{
$bits = 0;
while ($value > 0) {
$bits++;
$value >>= 1;
}
return $bits;
}
}