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" |
11 "sort" | 11 "sort" |
12 "time" | 12 "time" |
13 | 13 |
14 "github.com/luci/gae/service/blobstore" | 14 "github.com/luci/gae/service/blobstore" |
15 ds "github.com/luci/gae/service/datastore" | 15 ds "github.com/luci/gae/service/datastore" |
16 "github.com/luci/gae/service/datastore/dskey" | |
17 "github.com/luci/luci-go/common/cmpbin" | 16 "github.com/luci/luci-go/common/cmpbin" |
18 ) | 17 ) |
19 | 18 |
20 // MaxIndexColumns is the maximum number of sort columns (e.g. sort orders) that | 19 // MaxIndexColumns is the maximum number of sort columns (e.g. sort orders) that |
21 // ReadIndexDefinition is willing to deserialize. 64 was chosen as | 20 // ReadIndexDefinition is willing to deserialize. 64 was chosen as |
22 // a likely-astronomical number. | 21 // a likely-astronomical number. |
23 const MaxIndexColumns = 64 | 22 const MaxIndexColumns = 64 |
24 | 23 |
25 // WritePropertyMapDeterministic allows tests to make WritePropertyMap | 24 // WritePropertyMapDeterministic allows tests to make WritePropertyMap |
26 // deterministic. | 25 // deterministic. |
(...skipping 17 matching lines...) Expand all Loading... |
44 | 43 |
45 // With- and WithoutContext indicate if the serialization method should include | 44 // With- and WithoutContext indicate if the serialization method should include |
46 // context for Keys. See KeyContext for more information. | 45 // context for Keys. See KeyContext for more information. |
47 const ( | 46 const ( |
48 WithContext KeyContext = true | 47 WithContext KeyContext = true |
49 WithoutContext = false | 48 WithoutContext = false |
50 ) | 49 ) |
51 | 50 |
52 // WriteKey encodes a key to the buffer. If context is WithContext, then this | 51 // WriteKey encodes a key to the buffer. If context is WithContext, then this |
53 // encoded value will include the appid and namespace of the key. | 52 // encoded value will include the appid and namespace of the key. |
54 func WriteKey(buf Buffer, context KeyContext, k ds.Key) (err error) { | 53 func WriteKey(buf Buffer, context KeyContext, k *ds.Key) (err error) { |
55 // [appid ++ namespace]? ++ [1 ++ token]* ++ NULL | 54 // [appid ++ namespace]? ++ [1 ++ token]* ++ NULL |
56 defer recoverTo(&err) | 55 defer recoverTo(&err) |
57 » appid, namespace, toks := dskey.Split(k) | 56 » appid, namespace, toks := k.Split() |
58 if context == WithContext { | 57 if context == WithContext { |
59 panicIf(buf.WriteByte(1)) | 58 panicIf(buf.WriteByte(1)) |
60 _, e := cmpbin.WriteString(buf, appid) | 59 _, e := cmpbin.WriteString(buf, appid) |
61 panicIf(e) | 60 panicIf(e) |
62 _, e = cmpbin.WriteString(buf, namespace) | 61 _, e = cmpbin.WriteString(buf, namespace) |
63 panicIf(e) | 62 panicIf(e) |
64 } else { | 63 } else { |
65 panicIf(buf.WriteByte(0)) | 64 panicIf(buf.WriteByte(0)) |
66 } | 65 } |
67 for _, tok := range toks { | 66 for _, tok := range toks { |
68 panicIf(buf.WriteByte(1)) | 67 panicIf(buf.WriteByte(1)) |
69 panicIf(WriteKeyTok(buf, tok)) | 68 panicIf(WriteKeyTok(buf, tok)) |
70 } | 69 } |
71 return buf.WriteByte(0) | 70 return buf.WriteByte(0) |
72 } | 71 } |
73 | 72 |
74 // ReadKey deserializes a key from the buffer. The value of context must match | 73 // ReadKey deserializes a key from the buffer. The value of context must match |
75 // the value of context that was passed to WriteKey when the key was encoded. | 74 // the value of context that was passed to WriteKey when the key was encoded. |
76 // If context == WithoutContext, then the appid and namespace parameters are | 75 // If context == WithoutContext, then the appid and namespace parameters are |
77 // used in the decoded Key. Otherwise they're ignored. | 76 // used in the decoded Key. Otherwise they're ignored. |
78 func ReadKey(buf Buffer, context KeyContext, appid, namespace string) (ret ds.Ke
y, err error) { | 77 func ReadKey(buf Buffer, context KeyContext, appid, namespace string) (ret *ds.K
ey, err error) { |
79 defer recoverTo(&err) | 78 defer recoverTo(&err) |
80 actualCtx, e := buf.ReadByte() | 79 actualCtx, e := buf.ReadByte() |
81 panicIf(e) | 80 panicIf(e) |
82 | 81 |
83 actualAid, actualNS := "", "" | 82 actualAid, actualNS := "", "" |
84 if actualCtx == 1 { | 83 if actualCtx == 1 { |
85 actualAid, _, e = cmpbin.ReadString(buf) | 84 actualAid, _, e = cmpbin.ReadString(buf) |
86 panicIf(e) | 85 panicIf(e) |
87 actualNS, _, e = cmpbin.ReadString(buf) | 86 actualNS, _, e = cmpbin.ReadString(buf) |
88 panicIf(e) | 87 panicIf(e) |
(...skipping 21 matching lines...) Expand all Loading... |
110 ReadKeyNumToksReasonableLimit) | 109 ReadKeyNumToksReasonableLimit) |
111 return | 110 return |
112 } | 111 } |
113 | 112 |
114 tok, e := ReadKeyTok(buf) | 113 tok, e := ReadKeyTok(buf) |
115 panicIf(e) | 114 panicIf(e) |
116 | 115 |
117 toks = append(toks, tok) | 116 toks = append(toks, tok) |
118 } | 117 } |
119 | 118 |
120 » return dskey.NewToks(actualAid, actualNS, toks), nil | 119 » return ds.NewKeyToks(actualAid, actualNS, toks), nil |
121 } | 120 } |
122 | 121 |
123 // WriteKeyTok writes a KeyTok to the buffer. You usually want WriteKey | 122 // WriteKeyTok writes a KeyTok to the buffer. You usually want WriteKey |
124 // instead of this. | 123 // instead of this. |
125 func WriteKeyTok(buf Buffer, tok ds.KeyTok) (err error) { | 124 func WriteKeyTok(buf Buffer, tok ds.KeyTok) (err error) { |
126 // tok.kind ++ typ ++ [tok.stringID || tok.intID] | 125 // tok.kind ++ typ ++ [tok.stringID || tok.intID] |
127 defer recoverTo(&err) | 126 defer recoverTo(&err) |
128 _, e := cmpbin.WriteString(buf, tok.Kind) | 127 _, e := cmpbin.WriteString(buf, tok.Kind) |
129 panicIf(e) | 128 panicIf(e) |
130 if tok.StringID != "" { | 129 if tok.StringID != "" { |
(...skipping 105 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
236 _, err = cmpbin.WriteFloat64(buf, p.Value().(float64)) | 235 _, err = cmpbin.WriteFloat64(buf, p.Value().(float64)) |
237 case ds.PTString: | 236 case ds.PTString: |
238 _, err = cmpbin.WriteString(buf, p.Value().(string)) | 237 _, err = cmpbin.WriteString(buf, p.Value().(string)) |
239 case ds.PTBytes: | 238 case ds.PTBytes: |
240 _, err = cmpbin.WriteBytes(buf, p.Value().([]byte)) | 239 _, err = cmpbin.WriteBytes(buf, p.Value().([]byte)) |
241 case ds.PTTime: | 240 case ds.PTTime: |
242 err = WriteTime(buf, p.Value().(time.Time)) | 241 err = WriteTime(buf, p.Value().(time.Time)) |
243 case ds.PTGeoPoint: | 242 case ds.PTGeoPoint: |
244 err = WriteGeoPoint(buf, p.Value().(ds.GeoPoint)) | 243 err = WriteGeoPoint(buf, p.Value().(ds.GeoPoint)) |
245 case ds.PTKey: | 244 case ds.PTKey: |
246 » » err = WriteKey(buf, context, p.Value().(ds.Key)) | 245 » » err = WriteKey(buf, context, p.Value().(*ds.Key)) |
247 case ds.PTBlobKey: | 246 case ds.PTBlobKey: |
248 _, err = cmpbin.WriteString(buf, string(p.Value().(blobstore.Key
))) | 247 _, err = cmpbin.WriteString(buf, string(p.Value().(blobstore.Key
))) |
249 } | 248 } |
250 return | 249 return |
251 } | 250 } |
252 | 251 |
253 // ReadProperty reads a Property from the buffer. `context`, `appid`, and | 252 // ReadProperty reads a Property from the buffer. `context`, `appid`, and |
254 // `namespace` behave the same way they do for ReadKey, but only have an | 253 // `namespace` behave the same way they do for ReadKey, but only have an |
255 // effect if the decoded property has a Key value. | 254 // effect if the decoded property has a Key value. |
256 func ReadProperty(buf Buffer, context KeyContext, appid, namespace string) (p ds
.Property, err error) { | 255 func ReadProperty(buf Buffer, context KeyContext, appid, namespace string) (p ds
.Property, err error) { |
(...skipping 200 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
457 switch x := i.(type) { | 456 switch x := i.(type) { |
458 case ds.GeoPoint: | 457 case ds.GeoPoint: |
459 err = WriteGeoPoint(buf, x) | 458 err = WriteGeoPoint(buf, x) |
460 | 459 |
461 case ds.IndexColumn: | 460 case ds.IndexColumn: |
462 err = WriteIndexColumn(buf, x) | 461 err = WriteIndexColumn(buf, x) |
463 | 462 |
464 case ds.IndexDefinition: | 463 case ds.IndexDefinition: |
465 err = WriteIndexDefinition(buf, x) | 464 err = WriteIndexDefinition(buf, x) |
466 | 465 |
467 » case ds.Key: | 466 » case *ds.Key: |
468 err = WriteKey(buf, ctx, x) | 467 err = WriteKey(buf, ctx, x) |
469 | 468 |
470 case ds.KeyTok: | 469 case ds.KeyTok: |
471 err = WriteKeyTok(buf, x) | 470 err = WriteKeyTok(buf, x) |
472 | 471 |
473 case ds.Property: | 472 case ds.Property: |
474 err = WriteProperty(buf, ctx, x) | 473 err = WriteProperty(buf, ctx, x) |
475 | 474 |
476 case ds.PropertyMap: | 475 case ds.PropertyMap: |
477 err = WritePropertyMap(buf, ctx, x) | 476 err = WritePropertyMap(buf, ctx, x) |
(...skipping 64 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
542 } | 541 } |
543 } | 542 } |
544 | 543 |
545 func recoverTo(err *error) { | 544 func recoverTo(err *error) { |
546 if r := recover(); r != nil { | 545 if r := recover(); r != nil { |
547 if rerr := r.(parseError); rerr != nil { | 546 if rerr := r.(parseError); rerr != nil { |
548 *err = error(rerr) | 547 *err = error(rerr) |
549 } | 548 } |
550 } | 549 } |
551 } | 550 } |
OLD | NEW |