]>
Commit | Line | Data |
---|---|---|
f035d41b XL |
1 | use std::convert::TryInto; |
2 | use std::io; | |
3 | ||
4 | /// Read a u32 in little endian format from the beginning of the given slice. | |
5 | /// This panics if the slice has length less than 4. | |
6 | #[inline] | |
7 | pub fn read_u32_le(slice: &[u8]) -> u32 { | |
8 | u32::from_le_bytes(slice[..4].try_into().unwrap()) | |
9 | } | |
10 | ||
11 | /// Read a u64 in little endian format from the beginning of the given slice. | |
12 | /// This panics if the slice has length less than 8. | |
13 | #[inline] | |
14 | pub fn read_u64_le(slice: &[u8]) -> u64 { | |
15 | u64::from_le_bytes(slice[..8].try_into().unwrap()) | |
16 | } | |
17 | ||
18 | /// Write a u32 in little endian format to the beginning of the given slice. | |
19 | /// This panics if the slice has length less than 4. | |
20 | #[inline] | |
21 | pub fn write_u32_le(n: u32, slice: &mut [u8]) { | |
22 | assert!(slice.len() >= 4); | |
23 | let bytes = n.to_le_bytes(); | |
24 | slice[0] = bytes[0]; | |
25 | slice[1] = bytes[1]; | |
26 | slice[2] = bytes[2]; | |
27 | slice[3] = bytes[3]; | |
28 | } | |
29 | ||
30 | /// Like write_u32_le, but to an io::Write implementation. If every byte could | |
31 | /// not be writen, then this returns an error. | |
32 | #[inline] | |
33 | pub fn io_write_u32_le<W: io::Write>(n: u32, mut wtr: W) -> io::Result<()> { | |
34 | let mut buf = [0; 4]; | |
35 | write_u32_le(n, &mut buf); | |
36 | wtr.write_all(&buf) | |
37 | } | |
38 | ||
39 | /// Write a u64 in little endian format to the beginning of the given slice. | |
40 | /// This panics if the slice has length less than 8. | |
41 | #[inline] | |
42 | pub fn write_u64_le(n: u64, slice: &mut [u8]) { | |
43 | assert!(slice.len() >= 8); | |
44 | let bytes = n.to_le_bytes(); | |
45 | slice[0] = bytes[0]; | |
46 | slice[1] = bytes[1]; | |
47 | slice[2] = bytes[2]; | |
48 | slice[3] = bytes[3]; | |
49 | slice[4] = bytes[4]; | |
50 | slice[5] = bytes[5]; | |
51 | slice[6] = bytes[6]; | |
52 | slice[7] = bytes[7]; | |
53 | } | |
54 | ||
55 | /// Like write_u64_le, but to an io::Write implementation. If every byte could | |
56 | /// not be writen, then this returns an error. | |
57 | #[inline] | |
58 | pub fn io_write_u64_le<W: io::Write>(n: u64, mut wtr: W) -> io::Result<()> { | |
59 | let mut buf = [0; 8]; | |
60 | write_u64_le(n, &mut buf); | |
61 | wtr.write_all(&buf) | |
62 | } | |
63 | ||
64 | /// pack_uint packs the given integer in the smallest number of bytes possible, | |
65 | /// and writes it to the given writer. The number of bytes written is returned | |
66 | /// on success. | |
67 | #[inline] | |
68 | pub fn pack_uint<W: io::Write>(wtr: W, n: u64) -> io::Result<u8> { | |
69 | let nbytes = pack_size(n); | |
70 | pack_uint_in(wtr, n, nbytes).map(|_| nbytes) | |
71 | } | |
72 | ||
73 | /// pack_uint_in is like pack_uint, but always uses the number of bytes given | |
74 | /// to pack the number given. | |
75 | /// | |
76 | /// `nbytes` must be >= pack_size(n) and <= 8, where `pack_size(n)` is the | |
77 | /// smallest number of bytes that can store the integer given. | |
78 | #[inline] | |
79 | pub fn pack_uint_in<W: io::Write>( | |
80 | mut wtr: W, | |
81 | mut n: u64, | |
82 | nbytes: u8, | |
83 | ) -> io::Result<()> { | |
84 | assert!(1 <= nbytes && nbytes <= 8); | |
85 | let mut buf = [0u8; 8]; | |
86 | for i in 0..nbytes { | |
87 | buf[i as usize] = n as u8; | |
88 | n = n >> 8; | |
89 | } | |
90 | wtr.write_all(&buf[..nbytes as usize])?; | |
91 | Ok(()) | |
92 | } | |
93 | ||
94 | /// unpack_uint is the dual of pack_uint. It unpacks the integer at the current | |
95 | /// position in `slice` after reading `nbytes` bytes. | |
96 | /// | |
97 | /// `nbytes` must be >= 1 and <= 8. | |
98 | #[inline] | |
99 | pub fn unpack_uint(slice: &[u8], nbytes: u8) -> u64 { | |
100 | assert!(1 <= nbytes && nbytes <= 8); | |
101 | ||
102 | let mut n = 0; | |
103 | for (i, &b) in slice[..nbytes as usize].iter().enumerate() { | |
104 | n = n | ((b as u64) << (8 * i)); | |
105 | } | |
106 | n | |
107 | } | |
108 | ||
109 | /// pack_size returns the smallest number of bytes that can encode `n`. | |
110 | #[inline] | |
111 | pub fn pack_size(n: u64) -> u8 { | |
112 | if n < 1 << 8 { | |
113 | 1 | |
114 | } else if n < 1 << 16 { | |
115 | 2 | |
116 | } else if n < 1 << 24 { | |
117 | 3 | |
118 | } else if n < 1 << 32 { | |
119 | 4 | |
120 | } else if n < 1 << 40 { | |
121 | 5 | |
122 | } else if n < 1 << 48 { | |
123 | 6 | |
124 | } else if n < 1 << 56 { | |
125 | 7 | |
126 | } else { | |
127 | 8 | |
128 | } | |
129 | } | |
130 | ||
131 | #[cfg(test)] | |
132 | mod tests { | |
133 | use super::*; | |
134 | use quickcheck::{QuickCheck, StdGen}; | |
135 | use std::io; | |
136 | ||
137 | #[test] | |
138 | fn prop_pack_in_out() { | |
139 | fn p(num: u64) -> bool { | |
140 | let mut buf = io::Cursor::new(vec![]); | |
141 | let size = pack_uint(&mut buf, num).unwrap(); | |
142 | buf.set_position(0); | |
143 | num == unpack_uint(buf.get_ref(), size) | |
144 | } | |
145 | QuickCheck::new() | |
146 | .gen(StdGen::new(::rand::thread_rng(), 257)) // pick byte boundary | |
147 | .quickcheck(p as fn(u64) -> bool); | |
148 | } | |
149 | } |