diff options
Diffstat (limited to 'common/unqlite/bitvec.c')
-rw-r--r-- | common/unqlite/bitvec.c | 222 |
1 files changed, 222 insertions, 0 deletions
diff --git a/common/unqlite/bitvec.c b/common/unqlite/bitvec.c new file mode 100644 index 0000000..5b78430 --- /dev/null +++ b/common/unqlite/bitvec.c | |||
@@ -0,0 +1,222 @@ | |||
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: bitvec.c v1.0 Win7 2013-02-27 15:16 stable <chm@symisc.net> $ */ | ||
14 | #ifndef UNQLITE_AMALGAMATION | ||
15 | #include "unqliteInt.h" | ||
16 | #endif | ||
17 | |||
18 | /** This file implements an object that represents a dynmaic | ||
19 | ** bitmap. | ||
20 | ** | ||
21 | ** A bitmap is used to record which pages of a database file have been | ||
22 | ** journalled during a transaction, or which pages have the "dont-write" | ||
23 | ** property. Usually only a few pages are meet either condition. | ||
24 | ** So the bitmap is usually sparse and has low cardinality. | ||
25 | */ | ||
26 | /* | ||
27 | * Actually, this is not a bitmap but a simple hashtable where page | ||
28 | * number (64-bit unsigned integers) are used as the lookup keys. | ||
29 | */ | ||
30 | typedef struct bitvec_rec bitvec_rec; | ||
31 | struct bitvec_rec | ||
32 | { | ||
33 | pgno iPage; /* Page number */ | ||
34 | bitvec_rec *pNext,*pNextCol; /* Collison link */ | ||
35 | }; | ||
36 | struct Bitvec | ||
37 | { | ||
38 | SyMemBackend *pAlloc; /* Memory allocator */ | ||
39 | sxu32 nRec; /* Total number of records */ | ||
40 | sxu32 nSize; /* Table size */ | ||
41 | bitvec_rec **apRec; /* Record table */ | ||
42 | bitvec_rec *pList; /* List of records */ | ||
43 | }; | ||
44 | /* | ||
45 | * Allocate a new bitvec instance. | ||
46 | */ | ||
47 | UNQLITE_PRIVATE Bitvec * unqliteBitvecCreate(SyMemBackend *pAlloc,pgno iSize) | ||
48 | { | ||
49 | bitvec_rec **apNew; | ||
50 | Bitvec *p; | ||
51 | |||
52 | p = (Bitvec *)SyMemBackendAlloc(pAlloc,sizeof(*p) ); | ||
53 | if( p == 0 ){ | ||
54 | SXUNUSED(iSize); /* cc warning */ | ||
55 | return 0; | ||
56 | } | ||
57 | /* Zero the structure */ | ||
58 | SyZero(p,sizeof(Bitvec)); | ||
59 | /* Allocate a new table */ | ||
60 | p->nSize = 64; /* Must be a power of two */ | ||
61 | apNew = (bitvec_rec **)SyMemBackendAlloc(pAlloc,p->nSize * sizeof(bitvec_rec *)); | ||
62 | if( apNew == 0 ){ | ||
63 | SyMemBackendFree(pAlloc,p); | ||
64 | return 0; | ||
65 | } | ||
66 | /* Zero the new table */ | ||
67 | SyZero((void *)apNew,p->nSize * sizeof(bitvec_rec *)); | ||
68 | /* Fill-in */ | ||
69 | p->apRec = apNew; | ||
70 | p->pAlloc = pAlloc; | ||
71 | return p; | ||
72 | } | ||
73 | /* | ||
74 | * Check if the given page number is already installed in the table. | ||
75 | * Return true if installed. False otherwise. | ||
76 | */ | ||
77 | UNQLITE_PRIVATE int unqliteBitvecTest(Bitvec *p,pgno i) | ||
78 | { | ||
79 | bitvec_rec *pRec; | ||
80 | /* Point to the desired bucket */ | ||
81 | pRec = p->apRec[i & (p->nSize - 1)]; | ||
82 | for(;;){ | ||
83 | if( pRec == 0 ){ break; } | ||
84 | if( pRec->iPage == i ){ | ||
85 | /* Page found */ | ||
86 | return 1; | ||
87 | } | ||
88 | /* Point to the next entry */ | ||
89 | pRec = pRec->pNextCol; | ||
90 | |||
91 | if( pRec == 0 ){ break; } | ||
92 | if( pRec->iPage == i ){ | ||
93 | /* Page found */ | ||
94 | return 1; | ||
95 | } | ||
96 | /* Point to the next entry */ | ||
97 | pRec = pRec->pNextCol; | ||
98 | |||
99 | |||
100 | if( pRec == 0 ){ break; } | ||
101 | if( pRec->iPage == i ){ | ||
102 | /* Page found */ | ||
103 | return 1; | ||
104 | } | ||
105 | /* Point to the next entry */ | ||
106 | pRec = pRec->pNextCol; | ||
107 | |||
108 | |||
109 | if( pRec == 0 ){ break; } | ||
110 | if( pRec->iPage == i ){ | ||
111 | /* Page found */ | ||
112 | return 1; | ||
113 | } | ||
114 | /* Point to the next entry */ | ||
115 | pRec = pRec->pNextCol; | ||
116 | } | ||
117 | /* No such entry */ | ||
118 | return 0; | ||
119 | } | ||
120 | /* | ||
121 | * Install a given page number in our bitmap (Actually, our hashtable). | ||
122 | */ | ||
123 | UNQLITE_PRIVATE int unqliteBitvecSet(Bitvec *p,pgno i) | ||
124 | { | ||
125 | bitvec_rec *pRec; | ||
126 | sxi32 iBuck; | ||
127 | /* Allocate a new instance */ | ||
128 | pRec = (bitvec_rec *)SyMemBackendPoolAlloc(p->pAlloc,sizeof(bitvec_rec)); | ||
129 | if( pRec == 0 ){ | ||
130 | return UNQLITE_NOMEM; | ||
131 | } | ||
132 | /* Zero the structure */ | ||
133 | SyZero(pRec,sizeof(bitvec_rec)); | ||
134 | /* Fill-in */ | ||
135 | pRec->iPage = i; | ||
136 | iBuck = i & (p->nSize - 1); | ||
137 | pRec->pNextCol = p->apRec[iBuck]; | ||
138 | p->apRec[iBuck] = pRec; | ||
139 | pRec->pNext = p->pList; | ||
140 | p->pList = pRec; | ||
141 | p->nRec++; | ||
142 | if( p->nRec >= (p->nSize * 3) && p->nRec < 100000 ){ | ||
143 | /* Grow the hashtable */ | ||
144 | sxu32 nNewSize = p->nSize << 1; | ||
145 | bitvec_rec *pEntry,**apNew; | ||
146 | sxu32 n; | ||
147 | apNew = (bitvec_rec **)SyMemBackendAlloc(p->pAlloc, nNewSize * sizeof(bitvec_rec *)); | ||
148 | if( apNew ){ | ||
149 | sxu32 iBucket; | ||
150 | /* Zero the new table */ | ||
151 | SyZero((void *)apNew, nNewSize * sizeof(bitvec_rec *)); | ||
152 | /* Rehash all entries */ | ||
153 | n = 0; | ||
154 | pEntry = p->pList; | ||
155 | for(;;){ | ||
156 | /* Loop one */ | ||
157 | if( n >= p->nRec ){ | ||
158 | break; | ||
159 | } | ||
160 | pEntry->pNextCol = 0; | ||
161 | /* Install in the new bucket */ | ||
162 | iBucket = pEntry->iPage & (nNewSize - 1); | ||
163 | pEntry->pNextCol = apNew[iBucket]; | ||
164 | apNew[iBucket] = pEntry; | ||
165 | /* Point to the next entry */ | ||
166 | pEntry = pEntry->pNext; | ||
167 | n++; | ||
168 | } | ||
169 | /* Release the old table and reflect the change */ | ||
170 | SyMemBackendFree(p->pAlloc,(void *)p->apRec); | ||
171 | p->apRec = apNew; | ||
172 | p->nSize = nNewSize; | ||
173 | } | ||
174 | } | ||
175 | return UNQLITE_OK; | ||
176 | } | ||
177 | /* | ||
178 | * Destroy a bitvec instance. Reclaim all memory used. | ||
179 | */ | ||
180 | UNQLITE_PRIVATE void unqliteBitvecDestroy(Bitvec *p) | ||
181 | { | ||
182 | bitvec_rec *pNext,*pRec = p->pList; | ||
183 | SyMemBackend *pAlloc = p->pAlloc; | ||
184 | |||
185 | for(;;){ | ||
186 | if( p->nRec < 1 ){ | ||
187 | break; | ||
188 | } | ||
189 | pNext = pRec->pNext; | ||
190 | SyMemBackendPoolFree(pAlloc,(void *)pRec); | ||
191 | pRec = pNext; | ||
192 | p->nRec--; | ||
193 | |||
194 | if( p->nRec < 1 ){ | ||
195 | break; | ||
196 | } | ||
197 | pNext = pRec->pNext; | ||
198 | SyMemBackendPoolFree(pAlloc,(void *)pRec); | ||
199 | pRec = pNext; | ||
200 | p->nRec--; | ||
201 | |||
202 | |||
203 | if( p->nRec < 1 ){ | ||
204 | break; | ||
205 | } | ||
206 | pNext = pRec->pNext; | ||
207 | SyMemBackendPoolFree(pAlloc,(void *)pRec); | ||
208 | pRec = pNext; | ||
209 | p->nRec--; | ||
210 | |||
211 | |||
212 | if( p->nRec < 1 ){ | ||
213 | break; | ||
214 | } | ||
215 | pNext = pRec->pNext; | ||
216 | SyMemBackendPoolFree(pAlloc,(void *)pRec); | ||
217 | pRec = pNext; | ||
218 | p->nRec--; | ||
219 | } | ||
220 | SyMemBackendFree(pAlloc,(void *)p->apRec); | ||
221 | SyMemBackendFree(pAlloc,p); | ||
222 | } | ||