{"id":13723851,"url":"https://github.com/goccy/go-reflect","last_synced_at":"2025-05-15T11:04:42.499Z","repository":{"id":37591471,"uuid":"277211313","full_name":"goccy/go-reflect","owner":"goccy","description":"Zero-allocation reflection library for Go","archived":false,"fork":false,"pushed_at":"2023-11-16T23:17:57.000Z","size":101,"stargazers_count":564,"open_issues_count":5,"forks_count":22,"subscribers_count":8,"default_branch":"master","last_synced_at":"2025-04-14T16:59:21.513Z","etag":null,"topics":["go","golang","golang-library","reflection"],"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/goccy.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null}},"created_at":"2020-07-05T01:28:11.000Z","updated_at":"2025-04-09T20:33:31.000Z","dependencies_parsed_at":"2024-02-03T12:47:30.490Z","dependency_job_id":null,"html_url":"https://github.com/goccy/go-reflect","commit_stats":{"total_commits":48,"total_committers":4,"mean_commits":12.0,"dds":0.125,"last_synced_commit":"4646ad15ec8afb955a3bfb6af969f007f169f383"},"previous_names":[],"tags_count":7,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/goccy%2Fgo-reflect","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/goccy%2Fgo-reflect/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/goccy%2Fgo-reflect/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/goccy%2Fgo-reflect/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/goccy","download_url":"https://codeload.github.com/goccy/go-reflect/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248923721,"owners_count":21183951,"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":["go","golang","golang-library","reflection"],"created_at":"2024-08-03T01:01:46.335Z","updated_at":"2025-04-14T17:00:08.497Z","avatar_url":"https://github.com/goccy.png","language":"Go","funding_links":[],"categories":["Go"],"sub_categories":[],"readme":"# go-reflect\n\n![Go](https://github.com/goccy/go-reflect/workflows/Go/badge.svg)\n[![GoDoc](https://godoc.org/github.com/goccy/go-reflect?status.svg)](https://pkg.go.dev/github.com/goccy/go-reflect?tab=doc)\n[![codecov](https://codecov.io/gh/goccy/go-reflect/branch/master/graph/badge.svg)](https://codecov.io/gh/goccy/go-reflect)\n[![Go Report Card](https://goreportcard.com/badge/github.com/goccy/go-reflect)](https://goreportcard.com/report/github.com/goccy/go-reflect)\n\nZero-allocation reflection library for Go\n\n# Features\n\n- 100% Compatibility APIs with `reflect` library\n- No allocation occurs when using the reflect.Type features\n- You can choose to escape ( `reflect.ValueOf` ) or noescape ( `reflect.ValueNoEscapeOf` ) when creating reflect.Value\n\n# Status\n\nAll the tests in the reflect library have been passed\nexcept the tests that use some private functions.\n\n# Installation\n\n```bash\ngo get github.com/goccy/go-reflect\n```\n\n# How to use\n\nReplace import statement from `reflect` to `github.com/goccy/go-reflect`\n\n```bash\n-import \"reflect\"\n+import \"github.com/goccy/go-reflect\"\n```\n\n# Benchmarks\n\nSource https://github.com/goccy/go-reflect/blob/master/benchmark_test.go\n\n## Benchmark about reflect.Type\n\n```\n$ go test -bench TypeOf\n```\n\n```\ngoos: darwin\ngoarch: amd64\npkg: github.com/goccy/go-reflect\nBenchmark_TypeOf_Reflect-12             100000000               13.8 ns/op             8 B/op          1 allocs/op\nBenchmark_TypeOf_GoReflect-12           2000000000               1.70 ns/op            0 B/op          0 allocs/op\nPASS\nok      github.com/goccy/go-reflect     5.369s\n```\n\n## Benchmark about reflect.Value\n\n```\n$ go test -bench ValueOf\n```\n\n```\ngoos: darwin\ngoarch: amd64\npkg: github.com/goccy/go-reflect\nBenchmark_ValueOf_Reflect-12            100000000               13.0 ns/op             8 B/op          1 allocs/op\nBenchmark_ValueOf_GoReflect-12          300000000                4.64 ns/op            0 B/op          0 allocs/op\nPASS\nok      github.com/goccy/go-reflect     3.578s\n```\n\n# Real World Example\n\n## Implements Fast Marshaler\n\nI would like to introduce the technique I use for [github.com/goccy/go-json](https://github.com/goccy/go-json).  \nUsing this technique, allocation can be suppressed to once for any marshaler.  \n\nOriginal Source is https://github.com/goccy/go-reflect/blob/master/benchmark_marshaler_test.go\n\n```go\npackage reflect_test\n\nimport (\n    \"errors\"\n    \"strconv\"\n    \"sync\"\n    \"testing\"\n    \"unsafe\"\n\n    \"github.com/goccy/go-reflect\"\n)\n\nvar (\n    typeToEncoderMap sync.Map\n    bufpool          = sync.Pool{\n        New: func() interface{} {\n            return \u0026buffer{\n                b: make([]byte, 0, 1024),\n            }\n        },\n    }\n)\n\ntype buffer struct {\n    b []byte\n}\n\ntype encoder func(*buffer, unsafe.Pointer) error\n\nfunc Marshal(v interface{}) ([]byte, error) {\n\n    // Technique 1.\n    // Get type information and pointer from interface{} value without allocation.\n    typ, ptr := reflect.TypeAndPtrOf(v)\n    typeID := reflect.TypeID(typ)\n\n    // Technique 2.\n    // Reuse the buffer once allocated using sync.Pool\n    buf := bufpool.Get().(*buffer)\n    buf.b = buf.b[:0]\n    defer bufpool.Put(buf)\n\n    // Technique 3.\n    // builds a optimized path by typeID and caches it\n    if enc, ok := typeToEncoderMap.Load(typeID); ok {\n        if err := enc.(encoder)(buf, ptr); err != nil {\n            return nil, err\n        }\n\n        // allocate a new buffer required length only\n        b := make([]byte, len(buf.b))\n        copy(b, buf.b)\n        return b, nil\n    }\n\n    // First time,\n    // builds a optimized path by type and caches it with typeID.\n    enc, err := compile(typ)\n    if err != nil {\n        return nil, err\n    }\n    typeToEncoderMap.Store(typeID, enc)\n    if err := enc(buf, ptr); err != nil {\n        return nil, err\n    }\n\n    // allocate a new buffer required length only\n    b := make([]byte, len(buf.b))\n    copy(b, buf.b)\n    return b, nil\n}\n\nfunc compile(typ reflect.Type) (encoder, error) {\n    switch typ.Kind() {\n    case reflect.Struct:\n        return compileStruct(typ)\n    case reflect.Int:\n        return compileInt(typ)\n    }\n    return nil, errors.New(\"unsupported type\")\n}\n\nfunc compileStruct(typ reflect.Type) (encoder, error) {\n\n    encoders := []encoder{}\n\n    for i := 0; i \u003c typ.NumField(); i++ {\n        field := typ.Field(i)\n        enc, err := compile(field.Type)\n        if err != nil {\n            return nil, err\n        }\n        offset := field.Offset\n        encoders = append(encoders, func(buf *buffer, p unsafe.Pointer) error {\n            return enc(buf, unsafe.Pointer(uintptr(p)+offset))\n        })\n    }\n    return func(buf *buffer, p unsafe.Pointer) error {\n        buf.b = append(buf.b, '{')\n        for _, enc := range encoders {\n            if err := enc(buf, p); err != nil {\n                return err\n            }\n        }\n        buf.b = append(buf.b, '}')\n        return nil\n    }, nil\n}\n\nfunc compileInt(typ reflect.Type) (encoder, error) {\n    return func(buf *buffer, p unsafe.Pointer) error {\n        value := *(*int)(p)\n        buf.b = strconv.AppendInt(buf.b, int64(value), 10)\n        return nil\n    }, nil\n}\n\nfunc Benchmark_Marshal(b *testing.B) {\n    b.ReportAllocs()\n    for n := 0; n \u003c b.N; n++ {\n        bytes, err := Marshal(struct{ I int }{10})\n        if err != nil {\n            b.Fatal(err)\n        }\n        if string(bytes) != \"{10}\" {\n            b.Fatal(\"unexpected error\")\n        }\n    }\n}\n```\n\nThe benchmark result is as follows.  \n\n```bash\n\n$ go test -bench Benchmark_Marshal\ngoos: darwin\ngoarch: amd64\npkg: github.com/goccy/go-reflect\nBenchmark_Marshal-16            16586372                71.0 ns/op             4 B/op          1 allocs/op\nPASS\n```\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgoccy%2Fgo-reflect","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fgoccy%2Fgo-reflect","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgoccy%2Fgo-reflect/lists"}