get.pme.sh/pnats@v0.0.0-20240304004023-26bb5a137ed0/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 // We start with a pwc or fwc. 49 parts = append(parts, filter[i:i+1]) 50 if i+1 <= e { 51 i++ // Skip next tsep from next part too. 52 } 53 start = i + 1 54 } 55 } 56 if start < len(filter) { 57 // Check to see if we need to eat a leading tsep. 58 if filter[start] == tsep { 59 start++ 60 } 61 parts = append(parts, filter[start:]) 62 } 63 return parts 64 } 65 66 // Match our parts against a fragment, which could be prefix for nodes or a suffix for leafs. 67 func matchParts(parts [][]byte, frag []byte) ([][]byte, bool) { 68 if len(frag) == 0 { 69 return parts, true 70 } 71 72 var si int 73 lpi := len(parts) - 1 74 lf := len(frag) 75 76 for i, part := range parts { 77 if si >= lf { 78 return parts[i:], true 79 } 80 lp := len(part) 81 // Check for pwc or fwc place holders. 82 if lp == 1 { 83 if part[0] == pwc { 84 index := bytes.IndexByte(frag[si:], tsep) 85 // We are trying to match pwc and did not find our tsep. 86 // Will need to move to next node from caller. 87 if index < 0 { 88 if i == lpi { 89 return nil, true 90 } 91 return parts[i:], true 92 } 93 si += index + 1 94 continue 95 } else if part[0] == fwc { 96 // If we are here we should be good. 97 return nil, true 98 } 99 } 100 end := min(si+lp, len(frag)) 101 // If part is bigger then the fragment, adjust to a portion on the part. 102 if partialPart := lp > end; partialPart { 103 // Frag is smaller then part itself. 104 part = part[:end-si] 105 } 106 if !bytes.Equal(part, frag[si:end]) { 107 return parts, false 108 } 109 // If we still have a portion of the fragment left, update and continue. 110 if end < lf { 111 si = end 112 continue 113 } 114 // If we matched a partial, do not move past current part 115 // but update the part to what was consumed. This allows upper layers to continue. 116 if end < lp { 117 if end >= lf { 118 parts = append([][]byte{}, parts...) // Create a copy before modifying. 119 parts[i] = parts[i][lf-si:] 120 } else { 121 i++ 122 } 123 return parts[i:], true 124 } 125 if i == lpi { 126 return nil, true 127 } 128 // If we are here we are not the last part which means we have a wildcard 129 // gap, so we need to match anything up to next tsep. 130 si += len(part) 131 } 132 return parts, false 133 }