From 4bbaaa60a501202924ccff7577e8b85f682712c8 Mon Sep 17 00:00:00 2001 From: Konnyaku <50104361+AKonnyaku@users.noreply.github.com> Date: Sun, 1 Feb 2026 06:00:17 +0800 Subject: [PATCH] perf: optimize token index lookup in enforcer --- enforcer.go | 10 ++-------- management_api.go | 5 +---- model/assertion.go | 8 ++++++++ model/model.go | 6 ++++++ model/model_test.go | 29 +++++++++++++++++++++++++++++ 5 files changed, 46 insertions(+), 12 deletions(-) diff --git a/enforcer.go b/enforcer.go index a6bf1740a..27beafbe6 100644 --- a/enforcer.go +++ b/enforcer.go @@ -740,14 +740,8 @@ func (e *Enforcer) enforce(matcher string, explains *[]string, rvals ...interfac expString = util.EscapeStringLiterals(util.RemoveComments(util.EscapeAssertion(matcher))) } - rTokens := make(map[string]int, len(e.model["r"][rType].Tokens)) - for i, token := range e.model["r"][rType].Tokens { - rTokens[token] = i - } - pTokens := make(map[string]int, len(e.model["p"][pType].Tokens)) - for i, token := range e.model["p"][pType].Tokens { - pTokens[token] = i - } + rTokens := e.model["r"][rType].TokenIndexMap + pTokens := e.model["p"][pType].TokenIndexMap if e.acceptJsonRequest { // try to parse all request values from json to map[string]interface{} diff --git a/management_api.go b/management_api.go index 7a8f768ee..529f08573 100644 --- a/management_api.go +++ b/management_api.go @@ -167,10 +167,7 @@ func (e *Enforcer) GetFilteredNamedPolicyWithMatcher(ptype string, matcher strin return res, err } - pTokens := make(map[string]int, len(e.model["p"][ptype].Tokens)) - for i, token := range e.model["p"][ptype].Tokens { - pTokens[token] = i - } + pTokens := e.model["p"][ptype].TokenIndexMap parameters := enforceParameters{ pTokens: pTokens, diff --git a/model/assertion.go b/model/assertion.go index b5b8e9199..ef16b7d78 100644 --- a/model/assertion.go +++ b/model/assertion.go @@ -35,6 +35,7 @@ type Assertion struct { CondRM rbac.ConditionalRoleManager FieldIndexMap map[string]int FieldIndexMutex sync.RWMutex + TokenIndexMap map[string]int } func (ast *Assertion) buildIncrementalRoleLinks(rm rbac.RoleManager, op PolicyOp, rules [][]string) error { @@ -195,5 +196,12 @@ func (ast *Assertion) copy() *Assertion { CondRM: ast.CondRM, } + if ast.TokenIndexMap != nil { + newAst.TokenIndexMap = make(map[string]int) + for k, v := range ast.TokenIndexMap { + newAst.TokenIndexMap[k] = v + } + } + return newAst } diff --git a/model/model.go b/model/model.go index b541e1b84..0a14d6e0e 100644 --- a/model/model.go +++ b/model/model.go @@ -80,13 +80,19 @@ func (model Model) AddDef(sec string, key string, value string) bool { if sec == "r" || sec == "p" { ast.Tokens = strings.Split(ast.Value, ",") + ast.TokenIndexMap = make(map[string]int, len(ast.Tokens)) for i := range ast.Tokens { ast.Tokens[i] = key + "_" + strings.TrimSpace(ast.Tokens[i]) + ast.TokenIndexMap[ast.Tokens[i]] = i } } else if sec == "g" { ast.ParamsTokens = getParamsToken(ast.Value) ast.Tokens = strings.Split(ast.Value, ",") ast.Tokens = ast.Tokens[:len(ast.Tokens)-len(ast.ParamsTokens)] + ast.TokenIndexMap = make(map[string]int, len(ast.Tokens)) + for i, token := range ast.Tokens { + ast.TokenIndexMap[token] = i + } } else { ast.Value = util.RemoveComments(util.EscapeAssertion(ast.Value)) } diff --git a/model/model_test.go b/model/model_test.go index aec970362..b22bf6e65 100644 --- a/model/model_test.go +++ b/model/model_test.go @@ -119,12 +119,41 @@ func TestModel_AddDef(t *testing.T) { if !ok { t.Errorf("non empty assertion should be added") } + + ast := m["r"]["r"] + if len(ast.TokenIndexMap) != 3 { + t.Errorf("TokenIndexMap length should be 3, got %d", len(ast.TokenIndexMap)) + } + if ast.TokenIndexMap["r_sub"] != 0 || ast.TokenIndexMap["r_obj"] != 1 || ast.TokenIndexMap["r_act"] != 2 { + t.Errorf("TokenIndexMap values are incorrect: %v", ast.TokenIndexMap) + } + ok = m.AddDef(s, s, "") if ok { t.Errorf("empty assertion value should not be added") } } +func TestAssertion_Copy(t *testing.T) { + m := NewModel() + _ = m.AddDef("r", "r", "sub, obj, act") + ast := m["r"]["r"] + newAst := ast.copy() + + if len(newAst.TokenIndexMap) != 3 { + t.Errorf("Copied TokenIndexMap length should be 3, got %d", len(newAst.TokenIndexMap)) + } + if newAst.TokenIndexMap["r_sub"] != 0 || newAst.TokenIndexMap["r_obj"] != 1 || newAst.TokenIndexMap["r_act"] != 2 { + t.Errorf("Copied TokenIndexMap values are incorrect: %v", newAst.TokenIndexMap) + } + + // Verify it's a deep copy + newAst.TokenIndexMap["r_sub"] = 10 + if ast.TokenIndexMap["r_sub"] != 0 { + t.Errorf("Deep copy failed, modifying copy affected original") + } +} + func TestModelToTest(t *testing.T) { testModelToText(t, "r.sub == p.sub && r.obj == p.obj && r_func(r.act, p.act) && testr_func(r.act, p.act)", "r_sub == p_sub && r_obj == p_obj && r_func(r_act, p_act) && testr_func(r_act, p_act)") testModelToText(t, "r.sub == p.sub && r.obj == p.obj && p_func(r.act, p.act) && testp_func(r.act, p.act)", "r_sub == p_sub && r_obj == p_obj && p_func(r_act, p_act) && testp_func(r_act, p_act)")