1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594
// Copyright 2018-2021 Parity Technologies (UK) Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! A storage stash allowing to store indexed elements efficiently.
mod impls;
mod iter;
mod storage;
#[cfg(test)]
mod tests;
use self::iter::Entries;
pub use self::iter::{
Iter,
IterMut,
};
use crate::{
lazy::LazyIndexMap,
traits::PackedLayout,
Pack,
};
use ink_primitives::Key;
/// An index into the stash.
type Index = u32;
/// A stash data structure operating on contract storage.
///
/// This allows to store information similar to a vector but in unordered
/// fashion which enables constant time random deletion of elements. This allows
/// for efficient attachment of data to some numeric indices.
#[derive(Debug)]
pub struct Stash<T>
where
T: PackedLayout,
{
/// The combined and commonly used header data.
header: Pack<Header>,
/// The storage entries of the stash.
entries: LazyIndexMap<Entry<T>>,
}
/// Stores general commonly required information about the storage stash.
#[derive(Debug, Default, scale::Encode, scale::Decode)]
#[cfg_attr(feature = "std", derive(scale_info::TypeInfo))]
struct Header {
/// The latest vacant index.
///
/// - If all entries are occupied:
/// - Points to the entry at index `self.len`.
/// - If some entries are vacant:
/// - Points to the entry that has been vacated most recently.
last_vacant: Index,
/// The number of items stored in the stash.
///
/// # Note
///
/// We cannot simply use the underlying length of the vector
/// since it would include vacant slots as well.
len: u32,
/// The number of entries currently managed by the stash.
len_entries: u32,
}
/// A vacant entry with previous and next vacant indices.
#[derive(Debug, Copy, Clone, scale::Encode, scale::Decode)]
#[cfg_attr(feature = "std", derive(scale_info::TypeInfo))]
pub struct VacantEntry {
/// The next vacant index.
next: Index,
/// The previous vacant index.
prev: Index,
}
/// An entry within the stash.
///
/// The vacant entries within a storage stash form a doubly linked list of
/// vacant entries that is used to quickly re-use their vacant storage.
#[derive(Debug, scale::Encode, scale::Decode)]
#[cfg_attr(feature = "std", derive(scale_info::TypeInfo))]
pub enum Entry<T> {
/// A vacant entry that holds the index to the next and previous vacant entry.
Vacant(VacantEntry),
/// An occupied entry that hold the value.
Occupied(T),
}
impl<T> Entry<T> {
/// Returns `true` if the entry is occupied.
pub fn is_occupied(&self) -> bool {
if let Entry::Occupied(_) = self {
return true
}
false
}
/// Returns `true` if the entry is vacant.
pub fn is_vacant(&self) -> bool {
!self.is_occupied()
}
/// Returns the vacant entry if the entry is vacant, otherwise returns `None`.
fn try_to_vacant(&self) -> Option<VacantEntry> {
match self {
Entry::Occupied(_) => None,
Entry::Vacant(vacant_entry) => Some(*vacant_entry),
}
}
/// Returns the vacant entry if the entry is vacant, otherwise returns `None`.
fn try_to_vacant_mut(&mut self) -> Option<&mut VacantEntry> {
match self {
Entry::Occupied(_) => None,
Entry::Vacant(vacant_entry) => Some(vacant_entry),
}
}
}
impl<T> Stash<T>
where
T: PackedLayout,
{
/// Creates a new empty stash.
pub fn new() -> Self {
Self {
header: Pack::new(Header {
last_vacant: 0,
len: 0,
len_entries: 0,
}),
entries: LazyIndexMap::new(),
}
}
/// Returns the number of elements stored in the stash.
pub fn len(&self) -> u32 {
self.header.len
}
/// Returns `true` if the stash contains no elements.
pub fn is_empty(&self) -> bool {
self.len() == 0
}
/// Returns the number of entries the stash can hold without
/// allocating another storage cell.
///
/// # Note
///
/// This is the total number of occupied and vacant entries of the stash.
pub fn capacity(&self) -> u32 {
self.len_entries()
}
/// Returns the number of entries currently managed by the storage stash.
fn len_entries(&self) -> u32 {
self.header.len_entries
}
/// Returns the underlying key to the cells.
///
/// # Note
///
/// This is a low-level utility getter and should
/// normally not be required by users.
pub fn entries_key(&self) -> Option<&Key> {
self.entries.key()
}
/// Returns an iterator yielding shared references to all elements of the stash.
///
/// # Note
///
/// Avoid unbounded iteration over big storage stashes.
/// Prefer using methods like `Iterator::take` in order to limit the number
/// of yielded elements.
pub fn iter(&self) -> Iter<T> {
Iter::new(self)
}
/// Returns an iterator yielding exclusive references to all elements of the stash.
///
/// # Note
///
/// Avoid unbounded iteration over big storage stashes.
/// Prefer using methods like `Iterator::take` in order to limit the number
/// of yielded elements.
pub fn iter_mut(&mut self) -> IterMut<T> {
IterMut::new(self)
}
/// Returns an iterator yielding shared references to all entries of the stash.
pub fn entries(&self) -> Entries<T> {
Entries::new(self)
}
/// Returns `true` if the storage stash has vacant entries.
fn has_vacant_entries(&self) -> bool {
self.header.len != self.header.len_entries
}
/// Returns the index of the last vacant entry if any.
fn last_vacant_index(&self) -> Option<Index> {
if self.has_vacant_entries() {
Some(self.header.last_vacant)
} else {
None
}
}
}
impl<T> Stash<T>
where
T: PackedLayout,
{
/// Returns a shared reference to the element at the given index.
pub fn get(&self, at: Index) -> Option<&T> {
if at >= self.len_entries() {
// Bail out early if the index is out of bounds.
return None
}
self.entries.get(at).and_then(|entry| {
match entry {
Entry::Occupied(val) => Some(val),
Entry::Vacant { .. } => None,
}
})
}
/// Returns an exclusive reference to the element at the given index.
pub fn get_mut(&mut self, at: Index) -> Option<&mut T> {
if at >= self.len_entries() {
// Bail out early if the index is out of bounds.
return None
}
self.entries.get_mut(at).and_then(|entry| {
match entry {
Entry::Occupied(val) => Some(val),
Entry::Vacant { .. } => None,
}
})
}
}
impl<T> Stash<T>
where
T: PackedLayout,
{
/// Clears the underlying storage cells of the storage vector.
///
/// # Note
///
/// This completely invalidates the storage vector's invariants about
/// the contents of its associated storage region.
///
/// This API is used for the `Drop` implementation of [`Vec`] as well as
/// for the [`SpreadLayout::clear_spread`][`crate::traits::SpreadLayout::clear_spread`]
/// trait implementation.
fn clear_cells(&self) {
if self.entries.key().is_none() {
// We won't clear any storage if we are in lazy state since there
// probably has not been any state written to storage, yet.
return
}
for index in 0..self.len_entries() {
// It might seem wasteful to clear all entries instead of just
// the occupied ones. However this spares us from having one extra
// read for every element in the storage stash to filter out vacant
// entries. So this is actually a trade-off and at the time of this
// implementation it is unclear which path is more efficient.
//
// The bet is that clearing a storage cell is cheaper than reading one.
self.entries.clear_packed_at(index);
}
}
}
impl<T> Stash<T>
where
T: PackedLayout,
{
/// Rebinds the `prev` and `next` bindings of the neighbors of the vacant entry.
///
/// # Note
///
/// The `removed_index` points to the index of the removed vacant entry.
fn remove_vacant_entry(&mut self, removed_index: Index, vacant_entry: VacantEntry) {
let prev_vacant = vacant_entry.prev;
let next_vacant = vacant_entry.next;
if prev_vacant == removed_index && next_vacant == removed_index {
// There is no other vacant entry left in the storage stash so
// there is nothing to update. Bail out early.
self.header.last_vacant = self.header.len;
return
}
let prev = self
.entries
.get_mut(prev_vacant)
.map(Entry::try_to_vacant_mut)
.flatten()
.expect("`prev` must point to an existing entry at this point");
if prev_vacant == next_vacant {
// There is only one other vacant entry left.
// We can update the single vacant entry in a single look-up.
debug_assert_eq!(prev.prev, removed_index);
debug_assert_eq!(prev.next, removed_index);
prev.prev = prev_vacant;
prev.next = prev_vacant;
} else {
// There are multiple other vacant entries left.
debug_assert_eq!(prev.next, removed_index);
prev.next = next_vacant;
let next = self
.entries
.get_mut(next_vacant)
.map(Entry::try_to_vacant_mut)
.flatten()
.expect("`next` must point to an existing entry at this point");
debug_assert_eq!(next.prev, removed_index);
next.prev = prev_vacant;
}
// Bind the last vacant pointer to the vacant position with the lower index.
// This has the effect that lower indices are refilled more quickly.
use core::cmp::min;
if removed_index == self.header.last_vacant {
self.header.last_vacant = min(prev_vacant, next_vacant);
}
}
/// Returns the previous and next vacant entry for the entry at index `at`.
///
/// If there exists a last vacant entry, the return value is a tuple
/// `(index_of_previous_vacant, index_of_next_vacant)`.
/// The two `index_` values hereby are selected in a way that makes it
/// more likely that the stash is refilled from low indices.
///
/// If no vacant entry exists a self-referential tuple of `(at, at)`
/// is returned.
fn fetch_prev_and_next_vacant_entry(&self, at: Index) -> (Index, Index) {
if let Some(index) = self.last_vacant_index() {
let root_vacant = self
.entries
.get(index)
.map(|entry| entry.try_to_vacant())
.flatten()
.expect("last_vacant must point to an existing vacant entry");
// Form the linked vacant entries in a way that makes it more likely
// for them to refill the stash from low indices.
if at < index {
// Insert before root if new vacant index is smaller than root.
(root_vacant.prev, index)
} else if at < root_vacant.next {
// Insert between root and its next vacant entry if smaller than
// current root's next index.
(index, root_vacant.next)
} else {
// Insert before root entry if index is greater. But we won't
// update the new element to be the new root index in this case.
(root_vacant.prev, index)
}
} else {
// Default previous and next to the given at index.
// So the resulting vacant index is pointing to itself.
(at, at)
}
}
/// Updates links from and to neighboring vacant entries.
fn update_neighboring_vacant_entry_links(
&mut self,
prev: Index,
next: Index,
at: Index,
) {
if prev == next {
// Previous and next are the same so we can update the vacant
// neighbour with a single look-up.
let entry = self
.entries
.get_mut(next)
.map(Entry::try_to_vacant_mut)
.flatten()
.expect("`next` must point to an existing vacant entry at this point");
entry.prev = at;
entry.next = at;
} else {
// Previous and next vacant entries are different and thus need
// different look-ups to update them.
self.entries
.get_mut(prev)
.map(Entry::try_to_vacant_mut)
.flatten()
.expect("`prev` must point to an existing vacant entry at this point")
.next = at;
self.entries
.get_mut(next)
.map(Entry::try_to_vacant_mut)
.flatten()
.expect("`next` must point to an existing vacant entry at this point")
.prev = at;
}
}
/// Put the element into the stash at the next vacant position.
///
/// Returns the stash index that the element was put into.
pub fn put(&mut self, new_value: T) -> Index {
let new_entry = Some(Entry::Occupied(new_value));
let new_index = if let Some(index) = self.last_vacant_index() {
// Put the new element to the most recent vacant index if not all entries are occupied.
let old_entry = self
.entries
.put_get(index, new_entry)
.expect("a `last_vacant_index()` must point to an occupied cell");
let vacant_entry = match old_entry {
Entry::Vacant(vacant_entry) => vacant_entry,
Entry::Occupied(_) => {
unreachable!("`last_vacant_index()` must point to a vacant entry")
}
};
self.remove_vacant_entry(index, vacant_entry);
index
} else {
// Push the new element to the end if all entries are occupied.
let new_index = self.header.len_entries;
self.entries.put(new_index, new_entry);
self.header.last_vacant += 1;
self.header.len_entries += 1;
new_index
};
self.header.len += 1;
new_index
}
/// Takes the element stored at the given index if any.
pub fn take(&mut self, at: Index) -> Option<T> {
// Cases:
// - There are vacant entries already.
// - There are no vacant entries before.
if at >= self.len_entries() {
// Early return since `at` index is out of bounds.
return None
}
// Precompute previous and next vacant entries as we might need them later.
// Due to borrow checker constraints we cannot have this at a later stage.
let (prev, next) = self.fetch_prev_and_next_vacant_entry(at);
let entry_mut = self.entries.get_mut(at).expect("index is out of bounds");
if entry_mut.is_vacant() {
// Early return if the taken entry is already vacant.
return None
}
// At this point we know that the entry is occupied with a value.
let new_vacant_entry = Entry::Vacant(VacantEntry { next, prev });
let taken_entry = core::mem::replace(entry_mut, new_vacant_entry);
self.update_neighboring_vacant_entry_links(prev, next, at);
// Take the value out of the taken occupied entry and return it.
match taken_entry {
Entry::Occupied(value) => {
use core::cmp::min;
self.header.last_vacant =
min(self.header.last_vacant, min(at, min(prev, next)));
self.header.len -= 1;
Some(value)
}
Entry::Vacant { .. } => {
unreachable!("the taken entry is known to be occupied")
}
}
}
/// Removes the element stored at the given index if any.
///
/// This method acts similar to the take API and even still returns an Option.
/// However, it guarantees to make no contract storage reads to the indexed
/// element and will only write to its internal low-level lazy cache that the
/// element at the given index is going to be removed at the end of the contract
/// execution.
///
/// Calling this method with an index out of bounds for the returns `None` and
/// does not `remove` the element, otherwise it returns `Some(())`.
///
/// # Safety
///
/// The caller must ensure that `at` refers to an occupied index. Behavior is
/// unspecified if `at` refers to a vacant index and could seriously damage the
/// contract storage integrity.
pub unsafe fn remove_occupied(&mut self, at: Index) -> Option<()> {
// This function is written similar to [`Stash::take`], with the exception
// that the caller has to ensure that `at` refers to an occupied entry whereby
// the procedure can avoid loading the occupied entry which might be handy if
// the stored `T` is especially costly to load from contract storage.
if at >= self.len_entries() {
// Early return since `at` index is out of bounds.
return None
}
// Precompute previous and next vacant entries as we might need them later.
// Due to borrow checker constraints we cannot have this at a later stage.
let (prev, next) = self.fetch_prev_and_next_vacant_entry(at);
let new_vacant_entry = Entry::Vacant(VacantEntry { next, prev });
self.entries.put(at, Some(new_vacant_entry));
self.update_neighboring_vacant_entry_links(prev, next, at);
use core::cmp::min;
self.header.last_vacant = min(self.header.last_vacant, min(at, min(prev, next)));
self.header.len -= 1;
Some(())
}
/// Defragments the underlying storage to minimize footprint.
///
/// Returns the number of storage cells freed this way.
///
/// This might invalidate indices stored outside the stash.
///
/// # Callback
///
/// In order to keep those indices up-to-date the caller can provide
/// a callback function that is called for every moved entry
/// with a shared reference to the entries value and the old as well
/// as the new index.
///
/// # Note
///
/// - If `max_iterations` is `Some` concrete value it is used in order to
/// bound the number of iterations and won't try to defrag until the stash
/// is optimally compacted.
/// - Users are advised to call this method using `Some` concrete
/// value to keep gas costs within certain bounds.
/// - The call to the given callback takes place before the reinsertion
/// of the shifted occupied entry.
pub fn defrag<C>(&mut self, max_iterations: Option<u32>, mut callback: C) -> u32
where
C: FnMut(Index, Index, &T),
{
let len_entries = self.len_entries();
let mut freed_cells = 0;
for index in (0..len_entries)
.rev()
.take(max_iterations.unwrap_or(len_entries) as usize)
{
if !self.has_vacant_entries() {
// Bail out as soon as there are no more vacant entries left.
return freed_cells
}
// In any case we are going to free yet another storage cell.
freed_cells += 1;
match self
.entries
.put_get(index, None)
.expect("index is out of bounds")
{
Entry::Vacant(vacant_entry) => {
// Remove the vacant entry and rebind its neighbors.
self.remove_vacant_entry(index, vacant_entry);
}
Entry::Occupied(value) => {
// Move the occupied entry into one of the remaining vacant
// entries. We do not re-use the `put` method to not update
// the length and other header information.
let vacant_index = self
.last_vacant_index()
.expect("it has been asserted that there are vacant entries");
callback(index, vacant_index, &value);
let new_entry = Some(Entry::Occupied(value));
let old_entry = self.entries.put_get(vacant_index, new_entry).expect(
"`last_vacant_index` index must point to an occupied cell",
);
let vacant_entry = match old_entry {
Entry::Vacant(vacant_entry) => vacant_entry,
Entry::Occupied(_) => {
unreachable!(
"`last_vacant_index` must point to a vacant entry"
)
}
};
self.remove_vacant_entry(vacant_index, vacant_entry);
}
}
self.header.len_entries -= 1;
}
freed_cells
}
}