mirror of
https://github.com/xiaoqidun/ofdgo.git
synced 2026-10-29 14:16:55 +08:00
346 lines
8.4 KiB
Go
346 lines
8.4 KiB
Go
// Copyright 2025-2026 肖其顿 (XIAO QI DUN)
|
|
//
|
|
// 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 ofdgo
|
|
|
|
import (
|
|
"encoding/binary"
|
|
"fmt"
|
|
"io"
|
|
"math"
|
|
"math/bits"
|
|
"unicode/utf8"
|
|
)
|
|
|
|
// u3dValues 读取U3D未压缩字段,不允许越过当前块
|
|
type u3dValues struct {
|
|
data []byte
|
|
pos int
|
|
err error
|
|
}
|
|
|
|
// u3dBits 按ECMA-363第10章逆解16位算术区间,字节内按低位优先读取
|
|
type u3dBits struct {
|
|
data []byte
|
|
pos uint64
|
|
low, high, code uint32
|
|
err error
|
|
reserve func(uint64, uint64) error
|
|
}
|
|
|
|
// u3dHistogram 保存一个动态压缩上下文的频次及前缀和
|
|
type u3dHistogram struct {
|
|
counts []uint32
|
|
tree []uint32
|
|
total uint32
|
|
}
|
|
|
|
// take 读取定长字段,失败后不继续推进位置
|
|
// 入参: size 字节数
|
|
// 返回: []byte 字段数据,失败时为空
|
|
func (r *u3dValues) take(size int) []byte {
|
|
if r.err != nil {
|
|
return nil
|
|
}
|
|
if size < 0 || size > len(r.data)-r.pos {
|
|
r.err = io.ErrUnexpectedEOF
|
|
return nil
|
|
}
|
|
value := r.data[r.pos : r.pos+size]
|
|
r.pos += size
|
|
return value
|
|
}
|
|
|
|
// u8 读取一个未压缩字节
|
|
// 返回: byte 数值
|
|
func (r *u3dValues) u8() byte {
|
|
value := r.take(1)
|
|
if value == nil {
|
|
return 0
|
|
}
|
|
return value[0]
|
|
}
|
|
|
|
// u32 读取小端32位整数
|
|
// 返回: uint32 数值
|
|
func (r *u3dValues) u32() uint32 {
|
|
value := r.take(4)
|
|
if value == nil {
|
|
return 0
|
|
}
|
|
return binary.LittleEndian.Uint32(value)
|
|
}
|
|
|
|
// f32 读取有限单精度数值
|
|
// 返回: float32 数值
|
|
func (r *u3dValues) f32() float32 {
|
|
value := math.Float32frombits(r.u32())
|
|
if !finite(float64(value)) && r.err == nil {
|
|
r.err = fmt.Errorf("invalid U3D floating-point value")
|
|
}
|
|
return value
|
|
}
|
|
|
|
// text 读取带16位字节长度的UTF-8字符串
|
|
// 返回: string 文本
|
|
func (r *u3dValues) text() string {
|
|
length := r.take(2)
|
|
if length == nil {
|
|
return ""
|
|
}
|
|
data := r.take(int(binary.LittleEndian.Uint16(length)))
|
|
if r.err == nil && !utf8.Valid(data) {
|
|
r.err = fmt.Errorf("invalid U3D UTF-8 string")
|
|
}
|
|
return string(data)
|
|
}
|
|
|
|
// done 检查字段是否恰好覆盖当前块
|
|
// 返回: error 读取或多余字段错误
|
|
func (r *u3dValues) done() error {
|
|
if r.err != nil {
|
|
return r.err
|
|
}
|
|
if r.pos != len(r.data) {
|
|
return fmt.Errorf("unexpected U3D block data")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// newU3DBits 初始化当前块的独立算术解码状态
|
|
// 入参: data 块数据
|
|
// 返回: *u3dBits 解码器
|
|
func newU3DBits(data []byte) *u3dBits {
|
|
r := &u3dBits{data: data, high: 0xffff}
|
|
for range 16 {
|
|
r.code = r.code<<1 | r.bit()
|
|
}
|
|
return r
|
|
}
|
|
|
|
// bit 读取低位优先比特,区间预读最多允许16个末尾零位
|
|
// 返回: uint32 比特
|
|
func (r *u3dBits) bit() uint32 {
|
|
if r.err != nil {
|
|
return 0
|
|
}
|
|
limit := uint64(len(r.data)) * 8
|
|
var value uint32
|
|
if r.pos < limit {
|
|
value = uint32(r.data[r.pos/8]>>(r.pos%8)) & 1
|
|
} else if r.pos >= limit+16 {
|
|
r.err = io.ErrUnexpectedEOF
|
|
return 0
|
|
}
|
|
r.pos++
|
|
return value
|
|
}
|
|
|
|
// cumulative 求当前编码值在上下文累计频次中的位置
|
|
// 入参: total 总频次
|
|
// 返回: uint32 累计频次位置
|
|
func (r *u3dBits) cumulative(total uint32) uint32 {
|
|
if r.err != nil {
|
|
return 0
|
|
}
|
|
if total == 0 || r.code < r.low || r.code > r.high {
|
|
r.err = fmt.Errorf("invalid U3D arithmetic interval")
|
|
return 0
|
|
}
|
|
return uint32((uint64(r.code-r.low+1)*uint64(total) - 1) / uint64(r.high-r.low+1))
|
|
}
|
|
|
|
// advance 收缩符号区间并执行同位及下溢归一化
|
|
// 入参: start 累计频次, count 符号频次, total 总频次
|
|
func (r *u3dBits) advance(start, count, total uint32) {
|
|
if r.err != nil {
|
|
return
|
|
}
|
|
if count == 0 || start >= total || count > total-start {
|
|
r.err = fmt.Errorf("invalid U3D symbol frequency")
|
|
return
|
|
}
|
|
width := uint64(r.high-r.low) + 1
|
|
r.high = r.low + uint32(width*uint64(start+count)/uint64(total)) - 1
|
|
r.low += uint32(width * uint64(start) / uint64(total))
|
|
for r.err == nil {
|
|
var offset uint32
|
|
switch {
|
|
case r.high < 0x8000:
|
|
case r.low >= 0x8000:
|
|
offset = 0x8000
|
|
case r.low >= 0x4000 && r.high < 0xc000:
|
|
offset = 0x4000
|
|
default:
|
|
return
|
|
}
|
|
r.low = (r.low - offset) << 1
|
|
r.high = (r.high-offset)<<1 | 1
|
|
r.code = (r.code-offset)<<1 | r.bit()
|
|
}
|
|
}
|
|
|
|
// byteValue 逆解静态256符号上下文中的反序字节
|
|
// 返回: byte 原始字节
|
|
func (r *u3dBits) byteValue() byte {
|
|
value := r.cumulative(256)
|
|
r.advance(value, 1, 256)
|
|
return bits.Reverse8(byte(value))
|
|
}
|
|
|
|
// u32 逆解四个小端字节,不重置压缩区间
|
|
// 返回: uint32 原始整数
|
|
func (r *u3dBits) u32() uint32 {
|
|
var value uint32
|
|
for shift := range 4 {
|
|
value |= uint32(r.byteValue()) << (8 * shift)
|
|
}
|
|
return value
|
|
}
|
|
|
|
// index 读取静态上下文的网格索引,大范围按标准使用未压缩整数
|
|
// 入参: count 索引范围
|
|
// 返回: uint32 索引
|
|
func (r *u3dBits) index(count uint32) uint32 {
|
|
if count > 0x3ffe {
|
|
return r.u32()
|
|
}
|
|
value := r.cumulative(count)
|
|
r.advance(value, 1, count)
|
|
return value
|
|
}
|
|
|
|
// dynamic 读取动态上下文数值,逃逸后的字面量仍使用当前算术区间
|
|
// 入参: h 频次上下文
|
|
// 返回: uint32 原始整数
|
|
func (r *u3dBits) dynamic(h *u3dHistogram) uint32 {
|
|
return r.dynamicValue(h, false)
|
|
}
|
|
|
|
// dynamicValue 按字段位宽处理动态上下文的逃逸字面量,分配前检查可选预算
|
|
// 入参: h 频次上下文, small 是否为8位字段
|
|
// 返回: uint32 解码值
|
|
func (r *u3dBits) dynamicValue(h *u3dHistogram, small bool) uint32 {
|
|
if r.err != nil {
|
|
return 0
|
|
}
|
|
if h.total == 0 {
|
|
if r.reserve != nil {
|
|
if err := r.reserve(1, 32); err != nil {
|
|
r.err = err
|
|
return 0
|
|
}
|
|
}
|
|
h.counts = make([]uint32, 1)
|
|
h.tree = make([]uint32, 2)
|
|
h.add(0)
|
|
}
|
|
index := r.cumulative(h.total)
|
|
symbol, start := h.find(index)
|
|
if r.err != nil {
|
|
return 0
|
|
}
|
|
r.advance(start, h.counts[symbol], h.total)
|
|
r.histogramAdd(h, symbol)
|
|
if r.err != nil {
|
|
return 0
|
|
}
|
|
if symbol != 0 {
|
|
if small && symbol > 256 {
|
|
r.err = fmt.Errorf("invalid U3D compressed byte")
|
|
}
|
|
return symbol - 1
|
|
}
|
|
var value uint32
|
|
if small {
|
|
value = uint32(r.byteValue())
|
|
} else {
|
|
value = r.u32()
|
|
}
|
|
if value < 0xffff && r.err == nil {
|
|
r.histogramAdd(h, value+1)
|
|
}
|
|
return value
|
|
}
|
|
|
|
// histogramAdd 在频次数组扩展前计入解码预算
|
|
// 入参: h 频次上下文, symbol 待更新符号
|
|
func (r *u3dBits) histogramAdd(h *u3dHistogram, symbol uint32) {
|
|
if r.err != nil {
|
|
return
|
|
}
|
|
if r.reserve != nil && symbol <= 0xffff && int(symbol) >= len(h.counts) {
|
|
length := min(65536, max(int(symbol)+1, len(h.counts)*2))
|
|
if err := r.reserve(uint64(length-len(h.counts)), 32); err != nil {
|
|
r.err = err
|
|
return
|
|
}
|
|
}
|
|
h.add(symbol)
|
|
}
|
|
|
|
// find 以树状前缀和定位符号,避免按符号数量线性扫描
|
|
// 入参: position 累计频次位置
|
|
// 返回: uint32 符号, uint32 符号前缀频次
|
|
func (h *u3dHistogram) find(position uint32) (uint32, uint32) {
|
|
index, prefix := 0, uint32(0)
|
|
for step := 1 << (bits.Len(uint(len(h.tree))) - 1); step != 0; step >>= 1 {
|
|
next := index + step
|
|
if next < len(h.tree) && h.tree[next] <= position-prefix {
|
|
prefix += h.tree[next]
|
|
index = next
|
|
}
|
|
}
|
|
return uint32(index), prefix
|
|
}
|
|
|
|
// add 更新动态频次,并按ECMA-363阈值缩减历史频次
|
|
// 入参: symbol 符号,超过16位的值不加入上下文
|
|
func (h *u3dHistogram) add(symbol uint32) {
|
|
if symbol > 0xffff {
|
|
return
|
|
}
|
|
if h.total >= 0x1fff {
|
|
for index := range h.counts {
|
|
h.counts[index] >>= 1
|
|
}
|
|
h.counts[0]++
|
|
h.rebuild()
|
|
}
|
|
if int(symbol) >= len(h.counts) {
|
|
length := min(65536, max(int(symbol)+1, len(h.counts)*2))
|
|
h.counts = append(h.counts, make([]uint32, length-len(h.counts))...)
|
|
h.rebuild()
|
|
}
|
|
h.counts[symbol]++
|
|
h.total++
|
|
for index := int(symbol) + 1; index < len(h.tree); index += index & -index {
|
|
h.tree[index]++
|
|
}
|
|
}
|
|
|
|
// rebuild 在线性时间内重建动态频次索引
|
|
func (h *u3dHistogram) rebuild() {
|
|
h.tree = make([]uint32, len(h.counts)+1)
|
|
h.total = 0
|
|
for index, count := range h.counts {
|
|
h.total += count
|
|
i := index + 1
|
|
h.tree[i] += count
|
|
if next := i + (i & -i); next < len(h.tree) {
|
|
h.tree[next] += h.tree[i]
|
|
}
|
|
}
|
|
}
|