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

Side by Side Diff: go/src/infra/gae/libs/wrapper/memory/plist.go

Issue 1152383003: Simple memory testing for gae/wrapper (Closed) Base URL: https://chromium.googlesource.com/infra/infra.git@better_context_lite
Patch Set: add go-slab dependency Created 5 years, 6 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
(Empty)
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
3 // found in the LICENSE file.
4
5 package memory
6
7 import (
8 "bytes"
9 "fmt"
10 "reflect"
11 "time"
12
13 "github.com/luci/luci-go/common/funnybase"
14
15 "appengine"
16 "appengine/datastore"
17 )
18
19 type typData struct {
20 noIndex bool
21 typ propValType
22 data interface{}
23 }
24
25 func newTypData(noIndex bool, v interface{}) (ret *typData, err error) {
26 typ := pvUNKNOWN
27
28 switch x := v.(type) {
29 case nil:
30 typ = pvNull
31 case time.Time:
32 typ = pvTime
33 case int64:
34 typ = pvInt
35 case float64:
36 typ = pvFloat
37 case bool:
38 if x {
39 typ = pvBoolTrue
40 } else {
41 typ = pvBoolFalse
42 }
43 case []byte, datastore.ByteString:
44 typ = pvBytes
45 case appengine.BlobKey:
46 typ = pvBlobKey
47 case string:
48 typ = pvStr
49 case appengine.GeoPoint:
50 typ = pvGeoPoint
51 case *datastore.Key:
52 typ = pvKey
53 }
54 if typ == pvUNKNOWN {
55 err = fmt.Errorf("propValTypeOf: unknown type of %#v", v)
56 }
57
58 return &typData{noIndex, typ, v}, err
59 }
60
61 func (td *typData) WriteBinary(buf *bytes.Buffer) error {
62 typb := byte(td.typ)
63 if td.noIndex {
64 typb |= 0x80
65 }
66 buf.WriteByte(typb)
67 switch td.typ {
68 case pvNull, pvBoolFalse, pvBoolTrue:
69 return nil
70 case pvInt:
71 funnybase.Write(buf, td.data.(int64))
72 case pvFloat:
73 writeFloat64(buf, td.data.(float64))
74 case pvStr:
75 writeString(buf, td.data.(string))
76 case pvBytes:
77 if td.noIndex {
78 writeBytes(buf, td.data.([]byte))
79 } else {
80 writeBytes(buf, td.data.(datastore.ByteString))
81 }
82 case pvTime:
83 t := td.data.(time.Time)
84 funnybase.WriteUint(buf, uint64(t.Unix())*1e6+uint64(t.Nanosecon d()/1e3))
85 case pvGeoPoint:
86 t := td.data.(appengine.GeoPoint)
87 writeFloat64(buf, t.Lat)
88 writeFloat64(buf, t.Lng)
89 case pvKey:
90 writeKey(buf, withNS, td.data.(*datastore.Key))
91 case pvBlobKey:
92 writeString(buf, string(td.data.(appengine.BlobKey)))
93 default:
94 return fmt.Errorf("write: unknown type! %v", td)
95 }
96 return nil
97 }
98
99 func (td *typData) ReadBinary(buf *bytes.Buffer) error {
100 typb, err := buf.ReadByte()
101 if err != nil {
102 return err
103 }
104 td.noIndex = (typb & 0x80) != 0 // highbit means noindex
105 td.typ = propValType(typb & 0x7f)
106 switch td.typ {
107 case pvNull:
108 td.data = nil
109 case pvBoolTrue:
110 td.data = true
111 case pvBoolFalse:
112 td.data = false
113 case pvInt:
114 v, err := funnybase.Read(buf)
115 if err != nil {
116 return err
117 }
118 td.data = v
119 case pvFloat:
120 td.data, err = readFloat64(buf)
121 if err != nil {
122 return err
123 }
124 case pvStr:
125 td.data, err = readString(buf)
126 if err != nil {
127 return err
128 }
129 case pvBytes:
130 b, err := readBytes(buf)
131 if err != nil {
132 return err
133 }
134 if td.noIndex {
135 td.data = b
136 } else {
137 td.data = datastore.ByteString(b)
138 }
139 case pvTime:
140 v, err := funnybase.ReadUint(buf)
141 if err != nil {
142 return err
143 }
144 td.data = time.Unix(int64(v/1e6), int64((v%1e6)*1e3))
145 case pvGeoPoint:
146 pt := appengine.GeoPoint{}
147 pt.Lat, err = readFloat64(buf)
148 if err != nil {
149 return err
150 }
151 pt.Lng, err = readFloat64(buf)
152 if err != nil {
153 return err
154 }
155 td.data = pt
156 case pvKey:
157 td.data, err = readKey(buf, true)
158 if err != nil {
159 return err
160 }
161 case pvBlobKey:
162 s, err := readString(buf)
163 if err != nil {
164 return err
165 }
166 td.data = appengine.BlobKey(s)
167 default:
168 return fmt.Errorf("read: unknown type! %v", td)
169 }
170
171 return nil
172 }
173
174 type pval struct {
175 name string
176 multi bool
177 vals []*typData
178 }
179
180 type propertyList []datastore.Property
181
182 var _ = datastore.PropertyLoadSaver((*propertyList)(nil))
183
184 func (pl *propertyList) Load(ch <-chan datastore.Property) error {
185 return (*datastore.PropertyList)(pl).Load(ch)
186 }
187
188 func (pl *propertyList) Save(ch chan<- datastore.Property) error {
189 return (*datastore.PropertyList)(pl).Save(ch)
190 }
191
192 func (pl *propertyList) collate() ([]*pval, error) {
193 if pl == nil || len(*pl) == 0 {
194 return nil, nil
195 }
196
197 cols := []*pval{}
198 colIdx := map[string]int{}
199
200 for _, p := range *pl {
201 if idx, ok := colIdx[p.Name]; !ok {
202 colIdx[p.Name] = len(cols)
203 td, err := newTypData(p.NoIndex, p.Value)
204 if err != nil {
205 return nil, err
206 }
207 cols = append(cols, &pval{p.Name, p.Multiple, []*typData {td}})
208 } else {
209 c := cols[idx]
210 if c.multi != p.Multiple {
211 return nil, fmt.Errorf(
212 "propertyList.MarshalBinary: field %q ha s conflicting values of Multiple", p.Name)
213 }
214 td, err := newTypData(p.NoIndex, p.Value)
215 if err != nil {
216 return nil, err
217 }
218 c.vals = append(c.vals, td)
219 }
220 }
221
222 return cols, nil
223 }
224
225 func (pl *propertyList) addCollated(pv *pval) {
226 for _, v := range pv.vals {
227 *pl = append(*pl, datastore.Property{
228 Name: pv.name,
229 Multiple: pv.multi,
230 NoIndex: v.noIndex,
231 Value: v.data,
232 })
233 }
234 }
235
236 func (pl *propertyList) MarshalBinary() ([]byte, error) {
237 cols, err := pl.collate()
238 if err != nil || len(cols) == 0 {
239 return nil, err
240 }
241
242 pieces := make([][]byte, 0, len(*pl)*2+1)
243 for _, pv := range cols {
244 // TODO(riannucci): estimate buffer size better.
245 buf := bytes.NewBuffer(make([]byte, 0, funnybase.MaxFunnyBaseLen 64+len(pv.name)))
246 writeString(buf, pv.name)
247 err := pv.WriteBinary(buf)
248 if err != nil {
249 return nil, err
250 }
251 pieces = append(pieces, buf.Bytes())
252 }
253 return bytes.Join(pieces, nil), nil
254 }
255
256 func (pl *propertyList) UnmarshalBinary(data []byte) error {
257 buf := bytes.NewBuffer(data)
258 for buf.Len() > 0 {
259 name, err := readString(buf)
260 if err != nil {
261 return err
262 }
263
264 pv := &pval{name: name}
265 err = pv.ReadBinary(buf)
266 if err != nil {
267 return err
268 }
269 pl.addCollated(pv)
270 }
271
272 return nil
273 }
274
275 func toPL(src interface{}) (ret *propertyList, err error) {
276 propchan := make(chan datastore.Property)
277 ret = &propertyList{}
278 go func() { err = datastore.SaveStruct(src, propchan) }()
279 err2 := ret.Load(propchan)
280 if err != nil {
281 return
282 }
283 return ret, err2
284 }
285
286 func fromPL(props *propertyList, dst interface{}) (err error) {
287 propchan := make(chan datastore.Property)
288 go func() { err = props.Save(propchan) }()
289 err2 := datastore.LoadStruct(dst, propchan)
290 if err != nil {
291 return err
292 }
293 return err2
294 }
295
296 type propValType byte
297
298 var byteSliceType = reflect.TypeOf([]byte(nil))
299
300 // These constants are in the order described by
301 // https://cloud.google.com/appengine/docs/go/datastore/entities#Go_Value_type _ordering
302 // with a slight divergence for the Int/Time split.
303 const (
304 pvNull propValType = iota
305 pvInt
306
307 // NOTE: this is a slight divergence; times and integers actually sort
308 // together (apparently?) in datastore. This is probably insane, and I d on't
309 // want to add the complexity of field 'meaning' as a sparate concept fr om the
310 // field's 'type' (which is what datastore seems to do, judging from the
311 // protobufs). So if you're here because you implemented an app which re lies
312 // on time.Time and int64 sorting together, then this is why your app ac ts
313 // differently in production. My advice is to NOT DO THAT. If you really want
314 // this (and you probably don't), you should take care of the time.Time <->
315 // int64 conversion in your app and just use a property type of int64.
316 pvTime
317
318 // NOTE: this is also a slight divergence, but not a semantic one. IIUC, in
319 // datastore 'bool' is actually the type and the value is either 0 or
320 // 1 (taking another byte to store). Since we have plenty of space in th is
321 // type byte, I just merge the value into the type for booleans. If this
322 // becomes problematic, consider changing this to just pvBool, and then
323 // encoding a 0 or 1 as a byte in the relevant marshalling routines.
324 pvBoolFalse
325 pvBoolTrue
326 pvBytes // []byte or datastore.ByteString
327 pvStr // string or string noindex
328 pvFloat
329 pvGeoPoint
330
331 // These two are problematic, because they force us to bind to the appen gine
332 // SDK code. If we can drop support for these and turn them into hard er rors,
333 // that could let us decouple from the various appengine SDKs. Maybe.
334 pvKey // TODO(riannucci): remove support for this (use a string)
335 pvBlobKey // TODO(riannucci): remove support for this (use a string)
336
337 pvUNKNOWN
338 )
339
340 func (p *pval) ReadBinary(buf *bytes.Buffer) error {
341 n, err := funnybase.ReadUint(buf)
342 if err != nil {
343 return err
344 }
345 p.multi = n > 1
346
347 p.vals = make([]*typData, n)
348 for i := range p.vals {
349 p.vals[i] = &typData{}
350 err := p.vals[i].ReadBinary(buf)
351 if err != nil {
352 return err
353 }
354 }
355
356 return nil
357 }
358
359 func (p *pval) WriteBinary(buf *bytes.Buffer) error {
360 funnybase.WriteUint(buf, uint64(len(p.vals)))
361 for _, v := range p.vals {
362 if err := v.WriteBinary(buf); err != nil {
363 return err
364 }
365 }
366 return nil
367 }
OLDNEW
« no previous file with comments | « go/src/infra/gae/libs/wrapper/memory/memory.infra_testing ('k') | go/src/infra/gae/libs/wrapper/memory/plist_test.go » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698