// Copyright 2023 LiveKit, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package dependencydescriptor import ( "errors" "fmt" ) type BitStreamWriter struct { buf []byte pos int bitOffset int // bit offset in the current byte } func NewBitStreamWriter(buf []byte) *BitStreamWriter { return &BitStreamWriter{buf: buf} } func (w *BitStreamWriter) RemainingBits() int { return (len(w.buf)-w.pos)*8 - w.bitOffset } func (w *BitStreamWriter) WriteBits(val uint64, bitCount int) error { if bitCount > w.RemainingBits() { return errors.New("insufficient space") } totalBits := bitCount // push bits to the highest bits of uint64 val <<= 64 - bitCount buf := w.buf[w.pos:] // The first byte is relatively special; the bit offset to write to may put us // in the middle of the byte, and the total bit count to write may require we // save the bits at the end of the byte. remainingBitsInCurrentByte := 8 - w.bitOffset bitsInFirstByte := bitCount if bitsInFirstByte > remainingBitsInCurrentByte { bitsInFirstByte = remainingBitsInCurrentByte } buf[0] = w.writePartialByte(uint8(val>>56), bitsInFirstByte, buf[0], w.bitOffset) if bitCount <= remainingBitsInCurrentByte { // no bit left to write return w.consumeBits(totalBits) } // write the rest of the bits val <<= bitsInFirstByte buf = buf[1:] bitCount -= bitsInFirstByte for bitCount >= 8 { buf[0] = uint8(val >> 56) buf = buf[1:] val <<= 8 bitCount -= 8 } // write the last bits if bitCount > 0 { buf[0] = w.writePartialByte(uint8(val>>56), bitCount, buf[0], 0) } return w.consumeBits(totalBits) } func (w *BitStreamWriter) consumeBits(bitCount int) error { if bitCount > w.RemainingBits() { return errors.New("insufficient space") } w.pos += (w.bitOffset + bitCount) / 8 w.bitOffset = (w.bitOffset + bitCount) % 8 return nil } func (w *BitStreamWriter) writePartialByte(source uint8, sourceBitCount int, target uint8, targetBitOffset int) uint8 { // if !(targetBitOffset < 8 && sourceBitCount <= (8-targetBitOffset)) { // return fmt.Errorf("invalid argument, source %d, sourceBitCount %d, target %d, targetBitOffset %d", source, sourceBitCount, target, targetBitOffset) // } // generate mask for bits to overwrite, shift source bits to highest bits, then position to target bit offset mask := uint8(0xff<<(8-sourceBitCount)) >> uint8(targetBitOffset) // clear target bits and write source bits return (target & ^mask) | (source >> targetBitOffset) } func (w *BitStreamWriter) WriteNonSymmetric(val, numValues uint32) error { if val >= numValues || numValues > 1<<31 { return fmt.Errorf("invalid argument, val %d, numValues %d", val, numValues) } if numValues == 1 { // When there is only one possible value, it requires zero bits to store it. // But WriteBits doesn't support writing zero bits. return nil } countBits := bitwidth(numValues) numMinBitsValues := (uint32(1) << countBits) - numValues if val < numMinBitsValues { return w.WriteBits(uint64(val), countBits-1) } else { return w.WriteBits(uint64(val+numMinBitsValues), countBits) } } func SizeNonSymmetricBits(val, numValues uint32) int { countBits := bitwidth(numValues) numMinBitsValues := (uint32(1) << countBits) - numValues if val < numMinBitsValues { return countBits - 1 } else { return countBits } }