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 }