Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2015 The Chromium Authors. All rights reserved. | 1 // Copyright 2015 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 //go:generate stringer -type=Toggle | 5 //go:generate stringer -type=Toggle |
| 6 | 6 |
| 7 package datastore | 7 package datastore |
| 8 | 8 |
| 9 // GeoPoint represents a location as latitude/longitude in degrees. | 9 // GeoPoint represents a location as latitude/longitude in degrees. |
| 10 // | 10 // |
| (...skipping 29 matching lines...) Expand all Loading... | |
| 40 // Toggle is a tri-state boolean (Auto/True/False), which allows structs | 40 // Toggle is a tri-state boolean (Auto/True/False), which allows structs |
| 41 // to control boolean flags for metadata in a non-ambiguous way. | 41 // to control boolean flags for metadata in a non-ambiguous way. |
| 42 type Toggle byte | 42 type Toggle byte |
| 43 | 43 |
| 44 // These are the allowed values for Toggle. Any other values are invalid. | 44 // These are the allowed values for Toggle. Any other values are invalid. |
| 45 const ( | 45 const ( |
| 46 Auto Toggle = iota | 46 Auto Toggle = iota |
| 47 On | 47 On |
| 48 Off | 48 Off |
| 49 ) | 49 ) |
| 50 | |
| 51 // BoolList is a convenience wrapper for []bool that provides summary methods | |
| 52 // for working with the list in aggregate. | |
| 53 type BoolList []bool | |
| 54 | |
| 55 // All returns true iff all of the booleans in this list are true. | |
| 56 func (bl BoolList) All() bool { | |
| 57 for _, b := range bl { | |
| 58 if !b { | |
| 59 return false | |
| 60 } | |
| 61 } | |
| 62 return true | |
| 63 } | |
| 64 | |
| 65 // Any returns true iff any of the booleans in this list are true. | |
| 66 func (bl BoolList) Any() bool { | |
| 67 for _, b := range bl { | |
| 68 if b { | |
| 69 return true | |
| 70 } | |
| 71 } | |
| 72 return false | |
| 73 } | |
| 74 | |
| 75 // ExistsResult is a 2-dimensional boolean array that represents the existence | |
| 76 // of entries in the datastore. It is returned by the datastore Exists method. | |
| 77 // It is designed to accommodate the potentially-nested variadic arguments that | |
| 78 // can be passed to Exists. | |
| 79 // | |
| 80 // The first dimension contains one entry for each Exists input index. If the | |
| 81 // argument is a single entry, the boolean value at this index will be true if | |
| 82 // that argument was present in the datastore and false otherwise. If the | |
| 83 // argument is a slice, it will contain an aggregate value that is true iff no | |
| 84 // values in that slice were missing from the datastore. | |
| 85 // | |
| 86 // The second dimension presents a boolean slice for each input argument. Single | |
| 87 // arguments will have a slice of size 1 whose value corresponds to the first | |
| 88 // dimension value for that argument. Slice arguments have a slice of the same | |
| 89 // size. A given index in the second dimension slice is true iff the element at | |
| 90 // that index was present. | |
| 91 type ExistsResult struct { | |
| 92 // values is the first dimension aggregate values. | |
| 93 values BoolList | |
| 94 // slices is the set of second dimension positional values. | |
| 95 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
| |
| 96 } | |
| 97 | |
| 98 func (r *ExistsResult) init(sizes ...int) { | |
| 99 // In order to reduce allocations, we allocate a single continuous boole an | |
| 100 // slice and then partition it up into first- and second-dimension slice s. | |
| 101 // | |
| 102 // To determine the size of the continuous slize, count the number of el ements | |
| 103 // that we'll need: | |
| 104 // - Single arguments and slice arguments with size 1 will have their | |
| 105 // second-dimension slice just point to their element in the first-dim ension | |
| 106 // slice. | |
| 107 // - Slice elements of size >1 will have their second-dimension slice ad ded to | |
| 108 // the end of the continuous slice. Their slice will be carved off in the | |
| 109 // subsequent loop. | |
| 110 // | |
| 111 // Consequently, we need one element for each argument, plus len(slice) | |
| 112 // additional elements for each slice argument of size >1. | |
| 113 count := len(sizes) // [0..n) | |
| 114 for _, s := range sizes { | |
| 115 if s > 1 { | |
| 116 count += s | |
| 117 } | |
| 118 } | |
| 119 | |
| 120 // Allocate our continuous array and partition it into first- and | |
| 121 // second-dimension slices. | |
| 122 entries := make(BoolList, count) | |
| 123 r.values, entries = entries[:len(sizes)], entries[len(sizes):] | |
| 124 r.slices = make([]BoolList, len(sizes)) | |
| 125 for i, s := range sizes { | |
| 126 switch { | |
| 127 case s <= 0: | |
| 128 break | |
| 129 | |
| 130 case s == 1: | |
| 131 // Single-entry slice out of "entries". | |
| 132 r.slices[i] = r.values[i : i+1] | |
| 133 | |
| 134 default: | |
| 135 r.slices[i], entries = entries[:s], entries[s:] | |
| 136 } | |
| 137 } | |
| 138 } | |
| 139 | |
| 140 func (r *ExistsResult) set(i, j int) { r.slices[i][j] = true } | |
| 141 | |
| 142 // updateSlices updates the top-level value for multi-dimensional elements based | |
| 143 // on their current values. | |
| 144 func (r *ExistsResult) updateSlices() { | |
| 145 for i, s := range r.slices { | |
| 146 // Zero-length slices will have a first-dimension true value, si nce they | |
| 147 // have no entries and first-dimension is true when there are no false | |
| 148 // entries. | |
| 149 r.values[i] = (len(s) == 0 || s.All()) | |
| 150 } | |
| 151 } | |
| 152 | |
| 153 // All returns true if all of the available boolean slots are true. | |
| 154 func (r *ExistsResult) All() bool { return r.values.All() } | |
| 155 | |
| 156 // Any returns true if any of the boolean slots are true. | |
| 157 func (r *ExistsResult) Any() bool { | |
| 158 // We have to implement our own Any so zero-length slices don't count to wards | |
| 159 // our result. | |
| 160 for i, b := range r.values { | |
| 161 if b && len(r.slices[i]) > 0 { | |
| 162 return true | |
| 163 } | |
| 164 } | |
| 165 return false | |
| 166 } | |
| 167 | |
| 168 // Get returns the boolean value at the specified index. | |
| 169 // | |
| 170 // The one-argument form returns the first-dimension boolean. If i is a slice | |
| 171 // argument, this will be true iff all of the slice's booleans are true. | |
| 172 // | |
| 173 // An optional second argument can be passed to access a specific boolean value | |
| 174 // in slice i. If the argument at i is a single argument, the only valid index, | |
| 175 // 0, will be the same as calling the single-argument Get. | |
| 176 // | |
| 177 // Passing more than one additional argument will result in a panic. | |
| 178 func (r *ExistsResult) Get(i int, j ...int) bool { | |
| 179 switch len(j) { | |
| 180 case 0: | |
| 181 return r.values[i] | |
| 182 case 1: | |
| 183 return r.slices[i][j[0]] | |
| 184 default: | |
| 185 panic("this method takes one or two arguments") | |
| 186 } | |
| 187 } | |
| 188 | |
| 189 // List returns the BoolList for the given argument index. | |
| 190 // | |
| 191 // The zero-argument form returns the first-dimension boolean list. | |
| 192 // | |
| 193 // An optional argument can be passed to access a specific argument's boolean | |
| 194 // slice. If the argument at i is a non-slice argument, the list will be a slice | |
| 195 // of size 1 containing i's first-dimension value. | |
| 196 // | |
| 197 // Passing more than one argument will result in a panic. | |
| 198 func (r *ExistsResult) List(i ...int) BoolList { | |
| 199 switch len(i) { | |
| 200 case 0: | |
| 201 return r.values | |
| 202 case 1: | |
| 203 return r.slices[i[0]] | |
| 204 default: | |
| 205 panic("this method takes zero or one arguments") | |
| 206 } | |
| 207 } | |
| 208 | |
| 209 // Len returns the number of boolean results available. | |
| 210 // | |
| 211 // The zero-argument form returns the first-dimension size, which will equal the | |
| 212 // total number of arguments passed to Exists. | |
| 213 // | |
| 214 // The one-argument form returns the number of booleans in the slice for | |
| 215 // argument i. | |
| 216 // | |
| 217 // Passing more than one argument will result in a panic. | |
| 218 func (r *ExistsResult) Len(i ...int) int { | |
| 219 switch len(i) { | |
| 220 case 0: | |
| 221 return len(r.values) | |
| 222 case 1: | |
| 223 return len(r.slices[i[0]]) | |
| 224 default: | |
| 225 panic("this method takes zero or one arguments") | |
| 226 } | |
| 227 } | |
| OLD | NEW |