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

Side by Side Diff: service/datastore/serialize/serialize.go

Issue 1550903002: impl/memory: Fix time serialization encoding. (Closed) Base URL: https://github.com/luci/gae@master
Patch Set: Move logic to serialize, add blobstore.Key support. Created 4 years, 11 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 unified diff | Download patch
OLDNEW
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 serialize 5 package serialize
6 6
7 import ( 7 import (
8 "bytes" 8 "bytes"
9 "errors" 9 "errors"
10 "fmt" 10 "fmt"
(...skipping 171 matching lines...) Expand 10 before | Expand all | Expand 10 after
182 182
183 gp.Lng, _, e = cmpbin.ReadFloat64(buf) 183 gp.Lng, _, e = cmpbin.ReadFloat64(buf)
184 panicIf(e) 184 panicIf(e)
185 185
186 if !gp.Valid() { 186 if !gp.Valid() {
187 err = fmt.Errorf("helper: decoded invalid GeoPoint: %v", gp) 187 err = fmt.Errorf("helper: decoded invalid GeoPoint: %v", gp)
188 } 188 }
189 return 189 return
190 } 190 }
191 191
192 // WriteTime writes a time.Time in a byte-sortable way. 192 // timeToInt converts a time value to a datastore-appropraite integer value.
193 // 193 //
194 // This method truncates the time to microseconds and drops the timezone, 194 // This method truncates the time to microseconds and drops the timezone,
195 // because that's the (undocumented) way that the appengine SDK does it. 195 // because that's the (undocumented) way that the appengine SDK does it.
196 func timeToInt(t time.Time) int64 {
197 t = ds.RoundTime(t)
198 return t.Unix()*1e6 + int64(t.Nanosecond()/1e3)
199 }
200
201 // WriteTime writes a time to the buffer.
202 //
203 // The supplied time is rounded via datastore.RoundTime and written as a
204 // microseconds-since-epoch integer to comform to datastore storage standards.
196 func WriteTime(buf Buffer, t time.Time) error { 205 func WriteTime(buf Buffer, t time.Time) error {
197 name, off := t.Zone() 206 name, off := t.Zone()
198 if name != "UTC" || off != 0 { 207 if name != "UTC" || off != 0 {
199 panic(fmt.Errorf("helper: UTC OR DEATH: %s", t)) 208 panic(fmt.Errorf("helper: UTC OR DEATH: %s", t))
200 } 209 }
201 » _, err := cmpbin.WriteInt(buf, t.Unix()*1e6+int64(t.Nanosecond()/1e3)) 210
211 » _, err := cmpbin.WriteInt(buf, timeToInt(t))
202 return err 212 return err
203 } 213 }
204 214
205 // ReadTime reads a time.Time from the buffer. 215 // ReadTime reads a time.Time from the buffer.
206 func ReadTime(buf Buffer) (time.Time, error) { 216 func ReadTime(buf Buffer) (time.Time, error) {
207 v, _, err := cmpbin.ReadInt(buf) 217 v, _, err := cmpbin.ReadInt(buf)
208 if err != nil { 218 if err != nil {
209 return time.Time{}, err 219 return time.Time{}, err
210 } 220 }
221
211 t := time.Unix(v/1e6, (v%1e6)*1e3) 222 t := time.Unix(v/1e6, (v%1e6)*1e3)
212 if t.IsZero() { 223 if t.IsZero() {
213 return time.Time{}, nil 224 return time.Time{}, nil
214 } 225 }
215 return t.UTC(), nil 226 return t.UTC(), nil
216 } 227 }
217 228
218 // WriteProperty writes a Property to the buffer. `context` behaves the same 229 // WriteProperty writes a Property to the buffer. `context` behaves the same
219 // way that it does for WriteKey, but only has an effect if `p` contains a 230 // way that it does for WriteKey, but only has an effect if `p` contains a
220 // Key as its Value. 231 // Key as its Value.
221 func WriteProperty(buf Buffer, context KeyContext, p ds.Property) (err error) { 232 func WriteProperty(buf Buffer, context KeyContext, p ds.Property) (err error) {
222 defer recoverTo(&err) 233 defer recoverTo(&err)
223 typb := byte(p.Type()) 234 typb := byte(p.Type())
224 if p.IndexSetting() != ds.NoIndex { 235 if p.IndexSetting() != ds.NoIndex {
225 typb |= 0x80 236 typb |= 0x80
226 } 237 }
227 panicIf(buf.WriteByte(typb)) 238 panicIf(buf.WriteByte(typb))
228 » switch p.Type() { 239
240 » err = writeRawProperty(buf, context, p.Type(), p.Value())
241 » return
242 }
243
244 func writeRawProperty(buf Buffer, context KeyContext, t ds.PropertyType, val int erface{}) (err error) {
245 » t, val = RawType(t, val)
246 » switch t {
229 case ds.PTNull: 247 case ds.PTNull:
230 case ds.PTBool: 248 case ds.PTBool:
231 » » b := p.Value().(bool) 249 » » b := val.(bool)
232 if b { 250 if b {
233 err = buf.WriteByte(1) 251 err = buf.WriteByte(1)
234 } else { 252 } else {
235 err = buf.WriteByte(0) 253 err = buf.WriteByte(0)
236 } 254 }
237 case ds.PTInt: 255 case ds.PTInt:
238 » » _, err = cmpbin.WriteInt(buf, p.Value().(int64)) 256 » » _, err = cmpbin.WriteInt(buf, val.(int64))
239 case ds.PTFloat: 257 case ds.PTFloat:
240 » » _, err = cmpbin.WriteFloat64(buf, p.Value().(float64)) 258 » » _, err = cmpbin.WriteFloat64(buf, val.(float64))
241 case ds.PTString: 259 case ds.PTString:
242 » » _, err = cmpbin.WriteString(buf, p.Value().(string)) 260 » » _, err = cmpbin.WriteString(buf, val.(string))
243 case ds.PTBytes: 261 case ds.PTBytes:
244 » » _, err = cmpbin.WriteBytes(buf, p.Value().([]byte)) 262 » » _, err = cmpbin.WriteBytes(buf, val.([]byte))
245 » case ds.PTTime:
246 » » err = WriteTime(buf, p.Value().(time.Time))
247 case ds.PTGeoPoint: 263 case ds.PTGeoPoint:
248 » » err = WriteGeoPoint(buf, p.Value().(ds.GeoPoint)) 264 » » err = WriteGeoPoint(buf, val.(ds.GeoPoint))
249 case ds.PTKey: 265 case ds.PTKey:
250 » » err = WriteKey(buf, context, p.Value().(*ds.Key)) 266 » » err = WriteKey(buf, context, val.(*ds.Key))
251 » case ds.PTBlobKey: 267
252 » » _, err = cmpbin.WriteString(buf, string(p.Value().(blobstore.Key ))) 268 » default:
269 » » err = fmt.Errorf("unsupported type: %v", t)
253 } 270 }
254 return 271 return
255 } 272 }
256 273
274 // RawProperty converts a decomposed Property into a Property that matches its
275 // primitive datastore storage format.
276 //
277 // See RawType for more information.
278 func RawProperty(p ds.Property) ds.Property {
279 _, v := RawType(p.Type(), p.Value())
280
281 ret := ds.Property{}
282 ret.SetValue(v, p.IndexSetting())
283 return ret
284 }
285
286 // RawType converts a decomposed Property into fields that represent its
287 // primitive datastore storage format.
288 //
289 // The following conversions are in place:
290 // - PTTime is converted to an microseconds-from-epoch integer value.
291 // - PTBlobKey is converted to a string.
iannucci 2015/12/29 21:41:48 and PTBytes too?
292 func RawType(t ds.PropertyType, v interface{}) (ds.PropertyType, interface{}) {
293 switch t {
294 case ds.PTTime:
295 return ds.PTInt, timeToInt(v.(time.Time))
296 case ds.PTBlobKey:
297 return ds.PTString, string(v.(blobstore.Key))
298 default:
299 return t, v
300 }
301 }
302
257 // ReadProperty reads a Property from the buffer. `context`, `appid`, and 303 // ReadProperty reads a Property from the buffer. `context`, `appid`, and
258 // `namespace` behave the same way they do for ReadKey, but only have an 304 // `namespace` behave the same way they do for ReadKey, but only have an
259 // effect if the decoded property has a Key value. 305 // effect if the decoded property has a Key value.
260 func ReadProperty(buf Buffer, context KeyContext, appid, namespace string) (p ds .Property, err error) { 306 func ReadProperty(buf Buffer, context KeyContext, appid, namespace string) (p ds .Property, err error) {
261 val := interface{}(nil) 307 val := interface{}(nil)
262 b, err := buf.ReadByte() 308 b, err := buf.ReadByte()
263 if err != nil { 309 if err != nil {
264 return 310 return
265 } 311 }
266 is := ds.ShouldIndex 312 is := ds.ShouldIndex
(...skipping 230 matching lines...) Expand 10 before | Expand all | Expand 10 after
497 for k, vals := range pm { 543 for k, vals := range pm {
498 newVals := PropertySlice(vals) 544 newVals := PropertySlice(vals)
499 if len(newVals) > 0 { 545 if len(newVals) > 0 {
500 ret[k] = newVals 546 ret[k] = newVals
501 } 547 }
502 } 548 }
503 return 549 return
504 } 550 }
505 551
506 func toBytesErr(i interface{}, ctx KeyContext) (ret []byte, err error) { 552 func toBytesErr(i interface{}, ctx KeyContext) (ret []byte, err error) {
507 » buf := &bytes.Buffer{} 553 » buf := bytes.Buffer{}
508 » switch x := i.(type) {
509 » case ds.GeoPoint:
510 » » err = WriteGeoPoint(buf, x)
511 554
555 switch t := i.(type) {
512 case ds.IndexColumn: 556 case ds.IndexColumn:
513 » » err = WriteIndexColumn(buf, x) 557 » » err = WriteIndexColumn(&buf, t)
514 558
515 case ds.IndexDefinition: 559 case ds.IndexDefinition:
516 » » err = WriteIndexDefinition(buf, x) 560 » » err = WriteIndexDefinition(&buf, t)
517
518 » case *ds.Key:
519 » » err = WriteKey(buf, ctx, x)
520 561
521 case ds.KeyTok: 562 case ds.KeyTok:
522 » » err = WriteKeyTok(buf, x) 563 » » err = WriteKeyTok(&buf, t)
523 564
524 case ds.Property: 565 case ds.Property:
525 » » err = WriteProperty(buf, ctx, x) 566 » » err = WriteProperty(&buf, ctx, t)
526 567
527 case ds.PropertyMap: 568 case ds.PropertyMap:
528 » » err = WritePropertyMap(buf, ctx, x) 569 » » err = WritePropertyMap(&buf, ctx, t)
529
530 » case time.Time:
531 » » err = WriteTime(buf, x)
532 570
533 default: 571 default:
534 » » err = fmt.Errorf("unknown type for ToBytes: %T", i) 572 » » var pt ds.PropertyType
573 » » pt, err = ds.PropertyTypeOf(i, false)
574 » » if err == nil {
575 » » » err = writeRawProperty(&buf, ctx, pt, t)
576 » » }
535 } 577 }
578
536 if err == nil { 579 if err == nil {
537 ret = buf.Bytes() 580 ret = buf.Bytes()
538 } 581 }
539 return 582 return
540 } 583 }
541 584
542 // ToBytesErr serializes i to a byte slice, if it's one of the type supported 585 // ToBytesErr serializes i to a byte slice, if it's one of the type supported
543 // by this library, otherwise it returns an error. 586 // by this library, otherwise it returns an error.
544 // 587 //
545 // Key types will be serialized using the 'WithoutContext' option (e.g. their 588 // Key types will be serialized using the 'WithoutContext' option (e.g. their
(...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after
593 } 636 }
594 } 637 }
595 638
596 func recoverTo(err *error) { 639 func recoverTo(err *error) {
597 if r := recover(); r != nil { 640 if r := recover(); r != nil {
598 if rerr := r.(parseError); rerr != nil { 641 if rerr := r.(parseError); rerr != nil {
599 *err = error(rerr) 642 *err = error(rerr)
600 } 643 }
601 } 644 }
602 } 645 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698