/* * Copyright © 2016-2025 The LmdbJava Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.lmdbjava; import static java.io.File.createTempFile; import static java.lang.System.getProperty; import static java.lang.Thread.currentThread; import static java.util.Objects.requireNonNull; import static jnr.ffi.LibraryLoader.create; import static jnr.ffi.Runtime.getRuntime; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.nio.file.Files; import jnr.ffi.Pointer; import jnr.ffi.Struct; import jnr.ffi.annotations.Delegate; import jnr.ffi.annotations.In; import jnr.ffi.annotations.Out; import jnr.ffi.byref.IntByReference; import jnr.ffi.byref.NativeLongByReference; import jnr.ffi.byref.PointerByReference; import jnr.ffi.types.size_t; /** * JNR-FFI interface to LMDB. * *

For performance reasons pointers are used rather than structs. */ final class Library { /** * Java system property name that can be set to the path of an existing directory into which the * LMDB system library will be extracted from the LmdbJava JAR. If unspecified the LMDB system * library is extracted to the java.io.tmpdir. Ignored if the LMDB system library is * not being extracted from the LmdbJava JAR (as would be the case if other system properties * defined in TargetName have been set). */ public static final String LMDB_EXTRACT_DIR_PROP = "lmdbjava.extract.dir"; /** Indicates the directory where the LMDB system library will be extracted. */ static final String EXTRACT_DIR = getProperty(LMDB_EXTRACT_DIR_PROP, getProperty("java.io.tmpdir")); static final Lmdb LIB; static final jnr.ffi.Runtime RUNTIME; static { final String libToLoad; if (TargetName.IS_EXTERNAL) { libToLoad = TargetName.RESOLVED_FILENAME; } else { libToLoad = extract(TargetName.RESOLVED_FILENAME); } LIB = create(Lmdb.class).load(libToLoad); RUNTIME = getRuntime(LIB); } private Library() {} private static String extract(final String name) { final String suffix = name.substring(name.lastIndexOf('.')); final File file; try { final File dir = new File(EXTRACT_DIR); if (!dir.exists() || !dir.isDirectory()) { throw new IllegalStateException("Invalid extraction directory " + dir); } file = createTempFile("lmdbjava-native-library-", suffix, dir); file.deleteOnExit(); final ClassLoader cl = currentThread().getContextClassLoader(); try (InputStream in = cl.getResourceAsStream(name); OutputStream out = Files.newOutputStream(file.toPath())) { requireNonNull(in, "Classpath resource not found"); int bytes; final byte[] buffer = new byte[4_096]; while (-1 != (bytes = in.read(buffer))) { out.write(buffer, 0, bytes); } } return file.getAbsolutePath(); } catch (final IOException e) { throw new LmdbException("Failed to extract " + name, e); } } /** Structure to wrap a native MDB_envinfo. Not for external use. */ public static final class MDB_envinfo extends Struct { public final Pointer f0_me_mapaddr; public final size_t f1_me_mapsize; public final size_t f2_me_last_pgno; public final size_t f3_me_last_txnid; public final u_int32_t f4_me_maxreaders; public final u_int32_t f5_me_numreaders; MDB_envinfo(final jnr.ffi.Runtime runtime) { super(runtime); this.f0_me_mapaddr = new Pointer(); this.f1_me_mapsize = new size_t(); this.f2_me_last_pgno = new size_t(); this.f3_me_last_txnid = new size_t(); this.f4_me_maxreaders = new u_int32_t(); this.f5_me_numreaders = new u_int32_t(); } } /** Structure to wrap a native MDB_stat. Not for external use. */ public static final class MDB_stat extends Struct { public final u_int32_t f0_ms_psize; public final u_int32_t f1_ms_depth; public final size_t f2_ms_branch_pages; public final size_t f3_ms_leaf_pages; public final size_t f4_ms_overflow_pages; public final size_t f5_ms_entries; MDB_stat(final jnr.ffi.Runtime runtime) { super(runtime); this.f0_ms_psize = new u_int32_t(); this.f1_ms_depth = new u_int32_t(); this.f2_ms_branch_pages = new size_t(); this.f3_ms_leaf_pages = new size_t(); this.f4_ms_overflow_pages = new size_t(); this.f5_ms_entries = new size_t(); } } /** Custom comparator callback used by mdb_set_compare. */ public interface ComparatorCallback { @Delegate int compare(@In Pointer keyA, @In Pointer keyB); } /** JNR API for MDB-defined C functions. Not for external use. */ public interface Lmdb { void mdb_cursor_close(@In Pointer cursor); int mdb_cursor_count(@In Pointer cursor, NativeLongByReference countp); int mdb_cursor_del(@In Pointer cursor, int flags); int mdb_cursor_get(@In Pointer cursor, Pointer k, @Out Pointer v, int cursorOp); int mdb_cursor_open(@In Pointer txn, @In Pointer dbi, PointerByReference cursorPtr); int mdb_cursor_put(@In Pointer cursor, @In Pointer key, @In Pointer data, int flags); int mdb_cursor_renew(@In Pointer txn, @In Pointer cursor); void mdb_dbi_close(@In Pointer env, @In Pointer dbi); int mdb_dbi_flags(@In Pointer txn, @In Pointer dbi, @Out IntByReference flags); int mdb_dbi_open(@In Pointer txn, @In byte[] name, int flags, @In Pointer dbiPtr); int mdb_del(@In Pointer txn, @In Pointer dbi, @In Pointer key, @In Pointer data); int mdb_drop(@In Pointer txn, @In Pointer dbi, int del); void mdb_env_close(@In Pointer env); int mdb_env_copy2(@In Pointer env, @In String path, int flags); int mdb_env_create(PointerByReference envPtr); int mdb_env_get_fd(@In Pointer env, @In Pointer fd); int mdb_env_get_flags(@In Pointer env, int flags); int mdb_env_get_maxkeysize(@In Pointer env); int mdb_env_get_maxreaders(@In Pointer env, int readers); int mdb_env_get_path(@In Pointer env, String path); int mdb_env_info(@In Pointer env, @Out MDB_envinfo info); int mdb_env_open(@In Pointer env, @In String path, int flags, int mode); int mdb_env_set_flags(@In Pointer env, int flags, int onoff); int mdb_env_set_mapsize(@In Pointer env, @size_t long size); int mdb_env_set_maxdbs(@In Pointer env, int dbs); int mdb_env_set_maxreaders(@In Pointer env, int readers); int mdb_env_stat(@In Pointer env, @Out MDB_stat stat); int mdb_env_sync(@In Pointer env, int f); int mdb_get(@In Pointer txn, @In Pointer dbi, @In Pointer key, @Out Pointer data); int mdb_put(@In Pointer txn, @In Pointer dbi, @In Pointer key, @In Pointer data, int flags); int mdb_reader_check(@In Pointer env, @Out IntByReference dead); int mdb_set_compare(@In Pointer txn, @In Pointer dbi, ComparatorCallback cb); int mdb_stat(@In Pointer txn, @In Pointer dbi, @Out MDB_stat stat); String mdb_strerror(int rc); void mdb_txn_abort(@In Pointer txn); int mdb_txn_begin(@In Pointer env, @In Pointer parentTx, int flags, Pointer txPtr); int mdb_txn_commit(@In Pointer txn); Pointer mdb_txn_env(@In Pointer txn); long mdb_txn_id(@In Pointer txn); int mdb_txn_renew(@In Pointer txn); void mdb_txn_reset(@In Pointer txn); int mdb_cmp(@In Pointer txn, @In Pointer dbi, @In Pointer key1, @In Pointer key2); Pointer mdb_version(IntByReference major, IntByReference minor, IntByReference patch); } }