| 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 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 func writeIndexValue(buf Buffer, context KeyContext, v interface{}) (err error)
{ |
| 234 » » } else { | 249 » switch t := v.(type) { |
| 235 » » » err = buf.WriteByte(0) | 250 » case nil: |
| 251 » case bool: |
| 252 » » b := byte(0) |
| 253 » » if t { |
| 254 » » » b = 1 |
| 236 } | 255 } |
| 237 » case ds.PTInt: | 256 » » err = buf.WriteByte(b) |
| 238 » » _, err = cmpbin.WriteInt(buf, p.Value().(int64)) | 257 » case int64: |
| 239 » case ds.PTFloat: | 258 » » _, err = cmpbin.WriteInt(buf, t) |
| 240 » » _, err = cmpbin.WriteFloat64(buf, p.Value().(float64)) | 259 » case float64: |
| 241 » case ds.PTString: | 260 » » _, err = cmpbin.WriteFloat64(buf, t) |
| 242 » » _, err = cmpbin.WriteString(buf, p.Value().(string)) | 261 » case string: |
| 243 » case ds.PTBytes: | 262 » » _, err = cmpbin.WriteString(buf, t) |
| 244 » » _, err = cmpbin.WriteBytes(buf, p.Value().([]byte)) | 263 » case []byte: |
| 245 » case ds.PTTime: | 264 » » _, err = cmpbin.WriteBytes(buf, t) |
| 246 » » err = WriteTime(buf, p.Value().(time.Time)) | 265 » case ds.GeoPoint: |
| 247 » case ds.PTGeoPoint: | 266 » » err = WriteGeoPoint(buf, t) |
| 248 » » err = WriteGeoPoint(buf, p.Value().(ds.GeoPoint)) | 267 » case *ds.Key: |
| 249 » case ds.PTKey: | 268 » » err = WriteKey(buf, context, t) |
| 250 » » err = WriteKey(buf, context, p.Value().(*ds.Key)) | 269 |
| 251 » case ds.PTBlobKey: | 270 » default: |
| 252 » » _, err = cmpbin.WriteString(buf, string(p.Value().(blobstore.Key
))) | 271 » » err = fmt.Errorf("unsupported type: %T", t) |
| 253 } | 272 } |
| 254 return | 273 return |
| 255 } | 274 } |
| 256 | 275 |
| 257 // ReadProperty reads a Property from the buffer. `context`, `appid`, and | 276 // ReadProperty reads a Property from the buffer. `context`, `appid`, and |
| 258 // `namespace` behave the same way they do for ReadKey, but only have an | 277 // `namespace` behave the same way they do for ReadKey, but only have an |
| 259 // effect if the decoded property has a Key value. | 278 // effect if the decoded property has a Key value. |
| 260 func ReadProperty(buf Buffer, context KeyContext, appid, namespace string) (p ds
.Property, err error) { | 279 func ReadProperty(buf Buffer, context KeyContext, appid, namespace string) (p ds
.Property, err error) { |
| 261 val := interface{}(nil) | 280 val := interface{}(nil) |
| 262 b, err := buf.ReadByte() | 281 b, err := buf.ReadByte() |
| (...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 294 val = blobstore.Key(s) | 313 val = blobstore.Key(s) |
| 295 default: | 314 default: |
| 296 err = fmt.Errorf("read: unknown type! %v", b) | 315 err = fmt.Errorf("read: unknown type! %v", b) |
| 297 } | 316 } |
| 298 if err == nil { | 317 if err == nil { |
| 299 err = p.SetValue(val, is) | 318 err = p.SetValue(val, is) |
| 300 } | 319 } |
| 301 return | 320 return |
| 302 } | 321 } |
| 303 | 322 |
| 304 // WritePropertyMap writes an entire PropertyMap to the buffer. `context` behave
s the same | 323 // WritePropertyMap writes an entire PropertyMap to the buffer. `context` |
| 305 // way that it does for WriteKey. If WritePropertyMapDeterministic is true, then | 324 // behaves the same way that it does for WriteKey. |
| 306 // the rows will be sorted by property name before they're serialized to buf | 325 // |
| 307 // (mostly useful for testing, but also potentially useful if you need to make | 326 // If WritePropertyMapDeterministic is true, then the rows will be sorted by |
| 308 // a hash of the property data). | 327 // property name before they're serialized to buf (mostly useful for testing, |
| 328 // but also potentially useful if you need to make a hash of the property data). |
| 309 // | 329 // |
| 310 // Write skips metadata keys. | 330 // Write skips metadata keys. |
| 311 func WritePropertyMap(buf Buffer, context KeyContext, pm ds.PropertyMap) (err er
ror) { | 331 func WritePropertyMap(buf Buffer, context KeyContext, pm ds.PropertyMap) (err er
ror) { |
| 312 defer recoverTo(&err) | 332 defer recoverTo(&err) |
| 313 rows := make(sort.StringSlice, 0, len(pm)) | 333 rows := make(sort.StringSlice, 0, len(pm)) |
| 314 tmpBuf := &bytes.Buffer{} | 334 tmpBuf := &bytes.Buffer{} |
| 315 pm, _ = pm.Save(false) | 335 pm, _ = pm.Save(false) |
| 316 for name, vals := range pm { | 336 for name, vals := range pm { |
| 317 tmpBuf.Reset() | 337 tmpBuf.Reset() |
| 318 _, e := cmpbin.WriteString(tmpBuf, name) | 338 _, 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 } | 479 func (s SerializedPslice) Less(i, j int) bool { return bytes.Compare(s[i], s[j])
< 0 } |
| 460 | 480 |
| 461 // PropertySlice serializes a single row of a DSProperty map. | 481 // PropertySlice serializes a single row of a DSProperty map. |
| 462 func PropertySlice(vals ds.PropertySlice) SerializedPslice { | 482 func PropertySlice(vals ds.PropertySlice) SerializedPslice { |
| 463 dups := stringset.New(0) | 483 dups := stringset.New(0) |
| 464 ret := make(SerializedPslice, 0, len(vals)) | 484 ret := make(SerializedPslice, 0, len(vals)) |
| 465 for _, v := range vals { | 485 for _, v := range vals { |
| 466 if v.IndexSetting() == ds.NoIndex { | 486 if v.IndexSetting() == ds.NoIndex { |
| 467 continue | 487 continue |
| 468 } | 488 } |
| 469 » » data := ToBytes(v.ForIndex()) | 489 |
| 490 » » data := ToBytes(v) |
| 470 dataS := string(data) | 491 dataS := string(data) |
| 471 if !dups.Add(dataS) { | 492 if !dups.Add(dataS) { |
| 472 continue | 493 continue |
| 473 } | 494 } |
| 474 ret = append(ret, data) | 495 ret = append(ret, data) |
| 475 } | 496 } |
| 476 return ret | 497 return ret |
| 477 } | 498 } |
| 478 | 499 |
| 479 // SerializedPmap maps from | 500 // SerializedPmap maps from |
| (...skipping 17 matching lines...) Expand all Loading... |
| 497 for k, vals := range pm { | 518 for k, vals := range pm { |
| 498 newVals := PropertySlice(vals) | 519 newVals := PropertySlice(vals) |
| 499 if len(newVals) > 0 { | 520 if len(newVals) > 0 { |
| 500 ret[k] = newVals | 521 ret[k] = newVals |
| 501 } | 522 } |
| 502 } | 523 } |
| 503 return | 524 return |
| 504 } | 525 } |
| 505 | 526 |
| 506 func toBytesErr(i interface{}, ctx KeyContext) (ret []byte, err error) { | 527 func toBytesErr(i interface{}, ctx KeyContext) (ret []byte, err error) { |
| 507 » buf := &bytes.Buffer{} | 528 » buf := bytes.Buffer{} |
| 508 » switch x := i.(type) { | |
| 509 » case ds.GeoPoint: | |
| 510 » » err = WriteGeoPoint(buf, x) | |
| 511 | 529 |
| 530 switch t := i.(type) { |
| 512 case ds.IndexColumn: | 531 case ds.IndexColumn: |
| 513 » » err = WriteIndexColumn(buf, x) | 532 » » err = WriteIndexColumn(&buf, t) |
| 514 | 533 |
| 515 case ds.IndexDefinition: | 534 case ds.IndexDefinition: |
| 516 » » err = WriteIndexDefinition(buf, x) | 535 » » err = WriteIndexDefinition(&buf, t) |
| 517 | |
| 518 » case *ds.Key: | |
| 519 » » err = WriteKey(buf, ctx, x) | |
| 520 | 536 |
| 521 case ds.KeyTok: | 537 case ds.KeyTok: |
| 522 » » err = WriteKeyTok(buf, x) | 538 » » err = WriteKeyTok(&buf, t) |
| 523 | 539 |
| 524 case ds.Property: | 540 case ds.Property: |
| 525 » » err = WriteProperty(buf, ctx, x) | 541 » » err = WriteIndexProperty(&buf, ctx, t) |
| 526 | 542 |
| 527 case ds.PropertyMap: | 543 case ds.PropertyMap: |
| 528 » » err = WritePropertyMap(buf, ctx, x) | 544 » » err = WritePropertyMap(&buf, ctx, t) |
| 529 | |
| 530 » case time.Time: | |
| 531 » » err = WriteTime(buf, x) | |
| 532 | 545 |
| 533 default: | 546 default: |
| 534 » » err = fmt.Errorf("unknown type for ToBytes: %T", i) | 547 » » _, v := ds.MkProperty(i).IndexTypeAndValue() |
| 548 » » err = writeIndexValue(&buf, ctx, v) |
| 535 } | 549 } |
| 550 |
| 536 if err == nil { | 551 if err == nil { |
| 537 ret = buf.Bytes() | 552 ret = buf.Bytes() |
| 538 } | 553 } |
| 539 return | 554 return |
| 540 } | 555 } |
| 541 | 556 |
| 542 // ToBytesErr serializes i to a byte slice, if it's one of the type supported | 557 // ToBytesErr serializes i to a byte slice, if it's one of the type supported |
| 543 // by this library, otherwise it returns an error. | 558 // by this library, otherwise it returns an error. |
| 544 // | 559 // |
| 545 // Key types will be serialized using the 'WithoutContext' option (e.g. their | 560 // 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 } | 608 } |
| 594 } | 609 } |
| 595 | 610 |
| 596 func recoverTo(err *error) { | 611 func recoverTo(err *error) { |
| 597 if r := recover(); r != nil { | 612 if r := recover(); r != nil { |
| 598 if rerr := r.(parseError); rerr != nil { | 613 if rerr := r.(parseError); rerr != nil { |
| 599 *err = error(rerr) | 614 *err = error(rerr) |
| 600 } | 615 } |
| 601 } | 616 } |
| 602 } | 617 } |
| OLD | NEW |