OLD | NEW |
1 // Copyright 2015 The LUCI Authors. All rights reserved. | 1 // Copyright 2015 The LUCI Authors. All rights reserved. |
2 // Use of this source code is governed under the Apache License, Version 2.0 | 2 // Use of this source code is governed under the Apache License, Version 2.0 |
3 // that can be found in the LICENSE file. | 3 // that can be 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" |
(...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
68 panicIf(buf.WriteByte(1)) | 68 panicIf(buf.WriteByte(1)) |
69 panicIf(WriteKeyTok(buf, tok)) | 69 panicIf(WriteKeyTok(buf, tok)) |
70 } | 70 } |
71 return buf.WriteByte(0) | 71 return buf.WriteByte(0) |
72 } | 72 } |
73 | 73 |
74 // 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 |
75 // 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. |
76 // If context == WithoutContext, then the appid and namespace parameters are | 76 // If context == WithoutContext, then the appid and namespace parameters are |
77 // used in the decoded Key. Otherwise they're ignored. | 77 // used in the decoded Key. Otherwise they're ignored. |
78 func ReadKey(buf Buffer, context KeyContext, appid, namespace string) (ret *ds.K
ey, err error) { | 78 func ReadKey(buf Buffer, context KeyContext, inKC ds.KeyContext) (ret *ds.Key, e
rr error) { |
79 defer recoverTo(&err) | 79 defer recoverTo(&err) |
80 actualCtx, e := buf.ReadByte() | 80 actualCtx, e := buf.ReadByte() |
81 panicIf(e) | 81 panicIf(e) |
82 | 82 |
83 » actualAid, actualNS := "", "" | 83 » var kc ds.KeyContext |
84 if actualCtx == 1 { | 84 if actualCtx == 1 { |
85 » » actualAid, _, e = cmpbin.ReadString(buf) | 85 » » kc.AppID, _, e = cmpbin.ReadString(buf) |
86 panicIf(e) | 86 panicIf(e) |
87 » » actualNS, _, e = cmpbin.ReadString(buf) | 87 » » kc.Namespace, _, e = cmpbin.ReadString(buf) |
88 panicIf(e) | 88 panicIf(e) |
89 } else if actualCtx != 0 { | 89 } else if actualCtx != 0 { |
90 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) |
91 return | 91 return |
92 } | 92 } |
93 | 93 |
94 if context == WithoutContext { | 94 if context == WithoutContext { |
95 // overrwrite with the supplied ones | 95 // overrwrite with the supplied ones |
96 » » actualAid = appid | 96 » » kc = inKC |
97 » » actualNS = namespace | |
98 } | 97 } |
99 | 98 |
100 toks := []ds.KeyTok{} | 99 toks := []ds.KeyTok{} |
101 for { | 100 for { |
102 ctrlByte, e := buf.ReadByte() | 101 ctrlByte, e := buf.ReadByte() |
103 panicIf(e) | 102 panicIf(e) |
104 if ctrlByte == 0 { | 103 if ctrlByte == 0 { |
105 break | 104 break |
106 } | 105 } |
107 if len(toks)+1 > ReadKeyNumToksReasonableLimit { | 106 if len(toks)+1 > ReadKeyNumToksReasonableLimit { |
108 err = fmt.Errorf( | 107 err = fmt.Errorf( |
109 "helper: tried to decode huge key with > %d toke
ns", | 108 "helper: tried to decode huge key with > %d toke
ns", |
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 ds.NewKeyToks(actualAid, actualNS, toks), nil | 119 » return kc.NewKeyToks(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 139 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
270 err = WriteGeoPoint(buf, t) | 269 err = WriteGeoPoint(buf, t) |
271 case *ds.Key: | 270 case *ds.Key: |
272 err = WriteKey(buf, context, t) | 271 err = WriteKey(buf, context, t) |
273 | 272 |
274 default: | 273 default: |
275 err = fmt.Errorf("unsupported type: %T", t) | 274 err = fmt.Errorf("unsupported type: %T", t) |
276 } | 275 } |
277 return | 276 return |
278 } | 277 } |
279 | 278 |
280 // ReadProperty reads a Property from the buffer. `context`, `appid`, and | 279 // ReadProperty reads a Property from the buffer. `context` and `kc` behave the |
281 // `namespace` behave the same way they do for ReadKey, but only have an | 280 // same way they do for ReadKey, but only have an effect if the decoded property |
282 // effect if the decoded property has a Key value. | 281 // has a Key value. |
283 func ReadProperty(buf Buffer, context KeyContext, appid, namespace string) (p ds
.Property, err error) { | 282 func ReadProperty(buf Buffer, context KeyContext, kc ds.KeyContext) (p ds.Proper
ty, err error) { |
284 val := interface{}(nil) | 283 val := interface{}(nil) |
285 b, err := buf.ReadByte() | 284 b, err := buf.ReadByte() |
286 if err != nil { | 285 if err != nil { |
287 return | 286 return |
288 } | 287 } |
289 is := ds.ShouldIndex | 288 is := ds.ShouldIndex |
290 if (b & 0x80) == 0 { | 289 if (b & 0x80) == 0 { |
291 is = ds.NoIndex | 290 is = ds.NoIndex |
292 } | 291 } |
293 switch ds.PropertyType(b & 0x7f) { | 292 switch ds.PropertyType(b & 0x7f) { |
294 case ds.PTNull: | 293 case ds.PTNull: |
295 case ds.PTBool: | 294 case ds.PTBool: |
296 b, err = buf.ReadByte() | 295 b, err = buf.ReadByte() |
297 val = (b != 0) | 296 val = (b != 0) |
298 case ds.PTInt: | 297 case ds.PTInt: |
299 val, _, err = cmpbin.ReadInt(buf) | 298 val, _, err = cmpbin.ReadInt(buf) |
300 case ds.PTFloat: | 299 case ds.PTFloat: |
301 val, _, err = cmpbin.ReadFloat64(buf) | 300 val, _, err = cmpbin.ReadFloat64(buf) |
302 case ds.PTString: | 301 case ds.PTString: |
303 val, _, err = cmpbin.ReadString(buf) | 302 val, _, err = cmpbin.ReadString(buf) |
304 case ds.PTBytes: | 303 case ds.PTBytes: |
305 val, _, err = cmpbin.ReadBytes(buf) | 304 val, _, err = cmpbin.ReadBytes(buf) |
306 case ds.PTTime: | 305 case ds.PTTime: |
307 val, err = ReadTime(buf) | 306 val, err = ReadTime(buf) |
308 case ds.PTGeoPoint: | 307 case ds.PTGeoPoint: |
309 val, err = ReadGeoPoint(buf) | 308 val, err = ReadGeoPoint(buf) |
310 case ds.PTKey: | 309 case ds.PTKey: |
311 » » val, err = ReadKey(buf, context, appid, namespace) | 310 » » val, err = ReadKey(buf, context, kc) |
312 case ds.PTBlobKey: | 311 case ds.PTBlobKey: |
313 s := "" | 312 s := "" |
314 if s, _, err = cmpbin.ReadString(buf); err != nil { | 313 if s, _, err = cmpbin.ReadString(buf); err != nil { |
315 break | 314 break |
316 } | 315 } |
317 val = blobstore.Key(s) | 316 val = blobstore.Key(s) |
318 default: | 317 default: |
319 err = fmt.Errorf("read: unknown type! %v", b) | 318 err = fmt.Errorf("read: unknown type! %v", b) |
320 } | 319 } |
321 if err == nil { | 320 if err == nil { |
(...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
369 panicIf(e) | 368 panicIf(e) |
370 for _, r := range rows { | 369 for _, r := range rows { |
371 _, e := buf.WriteString(r) | 370 _, e := buf.WriteString(r) |
372 panicIf(e) | 371 panicIf(e) |
373 } | 372 } |
374 return | 373 return |
375 } | 374 } |
376 | 375 |
377 // ReadPropertyMap reads a PropertyMap from the buffer. `context` and | 376 // ReadPropertyMap reads a PropertyMap from the buffer. `context` and |
378 // friends behave the same way that they do for ReadKey. | 377 // friends behave the same way that they do for ReadKey. |
379 func ReadPropertyMap(buf Buffer, context KeyContext, appid, namespace string) (p
m ds.PropertyMap, err error) { | 378 func ReadPropertyMap(buf Buffer, context KeyContext, kc ds.KeyContext) (pm ds.Pr
opertyMap, err error) { |
380 defer recoverTo(&err) | 379 defer recoverTo(&err) |
381 | 380 |
382 numRows := uint64(0) | 381 numRows := uint64(0) |
383 numRows, _, e := cmpbin.ReadUint(buf) | 382 numRows, _, e := cmpbin.ReadUint(buf) |
384 panicIf(e) | 383 panicIf(e) |
385 if numRows > ReadPropertyMapReasonableLimit { | 384 if numRows > ReadPropertyMapReasonableLimit { |
386 err = fmt.Errorf("helper: tried to decode map with huge number o
f rows %d", numRows) | 385 err = fmt.Errorf("helper: tried to decode map with huge number o
f rows %d", numRows) |
387 return | 386 return |
388 } | 387 } |
389 | 388 |
390 pm = make(ds.PropertyMap, numRows) | 389 pm = make(ds.PropertyMap, numRows) |
391 | 390 |
392 name, prop := "", ds.Property{} | 391 name, prop := "", ds.Property{} |
393 for i := uint64(0); i < numRows; i++ { | 392 for i := uint64(0); i < numRows; i++ { |
394 name, _, e = cmpbin.ReadString(buf) | 393 name, _, e = cmpbin.ReadString(buf) |
395 panicIf(e) | 394 panicIf(e) |
396 | 395 |
397 numProps, _, e := cmpbin.ReadInt(buf) | 396 numProps, _, e := cmpbin.ReadInt(buf) |
398 panicIf(e) | 397 panicIf(e) |
399 switch { | 398 switch { |
400 case numProps < 0: | 399 case numProps < 0: |
401 // Single property. | 400 // Single property. |
402 » » » prop, err = ReadProperty(buf, context, appid, namespace) | 401 » » » prop, err = ReadProperty(buf, context, kc) |
403 panicIf(err) | 402 panicIf(err) |
404 pm[name] = prop | 403 pm[name] = prop |
405 | 404 |
406 case uint64(numProps) > ReadPropertyMapReasonableLimit: | 405 case uint64(numProps) > ReadPropertyMapReasonableLimit: |
407 err = fmt.Errorf("helper: tried to decode map with huge
number of properties %d", numProps) | 406 err = fmt.Errorf("helper: tried to decode map with huge
number of properties %d", numProps) |
408 return | 407 return |
409 | 408 |
410 default: | 409 default: |
411 props := make(ds.PropertySlice, 0, numProps) | 410 props := make(ds.PropertySlice, 0, numProps) |
412 for j := int64(0); j < numProps; j++ { | 411 for j := int64(0); j < numProps; j++ { |
413 » » » » prop, err = ReadProperty(buf, context, appid, na
mespace) | 412 » » » » prop, err = ReadProperty(buf, context, kc) |
414 panicIf(err) | 413 panicIf(err) |
415 props = append(props, prop) | 414 props = append(props, prop) |
416 } | 415 } |
417 pm[name] = props | 416 pm[name] = props |
418 } | 417 } |
419 } | 418 } |
420 return | 419 return |
421 } | 420 } |
422 | 421 |
423 // WriteIndexColumn writes an IndexColumn to the buffer. | 422 // WriteIndexColumn writes an IndexColumn to the buffer. |
(...skipping 211 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
635 } | 634 } |
636 } | 635 } |
637 | 636 |
638 func recoverTo(err *error) { | 637 func recoverTo(err *error) { |
639 if r := recover(); r != nil { | 638 if r := recover(); r != nil { |
640 if rerr := r.(parseError); rerr != nil { | 639 if rerr := r.(parseError); rerr != nil { |
641 *err = error(rerr) | 640 *err = error(rerr) |
642 } | 641 } |
643 } | 642 } |
644 } | 643 } |
OLD | NEW |