Skip to content

Commit 46a08b0

Browse files
authored
Generate CStrs instead of byte arrays. (#2471)
1 parent a968109 commit 46a08b0

File tree

14 files changed

+123
-47
lines changed

14 files changed

+123
-47
lines changed

CHANGELOG.md

+7-1
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,13 @@
166166

167167
## Added
168168

169+
* Added the `--generate-cstr` CLI flag to generate string constants as `&CStr`
170+
instead of `&[u8]`. (Requires Rust 1.59 or higher.)
171+
169172
## Changed
173+
174+
* Non-UTF-8 string constants are now generated as references (`&[u8; SIZE]`)
175+
instead of arrays (`[u8; SIZE]`) to match UTF-8 strings.
170176
* Wrappers for static functions that return `void` no longer contain a `return`
171177
statement and only call the static function instead.
172178

@@ -252,7 +258,7 @@
252258
* The return type is now ommited in signatures of functions returning `void`.
253259
* Updated the `clap` dependency for `bindgen-cli` to 4.
254260
* Rewrote the `bindgen-cli` argument parser which could introduce unexpected
255-
behavior changes.
261+
behavior changes.
256262
* The `ParseCallbacks::add_derives` method now receives `DeriveInfo<'_>` as
257263
argument instead of a `&str`. This type also includes the kind of target type.
258264

bindgen-cli/options.rs

+8
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,9 @@ struct BindgenCommand {
156156
/// Generate block signatures instead of void pointers.
157157
#[arg(long)]
158158
generate_block: bool,
159+
/// Generate string constants as `&CStr` instead of `&[u8]`.
160+
#[arg(long)]
161+
generate_cstr: bool,
159162
/// Use extern crate instead of use for block.
160163
#[arg(long)]
161164
block_extern_crate: bool,
@@ -430,6 +433,7 @@ where
430433
no_recursive_allowlist,
431434
objc_extern_crate,
432435
generate_block,
436+
generate_cstr,
433437
block_extern_crate,
434438
distrust_clang_mangling,
435439
builtins,
@@ -755,6 +759,10 @@ where
755759
builder = builder.generate_block(true);
756760
}
757761

762+
if generate_cstr {
763+
builder = builder.generate_cstr(true);
764+
}
765+
758766
if block_extern_crate {
759767
builder = builder.block_extern_crate(true);
760768
}

bindgen-tests/tests/expectations/tests/libclang-5/constant-evaluate.rs

+2-2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

bindgen-tests/tests/expectations/tests/libclang-9/constant-evaluate.rs

+2-2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

bindgen-tests/tests/expectations/tests/macro_const.rs

+2-2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

bindgen-tests/tests/expectations/tests/macro_const_1_0.rs

+2-2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

bindgen-tests/tests/expectations/tests/strings_array.rs

+10
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

bindgen-tests/tests/expectations/tests/strings_cstr.rs

+17
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
// bindgen-flags: --rust-target=1.0
2+
3+
const char* MY_STRING_UTF8 = "Hello, world!";
4+
const char* MY_STRING_INTERIOR_NULL = "Hello,\0World!";
5+
const char* MY_STRING_NON_UTF8 = "ABCDE\xFF";
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
// bindgen-flags: --rust-target=1.59 --generate-cstr
2+
3+
const char* MY_STRING_UTF8 = "Hello, world!";
4+
const char* MY_STRING_INTERIOR_NULL = "Hello,\0World!";
5+
const char* MY_STRING_NON_UTF8 = "ABCDE\xFF";

bindgen/codegen/helpers.rs

-6
Original file line numberDiff line numberDiff line change
@@ -249,12 +249,6 @@ pub(crate) mod ast_ty {
249249
quote!(#val)
250250
}
251251

252-
pub(crate) fn byte_array_expr(bytes: &[u8]) -> TokenStream {
253-
let mut bytes: Vec<_> = bytes.to_vec();
254-
bytes.push(0);
255-
quote! { [ #(#bytes),* ] }
256-
}
257-
258252
pub(crate) fn cstr_expr(mut string: String) -> TokenStream {
259253
string.push('\0');
260254
let b = proc_macro2::Literal::byte_string(string.as_bytes());

bindgen/codegen/mod.rs

+40-32
Original file line numberDiff line numberDiff line change
@@ -57,9 +57,10 @@ use crate::{Entry, HashMap, HashSet};
5757
use std::borrow::Cow;
5858
use std::cell::Cell;
5959
use std::collections::VecDeque;
60+
use std::ffi::CStr;
6061
use std::fmt::{self, Write};
6162
use std::ops;
62-
use std::str::FromStr;
63+
use std::str::{self, FromStr};
6364

6465
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
6566
pub enum CodegenError {
@@ -692,41 +693,48 @@ impl CodeGenerator for Var {
692693
});
693694
}
694695
VarType::String(ref bytes) => {
695-
// Account the trailing zero.
696-
//
696+
let prefix = ctx.trait_prefix();
697+
698+
let options = ctx.options();
699+
let rust_features = options.rust_features;
700+
701+
let mut cstr_bytes = bytes.clone();
702+
cstr_bytes.push(0);
703+
let len = proc_macro2::Literal::usize_unsuffixed(
704+
cstr_bytes.len(),
705+
);
706+
let cstr = CStr::from_bytes_with_nul(&cstr_bytes).unwrap();
707+
697708
// TODO: Here we ignore the type we just made up, probably
698709
// we should refactor how the variable type and ty ID work.
699-
let len = bytes.len() + 1;
700-
let ty = quote! {
701-
[u8; #len]
702-
};
710+
let array_ty = quote! { [u8; #len] };
711+
let cstr_ty = quote! { ::#prefix::ffi::CStr };
703712

704-
match String::from_utf8(bytes.clone()) {
705-
Ok(string) => {
706-
let cstr = helpers::ast_ty::cstr_expr(string);
707-
if ctx
708-
.options()
709-
.rust_features
710-
.static_lifetime_elision
711-
{
712-
result.push(quote! {
713-
#(#attrs)*
714-
pub const #canonical_ident : &#ty = #cstr ;
715-
});
716-
} else {
717-
result.push(quote! {
718-
#(#attrs)*
719-
pub const #canonical_ident : &'static #ty = #cstr ;
720-
});
721-
}
722-
}
723-
Err(..) => {
724-
let bytes = helpers::ast_ty::byte_array_expr(bytes);
725-
result.push(quote! {
726-
#(#attrs)*
727-
pub const #canonical_ident : #ty = #bytes ;
728-
});
713+
let bytes = proc_macro2::Literal::byte_string(
714+
cstr.to_bytes_with_nul(),
715+
);
716+
717+
if rust_features.const_cstr && options.generate_cstr {
718+
result.push(quote! {
719+
#(#attrs)*
720+
#[allow(unsafe_code)]
721+
pub const #canonical_ident: &#cstr_ty = unsafe {
722+
#cstr_ty::from_bytes_with_nul_unchecked(#bytes)
723+
};
724+
});
725+
} else {
726+
let lifetime = if rust_features.static_lifetime_elision
727+
{
728+
None
729+
} else {
730+
Some(quote! { 'static })
729731
}
732+
.into_iter();
733+
734+
result.push(quote! {
735+
#(#attrs)*
736+
pub const #canonical_ident: &#(#lifetime )*#array_ty = #bytes ;
737+
});
730738
}
731739
}
732740
VarType::Float(f) => {

bindgen/features.rs

+6
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,9 @@ macro_rules! rust_target_base {
128128
/// Rust stable 1.47
129129
/// * `larger_arrays` ([Tracking issue](https://github.com/rust-lang/rust/pull/74060))
130130
=> Stable_1_47 => 1.47;
131+
/// Rust stable 1.59
132+
/// * `CStr::from_bytes_with_nul_unchecked` in `const` contexts ([PR](https://github.com/rust-lang/rust/pull/54745))
133+
=> Stable_1_59 => 1.59;
131134
/// Rust stable 1.64
132135
/// * `core_ffi_c` ([Tracking issue](https://github.com/rust-lang/rust/issues/94501))
133136
=> Stable_1_64 => 1.64;
@@ -241,6 +244,9 @@ rust_feature_def!(
241244
Stable_1_47 {
242245
=> larger_arrays;
243246
}
247+
Stable_1_59 {
248+
=> const_cstr;
249+
}
244250
Stable_1_64 {
245251
=> core_ffi_c;
246252
}

bindgen/options/mod.rs

+17
Original file line numberDiff line numberDiff line change
@@ -1416,6 +1416,23 @@ options! {
14161416
},
14171417
as_args: "--generate-block",
14181418
},
1419+
/// Whether to generate strings as `CStr`.
1420+
generate_cstr: bool {
1421+
methods: {
1422+
/// Set whether string constants should be generated as `&CStr` instead of `&[u8]`.
1423+
///
1424+
/// A minimum Rust target of 1.59 is required for this to have any effect as support
1425+
/// for `CStr::from_bytes_with_nul_unchecked` in `const` contexts is needed.
1426+
///
1427+
/// This option is disabled by default but will become enabled by default in a future
1428+
/// release, so enabling this is recommended.
1429+
pub fn generate_cstr(mut self, doit: bool) -> Self {
1430+
self.options.generate_cstr = doit;
1431+
self
1432+
}
1433+
},
1434+
as_args: "--generate-cstr",
1435+
},
14191436
/// Whether to emit `#[macro_use] extern crate block;` instead of `use block;` in the prologue
14201437
/// of the files generated from apple block files.
14211438
block_extern_crate: bool {

0 commit comments

Comments
 (0)