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 datastore | 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" |
| 16 "github.com/luci/gae/service/datastore/dskey" |
15 "github.com/luci/luci-go/common/cmpbin" | 17 "github.com/luci/luci-go/common/cmpbin" |
16 ) | 18 ) |
17 | 19 |
| 20 // MaxIndexColumns is the maximum number of sort columns (e.g. sort orders) that |
| 21 // ReadIndexDefinition is willing to deserialize. |
18 const MaxIndexColumns = 64 | 22 const MaxIndexColumns = 64 |
19 | 23 |
20 // WritePropertyMapDeterministic allows tests to make WritePropertyMap | 24 // WritePropertyMapDeterministic allows tests to make WritePropertyMap |
21 // deterministic. | 25 // deterministic. |
22 var WritePropertyMapDeterministic = false | 26 var WritePropertyMapDeterministic = false |
23 | 27 |
24 // ReadPropertyMapReasonableLimit sets a limit on the number of rows and | 28 // ReadPropertyMapReasonableLimit sets a limit on the number of rows and |
25 // number of properties per row which can be read by ReadPropertyMap. The | 29 // number of properties per row which can be read by ReadPropertyMap. The |
26 // total number of Property objects readable by this method is this number | 30 // total number of Property objects readable by this method is this number |
27 // squared (e.g. Limit rows * Limit properties) | 31 // squared (e.g. Limit rows * Limit properties) |
(...skipping 11 matching lines...) Expand all Loading... |
39 | 43 |
40 // With- and WithoutContext indicate if the serialization method should include | 44 // With- and WithoutContext indicate if the serialization method should include |
41 // context for Keys. See KeyContext for more information. | 45 // context for Keys. See KeyContext for more information. |
42 const ( | 46 const ( |
43 WithContext KeyContext = true | 47 WithContext KeyContext = true |
44 WithoutContext = false | 48 WithoutContext = false |
45 ) | 49 ) |
46 | 50 |
47 // 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 |
48 // encoded value will include the appid and namespace of the key. | 52 // encoded value will include the appid and namespace of the key. |
49 func WriteKey(buf Buffer, context KeyContext, k Key) (err error) { | 53 func WriteKey(buf Buffer, context KeyContext, k ds.Key) (err error) { |
50 // [appid ++ namespace]? ++ #tokens ++ tokens* | 54 // [appid ++ namespace]? ++ #tokens ++ tokens* |
51 defer recoverTo(&err) | 55 defer recoverTo(&err) |
52 » appid, namespace, toks := KeySplit(k) | 56 » appid, namespace, toks := dskey.Split(k) |
53 if context == WithContext { | 57 if context == WithContext { |
54 panicIf(buf.WriteByte(1)) | 58 panicIf(buf.WriteByte(1)) |
55 _, e := cmpbin.WriteString(buf, appid) | 59 _, e := cmpbin.WriteString(buf, appid) |
56 panicIf(e) | 60 panicIf(e) |
57 _, e = cmpbin.WriteString(buf, namespace) | 61 _, e = cmpbin.WriteString(buf, namespace) |
58 panicIf(e) | 62 panicIf(e) |
59 } else { | 63 } else { |
60 panicIf(buf.WriteByte(0)) | 64 panicIf(buf.WriteByte(0)) |
61 } | 65 } |
62 _, e := cmpbin.WriteUint(buf, uint64(len(toks))) | 66 _, e := cmpbin.WriteUint(buf, uint64(len(toks))) |
63 panicIf(e) | 67 panicIf(e) |
64 for _, tok := range toks { | 68 for _, tok := range toks { |
65 panicIf(WriteKeyTok(buf, tok)) | 69 panicIf(WriteKeyTok(buf, tok)) |
66 } | 70 } |
67 return nil | 71 return nil |
68 } | 72 } |
69 | 73 |
70 // ReadKey deserializes a key from the buffer. The value of context must match | 74 // ReadKey deserializes a key from the buffer. The value of context must match |
71 // the value of context that was passed to WriteKey when the key was encoded. | 75 // the value of context that was passed to WriteKey when the key was encoded. |
72 // If context == WithoutContext, then the appid and namespace parameters are | 76 // If context == WithoutContext, then the appid and namespace parameters are |
73 // used in the decoded Key. Otherwise they're ignored. | 77 // used in the decoded Key. Otherwise they're ignored. |
74 func ReadKey(buf Buffer, context KeyContext, appid, namespace string) (ret Key,
err error) { | 78 func ReadKey(buf Buffer, context KeyContext, appid, namespace string) (ret ds.Ke
y, err error) { |
75 defer recoverTo(&err) | 79 defer recoverTo(&err) |
76 actualCtx, e := buf.ReadByte() | 80 actualCtx, e := buf.ReadByte() |
77 panicIf(e) | 81 panicIf(e) |
78 | 82 |
79 actualAid, actualNS := "", "" | 83 actualAid, actualNS := "", "" |
80 if actualCtx == 1 { | 84 if actualCtx == 1 { |
81 actualAid, _, e = cmpbin.ReadString(buf) | 85 actualAid, _, e = cmpbin.ReadString(buf) |
82 panicIf(e) | 86 panicIf(e) |
83 actualNS, _, e = cmpbin.ReadString(buf) | 87 actualNS, _, e = cmpbin.ReadString(buf) |
84 panicIf(e) | 88 panicIf(e) |
85 } else if actualCtx != 0 { | 89 } else if actualCtx != 0 { |
86 err = fmt.Errorf("helper: expected actualCtx to be 0 or 1, got %
d", actualCtx) | 90 err = fmt.Errorf("helper: expected actualCtx to be 0 or 1, got %
d", actualCtx) |
87 return | 91 return |
88 } | 92 } |
89 | 93 |
90 if context == WithoutContext { | 94 if context == WithoutContext { |
91 // overrwrite with the supplied ones | 95 // overrwrite with the supplied ones |
92 actualAid = appid | 96 actualAid = appid |
93 actualNS = namespace | 97 actualNS = namespace |
94 } | 98 } |
95 | 99 |
96 numToks, _, e := cmpbin.ReadUint(buf) | 100 numToks, _, e := cmpbin.ReadUint(buf) |
97 panicIf(e) | 101 panicIf(e) |
98 if numToks > ReadKeyNumToksReasonableLimit { | 102 if numToks > ReadKeyNumToksReasonableLimit { |
99 err = fmt.Errorf("helper: tried to decode huge key of length %d"
, numToks) | 103 err = fmt.Errorf("helper: tried to decode huge key of length %d"
, numToks) |
100 return | 104 return |
101 } | 105 } |
102 | 106 |
103 » toks := make([]KeyTok, numToks) | 107 » toks := make([]ds.KeyTok, numToks) |
104 for i := uint64(0); i < numToks; i++ { | 108 for i := uint64(0); i < numToks; i++ { |
105 toks[i], e = ReadKeyTok(buf) | 109 toks[i], e = ReadKeyTok(buf) |
106 panicIf(e) | 110 panicIf(e) |
107 } | 111 } |
108 | 112 |
109 » return NewKeyToks(actualAid, actualNS, toks), nil | 113 » return dskey.NewToks(actualAid, actualNS, toks), nil |
110 } | 114 } |
111 | 115 |
112 // WriteKeyTok writes a KeyTok to the buffer. You usually want WriteKey | 116 // WriteKeyTok writes a KeyTok to the buffer. You usually want WriteKey |
113 // instead of this. | 117 // instead of this. |
114 func WriteKeyTok(buf Buffer, tok KeyTok) (err error) { | 118 func WriteKeyTok(buf Buffer, tok ds.KeyTok) (err error) { |
115 // tok.kind ++ typ ++ [tok.stringID || tok.intID] | 119 // tok.kind ++ typ ++ [tok.stringID || tok.intID] |
116 defer recoverTo(&err) | 120 defer recoverTo(&err) |
117 _, e := cmpbin.WriteString(buf, tok.Kind) | 121 _, e := cmpbin.WriteString(buf, tok.Kind) |
118 panicIf(e) | 122 panicIf(e) |
119 if tok.StringID != "" { | 123 if tok.StringID != "" { |
120 » » panicIf(buf.WriteByte(byte(PTString))) | 124 » » panicIf(buf.WriteByte(byte(ds.PTString))) |
121 _, e := cmpbin.WriteString(buf, tok.StringID) | 125 _, e := cmpbin.WriteString(buf, tok.StringID) |
122 panicIf(e) | 126 panicIf(e) |
123 } else { | 127 } else { |
124 » » panicIf(buf.WriteByte(byte(PTInt))) | 128 » » panicIf(buf.WriteByte(byte(ds.PTInt))) |
125 _, e := cmpbin.WriteInt(buf, tok.IntID) | 129 _, e := cmpbin.WriteInt(buf, tok.IntID) |
126 panicIf(e) | 130 panicIf(e) |
127 } | 131 } |
128 return nil | 132 return nil |
129 } | 133 } |
130 | 134 |
131 // ReadKeyTok reads a KeyTok from the buffer. You usually want ReadKey | 135 // ReadKeyTok reads a KeyTok from the buffer. You usually want ReadKey |
132 // instead of this. | 136 // instead of this. |
133 func ReadKeyTok(buf Buffer) (ret KeyTok, err error) { | 137 func ReadKeyTok(buf Buffer) (ret ds.KeyTok, err error) { |
134 defer recoverTo(&err) | 138 defer recoverTo(&err) |
135 e := error(nil) | 139 e := error(nil) |
136 ret.Kind, _, e = cmpbin.ReadString(buf) | 140 ret.Kind, _, e = cmpbin.ReadString(buf) |
137 panicIf(e) | 141 panicIf(e) |
138 | 142 |
139 typ, e := buf.ReadByte() | 143 typ, e := buf.ReadByte() |
140 panicIf(e) | 144 panicIf(e) |
141 | 145 |
142 » switch PropertyType(typ) { | 146 » switch ds.PropertyType(typ) { |
143 » case PTString: | 147 » case ds.PTString: |
144 ret.StringID, _, err = cmpbin.ReadString(buf) | 148 ret.StringID, _, err = cmpbin.ReadString(buf) |
145 » case PTInt: | 149 » case ds.PTInt: |
146 ret.IntID, _, err = cmpbin.ReadInt(buf) | 150 ret.IntID, _, err = cmpbin.ReadInt(buf) |
147 if err == nil && ret.IntID <= 0 { | 151 if err == nil && ret.IntID <= 0 { |
148 err = errors.New("helper: decoded key with empty stringI
D and zero/negative intID") | 152 err = errors.New("helper: decoded key with empty stringI
D and zero/negative intID") |
149 } | 153 } |
150 default: | 154 default: |
151 » » err = fmt.Errorf("helper: invalid type %s", PropertyType(typ)) | 155 » » err = fmt.Errorf("helper: invalid type %s", ds.PropertyType(typ)
) |
152 } | 156 } |
153 return | 157 return |
154 } | 158 } |
155 | 159 |
156 // Write writes a GeoPoint to the buffer. | 160 // WriteGeoPoint writes a GeoPoint to the buffer. |
157 func (gp GeoPoint) Write(buf Buffer) (err error) { | 161 func WriteGeoPoint(buf Buffer, gp ds.GeoPoint) (err error) { |
158 defer recoverTo(&err) | 162 defer recoverTo(&err) |
159 _, e := cmpbin.WriteFloat64(buf, gp.Lat) | 163 _, e := cmpbin.WriteFloat64(buf, gp.Lat) |
160 panicIf(e) | 164 panicIf(e) |
161 _, e = cmpbin.WriteFloat64(buf, gp.Lng) | 165 _, e = cmpbin.WriteFloat64(buf, gp.Lng) |
162 return e | 166 return e |
163 } | 167 } |
164 | 168 |
165 // Read reads a GeoPoint from the buffer. | 169 // ReadGeoPoint reads a GeoPoint from the buffer. |
166 func (gp *GeoPoint) Read(buf Buffer) (err error) { | 170 func ReadGeoPoint(buf Buffer) (gp ds.GeoPoint, err error) { |
167 defer recoverTo(&err) | 171 defer recoverTo(&err) |
168 e := error(nil) | 172 e := error(nil) |
169 gp.Lat, _, e = cmpbin.ReadFloat64(buf) | 173 gp.Lat, _, e = cmpbin.ReadFloat64(buf) |
170 panicIf(e) | 174 panicIf(e) |
171 | 175 |
172 gp.Lng, _, e = cmpbin.ReadFloat64(buf) | 176 gp.Lng, _, e = cmpbin.ReadFloat64(buf) |
173 panicIf(e) | 177 panicIf(e) |
174 | 178 |
175 if !gp.Valid() { | 179 if !gp.Valid() { |
176 err = fmt.Errorf("helper: decoded invalid GeoPoint: %v", gp) | 180 err = fmt.Errorf("helper: decoded invalid GeoPoint: %v", gp) |
(...skipping 16 matching lines...) Expand all Loading... |
193 | 197 |
194 // ReadTime reads a time.Time from the buffer. | 198 // ReadTime reads a time.Time from the buffer. |
195 func ReadTime(buf Buffer) (time.Time, error) { | 199 func ReadTime(buf Buffer) (time.Time, error) { |
196 v, _, err := cmpbin.ReadUint(buf) | 200 v, _, err := cmpbin.ReadUint(buf) |
197 if err != nil { | 201 if err != nil { |
198 return time.Time{}, err | 202 return time.Time{}, err |
199 } | 203 } |
200 return time.Unix(int64(v/1e6), int64((v%1e6)*1e3)).UTC(), nil | 204 return time.Unix(int64(v/1e6), int64((v%1e6)*1e3)).UTC(), nil |
201 } | 205 } |
202 | 206 |
203 // Write writes a Property to the buffer. `context` behaves the same | 207 // WriteProperty writes a Property to the buffer. `context` behaves the same |
204 // way that it does for WriteKey, but only has an effect if `p` contains a | 208 // way that it does for WriteKey, but only has an effect if `p` contains a |
205 // Key as its Value. | 209 // Key as its Value. |
206 func (p *Property) Write(buf Buffer, context KeyContext) (err error) { | 210 func WriteProperty(buf Buffer, context KeyContext, p ds.Property) (err error) { |
207 defer recoverTo(&err) | 211 defer recoverTo(&err) |
208 typb := byte(p.Type()) | 212 typb := byte(p.Type()) |
209 » if p.IndexSetting() == NoIndex { | 213 » if p.IndexSetting() == ds.NoIndex { |
210 typb |= 0x80 | 214 typb |= 0x80 |
211 } | 215 } |
212 panicIf(buf.WriteByte(typb)) | 216 panicIf(buf.WriteByte(typb)) |
213 switch p.Type() { | 217 switch p.Type() { |
214 » case PTNull, PTBoolTrue, PTBoolFalse: | 218 » case ds.PTNull, ds.PTBoolTrue, ds.PTBoolFalse: |
215 » case PTInt: | 219 » case ds.PTInt: |
216 _, err = cmpbin.WriteInt(buf, p.Value().(int64)) | 220 _, err = cmpbin.WriteInt(buf, p.Value().(int64)) |
217 » case PTFloat: | 221 » case ds.PTFloat: |
218 _, err = cmpbin.WriteFloat64(buf, p.Value().(float64)) | 222 _, err = cmpbin.WriteFloat64(buf, p.Value().(float64)) |
219 » case PTString: | 223 » case ds.PTString: |
220 _, err = cmpbin.WriteString(buf, p.Value().(string)) | 224 _, err = cmpbin.WriteString(buf, p.Value().(string)) |
221 » case PTBytes: | 225 » case ds.PTBytes: |
222 » » if p.IndexSetting() == NoIndex { | 226 » » if p.IndexSetting() == ds.NoIndex { |
223 _, err = cmpbin.WriteBytes(buf, p.Value().([]byte)) | 227 _, err = cmpbin.WriteBytes(buf, p.Value().([]byte)) |
224 } else { | 228 } else { |
225 » » » _, err = cmpbin.WriteBytes(buf, p.Value().(ByteString)) | 229 » » » _, err = cmpbin.WriteBytes(buf, p.Value().(ds.ByteString
)) |
226 } | 230 } |
227 » case PTTime: | 231 » case ds.PTTime: |
228 err = WriteTime(buf, p.Value().(time.Time)) | 232 err = WriteTime(buf, p.Value().(time.Time)) |
229 » case PTGeoPoint: | 233 » case ds.PTGeoPoint: |
230 » » err = p.Value().(GeoPoint).Write(buf) | 234 » » err = WriteGeoPoint(buf, p.Value().(ds.GeoPoint)) |
231 » case PTKey: | 235 » case ds.PTKey: |
232 » » err = WriteKey(buf, context, p.Value().(Key)) | 236 » » err = WriteKey(buf, context, p.Value().(ds.Key)) |
233 » case PTBlobKey: | 237 » case ds.PTBlobKey: |
234 _, err = cmpbin.WriteString(buf, string(p.Value().(blobstore.Key
))) | 238 _, err = cmpbin.WriteString(buf, string(p.Value().(blobstore.Key
))) |
235 } | 239 } |
236 return | 240 return |
237 } | 241 } |
238 | 242 |
239 // Read reads a Property from the buffer. `context`, `appid`, and | 243 // ReadProperty reads a Property from the buffer. `context`, `appid`, and |
240 // `namespace` behave the same way they do for ReadKey, but only have an | 244 // `namespace` behave the same way they do for ReadKey, but only have an |
241 // effect if the decoded property has a Key value. | 245 // effect if the decoded property has a Key value. |
242 func (p *Property) Read(buf Buffer, context KeyContext, appid, namespace string)
(err error) { | 246 func ReadProperty(buf Buffer, context KeyContext, appid, namespace string) (p ds
.Property, err error) { |
243 val := interface{}(nil) | 247 val := interface{}(nil) |
244 typb, err := buf.ReadByte() | 248 typb, err := buf.ReadByte() |
245 if err != nil { | 249 if err != nil { |
246 return | 250 return |
247 } | 251 } |
248 » is := ShouldIndex | 252 » is := ds.ShouldIndex |
249 if (typb & 0x80) != 0 { | 253 if (typb & 0x80) != 0 { |
250 » » is = NoIndex | 254 » » is = ds.NoIndex |
251 } | 255 } |
252 » switch PropertyType(typb & 0x7f) { | 256 » switch ds.PropertyType(typb & 0x7f) { |
253 » case PTNull: | 257 » case ds.PTNull: |
254 » case PTBoolTrue: | 258 » case ds.PTBoolTrue: |
255 val = true | 259 val = true |
256 » case PTBoolFalse: | 260 » case ds.PTBoolFalse: |
257 val = false | 261 val = false |
258 » case PTInt: | 262 » case ds.PTInt: |
259 val, _, err = cmpbin.ReadInt(buf) | 263 val, _, err = cmpbin.ReadInt(buf) |
260 » case PTFloat: | 264 » case ds.PTFloat: |
261 val, _, err = cmpbin.ReadFloat64(buf) | 265 val, _, err = cmpbin.ReadFloat64(buf) |
262 » case PTString: | 266 » case ds.PTString: |
263 val, _, err = cmpbin.ReadString(buf) | 267 val, _, err = cmpbin.ReadString(buf) |
264 » case PTBytes: | 268 » case ds.PTBytes: |
265 b := []byte(nil) | 269 b := []byte(nil) |
266 if b, _, err = cmpbin.ReadBytes(buf); err != nil { | 270 if b, _, err = cmpbin.ReadBytes(buf); err != nil { |
267 break | 271 break |
268 } | 272 } |
269 » » if is == NoIndex { | 273 » » if is == ds.NoIndex { |
270 val = b | 274 val = b |
271 } else { | 275 } else { |
272 » » » val = ByteString(b) | 276 » » » val = ds.ByteString(b) |
273 } | 277 } |
274 » case PTTime: | 278 » case ds.PTTime: |
275 val, err = ReadTime(buf) | 279 val, err = ReadTime(buf) |
276 » case PTGeoPoint: | 280 » case ds.PTGeoPoint: |
277 » » gp := GeoPoint{} | 281 » » val, err = ReadGeoPoint(buf) |
278 » » err = gp.Read(buf) | 282 » case ds.PTKey: |
279 » » val = gp | |
280 » case PTKey: | |
281 val, err = ReadKey(buf, context, appid, namespace) | 283 val, err = ReadKey(buf, context, appid, namespace) |
282 » case PTBlobKey: | 284 » case ds.PTBlobKey: |
283 s := "" | 285 s := "" |
284 if s, _, err = cmpbin.ReadString(buf); err != nil { | 286 if s, _, err = cmpbin.ReadString(buf); err != nil { |
285 break | 287 break |
286 } | 288 } |
287 val = blobstore.Key(s) | 289 val = blobstore.Key(s) |
288 default: | 290 default: |
289 err = fmt.Errorf("read: unknown type! %v", typb) | 291 err = fmt.Errorf("read: unknown type! %v", typb) |
290 } | 292 } |
291 if err == nil { | 293 if err == nil { |
292 err = p.SetValue(val, is) | 294 err = p.SetValue(val, is) |
293 } | 295 } |
294 return | 296 return |
295 } | 297 } |
296 | 298 |
297 // Write writes an entire PropertyMap to the buffer. `context` behaves the same | 299 // WritePropertyMap writes an entire PropertyMap to the buffer. `context` behave
s the same |
298 // way that it does for WriteKey. If WritePropertyMapDeterministic is true, then | 300 // way that it does for WriteKey. If WritePropertyMapDeterministic is true, then |
299 // the rows will be sorted by property name before they're serialized to buf | 301 // the rows will be sorted by property name before they're serialized to buf |
300 // (mostly useful for testing, but also potentially useful if you need to make | 302 // (mostly useful for testing, but also potentially useful if you need to make |
301 // a hash of the property data). | 303 // a hash of the property data). |
302 // | 304 // |
303 // Write skips metadata keys. | 305 // Write skips metadata keys. |
304 func (pm PropertyMap) Write(buf Buffer, context KeyContext) (err error) { | 306 func WritePropertyMap(buf Buffer, context KeyContext, pm ds.PropertyMap) (err er
ror) { |
305 defer recoverTo(&err) | 307 defer recoverTo(&err) |
306 rows := make(sort.StringSlice, 0, len(pm)) | 308 rows := make(sort.StringSlice, 0, len(pm)) |
307 tmpBuf := &bytes.Buffer{} | 309 tmpBuf := &bytes.Buffer{} |
| 310 pm, _ = pm.Save(false) |
308 for name, vals := range pm { | 311 for name, vals := range pm { |
309 if isMetaKey(name) { | |
310 continue | |
311 } | |
312 tmpBuf.Reset() | 312 tmpBuf.Reset() |
313 _, e := cmpbin.WriteString(tmpBuf, name) | 313 _, e := cmpbin.WriteString(tmpBuf, name) |
314 panicIf(e) | 314 panicIf(e) |
315 _, e = cmpbin.WriteUint(tmpBuf, uint64(len(vals))) | 315 _, e = cmpbin.WriteUint(tmpBuf, uint64(len(vals))) |
316 panicIf(e) | 316 panicIf(e) |
317 for _, p := range vals { | 317 for _, p := range vals { |
318 » » » panicIf(p.Write(tmpBuf, context)) | 318 » » » panicIf(WriteProperty(tmpBuf, context, p)) |
319 } | 319 } |
320 rows = append(rows, tmpBuf.String()) | 320 rows = append(rows, tmpBuf.String()) |
321 } | 321 } |
322 | 322 |
323 if WritePropertyMapDeterministic { | 323 if WritePropertyMapDeterministic { |
324 rows.Sort() | 324 rows.Sort() |
325 } | 325 } |
326 | 326 |
327 _, e := cmpbin.WriteUint(buf, uint64(len(pm))) | 327 _, e := cmpbin.WriteUint(buf, uint64(len(pm))) |
328 panicIf(e) | 328 panicIf(e) |
329 for _, r := range rows { | 329 for _, r := range rows { |
330 _, e := buf.WriteString(r) | 330 _, e := buf.WriteString(r) |
331 panicIf(e) | 331 panicIf(e) |
332 } | 332 } |
333 return | 333 return |
334 } | 334 } |
335 | 335 |
336 // Read reads a PropertyMap from the buffer. `context` and | 336 // ReadPropertyMap reads a PropertyMap from the buffer. `context` and |
337 // friends behave the same way that they do for ReadKey. | 337 // friends behave the same way that they do for ReadKey. |
338 func (pm PropertyMap) Read(buf Buffer, context KeyContext, appid, namespace stri
ng) (err error) { | 338 func ReadPropertyMap(buf Buffer, context KeyContext, appid, namespace string) (p
m ds.PropertyMap, err error) { |
339 defer recoverTo(&err) | 339 defer recoverTo(&err) |
340 | 340 |
341 numRows := uint64(0) | 341 numRows := uint64(0) |
342 numRows, _, e := cmpbin.ReadUint(buf) | 342 numRows, _, e := cmpbin.ReadUint(buf) |
343 panicIf(e) | 343 panicIf(e) |
344 if numRows > ReadPropertyMapReasonableLimit { | 344 if numRows > ReadPropertyMapReasonableLimit { |
345 err = fmt.Errorf("helper: tried to decode map with huge number o
f rows %d", numRows) | 345 err = fmt.Errorf("helper: tried to decode map with huge number o
f rows %d", numRows) |
346 return | 346 return |
347 } | 347 } |
348 | 348 |
349 » name, prop := "", Property{} | 349 » pm = make(ds.PropertyMap, numRows) |
| 350 |
| 351 » name, prop := "", ds.Property{} |
350 for i := uint64(0); i < numRows; i++ { | 352 for i := uint64(0); i < numRows; i++ { |
351 name, _, e = cmpbin.ReadString(buf) | 353 name, _, e = cmpbin.ReadString(buf) |
352 panicIf(e) | 354 panicIf(e) |
353 | 355 |
354 numProps, _, e := cmpbin.ReadUint(buf) | 356 numProps, _, e := cmpbin.ReadUint(buf) |
355 panicIf(e) | 357 panicIf(e) |
356 if numProps > ReadPropertyMapReasonableLimit { | 358 if numProps > ReadPropertyMapReasonableLimit { |
357 err = fmt.Errorf("helper: tried to decode map with huge
number of properties %d", numProps) | 359 err = fmt.Errorf("helper: tried to decode map with huge
number of properties %d", numProps) |
358 return | 360 return |
359 } | 361 } |
360 » » props := make([]Property, 0, numProps) | 362 » » props := make([]ds.Property, 0, numProps) |
361 for j := uint64(0); j < numProps; j++ { | 363 for j := uint64(0); j < numProps; j++ { |
362 » » » panicIf(prop.Read(buf, context, appid, namespace)) | 364 » » » prop, err = ReadProperty(buf, context, appid, namespace) |
| 365 » » » panicIf(err) |
363 props = append(props, prop) | 366 props = append(props, prop) |
364 } | 367 } |
365 pm[name] = props | 368 pm[name] = props |
366 } | 369 } |
367 return | 370 return |
368 } | 371 } |
369 | 372 |
370 func (c *IndexColumn) Write(buf Buffer) (err error) { | 373 // WriteIndexColumn writes an IndexColumn to the buffer. |
| 374 func WriteIndexColumn(buf Buffer, c ds.IndexColumn) (err error) { |
371 defer recoverTo(&err) | 375 defer recoverTo(&err) |
372 | 376 |
373 » if c.Direction == ASCENDING { | 377 » if c.Direction == ds.ASCENDING { |
374 panicIf(buf.WriteByte(0)) | 378 panicIf(buf.WriteByte(0)) |
375 } else { | 379 } else { |
376 panicIf(buf.WriteByte(1)) | 380 panicIf(buf.WriteByte(1)) |
377 } | 381 } |
378 _, err = cmpbin.WriteString(buf, c.Property) | 382 _, err = cmpbin.WriteString(buf, c.Property) |
379 return | 383 return |
380 } | 384 } |
381 | 385 |
382 func (c *IndexColumn) Read(buf Buffer) (err error) { | 386 // ReadIndexColumn reads an IndexColumn from the buffer. |
| 387 func ReadIndexColumn(buf Buffer) (c ds.IndexColumn, err error) { |
383 defer recoverTo(&err) | 388 defer recoverTo(&err) |
384 | 389 |
385 dir, err := buf.ReadByte() | 390 dir, err := buf.ReadByte() |
386 panicIf(err) | 391 panicIf(err) |
387 | 392 |
388 c.Direction = dir != 0 | 393 c.Direction = dir != 0 |
389 c.Property, _, err = cmpbin.ReadString(buf) | 394 c.Property, _, err = cmpbin.ReadString(buf) |
390 » return err | 395 » return |
391 } | 396 } |
392 | 397 |
393 func (i *IndexDefinition) Write(buf Buffer) (err error) { | 398 // WriteIndexDefinition writes an IndexDefinition to the buffer |
| 399 func WriteIndexDefinition(buf Buffer, i ds.IndexDefinition) (err error) { |
394 defer recoverTo(&err) | 400 defer recoverTo(&err) |
395 | 401 |
396 if i.Builtin() { | 402 if i.Builtin() { |
397 panicIf(buf.WriteByte(0)) | 403 panicIf(buf.WriteByte(0)) |
398 } else { | 404 } else { |
399 panicIf(buf.WriteByte(1)) | 405 panicIf(buf.WriteByte(1)) |
400 } | 406 } |
401 _, err = cmpbin.WriteString(buf, i.Kind) | 407 _, err = cmpbin.WriteString(buf, i.Kind) |
402 panicIf(err) | 408 panicIf(err) |
403 if !i.Ancestor { | 409 if !i.Ancestor { |
404 panicIf(buf.WriteByte(0)) | 410 panicIf(buf.WriteByte(0)) |
405 } else { | 411 } else { |
406 panicIf(buf.WriteByte(1)) | 412 panicIf(buf.WriteByte(1)) |
407 } | 413 } |
408 _, err = cmpbin.WriteUint(buf, uint64(len(i.SortBy))) | 414 _, err = cmpbin.WriteUint(buf, uint64(len(i.SortBy))) |
409 panicIf(err) | 415 panicIf(err) |
410 for _, sb := range i.SortBy { | 416 for _, sb := range i.SortBy { |
411 » » panicIf(sb.Write(buf)) | 417 » » panicIf(WriteIndexColumn(buf, sb)) |
412 } | 418 } |
413 return | 419 return |
414 } | 420 } |
415 | 421 |
416 func (i *IndexDefinition) Read(buf Buffer) (err error) { | 422 // ReadIndexDefinition reads an IndexDefinition from the buffer. |
| 423 func ReadIndexDefinition(buf Buffer) (i ds.IndexDefinition, err error) { |
417 defer recoverTo(&err) | 424 defer recoverTo(&err) |
418 | 425 |
419 // discard builtin/complex byte | 426 // discard builtin/complex byte |
420 _, err = buf.ReadByte() | 427 _, err = buf.ReadByte() |
421 panicIf(err) | 428 panicIf(err) |
422 | 429 |
423 i.Kind, _, err = cmpbin.ReadString(buf) | 430 i.Kind, _, err = cmpbin.ReadString(buf) |
424 panicIf(err) | 431 panicIf(err) |
425 | 432 |
426 anc, err := buf.ReadByte() | 433 anc, err := buf.ReadByte() |
427 panicIf(err) | 434 panicIf(err) |
428 | 435 |
429 i.Ancestor = anc == 1 | 436 i.Ancestor = anc == 1 |
430 | 437 |
431 numSorts, _, err := cmpbin.ReadUint(buf) | 438 numSorts, _, err := cmpbin.ReadUint(buf) |
432 panicIf(err) | 439 panicIf(err) |
433 | 440 |
434 if numSorts > MaxIndexColumns { | 441 if numSorts > MaxIndexColumns { |
435 » » return fmt.Errorf("datastore: Got over %d sort orders: %d", | 442 » » err = fmt.Errorf("datastore: Got over %d sort orders: %d", |
436 MaxIndexColumns, numSorts) | 443 MaxIndexColumns, numSorts) |
| 444 return |
437 } | 445 } |
438 | 446 |
439 if numSorts > 0 { | 447 if numSorts > 0 { |
440 » » i.SortBy = make([]IndexColumn, numSorts) | 448 » » i.SortBy = make([]ds.IndexColumn, numSorts) |
441 for idx := range i.SortBy { | 449 for idx := range i.SortBy { |
442 » » » panicIf(i.SortBy[idx].Read(buf)) | 450 » » » i.SortBy[idx], err = ReadIndexColumn(buf) |
| 451 » » » panicIf(err) |
443 } | 452 } |
444 } | 453 } |
445 | 454 |
446 return | 455 return |
447 } | 456 } |
448 | 457 |
| 458 func toBytesErr(i interface{}, ctx KeyContext) (ret []byte, err error) { |
| 459 buf := &bytes.Buffer{} |
| 460 switch x := i.(type) { |
| 461 case ds.GeoPoint: |
| 462 err = WriteGeoPoint(buf, x) |
| 463 |
| 464 case ds.IndexColumn: |
| 465 err = WriteIndexColumn(buf, x) |
| 466 |
| 467 case ds.IndexDefinition: |
| 468 err = WriteIndexDefinition(buf, x) |
| 469 |
| 470 case ds.Key: |
| 471 err = WriteKey(buf, ctx, x) |
| 472 |
| 473 case ds.KeyTok: |
| 474 err = WriteKeyTok(buf, x) |
| 475 |
| 476 case ds.Property: |
| 477 err = WriteProperty(buf, ctx, x) |
| 478 |
| 479 case ds.PropertyMap: |
| 480 err = WritePropertyMap(buf, ctx, x) |
| 481 |
| 482 case time.Time: |
| 483 err = WriteTime(buf, x) |
| 484 |
| 485 default: |
| 486 err = fmt.Errorf("unknown type for ToBytes: %T", i) |
| 487 } |
| 488 if err == nil { |
| 489 ret = buf.Bytes() |
| 490 } |
| 491 return |
| 492 } |
| 493 |
| 494 // ToBytesErr serializes i to a byte slice, if it's one of the type supported |
| 495 // by this library, otherwise it returns an error. |
| 496 // |
| 497 // Key types will be serialized using the 'WithoutContext' option (e.g. their |
| 498 // encoded forms will not contain AppID or Namespace). |
| 499 func ToBytesErr(i interface{}) ([]byte, error) { |
| 500 return toBytesErr(i, WithoutContext) |
| 501 } |
| 502 |
| 503 // ToBytesWithContextErr serializes i to a byte slice, if it's one of the type |
| 504 // supported by this library, otherwise it returns an error. |
| 505 // |
| 506 // Key types will be serialized using the 'WithContext' option (e.g. their |
| 507 // encoded forms will contain AppID and Namespace). |
| 508 func ToBytesWithContextErr(i interface{}) ([]byte, error) { |
| 509 return toBytesErr(i, WithContext) |
| 510 } |
| 511 |
| 512 // ToBytes serializes i to a byte slice, if it's one of the type supported |
| 513 // by this library. If an error is encountered (e.g. `i` is not a supported |
| 514 // type), this method panics. |
| 515 // |
| 516 // Key types will be serialized using the 'WithoutContext' option (e.g. their |
| 517 // encoded forms will not contain AppID or Namespace). |
| 518 func ToBytes(i interface{}) []byte { |
| 519 ret, err := ToBytesErr(i) |
| 520 if err != nil { |
| 521 panic(err) |
| 522 } |
| 523 return ret |
| 524 } |
| 525 |
| 526 // ToBytesWithContext serializes i to a byte slice, if it's one of the type |
| 527 // supported by this library. If an error is encountered (e.g. `i` is not |
| 528 // a supported type), this method panics. |
| 529 // |
| 530 // Key types will be serialized using the 'WithContext' option (e.g. their |
| 531 // encoded forms will not contain AppID or Namespace). |
| 532 func ToBytesWithContext(i interface{}) []byte { |
| 533 ret, err := ToBytesWithContextErr(i) |
| 534 if err != nil { |
| 535 panic(err) |
| 536 } |
| 537 return ret |
| 538 } |
| 539 |
449 type parseError error | 540 type parseError error |
450 | 541 |
451 func panicIf(err error) { | 542 func panicIf(err error) { |
452 if err != nil { | 543 if err != nil { |
453 panic(parseError(err)) | 544 panic(parseError(err)) |
454 } | 545 } |
455 } | 546 } |
456 | 547 |
457 func recoverTo(err *error) { | 548 func recoverTo(err *error) { |
458 if r := recover(); r != nil { | 549 if r := recover(); r != nil { |
459 if rerr := r.(parseError); rerr != nil { | 550 if rerr := r.(parseError); rerr != nil { |
460 *err = error(rerr) | 551 *err = error(rerr) |
461 } | 552 } |
462 } | 553 } |
463 } | 554 } |
OLD | NEW |