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) | |
241 » return | |
242 } | |
243 | |
244 func writeRawProperty(buf Buffer, context KeyContext, p ds.Property) (err error) { | |
245 » p = p.ForIndex() | |
iannucci
2015/12/30 23:12:24
I think this may end up doing unnecessary copies,
| |
246 » switch t := p.Type(); t { | |
229 case ds.PTNull: | 247 case ds.PTNull: |
230 case ds.PTBool: | 248 case ds.PTBool: |
231 b := p.Value().(bool) | 249 b := p.Value().(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, p.Value().(int64)) |
239 case ds.PTFloat: | 257 case ds.PTFloat: |
240 _, err = cmpbin.WriteFloat64(buf, p.Value().(float64)) | 258 _, err = cmpbin.WriteFloat64(buf, p.Value().(float64)) |
241 case ds.PTString: | 259 case ds.PTString: |
242 _, err = cmpbin.WriteString(buf, p.Value().(string)) | 260 _, err = cmpbin.WriteString(buf, p.Value().(string)) |
243 case ds.PTBytes: | |
244 _, err = cmpbin.WriteBytes(buf, p.Value().([]byte)) | |
245 case ds.PTTime: | |
246 err = WriteTime(buf, p.Value().(time.Time)) | |
247 case ds.PTGeoPoint: | 261 case ds.PTGeoPoint: |
248 err = WriteGeoPoint(buf, p.Value().(ds.GeoPoint)) | 262 err = WriteGeoPoint(buf, p.Value().(ds.GeoPoint)) |
249 case ds.PTKey: | 263 case ds.PTKey: |
250 err = WriteKey(buf, context, p.Value().(*ds.Key)) | 264 err = WriteKey(buf, context, p.Value().(*ds.Key)) |
251 » case ds.PTBlobKey: | 265 |
252 » » _, err = cmpbin.WriteString(buf, string(p.Value().(blobstore.Key ))) | 266 » default: |
267 » » err = fmt.Errorf("unsupported type: %v", t) | |
253 } | 268 } |
254 return | 269 return |
255 } | 270 } |
256 | 271 |
257 // ReadProperty reads a Property from the buffer. `context`, `appid`, and | 272 // ReadProperty reads a Property from the buffer. `context`, `appid`, and |
258 // `namespace` behave the same way they do for ReadKey, but only have an | 273 // `namespace` behave the same way they do for ReadKey, but only have an |
259 // effect if the decoded property has a Key value. | 274 // effect if the decoded property has a Key value. |
260 func ReadProperty(buf Buffer, context KeyContext, appid, namespace string) (p ds .Property, err error) { | 275 func ReadProperty(buf Buffer, context KeyContext, appid, namespace string) (p ds .Property, err error) { |
261 val := interface{}(nil) | 276 val := interface{}(nil) |
262 b, err := buf.ReadByte() | 277 b, err := buf.ReadByte() |
(...skipping 234 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
497 for k, vals := range pm { | 512 for k, vals := range pm { |
498 newVals := PropertySlice(vals) | 513 newVals := PropertySlice(vals) |
499 if len(newVals) > 0 { | 514 if len(newVals) > 0 { |
500 ret[k] = newVals | 515 ret[k] = newVals |
501 } | 516 } |
502 } | 517 } |
503 return | 518 return |
504 } | 519 } |
505 | 520 |
506 func toBytesErr(i interface{}, ctx KeyContext) (ret []byte, err error) { | 521 func toBytesErr(i interface{}, ctx KeyContext) (ret []byte, err error) { |
507 » buf := &bytes.Buffer{} | 522 » buf := bytes.Buffer{} |
508 » switch x := i.(type) { | |
509 » case ds.GeoPoint: | |
510 » » err = WriteGeoPoint(buf, x) | |
511 | 523 |
524 switch t := i.(type) { | |
512 case ds.IndexColumn: | 525 case ds.IndexColumn: |
513 » » err = WriteIndexColumn(buf, x) | 526 » » err = WriteIndexColumn(&buf, t) |
514 | 527 |
515 case ds.IndexDefinition: | 528 case ds.IndexDefinition: |
516 » » err = WriteIndexDefinition(buf, x) | 529 » » err = WriteIndexDefinition(&buf, t) |
517 | |
518 » case *ds.Key: | |
519 » » err = WriteKey(buf, ctx, x) | |
520 | 530 |
521 case ds.KeyTok: | 531 case ds.KeyTok: |
522 » » err = WriteKeyTok(buf, x) | 532 » » err = WriteKeyTok(&buf, t) |
523 | 533 |
524 case ds.Property: | 534 case ds.Property: |
525 » » err = WriteProperty(buf, ctx, x) | 535 » » err = WriteProperty(&buf, ctx, t) |
526 | 536 |
527 case ds.PropertyMap: | 537 case ds.PropertyMap: |
528 » » err = WritePropertyMap(buf, ctx, x) | 538 » » err = WritePropertyMap(&buf, ctx, t) |
529 | |
530 » case time.Time: | |
531 » » err = WriteTime(buf, x) | |
532 | 539 |
533 default: | 540 default: |
534 » » err = fmt.Errorf("unknown type for ToBytes: %T", i) | 541 » » err = writeRawProperty(&buf, ctx, ds.MkProperty(i)) |
535 } | 542 } |
543 | |
536 if err == nil { | 544 if err == nil { |
537 ret = buf.Bytes() | 545 ret = buf.Bytes() |
538 } | 546 } |
539 return | 547 return |
540 } | 548 } |
541 | 549 |
542 // ToBytesErr serializes i to a byte slice, if it's one of the type supported | 550 // ToBytesErr serializes i to a byte slice, if it's one of the type supported |
543 // by this library, otherwise it returns an error. | 551 // by this library, otherwise it returns an error. |
544 // | 552 // |
545 // Key types will be serialized using the 'WithoutContext' option (e.g. their | 553 // 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 } | 601 } |
594 } | 602 } |
595 | 603 |
596 func recoverTo(err *error) { | 604 func recoverTo(err *error) { |
597 if r := recover(); r != nil { | 605 if r := recover(); r != nil { |
598 if rerr := r.(parseError); rerr != nil { | 606 if rerr := r.(parseError); rerr != nil { |
599 *err = error(rerr) | 607 *err = error(rerr) |
600 } | 608 } |
601 } | 609 } |
602 } | 610 } |
OLD | NEW |