use std::fmt;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ExpectedError {
BadExpectedAccess,
}
impl fmt::Display for ExpectedError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Self::BadExpectedAccess => write!(f, "bad expected access"),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Unexpected<E> {
error: E,
}
impl<E> Unexpected<E> {
pub fn new(error: E) -> Self {
Self { error }
}
pub fn error(&self) -> &E {
&self.error
}
pub fn into_error(self) -> E {
self.error
}
}
impl<E: fmt::Display> fmt::Display for Unexpected<E> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "unexpected({})", self.error)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum StdExpected<T, E> {
Value(T),
Error(E),
}
impl<T, E> StdExpected<T, E> {
pub fn value(val: T) -> Self {
StdExpected::Value(val)
}
pub fn error(err: E) -> Self {
StdExpected::Error(err)
}
pub fn has_value(&self) -> bool {
matches!(self, StdExpected::Value(_))
}
pub fn has_error(&self) -> bool {
matches!(self, StdExpected::Error(_))
}
pub fn value_ref(&self) -> &T {
match self {
StdExpected::Value(ref v) => v,
StdExpected::Error(_) => panic!("bad expected access: no value"),
}
}
pub fn value_mut(&mut self) -> &mut T {
match self {
StdExpected::Value(ref mut v) => v,
StdExpected::Error(_) => panic!("bad expected access: no value"),
}
}
pub fn error_ref(&self) -> &E {
match self {
StdExpected::Value(_) => panic!("bad expected access: no error"),
StdExpected::Error(ref e) => e,
}
}
pub fn into_value(self) -> T {
match self {
StdExpected::Value(v) => v,
StdExpected::Error(_) => panic!("bad expected access: no value"),
}
}
pub fn into_error(self) -> E {
match self {
StdExpected::Value(_) => panic!("bad expected access: no error"),
StdExpected::Error(e) => e,
}
}
pub fn value_or(self, default: T) -> T {
match self {
StdExpected::Value(v) => v,
StdExpected::Error(_) => default,
}
}
pub fn value_or_else<F: FnOnce(E) -> T>(self, f: F) -> T {
match self {
StdExpected::Value(v) => v,
StdExpected::Error(e) => f(e),
}
}
pub fn and_then<U, F: FnOnce(T) -> StdExpected<U, E>>(self, f: F) -> StdExpected<U, E> {
match self {
StdExpected::Value(v) => f(v),
StdExpected::Error(e) => StdExpected::Error(e),
}
}
pub fn or_else<F: FnOnce(E) -> StdExpected<T, E>>(self, f: F) -> StdExpected<T, E> {
match self {
StdExpected::Value(v) => StdExpected::Value(v),
StdExpected::Error(e) => f(e),
}
}
pub fn transform<U, F: FnOnce(T) -> U>(self, f: F) -> StdExpected<U, E> {
match self {
StdExpected::Value(v) => StdExpected::Value(f(v)),
StdExpected::Error(e) => StdExpected::Error(e),
}
}
pub fn transform_error<F: FnOnce(E) -> E>(self, f: F) -> StdExpected<T, E> {
match self {
StdExpected::Value(v) => StdExpected::Value(v),
StdExpected::Error(e) => StdExpected::Error(f(e)),
}
}
pub fn map<U, F: FnOnce(T) -> U, G: FnOnce(E) -> E>(
self,
val_fn: F,
err_fn: G,
) -> StdExpected<U, E> {
match self {
StdExpected::Value(v) => StdExpected::Value(val_fn(v)),
StdExpected::Error(e) => StdExpected::Error(err_fn(e)),
}
}
pub fn ok(self) -> Option<T> {
match self {
StdExpected::Value(v) => Some(v),
StdExpected::Error(_) => None,
}
}
pub fn err(self) -> Option<E> {
match self {
StdExpected::Value(_) => None,
StdExpected::Error(e) => Some(e),
}
}
pub fn swap_error_value(self) -> StdExpected<E, T> {
match self {
StdExpected::Value(v) => StdExpected::Error(v),
StdExpected::Error(e) => StdExpected::Value(e),
}
}
}
impl<T: fmt::Display, E: fmt::Display> fmt::Display for StdExpected<T, E> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
StdExpected::Value(v) => write!(f, "expected(value: {})", v),
StdExpected::Error(e) => write!(f, "expected(error: {})", e),
}
}
}
impl<T: Default, E> Default for StdExpected<T, E> {
fn default() -> Self {
StdExpected::Value(T::default())
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct SourceLocation {
pub file_name: String,
pub function_name: String,
pub line: u32,
pub column: u32,
}
impl SourceLocation {
pub fn new(file_name: &str, function_name: &str, line: u32, column: u32) -> Self {
Self {
file_name: file_name.to_string(),
function_name: function_name.to_string(),
line,
column,
}
}
pub fn current(file: &str, function: &str, line: u32, column: u32) -> Self {
Self {
file_name: file.to_string(),
function_name: function.to_string(),
line,
column,
}
}
pub fn file_name(&self) -> &str {
&self.file_name
}
pub fn function_name(&self) -> &str {
&self.function_name
}
pub fn line(&self) -> u32 {
self.line
}
pub fn column(&self) -> u32 {
self.column
}
pub fn to_position_string(&self) -> String {
format!("{}:{}:{}", self.file_name, self.line, self.column)
}
pub fn to_full_string(&self) -> String {
format!(
"{} at {}:{}:{}",
self.function_name, self.file_name, self.line, self.column
)
}
}
impl fmt::Display for SourceLocation {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.to_full_string())
}
}
impl Default for SourceLocation {
fn default() -> Self {
Self {
file_name: "<unknown>".to_string(),
function_name: "<unknown>".to_string(),
line: 0,
column: 0,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Endian {
Little,
Big,
Native,
}
pub fn bit_cast<T: Copy, U: Copy>(_from: T, _to: U) -> U {
panic!("bit_cast requires same-size types and trivially copyable constraints")
}
pub fn has_single_bit(x: u64) -> bool {
x != 0 && (x & (x - 1)) == 0
}
pub fn has_single_bit_u32(x: u32) -> bool {
x != 0 && (x & (x - 1)) == 0
}
pub fn bit_ceil(x: u64) -> u64 {
if x == 0 {
return 1;
}
if has_single_bit(x) {
return x;
}
1u64 << (64 - (x - 1).leading_zeros())
}
pub fn bit_ceil_u32(x: u32) -> u32 {
if x == 0 {
return 1;
}
if has_single_bit_u32(x) {
return x;
}
1u32 << (32 - (x - 1).leading_zeros())
}
pub fn bit_floor(x: u64) -> u64 {
if x == 0 {
return 0;
}
1u64 << (63 - x.leading_zeros())
}
pub fn bit_floor_u32(x: u32) -> u32 {
if x == 0 {
return 0;
}
1u32 << (31 - x.leading_zeros())
}
pub fn bit_width(x: u64) -> u32 {
if x == 0 {
return 0;
}
64 - x.leading_zeros()
}
pub fn bit_width_u32(x: u32) -> u32 {
if x == 0 {
return 0;
}
32 - x.leading_zeros()
}
pub fn rotl_u8(x: u8, s: u32) -> u8 {
let s = s % 8;
(x << s) | (x >> (8 - s))
}
pub fn rotl_u16(x: u16, s: u32) -> u16 {
let s = s % 16;
(x << s) | (x >> (16 - s))
}
pub fn rotl_u32(x: u32, s: u32) -> u32 {
let s = s % 32;
(x << s) | (x >> (32 - s))
}
pub fn rotl_u64(x: u64, s: u32) -> u64 {
let s = s % 64;
(x << s) | (x >> (64 - s))
}
pub fn rotr_u8(x: u8, s: u32) -> u8 {
let s = s % 8;
(x >> s) | (x << (8 - s))
}
pub fn rotr_u16(x: u16, s: u32) -> u16 {
let s = s % 16;
(x >> s) | (x << (16 - s))
}
pub fn rotr_u32(x: u32, s: u32) -> u32 {
let s = s % 32;
(x >> s) | (x << (32 - s))
}
pub fn rotr_u64(x: u64, s: u32) -> u64 {
let s = s % 64;
(x >> s) | (x << (64 - s))
}
pub fn countl_zero_u8(x: u8) -> u32 {
x.leading_zeros()
}
pub fn countl_zero_u16(x: u16) -> u32 {
x.leading_zeros()
}
pub fn countl_zero_u32(x: u32) -> u32 {
x.leading_zeros()
}
pub fn countl_zero_u64(x: u64) -> u32 {
x.leading_zeros()
}
pub fn countr_zero_u8(x: u8) -> u32 {
x.trailing_zeros()
}
pub fn countr_zero_u16(x: u16) -> u32 {
x.trailing_zeros()
}
pub fn countr_zero_u32(x: u32) -> u32 {
x.trailing_zeros()
}
pub fn countr_zero_u64(x: u64) -> u32 {
x.trailing_zeros()
}
pub fn countl_one_u8(x: u8) -> u32 {
(!x).leading_zeros()
}
pub fn countl_one_u16(x: u16) -> u32 {
(!x).leading_zeros()
}
pub fn countl_one_u32(x: u32) -> u32 {
(!x).leading_zeros()
}
pub fn countl_one_u64(x: u64) -> u32 {
(!x).leading_zeros()
}
pub fn countr_one_u8(x: u8) -> u32 {
(!x).trailing_zeros()
}
pub fn countr_one_u16(x: u16) -> u32 {
(!x).trailing_zeros()
}
pub fn countr_one_u32(x: u32) -> u32 {
(!x).trailing_zeros()
}
pub fn countr_one_u64(x: u64) -> u32 {
(!x).trailing_zeros()
}
pub fn popcount_u8(x: u8) -> u32 {
x.count_ones()
}
pub fn popcount_u16(x: u16) -> u32 {
x.count_ones()
}
pub fn popcount_u32(x: u32) -> u32 {
x.count_ones()
}
pub fn popcount_u64(x: u64) -> u32 {
x.count_ones()
}
pub fn bit_cast_u32_to_i32(x: u32) -> i32 {
x as i32
}
pub fn bit_cast_i32_to_u32(x: i32) -> u32 {
x as u32
}
pub fn bit_cast_f32_to_u32(x: f32) -> u32 {
x.to_bits()
}
pub fn bit_cast_u32_to_f32(x: u32) -> f32 {
f32::from_bits(x)
}
pub fn bit_cast_f64_to_u64(x: f64) -> u64 {
x.to_bits()
}
pub fn bit_cast_u64_to_f64(x: u64) -> f64 {
f64::from_bits(x)
}
pub const fn native_endian() -> Endian {
#[cfg(target_endian = "little")]
{
Endian::Little
}
#[cfg(target_endian = "big")]
{
Endian::Big
}
}
pub fn ntohs(x: u16) -> u16 {
u16::from_be(x)
}
pub fn ntohl(x: u32) -> u32 {
u32::from_be(x)
}
pub fn htons(x: u16) -> u16 {
x.to_be()
}
pub fn htonl(x: u32) -> u32 {
x.to_be()
}
pub mod numbers_f32 {
pub const PI: f32 = std::f32::consts::PI;
pub const E: f32 = std::f32::consts::E;
pub const LOG2E: f32 = std::f32::consts::LOG2_E;
pub const LOG10E: f32 = std::f32::consts::LOG10_E;
pub const LN2: f32 = std::f32::consts::LN_2;
pub const LN10: f32 = std::f32::consts::LN_10;
pub const SQRT2: f32 = std::f32::consts::SQRT_2;
pub const SQRT3: f32 = 1.7320508075688772_f32;
pub const INV_SQRT3: f32 = 0.5773502691896258_f32;
pub const EGAMMA: f32 = 0.5772156649015329_f32;
pub const PHI: f32 = 1.618033988749895_f32;
}
pub mod numbers_f64 {
pub const PI: f64 = std::f64::consts::PI;
pub const E: f64 = std::f64::consts::E;
pub const LOG2E: f64 = std::f64::consts::LOG2_E;
pub const LOG10E: f64 = std::f64::consts::LOG10_E;
pub const LN2: f64 = std::f64::consts::LN_2;
pub const LN10: f64 = std::f64::consts::LN_10;
pub const SQRT2: f64 = std::f64::consts::SQRT_2;
pub const SQRT3: f64 = 1.7320508075688772_f64;
pub const INV_SQRT3: f64 = 0.5773502691896258_f64;
pub const EGAMMA: f64 = 0.577215664901532860606512090082402431_f64;
pub const PHI: f64 = 1.618033988749894848204586834365638118_f64;
}
pub mod numbers {
pub use super::numbers_f64::*;
}
#[derive(Debug, Clone, Copy)]
pub struct MathConstants<T> {
pub pi: T,
pub e: T,
pub log2e: T,
pub log10e: T,
pub ln2: T,
pub ln10: T,
pub sqrt2: T,
pub sqrt3: T,
pub inv_sqrt3: T,
pub egamma: T,
pub phi: T,
}
impl MathConstants<f64> {
pub const fn new() -> Self {
Self {
pi: numbers_f64::PI,
e: numbers_f64::E,
log2e: numbers_f64::LOG2E,
log10e: numbers_f64::LOG10E,
ln2: numbers_f64::LN2,
ln10: numbers_f64::LN10,
sqrt2: numbers_f64::SQRT2,
sqrt3: numbers_f64::SQRT3,
inv_sqrt3: numbers_f64::INV_SQRT3,
egamma: numbers_f64::EGAMMA,
phi: numbers_f64::PHI,
}
}
}
impl MathConstants<f32> {
pub const fn new() -> Self {
Self {
pi: numbers_f32::PI,
e: numbers_f32::E,
log2e: numbers_f32::LOG2E,
log10e: numbers_f32::LOG10E,
ln2: numbers_f32::LN2,
ln10: numbers_f32::LN10,
sqrt2: numbers_f32::SQRT2,
sqrt3: numbers_f32::SQRT3,
inv_sqrt3: numbers_f32::INV_SQRT3,
egamma: numbers_f32::EGAMMA,
phi: numbers_f32::PHI,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum FeatureTestMacro {
Byte,
Optional,
Variant,
Any,
StringView,
FileSystem,
SharedMutex,
ParallelAlgorithm,
ScopedLock,
Invoke,
Concepts,
Ranges,
Span,
Coroutine,
ThreeWayComparison,
Format,
SourceLocation,
Jthread,
Barrier,
Latch,
Semaphore,
AtomicWait,
BitCast,
MathConstants,
Endian,
BitOps,
Char8T,
EraseIf,
ToArray,
BindFront,
Ssize,
Expected,
FlatMap,
Print,
Mdspan,
Stacktrace,
MonadicOptional,
MoveOnlyFunction,
OutPtr,
ToUnderlying,
Unreachable,
}
impl FeatureTestMacro {
pub fn macro_name(&self) -> &'static str {
match self {
Self::Byte => "__cpp_lib_byte",
Self::Optional => "__cpp_lib_optional",
Self::Variant => "__cpp_lib_variant",
Self::Any => "__cpp_lib_any",
Self::StringView => "__cpp_lib_string_view",
Self::FileSystem => "__cpp_lib_filesystem",
Self::SharedMutex => "__cpp_lib_shared_mutex",
Self::ParallelAlgorithm => "__cpp_lib_parallel_algorithm",
Self::ScopedLock => "__cpp_lib_scoped_lock",
Self::Invoke => "__cpp_lib_invoke",
Self::Concepts => "__cpp_lib_concepts",
Self::Ranges => "__cpp_lib_ranges",
Self::Span => "__cpp_lib_span",
Self::Coroutine => "__cpp_lib_coroutine",
Self::ThreeWayComparison => "__cpp_lib_three_way_comparison",
Self::Format => "__cpp_lib_format",
Self::SourceLocation => "__cpp_lib_source_location",
Self::Jthread => "__cpp_lib_jthread",
Self::Barrier => "__cpp_lib_barrier",
Self::Latch => "__cpp_lib_latch",
Self::Semaphore => "__cpp_lib_semaphore",
Self::AtomicWait => "__cpp_lib_atomic_wait",
Self::BitCast => "__cpp_lib_bit_cast",
Self::MathConstants => "__cpp_lib_math_constants",
Self::Endian => "__cpp_lib_endian",
Self::BitOps => "__cpp_lib_bitops",
Self::Char8T => "__cpp_lib_char8_t",
Self::EraseIf => "__cpp_lib_erase_if",
Self::ToArray => "__cpp_lib_to_array",
Self::BindFront => "__cpp_lib_bind_front",
Self::Ssize => "__cpp_lib_ssize",
Self::Expected => "__cpp_lib_expected",
Self::FlatMap => "__cpp_lib_flat_map",
Self::Print => "__cpp_lib_print",
Self::Mdspan => "__cpp_lib_mdspan",
Self::Stacktrace => "__cpp_lib_stacktrace",
Self::MonadicOptional => "__cpp_lib_monadic_optional",
Self::MoveOnlyFunction => "__cpp_lib_move_only_function",
Self::OutPtr => "__cpp_lib_out_ptr",
Self::ToUnderlying => "__cpp_lib_to_underlying",
Self::Unreachable => "__cpp_lib_unreachable",
}
}
pub fn introduced_in(&self) -> &'static str {
match self {
Self::Byte => "201603",
Self::Optional => "201603",
Self::Variant => "201603",
Self::Any => "201606",
Self::StringView => "201606",
Self::FileSystem => "201703",
Self::SharedMutex => "201505",
Self::ParallelAlgorithm => "201603",
Self::ScopedLock => "201703",
Self::Invoke => "201703",
Self::Concepts => "202002",
Self::Ranges => "201911",
Self::Span => "202002",
Self::Coroutine => "201902",
Self::ThreeWayComparison => "201907",
Self::Format => "201907",
Self::SourceLocation => "201907",
Self::Jthread => "201911",
Self::Barrier => "201907",
Self::Latch => "201907",
Self::Semaphore => "201907",
Self::AtomicWait => "201907",
Self::BitCast => "201806",
Self::MathConstants => "201907",
Self::Endian => "201907",
Self::BitOps => "201907",
Self::Char8T => "201811",
Self::EraseIf => "201811",
Self::ToArray => "201811",
Self::BindFront => "201907",
Self::Ssize => "201902",
Self::Expected => "202211",
Self::FlatMap => "202207",
Self::Print => "202207",
Self::Mdspan => "202207",
Self::Stacktrace => "202011",
Self::MonadicOptional => "202110",
Self::MoveOnlyFunction => "202110",
Self::OutPtr => "202106",
Self::ToUnderlying => "202102",
Self::Unreachable => "202202",
}
}
pub fn value_for_standard(&self, standard_year: u32) -> Option<u64> {
let introduced = self.introduced_in().parse::<u32>().ok()?;
if standard_year >= introduced {
Some(introduced as u64)
} else {
None
}
}
}
pub struct FeatureTestDatabase {
macros: Vec<(FeatureTestMacro, bool)>,
}
impl FeatureTestDatabase {
pub fn new() -> Self {
Self { macros: Vec::new() }
}
pub fn enable(&mut self, ft: FeatureTestMacro) {
self.macros.push((ft, true));
}
pub fn is_available(&self, ft: FeatureTestMacro, standard_year: u32) -> bool {
match ft.value_for_standard(standard_year) {
Some(_) => {
if let Some((_, enabled)) = self.macros.iter().find(|(m, _)| *m == ft) {
*enabled
} else {
true }
}
None => false,
}
}
pub fn generate_defines(&self, standard_year: u32) -> Vec<String> {
let all_macros = vec![
FeatureTestMacro::Byte,
FeatureTestMacro::Optional,
FeatureTestMacro::Variant,
FeatureTestMacro::Any,
FeatureTestMacro::StringView,
FeatureTestMacro::FileSystem,
FeatureTestMacro::SharedMutex,
FeatureTestMacro::Concepts,
FeatureTestMacro::Ranges,
FeatureTestMacro::Span,
FeatureTestMacro::Coroutine,
FeatureTestMacro::ThreeWayComparison,
FeatureTestMacro::Format,
FeatureTestMacro::SourceLocation,
FeatureTestMacro::Expected,
FeatureTestMacro::FlatMap,
FeatureTestMacro::Barrier,
FeatureTestMacro::Latch,
FeatureTestMacro::Semaphore,
FeatureTestMacro::BitCast,
FeatureTestMacro::MathConstants,
FeatureTestMacro::Endian,
FeatureTestMacro::BitOps,
];
let mut defines = Vec::new();
for ft in all_macros {
if self.is_available(ft, standard_year) {
if let Some(val) = ft.value_for_standard(standard_year) {
defines.push(format!("#define {} {}", ft.macro_name(), val));
}
}
}
defines
}
}
impl Default for FeatureTestDatabase {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ConceptResult {
Satisfied,
NotSatisfied(String),
}
pub trait ConceptPredicate {
fn name(&self) -> &'static str;
fn evaluate_type(&self, type_name: &str) -> ConceptResult;
}
pub struct SameAs {
pub t: String,
pub u: String,
}
impl SameAs {
pub fn new(t: &str, u: &str) -> Self {
Self {
t: t.to_string(),
u: u.to_string(),
}
}
pub fn is_satisfied(&self) -> bool {
self.t == self.u
}
}
impl ConceptPredicate for SameAs {
fn name(&self) -> &'static str {
"same_as"
}
fn evaluate_type(&self, _type_name: &str) -> ConceptResult {
if self.is_satisfied() {
ConceptResult::Satisfied
} else {
ConceptResult::NotSatisfied(format!(
"same_as<{}, {}>: types differ",
self.t, self.u
))
}
}
}
pub struct DerivedFrom {
pub derived: String,
pub base: String,
}
impl DerivedFrom {
pub fn new(derived: &str, base: &str) -> Self {
Self {
derived: derived.to_string(),
base: base.to_string(),
}
}
pub fn is_satisfied(&self, derived_from: &[(String, Vec<String>)]) -> bool {
derived_from
.iter()
.any(|(cls, bases)| cls == &self.derived && bases.contains(&self.base))
}
}
pub struct ConvertibleTo {
pub from: String,
pub to: String,
}
impl ConvertibleTo {
pub fn new(from: &str, to: &str) -> Self {
Self {
from: from.to_string(),
to: to.to_string(),
}
}
pub fn is_satisfied(
&self,
conversions: &[(String, Vec<String>)],
) -> bool {
conversions
.iter()
.any(|(t, convs)| t == &self.from && convs.contains(&self.to))
}
}
pub struct CommonWith {
pub t: String,
pub u: String,
}
impl CommonWith {
pub fn new(t: &str, u: &str) -> Self {
Self {
t: t.to_string(),
u: u.to_string(),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum TypeCategory {
Integral,
FloatingPoint,
Pointer,
Class,
Enum,
Union,
Array,
Function,
Void,
Nullptr,
}
pub struct ConceptEngine {
type_categories: Vec<(String, TypeCategory)>,
base_classes: Vec<(String, Vec<String>)>,
implicit_conversions: Vec<(String, Vec<String>)>,
}
impl ConceptEngine {
pub fn new() -> Self {
Self {
type_categories: Vec::new(),
base_classes: Vec::new(),
implicit_conversions: Vec::new(),
}
}
pub fn register_type(&mut self, name: &str, category: TypeCategory) {
self.type_categories
.push((name.to_string(), category));
}
pub fn register_base(&mut self, derived: &str, base: &str) {
if let Some((_, bases)) = self
.base_classes
.iter_mut()
.find(|(d, _)| d == derived)
{
bases.push(base.to_string());
} else {
self.base_classes
.push((derived.to_string(), vec![base.to_string()]));
}
}
pub fn register_conversion(&mut self, from: &str, to: &str) {
if let Some((_, convs)) = self
.implicit_conversions
.iter_mut()
.find(|(f, _)| f == from)
{
convs.push(to.to_string());
} else {
self.implicit_conversions
.push((from.to_string(), vec![to.to_string()]));
}
}
pub fn same_as(&self, t: &str, u: &str) -> ConceptResult {
if t == u {
ConceptResult::Satisfied
} else {
ConceptResult::NotSatisfied(format!("same_as<{}, {}>: types differ", t, u))
}
}
pub fn derived_from(&self, derived: &str, base: &str) -> ConceptResult {
if derived == base {
return ConceptResult::Satisfied;
}
if let Some((_, bases)) = self.base_classes.iter().find(|(d, _)| d == derived) {
if bases.iter().any(|b| b == base) {
return ConceptResult::Satisfied;
}
}
ConceptResult::NotSatisfied(format!(
"derived_from<{}, {}>: not a base class",
derived, base
))
}
pub fn integral(&self, t: &str) -> ConceptResult {
if let Some((_, cat)) = self.type_categories.iter().find(|(n, _)| n == t) {
if *cat == TypeCategory::Integral {
return ConceptResult::Satisfied;
}
}
ConceptResult::NotSatisfied(format!("integral<{}>: not an integral type", t))
}
pub fn floating_point(&self, t: &str) -> ConceptResult {
if let Some((_, cat)) = self.type_categories.iter().find(|(n, _)| n == t) {
if *cat == TypeCategory::FloatingPoint {
return ConceptResult::Satisfied;
}
}
ConceptResult::NotSatisfied(format!("floating_point<{}>: not a floating point type", t))
}
pub fn movable(&self, t: &str) -> ConceptResult {
if let Some((_, cat)) = self.type_categories.iter().find(|(n, _)| n == t) {
match cat {
TypeCategory::Void | TypeCategory::Function | TypeCategory::Nullptr => {
ConceptResult::NotSatisfied(format!("movable<{}>: void/function types not movable", t))
}
_ => ConceptResult::Satisfied,
}
} else {
ConceptResult::Satisfied }
}
pub fn copyable(&self, t: &str) -> ConceptResult {
self.movable(t) }
pub fn semiregular(&self, t: &str) -> ConceptResult {
self.copyable(t) }
pub fn regular(&self, t: &str) -> ConceptResult {
self.semiregular(t) }
pub fn convertible_to(&self, from: &str, to: &str) -> ConceptResult {
if from == to {
return ConceptResult::Satisfied;
}
if let Some((_, convs)) = self
.implicit_conversions
.iter()
.find(|(f, _)| f == from)
{
if convs.iter().any(|c| c == to) {
return ConceptResult::Satisfied;
}
}
ConceptResult::NotSatisfied(format!(
"convertible_to<{}, {}>: no implicit conversion",
from, to
))
}
pub fn common_with(&self, t: &str, u: &str) -> ConceptResult {
if t == u {
return ConceptResult::Satisfied;
}
if self.convertible_to(t, u) == ConceptResult::Satisfied
|| self.convertible_to(u, t) == ConceptResult::Satisfied
{
return ConceptResult::Satisfied;
}
ConceptResult::NotSatisfied(format!(
"common_with<{}, {}>: no common reference type",
t, u
))
}
pub fn invocable(&self, _fn_name: &str, _arg_types: &[String]) -> ConceptResult {
ConceptResult::Satisfied }
pub fn predicate(&self, _fn_name: &str, _arg_types: &[String]) -> ConceptResult {
ConceptResult::Satisfied }
pub fn totally_ordered(&self, t: &str) -> ConceptResult {
if let Some((_, cat)) = self.type_categories.iter().find(|(n, _)| n == t) {
match cat {
TypeCategory::Integral | TypeCategory::FloatingPoint => {
ConceptResult::Satisfied
}
TypeCategory::Class | TypeCategory::Enum => ConceptResult::Satisfied,
_ => ConceptResult::NotSatisfied(format!(
"totally_ordered<{}>: not orderable",
t
)),
}
} else {
ConceptResult::NotSatisfied(format!("totally_ordered<{}>: unknown type", t))
}
}
}
impl Default for ConceptEngine {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum CoroutineState {
NotStarted,
Suspended,
Running,
Returning,
Complete,
}
pub struct CoroutineHandle {
pub address: usize,
pub state: CoroutineState,
pub done: bool,
}
impl CoroutineHandle {
pub fn new(address: usize) -> Self {
Self {
address,
state: CoroutineState::NotStarted,
done: false,
}
}
pub fn done(&self) -> bool {
self.done || self.state == CoroutineState::Complete
}
pub fn resume(&mut self) {
if !self.done {
self.state = CoroutineState::Running;
}
}
pub fn destroy(&mut self) {
self.done = true;
self.state = CoroutineState::Complete;
}
pub fn address(&self) -> usize {
self.address
}
}
impl fmt::Debug for CoroutineHandle {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("CoroutineHandle")
.field("address", &format_args!("{:#x}", self.address))
.field("state", &self.state)
.field("done", &self.done)
.finish()
}
}
#[derive(Debug, Clone, Copy)]
pub struct SuspendNever;
impl SuspendNever {
pub fn await_ready(&self) -> bool {
true
}
pub fn await_suspend(&self, _handle: &CoroutineHandle) -> bool {
false
}
pub fn await_resume(&self) {}
}
#[derive(Debug, Clone, Copy)]
pub struct SuspendAlways;
impl SuspendAlways {
pub fn await_ready(&self) -> bool {
false
}
pub fn await_suspend(&self, _handle: &CoroutineHandle) -> bool {
true
}
pub fn await_resume(&self) {}
}
#[derive(Debug, Clone)]
pub struct CoroutinePromise {
pub return_type: String,
pub promise_type: String,
pub initial_suspend: CoroutineSuspendPoint,
pub final_suspend: CoroutineSuspendPoint,
}
#[derive(Debug, Clone)]
pub enum CoroutineSuspendPoint {
Never,
Always,
Custom(String),
}
impl CoroutinePromise {
pub fn new(return_type: &str) -> Self {
Self {
return_type: return_type.to_string(),
promise_type: format!("{}_promise_type", return_type),
initial_suspend: CoroutineSuspendPoint::Always,
final_suspend: CoroutineSuspendPoint::Always,
}
}
pub fn with_promise_type(mut self, pt: &str) -> Self {
self.promise_type = pt.to_string();
self
}
pub fn with_initial_suspend(mut self, sp: CoroutineSuspendPoint) -> Self {
self.initial_suspend = sp;
self
}
pub fn with_final_suspend(mut self, sp: CoroutineSuspendPoint) -> Self {
self.final_suspend = sp;
self
}
}
pub struct CoroutineTraits {
pub return_type: String,
pub promise_type: String,
pub allocator_type: Option<String>,
}
impl CoroutineTraits {
pub fn new(return_type: &str, promise_type: &str) -> Self {
Self {
return_type: return_type.to_string(),
promise_type: promise_type.to_string(),
allocator_type: None,
}
}
pub fn with_allocator(mut self, alloc: &str) -> Self {
self.allocator_type = Some(alloc.to_string());
self
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum StrongOrdering {
Less,
Equal,
Greater,
}
impl StrongOrdering {
pub fn as_weak(&self) -> WeakOrdering {
match self {
StrongOrdering::Less => WeakOrdering::Less,
StrongOrdering::Equal => WeakOrdering::Equivalent,
StrongOrdering::Greater => WeakOrdering::Greater,
}
}
pub fn as_partial(&self) -> PartialOrdering {
match self {
StrongOrdering::Less => PartialOrdering::Less,
StrongOrdering::Equal => PartialOrdering::Equivalent,
StrongOrdering::Greater => PartialOrdering::Greater,
}
}
pub fn is_lt(&self) -> bool {
matches!(self, StrongOrdering::Less)
}
pub fn is_eq(&self) -> bool {
matches!(self, StrongOrdering::Equal)
}
pub fn is_gt(&self) -> bool {
matches!(self, StrongOrdering::Greater)
}
}
impl From<std::cmp::Ordering> for StrongOrdering {
fn from(o: std::cmp::Ordering) -> Self {
match o {
std::cmp::Ordering::Less => StrongOrdering::Less,
std::cmp::Ordering::Equal => StrongOrdering::Equal,
std::cmp::Ordering::Greater => StrongOrdering::Greater,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum WeakOrdering {
Less,
Equivalent,
Greater,
}
impl WeakOrdering {
pub fn as_partial(&self) -> PartialOrdering {
match self {
WeakOrdering::Less => PartialOrdering::Less,
WeakOrdering::Equivalent => PartialOrdering::Equivalent,
WeakOrdering::Greater => PartialOrdering::Greater,
}
}
pub fn is_lt(&self) -> bool {
matches!(self, WeakOrdering::Less)
}
pub fn is_eqv(&self) -> bool {
matches!(self, WeakOrdering::Equivalent)
}
pub fn is_gt(&self) -> bool {
matches!(self, WeakOrdering::Greater)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum PartialOrdering {
Less,
Equivalent,
Greater,
Unordered,
}
impl PartialOrdering {
pub fn is_lt(&self) -> bool {
matches!(self, PartialOrdering::Less)
}
pub fn is_eqv(&self) -> bool {
matches!(self, PartialOrdering::Equivalent)
}
pub fn is_gt(&self) -> bool {
matches!(self, PartialOrdering::Greater)
}
pub fn is_unordered(&self) -> bool {
matches!(self, PartialOrdering::Unordered)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ComparisonCategory {
Strong(StrongOrdering),
Weak(WeakOrdering),
Partial(PartialOrdering),
}
impl ComparisonCategory {
pub fn strong(o: StrongOrdering) -> Self {
ComparisonCategory::Strong(o)
}
pub fn weak(o: WeakOrdering) -> Self {
ComparisonCategory::Weak(o)
}
pub fn partial(o: PartialOrdering) -> Self {
ComparisonCategory::Partial(o)
}
}
pub fn common_comparison_category(
a: ComparisonCategory,
b: ComparisonCategory,
) -> ComparisonCategory {
match (a, b) {
(ComparisonCategory::Strong(_), ComparisonCategory::Strong(_)) => a,
(ComparisonCategory::Weak(_), ComparisonCategory::Weak(_)) => a,
(ComparisonCategory::Partial(_), ComparisonCategory::Partial(_)) => a,
(ComparisonCategory::Strong(s), ComparisonCategory::Weak(_)) => {
ComparisonCategory::Weak(s.as_weak())
}
(ComparisonCategory::Weak(_), ComparisonCategory::Strong(s)) => {
ComparisonCategory::Weak(s.as_weak())
}
(ComparisonCategory::Strong(s), ComparisonCategory::Partial(_)) => {
ComparisonCategory::Partial(s.as_partial())
}
(ComparisonCategory::Partial(_), ComparisonCategory::Strong(s)) => {
ComparisonCategory::Partial(s.as_partial())
}
(ComparisonCategory::Weak(w), ComparisonCategory::Partial(_)) => {
ComparisonCategory::Partial(w.as_partial())
}
(ComparisonCategory::Partial(_), ComparisonCategory::Weak(w)) => {
ComparisonCategory::Partial(w.as_partial())
}
}
}
pub fn three_way_compare<T: Ord>(a: &T, b: &T) -> StrongOrdering {
match a.cmp(b) {
std::cmp::Ordering::Less => StrongOrdering::Less,
std::cmp::Ordering::Equal => StrongOrdering::Equal,
std::cmp::Ordering::Greater => StrongOrdering::Greater,
}
}
pub struct CountingSemaphore {
counter: i64,
max_value: i64,
}
impl CountingSemaphore {
pub fn new(desired: i64, max_value: i64) -> Self {
Self {
counter: desired.min(max_value).max(0),
max_value,
}
}
pub fn release(&mut self, update: i64) {
self.counter = (self.counter + update).min(self.max_value);
}
pub fn acquire(&mut self) -> bool {
if self.counter > 0 {
self.counter -= 1;
true
} else {
false
}
}
pub fn try_acquire(&mut self) -> bool {
self.acquire()
}
pub fn try_acquire_for(&mut self, _timeout_ms: u64) -> bool {
self.acquire()
}
pub fn max(&self) -> i64 {
self.max_value
}
pub fn value(&self) -> i64 {
self.counter
}
}
impl fmt::Debug for CountingSemaphore {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("CountingSemaphore")
.field("counter", &self.counter)
.field("max_value", &self.max_value)
.finish()
}
}
pub struct BinarySemaphore {
available: bool,
}
impl BinarySemaphore {
pub fn new(initially_available: bool) -> Self {
Self {
available: initially_available,
}
}
pub fn release(&mut self) {
self.available = true;
}
pub fn acquire(&mut self) -> bool {
if self.available {
self.available = false;
true
} else {
false
}
}
pub fn try_acquire(&mut self) -> bool {
self.acquire()
}
pub fn is_available(&self) -> bool {
self.available
}
}
impl fmt::Debug for BinarySemaphore {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("BinarySemaphore")
.field("available", &self.available)
.finish()
}
}
pub struct BasicSyncbuf {
buffer: Vec<u8>,
emit_on_sync: bool,
emit_on_destroy: bool,
}
impl BasicSyncbuf {
pub fn new() -> Self {
Self {
buffer: Vec::new(),
emit_on_sync: true,
emit_on_destroy: true,
}
}
pub fn set_emit_on_sync(&mut self, emit: bool) {
self.emit_on_sync = emit;
}
pub fn emit_on_sync(&self) -> bool {
self.emit_on_sync
}
pub fn sputn(&mut self, data: &[u8]) -> usize {
self.buffer.extend_from_slice(data);
data.len()
}
pub fn sputc(&mut self, c: u8) {
self.buffer.push(c);
}
pub fn emit(&mut self) -> Vec<u8> {
let result = self.buffer.clone();
self.buffer.clear();
result
}
pub fn sync(&mut self) -> Vec<u8> {
if self.emit_on_sync {
self.emit()
} else {
self.buffer.clone()
}
}
pub fn str(&self) -> String {
String::from_utf8_lossy(&self.buffer).to_string()
}
}
impl fmt::Debug for BasicSyncbuf {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("BasicSyncbuf")
.field("buffer_len", &self.buffer.len())
.field("emit_on_sync", &self.emit_on_sync)
.finish()
}
}
impl Default for BasicSyncbuf {
fn default() -> Self {
Self::new()
}
}
impl Drop for BasicSyncbuf {
fn drop(&mut self) {
if self.emit_on_destroy && !self.buffer.is_empty() {
}
}
}
pub struct BasicOsyncstream {
buffer: BasicSyncbuf,
wrapped_stream: Option<String>,
}
impl BasicOsyncstream {
pub fn new(wrapped: Option<&str>) -> Self {
Self {
buffer: BasicSyncbuf::new(),
wrapped_stream: wrapped.map(|s| s.to_string()),
}
}
pub fn rdbuf(&mut self) -> &mut BasicSyncbuf {
&mut self.buffer
}
pub fn emit(&mut self) -> Vec<u8> {
self.buffer.emit()
}
pub fn write(&mut self, s: &str) {
self.buffer.sputn(s.as_bytes());
}
pub fn flush(&mut self) -> Vec<u8> {
self.buffer.sync()
}
pub fn wrapped_stream(&self) -> Option<&str> {
self.wrapped_stream.as_deref()
}
}
impl fmt::Debug for BasicOsyncstream {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("BasicOsyncstream")
.field("buffer", &self.buffer)
.field("wrapped_stream", &self.wrapped_stream)
.finish()
}
}
#[derive(Debug, Clone)]
pub struct StopSource {
stop_requested: bool,
stop_possible: bool,
}
impl StopSource {
pub fn new() -> Self {
Self {
stop_requested: false,
stop_possible: true,
}
}
pub fn request_stop(&mut self) -> bool {
if self.stop_possible {
self.stop_requested = true;
true
} else {
false
}
}
pub fn stop_requested(&self) -> bool {
self.stop_requested
}
pub fn get_token(&self) -> StopToken {
StopToken {
stop_requested: self.stop_requested,
}
}
pub fn stop_possible(&self) -> bool {
self.stop_possible
}
}
impl Default for StopSource {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone, Copy)]
pub struct StopToken {
stop_requested: bool,
}
impl StopToken {
pub fn never_stoppable() -> Self {
Self {
stop_requested: false,
}
}
pub fn stop_requested(&self) -> bool {
self.stop_requested
}
pub fn stop_possible(&self) -> bool {
true
}
}
pub struct StopCallback<F: FnOnce()> {
callback: Option<F>,
token: StopToken,
executed: bool,
}
impl<F: FnOnce()> StopCallback<F> {
pub fn new(token: StopToken, callback: F) -> Self {
let mut sc = Self {
callback: Some(callback),
token,
executed: false,
};
if token.stop_requested() {
sc.execute();
}
sc
}
fn execute(&mut self) {
if !self.executed {
if let Some(cb) = self.callback.take() {
cb();
self.executed = true;
}
}
}
pub fn is_executed(&self) -> bool {
self.executed
}
}
impl<F: FnOnce()> Drop for StopCallback<F> {
fn drop(&mut self) {
self.execute();
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum BarrierPhase {
Phase0,
Phase1,
}
impl BarrierPhase {
pub fn next(&self) -> Self {
match self {
BarrierPhase::Phase0 => BarrierPhase::Phase1,
BarrierPhase::Phase1 => BarrierPhase::Phase0,
}
}
}
pub struct Barrier {
expected: usize,
arrived: usize,
phase: BarrierPhase,
completion_description: Option<String>,
}
impl Barrier {
pub fn new(expected: usize) -> Self {
Self {
expected,
arrived: 0,
phase: BarrierPhase::Phase0,
completion_description: None,
}
}
pub fn with_completion(expected: usize, completion: &str) -> Self {
Self {
expected,
arrived: 0,
phase: BarrierPhase::Phase0,
completion_description: Some(completion.to_string()),
}
}
pub fn arrive(&mut self) -> usize {
self.arrived += 1;
self.arrived
}
pub fn wait(&mut self) {
while self.arrived < self.expected {
}
}
pub fn arrive_and_wait(&mut self) {
self.arrive();
self.wait();
if self.arrived >= self.expected {
self.arrived = 0;
self.phase = self.phase.next();
}
}
pub fn is_ready(&self) -> bool {
self.arrived >= self.expected
}
pub fn phase(&self) -> BarrierPhase {
self.phase
}
}
impl fmt::Debug for Barrier {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("Barrier")
.field("expected", &self.expected)
.field("arrived", &self.arrived)
.field("phase", &self.phase)
.finish()
}
}
pub struct Latch {
count: usize,
}
impl Latch {
pub fn new(count: usize) -> Self {
Self { count }
}
pub fn count_down(&mut self) {
if self.count > 0 {
self.count -= 1;
}
}
pub fn count_down_by(&mut self, n: usize) {
self.count = self.count.saturating_sub(n);
}
pub fn wait(&self) {
}
pub fn try_wait(&self) -> bool {
self.count == 0
}
pub fn arrive_and_wait(&mut self) {
self.count_down();
self.wait();
}
pub fn is_ready(&self) -> bool {
self.count == 0
}
pub fn count(&self) -> usize {
self.count
}
}
impl fmt::Debug for Latch {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("Latch")
.field("count", &self.count)
.finish()
}
}
pub type Syncbuf = BasicSyncbuf;
pub type Osyncstream = BasicOsyncstream;
pub struct Stl3Registry {
pub expected_enabled: bool,
pub source_location_enabled: bool,
pub bit_ops_enabled: bool,
pub numbers_enabled: bool,
pub concepts_enabled: bool,
pub coroutine_enabled: bool,
pub compare_enabled: bool,
pub semaphore_enabled: bool,
pub syncstream_enabled: bool,
pub stop_token_enabled: bool,
pub barrier_enabled: bool,
pub latch_enabled: bool,
pub standard_year: u32,
pub feature_db: FeatureTestDatabase,
}
impl Stl3Registry {
pub fn new() -> Self {
Self {
expected_enabled: false,
source_location_enabled: false,
bit_ops_enabled: false,
numbers_enabled: false,
concepts_enabled: false,
coroutine_enabled: false,
compare_enabled: false,
semaphore_enabled: false,
syncstream_enabled: false,
stop_token_enabled: false,
barrier_enabled: false,
latch_enabled: false,
standard_year: 201703,
feature_db: FeatureTestDatabase::new(),
}
}
pub fn with_standard(mut self, year: u32) -> Self {
self.standard_year = year;
self.expected_enabled = year >= 202302;
self.source_location_enabled = year >= 202002;
self.bit_ops_enabled = year >= 202002;
self.numbers_enabled = year >= 202002;
self.concepts_enabled = year >= 202002;
self.coroutine_enabled = year >= 202002;
self.compare_enabled = year >= 202002;
self.semaphore_enabled = year >= 202002;
self.syncstream_enabled = year >= 202002;
self.stop_token_enabled = year >= 202002;
self.barrier_enabled = year >= 202002;
self.latch_enabled = year >= 202002;
self
}
pub fn feature_count(&self) -> usize {
[
self.expected_enabled, self.source_location_enabled,
self.bit_ops_enabled, self.numbers_enabled, self.concepts_enabled,
self.coroutine_enabled, self.compare_enabled, self.semaphore_enabled,
self.syncstream_enabled, self.stop_token_enabled, self.barrier_enabled,
self.latch_enabled,
]
.iter()
.filter(|&&x| x)
.count()
}
pub fn headers_for_standard(&self) -> Vec<&'static str> {
let mut headers = Vec::new();
if self.expected_enabled {
headers.push("<expected>");
}
if self.source_location_enabled {
headers.push("<source_location>");
}
if self.bit_ops_enabled {
headers.push("<bit>");
}
if self.numbers_enabled {
headers.push("<numbers>");
}
if self.concepts_enabled {
headers.push("<concepts>");
}
if self.coroutine_enabled {
headers.push("<coroutine>");
}
if self.compare_enabled {
headers.push("<compare>");
}
if self.semaphore_enabled {
headers.push("<semaphore>");
}
if self.syncstream_enabled {
headers.push("<syncstream>");
}
if self.stop_token_enabled {
headers.push("<stop_token>");
}
if self.barrier_enabled {
headers.push("<barrier>");
}
if self.latch_enabled {
headers.push("<latch>");
}
headers
}
}
impl Default for Stl3Registry {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_expected_value() {
let e = StdExpected::<i32, String>::value(42);
assert!(e.has_value());
assert!(!e.has_error());
assert_eq!(*e.value_ref(), 42);
}
#[test]
fn test_expected_error() {
let e = StdExpected::<i32, String>::error("fail".to_string());
assert!(!e.has_value());
assert!(e.has_error());
assert_eq!(e.error_ref(), "fail");
}
#[test]
fn test_expected_value_or() {
let e1 = StdExpected::<i32, String>::value(10);
assert_eq!(e1.value_or(0), 10);
let e2 = StdExpected::<i32, String>::error("err".to_string());
assert_eq!(e2.value_or(0), 0);
}
#[test]
fn test_expected_value_or_else() {
let e = StdExpected::<i32, String>::error("err".to_string());
assert_eq!(e.value_or_else(|_| 99), 99);
}
#[test]
fn test_expected_and_then() {
let e = StdExpected::<i32, String>::value(5);
let result = e.and_then(|v| StdExpected::value(v * 2));
assert_eq!(result.into_value(), 10);
}
#[test]
fn test_expected_and_then_error() {
let e = StdExpected::<i32, String>::error("fail".to_string());
let result = e.and_then(|v| StdExpected::<i32, String>::value(v * 2));
assert!(result.has_error());
}
#[test]
fn test_expected_or_else() {
let e = StdExpected::<i32, String>::error("err".to_string());
let result = e.or_else(|_| StdExpected::value(42));
assert_eq!(result.into_value(), 42);
}
#[test]
fn test_expected_transform() {
let e = StdExpected::<i32, String>::value(3);
let result = e.transform(|v| v * 10);
assert_eq!(result.into_value(), 30);
}
#[test]
fn test_expected_transform_error() {
let e = StdExpected::<i32, String>::error("bad".to_string());
let result = e.transform_error(|err| format!("prefix_{}", err));
assert_eq!(result.error_ref(), "prefix_bad");
}
#[test]
fn test_expected_ok_err() {
let e1 = StdExpected::<i32, String>::value(1);
assert_eq!(e1.ok(), Some(1));
assert_eq!(e1.err(), None);
let e2 = StdExpected::<i32, String>::error("e".to_string());
assert_eq!(e2.ok(), None);
assert_eq!(e2.err(), Some("e".to_string()));
}
#[test]
fn test_expected_swap() {
let e = StdExpected::<String, String>::value("val".to_string());
let swapped = e.swap_error_value();
assert!(swapped.has_error());
assert_eq!(swapped.error_ref(), "val");
}
#[test]
fn test_expected_map() {
let e = StdExpected::<i32, String>::value(2);
let result = e.map(|v| v + 1, |err| format!("_{}", err));
assert_eq!(result.into_value(), 3);
}
#[test]
fn test_unexpected_new() {
let u = Unexpected::new(404);
assert_eq!(*u.error(), 404);
}
#[test]
fn test_source_location_new() {
let loc = SourceLocation::new("test.cpp", "main", 10, 5);
assert_eq!(loc.file_name(), "test.cpp");
assert_eq!(loc.function_name(), "main");
assert_eq!(loc.line(), 10);
assert_eq!(loc.column(), 5);
}
#[test]
fn test_source_location_current() {
let loc = SourceLocation::current("file.cc", "foo", 42, 3);
assert_eq!(loc.line(), 42);
}
#[test]
fn test_source_location_position_string() {
let loc = SourceLocation::new("a.cpp", "fn", 1, 1);
assert_eq!(loc.to_position_string(), "a.cpp:1:1");
}
#[test]
fn test_source_location_full_string() {
let loc = SourceLocation::new("b.cpp", "bar", 7, 3);
assert_eq!(loc.to_full_string(), "bar at b.cpp:7:3");
}
#[test]
fn test_source_location_default() {
let loc = SourceLocation::default();
assert_eq!(loc.file_name(), "<unknown>");
assert_eq!(loc.line(), 0);
}
#[test]
fn test_has_single_bit() {
assert!(has_single_bit(1));
assert!(has_single_bit(2));
assert!(has_single_bit(4));
assert!(has_single_bit(1024));
assert!(!has_single_bit(0));
assert!(!has_single_bit(3));
assert!(!has_single_bit(6));
}
#[test]
fn test_bit_ceil() {
assert_eq!(bit_ceil(0), 1);
assert_eq!(bit_ceil(1), 1);
assert_eq!(bit_ceil(2), 2);
assert_eq!(bit_ceil(3), 4);
assert_eq!(bit_ceil(5), 8);
assert_eq!(bit_ceil(10), 16);
}
#[test]
fn test_bit_floor() {
assert_eq!(bit_floor(0), 0);
assert_eq!(bit_floor(1), 1);
assert_eq!(bit_floor(2), 2);
assert_eq!(bit_floor(3), 2);
assert_eq!(bit_floor(5), 4);
assert_eq!(bit_floor(10), 8);
}
#[test]
fn test_bit_width() {
assert_eq!(bit_width(0), 0);
assert_eq!(bit_width(1), 1);
assert_eq!(bit_width(2), 2);
assert_eq!(bit_width(3), 2);
assert_eq!(bit_width(4), 3);
assert_eq!(bit_width(255), 8);
}
#[test]
fn test_rotl_u32() {
assert_eq!(rotl_u32(1, 1), 2);
assert_eq!(rotl_u32(1, 31), 0x80000000);
assert_eq!(rotl_u32(0x80000000, 1), 1);
}
#[test]
fn test_rotr_u32() {
assert_eq!(rotr_u32(2, 1), 1);
assert_eq!(rotr_u32(1, 1), 0x80000000);
}
#[test]
fn test_countl_zero() {
assert_eq!(countl_zero_u32(0), 32);
assert_eq!(countl_zero_u32(1), 31);
assert_eq!(countl_zero_u32(0x80000000), 0);
}
#[test]
fn test_countr_zero() {
assert_eq!(countr_zero_u32(0), 32);
assert_eq!(countr_zero_u32(1), 0);
assert_eq!(countr_zero_u32(8), 3);
}
#[test]
fn test_countl_one() {
assert_eq!(countl_one_u32(!0u32), 32);
assert_eq!(countl_one_u32(!1u32), 31);
}
#[test]
fn test_countr_one() {
assert_eq!(countr_one_u32(!0u32), 32);
assert_eq!(countr_one_u32(!2u32), 1);
}
#[test]
fn test_popcount() {
assert_eq!(popcount_u32(0), 0);
assert_eq!(popcount_u32(1), 1);
assert_eq!(popcount_u32(3), 2);
assert_eq!(popcount_u32(0xFF), 8);
}
#[test]
fn test_bit_cast_f32_u32() {
let v: f32 = 1.0;
assert_eq!(bit_cast_f32_to_u32(v), 0x3F800000);
assert_eq!(bit_cast_u32_to_f32(0x3F800000), 1.0);
}
#[test]
fn test_ntohs_htons() {
assert_eq!(htons(0x1234), 0x1234u16.to_be());
assert_eq!(ntohs(0x1234u16.to_be()), 0x1234);
}
#[test]
fn test_has_single_bit_u32() {
assert!(has_single_bit_u32(16));
assert!(!has_single_bit_u32(18));
}
#[test]
fn test_bit_ceil_u32() {
assert_eq!(bit_ceil_u32(7), 8);
assert_eq!(bit_ceil_u32(16), 16);
}
#[test]
fn test_bit_floor_u32() {
assert_eq!(bit_floor_u32(7), 4);
assert_eq!(bit_floor_u32(16), 16);
}
#[test]
fn test_numbers_f64_pi() {
assert!((numbers_f64::PI - std::f64::consts::PI).abs() < 1e-15);
}
#[test]
fn test_numbers_f64_e() {
assert!((numbers_f64::E - std::f64::consts::E).abs() < 1e-15);
}
#[test]
fn test_numbers_f64_sqrt2() {
assert!((numbers_f64::SQRT2 - 1.4142135623730951).abs() < 1e-15);
}
#[test]
fn test_numbers_f64_phi() {
let expected = (1.0 + 5.0_f64.sqrt()) / 2.0;
assert!((numbers_f64::PHI - expected).abs() < 1e-15);
}
#[test]
fn test_numbers_f32() {
assert!((numbers_f32::PI - std::f32::consts::PI).abs() < 1e-7);
}
#[test]
fn test_math_constants_f64() {
let mc = MathConstants::<f64>::new();
assert!((mc.pi - std::f64::consts::PI).abs() < 1e-15);
}
#[test]
fn test_math_constants_f32() {
let mc = MathConstants::<f32>::new();
assert!((mc.pi - std::f32::consts::PI).abs() < 1e-7);
}
#[test]
fn test_feature_macro_names() {
assert_eq!(FeatureTestMacro::Byte.macro_name(), "__cpp_lib_byte");
assert_eq!(FeatureTestMacro::Expected.macro_name(), "__cpp_lib_expected");
assert_eq!(
FeatureTestMacro::ThreeWayComparison.macro_name(),
"__cpp_lib_three_way_comparison"
);
}
#[test]
fn test_feature_macro_introduced() {
assert_eq!(FeatureTestMacro::Byte.introduced_in(), "201603");
assert_eq!(FeatureTestMacro::Concepts.introduced_in(), "202002");
assert_eq!(FeatureTestMacro::Expected.introduced_in(), "202211");
}
#[test]
fn test_feature_macro_value_for_standard() {
assert_eq!(
FeatureTestMacro::Byte.value_for_standard(201703),
Some(201603)
);
assert_eq!(FeatureTestMacro::Byte.value_for_standard(201000), None);
assert_eq!(
FeatureTestMacro::Concepts.value_for_standard(202002),
Some(202002)
);
}
#[test]
fn test_feature_test_database_generate_defines() {
let db = FeatureTestDatabase::new();
let defines = db.generate_defines(202002);
assert!(!defines.is_empty());
assert!(defines.iter().any(|d| d.contains("__cpp_lib_span")));
}
#[test]
fn test_feature_test_database_enable_disable() {
let mut db = FeatureTestDatabase::new();
assert!(db.is_available(FeatureTestMacro::Span, 202002));
db.enable(FeatureTestMacro::Span); assert!(db.is_available(FeatureTestMacro::Span, 202002));
}
#[test]
fn test_feature_test_database_cpp23() {
let db = FeatureTestDatabase::new();
assert!(db.is_available(FeatureTestMacro::Expected, 202302));
assert!(!db.is_available(FeatureTestMacro::Expected, 202002));
}
#[test]
fn test_concept_same_as() {
let mut engine = ConceptEngine::new();
assert_eq!(engine.same_as("int", "int"), ConceptResult::Satisfied);
assert!(matches!(
engine.same_as("int", "float"),
ConceptResult::NotSatisfied(_)
));
}
#[test]
fn test_concept_derived_from() {
let mut engine = ConceptEngine::new();
engine.register_base("Derived", "Base");
assert_eq!(
engine.derived_from("Derived", "Base"),
ConceptResult::Satisfied
);
assert!(matches!(
engine.derived_from("Base", "Derived"),
ConceptResult::NotSatisfied(_)
));
}
#[test]
fn test_concept_integral() {
let mut engine = ConceptEngine::new();
engine.register_type("int", TypeCategory::Integral);
engine.register_type("float", TypeCategory::FloatingPoint);
assert_eq!(engine.integral("int"), ConceptResult::Satisfied);
assert!(matches!(
engine.integral("float"),
ConceptResult::NotSatisfied(_)
));
}
#[test]
fn test_concept_floating_point() {
let mut engine = ConceptEngine::new();
engine.register_type("double", TypeCategory::FloatingPoint);
engine.register_type("int", TypeCategory::Integral);
assert_eq!(engine.floating_point("double"), ConceptResult::Satisfied);
assert!(matches!(
engine.floating_point("int"),
ConceptResult::NotSatisfied(_)
));
}
#[test]
fn test_concept_movable() {
let mut engine = ConceptEngine::new();
engine.register_type("int", TypeCategory::Integral);
engine.register_type("void", TypeCategory::Void);
assert_eq!(engine.movable("int"), ConceptResult::Satisfied);
assert!(matches!(
engine.movable("void"),
ConceptResult::NotSatisfied(_)
));
}
#[test]
fn test_concept_copyable() {
let mut engine = ConceptEngine::new();
engine.register_type("int", TypeCategory::Integral);
assert_eq!(engine.copyable("int"), ConceptResult::Satisfied);
}
#[test]
fn test_concept_regular() {
let mut engine = ConceptEngine::new();
engine.register_type("int", TypeCategory::Integral);
assert_eq!(engine.regular("int"), ConceptResult::Satisfied);
}
#[test]
fn test_concept_convertible_to() {
let mut engine = ConceptEngine::new();
engine.register_conversion("int", "long");
assert_eq!(
engine.convertible_to("int", "long"),
ConceptResult::Satisfied
);
assert_eq!(
engine.convertible_to("int", "int"),
ConceptResult::Satisfied
);
assert!(matches!(
engine.convertible_to("long", "int"),
ConceptResult::NotSatisfied(_)
));
}
#[test]
fn test_concept_common_with() {
let mut engine = ConceptEngine::new();
engine.register_conversion("int", "long");
assert_eq!(
engine.common_with("int", "long"),
ConceptResult::Satisfied
);
}
#[test]
fn test_concept_totally_ordered() {
let mut engine = ConceptEngine::new();
engine.register_type("int", TypeCategory::Integral);
engine.register_type("void", TypeCategory::Void);
assert_eq!(engine.totally_ordered("int"), ConceptResult::Satisfied);
assert!(matches!(
engine.totally_ordered("void"),
ConceptResult::NotSatisfied(_)
));
}
#[test]
fn test_coroutine_handle_new() {
let h = CoroutineHandle::new(0x1000);
assert!(!h.done());
assert_eq!(h.address(), 0x1000);
}
#[test]
fn test_coroutine_handle_resume_destroy() {
let mut h = CoroutineHandle::new(0x2000);
h.resume();
assert!(matches!(h.state, CoroutineState::Running));
h.destroy();
assert!(h.done());
}
#[test]
fn test_suspend_never() {
let sn = SuspendNever;
assert!(sn.await_ready());
assert!(!sn.await_suspend(&CoroutineHandle::new(0)));
}
#[test]
fn test_suspend_always() {
let sa = SuspendAlways;
assert!(!sa.await_ready());
assert!(sa.await_suspend(&CoroutineHandle::new(0)));
}
#[test]
fn test_coroutine_promise() {
let p = CoroutinePromise::new("task<int>")
.with_promise_type("task_promise<int>")
.with_initial_suspend(CoroutineSuspendPoint::Never);
assert_eq!(p.return_type, "task<int>");
assert!(matches!(p.initial_suspend, CoroutineSuspendPoint::Never));
}
#[test]
fn test_coroutine_traits() {
let ct = CoroutineTraits::new("generator<int>", "gen_promise<int>")
.with_allocator("std::allocator<int>");
assert_eq!(ct.return_type, "generator<int>");
assert!(ct.allocator_type.is_some());
}
#[test]
fn test_strong_ordering() {
assert!(StrongOrdering::Less.is_lt());
assert!(StrongOrdering::Equal.is_eq());
assert!(StrongOrdering::Greater.is_gt());
}
#[test]
fn test_strong_to_weak() {
assert_eq!(
StrongOrdering::Less.as_weak(),
WeakOrdering::Less
);
assert_eq!(
StrongOrdering::Equal.as_weak(),
WeakOrdering::Equivalent
);
}
#[test]
fn test_strong_to_partial() {
assert_eq!(
StrongOrdering::Less.as_partial(),
PartialOrdering::Less
);
}
#[test]
fn test_partial_ordering_unordered() {
assert!(PartialOrdering::Unordered.is_unordered());
assert!(!PartialOrdering::Less.is_unordered());
}
#[test]
fn test_common_comparison_category() {
let result = common_comparison_category(
ComparisonCategory::strong(StrongOrdering::Equal),
ComparisonCategory::weak(WeakOrdering::Equivalent),
);
assert!(matches!(result, ComparisonCategory::Weak(_)));
}
#[test]
fn test_three_way_compare() {
assert_eq!(three_way_compare(&1i32, &2i32), StrongOrdering::Less);
assert_eq!(three_way_compare(&5i32, &5i32), StrongOrdering::Equal);
assert_eq!(three_way_compare(&10i32, &1i32), StrongOrdering::Greater);
}
#[test]
fn test_weak_ordering_methods() {
assert!(WeakOrdering::Less.is_lt());
assert!(WeakOrdering::Equivalent.is_eqv());
assert!(WeakOrdering::Greater.is_gt());
}
#[test]
fn test_counting_semaphore_new() {
let cs = CountingSemaphore::new(5, 10);
assert_eq!(cs.value(), 5);
assert_eq!(cs.max(), 10);
}
#[test]
fn test_counting_semaphore_acquire_release() {
let mut cs = CountingSemaphore::new(3, 10);
assert!(cs.acquire());
assert_eq!(cs.value(), 2);
cs.release(1);
assert_eq!(cs.value(), 3);
}
#[test]
fn test_counting_semaphore_max() {
let mut cs = CountingSemaphore::new(0, 5);
cs.release(10);
assert_eq!(cs.value(), 5); }
#[test]
fn test_counting_semaphore_try_acquire_empty() {
let mut cs = CountingSemaphore::new(0, 10);
assert!(!cs.try_acquire());
}
#[test]
fn test_binary_semaphore() {
let mut bs = BinarySemaphore::new(true);
assert!(bs.is_available());
assert!(bs.acquire());
assert!(!bs.is_available());
assert!(!bs.acquire());
bs.release();
assert!(bs.is_available());
}
#[test]
fn test_binary_semaphore_try_acquire() {
let mut bs = BinarySemaphore::new(false);
assert!(!bs.try_acquire());
}
#[test]
fn test_basic_syncbuf_write() {
let mut buf = BasicSyncbuf::new();
buf.sputn(b"hello");
assert_eq!(buf.str(), "hello");
}
#[test]
fn test_basic_syncbuf_emit() {
let mut buf = BasicSyncbuf::new();
buf.sputn(b"world");
let emitted = buf.emit();
assert_eq!(emitted, b"world");
assert!(buf.str().is_empty());
}
#[test]
fn test_basic_syncbuf_emit_on_sync() {
let mut buf = BasicSyncbuf::new();
buf.set_emit_on_sync(false);
assert!(!buf.emit_on_sync());
buf.set_emit_on_sync(true);
assert!(buf.emit_on_sync());
}
#[test]
fn test_basic_osyncstream_write() {
let mut stream = BasicOsyncstream::new(Some("stdout"));
stream.write("test message");
assert!(stream.wrapped_stream().is_some());
let emitted = stream.emit();
assert_eq!(String::from_utf8_lossy(&emitted), "test message");
}
#[test]
fn test_basic_osyncstream_flush() {
let mut stream = BasicOsyncstream::new(None);
stream.write("flush test");
let result = stream.flush();
assert_eq!(String::from_utf8_lossy(&result), "flush test");
}
#[test]
fn test_stop_source_request_stop() {
let mut source = StopSource::new();
assert!(!source.stop_requested());
assert!(source.request_stop());
assert!(source.stop_requested());
}
#[test]
fn test_stop_token_from_source() {
let mut source = StopSource::new();
let token = source.get_token();
assert!(!token.stop_requested());
source.request_stop();
let token2 = source.get_token();
assert!(token2.stop_requested());
}
#[test]
fn test_stop_token_never_stoppable() {
let token = StopToken::never_stoppable();
assert!(!token.stop_requested());
assert!(token.stop_possible());
}
#[test]
fn test_stop_callback() {
let source = StopSource::new();
let token = source.get_token();
let mut called = false;
{
let _cb = StopCallback::new(token, || called = true);
} }
#[test]
fn test_barrier_new() {
let b = Barrier::new(4);
assert_eq!(b.expected, 4);
assert!(!b.is_ready());
}
#[test]
fn test_barrier_arrive() {
let mut b = Barrier::new(3);
assert_eq!(b.arrive(), 1);
assert_eq!(b.arrive(), 2);
assert!(!b.is_ready());
assert_eq!(b.arrive(), 3);
assert!(b.is_ready());
}
#[test]
fn test_barrier_arrive_and_wait() {
let mut b = Barrier::new(2);
b.arrive();
b.arrive_and_wait();
assert!(b.is_ready());
}
#[test]
fn test_barrier_with_completion() {
let b = Barrier::with_completion(2, "cleanup");
assert_eq!(b.completion_description, Some("cleanup".to_string()));
}
#[test]
fn test_barrier_phase() {
let mut b = Barrier::new(1);
assert_eq!(b.phase(), BarrierPhase::Phase0);
b.arrive_and_wait();
assert_eq!(b.phase(), BarrierPhase::Phase1);
}
#[test]
fn test_latch_new() {
let l = Latch::new(5);
assert_eq!(l.count(), 5);
assert!(!l.is_ready());
}
#[test]
fn test_latch_count_down() {
let mut l = Latch::new(3);
l.count_down();
assert_eq!(l.count(), 2);
l.count_down();
l.count_down();
assert_eq!(l.count(), 0);
assert!(l.is_ready());
}
#[test]
fn test_latch_count_down_by() {
let mut l = Latch::new(10);
l.count_down_by(3);
assert_eq!(l.count(), 7);
l.count_down_by(10);
assert_eq!(l.count(), 0);
}
#[test]
fn test_latch_arrive_and_wait() {
let mut l = Latch::new(1);
l.arrive_and_wait();
assert!(l.is_ready());
}
#[test]
fn test_latch_try_wait() {
let l = Latch::new(0);
assert!(l.try_wait());
let l2 = Latch::new(1);
assert!(!l2.try_wait());
}
#[test]
fn test_stl3_registry_default() {
let reg = Stl3Registry::new();
assert!(!reg.expected_enabled);
assert_eq!(reg.feature_count(), 0);
}
#[test]
fn test_stl3_registry_cpp20() {
let reg = Stl3Registry::new().with_standard(202002);
assert!(reg.source_location_enabled);
assert!(reg.bit_ops_enabled);
assert!(reg.concepts_enabled);
assert!(!reg.expected_enabled);
assert!(reg.feature_count() >= 10);
}
#[test]
fn test_stl3_registry_cpp23() {
let reg = Stl3Registry::new().with_standard(202302);
assert!(reg.expected_enabled);
assert!(reg.feature_count() == 12);
}
#[test]
fn test_stl3_registry_headers() {
let reg = Stl3Registry::new().with_standard(202002);
let headers = reg.headers_for_standard();
assert!(headers.contains(&"<bit>"));
assert!(headers.contains(&"<concepts>"));
assert!(!headers.contains(&"<expected>"));
}
#[test]
fn test_stl3_registry_headers_cpp23() {
let reg = Stl3Registry::new().with_standard(202302);
let headers = reg.headers_for_standard();
assert!(headers.contains(&"<expected>"));
}
}