Skip to content

Commit 2ff2a7a

Browse files
authored
Turbopack: Add turbo-frozenmap crate with FrozenMap and FrozenSet implementations (#87042)
This implements a `FrozenMap` type as a flat sorted boxed slice of `(K, V)` tuples. This is intended to be used with `TaskInput`s and `turbo_task::Value`s, where the data is stored immutably, and where we already try to call `shrink_to_fit`. This map is smaller than `HashMap` or `BTreeMap`, and provides `O(log n)` lookups with excellent cache locality (so it's probably faster than `HashMap` in most cases). Since the map is sorted, equality doesn't care about order, so this is a better implementation of `Eq` for our use-cases than what `IndexMap` provides. It has a variety of ways it can be constructed: A `HashMap`, a `Vec<(K, V)>`, a `BTreeMap`, etc. There are pros and cons of all of these: - `Vec<(K, V)>` is good if you don't need lookups until after freezing, and you don't expect many duplicates, since sorting at the end is cheaper than maintaining a map. - A `BTreeMap` is good if you need a real map, because we can avoid a sort at the end. - `HashMap`/`IndexMap` are okay if you're getting this data structure from somewhere else. `IndexSet` is also provided as a thin wrapper around `IndexMap`. ## Remaining Issues This can't be used with `ResolvedVc` as keys, because `ResolvedVc` does not implement `Ord`. I think it should be okay to do that because it's no worse than our current implementations of `Eq` and `Hash`, but I know this is contentious, so I'm leaving it out of this PR. ## Implementation Strategy I used Claude Code to write most of this: - Provided it rust's stdlib implementation of BTreeMap. - Asked it to extract the public interface into a separate file, excluding nightly-only features. - Asked it to remove all `&mut` methods. - Asked it to implement `FrozenMap` with the same API. Then I did a bunch of manual cleanup: - Replaced a bunch of manual trait implementations with derives. - Added bincode and serde traits. - Added various `turbo-tasks` trait implementations. - Added some methods/impls for getting the underlying slice. - Optimized a bunch of the constructors. Then I asked Claude Code to implement the Set version, and did another round of cleanup: - Removed bunch of unsafe code where it was trying to transmute `(T, ())` to `T`. The memory layout of a tuple is technically undefined, so this wasn't safe, and the APIs it was trying to create doing this weren't really needed either. - Replaced some of the iterator newtype wrappers with simpler type aliases. The newtype was overkill for our use-case.
1 parent ae0603c commit 2ff2a7a

File tree

10 files changed

+1510
-7
lines changed

10 files changed

+1510
-7
lines changed

Cargo.lock

Lines changed: 10 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -294,13 +294,12 @@ next-taskless = { path = "crates/next-taskless" }
294294
# Turbopack
295295
auto-hash-map = { path = "turbopack/crates/turbo-tasks-auto-hash-map" }
296296
turbo-bincode = { path = "turbopack/crates/turbo-bincode" }
297-
turbo-prehash = { path = "turbopack/crates/turbo-prehash" }
298-
turbo-rcstr = { path = "turbopack/crates/turbo-rcstr" }
299297
turbo-dyn-eq-hash = { path = "turbopack/crates/turbo-dyn-eq-hash" }
300298
turbo-esregex = { path = "turbopack/crates/turbo-esregex" }
299+
turbo-frozenmap = { path = "turbopack/crates/turbo-frozenmap" }
301300
turbo-persistence = { path = "turbopack/crates/turbo-persistence" }
302-
turbo-unix-path = { path = "turbopack/crates/turbo-unix-path" }
303-
turbo-tasks-malloc = { path = "turbopack/crates/turbo-tasks-malloc", default-features = false }
301+
turbo-prehash = { path = "turbopack/crates/turbo-prehash" }
302+
turbo-rcstr = { path = "turbopack/crates/turbo-rcstr" }
304303
turbo-tasks = { path = "turbopack/crates/turbo-tasks" }
305304
turbo-tasks-backend = { path = "turbopack/crates/turbo-tasks-backend" }
306305
turbo-tasks-bytes = { path = "turbopack/crates/turbo-tasks-bytes" }
@@ -309,7 +308,9 @@ turbo-tasks-fetch = { path = "turbopack/crates/turbo-tasks-fetch" }
309308
turbo-tasks-fs = { path = "turbopack/crates/turbo-tasks-fs" }
310309
turbo-tasks-hash = { path = "turbopack/crates/turbo-tasks-hash" }
311310
turbo-tasks-macros = { path = "turbopack/crates/turbo-tasks-macros" }
311+
turbo-tasks-malloc = { path = "turbopack/crates/turbo-tasks-malloc", default-features = false }
312312
turbo-tasks-testing = { path = "turbopack/crates/turbo-tasks-testing" }
313+
turbo-unix-path = { path = "turbopack/crates/turbo-unix-path" }
313314
turbopack = { path = "turbopack/crates/turbopack" }
314315
turbopack-bench = { path = "turbopack/crates/turbopack-bench" }
315316
turbopack-nodejs = { path = "turbopack/crates/turbopack-nodejs" }
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
[package]
2+
name = "turbo-frozenmap"
3+
version = "0.1.0"
4+
edition = "2024"
5+
license = "MIT"
6+
7+
[dependencies]
8+
bincode = { workspace = true }
9+
indexmap = { workspace = true }
10+
serde = { workspace = true }
11+
12+
[dev-dependencies]
13+
14+
[lints]
15+
workspace = true
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
//! A frozen (immutable) ordered map and set implementation.
2+
3+
pub mod map;
4+
pub mod set;
5+
6+
pub use crate::{map::FrozenMap, set::FrozenSet};

0 commit comments

Comments
 (0)