summaryrefslogtreecommitdiffstats
path: root/src/lib.rs
blob: ddb4405ddfa3865e20eed57c5334fee82358b611 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
use std::ffi;
use std::ptr;
use std::slice;

#[link(name = "iec16022")]
unsafe extern "C" {
    /// encoding function
    ///
    /// Returns the grid (malloced) containing the matrix. L corner at 0,0.
    /// Takes suggested size in `w_ptr`, `h_ptr`, or 0,0. Fills in actual size.
    /// Takes `barcodelen` and `barcode` to be encoded
    /// Note, if `encodingptr` is null, then fills with auto picked (malloced)
    /// encoding.
    /// If `lenp` not null, then the length of encoded data before any final unlatch
    /// or pad is stored.
    /// If `maxp` not null, then the max storage of this size code is stored
    /// If `eccp` not null, then the number of ecc bytes used in this size is stored
    /// Returns 0 on error (writes to stderr with details).
    fn iec16022ecc200f(
        w_ptr: *mut ffi::c_int,
        h_ptr: *mut ffi::c_int,
        encodingptr: *mut *mut ffi::c_char,
        barcodelen: ffi::c_int,
        barcode: *const ffi::c_uchar,
        lenp: *mut ffi::c_int,
        maxp: *mut ffi::c_int,
        eccp: *mut ffi::c_int,
        flags: ffi::c_int,
    ) -> *mut ffi::c_uchar;
}

#[derive(PartialEq, Eq, Debug)]
pub enum EncodeError {
    DataTooLong,
    Encoder,
    DimensionsTooLarge,
}

#[derive(Debug)]
pub struct DataMatrix {
    width: usize,
    height: usize,
    grid_ptr: *mut ffi::c_uchar,
}

impl DataMatrix {
    /// Encodes data as a data matrix
    pub fn encode(data: &[u8]) -> Result<Self, EncodeError> {
        let mut width = 0;
        let mut height = 0;
        let data_ptr = data.as_ptr();
        let data_len = data
            .len()
            .try_into()
            .map_err(|_| EncodeError::DataTooLong)?;

        // SAFETY: ffi
        let grid_ptr = unsafe {
            iec16022ecc200f(
                &raw mut width,
                &raw mut height,
                ptr::null_mut(),
                data_len,
                data_ptr,
                ptr::null_mut(),
                ptr::null_mut(),
                ptr::null_mut(),
                0,
            )
        };

        if grid_ptr.is_null() || width == 0 || height == 0 {
            Err(EncodeError::Encoder)
        } else {
            Ok(Self {
                width: width
                    .try_into()
                    .map_err(|_| EncodeError::DimensionsTooLarge)?,
                height: height
                    .try_into()
                    .map_err(|_| EncodeError::DimensionsTooLarge)?,
                grid_ptr,
            })
        }
    }

    #[must_use]
    pub const fn width(&self) -> usize {
        self.width
    }

    #[must_use]
    pub const fn height(&self) -> usize {
        self.height
    }

    #[must_use]
    const fn grid(&self) -> &[bool] {
        // SAFETY: returned grid consists only contains values 0 and 1
        unsafe { slice::from_raw_parts(self.grid_ptr as *const bool, self.width * self.height) }
    }

    #[must_use]
    pub const fn get(&self, row: usize, col: usize) -> bool {
        assert!(row < self.height(), "row index out of bounds");
        assert!(col < self.width(), "col index out of bounds");

        let idx = row * self.width + col;
        self.grid()[idx]
    }
}

impl Drop for DataMatrix {
    fn drop(&mut self) {
        // SAFETY: self.ptr malloc'ed in ffi
        unsafe { libc::free(self.grid_ptr.cast()) };
    }
}