<?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;
    }
}
