Skip to content

Commit

Permalink
Merge pull request #6 from ccouzens/leptonica_1.83.0
Browse files Browse the repository at this point in the history
Leptonica 1.83.0
  • Loading branch information
ccouzens authored Feb 19, 2023
2 parents 4bb9d74 + fe8f2c8 commit 033498e
Show file tree
Hide file tree
Showing 16 changed files with 493 additions and 212 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "leptonica-plumbing"
version = "0.6.0"
version = "1.0.0"
authors = ["Chris Couzens <[email protected]>"]
edition = "2018"
description = "Safe wrapper of `leptonica-sys`"
Expand Down
23 changes: 23 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,26 @@ their memory safety.

Having a safety layer that stays simple improves the correctness and
maintainability of the above libraries.

## Testing

To test for memory leaks, test with `valgrind`.

```bash
cargo test --release && valgrind --leak-check=yes --error-exitcode=1 --leak-check=full --show-leak-kinds=all "$(find target/*/deps/ -executable -name 'leptonica_plumbing-*')"
```

You may find that leptonica always leaks 16B of memory.

To test with a manually compiled Leptonica, test with additional environment
variables

```bash
LD_LIBRARY_PATH="$(pwd)/../../DanBloomberg/leptonica/local/lib" PKG_CONFIG_PATH="$(pwd)/../../DanBloomberg/leptonica/local/lib/pkgconfig" cargo test
```

The two can be combined

```bash
LD_LIBRARY_PATH="$(pwd)/../../DanBloomberg/leptonica/local/lib" PKG_CONFIG_PATH="$(pwd)/../../DanBloomberg/leptonica/local/lib/pkgconfig" bash -c 'cargo test --release && valgrind --leak-check=yes --error-exitcode=1 --leak-check=full --show-leak-kinds=all "$(find target/*/deps/ -executable -name 'leptonica_plumbing-*')"'
```
21 changes: 0 additions & 21 deletions src/borrowed_box.rs

This file was deleted.

29 changes: 0 additions & 29 deletions src/borrowed_pix.rs

This file was deleted.

86 changes: 62 additions & 24 deletions src/box.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
extern crate leptonica_sys;
extern crate thiserror;
use crate::memory::{LeptonicaDestroy, RefCountedExclusive};

use self::thiserror::Error;
use leptonica_sys::{boxCreateValid, boxDestroy, l_int32};
use leptonica_sys::{boxCreateValid, boxDestroy, boxGetGeometry, l_int32, l_ok};
use thiserror::Error;

/// Wrapper around Leptonica's [`Box`](https://tpgit.github.io/Leptonica/struct_box.html) structure
#[derive(Debug, PartialEq)]
Expand All @@ -13,29 +12,30 @@ pub struct Box(*mut leptonica_sys::Box);
#[error("Box::create_valid returned null")]
pub struct BoxCreateValidError();

impl Drop for Box {
fn drop(&mut self) {
unsafe {
boxDestroy(&mut self.0);
}
}
}

impl AsRef<leptonica_sys::Box> for Box {
fn as_ref(&self) -> &leptonica_sys::Box {
unsafe { &*self.0 }
}
}

impl AsMut<leptonica_sys::Box> for Box {
fn as_mut(&mut self) -> &mut leptonica_sys::Box {
unsafe { &mut *self.0 }
}
}

impl Box {
/// Convinience wrapper for [Self::create_valid]
pub fn new(
x: l_int32,
y: l_int32,
w: l_int32,
h: l_int32,
) -> Result<Self, BoxCreateValidError> {
Self::create_valid(x, y, w, h)
/// Create an owned Box from a box pointer
///
/// # Safety
///
/// The pointer must be to a valid Box struct.
/// The data pointed at may not be mutated while held by
/// this struct except by this struct.
/// On drop, the destroy method will be called (decrements
/// the ref counter).
pub unsafe fn new_from_pointer(b: *mut leptonica_sys::Box) -> Self {
Self(b)
}

/// Wrapper for [`boxCreateValid`](https://tpgit.github.io/Leptonica/boxbasic_8c.html#a435610d86a8562dc60bfd75fe0a15420)
Expand All @@ -46,21 +46,59 @@ impl Box {
y: l_int32,
w: l_int32,
h: l_int32,
) -> Result<Self, BoxCreateValidError> {
) -> Result<RefCountedExclusive<Self>, BoxCreateValidError> {
let ptr = unsafe { boxCreateValid(x, y, w, h) };
if ptr.is_null() {
Err(BoxCreateValidError())
} else {
Ok(Self(ptr))
Ok(unsafe { RefCountedExclusive::new(Self(ptr)) })
}
}

/// Wrapper for [`boxGetGeometry`](https://tpgit.github.io/Leptonica/boxbasic_8c.html#aaf754e00c062c3f0f726bea73a17e646)
pub fn get_geometry(
&self,
px: Option<&mut l_int32>,
py: Option<&mut l_int32>,
pw: Option<&mut l_int32>,
ph: Option<&mut l_int32>,
) -> l_ok {
unsafe {
boxGetGeometry(
self.0,
match px {
None => std::ptr::null_mut(),
Some(px) => px,
},
match py {
None => std::ptr::null_mut(),
Some(py) => py,
},
match pw {
None => std::ptr::null_mut(),
Some(pw) => pw,
},
match ph {
None => std::ptr::null_mut(),
Some(ph) => ph,
},
)
}
}
}

impl LeptonicaDestroy for Box {
unsafe fn destroy(&mut self) {
boxDestroy(&mut self.0);
}
}

#[test]
fn create_valid_test() {
let r#box = Box::create_valid(1, 2, 3, 4).unwrap();
let lbox: &leptonica_sys::Box = r#box.as_ref();
assert_eq!(lbox.w, 3);
let mut pw = 0;
r#box.get_geometry(None, None, Some(&mut pw), None);
assert_eq!(pw, 3);
}

#[test]
Expand Down
98 changes: 73 additions & 25 deletions src/boxa.rs
Original file line number Diff line number Diff line change
@@ -1,26 +1,28 @@
extern crate leptonica_sys;
extern crate thiserror;
use std::convert::TryInto;

use leptonica_sys::{boxaCreate, boxaDestroy, l_int32};
use leptonica_sys::{boxaCreate, boxaDestroy, boxaGetBox, boxaGetCount, l_int32, L_CLONE, L_COPY};

use crate::{
memory::{LeptonicaDestroy, RefCounted, RefCountedExclusive},
Box,
};

/// Wrapper around Leptonica's [`Boxa`](https://tpgit.github.io/Leptonica/struct_boxa.html) structure
#[derive(Debug, PartialEq)]
pub struct Boxa(*mut leptonica_sys::Boxa);

impl Drop for Boxa {
fn drop(&mut self) {
unsafe {
boxaDestroy(&mut self.0);
}
}
}

impl AsRef<leptonica_sys::Boxa> for Boxa {
fn as_ref(&self) -> &leptonica_sys::Boxa {
unsafe { &*self.0 }
}
}

impl AsMut<leptonica_sys::Boxa> for Boxa {
fn as_mut(&mut self) -> &mut leptonica_sys::Boxa {
unsafe { &mut *self.0 }
}
}

impl Boxa {
/// Create a new Boxa from a pointer
///
Expand All @@ -35,30 +37,76 @@ impl Boxa {
/// Wrapper for [`boxaCreate`](https://tpgit.github.io/Leptonica/boxbasic_8c.html#ae59916b7506831be9bf2119dea063253)
///
/// Input: n (initial number of ptrs) Return: boxa, or null on error
pub fn create(n: l_int32) -> Option<Boxa> {
pub fn create(n: l_int32) -> Option<RefCountedExclusive<Boxa>> {
let ptr = unsafe { boxaCreate(n) };
if ptr.is_null() {
None
} else {
Some(Self(ptr))
Some(unsafe { RefCountedExclusive::new(Self(ptr)) })
}
}

/// Safely borrow the nth item
pub fn get(&self, i: isize) -> Option<crate::BorrowedBox> {
let lboxa: &leptonica_sys::Boxa = self.as_ref();
if lboxa.n <= std::convert::TryFrom::try_from(i).ok()? {
None
} else {
unsafe { Some(crate::BorrowedBox::new(&*lboxa.box_.offset(i))) }
/// Wrapper for [`boxaGetCount`](https://tpgit.github.io/Leptonica/boxbasic_8c.html#a82555cab9ef5578c4728ef5109264723)
pub fn get_count(&self) -> l_int32 {
unsafe { boxaGetCount(self.0) }
}

/// Wrapper for [`boxaGetBox`](https://tpgit.github.io/Leptonica/boxbasic_8c.html#ac7c6fcfadf130bfa738ce6aab51318e5) with copied `accessflag`: `L_COPY`
pub fn get_box_copied(&self, index: l_int32) -> Option<RefCountedExclusive<Box>> {
unsafe {
boxaGetBox(self.0, index, L_COPY.try_into().unwrap())
.as_mut()
.map(|raw| RefCountedExclusive::new(Box::new_from_pointer(raw)))
}
}

/// Wrapper for [`boxaGetBox`](https://tpgit.github.io/Leptonica/boxbasic_8c.html#ac7c6fcfadf130bfa738ce6aab51318e5) with copied `accessflag`: `L_CLONE`
pub fn get_box_cloned(&self, index: l_int32) -> Option<RefCounted<Box>> {
unsafe {
boxaGetBox(self.0, index, L_CLONE.try_into().unwrap())
.as_mut()
.map(|raw| RefCounted::new(Box::new_from_pointer(raw)))
}
}
}

impl LeptonicaDestroy for Boxa {
unsafe fn destroy(&mut self) {
boxaDestroy(&mut self.0);
}
}

#[test]
fn create_valid_test() {
let boxa = Boxa::create(4).unwrap();
let lboxa: &leptonica_sys::Boxa = boxa.as_ref();
assert_eq!(lboxa.nalloc, 4);
assert_eq!(lboxa.n, 0);
fn get_test() {
use leptonica_sys::boxaAddBox;

let mut boxa = Boxa::create(4).unwrap();
assert_eq!(boxa.get_count(), 0);
let mut box_1 = Box::create_valid(1, 2, 3, 4).unwrap();
let mut box_2 = Box::create_valid(5, 6, 7, 8).unwrap();
unsafe {
boxaAddBox(boxa.as_mut(), box_1.as_mut(), L_CLONE.try_into().unwrap());
boxaAddBox(boxa.as_mut(), box_2.as_mut(), L_CLONE.try_into().unwrap());
}
assert_eq!(boxa.get_count(), 2);

let box_1_cloned = boxa.get_box_cloned(0).unwrap();
let box_2_copied = boxa.get_box_copied(1).unwrap();

let (mut px, mut py, mut pw, mut ph) = (-1, -1, -1, -1);
box_1_cloned.get_geometry(Some(&mut px), Some(&mut py), Some(&mut pw), Some(&mut ph));
assert_eq!((px, py, pw, ph), (1, 2, 3, 4));
// Because Cloned reuses and reference counts the same pointer
assert_eq!(
box_1.as_ref() as *const leptonica_sys::Box,
box_1_cloned.as_ref() as *const leptonica_sys::Box
);

box_2_copied.get_geometry(Some(&mut px), Some(&mut py), Some(&mut pw), Some(&mut ph));
assert_eq!((px, py, pw, ph), (5, 6, 7, 8));
// Because Copied creates a new instance
assert!(
box_2.as_ref() as *const leptonica_sys::Box
!= box_2_copied.as_ref() as *const leptonica_sys::Box
);
}
Loading

0 comments on commit 033498e

Please sign in to comment.