递归数据结构Unarshalling会产生错误;在lang protobuf中

发布于 2025-01-26 09:18:02 字数 2147 浏览 5 评论 0原文

OS和Protobuf版本

go1.18.1 linux/amd64,github.com/golang/protobuf v1.5.5.2

简介

我正在尝试使用递归原始定义。

.proto文件

message AsyncConsensus {
  int32 sender = 1;
  int32 receiver = 2;
  string unique_id = 3; // to specify the fall back block id to which the vote asyn is for
  int32 type = 4; // 1-propose, 2-vote, 3-timeout, 4-propose-async, 5-vote-async, 6-timeout-internal, 7-consensus-external-request, 8-consensus-external-response, 9-fallback-complete
  string note = 5;
  int32 v = 6 ; // view number
  int32 r = 7;// round number
  message Block {
    string id = 1;
    int32 v = 2 ; // view number
    int32 r = 3;// round number
    Block parent = 4;
    repeated int32 commands = 5;
    int32 level = 6; // for the fallback mode
  }
  Block blockHigh = 8;
  Block blockNew = 9;
  Block blockCommit = 10;
}

以下是我和UN-MARSHAL

func (t *AsyncConsensus) Marshal(wire io.Writer) error {
    data, err := proto.Marshal(t)
    if err != nil {
        return err
    }
    lengthWritten := len(data)
    var b [8]byte
    bs := b[:8]
    binary.LittleEndian.PutUint64(bs, uint64(lengthWritten))
    _, err = wire.Write(bs)
    if err != nil {
        return err
    }
    _, err = wire.Write(data)
    if err != nil {
        return err
    }
    return nil
}

func (t *AsyncConsensus) Unmarshal(wire io.Reader) error {

    var b [8]byte
    bs := b[:8]
    _, err := io.ReadFull(wire, bs)
    if err != nil {
        return err
    }
    numBytes := binary.LittleEndian.Uint64(bs)
    data := make([]byte, numBytes)
    length, err := io.ReadFull(wire, data)
    if err != nil {
        return err
    }
    err = proto.Unmarshal(data[:length], t)
    if err != nil {
        return err
    }
    return nil
}

func (t *AsyncConsensus) New() Serializable {
    return new(AsyncConsensus)
}

我的预期结果

在通过TCP填写并发送到同一过程时,它应该正确地删除并产生正确的数据结构。

产生的错误

错误“无法解析无效的线程 - 格式数据”

其他信息

我尝试了非恢复.proto定义,以前从未有这个问题。

OS and protobuf version

go1.18.1 linux/amd64, github.com/golang/protobuf v1.5.2

Introduction

I am trying to use recursive proto definitions.

.proto file

message AsyncConsensus {
  int32 sender = 1;
  int32 receiver = 2;
  string unique_id = 3; // to specify the fall back block id to which the vote asyn is for
  int32 type = 4; // 1-propose, 2-vote, 3-timeout, 4-propose-async, 5-vote-async, 6-timeout-internal, 7-consensus-external-request, 8-consensus-external-response, 9-fallback-complete
  string note = 5;
  int32 v = 6 ; // view number
  int32 r = 7;// round number
  message Block {
    string id = 1;
    int32 v = 2 ; // view number
    int32 r = 3;// round number
    Block parent = 4;
    repeated int32 commands = 5;
    int32 level = 6; // for the fallback mode
  }
  Block blockHigh = 8;
  Block blockNew = 9;
  Block blockCommit = 10;
}

The following is how I Marshal and Un-Marshal

func (t *AsyncConsensus) Marshal(wire io.Writer) error {
    data, err := proto.Marshal(t)
    if err != nil {
        return err
    }
    lengthWritten := len(data)
    var b [8]byte
    bs := b[:8]
    binary.LittleEndian.PutUint64(bs, uint64(lengthWritten))
    _, err = wire.Write(bs)
    if err != nil {
        return err
    }
    _, err = wire.Write(data)
    if err != nil {
        return err
    }
    return nil
}

func (t *AsyncConsensus) Unmarshal(wire io.Reader) error {

    var b [8]byte
    bs := b[:8]
    _, err := io.ReadFull(wire, bs)
    if err != nil {
        return err
    }
    numBytes := binary.LittleEndian.Uint64(bs)
    data := make([]byte, numBytes)
    length, err := io.ReadFull(wire, data)
    if err != nil {
        return err
    }
    err = proto.Unmarshal(data[:length], t)
    if err != nil {
        return err
    }
    return nil
}

func (t *AsyncConsensus) New() Serializable {
    return new(AsyncConsensus)
}

My expected outcome

When marshaled and sent to the same process via TCP, it should correctly unmarshal and produce correct data structures.

Resulting error

error "cannot parse invalid wire-format data"

Additional information

I tried with non-recursive .proto definitions, and never had this issue before.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

你的心境我的脸 2025-02-02 09:18:02

我能想到的最愚蠢的错误是WIRE.WRITE(bs)不要写入那么多的字节我只是确保在两种情况下它们的返回值实际上都是8个。

然后,我不太了解Golang/Protobuf,但我想它应该可以做到这一点。您不应该创建Go Code然后呼唤它吗?我不确定如何称呼它。

如果您认为这实际上是protobuf实现中的问题,则有一些在线protobuf-decoders可以帮助您。但是他们有时会错误地解释流,这可能是递归模式的情况,因此您必须小心。但是至少他们帮助我多次调试dedis/protobuf软件包。

作为最后的手段,您可以用递归数据进行最小的示例,检查它是否有效,然后慢慢添加字段直到破裂...

The stupidest error I can think about is that the wire.Write(bs) don’t write as many bytes as the io.ReadFull(wire, bs) read - so I’d just make sure that their return value is actually 8 in both cases.

Then I don’t know the golang/protobuf very well, but I guess it should be able to do this. Shouldn’t you create the go-code and then call out to it? I’m not sure how to call it.

If you think that it’s actually a problem in the protobuf implementation, there are some online protobuf-decoders, which can help. But they sometimes interpret the stream incorrectly, which could be the case here with a recursive pattern, so you have to be careful. But at least they helped me to debug the dedis/protobuf package more than once.

As a last resort you can make a minimal example with recursive data, check if it works, and then slowly add fields until it breaks…

妥活 2025-02-02 09:18:02

这不是Protobuf的错误,而是您元帅unarshal protobuf structs的概括。

作为具体的指南,切勿同时元帅unarshal Protobuf结构是我对比赛条件的帮助。

在您提供的具体示例中,我看到了递归数据结构,因此,即使您使用元帅unmarshal的每个调用使用一个单独的结构父母可以导致共享指针。

使用深层副本技术来删除任何依赖性,以免在比赛条件下进行。

func CloneMyStruct(orig *proto.AsyncConsensus_Block) (*proto.AsyncConsensus_Block, error) {
    origJSON, err := json.Marshal(orig)
    if err != nil {
        return nil, err
    }

    clone := proto.AsyncConsensus_Block{}
    if err = json.Unmarshal(origJSON, &clone); err != nil {
        return nil, err
    }

    return &clone, nil
}

This is not a bug with Protobuf, but its a mater of how you marshal and unmarshal protobuf structs.

As a concrete guideline, never concurrently marshal and unmarshal protobuf structs as it my lead to race conditions.

In the specific example you have provided, I see recursive data structs, so even if you use a separate struct for each invocation of marshal and unmarshal, it's likely that the pointers in the parent can lead to shared pointers.

Use a deep copy technique to remove any dependency so that you do not run in to race conditions.

func CloneMyStruct(orig *proto.AsyncConsensus_Block) (*proto.AsyncConsensus_Block, error) {
    origJSON, err := json.Marshal(orig)
    if err != nil {
        return nil, err
    }

    clone := proto.AsyncConsensus_Block{}
    if err = json.Unmarshal(origJSON, &clone); err != nil {
        return nil, err
    }

    return &clone, nil
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文