refactor(ocr): 拆分字符集限制枚举并引入声明式多路组合子宏
- 将原本臃肿的 CharsetRestrict 拆分为 CharRestrict(内容过滤)和 IdRestrict(索引过滤),实现职责解耦 - 引入 any_of! 声明式宏实现无 Box、无堆内存分配的栈上多路组合,规避孤儿规则 - 完善 estimated_capacity 容量预估函数,实现真正的 O(1) 精准内存开辟
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
use crate::charset::CharsetRestrict;
|
||||
use crate::charset::{TokenFilter, ValidationCtx};
|
||||
use crate::model_metadata::ModelMetadata;
|
||||
use crate::models::base::ModelArgs;
|
||||
use crate::models::loader::{ModelLoader, ModelSession, ModelType};
|
||||
@@ -158,45 +158,34 @@ impl<'a> OcrBuilder<'a> {
|
||||
self.color_filter_custom_ranges = Some(value);
|
||||
self
|
||||
}
|
||||
pub fn charset_restrict(mut self, restrict: &CharsetRestrict) -> Self {
|
||||
pub fn charset_restrict(mut self, restrict: &dyn TokenFilter) -> Self {
|
||||
let charset = &self.ocr.model_metadata.charset;
|
||||
let tokens = &charset.tokens;
|
||||
// let mut temp_indices = Vec::new();
|
||||
let mut has_any_match = false;
|
||||
|
||||
let estimated_capacity = match restrict {
|
||||
CharsetRestrict::Digit => 16,
|
||||
CharsetRestrict::Lowercase | CharsetRestrict::Uppercase => 32,
|
||||
CharsetRestrict::CustomList(vec) => vec.len() + 1, // 动态匹配列表大小
|
||||
CharsetRestrict::TopN(n) => *n + 1,
|
||||
_ => 128, // 组合子(Or)等复杂情况,给个 128 黄金保底值
|
||||
};
|
||||
let estimated_capacity = restrict.estimated_capacity();
|
||||
// 精准开辟内存,完美避开 8210 个槽位的巨大空置浪费
|
||||
let mut temp_indices = Vec::with_capacity(estimated_capacity);
|
||||
|
||||
if let CharsetRestrict::TopN(n) = *restrict {
|
||||
let limit = std::cmp::min(n, tokens.len());
|
||||
// 边界防御:CTC Blank (索引 0) 必须无条件放行
|
||||
temp_indices.push(0);
|
||||
// temp_indices.extend(0..limit);
|
||||
// has_any_match = limit > &0;
|
||||
// 塞入剩余的有效索引范围(排除0,从1开始截取)
|
||||
if limit > 1 {
|
||||
temp_indices.extend(1..limit);
|
||||
for (idx, token) in tokens.iter().enumerate() {
|
||||
let token_str = token.as_ref();
|
||||
// CTC Blank 空字符串无条件放行,其余交给超高性能的 matches
|
||||
if token_str.is_empty() || idx == 0 {
|
||||
temp_indices.push(idx);
|
||||
};
|
||||
// 组装无拷贝上下文
|
||||
let ctx = ValidationCtx {
|
||||
text: token_str,
|
||||
token_id: idx,
|
||||
};
|
||||
|
||||
if restrict.matches(&ctx) {
|
||||
temp_indices.push(idx);
|
||||
has_any_match = true;
|
||||
}
|
||||
} else {
|
||||
for (idx, token) in tokens.iter().enumerate() {
|
||||
let token_str = token.as_ref();
|
||||
// CTC Blank 空字符串无条件放行,其余交给超高性能的 matches
|
||||
if token_str.is_empty() {
|
||||
temp_indices.push(idx);
|
||||
} else if restrict.matches(token_str) {
|
||||
temp_indices.push(idx);
|
||||
has_any_match = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// self.charset_restrict = Some(restrict);
|
||||
// 终极防御:如果除了 Blank 外什么都没匹配上,退化恢复为 None(全量识别)
|
||||
if !has_any_match {
|
||||
@@ -266,7 +255,7 @@ impl<'a> OcrBuilder<'a> {
|
||||
// fn valid_indices(&self) -> (bool, HashSet<usize>) {
|
||||
// let charset = &self.ocr.model_metadata.charset;
|
||||
|
||||
/// 🌟 【按需延迟打印】:当用户真的需要“知道当前有哪些限制字符”时,一秒反查并打印
|
||||
/// 【按需延迟打印】:当用户真的需要“知道当前有哪些限制字符”时,一秒反查并打印
|
||||
/// 这里的 &str 完美借用了自 tokens,依然是彻底的零拷贝!
|
||||
pub fn get_valid_tokens(&self) -> Vec<&str> {
|
||||
let charset = &self.ocr.model_metadata.charset;
|
||||
|
||||
Reference in New Issue
Block a user