forked from ipfs/go-ds-flatfs
-
Notifications
You must be signed in to change notification settings - Fork 0
/
shard.go
151 lines (129 loc) · 3.46 KB
/
shard.go
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
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
package flatfs
import (
"fmt"
"os"
"path/filepath"
"strconv"
"strings"
)
var IPFS_DEF_SHARD = NextToLast(2)
var IPFS_DEF_SHARD_STR = IPFS_DEF_SHARD.String()
const PREFIX = "/repo/flatfs/shard/"
const SHARDING_FN = "SHARDING"
const README_FN = "_README"
type ShardIdV1 struct {
funName string
param int
fun ShardFunc
}
func (f *ShardIdV1) String() string {
return fmt.Sprintf("%sv1/%s/%d", PREFIX, f.funName, f.param)
}
func (f *ShardIdV1) Func() ShardFunc {
return f.fun
}
// Prefix returns a sharding function taking the first prefixLen characters of the key.
// If too short, the key is padded with "_".
func Prefix(prefixLen int) *ShardIdV1 {
padding := strings.Repeat("_", prefixLen)
return &ShardIdV1{
funName: "prefix",
param: prefixLen,
fun: func(noslash string) string {
return (noslash + padding)[:prefixLen]
},
}
}
// Prefix returns a sharding function taking the last suffixLen characters of the key.
// If too short, the key is padded with "_".
func Suffix(suffixLen int) *ShardIdV1 {
padding := strings.Repeat("_", suffixLen)
return &ShardIdV1{
funName: "suffix",
param: suffixLen,
fun: func(noslash string) string {
str := padding + noslash
return str[len(str)-suffixLen:]
},
}
}
// Prefix returns a sharding function taking the suffixLen characters of the key
// before the very last character.
// If too short, the key is padded with "_".
func NextToLast(suffixLen int) *ShardIdV1 {
padding := strings.Repeat("_", suffixLen+1)
return &ShardIdV1{
funName: "next-to-last",
param: suffixLen,
fun: func(noslash string) string {
str := padding + noslash
offset := len(str) - suffixLen - 1
return str[offset : offset+suffixLen]
},
}
}
func ParseShardFunc(str string) (*ShardIdV1, error) {
str = strings.TrimSpace(str)
if len(str) == 0 {
return nil, fmt.Errorf("empty shard identifier")
}
trimmed := strings.TrimPrefix(str, PREFIX)
if str == trimmed { // nothing trimmed
return nil, fmt.Errorf("invalid or no prefix in shard identifier: %s", str)
}
str = trimmed
parts := strings.Split(str, "/")
if len(parts) != 3 {
return nil, fmt.Errorf("invalid shard identifier: %s", str)
}
version := parts[0]
if version != "v1" {
return nil, fmt.Errorf("expected 'v1' for version string got: %s", version)
}
funName := parts[1]
param, err := strconv.Atoi(parts[2])
if err != nil {
return nil, fmt.Errorf("invalid parameter: %v", err)
}
switch funName {
case "prefix":
return Prefix(param), nil
case "suffix":
return Suffix(param), nil
case "next-to-last":
return NextToLast(param), nil
default:
return nil, fmt.Errorf("expected 'prefix', 'suffix' or 'next-to-last' got: %s", funName)
}
}
func ReadShardFunc(dir string) (*ShardIdV1, error) {
buf, err := os.ReadFile(filepath.Join(dir, SHARDING_FN))
if os.IsNotExist(err) {
return nil, ErrShardingFileMissing
} else if err != nil {
return nil, err
}
return ParseShardFunc(string(buf))
}
func WriteShardFunc(dir string, id *ShardIdV1) error {
file, err := os.OpenFile(filepath.Join(dir, SHARDING_FN), os.O_WRONLY|os.O_CREATE|os.O_EXCL, 0666)
if err != nil {
return err
}
defer file.Close()
_, err = file.WriteString(id.String())
if err != nil {
return err
}
_, err = file.WriteString("\n")
return err
}
func WriteReadme(dir string, id *ShardIdV1) error {
if id.String() == IPFS_DEF_SHARD.String() {
err := os.WriteFile(filepath.Join(dir, README_FN), []byte(README_IPFS_DEF_SHARD), 0444)
if err != nil {
return err
}
}
return nil
}