{"id":26678949,"url":"https://github.com/pchchv/pbr","last_synced_at":"2026-04-30T00:33:59.692Z","repository":{"id":282370803,"uuid":"948365167","full_name":"pchchv/pbr","owner":"pchchv","description":"Low-level protobuf data reader","archived":false,"fork":false,"pushed_at":"2025-03-21T09:46:40.000Z","size":51,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-03-21T10:40:01.007Z","etag":null,"topics":["go","go-lib","go-library","go-package","golang","golang-library","golang-package","protobuf","protocol-buffers"],"latest_commit_sha":null,"homepage":"https://pkg.go.dev/github.com/pchchv/pbr","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/pchchv.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,"publiccode":null,"codemeta":null}},"created_at":"2025-03-14T07:55:00.000Z","updated_at":"2025-03-21T09:54:45.000Z","dependencies_parsed_at":"2025-03-21T10:33:41.541Z","dependency_job_id":null,"html_url":"https://github.com/pchchv/pbr","commit_stats":null,"previous_names":["pchchv/pbr"],"tags_count":3,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pchchv%2Fpbr","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pchchv%2Fpbr/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pchchv%2Fpbr/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pchchv%2Fpbr/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/pchchv","download_url":"https://codeload.github.com/pchchv/pbr/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":245591495,"owners_count":20640692,"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","go-lib","go-library","go-package","golang","golang-library","golang-package","protobuf","protocol-buffers"],"created_at":"2025-03-26T05:17:45.673Z","updated_at":"2026-04-30T00:33:54.674Z","avatar_url":"https://github.com/pchchv.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# pbr [![CI](https://github.com/pchchv/pbr/workflows/CI/badge.svg)](https://github.com/pchchv/pbr/actions?query=workflow%3ACI+event%3Apush) [![Go Report Card](http://goreportcard.com/badge/github.com/pchchv/pbr)](https://goreportcard.com/report/github.com/pchchv/pbr) [![Godoc Reference](https://pkg.go.dev/badge/github.com/pchchv/pbr)](https://pkg.go.dev/github.com/pchchv/pbr)\n\nPackage `pbr` is a low-level reader for [protocol buffers](https://developers.google.com/protocol-buffers) encoded data in Golang.  \nMain feature is the support for lazy/conditional decoding of fields.\n\nThis package can help decoding performance in two ways:\n\n1. fields can be conditionally decoded, skipping fields that are not needed for a specific use case\n\n2. decoding directly into specific types or performing other transformations, where additional state can be skipped by manually decoding into types directly.\n\n**Note:** using [gogoprotobuf](https://github.com/gogo/protobuf) is still faster.   \n**Note:** Writing code with this package is like writing an auto-generated protobuf decoder and is very time consuming. It should be used only in specific cases and for stable protobuf definitions.\n\n## Usage\n\nFirst, the encoded protobuf data is used to initialize a new Message. Then the fields are searched by reading or skipping them.\n\n```go\nmsg := pbr.New(encodedData)\nfor msg.Next() {\n    switch msg.FieldNumber() {\n    case 1: // an int64 type\n        v, err := msg.Int64()\n        if err != nil {\n            // handle\n        }\n    case 3: // repeated number types can be returned as a slice\n        ids, err := msg.RepeatedInt64(nil)\n        if err != nil {\n            // handle\n        }\n    case 2: // for more control repeated+packed fields can be read using an iterator\n        iter, err := msg.Iterator(nil)\n        if err != nil {\n            // handle\n        }\n\n        userIDs := make([]UserID, 0, iter.Count(pbr.WireTypeVarint))\n        for iter.HasNext() {\n            v, err := iter.Int64()\n            if err != nil {\n                // handle\n            }\n\n            userIDs = append(userIDs, UserID(v))\n        }\n    default:\n        msg.Skip() // required if value not needed.\n    }\n}\n\nif msg.Error() != nil {\n    // handle\n}\n```\n\nAfter calling `Next()` you **must** call an accessor function (`Int64()`, `RepeatedInt64()`, `Iterator()`, etc.) or `Skip()` to ignore the field. All these functions, including `Next()` and `Skip()`, must not be called twice in a row.\n\n### Value Accessor Functions\n\nThere is an accessor for each one the protobuf [scalar value types](https://developers.google.com/protocol-buffers/docs/proto#scalar).\n\nThere is a corresponding set of functions for repeated fields, e.g. `RepeatedInt64(buf []int64) ([]int64, error)`. Repeated fields may or may not be packed, so a predefined buffer variable should be passed when called. E.g.:\n\n```go\nvar ids []int64\nmsg := pbr.New(encodedData)\nfor msg.Next() {\n    switch msg.FieldNumber() {\n    case 1: // repeated int64 field\n        var err error\n        ids, err = msg.RepeatedInt64(ids)\n        if err != nil {\n            // handle\n        }\n    default:\n        msg.Skip()\n    }\n}\n\nif msg.Error() != nil {\n    // handle\n}\n```\n\nIf the ids are 'packed', `RepeatedInt64()` will be called once. If the ids are simply repeated `RepeatedInt64()` will be called N times, but the resulting array of ids will be the same.\n\nFor more control over the values in a packed, repeated field use an Iterator.  \nSee above for an example.\n\n### Decoding Embedded Messages\n\nEmbedded messages can be handled recursively, or the raw data can be returned and decoded using a standard/auto-generated `proto.Unmarshal` function.\n\n```go\nmsg := pbr.New(encodedData)\nfor msg.Next() {\n    fn := msg.FieldNumber()\n    // use pbr recursively\n    if fn == 1 \u0026\u0026 needFieldNumber1 {\n        embeddedMsg, err := msg.Message()\n        for embeddedMsg.Next() {\n            switch embeddedMsg.FieldNumber() {\n            case 1:\n                // do something\n            default:\n                embeddedMsg.Skip()\n            }\n        }\n    }\n\n    // if you need the whole message decode the message in the standard way.\n    if fn == 2 \u0026\u0026 needFieldNumber2 {\n        data, err := msg.MessageData()\n        v := \u0026ProtoBufThing()\n        err = proto.Unmarshal(data, v)\n    }\n}\n```\n\n## Larger Example\nStart with a customer message with embedded orders and items, need to count only the number of items in open orders.\n\n```protobuf\nmessage Customer {\n  required int64 id = 1;\n  optional string username = 2;\n  repeated Order orders = 3;\n  repeated int64 favorite_ids = 4 [packed=true];\n}\n\nmessage Order {\n  required int64 id = 1;\n  required bool open = 2;\n  repeated Item items = 3;\n}\n\nmessage Item {\n  // a big object\n}\n```\n\nSample Code:\n\n```go\nvar openCount, itemCount, favoritesCount int\ncustomer := pbr.New(data)\nfor customer.Next() {\n    switch customer.FieldNumber() {\n    case 1: // id\n        id, err := customer.Int64()\n        if err != nil {\n            panic(err)\n        }\n        _ = id // do something or skip this case if not needed\n    case 2: // username\n        username, err := customer.String()\n        if err != nil {\n            panic(err)\n        }\n        _ = username // do something or skip this case if not needed\n    case 3: // orders\n        var open bool\n        var count int\n        orderData, _ := customer.MessageData()\n        order := pbr.New(orderData)\n        for order.Next() {\n            switch order.FieldNumber() {\n            case 2: // open\n                v, _ := order.Bool()\n                open = v\n            case 3: // item\n                count++\n\n                // we're not reading the data but we still need to skip it.\n                order.Skip()\n            default:\n                // required to move past unneeded fields\n                order.Skip()\n            }\n        }\n\n        if open {\n            openCount++\n            itemCount += count\n        }\n    case 4: // favorite ids\n        iter, err := customer.Iterator(nil)\n        if err != nil {\n        \tpanic(err)\n        }\n\n        // typically this section would only be run once but it is valid\n        // protobuf to contain multiple sections of repeated fields that should be concatenated together\n        favoritesCount += iter.Count(pbr.WireTypeVarint)\n    default:\n        // unread fields must be skipped\n        customer.Skip()\n    }\n}\n\nfmt.Printf(\"Open Orders: %d\\n\", openCount)\nfmt.Printf(\"Items:       %d\\n\", itemCount)\nfmt.Printf(\"Favorites:   %d\\n\", favoritesCount)\n\n// Output:\n// Open Orders: 2\n// Items:       4\n// Favorites:   8\n```\n\n## Wire Type Start Group and End Group\n\nGroups are an old type of protobuf wires that have been deprecated for a long time.\nThey work like parentheses, but do not contain any information about the length of the data.\nTherefore, their contents cannot be effectively skipped. Only the start and end group indicators can be read and skipped like any other field. This will cause the data to be read without parentheses, whatever that means in practice. To get the raw protobuf data within a group, try the following:\n\n```go\nvar groupFieldNum = 123\nvar groupData []byte\nmsg := New(data)\nfor msg.Next() {\n    if msg.FieldNumber() == groupFieldNum \u0026\u0026 msg.WireType() == WireTypeStartGroup {\n        start, end := msg.Index, msg.Index\n        for msg.Next() {\n            msg.Skip()\n            if msg.FieldNumber() == groupFieldNum \u0026\u0026 msg.WireType() == WireTypeEndGroup {\n                break\n            }\n            end = msg.Index\n        }\n        // groupData would be the raw protobuf encoded bytes of the fields in the group.\n        groupData = msg.Data[start:end]\n    }\n}\n```","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpchchv%2Fpbr","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpchchv%2Fpbr","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpchchv%2Fpbr/lists"}