Skip to content

Commit

Permalink
Bindgen small improvements (#485)
Browse files Browse the repository at this point in the history
* fix supported SDK version bounds, add tests for the feature-mask

* better explain `DerivesMask::distance`, add more tests
  • Loading branch information
boozook authored Feb 7, 2025
1 parent 1bb67a7 commit 750a3f2
Show file tree
Hide file tree
Showing 7 changed files with 122 additions and 96 deletions.
4 changes: 2 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion support/bindgen-cfg/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "playdate-bindgen-cfg"
version = "0.2.0"
version = "0.2.1"
readme = "README.md"
description = "Minimal configuration for playdate-bindgen."
keywords = ["playdate", "bindings", "ffi", "code-generation"]
Expand Down
6 changes: 3 additions & 3 deletions support/bindgen-cfg/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -408,17 +408,17 @@ impl Filename {
if !s.starts_with(Self::PREFIX.as_bytes()) || !s.ends_with(Self::DOT_EXT.as_bytes()) {
None
} else {
let from_encoded_bytes = std::ffi::OsStr::from_encoded_bytes_unchecked;
use std::ffi::OsStr;

if let Some(target) = target {
let s = unsafe { from_encoded_bytes(&s[Self::PREFIX.len()..]) };
let s = unsafe { OsStr::from_encoded_bytes_unchecked(&s[Self::PREFIX.len()..]) };
s.to_string_lossy()
.split_once(target)
.and_then(|(version, _)| version.get(..(version.len() - 1)))
.map(OsString::from)
.map(Cow::Owned)
} else if let Some((prefix, _)) = &s[Self::PREFIX.len()..].split_once(|c| *c == b'-') {
let os = unsafe { from_encoded_bytes(prefix) };
let os = unsafe { OsStr::from_encoded_bytes_unchecked(prefix) };
Some(os.into())
} else {
None
Expand Down
153 changes: 105 additions & 48 deletions support/bindgen-cfg/src/mask.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@ use crate::Derive;

#[derive(Debug, Clone)]
pub struct DerivesMask {
inner: Vec<bool>,
values: Vec<bool>,
}

impl DerivesMask {
pub fn push(&mut self, value: bool) { self.inner.push(value) }
pub fn push(&mut self, value: bool) { self.values.push(value) }


pub fn from_ascii(mask: &[u8]) -> Result<Self, ParseMaskError> {
Expand All @@ -19,7 +19,7 @@ impl DerivesMask {
_ => return Err(ParseMaskError),
}
}
Ok(Self { inner: values })
Ok(Self { values })
}

pub fn from_str(mask: &str) -> Result<Self, ParseMaskError> {
Expand All @@ -39,16 +39,16 @@ impl Default for DerivesMask {
impl From<Derive> for DerivesMask {
fn from(values: Derive) -> Self {
// Caution: do not change the order of items!
Self { inner: vec![
values.default,
values.eq,
values.copy,
values.debug,
values.hash,
values.ord,
values.partialeq,
values.partialord,
values.constparamty,
Self { values: vec![
values.default,
values.eq,
values.copy,
values.debug,
values.hash,
values.ord,
values.partialeq,
values.partialord,
values.constparamty,
] }
}
}
Expand All @@ -59,28 +59,28 @@ impl From<&'_ Derive> for DerivesMask {

impl std::fmt::Display for DerivesMask {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let iter = self.inner.iter().map(|v| if *v { "1" } else { "0" });
let iter = self.values.iter().map(|v| if *v { "1" } else { "0" });
write!(f, "{}", iter.collect::<String>())
}
}


impl PartialEq for DerivesMask {
fn eq(&self, other: &Self) -> bool { self.inner == other.inner }
fn eq(&self, other: &Self) -> bool { self.values == other.values }
}

impl PartialOrd for DerivesMask {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
if other.inner.len() > self.inner.len() && other.inner[self.inner.len()..].contains(&true) {
if other.values.len() > self.values.len() && other.values[self.values.len()..].contains(&true) {
return Some(std::cmp::Ordering::Less);
}

let len = self.inner.len().min(other.inner.len());
let a = &self.inner[..len];
let b = &other.inner[..len];
let len = self.values.len().min(other.values.len());
let a = &self.values[..len];
let b = &other.values[..len];

let res = if a == b {
if self.inner.len() > other.inner.len() && self.inner[other.inner.len()..].contains(&true) {
if self.values.len() > other.values.len() && self.values[other.values.len()..].contains(&true) {
std::cmp::Ordering::Greater
} else {
std::cmp::Ordering::Equal
Expand All @@ -100,28 +100,40 @@ impl DerivesMask {
/// The cost of positive remainder of `other`.
const REST_DISTANCE: isize = 100;

/// Calc distance between two masks.
/// Calc "distance" between two masks.
///
/// __Non-commutative function.__
///
/// If distance between `a` & `b` is gt then `0`, that means `a` doesn't covers `b`.
/// Here the name "distance" means mesure of "how enough `a` (not) covers `b`".
///
/// Result is signed, so if "`a` covers `b`" result is `0 - n` where `n = a - b`.
/// If distance between `a` & `b` is gt then `0`, that means `a` doesn't covers `b`, so if `a > b` => `d < 0`.
///
/// Actually it's almost same as `xor` (`a ^ b`),
/// e.g. `0b1000 ^ 0b1010 = 0b10` and the "distance" is `1` which means
/// "`b` has one feature uncovered by `a`".
///
///
/// e.g. if `a > b` => `d < 0`, so
/// - `100 d 000 = -1`
/// - `100 d 111 = 2`
/// - `100 d 1111 = >2`
/// - `1001 d 111 = >2`
/// - `1001 d 111 = 2`
///
/// Various len:
/// - if `a = 10010` and `b = 100`, the remainder of `a` (10) is not contributes to the distance because b doesn't extend that far
/// and "`a` covers `b`",
/// - if `a = 100` and `b = 10010`, the remainder of `b` (10) __is__ contributes to the distance significantly.
pub fn distance(&self, other: &Self) -> isize {
if self == other {
0
} else {
let a = self.inner.as_slice();
let b = other.inner.as_slice();
let a = self.values.as_slice();
let b = other.values.as_slice();

let prefix = {
let len = self.inner.len().min(other.inner.len());
let a = &self.inner[..len];
let b = &other.inner[..len];
let len = self.values.len().min(other.values.len());
let a = &self.values[..len];
let b = &other.values[..len];
a.iter().zip(b).fold(0, |acc, (a, b)| {
acc +
match (a, b) {
Expand All @@ -138,7 +150,7 @@ impl DerivesMask {
}
// We do not take into account the remainder of `a` because we do not consider the net (real) distance,
// but the difference, meaning “by how much `a` covers `b`”.
// Otherwise it will be like that:
// Otherwise it could be like that:
// else if a[b.len()..].contains(&true) { -Self::REST_DISTANCE }
else {
0
Expand All @@ -161,20 +173,65 @@ mod tests {
use super::*;


#[test]
fn fmt() {
let mut mask = DerivesMask::default();
assert!(!mask.to_string().contains('1'));

mask.values[0] = true;
assert_eq!("100000000", &mask.to_string());

mask.values.fill(true);
assert_eq!("111111111", &mask.to_string());
}


#[test]
fn err() {
assert!(DerivesMask::from_str("123456789").is_err());
assert!(DerivesMask::from_str("-").is_err());
assert!(DerivesMask::from_str("xyz").is_err());
}

#[test]
fn from_str() {
let empty = DerivesMask::default();
let full = DerivesMask::from_str("111111111").unwrap();
assert_ne!(empty, full);
assert_eq!(empty, DerivesMask::from_str("000000000").unwrap());
assert!(DerivesMask::from_str("001000000").unwrap().values[2]);

assert!(DerivesMask::from_str("").unwrap().values.is_empty());
assert_eq!(3, DerivesMask::from_str("111").unwrap().values.len());
}

#[test]
fn from_ascii() {
let empty = DerivesMask::default();
let full = DerivesMask::from_ascii(b"111111111").unwrap();
assert_ne!(empty, full);
assert_eq!(empty, DerivesMask::from_ascii(b"000000000").unwrap());
assert!(DerivesMask::from_ascii(b"001000000").unwrap().values[2]);

assert!(DerivesMask::from_ascii(b"").unwrap().values.is_empty());
assert_eq!(3, DerivesMask::from_ascii(b"111").unwrap().values.len());
}


#[test]
fn eq() {
let mut a = DerivesMask::default();
let mut b = DerivesMask::default();
assert_eq!(a, b);

a.inner[0] = true;
a.values[0] = true;
assert_ne!(a, b);

b.inner[0] = true;
b.values[0] = true;
assert_eq!(a, b);

let last = b.inner.len() - 1;
b.inner[last] = true;
let last = b.values.len() - 1;
b.values[last] = true;
assert_ne!(a, b);
}

Expand All @@ -186,26 +243,26 @@ mod tests {
assert!(a >= b, "{a} >= {b}");
assert!(a <= b, "{a} <= {b}");

a.inner[0] = true;
a.values[0] = true;
assert!(a != b, "{a} != {b}");
assert!(a > b, "{a} > {b}");
assert!(a >= b, "{a} >= {b}");
assert!(!(a <= b), "{a} <= {b}");
assert!(b <= a, "{b} <= {a}");
assert!(b < a, "{b} < {a}");

b.inner[0] = true;
b.values[0] = true;
assert!(a == b, "{a} == {b}");
assert!(a >= b, "{a} >= {b}");
assert!(a <= b, "{a} <= {b}");

let last = b.inner.len() - 1;
b.inner[last] = true;
let last = b.values.len() - 1;
b.values[last] = true;
assert!(a != b, "{a} != {b}");
assert!(a < b, "{a} < {b}");
assert!(a <= b, "{a} <= {b}");

a.inner.fill(true);
a.values.fill(true);
assert!(a != b, "{a} != {b}");
assert!(a > b, "{a} > {b}");
assert!(a >= b, "{a} >= {b}");
Expand Down Expand Up @@ -243,32 +300,32 @@ mod tests {
assert_eq!(0, a.distance(&b));
assert_eq!(0, b.distance(&a));

a.inner[0] = true;
a.values[0] = true;
assert_eq!(-1, a.distance(&b));

b.inner[0] = true;
b.values[0] = true;
assert_eq!(0, a.distance(&b));

let last = b.inner.len() - 1;
b.inner[last] = true;
let last = b.values.len() - 1;
b.values[last] = true;
assert_eq!(1, a.distance(&b));
assert_eq!(-1, b.distance(&a));

b.push(true);
assert_eq!(TAIL + 1, a.distance(&b));

b.inner[0] = false;
b.values[0] = false;
assert_eq!(TAIL, a.distance(&b));

a.inner[last] = true;
a.values[last] = true;
assert_eq!(TAIL - 1, a.distance(&b));
a.inner[1] = true;
a.values[1] = true;
assert_eq!(TAIL - 2, a.distance(&b));


// 1001 d 111:
let a = DerivesMask { inner: vec![true, false, false, true] };
let b = DerivesMask { inner: vec![true, true, true] };
let a = DerivesMask { values: vec![true, false, false, true] };
let b = DerivesMask { values: vec![true, true, true] };
assert_eq!(2, a.distance(&b));
assert_eq!(TAIL - 2, b.distance(&a));
}
Expand Down
5 changes: 3 additions & 2 deletions support/bindgen/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
[package]
name = "playdate-bindgen"
version = "0.2.1"
version = "0.2.2"
readme = "README.md"
description = "Bindgen configuration for Playdate API and utils."
description = "Preconfigured Bindgen with extra codegen for Playdate C-API."
keywords = ["playdate", "bindings", "ffi", "code-generation"]
categories = ["development-tools::ffi", "development-tools::build-utils"]
edition.workspace = true
Expand All @@ -19,6 +19,7 @@ semver.workspace = true
html2md = { version = "0.2.15", optional = true }
html5ever = { version = "0.27.0", optional = true }
markup5ever_rcdom = { version = "0.3.0", optional = true }

# doc-gen:
proc-macro2 = { version = "1.0", optional = true }
syn = { version = "2.0", optional = true } # +full, extra-traits, visit-mut
Expand Down
31 changes: 0 additions & 31 deletions support/bindgen/src/cfg.rs

This file was deleted.

Loading

0 comments on commit 750a3f2

Please sign in to comment.