Skip to main content

parse_net/
write.rs

1//! Output: the [`Sink`] trait, [`SliceSink`], [`CountSink`], and the writers
2//! for numbers, tokens, quoted strings, parameters, and field values.
3//!
4//! Every writer validates all of its input before the first
5//! [`put`](Sink::put), and checks [`remaining`](Sink::remaining) when its
6//! output takes several `put` calls, so after any error a [`SliceSink`] holds
7//! exactly what it held before. No writer can emit CR, LF, or NUL, and no
8//! writer produces a value its own parser would refuse.
9//!
10//! # Examples
11//!
12//! ```
13//! use parse_net::{self as pn, write};
14//!
15//! let mut buf = [0u8; 64];
16//! let mut out = pn::SliceSink::new(&mut buf);
17//! write::write_word(b"gzip", &mut out)?;
18//! write::write_param(b"title", b"say \"hi\"", &mut out)?;
19//! assert_eq!(out.written(), b"gzip; title=\"say \\\"hi\\\"\"");
20//! assert_eq!(write::write_quoted(b"a\nb", &mut out), Err(pn::WriteError::Forbidden { at: 1 }));
21//! # Ok::<(), pn::WriteError>(())
22//! ```
23
24use core::fmt;
25
26pub use crate::error::Full;
27use crate::{class, error::WriteError, text};
28
29/// The output abstraction every writer takes.
30///
31/// `put` appends all of `bytes` or fails with [`Full`]. `remaining` reports
32/// how many more bytes fit, when the sink knows; writers that produce a value
33/// in several `put` calls check it first, so a sink that knows its room never
34/// holds half a value after an error.
35///
36/// # Examples
37///
38/// ```
39/// use parse_net::{Full, Sink};
40///
41/// /// A sink that keeps only the last byte it was given.
42/// struct Last(Option<u8>);
43/// impl Sink for Last {
44///     fn put(&mut self, bytes: &[u8]) -> Result<(), Full> {
45///         if let Some(&b) = bytes.last() {
46///             self.0 = Some(b);
47///         }
48///         Ok(())
49///     }
50/// }
51///
52/// let mut last = Last(None);
53/// parse_net::write::write_token(b"gzip", &mut last)?;
54/// assert_eq!(last.0, Some(b'p'));
55/// # Ok::<(), parse_net::WriteError>(())
56/// ```
57pub trait Sink {
58    /// Appends all of `bytes`, or fails with [`Full`] and appends nothing.
59    ///
60    /// # Errors
61    ///
62    /// [`Full`] when the sink has no room for `bytes`.
63    fn put(&mut self, bytes: &[u8]) -> Result<(), Full>;
64
65    /// How many more bytes fit, when the sink knows; `None` otherwise.
66    fn remaining(&self) -> Option<usize> {
67        None
68    }
69}
70
71impl<S: Sink + ?Sized> Sink for &mut S {
72    #[inline]
73    fn put(&mut self, bytes: &[u8]) -> Result<(), Full> {
74        (**self).put(bytes)
75    }
76
77    #[inline]
78    fn remaining(&self) -> Option<usize> {
79        (**self).remaining()
80    }
81}
82
83#[cfg(feature = "alloc")]
84#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
85impl Sink for alloc::vec::Vec<u8> {
86    #[inline]
87    fn put(&mut self, bytes: &[u8]) -> Result<(), Full> {
88        self.extend_from_slice(bytes);
89        Ok(())
90    }
91}
92
93/// A cursor over a caller buffer.
94///
95/// No allocation; reports [`remaining`](Sink::remaining); a `put` that does
96/// not fit writes nothing. `Debug` prints the lengths only, never the bytes.
97///
98/// # Examples
99///
100/// ```
101/// use parse_net::{Sink, SliceSink};
102///
103/// let mut buf = [0u8; 8];
104/// let mut out = SliceSink::new(&mut buf);
105/// out.put(b"gzip")?;
106/// assert_eq!(out.written(), b"gzip");
107/// assert_eq!(out.remaining(), Some(4));
108/// out.clear();
109/// assert!(out.is_empty());
110/// # Ok::<(), parse_net::Full>(())
111/// ```
112pub struct SliceSink<'a> {
113    buf: &'a mut [u8],
114    len: usize,
115}
116
117impl<'a> SliceSink<'a> {
118    /// A sink that writes into `buf`, starting at its first byte.
119    ///
120    /// # Examples
121    ///
122    /// ```
123    /// use parse_net::{Sink, SliceSink};
124    ///
125    /// let mut buf = [0u8; 4];
126    /// let out = SliceSink::new(&mut buf);
127    /// assert_eq!((out.len(), out.remaining()), (0, Some(4)));
128    /// ```
129    #[must_use]
130    pub fn new(buf: &'a mut [u8]) -> Self {
131        Self { buf, len: 0 }
132    }
133
134    /// The bytes written so far.
135    ///
136    /// # Examples
137    ///
138    /// ```
139    /// use parse_net::{write, SliceSink};
140    ///
141    /// let mut buf = [0u8; 16];
142    /// let mut out = SliceSink::new(&mut buf);
143    /// write::write_token(b"br", &mut out)?;
144    /// assert_eq!(out.written(), b"br");
145    /// # Ok::<(), parse_net::WriteError>(())
146    /// ```
147    #[must_use]
148    pub fn written(&self) -> &[u8] {
149        self.buf.get(..self.len).unwrap_or(&[])
150    }
151
152    /// The number of bytes written so far.
153    ///
154    /// # Examples
155    ///
156    /// ```
157    /// use parse_net::{write, SliceSink};
158    ///
159    /// let mut buf = [0u8; 16];
160    /// let mut out = SliceSink::new(&mut buf);
161    /// write::write_quoted(b"a b", &mut out)?;
162    /// assert_eq!(out.len(), 5);
163    /// # Ok::<(), parse_net::WriteError>(())
164    /// ```
165    #[must_use]
166    pub fn len(&self) -> usize {
167        self.len
168    }
169
170    /// Whether nothing has been written.
171    ///
172    /// # Examples
173    ///
174    /// ```
175    /// use parse_net::{write, SliceSink};
176    ///
177    /// let mut buf = [0u8; 16];
178    /// let mut out = SliceSink::new(&mut buf);
179    /// assert!(out.is_empty());
180    /// write::write_token(b"gzip", &mut out)?;
181    /// assert!(!out.is_empty());
182    /// # Ok::<(), parse_net::WriteError>(())
183    /// ```
184    #[must_use]
185    pub fn is_empty(&self) -> bool {
186        self.len == 0
187    }
188
189    /// Forgets what was written; the buffer is reused from its start.
190    ///
191    /// # Examples
192    ///
193    /// ```
194    /// use parse_net::{write, SliceSink};
195    ///
196    /// let mut buf = [0u8; 8];
197    /// let mut out = SliceSink::new(&mut buf);
198    /// write::write_token(b"gzip", &mut out)?;
199    /// out.clear();
200    /// write::write_token(b"br", &mut out)?;
201    /// assert_eq!(out.written(), b"br");
202    /// # Ok::<(), parse_net::WriteError>(())
203    /// ```
204    pub fn clear(&mut self) {
205        self.len = 0;
206    }
207}
208
209impl Sink for SliceSink<'_> {
210    #[inline]
211    fn put(&mut self, bytes: &[u8]) -> Result<(), Full> {
212        let end = self.len.checked_add(bytes.len()).ok_or(Full)?;
213        let dst = self.buf.get_mut(self.len..end).ok_or(Full)?;
214        dst.copy_from_slice(bytes);
215        self.len = end;
216        Ok(())
217    }
218
219    #[inline]
220    fn remaining(&self) -> Option<usize> {
221        Some(self.buf.len().saturating_sub(self.len))
222    }
223}
224
225impl fmt::Debug for SliceSink<'_> {
226    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
227        f.debug_struct("SliceSink")
228            .field("len", &self.len)
229            .field("capacity", &self.buf.len())
230            .finish()
231    }
232}
233
234/// A sink that counts bytes and writes nothing.
235///
236/// Run a writer into it to learn the exact output length, then size a buffer
237/// once. Saturates at `usize::MAX`.
238///
239/// # Examples
240///
241/// ```
242/// use parse_net::{self as pn, write};
243///
244/// let mut count = pn::CountSink::new();
245/// write::write_param(b"filename", b"my report.pdf", &mut count)?;
246/// let mut buf = [0u8; 64];
247/// let mut out = pn::SliceSink::new(&mut buf[..count.count()]);
248/// write::write_param(b"filename", b"my report.pdf", &mut out)?;
249/// assert_eq!(out.written(), b"; filename=\"my report.pdf\"");
250/// # Ok::<(), pn::WriteError>(())
251/// ```
252#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
253pub struct CountSink {
254    n: usize,
255}
256
257impl CountSink {
258    /// A counter at zero.
259    ///
260    /// # Examples
261    ///
262    /// ```
263    /// use parse_net::CountSink;
264    ///
265    /// const ZERO: CountSink = CountSink::new();
266    /// assert_eq!((ZERO.count(), ZERO), (0, CountSink::default()));
267    /// ```
268    #[must_use]
269    pub const fn new() -> Self {
270        Self { n: 0 }
271    }
272
273    /// The number of bytes put so far.
274    ///
275    /// # Examples
276    ///
277    /// ```
278    /// use parse_net::{write, CountSink};
279    ///
280    /// let mut count = CountSink::new();
281    /// write::write_quoted(b"a b", &mut count)?;
282    /// assert_eq!(count.count(), 5);
283    /// # Ok::<(), parse_net::WriteError>(())
284    /// ```
285    #[must_use]
286    pub const fn count(&self) -> usize {
287        self.n
288    }
289}
290
291impl Sink for CountSink {
292    #[inline]
293    fn put(&mut self, bytes: &[u8]) -> Result<(), Full> {
294        self.n = self.n.saturating_add(bytes.len());
295        Ok(())
296    }
297}
298
299/// Fails with `Full` before anything is written when the sink knows it has
300/// less than `len` bytes of room.
301#[inline]
302pub(crate) fn reserve(out: &impl Sink, len: usize) -> Result<(), WriteError> {
303    match out.remaining() {
304        Some(room) if room < len => Err(WriteError::Full),
305        _ => Ok(()),
306    }
307}
308
309/// Fails with `DuplicateName` when two parameter names are equal ignoring
310/// ASCII case: the output would be refused by every parser of the field.
311pub(crate) fn unique_names(params: &[(&str, &str)]) -> Result<(), WriteError> {
312    for (i, (a, _)) in params.iter().enumerate() {
313        let later = params.get(i + 1..).unwrap_or(&[]);
314        if later.iter().any(|(b, _)| a.eq_ignore_ascii_case(b)) {
315            return Err(WriteError::DuplicateName);
316        }
317    }
318    Ok(())
319}
320
321/// Writes `n` in decimal, with no sign and no padding.
322///
323/// # Errors
324///
325/// [`WriteError::Full`].
326///
327/// # Examples
328///
329/// ```
330/// use parse_net::{write::write_decimal_u64, SliceSink};
331///
332/// let mut buf = [0u8; 20];
333/// let mut out = SliceSink::new(&mut buf);
334/// write_decimal_u64(u64::MAX, &mut out)?;
335/// assert_eq!(out.written(), b"18446744073709551615");
336/// # Ok::<(), parse_net::WriteError>(())
337/// ```
338pub fn write_decimal_u64(n: u64, out: &mut impl Sink) -> Result<(), WriteError> {
339    let mut digits = [0u8; 20];
340    let mut i = digits.len();
341    let mut v = n;
342    loop {
343        i -= 1;
344        if let Some(d) = digits.get_mut(i) {
345            // `v % 10` is below 10, so the cast cannot truncate.
346            *d = b'0' + (v % 10) as u8;
347        }
348        v /= 10;
349        if v == 0 || i == 0 {
350            break;
351        }
352    }
353    out.put(digits.get(i..).unwrap_or(&[]))?;
354    Ok(())
355}
356
357/// Writes `n` in lowercase hexadecimal, with no prefix and no padding.
358///
359/// # Errors
360///
361/// [`WriteError::Full`].
362///
363/// # Examples
364///
365/// ```
366/// use parse_net::{write::write_hex_u64, SliceSink};
367///
368/// let mut buf = [0u8; 16];
369/// let mut out = SliceSink::new(&mut buf);
370/// write_hex_u64(0xdead_beef, &mut out)?;
371/// assert_eq!(out.written(), b"deadbeef");
372/// # Ok::<(), parse_net::WriteError>(())
373/// ```
374pub fn write_hex_u64(n: u64, out: &mut impl Sink) -> Result<(), WriteError> {
375    const HEX: &[u8; 16] = b"0123456789abcdef";
376    let mut digits = [0u8; 16];
377    let mut i = digits.len();
378    let mut v = n;
379    loop {
380        i -= 1;
381        if let (Some(d), Some(&h)) = (digits.get_mut(i), HEX.get((v & 0xf) as usize)) {
382            *d = h;
383        }
384        v >>= 4;
385        if v == 0 || i == 0 {
386            break;
387        }
388    }
389    out.put(digits.get(i..).unwrap_or(&[]))?;
390    Ok(())
391}
392
393/// Writes `s` as a token, after checking it is one.
394///
395/// # Errors
396///
397/// [`WriteError::NotToken`] (also for empty input), [`WriteError::Full`].
398///
399/// # Examples
400///
401/// ```
402/// use parse_net::{write::write_token, SliceSink, WriteError};
403///
404/// let mut buf = [0u8; 16];
405/// let mut out = SliceSink::new(&mut buf);
406/// write_token(b"no-cache", &mut out)?;
407/// assert_eq!(write_token(b"no cache", &mut out), Err(WriteError::NotToken));
408/// assert_eq!(out.written(), b"no-cache");
409/// # Ok::<(), WriteError>(())
410/// ```
411pub fn write_token(s: &[u8], out: &mut impl Sink) -> Result<(), WriteError> {
412    if !text::is_token(s) {
413        return Err(WriteError::NotToken);
414    }
415    out.put(s)?;
416    Ok(())
417}
418
419/// The length of `s` written as a quoted string, after checking every byte
420/// may be written.
421pub(crate) fn quoted_len(s: &[u8]) -> Result<usize, WriteError> {
422    let mut len = s.len().saturating_add(2);
423    for (i, &b) in s.iter().enumerate() {
424        match b {
425            b'"' | b'\\' => len = len.saturating_add(1),
426            b'\t' => {}
427            _ if class::is_quoted_pair_char(b) => {}
428            _ => return Err(WriteError::forbidden(i)),
429        }
430    }
431    Ok(len)
432}
433
434/// Writes `s` as a quoted string.
435///
436/// Only `"` and `\` are escaped (RFC 9110 section 5.6.4: senders SHOULD NOT
437/// generate other quoted pairs); HTAB, SP, VCHAR, and obs-text are written as
438/// they are.
439///
440/// # Errors
441///
442/// [`WriteError::Forbidden`] for CR, LF, NUL, another control byte, or DEL;
443/// [`WriteError::Full`].
444///
445/// # Examples
446///
447/// ```
448/// use parse_net::{write::write_quoted, SliceSink};
449///
450/// let mut buf = [0u8; 16];
451/// let mut out = SliceSink::new(&mut buf);
452/// write_quoted(b"a \"b\"", &mut out)?;
453/// assert_eq!(out.written(), b"\"a \\\"b\\\"\"");
454/// # Ok::<(), parse_net::WriteError>(())
455/// ```
456pub fn write_quoted(s: &[u8], out: &mut impl Sink) -> Result<(), WriteError> {
457    let len = quoted_len(s)?;
458    reserve(out, len)?;
459    put_quoted(s, out)
460}
461
462/// Writes an already validated quoted string.
463fn put_quoted(s: &[u8], out: &mut impl Sink) -> Result<(), WriteError> {
464    out.put(b"\"")?;
465    let mut rest = s;
466    while let Some(i) = rest.iter().position(|&b| b == b'"' || b == b'\\') {
467        let (plain, tail) = rest.split_at(i);
468        out.put(plain)?;
469        if let Some((escaped, after)) = tail.split_first() {
470            out.put(&[b'\\', *escaped])?;
471            rest = after;
472        } else {
473            rest = &[];
474        }
475    }
476    out.put(rest)?;
477    out.put(b"\"")?;
478    Ok(())
479}
480
481/// The length of `s` written by [`write_word`].
482pub(crate) fn word_len(s: &[u8]) -> Result<usize, WriteError> {
483    if text::is_token(s) {
484        Ok(s.len())
485    } else {
486        quoted_len(s)
487    }
488}
489
490/// Writes `s` as a token when it is a non-empty token, and as a quoted string
491/// otherwise (`token / quoted-string`, RFC 9110 section 5.6.6).
492///
493/// # Errors
494///
495/// As [`write_quoted`].
496///
497/// # Examples
498///
499/// ```
500/// use parse_net::{write::write_word, SliceSink};
501///
502/// let mut buf = [0u8; 32];
503/// let mut out = SliceSink::new(&mut buf);
504/// write_word(b"utf-8", &mut out)?;
505/// write_word(b" ", &mut out)?;
506/// write_word(b"", &mut out)?;
507/// assert_eq!(out.written(), b"utf-8\" \"\"\"");
508/// # Ok::<(), parse_net::WriteError>(())
509/// ```
510pub fn write_word(s: &[u8], out: &mut impl Sink) -> Result<(), WriteError> {
511    if text::is_token(s) {
512        out.put(s)?;
513        Ok(())
514    } else {
515        write_quoted(s, out)
516    }
517}
518
519/// Writes `s` as a field value, after checking every byte is `field-vchar`,
520/// SP, HTAB, or `obs-text`.
521///
522/// This is the header-injection guard: a value containing CR, LF, or NUL can
523/// never reach the wire through it.
524///
525/// # Errors
526///
527/// [`WriteError::Forbidden`] at the first refused byte; [`WriteError::Full`].
528///
529/// # Examples
530///
531/// ```
532/// use parse_net::{write::write_field_value, SliceSink, WriteError};
533///
534/// let mut buf = [0u8; 32];
535/// let mut out = SliceSink::new(&mut buf);
536/// write_field_value(b"text/html; charset=utf-8", &mut out)?;
537/// assert_eq!(write_field_value(b"a\r\nb", &mut out), Err(WriteError::Forbidden { at: 1 }));
538/// # Ok::<(), WriteError>(())
539/// ```
540pub fn write_field_value(s: &[u8], out: &mut impl Sink) -> Result<(), WriteError> {
541    if let Err(e) = crate::scan::check_field_value(s) {
542        return Err(WriteError::forbidden(e.offset()));
543    }
544    out.put(s)?;
545    Ok(())
546}
547
548/// Writes `; name=value`, the value through [`write_word`].
549///
550/// # Errors
551///
552/// [`WriteError::NotToken`] for a name that is not a token, then as
553/// [`write_word`].
554///
555/// # Examples
556///
557/// ```
558/// use parse_net::{write::write_param, SliceSink};
559///
560/// let mut buf = [0u8; 32];
561/// let mut out = SliceSink::new(&mut buf);
562/// write_param(b"charset", b"utf-8", &mut out)?;
563/// assert_eq!(out.written(), b"; charset=utf-8");
564/// # Ok::<(), parse_net::WriteError>(())
565/// ```
566pub fn write_param(name: &[u8], value: &[u8], out: &mut impl Sink) -> Result<(), WriteError> {
567    if !text::is_token(name) {
568        return Err(WriteError::NotToken);
569    }
570    let value_len = word_len(value)?;
571    reserve(out, name.len().saturating_add(3).saturating_add(value_len))?;
572    out.put(b"; ")?;
573    out.put(name)?;
574    out.put(b"=")?;
575    if text::is_token(value) {
576        out.put(value)?;
577        Ok(())
578    } else {
579        put_quoted(value, out)
580    }
581}
582
583#[cfg(test)]
584mod tests {
585    use std::vec::Vec;
586
587    use super::*;
588
589    fn written(f: impl Fn(&mut SliceSink<'_>) -> Result<(), WriteError>) -> Vec<u8> {
590        let mut buf = [0u8; 256];
591        let mut out = SliceSink::new(&mut buf);
592        f(&mut out).unwrap();
593        out.written().to_vec()
594    }
595
596    #[test]
597    fn test_number_writers_known_values() {
598        assert_eq!(written(|o| write_decimal_u64(0, o)), b"0");
599        assert_eq!(written(|o| write_decimal_u64(10, o)), b"10");
600        assert_eq!(
601            written(|o| write_decimal_u64(u64::MAX, o)),
602            b"18446744073709551615"
603        );
604        assert_eq!(written(|o| write_hex_u64(0, o)), b"0");
605        assert_eq!(written(|o| write_hex_u64(u64::MAX, o)), b"ffffffffffffffff");
606        assert_eq!(written(|o| write_hex_u64(0x10, o)), b"10");
607    }
608
609    #[test]
610    fn test_writers_refuse_crlf_in_every_position() {
611        let bad: Vec<u8> = (0u8..0x20).filter(|&b| b != b'\t').chain([0x7f]).collect();
612        for &b in &bad {
613            for pos in 0..3 {
614                let mut input = *b"abc";
615                input[pos] = b;
616                let mut buf = [0u8; 64];
617                let mut out = SliceSink::new(&mut buf);
618                let at = pos as u32;
619                assert_eq!(
620                    write_quoted(&input, &mut out),
621                    Err(WriteError::Forbidden { at })
622                );
623                assert_eq!(
624                    write_word(&input, &mut out),
625                    Err(WriteError::Forbidden { at })
626                );
627                assert_eq!(
628                    write_field_value(&input, &mut out),
629                    Err(WriteError::Forbidden { at })
630                );
631                assert_eq!(
632                    write_param(b"n", &input, &mut out),
633                    Err(WriteError::Forbidden { at })
634                );
635                assert_eq!(write_token(&input, &mut out), Err(WriteError::NotToken));
636                assert!(out.is_empty());
637            }
638        }
639    }
640
641    #[test]
642    fn test_quoted_escapes_only_quote_and_backslash() {
643        assert_eq!(written(|o| write_quoted(b"", o)), b"\"\"");
644        assert_eq!(
645            written(|o| write_quoted(b"a\\b\"c\td\x80", o)),
646            b"\"a\\\\b\\\"c\td\x80\""
647        );
648    }
649
650    #[test]
651    fn test_slice_sink_one_byte_short_writes_nothing() {
652        for (input, needed) in [(&b"a\"b"[..], 6usize), (b"x y", 5)] {
653            let mut buf = [0u8; 16];
654            let mut out = SliceSink::new(&mut buf[..needed - 1]);
655            assert_eq!(write_quoted(input, &mut out), Err(WriteError::Full));
656            assert!(out.is_empty());
657            let mut out = SliceSink::new(&mut buf[..needed]);
658            assert_eq!(write_quoted(input, &mut out), Ok(()));
659        }
660        let mut buf = [0u8; 9];
661        let mut out = SliceSink::new(&mut buf);
662        assert_eq!(
663            write_param(b"name", b"a b", &mut out),
664            Err(WriteError::Full)
665        );
666        assert!(out.is_empty());
667        let mut buf = [0u8; 3];
668        let mut out = SliceSink::new(&mut buf);
669        assert_eq!(write_decimal_u64(1234, &mut out), Err(WriteError::Full));
670        assert!(out.is_empty());
671    }
672
673    #[test]
674    fn test_count_sink_matches_slice_output() {
675        let mut count = CountSink::new();
676        write_param(b"a", b"x\"y", &mut count).unwrap();
677        assert_eq!(
678            count.count(),
679            written(|o| write_param(b"a", b"x\"y", o)).len()
680        );
681        let mut c = CountSink { n: usize::MAX };
682        c.put(b"x").unwrap();
683        assert_eq!(c.count(), usize::MAX);
684    }
685
686    #[test]
687    fn test_param_name_must_be_token() {
688        let mut buf = [0u8; 16];
689        let mut out = SliceSink::new(&mut buf);
690        assert_eq!(
691            write_param(b"a b", b"x", &mut out),
692            Err(WriteError::NotToken)
693        );
694        assert_eq!(write_param(b"", b"x", &mut out), Err(WriteError::NotToken));
695    }
696
697    #[test]
698    fn test_sink_for_mut_reference_and_debug() {
699        let mut buf = [0u8; 4];
700        let mut out = SliceSink::new(&mut buf);
701        {
702            let r = &mut out;
703            write_token(b"ab", &mut &mut *r).unwrap();
704            assert_eq!(Sink::remaining(&r), Some(2));
705        }
706        assert_eq!(std::format!("{out:?}"), "SliceSink { len: 2, capacity: 4 }");
707    }
708
709    #[cfg(feature = "alloc")]
710    #[test]
711    fn test_vec_sink_grows() {
712        let mut v = Vec::new();
713        write_quoted(b"a b", &mut v).unwrap();
714        assert_eq!(v, b"\"a b\"");
715        assert_eq!(Sink::remaining(&v), None);
716    }
717}