Kind Realm (renderable)
Name definition
Namespace akkadia / beta
Exported functions 7
Module gno.land/r/akkadia/beta/definition
gno 0.9 gno
definition.gno gno
⧉
1 package definition 2 3 import ( 4 "chain/runtime/unsafe" 5 "net/url" 6 "sort" 7 "strings" 8 DeleteAll (cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string})
Get (key string) []map[string]string
GetField (key string, fieldKey string) map[string]string
GetKeyCount () int
ListKeys (page int, count int) []string
Render (path string) string
SetDefinitions (cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, keys string, values string)
Signatures reconstructed verbatim from vm/qfuncs — interface params keep their inline definitions.
9 "gno.land/p/akkadia/beta/accesscontrol"
10 "gno.land/p/nt/bptree/v0"
11 "gno.land/r/akkadia/beta/admin"
12 )
13
14 type Field struct {
15 Key string
16 Value string
17 }
18
19 var definitions *bptree.BPTree
20
21 func init() {
22 definitions = bptree.NewBPTree32()
23 }
24
25 // DeleteAll removes all definitions so an administrator can reload the catalog.
26 func DeleteAll(cur realm) {
27 assertCanWrite( 0 , cur)
28 definitions = bptree.NewBPTree32()
29 }
30
31 // SetDefinitions accepts URL-escaped keys and query-encoded field objects as paired
32 // comma-separated strings, so MsgCall can pass a batch without realm DTOs.
33 func SetDefinitions(cur realm, keys, values string ) {
34 assertCanWrite( 0 , cur)
35 if len(keys)+len(values) > 131071 {
36 panic( "call bytes exceeds 131071" )
37 }
38 if keys == "" && values == "" {
39 assertBatchCount( 0 )
40 }
41 if keys == "" || values == "" {
42 panic( "keys and values count mismatch" )
43 }
44 type entry struct {
45 Key string
46 Fields []Field
47 }
48 entries := []entry{}
49 keyStart, keyEnd := 0 , 0
50 valueStart, valueEnd := 0 , 0
51 for {
52 assertBatchCount(len(entries) + 1 )
53 for keyEnd < len(keys) && keys[keyEnd] != ',' {
54 keyEnd++
55 }
56 for valueEnd < len(values) && values[valueEnd] != ',' {
57 valueEnd++
58 }
59 if keyStart == keyEnd || valueStart == valueEnd {
60 panic( "empty entry token" )
61 }
62 key, err := url.QueryUnescape(keys[keyStart:keyEnd])
63 if err != nil {
64 panic( "invalid encoded key" )
65 }
66 attributes, err := url.ParseQuery(values[valueStart:valueEnd])
67 if err != nil {
68 panic( "invalid encoded fields" )
69 }
70 if len(attributes) < 1 || len(attributes) > 32 {
71 panic( "field count must be between 1 and 32" )
72 }
73 fields := make([]Field, 0 , len(attributes))
74 for name, fieldValues := range attributes {
75 if len(fieldValues) != 1 {
76 panic( "duplicate field key" )
77 }
78 fields = append(fields, Field{name, fieldValues[ 0 ]})
79 }
80 entries = append(entries, entry{key, fields})
81 keyDone := keyEnd >= len(keys)
82 valueDone := valueEnd >= len(values)
83 if keyDone != valueDone {
84 panic( "keys and values count mismatch" )
85 }
86 if keyDone {
87 break
88 }
89 keyEnd++
90 valueEnd++
91 keyStart, valueStart = keyEnd, valueEnd
92 }
93 // Prepare every final record before writing any state.
94 prepared := make([][]Field, len(entries))
95 batchBytes := 0
96 for i, entry := range entries {
97 assertIdentifier(entry.Key, "key" , 128 )
98 batchBytes += len(entry.Key) + assertFields(entry.Fields)
99 if batchBytes > 65536 {
100 panic( "batch bytes exceeds 65536" )
101 }
102 for j := 0 ; j < i; j++ {
103 if entries[j].Key == entry.Key {
104 panic( "duplicate entry" )
105 }
106 }
107 fields, _ := getFields(entry.Key)
108 for _, update := range entry.Fields {
109 found := false
110 for j := range fields {
111 if fields[j].Key == update.Key {
112 fields[j].Value = update.Value
113 found = true
114 break
115 }
116 }
117 if !found {
118 fields = append(fields, update)
119 }
120 }
121 assertFields(fields)
122 // At most 32 fields; insertion sort avoids another persistent index.
123 for j := 1 ; j < len(fields); j++ {
124 for k := j; k > 0 && fields[k].Key < fields[k- 1 ].Key; k-- {
125 fields[k], fields[k- 1 ] = fields[k- 1 ], fields[k]
126 }
127 }
128 prepared[i] = fields
129 }
130 for i, entry := range entries {
131 definitions.Set(entry.Key, prepared[i])
132 }
133 }
134
135 // Get returns ordered field records. An empty list means the key is absent;
136 // stored definitions always contain at least one field.
137 func Get(key string ) [] map [ string ] string {
138 fields, _ := getFields(key)
139 result := make([] map [ string ] string , 0 , len(fields))
140 for _, field := range fields {
141 result = append(result, map [ string ] string { "key" : field.Key, "value" : field.Value})
142 }
143 return result
144 }
145
146 func getFields(key string ) (fields []Field, found bool ) {
147 if !definitions.Has(key) {
148 return nil , false
149 }
150 stored := definitions.Get(key).([]Field)
151 fields = make([]Field, len(stored))
152 copy(fields, stored)
153 return fields, true
154 }
155
156 // GetField includes "value" even when it is empty. An empty map means absent.
157 func GetField(key, fieldKey string ) map [ string ] string {
158 fields, _ := getFields(key)
159 for _, field := range fields {
160 if field.Key == fieldKey {
161 return map [ string ] string { "value" : field.Value}
162 }
163 }
164 return map [ string ] string {}
165 }
166
167 func GetKeyCount() int { return definitions.Size() }
168
169 func ListKeys(page, count int ) [] string {
170 assertPage(page, count)
171 result := [] string {}
172 // Compare before multiplication so an extreme page cannot overflow.
173 if definitions.Size() == 0 || page- 1 > (definitions.Size()- 1 )/count {
174 return result
175 }
176 // Sort a query-local view so existing records keep their original storage keys.
177 ordered := make([] string , 0 , definitions.Size())
178 definitions.Iterate( "" , "" , func (key string , _ any ) bool {
179 numeric := true
180 for _, r := range key {
181 if r < '0' || r > '9' {
182 numeric = false
183 break
184 }
185 }
186 if numeric {
187 // Keys are at most 128 bytes. Padding avoids integer overflow; the
188 // original key breaks ties such as "01" and "1" without merging them.
189 ordered = append(ordered, "0" +strings.Repeat( "0" , 128 -len(key))+key+ ":" +key)
190 } else {
191 ordered = append(ordered, "1:" +key)
192 }
193 return false
194 })
195 sort.Strings(ordered)
196 start := (page - 1 ) * count
197 end := start + count
198 if end > len(ordered) {
199 end = len(ordered)
200 }
201 for _, key := range ordered[start:end] {
202 result = append(result, key[strings.IndexByte(key, ':' )+ 1 :])
203 }
204 return result
205 }
206
207 func assertCanWrite(_ int , rlm realm) {
208 accesscontrol.AssertIsAdmin( 0 , rlm, admin.IsAdmin)
209 // MsgRun has the signer address but an ephemeral /e/<address>/run path.
210 if !rlm.Previous().IsUser() {
211 panic( "direct user call or run required" )
212 }
213 if len(unsafe.OriginSend()) != 0 {
214 panic( "attached coins not allowed" )
215 }
216 }
217
vm/qrender output, sanitized (docs/render-security.md) and displayed in an empty-sandbox iframe — scripts, forms and popups cannot run. Links stay inert in-preview; right-click to open.