From e268f521ad1fd83eca6f554127b87cdeb99bd53d Mon Sep 17 00:00:00 2001 From: Jacalz Date: Sat, 10 Apr 2021 18:23:37 +0200 Subject: [PATCH] Add fallback and update gen.go --- data/binding/convert.go | 82 +++++++++++++------ data/binding/convert_benchmark_test.go | 107 +++++++++++++++++++++++++ data/binding/convert_helper.go | 40 +++++++++ data/binding/gen.go | 69 +++++++++++----- 4 files changed, 251 insertions(+), 47 deletions(-) create mode 100644 data/binding/convert_benchmark_test.go diff --git a/data/binding/convert.go b/data/binding/convert.go index c228753a4b..1024c1717c 100644 --- a/data/binding/convert.go +++ b/data/binding/convert.go @@ -5,7 +5,6 @@ package binding import ( "fmt" - "strconv" "fyne.io/fyne/v2" ) @@ -24,7 +23,9 @@ type stringFromBool struct { // // Since: 2.0 func BoolToString(v Bool) String { - return BoolToStringWithFormat(v, "") + str := &stringFromBool{from: v} + v.AddListener(str) + return str } // BoolToStringWithFormat creates a binding that connects a Bool data item to a String and is @@ -33,6 +34,10 @@ func BoolToString(v Bool) String { // // Since: 2.0 func BoolToStringWithFormat(v Bool, format string) String { + if format == "%t" { // Same as not using custom formatting. + return BoolToString(v) + } + str := &stringFromBool{from: v, format: format} v.AddListener(str) return str @@ -48,12 +53,11 @@ func (s *stringFromBool) Get() (string, error) { return fmt.Sprintf(s.format, val), nil } - return strconv.FormatBool(val), nil + return formatBool(val), nil } func (s *stringFromBool) Set(str string) error { var val bool - if s.format != "" { n, err := fmt.Sscanf(str, s.format+" ", &val) // " " denotes match to end of string if err != nil { @@ -63,7 +67,7 @@ func (s *stringFromBool) Set(str string) error { return errParseFailed } } else { - new, err := strconv.ParseBool(str) + new, err := parseBool(str) if err != nil { return err } @@ -105,7 +109,9 @@ type stringFromFloat struct { // // Since: 2.0 func FloatToString(v Float) String { - return FloatToStringWithFormat(v, "") + str := &stringFromFloat{from: v} + v.AddListener(str) + return str } // FloatToStringWithFormat creates a binding that connects a Float data item to a String and is @@ -114,6 +120,10 @@ func FloatToString(v Float) String { // // Since: 2.0 func FloatToStringWithFormat(v Float, format string) String { + if format == "%f" { // Same as not using custom formatting. + return FloatToString(v) + } + str := &stringFromFloat{from: v, format: format} v.AddListener(str) return str @@ -129,12 +139,11 @@ func (s *stringFromFloat) Get() (string, error) { return fmt.Sprintf(s.format, val), nil } - return strconv.FormatFloat(val, 'f', 6, 64), nil + return formatFloat(val), nil } func (s *stringFromFloat) Set(str string) error { var val float64 - if s.format != "" { n, err := fmt.Sscanf(str, s.format+" ", &val) // " " denotes match to end of string if err != nil { @@ -144,7 +153,7 @@ func (s *stringFromFloat) Set(str string) error { return errParseFailed } } else { - new, err := strconv.ParseFloat(str, 64) + new, err := parseFloat(str) if err != nil { return err } @@ -186,7 +195,9 @@ type stringFromInt struct { // // Since: 2.0 func IntToString(v Int) String { - return IntToStringWithFormat(v, "") + str := &stringFromInt{from: v} + v.AddListener(str) + return str } // IntToStringWithFormat creates a binding that connects a Int data item to a String and is @@ -195,6 +206,10 @@ func IntToString(v Int) String { // // Since: 2.0 func IntToStringWithFormat(v Int, format string) String { + if format == "%d" { // Same as not using custom formatting. + return IntToString(v) + } + str := &stringFromInt{from: v, format: format} v.AddListener(str) return str @@ -210,12 +225,11 @@ func (s *stringFromInt) Get() (string, error) { return fmt.Sprintf(s.format, val), nil } - return strconv.Itoa(val), nil + return formatInt(val), nil } func (s *stringFromInt) Set(str string) error { var val int - if s.format != "" { n, err := fmt.Sscanf(str, s.format+" ", &val) // " " denotes match to end of string if err != nil { @@ -225,11 +239,11 @@ func (s *stringFromInt) Set(str string) error { return errParseFailed } } else { - new, err := strconv.ParseInt(str, 0, 64) + new, err := parseInt(str) if err != nil { return err } - val = int(new) + val = new } old, err := s.from.Get() @@ -320,7 +334,9 @@ type stringToBool struct { // // Since: 2.0 func StringToBool(str String) Bool { - return StringToBoolWithFormat(str, "") + v := &stringToBool{from: str} + str.AddListener(v) + return v } // StringToBoolWithFormat creates a binding that connects a String data item to a Bool and is @@ -330,6 +346,10 @@ func StringToBool(str String) Bool { // // Since: 2.0 func StringToBoolWithFormat(str String, format string) Bool { + if format == "%t" { // Same as not using custom format. + return StringToBool(str) + } + v := &stringToBool{from: str, format: format} str.AddListener(v) return v @@ -351,7 +371,7 @@ func (s *stringToBool) Get() (bool, error) { return false, errParseFailed } } else { - new, err := strconv.ParseBool(str) + new, err := parseBool(str) if err != nil { return false, err } @@ -366,7 +386,7 @@ func (s *stringToBool) Set(val bool) error { if s.format != "" { str = fmt.Sprintf(s.format, val) } else { - str = strconv.FormatBool(val) + str = formatBool(val) } old, err := s.from.Get() @@ -402,7 +422,9 @@ type stringToFloat struct { // // Since: 2.0 func StringToFloat(str String) Float { - return StringToFloatWithFormat(str, "") + v := &stringToFloat{from: str} + str.AddListener(v) + return v } // StringToFloatWithFormat creates a binding that connects a String data item to a Float and is @@ -412,6 +434,10 @@ func StringToFloat(str String) Float { // // Since: 2.0 func StringToFloatWithFormat(str String, format string) Float { + if format == "%f" { // Same as not using custom format. + return StringToFloat(str) + } + v := &stringToFloat{from: str, format: format} str.AddListener(v) return v @@ -433,9 +459,9 @@ func (s *stringToFloat) Get() (float64, error) { return 0.0, errParseFailed } } else { - new, err := strconv.ParseFloat(str, 64) + new, err := parseFloat(str) if err != nil { - return 0, err + return 0.0, err } val = new } @@ -448,7 +474,7 @@ func (s *stringToFloat) Set(val float64) error { if s.format != "" { str = fmt.Sprintf(s.format, val) } else { - str = strconv.FormatFloat(val, 'f', 6, 64) + str = formatFloat(val) } old, err := s.from.Get() @@ -484,7 +510,9 @@ type stringToInt struct { // // Since: 2.0 func StringToInt(str String) Int { - return StringToIntWithFormat(str, "") + v := &stringToInt{from: str} + str.AddListener(v) + return v } // StringToIntWithFormat creates a binding that connects a String data item to a Int and is @@ -494,6 +522,10 @@ func StringToInt(str String) Int { // // Since: 2.0 func StringToIntWithFormat(str String, format string) Int { + if format == "%d" { // Same as not using custom format. + return StringToInt(str) + } + v := &stringToInt{from: str, format: format} str.AddListener(v) return v @@ -515,11 +547,11 @@ func (s *stringToInt) Get() (int, error) { return 0, errParseFailed } } else { - new, err := strconv.Atoi(str) + new, err := parseInt(str) if err != nil { return 0, err } - val = int(new) + val = new } return val, nil @@ -530,7 +562,7 @@ func (s *stringToInt) Set(val int) error { if s.format != "" { str = fmt.Sprintf(s.format, val) } else { - str = strconv.Itoa(val) + str = formatInt(val) } old, err := s.from.Get() diff --git a/data/binding/convert_benchmark_test.go b/data/binding/convert_benchmark_test.go new file mode 100644 index 0000000000..b2bd985897 --- /dev/null +++ b/data/binding/convert_benchmark_test.go @@ -0,0 +1,107 @@ +package binding + +import ( + "testing" +) + +func BenchmarkBoolToString(b *testing.B) { + for i := 0; i < b.N; i++ { + bo := NewBool() + s := BoolToString(bo) + s.Get() + + bo.Set(true) + s.Get() + + s.Set("trap") + bo.Get() + + s.Set("false") + bo.Get() + } +} + +func BenchmarkFloatToString(b *testing.B) { + for i := 0; i < b.N; i++ { + f := NewFloat() + s := FloatToString(f) + s.Get() + + f.Set(0.3) + s.Get() + + s.Set("wrong") + f.Get() + + s.Set("5.00") + f.Get() + } +} + +func BenchmarkIntToString(b *testing.B) { + for i := 0; i < b.N; i++ { + i := NewInt() + s := IntToString(i) + s.Get() + + i.Set(3) + s.Get() + + s.Set("wrong") + i.Get() + + s.Set("5") + i.Get() + } +} + +func BenchmarkStringToBool(b *testing.B) { + for i := 0; i < b.N; i++ { + s := NewString() + b := StringToBool(s) + b.Get() + + s.Set("true") + b.Get() + + s.Set("trap") // bug in fmt.SScanf means "wrong" parses as "false" + b.Get() + + b.Set(false) + s.Get() + } +} + +func BenchmarkStringToFloat(b *testing.B) { + for i := 0; i < b.N; i++ { + s := NewString() + f := StringToFloat(s) + f.Get() + + s.Set("3") + f.Get() + + s.Set("wrong") + f.Get() + + f.Set(5) + s.Get() + } +} + +func BenchmarkStringToInt(b *testing.B) { + for i := 0; i < b.N; i++ { + s := NewString() + i := StringToInt(s) + i.Get() + + s.Set("3") + i.Get() + + s.Set("wrong") + i.Get() + + i.Set(5) + s.Get() + } +} diff --git a/data/binding/convert_helper.go b/data/binding/convert_helper.go index 97ecc2d833..a1d3a69e64 100644 --- a/data/binding/convert_helper.go +++ b/data/binding/convert_helper.go @@ -1,6 +1,8 @@ package binding import ( + "strconv" + "fyne.io/fyne/v2" "fyne.io/fyne/v2/storage" ) @@ -16,3 +18,41 @@ func uriToString(in fyne.URI) (string, error) { return in.String(), nil } + +func parseBool(in string) (bool, error) { + out, err := strconv.ParseBool(in) + if err != nil { + return false, err + } + + return out, nil +} + +func parseFloat(in string) (float64, error) { + out, err := strconv.ParseFloat(in, 64) + if err != nil { + return 0, err + } + + return out, nil +} + +func parseInt(in string) (int, error) { + out, err := strconv.ParseInt(in, 0, 64) + if err != nil { + return 0, err + } + return int(out), nil +} + +func formatBool(in bool) string { + return strconv.FormatBool(in) +} + +func formatFloat(in float64) string { + return strconv.FormatFloat(in, 'f', 6, 64) +} + +func formatInt(in int) string { + return strconv.FormatInt(int64(in), 10) +} diff --git a/data/binding/gen.go b/data/binding/gen.go index 993df738f0..a30b546b3a 100644 --- a/data/binding/gen.go +++ b/data/binding/gen.go @@ -154,13 +154,9 @@ type stringFrom{{ .Name }} struct { // // Since: {{ .Since }} func {{ .Name }}ToString(v {{ .Name }}) String { -{{- if .Format }} - return {{ .Name }}ToStringWithFormat(v, "{{ .Format }}") -{{- else }} str := &stringFrom{{ .Name }}{from: v} v.AddListener(str) return str -{{- end }} } {{ if .Format }} // {{ .Name }}ToStringWithFormat creates a binding that connects a {{ .Name }} data item to a String and is @@ -169,6 +165,10 @@ func {{ .Name }}ToString(v {{ .Name }}) String { // // Since: {{ .Since }} func {{ .Name }}ToStringWithFormat(v {{ .Name }}, format string) String { + if format == "{{ .Format }}" { // Same as not using custom formatting. + return {{ .Name }}ToString(v) + } + str := &stringFrom{{ .Name }}{from: v, format: format} v.AddListener(str) return str @@ -182,7 +182,11 @@ func (s *stringFrom{{ .Name }}) Get() (string, error) { {{ if .ToString }} return {{ .ToString }}(val) {{- else }} - return fmt.Sprintf(s.format, val), nil + if s.format != "" { + return fmt.Sprintf(s.format, val), nil + } + + return format{{ .Name }}(val), nil {{- end }} } @@ -194,12 +198,20 @@ func (s *stringFrom{{ .Name }}) Set(str string) error { } {{ else }} var val {{ .Type }} - n, err := fmt.Sscanf(str, s.format+" ", &val) // " " denotes match to end of string - if err != nil { - return err - } - if n != 1 { - return errParseFailed + if s.format != "" { + n, err := fmt.Sscanf(str, s.format+" ", &val) // " " denotes match to end of string + if err != nil { + return err + } + if n != 1 { + return errParseFailed + } + } else { + new, err := parse{{ .Name }}(str) + if err != nil { + return err + } + val = new } {{ end }} old, err := s.from.Get() @@ -239,13 +251,9 @@ type stringTo{{ .Name }} struct { // // Since: {{ .Since }} func StringTo{{ .Name }}(str String) {{ .Name }} { -{{- if .Format }} - return StringTo{{ .Name }}WithFormat(str, "{{ .Format }}") -{{- else }} v := &stringTo{{ .Name }}{from: str} str.AddListener(v) return v -{{- end }} } {{ if .Format }} // StringTo{{ .Name }}WithFormat creates a binding that connects a String data item to a {{ .Name }} and is @@ -255,6 +263,10 @@ func StringTo{{ .Name }}(str String) {{ .Name }} { // // Since: {{ .Since }} func StringTo{{ .Name }}WithFormat(str String, format string) {{ .Name }} { + if format == "{{ .Format }}" { // Same as not using custom format. + return StringTo{{ .Name }}(str) + } + v := &stringTo{{ .Name }}{from: str, format: format} str.AddListener(v) return v @@ -269,12 +281,20 @@ func (s *stringTo{{ .Name }}) Get() ({{ .Type }}, error) { return {{ .FromString }}(str) {{- else }} var val {{ .Type }} - n, err := fmt.Sscanf(str, s.format+" ", &val) // " " denotes match to end of string - if err != nil { - return {{ .Default }}, err - } - if n != 1 { - return {{ .Default }}, errParseFailed + if s.format != "" { + n, err := fmt.Sscanf(str, s.format+" ", &val) // " " denotes match to end of string + if err != nil { + return {{ .Default }}, err + } + if n != 1 { + return {{ .Default }}, errParseFailed + } + } else { + new, err := parse{{ .Name }}(str) + if err != nil { + return {{ .Default }}, err + } + val = new } return val, nil @@ -288,7 +308,12 @@ func (s *stringTo{{ .Name }}) Set(val {{ .Type }}) error { return err } {{- else }} - str := fmt.Sprintf(s.format, val) + var str string + if s.format != "" { + str = fmt.Sprintf(s.format, val) + } else { + str = format{{ .Name }}(val) + } {{ end }} old, err := s.from.Get() if str == old {