storage/: remove reverse-key-listing feature

We don't use reverse listing in any of our code, outside of tests, and
it is only exposed through libuplink in the
lib/uplink.(*Project).ListBuckets() API. We also don't know of any users
who might have a need for reverse listing through ListBuckets().

Since one of our prospective pointerdb backends can not support
backwards iteration, and because of the above considerations, we are
going to remove the reverse listing feature.

Change-Id: I8d2a1f33d01ee70b79918d584b8c671f57eef2a0
This commit is contained in:
paul cannon 2019-09-25 16:30:41 -05:00 committed by Michal Niewrzal
parent 28a70200d7
commit 0c025fa937
30 changed files with 326 additions and 1081 deletions

View File

@ -91,7 +91,7 @@ func TestDownloadWithSomeNodesOffline(t *testing.T) {
// get a remote segment from pointerdb // get a remote segment from pointerdb
pdb := satellite.Metainfo.Service pdb := satellite.Metainfo.Service
listResponse, _, err := pdb.List(ctx, "", "", "", true, 0, 0) listResponse, _, err := pdb.List(ctx, "", "", true, 0, 0)
require.NoError(t, err) require.NoError(t, err)
var path string var path string
@ -195,7 +195,7 @@ func TestDownloadFromUnresponsiveNode(t *testing.T) {
// get a remote segment from pointerdb // get a remote segment from pointerdb
pdb := planet.Satellites[0].Metainfo.Service pdb := planet.Satellites[0].Metainfo.Service
listResponse, _, err := pdb.List(ctx, "", "", "", true, 0, 0) listResponse, _, err := pdb.List(ctx, "", "", true, 0, 0)
require.NoError(t, err) require.NoError(t, err)
var path string var path string

View File

@ -1310,7 +1310,7 @@ type ListSegmentsRequestOld struct {
Bucket []byte `protobuf:"bytes,1,opt,name=bucket,proto3" json:"bucket,omitempty"` Bucket []byte `protobuf:"bytes,1,opt,name=bucket,proto3" json:"bucket,omitempty"`
Prefix []byte `protobuf:"bytes,2,opt,name=prefix,proto3" json:"prefix,omitempty"` Prefix []byte `protobuf:"bytes,2,opt,name=prefix,proto3" json:"prefix,omitempty"`
StartAfter []byte `protobuf:"bytes,3,opt,name=start_after,json=startAfter,proto3" json:"start_after,omitempty"` StartAfter []byte `protobuf:"bytes,3,opt,name=start_after,json=startAfter,proto3" json:"start_after,omitempty"`
EndBefore []byte `protobuf:"bytes,4,opt,name=end_before,json=endBefore,proto3" json:"end_before,omitempty"` EndBefore []byte `protobuf:"bytes,4,opt,name=end_before,json=endBefore,proto3" json:"end_before,omitempty"` // Deprecated: Do not use.
Recursive bool `protobuf:"varint,5,opt,name=recursive,proto3" json:"recursive,omitempty"` Recursive bool `protobuf:"varint,5,opt,name=recursive,proto3" json:"recursive,omitempty"`
Limit int32 `protobuf:"varint,6,opt,name=limit,proto3" json:"limit,omitempty"` Limit int32 `protobuf:"varint,6,opt,name=limit,proto3" json:"limit,omitempty"`
MetaFlags uint32 `protobuf:"fixed32,7,opt,name=meta_flags,json=metaFlags,proto3" json:"meta_flags,omitempty"` MetaFlags uint32 `protobuf:"fixed32,7,opt,name=meta_flags,json=metaFlags,proto3" json:"meta_flags,omitempty"`
@ -1371,6 +1371,7 @@ func (m *ListSegmentsRequestOld) GetStartAfter() []byte {
return nil return nil
} }
// Deprecated: Do not use.
func (m *ListSegmentsRequestOld) GetEndBefore() []byte { func (m *ListSegmentsRequestOld) GetEndBefore() []byte {
if m != nil { if m != nil {
return m.EndBefore return m.EndBefore
@ -5133,239 +5134,239 @@ func init() {
func init() { proto.RegisterFile("metainfo.proto", fileDescriptor_631e2f30a93cd64e) } func init() { proto.RegisterFile("metainfo.proto", fileDescriptor_631e2f30a93cd64e) }
var fileDescriptor_631e2f30a93cd64e = []byte{ var fileDescriptor_631e2f30a93cd64e = []byte{
// 3709 bytes of a gzipped FileDescriptorProto // 3712 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xd4, 0x5b, 0x4b, 0x6c, 0x1c, 0x59, 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xd4, 0x5b, 0x5d, 0x6c, 0x1c, 0x57,
0xd5, 0x76, 0xbf, 0xbb, 0x4f, 0xb7, 0xdd, 0xdd, 0xd7, 0x8e, 0xdd, 0x29, 0xc7, 0x71, 0x52, 0x49, 0xf5, 0xf7, 0xec, 0xf7, 0x9e, 0x5d, 0x7b, 0xd7, 0xd7, 0x8e, 0xbd, 0x19, 0xc7, 0x71, 0x32, 0x49,
0x66, 0xfc, 0xeb, 0x9f, 0x71, 0x46, 0x9e, 0xff, 0x47, 0x83, 0x92, 0x61, 0xb0, 0xd3, 0x9e, 0x74, 0x5a, 0xff, 0xf5, 0x6f, 0x9d, 0xca, 0x05, 0x54, 0x94, 0x94, 0x62, 0x67, 0xdd, 0x78, 0x9b, 0xd8,
0x4f, 0x62, 0xc7, 0x53, 0x4e, 0x26, 0x21, 0x0c, 0xb4, 0xca, 0x5d, 0xd7, 0x76, 0x91, 0xee, 0xae, 0x71, 0xc7, 0x49, 0x13, 0x42, 0x61, 0x35, 0xde, 0xb9, 0xb6, 0x87, 0xec, 0xee, 0x2c, 0x33, 0xb3,
0xa6, 0xaa, 0x7a, 0xc6, 0x19, 0x56, 0x48, 0x23, 0x21, 0x04, 0x42, 0x23, 0xd6, 0x88, 0xd5, 0xec, 0xad, 0x53, 0x9e, 0x90, 0x2a, 0x21, 0x84, 0x84, 0x2a, 0x9e, 0x11, 0x4f, 0x7d, 0xe3, 0x09, 0xde,
0x58, 0xc1, 0x0e, 0x09, 0xd8, 0x02, 0x12, 0x1a, 0xa4, 0x11, 0x62, 0xc1, 0x62, 0x60, 0xc5, 0x0e, 0x90, 0x80, 0x57, 0x40, 0x42, 0x45, 0xaa, 0x10, 0x0f, 0x3c, 0x14, 0x9e, 0x78, 0x83, 0x17, 0xde,
0x36, 0xec, 0x40, 0x48, 0xe8, 0xbe, 0xea, 0x5d, 0xfd, 0xf0, 0x23, 0x61, 0x76, 0x55, 0xe7, 0x9e, 0x40, 0x48, 0xe8, 0x7e, 0xcd, 0xf7, 0xec, 0x87, 0x3f, 0x12, 0xfa, 0x36, 0x73, 0xee, 0xb9, 0x67,
0x7b, 0xea, 0xde, 0xf3, 0xf8, 0xce, 0xa9, 0x73, 0xab, 0x60, 0xa6, 0x8b, 0x6d, 0x55, 0xef, 0xed, 0xee, 0x3d, 0x1f, 0xbf, 0x73, 0xe6, 0xdc, 0x19, 0x98, 0xea, 0x60, 0x47, 0x33, 0xba, 0xfb, 0xe6,
0x1b, 0xab, 0x7d, 0xd3, 0xb0, 0x0d, 0x94, 0x17, 0xf7, 0x52, 0x05, 0xf7, 0xda, 0xe6, 0xd3, 0xbe, 0x4a, 0xcf, 0x32, 0x1d, 0x13, 0x15, 0xc4, 0xbd, 0x5c, 0xc5, 0xdd, 0x96, 0xf5, 0xb4, 0xe7, 0x18,
0xad, 0x1b, 0x3d, 0x36, 0x26, 0xc1, 0x81, 0x71, 0xc0, 0xf9, 0xa4, 0xe5, 0x03, 0xc3, 0x38, 0xe8, 0x66, 0x97, 0x8d, 0xc9, 0x70, 0x60, 0x1e, 0x70, 0x3e, 0x79, 0xe9, 0xc0, 0x34, 0x0f, 0xda, 0xf8,
0xe0, 0xeb, 0xf4, 0x6e, 0x6f, 0xb0, 0x7f, 0xdd, 0xd6, 0xbb, 0xd8, 0xb2, 0xd5, 0x6e, 0x5f, 0x30, 0x3a, 0xbd, 0xdb, 0xeb, 0xef, 0x5f, 0x77, 0x8c, 0x0e, 0xb6, 0x1d, 0xad, 0xd3, 0x13, 0xcc, 0x5d,
0xf7, 0x0c, 0x0d, 0xf3, 0xeb, 0x72, 0xdf, 0xd0, 0x7b, 0x36, 0x36, 0xb5, 0x3d, 0x4e, 0x28, 0x19, 0x53, 0xc7, 0xfc, 0xba, 0xd2, 0x33, 0x8d, 0xae, 0x83, 0x2d, 0x7d, 0x8f, 0x13, 0xca, 0xa6, 0xa5,
0xa6, 0x86, 0x4d, 0x8b, 0xdd, 0xc9, 0x2b, 0x30, 0xad, 0xe0, 0x6f, 0x0e, 0xb0, 0x65, 0x37, 0xb0, 0x63, 0xcb, 0x66, 0x77, 0xca, 0x32, 0x4c, 0xaa, 0xf8, 0xdb, 0x7d, 0x6c, 0x3b, 0x9b, 0x58, 0xd3,
0xaa, 0x61, 0x13, 0x2d, 0x40, 0x4e, 0xed, 0xeb, 0xad, 0x27, 0xf8, 0x69, 0x2d, 0x71, 0x29, 0xb1, 0xb1, 0x85, 0xe6, 0x21, 0xaf, 0xf5, 0x8c, 0xe6, 0x13, 0xfc, 0xb4, 0x26, 0x5d, 0x92, 0x96, 0xcb,
0x52, 0x52, 0xb2, 0x6a, 0x5f, 0xbf, 0x83, 0x9f, 0xca, 0x3f, 0x49, 0x41, 0x76, 0x63, 0xd0, 0x7e, 0x6a, 0x4e, 0xeb, 0x19, 0x77, 0xf0, 0x53, 0xe5, 0xa7, 0x69, 0xc8, 0xad, 0xf7, 0x5b, 0x4f, 0xb0,
0x82, 0x6d, 0x84, 0x20, 0xdd, 0x53, 0xbb, 0x98, 0x33, 0xd0, 0x6b, 0xf4, 0x1a, 0x14, 0xfb, 0xaa, 0x83, 0x10, 0x64, 0xba, 0x5a, 0x07, 0x73, 0x06, 0x7a, 0x8d, 0x5e, 0x83, 0x52, 0x4f, 0x73, 0x0e,
0x7d, 0xd8, 0x6a, 0xeb, 0xfd, 0x43, 0x6c, 0xd6, 0x92, 0x97, 0x12, 0x2b, 0x33, 0x6b, 0x0b, 0xab, 0x9b, 0x2d, 0xa3, 0x77, 0x88, 0xad, 0x5a, 0xea, 0x92, 0xb4, 0x3c, 0xb5, 0x3a, 0xbf, 0xe2, 0xdb,
0x9e, 0x8d, 0xdc, 0xa2, 0x23, 0xbb, 0x03, 0xdd, 0xc6, 0x0a, 0x10, 0x5e, 0x46, 0x40, 0xb7, 0x00, 0xc8, 0x2d, 0x3a, 0xb2, 0xdb, 0x37, 0x1c, 0xac, 0x02, 0xe1, 0x65, 0x04, 0x74, 0x0b, 0xa0, 0x65,
0xda, 0x26, 0x56, 0x6d, 0xac, 0xb5, 0x54, 0xbb, 0x96, 0xba, 0x94, 0x58, 0x29, 0xae, 0x49, 0xab, 0x61, 0xcd, 0xc1, 0x7a, 0x53, 0x73, 0x6a, 0xe9, 0x4b, 0xd2, 0x72, 0x69, 0x55, 0x5e, 0x61, 0x7b,
0x6c, 0x8f, 0xab, 0x62, 0x8f, 0xab, 0xf7, 0xc5, 0x1e, 0x37, 0xf2, 0xbf, 0xf9, 0x6c, 0x79, 0xea, 0x5c, 0x11, 0x7b, 0x5c, 0xb9, 0x2f, 0xf6, 0xb8, 0x5e, 0xf8, 0xed, 0x67, 0x4b, 0x13, 0x1f, 0xfd,
0xa3, 0x3f, 0x2f, 0x27, 0x94, 0x02, 0x9f, 0xb7, 0x6e, 0xa3, 0x57, 0x60, 0x4e, 0xc3, 0xfb, 0xea, 0x65, 0x49, 0x52, 0x8b, 0x7c, 0xde, 0x9a, 0x83, 0x5e, 0x81, 0x59, 0x1d, 0xef, 0x6b, 0xfd, 0xb6,
0xa0, 0x63, 0xb7, 0x2c, 0x7c, 0xd0, 0xc5, 0x3d, 0xbb, 0x65, 0xe9, 0x1f, 0xe0, 0x5a, 0xfa, 0x52, 0xd3, 0xb4, 0xf1, 0x41, 0x07, 0x77, 0x9d, 0xa6, 0x6d, 0x7c, 0x80, 0x6b, 0x99, 0x4b, 0xd2, 0x72,
0x62, 0x25, 0xa5, 0x20, 0x3e, 0xb6, 0xcb, 0x86, 0x76, 0xf5, 0x0f, 0x30, 0x7a, 0x08, 0xe7, 0xc5, 0x5a, 0x45, 0x7c, 0x6c, 0x97, 0x0d, 0xed, 0x1a, 0x1f, 0x60, 0xf4, 0x10, 0xce, 0x8b, 0x19, 0x16,
0x0c, 0x13, 0x6b, 0x83, 0x9e, 0xa6, 0xf6, 0xda, 0x4f, 0x5b, 0x56, 0xfb, 0x10, 0x77, 0x71, 0x2d, 0xd6, 0xfb, 0x5d, 0x5d, 0xeb, 0xb6, 0x9e, 0x36, 0xed, 0xd6, 0x21, 0xee, 0xe0, 0x5a, 0x96, 0xae,
0x43, 0x57, 0xb1, 0xb8, 0xea, 0x2a, 0x4f, 0x71, 0x78, 0x76, 0x29, 0x8b, 0xb2, 0xc0, 0x67, 0x07, 0x62, 0x61, 0xc5, 0x53, 0x9e, 0xea, 0xf2, 0xec, 0x52, 0x16, 0x75, 0x9e, 0xcf, 0x0e, 0x0f, 0x20,
0x07, 0x90, 0x06, 0x4b, 0x42, 0xb0, 0xbb, 0xfb, 0x56, 0x5f, 0x35, 0xd5, 0x2e, 0xb6, 0xb1, 0x69, 0x1d, 0x16, 0x85, 0x60, 0x6f, 0xf7, 0xcd, 0x9e, 0x66, 0x69, 0x1d, 0xec, 0x60, 0xcb, 0xae, 0xe5,
0xd5, 0xb2, 0x54, 0xf8, 0x25, 0xaf, 0x6e, 0x36, 0x9d, 0xcb, 0x1d, 0x87, 0x4f, 0x59, 0xe4, 0x62, 0xa8, 0xf0, 0x4b, 0x7e, 0xdd, 0x6c, 0xb8, 0x97, 0x3b, 0x2e, 0x9f, 0xba, 0xc0, 0xc5, 0xc4, 0x0d,
0xa2, 0x06, 0xd1, 0x12, 0x40, 0x5f, 0x35, 0xed, 0x1e, 0x36, 0x5b, 0xba, 0x56, 0xcb, 0x51, 0x4b, 0xa2, 0x45, 0x80, 0x9e, 0x66, 0x39, 0x5d, 0x6c, 0x35, 0x0d, 0xbd, 0x96, 0xa7, 0x96, 0x28, 0x72,
0x14, 0x38, 0xa5, 0xa9, 0xc9, 0x3a, 0xcc, 0x30, 0x63, 0xdd, 0xd5, 0x2d, 0xbb, 0x69, 0xe3, 0x6e, 0x4a, 0x43, 0x57, 0x0c, 0x98, 0x62, 0xc6, 0xba, 0x6b, 0xd8, 0x4e, 0xc3, 0xc1, 0x9d, 0x58, 0xa3,
0xa4, 0xd1, 0xfc, 0xaa, 0x4f, 0x1e, 0x4b, 0xf5, 0xf2, 0xc7, 0x29, 0x98, 0x65, 0xcf, 0xba, 0x45, 0x05, 0x55, 0x9f, 0x3a, 0x96, 0xea, 0x95, 0x8f, 0xd3, 0x30, 0xc3, 0x9e, 0x75, 0x8b, 0xd2, 0xb8,
0x69, 0xdc, 0x9f, 0xd0, 0x75, 0xc8, 0x1e, 0x52, 0x9f, 0xaa, 0x95, 0xa9, 0xe0, 0x85, 0x55, 0xc7, 0x3f, 0xa1, 0xeb, 0x90, 0x3b, 0xa4, 0x3e, 0x55, 0xab, 0x50, 0xc1, 0xf3, 0x2b, 0xae, 0xbf, 0x07,
0xdf, 0x7d, 0x2e, 0xa7, 0x70, 0xb6, 0x53, 0x76, 0xab, 0x38, 0x8f, 0x48, 0x1d, 0xcf, 0x23, 0xd2, 0x5c, 0x4e, 0xe5, 0x6c, 0xa7, 0xec, 0x56, 0x49, 0x1e, 0x91, 0x3e, 0x9e, 0x47, 0x64, 0xce, 0xd2,
0x67, 0xe9, 0x11, 0x99, 0xd3, 0xf7, 0x88, 0x6c, 0xd0, 0x23, 0xbe, 0x0c, 0x73, 0x7e, 0x2b, 0x59, 0x23, 0xb2, 0xa7, 0xef, 0x11, 0xb9, 0xb0, 0x47, 0x7c, 0x15, 0x66, 0x83, 0x56, 0xb2, 0x7b, 0x66,
0x7d, 0xa3, 0x67, 0x61, 0xb4, 0x02, 0xd9, 0x3d, 0x4a, 0xa7, 0x7a, 0x2f, 0xae, 0x55, 0x5c, 0x33, 0xd7, 0xc6, 0x68, 0x19, 0x72, 0x7b, 0x94, 0x4e, 0xf5, 0x5e, 0x5a, 0xad, 0x7a, 0x66, 0x62, 0xfc,
0x31, 0x7e, 0x85, 0x8f, 0xcb, 0x0f, 0xa1, 0xc2, 0x28, 0xb7, 0xb1, 0x7d, 0x9a, 0x46, 0x96, 0x5f, 0x2a, 0x1f, 0x57, 0x1e, 0x42, 0x95, 0x51, 0x6e, 0x63, 0xe7, 0x34, 0x8d, 0xac, 0xbc, 0x0e, 0xd3,
0x87, 0xaa, 0x47, 0xf0, 0xc4, 0xeb, 0x7a, 0x2c, 0xfc, 0xaf, 0x8e, 0x3b, 0xf8, 0x74, 0xfd, 0x4f, 0x3e, 0xc1, 0x63, 0xaf, 0xeb, 0xb1, 0xf0, 0xbf, 0x3a, 0x6e, 0xe3, 0xd3, 0xf5, 0x3f, 0x65, 0x4e,
0x9e, 0x17, 0x5a, 0x13, 0xb2, 0xd9, 0xea, 0xe4, 0x8f, 0x12, 0x62, 0xcd, 0x24, 0xc0, 0x8e, 0xfd, 0x68, 0x4d, 0xc8, 0x66, 0xab, 0x53, 0x3e, 0x92, 0xc4, 0x9a, 0x49, 0x80, 0x1d, 0xfb, 0x91, 0x73,
0xc8, 0x79, 0xc8, 0xb6, 0x07, 0xa6, 0x65, 0x98, 0x02, 0x6c, 0xd9, 0x1d, 0x9a, 0x83, 0x4c, 0x47, 0x90, 0x6b, 0xf5, 0x2d, 0xdb, 0xb4, 0x04, 0xd8, 0xb2, 0x3b, 0x34, 0x0b, 0xd9, 0xb6, 0xd1, 0x31,
0xef, 0xea, 0x2c, 0x26, 0x33, 0x0a, 0xbb, 0x41, 0x17, 0xa0, 0xa0, 0xe9, 0x26, 0x6e, 0x13, 0xc3, 0x58, 0x4c, 0x66, 0x55, 0x76, 0x83, 0x2e, 0x40, 0x51, 0x37, 0x2c, 0xdc, 0x22, 0x86, 0xa7, 0x7e,
0x53, 0x3f, 0xce, 0x28, 0x2e, 0x41, 0x7e, 0x04, 0xc8, 0xbb, 0x22, 0xae, 0xc6, 0x55, 0xc8, 0xe8, 0x9c, 0x55, 0x3d, 0x82, 0xf2, 0x08, 0x90, 0x7f, 0x45, 0x5c, 0x8d, 0x2b, 0x90, 0x35, 0x1c, 0xdc,
0x36, 0xee, 0x5a, 0xb5, 0xc4, 0xa5, 0xd4, 0x4a, 0x71, 0xad, 0x16, 0xd4, 0xa2, 0xc0, 0x07, 0x85, 0xb1, 0x6b, 0xd2, 0xa5, 0xf4, 0x72, 0x69, 0xb5, 0x16, 0xd6, 0xa2, 0xc0, 0x07, 0x95, 0xb1, 0x11,
0xb1, 0x11, 0x25, 0x74, 0x0d, 0x13, 0xd3, 0x07, 0xe7, 0x15, 0x7a, 0x2d, 0x7f, 0x3b, 0x01, 0x8b, 0x25, 0x74, 0x4c, 0x0b, 0xd3, 0x07, 0x17, 0x54, 0x7a, 0xad, 0x7c, 0x57, 0x82, 0x05, 0xc6, 0xbd,
0x8c, 0x7b, 0x17, 0xdb, 0xeb, 0xb6, 0x6d, 0xea, 0x7b, 0x03, 0xf2, 0xc8, 0x53, 0x8d, 0x74, 0xbf, 0x8b, 0x9d, 0x35, 0xc7, 0xb1, 0x8c, 0xbd, 0x3e, 0x79, 0xe4, 0xa9, 0x46, 0x7a, 0xd0, 0x7d, 0x53,
0xfb, 0x26, 0x83, 0xee, 0x7b, 0x11, 0x2e, 0x44, 0x2f, 0x81, 0x1b, 0xe4, 0xc3, 0x04, 0xcc, 0xae, 0x61, 0xf7, 0xbd, 0x08, 0x17, 0xe2, 0x97, 0xc0, 0x0d, 0xf2, 0xa1, 0x04, 0x33, 0x6b, 0xba, 0x6e,
0x6b, 0x9a, 0x89, 0x2d, 0x0b, 0x6b, 0xf7, 0x48, 0x8a, 0xbb, 0x4b, 0x75, 0xb6, 0x22, 0x34, 0xc9, 0x61, 0xdb, 0xc6, 0xfa, 0x3d, 0x92, 0xe2, 0xee, 0x52, 0x9d, 0x2d, 0x0b, 0x4d, 0x32, 0x2f, 0x42,
0xbc, 0x08, 0xad, 0xf2, 0xf4, 0xe7, 0xb2, 0x08, 0xed, 0xde, 0x82, 0x39, 0xcb, 0x36, 0x4c, 0xf5, 0x2b, 0x3c, 0xfd, 0x79, 0x2c, 0x42, 0xbb, 0xb7, 0x60, 0xd6, 0x76, 0x4c, 0x4b, 0x3b, 0xc0, 0x4d,
0x00, 0xb7, 0x48, 0xfe, 0x6c, 0xa9, 0x4c, 0x1a, 0x87, 0xc5, 0xea, 0x2a, 0x4d, 0xaa, 0xdb, 0x86, 0x92, 0x3f, 0x9b, 0x1a, 0x93, 0xc6, 0x61, 0x71, 0x7a, 0x85, 0x26, 0xd5, 0x6d, 0x53, 0xc7, 0xfc,
0x86, 0xf9, 0x63, 0x14, 0xc4, 0xd9, 0x3d, 0x34, 0xf9, 0xf7, 0x49, 0x98, 0xe7, 0x98, 0xf2, 0xd0, 0x31, 0x2a, 0xe2, 0xec, 0x3e, 0x9a, 0xf2, 0x87, 0x14, 0xcc, 0x71, 0x4c, 0x79, 0x68, 0x19, 0xae,
0xd4, 0x1d, 0x67, 0xbc, 0xd7, 0xd1, 0x8e, 0xe5, 0x1c, 0x9e, 0x08, 0x28, 0x09, 0x7f, 0x27, 0xda, 0x33, 0xde, 0x6b, 0xeb, 0xc7, 0x72, 0x0e, 0x5f, 0x04, 0x94, 0x85, 0xbf, 0x13, 0xed, 0x11, 0x9c,
0x23, 0x38, 0xc7, 0x75, 0x44, 0xaf, 0x51, 0x0d, 0x72, 0x1c, 0xe5, 0x38, 0xc0, 0x89, 0x5b, 0x74, 0xe3, 0x3a, 0xa2, 0xd7, 0xa8, 0x06, 0x79, 0x8e, 0x72, 0x1c, 0xe0, 0xc4, 0x2d, 0xba, 0x01, 0xe0,
0x03, 0xc0, 0x45, 0xb3, 0x71, 0x60, 0xcc, 0xc3, 0x8e, 0x6e, 0x80, 0xd4, 0x55, 0x8f, 0x04, 0x6a, 0xa1, 0xd9, 0x28, 0x30, 0xe6, 0x63, 0x47, 0x37, 0x40, 0xee, 0x68, 0x47, 0x02, 0xb5, 0xb0, 0x1e,
0x61, 0xcd, 0x0f, 0xa5, 0x19, 0xfa, 0xa4, 0x85, 0xae, 0x7a, 0xb4, 0x29, 0x18, 0xbc, 0x78, 0x5a, 0x84, 0xd2, 0x2c, 0x7d, 0xd2, 0x7c, 0x47, 0x3b, 0xda, 0x10, 0x0c, 0x7e, 0x3c, 0xad, 0x03, 0xe0,
0x07, 0xc0, 0x47, 0x7d, 0xdd, 0x54, 0xa9, 0xbf, 0x66, 0x27, 0xc8, 0x2e, 0x9e, 0x79, 0xf2, 0xa7, 0xa3, 0x9e, 0x61, 0x69, 0xd4, 0x5f, 0x73, 0x63, 0x64, 0x17, 0xdf, 0x3c, 0xe5, 0x53, 0x09, 0xe6,
0x09, 0x58, 0xf0, 0x6b, 0x94, 0x59, 0x9c, 0xa8, 0xb4, 0x01, 0x15, 0x55, 0xd8, 0xbc, 0x45, 0xad, 0x83, 0x1a, 0x65, 0x16, 0x27, 0x2a, 0xdd, 0x84, 0xaa, 0x26, 0x6c, 0xde, 0xa4, 0x56, 0x14, 0x7e,
0x28, 0xfc, 0x7c, 0xc9, 0x55, 0x6e, 0x84, 0x57, 0x28, 0x65, 0x67, 0x1a, 0xbd, 0xb7, 0xd0, 0xab, 0xbe, 0xe8, 0x29, 0x37, 0xc6, 0x2b, 0xd4, 0x8a, 0x3b, 0x8d, 0xde, 0xdb, 0xe8, 0x55, 0x98, 0xb4,
0x30, 0x6d, 0x1a, 0x86, 0xdd, 0xea, 0xeb, 0xb8, 0x8d, 0x1d, 0x07, 0xdc, 0x28, 0x93, 0x25, 0xfd, 0x4c, 0xd3, 0x69, 0xf6, 0x0c, 0xdc, 0xc2, 0xae, 0x03, 0xae, 0x57, 0xc8, 0x92, 0xfe, 0xfc, 0xd9,
0xe9, 0xb3, 0xe5, 0xdc, 0x0e, 0xa1, 0x37, 0xeb, 0x4a, 0x91, 0x70, 0xb1, 0x1b, 0x8d, 0x26, 0x27, 0x52, 0x7e, 0x87, 0xd0, 0x1b, 0x75, 0xb5, 0x44, 0xb8, 0xd8, 0x8d, 0x4e, 0x93, 0x93, 0x65, 0xbc,
0x53, 0x7f, 0x4f, 0xb5, 0x31, 0xad, 0x97, 0x52, 0x74, 0xca, 0x02, 0x9f, 0x52, 0xa6, 0x5c, 0x3b, 0xa7, 0x39, 0x98, 0xd6, 0x4b, 0x69, 0x3a, 0x65, 0x9e, 0x4f, 0xa9, 0x50, 0xae, 0x1d, 0x36, 0x7e,
0x6c, 0xfc, 0x0e, 0x7e, 0xaa, 0x40, 0xdf, 0xb9, 0x96, 0xff, 0xe5, 0x6e, 0xea, 0x96, 0xd1, 0x25, 0x07, 0x3f, 0x55, 0xa1, 0xe7, 0x5e, 0x2b, 0xff, 0xf6, 0x36, 0x75, 0xcb, 0xec, 0x90, 0x15, 0x3d,
0x2b, 0x7a, 0xee, 0x7e, 0xf2, 0x12, 0xe4, 0xb8, 0x53, 0x70, 0x27, 0x41, 0x1e, 0x27, 0xd9, 0x61, 0x77, 0x3f, 0x79, 0x09, 0xf2, 0xdc, 0x29, 0xb8, 0x93, 0x20, 0x9f, 0x93, 0xec, 0xb0, 0x2b, 0x55,
0x57, 0x8a, 0x60, 0x41, 0x37, 0xa0, 0x6c, 0x98, 0xfa, 0x81, 0xde, 0x53, 0x3b, 0x42, 0xf1, 0x19, 0xb0, 0xa0, 0x1b, 0x50, 0x31, 0x2d, 0xe3, 0xc0, 0xe8, 0x6a, 0x6d, 0xa1, 0xf8, 0x2c, 0x55, 0x7c,
0xaa, 0xf8, 0xa8, 0x00, 0x9b, 0x11, 0xac, 0x4c, 0xd9, 0x72, 0x03, 0x6a, 0x81, 0xcd, 0xbb, 0x26, 0x5c, 0x80, 0x4d, 0x09, 0x56, 0xa6, 0x6c, 0x65, 0x13, 0x6a, 0xa1, 0xcd, 0x7b, 0x26, 0xf5, 0x2d,
0xf5, 0x2c, 0x23, 0x31, 0x72, 0x19, 0xf2, 0x0f, 0x13, 0x70, 0x9e, 0x8b, 0xaa, 0x1b, 0xef, 0xf7, 0x43, 0x1a, 0xba, 0x0c, 0xe5, 0x47, 0x12, 0x9c, 0xe7, 0xa2, 0xea, 0xe6, 0xfb, 0xdd, 0xb6, 0xa9,
0x3a, 0x86, 0xaa, 0x3d, 0x77, 0x4d, 0xca, 0x9f, 0x24, 0x40, 0x0a, 0x2d, 0xea, 0x2c, 0x9c, 0xd6, 0xe9, 0xcf, 0x5d, 0x93, 0xca, 0x27, 0x12, 0xc8, 0x91, 0x45, 0x9d, 0x85, 0xd3, 0xfa, 0x74, 0x95,
0xa3, 0xab, 0xe4, 0x68, 0x93, 0x1d, 0xdf, 0x5b, 0x7f, 0x90, 0x80, 0x73, 0x7c, 0x43, 0xcd, 0xde, 0x1a, 0x6e, 0xb2, 0xe3, 0x7b, 0xeb, 0x0f, 0x25, 0x38, 0xc7, 0x37, 0xd4, 0xe8, 0xee, 0x9b, 0xcf,
0xbe, 0xf1, 0xfc, 0x35, 0xfc, 0xa6, 0x03, 0xb2, 0x6c, 0x3d, 0x91, 0xee, 0x33, 0x5a, 0x25, 0x24, 0x5f, 0xc3, 0x6f, 0xba, 0x20, 0xcb, 0xd6, 0x13, 0xeb, 0x3e, 0xc3, 0x55, 0x42, 0xb2, 0xb8, 0x08,
0x8b, 0x8b, 0x30, 0xf4, 0xd5, 0x0e, 0xcf, 0x71, 0x6b, 0x3f, 0x4e, 0x38, 0xc1, 0xe1, 0x2f, 0x39, 0xc3, 0x40, 0xed, 0xf0, 0x1c, 0xb7, 0xf6, 0x13, 0xc9, 0x0d, 0x8e, 0x60, 0xc9, 0x71, 0xba, 0xae,
0x4e, 0xd7, 0x75, 0x02, 0xce, 0x90, 0x1c, 0xdf, 0x19, 0xbe, 0x9f, 0x84, 0x79, 0x52, 0x34, 0xf0, 0x13, 0x72, 0x86, 0xd4, 0xe8, 0xce, 0xf0, 0x51, 0x0a, 0xe6, 0x48, 0xd1, 0xc0, 0x17, 0x69, 0x9f,
0x45, 0x5a, 0x67, 0xa1, 0xb2, 0x79, 0xc8, 0xf6, 0x4d, 0xbc, 0xaf, 0x1f, 0x71, 0xa5, 0xf1, 0x3b, 0x85, 0xca, 0xe6, 0x20, 0xd7, 0xb3, 0xf0, 0xbe, 0x71, 0xc4, 0x95, 0xc6, 0xef, 0xd0, 0x12, 0x94,
0xb4, 0x0c, 0x45, 0xcb, 0x56, 0x4d, 0xbb, 0xa5, 0xee, 0x13, 0x0b, 0x53, 0x17, 0x56, 0x80, 0x92, 0x6c, 0x47, 0xb3, 0x9c, 0xa6, 0xb6, 0x4f, 0x2c, 0x4c, 0x5d, 0x58, 0x05, 0x4a, 0x5a, 0x23, 0x14,
0xd6, 0x09, 0x85, 0x14, 0x11, 0xb8, 0xa7, 0xb5, 0xf6, 0xf0, 0x3e, 0xa9, 0x61, 0xd2, 0xac, 0x88, 0x74, 0x19, 0x00, 0x77, 0xf5, 0xe6, 0x1e, 0xde, 0x27, 0x35, 0x4c, 0x86, 0xee, 0x2a, 0x55, 0x93,
0xc0, 0x3d, 0x6d, 0x83, 0x12, 0x48, 0x01, 0x65, 0x62, 0x52, 0x62, 0xe9, 0xef, 0xb1, 0xec, 0x95, 0xd4, 0x22, 0xee, 0xea, 0xeb, 0x94, 0x48, 0x8a, 0x28, 0x0b, 0x93, 0x32, 0xcb, 0x78, 0x8f, 0x65,
0x57, 0x5c, 0x82, 0x5b, 0x74, 0x65, 0xbd, 0x45, 0xd7, 0x12, 0x00, 0xd9, 0x45, 0x6b, 0xbf, 0xa3, 0xb0, 0x82, 0xea, 0x11, 0xbc, 0xc2, 0x2b, 0xe7, 0x2f, 0xbc, 0x16, 0x01, 0xc8, 0x4e, 0x9a, 0xfb,
0x1e, 0x58, 0xf4, 0x45, 0x2b, 0xa7, 0x14, 0x08, 0xe5, 0x4d, 0x42, 0xa0, 0xe9, 0xc9, 0xaf, 0x0e, 0x6d, 0xed, 0xc0, 0xa6, 0x2f, 0x5b, 0x79, 0xb5, 0x48, 0x28, 0x6f, 0x12, 0x02, 0x4d, 0x51, 0x41,
0xd7, 0x5c, 0x37, 0xfd, 0xb5, 0xd7, 0x0b, 0xae, 0x3a, 0x62, 0x66, 0xac, 0x8e, 0xa8, 0xc4, 0x24, 0x95, 0x78, 0x26, 0xbb, 0x19, 0xac, 0xbf, 0x5e, 0xf0, 0x54, 0x92, 0x30, 0x63, 0x65, 0x48, 0x35,
0x0c, 0x69, 0xf1, 0x32, 0x47, 0x7d, 0x2a, 0xe1, 0xf1, 0xa9, 0xc9, 0xd0, 0x60, 0x11, 0x0a, 0xba, 0x26, 0x63, 0xc8, 0x88, 0x17, 0x3a, 0xea, 0x57, 0x92, 0xcf, 0xaf, 0xc6, 0x43, 0x84, 0x05, 0x28,
0xd5, 0xe2, 0x5a, 0x4e, 0xd1, 0x47, 0xe4, 0x75, 0x6b, 0x87, 0xde, 0xcb, 0xdf, 0xa3, 0x4e, 0x18, 0x1a, 0x76, 0x93, 0x6b, 0x3a, 0x4d, 0x1f, 0x51, 0x30, 0xec, 0x1d, 0x7a, 0xaf, 0xfc, 0x80, 0x3a,
0x51, 0xea, 0x1d, 0xcb, 0xca, 0xcb, 0x50, 0x64, 0x76, 0x6d, 0x79, 0x8a, 0x3e, 0x60, 0xa4, 0xed, 0x62, 0x4c, 0xb9, 0x77, 0x2c, 0x4b, 0x2f, 0x41, 0x89, 0xd9, 0xb6, 0xe9, 0x2b, 0xfc, 0x80, 0x91,
0x31, 0x4a, 0xbf, 0x45, 0x82, 0xf1, 0x51, 0x45, 0xdf, 0xbd, 0x8e, 0x26, 0x6f, 0x02, 0xda, 0x31, 0xb6, 0x47, 0x28, 0xff, 0x16, 0x08, 0xce, 0xc7, 0x15, 0x7e, 0xf7, 0xda, 0xba, 0xb2, 0x01, 0x68,
0x8d, 0x6f, 0xe0, 0xb6, 0x17, 0x9a, 0x26, 0x5e, 0xa3, 0xfc, 0x1a, 0xcc, 0xfa, 0xc4, 0xf0, 0xea, 0xc7, 0x32, 0xbf, 0x85, 0x5b, 0x7e, 0x78, 0x1a, 0x7b, 0x8d, 0xca, 0x6b, 0x30, 0x13, 0x10, 0xc3,
0xf9, 0x32, 0x94, 0xfa, 0x8c, 0xdc, 0xb2, 0xd4, 0x8e, 0x70, 0xd3, 0x22, 0xa7, 0xed, 0xaa, 0x1d, 0x2b, 0xe8, 0xcb, 0x50, 0xee, 0x31, 0x72, 0xd3, 0xd6, 0xda, 0xc2, 0x55, 0x4b, 0x9c, 0xb6, 0xab,
0x5b, 0xfe, 0x6e, 0x0e, 0xb2, 0xf7, 0xf6, 0xc8, 0x6d, 0xac, 0x3b, 0x5f, 0x83, 0x19, 0xb7, 0x82, 0xb5, 0x1d, 0xe5, 0xfb, 0x79, 0xc8, 0xdd, 0xdb, 0x23, 0xb7, 0x89, 0x2e, 0x7d, 0x0d, 0xa6, 0xbc,
0xf2, 0x60, 0xc1, 0xb4, 0x43, 0xdd, 0xe1, 0xa0, 0xf0, 0x1e, 0x36, 0x2d, 0xb7, 0xb8, 0x17, 0xb7, 0x2a, 0xca, 0x87, 0x07, 0x93, 0x2e, 0x75, 0x87, 0x03, 0xc3, 0x7b, 0xd8, 0xb2, 0xbd, 0x02, 0x5f,
0x64, 0x3b, 0x96, 0xad, 0xda, 0x03, 0x8b, 0xba, 0xf4, 0x8c, 0x77, 0x3b, 0xec, 0xd1, 0xab, 0xbb, 0xdc, 0x92, 0xed, 0xd8, 0x8e, 0xe6, 0xf4, 0x6d, 0xea, 0xd6, 0x53, 0xfe, 0xed, 0xb0, 0x47, 0xaf,
0x74, 0x58, 0xe1, 0x6c, 0xe8, 0x65, 0x28, 0x58, 0xb6, 0x89, 0xd5, 0x2e, 0x51, 0x68, 0x86, 0x06, 0xec, 0xd2, 0x61, 0x95, 0xb3, 0xa1, 0x97, 0xa1, 0x68, 0x3b, 0x16, 0xd6, 0x3a, 0x44, 0xa1, 0x59,
0x77, 0x85, 0x07, 0x77, 0x7e, 0x97, 0x0e, 0x34, 0xeb, 0x4a, 0x9e, 0xb1, 0x34, 0xb5, 0x40, 0x1f, 0x1a, 0x0a, 0x55, 0x1e, 0xe0, 0x85, 0x5d, 0x3a, 0xd0, 0xa8, 0xab, 0x05, 0xc6, 0xd2, 0xd0, 0x43,
0x20, 0x7b, 0xbc, 0x16, 0xcc, 0x3a, 0x79, 0x26, 0x79, 0x3a, 0x91, 0x91, 0x9b, 0x40, 0x46, 0x9e, 0xbd, 0x80, 0xdc, 0xf1, 0xda, 0x30, 0x6b, 0xe4, 0x99, 0xe4, 0xe9, 0x44, 0x46, 0x7e, 0x0c, 0x19,
0x4d, 0x5b, 0x27, 0x25, 0x38, 0xab, 0xfc, 0x30, 0x95, 0x91, 0x9f, 0x64, 0x1d, 0x7c, 0xde, 0xba, 0x05, 0x36, 0x6d, 0x8d, 0x94, 0xe1, 0xac, 0xfa, 0xc3, 0x54, 0x46, 0x61, 0x9c, 0x75, 0xf0, 0x79,
0x8d, 0x6e, 0x43, 0xcd, 0xd5, 0x36, 0xd1, 0x93, 0xa6, 0xda, 0x6a, 0xab, 0x67, 0xf4, 0xda, 0xb8, 0x6b, 0x0e, 0xba, 0x0d, 0x35, 0x4f, 0xdb, 0x44, 0x4f, 0xba, 0xe6, 0x68, 0xcd, 0xae, 0xd9, 0x6d,
0x56, 0xa0, 0xaa, 0x98, 0xe6, 0xaa, 0xc8, 0x6c, 0x13, 0xa2, 0x32, 0xef, 0xb0, 0x6f, 0x71, 0x6e, 0xe1, 0x5a, 0x91, 0xaa, 0x62, 0x92, 0xab, 0x22, 0xbb, 0x4d, 0x88, 0xea, 0x9c, 0xcb, 0xbe, 0xc5,
0x4a, 0x47, 0x2f, 0x03, 0x0a, 0x0b, 0xaa, 0x01, 0x35, 0x5d, 0x35, 0x34, 0x07, 0xbd, 0x04, 0x68, 0xb9, 0x29, 0x1d, 0xbd, 0x0c, 0x28, 0x2a, 0xa8, 0x06, 0xd4, 0x74, 0xd3, 0x91, 0x39, 0xe8, 0x25,
0x5f, 0x3f, 0x0a, 0xd6, 0xc8, 0x45, 0x0a, 0xef, 0x15, 0x3a, 0xe2, 0x2d, 0x8e, 0x1b, 0x50, 0x0d, 0x40, 0xfb, 0xc6, 0x51, 0xb8, 0x4e, 0x2e, 0x51, 0x88, 0xaf, 0xd2, 0x11, 0x7f, 0x81, 0xbc, 0x09,
0x37, 0x19, 0x4a, 0xa3, 0xab, 0xf3, 0x8a, 0x19, 0xec, 0x2e, 0x3c, 0x80, 0x73, 0xd1, 0x5d, 0x85, 0xd3, 0xd1, 0x46, 0x43, 0x79, 0x78, 0x85, 0x5e, 0xb5, 0xc2, 0x1d, 0x86, 0x07, 0x70, 0x2e, 0xbe,
0xe9, 0x31, 0xbb, 0x0a, 0x73, 0x38, 0xa6, 0x9d, 0x60, 0x1b, 0xb6, 0xda, 0x61, 0xdb, 0x98, 0xa1, 0xb3, 0x30, 0x39, 0x62, 0x67, 0x61, 0x16, 0x27, 0xb4, 0x14, 0x1c, 0xd3, 0xd1, 0xda, 0x6c, 0x1b,
0xdb, 0x28, 0x50, 0x0a, 0x5d, 0xff, 0x32, 0x14, 0xf5, 0x5e, 0x47, 0xef, 0x61, 0x36, 0x5e, 0xa6, 0x53, 0x74, 0x1b, 0x45, 0x4a, 0xa1, 0xeb, 0x5f, 0x82, 0x92, 0xd1, 0x6d, 0x1b, 0x5d, 0xcc, 0xc6,
0xe3, 0xc0, 0x48, 0x82, 0xc1, 0xc4, 0x5d, 0xc3, 0xe6, 0x0c, 0x15, 0xc6, 0xc0, 0x48, 0x84, 0x41, 0x2b, 0x74, 0x1c, 0x18, 0x49, 0x30, 0x58, 0xb8, 0x63, 0x3a, 0x9c, 0xa1, 0xca, 0x18, 0x18, 0x89,
0x7e, 0x1b, 0xb2, 0xcc, 0x6b, 0x51, 0x11, 0x72, 0xcd, 0xed, 0x77, 0xd6, 0xef, 0x36, 0xeb, 0x95, 0x30, 0x28, 0x6f, 0x43, 0x8e, 0x79, 0x2d, 0x2a, 0x41, 0xbe, 0xb1, 0xfd, 0xce, 0xda, 0xdd, 0x46,
0x29, 0x34, 0x0d, 0x85, 0x07, 0x3b, 0x77, 0xef, 0xad, 0xd7, 0x9b, 0xdb, 0xb7, 0x2b, 0x09, 0x34, 0xbd, 0x3a, 0x81, 0x26, 0xa1, 0xf8, 0x60, 0xe7, 0xee, 0xbd, 0xb5, 0x7a, 0x63, 0xfb, 0x76, 0x55,
0x03, 0x70, 0xeb, 0xde, 0xd6, 0x56, 0xf3, 0xfe, 0x7d, 0x72, 0x9f, 0x24, 0xc3, 0xfc, 0x7e, 0xb3, 0x42, 0x53, 0x00, 0xb7, 0xee, 0x6d, 0x6d, 0x35, 0xee, 0xdf, 0x27, 0xf7, 0x29, 0x32, 0xcc, 0xef,
0x5e, 0x49, 0xa1, 0x12, 0xe4, 0xeb, 0x9b, 0x77, 0x37, 0xe9, 0x60, 0x5a, 0xfe, 0x30, 0x05, 0x88, 0x37, 0xea, 0xd5, 0x34, 0x2a, 0x43, 0xa1, 0xbe, 0x71, 0x77, 0x83, 0x0e, 0x66, 0x94, 0x0f, 0xd3,
0x05, 0xc4, 0x06, 0x3e, 0xd0, 0x7b, 0x27, 0x79, 0x2d, 0x3f, 0x9b, 0x40, 0xf6, 0x3b, 0x78, 0xfa, 0x80, 0x58, 0x40, 0xac, 0xe3, 0x03, 0xa3, 0x7b, 0x92, 0x57, 0xf3, 0xb3, 0x09, 0xe4, 0xa0, 0x83,
0x78, 0x0e, 0x1e, 0xe9, 0x3a, 0xb9, 0x53, 0x75, 0x9d, 0xfc, 0x49, 0x5c, 0x47, 0xfe, 0x55, 0x12, 0x67, 0x8e, 0xe7, 0xe0, 0xb1, 0xae, 0x93, 0x3f, 0x55, 0xd7, 0x29, 0x9c, 0xc4, 0x75, 0x94, 0x5f,
0x66, 0x7d, 0x66, 0xe0, 0x68, 0x7a, 0x66, 0x6a, 0xf5, 0xc1, 0x5d, 0x7a, 0x24, 0xdc, 0x45, 0x2a, 0xa7, 0x60, 0x26, 0x60, 0x06, 0x8e, 0xa6, 0x67, 0xa6, 0xd6, 0x00, 0xdc, 0x65, 0x86, 0xc2, 0x5d,
0x30, 0x73, 0xaa, 0x0a, 0xcc, 0x9e, 0x48, 0x81, 0x7f, 0x4b, 0x08, 0x05, 0xfa, 0xde, 0x0e, 0x27, 0xac, 0x02, 0xb3, 0xa7, 0xaa, 0xc0, 0xdc, 0x89, 0x14, 0xf8, 0x77, 0x49, 0x28, 0x30, 0xf0, 0x86,
0x77, 0x64, 0x9f, 0x62, 0x12, 0x23, 0x15, 0x33, 0x0c, 0x3a, 0x93, 0x27, 0x87, 0xce, 0x54, 0x0c, 0x38, 0xbe, 0x23, 0x07, 0x14, 0x23, 0x0d, 0x55, 0xcc, 0x20, 0xe8, 0x4c, 0x9d, 0x1c, 0x3a, 0xd3,
0x74, 0xca, 0xf3, 0x30, 0xe7, 0xdf, 0x2e, 0x6f, 0xea, 0xfc, 0x28, 0x01, 0x15, 0x36, 0x70, 0x92, 0x09, 0xd0, 0xa9, 0xcc, 0xc1, 0x6c, 0x70, 0xbb, 0xbc, 0xb1, 0xf3, 0x63, 0x09, 0xaa, 0x6c, 0xe0,
0x96, 0xe3, 0x59, 0xb9, 0x9d, 0xfc, 0x3a, 0x54, 0x3d, 0xab, 0x73, 0xfb, 0x96, 0x06, 0x25, 0x86, 0x24, 0x6d, 0xc7, 0xb3, 0x72, 0x3b, 0xe5, 0x75, 0x98, 0xf6, 0xad, 0xce, 0xeb, 0x5d, 0x9a, 0x94,
0xfb, 0x96, 0x8c, 0x59, 0xe1, 0xe3, 0xf2, 0x4f, 0x93, 0x62, 0xfe, 0x49, 0x7b, 0x88, 0x91, 0xdb, 0x18, 0xed, 0x5d, 0x32, 0x66, 0x95, 0x8f, 0x2b, 0x3f, 0x4b, 0x89, 0xf9, 0x27, 0xed, 0x23, 0xc6,
0xfb, 0x1f, 0xa8, 0x78, 0xb6, 0xe7, 0x2d, 0xa7, 0xcb, 0xee, 0x06, 0x59, 0x5d, 0xed, 0x63, 0xe5, 0x6e, 0xef, 0xff, 0xa0, 0xea, 0xdb, 0x9e, 0xbf, 0xa4, 0xae, 0x78, 0x1b, 0x64, 0xb5, 0x75, 0x80,
0x0d, 0xc9, 0x54, 0x80, 0xf5, 0x16, 0xeb, 0x4c, 0xfa, 0x4a, 0xe8, 0x74, 0x6c, 0x09, 0x9d, 0xf1, 0x95, 0x37, 0x25, 0xd3, 0x21, 0xd6, 0x5b, 0xac, 0x3b, 0x19, 0x28, 0xa1, 0x33, 0x89, 0x25, 0x74,
0x96, 0xd0, 0x4d, 0x28, 0xb3, 0x2d, 0xb7, 0xf4, 0x5e, 0xbb, 0x33, 0xd0, 0xb0, 0x1b, 0x1f, 0x01, 0xd6, 0x5f, 0x42, 0x37, 0xa0, 0xc2, 0xb6, 0xdc, 0x34, 0xba, 0xad, 0x76, 0x5f, 0xc7, 0x5e, 0x7c,
0xdd, 0x88, 0x6e, 0x64, 0x93, 0xf3, 0x29, 0x33, 0x6c, 0xa2, 0xb8, 0x97, 0x1f, 0x09, 0x80, 0x1f, 0x84, 0x74, 0x23, 0x3a, 0x92, 0x0d, 0xce, 0xa7, 0x4e, 0xb1, 0x89, 0xe2, 0x5e, 0x79, 0x24, 0x00,
0xb3, 0xc9, 0xe9, 0x17, 0x3b, 0xac, 0xc9, 0xf9, 0xeb, 0x14, 0xcc, 0xf8, 0xb9, 0x23, 0x1c, 0x24, 0x7e, 0xc4, 0x46, 0x67, 0x50, 0xec, 0xa0, 0x46, 0xe7, 0x6f, 0xd2, 0x30, 0x15, 0xe4, 0x8e, 0x71,
0x31, 0xc2, 0x41, 0x92, 0x71, 0x75, 0x5b, 0x6a, 0xbc, 0xba, 0xcd, 0x5f, 0x88, 0xa5, 0x4f, 0xa1, 0x10, 0x69, 0x88, 0x83, 0xa4, 0x92, 0xea, 0xb6, 0xf4, 0x68, 0x75, 0x5b, 0xb0, 0x10, 0xcb, 0x9c,
0x10, 0xcb, 0x9c, 0x42, 0x21, 0x96, 0x3d, 0xfd, 0x42, 0x2c, 0x77, 0x72, 0x34, 0xc9, 0xc7, 0xa1, 0x42, 0x21, 0x96, 0x3d, 0x85, 0x42, 0x2c, 0x77, 0xfa, 0x85, 0x58, 0xfe, 0xe4, 0x68, 0x52, 0x48,
0xc9, 0xff, 0xc1, 0x7c, 0xb4, 0x37, 0x21, 0x09, 0xf2, 0xce, 0xf4, 0x04, 0x7b, 0xe7, 0x11, 0xf7, 0x42, 0x93, 0x2f, 0xc0, 0x5c, 0xbc, 0x37, 0x21, 0x19, 0x0a, 0xee, 0x74, 0x89, 0xbd, 0xf3, 0x88,
0xf2, 0xc7, 0x09, 0xa8, 0x79, 0x92, 0xd6, 0x09, 0xcf, 0x12, 0xce, 0x0c, 0x73, 0xde, 0x82, 0xf3, 0x7b, 0xe5, 0x63, 0x09, 0x6a, 0xbe, 0xa4, 0x75, 0xc2, 0xf3, 0x84, 0x33, 0xc3, 0x9c, 0xb7, 0xe0,
0x11, 0xab, 0xe4, 0x71, 0x30, 0x19, 0xdc, 0xcb, 0xdf, 0x12, 0xb2, 0xde, 0xd4, 0x7b, 0xba, 0x75, 0x7c, 0xcc, 0x2a, 0x79, 0x1c, 0x8c, 0x07, 0xf7, 0xca, 0x77, 0x84, 0xac, 0x37, 0x8d, 0xae, 0x61,
0x78, 0xc2, 0x2d, 0x4f, 0xf8, 0xf0, 0x0b, 0x20, 0x45, 0x3d, 0x9c, 0x23, 0xff, 0xdf, 0x93, 0x50, 0x1f, 0x9e, 0x70, 0xcb, 0x63, 0x3e, 0xfc, 0x02, 0xc8, 0x71, 0x0f, 0xe7, 0xc8, 0xff, 0x8f, 0x14,
0xdc, 0x55, 0x6d, 0x31, 0xef, 0xec, 0x4a, 0x87, 0x13, 0xb5, 0xc7, 0x9b, 0x30, 0x4d, 0xc3, 0x8e, 0x94, 0x76, 0x35, 0x47, 0xcc, 0x3b, 0xbb, 0xd2, 0xe1, 0x44, 0x2d, 0xf2, 0x06, 0x4c, 0xd2, 0xb0,
0x24, 0x7f, 0x4d, 0xb5, 0xf1, 0x44, 0xd1, 0x56, 0x12, 0x53, 0xeb, 0xaa, 0x8d, 0xd1, 0x16, 0x94, 0x23, 0xc9, 0x5f, 0xd7, 0x1c, 0x3c, 0x56, 0xb4, 0x95, 0xc5, 0xd4, 0xba, 0xe6, 0x60, 0xb4, 0x05,
0xdd, 0xa6, 0x37, 0x13, 0x36, 0x49, 0xd8, 0xcd, 0xb8, 0x93, 0xa9, 0xb8, 0xeb, 0x30, 0x6b, 0xa9, 0x15, 0xaf, 0xf1, 0xcd, 0x84, 0x8d, 0x13, 0x76, 0x53, 0xde, 0x64, 0x2a, 0xee, 0x3a, 0xcc, 0xd8,
0x36, 0xee, 0x74, 0x74, 0x5a, 0x80, 0x1f, 0xf4, 0x54, 0x7b, 0x60, 0xf2, 0xf7, 0x1f, 0x05, 0x39, 0x9a, 0x83, 0xdb, 0x6d, 0x83, 0x16, 0xe0, 0x07, 0x5d, 0xcd, 0xe9, 0x5b, 0xfc, 0xfd, 0x47, 0x45,
0x43, 0xbb, 0x62, 0x44, 0xfe, 0x4b, 0x12, 0x72, 0xfc, 0xfd, 0x64, 0xd2, 0xaa, 0xe1, 0xff, 0x21, 0xee, 0xd0, 0xae, 0x18, 0x51, 0xfe, 0x9a, 0x82, 0x3c, 0x7f, 0x3f, 0x19, 0xb7, 0x6a, 0xf8, 0x22,
0xdf, 0x37, 0x2c, 0xdd, 0x16, 0x00, 0x58, 0x5c, 0x3b, 0xef, 0xfa, 0x0a, 0x97, 0xb9, 0xc3, 0x19, 0x14, 0x7a, 0xa6, 0x6d, 0x38, 0x02, 0x00, 0x4b, 0xab, 0xe7, 0x3d, 0x5f, 0xe1, 0x32, 0x77, 0x38,
0x14, 0x87, 0x15, 0xbd, 0x0e, 0xb3, 0xae, 0xe9, 0x9e, 0xe0, 0xa7, 0x1c, 0x19, 0x52, 0x51, 0xc8, 0x83, 0xea, 0xb2, 0xa2, 0xd7, 0x61, 0xc6, 0x33, 0xdd, 0x13, 0xfc, 0x94, 0x23, 0x43, 0x3a, 0x0e,
0xe0, 0x46, 0xf9, 0x1d, 0xfc, 0x94, 0x81, 0xc2, 0x15, 0x98, 0xf6, 0x4d, 0xe7, 0xdd, 0x9e, 0x92, 0x19, 0xbc, 0x28, 0xbf, 0x83, 0x9f, 0x32, 0x50, 0xb8, 0x02, 0x93, 0x81, 0xe9, 0xac, 0xee, 0x53,
0x97, 0x13, 0xad, 0xc2, 0x2c, 0x79, 0xfb, 0xf0, 0x1c, 0x60, 0xd0, 0xd8, 0x67, 0x07, 0x17, 0x55, 0xcb, 0x7e, 0x4e, 0xb4, 0x02, 0x33, 0xe4, 0xed, 0xc3, 0x77, 0x88, 0x41, 0x63, 0x9f, 0x1d, 0x5e,
0x32, 0xe4, 0x9c, 0x5c, 0xd4, 0xc9, 0x3b, 0xdc, 0x9a, 0x53, 0xcf, 0x61, 0xad, 0xc5, 0xdf, 0x6f, 0x4c, 0x93, 0x21, 0xf7, 0xf4, 0xa2, 0x4e, 0xde, 0xe1, 0x56, 0xdd, 0x7a, 0x0e, 0xeb, 0x4d, 0xfe,
0xe8, 0x0c, 0x76, 0x9c, 0xea, 0x2e, 0xb8, 0x49, 0xc7, 0xe8, 0x9c, 0x17, 0x21, 0x4b, 0x4f, 0x0d, 0x7e, 0x43, 0x67, 0xb0, 0x23, 0x55, 0x6f, 0xc1, 0x0d, 0x3a, 0x46, 0xe7, 0xbc, 0x08, 0x39, 0x7a,
0xac, 0x5a, 0x8e, 0x66, 0x9f, 0xb2, 0xbb, 0x79, 0xda, 0x47, 0x53, 0xf8, 0xb0, 0xdc, 0x80, 0x0c, 0x72, 0x60, 0xd7, 0xf2, 0x34, 0xfb, 0x54, 0xbc, 0xcd, 0xd3, 0x5e, 0x9a, 0xca, 0x87, 0x95, 0x4d,
0x25, 0xa0, 0x45, 0x28, 0xb0, 0x73, 0x86, 0xde, 0xa0, 0x4b, 0xf5, 0x9b, 0x51, 0xf2, 0x94, 0xb0, 0xc8, 0x52, 0x02, 0x5a, 0x80, 0x22, 0x3b, 0x6b, 0xe8, 0xf6, 0x3b, 0x54, 0xbf, 0x59, 0xb5, 0x40,
0x3d, 0xe8, 0x22, 0x19, 0xd2, 0x3d, 0x43, 0x13, 0xf5, 0xd6, 0x0c, 0xd7, 0x43, 0x76, 0xdb, 0xd0, 0x09, 0xdb, 0xfd, 0x0e, 0x52, 0x20, 0xd3, 0x35, 0x75, 0x51, 0x6f, 0x4d, 0x71, 0x3d, 0xe4, 0xb6,
0x70, 0xb3, 0xae, 0xd0, 0x31, 0xb9, 0x01, 0xe5, 0x80, 0x5e, 0xc9, 0xeb, 0x56, 0x5f, 0x35, 0x6d, 0x4d, 0x1d, 0x37, 0xea, 0x2a, 0x1d, 0x53, 0x36, 0xa1, 0x12, 0xd2, 0x2b, 0x79, 0xdd, 0xea, 0x69,
0x22, 0x72, 0x8f, 0xf7, 0xce, 0x33, 0x0a, 0x6d, 0xab, 0x6c, 0x53, 0x0a, 0x49, 0xcd, 0x7a, 0x4f, 0x96, 0x43, 0x44, 0xee, 0xf1, 0xfe, 0x79, 0x56, 0xa5, 0x6d, 0x95, 0x6d, 0x4a, 0x21, 0xa9, 0xd9,
0xc3, 0x47, 0xe2, 0x48, 0x91, 0xde, 0xc8, 0x7f, 0x48, 0xc0, 0x2c, 0x17, 0x75, 0xb2, 0x57, 0xa6, 0xe8, 0xea, 0xf8, 0x48, 0x1c, 0x2b, 0xd2, 0x1b, 0xe5, 0x8f, 0x12, 0xcc, 0x70, 0x51, 0x27, 0x7b,
0x67, 0xe3, 0x33, 0x2f, 0x40, 0xb9, 0xab, 0x1e, 0xb5, 0xe8, 0x11, 0x03, 0x6b, 0x80, 0xf2, 0xfe, 0x65, 0x7a, 0x36, 0x3e, 0xf3, 0x02, 0x54, 0x3a, 0xda, 0x51, 0x93, 0x1e, 0x33, 0xb0, 0x26, 0x28,
0xe9, 0x74, 0x57, 0x3d, 0x72, 0xfb, 0x9d, 0xf2, 0xef, 0x12, 0x30, 0xe7, 0xdf, 0x16, 0x47, 0xc8, 0xef, 0xa1, 0x4e, 0x76, 0xb4, 0x23, 0xaf, 0xe7, 0xa9, 0xfc, 0x5e, 0x82, 0xd9, 0xe0, 0xb6, 0x38,
0x57, 0x00, 0xc4, 0xeb, 0xb9, 0xb3, 0xce, 0x2a, 0x5f, 0x67, 0x41, 0xf4, 0x94, 0xeb, 0x4a, 0x81, 0x42, 0xbe, 0x02, 0x20, 0x5e, 0xcf, 0xdd, 0x75, 0x4e, 0xf3, 0x75, 0x16, 0x45, 0x5f, 0xb9, 0xae,
0x33, 0x35, 0xa3, 0x7b, 0xae, 0xc9, 0xd3, 0xe8, 0xb9, 0x4e, 0xd0, 0x80, 0xff, 0x63, 0xd2, 0xd9, 0x16, 0x39, 0x53, 0x23, 0xbe, 0xef, 0x9a, 0x3a, 0x8d, 0xbe, 0xeb, 0x18, 0x4d, 0xf8, 0x3f, 0xa5,
0xce, 0x09, 0x5f, 0x08, 0x26, 0xdf, 0x7f, 0x4c, 0x98, 0x26, 0x8f, 0x1b, 0xa6, 0xa9, 0xf1, 0xc3, 0xdc, 0xed, 0x9c, 0xf0, 0x85, 0x60, 0xfc, 0xfd, 0x27, 0x84, 0x69, 0xea, 0xb8, 0x61, 0x9a, 0x1e,
0x34, 0x1d, 0x17, 0xa6, 0xb7, 0x61, 0x7a, 0xd0, 0xef, 0x18, 0xaa, 0xd6, 0x32, 0xb1, 0x35, 0xe8, 0x3d, 0x4c, 0x33, 0x49, 0x61, 0x7a, 0x1b, 0x26, 0xfb, 0xbd, 0xb6, 0xa9, 0xe9, 0x4d, 0x0b, 0xdb,
0xd8, 0xfc, 0xec, 0x49, 0x0e, 0xbb, 0x10, 0x51, 0xea, 0x83, 0x3e, 0x3f, 0x82, 0x19, 0x74, 0x6c, 0xfd, 0xb6, 0xc3, 0xcf, 0x9f, 0x94, 0xa8, 0x0b, 0x11, 0xa5, 0x3e, 0xe8, 0xf1, 0x63, 0x98, 0x7e,
0xa5, 0x34, 0xf0, 0xdc, 0xc9, 0xdf, 0x71, 0xbb, 0xed, 0x21, 0xd6, 0xe1, 0x61, 0xfa, 0x22, 0xe4, 0xdb, 0x51, 0xcb, 0x7d, 0xdf, 0x9d, 0xf2, 0x3d, 0xaf, 0xe3, 0x1e, 0x61, 0x1d, 0x1c, 0xa6, 0x2f,
0xe8, 0x29, 0xb1, 0x73, 0x54, 0x18, 0x8c, 0xd4, 0x2c, 0x19, 0x6e, 0x6a, 0xe8, 0x1a, 0xa4, 0x0f, 0x42, 0x9e, 0x9e, 0x14, 0xbb, 0xc7, 0x85, 0xe1, 0x48, 0xcd, 0x91, 0xe1, 0x86, 0x8e, 0xae, 0x41,
0x55, 0xeb, 0x90, 0x7f, 0xd8, 0x54, 0x15, 0xc7, 0x63, 0xf4, 0x71, 0x0d, 0xd5, 0x3a, 0x54, 0xe8, 0xe6, 0x50, 0xb3, 0x0f, 0xf9, 0xc7, 0x4d, 0xd3, 0xe2, 0x88, 0x8c, 0x3e, 0x6e, 0x53, 0xb3, 0x0f,
0xb0, 0xfc, 0xef, 0x24, 0x94, 0x48, 0xc2, 0x13, 0x26, 0x40, 0x6b, 0xc1, 0x80, 0x2a, 0xae, 0x9d, 0x55, 0x3a, 0xac, 0xfc, 0x27, 0x05, 0x65, 0x92, 0xf0, 0x84, 0x09, 0xd0, 0x6a, 0x38, 0xa0, 0x4a,
0xf3, 0xec, 0xcf, 0xcd, 0x8d, 0x9e, 0xa8, 0x0a, 0x80, 0x40, 0x32, 0x1e, 0x04, 0x52, 0x1e, 0x10, 0xab, 0xe7, 0x7c, 0xfb, 0xf3, 0x72, 0xa3, 0x2f, 0xaa, 0x42, 0x20, 0x90, 0x4a, 0x06, 0x81, 0xb4,
0x08, 0x1f, 0x7e, 0x66, 0xc6, 0x38, 0xfc, 0x7c, 0x1b, 0xce, 0x39, 0x27, 0x80, 0x9e, 0x78, 0x24, 0x0f, 0x04, 0xa2, 0x07, 0xa0, 0xd9, 0x11, 0x0e, 0x40, 0xdf, 0x86, 0x73, 0xee, 0x29, 0xa0, 0x2f,
0xa5, 0xfd, 0x18, 0xc1, 0x31, 0x2b, 0xe6, 0xba, 0x34, 0x2b, 0x9c, 0x4e, 0x73, 0xc7, 0x4e, 0xa7, 0x1e, 0x49, 0x69, 0x3f, 0x42, 0x70, 0xcc, 0x88, 0xb9, 0x1e, 0xcd, 0x8e, 0xa6, 0xd3, 0xfc, 0xb1,
0x31, 0xf9, 0x2f, 0x1f, 0x9b, 0xff, 0xea, 0xce, 0x11, 0x97, 0xff, 0x1d, 0x14, 0xfd, 0x2f, 0x54, 0xd3, 0x69, 0x42, 0xfe, 0x2b, 0x24, 0xe6, 0xbf, 0xba, 0x7b, 0xcc, 0x15, 0x7c, 0x07, 0x45, 0xff,
0xad, 0x41, 0xbb, 0x8d, 0x2d, 0x6b, 0x7f, 0xd0, 0x69, 0x71, 0xa4, 0x67, 0xde, 0x50, 0x71, 0x07, 0x0f, 0xd3, 0x76, 0xbf, 0xd5, 0xc2, 0xb6, 0xbd, 0xdf, 0x6f, 0x37, 0x39, 0xd2, 0x33, 0x6f, 0xa8,
0x76, 0x18, 0xc4, 0xff, 0x36, 0xe9, 0xf8, 0xd3, 0x96, 0xfa, 0x04, 0xb3, 0x2c, 0xf1, 0x5f, 0x8e, 0x7a, 0x03, 0x3b, 0x0c, 0xe2, 0x7f, 0x97, 0x72, 0xfd, 0x69, 0x4b, 0x7b, 0x82, 0x59, 0x96, 0xf8,
0xa9, 0xcf, 0x22, 0x0f, 0xc7, 0xe6, 0xd5, 0x4c, 0x6c, 0x5e, 0x65, 0x6d, 0xff, 0x90, 0x2a, 0x79, 0x1f, 0xc7, 0xd4, 0x67, 0x91, 0x87, 0x13, 0xf3, 0x6a, 0x36, 0x31, 0xaf, 0xb2, 0xb6, 0x7f, 0x44,
0x7d, 0xf8, 0x33, 0xf7, 0xe0, 0xf7, 0x34, 0xca, 0xf5, 0x67, 0xa2, 0x69, 0xf9, 0x53, 0xf7, 0x60, 0x95, 0xbc, 0x3e, 0xfc, 0xb9, 0x77, 0xf8, 0x7b, 0x1a, 0xe5, 0xfa, 0x33, 0xd1, 0xb4, 0xf2, 0xa9,
0x38, 0xaa, 0x7a, 0xff, 0x7c, 0xe6, 0xa6, 0x5f, 0xba, 0x9b, 0x3a, 0x95, 0xd7, 0x88, 0xc9, 0xb5, 0x77, 0x38, 0x1c, 0x57, 0xbd, 0x7f, 0x3e, 0x73, 0xd3, 0xaf, 0xbc, 0x4d, 0x9d, 0xca, 0x6b, 0xc4,
0x70, 0x13, 0x72, 0x2c, 0x0d, 0x88, 0xcd, 0xc7, 0xe4, 0x01, 0x47, 0xdd, 0x24, 0x0f, 0x88, 0x29, 0xf8, 0x5a, 0xb8, 0x09, 0x79, 0x96, 0x06, 0xc4, 0xe6, 0x13, 0xf2, 0x80, 0xab, 0x6e, 0x92, 0x07,
0xa1, 0x14, 0xe0, 0xe5, 0x7a, 0xb6, 0x29, 0x60, 0x09, 0x16, 0x23, 0x15, 0xc9, 0x5d, 0xfe, 0x93, 0xc4, 0x94, 0x48, 0x0a, 0xf0, 0x73, 0x3d, 0xdb, 0x14, 0xb0, 0x08, 0x0b, 0xb1, 0x8a, 0xe4, 0x2e,
0x04, 0x20, 0x3e, 0x7e, 0xa2, 0x7e, 0xd1, 0x84, 0xbe, 0xbe, 0x01, 0x65, 0xd6, 0x11, 0x6a, 0x8d, 0xff, 0x89, 0x04, 0x88, 0x8f, 0x9f, 0xa8, 0x5f, 0x34, 0xa6, 0xaf, 0xaf, 0x43, 0x85, 0x75, 0x84,
0xef, 0xf2, 0x33, 0x6c, 0x86, 0x53, 0x9c, 0x3a, 0x6d, 0xa1, 0x94, 0xa7, 0x2d, 0x24, 0x3f, 0x76, 0x9a, 0xa3, 0xbb, 0xfc, 0x14, 0x9b, 0xe1, 0x16, 0xa7, 0x6e, 0x5b, 0x28, 0xed, 0x6b, 0x0b, 0x29,
0x4a, 0x4f, 0x5f, 0x33, 0xe7, 0xba, 0xbf, 0x99, 0x13, 0x7e, 0xcc, 0x38, 0xdd, 0x1c, 0xb7, 0x42, 0x8f, 0xdd, 0xd2, 0x33, 0xd0, 0xcc, 0xb9, 0x1e, 0x6c, 0xe6, 0x44, 0x1f, 0x33, 0x4a, 0x37, 0xc7,
0x76, 0xba, 0x39, 0xde, 0xa0, 0x4d, 0x8c, 0x1f, 0xb4, 0xbf, 0x48, 0x38, 0x1f, 0x1b, 0x04, 0x3e, 0xab, 0x90, 0xdd, 0x6e, 0x8e, 0x3f, 0x68, 0xa5, 0xd1, 0x83, 0xf6, 0x97, 0x92, 0xfb, 0xc1, 0x41,
0x31, 0xf9, 0x3c, 0xa8, 0x5e, 0xfe, 0x79, 0xca, 0xfd, 0xc4, 0x21, 0xf0, 0x31, 0xca, 0xe7, 0x13, 0xe8, 0x33, 0x93, 0xcf, 0x83, 0xea, 0x95, 0x5f, 0xa4, 0xbd, 0xcf, 0x1c, 0x42, 0x1f, 0xa4, 0x7c,
0x70, 0xe2, 0x73, 0x49, 0x3a, 0xfe, 0x1d, 0xed, 0x32, 0x94, 0x22, 0xbe, 0x5c, 0x2b, 0x5a, 0x9e, 0x3e, 0x01, 0x27, 0x39, 0x97, 0x64, 0x92, 0xdf, 0xd1, 0x2e, 0x43, 0x39, 0xe6, 0xeb, 0xb5, 0x92,
0x03, 0xb9, 0x98, 0x34, 0x98, 0x3d, 0x6e, 0x1a, 0xcc, 0x45, 0xa4, 0xc1, 0x97, 0x21, 0xdd, 0xc3, 0xed, 0x3b, 0x90, 0x4b, 0x48, 0x83, 0xb9, 0xe3, 0xa6, 0xc1, 0x7c, 0x4c, 0x1a, 0x7c, 0x19, 0x32,
0x47, 0xe2, 0x64, 0x73, 0x88, 0x15, 0x29, 0x9b, 0xfc, 0x3e, 0x94, 0x36, 0x54, 0xbb, 0x7d, 0x78, 0x5d, 0x7c, 0x24, 0x4e, 0x36, 0x07, 0x58, 0x91, 0xb2, 0x29, 0xef, 0x43, 0x79, 0x5d, 0x73, 0x5a,
0x6c, 0x7f, 0xfb, 0x02, 0xe4, 0x4d, 0x36, 0x20, 0xa2, 0x49, 0xf2, 0x7c, 0xff, 0xe9, 0x11, 0x4d, 0x87, 0xc7, 0xf6, 0xb7, 0x2f, 0x41, 0xc1, 0x62, 0x03, 0x22, 0x9a, 0x64, 0xdf, 0x37, 0xa0, 0x3e,
0xc3, 0xc9, 0xe1, 0x95, 0xff, 0x0a, 0x50, 0x09, 0x0e, 0xa3, 0x3a, 0x4c, 0xf3, 0x63, 0x7c, 0xd6, 0xd1, 0x34, 0x9c, 0x5c, 0x5e, 0xe5, 0x6f, 0x00, 0xd5, 0xf0, 0x30, 0xaa, 0xc3, 0x24, 0x3f, 0xc6,
0x6a, 0xe4, 0x41, 0xb4, 0x14, 0xfc, 0xa2, 0xd4, 0xf7, 0x15, 0x78, 0x63, 0x4a, 0x29, 0xed, 0x79, 0x67, 0xad, 0x46, 0x1e, 0x44, 0x8b, 0xe1, 0xaf, 0x4a, 0x03, 0x5f, 0x82, 0x6f, 0x4e, 0xa8, 0xe5,
0xc8, 0xe8, 0x06, 0xf0, 0x93, 0xff, 0xd6, 0x01, 0x76, 0x3f, 0x39, 0x0f, 0x88, 0x70, 0xbb, 0xfd, 0x3d, 0x1f, 0x19, 0xdd, 0x00, 0x7e, 0xf2, 0xdf, 0x3c, 0xc0, 0xde, 0x67, 0xe7, 0x21, 0x11, 0x5e,
0x8d, 0x29, 0xa5, 0xb0, 0x27, 0x68, 0x9e, 0x25, 0x68, 0x14, 0x1b, 0x39, 0xa2, 0x86, 0x96, 0xe0, 0xb7, 0x7f, 0x73, 0x42, 0x2d, 0xee, 0x09, 0x9a, 0x6f, 0x09, 0x3a, 0xc5, 0x46, 0x8e, 0xa8, 0x91,
0x4b, 0x41, 0xee, 0x12, 0x18, 0x19, 0x7d, 0xc9, 0xf9, 0x1e, 0xa1, 0xa3, 0x5b, 0xb6, 0xd3, 0xf3, 0x25, 0x04, 0x52, 0x90, 0xb7, 0x04, 0x46, 0x46, 0x5f, 0x71, 0xbf, 0x47, 0x68, 0x1b, 0xb6, 0xe3,
0x89, 0xf8, 0x30, 0xd6, 0x95, 0xc0, 0x17, 0x4d, 0x88, 0xe8, 0x6b, 0x30, 0xcf, 0xe7, 0x5b, 0xd8, 0xf6, 0x7c, 0x62, 0x3e, 0x8e, 0xf5, 0x24, 0xf0, 0x45, 0x13, 0x22, 0xfa, 0x06, 0xcc, 0xf1, 0xf9,
0x6e, 0xa9, 0xee, 0x77, 0x09, 0xbc, 0xfd, 0x73, 0x2d, 0x28, 0x2a, 0xf2, 0x53, 0x8a, 0xc6, 0x94, 0x36, 0x76, 0x9a, 0x9a, 0xf7, 0x5d, 0x02, 0x6f, 0xff, 0x5c, 0x0b, 0x8b, 0x8a, 0xfd, 0x94, 0x62,
0x32, 0xb7, 0x17, 0x31, 0x8c, 0xd6, 0xa1, 0xc4, 0xbb, 0xe5, 0x7b, 0xa4, 0x48, 0xe0, 0x6d, 0xa0, 0x73, 0x42, 0x9d, 0xdd, 0x8b, 0x19, 0x46, 0x6b, 0x50, 0xe6, 0xdd, 0xf2, 0x3d, 0x52, 0x24, 0xf0,
0x0b, 0xc1, 0xd6, 0xb1, 0xf7, 0x75, 0xbd, 0x31, 0xa5, 0x14, 0x0d, 0x97, 0x4a, 0xf4, 0xc4, 0x45, 0x36, 0xd0, 0x85, 0x70, 0xeb, 0xd8, 0xff, 0xba, 0xbe, 0x39, 0xa1, 0x96, 0x4c, 0x8f, 0x4a, 0xf4,
0xb4, 0x69, 0x31, 0xcb, 0x0b, 0xe9, 0xa5, 0xa0, 0x0c, 0xdf, 0xcb, 0x24, 0xd1, 0x93, 0xe1, 0x21, 0xc4, 0x45, 0xb4, 0x68, 0x31, 0xcb, 0x0b, 0xe9, 0xc5, 0xb0, 0x8c, 0xc0, 0xcb, 0x24, 0xd1, 0x93,
0x13, 0x53, 0x71, 0x29, 0xc4, 0x54, 0xf9, 0xa0, 0xa9, 0x82, 0x07, 0x33, 0xc4, 0x54, 0x86, 0xa0, 0xe9, 0x23, 0x13, 0x53, 0x71, 0x29, 0xc4, 0x54, 0x85, 0xb0, 0xa9, 0xc2, 0x07, 0x33, 0xc4, 0x54,
0x11, 0x25, 0xf3, 0xc9, 0x54, 0xc9, 0x85, 0xa0, 0x92, 0x43, 0x07, 0x1f, 0x44, 0xc9, 0x86, 0x43, 0xa6, 0xa0, 0x11, 0x25, 0xf3, 0xc9, 0x54, 0xc9, 0xc5, 0xb0, 0x92, 0x23, 0x07, 0x1f, 0x44, 0xc9,
0x44, 0xf7, 0x61, 0xd6, 0xab, 0x05, 0x61, 0x70, 0xa0, 0x72, 0xe4, 0x48, 0x65, 0x04, 0xad, 0x5e, 0xa6, 0x4b, 0x44, 0xf7, 0x61, 0xc6, 0xaf, 0x05, 0x61, 0x70, 0xa0, 0x72, 0x94, 0x58, 0x65, 0x84,
0x35, 0x82, 0x63, 0xe8, 0x21, 0xcc, 0x71, 0xa9, 0xfb, 0x34, 0xc5, 0x0a, 0xb1, 0x45, 0x2a, 0xf6, 0xad, 0x3e, 0x6d, 0x86, 0xc7, 0xd0, 0x43, 0x98, 0xe5, 0x52, 0xf7, 0x69, 0x8a, 0x15, 0x62, 0x4b,
0x4a, 0x50, 0x6c, 0x44, 0x41, 0xd3, 0x98, 0x52, 0x90, 0x11, 0x1a, 0x24, 0x1a, 0x17, 0x00, 0xc3, 0x54, 0xec, 0x95, 0xb0, 0xd8, 0x98, 0x82, 0x66, 0x73, 0x42, 0x45, 0x66, 0x64, 0x90, 0x68, 0x5c,
0xac, 0x56, 0x0a, 0x6a, 0x3c, 0xa2, 0xcb, 0x42, 0x34, 0x6e, 0x79, 0xc8, 0xe8, 0x36, 0xcc, 0x08, 0x00, 0x0c, 0xb3, 0x5a, 0x39, 0xac, 0xf1, 0x98, 0x2e, 0x0b, 0xd1, 0xb8, 0xed, 0x23, 0xa3, 0xdb,
0x29, 0xdc, 0x70, 0xec, 0x0c, 0xff, 0x62, 0x48, 0x4c, 0xd0, 0x72, 0xe2, 0xe9, 0xdc, 0x74, 0xf7, 0x30, 0x25, 0xa4, 0x70, 0xc3, 0xb1, 0x33, 0xfc, 0x8b, 0x11, 0x31, 0x61, 0xcb, 0x89, 0xa7, 0x73,
0x61, 0x56, 0x08, 0xea, 0xaa, 0x4f, 0x30, 0x87, 0x49, 0x7a, 0x8a, 0x1f, 0x55, 0x1e, 0x85, 0xde, 0xd3, 0xdd, 0x87, 0x19, 0x21, 0xa8, 0xa3, 0x3d, 0xc1, 0x1c, 0x26, 0xe9, 0x29, 0x7e, 0x5c, 0x79,
0x55, 0x88, 0xf6, 0xac, 0xe0, 0x18, 0xd1, 0x9e, 0x6f, 0x93, 0x42, 0x7b, 0xe5, 0xa0, 0xf6, 0x62, 0x14, 0x79, 0x57, 0x21, 0xda, 0xb3, 0xc3, 0x63, 0x44, 0x7b, 0x81, 0x4d, 0x0a, 0xed, 0x55, 0xc2,
0x2b, 0x73, 0xa2, 0x3d, 0x2b, 0x34, 0x88, 0x1e, 0xc3, 0x39, 0x21, 0xd8, 0x6f, 0x97, 0x0a, 0x95, 0xda, 0x4b, 0xac, 0xcc, 0x89, 0xf6, 0xec, 0xc8, 0x20, 0x7a, 0x0c, 0xe7, 0x84, 0xe0, 0xa0, 0x5d,
0x7c, 0x35, 0x24, 0x39, 0xda, 0x30, 0x62, 0xcf, 0x3e, 0xcb, 0xac, 0xbb, 0xd0, 0x4f, 0x3d, 0xb1, 0xaa, 0x54, 0xf2, 0xd5, 0x88, 0xe4, 0x78, 0xc3, 0x88, 0x3d, 0x07, 0x2c, 0xb3, 0xe6, 0x41, 0x3f,
0x1a, 0x0c, 0xa7, 0x70, 0x4d, 0x45, 0xc2, 0xc9, 0x72, 0xa9, 0x68, 0x0b, 0x2a, 0x42, 0x84, 0xc6, 0xf5, 0xc4, 0xe9, 0x70, 0x38, 0x45, 0x6b, 0x2a, 0x12, 0x4e, 0xb6, 0x47, 0x45, 0x5b, 0x50, 0x15,
0x73, 0x68, 0x0d, 0x05, 0x0f, 0xb0, 0xa2, 0x6b, 0x84, 0xc6, 0x94, 0x52, 0xb6, 0xfc, 0x23, 0x1b, 0x22, 0x74, 0x9e, 0x43, 0x6b, 0x28, 0x7c, 0x80, 0x15, 0x5f, 0x23, 0x6c, 0x4e, 0xa8, 0x15, 0x3b,
0x05, 0xc8, 0xf1, 0x51, 0xf9, 0x2d, 0x98, 0xe6, 0x38, 0xcb, 0x53, 0xf2, 0x17, 0xa1, 0x60, 0xf2, 0x38, 0xb2, 0x5e, 0x84, 0x3c, 0x1f, 0x55, 0xde, 0x82, 0x49, 0x8e, 0xb3, 0x3c, 0x25, 0x7f, 0x19,
0x6b, 0x01, 0xd9, 0x8b, 0x21, 0xc8, 0x66, 0xe3, 0x14, 0xb3, 0x5d, 0x6e, 0xf9, 0x9f, 0x00, 0xd5, 0x8a, 0x16, 0xbf, 0x16, 0x90, 0xbd, 0x10, 0x81, 0x6c, 0x36, 0x4e, 0x31, 0xdb, 0xe3, 0x56, 0xfe,
0x10, 0x03, 0xda, 0x8c, 0x46, 0xed, 0x8b, 0x71, 0xa8, 0xcd, 0xa6, 0x86, 0x60, 0xfb, 0x66, 0x04, 0x05, 0x30, 0x1d, 0x61, 0x40, 0x1b, 0xf1, 0xa8, 0x7d, 0x31, 0x09, 0xb5, 0xd9, 0xd4, 0x08, 0x6c,
0x6c, 0x2f, 0x46, 0xc2, 0xb6, 0x23, 0xc0, 0x83, 0xdb, 0x9b, 0xd1, 0xb8, 0x7d, 0x31, 0x0e, 0xb7, 0xdf, 0x8c, 0x81, 0xed, 0x85, 0x58, 0xd8, 0x76, 0x05, 0xf8, 0x70, 0x7b, 0x23, 0x1e, 0xb7, 0x2f,
0x83, 0x8b, 0xe0, 0xa6, 0x7c, 0x23, 0x0a, 0xb8, 0x2f, 0x44, 0x03, 0xb7, 0x23, 0xc2, 0x8b, 0xdc, 0x26, 0xe1, 0x76, 0x78, 0x11, 0xdc, 0x94, 0x6f, 0xc4, 0x01, 0xf7, 0x85, 0x78, 0xe0, 0x76, 0x45,
0x5f, 0x1f, 0x81, 0xdc, 0x2f, 0x8c, 0x42, 0x6e, 0x47, 0x6a, 0x34, 0x74, 0x6f, 0x44, 0x42, 0xf7, 0xf8, 0x91, 0xfb, 0x9b, 0x43, 0x90, 0xfb, 0x85, 0x61, 0xc8, 0xed, 0x4a, 0x8d, 0x87, 0xee, 0xf5,
0x52, 0x0c, 0x74, 0x3b, 0xc2, 0x7c, 0xd8, 0xbd, 0x19, 0x8d, 0xdd, 0x17, 0xe3, 0xb0, 0xdb, 0xd5, 0x58, 0xe8, 0x5e, 0x4c, 0x80, 0x6e, 0x57, 0x58, 0x00, 0xbb, 0x37, 0xe2, 0xb1, 0xfb, 0x62, 0x12,
0x95, 0x0f, 0xbc, 0x6f, 0x46, 0x80, 0xf7, 0x62, 0x24, 0x78, 0xbb, 0x06, 0x73, 0xd1, 0xfb, 0x8d, 0x76, 0x7b, 0xba, 0x0a, 0x80, 0xf7, 0xcd, 0x18, 0xf0, 0x5e, 0x88, 0x05, 0x6f, 0xcf, 0x60, 0x1e,
0x28, 0xf4, 0xbe, 0x10, 0x8d, 0xde, 0xae, 0xa6, 0x3d, 0xf0, 0xfd, 0x60, 0x18, 0x7c, 0x5f, 0x19, 0x7a, 0xbf, 0x11, 0x87, 0xde, 0x17, 0xe2, 0xd1, 0xdb, 0xd3, 0xb4, 0x0f, 0xbe, 0x1f, 0x0c, 0x82,
0x0a, 0xdf, 0x8e, 0xbc, 0x08, 0xfc, 0x7e, 0x34, 0x14, 0xbf, 0xaf, 0x0e, 0xc7, 0x6f, 0x47, 0x70, 0xef, 0x2b, 0x03, 0xe1, 0xdb, 0x95, 0x17, 0x83, 0xdf, 0x8f, 0x06, 0xe2, 0xf7, 0xd5, 0xc1, 0xf8,
0x14, 0x80, 0x6f, 0x46, 0x03, 0xf8, 0xc5, 0x38, 0x00, 0x77, 0xd5, 0xee, 0x43, 0xf0, 0x46, 0x0c, 0xed, 0x0a, 0x8e, 0x03, 0xf0, 0x8d, 0x78, 0x00, 0xbf, 0x98, 0x04, 0xe0, 0x9e, 0xda, 0x03, 0x08,
0x82, 0x2f, 0xc7, 0x22, 0xb8, 0x23, 0x28, 0x00, 0xe1, 0x0f, 0x86, 0x41, 0xf8, 0x95, 0xa1, 0x10, 0xbe, 0x99, 0x80, 0xe0, 0x4b, 0x89, 0x08, 0xee, 0x0a, 0x0a, 0x41, 0xf8, 0x83, 0x41, 0x10, 0x7e,
0xee, 0x6a, 0x30, 0x8c, 0xe1, 0x8f, 0x86, 0x62, 0xf8, 0xd5, 0xe1, 0x18, 0xee, 0x6a, 0x30, 0x02, 0x65, 0x20, 0x84, 0x7b, 0x1a, 0x8c, 0x62, 0xf8, 0xa3, 0x81, 0x18, 0x7e, 0x75, 0x30, 0x86, 0x7b,
0xc4, 0xbf, 0x3a, 0x1c, 0xc4, 0xaf, 0x8d, 0x00, 0x71, 0x47, 0x76, 0x24, 0x8a, 0x6f, 0x44, 0xa2, 0x1a, 0x8c, 0x01, 0xf1, 0xaf, 0x0f, 0x06, 0xf1, 0x6b, 0x43, 0x40, 0xdc, 0x95, 0x1d, 0x8b, 0xe2,
0xf8, 0x52, 0x0c, 0x8a, 0xbb, 0x91, 0xe5, 0x85, 0xf1, 0xed, 0x58, 0x18, 0xbf, 0x3c, 0x04, 0xc6, 0xeb, 0xb1, 0x28, 0xbe, 0x98, 0x80, 0xe2, 0x5e, 0x64, 0xf9, 0x61, 0x7c, 0x3b, 0x11, 0xc6, 0x2f,
0x1d, 0x59, 0x21, 0x1c, 0x07, 0xc8, 0x8b, 0xe1, 0xb5, 0x7f, 0x54, 0x21, 0xbf, 0xc5, 0x65, 0xa0, 0x0f, 0x80, 0x71, 0x57, 0x56, 0x04, 0xc7, 0x01, 0x0a, 0x62, 0x78, 0xf5, 0x9f, 0xd3, 0x50, 0xd8,
0x2d, 0x28, 0x31, 0xd8, 0xe4, 0xff, 0xcb, 0x0e, 0x2f, 0x91, 0xa5, 0x11, 0x58, 0x8c, 0xea, 0x50, 0xe2, 0x32, 0xd0, 0x16, 0x94, 0x19, 0x6c, 0xf2, 0x7f, 0x66, 0x07, 0x97, 0xc8, 0xf2, 0x10, 0x2c,
0xb8, 0x8d, 0x6d, 0x2e, 0x6b, 0x48, 0xad, 0x2c, 0x0d, 0x03, 0x64, 0xb2, 0x28, 0xa6, 0xcb, 0xb8, 0x46, 0x75, 0x28, 0xde, 0xc6, 0x0e, 0x97, 0x35, 0xa0, 0x56, 0x96, 0x07, 0x01, 0x32, 0x59, 0x14,
0x45, 0xf9, 0xb2, 0xa9, 0x34, 0x02, 0x9b, 0x51, 0x03, 0x8a, 0x44, 0xa9, 0x6c, 0xcc, 0x42, 0xc3, 0xd3, 0x65, 0xd2, 0xa2, 0x02, 0xd9, 0x54, 0x1e, 0x82, 0xcd, 0x68, 0x13, 0x4a, 0x44, 0xa9, 0x6c,
0xca, 0x67, 0x69, 0x28, 0x44, 0x23, 0x0c, 0x73, 0xbb, 0x62, 0x7b, 0x5e, 0x30, 0x1d, 0xaf, 0x8c, 0xcc, 0x46, 0x83, 0xca, 0x67, 0x79, 0x20, 0x44, 0x23, 0x0c, 0xb3, 0xbb, 0x62, 0x7b, 0x7e, 0x30,
0x96, 0xc6, 0xc4, 0x6c, 0xf4, 0x16, 0x14, 0xa9, 0xb7, 0xf2, 0x6f, 0x75, 0x87, 0xd6, 0xd3, 0xd2, 0x1d, 0xad, 0x8c, 0x96, 0x47, 0xc4, 0x6c, 0xf4, 0x16, 0x94, 0xa8, 0xb7, 0xf2, 0x6f, 0x75, 0x07,
0x70, 0xc8, 0xa6, 0x06, 0xa6, 0x51, 0xca, 0x85, 0x0d, 0x2f, 0xac, 0xa5, 0x11, 0xd8, 0xcd, 0x0d, 0xd6, 0xd3, 0xf2, 0x60, 0xc8, 0xa6, 0x06, 0xa6, 0x51, 0xca, 0x85, 0x0d, 0x2e, 0xac, 0xe5, 0x21,
0xcc, 0x65, 0x0d, 0xa9, 0xb0, 0xa5, 0x61, 0x00, 0x2e, 0x2c, 0xc2, 0x06, 0x7c, 0x16, 0x09, 0xd5, 0xd8, 0xcd, 0x0d, 0xcc, 0x65, 0x0d, 0xa8, 0xb0, 0xe5, 0x41, 0x00, 0x2e, 0x2c, 0xc2, 0x06, 0x02,
0xda, 0xd2, 0x50, 0x28, 0x47, 0xef, 0x42, 0xd5, 0x13, 0xd8, 0x7c, 0x5d, 0x63, 0xd4, 0xdc, 0xd2, 0x16, 0x89, 0xd4, 0xda, 0xf2, 0x40, 0x28, 0x47, 0xef, 0xc2, 0xb4, 0x2f, 0xb0, 0xf9, 0xba, 0x46,
0x38, 0xc0, 0x8e, 0x5a, 0x80, 0xbc, 0xa1, 0xcd, 0xc5, 0x8f, 0x53, 0x7b, 0x4b, 0x63, 0x01, 0x3c, 0xa8, 0xb9, 0xe5, 0x51, 0x80, 0x1d, 0x35, 0x01, 0xf9, 0x43, 0x9b, 0x8b, 0x1f, 0xa5, 0xf6, 0x96,
0xb1, 0x0e, 0x7d, 0xae, 0x38, 0xce, 0x1e, 0x5e, 0x84, 0x4b, 0x23, 0x20, 0x1e, 0xed, 0xc0, 0x34, 0x47, 0x02, 0x78, 0x62, 0x1d, 0xfa, 0x5c, 0x71, 0x9c, 0x3d, 0xb8, 0x08, 0x97, 0x87, 0x40, 0x3c,
0xb3, 0x97, 0x90, 0x37, 0xa2, 0x1a, 0x97, 0x46, 0x61, 0x3d, 0xd1, 0xaf, 0x8b, 0xc8, 0x42, 0xea, 0xda, 0x81, 0x49, 0x66, 0x2f, 0x21, 0x6f, 0x48, 0x35, 0x2e, 0x0f, 0xc3, 0x7a, 0xa2, 0x5f, 0x0f,
0x18, 0x55, 0xb9, 0x34, 0x0e, 0xec, 0x13, 0xfd, 0x7a, 0xd4, 0x2e, 0xc4, 0x8f, 0x53, 0x9d, 0x4b, 0x91, 0x85, 0xd4, 0x11, 0xaa, 0x72, 0x79, 0x14, 0xd8, 0x27, 0xfa, 0xf5, 0xa9, 0x5d, 0x88, 0x1f,
0x63, 0xc1, 0x3f, 0xda, 0x83, 0x59, 0xaf, 0xde, 0xc5, 0x13, 0xc6, 0xaa, 0xd2, 0xa5, 0xf1, 0xd2, 0xa5, 0x3a, 0x97, 0x47, 0x82, 0x7f, 0xb4, 0x07, 0x33, 0x7e, 0xbd, 0x8b, 0x27, 0x8c, 0x54, 0xa5,
0x00, 0xba, 0x03, 0x25, 0xef, 0x3f, 0x12, 0x68, 0x68, 0xbd, 0x2e, 0x0d, 0xcf, 0x03, 0xe8, 0x1d, 0xcb, 0xa3, 0xa5, 0x01, 0x74, 0x07, 0xca, 0xfe, 0x7f, 0x24, 0xd0, 0xc0, 0x7a, 0x5d, 0x1e, 0x9c,
0x28, 0x0b, 0xd0, 0x16, 0x8b, 0x1d, 0x59, 0xb8, 0x4b, 0xa3, 0x73, 0x02, 0x7a, 0x0d, 0x32, 0xb4, 0x07, 0xd0, 0x3b, 0x50, 0x11, 0xa0, 0x2d, 0x16, 0x3b, 0xb4, 0x70, 0x97, 0x87, 0xe7, 0x04, 0xf4,
0xe0, 0x46, 0xf3, 0xd1, 0x5d, 0x15, 0x69, 0x21, 0xa6, 0x74, 0x47, 0x0f, 0xa1, 0xc2, 0x40, 0x9e, 0x1a, 0x64, 0x69, 0xc1, 0x8d, 0xe6, 0xe2, 0xbb, 0x2a, 0xf2, 0x7c, 0x42, 0xe9, 0x8e, 0x1e, 0x42,
0x8b, 0xbe, 0xd7, 0xd1, 0x22, 0x96, 0x14, 0xf8, 0x83, 0x34, 0x62, 0x49, 0xa1, 0x3f, 0x22, 0xbf, 0x95, 0x81, 0x3c, 0x17, 0x7d, 0xaf, 0xad, 0xc7, 0x2c, 0x29, 0xf4, 0x17, 0x69, 0xcc, 0x92, 0x22,
0x02, 0x15, 0x9f, 0xb3, 0x12, 0xda, 0xe5, 0xe1, 0xfe, 0x4a, 0x24, 0xcb, 0x23, 0x5c, 0x96, 0x88, 0x7f, 0x45, 0x7e, 0x0d, 0xaa, 0x01, 0x67, 0x25, 0xb4, 0xcb, 0x83, 0xfd, 0x95, 0x48, 0x56, 0x86,
0xd9, 0x85, 0x19, 0xcf, 0x4f, 0x57, 0x84, 0x12, 0x76, 0x74, 0xff, 0xef, 0x61, 0xd2, 0xa5, 0x18, 0xb8, 0x2c, 0x11, 0xb3, 0x0b, 0x53, 0xbe, 0x1f, 0xaf, 0x08, 0x25, 0xea, 0xe8, 0xc1, 0x5f, 0xc4,
0x06, 0x57, 0x68, 0x0b, 0x50, 0xc0, 0x34, 0x84, 0x7a, 0x65, 0x94, 0x75, 0x88, 0xf0, 0xab, 0x23, 0xe4, 0x4b, 0x09, 0x0c, 0x9e, 0xd0, 0x26, 0xa0, 0x90, 0x69, 0x08, 0xf5, 0xca, 0x30, 0xeb, 0x10,
0x0d, 0xc4, 0x15, 0xe2, 0x73, 0xd3, 0x68, 0x85, 0x04, 0xff, 0xfe, 0x8a, 0x50, 0x48, 0xf8, 0x6f, 0xe1, 0x57, 0x87, 0x1a, 0x88, 0x2b, 0x24, 0xe0, 0xa6, 0xf1, 0x0a, 0x09, 0xff, 0x01, 0x16, 0xa3,
0xac, 0x77, 0xa0, 0xec, 0xf5, 0xd1, 0x80, 0x0d, 0xa3, 0xff, 0x91, 0xf2, 0xda, 0x30, 0xee, 0xb7, 0x90, 0xe8, 0x1f, 0x59, 0xef, 0x40, 0xc5, 0xef, 0xa3, 0x21, 0x1b, 0xc6, 0xff, 0x27, 0xe5, 0xb7,
0xa1, 0x77, 0xa1, 0xea, 0xcf, 0x61, 0x84, 0xe8, 0x5b, 0x50, 0xf4, 0x9f, 0x39, 0x7e, 0x78, 0x88, 0x61, 0xd2, 0x6f, 0x43, 0xef, 0xc2, 0x74, 0x30, 0x87, 0x11, 0x62, 0x60, 0x41, 0xf1, 0x7f, 0xe6,
0xf9, 0x61, 0x86, 0xe4, 0x41, 0xcf, 0x9f, 0x2e, 0xde, 0xc0, 0x0a, 0xff, 0x47, 0xe3, 0x0d, 0xac, 0x04, 0xe1, 0x21, 0xe1, 0x87, 0x19, 0x92, 0x07, 0x7d, 0x7f, 0xba, 0xf8, 0x03, 0x2b, 0xfa, 0x1f,
0x88, 0xdf, 0x63, 0x36, 0xd2, 0x8f, 0x93, 0xfd, 0xbd, 0xbd, 0x2c, 0x3d, 0x97, 0x7d, 0xf5, 0x3f, 0x8d, 0x3f, 0xb0, 0x62, 0x7e, 0x8f, 0x59, 0xcf, 0x3c, 0x4e, 0xf5, 0xf6, 0xf6, 0x72, 0xf4, 0x5c,
0x01, 0x00, 0x00, 0xff, 0xff, 0x48, 0x9e, 0xe0, 0x05, 0xd0, 0x44, 0x00, 0x00, 0xf6, 0xd5, 0xff, 0x06, 0x00, 0x00, 0xff, 0xff, 0x70, 0x7c, 0x53, 0x8c, 0xd4, 0x44, 0x00, 0x00,
} }
type DRPCMetainfoClient interface { type DRPCMetainfoClient interface {

View File

@ -215,7 +215,7 @@ message ListSegmentsRequestOld {
bytes bucket = 1; bytes bucket = 1;
bytes prefix = 2; bytes prefix = 2;
bytes start_after = 3; bytes start_after = 3;
bytes end_before = 4; bytes end_before = 4 [deprecated=true];
bool recursive = 5; bool recursive = 5;
int32 limit = 6; int32 limit = 6;
fixed32 meta_flags = 7; fixed32 meta_flags = 7;

View File

@ -77,9 +77,9 @@ func (create CreateObject) Object(bucket Bucket, path Path) Object {
type ListDirection int8 type ListDirection int8
const ( const (
// Before lists backwards from cursor, without cursor // Before lists backwards from cursor, without cursor [NOT SUPPORTED]
Before = ListDirection(-2) Before = ListDirection(-2)
// Backward lists backwards from cursor, including cursor // Backward lists backwards from cursor, including cursor [NOT SUPPORTED]
Backward = ListDirection(-1) Backward = ListDirection(-1)
// Forward lists forwards from cursor, including cursor // Forward lists forwards from cursor, including cursor
Forward = ListDirection(1) Forward = ListDirection(1)
@ -114,24 +114,12 @@ func (opts ListOptions) NextPage(list ObjectList) ListOptions {
return ListOptions{} return ListOptions{}
} }
switch opts.Direction {
case Before, Backward:
return ListOptions{
Prefix: opts.Prefix,
Cursor: list.Items[0].Path,
Direction: Before,
Limit: opts.Limit,
}
case After, Forward:
return ListOptions{ return ListOptions{
Prefix: opts.Prefix, Prefix: opts.Prefix,
Cursor: list.Items[len(list.Items)-1].Path, Cursor: list.Items[len(list.Items)-1].Path,
Direction: After, Direction: After,
Limit: opts.Limit, Limit: opts.Limit,
} }
}
return ListOptions{}
} }
// BucketListOptions lists objects // BucketListOptions lists objects
@ -153,22 +141,11 @@ func (opts BucketListOptions) NextPage(list BucketList) BucketListOptions {
return BucketListOptions{} return BucketListOptions{}
} }
switch opts.Direction {
case Before, Backward:
return BucketListOptions{
Cursor: list.Items[0].Name,
Direction: Before,
Limit: opts.Limit,
}
case After, Forward:
return BucketListOptions{ return BucketListOptions{
Cursor: list.Items[len(list.Items)-1].Name, Cursor: list.Items[len(list.Items)-1].Name,
Direction: After, Direction: After,
Limit: opts.Limit, Limit: opts.Limit,
} }
}
return BucketListOptions{}
} }
// MetainfoLimits lists limits specified for the Metainfo database // MetainfoLimits lists limits specified for the Metainfo database

View File

@ -2431,7 +2431,13 @@
{ {
"id": 4, "id": 4,
"name": "end_before", "name": "end_before",
"type": "bytes" "type": "bytes",
"options": [
{
"name": "deprecated",
"value": "true"
}
]
}, },
{ {
"id": 5, "id": 5,

View File

@ -438,7 +438,7 @@ func (endpoint *Endpoint) ListSegmentsOld(ctx context.Context, req *pb.ListSegme
return nil, rpcstatus.Error(rpcstatus.InvalidArgument, err.Error()) return nil, rpcstatus.Error(rpcstatus.InvalidArgument, err.Error())
} }
items, more, err := endpoint.metainfo.List(ctx, prefix, string(req.StartAfter), string(req.EndBefore), req.Recursive, req.Limit, req.MetaFlags) items, more, err := endpoint.metainfo.List(ctx, prefix, string(req.StartAfter), req.Recursive, req.Limit, req.MetaFlags)
if err != nil { if err != nil {
return nil, rpcstatus.Error(rpcstatus.Internal, err.Error()) return nil, rpcstatus.Error(rpcstatus.Internal, err.Error())
} }
@ -924,7 +924,7 @@ func (endpoint *Endpoint) setBucketAttribution(ctx context.Context, header *pb.R
return rpcstatus.Error(rpcstatus.InvalidArgument, err.Error()) return rpcstatus.Error(rpcstatus.InvalidArgument, err.Error())
} }
items, _, err := endpoint.metainfo.List(ctx, prefix, "", "", true, 1, 0) items, _, err := endpoint.metainfo.List(ctx, prefix, "", true, 1, 0)
if err != nil { if err != nil {
endpoint.log.Error("error while listing segments", zap.Error(err)) endpoint.log.Error("error while listing segments", zap.Error(err))
return rpcstatus.Error(rpcstatus.Internal, err.Error()) return rpcstatus.Error(rpcstatus.Internal, err.Error())
@ -1292,8 +1292,7 @@ func (endpoint *Endpoint) ListObjects(ctx context.Context, req *pb.ObjectListReq
metaflags := meta.All metaflags := meta.All
// TODO use flags // TODO use flags
// TODO find out how EncryptedCursor -> startAfter/endAfter segments, more, err := endpoint.metainfo.List(ctx, prefix, string(req.EncryptedCursor), req.Recursive, req.Limit, metaflags)
segments, more, err := endpoint.metainfo.List(ctx, prefix, string(req.EncryptedCursor), "", req.Recursive, req.Limit, metaflags)
if err != nil { if err != nil {
return nil, rpcstatus.Error(rpcstatus.Internal, err.Error()) return nil, rpcstatus.Error(rpcstatus.Internal, err.Error())
} }

View File

@ -196,7 +196,7 @@ func (s *Service) GetWithBytes(ctx context.Context, path string) (pointerBytes [
} }
// List returns all Path keys in the pointers bucket // List returns all Path keys in the pointers bucket
func (s *Service) List(ctx context.Context, prefix string, startAfter string, endBefore string, recursive bool, limit int32, func (s *Service) List(ctx context.Context, prefix string, startAfter string, recursive bool, limit int32,
metaFlags uint32) (items []*pb.ListResponse_Item, more bool, err error) { metaFlags uint32) (items []*pb.ListResponse_Item, more bool, err error) {
defer mon.Task()(&ctx)(&err) defer mon.Task()(&ctx)(&err)
@ -211,7 +211,6 @@ func (s *Service) List(ctx context.Context, prefix string, startAfter string, en
rawItems, more, err := storage.ListV2(ctx, s.db, storage.ListOptions{ rawItems, more, err := storage.ListV2(ctx, s.db, storage.ListOptions{
Prefix: prefixKey, Prefix: prefixKey,
StartAfter: storage.Key(startAfter), StartAfter: storage.Key(startAfter),
EndBefore: storage.Key(endBefore),
Recursive: recursive, Recursive: recursive,
Limit: int(limit), Limit: int(limit),
IncludeValue: metaFlags != meta.None, IncludeValue: metaFlags != meta.None,

View File

@ -597,7 +597,7 @@ func TestRepairMultipleDisqualified(t *testing.T) {
// get a remote segment from metainfo // get a remote segment from metainfo
metainfo := satellite.Metainfo.Service metainfo := satellite.Metainfo.Service
listResponse, _, err := metainfo.List(ctx, "", "", "", true, 0, 0) listResponse, _, err := metainfo.List(ctx, "", "", true, 0, 0)
require.NoError(t, err) require.NoError(t, err)
var path string var path string
@ -1036,7 +1036,7 @@ func getRemoteSegment(
// get a remote segment from metainfo // get a remote segment from metainfo
metainfo := satellite.Metainfo.Service metainfo := satellite.Metainfo.Service
listResponse, _, err := metainfo.List(ctx, "", "", "", true, 0, 0) listResponse, _, err := metainfo.List(ctx, "", "", true, 0, 0)
require.NoError(t, err) require.NoError(t, err)
for _, v := range listResponse { for _, v := range listResponse {

View File

@ -226,12 +226,7 @@ func (client *Client) GetAll(ctx context.Context, keys storage.Keys) (_ storage.
func (client *Client) Iterate(ctx context.Context, opts storage.IterateOptions, fn func(context.Context, storage.Iterator) error) (err error) { func (client *Client) Iterate(ctx context.Context, opts storage.IterateOptions, fn func(context.Context, storage.Iterator) error) (err error) {
defer mon.Task()(&ctx)(&err) defer mon.Task()(&ctx)(&err)
return client.view(func(bucket *bolt.Bucket) error { return client.view(func(bucket *bolt.Bucket) error {
var cursor advancer var cursor advancer = forward{bucket.Cursor()}
if !opts.Reverse {
cursor = forward{bucket.Cursor()}
} else {
cursor = backward{bucket.Cursor()}
}
start := true start := true
lastPrefix := []byte{} lastPrefix := []byte{}
@ -307,46 +302,6 @@ func (cursor forward) Advance() (key, value []byte) {
return cursor.Next() return cursor.Next()
} }
type backward struct {
*bolt.Cursor
}
func (cursor backward) PositionToFirst(prefix, first storage.Key) (key, value []byte) {
if prefix.IsZero() {
// there's no prefix
if first.IsZero() {
// and no first item, so start from the end
return cursor.Last()
}
} else {
// there's a prefix
if first.IsZero() || storage.AfterPrefix(prefix).Less(first) {
// there's no first, or it's after our prefix
// storage.AfterPrefix("axxx/") is the next item after prefixes
// so we position to the item before
nextkey := storage.AfterPrefix(prefix)
_, _ = cursor.Seek(nextkey)
return cursor.Prev()
}
}
// otherwise try to position on first or one before that
key, value = cursor.Seek(first)
if !bytes.Equal(key, first) {
key, value = cursor.Prev()
}
return key, value
}
func (cursor backward) SkipPrefix(prefix storage.Key) (key, value []byte) {
_, _ = cursor.Seek(prefix)
return cursor.Prev()
}
func (cursor backward) Advance() (key, value []byte) {
return cursor.Prev()
}
// CompareAndSwap atomically compares and swaps oldValue with newValue // CompareAndSwap atomically compares and swaps oldValue with newValue
func (client *Client) CompareAndSwap(ctx context.Context, key storage.Key, oldValue, newValue storage.Value) (err error) { func (client *Client) CompareAndSwap(ctx context.Context, key storage.Key, oldValue, newValue storage.Value) (err error) {
defer mon.Task()(&ctx)(&err) defer mon.Task()(&ctx)(&err)

View File

@ -85,8 +85,6 @@ type IterateOptions struct {
First Key First Key
// Recurse, do not collapse items based on Delimiter // Recurse, do not collapse items based on Delimiter
Recurse bool Recurse bool
// Reverse iterates in reverse order
Reverse bool
} }
// Iterator iterates over a sequence of ListItems // Iterator iterates over a sequence of ListItems

View File

@ -60,26 +60,6 @@ func SortAndCollapse(items Items, prefix []byte) Items {
return result return result
} }
// ReverseItems reverses items in the list
// items will be reused and modified
// TODO: remove this
func ReverseItems(items Items) Items {
for i := len(items)/2 - 1; i >= 0; i-- {
k := len(items) - 1 - i
items[i], items[k] = items[k], items[i]
}
return items
}
// ReverseKeys reverses the list of keys
func ReverseKeys(keys Keys) Keys {
for i := len(keys)/2 - 1; i >= 0; i-- {
k := len(keys) - 1 - i
keys[i], keys[k] = keys[k], keys[i]
}
return keys
}
// StaticIterator implements an iterator over list of items // StaticIterator implements an iterator over list of items
type StaticIterator struct { type StaticIterator struct {
Items Items Items Items

View File

@ -5,14 +5,12 @@ package storage
import ( import (
"context" "context"
"errors"
) )
// ListOptions are items that are optional for the LIST method // ListOptions are items that are optional for the LIST method
type ListOptions struct { type ListOptions struct {
Prefix Key Prefix Key
StartAfter Key // StartAfter is relative to Prefix StartAfter Key // StartAfter is relative to Prefix
EndBefore Key // EndBefore is relative to Prefix
Recursive bool Recursive bool
IncludeValue bool IncludeValue bool
Limit int Limit int
@ -24,12 +22,9 @@ type ListOptions struct {
// more indicates if the result was truncated. If false // more indicates if the result was truncated. If false
// then the result []ListItem includes all requested keys. // then the result []ListItem includes all requested keys.
// If true then the caller must call List again to get more // If true then the caller must call List again to get more
// results by setting `StartAfter` or `EndBefore` appropriately. // results by setting `StartAfter` appropriately.
func ListV2(ctx context.Context, store KeyValueStore, opts ListOptions) (result Items, more bool, err error) { func ListV2(ctx context.Context, store KeyValueStore, opts ListOptions) (result Items, more bool, err error) {
defer mon.Task()(&ctx)(&err) defer mon.Task()(&ctx)(&err)
if !opts.StartAfter.IsZero() && !opts.EndBefore.IsZero() {
return nil, false, errors.New("start-after and end-before cannot be combined")
}
limit := opts.Limit limit := opts.Limit
if limit <= 0 || limit > LookupLimit { if limit <= 0 || limit > LookupLimit {
@ -37,15 +32,8 @@ func ListV2(ctx context.Context, store KeyValueStore, opts ListOptions) (result
} }
more = true more = true
reverse := !opts.EndBefore.IsZero()
var first Key
if !reverse {
first = opts.StartAfter
} else {
first = opts.EndBefore
}
first := opts.StartAfter
iterate := func(ctx context.Context, it Iterator) error { iterate := func(ctx context.Context, it Iterator) error {
var item ListItem var item ListItem
skipFirst := true skipFirst := true
@ -86,23 +74,15 @@ func ListV2(ctx context.Context, store KeyValueStore, opts ListOptions) (result
} }
var firstFull Key var firstFull Key
if !reverse && !opts.StartAfter.IsZero() { if !opts.StartAfter.IsZero() {
firstFull = joinKey(opts.Prefix, opts.StartAfter) firstFull = joinKey(opts.Prefix, opts.StartAfter)
} }
if reverse && !opts.EndBefore.IsZero() {
firstFull = joinKey(opts.Prefix, opts.EndBefore)
}
err = store.Iterate(ctx, IterateOptions{ err = store.Iterate(ctx, IterateOptions{
Prefix: opts.Prefix, Prefix: opts.Prefix,
First: firstFull, First: firstFull,
Reverse: reverse,
Recurse: opts.Recursive, Recurse: opts.Recursive,
}, iterate) }, iterate)
if reverse {
result = ReverseItems(result)
}
return result, more, err return result, more, err
} }

View File

@ -57,29 +57,6 @@ FROM (
) x ) x
ORDER BY p ORDER BY p
LIMIT $4 LIMIT $4
`
alternateReverseQuery = `
SELECT DISTINCT
$2::BYTEA || x.localpath AS p,
first_value(x.metadata) OVER (PARTITION BY x.localpath ORDER BY x.fullpath) AS m
FROM (
SELECT
pd.fullpath,
local_path(pd.fullpath, $2::BYTEA, set_byte(' '::BYTEA, 0, b.delim)) AS localpath,
pd.metadata
FROM
pathdata pd,
buckets b
WHERE
b.bucketname = $1::BYTEA
AND pd.bucket = b.bucketname
AND pd.fullpath >= $2::BYTEA
AND ($2::BYTEA = ''::BYTEA OR pd.fullpath < bytea_increment($2::BYTEA))
AND ($3::BYTEA = ''::BYTEA OR pd.fullpath <= $3::BYTEA)
) x
ORDER BY p DESC
LIMIT $4
` `
) )
@ -120,13 +97,7 @@ func (opi *alternateOrderedPostgresIterator) doNextQuery(ctx context.Context) (_
if start == nil { if start == nil {
start = opi.opts.First start = opi.opts.First
} }
var query string return opi.client.pgConn.Query(alternateForwardQuery, []byte(opi.bucket), []byte(opi.opts.Prefix), []byte(start), opi.batchSize+1)
if opi.opts.Reverse {
query = alternateReverseQuery
} else {
query = alternateForwardQuery
}
return opi.client.pgConn.Query(query, []byte(opi.bucket), []byte(opi.opts.Prefix), []byte(start), opi.batchSize+1)
} }
func newAlternateOrderedPostgresIterator(ctx context.Context, altClient *AlternateClient, opts storage.IterateOptions, batchSize int) (_ *alternateOrderedPostgresIterator, err error) { func newAlternateOrderedPostgresIterator(ctx context.Context, altClient *AlternateClient, opts storage.IterateOptions, batchSize int) (_ *alternateOrderedPostgresIterator, err error) {

View File

@ -6,7 +6,6 @@ package postgreskv
import ( import (
"context" "context"
"database/sql" "database/sql"
"fmt"
"github.com/lib/pq" "github.com/lib/pq"
"github.com/zeebo/errs" "github.com/zeebo/errs"
@ -254,28 +253,18 @@ func (opi *orderedPostgresIterator) doNextQuery(ctx context.Context) (_ *sql.Row
} }
var query string var query string
if !opi.opts.Recurse { if !opi.opts.Recurse {
if opi.opts.Reverse {
query = "SELECT p, m FROM list_directory_reverse($1::BYTEA, $2::BYTEA, $3::BYTEA, $4) ld(p, m)"
} else {
query = "SELECT p, m FROM list_directory($1::BYTEA, $2::BYTEA, $3::BYTEA, $4) ld(p, m)" query = "SELECT p, m FROM list_directory($1::BYTEA, $2::BYTEA, $3::BYTEA, $4) ld(p, m)"
}
} else { } else {
startCmp := ">=" query = `
orderDir := ""
if opi.opts.Reverse {
startCmp = "<="
orderDir = " DESC"
}
query = fmt.Sprintf(`
SELECT fullpath, metadata SELECT fullpath, metadata
FROM pathdata FROM pathdata
WHERE bucket = $1::BYTEA WHERE bucket = $1::BYTEA
AND ($2::BYTEA = ''::BYTEA OR fullpath >= $2::BYTEA) AND ($2::BYTEA = ''::BYTEA OR fullpath >= $2::BYTEA)
AND ($2::BYTEA = ''::BYTEA OR fullpath < bytea_increment($2::BYTEA)) AND ($2::BYTEA = ''::BYTEA OR fullpath < bytea_increment($2::BYTEA))
AND ($3::BYTEA = ''::BYTEA OR fullpath %s $3::BYTEA) AND ($3::BYTEA = ''::BYTEA OR fullpath >= $3::BYTEA)
ORDER BY fullpath%s ORDER BY fullpath
LIMIT $4 LIMIT $4
`, startCmp, orderDir) `
} }
return opi.client.pgConn.Query(query, []byte(opi.bucket), []byte(opi.opts.Prefix), []byte(start), opi.batchSize+1) return opi.client.pgConn.Query(query, []byte(opi.bucket), []byte(opi.opts.Prefix), []byte(start), opi.batchSize+1)
} }

View File

@ -164,21 +164,13 @@ func (client *Client) GetAll(ctx context.Context, keys storage.Keys) (_ storage.
// Iterate iterates over items based on opts // Iterate iterates over items based on opts
func (client *Client) Iterate(ctx context.Context, opts storage.IterateOptions, fn func(context.Context, storage.Iterator) error) (err error) { func (client *Client) Iterate(ctx context.Context, opts storage.IterateOptions, fn func(context.Context, storage.Iterator) error) (err error) {
defer mon.Task()(&ctx)(&err) defer mon.Task()(&ctx)(&err)
var all storage.Items all, err := client.allPrefixedItems(opts.Prefix, opts.First, nil)
if !opts.Reverse {
all, err = client.allPrefixedItems(opts.Prefix, opts.First, nil)
} else {
all, err = client.allPrefixedItems(opts.Prefix, nil, opts.First)
}
if err != nil { if err != nil {
return err return err
} }
if !opts.Recurse { if !opts.Recurse {
all = storage.SortAndCollapse(all, opts.Prefix) all = storage.SortAndCollapse(all, opts.Prefix)
} }
if opts.Reverse {
all = storage.ReverseItems(all)
}
return fn(ctx, &storage.StaticIterator{ return fn(ctx, &storage.StaticIterator{
Items: all, Items: all,
}) })

View File

@ -74,7 +74,6 @@ func (store *Logger) Iterate(ctx context.Context, opts storage.IterateOptions, f
zap.ByteString("prefix", opts.Prefix), zap.ByteString("prefix", opts.Prefix),
zap.ByteString("first", opts.First), zap.ByteString("first", opts.First),
zap.Bool("recurse", opts.Recurse), zap.Bool("recurse", opts.Recurse),
zap.Bool("reverse", opts.Reverse),
) )
return store.store.Iterate(ctx, opts, func(ctx context.Context, it storage.Iterator) error { return store.store.Iterate(ctx, opts, func(ctx context.Context, it storage.Iterator) error {
return fn(ctx, storage.IteratorFunc(func(ctx context.Context, item *storage.ListItem) bool { return fn(ctx, storage.IteratorFunc(func(ctx context.Context, item *storage.ListItem) bool {

View File

@ -30,7 +30,6 @@ type Client struct {
Put int Put int
List int List int
GetAll int GetAll int
ReverseList int
Delete int Delete int
Close int Close int
Iterate int Iterate int
@ -202,12 +201,7 @@ func (store *Client) Iterate(ctx context.Context, opts storage.IterateOptions, f
return errInternal return errInternal
} }
var cursor advancer var cursor advancer = &forward{newCursor(store)}
if !opts.Reverse {
cursor = &forward{newCursor(store)}
} else {
cursor = &backward{newCursor(store)}
}
cursor.PositionToFirst(opts.Prefix, opts.First) cursor.PositionToFirst(opts.Prefix, opts.First)
var lastPrefix storage.Key var lastPrefix storage.Key

View File

@ -194,19 +194,12 @@ func BenchmarkPathOperationsInLargeDb(b *testing.B, store storage.KeyValueStore)
} }
doTest("DeepRecursive", deepRecursive) doTest("DeepRecursive", deepRecursive)
doTest("DeepRecursiveReverse", deepRecursiveReverse)
doTest("DeepNonRecursive", deepNonRecursive) doTest("DeepNonRecursive", deepNonRecursive)
doTest("DeepNonRecursiveReverse", deepNonRecursiveReverse)
doTest("ShallowRecursive", shallowRecursive) doTest("ShallowRecursive", shallowRecursive)
doTest("ShallowRecursiveReverse", shallowRecursiveReverse)
doTest("ShallowNonRecursive", shallowNonRecursive) doTest("ShallowNonRecursive", shallowNonRecursive)
doTest("ShallowNonRecursiveReverse", shallowNonRecursiveReverse)
doTest("TopRecursiveLimit", topRecursiveLimit) doTest("TopRecursiveLimit", topRecursiveLimit)
doTest("TopRecursiveLimitReverse", topRecursiveLimitReverse)
doTest("TopRecursiveStartAt", topRecursiveStartAt) doTest("TopRecursiveStartAt", topRecursiveStartAt)
doTest("TopRecursiveStartAtReverse", topRecursiveStartAtReverse)
doTest("TopNonRecursive", topNonRecursive) doTest("TopNonRecursive", topNonRecursive)
doTest("TopNonRecursiveReverse", topNonRecursiveReverse)
cleanupStore(b, store) cleanupStore(b, store)
} }
@ -347,10 +340,7 @@ func benchAndVerifyIteration(b *testing.B, store storage.KeyValueStore, opts *ve
if result.Key.Equal(lastKey) { if result.Key.Equal(lastKey) {
errorf("got the same key (%q) twice in a row, not on a lookup boundary!", lastKey) errorf("got the same key (%q) twice in a row, not on a lookup boundary!", lastKey)
} }
if opts.iterateOpts.Reverse && !lastKey.IsZero() && lastKey.Less(result.Key) { if result.Key.Less(lastKey) {
errorf("KeyValueStore returned items out of order! %q > %q", result.Key, lastKey)
}
if !opts.iterateOpts.Reverse && result.Key.Less(lastKey) {
errorf("KeyValueStore returned items out of order! %q < %q", result.Key, lastKey) errorf("KeyValueStore returned items out of order! %q < %q", result.Key, lastKey)
} }
if result.IsPrefix { if result.IsPrefix {
@ -410,31 +400,6 @@ func deepRecursive(b *testing.B, store storage.KeyValueStore) {
benchAndVerifyIteration(b, store, opts) benchAndVerifyIteration(b, store, opts)
} }
func deepRecursiveReverse(b *testing.B, store storage.KeyValueStore) {
opts := &verifyOpts{
iterateOpts: storage.IterateOptions{
Prefix: storage.Key(largestLevel2Directory),
Recurse: true,
Reverse: true,
},
}
// these are not expected to exhaust all available items
opts.doIterations = 500
opts.batchSize = storage.LookupLimit
opts.expectCount = opts.doIterations * opts.batchSize
// verify with:
// select encode(fullpath, 'escape') from (
// select rank() over (order by fullpath desc), fullpath from pathdata
// where fullpath < bytea_increment($1::bytea)
// ) x where rank = ($2 * $3);
// where $1 = largestLevel2Directory, $2 = doIterations, and $3 = batchSize
opts.expectLastKey = storage.Key("Peronosporales/hateless/apetaly/poikilocythemia/capped/abrash/dugout/notodontid/jasponyx/cassican/brunelliaceous")
benchAndVerifyIteration(b, store, opts)
}
func deepNonRecursive(b *testing.B, store storage.KeyValueStore) { func deepNonRecursive(b *testing.B, store storage.KeyValueStore) {
opts := &verifyOpts{ opts := &verifyOpts{
iterateOpts: storage.IterateOptions{ iterateOpts: storage.IterateOptions{
@ -460,32 +425,6 @@ func deepNonRecursive(b *testing.B, store storage.KeyValueStore) {
benchAndVerifyIteration(b, store, opts) benchAndVerifyIteration(b, store, opts)
} }
func deepNonRecursiveReverse(b *testing.B, store storage.KeyValueStore) {
opts := &verifyOpts{
iterateOpts: storage.IterateOptions{
Prefix: storage.Key(largestLevel2Directory),
Recurse: false,
Reverse: true,
},
doIterations: 1,
batchSize: 10000,
}
// verify with:
// select count(*) from list_directory(''::bytea, $1::bytea) ld(fp, md);
// where $1 is largestLevel2Directory
opts.expectCount = 119
// verify with:
// select encode(fp, 'escape') from (
// select * from list_directory(''::bytea, $1::bytea) ld(fp, md)
// ) x order by fp limit 1;
// where $1 is largestLevel2Directory
opts.expectLastKey = storage.Key("Peronosporales/hateless/Absyrtus")
benchAndVerifyIteration(b, store, opts)
}
func shallowRecursive(b *testing.B, store storage.KeyValueStore) { func shallowRecursive(b *testing.B, store storage.KeyValueStore) {
opts := &verifyOpts{ opts := &verifyOpts{
iterateOpts: storage.IterateOptions{ iterateOpts: storage.IterateOptions{
@ -515,36 +454,6 @@ func shallowRecursive(b *testing.B, store storage.KeyValueStore) {
benchAndVerifyIteration(b, store, opts) benchAndVerifyIteration(b, store, opts)
} }
func shallowRecursiveReverse(b *testing.B, store storage.KeyValueStore) {
opts := &verifyOpts{
iterateOpts: storage.IterateOptions{
Prefix: storage.Key(largestSingleDirectory),
Recurse: true,
Reverse: true,
},
}
// verify with:
// select count(*) from pathdata
// where fullpath > $1::bytea and fullpath < bytea_increment($1::bytea);
// where $1 = largestSingleDirectory
opts.expectCount = 18574
// verify with:
// select convert_from(fullpath, 'UTF8') from pathdata
// where fullpath > $1::bytea and fullpath < bytea_increment($1::bytea)
// order by fullpath limit 1;
// where $1 = largestSingleDirectory
opts.expectLastKey = storage.Key("Peronosporales/hateless/tod/unricht/sniveling/Puyallup/Aaronite")
// i didn't plan it this way, but expectedCount happens to have some nicely-sized factors for
// our purposes with no messy remainder. 74 * 251 = 18574
opts.doIterations = 74
opts.batchSize = 251
benchAndVerifyIteration(b, store, opts)
}
func shallowNonRecursive(b *testing.B, store storage.KeyValueStore) { func shallowNonRecursive(b *testing.B, store storage.KeyValueStore) {
opts := &verifyOpts{ opts := &verifyOpts{
iterateOpts: storage.IterateOptions{ iterateOpts: storage.IterateOptions{
@ -570,32 +479,6 @@ func shallowNonRecursive(b *testing.B, store storage.KeyValueStore) {
benchAndVerifyIteration(b, store, opts) benchAndVerifyIteration(b, store, opts)
} }
func shallowNonRecursiveReverse(b *testing.B, store storage.KeyValueStore) {
opts := &verifyOpts{
iterateOpts: storage.IterateOptions{
Prefix: storage.Key(largestSingleDirectory),
Recurse: false,
Reverse: true,
},
doIterations: 2,
batchSize: 10000,
}
// verify with:
// select count(*) from list_directory(''::bytea, $1::bytea) ld(fp, md);
// where $1 is largestSingleDirectory
opts.expectCount = 18574
// verify with:
// select encode(fp, 'escape') from (
// select * from list_directory(''::bytea, $1::bytea) ld(fp, md)
// ) x order by fp limit 1;
// where $1 = largestSingleDirectory
opts.expectLastKey = storage.Key("Peronosporales/hateless/tod/unricht/sniveling/Puyallup/Aaronite")
benchAndVerifyIteration(b, store, opts)
}
func topRecursiveLimit(b *testing.B, store storage.KeyValueStore) { func topRecursiveLimit(b *testing.B, store storage.KeyValueStore) {
opts := &verifyOpts{ opts := &verifyOpts{
iterateOpts: storage.IterateOptions{ iterateOpts: storage.IterateOptions{
@ -618,29 +501,6 @@ func topRecursiveLimit(b *testing.B, store storage.KeyValueStore) {
benchAndVerifyIteration(b, store, opts) benchAndVerifyIteration(b, store, opts)
} }
func topRecursiveLimitReverse(b *testing.B, store storage.KeyValueStore) {
opts := &verifyOpts{
iterateOpts: storage.IterateOptions{
Recurse: true,
Reverse: true,
},
doIterations: 100,
batchSize: 10000,
}
// not expected to exhaust items
opts.expectCount = opts.doIterations * opts.batchSize
// verify with:
// select encode(fullpath, 'escape') from (
// select rank() over (order by fullpath desc), fullpath from pathdata
// ) x where rank = $1;
// where $1 = expectCount
opts.expectLastKey = storage.Key("nonresuscitation/synchronically/cabook/homeozoic/inclinatorium/iguanodont/thiophenol/congeliturbation/Alaric")
benchAndVerifyIteration(b, store, opts)
}
func topRecursiveStartAt(b *testing.B, store storage.KeyValueStore) { func topRecursiveStartAt(b *testing.B, store storage.KeyValueStore) {
opts := &verifyOpts{ opts := &verifyOpts{
iterateOpts: storage.IterateOptions{ iterateOpts: storage.IterateOptions{
@ -666,36 +526,6 @@ func topRecursiveStartAt(b *testing.B, store storage.KeyValueStore) {
benchAndVerifyIteration(b, store, opts) benchAndVerifyIteration(b, store, opts)
} }
func topRecursiveStartAtReverse(b *testing.B, store storage.KeyValueStore) {
opts := &verifyOpts{
iterateOpts: storage.IterateOptions{
Recurse: true,
Reverse: true,
},
doIterations: 61,
batchSize: 10000,
}
// this is pretty arbitrary. just the key 100 positions before the end of the Peronosporales/hateless/ dir.
opts.iterateOpts.First = storage.Key("Peronosporales/hateless/warrener/anthropomancy/geisotherm/wickerwork")
// we *do* expect to exhaust the available items this time.
// verify with:
// select count(*) from (
// select fullpath from pathdata where fullpath <= $1::bytea order by fullpath desc limit $2
// ) x;
// where $1 = iterateOpts.First and $2 = (doIterations * batchSize)
opts.expectCount = 608405
// since expectCount < (doIterations * batchSize), and we're going in reverse, the last key read
// should be the first one lexicographically.
// verify with:
// select encode(fullpath, 'escape') from pathdata order by fullpath limit 1;
opts.expectLastKey = storage.Key("Lissamphibia")
benchAndVerifyIteration(b, store, opts)
}
func topNonRecursive(b *testing.B, store storage.KeyValueStore) { func topNonRecursive(b *testing.B, store storage.KeyValueStore) {
opts := &verifyOpts{ opts := &verifyOpts{
iterateOpts: storage.IterateOptions{ iterateOpts: storage.IterateOptions{
@ -718,27 +548,6 @@ func topNonRecursive(b *testing.B, store storage.KeyValueStore) {
benchAndVerifyIteration(b, store, opts) benchAndVerifyIteration(b, store, opts)
} }
func topNonRecursiveReverse(b *testing.B, store storage.KeyValueStore) {
opts := &verifyOpts{
iterateOpts: storage.IterateOptions{
Recurse: false,
Reverse: true,
},
doIterations: 1,
batchSize: 10000,
}
// verify with:
// select count(*) from list_directory(''::bytea, ''::bytea);
opts.expectCount = 21
// verify with:
// select encode(fullpath, 'escape') from pathdata order by fullpath limit 1;
opts.expectLastKey = storage.Key("Lissamphibia")
benchAndVerifyIteration(b, store, opts)
}
func cleanupBigPathset(tb testing.TB, store storage.KeyValueStore) { func cleanupBigPathset(tb testing.TB, store storage.KeyValueStore) {
if *noCleanDb { if *noCleanDb {
tb.Skip("Instructed not to clean up this KeyValueStore after long benchmarks are complete.") tb.Skip("Instructed not to clean up this KeyValueStore after long benchmarks are complete.")

View File

@ -42,17 +42,6 @@ func testIterate(t *testing.T, store storage.KeyValueStore) {
newItem("g", "g", false), newItem("g", "g", false),
newItem("h", "h", false), newItem("h", "h", false),
}}, }},
{"no limits reverse",
storage.IterateOptions{
Reverse: true,
}, storage.Items{
newItem("h", "h", false),
newItem("g", "g", false),
newItem("c/", "", true),
newItem("c", "c", false),
newItem("b/", "", true),
newItem("a", "a", false),
}},
{"at a", {"at a",
storage.IterateOptions{ storage.IterateOptions{
@ -66,14 +55,6 @@ func testIterate(t *testing.T, store storage.KeyValueStore) {
newItem("h", "h", false), newItem("h", "h", false),
}}, }},
{"reverse at a",
storage.IterateOptions{
First: storage.Key("a"),
Reverse: true,
}, storage.Items{
newItem("a", "a", false),
}},
{"after a", {"after a",
storage.IterateOptions{ storage.IterateOptions{
First: storage.NextKey(storage.Key("a")), First: storage.NextKey(storage.Key("a")),
@ -126,16 +107,6 @@ func testIterate(t *testing.T, store storage.KeyValueStore) {
newItem("g", "g", false), newItem("g", "g", false),
newItem("h", "h", false), newItem("h", "h", false),
}}, }},
{"reverse after e",
storage.IterateOptions{
First: storage.NextKey(storage.Key("e")),
Reverse: true,
}, storage.Items{
newItem("c/", "", true),
newItem("c", "c", false),
newItem("b/", "", true),
newItem("a", "a", false),
}},
{"prefix b slash", {"prefix b slash",
storage.IterateOptions{ storage.IterateOptions{
Prefix: storage.Key("b/"), Prefix: storage.Key("b/"),

View File

@ -61,21 +61,6 @@ func testIterateAll(t *testing.T, store storage.KeyValueStore) {
newItem("g", "g", false), newItem("g", "g", false),
newItem("h", "h", false), newItem("h", "h", false),
}}, }},
{"no limits reverse",
storage.IterateOptions{
Recurse: true, Reverse: true,
}, storage.Items{
newItem("h", "h", false),
newItem("g", "g", false),
newItem("c/1", "c/1", false),
newItem("c//", "c//", false),
newItem("c/", "c/", false),
newItem("c", "c", false),
newItem("b/3", "b/3", false),
newItem("b/2", "b/2", false),
newItem("b/1", "b/1", false),
newItem("a", "a", false),
}},
{"at a", {"at a",
storage.IterateOptions{ storage.IterateOptions{
@ -93,13 +78,6 @@ func testIterateAll(t *testing.T, store storage.KeyValueStore) {
newItem("g", "g", false), newItem("g", "g", false),
newItem("h", "h", false), newItem("h", "h", false),
}}, }},
{"at a reverse",
storage.IterateOptions{
First: storage.Key("a"),
Recurse: true, Reverse: true,
}, storage.Items{
newItem("a", "a", false),
}},
{"after a", {"after a",
storage.IterateOptions{ storage.IterateOptions{
@ -180,20 +158,6 @@ func testIterateAll(t *testing.T, store storage.KeyValueStore) {
newItem("g", "g", false), newItem("g", "g", false),
newItem("h", "h", false), newItem("h", "h", false),
}}, }},
{"at e reverse",
storage.IterateOptions{
First: storage.Key("e"),
Recurse: true, Reverse: true,
}, storage.Items{
newItem("c/1", "c/1", false),
newItem("c//", "c//", false),
newItem("c/", "c/", false),
newItem("c", "c", false),
newItem("b/3", "b/3", false),
newItem("b/2", "b/2", false),
newItem("b/1", "b/1", false),
newItem("a", "a", false),
}},
{"prefix b slash", {"prefix b slash",
storage.IterateOptions{ storage.IterateOptions{
@ -221,23 +185,6 @@ func testIterateAll(t *testing.T, store storage.KeyValueStore) {
newItem("b/2", "b/2", false), newItem("b/2", "b/2", false),
newItem("b/3", "b/3", false), newItem("b/3", "b/3", false),
}}, }},
{"reverse prefix b slash",
storage.IterateOptions{
Prefix: storage.Key("b/"),
Recurse: true, Reverse: true,
}, storage.Items{
newItem("b/3", "b/3", false),
newItem("b/2", "b/2", false),
newItem("b/1", "b/1", false),
}},
{"reverse prefix b slash at b slash 2",
storage.IterateOptions{
Prefix: storage.Key("b/"), First: storage.Key("b/2"),
Recurse: true, Reverse: true,
}, storage.Items{
newItem("b/2", "b/2", false),
newItem("b/1", "b/1", false),
}},
{"prefix c slash", {"prefix c slash",
storage.IterateOptions{ storage.IterateOptions{
@ -248,15 +195,6 @@ func testIterateAll(t *testing.T, store storage.KeyValueStore) {
newItem("c//", "c//", false), newItem("c//", "c//", false),
newItem("c/1", "c/1", false), newItem("c/1", "c/1", false),
}}, }},
{"reverse prefix c slash",
storage.IterateOptions{
Prefix: storage.Key("c/"),
Recurse: true, Reverse: true,
}, storage.Items{
newItem("c/1", "c/1", false),
newItem("c//", "c//", false),
newItem("c/", "c/", false),
}},
{"prefix c slash slash", {"prefix c slash slash",
storage.IterateOptions{ storage.IterateOptions{
@ -265,12 +203,5 @@ func testIterateAll(t *testing.T, store storage.KeyValueStore) {
}, storage.Items{ }, storage.Items{
newItem("c//", "c//", false), newItem("c//", "c//", false),
}}, }},
{"reverse prefix c slash slash",
storage.IterateOptions{
Prefix: storage.Key("c//"),
Recurse: true, Reverse: true,
}, storage.Items{
newItem("c//", "c//", false),
}},
}) })
} }

View File

@ -122,49 +122,6 @@ func testListV2(t *testing.T, store storage.KeyValueStore) {
newItem("my-album/", "", true), newItem("my-album/", "", true),
}, },
}, },
{"end before 2 recursive",
storage.ListOptions{
Recursive: true,
EndBefore: storage.Key("music/z-song5.mp3"),
Limit: 2,
},
true, storage.Items{
newItem("music/my-album/song3.mp3", "", false),
newItem("music/my-album/song4.mp3", "", false),
},
},
{"end before non-existing 2 recursive",
storage.ListOptions{
Recursive: true,
EndBefore: storage.Key("music/my-album/song5.mp3"),
Limit: 2,
},
true, storage.Items{
newItem("music/my-album/song3.mp3", "", false),
newItem("music/my-album/song4.mp3", "", false),
},
},
{"end before 2",
storage.ListOptions{
Prefix: storage.Key("music/"),
EndBefore: storage.Key("z-song5.mp3"),
Limit: 2,
},
true, storage.Items{
newItem("a-song2.mp3", "", false),
newItem("my-album/", "", true),
},
},
{"end before 2 prefixed",
storage.ListOptions{
Prefix: storage.Key("music/my-album/"),
EndBefore: storage.Key("song4.mp3"),
Limit: 2,
},
false, storage.Items{
newItem("song3.mp3", "", false),
},
},
} }
for _, test := range tests { for _, test := range tests {

View File

@ -39,30 +39,12 @@ func testPrefix(t *testing.T, store storage.KeyValueStore) {
newItem("x-b/2", "b/2", false), newItem("x-b/2", "b/2", false),
newItem("x-b/3", "b/3", false), newItem("x-b/3", "b/3", false),
}}, }},
{"reverse prefix x dash b slash",
storage.IterateOptions{
Prefix: storage.Key("x-"), First: storage.Key("x-b/3"),
Recurse: true, Reverse: true,
}, storage.Items{
newItem("x-b/3", "b/3", false),
newItem("x-b/2", "b/2", false),
newItem("x-b/1", "b/1", false),
newItem("x-a", "a", false),
}},
{"prefix x dash b slash", {"prefix x dash b slash",
storage.IterateOptions{ storage.IterateOptions{
Prefix: storage.Key("x-"), First: storage.Key("x-b"), Prefix: storage.Key("x-"), First: storage.Key("x-b"),
}, storage.Items{ }, storage.Items{
newItem("x-b/", "", true), newItem("x-b/", "", true),
}}, }},
{"reverse x dash b slash",
storage.IterateOptions{
Prefix: storage.Key("x-"), First: storage.Key("x-b/2"),
Reverse: true,
}, storage.Items{
newItem("x-b/", "", true),
newItem("x-a", "a", false),
}},
{"prefix y- slash", {"prefix y- slash",
storage.IterateOptions{ storage.IterateOptions{
Prefix: storage.Key("y-"), Prefix: storage.Key("y-"),

View File

@ -134,7 +134,7 @@ func (client *Client) DeleteSegmentOld(ctx context.Context, bucket string, path
} }
// ListSegmentsOld lists the available segments // ListSegmentsOld lists the available segments
func (client *Client) ListSegmentsOld(ctx context.Context, bucket string, prefix, startAfter, endBefore storj.Path, recursive bool, limit int32, metaFlags uint32) (items []ListItem, more bool, err error) { func (client *Client) ListSegmentsOld(ctx context.Context, bucket string, prefix, startAfter, ignoredEndBefore storj.Path, recursive bool, limit int32, metaFlags uint32) (items []ListItem, more bool, err error) {
defer mon.Task()(&ctx)(&err) defer mon.Task()(&ctx)(&err)
response, err := client.client.ListSegmentsOld(ctx, &pb.ListSegmentsRequestOld{ response, err := client.client.ListSegmentsOld(ctx, &pb.ListSegmentsRequestOld{
@ -142,7 +142,6 @@ func (client *Client) ListSegmentsOld(ctx context.Context, bucket string, prefix
Bucket: []byte(bucket), Bucket: []byte(bucket),
Prefix: []byte(prefix), Prefix: []byte(prefix),
StartAfter: []byte(startAfter), StartAfter: []byte(startAfter),
EndBefore: []byte(endBefore),
Recursive: recursive, Recursive: recursive,
Limit: limit, Limit: limit,
MetaFlags: metaFlags, MetaFlags: metaFlags,

View File

@ -169,15 +169,9 @@ func (db *DB) ListObjects(ctx context.Context, bucket string, options storj.List
prefix: bucket, prefix: bucket,
} }
var startAfter, endBefore string var startAfter string
switch options.Direction { switch options.Direction {
// TODO for now we are supporting only storj.After // TODO for now we are supporting only storj.After
// case storj.Before:
// // before lists backwards from cursor, without cursor
// endBefore = options.Cursor
// case storj.Backward:
// // backward lists backwards from cursor, including cursor
// endBefore = keyAfter(options.Cursor)
// case storj.Forward: // case storj.Forward:
// // forward lists forwards from cursor, including cursor // // forward lists forwards from cursor, including cursor
// startAfter = keyBefore(options.Cursor) // startAfter = keyBefore(options.Cursor)
@ -188,12 +182,7 @@ func (db *DB) ListObjects(ctx context.Context, bucket string, options storj.List
return storj.ObjectList{}, errClass.New("invalid direction %d", options.Direction) return storj.ObjectList{}, errClass.New("invalid direction %d", options.Direction)
} }
// TODO: remove this hack-fix of specifying the last key items, more, err := objects.List(ctx, options.Prefix, startAfter, options.Recursive, options.Limit, meta.All)
if options.Cursor == "" && (options.Direction == storj.Before || options.Direction == storj.Backward) {
endBefore = "\x7f\x7f\x7f\x7f\x7f\x7f\x7f"
}
items, more, err := objects.List(ctx, options.Prefix, startAfter, endBefore, options.Recursive, options.Limit, meta.All)
if err != nil { if err != nil {
return storj.ObjectList{}, err return storj.ObjectList{}, err
} }

View File

@ -294,8 +294,6 @@ func TestListObjectsEmpty(t *testing.T) {
// TODO for now we are supporting only storj.After // TODO for now we are supporting only storj.After
for _, direction := range []storj.ListDirection{ for _, direction := range []storj.ListDirection{
// storj.Before,
// storj.Backward,
// storj.Forward, // storj.Forward,
storj.After, storj.After,
} { } {
@ -334,311 +332,80 @@ func TestListObjects(t *testing.T) {
result []string result []string
}{ }{
{ {
options: options("", "", storj.After, 0), options: options("", "", 0),
result: []string{"a", "a/", "aa", "b", "b/", "bb", "c"}, result: []string{"a", "a/", "aa", "b", "b/", "bb", "c"},
}, { }, {
options: options("", "`", storj.After, 0), options: options("", "`", 0),
result: []string{"a", "a/", "aa", "b", "b/", "bb", "c"}, result: []string{"a", "a/", "aa", "b", "b/", "bb", "c"},
}, { }, {
options: options("", "b", storj.After, 0), options: options("", "b", 0),
result: []string{"b/", "bb", "c"}, result: []string{"b/", "bb", "c"},
}, { }, {
options: options("", "c", storj.After, 0), options: options("", "c", 0),
result: []string{}, result: []string{},
}, { }, {
options: options("", "ca", storj.After, 0), options: options("", "ca", 0),
result: []string{}, result: []string{},
}, { }, {
options: options("", "", storj.After, 1), options: options("", "", 1),
more: true, more: true,
result: []string{"a"}, result: []string{"a"},
}, { }, {
options: options("", "`", storj.After, 1), options: options("", "`", 1),
more: true, more: true,
result: []string{"a"}, result: []string{"a"},
}, { }, {
options: options("", "aa", storj.After, 1), options: options("", "aa", 1),
more: true, more: true,
result: []string{"b"}, result: []string{"b"},
}, { }, {
options: options("", "c", storj.After, 1), options: options("", "c", 1),
result: []string{}, result: []string{},
}, { }, {
options: options("", "ca", storj.After, 1), options: options("", "ca", 1),
result: []string{}, result: []string{},
}, { }, {
options: options("", "", storj.After, 2), options: options("", "", 2),
more: true, more: true,
result: []string{"a", "a/"}, result: []string{"a", "a/"},
}, { }, {
options: options("", "`", storj.After, 2), options: options("", "`", 2),
more: true, more: true,
result: []string{"a", "a/"}, result: []string{"a", "a/"},
}, { }, {
options: options("", "aa", storj.After, 2), options: options("", "aa", 2),
more: true, more: true,
result: []string{"b", "b/"}, result: []string{"b", "b/"},
}, { }, {
options: options("", "bb", storj.After, 2), options: options("", "bb", 2),
result: []string{"c"}, result: []string{"c"},
}, { }, {
options: options("", "c", storj.After, 2), options: options("", "c", 2),
result: []string{}, result: []string{},
}, { }, {
options: options("", "ca", storj.After, 2), options: options("", "ca", 2),
result: []string{}, result: []string{},
}, { }, {
options: optionsRecursive("", "", storj.After, 0), options: optionsRecursive("", "", 0),
result: []string{"a", "a/xa", "a/xaa", "a/xb", "a/xbb", "a/xc", "aa", "b", "b/ya", "b/yaa", "b/yb", "b/ybb", "b/yc", "bb", "c"}, result: []string{"a", "a/xa", "a/xaa", "a/xb", "a/xbb", "a/xc", "aa", "b", "b/ya", "b/yaa", "b/yb", "b/ybb", "b/yc", "bb", "c"},
}, { }, {
options: options("a", "", storj.After, 0), options: options("a", "", 0),
result: []string{"xa", "xaa", "xb", "xbb", "xc"}, result: []string{"xa", "xaa", "xb", "xbb", "xc"},
}, { }, {
options: options("a/", "", storj.After, 0), options: options("a/", "", 0),
result: []string{"xa", "xaa", "xb", "xbb", "xc"}, result: []string{"xa", "xaa", "xb", "xbb", "xc"},
}, { }, {
options: options("a/", "xb", storj.After, 0), options: options("a/", "xb", 0),
result: []string{"xbb", "xc"}, result: []string{"xbb", "xc"},
}, { }, {
options: optionsRecursive("", "a/xbb", storj.After, 5), options: optionsRecursive("", "a/xbb", 5),
more: true, more: true,
result: []string{"a/xc", "aa", "b", "b/ya", "b/yaa"}, result: []string{"a/xc", "aa", "b", "b/ya", "b/yaa"},
}, { }, {
options: options("a/", "xaa", storj.After, 2), options: options("a/", "xaa", 2),
more: true, more: true,
result: []string{"xb", "xbb"}, result: []string{"xb", "xbb"},
}, },
// TODO commented until we will decide if we will support direction for object listing
//
// {
// options: options("", "", storj.Forward, 0),
// result: []string{"a", "a/", "aa", "b", "b/", "bb", "c"},
// }, {
// options: options("", "`", storj.Forward, 0),
// result: []string{"a", "a/", "aa", "b", "b/", "bb", "c"},
// }, {
// options: options("", "b", storj.Forward, 0),
// result: []string{"b", "b/", "bb", "c"},
// }, {
// options: options("", "c", storj.Forward, 0),
// result: []string{"c"},
// }, {
// options: options("", "ca", storj.Forward, 0),
// result: []string{},
// }, {
// options: options("", "", storj.Forward, 1),
// more: true,
// result: []string{"a"},
// }, {
// options: options("", "`", storj.Forward, 1),
// more: true,
// result: []string{"a"},
// }, {
// options: options("", "aa", storj.Forward, 1),
// more: true,
// result: []string{"aa"},
// }, {
// options: options("", "c", storj.Forward, 1),
// result: []string{"c"},
// }, {
// options: options("", "ca", storj.Forward, 1),
// result: []string{},
// }, {
// options: options("", "", storj.Forward, 2),
// more: true,
// result: []string{"a", "a/"},
// }, {
// options: options("", "`", storj.Forward, 2),
// more: true,
// result: []string{"a", "a/"},
// }, {
// options: options("", "aa", storj.Forward, 2),
// more: true,
// result: []string{"aa", "b"},
// }, {
// options: options("", "bb", storj.Forward, 2),
// result: []string{"bb", "c"},
// }, {
// options: options("", "c", storj.Forward, 2),
// result: []string{"c"},
// }, {
// options: options("", "ca", storj.Forward, 2),
// result: []string{},
// }, {
// options: optionsRecursive("", "", storj.Forward, 0),
// result: []string{"a", "a/xa", "a/xaa", "a/xb", "a/xbb", "a/xc", "aa", "b", "b/ya", "b/yaa", "b/yb", "b/ybb", "b/yc", "bb", "c"},
// }, {
// options: options("a", "", storj.Forward, 0),
// result: []string{"xa", "xaa", "xb", "xbb", "xc"},
// }, {
// options: options("a/", "", storj.Forward, 0),
// result: []string{"xa", "xaa", "xb", "xbb", "xc"},
// }, {
// options: options("a/", "xb", storj.Forward, 0),
// result: []string{"xb", "xbb", "xc"},
// }, {
// options: optionsRecursive("", "a/xbb", storj.Forward, 5),
// more: true,
// result: []string{"a/xbb", "a/xc", "aa", "b", "b/ya"},
// }, {
// options: options("a/", "xaa", storj.Forward, 2),
// more: true,
// result: []string{"xaa", "xb"},
// }, {
// options: options("", "", storj.Backward, 0),
// result: []string{"a", "a/", "aa", "b", "b/", "bb", "c"},
// }, {
// options: options("", "`", storj.Backward, 0),
// result: []string{},
// }, {
// options: options("", "b", storj.Backward, 0),
// result: []string{"a", "a/", "aa", "b"},
// }, {
// options: options("", "c", storj.Backward, 0),
// result: []string{"a", "a/", "aa", "b", "b/", "bb", "c"},
// }, {
// options: options("", "ca", storj.Backward, 0),
// result: []string{"a", "a/", "aa", "b", "b/", "bb", "c"},
// }, {
// options: options("", "", storj.Backward, 1),
// more: true,
// result: []string{"c"},
// }, {
// options: options("", "`", storj.Backward, 1),
// result: []string{},
// }, {
// options: options("", "aa", storj.Backward, 1),
// more: true,
// result: []string{"aa"},
// }, {
// options: options("", "c", storj.Backward, 1),
// more: true,
// result: []string{"c"},
// }, {
// options: options("", "ca", storj.Backward, 1),
// more: true,
// result: []string{"c"},
// }, {
// options: options("", "", storj.Backward, 2),
// more: true,
// result: []string{"bb", "c"},
// }, {
// options: options("", "`", storj.Backward, 2),
// result: []string{},
// }, {
// options: options("", "a/", storj.Backward, 2),
// result: []string{"a"},
// }, {
// options: options("", "bb", storj.Backward, 2),
// more: true,
// result: []string{"b/", "bb"},
// }, {
// options: options("", "c", storj.Backward, 2),
// more: true,
// result: []string{"bb", "c"},
// }, {
// options: options("", "ca", storj.Backward, 2),
// more: true,
// result: []string{"bb", "c"},
// }, {
// options: optionsRecursive("", "", storj.Backward, 0),
// result: []string{"a", "a/xa", "a/xaa", "a/xb", "a/xbb", "a/xc", "aa", "b", "b/ya", "b/yaa", "b/yb", "b/ybb", "b/yc", "bb", "c"},
// }, {
// options: options("a", "", storj.Backward, 0),
// result: []string{"xa", "xaa", "xb", "xbb", "xc"},
// }, {
// options: options("a/", "", storj.Backward, 0),
// result: []string{"xa", "xaa", "xb", "xbb", "xc"},
// }, {
// options: options("a/", "xb", storj.Backward, 0),
// result: []string{"xa", "xaa", "xb"},
// }, {
// options: optionsRecursive("", "b/yaa", storj.Backward, 5),
// more: true,
// result: []string{"a/xc", "aa", "b", "b/ya", "b/yaa"},
// }, {
// options: options("a/", "xbb", storj.Backward, 2),
// more: true,
// result: []string{"xb", "xbb"},
// }, {
// options: options("", "", storj.Before, 0),
// result: []string{"a", "a/", "aa", "b", "b/", "bb", "c"},
// }, {
// options: options("", "`", storj.Before, 0),
// result: []string{},
// }, {
// options: options("", "a", storj.Before, 0),
// result: []string{},
// }, {
// options: options("", "b", storj.Before, 0),
// result: []string{"a", "a/", "aa"},
// }, {
// options: options("", "c", storj.Before, 0),
// result: []string{"a", "a/", "aa", "b", "b/", "bb"},
// }, {
// options: options("", "ca", storj.Before, 0),
// result: []string{"a", "a/", "aa", "b", "b/", "bb", "c"},
// }, {
// options: options("", "", storj.Before, 1),
// more: true,
// result: []string{"c"},
// }, {
// options: options("", "`", storj.Before, 1),
// result: []string{},
// }, {
// options: options("", "a/", storj.Before, 1),
// result: []string{"a"},
// }, {
// options: options("", "c", storj.Before, 1),
// more: true,
// result: []string{"bb"},
// }, {
// options: options("", "ca", storj.Before, 1),
// more: true,
// result: []string{"c"},
// }, {
// options: options("", "", storj.Before, 2),
// more: true,
// result: []string{"bb", "c"},
// }, {
// options: options("", "`", storj.Before, 2),
// result: []string{},
// }, {
// options: options("", "a/", storj.Before, 2),
// result: []string{"a"},
// }, {
// options: options("", "bb", storj.Before, 2),
// more: true,
// result: []string{"b", "b/"},
// }, {
// options: options("", "c", storj.Before, 2),
// more: true,
// result: []string{"b/", "bb"},
// }, {
// options: options("", "ca", storj.Before, 2),
// more: true,
// result: []string{"bb", "c"},
// }, {
// options: optionsRecursive("", "", storj.Before, 0),
// result: []string{"a", "a/xa", "a/xaa", "a/xb", "a/xbb", "a/xc", "aa", "b", "b/ya", "b/yaa", "b/yb", "b/ybb", "b/yc", "bb", "c"},
// }, {
// options: options("a", "", storj.Before, 0),
// result: []string{"xa", "xaa", "xb", "xbb", "xc"},
// }, {
// options: options("a/", "", storj.Before, 0),
// result: []string{"xa", "xaa", "xb", "xbb", "xc"},
// }, {
// options: options("a/", "xb", storj.Before, 0),
// result: []string{"xa", "xaa"},
// }, {
// options: optionsRecursive("", "b/yaa", storj.Before, 5),
// more: true,
// result: []string{"a/xbb", "a/xc", "aa", "b", "b/ya"},
// }, {
// options: options("a/", "xbb", storj.Before, 2),
// more: true,
// result: []string{"xaa", "xb"},
// },
} { } {
errTag := fmt.Sprintf("%d. %+v", i, tt) errTag := fmt.Sprintf("%d. %+v", i, tt)
@ -655,20 +422,20 @@ func TestListObjects(t *testing.T) {
} }
}) })
} }
func options(prefix, cursor string, direction storj.ListDirection, limit int) storj.ListOptions { func options(prefix, cursor string, limit int) storj.ListOptions {
return storj.ListOptions{ return storj.ListOptions{
Prefix: prefix, Prefix: prefix,
Cursor: cursor, Cursor: cursor,
Direction: direction, Direction: storj.After,
Limit: limit, Limit: limit,
} }
} }
func optionsRecursive(prefix, cursor string, direction storj.ListDirection, limit int) storj.ListOptions { func optionsRecursive(prefix, cursor string, limit int) storj.ListOptions {
return storj.ListOptions{ return storj.ListOptions{
Prefix: prefix, Prefix: prefix,
Cursor: cursor, Cursor: cursor,
Direction: direction, Direction: storj.After,
Limit: limit, Limit: limit,
Recursive: true, Recursive: true,
} }

View File

@ -59,8 +59,8 @@ func (o *prefixedObjStore) Delete(ctx context.Context, path storj.Path) (err err
return o.store.Delete(ctx, storj.JoinPaths(o.prefix, path)) return o.store.Delete(ctx, storj.JoinPaths(o.prefix, path))
} }
func (o *prefixedObjStore) List(ctx context.Context, prefix, startAfter, endBefore storj.Path, recursive bool, limit int, metaFlags uint32) (items []objects.ListItem, more bool, err error) { func (o *prefixedObjStore) List(ctx context.Context, prefix, startAfter storj.Path, recursive bool, limit int, metaFlags uint32) (items []objects.ListItem, more bool, err error) {
defer mon.Task()(&ctx)(&err) defer mon.Task()(&ctx)(&err)
return o.store.List(ctx, storj.JoinPaths(o.prefix, prefix), startAfter, endBefore, recursive, limit, metaFlags) return o.store.List(ctx, storj.JoinPaths(o.prefix, prefix), startAfter, recursive, limit, metaFlags)
} }

View File

@ -43,7 +43,7 @@ type Store interface {
Get(ctx context.Context, path storj.Path) (rr ranger.Ranger, meta Meta, err error) Get(ctx context.Context, path storj.Path) (rr ranger.Ranger, meta Meta, err error)
Put(ctx context.Context, path storj.Path, data io.Reader, metadata pb.SerializableMeta, expiration time.Time) (meta Meta, err error) Put(ctx context.Context, path storj.Path, data io.Reader, metadata pb.SerializableMeta, expiration time.Time) (meta Meta, err error)
Delete(ctx context.Context, path storj.Path) (err error) Delete(ctx context.Context, path storj.Path) (err error)
List(ctx context.Context, prefix, startAfter, endBefore storj.Path, recursive bool, limit int, metaFlags uint32) (items []ListItem, more bool, err error) List(ctx context.Context, prefix, startAfter storj.Path, recursive bool, limit int, metaFlags uint32) (items []ListItem, more bool, err error)
} }
type objStore struct { type objStore struct {
@ -123,11 +123,11 @@ func (o *objStore) Delete(ctx context.Context, path storj.Path) (err error) {
return err return err
} }
func (o *objStore) List(ctx context.Context, prefix, startAfter, endBefore storj.Path, recursive bool, limit int, metaFlags uint32) ( func (o *objStore) List(ctx context.Context, prefix, startAfter storj.Path, recursive bool, limit int, metaFlags uint32) (
items []ListItem, more bool, err error) { items []ListItem, more bool, err error) {
defer mon.Task()(&ctx)(&err) defer mon.Task()(&ctx)(&err)
strItems, more, err := o.store.List(ctx, prefix, startAfter, endBefore, o.pathCipher, recursive, limit, metaFlags) strItems, more, err := o.store.List(ctx, prefix, startAfter, o.pathCipher, recursive, limit, metaFlags)
if err != nil { if err != nil {
return nil, false, err return nil, false, err
} }

View File

@ -21,7 +21,7 @@ type Store interface {
Get(ctx context.Context, path storj.Path, pathCipher storj.CipherSuite) (ranger.Ranger, Meta, error) Get(ctx context.Context, path storj.Path, pathCipher storj.CipherSuite) (ranger.Ranger, Meta, error)
Put(ctx context.Context, path storj.Path, pathCipher storj.CipherSuite, data io.Reader, metadata []byte, expiration time.Time) (Meta, error) Put(ctx context.Context, path storj.Path, pathCipher storj.CipherSuite, data io.Reader, metadata []byte, expiration time.Time) (Meta, error)
Delete(ctx context.Context, path storj.Path, pathCipher storj.CipherSuite) error Delete(ctx context.Context, path storj.Path, pathCipher storj.CipherSuite) error
List(ctx context.Context, prefix, startAfter, endBefore storj.Path, pathCipher storj.CipherSuite, recursive bool, limit int, metaFlags uint32) (items []ListItem, more bool, err error) List(ctx context.Context, prefix, startAfter storj.Path, pathCipher storj.CipherSuite, recursive bool, limit int, metaFlags uint32) (items []ListItem, more bool, err error)
} }
type shimStore struct { type shimStore struct {
@ -66,8 +66,8 @@ func (s *shimStore) Delete(ctx context.Context, path storj.Path, pathCipher stor
} }
// List parses the passed in path and dispatches to the typed store. // List parses the passed in path and dispatches to the typed store.
func (s *shimStore) List(ctx context.Context, prefix storj.Path, startAfter storj.Path, endBefore storj.Path, pathCipher storj.CipherSuite, recursive bool, limit int, metaFlags uint32) (items []ListItem, more bool, err error) { func (s *shimStore) List(ctx context.Context, prefix storj.Path, startAfter storj.Path, pathCipher storj.CipherSuite, recursive bool, limit int, metaFlags uint32) (items []ListItem, more bool, err error) {
defer mon.Task()(&ctx)(&err) defer mon.Task()(&ctx)(&err)
return s.store.List(ctx, ParsePath(prefix), startAfter, endBefore, pathCipher, recursive, limit, metaFlags) return s.store.List(ctx, ParsePath(prefix), startAfter, pathCipher, recursive, limit, metaFlags)
} }

View File

@ -59,7 +59,7 @@ type typedStore interface {
Get(ctx context.Context, path Path, pathCipher storj.CipherSuite) (ranger.Ranger, Meta, error) Get(ctx context.Context, path Path, pathCipher storj.CipherSuite) (ranger.Ranger, Meta, error)
Put(ctx context.Context, path Path, pathCipher storj.CipherSuite, data io.Reader, metadata []byte, expiration time.Time) (Meta, error) Put(ctx context.Context, path Path, pathCipher storj.CipherSuite, data io.Reader, metadata []byte, expiration time.Time) (Meta, error)
Delete(ctx context.Context, path Path, pathCipher storj.CipherSuite) error Delete(ctx context.Context, path Path, pathCipher storj.CipherSuite) error
List(ctx context.Context, prefix Path, startAfter, endBefore string, pathCipher storj.CipherSuite, recursive bool, limit int, metaFlags uint32) (items []ListItem, more bool, err error) List(ctx context.Context, prefix Path, startAfter string, pathCipher storj.CipherSuite, recursive bool, limit int, metaFlags uint32) (items []ListItem, more bool, err error)
} }
// streamStore is a store for streams. It implements typedStore as part of an ongoing migration // streamStore is a store for streams. It implements typedStore as part of an ongoing migration
@ -582,7 +582,7 @@ func pathForKey(raw string) paths.Unencrypted {
} }
// List all the paths inside l/, stripping off the l/ prefix // List all the paths inside l/, stripping off the l/ prefix
func (s *streamStore) List(ctx context.Context, prefix Path, startAfter, endBefore string, pathCipher storj.CipherSuite, recursive bool, limit int, metaFlags uint32) (items []ListItem, more bool, err error) { func (s *streamStore) List(ctx context.Context, prefix Path, startAfter string, pathCipher storj.CipherSuite, recursive bool, limit int, metaFlags uint32) (items []ListItem, more bool, err error) {
defer mon.Task()(&ctx)(&err) defer mon.Task()(&ctx)(&err)
// TODO use flags with listing // TODO use flags with listing
@ -611,9 +611,9 @@ func (s *streamStore) List(ctx context.Context, prefix Path, startAfter, endBefo
encPrefix = paths.NewEncrypted(encPrefix.Raw()[:lastSlashIdx]) encPrefix = paths.NewEncrypted(encPrefix.Raw()[:lastSlashIdx])
} }
// We have to encrypt startAfter and endBefore but only if they don't contain a bucket. // We have to encrypt startAfter but only if it doesn't contain a bucket.
// They contain a bucket if and only if the prefix has no bucket. This is why they are raw // It contains a bucket if and only if the prefix has no bucket. This is why it is a raw
// strings instead of a typed string: it's either a bucket or an unencrypted path component // string instead of a typed string: it's either a bucket or an unencrypted path component
// and that isn't known at compile time. // and that isn't known at compile time.
needsEncryption := prefix.Bucket() != "" needsEncryption := prefix.Bucket() != ""
if needsEncryption { if needsEncryption {

View File

@ -132,7 +132,7 @@ func TestStreamsInterruptedDelete(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
// Ensure the item shows when we list // Ensure the item shows when we list
listItems, _, err := streamStore.List(ctx, bucketName, "", "", storj.EncNull, true, 10, meta.None) listItems, _, err := streamStore.List(ctx, bucketName, "", storj.EncNull, true, 10, meta.None)
require.NoError(t, err) require.NoError(t, err)
require.True(t, len(listItems) == 1) require.True(t, len(listItems) == 1)
@ -158,7 +158,7 @@ func TestStreamsInterruptedDelete(t *testing.T) {
// It should *still* show when we list, as we've only deleted one // It should *still* show when we list, as we've only deleted one
// segment // segment
listItems, _, err = streamStore.List(ctx, bucketName, "", "", storj.EncNull, true, 10, meta.None) listItems, _, err = streamStore.List(ctx, bucketName, "", storj.EncNull, true, 10, meta.None)
require.NoError(t, err) require.NoError(t, err)
require.True(t, len(listItems) == 1) require.True(t, len(listItems) == 1)
@ -168,7 +168,7 @@ func TestStreamsInterruptedDelete(t *testing.T) {
_ = streamStore.Delete(ctx, fullPath, storj.EncNull) _ = streamStore.Delete(ctx, fullPath, storj.EncNull)
// Now it should have 0 list items // Now it should have 0 list items
listItems, _, err = streamStore.List(ctx, bucketName, "", "", storj.EncNull, true, 10, meta.None) listItems, _, err = streamStore.List(ctx, bucketName, "", storj.EncNull, true, 10, meta.None)
require.NoError(t, err) require.NoError(t, err)
require.True(t, len(listItems) == 0) require.True(t, len(listItems) == 0)
}) })
@ -203,39 +203,39 @@ func TestStreamStoreList(t *testing.T) {
prefix := bucketName prefix := bucketName
// should list all // should list all
items, more, err := streamStore.List(ctx, prefix, "", "", storj.EncNull, true, 10, meta.None) items, more, err := streamStore.List(ctx, prefix, "", storj.EncNull, true, 10, meta.None)
require.NoError(t, err) require.NoError(t, err)
require.False(t, more) require.False(t, more)
require.Equal(t, len(objects), len(items)) require.Equal(t, len(objects), len(items))
// should list first two and more = true // should list first two and more = true
items, more, err = streamStore.List(ctx, prefix, "", "", storj.EncNull, true, 2, meta.None) items, more, err = streamStore.List(ctx, prefix, "", storj.EncNull, true, 2, meta.None)
require.NoError(t, err) require.NoError(t, err)
require.True(t, more) require.True(t, more)
require.Equal(t, 2, len(items)) require.Equal(t, 2, len(items))
// should list only prefixes // should list only prefixes
items, more, err = streamStore.List(ctx, prefix, "", "", storj.EncNull, false, 10, meta.None) items, more, err = streamStore.List(ctx, prefix, "", storj.EncNull, false, 10, meta.None)
require.NoError(t, err) require.NoError(t, err)
require.False(t, more) require.False(t, more)
require.Equal(t, 2, len(items)) require.Equal(t, 2, len(items))
// should list only BBBB bucket // should list only BBBB bucket
prefix = storj.JoinPaths(bucketName, "bbbb") prefix = storj.JoinPaths(bucketName, "bbbb")
items, more, err = streamStore.List(ctx, prefix, "", "", storj.EncNull, false, 10, meta.None) items, more, err = streamStore.List(ctx, prefix, "", storj.EncNull, false, 10, meta.None)
require.NoError(t, err) require.NoError(t, err)
require.False(t, more) require.False(t, more)
require.Equal(t, 3, len(items)) require.Equal(t, 3, len(items))
// should list only BBBB bucket after afile // should list only BBBB bucket after afile
items, more, err = streamStore.List(ctx, prefix, "afile1", "", storj.EncNull, false, 10, meta.None) items, more, err = streamStore.List(ctx, prefix, "afile1", storj.EncNull, false, 10, meta.None)
require.NoError(t, err) require.NoError(t, err)
require.False(t, more) require.False(t, more)
require.Equal(t, 2, len(items)) require.Equal(t, 2, len(items))
// should list nothing // should list nothing
prefix = storj.JoinPaths(bucketName, "cccc") prefix = storj.JoinPaths(bucketName, "cccc")
items, more, err = streamStore.List(ctx, prefix, "", "", storj.EncNull, true, 10, meta.None) items, more, err = streamStore.List(ctx, prefix, "", storj.EncNull, true, 10, meta.None)
require.NoError(t, err) require.NoError(t, err)
require.False(t, more) require.False(t, more)
require.Equal(t, 0, len(items)) require.Equal(t, 0, len(items))