extern crate libc;
use libc::{E2BIG, EILSEQ, EINVAL};
use std::{
ffi::CString,
io,
io::{Read, Result, Write},
iter, mem, ptr, str,
};
use libc::{c_char, c_int, c_void, size_t};
#[allow(dead_code)]
const DEFAULT_BUF_SIZE: usize = 64 * 1024;
#[allow(non_camel_case_types)]
type iconv_t = *mut c_void;
lazy_static! {
static ref LIBICONV: libloading::Library = libloading::Library::new("libiconv.dll").unwrap();
}
pub(crate) struct Converter {
cd: iconv_t,
}
impl Converter {
pub fn new(from: &str, to: &str) -> Converter {
lazy_static! {
static ref ICONV_OPEN: extern "C" fn(__tocode: *const c_char, __fromcode: *const c_char) -> iconv_t =
unsafe { *LIBICONV.get("libiconv_open".as_bytes(),).unwrap() };
};
let from_encoding = CString::new(from).unwrap();
let to_encoding = CString::new(to).unwrap();
let handle = ICONV_OPEN(to_encoding.as_ptr(), from_encoding.as_ptr());
if handle as isize == -1 {
panic!(
"Error creating conversion descriptor from {:} to {:}",
from, to
);
}
Converter { cd: handle }
}
pub fn convert(&self, input: &[u8], output: &mut [u8]) -> (usize, usize, c_int) {
lazy_static! {
static ref ICONV: extern "C" fn(
__cd: iconv_t,
__inbuf: *mut *mut c_char,
__inbytesleft: *mut size_t,
__outbuf: *mut *mut c_char,
__outbytesleft: *mut size_t,
) -> size_t = unsafe { *LIBICONV.get("libiconv".as_bytes()).unwrap() };
};
let input_left = input.len() as size_t;
let output_left = output.len() as size_t;
if input_left > 0 && output_left > 0 {
let input_ptr = input.as_ptr();
let output_ptr = output.as_ptr();
let ret = unsafe {
ICONV(
self.cd,
mem::transmute(&input_ptr),
mem::transmute(&input_left),
mem::transmute(&output_ptr),
mem::transmute(&output_left),
)
};
let bytes_read = input.len() - input_left as usize;
let bytes_written = output.len() - output_left as usize;
return (
bytes_read,
bytes_written,
if ret as isize == -1 {
io::Error::last_os_error().raw_os_error().unwrap() as c_int
} else {
0
},
);
} else if input_left == 0 && output_left > 0 {
let output_ptr = output.as_ptr();
let ret = unsafe {
ICONV(
self.cd,
ptr::null_mut::<*mut c_char>(),
mem::transmute(&input_left),
mem::transmute(&output_ptr),
mem::transmute(&output_left),
)
};
let bytes_written = output.len() - output_left as usize;
return (
0,
bytes_written,
if ret as isize == -1 {
io::Error::last_os_error().raw_os_error().unwrap() as c_int
} else {
0
},
);
} else {
let ret = unsafe {
ICONV(
self.cd,
ptr::null_mut::<*mut c_char>(),
mem::transmute(&input_left),
ptr::null_mut::<*mut c_char>(),
mem::transmute(&output_left),
)
};
return (0, 0, ret as i32);
}
}
}
impl Drop for Converter {
fn drop(&mut self) {
lazy_static! {
static ref ICONV_CLOSE: extern "C" fn(__cd: iconv_t) -> c_int =
unsafe { *LIBICONV.get("libiconv_close".as_bytes()).unwrap() };
};
ICONV_CLOSE(self.cd);
}
}
pub struct IconvReader<R> {
inner: R,
conv: Converter,
buf: Vec<u8>,
read_pos: usize,
write_pos: usize,
err: Option<io::Error>,
tempbuf: Vec<u8>, }
impl<R: Read> IconvReader<R> {
#[allow(dead_code)]
pub fn new(r: R, from: &str, to: &str) -> IconvReader<R> {
let conv = Converter::new(from, to);
let mut buf = Vec::with_capacity(DEFAULT_BUF_SIZE);
buf.extend(iter::repeat(0).take(DEFAULT_BUF_SIZE));
IconvReader {
inner: r,
conv: conv,
buf: buf,
read_pos: 0,
write_pos: 0,
err: None,
tempbuf: Vec::new(), }
}
fn fill_buf(&mut self) {
if self.read_pos > 0 {
unsafe {
ptr::copy::<u8>(
self.buf.as_mut_ptr(),
mem::transmute(&self.buf[self.read_pos]),
self.write_pos - self.read_pos,
);
}
self.write_pos -= self.read_pos;
self.read_pos = 0;
}
match self.inner.read(&mut self.buf[self.write_pos..]) {
Ok(nread) => {
self.write_pos += nread;
},
Err(e) => {
self.err = Some(e);
},
}
}
}
impl<R: Read> Read for IconvReader<R> {
fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
if self.tempbuf.len() != 0 {
let mut nwrite = 0;
for slot in self.tempbuf.iter() {
buf[nwrite] = *slot;
nwrite += 1;
}
if nwrite < self.tempbuf.len() {
self.tempbuf = self.tempbuf[nwrite..].to_vec();
} else {
self.tempbuf = Vec::new();
}
return Ok(nwrite);
}
while self.write_pos == 0 || self.read_pos == self.write_pos {
match self.err {
Some(ref e) => {
return Err(io::Error::new(e.kind(), ""));
},
None => self.fill_buf(),
}
}
let (nread, nwrite, err) = self
.conv
.convert(&self.buf[self.read_pos..self.write_pos], buf);
self.read_pos += nread;
match err {
EILSEQ => {
return Err(io::Error::new(io::ErrorKind::InvalidInput, ""));
},
EINVAL => {
self.fill_buf();
return Ok(nwrite);
},
E2BIG => {
if nread == 0 && nwrite == 0 && buf.len() > 0 {
let mut tempbuf = Vec::with_capacity(8);
tempbuf.extend(iter::repeat(0u8).take(8));
assert!(self.tempbuf.is_empty());
let (nread, temp_nwrite, err) = self
.conv
.convert(&self.buf[self.read_pos..self.write_pos], &mut tempbuf[..]);
self.read_pos += nread;
let mut nwrite = 0;
for slot in tempbuf.iter() {
buf[nwrite] = *slot;
nwrite += 1
}
self.tempbuf = tempbuf[nwrite..temp_nwrite].to_vec();
match err {
EILSEQ => return Err(io::Error::new(io::ErrorKind::InvalidInput, "")),
_ => return Ok(nwrite),
}
}
return Ok(nwrite);
},
0 => {
return Ok(nwrite);
},
_ => unreachable!(),
}
}
}
#[allow(dead_code)]
pub struct IconvWriter<W> {
inner: W,
conv: Converter,
buf: Vec<u8>,
read_pos: usize,
write_pos: usize,
err: Option<io::Error>,
}
impl<W: Write> IconvWriter<W> {
#[allow(dead_code)]
pub fn new(r: W, from: &str, to: &str) -> IconvWriter<W> {
let conv = Converter::new(from, to);
let mut buf = Vec::with_capacity(DEFAULT_BUF_SIZE);
buf.extend(iter::repeat(0u8).take(DEFAULT_BUF_SIZE));
IconvWriter {
inner: r,
conv: conv,
buf: buf,
read_pos: 0,
write_pos: 0,
err: None,
}
}
}
impl<W: Write> Write for IconvWriter<W> {
fn write(&mut self, _buf: &[u8]) -> Result<usize> {
unimplemented!()
}
fn flush(&mut self) -> Result<()> {
unimplemented!()
}
}
pub fn convert_bytes(inbuf: &[u8], from: &str, to: &str) -> Option<Vec<u8>> {
let converter = Converter::new(from, to);
let mut outbuf_size = inbuf.len() * 2;
let mut total_nread = 0;
let mut total_nwrite = 0;
let mut outbuf = Vec::with_capacity(outbuf_size);
unsafe { outbuf.set_len(outbuf_size) };
while total_nread < inbuf.len() {
let (nread, nwrite, err) =
converter.convert(&inbuf[total_nread..], &mut outbuf[total_nwrite..]);
total_nread += nread;
total_nwrite += nwrite;
match err {
EINVAL | EILSEQ => return None,
E2BIG => {
outbuf_size += inbuf.len();
outbuf.reserve(outbuf_size);
unsafe { outbuf.set_len(outbuf_size) };
},
_ => (),
}
}
unsafe { outbuf.set_len(total_nwrite) };
outbuf.shrink_to_fit();
return Some(outbuf);
}
pub trait IconvEncodable {
fn encode_with_encoding(&self, encoding: &str) -> Option<Vec<u8>>;
}
impl<'a> IconvEncodable for &'a [u8] {
fn encode_with_encoding(&self, encoding: &str) -> Option<Vec<u8>> {
convert_bytes(*self, "UTF-8", encoding)
}
}
impl<'a> IconvEncodable for Vec<u8> {
fn encode_with_encoding(&self, encoding: &str) -> Option<Vec<u8>> {
convert_bytes(&self[..], "UTF-8", encoding)
}
}
impl<'a> IconvEncodable for &'a str {
fn encode_with_encoding(&self, encoding: &str) -> Option<Vec<u8>> {
return self.as_bytes().encode_with_encoding(encoding);
}
}
impl<'a> IconvEncodable for String {
fn encode_with_encoding(&self, encoding: &str) -> Option<Vec<u8>> {
return self.as_bytes().encode_with_encoding(encoding);
}
}
pub trait IconvDecodable {
fn decode_with_encoding(&self, encoding: &str) -> Option<String>;
}
impl<'a> IconvDecodable for &'a [u8] {
fn decode_with_encoding(&self, encoding: &str) -> Option<String> {
convert_bytes(*self, encoding, "UTF-8")
.and_then(|bs| str::from_utf8(&bs[..]).map(|s| s.to_string()).ok())
}
}
impl<'a> IconvDecodable for Vec<u8> {
fn decode_with_encoding(&self, encoding: &str) -> Option<String> {
convert_bytes(&self[..], encoding, "UTF-8")
.and_then(|bs| str::from_utf8(&bs[..]).map(|s| s.to_string()).ok())
}
}