Skip to content

Commit 9ee64ac

Browse files
authored
Merge pull request #41 from qorix-group/adwi_option-result
ABI-compatible Option and Result types
2 parents 5b7d963 + acba861 commit 9ee64ac

File tree

4 files changed

+662
-0
lines changed

4 files changed

+662
-0
lines changed

scripts/internal/qnx_creds.py

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
#!/usr/bin/env python3
2+
3+
# *******************************************************************************
4+
# Copyright (c) 2025 Contributors to the Eclipse Foundation
5+
#
6+
# See the NOTICE file(s) distributed with this work for additional
7+
# information regarding copyright ownership.
8+
#
9+
# This program and the accompanying materials are made available under the
10+
# terms of the Apache License Version 2.0 which is available at
11+
# https://www.apache.org/licenses/LICENSE-2.0
12+
#
13+
# SPDX-License-Identifier: Apache-2.0
14+
# *******************************************************************************
15+
16+
import http.cookiejar
17+
import json
18+
import netrc
19+
import os
20+
import sys
21+
import urllib.parse
22+
import urllib.request
23+
24+
25+
def eprint(*args, **kwargs):
26+
print(*args, file=sys.stderr, **kwargs)
27+
28+
29+
if __name__ == "__main__":
30+
data = json.load(sys.stdin)
31+
32+
if "qnx.com" not in data["uri"]:
33+
eprint("Unsupported domain")
34+
sys.exit(1)
35+
36+
if "SCORE_QNX_USER" in os.environ and "SCORE_QNX_PASSWORD" in os.environ:
37+
login = os.environ["SCORE_QNX_USER"]
38+
password = os.environ["SCORE_QNX_PASSWORD"]
39+
else:
40+
try:
41+
nrc = netrc.netrc()
42+
auth = nrc.authenticators("qnx.com")
43+
if auth:
44+
login, _, password = auth
45+
else:
46+
raise Exception("No credential found for QNX")
47+
except Exception as excp:
48+
eprint(excp)
49+
eprint("Failed getting credentials from .netrc")
50+
sys.exit(1)
51+
52+
data = urllib.parse.urlencode(
53+
{"userlogin": login, "password": password, "UseCookie": "1"}
54+
)
55+
data = data.encode("ascii")
56+
57+
cookie_jar = http.cookiejar.CookieJar()
58+
cookie_processor = urllib.request.HTTPCookieProcessor(cookie_jar)
59+
opener = urllib.request.build_opener(cookie_processor)
60+
urllib.request.install_opener(opener)
61+
62+
r = urllib.request.urlopen("https://www.qnx.com/account/login.html", data)
63+
if r.status != 200:
64+
eprint("Failed to login to QNX")
65+
sys.exit(1)
66+
67+
cookies = {c.name: c.value for c in list(cookie_jar)}
68+
if not "myQNX" in cookies:
69+
eprint("Failed to get myQNX cookie from login page")
70+
sys.exit(1)
71+
72+
myQNX = cookies["myQNX"]
73+
print(
74+
json.dumps(
75+
{
76+
"headers": {
77+
"Cookie": [f"myQNX={myQNX}"],
78+
}
79+
}
80+
)
81+
)

src/containers/inline/mod.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,14 @@
1111
// SPDX-License-Identifier: Apache-2.0
1212
// *******************************************************************************
1313

14+
mod option;
1415
mod queue;
16+
mod result;
1517
mod string;
1618
mod vec;
1719

20+
pub use self::option::InlineOption;
1821
pub use self::queue::InlineQueue;
22+
pub use self::result::InlineResult;
1923
pub use self::string::InlineString;
2024
pub use self::vec::InlineVec;

src/containers/inline/option.rs

Lines changed: 280 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,280 @@
1+
// *******************************************************************************
2+
// Copyright (c) 2025 Contributors to the Eclipse Foundation
3+
//
4+
// See the NOTICE file(s) distributed with this work for additional
5+
// information regarding copyright ownership.
6+
//
7+
// This program and the accompanying materials are made available under the
8+
// terms of the Apache License Version 2.0 which is available at
9+
// https://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// SPDX-License-Identifier: Apache-2.0
12+
// *******************************************************************************
13+
14+
use core::cmp;
15+
use core::fmt;
16+
use core::mem::MaybeUninit;
17+
18+
/// An optional value, similar to [`Option`] in the Rust standard library.
19+
///
20+
/// This data structure has a stable, well-defined memory layout and satisfies the requirements for
21+
/// [ABI-compatible types](https://eclipse-score.github.io/score/main/features/communication/abi_compatible_data_types/index.html).
22+
/// Unlike the built-in [`Option`] or other enum-based types, this data structure will **not** be affected by
23+
/// [*null-pointer optimization*](https://doc.rust-lang.org/core/option/index.html#representation),
24+
/// where invalid byte representations of `T` are used to encode the `None` case.
25+
///
26+
/// **Note:** When encoding an instance of this type in other programming languages,
27+
/// the `is_some` field must only be assigned the binary values `0x00` and `0x01`;
28+
/// everything else results in *Undefined Behavior*.
29+
#[derive(Clone, Copy)]
30+
#[repr(C)]
31+
pub struct InlineOption<T: Copy> {
32+
/// This is valid/initialized if and only if `is_some` is true.
33+
value: MaybeUninit<T>,
34+
is_some: bool,
35+
}
36+
37+
impl<T: Copy> InlineOption<T> {
38+
/// Creates a new instance populated by `value`.
39+
pub const fn some(value: T) -> Self {
40+
Self {
41+
value: MaybeUninit::new(value),
42+
is_some: true,
43+
}
44+
}
45+
46+
/// Creates an empty instance.
47+
pub const fn none() -> Self {
48+
Self {
49+
value: MaybeUninit::uninit(),
50+
is_some: false,
51+
}
52+
}
53+
54+
/// Creates a new instance from a standard [`Option`].
55+
pub fn from_option(option: Option<T>) -> Self {
56+
if let Some(value) = option {
57+
Self::some(value)
58+
} else {
59+
Self::none()
60+
}
61+
}
62+
63+
/// Converts this instance into a standard [`Option`].
64+
pub const fn into_option(self) -> Option<T> {
65+
if self.is_some {
66+
// SAFETY: `is_some` is true, so `value` must be valid as per its invariant.
67+
Some(unsafe { self.value.assume_init() })
68+
} else {
69+
None
70+
}
71+
}
72+
73+
/// Returns an optional reference to the contained value.
74+
pub const fn as_ref(&self) -> Option<&T> {
75+
if self.is_some {
76+
// SAFETY: `is_some` is true, so `value` must be valid as per its invariant.
77+
Some(unsafe { self.value.assume_init_ref() })
78+
} else {
79+
None
80+
}
81+
}
82+
83+
/// Returns an optional mutable reference to the contained value.
84+
pub const fn as_mut(&mut self) -> Option<&mut T> {
85+
if self.is_some {
86+
// SAFETY: `is_some` is true, so `value` must be valid as per its invariant.
87+
Some(unsafe { self.value.assume_init_mut() })
88+
} else {
89+
None
90+
}
91+
}
92+
}
93+
94+
impl<T: Copy> Default for InlineOption<T> {
95+
fn default() -> Self {
96+
Self {
97+
value: MaybeUninit::uninit(),
98+
is_some: false,
99+
}
100+
}
101+
}
102+
103+
impl<T: Copy> From<T> for InlineOption<T> {
104+
fn from(value: T) -> Self {
105+
Self {
106+
value: MaybeUninit::new(value),
107+
is_some: true,
108+
}
109+
}
110+
}
111+
112+
impl<T: Copy> From<Option<T>> for InlineOption<T> {
113+
fn from(option: Option<T>) -> Self {
114+
Self::from_option(option)
115+
}
116+
}
117+
118+
impl<T: Copy> From<InlineOption<T>> for Option<T> {
119+
fn from(option: InlineOption<T>) -> Self {
120+
option.into_option()
121+
}
122+
}
123+
124+
impl<T: PartialEq + Copy> PartialEq for InlineOption<T> {
125+
fn eq(&self, other: &Self) -> bool {
126+
match (self.as_ref(), other.as_ref()) {
127+
(Some(a), Some(b)) => a.eq(b),
128+
(Some(_), None) => false,
129+
(None, Some(_)) => false,
130+
(None, None) => true,
131+
}
132+
}
133+
}
134+
135+
impl<T: Eq + Copy> Eq for InlineOption<T> {}
136+
137+
impl<T: PartialOrd + Copy> PartialOrd for InlineOption<T> {
138+
fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
139+
match (self.as_ref(), other.as_ref()) {
140+
(Some(a), Some(b)) => a.partial_cmp(b),
141+
(Some(_), None) => Some(cmp::Ordering::Greater),
142+
(None, Some(_)) => Some(cmp::Ordering::Less),
143+
(None, None) => Some(cmp::Ordering::Equal),
144+
}
145+
}
146+
}
147+
148+
impl<T: Ord + Copy> Ord for InlineOption<T> {
149+
fn cmp(&self, other: &Self) -> cmp::Ordering {
150+
match (self.as_ref(), other.as_ref()) {
151+
(Some(a), Some(b)) => a.cmp(b),
152+
(Some(_), None) => cmp::Ordering::Greater,
153+
(None, Some(_)) => cmp::Ordering::Less,
154+
(None, None) => cmp::Ordering::Equal,
155+
}
156+
}
157+
}
158+
159+
impl<T: Copy> fmt::Display for InlineOption<T>
160+
where
161+
for<'a> Option<&'a T>: fmt::Display,
162+
{
163+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
164+
fmt::Display::fmt(&self.as_ref(), f)
165+
}
166+
}
167+
168+
impl<T: Copy> fmt::Debug for InlineOption<T>
169+
where
170+
for<'a> Option<&'a T>: fmt::Debug,
171+
{
172+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
173+
fmt::Debug::fmt(&self.as_ref(), f)
174+
}
175+
}
176+
177+
#[cfg(test)]
178+
mod tests {
179+
use super::*;
180+
181+
#[test]
182+
fn from_and_into() {
183+
let original_some = Some(0x1234567890abcdef_u64);
184+
let inline_some = InlineOption::from_option(original_some);
185+
assert_eq!(inline_some.into_option(), original_some);
186+
187+
let original_none: Option<u64> = None;
188+
let inline_none = InlineOption::from_option(original_none);
189+
assert_eq!(inline_none.into_option(), original_none);
190+
}
191+
192+
#[test]
193+
fn as_ref() {
194+
let original_some = Some(0x1234567890abcdef_u64);
195+
let inline_some = InlineOption::from_option(original_some);
196+
assert_eq!(inline_some.as_ref(), original_some.as_ref());
197+
198+
let original_none: Option<u64> = None;
199+
let inline_none = InlineOption::from_option(original_none);
200+
assert_eq!(inline_none.as_ref(), original_none.as_ref());
201+
}
202+
203+
#[test]
204+
fn as_mut() {
205+
let mut original_some = Some(0x1234567890abcdef_u64);
206+
let mut inline_some = InlineOption::from_option(original_some);
207+
assert_eq!(inline_some.as_mut(), original_some.as_mut());
208+
*original_some.as_mut().unwrap() = 5;
209+
*inline_some.as_mut().unwrap() = 5;
210+
assert_eq!(inline_some.as_mut(), original_some.as_mut());
211+
212+
let mut original_none: Option<u64> = None;
213+
let mut inline_none = InlineOption::from_option(original_none);
214+
assert_eq!(inline_none.as_mut(), original_none.as_mut());
215+
}
216+
217+
#[test]
218+
fn eq() {
219+
#[track_caller]
220+
fn check(lhs: &InlineOption<i32>, rhs: &InlineOption<i32>) {
221+
// Check that InlineOption behaves exactly like Option, since Option's implementation is assumed to be correct.
222+
assert_eq!(lhs.eq(rhs), lhs.as_ref().eq(&rhs.as_ref()));
223+
assert_eq!(lhs.ne(rhs), lhs.as_ref().ne(&rhs.as_ref()));
224+
}
225+
226+
let some_5 = InlineOption::some(5);
227+
let some_6 = InlineOption::some(6);
228+
let none = InlineOption::none();
229+
// Check all combinations
230+
for lhs in &[some_5, some_6, none] {
231+
for rhs in &[some_5, some_6, none] {
232+
check(lhs, rhs);
233+
}
234+
}
235+
}
236+
237+
#[test]
238+
fn partial_cmp() {
239+
#[track_caller]
240+
fn check(lhs: &InlineOption<i32>, rhs: &InlineOption<i32>) {
241+
// Check that InlineOption behaves exactly like Option, since Option's implementation is assumed to be correct.
242+
assert_eq!(lhs.partial_cmp(rhs), lhs.as_ref().partial_cmp(&rhs.as_ref()));
243+
assert_eq!(lhs < rhs, lhs.as_ref() < rhs.as_ref());
244+
assert_eq!(lhs <= rhs, lhs.as_ref() <= rhs.as_ref());
245+
assert_eq!(lhs > rhs, lhs.as_ref() > rhs.as_ref());
246+
assert_eq!(lhs >= rhs, lhs.as_ref() >= rhs.as_ref());
247+
}
248+
249+
let some_5 = InlineOption::some(5);
250+
let some_6 = InlineOption::some(6);
251+
let none = InlineOption::none();
252+
// Check all combinations
253+
for lhs in &[some_5, some_6, none] {
254+
for rhs in &[some_5, some_6, none] {
255+
check(lhs, rhs);
256+
}
257+
}
258+
}
259+
260+
#[test]
261+
fn cmp() {
262+
#[track_caller]
263+
fn check(lhs: &InlineOption<i32>, rhs: &InlineOption<i32>) {
264+
// Check that InlineOption behaves exactly like Option, since Option's implementation is assumed to be correct.
265+
assert_eq!(lhs.cmp(rhs), lhs.as_ref().cmp(&rhs.as_ref()));
266+
assert_eq!(lhs.max(rhs).as_ref(), lhs.as_ref().max(rhs.as_ref()));
267+
assert_eq!(lhs.min(rhs).as_ref(), lhs.as_ref().min(rhs.as_ref()));
268+
}
269+
270+
let some_5 = InlineOption::some(5);
271+
let some_6 = InlineOption::some(6);
272+
let none = InlineOption::none();
273+
// Check all combinations
274+
for lhs in &[some_5, some_6, none] {
275+
for rhs in &[some_5, some_6, none] {
276+
check(lhs, rhs);
277+
}
278+
}
279+
}
280+
}

0 commit comments

Comments
 (0)