Merge branch 'feature-sophia' into develop

Conflicts:
	Builds/VisualStudio2012/RippleD.vcxproj
	Builds/VisualStudio2012/RippleD.vcxproj.filters
This commit is contained in:
Vinnie Falco
2013-09-19 18:27:57 -07:00
59 changed files with 10176 additions and 1 deletions

1
.gitignore vendored
View File

@@ -30,7 +30,6 @@ Release/*.*
tmp
# Ignore database directory.
db
db/*.db
db/*.db-*

View File

@@ -92,6 +92,7 @@ SOURCES += \
# New style
#
SOURCES += \
../../src/ripple/sophia/ripple_sophia.c \
../../src/ripple/testoverlay/ripple_testoverlay.cpp \
../../src/ripple/validators/ripple_validators.cpp

View File

@@ -22,6 +22,7 @@
<ClCompile Include="..\..\build\proto\ripple.pb.cc" />
<ClCompile Include="..\..\src\ripple\beast\ripple_beast.cpp" />
<ClCompile Include="..\..\src\ripple\beast\ripple_beastc.c" />
<ClCompile Include="..\..\src\ripple\sophia\ripple_sophia.cpp" />
<ClCompile Include="..\..\src\ripple\testoverlay\impl\TestOverlay.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
@@ -1430,6 +1431,7 @@
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\..\src\ripple\sophia\ripple_sophia.h" />
<ClInclude Include="..\..\src\ripple\testoverlay\api\ConfigType.h" />
<ClInclude Include="..\..\src\ripple\testoverlay\api\ConnectionType.h" />
<ClInclude Include="..\..\src\ripple\testoverlay\api\InitPolicy.h" />

View File

@@ -175,6 +175,9 @@
<Filter Include="[1] Ripple\ripple_websocket\autosocket">
<UniqueIdentifier>{99ac4d07-04a7-4ce3-96c7-b8ea578f1a61}</UniqueIdentifier>
</Filter>
<Filter Include="[2] Ripple %28New%29\sophia">
<UniqueIdentifier>{29b20c8e-267a-487a-9086-fb0c85a922f6}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="..\..\src\ripple_basics\containers\RangeSet.cpp">
@@ -918,6 +921,9 @@
<ClCompile Include="..\..\src\ripple_net\basics\AsyncService.cpp">
<Filter>[1] Ripple\ripple_net\basics</Filter>
</ClCompile>
<ClCompile Include="..\..\src\ripple\sophia\ripple_sophia.cpp">
<Filter>[2] Ripple %28New%29\sophia</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\..\src\ripple_basics\containers\KeyCache.h">
@@ -1821,6 +1827,9 @@
<ClInclude Include="..\..\src\ripple_net\basics\AsyncService.h">
<Filter>[1] Ripple\ripple_net\basics</Filter>
</ClInclude>
<ClInclude Include="..\..\src\ripple\sophia\ripple_sophia.h">
<Filter>[2] Ripple %28New%29\sophia</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<CustomBuild Include="..\..\src\ripple_data\protocol\ripple.proto">

View File

@@ -113,6 +113,7 @@ else:
# These are all relative to the repo dir.
#
INCLUDE_PATHS = [
'.',
'src',
'src/leveldb',
'src/leveldb/port',
@@ -161,6 +162,7 @@ COMPILED_FILES.extend([
'src/ripple_leveldb/ripple_leveldb.cpp',
'src/ripple_mdb/ripple_mdb.c',
'src/ripple_net/ripple_net.cpp',
'src/ripple/sophia/ripple_sophia.c',
'src/ripple_websocket/ripple_websocket.cpp'
])

View File

@@ -0,0 +1,26 @@
//------------------------------------------------------------------------------
/*
Copyright (c) 2011-2013, OpenCoin, Inc.
*/
//==============================================================================
#include "BeastConfig.h"
#include "ripple_sophia.h"
//#if RIPPLE_SOPHIA_AVAILBLE
#include "../sophia/db/cat.c"
#include "../sophia/db/crc.c"
#include "../sophia/db/cursor.c"
#include "../sophia/db/e.c"
#include "../sophia/db/file.c"
#include "../sophia/db/gc.c"
#include "../sophia/db/i.c"
#include "../sophia/db/merge.c"
#include "../sophia/db/recover.c"
#include "../sophia/db/rep.c"
#include "../sophia/db/sp.c"
#include "../sophia/db/util.c"
//#endif

View File

@@ -0,0 +1,32 @@
//------------------------------------------------------------------------------
/*
Copyright (c) 2011-2013, OpenCoin, Inc.
*/
//==============================================================================
#ifndef RIPPLE_SOPHIA_H_INCLUDED
#define RIPPLE_SOPHIA_H_INCLUDED
#include "beast/beast/Config.h"
#if ! BEAST_WIN32
#define RIPPLE_SOPHIA_AVAILABLE 1
#ifdef __cplusplus
extern "C" {
#endif
#include "../sophia/db/sophia.h"
#ifdef __cplusplus
}
#endif
#else
#define RIPPLE_SOPHIA_AVAILABLE 0
#endif
#endif

View File

@@ -610,6 +610,10 @@ void NodeStore::addAvailableBackends ()
NodeStore::addBackendFactory (MdbBackendFactory::getInstance ());
#endif
#if RIPPLE_SOPHIA_AVAILABLE
NodeStore::addBackendFactory (SophiaBackendFactory::getInstance ());
#endif
NodeStore::addBackendFactory (KeyvaDBBackendFactory::getInstance ());
}
@@ -957,6 +961,10 @@ public:
#if RIPPLE_MDB_AVAILABLE
testBackend ("mdb", seedValue);
#endif
#if RIPPLE_SOPHIA_AVAILABLE
testBackend ("sophia", seedValue);
#endif
}
};
@@ -1064,6 +1072,10 @@ public:
testBackend ("mdb", seedValue);
#endif
#if RIPPLE_SOPHIA_AVAILABLE
testBackend ("sophia", seedValue);
#endif
testBackend ("sqlite", seedValue);
}
};
@@ -1246,6 +1258,10 @@ public:
#if RIPPLE_MDB_AVAILABLE
testNodeStore ("mdb", useEphemeralDatabase, true, seedValue);
#endif
#if RIPPLE_SOPHIA_AVAILABLE
testNodeStore ("sophia", useEphemeralDatabase, true, seedValue);
#endif
}
//--------------------------------------------------------------------------
@@ -1262,6 +1278,10 @@ public:
//testImport ("mdb", "mdb", seedValue);
#endif
#if RIPPLE_SOPHIA_AVAILABLE
//testImport ("sophia", "sophia", seedValue);
#endif
testImport ("sqlite", "sqlite", seedValue);
}

View File

@@ -0,0 +1,182 @@
//------------------------------------------------------------------------------
/*
Copyright (c) 2011-2013, OpenCoin, Inc.
*/
//==============================================================================
class SophiaBackendFactory::Backend
: public NodeStore::Backend
, LeakChecked <SophiaBackendFactory::Backend>
{
public:
typedef RecycledObjectPool <std::string> StringPool;
typedef NodeStore::Batch Batch;
typedef NodeStore::EncodedBlob EncodedBlob;
typedef NodeStore::DecodedBlob DecodedBlob;
//--------------------------------------------------------------------------
Backend (int keyBytes,
StringPairArray const& keyValues,
NodeStore::Scheduler& scheduler)
: m_keyBytes (keyBytes)
, m_scheduler (scheduler)
, m_name (keyValues ["path"].toStdString ())
, m_env (nullptr)
, m_db (nullptr)
{
if (m_name.empty())
Throw (std::runtime_error ("Missing path in Sophia backend"));
m_env = sp_env ();
if (m_env != nullptr)
{
sp_ctl (m_env, SPDIR, SPO_RDWR | SPO_CREAT, m_name.c_str());
m_db = sp_open (m_env);
}
}
~Backend ()
{
if (m_db != nullptr)
sp_destroy (m_db);
if (m_env != nullptr)
sp_destroy (m_env);
}
std::string getName()
{
return m_name;
}
//--------------------------------------------------------------------------
Status fetch (void const* key, NodeObject::Ptr* pObject)
{
pObject->reset ();
Status status (unknown);
void* v (nullptr);
std::size_t vsize;
int rc (sp_get (m_db, key, m_keyBytes, &v, &vsize));
if (rc == 1)
{
DecodedBlob decoded (key, v, vsize);
if (decoded.wasOk ())
{
*pObject = decoded.createObject ();
status = ok;
}
else
{
status = dataCorrupt;
}
::free (v);
}
else if (rc == 0)
{
status = notFound;
}
else
{
String s;
s << "Sophia failed with error code " << rc;
Throw (std::runtime_error (s.toStdString()), __FILE__, __LINE__);
status = notFound;
}
return status;
}
void store (NodeObject::ref object)
{
EncodedBlob::Pool::ScopedItem item (m_blobPool);
EncodedBlob& encoded (item.getObject ());
encoded.prepare (object);
int rv (sp_set (m_db,
encoded.getKey(), m_keyBytes,
encoded.getData(), encoded.getSize()));
if (rv != 0)
{
String s;
s << "Sophia failed with error code " << rv;
Throw (std::runtime_error (s.toStdString()), __FILE__, __LINE__);
}
}
void storeBatch (Batch const& batch)
{
for (NodeStore::Batch::const_iterator iter (batch.begin());
iter != batch.end(); ++iter)
{
store (*iter);
}
}
void visitAll (VisitCallback& callback)
{
}
int getWriteLoad ()
{
return 0;
}
void stopAsync ()
{
m_scheduler.scheduledTasksStopped ();
}
private:
size_t const m_keyBytes;
NodeStore::Scheduler& m_scheduler;
StringPool m_stringPool;
NodeStore::EncodedBlob::Pool m_blobPool;
std::string m_name;
void* m_env;
void* m_db;
};
//------------------------------------------------------------------------------
SophiaBackendFactory::SophiaBackendFactory ()
{
leveldb::Options options;
options.create_if_missing = true;
options.block_cache = leveldb::NewLRUCache (getConfig ().getSize (
siHashNodeDBCache) * 1024 * 1024);
}
SophiaBackendFactory::~SophiaBackendFactory ()
{
}
SophiaBackendFactory* SophiaBackendFactory::getInstance ()
{
return new SophiaBackendFactory;
}
String SophiaBackendFactory::getName () const
{
return "sophia";
}
NodeStore::Backend* SophiaBackendFactory::createInstance (
size_t keyBytes,
StringPairArray const& keyValues,
NodeStore::Scheduler& scheduler)
{
return new SophiaBackendFactory::Backend (keyBytes, keyValues, scheduler);
}
//------------------------------------------------------------------------------

View File

@@ -0,0 +1,32 @@
//------------------------------------------------------------------------------
/*
Copyright (c) 2011-2013, OpenCoin, Inc.
*/
//==============================================================================
#ifndef RIPPLE_CORE_SOPHIABACKENDFACTORY_H_INCLUDED
#define RIPPLE_CORE_SOPHIABACKENDFACTORY_H_INCLUDED
/** Factory to produce Sophia backends for the NodeStore.
@see NodeStore
*/
class SophiaBackendFactory : public NodeStore::BackendFactory
{
private:
class Backend;
SophiaBackendFactory ();
~SophiaBackendFactory ();
public:
static SophiaBackendFactory* getInstance ();
String getName () const;
NodeStore::Backend* createInstance (size_t keyBytes,
StringPairArray const& keyValues,
NodeStore::Scheduler& scheduler);
};
#endif

View File

@@ -24,6 +24,7 @@
#include "../ripple_hyperleveldb/ripple_hyperleveldb.h"
#include "../ripple_leveldb/ripple_leveldb.h"
#include "../ripple_mdb/ripple_mdb.h"
#include "../ripple/sophia/ripple_sophia.h"
namespace ripple
{
@@ -48,6 +49,8 @@ namespace ripple
# include "node/NullBackendFactory.cpp"
# include "node/MdbBackendFactory.h"
# include "node/MdbBackendFactory.cpp"
# include "node/SophiaBackendFactory.h"
# include "node/SophiaBackendFactory.cpp"
#include "node/NodeStore.cpp"
#include "node/NodeObject.cpp"

15
src/sophia/.gitignore vendored Normal file
View File

@@ -0,0 +1,15 @@
*.o
*.mk
out
*.a
gyp-mac-tool
sophia.Makefile
.*.sw[op]
# Test binaries
test/common
test/crash
test/i
test/limit
test/merge
test/recover

29
src/sophia/COPYRIGHT Normal file
View File

@@ -0,0 +1,29 @@
Copyright (C) 2013 Dmitry Simonenko (pmwkaa@gmail.com)
Redistribution and use in source and binary forms, with or
without modification, are permitted provided that the following
conditions are met:
1. Redistributions of source code must retain the above
copyright notice, this list of conditions and the
following disclaimer.
2. Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following
disclaimer in the documentation and/or other materials
provided with the distribution.
THIS SOFTWARE IS PROVIDED BY AUTHORS ``AS IS'' AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
AUTHORS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
SUCH DAMAGE.

5
src/sophia/README Normal file
View File

@@ -0,0 +1,5 @@
sophia - is an modern embeddable key-value database
designed for a highload.
http://sphia.org

58
src/sophia/db/a.h Normal file
View File

@@ -0,0 +1,58 @@
#ifndef SP_A_H_
#define SP_A_H_
/*
* sophia database
* sphia.org
*
* Copyright (c) Dmitry Simonenko
* BSD License
*/
typedef struct spa spa;
struct spa {
spallocf alloc;
void *arg;
};
static inline void
sp_allocinit(spa *a, spallocf f, void *arg) {
a->alloc = f;
a->arg = arg;
}
static inline void*
sp_allocstd(void *ptr, size_t size, void *arg spunused) {
if (splikely(size > 0)) {
if (ptr != NULL)
return realloc(ptr, size);
return malloc(size);
}
assert(ptr != NULL);
free(ptr);
return NULL;
}
static inline void *sp_realloc(spa *a, void *ptr, size_t size) {
return a->alloc(ptr, size, a->arg);
}
static inline void *sp_malloc(spa *a, size_t size) {
return a->alloc(NULL, size, a->arg);
}
static inline char *sp_strdup(spa *a, char *str) {
int sz = strlen(str) + 1;
char *s = a->alloc(NULL, sz, a->arg);
if (spunlikely(s == NULL))
return NULL;
memcpy(s, str, sz);
return s;
}
static inline void sp_free(spa *a, void *ptr) {
a->alloc(ptr, 0, a->arg);
}
#endif

195
src/sophia/db/cat.c Normal file
View File

@@ -0,0 +1,195 @@
/*
* sophia database
* sphia.org
*
* Copyright (c) Dmitry Simonenko
* BSD License
*/
#include "sp.h"
static inline int sp_catensure(spcat *c) {
if ((c->count + 1) < c->top)
return 0;
c->top *= 2;
c->i = realloc(c->i, c->top * sizeof(sppage*));
if (c->i == NULL)
return -1;
return 0;
}
int sp_catinit(spcat *c, spa *a, int top, spcmpf cmp, void *cmparg) {
c->a = a;
c->cmp = cmp;
c->cmparg = cmparg;
c->count = 0;
c->top = top;
c->i = sp_malloc(a, sizeof(sppage*) * top);
if (spunlikely(c->i == NULL))
return -1;
return 0;
}
void sp_catfree(spcat *c) {
uint32_t p = 0;
while (p < c->count) {
sp_free(c->a, c->i[p]->min);
sp_free(c->a, c->i[p]->max);
sp_free(c->a, c->i[p]);
p++;
}
sp_free(c->a, c->i);
}
static inline int
cmppage(spcat *c, sppage *p, sppage *v) {
int l = c->cmp(p->min->key,
p->min->size,
v->min->key,
v->min->size, c->cmparg);
assert(l == c->cmp(p->max->key,
p->max->size,
v->max->key,
v->max->size, c->cmparg));
return l;
}
static inline sppage*
sp_catsearch(spcat *c, sppage *v, uint32_t *index) {
int min = 0;
int max = c->count - 1;
while (max >= min) {
int mid = min + ((max - min) >> 1);
switch (cmppage(c, c->i[mid], v)) {
case -1: min = mid + 1;
continue;
case 1: max = mid - 1;
continue;
default:
*index = mid;
return c->i[mid];
}
}
*index = min;
return NULL;
}
int sp_catset(spcat *c, sppage *n, sppage **o)
{
uint32_t i;
sppage *p = sp_catsearch(c, n, &i);
if (p) {
/* replace */
*o = c->i[i];
c->i[i] = p;
return 0;
}
/* insert */
int rc = sp_catensure(c);
if (spunlikely(rc == -1))
return -1;
/* split page index and insert new page */
memmove(&c->i[i + 1], &c->i[i], sizeof(sppage*) * (c->count - i));
c->i[i] = n;
c->count++;
*o = NULL;
return 0;
}
int sp_catdel(spcat *c, uint32_t idx)
{
assert(idx < c->count);
if (splikely(idx != (uint32_t)(c->count-1)))
memmove(&c->i[idx], &c->i[idx + 1],
sizeof(sppage*) * (c->count - idx - 1));
c->count--;
return 0;
}
static inline int
cmpkey(spcat *c, sppage *p, void *rkey, int size)
{
register int l =
c->cmp(p->min->key, p->min->size, rkey, size, c->cmparg);
register int r =
c->cmp(p->max->key, p->max->size, rkey, size, c->cmparg);
/* inside page range */
if (l <= 0 && r >= 0)
return 0;
/* key > page */
if (l == -1)
return -1;
/* key < page */
assert(r == 1);
return 1;
}
sppage*
sp_catfind(spcat *c, char *rkey, int size, uint32_t *index)
{
register int min = 0;
register int max = c->count - 1;
while (max >= min) {
register int mid = min + ((max - min) >> 1);
switch (cmpkey(c, c->i[mid], rkey, size)) {
case -1: min = mid + 1;
continue;
case 1: max = mid - 1;
continue;
default: *index = mid;
return c->i[mid];
}
}
*index = min;
return NULL;
}
sppage *sp_catroute(spcat *c, char *rkey, int size, uint32_t *idx)
{
if (spunlikely(c->count == 1))
return c->i[0];
uint32_t i;
sppage *p = sp_catfind(c, rkey, size, &i);
if (splikely(p)) {
*idx = i;
return p;
}
if (spunlikely(i >= c->count))
i = c->count - 1;
if (i > 0 && c->cmp(c->i[i]->min->key, c->i[i]->min->size,
rkey,
size, c->cmparg) == 1) {
i = i - 1;
}
if (idx)
*idx = i;
return c->i[i];
}
int sp_catown(spcat *c, uint32_t idx, spv *v)
{
register sppage *p = c->i[idx];
/* equal or equal min or equal max */
switch (cmpkey(c, p, v->key, v->size)) {
case 0:
return 1;
case -1: /* key > page */
/* key > max */
if (idx == c->count-1)
return 1;
break;
case 1: /* key < page */
/* key < min */
if (idx == 0)
return 1;
break;
}
/* key > page && key < page+1.min */
if (c->cmp(v->key, v->size,
c->i[idx + 1]->min->key,
c->i[idx + 1]->min->size, c->cmparg) == -1)
return 1;
return 0;
}

32
src/sophia/db/cat.h Normal file
View File

@@ -0,0 +1,32 @@
#ifndef SP_CAT_H_
#define SP_CAT_H_
/*
* sophia database
* sphia.org
*
* Copyright (c) Dmitry Simonenko
* BSD License
*/
typedef struct spcat spcat;
struct spcat {
spa *a;
sppage **i;
uint32_t count;
uint32_t top;
spcmpf cmp;
void *cmparg;
};
int sp_catinit(spcat*, spa*, int, spcmpf, void*);
void sp_catfree(spcat*);
int sp_catset(spcat*, sppage*, sppage**);
int sp_catget(spcat*, uint64_t);
int sp_catdel(spcat*, uint32_t);
sppage *sp_catfind(spcat*, char*, int, uint32_t*);
sppage *sp_catroute(spcat*, char*, int, uint32_t*);
int sp_catown(spcat*, uint32_t, spv*);
#endif

129
src/sophia/db/core.h Normal file
View File

@@ -0,0 +1,129 @@
#ifndef SP_CORE_H_
#define SP_CORE_H_
/*
* sophia database
* sphia.org
*
* Copyright (c) Dmitry Simonenko
* BSD License
*/
#define SP_VERSION_MAJOR 1
#define SP_VERSION_MINOR 1
typedef struct sp sp;
typedef struct spenv spenv;
enum spmagic {
SPMCUR = 0x15481936L,
SPMENV = 0x06154834L,
SPMDB = 0x00fec0feL,
SPMNONE = 0L
};
typedef enum spmagic spmagic;
struct spenv {
spmagic m;
spe e;
int inuse;
spallocf alloc;
void *allocarg;
spcmpf cmp;
void *cmparg;
uint32_t flags;
char *dir;
int merge;
uint32_t mergewm;
uint32_t page;
uint32_t dbnewsize;
float dbgrow;
int gc;
float gcfactor;
};
struct sp {
spmagic m;
spenv *e;
spa a;
sprep rep;
spi *i, i0, i1;
int iskip; /* skip second index during read */
uint64_t psn; /* page sequence number */
spcat s;
volatile int stop;
sptask merger;
sprefset refs; /* pre allocated key buffer (page merge) */
int lockc; /* incremental cursor lock */
spspinlock lockr; /* repository lock */
spspinlock locks; /* space lock */
spspinlock locki; /* index lock */
};
int sp_rotate(sp*);
static inline int sp_active(sp *s) {
return !s->stop;
}
static inline int
sp_e(sp *s, int type, ...) {
va_list args;
va_start(args, type);
sp_ve(&s->e->e, type, args);
va_end(args);
return -1;
}
static inline int
sp_ee(spenv *e, int type, ...) {
va_list args;
va_start(args, type);
sp_ve(&e->e, type, args);
va_end(args);
return -1;
}
static inline void
sp_glock(sp *s) {
if (s->lockc > 0)
return;
sp_lock(&s->lockr);
sp_replockall(&s->rep);
sp_lock(&s->locki);
sp_lock(&s->locks);
s->lockc++;
}
static inline void
sp_gunlock(sp *s) {
s->lockc--;
if (s->lockc > 0)
return;
sp_unlock(&s->locks);
sp_unlock(&s->locki);
sp_repunlockall(&s->rep);
sp_unlock(&s->lockr);
}
static inline void
sp_iskipset(sp *s, int v) {
sp_lock(&s->locki);
s->iskip = v;
sp_unlock(&s->locki);
}
static inline spi*
sp_ipair(sp *s) {
return (s->i == &s->i0 ? &s->i1 : &s->i0);
}
static inline spi*
sp_iswap(sp *s) {
spi *old = s->i;
s->i = sp_ipair(s);
return old;
}
#endif

343
src/sophia/db/crc.c Normal file
View File

@@ -0,0 +1,343 @@
/*
* sophia database
* sphia.org
*
* Copyright (c) Dmitry Simonenko
* BSD License
*/
/*
* Copyright (c) 2008-2010 Massachusetts Institute of Technology
* Copyright (c) 2004-2006 Intel Corporation
*
* This software program is licensed subject to the BSD License,
* available at http://www.opensource.org/licenses/bsd-license.html
*/
#include "sp.h"
static const uint32_t crc_tableil8_o32[256] =
{
0x00000000, 0xF26B8303, 0xE13B70F7, 0x1350F3F4, 0xC79A971F, 0x35F1141C, 0x26A1E7E8, 0xD4CA64EB,
0x8AD958CF, 0x78B2DBCC, 0x6BE22838, 0x9989AB3B, 0x4D43CFD0, 0xBF284CD3, 0xAC78BF27, 0x5E133C24,
0x105EC76F, 0xE235446C, 0xF165B798, 0x030E349B, 0xD7C45070, 0x25AFD373, 0x36FF2087, 0xC494A384,
0x9A879FA0, 0x68EC1CA3, 0x7BBCEF57, 0x89D76C54, 0x5D1D08BF, 0xAF768BBC, 0xBC267848, 0x4E4DFB4B,
0x20BD8EDE, 0xD2D60DDD, 0xC186FE29, 0x33ED7D2A, 0xE72719C1, 0x154C9AC2, 0x061C6936, 0xF477EA35,
0xAA64D611, 0x580F5512, 0x4B5FA6E6, 0xB93425E5, 0x6DFE410E, 0x9F95C20D, 0x8CC531F9, 0x7EAEB2FA,
0x30E349B1, 0xC288CAB2, 0xD1D83946, 0x23B3BA45, 0xF779DEAE, 0x05125DAD, 0x1642AE59, 0xE4292D5A,
0xBA3A117E, 0x4851927D, 0x5B016189, 0xA96AE28A, 0x7DA08661, 0x8FCB0562, 0x9C9BF696, 0x6EF07595,
0x417B1DBC, 0xB3109EBF, 0xA0406D4B, 0x522BEE48, 0x86E18AA3, 0x748A09A0, 0x67DAFA54, 0x95B17957,
0xCBA24573, 0x39C9C670, 0x2A993584, 0xD8F2B687, 0x0C38D26C, 0xFE53516F, 0xED03A29B, 0x1F682198,
0x5125DAD3, 0xA34E59D0, 0xB01EAA24, 0x42752927, 0x96BF4DCC, 0x64D4CECF, 0x77843D3B, 0x85EFBE38,
0xDBFC821C, 0x2997011F, 0x3AC7F2EB, 0xC8AC71E8, 0x1C661503, 0xEE0D9600, 0xFD5D65F4, 0x0F36E6F7,
0x61C69362, 0x93AD1061, 0x80FDE395, 0x72966096, 0xA65C047D, 0x5437877E, 0x4767748A, 0xB50CF789,
0xEB1FCBAD, 0x197448AE, 0x0A24BB5A, 0xF84F3859, 0x2C855CB2, 0xDEEEDFB1, 0xCDBE2C45, 0x3FD5AF46,
0x7198540D, 0x83F3D70E, 0x90A324FA, 0x62C8A7F9, 0xB602C312, 0x44694011, 0x5739B3E5, 0xA55230E6,
0xFB410CC2, 0x092A8FC1, 0x1A7A7C35, 0xE811FF36, 0x3CDB9BDD, 0xCEB018DE, 0xDDE0EB2A, 0x2F8B6829,
0x82F63B78, 0x709DB87B, 0x63CD4B8F, 0x91A6C88C, 0x456CAC67, 0xB7072F64, 0xA457DC90, 0x563C5F93,
0x082F63B7, 0xFA44E0B4, 0xE9141340, 0x1B7F9043, 0xCFB5F4A8, 0x3DDE77AB, 0x2E8E845F, 0xDCE5075C,
0x92A8FC17, 0x60C37F14, 0x73938CE0, 0x81F80FE3, 0x55326B08, 0xA759E80B, 0xB4091BFF, 0x466298FC,
0x1871A4D8, 0xEA1A27DB, 0xF94AD42F, 0x0B21572C, 0xDFEB33C7, 0x2D80B0C4, 0x3ED04330, 0xCCBBC033,
0xA24BB5A6, 0x502036A5, 0x4370C551, 0xB11B4652, 0x65D122B9, 0x97BAA1BA, 0x84EA524E, 0x7681D14D,
0x2892ED69, 0xDAF96E6A, 0xC9A99D9E, 0x3BC21E9D, 0xEF087A76, 0x1D63F975, 0x0E330A81, 0xFC588982,
0xB21572C9, 0x407EF1CA, 0x532E023E, 0xA145813D, 0x758FE5D6, 0x87E466D5, 0x94B49521, 0x66DF1622,
0x38CC2A06, 0xCAA7A905, 0xD9F75AF1, 0x2B9CD9F2, 0xFF56BD19, 0x0D3D3E1A, 0x1E6DCDEE, 0xEC064EED,
0xC38D26C4, 0x31E6A5C7, 0x22B65633, 0xD0DDD530, 0x0417B1DB, 0xF67C32D8, 0xE52CC12C, 0x1747422F,
0x49547E0B, 0xBB3FFD08, 0xA86F0EFC, 0x5A048DFF, 0x8ECEE914, 0x7CA56A17, 0x6FF599E3, 0x9D9E1AE0,
0xD3D3E1AB, 0x21B862A8, 0x32E8915C, 0xC083125F, 0x144976B4, 0xE622F5B7, 0xF5720643, 0x07198540,
0x590AB964, 0xAB613A67, 0xB831C993, 0x4A5A4A90, 0x9E902E7B, 0x6CFBAD78, 0x7FAB5E8C, 0x8DC0DD8F,
0xE330A81A, 0x115B2B19, 0x020BD8ED, 0xF0605BEE, 0x24AA3F05, 0xD6C1BC06, 0xC5914FF2, 0x37FACCF1,
0x69E9F0D5, 0x9B8273D6, 0x88D28022, 0x7AB90321, 0xAE7367CA, 0x5C18E4C9, 0x4F48173D, 0xBD23943E,
0xF36E6F75, 0x0105EC76, 0x12551F82, 0xE03E9C81, 0x34F4F86A, 0xC69F7B69, 0xD5CF889D, 0x27A40B9E,
0x79B737BA, 0x8BDCB4B9, 0x988C474D, 0x6AE7C44E, 0xBE2DA0A5, 0x4C4623A6, 0x5F16D052, 0xAD7D5351
};
static const uint32_t crc_tableil8_o40[256] =
{
0x00000000, 0x13A29877, 0x274530EE, 0x34E7A899, 0x4E8A61DC, 0x5D28F9AB, 0x69CF5132, 0x7A6DC945,
0x9D14C3B8, 0x8EB65BCF, 0xBA51F356, 0xA9F36B21, 0xD39EA264, 0xC03C3A13, 0xF4DB928A, 0xE7790AFD,
0x3FC5F181, 0x2C6769F6, 0x1880C16F, 0x0B225918, 0x714F905D, 0x62ED082A, 0x560AA0B3, 0x45A838C4,
0xA2D13239, 0xB173AA4E, 0x859402D7, 0x96369AA0, 0xEC5B53E5, 0xFFF9CB92, 0xCB1E630B, 0xD8BCFB7C,
0x7F8BE302, 0x6C297B75, 0x58CED3EC, 0x4B6C4B9B, 0x310182DE, 0x22A31AA9, 0x1644B230, 0x05E62A47,
0xE29F20BA, 0xF13DB8CD, 0xC5DA1054, 0xD6788823, 0xAC154166, 0xBFB7D911, 0x8B507188, 0x98F2E9FF,
0x404E1283, 0x53EC8AF4, 0x670B226D, 0x74A9BA1A, 0x0EC4735F, 0x1D66EB28, 0x298143B1, 0x3A23DBC6,
0xDD5AD13B, 0xCEF8494C, 0xFA1FE1D5, 0xE9BD79A2, 0x93D0B0E7, 0x80722890, 0xB4958009, 0xA737187E,
0xFF17C604, 0xECB55E73, 0xD852F6EA, 0xCBF06E9D, 0xB19DA7D8, 0xA23F3FAF, 0x96D89736, 0x857A0F41,
0x620305BC, 0x71A19DCB, 0x45463552, 0x56E4AD25, 0x2C896460, 0x3F2BFC17, 0x0BCC548E, 0x186ECCF9,
0xC0D23785, 0xD370AFF2, 0xE797076B, 0xF4359F1C, 0x8E585659, 0x9DFACE2E, 0xA91D66B7, 0xBABFFEC0,
0x5DC6F43D, 0x4E646C4A, 0x7A83C4D3, 0x69215CA4, 0x134C95E1, 0x00EE0D96, 0x3409A50F, 0x27AB3D78,
0x809C2506, 0x933EBD71, 0xA7D915E8, 0xB47B8D9F, 0xCE1644DA, 0xDDB4DCAD, 0xE9537434, 0xFAF1EC43,
0x1D88E6BE, 0x0E2A7EC9, 0x3ACDD650, 0x296F4E27, 0x53028762, 0x40A01F15, 0x7447B78C, 0x67E52FFB,
0xBF59D487, 0xACFB4CF0, 0x981CE469, 0x8BBE7C1E, 0xF1D3B55B, 0xE2712D2C, 0xD69685B5, 0xC5341DC2,
0x224D173F, 0x31EF8F48, 0x050827D1, 0x16AABFA6, 0x6CC776E3, 0x7F65EE94, 0x4B82460D, 0x5820DE7A,
0xFBC3FAF9, 0xE861628E, 0xDC86CA17, 0xCF245260, 0xB5499B25, 0xA6EB0352, 0x920CABCB, 0x81AE33BC,
0x66D73941, 0x7575A136, 0x419209AF, 0x523091D8, 0x285D589D, 0x3BFFC0EA, 0x0F186873, 0x1CBAF004,
0xC4060B78, 0xD7A4930F, 0xE3433B96, 0xF0E1A3E1, 0x8A8C6AA4, 0x992EF2D3, 0xADC95A4A, 0xBE6BC23D,
0x5912C8C0, 0x4AB050B7, 0x7E57F82E, 0x6DF56059, 0x1798A91C, 0x043A316B, 0x30DD99F2, 0x237F0185,
0x844819FB, 0x97EA818C, 0xA30D2915, 0xB0AFB162, 0xCAC27827, 0xD960E050, 0xED8748C9, 0xFE25D0BE,
0x195CDA43, 0x0AFE4234, 0x3E19EAAD, 0x2DBB72DA, 0x57D6BB9F, 0x447423E8, 0x70938B71, 0x63311306,
0xBB8DE87A, 0xA82F700D, 0x9CC8D894, 0x8F6A40E3, 0xF50789A6, 0xE6A511D1, 0xD242B948, 0xC1E0213F,
0x26992BC2, 0x353BB3B5, 0x01DC1B2C, 0x127E835B, 0x68134A1E, 0x7BB1D269, 0x4F567AF0, 0x5CF4E287,
0x04D43CFD, 0x1776A48A, 0x23910C13, 0x30339464, 0x4A5E5D21, 0x59FCC556, 0x6D1B6DCF, 0x7EB9F5B8,
0x99C0FF45, 0x8A626732, 0xBE85CFAB, 0xAD2757DC, 0xD74A9E99, 0xC4E806EE, 0xF00FAE77, 0xE3AD3600,
0x3B11CD7C, 0x28B3550B, 0x1C54FD92, 0x0FF665E5, 0x759BACA0, 0x663934D7, 0x52DE9C4E, 0x417C0439,
0xA6050EC4, 0xB5A796B3, 0x81403E2A, 0x92E2A65D, 0xE88F6F18, 0xFB2DF76F, 0xCFCA5FF6, 0xDC68C781,
0x7B5FDFFF, 0x68FD4788, 0x5C1AEF11, 0x4FB87766, 0x35D5BE23, 0x26772654, 0x12908ECD, 0x013216BA,
0xE64B1C47, 0xF5E98430, 0xC10E2CA9, 0xD2ACB4DE, 0xA8C17D9B, 0xBB63E5EC, 0x8F844D75, 0x9C26D502,
0x449A2E7E, 0x5738B609, 0x63DF1E90, 0x707D86E7, 0x0A104FA2, 0x19B2D7D5, 0x2D557F4C, 0x3EF7E73B,
0xD98EEDC6, 0xCA2C75B1, 0xFECBDD28, 0xED69455F, 0x97048C1A, 0x84A6146D, 0xB041BCF4, 0xA3E32483
};
static const uint32_t crc_tableil8_o48[256] =
{
0x00000000, 0xA541927E, 0x4F6F520D, 0xEA2EC073, 0x9EDEA41A, 0x3B9F3664, 0xD1B1F617, 0x74F06469,
0x38513EC5, 0x9D10ACBB, 0x773E6CC8, 0xD27FFEB6, 0xA68F9ADF, 0x03CE08A1, 0xE9E0C8D2, 0x4CA15AAC,
0x70A27D8A, 0xD5E3EFF4, 0x3FCD2F87, 0x9A8CBDF9, 0xEE7CD990, 0x4B3D4BEE, 0xA1138B9D, 0x045219E3,
0x48F3434F, 0xEDB2D131, 0x079C1142, 0xA2DD833C, 0xD62DE755, 0x736C752B, 0x9942B558, 0x3C032726,
0xE144FB14, 0x4405696A, 0xAE2BA919, 0x0B6A3B67, 0x7F9A5F0E, 0xDADBCD70, 0x30F50D03, 0x95B49F7D,
0xD915C5D1, 0x7C5457AF, 0x967A97DC, 0x333B05A2, 0x47CB61CB, 0xE28AF3B5, 0x08A433C6, 0xADE5A1B8,
0x91E6869E, 0x34A714E0, 0xDE89D493, 0x7BC846ED, 0x0F382284, 0xAA79B0FA, 0x40577089, 0xE516E2F7,
0xA9B7B85B, 0x0CF62A25, 0xE6D8EA56, 0x43997828, 0x37691C41, 0x92288E3F, 0x78064E4C, 0xDD47DC32,
0xC76580D9, 0x622412A7, 0x880AD2D4, 0x2D4B40AA, 0x59BB24C3, 0xFCFAB6BD, 0x16D476CE, 0xB395E4B0,
0xFF34BE1C, 0x5A752C62, 0xB05BEC11, 0x151A7E6F, 0x61EA1A06, 0xC4AB8878, 0x2E85480B, 0x8BC4DA75,
0xB7C7FD53, 0x12866F2D, 0xF8A8AF5E, 0x5DE93D20, 0x29195949, 0x8C58CB37, 0x66760B44, 0xC337993A,
0x8F96C396, 0x2AD751E8, 0xC0F9919B, 0x65B803E5, 0x1148678C, 0xB409F5F2, 0x5E273581, 0xFB66A7FF,
0x26217BCD, 0x8360E9B3, 0x694E29C0, 0xCC0FBBBE, 0xB8FFDFD7, 0x1DBE4DA9, 0xF7908DDA, 0x52D11FA4,
0x1E704508, 0xBB31D776, 0x511F1705, 0xF45E857B, 0x80AEE112, 0x25EF736C, 0xCFC1B31F, 0x6A802161,
0x56830647, 0xF3C29439, 0x19EC544A, 0xBCADC634, 0xC85DA25D, 0x6D1C3023, 0x8732F050, 0x2273622E,
0x6ED23882, 0xCB93AAFC, 0x21BD6A8F, 0x84FCF8F1, 0xF00C9C98, 0x554D0EE6, 0xBF63CE95, 0x1A225CEB,
0x8B277743, 0x2E66E53D, 0xC448254E, 0x6109B730, 0x15F9D359, 0xB0B84127, 0x5A968154, 0xFFD7132A,
0xB3764986, 0x1637DBF8, 0xFC191B8B, 0x595889F5, 0x2DA8ED9C, 0x88E97FE2, 0x62C7BF91, 0xC7862DEF,
0xFB850AC9, 0x5EC498B7, 0xB4EA58C4, 0x11ABCABA, 0x655BAED3, 0xC01A3CAD, 0x2A34FCDE, 0x8F756EA0,
0xC3D4340C, 0x6695A672, 0x8CBB6601, 0x29FAF47F, 0x5D0A9016, 0xF84B0268, 0x1265C21B, 0xB7245065,
0x6A638C57, 0xCF221E29, 0x250CDE5A, 0x804D4C24, 0xF4BD284D, 0x51FCBA33, 0xBBD27A40, 0x1E93E83E,
0x5232B292, 0xF77320EC, 0x1D5DE09F, 0xB81C72E1, 0xCCEC1688, 0x69AD84F6, 0x83834485, 0x26C2D6FB,
0x1AC1F1DD, 0xBF8063A3, 0x55AEA3D0, 0xF0EF31AE, 0x841F55C7, 0x215EC7B9, 0xCB7007CA, 0x6E3195B4,
0x2290CF18, 0x87D15D66, 0x6DFF9D15, 0xC8BE0F6B, 0xBC4E6B02, 0x190FF97C, 0xF321390F, 0x5660AB71,
0x4C42F79A, 0xE90365E4, 0x032DA597, 0xA66C37E9, 0xD29C5380, 0x77DDC1FE, 0x9DF3018D, 0x38B293F3,
0x7413C95F, 0xD1525B21, 0x3B7C9B52, 0x9E3D092C, 0xEACD6D45, 0x4F8CFF3B, 0xA5A23F48, 0x00E3AD36,
0x3CE08A10, 0x99A1186E, 0x738FD81D, 0xD6CE4A63, 0xA23E2E0A, 0x077FBC74, 0xED517C07, 0x4810EE79,
0x04B1B4D5, 0xA1F026AB, 0x4BDEE6D8, 0xEE9F74A6, 0x9A6F10CF, 0x3F2E82B1, 0xD50042C2, 0x7041D0BC,
0xAD060C8E, 0x08479EF0, 0xE2695E83, 0x4728CCFD, 0x33D8A894, 0x96993AEA, 0x7CB7FA99, 0xD9F668E7,
0x9557324B, 0x3016A035, 0xDA386046, 0x7F79F238, 0x0B899651, 0xAEC8042F, 0x44E6C45C, 0xE1A75622,
0xDDA47104, 0x78E5E37A, 0x92CB2309, 0x378AB177, 0x437AD51E, 0xE63B4760, 0x0C158713, 0xA954156D,
0xE5F54FC1, 0x40B4DDBF, 0xAA9A1DCC, 0x0FDB8FB2, 0x7B2BEBDB, 0xDE6A79A5, 0x3444B9D6, 0x91052BA8
};
static const uint32_t crc_tableil8_o56[256] =
{
0x00000000, 0xDD45AAB8, 0xBF672381, 0x62228939, 0x7B2231F3, 0xA6679B4B, 0xC4451272, 0x1900B8CA,
0xF64463E6, 0x2B01C95E, 0x49234067, 0x9466EADF, 0x8D665215, 0x5023F8AD, 0x32017194, 0xEF44DB2C,
0xE964B13D, 0x34211B85, 0x560392BC, 0x8B463804, 0x924680CE, 0x4F032A76, 0x2D21A34F, 0xF06409F7,
0x1F20D2DB, 0xC2657863, 0xA047F15A, 0x7D025BE2, 0x6402E328, 0xB9474990, 0xDB65C0A9, 0x06206A11,
0xD725148B, 0x0A60BE33, 0x6842370A, 0xB5079DB2, 0xAC072578, 0x71428FC0, 0x136006F9, 0xCE25AC41,
0x2161776D, 0xFC24DDD5, 0x9E0654EC, 0x4343FE54, 0x5A43469E, 0x8706EC26, 0xE524651F, 0x3861CFA7,
0x3E41A5B6, 0xE3040F0E, 0x81268637, 0x5C632C8F, 0x45639445, 0x98263EFD, 0xFA04B7C4, 0x27411D7C,
0xC805C650, 0x15406CE8, 0x7762E5D1, 0xAA274F69, 0xB327F7A3, 0x6E625D1B, 0x0C40D422, 0xD1057E9A,
0xABA65FE7, 0x76E3F55F, 0x14C17C66, 0xC984D6DE, 0xD0846E14, 0x0DC1C4AC, 0x6FE34D95, 0xB2A6E72D,
0x5DE23C01, 0x80A796B9, 0xE2851F80, 0x3FC0B538, 0x26C00DF2, 0xFB85A74A, 0x99A72E73, 0x44E284CB,
0x42C2EEDA, 0x9F874462, 0xFDA5CD5B, 0x20E067E3, 0x39E0DF29, 0xE4A57591, 0x8687FCA8, 0x5BC25610,
0xB4868D3C, 0x69C32784, 0x0BE1AEBD, 0xD6A40405, 0xCFA4BCCF, 0x12E11677, 0x70C39F4E, 0xAD8635F6,
0x7C834B6C, 0xA1C6E1D4, 0xC3E468ED, 0x1EA1C255, 0x07A17A9F, 0xDAE4D027, 0xB8C6591E, 0x6583F3A6,
0x8AC7288A, 0x57828232, 0x35A00B0B, 0xE8E5A1B3, 0xF1E51979, 0x2CA0B3C1, 0x4E823AF8, 0x93C79040,
0x95E7FA51, 0x48A250E9, 0x2A80D9D0, 0xF7C57368, 0xEEC5CBA2, 0x3380611A, 0x51A2E823, 0x8CE7429B,
0x63A399B7, 0xBEE6330F, 0xDCC4BA36, 0x0181108E, 0x1881A844, 0xC5C402FC, 0xA7E68BC5, 0x7AA3217D,
0x52A0C93F, 0x8FE56387, 0xEDC7EABE, 0x30824006, 0x2982F8CC, 0xF4C75274, 0x96E5DB4D, 0x4BA071F5,
0xA4E4AAD9, 0x79A10061, 0x1B838958, 0xC6C623E0, 0xDFC69B2A, 0x02833192, 0x60A1B8AB, 0xBDE41213,
0xBBC47802, 0x6681D2BA, 0x04A35B83, 0xD9E6F13B, 0xC0E649F1, 0x1DA3E349, 0x7F816A70, 0xA2C4C0C8,
0x4D801BE4, 0x90C5B15C, 0xF2E73865, 0x2FA292DD, 0x36A22A17, 0xEBE780AF, 0x89C50996, 0x5480A32E,
0x8585DDB4, 0x58C0770C, 0x3AE2FE35, 0xE7A7548D, 0xFEA7EC47, 0x23E246FF, 0x41C0CFC6, 0x9C85657E,
0x73C1BE52, 0xAE8414EA, 0xCCA69DD3, 0x11E3376B, 0x08E38FA1, 0xD5A62519, 0xB784AC20, 0x6AC10698,
0x6CE16C89, 0xB1A4C631, 0xD3864F08, 0x0EC3E5B0, 0x17C35D7A, 0xCA86F7C2, 0xA8A47EFB, 0x75E1D443,
0x9AA50F6F, 0x47E0A5D7, 0x25C22CEE, 0xF8878656, 0xE1873E9C, 0x3CC29424, 0x5EE01D1D, 0x83A5B7A5,
0xF90696D8, 0x24433C60, 0x4661B559, 0x9B241FE1, 0x8224A72B, 0x5F610D93, 0x3D4384AA, 0xE0062E12,
0x0F42F53E, 0xD2075F86, 0xB025D6BF, 0x6D607C07, 0x7460C4CD, 0xA9256E75, 0xCB07E74C, 0x16424DF4,
0x106227E5, 0xCD278D5D, 0xAF050464, 0x7240AEDC, 0x6B401616, 0xB605BCAE, 0xD4273597, 0x09629F2F,
0xE6264403, 0x3B63EEBB, 0x59416782, 0x8404CD3A, 0x9D0475F0, 0x4041DF48, 0x22635671, 0xFF26FCC9,
0x2E238253, 0xF36628EB, 0x9144A1D2, 0x4C010B6A, 0x5501B3A0, 0x88441918, 0xEA669021, 0x37233A99,
0xD867E1B5, 0x05224B0D, 0x6700C234, 0xBA45688C, 0xA345D046, 0x7E007AFE, 0x1C22F3C7, 0xC167597F,
0xC747336E, 0x1A0299D6, 0x782010EF, 0xA565BA57, 0xBC65029D, 0x6120A825, 0x0302211C, 0xDE478BA4,
0x31035088, 0xEC46FA30, 0x8E647309, 0x5321D9B1, 0x4A21617B, 0x9764CBC3, 0xF54642FA, 0x2803E842
};
static const uint32_t crc_tableil8_o64[256] =
{
0x00000000, 0x38116FAC, 0x7022DF58, 0x4833B0F4, 0xE045BEB0, 0xD854D11C, 0x906761E8, 0xA8760E44,
0xC5670B91, 0xFD76643D, 0xB545D4C9, 0x8D54BB65, 0x2522B521, 0x1D33DA8D, 0x55006A79, 0x6D1105D5,
0x8F2261D3, 0xB7330E7F, 0xFF00BE8B, 0xC711D127, 0x6F67DF63, 0x5776B0CF, 0x1F45003B, 0x27546F97,
0x4A456A42, 0x725405EE, 0x3A67B51A, 0x0276DAB6, 0xAA00D4F2, 0x9211BB5E, 0xDA220BAA, 0xE2336406,
0x1BA8B557, 0x23B9DAFB, 0x6B8A6A0F, 0x539B05A3, 0xFBED0BE7, 0xC3FC644B, 0x8BCFD4BF, 0xB3DEBB13,
0xDECFBEC6, 0xE6DED16A, 0xAEED619E, 0x96FC0E32, 0x3E8A0076, 0x069B6FDA, 0x4EA8DF2E, 0x76B9B082,
0x948AD484, 0xAC9BBB28, 0xE4A80BDC, 0xDCB96470, 0x74CF6A34, 0x4CDE0598, 0x04EDB56C, 0x3CFCDAC0,
0x51EDDF15, 0x69FCB0B9, 0x21CF004D, 0x19DE6FE1, 0xB1A861A5, 0x89B90E09, 0xC18ABEFD, 0xF99BD151,
0x37516AAE, 0x0F400502, 0x4773B5F6, 0x7F62DA5A, 0xD714D41E, 0xEF05BBB2, 0xA7360B46, 0x9F2764EA,
0xF236613F, 0xCA270E93, 0x8214BE67, 0xBA05D1CB, 0x1273DF8F, 0x2A62B023, 0x625100D7, 0x5A406F7B,
0xB8730B7D, 0x806264D1, 0xC851D425, 0xF040BB89, 0x5836B5CD, 0x6027DA61, 0x28146A95, 0x10050539,
0x7D1400EC, 0x45056F40, 0x0D36DFB4, 0x3527B018, 0x9D51BE5C, 0xA540D1F0, 0xED736104, 0xD5620EA8,
0x2CF9DFF9, 0x14E8B055, 0x5CDB00A1, 0x64CA6F0D, 0xCCBC6149, 0xF4AD0EE5, 0xBC9EBE11, 0x848FD1BD,
0xE99ED468, 0xD18FBBC4, 0x99BC0B30, 0xA1AD649C, 0x09DB6AD8, 0x31CA0574, 0x79F9B580, 0x41E8DA2C,
0xA3DBBE2A, 0x9BCAD186, 0xD3F96172, 0xEBE80EDE, 0x439E009A, 0x7B8F6F36, 0x33BCDFC2, 0x0BADB06E,
0x66BCB5BB, 0x5EADDA17, 0x169E6AE3, 0x2E8F054F, 0x86F90B0B, 0xBEE864A7, 0xF6DBD453, 0xCECABBFF,
0x6EA2D55C, 0x56B3BAF0, 0x1E800A04, 0x269165A8, 0x8EE76BEC, 0xB6F60440, 0xFEC5B4B4, 0xC6D4DB18,
0xABC5DECD, 0x93D4B161, 0xDBE70195, 0xE3F66E39, 0x4B80607D, 0x73910FD1, 0x3BA2BF25, 0x03B3D089,
0xE180B48F, 0xD991DB23, 0x91A26BD7, 0xA9B3047B, 0x01C50A3F, 0x39D46593, 0x71E7D567, 0x49F6BACB,
0x24E7BF1E, 0x1CF6D0B2, 0x54C56046, 0x6CD40FEA, 0xC4A201AE, 0xFCB36E02, 0xB480DEF6, 0x8C91B15A,
0x750A600B, 0x4D1B0FA7, 0x0528BF53, 0x3D39D0FF, 0x954FDEBB, 0xAD5EB117, 0xE56D01E3, 0xDD7C6E4F,
0xB06D6B9A, 0x887C0436, 0xC04FB4C2, 0xF85EDB6E, 0x5028D52A, 0x6839BA86, 0x200A0A72, 0x181B65DE,
0xFA2801D8, 0xC2396E74, 0x8A0ADE80, 0xB21BB12C, 0x1A6DBF68, 0x227CD0C4, 0x6A4F6030, 0x525E0F9C,
0x3F4F0A49, 0x075E65E5, 0x4F6DD511, 0x777CBABD, 0xDF0AB4F9, 0xE71BDB55, 0xAF286BA1, 0x9739040D,
0x59F3BFF2, 0x61E2D05E, 0x29D160AA, 0x11C00F06, 0xB9B60142, 0x81A76EEE, 0xC994DE1A, 0xF185B1B6,
0x9C94B463, 0xA485DBCF, 0xECB66B3B, 0xD4A70497, 0x7CD10AD3, 0x44C0657F, 0x0CF3D58B, 0x34E2BA27,
0xD6D1DE21, 0xEEC0B18D, 0xA6F30179, 0x9EE26ED5, 0x36946091, 0x0E850F3D, 0x46B6BFC9, 0x7EA7D065,
0x13B6D5B0, 0x2BA7BA1C, 0x63940AE8, 0x5B856544, 0xF3F36B00, 0xCBE204AC, 0x83D1B458, 0xBBC0DBF4,
0x425B0AA5, 0x7A4A6509, 0x3279D5FD, 0x0A68BA51, 0xA21EB415, 0x9A0FDBB9, 0xD23C6B4D, 0xEA2D04E1,
0x873C0134, 0xBF2D6E98, 0xF71EDE6C, 0xCF0FB1C0, 0x6779BF84, 0x5F68D028, 0x175B60DC, 0x2F4A0F70,
0xCD796B76, 0xF56804DA, 0xBD5BB42E, 0x854ADB82, 0x2D3CD5C6, 0x152DBA6A, 0x5D1E0A9E, 0x650F6532,
0x081E60E7, 0x300F0F4B, 0x783CBFBF, 0x402DD013, 0xE85BDE57, 0xD04AB1FB, 0x9879010F, 0xA0686EA3
};
static const uint32_t crc_tableil8_o72[256] =
{
0x00000000, 0xEF306B19, 0xDB8CA0C3, 0x34BCCBDA, 0xB2F53777, 0x5DC55C6E, 0x697997B4, 0x8649FCAD,
0x6006181F, 0x8F367306, 0xBB8AB8DC, 0x54BAD3C5, 0xD2F32F68, 0x3DC34471, 0x097F8FAB, 0xE64FE4B2,
0xC00C303E, 0x2F3C5B27, 0x1B8090FD, 0xF4B0FBE4, 0x72F90749, 0x9DC96C50, 0xA975A78A, 0x4645CC93,
0xA00A2821, 0x4F3A4338, 0x7B8688E2, 0x94B6E3FB, 0x12FF1F56, 0xFDCF744F, 0xC973BF95, 0x2643D48C,
0x85F4168D, 0x6AC47D94, 0x5E78B64E, 0xB148DD57, 0x370121FA, 0xD8314AE3, 0xEC8D8139, 0x03BDEA20,
0xE5F20E92, 0x0AC2658B, 0x3E7EAE51, 0xD14EC548, 0x570739E5, 0xB83752FC, 0x8C8B9926, 0x63BBF23F,
0x45F826B3, 0xAAC84DAA, 0x9E748670, 0x7144ED69, 0xF70D11C4, 0x183D7ADD, 0x2C81B107, 0xC3B1DA1E,
0x25FE3EAC, 0xCACE55B5, 0xFE729E6F, 0x1142F576, 0x970B09DB, 0x783B62C2, 0x4C87A918, 0xA3B7C201,
0x0E045BEB, 0xE13430F2, 0xD588FB28, 0x3AB89031, 0xBCF16C9C, 0x53C10785, 0x677DCC5F, 0x884DA746,
0x6E0243F4, 0x813228ED, 0xB58EE337, 0x5ABE882E, 0xDCF77483, 0x33C71F9A, 0x077BD440, 0xE84BBF59,
0xCE086BD5, 0x213800CC, 0x1584CB16, 0xFAB4A00F, 0x7CFD5CA2, 0x93CD37BB, 0xA771FC61, 0x48419778,
0xAE0E73CA, 0x413E18D3, 0x7582D309, 0x9AB2B810, 0x1CFB44BD, 0xF3CB2FA4, 0xC777E47E, 0x28478F67,
0x8BF04D66, 0x64C0267F, 0x507CEDA5, 0xBF4C86BC, 0x39057A11, 0xD6351108, 0xE289DAD2, 0x0DB9B1CB,
0xEBF65579, 0x04C63E60, 0x307AF5BA, 0xDF4A9EA3, 0x5903620E, 0xB6330917, 0x828FC2CD, 0x6DBFA9D4,
0x4BFC7D58, 0xA4CC1641, 0x9070DD9B, 0x7F40B682, 0xF9094A2F, 0x16392136, 0x2285EAEC, 0xCDB581F5,
0x2BFA6547, 0xC4CA0E5E, 0xF076C584, 0x1F46AE9D, 0x990F5230, 0x763F3929, 0x4283F2F3, 0xADB399EA,
0x1C08B7D6, 0xF338DCCF, 0xC7841715, 0x28B47C0C, 0xAEFD80A1, 0x41CDEBB8, 0x75712062, 0x9A414B7B,
0x7C0EAFC9, 0x933EC4D0, 0xA7820F0A, 0x48B26413, 0xCEFB98BE, 0x21CBF3A7, 0x1577387D, 0xFA475364,
0xDC0487E8, 0x3334ECF1, 0x0788272B, 0xE8B84C32, 0x6EF1B09F, 0x81C1DB86, 0xB57D105C, 0x5A4D7B45,
0xBC029FF7, 0x5332F4EE, 0x678E3F34, 0x88BE542D, 0x0EF7A880, 0xE1C7C399, 0xD57B0843, 0x3A4B635A,
0x99FCA15B, 0x76CCCA42, 0x42700198, 0xAD406A81, 0x2B09962C, 0xC439FD35, 0xF08536EF, 0x1FB55DF6,
0xF9FAB944, 0x16CAD25D, 0x22761987, 0xCD46729E, 0x4B0F8E33, 0xA43FE52A, 0x90832EF0, 0x7FB345E9,
0x59F09165, 0xB6C0FA7C, 0x827C31A6, 0x6D4C5ABF, 0xEB05A612, 0x0435CD0B, 0x308906D1, 0xDFB96DC8,
0x39F6897A, 0xD6C6E263, 0xE27A29B9, 0x0D4A42A0, 0x8B03BE0D, 0x6433D514, 0x508F1ECE, 0xBFBF75D7,
0x120CEC3D, 0xFD3C8724, 0xC9804CFE, 0x26B027E7, 0xA0F9DB4A, 0x4FC9B053, 0x7B757B89, 0x94451090,
0x720AF422, 0x9D3A9F3B, 0xA98654E1, 0x46B63FF8, 0xC0FFC355, 0x2FCFA84C, 0x1B736396, 0xF443088F,
0xD200DC03, 0x3D30B71A, 0x098C7CC0, 0xE6BC17D9, 0x60F5EB74, 0x8FC5806D, 0xBB794BB7, 0x544920AE,
0xB206C41C, 0x5D36AF05, 0x698A64DF, 0x86BA0FC6, 0x00F3F36B, 0xEFC39872, 0xDB7F53A8, 0x344F38B1,
0x97F8FAB0, 0x78C891A9, 0x4C745A73, 0xA344316A, 0x250DCDC7, 0xCA3DA6DE, 0xFE816D04, 0x11B1061D,
0xF7FEE2AF, 0x18CE89B6, 0x2C72426C, 0xC3422975, 0x450BD5D8, 0xAA3BBEC1, 0x9E87751B, 0x71B71E02,
0x57F4CA8E, 0xB8C4A197, 0x8C786A4D, 0x63480154, 0xE501FDF9, 0x0A3196E0, 0x3E8D5D3A, 0xD1BD3623,
0x37F2D291, 0xD8C2B988, 0xEC7E7252, 0x034E194B, 0x8507E5E6, 0x6A378EFF, 0x5E8B4525, 0xB1BB2E3C
};
static const uint32_t crc_tableil8_o80[256] =
{
0x00000000, 0x68032CC8, 0xD0065990, 0xB8057558, 0xA5E0C5D1, 0xCDE3E919, 0x75E69C41, 0x1DE5B089,
0x4E2DFD53, 0x262ED19B, 0x9E2BA4C3, 0xF628880B, 0xEBCD3882, 0x83CE144A, 0x3BCB6112, 0x53C84DDA,
0x9C5BFAA6, 0xF458D66E, 0x4C5DA336, 0x245E8FFE, 0x39BB3F77, 0x51B813BF, 0xE9BD66E7, 0x81BE4A2F,
0xD27607F5, 0xBA752B3D, 0x02705E65, 0x6A7372AD, 0x7796C224, 0x1F95EEEC, 0xA7909BB4, 0xCF93B77C,
0x3D5B83BD, 0x5558AF75, 0xED5DDA2D, 0x855EF6E5, 0x98BB466C, 0xF0B86AA4, 0x48BD1FFC, 0x20BE3334,
0x73767EEE, 0x1B755226, 0xA370277E, 0xCB730BB6, 0xD696BB3F, 0xBE9597F7, 0x0690E2AF, 0x6E93CE67,
0xA100791B, 0xC90355D3, 0x7106208B, 0x19050C43, 0x04E0BCCA, 0x6CE39002, 0xD4E6E55A, 0xBCE5C992,
0xEF2D8448, 0x872EA880, 0x3F2BDDD8, 0x5728F110, 0x4ACD4199, 0x22CE6D51, 0x9ACB1809, 0xF2C834C1,
0x7AB7077A, 0x12B42BB2, 0xAAB15EEA, 0xC2B27222, 0xDF57C2AB, 0xB754EE63, 0x0F519B3B, 0x6752B7F3,
0x349AFA29, 0x5C99D6E1, 0xE49CA3B9, 0x8C9F8F71, 0x917A3FF8, 0xF9791330, 0x417C6668, 0x297F4AA0,
0xE6ECFDDC, 0x8EEFD114, 0x36EAA44C, 0x5EE98884, 0x430C380D, 0x2B0F14C5, 0x930A619D, 0xFB094D55,
0xA8C1008F, 0xC0C22C47, 0x78C7591F, 0x10C475D7, 0x0D21C55E, 0x6522E996, 0xDD279CCE, 0xB524B006,
0x47EC84C7, 0x2FEFA80F, 0x97EADD57, 0xFFE9F19F, 0xE20C4116, 0x8A0F6DDE, 0x320A1886, 0x5A09344E,
0x09C17994, 0x61C2555C, 0xD9C72004, 0xB1C40CCC, 0xAC21BC45, 0xC422908D, 0x7C27E5D5, 0x1424C91D,
0xDBB77E61, 0xB3B452A9, 0x0BB127F1, 0x63B20B39, 0x7E57BBB0, 0x16549778, 0xAE51E220, 0xC652CEE8,
0x959A8332, 0xFD99AFFA, 0x459CDAA2, 0x2D9FF66A, 0x307A46E3, 0x58796A2B, 0xE07C1F73, 0x887F33BB,
0xF56E0EF4, 0x9D6D223C, 0x25685764, 0x4D6B7BAC, 0x508ECB25, 0x388DE7ED, 0x808892B5, 0xE88BBE7D,
0xBB43F3A7, 0xD340DF6F, 0x6B45AA37, 0x034686FF, 0x1EA33676, 0x76A01ABE, 0xCEA56FE6, 0xA6A6432E,
0x6935F452, 0x0136D89A, 0xB933ADC2, 0xD130810A, 0xCCD53183, 0xA4D61D4B, 0x1CD36813, 0x74D044DB,
0x27180901, 0x4F1B25C9, 0xF71E5091, 0x9F1D7C59, 0x82F8CCD0, 0xEAFBE018, 0x52FE9540, 0x3AFDB988,
0xC8358D49, 0xA036A181, 0x1833D4D9, 0x7030F811, 0x6DD54898, 0x05D66450, 0xBDD31108, 0xD5D03DC0,
0x8618701A, 0xEE1B5CD2, 0x561E298A, 0x3E1D0542, 0x23F8B5CB, 0x4BFB9903, 0xF3FEEC5B, 0x9BFDC093,
0x546E77EF, 0x3C6D5B27, 0x84682E7F, 0xEC6B02B7, 0xF18EB23E, 0x998D9EF6, 0x2188EBAE, 0x498BC766,
0x1A438ABC, 0x7240A674, 0xCA45D32C, 0xA246FFE4, 0xBFA34F6D, 0xD7A063A5, 0x6FA516FD, 0x07A63A35,
0x8FD9098E, 0xE7DA2546, 0x5FDF501E, 0x37DC7CD6, 0x2A39CC5F, 0x423AE097, 0xFA3F95CF, 0x923CB907,
0xC1F4F4DD, 0xA9F7D815, 0x11F2AD4D, 0x79F18185, 0x6414310C, 0x0C171DC4, 0xB412689C, 0xDC114454,
0x1382F328, 0x7B81DFE0, 0xC384AAB8, 0xAB878670, 0xB66236F9, 0xDE611A31, 0x66646F69, 0x0E6743A1,
0x5DAF0E7B, 0x35AC22B3, 0x8DA957EB, 0xE5AA7B23, 0xF84FCBAA, 0x904CE762, 0x2849923A, 0x404ABEF2,
0xB2828A33, 0xDA81A6FB, 0x6284D3A3, 0x0A87FF6B, 0x17624FE2, 0x7F61632A, 0xC7641672, 0xAF673ABA,
0xFCAF7760, 0x94AC5BA8, 0x2CA92EF0, 0x44AA0238, 0x594FB2B1, 0x314C9E79, 0x8949EB21, 0xE14AC7E9,
0x2ED97095, 0x46DA5C5D, 0xFEDF2905, 0x96DC05CD, 0x8B39B544, 0xE33A998C, 0x5B3FECD4, 0x333CC01C,
0x60F48DC6, 0x08F7A10E, 0xB0F2D456, 0xD8F1F89E, 0xC5144817, 0xAD1764DF, 0x15121187, 0x7D113D4F
};
static const uint32_t crc_tableil8_o88[256] =
{
0x00000000, 0x493C7D27, 0x9278FA4E, 0xDB448769, 0x211D826D, 0x6821FF4A, 0xB3657823, 0xFA590504,
0x423B04DA, 0x0B0779FD, 0xD043FE94, 0x997F83B3, 0x632686B7, 0x2A1AFB90, 0xF15E7CF9, 0xB86201DE,
0x847609B4, 0xCD4A7493, 0x160EF3FA, 0x5F328EDD, 0xA56B8BD9, 0xEC57F6FE, 0x37137197, 0x7E2F0CB0,
0xC64D0D6E, 0x8F717049, 0x5435F720, 0x1D098A07, 0xE7508F03, 0xAE6CF224, 0x7528754D, 0x3C14086A,
0x0D006599, 0x443C18BE, 0x9F789FD7, 0xD644E2F0, 0x2C1DE7F4, 0x65219AD3, 0xBE651DBA, 0xF759609D,
0x4F3B6143, 0x06071C64, 0xDD439B0D, 0x947FE62A, 0x6E26E32E, 0x271A9E09, 0xFC5E1960, 0xB5626447,
0x89766C2D, 0xC04A110A, 0x1B0E9663, 0x5232EB44, 0xA86BEE40, 0xE1579367, 0x3A13140E, 0x732F6929,
0xCB4D68F7, 0x827115D0, 0x593592B9, 0x1009EF9E, 0xEA50EA9A, 0xA36C97BD, 0x782810D4, 0x31146DF3,
0x1A00CB32, 0x533CB615, 0x8878317C, 0xC1444C5B, 0x3B1D495F, 0x72213478, 0xA965B311, 0xE059CE36,
0x583BCFE8, 0x1107B2CF, 0xCA4335A6, 0x837F4881, 0x79264D85, 0x301A30A2, 0xEB5EB7CB, 0xA262CAEC,
0x9E76C286, 0xD74ABFA1, 0x0C0E38C8, 0x453245EF, 0xBF6B40EB, 0xF6573DCC, 0x2D13BAA5, 0x642FC782,
0xDC4DC65C, 0x9571BB7B, 0x4E353C12, 0x07094135, 0xFD504431, 0xB46C3916, 0x6F28BE7F, 0x2614C358,
0x1700AEAB, 0x5E3CD38C, 0x857854E5, 0xCC4429C2, 0x361D2CC6, 0x7F2151E1, 0xA465D688, 0xED59ABAF,
0x553BAA71, 0x1C07D756, 0xC743503F, 0x8E7F2D18, 0x7426281C, 0x3D1A553B, 0xE65ED252, 0xAF62AF75,
0x9376A71F, 0xDA4ADA38, 0x010E5D51, 0x48322076, 0xB26B2572, 0xFB575855, 0x2013DF3C, 0x692FA21B,
0xD14DA3C5, 0x9871DEE2, 0x4335598B, 0x0A0924AC, 0xF05021A8, 0xB96C5C8F, 0x6228DBE6, 0x2B14A6C1,
0x34019664, 0x7D3DEB43, 0xA6796C2A, 0xEF45110D, 0x151C1409, 0x5C20692E, 0x8764EE47, 0xCE589360,
0x763A92BE, 0x3F06EF99, 0xE44268F0, 0xAD7E15D7, 0x572710D3, 0x1E1B6DF4, 0xC55FEA9D, 0x8C6397BA,
0xB0779FD0, 0xF94BE2F7, 0x220F659E, 0x6B3318B9, 0x916A1DBD, 0xD856609A, 0x0312E7F3, 0x4A2E9AD4,
0xF24C9B0A, 0xBB70E62D, 0x60346144, 0x29081C63, 0xD3511967, 0x9A6D6440, 0x4129E329, 0x08159E0E,
0x3901F3FD, 0x703D8EDA, 0xAB7909B3, 0xE2457494, 0x181C7190, 0x51200CB7, 0x8A648BDE, 0xC358F6F9,
0x7B3AF727, 0x32068A00, 0xE9420D69, 0xA07E704E, 0x5A27754A, 0x131B086D, 0xC85F8F04, 0x8163F223,
0xBD77FA49, 0xF44B876E, 0x2F0F0007, 0x66337D20, 0x9C6A7824, 0xD5560503, 0x0E12826A, 0x472EFF4D,
0xFF4CFE93, 0xB67083B4, 0x6D3404DD, 0x240879FA, 0xDE517CFE, 0x976D01D9, 0x4C2986B0, 0x0515FB97,
0x2E015D56, 0x673D2071, 0xBC79A718, 0xF545DA3F, 0x0F1CDF3B, 0x4620A21C, 0x9D642575, 0xD4585852,
0x6C3A598C, 0x250624AB, 0xFE42A3C2, 0xB77EDEE5, 0x4D27DBE1, 0x041BA6C6, 0xDF5F21AF, 0x96635C88,
0xAA7754E2, 0xE34B29C5, 0x380FAEAC, 0x7133D38B, 0x8B6AD68F, 0xC256ABA8, 0x19122CC1, 0x502E51E6,
0xE84C5038, 0xA1702D1F, 0x7A34AA76, 0x3308D751, 0xC951D255, 0x806DAF72, 0x5B29281B, 0x1215553C,
0x230138CF, 0x6A3D45E8, 0xB179C281, 0xF845BFA6, 0x021CBAA2, 0x4B20C785, 0x906440EC, 0xD9583DCB,
0x613A3C15, 0x28064132, 0xF342C65B, 0xBA7EBB7C, 0x4027BE78, 0x091BC35F, 0xD25F4436, 0x9B633911,
0xA777317B, 0xEE4B4C5C, 0x350FCB35, 0x7C33B612, 0x866AB316, 0xCF56CE31, 0x14124958, 0x5D2E347F,
0xE54C35A1, 0xAC704886, 0x7734CFEF, 0x3E08B2C8, 0xC451B7CC, 0x8D6DCAEB, 0x56294D82, 0x1F1530A5
};
uint32_t sp_crc32c(uint32_t crc, const void *buf, int len)
{
const char *p_buf = (const char*)buf;
int initial_bytes = (sizeof(int32_t) - (intptr_t)p_buf) & (sizeof(int32_t) - 1);
if (len < initial_bytes)
initial_bytes = len;
int li;
for (li = 0; li < initial_bytes; li++)
crc = crc_tableil8_o32[(crc ^ *p_buf++) & 0x000000FF] ^ (crc >> 8);
len -= initial_bytes;
int running_len = len & ~(sizeof(uint64_t) - 1);
int end_bytes = len - running_len;
for (li = 0; li < running_len / 8; li++) {
crc ^= *(uint32_t*)p_buf;
p_buf += 4;
uint32_t term1 = crc_tableil8_o88[(crc) & 0x000000FF] ^
crc_tableil8_o80[(crc >> 8) & 0x000000FF];
uint32_t term2 = crc >> 16;
crc = term1 ^
crc_tableil8_o72[term2 & 0x000000FF] ^
crc_tableil8_o64[(term2 >> 8) & 0x000000FF];
term1 = crc_tableil8_o56[(*(uint32_t*)p_buf) & 0x000000FF] ^
crc_tableil8_o48[((*(uint32_t*)p_buf) >> 8) & 0x000000FF];
term2 = (*(uint32_t*)p_buf) >> 16;
crc = crc ^ term1 ^
crc_tableil8_o40[term2 & 0x000000FF] ^
crc_tableil8_o32[(term2 >> 8) & 0x000000FF];
p_buf += 4;
}
for (li = 0; li < end_bytes; li++)
crc = crc_tableil8_o32[(crc ^ *p_buf++) & 0x000000FF] ^ (crc >> 8);
return crc;
}

14
src/sophia/db/crc.h Normal file
View File

@@ -0,0 +1,14 @@
#ifndef SP_CRC_H_
#define SP_CRC_H_
/*
* sophia database
* sphia.org
*
* Copyright (c) Dmitry Simonenko
* BSD License
*/
uint32_t sp_crc32c(uint32_t, const void*, int);
#endif

551
src/sophia/db/cursor.c Normal file
View File

@@ -0,0 +1,551 @@
/*
* sophia database
* sphia.org
*
* Copyright (c) Dmitry Simonenko
* BSD License
*/
#include "sp.h"
static inline void
sp_pageopen(spc *c, uint32_t page)
{
assert(page < c->s->s.count);
sppage *p = c->s->s.i[page];
spepoch *e = p->epoch;
c->pi = page;
c->p = p;
c->ph = (sppageh*)(e->db.map + p->offset);
/* validate header */
assert(c->ph->id > 0);
c->pvi = 0 ;
c->pv = (spvh*)((char*)c->ph + sizeof(sppageh));
c->dup = 0;
}
static inline void sp_pageclose(spc *c) {
c->p = NULL;
c->ph = NULL;
c->pv = NULL;
}
static inline void
sp_pagesetlast(spc *c)
{
c->pvi = c->ph->count - 1;
c->pv = (spvh*)((char*)c->ph + sizeof(sppageh) + c->ph->bsize * c->pvi);
}
static inline int
sp_pageseek(spc *c, char *rkey, int size, int *minp, int *midp, int *maxp)
{
int min = 0;
int max = c->ph->count - 1;
int mid = 0;
while (max >= min)
{
mid = min + ((max - min) >> 1);
spvh *a = (spvh*)((char*)c->ph + sizeof(sppageh) + c->ph->bsize * mid);
register int rc =
c->s->e->cmp(a->key, a->size, rkey, size, c->s->e->cmparg);
switch (rc) {
case -1: min = mid + 1;
continue;
case 1: max = mid - 1;
continue;
default:
*minp = min;
*midp = mid;
*maxp = max;
return 1;
}
}
*minp = min;
*midp = mid;
*maxp = max;
return 0;
}
static inline int
sp_pagesetlte(spc *c, char *rkey, int size)
{
int mid, min, max;
int match;
int eq = 0;
if (sp_pageseek(c, rkey, size, &min, &mid, &max)) {
eq = 1;
match = mid;
} else {
match = min;
}
/* not found, set minimal */
if (match >= c->ph->count) {
c->pv = (spvh*)((char*)c->ph + sizeof(sppageh));
c->pvi = 0;
} else {
c->pv = (spvh*)((char*)c->ph + sizeof(sppageh) + c->ph->bsize * match);
c->pvi = match;
}
return eq;
}
static inline int
sp_pagesetgte(spc *c, char *rkey, int size)
{
int mid, min, max;
int match;
int eq = 0;
if (sp_pageseek(c, rkey, size, &min, &mid, &max)) {
eq = 1;
match = mid;
} else {
match = max;
}
/* not found, set max */
if (match >= c->ph->count)
match = c->ph->count - 1;
c->pv = (spvh*)((char*)c->ph + sizeof(sppageh) + c->ph->bsize * match);
c->pvi = match;
return eq;
}
static inline void
sp_pagenext(spc *c)
{
c->pvi++;
c->pv = (spvh*)((char*)c->pv + c->ph->bsize);
if (spunlikely(c->pvi == c->ph->count)) {
sp_pageclose(c);
uint32_t next_page = c->pi + 1;
if (splikely(next_page < c->s->s.count))
sp_pageopen(c, next_page);
}
}
static inline void
sp_pageprev(spc *c)
{
c->pvi--;
c->pv = (spvh*)((char*)c->pv - c->ph->bsize);
if (spunlikely(c->pvi < 0)) {
sp_pageclose(c);
int prev_page = c->pi - 1;
if (splikely(prev_page >= 0)) {
sp_pageopen(c, prev_page);
sp_pagesetlast(c);
}
}
}
static inline void sp_first(spc *c)
{
sp_iopen(&c->i0, c->s->i);
if (! c->s->iskip)
sp_iopen(&c->i1, sp_ipair(c->s));
if (spunlikely(c->s->s.count == 0))
return;
sp_pageopen(c, 0);
}
static inline int
sp_firstkey(spc *c, char *rkey, int size)
{
/* do lte search on all indexes for the key */
int eq = sp_ilte(c->s->i, &c->i0, rkey, size);
if (! c->s->iskip)
eq = eq + sp_ilte(sp_ipair(c->s), &c->i1, rkey, size);
/* match page for the key */
if (spunlikely(c->s->s.count == 0))
return eq;
/* open page and do lte search of key */
uint32_t idx = 0;
sppage *p spunused;
p = sp_catroute(&c->s->s, rkey, size, &idx);
assert(p != NULL);
sp_pageopen(c, idx);
eq = eq + sp_pagesetlte(c, rkey, size);
return eq;
}
static inline void sp_last(spc *c)
{
sp_iopen(&c->i0, c->s->i);
sp_ilast(&c->i0);
if (! c->s->iskip) {
sp_iopen(&c->i1, sp_ipair(c->s));
sp_ilast(&c->i1);
}
if (spunlikely(c->s->s.count == 0))
return;
sp_pageopen(c, c->s->s.count-1);
sp_pagesetlast(c);
}
static inline int sp_lastkey(spc *c, char *rkey, int size)
{
/* do gte search the index for the key */
int eq = sp_igte(c->s->i, &c->i0, rkey, size);
if (! c->s->iskip)
eq = eq + sp_igte(sp_ipair(c->s), &c->i1, rkey, size);
/* match page for the key */
if (spunlikely(c->s->s.count == 0))
return eq;
/* open page and do gte search of key */
uint32_t idx = 0;
sppage *p spunused;
p = sp_catroute(&c->s->s, rkey, size, &idx);
assert(p != NULL);
sp_pageopen(c, idx);
eq = eq + sp_pagesetgte(c, rkey, size);
return eq;
}
void sp_cursoropen(spc *c, sp *s, sporder o, char *rkey, int size)
{
/* lock all */
sp_glock(s);
c->m = SPMCUR;
c->o = o;
c->s = s;
c->dup = 0;
c->p = NULL;
c->ph = NULL;
c->pi = 0;
c->pvi = 0;
c->pv = NULL;
c->vsrc = SPCNONE;
sp_iinv(c->s->i, &c->i0);
sp_iinv(sp_ipair(c->s), &c->i1);
c->r.type = SPREFNONE;
switch (o) {
case SPGTE:
case SPGT:
if (rkey) {
/* init first key and skip on gt */
if (sp_firstkey(c, rkey, size) && o == SPGT)
sp_iterate(c);
return;
}
sp_first(c);
break;
case SPLTE:
case SPLT:
if (rkey) {
/* init last key and skip on lt */
if (sp_lastkey(c, rkey, size) && o == SPLT)
sp_iterate(c);
return;
}
sp_last(c);
break;
}
}
void sp_cursorclose(spc *c)
{
/* unlock all */
sp_gunlock(c->s);
}
static inline void sp_reset(spc *c) {
c->vsrc = SPCNONE;
c->r.type = SPREFNONE;
c->dup = 0;
}
static inline int sp_next(spc *c)
{
switch (c->vsrc) {
case SPCI0:
sp_inext(&c->i0);
if (c->dup & SPCPDUP)
sp_pagenext(c);
if (c->dup & SPCVDUP)
sp_inext(&c->i1);
break;
case SPCI1:
sp_inext(&c->i1);
if (c->dup & SPCPDUP)
sp_pagenext(c);
if (c->dup & SPCVDUP)
sp_inext(&c->i0);
break;
case SPCP:
assert(c->p != NULL);
assert(c->r.v.vh == c->pv);
sp_pagenext(c);
break;
case SPCNONE:
break;
}
sp_reset(c);
int rc;
spcsrc src = SPCNONE;
spv *v0 = sp_ival(&c->i0);
spv *v1 = sp_ival(&c->i1);
spv *v = NULL;
/* END */
if (v0 == NULL && v1 == NULL && c->pv == NULL) {
c->vsrc = SPCNONE;
return 0;
}
if (v0 && v1) {
rc = c->s->e->cmp(v0->key, v0->size, v1->key, v1->size,
c->s->e->cmparg);
switch (rc) {
case 0: c->dup |= SPCVDUP;
case -1: v = v0, src = SPCI0;
break;
case 1: v = v1, src = SPCI1;
break;
}
} else if (v0) {
v = v0, src = SPCI0;
} else if (v1) {
v = v1, src = SPCI1;
}
/* no page key */
if (c->pv == NULL) {
c->vsrc = src;
c->r.type = SPREFM;
c->r.v.v = v;
return 1;
}
/* no index key */
if (v == NULL) {
c->vsrc = SPCP;
c->r.type = SPREFD;
c->r.v.vh = c->pv;
return 1;
}
/* compare in-memory key and page one's */
rc = c->s->e->cmp(v->key, v->size, c->pv->key, c->pv->size,
c->s->e->cmparg);
switch (rc) {
case 0: c->dup |= SPCPDUP;
case -1:
c->vsrc = src;
c->r.type = SPREFM;
c->r.v.v = v;
break;
case 1:
c->vsrc = SPCP;
c->r.type = SPREFD;
c->r.v.vh = c->pv;
break;
}
return 1;
}
static inline int sp_prev(spc *c)
{
switch (c->vsrc) {
case SPCI0:
sp_iprev(&c->i0);
if (c->dup & SPCPDUP)
sp_pageprev(c);
if (c->dup & SPCVDUP)
sp_iprev(&c->i1);
break;
case SPCI1:
sp_iprev(&c->i1);
if (c->dup & SPCPDUP)
sp_pageprev(c);
if (c->dup & SPCVDUP)
sp_iprev(&c->i0);
break;
case SPCP:
assert(c->p != NULL);
assert(c->r.v.vh == c->pv);
sp_pageprev(c);
break;
case SPCNONE:
break;
}
sp_reset(c);
int rc;
spcsrc src = SPCNONE;
spv *v0 = sp_ival(&c->i0);
spv *v1 = sp_ival(&c->i1);
spv *v = NULL;
/* END */
if (v0 == NULL && v1 == NULL && c->pv == NULL) {
c->vsrc = SPCNONE;
return 0;
}
if (v0 && v1) {
rc = c->s->e->cmp(v0->key, v0->size, v1->key, v1->size,
c->s->e->cmparg);
switch (rc) {
case 0: c->dup |= SPCVDUP;
case -1: v = v1, src = SPCI1;
break;
case 1: v = v0, src = SPCI0;
break;
}
} else if (v0) {
v = v0, src = SPCI0;
} else if (v1) {
v = v1, src = SPCI1;
}
/* no page key */
if (c->pv == NULL) {
c->vsrc = src;
c->r.type = SPREFM;
c->r.v.v = v;
return 1;
}
/* no index key */
if (v == NULL) {
c->vsrc = SPCP;
c->r.type = SPREFD;
c->r.v.vh = c->pv;
return 1;
}
/* compare in-memory key and page one's */
rc = c->s->e->cmp(v->key, v->size, c->pv->key, c->pv->size,
c->s->e->cmparg);
switch (rc) {
case 0: c->dup |= SPCPDUP;
case 1:
c->vsrc = src;
c->r.type = SPREFM;
c->r.v.v = v;
break;
case -1:
c->vsrc = SPCP;
c->r.type = SPREFD;
c->r.v.vh = c->pv;
break;
}
return 1;
}
int sp_iterate(spc *c) {
int rc = 0;
do {
switch (c->o) {
case SPGTE:
case SPGT: rc = sp_next(c);
break;
case SPLTE:
case SPLT: rc = sp_prev(c);
break;
}
} while (rc > 0 && sp_refisdel(&c->r));
return rc;
}
static inline int
sp_matchi(sp *s, spi *i, void *key, size_t size, void **v, size_t *vsize)
{
spv *a = sp_igetraw(i, key, size);
if (splikely(a)) {
if (a->flags & SPDEL)
return 0;
if (v) {
*vsize = sp_vvsize(a);
*v = NULL;
if (*vsize > 0) {
*v = sp_memdup(s, sp_vv(a), sp_vvsize(a));
if (spunlikely(*v == NULL))
return sp_e(s, SPEOOM, "failed to allocate key");
}
}
return 1;
}
return 0;
}
int sp_match(sp *s, void *k, size_t ksize, void **v, size_t *vsize)
{
/* I. match both in-memory index'es for the key */
sp_lock(&s->locki);
int rc = sp_matchi(s, s->i, k, ksize, v, vsize);
if (rc == -1 || rc == 1) {
sp_unlock(&s->locki);
return rc;
}
/*
* skip the second index if it is been truncating at the
* somement, all updates are on disk pages.
*/
if (! s->iskip) {
int rc = sp_matchi(s, sp_ipair(s), k, ksize, v, vsize);
if (rc == -1 || rc == 1) {
sp_unlock(&s->locki);
return rc;
}
}
sp_unlock(&s->locki);
/* II. match the page */
sp_lock(&s->locks);
uint32_t page = 0;
sppage *p = sp_catfind(&s->s, k, ksize, &page);
if (p == NULL) {
sp_unlock(&s->locks);
return 0;
}
/* III. match the key in the page */
spepoch *e = (spepoch*)p->epoch;
sp_lock(&e->lock);
sppageh *ph = (sppageh*)(e->db.map + p->offset);
rc = 0;
register uint32_t min = 0;
register uint32_t max = ph->count - 1;
spvh *match = NULL;
while (max >= min) {
register uint32_t mid = min + ((max - min) >> 1);
register spvh *v =
(spvh*)(e->db.map + p->offset + sizeof(sppageh) +
ph->bsize * mid);
switch (s->e->cmp(v->key, v->size, k, ksize, s->e->cmparg)) {
case -1: min = mid + 1;
continue;
case 1: max = mid - 1;
continue;
default:
match = v;
goto done;
}
}
done:
if (match == NULL)
goto ret;
if (v) {
*vsize = match->vsize;
*v = NULL;
if (match->vsize > 0) {
*v = sp_memdup(s, e->db.map + p->offset + match->voffset, match->vsize);
if (spunlikely(*v == NULL)) {
sp_e(s, SPEOOM, "failed to allocate key");
rc = -1;
goto ret;
}
}
}
rc = 1;
ret:
sp_unlock(&e->lock);
sp_unlock(&s->locks);
return rc;
}

47
src/sophia/db/cursor.h Normal file
View File

@@ -0,0 +1,47 @@
#ifndef SP_CURSOR_H_
#define SP_CURSOR_H_
/*
* sophia database
* sphia.org
*
* Copyright (c) Dmitry Simonenko
* BSD License
*/
typedef struct spc spc;
enum spcsrc {
SPCNONE,
SPCI0,
SPCI1,
SPCP
};
#define SPCVDUP 1
#define SPCPDUP 2
typedef enum spcsrc spcsrc;
struct spc {
spmagic m;
sporder o;
sp *s;
spii i0, i1;
int dup; /* last iteration duplicate flags */
sppageh *ph;
sppage *p;
int pi; /* page space index */
spvh *pv;
int pvi; /* version page index */
spcsrc vsrc; /* last iteration source */
spref r; /* last iteration result */
};
void sp_cursoropen(spc*, sp*, sporder, char*, int);
void sp_cursorclose(spc*);
int sp_iterate(spc*);
int sp_match(sp*, void*, size_t, void**, size_t*);
#endif

49
src/sophia/db/e.c Normal file
View File

@@ -0,0 +1,49 @@
/*
* sophia database
* sphia.org
*
* Copyright (c) Dmitry Simonenko
* BSD License
*/
#include "sp.h"
void sp_ve(spe *e, int type, va_list args)
{
sp_lock(&e->lock);
if (e->type != SPENONE) {
sp_unlock(&e->lock);
return;
}
e->type = type;
switch (type) {
case SPE: {
char *fmt = va_arg(args, char*);
int len = snprintf(e->e, sizeof(e->e), "error: ");
vsnprintf(e->e + len, sizeof(e->e) - len, fmt, args);
break;
}
case SPEOOM: {
char *msg = va_arg(args, char*);
snprintf(e->e, sizeof(e->e), "out-of-memory error: %s", msg);
break;
}
case SPESYS: {
e->errno_ = errno;
char *msg = va_arg(args, char*);
snprintf(e->e, sizeof(e->e), "system error: %s (errno: %d, %s)",
msg, e->errno_, strerror(e->errno_));
break;
}
case SPEIO: {
e->errno_ = errno;
char *msg = va_arg(args, char*);
uint32_t epoch = va_arg(args, uint32_t);
snprintf(e->e, sizeof(e->e), "io error: [epoch %"PRIu32"] %s (errno: %d, %s)",
epoch, msg, e->errno_, strerror(e->errno_));
break;
}
}
sp_unlock(&e->lock);
}

49
src/sophia/db/e.h Normal file
View File

@@ -0,0 +1,49 @@
#ifndef SP_E_H_
#define SP_E_H_
/*
* sophia database
* sphia.org
*
* Copyright (c) Dmitry Simonenko
* BSD License
*/
typedef struct spe spe;
enum {
SPENONE,
SPE, /* general error (with format) */
SPEOOM, /* out of memory */
SPESYS, /* system + errno */
SPEIO /* system-io + errno */
};
struct spe {
spspinlock lock;
int type;
int errno_;
char e[256];
};
static inline void sp_einit(spe *e) {
e->lock = 0;
e->type = SPENONE;
e->e[0] = 0;
sp_lockinit(&e->lock);
}
static inline void sp_efree(spe *e) {
sp_lockfree(&e->lock);
}
static inline int sp_eis(spe *e) {
sp_lock(&e->lock);
register int is = e->type != SPENONE;
sp_unlock(&e->lock);
return is;
}
void sp_ve(spe *e, int type, va_list args);
#endif

355
src/sophia/db/file.c Normal file
View File

@@ -0,0 +1,355 @@
/*
* sophia database
* sphia.org
*
* Copyright (c) Dmitry Simonenko
* BSD License
*/
#include "sp.h"
#include <sys/mman.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
int sp_fileexists(char *path) {
struct stat st;
int rc = lstat(path, &st);
return rc == 0;
}
int sp_filerm(char *path) {
return unlink(path);
}
static inline ssize_t
sp_mapsizeof(char *path) {
struct stat st;
int rc = lstat(path, &st);
if (spunlikely(rc == -1))
return -1;
return st.st_size;
}
static inline int
sp_mapresize(spfile *f, size_t size) {
int rc = ftruncate(f->fd, size);
if (spunlikely(rc == -1))
return -1;
f->size = size;
return 0;
}
static inline int
sp_map(spfile *f, int flags) {
void *p = mmap(NULL, f->size, flags, MAP_SHARED, f->fd, 0);
if (p == MAP_FAILED)
return -1;
f->map = p;
return 0;
}
static inline int
sp_unmap(spfile *f) {
int rc;
if (f->map) {
rc = munmap(f->map, f->size);
f->map = NULL;
return rc;
}
return 0;
}
static inline int
sp_mapopenof(spfile *f, char *path, int flags, uint64_t size)
{
f->fd = open(path, flags, 0600);
if (spunlikely(f->fd == -1))
return -1;
f->file = sp_strdup(f->a, path);
if (spunlikely(f->file == NULL)) {
close(f->fd);
f->fd = -1;
return -1;
}
f->used = 0;
f->creat = (flags & O_CREAT ? 1 : 0);
int rc;
if (! f->creat) {
ssize_t sz = sp_mapsizeof(path);
if (spunlikely(sz == -1))
goto err;
f->size = sz;
rc = sp_map(f, PROT_READ);
if (spunlikely(rc == -1))
goto err;
return 0;
}
f->size = 0;
rc = sp_mapresize(f, size);
if (spunlikely(rc == -1))
goto err;
rc = sp_map(f, PROT_READ|PROT_WRITE);
if (spunlikely(rc == -1))
goto err;
return 0;
err:
close(f->fd);
f->fd = -1;
sp_free(f->a, f->file);
f->file = NULL;
return -1;
}
int sp_mapopen(spfile *f, char *path) {
return sp_mapopenof(f, path, O_RDONLY, 0);
}
int sp_mapnew(spfile *f, char *path, uint64_t size) {
return sp_mapopenof(f, path, O_RDWR|O_CREAT, size);
}
static inline int
sp_mapsync(spfile *f) {
return msync(f->map, f->size, MS_SYNC);
}
int sp_mapunlink(spfile *f) {
return sp_filerm(f->file);
}
static inline int
sp_mapcut(spfile *f)
{
if (f->creat == 0)
return 0;
int rc = sp_mapsync(f);
if (spunlikely(rc == -1))
return -1;
rc = sp_unmap(f);
if (spunlikely(rc == -1))
return -1;
rc = sp_mapresize(f, f->used);
if (spunlikely(rc == -1))
return -1;
return 0;
}
static inline int
sp_fileclose(spfile *f)
{
/* leave file incomplete */
if (splikely(f->file)) {
sp_free(f->a, f->file);
f->file = NULL;
}
int rc;
if (spunlikely(f->fd != -1)) {
rc = close(f->fd);
if (spunlikely(rc == -1))
return -1;
f->fd = -1;
}
return 0;
}
static inline int
sp_filecomplete(spfile *f)
{
if (f->creat == 0)
return 0;
/* remove .incomplete part */
f->creat = 0;
char path[1024];
snprintf(path, sizeof(path), "%s", f->file);
char *ext = strrchr(path, '.');
assert(ext != NULL);
*ext = 0;
int rc = rename(f->file, path);
if (spunlikely(rc == -1))
return -1;
char *p = sp_strdup(f->a, path);
if (spunlikely(p == NULL))
return -1;
sp_free(f->a, f->file);
f->file = p;
return 0;
}
int sp_mapunmap(spfile *f) {
return sp_unmap(f);
}
int sp_mapclose(spfile *f)
{
int rc = sp_mapcut(f);
if (spunlikely(rc == -1))
return -1;
if (f->map) {
rc = sp_unmap(f);
if (spunlikely(rc == -1))
return -1;
}
return sp_fileclose(f);
}
int sp_mapcomplete(spfile *f)
{
if (f->creat == 0)
return 0;
/* sync and truncate file by used size */
int rc = sp_mapcut(f);
if (spunlikely(rc == -1))
return -1;
/* remove .incomplete part */
rc = sp_filecomplete(f);
if (spunlikely(rc == -1))
return -1;
return sp_map(f, PROT_READ);
}
int sp_mapensure(spfile *f, uint64_t size, float grow)
{
if (splikely((f->used + size) < f->size))
return 0;
int rc = sp_unmap(f);
if (spunlikely(rc == -1))
return -1;
long double nsz = f->size * grow;
if (spunlikely(nsz < size))
nsz = size;
rc = sp_mapresize(f, nsz);
if (spunlikely(rc == -1))
return -1;
return sp_map(f, PROT_READ|PROT_WRITE);
}
static inline int
sp_logopenof(spfile *f, char *path, int flags)
{
f->creat = 1;
f->fd = open(path, flags, 0600);
if (spunlikely(f->fd == -1))
return -1;
f->file = sp_strdup(f->a, path);
if (spunlikely(f->file == NULL)) {
close(f->fd);
f->fd = -1;
return -1;
}
f->size = 0;
f->used = 0;
return 0;
}
int sp_lognew(spfile *f, char *dir, uint32_t epoch)
{
char path[1024];
snprintf(path, sizeof(path), "%s/%"PRIu32".log.incomplete",
dir, epoch);
int rc = sp_logopenof(f, path, O_WRONLY|O_APPEND|O_CREAT);
if (spunlikely(rc == -1))
return -1;
// posix_fadvise(seq)
return 0;
}
int sp_logcontinue(spfile *f, char *dir, uint32_t epoch) {
char path[1024];
snprintf(path, sizeof(path), "%s/%"PRIu32".log.incomplete",
dir, epoch);
int rc = sp_logopenof(f, path, O_WRONLY|O_APPEND);
if (spunlikely(rc == -1))
return -1;
return 0;
}
int sp_logclose(spfile *f) {
return sp_fileclose(f);
}
static inline int
sp_logsync(spfile *f) {
return (f->creat ? fsync(f->fd) : 0);
}
int sp_logcomplete(spfile *f)
{
int rc = sp_logsync(f);
if (spunlikely(rc == -1))
return -1;
return sp_filecomplete(f);
}
int sp_logcompleteforce(spfile *f) {
int rc = sp_logsync(f);
if (spunlikely(rc == -1))
return -1;
int oldcreat = f->creat;
f->creat = 1;
rc = sp_filecomplete(f);
f->creat = oldcreat;
return rc;
}
int sp_logunlink(spfile *f) {
return sp_filerm(f->file);
}
int sp_logflush(spfile *f)
{
register struct iovec *v = f->iov;
register uint64_t size = 0;
register int n = f->iovc;
do {
int r;
do {
r = writev(f->fd, v, n);
} while (r == -1 && errno == EINTR);
if (r < 0) {
f->iovc = 0;
return -1;
}
size += r;
while (n > 0) {
if (v->iov_len > (size_t)r) {
v->iov_base = (char*)v->iov_base + r;
v->iov_len -= r;
break;
} else {
r -= v->iov_len;
v++;
n--;
}
}
} while (n > 0);
f->used += size;
f->iovc = 0;
return 0;
}
int sp_logrlb(spfile *f)
{
assert(f->iovc == 0);
int rc = ftruncate(f->fd, f->svp);
if (spunlikely(rc == -1))
return -1;
f->used = f->svp;
f->svp = 0;
return lseek(f->fd, f->used, SEEK_SET);
}
int sp_logeof(spfile *f)
{
sp_filesvp(f);
speofh eof = { SPEOF };
sp_logadd(f, (char*)&eof, sizeof(eof));
int rc = sp_logflush(f);
if (spunlikely(rc == -1)) {
sp_logrlb(f);
return -1;
}
return 0;
}

106
src/sophia/db/file.h Normal file
View File

@@ -0,0 +1,106 @@
#ifndef SP_FILE_H_
#define SP_FILE_H_
/*
* sophia database
* sphia.org
*
* Copyright (c) Dmitry Simonenko
* BSD License
*/
typedef struct spfile spfile;
struct spfile {
spa *a;
int creat;
uint64_t used;
uint64_t size;
uint64_t svp;
char *file;
int fd;
char *map;
struct iovec iov[8];
int iovc;
};
int sp_fileexists(char*);
int sp_filerm(char*);
static inline void
sp_fileinit(spfile *f, spa *a) {
memset(f, 0, sizeof(*f));
f->a = a;
f->fd = -1;
}
static inline void
sp_filesvp(spfile *f) {
f->svp = f->used;
}
int sp_mapopen(spfile*, char*);
int sp_mapnew(spfile*, char*, uint64_t);
int sp_mapclose(spfile*);
int sp_mapcomplete(spfile*);
int sp_mapunmap(spfile*);
int sp_mapunlink(spfile*);
int sp_mapensure(spfile*, uint64_t, float);
static inline int
sp_mapepoch(spfile *f, char *dir, uint32_t epoch, char *ext) {
char path[1024];
snprintf(path, sizeof(path), "%s/%"PRIu32".%s", dir, epoch, ext);
return sp_mapopen(f, path);
}
static inline int
sp_mapepochnew(spfile *f, uint64_t size,
char *dir, uint32_t epoch, char *ext) {
char path[1024];
snprintf(path, sizeof(path), "%s/%"PRIu32".%s.incomplete", dir, epoch, ext);
return sp_mapnew(f, path, size);
}
static inline void
sp_mapuse(spfile *f, size_t size) {
f->used += size;
assert(f->used <= f->size);
}
static inline void
sp_maprlb(spfile *f) {
f->used = f->svp;
}
static inline int
sp_mapinbound(spfile *f, size_t off) {
return off <= f->size;
}
int sp_lognew(spfile*, char*, uint32_t);
int sp_logcontinue(spfile*, char*, uint32_t);
int sp_logclose(spfile*);
int sp_logcomplete(spfile*);
int sp_logcompleteforce(spfile*);
int sp_logunlink(spfile*);
int sp_logflush(spfile*);
int sp_logrlb(spfile*);
int sp_logeof(spfile*);
static inline void
sp_logadd(spfile *f, const void *buf, int size) {
assert(f->iovc < 8);
f->iov[f->iovc].iov_base = (void*)buf;
f->iov[f->iovc].iov_len = size;
f->iovc++;
}
static inline int
sp_epochrm(char *dir, uint32_t epoch, char *ext) {
char path[1024];
snprintf(path, sizeof(path), "%s/%"PRIu32".%s", dir, epoch, ext);
return sp_filerm(path);
}
#endif

71
src/sophia/db/gc.c Normal file
View File

@@ -0,0 +1,71 @@
/*
* sophia database
* sphia.org
*
* Copyright (c) Dmitry Simonenko
* BSD License
*/
#include "sp.h"
int sp_gc(sp *s, spepoch *x)
{
/*
* copy all yet active pages from a epoch's
* databases picked for the garbage
* collecting.
*/
for (;;)
{
sp_lock(&s->lockr);
spepoch *g = sp_repgc(&s->rep, s->e->gcfactor);
sp_unlock(&s->lockr);
if (g == NULL)
break;
int rc;
splist *i, *n;
sp_listforeach_safe(&g->pages, i, n) {
sppage *p = spcast(i, sppage, link);
/* map origin page and copy to db file */
sppageh *h = (sppageh*)(g->db.map + p->offset);
sp_lock(&x->lock);
rc = sp_mapensure(&x->db, sizeof(sppageh) + h->size, s->e->dbgrow);
if (spunlikely(rc == -1)) {
sp_unlock(&x->lock);
return sp_e(s, SPEIO, "failed to remap db file", x->epoch);
}
sp_unlock(&x->lock);
memcpy(x->db.map + x->db.used, h, sizeof(sppageh) + h->size);
/* update page location */
sp_lock(&s->locks);
sp_listunlink(&p->link);
sp_listappend(&x->pages, &p->link);
p->epoch = x;
p->offset = x->db.used;
sp_unlock(&s->locks);
/* advance file pointer */
sp_mapuse(&x->db, sizeof(sppageh) + h->size);
}
/*
* remove old files and unlink the epoch
* from the repository.
*/
rc = sp_mapunlink(&g->db);
if (spunlikely(rc == -1))
return sp_e(s, SPEIO, "failed to unlink db file", g->epoch);
rc = sp_mapclose(&g->db);
if (spunlikely(rc == -1))
return sp_e(s, SPEIO, "failed to close db file", g->epoch);
sp_lock(&s->lockr);
sp_repdetach(&s->rep, g);
sp_free(&s->a, g);
sp_unlock(&s->lockr);
}
return 0;
}

14
src/sophia/db/gc.h Normal file
View File

@@ -0,0 +1,14 @@
#ifndef SP_GC_H_
#define SP_GC_H_
/*
* sophia database
* sphia.org
*
* Copyright (c) Dmitry Simonenko
* BSD License
*/
int sp_gc(sp*, spepoch*);
#endif

368
src/sophia/db/i.c Normal file
View File

@@ -0,0 +1,368 @@
/*
* sophia database
* sphia.org
*
* Copyright (c) Dmitry Simonenko
* BSD License
*/
#include "sp.h"
static inline int sp_iensure(spi *i) {
if (splikely((i->icount + 1) < i->itop))
return 0;
i->itop *= 2;
i->i = sp_realloc(i->a, i->i, i->itop * sizeof(spipage*));
if (spunlikely(i->i == NULL))
return -1;
return 0;
}
static inline int
sp_ipagesize(spi *i) {
return sizeof(spipage) + sizeof(spv*) * i->pagesize;
}
static inline spipage*
sp_ipagealloc(spi *i) {
spipage *p = sp_malloc(i->a, sp_ipagesize(i));
if (spunlikely(p == NULL))
return NULL;
p->count = 0;
return p;
}
int sp_iinit(spi *i, spa *a, int pagesize, spcmpf cmp, void *cmparg)
{
i->a = a;
i->cmp = cmp;
i->cmparg = cmparg;
i->count = 0;
i->pagesize = pagesize;
/* start from 4 pages vector */
i->itop = 2;
i->icount = 1;
i->i = NULL;
int rc = sp_iensure(i);
if (spunlikely(rc == -1))
return -1;
/* allocate first page */
i->i[0] = sp_ipagealloc(i);
if (spunlikely(i->i[0] == NULL)) {
sp_free(i->a, i->i);
i->i = NULL;
return -1;
}
return 0;
}
void sp_ifree(spi *i)
{
uint32_t p = 0;
while (p < i->icount) {
uint32_t k = 0;
while (k < i->i[p]->count) {
sp_free(i->a, i->i[p]->i[k]);
k++;
}
sp_free(i->a, i->i[p]);
p++;
}
sp_free(i->a, i->i);
i->i = NULL;
}
int sp_itruncate(spi *i)
{
sp_ifree(i);
return sp_iinit(i, i->a, i->pagesize, i->cmp, i->cmparg);
}
static inline void*
sp_iminof(spi *i, spipage *p, char *rkey, int size, uint32_t *idx)
{
register int min = 0;
register int max = p->count - 1;
while (max >= min) {
register int mid = min + ((max - min) >> 1);
register int rc =
i->cmp(p->i[mid]->key,
p->i[mid]->size, rkey, size, i->cmparg);
switch (rc) {
case -1: min = mid + 1;
continue;
case 1: max = mid - 1;
continue;
default: *idx = mid;
return p->i[mid];
}
}
*idx = min;
return NULL;
}
static inline void*
sp_imaxof(spi *i, spipage *p, char *rkey, int size, uint32_t *idx)
{
register int min = 0;
register int max = p->count - 1;
while (max >= min) {
register int mid = min + ((max - min) >> 1);
register int rc =
i->cmp(p->i[mid]->key,
p->i[mid]->size,
rkey, size, i->cmparg);
switch (rc) {
case -1: min = mid + 1;
continue;
case 1: max = mid - 1;
continue;
default: *idx = mid;
return p->i[mid];
}
}
*idx = max;
return NULL;
}
static inline int
sp_ipagecmp(spi *i, spipage *p, char *rkey, int size)
{
if (spunlikely(p->count == 0))
return 0;
register int l =
i->cmp(p->i[0]->key, p->i[0]->size, rkey, size, i->cmparg);
register int r =
i->cmp(p->i[p->count-1]->key, p->i[p->count-1]->size,
rkey, size, i->cmparg);
/* inside page range */
if (l <= 0 && r >= 0)
return 0;
/* page min < key */
if (l == -1)
return -1;
/* page max > key */
assert(r == 1);
return 1;
}
static inline spipage*
sp_ipageof(spi *i, char *rkey, int size, uint32_t *idx)
{
register int min = 0;
register int max = i->icount - 1;
while (max >= min) {
register int mid = min + ((max - min) >> 1);
switch (sp_ipagecmp(i, i->i[mid], rkey, size)) {
case -1: min = mid + 1;
continue;
case 1: max = mid - 1;
continue;
default:
*idx = mid;
return i->i[mid];
}
}
*idx = min;
return NULL;
}
int sp_isetorget(spi *i, spv *v, spii *old)
{
/* 1. do binary search on the vector and find usable
* page for a key */
spipage *p = i->i[0];
uint32_t a = 0;
if (splikely(i->icount > 1)) {
p = sp_ipageof(i, v->key, v->size, &a);
if (spunlikely(p == NULL)) {
if (a >= i->icount)
a = i->icount-1;
p = i->i[a];
assert(a < i->icount);
}
assert(p != NULL);
}
/* 2. if page is full, split it and insert new one:
* copy second half of the keys from first page to second */
/* 2.1. insert page to vector, by moving pointers forward */
if (spunlikely(p->count == i->pagesize)) {
int rc = sp_iensure(i);
if (spunlikely(rc == -1))
return -1;
/* split page */
spipage *n = sp_ipagealloc(i);
if (spunlikely(n == NULL))
return -1;
int half = p->count >> 1;
memcpy(&n->i[0], &p->i[half], sizeof(void*) * (half));
n->count = half;
p->count = half;
/* split page i and insert new page */
memmove(&i->i[a + 1], &i->i[a], sizeof(spipage*) * (i->icount - a));
i->i[a] = p;
i->i[a+1] = n;
i->icount++;
/* choose which part to use */
if (sp_ipagecmp(i, n, v->key, v->size) <= 0) {
p = n;
a++;
}
}
/* 3. if page is not full, do nothing */
/* 4. do binary search on a page and match room, move
* pointers forward */
/* 5. insert key, increment counters */
assert(p->count < i->pagesize);
uint32_t j;
void *o = sp_iminof(i, p, v->key, v->size, &j);
if (spunlikely(o)) {
old->i = i;
old->p = a;
old->n = j;
assert(sp_ival(old) == o);
return 1;
}
if (j >= p->count) {
p->i[p->count] = v;
} else {
memmove(&p->i[j + 1], &p->i[j], sizeof(spv*) * (p->count - j));
p->i[j] = v;
}
i->count++;
p->count++;
return 0;
}
int sp_idelraw(spi *i, char *rkey, int size, spv **old)
{
spipage *p = i->i[0];
uint32_t a = 0;
if (splikely(i->icount > 1))
p = sp_ipageof(i, rkey, size, &a);
if (spunlikely(p == NULL))
return 0;
uint32_t j;
*old = sp_iminof(i, p, rkey, size, &j);
if (spunlikely(*old == NULL))
return 0;
if (splikely(j != (uint32_t)(p->count-1)))
memmove(&p->i[j], &p->i[j + 1], sizeof(spv*) * (p->count - j - 1));
p->count--;
i->count--;
if (splikely(p->count > 0))
return 1;
/* do not touch last page */
if (spunlikely(i->icount == 1))
return 1;
/* remove empty page */
sp_free(i->a, i->i[a]);
if (splikely(a != (i->icount - 1)))
memmove(&i->i[a], &i->i[a + 1], sizeof(spipage*) * (i->icount - a - 1));
i->icount--;
return 1;
}
spv *sp_igetraw(spi *i, char *rkey, int size)
{
spipage *p = i->i[0];
uint32_t a = 0;
if (splikely(i->icount > 1))
p = sp_ipageof(i, rkey, size, &a);
if (p == NULL)
return NULL;
uint32_t j;
return sp_iminof(i, p, rkey, size, &j);
}
static inline int
sp_iworldcmp(spi *i, char *rkey, int size)
{
register spipage *last = i->i[i->icount-1];
register int l =
i->cmp(i->i[0]->i[0]->key,
i->i[0]->i[0]->size, rkey, size, i->cmparg);
register int r =
i->cmp(last->i[last->count-1]->key,
last->i[last->count-1]->size,
rkey, size, i->cmparg);
/* inside index range */
if (l <= 0 && r >= 0)
return 0;
/* index min < key */
if (l == -1)
return -1;
/* index max > key */
assert(r == 1);
return 1;
}
int sp_ilte(spi *i, spii *ii, char *k, int size)
{
if (spunlikely(i->count == 0)) {
sp_iinv(i, ii);
return 0;
}
spipage *p = i->i[0];
uint32_t a = 0;
if (splikely(i->icount > 1))
p = sp_ipageof(i, k, size, &a);
if (p == NULL) {
switch (sp_iworldcmp(i, k, size)) {
case -1:
sp_iinv(i, ii);
break;
case 1:
ii->i = i;
ii->p = i->icount - 1;
ii->n = i->i[i->icount - 1]->count - 1;
break;
case 0:
assert(0);
}
return 0;
}
uint32_t j;
int eq = sp_iminof(i, p, k, size, &j) != NULL;
ii->i = i;
ii->p = a;
ii->n = j;
return eq;
}
int sp_igte(spi *i, spii *ii, char *k, int size)
{
if (spunlikely(i->count == 0)) {
sp_iinv(i, ii);
return 0;
}
spipage *p = i->i[0];
uint32_t a = 0;
if (splikely(i->icount > 1))
p = sp_ipageof(i, k, size, &a);
if (p == NULL) {
switch (sp_iworldcmp(i, k, size)) {
case -1:
ii->i = i;
ii->p = i->icount - 1;
ii->n = i->i[i->icount - 1]->count - 1;
break;
case 1:
sp_iinv(i, ii);
break;
case 0:
assert(0);
}
return 0;
}
uint32_t j;
int eq = sp_imaxof(i, p, k, size, &j) != NULL;
ii->i = i;
ii->p = a;
ii->n = j;
return eq;
}

155
src/sophia/db/i.h Normal file
View File

@@ -0,0 +1,155 @@
#ifndef SP_I_H_
#define SP_I_H_
/*
* sophia database
* sphia.org
*
* Copyright (c) Dmitry Simonenko
* BSD License
*/
typedef struct spipage spipage;
typedef struct spi spi;
typedef struct spii spii;
struct spipage {
uint16_t count;
spv *i[];
} sppacked;
struct spi {
spa *a;
int pagesize;
spipage **i;
uint32_t itop;
uint32_t icount;
uint32_t count;
spcmpf cmp;
void *cmparg;
};
struct spii {
spi *i;
long long p, n;
};
int sp_iinit(spi*, spa*, int, spcmpf, void*);
void sp_ifree(spi*);
int sp_itruncate(spi*);
int sp_isetorget(spi *i, spv*, spii*);
int sp_idelraw(spi*, char*, int, spv**);
spv *sp_igetraw(spi*, char*, int);
static inline int
sp_idel(spi *i, spv *v, spv **old) {
return sp_idelraw(i, v->key, v->size, old);
}
static inline spv*
sp_iget(spi *i, spv *v) {
return sp_igetraw(i, v->key, v->size);
}
static inline void*
sp_imax(spi *i) {
if (spunlikely(i->count == 0))
return NULL;
return i->i[i->icount-1]->i[i->i[i->icount-1]->count-1];
}
static inline void
sp_ifirst(spii *it) {
it->p = 0;
it->n = 0;
}
static inline void
sp_ilast(spii *it) {
it->p = it->i->icount - 1;
it->n = it->i->i[it->i->icount - 1]->count - 1;
}
static inline void
sp_iopen(spii *it, spi *i) {
it->i = i;
sp_ifirst(it);
}
static inline int
sp_ihas(spii *it) {
return (it->p >= 0 && it->n >= 0) &&
(it->p < it->i->icount) &&
(it->n < it->i->i[it->p]->count);
}
static inline void
sp_ivalset(spii *it, spv *v) {
it->i->i[it->p]->i[it->n] = v;
}
static inline spv*
sp_ival(spii *it) {
if (spunlikely(! sp_ihas(it)))
return NULL;
return it->i->i[it->p]->i[it->n];
}
static inline int
sp_inext(spii *it) {
if (spunlikely(! sp_ihas(it)))
return 0;
it->n++;
while (it->p < it->i->icount) {
spipage *p = it->i->i[it->p];
if (spunlikely(it->n >= p->count)) {
it->p++;
it->n = 0;
continue;
}
return 1;
}
return 0;
}
static inline int
sp_iprev(spii *it) {
if (spunlikely(! sp_ihas(it)))
return 0;
it->n--;
while (it->p >= 0) {
if (spunlikely(it->n < 0)) {
if (it->p == 0)
return 0;
it->p--;
it->n = it->i->i[it->p]->count-1;
continue;
}
return 1;
}
return 0;
}
static inline void
sp_iinv(spi *i, spii *ii) {
ii->i = i;
ii->p = -1;
ii->n = -1;
}
int sp_ilte(spi*, spii*, char*, int);
int sp_igte(spi*, spii*, char*, int);
static inline int
sp_iset(spi *i, spv *v, spv **old)
{
spii pos;
int rc = sp_isetorget(i, v, &pos);
if (splikely(rc <= 0))
return rc;
*old = sp_ival(&pos);
sp_ivalset(&pos, v);
return 1;
}
#endif

91
src/sophia/db/list.h Normal file
View File

@@ -0,0 +1,91 @@
#ifndef SP_LIST_H_
#define SP_LIST_H_
/*
* sophia database
* sphia.org
*
* Copyright (c) Dmitry Simonenko
* BSD License
*/
typedef struct splist splist;
struct splist {
splist *next, *prev;
};
static inline void
sp_listinit(splist *h) {
h->next = h->prev = h;
}
static inline void
sp_listappend(splist *h, splist *n) {
n->next = h;
n->prev = h->prev;
n->prev->next = n;
n->next->prev = n;
}
static inline void
sp_listunlink(splist *n) {
n->prev->next = n->next;
n->next->prev = n->prev;
}
static inline void
sp_listpush(splist *h, splist *n) {
n->next = h->next;
n->prev = h;
n->prev->next = n;
n->next->prev = n;
}
static inline splist*
sp_listpop(splist *h) {
register splist *pop = h->next;
sp_listunlink(pop);
return pop;
}
static inline int
sp_listempty(splist *l) {
return l->next == l && l->prev == l;
}
static inline void
sp_listmerge(splist *a, splist *b) {
if (spunlikely(sp_listempty(b)))
return;
register splist *first = b->next;
register splist *last = b->prev;
first->prev = a->prev;
a->prev->next = first;
last->next = a;
a->prev = last;
}
static inline void
sp_listreplace(splist *o, splist *n) {
n->next = o->next;
n->next->prev = n;
n->prev = o->prev;
n->prev->next = n;
}
#define sp_listlast(H, N) ((H) == (N))
#define sp_listforeach(H, I) \
for (I = (H)->next; I != H; I = (I)->next)
#define sp_listforeach_continue(H, I) \
for (; I != H; I = (I)->next)
#define sp_listforeach_safe(H, I, N) \
for (I = (H)->next; I != H && (N = I->next); I = N)
#define sp_listforeach_reverse(H, I) \
for (I = (H)->prev; I != H; I = (I)->prev)
#endif

77
src/sophia/db/lock.h Normal file
View File

@@ -0,0 +1,77 @@
#ifndef SP_LOCK_H_
#define SP_LOCK_H_
/*
* sophia database
* sphia.org
*
* Copyright (c) Dmitry Simonenko
* BSD License
*/
#include <unistd.h>
typedef uint8_t spspinlock;
#if defined(__x86_64__) || defined(__i386) || defined(_X86_)
# define CPU_PAUSE __asm__ ("pause")
#else
# define CPU_PAUSE do { } while(0)
#endif
static inline void
sp_lockinit(volatile spspinlock *l) {
*l = 0;
}
static inline void
sp_lockfree(volatile spspinlock *l) {
*l = 0;
}
static inline void
sp_lock(volatile spspinlock *l) {
if (__sync_lock_test_and_set(l, 1) != 0) {
unsigned int spin_count = 0U;
for (;;) {
CPU_PAUSE;
if (*l == 0U && __sync_lock_test_and_set(l, 1) == 0)
break;
if (++spin_count > 100U)
usleep(0);
}
}
}
static inline void
sp_unlock(volatile spspinlock *l) {
__sync_lock_release(l);
}
#if 0
#include <pthread.h>
typedef pthread_spinlock_t spspinlock;
static inline void
sp_lockinit(volatile spspinlock *l) {
pthread_spin_init(l, 0);
}
static inline void
sp_lockfree(volatile spspinlock *l) {
pthread_spin_destroy(l);
}
static inline void
sp_lock(volatile spspinlock *l) {
pthread_spin_lock(l);
}
static inline void
sp_unlock(volatile spspinlock *l) {
pthread_spin_unlock(l);
}
#endif
#endif

20
src/sophia/db/macro.h Normal file
View File

@@ -0,0 +1,20 @@
#ifndef SP_MACRO_H_
#define SP_MACRO_H_
/*
* sophia database
* sphia.org
*
* Copyright (c) Dmitry Simonenko
* BSD License
*/
#define sppacked __attribute__((packed))
#define spunused __attribute__((unused))
#define sphot __attribute__((hot))
#define splikely(EXPR) __builtin_expect(!! (EXPR), 1)
#define spunlikely(EXPR) __builtin_expect(!! (EXPR), 0)
#define spdiv(a, b) ((a) + (b) - 1) / (b)
#define spcast(N, T, F) ((T*)((char*)(N) - __builtin_offsetof(T, F)))
#endif

26
src/sophia/db/makefile Normal file
View File

@@ -0,0 +1,26 @@
CC ?= gcc
RM ?= rm
CFLAGS ?= -I. -std=c99 -pedantic -Wextra -Wall -pthread -O2 -DNDEBUG
LDFLAGS ?=
OBJECTS = file.o \
crc.o \
e.o \
i.o \
cat.o \
rep.o \
util.o \
sp.o \
recover.o \
merge.o \
gc.o \
cursor.o
TARGET = libsophia.a
$(TARGET): clean $(OBJECTS)
$(AR) cru $(TARGET) $(OBJECTS)
.c.o:
$(CC) $(CFLAGS) -c $<
clean:
$(RM) -f $(OBJECTS) $(TARGET)

662
src/sophia/db/merge.c Normal file
View File

@@ -0,0 +1,662 @@
/*
* sophia database
* sphia.org
*
* Copyright (c) Dmitry Simonenko
* BSD License
*/
#include "sp.h"
typedef struct {
uint32_t count;
uint32_t psize;
uint32_t bsize;
} spupdate0;
static inline void
sp_mergeget0(spii *pos, uint32_t n, spupdate0 *u)
{
memset(u, 0, sizeof(*u));
/*
* collect n or less versions for scheduled page write,
* not marked as delete, calculate page size and the
* block size.
*/
spii i = *pos;
while (u->count < n && sp_ihas(&i)) {
spv *v = sp_ival(&i);
if (v->flags & SPDEL) {
sp_inext(&i);
continue;
}
if (v->size > u->bsize)
u->bsize = v->size;
sp_inext(&i);
u->count++;
u->psize += sp_vvsize(v);
}
u->bsize += sizeof(spvh);
u->psize += sizeof(sppageh) + u->bsize * u->count;
}
static inline int sp_merge0(sp *s, spepoch *x, spi *index)
{
spv *max = NULL;
spv *min = NULL;
int rc;
spii i;
sp_iopen(&i, index);
while (sp_active(s))
{
/* get the new page properties and a data */
spupdate0 u;
sp_mergeget0(&i, s->e->page, &u);
if (spunlikely(u.count == 0))
break;
/* ensure enough space for the page in the file */
sp_lock(&x->lock);
rc = sp_mapensure(&x->db, u.psize, s->e->dbgrow);
if (spunlikely(rc == -1)) {
sp_unlock(&x->lock);
sp_e(s, SPEIO, "failed to remap db file", x->epoch);
goto err;
}
sp_unlock(&x->lock);
/* write the page.
*
* [header] [keys (block sized)] [values]
*
* Use partly precalculated crc for a version.
*/
sppageh *h = (sppageh*)(x->db.map + x->db.used);
h->id = ++s->psn;
h->count = u.count;
h->bsize = u.bsize;
h->size = u.psize - sizeof(sppageh);
h->crc = sp_crc32c(0, &h->id, sizeof(sppageh) - sizeof(h->crc));
char *ph = x->db.map + x->db.used + sizeof(sppageh);
char *pv = ph + u.count * u.bsize;
uint32_t current = 0;
spv *last = NULL;
while (sp_active(s) && current < u.count)
{
spv *v = sp_ival(&i);
if (v->flags & SPDEL) {
sp_inext(&i);
continue;
}
if (spunlikely(min == NULL)) {
min = sp_vdup(s, v);
if (spunlikely(min == NULL)) {
sp_e(s, SPEOOM, "failed to allocate key");
goto err;
}
}
assert(v->size <= u.bsize);
spvh *vh = (spvh*)(ph);
vh->size = v->size;
vh->flags = v->flags;
vh->vsize = sp_vvsize(v);
vh->voffset = pv - (char*)h;
vh->crc = sp_crc32c(v->crc, &vh->size, sizeof(spvh) - sizeof(vh->crc));
memcpy(vh->key, v->key, v->size);
memcpy(pv, sp_vv(v), vh->vsize);
ph += u.bsize;
pv += vh->vsize;
last = v;
current++;
sp_inext(&i);
}
/* cancellation point check */
if (! sp_active(s))
goto err;
/* create in-memory page */
sppage *page = sp_pagenew(s, x);
if (spunlikely(page == NULL)) {
sp_e(s, SPEOOM, "failed to allocate page");
goto err;
}
max = sp_vdup(s, last);
if (spunlikely(max == NULL)) {
sp_e(s, SPEOOM, "failed to allocate key");
goto err;
}
assert(min != NULL);
page->id = s->psn;
page->offset = x->db.used;
page->size = u.psize;
page->min = min;
page->max = max;
/* insert page to the index */
sp_lock(&s->locks);
sppage *o = NULL;
rc = sp_catset(&s->s, page, &o);
if (spunlikely(rc == -1)) {
sp_unlock(&s->locks);
sp_pagefree(s, page);
sp_e(s, SPEOOM, "failed to allocate page index page");
goto err;
}
sp_unlock(&s->locks);
/* attach page to the epoch list */
sp_pageattach(page);
/* advance file buffer */
sp_mapuse(&x->db, u.psize);
min = NULL;
max = NULL;
}
return 0;
err:
if (min)
sp_free(&s->a, min);
if (max)
sp_free(&s->a, max);
return -1;
}
typedef struct {
uint32_t pi;
sppage *p;
spepoch *s; /* p->epoch */
uint32_t count;
uint32_t bsize;
} spupdate;
typedef struct {
/* a is an original page version
b is in-memory version */
int a_bsize, b_bsize;
int a_count, b_count;
int A, B;
spvh *a;
spv *b;
spref last;
spii i;
spepoch *x;
} spmerge;
typedef struct {
splist split;
int count;
} spsplit;
static inline int
sp_mergeget(sp *s, spii *from, spupdate *u)
{
spii i = *from;
if (spunlikely(! sp_ihas(&i)))
return 0;
memset(u, 0, sizeof(spupdate));
/* match the origin page and a associated
* range of keys. */
sppage *origin = NULL;
uint32_t origin_idx = 0;
uint32_t n = 0;
while (sp_ihas(&i)) {
spv *v = sp_ival(&i);
if (splikely(origin)) {
if (! sp_catown(&s->s, origin_idx, v))
break;
} else {
origin = sp_catroute(&s->s, v->key, v->size, &origin_idx);
assert(((spepoch*)origin->epoch)->type == SPDB);
}
if (v->size > u->bsize)
u->bsize = v->size;
sp_inext(&i);
n++;
}
assert(n > 0);
u->count = n;
u->bsize += sizeof(spvh);
u->pi = origin_idx;
u->p = origin;
u->s = origin->epoch;
return 1;
}
static inline void
sp_mergeinit(spepoch *x, spmerge *m, spupdate *u, spii *from)
{
sppageh *h = (sppageh*)(u->s->db.map + u->p->offset);
uint32_t bsize = u->bsize;
if (h->bsize > bsize)
bsize = h->bsize;
m->a_bsize = h->bsize;
m->b_bsize = bsize;
memset(&m->last, 0, sizeof(m->last));
m->i = *from;
m->A = 0;
m->B = 0;
m->a_count = h->count;
m->b_count = u->count;
m->a = (spvh*)((char*)h + sizeof(sppageh));
m->b = sp_ival(from);
m->x = x;
}
static inline int sp_mergenext(sp *s, spmerge *m)
{
if (m->A < m->a_count && m->B < m->b_count)
{
register int cmp =
s->e->cmp(m->a->key, m->a->size,
m->b->key,
m->b->size, s->e->cmparg);
switch (cmp) {
case 0:
/* use updated key B */
m->last.type = SPREFM;
m->last.v.v = m->b;
m->A++;
m->a = (spvh*)((char*)m->a + m->a_bsize);
m->B++;
sp_inext(&m->i);
m->b = sp_ival(&m->i);
return 1;
case -1:
/* use A */
m->last.type = SPREFD;
m->last.v.vh = m->a;
m->A++;
m->a = (spvh*)((char*)m->a + m->a_bsize);
return 1;
case 1:
/* use B */
m->last.type = SPREFM;
m->last.v.v = m->b;
m->B++;
sp_inext(&m->i);
m->b = sp_ival(&m->i);
return 1;
}
}
if (m->A < m->a_count) {
/* use A */
m->last.type = SPREFD;
m->last.v.vh = m->a;
m->A++;
m->a = (spvh*)((char*)m->a + m->a_bsize);
return 1;
}
if (m->B < m->b_count) {
/* use B */
m->last.type = SPREFM;
m->last.v.v = m->b;
m->B++;
sp_inext(&m->i);
m->b = sp_ival(&m->i);
return 1;
}
return 0;
}
static inline void
sp_splitinit(spsplit *l) {
sp_listinit(&l->split);
l->count = 0;
}
static inline void
sp_splitfree(sp *s, spsplit *l) {
splist *i, *n;
sp_listforeach_safe(&l->split, i, n) {
sppage *p = spcast(i, sppage, link);
sp_pagefree(s, p);
}
}
static inline int sp_split(sp *s, spupdate *u, spmerge *m, spsplit *l)
{
int rc;
int bsize = m->b_bsize;
uint32_t pagesize = sizeof(sppageh);
uint32_t count = 0;
/*
* merge in-memory keys with the origin page keys,
* skip any deletes and calculate result
* page size.
*/
sp_refsetreset(&s->refs);
while (count < s->e->page && sp_mergenext(s, m)) {
if (sp_refisdel(&m->last))
continue;
sp_refsetadd(&s->refs, &m->last);
pagesize += bsize + sp_refvsize(&m->last);
count++;
}
if (spunlikely(count == 0 && l->count > 0))
return 0;
/*
* set the origin page id for a first spitted page
*/
uint32_t psn = (l->count == 0) ? u->p->id : ++s->psn;
/* ensure enough space for the page in the file */
sp_lock(&m->x->lock);
rc = sp_mapensure(&m->x->db, pagesize, s->e->dbgrow);
if (spunlikely(rc == -1)) {
sp_unlock(&m->x->lock);
return sp_e(s, SPEIO, "failed to remap db file",
m->x->epoch);
}
sp_unlock(&m->x->lock);
/* in case if all origin page keys are deleted.
*
* write special page header without any data, indicating
* that page should be skipped during recovery
* and not being added to the index.
*/
if (spunlikely(count == 0 && l->count == 0)) {
sppageh *h = (sppageh*)(m->x->db.map + m->x->db.used);
h->id = psn;
h->count = 0;
h->bsize = 0;
h->size = 0;
h->crc = sp_crc32c(0, &h->id, sizeof(sppageh) - sizeof(h->crc));
sp_mapuse(&m->x->db, pagesize);
return 0;
}
spref *r = s->refs.r;
spref *min = r;
spref *max = r + (count - 1);
/*
* write the page
*/
sppageh *h = (sppageh*)(m->x->db.map + m->x->db.used);
h->id = psn;
h->count = count;
h->bsize = bsize;
h->size = pagesize - sizeof(sppageh);
h->crc = sp_crc32c(0, &h->id, sizeof(sppageh) - sizeof(h->crc));
spvh *ptr = (spvh*)(m->x->db.map + m->x->db.used + sizeof(sppageh));
char *ptrv = (char*)ptr + count * bsize;
uint32_t i = 0;
while (i < count)
{
uint32_t voffset = ptrv - (char*)h;
switch (r->type) {
case SPREFD:
memcpy(ptr, r->v.vh, sizeof(spvh) + r->v.vh->size);
memcpy(ptrv, u->s->db.map + u->p->offset + r->v.vh->voffset,
r->v.vh->vsize);
ptr->voffset = voffset;
uint32_t crc;
crc = sp_crc32c(0, ptr->key, ptr->size);
crc = sp_crc32c(crc, ptrv, r->v.vh->vsize);
crc = sp_crc32c(crc, &ptr->size, sizeof(spvh) - sizeof(ptr->crc));
ptr->crc = crc;
ptrv += r->v.vh->vsize;
break;
case SPREFM:
ptr->size = r->v.v->size;
ptr->flags = r->v.v->flags;
ptr->voffset = voffset;
ptr->vsize = sp_vvsize(r->v.v);
ptr->crc = sp_crc32c(r->v.v->crc, &ptr->size, sizeof(spvh) -
sizeof(ptr->crc));
memcpy(ptr->key, r->v.v->key, r->v.v->size);
memcpy(ptrv, sp_vv(r->v.v), ptr->vsize);
ptrv += ptr->vsize;
break;
}
assert((uint32_t)(ptrv - (char*)h) <= pagesize);
ptr = (spvh*)((char*)ptr + bsize);
r++;
i++;
}
/* create in-memory page */
sppage *p = sp_pagenew(s, m->x);
if (spunlikely(p == NULL))
return sp_e(s, SPEOOM, "failed to allocate page");
p->id = psn;
p->offset = m->x->db.used;
p->size = pagesize;
p->min = sp_vdupref(s, min, m->x->epoch);
if (spunlikely(p->min == NULL)) {
sp_free(&s->a, p);
return sp_e(s, SPEOOM, "failed to allocate key");
}
p->max = sp_vdupref(s, max, m->x->epoch);
if (spunlikely(p->max == NULL)) {
sp_free(&s->a, p->min);
sp_free(&s->a, p);
return sp_e(s, SPEOOM, "failed to allocate key");
}
/* add page to split list */
sp_listappend(&l->split, &p->link);
l->count++;
/* advance buffer */
sp_mapuse(&m->x->db, pagesize);
return 1;
}
static inline int sp_splitcommit(sp *s, spupdate *u, spmerge *m, spsplit *l)
{
sp_lock(&s->locks);
/* remove origin page, if there were no page
* updates after split */
if (spunlikely(l->count == 0)) {
sp_pagefree(s, u->p);
u->s->ngc++;
u->p = NULL;
sp_catdel(&s->s, u->pi);
sp_unlock(&s->locks);
return 0;
}
splist *i, *n;
sp_listforeach_safe(&l->split, i, n)
{
sppage *p = spcast(i, sppage, link);
/* update origin page first */
if (spunlikely(p->id == u->p->id)) {
sp_listunlink(&p->link);
/* relink origin page to new epoch */
sppage *origin = u->p;
assert(origin->epoch != m->x);
sp_listunlink(&origin->link);
u->s->ngc++; /* origin db epoch */
m->x->n++; /* current db epoch */
sp_listappend(&m->x->pages, &origin->link);
/* update origin page */
origin->offset = p->offset;
assert(p->epoch == m->x);
origin->epoch = m->x;
origin->size = p->size;
sp_free(&s->a, origin->min);
sp_free(&s->a, origin->max);
origin->min = p->min;
origin->max = p->max;
sp_free(&s->a, p);
continue;
}
/* insert split page */
sppage *o = NULL;
int rc = sp_catset(&s->s, p, &o);
if (spunlikely(rc == -1)) {
sp_unlock(&s->locks);
return sp_e(s, SPEOOM, "failed to allocate page index page");
}
assert(o == NULL);
sp_pageattach(p);
m->x->n++;
}
sp_unlock(&s->locks);
return 0;
}
static inline int sp_mergeN(sp *s, spepoch *x, spi *index)
{
int rc;
spii i;
sp_iopen(&i, index);
spupdate u;
while (sp_mergeget(s, &i, &u))
{
spmerge m;
sp_mergeinit(x, &m, &u, &i);
spsplit l;
sp_splitinit(&l);
while (sp_active(s)) {
rc = sp_split(s, &u, &m, &l);
if (spunlikely(rc == 0))
break;
else
if (spunlikely(rc == -1)) {
sp_splitfree(s, &l);
return -1;
}
}
if (spunlikely(! sp_active(s)))
return 0;
rc = sp_splitcommit(s, &u, &m, &l);
if (spunlikely(rc == -1)) {
sp_splitfree(s, &l);
return -1;
}
i = m.i;
}
return 0;
}
int sp_merge(sp *s)
{
sp_lock(&s->lockr);
sp_lock(&s->locki);
spepoch *x = sp_replive(&s->rep);
/* rotate current live epoch */
sp_repset(&s->rep, x, SPXFER);
int rc = sp_rotate(s);
if (spunlikely(rc == -1)) {
sp_lock(&s->lockr);
sp_lock(&s->locki);
return -1;
}
/* swap index */
spi *index = sp_iswap(s);
sp_unlock(&s->lockr);
sp_unlock(&s->locki);
/* complete old live epoch log */
rc = sp_logeof(&x->log);
if (spunlikely(rc == -1))
return sp_e(s, SPEIO, "failed to write eof marker", x->epoch);
rc = sp_logcomplete(&x->log);
if (spunlikely(rc == -1))
return sp_e(s, SPEIO, "failed to complete log file", x->epoch);
/* create db file */
rc = sp_mapepochnew(&x->db, s->e->dbnewsize, s->e->dir, x->epoch, "db");
if (spunlikely(rc == -1))
return sp_e(s, SPEIO, "failed to create db file", x->epoch);
/* merge index */
if (splikely(s->s.count > 0))
rc = sp_mergeN(s, x, index);
else
rc = sp_merge0(s, x, index);
/* check cancellation point */
if (! sp_active(s)) {
sp_mapunlink(&x->db);
sp_mapclose(&x->db);
return rc;
}
if (spunlikely(rc == -1))
return -1;
/* gc */
if (s->e->gc) {
rc = sp_gc(s, x);
if (spunlikely(rc == -1))
return -1;
}
/* sync/truncate db file and remap read-only only if
* database file is not empty. */
if (splikely(x->db.used > 0)) {
sp_lock(&x->lock);
rc = sp_mapcomplete(&x->db);
if (spunlikely(rc == -1)) {
sp_unlock(&x->lock);
return sp_e(s, SPEIO, "failed to complete db file", x->epoch);
}
sp_unlock(&x->lock);
/* set epoch as db */
sp_lock(&s->lockr);
sp_repset(&s->rep, x, SPDB);
sp_unlock(&s->lockr);
/* remove log file */
rc = sp_logunlink(&x->log);
if (spunlikely(rc == -1))
return sp_e(s, SPEIO, "failed to unlink log file", x->epoch);
rc = sp_logclose(&x->log);
if (spunlikely(rc == -1))
return sp_e(s, SPEIO, "failed to close log file", x->epoch);
} else {
/* there are possible situation when all keys has
* been deleted. */
rc = sp_mapunlink(&x->db);
if (spunlikely(rc == -1))
return sp_e(s, SPEIO, "failed to unlink db file", x->epoch);
rc = sp_mapclose(&x->db);
if (spunlikely(rc == -1))
return sp_e(s, SPEIO, "failed to close db file", x->epoch);
}
/* remove all xfer epochs that took part in the merge
* including current, if it's database file
* is empty. */
while (sp_active(s)) {
sp_lock(&s->lockr);
spepoch *e = sp_repxfer(&s->rep);
sp_unlock(&s->lockr);
if (e == NULL)
break;
rc = sp_logunlink(&e->log);
if (spunlikely(rc == -1))
return sp_e(s, SPEIO, "failed to unlink log file", e->epoch);
rc = sp_logclose(&e->log);
if (spunlikely(rc == -1))
return sp_e(s, SPEIO, "failed to close log file", e->epoch);
sp_lock(&s->lockr);
sp_repdetach(&s->rep, e);
sp_free(&s->a, e);
sp_unlock(&s->lockr);
}
/* truncate the index (skip index on a read) */
sp_iskipset(s, 1);
rc = sp_itruncate(index);
if (spunlikely(rc == -1)) {
sp_iskipset(s, 0);
return sp_e(s, SPE, "failed create index");
}
sp_iskipset(s, 0);
return 0;
}

14
src/sophia/db/merge.h Normal file
View File

@@ -0,0 +1,14 @@
#ifndef SP_MERGE_H_
#define SP_MERGE_H_
/*
* sophia database
* sphia.org
*
* Copyright (c) Dmitry Simonenko
* BSD License
*/
int sp_merge(sp*);
#endif

87
src/sophia/db/meta.h Normal file
View File

@@ -0,0 +1,87 @@
#ifndef SP_META_H_
#define SP_META_H_
/*
* sophia database
* sphia.org
*
* Copyright (c) Dmitry Simonenko
* BSD License
*/
/* on-disk */
typedef struct splogh splogh;
typedef struct speofh speofh;
typedef struct sppageh sppageh;
typedef struct spvh spvh;
#define SPEOF 0x00aaeefdL
#define SPMAGIC 0x00f0e0d0L
struct splogh {
uint32_t magic;
uint8_t version[2];
} sppacked;
struct speofh {
uint32_t magic;
} sppacked;
struct sppageh {
uint32_t crc;
uint64_t id;
uint16_t count;
uint32_t size;
uint32_t bsize;
} sppacked;
struct spvh {
uint32_t crc;
uint32_t size;
uint32_t voffset;
uint32_t vsize;
uint8_t flags;
char key[];
} sppacked;
/* in-memory */
typedef struct spv spv;
typedef struct sppage sppage;
#define SPSET 1
#define SPDEL 2
struct spv {
uint32_t epoch;
uint32_t crc; /* key-value crc without header */
uint16_t size;
uint8_t flags;
char key[];
/* uint32_t vsize */
/* char v[] */
} sppacked;
struct sppage {
uint64_t id;
uint64_t offset;
void *epoch;
uint32_t size;
spv *min;
spv *max;
splist link;
} sppacked;
static inline char*
sp_vv(spv *v) {
return v->key + v->size + sizeof(uint32_t);
}
static inline uint32_t
sp_vvsize(spv *v) {
register char *p = (char*)(v->key + v->size);
return *(uint32_t*)p;
}
#endif

433
src/sophia/db/recover.c Normal file
View File

@@ -0,0 +1,433 @@
/*
* sophia database
* sphia.org
*
* Copyright (c) Dmitry Simonenko
* BSD License
*/
#include "sp.h"
#include "track.h"
#include <ctype.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>
#include <unistd.h>
static inline int sp_dircreate(sp *s) {
int rc = mkdir(s->e->dir, 0700);
if (spunlikely(rc == -1)) {
sp_e(s, SPE, "failed to create directory %s (errno: %d, %s)",
s->e->dir, errno, strerror(errno));
return -1;
}
return 0;
}
static inline ssize_t
sp_epochof(char *s) {
size_t v = 0;
while (*s && *s != '.') {
if (spunlikely(!isdigit(*s)))
return -1;
v = (v * 10) + *s - '0';
s++;
}
return v;
}
static int sp_diropen(sp *s)
{
/* read repository and determine states */
DIR *d = opendir(s->e->dir);
if (spunlikely(d == NULL)) {
sp_e(s, SPE, "failed to open directory %s (errno: %d, %s)",
s->e->dir, errno, strerror(errno));
return -1;
}
struct dirent *de;
while ((de = readdir(d))) {
if (*de->d_name == '.')
continue;
ssize_t epoch = sp_epochof(de->d_name);
if (epoch == -1)
continue;
spepoch *e = sp_repmatch(&s->rep, epoch);
if (e == NULL) {
e = sp_repalloc(&s->rep, epoch);
if (spunlikely(e == NULL)) {
closedir(d);
sp_e(s, SPEOOM, "failed to allocate repository");
return -1;
}
sp_repattach(&s->rep, e);
}
char *ext = strstr(de->d_name, ".db");
if (ext) {
ext = strstr(de->d_name, ".incomplete");
e->recover |= (ext? SPRDBI: SPRDB);
continue;
}
ext = strstr(de->d_name, ".log");
if (ext) {
ext = strstr(de->d_name, ".incomplete");
e->recover |= (ext? SPRLOGI: SPRLOG);
}
continue;
}
closedir(d);
if (s->rep.n == 0)
return 0;
/* set current and sort by epoch */
int rc = sp_repprepare(&s->rep);
if (spunlikely(rc == -1))
return sp_e(s, SPEOOM, "failed to allocate repository");
return 0;
}
static int sp_recoverdb(sp *s, spepoch *x, sptrack *t)
{
int rc = sp_mapepoch(&x->db, s->e->dir, x->epoch, "db");
if (spunlikely(rc == -1))
return sp_e(s, SPEIO, "failed to open db file", x->epoch);
sppageh *h = (sppageh*)(x->db.map);
for(;;)
{
if (spunlikely((uint64_t)((char*)h - x->db.map) >= x->db.size))
break;
/* validate header */
uint32_t crc = sp_crc32c(0, &h->id, sizeof(sppageh) - sizeof(h->crc));
if (crc != h->crc) {
sp_mapclose(&x->db);
return sp_e(s, SPE, "page crc failed %"PRIu32".db", x->epoch);
}
assert(h->id > 0);
x->n++;
x->nupdate += h->count;
/* match page in hash by h.id, skip if matched */
if (sp_trackhas(t, h->id)) {
/* skip to a next page */
h = (sppageh*)((char*)h + sizeof(sppageh) + h->size);
x->ngc++;
continue;
}
/* track page id */
rc = sp_trackset(t, h->id);
if (spunlikely(rc == -1)) {
sp_mapclose(&x->db);
return sp_e(s, SPEOOM, "failed to allocate track item");
}
/* if this is a page delete marker, then skip to
* a next page */
if (h->count == 0) {
h = (sppageh*)((char*)h + sizeof(sppageh) + h->size);
continue;
}
/* set page min (first block)*/
spvh *minp = (spvh*)((char*)h + sizeof(sppageh));
crc = sp_crc32c(0, minp->key, minp->size);
crc = sp_crc32c(crc, (char*)h + minp->voffset, minp->vsize);
crc = sp_crc32c(crc, (char*)&minp->size, sizeof(spvh) - sizeof(minp->crc));
if (crc != minp->crc) {
sp_mapclose(&x->db);
return sp_e(s, SPE, "page min key crc failed %"PRIu32".db", x->epoch);
}
assert(minp->flags == SPSET);
/* set page max (last block) */
spvh *maxp = (spvh*)((char*)h + sizeof(sppageh) + h->bsize * (h->count - 1));
crc = sp_crc32c(0, maxp->key, maxp->size);
crc = sp_crc32c(crc, (char*)h + maxp->voffset, maxp->vsize);
crc = sp_crc32c(crc, (char*)&maxp->size, sizeof(spvh) - sizeof(maxp->crc));
if (crc != maxp->crc) {
sp_mapclose(&x->db);
return sp_e(s, SPE, "page max key crc failed %"PRIu32".db", x->epoch);
}
assert(maxp->flags == SPSET);
spv *min = sp_vnewh(s, minp);
if (spunlikely(min == NULL)) {
sp_mapclose(&x->db);
return sp_e(s, SPEOOM, "failed to allocate key");
}
assert(min->flags == SPSET);
min->epoch = x->epoch;
spv *max = sp_vnewh(s, maxp);
if (spunlikely(max == NULL)) {
sp_free(&s->a, min);
sp_mapclose(&x->db);
return sp_e(s, SPEOOM, "failed to allocate key");
}
assert(max->flags == SPSET);
max->epoch = x->epoch;
/* allocate and insert new page */
sppage *page = sp_pagenew(s, x);
if (spunlikely(page == NULL)) {
sp_free(&s->a, min);
sp_free(&s->a, max);
sp_mapclose(&x->db);
return sp_e(s, SPEOOM, "failed to allocate page");
}
page->id = h->id;
page->offset = (char*)h - x->db.map;
page->size = sizeof(sppageh) + h->size;
page->min = min;
page->max = max;
sppage *o = NULL;
rc = sp_catset(&s->s, page, &o);
if (spunlikely(rc == -1)) {
sp_pagefree(s, page);
sp_mapclose(&x->db);
return sp_e(s, SPEOOM, "failed to allocate page index page");
}
assert(o == NULL);
/* attach page to the source */
sp_pageattach(page);
/* skip to a next page */
h = (sppageh*)((char*)h + sizeof(sppageh) + h->size);
}
return 0;
}
static int sp_recoverlog(sp *s, spepoch *x, int incomplete)
{
/* open and map log file */
char *ext = (incomplete ? "log.incomplete" : "log");
int rc;
rc = sp_mapepoch(&x->log, s->e->dir, x->epoch, ext);
if (spunlikely(rc == -1))
return sp_e(s, SPEIO, "failed to open log file", x->epoch);
/* validate log header */
if (spunlikely(! sp_mapinbound(&x->log, sizeof(splogh)) ))
return sp_e(s, SPE, "bad log file %"PRIu32".log", x->epoch);
splogh *h = (splogh*)(x->log.map);
if (spunlikely(h->magic != SPMAGIC))
return sp_e(s, SPE, "log bad magic %"PRIu32".log", x->epoch);
if (spunlikely(h->version[0] != SP_VERSION_MAJOR &&
h->version[1] != SP_VERSION_MINOR))
return sp_e(s, SPE, "unknown file version of %"PRIu32".log", x->epoch);
uint64_t offset = sizeof(splogh);
uint32_t unique = 0;
int eof = 0;
while (offset < x->log.size)
{
/* check for a eof */
if (spunlikely(offset == (x->log.size - sizeof(speofh)))) {
speofh *eofh = (speofh*)(x->log.map + offset);
if (eofh->magic != SPEOF) {
sp_mapclose(&x->log);
return sp_e(s, SPE, "bad log eof magic %"PRIu32".log", x->epoch);
}
eof++;
offset += sizeof(speofh);
break;
}
/* validate a record */
if (spunlikely(! sp_mapinbound(&x->log, offset + sizeof(spvh)) )) {
sp_mapclose(&x->log);
return sp_e(s, SPE, "log file corrupted %"PRIu32".log", x->epoch);
}
spvh *vh = (spvh*)(x->log.map + offset);
uint32_t crc0, crc1;
crc0 = sp_crc32c(0, vh->key, vh->size);
crc0 = sp_crc32c(crc0, vh->key + vh->size, vh->vsize);
crc1 = sp_crc32c(crc0, &vh->size, sizeof(spvh) - sizeof(vh->crc));
if (spunlikely(crc1 != vh->crc)) {
sp_mapclose(&x->log);
return sp_e(s, SPE, "log record crc failed %"PRIu32".log", x->epoch);
}
int c0 = vh->flags != SPSET && vh->flags != SPDEL;
int c1 = vh->voffset != 0;
int c2 = !sp_mapinbound(&x->log, offset + sizeof(spvh) + vh->size +
vh->vsize);
if (spunlikely((c0 + c1 + c2) > 0)) {
sp_mapclose(&x->log);
return sp_e(s, SPE, "bad log record %"PRIu32".log", x->epoch);
}
/* add a key to the key index.
*
* key index have only actual key, replace should be done
* within the same epoch by a newest records only and skipped
* in a older epochs.
*/
spv *v = sp_vnewv(s, vh->key, vh->size, vh->key + vh->size, vh->vsize);
if (spunlikely(v == NULL)) {
sp_mapclose(&x->log);
return sp_e(s, SPEOOM, "failed to allocate key");
}
v->flags = vh->flags;
v->epoch = x->epoch;
v->crc = crc0;
spii pos;
switch (sp_isetorget(s->i, v, &pos)) {
case 1: {
spv *old = sp_ival(&pos);
if (old->epoch == x->epoch) {
sp_ivalset(&pos, v);
sp_free(&s->a, old);
} else {
sp_free(&s->a, v);
}
break;
}
case 0:
unique++;
break;
case -1:
sp_mapclose(&x->log);
return sp_e(s, SPEOOM, "failed to allocate key index page");
}
offset += sizeof(spvh) + vh->size + vh->vsize;
x->nupdate++;
}
if ((offset > x->log.size) || ((offset < x->log.size) && !eof)) {
sp_mapclose(&x->log);
return sp_e(s, SPE, "log file corrupted %"PRIu32".log", x->epoch);
}
/* unmap file only, unlink-close will ocurre in merge or
* during shutdown */
rc = sp_mapunmap(&x->log);
if (spunlikely(rc == -1))
return sp_e(s, SPEIO, "failed to unmap log file", x->epoch);
/*
* if there is eof marker missing, try to add one
* (only for incomplete files), otherwise indicate corrupt
*/
if (incomplete == 0 && !eof)
return sp_e(s, SPE, "bad log eof marker %"PRIu32".log", x->epoch);
if (incomplete) {
if (! eof) {
rc = sp_logclose(&x->log);
if (spunlikely(rc == -1))
return sp_e(s, SPEIO, "failed to close log file", x->epoch);
rc = sp_logcontinue(&x->log, s->e->dir, x->epoch);
if (spunlikely(rc == -1)) {
sp_logclose(&x->log);
return sp_e(s, SPEIO, "failed to reopen log file", x->epoch);
}
rc = sp_logeof(&x->log);
if (spunlikely(rc == -1)) {
sp_logclose(&x->log);
return sp_e(s, SPEIO, "failed to add eof marker", x->epoch);
}
}
rc = sp_logcompleteforce(&x->log);
if (spunlikely(rc == -1)) {
sp_logclose(&x->log);
return sp_e(s, SPEIO, "failed to complete log file", x->epoch);
}
}
return 0;
}
static int sp_dirrecover(sp *s)
{
sptrack t;
int rc = sp_trackinit(&t, &s->a, 1024);
if (spunlikely(rc == -1))
return sp_e(s, SPEOOM, "failed to allocate track");
/* recover from yongest epochs (biggest numbers) */
splist *i;
sp_listforeach_reverse(&s->rep.l, i){
spepoch *e = spcast(i, spepoch, link);
switch (e->recover) {
case SPRDB|SPRLOG:
case SPRDB:
sp_repset(&s->rep, e, SPDB);
rc = sp_recoverdb(s, e, &t);
if (spunlikely(rc == -1))
goto err;
if (e->recover == (SPRDB|SPRLOG)) {
rc = sp_epochrm(s->e->dir, e->epoch, "log");
if (spunlikely(rc == -1))
goto err;
}
break;
case SPRLOG|SPRDBI:
rc = sp_epochrm(s->e->dir, e->epoch, "db.incomplete");
if (spunlikely(rc == -1))
goto err;
case SPRLOG:
sp_repset(&s->rep, e, SPXFER);
rc = sp_recoverlog(s, e, 0);
if (spunlikely(rc == -1))
goto err;
break;
case SPRLOGI:
sp_repset(&s->rep, e, SPXFER);
rc = sp_recoverlog(s, e, 1);
if (spunlikely(rc == -1))
goto err;
break;
default:
/* corrupted states: */
/* db.incomplete */
/* log.incomplete + db.incomplete */
/* log.incomplete + db */
sp_trackfree(&t);
return sp_e(s, SPE, "repository is corrupted");
}
}
/*
* set maximum loaded psn as current one.
*/
s->psn = t.max;
sp_trackfree(&t);
return 0;
err:
sp_trackfree(&t);
return -1;
}
int sp_recover(sp *s)
{
int exists = sp_fileexists(s->e->dir);
int rc;
if (!exists) {
if (! (s->e->flags & SPO_CREAT))
return sp_e(s, SPE, "directory doesn't exists and no SPO_CREAT specified");
if (s->e->flags & SPO_RDONLY)
return sp_e(s, SPE, "directory doesn't exists");
rc = sp_dircreate(s);
} else {
rc = sp_diropen(s);
if (spunlikely(rc == -1))
return -1;
if (s->rep.n == 0)
return 0;
rc = sp_dirrecover(s);
}
return rc;
}

14
src/sophia/db/recover.h Normal file
View File

@@ -0,0 +1,14 @@
#ifndef SP_RECOVER_H_
#define SP_RECOVER_H_
/*
* sophia database
* sphia.org
*
* Copyright (c) Dmitry Simonenko
* BSD License
*/
int sp_recover(sp*);
#endif

111
src/sophia/db/ref.h Normal file
View File

@@ -0,0 +1,111 @@
#ifndef SP_KEY_H_
#define SP_KEY_H_
/*
* sophia database
* sphia.org
*
* Copyright (c) Dmitry Simonenko
* BSD License
*/
typedef struct spref spref;
typedef struct sprefset sprefset;
#define SPREFNONE 0
#define SPREFD 1
#define SPREFM 2
struct spref {
uint8_t type;
union {
spvh *vh;
spv *v;
} v;
} sppacked;
struct sprefset {
spref *r;
int used;
int max;
};
static inline char*
sp_refk(spref *r) {
switch (r->type) {
case SPREFD: return r->v.vh->key;
case SPREFM: return r->v.v->key;
}
return NULL;
}
static inline size_t
sp_refksize(spref *r) {
switch (r->type) {
case SPREFD: return r->v.vh->size;
case SPREFM: return r->v.v->size;
}
return 0;
}
static inline char*
sp_refv(spref *r, char *p) {
switch (r->type) {
case SPREFD: return p + r->v.vh->voffset;
case SPREFM: return sp_vv(r->v.v);
}
return NULL;
}
static inline size_t
sp_refvsize(spref *r) {
switch (r->type) {
case SPREFD: return r->v.vh->vsize;
case SPREFM: return sp_vvsize(r->v.v);
}
return 0;
}
static inline int
sp_refisdel(spref *r) {
register int flags = 0;
switch (r->type) {
case SPREFM: flags = r->v.v->flags;
break;
case SPREFD: flags = r->v.vh->flags;
break;
}
return (flags & SPDEL? 1: 0);
}
static inline int
sp_refsetinit(sprefset *s, spa *a, int count) {
s->r = sp_malloc(a, count * sizeof(spref));
if (spunlikely(s->r == NULL))
return -1;
s->used = 0;
s->max = count;
return 0;
}
static inline void
sp_refsetfree(sprefset *s, spa *a) {
if (s->r) {
sp_free(a, s->r);
s->r = NULL;
}
}
static inline void
sp_refsetadd(sprefset *s, spref *r) {
assert(s->used < s->max);
s->r[s->used] = *r;
s->used++;
}
static inline void
sp_refsetreset(sprefset *s) {
s->used = 0;
}
#endif

128
src/sophia/db/rep.c Normal file
View File

@@ -0,0 +1,128 @@
/*
* sophia database
* sphia.org
*
* Copyright (c) Dmitry Simonenko
* BSD License
*/
#include "sp.h"
void sp_repinit(sprep *r, spa *a) {
sp_listinit(&r->l);
r->a = a;
r->n = 0;
r->ndb = 0;
r->nxfer = 0;
r->epoch = 0;
}
void sp_repfree(sprep *r) {
splist *i, *n;
sp_listforeach_safe(&r->l, i, n) {
spepoch *e = spcast(i, spepoch, link);
sp_lockfree(&e->lock);
sp_free(r->a, e);
}
}
static inline int sp_repcmp(const void *p1, const void *p2) {
register const spepoch *a = *(spepoch**)p1;
register const spepoch *b = *(spepoch**)p2;
assert(a->epoch != b->epoch);
return (a->epoch > b->epoch)? 1: -1;
}
int sp_repprepare(sprep *r) {
spepoch **a = sp_malloc(r->a, sizeof(spepoch*) * r->n);
if (spunlikely(a == NULL))
return -1;
uint32_t epoch = 0;
int j = 0;
splist *i;
sp_listforeach(&r->l, i) {
a[j] = spcast(i, spepoch, link);
if (a[j]->epoch > epoch)
epoch = a[j]->epoch;
j++;
}
qsort(a, r->n, sizeof(spepoch*), sp_repcmp);
sp_listinit(&r->l);
j = 0;
while (j < r->n) {
sp_listinit(&a[j]->link);
sp_listappend(&r->l, &a[j]->link);
j++;
}
sp_free(r->a, a);
r->epoch = epoch;
return 0;
}
spepoch *sp_repmatch(sprep *r, uint32_t epoch) {
splist *i;
sp_listforeach(&r->l, i) {
spepoch *e = spcast(i, spepoch, link);
if (e->epoch == epoch)
return e;
}
return NULL;
}
spepoch *sp_repalloc(sprep *r, uint32_t epoch)
{
spepoch *e = sp_malloc(r->a, sizeof(spepoch));
if (spunlikely(e == NULL))
return NULL;
memset(e, 0, sizeof(spepoch));
e->recover = SPRNONE;
e->epoch = epoch;
e->type = SPUNDEF;
e->nupdate = 0;
e->n = 0;
e->ngc = 0;
sp_lockinit(&e->lock);
sp_fileinit(&e->db, r->a);
sp_fileinit(&e->log, r->a);
sp_listinit(&e->pages);
sp_listinit(&e->link);
return e;
}
void sp_repattach(sprep *r, spepoch *e) {
sp_listappend(&r->l, &e->link);
r->n++;
}
void sp_repdetach(sprep *r, spepoch *e) {
sp_listunlink(&e->link);
r->n--;
sp_repset(r, e, SPUNDEF);
}
void sp_repset(sprep *r, spepoch *e, spepochtype t) {
switch (t) {
case SPUNDEF:
if (e->type == SPXFER)
r->nxfer--;
else
if (e->type == SPDB)
r->ndb--;
break;
case SPLIVE:
assert(e->type == SPUNDEF);
break;
case SPXFER:
assert(e->type == SPLIVE || e->type == SPUNDEF);
r->nxfer++;
break;
case SPDB:
assert(e->type == SPXFER || e->type == SPUNDEF);
if (e->type == SPXFER)
r->nxfer--;
r->ndb++;
break;
}
e->type = t;
}

120
src/sophia/db/rep.h Normal file
View File

@@ -0,0 +1,120 @@
#ifndef SP_REP_H_
#define SP_REP_H_
/*
* sophia database
* sphia.org
*
* Copyright (c) Dmitry Simonenko
* BSD License
*/
typedef struct spepoch spepoch;
typedef struct sprep sprep;
enum spepochtype {
SPUNDEF,
SPLIVE,
SPXFER,
SPDB
};
typedef enum spepochtype spepochtype;
#define SPRNONE 0
#define SPRDB 1
#define SPRDBI 2
#define SPRLOG 4
#define SPRLOGI 8
struct spepoch {
uint32_t epoch;
uint32_t n; /* count of pages */
uint32_t ngc; /* count of gc pages */
uint32_t nupdate; /* count of updated keys */
spepochtype type; /* epoch life-cycle state */
uint32_t recover; /* recover status */
spfile log, db;
spspinlock lock; /* db lock */
splist pages; /* list of associated pages */
splist link;
};
struct sprep {
spa *a;
uint32_t epoch;
splist l;
int n;
int ndb;
int nxfer;
};
void sp_repinit(sprep*, spa*);
void sp_repfree(sprep*);
int sp_repprepare(sprep*);
spepoch *sp_repmatch(sprep *r, uint32_t epoch);
spepoch *sp_repalloc(sprep*, uint32_t);
void sp_repattach(sprep*, spepoch*);
void sp_repdetach(sprep*, spepoch*);
void sp_repset(sprep*, spepoch*, spepochtype);
static inline uint32_t
sp_repepoch(sprep *r) {
return r->epoch;
}
static inline void
sp_repepochincrement(sprep *r) {
r->epoch++;
}
static inline void
sp_replockall(sprep *r) {
register splist *i;
sp_listforeach(&r->l, i) {
register spepoch *e = spcast(i, spepoch, link);
sp_lock(&e->lock);
}
}
static inline void
sp_repunlockall(sprep *r) {
register splist *i;
sp_listforeach(&r->l, i) {
register spepoch *e = spcast(i, spepoch, link);
sp_unlock(&e->lock);
}
}
static inline spepoch*
sp_replive(sprep *r) {
register spepoch *e = spcast(r->l.prev, spepoch, link);
assert(e->type == SPLIVE);
return e;
}
static inline spepoch*
sp_repxfer(sprep *r) {
register splist *i;
sp_listforeach(&r->l, i) {
register spepoch *s = spcast(i, spepoch, link);
if (s->type == SPXFER)
return s;
}
return NULL;
}
static inline spepoch*
sp_repgc(sprep *r, float factor) {
register splist *i;
sp_listforeach(&r->l, i) {
register spepoch *s = spcast(i, spepoch, link);
if (s->type != SPDB)
continue;
if (s->ngc > (s->n * factor))
return s;
}
return NULL;
}
#endif

84
src/sophia/db/sophia.h Normal file
View File

@@ -0,0 +1,84 @@
#ifndef SOPHIA_H_
#define SOPHIA_H_
#ifdef __cplusplus
extern "C" {
#endif
/*
* sophia database
* sphia.org
*
* Copyright (c) Dmitry Simonenko
* BSD License
*/
#include <stdlib.h>
#include <stdint.h>
typedef void *(*spallocf)(void*, size_t, void*);
typedef int (*spcmpf)(char*, size_t, char*, size_t, void*);
typedef enum {
/* env related */
SPDIR, /* uint32_t, char* */
SPALLOC, /* spallocf, void* */
SPCMP, /* spcmpf, void* */
SPPAGE, /* uint32_t */
SPGC, /* int */
SPGCF, /* double */
SPGROW, /* uint32_t, double */
SPMERGE, /* int */
SPMERGEWM, /* uint32_t */
/* db related */
SPMERGEFORCE,
/* unrelated */
SPVERSION /* uint32_t*, uint32_t* */
} spopt;
enum {
SPO_RDONLY = 1,
SPO_RDWR = 2,
SPO_CREAT = 4,
SPO_SYNC = 8
};
typedef enum {
SPGT,
SPGTE,
SPLT,
SPLTE
} sporder;
typedef struct {
uint32_t epoch;
uint64_t psn;
uint32_t repn;
uint32_t repndb;
uint32_t repnxfer;
uint32_t catn;
uint32_t indexn;
uint32_t indexpages;
} spstat;
void *sp_env(void);
void *sp_open(void*);
int sp_ctl(void*, spopt, ...);
int sp_destroy(void*);
int sp_set(void*, const void*, size_t, const void*, size_t);
int sp_delete(void*, const void*, size_t);
int sp_get(void*, const void*, size_t, void**, size_t*);
void *sp_cursor(void*, sporder, const void*, size_t);
int sp_fetch(void*);
const char *sp_key(void*);
size_t sp_keysize(void*);
const char *sp_value(void*);
size_t sp_valuesize(void*);
char *sp_error(void*);
void sp_stat(void*, spstat*);
#ifdef __cplusplus
}
#endif
#endif

622
src/sophia/db/sp.c Normal file
View File

@@ -0,0 +1,622 @@
/*
* sophia database
* sphia.org
*
* Copyright (c) Dmitry Simonenko
* BSD License
*/
#include "sp.h"
static inline int sphot
cmpstd(char *a, size_t asz, char *b, size_t bsz, void *arg spunused) {
register size_t sz = (asz < bsz ? asz : bsz);
register int rc = memcmp(a, b, sz);
return (rc == 0 ? rc : (rc > 0 ? 1 : -1));
}
static inline void sp_envinit(spenv *e) {
e->m = SPMENV;
e->inuse = 0;
sp_einit(&e->e);
e->alloc = sp_allocstd;
e->allocarg = NULL;
e->cmp = cmpstd;
e->cmparg = NULL;
e->page = 2048;
e->dir = NULL;
e->flags = 0;
e->mergewm = 100000;
e->merge = 1;
e->dbnewsize = 2 * 1024 * 1024;
e->dbgrow = 1.4;
e->gc = 1;
e->gcfactor = 0.5;
}
static inline void sp_envfree(spenv *e) {
if (e->dir) {
free(e->dir);
e->dir = NULL;
}
sp_efree(&e->e);
}
static inline int sp_envvalidate(spenv *e)
{
/* check if environment is not already
* in use.
* do not set other environment error status
* in that case.
*/
if (e->inuse)
return -1;
if (e->dir == NULL)
return sp_ee(e, SPE, "directory is not specified");
if (e->mergewm < 2)
return sp_ee(e, SPE, "bad merge watermark count");
if (e->page < 2)
return sp_ee(e, SPE, "bad page size");
if ((e->page % 2) > 0)
return sp_ee(e, SPE, "bad page size must be even");
return 0;
}
void *sp_env(void) {
spenv *e = malloc(sizeof(spenv));
if (spunlikely(e == NULL))
return NULL;
sp_envinit(e);
return e;
}
static int sp_ctlenv(spenv *e, spopt opt, va_list args)
{
if (e->inuse)
return sp_ee(e, SPEOOM, "can't change env opts while in-use");
switch (opt) {
case SPDIR: {
uint32_t flags = va_arg(args, uint32_t);
char *path = va_arg(args, char*);
char *p = strdup(path);
if (spunlikely(p == NULL))
return sp_ee(e, SPEOOM, "failed to allocate memory");
if (spunlikely(e->dir)) {
free(e->dir);
e->dir = NULL;
}
e->dir = p;
e->flags = flags;
break;
}
case SPALLOC:
e->alloc = va_arg(args, spallocf);
e->allocarg = va_arg(args, void*);
break;
case SPCMP:
e->cmp = va_arg(args, spcmpf);
e->cmparg = va_arg(args, void*);
break;
case SPPAGE:
e->page = va_arg(args, uint32_t);
break;
case SPGC:
e->gc = va_arg(args, int);
break;
case SPGCF:
e->gcfactor = va_arg(args, double);
break;
case SPGROW:
e->dbnewsize = va_arg(args, uint32_t);
e->dbgrow = va_arg(args, double);
break;
case SPMERGE:
e->merge = va_arg(args, int);
break;
case SPMERGEWM:
e->mergewm = va_arg(args, uint32_t);
break;
default:
return sp_ee(e, SPE, "bad arguments");
}
return 0;
}
static int sp_ctldb(sp *s, spopt opt, va_list args spunused)
{
switch (opt) {
case SPMERGEFORCE:
if (s->e->merge)
return sp_e(s, SPE, "force merge doesn't work with merger thread active");
return sp_merge(s);
default:
return sp_e(s, SPE, "bad arguments");
}
return 0;
}
int sp_ctl(void *o, spopt opt, ...)
{
va_list args;
va_start(args, opt);
spmagic *magic = (spmagic*)o;
int rc;
if (opt == SPVERSION) {
uint32_t *major = va_arg(args, uint32_t*);
uint32_t *minor = va_arg(args, uint32_t*);
*major = SP_VERSION_MAJOR;
*minor = SP_VERSION_MINOR;
return 0;
}
switch (*magic) {
case SPMENV: rc = sp_ctlenv(o, opt, args);
break;
case SPMDB: rc = sp_ctldb(o, opt, args);
break;
default: rc = -1;
break;
}
va_end(args);
return rc;
}
int sp_rotate(sp *s)
{
int rc;
sp_repepochincrement(&s->rep);
/* allocate new epoch */
spepoch *e = sp_repalloc(&s->rep, sp_repepoch(&s->rep));
if (spunlikely(s == NULL))
return sp_e(s, SPEOOM, "failed to allocate repository");
/* create log file */
rc = sp_lognew(&e->log, s->e->dir, sp_repepoch(&s->rep));
if (spunlikely(rc == -1)) {
sp_free(&s->a, e);
return sp_e(s, SPEIO, "failed to create log file", e->epoch);
}
splogh h;
h.magic = SPMAGIC;
h.version[0] = SP_VERSION_MAJOR;
h.version[1] = SP_VERSION_MINOR;
sp_logadd(&e->log, (char*)&h, sizeof(h));
rc = sp_logflush(&e->log);
if (spunlikely(rc == -1)) {
sp_logclose(&e->log);
sp_free(&s->a, e);
return sp_e(s, SPEIO, "failed to write log file", e->epoch);
}
/* attach epoch and mark it is as live */
sp_repattach(&s->rep, e);
sp_repset(&s->rep, e, SPLIVE);
return 0;
}
static inline int sp_closerep(sp *s)
{
int rcret = 0;
int rc = 0;
splist *i, *n;
sp_listforeach_safe(&s->rep.l, i, n) {
spepoch *e = spcast(i, spepoch, link);
switch (e->type) {
case SPUNDEF:
/* this type is true to a epoch that has beed
* scheduled for a recovery, but not happen to
* proceed yet. */
break;
case SPLIVE:
if (e->nupdate == 0) {
rc = sp_logunlink(&e->log);
if (spunlikely(rc == -1))
rcret = sp_e(s, SPEIO, "failed to unlink log file", e->epoch);
rc = sp_logclose(&e->log);
if (spunlikely(rc == -1))
rcret = sp_e(s, SPEIO, "failed to close log file", e->epoch);
break;
} else {
rc = sp_logeof(&e->log);
if (spunlikely(rc == -1))
rcret = sp_e(s, SPEIO, "failed to write eof marker", e->epoch);
}
case SPXFER:
rc = sp_logcomplete(&e->log);
if (spunlikely(rc == -1))
rcret = sp_e(s, SPEIO, "failed to complete log file", e->epoch);
rc = sp_logclose(&e->log);
if (spunlikely(rc == -1))
rcret = sp_e(s, SPEIO, "failed to close log file", e->epoch);
break;
case SPDB:
rc = sp_mapclose(&e->db);
if (spunlikely(rc == -1))
rcret = sp_e(s, SPEIO, "failed to close db file", e->epoch);
break;
}
sp_free(&s->a, e);
}
return rcret;
}
static inline int sp_close(sp *s)
{
int rcret = 0;
int rc = 0;
s->stop = 1;
if (s->e->merge) {
rc = sp_taskstop(&s->merger);
if (spunlikely(rc == -1))
rcret = sp_e(s, SPESYS, "failed to stop merger thread");
}
sp_refsetfree(&s->refs, &s->a);
rc = sp_closerep(s);
if (spunlikely(rc == -1))
rcret = -1;
sp_ifree(&s->i0);
sp_ifree(&s->i1);
sp_catfree(&s->s);
s->e->inuse = 0;
sp_lockfree(&s->lockr);
sp_lockfree(&s->locks);
sp_lockfree(&s->locki);
return rcret;
}
static void *merger(void *arg)
{
sptask *self = arg;
sp *s = self->arg;
do {
sp_lock(&s->locki);
int merge = s->i->count > s->e->mergewm;
sp_unlock(&s->locki);
if (! merge)
continue;
int rc = sp_merge(s);
if (spunlikely(rc == -1)) {
sp_taskdone(self);
return NULL;
}
} while (sp_taskwait(self));
return NULL;
}
void *sp_open(void *e)
{
spenv *env = e;
assert(env->m == SPMENV);
int rc = sp_envvalidate(env);
if (spunlikely(rc == -1))
return NULL;
spa a;
sp_allocinit(&a, env->alloc, env->allocarg);
sp *s = sp_malloc(&a, sizeof(sp));
if (spunlikely(s == NULL)) {
sp_e(s, SPEOOM, "failed to allocate db handle");
return NULL;
}
memset(s, 0, sizeof(sp));
s->m = SPMDB;
s->e = env;
s->e->inuse = 1;
memcpy(&s->a, &a, sizeof(s->a));
/* init locks */
sp_lockinit(&s->lockr);
sp_lockinit(&s->locks);
sp_lockinit(&s->locki);
s->lockc = 0;
/* init key index */
rc = sp_iinit(&s->i0, &s->a, 1024, s->e->cmp, s->e->cmparg);
if (spunlikely(rc == -1)) {
sp_e(s, SPEOOM, "failed to allocate key index");
goto e0;
}
rc = sp_iinit(&s->i1, &s->a, 1024, s->e->cmp, s->e->cmparg);
if (spunlikely(rc == -1)) {
sp_e(s, SPEOOM, "failed to allocate key index");
goto e1;
}
s->i = &s->i0;
/* init page index */
s->psn = 0;
rc = sp_catinit(&s->s, &s->a, 512, s->e->cmp, s->e->cmparg);
if (spunlikely(rc == -1)) {
sp_e(s, SPEOOM, "failed to allocate page index");
goto e2;
}
sp_repinit(&s->rep, &s->a);
rc = sp_recover(s);
if (spunlikely(rc == -1))
goto e3;
/* do not create new live epoch in read-only mode */
if (! (s->e->flags & SPO_RDONLY)) {
rc = sp_rotate(s);
if (spunlikely(rc == -1))
goto e3;
}
s->stop = 0;
rc = sp_refsetinit(&s->refs, &s->a, s->e->page);
if (spunlikely(rc == -1)) {
sp_e(s, SPEOOM, "failed to allocate key buffer");
goto e3;
}
if (s->e->merge) {
rc = sp_taskstart(&s->merger, merger, s);
if (spunlikely(rc == -1)) {
sp_e(s, SPESYS, "failed to start merger thread");
goto e4;
}
sp_taskwakeup(&s->merger);
}
return s;
e4: sp_refsetfree(&s->refs, &s->a);
e3: sp_closerep(s);
sp_catfree(&s->s);
e2: sp_ifree(&s->i1);
e1: sp_ifree(&s->i0);
e0: s->e->inuse = 0;
sp_lockfree(&s->lockr);
sp_lockfree(&s->locks);
sp_lockfree(&s->locki);
sp_free(&a, s);
return NULL;
}
int sp_destroy(void *o)
{
spmagic *magic = (spmagic*)o;
spa *a = NULL;
int rc = 0;
switch (*magic) {
case SPMNONE:
assert(0);
return -1;
case SPMENV: {
spenv *env = (spenv*)o;
if (env->inuse)
return -1;
sp_envfree(env);
*magic = SPMNONE;
free(o);
return 0;
}
case SPMCUR: {
spc *c = (spc*)o;
a = &c->s->a;
sp_cursorclose(c);
break;
}
case SPMDB: {
sp *s = (sp*)o;
a = &s->a;
rc = sp_close(s);
break;
}
default:
return -1;
}
*magic = SPMNONE;
sp_free(a, o);
return rc;
}
char *sp_error(void *o)
{
spmagic *magic = (spmagic*)o;
spe *e = NULL;
switch (*magic) {
case SPMDB: {
sp *s = o;
e = &s->e->e;
break;
}
case SPMENV: {
spenv *env = o;
e = &env->e;
break;
}
default:
assert(0);
return NULL;
}
if (! sp_eis(e))
return NULL;
return e->e;
}
static inline int
sp_do(sp *s, int op, void *k, size_t ksize, void *v, size_t vsize)
{
/* allocate new version.
*
* try to reduce lock contention by making the alloc and
* the crc calculation before log write.
*/
spv *n = sp_vnewv(s, k, ksize, v, vsize);
if (spunlikely(n == NULL))
return sp_e(s, SPEOOM, "failed to allocate version");
/* prepare log record */
spvh h = {
.crc = 0,
.size = ksize,
.voffset = 0,
.vsize = vsize,
.flags = op
};
/* calculate crc */
uint32_t crc;
crc = sp_crc32c(0, k, ksize);
crc = sp_crc32c(crc, v, vsize);
h.crc = sp_crc32c(crc, &h.size, sizeof(spvh) - sizeof(uint32_t));
sp_lock(&s->lockr);
sp_lock(&s->locki);
/* write to current live epoch log */
spepoch *live = sp_replive(&s->rep);
sp_filesvp(&live->log);
sp_logadd(&live->log, &h, sizeof(spvh));
sp_logadd(&live->log, k, ksize);
sp_logadd(&live->log, v, vsize);
int rc = sp_logflush(&live->log);
if (spunlikely(rc == -1)) {
sp_free(&s->a, n);
sp_logrlb(&live->log);
sp_unlock(&s->locki);
sp_unlock(&s->lockr);
return sp_e(s, SPEIO, "failed to write log file", live->epoch);
}
/* add new version to the index */
spv *old = NULL;
n->epoch = live->epoch;
n->flags = op;
n->crc = crc;
rc = sp_iset(s->i, n, &old);
if (spunlikely(rc == -1)) {
sp_free(&s->a, n);
sp_unlock(&s->locki);
sp_unlock(&s->lockr);
return sp_e(s, SPEOOM, "failed to allocate key index page");
}
sp_unlock(&s->locki);
sp_unlock(&s->lockr);
if (old)
sp_free(&s->a, old);
/* wake up merger on merge watermark reached */
live->nupdate++;
if ((live->nupdate % s->e->mergewm) == 0) {
if (splikely(s->e->merge))
sp_taskwakeup(&s->merger);
}
return 0;
}
int sp_set(void *o, const void *k, size_t ksize, const void *v, size_t vsize)
{
sp *s = o;
assert(s->m == SPMDB);
if (spunlikely(sp_eis(&s->e->e)))
return -1;
if (spunlikely(s->e->flags & SPO_RDONLY))
return sp_e(s, SPE, "db handle is read-only");
if (spunlikely(ksize > UINT16_MAX))
return sp_e(s, SPE, "key size limit reached");
if (spunlikely(vsize > UINT32_MAX))
return sp_e(s, SPE, "value size limit reached");
return sp_do(s, SPSET, (char*)k, ksize, (char*)v, vsize);
}
int sp_delete(void *o, const void *k, size_t ksize)
{
sp *s = o;
assert(s->m == SPMDB);
if (spunlikely(sp_eis(&s->e->e)))
return -1;
if (spunlikely(s->e->flags & SPO_RDONLY))
return sp_e(s, SPE, "db handle is read-only");
if (spunlikely(ksize > UINT16_MAX))
return sp_e(s, SPE, "key size limit reached");
return sp_do(s, SPDEL, (char*)k, ksize, NULL, 0);
}
static inline int
sp_checkro(sp *s, size_t ksize)
{
if (spunlikely(sp_eis(&s->e->e)))
return -1;
if (spunlikely(ksize > UINT16_MAX))
return sp_e(s, SPE, "key size limit reached");
return 0;
}
int sp_get(void *o, const void *k, size_t ksize, void **v, size_t *vsize)
{
sp *s = o;
assert(s->m == SPMDB);
if (spunlikely(sp_checkro(s, ksize) == -1))
return -1;
return sp_match(s, (char*)k, ksize, v, vsize);
}
void *sp_cursor(void *o, sporder order, const void *k, size_t ksize)
{
sp *s = o;
assert(s->m == SPMDB);
if (spunlikely(sp_checkro(s, ksize) == -1))
return NULL;
spc *c = sp_malloc(&s->a, sizeof(spc));
if (spunlikely(c == NULL)) {
sp_e(s, SPEOOM, "failed to allocate cursor handle");
return NULL;
}
memset(c, 0, sizeof(spc));
sp_cursoropen(c, s, order, (char*)k, ksize);
return c;
}
int sp_fetch(void *o) {
spc *c = o;
assert(c->m == SPMCUR);
if (spunlikely(sp_eis(&c->s->e->e)))
return -1;
return sp_iterate(c);
}
const char *sp_key(void *o)
{
spc *c = o;
assert(c->m == SPMCUR);
return sp_refk(&c->r);
}
size_t sp_keysize(void *o)
{
spc *c = o;
assert(c->m == SPMCUR);
return sp_refksize(&c->r);
}
const char *sp_value(void *o)
{
spc *c = o;
assert(c->m == SPMCUR);
return sp_refv(&c->r, (char*)c->ph);
}
size_t sp_valuesize(void *o)
{
spc *c = o;
assert(c->m == SPMCUR);
return sp_refvsize(&c->r);
}
void sp_stat(void *o, spstat *stat)
{
spmagic *magic = (spmagic*)o;
if (*magic != SPMDB) {
memset(stat, 0, sizeof(*stat));
return;
}
sp *s = o;
sp_lock(&s->lockr);
sp_lock(&s->locki);
sp_lock(&s->locks);
stat->epoch = s->rep.epoch;
stat->psn = s->psn;
stat->repn = s->rep.n;
stat->repndb = s->rep.ndb;
stat->repnxfer = s->rep.nxfer;
stat->catn = s->s.count;
stat->indexn = s->i->count;
stat->indexpages = s->i->icount;
sp_unlock(&s->locks);
sp_unlock(&s->locki);
sp_unlock(&s->lockr);
}

50
src/sophia/db/sp.h Normal file
View File

@@ -0,0 +1,50 @@
#ifndef SP_H_
#define SP_H_
/*
* sophia database
* sphia.org
*
* Copyright (c) Dmitry Simonenko
* BSD License
*/
#define _GNU_SOURCE 1
#include <stdlib.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdint.h>
#include <inttypes.h>
#include <limits.h>
#include <string.h>
#include <assert.h>
#include <pthread.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/uio.h>
#include <errno.h>
#include "sophia.h"
#include "macro.h"
#include "crc.h"
#include "lock.h"
#include "list.h"
#include "e.h"
#include "a.h"
#include "meta.h"
#include "file.h"
#include "ref.h"
#include "i.h"
#include "rep.h"
#include "cat.h"
#include "task.h"
#include "core.h"
#include "util.h"
#include "recover.h"
#include "merge.h"
#include "gc.h"
#include "cursor.h"
#endif

70
src/sophia/db/task.h Normal file
View File

@@ -0,0 +1,70 @@
#ifndef SP_TASK_H_
#define SP_TASK_H_
/*
* sophia database
* sphia.org
*
* Copyright (c) Dmitry Simonenko
* BSD License
*/
typedef struct sptask sptask;
struct sptask {
volatile int run;
void *arg;
pthread_t id;
pthread_mutex_t l;
pthread_cond_t c;
};
static inline int
sp_taskstart(sptask *t, void*(*f)(void*), void *arg) {
t->run = 1;
t->arg = arg;
pthread_mutex_init(&t->l, NULL);
pthread_cond_init(&t->c, NULL);
return pthread_create(&t->id, NULL, f, t);
}
static inline int
sp_taskstop(sptask *t) {
pthread_mutex_lock(&t->l);
if (t->run == 0) {
pthread_mutex_unlock(&t->l);
return 0;
}
t->run = 0;
pthread_cond_signal(&t->c);
pthread_mutex_unlock(&t->l);
return pthread_join(t->id, NULL);
}
static inline void
sp_taskwakeup(sptask *t) {
pthread_mutex_lock(&t->l);
pthread_cond_signal(&t->c);
pthread_mutex_unlock(&t->l);
}
static inline int
sp_taskwait(sptask *t) {
pthread_mutex_lock(&t->l);
if (t->run == 0) {
pthread_mutex_unlock(&t->l);
return 0;
}
pthread_cond_wait(&t->c, &t->l);
pthread_mutex_unlock(&t->l);
return t->run;
}
static inline void
sp_taskdone(sptask *t) {
pthread_mutex_lock(&t->l);
t->run = 0;
pthread_mutex_unlock(&t->l);
}
#endif

99
src/sophia/db/track.h Normal file
View File

@@ -0,0 +1,99 @@
#ifndef SP_TRACK_H_
#define SP_TRACK_H_
/*
* sophia database
* sphia.org
*
* Copyright (c) Dmitry Simonenko
* BSD License
*/
typedef struct sptrack sptrack;
struct sptrack {
spa *a;
uint64_t max;
uint64_t *i;
int count;
int size;
};
static inline int
sp_trackinit(sptrack *t, spa *a, int size) {
t->a = a;
t->max = 0;
t->count = 0;
t->size = size;
int sz = size * sizeof(uint64_t);
t->i = sp_malloc(a, sz);
if (spunlikely(t->i == NULL))
return -1;
memset(t->i, 0, sz);
return 0;
}
static inline void sp_trackfree(sptrack *t) {
if (spunlikely(t->i == NULL))
return;
sp_free(t->a, t->i);
t->i = NULL;
}
static inline void
sp_trackinsert(sptrack *t, uint64_t id) {
uint32_t pos = id % t->size;
for (;;) {
if (spunlikely(t->i[pos] != 0)) {
pos = (pos + 1) % t->size;
continue;
}
if (id > t->max)
t->max = id;
t->i[pos] = id;
break;
}
t->count++;
}
static inline int
sp_trackresize(sptrack *t) {
sptrack nt;
int rc = sp_trackinit(&nt, t->a, t->size * 2);
if (spunlikely(rc == -1))
return -1;
int i = 0;
while (i < t->size) {
if (t->i[i])
sp_trackinsert(&nt, t->i[i]);
i++;
}
sp_trackfree(t);
*t = nt;
return 0;
}
static inline int
sp_trackset(sptrack *t, uint64_t id) {
if (spunlikely(t->count > (t->size / 2)))
if (spunlikely(sp_trackresize(t) == -1))
return -1;
sp_trackinsert(t, id);
return 0;
}
static inline int
sp_trackhas(sptrack *t, uint64_t id) {
uint32_t pos = id % t->size;
for (;;) {
if (spunlikely(t->i[pos] == 0))
return 0;
if (t->i[pos] == id)
return 1;
pos = (pos + 1) % t->size;
continue;
}
return 0;
}
#endif

105
src/sophia/db/util.c Normal file
View File

@@ -0,0 +1,105 @@
/*
* sophia database
* sphia.org
*
* Copyright (c) Dmitry Simonenko
* BSD License
*/
#include "sp.h"
char *sp_memdup(sp *s, void *src, size_t size)
{
char *v = sp_malloc(&s->a, size);
if (spunlikely(v == NULL))
return NULL;
memcpy(v, src, size);
return v;
}
sppage *sp_pagenew(sp *s, spepoch *e) {
sppage *page = sp_malloc(&s->a, sizeof(sppage));
if (spunlikely(page == NULL))
return NULL;
memset(page, 0, sizeof(sppage));
page->epoch = e;
sp_listinit(&page->link);
return page;
}
void sp_pageattach(sppage *p) {
assert(p->epoch != NULL);
sp_listappend(&((spepoch*)p->epoch)->pages, &p->link);
}
void sp_pagedetach(sppage *p) {
assert(p->epoch != NULL);
sp_listunlink(&p->link);
}
void sp_pagefree(sp *s, sppage *p) {
sp_listunlink(&p->link);
sp_free(&s->a, p->min);
sp_free(&s->a, p->max);
sp_free(&s->a, p);
}
static inline spv*
sp_vnewof(sp *s, void *k, uint16_t size, int reserve) {
spv *v = sp_malloc(&s->a, sizeof(spv) + size + reserve);
if (spunlikely(v == NULL))
return NULL;
v->epoch = 0;
v->size = size;
v->flags = 0;
memcpy(v->key, k, size);
return v;
}
spv *sp_vnew(sp *s, void *k, uint16_t size) {
return sp_vnewof(s, k, size, 0);
}
spv *sp_vnewv(sp *s, void *k, uint16_t size, void *v, uint32_t vsize)
{
spv *vn = sp_vnewof(s, k, size, sizeof(uint32_t) + vsize);
if (spunlikely(vn == NULL))
return NULL;
memcpy(vn->key + vn->size, &vsize, sizeof(uint32_t));
memcpy(vn->key + vn->size + sizeof(uint32_t), v, vsize);
return vn;
}
spv *sp_vnewh(sp *s, spvh *v) {
spv *vn = sp_vnewof(s, v->key, v->size, 0);
if (spunlikely(vn == NULL))
return NULL;
vn->flags = v->flags;
return vn;
}
spv *sp_vdup(sp *s, spv *v)
{
spv *vn = sp_malloc(&s->a, sizeof(spv) + v->size);
if (spunlikely(vn == NULL))
return NULL;
memcpy(vn, v, sizeof(spv) + v->size);
return vn;
}
spv *sp_vdupref(sp *s, spref *r, uint32_t epoch)
{
spv *vn = NULL;
switch (r->type) {
case SPREFM: vn = sp_vdup(s, r->v.v);
break;
case SPREFD: vn = sp_vnewh(s, r->v.vh);
break;
}
if (spunlikely(vn == NULL))
return NULL;
vn->epoch = epoch;
vn->flags = 0;
return vn;
}

25
src/sophia/db/util.h Normal file
View File

@@ -0,0 +1,25 @@
#ifndef SP_UTIL_H_
#define SP_UTIL_H_
/*
* sophia database
* sphia.org
*
* Copyright (c) Dmitry Simonenko
* BSD License
*/
char *sp_memdup(sp*, void*, size_t);
spv *sp_vnew(sp*, void*, uint16_t);
spv *sp_vnewv(sp*, void*, uint16_t, void*, uint32_t);
spv *sp_vnewh(sp*, spvh*);
spv *sp_vdup(sp*, spv*);
spv *sp_vdupref(sp*, spref*, uint32_t);
sppage *sp_pagenew(sp*, spepoch*);
void sp_pagefree(sp*, sppage*);
void sp_pageattach(sppage*);
void sp_pagedetach(sppage*);
#endif

7
src/sophia/makefile Normal file
View File

@@ -0,0 +1,7 @@
all:
@(cd db; $(MAKE))
@(cd test; $(MAKE))
clean:
@(cd db; $(MAKE) clean)
@(cd test; $(MAKE) clean)

30
src/sophia/sophia.gyp Normal file
View File

@@ -0,0 +1,30 @@
{
'targets': [
{
'target_name': 'sophia',
'product_prefix': 'lib',
'type': 'static_library',
'include_dirs': ['db'],
'link_settings': {
'libraries': ['-lpthread'],
},
'direct_dependent_settings': {
'include_dirs': ['db'],
},
'sources': [
'db/cat.c',
'db/crc.c',
'db/cursor.c',
'db/e.c',
'db/file.c',
'db/gc.c',
'db/i.c',
'db/merge.c',
'db/recover.c',
'db/rep.c',
'db/sp.c',
'db/util.c',
],
},
],
}

911
src/sophia/test/common.c Normal file
View File

@@ -0,0 +1,911 @@
/*
* sophia database
* sphia.org
*
* Copyright (c) Dmitry Simonenko
* BSD License
*/
#include <sophia.h>
#include "test.h"
static char *dbrep = "./rep";
static inline int
cmp(char *a, size_t asz, char *b, size_t bsz, void *arg) {
register uint32_t av = *(uint32_t*)a;
register uint32_t bv = *(uint32_t*)b;
if (av == bv)
return 0;
return (av > bv) ? 1 : -1;
}
static void
env(void) {
void *env = sp_env();
t( env != NULL );
t( sp_ctl(env, SPDIR, SPO_CREAT|SPO_RDWR, dbrep) == 0 );
t( sp_ctl(env, SPCMP, cmp, NULL) == 0 );
t( sp_destroy(env) == 0 );
}
static void
env_opts(void) {
void *env = sp_env();
t( env != NULL );
t( sp_ctl(env, SPDIR, SPO_CREAT|SPO_RDWR, dbrep) == 0 );
t( sp_ctl(env, SPCMP, cmp, NULL) == 0 );
t( sp_ctl(env, SPALLOC, NULL, NULL) == 0 );
t( sp_ctl(env, SPPAGE, 1024) == 0 );
t( sp_ctl(env, SPGC, 0.5) == 0 );
t( sp_ctl(env, SPGROW, 16 * 1024 * 1024, 2.0) == 0 );
t( sp_ctl(env, SPMERGE, 1) == 0 );
uint32_t major, minor;
t( sp_ctl(NULL, SPVERSION, &major, &minor) == 0 );
t( major == 1 );
t( minor == 1 );
t( sp_destroy(env) == 0 );
}
static void
open_ro_creat(void) {
void *env = sp_env();
t( env != NULL );
t( sp_ctl(env, SPDIR, SPO_CREAT|SPO_RDONLY, dbrep) == 0 );
t( sp_ctl(env, SPCMP, cmp, NULL) == 0 );
void *db = sp_open(env);
t( db == NULL );
t( sp_error(env) != NULL );
t( sp_destroy(env) == 0 );
}
static void
open_rdwr(void) {
void *env = sp_env();
t( env != NULL );
t( sp_ctl(env, SPDIR, SPO_RDWR, dbrep) == 0 );
t( sp_ctl(env, SPCMP, cmp, NULL) == 0 );
void *db = sp_open(env);
t( db == NULL );
t( sp_error(env) != NULL );
t( sp_destroy(env) == 0 );
}
static void
open_rdwr_creat(void) {
void *env = sp_env();
t( env != NULL );
t( sp_ctl(env, SPDIR, SPO_CREAT|SPO_RDWR, dbrep) == 0 );
t( sp_ctl(env, SPCMP, cmp, NULL) == 0 );
void *db = sp_open(env);
t( db != NULL );
t( sp_destroy(db) == 0 );
t( sp_destroy(env) == 0 );
t( rmrf(dbrep) == 0 );
}
static void
open_reopen(void) {
void *env = sp_env();
t( env != NULL );
t( sp_ctl(env, SPDIR, SPO_CREAT|SPO_RDWR, dbrep) == 0 );
t( sp_ctl(env, SPCMP, cmp, NULL) == 0 );
void *db = sp_open(env);
t( db != NULL );
t( sp_destroy(db) == 0 );
db = sp_open(env);
t( db != NULL );
t( sp_destroy(db) == 0 );
t( sp_destroy(env) == 0 );
t( rmrf(dbrep) == 0 );
}
static void
open_reopen_ro(void) {
void *env = sp_env();
t( env != NULL );
t( sp_ctl(env, SPDIR, SPO_CREAT|SPO_RDWR, dbrep) == 0 );
t( sp_ctl(env, SPCMP, cmp, NULL) == 0 );
void *db = sp_open(env);
t( db != NULL );
t( sp_destroy(db) == 0 );
t( sp_ctl(env, SPDIR, SPO_RDONLY, dbrep) == 0 );
db = sp_open(env);
t( db != NULL );
t( sp_destroy(db) == 0 );
t( sp_destroy(env) == 0 );
t( rmrf(dbrep) == 0 );
}
static void
set(void) {
void *env = sp_env();
t( env != NULL );
t( sp_ctl(env, SPDIR, SPO_CREAT|SPO_RDWR, dbrep) == 0 );
t( sp_ctl(env, SPCMP, cmp, NULL) == 0 );
void *db = sp_open(env);
t( db != NULL );
uint32_t k = 1, v = 1;
t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0);
t( sp_destroy(db) == 0 );
t( sp_destroy(env) == 0 );
t( rmrf(dbrep) == 0 );
}
static void
set_get(void) {
void *env = sp_env();
t( env != NULL );
t( sp_ctl(env, SPDIR, SPO_CREAT|SPO_RDWR, dbrep) == 0 );
t( sp_ctl(env, SPCMP, cmp, NULL) == 0 );
void *db = sp_open(env);
t( db != NULL );
uint32_t k = 1, v = 1;
t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );
size_t vsize = 0;
void *vp = NULL;
t( sp_get(db, &k, sizeof(k), &vp, &vsize) == 1 );
t( vsize == sizeof(v) );
t( *(uint32_t*)vp == v );
free(vp);
t( sp_destroy(db) == 0 );
t( sp_destroy(env) == 0 );
t( rmrf(dbrep) == 0 );
}
static void
set_get_zerovalue(void) {
void *env = sp_env();
t( env != NULL );
t( sp_ctl(env, SPDIR, SPO_CREAT|SPO_RDWR, dbrep) == 0 );
t( sp_ctl(env, SPCMP, cmp, NULL) == 0 );
void *db = sp_open(env);
t( db != NULL );
uint32_t k = 1;
t( sp_set(db, &k, sizeof(k), NULL, 0) == 0 );
size_t vsize = 0;
void *vp = NULL;
t( sp_get(db, &k, sizeof(k), &vp, &vsize) == 1 );
t( vsize == 0 );
t( vp == NULL );
t( sp_destroy(db) == 0 );
t( sp_destroy(env) == 0 );
t( rmrf(dbrep) == 0 );
}
static void
replace(void) {
void *env = sp_env();
t( env != NULL );
t( sp_ctl(env, SPDIR, SPO_CREAT|SPO_RDWR, dbrep) == 0 );
t( sp_ctl(env, SPCMP, cmp, NULL) == 0 );
void *db = sp_open(env);
t( db != NULL );
uint32_t k = 1, v = 1;
t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0);
t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0);
t( sp_destroy(db) == 0 );
t( sp_destroy(env) == 0 );
t( rmrf(dbrep) == 0 );
}
static void
replace_get(void) {
void *env = sp_env();
t( env != NULL );
t( sp_ctl(env, SPDIR, SPO_CREAT|SPO_RDWR, dbrep) == 0 );
t( sp_ctl(env, SPCMP, cmp, NULL) == 0 );
void *db = sp_open(env);
t( db != NULL );
uint32_t k = 1, v = 1;
t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0);
size_t vsize = 0;
void *vp = NULL;
t( sp_get(db, &k, sizeof(k), &vp, &vsize) == 1 );
t( vsize == sizeof(v) );
t( *(uint32_t*)vp == 1 );
free(vp);
v = 2;
t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0);
t( sp_get(db, &k, sizeof(k), &vp, &vsize) == 1 );
t( vsize == sizeof(v) );
t( *(uint32_t*)vp == 2 );
free(vp);
t( sp_destroy(db) == 0 );
t( sp_destroy(env) == 0 );
t( rmrf(dbrep) == 0 );
}
static void
set_delete(void) {
void *env = sp_env();
t( env != NULL );
t( sp_ctl(env, SPDIR, SPO_CREAT|SPO_RDWR, dbrep) == 0 );
t( sp_ctl(env, SPCMP, cmp, NULL) == 0 );
void *db = sp_open(env);
t( db != NULL );
uint32_t k = 1, v = 1;
t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );
t( sp_delete(db, &k, sizeof(k)) == 0 );
t( sp_destroy(db) == 0 );
t( sp_destroy(env) == 0 );
t( rmrf(dbrep) == 0 );
}
static void
set_delete_get(void) {
void *env = sp_env();
t( env != NULL );
t( sp_ctl(env, SPDIR, SPO_CREAT|SPO_RDWR, dbrep) == 0 );
t( sp_ctl(env, SPCMP, cmp, NULL) == 0 );
void *db = sp_open(env);
t( db != NULL );
uint32_t k = 1, v = 1;
t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );
t( sp_delete(db, &k, sizeof(k)) == 0 );
size_t vsize = 0;
void *vp = NULL;
t( sp_get(db, &k, sizeof(k), &vp, &vsize) == 0 );
t( sp_destroy(db) == 0 );
t( sp_destroy(env) == 0 );
t( rmrf(dbrep) == 0 );
}
static void
set_delete_set_get(void) {
void *env = sp_env();
t( env != NULL );
t( sp_ctl(env, SPDIR, SPO_CREAT|SPO_RDWR, dbrep) == 0 );
t( sp_ctl(env, SPCMP, cmp, NULL) == 0 );
void *db = sp_open(env);
t( db != NULL );
uint32_t k = 1, v = 1;
t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );
t( sp_delete(db, &k, sizeof(k)) == 0 );
v = 2;
t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );
size_t vsize = 0;
void *vp = NULL;
t( sp_get(db, &k, sizeof(k), &vp, &vsize) == 1 );
t( vsize == sizeof(v) );
t( *(uint32_t*)vp == 2 );
free(vp);
t( sp_destroy(db) == 0 );
t( sp_destroy(env) == 0 );
t( rmrf(dbrep) == 0 );
}
static void
delete(void) {
void *env = sp_env();
t( env != NULL );
t( sp_ctl(env, SPDIR, SPO_CREAT|SPO_RDWR, dbrep) == 0 );
t( sp_ctl(env, SPCMP, cmp, NULL) == 0 );
void *db = sp_open(env);
t( db != NULL );
uint32_t k = 1;
t( sp_delete(db, &k, sizeof(k)) == 0 );
t( sp_destroy(db) == 0 );
t( sp_destroy(env) == 0 );
t( rmrf(dbrep) == 0 );
}
static void
delete_set_get(void) {
void *env = sp_env();
t( env != NULL );
t( sp_ctl(env, SPDIR, SPO_CREAT|SPO_RDWR, dbrep) == 0 );
t( sp_ctl(env, SPCMP, cmp, NULL) == 0 );
void *db = sp_open(env);
t( db != NULL );
uint32_t k = 1, v = 1;
t( sp_delete(db, &k, sizeof(k)) == 0 );
v = 2;
t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );
size_t vsize = 0;
void *vp = NULL;
t( sp_get(db, &k, sizeof(k), &vp, &vsize) == 1 );
t( vsize == sizeof(v) );
t( *(uint32_t*)vp == 2 );
free(vp);
t( sp_destroy(db) == 0 );
t( sp_destroy(env) == 0 );
t( rmrf(dbrep) == 0 );
}
static void
cursor(void) {
void *env = sp_env();
t( env != NULL );
t( sp_ctl(env, SPDIR, SPO_CREAT|SPO_RDWR, dbrep) == 0 );
t( sp_ctl(env, SPCMP, cmp, NULL) == 0 );
void *db = sp_open(env);
t( db != NULL );
void *cur = sp_cursor(db, SPGTE, NULL, 0);
t( cur != NULL );
t( sp_destroy(cur) == 0 );
t( sp_destroy(db) == 0 );
t( sp_destroy(env) == 0 );
t( rmrf(dbrep) == 0 );
}
static void
fetch_gte_empty(void) {
void *env = sp_env();
t( env != NULL );
t( sp_ctl(env, SPDIR, SPO_CREAT|SPO_RDWR, dbrep) == 0 );
t( sp_ctl(env, SPCMP, cmp, NULL) == 0 );
void *db = sp_open(env);
t( db != NULL );
void *cur = sp_cursor(db, SPGTE, NULL, 0);
t( cur != NULL );
t( sp_fetch(cur) == 0 );
t( sp_fetch(cur) == 0 );
t( sp_destroy(cur) == 0 );
t( sp_destroy(db) == 0 );
t( sp_destroy(env) == 0 );
t( rmrf(dbrep) == 0 );
}
static void
fetch_gt_empty(void) {
void *env = sp_env();
t( env != NULL );
t( sp_ctl(env, SPDIR, SPO_CREAT|SPO_RDWR, dbrep) == 0 );
t( sp_ctl(env, SPCMP, cmp, NULL) == 0 );
void *db = sp_open(env);
t( db != NULL );
void *cur = sp_cursor(db, SPGT, NULL, 0);
t( cur != NULL );
t( sp_fetch(cur) == 0 );
t( sp_fetch(cur) == 0 );
t( sp_destroy(cur) == 0 );
t( sp_destroy(db) == 0 );
t( sp_destroy(env) == 0 );
t( rmrf(dbrep) == 0 );
}
static void
fetch_lte_empty(void) {
void *env = sp_env();
t( env != NULL );
t( sp_ctl(env, SPDIR, SPO_CREAT|SPO_RDWR, dbrep) == 0 );
t( sp_ctl(env, SPCMP, cmp, NULL) == 0 );
void *db = sp_open(env);
t( db != NULL );
void *cur = sp_cursor(db, SPLTE, NULL, 0);
t( cur != NULL );
t( sp_fetch(cur) == 0 );
t( sp_fetch(cur) == 0 );
t( sp_destroy(cur) == 0 );
t( sp_destroy(db) == 0 );
t( sp_destroy(env) == 0 );
t( rmrf(dbrep) == 0 );
}
static void
fetch_lt_empty(void) {
void *env = sp_env();
t( env != NULL );
t( sp_ctl(env, SPDIR, SPO_CREAT|SPO_RDWR, dbrep) == 0 );
t( sp_ctl(env, SPCMP, cmp, NULL) == 0 );
void *db = sp_open(env);
t( db != NULL );
void *cur = sp_cursor(db, SPLT, NULL, 0);
t( cur != NULL );
t( sp_fetch(cur) == 0 );
t( sp_fetch(cur) == 0 );
t( sp_destroy(cur) == 0 );
t( sp_destroy(db) == 0 );
t( sp_destroy(env) == 0 );
t( rmrf(dbrep) == 0 );
}
static void
fetch_kgte_empty(void) {
void *env = sp_env();
t( env != NULL );
t( sp_ctl(env, SPDIR, SPO_CREAT|SPO_RDWR, dbrep) == 0 );
t( sp_ctl(env, SPCMP, cmp, NULL) == 0 );
void *db = sp_open(env);
t( db != NULL );
uint32_t k = 1;
void *cur = sp_cursor(db, SPGTE, &k, sizeof(k));
t( cur != NULL );
t( sp_fetch(cur) == 0 );
t( sp_fetch(cur) == 0 );
t( sp_destroy(cur) == 0 );
t( sp_destroy(db) == 0 );
t( sp_destroy(env) == 0 );
t( rmrf(dbrep) == 0 );
}
static void
fetch_kgt_empty(void) {
void *env = sp_env();
t( env != NULL );
t( sp_ctl(env, SPDIR, SPO_CREAT|SPO_RDWR, dbrep) == 0 );
t( sp_ctl(env, SPCMP, cmp, NULL) == 0 );
void *db = sp_open(env);
t( db != NULL );
uint32_t k = 1;
void *cur = sp_cursor(db, SPGT, &k, sizeof(k));
t( cur != NULL );
t( sp_fetch(cur) == 0 );
t( sp_fetch(cur) == 0 );
t( sp_destroy(cur) == 0 );
t( sp_destroy(db) == 0 );
t( sp_destroy(env) == 0 );
t( rmrf(dbrep) == 0 );
}
static void
fetch_klte_empty(void) {
void *env = sp_env();
t( env != NULL );
t( sp_ctl(env, SPDIR, SPO_CREAT|SPO_RDWR, dbrep) == 0 );
t( sp_ctl(env, SPCMP, cmp, NULL) == 0 );
void *db = sp_open(env);
t( db != NULL );
uint32_t k = 1;
void *cur = sp_cursor(db, SPLTE, &k, sizeof(k));
t( cur != NULL );
t( sp_fetch(cur) == 0 );
t( sp_fetch(cur) == 0 );
t( sp_destroy(cur) == 0 );
t( sp_destroy(db) == 0 );
t( sp_destroy(env) == 0 );
t( rmrf(dbrep) == 0 );
}
static void
fetch_klt_empty(void) {
void *env = sp_env();
t( env != NULL );
t( sp_ctl(env, SPDIR, SPO_CREAT|SPO_RDWR, dbrep) == 0 );
t( sp_ctl(env, SPCMP, cmp, NULL) == 0 );
void *db = sp_open(env);
t( db != NULL );
uint32_t k = 1;
void *cur = sp_cursor(db, SPLT, &k, sizeof(k));
t( cur != NULL );
t( sp_fetch(cur) == 0 );
t( sp_fetch(cur) == 0 );
t( sp_destroy(cur) == 0 );
t( sp_destroy(db) == 0 );
t( sp_destroy(env) == 0 );
t( rmrf(dbrep) == 0 );
}
static void
fetch_gte(void) {
void *env = sp_env();
t( env != NULL );
t( sp_ctl(env, SPDIR, SPO_CREAT|SPO_RDWR, dbrep) == 0 );
t( sp_ctl(env, SPCMP, cmp, NULL) == 0 );
void *db = sp_open(env);
t( db != NULL );
uint32_t k = 1, v = 2;
t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );
k = 2;
t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );
k = 3;
t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );
void *cur = sp_cursor(db, SPGTE, NULL, 0);
t( cur != NULL );
t( sp_fetch(cur) == 1 );
t( *(uint32_t*)sp_key(cur) == 1 );
t( sp_keysize(cur) == sizeof(k) );
t( *(uint32_t*)sp_value(cur) == 2 );
t( sp_valuesize(cur) == sizeof(v) );
t( sp_fetch(cur) == 1 );
t( *(uint32_t*)sp_key(cur) == 2 );
t( sp_keysize(cur) == sizeof(k) );
t( *(uint32_t*)sp_value(cur) == 2 );
t( sp_valuesize(cur) == sizeof(v) );
t( sp_fetch(cur) == 1 );
t( *(uint32_t*)sp_key(cur) == 3 );
t( sp_keysize(cur) == sizeof(k) );
t( *(uint32_t*)sp_value(cur) == 2 );
t( sp_valuesize(cur) == sizeof(v) );
t( sp_fetch(cur) == 0 );
t( sp_fetch(cur) == 0 );
t( sp_destroy(cur) == 0 );
t( sp_destroy(db) == 0 );
t( sp_destroy(env) == 0 );
t( rmrf(dbrep) == 0 );
}
static void
fetch_gt(void) {
void *env = sp_env();
t( env != NULL );
t( sp_ctl(env, SPDIR, SPO_CREAT|SPO_RDWR, dbrep) == 0 );
t( sp_ctl(env, SPCMP, cmp, NULL) == 0 );
void *db = sp_open(env);
t( db != NULL );
uint32_t k = 1, v = 2;
t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );
k = 2;
t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );
k = 3;
t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );
void *cur = sp_cursor(db, SPGT, NULL, 0);
t( cur != NULL );
t( sp_fetch(cur) == 1 );
t( *(uint32_t*)sp_key(cur) == 1 );
t( sp_keysize(cur) == sizeof(k) );
t( *(uint32_t*)sp_value(cur) == 2 );
t( sp_valuesize(cur) == sizeof(v) );
t( sp_fetch(cur) == 1 );
t( *(uint32_t*)sp_key(cur) == 2 );
t( sp_keysize(cur) == sizeof(k) );
t( *(uint32_t*)sp_value(cur) == 2 );
t( sp_valuesize(cur) == sizeof(v) );
t( sp_fetch(cur) == 1 );
t( *(uint32_t*)sp_key(cur) == 3 );
t( sp_keysize(cur) == sizeof(k) );
t( *(uint32_t*)sp_value(cur) == 2 );
t( sp_valuesize(cur) == sizeof(v) );
t( sp_fetch(cur) == 0 );
t( sp_fetch(cur) == 0 );
t( sp_destroy(cur) == 0 );
t( sp_destroy(db) == 0 );
t( sp_destroy(env) == 0 );
t( rmrf(dbrep) == 0 );
}
static void
fetch_lte(void) {
void *env = sp_env();
t( env != NULL );
t( sp_ctl(env, SPDIR, SPO_CREAT|SPO_RDWR, dbrep) == 0 );
t( sp_ctl(env, SPCMP, cmp, NULL) == 0 );
void *db = sp_open(env);
t( db != NULL );
uint32_t k = 1, v = 2;
t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );
k = 2;
t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );
k = 3;
t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );
void *cur = sp_cursor(db, SPLTE, NULL, 0);
t( cur != NULL );
t( sp_fetch(cur) == 1 );
t( *(uint32_t*)sp_key(cur) == 3 );
t( sp_keysize(cur) == sizeof(k) );
t( *(uint32_t*)sp_value(cur) == 2 );
t( sp_valuesize(cur) == sizeof(v) );
t( sp_fetch(cur) == 1 );
t( *(uint32_t*)sp_key(cur) == 2 );
t( sp_keysize(cur) == sizeof(k) );
t( *(uint32_t*)sp_value(cur) == 2 );
t( sp_valuesize(cur) == sizeof(v) );
t( sp_fetch(cur) == 1 );
t( *(uint32_t*)sp_key(cur) == 1 );
t( sp_keysize(cur) == sizeof(k) );
t( *(uint32_t*)sp_value(cur) == 2 );
t( sp_valuesize(cur) == sizeof(v) );
t( sp_fetch(cur) == 0 );
t( sp_fetch(cur) == 0 );
t( sp_destroy(cur) == 0 );
t( sp_destroy(db) == 0 );
t( sp_destroy(env) == 0 );
t( rmrf(dbrep) == 0 );
}
static void
fetch_lt(void) {
void *env = sp_env();
t( env != NULL );
t( sp_ctl(env, SPDIR, SPO_CREAT|SPO_RDWR, dbrep) == 0 );
t( sp_ctl(env, SPCMP, cmp, NULL) == 0 );
void *db = sp_open(env);
t( db != NULL );
uint32_t k = 1, v = 2;
t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );
k = 2;
t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );
k = 3;
t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );
void *cur = sp_cursor(db, SPLT, NULL, 0);
t( cur != NULL );
t( sp_fetch(cur) == 1 );
t( *(uint32_t*)sp_key(cur) == 3 );
t( sp_keysize(cur) == sizeof(k) );
t( *(uint32_t*)sp_value(cur) == 2 );
t( sp_valuesize(cur) == sizeof(v) );
t( sp_fetch(cur) == 1 );
t( *(uint32_t*)sp_key(cur) == 2 );
t( sp_keysize(cur) == sizeof(k) );
t( *(uint32_t*)sp_value(cur) == 2 );
t( sp_valuesize(cur) == sizeof(v) );
t( sp_fetch(cur) == 1 );
t( *(uint32_t*)sp_key(cur) == 1 );
t( sp_keysize(cur) == sizeof(k) );
t( *(uint32_t*)sp_value(cur) == 2 );
t( sp_valuesize(cur) == sizeof(v) );
t( sp_fetch(cur) == 0 );
t( sp_fetch(cur) == 0 );
t( sp_destroy(cur) == 0 );
t( sp_destroy(db) == 0 );
t( sp_destroy(env) == 0 );
t( rmrf(dbrep) == 0 );
}
static void
fetch_kgte(void) {
void *env = sp_env();
t( env != NULL );
t( sp_ctl(env, SPDIR, SPO_CREAT|SPO_RDWR, dbrep) == 0 );
t( sp_ctl(env, SPCMP, cmp, NULL) == 0 );
void *db = sp_open(env);
t( db != NULL );
uint32_t k = 1, v = 2;
t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );
k = 2;
t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );
k = 3;
t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );
k = 2;
void *cur = sp_cursor(db, SPGTE, &k, sizeof(k));
t( cur != NULL );
t( sp_fetch(cur) == 1 );
t( *(uint32_t*)sp_key(cur) == 2 );
t( sp_keysize(cur) == sizeof(k) );
t( *(uint32_t*)sp_value(cur) == 2 );
t( sp_valuesize(cur) == sizeof(v) );
t( sp_fetch(cur) == 1 );
t( *(uint32_t*)sp_key(cur) == 3 );
t( sp_keysize(cur) == sizeof(k) );
t( *(uint32_t*)sp_value(cur) == 2 );
t( sp_valuesize(cur) == sizeof(v) );
t( sp_fetch(cur) == 0 );
t( sp_fetch(cur) == 0 );
t( sp_destroy(cur) == 0 );
t( sp_destroy(db) == 0 );
t( sp_destroy(env) == 0 );
t( rmrf(dbrep) == 0 );
}
static void
fetch_kgt(void) {
void *env = sp_env();
t( env != NULL );
t( sp_ctl(env, SPDIR, SPO_CREAT|SPO_RDWR, dbrep) == 0 );
t( sp_ctl(env, SPCMP, cmp, NULL) == 0 );
void *db = sp_open(env);
t( db != NULL );
uint32_t k = 1, v = 2;
t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );
k = 2;
t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );
k = 3;
t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );
k = 2;
void *cur = sp_cursor(db, SPGT, &k, sizeof(k));
t( cur != NULL );
t( sp_fetch(cur) == 1 );
t( *(uint32_t*)sp_key(cur) == 3 );
t( sp_keysize(cur) == sizeof(k) );
t( *(uint32_t*)sp_value(cur) == 2 );
t( sp_valuesize(cur) == sizeof(v) );
t( sp_fetch(cur) == 0 );
t( sp_fetch(cur) == 0 );
t( sp_destroy(cur) == 0 );
t( sp_destroy(db) == 0 );
t( sp_destroy(env) == 0 );
t( rmrf(dbrep) == 0 );
}
static void
fetch_klte(void) {
void *env = sp_env();
t( env != NULL );
t( sp_ctl(env, SPDIR, SPO_CREAT|SPO_RDWR, dbrep) == 0 );
t( sp_ctl(env, SPCMP, cmp, NULL) == 0 );
void *db = sp_open(env);
t( db != NULL );
uint32_t k = 1, v = 2;
t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );
k = 2;
t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );
k = 3;
t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );
k = 2;
void *cur = sp_cursor(db, SPLTE, &k, sizeof(k) );
t( cur != NULL );
t( sp_fetch(cur) == 1 );
t( *(uint32_t*)sp_key(cur) == 2 );
t( sp_keysize(cur) == sizeof(k) );
t( *(uint32_t*)sp_value(cur) == 2 );
t( sp_valuesize(cur) == sizeof(v) );
t( sp_fetch(cur) == 1 );
t( *(uint32_t*)sp_key(cur) == 1 );
t( sp_keysize(cur) == sizeof(k) );
t( *(uint32_t*)sp_value(cur) == 2 );
t( sp_valuesize(cur) == sizeof(v) );
t( sp_fetch(cur) == 0 );
t( sp_fetch(cur) == 0 );
t( sp_destroy(cur) == 0 );
t( sp_destroy(db) == 0 );
t( sp_destroy(env) == 0 );
t( rmrf(dbrep) == 0 );
}
static void
fetch_klt(void) {
void *env = sp_env();
t( env != NULL );
t( sp_ctl(env, SPDIR, SPO_CREAT|SPO_RDWR, dbrep) == 0 );
t( sp_ctl(env, SPCMP, cmp, NULL) == 0 );
void *db = sp_open(env);
t( db != NULL );
uint32_t k = 1, v = 2;
t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );
k = 2;
t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );
k = 3;
t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );
k = 2;
void *cur = sp_cursor(db, SPLT, &k, sizeof(k) );
t( cur != NULL );
t( sp_fetch(cur) == 1 );
t( *(uint32_t*)sp_key(cur) == 1 );
t( sp_keysize(cur) == sizeof(k) );
t( *(uint32_t*)sp_value(cur) == 2 );
t( sp_valuesize(cur) == sizeof(v) );
t( sp_fetch(cur) == 0 );
t( sp_fetch(cur) == 0 );
t( sp_destroy(cur) == 0 );
t( sp_destroy(db) == 0 );
t( sp_destroy(env) == 0 );
t( rmrf(dbrep) == 0 );
}
static void
fetch_after_end(void) {
void *env = sp_env();
t( env != NULL );
t( sp_ctl(env, SPDIR, SPO_CREAT|SPO_RDWR, dbrep) == 0 );
t( sp_ctl(env, SPCMP, cmp, NULL) == 0 );
void *db = sp_open(env);
t( db != NULL );
uint32_t k = 1, v = 2;
t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );
k = 2;
t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );
k = 3;
t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );
void *cur = sp_cursor(db, SPGTE, NULL, 0);
t( cur != NULL );
t( sp_fetch(cur) == 1 );
t( *(uint32_t*)sp_key(cur) == 1 );
t( sp_keysize(cur) == sizeof(k) );
t( *(uint32_t*)sp_value(cur) == 2 );
t( sp_valuesize(cur) == sizeof(v) );
t( sp_fetch(cur) == 1 );
t( *(uint32_t*)sp_key(cur) == 2 );
t( sp_keysize(cur) == sizeof(k) );
t( *(uint32_t*)sp_value(cur) == 2 );
t( sp_valuesize(cur) == sizeof(v) );
t( sp_fetch(cur) == 1 );
t( *(uint32_t*)sp_key(cur) == 3 );
t( sp_keysize(cur) == sizeof(k) );
t( *(uint32_t*)sp_value(cur) == 2 );
t( sp_valuesize(cur) == sizeof(v) );
t( sp_fetch(cur) == 0 );
t( sp_keysize(cur) == 0 );
t( sp_key(cur) == NULL );
t( sp_valuesize(cur) == 0 );
t( sp_value(cur) == NULL );
t( sp_destroy(cur) == 0 );
t( sp_destroy(db) == 0 );
t( sp_destroy(env) == 0 );
t( rmrf(dbrep) == 0 );
}
#if 0
static void
rotate_empty(void) {
void *env = sp_env();
t( env != NULL );
t( sp_ctl(env, SPDIR, SPO_CREAT|SPO_RDWR, dbrep) == 0 );
t( sp_ctl(env, SPCMP, cmp, NULL) == 0 );
t( sp_ctl(env, SPMERGE, 0) == 0 );
t( sp_ctl(env, SPTTL, 2) == 0 );
void *db = sp_open(env);
t( exists(dbrep, 1, "log.incomplete") == 1 );
t( sp_destroy(db) == 0 );
t( exists(dbrep, 1, "log.incomplete") == 0 );
t( exists(dbrep, 1, "log") == 0 );
t( sp_destroy(env) == 0 );
t( rmrf(dbrep) == 0 );
}
static void
rotate(void) {
void *env = sp_env();
t( env != NULL );
t( sp_ctl(env, SPDIR, SPO_CREAT|SPO_RDWR, dbrep) == 0 );
t( sp_ctl(env, SPCMP, cmp, NULL) == 0 );
t( sp_ctl(env, SPMERGE, 0) == 0 );
t( sp_ctl(env, SPTTL, 2) == 0 );
void *db = sp_open(env);
t( db != NULL );
uint32_t k = 1, v = 1;
t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0);
t( exists(dbrep, 1, "log.incomplete") == 1 );
t( exists(dbrep, 1, "db.incomplete") == 0 );
t( exists(dbrep, 1, "db") == 0 );
t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0);
t( exists(dbrep, 1, "log.incomplete") == 1 );
t( exists(dbrep, 2, "log.incomplete") == 1 );
t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0);
t( exists(dbrep, 3, "log.incomplete") == 0 );
t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0);
t( exists(dbrep, 1, "log.incomplete") == 1 );
t( exists(dbrep, 2, "log.incomplete") == 1 );
t( exists(dbrep, 3, "log.incomplete") == 1 );
t( sp_destroy(db) == 0 );
t( exists(dbrep, 1, "db.incomplete") == 0 );
t( exists(dbrep, 1, "db") == 0 );
t( exists(dbrep, 1, "log.incomplete") == 0 );
t( exists(dbrep, 2, "log.incomplete") == 0 );
t( exists(dbrep, 3, "log.incomplete") == 0 );
t( exists(dbrep, 1, "log") == 1 );
t( exists(dbrep, 2, "log") == 1 );
t( exists(dbrep, 3, "log") == 0 );
t( sp_destroy(env) == 0 );
t( rmrf(dbrep) == 0 );
}
#endif
int
main(int argc, char *argv[])
{
rmrf(dbrep);
test(env);
test(env_opts);
test(open_ro_creat);
test(open_rdwr);
test(open_rdwr_creat);
test(open_reopen);
test(open_reopen_ro);
test(set);
test(set_get);
test(set_get_zerovalue);
test(replace);
test(replace_get);
test(set_delete);
test(set_delete_get);
test(set_delete_set_get);
test(delete);
test(delete_set_get);
test(cursor);
test(fetch_gte_empty);
test(fetch_gt_empty);
test(fetch_lte_empty);
test(fetch_lt_empty);
test(fetch_kgte_empty);
test(fetch_kgt_empty);
test(fetch_klte_empty);
test(fetch_klt_empty);
test(fetch_gte);
test(fetch_gt);
test(fetch_lte);
test(fetch_lt);
test(fetch_kgte);
test(fetch_kgt);
test(fetch_klte);
test(fetch_klt);
test(fetch_after_end);
/*
test(rotate_empty);
test(rotate);
*/
return 0;
}

492
src/sophia/test/crash.c Normal file
View File

@@ -0,0 +1,492 @@
/*
* sophia database
* sphia.org
*
* Copyright (c) Dmitry Simonenko
* BSD License
*/
#include <unistd.h>
#include <fcntl.h>
#include <sophia.h>
#include <sp.h>
#include "test.h"
static char *dbrep = "./rep";
static spa a;
static inline int
cmp(char *a, size_t asz, char *b, size_t bsz, void *arg) {
register uint32_t av = *(uint32_t*)a;
register uint32_t bv = *(uint32_t*)b;
if (av == bv)
return 0;
return (av > bv) ? 1 : -1;
}
static void
log_empty(void) {
spfile f;
sp_fileinit(&f, &a);
t( mkdir(dbrep, 0755) == 0 );
t( sp_lognew(&f, dbrep, 1) == 0 );
t( sp_logcomplete(&f) == 0 );
t( sp_logclose(&f) == 0 );
t( exists(dbrep, 1, "log") == 1 );
void *env = sp_env();
t( env != NULL );
t( sp_ctl(env, SPDIR, SPO_CREAT|SPO_RDWR, dbrep) == 0 );
t( sp_ctl(env, SPCMP, cmp, NULL) == 0 );
t( sp_ctl(env, SPMERGE, 0) == 0 );
void *db = sp_open(env);
t( db == NULL );
t( sp_error(env) != NULL );
t( sp_destroy(env) == 0 );
t( rmrf(dbrep) == 0 );
}
static void
log_empty_incomplete(void) {
spfile f;
sp_fileinit(&f, &a);
t( mkdir(dbrep, 0755) == 0 );
t( sp_lognew(&f, dbrep, 1) == 0 );
t( sp_logclose(&f) == 0 );
t( exists(dbrep, 1, "log.incomplete") == 1 );
void *env = sp_env();
t( env != NULL );
t( sp_ctl(env, SPDIR, SPO_CREAT|SPO_RDWR, dbrep) == 0 );
t( sp_ctl(env, SPCMP, cmp, NULL) == 0 );
t( sp_ctl(env, SPMERGE, 0) == 0 );
void *db = sp_open(env);
t( db == NULL );
t( sp_error(env) != NULL );
t( sp_destroy(env) == 0 );
t( rmrf(dbrep) == 0 );
}
static void
log_badrecord(void) {
uint32_t k = 123;
splogh h = {
.magic = SPMAGIC,
.version[0] = SP_VERSION_MAJOR,
.version[1] = SP_VERSION_MINOR
};
spvh vh = {
.crc = 0,
.size = sizeof(uint32_t),
.voffset = 0,
.vsize = sizeof(uint32_t),
.flags = SPSET
};
speofh eof = { SPEOF };
spfile f;
sp_fileinit(&f, &a);
t( mkdir(dbrep, 0755) == 0 );
t( sp_lognew(&f, dbrep, 1) == 0 );
sp_logadd(&f, &h, sizeof(h));
sp_logadd(&f, &vh, sizeof(vh));
sp_logadd(&f, &k, sizeof(k));
sp_logadd(&f, &k, sizeof(k));
sp_logadd(&f, &eof, sizeof(eof));
t( sp_logflush(&f) == 0 );
t( sp_logcomplete(&f) == 0 );
t( sp_logclose(&f) == 0 );
t( exists(dbrep, 1, "log") == 1 );
void *env = sp_env();
t( env != NULL );
t( sp_ctl(env, SPDIR, SPO_CREAT|SPO_RDWR, dbrep) == 0 );
t( sp_ctl(env, SPCMP, cmp, NULL) == 0 );
t( sp_ctl(env, SPMERGE, 0) == 0 );
void *db = sp_open(env);
t( db == NULL );
t( sp_error(env) != NULL );
t( sp_destroy(env) == 0 );
t( rmrf(dbrep) == 0 );
}
static void
log_badrecord_incomplete(void) {
uint32_t k = 123;
splogh h = {
.magic = SPMAGIC,
.version[0] = SP_VERSION_MAJOR,
.version[1] = SP_VERSION_MINOR
};
spvh vh = {
.crc = 0,
.size = sizeof(uint32_t),
.voffset = 0,
.vsize = sizeof(uint32_t),
.flags = SPSET
};
speofh eof = { SPEOF };
spfile f;
sp_fileinit(&f, &a);
t( mkdir(dbrep, 0755) == 0 );
t( sp_lognew(&f, dbrep, 1) == 0 );
sp_logadd(&f, &h, sizeof(h));
sp_logadd(&f, &vh, sizeof(vh));
sp_logadd(&f, &k, sizeof(k));
sp_logadd(&f, &k, sizeof(k));
sp_logadd(&f, &eof, sizeof(eof));
t( sp_logflush(&f) == 0 );
t( sp_logclose(&f) == 0 );
t( exists(dbrep, 1, "log.incomplete") == 1 );
void *env = sp_env();
t( env != NULL );
t( sp_ctl(env, SPDIR, SPO_CREAT|SPO_RDWR, dbrep) == 0 );
t( sp_ctl(env, SPCMP, cmp, NULL) == 0 );
t( sp_ctl(env, SPMERGE, 0) == 0 );
void *db = sp_open(env);
t( db == NULL );
t( sp_error(env) != NULL );
t( sp_destroy(env) == 0 );
t( exists(dbrep, 1, "log.incomplete") == 1 );
t( rmrf(dbrep) == 0 );
}
static void
log_1_badrecord_2_goodrecord(void) {
uint32_t k = 123;
void *env = sp_env();
t( env != NULL );
t( sp_ctl(env, SPDIR, SPO_CREAT|SPO_RDWR, dbrep) == 0 );
t( sp_ctl(env, SPCMP, cmp, NULL) == 0 );
t( sp_ctl(env, SPMERGE, 0) == 0 );
void *db = sp_open(env);
t( db != NULL );
t( sp_set(db, &k, sizeof(k), &k, sizeof(k)) == 0 );
t( sp_destroy(db) == 0 );
t( exists(dbrep, 1, "log") == 1 );
splogh h = {
.magic = SPMAGIC,
.version[0] = SP_VERSION_MAJOR,
.version[1] = SP_VERSION_MINOR
};
spvh vh = {
.crc = 0,
.size = sizeof(uint32_t),
.voffset = 0,
.vsize = sizeof(uint32_t),
.flags = SPSET
};
speofh eof = { SPEOF };
spfile f;
sp_fileinit(&f, &a);
t( sp_lognew(&f, dbrep, 2) == 0 );
sp_logadd(&f, &h, sizeof(h));
sp_logadd(&f, &vh, sizeof(vh));
sp_logadd(&f, &k, sizeof(k));
sp_logadd(&f, &k, sizeof(k));
sp_logadd(&f, &eof, sizeof(eof));
t( sp_logflush(&f) == 0 );
t( sp_logcomplete(&f) == 0 );
t( sp_logclose(&f) == 0 );
t( exists(dbrep, 2, "log") == 1 );
db = sp_open(env);
t( db == NULL );
t( sp_destroy(env) == 0 );
t( exists(dbrep, 1, "log") == 1 );
t( exists(dbrep, 2, "log") == 1 );
t( rmrf(dbrep) == 0 );
}
static void
log_noeof(void) {
uint32_t k = 123;
splogh h = {
.magic = SPMAGIC,
.version[0] = SP_VERSION_MAJOR,
.version[1] = SP_VERSION_MINOR
};
spvh vh = {
.crc = 0,
.size = sizeof(uint32_t),
.voffset = 0,
.vsize = sizeof(uint32_t),
.flags = SPSET
};
uint32_t crc;
crc = sp_crc32c(0, &k, sizeof(k));
crc = sp_crc32c(crc, &k, sizeof(k));
vh.crc = sp_crc32c(crc, &vh.size, sizeof(spvh) - sizeof(uint32_t));
spfile f;
sp_fileinit(&f, &a);
t( mkdir(dbrep, 0755) == 0 );
t( sp_lognew(&f, dbrep, 1) == 0 );
sp_logadd(&f, &h, sizeof(h));
sp_logadd(&f, &vh, sizeof(vh));
sp_logadd(&f, &k, sizeof(k));
sp_logadd(&f, &k, sizeof(k));
t( sp_logflush(&f) == 0 );
t( sp_logclose(&f) == 0 );
t( exists(dbrep, 1, "log.incomplete") == 1 );
void *env = sp_env();
t( env != NULL );
t( sp_ctl(env, SPDIR, SPO_CREAT|SPO_RDWR, dbrep) == 0 );
t( sp_ctl(env, SPCMP, cmp, NULL) == 0 );
t( sp_ctl(env, SPMERGE, 0) == 0 );
void *db = sp_open(env);
t( db != NULL );
size_t vsize = 0;
void *vp = NULL;
t( sp_get(db, &k, sizeof(k), &vp, &vsize) == 1 );
t( vsize == sizeof(k) );
t( *(uint32_t*)vp == k );
free(vp);
t( sp_destroy(db) == 0 );
t( sp_destroy(env) == 0 );
t( rmrf(dbrep) == 0 );
}
static void
log_noeof_complete(void) {
uint32_t k = 123;
splogh h = {
.magic = SPMAGIC,
.version[0] = SP_VERSION_MAJOR,
.version[1] = SP_VERSION_MINOR
};
spvh vh = {
.crc = 0,
.size = sizeof(uint32_t),
.voffset = 0,
.vsize = sizeof(uint32_t),
.flags = SPSET
};
uint32_t crc;
crc = sp_crc32c(0, &k, sizeof(k));
crc = sp_crc32c(crc, &k, sizeof(k));
vh.crc = sp_crc32c(crc, &vh.size, sizeof(spvh) - sizeof(uint32_t));
spfile f;
sp_fileinit(&f, &a);
t( mkdir(dbrep, 0755) == 0 );
t( sp_lognew(&f, dbrep, 1) == 0 );
sp_logadd(&f, &h, sizeof(h));
sp_logadd(&f, &vh, sizeof(vh));
sp_logadd(&f, &k, sizeof(k));
sp_logadd(&f, &k, sizeof(k));
t( sp_logflush(&f) == 0 );
t( sp_logcomplete(&f) == 0 );
t( sp_logclose(&f) == 0 );
t( exists(dbrep, 1, "log") == 1 );
void *env = sp_env();
t( env != NULL );
t( sp_ctl(env, SPDIR, SPO_CREAT|SPO_RDWR, dbrep) == 0 );
t( sp_ctl(env, SPCMP, cmp, NULL) == 0 );
t( sp_ctl(env, SPMERGE, 0) == 0 );
void *db = sp_open(env);
t( db == NULL );
t( sp_error(env) != NULL );
t( sp_destroy(env) == 0 );
t( rmrf(dbrep) == 0 );
}
static void
db_empty(void) {
spfile f;
sp_fileinit(&f, &a);
t( mkdir(dbrep, 0755) == 0 );
char path[1024];
snprintf(path, sizeof(path), "%s/%"PRIu32".db",
dbrep, 1);
int fd = open(path, O_CREAT|O_RDWR, 0644);
t( fd != -1 );
t( close(fd) == 0 );
void *env = sp_env();
t( env != NULL );
t( sp_ctl(env, SPDIR, SPO_CREAT|SPO_RDWR, dbrep) == 0 );
t( sp_ctl(env, SPCMP, cmp, NULL) == 0 );
t( sp_ctl(env, SPMERGE, 0) == 0 );
void *db = sp_open(env);
t( db == NULL );
t( sp_error(env) != NULL );
t( exists(dbrep, 1, "db") == 1 );
t( sp_destroy(env) == 0 );
t( rmrf(dbrep) == 0 );
}
static void
db_empty_incomplete(void) {
spfile f;
sp_fileinit(&f, &a);
t( mkdir(dbrep, 0755) == 0 );
char path[1024];
snprintf(path, sizeof(path), "%s/%"PRIu32".db.incomplete",
dbrep, 1);
int fd = open(path, O_CREAT|O_RDWR, 0644);
t( fd != -1 );
t( close(fd) == 0 );
void *env = sp_env();
t( env != NULL );
t( sp_ctl(env, SPDIR, SPO_CREAT|SPO_RDWR, dbrep) == 0 );
t( sp_ctl(env, SPCMP, cmp, NULL) == 0 );
t( sp_ctl(env, SPMERGE, 0) == 0 );
void *db = sp_open(env);
t( db == NULL );
t( sp_error(env) != NULL );
t( exists(dbrep, 1, "db.incomplete") == 1 );
t( sp_destroy(env) == 0 );
t( rmrf(dbrep) == 0 );
}
static void
db_badpage(void) {
sppageh h = {
.crc = 0,
.count = 0,
.size = 1234,
.bsize = 1234,
};
t( mkdir(dbrep, 0755) == 0 );
char path[1024];
snprintf(path, sizeof(path), "%s/%"PRIu32".db",
dbrep, 1);
int fd = open(path, O_CREAT|O_RDWR, 0644);
t( fd != -1 );
t( write(fd, &h, sizeof(h)) == sizeof(h) );
t( close(fd) == 0 );
void *env = sp_env();
t( env != NULL );
t( sp_ctl(env, SPDIR, SPO_CREAT|SPO_RDWR, dbrep) == 0 );
t( sp_ctl(env, SPCMP, cmp, NULL) == 0 );
t( sp_ctl(env, SPMERGE, 0) == 0 );
void *db = sp_open(env);
t( db == NULL );
t( sp_error(env) != NULL );
t( sp_destroy(env) == 0 );
t( rmrf(dbrep) == 0 );
}
static void
log_incomplete_db_incomplete(void) {
uint32_t k = 123;
splogh h = {
.magic = SPMAGIC,
.version[0] = SP_VERSION_MAJOR,
.version[1] = SP_VERSION_MINOR
};
spvh vh = {
.crc = 0,
.size = sizeof(uint32_t),
.voffset = 0,
.vsize = sizeof(uint32_t),
.flags = SPSET
};
speofh eof = { SPEOF };
uint32_t crc;
crc = sp_crc32c(0, &k, sizeof(k));
crc = sp_crc32c(crc, &k, sizeof(k));
vh.crc = sp_crc32c(crc, &vh.size, sizeof(spvh) - sizeof(uint32_t));
spfile f;
sp_fileinit(&f, &a);
t( mkdir(dbrep, 0755) == 0 );
t( sp_lognew(&f, dbrep, 1) == 0 );
sp_logadd(&f, &h, sizeof(h));
sp_logadd(&f, &vh, sizeof(vh));
sp_logadd(&f, &k, sizeof(k));
sp_logadd(&f, &k, sizeof(k));
sp_logadd(&f, &eof, sizeof(eof));
t( sp_logflush(&f) == 0 );
t( sp_logclose(&f) == 0 );
char path[1024];
snprintf(path, sizeof(path), "%s/%"PRIu32".db.incomplete",
dbrep, 1);
int fd = open(path, O_CREAT|O_RDWR, 0644);
t( fd != -1 );
t( write(fd, &h, sizeof(h)) == sizeof(h) );
t( close(fd) == 0 );
void *env = sp_env();
t( env != NULL );
t( sp_ctl(env, SPDIR, SPO_CREAT|SPO_RDWR, dbrep) == 0 );
t( sp_ctl(env, SPCMP, cmp, NULL) == 0 );
t( sp_ctl(env, SPMERGE, 0) == 0 );
void *db = sp_open(env);
t( db == NULL );
t( sp_destroy(env) == 0 );
t( rmrf(dbrep) == 0 );
}
static void
log_db_incomplete(void) {
uint32_t k = 123;
splogh h = {
.magic = SPMAGIC,
.version[0] = SP_VERSION_MAJOR,
.version[1] = SP_VERSION_MINOR
};
spvh vh = {
.crc = 0,
.size = sizeof(uint32_t),
.voffset = 0,
.vsize = sizeof(uint32_t),
.flags = SPSET
};
speofh eof = { SPEOF };
uint32_t crc;
crc = sp_crc32c(0, &k, sizeof(k));
crc = sp_crc32c(crc, &k, sizeof(k));
vh.crc = sp_crc32c(crc, &vh.size, sizeof(spvh) - sizeof(uint32_t));
spfile f;
sp_fileinit(&f, &a);
t( mkdir(dbrep, 0755) == 0 );
t( sp_lognew(&f, dbrep, 1) == 0 );
sp_logadd(&f, &h, sizeof(h));
sp_logadd(&f, &vh, sizeof(vh));
sp_logadd(&f, &k, sizeof(k));
sp_logadd(&f, &k, sizeof(k));
sp_logadd(&f, &eof, sizeof(eof));
t( sp_logflush(&f) == 0 );
t( sp_logcomplete(&f) == 0 );
t( sp_logclose(&f) == 0 );
t( exists(dbrep, 1, "log") == 1 );
char path[1024];
snprintf(path, sizeof(path), "%s/%"PRIu32".db.incomplete",
dbrep, 1);
int fd = open(path, O_CREAT|O_RDWR, 0644);
t( fd != -1 );
t( write(fd, &h, sizeof(h)) == sizeof(h) );
t( close(fd) == 0 );
void *env = sp_env();
t( env != NULL );
t( sp_ctl(env, SPDIR, SPO_CREAT|SPO_RDWR, dbrep) == 0 );
t( sp_ctl(env, SPCMP, cmp, NULL) == 0 );
t( sp_ctl(env, SPMERGE, 0) == 0 );
void *db = sp_open(env);
t( db != NULL );
size_t vsize = 0;
void *vp = NULL;
t( sp_get(db, &k, sizeof(k), &vp, &vsize) == 1 );
t( vsize == sizeof(k) );
t( *(uint32_t*)vp == k );
free(vp);
t( sp_destroy(db) == 0 );
t( sp_destroy(env) == 0 );
t( rmrf(dbrep) == 0 );
}
int
main(int argc, char *argv[])
{
sp_allocinit(&a, sp_allocstd, NULL);
rmrf(dbrep);
test(log_empty);
test(log_empty_incomplete);
test(log_badrecord);
test(log_badrecord_incomplete);
test(log_1_badrecord_2_goodrecord);
test(log_noeof);
test(log_noeof_complete);
test(db_empty);
test(db_empty_incomplete);
test(db_badpage);
test(log_db_incomplete);
test(log_incomplete_db_incomplete);
return 0;
}

403
src/sophia/test/i.c Normal file
View File

@@ -0,0 +1,403 @@
/*
* sophia database
* sphia.org
*
* Copyright (c) Dmitry Simonenko
* BSD License
*/
#include <time.h>
#include <sophia.h>
#include <sp.h>
#include "test.h"
static spa a;
static inline spv *newv(uint32_t k) {
spv *v = sp_malloc(&a, sizeof(spv) + sizeof(uint32_t));
if (spunlikely(v == NULL))
return NULL;
v->epoch = 0;
v->crc = 0;
v->size = sizeof(k);
v->flags = 0;
memcpy(v->key, &k, sizeof(k));
return v;
}
static inline void freekey(spv *v) {
sp_free(&a, v);
}
static inline int
cmp(char *a, size_t asz, char *b, size_t bsz, void *arg) {
register uint32_t av = *(uint32_t*)a;
register uint32_t bv = *(uint32_t*)b;
if (av == bv)
return 0;
return (av > bv) ? 1 : -1;
}
static void
init(void) {
spi i;
t( sp_iinit(&i, &a, 256, cmp, NULL) == 0);
sp_ifree(&i);
}
static void
set(void) {
spi index;
t( sp_iinit(&index, &a, 16, cmp, NULL) == 0);
int k = 0;
while (k < 8) {
spv *v = newv(k);
spv *old = NULL;
t( v != NULL );
t( sp_iset(&index, v, &old) == 0);
t( old == NULL );
k++;
}
sp_ifree(&index);
}
static void
set_split(void) {
spi index;
t( sp_iinit(&index, &a, 16, cmp, NULL) == 0);
int k = 0;
while (k < 32) {
spv *v = newv(k);
spv *old = NULL;
t( v != NULL );
t( sp_iset(&index, v, &old) == 0);
t( old == NULL );
k++;
}
sp_ifree(&index);
}
static void
set_get(void) {
spi index;
t( sp_iinit(&index, &a, 16, cmp, NULL) == 0);
int k = 0;
while (k < 8) {
spv *v = newv(k);
spv *old = NULL;
t( v != NULL );
t( sp_iset(&index, v, &old) == 0);
t( old == NULL );
k++;
}
k = 0;
while (k < 8) {
spv *v = sp_igetraw(&index, (char*)&k, sizeof(k));
t( v != NULL );
t( *(uint32_t*)v->key == k );
k++;
}
sp_ifree(&index);
}
static void
set_get_split(void) {
spi index;
t( sp_iinit(&index, &a, 16, cmp, NULL) == 0);
int k = 0;
while (k < 32) {
spv *v = newv(k);
spv *old = NULL;
t( v != NULL );
t( sp_iset(&index, v, &old) == 0);
t( old == NULL );
k++;
}
k = 0;
while (k < 32) {
spv *v = sp_igetraw(&index, (char*)&k, sizeof(k));
t( v != NULL );
t( *(uint32_t*)v->key == k );
k++;
}
sp_ifree(&index);
}
static void
set_fetchfwd(void) {
spi index;
t( sp_iinit(&index, &a, 16, cmp, NULL) == 0);
int k = 0;
while (k < 8) {
spv *v = newv(k);
spv *old = NULL;
t( v != NULL );
t( sp_iset(&index, v, &old) == 0);
t( old == NULL );
k++;
}
spv *max = sp_imax(&index);
t( max != NULL );
t( *(uint32_t*)max->key == 7 );
k = 0;
spii it;
sp_iopen(&it, &index);
do {
spv *v = sp_ival(&it);
t( v != NULL );
t( *(uint32_t*)v->key == k );
k++;
} while (sp_inext(&it));
sp_ifree(&index);
}
static void
set_fetchbkw(void) {
spi index;
t( sp_iinit(&index, &a, 16, cmp, NULL) == 0);
int k = 0;
while (k < 8) {
spv *v = newv(k);
spv *old = NULL;
t( v != NULL );
t( sp_iset(&index, v, &old) == 0);
t( old == NULL );
k++;
}
k = 7;
spii it;
sp_iopen(&it, &index);
sp_ilast(&it);
do {
spv *v = sp_ival(&it);
t( v != NULL );
t( *(uint32_t*)v->key == k );
k--;
} while (sp_iprev(&it));
sp_ifree(&index);
}
static void
set_fetchfwd_split(void) {
spi index;
t( sp_iinit(&index, &a, 16, cmp, NULL) == 0);
int k = 0;
while (k < 73) {
spv *v = newv(k);
spv *old = NULL;
t( v != NULL );
t( sp_iset(&index, v, &old) == 0);
t( old == NULL );
k++;
}
k = 0;
spii it;
sp_iopen(&it, &index);
do {
spv *v = sp_ival(&it);
t( v != NULL );
t( *(uint32_t*)v->key == k );
k++;
} while (sp_inext(&it));
sp_ifree(&index);
}
static void
set_fetchbkw_split(void) {
spi index;
t( sp_iinit(&index, &a, 16, cmp, NULL) == 0);
int k = 0;
while (k < 89) {
spv *v = newv(k);
spv *old = NULL;
t( v != NULL );
t( sp_iset(&index, v, &old) == 0);
t( old == NULL );
k++;
}
spv *max = sp_imax(&index);
t( max != NULL );
t( *(uint32_t*)max->key == 88 );
k = 88;
spii it;
sp_iopen(&it, &index);
sp_ilast(&it);
do {
spv *v = sp_ival(&it);
t( v != NULL );
t( *(uint32_t*)v->key == k );
k--;
} while (sp_iprev(&it));
sp_ifree(&index);
}
static void
set_del(void) {
spi index;
t( sp_iinit(&index, &a, 16, cmp, NULL) == 0);
int k = 0;
while (k < 8) {
spv *v = newv(k);
spv *old = NULL;
t( v != NULL );
t( sp_iset(&index, v, &old) == 0);
t( old == NULL );
k++;
}
k = 0;
while (k < 8) {
spv *v = sp_igetraw(&index, (char*)&k, sizeof(k));
t( v != NULL );
t( *(uint32_t*)v->key == k );
spv *old = NULL;
t( sp_idelraw(&index, (char*)&k, sizeof(k), &old) == 1);
t( old != NULL );
t( *(uint32_t*)old->key == k );
freekey(old);
k++;
}
t ( index.count == 0 );
t ( index.icount == 1 );
spv *max = sp_imax(&index);
t( max == NULL );
spii it;
sp_iopen(&it, &index);
t( sp_ival(&it) == NULL );
t( sp_inext(&it) == 0 );
sp_ifree(&index);
}
static void
set_del_split(void) {
spi index;
t( sp_iinit(&index, &a, 16, cmp, NULL) == 0);
int k = 0;
while (k < 37) {
spv *v = newv(k);
spv *old = NULL;
t( v != NULL );
t( sp_iset(&index, v, &old) == 0);
t( old == NULL );
k++;
}
k = 0;
while (k < 37) {
spv *v = sp_igetraw(&index, (char*)&k, sizeof(k));
t( v != NULL );
t( *(uint32_t*)v->key == k );
spv *old = NULL;
t( sp_idelraw(&index, (char*)&k, sizeof(k), &old) == 1);
t( old != NULL );
t( *(uint32_t*)old->key == k );
freekey(old);
k++;
}
t ( index.count == 0 );
t ( index.icount == 1 );
spv *max = sp_imax(&index);
t( max == NULL );
spii it;
sp_iopen(&it, &index);
t( sp_ival(&it) == NULL );
t( sp_inext(&it) == 0 );
sp_ifree(&index);
}
static void
set_delbkw_split(void) {
spi index;
t( sp_iinit(&index, &a, 16, cmp, NULL) == 0);
int k = 0;
while (k < 37) {
spv *v = newv(k);
spv *old = NULL;
t( v != NULL );
t( sp_iset(&index, v, &old) == 0);
t( old == NULL );
k++;
}
k = 36;
while (k >= 0) {
spv *v = sp_igetraw(&index, (char*)&k, sizeof(k));
t( v != NULL );
t( *(uint32_t*)v->key == k );
spv *old = NULL;
t( sp_idelraw(&index, (char*)&k, sizeof(k), &old) == 1);
t( old != NULL );
t( *(uint32_t*)old->key == k );
freekey(old);
k--;
}
t ( index.count == 0 );
t ( index.icount == 1 );
spv *max = sp_imax(&index);
t( max == NULL );
spii it;
sp_iopen(&it, &index);
t( sp_ival(&it) == NULL );
t( sp_inext(&it) == 0 );
sp_ifree(&index);
}
static void
set_delrnd_split(void) {
spi index;
t( sp_iinit(&index, &a, 16, cmp, NULL) == 0);
int count = 397;
int k = 0;
while (k < count) {
spv *v = newv(k);
spv *old = NULL;
t( v != NULL );
t( sp_iset(&index, v, &old) == 0);
t( old == NULL );
k++;
}
srand(time(NULL));
int total = count;
while (total != 0) {
k = rand() % count;
spv *v = sp_igetraw(&index, (char*)&k, sizeof(k));
spv *old = NULL;
int rc = sp_idelraw(&index, (char*)&k, sizeof(k), &old);
if (rc == 1) {
t( v == old );
total--;
t( old != NULL );
freekey(old);
} else {
t( v == NULL );
}
}
t ( index.count == 0 );
t ( index.icount == 1 );
spv *max = sp_imax(&index);
t( max == NULL );
spii it;
sp_iopen(&it, &index);
t( sp_ival(&it) == NULL );
t( sp_inext(&it) == 0 );
sp_ifree(&index);
}
int
main(int argc, char *argv[])
{
sp_allocinit(&a, &sp_allocstd, NULL);
test(init);
test(set);
test(set_split);
test(set_get);
test(set_get_split);
test(set_fetchfwd);
test(set_fetchbkw);
test(set_fetchfwd_split);
test(set_fetchbkw_split);
test(set_del);
test(set_del_split);
test(set_delbkw_split);
test(set_delrnd_split);
return 0;
}

65
src/sophia/test/limit.c Normal file
View File

@@ -0,0 +1,65 @@
/*
* sophia database
* sphia.org
*
* Copyright (c) Dmitry Simonenko
* BSD License
*/
#include <sophia.h>
#include "test.h"
static char *dbrep = "./rep";
static inline int
cmp(char *a, size_t asz, char *b, size_t bsz, void *arg) {
register uint32_t av = *(uint32_t*)a;
register uint32_t bv = *(uint32_t*)b;
if (av == bv)
return 0;
return (av > bv) ? 1 : -1;
}
static void
limit_key(void) {
void *env = sp_env();
t( env != NULL );
t( sp_ctl(env, SPDIR, SPO_CREAT|SPO_RDWR, dbrep) == 0 );
t( sp_ctl(env, SPCMP, cmp, NULL) == 0 );
void *db = sp_open(env);
t( db != NULL );
char buf[1];
t( sp_set(db, buf, UINT16_MAX + 1, buf, sizeof(buf)) == -1 );
t( sp_error(env) != NULL );
t( sp_destroy(db) == 0 );
t( sp_destroy(env) == 0 );
rmrf(dbrep);
}
static void
limit_value(void) {
void *env = sp_env();
t( env != NULL );
t( sp_ctl(env, SPDIR, SPO_CREAT|SPO_RDWR, dbrep) == 0 );
t( sp_ctl(env, SPCMP, cmp, NULL) == 0 );
void *db = sp_open(env);
t( db != NULL );
char buf[1];
t( sp_set(db, buf, sizeof(buf), buf, UINT32_MAX + 1ULL) == -1 );
t( sp_error(env) != NULL );
t( sp_destroy(db) == 0 );
t( sp_destroy(env) == 0 );
rmrf(dbrep);
}
int
main(int argc, char *argv[])
{
rmrf(dbrep);
test(limit_key);
if (sizeof(size_t) > 4)
test(limit_value);
return 0;
}

30
src/sophia/test/makefile Normal file
View File

@@ -0,0 +1,30 @@
CC ?= gcc
RM ?= rm
CFLAGS ?= -I. -I../db -Wall -pthread -O0 -g
LDFLAGS ?= -pthread -L../db -lsophia
all: clean i common recover merge crash limit
common: common.o
$(CC) common.o $(LDFLAGS) -o common
recover: recover.o
$(CC) recover.o $(LDFLAGS) -o recover
merge: merge.o
$(CC) merge.o $(LDFLAGS) -o merge
crash: crash.o
$(CC) crash.o $(LDFLAGS) -o crash
limit: limit.o
$(CC) limit.o $(LDFLAGS) -o limit
i: i.o
$(CC) i.o $(LDFLAGS) -o i
.c.o:
$(CC) $(CFLAGS) -c $<
clean:
$(RM) -f common.o common recover.o recover merge.o merge
$(RM) -f crash.o crash i.o i limit.o limit
test:
./i
./common
./recover
./merge
./crash
./limit

890
src/sophia/test/merge.c Normal file
View File

@@ -0,0 +1,890 @@
/*
* sophia database
* sphia.org
*
* Copyright (c) Dmitry Simonenko
* BSD License
*/
#include <sophia.h>
#include "test.h"
static char *dbrep = "./rep";
static inline int
cmp(char *a, size_t asz, char *b, size_t bsz, void *arg) {
register uint32_t av = *(uint32_t*)a;
register uint32_t bv = *(uint32_t*)b;
if (av == bv)
return 0;
return (av > bv) ? 1 : -1;
}
static void
merge_liveonly(void) {
void *env = sp_env();
t( env != NULL );
t( sp_ctl(env, SPDIR, SPO_CREAT|SPO_RDWR, dbrep) == 0 );
t( sp_ctl(env, SPCMP, cmp, NULL) == 0 );
t( sp_ctl(env, SPMERGE, 0) == 0 );
void *db = sp_open(env);
t( exists(dbrep, 1, "log.incomplete") == 1 );
t( sp_ctl(db, SPMERGEFORCE) == 0 );
t( exists(dbrep, 1, "log.incomplete") == 0 );
t( exists(dbrep, 1, "log") == 0 );
t( exists(dbrep, 1, "db") == 0 );
t( exists(dbrep, 1, "db.incomplete") == 0 );
t( exists(dbrep, 2, "log.incomplete") == 1 );
t( exists(dbrep, 2, "log") == 0 );
t( sp_destroy(db) == 0 );
t( exists(dbrep, 1, "log") == 0 );
t( exists(dbrep, 2, "log") == 0 );
t( sp_destroy(env) == 0 );
t( rmrf(dbrep) == 0 );
}
static void
merge_phase0(void) {
void *env = sp_env();
t( env != NULL );
t( sp_ctl(env, SPDIR, SPO_CREAT|SPO_RDWR, dbrep) == 0 );
t( sp_ctl(env, SPCMP, cmp, NULL) == 0 );
t( sp_ctl(env, SPMERGE, 0) == 0 );
void *db = sp_open(env);
t( exists(dbrep, 1, "log.incomplete") == 1 );
uint32_t k = 1, v = 1;
t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );
k = 2;
t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );
k = 3;
t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );
t( sp_ctl(db, SPMERGEFORCE) == 0 );
t( exists(dbrep, 1, "log.incomplete") == 0 );
t( exists(dbrep, 1, "log") == 0 );
t( exists(dbrep, 1, "db.incomplete") == 0 );
t( exists(dbrep, 1, "db") == 1 );
t( exists(dbrep, 2, "log.incomplete") == 1 );
t( sp_ctl(db, SPMERGEFORCE) == 0 );
t( sp_destroy(db) == 0 );
t( exists(dbrep, 1, "log.incomplete") == 0 );
t( exists(dbrep, 1, "log") == 0 );
t( exists(dbrep, 1, "db.incomplete") == 0 );
t( exists(dbrep, 1, "db") == 1 );
t( exists(dbrep, 2, "log.incomplete") == 0 );
t( exists(dbrep, 2, "log") == 0 );
t( exists(dbrep, 2, "db") == 0 );
t( sp_destroy(env) == 0 );
t( rmrf(dbrep) == 0 );
}
static void
merge_phase1(void) {
void *env = sp_env();
t( env != NULL );
t( sp_ctl(env, SPDIR, SPO_CREAT|SPO_RDWR, dbrep) == 0 );
t( sp_ctl(env, SPCMP, cmp, NULL) == 0 );
t( sp_ctl(env, SPGC, 0) == 0 );
t( sp_ctl(env, SPMERGE, 0) == 0 );
void *db = sp_open(env);
t( exists(dbrep, 1, "log.incomplete") == 1 );
uint32_t k = 1, v = 1;
t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );
k = 2;
t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );
k = 3;
t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );
t( sp_ctl(db, SPMERGEFORCE) == 0 );
t( exists(dbrep, 1, "log.incomplete") == 0 );
t( exists(dbrep, 1, "log") == 0 );
t( exists(dbrep, 1, "db.incomplete") == 0 );
t( exists(dbrep, 1, "db") == 1 );
t( exists(dbrep, 2, "log.incomplete") == 1 );
t( exists(dbrep, 2, "log") == 0 );
k = 4;
t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );
k = 5;
t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );
k = 6;
t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );
t( sp_ctl(db, SPMERGEFORCE) == 0 );
t( exists(dbrep, 1, "db") == 1 );
t( exists(dbrep, 2, "db.incomplete") == 0 );
t( exists(dbrep, 2, "db") == 1 );
t( exists(dbrep, 2, "log.incomplete") == 0 );
t( exists(dbrep, 2, "log") == 0 );
t( exists(dbrep, 3, "log.incomplete") == 1 );
t( exists(dbrep, 3, "log") == 0 );
t( sp_destroy(db) == 0 );
t( exists(dbrep, 1, "db") == 1 );
t( exists(dbrep, 2, "db.incomplete") == 0 );
t( exists(dbrep, 2, "db") == 1 );
t( exists(dbrep, 2, "log.incomplete") == 0 );
t( exists(dbrep, 2, "log") == 0 );
t( exists(dbrep, 3, "log.incomplete") == 0 );
t( exists(dbrep, 3, "log") == 0 );
t( sp_destroy(env) == 0 );
t( rmrf(dbrep) == 0 );
}
static void
merge_phase1gc(void) {
void *env = sp_env();
t( env != NULL );
t( sp_ctl(env, SPDIR, SPO_CREAT|SPO_RDWR, dbrep) == 0 );
t( sp_ctl(env, SPCMP, cmp, NULL) == 0 );
t( sp_ctl(env, SPGC, 1) == 0 );
t( sp_ctl(env, SPMERGE, 0) == 0 );
void *db = sp_open(env);
t( exists(dbrep, 1, "log.incomplete") == 1 );
uint32_t k = 1, v = 1;
t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );
k = 2;
t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );
k = 3;
t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );
t( sp_ctl(db, SPMERGEFORCE) == 0 );
t( exists(dbrep, 1, "log.incomplete") == 0 );
t( exists(dbrep, 1, "log") == 0 );
t( exists(dbrep, 1, "db.incomplete") == 0 );
t( exists(dbrep, 1, "db") == 1 );
t( exists(dbrep, 2, "log.incomplete") == 1 );
t( exists(dbrep, 2, "log") == 0 );
k = 4;
t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );
k = 5;
t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );
k = 6;
t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );
t( sp_ctl(db, SPMERGEFORCE) == 0 );
t( exists(dbrep, 1, "db") == 0 );
t( exists(dbrep, 2, "db.incomplete") == 0 );
t( exists(dbrep, 2, "db") == 1 );
t( exists(dbrep, 2, "log.incomplete") == 0 );
t( exists(dbrep, 2, "log") == 0 );
t( exists(dbrep, 3, "log.incomplete") == 1 );
t( exists(dbrep, 3, "log") == 0 );
t( sp_destroy(db) == 0 );
t( exists(dbrep, 1, "db") == 0 );
t( exists(dbrep, 2, "db.incomplete") == 0 );
t( exists(dbrep, 2, "db") == 1 );
t( exists(dbrep, 2, "log.incomplete") == 0 );
t( exists(dbrep, 2, "log") == 0 );
t( exists(dbrep, 3, "log.incomplete") == 0 );
t( exists(dbrep, 3, "log") == 0 );
t( sp_destroy(env) == 0 );
t( rmrf(dbrep) == 0 );
}
static void
merge_phase1n(void) {
void *env = sp_env();
t( env != NULL );
t( sp_ctl(env, SPDIR, SPO_CREAT|SPO_RDWR, dbrep) == 0 );
t( sp_ctl(env, SPCMP, cmp, NULL) == 0 );
t( sp_ctl(env, SPGC, 0) == 0 );
t( sp_ctl(env, SPMERGE, 0) == 0 );
void *db = sp_open(env);
uint32_t k = 1, v = 1;
/* 1 */
t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );
t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );
t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );
t( sp_ctl(db, SPMERGEFORCE) == 0 );
/* 2 */
t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );
t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );
t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );
t( sp_ctl(db, SPMERGEFORCE) == 0 );
/* 3 */
t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );
t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );
t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );
t( sp_ctl(db, SPMERGEFORCE) == 0 );
/* 4 */
t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );
t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );
t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );
t( sp_ctl(db, SPMERGEFORCE) == 0 );
/* 5 */
t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );
t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );
t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );
t( sp_ctl(db, SPMERGEFORCE) == 0 );
/* merge */
t( exists(dbrep, 1, "db") == 1 );
t( exists(dbrep, 2, "db") == 1 );
t( exists(dbrep, 3, "db") == 1 );
t( exists(dbrep, 4, "db") == 1 );
t( exists(dbrep, 5, "db") == 1 );
t( sp_destroy(db) == 0 );
t( sp_destroy(env) == 0 );
t( rmrf(dbrep) == 0 );
}
static void
merge_phase1ngc(void) {
void *env = sp_env();
t( env != NULL );
t( sp_ctl(env, SPDIR, SPO_CREAT|SPO_RDWR, dbrep) == 0 );
t( sp_ctl(env, SPCMP, cmp, NULL) == 0 );
t( sp_ctl(env, SPGC, 1) == 0 );
t( sp_ctl(env, SPMERGE, 0) == 0 );
void *db = sp_open(env);
uint32_t k = 1, v = 1;
/* 1 */
t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );
t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );
t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );
t( sp_ctl(db, SPMERGEFORCE) == 0 );
/* 2 */
t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );
t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );
t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );
t( sp_ctl(db, SPMERGEFORCE) == 0 );
/* 3 */
t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );
t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );
t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );
t( sp_ctl(db, SPMERGEFORCE) == 0 );
/* 4 */
t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );
t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );
t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );
t( sp_ctl(db, SPMERGEFORCE) == 0 );
/* 5 */
t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );
t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );
t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );
t( sp_ctl(db, SPMERGEFORCE) == 0 );
/* merge */
t( exists(dbrep, 1, "db") == 0 );
t( exists(dbrep, 2, "db") == 0 );
t( exists(dbrep, 3, "db") == 0 );
t( exists(dbrep, 4, "db") == 0 );
t( exists(dbrep, 5, "db") == 1 );
t( sp_destroy(db) == 0 );
t( sp_destroy(env) == 0 );
t( rmrf(dbrep) == 0 );
}
static void
merge_delete(void) {
void *env = sp_env();
t( env != NULL );
t( sp_ctl(env, SPDIR, SPO_CREAT|SPO_RDWR, dbrep) == 0 );
t( sp_ctl(env, SPCMP, cmp, NULL) == 0 );
t( sp_ctl(env, SPGC, 0) == 0 );
t( sp_ctl(env, SPMERGE, 0) == 0 );
void *db = sp_open(env);
t( db != NULL );
uint32_t k = 0, v = 1;
t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0);
k = 1;
t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0);
t( sp_destroy(db) == 0 );
t( exists(dbrep, 1, "log") == 1 );
t( exists(dbrep, 1, "log.incomplete") == 0 );
db = sp_open(env);
t( db != NULL );
k = 0;
t( sp_delete(db, &k, sizeof(k)) == 0 );
k = 1;
t( sp_delete(db, &k, sizeof(k)) == 0 );
t( exists(dbrep, 1, "log") == 1 );
t( exists(dbrep, 2, "log.incomplete") == 1 );
t( exists(dbrep, 2, "log") == 0 );
t( sp_ctl(db, SPMERGEFORCE) == 0);
t( exists(dbrep, 1, "log") == 0 );
t( exists(dbrep, 2, "log.incomplete") == 0 );
t( exists(dbrep, 2, "log") == 0 );
t( exists(dbrep, 3, "log.incomplete") == 1 );
t( exists(dbrep, 1, "db") == 0 );
t( exists(dbrep, 2, "db") == 0 );
t( sp_destroy(db) == 0 );
db = sp_open(env);
t( db != NULL );
k = 0;
size_t vsize = 0;
void *vp = NULL;
t( sp_get(db, &k, sizeof(k), &vp, &vsize) == 0 );
k = 1;
vsize = 0;
vp = NULL;
t( sp_get(db, &k, sizeof(k), &vp, &vsize) == 0 );
t( sp_destroy(db) == 0 );
t( sp_destroy(env) == 0 );
t( rmrf(dbrep) == 0 );
}
static void
merge_deletegc(void) {
void *env = sp_env();
t( env != NULL );
t( sp_ctl(env, SPDIR, SPO_CREAT|SPO_RDWR, dbrep) == 0 );
t( sp_ctl(env, SPCMP, cmp, NULL) == 0 );
t( sp_ctl(env, SPGC, 1) == 0 );
t( sp_ctl(env, SPMERGE, 0) == 0 );
void *db = sp_open(env);
t( db != NULL );
uint32_t k = 0, v = 1;
t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0);
k = 1;
t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0);
t( sp_destroy(db) == 0 );
t( exists(dbrep, 1, "log") == 1 );
t( exists(dbrep, 1, "log.incomplete") == 0 );
db = sp_open(env);
t( db != NULL );
k = 0;
t( sp_delete(db, &k, sizeof(k)) == 0 );
k = 1;
t( sp_delete(db, &k, sizeof(k)) == 0 );
t( exists(dbrep, 1, "log") == 1 );
t( exists(dbrep, 2, "log.incomplete") == 1 );
t( exists(dbrep, 2, "log") == 0 );
t( sp_ctl(db, SPMERGEFORCE) == 0 );
t( exists(dbrep, 1, "log") == 0 );
t( exists(dbrep, 2, "log.incomplete") == 0 );
t( exists(dbrep, 2, "log") == 0 );
t( exists(dbrep, 1, "db") == 0 );
t( exists(dbrep, 2, "db") == 0 );
t( sp_destroy(db) == 0 );
db = sp_open(env);
t( db != NULL );
k = 0;
size_t vsize = 0;
void *vp = NULL;
t( sp_get(db, &k, sizeof(k), &vp, &vsize) == 0 );
k = 1;
vsize = 0;
vp = NULL;
t( sp_get(db, &k, sizeof(k), &vp, &vsize) == 0 );
t( sp_destroy(db) == 0 );
t( sp_destroy(env) == 0 );
t( rmrf(dbrep) == 0 );
}
static void
merge_delete_log_n(void) {
void *env = sp_env();
t( env != NULL );
t( sp_ctl(env, SPDIR, SPO_CREAT|SPO_RDWR, dbrep) == 0 );
t( sp_ctl(env, SPCMP, cmp, NULL) == 0 );
t( sp_ctl(env, SPGC, 0) == 0 );
t( sp_ctl(env, SPMERGE, 0) == 0 );
void *db = sp_open(env);
t( db != NULL );
uint32_t k = 0, v = 1;
while (k < 12) {
t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0);
k++;
}
t( sp_destroy(db) == 0 );
t( exists(dbrep, 1, "log") == 1 );
db = sp_open(env);
t( db != NULL );
k = 0;
while (k < 12) {
t( sp_delete(db, &k, sizeof(k)) == 0);
k++;
}
t( exists(dbrep, 1, "log.incomplete") == 0 );
t( exists(dbrep, 1, "log") == 1 );
t( exists(dbrep, 2, "log.incomplete") == 1 );
t( sp_destroy(db) == 0 );
db = sp_open(env);
t( db != NULL );
k = 0;
while (k < 12) {
size_t vsize = 0;
void *vp = NULL;
t( sp_get(db, &k, sizeof(k), &vp, &vsize) == 0 );
k++;
}
t( sp_destroy(db) == 0 );
t( exists(dbrep, 1, "log") == 1 );
t( exists(dbrep, 2, "log") == 1 );
t( exists(dbrep, 3, "log") == 0 );
t( sp_destroy(env) == 0 );
t( rmrf(dbrep) == 0 );
}
static void
merge_delete_page_n(void) {
void *env = sp_env();
t( env != NULL );
t( sp_ctl(env, SPDIR, SPO_CREAT|SPO_RDWR, dbrep) == 0 );
t( sp_ctl(env, SPCMP, cmp, NULL) == 0 );
t( sp_ctl(env, SPGC, 0) == 0 );
t( sp_ctl(env, SPMERGE, 0) == 0 );
void *db = sp_open(env);
t( db != NULL );
uint32_t k = 0, v = 1;
while (k < 12) {
t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0);
k++;
}
t( sp_destroy(db) == 0 );
t( exists(dbrep, 1, "log") == 1 );
db = sp_open(env);
t( db != NULL );
k = 0;
while (k < 12) {
t( sp_delete(db, &k, sizeof(k)) == 0);
k++;
}
t( exists(dbrep, 1, "log") == 1 );
t( exists(dbrep, 2, "log.incomplete") == 1 );
t( sp_ctl(db, SPMERGEFORCE) == 0);
t( sp_destroy(db) == 0 );
db = sp_open(env);
t( db != NULL );
k = 0;
while (k < 12) {
size_t vsize = 0;
void *vp = NULL;
t( sp_get(db, &k, sizeof(k), &vp, &vsize) == 0 );
k++;
}
t( sp_destroy(db) == 0 );
t( exists(dbrep, 1, "log") == 0 );
t( exists(dbrep, 2, "log") == 0 );
t( exists(dbrep, 2, "db") == 0 );
t( sp_destroy(env) == 0 );
t( rmrf(dbrep) == 0 );
}
static void
merge_delete_page_log_n(void) {
void *env = sp_env();
t( env != NULL );
t( sp_ctl(env, SPDIR, SPO_CREAT|SPO_RDWR, dbrep) == 0 );
t( sp_ctl(env, SPCMP, cmp, NULL) == 0 );
t( sp_ctl(env, SPGC, 0) == 0 );
t( sp_ctl(env, SPMERGE, 0) == 0 );
void *db = sp_open(env);
t( db != NULL );
uint32_t k = 0, v = 1;
while (k < 12) {
t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0);
k++;
}
t( sp_ctl(db, SPMERGEFORCE) == 0);
t( exists(dbrep, 1, "db") == 1 );
t( sp_destroy(db) == 0 );
db = sp_open(env);
t( db != NULL );
k = 0;
while (k < 12) {
t( sp_delete(db, &k, sizeof(k)) == 0);
k++;
}
t( sp_ctl(db, SPMERGEFORCE) == 0);
t( exists(dbrep, 2, "log.incomplete") == 0 );
t( exists(dbrep, 2, "db") == 1 );
t( exists(dbrep, 3, "log.incomplete") == 1 );
t( sp_destroy(db) == 0 );
db = sp_open(env);
t( db != NULL );
k = 0;
while (k < 12) {
size_t vsize = 0;
void *vp = NULL;
t( sp_get(db, &k, sizeof(k), &vp, &vsize) == 0 );
k++;
}
t( sp_destroy(db) == 0 );
t( exists(dbrep, 1, "db") == 1 );
t( exists(dbrep, 2, "db") == 1 );
t( exists(dbrep, 3, "log") == 0 );
t( sp_destroy(env) == 0 );
t( rmrf(dbrep) == 0 );
}
static void
merge_delete_page_log_n_even(void) {
void *env = sp_env();
t( env != NULL );
t( sp_ctl(env, SPDIR, SPO_CREAT|SPO_RDWR, dbrep) == 0 );
t( sp_ctl(env, SPCMP, cmp, NULL) == 0 );
t( sp_ctl(env, SPGC, 0) == 0 );
t( sp_ctl(env, SPMERGE, 0) == 0 );
void *db = sp_open(env);
t( db != NULL );
uint32_t k = 0, v = 1;
while (k < 12) {
v = k;
t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0);
k++;
}
t( sp_ctl(db, SPMERGEFORCE) == 0);
t( sp_destroy(db) == 0 );
t( exists(dbrep, 1, "db") == 1 );
db = sp_open(env);
t( db != NULL );
k = 0;
while (k < 12) {
if (k > 0 && (k % 2) == 0)
t( sp_delete(db, &k, sizeof(k)) == 0);
k++;
}
t( exists(dbrep, 2, "log.incomplete") == 1 );
t( sp_ctl(db, SPMERGEFORCE) == 0);
t( sp_destroy(db) == 0 );
db = sp_open(env);
t( db != NULL );
k = 0;
while (k < 12) {
size_t vsize = 0;
void *vp = NULL;
if (k == 0 || (k % 2) != 0) {
t( sp_get(db, &k, sizeof(k), &vp, &vsize) == 1 );
t( vsize == sizeof(v) );
t( *(uint32_t*)vp == k );
free(vp);
}
k++;
}
t( sp_destroy(db) == 0 );
t( sp_destroy(env) == 0 );
t( rmrf(dbrep) == 0 );
}
static void
merge_delete_page_log_n_extra(void) {
void *env = sp_env();
t( env != NULL );
t( sp_ctl(env, SPDIR, SPO_CREAT|SPO_RDWR, dbrep) == 0 );
t( sp_ctl(env, SPCMP, cmp, NULL) == 0 );
t( sp_ctl(env, SPGC, 0) == 0 );
t( sp_ctl(env, SPMERGE, 0) == 0 );
void *db = sp_open(env);
t( db != NULL );
uint32_t k = 0, v = 1;
while (k < 12) {
t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0);
k++;
}
t( sp_destroy(db) == 0 );
t( exists(dbrep, 1, "log") == 1 );
db = sp_open(env);
t( db != NULL );
k = 0;
while (k < 12) {
t( sp_delete(db, &k, sizeof(k)) == 0);
k++;
}
k = 13;
v = k;
t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0);
t( exists(dbrep, 2, "log.incomplete") == 1 );
t( sp_ctl(db, SPMERGEFORCE) == 0);
t( exists(dbrep, 1, "db") == 0 );
t( exists(dbrep, 2, "db") == 1 );
t( sp_destroy(db) == 0 );
db = sp_open(env);
t( db != NULL );
k = 0;
while (k < 12) {
size_t vsize = 0;
void *vp = NULL;
t( sp_get(db, &k, sizeof(k), &vp, &vsize) == 0 );
k++;
}
k = 13;
size_t vsize = 0;
void *vp = NULL;
t( sp_get(db, &k, sizeof(k), &vp, &vsize) == 1 );
t( vsize == sizeof(v) );
t( *(uint32_t*)vp == 13 );
free(vp);
t( sp_destroy(db) == 0 );
t( sp_destroy(env) == 0 );
t( rmrf(dbrep) == 0 );
}
static void
merge_delete_page_log_n_fetch_gte(void) {
void *env = sp_env();
t( env != NULL );
t( sp_ctl(env, SPDIR, SPO_CREAT|SPO_RDWR, dbrep) == 0 );
t( sp_ctl(env, SPCMP, cmp, NULL) == 0 );
t( sp_ctl(env, SPGC, 0) == 0 );
t( sp_ctl(env, SPMERGE, 0) == 0 );
void *db = sp_open(env);
t( db != NULL );
uint32_t k = 0, v = 1;
while (k < 12) {
v = k;
t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0);
k++;
}
t( sp_destroy(db) == 0 );
db = sp_open(env);
t( db != NULL );
k = 0;
while (k < 12) {
if (k > 0 && (k % 2) == 0)
t( sp_delete(db, &k, sizeof(k)) == 0);
k++;
}
t( sp_ctl(db, SPMERGEFORCE) == 0);
t( sp_destroy(db) == 0 );
db = sp_open(env);
t( db != NULL );
void *cur = sp_cursor(db, SPGTE, NULL, 0);
t( cur != NULL );
t( sp_fetch(cur) == 1 );
t( *(uint32_t*)sp_key(cur) == 0 );
t( sp_keysize(cur) == sizeof(k) );
t( *(uint32_t*)sp_value(cur) == 0 );
t( sp_valuesize(cur) == sizeof(v) );
t( sp_fetch(cur) == 1 );
t( *(uint32_t*)sp_key(cur) == 1 );
t( sp_keysize(cur) == sizeof(k) );
t( *(uint32_t*)sp_value(cur) == 1 );
t( sp_valuesize(cur) == sizeof(v) );
t( sp_fetch(cur) == 1 );
t( *(uint32_t*)sp_key(cur) == 3 );
t( sp_keysize(cur) == sizeof(k) );
t( *(uint32_t*)sp_value(cur) == 3 );
t( sp_valuesize(cur) == sizeof(v) );
t( sp_fetch(cur) == 1 );
t( *(uint32_t*)sp_key(cur) == 5 );
t( sp_keysize(cur) == sizeof(k) );
t( *(uint32_t*)sp_value(cur) == 5 );
t( sp_valuesize(cur) == sizeof(v) );
t( sp_fetch(cur) == 1 );
t( *(uint32_t*)sp_key(cur) == 7 );
t( sp_keysize(cur) == sizeof(k) );
t( *(uint32_t*)sp_value(cur) == 7 );
t( sp_valuesize(cur) == sizeof(v) );
t( sp_fetch(cur) == 1 );
t( *(uint32_t*)sp_key(cur) == 9 );
t( sp_keysize(cur) == sizeof(k) );
t( *(uint32_t*)sp_value(cur) == 9 );
t( sp_valuesize(cur) == sizeof(v) );
t( sp_fetch(cur) == 1 );
t( *(uint32_t*)sp_key(cur) == 11 );
t( sp_keysize(cur) == sizeof(k) );
t( *(uint32_t*)sp_value(cur) == 11 );
t( sp_valuesize(cur) == sizeof(v) );
t( sp_fetch(cur) == 0 );
t( sp_destroy(cur) == 0 );
t( sp_destroy(db) == 0 );
t( sp_destroy(env) == 0 );
t( rmrf(dbrep) == 0 );
}
static void
merge_delete_page_log_n_fetch_lte(void) {
void *env = sp_env();
t( env != NULL );
t( sp_ctl(env, SPDIR, SPO_CREAT|SPO_RDWR, dbrep) == 0 );
t( sp_ctl(env, SPCMP, cmp, NULL) == 0 );
t( sp_ctl(env, SPGC, 0) == 0 );
t( sp_ctl(env, SPMERGE, 0) == 0 );
void *db = sp_open(env);
t( db != NULL );
uint32_t k = 0, v = 1;
while (k < 12) {
v = k;
t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0);
k++;
}
t( sp_destroy(db) == 0 );
db = sp_open(env);
t( db != NULL );
k = 0;
while (k < 12) {
if (k > 0 && (k % 2) == 0)
t( sp_delete(db, &k, sizeof(k)) == 0);
k++;
}
t( sp_ctl(db, SPMERGEFORCE) == 0);
t( sp_destroy(db) == 0 );
db = sp_open(env);
t( db != NULL );
void *cur = sp_cursor(db, SPLTE, NULL, 0);
t( cur != NULL );
t( sp_fetch(cur) == 1 );
t( *(uint32_t*)sp_key(cur) == 11 );
t( sp_keysize(cur) == sizeof(k) );
t( *(uint32_t*)sp_value(cur) == 11 );
t( sp_valuesize(cur) == sizeof(v) );
t( sp_fetch(cur) == 1 );
t( *(uint32_t*)sp_key(cur) == 9 );
t( sp_keysize(cur) == sizeof(k) );
t( *(uint32_t*)sp_value(cur) == 9 );
t( sp_valuesize(cur) == sizeof(v) );
t( sp_fetch(cur) == 1 );
t( *(uint32_t*)sp_key(cur) == 7 );
t( sp_keysize(cur) == sizeof(k) );
t( *(uint32_t*)sp_value(cur) == 7 );
t( sp_valuesize(cur) == sizeof(v) );
t( sp_fetch(cur) == 1 );
t( *(uint32_t*)sp_key(cur) == 5 );
t( sp_keysize(cur) == sizeof(k) );
t( *(uint32_t*)sp_value(cur) == 5 );
t( sp_valuesize(cur) == sizeof(v) );
t( sp_fetch(cur) == 1 );
t( *(uint32_t*)sp_key(cur) == 3 );
t( sp_keysize(cur) == sizeof(k) );
t( *(uint32_t*)sp_value(cur) == 3 );
t( sp_valuesize(cur) == sizeof(v) );
t( sp_fetch(cur) == 1 );
t( *(uint32_t*)sp_key(cur) == 1 );
t( sp_keysize(cur) == sizeof(k) );
t( *(uint32_t*)sp_value(cur) == 1 );
t( sp_valuesize(cur) == sizeof(v) );
t( sp_fetch(cur) == 1 );
t( *(uint32_t*)sp_key(cur) == 0 );
t( sp_keysize(cur) == sizeof(k) );
t( *(uint32_t*)sp_value(cur) == 0 );
t( sp_valuesize(cur) == sizeof(v) );
t( sp_fetch(cur) == 0 );
t( sp_destroy(cur) == 0 );
t( sp_destroy(db) == 0 );
t( sp_destroy(env) == 0 );
t( rmrf(dbrep) == 0 );
}
static void
merge_delete_page_log_n_fetch_kgte(void) {
void *env = sp_env();
t( env != NULL );
t( sp_ctl(env, SPDIR, SPO_CREAT|SPO_RDWR, dbrep) == 0 );
t( sp_ctl(env, SPCMP, cmp, NULL) == 0 );
t( sp_ctl(env, SPGC, 0) == 0 );
t( sp_ctl(env, SPMERGE, 0) == 0 );
void *db = sp_open(env);
t( db != NULL );
uint32_t k = 0, v = 1;
while (k < 12) {
v = k;
t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0);
k++;
}
t( sp_destroy(db) == 0 );
db = sp_open(env);
t( db != NULL );
k = 0;
while (k < 12) {
if (k > 0 && (k % 2) == 0)
t( sp_delete(db, &k, sizeof(k)) == 0);
k++;
}
t( sp_ctl(db, SPMERGEFORCE) == 0);
t( sp_destroy(db) == 0 );
db = sp_open(env);
t( db != NULL );
k = 6;
void *cur = sp_cursor(db, SPGTE, &k, sizeof(k));
t( cur != NULL );
t( sp_fetch(cur) == 1 );
t( *(uint32_t*)sp_key(cur) == 7 );
t( sp_keysize(cur) == sizeof(k) );
t( *(uint32_t*)sp_value(cur) == 7 );
t( sp_valuesize(cur) == sizeof(v) );
t( sp_fetch(cur) == 1 );
t( *(uint32_t*)sp_key(cur) == 9 );
t( sp_keysize(cur) == sizeof(k) );
t( *(uint32_t*)sp_value(cur) == 9 );
t( sp_valuesize(cur) == sizeof(v) );
t( sp_fetch(cur) == 1 );
t( *(uint32_t*)sp_key(cur) == 11 );
t( sp_keysize(cur) == sizeof(k) );
t( *(uint32_t*)sp_value(cur) == 11 );
t( sp_valuesize(cur) == sizeof(v) );
t( sp_fetch(cur) == 0 );
t( sp_destroy(cur) == 0 );
t( sp_destroy(db) == 0 );
t( sp_destroy(env) == 0 );
t( rmrf(dbrep) == 0 );
}
static void
merge_delete_page_log_n_fetch_klte(void) {
void *env = sp_env();
t( env != NULL );
t( sp_ctl(env, SPDIR, SPO_CREAT|SPO_RDWR, dbrep) == 0 );
t( sp_ctl(env, SPCMP, cmp, NULL) == 0 );
t( sp_ctl(env, SPGC, 0) == 0 );
t( sp_ctl(env, SPMERGE, 0) == 0 );
void *db = sp_open(env);
t( db != NULL );
uint32_t k = 0, v = 1;
while (k < 12) {
v = k;
t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0);
k++;
}
t( sp_destroy(db) == 0 );
db = sp_open(env);
t( db != NULL );
k = 0;
while (k < 12) {
if (k > 0 && (k % 2) == 0)
t( sp_delete(db, &k, sizeof(k)) == 0);
k++;
}
t( sp_ctl(db, SPMERGEFORCE) == 0);
t( sp_destroy(db) == 0 );
db = sp_open(env);
t( db != NULL );
k = 6;
void *cur = sp_cursor(db, SPLTE, &k, sizeof(k));
t( cur != NULL );
t( sp_fetch(cur) == 1 );
t( *(uint32_t*)sp_key(cur) == 5 );
t( sp_keysize(cur) == sizeof(k) );
t( *(uint32_t*)sp_value(cur) == 5 );
t( sp_valuesize(cur) == sizeof(v) );
t( sp_fetch(cur) == 1 );
t( *(uint32_t*)sp_key(cur) == 3 );
t( sp_keysize(cur) == sizeof(k) );
t( *(uint32_t*)sp_value(cur) == 3 );
t( sp_valuesize(cur) == sizeof(v) );
t( sp_fetch(cur) == 1 );
t( *(uint32_t*)sp_key(cur) == 1 );
t( sp_keysize(cur) == sizeof(k) );
t( *(uint32_t*)sp_value(cur) == 1 );
t( sp_valuesize(cur) == sizeof(v) );
t( sp_fetch(cur) == 1 );
t( *(uint32_t*)sp_key(cur) == 0 );
t( sp_keysize(cur) == sizeof(k) );
t( *(uint32_t*)sp_value(cur) == 0 );
t( sp_valuesize(cur) == sizeof(v) );
t( sp_fetch(cur) == 0 );
t( sp_destroy(cur) == 0 );
t( sp_destroy(db) == 0 );
t( sp_destroy(env) == 0 );
t( rmrf(dbrep) == 0 );
}
int
main(int argc, char *argv[])
{
rmrf(dbrep);
test(merge_liveonly);
test(merge_phase0);
test(merge_phase1);
test(merge_phase1gc);
test(merge_phase1n);
test(merge_phase1ngc);
test(merge_delete);
test(merge_deletegc);
test(merge_delete_log_n);
test(merge_delete_page_n);
test(merge_delete_page_log_n);
test(merge_delete_page_log_n_even);
test(merge_delete_page_log_n_extra);
test(merge_delete_page_log_n_fetch_gte);
test(merge_delete_page_log_n_fetch_lte);
test(merge_delete_page_log_n_fetch_kgte);
test(merge_delete_page_log_n_fetch_klte);
return 0;
}

1550
src/sophia/test/recover.c Normal file

File diff suppressed because it is too large Load Diff

66
src/sophia/test/test.h Normal file
View File

@@ -0,0 +1,66 @@
#ifndef SP_TEST_H_
#define SP_TEST_H_
/*
* sophia database
* sphia.org
*
* Copyright (c) Dmitry Simonenko
* BSD License
*/
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <inttypes.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <dirent.h>
#define t(expr) ({ \
if (! (expr)) { \
printf("fail (%s:%d) %s\n", __FILE__, __LINE__, #expr); \
fflush(NULL); \
abort(); \
} \
})
#define test(f) ({ \
printf("%s: ", #f); \
fflush(NULL); \
f(); \
printf("ok\n"); \
fflush(NULL); \
})
static inline int
exists(char *path, uint32_t epoch, char *ext) {
char file[1024];
snprintf(file, sizeof(file), "%s/%"PRIu32".%s", path, epoch, ext);
struct stat st;
return lstat(file, &st) == 0;
}
static inline int rmrf(char *path) {
DIR *d = opendir(path);
if (d == NULL)
return -1;
char file[1024];
struct dirent *de;
while ((de = readdir(d))) {
if (de->d_name[0] == '.')
continue;
snprintf(file, sizeof(file), "%s/%s", path, de->d_name);
int rc = unlink(file);
if (rc == -1) {
closedir(d);
return -1;
}
}
closedir(d);
return rmdir(path);
}
#endif