summaryrefslogtreecommitdiff
path: root/internal
diff options
context:
space:
mode:
authorLuke Shumaker <lukeshu@lukeshu.com>2023-01-26 21:02:56 -0700
committerLuke Shumaker <lukeshu@lukeshu.com>2023-01-30 22:00:25 -0700
commit2828fa21c0ffd2a32a108b37c0417b01abc42929 (patch)
treeae671b894fa952e01a410c94fe27e1d0fec37e80 /internal
parent8aa12d3cb043859229810947da6c52e600d34b55 (diff)
Avoid doing type switching in inner functions
The CPU profiler tells me that the encoder is spending a lot of time on type switches.
Diffstat (limited to 'internal')
-rw-r--r--internal/allwriter.go174
-rw-r--r--internal/base64.go9
2 files changed, 182 insertions, 1 deletions
diff --git a/internal/allwriter.go b/internal/allwriter.go
new file mode 100644
index 0000000..187aa8e
--- /dev/null
+++ b/internal/allwriter.go
@@ -0,0 +1,174 @@
+// Copyright (C) 2023 Luke Shumaker <lukeshu@lukeshu.com>
+//
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+package internal
+
+import (
+ "io"
+ "unicode/utf8"
+)
+
+// interfaces /////////////////////////////////////////////////////////////////
+
+type RuneWriter interface {
+ WriteRune(rune) (int, error)
+}
+
+// An AllWriter is the union of several common writer interfaces.
+type AllWriter interface {
+ io.Writer
+ io.ByteWriter
+ RuneWriter
+ io.StringWriter
+}
+
+// implementations ////////////////////////////////////////////////////////////
+
+func WriteByte(w io.Writer, b byte) error {
+ var buf [1]byte
+ buf[0] = b
+ _, err := w.Write(buf[:])
+ return err
+}
+
+func WriteRune(w io.Writer, r rune) (int, error) {
+ var buf [utf8.UTFMax]byte
+ n := utf8.EncodeRune(buf[:], r)
+ return w.Write(buf[:n])
+}
+
+func WriteString(w io.Writer, s string) (int, error) {
+ return w.Write([]byte(s))
+}
+
+// wrappers ///////////////////////////////////////////////////////////////////
+
+// NNN
+
+type (
+ writerNNN interface{ io.Writer }
+ writerNNNWrapper struct{ writerNNN }
+)
+
+func (w writerNNNWrapper) WriteByte(b byte) error { return WriteByte(w, b) }
+func (w writerNNNWrapper) WriteRune(r rune) (int, error) { return WriteRune(w, r) }
+func (w writerNNNWrapper) WriteString(s string) (int, error) { return WriteString(w, s) }
+
+// NNY
+
+type (
+ writerNNY interface {
+ io.Writer
+ io.StringWriter
+ }
+ writerNNYWrapper struct{ writerNNY }
+)
+
+func (w writerNNYWrapper) WriteByte(b byte) error { return WriteByte(w, b) }
+func (w writerNNYWrapper) WriteRune(r rune) (int, error) { return WriteRune(w, r) }
+
+// NYN
+
+type (
+ writerNYN interface {
+ io.Writer
+ RuneWriter
+ }
+ writerNYNWrapper struct{ writerNYN }
+)
+
+func (w writerNYNWrapper) WriteByte(b byte) error { return WriteByte(w, b) }
+func (w writerNYNWrapper) WriteString(s string) (int, error) { return WriteString(w, s) }
+
+// NYY
+
+type (
+ writerNYY interface {
+ io.Writer
+ RuneWriter
+ io.StringWriter
+ }
+ writerNYYWrapper struct{ writerNYY }
+)
+
+func (w writerNYYWrapper) WriteByte(b byte) error { return WriteByte(w, b) }
+
+// YNN
+
+type (
+ writerYNN interface {
+ io.Writer
+ io.ByteWriter
+ }
+ writerYNNWrapper struct{ writerYNN }
+)
+
+func (w writerYNNWrapper) WriteRune(r rune) (int, error) { return WriteRune(w, r) }
+func (w writerYNNWrapper) WriteString(s string) (int, error) { return WriteString(w, s) }
+
+// YNY
+
+type (
+ writerYNY interface {
+ io.Writer
+ io.ByteWriter
+ io.StringWriter
+ }
+ writerYNYWrapper struct{ writerYNY }
+)
+
+func (w writerYNYWrapper) WriteRune(r rune) (int, error) { return WriteRune(w, r) }
+
+// YYN
+
+type (
+ writerYYN interface {
+ io.Writer
+ io.ByteWriter
+ RuneWriter
+ }
+ writerYYNWrapper struct{ writerYYN }
+)
+
+func (w writerYYNWrapper) WriteString(s string) (int, error) { return WriteString(w, s) }
+
+// NewAllWriter wraps an io.Writer turning it in to an AllWriter. If
+// the io.Writer already has any of the other write methods, then its
+// native version of those methods are used.
+func NewAllWriter(inner io.Writer) AllWriter {
+ switch inner := inner.(type) {
+ // 3 Y bits
+ case AllWriter: // YYY:
+ return inner
+ // 2 Y bits
+ case writerNYY:
+ return writerNYYWrapper{writerNYY: inner}
+ case writerYNY:
+ return writerYNYWrapper{writerYNY: inner}
+ case writerYYN:
+ return writerYYNWrapper{writerYYN: inner}
+ // 1 Y bit
+ case writerNNY:
+ return writerNNYWrapper{writerNNY: inner}
+ case writerNYN:
+ return writerNYNWrapper{writerNYN: inner}
+ case writerYNN:
+ return writerYNNWrapper{writerYNN: inner}
+ // 0 Y bits
+ default: // NNN:
+ return writerNNNWrapper{writerNNN: inner}
+ }
+}
+
+// discard /////////////////////////////////////////////////////////////////////
+
+// Discard is like io.Discard, but implements AllWriter.
+var Discard = discard{}
+
+type discard struct{}
+
+func (discard) Write(p []byte) (int, error) { return len(p), nil }
+func (discard) WriteByte(b byte) error { return nil }
+func (discard) WriteRune(r rune) (int, error) { return 0, nil }
+func (discard) WriteString(s string) (int, error) { return len(s), nil }
diff --git a/internal/base64.go b/internal/base64.go
index 15adbf4..291a229 100644
--- a/internal/base64.go
+++ b/internal/base64.go
@@ -19,7 +19,10 @@ type base64Decoder struct {
bufLen int
}
-func NewBase64Decoder(w io.Writer) io.WriteCloser {
+func NewBase64Decoder(w io.Writer) interface {
+ io.WriteCloser
+ RuneWriter
+} {
return &base64Decoder{
dst: w,
}
@@ -112,6 +115,10 @@ func (dec *base64Decoder) Write(dat []byte) (int, error) {
return len(dat), nil
}
+func (dec *base64Decoder) WriteRune(r rune) (int, error) {
+ return WriteRune(dec, r)
+}
+
func (dec *base64Decoder) Close() error {
if dec.bufLen == 0 {
return nil