star/bed/searcher/pattern.go
2025-04-26 19:33:14 +08:00

156 lines
2.8 KiB
Go

package searcher
import (
"errors"
"unicode/utf8"
)
func patternToTarget(pattern string) ([]byte, error) {
if len(pattern) > 3 && pattern[0] == '0' {
switch pattern[1] {
case 'x', 'X':
return decodeHexLiteral(pattern)
case 'b', 'B':
return decodeBinLiteral(pattern)
}
}
return unescapePattern(pattern), nil
}
func decodeHexLiteral(pattern string) ([]byte, error) {
bs := make([]byte, 0, len(pattern)/2+1)
var c byte
var lower bool
for i := 2; i < len(pattern); i++ {
if !isHex(pattern[i]) {
return nil, errors.New("invalid hex pattern: " + pattern)
}
c = c<<4 | hexToDigit(pattern[i])
if lower {
bs = append(bs, c)
c = 0
}
lower = !lower
}
if lower {
bs = append(bs, c<<4)
}
return bs, nil
}
func decodeBinLiteral(pattern string) ([]byte, error) {
bs := make([]byte, 0, len(pattern)/16+1)
var c byte
var bits int
for i := 2; i < len(pattern); i++ {
if !isBin(pattern[i]) {
return nil, errors.New("invalid bin pattern: " + pattern)
}
c = c<<1 | hexToDigit(pattern[i])
bits++
if bits == 8 {
bits = 0
bs = append(bs, c)
c = 0
}
}
if bits > 0 {
bs = append(bs, c<<uint(8-bits))
}
return bs, nil
}
func unescapePattern(pattern string) []byte {
var escape bool
var buf [4]byte
bs := make([]byte, 0, len(pattern))
for i := 0; i < len(pattern); i++ {
b := pattern[i]
if escape {
switch b {
case '0':
bs = append(bs, 0)
case 'a':
bs = append(bs, '\a')
case 'b':
bs = append(bs, '\b')
case 'f':
bs = append(bs, '\f')
case 'n':
bs = append(bs, '\n')
case 'r':
bs = append(bs, '\r')
case 't':
bs = append(bs, '\t')
case 'v':
bs = append(bs, '\v')
case 'x', 'u', 'U':
var n int
switch b {
case 'x':
n = 2
case 'u':
n = 4
case 'U':
n = 8
}
appended := true
var c rune
if i+n < len(pattern) {
for k := 1; k <= n; k++ {
if !isHex(pattern[i+k]) {
appended = false
break
}
c = c<<4 | rune(hexToDigit(pattern[i+k]))
}
if appended {
if b == 'x' {
bs = append(bs, byte(c))
} else {
n := utf8.EncodeRune(buf[:], c)
bs = append(bs, buf[:n]...)
}
i += n
}
}
if !appended {
bs = append(bs, b)
}
default:
bs = append(bs, b)
}
escape = false
} else if b == '\\' {
escape = true
} else {
bs = append(bs, b)
}
}
if escape {
bs = append(bs, '\\')
}
return bs
}
func isHex(b byte) bool {
return '0' <= b && b <= '9' || 'A' <= b && b <= 'F' || 'a' <= b && b <= 'f'
}
func hexToDigit(b byte) byte {
switch {
case '0' <= b && b <= '9':
return b - '0'
case 'A' <= b && b <= 'F':
return b - 'A' + 10
case 'a' <= b && b <= 'f':
return b - 'a' + 10
default:
return 0
}
}
func isBin(b byte) bool {
return b == '0' || b == '1'
}