github.com/mjibson/goon@v1.1.0/entity.go (about) 1 /* 2 * Copyright (c) 2012 The Goon Authors 3 * 4 * Permission to use, copy, modify, and distribute this software for any 5 * purpose with or without fee is hereby granted, provided that the above 6 * copyright notice and this permission notice appear in all copies. 7 * 8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 */ 16 17 package goon 18 19 import ( 20 "bytes" 21 "encoding/binary" 22 "fmt" 23 "math" 24 "reflect" 25 "strings" 26 "sync" 27 "time" 28 29 "google.golang.org/appengine" 30 "google.golang.org/appengine/datastore" 31 ) 32 33 // KindNameResolver takes an Entity and returns what the Kind should be for 34 // Datastore. 35 type KindNameResolver func(src interface{}) string 36 37 // ### Entity serialization ### 38 39 const serializationFormatVersion = 5 // Increase this whenever the format changes 40 41 // The entities are encoded to bytes with little endian ordering, as follows: 42 // 43 // [header][?prop1][..][?propN] 44 // 45 // header | uint32 | Always present 46 // The first 30 bits are used for propCount. The last 2 bits for flags. 47 // 48 // propX | []byte | X <= propCount 49 // Each property is serialized separately. 50 // 51 // 52 // A property gets serialized into bytes with little endian ordering as follows: 53 // 54 // [nameLen][?name][header][?valueLen][?value] 55 // 56 // nameLen | uint16 | Always present 57 // The length of the property name. 58 // 59 // name | string | nameLen > 0 60 // The property name. 61 // 62 // header | byte | Always present 63 // The first 4 bits specify the property value type (propType..), the last 4 bits are used for flags. 64 // 65 // valueLen | uint24 | type == (string|BlobKey|ByteString|[]byte|*Key) && value != zeroValue 66 // The length of the value bytes. 67 // 68 // value | []byte | type != (none|bool) && value != zeroValue 69 // 70 // None N/A 71 // Int64 8 bytes 72 // Bool N/A (value == propHasValue header flag) 73 // String valueLen bytes 74 // Float64 8 bytes 75 // ByteString valueLen bytes 76 // KeyPtr valueLen bytes 77 // Time 8 bytes 78 // BlobKey valueLen bytes 79 // GeoPoint 16 bytes, Lat+Lng float64 80 // ByteSlice valueLen bytes 81 82 // Entity header flags 83 const ( 84 entityExists = 1 << 30 85 //entityRESERVED = 1 << 31 // Unused flag 86 ) 87 88 const entityHeaderMaskPropCount = 1<<30 - 1 // All the bits used for propCount 89 const entityHeaderMaskFlags = ^entityHeaderMaskPropCount // All the bits used for flags 90 91 func serializeEntityHeader(propCount, flags int) uint32 { 92 return uint32((flags & entityHeaderMaskFlags) | (propCount & entityHeaderMaskPropCount)) 93 } 94 95 func deserializeEntityHeader(header uint32) (propCount, flags int) { 96 return int(header) & entityHeaderMaskPropCount, int(header) & entityHeaderMaskFlags 97 } 98 99 // The valid datastore.Property.Value types are: 100 // - int64 101 // - bool 102 // - string 103 // - float64 104 // - datastore.ByteString 105 // - *datastore.Key 106 // - time.Time 107 // - appengine.BlobKey 108 // - appengine.GeoPoint 109 // - []byte (up to 1 megabyte in length) 110 // - *Entity (representing a nested struct) 111 const ( 112 propTypeNone = 0 113 propTypeInt64 = 1 114 propTypeBool = 2 115 propTypeString = 3 116 propTypeFloat64 = 4 117 propTypeByteString = 5 118 propTypeKeyPtr = 6 119 propTypeTime = 7 120 propTypeBlobKey = 8 121 propTypeGeoPoint = 9 122 propTypeByteSlice = 10 123 //propTypeEntityPtr = 11 // TODO: Implement this? 124 // Space for 4 more types, as the propType value must fit in 4 bits (0-15) 125 ) 126 127 // Property header flags 128 const ( 129 propHasValue = 1 << 4 130 propMultiple = 1 << 5 131 propNoIndex = 1 << 6 132 //propRESERVED = 1 << 7 // Unused flag 133 ) 134 135 // We limit the maximum length of datastore.Property.Name, 136 // however this is our implementation specific and not datastore specific. 137 const propMaxNameLength = 1<<16 - 1 138 139 // Keep a pool of buffers around for more efficient allocation 140 var bufferPool = sync.Pool{ 141 New: func() interface{} { 142 return bytes.NewBuffer(make([]byte, 0, 16384)) // 16 KiB initial capacity 143 }, 144 } 145 146 // getBuffer returns a reusable buffer from a pool. 147 // Every buffer acquired with this function must be later freed via freeBuffer. 148 func getBuffer() *bytes.Buffer { 149 return bufferPool.Get().(*bytes.Buffer) 150 } 151 152 // freeBuffer returns the buffer to the pool, allowing for reuse. 153 func freeBuffer(buf *bytes.Buffer) { 154 buf.Reset() 155 bufferPool.Put(buf) 156 } 157 158 func writeInt16(buf *bytes.Buffer, i int) { 159 buf.WriteByte(byte(i)) 160 buf.WriteByte(byte(i >> 8)) 161 } 162 163 func writeInt24(buf *bytes.Buffer, i int) { 164 buf.WriteByte(byte(i)) 165 buf.WriteByte(byte(i >> 8)) 166 buf.WriteByte(byte(i >> 16)) 167 } 168 169 func toUnixMicro(t time.Time) int64 { 170 // We cannot use t.UnixNano() / 1e3 because we want to handle times more than 171 // 2^63 nanoseconds (which is about 292 years) away from 1970, and those cannot 172 // be represented in the numerator of a single int64 divide. 173 return t.Unix()*1e6 + int64(t.Nanosecond()/1e3) 174 } 175 176 func fromUnixMicro(t int64) time.Time { 177 return time.Unix(t/1e6, (t%1e6)*1e3).UTC() 178 } 179 180 func isIndexValue(p *datastore.Property) bool { 181 v := reflect.ValueOf(p.Value) 182 return v.Type().String() == "datastore.indexValue" 183 } 184 185 func serializeProperty(buf *bytes.Buffer, p *datastore.Property) error { 186 nameLen := len(p.Name) 187 if nameLen > propMaxNameLength { 188 return fmt.Errorf("Maximum property name length is %d, but received %d", propMaxNameLength, nameLen) 189 } 190 writeInt16(buf, nameLen) 191 if nameLen > 0 { 192 buf.WriteString(p.Name) 193 } 194 195 var header byte 196 if p.Multiple { 197 header |= propMultiple 198 } 199 if p.NoIndex { 200 header |= propNoIndex 201 } 202 203 v := reflect.ValueOf(p.Value) 204 unsupported := false 205 206 switch v.Kind() { 207 case reflect.Invalid: 208 // Has no type and no value, but is legal 209 buf.WriteByte(header) 210 case reflect.Int64: 211 header |= propTypeInt64 212 val := uint64(v.Int()) 213 if val == 0 { 214 buf.WriteByte(header) 215 } else { 216 header |= propHasValue 217 buf.WriteByte(header) 218 data := make([]byte, 8) 219 binary.LittleEndian.PutUint64(data, val) 220 buf.Write(data) 221 } 222 case reflect.Bool: 223 header |= propTypeBool 224 if v.Bool() { 225 header |= propHasValue 226 } 227 buf.WriteByte(header) 228 case reflect.String: 229 switch v.Interface().(type) { 230 case appengine.BlobKey: 231 header |= propTypeBlobKey 232 default: 233 header |= propTypeString 234 } 235 val := v.String() 236 if valLen := len(val); valLen == 0 { 237 buf.WriteByte(header) 238 } else { 239 header |= propHasValue 240 buf.WriteByte(header) 241 writeInt24(buf, valLen) 242 buf.WriteString(val) 243 } 244 case reflect.Float64: 245 header |= propTypeFloat64 246 val := v.Float() 247 if val == 0 { 248 buf.WriteByte(header) 249 } else { 250 header |= propHasValue 251 buf.WriteByte(header) 252 data := make([]byte, 8) 253 binary.LittleEndian.PutUint64(data, math.Float64bits(val)) 254 buf.Write(data) 255 } 256 case reflect.Ptr: 257 if k, ok := v.Interface().(*datastore.Key); ok { 258 header |= propTypeKeyPtr 259 if k == nil { 260 buf.WriteByte(header) 261 } else { 262 header |= propHasValue 263 buf.WriteByte(header) 264 val := k.Encode() 265 writeInt24(buf, len(val)) 266 buf.WriteString(val) 267 } 268 } else { 269 unsupported = true 270 } 271 case reflect.Struct: 272 switch s := v.Interface().(type) { 273 case time.Time: 274 header |= propTypeTime 275 if s.IsZero() { 276 buf.WriteByte(header) 277 } else { 278 header |= propHasValue 279 buf.WriteByte(header) 280 data := make([]byte, 8) 281 binary.LittleEndian.PutUint64(data, uint64(toUnixMicro(s))) 282 buf.Write(data) 283 } 284 case appengine.GeoPoint: 285 header |= propTypeGeoPoint 286 if s.Lat == 0 && s.Lng == 0 { 287 buf.WriteByte(header) 288 } else { 289 header |= propHasValue 290 buf.WriteByte(header) 291 data := make([]byte, 16) 292 binary.LittleEndian.PutUint64(data[:8], math.Float64bits(s.Lat)) 293 binary.LittleEndian.PutUint64(data[8:], math.Float64bits(s.Lng)) 294 buf.Write(data) 295 } 296 default: 297 unsupported = true 298 } 299 case reflect.Slice: 300 switch b := v.Interface().(type) { 301 case datastore.ByteString: 302 header |= propTypeByteString 303 if bLen := len(b); bLen == 0 { 304 buf.WriteByte(header) 305 } else { 306 header |= propHasValue 307 buf.WriteByte(header) 308 writeInt24(buf, bLen) 309 buf.Write(b) 310 } 311 case []byte: 312 header |= propTypeByteSlice 313 if bLen := len(b); bLen == 0 { 314 buf.WriteByte(header) 315 } else { 316 header |= propHasValue 317 buf.WriteByte(header) 318 writeInt24(buf, bLen) 319 buf.Write(b) 320 } 321 default: 322 unsupported = true 323 } 324 default: 325 unsupported = true 326 } 327 if unsupported { 328 return fmt.Errorf("unsupported datastore.Property value type: " + v.Type().String()) 329 } 330 return nil 331 } 332 333 func deserializeProperty(buf *bytes.Buffer, prop *datastore.Property) error { 334 next := func(n int) ([]byte, error) { 335 b := buf.Next(n) 336 if bLen := len(b); bLen != n { 337 return b, fmt.Errorf("Buffer EOF, expected %d bytes but got %v", n, bLen) 338 } 339 return b, nil 340 } 341 getSize := func() (int, error) { 342 sizeBytes, err := next(3) 343 if err != nil { 344 return 0, err 345 } 346 return int(sizeBytes[0]) | int(sizeBytes[1])<<8 | int(sizeBytes[2])<<16, nil 347 } 348 349 // Read the name length 350 nameLenBytes, err := next(2) 351 if err != nil { 352 return err 353 } 354 nameLen := int(nameLenBytes[0]) | int(nameLenBytes[1])<<8 355 // If thehre's a name, read it 356 if nameLen > 0 { 357 nameBytes, err := next(nameLen) 358 if err != nil { 359 return err 360 } 361 prop.Name = string(nameBytes) 362 } 363 364 // Read the header 365 header, err := buf.ReadByte() 366 if err != nil { 367 return err 368 } 369 370 // Apply the flags 371 prop.Multiple = (header&propMultiple != 0) 372 prop.NoIndex = (header&propNoIndex != 0) 373 374 // Determine the value 375 valueType := header & 0xF 376 zeroValue := header&propHasValue == 0 377 switch valueType { 378 case propTypeNone: 379 // nil interface, so nothing to do 380 case propTypeInt64: 381 if zeroValue { 382 prop.Value = int64(0) 383 } else { 384 valBytes, err := next(8) 385 if err != nil { 386 return err 387 } 388 prop.Value = int64(binary.LittleEndian.Uint64(valBytes)) 389 } 390 case propTypeBool: 391 prop.Value = !zeroValue 392 case propTypeString: 393 if zeroValue { 394 prop.Value = "" 395 } else { 396 size, err := getSize() 397 if err != nil { 398 return err 399 } 400 valBytes, err := next(size) 401 if err != nil { 402 return err 403 } 404 prop.Value = string(valBytes) 405 } 406 case propTypeBlobKey: 407 if zeroValue { 408 prop.Value = appengine.BlobKey("") 409 } else { 410 size, err := getSize() 411 if err != nil { 412 return err 413 } 414 valBytes, err := next(size) 415 if err != nil { 416 return err 417 } 418 prop.Value = appengine.BlobKey(valBytes) 419 } 420 case propTypeFloat64: 421 if zeroValue { 422 prop.Value = float64(0) 423 } else { 424 valBytes, err := next(8) 425 if err != nil { 426 return err 427 } 428 prop.Value = math.Float64frombits(binary.LittleEndian.Uint64(valBytes)) 429 } 430 case propTypeKeyPtr: 431 if zeroValue { 432 var key *datastore.Key 433 prop.Value = key 434 } else { 435 size, err := getSize() 436 if err != nil { 437 return err 438 } 439 valBytes, err := next(size) 440 if err != nil { 441 return err 442 } 443 prop.Value, err = datastore.DecodeKey(string(valBytes)) 444 if err != nil { 445 return err 446 } 447 } 448 case propTypeTime: 449 if zeroValue { 450 prop.Value = time.Time{} 451 } else { 452 valBytes, err := next(8) 453 if err != nil { 454 return err 455 } 456 prop.Value = fromUnixMicro(int64(binary.LittleEndian.Uint64(valBytes))) 457 } 458 case propTypeGeoPoint: 459 if zeroValue { 460 prop.Value = appengine.GeoPoint{} 461 } else { 462 valBytes, err := next(16) 463 if err != nil { 464 return err 465 } 466 prop.Value = appengine.GeoPoint{ 467 Lat: math.Float64frombits(binary.LittleEndian.Uint64(valBytes[:8])), 468 Lng: math.Float64frombits(binary.LittleEndian.Uint64(valBytes[8:])), 469 } 470 } 471 case propTypeByteString: 472 if zeroValue { 473 prop.Value = datastore.ByteString{} 474 } else { 475 size, err := getSize() 476 if err != nil { 477 return err 478 } 479 prop.Value = make(datastore.ByteString, size) 480 if _, err := buf.Read(prop.Value.(datastore.ByteString)); err != nil { 481 return err 482 } 483 } 484 case propTypeByteSlice: 485 if zeroValue { 486 prop.Value = []byte{} 487 } else { 488 size, err := getSize() 489 if err != nil { 490 return err 491 } 492 prop.Value = make([]byte, size) 493 if _, err := buf.Read(prop.Value.([]byte)); err != nil { 494 return err 495 } 496 } 497 default: 498 return fmt.Errorf("Unrecognized value type %d", valueType) 499 } 500 501 return nil 502 } 503 504 // serializeStruct takes a struct and serializes it to portable bytes. 505 func serializeStruct(src interface{}) ([]byte, error) { 506 if src == nil { 507 return serializeProperties(nil, false) 508 } 509 if k := reflect.Indirect(reflect.ValueOf(src)).Type().Kind(); k != reflect.Struct { 510 return nil, fmt.Errorf("goon: Expected struct, got instead: %v", k) 511 } 512 513 var err error 514 var props []datastore.Property 515 if pls, ok := src.(datastore.PropertyLoadSaver); ok { 516 props, err = pls.Save() 517 } else { 518 props, err = datastore.SaveStruct(src) 519 } 520 if err != nil { 521 return nil, err 522 } 523 524 return serializeProperties(props, true) 525 } 526 527 // serializeProperties takes a slice of properties and serializes it to portable bytes. 528 func serializeProperties(props []datastore.Property, exists bool) ([]byte, error) { 529 // NOTE: We use a separate exists bool to support nil-props for existing structs 530 if !exists { 531 return []byte{0, 0, 0, 0}, nil 532 } 533 534 buf := getBuffer() 535 defer freeBuffer(buf) 536 537 // Serialize the entity header 538 header := serializeEntityHeader(len(props), entityExists) 539 headerBytes := make([]byte, 4) 540 binary.LittleEndian.PutUint32(headerBytes, header) 541 buf.Write(headerBytes) 542 543 // Serialize the properties 544 for i := range props { 545 if err := serializeProperty(buf, &props[i]); err != nil { 546 return nil, err 547 } 548 } 549 550 output := make([]byte, buf.Len()) 551 copy(output, buf.Bytes()) 552 return output, nil 553 } 554 555 // deserializeStruct takes portable bytes b, generated by serializeStruct, and assigns correct values to struct dst. 556 func deserializeStruct(dst interface{}, b []byte) error { 557 if len(b) == 0 { 558 return fmt.Errorf("goon: Expected some data to deserialize, got none.") 559 } 560 if k := reflect.Indirect(reflect.ValueOf(dst)).Type().Kind(); k != reflect.Struct { 561 return fmt.Errorf("goon: Expected struct, got instead: %v", k) 562 } 563 564 // Deserialize the header 565 header := binary.LittleEndian.Uint32(b[:4]) 566 propCount, flags := deserializeEntityHeader(header) 567 if flags&entityExists == 0 { 568 return datastore.ErrNoSuchEntity 569 } 570 571 // Deserialize the properties 572 buf := bytes.NewBuffer(b[4:]) 573 props := make([]datastore.Property, propCount) 574 for i := 0; i < propCount; i++ { 575 if err := deserializeProperty(buf, &props[i]); err != nil { 576 return err 577 } 578 } 579 580 return deserializeProperties(dst, props) 581 } 582 583 // deserializeProperties takes a slice of properties and assigns correct values to struct dst. 584 func deserializeProperties(dst interface{}, props []datastore.Property) error { 585 if k := reflect.Indirect(reflect.ValueOf(dst)).Type().Kind(); k != reflect.Struct { 586 return fmt.Errorf("goon: Expected struct, got instead: %v", k) 587 } 588 if pls, ok := dst.(datastore.PropertyLoadSaver); ok { 589 return pls.Load(props) 590 } 591 return datastore.LoadStruct(dst, props) 592 } 593 594 // getStructKey returns the key of the struct based in its reflected or 595 // specified kind and id. The second return parameter is true if src has a 596 // string id. 597 func (g *Goon) getStructKey(src interface{}) (key *datastore.Key, hasStringId bool, err error) { 598 v := reflect.Indirect(reflect.ValueOf(src)) 599 t := v.Type() 600 k := t.Kind() 601 602 if k != reflect.Struct { 603 err = fmt.Errorf("goon: Expected struct, got instead: %v", k) 604 return 605 } 606 607 var parent *datastore.Key 608 var stringID string 609 var intID int64 610 var kind string 611 612 for i := 0; i < v.NumField(); i++ { 613 tf := t.Field(i) 614 vf := v.Field(i) 615 616 tag := tf.Tag.Get("goon") 617 tagValues := strings.Split(tag, ",") 618 if len(tagValues) > 0 { 619 tagValue := tagValues[0] 620 if tagValue == "id" { 621 switch vf.Kind() { 622 case reflect.Int64: 623 if intID != 0 || stringID != "" { 624 err = fmt.Errorf("goon: Only one field may be marked id") 625 return 626 } 627 intID = vf.Int() 628 case reflect.String: 629 if intID != 0 || stringID != "" { 630 err = fmt.Errorf("goon: Only one field may be marked id") 631 return 632 } 633 stringID = vf.String() 634 hasStringId = true 635 default: 636 err = fmt.Errorf("goon: ID field must be int64 or string in %v", t.Name()) 637 return 638 } 639 } else if tagValue == "kind" { 640 if vf.Kind() == reflect.String { 641 if kind != "" { 642 err = fmt.Errorf("goon: Only one field may be marked kind") 643 return 644 } 645 kind = vf.String() 646 if kind == "" && len(tagValues) > 1 && tagValues[1] != "" { 647 kind = tagValues[1] 648 } 649 } 650 } else if tagValue == "parent" { 651 dskeyType := reflect.TypeOf(&datastore.Key{}) 652 if vf.Type().ConvertibleTo(dskeyType) { 653 if parent != nil { 654 err = fmt.Errorf("goon: Only one field may be marked parent") 655 return 656 } 657 parent = vf.Convert(dskeyType).Interface().(*datastore.Key) 658 } 659 } 660 } 661 } 662 663 // if kind has not been manually set, fetch it from src's type 664 if kind == "" { 665 kind = g.KindNameResolver(src) 666 } 667 key = datastore.NewKey(g.Context, kind, stringID, intID, parent) 668 return 669 } 670 671 // DefaultKindName is the default implementation to determine the Kind 672 // an Entity has. Returns the basic Type of the src (no package name included). 673 func DefaultKindName(src interface{}) string { 674 v := reflect.ValueOf(src) 675 v = reflect.Indirect(v) 676 t := v.Type() 677 return t.Name() 678 } 679 680 func (g *Goon) setStructKey(src interface{}, key *datastore.Key) error { 681 v := reflect.ValueOf(src) 682 t := v.Type() 683 k := t.Kind() 684 685 if k != reflect.Ptr { 686 return fmt.Errorf("goon: Expected pointer to struct, got instead: %v", k) 687 } 688 689 v = reflect.Indirect(v) 690 t = v.Type() 691 k = t.Kind() 692 693 if k != reflect.Struct { 694 return fmt.Errorf(fmt.Sprintf("goon: Expected struct, got instead: %v", k)) 695 } 696 697 idSet := false 698 kindSet := false 699 parentSet := false 700 for i := 0; i < v.NumField(); i++ { 701 tf := t.Field(i) 702 vf := v.Field(i) 703 704 if !vf.CanSet() { 705 continue 706 } 707 708 tag := tf.Tag.Get("goon") 709 tagValues := strings.Split(tag, ",") 710 if len(tagValues) > 0 { 711 tagValue := tagValues[0] 712 if tagValue == "id" { 713 if idSet { 714 return fmt.Errorf("goon: Only one field may be marked id") 715 } 716 717 switch vf.Kind() { 718 case reflect.Int64: 719 vf.SetInt(key.IntID()) 720 idSet = true 721 case reflect.String: 722 vf.SetString(key.StringID()) 723 idSet = true 724 } 725 } else if tagValue == "kind" { 726 if kindSet { 727 return fmt.Errorf("goon: Only one field may be marked kind") 728 } 729 if vf.Kind() == reflect.String { 730 if (len(tagValues) <= 1 || key.Kind() != tagValues[1]) && g.KindNameResolver(src) != key.Kind() { 731 vf.Set(reflect.ValueOf(key.Kind())) 732 } 733 kindSet = true 734 } 735 } else if tagValue == "parent" { 736 if parentSet { 737 return fmt.Errorf("goon: Only one field may be marked parent") 738 } 739 dskeyType := reflect.TypeOf(&datastore.Key{}) 740 vfType := vf.Type() 741 if vfType.ConvertibleTo(dskeyType) { 742 vf.Set(reflect.ValueOf(key.Parent()).Convert(vfType)) 743 parentSet = true 744 } 745 } 746 } 747 } 748 749 if !idSet { 750 return fmt.Errorf("goon: Could not set id field") 751 } 752 753 return nil 754 }