Skip to content

Commit bbb5246

Browse files
author
Ivan Dlugos
committed
generated C-API binding - convert structs handling
1 parent 71b55ed commit bbb5246

File tree

6 files changed

+89
-183
lines changed

6 files changed

+89
-183
lines changed

lib/src/bindings/flatbuffers.dart

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ class OBXFlatbuffersManager<T> {
3838

3939
OBXFlatbuffersManager(this._modelEntity, this._entityBuilder);
4040

41-
Pointer<OBX_bytes> marshal(Map<String, dynamic> propVals) {
41+
OBX_bytes_wrapper marshal(Map<String, dynamic> propVals) {
4242
var builder = fb.Builder(initialSize: 1024);
4343

4444
// write all strings
@@ -91,7 +91,7 @@ class OBXFlatbuffersManager<T> {
9191
});
9292

9393
var endOffset = builder.endTable();
94-
return OBX_bytes.managedCopyOf(builder.finish(endOffset));
94+
return OBX_bytes_wrapper.managedCopyOf(builder.finish(endOffset));
9595
}
9696

9797
T unmarshal(final Uint8List bytes) {
@@ -142,10 +142,18 @@ class OBXFlatbuffersManager<T> {
142142
// expects pointer to OBX_bytes_array and manually resolves its contents (see objectbox.h)
143143
List<T> unmarshalArray(final Pointer<OBX_bytes_array> bytesArray,
144144
{bool allowMissing = false}) {
145-
var fn = (OBX_bytes b) => unmarshal(b.data);
146-
if (allowMissing) {
147-
fn = (OBX_bytes b) => b.isEmpty ? null : unmarshal(b.data);
145+
final result = <T>[];
146+
result.length = bytesArray.ref.count;
147+
148+
for (var i = 0; i < bytesArray.ref.count; i++) {
149+
final bytesPtr = bytesArray.ref.bytes.elementAt(i);
150+
if (allowMissing && (bytesPtr == nullptr || bytesPtr.ref.size == 0)) {
151+
result[i] = null;
152+
} else {
153+
result[i] = unmarshal(OBX_bytes_wrapper.safeDataAccess(bytesPtr));
154+
}
148155
}
149-
return bytesArray.ref.items().map<T>(fn).toList();
156+
157+
return result;
150158
}
151159
}

lib/src/bindings/structs.dart

Lines changed: 49 additions & 145 deletions
Original file line numberDiff line numberDiff line change
@@ -2,80 +2,62 @@ import 'dart:ffi';
22
import 'dart:typed_data' show Uint8List;
33
import 'package:ffi/ffi.dart' show allocate, free, Utf8;
44
import '../common.dart';
5+
import 'bindings.dart';
56

67
// Disable some linter rules for this file
78
// ignore_for_file: camel_case_types
89

9-
// Note: IntPtr seems to be the the correct representation for size_t: "Represents a native pointer-sized integer in C."
10-
11-
/// Represents the following C struct:
12-
/// struct OBX_id_array {
13-
/// obx_id* ids;
14-
/// size_t count;
15-
/// };
16-
class OBX_id_array extends Struct {
17-
Pointer<Uint64> _itemsPtr;
18-
19-
@IntPtr() // size_t
20-
int length;
21-
22-
/// Get a copy of the list
23-
List<int> items() => _itemsPtr.asTypedList(length).toList();
24-
25-
/// Execute the given function, managing the resources consistently
26-
static R executeWith<R>(
27-
List<int> items, R Function(Pointer<OBX_id_array>) fn) {
28-
// allocate a temporary structure
29-
final ptr = allocate<OBX_id_array>();
30-
31-
// fill it with data
32-
final array = ptr.ref;
33-
array.length = items.length;
34-
array._itemsPtr = allocate<Uint64>(count: array.length);
35-
for (var i = 0; i < items.length; ++i) {
36-
array._itemsPtr[i] = items[i];
37-
}
10+
/// Execute the given function, managing the resources consistently
11+
R executeWithIdArray<R>(List<int> items, R Function(Pointer<OBX_id_array>) fn) {
12+
// allocate a temporary structure
13+
final ptr = allocate<OBX_id_array>();
14+
15+
// fill it with data
16+
final array = ptr.ref;
17+
array.count = items.length;
18+
array.ids = allocate<Uint64>(count: items.length);
19+
for (var i = 0; i < items.length; ++i) {
20+
array.ids[i] = items[i];
21+
}
3822

39-
// call the function with the structure and free afterwards
40-
try {
41-
return fn(ptr);
42-
} finally {
43-
free(array._itemsPtr);
44-
free(ptr);
45-
}
23+
// call the function with the structure and free afterwards
24+
try {
25+
return fn(ptr);
26+
} finally {
27+
free(array.ids);
28+
free(ptr);
4629
}
4730
}
4831

49-
/// Represents the following C struct:
50-
/// struct OBX_bytes {
51-
/// const void* data;
52-
/// size_t size;
53-
/// };
54-
class OBX_bytes extends Struct {
55-
Pointer<Uint8> _dataPtr;
32+
class OBX_bytes_wrapper {
33+
Pointer<OBX_bytes> _cBytes;
34+
35+
OBX_bytes_wrapper(this._cBytes);
5636

57-
@IntPtr() // size_t
58-
int length;
37+
int get size => _cBytes == nullptr ? 0 : _cBytes.ref.size;
38+
39+
Uint8List get data => safeDataAccess(_cBytes);
5940

6041
/// Get access to the data (no-copy)
61-
Uint8List get data => isEmpty
62-
? throw ObjectBoxException(
63-
dartMsg: "can't access data of empty OBX_bytes")
64-
: _dataPtr.asTypedList(length);
42+
static Uint8List safeDataAccess(Pointer<OBX_bytes> cBytes) =>
43+
cBytes.address == 0 || cBytes.ref.size == 0
44+
? throw ObjectBoxException(
45+
dartMsg: "can't access data of empty OBX_bytes")
46+
: cBytes.ref.data.cast<Uint8>().asTypedList(cBytes.ref.size);
6547

66-
bool get isEmpty => length == 0 || _dataPtr.address == 0;
48+
bool get isEmpty => size == 0;
6749

68-
Pointer<Uint8> get ptr => _dataPtr;
50+
Pointer<Void> get ptr => _cBytes.ref.data;
6951

7052
/// Returns a pointer to OBX_bytes with copy of the passed data.
7153
/// Warning: this creates two unmanaged pointers which must be freed manually: OBX_bytes.freeManaged(result).
72-
static Pointer<OBX_bytes> managedCopyOf(Uint8List data) {
73-
final ptr = allocate<OBX_bytes>();
74-
final bytes = ptr.ref;
54+
OBX_bytes_wrapper.managedCopyOf(Uint8List data) {
55+
_cBytes = allocate<OBX_bytes>();
56+
final bytes = _cBytes.ref;
7557

76-
const align =
77-
true; // ObjectBox requires data to be aligned to the length of 4
78-
bytes.length = align ? ((data.length + 3.0) ~/ 4.0) * 4 : data.length;
58+
// ObjectBox requires data to be aligned to the length of 4
59+
const align = true;
60+
bytes.size = align ? ((data.length + 3.0) ~/ 4.0) * 4 : data.length;
7961

8062
// NOTE: currently there's no way to get access to the underlying memory of Uint8List to avoid a copy.
8163
// See https://github.com/dart-lang/ffi/issues/27
@@ -85,107 +67,29 @@ class OBX_bytes extends Struct {
8567
// }
8668

8769
// create a copy of the data
88-
bytes._dataPtr = allocate<Uint8>(count: bytes.length);
70+
bytes.data = allocate<Uint8>(count: bytes.size).cast<Void>();
8971
for (var i = 0; i < data.length; ++i) {
90-
bytes._dataPtr[i] = data[i];
72+
bytes.data.cast<Uint8>()[i] = data[i];
9173
}
92-
93-
return ptr;
9474
}
9575

9676
/// Free a dart-created OBX_bytes pointer.
97-
static void freeManaged(Pointer<OBX_bytes> ptr) {
98-
free(ptr.ref._dataPtr);
99-
free(ptr);
100-
}
101-
}
102-
103-
/// Represents the following C struct:
104-
/// struct OBX_bytes_array {
105-
/// OBX_bytes* bytes;
106-
/// size_t count;
107-
/// };
108-
class OBX_bytes_array extends Struct {
109-
Pointer<OBX_bytes> _items;
110-
111-
@IntPtr() // size_t
112-
int length;
113-
114-
/// Get a list of the underlying OBX_bytes (a shallow copy).
115-
List<OBX_bytes> items() {
116-
final result = <OBX_bytes>[];
117-
for (var i = 0; i < length; i++) {
118-
result.add(_items.elementAt(i).ref);
119-
}
120-
return result;
77+
void freeManaged() {
78+
free(_cBytes.ref.data);
79+
free(_cBytes);
12180
}
12281
}
12382

124-
class OBX_int8_array extends Struct {
125-
Pointer<Int8> _itemsPtr;
126-
127-
@IntPtr() // size_t
128-
int count;
129-
130-
List<int> items() => _itemsPtr.asTypedList(count).toList();
131-
}
132-
133-
class OBX_int16_array extends Struct {
134-
Pointer<Int16> _itemsPtr;
135-
136-
@IntPtr() // size_t
137-
int count;
138-
139-
List<int> items() => _itemsPtr.asTypedList(count).toList();
140-
}
141-
142-
class OBX_int32_array extends Struct {
143-
Pointer<Int32> _itemsPtr;
144-
145-
@IntPtr() // size_t
146-
int count;
147-
148-
List<int> items() => _itemsPtr.asTypedList(count).toList();
149-
}
150-
151-
class OBX_int64_array extends Struct {
152-
Pointer<Int64> _itemsPtr;
153-
154-
@IntPtr() // size_t
155-
int count;
156-
157-
List<int> items() => _itemsPtr.asTypedList(count).toList();
158-
}
159-
160-
class OBX_string_array extends Struct {
161-
Pointer<Pointer<Uint8>> _itemsPtr;
83+
class OBX_string_array_wrapper {
84+
Pointer<OBX_string_array> _cPtr;
16285

163-
@IntPtr() // size_t
164-
int count;
86+
OBX_string_array_wrapper(this._cPtr);
16587

16688
List<String> items() {
16789
final list = <String>[];
168-
for (var i = 0; i < count; i++) {
169-
list.add(Utf8.fromUtf8(_itemsPtr.elementAt(i).value.cast<Utf8>()));
90+
for (var i = 0; i < _cPtr.ref.count; i++) {
91+
list.add(Utf8.fromUtf8(_cPtr.ref.items.elementAt(i).value.cast<Utf8>()));
17092
}
17193
return list;
17294
}
17395
}
174-
175-
class OBX_float_array extends Struct {
176-
Pointer<Float> _itemsPtr;
177-
178-
@IntPtr() // size_t
179-
int count;
180-
181-
List<double> items() => _itemsPtr.asTypedList(count).toList();
182-
}
183-
184-
class OBX_double_array extends Struct {
185-
Pointer<Double> _itemsPtr;
186-
187-
@IntPtr() // size_t
188-
int count;
189-
190-
List<double> items() => _itemsPtr.asTypedList(count).toList();
191-
}

lib/src/box.dart

Lines changed: 12 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -64,15 +64,13 @@ class Box<T> {
6464
}
6565

6666
// put object into box and free the buffer
67-
// ignore: omit_local_variable_types
68-
final Pointer<OBX_bytes> bytesPtr = _fbManager.marshal(propVals);
67+
final bytes = _fbManager.marshal(propVals);
6968
try {
70-
final bytes = bytesPtr.ref;
7169
checkObx(bindings.obx_box_put5(
72-
_cBox, id, bytes.ptr, bytes.length, _getOBXPutMode(mode)));
70+
_cBox, id, bytes.ptr, bytes.size, _getOBXPutMode(mode)));
7371
} finally {
7472
// because fbManager.marshal() allocates the inner bytes, we need to clean those as well
75-
OBX_bytes.freeManaged(bytesPtr);
73+
bytes.freeManaged();
7674
}
7775
return id;
7876
}
@@ -123,21 +121,19 @@ class Box<T> {
123121
final bytesArrayPtr = checkObxPtr(
124122
bindings.obx_bytes_array(allPropVals.length),
125123
'could not create OBX_bytes_array');
126-
final listToFree = <Pointer<OBX_bytes>>[];
124+
final listToFree = <OBX_bytes_wrapper>[];
127125
try {
128126
for (var i = 0; i < allPropVals.length; i++) {
129-
final bytesPtr = _fbManager.marshal(allPropVals[i]);
130-
listToFree.add(bytesPtr);
131-
final bytes = bytesPtr.ref;
132-
bindings.obx_bytes_array_set(
133-
bytesArrayPtr, i, bytes.ptr, bytes.length);
127+
final bytes = _fbManager.marshal(allPropVals[i]);
128+
listToFree.add(bytes);
129+
bindings.obx_bytes_array_set(bytesArrayPtr, i, bytes.ptr, bytes.size);
134130
}
135131

136132
checkObx(bindings.obx_box_put_many(
137133
_cBox, bytesArrayPtr, allIdsMemory, _getOBXPutMode(mode)));
138134
} finally {
139135
bindings.obx_bytes_array_free(bytesArrayPtr);
140-
listToFree.forEach(OBX_bytes.freeManaged);
136+
listToFree.forEach((OBX_bytes_wrapper bytes) => bytes.freeManaged());
141137
}
142138
} finally {
143139
free(allIdsMemory);
@@ -164,7 +160,7 @@ class Box<T> {
164160
checkObx(err);
165161

166162
// ignore: omit_local_variable_types
167-
Pointer<Uint8> dataPtr = dataPtrPtr.value;
163+
Pointer<Uint8> dataPtr = dataPtrPtr.value.cast<Uint8>();
168164
final size = sizePtr.value;
169165

170166
// create a no-copy view
@@ -223,7 +219,7 @@ class Box<T> {
223219
if (ids.isEmpty) return [];
224220

225221
const allowMissing = true; // result includes null if an object is missing
226-
return OBX_id_array.executeWith(
222+
return executeWithIdArray(
227223
ids,
228224
(ptr) => _getMany(
229225
allowMissing,
@@ -287,7 +283,7 @@ class Box<T> {
287283
bool containsMany(List<int> ids) {
288284
final contains = allocate<Int32>();
289285
try {
290-
return OBX_id_array.executeWith(ids, (ptr) {
286+
return executeWithIdArray(ids, (ptr) {
291287
checkObx(bindings.obx_box_contains_many(_cBox, ptr, contains));
292288
return contains.value > 0 ? true : false;
293289
});
@@ -309,7 +305,7 @@ class Box<T> {
309305
int removeMany(List<int> ids) {
310306
final removedIds = allocate<Uint64>();
311307
try {
312-
return OBX_id_array.executeWith(ids, (ptr) {
308+
return executeWithIdArray(ids, (ptr) {
313309
checkObx(bindings.obx_box_remove_many(_cBox, ptr, removedIds));
314310
return removedIds.value;
315311
});

lib/src/query/property.dart

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -249,20 +249,14 @@ class StringPropertyQuery extends PropertyQuery<String> {
249249
_cProp, d ? 1 : 0, _caseSensitive ? 1 : 0));
250250
}
251251

252-
List<String> _unpack(Pointer<OBX_string_array> ptr) {
253-
try {
254-
return ptr.ref.items();
255-
} finally {
256-
bindings.obx_string_array_free(ptr);
257-
}
258-
}
259-
260252
@override
261253
List<String> find({String replaceNullWith}) {
262254
final ptr = replaceNullWith != null
263255
? Utf8.toUtf8(replaceNullWith).cast<Int8>()
264256
: Pointer<Int8>.fromAddress(0);
265-
return _unpack(_curryWithDefault<OBX_string_array, Int8>(
266-
bindings.obx_query_prop_find_strings, ptr, 'find utf8'));
257+
final stringArray = OBX_string_array_wrapper(
258+
_curryWithDefault<OBX_string_array, Int8>(
259+
bindings.obx_query_prop_find_strings, ptr, 'find utf8'));
260+
return stringArray.items();
267261
}
268262
}

0 commit comments

Comments
 (0)