Skip to content

Commit

Permalink
Implement the bool type (#40)
Browse files Browse the repository at this point in the history
This implements encoding and decoding bools.
  • Loading branch information
werber committed Jan 5, 2022
1 parent 2df3b02 commit f234453
Show file tree
Hide file tree
Showing 7 changed files with 124 additions and 44 deletions.
28 changes: 17 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,17 +27,19 @@ people := []struct {
LastName string `fixed:"16,25"`
Grade float64 `fixed:"26,30"`
Age uint `fixed:"31,33"`
Alive bool `fixed:"34,39"`
}{
{1, "Ian", "Lopshire", 99.5, 20},
{1, "Ian", "Lopshire", 99.5, 20, true},
}

data, err := fixedwidth.Marshal(people)
data, err := Marshal(people)
if err != nil {
log.Fatal(err)
}
fmt.Printf("%s", data)
// Output:
// 1 Ian Lopshire 99.5020
// 1 Ian Lopshire 99.5020 true

```

### Decode
Expand All @@ -49,27 +51,31 @@ var people []struct {
LastName string `fixed:"16,25"`
Grade float64 `fixed:"26,30"`
Age uint `fixed:"31,33"`
Alive bool `fixed:"34,39"`
Github bool `fixed:"40,41"`
}

// define some fixed-with data to parse
data := []byte("" +
"1 Ian Lopshire 99.50 20" + "\n" +
"2 John Doe 89.50 21" + "\n" +
"3 Jane Doe 79.50 22" + "\n")

"1 Ian Lopshire 99.50 20 false f" + "\n" +
"2 John Doe 89.50 21 true t" + "\n" +
"3 Jane Doe 79.50 22 false F" + "\n" +
"4 Ann Carraway 79.59 23 false T" + "\n")

err := fixedwidth.Unmarshal(data, &people)
err := Unmarshal(data, &people)
if err != nil {
log.Fatal(err)
}

fmt.Printf("%+v\n", people[0])
fmt.Printf("%+v\n", people[1])
fmt.Printf("%+v\n", people[2])
fmt.Printf("%+v\n", people[3])
// Output:
//{ID:1 FirstName:Ian LastName:Lopshire Grade:99.5 Age:20}
//{ID:2 FirstName:John LastName:Doe Grade:89.5 Age:21}
//{ID:3 FirstName:Jane LastName:Doe Grade:79.5 Age:22}
//{ID:1 FirstName:Ian LastName:Lopshire Grade:99.5 Age:20 Alive:false Github:false}
//{ID:2 FirstName:John LastName:Doe Grade:89.5 Age:21 Alive:true Github:true}
//{ID:3 FirstName:Jane LastName:Doe Grade:79.5 Age:22 Alive:false Github:false}
//{ID:4 FirstName:Ann LastName:Carraway Grade:79.59 Age:23 Alive:false Github:true}
```

It is also possible to read data incrementally
Expand Down
26 changes: 20 additions & 6 deletions bench_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,15 @@ type mixedData struct {
F11 float64 `fixed:"101,110"`
F12 *float64 `fixed:"111,120"`
F13 float32 `fixed:"121,130"`
F14 bool `fixed:"131,140"`
F15 bool `fixed:"141,150"`
//F14 *float32 `fixed:"131,140"`
}

var mixedDataInstance = mixedData{"foo", stringp("foo"), 42, int64p(42), 42, int32p(42), 42, int16p(42), 42, int8p(42), 4.2, float64p(4.2), 4.2} //,float32p(4.2)}
var mixedDataInstance = mixedData{"foo", stringp("foo"), 42, int64p(42), 42, int32p(42), 42, int16p(42), 42, int8p(42), 4.2, float64p(4.2), 4.2, false, true} //,float32p(4.2)}

func BenchmarkUnmarshal_MixedData_1(b *testing.B) {
data := []byte(` foo foo 42 42 42 42 42 42 42 42 4.2 4.2 4.2 4.2`)
data := []byte(` foo foo 42 42 42 42 42 42 42 42 4.2 4.2 4.2 4.2 false t`)
var v mixedData
b.ResetTimer()
for i := 0; i < b.N; i++ {
Expand All @@ -34,7 +36,7 @@ func BenchmarkUnmarshal_MixedData_1(b *testing.B) {
}

func BenchmarkUnmarshal_MixedData_1000(b *testing.B) {
data := bytes.Repeat([]byte(` foo foo 42 42 42 42 42 42 42 42 4.2 4.2 4.2 4.2`+"\n"), 100)
data := bytes.Repeat([]byte(` foo foo 42 42 42 42 42 42 42 42 4.2 4.2 4.2 4.2 false t`+"\n"), 100)
var v []mixedData
b.ResetTimer()
for i := 0; i < b.N; i++ {
Expand All @@ -43,7 +45,7 @@ func BenchmarkUnmarshal_MixedData_1000(b *testing.B) {
}

func BenchmarkUnmarshal_MixedData_100000(b *testing.B) {
data := bytes.Repeat([]byte(` foo foo 42 42 42 42 42 42 42 42 4.2 4.2 4.2 4.2`+"\n"), 10000)
data := bytes.Repeat([]byte(` foo foo 42 42 42 42 42 42 42 42 4.2 4.2 4.2 4.2 false t`+"\n"), 10000)
var v []mixedData
b.ResetTimer()
for i := 0; i < b.N; i++ {
Expand All @@ -52,7 +54,7 @@ func BenchmarkUnmarshal_MixedData_100000(b *testing.B) {
}

func BenchmarkDecode_CodePoints_MixedData_1_Ascii(b *testing.B) {
data := []byte(` foo foo 42 42 42 42 42 42 42 42 4.2 4.2 4.2 4.2`)
data := []byte(` foo foo 42 42 42 42 42 42 42 42 4.2 4.2 4.2 4.2 false t`)
var v mixedData
for i := 0; i < b.N; i++ {
d := NewDecoder(bytes.NewReader(data))
Expand All @@ -62,7 +64,7 @@ func BenchmarkDecode_CodePoints_MixedData_1_Ascii(b *testing.B) {
}

func BenchmarkDecode_CodePoints_MixedData_1_UTF8(b *testing.B) {
data := []byte(` f☃☃ f☃☃ 42 42 42 42 42 42 42 42 4.2 4.2 4.2 4.2`)
data := []byte(` f☃☃ f☃☃ 42 42 42 42 42 42 42 42 4.2 4.2 4.2 4.2 false t`)
var v mixedData
for i := 0; i < b.N; i++ {
d := NewDecoder(bytes.NewReader(data))
Expand Down Expand Up @@ -190,3 +192,15 @@ func BenchmarkMarshal_Float64(b *testing.B) {
Marshal(v)
}
}

func BenchmarkMarshal_Bool(b *testing.B) {
v := struct {
F1 bool `fixed:"1,10"`
}{
F1: false,
}
b.ResetTimer()
for i := 0; i < b.N; i++ {
Marshal(v)
}
}
16 changes: 16 additions & 0 deletions decode.go
Original file line number Diff line number Diff line change
Expand Up @@ -307,6 +307,8 @@ func newValueSetter(t reflect.Type) valueSetter {
return floatSetter(64)
case reflect.Uint, reflect.Uint64, reflect.Uint32, reflect.Uint16, reflect.Uint8:
return uintSetter
case reflect.Bool:
return boolSetter
}
return unknownSetter
}
Expand Down Expand Up @@ -410,3 +412,17 @@ func uintSetter(v reflect.Value, raw rawValue) error {
v.SetUint(uint64(i))
return nil
}

func boolSetter(v reflect.Value, raw rawValue) error {
if len(raw.data) == 0 {
return nil
}

val, err := strconv.ParseBool(raw.data)
if err != nil {
return err
}

v.SetBool(val)
return nil
}
67 changes: 48 additions & 19 deletions decode_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,16 @@ func ExampleUnmarshal() {
LastName string `fixed:"16,25"`
Grade float64 `fixed:"26,30"`
Age uint `fixed:"31,33"`
Alive bool `fixed:"34,39"`
Github bool `fixed:"40,41"`
}

// define some fixed-with data to parse
data := []byte("" +
"1 Ian Lopshire 99.50 20" + "\n" +
"2 John Doe 89.50 21" + "\n" +
"3 Jane Doe 79.50 22" + "\n")
"1 Ian Lopshire 99.50 20 false f" + "\n" +
"2 John Doe 89.50 21 true t" + "\n" +
"3 Jane Doe 79.50 22 false F" + "\n" +
"4 Ann Carraway 79.59 23 false T" + "\n")

err := Unmarshal(data, &people)
if err != nil {
Expand All @@ -34,10 +37,12 @@ func ExampleUnmarshal() {
fmt.Printf("%+v\n", people[0])
fmt.Printf("%+v\n", people[1])
fmt.Printf("%+v\n", people[2])
fmt.Printf("%+v\n", people[3])
// Output:
//{ID:1 FirstName:Ian LastName:Lopshire Grade:99.5 Age:20}
//{ID:2 FirstName:John LastName:Doe Grade:89.5 Age:21}
//{ID:3 FirstName:Jane LastName:Doe Grade:79.5 Age:22}
//{ID:1 FirstName:Ian LastName:Lopshire Grade:99.5 Age:20 Alive:false Github:false}
//{ID:2 FirstName:John LastName:Doe Grade:89.5 Age:21 Alive:true Github:true}
//{ID:3 FirstName:Jane LastName:Doe Grade:79.5 Age:22 Alive:false Github:false}
//{ID:4 FirstName:Ann LastName:Carraway Grade:79.59 Age:23 Alive:false Github:true}
}

func TestUnmarshal(t *testing.T) {
Expand All @@ -48,6 +53,8 @@ func TestUnmarshal(t *testing.T) {
Float float64 `fixed:"11,15"`
TextUnmarshaler EncodableString `fixed:"16,20"`
Uint uint `fixed:"21,25"`
LongBool bool `fixed:"26,31"`
ShortBool bool `fixed:"32,33"`
}
for _, tt := range []struct {
name string
Expand All @@ -58,40 +65,50 @@ func TestUnmarshal(t *testing.T) {
}{
{
name: "Slice Case (no trailing new line)",
rawValue: []byte("foo 123 1.2 bar 12345" + "\n" + "bar 321 2.1 foo 54321"),
rawValue: []byte("foo 123 1.2 bar 12345 false f" + "\n" + "bar 321 2.1 foo 54321 true t some_other_log_here"),
target: &[]allTypes{},
expected: &[]allTypes{
{"foo", 123, 1.2, EncodableString{"bar", nil}, uint(12345)},
{"bar", 321, 2.1, EncodableString{"foo", nil}, uint(54321)},
{"foo", 123, 1.2, EncodableString{"bar", nil}, uint(12345), false, false},
{"bar", 321, 2.1, EncodableString{"foo", nil}, uint(54321), true, true},
},
shouldErr: false,
},
{
name: "Slice Case (trailing new line)",
rawValue: []byte("foo 123 1.2 bar 12345" + "\n" + "bar 321 2.1 foo 54321" + "\n"),
rawValue: []byte("foo 123 1.2 bar 12345 false f" + "\n" + "bar 321 2.1 foo 54321 true t" + "\n"),
target: &[]allTypes{},
expected: &[]allTypes{
{"foo", 123, 1.2, EncodableString{"bar", nil}, uint(12345)},
{"bar", 321, 2.1, EncodableString{"foo", nil}, uint(54321)},
{"foo", 123, 1.2, EncodableString{"bar", nil}, uint(12345), false, false},
{"bar", 321, 2.1, EncodableString{"foo", nil}, uint(54321), true, true},
},
shouldErr: false,
},
{
name: "Slice Case (blank line mid file)",
rawValue: []byte("foo 123 1.2 bar 12345" + "\n" + "\n" + "bar 321 2.1 foo 54321" + "\n"),
rawValue: []byte("foo 123 1.2 bar 12345 false F" + "\n" + "\n" + "bar 321 2.1 foo 54321 true T" + "\n"),
target: &[]allTypes{},
expected: &[]allTypes{
{"foo", 123, 1.2, EncodableString{"bar", nil}, uint(12345)},
{"", 0, 0, EncodableString{"", nil}, uint(0)},
{"bar", 321, 2.1, EncodableString{"foo", nil}, uint(54321)},
{"foo", 123, 1.2, EncodableString{"bar", nil}, uint(12345), false, false},
{"", 0, 0, EncodableString{"", nil}, uint(0), false, false},
{"bar", 321, 2.1, EncodableString{"foo", nil}, uint(54321), true, true},
},
shouldErr: false,
},
{
name: "Slice Case (no trailing new line) with empty content",
rawValue: []byte("foo 123 1.2 bar 12345 false " + "\n" + "bar 321 2.1 foo 54321 true t some_other_log_here"),
target: &[]allTypes{},
expected: &[]allTypes{
{"foo", 123, 1.2, EncodableString{"bar", nil}, uint(12345), false, false},
{"bar", 321, 2.1, EncodableString{"foo", nil}, uint(54321), true, true},
},
shouldErr: false,
},
{
name: "Basic Struct Case",
rawValue: []byte("foo 123 1.2 bar 12345"),
rawValue: []byte("foo 123 1.2 bar 12345 false f"),
target: &allTypes{},
expected: &allTypes{"foo", 123, 1.2, EncodableString{"bar", nil}, uint(12345)},
expected: &allTypes{"foo", 123, 1.2, EncodableString{"bar", nil}, uint(12345), false, false},
shouldErr: false,
},
{
Expand All @@ -110,7 +127,7 @@ func TestUnmarshal(t *testing.T) {
},
{
name: "Invalid Target",
rawValue: []byte("foo 123 1.2 bar baz"),
rawValue: []byte("foo 123 1.2 bar baz false"),
target: allTypes{},
expected: allTypes{},
shouldErr: true,
Expand Down Expand Up @@ -273,6 +290,18 @@ func TestNewValueSetter(t *testing.T) {
{"uint16", []byte("1"), uint16(1), false},
{"uint32", []byte("1"), uint32(1), false},
{"uint64", []byte("1"), uint64(1), false},

{"bool negative", []byte("false"), bool(false), false},
{"bool positive", []byte("true"), bool(true), false},
{"bool empty", []byte(""), bool(false), false},
{"*bool positive", []byte("true"), boolp(true), false},
{"*bool negative", []byte("0"), boolp(false), false},
{"*bool empty", []byte(""), (*bool)(nil), false},
{"bool Invalid", []byte("foo"), bool(true), true},
{"short bool negative (lowercase)", []byte("f"), bool(false), false},
{"short bool positive (lowercase)", []byte("t"), bool(true), false},
{"short bool negative (uppercase)", []byte("F"), bool(false), false},
{"short bool positive (uppercase)", []byte("T"), bool(true), false},
} {
t.Run(tt.name, func(t *testing.T) {
// ensure we have an addressable target
Expand Down
8 changes: 7 additions & 1 deletion encode.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import (
// the encoding.TextMarshaler interface or be based on one
// of the following builtin types: string, int, int64,
// int32, int16, int8, uint, uint64, uint32, uint16,
// uint8, float64, float32, or struct. Pointers to
// uint8, float64, float32, bool, or struct. Pointers to
// encodable types and interfaces containing encodable
// types are also encodable.
//
Expand Down Expand Up @@ -155,6 +155,8 @@ func newValueEncoder(t reflect.Type) valueEncoder {
return floatEncoder(2, 32)
case reflect.Uint, reflect.Uint64, reflect.Uint32, reflect.Uint16, reflect.Uint8:
return uintEncoder
case reflect.Bool:
return boolEncoder
}
return unknownTypeEncoder(t)
}
Expand Down Expand Up @@ -232,6 +234,10 @@ func floatEncoder(perc, bitSize int) valueEncoder {
}
}

func boolEncoder(v reflect.Value) ([]byte, error) {
return []byte(strconv.FormatBool(v.Bool())), nil
}

func nilEncoder(v reflect.Value) ([]byte, error) {
return nil, nil
}
Expand Down
Loading

0 comments on commit f234453

Please sign in to comment.