diff --git a/Cargo.toml b/Cargo.toml index 12f86fb..a9e36f6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,5 +10,7 @@ members = [ "u144", "u160", "publish-ws", - "yagen" + "yagen", + "xpcom", + "xpcom/tool" ] diff --git a/publish-ws/Cargo.toml b/publish-ws/Cargo.toml index a2184b6..7f8afb2 100644 --- a/publish-ws/Cargo.toml +++ b/publish-ws/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "publish-ws" -version = "0.1.9" +version = "0.1.10" authors = ["natfoam"] edition = "2021" description = "Publish Workspace Packages" @@ -11,5 +11,5 @@ repository = "https://github.com/natfoam/lib" [dependencies] toml = "0.5.9" -serde = "1.0.137" -serde_derive = "1.0.137" \ No newline at end of file +serde = "1.0.138" +serde_derive = "1.0.138" \ No newline at end of file diff --git a/xpcom/Cargo.toml b/xpcom/Cargo.toml new file mode 100644 index 0000000..62e427b --- /dev/null +++ b/xpcom/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "xpcom" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/xpcom/src/lib.rs b/xpcom/src/lib.rs new file mode 100644 index 0000000..e1626ef --- /dev/null +++ b/xpcom/src/lib.rs @@ -0,0 +1,130 @@ +use std::{ + ptr::null_mut, + sync::atomic::{AtomicU32, Ordering}, +}; + +type HRESULT = u32; + +const S_OK: HRESULT = 0; +const E_NOINTERFACE: HRESULT = 0x80004002; +const E_POINTER: HRESULT = 0x80004003; + +type GUID = u128; + +type ULONG = u32; + +// + +pub trait Interface: 'static { + const ID: GUID; +} + +#[repr(transparent)] +pub struct Obj(&'static Vmt, I>); + +#[allow(non_snake_case)] +#[repr(C)] +struct Vmt { + QueryInterface: + extern "stdcall" fn(this: &mut T, riid: &GUID, ppvObject: *mut *mut T) -> HRESULT, + AddRef: extern "stdcall" fn(this: &mut T) -> ULONG, + Release: extern "stdcall" fn(this: &mut T) -> ULONG, + interface: I, +} + +#[repr(transparent)] +pub struct Ref(*mut Obj); + +impl Ref { + pub fn raw(&self) -> &mut Obj { + unsafe { &mut *self.0 } + } + pub fn new(raw: &mut Obj) -> Self { + (raw.0.AddRef)(raw); + Self(raw) + } +} + +impl Drop for Ref { + fn drop(&mut self) { + let raw = self.raw(); + (raw.0.Release)(raw); + } +} + +impl Clone for Ref { + fn clone(&self) -> Self { + Self::new(self.raw()) + } +} + +// + +pub trait Class: 'static + Sized { + type Interface: Interface; + const INTERFACE: Self::Interface; +} + +#[repr(C)] +struct ClassObj { + vmt: Box>, + counter: AtomicU32, + value: C, +} + +impl ClassObj { + fn new(value: C) -> Box { + Box::new(ClassObj { + vmt: Box::new(Vmt { + QueryInterface: ClassObj::QueryInterface, + AddRef: ClassObj::AddRef, + Release: ClassObj::Release, + interface: C::INTERFACE, + }), + counter: AtomicU32::default(), + value, + }) + } + extern "stdcall" fn QueryInterface( + &mut self, + riid: &GUID, + ppvObject: *mut *mut Self, + ) -> HRESULT { + if ppvObject == null_mut() { + return E_POINTER; + } + let result: (*mut Self, HRESULT) = if C::Interface::ID == *riid { + self.AddRef(); + (self, S_OK) + } else { + (null_mut(), E_NOINTERFACE) + }; + unsafe { *ppvObject = result.0 } + result.1 + } + extern "stdcall" fn AddRef(&mut self) -> ULONG { + self.counter.fetch_add(1, Ordering::Relaxed) + 1 + } + extern "stdcall" fn Release(&mut self) -> ULONG { + match self.counter.fetch_sub(1, Ordering::Relaxed) { + 0 => panic!("release"), + 1 => { + unsafe { Box::from_raw(self) }; + 0 + } + c => c - 1, + } + } +} + +pub fn new(value: C) -> Ref { + let i = Box::into_raw(ClassObj::new(value)) as *mut Obj; + Ref::new(unsafe { &mut *i }) +} + +#[cfg(test)] +mod tests { + + #[test] + fn it_works() {} +} diff --git a/xpcom/tool/Cargo.toml b/xpcom/tool/Cargo.toml new file mode 100644 index 0000000..4af233f --- /dev/null +++ b/xpcom/tool/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "xpcom-tool" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +serde = "1.0.138" +serde_derive = "1.0.138" +serde_json = "1.0.82" +serde_yaml = "0.8.24" diff --git a/xpcom/tool/src/guid.rs b/xpcom/tool/src/guid.rs new file mode 100644 index 0000000..52c4ef3 --- /dev/null +++ b/xpcom/tool/src/guid.rs @@ -0,0 +1,46 @@ +use serde::{ + de::{Error, Visitor}, + Deserialize, Serialize, +}; + +pub struct Guid(pub u128); + +impl Serialize for Guid { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + serializer.serialize_str(format!("{:X}", self.0).as_str()) + } +} + +struct StringVisitor; + +impl<'de> Visitor<'de> for StringVisitor { + type Value = String; + + fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { + formatter.write_str("a string GUID") + } + + fn visit_string(self, v: String) -> Result + where + E: Error, + { + Ok(v) + } +} + +impl<'de> Deserialize<'de> for Guid { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + let s = deserializer.deserialize_string(StringVisitor)?; + let x = u128::from_str_radix(s.as_str(), 16); + match x { + Ok(v) => Ok(Guid(v)), + Err(e) => Err(D::Error::custom(e)), + } + } +} diff --git a/xpcom/tool/src/main.rs b/xpcom/tool/src/main.rs new file mode 100644 index 0000000..2000c22 --- /dev/null +++ b/xpcom/tool/src/main.rs @@ -0,0 +1,19 @@ +mod guid; +mod types; + +use crate::types::{interface, method, param, ptr, struct_, Library, Type}; + +fn main() { + let library = Library::from([ + struct_("S", &[param("begin", Type::U8)]), + interface( + "IMy", + 0xFFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF, + &[method("GetValue", &[], ptr(Type::U32))], + ), + ]); + + // let x = serde_json::to_string(&library).unwrap(); + let x = serde_yaml::to_string(&library).unwrap(); + println!("{}", x); +} diff --git a/xpcom/tool/src/types/mod.rs b/xpcom/tool/src/types/mod.rs new file mode 100644 index 0000000..d9c2312 --- /dev/null +++ b/xpcom/tool/src/types/mod.rs @@ -0,0 +1,79 @@ +use std::collections::HashMap; + +use serde_derive::{Deserialize, Serialize}; + +use crate::guid::Guid; + +#[derive(Serialize, Deserialize, Clone)] +pub enum Type { + I8, + U8, + I16, + U16, + I32, + U32, + I64, + U64, + Ptr(Box), + Id(String), +} + +#[derive(Serialize, Deserialize, Clone)] +pub struct Param { + name: String, + r#type: Type, +} + +#[derive(Serialize, Deserialize, Clone)] +pub struct Method { + name: String, + params: Vec, + result: Type, +} + +type Struct = Vec; + +#[derive(Serialize, Deserialize)] +pub enum TypeDef { + Struct(Struct), + Interface { guid: Guid, methods: Vec }, +} + +fn type_def(name: &str, def: TypeDef) -> (String, TypeDef) { + (name.to_string(), def) +} + +pub fn struct_(name: &str, params: &[Param]) -> (String, TypeDef) { + type_def(name, TypeDef::Struct(params.to_vec())) +} + +pub fn interface(name: &str, guid: u128, methods: &[Method]) -> (String, TypeDef) { + type_def( + name, + TypeDef::Interface { + guid: Guid(guid), + methods: methods.to_vec(), + }, + ) +} + +pub fn method(name: &str, params: &[Param], result: Type) -> Method { + Method { + name: name.to_string(), + params: params.to_vec(), + result, + } +} + +pub fn ptr(type_: Type) -> Type { + Type::Ptr(Box::new(type_)) +} + +pub fn param(name: &str, type_: Type) -> Param { + Param { + name: name.to_string(), + r#type: type_, + } +} + +pub type Library = HashMap;