| 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")
|
| + }
|
| +}
|
|
|