#ifndef _WIN32
#include <pthread.h>
+#ifdef __APPLE__
+#include <semaphore.h>
+#endif
+#endif
+
+#ifndef BYTE_ORDER
+#define BYTE_ORDER __BYTE_ORDER
+#endif
+#ifndef LITTLE_ENDIAN
+#define LITTLE_ENDIAN __LITTLE_ENDIAN
+#endif
+#ifndef BIG_ENDIAN
+#define BIG_ENDIAN __BIG_ENDIAN
#endif
#include "mdb.h"
#include "midl.h"
-#if (__BYTE_ORDER == __LITTLE_ENDIAN) == (__BYTE_ORDER == __BIG_ENDIAN)
-# error "Unknown or unsupported endianness (__BYTE_ORDER)"
+#if (BYTE_ORDER == LITTLE_ENDIAN) == (BYTE_ORDER == BIG_ENDIAN)
+# error "Unknown or unsupported endianness (BYTE_ORDER)"
#elif (-6 & 5) || CHAR_BIT != 8 || UINT_MAX < 0xffffffff || ULONG_MAX % 0xFFFF
# error "Two's complement, reasonably sized integer types, please"
#endif
#define GET_PAGESIZE(x) {SYSTEM_INFO si; GetSystemInfo(&si); (x) = si.dwPageSize;}
#define close(fd) CloseHandle(fd)
#define munmap(ptr,len) UnmapViewOfFile(ptr)
+#else
+#ifdef __APPLE__
+#define LOCK_MUTEX_R(env) sem_wait((env)->me_rmutex)
+#define UNLOCK_MUTEX_R(env) sem_post((env)->me_rmutex)
+#define LOCK_MUTEX_W(env) sem_wait((env)->me_wmutex)
+#define UNLOCK_MUTEX_W(env) sem_post((env)->me_wmutex)
+#define fdatasync(fd) fsync(fd)
#else
/** Lock the reader mutex.
*/
/** Unlock the writer mutex.
*/
#define UNLOCK_MUTEX_W(env) pthread_mutex_unlock(&(env)->me_txns->mti_wmutex)
+#endif /* __APPLE__ */
/** Get the error code for the last failed system function.
*/
#define GET_PAGESIZE(x) ((x) = sysconf(_SC_PAGE_SIZE))
#endif
+#if defined(_WIN32) || defined(__APPLE__)
+#define MNAME_LEN 32
+#endif
+
/** @} */
#ifndef _WIN32
uint32_t mtb_magic;
/** Version number of this lock file. Must be set to #MDB_VERSION. */
uint32_t mtb_version;
-#ifdef _WIN32
- char mtb_rmname[32];
+#if defined(_WIN32) || defined(__APPLE__)
+ char mtb_rmname[MNAME_LEN];
#else
/** Mutex protecting access to this table.
* This is the reader lock that #LOCK_MUTEX_R acquires.
char pad[(sizeof(MDB_txbody)+CACHELINE-1) & ~(CACHELINE-1)];
} mt1;
union {
-#ifdef _WIN32
- char mt2_wmname[32];
+#if defined(_WIN32) || defined(__APPLE__)
+ char mt2_wmname[MNAME_LEN];
#define mti_wmname mt2.mt2_wmname
#else
pthread_mutex_t mt2_wmutex;
/** @} */
/** Common header for all page types.
- * Overflow pages occupy a number of contiguous pages with no
+ * Overflow records occupy a number of contiguous pages with no
* headers on any page after the first.
*/
typedef struct MDB_page {
#define mp_pgno mp_p.p_pgno
#define mp_next mp_p.p_next
- union padded {
+ union {
pgno_t p_pgno; /**< page number */
void * p_next; /**< for in-memory list of freed structs */
} mp_p;
+/** @defgroup mdb_page Page Flags
+ * @ingroup internal
+ * Flags for the page headers.
+ * @{
+ */
#define P_BRANCH 0x01 /**< branch page */
#define P_LEAF 0x02 /**< leaf page */
#define P_OVERFLOW 0x04 /**< overflow page */
#define P_META 0x08 /**< meta page */
#define P_DIRTY 0x10 /**< dirty page */
#define P_LEAF2 0x20 /**< for #MDB_DUPFIXED records */
- uint32_t mp_flags;
+/** @} */
+ uint32_t mp_flags; /**< @ref mdb_page */
#define mp_lower mp_pb.pb.pb_lower
#define mp_upper mp_pb.pb.pb_upper
#define mp_pages mp_pb.pb_pages
- union page_bounds {
+ union {
struct {
indx_t pb_lower; /**< lower bound of free space */
indx_t pb_upper; /**< upper bound of free space */
/** lo and hi are used for data size on leaf nodes and for
* child pgno on branch nodes. On 64 bit platforms, flags
* is also used for pgno. (Branch nodes have no flags).
- * They are in in host byte order in case that lets some
+ * They are in host byte order in case that lets some
* accesses be optimized into a 32-bit word access.
*/
-#define mn_lo mn_offset[__BYTE_ORDER!=__LITTLE_ENDIAN]
-#define mn_hi mn_offset[__BYTE_ORDER==__LITTLE_ENDIAN] /**< part of dsize or pgno */
- unsigned short mn_offset[2];
- unsigned short mn_flags; /**< flags for special node types */
+#define mn_lo mn_offset[BYTE_ORDER!=LITTLE_ENDIAN]
+#define mn_hi mn_offset[BYTE_ORDER==LITTLE_ENDIAN] /**< part of dsize or pgno */
+ unsigned short mn_offset[2]; /**< storage for #mn_lo and #mn_hi */
+/** @defgroup mdb_node Node Flags
+ * @ingroup internal
+ * Flags for node headers.
+ * @{
+ */
#define F_BIGDATA 0x01 /**< data put on overflow page */
#define F_SUBDATA 0x02 /**< data is a sub-database */
#define F_DUPDATA 0x04 /**< data has duplicates */
+/** @} */
+ unsigned short mn_flags; /**< @ref mdb_node */
unsigned short mn_ksize; /**< key size */
char mn_data[1]; /**< key and data are appended here */
} MDB_node;
*/
MDB_dbi mt_numdbs;
+/** @defgroup mdb_txn Transaction Flags
+ * @ingroup internal
+ * @{
+ */
#define MDB_TXN_RDONLY 0x01 /**< read-only transaction */
#define MDB_TXN_ERROR 0x02 /**< an error has occurred */
- unsigned int mt_flags;
+/** @} */
+ unsigned int mt_flags; /**< @ref mdb_txn */
/** Tracks which of the two meta pages was used at the start
* of this transaction.
*/
MDB_dbx *mc_dbx;
unsigned short mc_snum; /**< number of pushed pages */
unsigned short mc_top; /**< index of top page, mc_snum-1 */
- unsigned int mc_flags;
+/** @defgroup mdb_cursor Cursor Flags
+ * @ingroup internal
+ * Cursor state flags.
+ * @{
+ */
#define C_INITIALIZED 0x01 /**< cursor has been initialized and is valid */
#define C_EOF 0x02 /**< No more data */
-#define C_XDIRTY 0x04 /**< @deprecated mc_xcursor needs to be flushed */
+/** @} */
+ unsigned int mc_flags; /**< @ref mdb_cursor */
MDB_page *mc_pg[CURSOR_STACK]; /**< stack of pushed pages */
indx_t mc_ki[CURSOR_STACK]; /**< stack of page indices */
};
HANDLE me_mfd; /**< just for writing the meta pages */
/** Failed to update the meta page. Probably an I/O error. */
#define MDB_FATAL_ERROR 0x80000000U
- uint32_t me_flags;
+ uint32_t me_flags; /**< @ref mdb_env */
uint32_t me_extrapad; /**< unused for now */
unsigned int me_maxreaders; /**< size of the reader table */
MDB_dbi me_numdbs; /**< number of DBs opened */
HANDLE me_rmutex; /* Windows mutexes don't reside in shared mem */
HANDLE me_wmutex;
#endif
+#ifdef __APPLE__
+ sem_t *me_rmutex; /* Apple doesn't support shared mutexes */
+ sem_t *me_wmutex;
+#endif
};
/** max number of pages to commit in one writev() call */
#define MDB_COMMIT_PAGES 64
}
#endif
}
+#if defined(_WIN32) || defined(__APPLE__)
+/*
+ * hash_64 - 64 bit Fowler/Noll/Vo-0 FNV-1a hash code
+ *
+ * @(#) $Revision: 5.1 $
+ * @(#) $Id: hash_64a.c,v 5.1 2009/06/30 09:01:38 chongo Exp $
+ * @(#) $Source: /usr/local/src/cmd/fnv/RCS/hash_64a.c,v $
+ *
+ * http://www.isthe.com/chongo/tech/comp/fnv/index.html
+ *
+ ***
+ *
+ * Please do not copyright this code. This code is in the public domain.
+ *
+ * LANDON CURT NOLL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO
+ * EVENT SHALL LANDON CURT NOLL BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
+ * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
+ * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ *
+ * By:
+ * chongo <Landon Curt Noll> /\oo/\
+ * http://www.isthe.com/chongo/
+ *
+ * Share and Enjoy! :-)
+ */
+
+typedef unsigned long long mdb_hash_t;
+#define MDB_HASH_INIT ((mdb_hash_t)0xcbf29ce484222325ULL)
+
+/** perform a 64 bit Fowler/Noll/Vo FNV-1a hash on a buffer
+ * @param[in] str string to hash
+ * @param[in] hval initial value for hash
+ * @return 64 bit hash
+ *
+ * NOTE: To use the recommended 64 bit FNV-1a hash, use MDB_HASH_INIT as the
+ * hval arg on the first call.
+ */
+static inline mdb_hash_t
+mdb_hash_str(char *str, mdb_hash_t hval)
+{
+ unsigned char *s = (unsigned char *)str; /* unsigned string */
+ /*
+ * FNV-1a hash each octet of the string
+ */
+ while (*s) {
+ /* xor the bottom with the current octet */
+ hval ^= (mdb_hash_t)*s++;
+
+ /* multiply by the 64 bit FNV magic prime mod 2^64 */
+ hval += (hval << 1) + (hval << 4) + (hval << 5) +
+ (hval << 7) + (hval << 8) + (hval << 40);
+ }
+ /* return our new hash value */
+ return hval;
+}
+
+/** Hash the string and output the hash in hex.
+ * @param[in] str string to hash
+ * @param[out] hexbuf an array of 17 chars to hold the hash
+ */
+static void
+mdb_hash_hex(char *str, char *hexbuf)
+{
+ int i;
+ mdb_hash_t h = mdb_hash_str(str, MDB_HASH_INIT);
+ for (i=0; i<8; i++) {
+ hexbuf += sprintf(hexbuf, "%02x", (unsigned int)h & 0xff);
+ h >>= 8;
+ }
+}
+#endif
/** Open and/or initialize the lock region for the environment.
* @param[in] env The MDB environment.
#endif
if (*excl) {
#ifdef _WIN32
- char *ptr;
+ char hexbuf[17];
if (!mdb_sec_inited) {
InitializeSecurityDescriptor(&mdb_null_sd,
SECURITY_DESCRIPTOR_REVISION);
mdb_all_sa.lpSecurityDescriptor = &mdb_null_sd;
mdb_sec_inited = 1;
}
- /* FIXME: only using up to 20 characters of the env path here,
- * probably not enough to assure uniqueness...
- */
- sprintf(env->me_txns->mti_rmname, "Global\\MDBr%.20s", lpath);
- ptr = env->me_txns->mti_rmname + sizeof("Global\\MDBr");
- while ((ptr = strchr(ptr, '\\')))
- *ptr++ = '/';
+ mdb_hash_hex(lpath, hexbuf);
+ sprintf(env->me_txns->mti_rmname, "Global\\MDBr%s", hexbuf);
env->me_rmutex = CreateMutex(&mdb_all_sa, FALSE, env->me_txns->mti_rmname);
if (!env->me_rmutex) {
rc = ErrCode();
goto fail;
}
- sprintf(env->me_txns->mti_rmname, "Global\\MDBw%.20s", lpath);
- ptr = env->me_txns->mti_rmname + sizeof("Global\\MDBw");
- while ((ptr = strchr(ptr, '\\')))
- *ptr++ = '/';
- env->me_wmutex = CreateMutex(&mdb_all_sa, FALSE, env->me_txns->mti_rmname);
+ sprintf(env->me_txns->mti_wmname, "Global\\MDBw%s", hexbuf);
+ env->me_wmutex = CreateMutex(&mdb_all_sa, FALSE, env->me_txns->mti_wmname);
if (!env->me_wmutex) {
rc = ErrCode();
goto fail;
}
-#else
+#else /* _WIN32 */
+#ifdef __APPLE__
+ char hexbuf[17];
+ mdb_hash_hex(lpath, hexbuf);
+ sprintf(env->me_txns->mti_rmname, "MDBr%s", hexbuf);
+ if (sem_unlink(env->me_txns->mti_rmname)) {
+ rc = ErrCode();
+ if (rc != ENOENT && rc != EINVAL)
+ goto fail;
+ }
+ env->me_rmutex = sem_open(env->me_txns->mti_rmname, O_CREAT, mode, 1);
+ if (!env->me_rmutex) {
+ rc = ErrCode();
+ goto fail;
+ }
+ sprintf(env->me_txns->mti_wmname, "MDBw%s", hexbuf);
+ if (sem_unlink(env->me_txns->mti_wmname)) {
+ rc = ErrCode();
+ if (rc != ENOENT && rc != EINVAL)
+ goto fail;
+ }
+ env->me_wmutex = sem_open(env->me_txns->mti_wmname, O_CREAT, mode, 1);
+ if (!env->me_wmutex) {
+ rc = ErrCode();
+ goto fail;
+ }
+#else /* __APPLE__ */
pthread_mutexattr_t mattr;
pthread_mutexattr_init(&mattr);
}
pthread_mutex_init(&env->me_txns->mti_mutex, &mattr);
pthread_mutex_init(&env->me_txns->mti_wmutex, &mattr);
-#endif
+#endif /* __APPLE__ */
+#endif /* _WIN32 */
env->me_txns->mti_version = MDB_VERSION;
env->me_txns->mti_magic = MDB_MAGIC;
env->me_txns->mti_txnid = 0;
rc = ErrCode();
goto fail;
}
+#endif
+#ifdef __APPLE__
+ env->me_rmutex = sem_open(env->me_txns->mti_rmname, 0);
+ if (!env->me_rmutex) {
+ rc = ErrCode();
+ goto fail;
+ }
+ env->me_wmutex = sem_open(env->me_txns->mti_wmname, 0);
+ if (!env->me_wmutex) {
+ rc = ErrCode();
+ goto fail;
+ }
#endif
}
return MDB_SUCCESS;
static int
mdb_cmp_cint(const MDB_val *a, const MDB_val *b)
{
-#if __BYTE_ORDER == __LITTLE_ENDIAN
+#if BYTE_ORDER == LITTLE_ENDIAN
unsigned short *u, *c;
int x;