Example #1
0
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;
}