Skip to content

Commit 174e620

Browse files
Implement Box.putMany (#4)
1 parent e472eba commit 174e620

File tree

3 files changed

+141
-47
lines changed

3 files changed

+141
-47
lines changed

objectbox/lib/src/bindings/helpers.dart

-11
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,3 @@ checkObxPtr(Pointer ptr, String msg, [bool hasLastError = false]) {
1313
throw ObjectBoxException("$msg: ${hasLastError ? Common.lastErrorString() : ""}");
1414
return ptr;
1515
}
16-
17-
Uint8List loadMemory(Pointer<Uint8> data, int size) {
18-
if(data == null || data.address == 0)
19-
throw Exception("invalid memory pointer: $data");
20-
if(size < 0)
21-
throw Exception("invalid memory region size: $size");
22-
var buffer = new Uint8List(size);
23-
for(int i = 0; i < size; ++i)
24-
buffer[i] = data.elementAt(i).load<int>();
25-
return buffer;
26-
}

objectbox/lib/src/box.dart

+140-36
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ class _OBXFBEntityReader extends fb.TableReader<_OBXFBEntity> {
3535
new _OBXFBEntity._(bc, offset);
3636
}
3737

38-
class _IDArray {
38+
class _IDArray { // wrapper for "struct OBX_id_array"
3939
Pointer<Uint64> _idsPtr, _structPtr;
4040

4141
_IDArray(List<int> ids) {
@@ -55,6 +55,80 @@ class _IDArray {
5555
}
5656
}
5757

58+
class _ByteBuffer {
59+
Pointer<Uint8> _ptr;
60+
int _size;
61+
62+
_ByteBuffer(this._ptr, this._size);
63+
64+
_ByteBuffer.allocate(Uint8List dartData, [bool align = true]) {
65+
_ptr = Pointer<Uint8>.allocate(count: align ? ((dartData.length + 3.0) ~/ 4.0) * 4 : dartData.length);
66+
for(int i = 0; i < dartData.length; ++i)
67+
_ptr.elementAt(i).store(dartData[i]);
68+
_size = dartData.length;
69+
}
70+
71+
_ByteBuffer.fromOBXBytes(Pointer<Uint64> obxPtr) { // extract fields from "struct OBX_bytes"
72+
_ptr = Pointer<Uint8>.fromAddress(obxPtr.load<int>());
73+
_size = obxPtr.elementAt(1).load<int>();
74+
}
75+
76+
get ptr => _ptr;
77+
get voidPtr => Pointer<Void>.fromAddress(_ptr.address);
78+
get address => _ptr.address;
79+
get size => _size;
80+
81+
Uint8List get data {
82+
var buffer = new Uint8List(size);
83+
for(int i = 0; i < size; ++i)
84+
buffer[i] = _ptr.elementAt(i).load<int>();
85+
return buffer;
86+
}
87+
88+
free() => _ptr.free();
89+
}
90+
91+
class _SerializedByteBufferArray {
92+
Pointer<Uint64> _outerPtr, _innerPtr; // outerPtr points to the instance itself, innerPtr points to the respective OBX_bytes_array.bytes
93+
94+
_SerializedByteBufferArray(this._outerPtr, this._innerPtr);
95+
get ptr => _outerPtr;
96+
97+
free() {
98+
_innerPtr.free();
99+
_outerPtr.free();
100+
}
101+
}
102+
103+
class _ByteBufferArray {
104+
List<_ByteBuffer> _buffers;
105+
106+
_ByteBufferArray(this._buffers);
107+
108+
_ByteBufferArray.fromOBXBytesArray(Pointer<Uint64> bytesArray) {
109+
_buffers = [];
110+
Pointer<Uint64> bufferPtrs = Pointer<Uint64>.fromAddress(bytesArray.load<int>()); // bytesArray.bytes
111+
int numBuffers = bytesArray.elementAt(1).load<int>(); // bytesArray.count
112+
for(int i = 0; i < numBuffers; ++i) // loop through instances of "struct OBX_bytes"
113+
_buffers.add(_ByteBuffer.fromOBXBytes(bufferPtrs.elementAt(2 * i))); // 2 * i, because each instance of "struct OBX_bytes" has .data and .size
114+
}
115+
116+
_SerializedByteBufferArray toOBXBytesArray() {
117+
Pointer<Uint64> bufferPtrs = Pointer<Uint64>.allocate(count: _buffers.length * 2);
118+
for(int i = 0; i < _buffers.length; ++i) {
119+
bufferPtrs.elementAt(2 * i).store(_buffers[i].ptr.address);
120+
bufferPtrs.elementAt(2 * i + 1).store(_buffers[i].size);
121+
}
122+
123+
Pointer<Uint64> outerPtr = Pointer<Uint64>.allocate(count: 2);
124+
outerPtr.store(bufferPtrs.address);
125+
outerPtr.elementAt(1).store(_buffers.length);
126+
return _SerializedByteBufferArray(outerPtr, bufferPtrs);
127+
}
128+
129+
get buffers => _buffers;
130+
}
131+
58132
class Box<T> {
59133
Store _store;
60134
Pointer<Void> _objectboxBox;
@@ -69,7 +143,7 @@ class Box<T> {
69143
checkObxPtr(_objectboxBox, "failed to create box");
70144
}
71145

72-
_marshal(propVals) {
146+
_ByteBuffer _marshal(propVals) {
73147
var builder = new fb.Builder(initialSize: 1024);
74148

75149
// write all strings
@@ -98,12 +172,14 @@ class Box<T> {
98172
});
99173

100174
var endOffset = builder.endTable();
101-
return builder.finish(endOffset);
175+
return _ByteBuffer.allocate(builder.finish(endOffset));
102176
}
103-
104-
_unmarshal(buffer) {
177+
178+
T _unmarshal(_ByteBuffer buffer) {
179+
if(buffer.size == 0 || buffer.address == 0)
180+
return null;
105181
Map<String, dynamic> propVals = {};
106-
var entity = new _OBXFBEntity(buffer);
182+
var entity = new _OBXFBEntity(buffer.data);
107183

108184
_entityDefinition["properties"].forEach((p) {
109185
var propReader;
@@ -124,48 +200,76 @@ class Box<T> {
124200
return _entityBuilder(propVals);
125201
}
126202

127-
_unmarshalArray(Pointer<Uint64> bytesArray) { // expects pointer to OBX_bytes_array and manually resolves its contents (see objectbox.h)
128-
List<T> ret = [];
129-
int numObjects = bytesArray.elementAt(1).load<int>(); // bytesArray.count
130-
Pointer<Uint64> objectsPtrs = Pointer<Uint64>.fromAddress(bytesArray.load<int>()); // bytesArray.bytes
131-
for(int i = 0; i < numObjects; ++i) { // loop through instances of OBX_bytes
132-
Pointer<Uint8> data = Pointer<Uint8>.fromAddress(objectsPtrs.elementAt(2 * i).load<int>()); // bytesArray.bytes[i].data
133-
int size = objectsPtrs.elementAt(2 * i + 1).load<int>(); // bytesArray.bytes[i].size
134-
if(data.address == 0 || size == 0) {
135-
ret.add(null);
136-
continue;
137-
}
138-
ret.add(_unmarshal(loadMemory(data, size)));
203+
// expects pointer to OBX_bytes_array and manually resolves its contents (see objectbox.h)
204+
_unmarshalArray(Pointer<Uint64> bytesArray) {
205+
return _ByteBufferArray.fromOBXBytesArray(bytesArray).buffers.map(_unmarshal).toList();
206+
}
207+
208+
_getOBXPutMode(PutMode mode) {
209+
switch(mode) {
210+
case PutMode.Put: return OBXPutMode.PUT;
211+
case PutMode.Insert: return OBXPutMode.INSERT;
212+
case PutMode.Update: return OBXPutMode.UPDATE;
139213
}
140-
return ret;
141214
}
142215

143-
put(T inst, {PutMode mode = PutMode.Put}) { // if the respective ID property is given as null or 0, a newly assigned ID is returned, otherwise the existing ID is returned
216+
// if the respective ID property is given as null or 0, a newly assigned ID is returned, otherwise the existing ID is returned
217+
int put(T inst, {PutMode mode = PutMode.Put}) {
144218
var propVals = _entityReader(inst);
145219
var idPropName = _entityDefinition["idPropertyName"];
146220
if(propVals[idPropName] == null || propVals[idPropName] == 0) {
147221
final id = bindings.obx_box_id_for_put(_objectboxBox, 0);
148222
propVals[idPropName] = id;
149223
}
150-
var buffer = _marshal(propVals);
224+
225+
// put object into box and free the buffer
226+
_ByteBuffer buffer = _marshal(propVals);
227+
checkObx(bindings.obx_box_put(_objectboxBox, propVals[idPropName], buffer.voidPtr, buffer.size, _getOBXPutMode(mode)));
228+
buffer.free();
229+
return propVals[idPropName];
230+
}
231+
232+
// only instances whose ID property ot null or 0 will be given a new, valid number for that. A list of the final IDs is returned
233+
List<int> putMany(List<T> insts, {PutMode mode = PutMode.Put}) {
234+
if(insts.length == 0)
235+
return [];
151236

152-
// determine internal put mode from given enum
153-
var putMode;
154-
switch(mode) {
155-
case PutMode.Put: putMode = OBXPutMode.PUT; break;
156-
case PutMode.Insert: putMode = OBXPutMode.INSERT; break;
157-
case PutMode.Update: putMode = OBXPutMode.UPDATE; break;
237+
// read all property values and find number of instances where ID is missing
238+
var allPropVals = insts.map(_entityReader).toList();
239+
var idPropName = _entityDefinition["idPropertyName"];
240+
int numInstsMissingId = 0;
241+
for(var instPropVals in allPropVals)
242+
if(instPropVals[idPropName] == null || instPropVals[idPropName] == 0)
243+
++numInstsMissingId;
244+
245+
// generate new IDs for these instances and set them
246+
Pointer<Uint64> instIdsMemory;
247+
if(numInstsMissingId != 0) {
248+
instIdsMemory = Pointer<Uint64>.allocate(count: numInstsMissingId);
249+
checkObx(bindings.obx_box_ids_for_put(_objectboxBox, numInstsMissingId, instIdsMemory));
250+
int newIdIndex = 0;
251+
for(var instPropVals in allPropVals)
252+
if(instPropVals[idPropName] == null || instPropVals[idPropName] == 0)
253+
instPropVals[idPropName] = instIdsMemory.elementAt(newIdIndex++).load<int>();
254+
158255
}
159256

160-
// transform flatbuffers byte array into memory area for C, with a length of a multiple of four
161-
Pointer<Uint8> bufferPtr = Pointer<Uint8>.allocate(count: ((buffer.length + 3.0) / 4.0).toInt() * 4);
162-
for(int i = 0; i < buffer.length; ++i)
163-
bufferPtr.elementAt(i).store(buffer[i] as int);
257+
// because obx_box_put_many also needs a list of all IDs of the elements to be put into the box, generate this list now (only needed if not all IDs have been generated)
258+
if(numInstsMissingId != insts.length) {
259+
if(instIdsMemory != null)
260+
instIdsMemory.free();
261+
instIdsMemory = Pointer<Uint64>.allocate(count: insts.length);
262+
for(int i = 0; i < allPropVals.length; ++i)
263+
instIdsMemory.elementAt(i).store(allPropVals[i][idPropName]);
264+
}
164265

165-
// put object into box and free the buffer
166-
checkObx(bindings.obx_box_put(_objectboxBox, propVals[idPropName], Pointer<Void>.fromAddress(bufferPtr.address), buffer.length, putMode));
167-
bufferPtr.free();
168-
return propVals[idPropName];
266+
// marshal all objects to be put into the box
267+
var putObjects = _ByteBufferArray(allPropVals.map(_marshal).toList()).toOBXBytesArray();
268+
269+
checkObx(bindings.obx_box_put_many(_objectboxBox, putObjects.ptr, instIdsMemory, _getOBXPutMode(mode)));
270+
putObjects.free();
271+
instIdsMemory.free();
272+
return allPropVals.map((p) => p[idPropName] as int).toList();
169273
}
170274

171275
_inReadTransaction(fn) {
@@ -186,7 +290,7 @@ class Box<T> {
186290
var size = sizePtr.load<int>();
187291

188292
// transform bytes from memory to Dart byte list
189-
var buffer = loadMemory(data, size);
293+
var buffer = _ByteBuffer(data, size);
190294
dataPtr.free();
191295
sizePtr.free();
192296

objectbox_test/test/test.dart

+1
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ main() {
2121

2222
var note = Note.construct("Hello 😄 ${new Random().nextInt(1 << 32)}");
2323
note.id = box.put(note);
24+
print(box.putMany([Note.construct("ABC"), Note.construct("DEF")]));
2425
print("new note got id ${note.id}");
2526
print("refetched note: ${box.get(note.id)}");
2627

0 commit comments

Comments
 (0)