use core::cell::RefCell;
use core::ffi::c_void;
use core::ptr;
use core::sync::atomic::{AtomicBool, AtomicI32, AtomicPtr, Ordering};
use std::os::raw::c_int;
use crate::abi::allocator;
use crate::abi::callbacks::{xmlGenericErrorFunc, xmlStructuredErrorFunc};
use crate::abi::structs::_xmlError;
use crate::abi::types::xmlErrorLevel::XML_ERR_NONE;
use crate::abi::types::*;
use crate::abi::versioning;
static INIT_REF_COUNT: AtomicI32 = AtomicI32::new(0);
static THREADS_INITIALIZED: AtomicBool = AtomicBool::new(false);
static VALIDITY_CHECKING_DEFAULT: AtomicI32 = AtomicI32::new(0);
static DO_WARNINGS_DEFAULT: AtomicI32 = AtomicI32::new(1);
static INDENT_TREE_OUTPUT: AtomicI32 = AtomicI32::new(0);
static KEEP_BLANKS_DEFAULT: AtomicI32 = AtomicI32::new(1);
static LOAD_EXT_DTD_DEFAULT: AtomicI32 = AtomicI32::new(0);
static PEDANTIC_PARSER_DEFAULT: AtomicI32 = AtomicI32::new(0);
static SUBSTITUTE_ENTITIES_DEFAULT: AtomicI32 = AtomicI32::new(0);
static SAVE_NO_EMPTY_TAGS: AtomicI32 = AtomicI32::new(0);
static GET_WARNINGS_DEFAULT: AtomicI32 = AtomicI32::new(1);
static GENERIC_ERROR_CTX: AtomicPtr<c_void> = AtomicPtr::new(ptr::null_mut());
static GENERIC_ERROR_FUNC: AtomicPtr<c_void> = AtomicPtr::new(ptr::null_mut());
static STRUCTURED_ERROR_CTX: AtomicPtr<c_void> = AtomicPtr::new(ptr::null_mut());
static STRUCTURED_ERROR_FUNC: AtomicPtr<c_void> = AtomicPtr::new(ptr::null_mut());
static CATALOG_DEFAULTS: AtomicI32 = AtomicI32::new(0);
thread_local! {
static LAST_ERROR: RefCell<Option<_xmlError>> = const { RefCell::new(None) };
}
pub fn get_validity_checking_default() -> c_int {
VALIDITY_CHECKING_DEFAULT.load(Ordering::Relaxed)
}
pub fn set_validity_checking_default(val: c_int) {
VALIDITY_CHECKING_DEFAULT.store(val, Ordering::Relaxed);
}
pub fn get_do_warnings_default() -> c_int {
DO_WARNINGS_DEFAULT.load(Ordering::Relaxed)
}
pub fn set_do_warnings_default(val: c_int) {
DO_WARNINGS_DEFAULT.store(val, Ordering::Relaxed);
}
pub fn get_indent_tree_output() -> c_int {
INDENT_TREE_OUTPUT.load(Ordering::Relaxed)
}
pub fn set_indent_tree_output(val: c_int) {
INDENT_TREE_OUTPUT.store(val, Ordering::Relaxed);
}
pub fn get_keep_blanks_default() -> c_int {
KEEP_BLANKS_DEFAULT.load(Ordering::Relaxed)
}
pub fn set_keep_blanks_default(val: c_int) {
KEEP_BLANKS_DEFAULT.store(val, Ordering::Relaxed);
}
pub fn get_load_ext_dtd_default() -> c_int {
LOAD_EXT_DTD_DEFAULT.load(Ordering::Relaxed)
}
pub fn set_load_ext_dtd_default(val: c_int) {
LOAD_EXT_DTD_DEFAULT.store(val, Ordering::Relaxed);
}
pub fn get_pedantic_parser_default() -> c_int {
PEDANTIC_PARSER_DEFAULT.load(Ordering::Relaxed)
}
pub fn set_pedantic_parser_default(val: c_int) {
PEDANTIC_PARSER_DEFAULT.store(val, Ordering::Relaxed);
}
pub fn get_substitute_entities_default() -> c_int {
SUBSTITUTE_ENTITIES_DEFAULT.load(Ordering::Relaxed)
}
pub fn set_substitute_entities_default(val: c_int) {
SUBSTITUTE_ENTITIES_DEFAULT.store(val, Ordering::Relaxed);
}
pub fn get_save_no_empty_tags() -> c_int {
SAVE_NO_EMPTY_TAGS.load(Ordering::Relaxed)
}
pub fn set_save_no_empty_tags(val: c_int) {
SAVE_NO_EMPTY_TAGS.store(val, Ordering::Relaxed);
}
pub fn get_get_warnings_default() -> c_int {
GET_WARNINGS_DEFAULT.load(Ordering::Relaxed)
}
pub fn set_get_warnings_default(val: c_int) {
GET_WARNINGS_DEFAULT.store(val, Ordering::Relaxed);
}
pub unsafe fn set_generic_error_func(ctx: *mut c_void, handler: Option<xmlGenericErrorFunc>) {
GENERIC_ERROR_CTX.store(ctx, Ordering::Release);
GENERIC_ERROR_FUNC.store(
handler.map_or(ptr::null_mut(), |f| f as *mut c_void),
Ordering::Release,
);
}
pub fn get_generic_error_ctx() -> *mut c_void {
GENERIC_ERROR_CTX.load(Ordering::Acquire)
}
pub fn get_generic_error_func() -> Option<xmlGenericErrorFunc> {
let ptr = GENERIC_ERROR_FUNC.load(Ordering::Acquire);
if ptr.is_null() {
None
} else {
Some(unsafe { core::mem::transmute::<*mut c_void, xmlGenericErrorFunc>(ptr) })
}
}
pub unsafe fn set_structured_error_func(ctx: *mut c_void, handler: Option<xmlStructuredErrorFunc>) {
STRUCTURED_ERROR_CTX.store(ctx, Ordering::Release);
STRUCTURED_ERROR_FUNC.store(
handler.map_or(ptr::null_mut(), |f| f as *mut c_void),
Ordering::Release,
);
}
pub fn get_structured_error_ctx() -> *mut c_void {
STRUCTURED_ERROR_CTX.load(Ordering::Acquire)
}
pub fn get_structured_error_func() -> Option<xmlStructuredErrorFunc> {
let ptr = STRUCTURED_ERROR_FUNC.load(Ordering::Acquire);
if ptr.is_null() {
None
} else {
Some(unsafe { core::mem::transmute::<*mut c_void, xmlStructuredErrorFunc>(ptr) })
}
}
pub fn get_last_error() -> *mut _xmlError {
LAST_ERROR.with(|last| {
let mut last = last.borrow_mut();
last.as_mut()
.map_or(ptr::null_mut(), |e| e as *mut _xmlError)
})
}
pub fn with_last_error<F, R>(f: F) -> R
where
F: FnOnce(Option<&_xmlError>) -> R,
{
LAST_ERROR.with(|last| f(last.borrow().as_ref()))
}
pub fn set_last_error(err: _xmlError) {
LAST_ERROR.with(|last| {
*last.borrow_mut() = Some(err);
});
}
pub fn reset_last_error() {
LAST_ERROR.with(|last| {
*last.borrow_mut() = None;
});
}
pub fn get_catalog_defaults() -> c_int {
CATALOG_DEFAULTS.load(Ordering::Relaxed)
}
pub fn set_catalog_defaults(val: c_int) {
CATALOG_DEFAULTS.store(val, Ordering::Relaxed);
}
pub unsafe fn init_parser() {
let prev = INIT_REF_COUNT.fetch_add(1, Ordering::AcqRel);
if prev == 0 {
allocator::xmlInitMemory();
versioning::set_initialized();
crate::xml::encoding::init_encodings();
init_threads();
}
}
pub unsafe fn cleanup_parser() {
let prev = INIT_REF_COUNT.fetch_sub(1, Ordering::AcqRel);
if prev <= 1 {
crate::xml::catalog::cleanup();
crate::xml::encoding::cleanup_encodings();
allocator::xmlCleanupMemory();
}
}
pub fn init_threads() -> c_int {
if !THREADS_INITIALIZED.swap(true, Ordering::Release) {
}
0
}
pub fn cleanup_threads() {
THREADS_INITIALIZED.store(false, Ordering::Release);
}
pub fn threads_initialized() -> bool {
THREADS_INITIALIZED.load(Ordering::Acquire)
}
pub fn init_ref_count() -> c_int {
INIT_REF_COUNT.load(Ordering::Relaxed)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_parser_defaults_initial_values() {
assert_eq!(get_validity_checking_default(), 0);
assert_eq!(get_do_warnings_default(), 1);
assert_eq!(get_indent_tree_output(), 0);
assert_eq!(get_keep_blanks_default(), 1);
assert_eq!(get_load_ext_dtd_default(), 0);
assert_eq!(get_pedantic_parser_default(), 0);
assert_eq!(get_substitute_entities_default(), 0);
assert_eq!(get_save_no_empty_tags(), 0);
assert_eq!(get_get_warnings_default(), 1);
}
#[test]
fn test_parser_defaults_set_and_get() {
set_validity_checking_default(1);
assert_eq!(get_validity_checking_default(), 1);
set_validity_checking_default(0);
assert_eq!(get_validity_checking_default(), 0);
set_keep_blanks_default(0);
assert_eq!(get_keep_blanks_default(), 0);
set_keep_blanks_default(1);
assert_eq!(get_keep_blanks_default(), 1);
set_substitute_entities_default(1);
assert_eq!(get_substitute_entities_default(), 1);
set_substitute_entities_default(0);
assert_eq!(get_substitute_entities_default(), 0);
}
#[test]
fn test_init_cleanup_ref_count() {
unsafe {
init_parser();
assert_eq!(init_ref_count(), 1);
init_parser();
assert_eq!(init_ref_count(), 2);
cleanup_parser();
assert!(init_ref_count() == 1 || init_ref_count() == 0);
cleanup_parser();
}
}
#[test]
fn test_error_callbacks_default_null() {
assert!(get_generic_error_func().is_none());
assert!(get_structured_error_func().is_none());
}
#[test]
fn test_error_callbacks_set_and_get() {
unsafe {
unsafe extern "C" fn dummy_handler(_ctx: *mut c_void, _msg: *const core::ffi::c_char) {}
let dummy_func: xmlGenericErrorFunc = dummy_handler;
let dummy_ctx: *mut c_void = &mut 0 as *mut i32 as *mut c_void;
set_generic_error_func(dummy_ctx, Some(dummy_func));
assert!(get_generic_error_func().is_some());
assert_eq!(get_generic_error_ctx(), dummy_ctx);
set_generic_error_func(ptr::null_mut(), None);
assert!(get_generic_error_func().is_none());
}
}
#[test]
fn test_last_error_thread_local() {
assert!(get_last_error().is_null());
let err = _xmlError {
domain: XML_FROM_PARSER,
code: XML_ERR_OK as c_int,
message: ptr::null_mut(),
level: XML_ERR_NONE as c_int,
file: ptr::null_mut(),
line: 0,
str1: ptr::null_mut(),
str2: ptr::null_mut(),
str3: ptr::null_mut(),
int1: 0,
int2: 0,
ctxt: ptr::null_mut(),
node: ptr::null_mut(),
};
set_last_error(err);
assert!(!get_last_error().is_null());
unsafe {
assert_eq!((*get_last_error()).domain, XML_FROM_PARSER);
}
reset_last_error();
assert!(get_last_error().is_null());
}
#[test]
fn test_catalog_defaults() {
let orig = get_catalog_defaults();
set_catalog_defaults(1);
assert_eq!(get_catalog_defaults(), 1);
set_catalog_defaults(0);
assert_eq!(get_catalog_defaults(), 0);
set_catalog_defaults(orig);
}
}