lmdb 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.travis.yml +8 -0
- data/Gemfile +1 -1
- data/README.md +4 -0
- data/ext/lmdb_ext/extconf.rb +2 -0
- data/ext/lmdb_ext/lmdb_ext.c +533 -585
- data/ext/lmdb_ext/lmdb_ext.h +124 -0
- data/ext/lmdb_ext/prototypes.sh +4 -0
- data/lib/lmdb.rb +22 -7
- data/lmdb.gemspec +13 -13
- data/spec/helper.rb +28 -0
- data/spec/lmdb_spec.rb +260 -35
- metadata +7 -13
- data/lib/lmdb/database.rb +0 -66
- data/lib/lmdb/environment.rb +0 -135
- data/spec/lmdb/database_spec.rb +0 -23
- data/spec/lmdb/environment_spec.rb +0 -22
- data/spec/lmdb/lmdb_ext_spec.rb +0 -253
- data/spec/spec_helper.rb +0 -33
@@ -0,0 +1,124 @@
|
|
1
|
+
#ifndef _LMDB_EXT_H
|
2
|
+
#define _LMDB_EXT_H
|
3
|
+
|
4
|
+
#include "ruby.h"
|
5
|
+
#include "lmdb.h"
|
6
|
+
|
7
|
+
#define ENV_FLAGS ( \
|
8
|
+
MDB_FIXEDMAP | \
|
9
|
+
MDB_NOSUBDIR | \
|
10
|
+
MDB_NOSYNC | \
|
11
|
+
MDB_RDONLY | \
|
12
|
+
MDB_NOMETASYNC | \
|
13
|
+
MDB_WRITEMAP | \
|
14
|
+
MDB_MAPASYNC | \
|
15
|
+
MDB_NOTLS)
|
16
|
+
|
17
|
+
#define ENVIRONMENT(var, var_env) \
|
18
|
+
Environment* var_env; \
|
19
|
+
Data_Get_Struct(var, Environment, var_env); \
|
20
|
+
environment_check(var_env)
|
21
|
+
|
22
|
+
#define DATABASE(var, var_db) \
|
23
|
+
Database* var_db; \
|
24
|
+
Data_Get_Struct(var, Database, var_db);
|
25
|
+
|
26
|
+
#define TRANSACTION(var, var_txn) \
|
27
|
+
Transaction* var_txn; \
|
28
|
+
Data_Get_Struct(var, Transaction, var_txn)
|
29
|
+
|
30
|
+
#define CURSOR(var, var_cur) \
|
31
|
+
Cursor* var_cur; \
|
32
|
+
Data_Get_Struct(var, Cursor, var_cur); \
|
33
|
+
cursor_check(var_cur)
|
34
|
+
|
35
|
+
typedef struct Transaction Transaction;
|
36
|
+
|
37
|
+
typedef struct Transaction {
|
38
|
+
VALUE env;
|
39
|
+
VALUE parent;
|
40
|
+
MDB_txn* txn;
|
41
|
+
int refcount;
|
42
|
+
} Transaction;
|
43
|
+
|
44
|
+
typedef struct {
|
45
|
+
MDB_env* env;
|
46
|
+
VALUE txn;
|
47
|
+
int refcount;
|
48
|
+
} Environment;
|
49
|
+
|
50
|
+
typedef struct {
|
51
|
+
VALUE env;
|
52
|
+
MDB_dbi dbi;
|
53
|
+
int refcount;
|
54
|
+
} Database;
|
55
|
+
|
56
|
+
typedef struct {
|
57
|
+
VALUE db;
|
58
|
+
MDB_cursor* cur;
|
59
|
+
} Cursor;
|
60
|
+
|
61
|
+
typedef struct {
|
62
|
+
VALUE self;
|
63
|
+
const char* name;
|
64
|
+
int argc;
|
65
|
+
const VALUE* argv;
|
66
|
+
} HelperArgs;
|
67
|
+
|
68
|
+
static VALUE cEnvironment, cDatabase, cTransaction, cCursor, cError;
|
69
|
+
|
70
|
+
#define ERROR(name) static VALUE cError_##name;
|
71
|
+
#include "errors.h"
|
72
|
+
#undef ERROR
|
73
|
+
|
74
|
+
// BEGIN PROTOTYPES
|
75
|
+
void Init_lmdb_ext();
|
76
|
+
static VALUE call_with_transaction(VALUE venv, VALUE self, const char* name, int argc, const VALUE* argv, int flags);
|
77
|
+
static VALUE call_with_transaction_helper(VALUE arg);
|
78
|
+
static void check(int code);
|
79
|
+
static void cursor_check(Cursor* cursor);
|
80
|
+
static VALUE cursor_close(VALUE self);
|
81
|
+
static VALUE cursor_count(VALUE self);
|
82
|
+
static VALUE cursor_delete(int argc, VALUE *argv, VALUE self);
|
83
|
+
static VALUE cursor_first(VALUE self);
|
84
|
+
static void cursor_free(Cursor* cursor);
|
85
|
+
static VALUE cursor_get(VALUE self);
|
86
|
+
static void cursor_mark(Cursor* cursor);
|
87
|
+
static VALUE cursor_next(VALUE self);
|
88
|
+
static VALUE cursor_prev(VALUE self);
|
89
|
+
static VALUE cursor_put(int argc, VALUE* argv, VALUE self);
|
90
|
+
static VALUE cursor_set(VALUE self, VALUE vkey);
|
91
|
+
static VALUE cursor_set_range(VALUE self, VALUE vkey);
|
92
|
+
static VALUE database_clear(VALUE self);
|
93
|
+
static VALUE database_cursor(VALUE self);
|
94
|
+
static VALUE database_delete(int argc, VALUE *argv, VALUE self);
|
95
|
+
static void database_deref(Database* database);
|
96
|
+
static VALUE database_drop(VALUE self);
|
97
|
+
static VALUE database_get(VALUE self, VALUE vkey);
|
98
|
+
static void database_mark(Database* database);
|
99
|
+
static VALUE database_put(int argc, VALUE *argv, VALUE self);
|
100
|
+
static VALUE database_stat(VALUE self);
|
101
|
+
static void environment_check(Environment* environment);
|
102
|
+
static VALUE environment_close(VALUE self);
|
103
|
+
static VALUE environment_copy(VALUE self, VALUE path);
|
104
|
+
static VALUE environment_database(int argc, VALUE *argv, VALUE self);
|
105
|
+
static void environment_deref(Environment *environment);
|
106
|
+
static VALUE environment_flags(VALUE self);
|
107
|
+
static VALUE environment_info(VALUE self);
|
108
|
+
static MDB_txn* environment_need_txn(VALUE self);
|
109
|
+
static VALUE environment_open(int argc, VALUE *argv, VALUE klass);
|
110
|
+
static VALUE environment_path(VALUE self);
|
111
|
+
static VALUE environment_set_flags(VALUE self, VALUE vflags);
|
112
|
+
static VALUE environment_stat(VALUE self);
|
113
|
+
static VALUE environment_sync(int argc, VALUE *argv, VALUE self);
|
114
|
+
static VALUE environment_transaction(int argc, VALUE *argv, VALUE self);
|
115
|
+
static MDB_txn* environment_txn(VALUE self);
|
116
|
+
static VALUE stat2hash(const MDB_stat* stat);
|
117
|
+
static VALUE transaction_abort(VALUE self);
|
118
|
+
static VALUE transaction_commit(VALUE self);
|
119
|
+
static void transaction_deref(Transaction* transaction);
|
120
|
+
static void transaction_mark(Transaction* transaction);
|
121
|
+
static VALUE with_transaction(VALUE venv, VALUE(*fn)(VALUE), VALUE arg, int flags);
|
122
|
+
// END PROTOTYPES
|
123
|
+
|
124
|
+
#endif
|
data/lib/lmdb.rb
CHANGED
@@ -1,14 +1,29 @@
|
|
1
1
|
require 'lmdb_ext'
|
2
2
|
|
3
3
|
module LMDB
|
4
|
+
class Database
|
5
|
+
include Enumerable
|
4
6
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
7
|
+
def each
|
8
|
+
cursor do |c|
|
9
|
+
while i = c.next
|
10
|
+
yield(i)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
9
14
|
|
10
|
-
|
15
|
+
def [](key)
|
16
|
+
get(key)
|
17
|
+
end
|
18
|
+
|
19
|
+
def []=(key, value)
|
20
|
+
put(key, value)
|
21
|
+
value
|
22
|
+
end
|
11
23
|
|
12
|
-
|
13
|
-
|
24
|
+
def size
|
25
|
+
stat[:entries]
|
26
|
+
end
|
27
|
+
end
|
14
28
|
end
|
29
|
+
|
data/lmdb.gemspec
CHANGED
@@ -1,22 +1,22 @@
|
|
1
1
|
# -*- encoding: utf-8 -*-
|
2
2
|
|
3
3
|
Gem::Specification.new do |s|
|
4
|
-
s.name = File.basename(__FILE__,
|
5
|
-
s.version =
|
4
|
+
s.name = File.basename(__FILE__, '.gemspec')
|
5
|
+
s.version = '0.2.0'
|
6
6
|
s.platform = Gem::Platform::RUBY
|
7
|
-
s.licenses = [
|
8
|
-
s.summary =
|
9
|
-
s.email =
|
10
|
-
s.homepage =
|
11
|
-
s.description =
|
12
|
-
s.authors = [
|
13
|
-
s.extensions = Dir[
|
7
|
+
s.licenses = ['MIT']
|
8
|
+
s.summary = 'Ruby bindings to Lightning MDB'
|
9
|
+
s.email = 'mail@daniel-mendler.de'
|
10
|
+
s.homepage = 'https://github.com/minad/lmdb'
|
11
|
+
s.description = 'imdb is a Ruby binding to OpenLDAP Lightning MDB.'
|
12
|
+
s.authors = ['Daniel Mendler']
|
13
|
+
s.extensions = Dir['ext/**/extconf.rb']
|
14
14
|
|
15
15
|
s.files = `git ls-files`.split("\n")
|
16
16
|
s.test_files = `git ls-files -- spec/*`.split("\n")
|
17
|
-
s.require_paths = [
|
17
|
+
s.require_paths = ['lib']
|
18
18
|
|
19
|
-
s.add_development_dependency
|
20
|
-
s.add_development_dependency
|
21
|
-
s.add_development_dependency
|
19
|
+
s.add_development_dependency 'rake'
|
20
|
+
s.add_development_dependency 'rake-compiler'
|
21
|
+
s.add_development_dependency 'rspec'
|
22
22
|
end
|
data/spec/helper.rb
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'lmdb'
|
2
|
+
require 'rspec'
|
3
|
+
require 'fileutils'
|
4
|
+
|
5
|
+
SPEC_ROOT = File.dirname(__FILE__)
|
6
|
+
TEMP_ROOT = File.join(SPEC_ROOT, 'tmp')
|
7
|
+
|
8
|
+
module LMDB::SpecHelper
|
9
|
+
def mkpath(name = 'env')
|
10
|
+
path = File.join(TEMP_ROOT, name)
|
11
|
+
FileUtils.mkpath(path)
|
12
|
+
path
|
13
|
+
end
|
14
|
+
|
15
|
+
def path
|
16
|
+
@path ||= mkpath
|
17
|
+
end
|
18
|
+
|
19
|
+
def env
|
20
|
+
@env ||= LMDB::Environment.new path: path
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
RSpec.configure do |c|
|
25
|
+
c.include LMDB::SpecHelper
|
26
|
+
c.after { FileUtils.rm_rf TEMP_ROOT }
|
27
|
+
end
|
28
|
+
|
data/spec/lmdb_spec.rb
CHANGED
@@ -1,41 +1,266 @@
|
|
1
|
-
|
1
|
+
# coding: binary
|
2
|
+
require 'helper'
|
2
3
|
|
3
4
|
describe LMDB do
|
5
|
+
let(:env) { LMDB.open(path) }
|
6
|
+
after { env.close rescue nil }
|
4
7
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
8
|
+
let(:db) { env.database }
|
9
|
+
|
10
|
+
it 'has version constants' do
|
11
|
+
LMDB::VERSION_MAJOR.should be_instance_of(Fixnum)
|
12
|
+
LMDB::VERSION_MINOR.should be_instance_of(Fixnum)
|
13
|
+
LMDB::VERSION_PATCH.should be_instance_of(Fixnum)
|
14
|
+
LMDB::VERSION.should be_instance_of(String)
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'has environment flags' do
|
18
|
+
LMDB::FIXEDMAP.should be_instance_of(Fixnum)
|
19
|
+
LMDB::NOSUBDIR.should be_instance_of(Fixnum)
|
20
|
+
LMDB::NOSYNC.should be_instance_of(Fixnum)
|
21
|
+
LMDB::RDONLY.should be_instance_of(Fixnum)
|
22
|
+
LMDB::NOMETASYNC.should be_instance_of(Fixnum)
|
23
|
+
LMDB::WRITEMAP.should be_instance_of(Fixnum)
|
24
|
+
LMDB::MAPASYNC.should be_instance_of(Fixnum)
|
25
|
+
LMDB::NOTLS.should be_instance_of(Fixnum)
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'has database flags' do
|
29
|
+
LMDB::REVERSEKEY.should be_instance_of(Fixnum)
|
30
|
+
LMDB::DUPSORT.should be_instance_of(Fixnum)
|
31
|
+
LMDB::INTEGERKEY.should be_instance_of(Fixnum)
|
32
|
+
LMDB::DUPFIXED.should be_instance_of(Fixnum)
|
33
|
+
LMDB::INTEGERDUP.should be_instance_of(Fixnum)
|
34
|
+
LMDB::REVERSEDUP.should be_instance_of(Fixnum)
|
35
|
+
LMDB::CREATE.should be_instance_of(Fixnum)
|
36
|
+
LMDB::NOOVERWRITE.should be_instance_of(Fixnum)
|
37
|
+
LMDB::NODUPDATA.should be_instance_of(Fixnum)
|
38
|
+
LMDB::CURRENT.should be_instance_of(Fixnum)
|
39
|
+
LMDB::RESERVE.should be_instance_of(Fixnum)
|
40
|
+
LMDB::APPEND.should be_instance_of(Fixnum)
|
41
|
+
LMDB::APPENDDUP.should be_instance_of(Fixnum)
|
42
|
+
LMDB::MULTIPLE.should be_instance_of(Fixnum)
|
43
|
+
end
|
44
|
+
|
45
|
+
describe LMDB::Environment do
|
46
|
+
subject { env }
|
47
|
+
|
48
|
+
its(:path) { should == path }
|
49
|
+
its(:flags) { should == 0 }
|
50
|
+
|
51
|
+
describe 'open' do
|
52
|
+
it 'returns environment' do
|
53
|
+
env = LMDB::Environment.open(path)
|
54
|
+
env.should be_instance_of(described_class::Environment)
|
55
|
+
env.close
|
56
|
+
end
|
57
|
+
|
58
|
+
it 'accepts block' do
|
59
|
+
LMDB::Environment.open(path) do |env|
|
60
|
+
env.should be_instance_of(described_class::Environment)
|
61
|
+
42
|
62
|
+
end.should == 42
|
63
|
+
end
|
64
|
+
|
65
|
+
it 'accepts options' do
|
66
|
+
env = LMDB::Environment.open(path, :flags => LMDB::NOSYNC, :mode => 0777, :maxreaders => 777, :mapsize => 111111, :maxdbs => 666)
|
67
|
+
env.should be_instance_of(described_class::Environment)
|
68
|
+
env.info[:maxreaders].should == 777
|
69
|
+
env.info[:mapsize].should == 111111
|
70
|
+
env.close
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
it 'should return stat' do
|
75
|
+
stat = env.stat
|
76
|
+
stat[:psize].should be_instance_of(Fixnum)
|
77
|
+
stat[:depth].should be_instance_of(Fixnum)
|
78
|
+
stat[:branch_pages].should be_instance_of(Fixnum)
|
79
|
+
stat[:leaf_pages].should be_instance_of(Fixnum)
|
80
|
+
stat[:overflow_pages].should be_instance_of(Fixnum)
|
81
|
+
stat[:entries].should be_instance_of(Fixnum)
|
82
|
+
end
|
83
|
+
|
84
|
+
it 'should return info' do
|
85
|
+
info = env.info
|
86
|
+
info[:mapaddr].should be_instance_of(Fixnum)
|
87
|
+
info[:mapsize].should be_instance_of(Fixnum)
|
88
|
+
info[:last_pgno].should be_instance_of(Fixnum)
|
89
|
+
info[:last_txnid].should be_instance_of(Fixnum)
|
90
|
+
info[:maxreaders].should be_instance_of(Fixnum)
|
91
|
+
info[:numreaders].should be_instance_of(Fixnum)
|
92
|
+
end
|
93
|
+
|
94
|
+
it 'should copy' do
|
95
|
+
target = mkpath('copy')
|
96
|
+
subject.copy(target).should be_nil
|
97
|
+
end
|
98
|
+
|
99
|
+
it 'should sync' do
|
100
|
+
subject.sync.should be_nil
|
101
|
+
end
|
102
|
+
|
103
|
+
it 'should force-sync' do
|
104
|
+
subject.sync(true).should be_nil
|
105
|
+
end
|
106
|
+
|
107
|
+
it 'should accept custom flags' do
|
108
|
+
(subject.flags = LMDB::NOSYNC).should == LMDB::NOSYNC
|
109
|
+
subject.flags.should == LMDB::NOSYNC
|
110
|
+
|
111
|
+
(subject.flags = 0).should == 0
|
112
|
+
subject.flags.should == 0
|
113
|
+
end
|
114
|
+
|
115
|
+
describe 'transaction' do
|
116
|
+
subject { env}
|
117
|
+
|
118
|
+
it 'should create transactions' do
|
119
|
+
subject.transaction do |txn|
|
120
|
+
txn.should be_instance_of(described_class::Transaction)
|
121
|
+
txn.abort
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
it 'should create read-only transactions' do
|
126
|
+
subject.transaction(true) do |txn|
|
127
|
+
txn.should be_instance_of(described_class::Transaction)
|
128
|
+
txn.abort
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
it 'can create child transactions' do
|
133
|
+
env.transaction do |txn|
|
134
|
+
txn.should be_instance_of(described_class::Transaction)
|
135
|
+
env.transaction do |ctxn|
|
136
|
+
ctxn.should be_instance_of(described_class::Transaction)
|
137
|
+
ctxn.abort
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
it 'should support aborting parent transaction' do
|
143
|
+
env.transaction do |txn|
|
144
|
+
env.transaction do |ctxn|
|
145
|
+
db['key'] = 'value'
|
146
|
+
txn.abort
|
147
|
+
end
|
148
|
+
end
|
149
|
+
db['key'].should be(nil)
|
150
|
+
end
|
151
|
+
|
152
|
+
it 'should support comitting parent transaction' do
|
153
|
+
env.transaction do |txn|
|
154
|
+
env.transaction do |ctxn|
|
155
|
+
db['key'] = 'value'
|
156
|
+
txn.commit
|
157
|
+
end
|
158
|
+
end
|
159
|
+
db['key'].should == 'value'
|
160
|
+
end
|
38
161
|
end
|
39
162
|
end
|
40
163
|
|
41
|
-
|
164
|
+
describe LMDB::Database do
|
165
|
+
subject { db }
|
166
|
+
|
167
|
+
it 'should get/put data' do
|
168
|
+
subject.get('cat').should be_nil
|
169
|
+
subject.put('cat', 'garfield').should be_nil
|
170
|
+
subject.get('cat').should == 'garfield'
|
171
|
+
end
|
172
|
+
|
173
|
+
it 'stores key/values in same transaction' do
|
174
|
+
db.put('key', 'value').should be_nil
|
175
|
+
db.get('key').should == 'value'
|
176
|
+
end
|
177
|
+
|
178
|
+
it 'stores key/values in different transactions' do
|
179
|
+
env.transaction do
|
180
|
+
db.put('key', 'value').should be_nil
|
181
|
+
db.put('key2', 'value2').should be_nil
|
182
|
+
env.transaction do
|
183
|
+
db.put('key3', 'value3').should be_nil
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
env.transaction do
|
188
|
+
db.get('key').should == 'value'
|
189
|
+
db.get('key2').should == 'value2'
|
190
|
+
env.transaction do
|
191
|
+
db.get('key3').should == 'value3'
|
192
|
+
end
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
196
|
+
it 'should return stat' do
|
197
|
+
db.stat.should be_instance_of(Hash)
|
198
|
+
end
|
199
|
+
|
200
|
+
it 'should return size' do
|
201
|
+
db.size.should == 0
|
202
|
+
db.put('key', 'value')
|
203
|
+
db.size.should == 1
|
204
|
+
db.put('key2', 'value2')
|
205
|
+
db.size.should == 2
|
206
|
+
end
|
207
|
+
|
208
|
+
it 'should be enumerable' do
|
209
|
+
db['k1'] = 'v1'
|
210
|
+
db['k2'] = 'v2'
|
211
|
+
db.to_a.should == [['k1', 'v1'], ['k2', 'v2']]
|
212
|
+
end
|
213
|
+
|
214
|
+
it 'should have shortcuts' do
|
215
|
+
db['key'] = 'value'
|
216
|
+
db['key'].should == 'value'
|
217
|
+
end
|
218
|
+
|
219
|
+
it 'should store binary' do
|
220
|
+
bin1 = "\xAAx\BB\xCC1"
|
221
|
+
bin2 = "\xAAx\BB\xCC2"
|
222
|
+
db[bin1] = bin2
|
223
|
+
db['key'] = bin2
|
224
|
+
db[bin1].should == bin2
|
225
|
+
db['key'].should == bin2
|
226
|
+
end
|
227
|
+
end
|
228
|
+
|
229
|
+
describe LMDB::Cursor do
|
230
|
+
before do
|
231
|
+
db.put('key1', 'value1')
|
232
|
+
db.put('key2', 'value2')
|
233
|
+
end
|
234
|
+
|
235
|
+
it 'should get next key/value' do
|
236
|
+
db.cursor do |c|
|
237
|
+
c.first.should == ['key1', 'value1']
|
238
|
+
end
|
239
|
+
end
|
240
|
+
|
241
|
+
it 'should get next key/value' do
|
242
|
+
db.cursor do |c|
|
243
|
+
c.first
|
244
|
+
c.next.should == ['key2', 'value2']
|
245
|
+
end
|
246
|
+
end
|
247
|
+
|
248
|
+
it 'should seek to key' do
|
249
|
+
db.cursor do |c|
|
250
|
+
c.set('key1').should == ['key1', 'value1']
|
251
|
+
end
|
252
|
+
end
|
253
|
+
|
254
|
+
it 'should seek to closest key' do
|
255
|
+
db.cursor do |c|
|
256
|
+
c.set_range('key0').should == ['key1', 'value1']
|
257
|
+
end
|
258
|
+
end
|
259
|
+
|
260
|
+
it 'should seek to key with nuls' do
|
261
|
+
db.cursor do |c|
|
262
|
+
c.set_range('\x00').should == ['key1', 'value1']
|
263
|
+
end
|
264
|
+
end
|
265
|
+
end
|
266
|
+
end
|