一组WTF-8字符串,它们是格式错误的UTF-8,为16位片

时间:2017-12-11 08:50:11

标签: utf-8 rust

我正在尝试定义一组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()永远不会恐慌,并返回OsStringOsString 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_widevalid_wtf8_invalid_utf8。是否有一些我可以采取的更直接的方法,而不是扭转给定函数的逻辑?

由于Gen是抽象的,我不希望获得可执行代码 - 但伪代码或其他高级指令会很简洁。谢谢=)

1 个答案:

答案 0 :(得分:1)

我是否完全清楚您是否要在WTF-16 \ UTF-16WTF-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);
    }
}