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
1174 /// A set is this table with nothing stored against a member.
1175 type Set = Elements<()>;
1176
1177 fn set(members: &[&[u8]]) -> Set {
1178 let mut s = Set::new();
1179 for m in members {
1180 s.insert(m, ()).expect("room");
1181 }
1182 s
1183 }
1184
1185 #[test]
1186 fn an_empty_table_allocates_nothing() {
1187 let e = Set::new();
1188 assert_eq!(e.len(), 0);
1189 assert!(e.is_empty());
1190 assert_eq!(e.memory_bytes(), 0);
1191 assert!(!e.contains(b"anything"));
1192 }
1193
1194 #[test]
1195 fn what_goes_in_comes_out() {
1196 let mut h: Elements<u64> = Elements::new();
1197 assert_eq!(h.insert(b"name", 7), Ok(None));
1198 assert_eq!(h.insert(b"age", 41), Ok(None));
1199 assert_eq!(h.get(b"name"), Some(&7));
1200 assert_eq!(h.get(b"age"), Some(&41));
1201 assert_eq!(h.get(b"missing"), None);
1202 assert_eq!(h.len(), 2);
1203 }
1204
1205 /// The number `HSET` reports is how many fields were new, so an overwrite
1206 /// has to be distinguishable from an insert.
1207 #[test]
1208 fn writing_a_field_again_replaces_it_and_says_so() {
1209 let mut h: Elements<u64> = Elements::new();
1210 assert_eq!(h.insert(b"f", 1), Ok(None));
1211 assert_eq!(h.insert(b"f", 2), Ok(Some(1)));
1212 assert_eq!(h.len(), 1, "an overwrite is not a second element");
1213 assert_eq!(h.get(b"f"), Some(&2));
1214 }
1215
1216 /// The name is written once. Rewriting a field is a row update and the blob
1217 /// does not move, which is what per collection interning is for.
1218 #[test]
1219 fn rewriting_a_field_does_not_write_its_name_again() {
1220 let mut h: Elements<u64> = Elements::new();
1221 h.insert(b"a-fairly-long-field-name", 1).expect("room");
1222 let after_first = h.memory_bytes();
1223 for i in 0..1000 {
1224 h.insert(b"a-fairly-long-field-name", i).expect("room");
1225 }
1226 assert_eq!(h.memory_bytes(), after_first);
1227 assert_eq!(h.dead_name_bytes(), 0);
1228 }
1229
1230 #[test]
1231 fn removing_takes_the_element_out() {
1232 let mut s = set(&[b"a", b"b", b"c"]);
1233 assert_eq!(s.remove(b"b"), Some(()));
1234 assert_eq!(s.remove(b"b"), None);
1235 assert_eq!(s.len(), 2);
1236 assert!(s.contains(b"a"));
1237 assert!(s.contains(b"c"));
1238 assert!(!s.contains(b"b"));
1239 }
1240
1241 /// The row array has no holes, so a draw is one index and never a retry.
1242 #[test]
1243 fn the_rows_stay_dense_through_removals() {
1244 let mut s = set(&[b"a", b"b", b"c", b"d", b"e"]);
1245 s.remove(b"a").expect("there");
1246 s.remove(b"c").expect("there");
1247 assert_eq!(s.len(), 3);
1248 let mut seen: Vec<Vec<u8>> = (0..s.len())
1249 .map(|i| s.at(i).expect("dense").0.to_vec())
1250 .collect();
1251 seen.sort();
1252 assert_eq!(seen, vec![b"b".to_vec(), b"d".to_vec(), b"e".to_vec()]);
1253 assert_eq!(s.at(3), None);
1254 }
1255
1256 /// This is the case a tombstone would ruin, so it is the case with a test.
1257 /// Every member goes in, every member comes out one draw at a time, and the
1258 /// table answers correctly the whole way down.
1259 #[test]
1260 fn a_set_drained_one_draw_at_a_time_stays_correct() {
1261 let names: Vec<Vec<u8>> = (0..500u32).map(|i| format!("m{i}").into_bytes()).collect();
1262 let mut s = Set::new();
1263 for n in &names {
1264 s.insert(n, ()).expect("room");
1265 }
1266 let mut taken = Vec::new();
1267 // A fixed walk rather than a random one, because a test that draws
1268 // randomly and fails is a test nobody can rerun.
1269 while !s.is_empty() {
1270 let idx = (taken.len() * 7 + 3) % s.len();
1271 let (name, ()) = s.take_at(idx).expect("in range");
1272 assert!(!s.contains(&name), "it came out and stayed out");
1273 taken.push(name);
1274 }
1275 assert_eq!(taken.len(), names.len());
1276 taken.sort();
1277 let mut want = names;
1278 want.sort();
1279 assert_eq!(taken, want);
1280 }
1281
1282 /// Everything still probes correctly after a removal from the middle of a
1283 /// linear probe run, which is what the backward shift is for.
1284 #[test]
1285 fn removals_do_not_hide_what_is_behind_them() {
1286 let mut s = Set::new();
1287 let names: Vec<Vec<u8>> = (0..200u32).map(|i| format!("k{i}").into_bytes()).collect();
1288 for n in &names {
1289 s.insert(n, ()).expect("room");
1290 }
1291 for n in names.iter().step_by(3) {
1292 assert_eq!(s.remove(n), Some(()));
1293 }
1294 for (i, n) in names.iter().enumerate() {
1295 assert_eq!(s.contains(n), i % 3 != 0, "member {i}");
1296 }
1297 }
1298
1299 #[test]
1300 fn growth_keeps_everything_findable() {
1301 let names: Vec<Vec<u8>> = (0..5000u32)
1302 .map(|i| format!("member-number-{i}").into_bytes())
1303 .collect();
1304 let mut s = Set::new();
1305 for n in &names {
1306 s.insert(n, ()).expect("room");
1307 }
1308 assert_eq!(s.len(), names.len());
1309 for n in &names {
1310 assert!(s.contains(n));
1311 }
1312 assert!(!s.contains(b"member-number-5000"));
1313 }
1314
1315 #[test]
1316 fn a_walk_reads_them_in_the_order_they_went_in() {
1317 let s = set(&[b"first", b"second", b"third"]);
1318 let seen: Vec<&[u8]> = s.iter().map(|(n, ())| n).collect();
1319 assert_eq!(seen, vec![&b"first"[..], &b"second"[..], &b"third"[..]]);
1320 }
1321
1322 #[test]
1323 fn presizing_does_not_change_what_the_table_says() {
1324 let mut a = Set::with_capacity(1000);
1325 let mut b = Set::new();
1326 for i in 0..1000u32 {
1327 let n = format!("m{i}").into_bytes();
1328 a.insert(&n, ()).expect("room");
1329 b.insert(&n, ()).expect("room");
1330 }
1331 assert_eq!(a.len(), b.len());
1332 for i in 0..1000u32 {
1333 assert!(a.contains(format!("m{i}").as_bytes()));
1334 }
1335 }
1336
1337 #[test]
1338 fn a_name_that_is_too_long_is_refused_and_not_truncated() {
1339 let mut s = Set::new();
1340 let long = vec![b'x'; NAME_MAX + 1];
1341 assert_eq!(s.insert(&long, ()), Err(Full::Name));
1342 assert!(s.is_empty());
1343 let ok = vec![b'x'; NAME_MAX];
1344 assert_eq!(s.insert(&ok, ()), Ok(None));
1345 }
1346
1347 /// A row says how long a name is in one byte, and a name that does not fit
1348 /// in one byte keeps its length in the blob instead. Everything either side
1349 /// of that line has to read back as what went in, and the line itself is
1350 /// where an off by one lives, so this walks across it.
1351 #[test]
1352 fn a_name_too_long_to_measure_in_a_row_reads_back_whole() {
1353 let lens = [0, 1, 2, 253, 254, 255, 256, 257, 1000, NAME_MAX];
1354 // Distinct bytes per name as well as distinct lengths, so a read that
1355 // lands on the wrong name is not hidden by every name being x's.
1356 let names: Vec<Vec<u8>> = lens
1357 .iter()
1358 .enumerate()
1359 .map(|(i, &n)| vec![b'a' + u8::try_from(i).expect("under 26"); n])
1360 .collect();
1361
1362 let mut s = Set::new();
1363 for name in &names {
1364 assert_eq!(s.insert(name, ()), Ok(None), "length {}", name.len());
1365 }
1366 assert_eq!(s.len(), names.len(), "two of them collided into one row");
1367 for name in &names {
1368 assert!(s.contains(name), "length {} went missing", name.len());
1369 }
1370 let mut back: Vec<Vec<u8>> = s.iter().map(|(n, ())| n.to_vec()).collect();
1371 back.sort();
1372 let mut want = names.clone();
1373 want.sort();
1374 assert_eq!(back, want, "a walk gave back different bytes");
1375
1376 // And out again, one at a time, because a removal reads the length to
1377 // give the blob its bytes back and moves the last row into the hole.
1378 for (i, name) in names.iter().enumerate() {
1379 assert_eq!(s.remove(name), Some(()), "length {}", name.len());
1380 for later in &names[i + 1..] {
1381 assert!(s.contains(later), "length {} lost", later.len());
1382 }
1383 }
1384 assert!(s.is_empty());
1385 }
1386
1387 /// The same names through a blob rebuild, which is the one place that has to
1388 /// read a length out of bytes that are being moved underneath it.
1389 #[test]
1390 fn long_names_survive_the_blob_giving_its_dead_bytes_back() {
1391 let mut s = Set::new();
1392 let names: Vec<Vec<u8>> = (0..200u32)
1393 .map(|i| format!("{i:0>500}").into_bytes())
1394 .collect();
1395 for name in &names {
1396 s.insert(name, ()).expect("room");
1397 }
1398 let keep: Vec<Vec<u8>> = (0..100u32)
1399 .map(|i| format!("keep-{i:0>500}").into_bytes())
1400 .collect();
1401 for name in &keep {
1402 s.insert(name, ()).expect("room");
1403 }
1404 let before = s.name_bytes();
1405 // A hundred kilobytes of dead names against fifty of live ones, which is
1406 // over the floor and past the ratio, so the removals rebuild.
1407 for name in &names {
1408 assert_eq!(s.remove(name), Some(()));
1409 }
1410 assert!(
1411 s.name_bytes() * 2 < before,
1412 "the rebuild never ran, the blob went from {before} to {}",
1413 s.name_bytes()
1414 );
1415 assert_eq!(s.len(), keep.len());
1416 for name in &keep {
1417 assert!(s.contains(name), "a long name moved wrongly");
1418 }
1419 let mut back: Vec<Vec<u8>> = s.iter().map(|(n, ())| n.to_vec()).collect();
1420 back.sort();
1421 let mut want = keep.clone();
1422 want.sort();
1423 assert_eq!(back, want);
1424 }
1425
1426 /// Dead name bytes are given back once there are more of them than live
1427 /// ones, and everything still reads correctly on the other side of it.
1428 #[test]
1429 fn dead_name_bytes_come_back() {
1430 let mut s = Set::new();
1431 let long: Vec<Vec<u8>> = (0..400u32)
1432 .map(|i| format!("{i:0>64}").into_bytes())
1433 .collect();
1434 for n in &long {
1435 s.insert(n, ()).expect("room");
1436 }
1437 let full = s.memory_bytes();
1438 for n in long.iter().take(390) {
1439 s.remove(n).expect("there");
1440 }
1441 assert!(
1442 s.memory_bytes() < full,
1443 "the blob shrank, {} against {full}",
1444 s.memory_bytes()
1445 );
1446 // Not zero. What is left is under the floor, which is the point of
1447 // having a floor: a few hundred bytes are not worth a copy.
1448 assert!(
1449 s.dead_name_bytes() < 4096,
1450 "{} bytes left dead",
1451 s.dead_name_bytes()
1452 );
1453 for n in long.iter().skip(390) {
1454 assert!(s.contains(n), "still findable after the blob moved");
1455 }
1456 }
1457
1458 #[test]
1459 fn clearing_keeps_the_allocation_and_forgets_the_elements() {
1460 let mut s = set(&[b"a", b"b", b"c"]);
1461 let before = s.memory_bytes();
1462 s.clear();
1463 assert!(s.is_empty());
1464 assert!(!s.contains(b"a"));
1465 assert_eq!(s.memory_bytes(), before, "the room is kept for the refill");
1466 s.insert(b"a", ()).expect("room");
1467 assert!(s.contains(b"a"));
1468 }
1469
1470 /// Both markers have their row bits all ones and a live slot never does,
1471 /// however the tag comes out, because the table refuses to hold enough rows
1472 /// to fill twenty four bits.
1473 #[test]
1474 fn no_live_slot_can_look_like_a_marker() {
1475 let mut s = Set::new();
1476 for i in 0..2000u32 {
1477 s.insert(format!("m{i}").as_bytes(), ()).expect("room");
1478 }
1479 let live = s.slots.iter().filter(|v| **v & ROW != ROW).count();
1480 assert_eq!(live, s.len());
1481 assert_eq!(s.dead, 0, "nothing has been removed yet");
1482 const { assert!(MAX_ROWS < ROW as usize, "a row index is never all ones") }
1483 }
1484
1485 /// Every live row is reachable by name and by row index, every slot is one
1486 /// of the three things a slot may be, and there is always somewhere for a
1487 /// probe to stop.
1488 fn check(s: &Set, names: &[Vec<u8>]) {
1489 assert_eq!(s.len(), names.len());
1490 let mut live = 0usize;
1491 let mut dead = 0usize;
1492 for &slot in &s.slots {
1493 if slot == EMPTY {
1494 } else if slot == TOMB {
1495 dead += 1;
1496 } else {
1497 assert!(slot & ROW != ROW, "a slot is live, empty or dead");
1498 assert!(((slot & ROW) as usize) < s.len(), "a live slot names a row");
1499 live += 1;
1500 }
1501 }
1502 assert_eq!(live, s.len(), "one live slot per row and no more");
1503 assert_eq!(
1504 dead, s.dead as usize,
1505 "the dead count is what is in the array"
1506 );
1507 assert!(
1508 s.len() + s.dead as usize <= s.slots.len() * LOAD_NUM / LOAD_DEN,
1509 "there is always an empty slot left for a probe to stop at"
1510 );
1511 for (i, name) in names.iter().enumerate() {
1512 assert_eq!(
1513 s.index_of(name),
1514 Some(i),
1515 "{name:?} is not where it was put"
1516 );
1517 assert!(s.slot_of(i) < s.slots.len(), "row {i} has no slot");
1518 }
1519 }
1520
1521 #[test]
1522 fn a_removal_leaves_the_table_whole() {
1523 let names: Vec<Vec<u8>> = (0..500u32).map(|i| format!("m{i}").into_bytes()).collect();
1524 let mut s = Set::new();
1525 for name in &names {
1526 s.insert(name, ()).expect("room");
1527 }
1528
1529 // Every third one out, back to front, so the dense row array's swap
1530 // never moves something that has already been checked.
1531 let mut live = names.clone();
1532 for i in (0..live.len()).rev().step_by(3) {
1533 let gone = live.swap_remove(i);
1534 assert!(s.remove(&gone).is_some(), "{gone:?} was there");
1535 }
1536 check(&s, &live);
1537 for name in names.iter().filter(|n| !live.contains(n)) {
1538 assert!(!s.contains(name), "{name:?} came back");
1539 }
1540 }
1541
1542 /// The case the marker count exists for. Without it this loop leaves an
1543 /// array with no empty slot in it and the next probe never stops.
1544 #[test]
1545 fn a_table_churned_in_place_does_not_fill_up_with_markers() {
1546 let mut s = Set::new();
1547 for i in 0..1000u32 {
1548 s.insert(format!("m{i}").as_bytes(), ()).expect("room");
1549 }
1550 let slots = s.slots.len();
1551
1552 for i in 1000..100_000u32 {
1553 let gone = format!("m{}", i - 1000);
1554 assert!(s.remove(gone.as_bytes()).is_some());
1555 s.insert(format!("m{i}").as_bytes(), ()).expect("room");
1556 assert_eq!(s.len(), 1000);
1557 }
1558 assert_eq!(s.slots.len(), slots, "the array is the size it started at");
1559 let live: Vec<Vec<u8>> = (99_000..100_000u32)
1560 .map(|i| format!("m{i}").into_bytes())
1561 .collect();
1562 for name in &live {
1563 assert!(s.contains(name), "{name:?} is missing after the churn");
1564 }
1565 }
1566
1567 /// A drain collects after itself. Every removal that meets an empty slot on
1568 /// its right takes the markers behind it with it, and by the time the last
1569 /// member is gone there is nothing left in the array at all.
1570 #[test]
1571 fn emptying_a_set_a_member_at_a_time_leaves_nothing_behind() {
1572 let names: Vec<Vec<u8>> = (0..2000u32).map(|i| format!("m{i}").into_bytes()).collect();
1573 let mut s = Set::new();
1574 for name in &names {
1575 s.insert(name, ()).expect("room");
1576 }
1577 let slots = s.slots.len();
1578 for name in &names {
1579 assert!(s.remove(name).is_some(), "{name:?} was there");
1580 }
1581 assert!(s.is_empty());
1582 assert_eq!(s.dead, 0, "the drain cleared its own markers");
1583 assert!(s.slots.iter().all(|v| *v == EMPTY));
1584 for name in &names {
1585 assert!(!s.contains(name), "{name:?} came back");
1586 }
1587
1588 // And refilling reuses the array rather than growing past it.
1589 for name in &names {
1590 s.insert(name, ()).expect("room");
1591 }
1592 check(&s, &names);
1593 assert_eq!(s.slots.len(), slots, "the array is the size it was");
1594 }
1595
1596 /// The invariant the probe bound rests on. Live plus dead never goes up on a
1597 /// removal, so an unsuccessful probe is never longer after one than before.
1598 #[test]
1599 fn a_removal_never_makes_the_array_fuller() {
1600 let names: Vec<Vec<u8>> = (0..3000u32).map(|i| format!("m{i}").into_bytes()).collect();
1601 let mut s = Set::new();
1602 for name in &names {
1603 s.insert(name, ()).expect("room");
1604 }
1605 let mut was = s.len() + s.dead as usize;
1606 // Out of order, so the runs are broken up rather than eaten from one end.
1607 for i in (0..names.len()).rev().step_by(7) {
1608 s.remove(&names[i]).expect("was there");
1609 let now = s.len() + s.dead as usize;
1610 assert!(
1611 now <= was,
1612 "{now} occupied against {was} before the removal"
1613 );
1614 was = now;
1615 }
1616 }
1617
1618 #[test]
1619 fn a_rebuild_clears_the_markers() {
1620 let mut s = Set::new();
1621 for i in 0..1000u32 {
1622 s.insert(format!("m{i}").as_bytes(), ()).expect("room");
1623 }
1624 // Out of order, so most of these leave a marker rather than clearing one.
1625 for i in (0..1000u32).step_by(2) {
1626 s.remove(format!("m{i}").as_bytes()).expect("was there");
1627 }
1628 assert!(s.dead > 0, "some of those removals left a marker");
1629 s.grow_to(s.slots.len() * 2);
1630 assert_eq!(s.dead, 0, "and a rebuild took all of them");
1631 for i in (1..1000u32).step_by(2) {
1632 assert!(s.contains(format!("m{i}").as_bytes()));
1633 }
1634 }
1635
1636 /// Collect a whole scan, a page at a time, the way a client loops.
1637 fn scan_all(s: &Set, page: usize) -> Vec<Vec<u8>> {
1638 let mut out = Vec::new();
1639 let mut c = Cursor::START;
1640 loop {
1641 c = s.scan(c, page, |n, ()| out.push(n.to_vec()));
1642 if c.is_end() {
1643 return out;
1644 }
1645 }
1646 }
1647
1648 #[test]
1649 fn a_scan_of_a_still_collection_returns_everything_once() {
1650 let names: Vec<Vec<u8>> = (0..300u32).map(|i| format!("m{i}").into_bytes()).collect();
1651 let mut s = Set::new();
1652 for n in &names {
1653 s.insert(n, ()).expect("room");
1654 }
1655 for page in [1, 7, 10, 1000] {
1656 let mut seen = scan_all(&s, page);
1657 assert_eq!(seen.len(), names.len(), "page {page} returned a duplicate");
1658 seen.sort();
1659 let mut want = names.clone();
1660 want.sort();
1661 assert_eq!(seen, want, "page {page}");
1662 }
1663 }
1664
1665 #[test]
1666 fn scanning_an_empty_collection_is_over_immediately() {
1667 let s = Set::new();
1668 let mut hit = 0;
1669 assert!(s.scan(Cursor::START, 10, |_, ()| hit += 1).is_end());
1670 assert_eq!(hit, 0);
1671 }
1672
1673 /// The guarantee, which is the only reason the walk goes downward. Members
1674 /// are removed while the scan is running, and every member that was there
1675 /// the whole time has to come back at least once. Duplicates are allowed and
1676 /// are not what this is checking.
1677 #[test]
1678 fn a_scan_never_misses_a_member_that_stayed() {
1679 let names: Vec<Vec<u8>> = (0..400u32).map(|i| format!("m{i}").into_bytes()).collect();
1680 let mut s = Set::new();
1681 for n in &names {
1682 s.insert(n, ()).expect("room");
1683 }
1684
1685 // Every seventh member goes away, a few at a time, in the middle of the
1686 // scan. Removal moves the top row into the hole, so this is the case
1687 // that would break an upward walk.
1688 let doomed: Vec<Vec<u8>> = names.iter().step_by(7).cloned().collect();
1689 let mut gone = 0usize;
1690 let mut seen: Vec<Vec<u8>> = Vec::new();
1691 let mut c = Cursor::START;
1692 loop {
1693 c = s.scan(c, 9, |n, ()| seen.push(n.to_vec()));
1694 for n in doomed.iter().skip(gone).take(3) {
1695 s.remove(n);
1696 }
1697 gone = (gone + 3).min(doomed.len());
1698 if c.is_end() {
1699 break;
1700 }
1701 }
1702
1703 for n in &names {
1704 if doomed.contains(n) {
1705 continue;
1706 }
1707 assert!(
1708 seen.contains(n),
1709 "{} was there all along",
1710 String::from_utf8_lossy(n)
1711 );
1712 }
1713 }
1714
1715 /// A cursor that names a row past the end, because the collection shrank
1716 /// under it, carries on rather than panicking or ending early.
1717 #[test]
1718 fn a_stale_cursor_is_answered_and_not_refused() {
1719 let s = set(&[b"a", b"b", b"c"]);
1720 let mut seen = Vec::new();
1721 let c = s.scan(Cursor::at(1, 0, 900), 2, |n, ()| seen.push(n.to_vec()));
1722 assert_eq!(seen, vec![b"c".to_vec(), b"b".to_vec()]);
1723 assert_eq!(c.idx(), Some(0));
1724
1725 // And one from a layout this band does not have.
1726 let mut also = Vec::new();
1727 s.scan(Cursor::at(16, 9, 4), 99, |n, ()| also.push(n.to_vec()));
1728 assert_eq!(also.len(), 3);
1729 }
1730
1731 /// The two ways to take an element out have to agree, because `SPOP` uses
1732 /// the one that does not allocate and `SREM` uses the one that looks a name
1733 /// up, and a set has to end up in the same state either way.
1734 #[test]
1735 fn taking_by_index_and_by_name_leave_the_same_table() {
1736 let mut by_index = set(&[b"a", b"b", b"c", b"d"]);
1737 let mut by_name = set(&[b"a", b"b", b"c", b"d"]);
1738 let name = by_index.at(1).expect("in range").0.to_vec();
1739 assert_eq!(by_index.remove_at(1), Some(()));
1740 assert_eq!(by_name.remove(&name), Some(()));
1741 assert_eq!(by_index.remove_at(99), None);
1742
1743 let mut left: Vec<Vec<u8>> = by_index.iter().map(|(n, ())| n.to_vec()).collect();
1744 let mut also: Vec<Vec<u8>> = by_name.iter().map(|(n, ())| n.to_vec()).collect();
1745 left.sort();
1746 also.sort();
1747 assert_eq!(left, also);
1748 assert_eq!(left.len(), 3);
1749 }
1750
1751 /// The row is the thing there are a million of, so its size is a decision
1752 /// and not an accident. Four bytes of blob offset, one of name length and
1753 /// three of home slot, with no padding anywhere in it.
1754 ///
1755 /// The payload is in an array of its own, so a score costs its eight bytes
1756 /// and not twelve. That is the whole reason for the split and it is worth a
1757 /// test, because putting the score back in the row would compile.
1758 #[test]
1759 fn a_row_is_eight_bytes_whatever_the_collection_stores() {
1760 assert_eq!(size_of::<Row>(), 8);
1761 assert_eq!(size_of::<Row>() + size_of::<()>(), 8, "a set member");
1762 assert_eq!(size_of::<Row>() + size_of::<f64>(), 16, "a sorted set");
1763 assert_eq!(size_of::<Row>() + size_of::<u32>(), 12, "a hash field");
1764 }
1765
1766 /// A tailed table for tests, since every one of them wants the same shape.
1767 fn tailed() -> Elements<()> {
1768 Elements::tailed(8, 64)
1769 }
1770
1771 #[test]
1772 fn a_tail_comes_back_whatever_length_it_is() {
1773 let mut t = tailed();
1774 let long = vec![b'z'; 4000];
1775 for (name, tail) in [
1776 (&b"empty"[..], &b""[..]),
1777 (b"one", b"1"),
1778 (b"short", b"a value"),
1779 (b"at254", &vec![b'y'; 254][..]),
1780 (b"at255", &vec![b'x'; 255][..]),
1781 (b"long", &long[..]),
1782 ] {
1783 t.set_tailed(name, tail, ()).expect("room");
1784 }
1785 assert_eq!(t.tail(b"empty"), Some(&b""[..]));
1786 assert_eq!(t.tail(b"one"), Some(&b"1"[..]));
1787 assert_eq!(t.tail(b"short"), Some(&b"a value"[..]));
1788 assert_eq!(t.tail_len(b"at254"), Some(254));
1789 assert_eq!(t.tail_len(b"at255"), Some(255), "past the one byte length");
1790 assert_eq!(t.tail(b"long"), Some(&long[..]));
1791 assert_eq!(t.tail(b"absent"), None);
1792 assert_eq!(t.len(), 6);
1793 }
1794
1795 #[test]
1796 fn rewriting_a_tail_leaves_every_other_row_alone() {
1797 let mut t = tailed();
1798 for i in 0..200 {
1799 t.set_tailed(format!("f{i:04}").as_bytes(), b"v", ())
1800 .expect("room");
1801 }
1802 // Longer, then shorter, then long enough to need the four byte length,
1803 // because each of those moves the row's span somewhere different.
1804 for tail in [&b"a much longer value than before"[..], b"x", &[b'q'; 900]] {
1805 let (row, fresh) = t.set_tailed(b"f0100", tail, ()).expect("room");
1806 assert!(!fresh, "the field was already there");
1807 assert_eq!(t.tail(b"f0100"), Some(tail));
1808 assert_eq!(t.pair_at(row).map(|(n, _)| n), Some(&b"f0100"[..]));
1809 }
1810 for i in 0..200 {
1811 let name = format!("f{i:04}");
1812 let want: &[u8] = if i == 100 { &[b'q'; 900] } else { b"v" };
1813 assert_eq!(t.tail(name.as_bytes()), Some(want), "field {i}");
1814 }
1815 }
1816
1817 #[test]
1818 fn a_compaction_keeps_names_and_tails_together() {
1819 let mut t = tailed();
1820 for i in 0..500 {
1821 t.set_tailed(format!("f{i:04}").as_bytes(), b"a value here", ())
1822 .expect("room");
1823 }
1824 for i in 0..400 {
1825 t.remove(format!("f{i:04}").as_bytes()).expect("there");
1826 }
1827 // Enough dead bytes to have crossed the compaction line by now, and the
1828 // rows that are left have to have moved with both halves of their span.
1829 for i in 400..500 {
1830 let name = format!("f{i:04}");
1831 assert_eq!(t.tail(name.as_bytes()), Some(&b"a value here"[..]), "{i}");
1832 }
1833 let pairs: Vec<_> = t.pairs().map(|(n, v)| (n.to_vec(), v.to_vec())).collect();
1834 assert_eq!(pairs.len(), 100);
1835 assert!(pairs.iter().all(|(_, v)| v == b"a value here"));
1836 }
1837
1838 #[test]
1839 fn a_payload_can_be_changed_in_place() {
1840 let mut h: Elements<i64> = Elements::new();
1841 h.insert(b"counter", 1).expect("room");
1842 *h.get_mut(b"counter").expect("there") += 41;
1843 assert_eq!(h.get(b"counter"), Some(&42));
1844 assert_eq!(h.get_mut(b"nothing"), None);
1845 }
1846}