#![allow(
clippy::cast_possible_truncation,
clippy::cast_sign_loss,
clippy::cast_ptr_alignment,
clippy::missing_safety_doc
)]
use std::ffi::CStr;
use std::os::raw::{c_char, c_int, c_uint, c_ulong, c_void};
use std::ptr;
use libc;
use crate::abi::allocator::{xmlFree, xmlMalloc, xmlRealloc};
use crate::abi::callbacks::{
xmlInputCloseCallback, xmlInputReadCallback, xmlOutputCloseCallback, xmlOutputWriteCallback,
};
use crate::abi::structs::{
_xmlBuf, _xmlBuffer, _xmlCharEncodingHandler, _xmlOutputBuffer, _xmlParserInputBuffer,
};
use crate::abi::types::{xmlChar, xmlCharEncoding, xmlCharPtr};
use crate::xml::encoding;
const DEFAULT_BUFFER_SIZE: c_uint = 4000;
const MIN_BUFFER_SIZE: c_uint = 256;
const XML_BUFFER_ALLOC_DOUBLEIT: c_int = 0;
const XML_BUFFER_ALLOC_EXACT: c_int = 1;
const XML_BUFFER_ALLOC_IMMUTABLE: c_int = 2;
pub(crate) fn buf_create(size: c_int) -> *mut _xmlBuffer {
let buf_size = if size <= 0 {
DEFAULT_BUFFER_SIZE
} else {
size as c_uint
};
let buf_size = buf_size.max(MIN_BUFFER_SIZE);
let buf = unsafe { xmlMalloc(size_of::<_xmlBuffer>()) as *mut _xmlBuffer };
if buf.is_null() {
return ptr::null_mut();
}
let content = unsafe { xmlMalloc(buf_size as usize) as *mut xmlChar };
if content.is_null() {
unsafe { xmlFree(buf as *mut c_void) };
return ptr::null_mut();
}
unsafe {
ptr::write(content, 0);
}
unsafe {
ptr::write(
buf,
_xmlBuffer {
content,
use_: 0,
size: buf_size,
alloc: XML_BUFFER_ALLOC_DOUBLEIT,
contentIO: content, },
);
}
buf
}
pub(crate) fn buf_create_static(str: *const xmlChar, size: c_int) -> *mut _xmlBuffer {
if str.is_null() {
return ptr::null_mut();
}
let len = if size <= 0 {
let mut len: c_uint = 0;
unsafe {
while *str.add(len as usize) != 0 {
len += 1;
}
}
len
} else {
size as c_uint
};
let buf = unsafe { xmlMalloc(size_of::<_xmlBuffer>()) as *mut _xmlBuffer };
if buf.is_null() {
return ptr::null_mut();
}
unsafe {
ptr::write(
buf,
_xmlBuffer {
content: str as *mut xmlChar,
use_: len,
size: len + 1, alloc: XML_BUFFER_ALLOC_IMMUTABLE,
contentIO: ptr::null_mut(),
},
);
}
buf
}
pub(crate) fn buf_free(buf: *mut _xmlBuffer) {
if buf.is_null() {
return;
}
unsafe {
let alloc = (*buf).alloc;
let content = (*buf).content;
let content_io = (*buf).contentIO;
if alloc != XML_BUFFER_ALLOC_IMMUTABLE {
let base = if !content_io.is_null() {
content_io
} else {
content
};
if !base.is_null() {
xmlFree(base as *mut c_void);
}
}
xmlFree(buf as *mut c_void);
}
}
pub(crate) fn buf_empty(buf: *mut _xmlBuffer) {
if buf.is_null() {
return;
}
unsafe {
(*buf).use_ = 0;
if !(*buf).content.is_null() {
ptr::write((*buf).content, 0);
}
}
}
pub(crate) fn buf_content(buf: *mut _xmlBuffer) -> *mut xmlChar {
if buf.is_null() {
return ptr::null_mut();
}
unsafe { (*buf).content }
}
pub(crate) fn buf_length(buf: *mut _xmlBuffer) -> c_int {
if buf.is_null() {
return -1;
}
unsafe { (*buf).use_ as c_int }
}
pub(crate) fn buf_add(buf: *mut _xmlBuffer, str: *const xmlChar, len: c_int) -> c_int {
if buf.is_null() || str.is_null() || len <= 0 {
return 0;
}
let len = len as c_uint;
let b = unsafe { &mut *buf };
if b.alloc == XML_BUFFER_ALLOC_IMMUTABLE {
return -1;
}
let needed = b.use_.saturating_add(len).saturating_add(1);
if needed > b.size {
let new_size = if b.alloc == XML_BUFFER_ALLOC_EXACT {
needed
} else {
let mut doubled = b.size.saturating_mul(2).max(MIN_BUFFER_SIZE);
while doubled < needed {
doubled = doubled.saturating_mul(2);
}
doubled
};
let new_content =
unsafe { xmlRealloc(b.content as *mut c_void, new_size as usize) as *mut xmlChar };
if new_content.is_null() {
return -1;
}
b.content = new_content;
b.contentIO = new_content; b.size = new_size;
}
unsafe {
ptr::copy_nonoverlapping(str, b.content.add(b.use_ as usize), len as usize);
}
b.use_ = b.use_.saturating_add(len);
unsafe {
ptr::write(b.content.add(b.use_ as usize), 0);
}
len as c_int
}
pub(crate) fn buf_cat(buf: *mut _xmlBuffer, str: *const xmlChar) -> c_int {
if buf.is_null() || str.is_null() {
return -1;
}
let len = unsafe {
let mut i: c_uint = 0;
while *str.add(i as usize) != 0 {
i += 1;
}
i
};
buf_add(buf, str, len as c_int)
}
pub(crate) fn buf_ccat(buf: *mut _xmlBuffer, c: xmlChar) -> c_int {
buf_add(buf, &c as *const xmlChar, 1)
}
pub(crate) fn buf_shrink(buf: *mut _xmlBuffer, len: c_uint) -> c_int {
if buf.is_null() {
return -1;
}
let b = unsafe { &mut *buf };
if b.use_ == 0 {
return 0;
}
b.use_ = if len >= b.use_ { 0 } else { b.use_ - len };
unsafe {
ptr::write(b.content.add(b.use_ as usize), 0);
}
b.use_ as c_int
}
pub(crate) fn buf_add_head(buf: *mut _xmlBuffer, str: *const xmlChar, len: c_int) -> c_int {
if buf.is_null() || str.is_null() || len <= 0 {
return -1;
}
let len = len as c_uint;
unsafe {
let b = &mut *buf;
let needed = b.use_.saturating_add(len).saturating_add(1);
if needed > b.size {
let new_size = needed.saturating_mul(2).max(MIN_BUFFER_SIZE);
let new_content =
xmlRealloc(b.content as *mut c_void, new_size as usize) as *mut xmlChar;
if new_content.is_null() {
return -1;
}
b.content = new_content;
b.contentIO = new_content;
b.size = new_size;
}
if b.use_ > 0 {
core::ptr::copy(b.content, b.content.add(len as usize), b.use_ as usize);
}
core::ptr::copy_nonoverlapping(str, b.content, len as usize);
b.use_ = b.use_.saturating_add(len);
*b.content.add(b.use_ as usize) = 0;
}
0
}
pub(crate) fn buf_grow(buf: *mut _xmlBuffer, size: c_uint) -> c_int {
if buf.is_null() {
return -1;
}
let b = unsafe { &mut *buf };
if size <= b.size {
return 0; }
let new_content =
unsafe { xmlRealloc(b.content as *mut c_void, size as usize) as *mut xmlChar };
if new_content.is_null() {
return -1;
}
b.content = new_content;
b.contentIO = new_content;
b.size = size;
0
}
pub(crate) fn xml_buf_create(size: c_int) -> *mut _xmlBuf {
let buf_size = if size <= 0 {
DEFAULT_BUFFER_SIZE
} else {
size as c_uint
};
let buf_size = buf_size.max(MIN_BUFFER_SIZE);
let buf = unsafe { xmlMalloc(size_of::<_xmlBuf>()) as *mut _xmlBuf };
if buf.is_null() {
return ptr::null_mut();
}
let content = unsafe { xmlMalloc(buf_size as usize) as *mut xmlChar };
if content.is_null() {
unsafe { xmlFree(buf as *mut c_void) };
return ptr::null_mut();
}
unsafe {
ptr::write(content, 0);
}
unsafe {
ptr::write(
buf,
_xmlBuf {
content,
use_: 0,
size: buf_size,
alloc: XML_BUFFER_ALLOC_DOUBLEIT,
error: 0,
buffer: 0,
io: 0,
},
);
}
buf
}
pub(crate) fn xml_buf_free(buf: *mut _xmlBuf) {
if buf.is_null() {
return;
}
unsafe {
if !(*buf).content.is_null() {
xmlFree((*buf).content as *mut c_void);
}
xmlFree(buf as *mut c_void);
}
}
pub(crate) fn xml_buf_content(buf: *mut _xmlBuf) -> *mut xmlChar {
if buf.is_null() {
return ptr::null_mut();
}
unsafe { (*buf).content }
}
pub(crate) fn xml_buf_length(buf: *mut _xmlBuf) -> c_int {
if buf.is_null() {
return -1;
}
unsafe { (*buf).use_ as c_int }
}
pub(crate) fn xml_buf_add(buf: *mut _xmlBuf, str: *const xmlChar, len: c_int) -> c_int {
if buf.is_null() || str.is_null() || len <= 0 {
return 0;
}
let len = len as c_uint;
let b = unsafe { &mut *buf };
let needed = b.use_.saturating_add(len).saturating_add(1);
if needed > b.size {
let new_size = needed.saturating_mul(2).max(MIN_BUFFER_SIZE);
let new_content =
unsafe { xmlRealloc(b.content as *mut c_void, new_size as usize) as *mut xmlChar };
if new_content.is_null() {
return -1;
}
b.content = new_content;
b.size = new_size;
}
unsafe {
ptr::copy_nonoverlapping(str, b.content.add(b.use_ as usize), len as usize);
}
b.use_ = b.use_.saturating_add(len);
unsafe {
ptr::write(b.content.add(b.use_ as usize), 0);
}
len as c_int
}
pub(crate) fn xml_buf_cat(buf: *mut _xmlBuf, str: *const xmlChar) -> c_int {
if buf.is_null() || str.is_null() {
return -1;
}
let len = unsafe {
let mut i: c_uint = 0;
while *str.add(i as usize) != 0 {
i += 1;
}
i
};
xml_buf_add(buf, str, len as c_int)
}
pub(crate) fn xml_buf_grow(buf: *mut _xmlBuf, size: c_uint) -> c_int {
if buf.is_null() {
return -1;
}
let b = unsafe { &mut *buf };
if size <= b.size {
return 0;
}
let new_content =
unsafe { xmlRealloc(b.content as *mut c_void, size as usize) as *mut xmlChar };
if new_content.is_null() {
return -1;
}
b.content = new_content;
b.size = size;
0
}
pub(crate) fn xml_buf_shrink(buf: *mut _xmlBuf, len: c_uint) -> c_int {
if buf.is_null() {
return -1;
}
let b = unsafe { &mut *buf };
if b.use_ == 0 {
return 0;
}
b.use_ = if len >= b.use_ { 0 } else { b.use_ - len };
unsafe {
ptr::write(b.content.add(b.use_ as usize), 0);
}
b.use_ as c_int
}
fn encoding_from_int(enc: c_int) -> xmlCharEncoding {
match enc {
-1 => xmlCharEncoding::XML_CHAR_ENCODING_ERROR,
0 => xmlCharEncoding::XML_CHAR_ENCODING_NONE,
1 => xmlCharEncoding::XML_CHAR_ENCODING_UTF8,
2 => xmlCharEncoding::XML_CHAR_ENCODING_UTF16LE,
3 => xmlCharEncoding::XML_CHAR_ENCODING_UTF16BE,
4 => xmlCharEncoding::XML_CHAR_ENCODING_UCS4LE,
5 => xmlCharEncoding::XML_CHAR_ENCODING_UCS4BE,
6 => xmlCharEncoding::XML_CHAR_ENCODING_EBCDIC,
7 => xmlCharEncoding::XML_CHAR_ENCODING_UCS4_2143,
8 => xmlCharEncoding::XML_CHAR_ENCODING_UCS4_3412,
9 => xmlCharEncoding::XML_CHAR_ENCODING_UCS2,
10 => xmlCharEncoding::XML_CHAR_ENCODING_8859_1,
11 => xmlCharEncoding::XML_CHAR_ENCODING_8859_2,
12 => xmlCharEncoding::XML_CHAR_ENCODING_8859_3,
13 => xmlCharEncoding::XML_CHAR_ENCODING_8859_4,
14 => xmlCharEncoding::XML_CHAR_ENCODING_8859_5,
15 => xmlCharEncoding::XML_CHAR_ENCODING_8859_6,
16 => xmlCharEncoding::XML_CHAR_ENCODING_8859_7,
17 => xmlCharEncoding::XML_CHAR_ENCODING_8859_8,
18 => xmlCharEncoding::XML_CHAR_ENCODING_8859_9,
19 => xmlCharEncoding::XML_CHAR_ENCODING_2022_JP,
20 => xmlCharEncoding::XML_CHAR_ENCODING_SHIFT_JIS,
21 => xmlCharEncoding::XML_CHAR_ENCODING_EUC_JP,
22 => xmlCharEncoding::XML_CHAR_ENCODING_ASCII,
_ => xmlCharEncoding::XML_CHAR_ENCODING_ERROR,
}
}
fn find_handler_for_encoding(enc: c_int) -> *mut _xmlCharEncodingHandler {
let enc_enum = encoding_from_int(enc);
if enc_enum == xmlCharEncoding::XML_CHAR_ENCODING_NONE
|| enc_enum == xmlCharEncoding::XML_CHAR_ENCODING_UTF8
|| enc_enum == xmlCharEncoding::XML_CHAR_ENCODING_ERROR
{
return ptr::null_mut();
}
if let Some(name) = encoding::encoding_name(enc_enum) {
let mut name_nul = name.to_vec();
name_nul.push(0);
let handler = encoding::find_encoding_handler(name_nul.as_ptr() as *const xmlChar);
if !handler.is_null() {
return handler;
}
}
ptr::null_mut()
}
fn allocate_input_buffer() -> *mut _xmlParserInputBuffer {
let buf =
unsafe { xmlMalloc(size_of::<_xmlParserInputBuffer>()) as *mut _xmlParserInputBuffer };
if buf.is_null() {
return ptr::null_mut();
}
unsafe {
ptr::write(
buf,
_xmlParserInputBuffer {
context: ptr::null_mut(),
readcallback: None,
closecallback: None,
encoder: ptr::null_mut(),
buffer: ptr::null_mut(),
raw: ptr::null_mut(),
compressed: 0,
error: 0,
rawconsumed: 0,
},
);
}
buf
}
pub(crate) fn input_buffer_create_mem(
buffer: *const c_char,
size: c_int,
enc: c_int,
) -> *mut _xmlParserInputBuffer {
if buffer.is_null() || size <= 0 {
return ptr::null_mut();
}
let buf = allocate_input_buffer();
if buf.is_null() {
return ptr::null_mut();
}
let raw_buf = buf_create(size);
if raw_buf.is_null() {
unsafe { xmlFree(buf as *mut c_void) };
return ptr::null_mut();
}
buf_add(raw_buf, buffer as *const xmlChar, size);
let handler = find_handler_for_encoding(enc);
if !handler.is_null() {
let out_buf = buf_create((size as c_uint).saturating_mul(3).max(MIN_BUFFER_SIZE) as c_int);
if out_buf.is_null() {
buf_free(raw_buf);
unsafe { xmlFree(buf as *mut c_void) };
return ptr::null_mut();
}
let written = encoding::char_enc_in(handler, out_buf, raw_buf);
if written < 0 {
buf_free(raw_buf);
buf_free(out_buf);
unsafe { xmlFree(buf as *mut c_void) };
return ptr::null_mut();
}
unsafe {
(*buf).encoder = handler as *mut c_void;
(*buf).buffer = out_buf as *mut c_void;
(*buf).raw = raw_buf as *mut c_void;
}
} else {
unsafe {
(*buf).buffer = raw_buf as *mut c_void;
(*buf).raw = raw_buf as *mut c_void;
}
}
buf
}
unsafe extern "C" fn file_read_callback(
context: *mut c_void,
buffer: *mut c_char,
len: c_int,
) -> c_int {
if context.is_null() || buffer.is_null() || len <= 0 {
return -1;
}
let fd = context as c_int;
let ret = libc::read(fd, buffer as *mut c_void, len as usize);
if ret < 0 {
return -1;
}
ret as c_int
}
unsafe extern "C" fn file_close_callback(context: *mut c_void) -> c_int {
if context.is_null() {
return -1;
}
let fd = context as c_int;
libc::close(fd)
}
pub(crate) fn input_buffer_create_file(
filename: *const c_char,
enc: c_int,
) -> *mut _xmlParserInputBuffer {
if filename.is_null() {
return ptr::null_mut();
}
let filename_str = unsafe {
match CStr::from_ptr(filename).to_str() {
Ok(s) => s,
Err(_) => return ptr::null_mut(),
}
};
let fd = unsafe {
let path_c = std::ffi::CString::new(filename_str).unwrap_or_default();
libc::open(path_c.as_ptr(), libc::O_RDONLY)
};
if fd < 0 {
return ptr::null_mut();
}
let mut stat_buf: libc::stat = unsafe { std::mem::zeroed() };
let stat_ret = unsafe {
let path_c = std::ffi::CString::new(filename_str).unwrap_or_default();
libc::stat(path_c.as_ptr(), &mut stat_buf)
};
let file_size = if stat_ret == 0 {
stat_buf.st_size as usize
} else {
0
};
let read_size = if file_size > 0 {
file_size
} else {
4096 };
let mut data = vec![0u8; read_size];
let mut total_read: isize = 0;
loop {
let remaining = read_size.saturating_sub(total_read as usize);
if remaining == 0 {
let new_size = read_size.saturating_mul(2);
data.resize(new_size, 0u8);
}
let ret = unsafe {
libc::read(
fd,
data.as_mut_ptr().add(total_read as usize) as *mut c_void,
remaining,
)
};
if ret < 0 {
unsafe { libc::close(fd) };
return ptr::null_mut();
}
if ret == 0 {
break;
}
total_read += ret as isize;
}
unsafe { libc::close(fd) };
data.truncate(total_read as usize);
if data.is_empty() {
return ptr::null_mut();
}
input_buffer_create_mem(data.as_ptr() as *const c_char, data.len() as c_int, enc)
}
pub(crate) fn input_buffer_create_io(
ioread: Option<xmlInputReadCallback>,
ioclose: Option<xmlInputCloseCallback>,
ioctx: *mut c_void,
enc: c_int,
) -> *mut _xmlParserInputBuffer {
let buf = allocate_input_buffer();
if buf.is_null() {
return ptr::null_mut();
}
let raw_buf = buf_create(DEFAULT_BUFFER_SIZE as c_int);
if raw_buf.is_null() {
unsafe { xmlFree(buf as *mut c_void) };
return ptr::null_mut();
}
unsafe {
(*buf).context = ioctx;
(*buf).readcallback = ioread;
(*buf).closecallback = ioclose;
(*buf).raw = raw_buf as *mut c_void;
}
let handler = find_handler_for_encoding(enc);
if !handler.is_null() {
let out_buf = buf_create(DEFAULT_BUFFER_SIZE as c_int);
if out_buf.is_null() {
buf_free(raw_buf);
unsafe { xmlFree(buf as *mut c_void) };
return ptr::null_mut();
}
unsafe {
(*buf).encoder = handler as *mut c_void;
(*buf).buffer = out_buf as *mut c_void;
}
} else {
unsafe {
(*buf).buffer = raw_buf as *mut c_void;
}
}
buf
}
pub(crate) fn input_buffer_create_fd(fd: c_int, enc: c_int) -> *mut _xmlParserInputBuffer {
if fd < 0 {
return ptr::null_mut();
}
input_buffer_create_io(
Some(file_read_callback as xmlInputReadCallback),
Some(file_close_callback as xmlInputCloseCallback),
fd as *mut c_void,
enc,
)
}
pub(crate) fn input_buffer_free(buf: *mut _xmlParserInputBuffer) {
if buf.is_null() {
return;
}
unsafe {
if let Some(close_cb) = (*buf).closecallback {
close_cb((*buf).context);
}
if !(*buf).raw.is_null() {
buf_free((*buf).raw as *mut _xmlBuffer);
}
if !(*buf).buffer.is_null() && (*buf).buffer != (*buf).raw {
buf_free((*buf).buffer as *mut _xmlBuffer);
}
xmlFree(buf as *mut c_void);
}
}
pub(crate) fn input_buffer_read(
buf: *mut _xmlParserInputBuffer,
buffer: *mut c_char,
len: c_int,
) -> c_int {
if buf.is_null() || buffer.is_null() || len <= 0 {
return -1;
}
let b = unsafe { &mut *buf };
if b.error != 0 {
return -1;
}
if let Some(read_cb) = b.readcallback {
let raw_buf = b.raw as *mut _xmlBuffer;
if raw_buf.is_null() {
return -1;
}
let mut tmp = vec![0u8; len as usize];
let ret = unsafe { read_cb(b.context, tmp.as_mut_ptr() as *mut c_char, len) };
if ret < 0 {
b.error = 1;
return -1;
}
if ret == 0 {
return 0;
}
buf_add(raw_buf, tmp.as_ptr() as *const xmlChar, ret);
if !b.encoder.is_null() {
let out_buf = b.buffer as *mut _xmlBuffer;
if out_buf.is_null() {
return -1;
}
let handler = b.encoder as *mut _xmlCharEncodingHandler;
let conv_ret = encoding::char_enc_in(handler, out_buf, raw_buf);
if conv_ret < 0 {
b.error = 1;
return -1;
}
let out_b = unsafe { &*out_buf };
let to_copy = (out_b.use_ as c_int).min(len);
if to_copy > 0 {
unsafe {
ptr::copy_nonoverlapping(
out_b.content,
buffer as *mut xmlChar,
to_copy as usize,
);
}
buf_shrink(out_buf, to_copy as c_uint);
}
return to_copy;
}
let raw_b = unsafe { &*raw_buf };
let to_copy = (raw_b.use_ as c_int).min(len);
if to_copy > 0 {
unsafe {
ptr::copy_nonoverlapping(raw_b.content, buffer as *mut xmlChar, to_copy as usize);
}
buf_shrink(raw_buf, to_copy as c_uint);
}
return to_copy;
}
let src_buf = b.buffer as *mut _xmlBuffer;
if src_buf.is_null() {
return -1;
}
let src = unsafe { &mut *src_buf };
if src.content.is_null() || src.use_ == 0 {
return 0;
}
let to_copy = (src.use_ as c_int).min(len);
if to_copy > 0 {
unsafe {
ptr::copy_nonoverlapping(src.content, buffer as *mut xmlChar, to_copy as usize);
}
unsafe {
src.content = src.content.add(to_copy as usize);
}
src.use_ = src.use_.saturating_sub(to_copy as c_uint);
}
to_copy
}
pub(crate) fn input_buffer_push(
buf: *mut _xmlParserInputBuffer,
buffer: *const c_char,
len: c_int,
) -> c_int {
if buf.is_null() || buffer.is_null() || len <= 0 {
return -1;
}
let b = unsafe { &mut *buf };
if b.error != 0 {
return -1;
}
let raw_buf = b.raw as *mut _xmlBuffer;
if raw_buf.is_null() {
return -1;
}
buf_add(raw_buf, buffer as *const xmlChar, len);
if !b.encoder.is_null() {
let out_buf = b.buffer as *mut _xmlBuffer;
if out_buf.is_null() {
return -1;
}
let handler = b.encoder as *mut _xmlCharEncodingHandler;
let ret = encoding::char_enc_in(handler, out_buf, raw_buf);
if ret < 0 {
b.error = 1;
return -1;
}
}
len
}
pub(crate) fn input_buffer_set_encoder(
buf: *mut _xmlParserInputBuffer,
handler: *mut _xmlCharEncodingHandler,
) {
if buf.is_null() {
return;
}
unsafe {
(*buf).encoder = handler as *mut c_void;
}
}
unsafe extern "C" fn file_write_callback(
context: *mut c_void,
buffer: *const c_char,
len: c_int,
) -> c_int {
if context.is_null() || buffer.is_null() || len <= 0 {
return -1;
}
let fd = context as c_int;
let ret = libc::write(fd, buffer as *const c_void, len as usize);
if ret < 0 {
return -1;
}
ret as c_int
}
unsafe extern "C" fn file_close_output_callback(context: *mut c_void) -> c_int {
if context.is_null() {
return -1;
}
let fd = context as c_int;
libc::close(fd)
}
unsafe extern "C" fn buffer_write_callback(
context: *mut c_void,
buffer: *const c_char,
len: c_int,
) -> c_int {
if context.is_null() || buffer.is_null() || len <= 0 {
return -1;
}
let target_buf = context as *mut _xmlBuffer;
buf_add(target_buf, buffer as *const xmlChar, len)
}
fn allocate_output_buffer() -> *mut _xmlOutputBuffer {
let buf = unsafe { xmlMalloc(size_of::<_xmlOutputBuffer>()) as *mut _xmlOutputBuffer };
if buf.is_null() {
return ptr::null_mut();
}
unsafe {
ptr::write(
buf,
_xmlOutputBuffer {
context: ptr::null_mut(),
writecallback: None,
closecallback: None,
encoder: ptr::null_mut(),
buffer: ptr::null_mut(),
conv: ptr::null_mut(),
written: 0,
error: 0,
},
);
}
buf
}
pub(crate) fn output_buffer_create_filename(
URI: *const c_char,
encoder: *mut _xmlCharEncodingHandler,
compression: c_int,
) -> *mut _xmlOutputBuffer {
if URI.is_null() {
return ptr::null_mut();
}
let path_str = unsafe {
match CStr::from_ptr(URI).to_str() {
Ok(s) => s,
Err(_) => return ptr::null_mut(),
}
};
let path_c = std::ffi::CString::new(path_str).unwrap_or_default();
let fd = unsafe {
libc::open(
path_c.as_ptr(),
libc::O_WRONLY | libc::O_CREAT | libc::O_TRUNC,
0o644,
)
};
if fd < 0 {
return ptr::null_mut();
}
let obuf = allocate_output_buffer();
if obuf.is_null() {
unsafe { libc::close(fd) };
return ptr::null_mut();
}
let buf = buf_create(DEFAULT_BUFFER_SIZE as c_int);
if buf.is_null() {
unsafe {
libc::close(fd);
xmlFree(obuf as *mut c_void);
}
return ptr::null_mut();
}
let conv_buf = buf_create(DEFAULT_BUFFER_SIZE as c_int);
if conv_buf.is_null() {
unsafe {
libc::close(fd);
buf_free(buf);
xmlFree(obuf as *mut c_void);
}
return ptr::null_mut();
}
unsafe {
(*obuf).context = fd as *mut c_void;
(*obuf).writecallback = Some(file_write_callback as xmlOutputWriteCallback);
(*obuf).closecallback = Some(file_close_output_callback as xmlOutputCloseCallback);
(*obuf).encoder = encoder as *mut c_void;
(*obuf).buffer = buf as *mut c_void;
(*obuf).conv = conv_buf as *mut c_void;
(*obuf).written = 0;
(*obuf).error = 0;
}
obuf
}
pub(crate) fn output_buffer_create_fd(
fd: c_int,
encoder: *mut _xmlCharEncodingHandler,
) -> *mut _xmlOutputBuffer {
if fd < 0 {
return ptr::null_mut();
}
let obuf = allocate_output_buffer();
if obuf.is_null() {
return ptr::null_mut();
}
let buf = buf_create(DEFAULT_BUFFER_SIZE as c_int);
if buf.is_null() {
unsafe { xmlFree(obuf as *mut c_void) };
return ptr::null_mut();
}
let conv_buf = buf_create(DEFAULT_BUFFER_SIZE as c_int);
if conv_buf.is_null() {
unsafe {
buf_free(buf);
xmlFree(obuf as *mut c_void);
}
return ptr::null_mut();
}
unsafe {
(*obuf).context = fd as *mut c_void;
(*obuf).writecallback = Some(file_write_callback as xmlOutputWriteCallback);
(*obuf).closecallback = Some(file_close_output_callback as xmlOutputCloseCallback);
(*obuf).encoder = encoder as *mut c_void;
(*obuf).buffer = buf as *mut c_void;
(*obuf).conv = conv_buf as *mut c_void;
(*obuf).written = 0;
(*obuf).error = 0;
}
obuf
}
pub(crate) fn output_buffer_create_io(
iowrite: Option<xmlOutputWriteCallback>,
ioclose: Option<xmlOutputCloseCallback>,
ioctx: *mut c_void,
encoder: *mut _xmlCharEncodingHandler,
) -> *mut _xmlOutputBuffer {
let obuf = allocate_output_buffer();
if obuf.is_null() {
return ptr::null_mut();
}
let buf = buf_create(DEFAULT_BUFFER_SIZE as c_int);
if buf.is_null() {
unsafe { xmlFree(obuf as *mut c_void) };
return ptr::null_mut();
}
let conv_buf = buf_create(DEFAULT_BUFFER_SIZE as c_int);
if conv_buf.is_null() {
unsafe {
buf_free(buf);
xmlFree(obuf as *mut c_void);
}
return ptr::null_mut();
}
unsafe {
(*obuf).context = ioctx;
(*obuf).writecallback = iowrite;
(*obuf).closecallback = ioclose;
(*obuf).encoder = encoder as *mut c_void;
(*obuf).buffer = buf as *mut c_void;
(*obuf).conv = conv_buf as *mut c_void;
(*obuf).written = 0;
(*obuf).error = 0;
}
obuf
}
pub(crate) fn output_buffer_create_buffer(
target_buf: *mut _xmlBuffer,
encoder: *mut _xmlCharEncodingHandler,
) -> *mut _xmlOutputBuffer {
if target_buf.is_null() {
return ptr::null_mut();
}
let obuf = allocate_output_buffer();
if obuf.is_null() {
return ptr::null_mut();
}
let internal_buf = buf_create(DEFAULT_BUFFER_SIZE as c_int);
if internal_buf.is_null() {
unsafe { xmlFree(obuf as *mut c_void) };
return ptr::null_mut();
}
let conv_buf = buf_create(DEFAULT_BUFFER_SIZE as c_int);
if conv_buf.is_null() {
unsafe {
buf_free(internal_buf);
xmlFree(obuf as *mut c_void);
}
return ptr::null_mut();
}
unsafe {
(*obuf).context = target_buf as *mut c_void;
(*obuf).writecallback = Some(buffer_write_callback as xmlOutputWriteCallback);
(*obuf).closecallback = None;
(*obuf).encoder = encoder as *mut c_void;
(*obuf).buffer = internal_buf as *mut c_void;
(*obuf).conv = conv_buf as *mut c_void;
(*obuf).written = 0;
(*obuf).error = 0;
}
obuf
}
pub(crate) fn output_buffer_flush(out: *mut _xmlOutputBuffer) -> c_int {
if out.is_null() {
return -1;
}
let ob = unsafe { &mut *out };
if ob.error != 0 {
return -1;
}
let buf = ob.buffer as *mut _xmlBuffer;
if buf.is_null() {
return 0;
}
let b = unsafe { &*buf };
if b.use_ == 0 {
return 0;
}
let write_cb = match ob.writecallback {
Some(cb) => cb,
None => {
buf_empty(buf);
return 0;
}
};
let total_written = if !ob.encoder.is_null() {
let handler = ob.encoder as *mut _xmlCharEncodingHandler;
let conv = ob.conv as *mut _xmlBuffer;
buf_empty(conv);
let ret = encoding::char_enc_out(handler, conv, buf);
if ret < 0 {
ob.error = 1;
return -1;
}
let conv_b = unsafe { &*conv };
if conv_b.use_ > 0 {
let written = unsafe {
write_cb(
ob.context,
conv_b.content as *const c_char,
conv_b.use_ as c_int,
)
};
if written < 0 {
ob.error = 1;
return -1;
}
ob.written = ob.written.saturating_add(written);
buf_empty(conv);
written
} else {
0
}
} else {
let written = unsafe { write_cb(ob.context, b.content as *const c_char, b.use_ as c_int) };
if written < 0 {
ob.error = 1;
return -1;
}
ob.written = ob.written.saturating_add(written);
written
};
buf_empty(buf);
total_written
}
pub(crate) fn output_buffer_close(out: *mut _xmlOutputBuffer) -> c_int {
if out.is_null() {
return -1;
}
let ob = unsafe { &mut *out };
let flush_ret = output_buffer_flush(out);
if let Some(close_cb) = ob.closecallback {
unsafe {
close_cb(ob.context);
}
}
if !ob.buffer.is_null() {
buf_free(ob.buffer as *mut _xmlBuffer);
}
if !ob.conv.is_null() {
buf_free(ob.conv as *mut _xmlBuffer);
}
unsafe { xmlFree(out as *mut c_void) };
flush_ret
}
pub(crate) fn output_buffer_write(
out: *mut _xmlOutputBuffer,
len: c_int,
data: *const c_char,
) -> c_int {
if out.is_null() || data.is_null() || len <= 0 {
return -1;
}
let ob = unsafe { &mut *out };
if ob.error != 0 {
return -1;
}
let buf = ob.buffer as *mut _xmlBuffer;
if buf.is_null() {
return -1;
}
let ret = buf_add(buf, data as *const xmlChar, len);
if ret < 0 {
ob.error = 1;
return -1;
}
len
}
pub(crate) fn output_buffer_write_string(out: *mut _xmlOutputBuffer, str: *const c_char) -> c_int {
if out.is_null() || str.is_null() {
return -1;
}
let len = unsafe {
let mut i: c_int = 0;
while *str.add(i as usize) != 0 {
i += 1;
}
i
};
output_buffer_write(out, len, str)
}
pub(crate) fn output_buffer_write_char(out: *mut _xmlOutputBuffer, c: c_char) -> c_int {
output_buffer_write(out, 1, &c as *const c_char)
}
pub(crate) fn output_buffer_get_content(out: *mut _xmlOutputBuffer) -> *const xmlChar {
if out.is_null() {
return ptr::null();
}
let ob = unsafe { &*out };
let buf = ob.buffer as *mut _xmlBuffer;
if buf.is_null() {
return ptr::null();
}
buf_content(buf)
}
pub(crate) fn check_file_exists(filename: *const c_char) -> c_int {
if filename.is_null() {
return -1;
}
let path_str = unsafe {
match CStr::from_ptr(filename).to_str() {
Ok(s) => s,
Err(_) => return -1,
}
};
let path_c = match std::ffi::CString::new(path_str) {
Ok(c) => c,
Err(_) => return -1,
};
let mut stat_buf: libc::stat = unsafe { std::mem::zeroed() };
let ret = unsafe { libc::stat(path_c.as_ptr(), &mut stat_buf) };
if ret == 0 {
1
} else {
0
}
}
pub(crate) fn read_file_to_memory(filename: *const c_char, size: *mut c_int) -> *mut c_char {
if filename.is_null() {
return ptr::null_mut();
}
let path_str = unsafe {
match CStr::from_ptr(filename).to_str() {
Ok(s) => s,
Err(_) => return ptr::null_mut(),
}
};
let path_c = match std::ffi::CString::new(path_str) {
Ok(c) => c,
Err(_) => return ptr::null_mut(),
};
let fd = unsafe { libc::open(path_c.as_ptr(), libc::O_RDONLY) };
if fd < 0 {
return ptr::null_mut();
}
let mut stat_buf: libc::stat = unsafe { std::mem::zeroed() };
let file_size = if unsafe { libc::stat(path_c.as_ptr(), &mut stat_buf) } == 0 {
stat_buf.st_size as usize
} else {
0
};
let chunk_size = 4096usize;
let initial_capacity = if file_size > 0 { file_size } else { chunk_size };
let mut data = Vec::with_capacity(initial_capacity);
let mut buf = vec![0u8; chunk_size];
loop {
let ret = unsafe { libc::read(fd, buf.as_mut_ptr() as *mut c_void, chunk_size) };
if ret < 0 {
unsafe { libc::close(fd) };
return ptr::null_mut();
}
if ret == 0 {
break; }
data.extend_from_slice(&buf[..ret as usize]);
}
unsafe { libc::close(fd) };
if data.is_empty() {
return ptr::null_mut();
}
let result = unsafe { xmlMalloc(data.len()) as *mut c_char };
if result.is_null() {
return ptr::null_mut();
}
unsafe {
ptr::copy_nonoverlapping(data.as_ptr(), result as *mut u8, data.len());
}
if !size.is_null() {
unsafe {
*size = data.len() as c_int;
}
}
result
}
pub(crate) fn write_memory_to_file(
filename: *const c_char,
data: *const c_char,
size: c_int,
) -> c_int {
if filename.is_null() || data.is_null() || size <= 0 {
return -1;
}
let path_str = unsafe {
match CStr::from_ptr(filename).to_str() {
Ok(s) => s,
Err(_) => return -1,
}
};
let path_c = match std::ffi::CString::new(path_str) {
Ok(c) => c,
Err(_) => return -1,
};
let fd = unsafe {
libc::open(
path_c.as_ptr(),
libc::O_WRONLY | libc::O_CREAT | libc::O_TRUNC,
0o644,
)
};
if fd < 0 {
return -1;
}
let mut remaining = size as usize;
let mut offset: usize = 0;
while remaining > 0 {
let ret = unsafe {
libc::write(
fd,
(data as *const u8).add(offset) as *const c_void,
remaining,
)
};
if ret < 0 {
unsafe { libc::close(fd) };
return -1;
}
let written = ret as usize;
remaining -= written;
offset += written;
}
unsafe { libc::close(fd) };
0
}
pub(crate) fn get_cwd() -> *mut c_char {
let mut size: usize = 1024;
loop {
let buf = unsafe { xmlMalloc(size) as *mut c_char };
if buf.is_null() {
return ptr::null_mut();
}
let ret = unsafe { libc::getcwd(buf as *mut c_char, size) };
if !ret.is_null() {
return buf;
}
unsafe { xmlFree(buf as *mut c_void) };
let err = std::io::Error::last_os_error();
if err.raw_os_error() == Some(libc::ERANGE) {
size = size.saturating_mul(2);
if size > 65536 {
return ptr::null_mut(); }
} else {
return ptr::null_mut();
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::ffi::CString;
use std::os::raw::c_char;
fn c(s: &str) -> CString {
CString::new(s).unwrap()
}
unsafe fn c_bytes(bytes: &[u8]) -> CString {
CString::from_vec_unchecked(bytes.to_vec())
}
fn i8_slice(s: &[u8]) -> &[i8] {
unsafe { std::slice::from_raw_parts(s.as_ptr() as *const i8, s.len()) }
}
#[test]
fn test_buf_create_free() {
let buf = buf_create(100);
assert!(!buf.is_null());
let b = unsafe { &*buf };
assert!(!b.content.is_null());
assert_eq!(b.use_, 0);
assert!(b.size >= 100);
assert_eq!(b.alloc, XML_BUFFER_ALLOC_DOUBLEIT);
unsafe {
assert_eq!(*b.content, 0);
}
buf_free(buf);
}
#[test]
fn test_buf_create_default_size() {
let buf = buf_create(0);
assert!(!buf.is_null());
let b = unsafe { &*buf };
assert!(b.size >= MIN_BUFFER_SIZE);
buf_free(buf);
}
#[test]
fn test_buf_create_static() {
let s: &[u8] = b"hello\0";
let buf = buf_create_static(s.as_ptr() as *const xmlChar, 5);
assert!(!buf.is_null());
let b = unsafe { &*buf };
assert_eq!(b.use_, 5);
assert_eq!(b.alloc, XML_BUFFER_ALLOC_IMMUTABLE);
unsafe {
assert_eq!(*b.content.offset(0), b'h');
assert_eq!(*b.content.offset(4), b'o');
assert_eq!(*b.content.offset(5), 0);
}
buf_free(buf); }
#[test]
fn test_buf_add() {
let buf = buf_create(10);
assert!(!buf.is_null());
let s1: &[u8] = b"Hello\0";
let ret = buf_add(buf, s1.as_ptr() as *const xmlChar, 5);
assert_eq!(ret, 5);
let b = unsafe { &*buf };
assert_eq!(b.use_, 5);
unsafe {
assert_eq!(*b.content.offset(0), b'H');
assert_eq!(*b.content.offset(4), b'o');
assert_eq!(*b.content.offset(5), 0); }
let s2: &[u8] = b" World!\0";
let ret = buf_add(buf, s2.as_ptr() as *const xmlChar, 7);
assert_eq!(ret, 7);
let b = unsafe { &*buf };
assert_eq!(b.use_, 12);
unsafe {
assert_eq!(*b.content.offset(6), b'W');
assert_eq!(*b.content.offset(11), b'!');
assert_eq!(*b.content.offset(12), 0);
}
buf_free(buf);
}
#[test]
fn test_buf_add_null() {
let buf = buf_create(10);
let ret = buf_add(buf, ptr::null(), 5);
assert_eq!(ret, 0);
buf_free(buf);
}
#[test]
fn test_buf_cat() {
let buf = buf_create(10);
let s: &[u8] = b"Hello\0";
let ret = buf_cat(buf, s.as_ptr() as *const xmlChar);
assert_eq!(ret, 5);
let b = unsafe { &*buf };
assert_eq!(b.use_, 5);
buf_free(buf);
}
#[test]
fn test_buf_ccat() {
let buf = buf_create(10);
let ret = buf_ccat(buf, b'A' as xmlChar);
assert_eq!(ret, 1);
let b = unsafe { &*buf };
assert_eq!(b.use_, 1);
unsafe {
assert_eq!(*b.content, b'A');
}
buf_free(buf);
}
#[test]
fn test_buf_empty() {
let buf = buf_create(10);
let s: &[u8] = b"Hello\0";
buf_add(buf, s.as_ptr() as *const xmlChar, 5);
assert_eq!(unsafe { &*buf }.use_, 5);
buf_empty(buf);
let b = unsafe { &*buf };
assert_eq!(b.use_, 0);
unsafe {
assert_eq!(*b.content, 0);
}
buf_free(buf);
}
#[test]
fn test_buf_content() {
let buf = buf_create(10);
let content = buf_content(buf);
assert!(!content.is_null());
buf_free(buf);
}
#[test]
fn test_buf_length() {
let buf = buf_create(10);
assert_eq!(buf_length(buf), 0);
let s: &[u8] = b"Hi\0";
buf_add(buf, s.as_ptr() as *const xmlChar, 2);
assert_eq!(buf_length(buf), 2);
buf_free(buf);
}
#[test]
fn test_buf_shrink() {
let buf = buf_create(10);
let s: &[u8] = b"Hello World\0";
buf_add(buf, s.as_ptr() as *const xmlChar, 11);
assert_eq!(buf_length(buf), 11);
buf_shrink(buf, 5);
assert_eq!(buf_length(buf), 6);
let b = unsafe { &*buf };
unsafe {
assert_eq!(*b.content.offset(6), 0); }
buf_shrink(buf, 100);
assert_eq!(buf_length(buf), 0);
buf_free(buf);
}
#[test]
fn test_buf_grow() {
let buf = buf_create(10);
assert!(unsafe { &*buf }.size >= 10);
let ret = buf_grow(buf, 1000);
assert_eq!(ret, 0);
assert!(unsafe { &*buf }.size >= 1000);
buf_free(buf);
}
#[test]
fn test_buf_free_null() {
buf_free(ptr::null_mut()); }
#[test]
fn test_xml_buf_create_free() {
let buf = xml_buf_create(100);
assert!(!buf.is_null());
let b = unsafe { &*buf };
assert!(!b.content.is_null());
assert_eq!(b.use_, 0);
assert!(b.size >= 100);
assert_eq!(b.error, 0);
assert_eq!(b.buffer, 0);
assert_eq!(b.io, 0);
xml_buf_free(buf);
}
#[test]
fn test_xml_buf_add() {
let buf = xml_buf_create(10);
let s: &[u8] = b"Hello\0";
let ret = xml_buf_add(buf, s.as_ptr() as *const xmlChar, 5);
assert_eq!(ret, 5);
let b = unsafe { &*buf };
assert_eq!(b.use_, 5);
xml_buf_free(buf);
}
#[test]
fn test_xml_buf_cat() {
let buf = xml_buf_create(10);
let s: &[u8] = b"Hello\0";
let ret = xml_buf_cat(buf, s.as_ptr() as *const xmlChar);
assert_eq!(ret, 5);
xml_buf_free(buf);
}
#[test]
fn test_xml_buf_content() {
let buf = xml_buf_create(10);
let content = xml_buf_content(buf);
assert!(!content.is_null());
xml_buf_free(buf);
}
#[test]
fn test_xml_buf_length() {
let buf = xml_buf_create(10);
assert_eq!(xml_buf_length(buf), 0);
let s: &[u8] = b"Hi\0";
xml_buf_add(buf, s.as_ptr() as *const xmlChar, 2);
assert_eq!(xml_buf_length(buf), 2);
xml_buf_free(buf);
}
#[test]
fn test_xml_buf_grow() {
let buf = xml_buf_create(10);
let ret = xml_buf_grow(buf, 500);
assert_eq!(ret, 0);
assert!(unsafe { &*buf }.size >= 500);
xml_buf_free(buf);
}
#[test]
fn test_xml_buf_shrink() {
let buf = xml_buf_create(10);
let s: &[u8] = b"Hello\0";
xml_buf_add(buf, s.as_ptr() as *const xmlChar, 5);
assert_eq!(xml_buf_length(buf), 5);
xml_buf_shrink(buf, 3);
assert_eq!(xml_buf_length(buf), 2);
xml_buf_free(buf);
}
#[test]
fn test_input_buffer_create_mem() {
let data = c("Hello XML");
let buf = input_buffer_create_mem(data.as_ptr(), 9, 0); assert!(!buf.is_null());
let b = unsafe { &*buf };
assert!(b.readcallback.is_none());
assert!(!b.buffer.is_null());
assert_eq!(b.error, 0);
let mut out = [0i8; 16];
let ret = input_buffer_read(buf, out.as_mut_ptr(), 16);
assert_eq!(ret, 9);
assert_eq!(&out[..9], i8_slice(b"Hello XML"));
input_buffer_free(buf);
}
#[test]
fn test_input_buffer_create_mem_empty() {
let buf = input_buffer_create_mem(ptr::null(), 0, 0);
assert!(buf.is_null());
}
#[test]
fn test_input_buffer_read_partial() {
let data = c("Hello XML World");
let buf = input_buffer_create_mem(data.as_ptr(), 15, 0);
assert!(!buf.is_null());
let mut out1 = [0i8; 5];
let ret = input_buffer_read(buf, out1.as_mut_ptr(), 5);
assert_eq!(ret, 5);
assert_eq!(&out1[..5], i8_slice(b"Hello"));
let mut out2 = [0i8; 10];
let ret = input_buffer_read(buf, out2.as_mut_ptr(), 10);
assert_eq!(ret, 10);
assert_eq!(&out2[..10], i8_slice(b" XML World"));
input_buffer_free(buf);
}
#[test]
fn test_input_buffer_push() {
let buf = input_buffer_create_io(None, None, ptr::null_mut(), 0);
assert!(!buf.is_null());
let data1 = c("<root>");
let ret = input_buffer_push(buf, data1.as_ptr(), 6);
assert_eq!(ret, 6);
let data2 = c("</root>");
let ret = input_buffer_push(buf, data2.as_ptr(), 7);
assert_eq!(ret, 7);
let mut out = [0i8; 32];
let ret = input_buffer_read(buf, out.as_mut_ptr(), 32);
assert_eq!(ret, 13);
assert_eq!(&out[..13], i8_slice(b"<root></root>"));
input_buffer_free(buf);
}
#[test]
fn test_input_buffer_set_encoder() {
let buf = input_buffer_create_mem(ptr::null(), 0, 0);
let data = c("test");
let buf = input_buffer_create_mem(data.as_ptr(), 4, 0);
assert!(!buf.is_null());
input_buffer_set_encoder(buf, ptr::null_mut());
let b = unsafe { &*buf };
assert!(b.encoder.is_null());
input_buffer_free(buf);
}
#[test]
fn test_output_buffer_create_buffer() {
let internal_buf = buf_create(100);
assert!(!internal_buf.is_null());
let obuf = output_buffer_create_buffer(internal_buf, ptr::null_mut());
assert!(!obuf.is_null());
let data = c("Hello Output");
let ret = output_buffer_write(obuf, 12, data.as_ptr());
assert_eq!(ret, 12);
let flushed = output_buffer_flush(obuf);
assert_eq!(flushed, 12);
let content = output_buffer_get_content(obuf);
assert!(content.is_null() || unsafe { *content } == 0);
let ctx = unsafe { (*obuf).context as *mut _xmlBuffer };
let ctx_b = unsafe { &*ctx };
assert_eq!(ctx_b.use_, 12);
unsafe {
assert_eq!(*ctx_b.content.offset(0), b'H' as xmlChar);
}
output_buffer_close(obuf);
}
#[test]
fn test_output_buffer_write_string() {
let internal_buf = buf_create(100);
let obuf = output_buffer_create_buffer(internal_buf, ptr::null_mut());
assert!(!obuf.is_null());
let s = c("Hello");
let ret = output_buffer_write_string(obuf, s.as_ptr());
assert_eq!(ret, 5);
output_buffer_close(obuf);
}
#[test]
fn test_output_buffer_write_char() {
let internal_buf = buf_create(100);
let obuf = output_buffer_create_buffer(internal_buf, ptr::null_mut());
assert!(!obuf.is_null());
let ret = output_buffer_write_char(obuf, b'X' as c_char);
assert_eq!(ret, 1);
output_buffer_close(obuf);
}
#[test]
fn test_output_buffer_get_content() {
let internal_buf = buf_create(100);
let obuf = output_buffer_create_buffer(internal_buf, ptr::null_mut());
assert!(!obuf.is_null());
let content = output_buffer_get_content(obuf);
assert!(!content.is_null());
output_buffer_close(obuf);
}
#[test]
fn test_check_file_exists() {
let exists = check_file_exists(c("/dev/null").as_ptr());
assert!(exists == 1);
let not_exists = check_file_exists(c("/tmp/__nonexistent_file_xyz123__").as_ptr());
assert!(not_exists == 0);
}
#[test]
fn test_read_write_file() {
let tmpfile = c("/tmp/libxml_rs_test_io_file.txt");
let data = c("Hello File I/O!");
let ret = write_memory_to_file(tmpfile.as_ptr(), data.as_ptr(), 15);
assert_eq!(ret, 0);
assert!(check_file_exists(tmpfile.as_ptr()) == 1);
let mut size: c_int = 0;
let read_data = read_file_to_memory(tmpfile.as_ptr(), &mut size as *mut c_int);
assert!(!read_data.is_null());
assert_eq!(size, 15);
unsafe {
let slice = std::slice::from_raw_parts(read_data as *const u8, size as usize);
assert_eq!(slice, b"Hello File I/O!");
}
unsafe { xmlFree(read_data as *mut c_void) };
std::fs::remove_file("/tmp/libxml_rs_test_io_file.txt").ok();
}
#[test]
fn test_read_file_nonexistent() {
let result = read_file_to_memory(
c("/tmp/__nonexistent_file_xyz456__").as_ptr(),
ptr::null_mut(),
);
assert!(result.is_null());
}
#[test]
fn test_write_file_null() {
let ret = write_memory_to_file(ptr::null(), c("data").as_ptr(), 4);
assert_eq!(ret, -1);
}
#[test]
fn test_get_cwd() {
let cwd = get_cwd();
assert!(!cwd.is_null());
unsafe {
let s = CStr::from_ptr(cwd);
assert!(!s.to_bytes().is_empty());
xmlFree(cwd as *mut c_void);
}
}
#[test]
fn test_buf_add_large_data() {
let buf = buf_create(10);
let mut large_data = Vec::new();
large_data.resize(5000, b'X');
large_data.push(0);
let ret = buf_add(buf, large_data.as_ptr() as *const xmlChar, 5000);
assert_eq!(ret, 5000);
let b = unsafe { &*buf };
assert_eq!(b.use_, 5000);
assert!(b.size >= 5001);
buf_free(buf);
}
#[test]
fn test_input_buffer_free_null() {
input_buffer_free(ptr::null_mut()); }
#[test]
fn test_output_buffer_close_null() {
let ret = output_buffer_close(ptr::null_mut());
assert_eq!(ret, -1);
}
#[test]
fn test_buf_add_to_immutable() {
let s: &[u8] = b"static\0";
let buf = buf_create_static(s.as_ptr() as *const xmlChar, 6);
assert!(!buf.is_null());
let data: &[u8] = b"more\0";
let ret = buf_add(buf, data.as_ptr() as *const xmlChar, 4);
assert_eq!(ret, -1);
buf_free(buf);
}
#[test]
fn test_encoding_from_int() {
assert_eq!(
encoding_from_int(0),
xmlCharEncoding::XML_CHAR_ENCODING_NONE
);
assert_eq!(
encoding_from_int(1),
xmlCharEncoding::XML_CHAR_ENCODING_UTF8
);
assert_eq!(
encoding_from_int(10),
xmlCharEncoding::XML_CHAR_ENCODING_8859_1
);
assert_eq!(
encoding_from_int(22),
xmlCharEncoding::XML_CHAR_ENCODING_ASCII
);
assert_eq!(
encoding_from_int(999),
xmlCharEncoding::XML_CHAR_ENCODING_ERROR
);
}
#[test]
fn test_find_handler_for_encoding() {
let handler = find_handler_for_encoding(1);
assert!(handler.is_null());
let handler = find_handler_for_encoding(0);
assert!(handler.is_null());
let handler = find_handler_for_encoding(-1);
assert!(handler.is_null());
}
#[test]
fn test_input_buffer_with_encoding_latin1() {
encoding::init_encodings();
let latin1_data: &[u8] = &[0x48, 0x65, 0x6C, 0x6C, 0xF6, 0x00];
let buf = input_buffer_create_mem(
latin1_data.as_ptr() as *const c_char,
5,
10, );
assert!(!buf.is_null());
let mut out = [0i8; 16];
let ret = input_buffer_read(buf, out.as_mut_ptr(), 16);
assert!(ret > 0);
let expected = b"Hell\xC3\xB6";
assert_eq!(&out[..ret as usize], i8_slice(expected));
input_buffer_free(buf);
}
#[test]
fn test_output_buffer_with_encoding() {
encoding::init_encodings();
let enc_name: &[u8] = b"ISO-8859-1\0";
let handler = encoding::find_encoding_handler(enc_name.as_ptr() as *const xmlChar);
assert!(!handler.is_null(), "Latin-1 handler should be available");
let internal_buf = buf_create(100);
let obuf = output_buffer_create_buffer(internal_buf, handler);
assert!(!obuf.is_null());
let utf8_data = unsafe { c_bytes(&[0x48, 0x65, 0x6C, 0x6C, 0xC3, 0xB6]) };
let ret = output_buffer_write(obuf, 6, utf8_data.as_ptr());
assert_eq!(ret, 6);
let flushed = output_buffer_flush(obuf);
assert!(flushed > 0);
let ctx = unsafe { (*obuf).context as *mut _xmlBuffer };
let ctx_b = unsafe { &*ctx };
assert_eq!(ctx_b.use_, 5);
unsafe {
assert_eq!(*ctx_b.content.offset(0), 0x48); assert_eq!(*ctx_b.content.offset(4), 0xF6); }
output_buffer_close(obuf);
buf_free(internal_buf);
}
#[test]
fn test_input_buffer_create_fd() {
let fd =
unsafe { libc::open(b"/dev/null\0" as *const u8 as *const c_char, libc::O_RDONLY) };
assert!(fd >= 0);
let buf = input_buffer_create_fd(fd, 0);
assert!(!buf.is_null());
let mut out = [0i8; 16];
let ret = input_buffer_read(buf, out.as_mut_ptr(), 16);
assert_eq!(ret, 0);
input_buffer_free(buf); }
#[test]
fn test_input_buffer_create_io() {
static mut TEST_DATA: &[u8] = b"Hello from callback!";
static mut CALLED: bool = false;
unsafe extern "C" fn test_read(
_ctx: *mut c_void,
buffer: *mut c_char,
len: c_int,
) -> c_int {
if CALLED {
return 0; }
CALLED = true;
let data = TEST_DATA;
let to_copy = (data.len() as c_int).min(len);
if to_copy > 0 {
std::ptr::copy_nonoverlapping(data.as_ptr(), buffer as *mut u8, to_copy as usize);
}
to_copy
}
unsafe extern "C" fn test_close(_ctx: *mut c_void) -> c_int {
0
}
let buf = input_buffer_create_io(
Some(test_read as xmlInputReadCallback),
Some(test_close as xmlInputCloseCallback),
ptr::null_mut(),
0,
);
assert!(!buf.is_null());
let mut out = [0i8; 32];
let ret = input_buffer_read(buf, out.as_mut_ptr(), 32);
assert!(ret > 0);
input_buffer_free(buf);
}
}