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