Index: service/datastore/types.go |
diff --git a/service/datastore/types.go b/service/datastore/types.go |
index 94780f8408ba1cdb889836df6ca330d24d754559..ffc54a9c763a39c710883750d9cc28e122a58f01 100644 |
--- a/service/datastore/types.go |
+++ b/service/datastore/types.go |
@@ -47,3 +47,181 @@ const ( |
On |
Off |
) |
+ |
+// BoolList is a convenience wrapper for []bool that provides summary methods |
+// for working with the list in aggregate. |
+type BoolList []bool |
+ |
+// All returns true iff all of the booleans in this list are true. |
+func (bl BoolList) All() bool { |
+ for _, b := range bl { |
+ if !b { |
+ return false |
+ } |
+ } |
+ return true |
+} |
+ |
+// Any returns true iff any of the booleans in this list are true. |
+func (bl BoolList) Any() bool { |
+ for _, b := range bl { |
+ if b { |
+ return true |
+ } |
+ } |
+ return false |
+} |
+ |
+// ExistsResult is a 2-dimensional boolean array that represents the existence |
+// of entries in the datastore. It is returned by the datastore Exists method. |
+// It is designed to accommodate the potentially-nested variadic arguments that |
+// can be passed to Exists. |
+// |
+// The first dimension contains one entry for each Exists input index. If the |
+// argument is a single entry, the boolean value at this index will be true if |
+// that argument was present in the datastore and false otherwise. If the |
+// argument is a slice, it will contain an aggregate value that is true iff no |
+// values in that slice were missing from the datastore. |
+// |
+// The second dimension presents a boolean slice for each input argument. Single |
+// arguments will have a slice of size 1 whose value corresponds to the first |
+// dimension value for that argument. Slice arguments have a slice of the same |
+// size. A given index in the second dimension slice is true iff the element at |
+// that index was present. |
+type ExistsResult struct { |
+ // values is the first dimension aggregate values. |
+ values BoolList |
+ // slices is the set of second dimension positional values. |
+ slices []BoolList |
+} |
+ |
+func (r *ExistsResult) init(sizes ...int) { |
+ // In order to reduce allocations, we allocate a single continuous boolean |
+ // slice and then partition it up into first- and second-dimension slices. |
+ // |
+ // To determine the size of the continuous slize, count the number of elements |
+ // that we'll need: |
+ // - Single arguments and slice arguments with size 1 will have their |
+ // second-dimension slice just point to their element in the first-dimension |
+ // slice. |
+ // - Slice elements of size >1 will have their second-dimension slice added to |
+ // the end of the continuous slice. Their slice will be carved off in the |
+ // subsequent loop. |
+ // |
+ // Consequently, we need one element for each argument, plus len(slice) |
+ // additional elements for each slice argument of size >1. |
+ count := len(sizes) // [0..n) |
+ for _, s := range sizes { |
+ if s > 1 { |
+ count += s |
+ } |
+ } |
+ |
+ // Allocate our continuous array and partition it into first- and |
+ // second-dimension slices. |
+ entries := make(BoolList, count) |
+ r.values, entries = entries[:len(sizes)], entries[len(sizes):] |
+ r.slices = make([]BoolList, len(sizes)) |
+ for i, s := range sizes { |
+ switch { |
+ case s <= 0: |
+ break |
+ |
+ case s == 1: |
+ // Single-entry slice out of "entries". |
+ r.slices[i] = r.values[i : i+1] |
+ |
+ default: |
+ r.slices[i], entries = entries[:s], entries[s:] |
+ } |
+ } |
+} |
+ |
+func (r *ExistsResult) set(i, j int) { r.slices[i][j] = true } |
+ |
+// updateSlices updates the top-level value for multi-dimensional elements based |
+// on their current values. |
+func (r *ExistsResult) updateSlices() { |
+ for i, s := range r.slices { |
+ // Zero-length slices will have a first-dimension true value, since they |
+ // have no entries and first-dimension is true when there are no false |
+ // entries. |
+ r.values[i] = (len(s) == 0 || s.All()) |
+ } |
+} |
+ |
+// All returns true if all of the available boolean slots are true. |
+func (r *ExistsResult) All() bool { return r.values.All() } |
+ |
+// Any returns true if any of the boolean slots are true. |
+func (r *ExistsResult) Any() bool { |
+ // We have to implement our own Any so zero-length slices don't count towards |
+ // our result. |
+ for i, b := range r.values { |
+ if b && len(r.slices[i]) > 0 { |
+ return true |
+ } |
+ } |
+ return false |
+} |
+ |
+// Get returns the boolean value at the specified index. |
+// |
+// The one-argument form returns the first-dimension boolean. If i is a slice |
+// argument, this will be true iff all of the slice's booleans are true. |
+// |
+// An optional second argument can be passed to access a specific boolean value |
+// in slice i. If the argument at i is a single argument, the only valid index, |
+// 0, will be the same as calling the single-argument Get. |
+// |
+// Passing more than one additional argument will result in a panic. |
+func (r *ExistsResult) Get(i int, j ...int) bool { |
+ switch len(j) { |
+ case 0: |
+ return r.values[i] |
+ case 1: |
+ return r.slices[i][j[0]] |
+ default: |
+ panic("this method takes one or two arguments") |
+ } |
+} |
+ |
+// List returns the BoolList for the given argument index. |
+// |
+// The zero-argument form returns the first-dimension boolean list. |
+// |
+// An optional argument can be passed to access a specific argument's boolean |
+// slice. If the argument at i is a non-slice argument, the list will be a slice |
+// of size 1 containing i's first-dimension value. |
+// |
+// Passing more than one argument will result in a panic. |
+func (r *ExistsResult) List(i ...int) BoolList { |
+ switch len(i) { |
+ case 0: |
+ return r.values |
+ case 1: |
+ return r.slices[i[0]] |
+ default: |
+ panic("this method takes zero or one arguments") |
+ } |
+} |
+ |
+// Len returns the number of boolean results available. |
+// |
+// The zero-argument form returns the first-dimension size, which will equal the |
+// total number of arguments passed to Exists. |
+// |
+// The one-argument form returns the number of booleans in the slice for |
+// argument i. |
+// |
+// Passing more than one argument will result in a panic. |
+func (r *ExistsResult) Len(i ...int) int { |
+ switch len(i) { |
+ case 0: |
+ return len(r.values) |
+ case 1: |
+ return len(r.slices[i[0]]) |
+ default: |
+ panic("this method takes zero or one arguments") |
+ } |
+} |