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_ArrayMap.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  /// Resets the pattern to an unparsed state
184  bool reset(
185  const UT_StringHolder& pattern,
186  char delimiter=' ');
187 
188  /// Updates and reparses the pattern if the supplied pattern string is
189  /// different than the current pattern string
190  bool tryReset(
191  const UT_StringHolder& pattern,
192  char delimiter= ' ');
193 
194  /// Parses the input string into this pattern -- only valid if no pattern
195  /// has been parsed previously
196  bool parse(const UT_StringView& pattern);
197 
198 private:
199  /// Possible types for a value match
200  enum MatchType : uint8
201  {
202  /// A single constant value
203  eMatchTypeConstant,
204 
205  /// A simple range with a start and end point, and a step size of 1
206  eMatchTypeRange,
207 
208  /// A range with a custom step size
209  eMatchTypeRangeStep,
210  };
211 
212  /// Utility to do an inclusive vs. exclusive range end comparison
213  template <typename Value, typename Step, typename End>
214  static inline bool inclusiveComp(bool i,
215  const Value& v,
216  const Step& step,
217  const End& end)
218  {
219  if (step > 0)
220  return i ? (v <= end) : (v < end);
221  else
222  return i ? (v >= end) : (v > end);
223  }
224 
225  /// Value containing a float, string and integer part
226  struct Value
227  {
228  /// Appends a constant string, integer or float value from this
229  /// match value into the input array
230  void append(UT_StringArray& array) const
231  { array.append(myString); }
232  void append(UT_ExintArray& array) const
233  { if (myHasInteger) array.append(myInteger); }
234  void append(UT_FprealArray& array) const
235  { if (myHasFloat) array.append(myFloat); }
236 
237 
238  /// Appends values between start and end to the input array
239  static void append(UT_StringArray& array,
240  const Value& start,
241  const Value& end,
242  bool inclusive)
243  {
244  if (!start.myHasInteger || !end.myHasInteger)
245  return;
246 
247  for (exint i = start.myInteger;
248  inclusiveComp(
249  inclusive, i, 1, end.myInteger);
250  i++)
251  {
253  buffer.format("{}{}", start.myPrefix, i);
254  array.append(buffer.buffer());
255  }
256  }
257  static void append(UT_FprealArray& array,
258  const Value& start,
259  const Value& end,
260  bool inclusive)
261  {
262  if (!start.myHasFloat || !end.myHasFloat)
263  return;
264  for (fpreal i = start.myFloat;
265  inclusiveComp(
266  inclusive, i, 1.0, end.myFloat);
267  i += 1.0)
268  {
269  array.append(i);
270  }
271  }
272  static void append(UT_ExintArray& array,
273  const Value& start,
274  const Value& end,
275  bool inclusive)
276  {
277  if (!start.myHasInteger || !end.myHasInteger)
278  return;
279  for (exint i = start.myInteger;
280  inclusiveComp(
281  inclusive, i, 1, end.myInteger);
282  i++)
283  {
284  array.append(i);
285  }
286  }
287 
288 
289  /// Appends values between start and end, with the specified step
290  /// size, to the input array
291  static void append(UT_StringArray& array,
292  const Value& start,
293  const Value& end,
294  const Value& step,
295  bool inclusive)
296  {
297  if (!start.myHasInteger ||
298  !end.myHasInteger ||
299  !step.myHasInteger)
300  return;
301  if (step.myInteger == 0)
302  return;
303 
304  for (exint i = start.myInteger;
305  inclusiveComp(
306  inclusive, i,
307  step.myInteger, end.myInteger);
308  i += step.myInteger)
309  {
311  buffer.format("{}{}", start.myPrefix, i);
312  array.append(buffer.buffer());
313  }
314  }
315  static void append(UT_FprealArray& array,
316  const Value& start,
317  const Value& end,
318  const Value& step,
319  bool inclusive)
320  {
321  if (!start.myHasFloat ||
322  !end.myHasFloat ||
323  !step.myHasFloat)
324  return;
325  if (SYSequalZero(step.myFloat))
326  return;
327 
328  for (fpreal i = start.myFloat;
329  inclusiveComp(
330  inclusive, i,
331  step.myFloat, end.myFloat);
332  i += step.myFloat)
333  {
334  array.append(i);
335  }
336  }
337  static void append(UT_ExintArray& array,
338  const Value& start,
339  const Value& end,
340  const Value& step,
341  bool inclusive)
342  {
343  if (!start.myHasInteger ||
344  !end.myHasInteger ||
345  !step.myHasFloat)
346  return;
347  if (SYSequalZero(step.myFloat))
348  return;
349 
350  for (fpreal i = start.myInteger;
351  inclusiveComp(
352  inclusive, i,
353  step.myFloat, end.myInteger);
354  i += step.myFloat)
355  {
356  array.append(exint(i));
357  }
358  }
359 
360 
361  /// Performs an exact match comparison between an input value and the
362  /// constant value in this match value instance
363  bool match(const UT_StringHolder& value) const
364  { return myString == value; }
365  bool match(fpreal value) const
366  { return myHasFloat && SYSalmostEqual(value, myFloat); }
367  bool match(exint value) const
368  { return myHasInteger && (myInteger == value); }
369 
370 
371  /// Checks if the input value is less than the match value in this
372  /// instance
373  bool less(const UT_StringHolder& value, bool inclusive) const
374  { return true; }
375  bool less(exint value, bool inclusive) const
376  {
377  if (!myHasInteger) return true;
378  else if (inclusive) return value < myInteger;
379  else return value <= myInteger;
380  }
381  bool less(fpreal value, bool inclusive) const
382  {
383  if (!myHasFloat) return true;
384  else if (inclusive) return value < myFloat;
385  else return value <= myFloat;
386  }
387 
388  /// Checks if the input value is greater than the match value in this
389  /// instance
390  bool greater(const UT_StringHolder& value, bool inclusive) const
391  { return true; }
392  bool greater(exint value, bool inclusive) const
393  {
394  if (!myHasInteger) return true;
395  else if (inclusive) return value > myInteger;
396  else return value >= myInteger;
397  }
398  bool greater(fpreal value, bool inclusive) const
399  {
400  if (!myHasFloat) return true;
401  else if (inclusive) return value > myFloat;
402  else return value >= myFloat;
403  }
404 
405 
406  /// Checks if the input value is in the same value range as this match
407  /// value, module the step
408  bool step(const Value& start, const UT_StringHolder& value) const
409  { return false; }
410  bool step(const Value& start, exint value) const
411  {
412  if (!start.myHasInteger || !myHasFloat)
413  return false;
414 
415  fpreal delta = value - start.myInteger;
416  return SYSalmostEqual(SYSfmod(delta, myFloat), 0);
417  }
418  bool step(const Value& start, fpreal value) const
419  {
420  if (!start.myHasFloat || !myHasFloat)
421  return false;
422 
423  fpreal delta = value - start.myFloat;
424  return SYSalmostEqual(SYSfmod(delta, myFloat), 0);
425  }
426 
427  UT_StringHolder myPrefix;
428  UT_StringHolder myString;
429  fpreal myFloat;
430  exint myInteger;
431 
432  bool myHasFloat : 1;
433  bool myHasInteger : 1;
434  bool myHasPrefix : 1;
435  };
436 
437  /// Value match entry
438  struct ValueMatch
439  {
440  /// Does a full match, including parsing the prefix if the value is a
441  /// string
442  bool matchFull(const UT_StringHolder& value, bool inclusive) const
443  {
444  if (match(value, nullptr, inclusive))
445  return true;
446 
447  int len = value.length();
448  const char* data = value.c_str();
449  while (len--)
450  {
451  if (!SYSisdigit(data[len]))
452  break;
453  }
454 
455  exint suffix = atoi(data+len+1);
456 
457  if (myHasPrefix)
458  {
459  if (len < 0)
460  return false;
461 
462  UT_StringView prefix(data, len+1);
463  return match(suffix, &prefix, inclusive);
464  }
465  else
466  {
467  if (len > 0)
468  return false;
469  return match(suffix, nullptr, inclusive);
470  }
471  }
472  bool matchFull(const exint& value, bool inclusive) const
473  { return match(value, nullptr, inclusive); }
474  bool matchFull(const fpreal& value, bool inclusive) const
475  { return match(value, nullptr, inclusive); }
476 
477  /// Matches the pattern with the specified value
478  template <typename T>
479  bool match(const T& value,
480  const UT_StringView* prefix,
481  bool inclusive) const
482  {
483  // If the pattern does not have a prefix and we
484  // do, return early
485  if (!myMatchValues[0].myHasPrefix && prefix)
486  return false;
487 
488  // Constants only check the first value
489  if (myMatchType == eMatchTypeConstant)
490  {
491  if (!myMatchValues[0].match(value))
492  return false;
493  }
494  // Simple range check
495  else
496  {
497  if (myMatchValues[0].less(value, true))
498  return false;
499 
500  if (myMatchValues[1].greater(value, inclusive))
501  return false;
502  }
503 
504  // If the pattern has a prefix then we require
505  // a matching one
506  if (myMatchValues[0].myHasPrefix)
507  {
508  if (!prefix)
509  return false;
510  else if (*prefix != myMatchValues[0].myPrefix.c_str())
511  return false;
512  }
513 
514  // At this point, constants and ranges have matched
515  if (myMatchType <= eMatchTypeRange)
516  return true;
517  return myMatchValues[2].step(myMatchValues[0], value);
518  }
519 
520  /// Appends the values from the match into the specified array
521  template <typename T>
522  void append(T& array, bool inclusive) const
523  {
524  if (myMatchType == eMatchTypeConstant)
525  myMatchValues[0].append(array);
526  else if (myMatchType == eMatchTypeRange)
527  {
528  Value::append(
529  array,
530  myMatchValues[0],
531  myMatchValues[1],
532  inclusive);
533  }
534  else
535  {
536  Value::append(
537  array,
538  myMatchValues[0],
539  myMatchValues[1],
540  myMatchValues[2],
541  inclusive);
542  }
543  }
544 
545  /// Returns the nth value from the match as a float
546  fpreal floatValue(int index, fpreal default_value) const
547  {
548  if (index > myMatchType)
549  return default_value;
550 
551  const Value& value = myMatchValues[index];
552  if (value.myHasFloat)
553  return value.myFloat;
554  if (value.myHasInteger)
555  return (fpreal)(value.myInteger);
556 
557  return 0;
558  }
559 
560  /// Returns the range of values in the match segment as a float
561  /// array
562  void setRange(UT_FprealArray& range, bool inclusive) const
563  {
564  fpreal start = floatValue(0, 0);
565  fpreal end = floatValue(1, start);
566  fpreal step = floatValue(2, 1);
567 
568  fpreal last = SYSfloor((end-start)/step);
569  last = last*step + start;
570  if (!inclusive && SYSalmostEqual(end, last))
571  last -= step;
572 
573  range.clear();
574  range.append(start);
575  range.append(last);
576  range.append(step);
577  }
578 
579  /// Values in the match entry
580  Value myMatchValues[3];
581 
582  /// The match type
583  MatchType myMatchType;
584 
585  /// Whether or not the value sequence is an exclusion
586  bool myIsExclusion;
587 
588  /// Whether or not the values in this match have a prefix
589  bool myHasPrefix;
590 
591  /// Whether or not the values in this match are all numeric
592  bool myIsNumeric;
593  };
594 
595  /// Helper struct for parsing string data
596  struct ParseState
597  {
598  ParseState()
599  { reset(); }
600 
601  void reset()
602  {
603  myValueIndex = 0;
604  myRangeIndex = 0;
605  myLastChar = 0;
606  myIsQuoted = false;
607  myIsSkip = false;
608  myIsExclusion = false;
609 
610  for (int i = 0; i < 3; i++)
611  myValues[i].clear();
612  }
613 
614  UT_WorkBuffer myValues[3];
615  exint myRangeValues[3];
616  int myValueIndex;
617  int myRangeIndex;
618  char myLastChar;
619 
620  bool myIsQuoted;
621  bool myIsSkip;
622  bool myIsExclusion;
623  };
624 
625 private:
626  template <typename T>
627  bool match(const T& value,
628  bool inclusive,
629  const ValueMatch** value_match) const
630  {
631  if (!myIsValid)
632  return false;
633 
634  bool found = false;
635  for (auto&& match : myMatches)
636  {
637  if (!match.match(value, nullptr, inclusive))
638  continue;
639 
640  if (!myHasExclusion)
641  {
642  if (value_match)
643  *value_match = &match;
644  return true;
645  }
646 
647  if (!match.myIsExclusion)
648  {
649  if (value_match)
650  *value_match = &match;
651  found = true;
652  }
653  else if (found)
654  return false;
655  }
656 
657  return found;
658  }
659 
660  bool match(exint value,
661  const UT_StringView& prefix,
662  bool inclusive,
663  const ValueMatch** value_match) const
664  {
665  if (!myIsValid)
666  return false;
667 
668  bool found = false;
669  for (auto&& match : myMatches)
670  {
671  if (!match.match(value, &prefix, inclusive))
672  continue;
673 
674  if (!myHasExclusion)
675  {
676  if (value_match)
677  *value_match = &match;
678  return true;
679  }
680 
681  if (!match.myIsExclusion)
682  {
683  if (value_match)
684  *value_match = &match;
685  found = true;
686  }
687  else if (found)
688  return false;
689  }
690 
691  return found;
692  }
693 
694  template <typename T>
695  bool matchRange(UT_FprealArray& range,
696  const T& value,
697  bool inclusive) const
698  {
699  const ValueMatch* value_match = nullptr;
700  if (!match(value, inclusive, &value_match))
701  return false;
702  if (!value)
703  return false;
704 
705  value_match->setRange(range, inclusive);
706  return true;
707  }
708 
709 
710  template <typename T>
711  void append(T& array,
712  bool sorted,
713  bool inclusive) const
714  {
715  if (!myIsValid)
716  return;
717 
718  T swap_array;
719  for (auto&& match : myMatches)
720  {
721  if (!match.myIsExclusion)
722  {
723  match.append(array, inclusive);
724  continue;
725  }
726 
727  for (auto&& entry : array)
728  {
729  if (match.matchFull(entry, inclusive))
730  continue;
731  swap_array.append(entry);
732  }
733 
734  std::swap(array, swap_array);
735  swap_array.clear();
736  }
737 
738  if (sorted)
739  array.sort();
740  }
741 
742  template <typename T>
743  void appendMap(T& map,
744  bool sorted,
745  bool inclusive) const
746  {
747  if (!myIsValid)
748  return;
749 
750  typename T::mapped_type swap_array;
751  exint i = 0;
752  for (auto&& match : myMatches)
753  {
754  auto&& array = map[i++];
755 
756  if (!match.myIsExclusion)
757  {
758  match.append(array, inclusive);
759  continue;
760  }
761 
762  for (auto&& array : map)
763  {
764  for (auto&& entry : array.second)
765  {
766  if (match.matchFull(entry, inclusive))
767  continue;
768  swap_array.append(entry);
769  }
770 
771  std::swap(array.second, swap_array);
772  swap_array.clear();
773  }
774  }
775 
776  if (sorted)
777  {
778  for (auto&& array : map)
779  array.second.sort();
780  }
781  }
782 
783 
784  bool parse(const UT_StringHolder& pattern);
785  bool addPattern(ParseState& parse_state);
786 
787 private:
788  /// Array of patterns
789  UT_Array<ValueMatch> myMatches;
790 
791  /// The minimum step size across all components in the pattern
792  fpreal myMinimumStep;
793 
794  /// The delimiter between components, if multiple value sequences are
795  /// specified in the same pattern. The default delimiter is a space ' '
796  /// character, For example `1-10 20 30`, but this can be changed in the
797  /// constructor.
798  char myDelimiter;
799 
800  /// Indicates that the pattern has ranges with a variable step size in
801  /// different segments, for example 1-10:0.5 and 2-6:0.3
802  bool myHasVariableStep;
803 
804  /// Indicates whether or not the match is entirely numeric values, or if
805  /// some of the patterns include segments that are non-numeric strings
806  bool myIsNumeric;
807 
808  /// Whether or not at least one of the patterns has an exclusion. We store
809  /// this so that the `match` method can return early if finds a match and
810  /// knows the pattern does not have an exclusion rule in it.
811  bool myHasExclusion;
812 };
813 
814 #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.
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:1639
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
void reset(const UT_StringHolder &pattern)
Resets the pattern.
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
__hostdev__ uint64_t last(uint32_t i) const
Definition: NanoVDB.h:5976
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:729
bool hasVariableStep() const
Returns true if the pattern has segments with different step sizes.
Definition: format.h:895