From 7b4a9e237ec978f8fb939e0328ae6b98f1ed3347 Mon Sep 17 00:00:00 2001 From: Marcos Date: Fri, 19 Aug 2022 18:21:48 +0200 Subject: [PATCH] feat: array fixed type --- README.md | 20 +++++++- builder.go | 8 +++- compiler.go | 4 ++ examples/builder/builder.go | 16 ++++++- examples/compiler/compiler.go | 11 +++++ examples/parser/parser.go | 90 +++++++++++++++++++++++++++++++++-- field_array.go | 70 +++++++++++++++++++++++++++ parser.go | 4 ++ type_array.go | 45 ++++++++++++++++++ type_slice.go | 24 +++------- type_struct.go | 2 +- 11 files changed, 268 insertions(+), 26 deletions(-) create mode 100644 field_array.go create mode 100644 type_array.go diff --git a/README.md b/README.md index ef380c4..a440984 100644 --- a/README.md +++ b/README.md @@ -28,6 +28,7 @@ type ( EvenOrOdd bool Pet Animal Pointer *int + Flags [5]bool } ) @@ -67,7 +68,7 @@ func main() { parco.UInt8Header(), // up to 255 items parco.SmallVarchar(), // each item's type func(e *Example, friends parco.SliceView[string]) { e.Friends = friends }, - func(e *Example) parco.Slice[string] { return e.Friends }, + func(e *Example) parco.SliceView[string] { return e.Friends }, ), ). Bool( @@ -88,7 +89,19 @@ func main() { func(e *Example) *int { return e.Pointer }, ), ). - ParCo() + Array( + parco.ArrayField[Example, bool]( + 5, + parco.Bool(), + func(e *Example, flags parco.SliceView[bool]) { + copy(e.Flags[:], flags) + }, + func(e *Example) parco.SliceView[bool] { + return e.Flags[:] + }, + ), + ). + Parco() ex := Example{ Greet: "hey", @@ -98,6 +111,7 @@ func main() { EvenOrOdd: true, Pet: Animal{Age: 3, Specie: "cat"}, Pointer: parco.Ptr(73), + Flags: [5]bool{true, false, false, true, false}, } output := bytes.NewBuffer(nil) @@ -117,6 +131,7 @@ func main() { panic("not equals") } } + ``` ### Single types @@ -215,6 +230,7 @@ func main() { | bytes (blob) | ✅ | dyn | | map | ✅ | - | | slice | ✅ | - | +| array (fixed) | ✅ | - | | struct | ✅ | - | | time.Time | 👷🚧 | ? | | optional[T] (pointer) | ✅ | 1 + inner size | diff --git a/builder.go b/builder.go index 792884c..5dd498e 100644 --- a/builder.go +++ b/builder.go @@ -35,7 +35,7 @@ func (b ModelBuilder[T]) Parse(r io.Reader) (T, error) { return b.parser.Parse(r) } -func (b ModelBuilder[T]) ParCo() (*Parser[T], *Compiler[T]) { +func (b ModelBuilder[T]) Parco() (*Parser[T], *Compiler[T]) { return b.parser, b.compiler } @@ -57,6 +57,12 @@ func (b ModelBuilder[T]) Slice(field fieldBuilder[T]) ModelBuilder[T] { return b } +func (b ModelBuilder[T]) Array(field fieldBuilder[T]) ModelBuilder[T] { + b.parser.Array(field) + b.compiler.Array(field) + return b +} + func (b ModelBuilder[T]) Varchar(getter Getter[T, string], setter Setter[T, string]) ModelBuilder[T] { b.parser.Varchar(setter) b.compiler.Varchar(getter) diff --git a/compiler.go b/compiler.go index 827610e..147d6c1 100644 --- a/compiler.go +++ b/compiler.go @@ -37,6 +37,10 @@ func (c *Compiler[T]) Slice(field fieldCompiler[T]) *Compiler[T] { return c.register(field) } +func (c *Compiler[T]) Array(field fieldCompiler[T]) *Compiler[T] { + return c.register(field) +} + func (c *Compiler[T]) Map(field fieldCompiler[T]) *Compiler[T] { return c.register(field) } diff --git a/examples/builder/builder.go b/examples/builder/builder.go index aeeadae..eb1fa54 100644 --- a/examples/builder/builder.go +++ b/examples/builder/builder.go @@ -23,6 +23,7 @@ type ( EvenOrOdd bool Pet Animal Pointer *int + Flags [5]bool } ) @@ -83,7 +84,19 @@ func main() { func(e *Example) *int { return e.Pointer }, ), ). - ParCo() + Array( + parco.ArrayField[Example, bool]( + 5, + parco.Bool(), + func(e *Example, flags parco.SliceView[bool]) { + copy(e.Flags[:], flags) + }, + func(e *Example) parco.SliceView[bool] { + return e.Flags[:] + }, + ), + ). + Parco() ex := Example{ Greet: "hey", @@ -93,6 +106,7 @@ func main() { EvenOrOdd: true, Pet: Animal{Age: 3, Specie: "cat"}, Pointer: parco.Ptr(73), + Flags: [5]bool{true, false, false, true, false}, } output := bytes.NewBuffer(nil) diff --git a/examples/compiler/compiler.go b/examples/compiler/compiler.go index 2a9f5c3..adb5041 100644 --- a/examples/compiler/compiler.go +++ b/examples/compiler/compiler.go @@ -22,6 +22,7 @@ type ( EvenOrOdd bool Pet Animal Pointer *int + Flags [5]bool } ) @@ -76,6 +77,15 @@ func main() { parco.Int(binary.LittleEndian), func(e *Example) *int { return e.Pointer }, ), + ). + Array( + parco.ArrayFieldGetter[Example, bool]( + 5, + parco.Bool(), + func(e *Example) parco.SliceView[bool] { + return e.Flags[:] + }, + ), ) ex := Example{ @@ -92,6 +102,7 @@ func main() { Specie: "cat", }, Pointer: parco.Ptr(1), + Flags: [5]bool{true, false, false, true, false}, } output := bytes.NewBuffer(nil) diff --git a/examples/parser/parser.go b/examples/parser/parser.go index 72b2df4..3528a94 100644 --- a/examples/parser/parser.go +++ b/examples/parser/parser.go @@ -10,9 +10,80 @@ import ( var ( data = []byte{ - 3, 104, 101, 121, 42, 4, 7, 64, 98, 111, 108, 105, 114, 105, 8, 64, 100, 97, 110, 105, 114, 111, 100, - 9, 64, 101, 110, 114, 105, 103, 108, 101, 115, 4, 64, 102, 51, 114, 2, 4, 109, 97, 116, 104, 5, 7, 101, - 110, 103, 108, 105, 115, 104, 6, 1, 3, 99, 97, 116, 3, 1, 1, 0, 0, 0, 0, 0, 0, 0, + 3, + 104, + 101, + 121, + 42, + 4, + 7, + 64, + 98, + 111, + 108, + 105, + 114, + 105, + 8, + 64, + 100, + 97, + 110, + 105, + 114, + 111, + 100, + 9, + 64, + 101, + 110, + 114, + 105, + 103, + 108, + 101, + 115, + 4, + 64, + 102, + 51, + 114, + 2, + 4, + 109, + 97, + 116, + 104, + 5, + 7, + 101, + 110, + 103, + 108, + 105, + 115, + 104, + 6, + 1, + 3, + 99, + 97, + 116, + 3, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, } ) @@ -30,6 +101,7 @@ type ( EvenOrOdd bool Pet Animal Pointer *int + Flags [5]bool } ) @@ -84,6 +156,15 @@ func newExampleParser(factory parco.Factory[Example]) *parco.Parser[Example] { parco.Int(binary.LittleEndian), func(e *Example, value *int) { e.Pointer = value }, ), + ). + Array( + parco.ArrayFieldSetter[Example, bool]( + 5, + parco.Bool(), + func(e *Example, flags parco.SliceView[bool]) { + copy(e.Flags[:], flags) + }, + ), ) } @@ -105,6 +186,7 @@ func parseBytes(data []byte) { log.Println(parsed.EvenOrOdd) log.Println(parsed.Pet) log.Println(parsed.Pointer, *parsed.Pointer) + log.Println(parsed.Flags) } func parseStream(data []byte) { @@ -124,6 +206,7 @@ func parseStream(data []byte) { log.Println(parsed.EvenOrOdd) log.Println(parsed.Pet) log.Println(parsed.Pointer, *parsed.Pointer) + log.Println(parsed.Flags) } func parseWithPool(data []byte) { @@ -156,6 +239,7 @@ func parseWithPool(data []byte) { log.Println(parsed.EvenOrOdd) log.Println(parsed.Pet) log.Println(parsed.Pointer, *parsed.Pointer) + log.Println(parsed.Flags) // .... // Release model diff --git a/field_array.go b/field_array.go new file mode 100644 index 0000000..54da728 --- /dev/null +++ b/field_array.go @@ -0,0 +1,70 @@ +package parco + +import "io" + +type ( + BasicArrayField[T, U any] struct { + id string + size int + inner ArrayType[U] + setter Setter[T, SliceView[U]] + getter Getter[T, SliceView[U]] + } +) + +func (s BasicArrayField[T, U]) ID() string { + return s.id +} + +func (s BasicArrayField[T, U]) Parse(item *T, r io.Reader) error { + values, err := s.inner.Parse(r) + if err != nil { + return err + } + s.setter(item, values.Unwrap()) + return nil +} + +func (s BasicArrayField[T, U]) Compile(item *T, w io.Writer) error { + value := s.getter(item) + return s.inner.Compile(value, w) +} + +func ArrayField[T, U any]( + length int, + inner Type[U], + setter Setter[T, SliceView[U]], + getter Getter[T, SliceView[U]], +) Field[T, U] { + return BasicArrayField[T, U]{ + inner: Array[U](length, inner), + setter: setter, + getter: getter, + } +} + +func ArrayFieldGetter[T any, U any]( + length int, + inner Type[U], + getter Getter[T, SliceView[U]], +) Field[T, U] { + return ArrayField[T, U]( + length, + inner, + nil, + getter, + ) +} + +func ArrayFieldSetter[T, U any]( + length int, + inner Type[U], + setter Setter[T, SliceView[U]], +) Field[T, U] { + return ArrayField[T, U]( + length, + inner, + setter, + nil, + ) +} diff --git a/parser.go b/parser.go index df6e629..ba206dd 100644 --- a/parser.go +++ b/parser.go @@ -50,6 +50,10 @@ func (p *Parser[T]) Slice(field fieldParser[T]) *Parser[T] { return p.register(field) } +func (p *Parser[T]) Array(field fieldParser[T]) *Parser[T] { + return p.register(field) +} + func (p *Parser[T]) Map(field fieldParser[T]) *Parser[T] { return p.register(field) } diff --git a/type_array.go b/type_array.go new file mode 100644 index 0000000..517f00f --- /dev/null +++ b/type_array.go @@ -0,0 +1,45 @@ +package parco + +import ( + "io" +) + +type ( + ArrayType[T any] struct { + length int + inner Type[T] + } +) + +func (t ArrayType[T]) ByteLength() int { + return t.length * t.inner.ByteLength() +} + +func (t ArrayType[T]) Parse(r io.Reader) (res Iterable[T], err error) { + values := make([]T, t.length) + + for i := 0; i < t.length; i++ { + values[i], err = t.inner.Parse(r) + if err != nil { + return + } + } + + return SliceView[T](values), nil +} + +func (t ArrayType[T]) Compile(x Iterable[T], w io.Writer) error { + return x.Range(func(x T) error { + if err := t.inner.Compile(x, w); err != nil { + return err + } + return nil + }) +} + +func Array[T any](length int, inner Type[T]) ArrayType[T] { + return ArrayType[T]{ + length: length, + inner: inner, + } +} diff --git a/type_slice.go b/type_slice.go index fca4476..6747466 100644 --- a/type_slice.go +++ b/type_slice.go @@ -15,7 +15,6 @@ type ( length int header IntType inner Type[T] - pool Pooler } ) @@ -28,22 +27,15 @@ func (t SliceType[T]) Parse(r io.Reader) (res Iterable[T], err error) { length int ) length, err = t.header.Parse(r) - t.length = length if err != nil { return nil, err } - values := make([]T, t.length) + arrType := Array[T](length, t.inner) - // TODO: Consider using ParseBytes in order to allocate 1 []byte only - for i := 0; i < t.length; i++ { - values[i], err = t.inner.Parse(r) - if err != nil { - return - } - } + t.length = length - return SliceView[T](values), nil + return arrType.Parse(r) } func (t SliceType[T]) Compile(x Iterable[T], w io.Writer) error { @@ -53,18 +45,14 @@ func (t SliceType[T]) Compile(x Iterable[T], w io.Writer) error { return err } - return x.Range(func(x T) error { - if err := t.inner.Compile(x, w); err != nil { - return err - } - return nil - }) + arrType := Array[T](t.length, t.inner) + + return arrType.Compile(x, w) } func Slice[T any](header IntType, inner Type[T]) SliceType[T] { return SliceType[T]{ header: header, inner: inner, - pool: SinglePool, } } diff --git a/type_struct.go b/type_struct.go index f8b0373..c8d27bd 100644 --- a/type_struct.go +++ b/type_struct.go @@ -12,7 +12,7 @@ func (s StructType[T]) ByteLength() int { } func Struct[T any](b ModelBuilder[T]) StructType[T] { - parser, compiler := b.ParCo() + parser, compiler := b.Parco() return StructParco[T](parser, compiler) }