3
3
extern crate alloc;
4
4
use {
5
5
:: alloc:: collections:: { BTreeMap , BTreeSet } ,
6
- :: core:: { cmp:: Ordering , fmt} ,
6
+ :: core:: { cmp:: Ordering , fmt, num :: NonZero } ,
7
7
} ;
8
8
9
9
type Size = u32 ;
@@ -15,7 +15,7 @@ pub struct Allocation {
15
15
/// The location of this allocation within the buffer
16
16
pub offset : Location ,
17
17
/// The size of this allocation
18
- pub size : Size ,
18
+ pub size : NonZero < Size > ,
19
19
}
20
20
21
21
/// A super simple fast soft-realtime allocator for managing an external pool
@@ -40,9 +40,9 @@ pub struct OrderlyAllocator {
40
40
/// location
41
41
free : BTreeSet < FreeRegion > ,
42
42
/// An ordered collection of free-regions, sorted by location
43
- location_map : BTreeMap < Location , Size > ,
43
+ location_map : BTreeMap < Location , NonZero < Size > > ,
44
44
/// The total capacity
45
- capacity : Size ,
45
+ capacity : NonZero < Size > ,
46
46
/// The amount of free memory
47
47
available : Size ,
48
48
}
@@ -51,7 +51,7 @@ pub struct OrderlyAllocator {
51
51
#[ derive( PartialEq , Eq , Copy , Clone , Debug ) ]
52
52
struct FreeRegion {
53
53
location : Location ,
54
- size : Size ,
54
+ size : NonZero < Size > ,
55
55
}
56
56
57
57
impl PartialOrd for FreeRegion {
@@ -76,12 +76,17 @@ impl Ord for FreeRegion {
76
76
77
77
impl OrderlyAllocator {
78
78
/// Create a new allocator to manage a pool of memory
79
+ ///
80
+ /// Panics:
81
+ /// - Panics if `capacity == 0`
79
82
pub fn new ( capacity : Size ) -> Self {
83
+ let capacity = NonZero :: new ( capacity) . expect ( "`capacity == 0`" ) ;
84
+
80
85
let mut allocator = OrderlyAllocator {
81
86
free : BTreeSet :: new ( ) ,
82
87
location_map : BTreeMap :: new ( ) ,
83
88
capacity,
84
- available : capacity,
89
+ available : capacity. get ( ) ,
85
90
} ;
86
91
87
92
allocator. reset ( ) ;
@@ -94,9 +99,9 @@ impl OrderlyAllocator {
94
99
/// Uses a *best-fit* strategy, and returns [`Allocation`]s with arbitrary
95
100
/// alignment.
96
101
///
97
- /// # Panics
98
- ///
99
- /// - Panics if `size == 0` .
102
+ /// Returns `None` if:
103
+ /// - `size == 0`, or
104
+ /// - `size + 1` overflows .
100
105
pub fn alloc ( & mut self , size : Size ) -> Option < Allocation > {
101
106
self . alloc_with_align ( size, 1 )
102
107
}
@@ -110,42 +115,40 @@ impl OrderlyAllocator {
110
115
/// This is more prone to causing fragmentation compared to an unaligned
111
116
/// [`alloc`](Self::alloc).
112
117
///
113
- /// # Panics
114
- ///
115
- /// - Panics if `size == 0`.
116
- /// - Panics if `align == 0`.
118
+ /// Returns `None` if:
119
+ /// - there are no free-regions with `size + align - 1` available space, or
120
+ /// - `size == 0`, or
121
+ /// - `align == 0`, or
122
+ /// - `size + align` overflows.
117
123
pub fn alloc_with_align (
118
124
& mut self ,
119
125
size : Size ,
120
126
align : Size ,
121
127
) -> Option < Allocation > {
122
- assert ! (
123
- size > 0 && align > 0 ,
124
- "`size` & `align` must be greater than zero: size={size}, align={align}"
125
- ) ;
128
+ let size = NonZero :: new ( size) ?;
129
+ let align = NonZero :: new ( align) ?;
126
130
127
131
let FreeRegion {
128
132
location : mut free_region_location,
129
- size : mut free_region_size,
130
- } = self . find_free_region ( size + align - 1 ) ?;
133
+ size : free_region_size,
134
+ } = self . find_free_region ( size. checked_add ( align. get ( ) - 1 ) ? ) ?;
131
135
132
136
self . remove_free_region ( free_region_location, free_region_size) ;
133
137
134
- let misalignment = free_region_location % align;
135
- if misalignment > 0 {
138
+ let mut free_region_size: u32 = free_region_size. get ( ) ;
139
+
140
+ if let Some ( misalignment) = NonZero :: new ( free_region_location % align) {
136
141
self . insert_free_region ( free_region_location, misalignment) ;
137
- free_region_location += misalignment;
138
- free_region_size -= misalignment;
142
+ free_region_location += misalignment. get ( ) ;
143
+ free_region_size -= misalignment. get ( ) ;
139
144
}
140
145
141
- if size < free_region_size {
142
- self . insert_free_region (
143
- free_region_location + size,
144
- free_region_size - size,
145
- ) ;
146
+ if let Some ( size_leftover) = NonZero :: new ( free_region_size - size. get ( ) ) {
147
+ self
148
+ . insert_free_region ( free_region_location + size. get ( ) , size_leftover) ;
146
149
}
147
150
148
- self . available -= size;
151
+ self . available -= size. get ( ) ;
149
152
150
153
Some ( Allocation {
151
154
size,
@@ -172,38 +175,44 @@ impl OrderlyAllocator {
172
175
if let Some ( FreeRegion { location, size } ) =
173
176
self . previous_free_region ( alloc. offset )
174
177
{
175
- if location + size == free_region. location {
178
+ if location + size. get ( ) == free_region. location {
176
179
self . remove_free_region ( location, size) ;
177
180
free_region. location = location;
178
- free_region. size += size;
181
+ // note: this unwrap is ok because the sum of all free-regions cannot
182
+ // be larger than the total size of the allocator; which we know is
183
+ // some `Size`.
184
+ free_region. size = free_region. size . checked_add ( size. get ( ) ) . unwrap ( ) ;
179
185
}
180
186
} ;
181
187
182
188
if let Some ( FreeRegion { location, size } ) =
183
189
self . following_free_region ( alloc. offset )
184
190
{
185
- if free_region. location + free_region. size == location {
191
+ if free_region. location + free_region. size . get ( ) == location {
186
192
self . remove_free_region ( location, size) ;
187
- free_region. size += size;
193
+ // note: this unwrap is ok because the sum of all free-regions cannot
194
+ // be larger than the total size of the allocator; which we know is
195
+ // some `Size`.
196
+ free_region. size = free_region. size . checked_add ( size. get ( ) ) . unwrap ( ) ;
188
197
}
189
198
}
190
199
}
191
200
192
201
self . insert_free_region ( free_region. location , free_region. size ) ;
193
- self . available += alloc. size ;
202
+ self . available += alloc. size . get ( ) ;
194
203
}
195
204
196
205
/// Free ***all*** allocations
197
206
pub fn reset ( & mut self ) {
198
207
self . free . clear ( ) ;
199
208
self . location_map . clear ( ) ;
200
- self . available = self . capacity ;
209
+ self . available = self . capacity . get ( ) ;
201
210
self . insert_free_region ( 0 , self . capacity ) ;
202
211
}
203
212
204
213
/// Get the total capacity of the pool
205
214
pub fn capacity ( & self ) -> Size {
206
- self . capacity
215
+ self . capacity . get ( )
207
216
}
208
217
209
218
/// Get the total available memory in this pool
@@ -216,16 +225,16 @@ impl OrderlyAllocator {
216
225
217
226
/// Get the size of the largest available memory region in this pool
218
227
pub fn largest_available ( & self ) -> Size {
219
- self . free . last ( ) . map_or ( 0 , |region| region. size )
228
+ self . free . last ( ) . map_or ( 0 , |region| region. size . get ( ) )
220
229
}
221
230
222
231
/// Returns true if there are no allocations
223
232
pub fn is_empty ( & self ) -> bool {
224
- self . capacity == self . available
233
+ self . capacity . get ( ) == self . available
225
234
}
226
235
227
236
/// Try to find a region with at least `size`
228
- fn find_free_region ( & mut self , size : Size ) -> Option < FreeRegion > {
237
+ fn find_free_region ( & mut self , size : NonZero < Size > ) -> Option < FreeRegion > {
229
238
self
230
239
. free
231
240
. range ( FreeRegion { size, location : 0 } ..)
@@ -253,7 +262,7 @@ impl OrderlyAllocator {
253
262
}
254
263
255
264
/// remove a region from the internal free lists
256
- fn remove_free_region ( & mut self , location : Location , size : Size ) {
265
+ fn remove_free_region ( & mut self , location : Location , size : NonZero < Size > ) {
257
266
self . location_map . remove ( & location) ;
258
267
let region_existed = self . free . remove ( & FreeRegion { location, size } ) ;
259
268
@@ -265,7 +274,7 @@ impl OrderlyAllocator {
265
274
}
266
275
267
276
/// add a region to the internal free lists
268
- fn insert_free_region ( & mut self , location : Location , size : Size ) {
277
+ fn insert_free_region ( & mut self , location : Location , size : NonZero < Size > ) {
269
278
self . free . insert ( FreeRegion { location, size } ) ;
270
279
let existing_size = self . location_map . insert ( location, size) ;
271
280
@@ -275,7 +284,7 @@ impl OrderlyAllocator {
275
284
new = FreeRegion { location, size } ,
276
285
existing = FreeRegion {
277
286
location,
278
- size: existing_size. unwrap ( )
287
+ size: existing_size. unwrap_or_else ( || unreachable! ( ) )
279
288
}
280
289
)
281
290
}
0 commit comments