]> pd.if.org Git - btree/blob - btree2t.c
Remove debug code.
[btree] / btree2t.c
1 // btree version 2t
2 //      with reworked bt_deletekey code
3 // 25 FEB 2014
4
5 // author: karl malbrain, malbrain@cal.berkeley.edu
6
7 /*
8 This work, including the source code, documentation
9 and related data, is placed into the public domain.
10
11 The orginal author is Karl Malbrain.
12
13 THIS SOFTWARE IS PROVIDED AS-IS WITHOUT WARRANTY
14 OF ANY KIND, NOT EVEN THE IMPLIED WARRANTY OF
15 MERCHANTABILITY. THE AUTHOR OF THIS SOFTWARE,
16 ASSUMES _NO_ RESPONSIBILITY FOR ANY CONSEQUENCE
17 RESULTING FROM THE USE, MODIFICATION, OR
18 REDISTRIBUTION OF THIS SOFTWARE.
19 */
20
21 // Please see the project home page for documentation
22 // code.google.com/p/high-concurrency-btree
23
24 #define _FILE_OFFSET_BITS 64
25 #define _LARGEFILE64_SOURCE
26
27 #ifdef linux
28 #define _GNU_SOURCE
29 #endif
30
31 #ifdef unix
32 #include <unistd.h>
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <time.h>
36 #include <fcntl.h>
37 #include <sys/mman.h>
38 #include <errno.h>
39 #else
40 #define WIN32_LEAN_AND_MEAN
41 #include <windows.h>
42 #include <stdio.h>
43 #include <stdlib.h>
44 #include <time.h>
45 #include <fcntl.h>
46 #endif
47
48 #include <memory.h>
49 #include <string.h>
50
51 typedef unsigned long long      uid;
52
53 #ifndef unix
54 typedef unsigned long long      off64_t;
55 typedef unsigned short          ushort;
56 typedef unsigned int            uint;
57 #endif
58
59 #define BT_latchtable   128                                     // number of latch manager slots
60
61 #define BT_ro 0x6f72    // ro
62 #define BT_rw 0x7772    // rw
63 #define BT_fl 0x6c66    // fl
64
65 #define BT_maxbits              24                                      // maximum page size in bits
66 #define BT_minbits              9                                       // minimum page size in bits
67 #define BT_minpage              (1 << BT_minbits)       // minimum page size
68 #define BT_maxpage              (1 << BT_maxbits)       // maximum page size
69
70 /*
71 There are five lock types for each node in three independent sets: 
72 1. (set 1) AccessIntent: Sharable. Going to Read the node. Incompatible with NodeDelete. 
73 2. (set 1) NodeDelete: Exclusive. About to release the node. Incompatible with AccessIntent. 
74 3. (set 2) ReadLock: Sharable. Read the node. Incompatible with WriteLock. 
75 4. (set 2) WriteLock: Exclusive. Modify the node. Incompatible with ReadLock and other WriteLocks. 
76 5. (set 3) ParentModification: Exclusive. Change the node's parent keys. Incompatible with another ParentModification. 
77 */
78
79 typedef enum{
80         BtLockAccess,
81         BtLockDelete,
82         BtLockRead,
83         BtLockWrite,
84         BtLockParent
85 }BtLock;
86
87 //      definition for latch implementation
88
89 // exclusive is set for write access
90 // share is count of read accessors
91 // grant write lock when share == 0
92
93 volatile typedef struct {
94         unsigned char mutex[1];
95         unsigned char exclusive:1;
96         unsigned char pending:1;
97         ushort share;
98 } BtSpinLatch;
99
100 //  hash table entries
101
102 typedef struct {
103         BtSpinLatch latch[1];
104         volatile ushort slot;           // Latch table entry at head of chain
105 } BtHashEntry;
106
107 //      latch manager table structure
108
109 typedef struct {
110         BtSpinLatch readwr[1];          // read/write page lock
111         BtSpinLatch access[1];          // Access Intent/Page delete
112         BtSpinLatch parent[1];          // Posting of fence key in parent
113         BtSpinLatch busy[1];            // slot is being moved between chains
114         volatile ushort next;           // next entry in hash table chain
115         volatile ushort prev;           // prev entry in hash table chain
116         volatile ushort pin;            // number of outstanding locks
117         volatile ushort hash;           // hash slot entry is under
118         volatile uid page_no;           // latch set page number
119 } BtLatchSet;
120
121 //      Define the length of the page and key pointers
122
123 #define BtId 6
124
125 //      Page key slot definition.
126
127 //      If BT_maxbits is 15 or less, you can save 2 bytes
128 //      for each key stored by making the first two uints
129 //      into ushorts.  You can also save 4 bytes by removing
130 //      the tod field from the key.
131
132 //      Keys are marked dead, but remain on the page until
133 //      cleanup is called. The fence key (highest key) for
134 //      the page is always present, even if dead.
135
136 typedef struct {
137         uint off:BT_maxbits;            // page offset for key start
138         uint dead:1;                            // set for deleted key
139         uint tod;                                       // time-stamp for key
140         unsigned char id[BtId];         // id associated with key
141 } BtSlot;
142
143 //      The key structure occupies space at the upper end of
144 //      each page.  It's a length byte followed by the value
145 //      bytes.
146
147 typedef struct {
148         unsigned char len;
149         unsigned char key[0];
150 } *BtKey;
151
152 //      The first part of an index page.
153 //      It is immediately followed
154 //      by the BtSlot array of keys.
155
156 typedef struct BtPage_ {
157         uint cnt;                                       // count of keys in page
158         uint act;                                       // count of active keys
159         uint min;                                       // next key offset
160         unsigned char bits:7;           // page size in bits
161         unsigned char free:1;           // page is on free list
162         unsigned char lvl:6;            // level of page
163         unsigned char kill:1;           // page is being deleted
164         unsigned char dirty:1;          // page is dirty
165         unsigned char right[BtId];      // page number to right
166 } *BtPage;
167
168 //      The memory mapping hash table entry
169
170 typedef struct {
171         BtPage page;            // mapped page pointer
172         uid  page_no;           // mapped page number
173         void *lruprev;          // least recently used previous cache block
174         void *lrunext;          // lru next cache block
175         void *hashprev;         // previous cache block for the same hash idx
176         void *hashnext;         // next cache block for the same hash idx
177 #ifndef unix
178         HANDLE hmap;
179 #endif
180 }BtHash;
181
182 typedef struct {
183         struct BtPage_ alloc[2];        // next & free page_nos in right ptr
184         BtSpinLatch lock[1];            // allocation area lite latch
185         ushort latchdeployed;           // highest number of latch entries deployed
186         ushort nlatchpage;                      // number of latch pages at BT_latch
187         ushort latchtotal;                      // number of page latch entries
188         ushort latchhash;                       // number of latch hash table slots
189         ushort latchvictim;                     // next latch entry to examine
190         BtHashEntry table[0];           // the hash table
191 } BtLatchMgr;
192
193 //      The object structure for Btree access
194
195 typedef struct _BtDb {
196         uint page_size;         // each page size       
197         uint page_bits;         // each page size in bits       
198         uint seg_bits;          // segment size in pages in bits
199         uid page_no;            // current page number  
200         uid cursor_page;        // current cursor page number   
201         int  err;
202         uint mode;                      // read-write mode
203         uint mapped_io;         // use memory mapping
204         BtPage temp;            // temporary frame buffer (memory mapped/file IO)
205         BtPage alloc;           // frame buffer for alloc page ( page 0 )
206         BtPage cursor;          // cached frame for start/next (never mapped)
207         BtPage frame;           // spare frame for the page split (never mapped)
208         BtPage zero;            // zeroes frame buffer (never mapped)
209         BtPage page;            // current page
210         BtLatchSet *latch;              // current page latch
211         BtLatchMgr *latchmgr;   // mapped latch page from allocation page
212         BtLatchSet *latchsets;  // mapped latch set from latch pages
213 #ifdef unix
214         int idx;
215 #else
216         HANDLE idx;
217         HANDLE halloc;          // allocation and latch table handle
218 #endif
219         unsigned char *mem;     // frame, cursor, page memory buffer
220         int nodecnt;            // highest page cache segment in use
221         int nodemax;            // highest page cache segment allocated
222         int hashmask;           // number of pages in segments - 1
223         int hashsize;           // size of hash table
224         int found;                      // last deletekey found key
225         BtHash *lrufirst;       // lru list head
226         BtHash *lrulast;        // lru list tail
227         ushort *cache;          // hash table for cached segments
228         BtHash *nodes;          // segment cache
229 } BtDb;
230
231 typedef enum {
232 BTERR_ok = 0,
233 BTERR_notfound,
234 BTERR_struct,
235 BTERR_ovflw,
236 BTERR_lock,
237 BTERR_hash,
238 BTERR_kill,
239 BTERR_map,
240 BTERR_wrt,
241 BTERR_eof
242 } BTERR;
243
244 // B-Tree functions
245 extern void bt_close (BtDb *bt);
246 extern BtDb *bt_open (char *name, uint mode, uint bits, uint cacheblk, uint pgblk, uint hashsize);
247 extern BTERR  bt_insertkey (BtDb *bt, unsigned char *key, uint len, uint lvl, uid id, uint tod);
248 extern BTERR  bt_deletekey (BtDb *bt, unsigned char *key, uint len, uint lvl);
249 extern uid bt_findkey    (BtDb *bt, unsigned char *key, uint len);
250 extern uint bt_startkey  (BtDb *bt, unsigned char *key, uint len);
251 extern uint bt_nextkey   (BtDb *bt, uint slot);
252
253 //      internal functions
254 BTERR bt_update (BtDb *bt, BtPage page, uid page_no);
255 BTERR bt_mappage (BtDb *bt, BtPage *page, uid page_no);
256 //  Helper functions to return slot values
257
258 extern BtKey bt_key (BtDb *bt, uint slot);
259 extern uid bt_uid (BtDb *bt, uint slot);
260 extern uint bt_tod (BtDb *bt, uint slot);
261
262 //  BTree page number constants
263 #define ALLOC_page              0
264 #define ROOT_page               1
265 #define LEAF_page               2
266 #define LATCH_page              3
267
268 //      Number of levels to create in a new BTree
269
270 #define MIN_lvl                 2
271
272 //  The page is allocated from low and hi ends.
273 //  The key offsets and row-id's are allocated
274 //  from the bottom, while the text of the key
275 //  is allocated from the top.  When the two
276 //  areas meet, the page is split into two.
277
278 //  A key consists of a length byte, two bytes of
279 //  index number (0 - 65534), and up to 253 bytes
280 //  of key value.  Duplicate keys are discarded.
281 //  Associated with each key is a 48 bit row-id.
282
283 //  The b-tree root is always located at page 1.
284 //      The first leaf page of level zero is always
285 //      located on page 2.
286
287 //      The b-tree pages are linked with right
288 //      pointers to facilitate enumerators,
289 //      and provide for concurrency.
290
291 //      When to root page fills, it is split in two and
292 //      the tree height is raised by a new root at page
293 //      one with two keys.
294
295 //      Deleted keys are marked with a dead bit until
296 //      page cleanup The fence key for a node is always
297 //      present, even after deletion and cleanup.
298
299 //  Deleted leaf pages are reclaimed  on a free list.
300 //      The upper levels of the btree are fixed on creation.
301
302 //  Groups of pages from the btree are optionally
303 //  cached with memory mapping. A hash table is used to keep
304 //  track of the cached pages.  This behaviour is controlled
305 //  by the number of cache blocks parameter and pages per block
306 //      given to bt_open.
307
308 //  To achieve maximum concurrency one page is locked at a time
309 //  as the tree is traversed to find leaf key in question. The right
310 //  page numbers are used in cases where the page is being split,
311 //      or consolidated.
312
313 //  Page 0 (ALLOC page) is dedicated to lock for new page extensions,
314 //      and chains empty leaf pages together for reuse.
315
316 //      Parent locks are obtained to prevent resplitting or deleting a node
317 //      before its fence is posted into its upper level.
318
319 //      A special open mode of BT_fl is provided to safely access files on
320 //      WIN32 networks. WIN32 network operations should not use memory mapping.
321 //      This WIN32 mode sets FILE_FLAG_NOBUFFERING and FILE_FLAG_WRITETHROUGH
322 //      to prevent local caching of network file contents.
323
324 //      Access macros to address slot and key values from the page.
325 //      Page slots use 1 based indexing.
326
327 #define slotptr(page, slot) (((BtSlot *)(page+1)) + (slot-1))
328 #define keyptr(page, slot) ((BtKey)((unsigned char*)(page) + slotptr(page, slot)->off))
329
330 void bt_putid(unsigned char *dest, uid id)
331 {
332 int i = BtId;
333
334         while( i-- )
335                 dest[i] = (unsigned char)id, id >>= 8;
336 }
337
338 uid bt_getid(unsigned char *src)
339 {
340 uid id = 0;
341 int i;
342
343         for( i = 0; i < BtId; i++ )
344                 id <<= 8, id |= *src++; 
345
346         return id;
347 }
348
349 BTERR bt_abort (BtDb *bt, BtPage page, uid page_no, BTERR err)
350 {
351 BtKey ptr;
352
353         fprintf(stderr, "\n Btree2 abort, error %d on page %.8x\n", err, page_no);
354         fprintf(stderr, "level=%d kill=%d free=%d cnt=%x act=%x\n", page->lvl, page->kill, page->free, page->cnt, page->act);
355         ptr = keyptr(page, page->cnt);
356         fprintf(stderr, "fence='%.*s'\n", ptr->len, ptr->key);
357         fprintf(stderr, "right=%.8x\n", bt_getid(page->right));
358         return bt->err = err;
359 }
360
361 //      Latch Manager
362
363 //      wait until write lock mode is clear
364 //      and add 1 to the share count
365
366 void bt_spinreadlock(BtSpinLatch *latch)
367 {
368 ushort prev;
369
370   do {
371         //      obtain latch mutex
372 #ifdef unix
373         if( __sync_lock_test_and_set(latch->mutex, 1) )
374                 continue;
375 #else
376         if( _InterlockedExchange8(latch->mutex, 1) )
377                 continue;
378 #endif
379         //  see if exclusive request is granted or pending
380
381         if( prev = !(latch->exclusive | latch->pending) )
382                 latch->share++;
383
384 #ifdef unix
385         *latch->mutex = 0;
386 #else
387         _InterlockedExchange8(latch->mutex, 0);
388 #endif
389
390         if( prev )
391                 return;
392
393 #ifdef  unix
394   } while( sched_yield(), 1 );
395 #else
396   } while( SwitchToThread(), 1 );
397 #endif
398 }
399
400 //      wait for other read and write latches to relinquish
401
402 void bt_spinwritelock(BtSpinLatch *latch)
403 {
404 uint prev;
405
406   do {
407 #ifdef  unix
408         if( __sync_lock_test_and_set(latch->mutex, 1) )
409                 continue;
410 #else
411         if( _InterlockedExchange8(latch->mutex, 1) )
412                 continue;
413 #endif
414         if( prev = !(latch->share | latch->exclusive) )
415                 latch->exclusive = 1, latch->pending = 0;
416         else
417                 latch->pending = 1;
418 #ifdef unix
419         *latch->mutex = 0;
420 #else
421         _InterlockedExchange8(latch->mutex, 0);
422 #endif
423         if( prev )
424                 return;
425 #ifdef  unix
426   } while( sched_yield(), 1 );
427 #else
428   } while( SwitchToThread(), 1 );
429 #endif
430 }
431
432 //      try to obtain write lock
433
434 //      return 1 if obtained,
435 //              0 otherwise
436
437 int bt_spinwritetry(BtSpinLatch *latch)
438 {
439 uint prev;
440
441 #ifdef unix
442         if( __sync_lock_test_and_set(latch->mutex, 1) )
443                 return 0;
444 #else
445         if( _InterlockedExchange8(latch->mutex, 1) )
446                 return 0;
447 #endif
448         //      take write access if all bits are clear
449
450         if( prev = !(latch->exclusive | latch->share) )
451                 latch->exclusive = 1;
452
453 #ifdef unix
454         *latch->mutex = 0;
455 #else
456         _InterlockedExchange8(latch->mutex, 0);
457 #endif
458         return prev;
459 }
460
461 //      clear write mode
462
463 void bt_spinreleasewrite(BtSpinLatch *latch)
464 {
465 #ifdef unix
466         while( __sync_lock_test_and_set(latch->mutex, 1) )
467                 sched_yield();
468 #else
469         while( _InterlockedExchange8(latch->mutex, 1) )
470                 SwitchToThread();
471 #endif
472         latch->exclusive = 0;
473 #ifdef unix
474         *latch->mutex = 0;
475 #else
476         _InterlockedExchange8(latch->mutex, 0);
477 #endif
478 }
479
480 //      decrement reader count
481
482 void bt_spinreleaseread(BtSpinLatch *latch)
483 {
484 #ifdef unix
485         while( __sync_lock_test_and_set(latch->mutex, 1) )
486                 sched_yield();
487 #else
488         while( _InterlockedExchange8(latch->mutex, 1) )
489                 SwitchToThread();
490 #endif
491         latch->share--;
492 #ifdef unix
493         *latch->mutex = 0;
494 #else
495         _InterlockedExchange8(latch->mutex, 0);
496 #endif
497 }
498
499 //      link latch table entry into latch hash table
500
501 void bt_latchlink (BtDb *bt, ushort hashidx, ushort victim, uid page_no)
502 {
503 BtLatchSet *latch = bt->latchsets + victim;
504
505         if( latch->next = bt->latchmgr->table[hashidx].slot )
506                 bt->latchsets[latch->next].prev = victim;
507
508         bt->latchmgr->table[hashidx].slot = victim;
509         latch->page_no = page_no;
510         latch->hash = hashidx;
511         latch->prev = 0;
512 }
513
514 //      release latch pin
515
516 void bt_unpinlatch (BtLatchSet *latch)
517 {
518 #ifdef unix
519         __sync_fetch_and_add(&latch->pin, -1);
520 #else
521         _InterlockedDecrement16 (&latch->pin);
522 #endif
523 }
524
525 //      find existing latchset or inspire new one
526 //      return with latchset pinned
527
528 BtLatchSet *bt_pinlatch (BtDb *bt, uid page_no)
529 {
530 ushort hashidx = page_no % bt->latchmgr->latchhash;
531 ushort slot, avail = 0, victim, idx;
532 BtLatchSet *latch;
533
534         //  obtain read lock on hash table entry
535
536         bt_spinreadlock(bt->latchmgr->table[hashidx].latch);
537
538         if( slot = bt->latchmgr->table[hashidx].slot ) do
539         {
540                 latch = bt->latchsets + slot;
541                 if( page_no == latch->page_no )
542                         break;
543         } while( slot = latch->next );
544
545         if( slot ) {
546 #ifdef unix
547                 __sync_fetch_and_add(&latch->pin, 1);
548 #else
549                 _InterlockedIncrement16 (&latch->pin);
550 #endif
551         }
552
553     bt_spinreleaseread (bt->latchmgr->table[hashidx].latch);
554
555         if( slot )
556                 return latch;
557
558   //  try again, this time with write lock
559
560   bt_spinwritelock(bt->latchmgr->table[hashidx].latch);
561
562   if( slot = bt->latchmgr->table[hashidx].slot ) do
563   {
564         latch = bt->latchsets + slot;
565         if( page_no == latch->page_no )
566                 break;
567         if( !latch->pin && !avail )
568                 avail = slot;
569   } while( slot = latch->next );
570
571   //  found our entry, or take over an unpinned one
572
573   if( slot || (slot = avail) ) {
574         latch = bt->latchsets + slot;
575 #ifdef unix
576         __sync_fetch_and_add(&latch->pin, 1);
577 #else
578         _InterlockedIncrement16 (&latch->pin);
579 #endif
580         latch->page_no = page_no;
581         bt_spinreleasewrite(bt->latchmgr->table[hashidx].latch);
582         return latch;
583   }
584
585         //  see if there are any unused entries
586 #ifdef unix
587         victim = __sync_fetch_and_add (&bt->latchmgr->latchdeployed, 1) + 1;
588 #else
589         victim = _InterlockedIncrement16 (&bt->latchmgr->latchdeployed);
590 #endif
591
592         if( victim < bt->latchmgr->latchtotal ) {
593                 latch = bt->latchsets + victim;
594 #ifdef unix
595                 __sync_fetch_and_add(&latch->pin, 1);
596 #else
597                 _InterlockedIncrement16 (&latch->pin);
598 #endif
599                 bt_latchlink (bt, hashidx, victim, page_no);
600                 bt_spinreleasewrite (bt->latchmgr->table[hashidx].latch);
601                 return latch;
602         }
603
604 #ifdef unix
605         victim = __sync_fetch_and_add (&bt->latchmgr->latchdeployed, -1);
606 #else
607         victim = _InterlockedDecrement16 (&bt->latchmgr->latchdeployed);
608 #endif
609   //  find and reuse previous lock entry
610
611   while( 1 ) {
612 #ifdef unix
613         victim = __sync_fetch_and_add(&bt->latchmgr->latchvictim, 1);
614 #else
615         victim = _InterlockedIncrement16 (&bt->latchmgr->latchvictim) - 1;
616 #endif
617         //      we don't use slot zero
618
619         if( victim %= bt->latchmgr->latchtotal )
620                 latch = bt->latchsets + victim;
621         else
622                 continue;
623
624         //      take control of our slot
625         //      from other threads
626
627         if( latch->pin || !bt_spinwritetry (latch->busy) )
628                 continue;
629
630         idx = latch->hash;
631
632         // try to get write lock on hash chain
633         //      skip entry if not obtained
634         //      or has outstanding locks
635
636         if( !bt_spinwritetry (bt->latchmgr->table[idx].latch) ) {
637                 bt_spinreleasewrite (latch->busy);
638                 continue;
639         }
640
641         if( latch->pin ) {
642                 bt_spinreleasewrite (latch->busy);
643                 bt_spinreleasewrite (bt->latchmgr->table[idx].latch);
644                 continue;
645         }
646
647         //  unlink our available victim from its hash chain
648
649         if( latch->prev )
650                 bt->latchsets[latch->prev].next = latch->next;
651         else
652                 bt->latchmgr->table[idx].slot = latch->next;
653
654         if( latch->next )
655                 bt->latchsets[latch->next].prev = latch->prev;
656
657         bt_spinreleasewrite (bt->latchmgr->table[idx].latch);
658 #ifdef unix
659         __sync_fetch_and_add(&latch->pin, 1);
660 #else
661         _InterlockedIncrement16 (&latch->pin);
662 #endif
663         bt_latchlink (bt, hashidx, victim, page_no);
664         bt_spinreleasewrite (bt->latchmgr->table[hashidx].latch);
665         bt_spinreleasewrite (latch->busy);
666         return latch;
667   }
668 }
669
670 //      close and release memory
671
672 void bt_close (BtDb *bt)
673 {
674 BtHash *hash;
675 #ifdef unix
676         munmap (bt->latchsets, bt->latchmgr->nlatchpage * bt->page_size);
677         munmap (bt->latchmgr, bt->page_size);
678 #else
679         FlushViewOfFile(bt->latchmgr, 0);
680         UnmapViewOfFile(bt->latchmgr);
681         CloseHandle(bt->halloc);
682 #endif
683 #ifdef unix
684         // release mapped pages
685
686         if( hash = bt->lrufirst )
687                 do munmap (hash->page, (bt->hashmask+1) << bt->page_bits);
688                 while(hash = hash->lrunext);
689
690         if( bt->mem )
691                 free (bt->mem);
692         close (bt->idx);
693         free (bt->cache);
694         free (bt);
695 #else
696         if( hash = bt->lrufirst )
697           do
698           {
699                 FlushViewOfFile(hash->page, 0);
700                 UnmapViewOfFile(hash->page);
701                 CloseHandle(hash->hmap);
702           } while(hash = hash->lrunext);
703
704         if( bt->mem)
705                 VirtualFree (bt->mem, 0, MEM_RELEASE);
706         FlushFileBuffers(bt->idx);
707         CloseHandle(bt->idx);
708         GlobalFree (bt->cache);
709         GlobalFree (bt);
710 #endif
711 }
712 //  open/create new btree
713
714 //      call with file_name, BT_openmode, bits in page size (e.g. 16),
715 //              size of mapped page pool (e.g. 8192)
716
717 BtDb *bt_open (char *name, uint mode, uint bits, uint nodemax, uint segsize, uint hashsize)
718 {
719 uint lvl, attr, cacheblk, last, slot, idx;
720 uint nlatchpage, latchhash;
721 BtLatchMgr *latchmgr;
722 off64_t size;
723 uint amt[1];
724 BtKey key;
725 BtDb* bt;
726 int flag;
727
728 #ifndef unix
729 SYSTEM_INFO sysinfo[1];
730 OVERLAPPED ovl[1];
731 #else
732 struct flock lock[1];
733 #endif
734
735         // determine sanity of page size and buffer pool
736
737         if( bits > BT_maxbits )
738                 bits = BT_maxbits;
739         else if( bits < BT_minbits )
740                 bits = BT_minbits;
741
742 #ifdef unix
743         bt = calloc (1, sizeof(BtDb));
744
745         bt->idx = open ((char*)name, O_RDWR | O_CREAT, 0666);
746
747         if( bt->idx == -1 )
748                 return free(bt), NULL;
749         
750         cacheblk = 4096;        // minimum mmap segment size for unix
751
752 #else
753         bt = GlobalAlloc (GMEM_FIXED|GMEM_ZEROINIT, sizeof(BtDb));
754         attr = FILE_ATTRIBUTE_NORMAL;
755         bt->idx = CreateFile(name, GENERIC_READ| GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_ALWAYS, attr, NULL);
756
757         if( bt->idx == INVALID_HANDLE_VALUE )
758                 return GlobalFree(bt), NULL;
759
760         // normalize cacheblk to multiple of sysinfo->dwAllocationGranularity
761         GetSystemInfo(sysinfo);
762         cacheblk = sysinfo->dwAllocationGranularity;
763 #endif
764
765 #ifdef unix
766         memset (lock, 0, sizeof(lock));
767
768         lock->l_type = F_WRLCK;
769         lock->l_len = sizeof(struct BtPage_);
770         lock->l_whence = 0;
771
772         if( fcntl (bt->idx, F_SETLKW, lock) < 0 )
773                 return bt_close (bt), NULL;
774 #else
775         memset (ovl, 0, sizeof(ovl));
776         len = sizeof(struct BtPage_);
777
778         //      use large offsets to
779         //      simulate advisory locking
780
781         ovl->OffsetHigh |= 0x80000000;
782
783         if( mode == BtLockDelete || mode == BtLockWrite || mode == BtLockParent )
784                 flags |= LOCKFILE_EXCLUSIVE_LOCK;
785
786         if( LockFileEx (bt->idx, flags, 0, len, 0L, ovl) )
787                 return bt_close (bt), NULL;
788 #endif 
789 #ifdef unix
790         latchmgr = malloc (BT_maxpage);
791         *amt = 0;
792
793         // read minimum page size to get root info
794
795         if( size = lseek (bt->idx, 0L, 2) ) {
796                 if( pread(bt->idx, latchmgr, BT_minpage, 0) == BT_minpage )
797                         bits = latchmgr->alloc->bits;
798                 else
799                         return free(bt), free(latchmgr), NULL;
800         } else if( mode == BT_ro )
801                 return free(latchmgr), bt_close (bt), NULL;
802 #else
803         latchmgr = VirtualAlloc(NULL, BT_maxpage, MEM_COMMIT, PAGE_READWRITE);
804         size = GetFileSize(bt->idx, amt);
805
806         if( size || *amt ) {
807                 if( !ReadFile(bt->idx, (char *)latchmgr, BT_minpage, amt, NULL) )
808                         return bt_close (bt), NULL;
809                 bits = latchmgr->alloc->bits;
810         } else if( mode == BT_ro )
811                 return bt_close (bt), NULL;
812 #endif
813
814         bt->page_size = 1 << bits;
815         bt->page_bits = bits;
816
817         bt->mode = mode;
818
819         if( cacheblk < bt->page_size )
820                 cacheblk = bt->page_size;
821
822         //  mask for partial memmaps
823
824         bt->hashmask = (cacheblk >> bits) - 1;
825
826         //      see if requested size of pages per memmap is greater
827
828         if( (1 << segsize) > bt->hashmask )
829                 bt->hashmask = (1 << segsize) - 1;
830
831         bt->seg_bits = 0;
832
833         while( (1 << bt->seg_bits) <= bt->hashmask )
834                 bt->seg_bits++;
835
836         bt->hashsize = hashsize;
837
838         if( bt->nodemax = nodemax ) {
839 #ifdef unix
840           bt->nodes = calloc (nodemax, sizeof(BtHash));
841           bt->cache = calloc (hashsize, sizeof(ushort));
842 #else
843           bt->nodes = GlobalAlloc (GMEM_FIXED|GMEM_ZEROINIT, nodemax * sizeof(BtHash));
844           bt->cache = GlobalAlloc (GMEM_FIXED|GMEM_ZEROINIT, hashsize * sizeof(ushort));
845 #endif
846           bt->mapped_io = 1;
847         }
848
849         if( size || *amt ) {
850                 goto btlatch;
851         }
852
853         // initialize an empty b-tree with latch page, root page, page of leaves
854         // and page(s) of latches
855
856         memset (latchmgr, 0, 1 << bits);
857         nlatchpage = BT_latchtable / (bt->page_size / sizeof(BtLatchSet)) + 1; 
858         bt_putid(latchmgr->alloc->right, MIN_lvl+1+nlatchpage);
859         latchmgr->alloc->bits = bt->page_bits;
860
861         latchmgr->nlatchpage = nlatchpage;
862         latchmgr->latchtotal = nlatchpage * (bt->page_size / sizeof(BtLatchSet));
863
864         //  initialize latch manager
865
866         latchhash = (bt->page_size - sizeof(BtLatchMgr)) / sizeof(BtHashEntry);
867
868         //      size of hash table = total number of latchsets
869
870         if( latchhash > latchmgr->latchtotal )
871                 latchhash = latchmgr->latchtotal;
872
873         latchmgr->latchhash = latchhash;
874
875 #ifdef unix
876         if( write (bt->idx, latchmgr, bt->page_size) < bt->page_size )
877                 return bt_close (bt), NULL;
878 #else
879         if( !WriteFile (bt->idx, (char *)latchmgr, bt->page_size, amt, NULL) )
880                 return bt_close (bt), NULL;
881
882         if( *amt < bt->page_size )
883                 return bt_close (bt), NULL;
884 #endif
885
886         memset (latchmgr, 0, 1 << bits);
887         latchmgr->alloc->bits = bt->page_bits;
888
889         for( lvl=MIN_lvl; lvl--; ) {
890                 slotptr(latchmgr->alloc, 1)->off = bt->page_size - 3;
891                 bt_putid(slotptr(latchmgr->alloc, 1)->id, lvl ? MIN_lvl - lvl + 1 : 0);         // next(lower) page number
892                 key = keyptr(latchmgr->alloc, 1);
893                 key->len = 2;           // create stopper key
894                 key->key[0] = 0xff;
895                 key->key[1] = 0xff;
896                 latchmgr->alloc->min = bt->page_size - 3;
897                 latchmgr->alloc->lvl = lvl;
898                 latchmgr->alloc->cnt = 1;
899                 latchmgr->alloc->act = 1;
900 #ifdef unix
901                 if( write (bt->idx, latchmgr, bt->page_size) < bt->page_size )
902                         return bt_close (bt), NULL;
903 #else
904                 if( !WriteFile (bt->idx, (char *)latchmgr, bt->page_size, amt, NULL) )
905                         return bt_close (bt), NULL;
906
907                 if( *amt < bt->page_size )
908                         return bt_close (bt), NULL;
909 #endif
910         }
911
912         // clear out latch manager locks
913         //      and rest of pages to round out segment
914
915         memset(latchmgr, 0, bt->page_size);
916         last = MIN_lvl + 1;
917
918         while( last <= ((MIN_lvl + 1 + nlatchpage) | bt->hashmask) ) {
919 #ifdef unix
920                 pwrite(bt->idx, latchmgr, bt->page_size, last << bt->page_bits);
921 #else
922                 SetFilePointer (bt->idx, last << bt->page_bits, NULL, FILE_BEGIN);
923                 if( !WriteFile (bt->idx, (char *)latchmgr, bt->page_size, amt, NULL) )
924                         return bt_close (bt), NULL;
925                 if( *amt < bt->page_size )
926                         return bt_close (bt), NULL;
927 #endif
928                 last++;
929         }
930
931 btlatch:
932 #ifdef unix
933         lock->l_type = F_UNLCK;
934         if( fcntl (bt->idx, F_SETLK, lock) < 0 )
935                 return bt_close (bt), NULL;
936 #else
937         if( !UnlockFileEx (bt->idx, 0, sizeof(struct BtPage_), 0, ovl) )
938                 return bt_close (bt), NULL;
939 #endif
940 #ifdef unix
941         flag = PROT_READ | PROT_WRITE;
942         bt->latchmgr = mmap (0, bt->page_size, flag, MAP_SHARED, bt->idx, ALLOC_page * bt->page_size);
943         if( bt->latchmgr == MAP_FAILED )
944                 return bt_close (bt), NULL;
945         bt->latchsets = (BtLatchSet *)mmap (0, bt->latchmgr->nlatchpage * bt->page_size, flag, MAP_SHARED, bt->idx, LATCH_page * bt->page_size);
946         if( bt->latchsets == MAP_FAILED )
947                 return bt_close (bt), NULL;
948 #else
949         flag = PAGE_READWRITE;
950         bt->halloc = CreateFileMapping(bt->idx, NULL, flag, 0, (BT_latchtable / (bt->page_size / sizeof(BtLatchSet)) + 1 + LATCH_page) * bt->page_size, NULL);
951         if( !bt->halloc )
952                 return bt_close (bt), NULL;
953
954         flag = FILE_MAP_WRITE;
955         bt->latchmgr = MapViewOfFile(bt->halloc, flag, 0, 0, (BT_latchtable / (bt->page_size / sizeof(BtLatchSet)) + 1 + LATCH_page) * bt->page_size);
956         if( !bt->latchmgr )
957                 return GetLastError(), bt_close (bt), NULL;
958
959         bt->latchsets = (void *)((char *)bt->latchmgr + LATCH_page * bt->page_size);
960 #endif
961
962 #ifdef unix
963         free (latchmgr);
964 #else
965         VirtualFree (latchmgr, 0, MEM_RELEASE);
966 #endif
967
968 #ifdef unix
969         bt->mem = malloc (6 * bt->page_size);
970 #else
971         bt->mem = VirtualAlloc(NULL, 6 * bt->page_size, MEM_COMMIT, PAGE_READWRITE);
972 #endif
973         bt->frame = (BtPage)bt->mem;
974         bt->cursor = (BtPage)(bt->mem + bt->page_size);
975         bt->page = (BtPage)(bt->mem + 2 * bt->page_size);
976         bt->alloc = (BtPage)(bt->mem + 3 * bt->page_size);
977         bt->temp = (BtPage)(bt->mem + 4 * bt->page_size);
978         bt->zero = (BtPage)(bt->mem + 5 * bt->page_size);
979
980         memset (bt->zero, 0, bt->page_size);
981         return bt;
982 }
983
984 // place write, read, or parent lock on requested page_no.
985
986 void bt_lockpage(BtLock mode, BtLatchSet *latch)
987 {
988         switch( mode ) {
989         case BtLockRead:
990                 bt_spinreadlock (latch->readwr);
991                 break;
992         case BtLockWrite:
993                 bt_spinwritelock (latch->readwr);
994                 break;
995         case BtLockAccess:
996                 bt_spinreadlock (latch->access);
997                 break;
998         case BtLockDelete:
999                 bt_spinwritelock (latch->access);
1000                 break;
1001         case BtLockParent:
1002                 bt_spinwritelock (latch->parent);
1003                 break;
1004         }
1005 }
1006
1007 // remove write, read, or parent lock on requested page
1008
1009 void bt_unlockpage(BtLock mode, BtLatchSet *latch)
1010 {
1011         switch( mode ) {
1012         case BtLockRead:
1013                 bt_spinreleaseread (latch->readwr);
1014                 break;
1015         case BtLockWrite:
1016                 bt_spinreleasewrite (latch->readwr);
1017                 break;
1018         case BtLockAccess:
1019                 bt_spinreleaseread (latch->access);
1020                 break;
1021         case BtLockDelete:
1022                 bt_spinreleasewrite (latch->access);
1023                 break;
1024         case BtLockParent:
1025                 bt_spinreleasewrite (latch->parent);
1026                 break;
1027         }
1028 }
1029
1030 //      allocate a new page and write page into it
1031
1032 uid bt_newpage(BtDb *bt, BtPage page)
1033 {
1034 uid new_page;
1035 int reuse;
1036
1037         //      lock allocation page
1038
1039         bt_spinwritelock(bt->latchmgr->lock);
1040
1041         // use empty chain first
1042         // else allocate empty page
1043
1044         if( new_page = bt_getid(bt->latchmgr->alloc[1].right) ) {
1045                 if( bt_mappage (bt, &bt->temp, new_page) )
1046                         return 0;
1047                 bt_putid(bt->latchmgr->alloc[1].right, bt_getid(bt->temp->right));
1048                 reuse = 1;
1049         } else {
1050                 new_page = bt_getid(bt->latchmgr->alloc->right);
1051                 bt_putid(bt->latchmgr->alloc->right, new_page+1);
1052                 reuse = 0;
1053         }
1054
1055         bt_spinreleasewrite(bt->latchmgr->lock);
1056
1057         if( !bt->mapped_io )
1058           if( bt_update(bt, page, new_page) )
1059                 return 0;       //don't unlock on error
1060           else
1061                 return new_page;
1062
1063 #ifdef unix
1064         if( pwrite(bt->idx, page, bt->page_size, new_page << bt->page_bits) < bt->page_size )
1065                 return bt->err = BTERR_wrt, 0;
1066
1067         // if writing first page of pool block, zero last page in the block
1068
1069         if( !reuse && bt->hashmask > 0 && (new_page & bt->hashmask) == 0 )
1070         {
1071                 // use zero buffer to write zeros
1072                 if( pwrite(bt->idx,bt->zero, bt->page_size, (new_page | bt->hashmask) << bt->page_bits) < bt->page_size )
1073                         return bt->err = BTERR_wrt, 0;
1074         }
1075 #else
1076         //      bring new page into pool and copy page.
1077         //      this will extend the file into the new pages.
1078
1079         if( bt_mappage (bt, &bt->temp, new_page) )
1080                 return 0;
1081
1082         memcpy(bt->temp, page, bt->page_size);
1083 #endif
1084         return new_page;
1085 }
1086
1087 //  compare two keys, returning > 0, = 0, or < 0
1088 //  as the comparison value
1089
1090 int keycmp (BtKey key1, unsigned char *key2, uint len2)
1091 {
1092 uint len1 = key1->len;
1093 int ans;
1094
1095         if( ans = memcmp (key1->key, key2, len1 > len2 ? len2 : len1) )
1096                 return ans;
1097
1098         if( len1 > len2 )
1099                 return 1;
1100         if( len1 < len2 )
1101                 return -1;
1102
1103         return 0;
1104 }
1105
1106 //  Update current page of btree by writing file contents
1107 //      or flushing mapped area to disk.
1108
1109 BTERR bt_update (BtDb *bt, BtPage page, uid page_no)
1110 {
1111 off64_t off = page_no << bt->page_bits;
1112
1113 #ifdef unix
1114     if( !bt->mapped_io )
1115          if( pwrite(bt->idx, page, bt->page_size, off) != bt->page_size )
1116                  return bt->err = BTERR_wrt;
1117 #else
1118 uint amt[1];
1119         if( !bt->mapped_io )
1120         {
1121                 SetFilePointer (bt->idx, (long)off, (long*)(&off)+1, FILE_BEGIN);
1122                 if( !WriteFile (bt->idx, (char *)page, bt->page_size, amt, NULL) )
1123                         return GetLastError(), bt->err = BTERR_wrt;
1124
1125                 if( *amt < bt->page_size )
1126                         return GetLastError(), bt->err = BTERR_wrt;
1127         } 
1128         else if( bt->mode == BT_fl ) {
1129                         FlushViewOfFile(page, bt->page_size);
1130                         FlushFileBuffers(bt->idx);
1131         }
1132 #endif
1133         return 0;
1134 }
1135
1136 // find page in cache 
1137
1138 BtHash *bt_findhash(BtDb *bt, uid page_no)
1139 {
1140 BtHash *hash;
1141 uint idx;
1142
1143         // compute cache block first page and hash idx 
1144
1145         page_no &= ~bt->hashmask;
1146         idx = (uint)(page_no >> bt->seg_bits) % bt->hashsize;
1147
1148         if( bt->cache[idx] ) 
1149                 hash = bt->nodes + bt->cache[idx];
1150         else
1151                 return NULL;
1152
1153         do if( hash->page_no == page_no )
1154                  break;
1155         while(hash = hash->hashnext );
1156
1157         return hash;
1158 }
1159
1160 // add page cache entry to hash index
1161
1162 void bt_linkhash(BtDb *bt, BtHash *node, uid page_no)
1163 {
1164 uint idx = (uint)(page_no >> bt->seg_bits) % bt->hashsize;
1165 BtHash *hash;
1166
1167         if( bt->cache[idx] ) {
1168                 node->hashnext = hash = bt->nodes + bt->cache[idx];
1169                 hash->hashprev = node;
1170         }
1171
1172         node->hashprev = NULL;
1173         bt->cache[idx] = (ushort)(node - bt->nodes);
1174 }
1175
1176 // remove cache entry from hash table
1177
1178 void bt_unlinkhash(BtDb *bt, BtHash *node)
1179 {
1180 uint idx = (uint)(node->page_no >> bt->seg_bits) % bt->hashsize;
1181 BtHash *hash;
1182
1183         // unlink node
1184         if( hash = node->hashprev )
1185                 hash->hashnext = node->hashnext;
1186         else if( hash = node->hashnext )
1187                 bt->cache[idx] = (ushort)(hash - bt->nodes);
1188         else
1189                 bt->cache[idx] = 0;
1190
1191         if( hash = node->hashnext )
1192                 hash->hashprev = node->hashprev;
1193 }
1194
1195 // add cache page to lru chain and map pages
1196
1197 BtPage bt_linklru(BtDb *bt, BtHash *hash, uid page_no)
1198 {
1199 int flag;
1200 off64_t off = (page_no & ~bt->hashmask) << bt->page_bits;
1201 off64_t limit = off + ((bt->hashmask+1) << bt->page_bits);
1202 BtHash *node;
1203
1204         memset(hash, 0, sizeof(BtHash));
1205         hash->page_no = (page_no & ~bt->hashmask);
1206         bt_linkhash(bt, hash, page_no);
1207
1208         if( node = hash->lrunext = bt->lrufirst )
1209                 node->lruprev = hash;
1210         else
1211                 bt->lrulast = hash;
1212
1213         bt->lrufirst = hash;
1214
1215 #ifdef unix
1216         flag = PROT_READ | ( bt->mode == BT_ro ? 0 : PROT_WRITE );
1217         hash->page = (BtPage)mmap (0, (bt->hashmask+1) << bt->page_bits, flag, MAP_SHARED, bt->idx, off);
1218         if( hash->page == MAP_FAILED )
1219                 return bt->err = BTERR_map, (BtPage)NULL;
1220
1221 #else
1222         flag = ( bt->mode == BT_ro ? PAGE_READONLY : PAGE_READWRITE );
1223         hash->hmap = CreateFileMapping(bt->idx, NULL, flag,     (DWORD)(limit >> 32), (DWORD)limit, NULL);
1224         if( !hash->hmap )
1225                 return bt->err = BTERR_map, NULL;
1226
1227         flag = ( bt->mode == BT_ro ? FILE_MAP_READ : FILE_MAP_WRITE );
1228         hash->page = MapViewOfFile(hash->hmap, flag, (DWORD)(off >> 32), (DWORD)off, (bt->hashmask+1) << bt->page_bits);
1229         if( !hash->page )
1230                 return bt->err = BTERR_map, NULL;
1231 #endif
1232
1233         return (BtPage)((char*)hash->page + ((uint)(page_no & bt->hashmask) << bt->page_bits));
1234 }
1235
1236 //      find or place requested page in page-cache
1237 //      return memory address where page is located.
1238
1239 BtPage bt_hashpage(BtDb *bt, uid page_no)
1240 {
1241 BtHash *hash, *node, *next;
1242 BtPage page;
1243
1244         // find page in cache and move to top of lru list  
1245
1246         if( hash = bt_findhash(bt, page_no) ) {
1247                 page = (BtPage)((char*)hash->page + ((uint)(page_no & bt->hashmask) << bt->page_bits));
1248                 // swap node in lru list
1249                 if( node = hash->lruprev ) {
1250                         if( next = node->lrunext = hash->lrunext )
1251                                 next->lruprev = node;
1252                         else
1253                                 bt->lrulast = node;
1254
1255                         if( next = hash->lrunext = bt->lrufirst )
1256                                 next->lruprev = hash;
1257                         else
1258                                 return bt->err = BTERR_hash, (BtPage)NULL;
1259
1260                         hash->lruprev = NULL;
1261                         bt->lrufirst = hash;
1262                 }
1263                 return page;
1264         }
1265
1266         // map pages and add to cache entry
1267
1268         if( bt->nodecnt < bt->nodemax ) {
1269                 hash = bt->nodes + ++bt->nodecnt;
1270                 return bt_linklru(bt, hash, page_no);
1271         }
1272
1273         // hash table is already full, replace last lru entry from the cache
1274
1275         if( hash = bt->lrulast ) {
1276                 // unlink from lru list
1277                 if( node = bt->lrulast = hash->lruprev )
1278                         node->lrunext = NULL;
1279                 else
1280                         return bt->err = BTERR_hash, (BtPage)NULL;
1281
1282 #ifdef unix
1283                 munmap (hash->page, (bt->hashmask+1) << bt->page_bits);
1284 #else
1285                 FlushViewOfFile(hash->page, 0);
1286                 UnmapViewOfFile(hash->page);
1287                 CloseHandle(hash->hmap);
1288 #endif
1289                 // unlink from hash table
1290
1291                 bt_unlinkhash(bt, hash);
1292
1293                 // map and add to cache
1294
1295                 return bt_linklru(bt, hash, page_no);
1296         }
1297
1298         return bt->err = BTERR_hash, (BtPage)NULL;
1299 }
1300
1301 //  map a btree page onto current page
1302
1303 BTERR bt_mappage (BtDb *bt, BtPage *page, uid page_no)
1304 {
1305 off64_t off = page_no << bt->page_bits;
1306 #ifndef unix
1307 int amt[1];
1308 #endif
1309         
1310         if( bt->mapped_io ) {
1311                 bt->err = 0;
1312                 *page = bt_hashpage(bt, page_no);
1313                 return bt->err;
1314         }
1315 #ifdef unix
1316         if( pread(bt->idx, *page, bt->page_size, off) < bt->page_size )
1317                 return bt->err = BTERR_map;
1318 #else
1319         SetFilePointer (bt->idx, (long)off, (long*)(&off)+1, FILE_BEGIN);
1320
1321         if( !ReadFile(bt->idx, *page, bt->page_size, amt, NULL) )
1322                 return bt->err = BTERR_map;
1323
1324         if( *amt <  bt->page_size )
1325                 return bt->err = BTERR_map;
1326 #endif
1327         return 0;
1328 }
1329
1330 //      deallocate a deleted page 
1331 //      place on free chain out of allocator page
1332 //      call with page latched for Writing and Deleting
1333
1334 BTERR bt_freepage(BtDb *bt, uid page_no, BtPage page, BtLatchSet *latch)
1335 {
1336         if( bt_mappage (bt, &page, page_no) )
1337                 return bt->err;
1338
1339         //      lock allocation page
1340
1341         bt_spinwritelock (bt->latchmgr->lock);
1342
1343         //      store chain in second right
1344         bt_putid(page->right, bt_getid(bt->latchmgr->alloc[1].right));
1345         bt_putid(bt->latchmgr->alloc[1].right, page_no);
1346         page->free = 1;
1347
1348         if( bt_update(bt, page, page_no) )
1349                 return bt->err;
1350
1351         // unlock released page
1352
1353         bt_unlockpage (BtLockDelete, latch);
1354         bt_unlockpage (BtLockWrite, latch);
1355         bt_unpinlatch (latch);
1356
1357         // unlock allocation page
1358
1359         bt_spinreleasewrite (bt->latchmgr->lock);
1360         return 0;
1361 }
1362
1363 //  find slot in page for given key at a given level
1364
1365 int bt_findslot (BtDb *bt, unsigned char *key, uint len)
1366 {
1367 uint diff, higher = bt->page->cnt, low = 1, slot;
1368 uint good = 0;
1369
1370         //      make stopper key an infinite fence value
1371
1372         if( bt_getid (bt->page->right) )
1373                 higher++;
1374         else
1375                 good++;
1376
1377         //      low is the lowest candidate, higher is already
1378         //      tested as .ge. the given key, loop ends when they meet
1379
1380         while( diff = higher - low ) {
1381                 slot = low + ( diff >> 1 );
1382                 if( keycmp (keyptr(bt->page, slot), key, len) < 0 )
1383                         low = slot + 1;
1384                 else
1385                         higher = slot, good++;
1386         }
1387
1388         //      return zero if key is on right link page
1389
1390         return good ? higher : 0;
1391 }
1392
1393 //  find and load page at given level for given key
1394 //      leave page rd or wr locked as requested
1395
1396 int bt_loadpage (BtDb *bt, unsigned char *key, uint len, uint lvl, uint lock)
1397 {
1398 uid page_no = ROOT_page, prevpage = 0;
1399 uint drill = 0xff, slot;
1400 BtLatchSet *prevlatch;
1401 uint mode, prevmode;
1402
1403   //  start at root of btree and drill down
1404
1405   do {
1406         // determine lock mode of drill level
1407         mode = (lock == BtLockWrite) && (drill == lvl) ? BtLockWrite : BtLockRead; 
1408
1409         bt->latch = bt_pinlatch(bt, page_no);
1410         bt->page_no = page_no;
1411
1412         // obtain access lock using lock chaining
1413
1414         if( page_no > ROOT_page )
1415                 bt_lockpage(BtLockAccess, bt->latch);
1416
1417         if( prevpage ) {
1418                 bt_unlockpage(prevmode, prevlatch);
1419                 bt_unpinlatch(prevlatch);
1420                 prevpage = 0;
1421         }
1422
1423         // obtain read lock using lock chaining
1424
1425         bt_lockpage(mode, bt->latch);
1426
1427         if( page_no > ROOT_page )
1428                 bt_unlockpage(BtLockAccess, bt->latch);
1429
1430         //      map/obtain page contents
1431
1432         if( bt_mappage (bt, &bt->page, page_no) )
1433                 return 0;
1434
1435         // re-read and re-lock root after determining actual level of root
1436
1437         if( bt->page->lvl != drill) {
1438                 if( bt->page_no != ROOT_page )
1439                         return bt->err = BTERR_struct, 0;
1440                         
1441                 drill = bt->page->lvl;
1442
1443                 if( lock != BtLockRead && drill == lvl ) {
1444                         bt_unlockpage(mode, bt->latch);
1445                         bt_unpinlatch(bt->latch);
1446                         continue;
1447                 }
1448         }
1449
1450         prevpage = bt->page_no;
1451         prevlatch = bt->latch;
1452         prevmode = mode;
1453
1454         //  find key on page at this level
1455         //  and descend to requested level
1456
1457         if( !bt->page->kill )
1458          if( slot = bt_findslot (bt, key, len) ) {
1459           if( drill == lvl )
1460                 return slot;
1461
1462           while( slotptr(bt->page, slot)->dead )
1463                 if( slot++ < bt->page->cnt )
1464                         continue;
1465                 else
1466                         goto slideright;
1467
1468           page_no = bt_getid(slotptr(bt->page, slot)->id);
1469           drill--;
1470           continue;
1471          }
1472
1473         //  or slide right into next page
1474
1475 slideright:
1476         page_no = bt_getid(bt->page->right);
1477
1478   } while( page_no );
1479
1480   // return error on end of right chain
1481
1482   bt->err = BTERR_eof;
1483   return 0;     // return error
1484 }
1485
1486 //      a fence key was deleted from a page
1487 //      push new fence value upwards
1488
1489 BTERR bt_fixfence (BtDb *bt, uid page_no, uint lvl)
1490 {
1491 unsigned char leftkey[256], rightkey[256];
1492 BtLatchSet *latch = bt->latch;
1493 BtKey ptr;
1494
1495         // remove deleted key, the old fence value
1496
1497         ptr = keyptr(bt->page, bt->page->cnt);
1498         memcpy(rightkey, ptr, ptr->len + 1);
1499
1500         memset (slotptr(bt->page, bt->page->cnt--), 0, sizeof(BtSlot));
1501         bt->page->dirty = 1;
1502
1503         ptr = keyptr(bt->page, bt->page->cnt);
1504         memcpy(leftkey, ptr, ptr->len + 1);
1505
1506         if( bt_update (bt, bt->page, page_no) )
1507                 return bt->err;
1508
1509         bt_lockpage (BtLockParent, latch);
1510         bt_unlockpage (BtLockWrite, latch);
1511
1512         //  insert new (now smaller) fence key
1513
1514         if( bt_insertkey (bt, leftkey+1, *leftkey, lvl + 1, page_no, time(NULL)) )
1515                 return bt->err;
1516
1517         //  remove old (larger) fence key
1518
1519         if( bt_deletekey (bt, rightkey+1, *rightkey, lvl + 1) )
1520                 return bt->err;
1521
1522         bt_unlockpage (BtLockParent, latch);
1523         bt_unpinlatch (latch);
1524         return 0;
1525 }
1526
1527 //      root has a single child
1528 //      collapse a level from the btree
1529 //      call with root locked in bt->page
1530
1531 BTERR bt_collapseroot (BtDb *bt, BtPage root)
1532 {
1533 BtLatchSet *latch;
1534 uid child;
1535 uint idx;
1536
1537         // find the child entry
1538         //      and promote to new root
1539
1540   do {
1541         for( idx = 0; idx++ < root->cnt; )
1542           if( !slotptr(root, idx)->dead )
1543                 break;
1544
1545         child = bt_getid (slotptr(root, idx)->id);
1546         latch = bt_pinlatch (bt, child);
1547
1548         bt_lockpage (BtLockDelete, latch);
1549         bt_lockpage (BtLockWrite, latch);
1550
1551         if( bt_mappage (bt, &bt->temp, child) )
1552                 return bt->err;
1553
1554         memcpy (root, bt->temp, bt->page_size);
1555
1556         if( bt_update (bt, root, ROOT_page) )
1557                 return bt->err;
1558
1559         if( bt_freepage (bt, child, bt->temp, latch) )
1560                 return bt->err;
1561
1562   } while( root->lvl > 1 && root->act == 1 );
1563
1564   bt_unlockpage (BtLockWrite, bt->latch);
1565   bt_unpinlatch (bt->latch);
1566   return 0;
1567 }
1568
1569 //  find and delete key on page by marking delete flag bit
1570 //  when page becomes empty, delete it
1571
1572 BTERR bt_deletekey (BtDb *bt, unsigned char *key, uint len, uint lvl)
1573 {
1574 unsigned char lowerkey[256], higherkey[256];
1575 uint slot, dirty = 0, idx, fence, found;
1576 BtLatchSet *latch, *rlatch;
1577 uid page_no, right;
1578 BtKey ptr;
1579
1580         if( slot = bt_loadpage (bt, key, len, lvl, BtLockWrite) )
1581                 ptr = keyptr(bt->page, slot);
1582         else
1583                 return bt->err;
1584
1585         // are we deleting a fence slot?
1586
1587         fence = slot == bt->page->cnt;
1588
1589         // if key is found delete it, otherwise ignore request
1590
1591         if( found = !keycmp (ptr, key, len) )
1592           if( found = slotptr(bt->page, slot)->dead == 0 ) {
1593                 dirty = slotptr(bt->page,slot)->dead = 1;
1594                 bt->page->dirty = 1;
1595                 bt->page->act--;
1596
1597                 // collapse empty slots
1598
1599                 while( idx = bt->page->cnt - 1 )
1600                   if( slotptr(bt->page, idx)->dead ) {
1601                         *slotptr(bt->page, idx) = *slotptr(bt->page, idx + 1);
1602                         memset (slotptr(bt->page, bt->page->cnt--), 0, sizeof(BtSlot));
1603                   } else
1604                         break;
1605           }
1606
1607         right = bt_getid(bt->page->right);
1608         page_no = bt->page_no;
1609         latch = bt->latch;
1610
1611         if( !dirty ) {
1612           if( lvl )
1613                 return bt_abort (bt, bt->page, page_no, BTERR_notfound);
1614           bt_unlockpage(BtLockWrite, latch);
1615           bt_unpinlatch (latch);
1616           return bt->found = found, 0;
1617         }
1618
1619         //      did we delete a fence key in an upper level?
1620
1621         if( lvl && bt->page->act && fence )
1622           if( bt_fixfence (bt, page_no, lvl) )
1623                 return bt->err;
1624           else
1625                 return bt->found = found, 0;
1626
1627         //      is this a collapsed root?
1628
1629         if( lvl > 1 && page_no == ROOT_page && bt->page->act == 1 )
1630           if( bt_collapseroot (bt, bt->page) )
1631                 return bt->err;
1632           else
1633                 return bt->found = found, 0;
1634
1635         // return if page is not empty
1636
1637         if( bt->page->act ) {
1638           if( bt_update(bt, bt->page, page_no) )
1639                 return bt->err;
1640           bt_unlockpage(BtLockWrite, latch);
1641           bt_unpinlatch (latch);
1642           return bt->found = found, 0;
1643         }
1644
1645         // cache copy of fence key
1646         //      in order to find parent
1647
1648         ptr = keyptr(bt->page, bt->page->cnt);
1649         memcpy(lowerkey, ptr, ptr->len + 1);
1650
1651         // obtain lock on right page
1652
1653         rlatch = bt_pinlatch (bt, right);
1654         bt_lockpage(BtLockWrite, rlatch);
1655
1656         if( bt_mappage (bt, &bt->temp, right) )
1657                 return bt->err;
1658
1659         if( bt->temp->kill ) {
1660                 bt_abort(bt, bt->temp, right, 0);
1661                 return bt_abort(bt, bt->page, bt->page_no, BTERR_kill);
1662         }
1663
1664         // pull contents of next page into current empty page 
1665
1666         memcpy (bt->page, bt->temp, bt->page_size);
1667
1668         //      cache copy of key to update
1669
1670         ptr = keyptr(bt->temp, bt->temp->cnt);
1671         memcpy(higherkey, ptr, ptr->len + 1);
1672
1673         //  Mark right page as deleted and point it to left page
1674         //      until we can post updates at higher level.
1675
1676         bt_putid(bt->temp->right, page_no);
1677         bt->temp->kill = 1;
1678
1679         if( bt_update(bt, bt->page, page_no) )
1680                 return bt->err;
1681
1682         if( bt_update(bt, bt->temp, right) )
1683                 return bt->err;
1684
1685         bt_lockpage(BtLockParent, latch);
1686         bt_unlockpage(BtLockWrite, latch);
1687
1688         bt_lockpage(BtLockParent, rlatch);
1689         bt_unlockpage(BtLockWrite, rlatch);
1690
1691         //  redirect higher key directly to consolidated node
1692
1693         if( bt_insertkey (bt, higherkey+1, *higherkey, lvl+1, page_no, time(NULL)) )
1694                 return bt->err;
1695
1696         //  delete old lower key to consolidated node
1697
1698         if( bt_deletekey (bt, lowerkey + 1, *lowerkey, lvl + 1) )
1699                 return bt->err;
1700
1701         //  obtain write & delete lock on deleted node
1702         //      add right block to free chain
1703
1704         bt_lockpage(BtLockDelete, rlatch);
1705         bt_lockpage(BtLockWrite, rlatch);
1706         bt_unlockpage(BtLockParent, rlatch);
1707
1708         if( bt_freepage (bt, right, bt->temp, rlatch) )
1709                 return bt->err;
1710
1711         bt_unlockpage(BtLockParent, latch);
1712         bt_unpinlatch(latch);
1713         return 0;
1714 }
1715
1716 //      find key in leaf level and return row-id
1717
1718 uid bt_findkey (BtDb *bt, unsigned char *key, uint len)
1719 {
1720 uint  slot;
1721 BtKey ptr;
1722 uid id;
1723
1724         if( slot = bt_loadpage (bt, key, len, 0, BtLockRead) )
1725                 ptr = keyptr(bt->page, slot);
1726         else
1727                 return 0;
1728
1729         // if key exists, return row-id
1730         //      otherwise return 0
1731
1732         if( ptr->len == len && !memcmp (ptr->key, key, len) )
1733                 id = bt_getid(slotptr(bt->page,slot)->id);
1734         else
1735                 id = 0;
1736
1737         bt_unlockpage (BtLockRead, bt->latch);
1738         bt_unpinlatch (bt->latch);
1739         return id;
1740 }
1741
1742 //      check page for space available,
1743 //      clean if necessary and return
1744 //      0 - page needs splitting
1745 //      >0 - go ahead with new slot
1746  
1747 uint bt_cleanpage(BtDb *bt, uint amt, uint slot)
1748 {
1749 uint nxt = bt->page_size;
1750 BtPage page = bt->page;
1751 uint cnt = 0, idx = 0;
1752 uint max = page->cnt;
1753 uint newslot = slot;
1754 BtKey key;
1755 int ret;
1756
1757         if( page->min >= (max+1) * sizeof(BtSlot) + sizeof(*page) + amt + 1 )
1758                 return slot;
1759
1760         //      skip cleanup if nothing to reclaim
1761
1762         if( !page->dirty )
1763                 return 0;
1764
1765         memcpy (bt->frame, page, bt->page_size);
1766
1767         // skip page info and set rest of page to zero
1768
1769         memset (page+1, 0, bt->page_size - sizeof(*page));
1770         page->act = 0;
1771
1772         while( cnt++ < max ) {
1773                 if( cnt == slot )
1774                         newslot = idx + 1;
1775                 // always leave fence key in list
1776                 if( cnt < max && slotptr(bt->frame,cnt)->dead )
1777                         continue;
1778
1779                 // copy key
1780                 key = keyptr(bt->frame, cnt);
1781                 nxt -= key->len + 1;
1782                 memcpy ((unsigned char *)page + nxt, key, key->len + 1);
1783
1784                 // copy slot
1785                 memcpy(slotptr(page, ++idx)->id, slotptr(bt->frame, cnt)->id, BtId);
1786                 if( !(slotptr(page, idx)->dead = slotptr(bt->frame, cnt)->dead) )
1787                         page->act++;
1788                 slotptr(page, idx)->tod = slotptr(bt->frame, cnt)->tod;
1789                 slotptr(page, idx)->off = nxt;
1790         }
1791
1792         page->min = nxt;
1793         page->cnt = idx;
1794
1795         if( page->min >= (max+1) * sizeof(BtSlot) + sizeof(*page) + amt + 1 )
1796                 return newslot;
1797
1798         return 0;
1799 }
1800
1801 // split the root and raise the height of the btree
1802
1803 BTERR bt_splitroot(BtDb *bt, unsigned char *leftkey, uid page_no2)
1804 {
1805 uint nxt = bt->page_size;
1806 BtPage root = bt->page;
1807 uid right;
1808
1809         //  Obtain an empty page to use, and copy the current
1810         //  root contents into it
1811
1812         if( !(right = bt_newpage(bt, root)) )
1813                 return bt->err;
1814
1815         // preserve the page info at the bottom
1816         // and set rest to zero
1817
1818         memset(root+1, 0, bt->page_size - sizeof(*root));
1819
1820         // insert first key on newroot page
1821
1822         nxt -= *leftkey + 1;
1823         memcpy ((unsigned char *)root + nxt, leftkey, *leftkey + 1);
1824         bt_putid(slotptr(root, 1)->id, right);
1825         slotptr(root, 1)->off = nxt;
1826         
1827         // insert second key on newroot page
1828         // and increase the root height
1829
1830         nxt -= 3;
1831         ((unsigned char *)root)[nxt] = 2;
1832         ((unsigned char *)root)[nxt+1] = 0xff;
1833         ((unsigned char *)root)[nxt+2] = 0xff;
1834         bt_putid(slotptr(root, 2)->id, page_no2);
1835         slotptr(root, 2)->off = nxt;
1836
1837         bt_putid(root->right, 0);
1838         root->min = nxt;                // reset lowest used offset and key count
1839         root->cnt = 2;
1840         root->act = 2;
1841         root->lvl++;
1842
1843         // update and release root (bt->page)
1844
1845         if( bt_update(bt, root, bt->page_no) )
1846                 return bt->err;
1847
1848         bt_unlockpage(BtLockWrite, bt->latch);
1849         bt_unpinlatch(bt->latch);
1850         return 0;
1851 }
1852
1853 //  split already locked full node
1854 //      return unlocked.
1855
1856 BTERR bt_splitpage (BtDb *bt)
1857 {
1858 uint cnt = 0, idx = 0, max, nxt = bt->page_size;
1859 unsigned char fencekey[256], rightkey[256];
1860 uid page_no = bt->page_no, right;
1861 BtLatchSet *latch, *rlatch;
1862 BtPage page = bt->page;
1863 uint lvl = page->lvl;
1864 BtKey key;
1865
1866         latch = bt->latch;
1867
1868         //  split higher half of keys to bt->frame
1869         //      the last key (fence key) might be dead
1870
1871         memset (bt->frame, 0, bt->page_size);
1872         max = page->cnt;
1873         cnt = max / 2;
1874         idx = 0;
1875
1876         while( cnt++ < max ) {
1877                 key = keyptr(page, cnt);
1878                 nxt -= key->len + 1;
1879                 memcpy ((unsigned char *)bt->frame + nxt, key, key->len + 1);
1880                 memcpy(slotptr(bt->frame,++idx)->id, slotptr(page,cnt)->id, BtId);
1881                 if( !(slotptr(bt->frame, idx)->dead = slotptr(page, cnt)->dead) )
1882                         bt->frame->act++;
1883                 slotptr(bt->frame, idx)->tod = slotptr(page, cnt)->tod;
1884                 slotptr(bt->frame, idx)->off = nxt;
1885         }
1886
1887         //      remember fence key for new right page
1888
1889         memcpy (rightkey, key, key->len + 1);
1890
1891         bt->frame->bits = bt->page_bits;
1892         bt->frame->min = nxt;
1893         bt->frame->cnt = idx;
1894         bt->frame->lvl = lvl;
1895
1896         // link right node
1897
1898         if( page_no > ROOT_page )
1899                 memcpy (bt->frame->right, page->right, BtId);
1900
1901         //      get new free page and write frame to it.
1902
1903         if( !(right = bt_newpage(bt, bt->frame)) )
1904                 return bt->err;
1905
1906         //      update lower keys to continue in old page
1907
1908         memcpy (bt->frame, page, bt->page_size);
1909         memset (page+1, 0, bt->page_size - sizeof(*page));
1910         nxt = bt->page_size;
1911         page->dirty = 0;
1912         page->act = 0;
1913         cnt = 0;
1914         idx = 0;
1915
1916         //  assemble page of smaller keys
1917         //      (they're all active keys)
1918
1919         while( cnt++ < max / 2 ) {
1920                 key = keyptr(bt->frame, cnt);
1921                 nxt -= key->len + 1;
1922                 memcpy ((unsigned char *)page + nxt, key, key->len + 1);
1923                 memcpy(slotptr(page,++idx)->id, slotptr(bt->frame,cnt)->id, BtId);
1924                 slotptr(page, idx)->tod = slotptr(bt->frame, cnt)->tod;
1925                 slotptr(page, idx)->off = nxt;
1926                 page->act++;
1927         }
1928
1929         // remember fence key for smaller page
1930
1931         memcpy (fencekey, key, key->len + 1);
1932
1933         bt_putid(page->right, right);
1934         page->min = nxt;
1935         page->cnt = idx;
1936
1937         // if current page is the root page, split it
1938
1939         if( page_no == ROOT_page )
1940                 return bt_splitroot (bt, fencekey, right);
1941
1942         //      lock right page
1943
1944         rlatch = bt_pinlatch (bt, right);
1945         bt_lockpage (BtLockParent, rlatch);
1946
1947         // update left (containing) node
1948
1949         if( bt_update(bt, page, page_no) )
1950                 return bt->err;
1951
1952         bt_lockpage (BtLockParent, latch);
1953         bt_unlockpage (BtLockWrite, latch);
1954
1955         // insert new fence for reformulated left block
1956
1957         if( bt_insertkey (bt, fencekey+1, *fencekey, lvl+1, page_no, time(NULL)) )
1958                 return bt->err;
1959
1960         //      switch fence for right block of larger keys to new right page
1961
1962         if( bt_insertkey (bt, rightkey+1, *rightkey, lvl+1, right, time(NULL)) )
1963                 return bt->err;
1964
1965         bt_unlockpage (BtLockParent, latch);
1966         bt_unlockpage (BtLockParent, rlatch);
1967
1968         bt_unpinlatch (rlatch);
1969         bt_unpinlatch (latch);
1970         return 0;
1971 }
1972
1973 //  Insert new key into the btree at requested level.
1974 //  Pages are unlocked at exit.
1975
1976 BTERR bt_insertkey (BtDb *bt, unsigned char *key, uint len, uint lvl, uid id, uint tod)
1977 {
1978 uint slot, idx;
1979 BtPage page;
1980 BtKey ptr;
1981
1982   while( 1 ) {
1983         if( slot = bt_loadpage (bt, key, len, lvl, BtLockWrite) )
1984                 ptr = keyptr(bt->page, slot);
1985         else
1986         {
1987                 if( !bt->err )
1988                         bt->err = BTERR_ovflw;
1989                 return bt->err;
1990         }
1991
1992         // if key already exists, update id and return
1993
1994         page = bt->page;
1995
1996         if( !keycmp (ptr, key, len) ) {
1997           if( slotptr(page, slot)->dead )
1998                 page->act++;
1999           slotptr(page, slot)->dead = 0;
2000           slotptr(page, slot)->tod = tod;
2001           bt_putid(slotptr(page,slot)->id, id);
2002           if( bt_update(bt, bt->page, bt->page_no) )
2003                 return bt->err;
2004           bt_unlockpage(BtLockWrite, bt->latch);
2005           bt_unpinlatch (bt->latch);
2006           return 0;
2007         }
2008
2009         // check if page has enough space
2010
2011         if( slot = bt_cleanpage (bt, len, slot) )
2012                 break;
2013
2014         if( bt_splitpage (bt) )
2015                 return bt->err;
2016   }
2017
2018   // calculate next available slot and copy key into page
2019
2020   page->min -= len + 1; // reset lowest used offset
2021   ((unsigned char *)page)[page->min] = len;
2022   memcpy ((unsigned char *)page + page->min +1, key, len );
2023
2024   for( idx = slot; idx < page->cnt; idx++ )
2025         if( slotptr(page, idx)->dead )
2026                 break;
2027
2028   // now insert key into array before slot
2029   // preserving the fence slot
2030
2031   if( idx == page->cnt )
2032         idx++, page->cnt++;
2033
2034   page->act++;
2035
2036   while( idx > slot )
2037         *slotptr(page, idx) = *slotptr(page, idx -1), idx--;
2038
2039   bt_putid(slotptr(page,slot)->id, id);
2040   slotptr(page, slot)->off = page->min;
2041   slotptr(page, slot)->tod = tod;
2042   slotptr(page, slot)->dead = 0;
2043
2044   if( bt_update(bt, bt->page, bt->page_no) )
2045           return bt->err;
2046
2047   bt_unlockpage(BtLockWrite, bt->latch);
2048   bt_unpinlatch(bt->latch);
2049   return 0;
2050 }
2051
2052 //  cache page of keys into cursor and return starting slot for given key
2053
2054 uint bt_startkey (BtDb *bt, unsigned char *key, uint len)
2055 {
2056 uint slot;
2057
2058         // cache page for retrieval
2059
2060         if( slot = bt_loadpage (bt, key, len, 0, BtLockRead) )
2061           memcpy (bt->cursor, bt->page, bt->page_size);
2062         else
2063           return 0;
2064
2065         bt_unlockpage(BtLockRead, bt->latch);
2066         bt->cursor_page = bt->page_no;
2067         bt_unpinlatch (bt->latch);
2068         return slot;
2069 }
2070
2071 //  return next slot for cursor page
2072 //  or slide cursor right into next page
2073
2074 uint bt_nextkey (BtDb *bt, uint slot)
2075 {
2076 BtLatchSet *latch;
2077 off64_t right;
2078
2079   do {
2080         right = bt_getid(bt->cursor->right);
2081
2082         while( slot++ < bt->cursor->cnt )
2083           if( slotptr(bt->cursor,slot)->dead )
2084                 continue;
2085           else if( right || (slot < bt->cursor->cnt))
2086                 return slot;
2087           else
2088                 break;
2089
2090         if( !right )
2091                 break;
2092
2093         bt->cursor_page = right;
2094         latch = bt_pinlatch (bt, right);
2095     bt_lockpage(BtLockRead, latch);
2096
2097         if( bt_mappage (bt, &bt->page, right) )
2098                 return 0;
2099
2100         memcpy (bt->cursor, bt->page, bt->page_size);
2101         bt_unlockpage(BtLockRead, latch);
2102         bt_unpinlatch (latch);
2103         slot = 0;
2104   } while( 1 );
2105
2106   return bt->err = 0;
2107 }
2108
2109 BtKey bt_key(BtDb *bt, uint slot)
2110 {
2111         return keyptr(bt->cursor, slot);
2112 }
2113
2114 uid bt_uid(BtDb *bt, uint slot)
2115 {
2116         return bt_getid(slotptr(bt->cursor,slot)->id);
2117 }
2118
2119 uint bt_tod(BtDb *bt, uint slot)
2120 {
2121         return slotptr(bt->cursor,slot)->tod;
2122 }
2123
2124
2125 #ifdef STANDALONE
2126
2127 uint bt_audit (BtDb *bt)
2128 {
2129 ushort idx, hashidx;
2130 uid next, page_no;
2131 BtLatchSet *latch;
2132 uint cnt = 0;
2133 BtKey ptr;
2134
2135 #ifdef unix
2136         if( *(ushort *)(bt->latchmgr->lock) )
2137                 fprintf(stderr, "Alloc page locked\n");
2138         *(ushort *)(bt->latchmgr->lock) = 0;
2139
2140         for( idx = 1; idx <= bt->latchmgr->latchdeployed; idx++ ) {
2141                 latch = bt->latchsets + idx;
2142                 if( *(ushort *)latch->readwr )
2143                         fprintf(stderr, "latchset %d rwlocked for page %.8x\n", idx, latch->page_no);
2144                 *(ushort *)latch->readwr = 0;
2145
2146                 if( *(ushort *)latch->access )
2147                         fprintf(stderr, "latchset %d accesslocked for page %.8x\n", idx, latch->page_no);
2148                 *(ushort *)latch->access = 0;
2149
2150                 if( *(ushort *)latch->parent )
2151                         fprintf(stderr, "latchset %d parentlocked for page %.8x\n", idx, latch->page_no);
2152                 *(ushort *)latch->parent = 0;
2153
2154                 if( latch->pin ) {
2155                         fprintf(stderr, "latchset %d pinned for page %.8x\n", idx, latch->page_no);
2156                         latch->pin = 0;
2157                 }
2158         }
2159
2160         for( hashidx = 0; hashidx < bt->latchmgr->latchhash; hashidx++ ) {
2161           if( *(ushort *)(bt->latchmgr->table[hashidx].latch) )
2162                         fprintf(stderr, "hash entry %d locked\n", hashidx);
2163
2164           *(ushort *)(bt->latchmgr->table[hashidx].latch) = 0;
2165
2166           if( idx = bt->latchmgr->table[hashidx].slot ) do {
2167                 latch = bt->latchsets + idx;
2168                 if( *(ushort *)latch->busy )
2169                         fprintf(stderr, "latchset %d busylocked for page %.8x\n", idx, latch->page_no);
2170                 *(ushort *)latch->busy = 0;
2171                 if( latch->hash != hashidx )
2172                         fprintf(stderr, "latchset %d wrong hashidx\n", idx);
2173                 if( latch->pin )
2174                         fprintf(stderr, "latchset %d pinned for page %.8x\n", idx, latch->page_no);
2175           } while( idx = latch->next );
2176         }
2177
2178         next = bt->latchmgr->nlatchpage + LATCH_page;
2179         page_no = LEAF_page;
2180
2181         while( page_no < bt_getid(bt->latchmgr->alloc->right) ) {
2182                 pread (bt->idx, bt->frame, bt->page_size, page_no << bt->page_bits);
2183                 if( !bt->frame->free ) {
2184                  for( idx = 0; idx++ < bt->frame->cnt - 1; ) {
2185                   ptr = keyptr(bt->frame, idx+1);
2186                   if( keycmp (keyptr(bt->frame, idx), ptr->key, ptr->len) >= 0 )
2187                         fprintf(stderr, "page %.8x idx %.2x out of order\n", page_no, idx);
2188                  }
2189                  if( !bt->frame->lvl )
2190                         cnt += bt->frame->act;
2191                 }
2192
2193                 if( page_no > LEAF_page )
2194                         next = page_no + 1;
2195                 page_no = next;
2196         }
2197         return cnt - 1;
2198 #endif
2199 }
2200
2201 #ifndef unix
2202 double getCpuTime(int type)
2203 {
2204 FILETIME crtime[1];
2205 FILETIME xittime[1];
2206 FILETIME systime[1];
2207 FILETIME usrtime[1];
2208 SYSTEMTIME timeconv[1];
2209 double ans = 0;
2210
2211         memset (timeconv, 0, sizeof(SYSTEMTIME));
2212
2213         switch( type ) {
2214         case 0:
2215                 GetSystemTimeAsFileTime (xittime);
2216                 FileTimeToSystemTime (xittime, timeconv);
2217                 ans = (double)timeconv->wDayOfWeek * 3600 * 24;
2218                 break;
2219         case 1:
2220                 GetProcessTimes (GetCurrentProcess(), crtime, xittime, systime, usrtime);
2221                 FileTimeToSystemTime (usrtime, timeconv);
2222                 break;
2223         case 2:
2224                 GetProcessTimes (GetCurrentProcess(), crtime, xittime, systime, usrtime);
2225                 FileTimeToSystemTime (systime, timeconv);
2226                 break;
2227         }
2228
2229         ans += (double)timeconv->wHour * 3600;
2230         ans += (double)timeconv->wMinute * 60;
2231         ans += (double)timeconv->wSecond;
2232         ans += (double)timeconv->wMilliseconds / 1000;
2233         return ans;
2234 }
2235 #else
2236 #include <time.h>
2237 #include <sys/resource.h>
2238
2239 double getCpuTime(int type)
2240 {
2241 struct rusage used[1];
2242 struct timeval tv[1];
2243
2244         switch( type ) {
2245         case 0:
2246                 gettimeofday(tv, NULL);
2247                 return (double)tv->tv_sec + (double)tv->tv_usec / 1000000;
2248
2249         case 1:
2250                 getrusage(RUSAGE_SELF, used);
2251                 return (double)used->ru_utime.tv_sec + (double)used->ru_utime.tv_usec / 1000000;
2252
2253         case 2:
2254                 getrusage(RUSAGE_SELF, used);
2255                 return (double)used->ru_stime.tv_sec + (double)used->ru_stime.tv_usec / 1000000;
2256         }
2257
2258         return 0;
2259 }
2260 #endif
2261
2262 //  standalone program to index file of keys
2263 //  then list them onto std-out
2264
2265 int main (int argc, char **argv)
2266 {
2267 uint slot, line = 0, off = 0, found = 0;
2268 int ch, cnt = 0, bits = 12;
2269 unsigned char key[256];
2270 double done, start;
2271 uid next, page_no;
2272 uint pgblk = 0;
2273 float elapsed;
2274 time_t tod[1];
2275 uint scan = 0;
2276 uint len = 0;
2277 uint map = 0;
2278 BtKey ptr;
2279 BtDb *bt;
2280 FILE *in;
2281
2282         if( argc < 4 ) {
2283                 fprintf (stderr, "Usage: %s idx_file src_file Read/Write/Scan/Delete/Find [page_bits mapped_pool_segments pages_per_segment start_line_number]\n", argv[0]);
2284                 fprintf (stderr, "  page_bits: size of btree page in bits\n");
2285                 fprintf (stderr, "  mapped_pool_segments: size of buffer pool in segments\n");
2286                 fprintf (stderr, "  pages_per_segment: size of buffer pool segment in pages in bits\n");
2287                 exit(0);
2288         }
2289
2290         start = getCpuTime(0);
2291         time(tod);
2292
2293         if( argc > 4 )
2294                 bits = atoi(argv[4]);
2295
2296         if( argc > 5 )
2297                 map = atoi(argv[5]);
2298
2299         if( map > 65536 )
2300                 fprintf (stderr, "Warning: buffer_pool > 65536 segments\n");
2301
2302         if( map && map < 8 )
2303                 fprintf (stderr, "Buffer_pool too small\n");
2304
2305         if( argc > 6 )
2306                 pgblk = atoi(argv[6]);
2307
2308         if( bits + pgblk > 30 )
2309                 fprintf (stderr, "Warning: very large buffer pool segment size\n");
2310
2311         if( argc > 7 )
2312                 off = atoi(argv[7]);
2313
2314         bt = bt_open ((argv[1]), BT_rw, bits, map, pgblk, map /  8);
2315
2316         if( !bt ) {
2317                 fprintf(stderr, "Index Open Error %s\n", argv[1]);
2318                 exit (1);
2319         }
2320
2321         switch(argv[3][0]| 0x20)
2322         {
2323         case 'a':
2324                 fprintf(stderr, "started audit for %s\n", argv[2]);
2325                 cnt = bt_audit (bt);
2326                 fprintf(stderr, "finished audit for %s, %d keys\n", argv[2], cnt);
2327                 break;
2328
2329         case 'w':
2330                 fprintf(stderr, "started indexing for %s\n", argv[2]);
2331                 if( argc > 2 && (in = fopen (argv[2], "rb")) )
2332                   while( ch = getc(in), ch != EOF )
2333                         if( ch == '\n' )
2334                         {
2335                           if( off )
2336                                 sprintf((char *)key+len, "%.9d", line + off), len += 9;
2337
2338                           if( bt_insertkey (bt, key, len, 0, ++line, *tod) )
2339                                 fprintf(stderr, "Error %d Line: %d\n", bt->err, line), exit(0);
2340                           len = 0;
2341                         }
2342                         else if( len < 245 )
2343                                 key[len++] = ch;
2344                 fprintf(stderr, "finished adding keys for %s, %d \n", argv[2], line);
2345                 break;
2346
2347         case 'd':
2348                 fprintf(stderr, "started deleting keys for %s\n", argv[2]);
2349                 if( argc > 2 && (in = fopen (argv[2], "rb")) )
2350                   while( ch = getc(in), ch != EOF )
2351                         if( ch == '\n' )
2352                         {
2353                           if( off )
2354                                 sprintf((char *)key+len, "%.9d", line + off), len += 9;
2355                           line++;
2356                           if( bt_deletekey (bt, key, len, 0) )
2357                                 fprintf(stderr, "Error %d Line: %d\n", bt->err, line), exit(0);
2358                           len = 0;
2359                         }
2360                         else if( len < 245 )
2361                                 key[len++] = ch;
2362                 fprintf(stderr, "finished deleting keys for %s, %d \n", argv[2], line);
2363                 break;
2364
2365         case 'f':
2366                 fprintf(stderr, "started finding keys for %s\n", argv[2]);
2367                 if( argc > 2 && (in = fopen (argv[2], "rb")) )
2368                   while( ch = getc(in), ch != EOF )
2369                         if( ch == '\n' )
2370                         {
2371                           if( off )
2372                                 sprintf((char *)key+len, "%.9d", line + off), len += 9;
2373                           line++;
2374                           if( bt_findkey (bt, key, len) )
2375                                 found++;
2376                           else if( bt->err )
2377                                 fprintf(stderr, "Error %d Syserr %d Line: %d\n", bt->err, errno, line), exit(0);
2378                           len = 0;
2379                         }
2380                         else if( len < 245 )
2381                                 key[len++] = ch;
2382                 fprintf(stderr, "finished search of %d keys for %s, found %d\n", line, argv[2], found);
2383                 break;
2384
2385         case 's':
2386                 fprintf(stderr, "started scaning\n");
2387                 cnt = len = key[0] = 0;
2388
2389                 if( slot = bt_startkey (bt, key, len) )
2390                   slot--;
2391                 else
2392                   fprintf(stderr, "Error %d in StartKey. Syserror: %d\n", bt->err, errno), exit(0);
2393
2394                 while( slot = bt_nextkey (bt, slot) ) {
2395                         ptr = bt_key(bt, slot);
2396                         fwrite (ptr->key, ptr->len, 1, stdout);
2397                         fputc ('\n', stdout);
2398                         cnt++;
2399                 }
2400
2401                 fprintf(stderr, " Total keys read %d\n", cnt - 1);
2402                 break;
2403
2404         case 'c':
2405           fprintf(stderr, "started counting\n");
2406
2407           next = bt->latchmgr->nlatchpage + LATCH_page;
2408           page_no = LEAF_page;
2409           cnt = 0;
2410
2411           while( page_no < bt_getid(bt->latchmgr->alloc->right) ) {
2412           uid off = page_no << bt->page_bits;
2413 #ifdef unix
2414                 pread (bt->idx, bt->frame, bt->page_size, off);
2415 #else
2416                 DWORD amt[1];
2417
2418                 SetFilePointer (bt->idx, (long)off, (long*)(&off)+1, FILE_BEGIN);
2419
2420                 if( !ReadFile(bt->idx, bt->frame, bt->page_size, amt, NULL))
2421                         fprintf (stderr, "unable to read page %.8x", page_no);
2422
2423                 if( *amt <  bt->page_size )
2424                         fprintf (stderr, "unable to read page %.8x", page_no);
2425 #endif
2426                 if( !bt->frame->free && !bt->frame->lvl )
2427                         cnt += bt->frame->act;
2428                 if( page_no > LEAF_page )
2429                         next = page_no + 1;
2430                 page_no = next;
2431           }
2432                 
2433           cnt--;        // remove stopper key
2434           fprintf(stderr, " Total keys read %d\n", cnt);
2435           break;
2436         }
2437
2438         done = getCpuTime(0);
2439         elapsed = (float)(done - start);
2440         fprintf(stderr, " real %dm%.3fs\n", (int)(elapsed/60), elapsed - (int)(elapsed/60)*60);
2441         elapsed = getCpuTime(1);
2442         fprintf(stderr, " user %dm%.3fs\n", (int)(elapsed/60), elapsed - (int)(elapsed/60)*60);
2443         elapsed = getCpuTime(2);
2444         fprintf(stderr, " sys  %dm%.3fs\n", (int)(elapsed/60), elapsed - (int)(elapsed/60)*60);
2445         return 0;
2446 }
2447
2448 #endif  //STANDALONE