76 lines
2 KiB
Go
76 lines
2 KiB
Go
// Copyright (c) 2025 Tulir Asokan
|
|
//
|
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
|
|
package exstrings
|
|
|
|
import (
|
|
"crypto/sha256"
|
|
"crypto/subtle"
|
|
"strings"
|
|
"unsafe"
|
|
)
|
|
|
|
// UnsafeBytes returns a byte slice that points to the same memory as the input string.
|
|
//
|
|
// The returned byte slice must not be modified.
|
|
func UnsafeBytes(str string) []byte {
|
|
return unsafe.Slice(unsafe.StringData(str), len(str))
|
|
}
|
|
|
|
// SHA256 returns the SHA-256 hash of the input string without copying the string.
|
|
func SHA256(str string) [32]byte {
|
|
return sha256.Sum256(UnsafeBytes(str))
|
|
}
|
|
|
|
// ConstantTimeEqual compares two strings using [subtle.ConstantTimeCompare] without copying the strings.
|
|
//
|
|
// Note that ConstantTimeCompare is not constant time if the strings are of different length.
|
|
func ConstantTimeEqual(a, b string) bool {
|
|
return subtle.ConstantTimeCompare(UnsafeBytes(a), UnsafeBytes(b)) == 1
|
|
}
|
|
|
|
// LongestSequenceOf returns the length of the longest contiguous sequence of a single rune in a string.
|
|
func LongestSequenceOf(a string, b rune) int {
|
|
// IndexRune has some optimizations, so use it to find the starting point
|
|
firstIndex := strings.IndexRune(a, b)
|
|
if firstIndex == -1 {
|
|
return 0
|
|
}
|
|
count := 0
|
|
maxCount := 0
|
|
for _, r := range a[firstIndex:] {
|
|
if r == b {
|
|
count++
|
|
if count > maxCount {
|
|
maxCount = count
|
|
}
|
|
} else {
|
|
count = 0
|
|
}
|
|
}
|
|
return maxCount
|
|
}
|
|
|
|
// LongestSequenceOfFunc returns the length of the longest contiguous sequence of runes in a string.
|
|
//
|
|
// If the provided function returns zero or higher, the return value is added to the current count.
|
|
// If the return value is negative, the count is reset to zero.
|
|
func LongestSequenceOfFunc(a string, fn func(b rune) int) int {
|
|
count := 0
|
|
maxCount := 0
|
|
for _, r := range a {
|
|
val := fn(r)
|
|
if val < 0 {
|
|
count = 0
|
|
} else {
|
|
count += val
|
|
if count > maxCount {
|
|
maxCount = count
|
|
}
|
|
}
|
|
}
|
|
return maxCount
|
|
}
|