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

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: fixes Created 5 years, 7 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 "appengine"
9 "bytes"
10 "fmt"
11 "reflect"
12 "time"
13
14 "appengine/datastore"
15
16 "github.com/luci/luci-go/common/funnybase"
17 )
18
19 type typData struct {
20 typ propValType
21 data interface{}
22 }
23
24 func newTypData(v interface{}) (ret *typData, err error) {
25 typ := pvUNKNOWN
26
27 switch x := v.(type) {
28 case nil:
29 typ = pvNull
30 case time.Time:
31 // toMicro return t.Unix()*1e6 + int64(t.Nanosecond()/1e3)
32 // fromMicro return time.Unix(t/1e6, (t%1e6)*1e3)
33 typ = pvTime
34 case int, int8, int16, int32, int64:
35 typ = pvInt
36 case float32, float64:
37 typ = pvFloat
38 case bool:
39 if x {
40 typ = pvBoolTrue
41 } else {
42 typ = pvBoolFalse
43 }
44 case []byte, datastore.ByteString:
45 typ = pvBytes
46 case appengine.BlobKey:
47 typ = pvBlobKey
48 case string:
49 typ = pvStr
50 case appengine.GeoPoint:
51 typ = pvGeoPoint
52 case *datastore.Key:
53 typ = pvKey
54 }
55 if typ == pvUNKNOWN {
56 err = fmt.Errorf("propValTypeOf: unknown type of %v", v)
M-A Ruel 2015/05/25 18:21:10 %#v
iannucci 2015/05/27 19:33:32 done
57 }
58
59 return &typData{typ, v}, err
60 }
61
62 func (td *typData) WriteBinary(buf *bytes.Buffer) error {
63 buf.WriteByte(byte(td.typ))
64 switch td.typ {
65 case pvNull, pvBoolFalse, pvBoolTrue:
66 return nil
67 case pvInt:
68 funnybase.Write(buf, reflect.ValueOf(td.data).Int())
69 case pvFloat:
70 writeFloat64(buf, reflect.ValueOf(td.data).Float())
71 case pvStr:
72 writeString(buf, td.data.(string))
73 case pvBytes:
74 // []byte or datastore.ByteString
75 b := reflect.ValueOf(td.data).Convert(byteSliceType).Interface() .([]byte)
76 writeBytes(buf, b)
77 case pvTime:
78 t := td.data.(time.Time)
79 funnybase.WriteUint(buf, uint64(t.Unix())*1e6+uint64(t.Nanosecon d()/1e3))
80 case pvGeoPoint:
81 t := td.data.(appengine.GeoPoint)
82 writeFloat64(buf, t.Lat)
83 writeFloat64(buf, t.Lng)
84 case pvKey:
85 writeKey(buf, withNS, td.data.(*datastore.Key))
86 case pvBlobKey:
87 writeString(buf, string(td.data.(appengine.BlobKey)))
88 default:
89 return fmt.Errorf("write: unknown type! %v", td)
90 }
91 return nil
92 }
93
94 func (td *typData) ReadBinary(buf *bytes.Buffer, indexed bool) error {
95 typb, err := buf.ReadByte()
96 if err != nil {
97 return err
98 }
99 td.typ = propValType(typb)
100 switch td.typ {
101 case pvNull:
102 td.data = nil
103 case pvBoolTrue:
104 td.data = true
105 case pvBoolFalse:
106 td.data = false
107 case pvInt:
108 v, err := funnybase.Read(buf)
109 if err != nil {
110 return err
111 }
112 td.data = v
113 case pvFloat:
114 td.data, err = readFloat64(buf)
115 if err != nil {
116 return err
117 }
118 case pvStr:
119 td.data, err = readString(buf)
120 if err != nil {
121 return err
122 }
123 case pvBytes:
124 b, err := readBytes(buf)
125 if err != nil {
126 return err
127 }
128
129 if indexed {
130 td.data = datastore.ByteString(b)
131 } else {
132 td.data = b
133 }
134 case pvTime:
135 v, err := funnybase.ReadUint(buf)
136 if err != nil {
137 return err
138 }
139 td.data = time.Unix(int64(v/1e6), int64((v%1e6)*1e3))
140 case pvGeoPoint:
141 pt := appengine.GeoPoint{}
142 pt.Lat, err = readFloat64(buf)
143 if err != nil {
144 return err
145 }
146 pt.Lng, err = readFloat64(buf)
147 if err != nil {
148 return err
149 }
150 td.data = pt
151 case pvKey:
152 td.data, err = readKey(buf, true)
153 if err != nil {
154 return err
155 }
156 case pvBlobKey:
M-A Ruel 2015/05/25 18:21:10 Do we need to implement this at all?
iannucci 2015/05/27 19:33:32 maybe not... should I just panic for BlobKey's? I'
M-A Ruel 2015/05/27 20:14:47 I don't mind much.
157 s, err := readString(buf)
158 if err != nil {
159 return err
160 }
161 td.data = appengine.BlobKey(s)
162 default:
163 return fmt.Errorf("read: unknown type! %v", td)
164 }
165
166 return nil
167 }
168
169 type pval struct {
170 name string
171 noIndex bool
172 multi bool
173 vals []*typData
174 }
175
176 type propertyList []datastore.Property
177
178 func (pl *propertyList) Load(ch <-chan datastore.Property) error {
179 return (*datastore.PropertyList)(pl).Load(ch)
180 }
181
182 func (pl *propertyList) Save(ch chan<- datastore.Property) error {
183 return (*datastore.PropertyList)(pl).Save(ch)
184 }
185
186 func (pl *propertyList) collate() ([]*pval, error) {
187 if pl == nil || len(*pl) == 0 {
188 return nil, nil
189 }
190
191 cols := []*pval{}
192 colIdx := map[string]int{}
193
194 for _, p := range *pl {
195 var ok bool
196 idx := 0
197 if idx, ok = colIdx[p.Name]; !ok {
M-A Ruel 2015/05/25 18:21:10 if idx, ok := colIdx[p.Name]; !ok { and removing
iannucci 2015/05/27 19:33:32 yay!
198 colIdx[p.Name] = len(cols)
199 td, err := newTypData(p.Value)
200 if err != nil {
201 return nil, err
202 }
203 cols = append(cols, &pval{p.Name, p.NoIndex, p.Multiple, []*typData{td}})
204 } else {
205 c := cols[idx]
206 if c.noIndex != p.NoIndex {
207 return nil, fmt.Errorf(
208 "propertyList.MarshalBinary: field %q ha s conflicting values of NoIndex", p.Name)
209 }
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.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: pv.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 pvKey
331 pvBlobKey
332
333 pvUNKNOWN
334 )
335
336 func (p *pval) ReadBinary(buf *bytes.Buffer) error {
337 prefix, err := buf.ReadByte()
338 if err != nil {
339 return err
340 }
341 p.multi = (prefix & 1) == 1
342 p.noIndex = ((prefix >> 1) & 1) == 1
343 n, err := funnybase.ReadUint(buf)
344 if err != nil {
345 return err
346 }
347
348 p.vals = make([]*typData, n)
349 for i := range p.vals {
350 p.vals[i] = &typData{}
351 err := p.vals[i].ReadBinary(buf, !p.noIndex)
352 if err != nil {
353 return err
354 }
355 }
356
357 return nil
358 }
359
360 func (p *pval) WriteBinary(buf *bytes.Buffer) error {
361 buf.WriteByte(btoi(p.noIndex)<<1 | btoi(p.multi))
362 funnybase.WriteUint(buf, uint64(len(p.vals)))
363 for _, v := range p.vals {
364 err := v.WriteBinary(buf)
365 if err != nil {
366 return err
367 }
368 }
369 return nil
370 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698