2019-05-22 23:16:55 +02:00
|
|
|
package quicktemplate
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"io"
|
|
|
|
"strconv"
|
|
|
|
"sync"
|
|
|
|
)
|
|
|
|
|
|
|
|
// Writer implements auxiliary writer used by quicktemplate functions.
|
|
|
|
//
|
|
|
|
// Use AcquireWriter for creating new writers.
|
|
|
|
type Writer struct {
|
|
|
|
e QWriter
|
|
|
|
n QWriter
|
|
|
|
}
|
|
|
|
|
|
|
|
// W returns the underlying writer passed to AcquireWriter.
|
|
|
|
func (qw *Writer) W() io.Writer {
|
|
|
|
return qw.n.w
|
|
|
|
}
|
|
|
|
|
|
|
|
// E returns QWriter with enabled html escaping.
|
|
|
|
func (qw *Writer) E() *QWriter {
|
|
|
|
return &qw.e
|
|
|
|
}
|
|
|
|
|
|
|
|
// N returns QWriter without html escaping.
|
|
|
|
func (qw *Writer) N() *QWriter {
|
|
|
|
return &qw.n
|
|
|
|
}
|
|
|
|
|
|
|
|
// AcquireWriter returns new writer from the pool.
|
|
|
|
//
|
|
|
|
// Return unneeded writer to the pool by calling ReleaseWriter
|
|
|
|
// in order to reduce memory allocations.
|
|
|
|
func AcquireWriter(w io.Writer) *Writer {
|
|
|
|
v := writerPool.Get()
|
|
|
|
if v == nil {
|
|
|
|
qw := &Writer{}
|
|
|
|
qw.e.w = &htmlEscapeWriter{}
|
|
|
|
v = qw
|
|
|
|
}
|
|
|
|
qw := v.(*Writer)
|
|
|
|
qw.e.w.(*htmlEscapeWriter).w = w
|
|
|
|
qw.n.w = w
|
|
|
|
return qw
|
|
|
|
}
|
|
|
|
|
|
|
|
// ReleaseWriter returns the writer to the pool.
|
|
|
|
//
|
|
|
|
// Do not access released writer, otherwise data races may occur.
|
|
|
|
func ReleaseWriter(qw *Writer) {
|
|
|
|
hw := qw.e.w.(*htmlEscapeWriter)
|
|
|
|
hw.w = nil
|
|
|
|
qw.e.Reset()
|
|
|
|
qw.e.w = hw
|
|
|
|
|
|
|
|
qw.n.Reset()
|
|
|
|
|
|
|
|
writerPool.Put(qw)
|
|
|
|
}
|
|
|
|
|
|
|
|
var writerPool sync.Pool
|
|
|
|
|
|
|
|
// QWriter is auxiliary writer used by Writer.
|
|
|
|
type QWriter struct {
|
|
|
|
w io.Writer
|
|
|
|
err error
|
|
|
|
b []byte
|
|
|
|
}
|
|
|
|
|
|
|
|
// Write implements io.Writer.
|
|
|
|
func (w *QWriter) Write(p []byte) (int, error) {
|
|
|
|
if w.err != nil {
|
|
|
|
return 0, w.err
|
|
|
|
}
|
|
|
|
n, err := w.w.Write(p)
|
|
|
|
if err != nil {
|
|
|
|
w.err = err
|
|
|
|
}
|
|
|
|
return n, err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Reset resets QWriter to the original state.
|
|
|
|
func (w *QWriter) Reset() {
|
|
|
|
w.w = nil
|
|
|
|
w.err = nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// S writes s to w.
|
|
|
|
func (w *QWriter) S(s string) {
|
|
|
|
w.Write(unsafeStrToBytes(s))
|
|
|
|
}
|
|
|
|
|
|
|
|
// Z writes z to w.
|
|
|
|
func (w *QWriter) Z(z []byte) {
|
|
|
|
w.Write(z)
|
|
|
|
}
|
|
|
|
|
|
|
|
// SZ is a synonym to Z.
|
|
|
|
func (w *QWriter) SZ(z []byte) {
|
|
|
|
w.Write(z)
|
|
|
|
}
|
|
|
|
|
|
|
|
// D writes n to w.
|
|
|
|
func (w *QWriter) D(n int) {
|
|
|
|
bb, ok := w.w.(*ByteBuffer)
|
|
|
|
if ok {
|
|
|
|
bb.B = strconv.AppendInt(bb.B, int64(n), 10)
|
|
|
|
} else {
|
|
|
|
w.b = strconv.AppendInt(w.b[:0], int64(n), 10)
|
|
|
|
w.Write(w.b)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-10-17 16:53:31 +02:00
|
|
|
// DL writes n to w
|
|
|
|
func (w *QWriter) DL(n int64) {
|
|
|
|
bb, ok := w.w.(*ByteBuffer)
|
|
|
|
if ok {
|
|
|
|
bb.B = strconv.AppendInt(bb.B, n, 10)
|
|
|
|
} else {
|
|
|
|
w.b = strconv.AppendInt(w.b[:0], n, 10)
|
|
|
|
w.Write(w.b)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-30 22:39:18 +02:00
|
|
|
// DUL writes n to w
|
|
|
|
func (w *QWriter) DUL(n uint64) {
|
|
|
|
bb, ok := w.w.(*ByteBuffer)
|
|
|
|
if ok {
|
|
|
|
bb.B = strconv.AppendUint(bb.B, n, 10)
|
|
|
|
} else {
|
|
|
|
w.b = strconv.AppendUint(w.b[:0], n, 10)
|
|
|
|
w.Write(w.b)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-05-22 23:16:55 +02:00
|
|
|
// F writes f to w.
|
|
|
|
func (w *QWriter) F(f float64) {
|
|
|
|
n := int(f)
|
|
|
|
if float64(n) == f {
|
|
|
|
// Fast path - just int.
|
|
|
|
w.D(n)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// Slow path.
|
|
|
|
w.FPrec(f, -1)
|
|
|
|
}
|
|
|
|
|
|
|
|
// FPrec writes f to w using the given floating point precision.
|
|
|
|
func (w *QWriter) FPrec(f float64, prec int) {
|
|
|
|
bb, ok := w.w.(*ByteBuffer)
|
|
|
|
if ok {
|
|
|
|
bb.B = strconv.AppendFloat(bb.B, f, 'f', prec, 64)
|
|
|
|
} else {
|
|
|
|
w.b = strconv.AppendFloat(w.b[:0], f, 'f', prec, 64)
|
|
|
|
w.Write(w.b)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Q writes quoted json-safe s to w.
|
|
|
|
func (w *QWriter) Q(s string) {
|
2020-07-10 11:56:03 +02:00
|
|
|
bb, ok := w.w.(*ByteBuffer)
|
|
|
|
if ok {
|
|
|
|
bb.B = appendJSONString(bb.B, s, true)
|
|
|
|
} else {
|
|
|
|
w.b = appendJSONString(w.b[:0], s, true)
|
|
|
|
w.Write(w.b)
|
|
|
|
}
|
2019-05-22 23:16:55 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
var strQuote = []byte(`"`)
|
|
|
|
|
|
|
|
// QZ writes quoted json-safe z to w.
|
|
|
|
func (w *QWriter) QZ(z []byte) {
|
|
|
|
w.Q(unsafeBytesToStr(z))
|
|
|
|
}
|
|
|
|
|
|
|
|
// J writes json-safe s to w.
|
|
|
|
//
|
|
|
|
// Unlike Q it doesn't qoute resulting s.
|
|
|
|
func (w *QWriter) J(s string) {
|
2020-07-10 11:56:03 +02:00
|
|
|
bb, ok := w.w.(*ByteBuffer)
|
|
|
|
if ok {
|
|
|
|
bb.B = appendJSONString(bb.B, s, false)
|
|
|
|
} else {
|
|
|
|
w.b = appendJSONString(w.b[:0], s, false)
|
|
|
|
w.Write(w.b)
|
|
|
|
}
|
2019-05-22 23:16:55 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// JZ writes json-safe z to w.
|
|
|
|
//
|
|
|
|
// Unlike Q it doesn't qoute resulting z.
|
|
|
|
func (w *QWriter) JZ(z []byte) {
|
|
|
|
w.J(unsafeBytesToStr(z))
|
|
|
|
}
|
|
|
|
|
|
|
|
// V writes v to w.
|
|
|
|
func (w *QWriter) V(v interface{}) {
|
|
|
|
fmt.Fprintf(w, "%v", v)
|
|
|
|
}
|
|
|
|
|
|
|
|
// U writes url-encoded s to w.
|
|
|
|
func (w *QWriter) U(s string) {
|
|
|
|
bb, ok := w.w.(*ByteBuffer)
|
|
|
|
if ok {
|
|
|
|
bb.B = appendURLEncode(bb.B, s)
|
|
|
|
} else {
|
|
|
|
w.b = appendURLEncode(w.b[:0], s)
|
|
|
|
w.Write(w.b)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// UZ writes url-encoded z to w.
|
|
|
|
func (w *QWriter) UZ(z []byte) {
|
|
|
|
w.U(unsafeBytesToStr(z))
|
|
|
|
}
|