From e99432178f4a53eb6ccbdd994aeb475b6d458146 Mon Sep 17 00:00:00 2001 From: Kotauskas Date: Wed, 10 Jun 2020 13:09:11 +0300 Subject: [PATCH] Moved operator impls into format modules --- src/{extheadbyte.rs => extheadbyte/mod.rs} | 4 +- .../extheadbyte => extheadbyte/ops}/add.rs | 0 .../extheadbyte => extheadbyte/ops}/div.rs | 0 src/extheadbyte/ops/mod.rs | 4 + .../extheadbyte => extheadbyte/ops}/mul.rs | 0 .../extheadbyte => extheadbyte/ops}/sub.rs | 0 src/{headbyte.rs => headbyte/mod.rs} | 2 + src/{ops/headbyte => headbyte/ops}/add.rs | 0 src/{ops/headbyte => headbyte/ops}/div.rs | 0 src/headbyte/ops/mod.rs | 4 + src/{ops/headbyte => headbyte/ops}/mul.rs | 0 src/{ops/headbyte => headbyte/ops}/sub.rs | 0 src/lib.rs | 2 - src/linkedbytes/lbstring/mod.rs | 130 ++++++++++++++ .../lbstring/ops}/cmp.rs | 0 .../lbstring/ops}/from.rs | 0 .../lbstring/ops}/into.rs | 0 .../lbstring/ops}/mod.rs | 0 src/{linkedbytes.rs => linkedbytes/mod.rs} | 167 +++--------------- .../linkedbytes => linkedbytes/ops}/add.rs | 0 .../linkedbytes => linkedbytes/ops}/div.rs | 26 ++- .../linkedbytes => linkedbytes/ops}/fmt.rs | 0 .../linkedbytes => linkedbytes/ops}/from.rs | 0 .../linkedbytes => linkedbytes/ops}/mod.rs | 3 +- .../linkedbytes => linkedbytes/ops}/mul.rs | 0 .../linkedbytes => linkedbytes/ops}/sub.rs | 12 +- .../ops}/tryinto.rs | 0 src/ops/extheadbyte/mod.rs | 0 src/ops/headbyte/mod.rs | 0 src/ops/mod.rs | 5 - 30 files changed, 202 insertions(+), 157 deletions(-) rename src/{extheadbyte.rs => extheadbyte/mod.rs} (81%) rename src/{ops/extheadbyte => extheadbyte/ops}/add.rs (100%) rename src/{ops/extheadbyte => extheadbyte/ops}/div.rs (100%) create mode 100644 src/extheadbyte/ops/mod.rs rename src/{ops/extheadbyte => extheadbyte/ops}/mul.rs (100%) rename src/{ops/extheadbyte => extheadbyte/ops}/sub.rs (100%) rename src/{headbyte.rs => headbyte/mod.rs} (99%) rename src/{ops/headbyte => headbyte/ops}/add.rs (100%) rename src/{ops/headbyte => headbyte/ops}/div.rs (100%) create mode 100644 src/headbyte/ops/mod.rs rename src/{ops/headbyte => headbyte/ops}/mul.rs (100%) rename src/{ops/headbyte => headbyte/ops}/sub.rs (100%) create mode 100644 src/linkedbytes/lbstring/mod.rs rename src/{ops/lbstring => linkedbytes/lbstring/ops}/cmp.rs (100%) rename src/{ops/lbstring => linkedbytes/lbstring/ops}/from.rs (100%) rename src/{ops/lbstring => linkedbytes/lbstring/ops}/into.rs (100%) rename src/{ops/lbstring => linkedbytes/lbstring/ops}/mod.rs (100%) rename src/{linkedbytes.rs => linkedbytes/mod.rs} (75%) rename src/{ops/linkedbytes => linkedbytes/ops}/add.rs (100%) rename src/{ops/linkedbytes => linkedbytes/ops}/div.rs (87%) rename src/{ops/linkedbytes => linkedbytes/ops}/fmt.rs (100%) rename src/{ops/linkedbytes => linkedbytes/ops}/from.rs (100%) rename src/{ops/linkedbytes => linkedbytes/ops}/mod.rs (98%) rename src/{ops/linkedbytes => linkedbytes/ops}/mul.rs (100%) rename src/{ops/linkedbytes => linkedbytes/ops}/sub.rs (95%) rename src/{ops/linkedbytes => linkedbytes/ops}/tryinto.rs (100%) delete mode 100644 src/ops/extheadbyte/mod.rs delete mode 100644 src/ops/headbyte/mod.rs delete mode 100644 src/ops/mod.rs diff --git a/src/extheadbyte.rs b/src/extheadbyte/mod.rs similarity index 81% rename from src/extheadbyte.rs rename to src/extheadbyte/mod.rs index 2363e8a..bca63ee 100644 --- a/src/extheadbyte.rs +++ b/src/extheadbyte/mod.rs @@ -1,3 +1,5 @@ //! The Extended Head Byte format, capable of storing arbitrarily large signed integers and decimal fractions. //! -//! This format is strictly equivalent to Head Byte if there are 15 or less follow-up (1 exponent + 14 coefficient) bytes (the only exception). Otherwise, both the exponent and the **number** of coefficient bytes are stored using the Linked Bytes format. \ No newline at end of file +//! This format is strictly equivalent to Head Byte if there are 15 or less follow-up (1 exponent + 14 coefficient) bytes (the only exception). Otherwise, both the exponent and the **number** of coefficient bytes are stored using the Linked Bytes format. + +mod ops; \ No newline at end of file diff --git a/src/ops/extheadbyte/add.rs b/src/extheadbyte/ops/add.rs similarity index 100% rename from src/ops/extheadbyte/add.rs rename to src/extheadbyte/ops/add.rs diff --git a/src/ops/extheadbyte/div.rs b/src/extheadbyte/ops/div.rs similarity index 100% rename from src/ops/extheadbyte/div.rs rename to src/extheadbyte/ops/div.rs diff --git a/src/extheadbyte/ops/mod.rs b/src/extheadbyte/ops/mod.rs new file mode 100644 index 0000000..3ecc90b --- /dev/null +++ b/src/extheadbyte/ops/mod.rs @@ -0,0 +1,4 @@ +mod add; +mod sub; +mod mul; +mod div; \ No newline at end of file diff --git a/src/ops/extheadbyte/mul.rs b/src/extheadbyte/ops/mul.rs similarity index 100% rename from src/ops/extheadbyte/mul.rs rename to src/extheadbyte/ops/mul.rs diff --git a/src/ops/extheadbyte/sub.rs b/src/extheadbyte/ops/sub.rs similarity index 100% rename from src/ops/extheadbyte/sub.rs rename to src/extheadbyte/ops/sub.rs diff --git a/src/headbyte.rs b/src/headbyte/mod.rs similarity index 99% rename from src/headbyte.rs rename to src/headbyte/mod.rs index d268d4d..a271a80 100644 --- a/src/headbyte.rs +++ b/src/headbyte/mod.rs @@ -2,6 +2,8 @@ //! //! It's recommended to use this format instead of Extended Head Byte if you're accepting numbers from potentially untrusted locations, since Head Byte imposes a size limit (which is still extremely big, suiting most use cases) while Extended Head Byte does not. +mod ops; + use crate::Sign; use alloc::vec::Vec; diff --git a/src/ops/headbyte/add.rs b/src/headbyte/ops/add.rs similarity index 100% rename from src/ops/headbyte/add.rs rename to src/headbyte/ops/add.rs diff --git a/src/ops/headbyte/div.rs b/src/headbyte/ops/div.rs similarity index 100% rename from src/ops/headbyte/div.rs rename to src/headbyte/ops/div.rs diff --git a/src/headbyte/ops/mod.rs b/src/headbyte/ops/mod.rs new file mode 100644 index 0000000..3ecc90b --- /dev/null +++ b/src/headbyte/ops/mod.rs @@ -0,0 +1,4 @@ +mod add; +mod sub; +mod mul; +mod div; \ No newline at end of file diff --git a/src/ops/headbyte/mul.rs b/src/headbyte/ops/mul.rs similarity index 100% rename from src/ops/headbyte/mul.rs rename to src/headbyte/ops/mul.rs diff --git a/src/ops/headbyte/sub.rs b/src/headbyte/ops/sub.rs similarity index 100% rename from src/ops/headbyte/sub.rs rename to src/headbyte/ops/sub.rs diff --git a/src/lib.rs b/src/lib.rs index a0cbc91..ef737da 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -68,8 +68,6 @@ pub use headbyte::{HBNum, HeadByte}; pub mod linkedbytes; pub use linkedbytes::{LBNum, LBNumRef, LBString, LBSequence, LinkedByte}; -// This module is dedicated to the implementations of `Ord`, `Eq` and arithmetic operations on the BigBit formats. Please implement these there and only there. -mod ops; mod traits; pub use traits::*; pub(crate) mod tables; pub(crate) use tables::*; diff --git a/src/linkedbytes/lbstring/mod.rs b/src/linkedbytes/lbstring/mod.rs new file mode 100644 index 0000000..97c8892 --- /dev/null +++ b/src/linkedbytes/lbstring/mod.rs @@ -0,0 +1,130 @@ +//! A Unicode string format implemented using Linked Bytes. +//! +//! This module is the home for [`LBString`][lbs] and [`LBCharsIter`][lbci], which implement the Linked Bytes string storage format, as seen in the official specification. This is, perhaps, the most widely used feature of BigBit, since it's useful even when you don't need the powerful big number storage. The documentation on the `LBString` page elaborates on that. +//! +//! [lbs]: struct.LBString.html "LBString — a Unicode string stored using the Linked Bytes format" +//! [lbci]: struct.LBCharsIter.html "LBCharsIter — an iterator over the codepoints in an LBString" + +use super::{LBNum, LBSequence, LBNumRef}; + +/// A Unicode string stored using the Linked Bytes format. +/// +/// This is more compact than all of the current UTF formats (namely, UTF-1, 7, 8, 16, let alone 32), since no surrogate pairs are used. Instead, the Linked Bytes format is leveraged, with separate codepoints being stored as individual Linked Bytes numbers. Both the link/end bits of the bytes and length of the entire message, either via the null terminator (which still works since a linking 0 has the most significant bit set to 1 and cannot be confused with the null terminator when reinterpreted as `u8`) or via storing it separately (as Rust `String`s do), are available. This means that the UTF-32 number of each codepoint can be encoded using the usual Linked Bytes format, with the link bit cleared in a byte indicating that one character has ended and a new one is coming next. +/// +/// # Usage +/// Conversion from `String` or `&str`: +/// ``` +/// # extern crate alloc; +/// # use alloc::string::String; +/// # use bigbit::LBString; +/// static MY_STRING: &str = "My string!"; +/// let stdstring = String::from("This is a standard string!"); +/// +/// let my_string_lb = LBString::from(MY_STRING); // Creates an LBString from a string slice +/// let stdstring_lb = LBString::from(stdstring); // Creates an LBString from a String +/// let my_string_lb_2 = MY_STRING.chars().collect::(); // Creates an LBString from an iterator +/// +/// # assert_eq!(String::from(my_string_lb), MY_STRING); +/// # assert_eq!(String::from(stdstring_lb), "This is a standard string!"); +/// # assert_eq!(String::from(my_string_lb_2), MY_STRING); +/// ``` +#[derive(Clone, Debug)] +pub struct LBString(LBSequence); +impl LBString { + /// Returns an iterator over the codepoints in the string. + /// + /// This is the core method of this type. Most other methods use this to perform more complex operations, such as conversion from an `&str`. + #[inline(always)] + pub fn chars(&self) -> impl Iterator + '_ { + LBCharsIter::new(self) + } + + /// Counts the number of **codepoints** stored. + /// + /// This will iterate through the entire string and count how many codepoints were resolved successfully. Currently, this is implemented as simply `self.chars().count()`. + #[inline(always)] + pub fn len(&self) -> usize { + self.chars().count() + } + /// Returns `true` if there are no codepoints stored, `false` otherwise. + #[inline(always)] + pub fn is_empty(&self) -> bool { + self.0.is_empty() // We can use the container length, since if it's 0, then it's pointless to try to iterate, otherwise there's guaranteed to be a codepoint. + } + /// Returns an immutable reference to the underlying sequence. + #[inline(always)] + pub fn inner(&self) -> &LBSequence { + &self.0 + } +} +impl core::iter::FromIterator for LBString { + fn from_iter>(iter: I) -> Self { + let mut result = Self(LBSequence::empty()); + let mut lbn = LBNum::ZERO; + for c in iter.into_iter() { + lbn.make_zero(); // This is a specialized method for making the value zero without reallocating, + // which makes it vital for larger strings. + lbn += u32::from(c); + result.0.inner_mut().extend(lbn.iter_le()); + } + result + } +} +impl<'a> core::iter::FromIterator<&'a char> for LBString { + /// Convenience implementation for collections which iterate over references to items rather than the items themselves, to avoid repetitive `.copied()` in calling code. + #[inline(always)] + fn from_iter>(iter: I) -> Self { + iter.into_iter().copied().collect::() + } +} +impl core::fmt::Display for LBString { + fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { + use core::fmt::Write; + for c in self.chars() { + if let Err(e) = f.write_char(c) {return Err(e);} // Stop right where we are if we can't write anything. + } + Ok(()) + } +} +/// An iterator over the codepoints in an `LBString`. +/// +/// This resolves the codepoints on the fly, as all lazy iterators do. Thus creating such an iterator is totally free. +/// +/// The values are **not checked when resolving,** meaning that any invalid Unicode codepoints will be carried over into the result. The reason is that the validity of the values is ensured by the `LBString` type during creation. This means that any unsafe code which incorrectly modifies an `LBString` will most likely trigger a panic or an infinite loop. +pub struct LBCharsIter<'a> { + inner: &'a LBString, + index: usize +} +impl<'a> LBCharsIter<'a> { + pub fn new(s: &'a LBString) -> Self { + Self {inner: s, index: 0} + } +} +impl<'a> Iterator for LBCharsIter<'a> { + type Item = char; + fn next(&mut self) -> Option { // If anything breaks, blame this tymethod (seriously, please do). + use core::{convert::TryInto, hint::unreachable_unchecked}; + let mut chosen_range = self.index..self.index; + loop { + if let Some(v) = self.inner.inner().get(self.index) { + self.index += 1; + chosen_range.end = self.index; + if v.is_end() {break;} + } else { + return None; + } + } + // inner inner inner inner inner inner inner inner inner inner inner inner inner inner inner inner inner inner inner inner inner inner inner inner inner + // inner inner inner inner inner inner inner inner inner inner inner inner inner inner inner inner inner inner inner inner inner inner inner inner inner + // inner inner inner inner inner inner inner inner inner inner inner inner inner inner inner inner inner inner inner inner inner inner inner inner inner + // inner inner inner inner inner inner inner inner inner inner inner inner inner inner inner inner inner inner inner inner inner inner inner inner inner + let refnum = TryInto::::try_into(&self.inner.inner().inner()[chosen_range]) + .unwrap_or_else(|_| unsafe {unreachable_unchecked()}); // Value validity is a safety guarantee for LBString, which is why we can simply + // invoke UB if it fails. Great! + let codepoint = TryInto::::try_into(refnum) + .unwrap_or_else(|_| unsafe {unreachable_unchecked()}); // Same thing here. + let codepoint = TryInto::::try_into(codepoint) + .unwrap_or_else(|_| unsafe {unreachable_unchecked()}); // And here. + Some(codepoint) + } +} \ No newline at end of file diff --git a/src/ops/lbstring/cmp.rs b/src/linkedbytes/lbstring/ops/cmp.rs similarity index 100% rename from src/ops/lbstring/cmp.rs rename to src/linkedbytes/lbstring/ops/cmp.rs diff --git a/src/ops/lbstring/from.rs b/src/linkedbytes/lbstring/ops/from.rs similarity index 100% rename from src/ops/lbstring/from.rs rename to src/linkedbytes/lbstring/ops/from.rs diff --git a/src/ops/lbstring/into.rs b/src/linkedbytes/lbstring/ops/into.rs similarity index 100% rename from src/ops/lbstring/into.rs rename to src/linkedbytes/lbstring/ops/into.rs diff --git a/src/ops/lbstring/mod.rs b/src/linkedbytes/lbstring/ops/mod.rs similarity index 100% rename from src/ops/lbstring/mod.rs rename to src/linkedbytes/lbstring/ops/mod.rs diff --git a/src/linkedbytes.rs b/src/linkedbytes/mod.rs similarity index 75% rename from src/linkedbytes.rs rename to src/linkedbytes/mod.rs index 55ce5c1..864daf2 100644 --- a/src/linkedbytes.rs +++ b/src/linkedbytes/mod.rs @@ -2,8 +2,18 @@ //! //! If you only want non-negative integers, you should stick to this format. (Signed LB integers are also planned.) Otherwise, use either Head Byte or Extended Head Byte. -use core::{slice::SliceIndex, cmp::Ordering}; -use alloc::vec::Vec; +pub mod lbstring; pub use lbstring::LBString; +mod ops; + +use core::{ + slice::SliceIndex, + cmp::Ordering, + iter::FromIterator, +}; +use alloc::{ + vec::Vec, + borrow::{Borrow, BorrowMut}, +}; /// The `Result` specialization for the methods converting iterators/arrays of bytes into instances of `LBNum`. pub type DecodeResult = Result; @@ -52,7 +62,7 @@ impl LBNum { /// Decrements the value, returning `true` if the decrement did anything and `false` if `self` was zero. #[inline(always)] pub fn checked_decrement(&mut self) -> bool { - use crate::ops::linkedbytes::DecrementResult; + use ops::DecrementResult; match self.decrement_at_index(0) { DecrementResult::EndedWithBorrow | DecrementResult::NoSuchIndex => false, DecrementResult::Ok(_) => true @@ -92,6 +102,12 @@ impl LBNum { Self(op) } + /// Creates an `LBNumRef` referring to this number. + #[inline(always)] + pub fn borrow(&self) -> LBNumRef<'_> { + LBNumRef::from(self) + } + /// Ensures that the last element is an endpoint. pub(crate) fn ensure_last_is_end(&mut self) { if let Some(last) = self.0.inner_mut().last_mut() { @@ -184,7 +200,7 @@ impl core::convert::TryFrom for LBNum { } } } -impl core::iter::FromIterator for LBNum { +impl FromIterator for LBNum { /// Converts an iterator over linked bytes into an LBNum. **Little-endian byte order is assumed, regardless of platform.** /// /// If any of the bytes except for the last one are endpoints (most significant bit cleared), they are converted into linked (most significant bit set), and if the last byte is linked, it's converted into and endpoint. @@ -196,28 +212,25 @@ impl core::iter::FromIterator for LBNum { Self(LBSequence::from(resulting_vec)) } } -impl core::convert::AsRef<[LinkedByte]> for LBNum { +impl AsRef<[LinkedByte]> for LBNum { #[inline(always)] fn as_ref(&self) -> &[LinkedByte] { self.0.inner() } } -impl alloc::borrow::Borrow<[LinkedByte]> for LBNum { - #[inline(always)] - fn borrow(&self) -> &[LinkedByte] { - self.0.inner() - } -} -/// A Linked Bytes number behind a reference. +/// A borrowed Linked Bytes number, providing access to the data as a number while not owning it. /// /// The borrow checker ensures that the inner data will **never** be an invalid LB sequence, meaning that after the `TryFrom` check has passed, there's no way that any external code will tamper the borrowed slice. #[derive(Copy, Clone, Debug)] pub struct LBNumRef<'a> (&'a [LinkedByte]); impl<'a> LBNumRef<'a> { /// Constructs an `LBNumRef` referring to the specified Linked Byte slice. + /// + /// # Safety + /// The sequence must be a valid Linked Bytes number. #[inline(always)] - pub const fn new(op: &'a [LinkedByte]) -> Self { + pub const unsafe fn new_unchecked(op: &'a [LinkedByte]) -> Self { Self(op) } @@ -286,128 +299,6 @@ impl<'a> core::ops::Deref for LBNumRef<'a> { #[derive(Copy, Clone, Debug, Default, PartialEq, Eq)] pub struct InvalidLBSequence; -/// A Unicode string stored using the Linked Bytes format. -/// -/// This is more compact than all of the current UTF formats (namely, UTF-1, 7, 8, 16, let alone 32), since no surrogate pairs are used. Instead, the Linked Bytes format is leveraged, with separate codepoints being stored as individual Linked Bytes numbers. Both the link/end bits of the bytes and length of the entire message, either via the null terminator (which still works since a linking 0 has the most significant bit set to 1 and cannot be confused with the null terminator when reinterpreted as `u8`) or via storing it separately (as Rust `String`s do), are available. This means that the UTF-32 number of each codepoint can be encoded using the usual Linked Bytes format, with the link bit cleared in a byte indicating that one character has ended and a new one is coming next. -/// -/// # Usage -/// Conversion from `String` or `&str`: -/// ``` -/// # extern crate alloc; -/// # use alloc::string::String; -/// # use bigbit::LBString; -/// static MY_STRING: &str = "My string!"; -/// let stdstring = String::from("This is a standard string!"); -/// -/// let my_string_lb = LBString::from(MY_STRING); // Creates an LBString from a string slice -/// let stdstring_lb = LBString::from(stdstring); // Creates an LBString from a String -/// let my_string_lb_2 = MY_STRING.chars().collect::(); // Creates an LBString from an iterator -/// -/// # assert_eq!(String::from(my_string_lb), MY_STRING); -/// # assert_eq!(String::from(stdstring_lb), "This is a standard string!"); -/// # assert_eq!(String::from(my_string_lb_2), MY_STRING); -/// ``` -#[derive(Clone, Debug)] -pub struct LBString(LBSequence); -impl LBString { - /// Returns an iterator over the codepoints in the string. - /// - /// This is the core method of this type. Most other methods use this to perform more complex operations, such as conversion from an `&str`. - #[inline(always)] - pub fn chars(&self) -> impl Iterator + '_ { - LBCharsIter::new(self) - } - - /// Counts the number of **codepoints** stored. - /// - /// This will iterate through the entire string and count how many codepoints were resolved successfully. Currently, this is implemented as simply `self.chars().count()`. - #[inline(always)] - pub fn len(&self) -> usize { - self.chars().count() - } - /// Returns `true` if there are no codepoints stored, `false` otherwise. - #[inline(always)] - pub fn is_empty(&self) -> bool { - self.0.is_empty() // We can use the container length, since if it's 0, then it's pointless to try to iterate, otherwise there's guaranteed to be a codepoint. - } - /// Returns an immutable reference to the underlying sequence. - #[inline(always)] - pub fn inner(&self) -> &LBSequence { - &self.0 - } -} -impl core::iter::FromIterator for LBString { - fn from_iter>(iter: I) -> Self { - let mut result = Self(LBSequence::empty()); - let mut lbn = LBNum::ZERO; - for c in iter.into_iter() { - lbn.make_zero(); // This is a specialized method for making the value zero without reallocating, - // which makes it vital for larger strings. - lbn += u32::from(c); - result.0.inner_mut().extend(lbn.iter_le()); - } - result - } -} -impl<'a> core::iter::FromIterator<&'a char> for LBString { - /// Convenience implementation for collections which iterate over references to items rather than the items themselves, to avoid repetitive `.copied()` in calling code. - #[inline(always)] - fn from_iter>(iter: I) -> Self { - iter.into_iter().copied().collect::() - } -} -impl core::fmt::Display for LBString { - fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { - use core::fmt::Write; - for c in self.chars() { - if let Err(e) = f.write_char(c) {return Err(e);} // Stop right where we are if we can't write anything. - } - Ok(()) - } -} -/// An iterator over the codepoints in an `LBString`. -/// -/// This resolves the codepoints on the fly, as all lazy iterators do. Thus creating such an iterator is totally free. -/// -/// The values are **not checked when resolving,** meaning that any invalid Unicode codepoints will be carried over into the result. The reason is that the validity of the values is ensured by the `LBString` type during creation. This means that any unsafe code introspecting an `LBString` will most likely trigger a panic or an infinite loop. -pub struct LBCharsIter<'a> { - inner: &'a LBString, - index: usize -} -impl<'a> LBCharsIter<'a> { - pub fn new(s: &'a LBString) -> Self { - Self {inner: s, index: 0} - } -} -impl<'a> Iterator for LBCharsIter<'a> { - type Item = char; - fn next(&mut self) -> Option { // If anything breaks, blame this tymethod (seriously, please do). - use core::{convert::TryInto, hint::unreachable_unchecked}; - let mut chosen_range = self.index..self.index; - loop { - if let Some(v) = self.inner.inner().get(self.index) { - self.index += 1; - chosen_range.end = self.index; - if v.is_end() {break;} - } else { - return None; - } - } - // inner inner inner inner inner inner inner inner inner inner inner inner inner inner inner inner inner inner inner inner inner inner inner inner inner - // inner inner inner inner inner inner inner inner inner inner inner inner inner inner inner inner inner inner inner inner inner inner inner inner inner - // inner inner inner inner inner inner inner inner inner inner inner inner inner inner inner inner inner inner inner inner inner inner inner inner inner - // inner inner inner inner inner inner inner inner inner inner inner inner inner inner inner inner inner inner inner inner inner inner inner inner inner - let refnum = TryInto::::try_into(&self.inner.inner().inner()[chosen_range]) - .unwrap_or_else(|_| unsafe {unreachable_unchecked()}); // Value validity is a safety guarantee for LBString, which is why we can simply - // invoke UB if it fails. Great! - let codepoint = TryInto::::try_into(refnum) - .unwrap_or_else(|_| unsafe {unreachable_unchecked()}); // Same thing here. - let codepoint = TryInto::::try_into(codepoint) - .unwrap_or_else(|_| unsafe {unreachable_unchecked()}); // And here. - Some(codepoint) - } -} - /// An owned unchecked Linked Bytes sequence, used for storing either strings or numbers. #[derive(Clone, Debug)] pub struct LBSequence(pub(crate) Vec); @@ -504,7 +395,7 @@ impl AsRef<[LinkedByte]> for LBSequence { self.0.as_ref() } } -impl alloc::borrow::Borrow<[LinkedByte]> for LBSequence { +impl Borrow<[LinkedByte]> for LBSequence { #[inline(always)] fn borrow(&self) -> &[LinkedByte] { &self.0 @@ -516,13 +407,13 @@ impl AsMut<[LinkedByte]> for LBSequence { self.0.as_mut() } } -impl alloc::borrow::BorrowMut<[LinkedByte]> for LBSequence { +impl BorrowMut<[LinkedByte]> for LBSequence { #[inline(always)] fn borrow_mut(&mut self) -> &mut [LinkedByte] { &mut self.0 } } -impl core::iter::FromIterator for LBSequence { +impl FromIterator for LBSequence { /// Converts an iterator over linked bytes into an `LBSequence`. **Little-endian byte order is assumed, regardless of platform.** #[inline(always)] #[must_use] diff --git a/src/ops/linkedbytes/add.rs b/src/linkedbytes/ops/add.rs similarity index 100% rename from src/ops/linkedbytes/add.rs rename to src/linkedbytes/ops/add.rs diff --git a/src/ops/linkedbytes/div.rs b/src/linkedbytes/ops/div.rs similarity index 87% rename from src/ops/linkedbytes/div.rs rename to src/linkedbytes/ops/div.rs index 78104d2..95a475f 100644 --- a/src/ops/linkedbytes/div.rs +++ b/src/linkedbytes/ops/div.rs @@ -39,7 +39,7 @@ impl DivRemAssign<&Self> for LBNum { let mut quotient = Self::ZERO; loop { if (self as &Self) < rhs {break;} - unsafe {self.checked_sub_assign(&rhs);} + unsafe {self.checked_sub_assign(rhs.into());} quotient.increment(); } core::mem::replace(self, quotient) // This moves the remainder out of self, moves the quotient and tail-returns it to the callee. @@ -59,6 +59,26 @@ impl DivRemAssign for LBNum { self.div_rem_assign(&rhs) } } +impl DivRemAssign> for LBNum { + type Remainder = Self; + + /// Performs in-place integer division combined with returning the remainder. + /// + /// # Panics + /// Dividing by 0 triggers an immediate panic. + fn div_rem_assign(&mut self, rhs: LBNumRef<'_>) -> Self { + assert!(rhs > 0u8); + let mut quotient = Self::ZERO; + loop { + if *self < rhs {break;} + unsafe {self.checked_sub_assign(rhs);} + quotient.increment(); + } + core::mem::replace(self, quotient) // This moves the remainder out of self, moves the quotient and tail-returns it to the callee. + // While this kind of call might indeed be unintuitive, reading the core::mem::replace docs is + // all you need to do to understand this just fine. + } +} impl ops::Div<&Self> for LBNum { type Output = Self; @@ -175,7 +195,7 @@ macro_rules! impl_div_by_primitive { let mut quotient = Self::ZERO; loop { if (self as &Self) < &rhs {break;} - unsafe {self.checked_sub_assign(&LBNum::from(rhs));} + unsafe {self.checked_sub_assign(LBNum::from(rhs).borrow());} quotient.increment(); } core::mem::replace(self, quotient) @@ -199,7 +219,7 @@ macro_rules! impl_div_by_primitive { let mut result = Self::ZERO; loop { if (self as &Self) < &rhs {break;} - unsafe {self.checked_sub_assign(&LBNum::from(rhs));} + unsafe {self.checked_sub_assign(LBNum::from(rhs).borrow());} result.increment(); } *self = result; diff --git a/src/ops/linkedbytes/fmt.rs b/src/linkedbytes/ops/fmt.rs similarity index 100% rename from src/ops/linkedbytes/fmt.rs rename to src/linkedbytes/ops/fmt.rs diff --git a/src/ops/linkedbytes/from.rs b/src/linkedbytes/ops/from.rs similarity index 100% rename from src/ops/linkedbytes/from.rs rename to src/linkedbytes/ops/from.rs diff --git a/src/ops/linkedbytes/mod.rs b/src/linkedbytes/ops/mod.rs similarity index 98% rename from src/ops/linkedbytes/mod.rs rename to src/linkedbytes/ops/mod.rs index 9fa8b4c..e1f7844 100644 --- a/src/ops/linkedbytes/mod.rs +++ b/src/linkedbytes/ops/mod.rs @@ -2,8 +2,7 @@ use crate::{LBNum, LBNumRef}; use core::cmp::{self, Ordering}; mod add; mod sub; mod mul; mod div; mod from; mod tryinto; mod fmt; -#[allow(unused_imports)] -pub(crate) use {add::*, sub::*, mul::*, div::*, from::*, tryinto::*, fmt::*}; +pub(crate) use sub::DecrementResult; impl cmp::PartialEq for LBNum { #[inline(always)] diff --git a/src/ops/linkedbytes/mul.rs b/src/linkedbytes/ops/mul.rs similarity index 100% rename from src/ops/linkedbytes/mul.rs rename to src/linkedbytes/ops/mul.rs diff --git a/src/ops/linkedbytes/sub.rs b/src/linkedbytes/ops/sub.rs similarity index 95% rename from src/ops/linkedbytes/sub.rs rename to src/linkedbytes/ops/sub.rs index a9f5104..c5dc14b 100644 --- a/src/ops/linkedbytes/sub.rs +++ b/src/linkedbytes/ops/sub.rs @@ -1,4 +1,4 @@ -use crate::{LBNum, LinkedByte}; +use crate::{LBNum, LBNumRef, LinkedByte}; use core::{ops, convert::TryInto}; impl LBNum { @@ -21,20 +21,20 @@ impl LBNum { /// Performs checked subtraction. Returns `None` if the result underflowed 0, or the result wrapped in `Some` otherwise. #[inline(always)] pub fn checked_sub(mut self, rhs: &Self) -> Option { - unsafe{ if !self.checked_sub_assign(rhs) {Some(self)} else {None} } + unsafe{ if !self.checked_sub_assign(rhs.into()) {Some(self)} else {None} } } /// Performs checked subtraction, returning `true` if overflow occurred. /// /// # Safety /// If `true` is returned, the value of `self` is undefined. - pub(crate) unsafe fn checked_sub_assign(&mut self, rhs: &Self) -> bool { - if rhs.0.inner().is_empty() {return true;} + pub(crate) unsafe fn checked_sub_assign(&mut self, rhs: LBNumRef<'_>) -> bool { + if rhs.is_empty() {return true;} if self.0.len() < rhs.0.len() { self.convert_last_to_linked(); self.0.inner_mut().resize(rhs.0.len(), LinkedByte::ZERO_LINK); self.ensure_last_is_end(); } - for (i, other) in (0..self.0.len()).zip(rhs.0.iter_le()) { + for (i, other) in (0..self.0.len()).zip(rhs.iter_le()) { let this = &mut self.0.inner_mut()[i]; let (val, wrapped) = this.sub_with_borrow(other); *this = val; @@ -105,7 +105,7 @@ impl ops::SubAssign<&Self> for LBNum { #[inline(always)] fn sub_assign(&mut self, rhs: &Self) { unsafe { - if !self.checked_sub_assign(rhs) { + if !self.checked_sub_assign(rhs.into()) { panic!("BigBit integer underflow"); } } diff --git a/src/ops/linkedbytes/tryinto.rs b/src/linkedbytes/ops/tryinto.rs similarity index 100% rename from src/ops/linkedbytes/tryinto.rs rename to src/linkedbytes/ops/tryinto.rs diff --git a/src/ops/extheadbyte/mod.rs b/src/ops/extheadbyte/mod.rs deleted file mode 100644 index e69de29..0000000 diff --git a/src/ops/headbyte/mod.rs b/src/ops/headbyte/mod.rs deleted file mode 100644 index e69de29..0000000 diff --git a/src/ops/mod.rs b/src/ops/mod.rs deleted file mode 100644 index 3978c77..0000000 --- a/src/ops/mod.rs +++ /dev/null @@ -1,5 +0,0 @@ -// Uncomment when implementing operations for these -//pub(crate) mod headbyte; -//pub(crate) mod extheadbyte; -pub(crate) mod linkedbytes; -pub(crate) mod lbstring; \ No newline at end of file