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

Further user documentation improvements #261

Open
basshelal opened this issue Aug 17, 2021 · 1 comment
Open

Further user documentation improvements #261

basshelal opened this issue Aug 17, 2021 · 1 comment

Comments

@basshelal
Copy link
Contributor

basshelal commented Aug 17, 2021

I want to add more to the user docs based on what I've learned from experience over the last few months using JNR-FFI and reading and testing the codebase.
Ideally, we should eventually incorporate the same information in the javadocs too albeit a little more technical there.

Here's what I have in mind:

Struct

  • Struct.Function used to map struct callbacks, these don't yet allow structs as parameters and instead only allow pointers or primitives, for structs you need to create the struct then call Struct.useMemory() to get it working, this will be mentioned further in the Pointers & Memory document.
  • Struct.Enum used to map enum types, if you're using an enum * then use Pointer, we need to add an EnumRef at some point similar to StructRef.
  • Struct.StructRef used to map struct pointers, similarly Struct.inner() for inner structs, I went into detail about this here: How should I map nested structs #256 .
    Struct.StructRef is also used for struct "arrays" which in C look like struct pointers but allow you to get indexed structs, we have a case for this in Java in the form of Struct.StructRef.get(int length) returning an array of structs.
  • Struct.Padding was also mentioned in How should I map nested structs #256 and that needs its own section too for when C has padded structs, this has caused me major headaches when using the padded struct as an inner struct somewhere else.

Pointers & Memory

Pointer is the most powerful and most dangerous class you can use, use Memory or Runtime.getMemoryManager() to allocate and manage memory more easily. You can convert anything to and from a Pointer including structs using Struct.useMemory(Pointer pointer) to set an existing struct's values from a pointer and Struct.getMemory(Struct struct) to get a Pointer to a struct. If you're daring, you can access the memory directly using all of the get functions but this allows you to access raw memory unchecked which can cause segfaults or VM crashes at best, corrupt and useless data leading to silent failures at worst. Caution and testing is critical here.

Kotlin Support

Everything works in Kotlin and works really well, but there are a few weird things that some Kotlin users may struggle with (like I did) mostly regarding the type system's nullability but also in using Structs. Struct fields need to be marked with @JvmField Edit I don't think this is true, you just need to have properties with backing fields and they need to be initialized using the JNR-FFI Struct member constructors like Struct.UTF8StringRef() or Struct.Boolean() because those calls add the field to the Struct. You should probably use Struct? instead of Struct for function return types as you may end up with strange errors that make no sense, only use the non-nullable if you are absolutely sure that null is not a possibility. I'm not entirely sure if the same is true for Pointer as a C NULL is just a pointer to 0x0, I need to personally check this at some point before writing this.

Squeezing Performance

  • Add a section for preferring Pointer over Struct or ByReference types, the conversion is done for you in those cases and if you're super stingy about performance (millions of calls per second probably) then do this and get the values you need by calling the many Pointer.get functions instead of having JNR-FFI convert for you. You can also use a single instance of Struct and just set and use it's memory instead, as mentioned in Pointers & Memory
  • Use LibraryOption.LoadNow to open and load (dlopen) the library at load time rather than at first function call time, tiny improvement in performance but a good practice to catch errors early.

Known Issues / Limitations

  • We do not currently support Struct by value for both function parameters and returns, this is a small issue since most C libraries don't do this (for obvious reasons) but JNA does support this and in the case that your library makes use of this style, JNR-FFI is not yet supportive.
  • We do not support invoking C callbacks from Java, this is a known issue mentioned in Call a function pointer #31 that may or may not be added in the future.

Troubleshooting/ Common Issues

  • Tell users how to dump generated bytecode if they want to see what code is being generated for their mapped functions, this can be useful to figure out why a mapping may be faster than another for example or what exactly JNR-FFI is doing for you which maybe you would rather do yourself if possible (such as the Struct creation when mapping using a Struct by pointer)
@headius
Copy link
Member

headius commented Aug 18, 2021

This is a great idea, and as I mentioned in #262 I believe we need to work on having more and better documentation, both as external documentation and as javadocs and comments in the code, in large part so that we and other contributors can actually understand what's going on.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants