Skip to content

Commit

Permalink
Merge pull request #114 from gabe565/decrease-buffer-allocations
Browse files Browse the repository at this point in the history
Optimize serialization by decreasing buffer allocations
  • Loading branch information
arran4 authored Jan 28, 2025
2 parents 4b57639 + 9f4c6ed commit bb7a0a8
Show file tree
Hide file tree
Showing 4 changed files with 43 additions and 32 deletions.
7 changes: 3 additions & 4 deletions calendar.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package ics

import (
"bufio"
"bytes"
"context"
"errors"
"fmt"
Expand Down Expand Up @@ -418,7 +417,7 @@ func NewCalendarFor(service string) *Calendar {
}

func (cal *Calendar) Serialize(ops ...any) string {
b := bytes.NewBufferString("")
b := &strings.Builder{}
// We are intentionally ignoring the return value. _ used to communicate this to lint.
_ = cal.SerializeTo(b, ops...)
return b.String()
Expand All @@ -432,7 +431,7 @@ func (cal *Calendar) SerializeTo(w io.Writer, ops ...any) error {
if err != nil {
return err
}
_, _ = fmt.Fprint(w, "BEGIN:VCALENDAR", serializeConfig.NewLine)
_, _ = io.WriteString(w, "BEGIN:VCALENDAR"+serializeConfig.NewLine)
for _, p := range cal.CalendarProperties {
err := p.serialize(w, serializeConfig)
if err != nil {
Expand All @@ -445,7 +444,7 @@ func (cal *Calendar) SerializeTo(w io.Writer, ops ...any) error {
return err
}
}
_, _ = fmt.Fprint(w, "END:VCALENDAR", serializeConfig.NewLine)
_, _ = io.WriteString(w, "END:VCALENDAR"+serializeConfig.NewLine)
return nil
}

Expand Down
16 changes: 15 additions & 1 deletion calendar_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import (
"bytes"
"embed"
_ "embed"
"github.com/google/go-cmp/cmp"
"io"
"io/fs"
"net/http"
Expand All @@ -15,7 +14,9 @@ import (
"time"
"unicode/utf8"

"github.com/google/go-cmp/cmp"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

var (
Expand Down Expand Up @@ -493,3 +494,16 @@ func TestIssue77(t *testing.T) {
t.Fatalf("Error reading file: %s", err)
}
}

func BenchmarkSerialize(b *testing.B) {
calFile, err := TestData.Open("testdata/serialization/input2.ics")
require.NoError(b, err)

cal, err := ParseCalendar(calFile)
require.NoError(b, err)

b.ResetTimer()
for i := 0; i < b.N; i++ {
cal.Serialize()
}
}
23 changes: 11 additions & 12 deletions components.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package ics

import (
"bytes"
"encoding/base64"
"errors"
"fmt"
Expand Down Expand Up @@ -44,7 +43,7 @@ func (cb *ComponentBase) SubComponents() []Component {
}

func (cb *ComponentBase) serializeThis(writer io.Writer, componentType ComponentType, serialConfig *SerializationConfiguration) error {
_, _ = fmt.Fprint(writer, "BEGIN:"+componentType, serialConfig.NewLine)
_, _ = io.WriteString(writer, "BEGIN:"+string(componentType)+serialConfig.NewLine)
for _, p := range cb.Properties {
err := p.serialize(writer, serialConfig)
if err != nil {
Expand All @@ -57,7 +56,7 @@ func (cb *ComponentBase) serializeThis(writer io.Writer, componentType Component
return err
}
}
_, err := fmt.Fprint(writer, "END:"+componentType, serialConfig.NewLine)
_, err := io.WriteString(writer, "END:"+string(componentType)+serialConfig.NewLine)
return err
}

Expand Down Expand Up @@ -541,7 +540,7 @@ func (event *VEvent) Serialize(serialConfig *SerializationConfiguration) string
}

func (event *VEvent) serialize(serialConfig *SerializationConfiguration) (string, error) {
b := &bytes.Buffer{}
b := &strings.Builder{}
err := event.ComponentBase.serializeThis(b, ComponentVEvent, serialConfig)
return b.String(), err
}
Expand Down Expand Up @@ -615,7 +614,7 @@ func (todo *VTodo) Serialize(serialConfig *SerializationConfiguration) string {
}

func (todo *VTodo) serialize(serialConfig *SerializationConfiguration) (string, error) {
b := &bytes.Buffer{}
b := &strings.Builder{}
err := todo.ComponentBase.serializeThis(b, ComponentVTodo, serialConfig)
if err != nil {
return "", err
Expand Down Expand Up @@ -739,7 +738,7 @@ func (journal *VJournal) Serialize(serialConfig *SerializationConfiguration) str
}

func (journal *VJournal) serialize(serialConfig *SerializationConfiguration) (string, error) {
b := &bytes.Buffer{}
b := &strings.Builder{}
err := journal.ComponentBase.serializeThis(b, ComponentVJournal, serialConfig)
if err != nil {
return "", err
Expand Down Expand Up @@ -785,7 +784,7 @@ func (busy *VBusy) Serialize(serialConfig *SerializationConfiguration) string {
}

func (busy *VBusy) serialize(serialConfig *SerializationConfiguration) (string, error) {
b := &bytes.Buffer{}
b := &strings.Builder{}
err := busy.ComponentBase.serializeThis(b, ComponentVFreeBusy, serialConfig)
if err != nil {
return "", err
Expand Down Expand Up @@ -835,7 +834,7 @@ func (timezone *VTimezone) Serialize(serialConfig *SerializationConfiguration) s
}

func (timezone *VTimezone) serialize(serialConfig *SerializationConfiguration) (string, error) {
b := &bytes.Buffer{}
b := &strings.Builder{}
err := timezone.ComponentBase.serializeThis(b, ComponentVTimezone, serialConfig)
if err != nil {
return "", err
Expand Down Expand Up @@ -895,7 +894,7 @@ func (c *VAlarm) Serialize(serialConfig *SerializationConfiguration) string {
}

func (c *VAlarm) serialize(serialConfig *SerializationConfiguration) (string, error) {
b := &bytes.Buffer{}
b := &strings.Builder{}
err := c.ComponentBase.serializeThis(b, ComponentVAlarm, serialConfig)
if err != nil {
return "", err
Expand Down Expand Up @@ -953,7 +952,7 @@ func (standard *Standard) Serialize(serialConfig *SerializationConfiguration) st
}

func (standard *Standard) serialize(serialConfig *SerializationConfiguration) (string, error) {
b := &bytes.Buffer{}
b := &strings.Builder{}
err := standard.ComponentBase.serializeThis(b, ComponentStandard, serialConfig)
if err != nil {
return "", err
Expand All @@ -975,7 +974,7 @@ func (daylight *Daylight) Serialize(serialConfig *SerializationConfiguration) st
}

func (daylight *Daylight) serialize(serialConfig *SerializationConfiguration) (string, error) {
b := &bytes.Buffer{}
b := &strings.Builder{}
err := daylight.ComponentBase.serializeThis(b, ComponentDaylight, serialConfig)
if err != nil {
return "", err
Expand All @@ -998,7 +997,7 @@ func (general *GeneralComponent) Serialize(serialConfig *SerializationConfigurat
}

func (general *GeneralComponent) serialize(serialConfig *SerializationConfiguration) (string, error) {
b := &bytes.Buffer{}
b := &strings.Builder{}
err := general.ComponentBase.serializeThis(b, ComponentType(general.Token), serialConfig)
if err != nil {
return "", err
Expand Down
29 changes: 14 additions & 15 deletions property.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package ics

import (
"bytes"
"errors"
"fmt"
"io"
Expand Down Expand Up @@ -166,8 +165,8 @@ func (bp *BaseProperty) GetValueType() ValueDataType {
}

func (bp *BaseProperty) serialize(w io.Writer, serialConfig *SerializationConfiguration) error {
b := bytes.NewBufferString("")
_, _ = fmt.Fprint(b, bp.IANAToken)
var b strings.Builder
b.WriteString(bp.IANAToken)

var keys []string
for k := range bp.ICalParameters {
Expand All @@ -176,51 +175,51 @@ func (bp *BaseProperty) serialize(w io.Writer, serialConfig *SerializationConfig
sort.Strings(keys)
for _, k := range keys {
vs := bp.ICalParameters[k]
_, _ = fmt.Fprint(b, ";")
_, _ = fmt.Fprint(b, k)
_, _ = fmt.Fprint(b, "=")
b.WriteByte(';')
b.WriteString(k)
b.WriteByte('=')
for vi, v := range vs {
if vi > 0 {
_, _ = fmt.Fprint(b, ",")
b.WriteByte(',')
}
if Parameter(k).IsQuoted() {
v = quotedValueString(v)
_, _ = fmt.Fprint(b, v)
b.WriteString(v)
} else {
v = escapeValueString(v)
_, _ = fmt.Fprint(b, v)
b.WriteString(v)
}
}
}
_, _ = fmt.Fprint(b, ":")
b.WriteByte(':')
propertyValue := bp.Value
if bp.GetValueType() == ValueDataTypeText {
propertyValue = ToText(propertyValue)
}
_, _ = fmt.Fprint(b, propertyValue)
b.WriteString(propertyValue)
r := b.String()
if len(r) > serialConfig.MaxLength {
l := trimUT8StringUpTo(serialConfig.MaxLength, r)
_, err := fmt.Fprint(w, l, serialConfig.NewLine)
_, err := io.WriteString(w, l+serialConfig.NewLine)
if err != nil {
return fmt.Errorf("property %s serialization: %w", bp.IANAToken, err)
}
r = r[len(l):]

for len(r) > serialConfig.MaxLength-1 {
l := trimUT8StringUpTo(serialConfig.MaxLength-1, r)
_, err = fmt.Fprint(w, " ", l, serialConfig.NewLine)
_, err = io.WriteString(w, " "+l+serialConfig.NewLine)
if err != nil {
return fmt.Errorf("property %s serialization: %w", bp.IANAToken, err)
}
r = r[len(l):]
}
_, err = fmt.Fprint(w, " ")
_, err = io.WriteString(w, " ")
if err != nil {
return fmt.Errorf("property %s serialization: %w", bp.IANAToken, err)
}
}
_, err := fmt.Fprint(w, r, serialConfig.NewLine)
_, err := io.WriteString(w, r+serialConfig.NewLine)
if err != nil {
return fmt.Errorf("property %s serialization: %w", bp.IANAToken, err)
}
Expand Down

0 comments on commit bb7a0a8

Please sign in to comment.