Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,7 @@ members = [
"third_party/move/move-vm/test-utils",
"third_party/move/move-vm/transactional-tests",
"third_party/move/move-vm/types",
"third_party/move/shared-dsa",
"third_party/move/testing-infra/transactional-test-runner",
"third_party/move/tools/move-abigen",
"third_party/move/tools/move-asm",
Expand Down Expand Up @@ -783,6 +784,7 @@ rocksdb = { version = "0.24.0", features = ["lz4"] }
rsa = { version = "0.9.6" }
rstack-self = { version = "0.3.0", features = ["dw"], default-features = false }
rstest = "0.15.0"
rustc-hash = "2"
rusty-fork = "0.3.0"
rustversion = "1.0.14"
scopeguard = "1.2.0"
Expand Down Expand Up @@ -931,6 +933,7 @@ move-vm-test-utils = { path = "third_party/move/move-vm/test-utils", features =
"table-extension",
] }
move-vm-types = { path = "third_party/move/move-vm/types" }
shared-dsa = { path = "third_party/move/shared-dsa" }

[profile.release]
debug = true
Expand Down
15 changes: 15 additions & 0 deletions third_party/move/shared-dsa/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
[package]
name = "shared-dsa"
version = "0.1.0"

# Workspace inherited keys
authors = { workspace = true }
edition = { workspace = true }
homepage = { workspace = true }
license = { workspace = true }
publish = { workspace = true }
repository = { workspace = true }
rust-version = { workspace = true }

[dependencies]
rustc-hash = { workspace = true }
12 changes: 12 additions & 0 deletions third_party/move/shared-dsa/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// Copyright (c) Aptos Foundation
// Licensed pursuant to the Innovation-Enabling Source Code License, available at https://github.com/aptos-labs/aptos-core/blob/main/LICENSE

//! Collection of data structures and algorithms, for shared use across
//! various crates.

mod unordered_map;
mod unordered_set;

pub use std::collections::hash_map::{Entry, OccupiedEntry, VacantEntry};
pub use unordered_map::UnorderedMap;
pub use unordered_set::UnorderedSet;
260 changes: 260 additions & 0 deletions third_party/move/shared-dsa/src/unordered_map.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,260 @@
// Copyright (c) Aptos Foundation
// Licensed pursuant to the Innovation-Enabling Source Code License, available at https://github.com/aptos-labs/aptos-core/blob/main/LICENSE

use rustc_hash::{FxBuildHasher, FxHashMap};
use std::{borrow::Borrow, collections::hash_map::Entry, fmt, hash::Hash, iter::FromIterator};

/// A fast, non-cryptographic, non-hash-DoS-resistant hash map.
/// Iteration over keys or key-value pairs are not exposed to avoid any
/// reliance on non-deterministic ordering.
#[derive(Clone)]
pub struct UnorderedMap<K, V> {
inner: FxHashMap<K, V>,
}

// ---------------------------------------------------------------------------
// Methods that require no bounds on K/V
// ---------------------------------------------------------------------------

impl<K, V> UnorderedMap<K, V> {
#[inline]
pub fn new() -> Self {
Self {
inner: FxHashMap::default(),
}
}

/// Creates an empty map pre-allocated to hold at least `capacity` elements
/// without reallocation. The load factor is handled internally, so pass the
/// number of elements you expect, not an inflated value.
#[inline]
pub fn with_capacity(capacity: usize) -> Self {
Self {
inner: FxHashMap::with_capacity_and_hasher(capacity, FxBuildHasher),
Comment thread
vineethk marked this conversation as resolved.
}
}

#[inline]
pub fn len(&self) -> usize {
self.inner.len()
}

#[inline]
pub fn is_empty(&self) -> bool {
self.inner.is_empty()
}

#[inline]
pub fn clear(&mut self) {
self.inner.clear();
}
}

// ---------------------------------------------------------------------------
// Methods that require K: Hash + Eq
// ---------------------------------------------------------------------------

impl<K: Hash + Eq, V> UnorderedMap<K, V> {
#[inline]
pub fn get<Q>(&self, k: &Q) -> Option<&V>
where
K: Borrow<Q>,
Q: Hash + Eq + ?Sized,
{
self.inner.get(k)
}

#[inline]
pub fn get_mut<Q>(&mut self, k: &Q) -> Option<&mut V>
where
K: Borrow<Q>,
Q: Hash + Eq + ?Sized,
{
self.inner.get_mut(k)
}

#[inline]
pub fn contains_key<Q>(&self, k: &Q) -> bool
where
K: Borrow<Q>,
Q: Hash + Eq + ?Sized,
{
self.inner.contains_key(k)
}

/// Inserts a key-value pair. Returns the previous value if the key was
/// already present, or `None` if it was newly inserted.
#[inline]
pub fn insert(&mut self, k: K, v: V) -> Option<V> {
self.inner.insert(k, v)
}

/// Removes a key, returning its value if the key was present.
#[inline]
pub fn remove<Q>(&mut self, k: &Q) -> Option<V>
where
K: Borrow<Q>,
Q: Hash + Eq + ?Sized,
{
self.inner.remove(k)
}

/// Removes a key, returning the key-value pair if the key was present.
#[inline]
pub fn remove_entry<Q>(&mut self, k: &Q) -> Option<(K, V)>
where
K: Borrow<Q>,
Q: Hash + Eq + ?Sized,
{
self.inner.remove_entry(k)
}

#[inline]
pub fn entry(&mut self, key: K) -> Entry<'_, K, V> {
Comment thread
vineethk marked this conversation as resolved.
self.inner.entry(key)
}

/// Reserves space for at least `additional` more elements. Pass the number
/// of elements you expect to add, not an inflated value — the load factor
/// is handled internally.
#[inline]
pub fn reserve(&mut self, additional: usize) {
self.inner.reserve(additional);
}
}

// ---------------------------------------------------------------------------
// Trait implementations
// ---------------------------------------------------------------------------

impl<K, V> Default for UnorderedMap<K, V> {
#[inline]
fn default() -> Self {
Self::new()
}
}

impl<K, V> fmt::Debug for UnorderedMap<K, V> {
/// Only shows the length to avoid exposing arbitrary iteration order.
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("UnorderedMap")
.field("len", &self.inner.len())
.finish()
}
}

impl<K: Hash + Eq, V: PartialEq> PartialEq for UnorderedMap<K, V> {
#[inline]
fn eq(&self, other: &Self) -> bool {
self.inner == other.inner
}
}

impl<K: Hash + Eq, V: Eq> Eq for UnorderedMap<K, V> {}

impl<K: Hash + Eq, V> FromIterator<(K, V)> for UnorderedMap<K, V> {
#[inline]
fn from_iter<I: IntoIterator<Item = (K, V)>>(iter: I) -> Self {
Self {
inner: iter.into_iter().collect(),
}
}
}

impl<K: Hash + Eq, V> Extend<(K, V)> for UnorderedMap<K, V> {
#[inline]
fn extend<I: IntoIterator<Item = (K, V)>>(&mut self, iter: I) {
self.inner.extend(iter);
}
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn test_insert_get_remove() {
let mut map = UnorderedMap::new();
assert!(map.is_empty());

assert_eq!(map.insert("a", 1), None);
assert_eq!(map.insert("b", 2), None);
assert_eq!(map.len(), 2);

assert_eq!(map.get("a"), Some(&1));
assert_eq!(map.get("c"), None);
assert!(map.contains_key("b"));

assert_eq!(map.remove("a"), Some(1));
assert_eq!(map.len(), 1);
assert!(!map.contains_key("a"));
}

#[test]
fn test_get_mut() {
let mut map = UnorderedMap::new();
map.insert("x", 10);
*map.get_mut("x").unwrap() = 20;
assert_eq!(map.get("x"), Some(&20));
}

#[test]
fn test_remove_entry() {
let mut map = UnorderedMap::new();
map.insert("k", 42);
assert_eq!(map.remove_entry("k"), Some(("k", 42)));
assert!(map.is_empty());
}

#[test]
fn test_entry_api() {
let mut map = UnorderedMap::new();
map.entry("a").or_insert(1);
map.entry("a").and_modify(|v| *v += 10);
assert_eq!(map.get("a"), Some(&11));
}

#[test]
fn test_clear() {
let mut map = UnorderedMap::with_capacity(16);
map.insert(1, 1);
map.clear();
assert!(map.is_empty());
}

#[test]
fn test_default() {
let map: UnorderedMap<String, i32> = Default::default();
assert!(map.is_empty());
}

#[test]
fn test_eq() {
let a: UnorderedMap<_, _> = [(1, 2), (3, 4)].into_iter().collect();
let b: UnorderedMap<_, _> = [(3, 4), (1, 2)].into_iter().collect();
assert_eq!(a, b);
}

#[test]
fn test_extend() {
let mut map = UnorderedMap::new();
map.insert(1, "a");
map.extend([(2, "b"), (3, "c")]);
assert_eq!(map.len(), 3);
}

#[test]
fn test_reserve() {
let mut map = UnorderedMap::new();
map.insert(1, 1);
map.reserve(100);
}

#[test]
fn test_debug() {
let mut map = UnorderedMap::new();
map.insert(1, 2);
let s = format!("{:?}", map);
assert!(s.contains("len: 1"));
}
}
Loading
Loading