HDK
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
PDG_ValuePattern.h
Go to the documentation of this file.
1 /*
2  * PROPRIETARY INFORMATION. This software is proprietary to
3  * Side Effects Software Inc., and is not to be reproduced,
4  * transmitted, or disclosed in any way without written permission.
5  *
6  * COMMENTS:
7  */
8 
9 #ifndef __PDG_VALUE_PATTERN_H__
10 #define __PDG_VALUE_PATTERN_H__
11 
12 #include "PDG_API.h"
13 #include "PDG_BasePattern.h"
14 
15 #include <UT/UT_Array.h>
16 #include <UT/UT_Map.h>
17 #include <UT/UT_StringArray.h>
18 #include <UT/UT_StringHolder.h>
19 #include <UT/UT_ValArray.h>
20 #include <UT/UT_VectorTypes.h>
21 #include <UT/UT_WorkBuffer.h>
22 #include <SYS/SYS_String.h>
23 
24 class UT_StringView;
25 
26 /**
27  * Createss a queryable representation of a sequence of values, but does not
28  * generated the sequence itself unless requested. For example:
29  *
30  * `1-10 20 30 40`
31  * `1-10:2 !3`
32  */
34 {
35 public:
39 
40 public:
41  /// Creates an empty pattern instance that will have it's contents loaded
42  /// later.
43  PDG_ValuePattern(char delimiter=' ');
44 
45  /// Parses a pattern into a PDG_ValuePattern instance
47  const UT_StringHolder& pattern,
48  char delimiter=' ');
49 
50  /// Checks if the specified string value is in the pattern
52  bool inclusive) const
53  {
54  if (match(value, inclusive, nullptr))
55  return true;
56 
57  int len = value.length();
58  const char* data = value.c_str();
59  while (len--)
60  {
61  if (!SYSisdigit(data[len]))
62  break;
63  }
64 
65  int suffix = atoi(data+len+1);
66  UT_StringView prefix(data, len+1);
67  return match(
68  suffix, prefix, inclusive, nullptr);
69  }
70 
71  /// Checks if the specified float value is in the pattern
72  bool contains(fpreal value, bool inclusive) const
73  { return match(value, inclusive, nullptr); }
74 
75  /// Checks if the specified integer value is in the pattern
76  bool contains(exint value, bool inclusive) const
77  { return match(value, inclusive, nullptr); }
78 
79 
80  /// Checks if the specified float value matches the pattern and returns
81  /// the range of values from the component that matches
83  fpreal value,
84  bool inclusive) const
85  { return matchRange(range, value, inclusive); }
86 
87  /// Checks if the specified integer value matches the pattern and returns
88  /// the range of values from the component that matches
90  exint value,
91  bool inclusive) const
92  { return matchRange(range, value, inclusive); }
93 
94 
95  /// Returns the array of string values that match the pattern
97  bool sorted,
98  bool inclusive) const
99  { return append(values, sorted, inclusive); }
100 
101  /// Returns the array of float values that match the pattern
103  bool sorted,
104  bool inclusive) const
105  { return append(values, sorted, inclusive); }
106 
107  /// Returns the array of integer values that match the pattern
109  bool sorted,
110  bool inclusive) const
111  { return append(values, sorted, inclusive); }
112 
113 
114  /// Returns a map of component number -> string value array, after
115  /// considering exclusions
117  bool sorted,
118  bool inclusive) const
119  {
120  return appendMap(
121  values, sorted, inclusive);
122  }
123 
124  /// Returns a map of component number -> float value array, after
125  /// considering exclusions
127  bool sorted,
128  bool inclusive) const
129  {
130  return appendMap(
131  values, sorted, inclusive);
132  }
133 
134  /// Returns a map of component number -> int value array, after
135  /// considering exclusions
137  bool sorted,
138  bool inclusive) const
139  {
140  return appendMap(
141  values, sorted, inclusive);
142  }
143 
144 
145  /// Returns the value range for the specified component number
147  exint index,
148  bool inclusive) const
149  {
150  if (index < 0 || index >= myMatches.size())
151  return false;
152  if (myMatches[index].myIsExclusion)
153  return false;
154 
155  myMatches[index].setRange(
156  range, inclusive);
157 
158  return true;
159  }
160 
161  /// Returns the step value for the specified component number
163  {
164  if (index < 0 || index >= myMatches.size())
165  return false;
166 
167  return myMatches[index].floatValue(2, 1);
168  }
169 
170  /// Returns true if the pattern is strictly numeric (has no string
171  /// values in it), otherwise returns false
172  bool isNumeric() const
173  { return myIsNumeric; }
174 
175  /// Returns true if the pattern has segments with different step sizes
176  bool hasVariableStep() const
177  { return myHasVariableStep; }
178 
179  /// Returns the minimum step size from all components in the pattern
181  { return myMinimumStep; }
182 
183  /// Parses the input string into this pattern -- only valid if no pattern
184  /// has been parsed previously
185  bool parse(const UT_StringView& pattern);
186 
187 private:
188  /// Possible types for a value match
189  enum MatchType : uint8
190  {
191  /// A single constant value
192  eMatchTypeConstant,
193 
194  /// A simple range with a start and end point, and a step size of 1
195  eMatchTypeRange,
196 
197  /// A range with a custom step size
198  eMatchTypeRangeStep,
199  };
200 
201  /// Utility to do an inclusive vs. exclusive range end comparison
202  template <typename Value, typename Step, typename End>
203  static inline bool inclusiveComp(bool i,
204  const Value& v,
205  const Step& step,
206  const End& end)
207  {
208  if (step > 0)
209  return i ? (v <= end) : (v < end);
210  else
211  return i ? (v >= end) : (v > end);
212  }
213 
214  /// Value containing a float, string and integer part
215  struct Value
216  {
217  /// Appends a constant string, integer or float value from this
218  /// match value into the input array
219  void append(UT_StringArray& array) const
220  { array.append(myString); }
221  void append(UT_ExintArray& array) const
222  { if (myHasInteger) array.append(myInteger); }
223  void append(UT_FprealArray& array) const
224  { if (myHasFloat) array.append(myFloat); }
225 
226 
227  /// Appends values between start and end to the input array
228  static void append(UT_StringArray& array,
229  const Value& start,
230  const Value& end,
231  bool inclusive)
232  {
233  if (!start.myHasInteger || !end.myHasInteger)
234  return;
235 
236  for (exint i = start.myInteger;
237  inclusiveComp(
238  inclusive, i, 1, end.myInteger);
239  i++)
240  {
242  buffer.format("{}{}", start.myPrefix, i);
243  array.append(buffer.buffer());
244  }
245  }
246  static void append(UT_FprealArray& array,
247  const Value& start,
248  const Value& end,
249  bool inclusive)
250  {
251  if (!start.myHasFloat || !end.myHasFloat)
252  return;
253  for (fpreal i = start.myFloat;
254  inclusiveComp(
255  inclusive, i, 1.0, end.myFloat);
256  i += 1.0)
257  {
258  array.append(i);
259  }
260  }
261  static void append(UT_ExintArray& array,
262  const Value& start,
263  const Value& end,
264  bool inclusive)
265  {
266  if (!start.myHasInteger || !end.myHasInteger)
267  return;
268  for (exint i = start.myInteger;
269  inclusiveComp(
270  inclusive, i, 1, end.myInteger);
271  i++)
272  {
273  array.append(i);
274  }
275  }
276 
277 
278  /// Appends values between start and end, with the specified step
279  /// size, to the input array
280  static void append(UT_StringArray& array,
281  const Value& start,
282  const Value& end,
283  const Value& step,
284  bool inclusive)
285  {
286  if (!start.myHasInteger ||
287  !end.myHasInteger ||
288  !step.myHasInteger)
289  return;
290  if (step.myInteger == 0)
291  return;
292 
293  for (exint i = start.myInteger;
294  inclusiveComp(
295  inclusive, i,
296  step.myInteger, end.myInteger);
297  i += step.myInteger)
298  {
300  buffer.format("{}{}", start.myPrefix, i);
301  array.append(buffer.buffer());
302  }
303  }
304  static void append(UT_FprealArray& array,
305  const Value& start,
306  const Value& end,
307  const Value& step,
308  bool inclusive)
309  {
310  if (!start.myHasFloat ||
311  !end.myHasFloat ||
312  !step.myHasFloat)
313  return;
314  if (SYSequalZero(step.myFloat))
315  return;
316 
317  for (fpreal i = start.myFloat;
318  inclusiveComp(
319  inclusive, i,
320  step.myFloat, end.myFloat);
321  i += step.myFloat)
322  {
323  array.append(i);
324  }
325  }
326  static void append(UT_ExintArray& array,
327  const Value& start,
328  const Value& end,
329  const Value& step,
330  bool inclusive)
331  {
332  if (!start.myHasInteger ||
333  !end.myHasInteger ||
334  !step.myHasFloat)
335  return;
336  if (SYSequalZero(step.myFloat))
337  return;
338 
339  for (fpreal i = start.myInteger;
340  inclusiveComp(
341  inclusive, i,
342  step.myFloat, end.myInteger);
343  i += step.myFloat)
344  {
345  array.append(exint(i));
346  }
347  }
348 
349 
350  /// Performs an exact match comparison between an input value and the
351  /// constant value in this match value instance
352  bool match(const UT_StringHolder& value) const
353  { return myString == value; }
354  bool match(fpreal value) const
355  { return myHasFloat && SYSalmostEqual(value, myFloat); }
356  bool match(exint value) const
357  { return myHasInteger && (myInteger == value); }
358 
359 
360  /// Checks if the input value is less than the match value in this
361  /// instance
362  bool less(const UT_StringHolder& value, bool inclusive) const
363  { return true; }
364  bool less(exint value, bool inclusive) const
365  {
366  if (!myHasInteger) return true;
367  else if (inclusive) return value < myInteger;
368  else return value <= myInteger;
369  }
370  bool less(fpreal value, bool inclusive) const
371  {
372  if (!myHasFloat) return true;
373  else if (inclusive) return value < myFloat;
374  else return value <= myFloat;
375  }
376 
377  /// Checks if the input value is greater than the match value in this
378  /// instance
379  bool greater(const UT_StringHolder& value, bool inclusive) const
380  { return true; }
381  bool greater(exint value, bool inclusive) const
382  {
383  if (!myHasInteger) return true;
384  else if (inclusive) return value > myInteger;
385  else return value >= myInteger;
386  }
387  bool greater(fpreal value, bool inclusive) const
388  {
389  if (!myHasFloat) return true;
390  else if (inclusive) return value > myFloat;
391  else return value >= myFloat;
392  }
393 
394 
395  /// Checks if the input value is in the same value range as this match
396  /// value, module the step
397  bool step(const Value& start, const UT_StringHolder& value) const
398  { return false; }
399  bool step(const Value& start, exint value) const
400  {
401  if (!start.myHasInteger || !myHasFloat)
402  return false;
403 
404  fpreal delta = value - start.myInteger;
405  return SYSalmostEqual(SYSfmod(delta, myFloat), 0);
406  }
407  bool step(const Value& start, fpreal value) const
408  {
409  if (!start.myHasFloat || !myHasFloat)
410  return false;
411 
412  fpreal delta = value - start.myFloat;
413  return SYSalmostEqual(SYSfmod(delta, myFloat), 0);
414  }
415 
416  UT_StringHolder myPrefix;
417  UT_StringHolder myString;
418  fpreal myFloat;
419  exint myInteger;
420 
421  bool myHasFloat : 1;
422  bool myHasInteger : 1;
423  bool myHasPrefix : 1;
424  };
425 
426  /// Value match entry
427  struct ValueMatch
428  {
429  /// Does a full match, including parsing the prefix if the value is a
430  /// string
431  bool matchFull(const UT_StringHolder& value, bool inclusive) const
432  {
433  if (match(value, nullptr, inclusive))
434  return true;
435 
436  int len = value.length();
437  const char* data = value.c_str();
438  while (len--)
439  {
440  if (!SYSisdigit(data[len]))
441  break;
442  }
443 
444  exint suffix = atoi(data+len+1);
445 
446  if (myHasPrefix)
447  {
448  if (len < 0)
449  return false;
450 
451  UT_StringView prefix(data, len+1);
452  return match(suffix, &prefix, inclusive);
453  }
454  else
455  {
456  if (len > 0)
457  return false;
458  return match(suffix, nullptr, inclusive);
459  }
460  }
461  bool matchFull(const exint& value, bool inclusive) const
462  { return match(value, nullptr, inclusive); }
463  bool matchFull(const fpreal& value, bool inclusive) const
464  { return match(value, nullptr, inclusive); }
465 
466  /// Matches the pattern with the specified value
467  template <typename T>
468  bool match(const T& value,
469  const UT_StringView* prefix,
470  bool inclusive) const
471  {
472  // If the pattern does not have a prefix and we
473  // do, return early
474  if (!myMatchValues[0].myHasPrefix && prefix)
475  return false;
476 
477  // Constants only check the first value
478  if (myMatchType == eMatchTypeConstant)
479  {
480  if (!myMatchValues[0].match(value))
481  return false;
482  }
483  // Simple range check
484  else
485  {
486  if (myMatchValues[0].less(value, true))
487  return false;
488 
489  if (myMatchValues[1].greater(value, inclusive))
490  return false;
491  }
492 
493  // If the pattern has a prefix then we require
494  // a matching one
495  if (myMatchValues[0].myHasPrefix)
496  {
497  if (!prefix)
498  return false;
499  else if (*prefix != myMatchValues[0].myPrefix.c_str())
500  return false;
501  }
502 
503  // At this point, constants and ranges have matched
504  if (myMatchType <= eMatchTypeRange)
505  return true;
506  return myMatchValues[2].step(myMatchValues[0], value);
507  }
508 
509  /// Appends the values from the match into the specified array
510  template <typename T>
511  void append(T& array, bool inclusive) const
512  {
513  if (myMatchType == eMatchTypeConstant)
514  myMatchValues[0].append(array);
515  else if (myMatchType == eMatchTypeRange)
516  {
517  Value::append(
518  array,
519  myMatchValues[0],
520  myMatchValues[1],
521  inclusive);
522  }
523  else
524  {
525  Value::append(
526  array,
527  myMatchValues[0],
528  myMatchValues[1],
529  myMatchValues[2],
530  inclusive);
531  }
532  }
533 
534  /// Returns the nth value from the match as a float
535  fpreal floatValue(int index, fpreal default_value) const
536  {
537  if (index > myMatchType)
538  return default_value;
539 
540  const Value& value = myMatchValues[index];
541  if (value.myHasFloat)
542  return value.myFloat;
543  if (value.myHasInteger)
544  return (fpreal)(value.myInteger);
545 
546  return 0;
547  }
548 
549  /// Returns the range of values in the match segment as a float
550  /// array
551  void setRange(UT_FprealArray& range, bool inclusive) const
552  {
553  fpreal start = floatValue(0, 0);
554  fpreal end = floatValue(1, start);
555  fpreal step = floatValue(2, 1);
556 
557  fpreal last = SYSfloor((end-start)/step);
558  last = last*step + start;
559  if (!inclusive && SYSalmostEqual(end, last))
560  last -= step;
561 
562  range.clear();
563  range.append(start);
564  range.append(last);
565  range.append(step);
566  }
567 
568  /// Values in the match entry
569  Value myMatchValues[3];
570 
571  /// The match type
572  MatchType myMatchType;
573 
574  /// Whether or not the value sequence is an exclusion
575  bool myIsExclusion;
576 
577  /// Whether or not the values in this match have a prefix
578  bool myHasPrefix;
579 
580  /// Whether or not the values in this match are all numeric
581  bool myIsNumeric;
582  };
583 
584  /// Helper struct for parsing string data
585  struct ParseState
586  {
587  ParseState()
588  { reset(); }
589 
590  void reset()
591  {
592  myValueIndex = 0;
593  myRangeIndex = 0;
594  myLastChar = 0;
595  myIsQuoted = false;
596  myIsSkip = false;
597  myIsExclusion = false;
598 
599  for (int i = 0; i < 3; i++)
600  myValues[i].clear();
601  }
602 
603  UT_WorkBuffer myValues[3];
604  exint myRangeValues[3];
605  int myValueIndex;
606  int myRangeIndex;
607  char myLastChar;
608 
609  bool myIsQuoted;
610  bool myIsSkip;
611  bool myIsExclusion;
612  };
613 
614 private:
615  template <typename T>
616  bool match(const T& value,
617  bool inclusive,
618  const ValueMatch** value_match) const
619  {
620  if (!myIsValid)
621  return false;
622 
623  bool found = false;
624  for (auto&& match : myMatches)
625  {
626  if (!match.match(value, nullptr, inclusive))
627  continue;
628 
629  if (!myHasExclusion)
630  {
631  if (value_match)
632  *value_match = &match;
633  return true;
634  }
635 
636  if (!match.myIsExclusion)
637  {
638  if (value_match)
639  *value_match = &match;
640  found = true;
641  }
642  else if (found)
643  return false;
644  }
645 
646  return found;
647  }
648 
649  bool match(exint value,
650  const UT_StringView& prefix,
651  bool inclusive,
652  const ValueMatch** value_match) const
653  {
654  if (!myIsValid)
655  return false;
656 
657  bool found = false;
658  for (auto&& match : myMatches)
659  {
660  if (!match.match(value, &prefix, inclusive))
661  continue;
662 
663  if (!myHasExclusion)
664  {
665  if (value_match)
666  *value_match = &match;
667  return true;
668  }
669 
670  if (!match.myIsExclusion)
671  {
672  if (value_match)
673  *value_match = &match;
674  found = true;
675  }
676  else if (found)
677  return false;
678  }
679 
680  return found;
681  }
682 
683  template <typename T>
684  bool matchRange(UT_FprealArray& range,
685  const T& value,
686  bool inclusive) const
687  {
688  const ValueMatch* value_match = nullptr;
689  if (!match(value, inclusive, &value_match))
690  return false;
691  if (!value)
692  return false;
693 
694  value_match->setRange(range, inclusive);
695  return true;
696  }
697 
698 
699  template <typename T>
700  void append(T& array,
701  bool sorted,
702  bool inclusive) const
703  {
704  if (!myIsValid)
705  return;
706 
707  T swap_array;
708  for (auto&& match : myMatches)
709  {
710  if (!match.myIsExclusion)
711  {
712  match.append(array, inclusive);
713  continue;
714  }
715 
716  for (auto&& entry : array)
717  {
718  if (match.matchFull(entry, inclusive))
719  continue;
720  swap_array.append(entry);
721  }
722 
723  std::swap(array, swap_array);
724  swap_array.clear();
725  }
726 
727  if (sorted)
728  array.sort();
729  }
730 
731  template <typename T>
732  void appendMap(T& map,
733  bool sorted,
734  bool inclusive) const
735  {
736  if (!myIsValid)
737  return;
738 
739  typename T::mapped_type swap_array;
740  exint i = 0;
741  for (auto&& match : myMatches)
742  {
743  auto&& array = map[i++];
744 
745  if (!match.myIsExclusion)
746  {
747  match.append(array, inclusive);
748  continue;
749  }
750 
751  for (auto&& array : map)
752  {
753  for (auto&& entry : array.second)
754  {
755  if (match.matchFull(entry, inclusive))
756  continue;
757  swap_array.append(entry);
758  }
759 
760  std::swap(array.second, swap_array);
761  swap_array.clear();
762  }
763  }
764 
765  if (sorted)
766  {
767  for (auto&& array : map)
768  array.second.sort();
769  }
770  }
771 
772 
773  bool parse(const UT_StringHolder& pattern);
774  bool addPattern(ParseState& parse_state);
775 
776 private:
777  /// Array of patterns
778  UT_Array<ValueMatch> myMatches;
779 
780  /// The minimum step size across all components in the pattern
781  fpreal myMinimumStep;
782 
783  /// The delimiter between components, if multiple value sequences are
784  /// specified in the same pattern. The default delimiter is a space ' '
785  /// character, For example `1-10 20 30`, but this can be changed in the
786  /// constructor.
787  char myDelimiter;
788 
789  /// Indicates that the pattern has ranges with a variable step size in
790  /// different segments, for example 1-10:0.5 and 2-6:0.3
791  bool myHasVariableStep;
792 
793  /// Indicates whether or not the match is entirely numeric values, or if
794  /// some of the patterns include segments that are non-numeric strings
795  bool myIsNumeric;
796 
797  /// Whether or not at least one of the patterns has an exclusion. We store
798  /// this so that the `match` method can return early if finds a match and
799  /// knows the pattern does not have an exclusion rule in it.
800  bool myHasExclusion;
801 };
802 
803 #endif
bool isNumeric() const
void arrayMap(IntMap &values, bool sorted, bool inclusive) const
GLenum GLint * range
Definition: glcorearb.h:1925
void array(UT_StringArray &values, bool sorted, bool inclusive) const
Returns the array of string values that match the pattern.
Unsorted map container.
Definition: UT_Map.h:107
void swap(UT::ArraySet< Key, MULTI, MAX_LOAD_FACTOR_256, Clearer, Hash, KeyEqual > &a, UT::ArraySet< Key, MULTI, MAX_LOAD_FACTOR_256, Clearer, Hash, KeyEqual > &b)
Definition: UT_ArraySet.h:1631
const GLdouble * v
Definition: glcorearb.h:837
GLuint start
Definition: glcorearb.h:475
GLsizei const GLfloat * value
Definition: glcorearb.h:824
#define PDG_API
Definition: PDG_API.h:23
int64 exint
Definition: SYS_Types.h:125
bool containsRange(UT_FprealArray &range, exint value, bool inclusive) const
SYS_FORCE_INLINE const char * buffer() const
GLuint buffer
Definition: glcorearb.h:660
unsigned char uint8
Definition: SYS_Types.h:36
bool contains(exint value, bool inclusive) const
Checks if the specified integer value is in the pattern.
A utility class to do read-only operations on a subset of an existing string.
Definition: UT_StringView.h:39
GLboolean reset
Definition: glad.h:5138
exint length() const
A generic, discriminated value, whose type may be queried dynamically.
Definition: Value.h:44
bool contains(fpreal value, bool inclusive) const
Checks if the specified float value is in the pattern.
GLuint GLuint end
Definition: glcorearb.h:475
SYS_FORCE_INLINE const char * c_str() const
void arrayMap(FloatMap &values, bool sorted, bool inclusive) const
void array(UT_FprealArray &values, bool sorted, bool inclusive) const
Returns the array of float values that match the pattern.
SYS_API fpreal32 SYSfloor(fpreal32 val)
GLushort pattern
Definition: glad.h:2583
bool componentRange(UT_FprealArray &range, exint index, bool inclusive) const
Returns the value range for the specified component number.
exint append()
Definition: UT_Array.h:142
fpreal minimumStep() const
Returns the minimum step size from all components in the pattern.
void array(UT_ExintArray &values, bool sorted, bool inclusive) const
Returns the array of integer values that match the pattern.
bool SYSequalZero(const UT_Vector3T< T > &v)
Definition: UT_Vector3.h:1069
bool containsRange(UT_FprealArray &range, fpreal value, bool inclusive) const
size_t format(const char *fmt, const Args &...args)
GLenum GLsizei GLsizei GLint * values
Definition: glcorearb.h:1602
fpreal64 fpreal
Definition: SYS_Types.h:277
GLuint index
Definition: glcorearb.h:786
bool contains(const UT_StringHolder &value, bool inclusive) const
Checks if the specified string value is in the pattern.
Definition: core.h:1131
fpreal componentStep(exint index) const
Returns the step value for the specified component number.
void arrayMap(StringMap &values, bool sorted, bool inclusive) const
void clear()
Resets list to an empty list.
Definition: UT_Array.h:716
bool hasVariableStep() const
Returns true if the pattern has segments with different step sizes.
Definition: format.h:895