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 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 Loading... | |
| 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 Loading... | |
| 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 Loading... | |
| 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 } |
| OLD | NEW |