facet_value/
other.rs

1//! Extensible "Other" value types using tag 7 with a secondary discriminant.
2//!
3//! This module provides types that share tag 7 but are distinguished by a
4//! secondary `OtherKind` discriminant stored on the heap. This allows for
5//! unlimited future extensibility without consuming additional tag bits.
6//!
7//! Current types:
8//! - `VQName`: Qualified name (namespace + local name) for XML namespace support
9//! - `VUuid`: 128-bit UUID for preserving semantic identity
10
11#[cfg(feature = "alloc")]
12use alloc::alloc::{Layout, alloc, dealloc};
13use core::fmt::{self, Debug, Formatter};
14use core::hash::{Hash, Hasher};
15
16use crate::value::{TypeTag, Value};
17
18/// Secondary discriminant for "Other" types (tag 7).
19///
20/// This allows 256 subtypes to share a single tag value.
21#[repr(u8)]
22#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
23pub enum OtherKind {
24    /// Qualified name (namespace + local name)
25    QName = 0,
26    /// UUID (128-bit universally unique identifier)
27    Uuid = 1,
28}
29
30// ============================================================================
31// VQName - Qualified Name
32// ============================================================================
33
34/// Header for VQName values.
35///
36/// Layout: [kind: u8][_pad: 7 bytes][namespace: Value][local_name: Value]
37#[repr(C, align(8))]
38struct QNameHeader {
39    /// The OtherKind discriminant (always QName = 0)
40    kind: OtherKind,
41    /// Padding for alignment
42    _pad: [u8; 7],
43    /// Optional namespace (Value::NULL if none)
44    namespace: Value,
45    /// Local name (always a VString)
46    local_name: Value,
47}
48
49/// A qualified name consisting of an optional namespace and a local name.
50///
51/// `VQName` is used for XML namespace support, where elements and attributes
52/// can have qualified names like `{http://example.com}element`.
53///
54/// Both the namespace and local name are stored as `Value`s, allowing them
55/// to benefit from inline string optimization for short names.
56#[repr(transparent)]
57pub struct VQName(pub(crate) Value);
58
59impl VQName {
60    fn layout() -> Layout {
61        Layout::new::<QNameHeader>()
62    }
63
64    #[cfg(feature = "alloc")]
65    fn alloc() -> *mut QNameHeader {
66        unsafe { alloc(Self::layout()).cast::<QNameHeader>() }
67    }
68
69    #[cfg(feature = "alloc")]
70    fn dealloc(ptr: *mut QNameHeader) {
71        unsafe {
72            dealloc(ptr.cast::<u8>(), Self::layout());
73        }
74    }
75
76    fn header(&self) -> &QNameHeader {
77        unsafe { &*(self.0.heap_ptr() as *const QNameHeader) }
78    }
79
80    /// Creates a new qualified name with a namespace and local name.
81    #[cfg(feature = "alloc")]
82    #[must_use]
83    pub fn new(namespace: impl Into<Value>, local_name: impl Into<Value>) -> Self {
84        unsafe {
85            let ptr = Self::alloc();
86            // Use ptr::write to avoid dropping uninitialized memory
87            core::ptr::write(&raw mut (*ptr).kind, OtherKind::QName);
88            core::ptr::write(&raw mut (*ptr)._pad, [0; 7]);
89            core::ptr::write(&raw mut (*ptr).namespace, namespace.into());
90            core::ptr::write(&raw mut (*ptr).local_name, local_name.into());
91            VQName(Value::new_ptr(ptr.cast(), TypeTag::Other))
92        }
93    }
94
95    /// Creates a new qualified name without a namespace.
96    #[cfg(feature = "alloc")]
97    #[must_use]
98    pub fn new_local(local_name: impl Into<Value>) -> Self {
99        Self::new(Value::NULL, local_name)
100    }
101
102    /// Returns the namespace, or `None` if there is no namespace.
103    #[must_use]
104    pub fn namespace(&self) -> Option<&Value> {
105        let ns = &self.header().namespace;
106        if ns.is_null() { None } else { Some(ns) }
107    }
108
109    /// Returns the local name.
110    #[must_use]
111    pub fn local_name(&self) -> &Value {
112        &self.header().local_name
113    }
114
115    /// Returns `true` if this qualified name has a namespace.
116    #[must_use]
117    pub fn has_namespace(&self) -> bool {
118        !self.header().namespace.is_null()
119    }
120
121    // === Internal ===
122
123    pub(crate) fn clone_impl(&self) -> Value {
124        #[cfg(feature = "alloc")]
125        {
126            let h = self.header();
127            Self::new(h.namespace.clone(), h.local_name.clone()).0
128        }
129        #[cfg(not(feature = "alloc"))]
130        {
131            panic!("cannot clone VQName without alloc feature")
132        }
133    }
134
135    pub(crate) fn drop_impl(&mut self) {
136        #[cfg(feature = "alloc")]
137        unsafe {
138            let ptr = self.0.heap_ptr_mut() as *mut QNameHeader;
139            // Drop the contained Values
140            core::ptr::drop_in_place(&mut (*ptr).namespace);
141            core::ptr::drop_in_place(&mut (*ptr).local_name);
142            Self::dealloc(ptr);
143        }
144    }
145}
146
147impl Clone for VQName {
148    fn clone(&self) -> Self {
149        VQName(self.clone_impl())
150    }
151}
152
153impl PartialEq for VQName {
154    fn eq(&self, other: &Self) -> bool {
155        let (h1, h2) = (self.header(), other.header());
156        h1.namespace == h2.namespace && h1.local_name == h2.local_name
157    }
158}
159
160impl Eq for VQName {}
161
162impl Hash for VQName {
163    fn hash<H: Hasher>(&self, state: &mut H) {
164        let h = self.header();
165        h.namespace.hash(state);
166        h.local_name.hash(state);
167    }
168}
169
170impl Debug for VQName {
171    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
172        let h = self.header();
173        if h.namespace.is_null() {
174            write!(f, "{:?}", h.local_name)
175        } else {
176            write!(f, "{{{:?}}}{:?}", h.namespace, h.local_name)
177        }
178    }
179}
180
181#[cfg(feature = "alloc")]
182impl From<VQName> for Value {
183    fn from(qname: VQName) -> Self {
184        qname.0
185    }
186}
187
188// ============================================================================
189// VUuid - UUID
190// ============================================================================
191
192/// Header for VUuid values.
193///
194/// Layout: [kind: u8][_pad: 7 bytes][uuid_bytes: 16 bytes]
195#[repr(C, align(8))]
196struct UuidHeader {
197    /// The OtherKind discriminant (always Uuid = 1)
198    kind: OtherKind,
199    /// Padding for alignment
200    _pad: [u8; 7],
201    /// The 128-bit UUID in big-endian byte order
202    bytes: [u8; 16],
203}
204
205/// A 128-bit universally unique identifier (UUID).
206///
207/// `VUuid` stores UUIDs in their native 128-bit form rather than as
208/// 36-character strings, preserving semantic identity while being more
209/// memory-efficient.
210#[repr(transparent)]
211pub struct VUuid(pub(crate) Value);
212
213impl VUuid {
214    fn layout() -> Layout {
215        Layout::new::<UuidHeader>()
216    }
217
218    #[cfg(feature = "alloc")]
219    fn alloc() -> *mut UuidHeader {
220        unsafe { alloc(Self::layout()).cast::<UuidHeader>() }
221    }
222
223    #[cfg(feature = "alloc")]
224    fn dealloc(ptr: *mut UuidHeader) {
225        unsafe {
226            dealloc(ptr.cast::<u8>(), Self::layout());
227        }
228    }
229
230    fn header(&self) -> &UuidHeader {
231        unsafe { &*(self.0.heap_ptr() as *const UuidHeader) }
232    }
233
234    /// Creates a new UUID from 16 bytes (big-endian).
235    #[cfg(feature = "alloc")]
236    #[must_use]
237    pub fn new(bytes: [u8; 16]) -> Self {
238        unsafe {
239            let ptr = Self::alloc();
240            // Use ptr::write to avoid dropping uninitialized memory
241            core::ptr::write(&raw mut (*ptr).kind, OtherKind::Uuid);
242            core::ptr::write(&raw mut (*ptr)._pad, [0; 7]);
243            core::ptr::write(&raw mut (*ptr).bytes, bytes);
244            VUuid(Value::new_ptr(ptr.cast(), TypeTag::Other))
245        }
246    }
247
248    /// Creates a new UUID from two 64-bit integers (high and low parts).
249    #[cfg(feature = "alloc")]
250    #[must_use]
251    pub fn from_u64_pair(high: u64, low: u64) -> Self {
252        let mut bytes = [0u8; 16];
253        bytes[..8].copy_from_slice(&high.to_be_bytes());
254        bytes[8..].copy_from_slice(&low.to_be_bytes());
255        Self::new(bytes)
256    }
257
258    /// Creates a new UUID from a u128.
259    #[cfg(feature = "alloc")]
260    #[must_use]
261    pub fn from_u128(value: u128) -> Self {
262        Self::new(value.to_be_bytes())
263    }
264
265    /// Returns the UUID as 16 bytes (big-endian).
266    #[must_use]
267    pub fn as_bytes(&self) -> &[u8; 16] {
268        &self.header().bytes
269    }
270
271    /// Returns the UUID as a u128.
272    #[must_use]
273    pub fn as_u128(&self) -> u128 {
274        u128::from_be_bytes(self.header().bytes)
275    }
276
277    /// Returns the high 64 bits of the UUID.
278    #[must_use]
279    pub fn high(&self) -> u64 {
280        let bytes = &self.header().bytes;
281        u64::from_be_bytes([
282            bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7],
283        ])
284    }
285
286    /// Returns the low 64 bits of the UUID.
287    #[must_use]
288    pub fn low(&self) -> u64 {
289        let bytes = &self.header().bytes;
290        u64::from_be_bytes([
291            bytes[8], bytes[9], bytes[10], bytes[11], bytes[12], bytes[13], bytes[14], bytes[15],
292        ])
293    }
294
295    // === Internal ===
296
297    pub(crate) fn clone_impl(&self) -> Value {
298        #[cfg(feature = "alloc")]
299        {
300            Self::new(self.header().bytes).0
301        }
302        #[cfg(not(feature = "alloc"))]
303        {
304            panic!("cannot clone VUuid without alloc feature")
305        }
306    }
307
308    pub(crate) fn drop_impl(&mut self) {
309        #[cfg(feature = "alloc")]
310        unsafe {
311            Self::dealloc(self.0.heap_ptr_mut().cast());
312        }
313    }
314}
315
316impl Clone for VUuid {
317    fn clone(&self) -> Self {
318        VUuid(self.clone_impl())
319    }
320}
321
322impl PartialEq for VUuid {
323    fn eq(&self, other: &Self) -> bool {
324        self.header().bytes == other.header().bytes
325    }
326}
327
328impl Eq for VUuid {}
329
330impl Hash for VUuid {
331    fn hash<H: Hasher>(&self, state: &mut H) {
332        self.header().bytes.hash(state);
333    }
334}
335
336impl Debug for VUuid {
337    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
338        let bytes = &self.header().bytes;
339        // Format as standard UUID: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
340        write!(
341            f,
342            "{:02x}{:02x}{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}",
343            bytes[0],
344            bytes[1],
345            bytes[2],
346            bytes[3],
347            bytes[4],
348            bytes[5],
349            bytes[6],
350            bytes[7],
351            bytes[8],
352            bytes[9],
353            bytes[10],
354            bytes[11],
355            bytes[12],
356            bytes[13],
357            bytes[14],
358            bytes[15]
359        )
360    }
361}
362
363#[cfg(feature = "alloc")]
364impl From<VUuid> for Value {
365    fn from(uuid: VUuid) -> Self {
366        uuid.0
367    }
368}
369
370#[cfg(feature = "alloc")]
371impl From<[u8; 16]> for VUuid {
372    fn from(bytes: [u8; 16]) -> Self {
373        Self::new(bytes)
374    }
375}
376
377#[cfg(feature = "alloc")]
378impl From<u128> for VUuid {
379    fn from(value: u128) -> Self {
380        Self::from_u128(value)
381    }
382}
383
384// ============================================================================
385// Helper to get OtherKind from a Value with tag 7
386// ============================================================================
387
388/// Returns the OtherKind for a Value that has TypeTag::Other.
389///
390/// # Safety
391/// The value must have TypeTag::Other (tag 7) and point to valid heap memory.
392pub(crate) unsafe fn get_other_kind(value: &Value) -> OtherKind {
393    // The first byte of any Other header is the OtherKind discriminant
394    let ptr = value.heap_ptr();
395    unsafe { *(ptr as *const OtherKind) }
396}
397
398#[cfg(test)]
399mod tests {
400    use super::*;
401    use crate::VString;
402
403    #[test]
404    fn test_qname_with_namespace() {
405        let qname = VQName::new(VString::new("http://example.com"), VString::new("element"));
406        assert!(qname.has_namespace());
407        assert_eq!(
408            qname.namespace().unwrap().as_string().unwrap().as_str(),
409            "http://example.com"
410        );
411        assert_eq!(qname.local_name().as_string().unwrap().as_str(), "element");
412    }
413
414    #[test]
415    fn test_qname_local_only() {
416        let qname = VQName::new_local(VString::new("element"));
417        assert!(!qname.has_namespace());
418        assert!(qname.namespace().is_none());
419        assert_eq!(qname.local_name().as_string().unwrap().as_str(), "element");
420    }
421
422    #[test]
423    fn test_qname_clone() {
424        let qname = VQName::new(VString::new("ns"), VString::new("local"));
425        let cloned = qname.clone();
426        assert_eq!(qname, cloned);
427    }
428
429    #[test]
430    fn test_qname_debug() {
431        let qname = VQName::new(VString::new("ns"), VString::new("local"));
432        let debug = format!("{qname:?}");
433        assert!(debug.contains("ns"));
434        assert!(debug.contains("local"));
435    }
436
437    #[test]
438    fn test_uuid_new() {
439        let bytes = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16];
440        let uuid = VUuid::new(bytes);
441        assert_eq!(uuid.as_bytes(), &bytes);
442    }
443
444    #[test]
445    fn test_uuid_from_u128() {
446        let value: u128 = 0x0102030405060708090a0b0c0d0e0f10;
447        let uuid = VUuid::from_u128(value);
448        assert_eq!(uuid.as_u128(), value);
449    }
450
451    #[test]
452    fn test_uuid_high_low() {
453        let uuid = VUuid::from_u64_pair(0x0102030405060708, 0x090a0b0c0d0e0f10);
454        assert_eq!(uuid.high(), 0x0102030405060708);
455        assert_eq!(uuid.low(), 0x090a0b0c0d0e0f10);
456    }
457
458    #[test]
459    fn test_uuid_clone() {
460        let uuid = VUuid::from_u128(0x12345678_9abc_def0_1234_56789abcdef0);
461        let cloned = uuid.clone();
462        assert_eq!(uuid, cloned);
463    }
464
465    #[test]
466    fn test_uuid_debug_format() {
467        let uuid = VUuid::from_u128(0x12345678_9abc_def0_1234_56789abcdef0);
468        let debug = format!("{uuid:?}");
469        assert_eq!(debug, "12345678-9abc-def0-1234-56789abcdef0");
470    }
471}