| 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 |