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  }