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
30unsafe impl Send for MapRef<'_> {}
31
32impl MapRef<'_> {
33 #[inline]
35 pub(crate) unsafe fn from_ptr(ptr: *const ffi::VSMap, api: Api) -> Self {
36 debug_assert!(!ptr.is_null());
37 Self {
38 handle: ptr,
39 api,
40 marker: std::marker::PhantomData,
41 }
42 }
43
44 #[inline]
46 pub(crate) fn as_ptr(&self) -> *mut ffi::VSMap {
47 self.handle.cast_mut()
48 }
49}
50
51impl Deref for MapRef<'_> {
52 type Target = Map;
53
54 fn deref(&self) -> &Self::Target {
55 unsafe { &*std::ptr::from_ref(self).cast() }
56 }
57}
58
59impl DerefMut for MapRef<'_> {
60 fn deref_mut(&mut self) -> &mut Self::Target {
61 unsafe { &mut *std::ptr::from_mut(self).cast() }
62 }
63}
64
65#[derive(Debug, PartialEq, Eq, Hash)]
69pub struct Map {
70 handle: *const ffi::VSMap,
71 api: Api,
72}
73
74unsafe impl Send for Map {}
75
76impl Map {
77 #[must_use]
79 pub(crate) unsafe fn from_ptr(ptr: *mut ffi::VSMap, api: Api) -> Self {
80 debug_assert!(!ptr.is_null());
81 Self { handle: ptr, api }
82 }
83
84 #[must_use]
86 pub fn as_ptr(&self) -> *mut ffi::VSMap {
87 self.handle.cast_mut()
88 }
89}
90
91impl Map {
92 pub fn clear(&mut self) {
93 unsafe { (self.api.clearMap)(self.as_ptr()) }
95 }
96
97 pub fn set_error(&mut self, msg: &CStr) {
98 unsafe { (self.api.mapSetError)(self.as_ptr(), msg.as_ptr()) }
100 }
101
102 #[must_use]
103 pub fn get_error(&self) -> Option<&CStr> {
104 let ptr = unsafe { (self.api.mapGetError)(self.as_ptr()) };
105 if ptr.is_null() {
106 None
107 } else {
108 Some(unsafe { CStr::from_ptr(ptr) })
109 }
110 }
111
112 #[must_use]
113 pub fn len(&self) -> i32 {
114 unsafe { (self.api.mapNumKeys)(self.as_ptr()) }
116 }
117
118 #[must_use]
119 pub fn is_empty(&self) -> bool {
120 self.len() == 0
121 }
122
123 #[must_use]
129 pub fn get_key(&self, index: i32) -> &KeyStr {
130 assert!(!(index < 0 || index >= self.len()), "index out of bounds");
131
132 unsafe { KeyStr::from_ptr((self.api.mapGetKey)(self.as_ptr(), index)) }
134 }
135
136 pub fn delete_key(&mut self, key: &KeyStr) {
137 unsafe { (self.api.mapDeleteKey)(self.as_ptr(), key.as_ptr()) };
139 }
140
141 #[must_use]
142 pub fn num_elements(&self, key: &KeyStr) -> Option<i32> {
143 let res = unsafe { (self.api.mapNumElements)(self.as_ptr(), key.as_ptr()) };
145 if res == -1 { None } else { Some(res) }
146 }
147
148 unsafe fn get_internal<T>(
149 &self,
150 func: unsafe extern "system-unwind" fn(
151 *const ffi::VSMap,
152 *const c_char,
153 c_int,
154 *mut ffi::VSMapPropertyError,
155 ) -> T,
156 key: &KeyStr,
157 index: i32,
158 ) -> Result<T, MapPropertyError> {
159 let mut error = ffi::VSMapPropertyError::Success;
160 handle_get_error(
161 unsafe { func(self.as_ptr(), key.as_ptr(), index, &raw mut error) },
162 error,
163 )
164 }
165
166 pub fn get_int(&self, key: &KeyStr, index: i32) -> Result<i64, MapPropertyError> {
170 unsafe { self.get_internal(self.api.mapGetInt, key, index) }
171 }
172
173 pub fn get_float(&self, key: &KeyStr, index: i32) -> Result<f64, MapPropertyError> {
177 unsafe { self.get_internal(self.api.mapGetFloat, key, index) }
178 }
179
180 #[allow(clippy::cast_sign_loss)]
184 pub fn get_binary(&self, key: &KeyStr, index: i32) -> Result<&[u8], MapPropertyError> {
185 use ffi::VSDataTypeHint as dt;
186
187 unsafe {
188 if let dt::Unknown | dt::Binary =
189 self.get_internal(self.api.mapGetDataTypeHint, key, index)?
190 {
191 let size = self.get_internal(self.api.mapGetDataSize, key, index)?;
192 let ptr = self.get_internal(self.api.mapGetData, key, index)?;
193
194 Ok(std::slice::from_raw_parts(ptr.cast(), size as _))
195 } else {
196 Err(MapPropertyError::InvalidType)
197 }
198 }
199 }
200
201 #[allow(clippy::cast_sign_loss)]
205 pub fn get_utf8(&self, key: &KeyStr, index: i32) -> Result<&str, MapPropertyError> {
206 unsafe {
207 if let ffi::VSDataTypeHint::Utf8 =
208 self.get_internal(self.api.mapGetDataTypeHint, key, index)?
209 {
210 let size = self.get_internal(self.api.mapGetDataSize, key, index)?;
211 let ptr = self.get_internal(self.api.mapGetData, key, index)?;
212
213 Ok(std::str::from_utf8_unchecked(std::slice::from_raw_parts(
214 ptr.cast(),
215 size as _,
216 )))
217 } else {
218 Err(MapPropertyError::InvalidType)
219 }
220 }
221 }
222
223 pub fn get_function(&self, key: &KeyStr, index: i32) -> Result<Function, MapPropertyError> {
227 unsafe {
228 self.get_internal(self.api.mapGetFunction, key, index)
229 .map(|p| Function::from_ptr(p, self.api))
230 }
231 }
232
233 pub fn get_video_node(&self, key: &KeyStr, index: i32) -> Result<VideoNode, MapPropertyError> {
237 unsafe {
238 self.get_internal(self.api.mapGetNode, key, index)
239 .map(|p| VideoNode::from_ptr(p, self.api))
240 }
241 }
242
243 pub fn get_audio_node(&self, key: &KeyStr, index: i32) -> Result<AudioNode, MapPropertyError> {
247 unsafe {
248 self.get_internal(self.api.mapGetNode, key, index)
249 .map(|p| AudioNode::from_ptr(p, self.api))
250 }
251 }
252
253 pub fn get_video_frame(
257 &self,
258 key: &KeyStr,
259 index: i32,
260 ) -> Result<VideoFrame, MapPropertyError> {
261 unsafe {
262 self.get_internal(self.api.mapGetFrame, key, index)
263 .map(|p| VideoFrame::from_ptr(p, self.api))
264 }
265 }
266
267 pub fn get_audio_frame(
271 &self,
272 key: &KeyStr,
273 index: i32,
274 ) -> Result<AudioFrame, MapPropertyError> {
275 unsafe {
276 self.get_internal(self.api.mapGetFrame, key, index)
277 .map(|p| AudioFrame::from_ptr(p, self.api))
278 }
279 }
280
281 pub fn get(&self, key: &KeyStr, index: i32) -> Result<Value<'_>, MapPropertyError> {
285 use ffi::VSPropertyType as t;
286
287 unsafe {
288 match (self.api.mapGetType)(self.as_ptr(), key.as_ptr()) {
289 t::Unset => Err(MapPropertyError::KeyNotFound),
290 t::Int => self.get_int(key, index).map(Value::Int),
291 t::Float => self.get_float(key, index).map(Value::Float),
292 t::Data => {
293 use ffi::VSDataTypeHint as dt;
294
295 let size = self.get_internal(self.api.mapGetDataSize, key, index)?;
296 #[allow(clippy::cast_sign_loss)]
297 match self.get_internal(self.api.mapGetDataTypeHint, key, index)? {
298 dt::Unknown | dt::Binary => {
299 let ptr = self.get_internal(self.api.mapGetData, key, index)?;
300 Ok(Value::Data(std::slice::from_raw_parts(
301 ptr.cast(),
302 size as _,
303 )))
304 }
305 dt::Utf8 => {
306 let ptr = self.get_internal(self.api.mapGetData, key, index)?;
307 Ok(Value::Utf8(std::str::from_utf8_unchecked(
308 std::slice::from_raw_parts(ptr.cast(), size as _),
309 )))
310 }
311 }
312 }
313 t::Function => self.get_function(key, index).map(Value::Function),
314 t::VideoNode => self.get_video_node(key, index).map(Value::VideoNode),
315 t::AudioNode => self.get_audio_node(key, index).map(Value::AudioNode),
316 t::VideoFrame => self.get_video_frame(key, index).map(Value::VideoFrame),
317 t::AudioFrame => self.get_audio_frame(key, index).map(Value::AudioFrame),
318 }
319 }
320 }
321
322 pub fn get_int_saturated(&self, key: &KeyStr, index: i32) -> Result<i32, MapPropertyError> {
326 unsafe { self.get_internal(self.api.mapGetIntSaturated, key, index) }
327 }
328
329 pub fn get_int_array(&self, key: &KeyStr) -> Result<&[i64], MapPropertyError> {
333 let mut error = ffi::VSMapPropertyError::Success;
334 unsafe {
335 let size = self
336 .num_elements(key)
337 .ok_or(MapPropertyError::KeyNotFound)?;
338 let ptr = handle_get_error(
339 (self.api.mapGetIntArray)(self.as_ptr(), key.as_ptr(), &raw mut error),
340 error,
341 )?;
342
343 #[allow(clippy::cast_sign_loss)]
344 Ok(std::slice::from_raw_parts(ptr, size as _))
345 }
346 }
347
348 pub fn get_float_saturated(&self, key: &KeyStr, index: i32) -> Result<f32, MapPropertyError> {
352 unsafe { self.get_internal(self.api.mapGetFloatSaturated, key, index) }
354 }
355
356 pub fn get_float_array(&self, key: &KeyStr) -> Result<&[f64], MapPropertyError> {
360 let mut error = ffi::VSMapPropertyError::Success;
361 unsafe {
362 let size = self
363 .num_elements(key)
364 .ok_or(MapPropertyError::KeyNotFound)?;
365 let ptr = handle_get_error(
366 (self.api.mapGetFloatArray)(self.as_ptr(), key.as_ptr(), &raw mut error),
367 error,
368 )?;
369
370 #[allow(clippy::cast_sign_loss)]
371 Ok(std::slice::from_raw_parts(ptr, size as _))
372 }
373 }
374
375 pub fn set_empty(&mut self, key: &KeyStr, type_: ffi::VSPropertyType) {
381 let res = unsafe { (self.api.mapSetEmpty)(self.as_ptr(), key.as_ptr(), type_) };
383 assert!(res != 0);
384 }
385
386 unsafe fn set_internal<T>(
387 &mut self,
388 func: unsafe extern "system-unwind" fn(
389 *mut ffi::VSMap,
390 *const c_char,
391 T,
392 ffi::VSMapAppendMode,
393 ) -> c_int,
394 key: &KeyStr,
395 val: T,
396 append: ffi::VSMapAppendMode,
397 ) -> Result<(), MapPropertyError> {
398 handle_set_error(unsafe { func(self.as_ptr(), key.as_ptr(), val, append) })
399 }
400
401 pub fn set(
409 &mut self,
410 key: &KeyStr,
411 val: Value,
412 append: AppendMode,
413 ) -> Result<(), MapPropertyError> {
414 unsafe {
415 match val {
416 Value::Int(val) => self.set_internal(self.api.mapSetInt, key, val, append),
417 Value::Float(val) => self.set_internal(self.api.mapSetFloat, key, val, append),
418 Value::Data(val) => handle_set_error((self.api.mapSetData)(
419 self.as_ptr(),
420 key.as_ptr(),
421 val.as_ptr().cast(),
422 val.len().try_into().unwrap(),
423 ffi::VSDataTypeHint::Binary,
424 append,
425 )),
426 Value::Utf8(val) => handle_set_error((self.api.mapSetData)(
427 self.as_ptr(),
428 key.as_ptr(),
429 val.as_ptr().cast(),
430 val.len().try_into().unwrap(),
431 ffi::VSDataTypeHint::Utf8,
432 append,
433 )),
434 Value::VideoNode(val) => {
435 self.set_internal(self.api.mapSetNode, key, val.as_ptr(), append)
436 }
437 Value::AudioNode(val) => {
438 self.set_internal(self.api.mapSetNode, key, val.as_ptr(), append)
439 }
440 Value::VideoFrame(val) => {
441 self.set_internal(self.api.mapSetFrame, key, val.as_ptr(), append)
442 }
443 Value::AudioFrame(val) => {
444 self.set_internal(self.api.mapSetFrame, key, val.as_ptr(), append)
445 }
446 Value::Function(val) => {
447 self.set_internal(self.api.mapSetFunction, key, val.as_ptr(), append)
448 }
449 }
450 }
451 }
452
453 pub fn set_int_array(&mut self, key: &KeyStr, val: &[i64]) -> Result<(), MapPropertyError> {
461 unsafe {
462 handle_set_error((self.api.mapSetIntArray)(
463 self.as_ptr(),
464 key.as_ptr(),
465 val.as_ptr(),
466 val.len().try_into().unwrap(),
467 ))
468 }
469 }
470
471 pub fn set_float_array(&mut self, key: &KeyStr, val: &[f64]) -> Result<(), MapPropertyError> {
479 unsafe {
480 handle_set_error((self.api.mapSetFloatArray)(
481 self.as_ptr(),
482 key.as_ptr(),
483 val.as_ptr(),
484 val.len().try_into().unwrap(),
485 ))
486 }
487 }
488
489 pub fn consume_node(
493 &mut self,
494 key: &KeyStr,
495 node: impl Node,
496 append: AppendMode,
497 ) -> Result<(), MapPropertyError> {
498 let node = ManuallyDrop::new(node);
499 unsafe {
500 handle_set_error((self.api.mapConsumeNode)(
501 self.as_ptr(),
502 key.as_ptr(),
503 node.as_ptr(),
504 append,
505 ))
506 }
507 }
508
509 pub fn consume_frame(
513 &mut self,
514 key: &KeyStr,
515 frame: impl Frame,
516 append: AppendMode,
517 ) -> Result<(), MapPropertyError> {
518 let frame = ManuallyDrop::new(frame);
519 unsafe {
520 handle_set_error((self.api.mapConsumeFrame)(
521 self.as_ptr(),
522 key.as_ptr(),
523 frame.as_ptr(),
524 append,
525 ))
526 }
527 }
528
529 pub fn consume_function(
533 &mut self,
534 key: &KeyStr,
535 function: Function,
536 append: AppendMode,
537 ) -> Result<(), MapPropertyError> {
538 let function = ManuallyDrop::new(function);
539 unsafe {
540 handle_set_error((self.api.mapConsumeFunction)(
541 self.as_ptr(),
542 key.as_ptr(),
543 function.as_ptr(),
544 append,
545 ))
546 }
547 }
548}
549
550impl Drop for Map {
551 fn drop(&mut self) {
552 unsafe { (self.api.freeMap)(self.as_ptr()) }
554 }
555}
556
557impl Clone for Map {
558 fn clone(&self) -> Self {
559 unsafe {
561 let ptr = (self.api.createMap)();
562 (self.api.copyMap)(self.as_ptr(), ptr);
563 Self::from_ptr(ptr, self.api)
564 }
565 }
566}
567
568#[cfg(feature = "link-vs")]
569impl Default for Map {
570 fn default() -> Self {
571 unsafe {
572 let api = Api::default();
573 let ptr = (api.createMap)();
574 Self::from_ptr(ptr, api)
575 }
576 }
577}
578
579fn handle_get_error<T>(res: T, error: ffi::VSMapPropertyError) -> Result<T, MapPropertyError> {
582 use MapPropertyError as pe;
583 use ffi::VSMapPropertyError as e;
584
585 match error {
586 e::Success => Ok(res),
587 e::Unset => Err(pe::KeyNotFound),
588 e::Type => Err(pe::InvalidType),
589 e::Index => Err(pe::IndexOutOfBound),
590 e::Error => Err(pe::MapError),
591 }
592}
593
594fn handle_set_error(res: i32) -> Result<(), MapPropertyError> {
595 if res == 0 {
596 Ok(())
597 } else {
598 Err(MapPropertyError::InvalidType)
599 }
600}
601
602#[derive(Clone, Debug)]
605pub enum Value<'m> {
606 Int(i64),
607 Float(f64),
608 Data(&'m [u8]),
614 Utf8(&'m str),
615 VideoNode(VideoNode),
616 AudioNode(AudioNode),
617 VideoFrame(VideoFrame),
618 AudioFrame(AudioFrame),
619 Function(Function),
620}
621
622#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Error)]
623pub enum MapPropertyError {
624 #[error("The requested key was not found in the map")]
625 KeyNotFound,
626 #[error("The wrong function was used to retrieve the property")]
627 InvalidType,
628 #[error("The requested index was out of bound")]
629 IndexOutOfBound,
630 #[error("The map has errors. Use [`Map::get_error`] to retrieve the message")]
631 MapError,
632}
633
634pub type AppendMode = ffi::VSMapAppendMode;
635
636#[cfg(test)]
639#[cfg(feature = "link-vs")]
640mod tests {
641 use core::panic;
642
643 use const_str::cstr;
644 use testresult::TestResult;
645
646 use super::*;
647
648 #[test]
649 fn clear() -> TestResult {
650 let mut map = Map::default();
651 let key = crate::key!(c"what");
652 map.set(key, Value::Int(42), AppendMode::Replace)?;
653
654 map.clear();
655 match map.get(key, 0) {
656 Err(MapPropertyError::KeyNotFound) => Ok(()),
657 _ => panic!("Map is not cleared"),
658 }
659 }
660
661 #[test]
662 fn error() -> TestResult {
663 let mut map = Map::default();
664 let key = crate::key!(c"what");
665 map.set(key, Value::Float(42.0), AppendMode::Replace)?;
666
667 map.set_error(cstr!("Yes"));
668 match map.get_error() {
669 Some(msg) => assert_eq!(msg, cstr!("Yes"), "Error message is not match"),
670 None => panic!("Error is not set"),
671 }
672 let res = map.get(key, 0);
673 match res {
674 Err(MapPropertyError::KeyNotFound) => {}
675 _ => panic!("Map is not cleared after setting error"),
676 }
677
678 map.set(key, Value::Float(42.0), AppendMode::Replace)?;
679 let res = map.get(key, 0);
680 match res {
681 Err(MapPropertyError::MapError) => {}
682 _ => panic!(
683 "Map after setting error can only be freed, \
684 cleared, or queried for error"
685 ),
686 }
687
688 Ok(())
689 }
690
691 #[test]
692 fn len() -> TestResult {
693 let mut map = Map::default();
694 let key = crate::key!(c"what");
695
696 map.set(key, Value::Data(&[42, 43, 44, 45]), AppendMode::Replace)?;
697 assert_eq!(1, map.len(), "Number of keys is not correct");
698
699 assert!(!map.is_empty(), "Map is not empty");
700
701 Ok(())
702 }
703
704 #[test]
705 fn key() -> TestResult {
706 let mut map = Map::default();
707 let key = crate::key!(c"what");
708
709 map.set(key, Value::Float(42.0), AppendMode::Append)?;
710
711 assert_eq!(key, map.get_key(0), "Key is not correct");
712
713 match map.num_elements(key) {
714 Some(num) => assert_eq!(1, num),
715 None => panic!("Key `{key}` not found "),
716 }
717
718 map.delete_key(key);
719 assert_eq!(
720 0,
721 map.len(),
722 "Number of keys is not correct after deleting `{key}`"
723 );
724
725 Ok(())
726 }
727
728 #[test]
729 #[allow(clippy::float_cmp)]
730 fn get_set() -> TestResult {
731 let mut map = Map::default();
732 let key = crate::key!(c"what");
733
734 let source = i64::from(i32::MAX) + 1;
735 map.set(key, Value::Int(source), AppendMode::Replace)?;
736 let res = map.get(key, 0)?;
737 match res {
738 Value::Int(val) => assert_eq!(val, source, "Value of `{key}` is not correct"),
739 _ => panic!("Invalid type of `{key}`"),
740 }
741 let res = map.get_int_saturated(key, 0)?;
742 assert_eq!(res, i32::MAX, "Value of `{key}` is not correct");
743 map.set(key, Value::Int(source), AppendMode::Append)?;
744 assert_eq!(&[source, source], map.get_int_array(key)?);
745 map.set_int_array(key, &[1, 2, 3])?;
746 assert_eq!(&[1, 2, 3], map.get_int_array(key)?);
747
748 map.set(key, Value::Float(1e25), AppendMode::Replace)?;
749 let res = map.get(key, 0)?;
750 match res {
751 Value::Float(val) => {
752 assert_eq!(val, 1e25, "Value of `{key}` is not correct");
753 }
754 _ => panic!("Invalid type of `{key}`"),
755 }
756 let res = map.get_float_saturated(key, 0)?;
757 assert_eq!(
758 res, 9_999_999_562_023_526_247_432_192.0,
759 "Value of `{key}` is not correct"
760 );
761 map.set(key, Value::Float(f64::MAX), AppendMode::Append)?;
762 assert_eq!(&[1e25, f64::MAX], map.get_float_array(key)?);
763 map.set_float_array(key, &[1.0, 2.0, 3.0])?;
764 assert_eq!(&[1.0, 2.0, 3.0], map.get_float_array(key)?);
765
766 map.set(key, Value::Data(&[42, 43]), AppendMode::Replace)?;
767 let res = map.get(key, 0)?;
768 match res {
769 Value::Data(val) => {
770 assert_eq!(val, &[42, 43], "Value of `{key}` is not correct");
771 }
772 _ => panic!("Invalid type of `{key}`"),
773 }
774
775 map.set(key, Value::Utf8("good"), AppendMode::Replace)?;
776 let res = map.get(key, 0)?;
777 match res {
778 Value::Utf8(val) => {
779 assert_eq!(val, "good", "Value of `{key}` is not correct");
780 }
781 _ => panic!("Invalid type of `{key}`"),
782 }
783
784 Ok(())
785 }
786}