Skip to main content

capnp/
serialize_packed.rs

1// Copyright (c) 2013-2015 Sandstorm Development Group, Inc. and contributors
2// Licensed under the MIT License:
3//
4// Permission is hereby granted, free of charge, to any person obtaining a copy
5// of this software and associated documentation files (the "Software"), to deal
6// in the Software without restriction, including without limitation the rights
7// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8// copies of the Software, and to permit persons to whom the Software is
9// furnished to do so, subject to the following conditions:
10//
11// The above copyright notice and this permission notice shall be included in
12// all copies or substantial portions of the Software.
13//
14// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20// THE SOFTWARE.
21
22//! Reading and writing of messages using the
23//! [packed stream encoding](https://capnproto.org/encoding.html#packing).
24
25use crate::io::{BufRead, Read, Write};
26use core::{mem, ptr, slice};
27
28use crate::message;
29use crate::serialize;
30use crate::{Error, ErrorKind, Result};
31
32/// A `BufRead` wrapper that unpacks packed data. Returns an error on any `read()`
33/// call that would end within an all-zero (tag 0x00) or uncompressed (tag 0xff)
34/// run of words. Calls that come from `serialize_packed::read_message()` and
35/// `serialize_packed::try_read_message()` always mirror `write()` calls from
36/// `serialize_packed::write_message()`, so they always safely span such runs.
37struct PackedRead<R>
38where
39    R: BufRead,
40{
41    inner: R,
42}
43
44impl<R> PackedRead<R>
45where
46    R: BufRead,
47{
48    fn get_read_buffer(&mut self) -> Result<(*const u8, *const u8)> {
49        let buf = self.inner.fill_buf()?;
50        Ok((buf.as_ptr(), buf.as_ptr().wrapping_add(buf.len())))
51    }
52}
53
54#[inline]
55fn ptr_sub<T>(p1: *const T, p2: *const T) -> usize {
56    (p1 as usize - p2 as usize) / mem::size_of::<T>()
57}
58
59macro_rules! refresh_buffer(
60    ($this:expr, $size:ident, $in_ptr:ident, $in_end:ident, $out:ident,
61     $outBuf:ident, $buffer_begin:ident) => (
62        {
63            $this.inner.consume($size);
64            let (b, e) = $this.get_read_buffer()?;
65            $in_ptr = b;
66            $in_end = e;
67            $size = ptr_sub($in_end, $in_ptr);
68            $buffer_begin = b;
69            if $size == 0 {
70                return Err(Error::from_kind(ErrorKind::PrematureEndOfPackedInput));
71            }
72        }
73        );
74    );
75
76impl<R> Read for PackedRead<R>
77where
78    R: BufRead,
79{
80    fn read(&mut self, out_buf: &mut [u8]) -> Result<usize> {
81        let len = out_buf.len();
82        if len == 0 {
83            return Ok(0);
84        }
85
86        assert!(len % 8 == 0, "PackedRead reads must be word-aligned.");
87
88        unsafe {
89            let out_buf_start = out_buf.as_mut_ptr();
90            let mut out = out_buf_start;
91            let out_end: *mut u8 = out.wrapping_add(len);
92
93            let (mut in_ptr, mut in_end) = self.get_read_buffer()?;
94            let mut buffer_begin = in_ptr;
95            let mut size = ptr_sub(in_end, in_ptr);
96            if size == 0 {
97                return Ok(0);
98            }
99
100            loop {
101                let tag: u8;
102
103                assert_eq!(
104                    ptr_sub(out, out_buf_start) % 8,
105                    0,
106                    "Output pointer should always be aligned here."
107                );
108
109                if ptr_sub(in_end, in_ptr) < 10 {
110                    if ptr_sub(in_end, in_ptr) == 0 {
111                        refresh_buffer!(self, size, in_ptr, in_end, out, out_buf, buffer_begin);
112                        continue;
113                    }
114
115                    //# We have at least 1, but not 10, bytes available. We need to read
116                    //# slowly, doing a bounds check on each byte.
117
118                    tag = *in_ptr;
119                    in_ptr = in_ptr.add(1);
120
121                    for i in 0..8 {
122                        if (tag & (1u8 << i)) != 0 {
123                            if ptr_sub(in_end, in_ptr) == 0 {
124                                refresh_buffer!(
125                                    self,
126                                    size,
127                                    in_ptr,
128                                    in_end,
129                                    out,
130                                    out_buf,
131                                    buffer_begin
132                                );
133                            }
134                            *out = *in_ptr;
135                            out = out.add(1);
136                            in_ptr = in_ptr.add(1);
137                        } else {
138                            *out = 0;
139                            out = out.add(1);
140                        }
141                    }
142
143                    if ptr_sub(in_end, in_ptr) == 0 && (tag == 0 || tag == 0xff) {
144                        refresh_buffer!(self, size, in_ptr, in_end, out, out_buf, buffer_begin);
145                    }
146                } else {
147                    tag = *in_ptr;
148                    in_ptr = in_ptr.add(1);
149
150                    for n in 0..8 {
151                        let is_nonzero = (tag & (1u8 << n)) != 0;
152                        *out = (*in_ptr) & ((-i8::from(is_nonzero)) as u8);
153                        out = out.add(1);
154                        in_ptr = in_ptr.add(is_nonzero as usize);
155                    }
156                }
157                if tag == 0 {
158                    assert!(
159                        ptr_sub(in_end, in_ptr) > 0,
160                        "Should always have non-empty buffer here."
161                    );
162
163                    let run_length: usize = (*in_ptr) as usize * 8;
164                    in_ptr = in_ptr.add(1);
165
166                    if run_length > ptr_sub(out_end, out) {
167                        return Err(Error::from_kind(
168                            ErrorKind::PackedInputDidNotEndCleanlyOnASegmentBoundary,
169                        ));
170                    }
171
172                    ptr::write_bytes(out, 0, run_length);
173                    out = out.add(run_length);
174                } else if tag == 0xff {
175                    assert!(
176                        ptr_sub(in_end, in_ptr) > 0,
177                        "Should always have non-empty buffer here"
178                    );
179
180                    let mut run_length: usize = (*in_ptr) as usize * 8;
181                    in_ptr = in_ptr.add(1);
182
183                    if run_length > ptr_sub(out_end, out) {
184                        return Err(Error::from_kind(
185                            ErrorKind::PackedInputDidNotEndCleanlyOnASegmentBoundary,
186                        ));
187                    }
188
189                    let in_remaining = ptr_sub(in_end, in_ptr);
190                    if in_remaining >= run_length {
191                        //# Fast path.
192                        ptr::copy_nonoverlapping(in_ptr, out, run_length);
193                        out = out.add(run_length);
194                        in_ptr = in_ptr.add(run_length);
195                    } else {
196                        //# Copy over the first buffer, then do one big read for the rest.
197                        ptr::copy_nonoverlapping(in_ptr, out, in_remaining);
198                        out = out.add(in_remaining);
199                        run_length -= in_remaining;
200
201                        self.inner.consume(size);
202                        {
203                            let buf = slice::from_raw_parts_mut::<u8>(out, run_length);
204                            self.inner.read_exact(buf)?;
205                        }
206
207                        out = out.add(run_length);
208
209                        if out == out_end {
210                            return Ok(len);
211                        } else {
212                            let (b, e) = self.get_read_buffer()?;
213                            in_ptr = b;
214                            in_end = e;
215                            size = ptr_sub(e, b);
216                            buffer_begin = in_ptr;
217                            continue;
218                        }
219                    }
220                }
221
222                if out == out_end {
223                    self.inner.consume(ptr_sub(in_ptr, buffer_begin));
224                    return Ok(len);
225                }
226            }
227        }
228    }
229}
230
231/// Reads a packed message from a stream using the provided options.
232#[cfg(feature = "alloc")]
233pub fn read_message<R>(
234    read: R,
235    options: message::ReaderOptions,
236) -> Result<crate::message::Reader<serialize::OwnedSegments>>
237where
238    R: BufRead,
239{
240    let packed_read = PackedRead { inner: read };
241    serialize::read_message(packed_read, options)
242}
243
244/// Like read_message(), but returns None instead of an error if there are zero bytes left in `read`.
245#[cfg(feature = "alloc")]
246pub fn try_read_message<R>(
247    read: R,
248    options: message::ReaderOptions,
249) -> Result<Option<crate::message::Reader<serialize::OwnedSegments>>>
250where
251    R: BufRead,
252{
253    let packed_read = PackedRead { inner: read };
254    serialize::try_read_message(packed_read, options)
255}
256
257/// Like read_message(), but does not allocate.
258///
259/// Stores the message in `buffer`. Returns a `BufferNotLargeEnough`
260/// error if the buffer is not large enough.
261/// ALIGNMENT: If the "unaligned" feature is enabled, then there are no alignment requirements on `buffer`.
262/// Otherwise, `buffer` must be 8-byte aligned (attempts to read the message will trigger errors).
263pub fn read_message_no_alloc<R>(
264    read: R,
265    buffer: &mut [u8],
266    options: message::ReaderOptions,
267) -> Result<crate::message::Reader<serialize::NoAllocBufferSegments<&[u8]>>>
268where
269    R: BufRead,
270{
271    let packed_read = PackedRead { inner: read };
272    serialize::read_message_no_alloc(packed_read, buffer, options)
273}
274
275/// Like try_read_message(), but does not allocate.
276///
277/// Stores the message in `buffer`. Returns a `BufferNotLargeEnough`
278/// error if the buffer is not large enough.
279/// ALIGNMENT: If the "unaligned" feature is enabled, then there are no alignment requirements on `buffer`.
280/// Otherwise, `buffer` must be 8-byte aligned (attempts to read the message will trigger errors).
281pub fn try_read_message_no_alloc<R>(
282    read: R,
283    buffer: &mut [u8],
284    options: message::ReaderOptions,
285) -> Result<Option<crate::message::Reader<serialize::NoAllocBufferSegments<&[u8]>>>>
286where
287    R: BufRead,
288{
289    let packed_read = PackedRead { inner: read };
290    serialize::try_read_message_no_alloc(packed_read, buffer, options)
291}
292
293struct PackedWrite<W>
294where
295    W: Write,
296{
297    inner: W,
298}
299
300impl<W> Write for PackedWrite<W>
301where
302    W: Write,
303{
304    fn write_all(&mut self, in_buf: &[u8]) -> Result<()> {
305        unsafe {
306            let mut buf_idx: usize = 0;
307            let mut buf: [u8; 64] = [0; 64];
308
309            let mut in_ptr: *const u8 = in_buf.as_ptr();
310            let in_end: *const u8 = in_buf.as_ptr().wrapping_add(in_buf.len());
311
312            while in_ptr < in_end {
313                if buf_idx + 10 > buf.len() {
314                    //# Oops, we're out of space. We need at least 10
315                    //# bytes for the fast path, since we don't
316                    //# bounds-check on every byte.
317                    self.inner.write_all(&buf[..buf_idx])?;
318                    buf_idx = 0;
319                }
320
321                let tag_pos = buf_idx;
322                buf_idx += 1;
323
324                let bit0 = u8::from(*in_ptr != 0);
325                *buf.get_unchecked_mut(buf_idx) = *in_ptr;
326                buf_idx += bit0 as usize;
327                in_ptr = in_ptr.add(1);
328
329                let bit1 = u8::from(*in_ptr != 0);
330                *buf.get_unchecked_mut(buf_idx) = *in_ptr;
331                buf_idx += bit1 as usize;
332                in_ptr = in_ptr.add(1);
333
334                let bit2 = u8::from(*in_ptr != 0);
335                *buf.get_unchecked_mut(buf_idx) = *in_ptr;
336                buf_idx += bit2 as usize;
337                in_ptr = in_ptr.add(1);
338
339                let bit3 = u8::from(*in_ptr != 0);
340                *buf.get_unchecked_mut(buf_idx) = *in_ptr;
341                buf_idx += bit3 as usize;
342                in_ptr = in_ptr.add(1);
343
344                let bit4 = u8::from(*in_ptr != 0);
345                *buf.get_unchecked_mut(buf_idx) = *in_ptr;
346                buf_idx += bit4 as usize;
347                in_ptr = in_ptr.add(1);
348
349                let bit5 = u8::from(*in_ptr != 0);
350                *buf.get_unchecked_mut(buf_idx) = *in_ptr;
351                buf_idx += bit5 as usize;
352                in_ptr = in_ptr.add(1);
353
354                let bit6 = u8::from(*in_ptr != 0);
355                *buf.get_unchecked_mut(buf_idx) = *in_ptr;
356                buf_idx += bit6 as usize;
357                in_ptr = in_ptr.add(1);
358
359                let bit7 = u8::from(*in_ptr != 0);
360                *buf.get_unchecked_mut(buf_idx) = *in_ptr;
361                buf_idx += bit7 as usize;
362                in_ptr = in_ptr.add(1);
363
364                let tag: u8 = bit0
365                    | (bit1 << 1)
366                    | (bit2 << 2)
367                    | (bit3 << 3)
368                    | (bit4 << 4)
369                    | (bit5 << 5)
370                    | (bit6 << 6)
371                    | (bit7 << 7);
372
373                *buf.get_unchecked_mut(tag_pos) = tag;
374
375                if tag == 0 {
376                    //# An all-zero word is followed by a count of
377                    //# consecutive zero words (not including the first
378                    //# one).
379
380                    let mut in_word: *const [u8; 8] = in_ptr as *const [u8; 8];
381                    let mut limit: *const [u8; 8] = in_end as *const [u8; 8];
382                    if ptr_sub(limit, in_word) > 255 {
383                        limit = in_word.add(255);
384                    }
385                    while in_word < limit && *in_word == [0; 8] {
386                        in_word = in_word.add(1);
387                    }
388
389                    *buf.get_unchecked_mut(buf_idx) = ptr_sub(in_word, in_ptr as *const [u8; 8])
390                        .try_into()
391                        .unwrap();
392                    buf_idx += 1;
393                    in_ptr = in_word as *const u8;
394                } else if tag == 0xff {
395                    //# An all-nonzero word is followed by a count of
396                    //# consecutive uncompressed words, followed by the
397                    //# uncompressed words themselves.
398
399                    //# Count the number of consecutive words in the input
400                    //# which have no more than a single zero-byte. We look
401                    //# for at least two zeros because that's the point
402                    //# where our compression scheme becomes a net win.
403                    let run_start = in_ptr;
404                    let mut limit = in_end;
405                    if ptr_sub(limit, in_ptr) > 255 * 8 {
406                        limit = in_ptr.add(255 * 8);
407                    }
408
409                    while in_ptr < limit {
410                        let mut c = 0;
411
412                        for _ in 0..8 {
413                            c += u8::from(*in_ptr == 0);
414                            in_ptr = in_ptr.add(1);
415                        }
416
417                        if c >= 2 {
418                            //# Un-read the word with multiple zeros, since
419                            //# we'll want to compress that one.
420                            in_ptr = in_ptr.sub(8);
421                            break;
422                        }
423                    }
424
425                    let count: usize = ptr_sub(in_ptr, run_start);
426                    *buf.get_unchecked_mut(buf_idx) = (count / 8).try_into().unwrap();
427                    buf_idx += 1;
428
429                    self.inner.write_all(&buf[..buf_idx])?;
430                    buf_idx = 0;
431                    self.inner
432                        .write_all(slice::from_raw_parts::<u8>(run_start, count))?;
433                }
434            }
435
436            self.inner.write_all(&buf[..buf_idx])?;
437            Ok(())
438        }
439    }
440}
441
442/// Writes a packed message to a stream.
443///
444/// The only source of errors from this function are `write.write_all()` calls. If you pass in
445/// a writer that never returns an error, then this function will never return an error.
446pub fn write_message<W, A>(write: W, message: &crate::message::Builder<A>) -> Result<()>
447where
448    W: Write,
449    A: crate::message::Allocator,
450{
451    let packed_write = PackedWrite { inner: write };
452    serialize::write_message(packed_write, message)
453}
454
455#[cfg(feature = "alloc")]
456#[cfg(test)]
457mod tests {
458    use crate::io::{Read, Write};
459
460    use quickcheck::{quickcheck, TestResult};
461
462    use super::read_message;
463    use crate::message::ReaderOptions;
464    use crate::serialize::test::write_message_segments;
465    use crate::serialize_packed::{PackedRead, PackedWrite};
466    use crate::ErrorKind;
467
468    #[test]
469    pub(crate) fn premature_eof() {
470        let input_bytes: &[u8] = &[];
471        let mut packed_read = PackedRead { inner: input_bytes };
472
473        let mut output_bytes: alloc::vec::Vec<u8> = vec![0; 8];
474        assert!(packed_read.read_exact(&mut output_bytes[..]).is_err());
475    }
476
477    pub(crate) fn check_unpacks_to(packed: &[u8], unpacked: &[u8]) {
478        let mut packed_read = PackedRead { inner: packed };
479
480        let mut bytes: alloc::vec::Vec<u8> = vec![0; unpacked.len()];
481        packed_read.read_exact(&mut bytes[..]).unwrap();
482
483        assert!(packed_read.inner.is_empty()); // nothing left to read
484        assert_eq!(bytes, unpacked);
485    }
486
487    pub(crate) fn check_packing(unpacked: &[u8], packed: &[u8]) {
488        // --------
489        // write
490
491        let mut bytes: alloc::vec::Vec<u8> = vec![0; packed.len()];
492        {
493            let mut packed_write = PackedWrite {
494                inner: &mut bytes[..],
495            };
496            packed_write.write_all(unpacked).unwrap();
497        }
498
499        assert_eq!(bytes, packed);
500
501        // --------
502        // read
503        check_unpacks_to(packed, unpacked);
504    }
505
506    #[test]
507    pub(crate) fn simple_packing() {
508        check_packing(&[], &[]);
509        check_packing(&[0; 8], &[0, 0]);
510        check_packing(&[0, 0, 12, 0, 0, 34, 0, 0], &[0x24, 12, 34]);
511        check_packing(
512            &[1, 3, 2, 4, 5, 7, 6, 8],
513            &[0xff, 1, 3, 2, 4, 5, 7, 6, 8, 0],
514        );
515        check_packing(
516            &[0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 2, 4, 5, 7, 6, 8],
517            &[0, 0, 0xff, 1, 3, 2, 4, 5, 7, 6, 8, 0],
518        );
519        check_packing(
520            &[0, 0, 12, 0, 0, 34, 0, 0, 1, 3, 2, 4, 5, 7, 6, 8],
521            &[0x24, 12, 34, 0xff, 1, 3, 2, 4, 5, 7, 6, 8, 0],
522        );
523        check_packing(
524            &[1, 3, 2, 4, 5, 7, 6, 8, 8, 6, 7, 4, 5, 2, 3, 1],
525            &[0xff, 1, 3, 2, 4, 5, 7, 6, 8, 1, 8, 6, 7, 4, 5, 2, 3, 1],
526        );
527
528        check_packing(
529            &[
530                1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4,
531                5, 6, 7, 8, 0, 2, 4, 0, 9, 0, 5, 1,
532            ],
533            &[
534                0xff, 1, 2, 3, 4, 5, 6, 7, 8, 3, 1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8, 1,
535                2, 3, 4, 5, 6, 7, 8, 0xd6, 2, 4, 9, 5, 1,
536            ],
537        );
538        check_packing(
539            &[
540                1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8, 6, 2, 4, 3, 9, 0, 5, 1, 1, 2, 3, 4,
541                5, 6, 7, 8, 0, 2, 4, 0, 9, 0, 5, 1,
542            ],
543            &[
544                0xff, 1, 2, 3, 4, 5, 6, 7, 8, 3, 1, 2, 3, 4, 5, 6, 7, 8, 6, 2, 4, 3, 9, 0, 5, 1, 1,
545                2, 3, 4, 5, 6, 7, 8, 0xd6, 2, 4, 9, 5, 1,
546            ],
547        );
548
549        check_packing(
550            &[
551                8, 0, 100, 6, 0, 1, 1, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
552                0, 0, 0, 0, 0, 0, 0, 1, 0, 2, 0, 3, 1,
553            ],
554            &[0xed, 8, 100, 6, 1, 1, 2, 0, 2, 0xd4, 1, 2, 3, 1],
555        );
556
557        check_packing(&[0; 8], &[0, 0]);
558        check_packing(&[0; 16], &[0, 1]);
559        check_packing(
560            &[
561                0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
562            ],
563            &[0, 2],
564        );
565        check_packing(&[0; 258 * 8], &[0, 255, 0, 1]);
566    }
567
568    quickcheck! {
569        #[cfg_attr(miri, ignore)] // miri takes a long time with quickcheck
570        fn test_round_trip(segments: alloc::vec::Vec<alloc::vec::Vec<crate::Word>>) -> TestResult {
571            use crate::message::ReaderSegments;
572            if segments.is_empty() { return TestResult::discard(); }
573            let mut buf: alloc::vec::Vec<u8> = alloc::vec::Vec::new();
574
575            write_message_segments(&mut PackedWrite { inner: &mut buf }, &segments);
576            let message = read_message(&mut &buf[..], ReaderOptions::new()).unwrap();
577            let result_segments = message.into_segments();
578
579            TestResult::from_bool(segments.iter().enumerate().all(|(i, segment)| {
580                crate::Word::words_to_bytes(&segment[..]) == result_segments.get_segment(i as u32).unwrap()
581            }))
582        }
583
584        #[cfg_attr(miri, ignore)] // miri takes a long time with quickcheck
585        fn test_unpack(packed: alloc::vec::Vec<u8>) -> TestResult {
586            let len = packed.len();
587            let mut packed_read = PackedRead { inner: &packed[..] };
588
589            let mut out_buffer: alloc::vec::Vec<u8> = vec![0; len * 8];
590
591            let _ = packed_read.read_exact(&mut out_buffer);
592            TestResult::from_bool(true)
593        }
594    }
595
596    #[test]
597    fn did_not_end_cleanly_on_a_segment_boundary() {
598        let packed = &[0xff, 1, 2, 3, 4, 5, 6, 7, 8, 37, 1, 2];
599        let mut packed_read = PackedRead { inner: &packed[..] };
600
601        let mut bytes: alloc::vec::Vec<u8> = vec![0; 200];
602        match packed_read.read_exact(&mut bytes[..]) {
603            Ok(_) => panic!("should have been an error"),
604            Err(e) => {
605                assert_eq!(
606                    e.kind,
607                    ErrorKind::PackedInputDidNotEndCleanlyOnASegmentBoundary,
608                );
609            }
610        }
611    }
612
613    #[test]
614    fn premature_end_of_packed_input() {
615        fn helper(packed: &[u8]) {
616            let mut packed_read = PackedRead { inner: packed };
617
618            let mut bytes: alloc::vec::Vec<u8> = vec![0; 200];
619            match packed_read.read_exact(&mut bytes[..]) {
620                Ok(_) => panic!("should have been an error"),
621                Err(e) => {
622                    assert_eq!(e.kind, ErrorKind::PrematureEndOfPackedInput);
623                }
624            }
625        }
626
627        helper(&[0xf0, 1, 2]);
628        helper(&[0]);
629        helper(&[0xff, 1, 2, 3, 4, 5, 6, 7, 8]);
630
631        // In this case, the error is only due to the fact that the unpacked data does not
632        // fill up the given output buffer.
633        helper(&[1, 1]);
634    }
635
636    #[test]
637    fn packed_segment_table() {
638        let packed_buf = &[0x11, 4, 1, 0, 1, 0, 0];
639
640        check_unpacks_to(
641            packed_buf,
642            &[
643                4, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
644                0, 0, 0, 0,
645            ],
646        );
647
648        // At one point, this failed due to serialize::read_message()
649        // reading the segment table only one word at a time.
650        read_message(&mut &packed_buf[..], Default::default()).unwrap();
651    }
652}