إعادة تنفيذ نقي بلغة Rust لمكتبة libxml2 — مكتبة تحليل XML/HTML القياسية الفعلية في عالم المصادر المفتوحة.
أصبحت libxml2 غير مدعومة رسميًا منذ ديسمبر 2025 مع مشاكل أمنية معروفة. تهدف xmloxide إلى أن تكون بديلاً آمنًا للذاكرة وعالي الأداء ينجح في نفس مجموعات اختبار المطابقة.
المميزات
آمن للذاكرة — شجرة قائمة على الساحة (arena) مع عدم وجود unsafe في الواجهة العامة
مطابق للمعايير — نسبة نجاح 100% في مجموعة اختبار مطابقة XML من W3C (1727/1727 اختبارًا قابلًا للتطبيق)
— تحليل XML غير صحيح مع الاستمرار في إنتاج شجرة قابلة للاستخدام، تمامًا مثل libxml2
استرداد الأخطاء
واجهات برمجة تحليل متعددة — شجرة DOM، دفق SAX2، سحب XmlReader، تحليل دفعي/متزايد
محلل HTML — تحليل HTML 4.01 متسامح مع الأخطاء مع الإغلاق التلقائي والعناصر الفارغة
محلل WHATWG HTML5 — محلل كامل وفقًا لمعيار HTML الحي (8810/8810 من اختبارات html5lib-tests ناجحة)
دفق HTML5 — واجهة رد نداء شبيهة بـ SAX لـ HTML5 (html5::sax) تغلف المحلل دون بناء شجرة DOM
محددات CSS — استعلام عن العناصر باستخدام صيغة CSS مألوفة (css::select) تشمل الأدوات المركبة والفئات الزائفة والبحث السريع بـ #id
XPath 1.0+ — محلل ومقيّم تعبيرات كامل مع جميع دوال XPath 1.0 الأساسية بالإضافة إلى دوال XPath 2.0 رئيسية (matches()، replace()، tokenize()، upper-case()، lower-case()، abs()، min()، max()، والمزيد)
التحقق — التحقق من DTD، RelaxNG، XML Schema (XSD)، و ISO Schematron (ISO/IEC 19757-3)
التكامل مع Serde — ميزة اختيارية serde لتسلسل/إلغاء تسلسل XML من/إلى أنواع Rust
التحليل غير المتزامن — ميزة اختيارية async للتحليل من مصادر tokio::io::AsyncRead
XML قانوني — تسلسل C14N 1.0 و Exclusive C14N
XInclude — معالجة تضمين المستندات
كتالوجات XML — كتالوجات XML من OASIS لحل URIs
أداة CLI xmllint — أداة سطر أوامر لتحليل XML والتحقق منه والاستعلام عنه
بدون حالة عمومية — كل Document مستقل ويمكن إرساله (Send) ومشاركته بين الخيوط (Sync)
FFI بلغة C/C++ — واجهة C كاملة مع ملف رأس (include/xmloxide.h) للتضمين في مشاريع C/C++
أقل عدد من التبعيات — فقط encoding_rs (المكتبة ليس لها تبعيات أخرى؛ clap مخصص لسطر الأوامر فقط)
بداية سريعة
root@kitploit:~
use xmloxide::Document;
let doc = Document::parse_str("<root><child>Hello</child></root>").unwrap();
let root = doc.root_element().unwrap();
assert_eq!(doc.node_name(root), Some("root"));
assert_eq!(doc.text_content(root), "Hello");
التسلسل
root@kitploit:~
use xmloxide::Document;
use xmloxide::serial::serialize;
let doc = Document::parse_str("<root><child>Hello</child></root>").unwrap();
let xml = serialize(&doc);
assert_eq!(xml, "<root><child>Hello</child></root>");
استعلامات XPath
root@kitploit:~
use xmloxide::Document;
use xmloxide::xpath::{evaluate, XPathValue};
let doc = Document::parse_str("<library><book><title>Rust</title></book></library>").unwrap();
let root = doc.root_element().unwrap();
let result = evaluate(&doc, root, "count(book)").unwrap();
assert_eq!(result.to_number(), 1.0);
use xmloxide::html::parse_html;
let doc = parse_html("<p>Hello <br> World").unwrap();
let root = doc.root_element().unwrap();
assert_eq!(doc.node_name(root), Some("html"));
محددات CSS
root@kitploit:~
use xmloxide::css::select;
use xmloxide::Document;
let doc = Document::parse_str(r#"<div><p class="intro">Hello</p><p>World</p></div>"#).unwrap();
let root = doc.root_element().unwrap();
let intros = select(&doc, root, "p.intro").unwrap();
assert_eq!(intros.len(), 1);
assert_eq!(doc.text_content(intros[0]), "Hello");
تحليل HTML5 (WHATWG)
root@kitploit:~
use xmloxide::html5::parse_html5;
let doc = parse_html5("<p>Hello <b>world</b>").unwrap();
let root = doc.root_element().unwrap();
assert_eq!(doc.node_name(root), Some("html"));
كما أن تحليل القطع (الخوارزمية التي تقف وراء innerHTML) مدعوم:
root@kitploit:~
use xmloxide::html5::{parse_html5_with_options, Html5ParseOptions};
let opts = Html5ParseOptions {
scripting: false,
fragment_context: Some("body".to_string()),
};
let doc = parse_html5_with_options("<p>fragment</p>", &opts).unwrap();
دفق HTML5 (شبيه بـ SAX)
root@kitploit:~
use xmloxide::html5::sax::{Html5SaxHandler, parse_html5_sax};
struct LinkExtractor { hrefs: Vec<String> }
impl Html5SaxHandler for LinkExtractor {
fn start_element(&mut self, name: &str, attrs: &[(String, String)], _sc: bool) {
if name == "a" {
if let Some((_, href)) = attrs.iter().find(|(n, _)| n == "href") {
self.hrefs.push(href.clone());
}
}
}
}
let mut handler = LinkExtractor { hrefs: Vec::new() };
parse_html5_sax(r#"<a href="https://github.com/jonwiggins/xmloxide/blob/main/page">Link</a>"#, &mut handler);
assert_eq!(handler.hrefs, vec!["/page"]);
استرداد الأخطاء
root@kitploit:~
use xmloxide::parser::{parse_str_with_options, ParseOptions};
let opts = ParseOptions::default().recover(true);
let doc = parse_str_with_options("<root><unclosed>", &opts).unwrap();
for diag in &doc.diagnostics {
eprintln!("{}", diag);
}
أداة سطر الأوامر
root@kitploit:~
# تحليل وطباعة منسقة
xmllint --format document.xml
# تحقق مقابل مخطط
xmllint --schema schema.xsd document.xml
xmllint --relaxng schema.rng document.xml
xmllint --schematron schema.sch document.xml
xmllint --dtdvalid schema.dtd document.xml
# استعلام XPath
xmllint --xpath "//title" document.xml
# XML قانوني
xmllint --c14n document.xml
# تحليل HTML
xmllint --html page.html
نظرة عامة على الوحدات
الوحدة
الوصف
tree
شجرة DOM قائمة على الساحة (Document، NodeId، NodeKind)
parser
محلل تنازلي متكرر لـ XML 1.0 مع استرداد الأخطاء
parser::push
محلل دفعي/متزايد للإدخال المجزأ
html
محلل HTML 4.01 متسامح مع الأخطاء
html5
محلل WHATWG HTML الحي (محلل الرموز + باني الشجرة)
html5::sax
واجهة دفق شبيهة بـ SAX لـ HTML5 (لا تُبنى شجرة DOM)
css
محرك محددات CSS للاستعلام عن أشجار المستندات
sax
محلل دفق يعتمد على الأحداث بتقنية SAX2
reader
واجهة تحليل سحب XmlReader
serial
مسلسلات XML وHTML وHTML5، بالإضافة إلى XML قانوني (C14N)
xpath
محلل ومقيّم تعبيرات XPath 1.0+
validation::dtd
تحليل DTD والتحقق منه
validation::relaxng
التحقق من مخطط RelaxNG
validation::xsd
التحقق من XML Schema (XSD)
validation::schematron
التحقق القائم على قواعد ISO Schematron
serde_xml
تسلسل/إلغاء تسلسل Serde لـ XML (ميزة اختيارية serde)
async_xml
تحليل غير متزامن عبر tokio::io::AsyncRead (ميزة اختيارية async)
xinclude
تضمين المستندات وفقًا لـ XInclude 1.0
catalog
كتالوجات XML من OASIS لحل URIs
encoding
كشف ترميز الأحرف وتحويله
ffi
روابط FFI بلغة C/C++ (include/xmloxide.h)
الأداء
إنتاجية التحليل تنافسية مع libxml2 — ضمن 3-4% في معظم المستندات، وأسرع بنسبة 12% في SVG. التسلسل أسرع بين 1.5 و 2.4 مرة بفضل تصميم الشجرة القائمة على الساحة. XPath أسرع بين 1.1 و 2.7 مرة عبر جميع المعايير.
التحليل:
المستند
الحجم
xmloxide
libxml2
النتيجة
خلاصة Atom
4.9 كيلوبايت
26.7 ميكروثانية (176 ميجابايت/ثانية)
25.5 ميكروثانية (184 ميجابايت/ثانية)
أبطأ بنحو 4%
رسم SVG
6.3 كيلوبايت
58.5 ميكروثانية (103 ميجابايت/ثانية)
65.6 ميكروثانية (92 ميجابايت/ثانية)
أسرع بنسبة 12%
ملف Maven POM
11.5 كيلوبايت
76.9 ميكروثانية (142 ميجابايت/ثانية)
74.2 ميكروثانية (148 ميجابايت/ثانية)
أبطأ بنحو 4%
صفحة XHTML
10.2 كيلوبايت
69.5 ميكروثانية (139 ميجابايت/ثانية)
61.5 ميكروثانية (157 ميجابايت/ثانية)
أبطأ بنحو 13%
كبير (374 كيلوبايت)
374 كيلوبايت
2.15 ملي ثانية (169 ميجابايت/ثانية)
2.08 ملي ثانية (175 ميجابايت/ثانية)
أبطأ بنحو 3%
التسلسل:
المستند
الحجم
xmloxide
libxml2
النتيجة
خلاصة Atom
4.9 كيلوبايت
11.3 ميكروثانية
17.5 ميكروثانية
أسرع بـ 1.5 مرة
ملف Maven POM
11.5 كيلوبايت
20.1 ميكروثانية
47.5 ميكروثانية
أسرع بـ 2.4 مرة
كبير (374 كيلوبايت)
374 كيلوبايت
614 ميكروثانية
1397 ميكروثانية
أسرع بـ 2.3 مرة
XPath:
التعبير
xmloxide
libxml2
النتيجة
مسار بسيط (//entry/title)
1.51 ميكروثانية
1.63 ميكروثانية
أسرع بنسبة 8%
مسند سمة (//book[@id])
5.91 ميكروثانية
15.99 ميكروثانية
أسرع بـ 2.7 مرة
دالة count()
1.09 ميكروثانية
1.67 ميكروثانية
أسرع بـ 1.5 مرة
دالة string()
1.32 ميكروثانية
1.77 ميكروثانية
أسرع بـ 1.3 مرة
التحسينات الرئيسية: شجرة قائمة على الساحة للتسلسل السريع، فحوصات مسبقة على مستوى البايت للتحقق من الأحرف، مسح نصي شامل، مسارات سريعة لـ ASCII لتحليل الأسماء، تقسيم أسماء العناصر بدون نسخ، حل الكيانات المضمنة، دمج خطوات XPath // مع توسيع المحاور المدمجة، موصلات شجرة مضمنة، ومسارات سريعة لاختبار الأسماء لمحاور الطفل/السليل.
root@kitploit:~
# تشغيل المعايير (يتطلب مكتبة libxml2 النظامية)
cargo bench --features bench-libxml2 --bench comparison_bench
الاختبار
1078 اختبار وحدة عبر جميع الوحدات
138 اختبار FFI تغطي كامل واجهة C API (بما في ذلك SAX وSchematron وCSS)
مجموعة توافق libxml2 — 119/119 اختبارًا ناجحًا (100%) تغطي تحليل XML والمساحات الاسمية واكتشاف الأخطاء وتحليل HTML
مجموعة اختبار مطابقة XML من W3C — 1727/1727 اختبارًا قابلًا للتطبيق ناجحة (100%)
توفر xmloxide واجهة متوافقة مع C لتضمينها في مشاريع C/C++ (مثل Chromium أو محركات الألعاب أو أي قاعدة بيانات تستخدم حالياً libxml2).
root@kitploit:~
# بناء المكتبات المشتركة + الثابتة (يستخدم ملف Makefile المرفق)
make
# أو البناء بشكل فردي:
make shared # .so / .dylib / .dll
make static # .a / .lib
# بناء وتشغيل مثال C
make example
واجهة API الكاملة — بما في ذلك التنقل في الشجرة وتعديلها، تقييم XPath، التسلسل (العادي والمطبوع بشكل جميل)، تحليل HTML/HTML5، التحقق من DTD/RelaxNG/XSD/Schematron، C14N، دفق SAX، XmlReader، المحلل الدفعي، وكتالوجات XML — مُعلنة في include/xmloxide.h.
الانتقال من libxml2
libxml2
xmloxide (Rust)
xmloxide (C FFI)
xmlReadMemory
Document::parse_str
xmloxide_parse_str
xmlReadFile
Document::parse_file
xmloxide_parse_file
xmlParseDoc
Document::parse_bytes
xmloxide_parse_bytes
htmlReadMemory
html::parse_html
xmloxide_parse_html
(تحليل HTML5)
html5::parse_html5
—
(جزء HTML5 / innerHTML)
html5::parse_html5_with_options
—
(دفق HTML5)
html5::sax::parse_html5_sax
—
(محددات CSS / querySelector)
css::select
—
xmlFreeDoc
(إفلات Document)
xmloxide_free_doc
xmlDocGetRootElement
doc.root_element()
xmloxide_doc_root_element
xmlNodeGetContent
doc.text_content(id)
xmloxide_node_text_content
xmlNodeSetContent
doc.set_text_content(id, s)
xmloxide_set_text_content
xmlGetProp
doc.attribute(id, name)
xmloxide_node_attribute
xmlSetProp
doc.set_attribute(...)
xmloxide_set_attribute
xmlNewNode
doc.create_node(...)
xmloxide_create_element
سلامة الخيوط: على عكس libxml2، لا تحتوي xmloxide على حالة عمومية. كل Document مستقل ويمكن إرساله ومشاركته بين الخيوط. تستخدم طبقة FFI مخزناً محلياً للخيط لرسالة الخطأ الأخيرة — كل خيط له حالة خطأ خاصة به. لا حاجة لدوال التهيئة أو التنظيف.
اختبار الضعف (Fuzzing)
تتضمن xmloxide أهداف اختبار ضعف لاختبار الأمان:
root@kitploit:~
# تثبيت cargo-fuzz (يتطلب nightly)
cargo install cargo-fuzz
# تشغيل هدف اختبار ضعف
cargo +nightly fuzz run fuzz_xml_parse
cargo +nightly fuzz run fuzz_html_parse
cargo +nightly fuzz run fuzz_html5_parse
cargo +nightly fuzz run fuzz_html5_fragment
cargo +nightly fuzz run fuzz_xpath
cargo +nightly fuzz run fuzz_roundtrip
cargo +nightly fuzz run fuzz_sax
cargo +nightly fuzz run fuzz_reader
cargo +nightly fuzz run fuzz_push
cargo +nightly fuzz run fuzz_validation
cargo +nightly fuzz run fuzz_schematron
بدون XML 1.1 — تنفذ xmloxide XML 1.0 (الطبعة الخامسة) فقط. XML 1.1 نادر الاستخدام وغير مخطط له.
بدون XSLT — XSLT مواصفة منفصلة (libxslt) وخارج نطاق المشروع.
محللات HTML — يتم توفير كل من محلل HTML 4.01 (مطابق لسلوك libxml2) ومحلل WHATWG HTML5 كامل. ينجح محلل HTML5 في 100% من اختبارات html5lib-tests.
المحلل الدفعي يخزن داخليًا — واجهة المحلل الدفعي/المتزايد (PushParser) تقوم حالياً بتخزين جميع البيانات المدفوعة وتنفيذ التحليل الكامل عند finish()، بدلاً من التدفق الحقيقي مثل xmlParseChunk في libxml2. يتوفر دفق SAX (parse_sax لـ XML و html5::sax::parse_html5_sax لـ HTML5) كبديل لمعالجة المستندات الكبيرة المقيدة بالذاكرة.
محور XPath namespace:: — يعيد محور namespace:: عقدة العنصر عندما تتطابق المساحات الاسمية في النطاق (بدلاً من إنشاء عقد مساحة اسمية منفصلة)، متبعاً نفس نمط محور السمة.