go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/gae/service/datastore/types.go (about)

     1  // Copyright 2015 The LUCI Authors.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //      http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  //go:generate stringer -type=Toggle
    16  
    17  package datastore
    18  
    19  // GeoPoint represents a location as latitude/longitude in degrees.
    20  //
    21  // You probably shouldn't use these, but their inclusion here is so that the
    22  // datastore service can interact (and round-trip) correctly with other
    23  // datastore API implementations.
    24  type GeoPoint struct {
    25  	Lat, Lng float64
    26  }
    27  
    28  // Valid returns whether a GeoPoint is within [-90, 90] latitude and [-180,
    29  // 180] longitude.
    30  func (g GeoPoint) Valid() bool {
    31  	return -90 <= g.Lat && g.Lat <= 90 && -180 <= g.Lng && g.Lng <= 180
    32  }
    33  
    34  // TransactionOptions are the options for running a transaction.
    35  type TransactionOptions struct {
    36  	// Attempts controls the number of retries to perform when commits fail
    37  	// due to a conflicting transaction. If omitted, it defaults to 3.
    38  	Attempts int
    39  	// ReadOnly controls whether the transaction is a read only transaction.
    40  	// Read only transactions are potentially more efficient.
    41  	ReadOnly bool
    42  }
    43  
    44  // Toggle is a tri-state boolean (Auto/True/False), which allows structs
    45  // to control boolean flags for metadata in a non-ambiguous way.
    46  type Toggle byte
    47  
    48  // These are the allowed values for Toggle. Any other values are invalid.
    49  const (
    50  	Auto Toggle = iota
    51  	On
    52  	Off
    53  )
    54  
    55  // BoolList is a convenience wrapper for []bool that provides summary methods
    56  // for working with the list in aggregate.
    57  type BoolList []bool
    58  
    59  // All returns true iff all of the booleans in this list are true.
    60  func (bl BoolList) All() bool {
    61  	for _, b := range bl {
    62  		if !b {
    63  			return false
    64  		}
    65  	}
    66  	return true
    67  }
    68  
    69  // Any returns true iff any of the booleans in this list are true.
    70  func (bl BoolList) Any() bool {
    71  	for _, b := range bl {
    72  		if b {
    73  			return true
    74  		}
    75  	}
    76  	return false
    77  }
    78  
    79  // ExistsResult is a 2-dimensional boolean array that represents the existence
    80  // of entries in the datastore. It is returned by the datastore Exists method.
    81  // It is designed to accommodate the potentially-nested variadic arguments that
    82  // can be passed to Exists.
    83  //
    84  // The first dimension contains one entry for each Exists input index. If the
    85  // argument is a single entry, the boolean value at this index will be true if
    86  // that argument was present in the datastore and false otherwise. If the
    87  // argument is a slice, it will contain an aggregate value that is true iff no
    88  // values in that slice were missing from the datastore.
    89  //
    90  // The second dimension presents a boolean slice for each input argument. Single
    91  // arguments will have a slice of size 1 whose value corresponds to the first
    92  // dimension value for that argument. Slice arguments have a slice of the same
    93  // size. A given index in the second dimension slice is true iff the element at
    94  // that index was present.
    95  type ExistsResult struct {
    96  	// values is the first dimension aggregate values.
    97  	values BoolList
    98  	// slices is the set of second dimension positional values.
    99  	slices []BoolList
   100  }
   101  
   102  func (r *ExistsResult) init(sizes ...int) {
   103  	// In order to reduce allocations, we allocate a single continuous boolean
   104  	// slice and then partition it up into first- and second-dimension slices.
   105  	//
   106  	// To determine the size of the continuous slize, count the number of elements
   107  	// that we'll need:
   108  	// - Single arguments and slice arguments with size 1 will have their
   109  	//   second-dimension slice just point to their element in the first-dimension
   110  	//   slice.
   111  	// - Slice elements of size >1 will have their second-dimension slice added to
   112  	//   the end of the continuous slice. Their slice will be carved off in the
   113  	//   subsequent loop.
   114  	//
   115  	// Consequently, we need one element for each argument, plus len(slice)
   116  	// additional elements for each slice argument of size >1.
   117  	count := len(sizes) // [0..n)
   118  	for _, s := range sizes {
   119  		if s > 1 {
   120  			count += s
   121  		}
   122  	}
   123  
   124  	// Allocate our continuous array and partition it into first- and
   125  	// second-dimension slices.
   126  	entries := make(BoolList, count)
   127  	r.values, entries = entries[:len(sizes)], entries[len(sizes):]
   128  	r.slices = make([]BoolList, len(sizes))
   129  	for i, s := range sizes {
   130  		switch {
   131  		case s <= 0:
   132  			break
   133  
   134  		case s == 1:
   135  			// Single-entry slice out of "entries".
   136  			r.slices[i] = r.values[i : i+1]
   137  
   138  		default:
   139  			r.slices[i], entries = entries[:s], entries[s:]
   140  		}
   141  	}
   142  }
   143  
   144  func (r *ExistsResult) set(i, j int) { r.slices[i][j] = true }
   145  
   146  // updateSlices updates the top-level value for multi-dimensional elements based
   147  // on their current values.
   148  func (r *ExistsResult) updateSlices() {
   149  	for i, s := range r.slices {
   150  		// Zero-length slices will have a first-dimension true value, since they
   151  		// have no entries and first-dimension is true when there are no false
   152  		// entries.
   153  		r.values[i] = (len(s) == 0 || s.All())
   154  	}
   155  }
   156  
   157  // All returns true if all of the available boolean slots are true.
   158  func (r *ExistsResult) All() bool { return r.values.All() }
   159  
   160  // Any returns true if any of the boolean slots are true.
   161  func (r *ExistsResult) Any() bool {
   162  	// We have to implement our own Any so zero-length slices don't count towards
   163  	// our result.
   164  	for i, b := range r.values {
   165  		if b && len(r.slices[i]) > 0 {
   166  			return true
   167  		}
   168  	}
   169  	return false
   170  }
   171  
   172  // Get returns the boolean value at the specified index.
   173  //
   174  // The one-argument form returns the first-dimension boolean. If i is a slice
   175  // argument, this will be true iff all of the slice's booleans are true.
   176  //
   177  // An optional second argument can be passed to access a specific boolean value
   178  // in slice i. If the argument at i is a single argument, the only valid index,
   179  // 0, will be the same as calling the single-argument Get.
   180  //
   181  // Passing more than one additional argument will result in a panic.
   182  func (r *ExistsResult) Get(i int, j ...int) bool {
   183  	switch len(j) {
   184  	case 0:
   185  		return r.values[i]
   186  	case 1:
   187  		return r.slices[i][j[0]]
   188  	default:
   189  		panic("this method takes one or two arguments")
   190  	}
   191  }
   192  
   193  // List returns the BoolList for the given argument index.
   194  //
   195  // The zero-argument form returns the first-dimension boolean list.
   196  //
   197  // An optional argument can be passed to access a specific argument's boolean
   198  // slice. If the argument at i is a non-slice argument, the list will be a slice
   199  // of size 1 containing i's first-dimension value.
   200  //
   201  // Passing more than one argument will result in a panic.
   202  func (r *ExistsResult) List(i ...int) BoolList {
   203  	switch len(i) {
   204  	case 0:
   205  		return r.values
   206  	case 1:
   207  		return r.slices[i[0]]
   208  	default:
   209  		panic("this method takes zero or one arguments")
   210  	}
   211  }
   212  
   213  // Len returns the number of boolean results available.
   214  //
   215  // The zero-argument form returns the first-dimension size, which will equal the
   216  // total number of arguments passed to Exists.
   217  //
   218  // The one-argument form returns the number of booleans in the slice for
   219  // argument i.
   220  //
   221  // Passing more than one argument will result in a panic.
   222  func (r *ExistsResult) Len(i ...int) int {
   223  	switch len(i) {
   224  	case 0:
   225  		return len(r.values)
   226  	case 1:
   227  		return len(r.slices[i[0]])
   228  	default:
   229  		panic("this method takes zero or one arguments")
   230  	}
   231  }