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 |