Skip to content

Commit 3540889

Browse files
committed
WIP
Signed-off-by: Leonard Lyubich <[email protected]>
1 parent b9c6492 commit 3540889

File tree

7 files changed

+498
-2
lines changed

7 files changed

+498
-2
lines changed

pkg/core/object/metadata.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package object
2+
3+
import oid "github.com/nspcc-dev/neofs-sdk-go/object/id"
4+
5+
// TODO: docs
6+
// TODO: reuse type from SDK if possible
7+
type SearchResultItem struct {
8+
ID oid.ID
9+
Attributes []string
10+
}
Lines changed: 235 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,235 @@
1+
package meta
2+
3+
import (
4+
"bytes"
5+
"encoding/base64"
6+
"encoding/hex"
7+
"errors"
8+
"fmt"
9+
"math/big"
10+
11+
objectcore "github.com/nspcc-dev/neofs-node/pkg/core/object"
12+
cid "github.com/nspcc-dev/neofs-sdk-go/container/id"
13+
"github.com/nspcc-dev/neofs-sdk-go/object"
14+
oid "github.com/nspcc-dev/neofs-sdk-go/object/id"
15+
"github.com/nspcc-dev/neofs-sdk-go/user"
16+
"github.com/nspcc-dev/neofs-sdk-go/version"
17+
"go.etcd.io/bbolt"
18+
)
19+
20+
const (
21+
metaPrefixA = byte(iota)
22+
metaPrefixBI // integer attributes
23+
metaPrefixBS // all other attributes
24+
metaPrefixC
25+
)
26+
27+
var (
28+
maxUint256 = new(big.Int).SetBytes([]byte{255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
29+
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255})
30+
maxUint256Neg = new(big.Int).Neg(maxUint256)
31+
)
32+
33+
// TODO: fill on Init
34+
// TODO: system attributes
35+
func putMetadata(tx *bbolt.Tx, cnr cid.ID, id oid.ID, ver version.Version, owner user.ID, typ object.Type, creationEpoch uint64,
36+
payloadLen uint64, pldHash, pldHmmHash, splitID []byte, parentID, firstID oid.ID, attrs []object.Attribute) error {
37+
mb, err := tx.CreateBucketIfNotExists(metaBucketKey(cnr))
38+
if err != nil {
39+
return fmt.Errorf("create meta bucket for container: %w", err)
40+
}
41+
idk := [1 + oid.Size]byte{metaPrefixA}
42+
copy(idk[1:], id[:])
43+
if err := mb.Put(idk[:], nil); err != nil {
44+
return fmt.Errorf("put object ID to container's meta bucket: %w", err)
45+
}
46+
47+
var k []byte
48+
// TODO: move to global funcs
49+
makeKeyB := func(prefix byte, attr string, valLen int) (int, int) {
50+
ln := 1 + oid.Size + len(attr) + valLen + len(utf8Delimiter)*2 // TODO: constantize some stuff
51+
if len(k) < ln {
52+
k = make([]byte, ln)
53+
}
54+
k[0] = prefix
55+
off := 1 + copy(k[1:], attr)
56+
off += copy(k[off:], utf8Delimiter)
57+
valOff := off
58+
off += valLen
59+
off += copy(k[off:], utf8Delimiter)
60+
copy(k[off:], id[:])
61+
return ln, valOff
62+
}
63+
makeKeyC := func(attr string, valLen int) (int, int) {
64+
ln := 1 + oid.Size + len(attr) + valLen + len(utf8Delimiter) // TODO: constantize some stuff
65+
if len(k) < ln {
66+
k = make([]byte, ln)
67+
}
68+
k[0] = metaPrefixC
69+
off := 1 + copy(k[1:], id[:])
70+
off += copy(k[off:], attr)
71+
off += copy(k[off:], utf8Delimiter)
72+
return ln, off
73+
}
74+
// TODO: make generic to pass []byte directly
75+
putPlain := func(attr, val string) error {
76+
kLn, vOff := makeKeyB(metaPrefixBS, attr, len(val))
77+
copy(k[vOff:], val)
78+
if err := mb.Put(k[:kLn], nil); err != nil {
79+
return fmt.Errorf("put object attribute %q to container's meta bucket: %w", attr, err)
80+
}
81+
kLn, vOff = makeKeyC(attr, len(val))
82+
copy(k[vOff:], val)
83+
if err := mb.Put(k[:kLn], nil); err != nil {
84+
return fmt.Errorf("put object attribute %q to container's meta bucket: %w", attr, err) // TODO: distinguishable context
85+
}
86+
return nil
87+
}
88+
putInt := func(attr string, n *big.Int) error {
89+
// TODO: check uint256 overflow
90+
kLn, vOff := makeKeyB(metaPrefixBI, attr, 1+32) // sign + 256-bit. Sign makes some sensible order
91+
if n.Sign() >= 0 {
92+
k[vOff] = 1
93+
} else {
94+
k[vOff] = 0
95+
}
96+
vOff++
97+
n.FillBytes(k[vOff : vOff+32])
98+
if err := mb.Put(k[:kLn], nil); err != nil {
99+
return fmt.Errorf("put integer object attribute %q to container's meta bucket: %w", attr, err)
100+
}
101+
kLn, vOff = makeKeyC(attr, 1+32)
102+
if n.Sign() >= 0 {
103+
k[vOff] = 1
104+
} else {
105+
k[vOff] = 0
106+
}
107+
vOff++
108+
n.FillBytes(k[vOff : vOff+32])
109+
if err := mb.Put(k[:kLn], nil); err != nil {
110+
return fmt.Errorf("put integer object attribute %q to container's meta bucket: %w", attr, err) // TODO: distinguishable context
111+
}
112+
return nil
113+
}
114+
115+
if err := putPlain(object.FilterVersion, ver.String()); err != nil {
116+
return err
117+
}
118+
if err := putPlain(object.FilterOwnerID, owner.String()); err != nil {
119+
return err
120+
}
121+
if err := putPlain(object.FilterType, typ.String()); err != nil {
122+
return err
123+
}
124+
if err := putInt(object.FilterCreationEpoch, new(big.Int).SetUint64(creationEpoch)); err != nil {
125+
return err
126+
}
127+
if err := putInt(object.FilterPayloadSize, new(big.Int).SetUint64(payloadLen)); err != nil {
128+
return err
129+
}
130+
if err := putPlain(object.FilterPayloadChecksum, hex.EncodeToString(pldHash)); err != nil {
131+
return err
132+
}
133+
if err := putPlain(object.FilterPayloadHomomorphicHash, hex.EncodeToString(pldHmmHash)); err != nil {
134+
return err
135+
}
136+
if err := putPlain(object.FilterSplitID, string(splitID)); err != nil {
137+
return err
138+
}
139+
if err := putPlain(object.FilterFirstSplitObject, firstID.String()); err != nil {
140+
return err
141+
}
142+
if err := putPlain(object.FilterParentID, parentID.String()); err != nil {
143+
return err
144+
}
145+
146+
for i := range attrs {
147+
ak, av := attrs[i].Key(), attrs[i].Value()
148+
if n, isNum := parseInt(av); isNum && n.Cmp(maxUint256Neg) >= 0 && n.Cmp(maxUint256) <= 0 {
149+
if err := putInt(ak, n); err != nil {
150+
return err
151+
}
152+
continue
153+
}
154+
if err := putPlain(ak, av); err != nil {
155+
return err
156+
}
157+
}
158+
159+
return nil
160+
}
161+
162+
// TODO: docs
163+
func (db *DB) Search(cnr cid.ID, fs object.SearchFilters, attrs []string, cursor string, count uint32) ([]objectcore.SearchResultItem, string, error) {
164+
if cnr.IsZero() {
165+
return nil, "", cid.ErrZero
166+
}
167+
if count == 0 {
168+
return nil, "", errors.New("zero count")
169+
}
170+
171+
if len(fs) == 0 {
172+
if len(attrs) > 0 {
173+
return nil, "", errors.New("attributes are set without filters")
174+
}
175+
return db.searchUnfiltered(cnr, cursor, count)
176+
}
177+
178+
return nil, "", errors.New("filters are not supported yet") // TODO
179+
}
180+
181+
func (db *DB) searchUnfiltered(cnr cid.ID, cursor string, count uint32) ([]objectcore.SearchResultItem, string, error) {
182+
var cursorKey []byte
183+
var err error
184+
if cursor != "" {
185+
cursorKey = make([]byte, 1+base64.StdEncoding.DecodedLen(len(cursor)))
186+
n, err := base64.StdEncoding.Decode(cursorKey[1:], []byte(cursor))
187+
if err != nil {
188+
return nil, "", fmt.Errorf("decode cursor from base64: %w", err)
189+
}
190+
cursorKey[0] = metaPrefixA
191+
cursorKey = cursorKey[:1+n]
192+
}
193+
194+
res := make([]objectcore.SearchResultItem, count)
195+
var n uint32
196+
err = db.boltDB.View(func(tx *bbolt.Tx) error {
197+
mb := tx.Bucket(metaBucketKey(cnr))
198+
if mb == nil {
199+
return nil
200+
}
201+
202+
mbc := mb.Cursor()
203+
k, _ := mbc.Seek(cursorKey)
204+
if cursor != "" && bytes.Equal(k, cursorKey) { // cursor is the last response element, so go next
205+
k, _ = mbc.Next()
206+
}
207+
for ; k != nil; k, _ = mbc.Next() {
208+
if k[0] != metaPrefixA { // TODO: can k be []byte{}?
209+
continue
210+
}
211+
if n == count { // there are still elements
212+
cursor = base64.StdEncoding.EncodeToString(res[n-1].ID[:])
213+
return nil
214+
}
215+
k = k[1:]
216+
if len(k) != oid.Size {
217+
return fmt.Errorf("unexpected object key len %d, expected 33", len(k))
218+
}
219+
copy(res[n].ID[:], k)
220+
n++
221+
}
222+
cursor = ""
223+
return nil
224+
})
225+
if err != nil {
226+
return nil, "", fmt.Errorf("view BoltDB: %w", err)
227+
}
228+
return res[:n], cursor, nil
229+
}
230+
231+
func metaBucketKey(cnr cid.ID) []byte {
232+
k := [1 + cid.Size]byte{metadataPrefix}
233+
copy(k[1:], cnr[:])
234+
return k[:]
235+
}

0 commit comments

Comments
 (0)