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

Community realm. Read the code first. Community realm, deployed by its author. Read the code before you interact or send coins.

format.gno

8.10 Kb · 408 lines
  1package ufmt
  2
  3import (
  4	"strconv"
  5	"unicode/utf8"
  6)
  7
  8// This file is the low level: padding, integers, floats, strings. It is a port
  9// of format.go in Go's fmt, kept close enough to read side by side.
 10
 11const (
 12	ldigits = "0123456789abcdefx"
 13	udigits = "0123456789ABCDEFX"
 14)
 15
 16// clearFlags resets everything a verb's specifier sets.
 17func (p *printer) clearFlags() {
 18	p.plus, p.minus, p.sharp, p.space, p.zero = false, false, false, false, false
 19	p.plusV, p.sharpV = false, false
 20	p.wid, p.prec = 0, 0
 21	p.widPresent, p.precPresent = false, false
 22}
 23
 24// writePadding writes n pad bytes: zeros when the 0 flag is set, else spaces.
 25func (p *printer) writePadding(n int) {
 26	if n <= 0 {
 27		return
 28	}
 29	c := byte(' ')
 30	if p.zero {
 31		c = '0'
 32	}
 33	for i := 0; i < n; i++ {
 34		p.buf = append(p.buf, c)
 35	}
 36}
 37
 38// pad writes b, padded to the width in runes, on the side the - flag says.
 39func (p *printer) pad(b []byte) {
 40	if !p.widPresent || p.wid == 0 {
 41		p.buf = append(p.buf, b...)
 42		return
 43	}
 44	width := p.wid - utf8.RuneCount(b)
 45	if !p.minus {
 46		p.writePadding(width)
 47		p.buf = append(p.buf, b...)
 48	} else {
 49		p.buf = append(p.buf, b...)
 50		p.writePadding(width)
 51	}
 52}
 53
 54// padString is pad for a string.
 55func (p *printer) padString(s string) {
 56	if !p.widPresent || p.wid == 0 {
 57		p.buf = append(p.buf, s...)
 58		return
 59	}
 60	width := p.wid - utf8.RuneCountInString(s)
 61	if !p.minus {
 62		p.writePadding(width)
 63		p.buf = append(p.buf, s...)
 64	} else {
 65		p.buf = append(p.buf, s...)
 66		p.writePadding(width)
 67	}
 68}
 69
 70// padNoZero pads with spaces whatever the 0 flag says.
 71func (p *printer) padNoZero(b []byte) {
 72	oldZero := p.zero
 73	p.zero = false
 74	p.pad(b)
 75	p.zero = oldZero
 76}
 77
 78func (p *printer) fmtBoolean(v bool) {
 79	if v {
 80		p.padString("true")
 81	} else {
 82		p.padString("false")
 83	}
 84}
 85
 86// fmtInteger formats u, the magnitude, in base, with a '-' when negative.
 87func (p *printer) fmtInteger(u uint64, negative bool, base int, verb rune, digits string) {
 88	// Fast path: no flags, width or precision, lowercase digits.
 89	if !p.widPresent && !p.precPresent && !p.plus && !p.space && !p.sharp &&
 90		verb != 'O' && (base != 16 || digits[10] == 'a') {
 91		if negative {
 92			p.buf = append(p.buf, '-')
 93		}
 94		p.buf = strconv.AppendUint(p.buf, u, base)
 95		return
 96	}
 97
 98	prec := 0
 99	if p.precPresent {
100		prec = p.prec
101		// %.0d of zero prints nothing but its padding.
102		if prec == 0 && u == 0 {
103			oldZero := p.zero
104			p.zero = false
105			p.writePadding(p.wid)
106			p.zero = oldZero
107			return
108		}
109	} else if p.zero && !p.minus && p.widPresent {
110		// Zero padding is a precision in disguise, minus room for the sign.
111		prec = p.wid
112		if negative || p.plus || p.space {
113			prec--
114		}
115	}
116
117	size := 68 // 64 binary digits, a sign and a 0b/0x/0o prefix
118	if prec+4 > size {
119		size = prec + 4
120	}
121	buf := make([]byte, size)
122	i := size
123
124	switch base {
125	case 10:
126		for u >= 10 {
127			i--
128			next := u / 10
129			buf[i] = byte('0' + u - next*10)
130			u = next
131		}
132	case 16:
133		for u >= 16 {
134			i--
135			buf[i] = digits[u&0xF]
136			u >>= 4
137		}
138	case 8:
139		for u >= 8 {
140			i--
141			buf[i] = byte('0' + u&7)
142			u >>= 3
143		}
144	case 2:
145		for u >= 2 {
146			i--
147			buf[i] = byte('0' + u&1)
148			u >>= 1
149		}
150	}
151	i--
152	buf[i] = digits[u]
153	for i > 0 && prec > size-i {
154		i--
155		buf[i] = '0'
156	}
157
158	if p.sharp {
159		switch base {
160		case 2:
161			i--
162			buf[i] = 'b'
163			i--
164			buf[i] = '0'
165		case 8:
166			if buf[i] != '0' {
167				i--
168				buf[i] = '0'
169			}
170		case 16:
171			i--
172			buf[i] = digits[16]
173			i--
174			buf[i] = '0'
175		}
176	}
177	if verb == 'O' {
178		i--
179		buf[i] = 'o'
180		i--
181		buf[i] = '0'
182	}
183
184	if negative {
185		i--
186		buf[i] = '-'
187	} else if p.plus {
188		i--
189		buf[i] = '+'
190	} else if p.space {
191		i--
192		buf[i] = ' '
193	}
194
195	// The zeros, if any, are already in buf: pad the rest with spaces.
196	p.padNoZero(buf[i:])
197}
198
199// fmtUnicode formats u as "U+0078", or "U+0078 'x'" with the # flag.
200func (p *printer) fmtUnicode(u uint64) {
201	prec := 4
202	if p.precPresent && p.prec > 4 {
203		prec = p.prec
204	}
205	hex := strconv.FormatUint(u, 16)
206	b := []byte("U+")
207	for n := len(hex); n < prec; n++ {
208		b = append(b, '0')
209	}
210	for i := 0; i < len(hex); i++ {
211		c := hex[i]
212		if c >= 'a' {
213			c -= 'a' - 'A'
214		}
215		b = append(b, c)
216	}
217	if p.sharp && u <= utf8.MaxRune && strconv.IsPrint(rune(u)) {
218		b = append(b, ' ', '\'')
219		b = utf8.AppendRune(b, rune(u))
220		b = append(b, '\'')
221	}
222	p.padNoZero(b)
223}
224
225// fmtC formats u as the rune it encodes, or U+FFFD when it encodes none.
226func (p *printer) fmtC(u uint64) {
227	r := rune(utf8.RuneError)
228	if u <= utf8.MaxRune {
229		r = rune(u)
230	}
231	p.pad(utf8.AppendRune(nil, r))
232}
233
234// fmtQc formats u as a single-quoted rune literal.
235func (p *printer) fmtQc(u uint64) {
236	r := rune(utf8.RuneError)
237	if u <= utf8.MaxRune {
238		r = rune(u)
239	}
240	if p.plus {
241		p.pad(strconv.AppendQuoteRuneToASCII(nil, r))
242	} else {
243		p.pad(strconv.AppendQuoteRune(nil, r))
244	}
245}
246
247// fmtFloat formats v, with prec digits (-1 for the shortest) unless the verb
248// set its own precision.
249func (p *printer) fmtFloat(v float64, size int, verb rune, prec int) {
250	if p.precPresent {
251		prec = p.prec
252	}
253	// num[0] is reserved for the sign.
254	num := strconv.AppendFloat([]byte{0}, v, byte(verb), prec, size)
255	if num[1] == '-' || num[1] == '+' {
256		num = num[1:]
257	} else {
258		num[0] = '+'
259	}
260	if p.space && num[0] == '+' && !p.plus {
261		num[0] = ' '
262	}
263	// Infinities and NaN do not look like numbers, so they are never zero padded.
264	if num[1] == 'I' || num[1] == 'N' {
265		oldZero := p.zero
266		p.zero = false
267		if num[1] == 'N' && !p.space && !p.plus {
268			num = num[1:]
269		}
270		p.pad(num)
271		p.zero = oldZero
272		return
273	}
274
275	// The # flag keeps the decimal point, and for %g the trailing zeros.
276	if p.sharp && verb != 'b' {
277		digits := 0
278		if verb == 'v' || verb == 'g' || verb == 'G' || verb == 'x' {
279			digits = prec
280			if digits == -1 {
281				digits = 6
282			}
283		}
284		var tail []byte
285		hasDecimalPoint := false
286		sawNonzeroDigit := false
287		for i := 1; i < len(num); i++ {
288			c := num[i]
289			if c == '.' {
290				hasDecimalPoint = true
291				continue
292			}
293			if c == 'p' || c == 'P' || ((c == 'e' || c == 'E') && verb != 'x' && verb != 'X') {
294				tail = append(tail, num[i:]...)
295				num = num[:i]
296				break
297			}
298			if c != '0' {
299				sawNonzeroDigit = true
300			}
301			if sawNonzeroDigit {
302				digits--
303			}
304		}
305		if !hasDecimalPoint {
306			// A lone leading zero counts as one significant digit.
307			if len(num) == 2 && num[1] == '0' {
308				digits--
309			}
310			num = append(num, '.')
311		}
312		for digits > 0 {
313			num = append(num, '0')
314			digits--
315		}
316		num = append(num, tail...)
317	}
318
319	if p.plus || num[0] != '+' {
320		// Zero padding goes after the sign.
321		if p.zero && !p.minus && p.widPresent && p.wid > len(num) {
322			p.buf = append(p.buf, num[0])
323			p.writePadding(p.wid - len(num))
324			p.buf = append(p.buf, num[1:]...)
325			return
326		}
327		p.pad(num)
328		return
329	}
330	p.pad(num[1:])
331}
332
333// truncate cuts s to the precision, counted in runes.
334func (p *printer) truncate(s string) string {
335	if p.precPresent {
336		n := p.prec
337		for i := range s {
338			n--
339			if n < 0 {
340				return s[:i]
341			}
342		}
343	}
344	return s
345}
346
347func (p *printer) fmtS(s string) {
348	p.padString(p.truncate(s))
349}
350
351// fmtSx formats s as hex, two digits a byte. The space flag separates bytes,
352// and # prefixes 0x, once or before each byte when spaced.
353func (p *printer) fmtSx(s string, digits string) {
354	length := len(s)
355	if p.precPresent && p.prec < length {
356		length = p.prec
357	}
358	width := 2 * length
359	if width > 0 {
360		if p.space {
361			if p.sharp {
362				width *= 2
363			}
364			width += length - 1
365		} else if p.sharp {
366			width += 2
367		}
368	} else {
369		if p.widPresent {
370			p.writePadding(p.wid)
371		}
372		return
373	}
374	if p.widPresent && p.wid > width && !p.minus {
375		p.writePadding(p.wid - width)
376	}
377	if p.sharp {
378		p.buf = append(p.buf, '0', digits[16])
379	}
380	for i := 0; i < length; i++ {
381		if p.space && i > 0 {
382			p.buf = append(p.buf, ' ')
383			if p.sharp {
384				p.buf = append(p.buf, '0', digits[16])
385			}
386		}
387		c := s[i]
388		p.buf = append(p.buf, digits[c>>4], digits[c&0xF])
389	}
390	if p.widPresent && p.wid > width && p.minus {
391		p.writePadding(p.wid - width)
392	}
393}
394
395// fmtQ formats s as a double-quoted Go string, backquoted with the # flag when
396// it can be, and ASCII only with the + flag.
397func (p *printer) fmtQ(s string) {
398	s = p.truncate(s)
399	if p.sharp && strconv.CanBackquote(s) {
400		p.padString("`" + s + "`")
401		return
402	}
403	if p.plus {
404		p.pad(strconv.AppendQuoteToASCII(nil, s))
405	} else {
406		p.pad(strconv.AppendQuote(nil, s))
407	}
408}