1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
/*
 * SPDX-License-Identifier: MIT
 */
use std::ffi::{c_char, CStr, CString};
use std::fmt::{Debug, Formatter};
use std::io::{Seek, Write};

use pipewire_wrapper_proc_macro::RawWrapper;

use crate::spa::pod::pod_buf::PodBuf;
use crate::spa::pod::restricted::{PodHeader, PodRawValue};
use crate::spa::pod::{
    BasicTypePod, FromValue, PodError, PodResult, PodValue, SizedPod, WritePod, WriteValue,
    POD_ALIGN,
};
use crate::spa::type_::Type;
use crate::wrapper::RawWrapper;

use super::restricted::{write_align_padding, write_header};

#[derive(RawWrapper)]
#[repr(transparent)]
pub struct PodStringRef {
    #[raw]
    raw: spa_sys::spa_pod_string,
}

impl PodHeader for PodStringRef {
    fn pod_header(&self) -> &spa_sys::spa_pod {
        &self.raw.pod
    }

    fn static_type() -> Type {
        Type::STRING
    }
}

impl PodStringRef {
    fn content_size(&self) -> usize {
        self.raw.pod.size as usize
    }

    unsafe fn content_ptr(&self) -> *const c_char {
        (self as *const Self).offset(1).cast()
    }
}

impl<'a> PodRawValue for &'a PodStringRef {
    type RawValue = c_char;

    fn raw_value_ptr(&self) -> *const Self::RawValue {
        unsafe { (&self.raw.pod as *const spa_sys::spa_pod).offset(1).cast() }
    }

    fn parse_raw_value(ptr: *const Self::RawValue, size: usize) -> PodResult<Self::Value> {
        unsafe {
            if *(ptr as *const u8).add(size - 1) != 0 {
                Err(PodError::StringIsNotNullTerminated)
            } else {
                Ok(CStr::from_ptr(ptr))
            }
        }
    }
}

impl<'a> PodValue for &'a PodStringRef {
    type Value = &'a CStr;
    fn value(&self) -> PodResult<Self::Value> {
        Self::parse_raw_value(self.raw_value_ptr(), self.pod_header().size as usize)
    }
}

impl<'a> WritePod for &'a PodStringRef {
    fn write_pod<W>(buffer: &mut W, value: &<Self as PodValue>::Value) -> PodResult<()>
    where
        W: Write + Seek,
    {
        let string_bytes = value.to_bytes_with_nul();
        write_header(
            buffer,
            string_bytes.len() as u32,
            PodStringRef::static_type(),
        )?;
        buffer.write_all(string_bytes)?;
        write_align_padding(buffer)
    }
}

impl<'a> WriteValue for &'a PodStringRef {
    fn write_raw_value<W>(buffer: &mut W, value: &<Self as PodValue>::Value) -> PodResult<()>
    where
        W: Write + Seek,
    {
        let string_bytes = value.to_bytes_with_nul();
        buffer.write_all(string_bytes)?;
        Ok(())
    }
}

impl Debug for PodStringRef {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        unsafe {
            f.debug_struct("PodStringRef")
                .field("pod.type", &self.pod_type())
                .field("pod.size", &self.pod_size())
                .field("value", &self.value())
                .finish()
        }
    }
}

#[test]
fn test_from_value() {
    let string = CString::new("Test string").unwrap();
    let string_wrong = CString::new("Test string wrong").unwrap();
    let allocated_pod = PodStringRef::from_value(&string.as_ref()).unwrap();
    assert_eq!(allocated_pod.as_pod().as_ptr().align_offset(POD_ALIGN), 0);
    assert_eq!(allocated_pod.as_pod().pod_size(), 20);
    assert_eq!(allocated_pod.as_pod().pod_header().size, 12);
    assert_eq!(allocated_pod.as_pod().pod_header().type_, Type::STRING.raw);
    assert_eq!(allocated_pod.as_pod().value().unwrap(), string.as_ref());
    assert_ne!(
        allocated_pod.as_pod().value().unwrap(),
        string_wrong.as_ref()
    );
}