1#![allow(
2 non_camel_case_types,
3 non_snake_case,
4 dead_code,
5 unused_variables,
6 unused_assignments,
7 clippy::missing_safety_doc,
8 unreachable_code,
9 clippy::too_many_arguments,
10 clippy::needless_range_loop,
11 clippy::manual_range_contains
12)]
13#![deny(deprecated)]
14
15pub mod c_types;
16pub mod helpers;
17
18pub mod aliases;
19pub mod buffers;
20pub mod checksum;
21pub mod edithdu;
22pub mod fitsio;
23pub mod fitsio2;
24pub mod scalnull;
25
26pub mod cfileio;
30pub mod drvrfile;
31pub mod drvrgsiftp;
32pub mod drvrmem;
33pub mod drvrnet;
34
35#[cfg(all(feature = "shared_mem", not(target_os = "windows")))]
36pub mod drvrsmem;
37
38pub mod editcol;
39pub mod eval_defs;
40pub mod eval_f;
41pub mod eval_l;
42pub mod eval_tab;
43pub mod eval_y;
44pub mod fits_hcompress;
45pub mod fits_hdecompress;
46pub mod fitscore;
47pub mod getcol;
48pub mod getcolb;
49pub mod getcold;
50pub mod getcole;
51pub mod getcoli;
52pub mod getcolj;
53pub mod getcolk;
54pub mod getcoll;
55pub mod getcols;
56pub mod getcolsb;
57pub mod getcolui;
58pub mod getcoluj;
59pub mod getcoluk;
60pub mod getkey;
61pub mod group;
62pub mod grparser;
63pub mod histo;
64pub mod imcompress;
65pub mod iraffits;
66pub mod modkey;
67pub mod putcol;
68pub mod putcolb;
69pub mod putcold;
70pub mod putcole;
71pub mod putcoli;
72pub mod putcolj;
73pub mod putcolk;
74pub mod putcoll;
75pub mod putcols;
76pub mod putcolsb;
77pub mod putcolu;
78pub mod putcolui;
79pub mod putcoluj;
80pub mod putcoluk;
81pub mod putkey;
82pub mod quantize;
83pub mod region;
84pub mod relibc;
85pub mod simplerng;
86pub mod swapproc;
87pub mod wcssub;
88pub mod wcsutil;
89pub mod wrappers;
90pub mod zcompress;
91pub mod zuncompress;
92
93use std::{
94 ffi::{CStr, CString},
95 marker::PhantomData,
96 str::FromStr,
97 sync::{Mutex, MutexGuard},
98};
99
100use bytemuck::cast_slice;
101use fitsio::{
102 FLEN_VALUE, LONGLONG, TBIT, TBYTE, TCOMPLEX, TDBLCOMPLEX, TDOUBLE, TFLOAT, TINT, TLOGICAL,
103 TLONG, TLONGLONG, TSBYTE, TSHORT, TSTRING, TUINT, TULONG, TULONGLONG, TUSHORT, ULONGLONG,
104};
105
106use crate::c_types::*;
107
108pub(crate) static MUTEX_LOCK: Mutex<bool> = Mutex::new(false);
109
110pub(crate) fn FFLOCK<'a>() -> MutexGuard<'a, bool> {
111 MUTEX_LOCK.lock().unwrap()
112}
113
114pub(crate) fn FFUNLOCK(p: MutexGuard<'_, bool>) {
115 drop(p);
116}
117
118pub trait ToRaw {
119 fn as_raw_mut(&mut self) -> *mut Self;
120}
121
122pub trait AsMutPtr<T> {
123 fn as_mut_ptr(&self) -> *mut T;
124}
125
126impl<T> AsMutPtr<T> for Option<&mut [T]> {
127 fn as_mut_ptr(&self) -> *mut T {
128 match self {
129 Some(v) => v.as_ptr() as *mut T, None => std::ptr::null_mut(),
131 }
132 }
133}
134
135#[inline(always)]
136pub fn bb(n: u8) -> c_char {
137 n as c_char
138}
139
140#[macro_export]
141macro_rules! int_snprintf {
142 ($dst:expr, $len:expr, $($arg:tt)*) => {
143 {
144 let s = format!($($arg)*);
145 let s_bytes = s.as_bytes();
146 let mut s_len = s_bytes.len();
147
148 s_len = cmp::min($len-1, s_len);
149
150 let w = cast_slice_mut::<c_char, u8>(&mut $dst[..s_len]);
151 w.copy_from_slice(&s_bytes[..s_len]);
152 $dst[s_len] = 0; s_len as isize
155 }
156 };
157}
158
159#[macro_export]
160macro_rules! slice_to_str {
161 ($e:expr) => {
162 CStr::from_bytes_until_nul(cast_slice($e))
163 .unwrap()
164 .to_str()
165 .unwrap()
166 };
167}
168
169#[macro_export]
170macro_rules! cs {
171 ($e: expr) => {
172 cast_slice::<u8, c_char>($e.to_bytes_with_nul())
173 };
174}
175
176#[macro_export]
177macro_rules! nullable_slice_cstr {
178 ($e: ident) => {
179 let $e: Option<&[c_char]> = match $e.is_null() {
180 true => None,
181 false => Some(cast_slice(CStr::from_ptr($e).to_bytes_with_nul())),
182 };
183 };
184}
185
186#[macro_export]
187macro_rules! nullable_slice_cstr_mut {
188 ($e: ident) => {
189 let mut $e: Option<&mut [c_char]> = match $e.is_null() {
190 true => None,
191 false => {
192 let _c = CStr::from_ptr($e).to_bytes_with_nul();
193 let _l = _c.len();
194
195 Some(slice::from_raw_parts_mut($e, _l))
196 }
197 };
198 };
199}
200
201#[macro_export]
202macro_rules! raw_to_slice {
203 ($e: ident) => {
204 let $e: &[c_char] = cast_slice(CStr::from_ptr($e).to_bytes_with_nul());
205 };
206}
207
208pub(crate) struct TKeywords<'a> {
209 tfields: c_int, ttype: *const *const c_char, tform: *const *const c_char, tunit: *const *const c_char, marker: PhantomData<&'a ()>,
214}
215
216impl<'a> TKeywords<'a> {
217 pub fn new(
218 tfields: c_int,
219 ttype: *const *const c_char,
220 tform: *const *const c_char,
221 tunit: *const *const c_char,
222 ) -> Self {
223 TKeywords {
224 tfields,
225 ttype,
226 tform,
227 tunit,
228 marker: PhantomData,
229 }
230 }
231
232 pub unsafe fn tkeywords_to_vecs(
233 &'a self,
234 ) -> (
235 Vec<Option<&'a [c_char]>>,
236 Vec<&'a [c_char]>,
237 Option<Vec<Option<&'a [c_char]>>>,
238 ) {
239 unsafe {
240 let ttype = core::slice::from_raw_parts(self.ttype, self.tfields as usize);
242 let mut v_ttype = Vec::new();
243
244 for item in ttype {
245 let ttype_item = if item.is_null() {
246 None
247 } else {
248 Some(cast_slice(CStr::from_ptr(*item).to_bytes_with_nul()))
249 };
250 v_ttype.push(ttype_item);
251 }
252
253 let tform = core::slice::from_raw_parts(self.tform, self.tfields as usize);
255 let mut v_tform = Vec::new();
256
257 for item in tform {
258 let tform_item = cast_slice(CStr::from_ptr(*item).to_bytes_with_nul());
259 v_tform.push(tform_item);
260 }
261
262 let mut v_tunit = Vec::new();
264 let out_tunit = if self.tunit.is_null() {
265 None
266 } else {
267 let tunit = core::slice::from_raw_parts(self.tunit, self.tfields as usize);
268
269 for item in tunit {
270 let tunit_item = if item.is_null() {
271 None
272 } else {
273 Some(cast_slice(CStr::from_ptr(*item).to_bytes_with_nul()))
274 };
275 v_tunit.push(tunit_item);
276 }
277 Some(v_tunit)
278 };
279
280 (v_ttype, v_tform, out_tunit)
281 }
282 }
283}
284
285pub(crate) fn calculate_subsection_length(blc: &[c_long], trc: &[c_long], inc: &[c_long]) -> usize {
286 assert!(blc.len() == trc.len() && blc.len() == inc.len());
287
288 let len = blc.len();
289 let mut acc: usize = 1;
290 for ii in 0..len {
291 if blc[ii] < trc[ii] {
292 acc *= ((trc[ii] - blc[ii]) / inc[ii] + 1) as usize; } else {
294 acc *= ((blc[ii] - trc[ii]) / inc[ii] + 1) as usize;
295 }
296 }
297 acc
298}
299
300pub(crate) fn calculate_subsection_length_unit(blc: &[c_long], trc: &[c_long]) -> usize {
301 assert!(blc.len() == trc.len());
302
303 let len = blc.len();
304 let mut acc: usize = 1;
305 for ii in 0..len {
306 if blc[ii] < trc[ii] {
307 acc *= ((trc[ii] - blc[ii]) + 1) as usize; } else {
309 acc *= ((blc[ii] - trc[ii]) + 1) as usize;
310 }
311 }
312 acc
313}
314
315pub(crate) fn vecs_to_slices<T>(vecs: &[Vec<T>]) -> Vec<&[T]> {
316 vecs.iter().map(Vec::as_slice).collect()
317}
318
319pub(crate) fn vecs_to_slices_mut<T>(vecs: &mut [Vec<T>]) -> Vec<&mut [T]> {
320 vecs.iter_mut().map(Vec::as_mut_slice).collect()
321}
322
323#[derive(Debug, PartialEq, Clone, Copy)]
324pub enum NullCheckType {
325 None = 0, SetPixel = 1, SetNullArray = 2, }
329
330#[derive(Debug, PartialEq, Clone)]
331pub enum NullValue {
332 Float(f32),
333 Double(f64),
334 Long(c_long),
335 ULong(c_ulong),
336 LONGLONG(LONGLONG),
337 ULONGLONG(ULONGLONG),
338 Int(c_int),
339 UInt(c_uint),
340 Short(c_short),
341 UShort(c_ushort),
342 Byte(i8),
343 UByte(c_uchar),
344 Logical(c_char),
345 String(CString),
346}
347
348impl NullValue {
349 pub fn get_value_as_f64(&self) -> f64 {
350 match self {
351 NullValue::Float(v) => *v as f64,
352 NullValue::Double(v) => *v,
353 NullValue::Long(v) => *v as f64,
354 NullValue::ULong(v) => *v as f64,
355 NullValue::LONGLONG(v) => *v as f64,
356 NullValue::ULONGLONG(v) => *v as f64,
357 NullValue::Int(v) => *v as f64,
358 NullValue::UInt(v) => *v as f64,
359 NullValue::Short(v) => *v as f64,
360 NullValue::UShort(v) => *v as f64,
361 NullValue::Byte(v) => *v as f64,
362 NullValue::UByte(v) => *v as f64,
363 NullValue::Logical(v) => *v as f64,
364 _ => 0.0,
365 }
366 }
367
368 pub fn from_raw_ptr(datatype: c_int, value: *const c_void) -> Option<Self> {
369 if value.is_null() {
370 return None;
371 }
372
373 match datatype {
374 TFLOAT => Some(NullValue::Float(unsafe { *(value as *const f32) })),
375 TDOUBLE => Some(NullValue::Double(unsafe { *(value as *const f64) })),
376 TLONG => Some(NullValue::Long(unsafe { *(value as *const c_long) })),
377 TULONG => Some(NullValue::ULong(unsafe { *(value as *const c_ulong) })),
378 TLONGLONG => Some(NullValue::LONGLONG(unsafe { *(value as *const LONGLONG) })),
379 TULONGLONG => Some(NullValue::ULONGLONG(unsafe {
380 *(value as *const ULONGLONG)
381 })),
382 TINT => Some(NullValue::Int(unsafe { *(value as *const c_int) })),
383 TUINT => Some(NullValue::UInt(unsafe { *(value as *const c_uint) })),
384 TSHORT => Some(NullValue::Short(unsafe { *(value as *const c_short) })),
385 TUSHORT => Some(NullValue::UShort(unsafe { *(value as *const c_ushort) })),
386 TBYTE => Some(NullValue::UByte(unsafe { *(value as *const c_uchar) })),
387 TSBYTE => Some(NullValue::Byte(unsafe { *(value as *const i8) })),
388 TLOGICAL => Some(NullValue::Logical(unsafe { *(value as *const c_char) })),
389 TSTRING => {
390 let cstr = unsafe { CStr::from_ptr(value as *const c_char) };
391 Some(NullValue::String(cstr.to_owned()))
392 }
393 _ => None, }
395 }
396}
397
398#[derive(Debug, PartialEq)]
399pub enum KeywordDatatype<'a> {
400 TBYTE(&'a c_uchar),
401 TSBYTE(&'a c_char),
402 TSHORT(&'a c_short),
403 TUSHORT(&'a c_ushort),
404 TINT(&'a c_int),
405 TUINT(&'a c_uint),
406 TLONG(&'a c_long),
407 TULONG(&'a c_ulong),
408 TFLOAT(&'a f32),
409 TDOUBLE(&'a f64),
410 TSTRING(&'a [c_char]),
411 TLOGICAL(&'a c_int),
412 TCOMPLEX(&'a [f32; 2]),
413 TDBLCOMPLEX(&'a [f64; 2]),
414 TULONGLONG(&'a ULONGLONG),
415 TLONGLONG(&'a LONGLONG),
416 INVALID(c_int),
417}
418
419impl KeywordDatatype<'_> {
420 pub fn from_datatype(datatype: c_int, value: *const c_void) -> Self {
421 match datatype {
422 TBYTE => KeywordDatatype::TBYTE(unsafe { &*(value as *const c_uchar) }),
423 TSBYTE => KeywordDatatype::TSBYTE(unsafe { &*(value as *const c_char) }),
424 TSHORT => KeywordDatatype::TSHORT(unsafe { &*(value as *const c_short) }),
425 TUSHORT => KeywordDatatype::TUSHORT(unsafe { &*(value as *const c_ushort) }),
426 TINT => KeywordDatatype::TINT(unsafe { &*(value as *const c_int) }),
427 TUINT => KeywordDatatype::TUINT(unsafe { &*(value as *const c_uint) }),
428 TLONG => KeywordDatatype::TLONG(unsafe { &*(value as *const c_long) }),
429 TULONG => KeywordDatatype::TULONG(unsafe { &*(value as *const c_ulong) }),
430 TFLOAT => KeywordDatatype::TFLOAT(unsafe { &*(value as *const f32) }),
431 TDOUBLE => KeywordDatatype::TDOUBLE(unsafe { &*(value as *const f64) }),
432 TSTRING => KeywordDatatype::TSTRING(unsafe {
433 cast_slice(CStr::from_ptr(value as *const c_char).to_bytes_with_nul())
434 }),
435 TLOGICAL => KeywordDatatype::TLOGICAL(unsafe { &*(value as *const c_int) }),
436 TCOMPLEX => KeywordDatatype::TCOMPLEX(unsafe { &*(value as *const [f32; 2]) }),
437 TDBLCOMPLEX => KeywordDatatype::TDBLCOMPLEX(unsafe { &*(value as *const [f64; 2]) }),
438 TULONGLONG => KeywordDatatype::TULONGLONG(unsafe { &*(value as *const ULONGLONG) }),
439 TLONGLONG => KeywordDatatype::TLONGLONG(unsafe { &*(value as *const LONGLONG) }),
440 _ => KeywordDatatype::INVALID(datatype),
441 }
442 }
443
444 pub fn to_datatype_code(&self) -> c_int {
445 match self {
446 KeywordDatatype::TBYTE(_) => TBYTE,
447 KeywordDatatype::TSBYTE(_) => TSBYTE,
448 KeywordDatatype::TSHORT(_) => TSHORT,
449 KeywordDatatype::TUSHORT(_) => TUSHORT,
450 KeywordDatatype::TINT(_) => TINT,
451 KeywordDatatype::TUINT(_) => TUINT,
452 KeywordDatatype::TLONG(_) => TLONG,
453 KeywordDatatype::TULONG(_) => TULONG,
454 KeywordDatatype::TFLOAT(_) => TFLOAT,
455 KeywordDatatype::TDOUBLE(_) => TDOUBLE,
456 KeywordDatatype::TSTRING(_) => TSTRING,
457 KeywordDatatype::TLOGICAL(_) => TLOGICAL,
458 KeywordDatatype::TCOMPLEX(_) => TCOMPLEX,
459 KeywordDatatype::TDBLCOMPLEX(_) => TDBLCOMPLEX,
460 KeywordDatatype::TULONGLONG(_) => TULONGLONG,
461 KeywordDatatype::TLONGLONG(_) => TLONGLONG,
462 KeywordDatatype::INVALID(x) => *x,
463 }
464 }
465}
466
467#[derive(Debug, PartialEq)]
468pub enum KeywordDatatypeMut<'a> {
469 TBYTE(&'a mut c_uchar),
470 TSBYTE(&'a mut c_char),
471 TSHORT(&'a mut c_short),
472 TUSHORT(&'a mut c_ushort),
473 TINT(&'a mut c_int),
474 TUINT(&'a mut c_uint),
475 TLONG(&'a mut c_long),
476 TULONG(&'a mut c_ulong),
477 TFLOAT(&'a mut f32),
478 TDOUBLE(&'a mut f64),
479 TSTRING(&'a mut [c_char; FLEN_VALUE]),
480 TLOGICAL(&'a mut c_int),
481 TCOMPLEX(&'a mut [f32; 2]),
482 TDBLCOMPLEX(&'a mut [f64; 2]),
483 TULONGLONG(&'a mut ULONGLONG),
484 TLONGLONG(&'a mut LONGLONG),
485 INVALID(c_int),
486}
487
488impl KeywordDatatypeMut<'_> {
489 pub fn from_datatype(datatype: c_int, value: *mut c_void) -> Self {
490 match datatype {
491 TBYTE => KeywordDatatypeMut::TBYTE(unsafe { &mut *(value as *mut c_uchar) }),
492 TSBYTE => KeywordDatatypeMut::TSBYTE(unsafe { &mut *(value as *mut c_char) }),
493 TSHORT => KeywordDatatypeMut::TSHORT(unsafe { &mut *(value as *mut c_short) }),
494 TUSHORT => KeywordDatatypeMut::TUSHORT(unsafe { &mut *(value as *mut c_ushort) }),
495 TINT => KeywordDatatypeMut::TINT(unsafe { &mut *(value as *mut c_int) }),
496 TUINT => KeywordDatatypeMut::TUINT(unsafe { &mut *(value as *mut c_uint) }),
497 TLONG => KeywordDatatypeMut::TLONG(unsafe { &mut *(value as *mut c_long) }),
498 TULONG => KeywordDatatypeMut::TULONG(unsafe { &mut *(value as *mut c_ulong) }),
499 TFLOAT => KeywordDatatypeMut::TFLOAT(unsafe { &mut *(value as *mut f32) }),
500 TDOUBLE => KeywordDatatypeMut::TDOUBLE(unsafe { &mut *(value as *mut f64) }),
501 TSTRING => {
502 KeywordDatatypeMut::TSTRING(unsafe { &mut *(value as *mut [c_char; FLEN_VALUE]) })
503 }
504 TLOGICAL => KeywordDatatypeMut::TLOGICAL(unsafe { &mut *(value as *mut c_int) }),
505 TCOMPLEX => KeywordDatatypeMut::TCOMPLEX(unsafe { &mut *(value as *mut [f32; 2]) }),
506 TDBLCOMPLEX => {
507 KeywordDatatypeMut::TDBLCOMPLEX(unsafe { &mut *(value as *mut [f64; 2]) })
508 }
509 TULONGLONG => {
510 KeywordDatatypeMut::TULONGLONG(unsafe { &mut *(value as *mut ULONGLONG) })
511 }
512 TLONGLONG => KeywordDatatypeMut::TLONGLONG(unsafe { &mut *(value as *mut LONGLONG) }),
513 _ => KeywordDatatypeMut::INVALID(datatype),
514 }
515 }
516
517 pub fn to_datatype_code(&self) -> c_int {
518 match self {
519 KeywordDatatypeMut::TBYTE(_) => TBYTE,
520 KeywordDatatypeMut::TSBYTE(_) => TSBYTE,
521 KeywordDatatypeMut::TSHORT(_) => TSHORT,
522 KeywordDatatypeMut::TUSHORT(_) => TUSHORT,
523 KeywordDatatypeMut::TINT(_) => TINT,
524 KeywordDatatypeMut::TUINT(_) => TUINT,
525 KeywordDatatypeMut::TLONG(_) => TLONG,
526 KeywordDatatypeMut::TULONG(_) => TULONG,
527 KeywordDatatypeMut::TFLOAT(_) => TFLOAT,
528 KeywordDatatypeMut::TDOUBLE(_) => TDOUBLE,
529 KeywordDatatypeMut::TSTRING(_) => TSTRING,
530 KeywordDatatypeMut::TLOGICAL(_) => TLOGICAL,
531 KeywordDatatypeMut::TCOMPLEX(_) => TCOMPLEX,
532 KeywordDatatypeMut::TDBLCOMPLEX(_) => TDBLCOMPLEX,
533 KeywordDatatypeMut::TULONGLONG(_) => TULONGLONG,
534 KeywordDatatypeMut::TLONGLONG(_) => TLONGLONG,
535 KeywordDatatypeMut::INVALID(x) => *x,
536 }
537 }
538}
539
540pub(crate) fn bytes_per_datatype(datatype: c_int) -> Option<usize> {
541 match datatype {
542 TBIT => Some(1),
543 TBYTE => Some(1),
544 TLOGICAL => Some(1),
545 TSBYTE => Some(1),
546 TUSHORT => Some(2),
547 TSHORT => Some(2),
548 TUINT => Some(4),
549 TINT => Some(4),
550 TULONG => Some(std::mem::size_of::<c_ulong>()),
551 TLONG => Some(std::mem::size_of::<c_long>()),
552 TULONGLONG => Some(8),
553 TLONGLONG => Some(8),
554 TFLOAT => Some(4),
555 TDOUBLE => Some(8),
556 TCOMPLEX => Some(8),
557 TDBLCOMPLEX => Some(16),
558 TSTRING => Some(std::mem::size_of::<usize>()), _ => None,
560 }
561}
562
563fn atoi<F: FromStr>(input: &str) -> Result<F, <F as FromStr>::Err> {
565 let input = input.trim();
566 let i = input
567 .find(|c: char| !c.is_numeric() && c != '-' && c != '+')
568 .unwrap_or(input.len());
569 input[..i].parse::<F>()
570}
571
572fn fmt_f64(num: f64, precision: usize, exp_pad: usize) -> String {
574 let mut num = format!("{num:.precision$E}");
575 let exp = num.split_off(num.find('E').unwrap());
577
578 let (sign, exp) = if exp.starts_with("E-") {
579 ('-', &exp[2..])
580 } else {
581 ('+', &exp[1..])
582 };
583 num.push_str(&format!("E{sign}{exp:0>exp_pad$}"));
584
585 num.to_string()
586}
587
588const WB_MODE: *const c_char = c"wb".as_ptr().cast::<c_char>();
589const RB_MODE: *const c_char = c"rb".as_ptr().cast::<c_char>();
590
591#[cfg(not(target_os = "windows"))]
592unsafe extern "C" {
593
594 #[cfg_attr(target_os = "macos", link_name = "__stdinp")]
595 pub unsafe static mut stdin: *mut FILE;
596
597 #[cfg_attr(target_os = "macos", link_name = "__stdoutp")]
598 pub unsafe static mut stdout: *mut FILE;
599
600 #[cfg_attr(target_os = "macos", link_name = "__stderrp")]
601 pub unsafe static mut stderr: *mut FILE;
602}
603
604#[cfg(windows)]
605unsafe extern "C" {
606 pub unsafe fn __acrt_iob_func(idx: libc::c_uint) -> *mut FILE;
607}
608
609#[macro_export]
610#[cfg(not(target_os = "windows"))]
611macro_rules! STDIN {
612 () => {
613 $crate::stdin
614 };
615}
616
617#[macro_export]
618#[cfg(windows)]
619macro_rules! STDIN {
620 () => {
621 $crate::__acrt_iob_func(0)
622 };
623}
624
625#[macro_export]
626#[cfg(not(target_os = "windows"))]
627macro_rules! STDOUT {
628 () => {
629 $crate::stdout
630 };
631}
632
633#[macro_export]
634#[cfg(windows)]
635macro_rules! STDOUT {
636 () => {
637 $crate::__acrt_iob_func(1)
638 };
639}
640
641#[macro_export]
642#[cfg(not(target_os = "windows"))]
643macro_rules! STDERR {
644 () => {
645 $crate::stderr
646 };
647}
648
649#[macro_export]
650#[cfg(windows)]
651macro_rules! STDERR {
652 () => {
653 $crate::__acrt_iob_func(2)
654 };
655}
656
657#[cfg(test)]
658mod tests {
659 use std::{ffi::CString, slice};
660
661 use crate::cs;
662 use bytemuck::cast_slice;
663 use cfileio::{ffclos_safer, ffinit_safer};
664
665 use putkey::ffcrim_safer;
666 use tempfile::Builder;
667
668 use crate::{
669 fitsio::{USHORT_IMG, fitsfile},
670 helpers::testhelpers::with_temp_file,
671 };
672
673 use super::*;
674
675 use crate::aliases::rust_api::{fits_update_key, fits_write_img};
676
677 #[test]
678 fn test_write_image() {
679 unsafe {
680 with_temp_file(|_filename| {
681 let bitpix = USHORT_IMG;
682 let naxis = 2;
683 const NAXES: [c_long; 2] = [300, 200];
684 let mut storage: [[u16; NAXES[0] as usize]; NAXES[1] as usize] =
685 [[0; NAXES[0] as usize]; NAXES[1] as usize];
686 let mut fptr: Option<Box<fitsfile>> = None;
687 let mut status = 0;
688
689 let _tempfile = Builder::new()
690 .prefix("my-temporary-note")
691 .suffix(".fits")
692 .tempfile()
693 .unwrap();
694
695 let tdir = Builder::new().prefix("rsfitsio-").tempdir().unwrap();
696 let _abc = Builder::new().prefix("prefix").tempfile();
697 let tdir_path = tdir.path();
698 let filename = tdir_path.join("test.fits");
699
700 let filename_str = filename.to_str().expect("cannot create string filename");
701 let filename_cstr = CString::new(filename_str).unwrap();
702
703 status = ffinit_safer(
704 &mut fptr,
705 cast_slice(filename_cstr.as_bytes_with_nul()),
706 &mut status,
707 );
708 assert_eq!(status, 0);
709
710 let mut fptr = fptr.unwrap();
711
712 status = ffcrim_safer(&mut fptr, bitpix, naxis, &NAXES, &mut status);
713 assert_eq!(status, 0);
714
715 for jj in 0..(NAXES[1] as usize) {
716 for ii in 0..(NAXES[0] as usize) {
717 storage[jj][ii] = (ii + jj) as u16;
718 }
719 }
720
721 let fpixel = 1; let nelements = NAXES[0] * NAXES[1]; let s = slice::from_raw_parts(
726 storage.as_ptr() as *mut u16,
727 (NAXES[0] * NAXES[1]) as usize,
728 );
729 fits_write_img(
730 &mut fptr,
731 TUSHORT,
732 fpixel,
733 nelements as LONGLONG,
734 cast_slice(s),
735 &mut status,
736 );
737 assert_eq!(status, 0);
738
739 let exposure = 1500;
742 fits_update_key(
743 &mut fptr,
744 KeywordDatatype::TLONG(&exposure),
745 cs!(c"EXPOSURE"),
746 Some(cs!(c"Total Exposure Time")),
747 &mut status,
748 );
749 assert_eq!(status, 0);
750
751 ffclos_safer(fptr, &mut status); assert_eq!(status, 0);
753 });
754 }
755 }
756}