This repository has been archived by the owner on Dec 17, 2022. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 1
/
value.go
173 lines (141 loc) · 3.03 KB
/
value.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
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
package infinite
import (
"bytes"
"encoding/base64"
"fmt"
"sort"
"strconv"
"strings"
)
const maxNameLen = 255
// encodeValue encodes the given value to names.
//
// The value is first split into chunks. These chunks are encoded using base64.
// They are then enumerated. These will be returned as the names, to be saved as
// files to store data.
func encodeValue(bb []byte) ([]string, error) {
var (
nn []name
buf bytes.Buffer
maxBufLen int
)
next := func() {
if buf.Len() > 0 {
bb := make([]byte, buf.Len())
copy(bb, buf.Bytes())
n := name{
index: len(nn),
chunk: bb,
}
nn = append(nn, n)
buf = bytes.Buffer{}
}
maxBufLen = (maxNameLen / 4 * 3) - digits(len(nn)) - 1
}
next()
for _, b := range bb {
if buf.Len() >= maxBufLen {
next()
}
if err := buf.WriteByte(b); err != nil {
return nil, err
}
}
next()
ss := make([]string, len(nn))
for i, n := range nn {
ss[i] = n.String()
}
return ss, nil
}
// decodeValue decodes the given names to a value.
//
// Will fail when chunks cannot be constructed in order or when an invalid value
// is found. The chunk and index are extracted from the name, and then sorted in
// order. The chunk will be decoded using base64 before being concatenated as
// the value.
func decodeValue(ss []string) ([]byte, error) {
nn := make([]name, len(ss))
for i, s := range ss {
n, err := newName(s)
if err != nil {
return nil, err
}
nn[i] = n
}
sort.Sort(names(nn))
var (
bb []byte
prev = -1
)
for _, n := range nn {
if n.index-1 != prev {
return nil, ErrInvalidValue
}
prev = n.index
bb = append(bb, n.chunk...)
}
return bb, nil
}
// name represents an enumerated chunk.
//
// name is an intermediate representation of encoding or decoding a value.
type name struct {
index int
chunk []byte
}
// newName creates a new name.
//
// Will fail when the wrong amount of components are found, or when the second
// component does not contain an integer.
func newName(s string) (name, error) {
comps := strings.Split(s, ".")
if len(comps) != 2 {
return name{}, ErrInvalidValue
}
index, err := strconv.Atoi(comps[1])
if err != nil {
return name{}, ErrInvalidValue
}
chunk, err := base64.URLEncoding.DecodeString(comps[0])
if err != nil {
return name{}, ErrInvalidValue
}
return name{
index: index,
chunk: chunk,
}, nil
}
func (n name) String() string {
chunk := base64.URLEncoding.EncodeToString(n.chunk)
return fmt.Sprintf("%s.%d", chunk, n.index)
}
// names represents a sortable slice of names.
type names []name
func (n names) Len() int {
return len(n)
}
func (n names) Less(i int, j int) bool {
return n[i].index < n[j].index
}
func (n names) Swap(i int, j int) {
n[i], n[j] = n[j], n[i]
}
// digits returns the number of digits in the given number.
//
// If a negative number is given, it will first be converted to a positive
// one before its digits are checked.
func digits(n int) int {
if n < 0 {
n = -n
}
if n == 0 {
return 1
}
var c int
for n > 0 {
n /= 10
c++
}
return c
}