0
0
mirror of https://github.com/signalapp/libsignal.git synced 2024-09-19 19:42:19 +02:00

Determine ResultType from bridged fn signature

Examine the Rust signature to determine what the FFI output type should be. 
This lets us remove all usages of #[bridge_fn_void] since #[bridge_fn] now 
correctly detects the return type.
This commit is contained in:
Alex Konradi 2024-03-26 15:56:53 -04:00 committed by GitHub
parent acf64e6aa1
commit 08513b208c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 156 additions and 87 deletions

View File

@ -179,11 +179,49 @@ fn name_for_meta_key(
}
#[derive(Clone, Copy)]
#[cfg_attr(test, derive(Debug, PartialEq))]
enum ResultKind {
Regular,
Void,
}
impl From<&syn_mid::Signature> for ResultKind {
fn from(value: &syn_mid::Signature) -> Self {
let type_ = match &value.output {
ReturnType::Default => return Self::Void,
ReturnType::Type(_, type_) => type_.as_ref(),
};
let output_type = match &type_ {
syn::Type::Path(path) if path.qself.is_none() => &path.path,
syn::Type::Tuple(t) if t.elems.is_empty() => return ResultKind::Void,
_ => return ResultKind::Regular,
};
let is_void_result = |segment: &syn::PathSegment| {
if segment.ident != "Result" {
return false;
}
let PathArguments::AngleBracketed(args) = &segment.arguments else {
return false;
};
args.args.first().is_some_and(|arg| match arg {
GenericArgument::Type(syn::Type::Tuple(t)) => t.elems.is_empty(),
_ => false,
})
};
let last_segment = output_type.segments.last();
if last_segment.is_some_and(is_void_result) {
return ResultKind::Void;
}
ResultKind::Regular
}
}
enum BridgingKind<T = Type> {
Regular,
Io { runtime: T },
@ -225,7 +263,6 @@ impl Parse for BridgeIoParams {
fn bridge_fn_impl(
attr: TokenStream,
item: TokenStream,
result_kind: ResultKind,
bridging_kind: BridgingKind<()>,
) -> TokenStream {
let function = parse_macro_input!(item as ItemFn);
@ -245,6 +282,7 @@ fn bridge_fn_impl(
)
}
};
let result_kind = ResultKind::from(&function.sig);
let ffi_name = match name_for_meta_key(&item_names, "ffi", || {
ffi::name_from_ident(&function.sig.ident)
@ -319,41 +357,12 @@ fn bridge_fn_impl(
/// ```
#[proc_macro_attribute]
pub fn bridge_fn(attr: TokenStream, item: TokenStream) -> TokenStream {
bridge_fn_impl(attr, item, ResultKind::Regular, BridgingKind::Regular)
}
/// Generates C, Java, and Node entry points for a Rust function that returns `Result<(), _>`.
///
/// Because the C bindings conventions use out-parameters for successful return values,
/// a case of "no result on success" must be annotated specially.
///
/// See the [crate-level documentation](crate) for more information.
///
/// # Example
///
/// ```ignore
/// // Produces a C function manually named "signal_process_postkey"
/// // and a JNI function named "PostKey_1Process" (with JNI "_1" mangling for an underscore),
/// // with the Node entry point disabled.
/// # #[cfg(ignore_even_when_running_all_tests)]
/// #[bridge_fn_void(ffi = "process_postkey", node = false)]
/// fn PostKey_Process(post_key: &PostKey) -> Result<()> {
/// // ...
/// }
/// ```
#[proc_macro_attribute]
pub fn bridge_fn_void(attr: TokenStream, item: TokenStream) -> TokenStream {
bridge_fn_impl(attr, item, ResultKind::Void, BridgingKind::Regular)
bridge_fn_impl(attr, item, BridgingKind::Regular)
}
#[proc_macro_attribute]
pub fn bridge_io(attr: TokenStream, item: TokenStream) -> TokenStream {
bridge_fn_impl(
attr,
item,
ResultKind::Regular,
BridgingKind::Io { runtime: () },
)
bridge_fn_impl(attr, item, BridgingKind::Io { runtime: () })
}
#[cfg(test)]
@ -402,3 +411,63 @@ mod bridge_io_params_tests {
);
}
}
#[cfg(test)]
mod return_type_test {
use super::*;
#[test]
fn implicit() {
let parsed: ItemFn = parse_quote! {
fn no_return() {}
};
assert_eq!(ResultKind::from(&parsed.sig), ResultKind::Void)
}
#[test]
fn explicit_empty_tuple() {
let parsed: ItemFn = parse_quote! {
fn returns_empty_tuple() -> () {}
};
assert_eq!(ResultKind::from(&parsed.sig), ResultKind::Void)
}
#[test]
fn result_returns() {
let parsed: &[ItemFn] = &[
parse_quote! { fn result_empty_tuple() -> Result<(), Err> { unimplemented!() } },
parse_quote! { fn result_empty_tuple_alias() -> Result<()> { unimplemented!() } },
parse_quote! { fn result_fq_empty_tuple() -> std::result::Result<(), Err> { unimplemented!() } },
parse_quote! { fn result_fq_empty_tuple_alias() -> my::package::custom::Result<()> { unimplemented!() } },
];
for item in parsed {
assert_eq!(
ResultKind::from(&item.sig),
ResultKind::Void,
"{}",
item.to_token_stream()
);
}
}
#[test]
fn regular_types() {
let parsed: &[ItemFn] = &[
parse_quote! { fn returns_bool() -> bool { unimplemented!() } },
parse_quote! { fn returns_u32() -> u32 { unimplemented!() } },
parse_quote! { fn returns_result_u32_alias() -> Result<u32> { unimplemented!() } },
parse_quote! { fn returns_result_u32() -> Result<u32, Err> { unimplemented!() } },
parse_quote! { fn returns_fq_result_u32() -> std::result::Result<u32, Err> { unimplemented!() } },
];
for item in parsed {
assert_eq!(
ResultKind::from(&item.sig),
ResultKind::Regular,
"{}",
item.to_token_stream()
);
}
}
}

View File

@ -72,7 +72,7 @@ fn Aes256Ctr32_New(key: &[u8], nonce: &[u8], initial_ctr: u32) -> Result<Aes256C
Aes256Ctr32::from_key(key, nonce, initial_ctr)
}
#[bridge_fn_void(node = false)]
#[bridge_fn(node = false)]
fn Aes256Ctr32_Process(ctr: &mut Aes256Ctr32, data: &mut [u8], offset: u32, length: u32) {
let offset = offset as usize;
let length = length as usize;
@ -88,7 +88,7 @@ fn Aes256GcmEncryption_New(
Aes256GcmEncryption::new(key, nonce, associated_data)
}
#[bridge_fn_void(node = false)]
#[bridge_fn(node = false)]
fn Aes256GcmEncryption_Update(
gcm: &mut Aes256GcmEncryption,
data: &mut [u8],
@ -114,7 +114,7 @@ fn Aes256GcmDecryption_New(
Aes256GcmDecryption::new(key, nonce, associated_data)
}
#[bridge_fn_void(node = false)]
#[bridge_fn(node = false)]
fn Aes256GcmDecryption_Update(
gcm: &mut Aes256GcmDecryption,
data: &mut [u8],
@ -187,12 +187,12 @@ fn CryptographicHash_New(algo: String) -> Result<CryptographicHash> {
CryptographicHash::new(&algo)
}
#[bridge_fn_void(ffi = false, node = false)]
#[bridge_fn(ffi = false, node = false)]
fn CryptographicHash_Update(hash: &mut CryptographicHash, input: &[u8]) {
hash.update(input)
}
#[bridge_fn_void(ffi = false, node = false)]
#[bridge_fn(ffi = false, node = false)]
fn CryptographicHash_UpdateWithOffset(
hash: &mut CryptographicHash,
input: &[u8],
@ -214,12 +214,12 @@ fn CryptographicMac_New(algo: String, key: &[u8]) -> Result<CryptographicMac> {
CryptographicMac::new(&algo, key)
}
#[bridge_fn_void(ffi = false, node = false)]
#[bridge_fn(ffi = false, node = false)]
fn CryptographicMac_Update(mac: &mut CryptographicMac, input: &[u8]) {
mac.update(input)
}
#[bridge_fn_void(ffi = false, node = false)]
#[bridge_fn(ffi = false, node = false)]
fn CryptographicMac_UpdateWithOffset(
mac: &mut CryptographicMac,
input: &[u8],

View File

@ -98,7 +98,7 @@ bridge_get!(
HsmEnclaveClient::initial_request as InitialRequest -> &[u8]
);
#[bridge_fn_void]
#[bridge_fn]
fn HsmEnclaveClient_CompleteHandshake(
cli: &mut HsmEnclaveClient,
handshake_received: &[u8],

View File

@ -15,7 +15,7 @@ use base64::prelude::{Engine, BASE64_STANDARD};
use futures_util::future::TryFutureExt as _;
use http::uri::{InvalidUri, PathAndQuery};
use http::{HeaderMap, HeaderName, HeaderValue};
use libsignal_bridge_macros::{bridge_fn, bridge_fn_void, bridge_io};
use libsignal_bridge_macros::{bridge_fn, bridge_io};
use libsignal_net::auth::Auth;
use libsignal_net::chat::{
chat_service, ChatServiceError, ChatServiceWithDebugInfo, DebugInfo, Request, Response,
@ -286,7 +286,7 @@ fn HttpRequest_new(
})
}
#[bridge_fn_void(ffi = false)]
#[bridge_fn(ffi = false)]
fn HttpRequest_add_header(
request: &HttpRequest,
name: AsType<HeaderName, String>,

View File

@ -6,7 +6,7 @@
use std::convert::TryInto as _;
use std::time::Duration;
use libsignal_bridge_macros::{bridge_fn, bridge_fn_void, bridge_io};
use libsignal_bridge_macros::{bridge_fn, bridge_io};
use libsignal_net::auth::Auth;
use libsignal_net::cdsi::{
self, AciAndAccessKey, CdsiConnection, ClientResponseCollector, LookupResponse, Token, E164,
@ -64,7 +64,7 @@ fn LookupRequest_setToken(request: &LookupRequest, token: &[u8]) {
request.0.lock().expect("not poisoned").token = token.into();
}
#[bridge_fn_void]
#[bridge_fn]
fn LookupRequest_addAciAndAccessKey(
request: &LookupRequest,
aci: Aci,

View File

@ -90,7 +90,7 @@ fn HKDF_DeriveSecrets(
}
// Alternate implementation to fill an existing buffer.
#[bridge_fn_void(jni = false, node = false)]
#[bridge_fn(jni = false, node = false)]
fn HKDF_Derive(output: &mut [u8], ikm: &[u8], label: &[u8], salt: &[u8]) -> Result<()> {
hkdf::Hkdf::<sha2::Sha256>::new(Some(salt), ikm)
.expand(label, output)
@ -961,7 +961,7 @@ fn SessionRecord_GetSessionVersion(s: &SessionRecord) -> Result<u32> {
}
}
#[bridge_fn_void]
#[bridge_fn]
fn SessionRecord_ArchiveCurrentState(session_record: &mut SessionRecord) -> Result<()> {
session_record.archive_current_state()
}
@ -1088,7 +1088,7 @@ fn SessionRecord_InitializeBobSession(
// End SessionRecord testing functions
#[bridge_fn_void(ffi = "process_prekey_bundle")]
#[bridge_fn(ffi = "process_prekey_bundle")]
async fn SessionBuilder_ProcessPreKeyBundle(
bundle: &PreKeyBundle,
protocol_address: &ProtocolAddress,
@ -1285,7 +1285,7 @@ async fn SenderKeyDistributionMessage_Create(
create_sender_key_distribution_message(sender, distribution_id, store, &mut csprng).await
}
#[bridge_fn_void(
#[bridge_fn(
ffi = "process_sender_key_distribution_message",
jni = "GroupSessionBuilder_1ProcessSenderKeyDistributionMessage"
)]

View File

@ -72,7 +72,7 @@ bridge_get!(
SgxClientState::initial_request as InitialRequest -> &[u8]
);
#[bridge_fn_void]
#[bridge_fn]
fn SgxClientState_CompleteHandshake(
cli: &mut SgxClientState,
handshake_received: &[u8],

View File

@ -67,7 +67,7 @@ impl TryFrom<String> for TestingCdsiLookupError {
}
/// Return an error matching the requested description.
#[bridge_fn_void]
#[bridge_fn]
fn TESTING_CdsiLookupErrorConvert(
// The stringly-typed API makes the call sites more self-explanatory.
error_description: AsType<TestingCdsiLookupError, String>,

View File

@ -25,7 +25,7 @@ pub fn Username_Proof(username: String, randomness: &[u8]) -> Result<Vec<u8>, Us
Username::new(&username)?.proof(randomness)
}
#[bridge_fn_void]
#[bridge_fn]
pub fn Username_Verify(
proof: &[u8],
hash: &[u8],

View File

@ -48,7 +48,7 @@ macro_rules! fixed_length_serializable {
impl FixedLengthBincodeSerializable for $typ {
type Array = [u8; [<$typ:snake:upper _LEN>]];
}
#[bridge_fn_void]
#[bridge_fn]
fn [<$typ _CheckValidContents>](
buffer: &[u8]
) -> Result<(), ZkGroupDeserializationFailure> {
@ -420,21 +420,21 @@ fn ServerSecretParams_IssueAuthCredentialWithPniZkcDeterministic(
))
}
#[bridge_fn_void]
#[bridge_fn]
fn AuthCredentialWithPni_CheckValidContents(
bytes: &[u8],
) -> Result<(), ZkGroupDeserializationFailure> {
AuthCredentialWithPni::new(bytes).map(|_| ())
}
#[bridge_fn_void]
#[bridge_fn]
fn AuthCredentialWithPniResponse_CheckValidContents(
bytes: &[u8],
) -> Result<(), ZkGroupDeserializationFailure> {
AuthCredentialWithPniResponse::new(bytes).map(|_| ())
}
#[bridge_fn_void]
#[bridge_fn]
fn ServerSecretParams_VerifyAuthCredentialPresentation(
server_secret_params: Serialized<ServerSecretParams>,
group_public_params: Serialized<GroupPublicParams>,
@ -470,7 +470,7 @@ fn ServerSecretParams_IssueExpiringProfileKeyCredentialDeterministic(
.into())
}
#[bridge_fn_void]
#[bridge_fn]
fn ServerSecretParams_VerifyProfileKeyCredentialPresentation(
server_secret_params: Serialized<ServerSecretParams>,
group_public_params: Serialized<GroupPublicParams>,
@ -504,7 +504,7 @@ fn ServerSecretParams_IssueReceiptCredentialDeterministic(
.into()
}
#[bridge_fn_void]
#[bridge_fn]
fn ServerSecretParams_VerifyReceiptCredentialPresentation(
server_secret_params: Serialized<ServerSecretParams>,
presentation: Serialized<ReceiptCredentialPresentation>,
@ -520,7 +520,7 @@ fn GroupPublicParams_GetGroupIdentifier(
group_public_params.get_group_identifier()
}
#[bridge_fn_void]
#[bridge_fn]
fn ServerPublicParams_VerifySignature(
server_public_params: Serialized<ServerPublicParams>,
message: &[u8],
@ -529,7 +529,7 @@ fn ServerPublicParams_VerifySignature(
server_public_params.verify_signature(message, *notary_signature)
}
#[bridge_fn_void]
#[bridge_fn]
fn AuthCredentialPresentation_CheckValidContents(
presentation_bytes: &[u8],
) -> Result<(), ZkGroupDeserializationFailure> {
@ -588,7 +588,7 @@ fn ExpiringProfileKeyCredential_GetExpirationTime(
credential.get_expiration_time().into()
}
#[bridge_fn_void]
#[bridge_fn]
fn ProfileKeyCredentialPresentation_CheckValidContents(
presentation_bytes: &[u8],
) -> Result<(), ZkGroupDeserializationFailure> {
@ -660,7 +660,7 @@ fn ReceiptCredentialPresentation_GetReceiptSerial(
presentation.get_receipt_serial_bytes()
}
#[bridge_fn_void]
#[bridge_fn]
fn GenericServerSecretParams_CheckValidContents(
params_bytes: &[u8],
) -> Result<(), ZkGroupDeserializationFailure> {
@ -682,14 +682,14 @@ fn GenericServerSecretParams_GetPublicParams(params_bytes: &[u8]) -> Vec<u8> {
zkgroup::serialize(&public_params)
}
#[bridge_fn_void]
#[bridge_fn]
fn GenericServerPublicParams_CheckValidContents(
params_bytes: &[u8],
) -> Result<(), ZkGroupDeserializationFailure> {
validate_serialization::<GenericServerPublicParams>(params_bytes)
}
#[bridge_fn_void]
#[bridge_fn]
fn CallLinkSecretParams_CheckValidContents(
params_bytes: &[u8],
) -> Result<(), ZkGroupDeserializationFailure> {
@ -722,14 +722,14 @@ fn CallLinkSecretParams_DecryptUserId(
params.decrypt_uid(user_id.into_inner())
}
#[bridge_fn_void]
#[bridge_fn]
fn CallLinkPublicParams_CheckValidContents(
params_bytes: &[u8],
) -> Result<(), ZkGroupDeserializationFailure> {
validate_serialization::<CallLinkPublicParams>(params_bytes)
}
#[bridge_fn_void]
#[bridge_fn]
fn CreateCallLinkCredentialRequestContext_CheckValidContents(
context_bytes: &[u8],
) -> Result<(), ZkGroupDeserializationFailure> {
@ -754,7 +754,7 @@ fn CreateCallLinkCredentialRequestContext_GetRequest(context_bytes: &[u8]) -> Ve
zkgroup::serialize(&request)
}
#[bridge_fn_void]
#[bridge_fn]
fn CreateCallLinkCredentialRequest_CheckValidContents(
request_bytes: &[u8],
) -> Result<(), ZkGroupDeserializationFailure> {
@ -778,7 +778,7 @@ fn CreateCallLinkCredentialRequest_IssueDeterministic(
zkgroup::serialize(&response)
}
#[bridge_fn_void]
#[bridge_fn]
fn CreateCallLinkCredentialResponse_CheckValidContents(
response_bytes: &[u8],
) -> Result<(), ZkGroupDeserializationFailure> {
@ -803,7 +803,7 @@ fn CreateCallLinkCredentialRequestContext_ReceiveResponse(
Ok(zkgroup::serialize(&credential))
}
#[bridge_fn_void]
#[bridge_fn]
fn CreateCallLinkCredential_CheckValidContents(
params_bytes: &[u8],
) -> Result<(), ZkGroupDeserializationFailure> {
@ -836,14 +836,14 @@ fn CreateCallLinkCredential_PresentDeterministic(
Ok(zkgroup::serialize(&presentation))
}
#[bridge_fn_void]
#[bridge_fn]
fn CreateCallLinkCredentialPresentation_CheckValidContents(
presentation_bytes: &[u8],
) -> Result<(), ZkGroupDeserializationFailure> {
validate_serialization::<CreateCallLinkCredentialPresentation>(presentation_bytes)
}
#[bridge_fn_void]
#[bridge_fn]
fn CreateCallLinkCredentialPresentation_Verify(
presentation_bytes: &[u8],
room_id: &[u8],
@ -862,7 +862,7 @@ fn CreateCallLinkCredentialPresentation_Verify(
presentation.verify(room_id, now.as_seconds(), &server_params, &call_link_params)
}
#[bridge_fn_void]
#[bridge_fn]
fn CallLinkAuthCredentialResponse_CheckValidContents(
response_bytes: &[u8],
) -> Result<(), ZkGroupDeserializationFailure> {
@ -904,7 +904,7 @@ fn CallLinkAuthCredentialResponse_Receive(
Ok(zkgroup::serialize(&credential))
}
#[bridge_fn_void]
#[bridge_fn]
fn CallLinkAuthCredential_CheckValidContents(
credential_bytes: &[u8],
) -> Result<(), ZkGroupDeserializationFailure> {
@ -937,14 +937,14 @@ fn CallLinkAuthCredential_PresentDeterministic(
Ok(zkgroup::serialize(&presentation))
}
#[bridge_fn_void]
#[bridge_fn]
fn CallLinkAuthCredentialPresentation_CheckValidContents(
presentation_bytes: &[u8],
) -> Result<(), ZkGroupDeserializationFailure> {
validate_serialization::<CallLinkAuthCredentialPresentation>(presentation_bytes)
}
#[bridge_fn_void]
#[bridge_fn]
fn CallLinkAuthCredentialPresentation_Verify(
presentation_bytes: &[u8],
now: Timestamp,
@ -979,7 +979,7 @@ fn BackupAuthCredentialRequestContext_New(backup_key: &[u8; 32], uuid: Uuid) ->
zkgroup::serialize(&context)
}
#[bridge_fn_void]
#[bridge_fn]
fn BackupAuthCredentialRequestContext_CheckValidContents(
context_bytes: &[u8],
) -> Result<(), ZkGroupDeserializationFailure> {
@ -995,7 +995,7 @@ fn BackupAuthCredentialRequestContext_GetRequest(context_bytes: &[u8]) -> Vec<u8
zkgroup::serialize(&request)
}
#[bridge_fn_void]
#[bridge_fn]
fn BackupAuthCredentialRequest_CheckValidContents(
request_bytes: &[u8],
) -> Result<(), ZkGroupDeserializationFailure> {
@ -1024,7 +1024,7 @@ fn BackupAuthCredentialRequest_IssueDeterministic(
zkgroup::serialize(&response)
}
#[bridge_fn_void]
#[bridge_fn]
fn BackupAuthCredentialResponse_CheckValidContents(
response_bytes: &[u8],
) -> Result<(), ZkGroupDeserializationFailure> {
@ -1049,7 +1049,7 @@ fn BackupAuthCredentialRequestContext_ReceiveResponse(
Ok(zkgroup::serialize(&credential))
}
#[bridge_fn_void]
#[bridge_fn]
fn BackupAuthCredential_CheckValidContents(
params_bytes: &[u8],
) -> Result<(), ZkGroupDeserializationFailure> {
@ -1078,14 +1078,14 @@ fn BackupAuthCredential_PresentDeterministic(
Ok(zkgroup::serialize(&presentation))
}
#[bridge_fn_void]
#[bridge_fn]
fn BackupAuthCredentialPresentation_CheckValidContents(
presentation_bytes: &[u8],
) -> Result<(), ZkGroupDeserializationFailure> {
validate_serialization::<BackupAuthCredentialPresentation>(presentation_bytes)
}
#[bridge_fn_void]
#[bridge_fn]
fn BackupAuthCredentialPresentation_Verify(
presentation_bytes: &[u8],
now: Timestamp,
@ -1113,7 +1113,7 @@ fn BackupAuthCredentialPresentation_GetReceiptLevel(presentation_bytes: &[u8]) -
presentation.receipt_level()
}
#[bridge_fn_void]
#[bridge_fn]
fn GroupSendDerivedKeyPair_CheckValidContents(
bytes: &[u8],
) -> Result<(), ZkGroupDeserializationFailure> {
@ -1131,7 +1131,7 @@ fn GroupSendDerivedKeyPair_ForExpiration(
))
}
#[bridge_fn_void]
#[bridge_fn]
fn GroupSendEndorsementsResponse_CheckValidContents(
bytes: &[u8],
) -> Result<(), ZkGroupDeserializationFailure> {
@ -1246,7 +1246,7 @@ fn GroupSendEndorsementsResponse_ReceiveAndCombineWithCiphertexts(
.collect())
}
#[bridge_fn_void]
#[bridge_fn]
fn GroupSendEndorsement_CheckValidContents(
bytes: &[u8],
) -> Result<(), ZkGroupDeserializationFailure> {
@ -1283,7 +1283,7 @@ fn GroupSendEndorsement_ToToken(
zkgroup::serialize(&endorsement.to_token(&group_params))
}
#[bridge_fn_void]
#[bridge_fn]
fn GroupSendToken_CheckValidContents(bytes: &[u8]) -> Result<(), ZkGroupDeserializationFailure> {
validate_serialization::<GroupSendToken>(bytes)
}
@ -1295,7 +1295,7 @@ fn GroupSendToken_ToFullToken(token: &[u8], expiration: Timestamp) -> Vec<u8> {
zkgroup::serialize(&token.into_full_token(expiration.as_seconds()))
}
#[bridge_fn_void]
#[bridge_fn]
fn GroupSendFullToken_CheckValidContents(
bytes: &[u8],
) -> Result<(), ZkGroupDeserializationFailure> {
@ -1309,7 +1309,7 @@ fn GroupSendFullToken_GetExpiration(token: &[u8]) -> Timestamp {
Timestamp::from_seconds(token.expiration())
}
#[bridge_fn_void]
#[bridge_fn]
fn GroupSendFullToken_Verify(
token: &[u8],
user_ids: ServiceIdSequence<'_>,

View File

@ -1430,7 +1430,7 @@ SignalFfiError *signal_mp4_sanitizer_sanitize(SignalSanitizedMetadata **out, con
#endif
#if defined(SIGNAL_MEDIA_SUPPORTED)
SignalFfiError *signal_webp_sanitizer_sanitize(bool *out, const SignalSyncInputStream *input);
SignalFfiError *signal_webp_sanitizer_sanitize(const SignalSyncInputStream *input);
#endif
#if defined(SIGNAL_MEDIA_SUPPORTED)