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

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}