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

Side by Side Diff: go/src/infra/gae/libs/wrapper/memory/key.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, 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 "fmt"
9
M-A Ruel 2015/05/25 18:21:10 group stdlib. seriously, I prefer goimports native
iannucci 2015/05/27 19:33:32 done
10 "bytes"
11
12 "github.com/luci/luci-go/common/funnybase"
13
14 "appengine/datastore"
15 "appengine_internal"
16 basepb "appengine_internal/base"
17 "github.com/golang/protobuf/proto"
18
19 "github.com/mjibson/goon"
20 )
21
22 ///////////////////////////// fakeGAECtxForNewKey //////////////////////////////
23
24 type fakeGAECtxForNewKey string
25
26 func (fakeGAECtxForNewKey) Debugf(format string, args ...interface{}) {}
27 func (fakeGAECtxForNewKey) Infof(format string, args ...interface{}) {}
28 func (fakeGAECtxForNewKey) Warningf(format string, args ...interface{}) {}
29 func (fakeGAECtxForNewKey) Errorf(format string, args ...interface{}) {}
30 func (fakeGAECtxForNewKey) Criticalf(format string, args ...interface{}) {}
31 func (fakeGAECtxForNewKey) Request() interface{} { retur n nil }
32 func (f fakeGAECtxForNewKey) Call(service, method string, in, out appengine_inte rnal.ProtoMessage, opts *appengine_internal.CallOptions) error {
33 if service != "__go__" || method != "GetNamespace" {
34 panic(fmt.Errorf("fakeGAECtxForNewKey: cannot facilitate Call(%q , %q, ...)", service, method))
35 }
36 out.(*basepb.StringProto).Value = proto.String(string(f))
37 return nil
38 }
39 func (fakeGAECtxForNewKey) FullyQualifiedAppID() string { return "dev~my~app" }
40
41 /////////////////////////////// Key construction ///////////////////////////////
42
43 func newKey(ns, kind, stringID string, intID int64, parent *datastore.Key) *data store.Key {
44 return datastore.NewKey(fakeGAECtxForNewKey(ns), kind, stringID, intID, parent)
45 }
46 func newKeyObjError(ns string, knr goon.KindNameResolver, src interface{}) (*dat astore.Key, error) {
47 return (&goon.Goon{
48 Context: fakeGAECtxForNewKey(ns),
49 KindNameResolver: knr}).KeyError(src)
50 }
51 func newKeyObj(ns string, knr goon.KindNameResolver, obj interface{}) *datastore .Key {
52 k, err := newKeyObjError(ns, knr, obj)
53 if err != nil {
54 panic(err)
55 }
56 return k
57 }
58 func kind(ns string, knr goon.KindNameResolver, src interface{}) string {
59 return newKeyObj(ns, knr, src).Kind()
60 }
61
62 /////////////////////////////// Binary Encoding ////////////////////////////////
63
64 type keyTok struct {
65 kind string
66 intID uint64
67 stringID string
68 }
69
70 func keyToToks(key *datastore.Key) (namespace string, ret []*keyTok) {
71 var inner func(*datastore.Key)
72 inner = func(k *datastore.Key) {
73 if k.Parent() != nil {
74 inner(k.Parent())
75 }
76 ret = append(ret, &keyTok{
M-A Ruel 2015/05/25 18:21:10 put on one line
iannucci 2015/05/27 19:33:32 find/replace fail. done
77 k.Kind(), uint64(k.IntID()), k.StringID()})
78 }
79 inner(key)
80 namespace = key.Namespace()
81 return
82 }
83
84 func toksToKey(ns string, toks []*keyTok) *datastore.Key {
M-A Ruel 2015/05/25 18:21:10 Since you use a named return value in the other me
iannucci 2015/05/27 19:33:32 plus 4 characters! :P Done
85 ret := (*datastore.Key)(nil)
86 for _, t := range toks {
87 ret = newKey(ns, t.kind, t.stringID, int64(t.intID), ret)
88 }
89 return ret
90 }
91
92 type nsOption bool
93
94 const (
95 withNS nsOption = iota == 0
M-A Ruel 2015/05/25 18:21:10 Too clever for no value, please use true and false
iannucci 2015/05/27 19:33:32 (technically, it's for TWO values.... :D) done
96 noNS
97 )
98
99 func keyBytes(nso nsOption, k *datastore.Key) []byte {
100 buf := &bytes.Buffer{}
101 writeKey(buf, nso, k)
102 return buf.Bytes()
103 }
104
105 func keyFromBytes(nso nsOption, d []byte) (*datastore.Key, error) {
106 return readKey(bytes.NewBuffer(d), nso)
107 }
108 func keyFromByteString(nso nsOption, d string) (*datastore.Key, error) {
M-A Ruel 2015/05/25 18:21:09 I'd prefer you to only keep one of the 2 functions
iannucci 2015/05/27 19:33:32 turns out I only used one.
109 return readKey(bytes.NewBufferString(d), nso)
110 }
111
112 func writeKey(buf *bytes.Buffer, nso nsOption, k *datastore.Key) {
113 // namespace ++ #tokens ++ [tok.kind ++ tok.stringID ++ tok.intID?]*
114 namespace, toks := keyToToks(k)
115 if nso == withNS {
116 writeString(buf, namespace)
117 }
118 funnybase.WriteUint(buf, uint64(len(toks)))
119 for _, tok := range toks {
120 writeString(buf, tok.kind)
121 writeString(buf, tok.stringID)
122 if tok.stringID == "" {
123 funnybase.WriteUint(buf, tok.intID)
124 }
125 }
126 }
127
128 func readKey(buf *bytes.Buffer, nso nsOption) (*datastore.Key, error) {
129 var err error
M-A Ruel 2015/05/25 18:21:10 Not needed
iannucci 2015/05/27 19:33:32 moved into the if block to make its use more clear
130 toks := []*keyTok{}
M-A Ruel 2015/05/25 18:21:09 Move to line 143. Make it toks := make([]*keyTok,
131 namespace := ""
132 if nso == withNS {
133 namespace, err = readString(buf)
134 if err != nil {
135 return nil, err
136 }
137 }
138
139 numToks, err := funnybase.ReadUint(buf)
M-A Ruel 2015/05/25 18:21:09 You should ensure numToks has a reasonable limit
iannucci 2015/05/27 19:33:32 done
140 if err != nil {
141 return nil, err
142 }
143
144 for i := uint64(0); i < numToks; i++ {
145 tok := &keyTok{}
146 tok.kind, err = readString(buf)
147 if err != nil {
148 return nil, err
149 }
150 tok.stringID, err = readString(buf)
151 if err != nil {
152 return nil, err
153 }
154 if tok.stringID == "" {
155 tok.intID, err = funnybase.ReadUint(buf)
M-A Ruel 2015/05/25 18:21:10 You should ensure intID is positive, non zero.
iannucci 2015/05/27 19:33:32 done
156 if err != nil {
157 return nil, err
158 }
159 }
160 toks = append(toks, tok)
161 }
162
163 return toksToKey(namespace, toks), nil
164 }
165
166 //////////////////////////////// Key utilities /////////////////////////////////
167
168 func rootKey(key *datastore.Key) *datastore.Key {
169 for key.Parent() != nil {
170 key = key.Parent()
171 }
172 return key
173 }
174
175 type keyValidOption bool
176
177 const (
178 // UserKeyOnly is used with KeyValid, and ensures that the key is only o ne
179 // that's valid for a user program to write to.
180 UserKeyOnly keyValidOption = iota == 0
M-A Ruel 2015/05/25 18:21:10 too clever again
iannucci 2015/05/27 19:33:32 done
181
182 // AllowSpecialKeys is used with KeyValid, and allows keys for special
183 // metadata objects (like "__entity_group__").
184 AllowSpecialKeys
185 )
186
187 // KeyValid checks to see if a key is valid by applying a bunch of constraint
188 // rules to it (e.g. can't have StringID and IntID set at the same time, can't
189 // have a parent key which is Incomplete(), etc.)
190 //
191 // It verifies that the key is also in the provided namespace. It can also
192 // reject keys which are 'special' e.g. have a Kind starting with "__". This
193 // behavior is controllable with opt.
194 func KeyValid(ns string, k *datastore.Key, opt keyValidOption) bool {
195 // copied from the appengine SDK because why would any user program need to
196 // see if a key is valid?
197 if k == nil {
198 return false
199 }
200 // since we do "client-side" validataion of namespaces, check this here.
201 if k.Namespace() != ns {
202 return false
203 }
204 for ; k != nil; k = k.Parent() {
205 if opt == UserKeyOnly && len(k.Kind()) >= 2 && k.Kind()[:2] == " __" { // reserve all Kinds starting with __
206 return false
207 }
208 if k.Kind() == "" || k.AppID() == "" {
209 return false
210 }
211 if k.StringID() != "" && k.IntID() != 0 {
212 return false
213 }
214 if k.Parent() != nil {
215 if k.Parent().Incomplete() {
216 return false
217 }
218 if k.Parent().AppID() != k.AppID() || k.Parent().Namespa ce() != k.Namespace() {
219 return false
220 }
221 }
222 }
223 return true
224 }
225
226 // KeyCouldBeValid is like KeyValid, but it allows for the possibility that the
227 // last token of the key is Incomplete(). It returns true if the Key will become
228 // valid once it recieves an automatically-assigned ID.
229 func KeyCouldBeValid(ns string, k *datastore.Key, opt keyValidOption) bool {
230 // adds an id to k if it's incomplete.
231 testKey := k
232 if k.Incomplete() {
233 testKey = newKey(ns, k.Kind(), "", 1, k.Parent())
M-A Ruel 2015/05/25 18:21:10 k = newKey(ns, k.Kind(), "", 1, k.Parent()) you d
iannucci 2015/05/27 19:33:32 yes I do. k could be `newKey(ns, k.Kind(), "foo",
234 }
235 return KeyValid(ns, testKey, opt)
236 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698