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

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: Comments, tune-up. 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
« no previous file with comments | « service/datastore/properties_test.go ('k') | service/datastore/serialize/serialize_test.go » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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 // WriteTime writes a time.Time to the buffer.
193 // 193 //
194 // This method truncates the time to microseconds and drops the timezone, 194 // The supplied time is rounded via datastore.RoundTime and written as a
195 // because that's the (undocumented) way that the appengine SDK does it. 195 // microseconds-since-epoch integer to comform to datastore storage standards.
196 func WriteTime(buf Buffer, t time.Time) error { 196 func WriteTime(buf Buffer, t time.Time) error {
197 name, off := t.Zone() 197 name, off := t.Zone()
198 if name != "UTC" || off != 0 { 198 if name != "UTC" || off != 0 {
199 panic(fmt.Errorf("helper: UTC OR DEATH: %s", t)) 199 panic(fmt.Errorf("helper: UTC OR DEATH: %s", t))
200 } 200 }
201 » _, err := cmpbin.WriteInt(buf, t.Unix()*1e6+int64(t.Nanosecond()/1e3)) 201
202 » _, err := cmpbin.WriteInt(buf, ds.TimeToInt(t))
202 return err 203 return err
203 } 204 }
204 205
205 // ReadTime reads a time.Time from the buffer. 206 // ReadTime reads a time.Time from the buffer.
206 func ReadTime(buf Buffer) (time.Time, error) { 207 func ReadTime(buf Buffer) (time.Time, error) {
207 v, _, err := cmpbin.ReadInt(buf) 208 v, _, err := cmpbin.ReadInt(buf)
208 if err != nil { 209 if err != nil {
209 return time.Time{}, err 210 return time.Time{}, err
210 } 211 }
211 » t := time.Unix(v/1e6, (v%1e6)*1e3) 212 » return ds.IntToTime(v), nil
212 » if t.IsZero() {
213 » » return time.Time{}, nil
214 » }
215 » return t.UTC(), nil
216 } 213 }
217 214
218 // WriteProperty writes a Property to the buffer. `context` behaves the same 215 // 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 216 // way that it does for WriteKey, but only has an effect if `p` contains a
220 // Key as its Value. 217 // Key as its IndexValue.
221 func WriteProperty(buf Buffer, context KeyContext, p ds.Property) (err error) { 218 func WriteProperty(buf Buffer, context KeyContext, p ds.Property) error {
219 » return writePropertyImpl(buf, context, &p, false)
220 }
221
222 // WriteIndexProperty writes a Property to the buffer as its native index type.
223 // `context` behaves the same way that it does for WriteKey, but only has an
224 // effect if `p` contains a Key as its IndexValue.
225 func WriteIndexProperty(buf Buffer, context KeyContext, p ds.Property) error {
226 » return writePropertyImpl(buf, context, &p, true)
227 }
228
229 // writePropertyImpl is an implementation of WriteProperty and
230 // WriteIndexProperty.
231 func writePropertyImpl(buf Buffer, context KeyContext, p *ds.Property, index boo l) (err error) {
222 defer recoverTo(&err) 232 defer recoverTo(&err)
223 » typb := byte(p.Type()) 233
234 » it, v := p.IndexTypeAndValue()
235 » if !index {
236 » » it = p.Type()
237 » }
238 » typb := byte(it)
224 if p.IndexSetting() != ds.NoIndex { 239 if p.IndexSetting() != ds.NoIndex {
225 typb |= 0x80 240 typb |= 0x80
226 } 241 }
227 panicIf(buf.WriteByte(typb)) 242 panicIf(buf.WriteByte(typb))
228 » switch p.Type() { 243
229 » case ds.PTNull: 244 » err = writeIndexValue(buf, context, v)
230 » case ds.PTBool: 245 » return
231 » » b := p.Value().(bool) 246 }
232 » » if b { 247
233 » » » err = buf.WriteByte(1) 248 // writeIndexValue writes the index value of v to buf.
234 » » } else { 249 //
235 » » » err = buf.WriteByte(0) 250 // v may be one of the return types from ds.Property's GetIndexTypeAndValue
251 // method.
252 func writeIndexValue(buf Buffer, context KeyContext, v interface{}) (err error) {
253 » switch t := v.(type) {
254 » case nil:
255 » case bool:
256 » » b := byte(0)
257 » » if t {
258 » » » b = 1
236 } 259 }
237 » case ds.PTInt: 260 » » err = buf.WriteByte(b)
238 » » _, err = cmpbin.WriteInt(buf, p.Value().(int64)) 261 » case int64:
239 » case ds.PTFloat: 262 » » _, err = cmpbin.WriteInt(buf, t)
240 » » _, err = cmpbin.WriteFloat64(buf, p.Value().(float64)) 263 » case float64:
241 » case ds.PTString: 264 » » _, err = cmpbin.WriteFloat64(buf, t)
242 » » _, err = cmpbin.WriteString(buf, p.Value().(string)) 265 » case string:
243 » case ds.PTBytes: 266 » » _, err = cmpbin.WriteString(buf, t)
244 » » _, err = cmpbin.WriteBytes(buf, p.Value().([]byte)) 267 » case []byte:
245 » case ds.PTTime: 268 » » _, err = cmpbin.WriteBytes(buf, t)
246 » » err = WriteTime(buf, p.Value().(time.Time)) 269 » case ds.GeoPoint:
247 » case ds.PTGeoPoint: 270 » » err = WriteGeoPoint(buf, t)
248 » » err = WriteGeoPoint(buf, p.Value().(ds.GeoPoint)) 271 » case *ds.Key:
249 » case ds.PTKey: 272 » » err = WriteKey(buf, context, t)
250 » » err = WriteKey(buf, context, p.Value().(*ds.Key)) 273
251 » case ds.PTBlobKey: 274 » default:
252 » » _, err = cmpbin.WriteString(buf, string(p.Value().(blobstore.Key ))) 275 » » err = fmt.Errorf("unsupported type: %T", t)
253 } 276 }
254 return 277 return
255 } 278 }
256 279
257 // ReadProperty reads a Property from the buffer. `context`, `appid`, and 280 // ReadProperty reads a Property from the buffer. `context`, `appid`, and
258 // `namespace` behave the same way they do for ReadKey, but only have an 281 // `namespace` behave the same way they do for ReadKey, but only have an
259 // effect if the decoded property has a Key value. 282 // effect if the decoded property has a Key value.
260 func ReadProperty(buf Buffer, context KeyContext, appid, namespace string) (p ds .Property, err error) { 283 func ReadProperty(buf Buffer, context KeyContext, appid, namespace string) (p ds .Property, err error) {
261 val := interface{}(nil) 284 val := interface{}(nil)
262 b, err := buf.ReadByte() 285 b, err := buf.ReadByte()
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after
294 val = blobstore.Key(s) 317 val = blobstore.Key(s)
295 default: 318 default:
296 err = fmt.Errorf("read: unknown type! %v", b) 319 err = fmt.Errorf("read: unknown type! %v", b)
297 } 320 }
298 if err == nil { 321 if err == nil {
299 err = p.SetValue(val, is) 322 err = p.SetValue(val, is)
300 } 323 }
301 return 324 return
302 } 325 }
303 326
304 // WritePropertyMap writes an entire PropertyMap to the buffer. `context` behave s the same 327 // WritePropertyMap writes an entire PropertyMap to the buffer. `context`
305 // way that it does for WriteKey. If WritePropertyMapDeterministic is true, then 328 // behaves the same way that it does for WriteKey.
306 // the rows will be sorted by property name before they're serialized to buf 329 //
307 // (mostly useful for testing, but also potentially useful if you need to make 330 // If WritePropertyMapDeterministic is true, then the rows will be sorted by
308 // a hash of the property data). 331 // property name before they're serialized to buf (mostly useful for testing,
332 // but also potentially useful if you need to make a hash of the property data).
309 // 333 //
310 // Write skips metadata keys. 334 // Write skips metadata keys.
311 func WritePropertyMap(buf Buffer, context KeyContext, pm ds.PropertyMap) (err er ror) { 335 func WritePropertyMap(buf Buffer, context KeyContext, pm ds.PropertyMap) (err er ror) {
312 defer recoverTo(&err) 336 defer recoverTo(&err)
313 rows := make(sort.StringSlice, 0, len(pm)) 337 rows := make(sort.StringSlice, 0, len(pm))
314 tmpBuf := &bytes.Buffer{} 338 tmpBuf := &bytes.Buffer{}
315 pm, _ = pm.Save(false) 339 pm, _ = pm.Save(false)
316 for name, vals := range pm { 340 for name, vals := range pm {
317 tmpBuf.Reset() 341 tmpBuf.Reset()
318 _, e := cmpbin.WriteString(tmpBuf, name) 342 _, e := cmpbin.WriteString(tmpBuf, name)
(...skipping 140 matching lines...) Expand 10 before | Expand all | Expand 10 after
459 func (s SerializedPslice) Less(i, j int) bool { return bytes.Compare(s[i], s[j]) < 0 } 483 func (s SerializedPslice) Less(i, j int) bool { return bytes.Compare(s[i], s[j]) < 0 }
460 484
461 // PropertySlice serializes a single row of a DSProperty map. 485 // PropertySlice serializes a single row of a DSProperty map.
462 func PropertySlice(vals ds.PropertySlice) SerializedPslice { 486 func PropertySlice(vals ds.PropertySlice) SerializedPslice {
463 dups := stringset.New(0) 487 dups := stringset.New(0)
464 ret := make(SerializedPslice, 0, len(vals)) 488 ret := make(SerializedPslice, 0, len(vals))
465 for _, v := range vals { 489 for _, v := range vals {
466 if v.IndexSetting() == ds.NoIndex { 490 if v.IndexSetting() == ds.NoIndex {
467 continue 491 continue
468 } 492 }
469 » » data := ToBytes(v.ForIndex()) 493
494 » » data := ToBytes(v)
470 dataS := string(data) 495 dataS := string(data)
471 if !dups.Add(dataS) { 496 if !dups.Add(dataS) {
472 continue 497 continue
473 } 498 }
474 ret = append(ret, data) 499 ret = append(ret, data)
475 } 500 }
476 return ret 501 return ret
477 } 502 }
478 503
479 // SerializedPmap maps from 504 // SerializedPmap maps from
(...skipping 17 matching lines...) Expand all
497 for k, vals := range pm { 522 for k, vals := range pm {
498 newVals := PropertySlice(vals) 523 newVals := PropertySlice(vals)
499 if len(newVals) > 0 { 524 if len(newVals) > 0 {
500 ret[k] = newVals 525 ret[k] = newVals
501 } 526 }
502 } 527 }
503 return 528 return
504 } 529 }
505 530
506 func toBytesErr(i interface{}, ctx KeyContext) (ret []byte, err error) { 531 func toBytesErr(i interface{}, ctx KeyContext) (ret []byte, err error) {
507 » buf := &bytes.Buffer{} 532 » buf := bytes.Buffer{}
508 » switch x := i.(type) {
509 » case ds.GeoPoint:
510 » » err = WriteGeoPoint(buf, x)
511 533
534 switch t := i.(type) {
512 case ds.IndexColumn: 535 case ds.IndexColumn:
513 » » err = WriteIndexColumn(buf, x) 536 » » err = WriteIndexColumn(&buf, t)
514 537
515 case ds.IndexDefinition: 538 case ds.IndexDefinition:
516 » » err = WriteIndexDefinition(buf, x) 539 » » err = WriteIndexDefinition(&buf, t)
517
518 » case *ds.Key:
519 » » err = WriteKey(buf, ctx, x)
520 540
521 case ds.KeyTok: 541 case ds.KeyTok:
522 » » err = WriteKeyTok(buf, x) 542 » » err = WriteKeyTok(&buf, t)
523 543
524 case ds.Property: 544 case ds.Property:
525 » » err = WriteProperty(buf, ctx, x) 545 » » err = WriteIndexProperty(&buf, ctx, t)
526 546
527 case ds.PropertyMap: 547 case ds.PropertyMap:
528 » » err = WritePropertyMap(buf, ctx, x) 548 » » err = WritePropertyMap(&buf, ctx, t)
529
530 » case time.Time:
531 » » err = WriteTime(buf, x)
532 549
533 default: 550 default:
534 » » err = fmt.Errorf("unknown type for ToBytes: %T", i) 551 » » _, v := ds.MkProperty(i).IndexTypeAndValue()
552 » » err = writeIndexValue(&buf, ctx, v)
535 } 553 }
554
536 if err == nil { 555 if err == nil {
537 ret = buf.Bytes() 556 ret = buf.Bytes()
538 } 557 }
539 return 558 return
540 } 559 }
541 560
542 // ToBytesErr serializes i to a byte slice, if it's one of the type supported 561 // ToBytesErr serializes i to a byte slice, if it's one of the type supported
543 // by this library, otherwise it returns an error. 562 // by this library, otherwise it returns an error.
544 // 563 //
545 // Key types will be serialized using the 'WithoutContext' option (e.g. their 564 // 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 } 612 }
594 } 613 }
595 614
596 func recoverTo(err *error) { 615 func recoverTo(err *error) {
597 if r := recover(); r != nil { 616 if r := recover(); r != nil {
598 if rerr := r.(parseError); rerr != nil { 617 if rerr := r.(parseError); rerr != nil {
599 *err = error(rerr) 618 *err = error(rerr)
600 } 619 }
601 } 620 }
602 } 621 }
OLDNEW
« no previous file with comments | « service/datastore/properties_test.go ('k') | service/datastore/serialize/serialize_test.go » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698