summaryrefslogtreecommitdiffstats
path: root/common/unqlite/mem_kv.c
diff options
context:
space:
mode:
authorAaron Seigo <aseigo@kde.org>2014-12-14 12:00:05 +0100
committerAaron Seigo <aseigo@kde.org>2014-12-14 12:00:05 +0100
commit7cc25005b8c46d1fa783d33def2c6923e8ef8469 (patch)
tree64fa59d17af29838396cf37b912b3babd885e5dd /common/unqlite/mem_kv.c
parentbfc32f265e8ad72823db960fed371d72596003b7 (diff)
parenta6ed70495f9f3ecb21c26860dda16aadcdc91c3a (diff)
downloadsink-7cc25005b8c46d1fa783d33def2c6923e8ef8469.tar.gz
sink-7cc25005b8c46d1fa783d33def2c6923e8ef8469.zip
Merge branch 'unqlite'
Diffstat (limited to 'common/unqlite/mem_kv.c')
-rw-r--r--common/unqlite/mem_kv.c678
1 files changed, 678 insertions, 0 deletions
diff --git a/common/unqlite/mem_kv.c b/common/unqlite/mem_kv.c
new file mode 100644
index 0000000..bdd3776
--- /dev/null
+++ b/common/unqlite/mem_kv.c
@@ -0,0 +1,678 @@
1/*
2 * Symisc unQLite: An Embeddable NoSQL (Post Modern) Database Engine.
3 * Copyright (C) 2012-2013, Symisc Systems http://unqlite.org/
4 * Version 1.1.6
5 * For information on licensing, redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES
6 * please contact Symisc Systems via:
7 * legal@symisc.net
8 * licensing@symisc.net
9 * contact@symisc.net
10 * or visit:
11 * http://unqlite.org/licensing.html
12 */
13 /* $SymiscID: mem_kv.c v1.7 Win7 2012-11-28 01:41 stable <chm@symisc.net> $ */
14#ifndef UNQLITE_AMALGAMATION
15#include "unqliteInt.h"
16#endif
17/*
18 * This file implements an in-memory key value storage engine for unQLite.
19 * Note that this storage engine does not support transactions.
20 *
21 * Normaly, I (chm@symisc.net) planned to implement a red-black tree
22 * which is suitable for this kind of operation, but due to the lack
23 * of time, I decided to implement a tunned hashtable which everybody
24 * know works very well for this kind of operation.
25 * Again, I insist on a red-black tree implementation for future version
26 * of Unqlite.
27 */
28/* Forward declaration */
29typedef struct mem_hash_kv_engine mem_hash_kv_engine;
30/*
31 * Each record is storead in an instance of the following structure.
32 */
33typedef struct mem_hash_record mem_hash_record;
34struct mem_hash_record
35{
36 mem_hash_kv_engine *pEngine; /* Storage engine */
37 sxu32 nHash; /* Hash of the key */
38 const void *pKey; /* Key */
39 sxu32 nKeyLen; /* Key size (Max 1GB) */
40 const void *pData; /* Data */
41 sxu32 nDataLen; /* Data length (Max 4GB) */
42 mem_hash_record *pNext,*pPrev; /* Link to other records */
43 mem_hash_record *pNextHash,*pPrevHash; /* Collision link */
44};
45/*
46 * Each in-memory KV engine is represented by an instance
47 * of the following structure.
48 */
49struct mem_hash_kv_engine
50{
51 const unqlite_kv_io *pIo; /* IO methods: MUST be first */
52 /* Private data */
53 SyMemBackend sAlloc; /* Private memory allocator */
54 ProcHash xHash; /* Default hash function */
55 ProcCmp xCmp; /* Default comparison function */
56 sxu32 nRecord; /* Total number of records */
57 sxu32 nBucket; /* Bucket size: Must be a power of two */
58 mem_hash_record **apBucket; /* Hash bucket */
59 mem_hash_record *pFirst; /* First inserted entry */
60 mem_hash_record *pLast; /* Last inserted entry */
61};
62/*
63 * Allocate a new hash record.
64 */
65static mem_hash_record * MemHashNewRecord(
66 mem_hash_kv_engine *pEngine,
67 const void *pKey,int nKey,
68 const void *pData,unqlite_int64 nData,
69 sxu32 nHash
70 )
71{
72 SyMemBackend *pAlloc = &pEngine->sAlloc;
73 mem_hash_record *pRecord;
74 void *pDupData;
75 sxu32 nByte;
76 char *zPtr;
77
78 /* Total number of bytes to alloc */
79 nByte = sizeof(mem_hash_record) + nKey;
80 /* Allocate a new instance */
81 pRecord = (mem_hash_record *)SyMemBackendAlloc(pAlloc,nByte);
82 if( pRecord == 0 ){
83 return 0;
84 }
85 pDupData = (void *)SyMemBackendAlloc(pAlloc,(sxu32)nData);
86 if( pDupData == 0 ){
87 SyMemBackendFree(pAlloc,pRecord);
88 return 0;
89 }
90 zPtr = (char *)pRecord;
91 zPtr += sizeof(mem_hash_record);
92 /* Zero the structure */
93 SyZero(pRecord,sizeof(mem_hash_record));
94 /* Fill in the structure */
95 pRecord->pEngine = pEngine;
96 pRecord->nDataLen = (sxu32)nData;
97 pRecord->nKeyLen = (sxu32)nKey;
98 pRecord->nHash = nHash;
99 SyMemcpy(pKey,zPtr,pRecord->nKeyLen);
100 pRecord->pKey = (const void *)zPtr;
101 SyMemcpy(pData,pDupData,pRecord->nDataLen);
102 pRecord->pData = pDupData;
103 /* All done */
104 return pRecord;
105}
106/*
107 * Install a given record in the hashtable.
108 */
109static void MemHashLinkRecord(mem_hash_kv_engine *pEngine,mem_hash_record *pRecord)
110{
111 sxu32 nBucket = pRecord->nHash & (pEngine->nBucket - 1);
112 pRecord->pNextHash = pEngine->apBucket[nBucket];
113 if( pEngine->apBucket[nBucket] ){
114 pEngine->apBucket[nBucket]->pPrevHash = pRecord;
115 }
116 pEngine->apBucket[nBucket] = pRecord;
117 if( pEngine->pFirst == 0 ){
118 pEngine->pFirst = pEngine->pLast = pRecord;
119 }else{
120 MACRO_LD_PUSH(pEngine->pLast,pRecord);
121 }
122 pEngine->nRecord++;
123}
124/*
125 * Unlink a given record from the hashtable.
126 */
127static void MemHashUnlinkRecord(mem_hash_kv_engine *pEngine,mem_hash_record *pEntry)
128{
129 sxu32 nBucket = pEntry->nHash & (pEngine->nBucket - 1);
130 SyMemBackend *pAlloc = &pEngine->sAlloc;
131 if( pEntry->pPrevHash == 0 ){
132 pEngine->apBucket[nBucket] = pEntry->pNextHash;
133 }else{
134 pEntry->pPrevHash->pNextHash = pEntry->pNextHash;
135 }
136 if( pEntry->pNextHash ){
137 pEntry->pNextHash->pPrevHash = pEntry->pPrevHash;
138 }
139 MACRO_LD_REMOVE(pEngine->pLast,pEntry);
140 if( pEntry == pEngine->pFirst ){
141 pEngine->pFirst = pEntry->pPrev;
142 }
143 pEngine->nRecord--;
144 /* Release the entry */
145 SyMemBackendFree(pAlloc,(void *)pEntry->pData);
146 SyMemBackendFree(pAlloc,pEntry); /* Key is also stored here */
147}
148/*
149 * Perform a lookup for a given entry.
150 */
151static mem_hash_record * MemHashGetEntry(
152 mem_hash_kv_engine *pEngine,
153 const void *pKey,int nKeyLen
154 )
155{
156 mem_hash_record *pEntry;
157 sxu32 nHash,nBucket;
158 /* Hash the entry */
159 nHash = pEngine->xHash(pKey,(sxu32)nKeyLen);
160 nBucket = nHash & (pEngine->nBucket - 1);
161 pEntry = pEngine->apBucket[nBucket];
162 for(;;){
163 if( pEntry == 0 ){
164 break;
165 }
166 if( pEntry->nHash == nHash && pEntry->nKeyLen == (sxu32)nKeyLen &&
167 pEngine->xCmp(pEntry->pKey,pKey,pEntry->nKeyLen) == 0 ){
168 return pEntry;
169 }
170 pEntry = pEntry->pNextHash;
171 }
172 /* No such entry */
173 return 0;
174}
175/*
176 * Rehash all the entries in the given table.
177 */
178static int MemHashGrowTable(mem_hash_kv_engine *pEngine)
179{
180 sxu32 nNewSize = pEngine->nBucket << 1;
181 mem_hash_record *pEntry;
182 mem_hash_record **apNew;
183 sxu32 n,iBucket;
184 /* Allocate a new larger table */
185 apNew = (mem_hash_record **)SyMemBackendAlloc(&pEngine->sAlloc, nNewSize * sizeof(mem_hash_record *));
186 if( apNew == 0 ){
187 /* Not so fatal, simply a performance hit */
188 return UNQLITE_OK;
189 }
190 /* Zero the new table */
191 SyZero((void *)apNew, nNewSize * sizeof(mem_hash_record *));
192 /* Rehash all entries */
193 n = 0;
194 pEntry = pEngine->pLast;
195 for(;;){
196
197 /* Loop one */
198 if( n >= pEngine->nRecord ){
199 break;
200 }
201 pEntry->pNextHash = pEntry->pPrevHash = 0;
202 /* Install in the new bucket */
203 iBucket = pEntry->nHash & (nNewSize - 1);
204 pEntry->pNextHash = apNew[iBucket];
205 if( apNew[iBucket] ){
206 apNew[iBucket]->pPrevHash = pEntry;
207 }
208 apNew[iBucket] = pEntry;
209 /* Point to the next entry */
210 pEntry = pEntry->pNext;
211 n++;
212
213 /* Loop two */
214 if( n >= pEngine->nRecord ){
215 break;
216 }
217 pEntry->pNextHash = pEntry->pPrevHash = 0;
218 /* Install in the new bucket */
219 iBucket = pEntry->nHash & (nNewSize - 1);
220 pEntry->pNextHash = apNew[iBucket];
221 if( apNew[iBucket] ){
222 apNew[iBucket]->pPrevHash = pEntry;
223 }
224 apNew[iBucket] = pEntry;
225 /* Point to the next entry */
226 pEntry = pEntry->pNext;
227 n++;
228
229 /* Loop three */
230 if( n >= pEngine->nRecord ){
231 break;
232 }
233 pEntry->pNextHash = pEntry->pPrevHash = 0;
234 /* Install in the new bucket */
235 iBucket = pEntry->nHash & (nNewSize - 1);
236 pEntry->pNextHash = apNew[iBucket];
237 if( apNew[iBucket] ){
238 apNew[iBucket]->pPrevHash = pEntry;
239 }
240 apNew[iBucket] = pEntry;
241 /* Point to the next entry */
242 pEntry = pEntry->pNext;
243 n++;
244
245 /* Loop four */
246 if( n >= pEngine->nRecord ){
247 break;
248 }
249 pEntry->pNextHash = pEntry->pPrevHash = 0;
250 /* Install in the new bucket */
251 iBucket = pEntry->nHash & (nNewSize - 1);
252 pEntry->pNextHash = apNew[iBucket];
253 if( apNew[iBucket] ){
254 apNew[iBucket]->pPrevHash = pEntry;
255 }
256 apNew[iBucket] = pEntry;
257 /* Point to the next entry */
258 pEntry = pEntry->pNext;
259 n++;
260 }
261 /* Release the old table and reflect the change */
262 SyMemBackendFree(&pEngine->sAlloc,(void *)pEngine->apBucket);
263 pEngine->apBucket = apNew;
264 pEngine->nBucket = nNewSize;
265 return UNQLITE_OK;
266}
267/*
268 * Exported Interfaces.
269 */
270/*
271 * Each public cursor is identified by an instance of this structure.
272 */
273typedef struct mem_hash_cursor mem_hash_cursor;
274struct mem_hash_cursor
275{
276 unqlite_kv_engine *pStore; /* Must be first */
277 /* Private fields */
278 mem_hash_record *pCur; /* Current hash record */
279};
280/*
281 * Initialize the cursor.
282 */
283static void MemHashInitCursor(unqlite_kv_cursor *pCursor)
284{
285 mem_hash_kv_engine *pEngine = (mem_hash_kv_engine *)pCursor->pStore;
286 mem_hash_cursor *pMem = (mem_hash_cursor *)pCursor;
287 /* Point to the first inserted entry */
288 pMem->pCur = pEngine->pFirst;
289}
290/*
291 * Point to the first entry.
292 */
293static int MemHashCursorFirst(unqlite_kv_cursor *pCursor)
294{
295 mem_hash_kv_engine *pEngine = (mem_hash_kv_engine *)pCursor->pStore;
296 mem_hash_cursor *pMem = (mem_hash_cursor *)pCursor;
297 pMem->pCur = pEngine->pFirst;
298 return UNQLITE_OK;
299}
300/*
301 * Point to the last entry.
302 */
303static int MemHashCursorLast(unqlite_kv_cursor *pCursor)
304{
305 mem_hash_kv_engine *pEngine = (mem_hash_kv_engine *)pCursor->pStore;
306 mem_hash_cursor *pMem = (mem_hash_cursor *)pCursor;
307 pMem->pCur = pEngine->pLast;
308 return UNQLITE_OK;
309}
310/*
311 * is a Valid Cursor.
312 */
313static int MemHashCursorValid(unqlite_kv_cursor *pCursor)
314{
315 mem_hash_cursor *pMem = (mem_hash_cursor *)pCursor;
316 return pMem->pCur != 0 ? 1 : 0;
317}
318/*
319 * Point to the next entry.
320 */
321static int MemHashCursorNext(unqlite_kv_cursor *pCursor)
322{
323 mem_hash_cursor *pMem = (mem_hash_cursor *)pCursor;
324 if( pMem->pCur == 0){
325 return UNQLITE_EOF;
326 }
327 pMem->pCur = pMem->pCur->pPrev; /* Reverse link: Not a Bug */
328 return UNQLITE_OK;
329}
330/*
331 * Point to the previous entry.
332 */
333static int MemHashCursorPrev(unqlite_kv_cursor *pCursor)
334{
335 mem_hash_cursor *pMem = (mem_hash_cursor *)pCursor;
336 if( pMem->pCur == 0){
337 return UNQLITE_EOF;
338 }
339 pMem->pCur = pMem->pCur->pNext; /* Reverse link: Not a Bug */
340 return UNQLITE_OK;
341}
342/*
343 * Return key length.
344 */
345static int MemHashCursorKeyLength(unqlite_kv_cursor *pCursor,int *pLen)
346{
347 mem_hash_cursor *pMem = (mem_hash_cursor *)pCursor;
348 if( pMem->pCur == 0){
349 return UNQLITE_EOF;
350 }
351 *pLen = (int)pMem->pCur->nKeyLen;
352 return UNQLITE_OK;
353}
354/*
355 * Return data length.
356 */
357static int MemHashCursorDataLength(unqlite_kv_cursor *pCursor,unqlite_int64 *pLen)
358{
359 mem_hash_cursor *pMem = (mem_hash_cursor *)pCursor;
360 if( pMem->pCur == 0 ){
361 return UNQLITE_EOF;
362 }
363 *pLen = pMem->pCur->nDataLen;
364 return UNQLITE_OK;
365}
366/*
367 * Consume the key.
368 */
369static int MemHashCursorKey(unqlite_kv_cursor *pCursor,int (*xConsumer)(const void *,unsigned int,void *),void *pUserData)
370{
371 mem_hash_cursor *pMem = (mem_hash_cursor *)pCursor;
372 int rc;
373 if( pMem->pCur == 0){
374 return UNQLITE_EOF;
375 }
376 /* Invoke the callback */
377 rc = xConsumer(pMem->pCur->pKey,pMem->pCur->nKeyLen,pUserData);
378 /* Callback result */
379 return rc;
380}
381/*
382 * Consume the data.
383 */
384static int MemHashCursorData(unqlite_kv_cursor *pCursor,int (*xConsumer)(const void *,unsigned int,void *),void *pUserData)
385{
386 mem_hash_cursor *pMem = (mem_hash_cursor *)pCursor;
387 int rc;
388 if( pMem->pCur == 0){
389 return UNQLITE_EOF;
390 }
391 /* Invoke the callback */
392 rc = xConsumer(pMem->pCur->pData,pMem->pCur->nDataLen,pUserData);
393 /* Callback result */
394 return rc;
395}
396/*
397 * Reset the cursor.
398 */
399static void MemHashCursorReset(unqlite_kv_cursor *pCursor)
400{
401 mem_hash_cursor *pMem = (mem_hash_cursor *)pCursor;
402 pMem->pCur = ((mem_hash_kv_engine *)pCursor->pStore)->pFirst;
403}
404/*
405 * Remove a particular record.
406 */
407static int MemHashCursorDelete(unqlite_kv_cursor *pCursor)
408{
409 mem_hash_cursor *pMem = (mem_hash_cursor *)pCursor;
410 mem_hash_record *pNext;
411 if( pMem->pCur == 0 ){
412 /* Cursor does not point to anything */
413 return UNQLITE_NOTFOUND;
414 }
415 pNext = pMem->pCur->pPrev;
416 /* Perform the deletion */
417 MemHashUnlinkRecord(pMem->pCur->pEngine,pMem->pCur);
418 /* Point to the next entry */
419 pMem->pCur = pNext;
420 return UNQLITE_OK;
421}
422/*
423 * Find a particular record.
424 */
425static int MemHashCursorSeek(unqlite_kv_cursor *pCursor,const void *pKey,int nByte,int iPos)
426{
427 mem_hash_kv_engine *pEngine = (mem_hash_kv_engine *)pCursor->pStore;
428 mem_hash_cursor *pMem = (mem_hash_cursor *)pCursor;
429 /* Perform the lookup */
430 pMem->pCur = MemHashGetEntry(pEngine,pKey,nByte);
431 if( pMem->pCur == 0 ){
432 if( iPos != UNQLITE_CURSOR_MATCH_EXACT ){
433 /* noop; */
434 }
435 /* No such record */
436 return UNQLITE_NOTFOUND;
437 }
438 return UNQLITE_OK;
439}
440/*
441 * Builtin hash function.
442 */
443static sxu32 MemHashFunc(const void *pSrc,sxu32 nLen)
444{
445 register unsigned char *zIn = (unsigned char *)pSrc;
446 unsigned char *zEnd;
447 sxu32 nH = 5381;
448 zEnd = &zIn[nLen];
449 for(;;){
450 if( zIn >= zEnd ){ break; } nH = nH * 33 + zIn[0] ; zIn++;
451 if( zIn >= zEnd ){ break; } nH = nH * 33 + zIn[0] ; zIn++;
452 if( zIn >= zEnd ){ break; } nH = nH * 33 + zIn[0] ; zIn++;
453 if( zIn >= zEnd ){ break; } nH = nH * 33 + zIn[0] ; zIn++;
454 }
455 return nH;
456}
457/* Default bucket size */
458#define MEM_HASH_BUCKET_SIZE 64
459/* Default fill factor */
460#define MEM_HASH_FILL_FACTOR 4 /* or 3 */
461/*
462 * Initialize the in-memory storage engine.
463 */
464static int MemHashInit(unqlite_kv_engine *pKvEngine,int iPageSize)
465{
466 mem_hash_kv_engine *pEngine = (mem_hash_kv_engine *)pKvEngine;
467 /* Note that this instance is already zeroed */
468 /* Memory backend */
469 SyMemBackendInitFromParent(&pEngine->sAlloc,unqliteExportMemBackend());
470#if defined(UNQLITE_ENABLE_THREADS)
471 /* Already protected by the upper layers */
472 SyMemBackendDisbaleMutexing(&pEngine->sAlloc);
473#endif
474 /* Default hash & comparison function */
475 pEngine->xHash = MemHashFunc;
476 pEngine->xCmp = SyMemcmp;
477 /* Allocate a new bucket */
478 pEngine->apBucket = (mem_hash_record **)SyMemBackendAlloc(&pEngine->sAlloc,MEM_HASH_BUCKET_SIZE * sizeof(mem_hash_record *));
479 if( pEngine->apBucket == 0 ){
480 SXUNUSED(iPageSize); /* cc warning */
481 return UNQLITE_NOMEM;
482 }
483 /* Zero the bucket */
484 SyZero(pEngine->apBucket,MEM_HASH_BUCKET_SIZE * sizeof(mem_hash_record *));
485 pEngine->nRecord = 0;
486 pEngine->nBucket = MEM_HASH_BUCKET_SIZE;
487 return UNQLITE_OK;
488}
489/*
490 * Release the in-memory storage engine.
491 */
492static void MemHashRelease(unqlite_kv_engine *pKvEngine)
493{
494 mem_hash_kv_engine *pEngine = (mem_hash_kv_engine *)pKvEngine;
495 /* Release the private memory backend */
496 SyMemBackendRelease(&pEngine->sAlloc);
497}
498/*
499 * Configure the in-memory storage engine.
500 */
501static int MemHashConfigure(unqlite_kv_engine *pKvEngine,int iOp,va_list ap)
502{
503 mem_hash_kv_engine *pEngine = (mem_hash_kv_engine *)pKvEngine;
504 int rc = UNQLITE_OK;
505 switch(iOp){
506 case UNQLITE_KV_CONFIG_HASH_FUNC:{
507 /* Use a default hash function */
508 if( pEngine->nRecord > 0 ){
509 rc = UNQLITE_LOCKED;
510 }else{
511 ProcHash xHash = va_arg(ap,ProcHash);
512 if( xHash ){
513 pEngine->xHash = xHash;
514 }
515 }
516 break;
517 }
518 case UNQLITE_KV_CONFIG_CMP_FUNC: {
519 /* Default comparison function */
520 ProcCmp xCmp = va_arg(ap,ProcCmp);
521 if( xCmp ){
522 pEngine->xCmp = xCmp;
523 }
524 break;
525 }
526 default:
527 /* Unknown configuration option */
528 rc = UNQLITE_UNKNOWN;
529 }
530 return rc;
531}
532/*
533 * Replace method.
534 */
535static int MemHashReplace(
536 unqlite_kv_engine *pKv,
537 const void *pKey,int nKeyLen,
538 const void *pData,unqlite_int64 nDataLen
539 )
540{
541 mem_hash_kv_engine *pEngine = (mem_hash_kv_engine *)pKv;
542 mem_hash_record *pRecord;
543 if( nDataLen > SXU32_HIGH ){
544 /* Database limit */
545 pEngine->pIo->xErr(pEngine->pIo->pHandle,"Record size limit reached");
546 return UNQLITE_LIMIT;
547 }
548 /* Fetch the record first */
549 pRecord = MemHashGetEntry(pEngine,pKey,nKeyLen);
550 if( pRecord == 0 ){
551 /* Allocate a new record */
552 pRecord = MemHashNewRecord(pEngine,
553 pKey,nKeyLen,
554 pData,nDataLen,
555 pEngine->xHash(pKey,nKeyLen)
556 );
557 if( pRecord == 0 ){
558 return UNQLITE_NOMEM;
559 }
560 /* Link the entry */
561 MemHashLinkRecord(pEngine,pRecord);
562 if( (pEngine->nRecord >= pEngine->nBucket * MEM_HASH_FILL_FACTOR) && pEngine->nRecord < 100000 ){
563 /* Rehash the table */
564 MemHashGrowTable(pEngine);
565 }
566 }else{
567 sxu32 nData = (sxu32)nDataLen;
568 void *pNew;
569 /* Replace an existing record */
570 if( nData == pRecord->nDataLen ){
571 /* No need to free the old chunk */
572 pNew = (void *)pRecord->pData;
573 }else{
574 pNew = SyMemBackendAlloc(&pEngine->sAlloc,nData);
575 if( pNew == 0 ){
576 return UNQLITE_NOMEM;
577 }
578 /* Release the old data */
579 SyMemBackendFree(&pEngine->sAlloc,(void *)pRecord->pData);
580 }
581 /* Reflect the change */
582 pRecord->nDataLen = nData;
583 SyMemcpy(pData,pNew,nData);
584 pRecord->pData = pNew;
585 }
586 return UNQLITE_OK;
587}
588/*
589 * Append method.
590 */
591static int MemHashAppend(
592 unqlite_kv_engine *pKv,
593 const void *pKey,int nKeyLen,
594 const void *pData,unqlite_int64 nDataLen
595 )
596{
597 mem_hash_kv_engine *pEngine = (mem_hash_kv_engine *)pKv;
598 mem_hash_record *pRecord;
599 if( nDataLen > SXU32_HIGH ){
600 /* Database limit */
601 pEngine->pIo->xErr(pEngine->pIo->pHandle,"Record size limit reached");
602 return UNQLITE_LIMIT;
603 }
604 /* Fetch the record first */
605 pRecord = MemHashGetEntry(pEngine,pKey,nKeyLen);
606 if( pRecord == 0 ){
607 /* Allocate a new record */
608 pRecord = MemHashNewRecord(pEngine,
609 pKey,nKeyLen,
610 pData,nDataLen,
611 pEngine->xHash(pKey,nKeyLen)
612 );
613 if( pRecord == 0 ){
614 return UNQLITE_NOMEM;
615 }
616 /* Link the entry */
617 MemHashLinkRecord(pEngine,pRecord);
618 if( pEngine->nRecord * MEM_HASH_FILL_FACTOR >= pEngine->nBucket && pEngine->nRecord < 100000 ){
619 /* Rehash the table */
620 MemHashGrowTable(pEngine);
621 }
622 }else{
623 unqlite_int64 nNew = pRecord->nDataLen + nDataLen;
624 void *pOld = (void *)pRecord->pData;
625 sxu32 nData;
626 char *zNew;
627 /* Append data to the existing record */
628 if( nNew > SXU32_HIGH ){
629 /* Overflow */
630 pEngine->pIo->xErr(pEngine->pIo->pHandle,"Append operation will cause data overflow");
631 return UNQLITE_LIMIT;
632 }
633 nData = (sxu32)nNew;
634 /* Allocate bigger chunk */
635 zNew = (char *)SyMemBackendRealloc(&pEngine->sAlloc,pOld,nData);
636 if( zNew == 0 ){
637 return UNQLITE_NOMEM;
638 }
639 /* Reflect the change */
640 SyMemcpy(pData,&zNew[pRecord->nDataLen],(sxu32)nDataLen);
641 pRecord->pData = (const void *)zNew;
642 pRecord->nDataLen = nData;
643 }
644 return UNQLITE_OK;
645}
646/*
647 * Export the in-memory storage engine.
648 */
649UNQLITE_PRIVATE const unqlite_kv_methods * unqliteExportMemKvStorage(void)
650{
651 static const unqlite_kv_methods sMemStore = {
652 "mem", /* zName */
653 sizeof(mem_hash_kv_engine), /* szKv */
654 sizeof(mem_hash_cursor), /* szCursor */
655 1, /* iVersion */
656 MemHashInit, /* xInit */
657 MemHashRelease, /* xRelease */
658 MemHashConfigure, /* xConfig */
659 0, /* xOpen */
660 MemHashReplace, /* xReplace */
661 MemHashAppend, /* xAppend */
662 MemHashInitCursor, /* xCursorInit */
663 MemHashCursorSeek, /* xSeek */
664 MemHashCursorFirst, /* xFirst */
665 MemHashCursorLast, /* xLast */
666 MemHashCursorValid, /* xValid */
667 MemHashCursorNext, /* xNext */
668 MemHashCursorPrev, /* xPrev */
669 MemHashCursorDelete, /* xDelete */
670 MemHashCursorKeyLength, /* xKeyLength */
671 MemHashCursorKey, /* xKey */
672 MemHashCursorDataLength, /* xDataLength */
673 MemHashCursorData, /* xData */
674 MemHashCursorReset, /* xReset */
675 0 /* xRelease */
676 };
677 return &sMemStore;
678}