Gaia
searchspacepool_impl.h
1 /*
2  * Copyright (C) 2006-2013 Music Technology Group - Universitat Pompeu Fabra
3  *
4  * This file is part of Gaia
5  *
6  * Gaia is free software: you can redistribute it and/or modify it under
7  * the terms of the GNU Affero General Public License as published by the Free
8  * Software Foundation (FSF), either version 3 of the License, or (at your
9  * option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful, but WITHOUT
12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
13  * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
14  * details.
15  *
16  * You should have received a copy of the Affero GNU General Public License
17  * version 3 along with this program. If not, see http://www.gnu.org/licenses/
18  */
19 
20 
21 
22 // this function assumes that the memory has already been allocated
23 template <typename SearchPointType, typename DataSetType>
24 void copySearchPoints(BaseSearchSpace<SearchPointType, DataSetType>* dest,
25  const BaseSearchSpace<SearchPointType, DataSetType>* src,
26  int start, int end, int destStart) {
27  dest->setDataSet(src->dataSet());
28  int howmany = end - start;
29  if (howmany > 0) { // cause memcpy doesn't necessarily like copies of size 0
30  SearchPointType* destp = &((*dest)[destStart]);
31  const SearchPointType* srcp = &((*src)[start]);
32  memcpy(destp, srcp, howmany * sizeof(SearchPointType));
33  }
34 }
35 
36 // this function allocates the memory for dest
37 template <typename SearchPointType, typename DataSetType>
38 void copySearchPoints(BaseSearchSpace<SearchPointType, DataSetType>* dest,
39  const BaseSearchSpace<SearchPointType, DataSetType>* src,
40  int start, int end) {
41  dest->resize(end - start);
42  copySearchPoints(dest, src, start, end, 0);
43 }
44 
45 template <typename SearchPointType, typename DataSetType>
46 void copySearchPoints(BaseSearchSpace<SearchPointType, DataSetType>* dest,
47  const BaseSearchSpace<SearchPointType, DataSetType>* src) {
48  copySearchPoints(dest, src, 0, src->size());
49 }
50 
51 
52 template <typename SearchPointType, typename DataSetType>
53 BaseSearchSpace<SearchPointType, DataSetType>* BaseSearchSpacePool<SearchPointType, DataSetType>::ValueIndex::getSubSpaceInto(SearchSpaceType* result, int start, int end) const {
54  // get the maximum pre-sorted subspace we can get, then use set_union to merge it
55  // with the remaining part. this should be much sorter than just getting the whole
56  // stuff and then sorting it.
57 
58  result->setDataSet(sspace->dataSet());
59  result->resize(end - start);
60  QSet<int> stepsSet;
61  QPair<int, int> key;
62  foreach (key, sorted.keys()) {
63  stepsSet << key.first << key.second;
64  }
65  QList<int> steps = QList<int>::fromSet(stepsSet);
66  sort(steps);
67 
68  Q_ASSERT(steps.first() == 0 && steps.last() == sspace->size());
69 
70  int i;
71 
72  if (start == 0) {
73  for (i=0; i<steps.size(); i++) {
74  if (end < steps[i]) break;
75  }
76  i--;
77 
78  if (i <= 0) { // no need to merge
79  copySearchPoints(result, sspace, start, end);
80  result->pointerSort();
81  }
82  else { // merge with presorted chunk
83  SearchSpaceType* presorted = sorted[qMakePair(0, steps[i])];
84  SearchSpaceType tmp; // need a temp copy because we need to sort it before we merge it
85  copySearchPoints(&tmp, sspace, steps[i], end);
86  tmp.pointerSort();
87  typename Vector<SearchPointType>::iterator final = std::set_union(presorted->begin(), presorted->end(),
88  tmp.begin(), tmp.end(),
89  result->begin(), pointerOrderCompare<SearchPointType>);
90  Q_ASSERT(final == result->end());
91  Q_UNUSED(final); // needed in case we don't compile the assert
92  }
93  }
94  else if (end == sspace->size()) {
95  for (i=steps.size()-1; i>=0; i--) {
96  if (start > steps[i]) break;
97  }
98  i++;
99 
100  if (i >= (steps.size()-1)) { // no need to merge
101  copySearchPoints(result, sspace, start, end);
102  result->pointerSort();
103  }
104  else { // merge with presorted chunk
105  SearchSpaceType* presorted = sorted[qMakePair(steps[i], end)];
106  SearchSpaceType tmp;
107  copySearchPoints(&tmp, sspace, start, steps[i]);
108  tmp.pointerSort();
109  typename Vector<SearchPointType>::iterator final = std::set_union(presorted->begin(), presorted->end(),
110  tmp.begin(), tmp.end(),
111  result->begin(), pointerOrderCompare<SearchPointType>);
112  Q_ASSERT(final == result->end());
113  Q_UNUSED(final); // needed in case we don't compile the assert
114  }
115  }
116  else {
117  // Here we could both do the contained presorted interval, then merge the remaining ends,
118  // or the containing presorted interval, then remove the points in excess. We choose the
119  // latter, because then we can do the set_difference in place.
120  // So:
121  // - get the presorted chunk from L(start) to U(end)
122  // - remove points in excess (with set_difference) from start to U(Start) and from L(end) to end
123  int is, ie;
124  for (is=steps.size()-1; is>=0; is--) if (steps[is] <= start) break; is = qMax(is, 0);
125  for (ie=0; ie<steps.size(); ie++) if (steps[ie] >= end) break; ie = qMin(ie, steps.size()-1);
126 
127  copySearchPoints(result, sorted[qMakePair(steps[is], steps[ie])]);
128  SearchSpaceType tmp;
129 
130  // remove left chunk
131  copySearchPoints(&tmp, sspace, steps[is], start);
132  tmp.pointerSort();
133  result->setDifference(&tmp);
134 
135  // remove right chunk
136  copySearchPoints(&tmp, sspace, end, steps[ie]);
137  tmp.pointerSort();
138  result->setDifference(&tmp);
139  }
140 
141  return result;
142 }
143 
144 
145 
146 template <typename SearchPointType, typename DataSetType>
147 BaseSearchSpacePool<SearchPointType, DataSetType>::BaseSearchSpacePool(const DataSetType* dataset) : _dataset(dataset), _originalSpace(0) {
148  recreate();
149 }
150 
151 template <typename SearchPointType, typename DataSetType>
152 BaseSearchSpacePool<SearchPointType, DataSetType>::~BaseSearchSpacePool() {
153  clearIndices();
154 }
155 
156 
157 // helper function
158 /*
159 template <typename SearchPointType, typename DataSetType>
160 SearchSpaceType* createSearchSpaceFromDataSet(const DataSetType* dataset);
161 
162 template<>
163 SearchSpace* createSearchSpaceFromDataSet(const DataSet* dataset) {
164  SearchSpace* result = new SearchSpace();
165  const DataSet* refDataSet = dataset->referenceDataSet();
166 
167  const int size = dataset->size();
168  result->resize(size);
169  for (int i=0; i<size; i++) {
170  (*result)[i].ptr = dataset->at(i);
171  (*result)[i].ref = refDataSet->at(i);
172  }
173 
174  result->filterAll();
175 
176  return result;
177 }
178 
179 template<>
180 FrozenSearchSpace* createSearchSpaceFromDataSet(const FrozenDataSet* dataset) {
181  FrozenSearchSpace* result = new FrozenSearchSpace();
182 }
183 */
184 
185 template <typename SearchPointType, typename DataSetType>
187  // save the names of the descriptors which have been indexed to be able to reindex them
188  QStringList valueIndices = _valueIndex.keys();
189  QStringList labelIndices = _labelIndex.keys();
190  clearIndices();
191 
192  delete _originalSpace;
193  _originalSpace = SearchSpaceType::createFromDataSet(_dataset);
194 
195  // TODO: need to recreate previously existing indices (?)
196  foreach (const QString& name, valueIndices) indexOn(name);
197  foreach (const QString& name, labelIndices) indexOn(name);
198 }
199 
200 
201 template <typename SearchPointType, typename DataSetType>
202 void BaseSearchSpacePool<SearchPointType, DataSetType>::clearIndices() {
203  delete _originalSpace;
204  _originalSpace = 0;
205 
206  foreach (const QString& desc, _valueIndex.keys()) _valueIndex.value(desc).clear();
207  _valueIndex.clear();
208 
209  foreach (const QString& desc, _labelIndex.keys()) _labelIndex.value(desc).clear();
210  _labelIndex.clear();
211 }
212 
213 
214 template <typename SearchPointType, typename DataSetType>
215 BaseSearchSpace<SearchPointType, DataSetType>* BaseSearchSpacePool<SearchPointType, DataSetType>::getAllPoints() const {
216  SearchSpaceType* result = BaseSearchSpacePool<SearchPointType, DataSetType>::acquire();
217  copySearchPoints(result, _originalSpace);
218  result->setFilter(Filter::parse("where true"), true);
219  return result;
220 }
221 
222 
223 template <typename SearchPointType, typename DataSetType>
224 void BaseSearchSpacePool<SearchPointType, DataSetType>::indexOn(const QString& descriptorName) {
225  PointLayout layout = _dataset->referenceDataSet()->layout();
226  QString fullName = layout.fullName(descriptorName);
227  DescriptorType type = layout.descriptorLocation(fullName).type();
228 
229  switch (type) {
230 
231  case RealType:
232  return indexOnValue(fullName);
233 
234  case StringType:
235  return indexOnLabel(fullName);
236 
237  case EnumType:
238  return indexOnEnum(fullName);
239 
240  default:
241  throw GaiaException("SearchSpacePool::indexOn: internal error, invalid indexing type");
242  }
243 }
244 
245 
246 template <typename SearchPointType>
247 const Point* refPoint(const SearchPointType& p, const DataSet* refDataSet);
248 
249 template <>
250 inline const Point* refPoint(const SearchPoint& p, const DataSet* refDataSet) {
251  Q_UNUSED(refDataSet);
252  return p.ref;
253 }
254 
255 template <>
256 inline const Point* refPoint(const FrozenSearchPoint& p, const DataSet* refDataSet) {
257  return refDataSet->at(p.idx);
258 }
259 
260 template <typename SearchPointType, typename DataSetType>
261 const Point* refPoint(const SearchPointType& p, const BaseSearchSpace<SearchPointType, DataSetType>& sspace);
262 
263 template <>
264 inline const Point* refPoint(const SearchPoint& p, const SearchSpace& sspace) {
265  Q_UNUSED(sspace);
266  return p.ref;
267 }
268 
269 template <>
270 inline const Point* refPoint(const FrozenSearchPoint& p, const FrozenSearchSpace& sspace) {
271  return sspace.dataSet()->referenceDataSet()->at(p.idx);
272 }
273 
274 template <typename DataSetType, DescriptorType DescType>
275 class SortOn {
276  public:
277  SortOn(const DataSetType* dataset, const QString& descriptorName) {
278  _refDataSet = dataset->referenceDataSet();
279  // we assume that the descriptor is a fixed-length one
280  Region region = _refDataSet->layout().descriptorLocation(descriptorName);
281  if (region.lengthType() != FixedLength) {
282  throw GaiaException("SearchSpacePool: you can only index on fixed-length descriptors");
283  }
284 
285  if (region.size(DescType, FixedLength) != 1) {
286  throw GaiaException("SearchSpacePool: you can only index on descriptors of size 1");
287  }
288 
289  _index = region.index();
290  }
291 
292  template <typename SearchPointType>
293  inline bool operator()(const SearchPointType& p1, const SearchPointType& p2);
294 
295  protected:
296  template <typename SearchPointType>
297  inline const Point* ref(const SearchPointType& p) { return refPoint(p, _refDataSet); }
298  const DataSet* _refDataSet;
299  int _index;
300 };
301 
302 
303 template <> template <>
304 inline bool SortOn<DataSet, RealType>::operator()(const SearchPoint& p1, const SearchPoint& p2) {
305  return ref(p1)->frealData()[_index] < ref(p2)->frealData()[_index];
306 }
307 
308 template <> template <>
309 inline bool SortOn<DataSet, StringType>::operator()(const SearchPoint& p1, const SearchPoint& p2) {
310  return ref(p1)->fstringData()[_index] < ref(p2)->fstringData()[_index];
311 }
312 
313 template <> template <>
314 inline bool SortOn<DataSet, EnumType>::operator()(const SearchPoint& p1, const SearchPoint& p2) {
315  // we don't need to sort alphanumerically as we just want to have points with the same
316  // value grouped together (because in the filters we only allow == and !=)
317  return ref(p1)->fenumData()[_index] < ref(p2)->fenumData()[_index];
318 }
319 
320 template <> template <>
321 inline bool SortOn<FrozenDataSet, RealType>::operator()(const FrozenSearchPoint& p1, const FrozenSearchPoint& p2) {
322  return ref(p1)->frealData()[_index] < ref(p2)->frealData()[_index];
323 }
324 
325 template <> template <>
326 inline bool SortOn<FrozenDataSet, StringType>::operator()(const FrozenSearchPoint& p1, const FrozenSearchPoint& p2) {
327  return ref(p1)->fstringData()[_index] < ref(p2)->fstringData()[_index];
328 }
329 
330 template <> template <>
331 inline bool SortOn<FrozenDataSet, EnumType>::operator()(const FrozenSearchPoint& p1, const FrozenSearchPoint& p2) {
332  // we don't need to sort alphanumerically as we just want to have points with the same
333  // value grouped together (because in the filters we only allow == and !=)
334  return ref(p1)->fenumData()[_index] < ref(p2)->fenumData()[_index];
335 }
336 
337 
338 template <typename SearchPointType, typename DataSetType>
339 void BaseSearchSpacePool<SearchPointType, DataSetType>::indexOnValue(const QString& descriptorName) {
340  SearchSpaceType* index = getAllPoints();
341  gaia2::sort(range(*index), SortOn<DataSetType, RealType>(_dataset, descriptorName));
342 
343  ValueIndex completeIndex;
344  completeIndex.sspace = index;
345 
346  // make a cumulatively-sorted array of subspaces so we don't have to re-sort by pointer
347  // each time we return a subspace
348  static const int factor = 10;
349  const int chunkSize = (index->size() / factor) + 1;
350  int split = 0;
351 
352  // presort full search space
353  SearchSpaceType* full = new SearchSpaceType();
354  copySearchPoints(full, index);
355  full->pointerSort();
356  completeIndex.sorted.insert(qMakePair(0, full->size()), full);
357 
358  // presort rays (ie: either start is 0, or end is 0)
359  for (int i=0; i<factor-1; i++) {
360  split += chunkSize;
361 
362  SearchSpaceType* half = new SearchSpaceType();
363  copySearchPoints(half, index, 0, split);
364  half->pointerSort();
365  completeIndex.sorted.insert(qMakePair(0, split), half);
366 
367  half = new SearchSpaceType();
368  copySearchPoints(half, index, split, index->size());
369  half->pointerSort();
370  completeIndex.sorted.insert(qMakePair(split, (int)index->size()), half);
371  }
372 
373  // also presort segments (ie: neither start nor end is 0)
374  // memory usage should be about 3 times than without it (for factor=10)
375  // Note: we could make this entire function fit in this nested loop, but
376  // having it separate makes it easier to not compute the segments if
377  // we realize later that they suck up too much memory
378  for (int end=2*chunkSize; end < (int)index->size(); end += chunkSize) {
379  for (int start=chunkSize; start < end; start += chunkSize) {
380  SearchSpaceType* segment = new SearchSpaceType();
381  copySearchPoints(segment, index, start, end);
382  segment->pointerSort();
383  completeIndex.sorted.insert(qMakePair(start, end), segment);
384  }
385  }
386 
387  _valueIndex.insert(descriptorName, completeIndex);
388 }
389 
390 template <typename SearchPointType, typename DataSetType>
391 void BaseSearchSpacePool<SearchPointType, DataSetType>::indexOnLabel(const QString& descriptorName) {
392  SearchSpaceType* index = getAllPoints();
393  gaia2::sort(range(*index), SortOn<DataSetType, StringType>(_dataset, descriptorName));
394 
395  LabelIndex completeIndex;
396  completeIndex.sspace = index;
397 
398  // sort again each subspace, but by pointer order this time, and also fill the
399  // hashmap with the ranges
400  const DataSet* rds = _dataset->referenceDataSet();
401  int idx = rds->layout().descriptorLocation(descriptorName).index();
402  typename Vector<SearchPointType>::iterator start = index->begin(), end = index->begin();
403  while (start != index->end()) {
404  const QString& value = refPoint(*start, rds)->fstringData()[idx];
405  while ((end != index->end()) && (refPoint(*end, rds)->fstringData()[idx] == value)) {
406  end++;
407  }
408  gaia2::sort(start, end, pointerOrderCompare<SearchPointType>);
409  completeIndex.ranges.insert(value, qMakePair((int)(start - index->begin()), (int)(end - index->begin())));
410  start = end;
411  }
412 
413  _labelIndex.insert(descriptorName, completeIndex);
414 }
415 
416 template <typename SearchPointType, typename DataSetType>
417 void BaseSearchSpacePool<SearchPointType, DataSetType>::indexOnEnum(const QString& descriptorName) {
418  SearchSpaceType* index = getAllPoints();
419  gaia2::sort(range(*index), SortOn<DataSetType, EnumType>(_dataset, descriptorName));
420 
421  LabelIndex completeIndex;
422  completeIndex.sspace = index;
423 
424  // sort again each subspace, but by pointer order this time, and also fill the
425  // hashmap with the ranges
426  const DataSet* rds = _dataset->referenceDataSet();
427  int idx = rds->layout().descriptorLocation(descriptorName).index();
428  typename Vector<SearchPointType>::iterator start = index->begin(), end = index->begin();
429  while (start != index->end()) {
430  Enum value = refPoint(*start, rds)->fenumData()[idx];
431  while ((end != index->end()) && (refPoint(*end, rds)->fenumData()[idx] == value)) {
432  end++;
433  }
434  gaia2::sort(start, end, pointerOrderCompare<SearchPointType>);
435  QString svalue = refPoint(*start, rds)->layout().enumToString(descriptorName, value);
436  completeIndex.ranges.insert(svalue, qMakePair((int)(start - index->begin()), (int)(end - index->begin())));
437  start = end;
438  }
439 
440  _labelIndex.insert(descriptorName, completeIndex);
441 }
442 
443 // if value is not in array, returns the index of the first value which is > to given value
444 template <typename SearchSpaceType>
445 inline int binarySearch(const SearchSpaceType& sspace, int idx, Real value, int start, int end) {
446  while (start < end) {
447  int middle = (start + end) / 2;
448  if (refPoint(sspace[middle], sspace)->frealData()[idx] < value) {
449  start = middle + 1;
450  }
451  else {
452  end = middle;
453  }
454  }
455  return start;
456 }
457 
458 template <typename SearchPointType, typename DataSetType>
459 bool BaseSearchSpacePool<SearchPointType, DataSetType>::hasIndex(const QString& descriptorName) const {
460  return _valueIndex.contains(descriptorName) || _labelIndex.contains(descriptorName);
461 }
462 
463 
464 template <typename SearchPointType, typename DataSetType>
465 int BaseSearchSpacePool<SearchPointType, DataSetType>::getCuttingPos(const SearchSpaceType& sspace, int idx, int type,
466  Real value, int start, int end) const {
467 
468  // estimated cutting point, will need to be adjusted
469  int cuttingPos = binarySearch(sspace, idx, value, start, end);
470 
471  switch (type) {
472  case LT:
473  while (cuttingPos > start && refPoint(sspace[cuttingPos-1], sspace)->frealData()[idx] >= value) cuttingPos--;
474  break;
475 
476  case LTE:
477  while (cuttingPos < end && refPoint(sspace[cuttingPos], sspace)->frealData()[idx] <= value) cuttingPos++;
478  while (cuttingPos > start && refPoint(sspace[cuttingPos-1], sspace)->frealData()[idx] > value) cuttingPos--;
479  //cuttingPos++;
480  break;
481 
482  case GT:
483  while (cuttingPos < end && refPoint(sspace[cuttingPos], sspace)->frealData()[idx] <= value) cuttingPos++;
484  break;
485 
486  case GTE:
487  while (cuttingPos > start && refPoint(sspace[cuttingPos-1], sspace)->frealData()[idx] >= value) cuttingPos--;
488  //cuttingPos++;
489  while (cuttingPos < end && refPoint(sspace[cuttingPos], sspace)->frealData()[idx] < value) cuttingPos++;
490  break;
491 
492 
493  case EQ:
494  //break;
495  case NEQ:
496  throw GaiaException("SearchSpacePool::getCuttingPos undefined for '==' and '!='");
497  }
498 
499  return cuttingPos;
500 }
501 
502 template <typename SearchPointType, typename DataSetType>
503 BaseSearchSpace<SearchPointType, DataSetType>* BaseSearchSpacePool<SearchPointType, DataSetType>::getSubSpace(const parser::Predicate* pred) const {
504  // in case descriptor is not indexed, we return a null pointer
505 
506  const parser::PredValueComparison* comp = dynamic_cast<const parser::PredValueComparison*>(pred);
507  if (comp) {
508  // check for indexable comparisons: (var op constant) or (constant op var)
509  // ie: (var op var) and (constant op constant) are forbidden here
510  if (! ((dynamic_cast<const parser::ValueVariable*>(comp->_lhs) && dynamic_cast<const parser::ValueConstant*>(comp->_rhs)) ||
511  (dynamic_cast<const parser::ValueConstant*>(comp->_lhs) && dynamic_cast<const parser::ValueVariable*>(comp->_rhs)))) {
512  return 0;
513  }
514 
515  const parser::ValueVariable* var = 0;
516  const parser::ValueConstant* value = 0;
517  if (dynamic_cast<const parser::ValueVariable*>(comp->_lhs)) {
518  var = dynamic_cast<const parser::ValueVariable*>(comp->_lhs);
519  value = dynamic_cast<const parser::ValueConstant*>(comp->_rhs);
520  }
521  else {
522  var = dynamic_cast<const parser::ValueVariable*>(comp->_rhs);
523  value = dynamic_cast<const parser::ValueConstant*>(comp->_lhs);
524  }
525 
526  Q_ASSERT(var && value); // unnecessary, but just to make sure
527 
528  // look whether we have an index on that descriptor, and if yes:
529  // - get the exact cutting point, as well as whether we should take the lower
530  // part or the upper one.
531  // - returns this subspace
532  QString fullName = _dataset->referenceDataSet()->layout().fullName(var->name());
533  if (!hasIndex(fullName)) return 0;
534 
535  SearchSpaceType& fullSpace = *_valueIndex[fullName].sspace;
536  int idx = _dataset->referenceDataSet()->layout().descriptorLocation(fullName).index();
537  int start = 0, end = fullSpace.size(); // range to be extracted from the full searchspace
538 
539  switch (comp->_type) {
540  case LT:
541  case LTE:
542  end = getCuttingPos(fullSpace, idx, comp->_type, (Real)value->value(), start, end);
543  break;
544 
545  case GT:
546  case GTE:
547  start = getCuttingPos(fullSpace, idx, comp->_type, (Real)value->value(), start, end);
548  break;
549 
550  case EQ:
551  case NEQ:
552  throw GaiaException("SearchSpacePool: cannot optimize with a != operator atm. Please fill a feature request.");
553  }
554 
555  Filter* alwaysTrue = Filter::parse("where true");
556  SearchSpaceType* result = BaseSearchSpacePool<SearchPointType, DataSetType>::acquire();
557  result->setFilter(alwaysTrue, true);
558 
559  _valueIndex[fullName].getSubSpaceInto(result, start, end);
560 
561  return result;
562  }
563 
564  const parser::PredValueRange* rangePred = dynamic_cast<const parser::PredValueRange*>(pred);
565  if (rangePred) {
566  if (!dynamic_cast<parser::ValueVariable*>(rangePred->_var)) return 0;
567 
568  QString fullName = _dataset->referenceDataSet()->layout().fullName(rangePred->_var->toString());
569  if (!hasIndex(fullName)) return 0;
570 
571  SearchSpaceType& fullSpace = *_valueIndex[fullName].sspace;
572  int idx = _dataset->referenceDataSet()->layout().descriptorLocation(fullName).index();
573  int start = 0, end = fullSpace.size(); // range to be extracted from the full searchspace
574 
575  // find both cutting points, inclusive
576  start = getCuttingPos(fullSpace, idx, GTE, rangePred->_min, start, end);
577  end = getCuttingPos(fullSpace, idx, LTE, rangePred->_max, start, end);
578 
579  Filter* alwaysTrue = Filter::parse("where true");
580  SearchSpaceType* result = BaseSearchSpacePool<SearchPointType, DataSetType>::acquire();
581  result->setFilter(alwaysTrue, true);
582 
583  _valueIndex[fullName].getSubSpaceInto(result, start, end);
584 
585  return result;
586  }
587 
588  const parser::PredLabelComparison* labelPred = dynamic_cast<const parser::PredLabelComparison*>(pred);
589  if (labelPred) {
590  // check for indexable comparisons: (var op constant) or (constant op var)
591  // ie: (var op var) and (constant op constant) are forbidden here
592  if (! ((dynamic_cast<const parser::LabelVariable*>(labelPred->_lhs) && dynamic_cast<const parser::LabelConstant*>(labelPred->_rhs)) ||
593  (dynamic_cast<const parser::LabelConstant*>(labelPred->_lhs) && dynamic_cast<const parser::LabelVariable*>(labelPred->_rhs)))) {
594  return 0;
595  }
596 
597  const parser::LabelVariable* var = 0;
598  const parser::LabelConstant* value = 0;
599  if (dynamic_cast<const parser::LabelVariable*>(labelPred->_lhs)) {
600  var = dynamic_cast<const parser::LabelVariable*>(labelPred->_lhs);
601  value = dynamic_cast<const parser::LabelConstant*>(labelPred->_rhs);
602  }
603  else {
604  var = dynamic_cast<const parser::LabelVariable*>(labelPred->_rhs);
605  value = dynamic_cast<const parser::LabelConstant*>(labelPred->_lhs);
606  }
607 
608  Q_ASSERT(var && value); // unnecessary, but just to make sure
609 
610  // look whether we have an index on that descriptor, and if yes:
611  // - get either the corresponding subspace (type == EQ) or all of them except
612  // this one (type == NEQ)
613  // - returns this subspace
614  QString fullName = _dataset->referenceDataSet()->layout().fullName(var->name());
615  if (!hasIndex(fullName)) return 0;
616 
617  const LabelIndex& index = _labelIndex.value(fullName);
618  Filter* alwaysTrue = Filter::parse("where true");
619  SearchSpaceType* result = BaseSearchSpacePool<SearchPointType, DataSetType>::acquire();
620  result->setFilter(alwaysTrue, true);
621 
622  switch (labelPred->_type) {
623  case EQ: {
624  if (!index.ranges.contains(value->value())) return result;
625  QPair<int, int> rng = index.ranges.value(value->value());
626  copySearchPoints(result, index.sspace, rng.first, rng.second);
627  return result;
628  }
629 
630  case NEQ:
631  // if the constant value is not in our list of possible values, it means all the points are valid
632  if (!index.ranges.contains(value->value())) {
633  BaseSearchSpacePool<SearchPointType, DataSetType>::release(result);
634  return getAllPoints();
635  }
636 
637  // otherwise, copy all the subspaces for which the value is different than the given one
638  result->reserve(index.sspace->size());
639  for (GaiaMap<QString, QPair<int, int> >::const_iterator it = index.ranges.constBegin();
640  it != index.ranges.constEnd(); ++it) {
641  if (value->value() != it.key()) {
642  const QPair<int, int>& rng = it.value();
643  int previous = result->size();
644  result->resize(previous + (rng.second-rng.first));
645  copySearchPoints(result, index.sspace, rng.first, rng.second, previous);
646  }
647  }
648  // we need to sort by pointer order here, as we might reuse this subspace for another filter
649  result->pointerSort();
650 
651  return result;
652  }
653 
654  Q_ASSERT(false);
655  }
656 
657  return 0;
658 }
659 
660 
661 
662 template <typename SearchPointType, typename DataSetType>
663 BaseSearchSpace<SearchPointType, DataSetType>* BaseSearchSpacePool<SearchPointType, DataSetType>::acquire() {
664  if (!_sspoolMutex) _sspoolMutex = new QMutex();
665  QMutexLocker lock(_sspoolMutex);
666 
667  if (_sspool.isEmpty()) {
668  G_DEBUG(GSearchSpace, "Creating new SearchSpace because pool is empty");
669  return new SearchSpaceType();
670  }
671 
672  G_DEBUG(GSearchSpace, "Pool has " << _sspool.size() << " ready SearchSpaces, getting one from there");
673  SearchSpaceType* result = _sspool.takeLast();
674  // need to revalidate here because we don't want to hand out a searchspace that the optimizer
675  // or the view thinks it is already filtered & sorted
676  result->invalidate();
677  return result;
678 }
679 
680 
681 template <typename SearchPointType, typename DataSetType>
682 void BaseSearchSpacePool<SearchPointType, DataSetType>::release(SearchSpaceType* sspace) {
683  if (!sspace) return;
684 
685  if (!_sspoolMutex) _sspoolMutex = new QMutex();
686  QMutexLocker lock(_sspoolMutex);
687 
688  // optimization: only keep it if it has a certain number of points preallocated
689  // using capacity() here doesn't cut it, because when we resize, it still needs to construct
690  // all the new elements, even if they were previously allocated, and there's no performance
691  // gain in doing that...
692  if (sspace->unfilteredSize() > 50000) {
693  sspace->cleanSearchSpace();
694  _sspool << sspace;
695  G_DEBUG(GSearchSpace, "Returning SearchSpace to pool, now has " << _sspool.size() << " available ones");
696  }
697  else {
698  G_DEBUG(GSearchSpace, "SearchSpace is too small to return to pool, deleting it (pool has " << _sspool.size() << " available ones)");
699  delete sspace;
700  return;
701  }
702 
703  // make sure our pool doesn't grow too big
704  static const int MAX_SIZE = 10;
705 
706  while (_sspool.size() > MAX_SIZE) {
707  delete _sspool.takeLast();
708  }
709 }
710 
711 template <typename SearchPointType, typename DataSetType>
713  {
714  QMutexLocker lock(_sspoolMutex);
715  while (!_sspool.isEmpty()) delete _sspool.takeLast();
716  }
717  delete _sspoolMutex;
718  _sspoolMutex = 0;
719 }
720 
721 
722 template <typename SearchPointType, typename DataSetType>
723 SearchSpaceWrapper<SearchPointType, DataSetType>::~SearchSpaceWrapper() {
724  BaseSearchSpacePool<SearchPointType, DataSetType>::release(sspace);
725 }
SearchSpaceType * getAllPoints() const
Returns a SearchSpace containing all points.
Definition: searchspacepool.h:216
void indexOn(const QString &descriptorName)
Indexes on the given descriptorName.
Definition: searchspacepool.h:225
static Filter * parse(const QString &str)
Parses a given string and returns the newly created Filter object.
Definition: filter.cpp:181
int binarySearch(const Container< T > &v, T value)
Iterative function for binary search (more efficient than recursive one).
Definition: gaiamath.h:270
bool hasIndex(const QString &descriptorName) const
Returns whether an index exists for the given descriptorName.
Definition: searchspacepool.h:460
const PointLayout & layout() const
Returns the current layout of the point.
Definition: point.h:195
static void clear()
Clear the cache of preallocated SearchSpaces.
Definition: searchspacepool.h:713
void indexOnValue(const QString &descriptorName)
assumes descriptorName is the fully qualified name
Definition: searchspacepool.h:340
Definition: searchspacepool_impl.h:275
StringDescriptor enumToString(const QString &name, const EnumDescriptor &value) const
Converts the given Enum descriptor into its String representation.
Definition: pointlayout.cpp:190
DescriptorType
The possible types of descriptors accepted.
Definition: region.h:36
void recreate()
Recreates the SearchSpacePool and all the indices (needed for instance when the dataset it is pointin...
Definition: searchspacepool.h:187
void indexOnLabel(const QString &descriptorName)
assumes descriptorName is the fully qualified name
Definition: searchspacepool.h:392
void indexOnEnum(const QString &descriptorName)
assumes descriptorName is the fully qualified name
Definition: searchspacepool.h:418