fmt.go 3.24 KB
Newer Older
1 2
// TODO copyright/license

Kirill Smelkov's avatar
.  
Kirill Smelkov committed
3
// Package xfmt provide addons to std fmt and strconv packages with focus on
4
// formatting text without allocations.
Kirill Smelkov's avatar
.  
Kirill Smelkov committed
5 6 7 8
package xfmt

import (
	"encoding/hex"
Kirill Smelkov's avatar
.  
Kirill Smelkov committed
9 10
	"strconv"
	"unicode/utf8"
Kirill Smelkov's avatar
.  
Kirill Smelkov committed
11 12

	"../xslice"
Kirill Smelkov's avatar
.  
Kirill Smelkov committed
13 14

	"lab.nexedi.com/kirr/go123/mem"
Kirill Smelkov's avatar
.  
Kirill Smelkov committed
15 16
)

17 18 19 20 21 22 23 24 25 26 27
const (
	hexdigits = "0123456789abcdef"
)

// Stringer is interface for natively formatting a value representation via xfmt
type Stringer interface {
	// XFmtString method is used to append formatted value to destination buffer
	// The grown buffer have to be returned
	XFmtString(b []byte) []byte
}

Kirill Smelkov's avatar
.  
Kirill Smelkov committed
28 29 30 31 32 33 34 35 36
// Buffer provides syntactic sugar for formatting mimicking fmt.Printf style
// XXX combine with bytes.Buffer ?
type Buffer []byte

// Reset empties the buffer keeping underlying storage for future formattings
func (b *Buffer) Reset() {
	*b = (*b)[:0]
}

Kirill Smelkov's avatar
.  
Kirill Smelkov committed
37 38 39 40 41
// Bytes returns buffer storage as []byte
func (b Buffer) Bytes() []byte {
	return []byte(b)
}

42 43 44 45 46 47 48 49 50 51 52
// Append appends to b formatted x
//
// NOTE sadly since x is interface it makes real value substituted to it
// 	escape to heap (not so a problem since usually they already are) but then also
// 	if x has non-pointer receiver convT2I _allocates_ memory for the value copy.
//
//	-> always pass to append &object, even if object has non-pointer XFmtString receiver.
func Append(b []byte, x Stringer) []byte {
	return x.XFmtString(b)
}

Kirill Smelkov's avatar
.  
Kirill Smelkov committed
53 54 55 56 57 58 59
// V, similarly to %v, adds x formatted by default rules
// XXX -> v(interface {}) ?
func (b *Buffer) V(x Stringer) *Buffer {
	*b = Append(*b, x)
	return b
}

Kirill Smelkov's avatar
.  
Kirill Smelkov committed
60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104
// S appends string formatted by %s
func (b *Buffer) S(s string) *Buffer {
	*b = append(*b, s...)
	return b
}

// Sb appends []byte formatted by %s
func (b *Buffer) Sb(x []byte) *Buffer {
	*b = append(*b, x...)
	return b
}

// Cb appends byte formated by %c
func (b *Buffer) Cb(c byte) *Buffer {
	*b = append(*b, c)
	return b
}


// AppendRune appends to be UTF-8 encoding of r
func AppendRune(b []byte, r rune) []byte {
	l := len(b)
	b = xslice.Grow(b, utf8.UTFMax)
	n := utf8.EncodeRune(b[l:], r)
	return b[:l+n]
}

// C appends rune formatted by %c
func (b *Buffer) C(r rune) *Buffer {
	*b = AppendRune(*b, r)
	return b
}

// D appends int formatted by %d
func (b *Buffer) D(i int) *Buffer {
	*b = strconv.AppendInt(*b, int64(i), 10)
	return b
}

// X appends int formatted by %x
func (b *Buffer) X(i int) *Buffer {
	*b = strconv.AppendInt(*b, int64(i), 16)
	return b
}

105
// AppendHex appends to b hex representation of x
Kirill Smelkov's avatar
.  
Kirill Smelkov committed
106 107 108 109 110 111 112
func AppendHex(b []byte, x []byte) []byte {
	lx := hex.EncodedLen(len(x))
	lb := len(b)
	b = xslice.Grow(b, lx)
	hex.Encode(b[lb:], x)
	return b
}
113

Kirill Smelkov's avatar
.  
Kirill Smelkov committed
114 115
// Xb appends []byte formatted by %x
func (b *Buffer) Xb(x []byte) *Buffer {
Kirill Smelkov's avatar
.  
Kirill Smelkov committed
116 117 118 119
	*b = AppendHex(*b, x)
	return b
}

Kirill Smelkov's avatar
.  
Kirill Smelkov committed
120
// Xs appends string formatted by %x
Kirill Smelkov's avatar
.  
Kirill Smelkov committed
121
func (b *Buffer) Xs(x string) *Buffer {
Kirill Smelkov's avatar
.  
Kirill Smelkov committed
122
	return b.Xb(mem.Bytes(x))
Kirill Smelkov's avatar
.  
Kirill Smelkov committed
123 124 125 126 127 128
}

// TODO XX = %X

// AppendHex016 appends to b x formatted 16-character hex string
func AppendHex016(b []byte, x uint64) []byte {
129 130 131 132 133 134 135 136 137 138
        // like sprintf("%016x") but faster and less allocations
	l := len(b)
        b = xslice.Grow(b, 16)
	bb := b[l:]
        for i := 15; i >= 0; i-- {
                bb[i] = hexdigits[x & 0xf]
                x >>= 4
        }
	return b
}
Kirill Smelkov's avatar
.  
Kirill Smelkov committed
139 140 141 142 143 144

// X016, similarly to %016x, adds hex representation of uint64 x
func (b *Buffer) X016(x uint64) *Buffer {
	*b = AppendHex016(*b, x)
	return b
}