Skip to content

Commit

Permalink
Use own bufWriter
Browse files Browse the repository at this point in the history
Simplify and reduce code in Write funcs

Benchmarks:
name                   old time/op    new time/op    delta
ParseAllFrames-4          284µs ± 1%     286µs ± 2%    +0.62%  (p=0.628 n=6+7)
ParseArtistAndTitle-4    39.2µs ±26%    40.3µs ±17%    +2.69%  (p=0.805 n=7+7)
UTF8TextFrame-4           903ns ± 1%    1039ns ± 2%   +15.13%  (p=0.001 n=7+7)
UTF16TextFrame-4         1.95µs ± 2%    2.07µs ± 1%    +6.41%  (p=0.001 n=6+7)
ParseAndWrite-4          10.5ms ±17%    10.2ms ± 3%    -3.06%  (p=0.836 n=7+6)
ReuseTag-4               2.77µs ± 1%    2.85µs ± 4%    +2.70%  (p=0.181 n=6+7)
WriteBytesSize-4         27.3ns ± 2%    44.7ns ± 2%   +63.64%  (p=0.001 n=6+7)
ParseSize-4              8.20ns ± 2%    8.19ns ± 2%      ~     (p=1.000 n=7+7)

name                   old alloc/op   new alloc/op   delta
ParseAllFrames-4          525kB ± 0%     525kB ± 0%    +0.01%  (p=0.710 n=7+7)
ParseArtistAndTitle-4      809B ± 2%      813B ± 1%    +0.48%  (p=0.829 n=7+7)
UTF8TextFrame-4            418B ± 0%      418B ± 0%      ~     (all equal)
UTF16TextFrame-4         1.35kB ± 0%    1.35kB ± 0%      ~     (all equal)
ParseAndWrite-4           270kB ± 1%     271kB ± 0%    +0.64%  (p=0.008 n=7+6)
ReuseTag-4               2.29kB ± 0%    2.29kB ± 0%      ~     (all equal)
WriteBytesSize-4          10.0B ± 0%      0.0B       -100.00%  (p=0.001 n=7+7)
ParseSize-4               0.00B          0.00B           ~     (all equal)

name                   old allocs/op  new allocs/op  delta
ParseAllFrames-4           97.0 ± 0%      97.6 ± 1%    +0.59%  (p=0.070 n=7+7)
ParseArtistAndTitle-4      17.0 ±12%      17.3 ±10%    +1.68%  (p=0.837 n=7+7)
UTF8TextFrame-4            5.00 ± 0%      5.00 ± 0%      ~     (all equal)
UTF16TextFrame-4           20.0 ± 0%      20.0 ± 0%      ~     (all equal)
ParseAndWrite-4             100 ± 1%       100 ± 1%      ~     (p=1.000 n=7+7)
ReuseTag-4                 20.0 ± 0%      20.0 ± 0%      ~     (all equal)
WriteBytesSize-4           0.00           0.00           ~     (all equal)
ParseSize-4                0.00           0.00           ~     (all equal)
  • Loading branch information
n10v committed Apr 7, 2018
1 parent 4991a8d commit 8190b64
Show file tree
Hide file tree
Showing 16 changed files with 181 additions and 238 deletions.
92 changes: 92 additions & 0 deletions buf_writer.go
@@ -0,0 +1,92 @@
package id3v2

import (
"bufio"
"io"
)

type bufWriter struct {
err error
w *bufio.Writer
written int64
}

func newBufWriter(w io.Writer) *bufWriter {
return &bufWriter{w: bufio.NewWriter(w)}
}

func (bw *bufWriter) EncodeAndWriteText(src string, to Encoding) {
if bw.err != nil {
return
}
bw.err = encodeWriteText(bw, src, to)
}

func (bw *bufWriter) Flush() error {
if bw.err != nil {
return bw.err
}
return bw.w.Flush()
}

func (bw *bufWriter) Reset(w io.Writer) {
bw.err = nil
bw.written = 0
bw.w.Reset(w)
}

func (bw *bufWriter) WriteByte(c byte) {
if bw.err != nil {
return
}
bw.err = bw.w.WriteByte(c)
if bw.err == nil {
bw.written += 1
}
}

func (bw *bufWriter) WriteBytesSize(size uint) {
if bw.err != nil {
return
}
bw.err = writeBytesSize(bw, size)
}

func (bw *bufWriter) WriteString(s string) {
if bw.err != nil {
return
}

var n int
n, bw.err = bw.w.WriteString(s)
bw.written += int64(n)
}

func (bw *bufWriter) Write(p []byte) (n int, err error) {
if bw.err != nil {
return 0, bw.err
}
n, err = bw.w.Write(p)
bw.written += int64(n)
bw.err = err
return n, err
}

func (bw *bufWriter) Written() int64 {
return bw.written
}

func useBufWriter(w io.Writer, f func(*bufWriter)) (int64, error) {
var writtenBefore int64
bw, ok := w.(*bufWriter)
if ok {
writtenBefore = bw.Written()
} else {
bw = getBufWriter(w)
defer putBufWriter(bw)
}

f(bw)

return bw.Written() - writtenBefore, bw.Flush()
}
39 changes: 7 additions & 32 deletions comment_frame.go
Expand Up @@ -29,38 +29,13 @@ func (cf CommentFrame) WriteTo(w io.Writer) (n int64, err error) {
return n, ErrInvalidLanguageLength
}

bw, ok := resolveBufioWriter(w)
if !ok {
defer putBufioWriter(bw)
}

var nn int

if err = bw.WriteByte(cf.Encoding.Key); err == nil {
n += 1
}

nn, _ = bw.WriteString(cf.Language)
n += int64(nn)

nn, err = encodeWriteText(bw, cf.Description, cf.Encoding)
n += int64(nn)
if err != nil {
bw.Flush()
return
}

nn, _ = bw.Write(cf.Encoding.TerminationBytes)
n += int64(nn)

nn, err = encodeWriteText(bw, cf.Text, cf.Encoding)
n += int64(nn)
if err != nil {
bw.Flush()
return
}

return n, bw.Flush()
return useBufWriter(w, func(bw *bufWriter) {
bw.WriteByte(cf.Encoding.Key)
bw.WriteString(cf.Language)
bw.EncodeAndWriteText(cf.Description, cf.Encoding)
bw.Write(cf.Encoding.TerminationBytes)
bw.EncodeAndWriteText(cf.Text, cf.Encoding)
})
}

func parseCommentFrame(rd io.Reader) (Framer, error) {
Expand Down
12 changes: 6 additions & 6 deletions encoding.go
@@ -1,8 +1,6 @@
package id3v2

import (
"bufio"

xencoding "golang.org/x/text/encoding"
"golang.org/x/text/encoding/charmap"
"golang.org/x/text/encoding/unicode"
Expand Down Expand Up @@ -91,17 +89,19 @@ func decodeText(src []byte, from Encoding) string {
}

// encodeWriteText encodes src from UTF-8 to "to" encoding and writes to bw.
func encodeWriteText(bw *bufio.Writer, src string, to Encoding) (n int, err error) {
func encodeWriteText(bw *bufWriter, src string, to Encoding) error {
if to.Equals(EncodingUTF8) {
return bw.WriteString(src)
bw.WriteString(src)
return nil
}

toXEncoding := resolveXEncoding(nil, to)
encoded, err := toXEncoding.NewEncoder().String(src)
if err != nil {
return 0, err
return err
}
return bw.WriteString(encoded)
bw.WriteString(encoded)
return nil
}

// resolveXEncoding returns golang.org/x/text/encoding encoding
Expand Down
17 changes: 8 additions & 9 deletions encoding_test.go
@@ -1,7 +1,6 @@
package id3v2

import (
"bufio"
"bytes"
"testing"
)
Expand Down Expand Up @@ -38,22 +37,22 @@ func TestEncodeWriteText(t *testing.T) {
}

buf := new(bytes.Buffer)
bw := bufio.NewWriter(buf)
bw := newBufWriter(buf)

for _, tc := range testCases {
buf.Reset()
n, err := encodeWriteText(bw, tc.src, tc.to)
if err != nil {
t.Errorf("Error by encoding and writing text: %v", err)
}
bw.Reset(buf)

bw.Flush()
bw.EncodeAndWriteText(tc.src, tc.to)
if err := bw.Flush(); err != nil {
t.Fatal(err)
}
got := buf.Bytes()
if !bytes.Equal(got, tc.expected) {
t.Errorf("Expected %q from %q encoding, got %q", tc.expected, tc.to, got)
}
if n != tc.size {
t.Errorf("Expected %v size, got %v", tc.size, n)
if bw.Written() != int64(tc.size) {
t.Errorf("Expected %v size, got %v", tc.size, bw.Written())
}
}
}
30 changes: 0 additions & 30 deletions header.go
Expand Up @@ -5,7 +5,6 @@
package id3v2

import (
"bufio"
"bytes"
"errors"
"io"
Expand Down Expand Up @@ -57,32 +56,3 @@ func parseHeader(rd io.Reader) (tagHeader, error) {
func isID3Tag(data []byte) bool {
return len(data) == len(id3Identifier) && bytes.Equal(data, id3Identifier)
}

func writeTagHeader(bw *bufio.Writer, framesSize uint, version byte) error {
// Identifier
if _, err := bw.Write(id3Identifier); err != nil {
return err
}

// Version
if err := bw.WriteByte(version); err != nil {
return err
}

// Revision
if err := bw.WriteByte(0); err != nil {
return err
}

// Flags
if err := bw.WriteByte(0); err != nil {
return err
}

// Size of frames
if err := writeBytesSize(bw, framesSize); err != nil {
return err
}

return nil
}
7 changes: 2 additions & 5 deletions header_test.go
Expand Up @@ -5,7 +5,6 @@
package id3v2

import (
"bufio"
"bytes"
"testing"
)
Expand Down Expand Up @@ -36,11 +35,9 @@ func TestWriteTagHeader(t *testing.T) {
t.Parallel()

buf := new(bytes.Buffer)
bw := bufio.NewWriter(buf)
bw := newBufWriter(buf)

if err := writeTagHeader(bw, 15351, 4); err != nil {
t.Fatal(err)
}
writeTagHeader(bw, 15351, 4)
if err := bw.Flush(); err != nil {
t.Fatal(err)
}
Expand Down
19 changes: 9 additions & 10 deletions parse_test.go
Expand Up @@ -5,7 +5,6 @@
package id3v2

import (
"bufio"
"bytes"
"errors"
"fmt"
Expand Down Expand Up @@ -320,7 +319,7 @@ func TestParseOptionsParseFramesWithSequenceFrames(t *testing.T) {
} else if commentFrame.Language == "ger" {
isGerCommentInFrame = true
} else {
t.Error("Got unknown comment frame: %v", commentFrame)
t.Errorf("Got unknown comment frame: %v", commentFrame)
}
}

Expand Down Expand Up @@ -348,18 +347,18 @@ func TestParseInvalidFrameSize(t *testing.T) {
t.Parallel()

buf := new(bytes.Buffer)
bw := bufio.NewWriter(buf)
bw := newBufWriter(buf)

// Write tag header.
if err := writeTagHeader(bw, tagHeaderSize+16, 4); err != nil {
t.Fatal(err)
}
bw.Flush()
writeTagHeader(bw, tagHeaderSize+16, 4)
// Write valid TIT2 frame.
buf.Write([]byte{0x54, 0x49, 0x54, 0x32, 00, 00, 00, 06, 00, 00, 03}) // header and encoding
buf.WriteString("Title")
bw.Write([]byte{0x54, 0x49, 0x54, 0x32, 00, 00, 00, 06, 00, 00, 03}) // header and encoding
bw.WriteString("Title")
// Write invalid frame (size byte can't be greater than 127).
buf.Write([]byte{0x54, 0x49, 0x54, 0x32, 255, 255, 255, 255, 00, 00})
bw.Write([]byte{0x54, 0x49, 0x54, 0x32, 255, 255, 255, 255, 00, 00})
if err := bw.Flush(); err != nil {
t.Fatal(err)
}

tag, err := ParseReader(buf, defaultOpts)
if tag == nil || err != nil {
Expand Down
45 changes: 9 additions & 36 deletions picture_frame.go
Expand Up @@ -25,42 +25,15 @@ func (pf PictureFrame) Size() int {
}

func (pf PictureFrame) WriteTo(w io.Writer) (n int64, err error) {
bw, ok := resolveBufioWriter(w)
if !ok {
defer putBufioWriter(bw)
}

var nn int

if err = bw.WriteByte(pf.Encoding.Key); err == nil {
n += 1
}

nn, _ = bw.WriteString(pf.MimeType)
n += int64(nn)

if err = bw.WriteByte(0); err == nil {
n += 1
}

if err = bw.WriteByte(pf.PictureType); err == nil {
n += 1
}

nn, err = encodeWriteText(bw, pf.Description, pf.Encoding)
n += int64(nn)
if err != nil {
bw.Flush()
return
}

nn, _ = bw.Write(pf.Encoding.TerminationBytes)
n += int64(nn)

nn, _ = bw.Write(pf.Picture)
n += int64(nn)

return n, bw.Flush()
return useBufWriter(w, func(bw *bufWriter) {
bw.WriteByte(pf.Encoding.Key)
bw.WriteString(pf.MimeType)
bw.WriteByte(0)
bw.WriteByte(pf.PictureType)
bw.EncodeAndWriteText(pf.Description, pf.Encoding)
bw.Write(pf.Encoding.TerminationBytes)
bw.Write(pf.Picture)
})
}

func parsePictureFrame(rd io.Reader) (Framer, error) {
Expand Down

0 comments on commit 8190b64

Please sign in to comment.