我正在尝试定义一组16位切片(Rust:&[u16]
),它们是有效的WTF-8(重新编码时),但不是有效的UTF-8(重新编码时) ,这样我就可以随机生成这样的切片。这是为了在Windows机器上生成所有可能的std::ffi::OsString
,而不会解析为String
。
转化&[u16] -> OsString
是通过std::os::windows::ffi::OsStringExt::from_wide
完成的。这会重定向到libstd/sys_common/wtf8.rs
,将操作定义为:
/// Creates a WTF-8 string from a potentially ill-formed UTF-16 slice of 16-bit code units.
///
/// This is lossless: calling `.encode_wide()` on the resulting string
/// will always return the original code units.
pub fn from_wide(v: &[u16]) -> Wtf8Buf {
let mut string = Wtf8Buf::with_capacity(v.len());
for item in char::decode_utf16(v.iter().cloned()) {
match item {
Ok(ch) => string.push_char(ch),
Err(surrogate) => {
let surrogate = surrogate.unpaired_surrogate();
// Surrogates are known to be in the code point range.
let code_point = unsafe {
CodePoint::from_u32_unchecked(surrogate as u32)
};
// Skip the WTF-8 concatenation check,
// surrogate pairs are already decoded by decode_utf16
string.push_code_point_unchecked(code_point)
}
}
}
string
}
转化OsString -> Result<String, Wtf8Buf>
是通过into_string
在同一个文件中完成的:
/// Consumes the WTF-8 string and tries to convert it to UTF-8.
///
/// This does not copy the data.
///
/// If the contents are not well-formed UTF-8
/// (that is, if the string contains surrogates),
/// the original WTF-8 string is returned instead.
pub fn into_string(self) -> Result<String, Wtf8Buf> {
match self.next_surrogate(0) {
None => Ok(unsafe { String::from_utf8_unchecked(self.bytes) }),
Some(_) => Err(self),
}
}
将next_surrogate
定义为:
#[inline]
fn next_surrogate(&self, mut pos: usize) -> Option<(usize, u16)> {
let mut iter = self.bytes[pos..].iter();
loop {
let b = *iter.next()?;
if b < 0x80 {
pos += 1;
} else if b < 0xE0 {
iter.next();
pos += 2;
} else if b == 0xED {
match (iter.next(), iter.next()) {
(Some(&b2), Some(&b3)) if b2 >= 0xA0 => {
return Some((pos, decode_surrogate(b2, b3)))
}
_ => pos += 3
}
} else if b < 0xF0 {
iter.next();
iter.next();
pos += 3;
} else {
iter.next();
iter.next();
iter.next();
pos += 4;
}
}
}
我想要做的是设计一种生成Vec<u16>
的算法,使OsString::from_wide(vec.as_slice()).into_string().unwrap_err()
永远不会恐慌,并返回OsString
。 OsString
s的集合当然应该是最大的,而不是使用一个小的常数。
为了做到这一点,为了简化,我们可以定义两个操作:
encode_wide : &[u8] -> &[u16]
valid_wtf8_invalid_utf8 : () -> Gen<Vec<u8>>
其中Gen
是某种用于生成类型化随机数据的monad。通过将valid_wtf8_invalid_utf8()
给出的仿函数与encode_wide
进行映射,我们可以得到Gen<Vec<u16>>
,我们可能会得到Gen<OsString>
。
但是 - 我不确定如何定义操作encode_wide
和valid_wtf8_invalid_utf8
。是否有一些我可以采取的更直接的方法,而不是扭转给定函数的逻辑?
由于Gen
是抽象的,我不希望获得可执行代码 - 但伪代码或其他高级指令会很简洁。谢谢=)
答案 0 :(得分:1)
我是否完全清楚您是否要在WTF-16 \ UTF-16
或WTF-8 \ UTF-8
中生成字符串。我认为生成一个不是有效的UTF-16的WTF-16字符串可能更容易,所以在这里:
您需要确保至少有一个(16位)&#34;字符&#34;是一个代理人,不是代理人的一部分。 (此示例也可能在字符串中生成NUL
个字符。)
extern crate rand;
use rand::Rng;
pub fn gen_wtf16_invalid_utf16<R>(r: &mut R, len: usize) -> Vec<u16>
where
R: Rng,
{
assert!(len > 0);
let mut buf = Vec::with_capacity(len);
for _ in 0..len {
buf.push(r.next_u32() as u16);
}
// make element at position `p` a surrogate that is not part
// of a surrogate pair
let p = r.gen_range(0, len-1);
// if first elem or previous entry is not a leading surrogate
let gen_trail = (0 == p) || (0xd800 != buf[p-1] & 0xfc00);
// if last element or succeeding entry is not a traililng surrogate
let gen_lead = (p == len-1) || (0xdc00 != buf[p+1] & 0xfc00);
let (force_bits_mask, force_bits_value) = if gen_trail {
if gen_lead {
// trailing or leading surrogate
(0xf800, 0xd800)
} else {
// trailing surrogate
(0xfc00, 0xdc00)
}
} else {
// leading surrogate
debug_assert!(gen_lead);
(0xfc00, 0xd800)
};
debug_assert_eq!(0, (force_bits_value & !force_bits_mask));
buf[p] = (buf[p] & !force_bits_mask) | force_bits_value;
buf
}
fn main() {
let s = gen_wtf16_invalid_utf16(&mut rand::thread_rng(), 10);
for c in &s {
println!("0x{:04x}", c);
}
}