Files
mesh-controller/vendor/github.com/nats-io/nats-server/v2/server/stree/parts.go
T
jochen be92762969 Give every test a bus of its own, at the release the mesh runs (hq ADR 0237)
The live tests reached one shared bus and assert, read and remove the mesh's own objects by
their fixed names, so packages run in parallel deleted what each other read and the suite
passed only one package at a time; a red suite read as noise. internal/testbus starts a server
per test, linked in at the nats-server release go.mod pins, and a test holds that pin to the
catalogue's bus image and to the facts snapshot's bus when there is one, so the tests never run
a bus the mesh does not. The waiter test read a timing (the most connections held at one look)
and now reads the state it means (the fewest held across the wait). make check runs the packages
in parallel under the race detector, with a timeout.
2026-10-06 21:17:02 +02:00

148 lines
4.2 KiB
Go

// Copyright 2023-2025 The NATS Authors
// 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 stree
import (
"bytes"
)
// genParts will break a filter subject up into parts.
// We need to break this up into chunks based on wildcards, either pwc '*' or fwc '>'.
// We do not care about other tokens per se, just parts that are separated by wildcards with an optional end fwc.
func genParts(filter []byte, parts [][]byte) [][]byte {
var start int
for i, e := 0, len(filter)-1; i < len(filter); i++ {
if filter[i] == tsep {
// See if next token is pwc. Either internal or end pwc.
if i < e && filter[i+1] == pwc && (i+2 <= e && filter[i+2] == tsep || i+1 == e) {
if i > start {
parts = append(parts, filter[start:i+1])
}
parts = append(parts, filter[i+1:i+2])
i++ // Skip pwc
if i+2 <= e {
i++ // Skip next tsep from next part too.
}
start = i + 1
} else if i < e && filter[i+1] == fwc && i+1 == e {
if i > start {
parts = append(parts, filter[start:i+1])
}
parts = append(parts, filter[i+1:i+2])
i++ // Skip fwc
start = i + 1
}
} else if filter[i] == pwc || filter[i] == fwc {
// Wildcard must be at the start or preceded by tsep.
if prev := i - 1; prev >= 0 && filter[prev] != tsep {
continue
}
// Wildcard must be at the end or followed by tsep.
if next := i + 1; next == e || next < e && filter[next] != tsep {
continue
}
// Full wildcard must be terminal.
if filter[i] == fwc && i < e {
break
}
// We start with a pwc or fwc.
parts = append(parts, filter[i:i+1])
if i+1 <= e {
i++ // Skip next tsep from next part too.
}
start = i + 1
}
}
if start < len(filter) {
// Check to see if we need to eat a leading tsep.
if filter[start] == tsep {
start++
}
parts = append(parts, filter[start:])
}
return parts
}
// Match our parts against a fragment, which could be prefix for nodes or a suffix for leafs.
func matchParts(parts [][]byte, frag []byte) ([][]byte, bool) {
lf := len(frag)
if lf == 0 {
return parts, true
}
var si int
lpi := len(parts) - 1
for i, part := range parts {
if si >= lf {
return parts[i:], true
}
lp := len(part)
// Check for pwc or fwc place holders.
if lp == 1 {
if part[0] == pwc {
index := bytes.IndexByte(frag[si:], tsep)
// We are trying to match pwc and did not find our tsep.
// Will need to move to next node from caller.
if index < 0 {
if i == lpi {
return nil, true
}
return parts[i:], true
}
si += index + 1
continue
} else if part[0] == fwc {
// If we are here we should be good.
return nil, true
}
}
end := min(si+lp, lf)
// If part is bigger then the remaining fragment, adjust to a portion on the part.
if si+lp > end {
// Frag is smaller then part itself.
part = part[:end-si]
}
if !bytes.Equal(part, frag[si:end]) {
return parts, false
}
// If we still have a portion of the fragment left, update and continue.
if end < lf {
si = end
continue
}
// If we matched a partial, do not move past current part
// but update the part to what was consumed. This allows upper layers to continue.
if end < si+lp {
if end >= lf {
// Create a copy before modifying. Reuse slice capacity available at the
// end of the parts slice, since this saves us additional allocations.
lp := len(parts)
parts = append(parts[lp:], parts[:lp]...)
parts[i] = parts[i][lf-si:]
} else {
i++
}
return parts[i:], true
}
if i == lpi {
return nil, true
}
// If we are here we are not the last part which means we have a wildcard
// gap, so we need to match anything up to next tsep.
si += len(part)
}
return parts, false
}