diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index c0b58ba..f43f7c9 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -10,10 +10,18 @@ jobs: ci: strategy: matrix: - os: [ubuntu-latest, macos-latest] + os: [ubuntu-latest, macos-latest, windows-latest] runs-on: ${{ matrix.os }} steps: + - uses: KyleMayes/install-llvm-action@v2 + if: matrix.os == 'windows-latest' + with: + version: "18.1" + directory: ${{ runner.temp }}/llvm + - name: Set LIBCLANG_PATH + run: echo "LIBCLANG_PATH=$((gcm clang).source -replace "clang.exe")" >> $env:GITHUB_ENV + if: matrix.os == 'windows-latest' - uses: actions/checkout@main - uses: dtolnay/rust-toolchain@master with: diff --git a/mdbx-sys/build.rs b/mdbx-sys/build.rs index 3bad6f1..a2f6b69 100644 --- a/mdbx-sys/build.rs +++ b/mdbx-sys/build.rs @@ -106,5 +106,10 @@ fn main() { cc_builder.define("MDBX_HAVE_BUILTIN_CPU_SUPPORTS", "0"); } + if cfg!(windows) { + println!(r"cargo:rustc-link-lib=dylib=ntdll"); + println!(r"cargo:rustc-link-lib=dylib=user32"); + } + cc_builder.file(mdbx.join("mdbx.c")).compile("libmdbx.a"); } diff --git a/src/cursor.rs b/src/cursor.rs index ef7b748..bfa831b 100644 --- a/src/cursor.rs +++ b/src/cursor.rs @@ -13,7 +13,7 @@ use ffi::{ MDBX_NEXT_MULTIPLE, MDBX_NEXT_NODUP, MDBX_PREV, MDBX_PREV_DUP, MDBX_PREV_MULTIPLE, MDBX_PREV_NODUP, MDBX_SET, MDBX_SET_KEY, MDBX_SET_LOWERBOUND, MDBX_SET_RANGE, }; -use libc::{c_uint, c_void}; +use libc::c_void; use parking_lot::Mutex; use std::{borrow::Cow, fmt, marker::PhantomData, mem, ptr, result, sync::Arc}; @@ -498,7 +498,7 @@ impl<'txn> Cursor<'txn, RW> { }; mdbx_result(unsafe { txn_execute(&self.txn, |_| { - ffi::mdbx_cursor_put(self.cursor.0, &key_val, &mut data_val, flags.bits()) + ffi::mdbx_cursor_put(self.cursor.0, &key_val, &mut data_val, c_enum(flags.bits())) }) })?; @@ -514,7 +514,7 @@ impl<'txn> Cursor<'txn, RW> { pub fn del(&mut self, flags: WriteFlags) -> Result<()> { mdbx_result(unsafe { txn_execute(&self.txn, |_| { - ffi::mdbx_cursor_del(self.cursor.0, flags.bits()) + ffi::mdbx_cursor_del(self.cursor.0, c_enum(flags.bits())) }) })?; @@ -810,7 +810,7 @@ where cursor: &'cur mut Cursor<'txn, K>, /// The first operation to perform when the consumer calls Iter.next(). - op: c_uint, + op: ffi::MDBX_cursor_op, _marker: PhantomData, }, @@ -823,7 +823,7 @@ where Value: Decodable<'txn>, { /// Creates a new iterator backed by the given cursor. - fn new(cursor: &'cur mut Cursor<'txn, K>, op: c_uint) -> Self { + fn new(cursor: &'cur mut Cursor<'txn, K>, op: ffi::MDBX_cursor_op) -> Self { IterDup::Ok { cursor, op, diff --git a/src/database.rs b/src/database.rs index d11d501..a496374 100644 --- a/src/database.rs +++ b/src/database.rs @@ -7,6 +7,10 @@ use crate::{ use libc::c_uint; use mem::size_of; use sealed::sealed; +#[cfg(windows)] +use std::ffi::OsStr; +#[cfg(unix)] +use std::os::unix::ffi::OsStrExt; use std::{ ffi::CString, fmt, @@ -14,7 +18,6 @@ use std::{ marker::PhantomData, mem, ops::Deref, - os::unix::ffi::OsStrExt, path::Path, ptr, result, sync::mpsc::{sync_channel, SyncSender}, @@ -22,6 +25,18 @@ use std::{ time::Duration, }; +#[cfg(windows)] +/// Adding a 'missing' trait from windows OsStrExt +trait OsStrExtLmdb { + fn as_bytes(&self) -> &[u8]; +} +#[cfg(windows)] +impl OsStrExtLmdb for OsStr { + fn as_bytes(&self) -> &[u8] { + self.to_str().unwrap().as_bytes() + } +} + #[sealed] pub trait DatabaseKind: Debug + 'static { const EXTRA_FLAGS: ffi::MDBX_env_flags_t; diff --git a/src/error.rs b/src/error.rs index 1f77d2d..e711c23 100644 --- a/src/error.rs +++ b/src/error.rs @@ -153,6 +153,7 @@ mod test { #[test] fn test_description() { + #[cfg(not(windows))] assert_eq!("Permission denied", Error::from_err_code(13).to_string()); assert_eq!( diff --git a/src/flags.rs b/src/flags.rs index cbbc2f8..abf52c5 100644 --- a/src/flags.rs +++ b/src/flags.rs @@ -99,14 +99,14 @@ bitflags! { #[doc="Table options."] #[derive(Default, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Clone, Copy)] pub struct TableFlags: c_uint { - const REVERSE_KEY = MDBX_REVERSEKEY; - const DUP_SORT = MDBX_DUPSORT; - const INTEGER_KEY = MDBX_INTEGERKEY; - const DUP_FIXED = MDBX_DUPFIXED; - const INTEGER_DUP = MDBX_INTEGERDUP; - const REVERSE_DUP = MDBX_REVERSEDUP; - const CREATE = MDBX_CREATE; - const ACCEDE = MDBX_DB_ACCEDE; + const REVERSE_KEY = MDBX_REVERSEKEY as u32; + const DUP_SORT = MDBX_DUPSORT as u32; + const INTEGER_KEY = MDBX_INTEGERKEY as u32; + const DUP_FIXED = MDBX_DUPFIXED as u32; + const INTEGER_DUP = MDBX_INTEGERDUP as u32; + const REVERSE_DUP = MDBX_REVERSEDUP as u32; + const CREATE = MDBX_CREATE as u32; + const ACCEDE = MDBX_DB_ACCEDE as u32; } } @@ -114,14 +114,32 @@ bitflags! { #[doc="Write options."] #[derive(Default, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Clone, Copy)] pub struct WriteFlags: c_uint { - const UPSERT = MDBX_UPSERT; - const NO_OVERWRITE = MDBX_NOOVERWRITE; - const NO_DUP_DATA = MDBX_NODUPDATA; - const CURRENT = MDBX_CURRENT; - const ALLDUPS = MDBX_ALLDUPS; - const RESERVE = MDBX_RESERVE; - const APPEND = MDBX_APPEND; - const APPEND_DUP = MDBX_APPENDDUP; - const MULTIPLE = MDBX_MULTIPLE; + const UPSERT = MDBX_UPSERT as u32; + const NO_OVERWRITE = MDBX_NOOVERWRITE as u32; + const NO_DUP_DATA = MDBX_NODUPDATA as u32; + const CURRENT = MDBX_CURRENT as u32; + const ALLDUPS = MDBX_ALLDUPS as u32; + const RESERVE = MDBX_RESERVE as u32; + const APPEND = MDBX_APPEND as u32; + const APPEND_DUP = MDBX_APPENDDUP as u32; + const MULTIPLE = MDBX_MULTIPLE as u32; } } + +/// Compatibility shim to convert between `i32` and `u32` enums on Windows and UNIX. +/// +/// Windows treats C enums as `i32`, while Unix uses `u32`. We use `u32` enums internally +/// and then cast back to `i32` only where Windows requires it. +/// +/// See https://github.com/rust-lang/rust-bindgen/issues/1907 +#[cfg(windows)] +#[inline(always)] +pub const fn c_enum(rust_value: u32) -> i32 { + rust_value as i32 +} + +#[cfg(not(windows))] +#[inline(always)] +pub const fn c_enum(rust_value: u32) -> u32 { + rust_value +} diff --git a/src/lib.rs b/src/lib.rs index 343cef3..a3e1659 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,4 @@ -#![allow(clippy::type_complexity)] +#![allow(clippy::type_complexity, clippy::unnecessary_cast)] #![doc = include_str!("../README.md")] #![cfg_attr(docsrs, feature(doc_cfg))] diff --git a/src/table.rs b/src/table.rs index dfdf1df..3fd6ce0 100644 --- a/src/table.rs +++ b/src/table.rs @@ -1,6 +1,7 @@ use crate::{ database::DatabaseKind, error::{mdbx_result, Result}, + flags::c_enum, transaction::{txn_execute, TransactionKind}, Transaction, }; @@ -30,7 +31,7 @@ impl<'txn> Table<'txn> { }; let mut dbi: ffi::MDBX_dbi = 0; mdbx_result(txn_execute(&txn.txn_mutex(), |txn| unsafe { - ffi::mdbx_dbi_open(txn, name_ptr, flags, &mut dbi) + ffi::mdbx_dbi_open(txn, name_ptr, c_enum(flags), &mut dbi) }))?; Ok(Self::new_from_ptr(dbi)) } diff --git a/src/transaction.rs b/src/transaction.rs index 7a6a7bf..633f02c 100644 --- a/src/transaction.rs +++ b/src/transaction.rs @@ -1,7 +1,7 @@ use crate::{ database::{Database, DatabaseKind, NoWriteMap, TxnManagerMessage, TxnPtr}, error::{mdbx_result, Result}, - flags::{TableFlags, WriteFlags}, + flags::{c_enum, TableFlags, WriteFlags}, table::Table, Cursor, Decodable, Error, Stat, }; @@ -287,7 +287,13 @@ where iov_base: data.as_ptr() as *mut c_void, }; mdbx_result(txn_execute(&self.txn, |txn| unsafe { - ffi::mdbx_put(txn, table.dbi(), &key_val, &mut data_val, flags.bits()) + ffi::mdbx_put( + txn, + table.dbi(), + &key_val, + &mut data_val, + c_enum(flags.bits()), + ) }))?; Ok(()) @@ -319,7 +325,7 @@ where table.dbi(), &key_val, &mut data_val, - flags.bits() | ffi::MDBX_RESERVE, + c_enum(flags.bits() | ffi::MDBX_RESERVE as u32), ) }))?; Ok(slice::from_raw_parts_mut(