Skip to content Search Apps Documentation Source Content File Folder Download Copy Actions State String Boolean Number Struct Map Slice Pointer Function Closure Reference Nil Package Type Interface Unknown

json.gno

5.16 Kb · 202 lines
  1package validator_scores
  2
  3import (
  4	"sort"
  5	"strconv"
  6	"strings"
  7	"unicode/utf8"
  8)
  9
 10const hexDigits = "0123456789abcdef"
 11
 12// quoteSafe quotes a string the realm validated to need no JSON escaping
 13// (addresses, dates, hashes, arena names). Concatenation is cheap in gas;
 14// per-byte loops are not.
 15func quoteSafe(s string) string { return `"` + s + `"` }
 16
 17// quoteMoniker quotes a validated moniker (valid UTF-8, no control
 18// characters): only '"' and '\' need escaping.
 19func quoteMoniker(s string) string {
 20	if !strings.ContainsAny(s, `"\`) {
 21		return `"` + s + `"`
 22	}
 23	var b strings.Builder
 24	writeJSONString(&b, s)
 25	return b.String()
 26}
 27
 28// writeJSONString writes any string as a JSON string. Invalid UTF-8 bytes
 29// become U+FFFD. It costs gas per rune: use it only for short or unvalidated
 30// input.
 31func writeJSONString(b *strings.Builder, s string) {
 32	b.WriteByte('"')
 33	for i := 0; i < len(s); {
 34		r, size := utf8.DecodeRuneInString(s[i:])
 35		switch {
 36		case r == utf8.RuneError && size == 1:
 37			b.WriteString(`\ufffd`)
 38		case r == '"':
 39			b.WriteString(`\"`)
 40		case r == '\\':
 41			b.WriteString(`\\`)
 42		case r < 0x20:
 43			b.WriteString(`\u00`)
 44			b.WriteByte(hexDigits[r>>4])
 45			b.WriteByte(hexDigits[r&0xf])
 46		default:
 47			b.WriteString(s[i : i+size])
 48		}
 49		i += size
 50	}
 51	b.WriteByte('"')
 52}
 53
 54func writeStrings(b *strings.Builder, list []string) {
 55	b.WriteByte('[')
 56	for i, s := range list {
 57		if i > 0 {
 58			b.WriteByte(',')
 59		}
 60		b.WriteString(quoteSafe(s))
 61	}
 62	b.WriteByte(']')
 63}
 64
 65// candidate is a stored line split into its fields. Numbers stay in their
 66// canonical decimal text, which is valid JSON.
 67type candidate struct {
 68	operator    string
 69	moniker     string
 70	total       string
 71	rank        int
 72	rankText    string
 73	github      string
 74	arenaScores string // "s1,...,sN"
 75}
 76
 77func decode(line string) candidate {
 78	f := strings.Split(line, "|")
 79	rank, _ := strconv.Atoi(f[3])
 80	return candidate{operator: f[0], moniker: f[1], total: f[2], rank: rank, rankText: f[3], github: f[4], arenaScores: f[5]}
 81}
 82
 83type byRank []candidate
 84
 85func (s byRank) Len() int      { return len(s) }
 86func (s byRank) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
 87func (s byRank) Less(i, j int) bool {
 88	if s[i].rank != s[j].rank {
 89		return s[i].rank < s[j].rank
 90	}
 91	if s[i].moniker != s[j].moniker {
 92		return s[i].moniker < s[j].moniker
 93	}
 94	return s[i].operator < s[j].operator
 95}
 96
 97// sortedCandidates lists an edition's candidates by rank, then moniker
 98// (byte order), then operator.
 99func sortedCandidates(e *edition) []candidate {
100	list := make([]candidate, len(e.lines))
101	for i, line := range e.lines {
102		list[i] = decode(line)
103	}
104	sort.Sort(byRank(list))
105	return list
106}
107
108func latestSealed() *edition {
109	for i := len(editions) - 1; i >= 0; i-- {
110		if editions[i].sealed {
111			return editions[i]
112		}
113	}
114	return nil
115}
116
117// scoreFields is `"moniker":M,"total":T,"rank":R,"github":G`, shared by
118// both JSON shapes.
119func scoreFields(c candidate) string {
120	return `"moniker":` + quoteMoniker(c.moniker) + `,"total":` + c.total + `,"rank":` + c.rankText + `,"github":` + c.github
121}
122
123func editionJSON(e *edition) string {
124	var b strings.Builder
125	b.WriteString(`{"number":` + strconv.Itoa(e.number) + `,"computedAt":` + quoteSafe(e.computedAt) +
126		`,"scorecardSHA256":` + quoteSafe(e.scorecardSHA256) + `,"arenas":`)
127	writeStrings(&b, e.arenas)
128	b.WriteString(`,"candidates":[`)
129	for i, c := range sortedCandidates(e) {
130		if i > 0 {
131			b.WriteByte(',')
132		}
133		b.WriteString(`{"operator":` + quoteSafe(c.operator) + `,` + scoreFields(c) + `,"arenaScores":[` + c.arenaScores + `]}`)
134	}
135	b.WriteString(`]}`)
136	return b.String()
137}
138
139// LatestJSON returns the latest sealed edition, or "null".
140func LatestJSON() string {
141	e := latestSealed()
142	if e == nil {
143		return "null"
144	}
145	return editionJSON(e)
146}
147
148// EditionJSON returns sealed edition n, or "null".
149func EditionJSON(n int) string {
150	if n < 1 || n > len(editions) || !editions[n-1].sealed {
151		return "null"
152	}
153	return editionJSON(editions[n-1])
154}
155
156// CandidateJSON returns the operator's entries across sealed editions, in
157// edition order; "entries" is [] for an unknown address.
158func CandidateJSON(operator string) string {
159	var b strings.Builder
160	b.WriteString(`{"operator":`)
161	writeJSONString(&b, operator)
162	b.WriteString(`,"entries":[`)
163	first := true
164	for _, e := range editions {
165		if !e.sealed {
166			continue
167		}
168		line, ok := e.find(operator)
169		if !ok {
170			continue
171		}
172		if !first {
173			b.WriteByte(',')
174		}
175		first = false
176		c := decode(line)
177		b.WriteString(`{"edition":` + strconv.Itoa(e.number) + `,"computedAt":` + quoteSafe(e.computedAt) + `,` + scoreFields(c) + `,"arenas":`)
178		writeStrings(&b, e.arenas)
179		b.WriteString(`,"arenaScores":[` + c.arenaScores + `]}`)
180	}
181	b.WriteString(`]}`)
182	return b.String()
183}
184
185// OpenEditionJSON returns the open edition (number, scorecard hash and the
186// operators already added, in the order added), or "null".
187func OpenEditionJSON() string {
188	e := openEdition()
189	if e == nil {
190		return "null"
191	}
192	var b strings.Builder
193	b.WriteString(`{"number":` + strconv.Itoa(e.number) + `,"scorecardSHA256":` + quoteSafe(e.scorecardSHA256) + `,"operators":[`)
194	for i, line := range e.lines {
195		if i > 0 {
196			b.WriteByte(',')
197		}
198		b.WriteString(quoteSafe(line[:operatorLen]))
199	}
200	b.WriteString(`]}`)
201	return b.String()
202}