{"id":23588123,"url":"https://github.com/sonirico/parco","last_synced_at":"2025-05-07T10:34:58.257Z","repository":{"id":49254508,"uuid":"437430515","full_name":"sonirico/parco","owner":"sonirico","description":"🏇🏻 generalist, fast and tiny binary parser and compiler generator, powered by Go 1.18+ Generics","archived":false,"fork":false,"pushed_at":"2022-09-06T19:31:28.000Z","size":143,"stargazers_count":58,"open_issues_count":0,"forks_count":1,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-05-07T10:34:53.634Z","etag":null,"topics":["binary","compiler","deserialization","generics","go","golang","parser","serialization"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/sonirico.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null},"funding":{"github":["sonirico"],"patreon":null,"open_collective":null,"ko_fi":null,"tidelift":null,"community_bridge":null,"liberapay":null,"issuehunt":null,"otechie":null,"lfx_crowdfunding":null,"custom":null}},"created_at":"2021-12-12T01:55:48.000Z","updated_at":"2025-03-23T12:08:48.000Z","dependencies_parsed_at":"2022-08-12T20:01:13.452Z","dependency_job_id":null,"html_url":"https://github.com/sonirico/parco","commit_stats":null,"previous_names":[],"tags_count":14,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sonirico%2Fparco","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sonirico%2Fparco/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sonirico%2Fparco/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sonirico%2Fparco/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/sonirico","download_url":"https://codeload.github.com/sonirico/parco/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":252860321,"owners_count":21815497,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["binary","compiler","deserialization","generics","go","golang","parser","serialization"],"created_at":"2024-12-27T05:12:28.615Z","updated_at":"2025-05-07T10:34:58.219Z","avatar_url":"https://github.com/sonirico.png","language":"Go","funding_links":["https://github.com/sponsors/sonirico"],"categories":[],"sub_categories":[],"readme":"![build](https://github.com/sonirico/parco/actions/workflows/go.yml/badge.svg)\n# Parco\n\nHobbyist binary compiler and parser built with no reflection, highly\nextensible, focused on performance, usability through generics and\nwith zero dependencies.\n\nThere are plenty packages over the internet which work by leveraging the power of\nstruct tags and reflection. While sometimes that can be convenient for some\nscenarios, that approach leaves little room to define and register custom types in\naddition to have an appositive effect on performance.\n\n## Usage\n\n### Parser \u0026 compiler\n\n```go\npackage main\n\nimport (\n  \"bytes\"\n  \"encoding/binary\"\n  \"encoding/json\"\n  \"log\"\n  \"reflect\"\n  \"time\"\n\n  \"github.com/sonirico/parco\"\n)\n\ntype (\n  Animal struct {\n    Age    uint8\n    Specie string\n  }\n\n  Example struct {\n    Greet              string\n    LifeSense          uint8\n    Friends            []string\n    Grades             map[string]uint8\n    EvenOrOdd          bool\n    Pet                Animal\n    Pointer            *int\n    Flags              [5]bool\n    Balance            float32\n    MorePreciseBalance float64\n    CreatedAt          time.Time\n  }\n)\n\nfunc (e Example) String() string {\n  bts, _ := json.MarshalIndent(e, \"\", \"\\t\")\n  return string(bts)\n}\n\nfunc main() {\n  animalBuilder := parco.Builder[Animal](parco.ObjectFactory[Animal]()).\n    SmallVarchar(\n      func(a *Animal) string { return a.Specie },\n      func(a *Animal, specie string) { a.Specie = specie },\n    ).\n    UInt8(\n      func(a *Animal) uint8 { return a.Age },\n      func(a *Animal, age uint8) { a.Age = age },\n    )\n\n  exampleFactory := parco.ObjectFactory[Example]()\n\n  exampleParser, exampleCompiler := parco.Builder[Example](exampleFactory).\n    SmallVarchar(\n      func(e *Example) string { return e.Greet },\n      func(e *Example, s string) { e.Greet = s },\n    ).\n    UInt8(\n      func(e *Example) uint8 { return e.LifeSense },\n      func(e *Example, lifeSense uint8) { e.LifeSense = lifeSense },\n    ).\n    Map(\n      parco.MapField[Example, string, uint8](\n        parco.UInt8Header(),\n        parco.SmallVarchar(),\n        parco.UInt8(),\n        func(s *Example, grades map[string]uint8) { s.Grades = grades },\n        func(s *Example) map[string]uint8 { return s.Grades },\n      ),\n    ).\n    Slice(\n      parco.SliceField[Example, string](\n        parco.UInt8Header(),  // up to 255 items\n        parco.SmallVarchar(), // each item's type\n        func(e *Example, friends parco.SliceView[string]) { e.Friends = friends },\n        func(e *Example) parco.SliceView[string] { return e.Friends },\n      ),\n    ).\n    Bool(\n      func(e *Example) bool { return e.EvenOrOdd },\n      func(e *Example, evenOrOdd bool) { e.EvenOrOdd = evenOrOdd },\n    ).\n    Struct(\n      parco.StructField[Example, Animal](\n        func(e *Example) Animal { return e.Pet },\n        func(e *Example, a Animal) { e.Pet = a },\n        animalBuilder,\n      ),\n    ).\n    Option(\n      parco.OptionField[Example, int](\n        parco.Int(binary.LittleEndian),\n        func(e *Example, value *int) { e.Pointer = value },\n        func(e *Example) *int { return e.Pointer },\n      ),\n    ).\n    Array(\n      parco.ArrayField[Example, bool](\n        5,\n        parco.Bool(),\n        func(e *Example, flags parco.SliceView[bool]) {\n          copy(e.Flags[:], flags)\n        },\n        func(e *Example) parco.SliceView[bool] {\n          return e.Flags[:]\n        },\n      ),\n    ).\n    Float32(\n      binary.LittleEndian,\n      func(e *Example) float32 {\n        return e.Balance\n      },\n      func(e *Example, balance float32) {\n        e.Balance = balance\n      },\n    ).\n    Float64(\n      binary.LittleEndian,\n      func(e *Example) float64 {\n        return e.MorePreciseBalance\n      },\n      func(e *Example, balance float64) {\n        e.MorePreciseBalance = balance\n      },\n    ).\n    TimeUTC(\n      func(e *Example) time.Time {\n        return e.CreatedAt\n      },\n      func(e *Example, createdAt time.Time) {\n        e.CreatedAt = createdAt\n      },\n    ).\n    Parco()\n\n  ex := Example{\n    Greet:              \"hey\",\n    LifeSense:          42,\n    Grades:             map[string]uint8{\"math\": 5, \"english\": 6},\n    Friends:            []string{\"@boliri\", \"@danirod\", \"@enrigles\", \"@f3r\"},\n    EvenOrOdd:          true,\n    Pet:                Animal{Age: 3, Specie: \"cat\"},\n    Pointer:            parco.Ptr(73),\n    Flags:              [5]bool{true, false, false, true, false},\n    Balance:            234.987,\n    MorePreciseBalance: 1234243.5678,\n    CreatedAt:          time.Now().UTC(),\n  }\n\n  output := bytes.NewBuffer(nil)\n  if err := exampleCompiler.Compile(ex, output); err != nil {\n    log.Fatal(err)\n  }\n\n  log.Println(parco.FormatBytes(output.Bytes()))\n\n  parsed, err := exampleParser.ParseBytes(output.Bytes())\n\n  if err != nil {\n    log.Fatal(err)\n  }\n\n  log.Println(parsed.String())\n\n  if !reflect.DeepEqual(ex, parsed) {\n    panic(\"not equals\")\n  }\n}\n```\n\n### Single types\n\n#### Integer\n\n```go\nfunc main () {\n  intType := parco.Int(binary.LittleEndian)\n  buf := bytes.NewBuffer(nil)\n  _ = intType.Compile(math.MaxInt, buf)\n  n, _ := intType.Parse(buf)\n  log.Println(n == math.MaxInt)\n}\n```\n\n#### Slice of structs\n\n\n```go\ntype (\n  Animal struct {\n    Age    uint8\n    Specie string\n  }\n)\n\nfunc main() {\n  animalBuilder := parco.Builder[Animal](parco.ObjectFactory[Animal]()).\n    SmallVarchar(\n      func(a *Animal) string { return a.Specie },\n      func(a *Animal, specie string) { a.Specie = specie },\n    ).\n    UInt8(\n      func(a *Animal) uint8 { return a.Age },\n      func(a *Animal, age uint8) { a.Age = age },\n    )\n\n  animalsType := parco.Slice[Animal](\n    intType,\n    parco.Struct[Animal](animalBuilder),\n  )\n\n  payload := []Animal{\n    {\n      Specie: \"cat\",\n      Age:    32,\n    },\n    {\n      Specie: \"dog\",\n      Age:    12,\n    },\n  }\n\n  _ = animalsType.Compile(parco.SliceView[Animal](payload), buf)\n\n  log.Println(buf.Bytes())\n\n  res, _ := animalsType.Parse(buf)\n\n  log.Println(res.Len())\n\n  _ = res.Range(func(animal Animal) error {\n    log.Println(animal)\n    return nil\n  })\n}\n```\n\n---\n\n### Multi model parsers \u0026 compilers\n\nIt is also supported several models being serialized on the same wire. Just employ the multimodel API\nto register them.\n\n```go\ntype (\n  Animal struct {\n    Age    uint8\n    Specie string\n  }\n\n  Flat struct {\n    Price   float32\n    Address string\n  }\n)\n\nconst (\n  AnimalType int = 0\n  FlatType       = 1\n)\n\nfunc (a Animal) ParcoID() int {\n  return AnimalType\n}\n\nfunc (a Flat) ParcoID() int {\n  return FlatType\n}\n\nfunc main() {\n  animalBuilder := parco.Builder[Animal](parco.ObjectFactory[Animal]()).\n    SmallVarchar(\n      func(a *Animal) string { return a.Specie },\n      func(a *Animal, specie string) { a.Specie = specie },\n    ).\n    UInt8(\n      func(a *Animal) uint8 { return a.Age },\n      func(a *Animal, age uint8) { a.Age = age },\n    )\n\n  flatBuilder := parco.Builder[Flat](parco.ObjectFactory[Flat]()).\n    Float32(\n      binary.LittleEndian,\n      func(f *Flat) float32 { return f.Price },\n      func(f *Flat, price float32) { f.Price = price },\n    ).\n    SmallVarchar(\n      func(f *Flat) string { return f.Address },\n      func(f *Flat, address string) { f.Address = address },\n    )\n\n  parCo := parco.MultiBuilder(parco.UInt8Header()). // Register up to 255 different models\n                MustRegister(AnimalType, animalBuilder).\n                MustRegister(FlatType, flatBuilder)\n\n  buf := bytes.NewBuffer(nil)\n\n  // `Compile` API may be used if your models satisfy the `serializable` interface:\n  // type seriazable[T comparable] interface{ ParcoID() int }\n  _ = parCo.Compile(Animal{Age: 10, Specie: \"monkeys\"}, buf)\n  _ = parCo.Compile(Flat{Price: 42, Address: \"Plaza mayor\"}, buf)\n\n  // Or, the `CompileAny` can be employed instead by specifying each model ID.\n  _ = parCo.CompileAny(AnimalType, Animal{Age: 7, Specie: \"felix catus\"}, buf)\n\n  id, something, _ := parCo.Parse(buf)\n  Print(id, something)\n  id, something, _ = parCo.Parse(buf)\n  Print(id, something)\n  id, something, _ = parCo.Parse(buf)\n  Print(id, something)\n}\n\nfunc Print(id int, x any) {\n  switch id {\n  case AnimalType:\n    animal := x.(Animal)\n    log.Println(\"animal:\", animal)\n  case FlatType:\n    flat := x.(Flat)\n    log.Println(\"flat\", flat)\n  }\n}\n```\n\n### Supported fields\n\n| Field                 | Status | Size                           |\n|-----------------------|--------|--------------------------------|\n| byte                  | ✅      | 1                              |\n| int8                  | ✅      | 1                              |\n| uint8                 | ✅      | 1                              |\n| int16                 | ✅      | 2                              |\n| uint16                | ✅      | 2                              |\n| int32                 | ✅      | 4                              |\n| uint32                | ✅      | 4                              |\n| int64                 | ✅      | 8                              |\n| uint64                | ✅      | 8                              |\n| float32               | ✅      | 4                              |\n| float64               | ✅      | 8                              |\n| int                   | ✅      | 4/8                            |\n| bool                  | ✅      | 1                              |\n| small varchar         | ✅      | dyn (up to 255)                |\n| varchar               | ✅      | dyn (up to 65535)              |\n| text                  | ✅      | dyn (up to max uint32 chars)   |\n| long text             | ✅      | dyn (up to max uint64 chars)   |\n| string                | ✅      | dyn                            |\n| bytes (blob)          | ✅      | dyn                            |\n| map                   | ✅      | -                              |\n| slice                 | ✅      | -                              |\n| array (fixed)         | ✅      | -                              |\n| struct                | ✅      | -                              |\n| time.Time             | ✅      | 8 (+small varchar if TZ aware) |\n| optional[T] (pointer) | ✅      | 1 + inner size                 |\n\nFor fully functional examples showing the whole API, refer to [Examples](https://github.com/sonirico/parco/tree/master/examples).\n\n\n## Benchmarks\n\n```shell\nmake bench\n\ngoos: darwin\ngoarch: amd64\ncpu: Intel(R) Core(TM) i7-8750H CPU @ 2.20GHz\nParcoAlloc_Compile\nParcoAlloc_Compile/small_size\nParcoAlloc_Compile/small_size-12    276934       4015 ns/op      91.00 payload_bytes/op    237 B/op     5 allocs/op\nParcoAlloc_Compile/medium_size\nParcoAlloc_Compile/medium_size-12    48273      24906 ns/op     742.0 payload_bytes/op     239 B/op     5 allocs/op\nParcoAlloc_Compile/large_size\nParcoAlloc_Compile/large_size-12      4705     247203 ns/op    8123 payload_bytes/op       245 B/op     5 allocs/op\nParcoDiscard_Compile\nParcoDiscard_Compile/small_size\nParcoDiscard_Compile/small_size-12  322285       3741 ns/op      91.00 payload_bytes/op    238 B/op     5 allocs/op\nParcoDiscard_Compile/medium_size\nParcoDiscard_Compile/medium_size-12  50703      23336 ns/op     742.0 payload_bytes/op     238 B/op     5 allocs/op\nParcoDiscard_Compile/large_size\nParcoDiscard_Compile/large_size-12    5406     220967 ns/op    8123 payload_bytes/op       241 B/op     5 allocs/op\nJson_Compile\nJson_Compile/small_size\nJson_Compile/small_size-12          213540       5410 ns/op     270.0 payload_bytes/op    1330 B/op    26 allocs/op\nJson_Compile/medium_size\nJson_Compile/medium_size-12          23980      49912 ns/op    1680 payload_bytes/op     10256 B/op   206 allocs/op\nJson_Compile/large_size\nJson_Compile/large_size-12            2014     581209 ns/op   16598 payload_bytes/op    101265 B/op  2006 allocs/op\nMsgpack_Compile\nMsgpack_Compile/small_size\nMsgpack_Compile/small_size-12       242005       4760 ns/op     155.0 payload_bytes/op     762 B/op    25 allocs/op\nMsgpack_Compile/medium_size\nMsgpack_Compile/medium_size-12       33638      35485 ns/op     991.0 payload_bytes/op    4069 B/op   207 allocs/op\nMsgpack_Compile/large_size\nMsgpack_Compile/large_size-12         3277     357921 ns/op   10171 payload_bytes/op     37448 B/op  2007 allocs/op\n```\n\n## Roadmap\n\n- Static code generation.\n- Replace `encoding/binary` usage by faster implementations, such as `WriteByte` in order to achieve a zero alloc implementation.\n- Custom `Reader` and `Writer` interfaces to implement single byte ops.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsonirico%2Fparco","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsonirico%2Fparco","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsonirico%2Fparco/lists"}