diff --git a/scripts/internal/qnx_creds.py b/scripts/internal/qnx_creds.py new file mode 100644 index 00000000..06ae7951 --- /dev/null +++ b/scripts/internal/qnx_creds.py @@ -0,0 +1,81 @@ +#!/usr/bin/env python3 + +# ******************************************************************************* +# Copyright (c) 2025 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# This program and the accompanying materials are made available under the +# terms of the Apache License Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0 +# +# SPDX-License-Identifier: Apache-2.0 +# ******************************************************************************* + +import http.cookiejar +import json +import netrc +import os +import sys +import urllib.parse +import urllib.request + + +def eprint(*args, **kwargs): + print(*args, file=sys.stderr, **kwargs) + + +if __name__ == "__main__": + data = json.load(sys.stdin) + + if "qnx.com" not in data["uri"]: + eprint("Unsupported domain") + sys.exit(1) + + if "SCORE_QNX_USER" in os.environ and "SCORE_QNX_PASSWORD" in os.environ: + login = os.environ["SCORE_QNX_USER"] + password = os.environ["SCORE_QNX_PASSWORD"] + else: + try: + nrc = netrc.netrc() + auth = nrc.authenticators("qnx.com") + if auth: + login, _, password = auth + else: + raise Exception("No credential found for QNX") + except Exception as excp: + eprint(excp) + eprint("Failed getting credentials from .netrc") + sys.exit(1) + + data = urllib.parse.urlencode( + {"userlogin": login, "password": password, "UseCookie": "1"} + ) + data = data.encode("ascii") + + cookie_jar = http.cookiejar.CookieJar() + cookie_processor = urllib.request.HTTPCookieProcessor(cookie_jar) + opener = urllib.request.build_opener(cookie_processor) + urllib.request.install_opener(opener) + + r = urllib.request.urlopen("https://www.qnx.com/account/login.html", data) + if r.status != 200: + eprint("Failed to login to QNX") + sys.exit(1) + + cookies = {c.name: c.value for c in list(cookie_jar)} + if not "myQNX" in cookies: + eprint("Failed to get myQNX cookie from login page") + sys.exit(1) + + myQNX = cookies["myQNX"] + print( + json.dumps( + { + "headers": { + "Cookie": [f"myQNX={myQNX}"], + } + } + ) + ) diff --git a/src/containers/inline/mod.rs b/src/containers/inline/mod.rs index a70e8b6f..46bea584 100644 --- a/src/containers/inline/mod.rs +++ b/src/containers/inline/mod.rs @@ -11,10 +11,14 @@ // SPDX-License-Identifier: Apache-2.0 // ******************************************************************************* +mod option; mod queue; +mod result; mod string; mod vec; +pub use self::option::InlineOption; pub use self::queue::InlineQueue; +pub use self::result::InlineResult; pub use self::string::InlineString; pub use self::vec::InlineVec; diff --git a/src/containers/inline/option.rs b/src/containers/inline/option.rs new file mode 100644 index 00000000..4b6ac3f3 --- /dev/null +++ b/src/containers/inline/option.rs @@ -0,0 +1,280 @@ +// ******************************************************************************* +// Copyright (c) 2025 Contributors to the Eclipse Foundation +// +// See the NOTICE file(s) distributed with this work for additional +// information regarding copyright ownership. +// +// This program and the accompanying materials are made available under the +// terms of the Apache License Version 2.0 which is available at +// https://www.apache.org/licenses/LICENSE-2.0 +// +// SPDX-License-Identifier: Apache-2.0 +// ******************************************************************************* + +use core::cmp; +use core::fmt; +use core::mem::MaybeUninit; + +/// An optional value, similar to [`Option`] in the Rust standard library. +/// +/// This data structure has a stable, well-defined memory layout and satisfies the requirements for +/// [ABI-compatible types](https://eclipse-score.github.io/score/main/features/communication/abi_compatible_data_types/index.html). +/// Unlike the built-in [`Option`] or other enum-based types, this data structure will **not** be affected by +/// [*null-pointer optimization*](https://doc.rust-lang.org/core/option/index.html#representation), +/// where invalid byte representations of `T` are used to encode the `None` case. +/// +/// **Note:** When encoding an instance of this type in other programming languages, +/// the `is_some` field must only be assigned the binary values `0x00` and `0x01`; +/// everything else results in *Undefined Behavior*. +#[derive(Clone, Copy)] +#[repr(C)] +pub struct InlineOption { + /// This is valid/initialized if and only if `is_some` is true. + value: MaybeUninit, + is_some: bool, +} + +impl InlineOption { + /// Creates a new instance populated by `value`. + pub const fn some(value: T) -> Self { + Self { + value: MaybeUninit::new(value), + is_some: true, + } + } + + /// Creates an empty instance. + pub const fn none() -> Self { + Self { + value: MaybeUninit::uninit(), + is_some: false, + } + } + + /// Creates a new instance from a standard [`Option`]. + pub fn from_option(option: Option) -> Self { + if let Some(value) = option { + Self::some(value) + } else { + Self::none() + } + } + + /// Converts this instance into a standard [`Option`]. + pub const fn into_option(self) -> Option { + if self.is_some { + // SAFETY: `is_some` is true, so `value` must be valid as per its invariant. + Some(unsafe { self.value.assume_init() }) + } else { + None + } + } + + /// Returns an optional reference to the contained value. + pub const fn as_ref(&self) -> Option<&T> { + if self.is_some { + // SAFETY: `is_some` is true, so `value` must be valid as per its invariant. + Some(unsafe { self.value.assume_init_ref() }) + } else { + None + } + } + + /// Returns an optional mutable reference to the contained value. + pub const fn as_mut(&mut self) -> Option<&mut T> { + if self.is_some { + // SAFETY: `is_some` is true, so `value` must be valid as per its invariant. + Some(unsafe { self.value.assume_init_mut() }) + } else { + None + } + } +} + +impl Default for InlineOption { + fn default() -> Self { + Self { + value: MaybeUninit::uninit(), + is_some: false, + } + } +} + +impl From for InlineOption { + fn from(value: T) -> Self { + Self { + value: MaybeUninit::new(value), + is_some: true, + } + } +} + +impl From> for InlineOption { + fn from(option: Option) -> Self { + Self::from_option(option) + } +} + +impl From> for Option { + fn from(option: InlineOption) -> Self { + option.into_option() + } +} + +impl PartialEq for InlineOption { + fn eq(&self, other: &Self) -> bool { + match (self.as_ref(), other.as_ref()) { + (Some(a), Some(b)) => a.eq(b), + (Some(_), None) => false, + (None, Some(_)) => false, + (None, None) => true, + } + } +} + +impl Eq for InlineOption {} + +impl PartialOrd for InlineOption { + fn partial_cmp(&self, other: &Self) -> Option { + match (self.as_ref(), other.as_ref()) { + (Some(a), Some(b)) => a.partial_cmp(b), + (Some(_), None) => Some(cmp::Ordering::Greater), + (None, Some(_)) => Some(cmp::Ordering::Less), + (None, None) => Some(cmp::Ordering::Equal), + } + } +} + +impl Ord for InlineOption { + fn cmp(&self, other: &Self) -> cmp::Ordering { + match (self.as_ref(), other.as_ref()) { + (Some(a), Some(b)) => a.cmp(b), + (Some(_), None) => cmp::Ordering::Greater, + (None, Some(_)) => cmp::Ordering::Less, + (None, None) => cmp::Ordering::Equal, + } + } +} + +impl fmt::Display for InlineOption +where + for<'a> Option<&'a T>: fmt::Display, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt(&self.as_ref(), f) + } +} + +impl fmt::Debug for InlineOption +where + for<'a> Option<&'a T>: fmt::Debug, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Debug::fmt(&self.as_ref(), f) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn from_and_into() { + let original_some = Some(0x1234567890abcdef_u64); + let inline_some = InlineOption::from_option(original_some); + assert_eq!(inline_some.into_option(), original_some); + + let original_none: Option = None; + let inline_none = InlineOption::from_option(original_none); + assert_eq!(inline_none.into_option(), original_none); + } + + #[test] + fn as_ref() { + let original_some = Some(0x1234567890abcdef_u64); + let inline_some = InlineOption::from_option(original_some); + assert_eq!(inline_some.as_ref(), original_some.as_ref()); + + let original_none: Option = None; + let inline_none = InlineOption::from_option(original_none); + assert_eq!(inline_none.as_ref(), original_none.as_ref()); + } + + #[test] + fn as_mut() { + let mut original_some = Some(0x1234567890abcdef_u64); + let mut inline_some = InlineOption::from_option(original_some); + assert_eq!(inline_some.as_mut(), original_some.as_mut()); + *original_some.as_mut().unwrap() = 5; + *inline_some.as_mut().unwrap() = 5; + assert_eq!(inline_some.as_mut(), original_some.as_mut()); + + let mut original_none: Option = None; + let mut inline_none = InlineOption::from_option(original_none); + assert_eq!(inline_none.as_mut(), original_none.as_mut()); + } + + #[test] + fn eq() { + #[track_caller] + fn check(lhs: &InlineOption, rhs: &InlineOption) { + // Check that InlineOption behaves exactly like Option, since Option's implementation is assumed to be correct. + assert_eq!(lhs.eq(rhs), lhs.as_ref().eq(&rhs.as_ref())); + assert_eq!(lhs.ne(rhs), lhs.as_ref().ne(&rhs.as_ref())); + } + + let some_5 = InlineOption::some(5); + let some_6 = InlineOption::some(6); + let none = InlineOption::none(); + // Check all combinations + for lhs in &[some_5, some_6, none] { + for rhs in &[some_5, some_6, none] { + check(lhs, rhs); + } + } + } + + #[test] + fn partial_cmp() { + #[track_caller] + fn check(lhs: &InlineOption, rhs: &InlineOption) { + // Check that InlineOption behaves exactly like Option, since Option's implementation is assumed to be correct. + assert_eq!(lhs.partial_cmp(rhs), lhs.as_ref().partial_cmp(&rhs.as_ref())); + assert_eq!(lhs < rhs, lhs.as_ref() < rhs.as_ref()); + assert_eq!(lhs <= rhs, lhs.as_ref() <= rhs.as_ref()); + assert_eq!(lhs > rhs, lhs.as_ref() > rhs.as_ref()); + assert_eq!(lhs >= rhs, lhs.as_ref() >= rhs.as_ref()); + } + + let some_5 = InlineOption::some(5); + let some_6 = InlineOption::some(6); + let none = InlineOption::none(); + // Check all combinations + for lhs in &[some_5, some_6, none] { + for rhs in &[some_5, some_6, none] { + check(lhs, rhs); + } + } + } + + #[test] + fn cmp() { + #[track_caller] + fn check(lhs: &InlineOption, rhs: &InlineOption) { + // Check that InlineOption behaves exactly like Option, since Option's implementation is assumed to be correct. + assert_eq!(lhs.cmp(rhs), lhs.as_ref().cmp(&rhs.as_ref())); + assert_eq!(lhs.max(rhs).as_ref(), lhs.as_ref().max(rhs.as_ref())); + assert_eq!(lhs.min(rhs).as_ref(), lhs.as_ref().min(rhs.as_ref())); + } + + let some_5 = InlineOption::some(5); + let some_6 = InlineOption::some(6); + let none = InlineOption::none(); + // Check all combinations + for lhs in &[some_5, some_6, none] { + for rhs in &[some_5, some_6, none] { + check(lhs, rhs); + } + } + } +} diff --git a/src/containers/inline/result.rs b/src/containers/inline/result.rs new file mode 100644 index 00000000..51f8da9c --- /dev/null +++ b/src/containers/inline/result.rs @@ -0,0 +1,297 @@ +// ******************************************************************************* +// Copyright (c) 2025 Contributors to the Eclipse Foundation +// +// See the NOTICE file(s) distributed with this work for additional +// information regarding copyright ownership. +// +// This program and the accompanying materials are made available under the +// terms of the Apache License Version 2.0 which is available at +// https://www.apache.org/licenses/LICENSE-2.0 +// +// SPDX-License-Identifier: Apache-2.0 +// ******************************************************************************* + +use core::cmp; +use core::fmt; +use core::mem::ManuallyDrop; + +/// An result value for error handling, similar to [`Result`] in the Rust standard library. +/// +/// This data structure has a stable, well-defined memory layout and satisfies the requirements for +/// [ABI-compatible types](https://eclipse-score.github.io/score/main/features/communication/abi_compatible_data_types/index.html). +/// +/// **Note:** When encoding an instance of this type in other programming languages, +/// the `is_ok` field must only be assigned the binary values `0x00` and `0x01`; +/// everything else results in *Undefined Behavior*. +#[derive(Clone, Copy)] +#[repr(C)] +pub struct InlineResult { + /// When `is_ok` is true, then `value.ok` is valid, otherwise `value.err`. + value: ResultUnion, + is_ok: bool, +} + +#[derive(Clone, Copy)] +#[repr(C)] +union ResultUnion { + ok: ManuallyDrop, + err: ManuallyDrop, +} + +impl InlineResult { + /// Creates a new instance representing success with `value` as payload. + pub const fn ok(value: T) -> Self { + Self { + value: ResultUnion { + ok: ManuallyDrop::new(value), + }, + is_ok: true, + } + } + + /// Creates a new instance representing failure with `error` as payload. + pub const fn err(error: E) -> Self { + Self { + value: ResultUnion { + err: ManuallyDrop::new(error), + }, + is_ok: false, + } + } + + /// Creates a new instance from a standard [`Result`]. + pub fn from_result(result: Result) -> Self { + match result { + Ok(value) => Self::ok(value), + Err(error) => Self::err(error), + } + } + + /// Converts this instance into a standard [`Result`]. + pub const fn into_result(self) -> Result { + if self.is_ok { + // SAFETY: `is_ok` is true, so `value.ok` must be valid as per its invariant. + Ok(ManuallyDrop::into_inner(unsafe { self.value.ok })) + } else { + // SAFETY: `is_ok` is false, so `value.err` must be valid as per its invariant. + Err(ManuallyDrop::into_inner(unsafe { self.value.err })) + } + } + + /// Returns a result-reference to the contained value. + pub fn as_ref(&self) -> Result<&T, &E> { + if self.is_ok { + // SAFETY: `is_ok` is true, so `value.ok` must be valid as per its invariant. + Ok(unsafe { &self.value.ok }) + } else { + // SAFETY: `is_ok` is false, so `value.err` must be valid as per its invariant. + Err(unsafe { &self.value.err }) + } + } + + /// Returns a mutable result-reference to the contained value. + pub fn as_mut(&mut self) -> Result<&mut T, &mut E> { + if self.is_ok { + // SAFETY: `is_ok` is true, so `value.ok` must be valid as per its invariant. + Ok(unsafe { &mut self.value.ok }) + } else { + // SAFETY: `is_ok` is false, so `value.err` must be valid as per its invariant. + Err(unsafe { &mut self.value.err }) + } + } +} + +impl Default for InlineResult { + fn default() -> Self { + Self { + value: ResultUnion { + ok: ManuallyDrop::new(T::default()), + }, + is_ok: true, + } + } +} + +impl From for InlineResult { + fn from(value: T) -> Self { + Self { + value: ResultUnion { + ok: ManuallyDrop::new(value), + }, + is_ok: true, + } + } +} + +impl From> for InlineResult { + fn from(result: Result) -> Self { + Self::from_result(result) + } +} + +impl From> for Result { + fn from(result: InlineResult) -> Self { + result.into_result() + } +} + +impl PartialEq for InlineResult { + fn eq(&self, other: &Self) -> bool { + match (self.as_ref(), other.as_ref()) { + (Ok(a), Ok(b)) => a.eq(b), + (Ok(_), Err(_)) => false, + (Err(_), Ok(_)) => false, + (Err(a), Err(b)) => a.eq(b), + } + } +} + +impl Eq for InlineResult {} + +impl PartialOrd for InlineResult { + fn partial_cmp(&self, other: &Self) -> Option { + match (self.as_ref(), other.as_ref()) { + (Ok(a), Ok(b)) => a.partial_cmp(b), + (Ok(_), Err(_)) => Some(cmp::Ordering::Less), + (Err(_), Ok(_)) => Some(cmp::Ordering::Greater), + (Err(a), Err(b)) => a.partial_cmp(b), + } + } +} + +impl Ord for InlineResult { + fn cmp(&self, other: &Self) -> cmp::Ordering { + match (self.as_ref(), other.as_ref()) { + (Ok(a), Ok(b)) => a.cmp(b), + (Ok(_), Err(_)) => cmp::Ordering::Less, + (Err(_), Ok(_)) => cmp::Ordering::Greater, + (Err(a), Err(b)) => a.cmp(b), + } + } +} + +impl fmt::Display for InlineResult +where + for<'a> Result<&'a T, &'a E>: fmt::Display, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt(&self.as_ref(), f) + } +} + +impl fmt::Debug for InlineResult +where + for<'a> Result<&'a T, &'a E>: fmt::Debug, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Debug::fmt(&self.as_ref(), f) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn from_and_into() { + let original_ok: Result = Ok(0x1234567890abcdef_u64); + let inline_ok = InlineResult::from_result(original_ok); + assert_eq!(inline_ok.into_result(), original_ok); + + let original_err: Result = Err(1.2345_f64); + let inline_err = InlineResult::from_result(original_err); + assert_eq!(inline_err.into_result(), original_err); + } + + #[test] + fn as_ref() { + let original_ok: Result = Ok(0x1234567890abcdef_u64); + let inline_ok = InlineResult::from_result(original_ok); + assert_eq!(inline_ok.as_ref(), original_ok.as_ref()); + + let original_err: Result = Err(1.2345_f64); + let inline_err = InlineResult::from_result(original_err); + assert_eq!(inline_err.as_ref(), original_err.as_ref()); + } + + #[test] + fn as_mut() { + let mut original_ok: Result = Ok(0x1234567890abcdef_u64); + let mut inline_ok = InlineResult::from_result(original_ok); + assert_eq!(inline_ok.as_mut(), original_ok.as_mut()); + *original_ok.as_mut().unwrap() = 5; + *inline_ok.as_mut().unwrap() = 5; + assert_eq!(inline_ok.as_mut(), original_ok.as_mut()); + + let mut original_err: Result = Err(1.2345_f64); + let mut inline_err = InlineResult::from_result(original_err); + assert_eq!(inline_err.as_mut(), original_err.as_mut()); + } + + #[test] + fn eq() { + #[track_caller] + fn check(lhs: &InlineResult, rhs: &InlineResult) { + // Check that InlineResult behaves exactly like Result, since Result's implementation is assumed to be correct. + assert_eq!(lhs.eq(rhs), lhs.as_ref().eq(&rhs.as_ref())); + assert_eq!(lhs.ne(rhs), lhs.as_ref().ne(&rhs.as_ref())); + } + + let ok_5 = InlineResult::ok(5); + let ok_6 = InlineResult::ok(6); + let err_5 = InlineResult::err(5); + let err_6 = InlineResult::err(6); + // Check all combinations + for lhs in &[ok_5, ok_6, err_5, err_6] { + for rhs in &[ok_5, ok_6, err_5, err_6] { + check(lhs, rhs); + } + } + } + + #[test] + fn partial_cmp() { + #[track_caller] + fn check(lhs: &InlineResult, rhs: &InlineResult) { + // Check that InlineResult behaves exactly like Result, since Result's implementation is assumed to be correct. + assert_eq!(lhs.partial_cmp(rhs), lhs.as_ref().partial_cmp(&rhs.as_ref())); + assert_eq!(lhs < rhs, lhs.as_ref() < rhs.as_ref()); + assert_eq!(lhs <= rhs, lhs.as_ref() <= rhs.as_ref()); + assert_eq!(lhs > rhs, lhs.as_ref() > rhs.as_ref()); + assert_eq!(lhs >= rhs, lhs.as_ref() >= rhs.as_ref()); + } + + let ok_5 = InlineResult::ok(5); + let ok_6 = InlineResult::ok(6); + let err_5 = InlineResult::err(5); + let err_6 = InlineResult::err(6); + // Check all combinations + for lhs in &[ok_5, ok_6, err_5, err_6] { + for rhs in &[ok_5, ok_6, err_5, err_6] { + check(lhs, rhs); + } + } + } + + #[test] + fn cmp() { + #[track_caller] + fn check(lhs: &InlineResult, rhs: &InlineResult) { + // Check that InlineResult behaves exactly like Result, since Result's implementation is assumed to be correct. + assert_eq!(lhs.cmp(rhs), lhs.as_ref().cmp(&rhs.as_ref())); + assert_eq!(lhs.max(rhs).as_ref(), lhs.as_ref().max(rhs.as_ref())); + assert_eq!(lhs.min(rhs).as_ref(), lhs.as_ref().min(rhs.as_ref())); + } + + let ok_5 = InlineResult::ok(5); + let ok_6 = InlineResult::ok(6); + let err_5 = InlineResult::err(5); + let err_6 = InlineResult::err(6); + // Check all combinations + for lhs in &[ok_5, ok_6, err_5, err_6] { + for rhs in &[ok_5, ok_6, err_5, err_6] { + check(lhs, rhs); + } + } + } +}