В Rust - почти всё с чем пришлось столкнуться, ужасно сыро (идеально для ржавчины ? ).
пример bindgen
let _bindings = bindgen::Builder::default()
.header("D:\\BlackBox\\Generator\\Code\\C\\BitsUtil.h")
.header("D:\\BlackBox\\Generator\\Code\\C\\Host.h")
.....
если добавление header -ов поменять местами - bindgen упадет.
если переключиться на gnu toolchain - bindgen упадет.
если в С коде попадутся совершенно невинные магические комбинации кода - bindgen упадет.
…
компилятор молча компилирует, и даже файл black_box.lib и все сопутствующие появляется… но компилятор Rust не может его открыть … мрак.
отложил пока.
обнаружилось кое что поважнее…
желание не переписывать С-шный код и работать с ним через FFi это конечно хорошо, но не в случае с inline функциями.
а у меня при генерации кода они активно используются. поскольку полученный пакет это обычный байтовый массив, а предложенные пользователю интерфейсы в виде удобных полей - это куча микро функций типа
fn get_bytes(src: &[u8], byte: usize, bytes: usize) -> u64
fn set_bytes(src: u64, bytes: usize, dst: &mut [u8], byte: usize)
fn set_bits(src: u64, bits: usize, dst: &mut [u8], bit: usize)
fn get_bits(src: &[u8], bit: usize, bits: usize) -> u64
и если они на Rust будут не инлайнами - это плохо.
поэтому ту часть С функций, которая будет использована на стороне сгенеренного Rust кода придется переписать на Rust.
что и сделал.
было на С
const uint8_t Ol[] = {0x00, 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff};
const uint8_t lO[] = {0x00, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff};
#define INLINER inline;
INLINER UMAX get_bits(const uint8_t* src, size_t bit, size_t bits)
{
src += bit >> 3;
bit &= 7;
if (sizeof(UMAX) < bits >> 3) bits = sizeof(UMAX) << 3;
else if (bit + bits < 9) return *src >> bit & Ol[bits];
UMAX dst = 0;
memcpy(&dst, src, bit + bits >> 3);
dst >>= bit;
src += bit + bits >> 3;
bit = bit + bits & 7;
if (0 < bit) dst |= (UMAX)(*src & Ol[bit]) << bits - bit;
return dst;
}
INLINER void set_bits(UMAX src, size_t bits, uint8_t* dst, size_t bit)
{
dst += bit >> 3;
bit &= 7;
if (8 < bit + bits)
{
if (bit)
{
*dst = *dst & Ol[bit] | (src & Ol[ 8 - bit]) << bit;
dst++;
src >>= 8 - bit;
bits -= 8 - bit;
}
memcpy(dst, &src, bits >> 3);
if (bits & 7)
{
dst += bits >> 3;
*dst = *dst & lO[8 - (bits & 7)] | ((uint8_t*)&src)[(bits >> 3)] & Ol[bits & 7];
}
}
else * dst = *dst & (Ol[bit] | lO[8 - bit - bits]) | (src & Ol[bits]) << bit;
}
INLINER UMAX get_bytes(uint8_t* src, size_t byte, size_t bytes)
{
#ifdef UINT64_MAX
int32_t hi = 0;
#endif
int32_t lo = 0;
switch (bytes)
{
#ifdef UINT64_MAX
case 8:
hi |= (src[byte + 7] & 0xFF) << 24;
case 7:
hi |= (src[byte + 6] & 0xFF) << 16;
case 6:
hi |= (src[byte + 5] & 0xFF) << 8;
case 5:
hi |= src[byte + 4] & 0xFF;
#endif
case 4:
lo |= (src[byte + 3] & 0xFF) << 24;
case 3:
lo |= (src[byte + 2] & 0xFF) << 16;
case 2:
lo |= (src[byte + 1] & 0xFF) << 8;
case 1:
lo |= src[byte] & 0xFF;
}
return
#ifdef UINT64_MAX
(hi & 0xFFFFFFFFLL) << 32 |
#endif
lo & 0xFFFFFFFFLL;
}
INLINER int32_t set_bytes(const UMAX src, const size_t bytes, uint8_t* dst, const size_t byte)
{
#ifdef UINT64_MAX
uint32_t hi = (uint32_t)(src >> 32);
#endif
uint32_t lo = (uint32_t)(src & 0xFFFFFFFFL);
switch (bytes)
{
#ifdef UINT64_MAX
case 8:
dst[byte + 7] = hi >> 24;
case 7:
dst[byte + 6] = hi >> 16;
case 6:
dst[byte + 5] = hi >> 8;
case 5:
dst[byte + 4] = hi & 0xFF;
#endif
case 4:
dst[byte + 3] = lo >> 24;
case 3:
dst[byte + 2] = lo >> 16;
case 2:
dst[byte + 1] = lo >> 8;
case 1:
dst[byte] = lo & 0xFF;
}
return byte + bytes;
}
стало на Rust
use std::mem::transmute;
use std::ptr::copy_nonoverlapping;
const Ol: [u64; 9] = [0x00, 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff];
const lO: [u64; 9] = [0x00, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff];
#[inline(always)]
fn set_bytes(src: u64, bytes: usize, dst: &mut [u8], byte: usize) { unsafe { copy_nonoverlapping(transmute::<&u64, &u8>(&src), dst[byte..].as_mut_ptr(), bytes); } }
#[inline(always)]
fn get_bytes(src: &[u8], byte: usize, bytes: usize) -> u64 {
let mut dst = 0u64;
unsafe { copy_nonoverlapping(src[byte..].as_ptr(), transmute::<&mut u64, &mut u8>(&mut dst), bytes); }
dst
}
#[inline(always)]
fn set_bits(src: u64, bits: usize, dst: &mut [u8], bit: usize) {
let mut src = src;
let dst = &mut dst[bit >> 3..];
let bit = bit & 7;
let mut bits = bits;
if 8 < bit + bits
{
let dst = if 0 < bit
{
dst[0] = (dst[0] as u64 & Ol[bit] | (src & Ol[ 8 - bit ]) << bit) as u8;
src >>= 8 - bit;
bits -= 8 - bit;
&mut dst[1..]
} else { dst };
set_bytes(src, bits >> 3, dst, 0);
if 0 < bits & 7
{
let dst = &mut dst[bits >> 3..];
dst[0] = (dst[0] as u64 & lO[8 - (bits & 7)] | ((src >> ((bits >> 3) << 3)) & 0xFF) & Ol[bits & 7]) as u8;
}
} else { dst[0] = (dst[0] as u64 & (Ol[bit] | lO[8 - bit - bits]) | (src as u64 & Ol[bits]) << bit) as u8; }
}
#[inline(always)]
fn get_bits(src: &[u8], bit: usize, bits: usize) -> u64 {
let src = &src[bit >> 3..];
let bit = bit & 7;
let mut bits = bits;
if 8 < bits >> 7 { bits = 8 << 3; } else if bit + bits < 9 { return (src[0] >> bit) as u64 & Ol[bits]; }
let mut dst = get_bytes(src, 0, bit + bits >> 3);
dst >>= bit;
let src = &src[bit + bits >> 3..];
let bit = bit + bits & 7;
if 0 < bit { dst |= ((src[0] as u64 & Ol[bit])) << bits - bit; }
dst
}
extern crate rand;
#[cfg(test)]
#[test]
fn it_works() {
use rand::prelude::*;
let mut rng = thread_rng();
let mut buff = [0; 1000];
for i in 0..100000
{
let val = rng.gen::<u64>();
let len = rng.gen_range(1, 8);
let pos = rng.gen_range(0, buff.len() - len);
let expected = val & ((1u64 << (len * 8)) - 1);
set_bytes(val, len, buff.as_mut(), pos);
assert!(expected == get_bytes(&buff, pos, len), "val == get_bytes( &buff, {} , {} )", pos, len);
assert!(expected == get_bits(&buff, pos * 8, len * 8), " val == get_bits( &buff, {} , {} )", pos, len);
for i in buff.iter_mut() { *i = 0; }
set_bits(val, len * 8, buff.as_mut(), pos * 8);
assert!(expected == get_bytes(&buff, pos, len), "2 val == get_bytes( &buff, {} , {} )", pos, len);
assert!(expected == get_bits(&buff, pos * 8, len * 8), "2 val == get_bits( &buff, {} , {} )", pos * 8, len * 8);
//println!("val={} , pos={}, len={}", val, pos, len);
let len = rng.gen_range(1, 64);
let pos = rng.gen_range(0, buff.len() * 8 - len);
let expected = val & ((1u64 << len) - 1);
for i in buff.iter_mut() { *i = 0; }
set_bits(val, len, buff.as_mut(), pos);
assert_eq!(expected, get_bits(&buff, pos, len), " val == get_bits( &buff, {} , {} )", pos, len);
}
}
все тесты пройдены.
предложения / замечания ?