From 11f929e960d4a1e6c375e310e328d84c1b6d2b01 Mon Sep 17 00:00:00 2001 From: John Elfberg Larsson Date: Fri, 3 Mar 2023 12:38:29 +0100 Subject: [PATCH 01/10] Use alloc/core instead of std Also implement our own bare-bones version of Ipv{4,6}Addr --- src/enc/imp.rs | 9 +-- src/enc/mod.rs | 7 +- src/fmt.rs | 3 +- src/internal.rs | 3 +- src/lib.rs | 174 ++++++++++++++++++++++++++++++++++++++++++++-- src/parser.rs | 10 +-- src/view.rs | 2 +- tests/parse.rs | 2 +- tests/parse_ip.rs | 4 +- 9 files changed, 187 insertions(+), 27 deletions(-) diff --git a/src/enc/imp.rs b/src/enc/imp.rs index eb6421b4..cb89fe80 100644 --- a/src/enc/imp.rs +++ b/src/enc/imp.rs @@ -3,10 +3,11 @@ use super::table; #[cfg(feature = "unstable")] use super::table::Table; -use std::{fmt, ptr}; +use alloc::vec::Vec; +use core::{fmt, ptr}; #[cfg(feature = "unstable")] -use std::{borrow::Cow, str}; +use alloc::{borrow::Cow, str}; /// Returns immediately with an encoding error. macro_rules! err { @@ -57,7 +58,7 @@ impl EncodingError { } } -impl std::error::Error for EncodingError {} +// impl std::error::Error for EncodingError {} impl fmt::Display for EncodingError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { @@ -70,7 +71,7 @@ impl fmt::Display for EncodingError { } } -pub(crate) type Result = std::result::Result; +pub(crate) type Result = core::result::Result; #[cfg(feature = "unstable")] const fn gen_hex_table() -> [u8; 512] { diff --git a/src/enc/mod.rs b/src/enc/mod.rs index 3ef19dc7..1fd7ee61 100644 --- a/src/enc/mod.rs +++ b/src/enc/mod.rs @@ -89,13 +89,16 @@ pub fn validate + ?Sized>(s: &S, table: &Table) -> Result<()> { } } -use std::{ +use alloc::{ borrow::{self, Cow}, + string::{FromUtf8Error, String}, + vec::Vec, +}; +use core::{ fmt, hash, iter::FusedIterator, mem, str::{self, Utf8Error}, - string::FromUtf8Error, }; use crate::view::View; diff --git a/src/fmt.rs b/src/fmt.rs index c85f8f50..ec7094ff 100644 --- a/src/fmt.rs +++ b/src/fmt.rs @@ -1,5 +1,6 @@ use super::*; -use std::fmt; +use alloc::string::String; +use core::fmt; impl fmt::Display for ParseError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { diff --git a/src/internal.rs b/src/internal.rs index 03eb7c8b..6823ae44 100644 --- a/src/internal.rs +++ b/src/internal.rs @@ -1,6 +1,7 @@ #![allow(missing_debug_implementations)] -use std::{ +use alloc::{string::String, vec::Vec}; +use core::{ cell::Cell, mem::MaybeUninit, num::NonZeroU32, diff --git a/src/lib.rs b/src/lib.rs index b32d5171..d384ee8e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,5 +1,6 @@ #![warn(missing_debug_implementations, missing_docs, rust_2018_idioms)] #![deny(unsafe_op_in_unsafe_fn)] +#![cfg_attr(not(test), no_std)] //! A generic URI parser that strictly adheres to IETF [RFC 3986]. //! @@ -30,6 +31,8 @@ //! [`InvalidIpLiteral`]: ParseErrorKind::InvalidIpLiteral //! [draft]: https://datatracker.ietf.org/doc/html/draft-ietf-6man-rfc6874bis-02 +extern crate alloc; + /// Utilities for percent-encoding. pub mod enc; @@ -41,17 +44,174 @@ pub use view::*; mod parser; use crate::enc::{EStr, Split}; -use std::{ +use alloc::{string::String, vec::Vec}; +use core::{ + cmp::Ordering, + iter::Iterator, marker::PhantomData, - mem::ManuallyDrop, - net::{Ipv4Addr, Ipv6Addr}, + mem::{transmute, ManuallyDrop}, + // net::{Ipv4Addr, Ipv6Addr}, ptr::NonNull, - slice, str, + slice, + str, }; mod internal; use internal::*; +/// Bare-bones Ipv4Addr for `no_std` environments. +#[derive(Copy, Clone, PartialEq, Eq, Hash)] +pub struct Ipv4Addr { + octets: [u8; 4], +} + +impl Ipv4Addr { + #[inline] + pub const fn new(a: u8, b: u8, c: u8, d: u8) -> Ipv4Addr { + Ipv4Addr { + octets: [a, b, c, d], + } + } + + #[inline] + pub const fn octets(&self) -> [u8; 4] { + self.octets + } +} + +impl PartialOrd for Ipv4Addr { + #[inline] + fn partial_cmp(&self, other: &Ipv4Addr) -> Option { + Some(self.cmp(other)) + } +} + +impl Ord for Ipv4Addr { + #[inline] + fn cmp(&self, other: &Ipv4Addr) -> Ordering { + self.octets().cmp(&other.octets()) + } +} + +impl From for Ipv4Addr { + fn from(ip: u32) -> Self { + Self { + octets: ip.to_be_bytes(), + } + } +} + +impl core::fmt::Debug for Ipv4Addr { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + core::fmt::Display::fmt(&self, f) + } +} + +impl core::fmt::Display for Ipv4Addr { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!( + f, + "{}.{}.{}.{}", + self.octets[0], self.octets[1], self.octets[2], self.octets[3] + ) + } +} + +/// Bare-bones Ipv6Addr for `no_std` environments. +#[derive(Copy, Clone, PartialEq, Eq, Hash)] +pub struct Ipv6Addr { + octets: [u8; 16], +} + +impl Ipv6Addr { + #[inline] + pub const fn new(a: u16, b: u16, c: u16, d: u16, e: u16, f: u16, g: u16, h: u16) -> Ipv6Addr { + let addr16 = [ + a.to_be(), + b.to_be(), + c.to_be(), + d.to_be(), + e.to_be(), + f.to_be(), + g.to_be(), + h.to_be(), + ]; + Ipv6Addr { + // All elements in `addr16` are big endian. + // SAFETY: `[u16; 8]` is always safe to transmute to `[u8; 16]`. + octets: unsafe { transmute::<_, [u8; 16]>(addr16) }, + } + } + + #[inline] + pub const fn octets(&self) -> [u8; 16] { + self.octets + } + + #[inline] + pub const fn segments(&self) -> [u16; 8] { + // All elements in `self.octets` must be big endian. + // SAFETY: `[u8; 16]` is always safe to transmute to `[u16; 8]`. + let [a, b, c, d, e, f, g, h] = unsafe { transmute::<_, [u16; 8]>(self.octets) }; + // We want native endian u16 + [ + u16::from_be(a), + u16::from_be(b), + u16::from_be(c), + u16::from_be(d), + u16::from_be(e), + u16::from_be(f), + u16::from_be(g), + u16::from_be(h), + ] + } +} + +impl PartialOrd for Ipv6Addr { + #[inline] + fn partial_cmp(&self, other: &Ipv6Addr) -> Option { + Some(self.cmp(other)) + } +} + +impl Ord for Ipv6Addr { + #[inline] + fn cmp(&self, other: &Ipv6Addr) -> Ordering { + self.segments().cmp(&other.segments()) + } +} +impl core::fmt::Debug for Ipv6Addr { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "{:?}", self.octets()) + } +} + +impl From<[u16; 8]> for Ipv6Addr { + fn from(segments: [u16; 8]) -> Self { + let segments = segments.map(|s| s.to_be_bytes()); + let octets = [ + segments[0][0], + segments[0][1], + segments[1][0], + segments[1][1], + segments[2][0], + segments[2][1], + segments[3][0], + segments[3][1], + segments[4][0], + segments[4][1], + segments[5][0], + segments[5][1], + segments[6][0], + segments[6][1], + segments[7][0], + segments[7][1], + ]; + + Self { octets } + } +} + /// Detailed cause of a [`ParseError`]. #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum ParseErrorKind { @@ -90,9 +250,9 @@ impl ParseError { } } -impl std::error::Error for ParseError {} +// impl std::error::Error for ParseError {} -type Result = std::result::Result; +type Result = core::result::Result; #[cold] fn len_overflow() -> ! { @@ -135,7 +295,7 @@ fn len_overflow() -> ! { /// # Examples /// /// Create and convert between `Uri<&str>` and `Uri`: -/// +/// /// ``` /// use fluent_uri::Uri; /// diff --git a/src/parser.rs b/src/parser.rs index d903f395..cd132d75 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -1,15 +1,9 @@ use crate::{ enc::{imp::OCTET_TABLE_LO, table::*}, internal::Pointer, - AuthData, Data, RawHostData as HostData, Result, Tag, Uri, -}; -use std::{ - cell::Cell, - marker::PhantomData, - net::{Ipv4Addr, Ipv6Addr}, - num::NonZeroU32, - str, + AuthData, Data, Ipv4Addr, Ipv6Addr, RawHostData as HostData, Result, Tag, Uri, }; +use core::{cell::Cell, marker::PhantomData, num::NonZeroU32, str}; use super::{internal::Storage, Ipv6Data}; diff --git a/src/view.rs b/src/view.rs index f79bb1e2..65609604 100644 --- a/src/view.rs +++ b/src/view.rs @@ -1,4 +1,4 @@ -use std::{num::NonZeroU32, ops::Deref}; +use core::{num::NonZeroU32, ops::Deref}; use super::*; use crate::enc::SplitView; diff --git a/tests/parse.rs b/tests/parse.rs index 05aceb91..d82274d3 100644 --- a/tests/parse.rs +++ b/tests/parse.rs @@ -1,4 +1,4 @@ -use std::net::{Ipv4Addr, Ipv6Addr}; +// use std::net::{Ipv4Addr, Ipv6Addr}; use fluent_uri::{enc::EStr, ParseErrorKind::*, *}; diff --git a/tests/parse_ip.rs b/tests/parse_ip.rs index 154f81a3..66ff53cb 100644 --- a/tests/parse_ip.rs +++ b/tests/parse_ip.rs @@ -1,6 +1,6 @@ -use std::net::{Ipv4Addr, Ipv6Addr}; +// use std::net::{Ipv4Addr, Ipv6Addr}; -use fluent_uri::{HostData, Uri}; +use fluent_uri::{HostData, Ipv4Addr, Ipv6Addr, Uri}; fn parse_v4(s: &str) -> Option { let s = format!("//{s}"); From 5cd92d40eb10c4166609fdd2fde2b2dc82816de8 Mon Sep 17 00:00:00 2001 From: John Elfberg Larsson Date: Fri, 3 Mar 2023 12:52:37 +0100 Subject: [PATCH 02/10] Add simple docs to the Ipv{4,6}Addr functions --- src/lib.rs | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index d384ee8e..0c59fa55 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -66,6 +66,7 @@ pub struct Ipv4Addr { } impl Ipv4Addr { + /// Creates a new IPv4 address from four bytes. #[inline] pub const fn new(a: u8, b: u8, c: u8, d: u8) -> Ipv4Addr { Ipv4Addr { @@ -73,6 +74,7 @@ impl Ipv4Addr { } } + /// Returns the four bytes that make up the IPv4 address. #[inline] pub const fn octets(&self) -> [u8; 4] { self.octets @@ -124,6 +126,7 @@ pub struct Ipv6Addr { } impl Ipv6Addr { + /// Creates a new IPv6 address from eight 16-bit segments. #[inline] pub const fn new(a: u16, b: u16, c: u16, d: u16, e: u16, f: u16, g: u16, h: u16) -> Ipv6Addr { let addr16 = [ @@ -143,11 +146,7 @@ impl Ipv6Addr { } } - #[inline] - pub const fn octets(&self) -> [u8; 16] { - self.octets - } - + /// Returns the eight 16-bit segments that make up the IPv6 address. #[inline] pub const fn segments(&self) -> [u16; 8] { // All elements in `self.octets` must be big endian. @@ -182,7 +181,7 @@ impl Ord for Ipv6Addr { } impl core::fmt::Debug for Ipv6Addr { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - write!(f, "{:?}", self.octets()) + write!(f, "{:?}", self.segments()) } } From 8ce9cf7eed8335c0e469894398db613d8507513b Mon Sep 17 00:00:00 2001 From: John Elfberg Larsson Date: Fri, 3 Mar 2023 13:18:24 +0100 Subject: [PATCH 03/10] Fix no_std for the unstable feature flag --- src/enc/estring.rs | 8 ++++++-- src/enc/imp.rs | 4 +--- src/enc/mod.rs | 12 +++++------- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/enc/estring.rs b/src/enc/estring.rs index 7779119e..7fb6a3f6 100644 --- a/src/enc/estring.rs +++ b/src/enc/estring.rs @@ -1,4 +1,8 @@ -use std::{borrow::Borrow, fmt, hash, marker::PhantomData, ops::Deref}; +use alloc::string::String; +use core::{borrow::Borrow, fmt, hash, marker::PhantomData, ops::Deref}; + +#[cfg(feature = "unstable")] +use alloc::vec::Vec; use super::{ encoder::Encoder, @@ -341,7 +345,7 @@ impl Clone for EString { impl fmt::Debug for EString { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("EString") - .field("encoder", &std::any::type_name::()) + .field("encoder", &core::any::type_name::()) .field("contents", &self.string) .finish() } diff --git a/src/enc/imp.rs b/src/enc/imp.rs index cb89fe80..0991eaf1 100644 --- a/src/enc/imp.rs +++ b/src/enc/imp.rs @@ -7,7 +7,7 @@ use alloc::vec::Vec; use core::{fmt, ptr}; #[cfg(feature = "unstable")] -use alloc::{borrow::Cow, str}; +use alloc::{borrow::Cow, str, string::String}; /// Returns immediately with an encoding error. macro_rules! err { @@ -58,8 +58,6 @@ impl EncodingError { } } -// impl std::error::Error for EncodingError {} - impl fmt::Display for EncodingError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let msg = match self.kind { diff --git a/src/enc/mod.rs b/src/enc/mod.rs index 1fd7ee61..ce76d5c0 100644 --- a/src/enc/mod.rs +++ b/src/enc/mod.rs @@ -110,7 +110,7 @@ use crate::view::View; /// Parse key-value pairs from a query string into a hash map: /// /// ``` -/// use std::collections::HashMap; +/// use alloc::collections::HashMap; /// use fluent_uri::enc::EStr; /// /// let query = "name=%E5%BC%A0%E4%B8%89&speech=%C2%A1Ol%C3%A9!"; @@ -246,7 +246,7 @@ impl EStr { /// let dec = EStr::new("%C2%BF").decode(); /// assert_eq!(dec.as_bytes(), &[0xc2, 0xbf]); /// assert_eq!(dec.into_string()?, "¿"); - /// # Ok::<_, std::string::FromUtf8Error>(()) + /// # Ok::<_, alloc::string::FromUtf8Error>(()) /// ``` #[inline] pub fn decode(&self) -> Decode<'_> { @@ -278,7 +278,7 @@ impl EStr { /// assert_eq!(dec.to_str()?, "233"); /// assert!(dec.decoded_any()); /// assert_eq!(buf, b"233"); - /// # Ok::<_, std::str::Utf8Error>(()) + /// # Ok::<_, core::str::Utf8Error>(()) /// ``` #[cfg(feature = "unstable")] #[inline] @@ -781,9 +781,6 @@ impl FusedIterator for SplitView<'_> {} #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct BufferTooSmallError(()); -#[cfg(feature = "unstable")] -impl std::error::Error for BufferTooSmallError {} - #[cfg(feature = "unstable")] impl fmt::Display for BufferTooSmallError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { @@ -794,7 +791,8 @@ impl fmt::Display for BufferTooSmallError { #[cfg(feature = "unstable")] pub(crate) mod internal { use crate::enc::BufferTooSmallError; - use std::{collections::TryReserveError, mem::MaybeUninit}; + use alloc::{collections::TryReserveError, string::String, vec::Vec}; + use core::mem::MaybeUninit; pub trait AsMutVec { unsafe fn as_mut_vec(&mut self) -> &mut Vec; From 95540da29f6e3c5c6016f4956c630bb1d941e3ff Mon Sep 17 00:00:00 2001 From: John Elfberg Larsson Date: Fri, 3 Mar 2023 15:33:22 +0100 Subject: [PATCH 04/10] Add "std" flag and re-add std::error impls --- Cargo.toml | 3 ++- src/enc/imp.rs | 3 +++ src/enc/mod.rs | 3 +++ src/lib.rs | 5 +++-- 4 files changed, 11 insertions(+), 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 5c49d6a4..b9879614 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,9 +18,10 @@ bitflags = "1.3.2" ipv_future = [] rfc6874bis = [] unstable = [] +std = [] [package.metadata.docs.rs] -features = ["ipv_future", "rfc6874bis"] +features = ["ipv_future", "rfc6874bis", "std"] # Commented out to reduce compile time. diff --git a/src/enc/imp.rs b/src/enc/imp.rs index 0991eaf1..3196f424 100644 --- a/src/enc/imp.rs +++ b/src/enc/imp.rs @@ -58,6 +58,9 @@ impl EncodingError { } } +#[cfg(feature = "std")] +impl std::error::Error for EncodingError {} + impl fmt::Display for EncodingError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let msg = match self.kind { diff --git a/src/enc/mod.rs b/src/enc/mod.rs index ce76d5c0..3466438b 100644 --- a/src/enc/mod.rs +++ b/src/enc/mod.rs @@ -781,6 +781,9 @@ impl FusedIterator for SplitView<'_> {} #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct BufferTooSmallError(()); +#[cfg(all(feature = "unstable", feature = "std"))] +impl std::error::Error for BufferTooSmallError {} + #[cfg(feature = "unstable")] impl fmt::Display for BufferTooSmallError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { diff --git a/src/lib.rs b/src/lib.rs index 0c59fa55..b544029d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,6 +1,6 @@ #![warn(missing_debug_implementations, missing_docs, rust_2018_idioms)] #![deny(unsafe_op_in_unsafe_fn)] -#![cfg_attr(not(test), no_std)] +#![cfg_attr(all(not(test), not(feature = "std")), no_std)] //! A generic URI parser that strictly adheres to IETF [RFC 3986]. //! @@ -249,7 +249,8 @@ impl ParseError { } } -// impl std::error::Error for ParseError {} +#[cfg(feature = "std")] +impl std::error::Error for ParseError {} type Result = core::result::Result; From 5c89477e104aff351ca92a412cda30b50287623f Mon Sep 17 00:00:00 2001 From: John Elfberg Larsson Date: Fri, 3 Mar 2023 15:34:12 +0100 Subject: [PATCH 05/10] Use std in doc tests --- src/enc/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/enc/mod.rs b/src/enc/mod.rs index 3466438b..e4261a44 100644 --- a/src/enc/mod.rs +++ b/src/enc/mod.rs @@ -110,7 +110,7 @@ use crate::view::View; /// Parse key-value pairs from a query string into a hash map: /// /// ``` -/// use alloc::collections::HashMap; +/// use std::collections::HashMap; /// use fluent_uri::enc::EStr; /// /// let query = "name=%E5%BC%A0%E4%B8%89&speech=%C2%A1Ol%C3%A9!"; @@ -246,7 +246,7 @@ impl EStr { /// let dec = EStr::new("%C2%BF").decode(); /// assert_eq!(dec.as_bytes(), &[0xc2, 0xbf]); /// assert_eq!(dec.into_string()?, "¿"); - /// # Ok::<_, alloc::string::FromUtf8Error>(()) + /// # Ok::<_, std::string::FromUtf8Error>(()) /// ``` #[inline] pub fn decode(&self) -> Decode<'_> { From 8f5a28c0b352531532c6196691aa1b5a0641c877 Mon Sep 17 00:00:00 2001 From: John Elfberg Larsson Date: Fri, 3 Mar 2023 15:34:30 +0100 Subject: [PATCH 06/10] Remove homegrown structs This commit prepares for the eventual release of `core::net` in 1.69 --- src/lib.rs | 160 +--------------------------------------------- src/parser.rs | 10 ++- tests/parse.rs | 2 +- tests/parse_ip.rs | 4 +- 4 files changed, 14 insertions(+), 162 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index b544029d..96164915 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -46,171 +46,17 @@ mod parser; use crate::enc::{EStr, Split}; use alloc::{string::String, vec::Vec}; use core::{ - cmp::Ordering, iter::Iterator, marker::PhantomData, - mem::{transmute, ManuallyDrop}, - // net::{Ipv4Addr, Ipv6Addr}, + mem::ManuallyDrop, + net::{Ipv4Addr, Ipv6Addr}, ptr::NonNull, - slice, - str, + slice, str, }; mod internal; use internal::*; -/// Bare-bones Ipv4Addr for `no_std` environments. -#[derive(Copy, Clone, PartialEq, Eq, Hash)] -pub struct Ipv4Addr { - octets: [u8; 4], -} - -impl Ipv4Addr { - /// Creates a new IPv4 address from four bytes. - #[inline] - pub const fn new(a: u8, b: u8, c: u8, d: u8) -> Ipv4Addr { - Ipv4Addr { - octets: [a, b, c, d], - } - } - - /// Returns the four bytes that make up the IPv4 address. - #[inline] - pub const fn octets(&self) -> [u8; 4] { - self.octets - } -} - -impl PartialOrd for Ipv4Addr { - #[inline] - fn partial_cmp(&self, other: &Ipv4Addr) -> Option { - Some(self.cmp(other)) - } -} - -impl Ord for Ipv4Addr { - #[inline] - fn cmp(&self, other: &Ipv4Addr) -> Ordering { - self.octets().cmp(&other.octets()) - } -} - -impl From for Ipv4Addr { - fn from(ip: u32) -> Self { - Self { - octets: ip.to_be_bytes(), - } - } -} - -impl core::fmt::Debug for Ipv4Addr { - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - core::fmt::Display::fmt(&self, f) - } -} - -impl core::fmt::Display for Ipv4Addr { - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - write!( - f, - "{}.{}.{}.{}", - self.octets[0], self.octets[1], self.octets[2], self.octets[3] - ) - } -} - -/// Bare-bones Ipv6Addr for `no_std` environments. -#[derive(Copy, Clone, PartialEq, Eq, Hash)] -pub struct Ipv6Addr { - octets: [u8; 16], -} - -impl Ipv6Addr { - /// Creates a new IPv6 address from eight 16-bit segments. - #[inline] - pub const fn new(a: u16, b: u16, c: u16, d: u16, e: u16, f: u16, g: u16, h: u16) -> Ipv6Addr { - let addr16 = [ - a.to_be(), - b.to_be(), - c.to_be(), - d.to_be(), - e.to_be(), - f.to_be(), - g.to_be(), - h.to_be(), - ]; - Ipv6Addr { - // All elements in `addr16` are big endian. - // SAFETY: `[u16; 8]` is always safe to transmute to `[u8; 16]`. - octets: unsafe { transmute::<_, [u8; 16]>(addr16) }, - } - } - - /// Returns the eight 16-bit segments that make up the IPv6 address. - #[inline] - pub const fn segments(&self) -> [u16; 8] { - // All elements in `self.octets` must be big endian. - // SAFETY: `[u8; 16]` is always safe to transmute to `[u16; 8]`. - let [a, b, c, d, e, f, g, h] = unsafe { transmute::<_, [u16; 8]>(self.octets) }; - // We want native endian u16 - [ - u16::from_be(a), - u16::from_be(b), - u16::from_be(c), - u16::from_be(d), - u16::from_be(e), - u16::from_be(f), - u16::from_be(g), - u16::from_be(h), - ] - } -} - -impl PartialOrd for Ipv6Addr { - #[inline] - fn partial_cmp(&self, other: &Ipv6Addr) -> Option { - Some(self.cmp(other)) - } -} - -impl Ord for Ipv6Addr { - #[inline] - fn cmp(&self, other: &Ipv6Addr) -> Ordering { - self.segments().cmp(&other.segments()) - } -} -impl core::fmt::Debug for Ipv6Addr { - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - write!(f, "{:?}", self.segments()) - } -} - -impl From<[u16; 8]> for Ipv6Addr { - fn from(segments: [u16; 8]) -> Self { - let segments = segments.map(|s| s.to_be_bytes()); - let octets = [ - segments[0][0], - segments[0][1], - segments[1][0], - segments[1][1], - segments[2][0], - segments[2][1], - segments[3][0], - segments[3][1], - segments[4][0], - segments[4][1], - segments[5][0], - segments[5][1], - segments[6][0], - segments[6][1], - segments[7][0], - segments[7][1], - ]; - - Self { octets } - } -} - /// Detailed cause of a [`ParseError`]. #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum ParseErrorKind { diff --git a/src/parser.rs b/src/parser.rs index cd132d75..8bf05aa5 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -1,9 +1,15 @@ use crate::{ enc::{imp::OCTET_TABLE_LO, table::*}, internal::Pointer, - AuthData, Data, Ipv4Addr, Ipv6Addr, RawHostData as HostData, Result, Tag, Uri, + AuthData, Data, RawHostData as HostData, Result, Tag, Uri, +}; +use core::{ + cell::Cell, + marker::PhantomData, + net::{Ipv4Addr, Ipv6Addr}, + num::NonZeroU32, + str, }; -use core::{cell::Cell, marker::PhantomData, num::NonZeroU32, str}; use super::{internal::Storage, Ipv6Data}; diff --git a/tests/parse.rs b/tests/parse.rs index d82274d3..bc3dc40f 100644 --- a/tests/parse.rs +++ b/tests/parse.rs @@ -1,4 +1,4 @@ -// use std::net::{Ipv4Addr, Ipv6Addr}; +use core::net::{Ipv4Addr, Ipv6Addr}; use fluent_uri::{enc::EStr, ParseErrorKind::*, *}; diff --git a/tests/parse_ip.rs b/tests/parse_ip.rs index 66ff53cb..550d0e65 100644 --- a/tests/parse_ip.rs +++ b/tests/parse_ip.rs @@ -1,6 +1,6 @@ -// use std::net::{Ipv4Addr, Ipv6Addr}; +use core::net::{Ipv4Addr, Ipv6Addr}; -use fluent_uri::{HostData, Ipv4Addr, Ipv6Addr, Uri}; +use fluent_uri::{HostData, Uri}; fn parse_v4(s: &str) -> Option { let s = format!("//{s}"); From f8de953f014393fcaf9dd676cc648836448ed2bc Mon Sep 17 00:00:00 2001 From: yescallop Date: Sat, 4 Mar 2023 12:30:23 +0800 Subject: [PATCH 07/10] Add `std` as default feature and bump MSRV --- Cargo.toml | 3 ++- src/lib.rs | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index b9879614..6baf24a7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,7 +3,7 @@ name = "fluent-uri" version = "0.1.3" authors = ["Scallop Ye "] edition = "2021" -rust-version = "1.63.0" +rust-version = "1.69.0" description = "A generic URI parser that strictly adheres to IETF RFC 3986." documentation = "https://docs.rs/fluent-uri" repository = "https://github.com/yescallop/fluent-uri-rs" @@ -15,6 +15,7 @@ categories = ["encoding", "parser-implementations"] bitflags = "1.3.2" [features] +default = ["std"] ipv_future = [] rfc6874bis = [] unstable = [] diff --git a/src/lib.rs b/src/lib.rs index 96164915..638fb711 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,6 +1,6 @@ #![warn(missing_debug_implementations, missing_docs, rust_2018_idioms)] #![deny(unsafe_op_in_unsafe_fn)] -#![cfg_attr(all(not(test), not(feature = "std")), no_std)] +#![cfg_attr(not(feature = "std"), no_std)] //! A generic URI parser that strictly adheres to IETF [RFC 3986]. //! From d4537bd0dbdae9dfafe3953fed36b454b8bf421d Mon Sep 17 00:00:00 2001 From: John Elfberg Larsson Date: Fri, 10 Mar 2023 12:45:08 +0100 Subject: [PATCH 08/10] Exclude addr fields when `no_std` --- src/internal.rs | 5 +++++ src/lib.rs | 31 +++++++++++++++++-------------- src/parser.rs | 49 +++++++++++++++++++++++++++++-------------------- 3 files changed, 51 insertions(+), 34 deletions(-) diff --git a/src/internal.rs b/src/internal.rs index 6823ae44..c64688ea 100644 --- a/src/internal.rs +++ b/src/internal.rs @@ -8,6 +8,9 @@ use core::{ ops::{Deref, DerefMut}, }; +#[cfg(feature = "std")] +use std::net::{Ipv4Addr, Ipv6Addr}; + use super::*; use bitflags::bitflags; @@ -232,6 +235,7 @@ pub struct AuthData { #[derive(Clone, Copy)] pub union RawHostData { + #[cfg(feature = "std")] pub ipv4_addr: Ipv4Addr, pub ipv6: Ipv6Data, #[cfg(feature = "ipv_future")] @@ -241,6 +245,7 @@ pub union RawHostData { #[derive(Clone, Copy)] pub struct Ipv6Data { + #[cfg(feature = "std")] pub addr: Ipv6Addr, #[cfg(feature = "rfc6874bis")] pub zone_id_start: Option, diff --git a/src/lib.rs b/src/lib.rs index 638fb711..f809a2b2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -45,14 +45,10 @@ mod parser; use crate::enc::{EStr, Split}; use alloc::{string::String, vec::Vec}; -use core::{ - iter::Iterator, - marker::PhantomData, - mem::ManuallyDrop, - net::{Ipv4Addr, Ipv6Addr}, - ptr::NonNull, - slice, str, -}; +use core::{iter::Iterator, marker::PhantomData, mem::ManuallyDrop, ptr::NonNull, slice, str}; + +#[cfg(feature = "std")] +use std::net::{Ipv4Addr, Ipv6Addr}; mod internal; use internal::*; @@ -888,7 +884,7 @@ impl<'i, 'o, T: Io<'i, 'o>> Host { /// Returns the structured host data. #[inline] pub fn data(&'i self) -> HostData<'o> { - let data = self.raw_data(); + let _data = self.raw_data(); let tag = self.auth.uri.tag; // SAFETY: We only access the union after checking the tag. unsafe { @@ -896,11 +892,14 @@ impl<'i, 'o, T: Io<'i, 'o>> Host { // SAFETY: The validation is done. return HostData::RegName(EStr::new_unchecked(self.as_str().as_bytes())); } else if tag.contains(Tag::HOST_IPV4) { - return HostData::Ipv4(data.ipv4_addr); + return HostData::Ipv4( + #[cfg(feature = "std")] + _data.ipv4_addr, + ); } #[cfg(feature = "ipv_future")] if !tag.contains(Tag::HOST_IPV6) { - let dot_i = data.ipv_future_dot_i; + let dot_i = _data.ipv_future_dot_i; let bounds = self.bounds(); // SAFETY: The indexes are within bounds and the validation is done. return HostData::IpvFuture { @@ -909,10 +908,11 @@ impl<'i, 'o, T: Io<'i, 'o>> Host { }; } HostData::Ipv6 { - addr: data.ipv6.addr, + #[cfg(feature = "std")] + addr: _data.ipv6.addr, // SAFETY: The indexes are within bounds and the validation is done. #[cfg(feature = "rfc6874bis")] - zone_id: data + zone_id: _data .ipv6 .zone_id_start .map(|start| self.auth.uri.slice(start.get(), self.bounds().1 - 1)), @@ -925,10 +925,13 @@ impl<'i, 'o, T: Io<'i, 'o>> Host { #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum HostData<'a> { /// An IPv4 address. - Ipv4(Ipv4Addr), + #[cfg_attr(not(feature = "std"), non_exhaustive)] + Ipv4(#[cfg(feature = "std")] Ipv4Addr), /// An IPv6 address. + #[cfg_attr(not(feature = "std"), non_exhaustive)] Ipv6 { /// The address. + #[cfg(feature = "std")] addr: Ipv6Addr, /// An optional zone identifier. /// diff --git a/src/parser.rs b/src/parser.rs index 8bf05aa5..c52bfebf 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -3,13 +3,7 @@ use crate::{ internal::Pointer, AuthData, Data, RawHostData as HostData, Result, Tag, Uri, }; -use core::{ - cell::Cell, - marker::PhantomData, - net::{Ipv4Addr, Ipv6Addr}, - num::NonZeroU32, - str, -}; +use core::{cell::Cell, marker::PhantomData, num::NonZeroU32, str}; use super::{internal::Storage, Ipv6Data}; @@ -293,9 +287,15 @@ impl Parser { let v4 = self.scan_v4(); let (tag, data) = match v4 { - Some(addr) if !self.has_remaining() => { - (Tag::HOST_IPV4, HostData { ipv4_addr: addr }) - } + Some(_addr) if !self.has_remaining() => ( + Tag::HOST_IPV4, + HostData { + #[cfg(feature = "std")] + ipv4_addr: _addr.into(), + #[cfg(not(feature = "std"))] + reg_name: (), + }, + ), _ => (Tag::HOST_REG_NAME, HostData { reg_name: () }), }; @@ -330,11 +330,12 @@ impl Parser { return Ok(None); } - let host = if let Some(addr) = self.scan_v6() { + let host = if let Some(_addr) = self.scan_v6() { self.out.tag = Tag::HOST_IPV6; HostData { ipv6: Ipv6Data { - addr, + #[cfg(feature = "std")] + addr: _addr.into(), #[cfg(feature = "rfc6874bis")] zone_id_start: self.read_zone_id()?, }, @@ -356,7 +357,7 @@ impl Parser { Ok(Some(host)) } - fn scan_v6(&mut self) -> Option { + fn scan_v6(&mut self) -> Option<[u16; 8]> { let mut segs = [0; 8]; let mut ellipsis_i = 8; @@ -383,7 +384,7 @@ impl Parser { // Not enough space, triple colons, or no colon. return None; } - let octets = self.scan_v4()?.octets(); + let octets = self.scan_v4()?.to_be_bytes(); segs[i] = u16::from_be_bytes([octets[0], octets[1]]); segs[i + 1] = u16::from_be_bytes([octets[2], octets[3]]); i += 2; @@ -411,7 +412,7 @@ impl Parser { } } - Some(segs.into()) + Some(segs) } fn scan_v6_segment(&mut self) -> Option { @@ -481,22 +482,30 @@ impl Parser { self.scan(REG_NAME)?; let (tag, data) = match v4 { - Some(addr) if self.pos == v4_end => (Tag::HOST_IPV4, HostData { ipv4_addr: addr }), + Some(_addr) if self.pos == v4_end => ( + Tag::HOST_IPV4, + HostData { + #[cfg(feature = "std")] + ipv4_addr: _addr.into(), + #[cfg(not(feature = "std"))] + reg_name: (), + }, + ), _ => (Tag::HOST_REG_NAME, HostData { reg_name: () }), }; self.out.tag = tag; Ok(data) } - fn scan_v4(&mut self) -> Option { - let mut res = self.scan_v4_octet()? << 24; + fn scan_v4(&mut self) -> Option { + let mut addr = self.scan_v4_octet()? << 24; for i in (0..3).rev() { if !self.read_str(".") { return None; } - res |= self.scan_v4_octet()? << (i * 8); + addr |= self.scan_v4_octet()? << (i * 8); } - Some(Ipv4Addr::from(res)) + Some(addr) } fn scan_v4_octet(&mut self) -> Option { From 3386b4e39096297e5cd18d70fea193f18f285a69 Mon Sep 17 00:00:00 2001 From: John Elfberg Larsson Date: Fri, 10 Mar 2023 12:46:24 +0100 Subject: [PATCH 09/10] Fix `no_std` support --- .github/workflows/ci.yml | 4 ++-- src/lib.rs | 6 ++++-- tests/parse.rs | 2 +- tests/parse_ip.rs | 2 +- 4 files changed, 8 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 46f818a6..ba1623e8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -15,7 +15,7 @@ jobs: profile: minimal toolchain: nightly override: true - - name: Test with no feature + - name: Test with default features uses: actions-rs/cargo@v1 with: command: test @@ -28,7 +28,7 @@ jobs: uses: actions-rs/cargo@v1 with: command: doc - args: --no-deps --features ipv_future,rfc6874bis + args: --no-deps --features ipv_future,rfc6874bis,std - name: Deploy docs uses: peaceiris/actions-gh-pages@v3 with: diff --git a/src/lib.rs b/src/lib.rs index f809a2b2..f36be462 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -10,8 +10,8 @@ //! //! # Feature flags //! -//! All features are disabled by default. However, note that these features each -//! alter the enum [`HostData`] in a backward incompatible way that could make it +//! All features except `std` are disabled `by default. However, note that these features +//! each alter the enum [`HostData`] in a backward incompatible way that could make it //! impossible for two crates that depend on different features of `fluent-uri` to //! be used together. //! @@ -27,6 +27,8 @@ //! //! This feature is based on the homonymous [draft] and is thus subject to change. //! +//! - `std` (default): Enables `std` support. +//! //! [IPvFuture]: https://datatracker.ietf.org/doc/html/rfc3986/#section-3.2.2 //! [`InvalidIpLiteral`]: ParseErrorKind::InvalidIpLiteral //! [draft]: https://datatracker.ietf.org/doc/html/draft-ietf-6man-rfc6874bis-02 diff --git a/tests/parse.rs b/tests/parse.rs index bc3dc40f..05aceb91 100644 --- a/tests/parse.rs +++ b/tests/parse.rs @@ -1,4 +1,4 @@ -use core::net::{Ipv4Addr, Ipv6Addr}; +use std::net::{Ipv4Addr, Ipv6Addr}; use fluent_uri::{enc::EStr, ParseErrorKind::*, *}; diff --git a/tests/parse_ip.rs b/tests/parse_ip.rs index 550d0e65..154f81a3 100644 --- a/tests/parse_ip.rs +++ b/tests/parse_ip.rs @@ -1,4 +1,4 @@ -use core::net::{Ipv4Addr, Ipv6Addr}; +use std::net::{Ipv4Addr, Ipv6Addr}; use fluent_uri::{HostData, Uri}; From 86e15b6db8ca6db2a765e48c7b7554de39400c5b Mon Sep 17 00:00:00 2001 From: yescallop Date: Sat, 11 Mar 2023 19:37:59 +0800 Subject: [PATCH 10/10] Clean up --- Cargo.toml | 2 +- src/internal.rs | 2 +- src/lib.rs | 10 ++++++---- src/parser.rs | 10 +++++----- 4 files changed, 13 insertions(+), 11 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 6baf24a7..4ed08a35 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,7 +3,7 @@ name = "fluent-uri" version = "0.1.3" authors = ["Scallop Ye "] edition = "2021" -rust-version = "1.69.0" +rust-version = "1.63.0" description = "A generic URI parser that strictly adheres to IETF RFC 3986." documentation = "https://docs.rs/fluent-uri" repository = "https://github.com/yescallop/fluent-uri-rs" diff --git a/src/internal.rs b/src/internal.rs index c64688ea..c4e4028d 100644 --- a/src/internal.rs +++ b/src/internal.rs @@ -240,7 +240,7 @@ pub union RawHostData { pub ipv6: Ipv6Data, #[cfg(feature = "ipv_future")] pub ipv_future_dot_i: u32, - pub reg_name: (), + pub none: (), } #[derive(Clone, Copy)] diff --git a/src/lib.rs b/src/lib.rs index f36be462..0c7ac812 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -10,11 +10,14 @@ //! //! # Feature flags //! -//! All features except `std` are disabled `by default. However, note that these features +//! All features except `std` are disabled by default. Note that the last two features //! each alter the enum [`HostData`] in a backward incompatible way that could make it //! impossible for two crates that depend on different features of `fluent-uri` to //! be used together. //! +//! - `std`: Enables `std` support. This includes [`Error`] implementations +//! and `Ip{v4, v6}Addr` support in [`HostData`]. +//! //! - `ipv_future`: Enables the parsing of [IPvFuture] literal addresses, //! which fails with [`InvalidIpLiteral`] when disabled. //! @@ -27,11 +30,10 @@ //! //! This feature is based on the homonymous [draft] and is thus subject to change. //! -//! - `std` (default): Enables `std` support. -//! +//! [`Error`]: std::error::Error //! [IPvFuture]: https://datatracker.ietf.org/doc/html/rfc3986/#section-3.2.2 //! [`InvalidIpLiteral`]: ParseErrorKind::InvalidIpLiteral -//! [draft]: https://datatracker.ietf.org/doc/html/draft-ietf-6man-rfc6874bis-02 +//! [draft]: https://datatracker.ietf.org/doc/html/draft-ietf-6man-rfc6874bis-05 extern crate alloc; diff --git a/src/parser.rs b/src/parser.rs index c52bfebf..14729183 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -238,7 +238,7 @@ impl Parser { } else { // Empty authority. self.out.tag = Tag::HOST_REG_NAME; - host = (self.pos, self.pos, HostData { reg_name: () }); + host = (self.pos, self.pos, HostData { none: () }); } } else { // The whole authority scanned. Try to parse the host and port. @@ -293,10 +293,10 @@ impl Parser { #[cfg(feature = "std")] ipv4_addr: _addr.into(), #[cfg(not(feature = "std"))] - reg_name: (), + none: (), }, ), - _ => (Tag::HOST_REG_NAME, HostData { reg_name: () }), + _ => (Tag::HOST_REG_NAME, HostData { none: () }), }; self.out.tag = tag; @@ -488,10 +488,10 @@ impl Parser { #[cfg(feature = "std")] ipv4_addr: _addr.into(), #[cfg(not(feature = "std"))] - reg_name: (), + none: (), }, ), - _ => (Tag::HOST_REG_NAME, HostData { reg_name: () }), + _ => (Tag::HOST_REG_NAME, HostData { none: () }), }; self.out.tag = tag; Ok(data)