-
Notifications
You must be signed in to change notification settings - Fork 418
Expand file tree
/
Copy pathprefix.go
More file actions
101 lines (88 loc) · 2.4 KB
/
Copy pathprefix.go
File metadata and controls
101 lines (88 loc) · 2.4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
package main
import (
"bytes"
"encoding/base32"
"fmt"
"strings"
)
type prefixMatchFunc func(string) (func([]byte) bool, error)
func matchAnyOf(prefixes []string, match prefixMatchFunc) (func([]byte) bool, error) {
if len(prefixes) == 0 {
return nil, fmt.Errorf("at least one prefix required")
}
if len(prefixes) == 1 {
return match(prefixes[0])
}
tests := make([]func([]byte) bool, len(prefixes))
for i, p := range prefixes {
var err error
tests[i], err = match(p)
if err != nil {
return nil, err
}
}
return func(p []byte) bool {
for _, test := range tests {
if test(p) {
return true
}
}
return false
}, nil
}
// hasPrefix returns a function that checks if the input has the specified encoded prefix.
func hasPrefix(prefix string, enc *base32.Encoding) (func(input []byte) bool, error) {
prefixBytes, bits, err := decodePrefixBits(prefix, enc)
if err != nil {
return nil, err
}
return hasPrefixBits(prefixBytes, bits), nil
}
// decodePrefixBits returns base32-decoded prefix and number of decoded bits.
func decodePrefixBits(prefix string, enc *base32.Encoding) ([]byte, int, error) {
decodedBits := 5 * len(prefix)
quantums := (len(prefix) + 7) / 8
zeroChar := enc.EncodeToString([]byte{0})[0:1]
prefix += strings.Repeat(zeroChar, quantums*8-len(prefix))
buf := make([]byte, quantums*5)
_, err := enc.Decode(buf, []byte(prefix))
if err != nil {
return nil, 0, err
}
return buf, decodedBits, err
}
// hasPrefixBits returns a function that checks if the input has the specified prefix bits.
func hasPrefixBits(prefix []byte, bits int) func(input []byte) bool {
if len(prefix) == 0 || len(prefix) > 32 {
panic("invalid prefix")
}
if bits <= 0 || bits > 256 || bits > len(prefix)*8 {
panic("invalid bits")
}
if bits%8 == 0 {
return func(b []byte) bool {
return bytes.HasPrefix(b, prefix)
}
}
prefixBytes := bits / 8
shift := 8 - (bits % 8)
tailByte := prefix[prefixBytes] >> shift
prefix = prefix[:prefixBytes]
return func(b []byte) bool {
return len(b) > prefixBytes && // must be long enough to check tail byte
bytes.Equal(b[:prefixBytes], prefix) &&
b[prefixBytes]>>shift == tailByte
}
}
func longestMatching(prefixes []string, value string) string {
longest := ""
for _, p := range prefixes {
if strings.HasPrefix(value, p) && len(p) > len(longest) {
longest = p
}
}
if longest == "" {
panic("no matching prefix")
}
return longest
}