github.com/liquid-dev/text@v0.3.3-liquid/internal/tag/tag.go (about)

     1  // Copyright 2015 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  // Package tag contains functionality handling tags and related data.
     6  package tag // import "github.com/liquid-dev/text/internal/tag"
     7  
     8  import "sort"
     9  
    10  // An Index converts tags to a compact numeric value.
    11  //
    12  // All elements are of size 4. Tags may be up to 4 bytes long. Excess bytes can
    13  // be used to store additional information about the tag.
    14  type Index string
    15  
    16  // Elem returns the element data at the given index.
    17  func (s Index) Elem(x int) string {
    18  	return string(s[x*4 : x*4+4])
    19  }
    20  
    21  // Index reports the index of the given key or -1 if it could not be found.
    22  // Only the first len(key) bytes from the start of the 4-byte entries will be
    23  // considered for the search and the first match in Index will be returned.
    24  func (s Index) Index(key []byte) int {
    25  	n := len(key)
    26  	// search the index of the first entry with an equal or higher value than
    27  	// key in s.
    28  	index := sort.Search(len(s)/4, func(i int) bool {
    29  		return cmp(s[i*4:i*4+n], key) != -1
    30  	})
    31  	i := index * 4
    32  	if cmp(s[i:i+len(key)], key) != 0 {
    33  		return -1
    34  	}
    35  	return index
    36  }
    37  
    38  // Next finds the next occurrence of key after index x, which must have been
    39  // obtained from a call to Index using the same key. It returns x+1 or -1.
    40  func (s Index) Next(key []byte, x int) int {
    41  	if x++; x*4 < len(s) && cmp(s[x*4:x*4+len(key)], key) == 0 {
    42  		return x
    43  	}
    44  	return -1
    45  }
    46  
    47  // cmp returns an integer comparing a and b lexicographically.
    48  func cmp(a Index, b []byte) int {
    49  	n := len(a)
    50  	if len(b) < n {
    51  		n = len(b)
    52  	}
    53  	for i, c := range b[:n] {
    54  		switch {
    55  		case a[i] > c:
    56  			return 1
    57  		case a[i] < c:
    58  			return -1
    59  		}
    60  	}
    61  	switch {
    62  	case len(a) < len(b):
    63  		return -1
    64  	case len(a) > len(b):
    65  		return 1
    66  	}
    67  	return 0
    68  }
    69  
    70  // Compare returns an integer comparing a and b lexicographically.
    71  func Compare(a string, b []byte) int {
    72  	return cmp(Index(a), b)
    73  }
    74  
    75  // FixCase reformats b to the same pattern of cases as form.
    76  // If returns false if string b is malformed.
    77  func FixCase(form string, b []byte) bool {
    78  	if len(form) != len(b) {
    79  		return false
    80  	}
    81  	for i, c := range b {
    82  		if form[i] <= 'Z' {
    83  			if c >= 'a' {
    84  				c -= 'z' - 'Z'
    85  			}
    86  			if c < 'A' || 'Z' < c {
    87  				return false
    88  			}
    89  		} else {
    90  			if c <= 'Z' {
    91  				c += 'z' - 'Z'
    92  			}
    93  			if c < 'a' || 'z' < c {
    94  				return false
    95  			}
    96  		}
    97  		b[i] = c
    98  	}
    99  	return true
   100  }