github.com/nats-io/nats-server/v2@v2.11.0-preview.2/server/stree/parts.go (about) 1 // Copyright 2023-2024 The NATS Authors 2 // Licensed under the Apache License, Version 2.0 (the "License"); 3 // you may not use this file except in compliance with the License. 4 // You may obtain a copy of the License at 5 // 6 // http://www.apache.org/licenses/LICENSE-2.0 7 // 8 // Unless required by applicable law or agreed to in writing, software 9 // distributed under the License is distributed on an "AS IS" BASIS, 10 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 // See the License for the specific language governing permissions and 12 // limitations under the License. 13 14 package stree 15 16 import ( 17 "bytes" 18 ) 19 20 // genParts will break a filter subject up into parts. 21 // We need to break this up into chunks based on wildcards, either pwc '*' or fwc '>'. 22 // We do not care about other tokens per se, just parts that are separated by wildcards with an optional end fwc. 23 func genParts(filter []byte, parts [][]byte) [][]byte { 24 var start int 25 for i, e := 0, len(filter)-1; i < len(filter); i++ { 26 if filter[i] == tsep { 27 // See if next token is pwc. Either internal or end pwc. 28 if i < e && filter[i+1] == pwc && (i+2 <= e && filter[i+2] == tsep || i+1 == e) { 29 if i > start { 30 parts = append(parts, filter[start:i+1]) 31 } 32 parts = append(parts, filter[i+1:i+2]) 33 i++ // Skip pwc 34 if i+2 <= e { 35 i++ // Skip next tsep from next part too. 36 } 37 start = i + 1 38 } else if i < e && filter[i+1] == fwc && i+1 == e { 39 // We have a fwc 40 if i > start { 41 parts = append(parts, filter[start:i+1]) 42 } 43 parts = append(parts, filter[i+1:i+2]) 44 i++ // Skip fwc 45 start = i + 1 46 } 47 } else if filter[i] == pwc || filter[i] == fwc { 48 // Wildcard must be at the start or preceded by tsep. 49 if prev := i - 1; prev >= 0 && filter[prev] != tsep { 50 continue 51 } 52 // Wildcard must be at the end or followed by tsep. 53 if next := i + 1; next == e || next < e && filter[next] != tsep { 54 continue 55 } 56 // We start with a pwc or fwc. 57 parts = append(parts, filter[i:i+1]) 58 if i+1 <= e { 59 i++ // Skip next tsep from next part too. 60 } 61 start = i + 1 62 } 63 } 64 if start < len(filter) { 65 // Check to see if we need to eat a leading tsep. 66 if filter[start] == tsep { 67 start++ 68 } 69 parts = append(parts, filter[start:]) 70 } 71 return parts 72 } 73 74 // Match our parts against a fragment, which could be prefix for nodes or a suffix for leafs. 75 func matchParts(parts [][]byte, frag []byte) ([][]byte, bool) { 76 lf := len(frag) 77 if lf == 0 { 78 return parts, true 79 } 80 81 var si int 82 lpi := len(parts) - 1 83 84 for i, part := range parts { 85 if si >= lf { 86 return parts[i:], true 87 } 88 lp := len(part) 89 // Check for pwc or fwc place holders. 90 if lp == 1 { 91 if part[0] == pwc { 92 index := bytes.IndexByte(frag[si:], tsep) 93 // We are trying to match pwc and did not find our tsep. 94 // Will need to move to next node from caller. 95 if index < 0 { 96 if i == lpi { 97 return nil, true 98 } 99 return parts[i:], true 100 } 101 si += index + 1 102 continue 103 } else if part[0] == fwc { 104 // If we are here we should be good. 105 return nil, true 106 } 107 } 108 end := min(si+lp, lf) 109 // If part is bigger then the remaining fragment, adjust to a portion on the part. 110 if si+lp > end { 111 // Frag is smaller then part itself. 112 part = part[:end-si] 113 } 114 if !bytes.Equal(part, frag[si:end]) { 115 return parts, false 116 } 117 // If we still have a portion of the fragment left, update and continue. 118 if end < lf { 119 si = end 120 continue 121 } 122 // If we matched a partial, do not move past current part 123 // but update the part to what was consumed. This allows upper layers to continue. 124 if end < si+lp { 125 if end >= lf { 126 parts = append([][]byte{}, parts...) // Create a copy before modifying. 127 parts[i] = parts[i][lf-si:] 128 } else { 129 i++ 130 } 131 return parts[i:], true 132 } 133 if i == lpi { 134 return nil, true 135 } 136 // If we are here we are not the last part which means we have a wildcard 137 // gap, so we need to match anything up to next tsep. 138 si += len(part) 139 } 140 return parts, false 141 }