// column.cpp --
// This is part of Metakit, the homepage is http://www.equi4.com/metakit.html

/** @file
 * Implements c4_Column, c4_ColOfInts, and c4_ColIter
 */

#include "header.h"
#include "column.h"
#include "persist.h"

#if !q4_INLINE
#endif

/////////////////////////////////////////////////////////////////////////////

#if (!defined(HAVE_MEMMOVE) && !defined(HAVE_BCOPY)) || (!HAVE_MEMMOVE && !HAVE_BCOPY)
// in case we have no library memmove, or one that can't handle overlap

void f4_memmove(void *to_, const void *from_, int n_)
{
    char *to = (char *)to_;
    const char *from = (const char *)from_;

    if (to + n_ <= from || from + n_ <= to) {
        memcpy(to, from, n_);
    } else if (to < from) {
        while (--n_ >= 0) {
            *to++ = *from++;
        }
    } else if (to > from) {
        while (--n_ >= 0) {
            to[n_] = from[n_];
        }
    }
}

#endif

/////////////////////////////////////////////////////////////////////////////
// c4_Column

c4_Column::c4_Column(c4_Persist *persist_) : _position(0)
    , _size(0)
    , _persist
        (persist_)
    , _gap(0)
    , _slack(0)
    , _dirty(false)
{
}

#if defined(q4_CHECK) && q4_CHECK

// debugging version to verify that the internal data is consistent
void c4_Column::Validate() const
{
    d4_assert(0 <= _slack && _slack < kSegMax);

    if (_segments.GetSize() == 0) {
        return;
    }
    // ok, not initialized

    d4_assert(_gap <= _size);

    int n = fSegIndex(_size + _slack);
    d4_assert(n == _segments.GetSize() - 1);

    t4_byte *p = (t4_byte *)_segments.GetAt(n);

    if (fSegRest(_size + _slack) == 0) {
        d4_assert(p == 0);
    } else {
        d4_assert(p != 0);
    }

    while (--n >= 0) {
        t4_byte *p = (t4_byte *)_segments.GetAt(n);
        d4_assert(p != 0);
    }
}

#else

// nothing, so inline this thing to avoid even the calling overhead
d4_inline void c4_Column::Validate() const
{
}

#endif

c4_Column::~c4_Column()
{
    Validate();
    ReleaseAllSegments();

    // this is needed to remove this column from the cache
    d4_assert(_slack == 0);
    FinishSlack();

    _slack = -1;   // bad value in case we try to set up again (!)
}

c4_Strategy &c4_Column::Strategy() const
{
    d4_assert(_persist != 0);

    return _persist->Strategy();
}

bool c4_Column::IsMapped() const
{
    return _position > 1 && _persist != nullptr && Strategy()._mapStart != nullptr;
}

bool c4_Column::UsesMap(const t4_byte *ptr_) const
{
    // the most common falsifying case is checked first
    return _persist != nullptr
           && ptr_ >= Strategy()._mapStart
           && Strategy()._dataSize != 0
           && ptr_ < Strategy()._mapStart + Strategy()._dataSize;
}

bool c4_Column::RequiresMap() const
{
    if (_persist != nullptr && Strategy()._mapStart != nullptr) {
        for (int i = _segments.GetSize(); --i >= 0;) {
            if (UsesMap((t4_byte *)_segments.GetAt(i))) {
                return true;
            }
        }
    }
    return false;
}

void c4_Column::ReleaseSegment(int index_)
{
    auto *p = (t4_byte *)_segments.GetAt(index_);
    if (!UsesMap(p)) {
        delete [] p;
    }
}

void c4_Column::ReleaseAllSegments()
{
    //for (int i = 0; i < _segments.GetSize(); ++i)
    for (int i = _segments.GetSize(); --i >= 0;) {
        ReleaseSegment(i);
    }
    // last one might be a null pointer

    _segments.SetSize(0);

    _gap = 0;
    _slack = 0;

    if (_size == 0) {
        _position = 0;
    }

    _dirty = false;
}

//@func Define where data is on file, or setup buffers (opt cleared).
void c4_Column::SetLocation(t4_i32 pos_, t4_i32 size_)
{
    d4_assert(size_ > 0 || pos_ == 0);

    ReleaseAllSegments();

    _position = pos_;
    _size = size_;

    //  There are two position settings:
    //
    //     0 = raw buffer, no file access
    //    >1 = file position from where data can be loaded on demand

    _dirty = pos_ == 0;
}

void c4_Column::PullLocation(const t4_byte * &ptr_)
{
    d4_assert(_segments.GetSize() == 0);

    _size = PullValue(ptr_);
    _position = 0;
    if (_size > 0) {
        _position = PullValue(ptr_);
        if (_position > 0) {
            d4_assert(_persist != 0);
            _persist->OccupySpace(_position, _size);
        }
    }

    _dirty = false;
}

//@func How many contiguous bytes are there at a specified position.
int c4_Column::AvailAt(t4_i32 offset_) const
{
    d4_assert(offset_ <= _size);
    d4_assert(_gap <= _size);

    t4_i32 limit = _gap;

    if (offset_ >= _gap) {
        offset_ += _slack;
        limit = _size + _slack;
    }

    int count = kSegMax - fSegRest(offset_);
    if (offset_ + count > limit) {
        count = (int)(limit - offset_);
    }

    // either some real data or it must be at the very end of all data
    d4_assert(0 < count && count <= kSegMax || count == 0 && offset_ == _size
              +_slack);
    return count;
}

void c4_Column::SetupSegments()
{
    d4_assert(_segments.GetSize() == 0);
    d4_assert(_gap == 0);
    d4_assert(_slack == 0);

    //  The last entry in the _segments array is either a partial block
    //  or a null pointer, so calling "fSegIndex(_size)" is always allowed.

    int n = fSegIndex(_size) + 1;
    _segments.SetSize(n);

    // treat last block differently if it is a partial entry
    int last = n;
    if (fSegRest(_size)) {
        --last;
    }
    // this block is partial, size is 1 .. kSegMax-1
    else {
        --n;
    }
    // the last block is left as a null pointer

    int id = -1;
    if (_position < 0) {
        // special aside id, figure out the real position
        d4_assert(_persist != 0);
        id = ~_position;
        _position = _persist->LookupAside(id);
        d4_assert(_position >= 0);
    }

    if (IsMapped()) {
        // setup for mapped files is quick, just fill in the pointers
        d4_assert(_position > 1);
        d4_assert(_position + (n - 1) *kSegMax <= Strategy()._dataSize);
        const t4_byte *map = Strategy()._mapStart + _position;

        for (int i = 0; i < n; ++i) {
            _segments.SetAt(i, (t4_byte *)map); // loses const
            map += kSegMax;
        }
    } else {
        int chunk = kSegMax;
        t4_i32 pos = _position;

        // allocate buffers, load them if necessary
        for (int i = 0; i < n; ++i) {
            if (i == last) {
                chunk = fSegRest(_size);
            }

            auto *p = d4_new t4_byte[chunk];
            _segments.SetAt(i, p);

            if (_position > 0) {
                d4_dbgdef(int n = ) Strategy().DataRead(pos, p, chunk);
                d4_assert(n == chunk);
                pos += chunk;
            }
        }
    }

    if (id >= 0) {
        d4_assert(_persist != 0);
        _persist->ApplyAside(id, *this);
    }

    Validate();
}

//@func Makes sure the requested data is in a modifiable buffer.
t4_byte *c4_Column::CopyNow(t4_i32 offset_)
{
    d4_assert(offset_ <= _size);

    _dirty = true;

    const t4_byte *ptr = LoadNow(offset_);
    if (UsesMap(ptr)) {
        if (offset_ >= _gap) {
            offset_ += _slack;
        }

        // this will only force creation of a buffer
        ptr = CopyData(offset_, offset_, 0);
        d4_assert(!UsesMap(ptr));
    }

    return (t4_byte *)ptr;
}

//@func Copies data, creating a buffer if needed.  Must be in single segment.
t4_byte *c4_Column::CopyData(t4_i32 to_, t4_i32 from_, int count_)
{
    int i = fSegIndex(to_);
    auto *p = (t4_byte *)_segments.GetAt(i);

    if (UsesMap(p)) {
        int n = kSegMax;
        if (fSegOffset(i) + n > _size + _slack) {
            n = (int)(_size + _slack - fSegOffset(i));
        }

        d4_assert(n > 0);

        auto *q = d4_new t4_byte[n];
        memcpy(q, p, n); // some copying can be avoided, overwritten below...
        _segments.SetAt(i, q);

        p = q;
    }

    p += fSegRest(to_);

    if (count_ > 0) {
        d4_assert(fSegIndex(to_ + count_ - 1) == i);

        const auto *src = (const t4_byte *)_segments.GetAt(fSegIndex(from_));
        d4_memmove(p, src + fSegRest(from_), count_);
    }

    return p;
}

/*
 *  Resizing a segmented vector can be a complicated operation.
 *  For now, simply making it work in all cases is the first priority.
 *
 *  A major simplification - and good performance improvement - is caused
 *  by the trick of maintaining a "gap" in the data, which can be "moved"
 *  around to allow fast insertion as well as simple (delayed) deletion.
 *
 *  The only complexity comes from the fact that the gap must end up being
 *  less than one full segment in size.  Therefore, insertion and removal
 *  across segment boundaries needs to handle a variety of situations.
 *
 *  Since complete segments can be inserted quickly, this approach avoids
 *  lots of copying when consecutive insertions/deletions are clustered.
 *  Even random changes move half as much (on average) as without a gap.
 *
 *  The price is the overhead of up to one segment of empty space, and the
 *  complexity of this code (all the magic is within this c4_Column class).
 */

void c4_Column::MoveGapUp(t4_i32 dest_)
{
    d4_assert(dest_ <= _size);
    d4_assert(_gap < dest_);
    d4_assert(_slack > 0);

    // forward loop to copy contents down, in little pieces if need be
    while (_gap < dest_) {
        int n = kSegMax - fSegRest(_gap);
        t4_i32 curr = _gap + n;
        if (curr > dest_) {
            curr = dest_;
        }

        // copy to [_gap..curr), which is inside one segment
        d4_assert(_gap < curr);
        d4_assert(fSegIndex(_gap) == fSegIndex(curr - 1));

        // copy from [_gap + _slack .. curr + _slack), of the same size
        t4_i32 fromBeg = _gap + _slack;
        t4_i32 fromEnd = curr + _slack;

        while (fromBeg < fromEnd) {
            int k = kSegMax - fSegRest(fromBeg);
            if (fromBeg + k > fromEnd) {
                k = (int)(fromEnd - fromBeg);
            }

            d4_assert(k > 0);

            CopyData(_gap, fromBeg, k);

            _gap += k;
            fromBeg += k;
        }

        _gap = curr;
    }

    d4_assert(_gap == dest_);
}

void c4_Column::MoveGapDown(t4_i32 dest_)
{
    d4_assert(dest_ <= _size);
    d4_assert(_gap > dest_);
    d4_assert(_slack > 0);

    // reverse loop to copy contents up, in little pieces if need be
    t4_i32 toEnd = _gap + _slack;
    t4_i32 toBeg = dest_ + _slack;

    while (toEnd > toBeg) {
        int n = fSegRest(toEnd);
        t4_i32 curr = toEnd - (n ? n : kSegMax);
        if (curr < toBeg) {
            curr = toBeg;
        }

        // copy to [curr..toEnd), which is inside one segment
        d4_assert(curr < toEnd);
        d4_assert(fSegIndex(curr) == fSegIndex(toEnd - 1));

        // copy from [fromBeg .. _gap), which has the same size
        t4_i32 fromBeg = _gap - (toEnd - curr);

        while (_gap > fromBeg) {
            int k = fSegRest(_gap);
            if (k == 0) {
                k = kSegMax;
            }
            if (_gap - k < fromBeg) {
                k = (int)(_gap - fromBeg);
            }

            d4_assert(k > 0);

            toEnd -= k;
            _gap -= k;

            CopyData(toEnd, _gap, k);
        }
    }

    d4_assert(_gap == dest_);
}

void c4_Column::MoveGapTo(t4_i32 pos_)
{
    d4_assert(pos_ <= _size);

    if (_slack == 0) {
        // if there is no real gap, then just move it
        _gap = pos_;
    } else if (_gap < pos_) {
        // move the gap up, ie. some bytes down
        MoveGapUp(pos_);
    } else if (_gap > pos_) {
        // move the gap down, ie. some bytes up
        if (_gap - pos_ > _size - _gap + fSegRest(pos_)) {
            RemoveGap(); // it's faster to get rid of the gap instead
            _gap = pos_;
        } else {
            // normal case, move some bytes up
            MoveGapDown(pos_);
        }
    }

    d4_assert(_gap == pos_);

    Validate();
}

void c4_Column::RemoveGap()
{
    if (_slack > 0) {
        if (_gap < _size) {
            MoveGapUp(_size);
        }

        d4_assert(_gap == _size); // the gap is now at the end
        d4_assert(_slack < kSegMax);

        //  Case 1: gap is at start of segment
        //  ==================================
        //
        //    G G+S
        //
        //    |  |
        //    :----+xx:
        //    |   |
        //
        //    i    i+1 (limit)
        //
        //  Case 2: gap is inside segment
        //  =============================
        //
        //       G G+S
        //
        //       |  |
        //    :--+--+x:
        //    |   |
        //
        //    i    i+1 (limit)
        //
        //  Case 3: gap runs to end of segment
        //  ==================================
        //
        //       G   G+S
        //
        //       |  |
        //    :--+----:0000000:
        //    |   |   |
        //
        //    i    i+1     i+2 (limit)
        //
        //  Case 4: gap is across two segments
        //  ==================================
        //
        //       G   G+S
        //
        //       |    |
        //    :--+----:-+xxxxx:
        //    |   |   |
        //
        //    i    i+1     i+2 (limit)

        int i = fSegIndex(_gap);
        int n = fSegRest(_gap);

        if (n == 0) {
            // case 1
            ReleaseSegment(i);
            _segments.SetAt(i, nullptr);
        } else {
            if (n + _slack > kSegMax) {
                // case 4
                ReleaseSegment(i + 1);
            }

            // truncate rest of segment
            auto *p = d4_new t4_byte[n];
            memcpy(p, _segments.GetAt(i), n);

            ReleaseSegment(i);
            _segments.SetAt(i, p);
            _segments.SetSize(i + 1);
        }

        _slack = 0;
    }

    Validate();
}

void c4_Column::Grow(t4_i32 off_, t4_i32 diff_)
{
    d4_assert(off_ <= _size);
    d4_assert(diff_ > 0);

    if (_segments.GetSize() == 0) {
        SetupSegments();
    }

    Validate();

    _dirty = true;

    // move the gap so it starts where we want to insert
    MoveGapTo(off_);

    t4_i32 bigSlack = _slack;
    if (bigSlack < diff_) {
        // only do more if this isn't good enough
        // number of segments to insert
        int n = fSegIndex(diff_ - _slack + kSegMax - 1);
        d4_assert(n > 0);

        int i1 = fSegIndex(_gap);
        int i2 = fSegIndex(_gap + _slack);

        bool moveBack = false;

        if (i2 > i1) {
            // cases 3 and 4
            ++i1;
        } else if (fSegRest(_gap)) {
            // case 2
            moveBack = true;
        }

        _segments.InsertAt(i1, nullptr, n);
        for (int i = 0; i < n; ++i) {
            _segments.SetAt(i1 + i, d4_new t4_byte[(int)kSegMax]);
        }

        bigSlack += fSegOffset(n);

        if (moveBack) {
            d4_assert(i1 == fSegIndex(_gap));

            //  we have inserted too low, move bytes in front of gap back
            CopyData(fSegOffset(i1), fSegOffset(i1 + n), fSegRest(_gap));
        }
    }

    d4_assert(diff_ <= bigSlack && bigSlack < diff_ + kSegMax);

    _gap += diff_;
    _slack = (int)(bigSlack - diff_);
    _size += diff_;

    FinishSlack();
}

void c4_Column::Shrink(t4_i32 off_, t4_i32 diff_)
{
    d4_assert(off_ <= _size);
    d4_assert(diff_ > 0);

    if (_segments.GetSize() == 0) {
        SetupSegments();
    }

    Validate();

    _dirty = true;

    // the simplification here is that we have in fact simply *two*
    // gaps and we must merge them together and end up with just one

    if (_slack > 0) {
        if (_gap < off_) {
            // if too low, move the gap up
            MoveGapTo(off_);
        } else if (off_ + diff_ < _gap) {
            // if too high, move down to end
            MoveGapTo(off_ + diff_);
        }

        // the gap is now inside, or adjacent to, the deleted area
        d4_assert(off_ <= _gap && _gap <= off_ + diff_);
    }

    _gap = off_;

    // check whether the merged gap would cross a segment boundary
    int i1 = fSegIndex(_gap);
    int i2 = fSegIndex(_gap + _slack + diff_);

    // drop complete segments, not a partially filled boundary
    if (fSegRest(_gap)) {
        ++i1;
    }

    // moved up (was after the next if in the 1.7 May 28 build)
    _slack += diff_;
    _size -= diff_;

    int n = i2 - i1;
    if (n > 0) {
        for (int i = i1; i < i2; ++i) {
            ReleaseSegment(i);
        }

        _segments.RemoveAt(i1, n);

        // the logic in 1.7 of May 28 was warped (the assert "fix" was wrong)
        d4_assert(_slack >= fSegOffset(n));
        _slack -= fSegOffset(n);
    }

    d4_assert(0 <= _slack && _slack < 2 * kSegMax);

    // if the gap is at the end, get rid of a partial segment after it
    if (_gap == _size) {
        int i = fSegIndex(_size + _slack);
        if (i != fSegIndex(_gap)) {
            d4_assert(i == fSegIndex(_gap) + 1);
            d4_assert(i == _segments.GetSize() - 1);

            ReleaseSegment(i);
            _segments.SetAt(i, nullptr);

            _slack -= fSegRest(_size + _slack);

            d4_assert(_slack < kSegMax);
            d4_assert(fSegRest(_gap + _slack) == 0);
        }
    }

    // the slack may still be too large to leave as is
    if (_slack >= kSegMax) {
        // move the bytes just after the end of the gap one segment down
        int x = fSegRest(_gap + _slack);
        int r = kSegMax - x;
        if (_gap + r > _size) {
            r = (int)(_size - _gap);
        }

        CopyData(_gap, _gap + _slack, r);

        int i = fSegIndex(_gap + kSegMax - 1);
        ReleaseSegment(i);

        if (r + x < kSegMax) {
            _segments.SetAt(i, nullptr);
        } else {
            _segments.RemoveAt(i);
        }

        _slack -= r + x;
        _gap += r;
    }

    // if we have no data anymore, make sure not to use the file map either
    if (_size == 0 && _slack > 0) {
        CopyNow(0);
    }

    FinishSlack();
}

void c4_Column::FinishSlack()
{
    Validate();

    // optimization: if partial end segment easily fits in slack, move it down
    t4_i32 gapEnd = _gap + _slack;
    if (!fSegRest(gapEnd) && gapEnd >= _size + 500) {
        // slack is at least 500 bytes more than the partial end segment
        // also, the gap must end exactly on a segment boundary
        int i = fSegIndex(gapEnd);
        d4_assert(i == _segments.GetSize() - 1);

        int n = _size - _gap;
        CopyData(gapEnd - n, gapEnd, n);

        ReleaseSegment(i);
        _segments.SetAt(i, nullptr);

        _slack -= n;
        d4_assert(_slack >= 500);

        Validate();
    }
}

void c4_Column::SaveNow(c4_Strategy &strategy_, t4_i32 pos_)
{
    if (_segments.GetSize() == 0) {
        SetupSegments();
    }

    // write all segments
    c4_ColIter iter(*this, 0, _size);
    while (iter.Next(kSegMax)) {
        int n = iter.BufLen();
        strategy_.DataWrite(pos_, iter.BufLoad(), n);
        if (strategy_._failure != 0) {
            break;
        }
        pos_ += n;
    }
}

const t4_byte *c4_Column::FetchBytes(t4_i32 pos_, int len_, c4_Bytes &buffer_, bool forceCopy_)
{
    d4_assert(len_ > 0);
    d4_assert(pos_ + len_ <= ColSize());
    d4_assert(0 <= _slack && _slack < kSegMax);

    c4_ColIter iter(*this, pos_, pos_ + len_);
    iter.Next();

    // most common case, all bytes are inside the same segment
    if (!forceCopy_ && iter.BufLen() == len_) {
        return iter.BufLoad();
    }

    t4_byte *p = buffer_.SetBuffer(len_);
    do {
        d4_assert(iter.BufLen() > 0);
        memcpy(p, iter.BufLoad(), iter.BufLen());
        p += iter.BufLen();
    } while (iter.Next());
    d4_assert(p == buffer_.Contents() + len_);

    return buffer_.Contents();
}

void c4_Column::StoreBytes(t4_i32 pos_, const c4_Bytes &buffer_)
{
    int n = buffer_.Size();
    if (n > 0) {
        d4_assert(pos_ + n <= ColSize());

        c4_ColIter iter(*this, pos_, pos_ + n);

        const t4_byte *p = buffer_.Contents();
        while (iter.Next(n)) {
            d4_assert(iter.BufLen() > 0);
            memcpy(iter.BufSave(), p, iter.BufLen());
            p += iter.BufLen();
        }
        d4_assert(p == buffer_.Contents() + n);
    }
}

/*
PushValue and PullValue deal with variable-sized storage of
one unsigned integer value of up to 32 bits. Depending on the
magnitude of the integer, 1..6 bytes are used to represent it.
Each byte holds 7 significant bits and one continuation bit.
This saves storage, but it is also byte order independent.
Negative values are stored as a zero byte plus positive value.
 */

t4_i32 c4_Column::PullValue(const t4_byte * &ptr_)
{
    t4_i32 mask = *ptr_ ? 0 : ~0;

    t4_i32 v = 0;
    for (;;) {
        v = (v << 7) +  *ptr_;
        if (*ptr_++ & 0x80) {
            break;
        }
    }

    return mask ^ (v - 0x80); // oops, last byte had bit 7 set
}

void c4_Column::PushValue(t4_byte * &ptr_, t4_i32 v_)
{
    if (v_ < 0) {
        v_ = ~v_;
        *ptr_++ = 0;
    }

    int n = 0;
    do {
        n += 7;
    } while ((v_ >> n) && n < 32);

    while (n) {
        n -= 7;
        auto b = (t4_byte)((v_ >> n) & 0x7F);
        if (!n) {
            b |= 0x80;
        }
        // set bit 7 on the last byte
        *ptr_++ = b;
    }
}

void c4_Column::InsertData(t4_i32 index_, t4_i32 count_, bool clear_)
{
    d4_assert(index_ <= ColSize());

    if (count_ > 0) {
        Grow(index_, count_);

        // clear the contents, in separate chunks if necessary
        if (clear_) {
            c4_ColIter iter(*this, index_, index_ + count_);
            while (iter.Next()) {
                memset(iter.BufSave(), 0, iter.BufLen());
            }
        }
    }
}

void c4_Column::RemoveData(t4_i32 index_, t4_i32 count_)
{
    d4_assert(index_ + count_ <= ColSize());

    if (count_ > 0) {
        Shrink(index_, count_);
    }
}

/////////////////////////////////////////////////////////////////////////////

void c4_ColOfInts::Get_0b(int)
{
    *(t4_i32 *)_item = 0;
}

void c4_ColOfInts::Get_1b(int index_)
{
    t4_i32 off = index_ >> 3;
    d4_assert(off < ColSize());

    *(t4_i32 *)_item = (*LoadNow(off) >> (index_ & 7)) & 0x01;
}

void c4_ColOfInts::Get_2b(int index_)
{
    t4_i32 off = index_ >> 2;
    d4_assert(off < ColSize());

    *(t4_i32 *)_item = (*LoadNow(off) >> ((index_ & 3) << 1)) & 0x03;
}

void c4_ColOfInts::Get_4b(int index_)
{
    t4_i32 off = index_ >> 1;
    d4_assert(off < ColSize());

    *(t4_i32 *)_item = (*LoadNow(off) >> ((index_ & 1) << 2)) & 0x0F;
}

void c4_ColOfInts::Get_8i(int index_)
{
    t4_i32 off = index_;
    d4_assert(off < ColSize());

    *(t4_i32 *)_item = *(const signed char *)LoadNow(off);
}

void c4_ColOfInts::Get_16i(int index_)
{
    t4_i32 off = index_ * (t4_i32)2;
    d4_assert(off + 2 <= ColSize());

    const t4_byte *vec = LoadNow(off);

    _item[0] = vec[0];
    _item[1] = vec[1];

    *(t4_i32 *)_item = *(const short *)_item;
}

void c4_ColOfInts::Get_16r(int index_)
{
    t4_i32 off = index_ * (t4_i32)2;
    d4_assert(off + 2 <= ColSize());

    const t4_byte *vec = LoadNow(off);

    // 2003-02-02 - gcc 3.2.1 on linux (!) fails to compile this
    // sign-extension trick properly, use a temp buffer instead:
    //*(t4_i32*) _item = *(const short*) _item;

    t4_byte temp[2];
    temp[1] = vec[0];
    temp[0] = vec[1];
    *(t4_i32 *)_item = *(const short *)temp;
}

void c4_ColOfInts::Get_32i(int index_)
{
    t4_i32 off = index_ * (t4_i32)4;
    d4_assert(off + 4 <= ColSize());

    const t4_byte *vec = LoadNow(off);

    _item[0] = vec[0];
    _item[1] = vec[1];
    _item[2] = vec[2];
    _item[3] = vec[3];
}

void c4_ColOfInts::Get_32r(int index_)
{
    t4_i32 off = index_ * (t4_i32)4;
    d4_assert(off + 4 <= ColSize());

    const t4_byte *vec = LoadNow(off);

    _item[3] = vec[0];
    _item[2] = vec[1];
    _item[1] = vec[2];
    _item[0] = vec[3];
}

void c4_ColOfInts::Get_64i(int index_)
{
    t4_i32 off = index_ * (t4_i32)8;
    d4_assert(off + 8 <= ColSize());

    const t4_byte *vec = LoadNow(off);

    for (int i = 0; i < 8; ++i) {
        _item[i] = vec[i];
    }
}

void c4_ColOfInts::Get_64r(int index_)
{
    t4_i32 off = index_ * (t4_i32)8;
    d4_assert(off + 8 <= ColSize());

    const t4_byte *vec = LoadNow(off);

    for (int i = 0; i < 8; ++i) {
        _item[7 - i] = vec[i];
    }
}

/////////////////////////////////////////////////////////////////////////////

static int fBitsNeeded(t4_i32 v)
{
    if ((v >> 4) == 0) {
        static int bits[] = {
            0, 1, 2, 2, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4
        };
        return bits[(int)v];
    }

    if (v < 0) {
        // first flip all bits if bit 31 is set
        v = ~v;
    }
    // ... bit 31 is now always zero

    // then check if bits 15-31 used (32b), 7-31 used (16b), else (8b)
    return v >> 15 ? 32 : v >> 7 ? 16 : 8;
}

bool c4_ColOfInts::Set_0b(int, const t4_byte *item_)
{
    t4_i32 v = *(const t4_i32 *)item_;
    return v == 0;
}

bool c4_ColOfInts::Set_1b(int index_, const t4_byte *item_)
{
    t4_i32 v = *(const t4_i32 *)item_;

    t4_i32 off = index_ >> 3;
    d4_assert(off < ColSize());

    index_ &= 7;

    t4_byte *p = CopyNow(off);
    *p = (*p & ~(1 << index_)) | (((t4_byte)v & 1) << index_);

    return (v >> 1) == 0;
}

bool c4_ColOfInts::Set_2b(int index_, const t4_byte *item_)
{
    t4_i32 v = *(const t4_i32 *)item_;

    t4_i32 off = index_ >> 2;
    d4_assert(off < ColSize());

    const int n = (index_ & 3) << 1;

    t4_byte *p = CopyNow(off);
    *p = (*p & ~(0x03 << n)) | (((t4_byte)v & 0x03) << n);

    return (v >> 2) == 0;
}

bool c4_ColOfInts::Set_4b(int index_, const t4_byte *item_)
{
    t4_i32 v = *(const t4_i32 *)item_;

    t4_i32 off = index_ >> 1;
    d4_assert(off < ColSize());

    const int n = (index_ & 1) << 2;

    t4_byte *p = CopyNow(off);
    *p = (*p & ~(0x0F << n)) | (((t4_byte)v & 0x0F) << n);

    return (v >> 4) == 0;
}

// avoid a bug in MS EVC 3.0's code gen for ARM (i.e. WinCE)
#ifdef _ARM_
#pragma optimize("g",off)
#endif

bool c4_ColOfInts::Set_8i(int index_, const t4_byte *item_)
{
    t4_i32 v = *(const t4_i32 *)item_;

    t4_i32 off = index_;
    d4_assert(off < ColSize());

    *(char *)CopyNow(off) = (char)v;

    return v == (signed char)v;
}

#ifdef _ARM_
#pragma optimize("",on)
#endif

bool c4_ColOfInts::Set_16i(int index_, const t4_byte *item_)
{
    t4_i32 v = *(const t4_i32 *)item_;

    t4_i32 off = index_ * (t4_i32)2;
    d4_assert(off + 2 <= ColSize());

    *(short *)CopyNow(off) = (short)v;

    return v == (short)v;
}

bool c4_ColOfInts::Set_16r(int index_, const t4_byte *item_)
{
    t4_i32 v = *(const t4_i32 *)item_;

    t4_byte buf[2];
    *(short *)buf = (short)v;

    t4_i32 off = index_ * (t4_i32)2;
    d4_assert(off + 2 <= ColSize());

    t4_byte *vec = CopyNow(off);

    vec[1] = buf[0];
    vec[0] = buf[1];

    return v == (short)v;
}

bool c4_ColOfInts::Set_32i(int index_, const t4_byte *item_)
{
    t4_i32 v = *(const t4_i32 *)item_;

    t4_i32 off = index_ * (t4_i32)4;
    d4_assert(off + 4 <= ColSize());

    *(t4_i32 *)CopyNow(off) = (t4_i32)v;

    return true;
}

bool c4_ColOfInts::Set_32r(int index_, const t4_byte *item_)
{
    t4_i32 off = index_ * (t4_i32)4;
    d4_assert(off + 4 <= ColSize());

    t4_byte *vec = CopyNow(off);

    vec[3] = item_[0];
    vec[2] = item_[1];
    vec[1] = item_[2];
    vec[0] = item_[3];

    return true;
}

bool c4_ColOfInts::Set_64i(int index_, const t4_byte *item_)
{
    t4_i32 off = index_ * (t4_i32)8;
    d4_assert(off + 8 <= ColSize());

    t4_byte *vec = CopyNow(off);

    for (int i = 0; i < 8; ++i) {
        vec[i] = item_[i];
    }

    return true;
}

bool c4_ColOfInts::Set_64r(int index_, const t4_byte *item_)
{
    t4_i32 off = index_ * (t4_i32)8;
    d4_assert(off + 8 <= ColSize());

    t4_byte *vec = CopyNow(off);

    for (int i = 0; i < 8; ++i) {
        vec[7 - i] = item_[i];
    }

    return true;
}

/////////////////////////////////////////////////////////////////////////////

c4_ColOfInts::c4_ColOfInts(c4_Persist *persist_, int width_) : c4_Column
        (persist_)
    , _getter(&c4_ColOfInts::Get_0b)
    , _setter(&c4_ColOfInts::Set_0b)
    , _currWidth(0)
    , _dataWidth(width_)
    , _numRows(0)
    , _mustFlip(false)
{
}

void c4_ColOfInts::ForceFlip()
{
    _mustFlip = true;
}

int c4_ColOfInts::RowCount() const
{
    d4_assert(_numRows >= 0);

    return _numRows;
}

int c4_ColOfInts::CalcAccessWidth(int numRows_, t4_i32 colSize_)
{
    d4_assert(numRows_ > 0);

    int w = (int)((colSize_ << 3) / numRows_);

    // deduce sub-byte sizes for small vectors, see c4_ColOfInts::Set
    if (numRows_ <= 7 && 0 < colSize_ && colSize_ <= 6) {
        static t4_byte realWidth[][6] = {
            // sz =  1:  2:  3:  4:  5:  6:
            {
                8, 16, 1, 32, 2, 4
            },
            {    //  n = 1
                4, 8, 1, 16, 2, 0
            },
            {    //  n = 2
                2, 4, 8, 1, 0, 16
            },
            {    //  n = 3
                2, 4, 0, 8, 1, 0
            },
            {    //  n = 4
                1, 2, 4, 0, 8, 0
            },
            {    //  n = 5
                1, 2, 4, 0, 0, 8
            },
            {    //  n = 6
                1, 2, 0, 4, 0, 0
            }
            ,  //  n = 7
        };

        w = realWidth[numRows_ - 1][(int)colSize_ - 1];
        d4_assert(w > 0);
    }

    return (w & (w - 1)) == 0 ? w : -1;
}

void c4_ColOfInts::SetRowCount(int numRows_)
{
    _numRows = numRows_;
    if (numRows_ > 0) {
        int w = CalcAccessWidth(numRows_, ColSize());
        d4_assert(w >= 0);
        SetAccessWidth(w);
    }
}

void c4_ColOfInts::FlipBytes()
{
    if (_currWidth > 8) {
        int step = _currWidth >> 3;

        c4_ColIter iter(*this, 0, ColSize());
        while (iter.Next(step)) {
            t4_byte *data = iter.BufSave();
            d4_assert(data != 0);

            for (int j = 0; j < step; ++j) {
                t4_byte c = data[j];
                data[j] = data[step - j - 1];
                data[step - j - 1] = c;
            }
        }
    }
}

void c4_ColOfInts::SetAccessWidth(int bits_)
{
    d4_assert((bits_ & (bits_ - 1)) == 0);

    int l2bp1 = 0; // "log2 bits plus one" needed to represent value
    while (bits_) {
        ++l2bp1;
        bits_ >>= 1;
    }
    d4_assert(0 <= l2bp1 && l2bp1 < 8);

    _currWidth = (1 << l2bp1) >> 1;

    if (l2bp1 > 4 && (_mustFlip || (Persist() != nullptr && Strategy()._bytesFlipped))) {
        l2bp1 += 3;
    }
    // switch to the trailing entries for byte flipping

    // Metrowerks Codewarrior 11 is dumb, it requires the "&c4_ColOfInts::"

    static tGetter gTab[] = {
        &c4_ColOfInts::Get_0b,  //  0:  0 bits/entry
        &c4_ColOfInts::Get_1b,  //  1:  1 bits/entry
        &c4_ColOfInts::Get_2b,  //  2:  2 bits/entry
        &c4_ColOfInts::Get_4b,  //  3:  4 bits/entry

        &c4_ColOfInts::Get_8i,  //  4:  8 bits/entry
        &c4_ColOfInts::Get_16i,  //  5: 16 bits/entry
        &c4_ColOfInts::Get_32i,  //  6: 32 bits/entry
        &c4_ColOfInts::Get_64i,  //  7: 64 bits/entry

        &c4_ColOfInts::Get_16r,  //  8: 16 bits/entry, reversed
        &c4_ColOfInts::Get_32r,  //  9: 32 bits/entry, reversed
        &c4_ColOfInts::Get_64r,  // 10: 64 bits/entry, reversed
    };

    static tSetter sTab[] = {
        &c4_ColOfInts::Set_0b,  //  0:  0 bits/entry
        &c4_ColOfInts::Set_1b,  //  1:  1 bits/entry
        &c4_ColOfInts::Set_2b,  //  2:  2 bits/entry
        &c4_ColOfInts::Set_4b,  //  3:  4 bits/entry

        &c4_ColOfInts::Set_8i,  //  4:  8 bits/entry
        &c4_ColOfInts::Set_16i,  //  5: 16 bits/entry
        &c4_ColOfInts::Set_32i,  //  6: 32 bits/entry
        &c4_ColOfInts::Set_64i,  //  7: 64 bits/entry

        &c4_ColOfInts::Set_16r,  //  8: 16 bits/entry, reversed
        &c4_ColOfInts::Set_32r,  //  9: 32 bits/entry, reversed
        &c4_ColOfInts::Set_64r,  // 10: 64 bits/entry, reversed
    };

    d4_assert(l2bp1 < sizeof gTab / sizeof *gTab);

    _getter = gTab[l2bp1];
    _setter = sTab[l2bp1];

    d4_assert(_getter != 0 && _setter != 0);
}

int c4_ColOfInts::ItemSize(int)
{
    return _currWidth >= 8 ? _currWidth >> 3 : -_currWidth;
}

const void *c4_ColOfInts::Get(int index_, int &length_)
{
    d4_assert(sizeof _item >= _dataWidth);

    (this->*_getter)(index_);

    length_ = _dataWidth;
    return _item;
}

void c4_ColOfInts::Set(int index_, const c4_Bytes &buf_)
{
    d4_assert(buf_.Size() == _dataWidth);

    if ((this->*_setter)(index_, buf_.Contents())) {
        return;
    }

    d4_assert(buf_.Size() == sizeof(t4_i32));

    int n = fBitsNeeded(*(const t4_i32 *)buf_.Contents());
    if (n > _currWidth) {
        int k = RowCount();

        t4_i32 oldEnd = ColSize();
        t4_i32 newEnd = ((t4_i32)k * n + 7) >> 3;

        if (newEnd > oldEnd) {
            InsertData(oldEnd, newEnd - oldEnd, _currWidth == 0);

            // 14-5-2002: need to get rid of gap in case it risks not being a
            //  multiple of the increased size (bug, see s46 regression test)
            //
            // Example scenario: gap size is odd, data gets resized to 2/4-byte
            // ints, data at end fits without moving gap to end, then we end
            // up with a vector that has an int split *across* the gap - this
            // commits just fine, but access to that split int is now bad.
            //
            // Lesson: need stricter/simpler consistency, it's way too complex!
            if (n > 8) {
                RemoveGap();
            }
        }

        // data value exceeds width, expand to new size and repeat
        if (_currWidth > 0) {
            d4_assert(n % _currWidth == 0); // must be expanding by a multiple

            // To expand, we start by inserting a new appropriate chunk
            // at the end, and expand the entries in place (last to first).

            tGetter oldGetter = _getter;
            SetAccessWidth(n);

            d4_assert(sizeof _item >= _dataWidth);

            // this expansion in place works because it runs backwards
            while (--k >= 0) {
                (this->*oldGetter)(k);
                (this->*_setter)(k, _item);
            }
        } else {
            if (_dataWidth > (int)sizeof(t4_i32)) {
                n = _dataWidth << 3;
            }
            // don't trust setter result, use max instead

            SetAccessWidth(n);
        }

        // now repeat the failed call to _setter
        /* bool f = */ (this->*_setter)(index_, buf_.Contents());
        //? d4_assert(f);
    }
}

t4_i32 c4_ColOfInts::GetInt(int index_)
{
    int n;
    const void *p = Get(index_, n);
    d4_assert(n == sizeof(t4_i32));
    return *(const t4_i32 *)p;
}

void c4_ColOfInts::SetInt(int index_, t4_i32 value_)
{
    Set(index_, c4_Bytes(&value_, sizeof value_));
}

int c4_ColOfInts::DoCompare(const c4_Bytes &b1_, const c4_Bytes &b2_)
{
    d4_assert(b1_.Size() == sizeof(t4_i32));
    d4_assert(b2_.Size() == sizeof(t4_i32));

    t4_i32 v1 = *(const t4_i32 *)b1_.Contents();
    t4_i32 v2 = *(const t4_i32 *)b2_.Contents();

    return v1 == v2 ? 0 : v1 < v2 ? -1 : +1;
}

void c4_ColOfInts::Insert(int index_, const c4_Bytes &buf_, int count_)
{
    d4_assert(buf_.Size() == _dataWidth);
    d4_assert(count_ > 0);

    bool clear = true;
    const t4_byte *ptr = buf_.Contents();

    for (int i = 0; i < _dataWidth; ++i) {
        if (*ptr++) {
            clear = false;
            break;
        }
    }

    ResizeData(index_, count_, clear);

    if (!clear) {
        while (--count_ >= 0) {
            Set(index_++, buf_);
        }
    }
}

void c4_ColOfInts::Remove(int index_, int count_)
{
    d4_assert(count_ > 0);

    ResizeData(index_, -count_);
}

void c4_ColOfInts::ResizeData(int index_, int count_, bool clear_)
{
    _numRows += count_;

    if (!(_currWidth & 7)) {
        // not 1, 2, or 4
        const auto w = (t4_i32)(_currWidth >> 3);
        if (count_ > 0) {
            InsertData(index_ * w, count_ * w, clear_);
        } else {
            RemoveData(index_ * w, -count_ * w);
        }
        return;
    }

    d4_assert(_currWidth == 1 || _currWidth == 2 || _currWidth == 4);

    /*  _currwidth    1:  2:  4:
     *    shiftPos     3   2   1    shift the offset right this much
     *    maskPos      7   3   1    mask the offset with this
     */

    const int shiftPos = _currWidth == 4 ? 1 : 4 - _currWidth;
    const int maskPos = (1 << shiftPos) - 1;

    // the following code is similar to c4_Column::Resize, but at bit level

    // turn insertion into deletion by inserting entire bytes
    if (count_ > 0) {
        unsigned off = (unsigned)index_ >> shiftPos;
        int gapBytes = (count_ + maskPos) >> shiftPos;

        InsertData(off, gapBytes, clear_);

        // oops, we might have inserted too low by a few entries
        const int bits = (index_ & maskPos) * _currWidth;
        if (bits) {
            const int maskLow = (1 << bits) - 1;

            // move the first few bits to start of inserted range
            t4_byte *p = CopyNow(off + gapBytes);
            t4_byte one = *p & maskLow;
            *p &= ~maskLow;

            *CopyNow(off) = one;
        }

        index_ += count_;
        count_ -= gapBytes << shiftPos;
        d4_assert(count_ <= 0);
    }

    // now perform a deletion using a forward loop to copy down
    if (count_ < 0) {
        c4_Bytes temp;

        while (index_ < _numRows) {
            int length;
            const void *ptr = Get(index_ - count_, length);
            Set(index_++, c4_Bytes(ptr, length));
        }
    } else {
        d4_assert(count_ == 0);
    }

    FixSize(false);
}

void c4_ColOfInts::FixSize(bool fudge_)
{
    int n = RowCount();
    t4_i32 needBytes = ((t4_i32)n * _currWidth + 7) >> 3;

    // use a special trick to mark sizes less than 1 byte in storage
    if (fudge_ && 1 <= n && n <= 4 && (_currWidth & 7)) {
        const int shiftPos = _currWidth == 4 ? 1 : 4 - _currWidth;

        static t4_byte fakeSizes[3][4] = {
            {
                //  n:  1:  2:  3:  4:
                6, 1, 2, 2
            },
            {    //  4-bit entries:   4b  8b 12b 16b
                5, 5, 1, 1
            },
            {    //  2-bit entries:   2b  4b  6b  8b
                3, 3, 4, 5
            }
            ,  //  1-bit entries:   1b  2b  3b  4b
        };

        // The idea is to use an "impossible" size (ie. 5, for n = 2)
        // to give information about the current bit packing density.
        d4_assert(needBytes <= 2);
        needBytes = fakeSizes[shiftPos - 1][n - 1];
    }

    t4_i32 currSize = ColSize();

    if (needBytes < currSize) {
        RemoveData(needBytes, currSize - needBytes);
    } else if (needBytes > currSize) {
        InsertData(currSize, needBytes - currSize, true);
    }
}

/////////////////////////////////////////////////////////////////////////////

bool c4_ColIter::Next()
{
    _pos += _len;

    _len = _column.AvailAt(_pos);
    _ptr = _column.LoadNow(_pos);

    if (!_ptr) {
        _len = 0;
    } else if (_pos + _len >= _limit) {
        _len = _limit - _pos;
    } else {
        // 19990831 - optimization to avoid most copying
        // while the end is adjacent to the next segment, extend it
        while (_ptr + _len == _column.LoadNow(_pos + _len)) {
            int n = _column.AvailAt(_pos + _len);
            if (n == 0) {
                break;
            }
            // may be a short column (strings)

            _len += n;

            if (_pos + _len >= _limit) {
                _len = _limit - _pos;
                break;
            }
        }
    }

    return _len > 0;
}

bool c4_ColIter::Next(int max_)
{
    _pos += _len;

    _len = _column.AvailAt(_pos);
    _ptr = _column.LoadNow(_pos);

    if (!_ptr) {
        _len = 0;
    } else if (_pos + _len > _limit) {
        _len = _limit - _pos;
    }

    if (_len <= 0) {
        return false;
    }

    if (_len > max_) {
        _len = max_;
    }

    return true;
}

/////////////////////////////////////////////////////////////////////////////
