Skip to main content

yo_kv/
hashes.rs

1//! The hash commands.
2//!
3//! One method per Redis command on [`Keyspace`], the same arrangement the set
4//! and string commands use. The hash itself, and the choice between the two
5//! representations it can be in, is [`crate::hash`]. This file is what the wire
6//! and the embedded API both call.
7//!
8//! # Where a hash lives
9//!
10//! Exactly where a set lives. The record under the key holds a type tag and four
11//! bytes saying which slot of the database's hash slab the body is in, and
12//! reaching it is one key lookup and one dependent load. The two slabs are
13//! separate rather than one slab of an enum, because the record's tag already
14//! says which one to look in and a discriminant on the body would be a second
15//! copy of a fact that is already there.
16//!
17//! The same two invariants hold, and both are about not leaking. Every path that
18//! deletes a key goes through `drop_key` and every path that writes over one
19//! goes through `free_body`. And a hash that loses its last field is deleted
20//! rather than stored empty, because an empty hash does not exist in Redis:
21//! `HDEL` taking the last field makes `EXISTS` answer zero.
22//!
23//! # Returning a field's value
24//!
25//! A value in the listpack band may be stored as an integer, so there is no
26//! `&[u8]` to hand back for it without writing the digits somewhere first. The
27//! reading commands take a closure and hand it a [`Text`] instead, which the
28//! reply layer formats straight into the output buffer. That is Y18, and it is
29//! why `HGET` is not simply `-> Option<&[u8]>`.
30//!
31//! # Errors
32//!
33//! Every command here answers `WRONGTYPE` for a key holding something that is
34//! not a hash, and treats a missing key as an empty one.
35
36use yo_common::num::{parse_f64, parse_i64};
37use yo_common::{Code, Error, Result};
38
39use crate::hash::{Hash, Text};
40use crate::keyspace::Keyspace;
41use crate::news;
42use crate::scan::Cursor;
43use crate::strings;
44use crate::ttl::{self, Applied, Ask, Cond};
45use crate::value::{self, Kind};
46
47/// What Redis says when a field does not hold a number.
48const NOT_AN_INT: &str = "hash value is not an integer";
49/// And when it does not hold a float.
50const NOT_A_FLOAT: &str = "hash value is not a float";
51/// And when the sum leaves the range.
52const WOULD_OVERFLOW: &str = "increment or decrement would overflow";
53/// And when a field deadline lands past the year it stops fitting.
54const BAD_EXPIRE: &str = "invalid expire time, must be >= 0";
55
56impl Keyspace {
57    /// `HSET key field value [field value ...]`. Answers how many were new.
58    ///
59    /// The pairs arrive as an iterator for the reason `SADD`'s members do: the
60    /// wire layer has them as positions in the connection's read buffer, and
61    /// collecting them into a slice first would be an allocation per command on
62    /// a shard thread.
63    ///
64    /// Redis's parser rejects an odd number of arguments before this is reached.
65    /// The embedded API has no parser in front of it, so an empty iterator does
66    /// not create the key, the same guard `SADD` has.
67    pub fn hset<'a>(
68        &mut self,
69        key: &[u8],
70        pairs: impl Iterator<Item = (&'a [u8], &'a [u8])> + Clone,
71    ) -> Result<usize> {
72        for (f, v) in pairs.clone() {
73            strings::check_len(key, f.len())?;
74            strings::check_len(key, v.len())?;
75        }
76        let at = match self.hash_slot(key)? {
77            Some(at) => at,
78            None => {
79                if pairs.clone().next().is_none() {
80                    return Ok(0);
81                }
82                let hint = pairs.clone().count();
83                self.new_hash(key, hint)
84            }
85        };
86
87        // Copied out so the body can be borrowed mutably for the whole loop
88        // rather than once a pair.
89        let limits = self.hash_limits;
90        let hash = self
91            .hashes
92            .get_mut(at)
93            .expect("the record points at its body");
94        let mut added = 0;
95        for (field, value) in pairs {
96            if hash.set(field, value, &limits) {
97                added += 1;
98            }
99        }
100        Ok(added)
101    }
102
103    /// Replace whatever is under `key` with a hash of exactly these pairs.
104    ///
105    /// The write side of `HIMPORT SET`, and the one hash write that is a whole
106    /// value rather than an edit. A field the pairs do not name is gone
107    /// afterwards and so is any deadline the old value carried, because the old
108    /// value is gone rather than having been written over, and that is what a
109    /// real server does with the same command.
110    ///
111    /// The lengths and the type are both checked before anything is deleted, so
112    /// a call that cannot go through leaves the key exactly as it was. A caller
113    /// that has its own complaints to make about the arguments still has to ask
114    /// the type first, since `WRONGTYPE` comes before any of them.
115    pub fn hreplace<'a>(
116        &mut self,
117        key: &[u8],
118        pairs: impl Iterator<Item = (&'a [u8], &'a [u8])> + Clone,
119    ) -> Result<()> {
120        for (f, v) in pairs.clone() {
121            strings::check_len(key, f.len())?;
122            strings::check_len(key, v.len())?;
123        }
124        self.hlen(key)?;
125        self.del(key);
126        self.hset(key, pairs)?;
127        Ok(())
128    }
129
130    /// `HSETNX key field value`. Answers whether it was written.
131    ///
132    /// Unlike `SETNX` this is per field and not per key, so it writes into a
133    /// hash that already exists as long as that one field is missing.
134    pub fn hsetnx(&mut self, key: &[u8], field: &[u8], value: &[u8]) -> Result<bool> {
135        strings::check_len(key, field.len())?;
136        strings::check_len(key, value.len())?;
137        let at = match self.hash_slot(key)? {
138            Some(at) => {
139                if self.hash_at(at).contains(field) {
140                    return Ok(false);
141                }
142                at
143            }
144            None => self.new_hash(key, 1),
145        };
146        let limits = self.hash_limits;
147        self.hashes
148            .get_mut(at)
149            .expect("the record points at its body")
150            .set(field, value, &limits);
151        Ok(true)
152    }
153
154    /// `HGET key field`, as a borrow rather than a copy.
155    ///
156    /// `f` is handed `None` for a missing key and for a missing field alike,
157    /// because both are a nil reply and the caller has no reason to tell them
158    /// apart. `HEXISTS` is the command that does.
159    pub fn hget<R>(
160        &mut self,
161        key: &[u8],
162        field: &[u8],
163        f: impl FnOnce(Option<Text<'_>>) -> R,
164    ) -> Result<R> {
165        let Some(at) = self.hash_slot(key)? else {
166            return Ok(f(None));
167        };
168        Ok(f(self.hash_at(at).get(field)))
169    }
170
171    /// `HMGET key field [field ...]`, one call of `f` per field asked for.
172    ///
173    /// Every field gets a call, including the ones that are not there, because
174    /// the reply is positional: a client sending three fields gets three
175    /// entries back and matches them up by position. A missing key answers all
176    /// nils rather than an empty array for the same reason.
177    pub fn hmget<'a, F>(
178        &mut self,
179        key: &[u8],
180        fields: impl Iterator<Item = &'a [u8]>,
181        mut f: F,
182    ) -> Result<()>
183    where
184        F: FnMut(Option<Text<'_>>),
185    {
186        let slot = self.hash_slot(key)?;
187        for field in fields {
188            match slot {
189                Some(at) => f(self.hash_at(at).get(field)),
190                None => f(None),
191            }
192        }
193        Ok(())
194    }
195
196    /// `HDEL key field [field ...]`. Answers how many were there.
197    ///
198    /// The key goes when the last field does.
199    pub fn hdel<'a>(
200        &mut self,
201        key: &[u8],
202        fields: impl Iterator<Item = &'a [u8]>,
203    ) -> Result<usize> {
204        self.hdel_each(key, fields, |_| {})
205    }
206
207    /// The same removal, naming each field that was actually there.
208    ///
209    /// [`Keyspace::hdel`] is this with the names thrown away, which is all a
210    /// caller wants when it is only writing. The wire layer wants them because a
211    /// keyspace subscriber is told which fields went and the count does not say:
212    /// `HDEL k a b` that answers one has not said whether it was `a` or `b`, and
213    /// a field named twice is only reported once.
214    pub fn hdel_each<'a>(
215        &mut self,
216        key: &[u8],
217        fields: impl Iterator<Item = &'a [u8]>,
218        mut f: impl FnMut(&'a [u8]),
219    ) -> Result<usize> {
220        let Some(at) = self.hash_slot(key)? else {
221            return Ok(0);
222        };
223        let hash = self
224            .hashes
225            .get_mut(at)
226            .expect("the record points at its body");
227        let mut gone = 0;
228        for field in fields {
229            if hash.remove(field) {
230                gone += 1;
231                f(field);
232            }
233        }
234        if hash.is_empty() {
235            self.drop_key(key);
236        }
237        Ok(gone)
238    }
239
240    /// `HEXPIREAT` and the three commands that turn into it.
241    ///
242    /// `at` is an absolute unix millisecond, which is what `HEXPIRE`,
243    /// `HPEXPIRE` and `HEXPIREAT` all become before they get here, and one call
244    /// of `f` happens per field asked for because the reply is positional.
245    ///
246    /// The deadline is checked against [`ttl::MAX_AT`] before any field is
247    /// touched, because Redis rejects the whole command rather than failing
248    /// field by field, and a command that names ten fields either sets all ten
249    /// or errors.
250    ///
251    /// A key that is not there answers [`Applied::Missing`] for every field,
252    /// which is the -2 Redis replies, because a missing key and an empty hash
253    /// are the same thing. The key goes when the last field does, which happens
254    /// when the deadline given has already passed.
255    pub fn hexpire<'a, F>(
256        &mut self,
257        key: &[u8],
258        at: u64,
259        cond: Cond,
260        fields: impl Iterator<Item = &'a [u8]>,
261        mut f: F,
262    ) -> Result<()>
263    where
264        F: FnMut(Applied),
265    {
266        if !ttl::valid_at(at) {
267            return Err(Error::new(Code::Invalid, BAD_EXPIRE));
268        }
269        let Some(slot) = self.hash_slot(key)? else {
270            for _ in fields {
271                f(Applied::Missing);
272            }
273            return Ok(());
274        };
275        let now = self.clock.now_ms();
276        let listed = self.hash_at(slot).takes_deadlines();
277        let mut emptied = false;
278        for field in fields {
279            let hash = self.hash_at_mut(slot);
280            let applied = hash.expire(field, at, cond, now);
281            emptied = hash.is_empty();
282            f(applied);
283        }
284        if emptied {
285            self.drop_key(key);
286        } else {
287            self.watch_fields(key, slot, listed);
288        }
289        Ok(())
290    }
291
292    /// `HTTL` and its relatives, one call of `f` per field asked for.
293    ///
294    /// What comes back is when the deadline falls due. Turning that into what is
295    /// left, and into seconds where the command asks for seconds, is the reply
296    /// layer's job, because [`Ask::remaining_ms`] is where that arithmetic lives
297    /// and it needs the moment being asked at.
298    pub fn httl<'a, F>(
299        &mut self,
300        key: &[u8],
301        fields: impl Iterator<Item = &'a [u8]>,
302        mut f: F,
303    ) -> Result<()>
304    where
305        F: FnMut(Ask),
306    {
307        let slot = self.hash_slot(key)?;
308        for field in fields {
309            match slot {
310                Some(at) => f(self.hash_at(at).deadline(field)),
311                None => f(Ask::Missing),
312            }
313        }
314        Ok(())
315    }
316
317    /// `HPERSIST key FIELDS numfields field [field ...]`.
318    ///
319    /// [`Ask::At`] means the deadline that was there has been taken off, which
320    /// the reply layer reports as 1.
321    pub fn hpersist<'a, F>(
322        &mut self,
323        key: &[u8],
324        fields: impl Iterator<Item = &'a [u8]>,
325        mut f: F,
326    ) -> Result<()>
327    where
328        F: FnMut(Ask),
329    {
330        let slot = self.hash_slot(key)?;
331        for field in fields {
332            match slot {
333                Some(at) => f(self.hash_at_mut(at).persist(field)),
334                None => f(Ask::Missing),
335            }
336        }
337        Ok(())
338    }
339
340    /// `HGETDEL key FIELDS numfields field [field ...]`.
341    ///
342    /// The value goes out and the field goes away, in that order, which is the
343    /// whole command: a client that wants both without a race would otherwise
344    /// send `HGET` and `HDEL` and hope. One call of `f` per field asked for,
345    /// including the ones that were not there, because the reply is positional
346    /// the way `HMGET`'s is.
347    ///
348    /// The key goes when the last field does.
349    pub fn hgetdel<'a, F>(
350        &mut self,
351        key: &[u8],
352        fields: impl Iterator<Item = &'a [u8]>,
353        mut f: F,
354    ) -> Result<()>
355    where
356        F: FnMut(Option<Text<'_>>),
357    {
358        let Some(slot) = self.hash_slot(key)? else {
359            for _ in fields {
360                f(None);
361            }
362            return Ok(());
363        };
364        for field in fields {
365            let hash = self.hash_at_mut(slot);
366            f(hash.get(field));
367            hash.remove(field);
368        }
369        if self.hash_at(slot).is_empty() {
370            self.drop_key(key);
371        }
372        Ok(())
373    }
374
375    /// `HGETEX key [EX s | PX ms | EXAT ts | PXAT ts | PERSIST] FIELDS ...`.
376    ///
377    /// The read and the deadline change in one command, which is what makes it
378    /// worth having: a plain `HSET` clears the deadline on the field it writes,
379    /// so there is no way to touch a field's expiry and see its value with the
380    /// commands that were there before.
381    ///
382    /// [`strings::Expire::Keep`] is a plain `HGETEX` with no option, and it is
383    /// the default here rather than `Clear`, which is the one place this
384    /// disagrees with `SET`. `Clear` is `PERSIST` and `At` is the other four.
385    ///
386    /// A deadline that has already gone deletes the field, and the value still
387    /// goes out, because the read happened first. The key goes with the last
388    /// field.
389    pub fn hgetex<'a, F>(
390        &mut self,
391        key: &[u8],
392        expire: strings::Expire,
393        fields: impl Iterator<Item = &'a [u8]>,
394        mut f: F,
395    ) -> Result<()>
396    where
397        F: FnMut(Option<Text<'_>>),
398    {
399        // Before anything is read, because Redis rejects the whole command
400        // rather than expiring the fields it got to first.
401        check_at(expire)?;
402        let Some(slot) = self.hash_slot(key)? else {
403            for _ in fields {
404                f(None);
405            }
406            return Ok(());
407        };
408        let now = self.clock.now_ms();
409        let listed = self.hash_at(slot).takes_deadlines();
410        for field in fields {
411            let hash = self.hash_at_mut(slot);
412            f(hash.get(field));
413            match expire {
414                strings::Expire::Keep => {}
415                strings::Expire::Clear => {
416                    hash.persist(field);
417                }
418                // A deadline that has already gone answers Deleted and takes the
419                // field with it, which needs nothing here: the value went out
420                // above, before the field did, and the empty check below is
421                // what notices if that was the last one.
422                strings::Expire::At(at) => {
423                    hash.expire(field, at, Cond::Always, now);
424                }
425            }
426        }
427        if self.hash_at(slot).is_empty() {
428            self.drop_key(key);
429        } else {
430            self.watch_fields(key, slot, listed);
431        }
432        Ok(())
433    }
434
435    /// `HSETEX key [FNX | FXX] [EX .. | KEEPTTL] FIELDS n field value [..]`.
436    ///
437    /// Answers whether it wrote, which is all of it or none of it. `FNX` wants
438    /// every field named to be missing and `FXX` wants every one of them to be
439    /// there, so a list where one field disagrees writes nothing at all. That is
440    /// stricter than `HSETNX`, which is per field, and it is what makes this
441    /// usable as a compare and set over a group of fields.
442    ///
443    /// [`strings::Expire::Clear`] is a plain `HSETEX` and is the default, since
444    /// a write clears the deadline on the field it writes anyway. `Keep` is
445    /// `KEEPTTL` and has to put the deadline back afterwards for that reason.
446    ///
447    /// A deadline that has already gone still answers written, unlike the
448    /// `HEXPIRE` family which has a separate code for it. The fields are stored
449    /// and then removed, and if that empties the hash the key goes too, so
450    /// `HSETEX key EXAT 1` on a key that did not exist leaves it not existing.
451    pub fn hsetex<'a>(
452        &mut self,
453        key: &[u8],
454        exists: strings::Exists,
455        expire: strings::Expire,
456        pairs: impl Iterator<Item = (&'a [u8], &'a [u8])> + Clone,
457    ) -> Result<bool> {
458        for (f, v) in pairs.clone() {
459            strings::check_len(key, f.len())?;
460            strings::check_len(key, v.len())?;
461        }
462        check_at(expire)?;
463
464        let slot = self.hash_slot(key)?;
465        // The condition is answered before a single field is written, because
466        // it is about the whole list. A key that is not there has every field
467        // missing, so FXX fails on it and FNX passes without creating it yet.
468        let met = match exists {
469            strings::Exists::Always => true,
470            strings::Exists::IfMissing => {
471                slot.is_none_or(|at| pairs.clone().all(|(f, _)| !self.hash_at(at).contains(f)))
472            }
473            strings::Exists::IfPresent => {
474                slot.is_some_and(|at| pairs.clone().all(|(f, _)| self.hash_at(at).contains(f)))
475            }
476        };
477        if !met {
478            return Ok(false);
479        }
480        let slot = match slot {
481            Some(at) => at,
482            None => {
483                if pairs.clone().next().is_none() {
484                    return Ok(false);
485                }
486                self.new_hash(key, pairs.clone().count())
487            }
488        };
489
490        let limits = self.hash_limits;
491        let now = self.clock.now_ms();
492        let listed = self.hash_at(slot).takes_deadlines();
493        for (field, value) in pairs {
494            let hash = self.hash_at_mut(slot);
495            // KEEPTTL has to read the deadline first, because the write is what
496            // clears it. There is no band where the value can be replaced with
497            // the deadline left alone, and adding one would be a second way to
498            // write a field.
499            let kept = match expire {
500                strings::Expire::Keep => hash.deadline(field),
501                _ => Ask::Missing,
502            };
503            hash.set(field, value, &limits);
504            match expire {
505                strings::Expire::Clear => {}
506                strings::Expire::Keep => {
507                    if let Ask::At(at) = kept {
508                        hash.expire(field, at, Cond::Always, now);
509                    }
510                }
511                strings::Expire::At(at) => {
512                    hash.expire(field, at, Cond::Always, now);
513                }
514            }
515        }
516        if self.hash_at(slot).is_empty() {
517            self.drop_key(key);
518        } else {
519            self.watch_fields(key, slot, listed);
520        }
521        Ok(true)
522    }
523
524    /// `HLEN key`.
525    pub fn hlen(&mut self, key: &[u8]) -> Result<usize> {
526        match self.hash_slot(key)? {
527            Some(at) => Ok(self.hash_at(at).len()),
528            None => Ok(0),
529        }
530    }
531
532    /// `HEXISTS key field`.
533    pub fn hexists(&mut self, key: &[u8], field: &[u8]) -> Result<bool> {
534        match self.hash_slot(key)? {
535            Some(at) => Ok(self.hash_at(at).contains(field)),
536            None => Ok(false),
537        }
538    }
539
540    /// `HSTRLEN key field`, without writing the value anywhere.
541    ///
542    /// A value held as an integer answers with how many digits it would take,
543    /// counted rather than formatted, which is what [`Text::byte_len`] is for.
544    pub fn hstrlen(&mut self, key: &[u8], field: &[u8]) -> Result<usize> {
545        match self.hash_slot(key)? {
546            Some(at) => Ok(self.hash_at(at).value_len(field).unwrap_or(0)),
547            None => Ok(0),
548        }
549    }
550
551    /// `HGETALL key`, `HKEYS key` and `HVALS key`, which differ only in what
552    /// the caller does with each pair.
553    ///
554    /// One method for the three because the walk is the whole of the work and
555    /// three copies of it would be three chances for one of them to drift. The
556    /// caller taking a pair and using half of it costs nothing, since neither
557    /// half is formatted until something asks for it.
558    ///
559    /// `Ok(false)` means the key was not there, which is an empty reply for all
560    /// three and never a nil.
561    pub fn hgetall<F>(&mut self, key: &[u8], mut f: F) -> Result<bool>
562    where
563        F: FnMut(Text<'_>, Text<'_>),
564    {
565        self.with_hash(key, |hash| match hash {
566            Some(h) => {
567                for (field, value) in h.iter() {
568                    f(field, value);
569                }
570                true
571            }
572            None => false,
573        })
574    }
575
576    /// Hand the hash under `key` to `f`, or hand it `None` if there is no key.
577    ///
578    /// The same thing [`Keyspace::with_set`] is for, and here it matters more.
579    /// `HGETALL` on RESP3 answers a map, whose header carries the pair count, so
580    /// the wire layer needs the length and then the pairs. Going back through
581    /// [`Keyspace::hlen`] for the header would be a second key lookup on the
582    /// command that is most likely to be in a loop.
583    ///
584    /// A callback rather than a returned `&Hash` because the reap happens under
585    /// `&mut self` and a borrow carved out of that cannot outlive the call.
586    pub fn with_hash<R>(&mut self, key: &[u8], f: impl FnOnce(Option<&Hash>) -> R) -> Result<R> {
587        let at = self.hash_slot(key)?;
588        Ok(f(at.map(|at| self.hash_at(at))))
589    }
590
591    /// `HSCAN key cursor [COUNT n]`, with the cursor to resume from.
592    ///
593    /// `NOVALUES` is the caller's business: it gets both halves and drops the
594    /// one it does not want, exactly as `HKEYS` does.
595    pub fn hscan<F>(&mut self, key: &[u8], cursor: Cursor, count: usize, f: F) -> Result<Cursor>
596    where
597        F: FnMut(Text<'_>, Text<'_>),
598    {
599        let Some(at) = self.hash_slot(key)? else {
600            return Ok(Cursor::END);
601        };
602        Ok(self.hash_at(at).scan(cursor, count, f))
603    }
604
605    /// `HINCRBY key field increment`. Answers the sum.
606    ///
607    /// A field that is not there counts as zero and is created, which is what
608    /// makes this the counter primitive it is used as. A field holding
609    /// something that is not an integer is an error and leaves the hash exactly
610    /// as it was, and so is a sum that leaves the range: Redis checks the
611    /// overflow before the write rather than wrapping and storing the wrap.
612    pub fn hincrby(&mut self, key: &[u8], field: &[u8], by: i64) -> Result<i64> {
613        strings::check_len(key, field.len())?;
614        let at = match self.hash_slot(key)? {
615            Some(at) => at,
616            None => self.new_hash(key, 1),
617        };
618        let current = match self.hash_at(at).get(field) {
619            Some(Text::Int(n)) => n,
620            Some(Text::Str(s)) => {
621                parse_i64(s).ok_or_else(|| Error::new(Code::Invalid, NOT_AN_INT))?
622            }
623            None => 0,
624        };
625        let next = current
626            .checked_add(by)
627            .ok_or_else(|| Error::new(Code::Invalid, WOULD_OVERFLOW))?;
628
629        let mut buf = [0u8; yo_common::num::DIGITS_MAX];
630        let text = yo_common::num::i64_digits(&mut buf, next);
631        let limits = self.hash_limits;
632        self.hashes
633            .get_mut(at)
634            .expect("the record points at its body")
635            .set(field, text, &limits);
636        Ok(next)
637    }
638
639    /// `HINCRBYFLOAT key field increment`. Answers the sum.
640    ///
641    /// The same rules with the float versions of the errors. An infinite
642    /// increment is not refused up front, for the reason `INCRBYFLOAT` gives:
643    /// Redis parses it, does the addition and then reports that the result is
644    /// not finite, so `HINCRBYFLOAT k f inf` says the increment would produce
645    /// infinity and not that the increment is not a float.
646    pub fn hincrbyfloat(&mut self, key: &[u8], field: &[u8], by: f64) -> Result<f64> {
647        strings::check_len(key, field.len())?;
648        let at = match self.hash_slot(key)? {
649            Some(at) => at,
650            None => self.new_hash(key, 1),
651        };
652        let current = match self.hash_at(at).get(field) {
653            Some(Text::Int(n)) => n as f64,
654            Some(Text::Str(s)) => {
655                parse_f64(s).ok_or_else(|| Error::new(Code::Invalid, NOT_A_FLOAT))?
656            }
657            None => 0.0,
658        };
659        let next = current + by;
660        if !next.is_finite() {
661            return Err(Error::new(
662                Code::Invalid,
663                "increment would produce NaN or Infinity",
664            ));
665        }
666
667        let mut buf = [0u8; yo_common::num::DOUBLE_MAX];
668        let text = yo_common::num::write_double(&mut buf, next);
669        let limits = self.hash_limits;
670        self.hashes
671            .get_mut(at)
672            .expect("the record points at its body")
673            .set(field, text, &limits);
674        Ok(next)
675    }
676
677    /// `HRANDFIELD key`, as a borrow.
678    ///
679    /// `f` is handed `None` when the key is not there, which is a nil and not
680    /// an empty reply.
681    pub fn hrandfield<R>(
682        &mut self,
683        key: &[u8],
684        f: impl FnOnce(Option<(Text<'_>, Text<'_>)>) -> R,
685    ) -> Result<R> {
686        let Some(at) = self.hash_slot(key)? else {
687            return Ok(f(None));
688        };
689        let pick = self.rng.below(self.hash_at(at).len());
690        Ok(f(self.hash_at(at).at(pick)))
691    }
692
693    /// `HRANDFIELD key count`, which is two commands wearing one name.
694    ///
695    /// A negative count is the with repeats form: exactly that many fields,
696    /// drawn one at a time, and the same field can come back more than once. It
697    /// is the only form that can answer more fields than the hash holds.
698    ///
699    /// A positive count is distinct fields, at most as many as the hash holds.
700    /// `SRANDMEMBER` splits its distinct form two ways because a set can be
701    /// millions of members and drawing three of them should not walk all of
702    /// them. A hash draws differently: Redis's own `HRANDFIELD` with a positive
703    /// count builds the whole answer either way, so this walks the fields once
704    /// and takes each with the probability that leaves the right number at the
705    /// end. That is Knuth's selection sampling, it needs no memory at all, and
706    /// it is `O(len)` rather than `O(count)`.
707    ///
708    /// A shuffle is deliberately not done. Redis does not promise an order here
709    /// and the walk order is not the insertion order once a field has been
710    /// removed, so shuffling would buy a guarantee nobody is owed at the price
711    /// of an allocation.
712    pub fn hrandfield_n<F>(&mut self, key: &[u8], count: i64, mut f: F) -> Result<()>
713    where
714        F: FnMut(Text<'_>, Text<'_>),
715    {
716        let Some(at) = self.hash_slot(key)? else {
717            return Ok(());
718        };
719        // Borrowed apart rather than through `hash_at`, because drawing and
720        // reading have to be alive at the same time and a method taking `&self`
721        // would hold the whole database.
722        let rng = &mut self.rng;
723        let hash = self.hashes.get(at).expect("the record points at its body");
724        let len = hash.len();
725
726        let Ok(want) = usize::try_from(count) else {
727            let repeats = usize::try_from(count.unsigned_abs()).unwrap_or(usize::MAX);
728            for _ in 0..repeats {
729                let (field, value) = hash
730                    .at(rng.below(len))
731                    .expect("the draw was under the length");
732                f(field, value);
733            }
734            return Ok(());
735        };
736
737        let mut left = want.min(len);
738        let mut seen = len;
739        for i in 0..len {
740            if left == 0 {
741                break;
742            }
743            // Take this one with probability left/seen, which is what leaves
744            // exactly `left` taken by the end whatever the draws come out as.
745            if rng.below(seen) < left {
746                let (field, value) = hash.at(i).expect("i is under the length");
747                f(field, value);
748                left -= 1;
749            }
750            seen -= 1;
751        }
752        Ok(())
753    }
754
755    // ------------------------------------------------------------------ inside
756
757    /// The slot `key`'s hash is in, or `None` if there is no such key.
758    ///
759    /// This is the one place a hash command finds its body, so it is the one
760    /// place that has to reap first and answer `WRONGTYPE` for another type.
761    fn hash_slot(&mut self, key: &[u8]) -> Result<Option<u32>> {
762        let Some(at) = self.live_slot(key, Kind::Hash)? else {
763            return Ok(None);
764        };
765        // And now the fields, which is the second half of lazy expiry. It runs
766        // here rather than in every command so that there is one place a hash
767        // becomes live, and it is a load and a comparison on a hash that has
768        // never been given a field deadline, which is nearly all of them.
769        let now = self.clock.now_ms();
770        if self.reap_fields(key, at, now, false) {
771            return Ok(None);
772        }
773        Ok(Some(at))
774    }
775
776    /// Take the fields of the hash in `at` that are past their deadline, and say
777    /// whether that took the key with them.
778    ///
779    /// Both halves of field expiry end up here, the command that walked into a
780    /// dead field and the cycle that went looking for one, and `active` is which
781    /// of the two it was. That is the only difference between them: they count
782    /// into the same total, they say the same things, and a subscriber cannot
783    /// tell which one it was hearing from, exactly as on a real server.
784    ///
785    /// What it says is `hexpired` naming every field that went, and then `del`
786    /// if the hash has nothing left, because a hash with no fields is not a key.
787    /// Both travel out on [`news`], since neither has a command to report it: a
788    /// `HLEN` that answers two has not said that a third field went on the way
789    /// to counting them.
790    pub(crate) fn reap_fields(&mut self, key: &[u8], at: u32, now: u64, active: bool) -> bool {
791        let mut gone = 0u64;
792        let hash = self
793            .hashes
794            .get_mut(at)
795            .expect("the record points at its body");
796        hash.reap(now, |field| {
797            gone += 1;
798            news::say_of(key, news::What::FieldExpired, field);
799        });
800        if gone == 0 {
801            return false;
802        }
803        self.expired_fields += gone;
804        if active {
805            self.expired_fields_active += gone;
806        }
807        if !self.hash_at(at).is_empty() {
808            return false;
809        }
810        // The last field expiring deletes the key, exactly as the last HDEL
811        // does, because an empty hash is not a thing Redis stores.
812        self.drop_key(key);
813        news::say(key, news::What::Deleted);
814        true
815    }
816
817    /// Put `key` on the list [`Keyspace::field_expire_cycle`] sweeps, if this is
818    /// the moment its hash started taking field deadlines.
819    ///
820    /// `listed` is what [`Hash::takes_deadlines`] said before the command wrote
821    /// anything, so what this tests is the change and not the state. A hash
822    /// crosses that line once, the first time a deadline lands on it, which is
823    /// what keeps one name on the list per hash however many times the
824    /// `HEXPIRE` family is called on it.
825    ///
826    /// The one case that gets past the change test is a key that was deleted and
827    /// made again before the sweep noticed the first one, since the new hash
828    /// starts over. The check against the last name on the list covers the shape
829    /// that would actually run away, a client deleting and remaking the same key
830    /// in a loop, and anything else costs a second name for a key that is really
831    /// there and is really worth sweeping.
832    fn watch_fields(&mut self, key: &[u8], at: u32, listed: bool) {
833        if listed || !self.hash_at(at).takes_deadlines() {
834            return;
835        }
836        if self
837            .field_deadlines
838            .last()
839            .is_some_and(|last| **last == *key)
840        {
841            return;
842        }
843        self.field_deadlines.push(key.into());
844    }
845
846    /// The body in a slot the record pointed at, to be written.
847    #[inline]
848    fn hash_at_mut(&mut self, at: u32) -> &mut Hash {
849        self.hashes
850            .get_mut(at)
851            .expect("the record points at its body")
852    }
853
854    /// The body in a slot the record pointed at.
855    ///
856    /// Panicking here means a record outlived its body, which is the one bug the
857    /// slab deliberately does not carry a generation counter to catch, so this
858    /// is where it would be caught instead.
859    #[inline]
860    fn hash_at(&self, at: u32) -> &Hash {
861        self.hashes.get(at).expect("the record points at its body")
862    }
863
864    /// Make an empty hash under `key` and answer which slot it went in.
865    ///
866    /// The hint only picks the representation to start in, so that an `HSET`
867    /// with a thousand pairs builds a table once instead of filling a listpack
868    /// and then converting it.
869    fn new_hash(&mut self, key: &[u8], hint: usize) -> u32 {
870        // The body and, every so often, the slab that holds it. See
871        // `yo_alloc::first_touch` for why this is the one allocation a command
872        // is allowed to make.
873        let at =
874            yo_alloc::first_touch(|| self.hashes.insert(Hash::with_hint(hint, &self.hash_limits)));
875        let len = value::slot_record_len(false);
876        self.write_rec(key, len, |out| {
877            value::write_slot_record(out, Kind::Hash, at, None);
878        });
879        self.bodies += 1;
880        at
881    }
882}
883
884/// Refuses a deadline past the ceiling before the command touches anything.
885///
886/// Both `HGETEX` and `HSETEX` take the deadline as an option rather than as the
887/// argument it is in the `HEXPIRE` family, and both have to answer for it
888/// before they have read or written a field, since Redis refuses the whole
889/// command rather than half doing it.
890fn check_at(expire: strings::Expire) -> Result<()> {
891    match expire {
892        strings::Expire::At(at) if !ttl::valid_at(at) => Err(Error::new(Code::Invalid, BAD_EXPIRE)),
893        _ => Ok(()),
894    }
895}
896
897#[cfg(test)]
898mod tests {
899    use super::*;
900    use crate::hash::Encoding;
901    use crate::{Clock, many};
902
903    fn db() -> Keyspace {
904        Keyspace::with_clock(Clock::fixed(1_000))
905    }
906
907    /// A keyspace and the number of fields that takes a hash past the listpack
908    /// band in it.
909    ///
910    /// The band is a runtime setting rather than a constant, so under Miri it
911    /// moves down and the field count moves with it. What is crossed is the
912    /// same boundary in the same code, and the default of 512 is pinned where
913    /// it belongs, in the limits themselves.
914    fn promoting() -> (Keyspace, u32) {
915        let mut d = db();
916        if cfg!(miri) {
917            d.set_hash_limits(crate::hash::Limits {
918                max_listpack_entries: 40,
919                ..crate::hash::Limits::DEFAULT
920            });
921            return (d, 50);
922        }
923        (d, 600)
924    }
925
926    fn set(d: &mut Keyspace, key: &[u8], pairs: &[(&[u8], &[u8])]) -> usize {
927        d.hset(key, pairs.iter().copied()).expect("a hash")
928    }
929
930    fn get(d: &mut Keyspace, key: &[u8], field: &[u8]) -> Option<String> {
931        d.hget(key, field, |t| t.map(|t| text(&t))).expect("a hash")
932    }
933
934    fn text(t: &Text<'_>) -> String {
935        String::from_utf8(t.to_vec()).expect("utf8 in these tests")
936    }
937
938    fn all(d: &mut Keyspace, key: &[u8]) -> Vec<(String, String)> {
939        let mut out = Vec::new();
940        d.hgetall(key, |f, v| out.push((text(&f), text(&v))))
941            .expect("a hash");
942        out.sort();
943        out
944    }
945
946    fn expire(d: &mut Keyspace, key: &[u8], at: u64, fields: &[&[u8]]) -> Vec<Applied> {
947        let mut out = Vec::new();
948        d.hexpire(key, at, Cond::Always, fields.iter().copied(), |a| {
949            out.push(a);
950        })
951        .expect("a hash");
952        out
953    }
954
955    fn ttl_of(d: &mut Keyspace, key: &[u8], fields: &[&[u8]]) -> Vec<Ask> {
956        let mut out = Vec::new();
957        d.httl(key, fields.iter().copied(), |a| out.push(a))
958            .expect("a hash");
959        out
960    }
961
962    #[test]
963    fn setting_a_field_on_a_key_that_is_not_there_makes_it() {
964        let mut d = db();
965        assert_eq!(set(&mut d, b"h", &[(b"f", b"v")]), 1);
966        assert_eq!(d.kind_of(b"h"), Some(Kind::Hash));
967        assert_eq!(get(&mut d, b"h", b"f").as_deref(), Some("v"));
968    }
969
970    #[test]
971    fn writing_a_field_again_is_not_a_new_field() {
972        let mut d = db();
973        assert_eq!(set(&mut d, b"h", &[(b"f", b"one"), (b"g", b"two")]), 2);
974        assert_eq!(set(&mut d, b"h", &[(b"f", b"three")]), 0, "f was there");
975        assert_eq!(get(&mut d, b"h", b"f").as_deref(), Some("three"));
976        assert_eq!(d.hlen(b"h").expect("a hash"), 2);
977    }
978
979    #[test]
980    fn an_empty_write_does_not_make_a_key() {
981        let mut d = db();
982        let none: [(&[u8], &[u8]); 0] = [];
983        assert_eq!(d.hset(b"h", none.iter().copied()).expect("ok"), 0);
984        assert_eq!(d.kind_of(b"h"), None, "an empty hash does not exist");
985    }
986
987    #[test]
988    fn losing_the_last_field_loses_the_key() {
989        let mut d = db();
990        set(&mut d, b"h", &[(b"f", b"v"), (b"g", b"w")]);
991        assert_eq!(d.hdel(b"h", [b"f".as_slice()].into_iter()).expect("ok"), 1);
992        assert_eq!(d.kind_of(b"h"), Some(Kind::Hash), "g is still there");
993        assert_eq!(d.hdel(b"h", [b"g".as_slice()].into_iter()).expect("ok"), 1);
994        assert_eq!(d.kind_of(b"h"), None, "and now nothing is");
995        assert_eq!(d.len(), 0);
996    }
997
998    #[test]
999    fn every_command_says_wrongtype_for_a_string() {
1000        let mut d = db();
1001        d.set_plain(b"s", b"v").expect("room");
1002
1003        assert_eq!(
1004            d.hset(b"s", [(b"f".as_slice(), b"v".as_slice())].into_iter())
1005                .unwrap_err()
1006                .code(),
1007            Code::WrongType
1008        );
1009        assert!(d.hget(b"s", b"f", |_| ()).is_err());
1010        assert!(d.hdel(b"s", [b"f".as_slice()].into_iter()).is_err());
1011        assert!(d.hlen(b"s").is_err());
1012        assert!(d.hexists(b"s", b"f").is_err());
1013        assert!(d.hstrlen(b"s", b"f").is_err());
1014        assert!(d.hgetall(b"s", |_, _| ()).is_err());
1015        assert!(d.hsetnx(b"s", b"f", b"v").is_err());
1016        assert!(d.hincrby(b"s", b"f", 1).is_err());
1017        assert!(d.hincrbyfloat(b"s", b"f", 1.0).is_err());
1018        assert!(d.hrandfield(b"s", |_| ()).is_err());
1019        assert!(d.hrandfield_n(b"s", 1, |_, _| ()).is_err());
1020        assert!(d.hscan(b"s", Cursor::START, 10, |_, _| ()).is_err());
1021        assert!(
1022            d.hmget(b"s", [b"f".as_slice()].into_iter(), |_| ())
1023                .is_err()
1024        );
1025
1026        assert_eq!(
1027            d.kind_of(b"s"),
1028            Some(Kind::String),
1029            "and none of them wrote anything"
1030        );
1031    }
1032
1033    #[test]
1034    fn a_missing_key_reads_as_an_empty_hash() {
1035        let mut d = db();
1036        assert_eq!(d.hlen(b"nope").expect("ok"), 0);
1037        assert!(!d.hexists(b"nope", b"f").expect("ok"));
1038        assert_eq!(d.hstrlen(b"nope", b"f").expect("ok"), 0);
1039        assert_eq!(get(&mut d, b"nope", b"f"), None);
1040        assert!(!d.hgetall(b"nope", |_, _| ()).expect("ok"));
1041        assert_eq!(
1042            d.hdel(b"nope", [b"f".as_slice()].into_iter()).expect("ok"),
1043            0
1044        );
1045    }
1046
1047    #[test]
1048    fn hmget_answers_once_per_field_asked_for() {
1049        let mut d = db();
1050        set(&mut d, b"h", &[(b"a", b"1"), (b"c", b"3")]);
1051
1052        let mut got = Vec::new();
1053        d.hmget(b"h", [b"a".as_slice(), b"b", b"c"].into_iter(), |t| {
1054            got.push(t.map(|t| text(&t)));
1055        })
1056        .expect("a hash");
1057        assert_eq!(
1058            got,
1059            vec![Some("1".into()), None, Some("3".into())],
1060            "the reply is positional, so b gets a nil and not a gap"
1061        );
1062
1063        let mut missing = Vec::new();
1064        d.hmget(b"gone", [b"a".as_slice(), b"b"].into_iter(), |t| {
1065            missing.push(t.is_none());
1066        })
1067        .expect("no key");
1068        assert_eq!(missing, vec![true, true], "a missing key is all nils");
1069    }
1070
1071    #[test]
1072    fn hsetnx_writes_only_a_field_that_is_not_there() {
1073        let mut d = db();
1074        assert!(d.hsetnx(b"h", b"f", b"one").expect("ok"), "made the key");
1075        assert!(!d.hsetnx(b"h", b"f", b"two").expect("ok"), "f was there");
1076        assert_eq!(get(&mut d, b"h", b"f").as_deref(), Some("one"));
1077        assert!(
1078            d.hsetnx(b"h", b"g", b"two").expect("ok"),
1079            "and it is per field, not per key"
1080        );
1081        assert_eq!(d.hlen(b"h").expect("ok"), 2);
1082    }
1083
1084    #[test]
1085    fn hstrlen_counts_a_number_without_writing_it() {
1086        let mut d = db();
1087        set(&mut d, b"h", &[(b"n", b"-12345"), (b"s", b"hello")]);
1088        assert_eq!(d.hstrlen(b"h", b"n").expect("ok"), 6);
1089        assert_eq!(d.hstrlen(b"h", b"s").expect("ok"), 5);
1090        assert_eq!(d.hstrlen(b"h", b"nope").expect("ok"), 0);
1091    }
1092
1093    #[test]
1094    fn incrementing_counts_up_from_nothing_and_refuses_what_is_not_a_number() {
1095        let mut d = db();
1096        assert_eq!(d.hincrby(b"h", b"n", 5).expect("ok"), 5, "absent is zero");
1097        assert_eq!(d.hincrby(b"h", b"n", -7).expect("ok"), -2);
1098        assert_eq!(get(&mut d, b"h", b"n").as_deref(), Some("-2"));
1099
1100        set(&mut d, b"h", &[(b"s", b"words")]);
1101        let err = d.hincrby(b"h", b"s", 1).unwrap_err();
1102        assert_eq!(err.code(), Code::Invalid);
1103        assert_eq!(err.message(), NOT_AN_INT);
1104        assert_eq!(
1105            get(&mut d, b"h", b"s").as_deref(),
1106            Some("words"),
1107            "and it left the field alone"
1108        );
1109    }
1110
1111    #[test]
1112    fn an_increment_that_leaves_the_range_is_refused_and_not_wrapped() {
1113        let mut d = db();
1114        let max = i64::MAX.to_string();
1115        set(&mut d, b"h", &[(b"n", max.as_bytes())]);
1116        let err = d.hincrby(b"h", b"n", 1).unwrap_err();
1117        assert_eq!(err.message(), WOULD_OVERFLOW);
1118        assert_eq!(
1119            get(&mut d, b"h", b"n").as_deref(),
1120            Some(max.as_str()),
1121            "the field still holds what it held"
1122        );
1123    }
1124
1125    #[test]
1126    fn incrementing_by_a_float_reports_the_sum_and_refuses_infinity() {
1127        let mut d = db();
1128        assert!((d.hincrbyfloat(b"h", b"f", 10.5).expect("ok") - 10.5).abs() < 1e-9);
1129        assert!((d.hincrbyfloat(b"h", b"f", 0.1).expect("ok") - 10.6).abs() < 1e-9);
1130
1131        let err = d.hincrbyfloat(b"h", b"f", f64::INFINITY).unwrap_err();
1132        assert_eq!(err.message(), "increment would produce NaN or Infinity");
1133
1134        set(&mut d, b"h", &[(b"s", b"words")]);
1135        assert_eq!(
1136            d.hincrbyfloat(b"h", b"s", 1.0).unwrap_err().message(),
1137            NOT_A_FLOAT
1138        );
1139    }
1140
1141    #[test]
1142    fn a_hash_promotes_in_the_keyspace_and_object_encoding_says_so() {
1143        let (mut d, n) = promoting();
1144        set(&mut d, b"h", &[(b"f", b"v")]);
1145        assert_eq!(d.hash_encoding(b"h"), Some(Encoding::Listpack));
1146        assert_eq!(d.encoding_name(b"h"), Some("listpack"));
1147
1148        for i in 0..n {
1149            let f = format!("field-{i}");
1150            set(&mut d, b"h", &[(f.as_bytes(), b"v")]);
1151        }
1152        assert_eq!(d.hash_encoding(b"h"), Some(Encoding::Hashtable));
1153        assert_eq!(d.encoding_name(b"h"), Some("hashtable"));
1154        assert_eq!(d.hlen(b"h").expect("ok"), n as usize + 1);
1155        assert_eq!(
1156            d.hash_encoding(b"missing"),
1157            None,
1158            "and a key that is not a hash has no hash encoding"
1159        );
1160    }
1161
1162    #[test]
1163    fn a_hash_survives_being_given_a_deadline_and_goes_when_it_passes() {
1164        let mut d = db();
1165        set(&mut d, b"h", &[(b"f", b"v"), (b"g", b"w")]);
1166        assert!(d.set_expiry(b"h", Some(1_100)));
1167        assert_eq!(
1168            all(&mut d, b"h"),
1169            vec![("f".into(), "v".into()), ("g".into(), "w".into())],
1170            "writing the record did not touch the body"
1171        );
1172
1173        d.clock().advance(100);
1174        assert_eq!(d.kind_of(b"h"), None);
1175        assert_eq!(d.len(), 0);
1176        assert_eq!(d.expired_keys(), 1);
1177    }
1178
1179    #[test]
1180    fn writing_a_string_over_a_hash_gives_the_body_back() {
1181        let mut d = db();
1182        for i in 0..many(300u32) {
1183            let f = format!("field-{i}");
1184            set(&mut d, b"h", &[(f.as_bytes(), b"a value of some length")]);
1185        }
1186        assert_eq!(d.hashes.len(), 1);
1187        let held = d.memory_bytes();
1188        d.set_plain(b"h", b"now a string").expect("room");
1189
1190        assert_eq!(d.kind_of(b"h"), Some(Kind::String));
1191        // The slot rather than the byte count, because the byte count is mostly
1192        // the arena and the arena does not give a segment back until it is
1193        // compacted. A body that kept its slot would be reachable forever and
1194        // is the exact leak `free_body` exists to stop.
1195        assert_eq!(d.hashes.len(), 0, "the body went with the record");
1196        assert!(d.memory_bytes() < held, "and its bytes went with it");
1197    }
1198
1199    #[test]
1200    fn a_scan_walks_a_hash_in_the_keyspace_exactly_once() {
1201        // Fewer fields and a smaller page under Miri, so the scan still takes
1202        // about fifteen rounds to get through the hash and the cursor still has
1203        // to come back to the right place fourteen times.
1204        let (n, page) = if cfg!(miri) { (150u32, 10) } else { (500, 32) };
1205        let mut d = db();
1206        for i in 0..n {
1207            let f = format!("field-{i}");
1208            let v = format!("value-{i}");
1209            set(&mut d, b"h", &[(f.as_bytes(), v.as_bytes())]);
1210        }
1211
1212        let mut seen: Vec<(String, String)> = Vec::new();
1213        let mut cursor = Cursor::START;
1214        loop {
1215            cursor = d
1216                .hscan(b"h", cursor, page, |f, v| seen.push((text(&f), text(&v))))
1217                .expect("a hash");
1218            if cursor == Cursor::END {
1219                break;
1220            }
1221        }
1222        seen.sort();
1223        seen.dedup();
1224        assert_eq!(seen.len(), n as usize, "every field once and only once");
1225        for (f, v) in &seen {
1226            assert_eq!(
1227                f.strip_prefix("field-"),
1228                v.strip_prefix("value-"),
1229                "and paired with its own value"
1230            );
1231        }
1232    }
1233
1234    #[test]
1235    fn a_draw_takes_the_count_asked_for_and_repeats_only_when_told_to() {
1236        let mut d = db();
1237        d.seed(7);
1238        for i in 0..10u32 {
1239            let f = format!("f{i}");
1240            set(&mut d, b"h", &[(f.as_bytes(), b"v")]);
1241        }
1242
1243        let mut got = Vec::new();
1244        d.hrandfield_n(b"h", 4, |f, _| got.push(text(&f)))
1245            .expect("ok");
1246        assert_eq!(got.len(), 4);
1247        got.sort();
1248        got.dedup();
1249        assert_eq!(got.len(), 4, "a positive count is distinct");
1250
1251        let mut over = Vec::new();
1252        d.hrandfield_n(b"h", 25, |f, _| over.push(text(&f)))
1253            .expect("ok");
1254        assert_eq!(over.len(), 10, "and never more than the hash holds");
1255
1256        let mut with_repeats = Vec::new();
1257        d.hrandfield_n(b"h", -25, |f, _| with_repeats.push(text(&f)))
1258            .expect("ok");
1259        assert_eq!(
1260            with_repeats.len(),
1261            25,
1262            "a negative count is exactly that many, repeats and all"
1263        );
1264
1265        let one = d
1266            .hrandfield(b"h", |p| p.map(|(f, _)| text(&f)))
1267            .expect("ok");
1268        assert!(one.is_some());
1269        assert!(
1270            d.hrandfield(b"gone", |p| p.is_none()).expect("ok"),
1271            "and a missing key draws a nil"
1272        );
1273    }
1274
1275    #[test]
1276    fn a_field_deadline_goes_on_and_is_reported_back() {
1277        let mut d = db();
1278        set(&mut d, b"h", &[(b"a", b"1"), (b"b", b"2")]);
1279        assert_eq!(
1280            expire(&mut d, b"h", 5_000, &[b"a", b"nope"]),
1281            [Applied::Ok, Applied::Missing],
1282            "one call per field, in the order asked"
1283        );
1284        assert_eq!(
1285            ttl_of(&mut d, b"h", &[b"a", b"b", b"nope"]),
1286            [Ask::At(5_000), Ask::NoDeadline, Ask::Missing]
1287        );
1288        assert_eq!(
1289            d.encoding_name(b"h"),
1290            Some("listpackex"),
1291            "and the band widened to hold it"
1292        );
1293    }
1294
1295    #[test]
1296    fn a_field_is_gone_the_next_time_the_key_is_touched() {
1297        let mut d = db();
1298        set(&mut d, b"h", &[(b"a", b"1"), (b"b", b"2")]);
1299        expire(&mut d, b"h", 2_000, &[b"a"]);
1300
1301        assert_eq!(d.hlen(b"h").expect("ok"), 2, "still there at 1000");
1302        d.clock().advance(1_000);
1303        assert_eq!(d.hlen(b"h").expect("ok"), 1, "and gone at 2000");
1304        assert_eq!(get(&mut d, b"h", b"a"), None);
1305        assert_eq!(get(&mut d, b"h", b"b").as_deref(), Some("2"));
1306        assert_eq!(all(&mut d, b"h"), [("b".to_owned(), "2".to_owned())]);
1307    }
1308
1309    #[test]
1310    fn the_key_goes_when_its_last_field_expires() {
1311        let mut d = db();
1312        set(&mut d, b"h", &[(b"a", b"1")]);
1313        expire(&mut d, b"h", 2_000, &[b"a"]);
1314        assert_eq!(d.kind_of(b"h"), Some(Kind::Hash));
1315
1316        d.clock().advance(1_000);
1317        assert_eq!(d.hlen(b"h").expect("ok"), 0);
1318        assert_eq!(d.kind_of(b"h"), None, "an empty hash is not stored");
1319        assert_eq!(d.len(), 0);
1320    }
1321
1322    /// `HEXPIRE key 0` is a roundabout `HDEL`, and taking the last field with it
1323    /// takes the key.
1324    #[test]
1325    fn a_deadline_already_past_deletes_the_field_now() {
1326        let mut d = db();
1327        set(&mut d, b"h", &[(b"a", b"1"), (b"b", b"2")]);
1328        assert_eq!(expire(&mut d, b"h", 500, &[b"a"]), [Applied::Deleted]);
1329        assert_eq!(d.hlen(b"h").expect("ok"), 1);
1330
1331        assert_eq!(expire(&mut d, b"h", 500, &[b"b"]), [Applied::Deleted]);
1332        assert_eq!(d.kind_of(b"h"), None);
1333    }
1334
1335    #[test]
1336    fn persisting_puts_the_field_back_to_no_deadline() {
1337        let mut d = db();
1338        set(&mut d, b"h", &[(b"a", b"1")]);
1339        expire(&mut d, b"h", 5_000, &[b"a"]);
1340
1341        let mut out = Vec::new();
1342        d.hpersist(
1343            b"h",
1344            [b"a".as_slice(), b"nope".as_slice()].into_iter(),
1345            |a| {
1346                out.push(a);
1347            },
1348        )
1349        .expect("ok");
1350        assert_eq!(out, [Ask::At(5_000), Ask::Missing]);
1351        assert_eq!(ttl_of(&mut d, b"h", &[b"a"]), [Ask::NoDeadline]);
1352
1353        d.clock().advance(100_000);
1354        assert_eq!(d.hlen(b"h").expect("ok"), 1, "and it outlives its deadline");
1355    }
1356
1357    #[test]
1358    fn a_missing_key_answers_no_field_for_every_field_it_was_asked() {
1359        let mut d = db();
1360        assert_eq!(
1361            expire(&mut d, b"gone", 5_000, &[b"a", b"b"]),
1362            [Applied::Missing, Applied::Missing]
1363        );
1364        assert_eq!(
1365            ttl_of(&mut d, b"gone", &[b"a", b"b"]),
1366            [Ask::Missing, Ask::Missing]
1367        );
1368        assert_eq!(d.kind_of(b"gone"), None, "and asking did not create it");
1369    }
1370
1371    #[test]
1372    fn a_deadline_past_the_ceiling_is_refused_before_any_field_moves() {
1373        let mut d = db();
1374        set(&mut d, b"h", &[(b"a", b"1")]);
1375        let err = d
1376            .hexpire(
1377                b"h",
1378                crate::ttl::MAX_AT + 1,
1379                Cond::Always,
1380                [b"a".as_slice()].into_iter(),
1381                |_| unreachable!("no field is reached"),
1382            )
1383            .expect_err("past the ceiling");
1384        assert_eq!(err.code(), Code::Invalid);
1385        assert_eq!(ttl_of(&mut d, b"h", &[b"a"]), [Ask::NoDeadline]);
1386    }
1387
1388    #[test]
1389    fn every_field_ttl_command_says_wrongtype_and_writes_nothing() {
1390        let mut d = db();
1391        d.set_plain(b"s", b"v").expect("room");
1392        assert!(
1393            d.hexpire(
1394                b"s",
1395                5_000,
1396                Cond::Always,
1397                [b"a".as_slice()].into_iter(),
1398                |_| { unreachable!("nothing is reached") }
1399            )
1400            .is_err()
1401        );
1402        assert!(d.httl(b"s", [b"a".as_slice()].into_iter(), |_| {}).is_err());
1403        assert!(
1404            d.hpersist(b"s", [b"a".as_slice()].into_iter(), |_| {})
1405                .is_err()
1406        );
1407        assert_eq!(
1408            d.kind_of(b"s"),
1409            Some(Kind::String),
1410            "and the string is intact"
1411        );
1412    }
1413
1414    #[test]
1415    fn a_hash_that_never_expires_a_field_is_untouched_by_all_of_this() {
1416        let (mut d, n) = promoting();
1417        for i in 0..n {
1418            set(&mut d, b"h", &[(format!("f{i}").as_bytes(), b"v")]);
1419        }
1420        assert_eq!(d.encoding_name(b"h"), Some("hashtable"));
1421        d.clock().advance(1_000_000);
1422        assert_eq!(
1423            d.hlen(b"h").expect("ok"),
1424            n as usize,
1425            "nothing had a deadline"
1426        );
1427    }
1428
1429    /// `HGETDEL`, as the strings it handed back.
1430    fn getdel(d: &mut Keyspace, key: &[u8], fields: &[&[u8]]) -> Vec<Option<String>> {
1431        let mut out = Vec::new();
1432        d.hgetdel(key, fields.iter().copied(), |t| {
1433            out.push(t.map(|t| text(&t)));
1434        })
1435        .expect("a hash");
1436        out
1437    }
1438
1439    /// `HGETEX`, the same way.
1440    fn getex(
1441        d: &mut Keyspace,
1442        key: &[u8],
1443        expire: strings::Expire,
1444        fields: &[&[u8]],
1445    ) -> Vec<Option<String>> {
1446        let mut out = Vec::new();
1447        d.hgetex(key, expire, fields.iter().copied(), |t| {
1448            out.push(t.map(|t| text(&t)));
1449        })
1450        .expect("a hash");
1451        out
1452    }
1453
1454    /// `HSETEX`, with the two options spelled out.
1455    fn setex(
1456        d: &mut Keyspace,
1457        key: &[u8],
1458        exists: strings::Exists,
1459        expire: strings::Expire,
1460        pairs: &[(&[u8], &[u8])],
1461    ) -> bool {
1462        d.hsetex(key, exists, expire, pairs.iter().copied())
1463            .expect("a hash")
1464    }
1465
1466    #[test]
1467    fn getdel_hands_the_value_back_and_then_takes_the_field() {
1468        let mut d = db();
1469        set(&mut d, b"h", &[(b"a", b"1"), (b"b", b"2"), (b"c", b"3")]);
1470        assert_eq!(
1471            getdel(&mut d, b"h", &[b"a", b"nope"]),
1472            [Some("1".to_owned()), None],
1473            "positional, so a field that was not there is a hole and not a gap"
1474        );
1475        assert_eq!(all(&mut d, b"h").len(), 2);
1476        assert_eq!(
1477            getdel(&mut d, b"gone", &[b"a", b"b"]),
1478            [None, None],
1479            "and a missing key is all nils"
1480        );
1481        assert_eq!(d.kind_of(b"gone"), None, "which did not create it");
1482
1483        getdel(&mut d, b"h", &[b"b", b"c"]);
1484        assert_eq!(d.kind_of(b"h"), None, "the last field took the key with it");
1485    }
1486
1487    #[test]
1488    fn getdel_takes_the_deadline_with_the_field() {
1489        let mut d = db();
1490        set(&mut d, b"h", &[(b"a", b"1"), (b"b", b"2")]);
1491        expire(&mut d, b"h", 5_000, &[b"a"]);
1492        assert_eq!(getdel(&mut d, b"h", &[b"a"]), [Some("1".to_owned())]);
1493        set(&mut d, b"h", &[(b"a", b"9")]);
1494        assert_eq!(
1495            ttl_of(&mut d, b"h", &[b"a"]),
1496            [Ask::NoDeadline],
1497            "the field came back without the deadline it had"
1498        );
1499    }
1500
1501    #[test]
1502    fn getex_reads_and_moves_the_deadline_in_one_go() {
1503        let mut d = db();
1504        set(&mut d, b"h", &[(b"a", b"1")]);
1505        assert_eq!(
1506            getex(&mut d, b"h", strings::Expire::Keep, &[b"a"]),
1507            [Some("1".to_owned())]
1508        );
1509        assert_eq!(ttl_of(&mut d, b"h", &[b"a"]), [Ask::NoDeadline]);
1510
1511        getex(&mut d, b"h", strings::Expire::At(5_000), &[b"a"]);
1512        assert_eq!(ttl_of(&mut d, b"h", &[b"a"]), [Ask::At(5_000)]);
1513        assert_eq!(
1514            getex(&mut d, b"h", strings::Expire::Keep, &[b"a"]),
1515            [Some("1".to_owned())],
1516            "and a plain read is Keep and not Clear, which is the one place this disagrees with SET"
1517        );
1518        assert_eq!(ttl_of(&mut d, b"h", &[b"a"]), [Ask::At(5_000)]);
1519
1520        getex(&mut d, b"h", strings::Expire::Clear, &[b"a"]);
1521        assert_eq!(ttl_of(&mut d, b"h", &[b"a"]), [Ask::NoDeadline]);
1522    }
1523
1524    #[test]
1525    fn getex_hands_back_the_value_of_a_field_it_is_about_to_expire() {
1526        let mut d = db();
1527        set(&mut d, b"h", &[(b"a", b"1"), (b"b", b"2")]);
1528        assert_eq!(
1529            getex(&mut d, b"h", strings::Expire::At(1), &[b"a"]),
1530            [Some("1".to_owned())],
1531            "the read happened before the deadline was applied"
1532        );
1533        assert_eq!(get(&mut d, b"h", b"a"), None);
1534        assert_eq!(d.hlen(b"h").expect("ok"), 1);
1535
1536        getex(&mut d, b"h", strings::Expire::At(1), &[b"b"]);
1537        assert_eq!(d.kind_of(b"h"), None, "and the last one took the key");
1538    }
1539
1540    #[test]
1541    fn setex_writes_all_of_it_or_none_of_it() {
1542        let mut d = db();
1543        assert!(setex(
1544            &mut d,
1545            b"h",
1546            strings::Exists::Always,
1547            strings::Expire::Clear,
1548            &[(b"a", b"1")]
1549        ));
1550        assert_eq!(get(&mut d, b"h", b"a"), Some("1".to_owned()));
1551
1552        assert!(
1553            !setex(
1554                &mut d,
1555                b"h",
1556                strings::Exists::IfMissing,
1557                strings::Expire::Clear,
1558                &[(b"a", b"9"), (b"new", b"9")]
1559            ),
1560            "FNX wants every field named to be missing, and a is not"
1561        );
1562        assert_eq!(get(&mut d, b"h", b"a"), Some("1".to_owned()));
1563        assert_eq!(
1564            get(&mut d, b"h", b"new"),
1565            None,
1566            "and none of it was written"
1567        );
1568
1569        assert!(
1570            !setex(
1571                &mut d,
1572                b"h",
1573                strings::Exists::IfPresent,
1574                strings::Expire::Clear,
1575                &[(b"a", b"9"), (b"nope", b"9")]
1576            ),
1577            "and FXX wants every one of them to be there"
1578        );
1579        assert_eq!(get(&mut d, b"h", b"a"), Some("1".to_owned()));
1580
1581        assert!(setex(
1582            &mut d,
1583            b"h",
1584            strings::Exists::IfPresent,
1585            strings::Expire::Clear,
1586            &[(b"a", b"9")]
1587        ));
1588        assert_eq!(get(&mut d, b"h", b"a"), Some("9".to_owned()));
1589    }
1590
1591    #[test]
1592    fn setex_on_a_key_that_is_not_there_makes_it_only_when_it_can() {
1593        let mut d = db();
1594        assert!(
1595            !setex(
1596                &mut d,
1597                b"gone",
1598                strings::Exists::IfPresent,
1599                strings::Expire::Clear,
1600                &[(b"a", b"1")]
1601            ),
1602            "FXX cannot be met by a key with no fields at all"
1603        );
1604        assert_eq!(d.kind_of(b"gone"), None, "and it was not created");
1605
1606        assert!(setex(
1607            &mut d,
1608            b"fresh",
1609            strings::Exists::IfMissing,
1610            strings::Expire::Clear,
1611            &[(b"a", b"1")]
1612        ));
1613        assert_eq!(get(&mut d, b"fresh", b"a"), Some("1".to_owned()));
1614    }
1615
1616    #[test]
1617    fn setex_keeps_the_deadline_only_when_it_is_asked_to() {
1618        let mut d = db();
1619        set(&mut d, b"h", &[(b"a", b"1")]);
1620        expire(&mut d, b"h", 5_000, &[b"a"]);
1621
1622        setex(
1623            &mut d,
1624            b"h",
1625            strings::Exists::Always,
1626            strings::Expire::Keep,
1627            &[(b"a", b"2")],
1628        );
1629        assert_eq!(get(&mut d, b"h", b"a"), Some("2".to_owned()));
1630        assert_eq!(
1631            ttl_of(&mut d, b"h", &[b"a"]),
1632            [Ask::At(5_000)],
1633            "KEEPTTL put back what the write cleared"
1634        );
1635
1636        setex(
1637            &mut d,
1638            b"h",
1639            strings::Exists::Always,
1640            strings::Expire::Clear,
1641            &[(b"a", b"3")],
1642        );
1643        assert_eq!(
1644            ttl_of(&mut d, b"h", &[b"a"]),
1645            [Ask::NoDeadline],
1646            "and without it the write clears the deadline the way HSET does"
1647        );
1648
1649        setex(
1650            &mut d,
1651            b"h",
1652            strings::Exists::Always,
1653            strings::Expire::At(9_000),
1654            &[(b"a", b"4")],
1655        );
1656        assert_eq!(ttl_of(&mut d, b"h", &[b"a"]), [Ask::At(9_000)]);
1657    }
1658
1659    #[test]
1660    fn setex_with_a_deadline_that_has_gone_stores_and_then_removes() {
1661        let mut d = db();
1662        assert!(
1663            setex(
1664                &mut d,
1665                b"h",
1666                strings::Exists::Always,
1667                strings::Expire::At(1),
1668                &[(b"a", b"1")]
1669            ),
1670            "written, and not the separate code the HEXPIRE family has for this"
1671        );
1672        assert_eq!(
1673            d.kind_of(b"h"),
1674            None,
1675            "so a key that did not exist is still not there"
1676        );
1677
1678        set(&mut d, b"h", &[(b"keeper", b"1")]);
1679        setex(
1680            &mut d,
1681            b"h",
1682            strings::Exists::Always,
1683            strings::Expire::At(1),
1684            &[(b"a", b"1")],
1685        );
1686        assert_eq!(d.hlen(b"h").expect("ok"), 1, "and the rest of it survives");
1687    }
1688
1689    #[test]
1690    fn setex_refuses_a_deadline_past_the_ceiling_before_writing_anything() {
1691        let mut d = db();
1692        set(&mut d, b"h", &[(b"a", b"1")]);
1693        let err = d
1694            .hsetex(
1695                b"h",
1696                strings::Exists::Always,
1697                strings::Expire::At(crate::ttl::MAX_AT + 1),
1698                [(b"a".as_slice(), b"2".as_slice())].into_iter(),
1699            )
1700            .expect_err("past the ceiling");
1701        assert_eq!(err.code(), Code::Invalid);
1702        assert_eq!(get(&mut d, b"h", b"a"), Some("1".to_owned()));
1703    }
1704
1705    #[test]
1706    fn the_last_three_hash_commands_say_wrongtype_and_write_nothing() {
1707        let mut d = db();
1708        d.set_plain(b"s", b"v").expect("room");
1709        assert!(
1710            d.hgetdel(b"s", [b"a".as_slice()].into_iter(), |_| {})
1711                .is_err()
1712        );
1713        assert!(
1714            d.hgetex(
1715                b"s",
1716                strings::Expire::Keep,
1717                [b"a".as_slice()].into_iter(),
1718                |_| {}
1719            )
1720            .is_err()
1721        );
1722        assert!(
1723            d.hsetex(
1724                b"s",
1725                strings::Exists::Always,
1726                strings::Expire::Clear,
1727                [(b"a".as_slice(), b"1".as_slice())].into_iter(),
1728            )
1729            .is_err()
1730        );
1731        assert_eq!(d.kind_of(b"s"), Some(Kind::String));
1732    }
1733
1734    #[test]
1735    fn the_last_three_reach_a_table_the_same_way_they_reach_a_listpack() {
1736        let (mut d, n) = promoting();
1737        for i in 0..n {
1738            set(&mut d, b"h", &[(format!("f{i}").as_bytes(), b"v")]);
1739        }
1740        assert_eq!(d.encoding_name(b"h"), Some("hashtable"));
1741
1742        setex(
1743            &mut d,
1744            b"h",
1745            strings::Exists::Always,
1746            strings::Expire::At(5_000),
1747            &[(b"f0", b"x")],
1748        );
1749        assert_eq!(ttl_of(&mut d, b"h", &[b"f0"]), [Ask::At(5_000)]);
1750        assert_eq!(
1751            getex(&mut d, b"h", strings::Expire::Clear, &[b"f0"]),
1752            [Some("x".to_owned())]
1753        );
1754        assert_eq!(ttl_of(&mut d, b"h", &[b"f0"]), [Ask::NoDeadline]);
1755        assert_eq!(getdel(&mut d, b"h", &[b"f0"]), [Some("x".to_owned())]);
1756        assert_eq!(d.hlen(b"h").expect("ok"), n as usize - 1);
1757    }
1758
1759    #[test]
1760    fn a_flush_takes_the_hashes_with_it() {
1761        let mut d = db();
1762        for i in 0..many(200u32) {
1763            let f = format!("field-{i}");
1764            set(&mut d, b"h", &[(f.as_bytes(), b"v")]);
1765        }
1766        set(&mut d, b"other", &[(b"f", b"v")]);
1767        d.clear();
1768
1769        assert_eq!(d.len(), 0);
1770        assert_eq!(d.kind_of(b"h"), None);
1771        // Writing again reuses the slab from the start rather than growing past
1772        // the slots the cleared hashes had.
1773        set(&mut d, b"h", &[(b"f", b"v")]);
1774        assert_eq!(d.hlen(b"h").expect("ok"), 1);
1775    }
1776}