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

Unified 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 5 years 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 side-by-side diff with in-line comments
Download patch
Index: service/datastore/serialize/serialize.go
diff --git a/service/datastore/serialize/serialize.go b/service/datastore/serialize/serialize.go
index 736010820f222499212728ca30154029a905cc4f..fe8c7502f0cdc2c3adfae4b25211984a37f0174e 100644
--- a/service/datastore/serialize/serialize.go
+++ b/service/datastore/serialize/serialize.go
@@ -189,16 +189,26 @@ func ReadGeoPoint(buf Buffer) (gp ds.GeoPoint, err error) {
return
}
-// WriteTime writes a time.Time in a byte-sortable way.
+// timeToInt converts a time value to a datastore-appropraite integer value.
//
// This method truncates the time to microseconds and drops the timezone,
// because that's the (undocumented) way that the appengine SDK does it.
+func timeToInt(t time.Time) int64 {
+ t = ds.RoundTime(t)
+ return t.Unix()*1e6 + int64(t.Nanosecond()/1e3)
+}
+
+// WriteTime writes a time to the buffer.
+//
+// The supplied time is rounded via datastore.RoundTime and written as a
+// microseconds-since-epoch integer to comform to datastore storage standards.
func WriteTime(buf Buffer, t time.Time) error {
name, off := t.Zone()
if name != "UTC" || off != 0 {
panic(fmt.Errorf("helper: UTC OR DEATH: %s", t))
}
- _, err := cmpbin.WriteInt(buf, t.Unix()*1e6+int64(t.Nanosecond()/1e3))
+
+ _, err := cmpbin.WriteInt(buf, timeToInt(t))
return err
}
@@ -208,6 +218,7 @@ func ReadTime(buf Buffer) (time.Time, error) {
if err != nil {
return time.Time{}, err
}
+
t := time.Unix(v/1e6, (v%1e6)*1e3)
if t.IsZero() {
return time.Time{}, nil
@@ -225,35 +236,70 @@ func WriteProperty(buf Buffer, context KeyContext, p ds.Property) (err error) {
typb |= 0x80
}
panicIf(buf.WriteByte(typb))
- switch p.Type() {
+
+ err = writeRawProperty(buf, context, p.Type(), p.Value())
+ return
+}
+
+func writeRawProperty(buf Buffer, context KeyContext, t ds.PropertyType, val interface{}) (err error) {
+ t, val = RawType(t, val)
+ switch t {
case ds.PTNull:
case ds.PTBool:
- b := p.Value().(bool)
+ b := val.(bool)
if b {
err = buf.WriteByte(1)
} else {
err = buf.WriteByte(0)
}
case ds.PTInt:
- _, err = cmpbin.WriteInt(buf, p.Value().(int64))
+ _, err = cmpbin.WriteInt(buf, val.(int64))
case ds.PTFloat:
- _, err = cmpbin.WriteFloat64(buf, p.Value().(float64))
+ _, err = cmpbin.WriteFloat64(buf, val.(float64))
case ds.PTString:
- _, err = cmpbin.WriteString(buf, p.Value().(string))
+ _, err = cmpbin.WriteString(buf, val.(string))
case ds.PTBytes:
- _, err = cmpbin.WriteBytes(buf, p.Value().([]byte))
- case ds.PTTime:
- err = WriteTime(buf, p.Value().(time.Time))
+ _, err = cmpbin.WriteBytes(buf, val.([]byte))
case ds.PTGeoPoint:
- err = WriteGeoPoint(buf, p.Value().(ds.GeoPoint))
+ err = WriteGeoPoint(buf, val.(ds.GeoPoint))
case ds.PTKey:
- err = WriteKey(buf, context, p.Value().(*ds.Key))
- case ds.PTBlobKey:
- _, err = cmpbin.WriteString(buf, string(p.Value().(blobstore.Key)))
+ err = WriteKey(buf, context, val.(*ds.Key))
+
+ default:
+ err = fmt.Errorf("unsupported type: %v", t)
}
return
}
+// RawProperty converts a decomposed Property into a Property that matches its
+// primitive datastore storage format.
+//
+// See RawType for more information.
+func RawProperty(p ds.Property) ds.Property {
+ _, v := RawType(p.Type(), p.Value())
+
+ ret := ds.Property{}
+ ret.SetValue(v, p.IndexSetting())
+ return ret
+}
+
+// RawType converts a decomposed Property into fields that represent its
+// primitive datastore storage format.
+//
+// The following conversions are in place:
+// - PTTime is converted to an microseconds-from-epoch integer value.
+// - PTBlobKey is converted to a string.
iannucci 2015/12/29 21:41:48 and PTBytes too?
+func RawType(t ds.PropertyType, v interface{}) (ds.PropertyType, interface{}) {
+ switch t {
+ case ds.PTTime:
+ return ds.PTInt, timeToInt(v.(time.Time))
+ case ds.PTBlobKey:
+ return ds.PTString, string(v.(blobstore.Key))
+ default:
+ return t, v
+ }
+}
+
// ReadProperty reads a Property from the buffer. `context`, `appid`, and
// `namespace` behave the same way they do for ReadKey, but only have an
// effect if the decoded property has a Key value.
@@ -504,35 +550,32 @@ func PropertyMapPartially(k *ds.Key, pm ds.PropertyMap) (ret SerializedPmap) {
}
func toBytesErr(i interface{}, ctx KeyContext) (ret []byte, err error) {
- buf := &bytes.Buffer{}
- switch x := i.(type) {
- case ds.GeoPoint:
- err = WriteGeoPoint(buf, x)
+ buf := bytes.Buffer{}
+ switch t := i.(type) {
case ds.IndexColumn:
- err = WriteIndexColumn(buf, x)
+ err = WriteIndexColumn(&buf, t)
case ds.IndexDefinition:
- err = WriteIndexDefinition(buf, x)
-
- case *ds.Key:
- err = WriteKey(buf, ctx, x)
+ err = WriteIndexDefinition(&buf, t)
case ds.KeyTok:
- err = WriteKeyTok(buf, x)
+ err = WriteKeyTok(&buf, t)
case ds.Property:
- err = WriteProperty(buf, ctx, x)
+ err = WriteProperty(&buf, ctx, t)
case ds.PropertyMap:
- err = WritePropertyMap(buf, ctx, x)
-
- case time.Time:
- err = WriteTime(buf, x)
+ err = WritePropertyMap(&buf, ctx, t)
default:
- err = fmt.Errorf("unknown type for ToBytes: %T", i)
+ var pt ds.PropertyType
+ pt, err = ds.PropertyTypeOf(i, false)
+ if err == nil {
+ err = writeRawProperty(&buf, ctx, pt, t)
+ }
}
+
if err == nil {
ret = buf.Bytes()
}

Powered by Google App Engine
This is Rietveld 408576698