pub struct Out { /* private fields */ }Expand description
A reply buffer for one connection.
Owns its bytes so that a connection can fill it across several commands and
hand the whole thing to one write, which is 04 section 5: one writev per
connection per batch, not one per reply.
Implementations§
Source§impl Out
impl Out
Sourcepub fn with_capacity(proto: Proto, cap: usize) -> Out
pub fn with_capacity(proto: Proto, cap: usize) -> Out
An empty buffer with room already reserved.
Sourcepub const fn set_proto(&mut self, proto: Proto)
pub const fn set_proto(&mut self, proto: Proto)
Switches protocol, which is what HELLO does.
Takes effect from the next reply written. HELLO’s own reply is written
in the new protocol, which is why this is called before it rather than
after.
Sourcepub fn capacity(&self) -> usize
pub fn capacity(&self) -> usize
How much room it is holding, which is what it costs the process.
A reply buffer keeps its capacity between batches on purpose, so len
is what a client is owed and this is what the memory report owes.
Sourcepub fn clear(&mut self)
pub fn clear(&mut self)
Drops everything written, keeping the capacity.
Called after the batch has been written to the socket. The capacity is what stops a busy connection from allocating again.
Sourcepub fn consume(&mut self, n: usize)
pub fn consume(&mut self, n: usize)
Drops the first n bytes, which is what a partial write leaves behind.
§Panics
If n is past the end of what has been written.
Sourcepub fn truncate(&mut self, len: usize)
pub fn truncate(&mut self, len: usize)
Drops everything written after len, which has to be a length this
buffer reported earlier.
The dispatcher takes the length before it runs a command and rolls back to it when the command answers with an error, so a command that writes half a reply and then fails cannot leave the half on the wire. Every command is written to check its arguments before it writes anything, and this is what makes that a property of the dispatcher rather than a rule three hundred commands have to keep to.
Sourcepub fn reserve(&mut self, n: usize)
pub fn reserve(&mut self, n: usize)
Reserves room for n more bytes.
The presize half of Y18. Call it once with the whole reply’s size before writing any of it.
Sourcepub fn into_inner(self) -> Vec<u8> ⓘ
pub fn into_inner(self) -> Vec<u8> ⓘ
The buffer, taken.
Sourcepub fn raw(&mut self, bytes: &[u8])
pub fn raw(&mut self, bytes: &[u8])
Raw bytes, appended as they are.
For a reply that was assembled elsewhere, such as a cached COMMAND DOCS payload or a replicated frame passing through. Nothing checks that
what goes in is a valid frame, which is the point.
Sourcepub fn error(&mut self, msg: &[u8])
pub fn error(&mut self, msg: &[u8])
An error, -msg\r\n.
msg carries its own prefix, because the prefix is part of the
contract: a client branches on WRONGTYPE or MOVED or NOAUTH, and
which one applies is the command’s decision and not the codec’s. The
full taxonomy is in 12 section 1.
Sourcepub fn error_line(&mut self, prefix: &[u8], msg: &[u8])
pub fn error_line(&mut self, prefix: &[u8], msg: &[u8])
An error built from a prefix and a message that are not next to each other in memory, with any line ending in the message turned into a space.
The prefix carries its own trailing space, so this is called with
b"ERR " or b"WRONGTYPE ". Joining the two halves first would mean
allocating a string on the failure path of a thread that is not allowed
to allocate, which is the whole reason this exists.
The mapping of \r and \n to spaces is Redis’s, and it is not
cosmetic: an error message can quote what the client sent, and a client
that sends a command name with a newline in it would otherwise be
writing its own frames into somebody’s reply stream.
Sourcepub fn error_about(&mut self, before: &[u8], word: &[u8], after: &[u8])
pub fn error_about(&mut self, before: &[u8], word: &[u8], after: &[u8])
An error line with a word the client sent quoted in the middle of it.
Several of the search errors read Unknown argument \x` and name the word that was not understood, so the line is the server's own text, then the client's bytes, then the server's text again. Only the middle piece can carry a line ending and only the middle piece has them taken out, for the reason [Out::error_line`] gives.
Sourcepub fn blob_error(&mut self, msg: &[u8])
pub fn blob_error(&mut self, msg: &[u8])
A blob error, RESP3’s !, which may carry anything including newlines.
Degrades to a normal error line in RESP2, with line endings turned into spaces, because a RESP2 error is one line by definition.
Sourcepub fn uint(&mut self, n: u64)
pub fn uint(&mut self, n: u64)
An unsigned integer, :n\r\n.
Not the same as Out::int for the numbers with bit 63 set, and that is
the only reason it exists. ARLEN on a key with something at the top of
the index space is eighteen quintillion, which the signed path would put
on the wire as a negative number. Redis has the same pair of writers and
uses the unsigned one for exactly these replies.
Sourcepub fn bulk_int(&mut self, n: i64)
pub fn bulk_int(&mut self, n: i64)
A bulk string holding the decimal form of n.
Written straight into the buffer rather than through a temporary, which is worth having as its own method because several commands reply with a number as a string and every one of them would otherwise allocate.
Sourcepub fn bulk_u64(&mut self, n: u64)
pub fn bulk_u64(&mut self, n: u64)
A bulk string holding the decimal form of n, unsigned.
Not the same as Out::bulk_int for the numbers with bit 63 set, which
is the only reason it exists: a scan cursor packs a partition count into
the top bits, so a big enough collection hands back a number that the
signed path would report as negative and no client would send back.
Sourcepub fn bulk_double(&mut self, d: f64)
pub fn bulk_double(&mut self, d: f64)
A bulk string holding a double in Redis’s own formatting.
A score written into a flat RESP2 reply arrives here rather than at
Out::double, because there is no protocol choice left to make by
then.
The length has to go in front of the digits and the digits cannot be counted without writing them, so they are written first, the header is appended behind them, and the two are rotated into place. A double is a couple of dozen bytes at most, so the rotate is a few words, and nothing is allocated to hold a number on its way into a buffer it is already in.
Sourcepub fn human_double(&mut self, d: f64)
pub fn human_double(&mut self, d: f64)
A bulk string holding a double the way the two float increments write one.
INCRBYFLOAT and HINCRBYFLOAT go through ld2string in its human
mode where every other double goes through d2string, and the two
disagree about large and small magnitudes: this one never writes an
exponent. Both of them reply with a bulk string on RESP2 and on RESP3
alike, so unlike Out::double there is no protocol branch here.
Sourcepub fn distance(&mut self, d: f64)
pub fn distance(&mut self, d: f64)
A bulk string holding a distance, which is four places and no exponent.
The geo commands are the only ones that write a number this way, and
they write it as a bulk string on both protocols rather than as RESP3’s
double, so there is no protocol branch here either. See
yo_common::num::push_fixed4 for why four.
Sourcepub fn verbatim(&mut self, format: &[u8; 3], text: &[u8])
pub fn verbatim(&mut self, format: &[u8; 3], text: &[u8])
A verbatim string, RESP3’s =, with a three byte format such as txt
or mkd.
RESP2 has no such type and gets a plain bulk string of the text, without the format prefix, which is what Redis does.
Sourcepub fn big_number(&mut self, digits: &[u8])
pub fn big_number(&mut self, digits: &[u8])
A big number, RESP3’s (, given as its decimal digits.
RESP2 gets a bulk string of the same digits, which is what Redis does and what every client already handles.
Sourcepub fn nil(&mut self)
pub fn nil(&mut self)
Nothing, where a string was expected.
RESP3 has one null. RESP2 has two, and this is the one that stands in
for a missing string, which is what GET on a missing key returns.
Sourcepub fn nil_array(&mut self)
pub fn nil_array(&mut self)
Nothing, where an array was expected.
The other RESP2 null. EXEC on a dirty WATCH returns this one, and a
client that tells the two apart will notice if the wrong one is sent.
Sourcepub fn double(&mut self, d: f64)
pub fn double(&mut self, d: f64)
A double, RESP3’s ,.
RESP2 gets a bulk string of the same digits. The infinities and NaN are written as words in both.
Sourcepub fn bool(&mut self, b: bool)
pub fn bool(&mut self, b: bool)
A boolean, RESP3’s #t or #f.
RESP2 gets :1 or :0, which is what every command that returns a
boolean has always returned there.
Sourcepub fn array(&mut self, n: usize)
pub fn array(&mut self, n: usize)
An array header for n elements. The caller writes the elements next.
Sourcepub fn hoist(&mut self, start: usize, tail: usize)
pub fn hoist(&mut self, start: usize, tail: usize)
Move the last tail bytes back to start, so that something written
after a reply ends up in front of it.
Not every reply knows how long it is before it has been written. SSCAN
walks a window of the set and drops the members that do not match its
pattern, so the count is only true once the last member has been looked
at, and it answers with a cursor that the same walk produced. The
alternatives are both worse: walking the window twice runs the glob
twice, and collecting the members first is an allocation per call on a
thread that must not allocate.
Redis solves this with a linked list of reply nodes it can patch in
place. There is one flat buffer here, so the piece that belongs in front
is written behind and the two are rotated past each other, which is the
trick Out::bulk_double already uses and costs one move of bytes that
were about to be moved to a socket anyway.
§Panics
If start is past the end, or tail is longer than what follows it.
Sourcepub fn close_array(&mut self, start: usize, n: usize)
pub fn close_array(&mut self, start: usize, n: usize)
An array header for the elements written since start, which has to be
a length this buffer reported earlier.
Out::hoist is why this can be called after the elements rather than
before them.
Sourcepub fn close_set(&mut self, start: usize, n: usize)
pub fn close_set(&mut self, start: usize, n: usize)
The same for a set, which is what the algebra commands answer.
SINTER cannot count its own reply in advance any more than SSCAN
can. The answer is however many members survived a walk over the
smallest set, and finding that out ahead of writing it means running the
whole operation twice.
Sourcepub fn close_map(&mut self, start: usize, n: usize)
pub fn close_map(&mut self, start: usize, n: usize)
And for a map whose size is only known once it has been written, which
is XREAD.
XREAD names several streams and leaves out the ones that had nothing
new, so the number of pairs is whatever survived the walk. The count is
n on either protocol and only the tag changes, because the two shapes
do not agree on what a pair is: RESP3 sends a map of stream name to
entries and RESP2 sends an array of two element arrays. The caller writes
one or the other and this closes it.
Sourcepub fn map(&mut self, n: usize)
pub fn map(&mut self, n: usize)
A map header for n pairs. The caller writes 2 * n elements next,
key then value, n times.
RESP2 has no map and gets a flat array of twice as many elements, which
is exactly what a RESP2 client already expects from HGETALL and
CONFIG GET. The command does not know which one it wrote.
Sourcepub fn set(&mut self, n: usize)
pub fn set(&mut self, n: usize)
A set header for n elements.
RESP2 has no set and gets an array, which is what SMEMBERS has always
returned there.
Sourcepub fn push(&mut self, n: usize)
pub fn push(&mut self, n: usize)
A push header for n elements, RESP3’s >.
This is how pub/sub messages and client side caching invalidations are delivered. RESP2 has no out of band type, so they go out as plain arrays on the same connection, which is how RESP2 pub/sub has always worked and is why a RESP2 connection in subscribe mode can only do a handful of things.
Sourcepub fn attribute(&mut self, n: usize)
pub fn attribute(&mut self, n: usize)
An attribute header for n pairs, RESP3’s |.
Attributes are metadata attached to the frame that follows. RESP2 cannot
carry them at all, so the caller must check Out::proto before
writing one. There is no downgrade, because turning metadata into a
reply element would corrupt the reply.
§Panics
In debug, if the connection is not speaking RESP3.
Sourcepub const fn bulk_len(value_len: usize) -> usize
pub const fn bulk_len(value_len: usize) -> usize
The exact number of bytes Out::bulk would write for a value of this
length.
Sourcepub const fn header_len(n: usize) -> usize
pub const fn header_len(n: usize) -> usize
The exact number of bytes an aggregate header of n elements would
write, in either protocol, since both write one byte and the count.
Trait Implementations§
Auto Trait Implementations§
impl Freeze for Out
impl RefUnwindSafe for Out
impl Send for Out
impl Sync for Out
impl Unpin for Out
impl UnsafeUnpin for Out
impl UnwindSafe for Out
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read more