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

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: rebase 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
« no previous file with comments | « service/datastore/dskey/generic_key.go ('k') | service/datastore/dskey/key_test.go » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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 "strconv" 13 "strconv"
14 "strings" 14 "strings"
15 15
16 "github.com/golang/protobuf/proto"
17 ds "github.com/luci/gae/service/datastore"
16 pb "github.com/luci/gae/service/datastore/internal/protos/datastore" 18 pb "github.com/luci/gae/service/datastore/internal/protos/datastore"
17
18 "github.com/golang/protobuf/proto"
19 ) 19 )
20 20
21 // KeyEncode encodes the provided key as a base64-encoded protobuf. 21 // Encode encodes the provided key as a base64-encoded protobuf.
22 // 22 //
23 // This encoding is compatible with the SDK-provided encoding and is agnostic 23 // This encoding is compatible with the SDK-provided encoding and is agnostic
24 // to the underlying implementation of the Key. 24 // to the underlying implementation of the Key.
25 func KeyEncode(k Key) string { 25 //
26 // It's encoded with the urlsafe base64 table.
27 func Encode(k ds.Key) string {
26 n := 0 28 n := 0
27 for i := k; i != nil; i = i.Parent() { 29 for i := k; i != nil; i = i.Parent() {
28 n++ 30 n++
29 } 31 }
30 e := make([]*pb.Path_Element, n) 32 e := make([]*pb.Path_Element, n)
31 for i := k; i != nil; i = i.Parent() { 33 for i := k; i != nil; i = i.Parent() {
32 n-- 34 n--
33 kind := i.Kind() 35 kind := i.Kind()
34 e[n] = &pb.Path_Element{ 36 e[n] = &pb.Path_Element{
35 Type: &kind, 37 Type: &kind,
(...skipping 20 matching lines...) Expand all
56 }, 58 },
57 }) 59 })
58 if err != nil { 60 if err != nil {
59 panic(err) 61 panic(err)
60 } 62 }
61 63
62 // trim padding 64 // trim padding
63 return strings.TrimRight(base64.URLEncoding.EncodeToString(r), "=") 65 return strings.TrimRight(base64.URLEncoding.EncodeToString(r), "=")
64 } 66 }
65 67
66 // KeyToksDecode decodes a base64-encoded protobuf representation of a Key 68 // ToksDecode decodes a base64-encoded protobuf representation of a Key
67 // into a tokenized form. This is so that implementations of the gae wrapper 69 // into a tokenized form. This is so that implementations of the gae wrapper
68 // can decode to their own implementation of Key. 70 // can decode to their own implementation of Key.
69 // 71 //
70 // This encoding is compatible with the SDK-provided encoding and is agnostic 72 // This encoding is compatible with the SDK-provided encoding and is agnostic
71 // to the underlying implementation of the Key. 73 // to the underlying implementation of the Key.
72 func KeyToksDecode(encoded string) (appID, namespace string, toks []KeyTok, err error) { 74 func ToksDecode(encoded string) (appID, namespace string, toks []ds.KeyTok, err error) {
73 // Re-add padding 75 // Re-add padding
74 if m := len(encoded) % 4; m != 0 { 76 if m := len(encoded) % 4; m != 0 {
75 encoded += strings.Repeat("=", 4-m) 77 encoded += strings.Repeat("=", 4-m)
76 } 78 }
77 b, err := base64.URLEncoding.DecodeString(encoded) 79 b, err := base64.URLEncoding.DecodeString(encoded)
78 if err != nil { 80 if err != nil {
79 return 81 return
80 } 82 }
81 83
82 r := &pb.Reference{} 84 r := &pb.Reference{}
83 if err = proto.Unmarshal(b, r); err != nil { 85 if err = proto.Unmarshal(b, r); err != nil {
84 return 86 return
85 } 87 }
86 88
87 appID = r.GetApp() 89 appID = r.GetApp()
88 namespace = r.GetNameSpace() 90 namespace = r.GetNameSpace()
89 » toks = make([]KeyTok, len(r.Path.Element)) 91 » toks = make([]ds.KeyTok, len(r.Path.Element))
90 for i, e := range r.Path.Element { 92 for i, e := range r.Path.Element {
91 » » toks[i] = KeyTok{ 93 » » toks[i] = ds.KeyTok{
92 Kind: e.GetType(), 94 Kind: e.GetType(),
93 IntID: e.GetId(), 95 IntID: e.GetId(),
94 StringID: e.GetName(), 96 StringID: e.GetName(),
95 } 97 }
96 } 98 }
97 return 99 return
98 } 100 }
99 101
100 // KeyMarshalJSON returns a MarshalJSON-compatible serialization of a Key. 102 // MarshalJSON returns a MarshalJSON-compatible serialization of a Key.
101 func KeyMarshalJSON(k Key) ([]byte, error) { 103 func MarshalJSON(k ds.Key) ([]byte, error) {
102 » return []byte(`"` + KeyEncode(k) + `"`), nil 104 » return []byte(`"` + Encode(k) + `"`), nil
103 } 105 }
104 106
105 // KeyUnmarshalJSON returns the tokenized version of a Key as encoded by 107 // UnmarshalJSON returns the tokenized version of a ds.Key as encoded by
106 // KeyMarshalJSON. 108 // MarshalJSON.
107 func KeyUnmarshalJSON(buf []byte) (appID, namespace string, toks []KeyTok, err e rror) { 109 func UnmarshalJSON(buf []byte) (appID, namespace string, toks []ds.KeyTok, err e rror) {
108 if len(buf) < 2 || buf[0] != '"' || buf[len(buf)-1] != '"' { 110 if len(buf) < 2 || buf[0] != '"' || buf[len(buf)-1] != '"' {
109 err = errors.New("datastore: bad JSON key") 111 err = errors.New("datastore: bad JSON key")
110 } else { 112 } else {
111 » » appID, namespace, toks, err = KeyToksDecode(string(buf[1 : len(b uf)-1])) 113 » » appID, namespace, toks, err = ToksDecode(string(buf[1 : len(buf) -1]))
112 } 114 }
113 return 115 return
114 } 116 }
115 117
116 // KeyIncomplete returns true iff k doesn't have an id yet. 118 // Incomplete returns true iff k doesn't have an id yet.
117 func KeyIncomplete(k Key) bool { 119 func Incomplete(k ds.Key) bool {
118 return k != nil && k.StringID() == "" && k.IntID() == 0 120 return k != nil && k.StringID() == "" && k.IntID() == 0
119 } 121 }
120 122
121 // KeyValid determines if a key is valid, according to a couple rules: 123 // Valid determines if a key is valid, according to a couple rules:
122 // - k is not nil 124 // - k is not nil
123 // - every token of k: 125 // - every token of k:
124 // - (if !allowSpecial) token's kind doesn't start with '__' 126 // - (if !allowSpecial) token's kind doesn't start with '__'
125 // - token's kind and appid are non-blank 127 // - token's kind and appid are non-blank
126 // - token is not incomplete 128 // - token is not incomplete
127 // - all tokens have the same namespace and appid 129 // - all tokens have the same namespace and appid
128 func KeyValid(k Key, allowSpecial bool, aid, ns string) bool { 130 func Valid(k ds.Key, allowSpecial bool, aid, ns string) bool {
129 if k == nil { 131 if k == nil {
130 return false 132 return false
131 } 133 }
132 if aid != k.AppID() || ns != k.Namespace() { 134 if aid != k.AppID() || ns != k.Namespace() {
133 return false 135 return false
134 } 136 }
135 for ; k != nil; k = k.Parent() { 137 for ; k != nil; k = k.Parent() {
136 if !allowSpecial && len(k.Kind()) >= 2 && k.Kind()[:2] == "__" { 138 if !allowSpecial && len(k.Kind()) >= 2 && k.Kind()[:2] == "__" {
137 return false 139 return false
138 } 140 }
139 if k.Kind() == "" || k.AppID() == "" { 141 if k.Kind() == "" || k.AppID() == "" {
140 return false 142 return false
141 } 143 }
142 if k.StringID() != "" && k.IntID() != 0 { 144 if k.StringID() != "" && k.IntID() != 0 {
143 return false 145 return false
144 } 146 }
145 if k.Parent() != nil { 147 if k.Parent() != nil {
146 » » » if KeyIncomplete(k.Parent()) { 148 » » » if k.Parent().Incomplete() {
147 return false 149 return false
148 } 150 }
149 if k.Parent().AppID() != k.AppID() || k.Parent().Namespa ce() != k.Namespace() { 151 if k.Parent().AppID() != k.AppID() || k.Parent().Namespa ce() != k.Namespace() {
150 return false 152 return false
151 } 153 }
152 } 154 }
153 } 155 }
154 return true 156 return true
155 } 157 }
156 158
157 // KeyRoot returns the entity root for the given key. 159 // PartialValid returns true iff this key is suitable for use in a Put
158 func KeyRoot(k Key) Key { 160 // operation. This is the same as Valid(k, false, ...), but also allowing k to
161 // be Incomplete().
162 func PartialValid(k ds.Key, aid, ns string) bool {
163 » if k.Incomplete() {
164 » » k = New(k.AppID(), k.Namespace(), k.Kind(), "", 1, k.Parent())
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
« no previous file with comments | « service/datastore/dskey/generic_key.go ('k') | service/datastore/dskey/key_test.go » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698