From f8bd12e4b7b1e32ff01965f5232c27433ad18414 Mon Sep 17 00:00:00 2001 From: jochen Date: Tue, 3 Nov 2026 15:28:43 +0100 Subject: [PATCH] Encode each part of an index key, so no two keys meet and every part is a bus token (issue 1966 S4) A raw value holding "." collided with a longer path, and a space is refused by the bus: every byte outside [A-Za-z0-9_-] of a field value or an instance path part is written =XX (review round 1 of issue 1966 B1). --- go/buscalls/buscalls.go | 33 +++++++++++++++++++++++++++++---- go/packages_list_test.go | 26 ++++++++++++++++++++++++-- 2 files changed, 53 insertions(+), 6 deletions(-) diff --git a/go/buscalls/buscalls.go b/go/buscalls/buscalls.go index 3282903..44e007d 100644 --- a/go/buscalls/buscalls.go +++ b/go/buscalls/buscalls.go @@ -10,6 +10,7 @@ package buscalls import ( "encoding/json" + "fmt" "strings" "time" ) @@ -294,11 +295,35 @@ type IndexEntry struct { Sequence uint64 `json:"sequence"` } -// IndexKey is the key of an instance in a declared index: the values of the index's fields, in its order, joined by -// ".", then the instance's path with each "/" written "." (`17950.listing.a17`). The owner's watcher and the tool -// runner's reindex both write it, so a reindex leaves the index as the watcher keeps it. +// IndexKey is the key of an instance in a declared index: the values of the index's fields, in its order, then the +// parts of the instance's path, all joined by ".". Each value and each path part is encoded (keyToken): every byte +// outside [A-Za-z0-9_-] is written "=" and two upper-case hex digits ("." is "=2E", " " is "=20", "=" is "=3D"), so two +// different keys never meet and every part is a token the bus takes. `17950.listing.a17` is the key of price 17950 +// and instance `listing/a17`. The owner's watcher and the tool runner's reindex both write it, so a reindex leaves the +// index as the watcher keeps it. func IndexKey(fields []string, instance string) string { - return strings.Join(append(append([]string{}, fields...), strings.ReplaceAll(instance, "/", ".")), ".") + parts := make([]string, 0, len(fields)+2) + for _, f := range fields { + parts = append(parts, keyToken(f)) + } + for _, p := range strings.Split(instance, "/") { + parts = append(parts, keyToken(p)) + } + return strings.Join(parts, ".") +} + +// keyToken is v with every byte outside [A-Za-z0-9_-] written "=" and two upper-case hex digits (IndexKey). +func keyToken(v string) string { + var b strings.Builder + for i := 0; i < len(v); i++ { + c := v[i] + if c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z' || c >= '0' && c <= '9' || c == '_' || c == '-' { + b.WriteByte(c) + continue + } + fmt.Fprintf(&b, "=%02X", c) + } + return b.String() } // QueryAnswer is query's answer. diff --git a/go/packages_list_test.go b/go/packages_list_test.go index ee965ae..c23b74e 100644 --- a/go/packages_list_test.go +++ b/go/packages_list_test.go @@ -112,8 +112,30 @@ func TestMeshSDKHoldsOnlyWhatEveryModuleShares(t *testing.T) { } // 1966/AC5: an index's key is its fields' values, then the instance's path, joined by "."; its entry names the // instance and the sequence. - if k := buscalls.IndexKey([]string{"17950", "volvo"}, "listing/a17"); k != "17950.volvo.listing.a17" { - t.Errorf("an index key is %q", k) + for _, c := range []struct { + fields []string + instance string + want string + }{ + {[]string{"17950", "volvo"}, "listing/a17", "17950.volvo.listing.a17"}, + // A value holding "." and one that does not meet in no key. + {[]string{"a.b"}, "c", "a=2Eb.c"}, + {[]string{"a"}, "b/c", "a.b.c"}, + // A title with spaces is a token the bus takes; "=" itself is encoded. + {[]string{"How it is checked", "x=y"}, "decision/0290", "How=20it=20is=20checked.x=3Dy.decision.0290"}, + } { + k := buscalls.IndexKey(c.fields, c.instance) + if k != c.want { + t.Errorf("the index key of %q and %q is %q, want %q", c.fields, c.instance, k, c.want) + } + for _, token := range strings.Split(k, ".") { + if token == "" || strings.ContainsAny(token, " *>\t") { + t.Errorf("the index key %q holds a token the bus refuses: %q", k, token) + } + } + } + if buscalls.IndexKey([]string{"a.b"}, "c") == buscalls.IndexKey([]string{"a"}, "b/c") { + t.Error("two different index keys meet") } entry, _ := json.Marshal(buscalls.IndexEntry{Instance: "listing/a17", Sequence: 3}) if string(entry) != `{"instance":"listing/a17","sequence":3}` {