Skip to content

Commit 5e0feee

Browse files
committed
feat(s2n-events): Support defining associated C types for events
1 parent 9b89de7 commit 5e0feee

File tree

7 files changed

+340
-6
lines changed

7 files changed

+340
-6
lines changed

tools/s2n-events/src/generate_config.rs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -333,6 +333,37 @@ impl GenerateConfig {
333333
OutputMode::Mut => quote!(),
334334
}
335335
}
336+
337+
pub fn c_ffi(
338+
&self,
339+
inner: &TokenStream,
340+
connection_meta_c_type: &TokenStream,
341+
connection_info_c_type: &TokenStream,
342+
) -> TokenStream {
343+
if !self.c_api {
344+
return quote!();
345+
}
346+
347+
assert!(
348+
!connection_meta_c_type.is_empty(),
349+
"An associated C type must be specified for ConnectionMeta with the #[c_type()] \
350+
attribute."
351+
);
352+
assert!(
353+
!connection_info_c_type.is_empty(),
354+
"An associated C type must be specified for ConnectionInfo with the #[c_type()] \
355+
attribute."
356+
);
357+
358+
quote!(
359+
pub mod c_ffi {
360+
#[allow(unused_imports)]
361+
use std::ffi::*;
362+
363+
#inner
364+
}
365+
)
366+
}
336367
}
337368

338369
impl ToTokens for OutputMode {

tools/s2n-events/src/output.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,9 @@ pub struct Output {
3636
pub s2n_quic_core_path: TokenStream,
3737
pub top_level: TokenStream,
3838
pub feature_alloc: TokenStream,
39+
pub c_ffi: TokenStream,
40+
pub connection_meta_c_type: TokenStream,
41+
pub connection_info_c_type: TokenStream,
3942
pub root: PathBuf,
4043
}
4144

@@ -115,6 +118,9 @@ impl ToTokens for Output {
115118
top_level,
116119
feature_alloc: _,
117120
crate_name,
121+
c_ffi,
122+
connection_meta_c_type,
123+
connection_info_c_type,
118124
root: _,
119125
} = self;
120126

@@ -129,6 +135,9 @@ impl ToTokens for Output {
129135
let query_mut = self.config.query_mut();
130136
let query_mut_tuple = self.config.query_mut_tuple();
131137
let trait_constraints = self.config.trait_constraints();
138+
let c_ffi = self
139+
.config
140+
.c_ffi(c_ffi, connection_meta_c_type, connection_info_c_type);
132141

133142
let ref_subscriber = self.config.ref_subscriber(quote!(
134143
type ConnectionContext = T::ConnectionContext;
@@ -462,6 +471,8 @@ impl ToTokens for Output {
462471
}
463472
}
464473

474+
#c_ffi
475+
465476
#[cfg(any(test, feature = "testing"))]
466477
pub mod testing {
467478
use super::*;

tools/s2n-events/src/parser.rs

Lines changed: 90 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,34 @@ impl Struct {
9797
}
9898

9999
fn to_tokens(&self, output: &mut Output) {
100+
match self.attrs.c_definition {
101+
true => self.to_tokens_c_definition(output),
102+
false => self.to_tokens_rust_definition(output),
103+
}
104+
}
105+
106+
fn to_tokens_c_definition(&self, output: &mut Output) {
107+
assert!(
108+
self.attrs.event_name.is_none(),
109+
"C struct definitions cannot be directly used as events."
110+
);
111+
112+
let ident = &self.ident;
113+
let extra_attrs = &self.attrs.extra;
114+
let c_definition_attrs = &self.attrs.c_definition_attrs;
115+
let builder_fields = self.fields.iter().map(Field::builder);
116+
117+
output.c_ffi.extend(quote!(
118+
#c_definition_attrs
119+
#[derive(Clone, Debug)]
120+
#extra_attrs
121+
pub struct #ident {
122+
#(#builder_fields)*
123+
}
124+
));
125+
}
126+
127+
fn to_tokens_rust_definition(&self, output: &mut Output) {
100128
let Self {
101129
attrs,
102130
ident,
@@ -173,6 +201,12 @@ impl Struct {
173201
}
174202
));
175203

204+
if ident_str == "ConnectionMeta" {
205+
output.connection_meta_c_type = attrs.associated_c_type.clone();
206+
} else if ident_str == "ConnectionInfo" {
207+
output.connection_info_c_type = attrs.associated_c_type.clone();
208+
}
209+
176210
if let Some(event_name) = attrs.event_name.as_ref() {
177211
output.api.extend(quote!(
178212
#allow_deprecated
@@ -296,6 +330,12 @@ impl Struct {
296330
self.output #lock.push(out);
297331
}
298332
));
333+
334+
assert!(
335+
attrs.associated_c_type.is_empty(),
336+
"C types cannot be associated with endpoint events. Publishing endpoint \
337+
events from the C API is not yet supported."
338+
);
299339
}
300340
Subject::Connection => {
301341
output.subscriber.extend(quote!(
@@ -405,6 +445,15 @@ impl Struct {
405445
}
406446
}
407447
));
448+
449+
if output.config.c_api {
450+
let c_type = &attrs.associated_c_type;
451+
assert!(
452+
!c_type.is_empty(),
453+
"Events must specify an associated C type with the #[c_type()] \
454+
attribute.",
455+
);
456+
}
408457
}
409458
}
410459
}
@@ -432,18 +481,41 @@ impl Enum {
432481
}
433482

434483
fn to_tokens(&self, output: &mut Output) {
484+
assert!(
485+
self.attrs.event_name.is_none(),
486+
"enum events are not currently supported"
487+
);
488+
489+
match self.attrs.c_definition {
490+
true => self.to_tokens_c_definition(output),
491+
false => self.to_tokens_rust_definition(output),
492+
}
493+
}
494+
495+
fn to_tokens_c_definition(&self, output: &mut Output) {
496+
let ident = &self.ident;
497+
let extra_attrs = &self.attrs.extra;
498+
let c_definition_attrs = &self.attrs.c_definition_attrs;
499+
let builder_fields = self.variants.iter().map(Variant::builder);
500+
501+
output.c_ffi.extend(quote!(
502+
#c_definition_attrs
503+
#[derive(Clone, Debug)]
504+
#extra_attrs
505+
pub enum #ident {
506+
#(#builder_fields)*
507+
}
508+
));
509+
}
510+
511+
fn to_tokens_rust_definition(&self, output: &mut Output) {
435512
let Self {
436513
attrs,
437514
ident,
438515
generics,
439516
variants,
440517
} = self;
441518

442-
assert!(
443-
attrs.event_name.is_none(),
444-
"enum events are not currently supported"
445-
);
446-
447519
let derive_attrs = &attrs.derive_attrs;
448520
let builder_derive_attrs = &attrs.builder_derive_attrs;
449521
let extra_attrs = &attrs.extra;
@@ -543,6 +615,9 @@ pub struct ContainerAttrs {
543615
pub checkpoint: Vec<Checkpoint>,
544616
pub measure_counter: Vec<Metric>,
545617
pub extra: TokenStream,
618+
pub associated_c_type: TokenStream,
619+
pub c_definition: bool,
620+
pub c_definition_attrs: TokenStream,
546621
}
547622

548623
impl ContainerAttrs {
@@ -563,6 +638,9 @@ impl ContainerAttrs {
563638
checkpoint: vec![],
564639
measure_counter: vec![],
565640
extra: quote!(),
641+
associated_c_type: quote!(),
642+
c_definition: false,
643+
c_definition_attrs: TokenStream::default(),
566644
};
567645

568646
for attr in attrs {
@@ -591,6 +669,13 @@ impl ContainerAttrs {
591669
v.checkpoint.push(attr.parse_args().unwrap());
592670
} else if path.is_ident("measure_counter") {
593671
v.measure_counter.push(attr.parse_args().unwrap());
672+
} else if path.is_ident("c_type") {
673+
v.associated_c_type = attr.parse_args().unwrap();
674+
} else if path.is_ident("repr") {
675+
// Structs/enums with the #[repr(...)] attribute are assumed to be defined for the
676+
// C API.
677+
v.c_definition = true;
678+
attr.to_tokens(&mut v.c_definition_attrs);
594679
} else {
595680
attr.to_tokens(&mut v.extra)
596681
}

tools/s2n-events/tests/c_ffi_events/event/generated.rs

Lines changed: 71 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,42 @@ pub mod api {
155155
impl Event for CountEvent {
156156
const NAME: &'static str = "count_event";
157157
}
158+
impl IntoEvent<builder::ConnectionMeta> for &c_ffi::s2n_event_connection_meta {
159+
fn into_event(self) -> builder::ConnectionMeta {
160+
let duration = Duration::from_nanos(self.timestamp);
161+
let timestamp =
162+
unsafe { s2n_quic_core::time::Timestamp::from_duration(duration).into_event() };
163+
builder::ConnectionMeta { id: 0, timestamp }
164+
}
165+
}
166+
impl IntoEvent<builder::ConnectionInfo> for &c_ffi::s2n_event_connection_info {
167+
fn into_event(self) -> builder::ConnectionInfo {
168+
builder::ConnectionInfo {}
169+
}
170+
}
171+
impl<'a> IntoEvent<builder::ByteArrayEvent<'a>> for &c_ffi::s2n_byte_array_event {
172+
fn into_event(self) -> builder::ByteArrayEvent<'a> {
173+
let data =
174+
unsafe { std::slice::from_raw_parts(self.data, self.len.try_into().unwrap()) };
175+
builder::ByteArrayEvent { data }
176+
}
177+
}
178+
impl IntoEvent<builder::TestEnum> for c_ffi::s2n_test_enum {
179+
fn into_event(self) -> builder::TestEnum {
180+
match self {
181+
Self::S2N_TEST_VALUE_1 => builder::TestEnum::TestValue1,
182+
Self::S2N_TEST_VALUE_2 => builder::TestEnum::TestValue2,
183+
}
184+
}
185+
}
186+
impl IntoEvent<builder::EnumEvent> for &c_ffi::s2n_enum_event {
187+
fn into_event(self) -> builder::EnumEvent {
188+
let value = self.value.clone();
189+
builder::EnumEvent {
190+
value: value.into_event(),
191+
}
192+
}
193+
}
158194
}
159195
pub mod tracing {
160196
#![doc = r" This module contains event integration with [`tracing`](https://docs.rs/tracing)"]
@@ -293,7 +329,7 @@ pub mod builder {
293329
}
294330
}
295331
}
296-
#[derive(Clone, Debug)]
332+
#[derive(PartialEq, Clone, Debug)]
297333
pub enum TestEnum {
298334
TestValue1,
299335
TestValue2,
@@ -638,6 +674,40 @@ mod traits {
638674
}
639675
}
640676
}
677+
pub mod c_ffi {
678+
#[allow(unused_imports)]
679+
use std::ffi::*;
680+
#[repr(C)]
681+
#[derive(Clone, Debug)]
682+
#[allow(non_camel_case_types)]
683+
pub struct s2n_event_connection_meta {
684+
pub timestamp: u64,
685+
}
686+
#[repr(C)]
687+
#[derive(Clone, Debug)]
688+
#[allow(non_camel_case_types)]
689+
pub struct s2n_event_connection_info {}
690+
#[repr(C)]
691+
#[derive(Clone, Debug)]
692+
#[allow(non_camel_case_types)]
693+
pub struct s2n_byte_array_event {
694+
pub data: *const u8,
695+
pub len: u32,
696+
}
697+
#[repr(C)]
698+
#[derive(Clone, Debug)]
699+
#[allow(non_camel_case_types)]
700+
pub struct s2n_enum_event {
701+
pub value: s2n_test_enum,
702+
}
703+
#[repr(C)]
704+
#[derive(Clone, Debug)]
705+
#[allow(non_camel_case_types)]
706+
pub enum s2n_test_enum {
707+
S2N_TEST_VALUE_1,
708+
S2N_TEST_VALUE_2,
709+
}
710+
}
641711
#[cfg(any(test, feature = "testing"))]
642712
pub mod testing {
643713
use super::*;

tools/s2n-events/tests/c_ffi_events/events/common.rs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,42 @@ enum Subject {
88
},
99
}
1010

11+
#[c_type(s2n_event_connection_meta)]
1112
struct ConnectionMeta {
1213
id: u64,
1314
timestamp: Timestamp,
1415
}
1516

17+
#[repr(C)]
18+
#[allow(non_camel_case_types)]
19+
struct s2n_event_connection_meta {
20+
timestamp: u64,
21+
}
22+
23+
impl IntoEvent<builder::ConnectionMeta> for &c_ffi::s2n_event_connection_meta {
24+
fn into_event(self) -> builder::ConnectionMeta {
25+
let duration = Duration::from_nanos(self.timestamp);
26+
let timestamp = unsafe {
27+
s2n_quic_core::time::Timestamp::from_duration(duration).into_event()
28+
};
29+
builder::ConnectionMeta {
30+
id: 0,
31+
timestamp,
32+
}
33+
}
34+
}
35+
1636
struct EndpointMeta {}
1737

38+
#[c_type(s2n_event_connection_info)]
1839
struct ConnectionInfo {}
40+
41+
#[repr(C)]
42+
#[allow(non_camel_case_types)]
43+
struct s2n_event_connection_info {}
44+
45+
impl IntoEvent<builder::ConnectionInfo> for &c_ffi::s2n_event_connection_info {
46+
fn into_event(self) -> builder::ConnectionInfo {
47+
builder::ConnectionInfo {}
48+
}
49+
}

0 commit comments

Comments
 (0)