You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
tdepim/akregator/src/mk4storage/metakit/src/derived.cpp

1004 lines
23 KiB

// derived.cpp --
// $Id$
// This is part of Metakit, see http://www.equi4.com/metakit/
/** @file
* Derived views are virtual views which track changes
*/
#include "header.h"
#include "handler.h"
#include "store.h"
#include "derived.h"
#include <stdlib.h> // qsort
/////////////////////////////////////////////////////////////////////////////
// Implemented in this file
// class c4_Sequence;
class c4_DerivedSeq;
class c4_FilterSeq;
class c4_SortSeq;
class c4_ProjectSeq;
/////////////////////////////////////////////////////////////////////////////
class c4_FilterSeq : public c4_DerivedSeq
{
protected:
c4_DWordArray _rowMap;
c4_DWordArray _revMap;
c4_Row _lowRow;
c4_Row _highRow;
c4_Bytes _rowIds;
protected:
c4_FilterSeq (c4_Sequence& seq_);
virtual ~c4_FilterSeq ();
void FixupReverseMap();
int PosInMap(int index_) const;
bool Match(int index_, c4_Sequence& seq_,
const int* =0, const int* =0) const;
bool MatchOne(int prop_, const c4_Bytes& data_) const;
public:
c4_FilterSeq (c4_Sequence& seq_, c4_Cursor low_, c4_Cursor high_);
virtual int RemapIndex(int, const c4_Sequence*) const;
virtual int NumRows() const;
virtual int Compare(int, c4_Cursor) const;
virtual bool Get(int, int, c4_Bytes&);
virtual void InsertAt(int, c4_Cursor, int =1);
virtual void RemoveAt(int, int =1);
virtual void Set(int, const c4_Property&, const c4_Bytes&);
virtual void SetSize(int);
virtual c4_Notifier* PreChange(c4_Notifier& nf_);
virtual void PostChange(c4_Notifier& nf_);
};
/////////////////////////////////////////////////////////////////////////////
c4_FilterSeq::c4_FilterSeq (c4_Sequence& seq_)
: c4_DerivedSeq (seq_)
{
_rowMap.SetSize(_seq.NumRows());
_revMap.SetSize(_seq.NumRows());
d4_assert(NumRows() == _seq.NumRows());
for (int i = 0; i < NumRows(); ++i)
{
_rowMap.SetAt(i, i);
_revMap.SetAt(i, i);
}
}
c4_FilterSeq::c4_FilterSeq (c4_Sequence& seq_, c4_Cursor low_,
c4_Cursor high_)
: c4_DerivedSeq (seq_), _lowRow (*low_), _highRow (*high_)
{
d4_assert((&_lowRow)._index == 0);
d4_assert((&_highRow)._index == 0);
// use a sneaky way to obtain the sequence pointers and indices
c4_Sequence* lowSeq = (& _lowRow)._seq;
c4_Sequence* highSeq = (& _highRow)._seq;
d4_assert(lowSeq && highSeq);
// prepare column numbers to avoid looking them up on every row
// lowCols is a vector of column numbers to use for the low limits
// highCols is a vector of column numbers to use for the high limits
int nl = lowSeq->NumHandlers(), nh = highSeq->NumHandlers();
c4_Bytes lowVec, highVec;
int* lowCols = (int*) lowVec.SetBufferClear(nl * sizeof (int));
int* highCols = (int*) highVec.SetBufferClear(nh * sizeof (int));
for (int il = 0; il < nl; ++il)
lowCols[il] = seq_.PropIndex(lowSeq->NthPropId(il));
for (int ih = 0; ih < nh; ++ih)
highCols[ih] = seq_.PropIndex(highSeq->NthPropId(ih));
// set _rowIds flag buffer for fast matching
{
int max = -1;
{
for (int i1 = 0; i1 < nl; ++i1)
{
int n = lowSeq->NthPropId(i1);
if (max < n)
max = n;
}
for (int i2 = 0; i2 < nh; ++i2)
{
int n = highSeq->NthPropId(i2);
if (max < n)
max = n;
}
}
t4_byte* p = _rowIds.SetBufferClear(max + 1);
{
for (int i1 = 0; i1 < nl; ++i1)
p[lowSeq->NthPropId(i1)] |= 1;
for (int i2 = 0; i2 < nh; ++i2)
p[highSeq->NthPropId(i2)] |= 2;
}
}
// now go through all rows and select the ones that are in range
_rowMap.SetSize(_seq.NumRows()); // avoid growing, use safe upper bound
int n = 0;
for (int i = 0; i < _seq.NumRows(); ++i)
if (Match(i, _seq, lowCols, highCols))
_rowMap.SetAt(n++, i);
_rowMap.SetSize(n);
FixupReverseMap();
}
c4_FilterSeq::~c4_FilterSeq ()
{
}
void c4_FilterSeq::FixupReverseMap()
{
int n = _seq.NumRows();
_revMap.SetSize(0);
if (n > 0)
{
_revMap.InsertAt(0, ~ (t4_i32) 0, n); //!
for (int i = 0; i < _rowMap.GetSize(); ++i)
_revMap.SetAt((int) _rowMap.GetAt(i), i);
}
}
bool c4_FilterSeq::MatchOne(int prop_, const c4_Bytes& data_) const
{
d4_assert(prop_ < _rowIds.Size());
t4_byte flag = _rowIds.Contents()[prop_];
d4_assert(flag);
if (flag & 1)
{
c4_Sequence* lowSeq = (& _lowRow)._seq;
c4_Handler& h = lowSeq->NthHandler(lowSeq->PropIndex(prop_));
if (h.Compare(0, data_) > 0)
return false;
}
if (flag & 2)
{
c4_Sequence* highSeq = (& _highRow)._seq;
c4_Handler& h = highSeq->NthHandler(highSeq->PropIndex(prop_));
if (h.Compare(0, data_) < 0)
return false;
}
return true;
}
bool c4_FilterSeq::Match(int index_, c4_Sequence& seq_,
const int* lowCols_, const int* highCols_) const
{
// use a sneaky way to obtain the sequence pointers and indices
c4_Sequence* lowSeq = (& _lowRow)._seq;
c4_Sequence* highSeq = (& _highRow)._seq;
d4_assert(lowSeq && highSeq);
int nl = lowSeq->NumHandlers(), nh = highSeq->NumHandlers();
c4_Bytes data;
// check each of the lower limits
for (int cl = 0; cl < nl; ++cl)
{
c4_Handler& hl = lowSeq->NthHandler(cl);
int n = lowCols_ ? lowCols_[cl]
: seq_.PropIndex(lowSeq->NthPropId(cl));
if (n >= 0)
{
c4_Handler& h = seq_.NthHandler(n);
const c4_Sequence* hc = seq_.HandlerContext(n);
int i = seq_.RemapIndex(index_, hc);
h.GetBytes(i, data);
}
else
hl.ClearBytes(data);
if (hl.Compare(0, data) > 0)
return false;
}
// check each of the upper limits
for (int ch = 0; ch < nh; ++ch)
{
c4_Handler& hh = highSeq->NthHandler(ch);
int n = highCols_ ? highCols_[ch]
: seq_.PropIndex(highSeq->NthPropId(ch));
if (n >= 0)
{
c4_Handler& h = seq_.NthHandler(n);
const c4_Sequence* hc = seq_.HandlerContext(n);
int i = seq_.RemapIndex(index_, hc);
h.GetBytes(i, data);
}
else
hh.ClearBytes(data);
if (hh.Compare(0, data) < 0)
return false;
}
return true;
}
int c4_FilterSeq::RemapIndex(int index_, const c4_Sequence* seq_) const
{
return seq_ == this ? index_
: _seq.RemapIndex((int) _rowMap.GetAt(index_), seq_);
}
int c4_FilterSeq::NumRows() const
{
return _rowMap.GetSize();
}
int c4_FilterSeq::Compare(int index_, c4_Cursor cursor_) const
{
return _seq.Compare((int) _rowMap.GetAt(index_), cursor_);
}
bool c4_FilterSeq::Get(int index_, int propId_, c4_Bytes& bytes_)
{
return _seq.Get((int) _rowMap.GetAt(index_), propId_, bytes_);
}
void c4_FilterSeq::InsertAt(int, c4_Cursor, int)
{
d4_assert(0);
}
void c4_FilterSeq::RemoveAt(int, int)
{
d4_assert(0);
}
void c4_FilterSeq::Set(int, const c4_Property&, const c4_Bytes&)
{
d4_assert(0);
}
void c4_FilterSeq::SetSize(int)
{
d4_assert(0);
}
int c4_FilterSeq::PosInMap(int index_) const
{
int i = 0;
while (i < NumRows())
if ((int) _rowMap.GetAt(i) >= index_)
break;
else
++i;
return i;
}
c4_Notifier* c4_FilterSeq::PreChange(c4_Notifier& nf_)
{
if (!GetDependencies())
return 0;
c4_Notifier* chg = d4_new c4_Notifier (this);
bool pass = false;
switch (nf_._type)
{
case c4_Notifier::kSet:
pass = nf_._propId >= _rowIds.Size() ||
_rowIds.Contents()[nf_._propId] == 0;
// fall through...
case c4_Notifier::kSetAt:
{
int r = (int) _revMap.GetAt(nf_._index);
bool includeRow = r >= 0;
if (!pass)
if (nf_._type == c4_Notifier::kSetAt)
{
d4_assert(nf_._cursor != 0);
includeRow = Match(nf_._cursor->_index,
*nf_._cursor->_seq);
}
else // set just one property, and it's not in a row yet
includeRow = MatchOne(nf_._propId, *nf_._bytes);
if (r >= 0 && !includeRow)
chg->StartRemoveAt(r, 1);
else if (r < 0 && includeRow)
chg->StartInsertAt(PosInMap(nf_._index), *nf_._cursor, 1);
else if (includeRow)
{
d4_assert(r >= 0);
if (nf_._type == c4_Notifier::kSetAt)
chg->StartSetAt(r, *nf_._cursor);
else
chg->StartSet(r, nf_._propId, *nf_._bytes);
}
}
break;
case c4_Notifier::kInsertAt:
{
int i = PosInMap(nf_._index);
d4_assert(nf_._cursor != 0);
if (Match(nf_._cursor->_index, *nf_._cursor->_seq))
chg->StartInsertAt(i, *nf_._cursor, nf_._count);
}
break;
case c4_Notifier::kRemoveAt:
{
int i = PosInMap(nf_._index);
int j = PosInMap(nf_._index + nf_._count);
d4_assert(j >= i);
if (j > i)
chg->StartRemoveAt(i, j - i);
}
break;
case c4_Notifier::kMove:
{
int i = PosInMap(nf_._index);
bool inMap = i < NumRows() && (int) _rowMap.GetAt(i) == nf_._index;
if (inMap && nf_._index != nf_._count)
chg->StartMove(i, PosInMap(nf_._count));
}
break;
}
return chg;
}
void c4_FilterSeq::PostChange(c4_Notifier& nf_)
{
bool pass = false;
switch (nf_._type)
{
case c4_Notifier::kSet:
pass = nf_._propId >= _rowIds.Size() ||
_rowIds.Contents()[nf_._propId] == 0;
// fall through...
case c4_Notifier::kSetAt:
{
int r = (int) _revMap.GetAt(nf_._index);
bool includeRow = r >= 0;
if (!pass)
if (nf_._type == c4_Notifier::kSetAt)
{
d4_assert(nf_._cursor != 0);
includeRow = Match(nf_._cursor->_index,
*nf_._cursor->_seq);
}
else // set just one property, and it's not in a row yet
includeRow = MatchOne(nf_._propId, *nf_._bytes);
if (r >= 0 && !includeRow)
_rowMap.RemoveAt(r);
else if (r < 0 && includeRow)
_rowMap.InsertAt(PosInMap(nf_._index), nf_._index);
else
break;
FixupReverseMap();
}
break;
case c4_Notifier::kInsertAt:
{
int i = PosInMap(nf_._index);
if (Match(nf_._index, _seq))
{
_rowMap.InsertAt(i, 0, nf_._count);
for (int j = 0; j < nf_._count; ++j)
_rowMap.SetAt(i++, nf_._index + j);
}
while (i < NumRows())
_rowMap.ElementAt(i++) += nf_._count;
FixupReverseMap();
}
break;
case c4_Notifier::kRemoveAt:
{
int i = PosInMap(nf_._index);
int j = PosInMap(nf_._index + nf_._count);
d4_assert(j >= i);
if (j > i)
_rowMap.RemoveAt(i, j - i);
while (i < NumRows())
_rowMap.ElementAt(i++) -= nf_._count;
FixupReverseMap();
}
break;
case c4_Notifier::kMove:
{
int i = PosInMap(nf_._index);
bool inMap = i < NumRows() && (int) _rowMap.GetAt(i) == nf_._index;
if (inMap && nf_._index != nf_._count)
{
int j = PosInMap(nf_._count);
_rowMap.RemoveAt(i);
if (j > i)
--j;
_rowMap.InsertAt(j, nf_._count);
FixupReverseMap();
}
}
break;
}
}
/////////////////////////////////////////////////////////////////////////////
class c4_SortSeq : public c4_FilterSeq
{
public:
typedef t4_i32 T;
c4_SortSeq (c4_Sequence& seq_, c4_Sequence* down_);
virtual ~c4_SortSeq ();
virtual c4_Notifier* PreChange(c4_Notifier& nf_);
virtual void PostChange(c4_Notifier& nf_);
private:
struct c4_SortInfo
{
c4_Handler* _handler;
const c4_Sequence* _context;
c4_Bytes _buffer;
int CompareOne(c4_Sequence& seq_, T a, T b)
{
_handler->GetBytes(seq_.RemapIndex((int) b, _context), _buffer, true);
return _handler->Compare(seq_.RemapIndex((int) a, _context), _buffer);
}
};
bool LessThan(T a, T b);
bool TestSwap( T& first , T& second );
void MergeSortThis( T* ar, int size , T scratch[] );
void MergeSort( T ar[] , int size );
virtual int Compare(int, c4_Cursor) const;
int PosInMap(c4_Cursor cursor_) const;
c4_SortInfo* _info;
c4_Bytes _down;
int _width;
};
/////////////////////////////////////////////////////////////////////////////
bool c4_SortSeq::LessThan(T a, T b)
{
if (a == b)
return false;
// go through each of the columns and compare values, but since
// handler access is used, we must be careful to remap indices
c4_SortInfo* info;
for (info = _info; info->_handler; ++info)
{
int f = info->CompareOne(_seq, a, b);
if (f)
{
int n = info - _info;
if (_width < n)
_width = n;
return (_down.Contents()[n] ? -f : f) < 0;
}
}
_width = info - _info;
return a < b;
}
inline bool c4_SortSeq::TestSwap( T& first , T& second )
{
if ( LessThan( second , first ) )
{
T temp = first; first = second; second = temp;
return true;
}
return false;
}
void c4_SortSeq::MergeSortThis( T* ar, int size , T scratch[] )
{
switch( size )
{
//Handle the special cases for speed:
case 2:
TestSwap( ar[ 0 ] , ar[ 1 ] );
break;
case 3:
TestSwap( ar[ 0 ] , ar[ 1 ] );
if ( TestSwap( ar[ 1 ] , ar[ 2 ] ) )
TestSwap( ar[ 0 ] , ar[ 1 ] );
break;
case 4:
//Gotta optimize this....
TestSwap( ar[ 0 ] , ar[ 1 ] );
TestSwap( ar[ 2 ] , ar[ 3 ] );
TestSwap( ar[ 0 ] , ar[ 2 ] );
TestSwap( ar[ 1 ] , ar[ 3 ] );
TestSwap( ar[ 1 ] , ar[ 2 ] );
break;
//Gotta do special case for list of five.
default:
//Subdivide the list, recurse, and merge
{
int s1 = size / 2;
int s2 = size - s1;
T* from1_ = scratch;
T* from2_ = scratch + s1;
MergeSortThis( from1_ , s1 , ar );
MergeSortThis( from2_ , s2 , ar + s1 );
T* to1_ = from1_ + s1;
T* to2_ = from2_ + s2;
for (;;)
{
if ( LessThan( *from1_, *from2_) )
{
*ar++ = *from1_++;
if (from1_ >= to1_)
{
while( from2_ < to2_ )
*ar++ = *from2_++;
break;
}
}
else
{
*ar++ = *from2_++;
if (from2_ >= to2_)
{
while( from1_ < to1_ )
*ar++ = *from1_++;
break;
}
}
}
}
}
}
void c4_SortSeq::MergeSort( T ar[] , int size )
{
if ( size > 1 )
{
T* scratch = d4_new T [size];
memcpy(scratch , ar , size * sizeof (T));
MergeSortThis(ar , size , scratch);
delete [] scratch;
}
}
c4_SortSeq::c4_SortSeq (c4_Sequence& seq_, c4_Sequence* down_)
: c4_FilterSeq (seq_), _info (0), _width (-1)
{
d4_assert(NumRows() == seq_.NumRows());
if (NumRows() > 0)
{
// down is a vector of flags, true to sort in reverse order
char* down = (char*) _down.SetBufferClear(NumHandlers());
// set the down flag for all properties to be sorted in reverse
if (down_)
for (int i = 0; i < NumHandlers(); ++i)
if (down_->PropIndex(NthPropId(i)) >= 0)
down[i] = 1;
_width = -1;
int n = NumHandlers() + 1;
_info = d4_new c4_SortInfo [n];
int j;
for (j = 0; j < NumHandlers(); ++j)
{
_info[j]._handler = & _seq.NthHandler(j);
_info[j]._context = _seq.HandlerContext(j);
}
_info[j]._handler = 0;
// everything is ready, go sort the row index vector
MergeSort((T*) &_rowMap.ElementAt(0), NumRows());
delete [] _info;
_info = 0;
FixupReverseMap();
}
}
c4_SortSeq::~c4_SortSeq ()
{
d4_assert(!_info);
}
int c4_SortSeq::Compare(int index_, c4_Cursor cursor_) const
{
d4_assert(cursor_._seq != 0);
const char* down = (const char*) _down.Contents();
d4_assert(_down.Size() <= NumHandlers());
c4_Bytes data;
for (int colNum = 0; colNum < NumHandlers(); ++colNum)
{
c4_Handler& h = NthHandler(colNum);
const c4_Sequence* hc = HandlerContext(colNum);
if (!cursor_._seq->Get(cursor_._index, h.PropId(), data))
h.ClearBytes(data);
int f = h.Compare(RemapIndex(index_, hc), data);
if (f != 0)
return colNum < _down.Size() && down[colNum] ? -f : +f;
}
return 0;
}
int c4_SortSeq::PosInMap(c4_Cursor cursor_) const
{
int i = 0;
while (i < NumRows())
if (Compare(i, cursor_) >= 0)
break;
else
++i;
d4_assert(i == NumRows() || Compare(i, cursor_) >= 0);
return i;
}
c4_Notifier* c4_SortSeq::PreChange(c4_Notifier& /*nf_*/)
{
if (!GetDependencies())
return 0;
#if 0
c4_Notifier* chg = d4_new c4_Notifier (this);
switch (nf_._type)
{
case c4_Notifier::kSetAt:
case c4_Notifier::kSet:
{
d4_assert(0); // also needs nested propagation
/*
change can require a move *and* a change of contents
*/
}
break;
case c4_Notifier::kInsertAt:
{
d4_assert(0); // this case isn't really difficult
}
break;
case c4_Notifier::kRemoveAt:
{
d4_assert(0); // nested propagation is too difficult for now
// i.e. can only use sort as last derived view
/*
possible solution:
if 1 row, simple
else if contig in map, also simple
else propagate reorder first, then delete contig
it can be done here, as multiple notifications,
by simulating n-1 SetAt's of first row in others
needs some map juggling, allow temp dup entries?
or perhaps more consistent with n separate removes
*/
}
break;
case c4_Notifier::kMove:
{
// incorrect: may need to move if recnum matters (recs same)
}
break;
}
return chg;
#endif
// d4_assert(0); // fail, cannot handle a view dependent on this one yet
return 0;
}
void c4_SortSeq::PostChange(c4_Notifier& nf_)
{
switch (nf_._type)
{
case c4_Notifier::kSet:
if (_seq.PropIndex(nf_._propId) > _width)
break; // cannot affect sort order, valuable optimization
case c4_Notifier::kSetAt:
{
int oi = (int) _revMap.GetAt(nf_._index);
d4_assert(oi >= 0);
c4_Cursor cursor (_seq, nf_._index);
// move the entry if the sort order has been disrupted
if ((oi > 0 && Compare(oi - 1, cursor) > 0) ||
(oi+1 < NumRows() && Compare(oi+1, cursor) < 0))
{
_rowMap.RemoveAt(oi);
_rowMap.InsertAt(PosInMap(cursor), nf_._index);
FixupReverseMap();
}
_width = NumHandlers(); // sorry, no more optimization
}
break;
case c4_Notifier::kInsertAt:
{
// if cursor was not set, it started out as a single Set
c4_Cursor cursor (_seq, nf_._index);
if (nf_._cursor)
cursor = *nf_._cursor;
for (int n = 0; n < NumRows(); ++n)
if ((int) _rowMap.GetAt(n) >= nf_._index)
_rowMap.ElementAt(n) += nf_._count;
int i = PosInMap(cursor);
_rowMap.InsertAt(i, 0, nf_._count);
for (int j = 0; j < nf_._count; ++j)
_rowMap.SetAt(i++, nf_._index + j);
FixupReverseMap();
_width = NumHandlers(); // sorry, no more optimization
}
break;
case c4_Notifier::kRemoveAt:
{
int lo = nf_._index;
int hi = nf_._index + nf_._count;
int j = 0;
for (int i = 0; i < NumRows(); ++i)
{
int n = (int) _rowMap.GetAt(i);
if (n >= hi)
_rowMap.ElementAt(i) -= nf_._count;
if (!(lo <= n && n < hi))
_rowMap.SetAt(j++, _rowMap.GetAt(i));
}
d4_assert(j + nf_._count == NumRows());
_rowMap.SetSize(j);
FixupReverseMap();
_width = NumHandlers(); // sorry, no more optimization
}
break;
case c4_Notifier::kMove:
{
// incorrect: may need to move if recnum matters (recs same)
}
break;
}
}
/////////////////////////////////////////////////////////////////////////////
class c4_ProjectSeq : public c4_DerivedSeq
{
c4_DWordArray _colMap; // a bit large, but bytes would be too small
bool _frozen;
int _omitCount; // if > 0 then this is a dynamic "project without"
public:
c4_ProjectSeq (c4_Sequence& seq_, c4_Sequence& in_, bool, c4_Sequence* out_);
virtual ~c4_ProjectSeq ();
virtual int NumHandlers() const;
virtual c4_Handler& NthHandler(int) const;
virtual const c4_Sequence* HandlerContext(int) const;
virtual int AddHandler(c4_Handler*);
virtual bool Get(int, int, c4_Bytes&);
virtual void Set(int, const c4_Property&, const c4_Bytes&);
};
/////////////////////////////////////////////////////////////////////////////
c4_ProjectSeq::c4_ProjectSeq (c4_Sequence& seq_, c4_Sequence& in_,
bool reorder_, c4_Sequence* out_)
: c4_DerivedSeq (seq_), _frozen (!reorder_ && !out_), _omitCount (0)
{
// build the array with column indexes
for (int j = 0; j < in_.NumHandlers(); ++j)
{
int propId = in_.NthPropId(j);
int idx = _seq.PropIndex(propId);
// if the j'th property is in the sequence, add it
if (idx >= 0)
{
// but only if it's not in the out_ view
if (out_ && out_->PropIndex(propId) >= 0)
++_omitCount;
else
_colMap.Add(idx);
}
}
// if only reordering, append remaining columns from original view
if (reorder_)
{
for (int i = 0; i < _seq.NumHandlers(); ++i)
{
int propId = _seq.NthPropId(i);
// only consider properties we did not deal with before
if (in_.PropIndex(propId) < 0)
_colMap.Add(i);
}
d4_assert(_colMap.GetSize() == _seq.NumHandlers());
}
}
c4_ProjectSeq::~c4_ProjectSeq ()
{
}
int c4_ProjectSeq::NumHandlers() const
{
return _frozen ? _colMap.GetSize() : _seq.NumHandlers() - _omitCount;
}
c4_Handler& c4_ProjectSeq::NthHandler(int colNum_) const
{
int n = colNum_ < _colMap.GetSize() ? _colMap.GetAt(colNum_) : colNum_;
return _seq.NthHandler(n);
}
const c4_Sequence* c4_ProjectSeq::HandlerContext(int colNum_) const
{
int n = colNum_ < _colMap.GetSize() ? _colMap.GetAt(colNum_) : colNum_;
return _seq.HandlerContext(n);
}
int c4_ProjectSeq::AddHandler(c4_Handler* handler_)
{
int n = _seq.AddHandler(handler_);
return _frozen ? _colMap.Add(n) : n - _omitCount;
}
bool c4_ProjectSeq::Get(int index_, int propId_, c4_Bytes& buf_)
{
// fixed in 1.8: check that the property is visible
return PropIndex(propId_) >= 0 && _seq.Get(index_, propId_, buf_);
}
void c4_ProjectSeq::Set(int index_, const c4_Property& prop_, const c4_Bytes& bytes_)
{
int n = _seq.NumHandlers();
_seq.Set(index_, prop_, bytes_);
// if the number of handlers changed, then one must have been added
if (n != _seq.NumHandlers())
{
d4_assert(n == _seq.NumHandlers() - 1);
if (_frozen)
_colMap.Add(n);
}
}
/////////////////////////////////////////////////////////////////////////////
c4_Sequence* f4_CreateFilter(c4_Sequence& seq_, c4_Cursor l_, c4_Cursor h_)
{
return d4_new c4_FilterSeq (seq_, l_, h_);
}
c4_Sequence* f4_CreateSort(c4_Sequence& seq_, c4_Sequence* down_)
{
return d4_new c4_SortSeq (seq_, down_);
}
c4_Sequence* f4_CreateProject(c4_Sequence& seq_, c4_Sequence& in_,
bool reorder_, c4_Sequence* out_)
{
return d4_new c4_ProjectSeq (seq_, in_, reorder_, out_);
}
/////////////////////////////////////////////////////////////////////////////