README.md
1IOWOW - The C11 persistent key/value database engine based on [skip list](https://en.wikipedia.org/wiki/Skip_list)
2==================================================================================================================
3
4![Build Status](https://dev.softmotions.com/jenkins/buildStatus/icon?job=iowow_test)
5[![Join ejdb2 telegram](https://img.shields.io/badge/join-ejdb2%20telegram-0088cc.svg)](https://t.me/ejdb2)
6[![license](https://img.shields.io/github/license/Softmotions/ejdb.svg)](https://github.com/Softmotions/iowow/blob/master/LICENSE)
7![Maintained](https://img.shields.io/maintenance/yes/2020.svg)
8
9Website http://iowow.io
10
11# Key components
12
13* [iwkv.h](https://github.com/Softmotions/iowow/blob/master/src/kv/iwkv.h) Persistent key/value database engine
14* [iwfsmfile.h](https://github.com/Softmotions/iowow/blob/master/src/fs/iwfsmfile.h) File blocks allocation manager like `malloc()` on files
15
16# IWKV
17
18## Features
19
20* Support of multiple key-value databases within a single file
21* Online database backups
22* Native support of integer keys
23* [Write Ahead Logging (WAL) support](http://iowow.io/wal)
24* Ultra-fast traversal of database records
25* Compound keys support
26* Good performance comparing its main competitors: `lmdb`, `leveldb`, `kyoto cabinet`
27* Tiny C11 library (200Kb) can be easily embedded into any software
28
29[![Presentation](https://iowow.io/articles/iowow-presentation-cover-small.png)](https://iowow.io/articles/intro/)
30
31
32## Used by
33
34* EJDB - Embeddable JSON database engine. http://ejdb.org
35
36## Limitations
37
38* Maximum iwkv storage file size: `512 GB (0x7fffffff80)`
39* Total size of a single key+value record must be not greater than `255Mb (0xfffffff)`
40* In-memory cache for every opened database takes `~130Kb`, cache can be disposed by `iwkv_db_cache_release()`
41
42# Supported platforms
43
44## Linux
45### Ubuntu/Debian
46#### PPA repository
47
48```sh
49sudo add-apt-repository ppa:adamansky/iwowow
50sudo apt-get update
51sudo apt-get install iowow
52```
53
54#### Building debian packages
55
56```sh
57mkdir build && cd build
58cmake .. -DCMAKE_BUILD_TYPE=Release -DPACKAGE_DEB=ON
59make package
60```
61
62### RPM based Linux distributions
63```sh
64mkdir build && cd build
65cmake .. -DCMAKE_BUILD_TYPE=Release -DPACKAGE_RPM=ON
66make package
67```
68
69## FreeBSD
70
71Successfully tested on FreeBSD 10/11
72
73## OSX
74
75Successfully tested on OSX 10.12/10.13
76
77## Windows
78
79[Cross-compilation for windows](http://iowow.io/iw/win)
80
81
82## MIPS based systems (+big-endian)
83
84Successfully tested on Debian 9.4, MIPS 32, gcc 6.x compiler.
85
86# Examples
87
88[src/kv/examples](https://github.com/Softmotions/iowow/tree/master/src/kv/examples)
89
90## Store and retrieve records
91
92```c
93#include <iowow/iwkv.h>
94#include <string.h>
95#include <stdlib.h>
96
97int main() {
98 IWKV_OPTS opts = {
99 .path = "example1.db",
100 .oflags = IWKV_TRUNC // Cleanup database before open
101 };
102 IWKV iwkv;
103 IWDB mydb;
104 iwrc rc = iwkv_open(&opts, &iwkv);
105 if (rc) {
106 iwlog_ecode_error3(rc);
107 return 1;
108 }
109 // Now open mydb
110 // - Database id: 1
111 rc = iwkv_db(iwkv, 1, 0, &mydb);
112 if (rc) {
113 iwlog_ecode_error2(rc, "Failed to open mydb");
114 return 1;
115 }
116 // Work with db: put/get value
117 IWKV_val key, val;
118 key.data = "foo";
119 key.size = strlen(key.data);
120 val.data = "bar";
121 val.size = strlen(val.data);
122
123 fprintf(stdout, "put: %.*s => %.*s\n",
124 (int) key.size, (char *) key.data,
125 (int) val.size, (char *) val.data);
126
127 rc = iwkv_put(mydb, &key, &val, 0);
128 if (rc) {
129 iwlog_ecode_error3(rc);
130 return rc;
131 }
132 // Retrieve value associated with `foo` key
133 val.data = 0;
134 val.size = 0;
135 rc = iwkv_get(mydb, &key, &val);
136 if (rc) {
137 iwlog_ecode_error3(rc);
138 return rc;
139 }
140
141 fprintf(stdout, "get: %.*s => %.*s\n",
142 (int) key.size, (char *) key.data,
143 (int) val.size, (char *) val.data);
144
145 iwkv_val_dispose(&val);
146 iwkv_close(&iwkv);
147 return 0;
148}
149```
150**Compile and run:**
151
152```sh
153gcc -std=gnu11 -Wall -pedantic -c -o example1.o example1.c
154gcc -o example1 example1.o -liowow
155
156./example1
157 put: foo => bar
158 get: foo => bar
159```
160
161## Cursor iteration example
162
163```c
164///
165/// Fills database with a set of football table records
166/// then traverse records according to club name in ascending and descending orders.
167///
168
169#include "iwkv.h"
170#include <string.h>
171#include <stdlib.h>
172#include <stdint.h>
173
174static struct data_s {
175 const char *club;
176 uint8_t points;
177} _points[] = {
178
179 { "Aston Villa", 25 },
180 { "Manchester City", 57 },
181 { "Arsenal", 40 },
182 { "Everton", 37 },
183 { "West Ham United", 27 },
184 { "Tottenham Hotspur", 41 },
185 { "Wolverhampton Wanderers", 43 },
186 { "Norwich City", 21 },
187 { "Leicester City", 53 },
188 { "Manchester United", 45 },
189 { "Newcastle United", 35 },
190 { "Brighton & Hove Albion", 29 },
191 { "AFC Bournemouth", 27 },
192 { "Crystal Palace", 39 },
193 { "Sheffield United", 43 },
194 { "Burnley", 39 },
195 { "Southampton", 34 },
196 { "Watford", 27 },
197 { "Chelsea", 48 },
198 { "Liverpool", 82 },
199};
200
201static iwrc run(void) {
202 IWKV_OPTS opts = {
203 .path = "cursor1.db",
204 .oflags = IWKV_TRUNC // Cleanup database before open
205 };
206 IWKV iwkv;
207 IWDB db;
208 IWKV_cursor cur = 0;
209 iwrc rc = iwkv_open(&opts, &iwkv);
210 RCRET(rc);
211
212 rc = iwkv_db(iwkv, 1, 0, &db);
213 RCGO(rc, finish);
214
215 for (int i = 0; i < sizeof(_points) / sizeof(_points[0]); ++i) {
216 struct data_s *n = &_points[i];
217 IWKV_val key = { .data = (void *) n->club, .size = strlen(n->club) };
218 IWKV_val val = { .data = &n->points, .size = sizeof(n->points) };
219 RCC(rc, finish, iwkv_put(db, &key, &val, 0));
220 }
221
222 fprintf(stdout, ">>>> Traverse in descending order\n");
223 RCC(rc, finish, iwkv_cursor_open(db, &cur, IWKV_CURSOR_BEFORE_FIRST, 0));
224 while ((rc = iwkv_cursor_to(cur, IWKV_CURSOR_NEXT)) == 0) {
225 IWKV_val key, val;
226 RCC(rc, finish, iwkv_cursor_get(cur, &key, &val));
227 fprintf(stdout, "%.*s: %u\n",
228 (int) key.size, (char *) key.data,
229 *(uint8_t *) val.data);
230 iwkv_kv_dispose(&key, &val);
231 }
232 rc = 0;
233 iwkv_cursor_close(&cur);
234
235 fprintf(stdout, "\n>>>> Traverse in ascending order\n");
236 RCC(rc, finish, iwkv_cursor_open(db, &cur, IWKV_CURSOR_AFTER_LAST, 0));
237 while ((rc = iwkv_cursor_to(cur, IWKV_CURSOR_PREV)) == 0) {
238 IWKV_val key, val;
239 RCC(rc, finish, iwkv_cursor_get(cur, &key, &val));
240 fprintf(stdout, "%.*s: %u\n",
241 (int) key.size, (char *) key.data,
242 *(uint8_t *) val.data);
243 iwkv_kv_dispose(&key, &val);
244 }
245 rc = 0;
246 iwkv_cursor_close(&cur);
247
248 // Select all keys greater or equal than: Manchester United
249 {
250 fprintf(stdout, "\n>>>> Records GE: %s\n", _points[9].club);
251 IWKV_val key = { .data = (void *) _points[9].club, .size = strlen(_points[9].club) }, val;
252 RCC(rc, finish, iwkv_cursor_open(db, &cur, IWKV_CURSOR_GE, &key));
253 do {
254 RCC(rc, finish, iwkv_cursor_get(cur, &key, &val));
255 fprintf(stdout, "%.*s: %u\n",
256 (int) key.size, (char *) key.data,
257 *(uint8_t *) val.data);
258 iwkv_kv_dispose(&key, &val);
259 } while ((rc = iwkv_cursor_to(cur, IWKV_CURSOR_NEXT)) == 0);
260 rc = 0;
261 }
262 iwkv_cursor_close(&cur);
263
264finish:
265 if (cur) {
266 iwkv_cursor_close(&cur);
267 }
268 iwkv_close(&iwkv);
269 return rc;
270}
271
272int main() {
273 iwrc rc = run();
274 if (rc) {
275 iwlog_ecode_error3(rc);
276 return 1;
277 }
278 return 0;
279}
280```
281
282Output:
283```
284>>>> Traverse in descending order
285Wolverhampton Wanderers: 43
286West Ham United: 27
287Watford: 27
288Tottenham Hotspur: 41
289Southampton: 34
290Sheffield United: 43
291Norwich City: 21
292Newcastle United: 35
293Manchester United: 45
294Manchester City: 57
295Liverpool: 82
296Leicester City: 53
297Everton: 37
298Crystal Palace: 39
299Chelsea: 48
300Burnley: 39
301Brighton & Hove Albion: 29
302Aston Villa: 25
303Arsenal: 40
304AFC Bournemouth: 27
305
306>>>> Traverse in ascending order
307AFC Bournemouth: 27
308Arsenal: 40
309Aston Villa: 25
310Brighton & Hove Albion: 29
311Burnley: 39
312Chelsea: 48
313Crystal Palace: 39
314Everton: 37
315Leicester City: 53
316Liverpool: 82
317Manchester City: 57
318Manchester United: 45
319Newcastle United: 35
320Norwich City: 21
321Sheffield United: 43
322Southampton: 34
323Tottenham Hotspur: 41
324Watford: 27
325West Ham United: 27
326Wolverhampton Wanderers: 43
327
328>>>> Records GE: Manchester United
329Manchester United: 45
330Manchester City: 57
331Liverpool: 82
332Leicester City: 53
333Everton: 37
334Crystal Palace: 39
335Chelsea: 48
336Burnley: 39
337Brighton & Hove Albion: 29
338Aston Villa: 25
339Arsenal: 40
340AFC Bournemouth: 27
341```
342
343
344