VirtualBox

source: vbox/trunk/src/VBox/Main/include/Matching.h@ 92154

Last change on this file since 92154 was 85236, checked in by vboxsync, 4 years ago

Main/Matching: Removed unused member mDefIgnoreCase from ParsedRegexpFilter_base. Fixed a signedness issue. bugref:9790

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 13.9 KB
Line 
1/* $Id: Matching.h 85236 2020-07-11 16:35:49Z vboxsync $ */
2/** @file
3 * Declaration of template classes that provide simple API to
4 * do matching between values and value filters constructed from strings.
5 */
6
7/*
8 * Copyright (C) 2006-2020 Oracle Corporation
9 *
10 * This file is part of VirtualBox Open Source Edition (OSE), as
11 * available from http://www.virtualbox.org. This file is free software;
12 * you can redistribute it and/or modify it under the terms of the GNU
13 * General Public License (GPL) as published by the Free Software
14 * Foundation, in version 2 as it comes in the "COPYING" file of the
15 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
16 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
17 */
18
19#ifndef MAIN_INCLUDED_Matching_h
20#define MAIN_INCLUDED_Matching_h
21#ifndef RT_WITHOUT_PRAGMA_ONCE
22# pragma once
23#endif
24
25#include <VBox/com/string.h>
26
27#include <list>
28#include <limits>
29#include <algorithm>
30
31// min and max don't allow us to use numeric_limits::min() and max()
32#if defined (_MSC_VER)
33#undef min
34#undef max
35#endif
36
37namespace matching
38{
39
40using namespace std;
41using namespace com;
42
43class ParsedFilter_base
44{
45public:
46
47 ParsedFilter_base() : mValid (false), mNull (true), mErrorPosition (0) {};
48
49 /**
50 * Returns @c true if the filter is valid, @c false otherwise.
51 */
52 bool isValid() const { return mNull || mValid; }
53 bool isNull() const { return mNull; }
54
55 /**
56 * Returns the error position from the beginning of the filter
57 * string if #isValid() is false. Positions are zero-based.
58 */
59 size_t errorPosition() const { return mErrorPosition; }
60
61protected:
62
63 /**
64 * Returns @c true if current isNull() and isValid() values make further
65 * detailed matching meaningful, otherwise returns @c false.
66 * Must be called as a first method of every isMatch() implementation,
67 * so that isMatch() will immediately return @c false if isPreMatch() returns
68 * false.
69 */
70 bool isPreMatch() const
71 {
72 if (isNull() || !isValid())
73 return false;
74 return true;
75 }
76
77 bool mValid : 1;
78 bool mNull : 1;
79 size_t mErrorPosition;
80};
81
82class ParsedIntervalFilter_base : public ParsedFilter_base
83{
84public:
85 virtual ~ParsedIntervalFilter_base() { /* Make VC++ 14.2 happy */ }
86
87protected:
88
89 enum Mode { Single, Start, End };
90
91 union Widest
92 {
93 int64_t ll;
94 uint64_t ull;
95 };
96
97 struct Limits
98 {
99 Widest min;
100 Widest max;
101 };
102
103 ParsedIntervalFilter_base() {}
104
105 /**
106 * Called by #parse when a value token is encountered.
107 * This method can modify mNull, mValid and mErrorPosition when
108 * appropriate. Parsing stops if mValid is false after this method
109 * returns (mErrorPosition most point to the error position in this case).
110 */
111 virtual void parseValue (const char *aFilter, size_t aStart, size_t aEnd,
112 Mode aMode) = 0;
113
114 static void parse (const char *aFilter,
115 ParsedIntervalFilter_base *that);
116
117 static size_t parseValue (const char *aFilter, size_t aStart, size_t aEnd,
118 bool aIsSigned, const Limits &aLimits,
119 Widest &val);
120};
121
122/**
123 * Represents a parsed interval filter.
124 * The string format is:
125 * "int:(\<m\>|([\<m\>]-[\<n\>]))|(\<m\>|([\<m\>]-[\<n\>]))+"
126 * where \<m\> and \<n\> are numbers in the decimal, hex (0xNNN) or octal
127 * (0NNN) form, and \<m\> \< \<n\>. Spaces are allowed around \<m\> and \<n\>.
128 *
129 * @tparam T type of values to match. Must be a fundamental integer type.
130 */
131template <class T>
132class ParsedIntervalFilter : public ParsedIntervalFilter_base
133{
134 typedef ParsedIntervalFilter_base Base;
135 typedef numeric_limits <T> Lim;
136
137 typedef std::list <T> List;
138 typedef std::pair <T, T> Pair;
139 typedef std::list <Pair> PairList;
140
141public:
142
143 ParsedIntervalFilter() {}
144
145 ParsedIntervalFilter (const Bstr &aFilter) { Base::parse (Utf8Str (aFilter), this); }
146
147 ParsedIntervalFilter &operator= (const Bstr &aFilter)
148 {
149 mValues.clear();
150 mIntervals.clear();
151 Base::parse (Utf8Str (aFilter), this);
152 return *this;
153 }
154
155 bool isMatch (const T &aValue) const
156 {
157 if (!isPreMatch())
158 return false;
159
160 {
161 typename List::const_iterator it =
162 std::find (mValues.begin(), mValues.end(), aValue);
163 if (it != mValues.end())
164 return true;
165 }
166
167 for (typename PairList::const_iterator it = mIntervals.begin();
168 it != mIntervals.end(); ++ it)
169 {
170 if ((*it).first <= aValue &&
171 aValue <= (*it).second)
172 return true;
173 }
174
175 return false;
176 }
177
178protected:
179
180 struct Limits : public Base::Limits
181 {
182 Limits()
183 {
184 if (Lim::is_signed)
185 {
186 min.ll = (int64_t) Lim::min();
187 max.ll = (int64_t) Lim::max();
188 }
189 else
190 {
191 min.ull = (uint64_t) Lim::min();
192 max.ull = (uint64_t) Lim::max();
193 }
194 }
195
196 static T toValue (const Widest &aWidest)
197 {
198 if (Lim::is_signed)
199 return (T) aWidest.ll;
200 else
201 return (T) aWidest.ull;
202 }
203 };
204
205 virtual void parseValue (const char *aFilter, size_t aStart, size_t aEnd,
206 Mode aMode)
207 {
208 AssertReturn (Lim::is_integer, (void) 0);
209 AssertReturn (
210 (Lim::is_signed && Lim::digits <= numeric_limits <int64_t>::digits) ||
211 (!Lim::is_signed && Lim::digits <= numeric_limits <uint64_t>::digits),
212 (void) 0);
213
214 Limits limits;
215 Widest val;
216 size_t parsed = aEnd;
217
218 if (aStart != aEnd)
219 parsed = Base::parseValue (aFilter, aStart, aEnd,
220 Lim::is_signed, limits, val);
221
222 if (parsed != aEnd)
223 {
224 mValid = false;
225 mErrorPosition = parsed;
226 return;
227 }
228
229 switch (aMode)
230 {
231 /// @todo (dmik): future optimizations:
232 // 1) join intervals when they overlap
233 // 2) ignore single values that are within any existing interval
234 case Base::Single:
235 {
236 if (aStart == aEnd)
237 {
238 // an empty string (contains only spaces after "int:")
239 mValid = false;
240 mErrorPosition = aEnd;
241 AssertReturn (!mValues.size() && !mIntervals.size(), (void) 0);
242 break;
243 }
244 mValues.push_back (limits.toValue (val));
245 break;
246 }
247 case Base::Start:
248 {
249 // aStart == aEnd means smth. like "-[NNN]"
250 T m = aStart == aEnd ? limits.toValue (limits.min)
251 : limits.toValue (val);
252 mIntervals.push_back (Pair (m, m));
253 break;
254 }
255 case Base::End:
256 {
257 // aStart == aEnd means smth. like "[NNN]-"
258 T n = aStart == aEnd ? limits.toValue (limits.max)
259 : limits.toValue (val);
260 if (n < mIntervals.back().first)
261 {
262 // error at the beginning of N
263 mValid = false;
264 mErrorPosition = aStart;
265 break;
266 }
267 mIntervals.back().second = n;
268 break;
269 }
270 }
271 }
272
273 std::list <T> mValues;
274 std::list <std::pair <T, T> > mIntervals;
275};
276
277/**
278 * Represents a boolean filter.
279 * The string format is: "true|false|yes|no|1|0" or an empty string (any match).
280 */
281
282class ParsedBoolFilter : public ParsedFilter_base
283{
284public:
285
286 ParsedBoolFilter() : mValue (false), mValueAny (false) {}
287
288 ParsedBoolFilter (const Bstr &aFilter) { parse (aFilter); }
289
290 ParsedBoolFilter &operator= (const Bstr &aFilter)
291 {
292 parse (aFilter);
293 return *this;
294 }
295
296 bool isMatch (const bool aValue) const
297 {
298 if (!isPreMatch())
299 return false;
300
301 return mValueAny || mValue == aValue;
302 }
303
304 bool isMatch (const BOOL aValue) const
305 {
306 return isMatch (bool (aValue == TRUE));
307 }
308
309private:
310
311 void parse (const Bstr &aFilter);
312
313 bool mValue : 1;
314 bool mValueAny : 1;
315};
316
317class ParsedRegexpFilter_base : public ParsedFilter_base
318{
319protected:
320
321 ParsedRegexpFilter_base (bool aDefIgnoreCase = false,
322 size_t aMinLen = 0, size_t aMaxLen = 0)
323 : mIgnoreCase (aDefIgnoreCase)
324 , mMinLen (aMinLen)
325 , mMaxLen (aMaxLen)
326 {}
327
328 ParsedRegexpFilter_base (const Bstr &aFilter, bool aDefIgnoreCase = false,
329 size_t aMinLen = 0, size_t aMaxLen = 0)
330 : mIgnoreCase (aDefIgnoreCase)
331 , mMinLen (aMinLen)
332 , mMaxLen (aMaxLen)
333 {
334 parse (aFilter);
335 }
336
337 ParsedRegexpFilter_base &operator= (const Bstr &aFilter)
338 {
339 parse (aFilter);
340 return *this;
341 }
342
343 bool isMatch (const Bstr &aValue) const;
344
345private:
346
347 void parse (const Bstr &aFilter);
348
349 bool mIgnoreCase : 1;
350
351 size_t mMinLen;
352 size_t mMaxLen;
353
354 Bstr mSimple;
355};
356
357/**
358 * Represents a parsed regexp filter.
359 *
360 * The string format is: "rx:\<regexp\>" or "\<string\>"
361 * where \<regexp\> is a valid regexp and \<string\> is the exact match.
362 *
363 * @tparam Conv
364 * class that must define a public static function
365 * <tt>Bstr toBstr (T aValue)</tt>, where T is the
366 * type of values that should be accepted by #isMatch().
367 * This function is used to get the string representation of T
368 * for regexp matching.
369 * @tparam aIgnoreCase
370 * true if the case insensitive comparison should be done by default
371 * and false otherwise
372 * @tparam aMinLen
373 * minimum string length, or 0 if not limited.
374 * Used only when the filter string represents the exact match.
375 * @tparam aMaxLen
376 * maximum string length, or 0 if not limited.
377 * Used only when the filter string represents the exact match.
378 */
379template <class Conv, bool aIgnoreCase, size_t aMinLen = 0, size_t aMaxLen = 0>
380class ParsedRegexpFilter : public ParsedRegexpFilter_base
381{
382public:
383
384 enum { IgnoreCase = aIgnoreCase, MinLen = aMinLen, MaxLen = aMaxLen };
385
386 ParsedRegexpFilter() : ParsedRegexpFilter_base (IgnoreCase, MinLen, MaxLen) {}
387
388 ParsedRegexpFilter (const Bstr &aFilter)
389 : ParsedRegexpFilter_base (aFilter, IgnoreCase, MinLen, MaxLen) {}
390
391 ParsedRegexpFilter &operator= (const Bstr &aFilter)
392 {
393 ParsedRegexpFilter_base::operator= (aFilter);
394 return *this;
395 }
396
397 template <class T>
398 bool isMatch (const T &aValue) const
399 {
400 if (!this->isPreMatch())
401 return false;
402
403 return ParsedRegexpFilter_base::isMatch (Conv::toBstr (aValue));
404 }
405
406protected:
407};
408
409/**
410 * Joins two filters into one.
411 * Only one filter is active (i.e. used for matching or for error reporting)
412 * at any given time. The active filter is chosen every time when a new
413 * filter string is assigned to an instance of this class -- the filter
414 * for which isNull() = false after parsing the string becomes the active
415 * one (F1 is tried first).
416 *
417 * Both filters must have <tt>bool isMatch(const T&)</tt> methods where T is
418 * the same type as used in #isMatch().
419 *
420 * @tparam F1 first filter class
421 * @tparam F2 second filter class
422 */
423template <class F1, class F2>
424class TwoParsedFilters
425{
426public:
427
428 TwoParsedFilters() {}
429
430 TwoParsedFilters (const Bstr &aFilter)
431 {
432 mFilter1 = aFilter;
433 if (mFilter1.isNull())
434 mFilter2 = aFilter;
435 }
436
437 TwoParsedFilters &operator= (const Bstr &aFilter)
438 {
439 mFilter1 = aFilter;
440 if (mFilter1.isNull())
441 mFilter2 = aFilter;
442 else
443 mFilter2 = F2(); // reset to null
444 return *this;
445 }
446
447 template <class T>
448 bool isMatch (const T &aValue) const
449 {
450 return mFilter1.isMatch (aValue) || mFilter2.isMatch (aValue);
451 }
452
453 bool isValid() const { return isNull() || (mFilter1.isValid() && mFilter2.isValid()); }
454
455 bool isNull() const { return mFilter1.isNull() && mFilter2.isNull(); }
456
457 size_t errorPosition() const
458 {
459 return !mFilter1.isValid() ? mFilter1.errorPosition() :
460 !mFilter2.isValid() ? mFilter2.errorPosition() : 0;
461 }
462
463 const F1 &first() const { return mFilter1; }
464 const F2 &second() const { return mFilter2; }
465
466private:
467
468 F1 mFilter1;
469 F2 mFilter2;
470};
471
472/**
473 * Inherits from the given parsed filter class and keeps the string used to
474 * construct the filter as a member.
475 *
476 * @tparam F parsed filter class
477 */
478template <class F>
479class Matchable : public F
480{
481public:
482
483 Matchable() {}
484
485 /**
486 * Creates a new parsed filter from the given filter string.
487 * If the string format is invalid, #isValid() will return false.
488 */
489 Matchable (const Bstr &aString)
490 : F (aString), mString (aString) {}
491
492 Matchable (CBSTR aString)
493 : F (Bstr (aString)), mString (aString) {}
494
495 /**
496 * Assigns a new filter string to this object and recreates the parser.
497 * If the string format is invalid, #isValid() will return false.
498 */
499 Matchable &operator= (const Bstr &aString)
500 {
501 F::operator= (aString);
502 mString = aString;
503 return *this;
504 }
505
506 Matchable &operator= (CBSTR aString)
507 {
508 F::operator= (Bstr (aString));
509 mString = aString;
510 return *this;
511 }
512
513 /**
514 * Returns the filter string allowing to use the instance where
515 * Str can be used.
516 */
517 operator const Bstr&() const { return mString; }
518
519 /** Returns the filter string */
520 const Bstr& string() const { return mString; }
521
522private:
523
524 Bstr mString;
525};
526
527} /* namespace matching */
528
529#endif /* !MAIN_INCLUDED_Matching_h */
530/* vi: set tabstop=4 shiftwidth=4 expandtab: */
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use