Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Leptonica 1.83.0 #6

Merged
merged 27 commits into from
Feb 19, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
2194748
Start addressing the leptonica 1.83.0 private fields change
ccouzens Jan 15, 2023
7bda079
Refactor so that the Trait gets the name BorrowedPix
ccouzens Jan 21, 2023
a479747
Make borrowed pix work like borrowed box
ccouzens Jan 21, 2023
2ecc009
Remove borrowed pix method from raw leptonica type
ccouzens Jan 21, 2023
425c08e
Move concrete BorrowedBox type to wrapper name
ccouzens Jan 21, 2023
3724e6e
Add a get_geometry trait for Box
ccouzens Jan 21, 2023
1f3fae9
Use leptonica's wrapper functions for boxa
ccouzens Jan 21, 2023
958baa1
Convert existing codebase to work with leptonica 1.83
ccouzens Jan 22, 2023
7ba117d
Organize imports
ccouzens Jan 22, 2023
827f254
Rename BorrowedPixWrapper to ClonedPix
ccouzens Jan 22, 2023
fdde7cc
Use new function rather than accessing fields
ccouzens Jan 28, 2023
86bf6d5
Give Box a new method
ccouzens Jan 28, 2023
9f41a30
Make more use of new function
ccouzens Jan 29, 2023
f0b757a
Rename to new_from_pointer
ccouzens Jan 29, 2023
7133a3f
Improve documentation of ClonedPix
ccouzens Jan 29, 2023
76b5e31
Move str to its own file
ccouzens Jan 30, 2023
35adcca
Don't use as_ref for str
ccouzens Jan 31, 2023
0b122d8
Derive command to run tests with valgrind
ccouzens Jan 31, 2023
459c6cc
Add tests for version methods
ccouzens Feb 3, 2023
751503c
Make some notes on naming and memory
ccouzens Feb 7, 2023
1c74205
Create wrappers for leptonica pointers
ccouzens Feb 11, 2023
637081c
Use Memory wrappers with pix and pixa
ccouzens Feb 12, 2023
deb02f3
Move box and boxa to new memory managament helpers
ccouzens Feb 12, 2023
de7ab22
Remove old memory explination from Readme
ccouzens Feb 12, 2023
f485db0
Increase test coverage
ccouzens Feb 12, 2023
313b04a
Allow converting RefCountedExclusive to RefCounted
ccouzens Feb 17, 2023
fe8f2c8
Bump to version 1.0.0
ccouzens Feb 17, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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