| 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 rawdatastore | 5 package rawdatastore |
| 6 | 6 |
| 7 import ( | 7 import ( |
| 8 "bytes" | 8 "bytes" |
| 9 "errors" | 9 "errors" |
| 10 "fmt" | 10 "fmt" |
| (...skipping 133 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 144 ret.IntID, _, err = cmpbin.ReadInt(buf) | 144 ret.IntID, _, err = cmpbin.ReadInt(buf) |
| 145 if err == nil && ret.IntID <= 0 { | 145 if err == nil && ret.IntID <= 0 { |
| 146 err = errors.New("helper: decoded key with empty stringI
D and zero/negative intID") | 146 err = errors.New("helper: decoded key with empty stringI
D and zero/negative intID") |
| 147 } | 147 } |
| 148 default: | 148 default: |
| 149 err = fmt.Errorf("helper: invalid type %s", PropertyType(typ)) | 149 err = fmt.Errorf("helper: invalid type %s", PropertyType(typ)) |
| 150 } | 150 } |
| 151 return | 151 return |
| 152 } | 152 } |
| 153 | 153 |
| 154 // WriteGeoPoint writes a GeoPoint to the buffer. | 154 // Write writes a GeoPoint to the buffer. |
| 155 func WriteGeoPoint(buf Buffer, gp GeoPoint) (err error) { | 155 func (gp GeoPoint) Write(buf Buffer) (err error) { |
| 156 defer recoverTo(&err) | 156 defer recoverTo(&err) |
| 157 _, e := cmpbin.WriteFloat64(buf, gp.Lat) | 157 _, e := cmpbin.WriteFloat64(buf, gp.Lat) |
| 158 panicIf(e) | 158 panicIf(e) |
| 159 _, e = cmpbin.WriteFloat64(buf, gp.Lng) | 159 _, e = cmpbin.WriteFloat64(buf, gp.Lng) |
| 160 return e | 160 return e |
| 161 } | 161 } |
| 162 | 162 |
| 163 // ReadGeoPoint reads a GeoPoint from the buffer. | 163 // Read reads a GeoPoint from the buffer. |
| 164 func ReadGeoPoint(buf Buffer) (gp GeoPoint, err error) { | 164 func (gp *GeoPoint) Read(buf Buffer) (err error) { |
| 165 defer recoverTo(&err) | 165 defer recoverTo(&err) |
| 166 e := error(nil) | 166 e := error(nil) |
| 167 gp.Lat, _, e = cmpbin.ReadFloat64(buf) | 167 gp.Lat, _, e = cmpbin.ReadFloat64(buf) |
| 168 panicIf(e) | 168 panicIf(e) |
| 169 | 169 |
| 170 gp.Lng, _, e = cmpbin.ReadFloat64(buf) | 170 gp.Lng, _, e = cmpbin.ReadFloat64(buf) |
| 171 panicIf(e) | 171 panicIf(e) |
| 172 | 172 |
| 173 if !gp.Valid() { | 173 if !gp.Valid() { |
| 174 err = fmt.Errorf("helper: decoded invalid GeoPoint: %v", gp) | 174 err = fmt.Errorf("helper: decoded invalid GeoPoint: %v", gp) |
| (...skipping 16 matching lines...) Expand all Loading... |
| 191 | 191 |
| 192 // ReadTime reads a time.Time from the buffer. | 192 // ReadTime reads a time.Time from the buffer. |
| 193 func ReadTime(buf Buffer) (time.Time, error) { | 193 func ReadTime(buf Buffer) (time.Time, error) { |
| 194 v, _, err := cmpbin.ReadUint(buf) | 194 v, _, err := cmpbin.ReadUint(buf) |
| 195 if err != nil { | 195 if err != nil { |
| 196 return time.Time{}, err | 196 return time.Time{}, err |
| 197 } | 197 } |
| 198 return time.Unix(int64(v/1e6), int64((v%1e6)*1e3)).UTC(), nil | 198 return time.Unix(int64(v/1e6), int64((v%1e6)*1e3)).UTC(), nil |
| 199 } | 199 } |
| 200 | 200 |
| 201 // WriteProperty writes a Property to the buffer. `context` behaves the same | 201 // Write writes a Property to the buffer. `context` behaves the same |
| 202 // way that it does for WriteKey, but only has an effect if `p` contains a | 202 // way that it does for WriteKey, but only has an effect if `p` contains a |
| 203 // Key as its Value. | 203 // Key as its Value. |
| 204 func WriteProperty(buf Buffer, p Property, context KeyContext) (err error) { | 204 func (p *Property) Write(buf Buffer, context KeyContext) (err error) { |
| 205 defer recoverTo(&err) | 205 defer recoverTo(&err) |
| 206 typb := byte(p.Type()) | 206 typb := byte(p.Type()) |
| 207 if p.IndexSetting() == NoIndex { | 207 if p.IndexSetting() == NoIndex { |
| 208 typb |= 0x80 | 208 typb |= 0x80 |
| 209 } | 209 } |
| 210 panicIf(buf.WriteByte(typb)) | 210 panicIf(buf.WriteByte(typb)) |
| 211 switch p.Type() { | 211 switch p.Type() { |
| 212 case PTNull, PTBoolTrue, PTBoolFalse: | 212 case PTNull, PTBoolTrue, PTBoolFalse: |
| 213 case PTInt: | 213 case PTInt: |
| 214 _, err = cmpbin.WriteInt(buf, p.Value().(int64)) | 214 _, err = cmpbin.WriteInt(buf, p.Value().(int64)) |
| 215 case PTFloat: | 215 case PTFloat: |
| 216 _, err = cmpbin.WriteFloat64(buf, p.Value().(float64)) | 216 _, err = cmpbin.WriteFloat64(buf, p.Value().(float64)) |
| 217 case PTString: | 217 case PTString: |
| 218 _, err = cmpbin.WriteString(buf, p.Value().(string)) | 218 _, err = cmpbin.WriteString(buf, p.Value().(string)) |
| 219 case PTBytes: | 219 case PTBytes: |
| 220 if p.IndexSetting() == NoIndex { | 220 if p.IndexSetting() == NoIndex { |
| 221 _, err = cmpbin.WriteBytes(buf, p.Value().([]byte)) | 221 _, err = cmpbin.WriteBytes(buf, p.Value().([]byte)) |
| 222 } else { | 222 } else { |
| 223 _, err = cmpbin.WriteBytes(buf, p.Value().(ByteString)) | 223 _, err = cmpbin.WriteBytes(buf, p.Value().(ByteString)) |
| 224 } | 224 } |
| 225 case PTTime: | 225 case PTTime: |
| 226 err = WriteTime(buf, p.Value().(time.Time)) | 226 err = WriteTime(buf, p.Value().(time.Time)) |
| 227 case PTGeoPoint: | 227 case PTGeoPoint: |
| 228 » » err = WriteGeoPoint(buf, p.Value().(GeoPoint)) | 228 » » err = p.Value().(GeoPoint).Write(buf) |
| 229 case PTKey: | 229 case PTKey: |
| 230 err = WriteKey(buf, context, p.Value().(Key)) | 230 err = WriteKey(buf, context, p.Value().(Key)) |
| 231 case PTBlobKey: | 231 case PTBlobKey: |
| 232 _, err = cmpbin.WriteString(buf, string(p.Value().(blobstore.Key
))) | 232 _, err = cmpbin.WriteString(buf, string(p.Value().(blobstore.Key
))) |
| 233 } | 233 } |
| 234 return | 234 return |
| 235 } | 235 } |
| 236 | 236 |
| 237 // ReadProperty reads a Property from the buffer. `context`, `appid`, and | 237 // Read reads a Property from the buffer. `context`, `appid`, and |
| 238 // `namespace` behave the same way they do for ReadKey, but only have an | 238 // `namespace` behave the same way they do for ReadKey, but only have an |
| 239 // effect if the decoded property has a Key value. | 239 // effect if the decoded property has a Key value. |
| 240 func ReadProperty(buf Buffer, context KeyContext, appid, namespace string) (p Pr
operty, err error) { | 240 func (p *Property) Read(buf Buffer, context KeyContext, appid, namespace string)
(err error) { |
| 241 val := interface{}(nil) | 241 val := interface{}(nil) |
| 242 typb, err := buf.ReadByte() | 242 typb, err := buf.ReadByte() |
| 243 if err != nil { | 243 if err != nil { |
| 244 return | 244 return |
| 245 } | 245 } |
| 246 is := ShouldIndex | 246 is := ShouldIndex |
| 247 if (typb & 0x80) != 0 { | 247 if (typb & 0x80) != 0 { |
| 248 is = NoIndex | 248 is = NoIndex |
| 249 } | 249 } |
| 250 switch PropertyType(typb & 0x7f) { | 250 switch PropertyType(typb & 0x7f) { |
| (...skipping 14 matching lines...) Expand all Loading... |
| 265 break | 265 break |
| 266 } | 266 } |
| 267 if is == NoIndex { | 267 if is == NoIndex { |
| 268 val = b | 268 val = b |
| 269 } else { | 269 } else { |
| 270 val = ByteString(b) | 270 val = ByteString(b) |
| 271 } | 271 } |
| 272 case PTTime: | 272 case PTTime: |
| 273 val, err = ReadTime(buf) | 273 val, err = ReadTime(buf) |
| 274 case PTGeoPoint: | 274 case PTGeoPoint: |
| 275 » » val, err = ReadGeoPoint(buf) | 275 » » gp := GeoPoint{} |
| 276 » » err = gp.Read(buf) |
| 277 » » val = gp |
| 276 case PTKey: | 278 case PTKey: |
| 277 val, err = ReadKey(buf, context, appid, namespace) | 279 val, err = ReadKey(buf, context, appid, namespace) |
| 278 case PTBlobKey: | 280 case PTBlobKey: |
| 279 s := "" | 281 s := "" |
| 280 if s, _, err = cmpbin.ReadString(buf); err != nil { | 282 if s, _, err = cmpbin.ReadString(buf); err != nil { |
| 281 break | 283 break |
| 282 } | 284 } |
| 283 val = blobstore.Key(s) | 285 val = blobstore.Key(s) |
| 284 default: | 286 default: |
| 285 err = fmt.Errorf("read: unknown type! %v", typb) | 287 err = fmt.Errorf("read: unknown type! %v", typb) |
| 286 } | 288 } |
| 287 if err == nil { | 289 if err == nil { |
| 288 err = p.SetValue(val, is) | 290 err = p.SetValue(val, is) |
| 289 } | 291 } |
| 290 return | 292 return |
| 291 } | 293 } |
| 292 | 294 |
| 293 // WritePropertyMap writes an entire PropertyMap to the buffer. `context` | 295 // Write writes an entire PropertyMap to the buffer. `context` |
| 294 // behaves the same way that it does for WriteKey. If | 296 // behaves the same way that it does for WriteKey. If |
| 295 // WritePropertyMapDeterministic is true, then the rows will be sorted by | 297 // WritePropertyMapDeterministic is true, then the rows will be sorted by |
| 296 // property name before they're serialized to buf (mostly useful for testing, | 298 // property name before they're serialized to buf (mostly useful for testing, |
| 297 // but also potentially useful if you need to make a hash of the property data). | 299 // but also potentially useful if you need to make a hash of the property data). |
| 298 func WritePropertyMap(buf Buffer, propMap PropertyMap, context KeyContext) (err
error) { | 300 func (pm PropertyMap) Write(buf Buffer, context KeyContext) (err error) { |
| 299 defer recoverTo(&err) | 301 defer recoverTo(&err) |
| 300 » rows := make(sort.StringSlice, 0, len(propMap)) | 302 » rows := make(sort.StringSlice, 0, len(pm)) |
| 301 tmpBuf := &bytes.Buffer{} | 303 tmpBuf := &bytes.Buffer{} |
| 302 » for name, vals := range propMap { | 304 » for name, vals := range pm { |
| 303 tmpBuf.Reset() | 305 tmpBuf.Reset() |
| 304 _, e := cmpbin.WriteString(tmpBuf, name) | 306 _, e := cmpbin.WriteString(tmpBuf, name) |
| 305 panicIf(e) | 307 panicIf(e) |
| 306 _, e = cmpbin.WriteUint(tmpBuf, uint64(len(vals))) | 308 _, e = cmpbin.WriteUint(tmpBuf, uint64(len(vals))) |
| 307 panicIf(e) | 309 panicIf(e) |
| 308 for _, p := range vals { | 310 for _, p := range vals { |
| 309 » » » panicIf(WriteProperty(tmpBuf, p, context)) | 311 » » » panicIf(p.Write(tmpBuf, context)) |
| 310 } | 312 } |
| 311 rows = append(rows, tmpBuf.String()) | 313 rows = append(rows, tmpBuf.String()) |
| 312 } | 314 } |
| 313 | 315 |
| 314 if WritePropertyMapDeterministic { | 316 if WritePropertyMapDeterministic { |
| 315 rows.Sort() | 317 rows.Sort() |
| 316 } | 318 } |
| 317 | 319 |
| 318 » _, e := cmpbin.WriteUint(buf, uint64(len(propMap))) | 320 » _, e := cmpbin.WriteUint(buf, uint64(len(pm))) |
| 319 panicIf(e) | 321 panicIf(e) |
| 320 for _, r := range rows { | 322 for _, r := range rows { |
| 321 _, e := buf.WriteString(r) | 323 _, e := buf.WriteString(r) |
| 322 panicIf(e) | 324 panicIf(e) |
| 323 } | 325 } |
| 324 return | 326 return |
| 325 } | 327 } |
| 326 | 328 |
| 327 // ReadPropertyMap reads a PropertyMap from the buffer. `context` and | 329 // Read reads a PropertyMap from the buffer. `context` and |
| 328 // friends behave the same way that they do for ReadKey. | 330 // friends behave the same way that they do for ReadKey. |
| 329 func ReadPropertyMap(buf Buffer, context KeyContext, appid, namespace string) (p
ropMap PropertyMap, err error) { | 331 func (pm PropertyMap) Read(buf Buffer, context KeyContext, appid, namespace stri
ng) (err error) { |
| 330 defer recoverTo(&err) | 332 defer recoverTo(&err) |
| 331 | 333 |
| 332 numRows := uint64(0) | 334 numRows := uint64(0) |
| 333 numRows, _, e := cmpbin.ReadUint(buf) | 335 numRows, _, e := cmpbin.ReadUint(buf) |
| 334 panicIf(e) | 336 panicIf(e) |
| 335 if numRows > ReadPropertyMapReasonableLimit { | 337 if numRows > ReadPropertyMapReasonableLimit { |
| 336 err = fmt.Errorf("helper: tried to decode map with huge number o
f rows %d", numRows) | 338 err = fmt.Errorf("helper: tried to decode map with huge number o
f rows %d", numRows) |
| 337 return | 339 return |
| 338 } | 340 } |
| 339 | 341 |
| 340 name, prop := "", Property{} | 342 name, prop := "", Property{} |
| 341 propMap = make(PropertyMap, numRows) | |
| 342 for i := uint64(0); i < numRows; i++ { | 343 for i := uint64(0); i < numRows; i++ { |
| 343 name, _, e = cmpbin.ReadString(buf) | 344 name, _, e = cmpbin.ReadString(buf) |
| 344 panicIf(e) | 345 panicIf(e) |
| 345 | 346 |
| 346 numProps, _, e := cmpbin.ReadUint(buf) | 347 numProps, _, e := cmpbin.ReadUint(buf) |
| 347 panicIf(e) | 348 panicIf(e) |
| 348 if numProps > ReadPropertyMapReasonableLimit { | 349 if numProps > ReadPropertyMapReasonableLimit { |
| 349 err = fmt.Errorf("helper: tried to decode map with huge
number of properties %d", numProps) | 350 err = fmt.Errorf("helper: tried to decode map with huge
number of properties %d", numProps) |
| 350 return | 351 return |
| 351 } | 352 } |
| 352 props := make([]Property, 0, numProps) | 353 props := make([]Property, 0, numProps) |
| 353 for j := uint64(0); j < numProps; j++ { | 354 for j := uint64(0); j < numProps; j++ { |
| 354 » » » prop, e = ReadProperty(buf, context, appid, namespace) | 355 » » » panicIf(prop.Read(buf, context, appid, namespace)) |
| 355 » » » panicIf(e) | |
| 356 props = append(props, prop) | 356 props = append(props, prop) |
| 357 } | 357 } |
| 358 » » propMap[name] = props | 358 » » pm[name] = props |
| 359 } | 359 } |
| 360 return | 360 return |
| 361 } | 361 } |
| 362 | 362 |
| 363 type parseError error | 363 type parseError error |
| 364 | 364 |
| 365 func panicIf(err error) { | 365 func panicIf(err error) { |
| 366 if err != nil { | 366 if err != nil { |
| 367 panic(parseError(err)) | 367 panic(parseError(err)) |
| 368 } | 368 } |
| 369 } | 369 } |
| 370 | 370 |
| 371 func recoverTo(err *error) { | 371 func recoverTo(err *error) { |
| 372 if r := recover(); r != nil { | 372 if r := recover(); r != nil { |
| 373 if rerr := r.(parseError); rerr != nil { | 373 if rerr := r.(parseError); rerr != nil { |
| 374 *err = error(rerr) | 374 *err = error(rerr) |
| 375 } | 375 } |
| 376 } | 376 } |
| 377 } | 377 } |
| OLD | NEW |