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

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: final fixes? 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 func (pl *propertyList) Load(ch <-chan datastore.Property) error {
183 return (*datastore.PropertyList)(pl).Load(ch)
184 }
185
186 func (pl *propertyList) Save(ch chan<- datastore.Property) error {
187 return (*datastore.PropertyList)(pl).Save(ch)
188 }
189
190 func (pl *propertyList) collate() ([]*pval, error) {
191 if pl == nil || len(*pl) == 0 {
192 return nil, nil
193 }
194
195 cols := []*pval{}
196 colIdx := map[string]int{}
197
198 for _, p := range *pl {
199 if idx, ok := colIdx[p.Name]; !ok {
200 colIdx[p.Name] = len(cols)
201 td, err := newTypData(p.NoIndex, p.Value)
202 if err != nil {
203 return nil, err
204 }
205 cols = append(cols, &pval{p.Name, p.Multiple, []*typData {td}})
206 } else {
207 c := cols[idx]
208 if c.multi != p.Multiple {
209 return nil, fmt.Errorf(
210 "propertyList.MarshalBinary: field %q ha s conflicting values of Multiple", p.Name)
211 }
212 td, err := newTypData(p.NoIndex, p.Value)
213 if err != nil {
214 return nil, err
215 }
216 c.vals = append(c.vals, td)
217 }
218 }
219
220 return cols, nil
221 }
222
223 func (pl *propertyList) addCollated(pv *pval) {
224 for _, v := range pv.vals {
225 *pl = append(*pl, datastore.Property{
226 Name: pv.name,
227 Multiple: pv.multi,
228 NoIndex: v.noIndex,
229 Value: v.data,
230 })
231 }
232 }
233
234 func (pl *propertyList) MarshalBinary() ([]byte, error) {
235 cols, err := pl.collate()
236 if err != nil || len(cols) == 0 {
237 return nil, err
238 }
239
240 pieces := make([][]byte, 0, len(*pl)*2+1)
241 for _, pv := range cols {
242 // TODO(riannucci): estimate buffer size better.
243 buf := bytes.NewBuffer(make([]byte, 0, funnybase.MaxFunnyBaseLen 64+len(pv.name)))
244 writeString(buf, pv.name)
245 err := pv.WriteBinary(buf)
246 if err != nil {
247 return nil, err
248 }
249 pieces = append(pieces, buf.Bytes())
250 }
251 return bytes.Join(pieces, nil), nil
252 }
253
254 func (pl *propertyList) UnmarshalBinary(data []byte) error {
255 buf := bytes.NewBuffer(data)
256 for buf.Len() > 0 {
257 name, err := readString(buf)
258 if err != nil {
259 return err
260 }
261
262 pv := &pval{name: name}
263 err = pv.ReadBinary(buf)
264 if err != nil {
265 return err
266 }
267 pl.addCollated(pv)
268 }
269
270 return nil
271 }
272
273 func toPL(src interface{}) (ret *propertyList, err error) {
274 propchan := make(chan datastore.Property)
275 ret = &propertyList{}
276 go func() { err = datastore.SaveStruct(src, propchan) }()
277 err2 := ret.Load(propchan)
278 if err != nil {
279 return
280 }
281 return ret, err2
282 }
283
284 func fromPL(props *propertyList, dst interface{}) (err error) {
285 propchan := make(chan datastore.Property)
286 go func() { err = props.Save(propchan) }()
287 err2 := datastore.LoadStruct(dst, propchan)
288 if err != nil {
289 return err
290 }
291 return err2
292 }
293
294 type propValType byte
295
296 var byteSliceType = reflect.TypeOf([]byte(nil))
297
298 // These constants are in the order described by
299 // https://cloud.google.com/appengine/docs/go/datastore/entities#Go_Value_type _ordering
300 // with a slight divergence for the Int/Time split.
301 const (
302 pvNull propValType = iota
303 pvInt
304
305 // NOTE: this is a slight divergence; times and integers actually sort
306 // together (apparently?) in datastore. This is probably insane, and I d on't
307 // want to add the complexity of field 'meaning' as a sparate concept fr om the
308 // field's 'type' (which is what datastore seems to do, judging from the
309 // protobufs). So if you're here because you implemented an app which re lies
310 // on time.Time and int64 sorting together, then this is why your app ac ts
311 // differently in production. My advice is to NOT DO THAT. If you really want
312 // this (and you probably don't), you should take care of the time.Time <->
313 // int64 conversion in your app and just use a property type of int64.
314 pvTime
315
316 // NOTE: this is also a slight divergence, but not a semantic one. IIUC, in
317 // datastore 'bool' is actually the type and the value is either 0 or
318 // 1 (taking another byte to store). Since we have plenty of space in th is
319 // type byte, I just merge the value into the type for booleans. If this
320 // becomes problematic, consider changing this to just pvBool, and then
321 // encoding a 0 or 1 as a byte in the relevant marshalling routines.
322 pvBoolFalse
323 pvBoolTrue
324 pvBytes // []byte or datastore.ByteString
325 pvStr // string or string noindex
326 pvFloat
327 pvGeoPoint
328
329 // These two are problematic, because they force us to bind to the appen gine
330 // SDK code. If we can drop support for these and turn them into hard er rors,
331 // that could let us decouple from the various appengine SDKs. Maybe.
332 pvKey // TODO(riannucci): remove support for this (use a string)
333 pvBlobKey // TODO(riannucci): remove support for this (use a string)
334
335 pvUNKNOWN
336 )
337
338 func (p *pval) ReadBinary(buf *bytes.Buffer) error {
339 n, err := funnybase.ReadUint(buf)
340 if err != nil {
341 return err
342 }
343 p.multi = n > 1
344
345 p.vals = make([]*typData, n)
346 for i := range p.vals {
347 p.vals[i] = &typData{}
348 err := p.vals[i].ReadBinary(buf)
349 if err != nil {
350 return err
351 }
352 }
353
354 return nil
355 }
356
357 func (p *pval) WriteBinary(buf *bytes.Buffer) error {
358 funnybase.WriteUint(buf, uint64(len(p.vals)))
359 for _, v := range p.vals {
360 err := v.WriteBinary(buf)
M-A Ruel 2015/05/29 02:01:23 merge lines
iannucci 2015/05/29 02:42:29 done.....
361 if err != nil {
362 return err
363 }
364 }
365 return nil
366 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698