Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to update libc::c_char array with String?

Tags:

encoding

rust

ffi

I have write some FFI code to C/C++,

use libc::c_char;
use std::ffi::CString;

type arr_type = [c_char; 20]; // arr_type is the type in C
let mut arr : arr_type = [0; 20]; 

let s = "happy123";
let c_s = CString::new(s).unwrap();
let s_ptr = c_s.as_ptr();

How can I update arr with the String s? In C/C++ I can use memcpy, strcpy etc...

I have tried many, like use rlibc::memcpy and found it can't be used with libc.. , but the compiler don't let me pass, there is very few info about array in Rust.


Add: After read the replys, I want to add some info and more question.

1.

In C++, I use strcpy_s to copy string to char array, cause the length of string and the size of array are all known.

I have tried both methods below.

the std::iter::Zip method, seems very like strcpy_s, but I don't know if there is some performance affect.

the copy_nonoverlapping method, it use as_mut_ptr() cast the array to pointer then there is no length info, since it is in unsafe { } block, and I have tried copy a string longer then the array and there is no error displays... I wonder if that's ok?

And is there a function in Rust like strcpy_s in C++?

2.

I'm using windows and msvc, for the char array, I mean not deal with encoding or use default codepage encoding.

below are all ok in source file:

C++:

std::string s = "world is 世界";
std::wstring ws = L"world is 世界";

Qt:

QString qs = QStringLiteral("world is 世界");

Python 3:

s = 'world is 世界'

But in Rust, below may be wrong? as I see in Eclipse Debug window.

let s = "world is 世界";

I found rust-encoding and tried these:

use encoding::{Encoding, EncoderTrap};
use encoding::all::GB18030;

let s = "world is 世界";  
let enc = GB18030.encode(&s , EncoderTrap::Strict);

Is there any better way to do in Rust?

like image 202
sbant Avatar asked Mar 16 '23 15:03

sbant


2 Answers

Here's one solution that doesn't require unsafe code, but unfortunately most of the methods are marked unstable.

#![feature(libc)]
#![feature(core)]
#![feature(collections)]

extern crate libc;

use libc::c_char;
use std::ffi::CString;
use std::slice::IntSliceExt;

type arr_type = [c_char; 20];

fn main() {
    let mut c_string: arr_type = [0; 20]; 
    let value = CString::new("happy123").unwrap();

    c_string.clone_from_slice(value.as_bytes_with_nul().as_signed());
}
like image 101
Shepmaster Avatar answered Mar 23 '23 23:03

Shepmaster


I suggest updating every character on its own by iterating over the array and the string simultaneously, and assigning the string characters to the array characters. I added the final \0 to the Rust-string.

#![feature(libc)]
extern crate libc;

fn main() {
    use libc::c_char;

    type ArrType = [c_char; 20]; // arr_type is the type in C
    let mut arr : ArrType = [0; 20]; 

    let s = "happy123\0";
    assert!(s.len() <= arr.len());
    for (a, c) in arr.iter_mut().zip(s.bytes()) {
        *a = c as i8;
    }
}

Try it out in the PlayPen


in most cases llvm will optimize that loop to a memcopy.

define internal void @_ZN4main20hf4c098c7157f3263faaE() unnamed_addr #0 {
entry-block:
  %0 = alloca %"2.core::str::Bytes", align 8
  %arg4 = alloca %str_slice, align 8
  %1 = bitcast %"2.core::str::Bytes"* %0 to i8*
  call void @llvm.lifetime.start(i64 16, i8* %1)
  %2 = bitcast %str_slice* %arg4 to i8*
  call void @llvm.lifetime.start(i64 16, i8* %2)
  call void @llvm.memcpy.p0i8.p0i8.i64(i8* %2, i8* bitcast (%str_slice* @const26 to i8*), i64 16, i32 8, i1 false)
  call void @_ZN3str3str5bytes20h68b1cf722a654e56XOgE(%"2.core::str::Bytes"* noalias nocapture sret dereferenceable(16) %0, %str_slice* noalias nocapture dereferenceable(16) %arg4)
  call void @llvm.lifetime.end(i64 16, i8* %2)
  call void @llvm.lifetime.end(i64 16, i8* %1) #3, !alias.scope !0, !noalias !3
  call void @llvm.lifetime.end(i64 16, i8* %1)
  ret void
}
like image 22
oli_obk Avatar answered Mar 23 '23 23:03

oli_obk