zenjxl_decoder/api/
signature.rs1use crate::{
7 api::ProcessingResult,
8 error::{Error, Result},
9};
10
11pub(crate) const CODESTREAM_SIGNATURE: [u8; 2] = [0xff, 0x0a];
13pub(crate) const CONTAINER_SIGNATURE: [u8; 12] =
15 [0, 0, 0, 0xc, b'J', b'X', b'L', b' ', 0xd, 0xa, 0x87, 0xa];
16
17#[derive(Debug, PartialEq)]
18pub enum JxlSignatureType {
19 Codestream,
20 Container,
21}
22
23impl JxlSignatureType {
24 pub(crate) fn signature(&self) -> &[u8] {
25 match self {
26 JxlSignatureType::Container => &CONTAINER_SIGNATURE,
27 JxlSignatureType::Codestream => &CODESTREAM_SIGNATURE,
28 }
29 }
30}
31
32pub(crate) fn check_signature_internal(file_prefix: &[u8]) -> Result<Option<JxlSignatureType>> {
33 let prefix_len = file_prefix.len();
34
35 for st in [JxlSignatureType::Codestream, JxlSignatureType::Container] {
36 let len = st.signature().len();
37 let len_to_check = prefix_len.min(len);
39
40 if file_prefix[..len_to_check] == st.signature()[..len_to_check] {
41 return if prefix_len >= len {
43 Ok(Some(st))
44 } else {
45 Err(Error::OutOfBounds(len - prefix_len))
46 };
47 }
48 }
49 Ok(None)
51}
52
53pub fn check_signature(file_prefix: &[u8]) -> ProcessingResult<Option<JxlSignatureType>, ()> {
62 ProcessingResult::new(check_signature_internal(file_prefix)).unwrap()
63}
64
65#[cfg(test)]
66mod tests {
67 use crate::api::{
68 CODESTREAM_SIGNATURE, CONTAINER_SIGNATURE, JxlSignatureType, ProcessingResult,
69 check_signature,
70 };
71
72 macro_rules! signature_test {
73 ($test_name:ident, $bytes:expr, Complete(Some($expected_type:expr))) => {
74 #[test]
75 fn $test_name() {
76 let result = check_signature($bytes);
77 match result {
78 ProcessingResult::Complete { result } => {
79 assert_eq!(result, Some($expected_type));
80 }
81 _ => panic!("Expected Complete(Some(_)), but got {:?}", result),
82 }
83 }
84 };
85 ($test_name:ident, $bytes:expr, Complete(None)) => {
86 #[test]
87 fn $test_name() {
88 let result = check_signature($bytes);
89 match result {
90 ProcessingResult::Complete { result } => {
91 assert_eq!(result, None);
92 }
93 _ => panic!("Expected Complete(None), but got {:?}", result),
94 }
95 }
96 };
97 ($test_name:ident, $bytes:expr, NeedsMoreInput($expected_hint:expr)) => {
98 #[test]
99 fn $test_name() {
100 let result = check_signature($bytes);
101 match result {
102 ProcessingResult::NeedsMoreInput { size_hint, .. } => {
103 assert_eq!(size_hint, $expected_hint);
104 }
105 _ => panic!("Expected NeedsMoreInput, but got {:?}", result),
106 }
107 }
108 };
109 }
110
111 signature_test!(
112 full_container_sig,
113 &CONTAINER_SIGNATURE,
114 Complete(Some(JxlSignatureType::Container))
115 );
116
117 signature_test!(
118 partial_container_sig,
119 &CONTAINER_SIGNATURE[..5],
120 NeedsMoreInput(CONTAINER_SIGNATURE.len() - 5)
121 );
122
123 signature_test!(
124 full_codestream_sig,
125 &CODESTREAM_SIGNATURE,
126 Complete(Some(JxlSignatureType::Codestream))
127 );
128
129 signature_test!(
130 partial_codestream_sig,
131 &CODESTREAM_SIGNATURE[..1],
132 NeedsMoreInput(CODESTREAM_SIGNATURE.len() - 1)
133 );
134
135 signature_test!(
136 empty_prefix,
137 &[],
138 NeedsMoreInput(CODESTREAM_SIGNATURE.len())
139 );
140
141 signature_test!(invalid_sig, &[0x12, 0x34, 0x56, 0x77], Complete(None));
142
143 signature_test!(
144 container_with_extra_data,
145 &{
146 let mut data = CONTAINER_SIGNATURE.to_vec();
147 data.extend_from_slice(&[0x11, 0x22, 0x33]);
148 data
149 },
150 Complete(Some(JxlSignatureType::Container))
151 );
152
153 signature_test!(
154 codestream_with_extra_data,
155 &{
156 let mut data = CODESTREAM_SIGNATURE.to_vec();
157 data.extend_from_slice(&[0x44, 0x55, 0x66]);
158 data
159 },
160 Complete(Some(JxlSignatureType::Codestream))
161 );
162}