1#![allow(non_upper_case_globals)]
83#![allow(non_camel_case_types)]
84#![allow(non_snake_case)]
85#![allow(dead_code)]
87#![allow(clippy::too_many_arguments)]
89
90use std::ffi::{c_char, c_void, CStr};
91
92#[cfg(not(photodna_no_sdk))]
93use std::ffi::CString;
94
95pub const PHOTODNA_HASH_SIZE_EDGE_V2: usize = 0x39c; pub const PHOTODNA_HASH_SIZE_EDGE_V2_BASE64: usize = 0x4d0; pub const PHOTODNA_HASH_SIZE_MAX: usize = 0x4d0; pub const PHOTODNA_LIBRARY_VERSION: &str = "1.05";
110
111#[cfg(all(
114 any(target_os = "windows", target_os = "linux", target_os = "macos"),
115 not(photodna_no_sdk)
116))]
117pub const PHOTODNA_SDK_ROOT: &str = env!("PHOTODNA_SDK_ROOT");
118
119#[cfg(all(
122 any(target_os = "windows", target_os = "linux", target_os = "macos"),
123 not(photodna_no_sdk)
124))]
125pub const PHOTODNA_LIB_DIR: &str = env!("PHOTODNA_LIB_DIR");
126
127pub type ErrorCode = u32;
133
134pub const PhotoDna_ErrorUnknown: i32 = -7000;
136
137pub const PhotoDna_ErrorMemoryAllocationFailed: i32 = -7001;
139
140pub const PhotoDna_ErrorHostMemoryAllocationFailed: i32 = -7001;
142
143pub const PhotoDna_ErrorLibraryFailure: i32 = -7002;
145
146pub const PhotoDna_ErrorMemoryAccess: i32 = -7003;
148
149pub const PhotoDna_ErrorInvalidHash: i32 = -7004;
151
152pub const PhotoDna_ErrorHashFormatInvalidCharacters: i32 = -7005;
154
155pub const PhotoDna_ErrorImageTooSmall: i32 = -7006;
157
158pub const PhotoDna_ErrorNoBorder: i32 = -7007;
160
161pub const PhotoDna_ErrorBadArgument: i32 = -7008;
163
164pub const PhotoDna_ErrorImageIsFlat: i32 = -7009;
166
167pub const PhotoDna_ErrorNoBorderImageTooSmall: i32 = -7010;
169
170pub const PhotoDna_ErrorSourceFormatUnknown: i32 = -7011;
172
173pub const PhotoDna_ErrorInvalidStride: i32 = -7012;
175
176pub const PhotoDna_ErrorInvalidSubImage: i32 = -7013;
178
179pub type HashSize = u32;
185
186pub const PhotoDna_EdgeV2: HashSize = 0x0000039c;
188
189pub const PhotoDna_EdgeV2Base64: HashSize = 0x000004d0;
191
192pub const PhotoDna_MaxSize: HashSize = 0x000004d0;
194
195pub type PhotoDnaOptions = u32;
201
202pub const PhotoDna_OptionNone: PhotoDnaOptions = 0x00000000;
204
205pub const PhotoDna_Default: PhotoDnaOptions = 0x00000000;
207
208pub const PhotoDna_HashFormatMask: PhotoDnaOptions = 0x000000f0;
210
211pub const PhotoDna_HashFormatEdgeV2: PhotoDnaOptions = 0x00000080;
213
214pub const PhotoDna_HashFormatEdgeV2Base64: PhotoDnaOptions = 0x00000090;
216
217pub const PhotoDna_PixelLayoutMask: PhotoDnaOptions = 0x00001f00;
219
220pub const PhotoDna_Rgb: PhotoDnaOptions = 0x00000000;
222
223pub const PhotoDna_Bgr: PhotoDnaOptions = 0x00000000;
225
226pub const PhotoDna_Rgba: PhotoDnaOptions = 0x00000100;
228
229pub const PhotoDna_RgbaPm: PhotoDnaOptions = 0x00000700;
231
232pub const PhotoDna_Bgra: PhotoDnaOptions = 0x00000100;
234
235pub const PhotoDna_Argb: PhotoDnaOptions = 0x00000200;
237
238pub const PhotoDna_Abgr: PhotoDnaOptions = 0x00000200;
240
241pub const PhotoDna_Cmyk: PhotoDnaOptions = 0x00000300;
243
244pub const PhotoDna_Grey8: PhotoDnaOptions = 0x00000400;
246
247pub const PhotoDna_Grey32: PhotoDnaOptions = 0x00000500;
249
250pub const PhotoDna_YCbCr: PhotoDnaOptions = 0x00000600;
252
253pub const PhotoDna_Yuv420p: PhotoDnaOptions = 0x00000800;
256
257pub const PhotoDna_RemoveBorder: PhotoDnaOptions = 0x00200000;
259
260pub const PhotoDna_NoRotateFlip: PhotoDnaOptions = 0x01000000;
262
263pub const PhotoDna_CheckMemory: PhotoDnaOptions = 0x20000000;
266
267pub const PhotoDna_Verbose: PhotoDnaOptions = 0x40000000;
269
270pub const PhotoDna_Test: PhotoDnaOptions = 0x60000000;
272
273pub const PhotoDna_Other: PhotoDnaOptions = 0xffffffff; #[repr(C, packed)]
290#[derive(Copy, Clone)]
291pub struct HashResult {
292 pub result: i32,
294 pub hash_format: i32,
296 pub header_dimensions_image_x: i32,
298 pub header_dimensions_image_y: i32,
300 pub header_dimensions_image_w: i32,
302 pub header_dimensions_image_h: i32,
304 pub hash: [u8; PHOTODNA_HASH_SIZE_MAX],
306 pub reserved0: i32,
308 pub reserved1: i32,
310 pub reserved2: i32,
312 pub reserved3: i32,
314 pub reserved4: i32,
316 pub reserved5: i32,
318}
319
320impl Default for HashResult {
321 fn default() -> Self {
322 Self {
323 result: 0,
324 hash_format: 0,
325 header_dimensions_image_x: 0,
326 header_dimensions_image_y: 0,
327 header_dimensions_image_w: 0,
328 header_dimensions_image_h: 0,
329 hash: [0u8; PHOTODNA_HASH_SIZE_MAX],
330 reserved0: 0,
331 reserved1: 0,
332 reserved2: 0,
333 reserved3: 0,
334 reserved4: 0,
335 reserved5: 0,
336 }
337 }
338}
339
340impl core::fmt::Debug for HashResult {
341 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
342 let result = self.result;
344 let hash_format = self.hash_format;
345 let x = self.header_dimensions_image_x;
346 let y = self.header_dimensions_image_y;
347 let w = self.header_dimensions_image_w;
348 let h = self.header_dimensions_image_h;
349
350 f.debug_struct("HashResult")
351 .field("result", &result)
352 .field("hash_format", &hash_format)
353 .field("x", &x)
354 .field("y", &y)
355 .field("w", &w)
356 .field("h", &h)
357 .field("hash", &"[...]")
358 .finish()
359 }
360}
361
362pub type FnEdgeHashGeneratorInit =
368 unsafe extern "C" fn(library_path: *const c_char, max_threads: i32) -> *mut c_void;
369
370pub type FnEdgeHashGeneratorRelease = unsafe extern "C" fn(library_instance: *mut c_void);
372
373pub type FnGetErrorNumber = unsafe extern "C" fn(library_instance: *mut c_void) -> i32;
375
376pub type FnGetErrorString =
378 unsafe extern "C" fn(library_instance: *mut c_void, error: i32) -> *const c_char;
379
380pub type FnLibraryVersion = unsafe extern "C" fn(library_instance: *mut c_void) -> i32;
382
383pub type FnLibraryVersionMajor = unsafe extern "C" fn(library_instance: *mut c_void) -> i32;
385
386pub type FnLibraryVersionMinor = unsafe extern "C" fn(library_instance: *mut c_void) -> i32;
388
389pub type FnLibraryVersionPatch = unsafe extern "C" fn(library_instance: *mut c_void) -> i32;
391
392pub type FnLibraryVersionText =
394 unsafe extern "C" fn(library_instance: *mut c_void) -> *const c_char;
395
396pub type FnPhotoDnaEdgeHash = unsafe extern "C" fn(
398 library_instance: *mut c_void,
399 image_data: *const u8,
400 hash_value: *mut u8,
401 width: i32,
402 height: i32,
403 stride: i32,
404 options: PhotoDnaOptions,
405) -> i32;
406
407pub type FnPhotoDnaEdgeHashBorder = unsafe extern "C" fn(
409 library_instance: *mut c_void,
410 image_data: *const u8,
411 hash_results: *mut HashResult,
412 max_hash_count: i32,
413 width: i32,
414 height: i32,
415 stride: i32,
416 options: PhotoDnaOptions,
417) -> i32;
418
419pub type FnPhotoDnaEdgeHashBorderSub = unsafe extern "C" fn(
421 library_instance: *mut c_void,
422 image_data: *const u8,
423 hash_results: *mut HashResult,
424 max_hash_count: i32,
425 width: i32,
426 height: i32,
427 stride: i32,
428 x: i32,
429 y: i32,
430 w: i32,
431 h: i32,
432 options: PhotoDnaOptions,
433) -> i32;
434
435pub type FnPhotoDnaEdgeHashSub = unsafe extern "C" fn(
437 library_instance: *mut c_void,
438 image_data: *const u8,
439 hash_value: *mut u8,
440 width: i32,
441 height: i32,
442 stride: i32,
443 x: i32,
444 y: i32,
445 w: i32,
446 h: i32,
447 options: PhotoDnaOptions,
448) -> i32;
449
450#[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))]
455mod native {
456 use super::*;
457
458 pub fn get_library_filename() -> String {
460 #[cfg(target_os = "windows")]
461 {
462 #[cfg(target_arch = "x86_64")]
463 {
464 format!("libEdgeHashGenerator.{}.dll", PHOTODNA_LIBRARY_VERSION)
465 }
466 #[cfg(target_arch = "aarch64")]
467 {
468 format!(
469 "libEdgeHashGenerator-arm64.{}.dll",
470 PHOTODNA_LIBRARY_VERSION
471 )
472 }
473 #[cfg(target_arch = "x86")]
474 {
475 format!("libEdgeHashGenerator-x86.{}.dll", PHOTODNA_LIBRARY_VERSION)
476 }
477 #[cfg(not(any(target_arch = "x86_64", target_arch = "aarch64", target_arch = "x86")))]
478 {
479 format!("libEdgeHashGenerator.{}.dll", PHOTODNA_LIBRARY_VERSION)
480 }
481 }
482 #[cfg(target_os = "linux")]
483 {
484 #[cfg(target_arch = "x86_64")]
485 {
486 format!("libEdgeHashGenerator.so.{}", PHOTODNA_LIBRARY_VERSION)
487 }
488 #[cfg(target_arch = "aarch64")]
489 {
490 format!("libEdgeHashGenerator-arm64.so.{}", PHOTODNA_LIBRARY_VERSION)
491 }
492 #[cfg(target_arch = "x86")]
493 {
494 format!("libEdgeHashGenerator-x86.so.{}", PHOTODNA_LIBRARY_VERSION)
495 }
496 #[cfg(not(any(target_arch = "x86_64", target_arch = "aarch64", target_arch = "x86")))]
497 {
498 format!("libEdgeHashGenerator.so.{}", PHOTODNA_LIBRARY_VERSION)
499 }
500 }
501 #[cfg(target_os = "macos")]
502 {
503 #[cfg(target_arch = "aarch64")]
504 {
505 format!(
506 "libEdgeHashGenerator-arm64-macos.so.{}",
507 PHOTODNA_LIBRARY_VERSION
508 )
509 }
510 #[cfg(not(target_arch = "aarch64"))]
511 {
512 format!("libEdgeHashGenerator-macos.so.{}", PHOTODNA_LIBRARY_VERSION)
513 }
514 }
515 }
516}
517
518#[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))]
519pub use native::*;
520
521#[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))]
539pub struct EdgeHashGenerator {
540 _library: libloading::Library,
542 library_instance: *mut c_void,
544 fn_release: libloading::Symbol<'static, FnEdgeHashGeneratorRelease>,
546 fn_get_error_number: libloading::Symbol<'static, FnGetErrorNumber>,
548 fn_get_error_string: libloading::Symbol<'static, FnGetErrorString>,
550 fn_library_version: libloading::Symbol<'static, FnLibraryVersion>,
552 fn_library_version_major: libloading::Symbol<'static, FnLibraryVersionMajor>,
554 fn_library_version_minor: libloading::Symbol<'static, FnLibraryVersionMinor>,
556 fn_library_version_patch: libloading::Symbol<'static, FnLibraryVersionPatch>,
558 fn_library_version_text: libloading::Symbol<'static, FnLibraryVersionText>,
560 fn_photo_dna_edge_hash: libloading::Symbol<'static, FnPhotoDnaEdgeHash>,
562 fn_photo_dna_edge_hash_border: libloading::Symbol<'static, FnPhotoDnaEdgeHashBorder>,
564 fn_photo_dna_edge_hash_border_sub: libloading::Symbol<'static, FnPhotoDnaEdgeHashBorderSub>,
566 fn_photo_dna_edge_hash_sub: libloading::Symbol<'static, FnPhotoDnaEdgeHashSub>,
568}
569
570#[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))]
571impl EdgeHashGenerator {
572 pub fn new(library_dir: Option<&str>, max_threads: i32) -> Result<Self, String> {
594 #[cfg(photodna_no_sdk)]
595 {
596 let _ = (library_dir, max_threads); Err(
598 "PhotoDNA SDK not available: PHOTODNA_SDK_ROOT was not set at build time. \
599 Please rebuild with PHOTODNA_SDK_ROOT environment variable set to the SDK directory."
600 .to_string(),
601 )
602 }
603
604 #[cfg(not(photodna_no_sdk))]
605 {
606 let lib_dir = library_dir.unwrap_or(PHOTODNA_LIB_DIR);
607 let lib_filename = get_library_filename();
608 let lib_path = format!("{}/{}", lib_dir, lib_filename);
609
610 unsafe {
611 let library = libloading::Library::new(&lib_path)
613 .map_err(|e| format!("Failed to load library '{}': {}", lib_path, e))?;
614
615 let fn_init: libloading::Symbol<FnEdgeHashGeneratorInit> = library
617 .get(b"EdgeHashGeneratorInit\0")
618 .map_err(|e| format!("Failed to find symbol 'EdgeHashGeneratorInit': {}", e))?;
619 let fn_release: libloading::Symbol<FnEdgeHashGeneratorRelease> = library
620 .get(b"EdgeHashGeneratorRelease\0")
621 .map_err(|e| {
622 format!("Failed to find symbol 'EdgeHashGeneratorRelease': {}", e)
623 })?;
624 let fn_get_error_number: libloading::Symbol<FnGetErrorNumber> = library
625 .get(b"GetErrorNumber\0")
626 .map_err(|e| format!("Failed to find symbol 'GetErrorNumber': {}", e))?;
627 let fn_get_error_string: libloading::Symbol<FnGetErrorString> = library
628 .get(b"GetErrorString\0")
629 .map_err(|e| format!("Failed to find symbol 'GetErrorString': {}", e))?;
630 let fn_library_version: libloading::Symbol<FnLibraryVersion> = library
631 .get(b"LibraryVersion\0")
632 .map_err(|e| format!("Failed to find symbol 'LibraryVersion': {}", e))?;
633 let fn_library_version_major: libloading::Symbol<FnLibraryVersionMajor> = library
634 .get(b"LibraryVersionMajor\0")
635 .map_err(|e| format!("Failed to find symbol 'LibraryVersionMajor': {}", e))?;
636 let fn_library_version_minor: libloading::Symbol<FnLibraryVersionMinor> = library
637 .get(b"LibraryVersionMinor\0")
638 .map_err(|e| format!("Failed to find symbol 'LibraryVersionMinor': {}", e))?;
639 let fn_library_version_patch: libloading::Symbol<FnLibraryVersionPatch> = library
640 .get(b"LibraryVersionPatch\0")
641 .map_err(|e| format!("Failed to find symbol 'LibraryVersionPatch': {}", e))?;
642 let fn_library_version_text: libloading::Symbol<FnLibraryVersionText> = library
643 .get(b"LibraryVersionText\0")
644 .map_err(|e| format!("Failed to find symbol 'LibraryVersionText': {}", e))?;
645 let fn_photo_dna_edge_hash: libloading::Symbol<FnPhotoDnaEdgeHash> = library
646 .get(b"PhotoDnaEdgeHash\0")
647 .map_err(|e| format!("Failed to find symbol 'PhotoDnaEdgeHash': {}", e))?;
648 let fn_photo_dna_edge_hash_border: libloading::Symbol<FnPhotoDnaEdgeHashBorder> =
649 library.get(b"PhotoDnaEdgeHashBorder\0").map_err(|e| {
650 format!("Failed to find symbol 'PhotoDnaEdgeHashBorder': {}", e)
651 })?;
652 let fn_photo_dna_edge_hash_border_sub: libloading::Symbol<
653 FnPhotoDnaEdgeHashBorderSub,
654 > = library
655 .get(b"PhotoDnaEdgeHashBorderSub\0")
656 .map_err(|e| {
657 format!("Failed to find symbol 'PhotoDnaEdgeHashBorderSub': {}", e)
658 })?;
659 let fn_photo_dna_edge_hash_sub: libloading::Symbol<FnPhotoDnaEdgeHashSub> =
660 library.get(b"PhotoDnaEdgeHashSub\0").map_err(|e| {
661 format!("Failed to find symbol 'PhotoDnaEdgeHashSub': {}", e)
662 })?;
663
664 let c_lib_dir = CString::new(lib_dir).map_err(|e| e.to_string())?;
666 let library_instance = fn_init(c_lib_dir.as_ptr(), max_threads);
667
668 if library_instance.is_null() {
669 return Err("Failed to initialize PhotoDNA library".to_string());
670 }
671
672 #[allow(clippy::missing_transmute_annotations)]
675 let fn_release = std::mem::transmute(fn_release);
676 #[allow(clippy::missing_transmute_annotations)]
677 let fn_get_error_number = std::mem::transmute(fn_get_error_number);
678 #[allow(clippy::missing_transmute_annotations)]
679 let fn_get_error_string = std::mem::transmute(fn_get_error_string);
680 #[allow(clippy::missing_transmute_annotations)]
681 let fn_library_version = std::mem::transmute(fn_library_version);
682 #[allow(clippy::missing_transmute_annotations)]
683 let fn_library_version_major = std::mem::transmute(fn_library_version_major);
684 #[allow(clippy::missing_transmute_annotations)]
685 let fn_library_version_minor = std::mem::transmute(fn_library_version_minor);
686 #[allow(clippy::missing_transmute_annotations)]
687 let fn_library_version_patch = std::mem::transmute(fn_library_version_patch);
688 #[allow(clippy::missing_transmute_annotations)]
689 let fn_library_version_text = std::mem::transmute(fn_library_version_text);
690 #[allow(clippy::missing_transmute_annotations)]
691 let fn_photo_dna_edge_hash = std::mem::transmute(fn_photo_dna_edge_hash);
692 #[allow(clippy::missing_transmute_annotations)]
693 let fn_photo_dna_edge_hash_border =
694 std::mem::transmute(fn_photo_dna_edge_hash_border);
695 #[allow(clippy::missing_transmute_annotations)]
696 let fn_photo_dna_edge_hash_border_sub =
697 std::mem::transmute(fn_photo_dna_edge_hash_border_sub);
698 #[allow(clippy::missing_transmute_annotations)]
699 let fn_photo_dna_edge_hash_sub = std::mem::transmute(fn_photo_dna_edge_hash_sub);
700
701 Ok(Self {
702 _library: library,
703 library_instance,
704 fn_release,
705 fn_get_error_number,
706 fn_get_error_string,
707 fn_library_version,
708 fn_library_version_major,
709 fn_library_version_minor,
710 fn_library_version_patch,
711 fn_library_version_text,
712 fn_photo_dna_edge_hash,
713 fn_photo_dna_edge_hash_border,
714 fn_photo_dna_edge_hash_border_sub,
715 fn_photo_dna_edge_hash_sub,
716 })
717 }
718 }
719 }
720
721 pub fn raw_instance(&self) -> *mut c_void {
727 self.library_instance
728 }
729
730 pub fn get_error_number(&self) -> i32 {
732 unsafe { (self.fn_get_error_number)(self.library_instance) }
733 }
734
735 pub fn get_error_string(&self, error: i32) -> Option<&str> {
739 unsafe {
740 let ptr = (self.fn_get_error_string)(self.library_instance, error);
741 if ptr.is_null() {
742 None
743 } else {
744 CStr::from_ptr(ptr).to_str().ok()
745 }
746 }
747 }
748
749 pub fn library_version(&self) -> i32 {
753 unsafe { (self.fn_library_version)(self.library_instance) }
754 }
755
756 pub fn library_version_major(&self) -> i32 {
758 unsafe { (self.fn_library_version_major)(self.library_instance) }
759 }
760
761 pub fn library_version_minor(&self) -> i32 {
763 unsafe { (self.fn_library_version_minor)(self.library_instance) }
764 }
765
766 pub fn library_version_patch(&self) -> i32 {
768 unsafe { (self.fn_library_version_patch)(self.library_instance) }
769 }
770
771 pub fn library_version_text(&self) -> Option<&str> {
773 unsafe {
774 let ptr = (self.fn_library_version_text)(self.library_instance);
775 if ptr.is_null() {
776 None
777 } else {
778 CStr::from_ptr(ptr).to_str().ok()
779 }
780 }
781 }
782
783 pub unsafe fn photo_dna_edge_hash(
804 &self,
805 image_data: *const u8,
806 hash_value: *mut u8,
807 width: i32,
808 height: i32,
809 stride: i32,
810 options: PhotoDnaOptions,
811 ) -> i32 {
812 (self.fn_photo_dna_edge_hash)(
813 self.library_instance,
814 image_data,
815 hash_value,
816 width,
817 height,
818 stride,
819 options,
820 )
821 }
822
823 pub unsafe fn photo_dna_edge_hash_border(
847 &self,
848 image_data: *const u8,
849 hash_results: *mut HashResult,
850 max_hash_count: i32,
851 width: i32,
852 height: i32,
853 stride: i32,
854 options: PhotoDnaOptions,
855 ) -> i32 {
856 (self.fn_photo_dna_edge_hash_border)(
857 self.library_instance,
858 image_data,
859 hash_results,
860 max_hash_count,
861 width,
862 height,
863 stride,
864 options,
865 )
866 }
867
868 pub unsafe fn photo_dna_edge_hash_border_sub(
875 &self,
876 image_data: *const u8,
877 hash_results: *mut HashResult,
878 max_hash_count: i32,
879 width: i32,
880 height: i32,
881 stride: i32,
882 x: i32,
883 y: i32,
884 w: i32,
885 h: i32,
886 options: PhotoDnaOptions,
887 ) -> i32 {
888 (self.fn_photo_dna_edge_hash_border_sub)(
889 self.library_instance,
890 image_data,
891 hash_results,
892 max_hash_count,
893 width,
894 height,
895 stride,
896 x,
897 y,
898 w,
899 h,
900 options,
901 )
902 }
903
904 pub unsafe fn photo_dna_edge_hash_sub(
911 &self,
912 image_data: *const u8,
913 hash_value: *mut u8,
914 width: i32,
915 height: i32,
916 stride: i32,
917 x: i32,
918 y: i32,
919 w: i32,
920 h: i32,
921 options: PhotoDnaOptions,
922 ) -> i32 {
923 (self.fn_photo_dna_edge_hash_sub)(
924 self.library_instance,
925 image_data,
926 hash_value,
927 width,
928 height,
929 stride,
930 x,
931 y,
932 w,
933 h,
934 options,
935 )
936 }
937}
938
939#[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))]
940impl Drop for EdgeHashGenerator {
941 fn drop(&mut self) {
942 unsafe {
943 (self.fn_release)(self.library_instance);
945 }
947 }
948}
949
950#[cfg(any(
968 all(
969 feature = "wasm",
970 not(any(target_os = "windows", target_os = "linux", target_os = "macos"))
971 ),
972 target_os = "openbsd",
973 target_os = "freebsd",
974 target_os = "netbsd",
975 target_os = "dragonfly",
976))]
977pub mod wasm {
978 pub const PHOTODNA_WASM_BYTES: &[u8] = include_bytes!(env!("PHOTODNA_WASM_PATH"));
998
999 pub const PHOTODNA_WASM_SIZE: usize = PHOTODNA_WASM_BYTES.len();
1001}
1002
1003#[cfg(any(
1004 all(
1005 feature = "wasm",
1006 not(any(target_os = "windows", target_os = "linux", target_os = "macos"))
1007 ),
1008 target_os = "openbsd",
1009 target_os = "freebsd",
1010 target_os = "netbsd",
1011 target_os = "dragonfly",
1012))]
1013pub use wasm::*;
1014
1015pub const fn error_code_description(code: i32) -> &'static str {
1023 match code {
1024 0 => "Success",
1025 PhotoDna_ErrorUnknown => "An undetermined error occurred",
1026 PhotoDna_ErrorMemoryAllocationFailed => "Failed to allocate memory",
1027 PhotoDna_ErrorLibraryFailure => "General failure within the library",
1028 PhotoDna_ErrorMemoryAccess => "System memory exception occurred",
1029 PhotoDna_ErrorInvalidHash => "Hash does not conform to PhotoDNA specifications",
1030 PhotoDna_ErrorHashFormatInvalidCharacters => "Invalid character in Base64 or Hex hash",
1031 PhotoDna_ErrorImageTooSmall => "Image dimension is less than 50 pixels",
1032 PhotoDna_ErrorNoBorder => "No border was detected for the image",
1033 PhotoDna_ErrorBadArgument => "An invalid argument was passed to the function",
1034 PhotoDna_ErrorImageIsFlat => "Image has few or no gradients",
1035 PhotoDna_ErrorNoBorderImageTooSmall => "No border; image too small after border removal",
1036 PhotoDna_ErrorSourceFormatUnknown => "Not a known source image format",
1037 PhotoDna_ErrorInvalidStride => "Stride should be 0 or >= width in bytes",
1038 PhotoDna_ErrorInvalidSubImage => "Sub region is not within image boundaries",
1039 _ => "Unknown error code",
1040 }
1041}
1042
1043pub const fn hash_size_for_options(options: PhotoDnaOptions) -> usize {
1053 let format = options & PhotoDna_HashFormatMask;
1054 if format == PhotoDna_HashFormatEdgeV2Base64 {
1055 PHOTODNA_HASH_SIZE_EDGE_V2_BASE64
1056 } else {
1057 PHOTODNA_HASH_SIZE_EDGE_V2
1058 }
1059}
1060
1061#[cfg(test)]
1062mod tests {
1063 use super::*;
1064
1065 #[test]
1066 fn test_hash_result_size() {
1067 assert_eq!(
1069 core::mem::size_of::<HashResult>(),
1070 4 + 4 + 4 + 4 + 4 + 4 + PHOTODNA_HASH_SIZE_MAX + 4 * 6
1071 );
1072 }
1073
1074 #[test]
1075 fn test_error_code_descriptions() {
1076 assert_eq!(error_code_description(0), "Success");
1077 assert_eq!(
1078 error_code_description(PhotoDna_ErrorImageTooSmall),
1079 "Image dimension is less than 50 pixels"
1080 );
1081 }
1082
1083 #[test]
1084 fn test_hash_size_for_options() {
1085 assert_eq!(
1086 hash_size_for_options(PhotoDna_Default),
1087 PHOTODNA_HASH_SIZE_EDGE_V2
1088 );
1089 assert_eq!(
1090 hash_size_for_options(PhotoDna_HashFormatEdgeV2Base64),
1091 PHOTODNA_HASH_SIZE_EDGE_V2_BASE64
1092 );
1093 }
1094
1095 #[test]
1096 fn test_constants() {
1097 assert_eq!(PHOTODNA_HASH_SIZE_EDGE_V2, 924);
1098 assert_eq!(PHOTODNA_HASH_SIZE_EDGE_V2_BASE64, 1232);
1099 assert_eq!(PhotoDna_EdgeV2 as usize, PHOTODNA_HASH_SIZE_EDGE_V2);
1100 }
1101
1102 #[test]
1103 #[cfg(all(
1104 any(target_os = "windows", target_os = "linux", target_os = "macos"),
1105 not(photodna_no_sdk)
1106 ))]
1107 fn test_sdk_paths() {
1108 assert!(!PHOTODNA_SDK_ROOT.is_empty());
1110 assert!(!PHOTODNA_LIB_DIR.is_empty());
1111 }
1112}