mirror of
https://github.com/xiaoqidun/ofdgo.git
synced 2026-10-30 01:27:26 +08:00
1 parent
64f9ec4241
commit
0be872691d
76 files changed
+1926
-1307
No files matched your search
@@ -0,0 +1,366 @@
|
||||
// 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 threed
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
// finish 校验压缩块的冲刷字段及结束位置
|
||||
// 返回: error 截断、冲刷或多余数据错误
|
||||
func (r *u3dBits) finish() error {
|
||||
if r.err != nil {
|
||||
return r.err
|
||||
}
|
||||
if r.pos > uint64(len(r.data))*8 {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
if r.u32() != 0 {
|
||||
return fmt.Errorf("invalid U3D compression flush")
|
||||
}
|
||||
if r.err != nil {
|
||||
return r.err
|
||||
}
|
||||
if (r.pos-16+7)/8 != uint64(len(r.data)) {
|
||||
return fmt.Errorf("unexpected U3D compressed block data")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// 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]
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user