← Back to Realms

Realm

Gno.land Mainnet

gno.land/r/akkadia/beta/definition

Overview

Network
gnoland-1
Realm Path
gno.land/r/akkadia/beta/definition
Exported Functions
7
State Entries
2
Source Files
7
Total Package Entries
20

Exported Functions

7 exported functions

State

2 state entries

Source Code

FILES
definition.gno
go
1package definition
2
3import (
4	"chain/runtime/unsafe"
5	"net/url"
6	"sort"
7	"strings"
8
9	"gno.land/p/akkadia/beta/accesscontrol"
10	"gno.land/p/nt/bptree/v0"
11	"gno.land/r/akkadia/beta/admin"
12)
13
14type Field struct {
15	Key   string
16	Value string
17}
18
19var definitions *bptree.BPTree
20
21func init() {
22	definitions = bptree.NewBPTree32()
23}
24
25// DeleteAll removes all definitions so an administrator can reload the catalog.
26func 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.
33func 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.
137func 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
146func 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.
157func 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
167func GetKeyCount() int { return definitions.Size() }
168
169func 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
207func 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

Raw Package Data

Raw JSON data