1use std::{
2 ffi::{CStr, c_char, c_int},
3 mem::ManuallyDrop,
4 ops::{Deref, DerefMut},
5};
6
7use thiserror::Error;
8
9use crate::{
10 api::Api,
11 ffi,
12 frame::{AudioFrame, Frame, VideoFrame, internal::FrameFromPtr},
13 function::Function,
14 node::{AudioNode, Node, VideoNode},
15};
16
17mod key;
18pub use key::*;
19
20#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
24pub struct MapRef<'m> {
25 handle: *const ffi::VSMap,
26 api: Api,
27 marker: std::marker::PhantomData<&'m ()>,
28}
29
30impl MapRef<'_> {
31 #[inline]
33 pub(crate) unsafe fn from_ptr(ptr: *const ffi::VSMap, api: Api) -> Self {
34 debug_assert!(!ptr.is_null());
35 Self {
36 handle: ptr,
37 api,
38 marker: std::marker::PhantomData,
39 }
40 }
41
42 #[inline]
44 pub(crate) fn as_ptr(&self) -> *mut ffi::VSMap {
45 self.handle.cast_mut()
46 }
47}
48
49impl Deref for MapRef<'_> {
50 type Target = Map;
51
52 fn deref(&self) -> &Self::Target {
53 unsafe { &*std::ptr::from_ref(self).cast() }
54 }
55}
56
57impl DerefMut for MapRef<'_> {
58 fn deref_mut(&mut self) -> &mut Self::Target {
59 unsafe { &mut *std::ptr::from_mut(self).cast() }
60 }
61}
62
63#[derive(Debug, PartialEq, Eq, Hash)]
67pub struct Map {
68 handle: *const ffi::VSMap,
69 api: Api,
70}
71
72impl Map {
73 #[must_use]
75 pub(crate) unsafe fn from_ptr(ptr: *mut ffi::VSMap, api: Api) -> Self {
76 debug_assert!(!ptr.is_null());
77 Self { handle: ptr, api }
78 }
79
80 #[must_use]
82 pub fn as_ptr(&self) -> *mut ffi::VSMap {
83 self.handle.cast_mut()
84 }
85}
86
87impl Map {
88 pub fn clear(&mut self) {
89 unsafe { (self.api.clearMap)(self.as_ptr()) }
91 }
92
93 pub fn set_error(&mut self, msg: &CStr) {
94 unsafe { (self.api.mapSetError)(self.as_ptr(), msg.as_ptr()) }
96 }
97
98 #[must_use]
99 pub fn get_error(&self) -> Option<&CStr> {
100 let ptr = unsafe { (self.api.mapGetError)(self.as_ptr()) };
101 if ptr.is_null() {
102 None
103 } else {
104 Some(unsafe { CStr::from_ptr(ptr) })
105 }
106 }
107
108 #[must_use]
109 pub fn len(&self) -> i32 {
110 unsafe { (self.api.mapNumKeys)(self.as_ptr()) }
112 }
113
114 #[must_use]
115 pub fn is_empty(&self) -> bool {
116 self.len() == 0
117 }
118
119 #[must_use]
125 pub fn get_key(&self, index: i32) -> &KeyStr {
126 assert!(!(index < 0 || index >= self.len()), "index out of bounds");
127
128 unsafe { KeyStr::from_ptr((self.api.mapGetKey)(self.as_ptr(), index)) }
130 }
131
132 pub fn delete_key(&mut self, key: &KeyStr) {
133 unsafe { (self.api.mapDeleteKey)(self.as_ptr(), key.as_ptr()) };
135 }
136
137 #[must_use]
138 pub fn num_elements(&self, key: &KeyStr) -> Option<i32> {
139 let res = unsafe { (self.api.mapNumElements)(self.as_ptr(), key.as_ptr()) };
141 if res == -1 { None } else { Some(res) }
142 }
143
144 unsafe fn get_internal<T>(
145 &self,
146 func: unsafe extern "system-unwind" fn(
147 *const ffi::VSMap,
148 *const c_char,
149 c_int,
150 *mut ffi::VSMapPropertyError,
151 ) -> T,
152 key: &KeyStr,
153 index: i32,
154 ) -> Result<T, MapPropertyError> {
155 let mut error = ffi::VSMapPropertyError::Success;
156 handle_get_error(
157 unsafe { func(self.as_ptr(), key.as_ptr(), index, &mut error) },
158 error,
159 )
160 }
161
162 pub fn get_int(&self, key: &KeyStr, index: i32) -> Result<i64, MapPropertyError> {
166 unsafe { self.get_internal(self.api.mapGetInt, key, index) }
167 }
168
169 pub fn get_float(&self, key: &KeyStr, index: i32) -> Result<f64, MapPropertyError> {
173 unsafe { self.get_internal(self.api.mapGetFloat, key, index) }
174 }
175
176 #[allow(clippy::cast_sign_loss)]
180 pub fn get_binary(&self, key: &KeyStr, index: i32) -> Result<&[u8], MapPropertyError> {
181 use ffi::VSDataTypeHint as dt;
182
183 unsafe {
184 if let dt::Unknown | dt::Binary =
185 self.get_internal(self.api.mapGetDataTypeHint, key, index)?
186 {
187 let size = self.get_internal(self.api.mapGetDataSize, key, index)?;
188 let ptr = self.get_internal(self.api.mapGetData, key, index)?;
189
190 Ok(std::slice::from_raw_parts(ptr.cast(), size as _))
191 } else {
192 Err(MapPropertyError::InvalidType)
193 }
194 }
195 }
196
197 #[allow(clippy::cast_sign_loss)]
201 pub fn get_utf8(&self, key: &KeyStr, index: i32) -> Result<&str, MapPropertyError> {
202 unsafe {
203 if let ffi::VSDataTypeHint::Utf8 =
204 self.get_internal(self.api.mapGetDataTypeHint, key, index)?
205 {
206 let size = self.get_internal(self.api.mapGetDataSize, key, index)?;
207 let ptr = self.get_internal(self.api.mapGetData, key, index)?;
208
209 Ok(std::str::from_utf8_unchecked(std::slice::from_raw_parts(
210 ptr.cast(),
211 size as _,
212 )))
213 } else {
214 Err(MapPropertyError::InvalidType)
215 }
216 }
217 }
218
219 pub fn get_function(&self, key: &KeyStr, index: i32) -> Result<Function, MapPropertyError> {
223 unsafe {
224 self.get_internal(self.api.mapGetFunction, key, index)
225 .map(|p| Function::from_ptr(p, self.api))
226 }
227 }
228
229 pub fn get_video_node(&self, key: &KeyStr, index: i32) -> Result<VideoNode, MapPropertyError> {
233 unsafe {
234 self.get_internal(self.api.mapGetNode, key, index)
235 .map(|p| VideoNode::from_ptr(p, self.api))
236 }
237 }
238
239 pub fn get_audio_node(&self, key: &KeyStr, index: i32) -> Result<AudioNode, MapPropertyError> {
243 unsafe {
244 self.get_internal(self.api.mapGetNode, key, index)
245 .map(|p| AudioNode::from_ptr(p, self.api))
246 }
247 }
248
249 pub fn get_video_frame(
253 &self,
254 key: &KeyStr,
255 index: i32,
256 ) -> Result<VideoFrame, MapPropertyError> {
257 unsafe {
258 self.get_internal(self.api.mapGetFrame, key, index)
259 .map(|p| VideoFrame::from_ptr(p, self.api))
260 }
261 }
262
263 pub fn get_audio_frame(
267 &self,
268 key: &KeyStr,
269 index: i32,
270 ) -> Result<AudioFrame, MapPropertyError> {
271 unsafe {
272 self.get_internal(self.api.mapGetFrame, key, index)
273 .map(|p| AudioFrame::from_ptr(p, self.api))
274 }
275 }
276
277 pub fn get(&self, key: &KeyStr, index: i32) -> Result<Value, MapPropertyError> {
281 use ffi::VSPropertyType as t;
282
283 unsafe {
284 match (self.api.mapGetType)(self.as_ptr(), key.as_ptr()) {
285 t::Unset => Err(MapPropertyError::KeyNotFound),
286 t::Int => self.get_int(key, index).map(Value::Int),
287 t::Float => self.get_float(key, index).map(Value::Float),
288 t::Data => {
289 use ffi::VSDataTypeHint as dt;
290
291 let size = self.get_internal(self.api.mapGetDataSize, key, index)?;
292 #[allow(clippy::cast_sign_loss)]
293 match self.get_internal(self.api.mapGetDataTypeHint, key, index)? {
294 dt::Unknown | dt::Binary => {
295 let ptr = self.get_internal(self.api.mapGetData, key, index)?;
296 Ok(Value::Data(std::slice::from_raw_parts(
297 ptr.cast(),
298 size as _,
299 )))
300 }
301 dt::Utf8 => {
302 let ptr = self.get_internal(self.api.mapGetData, key, index)?;
303 Ok(Value::Utf8(std::str::from_utf8_unchecked(
304 std::slice::from_raw_parts(ptr.cast(), size as _),
305 )))
306 }
307 }
308 }
309 t::Function => self.get_function(key, index).map(Value::Function),
310 t::VideoNode => self.get_video_node(key, index).map(Value::VideoNode),
311 t::AudioNode => self.get_audio_node(key, index).map(Value::AudioNode),
312 t::VideoFrame => self.get_video_frame(key, index).map(Value::VideoFrame),
313 t::AudioFrame => self.get_audio_frame(key, index).map(Value::AudioFrame),
314 }
315 }
316 }
317
318 pub fn get_int_saturated(&self, key: &KeyStr, index: i32) -> Result<i32, MapPropertyError> {
322 unsafe { self.get_internal(self.api.mapGetIntSaturated, key, index) }
323 }
324
325 pub fn get_int_array(&self, key: &KeyStr) -> Result<&[i64], MapPropertyError> {
329 let mut error = ffi::VSMapPropertyError::Success;
330 unsafe {
331 let size = self
332 .num_elements(key)
333 .ok_or(MapPropertyError::KeyNotFound)?;
334 let ptr = handle_get_error(
335 (self.api.mapGetIntArray)(self.as_ptr(), key.as_ptr(), &mut error),
336 error,
337 )?;
338
339 #[allow(clippy::cast_sign_loss)]
340 Ok(std::slice::from_raw_parts(ptr, size as _))
341 }
342 }
343
344 pub fn get_float_saturated(&self, key: &KeyStr, index: i32) -> Result<f32, MapPropertyError> {
348 unsafe { self.get_internal(self.api.mapGetFloatSaturated, key, index) }
350 }
351
352 pub fn get_float_array(&self, key: &KeyStr) -> Result<&[f64], MapPropertyError> {
356 let mut error = ffi::VSMapPropertyError::Success;
357 unsafe {
358 let size = self
359 .num_elements(key)
360 .ok_or(MapPropertyError::KeyNotFound)?;
361 let ptr = handle_get_error(
362 (self.api.mapGetFloatArray)(self.as_ptr(), key.as_ptr(), &mut error),
363 error,
364 )?;
365
366 #[allow(clippy::cast_sign_loss)]
367 Ok(std::slice::from_raw_parts(ptr, size as _))
368 }
369 }
370
371 pub fn set_empty(&mut self, key: &KeyStr, type_: ffi::VSPropertyType) {
377 let res = unsafe { (self.api.mapSetEmpty)(self.as_ptr(), key.as_ptr(), type_) };
379 assert!(res != 0);
380 }
381
382 unsafe fn set_internal<T>(
383 &mut self,
384 func: unsafe extern "system-unwind" fn(
385 *mut ffi::VSMap,
386 *const c_char,
387 T,
388 ffi::VSMapAppendMode,
389 ) -> c_int,
390 key: &KeyStr,
391 val: T,
392 append: ffi::VSMapAppendMode,
393 ) -> Result<(), MapPropertyError> {
394 handle_set_error(unsafe { func(self.as_ptr(), key.as_ptr(), val, append) })
395 }
396
397 pub fn set(
405 &mut self,
406 key: &KeyStr,
407 val: Value,
408 append: AppendMode,
409 ) -> Result<(), MapPropertyError> {
410 unsafe {
411 match val {
412 Value::Int(val) => self.set_internal(self.api.mapSetInt, key, val, append),
413 Value::Float(val) => self.set_internal(self.api.mapSetFloat, key, val, append),
414 Value::Data(val) => handle_set_error((self.api.mapSetData)(
415 self.as_ptr(),
416 key.as_ptr(),
417 val.as_ptr().cast(),
418 val.len().try_into().unwrap(),
419 ffi::VSDataTypeHint::Binary,
420 append,
421 )),
422 Value::Utf8(val) => handle_set_error((self.api.mapSetData)(
423 self.as_ptr(),
424 key.as_ptr(),
425 val.as_ptr().cast(),
426 val.len().try_into().unwrap(),
427 ffi::VSDataTypeHint::Utf8,
428 append,
429 )),
430 Value::VideoNode(val) => {
431 self.set_internal(self.api.mapSetNode, key, val.as_ptr(), append)
432 }
433 Value::AudioNode(val) => {
434 self.set_internal(self.api.mapSetNode, key, val.as_ptr(), append)
435 }
436 Value::VideoFrame(val) => {
437 self.set_internal(self.api.mapSetFrame, key, val.as_ptr(), append)
438 }
439 Value::AudioFrame(val) => {
440 self.set_internal(self.api.mapSetFrame, key, val.as_ptr(), append)
441 }
442 Value::Function(val) => {
443 self.set_internal(self.api.mapSetFunction, key, val.as_ptr(), append)
444 }
445 }
446 }
447 }
448
449 pub fn set_int_array(&mut self, key: &KeyStr, val: &[i64]) -> Result<(), MapPropertyError> {
457 unsafe {
458 handle_set_error((self.api.mapSetIntArray)(
459 self.as_ptr(),
460 key.as_ptr(),
461 val.as_ptr(),
462 val.len().try_into().unwrap(),
463 ))
464 }
465 }
466
467 pub fn set_float_array(&mut self, key: &KeyStr, val: &[f64]) -> Result<(), MapPropertyError> {
475 unsafe {
476 handle_set_error((self.api.mapSetFloatArray)(
477 self.as_ptr(),
478 key.as_ptr(),
479 val.as_ptr(),
480 val.len().try_into().unwrap(),
481 ))
482 }
483 }
484
485 pub fn consume_node(
489 &mut self,
490 key: &KeyStr,
491 node: impl Node,
492 append: AppendMode,
493 ) -> Result<(), MapPropertyError> {
494 let node = ManuallyDrop::new(node);
495 unsafe {
496 handle_set_error((self.api.mapConsumeNode)(
497 self.as_ptr(),
498 key.as_ptr(),
499 node.as_ptr(),
500 append,
501 ))
502 }
503 }
504
505 pub fn consume_frame(
509 &mut self,
510 key: &KeyStr,
511 frame: impl Frame,
512 append: AppendMode,
513 ) -> Result<(), MapPropertyError> {
514 let frame = ManuallyDrop::new(frame);
515 unsafe {
516 handle_set_error((self.api.mapConsumeFrame)(
517 self.as_ptr(),
518 key.as_ptr(),
519 frame.as_ptr(),
520 append,
521 ))
522 }
523 }
524
525 pub fn consume_function(
529 &mut self,
530 key: &KeyStr,
531 function: Function,
532 append: AppendMode,
533 ) -> Result<(), MapPropertyError> {
534 let function = ManuallyDrop::new(function);
535 unsafe {
536 handle_set_error((self.api.mapConsumeFunction)(
537 self.as_ptr(),
538 key.as_ptr(),
539 function.as_ptr(),
540 append,
541 ))
542 }
543 }
544}
545
546impl Drop for Map {
547 fn drop(&mut self) {
548 unsafe { (self.api.freeMap)(self.as_ptr()) }
550 }
551}
552
553impl Clone for Map {
554 fn clone(&self) -> Self {
555 unsafe {
557 let ptr = (self.api.createMap)();
558 (self.api.copyMap)(self.as_ptr(), ptr);
559 Self::from_ptr(ptr, self.api)
560 }
561 }
562}
563
564#[cfg(feature = "link-library")]
565impl Default for Map {
566 fn default() -> Self {
567 unsafe {
568 let api = Api::default();
569 let ptr = (api.createMap)();
570 Self::from_ptr(ptr, api)
571 }
572 }
573}
574
575fn handle_get_error<T>(res: T, error: ffi::VSMapPropertyError) -> Result<T, MapPropertyError> {
578 use MapPropertyError as pe;
579 use ffi::VSMapPropertyError as e;
580
581 match error {
582 e::Success => Ok(res),
583 e::Unset => Err(pe::KeyNotFound),
584 e::Type => Err(pe::InvalidType),
585 e::Index => Err(pe::IndexOutOfBound),
586 e::Error => Err(pe::MapError),
587 }
588}
589
590fn handle_set_error(res: i32) -> Result<(), MapPropertyError> {
591 if res == 0 {
592 Ok(())
593 } else {
594 Err(MapPropertyError::InvalidType)
595 }
596}
597
598#[derive(Clone, Debug)]
601pub enum Value<'m> {
602 Int(i64),
603 Float(f64),
604 Data(&'m [u8]),
610 Utf8(&'m str),
611 VideoNode(VideoNode),
612 AudioNode(AudioNode),
613 VideoFrame(VideoFrame),
614 AudioFrame(AudioFrame),
615 Function(Function),
616}
617
618#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Error)]
619pub enum MapPropertyError {
620 #[error("The requested key was not found in the map")]
621 KeyNotFound,
622 #[error("The wrong function was used to retrieve the property")]
623 InvalidType,
624 #[error("The requested index was out of bound")]
625 IndexOutOfBound,
626 #[error("The map has errors. Use [`Map::get_error`] to retrieve the message")]
627 MapError,
628}
629
630pub type AppendMode = ffi::VSMapAppendMode;
631
632#[cfg(test)]
635#[cfg(feature = "link-library")]
636mod tests {
637 use core::panic;
638
639 use const_str::cstr;
640 use testresult::TestResult;
641
642 use super::*;
643
644 #[test]
645 fn clear() -> TestResult {
646 let mut map = Map::default();
647 let key = crate::key!(c"what");
648 map.set(key, Value::Int(42), AppendMode::Replace)?;
649
650 map.clear();
651 match map.get(key, 0) {
652 Err(MapPropertyError::KeyNotFound) => Ok(()),
653 _ => panic!("Map is not cleared"),
654 }
655 }
656
657 #[test]
658 fn error() -> TestResult {
659 let mut map = Map::default();
660 let key = crate::key!(c"what");
661 map.set(key, Value::Float(42.0), AppendMode::Replace)?;
662
663 map.set_error(cstr!("Yes"));
664 match map.get_error() {
665 Some(msg) => assert_eq!(msg, cstr!("Yes"), "Error message is not match"),
666 None => panic!("Error is not set"),
667 }
668 let res = map.get(key, 0);
669 match res {
670 Err(MapPropertyError::KeyNotFound) => {}
671 _ => panic!("Map is not cleared after setting error"),
672 }
673
674 map.set(key, Value::Float(42.0), AppendMode::Replace)?;
675 let res = map.get(key, 0);
676 match res {
677 Err(MapPropertyError::MapError) => {}
678 _ => panic!(
679 "Map after setting error can only be freed, \
680 cleared, or queried for error"
681 ),
682 }
683
684 Ok(())
685 }
686
687 #[test]
688 fn len() -> TestResult {
689 let mut map = Map::default();
690 let key = crate::key!(c"what");
691
692 map.set(key, Value::Data(&[42, 43, 44, 45]), AppendMode::Replace)?;
693 assert_eq!(1, map.len(), "Number of keys is not correct");
694
695 assert!(!map.is_empty(), "Map is not empty");
696
697 Ok(())
698 }
699
700 #[test]
701 fn key() -> TestResult {
702 let mut map = Map::default();
703 let key = crate::key!(c"what");
704
705 map.set(key, Value::Float(42.0), AppendMode::Append)?;
706
707 assert_eq!(key, map.get_key(0), "Key is not correct");
708
709 match map.num_elements(key) {
710 Some(num) => assert_eq!(1, num),
711 None => panic!("Key `{key}` not found "),
712 }
713
714 map.delete_key(key);
715 assert_eq!(
716 0,
717 map.len(),
718 "Number of keys is not correct after deleting `{key}`"
719 );
720
721 Ok(())
722 }
723
724 #[test]
725 #[allow(clippy::float_cmp)]
726 fn get_set() -> TestResult {
727 let mut map = Map::default();
728 let key = crate::key!(c"what");
729
730 let source = i64::from(i32::MAX) + 1;
731 map.set(key, Value::Int(source), AppendMode::Replace)?;
732 let res = map.get(key, 0)?;
733 match res {
734 Value::Int(val) => assert_eq!(val, source, "Value of `{key}` is not correct"),
735 _ => panic!("Invalid type of `{key}`"),
736 }
737 let res = map.get_int_saturated(key, 0)?;
738 assert_eq!(res, i32::MAX, "Value of `{key}` is not correct");
739 map.set(key, Value::Int(source), AppendMode::Append)?;
740 assert_eq!(&[source, source], map.get_int_array(key)?);
741 map.set_int_array(key, &[1, 2, 3])?;
742 assert_eq!(&[1, 2, 3], map.get_int_array(key)?);
743
744 map.set(key, Value::Float(1e25), AppendMode::Replace)?;
745 let res = map.get(key, 0)?;
746 match res {
747 Value::Float(val) => {
748 assert_eq!(val, 1e25, "Value of `{key}` is not correct");
749 }
750 _ => panic!("Invalid type of `{key}`"),
751 }
752 let res = map.get_float_saturated(key, 0)?;
753 assert_eq!(
754 res, 9_999_999_562_023_526_247_432_192.0,
755 "Value of `{key}` is not correct"
756 );
757 map.set(key, Value::Float(f64::MAX), AppendMode::Append)?;
758 assert_eq!(&[1e25, f64::MAX], map.get_float_array(key)?);
759 map.set_float_array(key, &[1.0, 2.0, 3.0])?;
760 assert_eq!(&[1.0, 2.0, 3.0], map.get_float_array(key)?);
761
762 map.set(key, Value::Data(&[42, 43]), AppendMode::Replace)?;
763 let res = map.get(key, 0)?;
764 match res {
765 Value::Data(val) => {
766 assert_eq!(val, &[42, 43], "Value of `{key}` is not correct");
767 }
768 _ => panic!("Invalid type of `{key}`"),
769 }
770
771 map.set(key, Value::Utf8("good"), AppendMode::Replace)?;
772 let res = map.get(key, 0)?;
773 match res {
774 Value::Utf8(val) => {
775 assert_eq!(val, "good", "Value of `{key}` is not correct");
776 }
777 _ => panic!("Invalid type of `{key}`"),
778 }
779
780 Ok(())
781 }
782}