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