pub struct Listpack { /* private fields */ }Expand description
A packed blob of elements.
Owns its bytes today. When the arena lands under it the bytes move there and
this becomes a view, which is why nothing here hands out a Vec or takes
one back.
Implementations§
Source§impl Listpack
impl Listpack
Sourcepub fn from_bytes(bytes: &[u8]) -> Result<Listpack, Malformed>
pub fn from_bytes(bytes: &[u8]) -> Result<Listpack, Malformed>
Take bytes somebody else wrote, after checking them.
An RDB, a .yo file and a RESTORE all arrive this way, so the walk is
not optional. A blob that does not check out is refused whole rather than
read up to the bad entry, because half a collection is worse than none.
Sourcepub fn as_bytes(&self) -> &[u8] ⓘ
pub fn as_bytes(&self) -> &[u8] ⓘ
The bytes, ready to be written to a file or an RDB unchanged.
Sourcepub fn len(&self) -> usize
pub fn len(&self) -> usize
How many elements.
The header answers this, which is why it is here and why it is kept right on every edit. A blob from elsewhere with an unknown count is walked instead, once, rather than being rejected.
Sourcepub fn is_empty(&self) -> bool
pub fn is_empty(&self) -> bool
Whether there is nothing in it, which for Redis means it does not exist.
Sourcepub fn get(&self, index: usize) -> Option<Entry<'_>>
pub fn get(&self, index: usize) -> Option<Entry<'_>>
The element at a position, counting from the front.
Linear, because the blob is linear. That is the whole design: at a
hundred and twenty eight elements the walk is cheaper than the index that
would have avoided it. From whichever end is nearer, though, because a
list in this band holds eight kilobytes and that is four hundred odd
entries rather than a hundred and twenty eight, and LINDEX key -1 on
one of those should not read all of it.
Sourcepub fn iter_at(&self, byte: usize) -> Iter<'_> ⓘ
pub fn iter_at(&self, byte: usize) -> Iter<'_> ⓘ
A forward walk that starts at a byte offset a previous walk reported.
The offset has to come from Iter::offset on a walk of this same blob,
taken while nothing has moved the bytes since. A stream node uses it to
resume a group read where the last one stopped instead of decoding the
whole node again, and it checks the entry it lands on before believing
it. An offset that is past the end gives an empty walk rather than
nonsense, and one that lands in the middle of an entry gives whatever
those bytes decode as, which is why the caller checks.
Sourcepub fn iter_from(&self, index: usize) -> Iter<'_> ⓘ
pub fn iter_from(&self, index: usize) -> Iter<'_> ⓘ
A forward walk that starts at index rather than at the front.
LRANGE key 300 320 on a packed list would otherwise decode three
hundred entries and throw them away, which is what a skip on the walk
does.
Sourcepub fn iter_back(&self) -> RevIter<'_> ⓘ
pub fn iter_back(&self) -> RevIter<'_> ⓘ
Every element, back to front.
The trailing length on each entry is what makes this cost the same per
element as the forward walk. LPOS with a negative rank counts matches
from the tail and stops when it has enough, so walking forward and
keeping the answers would be the wrong shape as well as the wrong cost.
Sourcepub fn get_back(&self, from_end: usize) -> Option<Entry<'_>>
pub fn get_back(&self, from_end: usize) -> Option<Entry<'_>>
The element at a position, counting from the back.
Reads the back length of the last entry and steps left, which is what the
trailing length field is for and why a RPOP on a small list does not
walk the whole blob.
Sourcepub fn find(&self, needle: &[u8], step: usize) -> Option<usize>
pub fn find(&self, needle: &[u8], step: usize) -> Option<usize>
Where an element is, or nothing.
step is what makes this work for a hash. A hash in this band is field,
value, field, value, so a field lookup is a find with a step of two, which
is the same trick Redis’s lpFind plays and the reason a hash does not
need a second structure down here.
Sourcepub fn find_parsed(
&self,
needle: &[u8],
as_int: Option<i64>,
step: usize,
) -> Option<usize>
pub fn find_parsed( &self, needle: &[u8], as_int: Option<i64>, step: usize, ) -> Option<usize>
The same walk with the needle already parsed.
Set algebra asks one member of one set about every other set, so the parse would otherwise happen once per question about the same bytes. It is also the only form that can answer about a member which was never text: an intset holds the number and the digits do not exist anywhere until somebody writes them.
Sourcepub fn find_each(
&self,
needle: &[u8],
as_int: Option<i64>,
limit: usize,
hit: &mut dyn FnMut(usize) -> bool,
) -> usize
pub fn find_each( &self, needle: &[u8], as_int: Option<i64>, limit: usize, hit: &mut dyn FnMut(usize) -> bool, ) -> usize
Every place an element is, front to back, handed over as they are found.
limit is how many elements may be looked at with 0 meaning all of them,
hit says whether to carry on, and what comes back is how many elements
were looked at. The walk itself is scan_each below.
Sourcepub fn find_each_back(
&self,
needle: &[u8],
as_int: Option<i64>,
limit: usize,
hit: &mut dyn FnMut(usize) -> bool,
) -> usize
pub fn find_each_back( &self, needle: &[u8], as_int: Option<i64>, limit: usize, hit: &mut dyn FnMut(usize) -> bool, ) -> usize
The same from the back, with indexes counted from the last element.
Sourcepub fn insert(&mut self, index: usize, value: &[u8])
pub fn insert(&mut self, index: usize, value: &[u8])
Put an element in front of the one at index.
An index at or past the end appends, which is what a sorted insert wants when the new element sorts last and saves the caller a branch.