Index: sandbox/linux/bpf_dsl/bpf_dsl.h |
diff --git a/sandbox/linux/bpf_dsl/bpf_dsl.h b/sandbox/linux/bpf_dsl/bpf_dsl.h |
index b46d1adb113afa0b60fd51b0e17f26331d124663..0664d516147d202d1d0fdde40a775be266f9fbce 100644 |
--- a/sandbox/linux/bpf_dsl/bpf_dsl.h |
+++ b/sandbox/linux/bpf_dsl/bpf_dsl.h |
@@ -8,6 +8,7 @@ |
#include <stdint.h> |
#include <utility> |
+#include <vector> |
#include "base/macros.h" |
#include "base/memory/ref_counted.h" |
@@ -59,10 +60,12 @@ class SandboxBPF; |
// |
// More generally, the DSL currently supports the following grammar: |
// |
-// result = Allow() | Error(errno) | Trap(trap_func, arg) |
+// result = Allow() | Error(errno) | Trap(trap_func, aux) |
// | If(bool, result)[.ElseIf(bool, result)].Else(result) |
-// bool = arg == val | (arg & mask) == val |
-// | !bool | bool && bool | bool || bool |
+// | Switch(arg)[.Case(val, result)].Default(result) |
+// bool = BoolConst(boolean) | !bool | bool && bool | bool || bool |
+// | arg == val |
+// arg = Arg<T>(num) | arg & mask |
// |
// The semantics of each function and operator are intended to be |
// intuitive, but are described in more detail below. |
@@ -76,6 +79,8 @@ namespace bpf_dsl { |
// Forward declarations of classes; see below for proper documentation. |
class Elser; |
+template <typename T> |
+class Caser; |
namespace internal { |
class ResultExprImpl; |
class BoolExprImpl; |
@@ -159,6 +164,9 @@ class SANDBOX_EXPORT Arg { |
DISALLOW_ASSIGN(Arg); |
}; |
+// Convert a bool value into a BoolExpr. |
+SANDBOX_EXPORT BoolExpr BoolConst(bool value); |
+ |
// Various ways to combine boolean expressions into more complex expressions. |
// They follow standard boolean algebra laws. |
SANDBOX_EXPORT BoolExpr operator!(const BoolExpr& cond); |
@@ -190,9 +198,62 @@ class SANDBOX_EXPORT Elser { |
Cons<Clause>::List clause_list_; |
friend Elser If(const BoolExpr&, const ResultExpr&); |
+ template <typename T> |
+ friend Caser<T> Switch(const Arg<T>&); |
DISALLOW_ASSIGN(Elser); |
}; |
+// Switch begins a switch expression dispatched according to the |
+// specified argument value. |
+template <typename T> |
+SANDBOX_EXPORT Caser<T> Switch(const Arg<T>& arg); |
+ |
+template <typename T> |
+class SANDBOX_EXPORT Caser { |
+ public: |
+ Caser(const Caser<T>& caser) : arg_(caser.arg_), elser_(caser.elser_) {} |
+ ~Caser() {} |
+ |
+ // Case adds a single-value "case" clause to the switch. |
+ Caser<T> Case(T value, ResultExpr result) const; |
+ |
+ // Cases adds a multiple-value "case" clause to the switch. |
+ // See also the SANDBOX_BPF_DSL_CASES macro below for a more idiomatic way |
+ // of using this function. |
+ Caser<T> Cases(const std::vector<T>& values, ResultExpr result) const; |
+ |
+ // Terminate the switch with a "default" clause. |
+ ResultExpr Default(ResultExpr result) const; |
+ |
+ private: |
+ Caser(const Arg<T>& arg, Elser elser) : arg_(arg), elser_(elser) {} |
+ |
+ Arg<T> arg_; |
+ Elser elser_; |
+ |
+ template <typename U> |
+ friend Caser<U> Switch(const Arg<U>&); |
+ DISALLOW_ASSIGN(Caser); |
+}; |
+ |
+// Recommended usage is to put |
+// #define CASES SANDBOX_BPF_DSL_CASES |
+// near the top of the .cc file (e.g., nearby any "using" statements), then |
+// use like: |
+// Switch(arg).CASES((3, 5, 7), result)...; |
+#define SANDBOX_BPF_DSL_CASES(values, result) \ |
+ Cases(SANDBOX_BPF_DSL_CASES_HELPER values, result) |
+ |
+// Helper macro to construct a std::vector from an initializer list. |
+// TODO(mdempsky): Convert to use C++11 initializer lists instead. |
+#define SANDBOX_BPF_DSL_CASES_HELPER(value, ...) \ |
+ ({ \ |
+ const __typeof__(value) bpf_dsl_cases_values[] = {value, __VA_ARGS__}; \ |
+ std::vector<__typeof__(value)>( \ |
+ bpf_dsl_cases_values, \ |
+ bpf_dsl_cases_values + arraysize(bpf_dsl_cases_values)); \ |
+ }) |
+ |
// ===================================================================== |
// Official API ends here. |
// ===================================================================== |
@@ -261,6 +322,36 @@ BoolExpr Arg<T>::EqualTo(T val) const { |
return internal::ArgEq(num_, sizeof(T), mask_, static_cast<uint64_t>(val)); |
} |
+template <typename T> |
+SANDBOX_EXPORT Caser<T> Switch(const Arg<T>& arg) { |
+ return Caser<T>(arg, Elser(Cons<Elser::Clause>::List())); |
+} |
+ |
+template <typename T> |
+Caser<T> Caser<T>::Case(T value, ResultExpr result) const { |
+ return SANDBOX_BPF_DSL_CASES((value), result); |
+} |
+ |
+template <typename T> |
+Caser<T> Caser<T>::Cases(const std::vector<T>& values, |
+ ResultExpr result) const { |
+ // Theoretically we could evaluate arg_ just once and emit a more efficient |
+ // dispatch table, but for now we simply translate into an equivalent |
+ // If/ElseIf/Else chain. |
+ |
+ typedef typename std::vector<T>::const_iterator Iter; |
+ BoolExpr test = BoolConst(false); |
+ for (Iter i = values.begin(), end = values.end(); i != end; ++i) { |
+ test = test || (arg_ == *i); |
+ } |
+ return Caser<T>(arg_, elser_.ElseIf(test, result)); |
+} |
+ |
+template <typename T> |
+ResultExpr Caser<T>::Default(ResultExpr result) const { |
+ return elser_.Else(result); |
+} |
+ |
} // namespace bpf_dsl |
} // namespace sandbox |