Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(275)

Unified Diff: service/datastore/types.go

Issue 2011773002: datastore: variadic Get, Put, Exists, Delete. (Closed) Base URL: https://chromium.googlesource.com/external/github.com/luci/gae@master
Patch Set: Update documentation and fix/clarify behavior on ExistsResult. Created 4 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
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
iannucci 2016/05/28 02:50:53 bitfield?
dnj 2016/05/28 17:47:21 IMO BoolList is more Go-friendly, since it actuall
+}
+
+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")
+ }
+}

Powered by Google App Engine
This is Rietveld 408576698