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 package rawdatastore | 5 package rawdatastore |
6 | 6 |
7 import ( | 7 import ( |
8 "bytes" | 8 "bytes" |
9 "errors" | 9 "errors" |
10 "fmt" | 10 "fmt" |
(...skipping 133 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
144 ret.IntID, _, err = cmpbin.ReadInt(buf) | 144 ret.IntID, _, err = cmpbin.ReadInt(buf) |
145 if err == nil && ret.IntID <= 0 { | 145 if err == nil && ret.IntID <= 0 { |
146 err = errors.New("helper: decoded key with empty stringI
D and zero/negative intID") | 146 err = errors.New("helper: decoded key with empty stringI
D and zero/negative intID") |
147 } | 147 } |
148 default: | 148 default: |
149 err = fmt.Errorf("helper: invalid type %s", PropertyType(typ)) | 149 err = fmt.Errorf("helper: invalid type %s", PropertyType(typ)) |
150 } | 150 } |
151 return | 151 return |
152 } | 152 } |
153 | 153 |
154 // WriteGeoPoint writes a GeoPoint to the buffer. | 154 // Write writes a GeoPoint to the buffer. |
155 func WriteGeoPoint(buf Buffer, gp GeoPoint) (err error) { | 155 func (gp GeoPoint) Write(buf Buffer) (err error) { |
156 defer recoverTo(&err) | 156 defer recoverTo(&err) |
157 _, e := cmpbin.WriteFloat64(buf, gp.Lat) | 157 _, e := cmpbin.WriteFloat64(buf, gp.Lat) |
158 panicIf(e) | 158 panicIf(e) |
159 _, e = cmpbin.WriteFloat64(buf, gp.Lng) | 159 _, e = cmpbin.WriteFloat64(buf, gp.Lng) |
160 return e | 160 return e |
161 } | 161 } |
162 | 162 |
163 // ReadGeoPoint reads a GeoPoint from the buffer. | 163 // Read reads a GeoPoint from the buffer. |
164 func ReadGeoPoint(buf Buffer) (gp GeoPoint, err error) { | 164 func (gp *GeoPoint) Read(buf Buffer) (err error) { |
165 defer recoverTo(&err) | 165 defer recoverTo(&err) |
166 e := error(nil) | 166 e := error(nil) |
167 gp.Lat, _, e = cmpbin.ReadFloat64(buf) | 167 gp.Lat, _, e = cmpbin.ReadFloat64(buf) |
168 panicIf(e) | 168 panicIf(e) |
169 | 169 |
170 gp.Lng, _, e = cmpbin.ReadFloat64(buf) | 170 gp.Lng, _, e = cmpbin.ReadFloat64(buf) |
171 panicIf(e) | 171 panicIf(e) |
172 | 172 |
173 if !gp.Valid() { | 173 if !gp.Valid() { |
174 err = fmt.Errorf("helper: decoded invalid GeoPoint: %v", gp) | 174 err = fmt.Errorf("helper: decoded invalid GeoPoint: %v", gp) |
(...skipping 16 matching lines...) Expand all Loading... |
191 | 191 |
192 // ReadTime reads a time.Time from the buffer. | 192 // ReadTime reads a time.Time from the buffer. |
193 func ReadTime(buf Buffer) (time.Time, error) { | 193 func ReadTime(buf Buffer) (time.Time, error) { |
194 v, _, err := cmpbin.ReadUint(buf) | 194 v, _, err := cmpbin.ReadUint(buf) |
195 if err != nil { | 195 if err != nil { |
196 return time.Time{}, err | 196 return time.Time{}, err |
197 } | 197 } |
198 return time.Unix(int64(v/1e6), int64((v%1e6)*1e3)).UTC(), nil | 198 return time.Unix(int64(v/1e6), int64((v%1e6)*1e3)).UTC(), nil |
199 } | 199 } |
200 | 200 |
201 // WriteProperty writes a Property to the buffer. `context` behaves the same | 201 // Write writes a Property to the buffer. `context` behaves the same |
202 // way that it does for WriteKey, but only has an effect if `p` contains a | 202 // way that it does for WriteKey, but only has an effect if `p` contains a |
203 // Key as its Value. | 203 // Key as its Value. |
204 func WriteProperty(buf Buffer, p Property, context KeyContext) (err error) { | 204 func (p *Property) Write(buf Buffer, context KeyContext) (err error) { |
205 defer recoverTo(&err) | 205 defer recoverTo(&err) |
206 typb := byte(p.Type()) | 206 typb := byte(p.Type()) |
207 if p.IndexSetting() == NoIndex { | 207 if p.IndexSetting() == NoIndex { |
208 typb |= 0x80 | 208 typb |= 0x80 |
209 } | 209 } |
210 panicIf(buf.WriteByte(typb)) | 210 panicIf(buf.WriteByte(typb)) |
211 switch p.Type() { | 211 switch p.Type() { |
212 case PTNull, PTBoolTrue, PTBoolFalse: | 212 case PTNull, PTBoolTrue, PTBoolFalse: |
213 case PTInt: | 213 case PTInt: |
214 _, err = cmpbin.WriteInt(buf, p.Value().(int64)) | 214 _, err = cmpbin.WriteInt(buf, p.Value().(int64)) |
215 case PTFloat: | 215 case PTFloat: |
216 _, err = cmpbin.WriteFloat64(buf, p.Value().(float64)) | 216 _, err = cmpbin.WriteFloat64(buf, p.Value().(float64)) |
217 case PTString: | 217 case PTString: |
218 _, err = cmpbin.WriteString(buf, p.Value().(string)) | 218 _, err = cmpbin.WriteString(buf, p.Value().(string)) |
219 case PTBytes: | 219 case PTBytes: |
220 if p.IndexSetting() == NoIndex { | 220 if p.IndexSetting() == NoIndex { |
221 _, err = cmpbin.WriteBytes(buf, p.Value().([]byte)) | 221 _, err = cmpbin.WriteBytes(buf, p.Value().([]byte)) |
222 } else { | 222 } else { |
223 _, err = cmpbin.WriteBytes(buf, p.Value().(ByteString)) | 223 _, err = cmpbin.WriteBytes(buf, p.Value().(ByteString)) |
224 } | 224 } |
225 case PTTime: | 225 case PTTime: |
226 err = WriteTime(buf, p.Value().(time.Time)) | 226 err = WriteTime(buf, p.Value().(time.Time)) |
227 case PTGeoPoint: | 227 case PTGeoPoint: |
228 » » err = WriteGeoPoint(buf, p.Value().(GeoPoint)) | 228 » » err = p.Value().(GeoPoint).Write(buf) |
229 case PTKey: | 229 case PTKey: |
230 err = WriteKey(buf, context, p.Value().(Key)) | 230 err = WriteKey(buf, context, p.Value().(Key)) |
231 case PTBlobKey: | 231 case PTBlobKey: |
232 _, err = cmpbin.WriteString(buf, string(p.Value().(blobstore.Key
))) | 232 _, err = cmpbin.WriteString(buf, string(p.Value().(blobstore.Key
))) |
233 } | 233 } |
234 return | 234 return |
235 } | 235 } |
236 | 236 |
237 // ReadProperty reads a Property from the buffer. `context`, `appid`, and | 237 // Read reads a Property from the buffer. `context`, `appid`, and |
238 // `namespace` behave the same way they do for ReadKey, but only have an | 238 // `namespace` behave the same way they do for ReadKey, but only have an |
239 // effect if the decoded property has a Key value. | 239 // effect if the decoded property has a Key value. |
240 func ReadProperty(buf Buffer, context KeyContext, appid, namespace string) (p Pr
operty, err error) { | 240 func (p *Property) Read(buf Buffer, context KeyContext, appid, namespace string)
(err error) { |
241 val := interface{}(nil) | 241 val := interface{}(nil) |
242 typb, err := buf.ReadByte() | 242 typb, err := buf.ReadByte() |
243 if err != nil { | 243 if err != nil { |
244 return | 244 return |
245 } | 245 } |
246 is := ShouldIndex | 246 is := ShouldIndex |
247 if (typb & 0x80) != 0 { | 247 if (typb & 0x80) != 0 { |
248 is = NoIndex | 248 is = NoIndex |
249 } | 249 } |
250 switch PropertyType(typb & 0x7f) { | 250 switch PropertyType(typb & 0x7f) { |
(...skipping 14 matching lines...) Expand all Loading... |
265 break | 265 break |
266 } | 266 } |
267 if is == NoIndex { | 267 if is == NoIndex { |
268 val = b | 268 val = b |
269 } else { | 269 } else { |
270 val = ByteString(b) | 270 val = ByteString(b) |
271 } | 271 } |
272 case PTTime: | 272 case PTTime: |
273 val, err = ReadTime(buf) | 273 val, err = ReadTime(buf) |
274 case PTGeoPoint: | 274 case PTGeoPoint: |
275 » » val, err = ReadGeoPoint(buf) | 275 » » gp := GeoPoint{} |
| 276 » » err = gp.Read(buf) |
| 277 » » val = gp |
276 case PTKey: | 278 case PTKey: |
277 val, err = ReadKey(buf, context, appid, namespace) | 279 val, err = ReadKey(buf, context, appid, namespace) |
278 case PTBlobKey: | 280 case PTBlobKey: |
279 s := "" | 281 s := "" |
280 if s, _, err = cmpbin.ReadString(buf); err != nil { | 282 if s, _, err = cmpbin.ReadString(buf); err != nil { |
281 break | 283 break |
282 } | 284 } |
283 val = blobstore.Key(s) | 285 val = blobstore.Key(s) |
284 default: | 286 default: |
285 err = fmt.Errorf("read: unknown type! %v", typb) | 287 err = fmt.Errorf("read: unknown type! %v", typb) |
286 } | 288 } |
287 if err == nil { | 289 if err == nil { |
288 err = p.SetValue(val, is) | 290 err = p.SetValue(val, is) |
289 } | 291 } |
290 return | 292 return |
291 } | 293 } |
292 | 294 |
293 // WritePropertyMap writes an entire PropertyMap to the buffer. `context` | 295 // Write writes an entire PropertyMap to the buffer. `context` |
294 // behaves the same way that it does for WriteKey. If | 296 // behaves the same way that it does for WriteKey. If |
295 // WritePropertyMapDeterministic is true, then the rows will be sorted by | 297 // WritePropertyMapDeterministic is true, then the rows will be sorted by |
296 // property name before they're serialized to buf (mostly useful for testing, | 298 // property name before they're serialized to buf (mostly useful for testing, |
297 // but also potentially useful if you need to make a hash of the property data). | 299 // but also potentially useful if you need to make a hash of the property data). |
298 func WritePropertyMap(buf Buffer, propMap PropertyMap, context KeyContext) (err
error) { | 300 func (pm PropertyMap) Write(buf Buffer, context KeyContext) (err error) { |
299 defer recoverTo(&err) | 301 defer recoverTo(&err) |
300 » rows := make(sort.StringSlice, 0, len(propMap)) | 302 » rows := make(sort.StringSlice, 0, len(pm)) |
301 tmpBuf := &bytes.Buffer{} | 303 tmpBuf := &bytes.Buffer{} |
302 » for name, vals := range propMap { | 304 » for name, vals := range pm { |
303 tmpBuf.Reset() | 305 tmpBuf.Reset() |
304 _, e := cmpbin.WriteString(tmpBuf, name) | 306 _, e := cmpbin.WriteString(tmpBuf, name) |
305 panicIf(e) | 307 panicIf(e) |
306 _, e = cmpbin.WriteUint(tmpBuf, uint64(len(vals))) | 308 _, e = cmpbin.WriteUint(tmpBuf, uint64(len(vals))) |
307 panicIf(e) | 309 panicIf(e) |
308 for _, p := range vals { | 310 for _, p := range vals { |
309 » » » panicIf(WriteProperty(tmpBuf, p, context)) | 311 » » » panicIf(p.Write(tmpBuf, context)) |
310 } | 312 } |
311 rows = append(rows, tmpBuf.String()) | 313 rows = append(rows, tmpBuf.String()) |
312 } | 314 } |
313 | 315 |
314 if WritePropertyMapDeterministic { | 316 if WritePropertyMapDeterministic { |
315 rows.Sort() | 317 rows.Sort() |
316 } | 318 } |
317 | 319 |
318 » _, e := cmpbin.WriteUint(buf, uint64(len(propMap))) | 320 » _, e := cmpbin.WriteUint(buf, uint64(len(pm))) |
319 panicIf(e) | 321 panicIf(e) |
320 for _, r := range rows { | 322 for _, r := range rows { |
321 _, e := buf.WriteString(r) | 323 _, e := buf.WriteString(r) |
322 panicIf(e) | 324 panicIf(e) |
323 } | 325 } |
324 return | 326 return |
325 } | 327 } |
326 | 328 |
327 // ReadPropertyMap reads a PropertyMap from the buffer. `context` and | 329 // Read reads a PropertyMap from the buffer. `context` and |
328 // friends behave the same way that they do for ReadKey. | 330 // friends behave the same way that they do for ReadKey. |
329 func ReadPropertyMap(buf Buffer, context KeyContext, appid, namespace string) (p
ropMap PropertyMap, err error) { | 331 func (pm PropertyMap) Read(buf Buffer, context KeyContext, appid, namespace stri
ng) (err error) { |
330 defer recoverTo(&err) | 332 defer recoverTo(&err) |
331 | 333 |
332 numRows := uint64(0) | 334 numRows := uint64(0) |
333 numRows, _, e := cmpbin.ReadUint(buf) | 335 numRows, _, e := cmpbin.ReadUint(buf) |
334 panicIf(e) | 336 panicIf(e) |
335 if numRows > ReadPropertyMapReasonableLimit { | 337 if numRows > ReadPropertyMapReasonableLimit { |
336 err = fmt.Errorf("helper: tried to decode map with huge number o
f rows %d", numRows) | 338 err = fmt.Errorf("helper: tried to decode map with huge number o
f rows %d", numRows) |
337 return | 339 return |
338 } | 340 } |
339 | 341 |
340 name, prop := "", Property{} | 342 name, prop := "", Property{} |
341 propMap = make(PropertyMap, numRows) | |
342 for i := uint64(0); i < numRows; i++ { | 343 for i := uint64(0); i < numRows; i++ { |
343 name, _, e = cmpbin.ReadString(buf) | 344 name, _, e = cmpbin.ReadString(buf) |
344 panicIf(e) | 345 panicIf(e) |
345 | 346 |
346 numProps, _, e := cmpbin.ReadUint(buf) | 347 numProps, _, e := cmpbin.ReadUint(buf) |
347 panicIf(e) | 348 panicIf(e) |
348 if numProps > ReadPropertyMapReasonableLimit { | 349 if numProps > ReadPropertyMapReasonableLimit { |
349 err = fmt.Errorf("helper: tried to decode map with huge
number of properties %d", numProps) | 350 err = fmt.Errorf("helper: tried to decode map with huge
number of properties %d", numProps) |
350 return | 351 return |
351 } | 352 } |
352 props := make([]Property, 0, numProps) | 353 props := make([]Property, 0, numProps) |
353 for j := uint64(0); j < numProps; j++ { | 354 for j := uint64(0); j < numProps; j++ { |
354 » » » prop, e = ReadProperty(buf, context, appid, namespace) | 355 » » » panicIf(prop.Read(buf, context, appid, namespace)) |
355 » » » panicIf(e) | |
356 props = append(props, prop) | 356 props = append(props, prop) |
357 } | 357 } |
358 » » propMap[name] = props | 358 » » pm[name] = props |
359 } | 359 } |
360 return | 360 return |
361 } | 361 } |
362 | 362 |
363 type parseError error | 363 type parseError error |
364 | 364 |
365 func panicIf(err error) { | 365 func panicIf(err error) { |
366 if err != nil { | 366 if err != nil { |
367 panic(parseError(err)) | 367 panic(parseError(err)) |
368 } | 368 } |
369 } | 369 } |
370 | 370 |
371 func recoverTo(err *error) { | 371 func recoverTo(err *error) { |
372 if r := recover(); r != nil { | 372 if r := recover(); r != nil { |
373 if rerr := r.(parseError); rerr != nil { | 373 if rerr := r.(parseError); rerr != nil { |
374 *err = error(rerr) | 374 *err = error(rerr) |
375 } | 375 } |
376 } | 376 } |
377 } | 377 } |
OLD | NEW |