Jacob Brown
Jacob Brown

Reputation: 7561

GraphicsMagick FFI issue

As an exercise, I'm attempting to write a GraphicsMagick FFI wrapper in Rust. I'm having an issue replicating some reference C code:

Demo C code:

Image
  *image = (Image *) NULL;
ImageInfo
  *imageInfo;
ExceptionInfo
  exception;

InitializeMagick(NULL);
imageInfo=CloneImageInfo(0);
GetExceptionInfo(&exception);

And here is my (naive) translation to Rust:

let img: *mut ffi::Image;
let img_info: *mut ffi::ImageInfo;
let exception: *mut ffi::ExceptionInfo = ptr::null_mut();
unsafe {             
  ffi::InitializeMagick(ptr::null_mut());
  img_info = 
    ffi::CloneImageInfo(ptr::null_mut() as *const ffi::ImageInfo);
  ffi::GetExceptionInfo(exception);
  // ...
}

This compiles just fine, but when I try to run it, I see:

magick/error.c:388: GetExceptionInfo: Assertion `exception != (ExceptionInfo *) ((void *)0)' failed

which is caused by ffi::GetExceptionInfo(exception). The only difference seems to be that the C exception isn't "initialized", but I don't know enough about C to know if there is a difference between a null and an empty/uninitialized pointer.

Upvotes: 1

Views: 180

Answers (2)

ArtemGr
ArtemGr

Reputation: 12547

The difference between your C and Rust code is that the C version allocates an ExceptionInfo instance on the stack and passes into the GetExceptionInfo a pointer referencing that instance.

Your Rust code, on the other hand, passes a NULL pointer.

GetExceptionInfo specifically guards against being passed a NULL pointer, you can see the assertion's code here, in magick/error.c.

I don't know what kind of FFI bindings you use, but if the ExceptionInfo is fully defined in them then you should be able to allocate it on the stack and pass a reference to it just like in the C version:

let mut exception: ffi::ExceptionInfo = unsafe {std::mem::uninitialized()};
unsafe {ffi::GetExceptionInfo (&mut exception);}

Upvotes: 4

Shepmaster
Shepmaster

Reputation: 430673

The error message states (rewritten a bit):

Assertion exception != NULL failed

That is, you cannot pass NULL to that method. Note the C code:

ExceptionInfo exception;

This is not a pointer. You need to allocate space for it and then pass in a reference to the allocated space.

The documentation shows the definition:

typedef struct _ExceptionInfo
{
  char
    *reason,
    *description;

  ExceptionType
    severity;

  unsigned long
    signature;
} ExceptionInfo;

You will need to represent this in Rust. Something like this untested code:

extern crate libc;

#[repr(C)]
struct ExceptionInfo {
    reason: *const libc::c_char,
    description: *const libc::c_char,
    severity: ExceptionType,
    signature: libc::c_ulong,
}

#[repr(C)]
enum ExceptionType {
    UndefinedException,
    WarningException = 300,
    // the rest
}

Then you need to allocate it and pass a reference. More untested code:

let img_info;
let mut exception = ffi::ExceptionInfo::new();

unsafe {             
    ffi::InitializeMagick(ptr::null_mut());
    img_info = 
        ffi::CloneImageInfo(ptr::null_mut() as *const ffi::ImageInfo);
    ffi::GetExceptionInfo(&mut exception);
    // ...
}

Note that Rust style is 4 space indents.

Upvotes: 4

Related Questions