int mkheap_putAndGet_reader(MKHeap *mkheap, MKEntry *out) { int n; bool fOK; int ins; /* First, check to see if the heap is empty */ MKEntry *e = mkheap_peek(mkheap); if (!e) { mke_set_empty(out); return -1; } Assert(!mke_is_empty(e)); /* _reader code is not compatible with the run code */ Assert(mke_get_run(e) == 0); /* Figure out the reader that provided the current top of the heap */ n = mke_get_reader(e); Assert(n >= 0 && n < mkheap->nreader); /* Read in a new one that will replenish the current top of the heap */ fOK = mkheap->readers[n].reader(mkheap->readers[n].mkhr_ctxt, out); if (fOK) { mke_set_reader(out, n); } else { mke_set_empty(out); } Assert(mke_get_run(out) == 0); /* * now insert it and pop the top of the heap or, if equal to the top, just * don't do the insert * * So, on success (ins >= 0) then *out will refer to the element which is * NOT in the heap (which could be either the result of mkheap_peek above * OR the result of the .reader() call, depending) */ ins = mkheap_putAndGet_impl(mkheap, out); Assert(ins >= 0); Assert(mke_get_reader(out) == n); Assert(!mke_is_empty(out)); return ins; }
/** * Return the top of the heap or NULL if the heap is empty */ MKEntry *mkheap_peek(MKHeap *mkheap) { if(mkheap->count == 0) return NULL; Assert(!mke_is_empty(mkheap->p)); return mkheap->p; }
/* * Insert with run. Runs provide a way for values SMALLER than * the top element to be inserted -- they get pushed to * the next run and then insert (and since 'run' will be part of * their comparison then they will definitely be inserted). * * If the entry is greater than the top, we can * insert it using the same run. * * If the entry is less than the heap top, we will * put it to the next run, and insert it. * * Both non-equal cases will ends up with the entry * inserted, and return a postive number. * * If the entry equal to the heaptop, we will NOT * insert the element, but return 0. Caller need to * handle this. In case of sort build runs, that means * e can be written to current run immediately. * * Note: run must be passed in as equal to the run of the top element of the heap. * Perhaps change the function interface so this is not required (make the * function itself peek at the top run and return whether or not the top run * of the heap was increased by this action */ int mkheap_putAndGet_run(MKHeap *mkheap, MKEntry *e, int16 run) { int ins; /* try insertion into specified run */ Assert(!mke_is_empty(e)); mke_set_run(e, run); ins = mkheap_putAndGet_impl(mkheap, e); /* success! Greater than or Equal. */ if(ins >= 0) { return ins; } /* failed, put into next run */ mke_set_run(e, run+1); ins = mkheap_putAndGet_impl(mkheap, e); Assert(ins > 0); return ins; }
void mkheap_verify_heap(MKHeap *heap, int top) { int empty_cnt = 0; int i; MKEntry e; if(heap->count == 0) return; Assert(heap->count > 0); Assert(heap->maxentry > 0); Assert(heap->count <= heap->maxentry); Assert(heap->maxentry <= heap->alloc_size); e = heap->p[0]; mke_set_lv(&e, 0); mke_clear_refc_copied(&e); /* Checking for lvtops */ for(i=0; i<mke_get_lv(heap->p); ++i) { int c; /* Too much trouble dealing with ref counters. Just don't */ /* if(!mke_test_flag(heap->lvtops+i, MKE_RefCnt | MKE_Copied)) { */ mke_set_lv(&e, i); if ( heap->mkctxt->fetchForPrep) tupsort_prepare(&e, heap->mkctxt, i); c = mkheap_compare(heap, &e, heap->lvtops+i); Assert(c==0); /* } */ } /* Verify Heap property */ for(i=top; i<heap->maxentry; ++i) { int left = LEFT(i); int right = RIGHT(i); int cl, cr; if(mke_is_empty(heap->p+i)) ++empty_cnt; if(left >= heap->maxentry) continue; cl = mkheap_compare(heap, heap->p+i, heap->p+left); Assert(cl<=0); if(i==0 && cl==0) Assert(mke_get_lv(heap->p) == heap->mkctxt->total_lv-1); if(right >= heap->maxentry) continue; cr = mkheap_compare(heap, heap->p+i, heap->p+right); Assert(cr<=0); if(i==0 && cr==0) Assert(mke_get_lv(heap->p) == heap->mkctxt->total_lv-1); } }
/* * Insert an entry and perhaps return the top element of the heap in *e * * Comparison happens from the specified level to the end of levels, as needed: * Return < 0 if smaller than heap top; *e is unchanged * Return = 0 if eq to heap top ; *e is unchanged (but will have value equal to the heap top) * Return > 0 if successfully inserted; *e is populated with the removed heap top * * If 0 would be returned but the heap is marked as needing uniqueness enforcement, error is generated instead */ static int mkheap_putAndGet_impl(MKHeap *mkheap, MKEntry *e) { int c = 0; int toplv; MKEntry tmp; /* can't put+get from an empty heap */ Assert(mkheap->count > 0); if ( mkheap->mkctxt->enforceUnique && mke_has_duplicates_with_root(mkheap)) { /** * See NOTE ON UNIQUENESS CHECKING in the comment at the top of the file * for information about why we check for duplicates here */ ERROR_UNIQUENESS_VIOLATED(); } if(mke_is_empty(e)) { /* adding an empty (sentinel): just remove from count and fallthrough to where top is removed */ --mkheap->count; } else if (mke_get_run(e) != mke_get_run(mkheap->p)) { /* this code assumes that the new one, with lower run, is LARGER than the top -- so it must be larger run */ Assert(mke_get_run(e) > mke_get_run(mkheap->p)); /* when the runs differ it is because we attempted once with the runs equal. * So if level is zero then: the level was zero AND validly prepared for the previous run -- and there is no need to prep again */ if ( mke_get_lv(e) != 0 ) { /* Not same run, at least prepare lv 0 */ if ( mkheap->mkctxt->fetchForPrep) tupsort_prepare(e, mkheap->mkctxt, 0); mke_set_lv(e, 0); } /* now fall through and let top be returned, new one is also inserted so no change to count */ } else { /* same run so figure out where it fits in relation to the heap top */ int lv = 0; toplv = mke_get_lv(mkheap->p); mke_set_lv(e, lv); /* populate level until we differ from the top element of the heap */ while (lv < toplv) { if ( mkheap->mkctxt->fetchForPrep) tupsort_prepare(e, mkheap->mkctxt, lv); c = mkheap_compare(mkheap, e, mkheap->lvtops+lv); if(c!=0) break; mke_set_lv(e, ++lv); } /* smaller than top */ if(c<0) return -1; /* we have not done e->lv == toplv yet since we increment at the end of the previous loop. Do it now. */ Assert(mke_get_lv(e) == lv); if(lv == toplv) { if ( mkheap->mkctxt->fetchForPrep) tupsort_prepare(e, mkheap->mkctxt, lv); c = mkheap_compare(mkheap, e, mkheap->p); if(c < 0) return -1; } if(c==0) { /* * Equal and at top level. * * This means that e is less-than/equal to all entries except the heap top. */ Assert(mke_get_lv(e) == lv); Assert(lv == mke_get_lv(mkheap->p)); /* * Expand more levels of lvtop in the current top and the new one until we detect a difference. */ while(lv < mkheap->mkctxt->total_lv - 1) { mkheap_save_lvtop(mkheap); ++lv; /* expand top */ if ( mkheap->mkctxt->fetchForPrep) tupsort_prepare(mkheap->p, mkheap->mkctxt, lv); /* expand new element */ if ( mkheap->mkctxt->fetchForPrep) tupsort_prepare(e, mkheap->mkctxt, lv); mke_set_lv(mkheap->p, lv); mke_set_lv(e, lv); c = mkheap_compare(mkheap, e, mkheap->p); if(c != 0) break; } if(c<=0) { /* if new one is less than current top then we just return that negative comparison */ /* if new one equals the current top then we could do an insert and immediate removal -- but it * won't matter so we simply return right away, leaving *e untouched */ /* enforce uniqueness first */ if(c == 0 && mkheap->mkctxt->enforceUnique) { ERROR_UNIQUENESS_VIOLATED(); } return c; } } } /* Now, I am bigger than top but not definitely smaller/equal to all other entries * * So we will: * return top as *e * do heap shuffling to restore heap ordering */ tmp = *e; *e = mkheap->p[0]; /* Sift down a hole to bottom of (current or next) run, depends on tmp.run */ mkheap_siftdown(mkheap, 0, &tmp); if(mkheap_need_heapify(mkheap)) mkheap_heapify(mkheap, false); if (mkheap->count > 0) { mkheap_update_lvtops(mkheap); } #ifdef USE_ASSERT_CHECKING if(gp_mk_sort_check) mkheap_verify_heap(mkheap, 0); #endif return 1; }