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  }