rocksdb-native 1.1.0 → 2.0.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.
package/lib/batch.js CHANGED
@@ -1,20 +1,26 @@
1
+ const c = require('compact-encoding')
1
2
  const b4a = require('b4a')
2
3
  const binding = require('../binding')
3
4
 
4
5
  const empty = b4a.alloc(0)
5
6
 
6
- module.exports = class RocksDBBatch {
7
+ class RocksDBBatch {
7
8
  constructor (db, opts = {}) {
8
9
  const {
9
- capacity = 8
10
+ capacity = 8,
11
+ encoding = null,
12
+ keyEncoding = encoding,
13
+ valueEncoding = encoding
10
14
  } = opts
11
15
 
12
16
  this._db = db
13
17
  this._capacity = capacity
14
- this._keys = []
15
- this._values = []
18
+ this._operations = []
16
19
  this._promises = []
17
20
 
21
+ this._keyEncoding = keyEncoding
22
+ this._valueEncoding = valueEncoding
23
+
18
24
  this._enqueuePromise = this._enqueuePromise.bind(this)
19
25
 
20
26
  this._request = null
@@ -31,8 +37,7 @@ module.exports = class RocksDBBatch {
31
37
  _onfinished () {
32
38
  const resolve = this._resolveRequest
33
39
 
34
- this._keys = []
35
- this._values = []
40
+ this._operations = []
36
41
  this._promises = []
37
42
  this._request = null
38
43
  this._resolveRequest = null
@@ -40,6 +45,85 @@ module.exports = class RocksDBBatch {
40
45
  if (resolve !== null) resolve()
41
46
  }
42
47
 
48
+ _resize () {
49
+ if (this._operations.length <= this._capacity) return false
50
+
51
+ while (this._operations.length > this._capacity) {
52
+ this._capacity *= 2
53
+ }
54
+
55
+ return true
56
+ }
57
+
58
+ async ready () {
59
+ if (this._handle !== null) return
60
+
61
+ if (this._db.opened === false) await this._db.ready()
62
+
63
+ this._init()
64
+ }
65
+
66
+ flush () {
67
+ if (this._request) throw new Error('Request already in progress')
68
+
69
+ this._request = new Promise((resolve) => { this._resolveRequest = resolve })
70
+ this._flush()
71
+
72
+ return this._request
73
+ }
74
+
75
+ tryFlush () {
76
+ if (this._request) throw new Error('Request already in progress')
77
+
78
+ this._request = true
79
+ this._flush()
80
+ }
81
+
82
+ async _flush () {
83
+ if (this._handle === null) await this.ready()
84
+ }
85
+
86
+ _enqueuePromise (resolve, reject) {
87
+ this._promises.push({ resolve, reject })
88
+ }
89
+
90
+ _encodeKey (k) {
91
+ if (this._keyEncoding) return c.encode(this._keyEncoding, k)
92
+ if (typeof k === 'string') return b4a.from(k)
93
+ return k
94
+ }
95
+
96
+ _encodeValue (v) {
97
+ if (this._valueEncoding) return c.encode(this._valueEncoding, v)
98
+ if (v === null) return empty
99
+ if (typeof v === 'string') return b4a.from(v)
100
+ return v
101
+ }
102
+
103
+ _decodeValue (b) {
104
+ if (this._valueEncoding) return c.decode(this._valueEncoding, b)
105
+ return b
106
+ }
107
+ }
108
+
109
+ exports.ReadBatch = class RocksDBReadBatch extends RocksDBBatch {
110
+ async _init () {
111
+ this._handle = binding.readInit(this)
112
+ this._buffer = binding.readBuffer(this._handle, this._capacity)
113
+ }
114
+
115
+ _resize () {
116
+ if (super._resize() && this._handle !== null) {
117
+ this._buffer = binding.readBuffer(this._handle, this._capacity)
118
+ }
119
+ }
120
+
121
+ async _flush () {
122
+ await super._flush()
123
+
124
+ binding.read(this._db._handle, this._handle, this._operations, this._onread)
125
+ }
126
+
43
127
  _onread (errs, values) {
44
128
  for (let i = 0, n = this._promises.length; i < n; i++) {
45
129
  const promise = this._promises[i]
@@ -48,123 +132,158 @@ module.exports = class RocksDBBatch {
48
132
  const err = errs[i]
49
133
 
50
134
  if (err) promise.reject(new Error(err))
51
- else promise.resolve(values[i].byteLength === 0 ? null : b4a.from(values[i]))
135
+ else promise.resolve(values[i].byteLength === 0 ? null : this._decodeValue(b4a.from(values[i])))
52
136
  }
53
137
 
54
138
  this._onfinished()
55
139
  }
56
140
 
57
- _onwrite (err) {
58
- for (let i = 0, n = this._promises.length; i < n; i++) {
59
- const promise = this._promises[i]
60
- if (promise === null) continue
141
+ get (key) {
142
+ if (this._request) throw new Error('Request already in progress')
61
143
 
62
- if (err) promise.reject(new Error(err))
63
- else promise.resolve(this._values[i].byteLength === 0 ? null : this._values[i])
64
- }
144
+ const promise = new Promise(this._enqueuePromise)
65
145
 
66
- this._onfinished()
146
+ this._operations.push(new RocksDBGet(this._encodeKey(key)))
147
+ this._resize()
148
+
149
+ return promise
67
150
  }
68
151
 
69
- _resize () {
70
- if (this._keys.length <= this._capacity) return
152
+ tryPut (key, value) {
153
+ if (this._request) throw new Error('Request already in progress')
71
154
 
72
- while (this._keys.length > this._capacity) {
73
- this._capacity *= 2
74
- }
155
+ this._operations.push(new RocksDBPut(this._encodeKey(key), this._encodeValue(value)))
156
+ this._promises.push(null)
157
+ this._resize()
158
+ }
159
+ }
160
+
161
+ exports.WriteBatch = class RocksDBWriteBatch extends RocksDBBatch {
162
+ async _init () {
163
+ this._handle = binding.writeInit(this)
164
+ this._buffer = binding.writeBuffer(this._handle, this._capacity)
165
+ }
75
166
 
76
- if (this._handle !== null) {
77
- this._buffer = binding.batchBuffer(this._handle, this._capacity)
167
+ _resize () {
168
+ if (super._resize() && this._handle !== null) {
169
+ this._buffer = binding.writeBuffer(this._handle, this._capacity)
78
170
  }
79
171
  }
80
172
 
81
- async ready () {
82
- if (this._handle !== null) return
173
+ async _flush () {
174
+ await super._flush()
83
175
 
84
- if (this._db.opened === false) await this._db.ready()
176
+ binding.write(this._db._handle, this._handle, this._operations, this._onwrite)
177
+ }
178
+
179
+ _onwrite (err) {
180
+ for (let i = 0, n = this._promises.length; i < n; i++) {
181
+ const promise = this._promises[i]
182
+ if (promise === null) continue
85
183
 
86
- this._handle = binding.batchInit(this._db._handle, this)
184
+ if (err) promise.reject(new Error(err))
185
+ else promise.resolve()
186
+ }
87
187
 
88
- this._buffer = binding.batchBuffer(this._handle, this._capacity)
188
+ this._onfinished()
89
189
  }
90
190
 
91
- add (key, value = empty) {
191
+ put (key, value) {
92
192
  if (this._request) throw new Error('Request already in progress')
93
193
 
94
194
  const promise = new Promise(this._enqueuePromise)
95
195
 
96
- this._keys.push(this._encodeKey(key))
97
- this._values.push(this._encodeValue(value))
196
+ this._operations.push(new RocksDBPut(this._encodeKey(key), this._encodeValue(value)))
98
197
  this._resize()
99
198
 
100
199
  return promise
101
200
  }
102
201
 
103
- tryAdd (key, value = empty) {
202
+ tryPut (key, value) {
104
203
  if (this._request) throw new Error('Request already in progress')
105
204
 
106
- this._keys.push(this._encodeKey(key))
107
- this._values.push(this._encodeValue(value))
205
+ this._operations.push(new RocksDBPut(this._encodeKey(key), this._encodeValue(value)))
108
206
  this._promises.push(null)
109
207
  this._resize()
110
208
  }
111
209
 
112
- read () {
210
+ delete (key) {
113
211
  if (this._request) throw new Error('Request already in progress')
114
212
 
115
- this._request = new Promise((resolve) => { this._resolveRequest = resolve })
116
- this._read()
213
+ const promise = new Promise(this._enqueuePromise)
117
214
 
118
- return this._request
215
+ this._operations.push(new RocksDBDelete(this._encodeKey(key)))
216
+ this._resize()
217
+
218
+ return promise
119
219
  }
120
220
 
121
- tryRead () {
221
+ tryDelete (key) {
122
222
  if (this._request) throw new Error('Request already in progress')
123
223
 
124
- this._request = true
125
- this._read()
224
+ this._operations.push(new RocksDBDelete(this._encodeKey(key)))
225
+ this._promises.push(null)
226
+ this._resize()
126
227
  }
127
228
 
128
- async _read () {
129
- if (this._handle === null) await this.ready()
229
+ deleteRange (start, end) {
230
+ if (this._request) throw new Error('Request already in progress')
231
+
232
+ const promise = new Promise(this._enqueuePromise)
233
+
234
+ this._operations.push(new RocksDBDeleteRange(this._encodeKey(start), this._encodeKey(end)))
235
+ this._resize()
130
236
 
131
- binding.batchRead(this._handle, this._keys, this._onread)
237
+ return promise
132
238
  }
133
239
 
134
- write () {
240
+ tryDeleteRange (start, end) {
135
241
  if (this._request) throw new Error('Request already in progress')
136
242
 
137
- this._request = new Promise((resolve) => { this._resolveRequest = resolve })
138
- this._write()
243
+ this._operations.push(new RocksDBDeleteRange(this._encodeKey(start), this._encodeKey(end)))
244
+ this._promises.push(null)
245
+ this._resize()
246
+ }
247
+ }
139
248
 
140
- return this._request
249
+ class RocksDBGet {
250
+ constructor (key) {
251
+ this.key = key
141
252
  }
142
253
 
143
- tryWrite () {
144
- if (this._request) throw new Error('Request already in progress')
254
+ get type () {
255
+ return binding.GET
256
+ }
257
+ }
145
258
 
146
- this._request = true
147
- this._write()
259
+ class RocksDBPut {
260
+ constructor (key, value) {
261
+ this.key = key
262
+ this.value = value
148
263
  }
149
264
 
150
- async _write () {
151
- if (this._handle === null) await this.ready()
265
+ get type () {
266
+ return binding.PUT
267
+ }
268
+ }
152
269
 
153
- binding.batchWrite(this._handle, this._keys, this._values, this._onwrite)
270
+ class RocksDBDelete {
271
+ constructor (key) {
272
+ this.key = key
154
273
  }
155
274
 
156
- _enqueuePromise (resolve, reject) {
157
- this._promises.push({ resolve, reject })
275
+ get type () {
276
+ return binding.DELETE
158
277
  }
278
+ }
159
279
 
160
- _encodeKey (k) {
161
- if (typeof k === 'string') return Buffer.from(k)
162
- return k
280
+ class RocksDBDeleteRange {
281
+ constructor (start, end) {
282
+ this.start = start
283
+ this.end = end
163
284
  }
164
285
 
165
- _encodeValue (v) {
166
- if (v === null) return empty
167
- if (typeof v === 'string') return Buffer.from(v)
168
- return v
286
+ get type () {
287
+ return binding.DELETE_RANGE
169
288
  }
170
289
  }
package/lib/iterator.js CHANGED
@@ -1,40 +1,36 @@
1
1
  const { Readable } = require('streamx')
2
+ const c = require('compact-encoding')
2
3
  const b4a = require('b4a')
3
4
  const binding = require('../binding')
4
5
 
5
6
  const empty = b4a.alloc(0)
6
7
 
7
8
  module.exports = class RocksDBIterator extends Readable {
8
- constructor (db, start, end, opts = {}) {
9
- if (typeof start === 'string') start = b4a.from(start)
10
- if (typeof end === 'string') end = b4a.from(end)
11
-
12
- if (start && !b4a.isBuffer(start)) {
13
- opts = start
14
- start = empty
15
- } else if (end && !b4a.isBuffer(end)) {
16
- opts = end
17
- end = empty
18
- }
19
-
9
+ constructor (db, opts = {}) {
20
10
  const {
21
- gt = empty,
22
- gte = start,
23
- lt = end,
24
- lte = empty,
11
+ gt = null,
12
+ gte = null,
13
+ lt = null,
14
+ lte = null,
25
15
  reverse = false,
26
16
  limit = Infinity,
27
- capacity = 8
17
+ capacity = 8,
18
+ encoding = null,
19
+ keyEncoding = encoding,
20
+ valueEncoding = encoding
28
21
  } = opts
29
22
 
30
23
  super()
31
24
 
32
25
  this._db = db
33
26
 
34
- this._gt = toBuffer(gt) || empty
35
- this._gte = toBuffer(gte) || empty
36
- this._lt = toBuffer(lt) || empty
37
- this._lte = toBuffer(lte) || empty
27
+ this._keyEncoding = keyEncoding
28
+ this._valueEncoding = valueEncoding
29
+
30
+ this._gt = gt ? this._encodeKey(gt) : empty
31
+ this._gte = gte ? this._encodeKey(gte) : empty
32
+ this._lt = lt ? this._encodeKey(lt) : empty
33
+ this._lte = lte ? this._encodeKey(lte) : empty
38
34
 
39
35
  this._reverse = reverse
40
36
  this._limit = limit
@@ -66,7 +62,7 @@ module.exports = class RocksDBIterator extends Readable {
66
62
  this._limit -= n
67
63
 
68
64
  for (let i = 0; i < n; i++) {
69
- this.push({ key: b4a.from(keys[i]), value: b4a.from(values[i]) })
65
+ this.push({ key: this._decodeKey(b4a.from(keys[i])), value: this._decodeValue(b4a.from(values[i])) })
70
66
  }
71
67
 
72
68
  if (n < this._capacity) this.push(null)
@@ -91,12 +87,11 @@ module.exports = class RocksDBIterator extends Readable {
91
87
 
92
88
  if (this._db.opened === false) await this._db.ready()
93
89
 
94
- this._handle = binding.iteratorInit(this._db._handle, this,
95
- this._onopen,
96
- this._onclose,
97
- this._onread
98
- )
90
+ this._init()
91
+ }
99
92
 
93
+ _init () {
94
+ this._handle = binding.iteratorInit(this, this._onopen, this._onclose, this._onread)
100
95
  this._buffer = binding.iteratorBuffer(this._handle, this._capacity)
101
96
  }
102
97
 
@@ -105,7 +100,7 @@ module.exports = class RocksDBIterator extends Readable {
105
100
 
106
101
  this._pendingOpen = cb
107
102
 
108
- binding.iteratorOpen(this._handle, this._gt, this._gte, this._lt, this._lte, this._reverse)
103
+ binding.iteratorOpen(this._db._handle, this._handle, this._gt, this._gte, this._lt, this._lte, this._reverse)
109
104
  }
110
105
 
111
106
  _read (cb) {
@@ -121,8 +116,20 @@ module.exports = class RocksDBIterator extends Readable {
121
116
 
122
117
  binding.iteratorClose(this._handle)
123
118
  }
124
- }
125
119
 
126
- function toBuffer (data) {
127
- return typeof data === 'string' ? b4a.from(data) : data
120
+ _encodeKey (k) {
121
+ if (this._keyEncoding) return c.encode(this._keyEncoding, k)
122
+ if (typeof k === 'string') return b4a.from(k)
123
+ return k
124
+ }
125
+
126
+ _decodeKey (b) {
127
+ if (this._keyEncoding) return c.decode(this._keyEncoding, b)
128
+ return b
129
+ }
130
+
131
+ _decodeValue (b) {
132
+ if (this._valueEncoding) return c.decode(this._valueEncoding, b)
133
+ return b
134
+ }
128
135
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rocksdb-native",
3
- "version": "1.1.0",
3
+ "version": "2.0.0",
4
4
  "description": "librocksdb bindings for JavaScript",
5
5
  "exports": {
6
6
  ".": "./index.js",
@@ -34,6 +34,7 @@
34
34
  "homepage": "https://github.com/holepunchto/rocksdb-native",
35
35
  "dependencies": {
36
36
  "b4a": "^1.6.6",
37
+ "compact-encoding": "^2.15.0",
37
38
  "load-addon": "^1.0.0",
38
39
  "ready-resource": "^1.0.0",
39
40
  "streamx": "^2.16.1"
@@ -14,18 +14,18 @@ typedef struct rocksdb_open_s rocksdb_open_t;
14
14
  typedef struct rocksdb_close_s rocksdb_close_t;
15
15
  typedef struct rocksdb_slice_s rocksdb_slice_t;
16
16
  typedef struct rocksdb_range_s rocksdb_range_t;
17
- typedef struct rocksdb_read_range_s rocksdb_read_range_t;
18
- typedef struct rocksdb_delete_range_s rocksdb_delete_range_t;
19
17
  typedef struct rocksdb_iterator_s rocksdb_iterator_t;
20
- typedef struct rocksdb_batch_s rocksdb_batch_t;
18
+ typedef struct rocksdb_read_s rocksdb_read_t;
19
+ typedef struct rocksdb_read_batch_s rocksdb_read_batch_t;
20
+ typedef struct rocksdb_write_s rocksdb_write_t;
21
+ typedef struct rocksdb_write_batch_s rocksdb_write_batch_t;
21
22
  typedef struct rocksdb_s rocksdb_t;
22
23
 
23
24
  typedef void (*rocksdb_open_cb)(rocksdb_open_t *req, int status);
24
25
  typedef void (*rocksdb_close_cb)(rocksdb_close_t *req, int status);
25
- typedef void (*rocksdb_read_range_cb)(rocksdb_read_range_t *req, int status);
26
- typedef void (*rocksdb_delete_range_cb)(rocksdb_delete_range_t *req, int status);
27
26
  typedef void (*rocksdb_iterator_cb)(rocksdb_iterator_t *iterator, int status);
28
- typedef void (*rocksdb_batch_cb)(rocksdb_batch_t *batch, int status);
27
+ typedef void (*rocksdb_read_batch_cb)(rocksdb_read_batch_t *batch, int status);
28
+ typedef void (*rocksdb_write_batch_cb)(rocksdb_write_batch_t *batch, int status);
29
29
 
30
30
  typedef enum {
31
31
  rocksdb_compaction_style_level = 0,
@@ -116,11 +116,13 @@ struct rocksdb_range_s {
116
116
  rocksdb_slice_t lte;
117
117
  };
118
118
 
119
- struct rocksdb_read_range_s {
119
+ struct rocksdb_iterator_s {
120
120
  uv_work_t worker;
121
121
 
122
122
  rocksdb_t *db;
123
123
 
124
+ void *handle; // Opaque iterator pointer
125
+
124
126
  rocksdb_range_t range;
125
127
  bool reverse;
126
128
 
@@ -132,67 +134,79 @@ struct rocksdb_read_range_s {
132
134
 
133
135
  char *error;
134
136
 
135
- rocksdb_read_range_cb cb;
137
+ rocksdb_iterator_cb cb;
136
138
 
137
139
  void *data;
138
140
  };
139
141
 
140
- struct rocksdb_delete_range_s {
141
- uv_work_t worker;
142
-
143
- rocksdb_t *db;
144
-
145
- rocksdb_range_t range;
146
-
147
- char *error;
142
+ typedef enum {
143
+ rocksdb_get = 1,
144
+ } rocksdb_read_type_t;
148
145
 
149
- rocksdb_delete_range_cb cb;
146
+ struct rocksdb_read_s {
147
+ rocksdb_read_type_t type;
150
148
 
151
- void *data;
149
+ union {
150
+ // For get
151
+ struct {
152
+ rocksdb_slice_t key;
153
+ rocksdb_slice_t value;
154
+ };
155
+ };
152
156
  };
153
157
 
154
- struct rocksdb_iterator_s {
158
+ struct rocksdb_read_batch_s {
155
159
  uv_work_t worker;
156
160
 
157
161
  rocksdb_t *db;
158
162
 
159
- void *handle; // Opaque iterator pointer
160
-
161
- rocksdb_range_t range;
162
- bool reverse;
163
-
164
163
  size_t len;
165
- size_t capacity;
166
164
 
167
- rocksdb_slice_t *keys;
168
- rocksdb_slice_t *values;
165
+ rocksdb_read_t *reads;
169
166
 
170
- char *error;
167
+ char **errors;
171
168
 
172
- rocksdb_iterator_cb cb;
169
+ rocksdb_read_batch_cb cb;
173
170
 
174
171
  void *data;
175
172
  };
176
173
 
177
- struct rocksdb_batch_s {
174
+ typedef enum {
175
+ rocksdb_put = 1,
176
+ rocksdb_delete,
177
+ rocksdb_delete_range,
178
+ } rocksdb_write_type_t;
179
+
180
+ struct rocksdb_write_s {
181
+ rocksdb_write_type_t type;
182
+
183
+ union {
184
+ // For put, delete
185
+ struct {
186
+ rocksdb_slice_t key;
187
+ rocksdb_slice_t value;
188
+ };
189
+
190
+ // For delete_range
191
+ struct {
192
+ rocksdb_slice_t start;
193
+ rocksdb_slice_t end;
194
+ };
195
+ };
196
+ };
197
+
198
+ struct rocksdb_write_batch_s {
178
199
  uv_work_t worker;
179
200
 
180
201
  rocksdb_t *db;
181
202
 
182
203
  size_t len;
183
204
 
184
- const rocksdb_slice_t *keys;
185
- rocksdb_slice_t *values;
186
-
187
- union {
188
- // A single error for the write or delete batch.
189
- char *error;
205
+ rocksdb_write_t *writes;
190
206
 
191
- // A list of errors for the read batch.
192
- char **errors;
193
- };
207
+ char *error;
194
208
 
195
- rocksdb_batch_cb cb;
209
+ rocksdb_write_batch_cb cb;
196
210
 
197
211
  void *data;
198
212
  };
@@ -222,16 +236,7 @@ rocksdb_slice_t
222
236
  rocksdb_slice_empty (void);
223
237
 
224
238
  int
225
- rocksdb_read_range (rocksdb_t *db, rocksdb_read_range_t *req, rocksdb_range_t range, bool reverse, rocksdb_slice_t *keys, rocksdb_slice_t *values, size_t capacity, rocksdb_read_range_cb cb);
226
-
227
- int
228
- rocksdb_delete_range (rocksdb_t *db, rocksdb_delete_range_t *req, rocksdb_range_t range, rocksdb_delete_range_cb cb);
229
-
230
- int
231
- rocksdb_iterator_init (rocksdb_t *db, rocksdb_iterator_t *iterator);
232
-
233
- int
234
- rocksdb_iterator_open (rocksdb_iterator_t *iterator, rocksdb_range_t range, bool reverse, rocksdb_iterator_cb cb);
239
+ rocksdb_iterator_open (rocksdb_t *db, rocksdb_iterator_t *iterator, rocksdb_range_t range, bool reverse, rocksdb_iterator_cb cb);
235
240
 
236
241
  int
237
242
  rocksdb_iterator_close (rocksdb_iterator_t *iterator, rocksdb_iterator_cb cb);
@@ -243,16 +248,10 @@ int
243
248
  rocksdb_iterator_read (rocksdb_iterator_t *iterator, rocksdb_slice_t *keys, rocksdb_slice_t *values, size_t capacity, rocksdb_iterator_cb cb);
244
249
 
245
250
  int
246
- rocksdb_batch_init (rocksdb_t *db, rocksdb_batch_t *batch);
247
-
248
- int
249
- rocksdb_batch_read (rocksdb_batch_t *batch, const rocksdb_slice_t *keys, rocksdb_slice_t *values, char **errors, size_t len, rocksdb_batch_cb cb);
250
-
251
- int
252
- rocksdb_batch_write (rocksdb_batch_t *batch, const rocksdb_slice_t *keys, const rocksdb_slice_t *values, size_t len, rocksdb_batch_cb cb);
251
+ rocksdb_read (rocksdb_t *db, rocksdb_read_batch_t *batch, rocksdb_read_t *reads, char **errors, size_t len, rocksdb_read_batch_cb cb);
253
252
 
254
253
  int
255
- rocksdb_batch_delete (rocksdb_batch_t *batch, const rocksdb_slice_t *keys, const rocksdb_slice_t *values, size_t len, rocksdb_batch_cb cb);
254
+ rocksdb_write (rocksdb_t *db, rocksdb_write_batch_t *batch, rocksdb_write_t *writes, size_t len, rocksdb_write_batch_cb cb);
256
255
 
257
256
  #ifdef __cplusplus
258
257
  }