summaryrefslogtreecommitdiff
path: root/biology/emboss/files/patch-ajindex.c
diff options
context:
space:
mode:
Diffstat (limited to 'biology/emboss/files/patch-ajindex.c')
-rw-r--r--biology/emboss/files/patch-ajindex.c5134
1 files changed, 5134 insertions, 0 deletions
diff --git a/biology/emboss/files/patch-ajindex.c b/biology/emboss/files/patch-ajindex.c
new file mode 100644
index 000000000000..410f90c1d2e2
--- /dev/null
+++ b/biology/emboss/files/patch-ajindex.c
@@ -0,0 +1,5134 @@
+--- ajax/ajindex.c.orig Mon Jan 23 12:17:02 2006
++++ ajax/ajindex.c Thu Dec 22 13:12:27 2005
+@@ -22,7 +22,6 @@
+
+
+
+-
+ static AjPBtpage btreeCacheLocate(const AjPBtcache cache, ajlong page);
+ static AjPBtpage btreeCacheLruUnlink(AjPBtcache cache);
+ static void btreeCacheUnlink(AjPBtcache cache, AjPBtpage cpage);
+@@ -35,10 +34,14 @@
+ AjBool isread);
+ static AjPBtpage btreeFindINode(AjPBtcache cache, AjPBtpage page,
+ const char *item);
++static AjPBtpage btreeSecFindINode(AjPBtcache cache, AjPBtpage page,
++ const char *item);
+
+
+ static AjPBtpage btreePageFromKey(AjPBtcache cache, unsigned char *buf,
+ const char *item);
++static AjPBtpage btreeSecPageFromKey(AjPBtcache cache, unsigned char *buf,
++ const char *item);
+ static ajint btreeNumInBucket(AjPBtcache cache, ajlong pageno);
+ static AjPBucket btreeReadBucket(AjPBtcache cache, ajlong pageno);
+ static void btreeWriteBucket(AjPBtcache cache, const AjPBucket bucket,
+@@ -55,6 +58,7 @@
+ AjPStr const *keys, const ajlong *ptrs,
+ ajint nkeys);
+ static AjBool btreeNodeIsFull(const AjPBtcache cache, AjPBtpage page);
++static AjBool btreeNodeIsFullSec(const AjPBtcache cache, AjPBtpage page);
+ static void btreeInsertNonFull(AjPBtcache cache, AjPBtpage page,
+ const AjPStr key, ajlong less,
+ ajlong greater);
+@@ -87,6 +91,8 @@
+
+
+
++
++
+ #if 0
+ static AjPBtpage btreeTraverseLeaves(AjPBtcache cache, AjPBtpage thys);
+ static void btreeJoinLeaves(AjPBtcache cache);
+@@ -168,6 +174,63 @@
+ static ajint btreeDbnoCompare(const void *a, const void *b);
+
+
++static AjPBtMem btreeAllocPriArray(AjPBtcache cache);
++static void btreeDeallocPriArray(AjPBtcache cache, AjPBtMem node);
++static AjPBtMem btreeAllocSecArray(AjPBtcache cache);
++static void btreeDeallocSecArray(AjPBtcache cache, AjPBtMem node);
++
++
++
++static void btreeAddToHybBucket(AjPBtcache cache, ajlong pageno,
++ const AjPBtHybrid id);
++static AjPBtpage btreeHybFindINode(AjPBtcache cache, AjPBtpage page,
++ const char *item);
++static AjPBtpage btreeHybPageFromKey(AjPBtcache cache,
++ unsigned char *buf, const char *key);
++static ajlong btreeHybInsertShift(AjPBtcache cache, AjPBtpage *retpage,
++ const char *key);
++static AjBool btreeHybReorderBuckets(AjPBtcache cache, AjPBtpage leaf);
++static AjPBtpage btreeHybSplitLeaf(AjPBtcache cache, AjPBtpage spage);
++static void btreeHybInsertKey(AjPBtcache cache, AjPBtpage page,
++ const AjPStr key, ajlong less,
++ ajlong greater);
++static void btreeHybSplitRoot(AjPBtcache cache);
++static void btreeHybDupInsert(AjPBtcache cache, AjPBtHybrid hyb,
++ AjPBtId btid);
++static void btreeGetNumKeys(AjPBtcache cache, unsigned char *buf,
++ ajlong **keys, ajlong **ptrs);
++static void btreeWriteNumNode(AjPBtcache cache, AjPBtpage spage,
++ const ajlong *keys, const ajlong *ptrs,
++ ajint nkeys);
++static AjPNumBucket btreeReadNumBucket(AjPBtcache cache, ajlong pageno);
++static void btreeNumBucketDel(AjPNumBucket *thys);
++static void btreeAddToNumBucket(AjPBtcache cache, ajlong pageno,
++ const AjPBtNumId num);
++static AjPBtpage btreeNumFindINode(AjPBtcache cache, AjPBtpage page,
++ const ajlong item);
++static AjPBtpage btreeNumPageFromKey(AjPBtcache cache, unsigned char *buf,
++ const ajlong key);
++static ajint btreeNumInNumBucket(AjPBtcache cache, ajlong pageno);
++static AjBool btreeReorderNumBuckets(AjPBtcache cache, AjPBtpage leaf);
++static AjPNumBucket btreeNumBucketNew(ajint n);
++static ajint btreeNumIdCompare(const void *a, const void *b);
++static AjBool btreeNumNodeIsFull(const AjPBtcache cache,
++ AjPBtpage page);
++static void btreeNumInsertNonFull(AjPBtcache cache, AjPBtpage page,
++ const ajlong key, ajlong less,
++ ajlong greater);
++static void btreeNumInsertKey(AjPBtcache cache, AjPBtpage page,
++ const ajlong key, ajlong less,
++ ajlong greater);
++static void btreeNumSplitRoot(AjPBtcache cache);
++static void btreeNumKeyShift(AjPBtcache cache, AjPBtpage tpage);
++static ajlong btreeNumInsertShift(AjPBtcache cache, AjPBtpage *retpage,
++ ajlong key);
++static AjPBtpage btreeNumSplitLeaf(AjPBtcache cache, AjPBtpage spage);
++
++
++
++
+
+
+
+@@ -196,11 +259,14 @@
+ {
+ FILE *fp;
+ AjPBtcache cache = NULL;
++#if 0
+ #if defined (HAVE64) && !defined(_OSF_SOURCE) && !defined(_AIX) && !defined(__hpux) && !defined(__ppc__) && !defined(__FreeBSD__)
+ struct stat64 buf;
+ #else
+ struct stat buf;
+ #endif
++#endif
++
+ ajlong filelen = 0L;
+
+ AjPStr fn = NULL;
+@@ -213,12 +279,18 @@
+ return NULL;
+
+
++ /* Commented out pending database updating */
++#if 0
++ if(strcmp(mode,"r"))
++ {
+ #if defined (HAVE64) && !defined(_OSF_SOURCE) && !defined(_AIX) && !defined(__hpux) && !defined(__ppc__) && !defined(__FreeBSD__)
+- if(!stat64(file, &buf))
++ if(!stat64(fn->Ptr, &buf))
+ #else
+- if(!stat(file, &buf))
++ if(!stat(fn->Ptr, &buf))
+ #endif
+- filelen = buf.st_size;
++ filelen = buf.st_size;
++ }
++#endif
+
+ AJNEW0(cache);
+
+@@ -244,6 +316,13 @@
+ cache->totsize = filelen;
+ cache->cachesize = cachesize;
+
++ cache->bmem = NULL;
++ cache->tmem = NULL;
++
++ cache->bsmem = NULL;
++ cache->tsmem = NULL;
++
++
+ /* Add slevel, sorder and snperbucket ???? */
+
+ ajStrDel(&fn);
+@@ -808,7 +887,7 @@
+ root = btreeCacheLocate(cache,0L);
+
+ if(!root)
+- ajFatal("Something has unlocked the PRI root cache page\n");
++ ajFatal("The PRI root cache page has been unlocked\n");
+
+ if(!cache->level)
+ return root;
+@@ -863,6 +942,48 @@
+
+
+
++/* @funcstatic btreeSecFindINode *************************************************
++**
++** Recursive search for insert node in a secondary tree
++**
++** @param [u] cache [AjPBtcache] cache
++** @param [u] page [AjPBtpage] page
++** @param [r] item [const char*] key to search for
++**
++** @return [AjPBtpage] leaf node where item should be inserted
++** @@
++******************************************************************************/
++
++static AjPBtpage btreeSecFindINode(AjPBtcache cache, AjPBtpage page,
++ const char *item)
++{
++ AjPBtpage ret = NULL;
++ AjPBtpage pg = NULL;
++
++ unsigned char *buf = NULL;
++ ajint status = 0;
++ ajint ival = 0;
++
++ /* ajDebug("In btreeSecFindINode\n"); */
++
++ ret = page;
++ buf = page->buf;
++ GBT_NODETYPE(buf,&ival);
++ if(ival != BT_LEAF)
++ {
++ status = ret->dirty;
++ ret->dirty = BT_LOCK; /* Lock in case of lots of overflow pages */
++ pg = btreeSecPageFromKey(cache,buf,item);
++ ret->dirty = status;
++ ret = btreeSecFindINode(cache,pg,item);
++ }
++
++ return ret;
++}
++
++
++
++
+ /* @funcstatic btreePageFromKey *******************************************
+ **
+ ** Return next lower index page given a key
+@@ -928,6 +1049,71 @@
+
+
+
++/* @funcstatic btreeSecPageFromKey *******************************************
++**
++** Return next lower index page given a key in a secondary tree
++**
++** @param [u] cache [AjPBtcache] cache
++** @param [u] buf [unsigned char *] page buffer
++** @param [r] key [const char *] key to search for
++**
++** @return [AjPBtpage] pointer to a page
++** @@
++******************************************************************************/
++
++static AjPBtpage btreeSecPageFromKey(AjPBtcache cache, unsigned char *buf,
++ const char *key)
++{
++ unsigned char *rootbuf = NULL;
++ ajint nkeys = 0;
++ ajint order = 0;
++ ajint i;
++
++ ajlong blockno = 0L;
++ AjPStr *karray = NULL;
++ ajlong *parray = NULL;
++ AjPBtpage page = NULL;
++
++ /* ajDebug("In btreePageFromKey\n"); */
++
++ rootbuf = buf;
++
++
++ GBT_NKEYS(rootbuf,&nkeys);
++ order = cache->sorder;
++
++ AJCNEW0(karray,order);
++ AJCNEW0(parray,order);
++ for(i=0;i<order;++i)
++ karray[i] = ajStrNew();
++
++ btreeGetKeys(cache,rootbuf,&karray,&parray);
++ i = 0;
++ while(i!=nkeys && strcmp(key,karray[i]->Ptr)>=0)
++ ++i;
++ if(i==nkeys)
++ {
++ if(strcmp(key,karray[i-1]->Ptr)<0)
++ blockno = parray[i-1];
++ else
++ blockno = parray[i];
++ }
++ else
++ blockno = parray[i];
++
++ for(i=0;i<order;++i)
++ ajStrDel(&karray[i]);
++ AJFREE(karray);
++ AJFREE(parray);
++
++ page = ajBtreeCacheRead(cache,blockno);
++
++ return page;
++}
++
++
++
++
+ /* @func ajBtreeIdNew *********************************************
+ **
+ ** Constructor for index bucket ID informationn
+@@ -1617,6 +1803,36 @@
+
+
+
++/* @funcstatic btreeNodeIsFullSec *****************************************
++**
++** Tests whether a secondary node is full of keys
++**
++** @param [r] cache [const AjPBtcache] cache
++** @param [u] page [AjPBtpage] original page
++**
++** @return [AjBool] true if full
++** @@
++******************************************************************************/
++
++static AjBool btreeNodeIsFullSec(const AjPBtcache cache, AjPBtpage page)
++{
++ unsigned char *buf = NULL;
++ ajint nkeys = 0;
++
++ /* ajDebug("In btreeNodeIsFull\n"); */
++
++ buf = page->buf;
++ GBT_NKEYS(buf,&nkeys);
++
++ if(nkeys == cache->sorder - 1)
++ return ajTrue;
++
++ return ajFalse;
++}
++
++
++
++
+ /* @funcstatic btreeInsertNonFull *****************************************
+ **
+ ** Insert a key into a non-full node
+@@ -2289,6 +2505,25 @@
+
+
+
++/* @funcstatic btreeNumIdCompare *******************************************
++**
++** Comparison function for ajListSort
++**
++** @param [r] a [const void*] ID 1
++** @param [r] b [const void*] ID 2
++**
++** @return [ajint] 0 = bases match
++** @@
++******************************************************************************/
++
++static ajint btreeNumIdCompare(const void *a, const void *b)
++{
++ return (*(AjPBtNumId*)a)->offset - (*(AjPBtNumId*)b)->offset;
++}
++
++
++
++
+ /* @funcstatic btreeWriteNode *******************************************
+ **
+ ** Write an internal node
+@@ -2999,8 +3234,7 @@
+ rno = totalkeys - lno;
+
+ maxnperbucket = nperbucket >> 1;
+- if(!maxnperbucket)
+- ++maxnperbucket;
++ ++maxnperbucket;
+
+ cbucket = btreeBucketNew(maxnperbucket);
+
+@@ -5729,7 +5963,17 @@
+ {
+ bentries = buckets[i]->Nentries;
+ for(j=0;j<bentries;++j)
+- ajListPush(list,(void *)buckets[i]->Ids[j]);
++ {
++ if(!buckets[i]->Ids[j]->dups)
++ ajListPush(list,(void *)buckets[i]->Ids[j]);
++ else
++ {
++ ajBtreeHybLeafList(cache,buckets[i]->Ids[j]->offset,
++ buckets[i]->Ids[j]->id,list);
++ ajBtreeIdDel(&buckets[i]->Ids[j]);
++ }
++ }
++
+ AJFREE(buckets[i]->keylen);
+ AJFREE(buckets[i]->Ids);
+ AJFREE(buckets[i]);
+@@ -6273,6 +6517,7 @@
+ **
+ ** @param [r] filename [const char*] file name
+ ** @param [r] indexdir [const char*] index file directory
++** @param [r] directory [const char*] file directory
+ ** @param [w] seqfiles [AjPStr**] sequence file names
+ ** @param [w] reffiles [AjPStr**] reference file names (if any)
+
+@@ -6282,6 +6527,7 @@
+ ******************************************************************************/
+
+ ajint ajBtreeReadEntries(const char *filename, const char *indexdir,
++ const char *directory,
+ AjPStr **seqfiles, AjPStr **reffiles)
+ {
+ AjPStr line = NULL;
+@@ -6292,6 +6538,8 @@
+
+ AjPStr seqname = NULL;
+ AjPStr refname = NULL;
++ AjPStr tseqname = NULL;
++ AjPStr trefname = NULL;
+
+ AjPFile inf = NULL;
+ char p;
+@@ -6303,6 +6551,9 @@
+ line = ajStrNew();
+ list = ajListNew();
+ reflist = ajListNew();
++
++ tseqname = ajStrNew();
++ trefname = ajStrNew();
+
+ fn = ajStrNew();
+ ajFmtPrintS(&fn,"%s/%s",indexdir,filename);
+@@ -6329,7 +6580,8 @@
+ while(ajFileReadLine(inf, &line))
+ {
+ seqname = ajStrNew();
+- ajFmtScanS(line,"%S",&seqname);
++ ajFmtScanS(line,"%S",&tseqname);
++ ajFmtPrintS(&seqname,"%s/%S",directory,tseqname);
+ ajListPushApp(list,(void *)seqname);
+ }
+
+@@ -6342,7 +6594,9 @@
+ {
+ seqname = ajStrNew();
+ refname = ajStrNew();
+- ajFmtScanS(line,"%S%S",&seqname,&refname);
++ ajFmtScanS(line,"%S%S",&tseqname,&trefname);
++ ajFmtPrintS(&seqname,"%s/%S",directory,tseqname);
++ ajFmtPrintS(&refname,"%s/%S",directory,trefname);
+ ajListPushApp(list,(void *)seqname);
+ ajListPushApp(reflist,(void *)refname);
+ }
+@@ -6357,6 +6611,11 @@
+ ajListDel(&reflist);
+ ajStrDel(&line);
+ ajStrDel(&fn);
++
++ ajStrDel(&tseqname);
++ ajStrDel(&trefname);
++
++
+ ajFileClose(&inf);
+
+ return entries;
+@@ -6967,7 +7226,7 @@
+ pripage = btreeCacheLocate(cache,0L);
+ pripage->dirty = BT_LOCK;
+
+- ajDebug("Created secondary tree at block %d\n",(ajint)secrootpage);
++ /* ajDebug("Created 2ry tree at block %d\n",(ajint)secrootpage); */
+ }
+ else
+ {
+@@ -7776,8 +8035,7 @@
+ rno = totalkeys - lno;
+
+ maxnperbucket = nperbucket >> 1;
+- if(!maxnperbucket)
+- ++maxnperbucket;
++ ++maxnperbucket;
+
+ cbucket = btreePriBucketNew(maxnperbucket);
+
+@@ -8230,11 +8488,13 @@
+ {
+ FILE *fp;
+ AjPBtcache cache = NULL;
++#if 0
+ #if defined (HAVE64) && !defined(_OSF_SOURCE) && !defined(_AIX) && !defined(__hpux) && !defined(__ppc__) && !defined(__FreeBSD__)
+ struct stat64 buf;
+ #else
+ struct stat buf;
+ #endif
++#endif
+ ajlong filelen = 0L;
+
+ AjPStr fn = NULL;
+@@ -8246,13 +8506,18 @@
+ if(!fp)
+ return NULL;
+
+-
++ /* Commented out pending database updating */
++#if 0
++ if(strcmp(mode,"r"))
++ {
+ #if defined (HAVE64) && !defined(_OSF_SOURCE) && !defined(_AIX) && !defined(__hpux) && !defined(__ppc__) && !defined(__FreeBSD__)
+- if(!stat64(file, &buf))
++ if(!stat64(fn->Ptr, &buf))
+ #else
+- if(!stat(file, &buf))
++ if(!stat(fn->Ptr, &buf))
++#endif
++ filelen = buf.st_size;
++ }
+ #endif
+- filelen = buf.st_size;
+
+ AJNEW0(cache);
+
+@@ -8283,6 +8548,12 @@
+ cache->snperbucket = sfill;
+ cache->count = count;
+ cache->kwlimit = kwlimit;
++
++ cache->bmem = NULL;
++ cache->tmem = NULL;
++
++ cache->bsmem = NULL;
++ cache->tsmem = NULL;
+
+ ajStrDel(&fn);
+
+@@ -8456,8 +8727,7 @@
+ rno = totalkeys - lno;
+
+ maxnperbucket = nperbucket >> 1;
+- if(!maxnperbucket)
+- ++maxnperbucket;
++ ++maxnperbucket;
+
+ cbucket = btreeSecBucketNew(maxnperbucket);
+
+@@ -9070,7 +9340,7 @@
+ if(!cache->slevel)
+ return root;
+
+- ret = btreeFindINode(cache,root,key);
++ ret = btreeSecFindINode(cache,root,key);
+
+ return ret;
+ }
+@@ -9694,7 +9964,7 @@
+
+ /* ajDebug("In btreeInsertKeySec\n"); */
+
+- if(!btreeNodeIsFull(cache,page))
++ if(!btreeNodeIsFullSec(cache,page))
+ {
+ btreeInsertNonFullSec(cache,page,key,less,greater);
+ return;
+@@ -10572,7 +10842,7 @@
+
+ list = ajListNew();
+
+- order = cache->order;
++ order = cache->sorder;
+
+ AJCNEW0(karray,order);
+ AJCNEW0(parray,order);
+@@ -11724,3 +11994,4580 @@
+ return (*(AjPBtId*)a)->dbno -
+ (*(AjPBtId*)b)->dbno;
+ }
++
++
++
++
++
++
++
++
++/* @func ajBtreeHybNew *********************************************
++**
++** Constructor for index bucket ID informationn
++**
++**
++** @return [AjPBtHybrid] Index ID object
++** @@
++******************************************************************************/
++
++AjPBtHybrid ajBtreeHybNew(void)
++{
++ AjPBtHybrid Id = NULL;
++
++ /* ajDebug("In ajBtreeHybNew\n"); */
++
++ AJNEW0(Id);
++ Id->key1 = ajStrNew();
++ Id->dbno = 0;
++ Id->dups = 0;
++ Id->offset = 0L;
++ Id->refoffset = 0L;
++ Id->treeblock = 0L;
++
++ return Id;
++}
++
++
++
++
++/* @func ajBtreeHybDel *********************************************
++**
++** Destructor for index bucket ID information
++**
++** @param [w] thys [AjPBtHybrid*] index ID object
++**
++** @return [void]
++** @@
++******************************************************************************/
++
++void ajBtreeHybDel(AjPBtHybrid *thys)
++{
++ AjPBtHybrid Id = NULL;
++
++ /* ajDebug("In ajBtreeIdDel\n"); */
++
++ if(!thys || !*thys)
++ return;
++ Id = *thys;
++
++ ajStrDel(&Id->key1);
++ AJFREE(Id);
++ *thys = NULL;
++
++ return;
++}
++
++
++
++
++/* @funcstatic btreeAllocPriArray *******************************************
++**
++** Allocate karray and parray arrays for a primary key
++**
++** @param [rw] cache [AjPBtcache] cache
++**
++** @return [AjPBtMem] memory node
++** @@
++******************************************************************************/
++
++static AjPBtMem btreeAllocPriArray(AjPBtcache cache)
++{
++ AjPBtMem node = NULL;
++ ajint i;
++ ajint limit;
++ AjPBtMem p = NULL;
++
++ limit = cache->order;
++ if(!cache->bmem)
++ {
++ AJNEW0(node);
++ cache->bmem = node;
++ cache->tmem = node;
++ node->prev = NULL;
++ node->next = NULL;
++ node->used = ajTrue;
++ AJCNEW0(node->karray,limit);
++ AJCNEW0(node->parray,limit);
++ AJCNEW0(node->overflows,limit);
++ for(i=0;i<limit;++i)
++ node->karray[i] = ajStrNew();
++
++ return node;
++ }
++
++ if(!cache->bmem->used)
++ {
++ cache->bmem->used = ajTrue;
++
++ if(cache->bmem->next)
++ {
++ p = cache->bmem->next;
++
++ cache->tmem->next = cache->bmem;
++ cache->bmem->next = NULL;
++
++ cache->bmem->prev = cache->tmem;
++
++ cache->tmem = cache->bmem;
++
++ cache->bmem = p;
++ cache->bmem->prev = NULL;
++
++ memset(cache->tmem->parray,0,sizeof(ajlong)*limit);
++
++ return cache->tmem;
++ }
++
++ memset(cache->bmem->parray,0,sizeof(ajlong)*limit);
++
++ return cache->bmem;
++ }
++
++
++ AJNEW0(node);
++ node->used = ajTrue;
++ node->next = NULL;
++ node->prev = cache->tmem;
++ cache->tmem->next = node;
++ cache->tmem = node;
++
++ AJCNEW0(node->karray,limit);
++ AJCNEW0(node->parray,limit);
++ AJCNEW0(node->overflows,limit);
++ for(i=0;i<limit;++i)
++ node->karray[i] = ajStrNew();
++
++ return node;
++}
++
++
++
++
++/* @funcstatic btreeDeallocPriArray *******************************************
++**
++** Deallocate karray and parray arrays for a primary key
++**
++** @param [rw] cache [AjPBtcache] cache
++** @param [rw] node [AjPBtMem] node
++**
++** @return [void]
++** @@
++******************************************************************************/
++
++static void btreeDeallocPriArray(AjPBtcache cache, AjPBtMem node)
++{
++ node->used = ajFalse;
++ if(!node->prev)
++ return;
++
++ node->prev->next = node->next;
++ if(node->next)
++ node->next->prev = node->prev;
++ else
++ cache->tmem = node->prev;
++
++ node->next = cache->bmem;
++ cache->bmem->prev = node;
++ cache->bmem = node;
++ node->prev = NULL;
++
++ return;
++}
++
++
++
++
++
++/* @funcstatic btreeAllocSecArray *******************************************
++**
++** Allocate karray and parray arrays for a primary key
++**
++** @param [rw] cache [AjPBtcache] cache
++**
++** @return [AjPBtMem] memory node
++** @@
++******************************************************************************/
++
++static AjPBtMem btreeAllocSecArray(AjPBtcache cache)
++{
++ AjPBtMem node = NULL;
++ ajint i;
++ ajint limit;
++ AjPBtMem p = NULL;
++
++ limit = cache->sorder;
++ if(!cache->bsmem)
++ {
++ AJNEW0(node);
++ cache->bsmem = node;
++ cache->tsmem = node;
++ node->prev = NULL;
++ node->next = NULL;
++ node->used = ajTrue;
++ AJCNEW0(node->karray,limit);
++ AJCNEW0(node->parray,limit);
++ AJCNEW0(node->overflows,limit);
++ for(i=0;i<limit;++i)
++ node->karray[i] = ajStrNew();
++
++ return node;
++ }
++
++ if(!cache->bsmem->used)
++ {
++ cache->bsmem->used = ajTrue;
++
++ if(cache->bsmem->next)
++ {
++ p = cache->bsmem->next;
++
++ cache->tsmem->next = cache->bsmem;
++ cache->bsmem->next = NULL;
++
++ cache->bsmem->prev = cache->tsmem;
++
++ cache->tsmem = cache->bsmem;
++
++ cache->bsmem = p;
++ cache->bsmem->prev = NULL;
++
++ memset(cache->tsmem->parray,0,sizeof(ajlong)*limit);
++ return cache->tsmem;
++ }
++
++ memset(cache->bsmem->parray,0,sizeof(ajlong)*limit);
++ return cache->bsmem;
++ }
++
++ AJNEW0(node);
++ node->used = ajTrue;
++ node->next = NULL;
++ node->prev = cache->tsmem;
++ cache->tsmem->next = node;
++ cache->tsmem = node;
++
++ AJCNEW0(node->karray,limit);
++ AJCNEW0(node->parray,limit);
++ AJCNEW0(node->overflows,limit);
++ for(i=0;i<limit;++i)
++ node->karray[i] = ajStrNew();
++
++ return node;
++}
++
++
++
++
++/* @funcstatic btreeDeallocSecArray *******************************************
++**
++** Deallocate karray and parray arrays for a primary key
++**
++** @param [rw] cache [AjPBtcache] cache
++** @param [rw] node [AjPBtMem] node
++**
++** @return [void]
++** @@
++******************************************************************************/
++
++static void btreeDeallocSecArray(AjPBtcache cache, AjPBtMem node)
++{
++ node->used = ajFalse;
++ if(!node->prev)
++ return;
++
++ node->prev->next = node->next;
++ if(node->next)
++ node->next->prev = node->prev;
++ else
++ cache->tsmem = node->prev;
++
++ node->next = cache->bsmem;
++ cache->bsmem->prev = node;
++ cache->bsmem = node;
++ node->prev = NULL;
++
++ return;
++}
++
++
++
++
++/* @funcstatic btreeAddToHybBucket *******************************************
++**
++** Add an ID to a bucket
++** Only called if there is room in the bucket
++**
++** @param [u] cache [AjPBtcache] cache
++** @param [r] pageno [ajlong] page number of bucket
++** @param [r] id [const AjPBtId] ID info
++**
++** @return [void]
++** @@
++******************************************************************************/
++
++static void btreeAddToHybBucket(AjPBtcache cache, ajlong pageno,
++ const AjPBtHybrid hyb)
++{
++ AjPBucket bucket = NULL;
++ AjPBtId destid = NULL;
++
++ ajint nentries;
++
++ /* ajDebug("In btreeAddToHybBucket\n"); */
++
++ bucket = btreeReadBucket(cache,pageno);
++ nentries = bucket->Nentries;
++
++
++ /* Reading a bucket always gives one extra ID position */
++ bucket->Ids[nentries] = ajBtreeIdNew();
++ destid = bucket->Ids[nentries];
++
++ ajStrAssS(&destid->id,hyb->key1);
++ destid->dbno = hyb->dbno;
++ destid->offset = hyb->offset;
++ destid->refoffset = hyb->refoffset;
++ destid->dups = hyb->dups;
++
++ ++bucket->Nentries;
++
++ btreeWriteBucket(cache,bucket,pageno);
++
++ btreeBucketDel(&bucket);
++
++ return;
++}
++
++
++
++
++/* @func ajBtreeHybFindInsert ***********************************************
++**
++** Find the node that should contain a new key for insertion
++**
++** @param [u] cache [AjPBtcache] cache
++** @param [r] key [const char*] key to search for
++**
++** @return [AjPBtpage] leaf node where item should be inserted
++** @@
++******************************************************************************/
++
++AjPBtpage ajBtreeHybFindInsert(AjPBtcache cache, const char *key)
++{
++ AjPBtpage root = NULL;
++ AjPBtpage ret = NULL;
++
++ /* ajDebug("In ajBtreeHybFindInsert\n"); */
++
++ /* The root node should always be in the cache (BT_LOCKed) */
++ root = btreeCacheLocate(cache,0L);
++
++ if(!root)
++ ajFatal("The PRI root cache page has been unlocked\n");
++
++ if(!cache->level)
++ return root;
++
++ ret = btreeHybFindINode(cache,root,key);
++
++ return ret;
++}
++
++
++
++
++/* @funcstatic btreeHybFindINode *********************************************
++**
++** Recursive search for insert node
++**
++** @param [u] cache [AjPBtcache] cache
++** @param [u] page [AjPBtpage] page
++** @param [r] item [const char*] key to search for
++**
++** @return [AjPBtpage] leaf node where item should be inserted
++** @@
++******************************************************************************/
++
++static AjPBtpage btreeHybFindINode(AjPBtcache cache, AjPBtpage page,
++ const char *item)
++{
++ AjPBtpage ret = NULL;
++ AjPBtpage pg = NULL;
++
++ unsigned char *buf = NULL;
++ ajint status = 0;
++ ajint ival = 0;
++
++ /* ajDebug("In btreeHybFindINode\n"); */
++
++ ret = page;
++ buf = page->buf;
++ GBT_NODETYPE(buf,&ival);
++ if(ival != BT_LEAF)
++ {
++ status = ret->dirty;
++ ret->dirty = BT_LOCK; /* Lock in case of lots of overflow pages */
++ pg = btreeHybPageFromKey(cache,buf,item);
++ ret->dirty = status;
++ ret = btreeHybFindINode(cache,pg,item);
++ }
++
++ return ret;
++}
++
++
++
++
++/* @funcstatic btreeHybPageFromKey *******************************************
++**
++** Return next lower index page given a key
++**
++** @param [u] cache [AjPBtcache] cache
++** @param [u] buf [unsigned char *] page buffer
++** @param [r] key [const char *] key to search for
++**
++** @return [AjPBtpage] pointer to a page
++** @@
++******************************************************************************/
++
++static AjPBtpage btreeHybPageFromKey(AjPBtcache cache, unsigned char *buf,
++ const char *key)
++{
++ unsigned char *rootbuf = NULL;
++ ajint nkeys = 0;
++ ajint order = 0;
++ ajint i;
++
++ ajlong blockno = 0L;
++ AjPStr *karray = NULL;
++ ajlong *parray = NULL;
++ AjPBtpage page = NULL;
++ AjPBtMem arrays = NULL;
++
++ /* ajDebug("In btreeHybPageFromKey\n"); */
++
++ rootbuf = buf;
++
++
++ GBT_NKEYS(rootbuf,&nkeys);
++ order = cache->order;
++
++ arrays = btreeAllocPriArray(cache);
++ karray = arrays->karray;
++ parray = arrays->parray;
++
++ btreeGetKeys(cache,rootbuf,&karray,&parray);
++ i = 0;
++ while(i!=nkeys && strcmp(key,karray[i]->Ptr)>=0)
++ ++i;
++ if(i==nkeys)
++ {
++ if(strcmp(key,karray[i-1]->Ptr)<0)
++ blockno = parray[i-1];
++ else
++ blockno = parray[i];
++ }
++ else
++ blockno = parray[i];
++
++ btreeDeallocPriArray(cache,arrays);
++
++ page = ajBtreeCacheRead(cache,blockno);
++
++ return page;
++}
++
++
++
++
++/* @funcstatic btreeHybInsertShift ********************************************
++**
++** Rebalance buckets on insertion
++**
++** @param [u] cache [AjPBtcache] cache
++** @param [u] retpage [AjPBtpage*] page
++** @param [r] key [const char *] key
++**
++** @return [ajlong] bucket block or 0L if shift not posible
++** @@
++******************************************************************************/
++
++static ajlong btreeHybInsertShift(AjPBtcache cache, AjPBtpage *retpage,
++ const char *key)
++{
++ unsigned char *tbuf = NULL;
++ unsigned char *pbuf = NULL;
++ unsigned char *sbuf = NULL;
++
++ AjPBtpage ppage = NULL;
++ AjPBtpage spage = NULL;
++ AjPBtpage tpage = NULL;
++
++ ajint tkeys = 0;
++ ajint pkeys = 0;
++ ajint skeys = 0;
++ ajint order = 0;
++
++ ajint i;
++ ajint n;
++
++ ajlong parent = 0L;
++ ajlong blockno = 0L;
++
++ AjPStr *kTarray = NULL;
++ AjPStr *kParray = NULL;
++ AjPStr *kSarray = NULL;
++ ajlong *pTarray = NULL;
++ ajlong *pParray = NULL;
++ ajlong *pSarray = NULL;
++
++ AjPStr *karray = NULL;
++ ajlong *parray = NULL;
++
++ ajint ppos = 0;
++ ajint pkeypos = 0;
++ ajint minsize = 0;
++
++ AjPBtMem arrays1 = NULL;
++ AjPBtMem arrays2 = NULL;
++ AjPBtMem arrays3 = NULL;
++
++ /* ajDebug("In btreeHybInsertShift\n"); */
++
++
++ tpage = *retpage;
++
++ tbuf = tpage->buf;
++
++ GBT_PREV(tbuf,&parent);
++ GBT_NKEYS(tbuf,&tkeys);
++
++ order = cache->order;
++ minsize = order / 2;
++ if(order % 2)
++ ++minsize;
++
++ if(tkeys <= minsize)
++ return 0L;
++
++ ppage = ajBtreeCacheRead(cache,parent);
++
++ pbuf = ppage->buf;
++ GBT_NKEYS(pbuf,&pkeys);
++
++
++ arrays1 = btreeAllocPriArray(cache);
++ kParray = arrays1->karray;
++ pParray = arrays1->parray;
++
++ arrays2 = btreeAllocPriArray(cache);
++ kSarray = arrays2->karray;
++ pSarray = arrays2->parray;
++
++ arrays3 = btreeAllocPriArray(cache);
++ kTarray = arrays3->karray;
++ pTarray = arrays3->parray;
++
++
++ btreeGetKeys(cache,pbuf,&kParray,&pParray);
++
++ i=0;
++ while(i!=pkeys && strcmp(key,kParray[i]->Ptr)>=0)
++ ++i;
++ pkeypos = i;
++
++ if(i==pkeys)
++ {
++ if(strcmp(key,kParray[i-1]->Ptr)<0)
++ ppos = i-1;
++ else
++ ppos = i;
++ }
++ else
++ ppos = i;
++
++
++ if(ppos) /* There is another leaf to the left */
++ {
++ spage = ajBtreeCacheRead(cache,pParray[ppos-1]);
++ sbuf = spage->buf;
++ GBT_NKEYS(sbuf,&skeys);
++ }
++
++ if(i && skeys != order-1) /* There is space in the left leaf */
++ {
++ /* ajDebug("Left shift\n"); */
++ btreeGetKeys(cache,tbuf,&kTarray,&pTarray);
++ if(skeys)
++ btreeGetKeys(cache,sbuf,&kSarray,&pSarray);
++
++ i = 0;
++ while(pParray[i] != tpage->pageno)
++ ++i;
++ --i;
++
++ pkeypos = i;
++
++ ajStrAssS(&kSarray[skeys],kParray[pkeypos]);
++ pSarray[skeys+1] = pTarray[0];
++ ++skeys;
++ --tkeys;
++ ajStrAssS(&kParray[pkeypos],kTarray[0]);
++ for(i=0;i<tkeys;++i)
++ {
++ ajStrAssS(&kTarray[i],kTarray[i+1]);
++ pTarray[i] = pTarray[i+1];
++ }
++ pTarray[i] = pTarray[i+1];
++ pTarray[i+1] = 0L;
++
++ btreeWriteNode(cache,spage,kSarray,pSarray,skeys);
++ btreeWriteNode(cache,tpage,kTarray,pTarray,tkeys);
++ btreeWriteNode(cache,ppage,kParray,pParray,pkeys);
++ if(!ppage->pageno)
++ ppage->dirty = BT_LOCK;
++
++ i = 0;
++ while(i!=pkeys && strcmp(key,kParray[i]->Ptr)>=0)
++ ++i;
++ if(i==pkeys)
++ {
++ if(strcmp(key,kParray[i-1]->Ptr)<0)
++ blockno = pParray[i-1];
++ else
++ blockno = pParray[i];
++ }
++ else
++ blockno = pParray[i];
++
++ if(blockno == spage->pageno)
++ {
++ *retpage = spage;
++ karray = kSarray;
++ parray = pSarray;
++ n = skeys;
++ }
++ else
++ {
++ karray = kTarray;
++ parray = pTarray;
++ n = tkeys;
++ }
++
++
++ i = 0;
++ while(i!=n && strcmp(key,karray[i]->Ptr)>=0)
++ ++i;
++ if(i==n)
++ {
++ if(strcmp(key,karray[i-1]->Ptr)<0)
++ blockno = parray[i-1];
++ else
++ blockno = parray[i];
++ }
++ else
++ blockno = parray[i];
++
++ btreeDeallocPriArray(cache,arrays1);
++ btreeDeallocPriArray(cache,arrays2);
++ btreeDeallocPriArray(cache,arrays3);
++
++ /* ajDebug("... returns blockno (a) %Ld\n",blockno); */
++
++ return blockno;
++ }
++
++
++ if(ppos != pkeys) /* There is a right node */
++ {
++ spage = ajBtreeCacheRead(cache,pParray[ppos+1]);
++ sbuf = spage->buf;
++ GBT_NKEYS(sbuf,&skeys);
++ }
++
++
++ /* Space in the right leaf */
++ if(ppos != pkeys && skeys != order-1)
++ {
++ /* ajDebug("Right shift\n"); */
++ btreeGetKeys(cache,tbuf,&kTarray,&pTarray);
++ btreeGetKeys(cache,sbuf,&kSarray,&pSarray);
++
++ i = 0;
++ while(pParray[i] != tpage->pageno)
++ ++i;
++ pkeypos = i;
++
++ pSarray[skeys+1] = pSarray[skeys];
++ for(i=skeys-1;i>-1;--i)
++ {
++ ajStrAssS(&kSarray[i+1],kSarray[i]);
++ pSarray[i+1] = pSarray[i];
++ }
++ ajStrAssS(&kSarray[0],kParray[pkeypos]);
++ pSarray[0] = pTarray[tkeys];
++ ajStrAssS(&kParray[pkeypos],kTarray[tkeys-1]);
++ ++skeys;
++ --tkeys;
++ pTarray[tkeys+1] = 0L;
++
++ btreeWriteNode(cache,spage,kSarray,pSarray,skeys);
++ btreeWriteNode(cache,tpage,kTarray,pTarray,tkeys);
++ btreeWriteNode(cache,ppage,kParray,pParray,pkeys);
++ if(!ppage->pageno)
++ ppage->dirty = BT_LOCK;
++
++ i = 0;
++ while(i!=pkeys && strcmp(key,kParray[i]->Ptr)>=0)
++ ++i;
++ if(i==pkeys)
++ {
++ if(strcmp(key,kParray[i-1]->Ptr)<0)
++ blockno = pParray[i-1];
++ else
++ blockno = pParray[i];
++ }
++ else
++ blockno = pParray[i];
++
++ if(blockno == spage->pageno)
++ {
++ *retpage = spage;
++ karray = kSarray;
++ parray = pSarray;
++ n = skeys;
++ }
++ else
++ {
++ karray = kTarray;
++ parray = pTarray;
++ n = tkeys;
++ }
++
++ i = 0;
++ while(i!=n && strcmp(key,karray[i]->Ptr)>=0)
++ ++i;
++ if(i==n)
++ {
++ if(strcmp(key,karray[i-1]->Ptr)<0)
++ blockno = parray[i-1];
++ else
++ blockno = parray[i];
++ }
++ else
++ blockno = parray[i];
++
++ btreeDeallocPriArray(cache,arrays1);
++ btreeDeallocPriArray(cache,arrays2);
++ btreeDeallocPriArray(cache,arrays3);
++
++ /* ajDebug("... returns blockno (b) %Ld\n",blockno); */
++
++ return blockno;
++ }
++
++
++ btreeDeallocPriArray(cache,arrays1);
++ btreeDeallocPriArray(cache,arrays2);
++ btreeDeallocPriArray(cache,arrays3);
++
++ /* ajDebug("... returns 0L\n"); */
++
++ return 0L;
++}
++
++
++
++
++/* @funcstatic btreeHybReorderBuckets *****************************************
++**
++** Re-order leaf buckets
++** Must only be called if one of the buckets is full
++**
++** @param [u] cache [AjPBtcache] cache
++** @param [u] leaf [AjPBtpage] leaf page
++**
++** @return [AjBool] true if reorder was successful i.e. leaf not full
++** @@
++******************************************************************************/
++
++static AjBool btreeHybReorderBuckets(AjPBtcache cache, AjPBtpage leaf)
++{
++ ajint nkeys = 0;
++ unsigned char *lbuf = NULL;
++ AjPBucket *buckets = NULL;
++ AjPStr *keys = NULL;
++ ajlong *ptrs = NULL;
++ ajlong *overflows = NULL;
++ AjPBtMem arrays = NULL;
++
++ ajint i = 0;
++ ajint j = 0;
++
++ ajint order;
++ ajint bentries = 0;
++ ajint totalkeys = 0;
++ ajint nperbucket = 0;
++ ajint maxnperbucket = 0;
++ ajint count = 0;
++ ajint totkeylen = 0;
++ ajint keylimit = 0;
++ ajint bucketn = 0;
++ ajint bucketlimit = 0;
++ ajint nodetype = 0;
++
++ AjPList idlist = NULL;
++ ajint dirtysave = 0;
++ AjPBtId bid = NULL;
++ AjPBucket cbucket = NULL;
++ AjPBtId cid = NULL;
++
++ ajint v = 0;
++
++ /* ajDebug("In btreeHybReorderBuckets\n"); */
++
++ dirtysave = leaf->dirty;
++
++ leaf->dirty = BT_LOCK;
++ lbuf = leaf->buf;
++
++ GBT_NODETYPE(lbuf,&nodetype);
++
++ order = cache->order;
++ nperbucket = cache->nperbucket;
++
++
++ /* Read keys/ptrs */
++ arrays = btreeAllocPriArray(cache);
++ keys = arrays->karray;
++ ptrs = arrays->parray;
++ overflows = arrays->overflows;
++
++ btreeGetKeys(cache,lbuf,&keys,&ptrs);
++
++ GBT_NKEYS(lbuf,&nkeys);
++
++
++ if(!nkeys)
++ ajFatal("BucketReorder: Attempt to reorder empty leaf");
++
++ for(i=0;i<nkeys;++i)
++ totalkeys += btreeNumInBucket(cache,ptrs[i]);
++ totalkeys += btreeNumInBucket(cache,ptrs[i]);
++
++ /* Set the number of entries per bucket to approximately half full */
++ maxnperbucket = nperbucket >> 1;
++
++ if(!maxnperbucket)
++ ++maxnperbucket;
++
++ /* Work out the number of new buckets needed */
++ bucketn = (totalkeys / maxnperbucket);
++ if(totalkeys % maxnperbucket)
++ ++bucketn;
++
++ if(bucketn > order)
++ {
++ btreeDeallocPriArray(cache,arrays);
++
++ leaf->dirty = dirtysave;
++ return ajFalse;
++ }
++
++
++ /* Read buckets */
++ AJCNEW0(buckets,nkeys+1);
++ keylimit = nkeys + 1;
++
++ for(i=0;i<keylimit;++i)
++ buckets[i] = btreeReadBucket(cache,ptrs[i]);
++
++
++ /* Read IDs from all buckets and push to list and sort (increasing id) */
++ idlist = ajListNew();
++
++ for(i=0;i<keylimit;++i)
++ {
++ overflows[i] = buckets[i]->Overflow;
++ bentries = buckets[i]->Nentries;
++ for(j=0;j<bentries;++j)
++ ajListPush(idlist,(void *)buckets[i]->Ids[j]);
++
++ AJFREE(buckets[i]->keylen);
++ AJFREE(buckets[i]->Ids);
++ AJFREE(buckets[i]);
++ }
++ ajListSort(idlist,btreeIdCompare);
++ AJFREE(buckets);
++
++ cbucket = btreeBucketNew(maxnperbucket);
++ bucketlimit = bucketn - 1;
++
++ for(i=0;i<bucketlimit;++i)
++ {
++ cbucket->Overflow = overflows[i];
++ cbucket->Nentries = 0;
++
++ count = 0;
++ while(count!=maxnperbucket)
++ {
++ ajListPop(idlist,(void **)&bid);
++
++ cid = cbucket->Ids[count];
++ ajStrAssS(&cid->id,bid->id);
++ cid->dbno = bid->dbno;
++ cid->dups = bid->dups;
++ cid->offset = bid->offset;
++ cid->refoffset = bid->refoffset;
++
++ cbucket->keylen[count] = BT_BUCKIDLEN(bid->id);
++ ++cbucket->Nentries;
++ ++count;
++ ajBtreeIdDel(&bid);
++ }
++
++
++ ajListPeek(idlist,(void **)&bid);
++ ajStrAssS(&keys[i],bid->id);
++
++ totkeylen += ajStrLen(bid->id);
++
++ if(!ptrs[i])
++ ptrs[i] = cache->totsize;
++ btreeWriteBucket(cache,cbucket,ptrs[i]);
++ }
++
++
++ /* Deal with greater-than bucket */
++
++ cbucket->Overflow = overflows[i];
++ cbucket->Nentries = 0;
++
++ count = 0;
++ while(ajListPop(idlist,(void **)&bid))
++ {
++ cid = cbucket->Ids[count];
++ ajStrAssS(&cid->id,bid->id);
++ cid->dbno = bid->dbno;
++ cid->dups = bid->dups;
++ cid->offset = bid->offset;
++ cid->refoffset = bid->refoffset;
++
++ ++cbucket->Nentries;
++ ++count;
++ ajBtreeIdDel(&bid);
++ }
++
++
++ if(!ptrs[i])
++ ptrs[i] = cache->totsize;
++ btreeWriteBucket(cache,cbucket,ptrs[i]);
++
++ cbucket->Nentries = maxnperbucket;
++ btreeBucketDel(&cbucket);
++
++ /* Now write out a modified leaf with new keys/ptrs */
++
++ nkeys = bucketn - 1;
++ v = nkeys;
++ SBT_NKEYS(lbuf,v);
++ v = totkeylen;
++ SBT_TOTLEN(lbuf,v);
++
++ btreeWriteNode(cache,leaf,keys,ptrs,nkeys);
++
++ leaf->dirty = BT_DIRTY;
++ if(nodetype == BT_ROOT)
++ leaf->dirty = BT_LOCK;
++
++ btreeDeallocPriArray(cache,arrays);
++
++ btreeBucketDel(&cbucket);
++ ajListDel(&idlist);
++
++ return ajTrue;
++}
++
++
++
++
++/* @funcstatic btreeHybSplitLeaf *********************************************
++**
++** Split a leaf and propagate up if necessary
++**
++** @param [u] cache [AjPBtcache] cache
++** @param [u] spage [AjPBtpage] page
++**
++** @return [AjPBtpage] pointer to a parent page
++** @@
++******************************************************************************/
++
++static AjPBtpage btreeHybSplitLeaf(AjPBtcache cache, AjPBtpage spage)
++{
++ ajint nkeys = 0;
++ ajint order = 0;
++ ajint totalkeys = 0;
++ ajint bentries = 0;
++ ajint keylimit = 0;
++ ajint nodetype = 0;
++
++ ajint rootnodetype = 0;
++
++ ajint i;
++ ajint j;
++
++ AjPBtpage lpage = NULL;
++ AjPBtpage rpage = NULL;
++ AjPBtpage page = NULL;
++
++ AjPStr mediankey = NULL;
++ ajlong mediangtr = 0L;
++ ajlong medianless = 0L;
++
++
++ AjPBtId bid = NULL;
++ AjPBtId cid = NULL;
++
++ unsigned char *buf = NULL;
++ unsigned char *lbuf = NULL;
++ unsigned char *rbuf = NULL;
++
++ AjPList idlist = NULL;
++
++ AjPBucket *buckets = NULL;
++ AjPBucket cbucket = NULL;
++
++ AjPStr *karray = NULL;
++ ajlong *parray = NULL;
++ AjPBtMem arrays = NULL;
++
++ ajint keypos = 0;
++ ajint lno = 0;
++ ajint rno = 0;
++
++ ajint bucketlimit = 0;
++ ajint maxnperbucket = 0;
++ ajint nperbucket = 0;
++ ajint bucketn = 0;
++ ajint count = 0;
++ ajint totkeylen = 0;
++
++ ajlong lblockno = 0L;
++ ajlong rblockno = 0L;
++ ajlong prev = 0L;
++ ajlong overflow = 0L;
++ ajlong prevsave = 0L;
++
++ ajlong zero = 0L;
++ ajlong join = 0L;
++
++ ajlong lv = 0L;
++ ajint v = 0;
++
++ /* ajDebug("In btreeHybSplitLeaf\n"); */
++
++ order = cache->order;
++ nperbucket = cache->nperbucket;
++
++ mediankey = ajStrNew();
++
++ arrays = btreeAllocPriArray(cache);
++ karray = arrays->karray;
++ parray = arrays->parray;
++
++
++ buf = spage->buf;
++ lbuf = buf;
++
++ GBT_NKEYS(buf,&nkeys);
++
++ for(i=nkeys+1; i<order; ++i)
++ parray[i] = 0L;
++
++ GBT_NODETYPE(buf,&rootnodetype);
++
++ if(rootnodetype == BT_ROOT)
++ {
++ /* ajDebug("Splitting root node\n"); */
++ lblockno = cache->totsize;
++ lpage = ajBtreeCacheWrite(cache,lblockno);
++ lpage->pageno = cache->totsize;
++ cache->totsize += cache->pagesize;
++ lbuf = lpage->buf;
++ lv = prev;
++ SBT_PREV(lbuf,lv);
++ }
++ else
++ {
++ lblockno = spage->pageno;
++ lpage = spage;
++ }
++
++ lpage->dirty = BT_LOCK;
++
++
++ rblockno = cache->totsize;
++ rpage = ajBtreeCacheWrite(cache,rblockno);
++ rpage->pageno = cache->totsize;
++ rpage->dirty = BT_LOCK;
++ cache->totsize += cache->pagesize;
++ rbuf = rpage->buf;
++
++
++ if(rootnodetype == BT_ROOT)
++ {
++ lv = zero;
++ SBT_RIGHT(rbuf,lv);
++ lv = zero;
++ SBT_LEFT(lbuf,lv);
++ }
++ else
++ {
++ GBT_RIGHT(lbuf,&join);
++ lv = join;
++ SBT_RIGHT(rbuf,lv);
++ }
++ lv = lblockno;
++ SBT_LEFT(rbuf,lv);
++ lv = rblockno;
++ SBT_RIGHT(lbuf,lv);
++
++
++ btreeGetKeys(cache,buf,&karray,&parray);
++
++
++ keylimit = nkeys+1;
++ AJCNEW0(buckets,keylimit);
++ for(i=0;i<keylimit;++i)
++ buckets[i] = btreeReadBucket(cache,parray[i]);
++
++ idlist = ajListNew();
++ for(i=0;i<keylimit;++i)
++ {
++ bentries = buckets[i]->Nentries;
++ for(j=0;j<bentries;++j)
++ ajListPush(idlist,(void *)buckets[i]->Ids[j]);
++ AJFREE(buckets[i]->keylen);
++ AJFREE(buckets[i]->Ids);
++ AJFREE(buckets[i]);
++ }
++ ajListSort(idlist,btreeIdCompare);
++ AJFREE(buckets);
++
++
++ totalkeys = ajListLength(idlist);
++
++ keypos = totalkeys / 2;
++
++ lno = keypos;
++ rno = totalkeys - lno;
++
++ maxnperbucket = nperbucket >> 1;
++ ++maxnperbucket;
++
++ cbucket = btreeBucketNew(maxnperbucket);
++
++ bucketn = lno / maxnperbucket;
++ if(lno % maxnperbucket)
++ ++bucketn;
++ bucketlimit = bucketn - 1;
++
++
++ totkeylen = 0;
++ count = 0;
++ for(i=0;i<bucketlimit;++i)
++ {
++ cbucket->Nentries = 0;
++ for(j=0;j<maxnperbucket;++j)
++ {
++ ajListPop(idlist,(void **)&bid);
++
++ cid = cbucket->Ids[j];
++ ajStrAssS(&cid->id,bid->id);
++ cid->dbno = bid->dbno;
++ cid->dups = bid->dups;
++ cid->offset = bid->offset;
++ cid->refoffset = bid->refoffset;
++
++ cbucket->keylen[j] = BT_BUCKIDLEN(bid->id);
++ ++count;
++ ++cbucket->Nentries;
++ ajBtreeIdDel(&bid);
++ }
++ ajListPeek(idlist,(void **)&bid);
++
++ ajStrAssS(&karray[i],bid->id);
++ totkeylen += ajStrLen(bid->id);
++
++ if(!parray[i])
++ parray[i] = cache->totsize;
++ btreeWriteBucket(cache,cbucket,parray[i]);
++ }
++
++ cbucket->Nentries = 0;
++
++ j = 0;
++ while(count != lno)
++ {
++ ajListPop(idlist,(void **)&bid);
++ cid = cbucket->Ids[j];
++ ++j;
++ ++count;
++
++ ajStrAssS(&cid->id,bid->id);
++ cid->dbno = bid->dbno;
++ cid->dups = bid->dups;
++ cid->offset = bid->offset;
++ cid->refoffset = bid->refoffset;
++
++ ++cbucket->Nentries;
++ ajBtreeIdDel(&bid);
++ }
++
++ if(!parray[i])
++ parray[i] = cache->totsize;
++ btreeWriteBucket(cache,cbucket,parray[i]);
++
++ nkeys = bucketn - 1;
++ v = nkeys;
++ SBT_NKEYS(lbuf,v);
++ v = totkeylen;
++ SBT_TOTLEN(lbuf,v);
++ nodetype = BT_LEAF;
++ v = nodetype;
++ SBT_NODETYPE(lbuf,v);
++ lpage->dirty = BT_DIRTY;
++
++ GBT_PREV(lbuf,&prevsave);
++
++ btreeWriteNode(cache,lpage,karray,parray,nkeys);
++
++ ajListPeek(idlist,(void **)&bid);
++ ajStrAssS(&mediankey,bid->id);
++
++ totkeylen = 0;
++ bucketn = rno / maxnperbucket;
++ if(rno % maxnperbucket)
++ ++bucketn;
++ bucketlimit = bucketn - 1;
++
++ for(i=0;i<bucketlimit;++i)
++ {
++ cbucket->Nentries = 0;
++ for(j=0;j<maxnperbucket;++j)
++ {
++ ajListPop(idlist,(void **)&bid);
++
++ cid = cbucket->Ids[j];
++ ajStrAssS(&cid->id,bid->id);
++ cid->dbno = bid->dbno;
++ cid->dups = bid->dups;
++ cid->offset = bid->offset;
++ cid->refoffset = bid->refoffset;
++
++ cbucket->keylen[j] = BT_BUCKIDLEN(bid->id);
++ ++cbucket->Nentries;
++ ajBtreeIdDel(&bid);
++ }
++
++ ajListPeek(idlist,(void **)&bid);
++ ajStrAssS(&karray[i],bid->id);
++ totkeylen += ajStrLen(bid->id);
++
++ parray[i] = cache->totsize;
++ btreeWriteBucket(cache,cbucket,parray[i]);
++ }
++
++ cbucket->Nentries = 0;
++
++ j = 0;
++ while(ajListPop(idlist,(void**)&bid))
++ {
++ cid = cbucket->Ids[j];
++ ++j;
++
++ ajStrAssS(&cid->id,bid->id);
++ cid->dbno = bid->dbno;
++ cid->dups = bid->dups;
++ cid->offset = bid->offset;
++ cid->refoffset = bid->refoffset;
++
++ ++cbucket->Nentries;
++ ajBtreeIdDel(&bid);
++ }
++
++ parray[i] = cache->totsize;
++ btreeWriteBucket(cache,cbucket,parray[i]);
++
++ nkeys = bucketn - 1;
++
++ v = nkeys;
++ SBT_NKEYS(rbuf,v);
++ v = totkeylen;
++ SBT_TOTLEN(rbuf,v);
++ nodetype = BT_LEAF;
++ v = nodetype;
++ SBT_NODETYPE(rbuf,v);
++ lv = prevsave;
++ SBT_PREV(rbuf,lv);
++ lv = overflow;
++ SBT_OVERFLOW(rbuf,lv);
++
++ btreeWriteNode(cache,rpage,karray,parray,nkeys);
++ rpage->dirty = BT_DIRTY;
++
++ cbucket->Nentries = maxnperbucket;
++ btreeBucketDel(&cbucket);
++ ajListDel(&idlist);
++
++
++
++ medianless = lblockno;
++ mediangtr = rblockno;
++
++
++ if(rootnodetype == BT_ROOT)
++ {
++ ajStrAssS(&karray[0],mediankey);
++ parray[0]=lblockno;
++ parray[1]=rblockno;
++ nkeys = 1;
++ btreeWriteNode(cache,spage,karray,parray,nkeys);
++ spage->dirty = BT_LOCK;
++
++ btreeDeallocPriArray(cache,arrays);
++
++ ajStrDel(&mediankey);
++ ++cache->level;
++ return spage;
++ }
++
++
++ btreeDeallocPriArray(cache,arrays);
++
++ page = ajBtreeCacheRead(cache,prevsave);
++ btreeHybInsertKey(cache,page,mediankey,medianless,mediangtr);
++ ajStrDel(&mediankey);
++
++ page = ajBtreeCacheRead(cache,prevsave);
++
++ return page;
++}
++
++
++
++
++/* @funcstatic btreeHybInsertKey *****************************************
++**
++** Insert a key into a potentially full node
++**
++** @param [u] cache [AjPBtcache] cache
++** @param [u] page [AjPBtpage] original page
++** @param [r] key [const AjPStr] key to insert
++** @param [r] less [ajlong] less-than pointer
++** @param [r] greater [ajlong] greater-than pointer
++**
++** @return [void]
++** @@
++******************************************************************************/
++
++static void btreeHybInsertKey(AjPBtcache cache, AjPBtpage page,
++ const AjPStr key, ajlong less, ajlong greater)
++{
++ unsigned char *lbuf = NULL;
++ unsigned char *rbuf = NULL;
++ unsigned char *tbuf = NULL;
++ AjPStr *karray = NULL;
++ ajlong *parray = NULL;
++ AjPStr *tkarray = NULL;
++ ajlong *tparray = NULL;
++
++ AjPBtMem arrays1 = NULL;
++ AjPBtMem arrays2 = NULL;
++
++ ajint nkeys = 0;
++ ajint order = 0;
++ ajint keypos = 0;
++ ajint rkeyno = 0;
++
++ ajint i = 0;
++ ajint n = 0;
++
++ ajint nodetype = 0;
++ AjPBtpage ipage = NULL;
++ AjPBtpage lpage = NULL;
++ AjPBtpage rpage = NULL;
++ AjPBtpage tpage = NULL;
++
++ ajlong blockno = 0L;
++ ajlong rblockno = 0L;
++ ajlong lblockno = 0L;
++ AjPStr mediankey = NULL;
++ ajlong medianless = 0L;
++ ajlong mediangtr = 0L;
++ ajlong overflow = 0L;
++ ajlong prev = 0L;
++ ajint totlen = 0;
++
++ ajlong lv = 0L;
++ ajint v = 0;
++
++ /* ajDebug("In btreeHybInsertKey\n"); */
++
++ if(!btreeNodeIsFull(cache,page))
++ {
++ btreeInsertNonFull(cache,page,key,less,greater);
++ return;
++ }
++
++ order = cache->order;
++ lbuf = page->buf;
++ GBT_NODETYPE(lbuf,&nodetype);
++ page->dirty = BT_LOCK;
++
++ if(nodetype == BT_ROOT)
++ {
++ arrays1 = btreeAllocPriArray(cache);
++ karray = arrays1->karray;
++ parray = arrays1->parray;
++
++ btreeHybSplitRoot(cache);
++
++ if(page->pageno)
++ page->dirty = BT_DIRTY;
++ btreeGetKeys(cache,lbuf,&karray,&parray);
++
++ if(strcmp(key->Ptr,karray[0]->Ptr)<0)
++ blockno = parray[0];
++ else
++ blockno = parray[1];
++ ipage = ajBtreeCacheRead(cache,blockno);
++ btreeInsertNonFull(cache,ipage,key,less,greater);
++
++ btreeDeallocPriArray(cache,arrays1);
++ return;
++ }
++
++
++ arrays1 = btreeAllocPriArray(cache);
++ karray = arrays1->karray;
++ parray = arrays1->parray;
++
++ arrays2 = btreeAllocPriArray(cache);
++ tkarray = arrays2->karray;
++ tparray = arrays2->parray;
++
++ mediankey = ajStrNew();
++
++ lpage = page;
++ lbuf = lpage->buf;
++
++ btreeGetKeys(cache,lbuf,&karray,&parray);
++
++ GBT_BLOCKNUMBER(lbuf,&lblockno);
++ rblockno = cache->totsize;
++ rpage = ajBtreeCacheWrite(cache,rblockno);
++ rpage->dirty = BT_LOCK;
++ rpage->pageno = cache->totsize;
++ cache->totsize += cache->pagesize;
++ rbuf = rpage->buf;
++ lv = rblockno;
++ SBT_BLOCKNUMBER(rbuf,lv);
++
++
++ GBT_PREV(lbuf,&prev);
++ lv = prev;
++ SBT_PREV(rbuf,lv);
++
++ nkeys = order - 1;
++ keypos = nkeys / 2;
++ if(!(nkeys % 2))
++ --keypos;
++
++ ajStrAssS(&mediankey,karray[keypos]);
++ medianless = lblockno;
++ mediangtr = rblockno;
++
++
++ GBT_NODETYPE(lbuf,&nodetype);
++ v = nodetype;
++ SBT_NODETYPE(rbuf,v);
++ lv = overflow;
++ SBT_OVERFLOW(rbuf,lv);
++
++
++ totlen = 0;
++ for(i=0;i<keypos;++i)
++ {
++ ajStrAssS(&tkarray[i],karray[i]);
++ totlen += ajStrLen(karray[i]);
++ tparray[i] = parray[i];
++ }
++ tparray[i] = parray[i];
++ v = totlen;
++ SBT_TOTLEN(lbuf,v);
++ n = i;
++ v = n;
++ SBT_NKEYS(lbuf,v);
++ btreeWriteNode(cache,lpage,tkarray,tparray,i);
++
++
++
++ for(i=0;i<n+1;++i)
++ {
++ tpage = ajBtreeCacheRead(cache,tparray[i]);
++ tbuf = tpage->buf;
++ lv = lblockno;
++ SBT_PREV(tbuf,lv);
++ tpage->dirty = BT_DIRTY;
++ }
++
++
++ totlen = 0;
++ for(i=keypos+1;i<nkeys;++i)
++ {
++ ajStrAssS(&tkarray[i-(keypos+1)],karray[i]);
++ totlen += ajStrLen(karray[i]);
++ tparray[i-(keypos+1)] = parray[i];
++ }
++ tparray[i-(keypos+1)] = parray[i];
++ v = totlen;
++ SBT_TOTLEN(rbuf,v);
++ rkeyno = (nkeys-keypos) - 1;
++ v = rkeyno;
++ SBT_NKEYS(rbuf,v);
++ rpage->dirty = BT_DIRTY;
++ btreeWriteNode(cache,rpage,tkarray,tparray,rkeyno);
++
++
++ for(i=0;i<rkeyno+1;++i)
++ {
++ tpage = ajBtreeCacheRead(cache,tparray[i]);
++ tbuf = tpage->buf;
++ lv = rblockno;
++ SBT_PREV(tbuf,lv);
++ tpage->dirty = BT_DIRTY;
++ }
++
++
++ ipage = rpage;
++ if(strcmp(key->Ptr,mediankey->Ptr)<0)
++ ipage = lpage;
++
++ btreeInsertNonFull(cache,ipage,key,less,greater);
++
++
++ btreeDeallocPriArray(cache,arrays1);
++ btreeDeallocPriArray(cache,arrays2);
++
++ ipage = ajBtreeCacheRead(cache,prev);
++
++ btreeHybInsertKey(cache,ipage,mediankey,medianless,mediangtr);
++ ajStrDel(&mediankey);
++
++ return;
++}
++
++
++
++
++/* @funcstatic btreeHybSplitRoot *****************************************
++**
++** Split the root node
++**
++** @param [u] cache [AjPBtcache] cache
++**
++** @return [void]
++** @@
++******************************************************************************/
++
++static void btreeHybSplitRoot(AjPBtcache cache)
++{
++ AjPBtpage rootpage = NULL;
++ AjPBtpage rpage = NULL;
++ AjPBtpage lpage = NULL;
++ AjPBtpage tpage = NULL;
++
++ AjPStr *karray = NULL;
++ AjPStr *tkarray = NULL;
++ ajlong *parray = NULL;
++ ajlong *tparray = NULL;
++ AjPBtMem arrays1 = NULL;
++ AjPBtMem arrays2 = NULL;
++
++ ajint order = 0;
++ ajint nkeys = 0;
++ ajint keypos = 0;
++
++ ajlong rblockno = 0L;
++ ajlong lblockno = 0L;
++
++ AjPStr key = NULL;
++ ajint i;
++
++ unsigned char *rootbuf = NULL;
++ unsigned char *rbuf = NULL;
++ unsigned char *lbuf = NULL;
++ unsigned char *tbuf = NULL;
++
++ ajint nodetype = 0;
++ ajlong overflow = 0L;
++ ajlong zero = 0L;
++ ajint totlen = 0;
++ ajint rkeyno = 0;
++ ajint n = 0;
++
++ ajlong lv = 0L;
++ ajint v = 0;
++
++
++ /* ajDebug("In btreeHybSplitRoot\n"); */
++
++ order = cache->order;
++
++ arrays1 = btreeAllocPriArray(cache);
++ karray = arrays1->karray;
++ parray = arrays1->parray;
++
++ arrays2 = btreeAllocPriArray(cache);
++ tkarray = arrays2->karray;
++ tparray = arrays2->parray;
++
++ key = ajStrNew();
++
++ rootpage = btreeCacheLocate(cache,0L);
++ if(!rootpage)
++ ajFatal("Root page has been unlocked 1");
++
++ rootbuf = rootpage->buf;
++
++ nkeys = order - 1;
++
++ keypos = nkeys / 2;
++ if(!(nkeys % 2))
++ --keypos;
++
++
++ rblockno = cache->totsize;
++ rpage = ajBtreeCacheWrite(cache,rblockno);
++ rpage->dirty = BT_LOCK;
++ rpage->pageno = cache->totsize;
++ cache->totsize += cache->pagesize;
++
++ lblockno = cache->totsize;
++ lpage = ajBtreeCacheWrite(cache,lblockno);
++ lpage->pageno = cache->totsize;
++ cache->totsize += cache->pagesize;
++
++ lv = rblockno;
++ SBT_BLOCKNUMBER(rpage->buf,lv);
++ lv = lblockno;
++ SBT_BLOCKNUMBER(lpage->buf,lv);
++
++ if(!cache->level)
++ {
++ lv = zero;
++ SBT_LEFT(lpage->buf,lv);
++ lv = rblockno;
++ SBT_RIGHT(lpage->buf,lv);
++ lv = lblockno;
++ SBT_LEFT(rpage->buf,lv);
++ lv = zero;
++ SBT_RIGHT(rpage->buf,lv);
++ }
++
++ btreeGetKeys(cache,rootbuf,&karray,&parray);
++
++ /* Get key for root node and write new root node */
++ ajStrAssS(&tkarray[0],karray[keypos]);
++ tparray[0] = lblockno;
++ tparray[1] = rblockno;
++
++
++ n = 1;
++ v = n;
++ SBT_NKEYS(rootbuf,v);
++ btreeWriteNode(cache,rootpage,tkarray,tparray,1);
++ rootpage->dirty = BT_LOCK;
++
++ rbuf = rpage->buf;
++ lbuf = lpage->buf;
++
++ if(cache->level)
++ nodetype = BT_INTERNAL;
++ else
++ nodetype = BT_LEAF;
++
++ v = nodetype;
++ SBT_NODETYPE(rbuf,v);
++ v = nodetype;
++ SBT_NODETYPE(lbuf,v);
++ lv = overflow;
++ SBT_OVERFLOW(rbuf,lv);
++ lv = overflow;
++ SBT_PREV(rbuf,lv);
++ lv = overflow;
++ SBT_OVERFLOW(lbuf,lv);
++ lv = overflow;
++ SBT_PREV(lbuf,lv);
++
++ totlen = 0;
++ for(i=0;i<keypos;++i)
++ {
++ ajStrAssS(&tkarray[i],karray[i]);
++ totlen += ajStrLen(karray[i]);
++ tparray[i] = parray[i];
++ }
++ tparray[i] = parray[i];
++ v = totlen;
++ SBT_TOTLEN(lbuf,v);
++ n = i;
++ v = n;
++ SBT_NKEYS(lbuf,v);
++ btreeWriteNode(cache,lpage,tkarray,tparray,i);
++
++ for(i=0;i<n+1;++i)
++ {
++ tpage = ajBtreeCacheRead(cache,tparray[i]);
++ tbuf = tpage->buf;
++ lv = lblockno;
++ SBT_PREV(tbuf,lv);
++ tpage->dirty = BT_DIRTY;
++ }
++
++ totlen = 0;
++ for(i=keypos+1;i<nkeys;++i)
++ {
++ ajStrAssS(&tkarray[i-(keypos+1)],karray[i]);
++ totlen += ajStrLen(karray[i]);
++ tparray[i-(keypos+1)] = parray[i];
++ }
++ tparray[i-(keypos+1)] = parray[i];
++ v = totlen;
++ SBT_TOTLEN(rbuf,v);
++ rkeyno = (nkeys-keypos) - 1;
++ v = rkeyno;
++ SBT_NKEYS(rbuf,v);
++ rpage->dirty = BT_DIRTY;
++ btreeWriteNode(cache,rpage,tkarray,tparray,rkeyno);
++
++ for(i=0;i<rkeyno+1;++i)
++ {
++ tpage = ajBtreeCacheRead(cache,tparray[i]);
++ tbuf = tpage->buf;
++ lv = rblockno;
++ SBT_PREV(tbuf,lv);
++ tpage->dirty = BT_DIRTY;
++ }
++
++
++ btreeDeallocPriArray(cache,arrays1);
++ btreeDeallocPriArray(cache,arrays2);
++
++ ++cache->level;
++
++ ajStrDel(&key);
++
++ return;
++}
++
++
++
++
++/* @func ajBtreeHybInsertId *********************************************
++**
++** Insert an ID structure into the tree
++**
++** @param [u] cache [AjPBtcache] cache
++** @param [r] id [const AjPBtId] Id object
++**
++** @return [void] pointer to a page
++** @@
++******************************************************************************/
++
++void ajBtreeHybInsertId(AjPBtcache cache, AjPBtHybrid hyb)
++{
++ AjPBtpage spage = NULL;
++ AjPBtpage parent = NULL;
++ AjPStr key = NULL;
++ const char *ckey = NULL;
++ AjPBucket lbucket = NULL;
++ AjPBucket rbucket = NULL;
++ AjPBucket bucket = NULL;
++ ajlong lblockno = 0L;
++ ajlong rblockno = 0L;
++ ajlong blockno = 0L;
++ ajlong shift = 0L;
++
++ ajint nkeys = 0;
++ ajint order = 0;
++
++ ajint nodetype = 0;
++ ajint nentries = 0;
++
++ AjPStr *karray = NULL;
++ ajlong *parray = NULL;
++ AjPBtMem arrays = NULL;
++ AjBool found = ajFalse;
++ AjPBtId btid = NULL;
++
++ ajint i;
++ ajint n;
++
++ unsigned char *buf = NULL;
++
++ /* ajDebug("In ajBtreeHybInsertId\n"); */
++
++ key = ajStrNew();
++
++
++ ajStrAssS(&key,hyb->key1);
++ if(!ajStrLen(key))
++ {
++ ajStrDel(&key);
++ return;
++ }
++
++ ckey = ajStrStr(key);
++ spage = ajBtreeHybFindInsert(cache,ckey);
++ buf = spage->buf;
++
++ GBT_NKEYS(buf,&nkeys);
++ GBT_NODETYPE(buf,&nodetype);
++
++ order = cache->order;
++
++ arrays = btreeAllocPriArray(cache);
++ karray = arrays->karray;
++ parray = arrays->parray;
++
++ if(!nkeys)
++ {
++ lbucket = btreeBucketNew(0);
++ rbucket = btreeBucketNew(0);
++
++ lblockno = cache->totsize;
++ btreeWriteBucket(cache,lbucket,lblockno);
++
++ rblockno = cache->totsize;
++ btreeWriteBucket(cache,rbucket,rblockno);
++
++ parray[0] = lblockno;
++ parray[1] = rblockno;
++ ajStrAssS(karray,key);
++
++ btreeWriteNode(cache,spage,karray,parray,1);
++
++ btreeBucketDel(&lbucket);
++ btreeBucketDel(&rbucket);
++
++ btreeAddToHybBucket(cache,rblockno,hyb);
++
++ btreeDeallocPriArray(cache,arrays);
++
++ ajStrDel(&key);
++
++ return;
++ }
++
++
++ /* Search to see whether entry exists */
++
++ btreeGetKeys(cache,buf,&karray,&parray);
++
++ i=0;
++ while(i!=nkeys && strcmp(key->Ptr,karray[i]->Ptr)>=0)
++ ++i;
++ if(i==nkeys)
++ {
++ if(strcmp(key->Ptr,karray[i-1]->Ptr)<0)
++ blockno = parray[i-1];
++ else
++ blockno = parray[i];
++ }
++ else
++ blockno = parray[i];
++
++
++ bucket = btreeReadBucket(cache,blockno);
++
++ nentries = bucket->Nentries;
++
++ found = ajFalse;
++
++ for(i=0;i<nentries;++i)
++ if(!strcmp(hyb->key1->Ptr,bucket->Ids[i]->id->Ptr))
++ {
++ found = ajTrue;
++ break;
++ }
++
++
++ if(found)
++ {
++ btid = bucket->Ids[i];
++
++ btreeHybDupInsert(cache,hyb,btid);
++
++ btreeWriteBucket(cache,bucket,blockno);
++ btreeBucketDel(&bucket);
++
++ btreeDeallocPriArray(cache,arrays);
++
++ ajStrDel(&key);
++
++ return;
++ }
++ else
++ btreeBucketDel(&bucket);
++
++ if(nodetype != BT_ROOT)
++ if((shift = btreeHybInsertShift(cache,&spage,key->Ptr)))
++ blockno = shift;
++
++ buf = spage->buf;
++
++ n = btreeNumInBucket(cache,blockno);
++
++ if(n == cache->nperbucket)
++ {
++ if(btreeHybReorderBuckets(cache,spage))
++ {
++ GBT_NKEYS(buf,&nkeys);
++ btreeGetKeys(cache,buf,&karray,&parray);
++
++ i=0;
++ while(i!=nkeys && strcmp(key->Ptr,karray[i]->Ptr)>=0)
++ ++i;
++
++ if(i==nkeys)
++ {
++ if(strcmp(key->Ptr,karray[i-1]->Ptr)<0)
++ blockno = parray[i-1];
++ else
++ blockno = parray[i];
++ }
++ else
++ blockno = parray[i];
++ }
++ else
++ {
++ parent = btreeHybSplitLeaf(cache,spage);
++ spage = ajBtreeHybFindInsert(cache,ckey);
++ buf = spage->buf;
++
++ btreeGetKeys(cache,buf,&karray,&parray);
++
++ GBT_NKEYS(buf,&nkeys);
++ i=0;
++ while(i!=nkeys && strcmp(key->Ptr,karray[i]->Ptr)>=0)
++ ++i;
++
++ if(i==nkeys)
++ {
++ if(strcmp(key->Ptr,karray[i-1]->Ptr)<0)
++ blockno = parray[i-1];
++ else
++ blockno = parray[i];
++ }
++ else
++ blockno = parray[i];
++
++ }
++ }
++
++
++ btreeAddToHybBucket(cache,blockno,hyb);
++
++ ++cache->count;
++
++ btreeDeallocPriArray(cache,arrays);
++
++ ajStrDel(&key);
++
++ return;
++}
++
++
++
++
++static void btreeHybDupInsert(AjPBtcache cache, AjPBtHybrid hyb,
++ AjPBtId btid)
++{
++ AjPBtpage page;
++ AjPBtpage rpage;
++ ajlong secrootpage = 0L;
++ unsigned char *buf;
++ ajlong right = 0L;
++ AjPBtNumId num = NULL;
++
++ /* ajDebug("In btreeHybDupInsert\n"); */
++
++ if(!btid->dups)
++ {
++ btid->dups = 1;
++ AJNEW0(num);
++
++ num->offset = btid->offset;
++ num->refoffset = btid->refoffset;
++ num->dbno = btid->dbno;
++
++ secrootpage = cache->totsize;
++
++ btid->offset = secrootpage;
++
++ ajBtreeCreateRootNode(cache,secrootpage);
++ cache->secrootblock = secrootpage;
++ page = ajBtreeCacheWrite(cache,secrootpage);
++ page->dirty = BT_DIRTY;
++ ajBtreeCacheSync(cache,secrootpage);
++ page->dirty = BT_LOCK;
++
++ rpage = btreeCacheLocate(cache, 0L);
++ rpage->dirty = BT_LOCK;
++
++ cache->slevel = 0;
++
++ ajBtreeInsertNum(cache,num,page);
++
++ num->offset = hyb->offset;
++ num->refoffset = hyb->refoffset;
++ num->dbno = hyb->dbno;
++
++ ajBtreeInsertNum(cache,num,page);
++ ++btid->dups;
++
++ AJFREE(num);
++
++ return;
++ }
++ else
++ {
++ cache->secrootblock = btid->offset;
++ page = ajBtreeCacheWrite(cache,cache->secrootblock);
++ page->dirty = BT_LOCK;
++ buf = page->buf;
++ GBT_RIGHT(buf,&right);
++ cache->slevel = (ajint) right;
++
++ AJNEW0(num);
++
++ num->offset = hyb->offset;
++ num->refoffset = hyb->refoffset;
++ num->dbno = hyb->dbno;
++
++ ajBtreeInsertNum(cache,num,page);
++
++ AJFREE(num);
++
++ ++btid->dups;
++ }
++
++
++ page->dirty = BT_DIRTY;
++
++ return;
++}
++
++
++
++
++/* @funcstatic btreeGetNumKeys *********************************************
++**
++** Get Keys and Pointers from an internal node
++**
++** @param [u] cache [AjPBtcache] cache
++** @param [u] buf [unsigned char *] page buffer
++** @param [w] keys [ajlong **] keys
++** @param [w] ptrs [ajlong**] ptrs
++**
++** @return [void]
++** @@
++******************************************************************************/
++
++static void btreeGetNumKeys(AjPBtcache cache, unsigned char *buf,
++ ajlong **keys, ajlong **ptrs)
++{
++ ajlong *karray = NULL;
++ ajlong *parray = NULL;
++
++ ajint nkeys = 0;
++ unsigned char *pptr = NULL;
++ ajint i;
++
++ /* ajDebug("In btreeGetNumKeys\n"); */
++
++
++ karray = *keys;
++ parray = *ptrs;
++
++ pptr = PBT_KEYLEN(buf);
++ GBT_NKEYS(buf,&nkeys);
++ if(!nkeys)
++ ajFatal("GetNumKeys: No keys in node");
++
++ for(i=0;i<nkeys;++i)
++ {
++ BT_GETAJLONG(pptr,&karray[i]);
++ pptr += sizeof(ajlong);
++ }
++
++ for(i=0;i<nkeys;++i)
++ {
++ BT_GETAJLONG(pptr,&parray[i]);
++ pptr += sizeof(ajlong);
++ }
++
++ BT_GETAJLONG(pptr,&parray[i]);
++
++ return;
++}
++
++
++
++
++/* @funcstatic btreeWriteNumNode *******************************************
++**
++** Write an internal node
++**
++** @param [u] cache [AjPBtcache] cache
++** @param [u] spage [AjPBtpage] buffer
++** @param [r] keys [AjPStr const *] keys
++** @param [r] ptrs [const ajlong*] page pointers
++** @param [r] nkeys [ajint] number of keys
++
++**
++** @return [void]
++** @@
++******************************************************************************/
++
++static void btreeWriteNumNode(AjPBtcache cache, AjPBtpage spage,
++ const ajlong *keys, const ajlong *ptrs,
++ ajint nkeys)
++{
++ unsigned char *pptr = NULL;
++ unsigned char *buf;
++
++ ajint tnkeys = 0;
++
++ ajlong aspace = 0L;
++ ajlong lv = 0L;
++ ajlong overflow = 0L;
++ ajint i;
++
++
++ /* ajDebug("In btreeWriteNumNode\n"); */
++
++ buf = spage->buf;
++
++ tnkeys = nkeys;
++ SBT_NKEYS(buf,tnkeys);
++
++ pptr = PBT_KEYLEN(buf);
++ aspace = 2 * nkeys * sizeof(ajlong) + sizeof(ajlong);
++ if((pptr+aspace)-buf > cache->pagesize)
++ ajFatal("WriteNumNode: too many keys for available pagesize");
++
++ for(i=0;i<nkeys;++i)
++ {
++ lv = keys[i];
++ BT_SETAJLONG(pptr,lv);
++ pptr += sizeof(ajlong);
++ }
++
++ for(i=0;i<nkeys;++i)
++ {
++ lv = ptrs[i];
++ BT_SETAJLONG(pptr,lv);
++ pptr += sizeof(ajlong);
++ }
++
++ lv = ptrs[i];
++ BT_SETAJLONG(pptr,lv);
++
++ spage->dirty = BT_DIRTY;
++
++ overflow = 0L;
++ SBT_OVERFLOW(buf,overflow);
++
++
++ return;
++}
++
++
++
++
++/* @funcstatic btreeWriteNumBucket *******************************************
++**
++** Write index bucket object to the cache given a disc page number
++**
++** @param [u] cache [AjPBtcache] cache
++** @param [r] bucket [const AjPBucket] bucket
++** @param [r] pageno [ajlong] page number
++**
++** @return [void]
++** @@
++******************************************************************************/
++
++static void btreeWriteNumBucket(AjPBtcache cache, const AjPNumBucket bucket,
++ ajlong pageno)
++{
++ AjPBtpage page = NULL;
++ unsigned char *buf = NULL;
++ unsigned char *pptr = NULL;
++
++ ajint v = 0;
++ ajint i = 0;
++ ajlong lv = 0L;
++ ajint nentries = 0;
++ ajlong overflow = 0L;
++
++ /* ajDebug("In btreeWriteNumBucket\n"); */
++
++ if(pageno == cache->totsize)
++ {
++ page = ajBtreeCacheWrite(cache,pageno);
++ page->pageno = cache->totsize;
++ cache->totsize += cache->pagesize;
++ buf = page->buf;
++ overflow = 0L;
++ lv = overflow;
++ SBT_BUCKOVERFLOW(buf,lv);
++ }
++ else
++ {
++ page = ajBtreeCacheRead(cache,pageno);
++ buf = page->buf;
++ GBT_BUCKOVERFLOW(buf,&overflow);
++ }
++
++ v = BT_BUCKET;
++ SBT_BUCKNODETYPE(buf,v);
++ page->dirty = BT_LOCK;
++
++ nentries = bucket->Nentries;
++ v = nentries;
++ SBT_BUCKNENTRIES(buf,v);
++
++ pptr = PBT_BUCKKEYLEN(buf);
++
++ for(i=0;i<nentries;++i)
++ {
++ lv = bucket->NumId[i]->offset;
++ BT_SETAJLONG(pptr,lv);
++ pptr += sizeof(ajlong);
++ lv = bucket->NumId[i]->refoffset;
++ BT_SETAJLONG(pptr,lv);
++ pptr += sizeof(ajlong);
++ v = bucket->NumId[i]->dbno;
++ BT_SETAJINT(pptr,v);
++ pptr += sizeof(ajint);
++ }
++
++ lv = 0L;
++ SBT_BUCKOVERFLOW(buf,lv);
++ page->dirty = BT_DIRTY;
++
++ return;
++}
++
++
++
++
++/* @funcstatic btreeReadNumBucket *********************************************
++**
++** Constructor for index bucket given a disc page number
++** Creates one empty key slot for possible addition
++**
++** @param [u] cache [AjPBtcache] cache
++** @param [r] pageno [ajlong] page number
++**
++** @return [AjPNumBucket] bucket
++** @@
++******************************************************************************/
++
++static AjPNumBucket btreeReadNumBucket(AjPBtcache cache, ajlong pageno)
++{
++ AjPNumBucket bucket = NULL;
++ AjPBtpage page = NULL;
++ unsigned char *buf = NULL;
++ unsigned char *pptr = NULL;
++ ajint nodetype = 0;
++ ajint nentries = 0;
++ ajlong overflow = 0L;
++ ajint dirtysave = 0;
++
++ ajint i;
++
++ /* ajDebug("In btreeReadNumBucket\n"); */
++
++ if(pageno == cache->secrootblock)
++ ajFatal("ReadNumBucket: cannot read bucket from a root page");
++
++ page = ajBtreeCacheRead(cache,pageno);
++ dirtysave = page->dirty;
++ page->dirty = BT_LOCK;
++ buf = page->buf;
++
++ GBT_BUCKNODETYPE(buf,&nodetype);
++ if(nodetype != BT_BUCKET)
++ ajFatal("ReadNumBucket: Nodetype mismatch. Not bucket (%d)",nodetype);
++
++ GBT_BUCKNENTRIES(buf,&nentries);
++ if(nentries > cache->snperbucket)
++ ajFatal("ReadNumBucket: Bucket too full");
++
++ GBT_BUCKOVERFLOW(buf,&overflow);
++
++ AJNEW0(bucket);
++ bucket->NodeType = nodetype;
++ bucket->Nentries = nentries;
++ bucket->Overflow = overflow;
++
++ AJCNEW0(bucket->NumId,nentries+1);
++ for(i=0;i<nentries;++i)
++ AJNEW0(bucket->NumId[i]);
++
++
++ pptr = PBT_BUCKKEYLEN(buf);
++
++ for(i=0;i<nentries;++i)
++ {
++ BT_GETAJLONG(pptr,&bucket->NumId[i]->offset);
++ pptr += sizeof(ajlong);
++ BT_GETAJLONG(pptr,&bucket->NumId[i]->refoffset);
++ pptr += sizeof(ajlong);
++ BT_GETAJINT(pptr,&bucket->NumId[i]->dbno);
++ pptr += sizeof(ajint);
++ }
++
++ page->dirty = dirtysave;
++
++ return bucket;
++}
++
++
++
++
++/* @funcstatic btreeNumBucketDel *********************************************
++**
++** Delete a bucket object
++**
++** @param [w] thys [AjPNumBucket*] bucket
++**
++** @return [void]
++** @@
++******************************************************************************/
++
++static void btreeNumBucketDel(AjPNumBucket *thys)
++{
++ AjPNumBucket pthis = NULL;
++ int i;
++
++
++ /* ajDebug("In btreeNumBucketDel\n"); */
++
++ if(!thys || !*thys)
++ return;
++
++ pthis = *thys;
++
++ if(pthis->Nentries)
++ {
++ for(i=0;i<pthis->Nentries;++i)
++ AJFREE(pthis->NumId[i]);
++
++ AJFREE(pthis->NumId);
++ }
++
++
++ AJFREE(pthis);
++
++ *thys = NULL;
++
++ return;
++}
++
++
++
++
++/* @func ajBtreeNumFindInsert ***********************************************
++**
++** Find the node that should contain a new key for insertion
++**
++** @param [u] cache [AjPBtcache] cache
++** @param [r] key [const ajlong] key to search for
++**
++** @return [AjPBtpage] leaf node where item should be inserted
++** @@
++******************************************************************************/
++
++AjPBtpage ajBtreeNumFindInsert(AjPBtcache cache, const ajlong key)
++{
++ AjPBtpage root = NULL;
++ AjPBtpage ret = NULL;
++
++ /* ajDebug("In ajBtreeNumFindInsert\n"); */
++
++ /* The root node should always be in the cache (BT_LOCKed) */
++ root = btreeCacheLocate(cache,cache->secrootblock);
++
++ /* ajDebug("cache->slevel = %d root=%d\n",cache->slevel,(ajint)root); */
++
++
++ if(!cache->slevel)
++ return root;
++
++ ret = btreeNumFindINode(cache,root,key);
++
++ return ret;
++}
++
++
++
++
++/* @funcstatic btreeNumFindINode **********************************************
++**
++** Recursive search for insert node in a secondary tree
++**
++** @param [u] cache [AjPBtcache] cache
++** @param [u] page [AjPBtpage] page
++** @param [r] item [const ajlong] key to search for
++**
++** @return [AjPBtpage] leaf node where item should be inserted
++** @@
++******************************************************************************/
++
++static AjPBtpage btreeNumFindINode(AjPBtcache cache, AjPBtpage page,
++ const ajlong item)
++{
++ AjPBtpage ret = NULL;
++ AjPBtpage pg = NULL;
++
++ unsigned char *buf = NULL;
++ ajint status = 0;
++ ajint ival = 0;
++
++ /* ajDebug("In btreeNumFindINode\n"); */
++
++ ret = page;
++ buf = page->buf;
++ GBT_NODETYPE(buf,&ival);
++ if(ival != BT_LEAF)
++ {
++ status = ret->dirty;
++ ret->dirty = BT_LOCK; /* Lock in case of lots of overflow pages */
++ pg = btreeNumPageFromKey(cache,buf,item);
++ ret->dirty = status;
++ ret = btreeNumFindINode(cache,pg,item);
++ }
++
++ return ret;
++}
++
++
++
++
++/* @funcstatic btreeNumPageFromKey *******************************************
++**
++** Return next lower index page given a key in a secondary tree
++**
++** @param [u] cache [AjPBtcache] cache
++** @param [u] buf [unsigned char *] page buffer
++** @param [r] key [const ajlong] key to search for
++**
++** @return [AjPBtpage] pointer to a page
++** @@
++******************************************************************************/
++
++static AjPBtpage btreeNumPageFromKey(AjPBtcache cache, unsigned char *buf,
++ const ajlong key)
++{
++ unsigned char *rootbuf = NULL;
++ ajint nkeys = 0;
++ ajint order = 0;
++ ajint i;
++
++ ajlong blockno = 0L;
++ ajlong *karray = NULL;
++ ajlong *parray = NULL;
++ AjPBtpage page = NULL;
++ AjPBtMem array = NULL;
++
++ /* ajDebug("In btreeNumPageFromKey\n"); */
++
++ rootbuf = buf;
++
++
++ GBT_NKEYS(rootbuf,&nkeys);
++ order = cache->sorder;
++
++ array = btreeAllocSecArray(cache);
++ karray = array->overflows;
++ parray = array->parray;
++
++ btreeGetNumKeys(cache,rootbuf,&karray,&parray);
++ i = 0;
++ while(i!=nkeys && key >= karray[i])
++ ++i;
++ if(i==nkeys)
++ {
++ if(key < karray[i-1])
++ blockno = parray[i-1];
++ else
++ blockno = parray[i];
++ }
++ else
++ blockno = parray[i];
++
++ btreeDeallocSecArray(cache,array);
++
++ page = ajBtreeCacheRead(cache,blockno);
++
++ return page;
++}
++
++
++
++
++/* @funcstatic btreeAddToNumBucket *******************************************
++**
++** Add offset info to a bucket
++** Only called if there is room in the bucket
++**
++** @param [u] cache [AjPBtcache] cache
++** @param [r] pageno [ajlong] page number of bucket
++** @param [r] num [const AjPBtNumId] ID info
++**
++** @return [void]
++** @@
++******************************************************************************/
++
++static void btreeAddToNumBucket(AjPBtcache cache, ajlong pageno,
++ const AjPBtNumId num)
++{
++ AjPNumBucket bucket = NULL;
++ ajint nentries;
++
++ /* ajDebug("In btreeAddToNumBucket\n"); */
++
++ bucket = btreeReadNumBucket(cache,pageno);
++
++ nentries = bucket->Nentries;
++
++ /* Reading a bucket always gives one extra ID position */
++
++ AJNEW0(bucket->NumId[nentries]);
++ bucket->NumId[nentries]->offset = num->offset;
++ bucket->NumId[nentries]->refoffset = num->refoffset;
++ bucket->NumId[nentries]->dbno = num->dbno;
++
++
++ ++bucket->Nentries;
++
++ btreeWriteNumBucket(cache,bucket,pageno);
++
++ btreeNumBucketDel(&bucket);
++
++ return;
++}
++
++
++
++
++/* @funcstatic btreeNumInNumBucket *******************************************
++**
++** Return number of entries in a bucket
++**
++** @param [u] cache [AjPBtcache] cache
++** @param [r] pageno [ajlong] page number
++**
++** @return [ajint] Number of entries in bucket
++** @@
++******************************************************************************/
++
++static ajint btreeNumInNumBucket(AjPBtcache cache, ajlong pageno)
++{
++ AjPBtpage page = NULL;
++ unsigned char *buf = NULL;
++ ajint nodetype = 0;
++ ajint nentries = 0;
++
++ /* ajDebug("In btreeNumInNumBucket\n"); */
++
++ if(pageno == cache->secrootblock)
++ ajFatal("NumInNumBucket: Attempt to read bucket from root page\n");
++
++ page = ajBtreeCacheRead(cache,pageno);
++
++ buf = page->buf;
++
++ GBT_BUCKNODETYPE(buf,&nodetype);
++ if(nodetype != BT_BUCKET)
++ ajFatal("NumInNumBucket: NodeType mismatch. Not bucket (%d)",
++ nodetype);
++
++ GBT_BUCKNENTRIES(buf,&nentries);
++
++ return nentries;
++}
++
++
++
++
++/* @funcstatic btreeNumBucketNew *********************************************
++**
++** Construct a bucket object
++**
++** @param [r] n [ajint] Number of IDs
++**
++** @return [AjPBucket] initialised disc block cache structure
++** @@
++******************************************************************************/
++
++static AjPNumBucket btreeNumBucketNew(ajint n)
++{
++ AjPNumBucket bucket = NULL;
++ ajint i;
++
++ /* ajDebug("In btreeNumBucketNew\n"); */
++
++ AJNEW0(bucket);
++
++ if(n)
++ {
++ AJCNEW0(bucket->NumId,n);
++ for(i=0;i<n;++i)
++ AJNEW0(bucket->NumId[i]);
++ }
++
++ bucket->NodeType = BT_BUCKET;
++ bucket->Nentries = n;
++ bucket->Overflow = 0L;
++
++ return bucket;
++}
++
++
++
++
++/* @funcstatic btreeReorderNumBuckets *****************************************
++**
++** Re-order leaf buckets
++** Must only be called if one of the buckets is full
++**
++** @param [u] cache [AjPBtcache] cache
++** @param [u] leaf [AjPBtpage] leaf page
++**
++** @return [AjBool] true if reorder was successful i.e. leaf not full
++** @@
++******************************************************************************/
++
++static AjBool btreeReorderNumBuckets(AjPBtcache cache, AjPBtpage leaf)
++{
++ ajint nkeys = 0;
++ unsigned char *lbuf = NULL;
++
++ ajlong *keys = NULL;
++ ajlong *ptrs = NULL;
++
++ AjPNumBucket *buckets = NULL;
++
++ ajint i = 0;
++ ajint j = 0;
++
++ ajint order;
++ ajint bentries = 0;
++ ajint totalkeys = 0;
++ ajint nperbucket = 0;
++ ajint maxnperbucket = 0;
++ ajint count = 0;
++ ajint keylimit = 0;
++ ajint bucketn = 0;
++ ajint bucketlimit = 0;
++ ajint nodetype = 0;
++
++ AjPList idlist = NULL;
++ ajint dirtysave = 0;
++ AjPBtNumId bid = NULL;
++ AjPNumBucket cbucket = NULL;
++ AjPBtNumId cid = NULL;
++
++ ajint v = 0;
++ AjPBtMem array = NULL;
++
++ /* ajDebug("In btreeReorderNumBuckets\n"); */
++
++ dirtysave = leaf->dirty;
++
++ leaf->dirty = BT_LOCK;
++ lbuf = leaf->buf;
++
++ GBT_NODETYPE(lbuf,&nodetype);
++
++ order = cache->sorder;
++ nperbucket = cache->snperbucket;
++
++
++ array = btreeAllocSecArray(cache);
++ keys = array->overflows;
++ ptrs = array->parray;
++
++ /* Read keys/ptrs */
++
++ btreeGetNumKeys(cache,lbuf,&keys,&ptrs);
++
++ GBT_NKEYS(lbuf,&nkeys);
++
++
++ if(!nkeys)
++ ajFatal("NumBucketReorder: Attempt to reorder empty leaf");
++
++ for(i=0;i<nkeys;++i)
++ totalkeys += btreeNumInNumBucket(cache,ptrs[i]);
++ totalkeys += btreeNumInNumBucket(cache,ptrs[i]);
++
++ /* Set the number of entries per bucket to approximately half full */
++ maxnperbucket = nperbucket >> 1;
++
++ if(!maxnperbucket)
++ ++maxnperbucket;
++
++ /* Work out the number of new buckets needed */
++ bucketn = (totalkeys / maxnperbucket);
++ if(totalkeys % maxnperbucket)
++ ++bucketn;
++
++ if(bucketn > order)
++ {
++ btreeDeallocSecArray(cache,array);
++
++ leaf->dirty = dirtysave;
++ return ajFalse;
++ }
++
++
++ /* Read buckets */
++ AJCNEW0(buckets,nkeys+1);
++ keylimit = nkeys + 1;
++
++ for(i=0;i<keylimit;++i)
++ buckets[i] = btreeReadNumBucket(cache,ptrs[i]);
++
++
++ /* Read IDs from all buckets and push to list and sort (increasing id) */
++ idlist = ajListNew();
++
++ for(i=0;i<keylimit;++i)
++ {
++ bentries = buckets[i]->Nentries;
++ for(j=0;j<bentries;++j)
++ ajListPush(idlist,(void *)buckets[i]->NumId[j]);
++
++ AJFREE(buckets[i]->NumId);
++ AJFREE(buckets[i]);
++ }
++ ajListSort(idlist,btreeNumIdCompare);
++ AJFREE(buckets);
++
++ cbucket = btreeNumBucketNew(maxnperbucket);
++ bucketlimit = bucketn - 1;
++
++ for(i=0;i<bucketlimit;++i)
++ {
++ cbucket->Nentries = 0;
++
++ count = 0;
++ while(count!=maxnperbucket)
++ {
++ ajListPop(idlist,(void **)&bid);
++
++ cid = cbucket->NumId[count];
++ cid->dbno = bid->dbno;
++ cid->offset = bid->offset;
++ cid->refoffset = bid->refoffset;
++
++ ++cbucket->Nentries;
++ ++count;
++ AJFREE(bid);
++ }
++
++
++ ajListPeek(idlist,(void **)&bid);
++ keys[i] = bid->offset;
++
++ if(!ptrs[i])
++ ptrs[i] = cache->totsize;
++ btreeWriteNumBucket(cache,cbucket,ptrs[i]);
++ }
++
++
++ /* Deal with greater-than bucket */
++
++ cbucket->Nentries = 0;
++
++ count = 0;
++ while(ajListPop(idlist,(void **)&bid))
++ {
++ cid = cbucket->NumId[count];
++ cid->dbno = bid->dbno;
++ cid->offset = bid->offset;
++ cid->refoffset = bid->refoffset;
++
++ ++cbucket->Nentries;
++ ++count;
++ AJFREE(bid);
++ }
++
++
++ if(!ptrs[i])
++ ptrs[i] = cache->totsize;
++ btreeWriteNumBucket(cache,cbucket,ptrs[i]);
++
++ cbucket->Nentries = maxnperbucket;
++ btreeNumBucketDel(&cbucket);
++
++ /* Now write out a modified leaf with new keys/ptrs */
++
++ nkeys = bucketn - 1;
++ v = nkeys;
++ SBT_NKEYS(lbuf,v);
++
++ btreeWriteNumNode(cache,leaf,keys,ptrs,nkeys);
++
++ leaf->dirty = BT_DIRTY;
++ if(nodetype == BT_ROOT)
++ leaf->dirty = BT_LOCK;
++
++ btreeDeallocSecArray(cache,array);
++
++ ajListDel(&idlist);
++
++ return ajTrue;
++}
++
++
++
++
++/* @funcstatic btreeNumNodeIsFull *****************************************
++**
++** Tests whether a node is full of keys
++**
++** @param [r] cache [const AjPBtcache] cache
++** @param [u] page [AjPBtpage] original page
++**
++** @return [AjBool] true if full
++** @@
++******************************************************************************/
++
++static AjBool btreeNumNodeIsFull(const AjPBtcache cache, AjPBtpage page)
++{
++ unsigned char *buf = NULL;
++ ajint nkeys = 0;
++
++ /* ajDebug("In btreeNumNodeIsFull\n"); */
++
++ buf = page->buf;
++ GBT_NKEYS(buf,&nkeys);
++
++ if(nkeys == cache->sorder - 1)
++ return ajTrue;
++
++ return ajFalse;
++}
++
++
++
++
++/* @funcstatic btreeNumInsertNonFull *****************************************
++**
++** Insert a key into a non-full node
++**
++** @param [u] cache [AjPBtcache] cache
++** @param [u] page [AjPBtpage] original page
++** @param [r] key [const ajlong] key to insert
++** @param [r] less [ajlong] less-than pointer
++** @param [r] greater [ajlong] greater-than pointer
++**
++** @return [void]
++** @@
++******************************************************************************/
++
++static void btreeNumInsertNonFull(AjPBtcache cache, AjPBtpage page,
++ const ajlong key, ajlong less,
++ ajlong greater)
++{
++ unsigned char *buf = NULL;
++ ajlong *karray = NULL;
++ ajlong *parray = NULL;
++ ajint nkeys = 0;
++ ajint order = 0;
++ ajint ipos = 0;
++ ajint i;
++ ajint count = 0;
++
++ ajlong lv = 0L;
++ ajint v = 0;
++ AjPBtMem array = NULL;
++
++ AjPBtpage ppage = NULL;
++ ajlong pageno = 0L;
++
++ ajint nodetype = 0;
++
++ /* ajDebug("In btreeNumInsertNonFull\n"); */
++
++ order = cache->sorder;
++
++ array = btreeAllocSecArray(cache);
++ karray = array->overflows;
++ parray = array->parray;
++
++
++ buf = page->buf;
++ GBT_NKEYS(buf,&nkeys);
++ GBT_NODETYPE(buf,&nodetype);
++
++ btreeGetNumKeys(cache,buf,&karray,&parray);
++
++ i = 0;
++ while(i!=nkeys && key >= karray[i])
++ ++i;
++
++ ipos = i;
++
++ count = nkeys - ipos;
++
++
++ if(ipos == nkeys)
++ {
++ karray[ipos] = key;
++ parray[ipos+1] = greater;
++ parray[ipos] = less;
++ }
++ else
++ {
++ parray[nkeys+1] = parray[nkeys];
++
++ for(i=nkeys-1; count>0; --count, --i)
++ {
++ karray[i+1] = karray[i];
++ parray[i+1] = parray[i];
++ }
++
++ karray[ipos] = key;
++ parray[ipos] = less;
++ parray[ipos+1] = greater;
++ }
++
++ ++nkeys;
++ v = nkeys;
++ SBT_NKEYS(buf,v);
++
++ btreeWriteNumNode(cache,page,karray,parray,nkeys);
++ if(nodetype == BT_ROOT)
++ page->dirty = BT_LOCK;
++
++ pageno = page->pageno;
++ ppage = ajBtreeCacheRead(cache,less);
++ lv = pageno;
++ SBT_PREV(ppage->buf,lv);
++ ppage->dirty = BT_DIRTY;
++ ppage = ajBtreeCacheRead(cache,greater);
++ lv = pageno;
++ SBT_PREV(ppage->buf,lv);
++ ppage->dirty = BT_DIRTY;
++
++
++ btreeDeallocSecArray(cache,array);
++
++ if(nodetype != BT_ROOT)
++ btreeNumKeyShift(cache,page);
++
++ return;
++}
++
++
++
++
++/* @funcstatic btreeNumInsertKey **********************************************
++**
++** Insert a key into a potentially full node
++**
++** @param [u] cache [AjPBtcache] cache
++** @param [u] page [AjPBtpage] original page
++** @param [r] key [const ajlong] key to insert
++** @param [r] less [ajlong] less-than pointer
++** @param [r] greater [ajlong] greater-than pointer
++**
++** @return [void]
++** @@
++******************************************************************************/
++
++static void btreeNumInsertKey(AjPBtcache cache, AjPBtpage page,
++ const ajlong key, ajlong less, ajlong greater)
++{
++ unsigned char *lbuf = NULL;
++ unsigned char *rbuf = NULL;
++ unsigned char *tbuf = NULL;
++ ajlong *karray = NULL;
++ ajlong *parray = NULL;
++ ajlong *tkarray = NULL;
++ ajlong *tparray = NULL;
++ ajint nkeys = 0;
++ ajint order = 0;
++ ajint keypos = 0;
++ ajint rkeyno = 0;
++
++ ajint i = 0;
++ ajint n = 0;
++
++ ajint nodetype = 0;
++ AjPBtpage ipage = NULL;
++ AjPBtpage lpage = NULL;
++ AjPBtpage rpage = NULL;
++ AjPBtpage tpage = NULL;
++
++ ajlong blockno = 0L;
++ ajlong rblockno = 0L;
++ ajlong lblockno = 0L;
++ ajlong mediankey = 0L;
++ ajlong medianless = 0L;
++ ajlong mediangtr = 0L;
++ ajlong overflow = 0L;
++ ajlong prev = 0L;
++ ajint totlen = 0;
++
++ ajlong lv = 0L;
++ ajint v = 0;
++ AjPBtMem array = NULL;
++ AjPBtMem array2 = NULL;
++
++ /* ajDebug("In btreeNumInsertKey\n"); */
++
++ if(!btreeNumNodeIsFull(cache,page))
++ {
++ btreeNumInsertNonFull(cache,page,key,less,greater);
++ return;
++ }
++
++ order = cache->sorder;
++ lbuf = page->buf;
++ GBT_NODETYPE(lbuf,&nodetype);
++ page->dirty = BT_LOCK;
++
++ if(nodetype == BT_ROOT)
++ {
++ array = btreeAllocSecArray(cache);
++ karray = array->overflows;
++ parray = array->parray;
++
++ btreeNumSplitRoot(cache);
++
++ if(page->pageno)
++ page->dirty = BT_DIRTY;
++ btreeGetNumKeys(cache,lbuf,&karray,&parray);
++
++ if(key < karray[0])
++ blockno = parray[0];
++ else
++ blockno = parray[1];
++ ipage = ajBtreeCacheRead(cache,blockno);
++ btreeNumInsertNonFull(cache,ipage,key,less,greater);
++
++ btreeDeallocSecArray(cache,array);
++ return;
++ }
++
++
++ array = btreeAllocSecArray(cache);
++ karray = array->overflows;
++ parray = array->parray;
++
++ array2 = btreeAllocSecArray(cache);
++ tkarray = array2->overflows;
++ tparray = array2->parray;
++
++
++ lpage = page;
++ lbuf = lpage->buf;
++
++ btreeGetNumKeys(cache,lbuf,&karray,&parray);
++
++ GBT_BLOCKNUMBER(lbuf,&lblockno);
++ rblockno = cache->totsize;
++ rpage = ajBtreeCacheWrite(cache,rblockno);
++ rpage->dirty = BT_LOCK;
++ rpage->pageno = cache->totsize;
++ cache->totsize += cache->pagesize;
++ rbuf = rpage->buf;
++ lv = rblockno;
++ SBT_BLOCKNUMBER(rbuf,lv);
++
++
++ GBT_PREV(lbuf,&prev);
++ lv = prev;
++ SBT_PREV(rbuf,lv);
++
++ nkeys = order - 1;
++ keypos = nkeys / 2;
++ if(!(nkeys % 2))
++ --keypos;
++
++ mediankey = karray[keypos];
++ medianless = lblockno;
++ mediangtr = rblockno;
++
++
++ GBT_NODETYPE(lbuf,&nodetype);
++ v = nodetype;
++ SBT_NODETYPE(rbuf,v);
++ lv = overflow;
++ SBT_OVERFLOW(rbuf,lv);
++
++
++ totlen = 0;
++ for(i=0;i<keypos;++i)
++ {
++ tkarray[i] = karray[i];
++ tparray[i] = parray[i];
++ }
++ tparray[i] = parray[i];
++
++ n = i;
++ v = n;
++ SBT_NKEYS(lbuf,v);
++ btreeWriteNumNode(cache,lpage,tkarray,tparray,i);
++
++
++
++ for(i=0;i<n+1;++i)
++ {
++ tpage = ajBtreeCacheRead(cache,tparray[i]);
++ tbuf = tpage->buf;
++ lv = lblockno;
++ SBT_PREV(tbuf,lv);
++ tpage->dirty = BT_DIRTY;
++ }
++
++
++ totlen = 0;
++ for(i=keypos+1;i<nkeys;++i)
++ {
++ tkarray[i-(keypos+1)] = karray[i];
++ tparray[i-(keypos+1)] = parray[i];
++ }
++ tparray[i-(keypos+1)] = parray[i];
++
++ rkeyno = (nkeys-keypos) - 1;
++ v = rkeyno;
++ SBT_NKEYS(rbuf,v);
++ rpage->dirty = BT_DIRTY;
++ btreeWriteNumNode(cache,rpage,tkarray,tparray,rkeyno);
++
++
++ for(i=0;i<rkeyno+1;++i)
++ {
++ tpage = ajBtreeCacheRead(cache,tparray[i]);
++ tbuf = tpage->buf;
++ lv = rblockno;
++ SBT_PREV(tbuf,lv);
++ tpage->dirty = BT_DIRTY;
++ }
++
++
++ ipage = rpage;
++ if(key < mediankey)
++ ipage = lpage;
++
++ btreeNumInsertNonFull(cache,ipage,key,less,greater);
++
++
++ btreeDeallocSecArray(cache,array);
++ btreeDeallocSecArray(cache,array2);
++
++ ipage = ajBtreeCacheRead(cache,prev);
++
++ btreeNumInsertKey(cache,ipage,mediankey,medianless,mediangtr);
++
++ return;
++}
++
++
++
++
++/* @funcstatic btreeNumSplitRoot *****************************************
++**
++** Split s secondary root node
++**
++** @param [u] cache [AjPBtcache] cache
++**
++** @return [void]
++** @@
++******************************************************************************/
++
++static void btreeNumSplitRoot(AjPBtcache cache)
++{
++ AjPBtpage rootpage = NULL;
++ AjPBtpage rpage = NULL;
++ AjPBtpage lpage = NULL;
++ AjPBtpage tpage = NULL;
++
++ ajlong *karray = NULL;
++ ajlong *tkarray = NULL;
++ ajlong *parray = NULL;
++ ajlong *tparray = NULL;
++
++ ajint order = 0;
++ ajint nkeys = 0;
++ ajint keypos = 0;
++
++ ajlong rblockno = 0L;
++ ajlong lblockno = 0L;
++
++ ajlong right;
++
++ ajint i;
++
++ unsigned char *rootbuf = NULL;
++ unsigned char *rbuf = NULL;
++ unsigned char *lbuf = NULL;
++ unsigned char *tbuf = NULL;
++
++ ajint nodetype = 0;
++ ajlong overflow = 0L;
++ ajlong zero = 0L;
++ ajint rkeyno = 0;
++ ajint n = 0;
++
++ ajlong lv = 0L;
++ ajint v = 0;
++
++ AjPBtMem array = NULL;
++ AjPBtMem array2 = NULL;
++
++
++ /* ajDebug("In btreeNumSplitRoot\n"); */
++
++ order = cache->sorder;
++
++ array = btreeAllocSecArray(cache);
++ karray = array->overflows;
++ parray = array->parray;
++
++ array2 = btreeAllocSecArray(cache);
++ tkarray = array2->overflows;
++ tparray = array2->parray;
++
++
++ rootpage = btreeCacheLocate(cache,cache->secrootblock);
++ rootbuf = rootpage->buf;
++
++ nkeys = order - 1;
++
++ keypos = nkeys / 2;
++ if(!(nkeys % 2))
++ --keypos;
++
++
++ rblockno = cache->totsize;
++ rpage = ajBtreeCacheWrite(cache,rblockno);
++ rpage->pageno = cache->totsize;
++ cache->totsize += cache->pagesize;
++ rpage->dirty = BT_LOCK;
++
++ lblockno = cache->totsize;
++ lpage = ajBtreeCacheWrite(cache,lblockno);
++ lpage->pageno = cache->totsize;
++ cache->totsize += cache->pagesize;
++
++ lv = rblockno;
++ SBT_BLOCKNUMBER(rpage->buf,lv);
++ lv = lblockno;
++ SBT_BLOCKNUMBER(lpage->buf,lv);
++
++ /* Comment this next block out after the beta test */
++ if(!cache->slevel)
++ {
++ fprintf(stderr,"btreeSplitRootSec: Shouldn't get here\n");
++ exit(0);
++ lv = zero;
++ SBT_LEFT(lpage->buf,lv);
++ lv = rblockno;
++ SBT_RIGHT(lpage->buf,lv);
++ lv = lblockno;
++ SBT_LEFT(rpage->buf,lv);
++ lv = zero;
++ SBT_RIGHT(rpage->buf,lv);
++ }
++
++ btreeGetNumKeys(cache,rootbuf,&karray,&parray);
++
++ /* Get key for root node and write new root node */
++ tkarray[0] = karray[keypos];
++ tparray[0] = lblockno;
++ tparray[1] = rblockno;
++
++
++ n = 1;
++ v = n;
++ SBT_NKEYS(rootbuf,v);
++ btreeWriteNumNode(cache,rootpage,tkarray,tparray,1);
++ right = (ajlong)(cache->slevel + 1);
++ SBT_RIGHT(rootbuf,right);
++
++ rootpage->dirty = BT_LOCK;
++
++ rbuf = rpage->buf;
++ lbuf = lpage->buf;
++
++ if(cache->slevel)
++ nodetype = BT_INTERNAL;
++ else
++ nodetype = BT_LEAF;
++
++ v = nodetype;
++ SBT_NODETYPE(rbuf,v);
++ v = nodetype;
++ SBT_NODETYPE(lbuf,v);
++ lv = overflow;
++ SBT_OVERFLOW(rbuf,lv);
++ lv = cache->secrootblock;
++ SBT_PREV(rbuf,lv);
++ lv = overflow;
++ SBT_OVERFLOW(lbuf,lv);
++ lv = cache->secrootblock;
++ SBT_PREV(lbuf,lv);
++
++ for(i=0;i<keypos;++i)
++ {
++ tkarray[i] = karray[i];
++ tparray[i] = parray[i];
++ }
++ tparray[i] = parray[i];
++
++ n = i;
++ v = n;
++ SBT_NKEYS(lbuf,v);
++ btreeWriteNumNode(cache,lpage,tkarray,tparray,i);
++
++ for(i=0;i<n+1;++i)
++ {
++ tpage = ajBtreeCacheRead(cache,tparray[i]);
++ tbuf = tpage->buf;
++ lv = lblockno;
++ SBT_PREV(tbuf,lv);
++ tpage->dirty = BT_DIRTY;
++ }
++
++ for(i=keypos+1;i<nkeys;++i)
++ {
++ tkarray[i-(keypos+1)] = karray[i];
++ tparray[i-(keypos+1)] = parray[i];
++ }
++ tparray[i-(keypos+1)] = parray[i];
++
++ rkeyno = (nkeys-keypos) - 1;
++ v = rkeyno;
++ SBT_NKEYS(rbuf,v);
++ rpage->dirty = BT_DIRTY;
++ btreeWriteNumNode(cache,rpage,tkarray,tparray,rkeyno);
++
++ for(i=0;i<rkeyno+1;++i)
++ {
++ tpage = ajBtreeCacheRead(cache,tparray[i]);
++ tbuf = tpage->buf;
++ lv = rblockno;
++ SBT_PREV(tbuf,lv);
++ tpage->dirty = BT_DIRTY;
++ }
++
++
++ ++cache->slevel;
++
++ btreeDeallocSecArray(cache,array);
++ btreeDeallocSecArray(cache,array2);
++
++ return;
++}
++
++
++
++
++/* @funcstatic btreeNumKeyShift ********************************************
++**
++** Rebalance Nodes on insertion
++**
++** @param [u] cache [AjPBtcache] cache
++** @param [u] tpage [AjPBtpage] page
++**
++** @return [void]
++** @@
++******************************************************************************/
++
++static void btreeNumKeyShift(AjPBtcache cache, AjPBtpage tpage)
++{
++ unsigned char *tbuf = NULL;
++ unsigned char *pbuf = NULL;
++ unsigned char *sbuf = NULL;
++ unsigned char *buf = NULL;
++
++ AjPBtpage ppage = NULL;
++ AjPBtpage spage = NULL;
++ AjPBtpage page = NULL;
++
++ ajint tkeys = 0;
++ ajint pkeys = 0;
++ ajint skeys = 0;
++ ajint order = 0;
++
++ ajint i;
++
++ ajlong parent = 0L;
++
++ ajlong *kTarray = NULL;
++ ajlong *kParray = NULL;
++ ajlong *kSarray = NULL;
++ ajlong *pTarray = NULL;
++ ajlong *pParray = NULL;
++ ajlong *pSarray = NULL;
++
++ ajint pkeypos = 0;
++ ajint minsize = 0;
++
++ ajlong lv = 0L;
++
++ AjPBtMem array = NULL;
++ AjPBtMem array2 = NULL;
++ AjPBtMem array3 = NULL;
++
++ /* ajDebug("In btreeNumKeyShift\n"); */
++
++ tbuf = tpage->buf;
++
++ GBT_PREV(tbuf,&parent);
++ GBT_NKEYS(tbuf,&tkeys);
++
++ order = cache->sorder;
++ minsize = order / 2;
++ if(order % 2)
++ ++minsize;
++
++ if(tkeys <= minsize)
++ return;
++
++
++ ppage = ajBtreeCacheRead(cache,parent);
++ pbuf = ppage->buf;
++ GBT_NKEYS(pbuf,&pkeys);
++
++ array = btreeAllocSecArray(cache);
++ kParray = array->overflows;
++ pParray = array->parray;
++
++ array2 = btreeAllocSecArray(cache);
++ kSarray = array2->overflows;
++ pSarray = array2->parray;
++
++ array3 = btreeAllocSecArray(cache);
++ kTarray = array3->overflows;
++ pTarray = array3->parray;
++
++
++ btreeGetNumKeys(cache,tbuf,&kTarray,&pTarray);
++ GBT_NKEYS(tbuf,&tkeys);
++
++
++ btreeGetNumKeys(cache,pbuf,&kParray,&pParray);
++ i=0;
++ while(pParray[i] != tpage->pageno)
++ ++i;
++
++ if(i) /* There is another leaf to the left */
++ {
++ pkeypos = i-1;
++ spage = ajBtreeCacheRead(cache,pParray[pkeypos]);
++ sbuf = spage->buf;
++ GBT_NKEYS(sbuf,&skeys);
++
++ }
++
++ if(i && skeys != order-1) /* There is space in the left leaf */
++ {
++ if(skeys)
++ btreeGetNumKeys(cache,sbuf,&kSarray,&pSarray);
++
++ kSarray[skeys] = kParray[pkeypos];
++ pSarray[skeys+1] = pTarray[0];
++ ++skeys;
++ --tkeys;
++ kParray[pkeypos] = kTarray[0];
++ for(i=0;i<tkeys;++i)
++ {
++ kTarray[i] = kTarray[i+1];
++ pTarray[i] = pTarray[i+1];
++ }
++ pTarray[i] = pTarray[i+1];
++ pTarray[i+1] = 0L;
++
++ btreeWriteNumNode(cache,spage,kSarray,pSarray,skeys);
++ btreeWriteNumNode(cache,tpage,kTarray,pTarray,tkeys);
++ btreeWriteNumNode(cache,ppage,kParray,pParray,pkeys);
++ if(!ppage->pageno)
++ ppage->dirty = BT_LOCK;
++
++ page = ajBtreeCacheRead(cache,pSarray[skeys]);
++ buf = page->buf;
++ lv = spage->pageno;
++ SBT_PREV(buf,lv);
++ page->dirty = BT_DIRTY;
++
++
++ btreeDeallocSecArray(cache,array);
++ btreeDeallocSecArray(cache,array2);
++ btreeDeallocSecArray(cache,array3);
++
++ return;
++ }
++
++
++
++ if(i != pkeys) /* There is a right node */
++ {
++ pkeypos = i;
++ spage = ajBtreeCacheRead(cache,pParray[pkeypos+1]);
++ sbuf = spage->buf;
++ GBT_NKEYS(sbuf,&skeys);
++ }
++
++
++ if(i != pkeys && skeys != order-1) /* Space in the right node */
++ {
++ if(skeys)
++ btreeGetNumKeys(cache,sbuf,&kSarray,&pSarray);
++
++ pSarray[skeys+1] = pSarray[skeys];
++ for(i=skeys-1;i>-1;--i)
++ {
++ kSarray[i+1] = kSarray[i];
++ pSarray[i+1] = pSarray[i];
++ }
++ kSarray[0] = kParray[pkeypos];
++ pSarray[0] = pTarray[tkeys];
++ kParray[pkeypos] = kTarray[tkeys-1];
++ ++skeys;
++ --tkeys;
++ pTarray[tkeys+1] = 0L;
++
++ btreeWriteNumNode(cache,spage,kSarray,pSarray,skeys);
++ btreeWriteNumNode(cache,tpage,kTarray,pTarray,tkeys);
++ btreeWriteNumNode(cache,ppage,kParray,pParray,pkeys);
++ if(!ppage->pageno)
++ ppage->dirty = BT_LOCK;
++
++ page = ajBtreeCacheRead(cache,pSarray[0]);
++ buf = page->buf;
++ lv = spage->pageno;
++ SBT_PREV(buf,lv);
++ page->dirty = BT_DIRTY;
++
++ btreeDeallocSecArray(cache,array);
++ btreeDeallocSecArray(cache,array2);
++ btreeDeallocSecArray(cache,array3);
++
++ return;
++ }
++
++
++ btreeDeallocSecArray(cache,array);
++ btreeDeallocSecArray(cache,array2);
++ btreeDeallocSecArray(cache,array3);
++
++ return;
++}
++
++
++
++
++/* @funcstatic btreeNumInsertShift ********************************************
++**
++** Rebalance buckets on insertion
++**
++** @param [u] cache [AjPBtcache] cache
++** @param [u] retpage [AjPBtpage*] page
++** @param [r] key [const ajlong] key
++**
++** @return [ajlong] bucket block or 0L if shift not posible
++** @@
++******************************************************************************/
++
++static ajlong btreeNumInsertShift(AjPBtcache cache, AjPBtpage *retpage,
++ ajlong key)
++{
++ unsigned char *tbuf = NULL;
++ unsigned char *pbuf = NULL;
++ unsigned char *sbuf = NULL;
++
++ AjPBtpage ppage = NULL;
++ AjPBtpage spage = NULL;
++ AjPBtpage tpage = NULL;
++
++ ajint tkeys = 0;
++ ajint pkeys = 0;
++ ajint skeys = 0;
++ ajint order = 0;
++
++ ajint i;
++ ajint n;
++
++ ajlong parent = 0L;
++ ajlong blockno = 0L;
++
++ ajlong *kTarray = NULL;
++ ajlong *kParray = NULL;
++ ajlong *kSarray = NULL;
++ ajlong *pTarray = NULL;
++ ajlong *pParray = NULL;
++ ajlong *pSarray = NULL;
++
++ ajlong *karray = NULL;
++ ajlong *parray = NULL;
++
++ ajint ppos = 0;
++ ajint pkeypos = 0;
++ ajint minsize = 0;
++
++ AjPBtMem array = NULL;
++ AjPBtMem array2 = NULL;
++ AjPBtMem array3 = NULL;
++
++
++
++ /* ajDebug("In btreeNumInsertShift\n"); */
++
++
++ tpage = *retpage;
++
++ tbuf = tpage->buf;
++
++ GBT_PREV(tbuf,&parent);
++ GBT_NKEYS(tbuf,&tkeys);
++
++
++ order = cache->sorder;
++ minsize = order / 2;
++ if(order % 2)
++ ++minsize;
++
++ if(tkeys <= minsize)
++ return 0L;
++
++ ppage = ajBtreeCacheRead(cache,parent);
++
++
++
++ pbuf = ppage->buf;
++ GBT_NKEYS(pbuf,&pkeys);
++
++
++ array = btreeAllocSecArray(cache);
++ kParray = array->overflows;
++ pParray = array->parray;
++
++ array2 = btreeAllocSecArray(cache);
++ kSarray = array2->overflows;
++ pSarray = array2->parray;
++
++ array3 = btreeAllocSecArray(cache);
++ kTarray = array3->overflows;
++ pTarray = array3->parray;
++
++
++ btreeGetNumKeys(cache,pbuf,&kParray,&pParray);
++
++ i=0;
++ while(i!=pkeys && key >= kParray[i])
++ ++i;
++ pkeypos = i;
++
++ if(i==pkeys)
++ {
++ if(key < kParray[i-1])
++ ppos = i-1;
++ else
++ ppos = i;
++ }
++ else
++ ppos = i;
++
++
++ if(ppos) /* There is another leaf to the left */
++ {
++ spage = ajBtreeCacheRead(cache,pParray[ppos-1]);
++ sbuf = spage->buf;
++ GBT_NKEYS(sbuf,&skeys);
++ }
++
++ if(i && skeys != order-1) /* There is space in the left leaf */
++ {
++ /* ajDebug("Left shift\n"); */
++ btreeGetNumKeys(cache,tbuf,&kTarray,&pTarray);
++ if(skeys)
++ btreeGetNumKeys(cache,sbuf,&kSarray,&pSarray);
++
++ i = 0;
++ while(pParray[i] != tpage->pageno)
++ ++i;
++ --i;
++
++ pkeypos = i;
++
++ kSarray[skeys] = kParray[pkeypos];
++ pSarray[skeys+1] = pTarray[0];
++ ++skeys;
++ --tkeys;
++ kParray[pkeypos] = kTarray[0];
++ for(i=0;i<tkeys;++i)
++ {
++ kTarray[i] = kTarray[i+1];
++ pTarray[i] = pTarray[i+1];
++ }
++ pTarray[i] = pTarray[i+1];
++ pTarray[i+1] = 0L;
++
++ btreeWriteNumNode(cache,spage,kSarray,pSarray,skeys);
++ btreeWriteNumNode(cache,tpage,kTarray,pTarray,tkeys);
++ btreeWriteNumNode(cache,ppage,kParray,pParray,pkeys);
++ if(!ppage->pageno)
++ ppage->dirty = BT_LOCK;
++
++ i = 0;
++ while(i!=pkeys && key >= kParray[i])
++ ++i;
++ if(i==pkeys)
++ {
++ if(key < kParray[i-1])
++ blockno = pParray[i-1];
++ else
++ blockno = pParray[i];
++ }
++ else
++ blockno = pParray[i];
++
++ if(blockno == spage->pageno)
++ {
++ *retpage = spage;
++ karray = kSarray;
++ parray = pSarray;
++ n = skeys;
++ }
++ else
++ {
++ karray = kTarray;
++ parray = pTarray;
++ n = tkeys;
++ }
++
++
++ i = 0;
++ while(i!=n && key >= karray[i])
++ ++i;
++ if(i==n)
++ {
++ if(key < karray[i-1])
++ blockno = parray[i-1];
++ else
++ blockno = parray[i];
++ }
++ else
++ blockno = parray[i];
++
++ btreeDeallocSecArray(cache,array);
++ btreeDeallocSecArray(cache,array2);
++ btreeDeallocSecArray(cache,array3);
++
++ /* ajDebug("... returns blockno (a) %Ld\n",blockno); */
++
++ return blockno;
++ }
++
++
++ if(ppos != pkeys) /* There is a right node */
++ {
++ spage = ajBtreeCacheRead(cache,pParray[ppos+1]);
++ sbuf = spage->buf;
++ GBT_NKEYS(sbuf,&skeys);
++ }
++
++
++ /* Space in the right leaf */
++ if(ppos != pkeys && skeys != order-1)
++ {
++ /* ajDebug("Right shift\n"); */
++ btreeGetNumKeys(cache,tbuf,&kTarray,&pTarray);
++ btreeGetNumKeys(cache,sbuf,&kSarray,&pSarray);
++
++ i = 0;
++ while(pParray[i] != tpage->pageno)
++ ++i;
++ pkeypos = i;
++
++ pSarray[skeys+1] = pSarray[skeys];
++ for(i=skeys-1;i>-1;--i)
++ {
++ kSarray[i+1] = kSarray[i];
++ pSarray[i+1] = pSarray[i];
++ }
++ kSarray[0] = kParray[pkeypos];
++ pSarray[0] = pTarray[tkeys];
++ kParray[pkeypos] = kTarray[tkeys-1];
++ ++skeys;
++ --tkeys;
++ pTarray[tkeys+1] = 0L;
++
++ btreeWriteNumNode(cache,spage,kSarray,pSarray,skeys);
++ btreeWriteNumNode(cache,tpage,kTarray,pTarray,tkeys);
++ btreeWriteNumNode(cache,ppage,kParray,pParray,pkeys);
++ if(!ppage->pageno)
++ ppage->dirty = BT_LOCK;
++
++ i = 0;
++ while(i!=pkeys && key >= kParray[i])
++ ++i;
++ if(i==pkeys)
++ {
++ if(key < kParray[i-1])
++ blockno = pParray[i-1];
++ else
++ blockno = pParray[i];
++ }
++ else
++ blockno = pParray[i];
++
++ if(blockno == spage->pageno)
++ {
++ *retpage = spage;
++ karray = kSarray;
++ parray = pSarray;
++ n = skeys;
++ }
++ else
++ {
++ karray = kTarray;
++ parray = pTarray;
++ n = tkeys;
++ }
++
++ i = 0;
++ while(i!=n && key >= karray[i])
++ ++i;
++ if(i==n)
++ {
++ if(key < karray[i-1])
++ blockno = parray[i-1];
++ else
++ blockno = parray[i];
++ }
++ else
++ blockno = parray[i];
++
++ btreeDeallocSecArray(cache,array);
++ btreeDeallocSecArray(cache,array2);
++ btreeDeallocSecArray(cache,array3);
++
++ /* ajDebug("... returns blockno (b) %Ld\n",blockno); */
++
++ return blockno;
++ }
++
++
++ btreeDeallocSecArray(cache,array);
++ btreeDeallocSecArray(cache,array2);
++ btreeDeallocSecArray(cache,array3);
++
++ /* ajDebug("... returns 0L\n"); */
++
++ return 0L;
++}
++
++
++
++
++void ajBtreeInsertNum(AjPBtcache cache, AjPBtNumId num, AjPBtpage page)
++{
++ unsigned char *buf = NULL;
++ ajint order;
++ AjPBtMem array = NULL;
++ ajlong key;
++ AjPBtpage spage = NULL;
++ AjPBtpage parent = NULL;
++ ajint nkeys = 0;
++ ajint nodetype = 0;
++ ajlong *karray = NULL;
++ ajlong *parray = NULL;
++ AjPNumBucket lbucket = NULL;
++ AjPNumBucket rbucket = NULL;
++ ajlong lblockno;
++ ajlong rblockno;
++ ajlong blockno;
++ ajlong shift;
++ ajint n;
++ ajint i;
++
++ /* ajDebug("In ajBtreeInsertNum\n"); */
++
++ key = num->offset;
++
++ spage = ajBtreeNumFindInsert(cache,key);
++ buf = spage->buf;
++
++ GBT_NKEYS(buf,&nkeys);
++ GBT_NODETYPE(buf,&nodetype);
++
++ order = cache->sorder;
++
++ array = btreeAllocSecArray(cache);
++ karray = array->overflows;
++ parray = array->parray;
++
++ if(!nkeys)
++ {
++ lbucket = btreeNumBucketNew(0);
++ rbucket = btreeNumBucketNew(0);
++
++ lblockno = cache->totsize;
++ btreeWriteNumBucket(cache,lbucket,lblockno);
++ rblockno = cache->totsize;
++ btreeWriteNumBucket(cache,rbucket,rblockno);
++
++ parray[0] = lblockno;
++ parray[1] = rblockno;
++ karray[0] = key;
++
++ btreeWriteNumNode(cache,spage,karray,parray,1);
++
++ btreeNumBucketDel(&lbucket);
++ btreeNumBucketDel(&rbucket);
++
++ btreeAddToNumBucket(cache,rblockno,num);
++
++ btreeDeallocSecArray(cache,array);
++
++ return;
++ }
++
++
++ btreeGetNumKeys(cache,buf,&karray,&parray);
++ i=0;
++ while(i != nkeys && key >= karray[i])
++ ++i;
++ if(i==nkeys)
++ {
++ if(key < karray[i-1])
++ blockno = parray[i-1];
++ else
++ blockno = parray[i];
++ }
++ else
++ blockno = parray[i];
++
++ if(nodetype != BT_ROOT)
++ if((shift = btreeNumInsertShift(cache,&spage,key)))
++ blockno = shift;
++
++
++ buf = spage->buf;
++ n = btreeNumInNumBucket(cache,blockno);
++
++ if(n == cache->snperbucket)
++ {
++ if(btreeReorderNumBuckets(cache,spage))
++ {
++ GBT_NKEYS(buf,&nkeys);
++ btreeGetNumKeys(cache,buf,&karray,&parray);
++ i=0;
++ while(i != nkeys && key >= karray[i])
++ ++i;
++ if(i==nkeys)
++ {
++ if(key < karray[i-1])
++ blockno = parray[i-1];
++ else
++ blockno = parray[i];
++ }
++ else
++ blockno = parray[i];
++ }
++ else
++ {
++ parent = btreeNumSplitLeaf(cache,spage);
++ spage = ajBtreeNumFindInsert(cache,key);
++ buf = spage->buf;
++
++ btreeGetNumKeys(cache,buf,&karray,&parray);
++ GBT_NKEYS(buf,&nkeys);
++
++ i=0;
++ while(i != nkeys && key >= karray[i])
++ ++i;
++ if(i==nkeys)
++ {
++ if(key < karray[i-1])
++ blockno = parray[i-1];
++ else
++ blockno = parray[i];
++ }
++ else
++ blockno = parray[i];
++ }
++ }
++
++ btreeAddToNumBucket(cache,blockno,num);
++
++ btreeDeallocSecArray(cache,array);
++
++ ++cache->count;
++
++ return;
++}
++
++
++
++
++/* @funcstatic btreeNumSplitLeaf *********************************************
++**
++** Split a leaf and propagate up if necessary
++**
++** @param [u] cache [AjPBtcache] cache
++** @param [u] spage [AjPBtpage] page
++**
++** @return [AjPBtpage] pointer to a parent page
++** @@
++******************************************************************************/
++
++static AjPBtpage btreeNumSplitLeaf(AjPBtcache cache, AjPBtpage spage)
++{
++ ajint nkeys = 0;
++ ajint order = 0;
++ ajint totalkeys = 0;
++ ajint bentries = 0;
++ ajint keylimit = 0;
++ ajint nodetype = 0;
++
++ ajint rootnodetype = 0;
++
++ ajint i;
++ ajint j;
++
++ AjPBtpage lpage = NULL;
++ AjPBtpage rpage = NULL;
++ AjPBtpage page = NULL;
++
++ ajlong mediankey = 0L;
++ ajlong mediangtr = 0L;
++ ajlong medianless = 0L;
++
++ AjPBtNumId bid = NULL;
++
++ unsigned char *buf = NULL;
++ unsigned char *lbuf = NULL;
++ unsigned char *rbuf = NULL;
++
++ AjPList idlist = NULL;
++
++ AjPNumBucket *buckets = NULL;
++ AjPNumBucket cbucket = NULL;
++
++ ajlong *karray = NULL;
++ ajlong *parray = NULL;
++
++ ajint keypos = 0;
++ ajint lno = 0;
++ ajint rno = 0;
++
++ ajint bucketlimit = 0;
++ ajint maxnperbucket = 0;
++ ajint nperbucket = 0;
++ ajint bucketn = 0;
++ ajint count = 0;
++
++ ajlong lblockno = 0L;
++ ajlong rblockno = 0L;
++ ajlong overflow = 0L;
++ ajlong prevsave = 0L;
++
++ ajlong zero = 0L;
++ ajlong join = 0L;
++
++ ajlong lv = 0L;
++ ajint v = 0;
++
++ AjPBtMem array = NULL;
++
++
++ /* ajDebug("In btreeNumSplitLeaf\n"); */
++
++ order = cache->sorder;
++ nperbucket = cache->snperbucket;
++
++ array = btreeAllocSecArray(cache);
++ karray = array->overflows;
++ parray = array->parray;
++
++ buf = spage->buf;
++ lbuf = buf;
++
++ GBT_NKEYS(buf,&nkeys);
++ GBT_NODETYPE(buf,&rootnodetype);
++
++ if(rootnodetype == BT_ROOT)
++ {
++ /* ajDebug("Root leaf splitting\n"); */
++ lblockno = cache->totsize;
++ lpage = ajBtreeCacheWrite(cache,lblockno);
++ lpage->pageno = cache->totsize;
++ cache->totsize += cache->pagesize;
++ lbuf = lpage->buf;
++ lv = cache->secrootblock;
++ SBT_PREV(lbuf,lv);
++ }
++ else
++ {
++ lblockno = spage->pageno;
++ lpage = spage;
++ }
++
++ lpage->dirty = BT_LOCK;
++
++ rblockno = cache->totsize;
++ rpage = ajBtreeCacheWrite(cache,rblockno);
++ rpage->pageno = cache->totsize;
++ cache->totsize += cache->pagesize;
++ rbuf = rpage->buf;
++ rpage->dirty = BT_LOCK;
++
++ if(rootnodetype == BT_ROOT)
++ {
++ lv = zero;
++ SBT_RIGHT(rbuf,lv);
++ lv = zero;
++ SBT_LEFT(lbuf,lv);
++ }
++ else
++ {
++ GBT_RIGHT(lbuf,&join);
++ lv = join;
++ SBT_RIGHT(rbuf,lv);
++ }
++ lv = lblockno;
++ SBT_LEFT(rbuf,lv);
++ lv = rblockno;
++ SBT_RIGHT(lbuf,lv);
++
++
++ btreeGetNumKeys(cache,buf,&karray,&parray);
++
++
++ keylimit = nkeys+1;
++ AJCNEW0(buckets,keylimit);
++ for(i=0;i<keylimit;++i)
++ buckets[i] = btreeReadNumBucket(cache,parray[i]);
++
++ idlist = ajListNew();
++ for(i=0;i<keylimit;++i)
++ {
++ bentries = buckets[i]->Nentries;
++ for(j=0;j<bentries;++j)
++ ajListPush(idlist,(void *)buckets[i]->NumId[j]);
++ AJFREE(buckets[i]->NumId);
++ AJFREE(buckets[i]);
++ }
++
++
++
++ ajListSort(idlist,btreeNumIdCompare);
++ AJFREE(buckets);
++
++ totalkeys = ajListLength(idlist);
++
++ keypos = totalkeys / 2;
++
++ lno = keypos;
++ rno = totalkeys - lno;
++
++ maxnperbucket = nperbucket >> 1;
++ ++maxnperbucket;
++
++ cbucket = btreeNumBucketNew(maxnperbucket);
++
++ bucketn = lno / maxnperbucket;
++ if(lno % maxnperbucket)
++ ++bucketn;
++ bucketlimit = bucketn - 1;
++
++
++ count = 0;
++ for(i=0;i<bucketlimit;++i)
++ {
++ cbucket->Nentries = 0;
++ for(j=0;j<maxnperbucket;++j)
++ {
++ ajListPop(idlist,(void **)&bid);
++
++ cbucket->NumId[j]->offset = bid->offset;
++ cbucket->NumId[j]->refoffset = bid->refoffset;
++ cbucket->NumId[j]->dbno = bid->dbno;
++
++ ++count;
++ ++cbucket->Nentries;
++ AJFREE(bid);
++ }
++ ajListPeek(idlist,(void **)&bid);
++
++ karray[i] = bid->offset;
++
++ if(!parray[i])
++ parray[i] = cache->totsize;
++ btreeWriteNumBucket(cache,cbucket,parray[i]);
++ }
++
++ cbucket->Nentries = 0;
++
++ j = 0;
++ while(count != lno)
++ {
++ ajListPop(idlist,(void **)&bid);
++
++ cbucket->NumId[j]->offset = bid->offset;
++ cbucket->NumId[j]->refoffset = bid->refoffset;
++ cbucket->NumId[j]->dbno = bid->dbno;
++
++ ++j;
++ ++count;
++
++
++ ++cbucket->Nentries;
++ AJFREE(bid);
++ }
++
++ if(!parray[i])
++ parray[i] = cache->totsize;
++ btreeWriteNumBucket(cache,cbucket,parray[i]);
++
++ nkeys = bucketn - 1;
++ v = nkeys;
++ SBT_NKEYS(lbuf,v);
++ nodetype = BT_LEAF;
++ v = nodetype;
++ SBT_NODETYPE(lbuf,v);
++ GBT_PREV(lbuf,&prevsave);
++ lpage->dirty = BT_DIRTY;
++ btreeWriteNumNode(cache,lpage,karray,parray,nkeys);
++
++ ajListPeek(idlist,(void **)&bid);
++ mediankey = bid->offset;
++
++ bucketn = rno / maxnperbucket;
++ if(rno % maxnperbucket)
++ ++bucketn;
++ bucketlimit = bucketn - 1;
++
++ for(i=0;i<bucketlimit;++i)
++ {
++ cbucket->Nentries = 0;
++ for(j=0;j<maxnperbucket;++j)
++ {
++ ajListPop(idlist,(void **)&bid);
++
++ cbucket->NumId[j]->offset = bid->offset;
++ cbucket->NumId[j]->refoffset = bid->refoffset;
++ cbucket->NumId[j]->dbno = bid->dbno;
++
++ ++cbucket->Nentries;
++ AJFREE(bid);
++ }
++
++ ajListPeek(idlist,(void **)&bid);
++ karray[i] = bid->offset;
++
++ parray[i] = cache->totsize;
++ btreeWriteNumBucket(cache,cbucket,parray[i]);
++ }
++
++ cbucket->Nentries = 0;
++
++ j = 0;
++ while(ajListPop(idlist,(void**)&bid))
++ {
++ cbucket->NumId[j]->offset = bid->offset;
++ cbucket->NumId[j]->refoffset = bid->refoffset;
++ cbucket->NumId[j]->dbno = bid->dbno;
++ ++j;
++
++
++ ++cbucket->Nentries;
++ AJFREE(bid);
++ }
++
++ parray[i] = cache->totsize;
++ btreeWriteNumBucket(cache,cbucket,parray[i]);
++
++ nkeys = bucketn - 1;
++
++ v = nkeys;
++ SBT_NKEYS(rbuf,v);
++ nodetype = BT_LEAF;
++ v = nodetype;
++ SBT_NODETYPE(rbuf,v);
++ lv = prevsave;
++ SBT_PREV(rbuf,lv);
++ lv = overflow;
++ SBT_OVERFLOW(rbuf,lv);
++
++ btreeWriteNumNode(cache,rpage,karray,parray,nkeys);
++ rpage->dirty = BT_DIRTY;
++
++ cbucket->Nentries = maxnperbucket;
++ btreeNumBucketDel(&cbucket);
++ ajListDel(&idlist);
++
++
++
++ medianless = lblockno;
++ mediangtr = rblockno;
++
++
++ if(rootnodetype == BT_ROOT)
++ {
++ karray[0] = mediankey;
++ parray[0]=lblockno;
++ parray[1]=rblockno;
++ nkeys = 1;
++ spage->dirty = BT_DIRTY;
++ btreeWriteNumNode(cache,spage,karray,parray,nkeys);
++
++ ++cache->slevel;
++ lv = cache->slevel;
++ SBT_RIGHT(buf,lv);
++ spage->dirty = BT_LOCK;
++
++ btreeDeallocSecArray(cache,array);
++
++ return spage;
++ }
++
++ btreeDeallocSecArray(cache,array);
++
++
++ page = ajBtreeCacheRead(cache,prevsave);
++ btreeNumInsertKey(cache,page,mediankey,medianless,mediangtr);
++
++
++ page = ajBtreeCacheRead(cache,prevsave);
++
++ return page;
++}
++
++
++
++
++/* @func ajBtreeFreePriArray *******************************************
++**
++** Free karray and parray arrays for a primary key
++**
++** @param [rw] cache [AjPBtcache] cache
++**
++** @return [void]
++** @@
++******************************************************************************/
++
++void ajBtreeFreePriArray(AjPBtcache cache)
++{
++ AjPBtMem p;
++ AjPBtMem next;
++ ajint i;
++
++ /* ajDebug("In ajBtreeFreePriArray\n"); */
++
++ if(!cache->bmem)
++ return;
++
++ p = cache->bmem;
++ next = p->next;
++
++ while(next)
++ {
++ AJFREE(p->parray);
++ AJFREE(p->overflows);
++ for(i=0;i<cache->order;++i)
++ ajStrDel(&p->karray[i]);
++ AJFREE(p->karray);
++ AJFREE(p);
++ p = next;
++ next = p->next;
++ }
++
++
++ AJFREE(p->parray);
++ AJFREE(p->overflows);
++ for(i=0;i<cache->order;++i)
++ ajStrDel(&p->karray[i]);
++ AJFREE(p->karray);
++ AJFREE(p);
++
++ cache->bmem = NULL;
++ cache->tmem = NULL;
++
++ return;
++}
++
++
++
++
++/* @func ajBtreeFreeSecArray *******************************************
++**
++** Free karray and parray arrays for a secondary key
++**
++** @param [rw] cache [AjPBtcache] cache
++**
++** @return [void]
++** @@
++******************************************************************************/
++
++void ajBtreeFreeSecArray(AjPBtcache cache)
++{
++ AjPBtMem p;
++ AjPBtMem next;
++ ajint i;
++
++ /* ajDebug("In ajBtreeFreeSecArray\n"); */
++
++ if(!cache->bsmem)
++ return;
++
++ p = cache->bsmem;
++ next = p->next;
++
++ while(next)
++ {
++ AJFREE(p->parray);
++ AJFREE(p->overflows);
++ for(i=0;i<cache->sorder;++i)
++ ajStrDel(&p->karray[i]);
++ AJFREE(p->karray);
++ AJFREE(p);
++
++ p = next;
++ next = p->next;
++ }
++
++
++ AJFREE(p->parray);
++ AJFREE(p->overflows);
++ for(i=0;i<cache->sorder;++i)
++ ajStrDel(&p->karray[i]);
++ AJFREE(p->karray);
++ AJFREE(p);
++
++ cache->bsmem = NULL;
++ cache->tsmem = NULL;
++
++ return;
++}
++
++
++
++
++/* @func ajBtreeHybLeafList ********************************************
++**
++** Read the leaves of a secondary hybrid tree
++**
++** @param [u] cache [AjPBtcache] cache
++** @param [r] rootblock [ajlong] root page of secondary tree
++** @param [r] idname [AjPStr] id name
++** @param [r] list [AjPList] list to add BtIDs to
++**
++** @return [void]
++** @@
++******************************************************************************/
++
++void ajBtreeHybLeafList(AjPBtcache cache, ajlong rootblock, AjPStr idname,
++ AjPList list)
++{
++ AjPBtId id = NULL;
++
++ ajint order;
++ ajlong *karray;
++ ajlong *parray;
++ AjPBtpage page;
++ unsigned char *buf;
++ ajint nodetype;
++ ajint i;
++ ajint j;
++ ajlong level = 0L;
++
++ AjPNumBucket bucket;
++ ajint nkeys;
++ ajlong right;
++ AjPBtMem array = NULL;
++
++ order = cache->sorder;
++
++ array = btreeAllocSecArray(cache);
++ karray = array->overflows;
++ parray = array->parray;
++
++ page = ajBtreeCacheRead(cache, rootblock);
++ buf = page->buf;
++
++ GBT_RIGHT(buf,&level);
++ cache->slevel = (ajint) level;
++
++ btreeGetNumKeys(cache,buf,&karray,&parray);
++ GBT_NODETYPE(buf,&nodetype);
++
++ while(nodetype != BT_LEAF && cache->slevel != 0)
++ {
++ page = ajBtreeCacheRead(cache,parray[0]);
++ buf = page->buf;
++ btreeGetNumKeys(cache,buf,&karray,&parray);
++ GBT_NODETYPE(buf,&nodetype);
++ }
++
++ do
++ {
++ GBT_NKEYS(buf,&nkeys);
++ for(i=0;i<nkeys+1;++i)
++ {
++ bucket = btreeReadNumBucket(cache,parray[i]);
++ for(j=0;j<bucket->Nentries;++j)
++ {
++ id = ajBtreeIdNew();
++ ajStrAssS(&id->id,idname);
++ id->offset = bucket->NumId[j]->offset;
++ id->refoffset = bucket->NumId[j]->refoffset;
++ id->dbno = bucket->NumId[j]->dbno;
++ ajListPush(list, (void*)id);
++ }
++ btreeNumBucketDel(&bucket);
++ }
++
++ right = 0L;
++ if(cache->slevel)
++ {
++ GBT_RIGHT(buf,&right);
++ if(right)
++ {
++ page = ajBtreeCacheRead(cache,right);
++ buf = page->buf;
++ btreeGetNumKeys(cache,buf,&karray,&parray);
++ }
++ }
++ } while(right);
++
++ btreeDeallocSecArray(cache,array);
++
++ return;
++}
++