#[cfg(feature = "alloc")]
use alloc::alloc::{Layout, alloc, dealloc};
use core::fmt::{self, Debug, Formatter};
use core::hash::{Hash, Hasher};
use crate::value::{TypeTag, Value};
#[repr(u8)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum OtherKind {
QName = 0,
Uuid = 1,
}
#[repr(C, align(8))]
struct QNameHeader {
kind: OtherKind,
_pad: [u8; 7],
namespace: Value,
local_name: Value,
}
#[repr(transparent)]
pub struct VQName(pub(crate) Value);
impl VQName {
const fn layout() -> Layout {
Layout::new::<QNameHeader>()
}
#[cfg(feature = "alloc")]
fn alloc() -> *mut QNameHeader {
unsafe { alloc(Self::layout()).cast::<QNameHeader>() }
}
#[cfg(feature = "alloc")]
fn dealloc(ptr: *mut QNameHeader) {
unsafe {
dealloc(ptr.cast::<u8>(), Self::layout());
}
}
fn header(&self) -> &QNameHeader {
unsafe { &*(self.0.heap_ptr() as *const QNameHeader) }
}
#[cfg(feature = "alloc")]
#[must_use]
pub fn new(namespace: impl Into<Value>, local_name: impl Into<Value>) -> Self {
unsafe {
let ptr = Self::alloc();
core::ptr::write(&raw mut (*ptr).kind, OtherKind::QName);
core::ptr::write(&raw mut (*ptr)._pad, [0; 7]);
core::ptr::write(&raw mut (*ptr).namespace, namespace.into());
core::ptr::write(&raw mut (*ptr).local_name, local_name.into());
VQName(Value::new_ptr(ptr.cast(), TypeTag::Other))
}
}
#[cfg(feature = "alloc")]
#[must_use]
pub fn new_local(local_name: impl Into<Value>) -> Self {
Self::new(Value::NULL, local_name)
}
#[must_use]
pub fn namespace(&self) -> Option<&Value> {
let ns = &self.header().namespace;
if ns.is_null() { None } else { Some(ns) }
}
#[must_use]
pub fn local_name(&self) -> &Value {
&self.header().local_name
}
#[must_use]
pub fn has_namespace(&self) -> bool {
!self.header().namespace.is_null()
}
pub(crate) fn clone_impl(&self) -> Value {
#[cfg(feature = "alloc")]
{
let h = self.header();
Self::new(h.namespace.clone(), h.local_name.clone()).0
}
#[cfg(not(feature = "alloc"))]
{
panic!("cannot clone VQName without alloc feature")
}
}
pub(crate) fn drop_impl(&mut self) {
#[cfg(feature = "alloc")]
unsafe {
let ptr = self.0.heap_ptr_mut() as *mut QNameHeader;
core::ptr::drop_in_place(&mut (*ptr).namespace);
core::ptr::drop_in_place(&mut (*ptr).local_name);
Self::dealloc(ptr);
}
}
}
impl Clone for VQName {
fn clone(&self) -> Self {
VQName(self.clone_impl())
}
}
impl PartialEq for VQName {
fn eq(&self, other: &Self) -> bool {
let (h1, h2) = (self.header(), other.header());
h1.namespace == h2.namespace && h1.local_name == h2.local_name
}
}
impl Eq for VQName {}
impl Hash for VQName {
fn hash<H: Hasher>(&self, state: &mut H) {
let h = self.header();
h.namespace.hash(state);
h.local_name.hash(state);
}
}
impl Debug for VQName {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
let h = self.header();
if h.namespace.is_null() {
write!(f, "{:?}", h.local_name)
} else {
write!(f, "{{{:?}}}{:?}", h.namespace, h.local_name)
}
}
}
#[cfg(feature = "alloc")]
impl From<VQName> for Value {
fn from(qname: VQName) -> Self {
qname.0
}
}
#[repr(C, align(8))]
struct UuidHeader {
kind: OtherKind,
_pad: [u8; 7],
bytes: [u8; 16],
}
#[repr(transparent)]
pub struct VUuid(pub(crate) Value);
impl VUuid {
const fn layout() -> Layout {
Layout::new::<UuidHeader>()
}
#[cfg(feature = "alloc")]
fn alloc() -> *mut UuidHeader {
unsafe { alloc(Self::layout()).cast::<UuidHeader>() }
}
#[cfg(feature = "alloc")]
fn dealloc(ptr: *mut UuidHeader) {
unsafe {
dealloc(ptr.cast::<u8>(), Self::layout());
}
}
fn header(&self) -> &UuidHeader {
unsafe { &*(self.0.heap_ptr() as *const UuidHeader) }
}
#[cfg(feature = "alloc")]
#[must_use]
pub fn new(bytes: [u8; 16]) -> Self {
unsafe {
let ptr = Self::alloc();
core::ptr::write(&raw mut (*ptr).kind, OtherKind::Uuid);
core::ptr::write(&raw mut (*ptr)._pad, [0; 7]);
core::ptr::write(&raw mut (*ptr).bytes, bytes);
VUuid(Value::new_ptr(ptr.cast(), TypeTag::Other))
}
}
#[cfg(feature = "alloc")]
#[must_use]
pub fn from_u64_pair(high: u64, low: u64) -> Self {
let mut bytes = [0u8; 16];
bytes[..8].copy_from_slice(&high.to_be_bytes());
bytes[8..].copy_from_slice(&low.to_be_bytes());
Self::new(bytes)
}
#[cfg(feature = "alloc")]
#[must_use]
pub fn from_u128(value: u128) -> Self {
Self::new(value.to_be_bytes())
}
#[must_use]
pub fn as_bytes(&self) -> &[u8; 16] {
&self.header().bytes
}
#[must_use]
pub fn as_u128(&self) -> u128 {
u128::from_be_bytes(self.header().bytes)
}
#[must_use]
pub fn high(&self) -> u64 {
let bytes = &self.header().bytes;
u64::from_be_bytes([
bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7],
])
}
#[must_use]
pub fn low(&self) -> u64 {
let bytes = &self.header().bytes;
u64::from_be_bytes([
bytes[8], bytes[9], bytes[10], bytes[11], bytes[12], bytes[13], bytes[14], bytes[15],
])
}
pub(crate) fn clone_impl(&self) -> Value {
#[cfg(feature = "alloc")]
{
Self::new(self.header().bytes).0
}
#[cfg(not(feature = "alloc"))]
{
panic!("cannot clone VUuid without alloc feature")
}
}
pub(crate) fn drop_impl(&mut self) {
#[cfg(feature = "alloc")]
unsafe {
Self::dealloc(self.0.heap_ptr_mut().cast());
}
}
}
impl Clone for VUuid {
fn clone(&self) -> Self {
VUuid(self.clone_impl())
}
}
impl PartialEq for VUuid {
fn eq(&self, other: &Self) -> bool {
self.header().bytes == other.header().bytes
}
}
impl Eq for VUuid {}
impl Hash for VUuid {
fn hash<H: Hasher>(&self, state: &mut H) {
self.header().bytes.hash(state);
}
}
impl Debug for VUuid {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
let bytes = &self.header().bytes;
write!(
f,
"{:02x}{:02x}{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}",
bytes[0],
bytes[1],
bytes[2],
bytes[3],
bytes[4],
bytes[5],
bytes[6],
bytes[7],
bytes[8],
bytes[9],
bytes[10],
bytes[11],
bytes[12],
bytes[13],
bytes[14],
bytes[15]
)
}
}
#[cfg(feature = "alloc")]
impl From<VUuid> for Value {
fn from(uuid: VUuid) -> Self {
uuid.0
}
}
#[cfg(feature = "alloc")]
impl From<[u8; 16]> for VUuid {
fn from(bytes: [u8; 16]) -> Self {
Self::new(bytes)
}
}
#[cfg(feature = "alloc")]
impl From<u128> for VUuid {
fn from(value: u128) -> Self {
Self::from_u128(value)
}
}
pub(crate) unsafe fn get_other_kind(value: &Value) -> OtherKind {
let ptr = value.heap_ptr();
unsafe { *(ptr as *const OtherKind) }
}
#[cfg(test)]
mod tests {
use super::*;
use crate::VString;
#[test]
fn test_qname_with_namespace() {
let qname = VQName::new(VString::new("http://example.com"), VString::new("element"));
assert!(qname.has_namespace());
assert_eq!(
qname.namespace().unwrap().as_string().unwrap().as_str(),
"http://example.com"
);
assert_eq!(qname.local_name().as_string().unwrap().as_str(), "element");
}
#[test]
fn test_qname_local_only() {
let qname = VQName::new_local(VString::new("element"));
assert!(!qname.has_namespace());
assert!(qname.namespace().is_none());
assert_eq!(qname.local_name().as_string().unwrap().as_str(), "element");
}
#[test]
fn test_qname_clone() {
let qname = VQName::new(VString::new("ns"), VString::new("local"));
let cloned = qname.clone();
assert_eq!(qname, cloned);
}
#[test]
fn test_qname_debug() {
let qname = VQName::new(VString::new("ns"), VString::new("local"));
let debug = format!("{qname:?}");
assert!(debug.contains("ns"));
assert!(debug.contains("local"));
}
#[test]
fn test_uuid_new() {
let bytes = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16];
let uuid = VUuid::new(bytes);
assert_eq!(uuid.as_bytes(), &bytes);
}
#[test]
fn test_uuid_from_u128() {
let value: u128 = 0x0102030405060708090a0b0c0d0e0f10;
let uuid = VUuid::from_u128(value);
assert_eq!(uuid.as_u128(), value);
}
#[test]
fn test_uuid_high_low() {
let uuid = VUuid::from_u64_pair(0x0102030405060708, 0x090a0b0c0d0e0f10);
assert_eq!(uuid.high(), 0x0102030405060708);
assert_eq!(uuid.low(), 0x090a0b0c0d0e0f10);
}
#[test]
fn test_uuid_clone() {
let uuid = VUuid::from_u128(0x12345678_9abc_def0_1234_56789abcdef0);
let cloned = uuid.clone();
assert_eq!(uuid, cloned);
}
#[test]
fn test_uuid_debug_format() {
let uuid = VUuid::from_u128(0x12345678_9abc_def0_1234_56789abcdef0);
let debug = format!("{uuid:?}");
assert_eq!(debug, "12345678-9abc-def0-1234-56789abcdef0");
}
}