/* SPDX-License-Identifier: Apache-2.0 OR MIT */ /* * Copyright (c) 2024 Rockchip Electronics Co., Ltd. */ #define MODULE_TAG "mpp_trie" #include "mpp_env.h" #include "mpp_mem.h" #include "mpp_time.h" #include "mpp_debug.h" #include "mpp_common.h" #include "mpp_trie.h" #define MPP_TRIE_DBG_PAVE (0x00000001) #define MPP_TRIE_DBG_SET (0x00000002) #define MPP_TRIE_DBG_GET (0x00000004) #define MPP_TRIE_DBG_CNT (0x00000008) #define MPP_TRIE_DBG_WALK (0x00000010) #define MPP_TRIE_DBG_LAST (0x00000020) #define MPP_TRIE_DBG_LAST_STEP (0x00000040) #define MPP_TRIE_DBG_LAST_CHECK (0x00000080) #define MPP_TRIE_DBG_IMPORT (0x00000100) #define trie_dbg(flag, fmt, ...) mpp_dbg_f(mpp_trie_debug, flag, fmt, ## __VA_ARGS__) #define trie_dbg_pave(fmt, ...) trie_dbg(MPP_TRIE_DBG_PAVE, fmt, ## __VA_ARGS__) #define trie_dbg_set(fmt, ...) trie_dbg(MPP_TRIE_DBG_SET, fmt, ## __VA_ARGS__) #define trie_dbg_get(fmt, ...) trie_dbg(MPP_TRIE_DBG_GET, fmt, ## __VA_ARGS__) #define trie_dbg_cnt(fmt, ...) trie_dbg(MPP_TRIE_DBG_CNT, fmt, ## __VA_ARGS__) #define trie_dbg_walk(fmt, ...) trie_dbg(MPP_TRIE_DBG_WALK, fmt, ## __VA_ARGS__) #define trie_dbg_last(fmt, ...) trie_dbg(MPP_TRIE_DBG_LAST, fmt, ## __VA_ARGS__) #define MPP_TRIE_KEY_LEN (4) #define MPP_TRIE_KEY_MAX (1 << (MPP_TRIE_KEY_LEN)) #define MPP_TRIE_INFO_MAX (1 << 12) #define MPP_TRIE_NAME_MAX (1 << 12) #define DEFAULT_NODE_COUNT 900 #define DEFAULT_INFO_COUNT 80 #define INVALID_NODE_ID (-1) #define MPP_TRIE_TAG_LEN_MAX ((sizeof(rk_u64) * 8) / MPP_TRIE_KEY_LEN) /* spatial optimized trie tree */ typedef struct MppTrieNode_t { /* next - next trie node index */ rk_s16 next[MPP_TRIE_KEY_MAX]; /* id - payload data offset of current trie node */ rk_s32 id; /* idx - trie node index in ascending order */ rk_s16 idx; /* prev - previous trie node index */ rk_s16 prev; /* tag_val - prefix tag */ rk_u64 tag_val; /* key - current key value in previous node as next */ rk_u16 key; /* * tag len - prefix tag length * zero - normal node with 16 next node * positive - tag node with 64bit prefix tag */ rk_s16 tag_len; /* next_cnt - valid next node count */ /* use union for array index support */ union { rk_u16 next_cnt; struct { rk_u16 count : 14; /* array element count */ rk_u16 sub_root : 1; /* subtree root flag (array) */ rk_u16 is_branch : 1; /* pure routing / branch node flag */ }; }; } MppTrieNode; typedef struct MppTrieImpl_t { char *name; rk_s32 name_len; rk_s32 buf_size; rk_s32 node_count; rk_s32 node_used; MppTrieNode *nodes; /* info and name record buffer */ void *info_buf; rk_s32 info_count; rk_s32 info_buf_size; rk_s32 info_buf_pos; rk_s32 info_name_max; } MppTrieImpl; /* work structure for trie traversal */ typedef struct MppTrieWalk_t { MppTrieNode *root; rk_u64 tag; rk_u32 len; rk_s32 match; } MppTrieWalk; static rk_u32 mpp_trie_debug = 0; static rk_s32 trie_get_node(MppTrieImpl *trie, rk_s32 prev, rk_u64 key) { MppTrieNode *node; rk_s32 idx; if (trie->node_used >= trie->node_count) { rk_s32 old_count = trie->node_count; rk_s32 new_count = old_count * 2; MppTrieNode *new_nodes = mpp_realloc(trie->nodes, MppTrieNode, new_count); if (!new_nodes) { mpp_loge_f("failed to realloc new nodes %d\n", new_count); return -1; } /* NOTE: new memory should be memset to zero */ memset(new_nodes + old_count, 0, sizeof(*new_nodes) * old_count); trie_dbg_cnt("trie %p enlarge node %p:%d -> %p:%d\n", trie, trie->nodes, trie->node_count, new_nodes, new_count); trie->nodes = new_nodes; trie->node_count = new_count; } idx = trie->node_used++; node = &trie->nodes[idx]; node->idx = idx; node->prev = (prev > 0) ? prev : 0; node->key = key; node->id = INVALID_NODE_ID; if (prev >= 0) trie->nodes[prev].next_cnt++; trie_dbg_cnt("get node %d\n", idx); return idx; } static rk_s32 trie_prepare_buf(MppTrieImpl *p, rk_s32 info_size) { if (p->info_count >= MPP_TRIE_INFO_MAX) { mpp_loge_f("invalid trie info count %d larger than max %d\n", p->info_count, MPP_TRIE_INFO_MAX); return rk_nok; } /* check and enlarge info buffer */ if (p->info_buf_pos + info_size > p->info_buf_size) { rk_s32 old_size = p->info_buf_size; rk_s32 new_size = old_size * 2; void *ptr = mpp_realloc(p->info_buf, void, new_size); if (!ptr) { mpp_loge_f("failed to realloc new info buffer %d\n", new_size); return rk_nok; } trie_dbg_cnt("trie %p enlarge info_buf %p:%d -> %p:%d\n", p, p->info_buf, old_size, ptr, new_size); p->info_buf = ptr; p->info_buf_size = new_size; } return rk_ok; } rk_s32 mpp_trie_init(MppTrie *trie, const char *name) { MppTrieImpl *p; rk_s32 name_len; rk_s32 ret = rk_nok; if (!trie || !name) { mpp_loge_f("invalid trie %p name %p\n", trie, name); return ret; } mpp_env_get_u32("mpp_trie_debug", &mpp_trie_debug, 0); name_len = strnlen(name, MPP_TRIE_NAME_MAX) + 1; p = mpp_calloc_size(MppTrieImpl, sizeof(*p) + name_len); if (!p) { mpp_loge_f("create trie impl %s failed\n", name); goto done; } p->name = (char *)(p + 1); p->name_len = name_len; strncpy(p->name, name, name_len); p->node_count = DEFAULT_NODE_COUNT; p->nodes = mpp_calloc(MppTrieNode, p->node_count); if (!p->nodes) { mpp_loge_f("create %d nodes failed\n", p->node_count); goto done; } p->info_buf_size = 4096; p->info_buf = mpp_calloc(void, p->info_buf_size); if (!p->info_buf) { mpp_loge_f("failed to alloc %d info buffer\n", p->info_buf_size); goto done; } /* get node 0 as root node*/ trie_get_node(p, -1, 0); ret = rk_ok; done: if (ret && p) { MPP_FREE(p->info_buf); MPP_FREE(p->nodes); MPP_FREE(p); } *trie = p; return ret; } static rk_s32 mpp_trie_walk(MppTrieWalk *p, rk_s32 idx, rk_u32 key, rk_u32 keyx, rk_u32 end) { MppTrieNode *node = &p->root[idx]; char log_buf[128]; rk_u32 log_size = sizeof(log_buf) - 1; rk_u32 log_len = 0; rk_u32 log_en = ((mpp_trie_debug & MPP_TRIE_DBG_WALK) != 0) ? 1 : 0; rk_s32 next = -1; if (log_en) log_len += snprintf(log_buf + log_len, log_size - log_len, "node %d:%d key %02x:%x -> ", node->idx, node->id, key, keyx); /* check high 4 bits */ if (!node->tag_len || p->match == node->idx) { /* not tag or tag has be matched -> normal next switch node */ next = node->next[keyx]; if (log_en) log_len += snprintf(log_buf + log_len, log_size - log_len, "tag %s -> ", (node->tag_len == 0) ? "n/a" : "match"); p->tag = 0; p->len = 0; p->match = -1; } else { /* with tag check len to fill or to stop */ rk_u64 val_new = (p->tag << 4) | keyx; rk_s32 len_new = p->len + 1; if (log_en) log_len += snprintf(log_buf + log_len, log_size - log_len, "tag %0*llx:%d - st %0*llx:%d -> ", node->tag_len, node->tag_val, node->tag_len, node->tag_len, val_new, len_new); /* clear old matched node */ p->match = -1; if (node->tag_len > len_new && !end) { /* more tag to fill keep current node */ p->tag = val_new; p->len = len_new; next = node->idx; if (log_en) log_len += snprintf(log_buf + log_len, log_size - log_len, "fill -> "); } else { /* check tag match with new value */ p->tag = 0; p->len = 0; if (node->tag_val != val_new) { if (log_en) log_len += snprintf(log_buf + log_len, log_size - log_len, "mismatch -> "); next = INVALID_NODE_ID; } else { if (log_en) log_len += snprintf(log_buf + log_len, log_size - log_len, "match -> "); next = node->idx; p->match = node->idx; } } } if (log_en) { snprintf(log_buf + log_len, log_size - log_len, "%d", next); mpp_logi_f("%s\n", log_buf); } return next; } static MppTrieNode *mpp_trie_get_node_at(MppTrieNode *root, rk_s32 start, const char *name) { const char *s = name; MppTrieWalk walk; rk_s32 idx = start; if (!root || !name || !*name) { mpp_loge_f("invalid root %p name %p\n", root, name); return NULL; } trie_dbg_get("root %p search %s from %d\n", root, name, start); walk.root = root; walk.tag = 0; walk.len = 0; walk.match = 0; do { char key = *s++; rk_u32 key0 = (key >> 4) & 0xf; rk_u32 key1 = key & 0xf; rk_u32 end = s[0] == '\0'; idx = mpp_trie_walk(&walk, idx, key, key0, 0); if (idx < 0) break; idx = mpp_trie_walk(&walk, idx, key, key1, end); if (idx < 0 || end) break; } while (1); trie_dbg_get("get %s ret node %d:%d\n", name, idx, (idx >= 0) ? root[idx].id : INVALID_NODE_ID); return (idx >= 0) ? &root[idx] : NULL; } rk_s32 mpp_trie_init_by_root(MppTrie *trie, void *root) { MppTrieImpl *p; MppTrieInfo *info; rk_s32 i; if (!trie || !root) { mpp_loge_f("invalid trie %p root %p\n", trie, root); return rk_nok; } *trie = NULL; p = mpp_calloc(MppTrieImpl, 1); if (!p) { mpp_loge_f("create trie impl failed\n"); return rk_nok; } info = mpp_trie_get_info_from_root(root, "__name__"); if (info) p->name = mpp_trie_info_ctx(info); info = mpp_trie_get_info_from_root(root, "__node__"); if (info) p->node_used = *(rk_u32 *)mpp_trie_info_ctx(info); info = mpp_trie_get_info_from_root(root, "__info__"); if (info) p->info_count = *(rk_u32 *)mpp_trie_info_ctx(info); /* import and update new buffer */ p->nodes = (MppTrieNode *)root; if (mpp_trie_debug & MPP_TRIE_DBG_IMPORT) mpp_trie_dump(p, "root import"); info = mpp_trie_get_info_first(p); for (i = 0; i < p->info_count; i++) { const char *name = mpp_trie_info_name(info); MppTrieInfo *info_set = info; MppTrieInfo *info_ret = mpp_trie_get_info(p, name); info = mpp_trie_get_info_next(p, info); if (info_ret && info_set == info_ret && info_ret->index == i) continue; /* * Fallback for entries under VLA/array sub-trees. * The info stores the segment name but the entry is registered * under a sub-root node. Walk back through prev links to find * the sub-root ancestor, then retry the name lookup from there. */ if (!info_ret) { rk_s32 off = (rk_s32)((rk_u8 *)info_set - (rk_u8 *)p->nodes); rk_s32 j; rk_s32 validated = 0; for (j = 0; j < p->node_used; j++) { MppTrieNode *n = &p->nodes[j]; if (n->id != off) continue; rk_s32 walk = n->idx; while (walk > 0 && !p->nodes[walk].sub_root) walk = p->nodes[walk].prev; if (walk > 0) { MppTrieNode *node = mpp_trie_get_node_at(p->nodes, walk, name); if (node && node == n) validated = 1; } break; } if (validated) continue; } mpp_loge_f("trie check on import found mismatch info %s [%d:%p] - [%d:%p]\n", name, i, info_set, info_ret ? info_ret->index : -1, info_ret); return rk_nok; } *trie = p; return rk_ok; } rk_s32 mpp_trie_deinit(MppTrie trie) { if (trie) { MppTrieImpl *p = (MppTrieImpl *)trie; /* NOTE: do not remvoe imported root */ if (p->node_count) mpp_free(p->nodes); else p->nodes = NULL; MPP_FREE(p->info_buf); MPP_FREE(p); return rk_ok; } return rk_nok; } /* * trie_pave_segment - pave one segment's characters into trie from start_idx * * Returns end node index on success, negative on error. */ static rk_s32 trie_pave_segment(MppTrieImpl *p, const char *seg, rk_s32 seg_len, rk_s32 start_idx) { rk_s32 idx = start_idx; rk_s32 i; for (i = 0; i < seg_len && seg[i]; i++) { rk_u32 key = seg[i]; rk_s32 key0 = (key >> 4) & 0xf; rk_s32 key1 = key & 0xf; MppTrieNode *node = p->nodes + idx; rk_s32 next; next = node->next[key0]; if (!next) { next = trie_get_node(p, idx, key0); if (next < 0) return next; node = p->nodes + idx; node->next[key0] = next; } idx = next; node = p->nodes + idx; next = node->next[key1]; if (!next) { next = trie_get_node(p, idx, key1); if (next < 0) return next; node = p->nodes + idx; node->next[key1] = next; } idx = next; } return idx; } static MppTrieNode *mpp_trie_get_node(MppTrieNode *root, const char *name) { return mpp_trie_get_node_at(root, 0, name); } static rk_s32 mpp_trie_check(MppTrie trie, char *base, const char *log) { MppTrieImpl *p = (MppTrieImpl *)trie; rk_s32 found = 0; rk_s32 i; for (i = 0; i < p->node_used; i++) { MppTrieNode *n = &p->nodes[i]; MppTrieNode *node; const char *name; if (i && !n->idx) continue; if (n->id < 0) continue; name = (const char *)((MppTrieInfo *)(base + n->id) + 1); /* * Verify entry is accessible via trie walk. * Try full-path from root first; if not found, try from subroot ancestor. */ node = mpp_trie_get_node_at(p->nodes, 0, name); if (!node || node != n) { rk_s32 walk = n->idx; while (walk > 0 && !p->nodes[walk].sub_root) walk = p->nodes[walk].prev; node = (walk > 0) ? mpp_trie_get_node_at(p->nodes, walk, name) : NULL; if (!node || node != n) { mpp_loge("trie check on %s: node %d '%s' mismatch\n", log, i, name); return rk_nok; } } found++; } if (found != p->info_count) { mpp_loge("trie check on %s: %d nodes with info, expected %d\n", log, found, p->info_count); return rk_nok; } /* Check subroot nodes have valid info and children */ for (i = 0; i < p->node_used; i++) { MppTrieNode *n = &p->nodes[i]; rk_s32 child_cnt = 0; rk_s32 j; if (!n->sub_root) continue; if (n->id < 0) { mpp_loge("trie check on %s: subroot node %d has no info entry\n", log, i); continue; } for (j = 0; j < MPP_TRIE_KEY_MAX; j++) { if (n->next[j] > 0) child_cnt++; } if (!child_cnt) mpp_loge("trie check on %s: subroot node %d has no children\n", log, i); } return rk_ok; } rk_s32 mpp_trie_last_info(MppTrie trie) { MppTrieImpl *p = (MppTrieImpl *)trie; MppTrieNode *root; MppTrieNode *node; char *buf; rk_s32 node_count; rk_s32 node_valid; rk_s32 nodes_size; rk_s32 pos = 0; rk_s32 i; rk_s32 j; if (!trie) { mpp_loge_f("invalid NULL trie\n"); return rk_nok; } /* write trie self entry info */ pos = p->info_count + 3; mpp_trie_add_info(trie, "__name__", p->name, p->name_len); mpp_trie_add_info(trie, "__info__", &pos, sizeof(pos)); /* NOTE: node count need to be update after shrinking */ mpp_trie_add_info(trie, "__node__", &p->node_used, sizeof(p->node_used)); root = p->nodes; node_count = p->node_used; node_valid = node_count; trie_dbg_last("shrink trie node start node %d info %d\n", node_count, p->info_count); if (mpp_trie_debug & MPP_TRIE_DBG_LAST_STEP) mpp_trie_dump_f(trie); for (i = node_count - 1; i > 0; i--) { MppTrieNode *prev; rk_s32 prev_idx; node = &root[i]; prev_idx = node->prev; prev = &root[prev_idx]; if (prev->next_cnt > 1) { trie_dbg_last("node %d:%d prev %d next count %d stop shrinking for multi next\n", i, node->id, prev_idx, prev->next_cnt); continue; } if (node->tag_len >= (rk_s16)MPP_TRIE_TAG_LEN_MAX) { trie_dbg_last("node %d:%d tag %d - %016llx stop shrinking for max tag len\n", i, node->id, node->tag_len, node->tag_val); continue; } if (prev->id >= 0) { trie_dbg_last("node %d:%d tag %d - %016llx stop shrinking for valid info node\n", i, node->id, node->tag_len, node->tag_val); continue; } if (node->sub_root) { trie_dbg_last("node %d:%d tag %d - %016llx stop shrinking for sub_root\n", i, node->id, node->tag_len, node->tag_val); continue; } prev->id = node->id; /* NOTE: do NOT increase tag length on root node */ prev->tag_len = node->tag_len + 1; prev->tag_val = ((rk_u64)node->key << (node->tag_len * 4)) | node->tag_val; prev->next_cnt = node->next_cnt; memcpy(prev->next, node->next, sizeof(node->next)); trie_dbg_last("node %d:%d shrink prev %d key %x tag %016llx -> %016llx\n", i, node->id, prev->idx, prev->key, node->tag_val, prev->tag_val); for (j = 0; j < MPP_TRIE_KEY_MAX; j++) { if (!prev->next[j]) continue; root[prev->next[j]].prev = prev_idx; } memset(node, 0, sizeof(*node)); node->id = INVALID_NODE_ID; node_valid--; } trie_dbg_last("shrink trie node finish count %d -> %d\n", node_count, node_valid); if (mpp_trie_debug & MPP_TRIE_DBG_LAST_STEP) mpp_trie_dump_f(trie); if (mpp_trie_debug & MPP_TRIE_DBG_LAST_CHECK) mpp_trie_check(trie, p->info_buf, "shrink merge tag stage"); trie_dbg_last("move trie node start to reduce memory %d -> %d\n", node_count, node_valid); for (i = 1; i < node_valid; i++) { node = &root[i]; /* skip valid node */ if (node->idx) continue; for (j = i; j < node_count; j++) { MppTrieNode *tmp = &root[j]; MppTrieNode *prev; rk_s32 k; /* skip empty node */ if (!tmp->idx) continue; trie_dbg_last("move node %d to %d prev %d\n", j, i, tmp->prev); prev = &root[tmp->prev]; /* relink previous node */ for (k = 0; k < MPP_TRIE_KEY_MAX; k++) { if (prev->next[k] != tmp->idx) continue; prev->next[k] = i; break; } memcpy(node, tmp, sizeof(*node)); node->idx = i; memset(tmp, 0, sizeof(*tmp)); /* relink next node */ for (k = 0; k < MPP_TRIE_KEY_MAX; k++) { if (!node->next[k]) continue; root[node->next[k]].prev = i; } break; } } p->node_used = node_valid; trie_dbg_last("move trie node finish used %d\n", p->node_used); if (mpp_trie_debug & MPP_TRIE_DBG_LAST_STEP) mpp_trie_dump_f(trie); if (mpp_trie_debug & MPP_TRIE_DBG_LAST_CHECK) mpp_trie_check(trie, p->info_buf, "shrink move node stage"); trie_dbg_last("create user buffer start\n"); nodes_size = sizeof(MppTrieNode) * p->node_used; p->buf_size = nodes_size + p->info_buf_pos; p->buf_size = MPP_ALIGN(p->buf_size, sizeof(void *)); buf = mpp_calloc(char, p->buf_size); if (!buf) { mpp_loge("failed to alloc trie buffer size %d\n", p->buf_size); return rk_nok; } p->nodes = (MppTrieNode *)buf; memcpy(p->nodes, root, nodes_size); /* Block copy all info entries, then adjust node->id offsets */ memcpy(buf + nodes_size, p->info_buf, p->info_buf_pos); for (i = 0; i < p->node_used; i++) { if (p->nodes[i].id >= 0) p->nodes[i].id += nodes_size; } if (mpp_trie_debug & MPP_TRIE_DBG_LAST_CHECK) mpp_trie_check(trie, (char *)p->nodes, "final buffer"); MPP_FREE(root); MPP_FREE(p->info_buf); /* NOTE: udpate final shrinked node count */ { MppTrieInfo *info = mpp_trie_get_info_from_root(p->nodes, "__node__"); if (info) *(rk_u32 *)mpp_trie_info_ctx(info) = p->node_used; } return rk_ok; } rk_s32 mpp_trie_add_info(MppTrie trie, const char *name, void *ctx, rk_u32 ctx_len) { MppTrieImpl *p = (MppTrieImpl *)trie; rk_s32 info_size; rk_u32 ctx_real; rk_s32 str_real; rk_s32 str_len; rk_s32 idx; if (!p) { mpp_loge_f("invalid trie %p name %s ctx %p\n", p, name, ctx); return rk_nok; } if (!name) return mpp_trie_last_info(p); str_real = strnlen(name, MPP_TRIE_NAME_MAX) + 1; /* pad name so the ctx area behind it stays 8-byte aligned; * ctx holds KmppEntry with 64-bit members */ str_len = MPP_ALIGN8(str_real + sizeof(MppTrieInfo)) - sizeof(MppTrieInfo); /* NOTE: align all ctx_len to eight bytes */ ctx_real = ctx_len; ctx_len = MPP_ALIGN8(ctx_real); info_size = sizeof(MppTrieInfo) + str_len + ctx_len; if (str_len >= MPP_TRIE_NAME_MAX) { mpp_loge_f("invalid trie name %s len %d larger than max %d\n", name, str_len, MPP_TRIE_NAME_MAX); return rk_nok; } if (trie_prepare_buf(p, info_size)) return rk_nok; idx = trie_pave_segment(p, name, str_real, 0); if (idx < 0) { mpp_loge_f("trie %p pave node %s failed\n", p, name); return rk_nok; } if (p->nodes[idx].id != -1) { mpp_logw("trie %p add_info duplicate key \"%s\" id=%d\n", p, name, p->nodes[idx].id); return rk_nok; } p->nodes[idx].id = p->info_buf_pos; if (p->info_name_max < str_real - 1) p->info_name_max = str_real - 1; { MppTrieInfo *info = (MppTrieInfo *)(p->info_buf + p->info_buf_pos); char *buf = (char *)(info + 1); info->index = p->info_count; info->ctx_len = ctx_len; info->str_len = str_len; memcpy(buf, name, str_real); while (str_real < str_len) buf[str_real++] = 0; buf += str_len; memcpy(buf, ctx, ctx_real); while (ctx_real < ctx_len) buf[ctx_real++] = 0; } trie_dbg_set("trie %p add info %d - %s at node %d pos %d ctx %p size %d\n", p, p->info_count, name, idx, p->info_buf_pos, ctx, ctx_len); p->info_buf_pos += info_size; p->info_count++; return rk_ok; } rk_s32 mpp_trie_add_entry(MppTrie trie, MppTrieStatus *st, const char *name, KmppEntry *entry) { MppTrieImpl *p = (MppTrieImpl *)trie; rk_s32 name_len; rk_s32 idx; if (!p || !st) { mpp_loge_f("invalid param trie %p st %p\n", p, st); return rk_nok; } if (!name) return mpp_trie_last_info(p); st->node_idx = -1; st->array_idx = -1; name_len = strnlen(name, MPP_TRIE_NAME_MAX); if (!name_len) { mpp_loge_f("invalid empty path\n"); return rk_nok; } idx = trie_pave_segment(p, name, name_len, st->root_idx); if (idx < 0) return rk_nok; MppTrieNode *node = &p->nodes[idx]; rk_s32 is_vla = entry && entry->type == ENTRY_TYPE_VLA_INFO; if (is_vla) { node->sub_root = 1; node->count = entry->vla.elem_count; } /* Attach info payload to node */ if (node->id < 0 && entry) { rk_s32 str_real = name_len + 1; rk_s32 str_len = MPP_ALIGN8(str_real + sizeof(MppTrieInfo)) - sizeof(MppTrieInfo); rk_s32 info_size = sizeof(MppTrieInfo) + str_len + sizeof(*entry); if (trie_prepare_buf(p, info_size)) return rk_nok; node->id = p->info_buf_pos; if (p->info_name_max < name_len) p->info_name_max = name_len; { MppTrieInfo *info = (MppTrieInfo *)(p->info_buf + p->info_buf_pos); char *buf = (char *)(info + 1); info->index = p->info_count; info->ctx_len = sizeof(*entry); info->str_len = str_len; memcpy(buf, name, name_len); buf[name_len] = '\0'; buf += str_len; ((KmppEntry *)buf)->val = entry->val; trie_dbg_set("trie %p add_entry %s %d - %s at node %d pos %d\n", p, is_vla ? "subroot" : "leaf", p->info_count, name, idx, node->id); } p->info_buf_pos += info_size; p->info_count++; } st->array_idx = is_vla ? entry->vla.elem_count : -1; st->node_idx = idx; return is_vla ? MPP_TRIE_SUBROOT : MPP_TRIE_LEAF; } rk_s32 mpp_trie_get_entry(MppTrie trie, MppTrieStatus *st, const char *name, KmppEntry *entry) { MppTrieImpl *p = (MppTrieImpl *)trie; rk_s32 text_len; rk_s32 index = -1; const char *s; rk_s32 idx; if (!p || !st || !name) { mpp_loge_f("invalid param trie %p st %p name %p\n", p, st, name); return rk_nok; } if (!*name) { mpp_loge_f("invalid empty path\n"); return rk_nok; } st->node_idx = -1; st->array_idx = -1; st->name_pos = 0; if (entry) entry->val = 0; /* Find first ':digits' boundary */ for (s = name; *s; s++) { if (*s == ':' && s[1] >= '0' && s[1] <= '9' && s > name) { text_len = s - name; s++; index = 0; while (*s >= '0' && *s <= '9') { index = index * 10 + (*s - '0'); s++; } if (*s == ':') s++; break; } } /* No ':digits' found - entire name is text */ if (index < 0) text_len = s - name; idx = st->root_idx; { MppTrieWalk walk; rk_s32 i; walk.root = p->nodes; walk.tag = 0; walk.len = 0; walk.match = 0; for (i = 0; i < text_len && name[i]; i++) { rk_u32 key = name[i]; rk_s32 key0 = (key >> 4) & 0xf; rk_s32 key1 = key & 0xf; rk_s32 end = (i == text_len - 1); idx = mpp_trie_walk(&walk, idx, key, key0, 0); if (idx < 0) return rk_nok; idx = mpp_trie_walk(&walk, idx, key, key1, end); if (idx < 0 || (idx <= 0 && !end)) return rk_nok; } } /* Read entry payload */ if (entry) { MppTrieNode *node = &p->nodes[idx]; if (node->id >= 0) { char *base = p->info_buf ? (char *)p->info_buf : (char *)p->nodes; MppTrieInfo *info = (MppTrieInfo *)(base + node->id); entry->val = ((KmppEntry *)mpp_trie_info_ctx(info))->val; } trie_dbg_get("trie %p get_entry node %d %s idx -%d\n", p, idx, index >= 0 ? "subroot" : "leaf", index); } st->node_idx = idx; st->array_idx = index; st->name_pos = s - name; return index >= 0 ? MPP_TRIE_SUBROOT : MPP_TRIE_LEAF; } rk_s32 mpp_trie_for_each_entry(MppTrie trie, MppTrieInfoCb cb, void *ctx) { MppTrieImpl *p = (MppTrieImpl *)trie; char *base; rk_s32 i; if (!p || !cb) return rk_nok; base = p->info_buf ? (char *)p->info_buf : (char *)p->nodes; for (i = 0; i < p->node_used; i++) { MppTrieNode *n = &p->nodes[i]; if (i && !n->idx) continue; if (n->id < 0) continue; MppTrieInfo *info = (MppTrieInfo *)(base + n->id); const char *name = mpp_trie_info_name(info); void *data = mpp_trie_info_ctx(info); if (mpp_trie_info_is_self(info)) continue; rk_s32 walk; rk_s32 subroot = 0; if (n->sub_root) { subroot = n->idx; } else if (mpp_trie_get_node_at(p->nodes, 0, name) != n) { walk = n->idx; while (walk > 0 && !p->nodes[walk].sub_root) walk = p->nodes[walk].prev; if (walk > 0) subroot = walk; } if (cb(name, data, subroot, ctx)) return rk_nok; } return rk_ok; } rk_s32 mpp_trie_get_node_count(MppTrie trie) { MppTrieImpl *p = (MppTrieImpl *)trie; return (p) ? p->node_used : 0; } rk_s32 mpp_trie_get_info_count(MppTrie trie) { MppTrieImpl *p = (MppTrieImpl *)trie; return (p) ? p->info_count : 0; } rk_s32 mpp_trie_get_buf_size(MppTrie trie) { MppTrieImpl *p = (MppTrieImpl *)trie; return (p) ? p->buf_size : 0; } rk_s32 mpp_trie_get_name_max(MppTrie trie) { MppTrieImpl *p = (MppTrieImpl *)trie; return (p) ? p->info_name_max : 0; } const char *mpp_trie_get_name(MppTrie trie) { MppTrieImpl *p = (MppTrieImpl *)trie; return (p) ? p->name : NULL; } void *mpp_trie_get_node_root(MppTrie trie) { MppTrieImpl *p = (MppTrieImpl *)trie; return (p) ? p->nodes : NULL; } MppTrieInfo *mpp_trie_get_info(MppTrie trie, const char *name) { MppTrieImpl *p = (MppTrieImpl *)trie; MppTrieNode *node; if (!trie || !name) { mpp_loge_f("invalid trie %p name %p\n", trie, name); return NULL; } node = mpp_trie_get_node(p->nodes, name); if (!node || node->id < 0) return NULL; return (MppTrieInfo *)(((char *)p->nodes) + node->id); } MppTrieInfo *mpp_trie_get_info_first(MppTrie trie) { MppTrieImpl *p = (MppTrieImpl *)trie; return (p) ? (MppTrieInfo *)(((char *)p->nodes) + p->node_used * sizeof(MppTrieNode)) : NULL; } MppTrieInfo *mpp_trie_get_info_next(MppTrie trie, MppTrieInfo *info) { MppTrieImpl *p = (MppTrieImpl *)trie; if (!p || !info || info->index >= p->info_count - 1) return NULL; return (MppTrieInfo *)((char *)(info + 1) + info->str_len + info->ctx_len); } MppTrieInfo *mpp_trie_get_info_from_root(void *root, const char *name) { MppTrieNode *node; if (!root || !name) { mpp_loge_f("invalid root %p name %p\n", root, name); return NULL; } mpp_env_get_u32("mpp_trie_debug", &mpp_trie_debug, 0); node = mpp_trie_get_node((MppTrieNode *)root, name); if (!node || node->id < 0) return NULL; return (MppTrieInfo *)(((char *)root) + node->id); } void mpp_trie_dump(MppTrie trie, const char *func) { MppTrieImpl *p = (MppTrieImpl *)trie; char *base = p->info_buf ? (char *)p->info_buf : (char *)p->nodes; rk_s32 i; rk_s32 next_cnt[17]; rk_s32 tag_len[17]; memset(next_cnt, 0, sizeof(next_cnt)); memset(tag_len, 0, sizeof(tag_len)); mpp_logi("%s dumping trie %p\n", func, trie); mpp_logi("name %s size %d node %d info %d\n", p->name, p->buf_size, p->node_used, p->info_count); for (i = 0; i < p->node_used; i++) { MppTrieNode *node = &p->nodes[i]; rk_s32 valid_count = 0; rk_s32 j; if (i && !node->idx) continue; if (node->id >= 0) { MppTrieInfo *info = (MppTrieInfo *)((char *)base + node->id); /* check before and after last info */ if (node->id < (rk_s32)(p->node_used * sizeof(MppTrieNode))) mpp_logi("node %d key %x info %d - %s\n", node->idx, node->key, node->id, mpp_trie_info_name(info)); else mpp_logi("node %d key %x info %d - %s\n", node->idx, node->key, node->id, mpp_trie_info_name(info)); } else mpp_logi("node %d key %x\n", node->idx, node->key); if (node->tag_len) mpp_logi(" prev %d count %d tag %d - %llx\n", node->prev, node->next_cnt, node->tag_len, node->tag_val); else mpp_logi(" prev %d count %d\n", node->prev, node->next_cnt); for (j = 0; j < MPP_TRIE_KEY_MAX; j++) { if (node->next[j] > 0) { mpp_logi(" next %d:%d -> %d\n", valid_count, j, node->next[j]); valid_count++; } } next_cnt[valid_count]++; tag_len[node->tag_len]++; } mpp_logi("node | next | tag | used %d\n", p->node_used); for (i = 0; i < 17; i++) { if (next_cnt[i] || tag_len[i]) mpp_logi("%2d | %4d | %4d |\n", i, next_cnt[i], tag_len[i]); } mpp_logi("%s dumping node end\n", func, p->node_used); } void mpp_trie_timing_test(MppTrie trie) { MppTrieInfo *root = mpp_trie_get_info_first(trie); MppTrieInfo *info = root; rk_s64 sum = 0; rk_s32 loop = 0; do { rk_s64 start, end; start = mpp_time(); mpp_trie_get_info(trie, mpp_trie_info_name(info)); end = mpp_time(); info = mpp_trie_get_info_next(trie, info); sum += end - start; loop++; } while (info); mpp_logi("trie access %d info %lld us averga %lld us\n", loop, sum, sum / loop); }