/rust/registry/src/index.crates.io-1949cf8c6b5b557f/url-2.5.7/src/host.rs
Line  | Count  | Source  | 
1  |  | // Copyright 2013-2016 The rust-url developers.  | 
2  |  | //  | 
3  |  | // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or  | 
4  |  | // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license  | 
5  |  | // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your  | 
6  |  | // option. This file may not be copied, modified, or distributed  | 
7  |  | // except according to those terms.  | 
8  |  |  | 
9  |  | use crate::net::{Ipv4Addr, Ipv6Addr}; | 
10  |  | use alloc::borrow::Cow;  | 
11  |  | use alloc::borrow::ToOwned;  | 
12  |  | use alloc::string::String;  | 
13  |  | use alloc::vec::Vec;  | 
14  |  | use core::cmp;  | 
15  |  | use core::fmt::{self, Formatter}; | 
16  |  |  | 
17  |  | use percent_encoding::{percent_decode, utf8_percent_encode, CONTROLS}; | 
18  |  | #[cfg(feature = "serde")]  | 
19  |  | use serde::{Deserialize, Serialize}; | 
20  |  |  | 
21  |  | use crate::parser::{ParseError, ParseResult}; | 
22  |  |  | 
23  |  | #[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]  | 
24  |  | #[derive(Copy, Clone, Debug, Eq, PartialEq)]  | 
25  |  | pub(crate) enum HostInternal { | 
26  |  |     None,  | 
27  |  |     Domain,  | 
28  |  |     Ipv4(Ipv4Addr),  | 
29  |  |     Ipv6(Ipv6Addr),  | 
30  |  | }  | 
31  |  |  | 
32  |  | impl From<Host<Cow<'_, str>>> for HostInternal { | 
33  | 0  |     fn from(host: Host<Cow<'_, str>>) -> Self { | 
34  | 0  |         match host { | 
35  | 0  |             Host::Domain(ref s) if s.is_empty() => Self::None,  | 
36  | 0  |             Host::Domain(_) => Self::Domain,  | 
37  | 0  |             Host::Ipv4(address) => Self::Ipv4(address),  | 
38  | 0  |             Host::Ipv6(address) => Self::Ipv6(address),  | 
39  |  |         }  | 
40  | 0  |     }  | 
41  |  | }  | 
42  |  |  | 
43  |  | /// The host name of an URL.  | 
44  |  | #[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]  | 
45  |  | #[derive(Clone, Debug, Eq, Ord, PartialOrd, Hash)]  | 
46  |  | pub enum Host<S = String> { | 
47  |  |     /// A DNS domain name, as '.' dot-separated labels.  | 
48  |  |     /// Non-ASCII labels are encoded in punycode per IDNA if this is the host of  | 
49  |  |     /// a special URL, or percent encoded for non-special URLs. Hosts for  | 
50  |  |     /// non-special URLs are also called opaque hosts.  | 
51  |  |     Domain(S),  | 
52  |  |  | 
53  |  |     /// An IPv4 address.  | 
54  |  |     /// `Url::host_str` returns the serialization of this address,  | 
55  |  |     /// as four decimal integers separated by `.` dots.  | 
56  |  |     Ipv4(Ipv4Addr),  | 
57  |  |  | 
58  |  |     /// An IPv6 address.  | 
59  |  |     /// `Url::host_str` returns the serialization of that address between `[` and `]` brackets,  | 
60  |  |     /// in the format per [RFC 5952 *A Recommendation  | 
61  |  |     /// for IPv6 Address Text Representation*](https://tools.ietf.org/html/rfc5952):  | 
62  |  |     /// lowercase hexadecimal with maximal `::` compression.  | 
63  |  |     Ipv6(Ipv6Addr),  | 
64  |  | }  | 
65  |  |  | 
66  |  | impl Host<&str> { | 
67  |  |     /// Return a copy of `self` that owns an allocated `String` but does not borrow an `&Url`.  | 
68  | 0  |     pub fn to_owned(&self) -> Host<String> { | 
69  | 0  |         match *self { | 
70  | 0  |             Host::Domain(domain) => Host::Domain(domain.to_owned()),  | 
71  | 0  |             Host::Ipv4(address) => Host::Ipv4(address),  | 
72  | 0  |             Host::Ipv6(address) => Host::Ipv6(address),  | 
73  |  |         }  | 
74  | 0  |     }  | 
75  |  | }  | 
76  |  |  | 
77  |  | impl Host<String> { | 
78  |  |     /// Parse a host: either an IPv6 address in [] square brackets, or a domain.  | 
79  |  |     ///  | 
80  |  |     /// <https://url.spec.whatwg.org/#host-parsing>  | 
81  | 0  |     pub fn parse(input: &str) -> Result<Self, ParseError> { | 
82  | 0  |         Host::<Cow<str>>::parse_cow(input.into()).map(|i| i.into_owned())  | 
83  | 0  |     }  | 
84  |  |  | 
85  |  |     /// <https://url.spec.whatwg.org/#concept-opaque-host-parser>  | 
86  | 0  |     pub fn parse_opaque(input: &str) -> Result<Self, ParseError> { | 
87  | 0  |         Host::<Cow<str>>::parse_opaque_cow(input.into()).map(|i| i.into_owned())  | 
88  | 0  |     }  | 
89  |  | }  | 
90  |  |  | 
91  |  | impl<'a> Host<Cow<'a, str>> { | 
92  | 0  |     pub(crate) fn parse_cow(input: Cow<'a, str>) -> Result<Self, ParseError> { | 
93  | 0  |         if input.starts_with('[') { | 
94  | 0  |             if !input.ends_with(']') { | 
95  | 0  |                 return Err(ParseError::InvalidIpv6Address);  | 
96  | 0  |             }  | 
97  | 0  |             return parse_ipv6addr(&input[1..input.len() - 1]).map(Host::Ipv6);  | 
98  | 0  |         }  | 
99  | 0  |         let domain: Cow<'_, [u8]> = percent_decode(input.as_bytes()).into();  | 
100  | 0  |         let domain: Cow<'a, [u8]> = match domain { | 
101  | 0  |             Cow::Owned(v) => Cow::Owned(v),  | 
102  |  |             // if borrowed then we can use the original cow  | 
103  | 0  |             Cow::Borrowed(_) => match input { | 
104  | 0  |                 Cow::Borrowed(input) => Cow::Borrowed(input.as_bytes()),  | 
105  | 0  |                 Cow::Owned(input) => Cow::Owned(input.into_bytes()),  | 
106  |  |             },  | 
107  |  |         };  | 
108  |  |  | 
109  | 0  |         let domain = idna::domain_to_ascii_from_cow(domain, idna::AsciiDenyList::URL)?;  | 
110  |  |  | 
111  | 0  |         if domain.is_empty() { | 
112  | 0  |             return Err(ParseError::EmptyHost);  | 
113  | 0  |         }  | 
114  |  |  | 
115  | 0  |         if ends_in_a_number(&domain) { | 
116  | 0  |             let address = parse_ipv4addr(&domain)?;  | 
117  | 0  |             Ok(Host::Ipv4(address))  | 
118  |  |         } else { | 
119  | 0  |             Ok(Host::Domain(domain))  | 
120  |  |         }  | 
121  | 0  |     }  | 
122  |  |  | 
123  | 0  |     pub(crate) fn parse_opaque_cow(input: Cow<'a, str>) -> Result<Self, ParseError> { | 
124  | 0  |         if input.starts_with('[') { | 
125  | 0  |             if !input.ends_with(']') { | 
126  | 0  |                 return Err(ParseError::InvalidIpv6Address);  | 
127  | 0  |             }  | 
128  | 0  |             return parse_ipv6addr(&input[1..input.len() - 1]).map(Host::Ipv6);  | 
129  | 0  |         }  | 
130  |  |  | 
131  | 0  |         let is_invalid_host_char = |c| { | 
132  | 0  |             matches!(  | 
133  | 0  |                 c,  | 
134  |  |                 '\0' | '\t'  | 
135  |  |                     | '\n'  | 
136  |  |                     | '\r'  | 
137  |  |                     | ' '  | 
138  |  |                     | '#'  | 
139  |  |                     | '/'  | 
140  |  |                     | ':'  | 
141  |  |                     | '<'  | 
142  |  |                     | '>'  | 
143  |  |                     | '?'  | 
144  |  |                     | '@'  | 
145  |  |                     | '['  | 
146  |  |                     | '\\'  | 
147  |  |                     | ']'  | 
148  |  |                     | '^'  | 
149  |  |                     | '|'  | 
150  |  |             )  | 
151  | 0  |         };  | 
152  |  |  | 
153  | 0  |         if input.find(is_invalid_host_char).is_some() { | 
154  | 0  |             Err(ParseError::InvalidDomainCharacter)  | 
155  |  |         } else { | 
156  |  |             Ok(Host::Domain(  | 
157  | 0  |                 match utf8_percent_encode(&input, CONTROLS).into() { | 
158  | 0  |                     Cow::Owned(v) => Cow::Owned(v),  | 
159  |  |                     // if we're borrowing, then we can return the original Cow  | 
160  | 0  |                     Cow::Borrowed(_) => input,  | 
161  |  |                 },  | 
162  |  |             ))  | 
163  |  |         }  | 
164  | 0  |     }  | 
165  |  |  | 
166  | 0  |     pub(crate) fn into_owned(self) -> Host<String> { | 
167  | 0  |         match self { | 
168  | 0  |             Host::Domain(s) => Host::Domain(s.into_owned()),  | 
169  | 0  |             Host::Ipv4(ip) => Host::Ipv4(ip),  | 
170  | 0  |             Host::Ipv6(ip) => Host::Ipv6(ip),  | 
171  |  |         }  | 
172  | 0  |     }  | 
173  |  | }  | 
174  |  |  | 
175  |  | impl<S: AsRef<str>> fmt::Display for Host<S> { | 
176  | 0  |     fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { | 
177  | 0  |         match *self { | 
178  | 0  |             Self::Domain(ref domain) => domain.as_ref().fmt(f),  | 
179  | 0  |             Self::Ipv4(ref addr) => addr.fmt(f),  | 
180  | 0  |             Self::Ipv6(ref addr) => { | 
181  | 0  |                 f.write_str("[")?; | 
182  | 0  |                 write_ipv6(addr, f)?;  | 
183  | 0  |                 f.write_str("]") | 
184  |  |             }  | 
185  |  |         }  | 
186  | 0  |     } Unexecuted instantiation: <url::host::Host<alloc::borrow::Cow<str>> as core::fmt::Display>::fmt Unexecuted instantiation: <url::host::Host as core::fmt::Display>::fmt  | 
187  |  | }  | 
188  |  |  | 
189  |  | impl<S, T> PartialEq<Host<T>> for Host<S>  | 
190  |  | where  | 
191  |  |     S: PartialEq<T>,  | 
192  |  | { | 
193  | 0  |     fn eq(&self, other: &Host<T>) -> bool { | 
194  | 0  |         match (self, other) { | 
195  | 0  |             (Self::Domain(a), Host::Domain(b)) => a == b,  | 
196  | 0  |             (Self::Ipv4(a), Host::Ipv4(b)) => a == b,  | 
197  | 0  |             (Self::Ipv6(a), Host::Ipv6(b)) => a == b,  | 
198  | 0  |             (_, _) => false,  | 
199  |  |         }  | 
200  | 0  |     } Unexecuted instantiation: <url::host::Host<alloc::borrow::Cow<str>> as core::cmp::PartialEq<url::host::Host>>::eq Unexecuted instantiation: <url::host::Host<&str> as core::cmp::PartialEq>::eq  | 
201  |  | }  | 
202  |  |  | 
203  | 0  | fn write_ipv6(addr: &Ipv6Addr, f: &mut Formatter<'_>) -> fmt::Result { | 
204  | 0  |     let segments = addr.segments();  | 
205  | 0  |     let (compress_start, compress_end) = longest_zero_sequence(&segments);  | 
206  | 0  |     let mut i = 0;  | 
207  | 0  |     while i < 8 { | 
208  | 0  |         if i == compress_start { | 
209  | 0  |             f.write_str(":")?; | 
210  | 0  |             if i == 0 { | 
211  | 0  |                 f.write_str(":")?; | 
212  | 0  |             }  | 
213  | 0  |             if compress_end < 8 { | 
214  | 0  |                 i = compress_end;  | 
215  | 0  |             } else { | 
216  | 0  |                 break;  | 
217  |  |             }  | 
218  | 0  |         }  | 
219  | 0  |         write!(f, "{:x}", segments[i as usize])?; | 
220  | 0  |         if i < 7 { | 
221  | 0  |             f.write_str(":")?; | 
222  | 0  |         }  | 
223  | 0  |         i += 1;  | 
224  |  |     }  | 
225  | 0  |     Ok(())  | 
226  | 0  | }  | 
227  |  |  | 
228  |  | // https://url.spec.whatwg.org/#concept-ipv6-serializer step 2 and 3  | 
229  | 0  | fn longest_zero_sequence(pieces: &[u16; 8]) -> (isize, isize) { | 
230  | 0  |     let mut longest = -1;  | 
231  | 0  |     let mut longest_length = -1;  | 
232  | 0  |     let mut start = -1;  | 
233  |  |     macro_rules! finish_sequence(  | 
234  |  |         ($end: expr) => { | 
235  |  |             if start >= 0 { | 
236  |  |                 let length = $end - start;  | 
237  |  |                 if length > longest_length { | 
238  |  |                     longest = start;  | 
239  |  |                     longest_length = length;  | 
240  |  |                 }  | 
241  |  |             }  | 
242  |  |         };  | 
243  |  |     );  | 
244  | 0  |     for i in 0..8 { | 
245  | 0  |         if pieces[i as usize] == 0 { | 
246  | 0  |             if start < 0 { | 
247  | 0  |                 start = i;  | 
248  | 0  |             }  | 
249  |  |         } else { | 
250  | 0  |             finish_sequence!(i);  | 
251  | 0  |             start = -1;  | 
252  |  |         }  | 
253  |  |     }  | 
254  | 0  |     finish_sequence!(8);  | 
255  |  |     // https://url.spec.whatwg.org/#concept-ipv6-serializer  | 
256  |  |     // step 3: ignore lone zeroes  | 
257  | 0  |     if longest_length < 2 { | 
258  | 0  |         (-1, -2)  | 
259  |  |     } else { | 
260  | 0  |         (longest, longest + longest_length)  | 
261  |  |     }  | 
262  | 0  | }  | 
263  |  |  | 
264  |  | /// <https://url.spec.whatwg.org/#ends-in-a-number-checker>  | 
265  | 0  | fn ends_in_a_number(input: &str) -> bool { | 
266  | 0  |     let mut parts = input.rsplit('.'); | 
267  | 0  |     let last = parts.next().unwrap();  | 
268  | 0  |     let last = if last.is_empty() { | 
269  | 0  |         if let Some(last) = parts.next() { | 
270  | 0  |             last  | 
271  |  |         } else { | 
272  | 0  |             return false;  | 
273  |  |         }  | 
274  |  |     } else { | 
275  | 0  |         last  | 
276  |  |     };  | 
277  | 0  |     if !last.is_empty() && last.as_bytes().iter().all(|c| c.is_ascii_digit()) { | 
278  | 0  |         return true;  | 
279  | 0  |     }  | 
280  |  |  | 
281  | 0  |     parse_ipv4number(last).is_ok()  | 
282  | 0  | }  | 
283  |  |  | 
284  |  | /// <https://url.spec.whatwg.org/#ipv4-number-parser>  | 
285  |  | /// Ok(None) means the input is a valid number, but it overflows a `u32`.  | 
286  | 0  | fn parse_ipv4number(mut input: &str) -> Result<Option<u32>, ()> { | 
287  | 0  |     if input.is_empty() { | 
288  | 0  |         return Err(());  | 
289  | 0  |     }  | 
290  |  |  | 
291  | 0  |     let mut r = 10;  | 
292  | 0  |     if input.starts_with("0x") || input.starts_with("0X") { | 
293  | 0  |         input = &input[2..];  | 
294  | 0  |         r = 16;  | 
295  | 0  |     } else if input.len() >= 2 && input.starts_with('0') { | 
296  | 0  |         input = &input[1..];  | 
297  | 0  |         r = 8;  | 
298  | 0  |     }  | 
299  |  |  | 
300  | 0  |     if input.is_empty() { | 
301  | 0  |         return Ok(Some(0));  | 
302  | 0  |     }  | 
303  |  |  | 
304  | 0  |     let valid_number = match r { | 
305  | 0  |         8 => input.as_bytes().iter().all(|c| (b'0'..=b'7').contains(c)),  | 
306  | 0  |         10 => input.as_bytes().iter().all(|c| c.is_ascii_digit()),  | 
307  | 0  |         16 => input.as_bytes().iter().all(|c| c.is_ascii_hexdigit()),  | 
308  | 0  |         _ => false,  | 
309  |  |     };  | 
310  | 0  |     if !valid_number { | 
311  | 0  |         return Err(());  | 
312  | 0  |     }  | 
313  |  |  | 
314  | 0  |     match u32::from_str_radix(input, r) { | 
315  | 0  |         Ok(num) => Ok(Some(num)),  | 
316  | 0  |         Err(_) => Ok(None), // The only possible error kind here is an integer overflow.  | 
317  |  |                             // The validity of the chars in the input is checked above.  | 
318  |  |     }  | 
319  | 0  | }  | 
320  |  |  | 
321  |  | /// <https://url.spec.whatwg.org/#concept-ipv4-parser>  | 
322  | 0  | fn parse_ipv4addr(input: &str) -> ParseResult<Ipv4Addr> { | 
323  | 0  |     let mut parts: Vec<&str> = input.split('.').collect(); | 
324  | 0  |     if parts.last() == Some(&"") { | 
325  | 0  |         parts.pop();  | 
326  | 0  |     }  | 
327  | 0  |     if parts.len() > 4 { | 
328  | 0  |         return Err(ParseError::InvalidIpv4Address);  | 
329  | 0  |     }  | 
330  | 0  |     let mut numbers: Vec<u32> = Vec::new();  | 
331  | 0  |     for part in parts { | 
332  | 0  |         match parse_ipv4number(part) { | 
333  | 0  |             Ok(Some(n)) => numbers.push(n),  | 
334  | 0  |             Ok(None) => return Err(ParseError::InvalidIpv4Address), // u32 overflow  | 
335  | 0  |             Err(()) => return Err(ParseError::InvalidIpv4Address),  | 
336  |  |         };  | 
337  |  |     }  | 
338  | 0  |     let mut ipv4 = numbers.pop().expect("a non-empty list of numbers"); | 
339  |  |     // Equivalent to: ipv4 >= 256 ** (4 − numbers.len())  | 
340  | 0  |     if ipv4 > u32::MAX >> (8 * numbers.len() as u32) { | 
341  | 0  |         return Err(ParseError::InvalidIpv4Address);  | 
342  | 0  |     }  | 
343  | 0  |     if numbers.iter().any(|x| *x > 255) { | 
344  | 0  |         return Err(ParseError::InvalidIpv4Address);  | 
345  | 0  |     }  | 
346  | 0  |     for (counter, n) in numbers.iter().enumerate() { | 
347  | 0  |         ipv4 += n << (8 * (3 - counter as u32))  | 
348  |  |     }  | 
349  | 0  |     Ok(Ipv4Addr::from(ipv4))  | 
350  | 0  | }  | 
351  |  |  | 
352  |  | /// <https://url.spec.whatwg.org/#concept-ipv6-parser>  | 
353  | 0  | fn parse_ipv6addr(input: &str) -> ParseResult<Ipv6Addr> { | 
354  | 0  |     let input = input.as_bytes();  | 
355  | 0  |     let len = input.len();  | 
356  | 0  |     let mut is_ip_v4 = false;  | 
357  | 0  |     let mut pieces = [0, 0, 0, 0, 0, 0, 0, 0];  | 
358  | 0  |     let mut piece_pointer = 0;  | 
359  | 0  |     let mut compress_pointer = None;  | 
360  | 0  |     let mut i = 0;  | 
361  |  |  | 
362  | 0  |     if len < 2 { | 
363  | 0  |         return Err(ParseError::InvalidIpv6Address);  | 
364  | 0  |     }  | 
365  |  |  | 
366  | 0  |     if input[0] == b':' { | 
367  | 0  |         if input[1] != b':' { | 
368  | 0  |             return Err(ParseError::InvalidIpv6Address);  | 
369  | 0  |         }  | 
370  | 0  |         i = 2;  | 
371  | 0  |         piece_pointer = 1;  | 
372  | 0  |         compress_pointer = Some(1);  | 
373  | 0  |     }  | 
374  |  |  | 
375  | 0  |     while i < len { | 
376  | 0  |         if piece_pointer == 8 { | 
377  | 0  |             return Err(ParseError::InvalidIpv6Address);  | 
378  | 0  |         }  | 
379  | 0  |         if input[i] == b':' { | 
380  | 0  |             if compress_pointer.is_some() { | 
381  | 0  |                 return Err(ParseError::InvalidIpv6Address);  | 
382  | 0  |             }  | 
383  | 0  |             i += 1;  | 
384  | 0  |             piece_pointer += 1;  | 
385  | 0  |             compress_pointer = Some(piece_pointer);  | 
386  | 0  |             continue;  | 
387  | 0  |         }  | 
388  | 0  |         let start = i;  | 
389  | 0  |         let end = cmp::min(len, start + 4);  | 
390  | 0  |         let mut value = 0u16;  | 
391  | 0  |         while i < end { | 
392  | 0  |             match (input[i] as char).to_digit(16) { | 
393  | 0  |                 Some(digit) => { | 
394  | 0  |                     value = value * 0x10 + digit as u16;  | 
395  | 0  |                     i += 1;  | 
396  | 0  |                 }  | 
397  | 0  |                 None => break,  | 
398  |  |             }  | 
399  |  |         }  | 
400  | 0  |         if i < len { | 
401  | 0  |             match input[i] { | 
402  |  |                 b'.' => { | 
403  | 0  |                     if i == start { | 
404  | 0  |                         return Err(ParseError::InvalidIpv6Address);  | 
405  | 0  |                     }  | 
406  | 0  |                     i = start;  | 
407  | 0  |                     if piece_pointer > 6 { | 
408  | 0  |                         return Err(ParseError::InvalidIpv6Address);  | 
409  | 0  |                     }  | 
410  | 0  |                     is_ip_v4 = true;  | 
411  |  |                 }  | 
412  |  |                 b':' => { | 
413  | 0  |                     i += 1;  | 
414  | 0  |                     if i == len { | 
415  | 0  |                         return Err(ParseError::InvalidIpv6Address);  | 
416  | 0  |                     }  | 
417  |  |                 }  | 
418  | 0  |                 _ => return Err(ParseError::InvalidIpv6Address),  | 
419  |  |             }  | 
420  | 0  |         }  | 
421  | 0  |         if is_ip_v4 { | 
422  | 0  |             break;  | 
423  | 0  |         }  | 
424  | 0  |         pieces[piece_pointer] = value;  | 
425  | 0  |         piece_pointer += 1;  | 
426  |  |     }  | 
427  |  |  | 
428  | 0  |     if is_ip_v4 { | 
429  | 0  |         if piece_pointer > 6 { | 
430  | 0  |             return Err(ParseError::InvalidIpv6Address);  | 
431  | 0  |         }  | 
432  | 0  |         let mut numbers_seen = 0;  | 
433  | 0  |         while i < len { | 
434  | 0  |             if numbers_seen > 0 { | 
435  | 0  |                 if numbers_seen < 4 && (i < len && input[i] == b'.') { | 
436  | 0  |                     i += 1  | 
437  |  |                 } else { | 
438  | 0  |                     return Err(ParseError::InvalidIpv6Address);  | 
439  |  |                 }  | 
440  | 0  |             }  | 
441  |  |  | 
442  | 0  |             let mut ipv4_piece = None;  | 
443  | 0  |             while i < len { | 
444  | 0  |                 let digit = match input[i] { | 
445  | 0  |                     c @ b'0'..=b'9' => c - b'0',  | 
446  | 0  |                     _ => break,  | 
447  |  |                 };  | 
448  | 0  |                 match ipv4_piece { | 
449  | 0  |                     None => ipv4_piece = Some(digit as u16),  | 
450  | 0  |                     Some(0) => return Err(ParseError::InvalidIpv6Address), // No leading zero  | 
451  | 0  |                     Some(ref mut v) => { | 
452  | 0  |                         *v = *v * 10 + digit as u16;  | 
453  | 0  |                         if *v > 255 { | 
454  | 0  |                             return Err(ParseError::InvalidIpv6Address);  | 
455  | 0  |                         }  | 
456  |  |                     }  | 
457  |  |                 }  | 
458  | 0  |                 i += 1;  | 
459  |  |             }  | 
460  |  |  | 
461  | 0  |             pieces[piece_pointer] = if let Some(v) = ipv4_piece { | 
462  | 0  |                 pieces[piece_pointer] * 0x100 + v  | 
463  |  |             } else { | 
464  | 0  |                 return Err(ParseError::InvalidIpv6Address);  | 
465  |  |             };  | 
466  | 0  |             numbers_seen += 1;  | 
467  |  |  | 
468  | 0  |             if numbers_seen == 2 || numbers_seen == 4 { | 
469  | 0  |                 piece_pointer += 1;  | 
470  | 0  |             }  | 
471  |  |         }  | 
472  |  |  | 
473  | 0  |         if numbers_seen != 4 { | 
474  | 0  |             return Err(ParseError::InvalidIpv6Address);  | 
475  | 0  |         }  | 
476  | 0  |     }  | 
477  |  |  | 
478  | 0  |     if i < len { | 
479  | 0  |         return Err(ParseError::InvalidIpv6Address);  | 
480  | 0  |     }  | 
481  |  |  | 
482  | 0  |     match compress_pointer { | 
483  | 0  |         Some(compress_pointer) => { | 
484  | 0  |             let mut swaps = piece_pointer - compress_pointer;  | 
485  | 0  |             piece_pointer = 7;  | 
486  | 0  |             while swaps > 0 { | 
487  | 0  |                 pieces.swap(piece_pointer, compress_pointer + swaps - 1);  | 
488  | 0  |                 swaps -= 1;  | 
489  | 0  |                 piece_pointer -= 1;  | 
490  | 0  |             }  | 
491  |  |         }  | 
492  |  |         _ => { | 
493  | 0  |             if piece_pointer != 8 { | 
494  | 0  |                 return Err(ParseError::InvalidIpv6Address);  | 
495  | 0  |             }  | 
496  |  |         }  | 
497  |  |     }  | 
498  | 0  |     Ok(Ipv6Addr::new(  | 
499  | 0  |         pieces[0], pieces[1], pieces[2], pieces[3], pieces[4], pieces[5], pieces[6], pieces[7],  | 
500  | 0  |     ))  | 
501  | 0  | }  |