yo_kv/elem.rs
1//! The element table, which is what a hash, a set and a sorted set are all
2//! made of underneath.
3//!
4//! One structure serves every collection because the three of them ask the same
5//! two questions. Is this member here, and what is stored against it. A hash
6//! stores a value address and a TTL slot against a field name, a set stores
7//! nothing at all against a member, and a sorted set stores a score. That is one
8//! table with a payload type the caller picks, and it is `05` section 4.2's
9//! element per row: a dense array of fixed size rows, plus a blob holding the
10//! variable length names, plus an open addressed slot array in front of them
11//! that turns a name into a row index.
12//!
13//! ```text
14//! slots rows payloads names
15//! +--------+ +--------------+ +-------+ +--------------------------+
16//! | tag|idx| ----> | name off,len | | score | | fieldbytesmemberbytes... |
17//! +--------+ +--------------+ +-------+ +--------------------------+
18//! one load one load same idx only touched on a tag hit
19//! ```
20//!
21//! The payload sits beside the row rather than in it, so that a score's eight
22//! byte alignment does not put four bytes of padding on every member.
23//!
24//! Three properties come out of that shape and all three are the reason for it.
25//!
26//! A probe is one load into the slot array and one into the row array. The top
27//! byte of a slot is a tag taken from the hash, so a collision on the low bits
28//! is thrown out without reading the name bytes at all, and the name is only
29//! compared when the tag says it is worth comparing.
30//!
31//! A walk is sequential. `HGETALL`, `SMEMBERS` and `HSCAN` read the row array
32//! front to back with no pointer chasing, which is the difference between the
33//! 13.6 nanoseconds a field walk actually costs and the number a linked
34//! structure would cost.
35//!
36//! A uniform draw is an index. `SPOP` and `SRANDMEMBER` pick a number under
37//! [`Elements::len`] and read that row, because the row array has no holes in
38//! it. That is K9 and it is the whole of aki's signature failure: `SPOP` came in
39//! at 0.58x at pipeline 16 and 0.29x at pipeline 1 there, because a draw had to
40//! remove from an ordered structure, and here there is no ordered structure to
41//! remove from.
42//!
43//! # Removal
44//!
45//! Keeping the row array dense means a removal moves the last row into the hole
46//! and fixes up the slot that pointed at it, which costs one extra probe. The
47//! alternative is a free list and holes, and then a draw has to retry until it
48//! lands on a live row, which is fine at 90 percent occupancy and unbounded on a
49//! set that has been drained down to its last member.
50//!
51//! The slot the removed member sat in is closed by writing a marker over it,
52//! and the marker is the empty one rather than the dead one whenever it can
53//! prove no probe ever ran past the slot, which is when the slot after it is
54//! already empty. A run of markers directly behind that one is cleared too, by
55//! the same argument applied to each in turn. So the common shapes leave nothing
56//! behind at all: a set filled and then drained collects its own markers on the
57//! way down, and a table with short runs in it almost never writes one.
58//!
59//! What is left over is counted and it counts against the load exactly as a live
60//! member does, so a table churned in place rebuilds on the same schedule as one
61//! that only grows and can never fill up with markers. That count is also what
62//! bounds a probe, and the bound is easier than it looks: a removal turns a full
63//! slot into a marker or into an empty one, so the two together never go up, so
64//! a probe is never longer than it was at the moment the table was fullest.
65//!
66//! It is not free, and the case where it is not is a table drained and then read
67//! from. Shifting the run back really did give the slot up, so a set emptied
68//! from a million down to ten used to answer a miss like a table holding ten,
69//! and now it answers like a table that once held a million, which is under
70//! three slots looked at either way and is the whole of the trade.
71//!
72//! This used to shift the run behind the hole back instead, which leaves no
73//! marker and costs a walk over that run with a home slot computed for every
74//! slot in it. The marker is two writes and the shift was the single most
75//! expensive thing on the removal path. Nothing here allocates, on either side
76//! of the change, because a removal is on a command path and `cargo xtask alloc`
77//! is the gate that says a command path allocates nothing.
78//!
79//! Neither the removal nor the marker reads a name, because every row carries
80//! the slot it wanted. Three bytes a row for that, packed in beside the name
81//! length, and it took a pop at a hundred thousand members from 123 ns to
82//! 25.7 ns, which is the difference between a random trip into the name blob per
83//! slot examined and no trip at all.
84//!
85//! # Names
86//!
87//! Names are interned per collection, which is `05` section 3's rule and the
88//! reason a hash field costs 16 bytes and not 16 bytes plus its name on every
89//! write. Writing the same field again is a row update and touches no name
90//! bytes. It is per collection and not global because a global table is state
91//! shared between shards and Y1 does not allow any.
92//!
93//! Removing a member leaves its bytes in the blob unreferenced. Those bytes come
94//! back when the dead share crosses a half and there are at least a few thousand
95//! of them, which is a rewrite of the blob and a walk over the rows to move
96//! their offsets, and until then they are counted and reported rather than
97//! pretended away. That accounting lives in [`crate::blob`], which is also what
98//! a key's bytes are kept in, so there is one copy of it.
99//!
100//! A hash writes its value into the same blob directly behind the field name,
101//! which is what [`Elements::tailed`] is. The pair is one span, so the row's
102//! offset finds both and a hash field carries no value offset at all.
103
104use yo_common::{bytes_eq, hash_key, tag_of};
105
106use crate::blob::Blob;
107use crate::scan::Cursor;
108
109/// The most rows one table holds.
110///
111/// A slot packs a tag and a row index into 32 bits, which leaves 24 bits for the
112/// index. A collection past this belongs in the partitioned band of `05`
113/// section 4.3, which is a set of these rather than a bigger one, and the band
114/// boundary is 262,144, well under this.
115pub const MAX_ROWS: usize = 0x00FF_FFFE;
116
117/// The longest name this table stores.
118///
119/// Redis has no limit on a field name below the 512 MiB it puts on everything.
120/// A name that long is a value that has been put in the wrong place, and holding
121/// the ceiling at what fits in sixteen bits is what lets a long name carry its
122/// own length in two bytes rather than four.
123pub const NAME_MAX: usize = u16::MAX as usize;
124
125/// The low twenty four bits of a slot, which are the row index.
126const ROW: u32 = 0x00FF_FFFF;
127
128/// A slot nothing has ever been written to. A probe stops here.
129const EMPTY: u32 = 0xFFFF_FFFF;
130
131/// A slot something was written to and then removed from. A probe keeps going.
132///
133/// Both markers have all twenty four row bits set and a live row index never
134/// does, because [`MAX_ROWS`] is one short of that, so `slot & ROW == ROW` tells
135/// a marker of either kind from a live slot in two instructions. Doing it that
136/// way rather than by stealing the top bit is what keeps the tag a full eight
137/// bits: a seven bit tag would double the rate at which a probe reads a row it
138/// is about to reject, and the probe is the hottest path in the engine.
139const TOMB: u32 = 0x00FF_FFFF;
140
141/// How full the slot array is allowed to get before it doubles.
142///
143/// Three quarters is where linear probing is still short and the array is not
144/// mostly air. The run length at this load is under three on average, which is
145/// inside one cache line of slots. Markers count towards it, because a marker is
146/// a slot a probe has to look at and step over.
147const LOAD_NUM: usize = 3;
148const LOAD_DEN: usize = 4;
149
150/// The smallest slot array, which is one cache line of slots.
151const MIN_SLOTS: usize = 16;
152
153/// The shortest name that keeps its length in the blob instead of in its row.
154///
155/// A row holds the length in one byte, so a name this long or longer writes its
156/// real length into the two bytes ahead of it and puts this sentinel in the
157/// byte. Nothing on the probe path pays much for that: the prefix sits in the
158/// cache line the name comparison was about to read anyway, and the branch is a
159/// comparison against a constant that goes the same way on every element of
160/// every collection anyone has ever measured.
161const LONG_NAME: usize = 255;
162
163/// How many bytes a long name's length prefix takes.
164const PREFIX: usize = 2;
165
166/// The shortest tail that keeps its length in four bytes rather than one.
167///
168/// A tail carries its own length, because unlike a name there is nowhere in the
169/// row left to put it. One byte covers every value anyone actually stores in a
170/// hash field and the escape covers the rest.
171const LONG_TAIL: usize = 255;
172
173/// How many bytes a long tail's length prefix takes, the marker included.
174const TAIL_PREFIX: usize = 5;
175
176/// How many bits of the home slot a row keeps.
177const HOME_BITS: u32 = 24;
178
179/// One element: where its name is and where it wanted to sit.
180///
181/// Eight bytes, and the packing is what makes it eight rather than twelve. The
182/// blob offset needs a whole `u32` because a large collection's names run to
183/// megabytes. The other four hold the name's length in the low byte and the home
184/// slot in the twenty four above it.
185///
186/// The home slot is where this row would sit in an empty table, and what it buys
187/// is that a removal and a growth never read a name and never hash one. Both of
188/// those walk slots and ask each one where it wanted to be, and asking the blob
189/// instead means a random cache miss per slot examined, on the two operations
190/// where there is no reply to send that would have paid for it.
191///
192/// Twenty four bits of it is every bit that matters until the slot array passes
193/// sixteen million, which is a table holding twelve million elements. Past there
194/// [`Elements::home_of`] hashes the name instead, and that is the right place for
195/// the cost to land: the partitioned band splits a collection at a quarter of a
196/// million, so a table that large is one partition of a set with two hundred
197/// million members in it.
198///
199/// The payload is deliberately not in here. See [`Elements::vals`].
200#[derive(Debug, Clone, Copy)]
201struct Row {
202 /// Where the name starts in the blob, or where its length prefix does.
203 at: u32,
204 /// The name's length in the low eight bits, its home slot in the top
205 /// twenty four.
206 packed: u32,
207}
208
209impl Row {
210 /// The row for a name of `len` bytes that has just been pushed at `at`.
211 #[inline]
212 fn new(at: u32, len: usize, h: u64) -> Row {
213 let len = u32::try_from(len.min(LONG_NAME)).expect("LONG_NAME is one byte");
214 Row {
215 at,
216 packed: ((h as u32 & ((1 << HOME_BITS) - 1)) << 8) | len,
217 }
218 }
219
220 /// The length byte, which is [`LONG_NAME`] when the real length is in the
221 /// blob.
222 #[inline]
223 const fn len_byte(self) -> usize {
224 (self.packed & 0xFF) as usize
225 }
226
227 /// The low [`HOME_BITS`] of the name's hash.
228 #[inline]
229 const fn home(self) -> usize {
230 (self.packed >> 8) as usize
231 }
232}
233
234/// An open addressed table of elements, keyed by name, dense in insertion order.
235///
236/// The payload is whatever the collection needs. A set uses `()`, a hash uses
237/// the value address and the TTL slot, a sorted set uses the score.
238#[derive(Debug, Clone)]
239pub struct Elements<V> {
240 /// Tag in the top byte, row index in the low 24 bits, or [`EMPTY`]/[`TOMB`].
241 slots: Box<[u32]>,
242 /// How many slots hold [`TOMB`].
243 ///
244 /// These count against the load exactly as live rows do, which is what stops
245 /// a table written and removed from in place filling up with them, and it is
246 /// also what a drained table watches to know when to rebuild.
247 ///
248 /// Four bytes and not eight, because a marker sits in a slot and the slot
249 /// array is indexed by a `u32`. It is next to [`Elements::tailed`] so that
250 /// the two of them share the eight bytes this used to take on its own.
251 dead: u32,
252 /// Whether a name in the blob is followed by a tail.
253 ///
254 /// A hash is the only collection that has a second variable length thing to
255 /// keep per element, and the obvious place for it is a blob of its own with
256 /// a four byte offset beside every row saying where in it to look. That is
257 /// what this used to be, and the four bytes were the single largest piece of
258 /// overhead in a hash: more than the row, more than the slot.
259 ///
260 /// Behind the name instead, the offset is not needed at all, because the row
261 /// already says where the name starts and the name says how long it is. It
262 /// costs one byte for the tail's own length, against four for the offset and
263 /// one for the length the separate blob was writing anyway.
264 ///
265 /// It is a flag rather than a type parameter because the alternative is
266 /// threading a constant through [`crate::parts::Parts`] and every scratch
267 /// table in `setops`, to save nothing per element. Nothing on the probe path
268 /// reads it: a name is found exactly as it was, and only the accounting and
269 /// the compaction care that there is anything behind it.
270 tailed: bool,
271 /// The rows, in insertion order, with no holes.
272 rows: Vec<Row>,
273 /// The payloads, one per row and at the same index.
274 ///
275 /// Beside the rows rather than inside them, because a payload with a
276 /// stricter alignment than the row's four bytes pays for that alignment on
277 /// every element. A sorted set's score is the case that matters: eight byte
278 /// aligned, so a row holding one is twenty four bytes to carry twenty, and
279 /// the four wasted bytes are per member. Split, the pair is twenty and there
280 /// is no padding anywhere. A set pays nothing for this either way, because
281 /// `Vec<()>` does not allocate.
282 ///
283 /// It costs the walks a second array, which is a second sequential stream
284 /// and not a second random access, so the prefetcher covers it.
285 vals: Vec<V>,
286 /// Every live name, back to back, and some dead ones.
287 ///
288 /// The length stays in the row rather than beside the offset, because one
289 /// byte of it is what keeps a row at eight bytes, and the names that do not
290 /// fit in one byte carry their own length in the blob instead of widening
291 /// every row that does.
292 ///
293 /// When [`Elements::tailed`] is set, each name has its element's bytes
294 /// written directly behind it and the pair is one span.
295 names: Blob,
296}
297
298impl<V: Copy> Default for Elements<V> {
299 fn default() -> Elements<V> {
300 Elements::new()
301 }
302}
303
304impl<V: Copy> Elements<V> {
305 /// An empty table that has not allocated anything yet.
306 ///
307 /// A collection is created by its first write, so the empty case is the one
308 /// that happens most often and it does not deserve an allocation.
309 #[must_use]
310 pub fn new() -> Elements<V> {
311 Elements {
312 slots: Box::new([]),
313 dead: 0,
314 rows: Vec::new(),
315 vals: Vec::new(),
316 names: Blob::new(),
317 tailed: false,
318 }
319 }
320
321 /// An empty table that keeps each element's bytes behind its name.
322 ///
323 /// Room for `n` elements and `blob` bytes of names and tails together. See
324 /// [`Elements::tailed`] for what a tail is and why it is not a second blob.
325 #[must_use]
326 pub fn tailed(n: usize, blob: usize) -> Elements<V> {
327 let mut e = Elements::with_capacity(n);
328 e.names = Blob::with_capacity(blob);
329 e.tailed = true;
330 e
331 }
332
333 /// An empty table with room for `n` elements already taken.
334 ///
335 /// This is Y18's presize rule. `SINTERSTORE` knows the result is no larger
336 /// than its smaller input, so it says so once instead of growing eight
337 /// times on the way there.
338 #[must_use]
339 pub fn with_capacity(n: usize) -> Elements<V> {
340 let mut e = Elements::new();
341 e.reserve(n);
342 e
343 }
344
345 /// Room for `n` elements in a table that already exists.
346 ///
347 /// [`Elements::with_capacity`] for a table being reused rather than built.
348 /// A scratch table that is cleared and refilled on every call keeps
349 /// whatever it grew to last time, so this does nothing at all unless the
350 /// run coming up is bigger than any run before it, which is what takes the
351 /// allocator off a `SUNION` sent in a loop.
352 ///
353 /// The slot array is only rebuilt when it could not hold `n` at the load
354 /// factor, rather than whenever a size is named. Rebuilding it to the size
355 /// it already is would be an allocation asked for by a call whose whole
356 /// point is to avoid one.
357 pub fn reserve(&mut self, n: usize) {
358 if n == 0 {
359 return;
360 }
361 self.rows.reserve(n.saturating_sub(self.rows.len()));
362 self.vals.reserve(n.saturating_sub(self.vals.len()));
363 if (n + self.dead as usize) * LOAD_DEN > self.slots.len() * LOAD_NUM {
364 self.grow_to(slots_for(n));
365 }
366 }
367
368 /// How many elements are here.
369 #[inline]
370 #[must_use]
371 pub fn len(&self) -> usize {
372 self.rows.len()
373 }
374
375 /// Whether the collection is empty, which for Redis means it does not exist.
376 #[inline]
377 #[must_use]
378 pub fn is_empty(&self) -> bool {
379 self.rows.is_empty()
380 }
381
382 /// What is stored against this name.
383 #[inline]
384 #[must_use]
385 pub fn get(&self, name: &[u8]) -> Option<&V> {
386 let at = self.find(name)?;
387 Some(&self.vals[at])
388 }
389
390 /// The payload, to be changed in place.
391 ///
392 /// This is the `HINCRBY` and `ZINCRBY` path. Neither of them writes a name,
393 /// so neither of them should pay for one.
394 #[inline]
395 pub fn get_mut(&mut self, name: &[u8]) -> Option<&mut V> {
396 let at = self.find(name)?;
397 Some(&mut self.vals[at])
398 }
399
400 /// Whether this name is here at all. `SISMEMBER` and `HEXISTS`.
401 #[inline]
402 #[must_use]
403 pub fn contains(&self, name: &[u8]) -> bool {
404 self.find(name).is_some()
405 }
406
407 /// Which row this name is in, for a caller keeping an array beside the rows.
408 ///
409 /// A hash's field deadlines are indexed by row position rather than by a
410 /// number in the row (`crate::ttl` says why), so `HEXPIRE` needs the position
411 /// the probe found rather than the payload it found there. That is the only
412 /// caller, and it is why this is a position and not a payload.
413 ///
414 /// The position is only good until the next insert or remove, since a remove
415 /// moves the last row into the hole.
416 #[inline]
417 #[must_use]
418 pub fn index_of(&self, name: &[u8]) -> Option<usize> {
419 self.find(name)
420 }
421
422 /// The hash of a name, for a caller about to ask several tables about it.
423 ///
424 /// `SINTER` over k sets asks the same question k times, and hashing the
425 /// member once instead of k times is the difference between the hash being
426 /// noise and it being most of the work. Pair it with
427 /// [`Elements::contains_hashed`].
428 #[inline]
429 #[must_use]
430 pub fn hash_of(name: &[u8]) -> u64 {
431 hash(name)
432 }
433
434 /// Whether this name is here, with its hash already in hand.
435 ///
436 /// The hash must be [`Elements::hash_of`] of the same bytes. Anything else
437 /// gives a wrong answer rather than an error, which is why this takes the
438 /// name too and compares it: a caller cannot fake membership with a number.
439 #[inline]
440 #[must_use]
441 pub fn contains_hashed(&self, h: u64, name: &[u8]) -> bool {
442 self.find_hashed(h, name).is_some()
443 }
444
445 /// Which row this name is in, with its hash already in hand.
446 #[inline]
447 #[must_use]
448 pub fn index_of_hashed(&self, h: u64, name: &[u8]) -> Option<usize> {
449 self.find_hashed(h, name)
450 }
451
452 /// What is stored against this name, with its hash already in hand.
453 #[inline]
454 #[must_use]
455 pub fn get_hashed(&self, h: u64, name: &[u8]) -> Option<&V> {
456 let at = self.find_hashed(h, name)?;
457 Some(&self.vals[at])
458 }
459
460 /// The payload to be changed in place, with the hash already in hand.
461 #[inline]
462 pub fn get_hashed_mut(&mut self, h: u64, name: &[u8]) -> Option<&mut V> {
463 let at = self.find_hashed(h, name)?;
464 Some(&mut self.vals[at])
465 }
466
467 /// Store `value` against `name`, and say what was there before.
468 ///
469 /// `None` means the element is new, which is the number `SADD` and `HSET`
470 /// report. A name over [`NAME_MAX`] or a table at [`MAX_ROWS`] is refused
471 /// rather than truncated, and refusing is a `false` here and an error
472 /// message from the layer above, which is the one that knows which command
473 /// is being answered.
474 pub fn insert(&mut self, name: &[u8], value: V) -> Result<Option<V>, Full> {
475 self.insert_hashed(hash(name), name, value)
476 }
477
478 /// Store `value` against `name`, with its hash already in hand.
479 ///
480 /// The partitioned band hashes once to pick a partition and would otherwise
481 /// hash again to place the row inside it, which on a short member is most of
482 /// the write.
483 pub fn insert_hashed(&mut self, h: u64, name: &[u8], value: V) -> Result<Option<V>, Full> {
484 if name.len() > NAME_MAX {
485 return Err(Full::Name);
486 }
487 if let Some(at) = self.find_hashed(h, name) {
488 return Ok(Some(std::mem::replace(&mut self.vals[at], value)));
489 }
490 if self.rows.len() >= MAX_ROWS {
491 return Err(Full::Rows);
492 }
493 self.reserve_one();
494 let at = u32::try_from(self.rows.len()).expect("MAX_ROWS is under u32::MAX");
495 let name_at = self.push_name(name);
496 self.rows.push(Row::new(name_at, name.len(), h));
497 self.vals.push(value);
498 self.put_slot(h, at);
499 Ok(None)
500 }
501
502 /// Store `tail` against `name`, and say which row it is in and whether the
503 /// name is new.
504 ///
505 /// `HSET`. Only for a table built by [`Elements::tailed`].
506 ///
507 /// A name that is already here keeps its row and its slot and gets a fresh
508 /// span in the blob, because the new tail need not be the length of the old
509 /// one. That copies the name again, which is the one thing this arrangement
510 /// costs that a separate value blob did not, and it is a few bytes against
511 /// the four an offset would have cost every field in the hash forever.
512 pub fn set_tailed(
513 &mut self,
514 name: &[u8],
515 tail: &[u8],
516 value: V,
517 ) -> Result<(usize, bool), Full> {
518 debug_assert!(self.tailed, "this table does not keep tails");
519 if name.len() > NAME_MAX {
520 return Err(Full::Name);
521 }
522 let h = hash(name);
523 if let Some(at) = self.find_hashed(h, name) {
524 self.rewrite_tail(at, name, tail);
525 self.vals[at] = value;
526 return Ok((at, false));
527 }
528 if self.rows.len() >= MAX_ROWS {
529 return Err(Full::Rows);
530 }
531 self.reserve_one();
532 let at = self.rows.len();
533 let name_at = self.push_name(name);
534 self.push_tail(tail);
535 self.rows.push(Row::new(name_at, name.len(), h));
536 self.vals.push(value);
537 self.put_slot(h, u32::try_from(at).expect("MAX_ROWS is under u32::MAX"));
538 Ok((at, true))
539 }
540
541 /// Put a fresh copy of a row's name and a new tail at the end of the blob.
542 fn rewrite_tail(&mut self, at: usize, name: &[u8], tail: &[u8]) {
543 let gone = self.footprint(&self.rows[at]);
544 let name_at = self.push_name(name);
545 self.push_tail(tail);
546 self.rows[at].at = name_at;
547 self.names.release(gone);
548 self.maybe_compact_names();
549 }
550
551 /// The tail stored against `name`.
552 #[inline]
553 #[must_use]
554 pub fn tail(&self, name: &[u8]) -> Option<&[u8]> {
555 let at = self.find(name)?;
556 Some(self.tail_of(&self.rows[at]))
557 }
558
559 /// How long the tail stored against `name` is. `HSTRLEN`.
560 #[inline]
561 #[must_use]
562 pub fn tail_len(&self, name: &[u8]) -> Option<usize> {
563 let at = self.find(name)?;
564 Some(self.tail_len_of(&self.rows[at]))
565 }
566
567 /// The name and tail of one row, by position.
568 #[inline]
569 #[must_use]
570 pub fn pair_at(&self, idx: usize) -> Option<(&[u8], &[u8])> {
571 let row = self.rows.get(idx)?;
572 Some((self.name_of(row), self.tail_of(row)))
573 }
574
575 /// Every name and tail, in insertion order. `HGETALL`.
576 pub fn pairs(&self) -> impl Iterator<Item = (&[u8], &[u8])> {
577 self.rows.iter().map(|r| (self.name_of(r), self.tail_of(r)))
578 }
579
580 /// Take an element out and hand back what it held.
581 ///
582 /// `SREM`, `HDEL` and the removing half of `SPOP`.
583 pub fn remove(&mut self, name: &[u8]) -> Option<V> {
584 let at = self.find(name)?;
585 Some(self.remove_row(at))
586 }
587
588 /// Take an element out, with its hash already in hand.
589 #[inline]
590 pub fn remove_hashed(&mut self, h: u64, name: &[u8]) -> Option<V> {
591 let at = self.find_hashed(h, name)?;
592 Some(self.remove_row(at))
593 }
594
595 /// Take the element at a position out, without looking its name up again.
596 ///
597 /// `SPOP` reads the name with [`Elements::at`], writes it into the reply,
598 /// and then calls this. That way the name is copied once, into the buffer it
599 /// was going to be copied into anyway, rather than into a `Vec` that exists
600 /// only to be dropped after the reply is framed.
601 pub fn remove_at(&mut self, idx: usize) -> Option<V> {
602 if idx >= self.rows.len() {
603 return None;
604 }
605 Some(self.remove_row(idx))
606 }
607
608 /// The name and payload of one row, by position.
609 ///
610 /// The dense draw. `SRANDMEMBER` picks a number under [`Elements::len`] and
611 /// calls this, and that is the whole operation: no walk, no ordered
612 /// structure, no allocation.
613 #[inline]
614 #[must_use]
615 pub fn at(&self, idx: usize) -> Option<(&[u8], &V)> {
616 let row = self.rows.get(idx)?;
617 Some((self.name_of(row), &self.vals[idx]))
618 }
619
620 /// The payload at `idx`, to be written over.
621 ///
622 /// The companion to [`Elements::index_of`], for a caller that has probed
623 /// once and wants to use the position it found rather than probe again.
624 #[inline]
625 pub fn at_mut(&mut self, idx: usize) -> Option<&mut V> {
626 self.vals.get_mut(idx)
627 }
628
629 /// Take the row at `idx` out and hand back its name and payload.
630 ///
631 /// The convenient form of a draw and a removal, for a caller that wants the
632 /// name and does not have a buffer to put it in. It allocates. The path that
633 /// answers a client uses [`Elements::at`] and then [`Elements::remove_at`]
634 /// and allocates nothing.
635 pub fn take_at(&mut self, idx: usize) -> Option<(Vec<u8>, V)> {
636 if idx >= self.rows.len() {
637 return None;
638 }
639 let name = self.name_of(&self.rows[idx]).to_vec();
640 let value = self.remove_row(idx);
641 Some((name, value))
642 }
643
644 /// Every element, in insertion order.
645 ///
646 /// The sequential walk. `HGETALL`, `SMEMBERS` and the scan cursor all read
647 /// the row array front to back, which is one stream of cache lines and no
648 /// pointer chasing.
649 pub fn iter(&self) -> impl Iterator<Item = (&[u8], &V)> {
650 self.rows
651 .iter()
652 .zip(&self.vals)
653 .map(|(r, v)| (self.name_of(r), v))
654 }
655
656 /// Every payload, to be changed in place, with no names in the way.
657 ///
658 /// For a payload that is a reference into somewhere else, which has to be
659 /// fixed up when that somewhere else moves. The names are deliberately not
660 /// offered here: this borrows the rows mutably, and handing out a name at
661 /// the same time would borrow the name blob as well for no caller that
662 /// wants it.
663 pub fn payloads_mut(&mut self) -> impl Iterator<Item = &mut V> {
664 self.vals.iter_mut()
665 }
666
667 /// Walk part of the table and say where to resume.
668 ///
669 /// This is `SSCAN`, `HSCAN` and `ZSCAN`. It reads downward from the cursor,
670 /// hands each element to `f`, and stops after `count` of them or at the
671 /// bottom, whichever comes first. A returned cursor that is
672 /// [`Cursor::is_end`] means the collection has been walked.
673 ///
674 /// Downward is what makes the guarantee hold while the collection is being
675 /// written, and [`crate::scan`] is where the argument for that lives. `count`
676 /// is a hint in Redis and a limit here, and a zero is read as one, because a
677 /// scan that returns nothing and the same cursor is a client that never
678 /// finishes.
679 ///
680 /// This band is one partition, so a cursor from a partitioned layout is
681 /// rebased onto it before anything is read.
682 pub fn scan<F>(&self, cursor: Cursor, count: usize, mut f: F) -> Cursor
683 where
684 F: FnMut(&[u8], &V),
685 {
686 self.scan_rows(cursor, count, |e, at| {
687 f(e.name_of(&e.rows[at]), &e.vals[at]);
688 })
689 }
690
691 /// [`Elements::scan`] handing back names and tails. This is `HSCAN`.
692 pub fn scan_pairs<F>(&self, cursor: Cursor, count: usize, mut f: F) -> Cursor
693 where
694 F: FnMut(&[u8], &[u8]),
695 {
696 self.scan_rows(cursor, count, |e, at| {
697 let row = &e.rows[at];
698 f(e.name_of(row), e.tail_of(row));
699 })
700 }
701
702 /// The walk itself, which does not care what is read out of each row.
703 fn scan_rows<F>(&self, cursor: Cursor, count: usize, mut f: F) -> Cursor
704 where
705 F: FnMut(&Elements<V>, usize),
706 {
707 if self.rows.is_empty() {
708 return Cursor::END;
709 }
710 let here = cursor.rebase(1);
711 let top = self.rows.len() - 1;
712 // A cursor from before a run of removals can name a row that is no
713 // longer there. Everything above the end has been walked already or was
714 // never there, so the top is the honest place to carry on from.
715 let mut at = match here.idx() {
716 Some(idx) => (idx as usize).min(top),
717 None => top,
718 };
719 for _ in 0..count.max(1) {
720 f(self, at);
721 if at == 0 {
722 return Cursor::END;
723 }
724 at -= 1;
725 }
726 Cursor::at(1, 0, at as u64)
727 }
728
729 /// Throw everything away and keep the allocations.
730 ///
731 /// Emptying a collection usually means it is about to be filled again, which
732 /// is `SINTERSTORE` over the same destination in a loop.
733 pub fn clear(&mut self) {
734 self.rows.clear();
735 self.vals.clear();
736 self.names.clear();
737 for slot in &mut self.slots {
738 *slot = EMPTY;
739 }
740 self.dead = 0;
741 }
742
743 /// What this table costs, not counting anything the payload points at.
744 ///
745 /// The payload is the caller's, so a value that lives in the arena is
746 /// counted by the arena and not twice here.
747 #[must_use]
748 pub fn memory_bytes(&self) -> usize {
749 self.slot_bytes() + self.row_bytes() + self.names.memory_bytes()
750 }
751
752 /// What the slot array costs on its own, for the memory measurements.
753 #[must_use]
754 pub fn slot_bytes(&self) -> usize {
755 self.slots.len() * size_of::<u32>()
756 }
757
758 /// What the row array costs on its own, capacity and not length, because
759 /// the slack a doubling `Vec` is holding is memory this table is using.
760 #[must_use]
761 pub fn row_bytes(&self) -> usize {
762 self.rows.capacity() * size_of::<Row>() + self.vals.capacity() * size_of::<V>()
763 }
764
765 /// What the name blob costs on its own, live bytes and dead ones together.
766 #[must_use]
767 pub fn name_bytes(&self) -> usize {
768 self.names.memory_bytes()
769 }
770
771 /// Name bytes no row points at any more.
772 ///
773 /// Reported rather than hidden, because a set that has been written and
774 /// rewritten holds them and `INFO memory` should say so.
775 #[inline]
776 #[must_use]
777 pub const fn dead_name_bytes(&self) -> usize {
778 self.names.dead()
779 }
780
781 /// Row index for a name, or nothing.
782 #[inline]
783 fn find(&self, name: &[u8]) -> Option<usize> {
784 self.find_hashed(hash(name), name)
785 }
786
787 /// The probe itself, with the hash already in hand.
788 ///
789 /// One load from the slot array. The tag in the top byte throws out a
790 /// collision on the low bits without touching the row, so the name
791 /// comparison below runs about once per hit and not once per probe.
792 ///
793 /// The stop is [`EMPTY`] and only [`EMPTY`], because a [`TOMB`] means
794 /// something used to be here and whatever probed past it is still behind it.
795 /// A marker cannot be mistaken for a match: its row bits are all ones and no
796 /// row index is, so the check that rejects it is on the arm the tag already
797 /// agreed with, which is one comparison in two hundred and fifty six.
798 #[inline]
799 fn find_hashed(&self, h: u64, name: &[u8]) -> Option<usize> {
800 if self.rows.is_empty() {
801 return None;
802 }
803 let mask = self.slots.len() - 1;
804 let tag = tag_of(h);
805 let mut at = (h as usize) & mask;
806 loop {
807 let slot = self.slots[at];
808 if slot == EMPTY {
809 return None;
810 }
811 if slot >> 24 == u32::from(tag) {
812 let row = slot & ROW;
813 if row != ROW && bytes_eq(self.name_of(&self.rows[row as usize]), name) {
814 return Some(row as usize);
815 }
816 }
817 at = (at + 1) & mask;
818 }
819 }
820
821 /// Put a row index in the first free slot the probe reaches.
822 ///
823 /// Free rather than empty, so an insert takes a marker back as soon as it
824 /// meets one. That is correct because the caller has already probed for this
825 /// name and not found it, and because a later probe for the same name walks
826 /// these slots in this order and stops only at an [`EMPTY`], which is at or
827 /// after wherever this lands.
828 fn put_slot(&mut self, h: u64, row: u32) {
829 let mask = self.slots.len() - 1;
830 let mut at = (h as usize) & mask;
831 while self.slots[at] & ROW != ROW {
832 at = (at + 1) & mask;
833 }
834 if self.slots[at] == TOMB {
835 self.dead -= 1;
836 }
837 self.slots[at] = (u32::from(tag_of(h)) << 24) | row;
838 }
839
840 /// Take the row at `at` out, keeping the row array dense.
841 fn remove_row(&mut self, at: usize) -> V {
842 let last = self.rows.len() - 1;
843 self.clear_slot(at);
844 if at != last {
845 // The last row moves into the hole, so the slot that pointed at the
846 // end now has to point here. One extra probe, which is what a draw
847 // being a single index costs.
848 self.repoint(last, at);
849 self.rows.swap(at, last);
850 self.vals.swap(at, last);
851 }
852 let row = self.rows.pop().expect("the table was not empty");
853 let value = self.vals.pop().expect("a payload per row");
854 let gone = self.footprint(&row);
855 self.names.release(gone);
856 self.maybe_compact_names();
857 value
858 }
859
860 /// Close the slot holding `row`.
861 ///
862 /// A slot whose neighbour is already [`EMPTY`] is a slot nothing ever probed
863 /// past, because a probe stops at the first empty one, so it can go straight
864 /// back to empty and cost nothing. Any run of markers directly behind it goes
865 /// with it, since the same argument now holds for each of them in turn, and
866 /// that is what makes a drain collect after itself.
867 ///
868 /// Otherwise the slot becomes a [`TOMB`], which says keep going and counts
869 /// against the load until the next rebuild.
870 fn clear_slot(&mut self, row: usize) {
871 let mask = self.slots.len() - 1;
872 let at = self.slot_of(row);
873 if self.slots[(at + 1) & mask] != EMPTY {
874 self.slots[at] = TOMB;
875 self.dead += 1;
876 return;
877 }
878 self.slots[at] = EMPTY;
879 // This terminates on the slot just emptied at the latest, so an array of
880 // nothing but markers is walked once and not forever.
881 let mut back = at.wrapping_sub(1) & mask;
882 while self.slots[back] == TOMB {
883 self.slots[back] = EMPTY;
884 self.dead -= 1;
885 back = back.wrapping_sub(1) & mask;
886 }
887 }
888
889 /// Point the slot holding `from` at `to` instead.
890 fn repoint(&mut self, from: usize, to: usize) {
891 let at = self.slot_of(from);
892 let to = u32::try_from(to).expect("a row index fits in 24 bits");
893 self.slots[at] = (self.slots[at] & !ROW) | to;
894 }
895
896 /// Which slot holds `row`.
897 ///
898 /// The row says where it wanted to sit, so this walks the same slots the
899 /// name would have walked without ever reading the name, and it matches on
900 /// the row index rather than on the tag because the tag is the one thing a
901 /// row does not keep. A marker cannot match, because its row bits are all
902 /// ones and no row index is.
903 fn slot_of(&self, row: usize) -> usize {
904 let mask = self.slots.len() - 1;
905 let want = u32::try_from(row).expect("a row index fits in 24 bits");
906 let mut at = self.home_of(row, mask);
907 loop {
908 debug_assert!(self.slots[at] != EMPTY, "the row being moved has a slot");
909 if self.slots[at] & ROW == want {
910 return at;
911 }
912 at = (at + 1) & mask;
913 }
914 }
915
916 /// Make sure there is room for one more before it is inserted.
917 ///
918 /// The row array grows by [`crate::grow`]'s policy rather than by `Vec`'s,
919 /// because a doubling row array on a large collection is the single largest
920 /// piece of memory nobody asked for in the whole structure. The slot array
921 /// keeps its power of two, which is not a policy, it is what makes the
922 /// probe a mask instead of a division.
923 fn reserve_one(&mut self) {
924 let want = self.rows.len() + 1;
925 crate::grow::reserve(&mut self.rows, 1);
926 crate::grow::reserve(&mut self.vals, 1);
927 // The markers are in here because they are what a probe has to walk
928 // past, so a table churned in place rebuilds on the same schedule as one
929 // that only grows. A rebuild the markers alone triggered comes back the
930 // same size or smaller and clears every one of them.
931 if (want + self.dead as usize) * LOAD_DEN > self.slots.len() * LOAD_NUM {
932 self.grow_to(slots_for(want));
933 }
934 }
935
936 /// Rebuild the slot array at a new size.
937 ///
938 /// The rows do not move and the names do not move. Only the slots are, and
939 /// they are rebuilt from the old slot array rather than from the names: the
940 /// tag is already in the old slot and the home is already in the row, so a
941 /// growth reads two flat arrays and hashes nothing.
942 fn grow_to(&mut self, slots: usize) {
943 let slots = slots.max(MIN_SLOTS).next_power_of_two();
944 let mask = slots - 1;
945 let old = std::mem::replace(&mut self.slots, vec![EMPTY; slots].into_boxed_slice());
946 self.dead = 0;
947 for &slot in &old {
948 if slot & ROW == ROW {
949 continue;
950 }
951 let row = (slot & ROW) as usize;
952 let mut at = self.home_of(row, mask);
953 while self.slots[at] != EMPTY {
954 at = (at + 1) & mask;
955 }
956 self.slots[at] = slot;
957 }
958 }
959
960 /// Where the row at `idx` wanted to sit, in a table with this `mask`.
961 ///
962 /// One comparison against a number the caller already had in a register, and
963 /// then a field of a row it was going to read anyway. The other arm is for a
964 /// table with more slots than a row has bits to name one, which costs a hash
965 /// and a trip into the blob and is the reason the row is eight bytes rather
966 /// than twelve for everybody else.
967 ///
968 /// The arms are split and the cold one is kept out of line because this is
969 /// called once per slot of the run behind a removal. Left as one function it
970 /// has a hash call in it, the call stops it being inlined into that loop, and
971 /// a pop of a thousand members measured 52 percent slower.
972 #[inline(always)]
973 fn home_of(&self, idx: usize, mask: usize) -> usize {
974 let row = self.rows[idx];
975 if mask < 1 << HOME_BITS {
976 row.home() & mask
977 } else {
978 self.home_by_hash(&row, mask)
979 }
980 }
981
982 /// Where a row wanted to sit in a table too large for the packed bits.
983 #[cold]
984 #[inline(never)]
985 fn home_by_hash(&self, row: &Row, mask: usize) -> usize {
986 hash(self.name_of(row)) as usize & mask
987 }
988
989 /// Append a name to the blob and say where it went.
990 ///
991 /// A long one goes in behind its own length, because a row has one byte to
992 /// say how long a name is and that is not enough for this one.
993 fn push_name(&mut self, name: &[u8]) -> u32 {
994 if name.len() < LONG_NAME {
995 return self.names.push(name);
996 }
997 let len = u16::try_from(name.len()).expect("the caller checked NAME_MAX");
998 let at = self.names.push(&len.to_le_bytes());
999 self.names.push(name);
1000 at
1001 }
1002
1003 /// The bytes of one row's name.
1004 #[inline(always)]
1005 fn name_of(&self, row: &Row) -> &[u8] {
1006 let len = row.len_byte();
1007 if len < LONG_NAME {
1008 self.names.read(row.at, len)
1009 } else {
1010 self.long_name(row.at)
1011 }
1012 }
1013
1014 /// The bytes of a name too long to measure in a row.
1015 #[cold]
1016 #[inline(never)]
1017 fn long_name(&self, at: u32) -> &[u8] {
1018 self.names.read(at + PREFIX as u32, self.long_len(at))
1019 }
1020
1021 /// The real length of a long name, from the bytes written ahead of it.
1022 #[inline]
1023 fn long_len(&self, at: u32) -> usize {
1024 let head = self.names.read(at, PREFIX);
1025 usize::from(u16::from_le_bytes([head[0], head[1]]))
1026 }
1027
1028 /// How many blob bytes one row's name occupies, its prefix included.
1029 #[inline(always)]
1030 fn name_span(&self, row: &Row) -> usize {
1031 let len = row.len_byte();
1032 if len < LONG_NAME {
1033 len
1034 } else {
1035 PREFIX + self.long_len(row.at)
1036 }
1037 }
1038
1039 /// How many blob bytes one row occupies, name and tail together.
1040 #[inline(always)]
1041 fn footprint(&self, row: &Row) -> usize {
1042 let name = self.name_span(row);
1043 if !self.tailed {
1044 return name;
1045 }
1046 name + self.tail_span(row.at + name as u32)
1047 }
1048
1049 /// Write a tail behind whatever was just pushed.
1050 ///
1051 /// A short one is a length byte and the bytes. A long one puts [`LONG_TAIL`]
1052 /// in the byte and the real length in the four behind it, which is the same
1053 /// shape [`Row`] uses for a long name and for the same reason: the common
1054 /// case pays one byte and the rare case pays for itself.
1055 fn push_tail(&mut self, tail: &[u8]) {
1056 if tail.len() < LONG_TAIL {
1057 self.names.push(&[tail.len() as u8]);
1058 } else {
1059 let len = u32::try_from(tail.len()).expect("a value is under four gigabytes");
1060 self.names.push(&[LONG_TAIL as u8]);
1061 self.names.push(&len.to_le_bytes());
1062 }
1063 self.names.push(tail);
1064 }
1065
1066 /// How long the tail at `at` is, and how many bytes its length took.
1067 #[inline]
1068 fn tail_head(&self, at: u32) -> (usize, usize) {
1069 let len = usize::from(self.names.read(at, 1)[0]);
1070 if len < LONG_TAIL {
1071 return (len, 1);
1072 }
1073 let head = self.names.read(at + 1, 4);
1074 let long = u32::from_le_bytes(head.try_into().expect("four bytes"));
1075 (long as usize, TAIL_PREFIX)
1076 }
1077
1078 /// How many blob bytes the tail at `at` occupies, its prefix included.
1079 #[inline]
1080 fn tail_span(&self, at: u32) -> usize {
1081 let (len, prefix) = self.tail_head(at);
1082 prefix + len
1083 }
1084
1085 /// The bytes of one row's tail.
1086 #[inline]
1087 fn tail_of(&self, row: &Row) -> &[u8] {
1088 let at = row.at + self.name_span(row) as u32;
1089 let (len, prefix) = self.tail_head(at);
1090 self.names.read(at + prefix as u32, len)
1091 }
1092
1093 /// How long one row's tail is, without reading it.
1094 #[inline]
1095 fn tail_len_of(&self, row: &Row) -> usize {
1096 let at = row.at + self.name_span(row) as u32;
1097 self.tail_head(at).0
1098 }
1099
1100 /// Give the dead name bytes back once there are more of them than live ones.
1101 ///
1102 /// The line and the floor are the blob's, and walking in row order is what
1103 /// leaves a name walk sequential afterwards.
1104 fn maybe_compact_names(&mut self) {
1105 if !self.names.worth_compacting() {
1106 return;
1107 }
1108 let rows = &mut self.rows;
1109 let tailed = self.tailed;
1110 self.names.compact(|keep| {
1111 for row in rows.iter_mut() {
1112 let len = row.len_byte();
1113 let mut take = if len < LONG_NAME {
1114 len
1115 } else {
1116 // The length is in the bytes rather than in the row, and the
1117 // blob this would normally read it from is half rebuilt, so
1118 // it comes off the old copy the rebuild is reading from.
1119 let head = keep.peek(row.at, PREFIX);
1120 PREFIX + usize::from(u16::from_le_bytes([head[0], head[1]]))
1121 };
1122 if tailed {
1123 // Same again for the tail, off the old copy for the same
1124 // reason, and it moves with the name because the two of them
1125 // are one span.
1126 let at = row.at + take as u32;
1127 let head = keep.peek(at, 1)[0];
1128 take += if usize::from(head) < LONG_TAIL {
1129 1 + usize::from(head)
1130 } else {
1131 let long = keep.peek(at + 1, 4);
1132 TAIL_PREFIX + u32::from_le_bytes(long.try_into().expect("four")) as usize
1133 };
1134 }
1135 keep.moved(&mut row.at, take);
1136 }
1137 });
1138 }
1139}
1140
1141/// Why an insert was refused.
1142///
1143/// Two ways, both of them a limit of this band rather than of Redis, and both
1144/// turned into Redis's own error text by the command layer above.
1145#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1146pub enum Full {
1147 /// The name is longer than [`NAME_MAX`].
1148 Name,
1149 /// The collection already holds [`MAX_ROWS`] elements.
1150 Rows,
1151}
1152
1153/// The hash a name is filed under.
1154///
1155/// wyhash at the shard's seed, the same call the key index makes, because a
1156/// field name and a key are the same kind of short byte string and there is no
1157/// reason to have two hashes in one process.
1158#[inline]
1159fn hash(name: &[u8]) -> u64 {
1160 hash_key(name)
1161}
1162
1163/// How many slots `n` elements need at the load factor.
1164fn slots_for(n: usize) -> usize {
1165 ((n * LOAD_DEN) / LOAD_NUM + 1)
1166 .max(MIN_SLOTS)
1167 .next_power_of_two()
1168}
1169
1170#[cfg(test)]
1171mod tests {
1172 use super::*;
1173 use crate::many;
1174
1175 /// A set is this table with nothing stored against a member.
1176 type Set = Elements<()>;
1177
1178 fn set(members: &[&[u8]]) -> Set {
1179 let mut s = Set::new();
1180 for m in members {
1181 s.insert(m, ()).expect("room");
1182 }
1183 s
1184 }
1185
1186 #[test]
1187 fn an_empty_table_allocates_nothing() {
1188 let e = Set::new();
1189 assert_eq!(e.len(), 0);
1190 assert!(e.is_empty());
1191 assert_eq!(e.memory_bytes(), 0);
1192 assert!(!e.contains(b"anything"));
1193 }
1194
1195 #[test]
1196 fn what_goes_in_comes_out() {
1197 let mut h: Elements<u64> = Elements::new();
1198 assert_eq!(h.insert(b"name", 7), Ok(None));
1199 assert_eq!(h.insert(b"age", 41), Ok(None));
1200 assert_eq!(h.get(b"name"), Some(&7));
1201 assert_eq!(h.get(b"age"), Some(&41));
1202 assert_eq!(h.get(b"missing"), None);
1203 assert_eq!(h.len(), 2);
1204 }
1205
1206 /// The number `HSET` reports is how many fields were new, so an overwrite
1207 /// has to be distinguishable from an insert.
1208 #[test]
1209 fn writing_a_field_again_replaces_it_and_says_so() {
1210 let mut h: Elements<u64> = Elements::new();
1211 assert_eq!(h.insert(b"f", 1), Ok(None));
1212 assert_eq!(h.insert(b"f", 2), Ok(Some(1)));
1213 assert_eq!(h.len(), 1, "an overwrite is not a second element");
1214 assert_eq!(h.get(b"f"), Some(&2));
1215 }
1216
1217 /// The name is written once. Rewriting a field is a row update and the blob
1218 /// does not move, which is what per collection interning is for.
1219 #[test]
1220 fn rewriting_a_field_does_not_write_its_name_again() {
1221 let mut h: Elements<u64> = Elements::new();
1222 h.insert(b"a-fairly-long-field-name", 1).expect("room");
1223 let after_first = h.memory_bytes();
1224 for i in 0..1000 {
1225 h.insert(b"a-fairly-long-field-name", i).expect("room");
1226 }
1227 assert_eq!(h.memory_bytes(), after_first);
1228 assert_eq!(h.dead_name_bytes(), 0);
1229 }
1230
1231 #[test]
1232 fn removing_takes_the_element_out() {
1233 let mut s = set(&[b"a", b"b", b"c"]);
1234 assert_eq!(s.remove(b"b"), Some(()));
1235 assert_eq!(s.remove(b"b"), None);
1236 assert_eq!(s.len(), 2);
1237 assert!(s.contains(b"a"));
1238 assert!(s.contains(b"c"));
1239 assert!(!s.contains(b"b"));
1240 }
1241
1242 /// The row array has no holes, so a draw is one index and never a retry.
1243 #[test]
1244 fn the_rows_stay_dense_through_removals() {
1245 let mut s = set(&[b"a", b"b", b"c", b"d", b"e"]);
1246 s.remove(b"a").expect("there");
1247 s.remove(b"c").expect("there");
1248 assert_eq!(s.len(), 3);
1249 let mut seen: Vec<Vec<u8>> = (0..s.len())
1250 .map(|i| s.at(i).expect("dense").0.to_vec())
1251 .collect();
1252 seen.sort();
1253 assert_eq!(seen, vec![b"b".to_vec(), b"d".to_vec(), b"e".to_vec()]);
1254 assert_eq!(s.at(3), None);
1255 }
1256
1257 /// This is the case a tombstone would ruin, so it is the case with a test.
1258 /// Every member goes in, every member comes out one draw at a time, and the
1259 /// table answers correctly the whole way down.
1260 #[test]
1261 fn a_set_drained_one_draw_at_a_time_stays_correct() {
1262 let names: Vec<Vec<u8>> = (0..many(500))
1263 .map(|i| format!("m{i}").into_bytes())
1264 .collect();
1265 let mut s = Set::new();
1266 for n in &names {
1267 s.insert(n, ()).expect("room");
1268 }
1269 let mut taken = Vec::new();
1270 // A fixed walk rather than a random one, because a test that draws
1271 // randomly and fails is a test nobody can rerun.
1272 while !s.is_empty() {
1273 let idx = (taken.len() * 7 + 3) % s.len();
1274 let (name, ()) = s.take_at(idx).expect("in range");
1275 assert!(!s.contains(&name), "it came out and stayed out");
1276 taken.push(name);
1277 }
1278 assert_eq!(taken.len(), names.len());
1279 taken.sort();
1280 let mut want = names;
1281 want.sort();
1282 assert_eq!(taken, want);
1283 }
1284
1285 /// Everything still probes correctly after a removal from the middle of a
1286 /// linear probe run, which is what the backward shift is for.
1287 #[test]
1288 fn removals_do_not_hide_what_is_behind_them() {
1289 let mut s = Set::new();
1290 let names: Vec<Vec<u8>> = (0..200u32).map(|i| format!("k{i}").into_bytes()).collect();
1291 for n in &names {
1292 s.insert(n, ()).expect("room");
1293 }
1294 for n in names.iter().step_by(3) {
1295 assert_eq!(s.remove(n), Some(()));
1296 }
1297 for (i, n) in names.iter().enumerate() {
1298 assert_eq!(s.contains(n), i % 3 != 0, "member {i}");
1299 }
1300 }
1301
1302 #[test]
1303 fn growth_keeps_everything_findable() {
1304 let n = many(5000);
1305 let names: Vec<Vec<u8>> = (0..n)
1306 .map(|i| format!("member-number-{i}").into_bytes())
1307 .collect();
1308 let mut s = Set::new();
1309 for name in &names {
1310 s.insert(name, ()).expect("room");
1311 }
1312 assert_eq!(s.len(), names.len());
1313 for name in &names {
1314 assert!(s.contains(name));
1315 }
1316 assert!(!s.contains(format!("member-number-{n}").as_bytes()));
1317 }
1318
1319 #[test]
1320 fn a_walk_reads_them_in_the_order_they_went_in() {
1321 let s = set(&[b"first", b"second", b"third"]);
1322 let seen: Vec<&[u8]> = s.iter().map(|(n, ())| n).collect();
1323 assert_eq!(seen, vec![&b"first"[..], &b"second"[..], &b"third"[..]]);
1324 }
1325
1326 #[test]
1327 fn presizing_does_not_change_what_the_table_says() {
1328 // The capacity and the member count are the same number on purpose: the
1329 // presized table is meant to be given exactly what it was asked for.
1330 let n = many(1000);
1331 let mut a = Set::with_capacity(n as usize);
1332 let mut b = Set::new();
1333 for i in 0..n {
1334 let name = format!("m{i}").into_bytes();
1335 a.insert(&name, ()).expect("room");
1336 b.insert(&name, ()).expect("room");
1337 }
1338 assert_eq!(a.len(), b.len());
1339 for i in 0..n {
1340 assert!(a.contains(format!("m{i}").as_bytes()));
1341 }
1342 }
1343
1344 #[test]
1345 fn a_name_that_is_too_long_is_refused_and_not_truncated() {
1346 let mut s = Set::new();
1347 let long = vec![b'x'; NAME_MAX + 1];
1348 assert_eq!(s.insert(&long, ()), Err(Full::Name));
1349 assert!(s.is_empty());
1350 let ok = vec![b'x'; NAME_MAX];
1351 assert_eq!(s.insert(&ok, ()), Ok(None));
1352 }
1353
1354 /// A row says how long a name is in one byte, and a name that does not fit
1355 /// in one byte keeps its length in the blob instead. Everything either side
1356 /// of that line has to read back as what went in, and the line itself is
1357 /// where an off by one lives, so this walks across it.
1358 #[test]
1359 fn a_name_too_long_to_measure_in_a_row_reads_back_whole() {
1360 // The five in the middle are the line and they stay whatever happens.
1361 // The last two are only there to be comfortably past it, and under Miri
1362 // they come down, because a name of NAME_MAX bytes is 64 kilobytes
1363 // hashed and copied on every insert, every lookup and every removal, and
1364 // that one length is most of what this test costs. The largest legal
1365 // name has a test of its own either way.
1366 let lens = if cfg!(miri) {
1367 [0, 1, 2, 253, 254, 255, 256, 257, 300, 600]
1368 } else {
1369 [0, 1, 2, 253, 254, 255, 256, 257, 1000, NAME_MAX]
1370 };
1371 // Distinct bytes per name as well as distinct lengths, so a read that
1372 // lands on the wrong name is not hidden by every name being x's.
1373 let names: Vec<Vec<u8>> = lens
1374 .iter()
1375 .enumerate()
1376 .map(|(i, &n)| vec![b'a' + u8::try_from(i).expect("under 26"); n])
1377 .collect();
1378
1379 let mut s = Set::new();
1380 for name in &names {
1381 assert_eq!(s.insert(name, ()), Ok(None), "length {}", name.len());
1382 }
1383 assert_eq!(s.len(), names.len(), "two of them collided into one row");
1384 for name in &names {
1385 assert!(s.contains(name), "length {} went missing", name.len());
1386 }
1387 let mut back: Vec<Vec<u8>> = s.iter().map(|(n, ())| n.to_vec()).collect();
1388 back.sort();
1389 let mut want = names.clone();
1390 want.sort();
1391 assert_eq!(back, want, "a walk gave back different bytes");
1392
1393 // And out again, one at a time, because a removal reads the length to
1394 // give the blob its bytes back and moves the last row into the hole.
1395 for (i, name) in names.iter().enumerate() {
1396 assert_eq!(s.remove(name), Some(()), "length {}", name.len());
1397 for later in &names[i + 1..] {
1398 assert!(s.contains(later), "length {} lost", later.len());
1399 }
1400 }
1401 assert!(s.is_empty());
1402 }
1403
1404 /// The same names through a blob rebuild, which is the one place that has to
1405 /// read a length out of bytes that are being moved underneath it.
1406 #[test]
1407 fn long_names_survive_the_blob_giving_its_dead_bytes_back() {
1408 let mut s = Set::new();
1409 let names: Vec<Vec<u8>> = (0..many(200))
1410 .map(|i| format!("{i:0>500}").into_bytes())
1411 .collect();
1412 for name in &names {
1413 s.insert(name, ()).expect("room");
1414 }
1415 let keep: Vec<Vec<u8>> = (0..many(100))
1416 .map(|i| format!("keep-{i:0>500}").into_bytes())
1417 .collect();
1418 for name in &keep {
1419 s.insert(name, ()).expect("room");
1420 }
1421 let before = s.name_bytes();
1422 // A hundred kilobytes of dead names against fifty of live ones, which is
1423 // over the floor and past the ratio, so the removals rebuild. Under Miri
1424 // it is ten against five, which is the same ratio and still well over
1425 // the floor.
1426 for name in &names {
1427 assert_eq!(s.remove(name), Some(()));
1428 }
1429 assert!(
1430 s.name_bytes() * 2 < before,
1431 "the rebuild never ran, the blob went from {before} to {}",
1432 s.name_bytes()
1433 );
1434 assert_eq!(s.len(), keep.len());
1435 for name in &keep {
1436 assert!(s.contains(name), "a long name moved wrongly");
1437 }
1438 let mut back: Vec<Vec<u8>> = s.iter().map(|(n, ())| n.to_vec()).collect();
1439 back.sort();
1440 let mut want = keep.clone();
1441 want.sort();
1442 assert_eq!(back, want);
1443 }
1444
1445 /// Dead name bytes are given back once there are more of them than live
1446 /// ones, and everything still reads correctly on the other side of it.
1447 #[test]
1448 fn dead_name_bytes_come_back() {
1449 let mut s = Set::new();
1450 // Fewer names under Miri but longer ones, because what has to hold is
1451 // that the dead bytes clear the 4096 byte floor below which the blob is
1452 // left alone. Ten names short of four hundred at 64 bytes is 25 kilobytes
1453 // dead, and ten short of a hundred at 256 is the same 25 kilobytes for a
1454 // quarter of the operations.
1455 let (count, width) = if cfg!(miri) { (100, 256) } else { (400, 64) };
1456 let long: Vec<Vec<u8>> = (0..count)
1457 .map(|i| format!("{i:0>width$}").into_bytes())
1458 .collect();
1459 for n in &long {
1460 s.insert(n, ()).expect("room");
1461 }
1462 let full = s.memory_bytes();
1463 for n in long.iter().take(count - 10) {
1464 s.remove(n).expect("there");
1465 }
1466 assert!(
1467 s.memory_bytes() < full,
1468 "the blob shrank, {} against {full}",
1469 s.memory_bytes()
1470 );
1471 // Not zero. What is left is under the floor, which is the point of
1472 // having a floor: a few hundred bytes are not worth a copy.
1473 assert!(
1474 s.dead_name_bytes() < 4096,
1475 "{} bytes left dead",
1476 s.dead_name_bytes()
1477 );
1478 for n in long.iter().skip(count - 10) {
1479 assert!(s.contains(n), "still findable after the blob moved");
1480 }
1481 }
1482
1483 #[test]
1484 fn clearing_keeps_the_allocation_and_forgets_the_elements() {
1485 let mut s = set(&[b"a", b"b", b"c"]);
1486 let before = s.memory_bytes();
1487 s.clear();
1488 assert!(s.is_empty());
1489 assert!(!s.contains(b"a"));
1490 assert_eq!(s.memory_bytes(), before, "the room is kept for the refill");
1491 s.insert(b"a", ()).expect("room");
1492 assert!(s.contains(b"a"));
1493 }
1494
1495 /// Both markers have their row bits all ones and a live slot never does,
1496 /// however the tag comes out, because the table refuses to hold enough rows
1497 /// to fill twenty four bits.
1498 #[test]
1499 fn no_live_slot_can_look_like_a_marker() {
1500 let mut s = Set::new();
1501 for i in 0..many(2000) {
1502 s.insert(format!("m{i}").as_bytes(), ()).expect("room");
1503 }
1504 let live = s.slots.iter().filter(|v| **v & ROW != ROW).count();
1505 assert_eq!(live, s.len());
1506 assert_eq!(s.dead, 0, "nothing has been removed yet");
1507 const { assert!(MAX_ROWS < ROW as usize, "a row index is never all ones") }
1508 }
1509
1510 /// Every live row is reachable by name and by row index, every slot is one
1511 /// of the three things a slot may be, and there is always somewhere for a
1512 /// probe to stop.
1513 fn check(s: &Set, names: &[Vec<u8>]) {
1514 assert_eq!(s.len(), names.len());
1515 let mut live = 0usize;
1516 let mut dead = 0usize;
1517 for &slot in &s.slots {
1518 if slot == EMPTY {
1519 } else if slot == TOMB {
1520 dead += 1;
1521 } else {
1522 assert!(slot & ROW != ROW, "a slot is live, empty or dead");
1523 assert!(((slot & ROW) as usize) < s.len(), "a live slot names a row");
1524 live += 1;
1525 }
1526 }
1527 assert_eq!(live, s.len(), "one live slot per row and no more");
1528 assert_eq!(
1529 dead, s.dead as usize,
1530 "the dead count is what is in the array"
1531 );
1532 assert!(
1533 s.len() + s.dead as usize <= s.slots.len() * LOAD_NUM / LOAD_DEN,
1534 "there is always an empty slot left for a probe to stop at"
1535 );
1536 for (i, name) in names.iter().enumerate() {
1537 assert_eq!(
1538 s.index_of(name),
1539 Some(i),
1540 "{name:?} is not where it was put"
1541 );
1542 assert!(s.slot_of(i) < s.slots.len(), "row {i} has no slot");
1543 }
1544 }
1545
1546 #[test]
1547 fn a_removal_leaves_the_table_whole() {
1548 let names: Vec<Vec<u8>> = (0..many(500))
1549 .map(|i| format!("m{i}").into_bytes())
1550 .collect();
1551 let mut s = Set::new();
1552 for name in &names {
1553 s.insert(name, ()).expect("room");
1554 }
1555
1556 // Every third one out, back to front, so the dense row array's swap
1557 // never moves something that has already been checked.
1558 let mut live = names.clone();
1559 for i in (0..live.len()).rev().step_by(3) {
1560 let gone = live.swap_remove(i);
1561 assert!(s.remove(&gone).is_some(), "{gone:?} was there");
1562 }
1563 check(&s, &live);
1564 for name in names.iter().filter(|n| !live.contains(n)) {
1565 assert!(!s.contains(name), "{name:?} came back");
1566 }
1567 }
1568
1569 /// The case the marker count exists for. Without it this loop leaves an
1570 /// array with no empty slot in it and the next probe never stops.
1571 #[test]
1572 fn a_table_churned_in_place_does_not_fill_up_with_markers() {
1573 // The two counts move together. What fills an array with markers is how
1574 // many removals happen per live member, so the churn has to stay ninety
1575 // nine times the population or there is nothing here to catch.
1576 let held = many(1000);
1577 let churn = many(100_000);
1578 let mut s = Set::new();
1579 for i in 0..held {
1580 s.insert(format!("m{i}").as_bytes(), ()).expect("room");
1581 }
1582 let slots = s.slots.len();
1583
1584 for i in held..churn {
1585 let gone = format!("m{}", i - held);
1586 assert!(s.remove(gone.as_bytes()).is_some());
1587 s.insert(format!("m{i}").as_bytes(), ()).expect("room");
1588 assert_eq!(s.len(), held as usize);
1589 }
1590 assert_eq!(s.slots.len(), slots, "the array is the size it started at");
1591 let live: Vec<Vec<u8>> = (churn - held..churn)
1592 .map(|i| format!("m{i}").into_bytes())
1593 .collect();
1594 for name in &live {
1595 assert!(s.contains(name), "{name:?} is missing after the churn");
1596 }
1597 }
1598
1599 /// A drain collects after itself. Every removal that meets an empty slot on
1600 /// its right takes the markers behind it with it, and by the time the last
1601 /// member is gone there is nothing left in the array at all.
1602 #[test]
1603 fn emptying_a_set_a_member_at_a_time_leaves_nothing_behind() {
1604 let names: Vec<Vec<u8>> = (0..many(2000))
1605 .map(|i| format!("m{i}").into_bytes())
1606 .collect();
1607 let mut s = Set::new();
1608 for name in &names {
1609 s.insert(name, ()).expect("room");
1610 }
1611 let slots = s.slots.len();
1612 for name in &names {
1613 assert!(s.remove(name).is_some(), "{name:?} was there");
1614 }
1615 assert!(s.is_empty());
1616 assert_eq!(s.dead, 0, "the drain cleared its own markers");
1617 assert!(s.slots.iter().all(|v| *v == EMPTY));
1618 for name in &names {
1619 assert!(!s.contains(name), "{name:?} came back");
1620 }
1621
1622 // And refilling reuses the array rather than growing past it.
1623 for name in &names {
1624 s.insert(name, ()).expect("room");
1625 }
1626 check(&s, &names);
1627 assert_eq!(s.slots.len(), slots, "the array is the size it was");
1628 }
1629
1630 /// The invariant the probe bound rests on. Live plus dead never goes up on a
1631 /// removal, so an unsuccessful probe is never longer after one than before.
1632 #[test]
1633 fn a_removal_never_makes_the_array_fuller() {
1634 let names: Vec<Vec<u8>> = (0..many(3000))
1635 .map(|i| format!("m{i}").into_bytes())
1636 .collect();
1637 let mut s = Set::new();
1638 for name in &names {
1639 s.insert(name, ()).expect("room");
1640 }
1641 let mut was = s.len() + s.dead as usize;
1642 // Out of order, so the runs are broken up rather than eaten from one end.
1643 for i in (0..names.len()).rev().step_by(7) {
1644 s.remove(&names[i]).expect("was there");
1645 let now = s.len() + s.dead as usize;
1646 assert!(
1647 now <= was,
1648 "{now} occupied against {was} before the removal"
1649 );
1650 was = now;
1651 }
1652 }
1653
1654 #[test]
1655 fn a_rebuild_clears_the_markers() {
1656 let n = many(1000);
1657 let mut s = Set::new();
1658 for i in 0..n {
1659 s.insert(format!("m{i}").as_bytes(), ()).expect("room");
1660 }
1661 // Out of order, so most of these leave a marker rather than clearing one.
1662 for i in (0..n).step_by(2) {
1663 s.remove(format!("m{i}").as_bytes()).expect("was there");
1664 }
1665 assert!(s.dead > 0, "some of those removals left a marker");
1666 s.grow_to(s.slots.len() * 2);
1667 assert_eq!(s.dead, 0, "and a rebuild took all of them");
1668 for i in (1..n).step_by(2) {
1669 assert!(s.contains(format!("m{i}").as_bytes()));
1670 }
1671 }
1672
1673 /// Collect a whole scan, a page at a time, the way a client loops.
1674 fn scan_all(s: &Set, page: usize) -> Vec<Vec<u8>> {
1675 let mut out = Vec::new();
1676 let mut c = Cursor::START;
1677 loop {
1678 c = s.scan(c, page, |n, ()| out.push(n.to_vec()));
1679 if c.is_end() {
1680 return out;
1681 }
1682 }
1683 }
1684
1685 #[test]
1686 fn a_scan_of_a_still_collection_returns_everything_once() {
1687 let names: Vec<Vec<u8>> = (0..many(300))
1688 .map(|i| format!("m{i}").into_bytes())
1689 .collect();
1690 let mut s = Set::new();
1691 for n in &names {
1692 s.insert(n, ()).expect("room");
1693 }
1694 for page in [1, 7, 10, 1000] {
1695 let mut seen = scan_all(&s, page);
1696 assert_eq!(seen.len(), names.len(), "page {page} returned a duplicate");
1697 seen.sort();
1698 let mut want = names.clone();
1699 want.sort();
1700 assert_eq!(seen, want, "page {page}");
1701 }
1702 }
1703
1704 #[test]
1705 fn scanning_an_empty_collection_is_over_immediately() {
1706 let s = Set::new();
1707 let mut hit = 0;
1708 assert!(s.scan(Cursor::START, 10, |_, ()| hit += 1).is_end());
1709 assert_eq!(hit, 0);
1710 }
1711
1712 /// The guarantee, which is the only reason the walk goes downward. Members
1713 /// are removed while the scan is running, and every member that was there
1714 /// the whole time has to come back at least once. Duplicates are allowed and
1715 /// are not what this is checking.
1716 #[test]
1717 fn a_scan_never_misses_a_member_that_stayed() {
1718 let names: Vec<Vec<u8>> = (0..many(400))
1719 .map(|i| format!("m{i}").into_bytes())
1720 .collect();
1721 let mut s = Set::new();
1722 for n in &names {
1723 s.insert(n, ()).expect("room");
1724 }
1725
1726 // Every seventh member goes away, a few at a time, in the middle of the
1727 // scan. Removal moves the top row into the hole, so this is the case
1728 // that would break an upward walk.
1729 let doomed: Vec<Vec<u8>> = names.iter().step_by(7).cloned().collect();
1730 let mut gone = 0usize;
1731 let mut seen: Vec<Vec<u8>> = Vec::new();
1732 let mut c = Cursor::START;
1733 loop {
1734 c = s.scan(c, 9, |n, ()| seen.push(n.to_vec()));
1735 for n in doomed.iter().skip(gone).take(3) {
1736 s.remove(n);
1737 }
1738 gone = (gone + 3).min(doomed.len());
1739 if c.is_end() {
1740 break;
1741 }
1742 }
1743
1744 for n in &names {
1745 if doomed.contains(n) {
1746 continue;
1747 }
1748 assert!(
1749 seen.contains(n),
1750 "{} was there all along",
1751 String::from_utf8_lossy(n)
1752 );
1753 }
1754 }
1755
1756 /// A cursor that names a row past the end, because the collection shrank
1757 /// under it, carries on rather than panicking or ending early.
1758 #[test]
1759 fn a_stale_cursor_is_answered_and_not_refused() {
1760 let s = set(&[b"a", b"b", b"c"]);
1761 let mut seen = Vec::new();
1762 let c = s.scan(Cursor::at(1, 0, 900), 2, |n, ()| seen.push(n.to_vec()));
1763 assert_eq!(seen, vec![b"c".to_vec(), b"b".to_vec()]);
1764 assert_eq!(c.idx(), Some(0));
1765
1766 // And one from a layout this band does not have.
1767 let mut also = Vec::new();
1768 s.scan(Cursor::at(16, 9, 4), 99, |n, ()| also.push(n.to_vec()));
1769 assert_eq!(also.len(), 3);
1770 }
1771
1772 /// The two ways to take an element out have to agree, because `SPOP` uses
1773 /// the one that does not allocate and `SREM` uses the one that looks a name
1774 /// up, and a set has to end up in the same state either way.
1775 #[test]
1776 fn taking_by_index_and_by_name_leave_the_same_table() {
1777 let mut by_index = set(&[b"a", b"b", b"c", b"d"]);
1778 let mut by_name = set(&[b"a", b"b", b"c", b"d"]);
1779 let name = by_index.at(1).expect("in range").0.to_vec();
1780 assert_eq!(by_index.remove_at(1), Some(()));
1781 assert_eq!(by_name.remove(&name), Some(()));
1782 assert_eq!(by_index.remove_at(99), None);
1783
1784 let mut left: Vec<Vec<u8>> = by_index.iter().map(|(n, ())| n.to_vec()).collect();
1785 let mut also: Vec<Vec<u8>> = by_name.iter().map(|(n, ())| n.to_vec()).collect();
1786 left.sort();
1787 also.sort();
1788 assert_eq!(left, also);
1789 assert_eq!(left.len(), 3);
1790 }
1791
1792 /// The row is the thing there are a million of, so its size is a decision
1793 /// and not an accident. Four bytes of blob offset, one of name length and
1794 /// three of home slot, with no padding anywhere in it.
1795 ///
1796 /// The payload is in an array of its own, so a score costs its eight bytes
1797 /// and not twelve. That is the whole reason for the split and it is worth a
1798 /// test, because putting the score back in the row would compile.
1799 #[test]
1800 fn a_row_is_eight_bytes_whatever_the_collection_stores() {
1801 assert_eq!(size_of::<Row>(), 8);
1802 assert_eq!(size_of::<Row>() + size_of::<()>(), 8, "a set member");
1803 assert_eq!(size_of::<Row>() + size_of::<f64>(), 16, "a sorted set");
1804 assert_eq!(size_of::<Row>() + size_of::<u32>(), 12, "a hash field");
1805 }
1806
1807 /// A tailed table for tests, since every one of them wants the same shape.
1808 fn tailed() -> Elements<()> {
1809 Elements::tailed(8, 64)
1810 }
1811
1812 #[test]
1813 fn a_tail_comes_back_whatever_length_it_is() {
1814 let mut t = tailed();
1815 let long = vec![b'z'; 4000];
1816 for (name, tail) in [
1817 (&b"empty"[..], &b""[..]),
1818 (b"one", b"1"),
1819 (b"short", b"a value"),
1820 (b"at254", &vec![b'y'; 254][..]),
1821 (b"at255", &vec![b'x'; 255][..]),
1822 (b"long", &long[..]),
1823 ] {
1824 t.set_tailed(name, tail, ()).expect("room");
1825 }
1826 assert_eq!(t.tail(b"empty"), Some(&b""[..]));
1827 assert_eq!(t.tail(b"one"), Some(&b"1"[..]));
1828 assert_eq!(t.tail(b"short"), Some(&b"a value"[..]));
1829 assert_eq!(t.tail_len(b"at254"), Some(254));
1830 assert_eq!(t.tail_len(b"at255"), Some(255), "past the one byte length");
1831 assert_eq!(t.tail(b"long"), Some(&long[..]));
1832 assert_eq!(t.tail(b"absent"), None);
1833 assert_eq!(t.len(), 6);
1834 }
1835
1836 #[test]
1837 fn rewriting_a_tail_leaves_every_other_row_alone() {
1838 let mut t = tailed();
1839 for i in 0..200 {
1840 t.set_tailed(format!("f{i:04}").as_bytes(), b"v", ())
1841 .expect("room");
1842 }
1843 // Longer, then shorter, then long enough to need the four byte length,
1844 // because each of those moves the row's span somewhere different.
1845 for tail in [&b"a much longer value than before"[..], b"x", &[b'q'; 900]] {
1846 let (row, fresh) = t.set_tailed(b"f0100", tail, ()).expect("room");
1847 assert!(!fresh, "the field was already there");
1848 assert_eq!(t.tail(b"f0100"), Some(tail));
1849 assert_eq!(t.pair_at(row).map(|(n, _)| n), Some(&b"f0100"[..]));
1850 }
1851 for i in 0..200 {
1852 let name = format!("f{i:04}");
1853 let want: &[u8] = if i == 100 { &[b'q'; 900] } else { b"v" };
1854 assert_eq!(t.tail(name.as_bytes()), Some(want), "field {i}");
1855 }
1856 }
1857
1858 #[test]
1859 fn a_compaction_keeps_names_and_tails_together() {
1860 let mut t = tailed();
1861 for i in 0..500 {
1862 t.set_tailed(format!("f{i:04}").as_bytes(), b"a value here", ())
1863 .expect("room");
1864 }
1865 for i in 0..400 {
1866 t.remove(format!("f{i:04}").as_bytes()).expect("there");
1867 }
1868 // Enough dead bytes to have crossed the compaction line by now, and the
1869 // rows that are left have to have moved with both halves of their span.
1870 for i in 400..500 {
1871 let name = format!("f{i:04}");
1872 assert_eq!(t.tail(name.as_bytes()), Some(&b"a value here"[..]), "{i}");
1873 }
1874 let pairs: Vec<_> = t.pairs().map(|(n, v)| (n.to_vec(), v.to_vec())).collect();
1875 assert_eq!(pairs.len(), 100);
1876 assert!(pairs.iter().all(|(_, v)| v == b"a value here"));
1877 }
1878
1879 #[test]
1880 fn a_payload_can_be_changed_in_place() {
1881 let mut h: Elements<i64> = Elements::new();
1882 h.insert(b"counter", 1).expect("room");
1883 *h.get_mut(b"counter").expect("there") += 41;
1884 assert_eq!(h.get(b"counter"), Some(&42));
1885 assert_eq!(h.get_mut(b"nothing"), None);
1886 }
1887}