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

Side by Side Diff: service/datastore/dskey/key.go

Issue 1292913002: Split off serialization and key functions to their own packages. (Closed) Base URL: https://github.com/luci/gae.git@make_queries_better
Patch Set: Created 5 years, 4 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
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 // adapted from github.com/golang/appengine/datastore 5 // adapted from github.com/golang/appengine/datastore
6 6
7 package datastore 7 package dskey
8 8
9 import ( 9 import (
10 "bytes" 10 "bytes"
11 "encoding/base64" 11 "encoding/base64"
12 "errors" 12 "errors"
13 "fmt"
13 "strconv" 14 "strconv"
14 "strings" 15 "strings"
15 16
17 "github.com/golang/protobuf/proto"
18 ds "github.com/luci/gae/service/datastore"
16 pb "github.com/luci/gae/service/datastore/internal/protos/datastore" 19 pb "github.com/luci/gae/service/datastore/internal/protos/datastore"
17
18 "github.com/golang/protobuf/proto"
19 ) 20 )
20 21
21 // KeyEncode encodes the provided key as a base64-encoded protobuf. 22 // Encode encodes the provided key as a base64-encoded protobuf.
dnj (Google) 2015/08/14 21:19:50 (Probably good to mention that it's URLencoded bas
iannucci 2015/08/14 22:13:03 it's not url encoded, it's encoded using the urlsa
dnj (Google) 2015/08/14 22:23:16 Yeah sorry that is what I meant.
22 // 23 //
23 // This encoding is compatible with the SDK-provided encoding and is agnostic 24 // This encoding is compatible with the SDK-provided encoding and is agnostic
24 // to the underlying implementation of the Key. 25 // to the underlying implementation of the Key.
25 func KeyEncode(k Key) string { 26 func Encode(k ds.Key) string {
26 n := 0 27 n := 0
27 for i := k; i != nil; i = i.Parent() { 28 for i := k; i != nil; i = i.Parent() {
28 n++ 29 n++
29 } 30 }
30 e := make([]*pb.Path_Element, n) 31 e := make([]*pb.Path_Element, n)
31 for i := k; i != nil; i = i.Parent() { 32 for i := k; i != nil; i = i.Parent() {
32 n-- 33 n--
33 kind := i.Kind() 34 kind := i.Kind()
34 e[n] = &pb.Path_Element{ 35 e[n] = &pb.Path_Element{
35 Type: &kind, 36 Type: &kind,
(...skipping 20 matching lines...) Expand all
56 }, 57 },
57 }) 58 })
58 if err != nil { 59 if err != nil {
59 panic(err) 60 panic(err)
60 } 61 }
61 62
62 // trim padding 63 // trim padding
63 return strings.TrimRight(base64.URLEncoding.EncodeToString(r), "=") 64 return strings.TrimRight(base64.URLEncoding.EncodeToString(r), "=")
64 } 65 }
65 66
66 // KeyToksDecode decodes a base64-encoded protobuf representation of a Key 67 // ToksDecode decodes a base64-encoded protobuf representation of a Key
67 // into a tokenized form. This is so that implementations of the gae wrapper 68 // into a tokenized form. This is so that implementations of the gae wrapper
68 // can decode to their own implementation of Key. 69 // can decode to their own implementation of Key.
69 // 70 //
70 // This encoding is compatible with the SDK-provided encoding and is agnostic 71 // This encoding is compatible with the SDK-provided encoding and is agnostic
71 // to the underlying implementation of the Key. 72 // to the underlying implementation of the Key.
72 func KeyToksDecode(encoded string) (appID, namespace string, toks []KeyTok, err error) { 73 func ToksDecode(encoded string) (appID, namespace string, toks []ds.KeyTok, err error) {
73 // Re-add padding 74 // Re-add padding
74 if m := len(encoded) % 4; m != 0 { 75 if m := len(encoded) % 4; m != 0 {
75 encoded += strings.Repeat("=", 4-m) 76 encoded += strings.Repeat("=", 4-m)
76 } 77 }
77 b, err := base64.URLEncoding.DecodeString(encoded) 78 b, err := base64.URLEncoding.DecodeString(encoded)
78 if err != nil { 79 if err != nil {
79 return 80 return
80 } 81 }
81 82
82 r := &pb.Reference{} 83 r := &pb.Reference{}
83 if err = proto.Unmarshal(b, r); err != nil { 84 if err = proto.Unmarshal(b, r); err != nil {
84 return 85 return
85 } 86 }
86 87
87 appID = r.GetApp() 88 appID = r.GetApp()
88 namespace = r.GetNameSpace() 89 namespace = r.GetNameSpace()
89 » toks = make([]KeyTok, len(r.Path.Element)) 90 » toks = make([]ds.KeyTok, len(r.Path.Element))
90 for i, e := range r.Path.Element { 91 for i, e := range r.Path.Element {
91 » » toks[i] = KeyTok{ 92 » » toks[i] = ds.KeyTok{
92 Kind: e.GetType(), 93 Kind: e.GetType(),
93 IntID: e.GetId(), 94 IntID: e.GetId(),
94 StringID: e.GetName(), 95 StringID: e.GetName(),
95 } 96 }
96 } 97 }
97 return 98 return
98 } 99 }
99 100
100 // KeyMarshalJSON returns a MarshalJSON-compatible serialization of a Key. 101 // MarshalJSON returns a MarshalJSON-compatible serialization of a Key.
101 func KeyMarshalJSON(k Key) ([]byte, error) { 102 func MarshalJSON(k ds.Key) ([]byte, error) {
102 » return []byte(`"` + KeyEncode(k) + `"`), nil 103 » return []byte(`"` + Encode(k) + `"`), nil
103 } 104 }
104 105
105 // KeyUnmarshalJSON returns the tokenized version of a Key as encoded by 106 // UnmarshalJSON returns the tokenized version of a ds.Key as encoded by
106 // KeyMarshalJSON. 107 // MarshalJSON.
107 func KeyUnmarshalJSON(buf []byte) (appID, namespace string, toks []KeyTok, err e rror) { 108 func UnmarshalJSON(buf []byte) (appID, namespace string, toks []ds.KeyTok, err e rror) {
108 if len(buf) < 2 || buf[0] != '"' || buf[len(buf)-1] != '"' { 109 if len(buf) < 2 || buf[0] != '"' || buf[len(buf)-1] != '"' {
109 err = errors.New("datastore: bad JSON key") 110 err = errors.New("datastore: bad JSON key")
110 } else { 111 } else {
111 » » appID, namespace, toks, err = KeyToksDecode(string(buf[1 : len(b uf)-1])) 112 » » appID, namespace, toks, err = ToksDecode(string(buf[1 : len(buf) -1]))
112 } 113 }
113 return 114 return
114 } 115 }
115 116
116 // KeyIncomplete returns true iff k doesn't have an id yet. 117 // Incomplete returns true iff k doesn't have an id yet.
117 func KeyIncomplete(k Key) bool { 118 func Incomplete(k ds.Key) bool {
118 return k != nil && k.StringID() == "" && k.IntID() == 0 119 return k != nil && k.StringID() == "" && k.IntID() == 0
119 } 120 }
120 121
121 // KeyValid determines if a key is valid, according to a couple rules: 122 // Valid determines if a key is valid, according to a couple rules:
122 // - k is not nil 123 // - k is not nil
123 // - every token of k: 124 // - every token of k:
124 // - (if !allowSpecial) token's kind doesn't start with '__' 125 // - (if !allowSpecial) token's kind doesn't start with '__'
125 // - token's kind and appid are non-blank 126 // - token's kind and appid are non-blank
126 // - token is not incomplete 127 // - token is not incomplete
127 // - all tokens have the same namespace and appid 128 // - all tokens have the same namespace and appid
128 func KeyValid(k Key, allowSpecial bool, aid, ns string) bool { 129 func Valid(k ds.Key, allowSpecial bool, aid, ns string) bool {
129 if k == nil { 130 if k == nil {
130 return false 131 return false
131 } 132 }
132 if aid != k.AppID() || ns != k.Namespace() { 133 if aid != k.AppID() || ns != k.Namespace() {
133 return false 134 return false
134 } 135 }
135 for ; k != nil; k = k.Parent() { 136 for ; k != nil; k = k.Parent() {
136 if !allowSpecial && len(k.Kind()) >= 2 && k.Kind()[:2] == "__" { 137 if !allowSpecial && len(k.Kind()) >= 2 && k.Kind()[:2] == "__" {
137 return false 138 return false
138 } 139 }
139 if k.Kind() == "" || k.AppID() == "" { 140 if k.Kind() == "" || k.AppID() == "" {
140 return false 141 return false
141 } 142 }
142 if k.StringID() != "" && k.IntID() != 0 { 143 if k.StringID() != "" && k.IntID() != 0 {
143 return false 144 return false
144 } 145 }
145 if k.Parent() != nil { 146 if k.Parent() != nil {
146 » » » if KeyIncomplete(k.Parent()) { 147 » » » if k.Parent().Incomplete() {
147 return false 148 return false
148 } 149 }
149 if k.Parent().AppID() != k.AppID() || k.Parent().Namespa ce() != k.Namespace() { 150 if k.Parent().AppID() != k.AppID() || k.Parent().Namespa ce() != k.Namespace() {
150 return false 151 return false
151 } 152 }
152 } 153 }
153 } 154 }
154 return true 155 return true
155 } 156 }
156 157
157 // KeyRoot returns the entity root for the given key. 158 // PartialValid returns true iff this key is suitable for use in a Put
158 func KeyRoot(k Key) Key { 159 // operation. This is the same as Valid(k, false, ...), but also allowing k to
160 // be Incomplete().
161 func PartialValid(k ds.Key, aid, ns string) bool {
162 » if k.Incomplete() {
163 » » k = New(k.AppID(), k.Namespace(), k.Kind(), "", 1, k.Parent())
164 » » fmt.Println(k.String(), aid == k.AppID(), ns == k.Namespace(), k .Kind(), k.StringID(), k.IntID())
165 » }
166 » return Valid(k, false, aid, ns)
167 }
168
169 // Root returns the entity root for the given key.
170 func Root(k ds.Key) ds.Key {
159 for k != nil && k.Parent() != nil { 171 for k != nil && k.Parent() != nil {
160 k = k.Parent() 172 k = k.Parent()
161 } 173 }
162 return k 174 return k
163 } 175 }
164 176
165 // KeysEqual returns true iff the two keys represent identical key values. 177 // Equal returns true iff the two keys represent identical key values.
166 func KeysEqual(a, b Key) (ret bool) { 178 func Equal(a, b ds.Key) (ret bool) {
167 ret = (a.Kind() == b.Kind() && 179 ret = (a.Kind() == b.Kind() &&
168 a.StringID() == b.StringID() && 180 a.StringID() == b.StringID() &&
169 a.IntID() == b.IntID() && 181 a.IntID() == b.IntID() &&
170 a.AppID() == b.AppID() && 182 a.AppID() == b.AppID() &&
171 a.Namespace() == b.Namespace()) 183 a.Namespace() == b.Namespace())
172 if !ret { 184 if !ret {
173 return 185 return
174 } 186 }
175 ap, bp := a.Parent(), b.Parent() 187 ap, bp := a.Parent(), b.Parent()
176 » return (ap == nil && bp == nil) || KeysEqual(ap, bp) 188 » return (ap == nil && bp == nil) || Equal(ap, bp)
177 } 189 }
178 190
179 func marshalDSKey(b *bytes.Buffer, k Key) { 191 func marshalDSKey(b *bytes.Buffer, k ds.Key) {
180 if k.Parent() != nil { 192 if k.Parent() != nil {
181 marshalDSKey(b, k.Parent()) 193 marshalDSKey(b, k.Parent())
182 } 194 }
183 b.WriteByte('/') 195 b.WriteByte('/')
184 b.WriteString(k.Kind()) 196 b.WriteString(k.Kind())
185 b.WriteByte(',') 197 b.WriteByte(',')
186 if k.StringID() != "" { 198 if k.StringID() != "" {
187 b.WriteString(k.StringID()) 199 b.WriteString(k.StringID())
188 } else { 200 } else {
189 b.WriteString(strconv.FormatInt(k.IntID(), 10)) 201 b.WriteString(strconv.FormatInt(k.IntID(), 10))
190 } 202 }
191 } 203 }
192 204
193 // KeyString returns a human-readable representation of the key, and is the 205 // String returns a human-readable representation of the key, and is the
194 // typical implementation of Key.String() (though it isn't guaranteed to be) 206 // typical implementation of Key.String() (though it isn't guaranteed to be)
195 func KeyString(k Key) string { 207 func String(k ds.Key) string {
196 if k == nil { 208 if k == nil {
197 return "" 209 return ""
198 } 210 }
199 b := bytes.NewBuffer(make([]byte, 0, 512)) 211 b := bytes.NewBuffer(make([]byte, 0, 512))
200 marshalDSKey(b, k) 212 marshalDSKey(b, k)
201 return b.String() 213 return b.String()
202 } 214 }
203 215
204 // KeySplit splits the key into its constituent parts. Note that if the key is 216 // Split splits the key into its constituent parts. Note that if the key is
205 // not KeyValid, this method may not provide a round-trip for k. 217 // not Valid, this method may not provide a round-trip for k.
206 func KeySplit(k Key) (appID, namespace string, toks []KeyTok) { 218 func Split(k ds.Key) (appID, namespace string, toks []ds.KeyTok) {
207 if k == nil { 219 if k == nil {
208 return 220 return
209 } 221 }
210 222
211 » if sk, ok := k.(*GenericKey); ok { 223 » if sk, ok := k.(*Generic); ok {
212 if sk == nil { 224 if sk == nil {
213 return 225 return
214 } 226 }
215 return sk.appID, sk.namespace, sk.toks 227 return sk.appID, sk.namespace, sk.toks
216 } 228 }
217 229
218 n := 0 230 n := 0
219 for i := k; i != nil; i = i.Parent() { 231 for i := k; i != nil; i = i.Parent() {
220 n++ 232 n++
221 } 233 }
222 » toks = make([]KeyTok, n) 234 » toks = make([]ds.KeyTok, n)
223 for i := k; i != nil; i = i.Parent() { 235 for i := k; i != nil; i = i.Parent() {
224 n-- 236 n--
225 toks[n].IntID = i.IntID() 237 toks[n].IntID = i.IntID()
226 toks[n].StringID = i.StringID() 238 toks[n].StringID = i.StringID()
227 toks[n].Kind = i.Kind() 239 toks[n].Kind = i.Kind()
228 } 240 }
229 appID = k.AppID() 241 appID = k.AppID()
230 namespace = k.Namespace() 242 namespace = k.Namespace()
231 return 243 return
232 } 244 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698