Skip to content

Commit dc5584e

Browse files
authored
Switch char to i8 (#452)
Fixes #246
1 parent 798c406 commit dc5584e

153 files changed

Lines changed: 2389 additions & 2050 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎cpp2rust/converter/models/converter_refcount.cpp‎

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1252,19 +1252,25 @@ bool ConverterRefCount::VisitStringLiteral(clang::StringLiteral *expr) {
12521252
}
12531253

12541254
if (IsArrayInitContext()) {
1255+
// The array may have a different element type than the literal, e.g.,
1256+
// unsigned char s[] = "abc".
1257+
auto elem_ty =
1258+
ctx_.getAsArrayType(curr_init_type_.back())->getElementType();
1259+
std::string elem(Trim(ToStringBase(elem_ty.getUnqualifiedType())));
12551260
uint64_t pad = 1;
12561261
if (auto *arr_ty = ctx_.getAsConstantArrayType(curr_init_type_.back())) {
12571262
uint64_t arr_size = arr_ty->getSize().getZExtValue();
12581263
if (expr->getString().empty()) {
1259-
StrCat(std::format("vec![0u8; {}].into_boxed_slice()", arr_size));
1264+
StrCat(std::format("vec![0{}; {}].into_boxed_slice()", elem, arr_size));
12601265
computed_expr_type_ = ComputedExprType::FreshValue;
12611266
return false;
12621267
}
12631268
pad = arr_size > expr->getString().size()
12641269
? arr_size - expr->getString().size()
12651270
: 0;
12661271
}
1267-
StrCat(std::format("Box::from(*b{})", GetEscapedStringLiteral(expr, pad)));
1272+
StrCat(std::format("{}::array_from_literal(b{})", elem,
1273+
GetEscapedStringLiteral(expr, pad)));
12681274
computed_expr_type_ = ComputedExprType::FreshValue;
12691275
return false;
12701276
}
@@ -2669,7 +2675,7 @@ bool ConverterRefCount::ConvertCXXOperatorCallExpr(
26692675
void ConverterRefCount::ConvertFunctionParameters(clang::FunctionDecl *decl) {
26702676
PushConversionKind push(*this, ConversionKind::Unboxed);
26712677
if (decl->isMain() && (decl->getNumParams() != 0U)) {
2672-
StrCat(std::format("{}: i32, {}: Ptr<Ptr<u8>>",
2678+
StrCat(std::format("{}: i32, {}: Ptr<Ptr<i8>>",
26732679
GetNamedDeclAsString(decl->getParamDecl(0)),
26742680
GetNamedDeclAsString(decl->getParamDecl(1))));
26752681
} else {
@@ -2812,10 +2818,10 @@ void ConverterRefCount::ConvertFunctionMain(
28122818
if (decl->getNumParams() != 0U) {
28132819
StrCat(std::format(R"(
28142820
pub fn main() {{
2815-
let argv: Vec<Value<Vec<u8>>> = ::std::env::args()
2816-
.map(|x| Rc::new(RefCell::new(x.as_bytes().to_vec())))
2821+
let argv: Vec<Value<Vec<i8>>> = ::std::env::args()
2822+
.map(|x| Rc::new(RefCell::new(x.bytes().map(|c| c as i8).collect())))
28172823
.collect();
2818-
let mut argv: Value<Vec<Ptr<u8>>> = Rc::new(RefCell::new(
2824+
let mut argv: Value<Vec<Ptr<i8>>> = Rc::new(RefCell::new(
28192825
argv.iter().map(|x| {{ x.borrow_mut().push(0); x.as_pointer() }}).collect(),
28202826
));
28212827
(*argv.borrow_mut()).push(Ptr::null());

‎cpp2rust/converter/models/converter_refcount.h‎

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,7 @@ class ConverterRefCount final : public Converter {
3535

3636
bool EmitsReprCForRecords() const override { return false; }
3737

38-
const char *CharRustType() const override { return "u8"; }
39-
bool CharRustTypeIsSigned() const override { return false; }
38+
const char *CharRustType() const override { return "i8"; }
4039

4140
std::string GetComparisonReferenceArg(const clang::CXXRecordDecl *decl,
4241
std::string_view value) override;

‎libcc2rs/src/alloc.rs‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ pub fn calloc_refcount(a0: usize, a1: usize) -> AnyPtr {
3030
Ptr::alloc_array(vec![0u8; a0 * a1].into_boxed_slice()).to_any()
3131
}
3232

33-
pub fn strdup_refcount(a0: Ptr<u8>) -> Ptr<u8> {
33+
pub fn strdup_refcount(a0: Ptr<i8>) -> Ptr<i8> {
3434
let mut bytes = a0.to_c_bytes();
3535
bytes.push(0);
3636
Ptr::alloc_array(bytes.into_boxed_slice())

‎libcc2rs/src/cstr.rs‎

Lines changed: 177 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,100 @@ use std::rc::Rc;
88

99
use crate::CStringIterator;
1010
use crate::rc::{Ptr, PtrKind};
11+
use crate::reinterpret::{ByteRepr, with_scratch};
1112

12-
impl fmt::Display for Ptr<u8> {
13+
// The element types of C strings: `char` and `signed char` map to `i8`,
14+
// `unsigned char` to `u8`.
15+
pub trait CChar: Copy + Default + PartialEq + ByteRepr + 'static {
16+
fn to_byte(self) -> u8;
17+
fn from_byte(b: u8) -> Self;
18+
// Calls `f` with `buf` viewed as a slice of `Self`; changes are copied back.
19+
fn with_bytes_mut<R>(buf: &mut [u8], f: impl FnOnce(&mut [Self]) -> R) -> R;
20+
// Calls `f` with `s` viewed as bytes. Free for u8; i8 needs a copy, which
21+
// lives on the stack for short slices.
22+
fn with_u8_slice<R>(s: &[Self], f: impl FnOnce(&[u8]) -> R) -> R;
23+
// Conversions between vectors of Self and of bytes, without reallocating.
24+
fn into_byte_vec(v: Vec<Self>) -> Vec<u8>;
25+
fn from_byte_vec(v: Vec<u8>) -> Vec<Self>;
26+
27+
// The array initialized by a narrow string literal, e.g., char s[] = "abc".
28+
fn array_from_literal<const N: usize>(s: &[u8; N]) -> Box<[Self]> {
29+
Box::new(s.map(Self::from_byte))
30+
}
31+
}
32+
33+
impl CChar for u8 {
34+
#[inline(always)]
35+
fn to_byte(self) -> u8 {
36+
self
37+
}
38+
#[inline(always)]
39+
fn from_byte(b: u8) -> Self {
40+
b
41+
}
42+
#[inline(always)]
43+
fn with_bytes_mut<R>(buf: &mut [u8], f: impl FnOnce(&mut [Self]) -> R) -> R {
44+
f(buf)
45+
}
46+
#[inline(always)]
47+
fn with_u8_slice<R>(s: &[Self], f: impl FnOnce(&[u8]) -> R) -> R {
48+
f(s)
49+
}
50+
#[inline(always)]
51+
fn into_byte_vec(v: Vec<Self>) -> Vec<u8> {
52+
v
53+
}
54+
#[inline(always)]
55+
fn from_byte_vec(v: Vec<u8>) -> Vec<Self> {
56+
v
57+
}
58+
}
59+
60+
impl CChar for i8 {
61+
#[inline(always)]
62+
fn to_byte(self) -> u8 {
63+
self as u8
64+
}
65+
#[inline(always)]
66+
fn from_byte(b: u8) -> Self {
67+
b as i8
68+
}
69+
fn with_bytes_mut<R>(buf: &mut [u8], f: impl FnOnce(&mut [Self]) -> R) -> R {
70+
with_scratch(buf.len(), |chars: &mut [i8]| {
71+
for (c, &b) in chars.iter_mut().zip(buf.iter()) {
72+
*c = b as i8;
73+
}
74+
let r = f(chars);
75+
for (b, &c) in buf.iter_mut().zip(chars.iter()) {
76+
*b = c as u8;
77+
}
78+
r
79+
})
80+
}
81+
fn with_u8_slice<R>(s: &[Self], f: impl FnOnce(&[u8]) -> R) -> R {
82+
with_scratch(s.len(), |buf: &mut [u8]| {
83+
for (b, &c) in buf.iter_mut().zip(s) {
84+
*b = c as u8;
85+
}
86+
f(buf)
87+
})
88+
}
89+
// Both collects reuse the allocation, as i8 and u8 have the same layout.
90+
fn into_byte_vec(v: Vec<Self>) -> Vec<u8> {
91+
v.into_iter().map(|c| c as u8).collect()
92+
}
93+
fn from_byte_vec(v: Vec<u8>) -> Vec<Self> {
94+
v.into_iter().map(|b| b as i8).collect()
95+
}
96+
}
97+
98+
impl<T: CChar> fmt::Display for Ptr<T> {
1399
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
14100
match &self.kind {
15101
PtrKind::Null => write!(f, "NULL"),
16102
_ => {
17103
for value in self {
18-
let ch = value.read();
104+
let ch = value.read().to_byte();
19105
if ch == 0 {
20106
break;
21107
}
@@ -62,7 +148,35 @@ impl_string_literal!(u16, STRING_LITERALS_U16);
62148
impl_string_literal!(u32, STRING_LITERALS_U32);
63149
impl_string_literal!(i32, STRING_LITERALS_I32);
64150

65-
impl Ptr<u8> {
151+
thread_local! {
152+
static STRING_LITERALS_I8: RefCell<HashMap<&'static [u8], Rc<RefCell<Box<[i8]>>>>> =
153+
RefCell::new(HashMap::new());
154+
}
155+
156+
impl Ptr<i8> {
157+
// Narrow string literals are emitted as byte strings (b"..."), as Rust has
158+
// no literal syntax for `[i8]`.
159+
#[inline]
160+
pub fn from_string_literal(s: &'static [u8]) -> Self {
161+
STRING_LITERALS_I8.with(|literals| {
162+
let mut literals = literals.borrow_mut();
163+
let weak = Rc::downgrade(literals.entry(s).or_insert_with(|| {
164+
Rc::new(RefCell::new(
165+
s.iter()
166+
.map(|&c| c as i8)
167+
.chain(std::iter::once(0))
168+
.collect(),
169+
))
170+
}));
171+
Ptr {
172+
offset: 0,
173+
kind: PtrKind::StackArray(weak),
174+
}
175+
})
176+
}
177+
}
178+
179+
impl<T: CChar> Ptr<T> {
66180
#[allow(clippy::explicit_counter_loop)]
67181
pub fn memcpy(&self, src: &Self, len: usize) {
68182
if *self > *src {
@@ -89,7 +203,7 @@ impl Ptr<u8> {
89203
}
90204

91205
#[allow(clippy::explicit_counter_loop)]
92-
pub fn memset(&self, value: u8, num: usize) {
206+
pub fn memset(&self, value: T, num: usize) {
93207
let mut dst = self.clone();
94208
for _ in 0..num {
95209
dst.write(value);
@@ -102,8 +216,9 @@ impl Ptr<u8> {
102216
let mut a = self.clone();
103217
let mut b = other.clone();
104218
for _ in 0..len {
105-
let va = a.read();
106-
let vb = b.read();
219+
// Bytes compare as unsigned char.
220+
let va = a.read().to_byte();
221+
let vb = b.read().to_byte();
107222
if va != vb {
108223
return (va as i32).wrapping_sub(vb as i32);
109224
}
@@ -113,15 +228,15 @@ impl Ptr<u8> {
113228
0
114229
}
115230

116-
pub fn to_c_string_iterator(&self) -> CStringIterator {
231+
pub fn to_c_string_iterator(&self) -> CStringIterator<T> {
117232
CStringIterator { ptr: self.clone() }
118233
}
119234

120235
// Calls `f` with the bytes of the C string, excluding the terminating NUL.
121236
// The bytes are borrowed from the allocation, so `f` must not write to it.
122-
pub fn with_c_str<R>(&self, f: impl FnOnce(&[u8]) -> R) -> R {
123-
fn until_nul(tail: &[u8]) -> &[u8] {
124-
match tail.iter().position(|&b| b == 0) {
237+
pub fn with_c_str<R>(&self, f: impl FnOnce(&[T]) -> R) -> R {
238+
fn until_nul<T: CChar>(tail: &[T]) -> &[T] {
239+
match tail.iter().position(|&b| b == T::default()) {
125240
Some(len) => &tail[..len],
126241
None => panic!("ub: unterminated string"),
127242
}
@@ -146,10 +261,10 @@ impl Ptr<u8> {
146261
}
147262
PtrKind::Field(root) => {
148263
let root = crate::field::upgrade(root);
149-
let b = Ptr::<u8>::borrow_field(&*root, self.offset);
264+
let b = Ptr::<T>::borrow_field(&*root, self.offset);
150265
f(until_nul(std::slice::from_ref(&*b)))
151266
}
152-
PtrKind::Reinterpreted(_) => f(&self.to_c_string_iterator().collect::<Vec<u8>>()),
267+
PtrKind::Reinterpreted(_) => f(&self.to_c_string_iterator().collect::<Vec<T>>()),
153268
}
154269
}
155270

@@ -161,16 +276,46 @@ impl Ptr<u8> {
161276
// Copies the C string, excluding the terminating NUL, using a single
162277
// allocation. The vector has room for one more byte, which is what callers
163278
// usually append (a NUL or a newline).
164-
pub fn to_c_bytes(&self) -> Vec<u8> {
279+
pub fn to_c_bytes(&self) -> Vec<T> {
165280
self.with_c_str(|s| {
166281
let mut bytes = Vec::with_capacity(s.len() + 1);
167282
bytes.extend_from_slice(s);
168283
bytes
169284
})
170285
}
171286

287+
// Copies `bytes` to the buffer pointed to, without adding a NUL.
288+
pub fn write_c_bytes(&self, bytes: &[u8]) {
289+
self.with_slice_mut(bytes.len(), |s| {
290+
for (d, &b) in s.iter_mut().zip(bytes) {
291+
*d = T::from_byte(b);
292+
}
293+
})
294+
}
295+
296+
// Allocates a heap copy of `bytes` followed by a NUL, like `strdup`.
297+
pub fn alloc_c_str(bytes: &[u8]) -> Self {
298+
Ptr::alloc_array(
299+
bytes
300+
.iter()
301+
.map(|&b| T::from_byte(b))
302+
.chain(std::iter::once(T::default()))
303+
.collect(),
304+
)
305+
}
306+
307+
// Like `with_c_str`, with the C string viewed as bytes.
308+
pub fn with_c_bytes<R>(&self, f: impl FnOnce(&[u8]) -> R) -> R {
309+
self.with_c_str(|s| T::with_u8_slice(s, f))
310+
}
311+
312+
// Like `to_c_bytes`, as bytes.
313+
pub fn to_c_u8_bytes(&self) -> Vec<u8> {
314+
T::into_byte_vec(self.to_c_bytes())
315+
}
316+
172317
pub fn to_rust_string(&self) -> String {
173-
self.with_c_str(|s| String::from_utf8_lossy(s).into_owned())
318+
self.with_c_bytes(|s| String::from_utf8_lossy(s).into_owned())
174319
}
175320
}
176321

@@ -228,6 +373,24 @@ mod tests {
228373
assert_eq!(p.to_rust_string(), "");
229374
}
230375

376+
#[test]
377+
fn signed_char_strings() {
378+
let p = Ptr::<i8>::from_string_literal(b"\xe9a");
379+
assert_eq!(p.read(), -23);
380+
assert_eq!(p.c_str_len(), 2);
381+
assert_eq!(p.to_c_bytes(), [-23, b'a' as i8]);
382+
assert_eq!(p.to_c_u8_bytes(), b"\xe9a");
383+
// memcmp compares bytes as unsigned char.
384+
let q = Ptr::<i8>::from_string_literal(b"a");
385+
assert!(p.memcmp(&q, 1) > 0);
386+
387+
let buf = Ptr::<i8>::alloc_c_str(b"xyz");
388+
buf.write_c_bytes(b"\xff");
389+
assert_eq!(buf.read(), -1);
390+
assert_eq!(buf.to_c_u8_bytes(), b"\xffyz");
391+
buf.delete();
392+
}
393+
231394
#[test]
232395
#[should_panic(expected = "ub: unterminated string")]
233396
fn unterminated_string_panics() {

‎libcc2rs/src/format.rs‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ pub fn format_c(fmt: &str, va: &[VaArg]) -> String {
6868
},
6969
ConversionType::Char => Arg::Char(i32::get(arg) as u8 as char),
7070
ConversionType::String => match arg {
71-
VaArg::Ptr(v) => Arg::Str(v.reinterpret_cast::<u8>().to_rust_string()),
71+
VaArg::Ptr(v) => Arg::Str(v.reinterpret_cast::<i8>().to_rust_string()),
7272
_ => panic!("format_c: %s expects a string argument"),
7373
},
7474
ConversionType::DecFloatLower
@@ -95,7 +95,7 @@ mod tests {
9595
use crate::Ptr;
9696

9797
fn s(lit: &'static [u8]) -> VaArg {
98-
Ptr::<u8>::from_string_literal(lit).into()
98+
Ptr::<i8>::from_string_literal(lit).into()
9999
}
100100

101101
#[test]

0 commit comments

Comments
 (0)