VirtualBox

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

Last change on this file since 82797 was 76562, checked in by vboxsync, 6 years ago

Main: Use MAIN_INCLUDED_ and MAIN_INCLUDED_SRC_ as header guard prefixes with scm.

  • 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 76562 2019-01-01 03:22:50Z 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-2019 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{
84protected:
85
86 enum Mode { Single, Start, End };
87
88 union Widest
89 {
90 int64_t ll;
91 uint64_t ull;
92 };
93
94 struct Limits
95 {
96 Widest min;
97 Widest max;
98 };
99
100 ParsedIntervalFilter_base() {}
101
102 /**
103 * Called by #parse when a value token is encountered.
104 * This method can modify mNull, mValid and mErrorPosition when
105 * appropriate. Parsing stops if mValid is false after this method
106 * returns (mErrorPosition most point to the error position in this case).
107 */
108 virtual void parseValue (const char *aFilter, size_t aStart, size_t aEnd,
109 Mode aMode) = 0;
110
111 static void parse (const char *aFilter,
112 ParsedIntervalFilter_base *that);
113
114 static size_t parseValue (const char *aFilter, size_t aStart, size_t aEnd,
115 bool aIsSigned, const Limits &aLimits,
116 Widest &val);
117};
118
119/**
120 * Represents a parsed interval filter.
121 * The string format is:
122 * "int:(\<m\>|([\<m\>]-[\<n\>]))|(\<m\>|([\<m\>]-[\<n\>]))+"
123 * where \<m\> and \<n\> are numbers in the decimal, hex (0xNNN) or octal
124 * (0NNN) form, and \<m\> \< \<n\>. Spaces are allowed around \<m\> and \<n\>.
125 *
126 * @tparam T type of values to match. Must be a fundamental integer type.
127 */
128template <class T>
129class ParsedIntervalFilter : public ParsedIntervalFilter_base
130{
131 typedef ParsedIntervalFilter_base Base;
132 typedef numeric_limits <T> Lim;
133
134 typedef std::list <T> List;
135 typedef std::pair <T, T> Pair;
136 typedef std::list <Pair> PairList;
137
138public:
139
140 ParsedIntervalFilter() {}
141
142 ParsedIntervalFilter (const Bstr &aFilter) { Base::parse (Utf8Str (aFilter), this); }
143
144 ParsedIntervalFilter &operator= (const Bstr &aFilter)
145 {
146 mValues.clear();
147 mIntervals.clear();
148 Base::parse (Utf8Str (aFilter), this);
149 return *this;
150 }
151
152 bool isMatch (const T &aValue) const
153 {
154 if (!isPreMatch())
155 return false;
156
157 {
158 typename List::const_iterator it =
159 std::find (mValues.begin(), mValues.end(), aValue);
160 if (it != mValues.end())
161 return true;
162 }
163
164 for (typename PairList::const_iterator it = mIntervals.begin();
165 it != mIntervals.end(); ++ it)
166 {
167 if ((*it).first <= aValue &&
168 aValue <= (*it).second)
169 return true;
170 }
171
172 return false;
173 }
174
175protected:
176
177 struct Limits : public Base::Limits
178 {
179 Limits()
180 {
181 if (Lim::is_signed)
182 {
183 min.ll = (int64_t) Lim::min();
184 max.ll = (int64_t) Lim::max();
185 }
186 else
187 {
188 min.ull = (uint64_t) Lim::min();
189 max.ull = (uint64_t) Lim::max();
190 }
191 }
192
193 static T toValue (const Widest &aWidest)
194 {
195 if (Lim::is_signed)
196 return (T) aWidest.ll;
197 else
198 return (T) aWidest.ull;
199 }
200 };
201
202 virtual void parseValue (const char *aFilter, size_t aStart, size_t aEnd,
203 Mode aMode)
204 {
205 AssertReturn (Lim::is_integer, (void) 0);
206 AssertReturn (
207 (Lim::is_signed && Lim::digits <= numeric_limits <int64_t>::digits) ||
208 (!Lim::is_signed && Lim::digits <= numeric_limits <uint64_t>::digits),
209 (void) 0);
210
211 Limits limits;
212 Widest val;
213 size_t parsed = aEnd;
214
215 if (aStart != aEnd)
216 parsed = Base::parseValue (aFilter, aStart, aEnd,
217 Lim::is_signed, limits, val);
218
219 if (parsed != aEnd)
220 {
221 mValid = false;
222 mErrorPosition = parsed;
223 return;
224 }
225
226 switch (aMode)
227 {
228 /// @todo (dmik): future optimizations:
229 // 1) join intervals when they overlap
230 // 2) ignore single values that are within any existing interval
231 case Base::Single:
232 {
233 if (aStart == aEnd)
234 {
235 // an empty string (contains only spaces after "int:")
236 mValid = false;
237 mErrorPosition = aEnd;
238 AssertReturn (!mValues.size() && !mIntervals.size(), (void) 0);
239 break;
240 }
241 mValues.push_back (limits.toValue (val));
242 break;
243 }
244 case Base::Start:
245 {
246 // aStart == aEnd means smth. like "-[NNN]"
247 T m = aStart == aEnd ? limits.toValue (limits.min)
248 : limits.toValue (val);
249 mIntervals.push_back (Pair (m, m));
250 break;
251 }
252 case Base::End:
253 {
254 // aStart == aEnd means smth. like "[NNN]-"
255 T n = aStart == aEnd ? limits.toValue (limits.max)
256 : limits.toValue (val);
257 if (n < mIntervals.back().first)
258 {
259 // error at the beginning of N
260 mValid = false;
261 mErrorPosition = aStart;
262 break;
263 }
264 mIntervals.back().second = n;
265 break;
266 }
267 }
268 }
269
270 std::list <T> mValues;
271 std::list <std::pair <T, T> > mIntervals;
272};
273
274/**
275 * Represents a boolean filter.
276 * The string format is: "true|false|yes|no|1|0" or an empty string (any match).
277 */
278
279class ParsedBoolFilter : public ParsedFilter_base
280{
281public:
282
283 ParsedBoolFilter() : mValue (false), mValueAny (false) {}
284
285 ParsedBoolFilter (const Bstr &aFilter) { parse (aFilter); }
286
287 ParsedBoolFilter &operator= (const Bstr &aFilter)
288 {
289 parse (aFilter);
290 return *this;
291 }
292
293 bool isMatch (const bool aValue) const
294 {
295 if (!isPreMatch())
296 return false;
297
298 return mValueAny || mValue == aValue;
299 }
300
301 bool isMatch (const BOOL aValue) const
302 {
303 return isMatch (bool (aValue == TRUE));
304 }
305
306private:
307
308 void parse (const Bstr &aFilter);
309
310 bool mValue : 1;
311 bool mValueAny : 1;
312};
313
314class ParsedRegexpFilter_base : public ParsedFilter_base
315{
316protected:
317
318 ParsedRegexpFilter_base (bool aDefIgnoreCase = false,
319 size_t aMinLen = 0, size_t aMaxLen = 0)
320 : mDefIgnoreCase (aDefIgnoreCase)
321 , mIgnoreCase (aDefIgnoreCase)
322 , mMinLen (aMinLen)
323 , mMaxLen (aMaxLen)
324 {}
325
326 ParsedRegexpFilter_base (const Bstr &aFilter, bool aDefIgnoreCase = false,
327 size_t aMinLen = 0, size_t aMaxLen = 0)
328 : mDefIgnoreCase (aDefIgnoreCase)
329 , mIgnoreCase (aDefIgnoreCase)
330 , mMinLen (aMinLen)
331 , mMaxLen (aMaxLen)
332 {
333 parse (aFilter);
334 }
335
336 ParsedRegexpFilter_base &operator= (const Bstr &aFilter)
337 {
338 parse (aFilter);
339 return *this;
340 }
341
342 bool isMatch (const Bstr &aValue) const;
343
344private:
345
346 void parse (const Bstr &aFilter);
347
348 bool mDefIgnoreCase : 1;
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.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette