md_codec/
use_site_path.rs1use crate::bitstream::{BitReader, BitWriter};
14use crate::error::Error;
15use crate::varint::{read_varint, write_varint};
16
17#[derive(Debug, Clone, Copy, PartialEq, Eq)]
19pub struct Alternative {
20 pub hardened: bool,
22 pub value: u32,
24}
25
26impl Alternative {
27 pub fn write(&self, w: &mut BitWriter) -> Result<(), Error> {
29 w.write_bits(u64::from(self.hardened), 1);
30 write_varint(w, self.value)?;
31 Ok(())
32 }
33
34 pub fn read(r: &mut BitReader) -> Result<Self, Error> {
36 let hardened = r.read_bits(1)? != 0;
37 let value = read_varint(r)?;
38 Ok(Self { hardened, value })
39 }
40}
41
42pub const MIN_ALT_COUNT: usize = 2;
44pub const MAX_ALT_COUNT: usize = 9;
46
47#[derive(Debug, Clone, PartialEq, Eq)]
49pub struct UseSitePath {
50 pub multipath: Option<Vec<Alternative>>,
52 pub wildcard_hardened: bool,
54}
55
56impl UseSitePath {
57 pub fn standard_multipath() -> Self {
59 Self {
60 multipath: Some(vec![
61 Alternative {
62 hardened: false,
63 value: 0,
64 },
65 Alternative {
66 hardened: false,
67 value: 1,
68 },
69 ]),
70 wildcard_hardened: false,
71 }
72 }
73
74 pub fn write(&self, w: &mut BitWriter) -> Result<(), Error> {
81 if let Some(alts) = &self.multipath {
82 if !(MIN_ALT_COUNT..=MAX_ALT_COUNT).contains(&alts.len()) {
83 return Err(Error::AltCountOutOfRange { got: alts.len() });
84 }
85 w.write_bits(1, 1);
86 w.write_bits((alts.len() - MIN_ALT_COUNT) as u64, 3);
88 for a in alts {
89 a.write(w)?;
90 }
91 } else {
92 w.write_bits(0, 1);
93 }
94 w.write_bits(u64::from(self.wildcard_hardened), 1);
95 Ok(())
96 }
97
98 pub fn read(r: &mut BitReader) -> Result<Self, Error> {
100 let has_multipath = r.read_bits(1)? != 0;
101 let multipath = if has_multipath {
102 let alt_count = (r.read_bits(3)? as usize) + MIN_ALT_COUNT;
103 let mut alts = Vec::with_capacity(alt_count);
104 for _ in 0..alt_count {
105 alts.push(Alternative::read(r)?);
106 }
107 Some(alts)
108 } else {
109 None
110 };
111 let wildcard_hardened = r.read_bits(1)? != 0;
112 Ok(Self {
113 multipath,
114 wildcard_hardened,
115 })
116 }
117}
118
119#[cfg(test)]
120mod tests {
121 use super::*;
122
123 #[test]
124 fn use_site_path_standard_round_trip() {
125 let p = UseSitePath::standard_multipath();
126 let mut w = BitWriter::new();
127 p.write(&mut w).unwrap();
128 let bytes = w.into_bytes();
129 let mut r = BitReader::new(&bytes);
130 assert_eq!(UseSitePath::read(&mut r).unwrap(), p);
131 }
132
133 #[test]
134 fn use_site_path_standard_bit_cost() {
135 let p = UseSitePath::standard_multipath();
137 let mut w = BitWriter::new();
138 p.write(&mut w).unwrap();
139 assert_eq!(w.bit_len(), 16);
140 }
141
142 #[test]
143 fn use_site_path_bare_star_round_trip() {
144 let p = UseSitePath {
145 multipath: None,
146 wildcard_hardened: false,
147 };
148 let mut w = BitWriter::new();
149 p.write(&mut w).unwrap();
150 let bytes = w.into_bytes();
151 let mut r = BitReader::new(&bytes);
152 assert_eq!(UseSitePath::read(&mut r).unwrap(), p);
153 }
154
155 #[test]
156 fn use_site_path_bare_star_bit_cost() {
157 let p = UseSitePath {
159 multipath: None,
160 wildcard_hardened: false,
161 };
162 let mut w = BitWriter::new();
163 p.write(&mut w).unwrap();
164 assert_eq!(w.bit_len(), 2);
165 }
166
167 #[test]
168 fn use_site_path_hardened_wildcard_round_trip() {
169 let p = UseSitePath {
170 multipath: None,
171 wildcard_hardened: true,
172 };
173 let mut w = BitWriter::new();
174 p.write(&mut w).unwrap();
175 let bytes = w.into_bytes();
176 let mut r = BitReader::new(&bytes);
177 assert_eq!(UseSitePath::read(&mut r).unwrap(), p);
178 }
179
180 #[test]
181 fn use_site_path_alt_count_too_small_rejected() {
182 let p = UseSitePath {
183 multipath: Some(vec![Alternative {
184 hardened: false,
185 value: 0,
186 }]),
187 wildcard_hardened: false,
188 };
189 let mut w = BitWriter::new();
190 assert!(matches!(
191 p.write(&mut w),
192 Err(Error::AltCountOutOfRange { got: 1 })
193 ));
194 }
195
196 #[test]
197 fn use_site_path_alt_count_too_large_rejected() {
198 let p = UseSitePath {
199 multipath: Some(
200 (0..10)
201 .map(|i| Alternative {
202 hardened: false,
203 value: i,
204 })
205 .collect(),
206 ),
207 wildcard_hardened: false,
208 };
209 let mut w = BitWriter::new();
210 assert!(matches!(
211 p.write(&mut w),
212 Err(Error::AltCountOutOfRange { got: 10 })
213 ));
214 }
215}