5
5
// Explicit pool used for managing resources.
6
6
7
7
import "dart:async" ;
8
- import 'dart:convert' ;
9
8
import 'dart:ffi' ;
10
- import 'dart:typed_data' ;
11
9
12
- import 'package:ffi/ffi.dart' as packageFfi;
13
- import 'package:ffi/ffi.dart' show Utf8;
10
+ import 'package:ffi/ffi.dart' ;
14
11
15
- /// Manages native resources.
12
+ import '../calloc.dart' ;
13
+
14
+ /// Keeps track of all allocated memory and frees all allocated memory on
15
+ /// [releaseAll] .
16
16
///
17
- /// Primary implementations are [Pool] and [Unmanaged] .
18
- abstract class ResourceManager {
19
- /// Allocates memory on the native heap.
20
- ///
21
- /// The native memory is under management by this [ResourceManager] .
22
- ///
23
- /// For POSIX-based systems, this uses malloc. On Windows, it uses HeapAlloc
24
- /// against the default public heap. Allocation of either element size or count
25
- /// of 0 is undefined.
26
- ///
27
- /// Throws an ArgumentError on failure to allocate.
28
- Pointer <T > allocate <T extends NativeType >({int count: 1 });
29
- }
17
+ /// Wraps an [Allocator] to do the actual allocation and freeing.
18
+ class Pool implements Allocator {
19
+ /// The [Allocator] used for allocation and freeing.
20
+ final Allocator _wrappedAllocator;
21
+
22
+ Pool (this ._wrappedAllocator);
30
23
31
- /// Manages native resources.
32
- class Pool implements ResourceManager {
33
24
/// Native memory under management by this [Pool] .
34
25
final List <Pointer <NativeType >> _managedMemoryPointers = [];
35
26
36
27
/// Callbacks for releasing native resources under management by this [Pool] .
37
28
final List <Function ()> _managedResourceReleaseCallbacks = [];
38
29
39
- /// Allocates memory on the native heap.
40
- ///
41
- /// The native memory is under management by this [Pool] .
42
- ///
43
- /// For POSIX-based systems, this uses malloc. On Windows, it uses HeapAlloc
44
- /// against the default public heap. Allocation of either element size or count
45
- /// of 0 is undefined.
30
+ /// Allocates memory on the native heap by using the allocator supplied to
31
+ /// the constructor.
46
32
///
47
- /// Throws an ArgumentError on failure to allocate.
48
- Pointer <T > allocate <T extends NativeType >({int count: 1 }) {
49
- final p = Unmanaged ().allocate <T >(count: count);
33
+ /// Throws an [ArgumentError] if the number of bytes or alignment cannot be
34
+ /// satisfied.
35
+ @override
36
+ Pointer <T > allocate <T extends NativeType >(int numBytes, {int ? alignment}) {
37
+ final p = _wrappedAllocator.allocate <T >(numBytes, alignment: alignment);
50
38
_managedMemoryPointers.add (p);
51
39
return p;
52
40
}
@@ -71,17 +59,21 @@ class Pool implements ResourceManager {
71
59
}
72
60
_managedResourceReleaseCallbacks.clear ();
73
61
for (final p in _managedMemoryPointers) {
74
- Unmanaged () .free (p);
62
+ _wrappedAllocator .free (p);
75
63
}
76
64
_managedMemoryPointers.clear ();
77
65
}
66
+
67
+ @override
68
+ void free (Pointer <NativeType > pointer) => throw UnsupportedError (
69
+ "Individually freeing Pool allocated memory is not allowed" );
78
70
}
79
71
80
72
/// Creates a [Pool] to manage native resources.
81
73
///
82
74
/// If the isolate is shut down, through `Isolate.kill()` , resources are _not_ cleaned up.
83
- R using <R >(R Function (Pool ) f) {
84
- final p = Pool ();
75
+ R using <R >(R Function (Pool ) f, [ Allocator wrappedAllocator = calloc] ) {
76
+ final p = Pool (wrappedAllocator );
85
77
try {
86
78
return f (p);
87
79
} finally {
@@ -96,8 +88,8 @@ R using<R>(R Function(Pool) f) {
96
88
/// Please note that all throws are caught and packaged in [RethrownError] .
97
89
///
98
90
/// If the isolate is shut down, through `Isolate.kill()` , resources are _not_ cleaned up.
99
- R usePool <R >(R Function () f) {
100
- final p = Pool ();
91
+ R usePool <R >(R Function () f, [ Allocator wrappedAllocator = calloc] ) {
92
+ final p = Pool (wrappedAllocator );
101
93
try {
102
94
return runZoned (() => f (),
103
95
zoneValues: {#_pool: p},
@@ -117,78 +109,3 @@ class RethrownError {
117
109
toString () => """RethrownError(${original })
118
110
${originalStackTrace }""" ;
119
111
}
120
-
121
- /// Does not manage it's resources.
122
- class Unmanaged implements ResourceManager {
123
- /// Allocates memory on the native heap.
124
- ///
125
- /// For POSIX-based systems, this uses malloc. On Windows, it uses HeapAlloc
126
- /// against the default public heap. Allocation of either element size or count
127
- /// of 0 is undefined.
128
- ///
129
- /// Throws an ArgumentError on failure to allocate.
130
- Pointer <T > allocate <T extends NativeType >({int count = 1 }) =>
131
- packageFfi.allocate (count: count);
132
-
133
- /// Releases memory on the native heap.
134
- ///
135
- /// For POSIX-based systems, this uses free. On Windows, it uses HeapFree
136
- /// against the default public heap. It may only be used against pointers
137
- /// allocated in a manner equivalent to [allocate] .
138
- ///
139
- /// Throws an ArgumentError on failure to free.
140
- ///
141
- void free (Pointer pointer) => packageFfi.free (pointer);
142
- }
143
-
144
- /// Does not manage it's resources.
145
- final Unmanaged unmanaged = Unmanaged ();
146
-
147
- extension Utf8InPool on String {
148
- /// Convert a [String] to a Utf8-encoded null-terminated C string.
149
- ///
150
- /// If 'string' contains NULL bytes, the converted string will be truncated
151
- /// prematurely. Unpaired surrogate code points in [string] will be preserved
152
- /// in the UTF-8 encoded result. See [Utf8Encoder] for details on encoding.
153
- ///
154
- /// Returns a malloc-allocated pointer to the result.
155
- ///
156
- /// The memory is managed by the [Pool] passed in as [pool] .
157
- Pointer <Utf8 > toUtf8 (ResourceManager pool) {
158
- final units = utf8.encode (this );
159
- final Pointer <Uint8 > result = pool.allocate <Uint8 >(count: units.length + 1 );
160
- final Uint8List nativeString = result.asTypedList (units.length + 1 );
161
- nativeString.setAll (0 , units);
162
- nativeString[units.length] = 0 ;
163
- return result.cast ();
164
- }
165
- }
166
-
167
- extension Utf8Helpers on Pointer <Utf8 > {
168
- /// Returns the length of a null-terminated string -- the number of (one-byte)
169
- /// characters before the first null byte.
170
- int strlen () {
171
- final Pointer <Uint8 > array = this .cast <Uint8 >();
172
- final Uint8List nativeString = array.asTypedList (_maxSize);
173
- return nativeString.indexWhere ((char) => char == 0 );
174
- }
175
-
176
- /// Creates a [String] containing the characters UTF-8 encoded in [this] .
177
- ///
178
- /// [this] must be a zero-terminated byte sequence of valid UTF-8
179
- /// encodings of Unicode code points. It may also contain UTF-8 encodings of
180
- /// unpaired surrogate code points, which is not otherwise valid UTF-8, but
181
- /// which may be created when encoding a Dart string containing an unpaired
182
- /// surrogate. See [Utf8Decoder] for details on decoding.
183
- ///
184
- /// Returns a Dart string containing the decoded code points.
185
- String contents () {
186
- final int length = strlen ();
187
- return utf8.decode (Uint8List .view (
188
- this .cast <Uint8 >().asTypedList (length).buffer, 0 , length));
189
- }
190
- }
191
-
192
- const int _kMaxSmi64 = (1 << 62 ) - 1 ;
193
- const int _kMaxSmi32 = (1 << 30 ) - 1 ;
194
- final int _maxSize = sizeOf <IntPtr >() == 8 ? _kMaxSmi64 : _kMaxSmi32;
0 commit comments