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

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: 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
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
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
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 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698