Skip to content

Commit

Permalink
Automatic support for C-String literals
Browse files Browse the repository at this point in the history
When `generate_cstr` is enabled, and the target rust version is >= 1.77, generate `c"..."` literals instead of the unsafe `from_bytes_with_nul_unchecked` calls.
  • Loading branch information
nyurik committed Nov 29, 2024
1 parent d2e30fb commit 66caf51
Show file tree
Hide file tree
Showing 5 changed files with 45 additions and 21 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.

4 changes: 4 additions & 0 deletions bindgen-tests/tests/expectations/tests/strings_cstr2.rs

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

5 changes: 5 additions & 0 deletions bindgen-tests/tests/headers/strings_cstr2.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
// bindgen-flags: --rust-target=1.77 --generate-cstr

const char* MY_STRING_UTF8 = "Hello, world!";
const char* MY_STRING_INTERIOR_NULL = "Hello,\0World!";
const char* MY_STRING_NON_UTF8 = "ABCDE\xFF";
48 changes: 30 additions & 18 deletions bindgen/codegen/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -717,26 +717,38 @@ impl CodeGenerator for Var {
let len = proc_macro2::Literal::usize_unsuffixed(
cstr_bytes.len(),
);
let cstr =
if options.generate_cstr && rust_features.const_cstr {
CStr::from_bytes_with_nul(&cstr_bytes).ok()
} else {
None
};

// TODO: Here we ignore the type we just made up, probably
// we should refactor how the variable type and ty ID work.
let array_ty = quote! { [u8; #len] };
let cstr_ty = quote! { ::#prefix::ffi::CStr };

let bytes = proc_macro2::Literal::byte_string(&cstr_bytes);

if options.generate_cstr &&
rust_features.const_cstr &&
CStr::from_bytes_with_nul(&cstr_bytes).is_ok()
{
result.push(quote! {
#(#attrs)*
#[allow(unsafe_code)]
pub const #canonical_ident: &#cstr_ty = unsafe {
#cstr_ty::from_bytes_with_nul_unchecked(#bytes)
};
});
if let Some(cstr) = cstr {
let cstr_ty = quote! { ::#prefix::ffi::CStr };
if rust_features.literal_cstr {
let cstr = proc_macro2::Literal::c_string(&cstr);
result.push(quote! {
#(#attrs)*
pub const #canonical_ident: &#cstr_ty = #cstr;
});
} else {
let bytes =
proc_macro2::Literal::byte_string(&cstr_bytes);
result.push(quote! {
#(#attrs)*
#[allow(unsafe_code)]
pub const #canonical_ident: &#cstr_ty = unsafe {
#cstr_ty::from_bytes_with_nul_unchecked(#bytes)
};
});
}
} else {
// TODO: Here we ignore the type we just made up, probably
// we should refactor how the variable type and ty ID work.
let array_ty = quote! { [u8; #len] };
let bytes =
proc_macro2::Literal::byte_string(&cstr_bytes);
let lifetime =
if true { None } else { Some(quote! { 'static }) }
.into_iter();
Expand Down
5 changes: 4 additions & 1 deletion bindgen/features.rs
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,10 @@ define_rust_targets! {
ptr_metadata: #81513,
layout_for_ptr: #69835,
},
Stable_1_77(77) => { offset_of: #106655 },
Stable_1_77(77) => {
offset_of: #106655,
literal_cstr: #117472,
},
Stable_1_73(73) => { thiscall_abi: #42202 },
Stable_1_71(71) => { c_unwind_abi: #106075 },
Stable_1_68(68) => { abi_efiapi: #105795 },
Expand Down

0 comments on commit 66caf51

Please sign in to comment.