Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(250)

Side by Side Diff: service/datastore/serialize/serialize.go

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

Powered by Google App Engine
This is Rietveld 408576698