#include "sqliteInt.h"
#define ROWSET_ALLOCATION_SIZE 1024
#define ROWSET_ENTRY_PER_CHUNK \
((ROWSET_ALLOCATION_SIZE-8)/sizeof(struct RowSetEntry))
struct RowSetEntry {
i64 v;
struct RowSetEntry *pRight;
struct RowSetEntry *pLeft;
};
struct RowSetChunk {
struct RowSetChunk *pNextChunk;
struct RowSetEntry aEntry[ROWSET_ENTRY_PER_CHUNK];
};
struct RowSet {
struct RowSetChunk *pChunk;
sqlite3 *db;
struct RowSetEntry *pEntry;
struct RowSetEntry *pLast;
struct RowSetEntry *pFresh;
struct RowSetEntry *pForest;
u16 nFresh;
u16 rsFlags;
int iBatch;
};
#define ROWSET_SORTED 0x01
#define ROWSET_NEXT 0x02
RowSet *sqlite3RowSetInit(sqlite3 *db){
RowSet *p = sqlite3DbMallocRawNN(db, sizeof(*p));
if( p ){
int N = sqlite3DbMallocSize(db, p);
p->pChunk = 0;
p->db = db;
p->pEntry = 0;
p->pLast = 0;
p->pForest = 0;
p->pFresh = (struct RowSetEntry*)(ROUND8(sizeof(*p)) + (char*)p);
p->nFresh = (u16)((N - ROUND8(sizeof(*p)))/sizeof(struct RowSetEntry));
p->rsFlags = ROWSET_SORTED;
p->iBatch = 0;
}
return p;
}
void sqlite3RowSetClear(void *pArg){
RowSet *p = (RowSet*)pArg;
struct RowSetChunk *pChunk, *pNextChunk;
for(pChunk=p->pChunk; pChunk; pChunk = pNextChunk){
pNextChunk = pChunk->pNextChunk;
sqlite3DbFree(p->db, pChunk);
}
p->pChunk = 0;
p->nFresh = 0;
p->pEntry = 0;
p->pLast = 0;
p->pForest = 0;
p->rsFlags = ROWSET_SORTED;
}
void sqlite3RowSetDelete(void *pArg){
sqlite3RowSetClear(pArg);
sqlite3DbFree(((RowSet*)pArg)->db, pArg);
}
static struct RowSetEntry *rowSetEntryAlloc(RowSet *p){
assert( p!=0 );
if( p->nFresh==0 ){
struct RowSetChunk *pNew;
pNew = sqlite3DbMallocRawNN(p->db, sizeof(*pNew));
if( pNew==0 ){
return 0;
}
pNew->pNextChunk = p->pChunk;
p->pChunk = pNew;
p->pFresh = pNew->aEntry;
p->nFresh = ROWSET_ENTRY_PER_CHUNK;
}
p->nFresh--;
return p->pFresh++;
}
void sqlite3RowSetInsert(RowSet *p, i64 rowid){
struct RowSetEntry *pEntry;
struct RowSetEntry *pLast;
assert( p!=0 && (p->rsFlags & ROWSET_NEXT)==0 );
pEntry = rowSetEntryAlloc(p);
if( pEntry==0 ) return;
pEntry->v = rowid;
pEntry->pRight = 0;
pLast = p->pLast;
if( pLast ){
if( rowid<=pLast->v ){
p->rsFlags &= ~ROWSET_SORTED;
}
pLast->pRight = pEntry;
}else{
p->pEntry = pEntry;
}
p->pLast = pEntry;
}
static struct RowSetEntry *rowSetEntryMerge(
struct RowSetEntry *pA,
struct RowSetEntry *pB
){
struct RowSetEntry head;
struct RowSetEntry *pTail;
pTail = &head;
assert( pA!=0 && pB!=0 );
for(;;){
assert( pA->pRight==0 || pA->v<=pA->pRight->v );
assert( pB->pRight==0 || pB->v<=pB->pRight->v );
if( pA->v<=pB->v ){
if( pA->v<pB->v ) pTail = pTail->pRight = pA;
pA = pA->pRight;
if( pA==0 ){
pTail->pRight = pB;
break;
}
}else{
pTail = pTail->pRight = pB;
pB = pB->pRight;
if( pB==0 ){
pTail->pRight = pA;
break;
}
}
}
return head.pRight;
}
static struct RowSetEntry *rowSetEntrySort(struct RowSetEntry *pIn){
unsigned int i;
struct RowSetEntry *pNext, *aBucket[40];
memset(aBucket, 0, sizeof(aBucket));
while( pIn ){
pNext = pIn->pRight;
pIn->pRight = 0;
for(i=0; aBucket[i]; i++){
pIn = rowSetEntryMerge(aBucket[i], pIn);
aBucket[i] = 0;
}
aBucket[i] = pIn;
pIn = pNext;
}
pIn = aBucket[0];
for(i=1; i<sizeof(aBucket)/sizeof(aBucket[0]); i++){
if( aBucket[i]==0 ) continue;
pIn = pIn ? rowSetEntryMerge(pIn, aBucket[i]) : aBucket[i];
}
return pIn;
}
static void rowSetTreeToList(
struct RowSetEntry *pIn,
struct RowSetEntry **ppFirst,
struct RowSetEntry **ppLast
){
assert( pIn!=0 );
if( pIn->pLeft ){
struct RowSetEntry *p;
rowSetTreeToList(pIn->pLeft, ppFirst, &p);
p->pRight = pIn;
}else{
*ppFirst = pIn;
}
if( pIn->pRight ){
rowSetTreeToList(pIn->pRight, &pIn->pRight, ppLast);
}else{
*ppLast = pIn;
}
assert( (*ppLast)->pRight==0 );
}
static struct RowSetEntry *rowSetNDeepTree(
struct RowSetEntry **ppList,
int iDepth
){
struct RowSetEntry *p;
struct RowSetEntry *pLeft;
if( *ppList==0 ){
return 0;
}
if( iDepth>1 ){
pLeft = rowSetNDeepTree(ppList, iDepth-1);
p = *ppList;
if( p==0 ){
return pLeft;
}
p->pLeft = pLeft;
*ppList = p->pRight;
p->pRight = rowSetNDeepTree(ppList, iDepth-1);
}else{
p = *ppList;
*ppList = p->pRight;
p->pLeft = p->pRight = 0;
}
return p;
}
static struct RowSetEntry *rowSetListToTree(struct RowSetEntry *pList){
int iDepth;
struct RowSetEntry *p;
struct RowSetEntry *pLeft;
assert( pList!=0 );
p = pList;
pList = p->pRight;
p->pLeft = p->pRight = 0;
for(iDepth=1; pList; iDepth++){
pLeft = p;
p = pList;
pList = p->pRight;
p->pLeft = pLeft;
p->pRight = rowSetNDeepTree(&pList, iDepth);
}
return p;
}
int sqlite3RowSetNext(RowSet *p, i64 *pRowid){
assert( p!=0 );
assert( p->pForest==0 );
if( (p->rsFlags & ROWSET_NEXT)==0 ){
if( (p->rsFlags & ROWSET_SORTED)==0 ){
p->pEntry = rowSetEntrySort(p->pEntry);
}
p->rsFlags |= ROWSET_SORTED|ROWSET_NEXT;
}
if( p->pEntry ){
*pRowid = p->pEntry->v;
p->pEntry = p->pEntry->pRight;
if( p->pEntry==0 ){
sqlite3RowSetClear(p);
}
return 1;
}else{
return 0;
}
}
int sqlite3RowSetTest(RowSet *pRowSet, int iBatch, sqlite3_int64 iRowid){
struct RowSetEntry *p, *pTree;
assert( pRowSet!=0 && (pRowSet->rsFlags & ROWSET_NEXT)==0 );
if( iBatch!=pRowSet->iBatch ){
p = pRowSet->pEntry;
if( p ){
struct RowSetEntry **ppPrevTree = &pRowSet->pForest;
if( (pRowSet->rsFlags & ROWSET_SORTED)==0 ){
p = rowSetEntrySort(p);
}
for(pTree = pRowSet->pForest; pTree; pTree=pTree->pRight){
ppPrevTree = &pTree->pRight;
if( pTree->pLeft==0 ){
pTree->pLeft = rowSetListToTree(p);
break;
}else{
struct RowSetEntry *pAux, *pTail;
rowSetTreeToList(pTree->pLeft, &pAux, &pTail);
pTree->pLeft = 0;
p = rowSetEntryMerge(pAux, p);
}
}
if( pTree==0 ){
*ppPrevTree = pTree = rowSetEntryAlloc(pRowSet);
if( pTree ){
pTree->v = 0;
pTree->pRight = 0;
pTree->pLeft = rowSetListToTree(p);
}
}
pRowSet->pEntry = 0;
pRowSet->pLast = 0;
pRowSet->rsFlags |= ROWSET_SORTED;
}
pRowSet->iBatch = iBatch;
}
for(pTree = pRowSet->pForest; pTree; pTree=pTree->pRight){
p = pTree->pLeft;
while( p ){
if( p->v<iRowid ){
p = p->pRight;
}else if( p->v>iRowid ){
p = p->pLeft;
}else{
return 1;
}
}
}
return 0;
}