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