1 // derived.cpp --
2 // This is part of Metakit, see http://www.equi4.com/metakit.html
3 
4 /** @file
5  * Derived views are virtual views which track changes
6  */
7 
8 #include "header.h"
9 #include "handler.h"
10 #include "store.h"
11 #include "derived.h"
12 
13 #include <stdlib.h>   // qsort
14 
15 /////////////////////////////////////////////////////////////////////////////
16 // Implemented in this file
17 
18 //  class c4_Sequence;
19 class c4_DerivedSeq;
20 class c4_FilterSeq;
21 class c4_SortSeq;
22 class c4_ProjectSeq;
23 
24 /////////////////////////////////////////////////////////////////////////////
25 
26 class c4_FilterSeq : public c4_DerivedSeq
27 {
28 protected:
29     c4_DWordArray _rowMap;
30     c4_DWordArray _revMap;
31     c4_Row _lowRow;
32     c4_Row _highRow;
33     c4_Bytes _rowIds;
34 
35 protected:
36     c4_FilterSeq(c4_Sequence &seq_);
37     ~c4_FilterSeq() override;
38 
39     void FixupReverseMap();
40     int PosInMap(int index_) const;
41     bool Match(int index_, c4_Sequence &seq_, const int * = nullptr, const int * = nullptr)
42     const;
43     bool MatchOne(int prop_, const c4_Bytes &data_) const;
44 
45 public:
46     c4_FilterSeq(c4_Sequence &seq_, c4_Cursor low_, c4_Cursor high_);
47 
48     int RemapIndex(int, const c4_Sequence *) const override;
49 
50     int NumRows() const override;
51 
52     int Compare(int, c4_Cursor) const override;
53     bool Get(int, int, c4_Bytes &) override;
54 
55     void InsertAt(int, c4_Cursor, int = 1) override;
56     void RemoveAt(int, int = 1) override;
57     void Set(int, const c4_Property &, const c4_Bytes &) override;
58     virtual void SetSize(int);
59 
60     c4_Notifier *PreChange(c4_Notifier &nf_) override;
61     void PostChange(c4_Notifier &nf_) override;
62 };
63 
64 /////////////////////////////////////////////////////////////////////////////
65 
c4_FilterSeq(c4_Sequence & seq_)66 c4_FilterSeq::c4_FilterSeq(c4_Sequence &seq_) : c4_DerivedSeq(seq_)
67 {
68     _rowMap.SetSize(_seq.NumRows());
69     _revMap.SetSize(_seq.NumRows());
70     d4_assert(NumRows() == _seq.NumRows());
71 
72     for (int i = 0; i < NumRows(); ++i) {
73         _rowMap.SetAt(i, i);
74         _revMap.SetAt(i, i);
75     }
76 }
77 
c4_FilterSeq(c4_Sequence & seq_,c4_Cursor low_,c4_Cursor high_)78 c4_FilterSeq::c4_FilterSeq(c4_Sequence &seq_, c4_Cursor low_, c4_Cursor high_)
79     : c4_DerivedSeq(seq_)
80     , _lowRow(*low_)
81     , _highRow(*high_)
82 {
83     d4_assert((&_lowRow)._index == 0);
84     d4_assert((&_highRow)._index == 0);
85 
86     // use a sneaky way to obtain the sequence pointers and indices
87     c4_Sequence *lowSeq = (&_lowRow)._seq;
88     c4_Sequence *highSeq = (&_highRow)._seq;
89     d4_assert(lowSeq && highSeq);
90 
91     // prepare column numbers to avoid looking them up on every row
92     // lowCols is a vector of column numbers to use for the low limits
93     // highCols is a vector of column numbers to use for the high limits
94     int nl = lowSeq->NumHandlers();
95     int nh = highSeq->NumHandlers();
96     c4_Bytes lowVec;
97     c4_Bytes highVec;
98     int *lowCols = (int *)lowVec.SetBufferClear(nl * sizeof(int));
99     int *highCols = (int *)highVec.SetBufferClear(nh * sizeof(int));
100 
101     for (int il = 0; il < nl; ++il) {
102         lowCols[il] = seq_.PropIndex(lowSeq->NthPropId(il));
103     }
104     for (int ih = 0; ih < nh; ++ih) {
105         highCols[ih] = seq_.PropIndex(highSeq->NthPropId(ih));
106     }
107 
108     // set _rowIds flag buffer for fast matching
109     {
110         int max = -1;
111 
112         {
113             for (int i1 = 0; i1 < nl; ++i1) {
114                 int n = lowSeq->NthPropId(i1);
115                 if (max < n) {
116                     max = n;
117                 }
118             }
119             for (int i2 = 0; i2 < nh; ++i2) {
120                 int n = highSeq->NthPropId(i2);
121                 if (max < n) {
122                     max = n;
123                 }
124             }
125         }
126 
127         t4_byte *p = _rowIds.SetBufferClear(max + 1);
128 
129         {
130             for (int i1 = 0; i1 < nl; ++i1) {
131                 p[lowSeq->NthPropId(i1)] |= 1;
132             }
133             for (int i2 = 0; i2 < nh; ++i2) {
134                 p[highSeq->NthPropId(i2)] |= 2;
135             }
136         }
137     }
138 
139     // now go through all rows and select the ones that are in range
140 
141     _rowMap.SetSize(_seq.NumRows()); // avoid growing, use safe upper bound
142 
143     int n = 0;
144 
145     for (int i = 0; i < _seq.NumRows(); ++i) {
146         if (Match(i, _seq, lowCols, highCols)) {
147             _rowMap.SetAt(n++, i);
148         }
149     }
150 
151     _rowMap.SetSize(n);
152 
153     FixupReverseMap();
154 }
155 
~c4_FilterSeq()156 c4_FilterSeq::~c4_FilterSeq()
157 {
158 }
159 
FixupReverseMap()160 void c4_FilterSeq::FixupReverseMap()
161 {
162     int n = _seq.NumRows();
163 
164     _revMap.SetSize(0);
165 
166     if (n > 0) {
167         _revMap.InsertAt(0, ~(t4_i32)0, n); //!
168 
169         for (int i = 0; i < _rowMap.GetSize(); ++i) {
170             _revMap.SetAt((int)_rowMap.GetAt(i), i);
171         }
172     }
173 }
174 
MatchOne(int prop_,const c4_Bytes & data_) const175 bool c4_FilterSeq::MatchOne(int prop_, const c4_Bytes &data_) const
176 {
177     d4_assert(prop_ < _rowIds.Size());
178 
179     t4_byte flag = _rowIds.Contents()[prop_];
180     d4_assert(flag);
181 
182     if (flag & 1) {
183         c4_Sequence *lowSeq = (&_lowRow)._seq;
184 
185         c4_Handler &h = lowSeq->NthHandler(lowSeq->PropIndex(prop_));
186         if (h.Compare(0, data_) > 0) {
187             return false;
188         }
189     }
190 
191     if (flag & 2) {
192         c4_Sequence *highSeq = (&_highRow)._seq;
193 
194         c4_Handler &h = highSeq->NthHandler(highSeq->PropIndex(prop_));
195         if (h.Compare(0, data_) < 0) {
196             return false;
197         }
198     }
199 
200     return true;
201 }
202 
Match(int index_,c4_Sequence & seq_,const int * lowCols_,const int * highCols_) const203 bool c4_FilterSeq::Match(int index_, c4_Sequence &seq_, const int *lowCols_, const int *highCols_) const
204 {
205     // use a sneaky way to obtain the sequence pointers and indices
206     c4_Sequence *lowSeq = (&_lowRow)._seq;
207     c4_Sequence *highSeq = (&_highRow)._seq;
208     d4_assert(lowSeq && highSeq);
209 
210     int nl = lowSeq->NumHandlers();
211     int nh = highSeq->NumHandlers();
212 
213     c4_Bytes data;
214 
215     // check each of the lower limits
216     for (int cl = 0; cl < nl; ++cl) {
217         c4_Handler &hl = lowSeq->NthHandler(cl);
218 
219         int n = lowCols_ ? lowCols_[cl] : seq_.PropIndex(lowSeq->NthPropId(cl));
220         if (n >= 0) {
221             c4_Handler &h = seq_.NthHandler(n);
222             const c4_Sequence *hc = seq_.HandlerContext(n);
223             int i = seq_.RemapIndex(index_, hc);
224 
225             h.GetBytes(i, data);
226         } else {
227             hl.ClearBytes(data);
228         }
229 
230         if (hl.Compare(0, data) > 0) {
231             return false;
232         }
233     }
234 
235     // check each of the upper limits
236     for (int ch = 0; ch < nh; ++ch) {
237         c4_Handler &hh = highSeq->NthHandler(ch);
238 
239         int n = highCols_ ? highCols_[ch] : seq_.PropIndex(highSeq->NthPropId(ch));
240         if (n >= 0) {
241             c4_Handler &h = seq_.NthHandler(n);
242             const c4_Sequence *hc = seq_.HandlerContext(n);
243             int i = seq_.RemapIndex(index_, hc);
244 
245             h.GetBytes(i, data);
246         } else {
247             hh.ClearBytes(data);
248         }
249 
250         if (hh.Compare(0, data) < 0) {
251             return false;
252         }
253     }
254 
255     return true;
256 }
257 
RemapIndex(int index_,const c4_Sequence * seq_) const258 int c4_FilterSeq::RemapIndex(int index_, const c4_Sequence *seq_) const
259 {
260     return seq_ == this ? index_ : _seq.RemapIndex((int)_rowMap.GetAt(index_),
261                                                    seq_);
262 }
263 
NumRows() const264 int c4_FilterSeq::NumRows() const
265 {
266     return _rowMap.GetSize();
267 }
268 
Compare(int index_,c4_Cursor cursor_) const269 int c4_FilterSeq::Compare(int index_, c4_Cursor cursor_) const
270 {
271     return _seq.Compare((int)_rowMap.GetAt(index_), cursor_);
272 }
273 
Get(int index_,int propId_,c4_Bytes & bytes_)274 bool c4_FilterSeq::Get(int index_, int propId_, c4_Bytes &bytes_)
275 {
276     return _seq.Get((int)_rowMap.GetAt(index_), propId_, bytes_);
277 }
278 
InsertAt(int,c4_Cursor,int)279 void c4_FilterSeq::InsertAt(int, c4_Cursor, int)
280 {
281     d4_assert(0);
282 }
283 
RemoveAt(int,int)284 void c4_FilterSeq::RemoveAt(int, int)
285 {
286     d4_assert(0);
287 }
288 
Set(int,const c4_Property &,const c4_Bytes &)289 void c4_FilterSeq::Set(int, const c4_Property &, const c4_Bytes &)
290 {
291     d4_assert(0);
292 }
293 
SetSize(int)294 void c4_FilterSeq::SetSize(int)
295 {
296     d4_assert(0);
297 }
298 
PosInMap(int index_) const299 int c4_FilterSeq::PosInMap(int index_) const
300 {
301     int i = 0;
302 
303     while (i < NumRows()) {
304         if ((int)_rowMap.GetAt(i) >= index_) {
305             break;
306         } else {
307             ++i;
308         }
309     }
310 
311     return i;
312 }
313 
PreChange(c4_Notifier & nf_)314 c4_Notifier *c4_FilterSeq::PreChange(c4_Notifier &nf_)
315 {
316     if (!GetDependencies()) {
317         return nullptr;
318     }
319 
320     c4_Notifier *chg = d4_new c4_Notifier(this);
321 
322     bool pass = false;
323 
324     switch (nf_._type) {
325     case c4_Notifier::kSet:
326         pass = nf_._propId >= _rowIds.Size()
327                || _rowIds.Contents()[nf_._propId] == 0;
328     // fall through...
329 
330     case c4_Notifier::kSetAt:
331     {
332         int r = (int)_revMap.GetAt(nf_._index);
333 
334         bool includeRow = r >= 0;
335         if (!pass) {
336             if (nf_._type == c4_Notifier::kSetAt) {
337                 d4_assert(nf_._cursor != 0);
338                 includeRow = Match(nf_._cursor->_index, *nf_._cursor->_seq);
339             } else {
340                 // set just one property, and it's not in a row yet
341                 includeRow = MatchOne(nf_._propId, *nf_._bytes);
342             }
343         }
344 
345         if (r >= 0 && !includeRow) {
346             chg->StartRemoveAt(r, 1);
347         } else if (r < 0 && includeRow) {
348             chg->StartInsertAt(PosInMap(nf_._index), *nf_._cursor, 1);
349         } else if (includeRow) {
350             d4_assert(r >= 0);
351 
352             if (nf_._type == c4_Notifier::kSetAt) {
353                 chg->StartSetAt(r, *nf_._cursor);
354             } else {
355                 chg->StartSet(r, nf_._propId, *nf_._bytes);
356             }
357         }
358         break;
359     }
360 
361     case c4_Notifier::kInsertAt:
362     {
363         int i = PosInMap(nf_._index);
364 
365         d4_assert(nf_._cursor != 0);
366         if (Match(nf_._cursor->_index, *nf_._cursor->_seq)) {
367             chg->StartInsertAt(i, *nf_._cursor, nf_._count);
368         }
369         break;
370     }
371 
372     case c4_Notifier::kRemoveAt:
373     {
374         int i = PosInMap(nf_._index);
375         int j = PosInMap(nf_._index + nf_._count);
376         d4_assert(j >= i);
377 
378         if (j > i) {
379             chg->StartRemoveAt(i, j - i);
380         }
381         break;
382     }
383 
384     case c4_Notifier::kMove:
385     {
386         int i = PosInMap(nf_._index);
387         bool inMap = i < NumRows() && (int)_rowMap.GetAt(i) == nf_._index;
388 
389         if (inMap && nf_._index != nf_._count) {
390             chg->StartMove(i, PosInMap(nf_._count));
391         }
392         break;
393     }
394     }
395 
396     return chg;
397 }
398 
PostChange(c4_Notifier & nf_)399 void c4_FilterSeq::PostChange(c4_Notifier &nf_)
400 {
401     bool pass = false;
402 
403     switch (nf_._type) {
404     case c4_Notifier::kSet:
405         pass = nf_._propId >= _rowIds.Size()
406                || _rowIds.Contents()[nf_._propId] == 0;
407     // fall through...
408 
409     case c4_Notifier::kSetAt:
410     {
411         int r = (int)_revMap.GetAt(nf_._index);
412 
413         bool includeRow = r >= 0;
414         if (!pass) {
415             if (nf_._type == c4_Notifier::kSetAt) {
416                 d4_assert(nf_._cursor != 0);
417                 includeRow = Match(nf_._cursor->_index, *nf_._cursor->_seq);
418             } else {
419                 // set just one property, and it's not in a row yet
420                 includeRow = MatchOne(nf_._propId, *nf_._bytes);
421             }
422         }
423 
424         if (r >= 0 && !includeRow) {
425             _rowMap.RemoveAt(r);
426         } else if (r < 0 && includeRow) {
427             _rowMap.InsertAt(PosInMap(nf_._index), nf_._index);
428         } else {
429             break;
430         }
431 
432         FixupReverseMap();
433         break;
434     }
435 
436     case c4_Notifier::kInsertAt:
437     {
438         int i = PosInMap(nf_._index);
439 
440         if (Match(nf_._index, _seq)) {
441             _rowMap.InsertAt(i, 0, nf_._count);
442 
443             for (int j = 0; j < nf_._count; ++j) {
444                 _rowMap.SetAt(i++, nf_._index + j);
445             }
446         }
447 
448         while (i < NumRows()) {
449             _rowMap.ElementAt(i++) += nf_._count;
450         }
451 
452         FixupReverseMap();
453         break;
454     }
455 
456     case c4_Notifier::kRemoveAt:
457     {
458         int i = PosInMap(nf_._index);
459         int j = PosInMap(nf_._index + nf_._count);
460         d4_assert(j >= i);
461 
462         if (j > i) {
463             _rowMap.RemoveAt(i, j - i);
464         }
465 
466         while (i < NumRows()) {
467             _rowMap.ElementAt(i++) -= nf_._count;
468         }
469 
470         FixupReverseMap();
471         break;
472     }
473 
474     case c4_Notifier::kMove:
475     {
476         int i = PosInMap(nf_._index);
477         bool inMap = i < NumRows() && (int)_rowMap.GetAt(i) == nf_._index;
478 
479         if (inMap && nf_._index != nf_._count) {
480             int j = PosInMap(nf_._count);
481 
482             _rowMap.RemoveAt(i);
483 
484             if (j > i) {
485                 --j;
486             }
487 
488             _rowMap.InsertAt(j, nf_._count);
489 
490             FixupReverseMap();
491         }
492         break;
493     }
494     }
495 }
496 
497 /////////////////////////////////////////////////////////////////////////////
498 
499 class c4_SortSeq : public c4_FilterSeq
500 {
501 public:
502     typedef t4_i32 T;
503 
504     c4_SortSeq(c4_Sequence &seq_, c4_Sequence *down_);
505     ~c4_SortSeq() override;
506 
507     c4_Notifier *PreChange(c4_Notifier &nf_) override;
508     void PostChange(c4_Notifier &nf_) override;
509 
510 private:
511     struct c4_SortInfo {
512         c4_Handler *_handler;
513         const c4_Sequence *_context;
514         c4_Bytes _buffer;
515 
CompareOnec4_SortSeq::c4_SortInfo516         int CompareOne(c4_Sequence &seq_, T a, T b)
517         {
518             _handler->GetBytes(seq_.RemapIndex((int)b, _context), _buffer, true)
519             ;
520             return _handler->Compare(seq_.RemapIndex((int)a, _context), _buffer)
521             ;
522         }
523     };
524 
525     bool LessThan(T a, T b);
526     bool TestSwap(T &first, T &second);
527     void MergeSortThis(T *ar, int size, T scratch[]);
528     void MergeSort(T ar[], int size);
529 
530     int Compare(int, c4_Cursor) const override;
531     int PosInMap(c4_Cursor cursor_) const;
532 
533     c4_SortInfo *_info;
534     c4_Bytes _down;
535     int _width;
536 };
537 
538 /////////////////////////////////////////////////////////////////////////////
539 
LessThan(T a,T b)540 bool c4_SortSeq::LessThan(T a, T b)
541 {
542     if (a == b) {
543         return false;
544     }
545 
546     // go through each of the columns and compare values, but since
547     // handler access is used, we must be careful to remap indices
548 
549     c4_SortInfo *info;
550 
551     for (info = _info; info->_handler; ++info) {
552         int f = info->CompareOne(_seq, a, b);
553         if (f) {
554             int n = info - _info;
555             if (_width < n) {
556                 _width = n;
557             }
558 
559             return (_down.Contents()[n] ? -f : f) < 0;
560         }
561     }
562 
563     _width = info - _info;
564     return a < b;
565 }
566 
TestSwap(T & first,T & second)567 inline bool c4_SortSeq::TestSwap(T &first, T &second)
568 {
569     if (LessThan(second, first)) {
570         T temp = first;
571         first = second;
572         second = temp;
573         return true;
574     }
575 
576     return false;
577 }
578 
MergeSortThis(T * ar,int size,T scratch[])579 void c4_SortSeq::MergeSortThis(T *ar, int size, T scratch[])
580 {
581     switch (size) {
582     //Handle the special cases for speed:
583     case 2:
584         TestSwap(ar[0], ar[1]);
585         break;
586 
587     case 3:
588         TestSwap(ar[0], ar[1]);
589         if (TestSwap(ar[1], ar[2])) {
590             TestSwap(ar[0], ar[1]);
591         }
592         break;
593 
594     case 4:
595         //Gotta optimize this....
596         TestSwap(ar[0], ar[1]);
597         TestSwap(ar[2], ar[3]);
598         TestSwap(ar[0], ar[2]);
599         TestSwap(ar[1], ar[3]);
600         TestSwap(ar[1], ar[2]);
601         break;
602 
603     //Gotta do special case for list of five.
604 
605     default:
606         //Subdivide the list, recurse, and merge
607     {
608         int s1 = size / 2;
609         int s2 = size - s1;
610         T *from1_ = scratch;
611         T *from2_ = scratch + s1;
612         MergeSortThis(from1_, s1, ar);
613         MergeSortThis(from2_, s2, ar + s1);
614 
615         T *to1_ = from1_ + s1;
616         T *to2_ = from2_ + s2;
617 
618         for (;;) {
619             if (LessThan(*from1_, *from2_)) {
620                 *ar++ = *from1_++;
621 
622                 if (from1_ >= to1_) {
623                     while (from2_ < to2_) {
624                         *ar++ = *from2_++;
625                     }
626                     break;
627                 }
628             } else {
629                 *ar++ = *from2_++;
630 
631                 if (from2_ >= to2_) {
632                     while (from1_ < to1_) {
633                         *ar++ = *from1_++;
634                     }
635                     break;
636                 }
637             }
638         }
639     }
640     }
641 }
642 
MergeSort(T ar[],int size)643 void c4_SortSeq::MergeSort(T ar[], int size)
644 {
645     if (size > 1) {
646         T *scratch = d4_new T[size];
647         memcpy(scratch, ar, size * sizeof(T));
648         MergeSortThis(ar, size, scratch);
649         delete [] scratch;
650     }
651 }
652 
c4_SortSeq(c4_Sequence & seq_,c4_Sequence * down_)653 c4_SortSeq::c4_SortSeq(c4_Sequence &seq_, c4_Sequence *down_) : c4_FilterSeq
654         (seq_)
655     , _info(nullptr)
656     , _width(-1)
657 {
658     d4_assert(NumRows() == seq_.NumRows());
659 
660     if (NumRows() > 0) {
661         // down is a vector of flags, true to sort in reverse order
662         char *down = (char *)_down.SetBufferClear(NumHandlers());
663 
664         // set the down flag for all properties to be sorted in reverse
665         if (down_) {
666             for (int i = 0; i < NumHandlers(); ++i) {
667                 if (down_->PropIndex(NthPropId(i)) >= 0) {
668                     down[i] = 1;
669                 }
670             }
671         }
672 
673         _width = -1;
674         int n = NumHandlers() + 1;
675         _info = d4_new c4_SortInfo[n];
676 
677         int j;
678 
679         for (j = 0; j < NumHandlers(); ++j) {
680             _info[j]._handler = &_seq.NthHandler(j);
681             _info[j]._context = _seq.HandlerContext(j);
682         }
683 
684         _info[j]._handler = nullptr;
685 
686         // everything is ready, go sort the row index vector
687         MergeSort((T *)&_rowMap.ElementAt(0), NumRows());
688 
689         delete [] _info;
690         _info = nullptr;
691 
692         FixupReverseMap();
693     }
694 }
695 
~c4_SortSeq()696 c4_SortSeq::~c4_SortSeq()
697 {
698     d4_assert(!_info);
699 }
700 
Compare(int index_,c4_Cursor cursor_) const701 int c4_SortSeq::Compare(int index_, c4_Cursor cursor_) const
702 {
703     d4_assert(cursor_._seq != 0);
704 
705     const char *down = (const char *)_down.Contents();
706     d4_assert(_down.Size() <= NumHandlers());
707 
708     c4_Bytes data;
709 
710     for (int colNum = 0; colNum < NumHandlers(); ++colNum) {
711         c4_Handler &h = NthHandler(colNum);
712         const c4_Sequence *hc = HandlerContext(colNum);
713 
714         if (!cursor_._seq->Get(cursor_._index, h.PropId(), data)) {
715             h.ClearBytes(data);
716         }
717 
718         int f = h.Compare(RemapIndex(index_, hc), data);
719         if (f != 0) {
720             return colNum < _down.Size() && down[colNum] ? -f : +f;
721         }
722     }
723 
724     return 0;
725 }
726 
PosInMap(c4_Cursor cursor_) const727 int c4_SortSeq::PosInMap(c4_Cursor cursor_) const
728 {
729     int i = 0;
730 
731     while (i < NumRows()) {
732         if (Compare(i, cursor_) >= 0) {
733             break;
734         } else {
735             ++i;
736         }
737     }
738 
739     d4_assert(i == NumRows() || Compare(i, cursor_) >= 0);
740     return i;
741 }
742 
PreChange(c4_Notifier &)743 c4_Notifier *c4_SortSeq::PreChange(c4_Notifier & /*nf_*/)
744 {
745     if (!GetDependencies()) {
746         return nullptr;
747     }
748 
749 #if 0
750     c4_Notifier *chg = d4_new c4_Notifier(this);
751 
752     switch (nf_._type) {
753     case c4_Notifier::kSetAt:
754     case c4_Notifier::kSet:
755         d4_assert(0); // also needs nested propagation
756 
757         /*
758         change can require a move *and* a change of contents
759          */
760         break;
761 
762     case c4_Notifier::kInsertAt:
763         d4_assert(0); // this case isn't really difficult
764         break;
765 
766     case c4_Notifier::kRemoveAt:
767         d4_assert(0); // nested propagation is too difficult for now
768         // i.e. can only use sort as last derived view
769         /*
770         possible solution:
771 
772         if 1 row, simple
773         else if contig in map, also simple
774         else propagate reorder first, then delete contig
775 
776         it can be done here, as multiple notifications,
777         by simulating n-1 SetAt's of first row in others
778         needs some map juggling, allow temp dup entries?
779 
780         or perhaps more consistent with n separate removes
781          */
782         break;
783 
784     case c4_Notifier::kMove:
785         // incorrect: may need to move if recnum matters (recs same)
786         break;
787     }
788 
789     return chg;
790 #endif
791 
792     //  d4_assert(0); // fail, cannot handle a view dependent on this one yet
793     return nullptr;
794 }
795 
PostChange(c4_Notifier & nf_)796 void c4_SortSeq::PostChange(c4_Notifier &nf_)
797 {
798     switch (nf_._type) {
799     case c4_Notifier::kSet:
800         if (_seq.PropIndex(nf_._propId) > _width) {
801             break;
802         }
803     // cannot affect sort order, valuable optimization
804 
805     case c4_Notifier::kSetAt:
806     {
807         int oi = (int)_revMap.GetAt(nf_._index);
808         d4_assert(oi >= 0);
809 
810         c4_Cursor cursor(_seq, nf_._index);
811 
812         // move the entry if the sort order has been disrupted
813         if ((oi > 0 && Compare(oi - 1, cursor) > 0) || (oi + 1 < NumRows()
814                                                         && Compare(oi + 1, cursor) < 0)) {
815             _rowMap.RemoveAt(oi);
816             _rowMap.InsertAt(PosInMap(cursor), nf_._index);
817 
818             FixupReverseMap();
819         }
820 
821         _width = NumHandlers(); // sorry, no more optimization
822         break;
823     }
824 
825     case c4_Notifier::kInsertAt:
826     {
827         // if cursor was not set, it started out as a single Set
828         c4_Cursor cursor(_seq, nf_._index);
829         if (nf_._cursor) {
830             cursor = *nf_._cursor;
831         }
832 
833         for (int n = 0; n < NumRows(); ++n) {
834             if ((int)_rowMap.GetAt(n) >= nf_._index) {
835                 _rowMap.ElementAt(n) += nf_._count;
836             }
837         }
838 
839         int i = PosInMap(cursor);
840         _rowMap.InsertAt(i, 0, nf_._count);
841 
842         for (int j = 0; j < nf_._count; ++j) {
843             _rowMap.SetAt(i++, nf_._index + j);
844         }
845 
846         FixupReverseMap();
847 
848         _width = NumHandlers(); // sorry, no more optimization
849         break;
850     }
851 
852     case c4_Notifier::kRemoveAt:
853     {
854         int lo = nf_._index;
855         int hi = nf_._index + nf_._count;
856 
857         int j = 0;
858         for (int i = 0; i < NumRows(); ++i) {
859             int n = (int)_rowMap.GetAt(i);
860 
861             if (n >= hi) {
862                 _rowMap.ElementAt(i) -= nf_._count;
863             }
864 
865             if (!(lo <= n && n < hi)) {
866                 _rowMap.SetAt(j++, _rowMap.GetAt(i));
867             }
868         }
869 
870         d4_assert(j + nf_._count == NumRows());
871         _rowMap.SetSize(j);
872 
873         FixupReverseMap();
874 
875         _width = NumHandlers(); // sorry, no more optimization
876         break;
877     }
878 
879     case c4_Notifier::kMove:
880         // incorrect: may need to move if recnum matters (recs same)
881         break;
882     }
883 }
884 
885 /////////////////////////////////////////////////////////////////////////////
886 
887 class c4_ProjectSeq : public c4_DerivedSeq
888 {
889     c4_DWordArray _colMap; // a bit large, but bytes would be too small
890     bool _frozen;
891     int _omitCount; // if > 0 then this is a dynamic "project without"
892 
893 public:
894     c4_ProjectSeq(c4_Sequence &seq_, c4_Sequence &in_, bool, c4_Sequence *out_);
895     ~c4_ProjectSeq() override;
896 
897     int NumHandlers() const override;
898     c4_Handler &NthHandler(int) const override;
899     const c4_Sequence *HandlerContext(int) const override;
900     int AddHandler(c4_Handler *) override;
901 
902     bool Get(int, int, c4_Bytes &) override;
903     void Set(int, const c4_Property &, const c4_Bytes &) override;
904 };
905 
906 /////////////////////////////////////////////////////////////////////////////
907 
c4_ProjectSeq(c4_Sequence & seq_,c4_Sequence & in_,bool reorder_,c4_Sequence * out_)908 c4_ProjectSeq::c4_ProjectSeq(c4_Sequence &seq_, c4_Sequence &in_, bool reorder_, c4_Sequence *out_) : c4_DerivedSeq(seq_)
909     , _frozen(!reorder_ && !out_)
910     , _omitCount(0)
911 {
912     // build the array with column indexes
913     for (int j = 0; j < in_.NumHandlers(); ++j) {
914         int propId = in_.NthPropId(j);
915         int idx = _seq.PropIndex(propId);
916 
917         // if the j'th property is in the sequence, add it
918         if (idx >= 0) {
919             // but only if it's not in the out_ view
920             if (out_ && out_->PropIndex(propId) >= 0) {
921                 ++_omitCount;
922             } else {
923                 _colMap.Add(idx);
924             }
925         }
926     }
927 
928     // if only reordering, append remaining columns from original view
929     if (reorder_) {
930         for (int i = 0; i < _seq.NumHandlers(); ++i) {
931             int propId = _seq.NthPropId(i);
932 
933             // only consider properties we did not deal with before
934             if (in_.PropIndex(propId) < 0) {
935                 _colMap.Add(i);
936             }
937         }
938 
939         d4_assert(_colMap.GetSize() == _seq.NumHandlers());
940     }
941 }
942 
~c4_ProjectSeq()943 c4_ProjectSeq::~c4_ProjectSeq()
944 {
945 }
946 
NumHandlers() const947 int c4_ProjectSeq::NumHandlers() const
948 {
949     return _frozen ? _colMap.GetSize() : _seq.NumHandlers() - _omitCount;
950 }
951 
NthHandler(int colNum_) const952 c4_Handler &c4_ProjectSeq::NthHandler(int colNum_) const
953 {
954     int n = colNum_ < _colMap.GetSize() ? _colMap.GetAt(colNum_) : colNum_;
955     return _seq.NthHandler(n);
956 }
957 
HandlerContext(int colNum_) const958 const c4_Sequence *c4_ProjectSeq::HandlerContext(int colNum_) const
959 {
960     int n = colNum_ < _colMap.GetSize() ? _colMap.GetAt(colNum_) : colNum_;
961     return _seq.HandlerContext(n);
962 }
963 
AddHandler(c4_Handler * handler_)964 int c4_ProjectSeq::AddHandler(c4_Handler *handler_)
965 {
966     int n = _seq.AddHandler(handler_);
967     return _frozen ? _colMap.Add(n) : n - _omitCount;
968 }
969 
Get(int index_,int propId_,c4_Bytes & buf_)970 bool c4_ProjectSeq::Get(int index_, int propId_, c4_Bytes &buf_)
971 {
972     // fixed in 1.8: check that the property is visible
973     return PropIndex(propId_) >= 0 && _seq.Get(index_, propId_, buf_);
974 }
975 
Set(int index_,const c4_Property & prop_,const c4_Bytes & bytes_)976 void c4_ProjectSeq::Set(int index_, const c4_Property &prop_, const c4_Bytes
977                         &bytes_)
978 {
979     int n = _seq.NumHandlers();
980     _seq.Set(index_, prop_, bytes_);
981 
982     // if the number of handlers changed, then one must have been added
983     if (n != _seq.NumHandlers()) {
984         d4_assert(n == _seq.NumHandlers() - 1);
985 
986         if (_frozen) {
987             _colMap.Add(n);
988         }
989     }
990 }
991 
992 /////////////////////////////////////////////////////////////////////////////
993 
f4_CreateFilter(c4_Sequence & seq_,c4_Cursor l_,c4_Cursor h_)994 c4_Sequence *f4_CreateFilter(c4_Sequence &seq_, c4_Cursor l_, c4_Cursor h_)
995 {
996     return d4_new c4_FilterSeq(seq_, l_, h_);
997 }
998 
f4_CreateSort(c4_Sequence & seq_,c4_Sequence * down_)999 c4_Sequence *f4_CreateSort(c4_Sequence &seq_, c4_Sequence *down_)
1000 {
1001     return d4_new c4_SortSeq(seq_, down_);
1002 }
1003 
f4_CreateProject(c4_Sequence & seq_,c4_Sequence & in_,bool reorder_,c4_Sequence * out_)1004 c4_Sequence *f4_CreateProject(c4_Sequence &seq_, c4_Sequence &in_, bool
1005                               reorder_, c4_Sequence *out_)
1006 {
1007     return d4_new c4_ProjectSeq(seq_, in_, reorder_, out_);
1008 }
1009 
1010 /////////////////////////////////////////////////////////////////////////////
1011