1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
//! Reference implementation of the audio-plugin-bsd C ABI contract.
//!
//! This is a `cdylib` *example* that plugin authors can copy as a starting
//! point. It exposes the five `extern "C"` symbols the host loader looks up
//! (see `audio_plugin_bsd::symbols`). It is deliberately **self-contained**:
//! it re-declares `RawPluginMetadata` with an identical `#[repr(C)]` layout
//! rather than depending on the host crate, so a real plugin can be written
//! in any language that speaks the C ABI.
//!
//! Build it with `cargo build --example test_plugin`; the resulting shared
//! object (`libtest_plugin.so` / `.dylib` / `.dll`) is consumed by the
//! `tests/load_lifecycle.rs` end-to-end test, which drives the real `dlopen`
//! path of `PluginLoader`.
//!
//! # Symbols exposed
//!
//! | Symbol | C signature | Purpose |
//! |---|---|---|
//! | `audio_plugin_abi_magic` | `extern "C" fn() -> u32` | ABI magic word (`"APLG"`). |
//! | `audio_plugin_abi_version` | `extern "C" fn() -> u32` | Encoded `(1, 0)` version. |
//! | `audio_plugin_metadata` | `extern "C" fn() -> *const RawPluginMetadata` | Static identity. |
//! | `audio_plugin_create` | `extern "C" fn() -> *mut c_void` | Allocate an opaque handle. |
//! | `audio_plugin_destroy` | `extern "C" fn(*mut c_void)` | Free an opaque handle. |
use c_void;
/// ABI magic word (`"APLG"` = `0x4150_4C47`).
///
/// Must match `audio_plugin_bsd::AUDIO_PLUGIN_ABI_MAGIC`. Re-declared here so
/// the plugin compiles with no host dependency.
const ABI_MAGIC: u32 = 0x4150_4C47;
/// ABI version `(1, 0)` — major in the high 16 bits, minor in the low 16.
const ABI_VERSION: u32 = 1u32 << 16;
/// `#[repr(C)]` metadata struct returned by `audio_plugin_metadata`.
///
/// This layout **must** match `audio_plugin_bsd::symbols::RawPluginMetadata`
/// field-for-field; the C ABI guarantees the match as long as the field order
/// and types are identical. Each string is a `(ptr, len)` pair pointing into
/// static byte storage owned by the plugin for the lifetime of the library.
// --- static identity strings ---------------------------------------------
//
// These are `static` byte slices, so their pointers remain valid for the
// entire lifetime of the loaded library — exactly the contract
// `audio_plugin_metadata` promises. The host copies the bytes out immediately
// and never retains the pointers (see `audio_plugin_bsd::symbols::raw_to_metadata`).
/// Static UTF-8 bytes for the plugin name.
static NAME: & = b"test-plugin";
/// Static UTF-8 bytes for the plugin version.
static VERSION: & = b"0.1.0";
/// Static UTF-8 bytes for the human-readable description.
static DESCRIPTION: & = b"cdylib test plugin";
/// `Sync` wrapper so the raw-pointer-bearing metadata can live in a `static`.
///
/// `RawPluginMetadata` contains `*const u8`, which is `!Sync` by default
/// (raw pointers are not auto-`Sync`). A shared `static` requires a `Sync`
/// type, so this newtype asserts the necessary invariant manually.
;
// SAFETY: `StaticMetadata` wraps a `RawPluginMetadata` whose `*const u8` fields
// point exclusively into the read-only `static` byte slices above (`NAME`,
// `VERSION`, `DESCRIPTION`), and whose remaining fields are plain integers.
// Once constructed the value is never mutated, and the pointed-to byte storage
// is immutable for the lifetime of the loaded library. Sharing it across
// threads (the host may call `audio_plugin_metadata` from any thread) is
// therefore sound.
unsafe
static METADATA: StaticMetadata = StaticMetadata;
/// Returns the ABI magic word.
///
/// The host loader compares the returned value against
/// `audio_plugin_bsd::AUDIO_PLUGIN_ABI_MAGIC` (`0x4150_4C47`).
pub extern "C"
/// Returns the encoded ABI version word (`major << 16 | minor`).
///
/// The host accepts any plugin whose **major** component matches its own;
/// see `audio_plugin_bsd::is_abi_compatible`.
pub extern "C"
/// Returns a pointer to the plugin's static metadata.
///
/// The pointer remains valid for the lifetime of the loaded library; the host
/// copies the string fields out immediately after the call and never retains
/// the pointers.
pub extern "C"
/// Allocates and returns an opaque plugin handle.
///
/// The 0.1.0 host adapter treats the handle as opaque (it is not dispatched to
/// for DSP in this milestone); we therefore allocate a small boxed sentinel so
/// that [`audio_plugin_destroy`] has something meaningful to reclaim. A plugin
/// is also free to return null — the adapter tolerates it — but returning a
/// real allocation exercises the full create/destroy lifecycle.
///
/// The returned pointer is owned by the caller and must be freed with
/// [`audio_plugin_destroy`].
pub extern "C"
/// Frees an opaque plugin handle returned by [`audio_plugin_create`].
///
/// A null pointer is a tolerated no-op (the host adapter forwards whatever
/// `create` returned verbatim, including null).
///
/// # Safety
///
/// `handle` must be null or a pointer previously returned by
/// [`audio_plugin_create`] that has not yet been freed.
pub unsafe extern "C"