From 2fd41891ac1f9b17d86c9831a4cb406c7ef19e1b Mon Sep 17 00:00:00 2001
From: Elias Naur <elias@orijtech.com>
Date: Wed, 22 Nov 2023 14:41:36 -0600
Subject: [PATCH 1/9] wip

---
 cmd/protoc-gen-go-pulsar/main.go              |    1 +
 features/protoc/main.go                       |   18 +-
 features/zeropb/zeropb.go                     |  233 ++++
 internal/testprotos/test3/test.pulsar.go      | 1072 ++++++++++++++++-
 .../testprotos/test3/test_import.pulsar.go    |   19 +-
 .../testprotos/test3/test_nesting.pulsar.go   |   31 +-
 scripts/fastreflect.sh                        |    6 +-
 testpb/1.pulsar.go                            |  254 +++-
 testpb/2.pulsar.go                            |   19 +-
 testpb/codec_test.go                          |  146 ++-
 10 files changed, 1738 insertions(+), 61 deletions(-)
 create mode 100644 features/zeropb/zeropb.go

diff --git a/cmd/protoc-gen-go-pulsar/main.go b/cmd/protoc-gen-go-pulsar/main.go
index 8fc27ae..891e481 100644
--- a/cmd/protoc-gen-go-pulsar/main.go
+++ b/cmd/protoc-gen-go-pulsar/main.go
@@ -8,6 +8,7 @@ import (
 
 	_ "github.com/cosmos/cosmos-proto/features/fastreflection"
 	_ "github.com/cosmos/cosmos-proto/features/protoc"
+	_ "github.com/cosmos/cosmos-proto/features/zeropb"
 	"github.com/cosmos/cosmos-proto/generator"
 	"google.golang.org/protobuf/reflect/protoreflect"
 
diff --git a/features/protoc/main.go b/features/protoc/main.go
index 3cc5a1a..e7d8949 100644
--- a/features/protoc/main.go
+++ b/features/protoc/main.go
@@ -1448,7 +1448,7 @@ func genMessageField(g *generator.GeneratedFile, f *fileInfo, m *messageInfo, fi
 		sf.append(oneof.GoName)
 		return
 	}
-	goType, pointer := fieldGoType(g, f, field)
+	goType, pointer := FieldGoType(g, field)
 	if pointer {
 		goType = "*" + goType
 	}
@@ -1490,7 +1490,7 @@ func genMessageDefaultDecls(g *generator.GeneratedFile, f *fileInfo, m *messageI
 			continue
 		}
 		name := "Default_" + m.GoIdent.GoName + "_" + field.GoName
-		goType, _ := fieldGoType(g, f, field)
+		goType, _ := FieldGoType(g, field)
 		defVal := field.Desc.Default()
 		switch field.Desc.Kind() {
 		case protoreflect.StringKind:
@@ -1610,7 +1610,7 @@ func genMessageGetterMethods(g *generator.GeneratedFile, f *fileInfo, m *message
 		}
 
 		// Getter for message field.
-		goType, pointer := fieldGoType(g, f, field)
+		goType, pointer := FieldGoType(g, field)
 		defaultValue := fieldDefaultValue(g, f, m, field)
 		g.Annotate(m.GoIdent.GoName+".Get"+field.GoName, field.Location)
 		leadingComments := appendDeprecationSuffix("",
@@ -1679,10 +1679,10 @@ func genMessageSetterMethods(g *generator.GeneratedFile, f *fileInfo, m *message
 	}
 }
 
-// fieldGoType returns the Go type used for a field.
+// FieldGoType returns the Go type used for a field.
 //
 // If it returns pointer=true, the struct field is a pointer to the type.
-func fieldGoType(g *generator.GeneratedFile, f *fileInfo, field *protogen.Field) (goType string, pointer bool) {
+func FieldGoType(g *generator.GeneratedFile, field *protogen.Field) (goType string, pointer bool) {
 	if field.Desc.IsWeak() {
 		return "struct{}", false
 	}
@@ -1718,8 +1718,8 @@ func fieldGoType(g *generator.GeneratedFile, f *fileInfo, field *protogen.Field)
 	case field.Desc.IsList():
 		return "[]" + goType, false
 	case field.Desc.IsMap():
-		keyType, _ := fieldGoType(g, f, field.Message.Fields[0])
-		valType, _ := fieldGoType(g, f, field.Message.Fields[1])
+		keyType, _ := FieldGoType(g, field.Message.Fields[0])
+		valType, _ := FieldGoType(g, field.Message.Fields[1])
 		return fmt.Sprintf("map[%v]%v", keyType, valType), false
 	}
 	return goType, pointer
@@ -1779,7 +1779,7 @@ func genExtensions(g *generator.GeneratedFile, f *fileInfo) {
 	for _, x := range f.allExtensions {
 		g.P("{")
 		g.P("ExtendedType: (*", x.Extendee.GoIdent, ")(nil),")
-		goType, pointer := fieldGoType(g, f, x.Extension)
+		goType, pointer := FieldGoType(g, x.Extension)
 		if pointer {
 			goType = "*" + goType
 		}
@@ -1852,7 +1852,7 @@ func genMessageOneofWrapperTypes(g *generator.GeneratedFile, f *fileInfo, m *mes
 			g.Annotate(field.GoIdent.GoName, field.Location)
 			g.Annotate(field.GoIdent.GoName+"."+field.GoName, field.Location)
 			g.P("type ", field.GoIdent, " struct {")
-			goType, _ := fieldGoType(g, f, field)
+			goType, _ := FieldGoType(g, field)
 			tags := structTags{
 				{"protobuf", fieldProtobufTagValue(field)},
 			}
diff --git a/features/zeropb/zeropb.go b/features/zeropb/zeropb.go
new file mode 100644
index 0000000..af62f6f
--- /dev/null
+++ b/features/zeropb/zeropb.go
@@ -0,0 +1,233 @@
+package zeropb
+
+import (
+	"github.com/cosmos/cosmos-proto/features/protoc"
+	"github.com/cosmos/cosmos-proto/generator"
+	"google.golang.org/protobuf/compiler/protogen"
+	"google.golang.org/protobuf/reflect/protoreflect"
+)
+
+const (
+	errorsPackage = protogen.GoImportPath("errors")
+	mathPackage   = protogen.GoImportPath("math")
+	binaryPackage = protogen.GoImportPath("encoding/binary")
+)
+
+func init() {
+	generator.RegisterFeature("zeropb", func(gen *generator.GeneratedFile, _ *protogen.Plugin) generator.FeatureGenerator {
+		return zeropbFeature{
+			gen: gen,
+		}
+	})
+}
+
+type zeropbFeature struct {
+	gen *generator.GeneratedFile
+}
+
+func (g zeropbFeature) GenerateFile(file *protogen.File, _ *protogen.Plugin) bool {
+	for _, m := range file.Messages {
+		g.generateMessage(file, m)
+	}
+	return true // only do this once
+}
+
+func (g zeropbFeature) GenerateHelpers() {}
+
+func (g zeropbFeature) generateMessage(f *protogen.File, m *protogen.Message) {
+	g.generateMarshal(f, m)
+	g.generateUnmarshal(f, m)
+}
+
+func (g zeropbFeature) generateMarshal(f *protogen.File, m *protogen.Message) {
+	g.gen.P("func (x *", m.GoIdent, ") MarshalZeroPB(buf []byte) (n int, err error) {")
+	g.gen.P("defer func() {")
+	g.gen.P("    if e := recover(); e != nil {")
+	g.gen.P("        err = ", errorsPackage.Ident("New"), `("buffer overflow")`)
+	g.gen.P("    }")
+	g.gen.P("}()")
+	for _, f := range m.Fields {
+		g.generateMarshalField(f)
+	}
+	g.gen.P("return n, nil")
+	g.gen.P("}")
+}
+
+func (g zeropbFeature) generateMarshalField(f *protogen.Field) {
+	d := f.Desc
+	switch {
+	case d.IsList():
+		g.gen.P("len_", d.Index(), " := uint16(len(x.", f.GoName, "))")
+		g.gen.P("if len(x.", f.GoName, ") != int(len_", d.Index(), ") {")
+		g.gen.P("    return n, ", errorsPackage.Ident("New"), `("field `, f.GoName, ` is too long")`)
+		g.gen.P("}")
+		g.gen.P(binaryPackage.Ident("LittleEndian"), ".PutUint16(buf[n:], len_", d.Index(), ")")
+		g.gen.P("n += 2")
+		g.gen.P("for _, e := range x.", f.GoName, " {")
+		g.generateMarshalPrimitive(d, "e")
+		g.gen.P("}")
+	case d.IsMap():
+		g.gen.P("len_", d.Index(), " := uint16(len(x.", f.GoName, "))")
+		g.gen.P("if len(x.", f.GoName, ") != int(len_", d.Index(), ") {")
+		g.gen.P("    return n, ", errorsPackage.Ident("New"), `("field `, f.GoName, ` is too long")`)
+		g.gen.P("}")
+		g.gen.P("binary.LittleEndian.PutUint16(buf[n:], len_", d.Index(), ")")
+		g.gen.P("n += 2")
+		g.gen.P("for k, v := range x.", f.GoName, " {")
+		g.generateMarshalPrimitive(d.MapKey(), "k")
+		g.generateMarshalPrimitive(d.MapValue(), "v")
+		g.gen.P("}")
+	case d.ContainingOneof() != nil:
+		g.gen.P("// TODO: field ", f.GoName)
+		return
+	default:
+		g.generateMarshalPrimitive(d, "x."+f.GoName)
+	}
+}
+
+func (g zeropbFeature) generateMarshalPrimitive(d protoreflect.FieldDescriptor, name string) {
+	switch d.Kind() {
+	case protoreflect.FloatKind:
+		g.gen.P("binary.LittleEndian.PutUint32(buf[n:], ", mathPackage.Ident("Float32bits"), "(", name, "))")
+		g.gen.P("n += 4")
+	case protoreflect.DoubleKind:
+		g.gen.P("binary.LittleEndian.PutUint64(buf[n:], ", mathPackage.Ident("Float64bits"), "(", name, "))")
+		g.gen.P("n += 8")
+	case protoreflect.Sfixed32Kind, protoreflect.Fixed32Kind, protoreflect.Int32Kind, protoreflect.Sint32Kind, protoreflect.Uint32Kind, protoreflect.EnumKind:
+		g.gen.P("binary.LittleEndian.PutUint32(buf[n:], uint32(", name, "))")
+		g.gen.P("n += 4")
+	case protoreflect.Sfixed64Kind, protoreflect.Fixed64Kind, protoreflect.Int64Kind, protoreflect.Sint64Kind, protoreflect.Uint64Kind:
+		g.gen.P("binary.LittleEndian.PutUint64(buf[n:], uint64(", name, "))")
+		g.gen.P("n += 8")
+	case protoreflect.BoolKind:
+		g.gen.P("bool_", d.Index(), " := uint32(0)")
+		g.gen.P("if ", name, " {")
+		g.gen.P("    bool_", d.Index(), " = 1")
+		g.gen.P("}")
+		g.gen.P("binary.LittleEndian.PutUint32(buf[n:], bool_", d.Index(), ")")
+		g.gen.P("n += 4")
+	case protoreflect.StringKind, protoreflect.BytesKind:
+		g.gen.P("len_", d.Index(), " := uint16(len(", name, "))")
+		g.gen.P("if len(", name, ") != int(len_", d.Index(), ") {")
+		g.gen.P("    return n, ", errorsPackage.Ident("New"), `("field `, name, ` is too long")`)
+		g.gen.P("}")
+		g.gen.P("binary.LittleEndian.PutUint16(buf[n:], len_", d.Index(), ")")
+		g.gen.P("n += 2")
+		// Reslice buf to convert a truncated write into a buffer overflow error.
+		g.gen.P("copy(buf[n:n+len(", name, ")], ", name, ")")
+		g.gen.P("n += len(", name, ")")
+	case protoreflect.MessageKind:
+		g.gen.P("n_", d.Index(), ", err := ", name, ".MarshalZeroPB(buf[n:])")
+		g.gen.P("n += n_", d.Index())
+		g.gen.P("if err != nil {")
+		g.gen.P("    return n, err")
+		g.gen.P("}")
+	default:
+		g.gen.P("// TODO: field ", name)
+		g.gen.P("_ = ", name)
+	}
+}
+
+func (g zeropbFeature) generateUnmarshal(f *protogen.File, m *protogen.Message) {
+	g.gen.P("func (x *", m.GoIdent, ") UnmarshalZeroPB(buf []byte) (n int, err error) {")
+	g.gen.P("defer func() {")
+	g.gen.P("    if e := recover(); e != nil {")
+	g.gen.P("        err = ", errorsPackage.Ident("New"), `("buffer underflow")`)
+	g.gen.P("    }")
+	g.gen.P("}()")
+	for _, f := range m.Fields {
+		g.generateUnmarshalField(f)
+	}
+	g.gen.P("return n, nil")
+	g.gen.P("}")
+}
+
+func (g zeropbFeature) generateUnmarshalField(f *protogen.Field) {
+	d := f.Desc
+	switch {
+	case d.IsList():
+		g.gen.P("len_", d.Index(), " := int(binary.LittleEndian.Uint16(buf[n:]))")
+		g.gen.P("n += 2")
+		typ, pointer := protoc.FieldGoType(g.gen, f)
+		if pointer {
+			typ = "*" + typ
+		}
+		g.gen.P("x.", f.GoName, " = make(", typ, ", len_", d.Index(), ")")
+		g.gen.P("for i := range x.", f.GoName, "{")
+		g.generateUnmarshalPrimitive(f, "x."+f.GoName+"[i]")
+		g.gen.P("}")
+	case d.IsMap():
+		g.gen.P("len_", d.Index(), " := int(", binaryPackage.Ident("LittleEndian"), ".Uint16(buf[n:]))")
+		g.gen.P("n += 2")
+		typ, _ := protoc.FieldGoType(g.gen, f)
+		g.gen.P("x.", f.GoName, " = make(", typ, ", len_", d.Index(), ")")
+		keyType, _ := protoc.FieldGoType(g.gen, f.Message.Fields[0])
+		valType, _ := protoc.FieldGoType(g.gen, f.Message.Fields[1])
+		g.gen.P("for i := 0; i < len_", d.Index(), "; i++ {")
+		g.gen.P("var k ", keyType)
+		g.gen.P("var v ", valType)
+		g.generateUnmarshalPrimitive(f.Message.Fields[0], "k")
+		g.generateUnmarshalPrimitive(f.Message.Fields[1], "v")
+		g.gen.P("    x.", f.GoName, "[k] = v")
+		g.gen.P("}")
+	case d.ContainingOneof() != nil:
+		g.gen.P("// TODO: field ", f.GoName)
+	default:
+		g.generateUnmarshalPrimitive(f, "x."+f.GoName)
+	}
+}
+
+func (g zeropbFeature) generateUnmarshalPrimitive(f *protogen.Field, name string) {
+	switch d := f.Desc; d.Kind() {
+	case protoreflect.FloatKind:
+		g.gen.P(name, " = float32(", mathPackage.Ident("Float32frombits"), "(", binaryPackage.Ident("LittleEndian"), ".Uint32(buf[n:])))")
+		g.gen.P("n += 4")
+	case protoreflect.DoubleKind:
+		g.gen.P(name, " = float64(", mathPackage.Ident("Float64frombits"), "(", binaryPackage.Ident("LittleEndian"), ".Uint64(buf[n:])))")
+		g.gen.P("n += 8")
+	case protoreflect.Sfixed32Kind, protoreflect.Int32Kind, protoreflect.Sint32Kind:
+		g.gen.P(name, " = int32(", binaryPackage.Ident("LittleEndian"), ".Uint32(buf[n:]))")
+		g.gen.P("n += 4")
+	case protoreflect.Fixed32Kind, protoreflect.Uint32Kind:
+		g.gen.P(name, " = ", binaryPackage.Ident("LittleEndian"), ".Uint32(buf[n:])")
+		g.gen.P("n += 4")
+	case protoreflect.Sfixed64Kind, protoreflect.Int64Kind, protoreflect.Sint64Kind:
+		g.gen.P(name, " = int64(", binaryPackage.Ident("LittleEndian"), ".Uint64(buf[n:]))")
+		g.gen.P("n += 8")
+	case protoreflect.Fixed64Kind, protoreflect.Uint64Kind:
+		g.gen.P(name, " = ", binaryPackage.Ident("LittleEndian"), ".Uint64(buf[n:])")
+		g.gen.P("n += 8")
+	case protoreflect.EnumKind:
+		typ := g.gen.QualifiedGoIdent(f.Enum.GoIdent)
+		g.gen.P(name, " = ", typ, "(", binaryPackage.Ident("LittleEndian"), ".Uint32(buf[n:]))")
+		g.gen.P("n += 4")
+	case protoreflect.BoolKind:
+		g.gen.P("bool_", d.Index(), " := ", binaryPackage.Ident("LittleEndian"), ".Uint32(buf[n:])")
+		g.gen.P(name, " = false")
+		g.gen.P("if bool_", d.Index(), " != 0 {")
+		g.gen.P("    ", name, " = true")
+		g.gen.P("}")
+		g.gen.P("n += 4")
+	case protoreflect.StringKind:
+		g.gen.P("len_", d.Index(), " := int(", binaryPackage.Ident("LittleEndian"), ".Uint16(buf[n:]))")
+		g.gen.P("n += 2")
+		g.gen.P(name, " = string(buf[n:n+len_", d.Index(), "])")
+		g.gen.P("n += len_", d.Index())
+	case protoreflect.BytesKind:
+		g.gen.P("len_", d.Index(), " := int(", binaryPackage.Ident("LittleEndian"), ".Uint16(buf[n:]))")
+		g.gen.P("n += 2")
+		g.gen.P(name, " = append([]byte{}, buf[n:n+len_", d.Index(), "]...)")
+		g.gen.P("n += len_", d.Index())
+	case protoreflect.MessageKind:
+		typ := g.gen.QualifiedGoIdent(f.Message.GoIdent)
+		g.gen.P(name, " = new(", typ, ")")
+		g.gen.P("n_", d.Index(), ", err := ", name, ".UnmarshalZeroPB(buf[n:])")
+		g.gen.P("n += n_", d.Index())
+		g.gen.P("if err != nil {")
+		g.gen.P("    return n, err")
+		g.gen.P("}")
+	default:
+		g.gen.P("// TODO: field ", name)
+		g.gen.P("_ = ", name)
+	}
+}
diff --git a/internal/testprotos/test3/test.pulsar.go b/internal/testprotos/test3/test.pulsar.go
index 1721a4a..49d00e0 100644
--- a/internal/testprotos/test3/test.pulsar.go
+++ b/internal/testprotos/test3/test.pulsar.go
@@ -3,6 +3,7 @@ package test3
 
 import (
 	binary "encoding/binary"
+	errors "errors"
 	fmt "fmt"
 	runtime "github.com/cosmos/cosmos-proto/runtime"
 	protoreflect "google.golang.org/protobuf/reflect/protoreflect"
@@ -11222,7 +11223,7 @@ func (x *fastReflection_ForeignMessage) ProtoMethods() *protoiface.Methods {
 // Code generated by protoc-gen-go. DO NOT EDIT.
 // versions:
 // 	protoc-gen-go v1.27.0
-// 	protoc        v3.18.1
+// 	protoc        v4.24.4
 // source: internal/testprotos/test3/test.proto
 
 const (
@@ -11402,6 +11403,7 @@ type TestAllTypes struct {
 	MapStringNestedMessage map[string]*TestAllTypes_NestedMessage `protobuf:"bytes,71,rep,name=map_string_nested_message,json=mapStringNestedMessage,proto3" json:"map_string_nested_message,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
 	MapStringNestedEnum    map[string]TestAllTypes_NestedEnum     `protobuf:"bytes,73,rep,name=map_string_nested_enum,json=mapStringNestedEnum,proto3" json:"map_string_nested_enum,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"varint,2,opt,name=value,proto3,enum=goproto.proto.test3.TestAllTypes_NestedEnum"`
 	// Types that are assignable to OneofField:
+	//
 	//	*TestAllTypes_OneofUint32
 	//	*TestAllTypes_OneofNestedMessage
 	//	*TestAllTypes_OneofString
@@ -12616,3 +12618,1071 @@ func file_internal_testprotos_test3_test_proto_init() {
 	file_internal_testprotos_test3_test_proto_goTypes = nil
 	file_internal_testprotos_test3_test_proto_depIdxs = nil
 }
+func (x *TestAllTypes) MarshalZeroPB(buf []byte) (n int, err error) {
+	defer func() {
+		if e := recover(); e != nil {
+			err = errors.New("buffer overflow")
+		}
+	}()
+	binary.LittleEndian.PutUint32(buf[n:], uint32(x.SingularInt32))
+	n += 4
+	binary.LittleEndian.PutUint64(buf[n:], uint64(x.SingularInt64))
+	n += 8
+	binary.LittleEndian.PutUint32(buf[n:], uint32(x.SingularUint32))
+	n += 4
+	binary.LittleEndian.PutUint64(buf[n:], uint64(x.SingularUint64))
+	n += 8
+	binary.LittleEndian.PutUint32(buf[n:], uint32(x.SingularSint32))
+	n += 4
+	binary.LittleEndian.PutUint64(buf[n:], uint64(x.SingularSint64))
+	n += 8
+	binary.LittleEndian.PutUint32(buf[n:], uint32(x.SingularFixed32))
+	n += 4
+	binary.LittleEndian.PutUint64(buf[n:], uint64(x.SingularFixed64))
+	n += 8
+	binary.LittleEndian.PutUint32(buf[n:], uint32(x.SingularSfixed32))
+	n += 4
+	binary.LittleEndian.PutUint64(buf[n:], uint64(x.SingularSfixed64))
+	n += 8
+	binary.LittleEndian.PutUint32(buf[n:], math.Float32bits(x.SingularFloat))
+	n += 4
+	binary.LittleEndian.PutUint64(buf[n:], math.Float64bits(x.SingularDouble))
+	n += 8
+	bool_12 := uint32(0)
+	if x.SingularBool {
+		bool_12 = 1
+	}
+	binary.LittleEndian.PutUint32(buf[n:], bool_12)
+	n += 4
+	len_13 := uint16(len(x.SingularString))
+	if len(x.SingularString) != int(len_13) {
+		return n, errors.New("field x.SingularString is too long")
+	}
+	binary.LittleEndian.PutUint16(buf[n:], len_13)
+	n += 2
+	copy(buf[n:n+len(x.SingularString)], x.SingularString)
+	n += len(x.SingularString)
+	len_14 := uint16(len(x.SingularBytes))
+	if len(x.SingularBytes) != int(len_14) {
+		return n, errors.New("field x.SingularBytes is too long")
+	}
+	binary.LittleEndian.PutUint16(buf[n:], len_14)
+	n += 2
+	copy(buf[n:n+len(x.SingularBytes)], x.SingularBytes)
+	n += len(x.SingularBytes)
+	n_15, err := x.SingularNestedMessage.MarshalZeroPB(buf[n:])
+	n += n_15
+	if err != nil {
+		return n, err
+	}
+	n_16, err := x.SingularForeignMessage.MarshalZeroPB(buf[n:])
+	n += n_16
+	if err != nil {
+		return n, err
+	}
+	n_17, err := x.SingularImportMessage.MarshalZeroPB(buf[n:])
+	n += n_17
+	if err != nil {
+		return n, err
+	}
+	binary.LittleEndian.PutUint32(buf[n:], uint32(x.SingularNestedEnum))
+	n += 4
+	binary.LittleEndian.PutUint32(buf[n:], uint32(x.SingularForeignEnum))
+	n += 4
+	binary.LittleEndian.PutUint32(buf[n:], uint32(x.SingularImportEnum))
+	n += 4
+	len_21 := uint16(len(x.RepeatedInt32))
+	if len(x.RepeatedInt32) != int(len_21) {
+		return n, errors.New("field RepeatedInt32 is too long")
+	}
+	binary.LittleEndian.PutUint16(buf[n:], len_21)
+	n += 2
+	for _, e := range x.RepeatedInt32 {
+		binary.LittleEndian.PutUint32(buf[n:], uint32(e))
+		n += 4
+	}
+	len_22 := uint16(len(x.RepeatedInt64))
+	if len(x.RepeatedInt64) != int(len_22) {
+		return n, errors.New("field RepeatedInt64 is too long")
+	}
+	binary.LittleEndian.PutUint16(buf[n:], len_22)
+	n += 2
+	for _, e := range x.RepeatedInt64 {
+		binary.LittleEndian.PutUint64(buf[n:], uint64(e))
+		n += 8
+	}
+	len_23 := uint16(len(x.RepeatedUint32))
+	if len(x.RepeatedUint32) != int(len_23) {
+		return n, errors.New("field RepeatedUint32 is too long")
+	}
+	binary.LittleEndian.PutUint16(buf[n:], len_23)
+	n += 2
+	for _, e := range x.RepeatedUint32 {
+		binary.LittleEndian.PutUint32(buf[n:], uint32(e))
+		n += 4
+	}
+	len_24 := uint16(len(x.RepeatedUint64))
+	if len(x.RepeatedUint64) != int(len_24) {
+		return n, errors.New("field RepeatedUint64 is too long")
+	}
+	binary.LittleEndian.PutUint16(buf[n:], len_24)
+	n += 2
+	for _, e := range x.RepeatedUint64 {
+		binary.LittleEndian.PutUint64(buf[n:], uint64(e))
+		n += 8
+	}
+	len_25 := uint16(len(x.RepeatedSint32))
+	if len(x.RepeatedSint32) != int(len_25) {
+		return n, errors.New("field RepeatedSint32 is too long")
+	}
+	binary.LittleEndian.PutUint16(buf[n:], len_25)
+	n += 2
+	for _, e := range x.RepeatedSint32 {
+		binary.LittleEndian.PutUint32(buf[n:], uint32(e))
+		n += 4
+	}
+	len_26 := uint16(len(x.RepeatedSint64))
+	if len(x.RepeatedSint64) != int(len_26) {
+		return n, errors.New("field RepeatedSint64 is too long")
+	}
+	binary.LittleEndian.PutUint16(buf[n:], len_26)
+	n += 2
+	for _, e := range x.RepeatedSint64 {
+		binary.LittleEndian.PutUint64(buf[n:], uint64(e))
+		n += 8
+	}
+	len_27 := uint16(len(x.RepeatedFixed32))
+	if len(x.RepeatedFixed32) != int(len_27) {
+		return n, errors.New("field RepeatedFixed32 is too long")
+	}
+	binary.LittleEndian.PutUint16(buf[n:], len_27)
+	n += 2
+	for _, e := range x.RepeatedFixed32 {
+		binary.LittleEndian.PutUint32(buf[n:], uint32(e))
+		n += 4
+	}
+	len_28 := uint16(len(x.RepeatedFixed64))
+	if len(x.RepeatedFixed64) != int(len_28) {
+		return n, errors.New("field RepeatedFixed64 is too long")
+	}
+	binary.LittleEndian.PutUint16(buf[n:], len_28)
+	n += 2
+	for _, e := range x.RepeatedFixed64 {
+		binary.LittleEndian.PutUint64(buf[n:], uint64(e))
+		n += 8
+	}
+	len_29 := uint16(len(x.RepeatedSfixed32))
+	if len(x.RepeatedSfixed32) != int(len_29) {
+		return n, errors.New("field RepeatedSfixed32 is too long")
+	}
+	binary.LittleEndian.PutUint16(buf[n:], len_29)
+	n += 2
+	for _, e := range x.RepeatedSfixed32 {
+		binary.LittleEndian.PutUint32(buf[n:], uint32(e))
+		n += 4
+	}
+	len_30 := uint16(len(x.RepeatedSfixed64))
+	if len(x.RepeatedSfixed64) != int(len_30) {
+		return n, errors.New("field RepeatedSfixed64 is too long")
+	}
+	binary.LittleEndian.PutUint16(buf[n:], len_30)
+	n += 2
+	for _, e := range x.RepeatedSfixed64 {
+		binary.LittleEndian.PutUint64(buf[n:], uint64(e))
+		n += 8
+	}
+	len_31 := uint16(len(x.RepeatedFloat))
+	if len(x.RepeatedFloat) != int(len_31) {
+		return n, errors.New("field RepeatedFloat is too long")
+	}
+	binary.LittleEndian.PutUint16(buf[n:], len_31)
+	n += 2
+	for _, e := range x.RepeatedFloat {
+		binary.LittleEndian.PutUint32(buf[n:], math.Float32bits(e))
+		n += 4
+	}
+	len_32 := uint16(len(x.RepeatedDouble))
+	if len(x.RepeatedDouble) != int(len_32) {
+		return n, errors.New("field RepeatedDouble is too long")
+	}
+	binary.LittleEndian.PutUint16(buf[n:], len_32)
+	n += 2
+	for _, e := range x.RepeatedDouble {
+		binary.LittleEndian.PutUint64(buf[n:], math.Float64bits(e))
+		n += 8
+	}
+	len_33 := uint16(len(x.RepeatedBool))
+	if len(x.RepeatedBool) != int(len_33) {
+		return n, errors.New("field RepeatedBool is too long")
+	}
+	binary.LittleEndian.PutUint16(buf[n:], len_33)
+	n += 2
+	for _, e := range x.RepeatedBool {
+		bool_33 := uint32(0)
+		if e {
+			bool_33 = 1
+		}
+		binary.LittleEndian.PutUint32(buf[n:], bool_33)
+		n += 4
+	}
+	len_34 := uint16(len(x.RepeatedString))
+	if len(x.RepeatedString) != int(len_34) {
+		return n, errors.New("field RepeatedString is too long")
+	}
+	binary.LittleEndian.PutUint16(buf[n:], len_34)
+	n += 2
+	for _, e := range x.RepeatedString {
+		len_34 := uint16(len(e))
+		if len(e) != int(len_34) {
+			return n, errors.New("field e is too long")
+		}
+		binary.LittleEndian.PutUint16(buf[n:], len_34)
+		n += 2
+		copy(buf[n:n+len(e)], e)
+		n += len(e)
+	}
+	len_35 := uint16(len(x.RepeatedBytes))
+	if len(x.RepeatedBytes) != int(len_35) {
+		return n, errors.New("field RepeatedBytes is too long")
+	}
+	binary.LittleEndian.PutUint16(buf[n:], len_35)
+	n += 2
+	for _, e := range x.RepeatedBytes {
+		len_35 := uint16(len(e))
+		if len(e) != int(len_35) {
+			return n, errors.New("field e is too long")
+		}
+		binary.LittleEndian.PutUint16(buf[n:], len_35)
+		n += 2
+		copy(buf[n:n+len(e)], e)
+		n += len(e)
+	}
+	len_36 := uint16(len(x.RepeatedNestedMessage))
+	if len(x.RepeatedNestedMessage) != int(len_36) {
+		return n, errors.New("field RepeatedNestedMessage is too long")
+	}
+	binary.LittleEndian.PutUint16(buf[n:], len_36)
+	n += 2
+	for _, e := range x.RepeatedNestedMessage {
+		n_36, err := e.MarshalZeroPB(buf[n:])
+		n += n_36
+		if err != nil {
+			return n, err
+		}
+	}
+	len_37 := uint16(len(x.RepeatedForeignMessage))
+	if len(x.RepeatedForeignMessage) != int(len_37) {
+		return n, errors.New("field RepeatedForeignMessage is too long")
+	}
+	binary.LittleEndian.PutUint16(buf[n:], len_37)
+	n += 2
+	for _, e := range x.RepeatedForeignMessage {
+		n_37, err := e.MarshalZeroPB(buf[n:])
+		n += n_37
+		if err != nil {
+			return n, err
+		}
+	}
+	len_38 := uint16(len(x.RepeatedImportmessage))
+	if len(x.RepeatedImportmessage) != int(len_38) {
+		return n, errors.New("field RepeatedImportmessage is too long")
+	}
+	binary.LittleEndian.PutUint16(buf[n:], len_38)
+	n += 2
+	for _, e := range x.RepeatedImportmessage {
+		n_38, err := e.MarshalZeroPB(buf[n:])
+		n += n_38
+		if err != nil {
+			return n, err
+		}
+	}
+	len_39 := uint16(len(x.RepeatedNestedEnum))
+	if len(x.RepeatedNestedEnum) != int(len_39) {
+		return n, errors.New("field RepeatedNestedEnum is too long")
+	}
+	binary.LittleEndian.PutUint16(buf[n:], len_39)
+	n += 2
+	for _, e := range x.RepeatedNestedEnum {
+		binary.LittleEndian.PutUint32(buf[n:], uint32(e))
+		n += 4
+	}
+	len_40 := uint16(len(x.RepeatedForeignEnum))
+	if len(x.RepeatedForeignEnum) != int(len_40) {
+		return n, errors.New("field RepeatedForeignEnum is too long")
+	}
+	binary.LittleEndian.PutUint16(buf[n:], len_40)
+	n += 2
+	for _, e := range x.RepeatedForeignEnum {
+		binary.LittleEndian.PutUint32(buf[n:], uint32(e))
+		n += 4
+	}
+	len_41 := uint16(len(x.RepeatedImportenum))
+	if len(x.RepeatedImportenum) != int(len_41) {
+		return n, errors.New("field RepeatedImportenum is too long")
+	}
+	binary.LittleEndian.PutUint16(buf[n:], len_41)
+	n += 2
+	for _, e := range x.RepeatedImportenum {
+		binary.LittleEndian.PutUint32(buf[n:], uint32(e))
+		n += 4
+	}
+	len_42 := uint16(len(x.MapInt32Int32))
+	if len(x.MapInt32Int32) != int(len_42) {
+		return n, errors.New("field MapInt32Int32 is too long")
+	}
+	binary.LittleEndian.PutUint16(buf[n:], len_42)
+	n += 2
+	for k, v := range x.MapInt32Int32 {
+		binary.LittleEndian.PutUint32(buf[n:], uint32(k))
+		n += 4
+		binary.LittleEndian.PutUint32(buf[n:], uint32(v))
+		n += 4
+	}
+	len_43 := uint16(len(x.MapInt64Int64))
+	if len(x.MapInt64Int64) != int(len_43) {
+		return n, errors.New("field MapInt64Int64 is too long")
+	}
+	binary.LittleEndian.PutUint16(buf[n:], len_43)
+	n += 2
+	for k, v := range x.MapInt64Int64 {
+		binary.LittleEndian.PutUint64(buf[n:], uint64(k))
+		n += 8
+		binary.LittleEndian.PutUint64(buf[n:], uint64(v))
+		n += 8
+	}
+	len_44 := uint16(len(x.MapUint32Uint32))
+	if len(x.MapUint32Uint32) != int(len_44) {
+		return n, errors.New("field MapUint32Uint32 is too long")
+	}
+	binary.LittleEndian.PutUint16(buf[n:], len_44)
+	n += 2
+	for k, v := range x.MapUint32Uint32 {
+		binary.LittleEndian.PutUint32(buf[n:], uint32(k))
+		n += 4
+		binary.LittleEndian.PutUint32(buf[n:], uint32(v))
+		n += 4
+	}
+	len_45 := uint16(len(x.MapUint64Uint64))
+	if len(x.MapUint64Uint64) != int(len_45) {
+		return n, errors.New("field MapUint64Uint64 is too long")
+	}
+	binary.LittleEndian.PutUint16(buf[n:], len_45)
+	n += 2
+	for k, v := range x.MapUint64Uint64 {
+		binary.LittleEndian.PutUint64(buf[n:], uint64(k))
+		n += 8
+		binary.LittleEndian.PutUint64(buf[n:], uint64(v))
+		n += 8
+	}
+	len_46 := uint16(len(x.MapSint32Sint32))
+	if len(x.MapSint32Sint32) != int(len_46) {
+		return n, errors.New("field MapSint32Sint32 is too long")
+	}
+	binary.LittleEndian.PutUint16(buf[n:], len_46)
+	n += 2
+	for k, v := range x.MapSint32Sint32 {
+		binary.LittleEndian.PutUint32(buf[n:], uint32(k))
+		n += 4
+		binary.LittleEndian.PutUint32(buf[n:], uint32(v))
+		n += 4
+	}
+	len_47 := uint16(len(x.MapSint64Sint64))
+	if len(x.MapSint64Sint64) != int(len_47) {
+		return n, errors.New("field MapSint64Sint64 is too long")
+	}
+	binary.LittleEndian.PutUint16(buf[n:], len_47)
+	n += 2
+	for k, v := range x.MapSint64Sint64 {
+		binary.LittleEndian.PutUint64(buf[n:], uint64(k))
+		n += 8
+		binary.LittleEndian.PutUint64(buf[n:], uint64(v))
+		n += 8
+	}
+	len_48 := uint16(len(x.MapFixed32Fixed32))
+	if len(x.MapFixed32Fixed32) != int(len_48) {
+		return n, errors.New("field MapFixed32Fixed32 is too long")
+	}
+	binary.LittleEndian.PutUint16(buf[n:], len_48)
+	n += 2
+	for k, v := range x.MapFixed32Fixed32 {
+		binary.LittleEndian.PutUint32(buf[n:], uint32(k))
+		n += 4
+		binary.LittleEndian.PutUint32(buf[n:], uint32(v))
+		n += 4
+	}
+	len_49 := uint16(len(x.MapFixed64Fixed64))
+	if len(x.MapFixed64Fixed64) != int(len_49) {
+		return n, errors.New("field MapFixed64Fixed64 is too long")
+	}
+	binary.LittleEndian.PutUint16(buf[n:], len_49)
+	n += 2
+	for k, v := range x.MapFixed64Fixed64 {
+		binary.LittleEndian.PutUint64(buf[n:], uint64(k))
+		n += 8
+		binary.LittleEndian.PutUint64(buf[n:], uint64(v))
+		n += 8
+	}
+	len_50 := uint16(len(x.MapSfixed32Sfixed32))
+	if len(x.MapSfixed32Sfixed32) != int(len_50) {
+		return n, errors.New("field MapSfixed32Sfixed32 is too long")
+	}
+	binary.LittleEndian.PutUint16(buf[n:], len_50)
+	n += 2
+	for k, v := range x.MapSfixed32Sfixed32 {
+		binary.LittleEndian.PutUint32(buf[n:], uint32(k))
+		n += 4
+		binary.LittleEndian.PutUint32(buf[n:], uint32(v))
+		n += 4
+	}
+	len_51 := uint16(len(x.MapSfixed64Sfixed64))
+	if len(x.MapSfixed64Sfixed64) != int(len_51) {
+		return n, errors.New("field MapSfixed64Sfixed64 is too long")
+	}
+	binary.LittleEndian.PutUint16(buf[n:], len_51)
+	n += 2
+	for k, v := range x.MapSfixed64Sfixed64 {
+		binary.LittleEndian.PutUint64(buf[n:], uint64(k))
+		n += 8
+		binary.LittleEndian.PutUint64(buf[n:], uint64(v))
+		n += 8
+	}
+	len_52 := uint16(len(x.MapInt32Float))
+	if len(x.MapInt32Float) != int(len_52) {
+		return n, errors.New("field MapInt32Float is too long")
+	}
+	binary.LittleEndian.PutUint16(buf[n:], len_52)
+	n += 2
+	for k, v := range x.MapInt32Float {
+		binary.LittleEndian.PutUint32(buf[n:], uint32(k))
+		n += 4
+		binary.LittleEndian.PutUint32(buf[n:], math.Float32bits(v))
+		n += 4
+	}
+	len_53 := uint16(len(x.MapInt32Double))
+	if len(x.MapInt32Double) != int(len_53) {
+		return n, errors.New("field MapInt32Double is too long")
+	}
+	binary.LittleEndian.PutUint16(buf[n:], len_53)
+	n += 2
+	for k, v := range x.MapInt32Double {
+		binary.LittleEndian.PutUint32(buf[n:], uint32(k))
+		n += 4
+		binary.LittleEndian.PutUint64(buf[n:], math.Float64bits(v))
+		n += 8
+	}
+	len_54 := uint16(len(x.MapBoolBool))
+	if len(x.MapBoolBool) != int(len_54) {
+		return n, errors.New("field MapBoolBool is too long")
+	}
+	binary.LittleEndian.PutUint16(buf[n:], len_54)
+	n += 2
+	for k, v := range x.MapBoolBool {
+		bool_0 := uint32(0)
+		if k {
+			bool_0 = 1
+		}
+		binary.LittleEndian.PutUint32(buf[n:], bool_0)
+		n += 4
+		bool_1 := uint32(0)
+		if v {
+			bool_1 = 1
+		}
+		binary.LittleEndian.PutUint32(buf[n:], bool_1)
+		n += 4
+	}
+	len_55 := uint16(len(x.MapStringString))
+	if len(x.MapStringString) != int(len_55) {
+		return n, errors.New("field MapStringString is too long")
+	}
+	binary.LittleEndian.PutUint16(buf[n:], len_55)
+	n += 2
+	for k, v := range x.MapStringString {
+		len_0 := uint16(len(k))
+		if len(k) != int(len_0) {
+			return n, errors.New("field k is too long")
+		}
+		binary.LittleEndian.PutUint16(buf[n:], len_0)
+		n += 2
+		copy(buf[n:n+len(k)], k)
+		n += len(k)
+		len_1 := uint16(len(v))
+		if len(v) != int(len_1) {
+			return n, errors.New("field v is too long")
+		}
+		binary.LittleEndian.PutUint16(buf[n:], len_1)
+		n += 2
+		copy(buf[n:n+len(v)], v)
+		n += len(v)
+	}
+	len_56 := uint16(len(x.MapStringBytes))
+	if len(x.MapStringBytes) != int(len_56) {
+		return n, errors.New("field MapStringBytes is too long")
+	}
+	binary.LittleEndian.PutUint16(buf[n:], len_56)
+	n += 2
+	for k, v := range x.MapStringBytes {
+		len_0 := uint16(len(k))
+		if len(k) != int(len_0) {
+			return n, errors.New("field k is too long")
+		}
+		binary.LittleEndian.PutUint16(buf[n:], len_0)
+		n += 2
+		copy(buf[n:n+len(k)], k)
+		n += len(k)
+		len_1 := uint16(len(v))
+		if len(v) != int(len_1) {
+			return n, errors.New("field v is too long")
+		}
+		binary.LittleEndian.PutUint16(buf[n:], len_1)
+		n += 2
+		copy(buf[n:n+len(v)], v)
+		n += len(v)
+	}
+	len_57 := uint16(len(x.MapStringNestedMessage))
+	if len(x.MapStringNestedMessage) != int(len_57) {
+		return n, errors.New("field MapStringNestedMessage is too long")
+	}
+	binary.LittleEndian.PutUint16(buf[n:], len_57)
+	n += 2
+	for k, v := range x.MapStringNestedMessage {
+		len_0 := uint16(len(k))
+		if len(k) != int(len_0) {
+			return n, errors.New("field k is too long")
+		}
+		binary.LittleEndian.PutUint16(buf[n:], len_0)
+		n += 2
+		copy(buf[n:n+len(k)], k)
+		n += len(k)
+		n_1, err := v.MarshalZeroPB(buf[n:])
+		n += n_1
+		if err != nil {
+			return n, err
+		}
+	}
+	len_58 := uint16(len(x.MapStringNestedEnum))
+	if len(x.MapStringNestedEnum) != int(len_58) {
+		return n, errors.New("field MapStringNestedEnum is too long")
+	}
+	binary.LittleEndian.PutUint16(buf[n:], len_58)
+	n += 2
+	for k, v := range x.MapStringNestedEnum {
+		len_0 := uint16(len(k))
+		if len(k) != int(len_0) {
+			return n, errors.New("field k is too long")
+		}
+		binary.LittleEndian.PutUint16(buf[n:], len_0)
+		n += 2
+		copy(buf[n:n+len(k)], k)
+		n += len(k)
+		binary.LittleEndian.PutUint32(buf[n:], uint32(v))
+		n += 4
+	}
+	// TODO: field OneofUint32
+	// TODO: field OneofNestedMessage
+	// TODO: field OneofString
+	// TODO: field OneofBytes
+	// TODO: field OneofBool
+	// TODO: field OneofUint64
+	// TODO: field OneofFloat
+	// TODO: field OneofDouble
+	// TODO: field OneofEnum
+	return n, nil
+}
+func (x *TestAllTypes) UnmarshalZeroPB(buf []byte) (n int, err error) {
+	defer func() {
+		if e := recover(); e != nil {
+			err = errors.New("buffer underflow")
+		}
+	}()
+	x.SingularInt32 = int32(binary.LittleEndian.Uint32(buf[n:]))
+	n += 4
+	x.SingularInt64 = int64(binary.LittleEndian.Uint64(buf[n:]))
+	n += 8
+	x.SingularUint32 = binary.LittleEndian.Uint32(buf[n:])
+	n += 4
+	x.SingularUint64 = binary.LittleEndian.Uint64(buf[n:])
+	n += 8
+	x.SingularSint32 = int32(binary.LittleEndian.Uint32(buf[n:]))
+	n += 4
+	x.SingularSint64 = int64(binary.LittleEndian.Uint64(buf[n:]))
+	n += 8
+	x.SingularFixed32 = binary.LittleEndian.Uint32(buf[n:])
+	n += 4
+	x.SingularFixed64 = binary.LittleEndian.Uint64(buf[n:])
+	n += 8
+	x.SingularSfixed32 = int32(binary.LittleEndian.Uint32(buf[n:]))
+	n += 4
+	x.SingularSfixed64 = int64(binary.LittleEndian.Uint64(buf[n:]))
+	n += 8
+	x.SingularFloat = float32(math.Float32frombits(binary.LittleEndian.Uint32(buf[n:])))
+	n += 4
+	x.SingularDouble = float64(math.Float64frombits(binary.LittleEndian.Uint64(buf[n:])))
+	n += 8
+	bool_12 := binary.LittleEndian.Uint32(buf[n:])
+	x.SingularBool = false
+	if bool_12 != 0 {
+		x.SingularBool = true
+	}
+	n += 4
+	len_13 := int(binary.LittleEndian.Uint16(buf[n:]))
+	n += 2
+	x.SingularString = string(buf[n : n+len_13])
+	n += len_13
+	len_14 := int(binary.LittleEndian.Uint16(buf[n:]))
+	n += 2
+	x.SingularBytes = append([]byte{}, buf[n:n+len_14]...)
+	n += len_14
+	x.SingularNestedMessage = new(goproto.proto.test3.TestAllTypes.NestedMessage)
+	n_15, err := x.SingularNestedMessage.UnmarshalZeroPB(buf[n:])
+	n += n_15
+	if err != nil {
+		return n, err
+	}
+	x.SingularForeignMessage = new(goproto.proto.test3.ForeignMessage)
+	n_16, err := x.SingularForeignMessage.UnmarshalZeroPB(buf[n:])
+	n += n_16
+	if err != nil {
+		return n, err
+	}
+	x.SingularImportMessage = new(goproto.proto.test3.ImportMessage)
+	n_17, err := x.SingularImportMessage.UnmarshalZeroPB(buf[n:])
+	n += n_17
+	if err != nil {
+		return n, err
+	}
+	x.SingularNestedEnum = goproto.proto.test3.TestAllTypes.NestedEnum(binary.LittleEndian.Uint32(buf[n:]))
+	n += 4
+	x.SingularForeignEnum = goproto.proto.test3.ForeignEnum(binary.LittleEndian.Uint32(buf[n:]))
+	n += 4
+	x.SingularImportEnum = goproto.proto.test3.ImportEnum(binary.LittleEndian.Uint32(buf[n:]))
+	n += 4
+	len_21 := int(binary.LittleEndian.Uint16(buf[n:]))
+	n += 2
+	x.RepeatedInt32 = make([]int32, len_21)
+	for i := range x.RepeatedInt32 {
+		x.RepeatedInt32[i] = int32(binary.LittleEndian.Uint32(buf[n:]))
+		n += 4
+	}
+	len_22 := int(binary.LittleEndian.Uint16(buf[n:]))
+	n += 2
+	x.RepeatedInt64 = make([]int64, len_22)
+	for i := range x.RepeatedInt64 {
+		x.RepeatedInt64[i] = int64(binary.LittleEndian.Uint64(buf[n:]))
+		n += 8
+	}
+	len_23 := int(binary.LittleEndian.Uint16(buf[n:]))
+	n += 2
+	x.RepeatedUint32 = make([]uint32, len_23)
+	for i := range x.RepeatedUint32 {
+		x.RepeatedUint32[i] = binary.LittleEndian.Uint32(buf[n:])
+		n += 4
+	}
+	len_24 := int(binary.LittleEndian.Uint16(buf[n:]))
+	n += 2
+	x.RepeatedUint64 = make([]uint64, len_24)
+	for i := range x.RepeatedUint64 {
+		x.RepeatedUint64[i] = binary.LittleEndian.Uint64(buf[n:])
+		n += 8
+	}
+	len_25 := int(binary.LittleEndian.Uint16(buf[n:]))
+	n += 2
+	x.RepeatedSint32 = make([]int32, len_25)
+	for i := range x.RepeatedSint32 {
+		x.RepeatedSint32[i] = int32(binary.LittleEndian.Uint32(buf[n:]))
+		n += 4
+	}
+	len_26 := int(binary.LittleEndian.Uint16(buf[n:]))
+	n += 2
+	x.RepeatedSint64 = make([]int64, len_26)
+	for i := range x.RepeatedSint64 {
+		x.RepeatedSint64[i] = int64(binary.LittleEndian.Uint64(buf[n:]))
+		n += 8
+	}
+	len_27 := int(binary.LittleEndian.Uint16(buf[n:]))
+	n += 2
+	x.RepeatedFixed32 = make([]uint32, len_27)
+	for i := range x.RepeatedFixed32 {
+		x.RepeatedFixed32[i] = binary.LittleEndian.Uint32(buf[n:])
+		n += 4
+	}
+	len_28 := int(binary.LittleEndian.Uint16(buf[n:]))
+	n += 2
+	x.RepeatedFixed64 = make([]uint64, len_28)
+	for i := range x.RepeatedFixed64 {
+		x.RepeatedFixed64[i] = binary.LittleEndian.Uint64(buf[n:])
+		n += 8
+	}
+	len_29 := int(binary.LittleEndian.Uint16(buf[n:]))
+	n += 2
+	x.RepeatedSfixed32 = make([]int32, len_29)
+	for i := range x.RepeatedSfixed32 {
+		x.RepeatedSfixed32[i] = int32(binary.LittleEndian.Uint32(buf[n:]))
+		n += 4
+	}
+	len_30 := int(binary.LittleEndian.Uint16(buf[n:]))
+	n += 2
+	x.RepeatedSfixed64 = make([]int64, len_30)
+	for i := range x.RepeatedSfixed64 {
+		x.RepeatedSfixed64[i] = int64(binary.LittleEndian.Uint64(buf[n:]))
+		n += 8
+	}
+	len_31 := int(binary.LittleEndian.Uint16(buf[n:]))
+	n += 2
+	x.RepeatedFloat = make([]float32, len_31)
+	for i := range x.RepeatedFloat {
+		x.RepeatedFloat[i] = float32(math.Float32frombits(binary.LittleEndian.Uint32(buf[n:])))
+		n += 4
+	}
+	len_32 := int(binary.LittleEndian.Uint16(buf[n:]))
+	n += 2
+	x.RepeatedDouble = make([]float64, len_32)
+	for i := range x.RepeatedDouble {
+		x.RepeatedDouble[i] = float64(math.Float64frombits(binary.LittleEndian.Uint64(buf[n:])))
+		n += 8
+	}
+	len_33 := int(binary.LittleEndian.Uint16(buf[n:]))
+	n += 2
+	x.RepeatedBool = make([]bool, len_33)
+	for i := range x.RepeatedBool {
+		bool_33 := binary.LittleEndian.Uint32(buf[n:])
+		x.RepeatedBool[i] = false
+		if bool_33 != 0 {
+			x.RepeatedBool[i] = true
+		}
+		n += 4
+	}
+	len_34 := int(binary.LittleEndian.Uint16(buf[n:]))
+	n += 2
+	x.RepeatedString = make([]string, len_34)
+	for i := range x.RepeatedString {
+		len_34 := int(binary.LittleEndian.Uint16(buf[n:]))
+		n += 2
+		x.RepeatedString[i] = string(buf[n : n+len_34])
+		n += len_34
+	}
+	len_35 := int(binary.LittleEndian.Uint16(buf[n:]))
+	n += 2
+	x.RepeatedBytes = make([][]byte, len_35)
+	for i := range x.RepeatedBytes {
+		len_35 := int(binary.LittleEndian.Uint16(buf[n:]))
+		n += 2
+		x.RepeatedBytes[i] = append([]byte{}, buf[n:n+len_35]...)
+		n += len_35
+	}
+	len_36 := int(binary.LittleEndian.Uint16(buf[n:]))
+	n += 2
+	x.RepeatedNestedMessage = make([]*TestAllTypes_NestedMessage, len_36)
+	for i := range x.RepeatedNestedMessage {
+		x.RepeatedNestedMessage[i] = new(goproto.proto.test3.TestAllTypes.NestedMessage)
+		n_36, err := x.RepeatedNestedMessage[i].UnmarshalZeroPB(buf[n:])
+		n += n_36
+		if err != nil {
+			return n, err
+		}
+	}
+	len_37 := int(binary.LittleEndian.Uint16(buf[n:]))
+	n += 2
+	x.RepeatedForeignMessage = make([]*ForeignMessage, len_37)
+	for i := range x.RepeatedForeignMessage {
+		x.RepeatedForeignMessage[i] = new(goproto.proto.test3.ForeignMessage)
+		n_37, err := x.RepeatedForeignMessage[i].UnmarshalZeroPB(buf[n:])
+		n += n_37
+		if err != nil {
+			return n, err
+		}
+	}
+	len_38 := int(binary.LittleEndian.Uint16(buf[n:]))
+	n += 2
+	x.RepeatedImportmessage = make([]*ImportMessage, len_38)
+	for i := range x.RepeatedImportmessage {
+		x.RepeatedImportmessage[i] = new(goproto.proto.test3.ImportMessage)
+		n_38, err := x.RepeatedImportmessage[i].UnmarshalZeroPB(buf[n:])
+		n += n_38
+		if err != nil {
+			return n, err
+		}
+	}
+	len_39 := int(binary.LittleEndian.Uint16(buf[n:]))
+	n += 2
+	x.RepeatedNestedEnum = make([]TestAllTypes_NestedEnum, len_39)
+	for i := range x.RepeatedNestedEnum {
+		x.RepeatedNestedEnum[i] = goproto.proto.test3.TestAllTypes.NestedEnum(binary.LittleEndian.Uint32(buf[n:]))
+		n += 4
+	}
+	len_40 := int(binary.LittleEndian.Uint16(buf[n:]))
+	n += 2
+	x.RepeatedForeignEnum = make([]ForeignEnum, len_40)
+	for i := range x.RepeatedForeignEnum {
+		x.RepeatedForeignEnum[i] = goproto.proto.test3.ForeignEnum(binary.LittleEndian.Uint32(buf[n:]))
+		n += 4
+	}
+	len_41 := int(binary.LittleEndian.Uint16(buf[n:]))
+	n += 2
+	x.RepeatedImportenum = make([]ImportEnum, len_41)
+	for i := range x.RepeatedImportenum {
+		x.RepeatedImportenum[i] = goproto.proto.test3.ImportEnum(binary.LittleEndian.Uint32(buf[n:]))
+		n += 4
+	}
+	len_42 := int(binary.LittleEndian.Uint16(buf[n:]))
+	n += 2
+	x.MapInt32Int32 = make(map[int32]int32, len_42)
+	for i := 0; i < len_42; i++ {
+		var k int32
+		var v int32
+		k = int32(binary.LittleEndian.Uint32(buf[n:]))
+		n += 4
+		v = int32(binary.LittleEndian.Uint32(buf[n:]))
+		n += 4
+		x.MapInt32Int32[k] = v
+	}
+	len_43 := int(binary.LittleEndian.Uint16(buf[n:]))
+	n += 2
+	x.MapInt64Int64 = make(map[int64]int64, len_43)
+	for i := 0; i < len_43; i++ {
+		var k int64
+		var v int64
+		k = int64(binary.LittleEndian.Uint64(buf[n:]))
+		n += 8
+		v = int64(binary.LittleEndian.Uint64(buf[n:]))
+		n += 8
+		x.MapInt64Int64[k] = v
+	}
+	len_44 := int(binary.LittleEndian.Uint16(buf[n:]))
+	n += 2
+	x.MapUint32Uint32 = make(map[uint32]uint32, len_44)
+	for i := 0; i < len_44; i++ {
+		var k uint32
+		var v uint32
+		k = binary.LittleEndian.Uint32(buf[n:])
+		n += 4
+		v = binary.LittleEndian.Uint32(buf[n:])
+		n += 4
+		x.MapUint32Uint32[k] = v
+	}
+	len_45 := int(binary.LittleEndian.Uint16(buf[n:]))
+	n += 2
+	x.MapUint64Uint64 = make(map[uint64]uint64, len_45)
+	for i := 0; i < len_45; i++ {
+		var k uint64
+		var v uint64
+		k = binary.LittleEndian.Uint64(buf[n:])
+		n += 8
+		v = binary.LittleEndian.Uint64(buf[n:])
+		n += 8
+		x.MapUint64Uint64[k] = v
+	}
+	len_46 := int(binary.LittleEndian.Uint16(buf[n:]))
+	n += 2
+	x.MapSint32Sint32 = make(map[int32]int32, len_46)
+	for i := 0; i < len_46; i++ {
+		var k int32
+		var v int32
+		k = int32(binary.LittleEndian.Uint32(buf[n:]))
+		n += 4
+		v = int32(binary.LittleEndian.Uint32(buf[n:]))
+		n += 4
+		x.MapSint32Sint32[k] = v
+	}
+	len_47 := int(binary.LittleEndian.Uint16(buf[n:]))
+	n += 2
+	x.MapSint64Sint64 = make(map[int64]int64, len_47)
+	for i := 0; i < len_47; i++ {
+		var k int64
+		var v int64
+		k = int64(binary.LittleEndian.Uint64(buf[n:]))
+		n += 8
+		v = int64(binary.LittleEndian.Uint64(buf[n:]))
+		n += 8
+		x.MapSint64Sint64[k] = v
+	}
+	len_48 := int(binary.LittleEndian.Uint16(buf[n:]))
+	n += 2
+	x.MapFixed32Fixed32 = make(map[uint32]uint32, len_48)
+	for i := 0; i < len_48; i++ {
+		var k uint32
+		var v uint32
+		k = binary.LittleEndian.Uint32(buf[n:])
+		n += 4
+		v = binary.LittleEndian.Uint32(buf[n:])
+		n += 4
+		x.MapFixed32Fixed32[k] = v
+	}
+	len_49 := int(binary.LittleEndian.Uint16(buf[n:]))
+	n += 2
+	x.MapFixed64Fixed64 = make(map[uint64]uint64, len_49)
+	for i := 0; i < len_49; i++ {
+		var k uint64
+		var v uint64
+		k = binary.LittleEndian.Uint64(buf[n:])
+		n += 8
+		v = binary.LittleEndian.Uint64(buf[n:])
+		n += 8
+		x.MapFixed64Fixed64[k] = v
+	}
+	len_50 := int(binary.LittleEndian.Uint16(buf[n:]))
+	n += 2
+	x.MapSfixed32Sfixed32 = make(map[int32]int32, len_50)
+	for i := 0; i < len_50; i++ {
+		var k int32
+		var v int32
+		k = int32(binary.LittleEndian.Uint32(buf[n:]))
+		n += 4
+		v = int32(binary.LittleEndian.Uint32(buf[n:]))
+		n += 4
+		x.MapSfixed32Sfixed32[k] = v
+	}
+	len_51 := int(binary.LittleEndian.Uint16(buf[n:]))
+	n += 2
+	x.MapSfixed64Sfixed64 = make(map[int64]int64, len_51)
+	for i := 0; i < len_51; i++ {
+		var k int64
+		var v int64
+		k = int64(binary.LittleEndian.Uint64(buf[n:]))
+		n += 8
+		v = int64(binary.LittleEndian.Uint64(buf[n:]))
+		n += 8
+		x.MapSfixed64Sfixed64[k] = v
+	}
+	len_52 := int(binary.LittleEndian.Uint16(buf[n:]))
+	n += 2
+	x.MapInt32Float = make(map[int32]float32, len_52)
+	for i := 0; i < len_52; i++ {
+		var k int32
+		var v float32
+		k = int32(binary.LittleEndian.Uint32(buf[n:]))
+		n += 4
+		v = float32(math.Float32frombits(binary.LittleEndian.Uint32(buf[n:])))
+		n += 4
+		x.MapInt32Float[k] = v
+	}
+	len_53 := int(binary.LittleEndian.Uint16(buf[n:]))
+	n += 2
+	x.MapInt32Double = make(map[int32]float64, len_53)
+	for i := 0; i < len_53; i++ {
+		var k int32
+		var v float64
+		k = int32(binary.LittleEndian.Uint32(buf[n:]))
+		n += 4
+		v = float64(math.Float64frombits(binary.LittleEndian.Uint64(buf[n:])))
+		n += 8
+		x.MapInt32Double[k] = v
+	}
+	len_54 := int(binary.LittleEndian.Uint16(buf[n:]))
+	n += 2
+	x.MapBoolBool = make(map[bool]bool, len_54)
+	for i := 0; i < len_54; i++ {
+		var k bool
+		var v bool
+		bool_0 := binary.LittleEndian.Uint32(buf[n:])
+		k = false
+		if bool_0 != 0 {
+			k = true
+		}
+		n += 4
+		bool_1 := binary.LittleEndian.Uint32(buf[n:])
+		v = false
+		if bool_1 != 0 {
+			v = true
+		}
+		n += 4
+		x.MapBoolBool[k] = v
+	}
+	len_55 := int(binary.LittleEndian.Uint16(buf[n:]))
+	n += 2
+	x.MapStringString = make(map[string]string, len_55)
+	for i := 0; i < len_55; i++ {
+		var k string
+		var v string
+		len_0 := int(binary.LittleEndian.Uint16(buf[n:]))
+		n += 2
+		k = string(buf[n : n+len_0])
+		n += len_0
+		len_1 := int(binary.LittleEndian.Uint16(buf[n:]))
+		n += 2
+		v = string(buf[n : n+len_1])
+		n += len_1
+		x.MapStringString[k] = v
+	}
+	len_56 := int(binary.LittleEndian.Uint16(buf[n:]))
+	n += 2
+	x.MapStringBytes = make(map[string][]byte, len_56)
+	for i := 0; i < len_56; i++ {
+		var k string
+		var v []byte
+		len_0 := int(binary.LittleEndian.Uint16(buf[n:]))
+		n += 2
+		k = string(buf[n : n+len_0])
+		n += len_0
+		len_1 := int(binary.LittleEndian.Uint16(buf[n:]))
+		n += 2
+		v = append([]byte{}, buf[n:n+len_1]...)
+		n += len_1
+		x.MapStringBytes[k] = v
+	}
+	len_57 := int(binary.LittleEndian.Uint16(buf[n:]))
+	n += 2
+	x.MapStringNestedMessage = make(map[string]*TestAllTypes_NestedMessage, len_57)
+	for i := 0; i < len_57; i++ {
+		var k string
+		var v *TestAllTypes_NestedMessage
+		len_0 := int(binary.LittleEndian.Uint16(buf[n:]))
+		n += 2
+		k = string(buf[n : n+len_0])
+		n += len_0
+		v = new(goproto.proto.test3.TestAllTypes.NestedMessage)
+		n_1, err := v.UnmarshalZeroPB(buf[n:])
+		n += n_1
+		if err != nil {
+			return n, err
+		}
+		x.MapStringNestedMessage[k] = v
+	}
+	len_58 := int(binary.LittleEndian.Uint16(buf[n:]))
+	n += 2
+	x.MapStringNestedEnum = make(map[string]TestAllTypes_NestedEnum, len_58)
+	for i := 0; i < len_58; i++ {
+		var k string
+		var v TestAllTypes_NestedEnum
+		len_0 := int(binary.LittleEndian.Uint16(buf[n:]))
+		n += 2
+		k = string(buf[n : n+len_0])
+		n += len_0
+		v = goproto.proto.test3.TestAllTypes.NestedEnum(binary.LittleEndian.Uint32(buf[n:]))
+		n += 4
+		x.MapStringNestedEnum[k] = v
+	}
+	// TODO: field OneofUint32
+	// TODO: field OneofNestedMessage
+	// TODO: field OneofString
+	// TODO: field OneofBytes
+	// TODO: field OneofBool
+	// TODO: field OneofUint64
+	// TODO: field OneofFloat
+	// TODO: field OneofDouble
+	// TODO: field OneofEnum
+	return n, nil
+}
+func (x *ForeignMessage) MarshalZeroPB(buf []byte) (n int, err error) {
+	defer func() {
+		if e := recover(); e != nil {
+			err = errors.New("buffer overflow")
+		}
+	}()
+	binary.LittleEndian.PutUint32(buf[n:], uint32(x.C))
+	n += 4
+	binary.LittleEndian.PutUint32(buf[n:], uint32(x.D))
+	n += 4
+	return n, nil
+}
+func (x *ForeignMessage) UnmarshalZeroPB(buf []byte) (n int, err error) {
+	defer func() {
+		if e := recover(); e != nil {
+			err = errors.New("buffer underflow")
+		}
+	}()
+	x.C = int32(binary.LittleEndian.Uint32(buf[n:]))
+	n += 4
+	x.D = int32(binary.LittleEndian.Uint32(buf[n:]))
+	n += 4
+	return n, nil
+}
diff --git a/internal/testprotos/test3/test_import.pulsar.go b/internal/testprotos/test3/test_import.pulsar.go
index 06ba752..ea25b01 100644
--- a/internal/testprotos/test3/test_import.pulsar.go
+++ b/internal/testprotos/test3/test_import.pulsar.go
@@ -2,6 +2,7 @@
 package test3
 
 import (
+	errors "errors"
 	fmt "fmt"
 	runtime "github.com/cosmos/cosmos-proto/runtime"
 	protoreflect "google.golang.org/protobuf/reflect/protoreflect"
@@ -375,7 +376,7 @@ func (x *fastReflection_ImportMessage) ProtoMethods() *protoiface.Methods {
 // Code generated by protoc-gen-go. DO NOT EDIT.
 // versions:
 // 	protoc-gen-go v1.27.0
-// 	protoc        v3.18.1
+// 	protoc        v4.24.4
 // source: internal/testprotos/test3/test_import.proto
 
 const (
@@ -536,3 +537,19 @@ func file_internal_testprotos_test3_test_import_proto_init() {
 	file_internal_testprotos_test3_test_import_proto_goTypes = nil
 	file_internal_testprotos_test3_test_import_proto_depIdxs = nil
 }
+func (x *ImportMessage) MarshalZeroPB(buf []byte) (n int, err error) {
+	defer func() {
+		if e := recover(); e != nil {
+			err = errors.New("buffer overflow")
+		}
+	}()
+	return n, nil
+}
+func (x *ImportMessage) UnmarshalZeroPB(buf []byte) (n int, err error) {
+	defer func() {
+		if e := recover(); e != nil {
+			err = errors.New("buffer underflow")
+		}
+	}()
+	return n, nil
+}
diff --git a/internal/testprotos/test3/test_nesting.pulsar.go b/internal/testprotos/test3/test_nesting.pulsar.go
index 4547c29..82a3d7e 100644
--- a/internal/testprotos/test3/test_nesting.pulsar.go
+++ b/internal/testprotos/test3/test_nesting.pulsar.go
@@ -2,6 +2,7 @@
 package test3
 
 import (
+	errors "errors"
 	fmt "fmt"
 	runtime "github.com/cosmos/cosmos-proto/runtime"
 	protoreflect "google.golang.org/protobuf/reflect/protoreflect"
@@ -1754,7 +1755,7 @@ func (x *fastReflection_MultiLayeredNesting_Nested1_Nested2_Nested3) ProtoMethod
 // Code generated by protoc-gen-go. DO NOT EDIT.
 // versions:
 // 	protoc-gen-go v1.27.0
-// 	protoc        v3.18.1
+// 	protoc        v4.24.4
 // source: internal/testprotos/test3/test_nesting.proto
 
 const (
@@ -1866,6 +1867,7 @@ type MultiLayeredNesting_Nested1_Nested2_Nested3 struct {
 	unknownFields protoimpl.UnknownFields
 
 	// Types that are assignable to Nested3Oneof:
+	//
 	//	*MultiLayeredNesting_Nested1_Nested2_Nested3_Nested_3String
 	//	*MultiLayeredNesting_Nested1_Nested2_Nested3_Nested_3Int32
 	Nested3Oneof isMultiLayeredNesting_Nested1_Nested2_Nested3_Nested3Oneof `protobuf_oneof:"nested3_oneof"`
@@ -2071,3 +2073,30 @@ func file_internal_testprotos_test3_test_nesting_proto_init() {
 	file_internal_testprotos_test3_test_nesting_proto_goTypes = nil
 	file_internal_testprotos_test3_test_nesting_proto_depIdxs = nil
 }
+func (x *MultiLayeredNesting) MarshalZeroPB(buf []byte) (n int, err error) {
+	defer func() {
+		if e := recover(); e != nil {
+			err = errors.New("buffer overflow")
+		}
+	}()
+	n_0, err := x.Nested1.MarshalZeroPB(buf[n:])
+	n += n_0
+	if err != nil {
+		return n, err
+	}
+	return n, nil
+}
+func (x *MultiLayeredNesting) UnmarshalZeroPB(buf []byte) (n int, err error) {
+	defer func() {
+		if e := recover(); e != nil {
+			err = errors.New("buffer underflow")
+		}
+	}()
+	x.Nested1 = new(goproto.proto.test3.MultiLayeredNesting.Nested1)
+	n_0, err := x.Nested1.UnmarshalZeroPB(buf[n:])
+	n += n_0
+	if err != nil {
+		return n, err
+	}
+	return n, nil
+}
diff --git a/scripts/fastreflect.sh b/scripts/fastreflect.sh
index 135c1e3..7a3edbe 100755
--- a/scripts/fastreflect.sh
+++ b/scripts/fastreflect.sh
@@ -2,12 +2,14 @@
 
 set -e
 
+PULSAR_EXE=$(which  protoc-gen-go-pulsar)
+
 build() {
     echo finding protobuf files in "$1"
     proto_files=$(find "$1" -name "*.proto")
     for file in $proto_files; do
       echo "building proto file $file"
-      protoc -I=. --plugin /usr/bin/protoc-gen-go-pulsar --go-pulsar_out=. --go-pulsar_opt=features=protoc+fast "$file"
+      protoc -I=. --plugin "$PULSAR_EXE" --go-pulsar_out=. --go-pulsar_opt=features=protoc+fast+zeropb "$file"
     done
 }
 
@@ -17,4 +19,4 @@ do
 done
 
 cp -r github.com/cosmos/cosmos-proto/* ./
-rm -rf github.com
\ No newline at end of file
+rm -rf github.com
diff --git a/testpb/1.pulsar.go b/testpb/1.pulsar.go
index 1397f0f..1e8a0ee 100644
--- a/testpb/1.pulsar.go
+++ b/testpb/1.pulsar.go
@@ -3,6 +3,7 @@ package testpb
 
 import (
 	binary "encoding/binary"
+	errors "errors"
 	fmt "fmt"
 	runtime "github.com/cosmos/cosmos-proto/runtime"
 	protoreflect "google.golang.org/protobuf/reflect/protoreflect"
@@ -2635,7 +2636,7 @@ func (x *fastReflection_B) ProtoMethods() *protoiface.Methods {
 // Code generated by protoc-gen-go. DO NOT EDIT.
 // versions:
 // 	protoc-gen-go v1.27.0
-// 	protoc        v3.18.1
+// 	protoc        v4.24.4
 // source: testpb/1.proto
 
 const (
@@ -2716,6 +2717,7 @@ type A struct {
 	MAP         map[string]*B `protobuf:"bytes,18,rep,name=MAP,proto3" json:"MAP,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
 	LIST        []*B          `protobuf:"bytes,19,rep,name=LIST,proto3" json:"LIST,omitempty"`
 	// Types that are assignable to ONEOF:
+	//
 	//	*A_ONEOF_B
 	//	*A_ONEOF_STRING
 	ONEOF     isA_ONEOF        `protobuf_oneof:"ONEOF"`
@@ -3123,3 +3125,253 @@ func file_testpb_1_proto_init() {
 	file_testpb_1_proto_goTypes = nil
 	file_testpb_1_proto_depIdxs = nil
 }
+func (x *A) MarshalZeroPB(buf []byte) (n int, err error) {
+	defer func() {
+		if e := recover(); e != nil {
+			err = errors.New("buffer overflow")
+		}
+	}()
+	binary.LittleEndian.PutUint32(buf[n:], uint32(x.Enum))
+	n += 4
+	bool_1 := uint32(0)
+	if x.SomeBoolean {
+		bool_1 = 1
+	}
+	binary.LittleEndian.PutUint32(buf[n:], bool_1)
+	n += 4
+	binary.LittleEndian.PutUint32(buf[n:], uint32(x.INT32))
+	n += 4
+	binary.LittleEndian.PutUint32(buf[n:], uint32(x.SINT32))
+	n += 4
+	binary.LittleEndian.PutUint32(buf[n:], uint32(x.UINT32))
+	n += 4
+	binary.LittleEndian.PutUint64(buf[n:], uint64(x.INT64))
+	n += 8
+	binary.LittleEndian.PutUint64(buf[n:], uint64(x.SING64))
+	n += 8
+	binary.LittleEndian.PutUint64(buf[n:], uint64(x.UINT64))
+	n += 8
+	binary.LittleEndian.PutUint32(buf[n:], uint32(x.SFIXED32))
+	n += 4
+	binary.LittleEndian.PutUint32(buf[n:], uint32(x.FIXED32))
+	n += 4
+	binary.LittleEndian.PutUint32(buf[n:], math.Float32bits(x.FLOAT))
+	n += 4
+	binary.LittleEndian.PutUint64(buf[n:], uint64(x.SFIXED64))
+	n += 8
+	binary.LittleEndian.PutUint64(buf[n:], uint64(x.FIXED64))
+	n += 8
+	binary.LittleEndian.PutUint64(buf[n:], math.Float64bits(x.DOUBLE))
+	n += 8
+	len_14 := uint16(len(x.STRING))
+	if len(x.STRING) != int(len_14) {
+		return n, errors.New("field x.STRING is too long")
+	}
+	binary.LittleEndian.PutUint16(buf[n:], len_14)
+	n += 2
+	copy(buf[n:n+len(x.STRING)], x.STRING)
+	n += len(x.STRING)
+	len_15 := uint16(len(x.BYTES))
+	if len(x.BYTES) != int(len_15) {
+		return n, errors.New("field x.BYTES is too long")
+	}
+	binary.LittleEndian.PutUint16(buf[n:], len_15)
+	n += 2
+	copy(buf[n:n+len(x.BYTES)], x.BYTES)
+	n += len(x.BYTES)
+	n_16, err := x.MESSAGE.MarshalZeroPB(buf[n:])
+	n += n_16
+	if err != nil {
+		return n, err
+	}
+	len_17 := uint16(len(x.MAP))
+	if len(x.MAP) != int(len_17) {
+		return n, errors.New("field MAP is too long")
+	}
+	binary.LittleEndian.PutUint16(buf[n:], len_17)
+	n += 2
+	for k, v := range x.MAP {
+		len_0 := uint16(len(k))
+		if len(k) != int(len_0) {
+			return n, errors.New("field k is too long")
+		}
+		binary.LittleEndian.PutUint16(buf[n:], len_0)
+		n += 2
+		copy(buf[n:n+len(k)], k)
+		n += len(k)
+		n_1, err := v.MarshalZeroPB(buf[n:])
+		n += n_1
+		if err != nil {
+			return n, err
+		}
+	}
+	len_18 := uint16(len(x.LIST))
+	if len(x.LIST) != int(len_18) {
+		return n, errors.New("field LIST is too long")
+	}
+	binary.LittleEndian.PutUint16(buf[n:], len_18)
+	n += 2
+	for _, e := range x.LIST {
+		n_18, err := e.MarshalZeroPB(buf[n:])
+		n += n_18
+		if err != nil {
+			return n, err
+		}
+	}
+	// TODO: field ONEOF_B
+	// TODO: field ONEOF_STRING
+	len_21 := uint16(len(x.LIST_ENUM))
+	if len(x.LIST_ENUM) != int(len_21) {
+		return n, errors.New("field LIST_ENUM is too long")
+	}
+	binary.LittleEndian.PutUint16(buf[n:], len_21)
+	n += 2
+	for _, e := range x.LIST_ENUM {
+		binary.LittleEndian.PutUint32(buf[n:], uint32(e))
+		n += 4
+	}
+	n_22, err := x.Imported.MarshalZeroPB(buf[n:])
+	n += n_22
+	if err != nil {
+		return n, err
+	}
+	len_23 := uint16(len(x.Type_))
+	if len(x.Type_) != int(len_23) {
+		return n, errors.New("field x.Type_ is too long")
+	}
+	binary.LittleEndian.PutUint16(buf[n:], len_23)
+	n += 2
+	copy(buf[n:n+len(x.Type_)], x.Type_)
+	n += len(x.Type_)
+	return n, nil
+}
+func (x *A) UnmarshalZeroPB(buf []byte) (n int, err error) {
+	defer func() {
+		if e := recover(); e != nil {
+			err = errors.New("buffer underflow")
+		}
+	}()
+	x.Enum = Enumeration(binary.LittleEndian.Uint32(buf[n:]))
+	n += 4
+	bool_1 := binary.LittleEndian.Uint32(buf[n:])
+	x.SomeBoolean = false
+	if bool_1 != 0 {
+		x.SomeBoolean = true
+	}
+	n += 4
+	x.INT32 = int32(binary.LittleEndian.Uint32(buf[n:]))
+	n += 4
+	x.SINT32 = int32(binary.LittleEndian.Uint32(buf[n:]))
+	n += 4
+	x.UINT32 = binary.LittleEndian.Uint32(buf[n:])
+	n += 4
+	x.INT64 = int64(binary.LittleEndian.Uint64(buf[n:]))
+	n += 8
+	x.SING64 = int64(binary.LittleEndian.Uint64(buf[n:]))
+	n += 8
+	x.UINT64 = binary.LittleEndian.Uint64(buf[n:])
+	n += 8
+	x.SFIXED32 = int32(binary.LittleEndian.Uint32(buf[n:]))
+	n += 4
+	x.FIXED32 = binary.LittleEndian.Uint32(buf[n:])
+	n += 4
+	x.FLOAT = float32(math.Float32frombits(binary.LittleEndian.Uint32(buf[n:])))
+	n += 4
+	x.SFIXED64 = int64(binary.LittleEndian.Uint64(buf[n:]))
+	n += 8
+	x.FIXED64 = binary.LittleEndian.Uint64(buf[n:])
+	n += 8
+	x.DOUBLE = float64(math.Float64frombits(binary.LittleEndian.Uint64(buf[n:])))
+	n += 8
+	len_14 := int(binary.LittleEndian.Uint16(buf[n:]))
+	n += 2
+	x.STRING = string(buf[n : n+len_14])
+	n += len_14
+	len_15 := int(binary.LittleEndian.Uint16(buf[n:]))
+	n += 2
+	x.BYTES = append([]byte{}, buf[n:n+len_15]...)
+	n += len_15
+	x.MESSAGE = new(B)
+	n_16, err := x.MESSAGE.UnmarshalZeroPB(buf[n:])
+	n += n_16
+	if err != nil {
+		return n, err
+	}
+	len_17 := int(binary.LittleEndian.Uint16(buf[n:]))
+	n += 2
+	x.MAP = make(map[string]*B, len_17)
+	for i := 0; i < len_17; i++ {
+		var k string
+		var v *B
+		len_0 := int(binary.LittleEndian.Uint16(buf[n:]))
+		n += 2
+		k = string(buf[n : n+len_0])
+		n += len_0
+		v = new(B)
+		n_1, err := v.UnmarshalZeroPB(buf[n:])
+		n += n_1
+		if err != nil {
+			return n, err
+		}
+		x.MAP[k] = v
+	}
+	len_18 := int(binary.LittleEndian.Uint16(buf[n:]))
+	n += 2
+	x.LIST = make([]*B, len_18)
+	for i := range x.LIST {
+		x.LIST[i] = new(B)
+		n_18, err := x.LIST[i].UnmarshalZeroPB(buf[n:])
+		n += n_18
+		if err != nil {
+			return n, err
+		}
+	}
+	// TODO: field ONEOF_B
+	// TODO: field ONEOF_STRING
+	len_21 := int(binary.LittleEndian.Uint16(buf[n:]))
+	n += 2
+	x.LIST_ENUM = make([]Enumeration, len_21)
+	for i := range x.LIST_ENUM {
+		x.LIST_ENUM[i] = Enumeration(binary.LittleEndian.Uint32(buf[n:]))
+		n += 4
+	}
+	x.Imported = new(ImportedMessage)
+	n_22, err := x.Imported.UnmarshalZeroPB(buf[n:])
+	n += n_22
+	if err != nil {
+		return n, err
+	}
+	len_23 := int(binary.LittleEndian.Uint16(buf[n:]))
+	n += 2
+	x.Type_ = string(buf[n : n+len_23])
+	n += len_23
+	return n, nil
+}
+func (x *B) MarshalZeroPB(buf []byte) (n int, err error) {
+	defer func() {
+		if e := recover(); e != nil {
+			err = errors.New("buffer overflow")
+		}
+	}()
+	len_0 := uint16(len(x.X))
+	if len(x.X) != int(len_0) {
+		return n, errors.New("field x.X is too long")
+	}
+	binary.LittleEndian.PutUint16(buf[n:], len_0)
+	n += 2
+	copy(buf[n:n+len(x.X)], x.X)
+	n += len(x.X)
+	return n, nil
+}
+func (x *B) UnmarshalZeroPB(buf []byte) (n int, err error) {
+	defer func() {
+		if e := recover(); e != nil {
+			err = errors.New("buffer underflow")
+		}
+	}()
+	len_0 := int(binary.LittleEndian.Uint16(buf[n:]))
+	n += 2
+	x.X = string(buf[n : n+len_0])
+	n += len_0
+	return n, nil
+}
diff --git a/testpb/2.pulsar.go b/testpb/2.pulsar.go
index cb2aac9..031be62 100644
--- a/testpb/2.pulsar.go
+++ b/testpb/2.pulsar.go
@@ -2,6 +2,7 @@
 package testpb
 
 import (
+	errors "errors"
 	fmt "fmt"
 	runtime "github.com/cosmos/cosmos-proto/runtime"
 	protoreflect "google.golang.org/protobuf/reflect/protoreflect"
@@ -371,7 +372,7 @@ func (x *fastReflection_ImportedMessage) ProtoMethods() *protoiface.Methods {
 // Code generated by protoc-gen-go. DO NOT EDIT.
 // versions:
 // 	protoc-gen-go v1.27.0
-// 	protoc        v3.18.1
+// 	protoc        v4.24.4
 // source: testpb/2.proto
 
 const (
@@ -480,3 +481,19 @@ func file_testpb_2_proto_init() {
 	file_testpb_2_proto_goTypes = nil
 	file_testpb_2_proto_depIdxs = nil
 }
+func (x *ImportedMessage) MarshalZeroPB(buf []byte) (n int, err error) {
+	defer func() {
+		if e := recover(); e != nil {
+			err = errors.New("buffer overflow")
+		}
+	}()
+	return n, nil
+}
+func (x *ImportedMessage) UnmarshalZeroPB(buf []byte) (n int, err error) {
+	defer func() {
+		if e := recover(); e != nil {
+			err = errors.New("buffer underflow")
+		}
+	}()
+	return n, nil
+}
diff --git a/testpb/codec_test.go b/testpb/codec_test.go
index fc8014e..b79d39a 100644
--- a/testpb/codec_test.go
+++ b/testpb/codec_test.go
@@ -9,62 +9,118 @@ import (
 	"google.golang.org/protobuf/types/dynamicpb"
 )
 
-func TestMarshal(t *testing.T) {
-	msg := &A{
-		Enum:        Enumeration_Two,
-		SomeBoolean: true,
-		INT32:       2,
-		SINT32:      3,
-		UINT32:      4,
-		INT64:       5,
-		SING64:      6,
-		UINT64:      7,
-		SFIXED32:    8,
-		FIXED32:     9,
-		FLOAT:       10.1,
-		SFIXED64:    11,
-		FIXED64:     12,
-		DOUBLE:      13,
-		STRING:      "fourteen",
-		BYTES:       []byte("fifteen"),
-		MESSAGE:     &B{X: "something"},
-		MAP:         map[string]*B{"a": &B{X: "aa"}},
-		LIST:        []*B{{X: "list"}},
-		ONEOF:       &A_ONEOF_B{ONEOF_B: &B{X: "ONEOF"}},
-		LIST_ENUM:   []Enumeration{Enumeration_One},
-	}
+var testA = &A{
+	Enum:        Enumeration_Two,
+	SomeBoolean: true,
+	INT32:       2,
+	SINT32:      3,
+	UINT32:      4,
+	INT64:       5,
+	SING64:      6,
+	UINT64:      7,
+	SFIXED32:    8,
+	FIXED32:     9,
+	FLOAT:       10.1,
+	SFIXED64:    11,
+	FIXED64:     12,
+	DOUBLE:      13,
+	STRING:      "fourteen",
+	BYTES:       []byte("fifteen"),
+	MESSAGE:     &B{X: "something"},
+	MAP:         map[string]*B{"a": &B{X: "aa"}},
+	LIST:        []*B{{X: "list"}},
+	ONEOF:       &A_ONEOF_B{ONEOF_B: &B{X: "ONEOF"}},
+	LIST_ENUM:   []Enumeration{Enumeration_One},
+}
 
+func TestMarshal(t *testing.T) {
 	dynA := dynamicpb.NewMessage(md_A)
 	// dynB := dynamicpb.NewMessage(md_B)
 
-	dynA.Set(fd_A_enum, protoreflect.ValueOfEnum(protoreflect.EnumNumber(msg.Enum)))
-	dynA.Set(fd_A_some_boolean, protoreflect.ValueOfBool(msg.SomeBoolean))
-	dynA.Set(fd_A_INT32, protoreflect.ValueOfInt32(msg.INT32))
-	dynA.Set(fd_A_SINT32, protoreflect.ValueOfInt32(msg.SINT32))
-	dynA.Set(fd_A_UINT32, protoreflect.ValueOfUint32(msg.UINT32))
-	dynA.Set(fd_A_INT64, protoreflect.ValueOfInt64(msg.INT64))
-	dynA.Set(fd_A_SING64, protoreflect.ValueOfInt64(msg.SING64))
-	dynA.Set(fd_A_UINT64, protoreflect.ValueOfUint64(msg.UINT64))
-	dynA.Set(fd_A_SFIXED32, protoreflect.ValueOfInt32(msg.SFIXED32))
-	dynA.Set(fd_A_FIXED32, protoreflect.ValueOfUint32(msg.FIXED32))
-	dynA.Set(fd_A_FLOAT, protoreflect.ValueOfFloat32(msg.FLOAT))
-	dynA.Set(fd_A_SFIXED64, protoreflect.ValueOfInt64(msg.SFIXED64))
-	dynA.Set(fd_A_FIXED64, protoreflect.ValueOfUint64(msg.FIXED64))
-	dynA.Set(fd_A_DOUBLE, protoreflect.ValueOfFloat64(msg.DOUBLE))
-	dynA.Set(fd_A_STRING, protoreflect.ValueOfString(msg.STRING))
-	dynA.Set(fd_A_BYTES, protoreflect.ValueOfBytes(msg.BYTES))
-	dynA.Set(fd_A_MESSAGE, protoreflect.ValueOfMessage(msg.MESSAGE.ProtoReflect()))
+	dynA.Set(fd_A_enum, protoreflect.ValueOfEnum(protoreflect.EnumNumber(testA.Enum)))
+	dynA.Set(fd_A_some_boolean, protoreflect.ValueOfBool(testA.SomeBoolean))
+	dynA.Set(fd_A_INT32, protoreflect.ValueOfInt32(testA.INT32))
+	dynA.Set(fd_A_SINT32, protoreflect.ValueOfInt32(testA.SINT32))
+	dynA.Set(fd_A_UINT32, protoreflect.ValueOfUint32(testA.UINT32))
+	dynA.Set(fd_A_INT64, protoreflect.ValueOfInt64(testA.INT64))
+	dynA.Set(fd_A_SING64, protoreflect.ValueOfInt64(testA.SING64))
+	dynA.Set(fd_A_UINT64, protoreflect.ValueOfUint64(testA.UINT64))
+	dynA.Set(fd_A_SFIXED32, protoreflect.ValueOfInt32(testA.SFIXED32))
+	dynA.Set(fd_A_FIXED32, protoreflect.ValueOfUint32(testA.FIXED32))
+	dynA.Set(fd_A_FLOAT, protoreflect.ValueOfFloat32(testA.FLOAT))
+	dynA.Set(fd_A_SFIXED64, protoreflect.ValueOfInt64(testA.SFIXED64))
+	dynA.Set(fd_A_FIXED64, protoreflect.ValueOfUint64(testA.FIXED64))
+	dynA.Set(fd_A_DOUBLE, protoreflect.ValueOfFloat64(testA.DOUBLE))
+	dynA.Set(fd_A_STRING, protoreflect.ValueOfString(testA.STRING))
+	dynA.Set(fd_A_BYTES, protoreflect.ValueOfBytes(testA.BYTES))
+	dynA.Set(fd_A_MESSAGE, protoreflect.ValueOfMessage(testA.MESSAGE.ProtoReflect()))
 	dynMap := dynA.Mutable(fd_A_MAP).Map()
-	dynMap.Set(protoreflect.MapKey(protoreflect.ValueOfString("a")), protoreflect.ValueOfMessage(msg.MAP["a"].ProtoReflect()))
-	dynA.Mutable(fd_A_LIST).List().AppendMutable().Message().Set(fd_B_x, protoreflect.ValueOfString(msg.LIST[0].X))
-	dynA.Set(fd_A_ONEOF_B, protoreflect.ValueOfMessage(msg.ONEOF.(*A_ONEOF_B).ONEOF_B.ProtoReflect()))
+	dynMap.Set(protoreflect.MapKey(protoreflect.ValueOfString("a")), protoreflect.ValueOfMessage(testA.MAP["a"].ProtoReflect()))
+	dynA.Mutable(fd_A_LIST).List().AppendMutable().Message().Set(fd_B_x, protoreflect.ValueOfString(testA.LIST[0].X))
+	dynA.Set(fd_A_ONEOF_B, protoreflect.ValueOfMessage(testA.ONEOF.(*A_ONEOF_B).ONEOF_B.ProtoReflect()))
 	dynA.Mutable(fd_A_LIST_ENUM).List().Append(protoreflect.ValueOfEnum((protoreflect.EnumNumber)(Enumeration_One)))
 
-	got, err := proto.MarshalOptions{Deterministic: true}.Marshal(msg)
+	got, err := proto.MarshalOptions{Deterministic: true}.Marshal(testA)
 	require.NoError(t, err)
 
 	expected, err := proto.MarshalOptions{Deterministic: true}.Marshal(dynA)
 	require.NoError(t, err)
 
 	require.Equal(t, expected, got)
+
+	zpbBuf := make([]byte, 64*1024)
+	n, err := testA.MarshalZeroPB(zpbBuf)
+	require.NoError(t, err)
+	var msg2 A
+	n2, err := msg2.UnmarshalZeroPB(zpbBuf[:n])
+	require.NoError(t, err)
+	require.Equal(t, n, n2)
+	zpbGot, err := proto.MarshalOptions{Deterministic: true}.Marshal(&msg2)
+	require.NoError(t, err)
+	require.Equal(t, expected, zpbGot)
+}
+
+func BenchmarkMarshalA(b *testing.B) {
+	protoOpts := proto.MarshalOptions{}
+	b.Run("proto", func(b *testing.B) {
+		for i := 0; i < b.N; i++ {
+			protoOpts.Marshal(testA)
+		}
+	})
+	zpbBuf := make([]byte, 64*1024)
+	b.Run("zeropb", func(b *testing.B) {
+		for i := 0; i < b.N; i++ {
+			testA.MarshalZeroPB(zpbBuf)
+		}
+	})
+	n, err := testA.MarshalZeroPB(zpbBuf)
+	require.NoError(b, err)
+	data := make([]byte, n)
+	testA.MarshalZeroPB(data)
+	b.Run("copy", func(b *testing.B) {
+		for i := 0; i < b.N; i++ {
+			copy(zpbBuf, data)
+			testA.MarshalZeroPB(zpbBuf)
+		}
+	})
+}
+
+func BenchmarkUnmarshalA(b *testing.B) {
+	protoA, err := proto.MarshalOptions{}.Marshal(testA)
+	require.NoError(b, err)
+	b.Run("proto", func(b *testing.B) {
+		var a A
+		for i := 0; i < b.N; i++ {
+			proto.Unmarshal(protoA, &a)
+		}
+	})
+	zpbBuf := make([]byte, 64*1024)
+	n, err := testA.MarshalZeroPB(zpbBuf)
+	require.NoError(b, err)
+	b.Run("zeropb", func(b *testing.B) {
+		var testA A
+		for i := 0; i < b.N; i++ {
+			testA.UnmarshalZeroPB(zpbBuf[:n])
+		}
+	})
 }

From 7a9403f781b8c1392bf797058184f4a8da612dfc Mon Sep 17 00:00:00 2001
From: Elias Naur <elias@orijtech.com>
Date: Wed, 29 Nov 2023 19:18:29 -0600
Subject: [PATCH 2/9] struct-based codec

---
 features/zeropb/zeropb.go | 57 ++++++++++++++++++++++++++++++++++++---
 runtime/zeropb/zeropb.go  | 18 +++++++++++++
 testpb/1.pulsar.go        | 34 +++++++++++++++++++++++
 testpb/2.pulsar.go        |  4 +++
 4 files changed, 109 insertions(+), 4 deletions(-)
 create mode 100644 runtime/zeropb/zeropb.go

diff --git a/features/zeropb/zeropb.go b/features/zeropb/zeropb.go
index af62f6f..d445bbb 100644
--- a/features/zeropb/zeropb.go
+++ b/features/zeropb/zeropb.go
@@ -11,6 +11,8 @@ const (
 	errorsPackage = protogen.GoImportPath("errors")
 	mathPackage   = protogen.GoImportPath("math")
 	binaryPackage = protogen.GoImportPath("encoding/binary")
+
+	runtimePackage = protogen.GoImportPath("github.com/cosmos/cosmos-proto/runtime/zeropb")
 )
 
 func init() {
@@ -35,11 +37,58 @@ func (g zeropbFeature) GenerateFile(file *protogen.File, _ *protogen.Plugin) boo
 func (g zeropbFeature) GenerateHelpers() {}
 
 func (g zeropbFeature) generateMessage(f *protogen.File, m *protogen.Message) {
-	g.generateMarshal(f, m)
-	g.generateUnmarshal(f, m)
+	g.generateType(m)
+	g.generateMarshal(m)
+	g.generateUnmarshal(m)
+}
+
+func (g zeropbFeature) generateType(m *protogen.Message) {
+	g.gen.P("type _", m.GoIdent, "_zeropb struct {")
+	for _, f := range m.Fields {
+		g.generateFieldDef(f)
+	}
+	g.gen.P("}")
+}
+
+func (g zeropbFeature) generateFieldDef(f *protogen.Field) {
+	d := f.Desc
+	switch {
+	case d.IsList():
+		g.gen.P(f.GoName, " ", runtimePackage.Ident("List"))
+	case d.IsMap():
+		g.gen.P("// TODO: field ", f.GoName)
+	case d.ContainingOneof() != nil:
+		g.gen.P("// TODO: field ", f.GoName)
+	default:
+		switch d.Kind() {
+		case protoreflect.FloatKind:
+			g.gen.P(f.GoName, " float32")
+		case protoreflect.DoubleKind:
+			g.gen.P(f.GoName, " float64")
+		case protoreflect.Sfixed32Kind, protoreflect.Int32Kind, protoreflect.Sint32Kind:
+			g.gen.P(f.GoName, " int32")
+		case protoreflect.Fixed32Kind, protoreflect.Uint32Kind:
+			g.gen.P(f.GoName, " uint32")
+		case protoreflect.Sfixed64Kind, protoreflect.Int64Kind, protoreflect.Sint64Kind:
+			g.gen.P(f.GoName, " int64")
+		case protoreflect.Fixed64Kind, protoreflect.Uint64Kind:
+			g.gen.P(f.GoName, " uint64")
+		case protoreflect.EnumKind, protoreflect.BoolKind:
+			g.gen.P(f.GoName, " uint32")
+		case protoreflect.StringKind:
+			g.gen.P(f.GoName, " ", runtimePackage.Ident("String"))
+		case protoreflect.BytesKind:
+			g.gen.P(f.GoName, " ", runtimePackage.Ident("Bytes"))
+		case protoreflect.MessageKind:
+			typ := g.gen.QualifiedGoIdent(f.Message.GoIdent)
+			g.gen.P(f.GoName, " ", typ)
+		default:
+			g.gen.P("// TODO: field ", f.GoName)
+		}
+	}
 }
 
-func (g zeropbFeature) generateMarshal(f *protogen.File, m *protogen.Message) {
+func (g zeropbFeature) generateMarshal(m *protogen.Message) {
 	g.gen.P("func (x *", m.GoIdent, ") MarshalZeroPB(buf []byte) (n int, err error) {")
 	g.gen.P("defer func() {")
 	g.gen.P("    if e := recover(); e != nil {")
@@ -128,7 +177,7 @@ func (g zeropbFeature) generateMarshalPrimitive(d protoreflect.FieldDescriptor,
 	}
 }
 
-func (g zeropbFeature) generateUnmarshal(f *protogen.File, m *protogen.Message) {
+func (g zeropbFeature) generateUnmarshal(m *protogen.Message) {
 	g.gen.P("func (x *", m.GoIdent, ") UnmarshalZeroPB(buf []byte) (n int, err error) {")
 	g.gen.P("defer func() {")
 	g.gen.P("    if e := recover(); e != nil {")
diff --git a/runtime/zeropb/zeropb.go b/runtime/zeropb/zeropb.go
new file mode 100644
index 0000000..d9eb990
--- /dev/null
+++ b/runtime/zeropb/zeropb.go
@@ -0,0 +1,18 @@
+package zeropb
+
+type List struct {
+	slice
+}
+
+type String struct {
+	slice
+}
+
+type Bytes struct {
+	slice
+}
+
+type slice struct {
+	offset int16
+	length uint16
+}
diff --git a/testpb/1.pulsar.go b/testpb/1.pulsar.go
index 1e8a0ee..331c10c 100644
--- a/testpb/1.pulsar.go
+++ b/testpb/1.pulsar.go
@@ -6,6 +6,7 @@ import (
 	errors "errors"
 	fmt "fmt"
 	runtime "github.com/cosmos/cosmos-proto/runtime"
+	zeropb "github.com/cosmos/cosmos-proto/runtime/zeropb"
 	protoreflect "google.golang.org/protobuf/reflect/protoreflect"
 	protoiface "google.golang.org/protobuf/runtime/protoiface"
 	protoimpl "google.golang.org/protobuf/runtime/protoimpl"
@@ -3125,6 +3126,34 @@ func file_testpb_1_proto_init() {
 	file_testpb_1_proto_goTypes = nil
 	file_testpb_1_proto_depIdxs = nil
 }
+
+type _A_zeropb struct {
+	Enum        uint32
+	SomeBoolean uint32
+	INT32       int32
+	SINT32      int32
+	UINT32      uint32
+	INT64       int64
+	SING64      int64
+	UINT64      uint64
+	SFIXED32    int32
+	FIXED32     uint32
+	FLOAT       float32
+	SFIXED64    int64
+	FIXED64     uint64
+	DOUBLE      float64
+	STRING      zeropb.String
+	BYTES       zeropb.Bytes
+	MESSAGE     B
+	// TODO: field MAP
+	LIST zeropb.List
+	// TODO: field ONEOF_B
+	// TODO: field ONEOF_STRING
+	LIST_ENUM zeropb.List
+	Imported  ImportedMessage
+	Type_     zeropb.String
+}
+
 func (x *A) MarshalZeroPB(buf []byte) (n int, err error) {
 	defer func() {
 		if e := recover(); e != nil {
@@ -3347,6 +3376,11 @@ func (x *A) UnmarshalZeroPB(buf []byte) (n int, err error) {
 	n += len_23
 	return n, nil
 }
+
+type _B_zeropb struct {
+	X zeropb.String
+}
+
 func (x *B) MarshalZeroPB(buf []byte) (n int, err error) {
 	defer func() {
 		if e := recover(); e != nil {
diff --git a/testpb/2.pulsar.go b/testpb/2.pulsar.go
index 031be62..97f877d 100644
--- a/testpb/2.pulsar.go
+++ b/testpb/2.pulsar.go
@@ -481,6 +481,10 @@ func file_testpb_2_proto_init() {
 	file_testpb_2_proto_goTypes = nil
 	file_testpb_2_proto_depIdxs = nil
 }
+
+type _ImportedMessage_zeropb struct {
+}
+
 func (x *ImportedMessage) MarshalZeroPB(buf []byte) (n int, err error) {
 	defer func() {
 		if e := recover(); e != nil {

From 687793010bac66da751a5597404a0d9814a2da05 Mon Sep 17 00:00:00 2001
From: Elias Naur <elias@orijtech.com>
Date: Wed, 29 Nov 2023 19:28:27 -0600
Subject: [PATCH 3/9] wip

---
 features/zeropb/zeropb.go | 10 ++++------
 runtime/zeropb/zeropb.go  | 14 +-------------
 testpb/1.pulsar.go        | 14 +++++++-------
 3 files changed, 12 insertions(+), 26 deletions(-)

diff --git a/features/zeropb/zeropb.go b/features/zeropb/zeropb.go
index d445bbb..b420255 100644
--- a/features/zeropb/zeropb.go
+++ b/features/zeropb/zeropb.go
@@ -54,9 +54,9 @@ func (g zeropbFeature) generateFieldDef(f *protogen.Field) {
 	d := f.Desc
 	switch {
 	case d.IsList():
-		g.gen.P(f.GoName, " ", runtimePackage.Ident("List"))
+		g.gen.P(f.GoName, " ", runtimePackage.Ident("Slice"), "[any]")
 	case d.IsMap():
-		g.gen.P("// TODO: field ", f.GoName)
+		g.gen.P(f.GoName, " ", runtimePackage.Ident("Slice"), "[any]")
 	case d.ContainingOneof() != nil:
 		g.gen.P("// TODO: field ", f.GoName)
 	default:
@@ -75,10 +75,8 @@ func (g zeropbFeature) generateFieldDef(f *protogen.Field) {
 			g.gen.P(f.GoName, " uint64")
 		case protoreflect.EnumKind, protoreflect.BoolKind:
 			g.gen.P(f.GoName, " uint32")
-		case protoreflect.StringKind:
-			g.gen.P(f.GoName, " ", runtimePackage.Ident("String"))
-		case protoreflect.BytesKind:
-			g.gen.P(f.GoName, " ", runtimePackage.Ident("Bytes"))
+		case protoreflect.StringKind, protoreflect.BytesKind:
+			g.gen.P(f.GoName, " ", runtimePackage.Ident("Slice"), "[byte]")
 		case protoreflect.MessageKind:
 			typ := g.gen.QualifiedGoIdent(f.Message.GoIdent)
 			g.gen.P(f.GoName, " ", typ)
diff --git a/runtime/zeropb/zeropb.go b/runtime/zeropb/zeropb.go
index d9eb990..30a84c6 100644
--- a/runtime/zeropb/zeropb.go
+++ b/runtime/zeropb/zeropb.go
@@ -1,18 +1,6 @@
 package zeropb
 
-type List struct {
-	slice
-}
-
-type String struct {
-	slice
-}
-
-type Bytes struct {
-	slice
-}
-
-type slice struct {
+type Slice[T any] struct {
 	offset int16
 	length uint16
 }
diff --git a/testpb/1.pulsar.go b/testpb/1.pulsar.go
index 331c10c..5cc45e7 100644
--- a/testpb/1.pulsar.go
+++ b/testpb/1.pulsar.go
@@ -3142,16 +3142,16 @@ type _A_zeropb struct {
 	SFIXED64    int64
 	FIXED64     uint64
 	DOUBLE      float64
-	STRING      zeropb.String
-	BYTES       zeropb.Bytes
+	STRING      zeropb.Slice[byte]
+	BYTES       zeropb.Slice[byte]
 	MESSAGE     B
-	// TODO: field MAP
-	LIST zeropb.List
+	MAP         zeropb.Slice[any]
+	LIST        zeropb.Slice[any]
 	// TODO: field ONEOF_B
 	// TODO: field ONEOF_STRING
-	LIST_ENUM zeropb.List
+	LIST_ENUM zeropb.Slice[any]
 	Imported  ImportedMessage
-	Type_     zeropb.String
+	Type_     zeropb.Slice[byte]
 }
 
 func (x *A) MarshalZeroPB(buf []byte) (n int, err error) {
@@ -3378,7 +3378,7 @@ func (x *A) UnmarshalZeroPB(buf []byte) (n int, err error) {
 }
 
 type _B_zeropb struct {
-	X zeropb.String
+	X zeropb.Slice[byte]
 }
 
 func (x *B) MarshalZeroPB(buf []byte) (n int, err error) {

From dbf8abb6bf5f2c055ccabc720c5fa280404bde9a Mon Sep 17 00:00:00 2001
From: Elias Naur <elias@orijtech.com>
Date: Wed, 29 Nov 2023 19:33:07 -0600
Subject: [PATCH 4/9] wip

---
 features/zeropb/zeropb.go | 45 ---------------------------------------
 testpb/1.pulsar.go        | 34 -----------------------------
 testpb/2.pulsar.go        |  4 ----
 3 files changed, 83 deletions(-)

diff --git a/features/zeropb/zeropb.go b/features/zeropb/zeropb.go
index b420255..589a082 100644
--- a/features/zeropb/zeropb.go
+++ b/features/zeropb/zeropb.go
@@ -37,55 +37,10 @@ func (g zeropbFeature) GenerateFile(file *protogen.File, _ *protogen.Plugin) boo
 func (g zeropbFeature) GenerateHelpers() {}
 
 func (g zeropbFeature) generateMessage(f *protogen.File, m *protogen.Message) {
-	g.generateType(m)
 	g.generateMarshal(m)
 	g.generateUnmarshal(m)
 }
 
-func (g zeropbFeature) generateType(m *protogen.Message) {
-	g.gen.P("type _", m.GoIdent, "_zeropb struct {")
-	for _, f := range m.Fields {
-		g.generateFieldDef(f)
-	}
-	g.gen.P("}")
-}
-
-func (g zeropbFeature) generateFieldDef(f *protogen.Field) {
-	d := f.Desc
-	switch {
-	case d.IsList():
-		g.gen.P(f.GoName, " ", runtimePackage.Ident("Slice"), "[any]")
-	case d.IsMap():
-		g.gen.P(f.GoName, " ", runtimePackage.Ident("Slice"), "[any]")
-	case d.ContainingOneof() != nil:
-		g.gen.P("// TODO: field ", f.GoName)
-	default:
-		switch d.Kind() {
-		case protoreflect.FloatKind:
-			g.gen.P(f.GoName, " float32")
-		case protoreflect.DoubleKind:
-			g.gen.P(f.GoName, " float64")
-		case protoreflect.Sfixed32Kind, protoreflect.Int32Kind, protoreflect.Sint32Kind:
-			g.gen.P(f.GoName, " int32")
-		case protoreflect.Fixed32Kind, protoreflect.Uint32Kind:
-			g.gen.P(f.GoName, " uint32")
-		case protoreflect.Sfixed64Kind, protoreflect.Int64Kind, protoreflect.Sint64Kind:
-			g.gen.P(f.GoName, " int64")
-		case protoreflect.Fixed64Kind, protoreflect.Uint64Kind:
-			g.gen.P(f.GoName, " uint64")
-		case protoreflect.EnumKind, protoreflect.BoolKind:
-			g.gen.P(f.GoName, " uint32")
-		case protoreflect.StringKind, protoreflect.BytesKind:
-			g.gen.P(f.GoName, " ", runtimePackage.Ident("Slice"), "[byte]")
-		case protoreflect.MessageKind:
-			typ := g.gen.QualifiedGoIdent(f.Message.GoIdent)
-			g.gen.P(f.GoName, " ", typ)
-		default:
-			g.gen.P("// TODO: field ", f.GoName)
-		}
-	}
-}
-
 func (g zeropbFeature) generateMarshal(m *protogen.Message) {
 	g.gen.P("func (x *", m.GoIdent, ") MarshalZeroPB(buf []byte) (n int, err error) {")
 	g.gen.P("defer func() {")
diff --git a/testpb/1.pulsar.go b/testpb/1.pulsar.go
index 5cc45e7..1e8a0ee 100644
--- a/testpb/1.pulsar.go
+++ b/testpb/1.pulsar.go
@@ -6,7 +6,6 @@ import (
 	errors "errors"
 	fmt "fmt"
 	runtime "github.com/cosmos/cosmos-proto/runtime"
-	zeropb "github.com/cosmos/cosmos-proto/runtime/zeropb"
 	protoreflect "google.golang.org/protobuf/reflect/protoreflect"
 	protoiface "google.golang.org/protobuf/runtime/protoiface"
 	protoimpl "google.golang.org/protobuf/runtime/protoimpl"
@@ -3126,34 +3125,6 @@ func file_testpb_1_proto_init() {
 	file_testpb_1_proto_goTypes = nil
 	file_testpb_1_proto_depIdxs = nil
 }
-
-type _A_zeropb struct {
-	Enum        uint32
-	SomeBoolean uint32
-	INT32       int32
-	SINT32      int32
-	UINT32      uint32
-	INT64       int64
-	SING64      int64
-	UINT64      uint64
-	SFIXED32    int32
-	FIXED32     uint32
-	FLOAT       float32
-	SFIXED64    int64
-	FIXED64     uint64
-	DOUBLE      float64
-	STRING      zeropb.Slice[byte]
-	BYTES       zeropb.Slice[byte]
-	MESSAGE     B
-	MAP         zeropb.Slice[any]
-	LIST        zeropb.Slice[any]
-	// TODO: field ONEOF_B
-	// TODO: field ONEOF_STRING
-	LIST_ENUM zeropb.Slice[any]
-	Imported  ImportedMessage
-	Type_     zeropb.Slice[byte]
-}
-
 func (x *A) MarshalZeroPB(buf []byte) (n int, err error) {
 	defer func() {
 		if e := recover(); e != nil {
@@ -3376,11 +3347,6 @@ func (x *A) UnmarshalZeroPB(buf []byte) (n int, err error) {
 	n += len_23
 	return n, nil
 }
-
-type _B_zeropb struct {
-	X zeropb.Slice[byte]
-}
-
 func (x *B) MarshalZeroPB(buf []byte) (n int, err error) {
 	defer func() {
 		if e := recover(); e != nil {
diff --git a/testpb/2.pulsar.go b/testpb/2.pulsar.go
index 97f877d..031be62 100644
--- a/testpb/2.pulsar.go
+++ b/testpb/2.pulsar.go
@@ -481,10 +481,6 @@ func file_testpb_2_proto_init() {
 	file_testpb_2_proto_goTypes = nil
 	file_testpb_2_proto_depIdxs = nil
 }
-
-type _ImportedMessage_zeropb struct {
-}
-
 func (x *ImportedMessage) MarshalZeroPB(buf []byte) (n int, err error) {
 	defer func() {
 		if e := recover(); e != nil {

From aa8bd708d7c3beba2fcb7a10f03e3aa21d5eb6aa Mon Sep 17 00:00:00 2001
From: Elias Naur <elias@orijtech.com>
Date: Wed, 29 Nov 2023 19:38:14 -0600
Subject: [PATCH 5/9] wip

---
 features/zeropb/zeropb.go | 251 ++++++++++++++++------------
 runtime/zeropb/zeropb.go  |  67 +++++++-
 testpb/1.pulsar.go        | 332 +++++++++++++++-----------------------
 testpb/2.pulsar.go        |  16 +-
 testpb/codec_test.go      |   3 +-
 5 files changed, 354 insertions(+), 315 deletions(-)

diff --git a/features/zeropb/zeropb.go b/features/zeropb/zeropb.go
index 589a082..45da39c 100644
--- a/features/zeropb/zeropb.go
+++ b/features/zeropb/zeropb.go
@@ -1,6 +1,9 @@
 package zeropb
 
 import (
+	"fmt"
+	"strconv"
+
 	"github.com/cosmos/cosmos-proto/features/protoc"
 	"github.com/cosmos/cosmos-proto/generator"
 	"google.golang.org/protobuf/compiler/protogen"
@@ -43,87 +46,132 @@ func (g zeropbFeature) generateMessage(f *protogen.File, m *protogen.Message) {
 
 func (g zeropbFeature) generateMarshal(m *protogen.Message) {
 	g.gen.P("func (x *", m.GoIdent, ") MarshalZeroPB(buf []byte) (n int, err error) {")
-	g.gen.P("defer func() {")
-	g.gen.P("    if e := recover(); e != nil {")
-	g.gen.P("        err = ", errorsPackage.Ident("New"), `("buffer overflow")`)
-	g.gen.P("    }")
-	g.gen.P("}()")
+	g.gen.P("    defer func() {")
+	g.gen.P("        if e := recover(); e != nil {")
+	g.gen.P("            err = ", errorsPackage.Ident("New"), `("buffer overflow")`)
+	g.gen.P("        }")
+	g.gen.P("    }()")
+	g.gen.P("    b := ", runtimePackage.Ident("NewBuffer"), "(buf)")
+	g.gen.P("    x.marshalZeroPB(b, b.Alloc(", structSize(m), "))")
+	g.gen.P("    return int(b.Allocated()), nil")
+	g.gen.P("}")
+	g.gen.P()
+	g.gen.P("func (x *", m.GoIdent, ") marshalZeroPB(b *", runtimePackage.Ident("Buffer"), ", buf ", runtimePackage.Ident("Allocation"), ") {")
+	offset := 0
 	for _, f := range m.Fields {
-		g.generateMarshalField(f)
+		g.generateMarshalField(f, offset)
+		offset += fieldSize(f)
 	}
-	g.gen.P("return n, nil")
 	g.gen.P("}")
 }
 
-func (g zeropbFeature) generateMarshalField(f *protogen.Field) {
+const (
+	sliceSize         = 2 * 2
+	segmentHeaderSize = 1 + 1 + 2
+)
+
+func structSize(m *protogen.Message) int {
+	n := 0
+	for _, f := range m.Fields {
+		d := f.Desc
+		switch {
+		case d.IsList(), d.IsMap():
+			n += sliceSize
+		default:
+			n += fieldSize(f)
+		}
+	}
+	return n
+}
+
+func fieldSize(f *protogen.Field) int {
+	d := f.Desc
+	switch {
+	case d.IsList(), d.IsMap():
+		return 4
+	}
+	return fieldElemSize(f)
+}
+
+func fieldElemSize(f *protogen.Field) int {
+	d := f.Desc
+	switch d.Kind() {
+	case protoreflect.FloatKind:
+		return 4
+	case protoreflect.DoubleKind:
+		return 8
+	case protoreflect.Sfixed32Kind, protoreflect.Fixed32Kind, protoreflect.Int32Kind, protoreflect.Sint32Kind, protoreflect.Uint32Kind, protoreflect.EnumKind:
+		return 4
+	case protoreflect.Sfixed64Kind, protoreflect.Fixed64Kind, protoreflect.Int64Kind, protoreflect.Sint64Kind, protoreflect.Uint64Kind:
+		return 8
+	case protoreflect.BoolKind:
+		return 4
+	case protoreflect.StringKind, protoreflect.BytesKind:
+		return sliceSize
+	case protoreflect.MessageKind:
+		return structSize(f.Message)
+	}
+	return 0
+}
+
+func (g zeropbFeature) generateMarshalField(f *protogen.Field, offset int) {
 	d := f.Desc
 	switch {
 	case d.IsList():
-		g.gen.P("len_", d.Index(), " := uint16(len(x.", f.GoName, "))")
-		g.gen.P("if len(x.", f.GoName, ") != int(len_", d.Index(), ") {")
-		g.gen.P("    return n, ", errorsPackage.Ident("New"), `("field `, f.GoName, ` is too long")`)
-		g.gen.P("}")
-		g.gen.P(binaryPackage.Ident("LittleEndian"), ".PutUint16(buf[n:], len_", d.Index(), ")")
-		g.gen.P("n += 2")
-		g.gen.P("for _, e := range x.", f.GoName, " {")
-		g.generateMarshalPrimitive(d, "e")
+		g.gen.P("buf_", d.Index(), " := b.AllocRel(len(x.", f.GoName, ")*", fieldElemSize(f), " + ", segmentHeaderSize, ", buf, ", offset, ", uint16(len(x.", f.GoName, ")))")
+		g.gen.P("{")
+		g.gen.P("    buf := buf_", d.Index())
+		// Write a segment header.
+		g.gen.P("    buf.Buf[0] = byte(len(x.", f.GoName, "))")
+		g.gen.P("    buf.Buf[1] = byte(len(x.", f.GoName, "))")
+		g.gen.P(binaryPackage.Ident("LittleEndian"), ".PutUint16(buf.Buf[2:], 0)")
+		g.gen.P("    for i, e := range x.", f.GoName, " {")
+		g.generateMarshalPrimitive(f, "e", fmt.Sprintf("uint16(i)*%d+4", fieldElemSize(f)))
+		g.gen.P("    }")
 		g.gen.P("}")
 	case d.IsMap():
-		g.gen.P("len_", d.Index(), " := uint16(len(x.", f.GoName, "))")
-		g.gen.P("if len(x.", f.GoName, ") != int(len_", d.Index(), ") {")
-		g.gen.P("    return n, ", errorsPackage.Ident("New"), `("field `, f.GoName, ` is too long")`)
-		g.gen.P("}")
-		g.gen.P("binary.LittleEndian.PutUint16(buf[n:], len_", d.Index(), ")")
-		g.gen.P("n += 2")
-		g.gen.P("for k, v := range x.", f.GoName, " {")
-		g.generateMarshalPrimitive(d.MapKey(), "k")
-		g.generateMarshalPrimitive(d.MapValue(), "v")
+		sz := fieldSize(f.Message.Fields[0]) + fieldSize(f.Message.Fields[1])
+		g.gen.P("buf_", d.Index(), " := b.AllocRel(len(x.", f.GoName, ")*", sz, ", buf, ", offset, ", uint16(len(x."+f.GoName+")))")
+		g.gen.P("{")
+		g.gen.P("    var n uint16")
+		g.gen.P("    buf := buf_", d.Index())
+		g.gen.P("    for k, v := range x.", f.GoName, " {")
+		g.generateMarshalPrimitive(f.Message.Fields[0], "k", "n")
+		g.gen.P("    n += ", fieldSize(f.Message.Fields[0]))
+		g.generateMarshalPrimitive(f.Message.Fields[1], "v", "n")
+		g.gen.P("    n += ", fieldSize(f.Message.Fields[1]))
+		g.gen.P("    }")
 		g.gen.P("}")
 	case d.ContainingOneof() != nil:
 		g.gen.P("// TODO: field ", f.GoName)
 		return
 	default:
-		g.generateMarshalPrimitive(d, "x."+f.GoName)
+		g.generateMarshalPrimitive(f, "x."+f.GoName, strconv.Itoa(offset))
 	}
 }
 
-func (g zeropbFeature) generateMarshalPrimitive(d protoreflect.FieldDescriptor, name string) {
+func (g zeropbFeature) generateMarshalPrimitive(f *protogen.Field, name, offset string) {
+	d := f.Desc
 	switch d.Kind() {
 	case protoreflect.FloatKind:
-		g.gen.P("binary.LittleEndian.PutUint32(buf[n:], ", mathPackage.Ident("Float32bits"), "(", name, "))")
-		g.gen.P("n += 4")
+		g.gen.P("binary.LittleEndian.PutUint32(buf.Buf[", offset, ":], ", mathPackage.Ident("Float32bits"), "(", name, "))")
 	case protoreflect.DoubleKind:
-		g.gen.P("binary.LittleEndian.PutUint64(buf[n:], ", mathPackage.Ident("Float64bits"), "(", name, "))")
-		g.gen.P("n += 8")
+		g.gen.P("binary.LittleEndian.PutUint64(buf.Buf[", offset, ":], ", mathPackage.Ident("Float64bits"), "(", name, "))")
 	case protoreflect.Sfixed32Kind, protoreflect.Fixed32Kind, protoreflect.Int32Kind, protoreflect.Sint32Kind, protoreflect.Uint32Kind, protoreflect.EnumKind:
-		g.gen.P("binary.LittleEndian.PutUint32(buf[n:], uint32(", name, "))")
-		g.gen.P("n += 4")
+		g.gen.P("binary.LittleEndian.PutUint32(buf.Buf[", offset, ":], uint32(", name, "))")
 	case protoreflect.Sfixed64Kind, protoreflect.Fixed64Kind, protoreflect.Int64Kind, protoreflect.Sint64Kind, protoreflect.Uint64Kind:
-		g.gen.P("binary.LittleEndian.PutUint64(buf[n:], uint64(", name, "))")
-		g.gen.P("n += 8")
+		g.gen.P("binary.LittleEndian.PutUint64(buf.Buf[", offset, ":], uint64(", name, "))")
 	case protoreflect.BoolKind:
 		g.gen.P("bool_", d.Index(), " := uint32(0)")
 		g.gen.P("if ", name, " {")
 		g.gen.P("    bool_", d.Index(), " = 1")
 		g.gen.P("}")
-		g.gen.P("binary.LittleEndian.PutUint32(buf[n:], bool_", d.Index(), ")")
-		g.gen.P("n += 4")
+		g.gen.P("binary.LittleEndian.PutUint32(buf.Buf[", offset, ":], bool_", d.Index(), ")")
 	case protoreflect.StringKind, protoreflect.BytesKind:
-		g.gen.P("len_", d.Index(), " := uint16(len(", name, "))")
-		g.gen.P("if len(", name, ") != int(len_", d.Index(), ") {")
-		g.gen.P("    return n, ", errorsPackage.Ident("New"), `("field `, name, ` is too long")`)
-		g.gen.P("}")
-		g.gen.P("binary.LittleEndian.PutUint16(buf[n:], len_", d.Index(), ")")
-		g.gen.P("n += 2")
-		// Reslice buf to convert a truncated write into a buffer overflow error.
-		g.gen.P("copy(buf[n:n+len(", name, ")], ", name, ")")
-		g.gen.P("n += len(", name, ")")
+		g.gen.P("buf_", d.Index(), " := b.AllocRel(len(", name, "), buf, ", offset, ", uint16(len(", name, ")))")
+		g.gen.P("copy(buf_", d.Index(), ".Buf, ", name, ")")
 	case protoreflect.MessageKind:
-		g.gen.P("n_", d.Index(), ", err := ", name, ".MarshalZeroPB(buf[n:])")
-		g.gen.P("n += n_", d.Index())
-		g.gen.P("if err != nil {")
-		g.gen.P("    return n, err")
-		g.gen.P("}")
+		g.gen.P(name, ".marshalZeroPB(b, buf.Slice(", offset, "))")
 	default:
 		g.gen.P("// TODO: field ", name)
 		g.gen.P("_ = ", name)
@@ -131,105 +179,100 @@ func (g zeropbFeature) generateMarshalPrimitive(d protoreflect.FieldDescriptor,
 }
 
 func (g zeropbFeature) generateUnmarshal(m *protogen.Message) {
-	g.gen.P("func (x *", m.GoIdent, ") UnmarshalZeroPB(buf []byte) (n int, err error) {")
-	g.gen.P("defer func() {")
-	g.gen.P("    if e := recover(); e != nil {")
-	g.gen.P("        err = ", errorsPackage.Ident("New"), `("buffer underflow")`)
-	g.gen.P("    }")
-	g.gen.P("}()")
+	g.gen.P("func (x *", m.GoIdent, ") UnmarshalZeroPB(buf []byte) (err error) {")
+	g.gen.P("    defer func() {")
+	g.gen.P("        if e := recover(); e != nil {")
+	g.gen.P("            err = ", errorsPackage.Ident("New"), `("buffer underflow")`)
+	g.gen.P("        }")
+	g.gen.P("    }()")
+	g.gen.P("    x.unmarshalZeroPB(buf, 0)")
+	g.gen.P("    return nil")
+	g.gen.P("}")
+	g.gen.P()
+	g.gen.P("func (x *", m.GoIdent, ") unmarshalZeroPB(buf []byte, n uint16) {")
+	offset := 0
 	for _, f := range m.Fields {
-		g.generateUnmarshalField(f)
+		g.generateUnmarshalField(f, offset)
+		offset += fieldSize(f)
 	}
-	g.gen.P("return n, nil")
 	g.gen.P("}")
 }
 
-func (g zeropbFeature) generateUnmarshalField(f *protogen.Field) {
+func (g zeropbFeature) generateUnmarshalField(f *protogen.Field, offset int) {
 	d := f.Desc
 	switch {
 	case d.IsList():
-		g.gen.P("len_", d.Index(), " := int(binary.LittleEndian.Uint16(buf[n:]))")
-		g.gen.P("n += 2")
+		g.gen.P("n_", d.Index(), ", len_", d.Index(), " := ", runtimePackage.Ident("ReadSlice"), "(buf, n+", offset, ")")
 		typ, pointer := protoc.FieldGoType(g.gen, f)
 		if pointer {
 			typ = "*" + typ
 		}
 		g.gen.P("x.", f.GoName, " = make(", typ, ", len_", d.Index(), ")")
-		g.gen.P("for i := range x.", f.GoName, "{")
-		g.generateUnmarshalPrimitive(f, "x."+f.GoName+"[i]")
+		g.gen.P("{")
+		g.gen.P("    for i := range x.", f.GoName, "{")
+		// Skip segment header.
+		g.generateUnmarshalPrimitive(f, "x."+f.GoName+"[i]", fmt.Sprintf("n_%d+4+uint16(i)*%d", d.Index(), fieldElemSize(f)))
+		g.gen.P("    }")
 		g.gen.P("}")
 	case d.IsMap():
-		g.gen.P("len_", d.Index(), " := int(", binaryPackage.Ident("LittleEndian"), ".Uint16(buf[n:]))")
-		g.gen.P("n += 2")
+		g.gen.P("n_", d.Index(), ", len_", d.Index(), " := ", runtimePackage.Ident("ReadSlice"), "(buf, n+", offset, ")")
 		typ, _ := protoc.FieldGoType(g.gen, f)
 		g.gen.P("x.", f.GoName, " = make(", typ, ", len_", d.Index(), ")")
 		keyType, _ := protoc.FieldGoType(g.gen, f.Message.Fields[0])
 		valType, _ := protoc.FieldGoType(g.gen, f.Message.Fields[1])
-		g.gen.P("for i := 0; i < len_", d.Index(), "; i++ {")
-		g.gen.P("var k ", keyType)
-		g.gen.P("var v ", valType)
-		g.generateUnmarshalPrimitive(f.Message.Fields[0], "k")
-		g.generateUnmarshalPrimitive(f.Message.Fields[1], "v")
-		g.gen.P("    x.", f.GoName, "[k] = v")
+		g.gen.P("{")
+		g.gen.P("    n := n_", d.Index())
+		g.gen.P("    for i := uint16(0); i < len_", d.Index(), "; i++ {")
+		g.gen.P("        var k ", keyType)
+		g.gen.P("        var v ", valType)
+		g.generateUnmarshalPrimitive(f.Message.Fields[0], "k", "n")
+		g.gen.P("        n += ", fieldSize(f.Message.Fields[0]))
+		g.generateUnmarshalPrimitive(f.Message.Fields[1], "v", "n")
+		g.gen.P("        n += ", fieldSize(f.Message.Fields[1]))
+		g.gen.P("        x.", f.GoName, "[k] = v")
+		g.gen.P("    }")
 		g.gen.P("}")
 	case d.ContainingOneof() != nil:
 		g.gen.P("// TODO: field ", f.GoName)
 	default:
-		g.generateUnmarshalPrimitive(f, "x."+f.GoName)
+		g.generateUnmarshalPrimitive(f, "x."+f.GoName, fmt.Sprintf("n+%d", offset))
 	}
 }
 
-func (g zeropbFeature) generateUnmarshalPrimitive(f *protogen.Field, name string) {
+func (g zeropbFeature) generateUnmarshalPrimitive(f *protogen.Field, name, offset string) {
 	switch d := f.Desc; d.Kind() {
 	case protoreflect.FloatKind:
-		g.gen.P(name, " = float32(", mathPackage.Ident("Float32frombits"), "(", binaryPackage.Ident("LittleEndian"), ".Uint32(buf[n:])))")
-		g.gen.P("n += 4")
+		g.gen.P(name, " = float32(", mathPackage.Ident("Float32frombits"), "(", binaryPackage.Ident("LittleEndian"), ".Uint32(buf[", offset, ":])))")
 	case protoreflect.DoubleKind:
-		g.gen.P(name, " = float64(", mathPackage.Ident("Float64frombits"), "(", binaryPackage.Ident("LittleEndian"), ".Uint64(buf[n:])))")
-		g.gen.P("n += 8")
+		g.gen.P(name, " = float64(", mathPackage.Ident("Float64frombits"), "(", binaryPackage.Ident("LittleEndian"), ".Uint64(buf[", offset, ":])))")
 	case protoreflect.Sfixed32Kind, protoreflect.Int32Kind, protoreflect.Sint32Kind:
-		g.gen.P(name, " = int32(", binaryPackage.Ident("LittleEndian"), ".Uint32(buf[n:]))")
-		g.gen.P("n += 4")
+		g.gen.P(name, " = int32(", binaryPackage.Ident("LittleEndian"), ".Uint32(buf[", offset, ":]))")
 	case protoreflect.Fixed32Kind, protoreflect.Uint32Kind:
-		g.gen.P(name, " = ", binaryPackage.Ident("LittleEndian"), ".Uint32(buf[n:])")
-		g.gen.P("n += 4")
+		g.gen.P(name, " = ", binaryPackage.Ident("LittleEndian"), ".Uint32(buf[", offset, ":])")
 	case protoreflect.Sfixed64Kind, protoreflect.Int64Kind, protoreflect.Sint64Kind:
-		g.gen.P(name, " = int64(", binaryPackage.Ident("LittleEndian"), ".Uint64(buf[n:]))")
-		g.gen.P("n += 8")
+		g.gen.P(name, " = int64(", binaryPackage.Ident("LittleEndian"), ".Uint64(buf[", offset, ":]))")
 	case protoreflect.Fixed64Kind, protoreflect.Uint64Kind:
-		g.gen.P(name, " = ", binaryPackage.Ident("LittleEndian"), ".Uint64(buf[n:])")
-		g.gen.P("n += 8")
+		g.gen.P(name, " = ", binaryPackage.Ident("LittleEndian"), ".Uint64(buf[", offset, ":])")
 	case protoreflect.EnumKind:
 		typ := g.gen.QualifiedGoIdent(f.Enum.GoIdent)
-		g.gen.P(name, " = ", typ, "(", binaryPackage.Ident("LittleEndian"), ".Uint32(buf[n:]))")
-		g.gen.P("n += 4")
+		g.gen.P(name, " = ", typ, "(", binaryPackage.Ident("LittleEndian"), ".Uint32(buf[", offset, ":]))")
 	case protoreflect.BoolKind:
-		g.gen.P("bool_", d.Index(), " := ", binaryPackage.Ident("LittleEndian"), ".Uint32(buf[n:])")
+		g.gen.P("bool_", d.Index(), " := ", binaryPackage.Ident("LittleEndian"), ".Uint32(buf[", offset, ":])")
 		g.gen.P(name, " = false")
 		g.gen.P("if bool_", d.Index(), " != 0 {")
 		g.gen.P("    ", name, " = true")
 		g.gen.P("}")
-		g.gen.P("n += 4")
 	case protoreflect.StringKind:
-		g.gen.P("len_", d.Index(), " := int(", binaryPackage.Ident("LittleEndian"), ".Uint16(buf[n:]))")
-		g.gen.P("n += 2")
-		g.gen.P(name, " = string(buf[n:n+len_", d.Index(), "])")
-		g.gen.P("n += len_", d.Index())
+		g.gen.P("n_", d.Index(), ", len_", d.Index(), " := ", runtimePackage.Ident("ReadSlice"), "(buf, ", offset, ")")
+		g.gen.P(name, " = string(buf[n_", d.Index(), ":n_", d.Index(), "+len_", d.Index(), "])")
 	case protoreflect.BytesKind:
-		g.gen.P("len_", d.Index(), " := int(", binaryPackage.Ident("LittleEndian"), ".Uint16(buf[n:]))")
-		g.gen.P("n += 2")
-		g.gen.P(name, " = append([]byte{}, buf[n:n+len_", d.Index(), "]...)")
-		g.gen.P("n += len_", d.Index())
+		g.gen.P("n_", d.Index(), ", len_", d.Index(), " := ", runtimePackage.Ident("ReadSlice"), "(buf, ", offset, ")")
+		g.gen.P(name, " = append([]byte{}, buf[n_", d.Index(), ":n_", d.Index(), "+len_", d.Index(), "]...)")
 	case protoreflect.MessageKind:
 		typ := g.gen.QualifiedGoIdent(f.Message.GoIdent)
 		g.gen.P(name, " = new(", typ, ")")
-		g.gen.P("n_", d.Index(), ", err := ", name, ".UnmarshalZeroPB(buf[n:])")
-		g.gen.P("n += n_", d.Index())
-		g.gen.P("if err != nil {")
-		g.gen.P("    return n, err")
-		g.gen.P("}")
+		g.gen.P(name, ".unmarshalZeroPB(buf, ", offset, ")")
 	default:
 		g.gen.P("// TODO: field ", name)
-		g.gen.P("_ = ", name)
 	}
 }
diff --git a/runtime/zeropb/zeropb.go b/runtime/zeropb/zeropb.go
index 30a84c6..b3f502e 100644
--- a/runtime/zeropb/zeropb.go
+++ b/runtime/zeropb/zeropb.go
@@ -1,6 +1,67 @@
 package zeropb
 
-type Slice[T any] struct {
-	offset int16
-	length uint16
+import (
+	"encoding/binary"
+	"fmt"
+	"math"
+)
+
+type Buffer struct {
+	buf       []byte
+	allocated uint16
+}
+
+type Allocation struct {
+	Buf    []byte
+	offset uint16
+}
+
+func NewBuffer(b []byte) *Buffer {
+	if len(b) > math.MaxUint16 {
+		b = b[:math.MaxUint16]
+	}
+	return &Buffer{
+		buf: b,
+	}
+}
+
+func (b *Buffer) Alloc(n int) Allocation {
+	n16 := uint16(n)
+	if int(n16) != n {
+		panic(fmt.Errorf("allocation %d too large", n))
+	}
+	a := Allocation{
+		Buf:    b.buf[:n16],
+		offset: b.allocated,
+	}
+	b.buf = b.buf[n16:]
+	b.allocated += n16
+	return a
+}
+
+func (b *Buffer) AllocRel(n int, dst Allocation, offset, len uint16) Allocation {
+	a := b.Alloc(n)
+	bo := binary.LittleEndian
+	// Write relative offset and len.
+	bo.PutUint16(dst.Buf[offset:], a.offset-dst.offset-offset)
+	bo.PutUint16(dst.Buf[offset+2:], len)
+	return a
+}
+
+func (b *Buffer) Allocated() uint16 {
+	return b.allocated
+}
+
+func (a Allocation) Slice(offset uint16) Allocation {
+	return Allocation{
+		Buf:    a.Buf[offset:],
+		offset: a.offset + offset,
+	}
+}
+
+func ReadSlice(buf []byte, offset uint16) (off, len uint16) {
+	bo := binary.LittleEndian
+	off = offset + bo.Uint16(buf[offset:])
+	len = bo.Uint16(buf[offset+2:])
+	return
 }
diff --git a/testpb/1.pulsar.go b/testpb/1.pulsar.go
index 1e8a0ee..249822b 100644
--- a/testpb/1.pulsar.go
+++ b/testpb/1.pulsar.go
@@ -6,6 +6,7 @@ import (
 	errors "errors"
 	fmt "fmt"
 	runtime "github.com/cosmos/cosmos-proto/runtime"
+	zeropb "github.com/cosmos/cosmos-proto/runtime/zeropb"
 	protoreflect "google.golang.org/protobuf/reflect/protoreflect"
 	protoiface "google.golang.org/protobuf/runtime/protoiface"
 	protoimpl "google.golang.org/protobuf/runtime/protoimpl"
@@ -3131,221 +3132,145 @@ func (x *A) MarshalZeroPB(buf []byte) (n int, err error) {
 			err = errors.New("buffer overflow")
 		}
 	}()
-	binary.LittleEndian.PutUint32(buf[n:], uint32(x.Enum))
-	n += 4
+	b := zeropb.NewBuffer(buf)
+	x.marshalZeroPB(b, b.Alloc(116))
+	return int(b.Allocated()), nil
+}
+
+func (x *A) marshalZeroPB(b *zeropb.Buffer, buf zeropb.Allocation) {
+	binary.LittleEndian.PutUint32(buf.Buf[0:], uint32(x.Enum))
 	bool_1 := uint32(0)
 	if x.SomeBoolean {
 		bool_1 = 1
 	}
-	binary.LittleEndian.PutUint32(buf[n:], bool_1)
-	n += 4
-	binary.LittleEndian.PutUint32(buf[n:], uint32(x.INT32))
-	n += 4
-	binary.LittleEndian.PutUint32(buf[n:], uint32(x.SINT32))
-	n += 4
-	binary.LittleEndian.PutUint32(buf[n:], uint32(x.UINT32))
-	n += 4
-	binary.LittleEndian.PutUint64(buf[n:], uint64(x.INT64))
-	n += 8
-	binary.LittleEndian.PutUint64(buf[n:], uint64(x.SING64))
-	n += 8
-	binary.LittleEndian.PutUint64(buf[n:], uint64(x.UINT64))
-	n += 8
-	binary.LittleEndian.PutUint32(buf[n:], uint32(x.SFIXED32))
-	n += 4
-	binary.LittleEndian.PutUint32(buf[n:], uint32(x.FIXED32))
-	n += 4
-	binary.LittleEndian.PutUint32(buf[n:], math.Float32bits(x.FLOAT))
-	n += 4
-	binary.LittleEndian.PutUint64(buf[n:], uint64(x.SFIXED64))
-	n += 8
-	binary.LittleEndian.PutUint64(buf[n:], uint64(x.FIXED64))
-	n += 8
-	binary.LittleEndian.PutUint64(buf[n:], math.Float64bits(x.DOUBLE))
-	n += 8
-	len_14 := uint16(len(x.STRING))
-	if len(x.STRING) != int(len_14) {
-		return n, errors.New("field x.STRING is too long")
-	}
-	binary.LittleEndian.PutUint16(buf[n:], len_14)
-	n += 2
-	copy(buf[n:n+len(x.STRING)], x.STRING)
-	n += len(x.STRING)
-	len_15 := uint16(len(x.BYTES))
-	if len(x.BYTES) != int(len_15) {
-		return n, errors.New("field x.BYTES is too long")
-	}
-	binary.LittleEndian.PutUint16(buf[n:], len_15)
-	n += 2
-	copy(buf[n:n+len(x.BYTES)], x.BYTES)
-	n += len(x.BYTES)
-	n_16, err := x.MESSAGE.MarshalZeroPB(buf[n:])
-	n += n_16
-	if err != nil {
-		return n, err
-	}
-	len_17 := uint16(len(x.MAP))
-	if len(x.MAP) != int(len_17) {
-		return n, errors.New("field MAP is too long")
-	}
-	binary.LittleEndian.PutUint16(buf[n:], len_17)
-	n += 2
-	for k, v := range x.MAP {
-		len_0 := uint16(len(k))
-		if len(k) != int(len_0) {
-			return n, errors.New("field k is too long")
-		}
-		binary.LittleEndian.PutUint16(buf[n:], len_0)
-		n += 2
-		copy(buf[n:n+len(k)], k)
-		n += len(k)
-		n_1, err := v.MarshalZeroPB(buf[n:])
-		n += n_1
-		if err != nil {
-			return n, err
-		}
-	}
-	len_18 := uint16(len(x.LIST))
-	if len(x.LIST) != int(len_18) {
-		return n, errors.New("field LIST is too long")
-	}
-	binary.LittleEndian.PutUint16(buf[n:], len_18)
-	n += 2
-	for _, e := range x.LIST {
-		n_18, err := e.MarshalZeroPB(buf[n:])
-		n += n_18
-		if err != nil {
-			return n, err
+	binary.LittleEndian.PutUint32(buf.Buf[4:], bool_1)
+	binary.LittleEndian.PutUint32(buf.Buf[8:], uint32(x.INT32))
+	binary.LittleEndian.PutUint32(buf.Buf[12:], uint32(x.SINT32))
+	binary.LittleEndian.PutUint32(buf.Buf[16:], uint32(x.UINT32))
+	binary.LittleEndian.PutUint64(buf.Buf[20:], uint64(x.INT64))
+	binary.LittleEndian.PutUint64(buf.Buf[28:], uint64(x.SING64))
+	binary.LittleEndian.PutUint64(buf.Buf[36:], uint64(x.UINT64))
+	binary.LittleEndian.PutUint32(buf.Buf[44:], uint32(x.SFIXED32))
+	binary.LittleEndian.PutUint32(buf.Buf[48:], uint32(x.FIXED32))
+	binary.LittleEndian.PutUint32(buf.Buf[52:], math.Float32bits(x.FLOAT))
+	binary.LittleEndian.PutUint64(buf.Buf[56:], uint64(x.SFIXED64))
+	binary.LittleEndian.PutUint64(buf.Buf[64:], uint64(x.FIXED64))
+	binary.LittleEndian.PutUint64(buf.Buf[72:], math.Float64bits(x.DOUBLE))
+	buf_14 := b.AllocRel(len(x.STRING), buf, 80, uint16(len(x.STRING)))
+	copy(buf_14.Buf, x.STRING)
+	buf_15 := b.AllocRel(len(x.BYTES), buf, 84, uint16(len(x.BYTES)))
+	copy(buf_15.Buf, x.BYTES)
+	x.MESSAGE.marshalZeroPB(b, buf.Slice(88))
+	buf_17 := b.AllocRel(len(x.MAP)*8, buf, 92, uint16(len(x.MAP)))
+	{
+		var n uint16
+		buf := buf_17
+		for k, v := range x.MAP {
+			buf_0 := b.AllocRel(len(k), buf, n, uint16(len(k)))
+			copy(buf_0.Buf, k)
+			n += 4
+			v.marshalZeroPB(b, buf.Slice(n))
+			n += 4
+		}
+	}
+	buf_18 := b.AllocRel(len(x.LIST)*4+4, buf, 96, uint16(len(x.LIST)))
+	{
+		buf := buf_18
+		buf.Buf[0] = byte(len(x.LIST))
+		buf.Buf[1] = byte(len(x.LIST))
+		binary.LittleEndian.PutUint16(buf.Buf[2:], 0)
+		for i, e := range x.LIST {
+			e.marshalZeroPB(b, buf.Slice(uint16(i)*4+4))
 		}
 	}
 	// TODO: field ONEOF_B
 	// TODO: field ONEOF_STRING
-	len_21 := uint16(len(x.LIST_ENUM))
-	if len(x.LIST_ENUM) != int(len_21) {
-		return n, errors.New("field LIST_ENUM is too long")
-	}
-	binary.LittleEndian.PutUint16(buf[n:], len_21)
-	n += 2
-	for _, e := range x.LIST_ENUM {
-		binary.LittleEndian.PutUint32(buf[n:], uint32(e))
-		n += 4
-	}
-	n_22, err := x.Imported.MarshalZeroPB(buf[n:])
-	n += n_22
-	if err != nil {
-		return n, err
-	}
-	len_23 := uint16(len(x.Type_))
-	if len(x.Type_) != int(len_23) {
-		return n, errors.New("field x.Type_ is too long")
-	}
-	binary.LittleEndian.PutUint16(buf[n:], len_23)
-	n += 2
-	copy(buf[n:n+len(x.Type_)], x.Type_)
-	n += len(x.Type_)
-	return n, nil
-}
-func (x *A) UnmarshalZeroPB(buf []byte) (n int, err error) {
+	buf_21 := b.AllocRel(len(x.LIST_ENUM)*4+4, buf, 108, uint16(len(x.LIST_ENUM)))
+	{
+		buf := buf_21
+		buf.Buf[0] = byte(len(x.LIST_ENUM))
+		buf.Buf[1] = byte(len(x.LIST_ENUM))
+		binary.LittleEndian.PutUint16(buf.Buf[2:], 0)
+		for i, e := range x.LIST_ENUM {
+			binary.LittleEndian.PutUint32(buf.Buf[uint16(i)*4+4:], uint32(e))
+		}
+	}
+	x.Imported.marshalZeroPB(b, buf.Slice(112))
+	buf_23 := b.AllocRel(len(x.Type_), buf, 112, uint16(len(x.Type_)))
+	copy(buf_23.Buf, x.Type_)
+}
+func (x *A) UnmarshalZeroPB(buf []byte) (err error) {
 	defer func() {
 		if e := recover(); e != nil {
 			err = errors.New("buffer underflow")
 		}
 	}()
-	x.Enum = Enumeration(binary.LittleEndian.Uint32(buf[n:]))
-	n += 4
-	bool_1 := binary.LittleEndian.Uint32(buf[n:])
+	x.unmarshalZeroPB(buf, 0)
+	return nil
+}
+
+func (x *A) unmarshalZeroPB(buf []byte, n uint16) {
+	x.Enum = Enumeration(binary.LittleEndian.Uint32(buf[n+0:]))
+	bool_1 := binary.LittleEndian.Uint32(buf[n+4:])
 	x.SomeBoolean = false
 	if bool_1 != 0 {
 		x.SomeBoolean = true
 	}
-	n += 4
-	x.INT32 = int32(binary.LittleEndian.Uint32(buf[n:]))
-	n += 4
-	x.SINT32 = int32(binary.LittleEndian.Uint32(buf[n:]))
-	n += 4
-	x.UINT32 = binary.LittleEndian.Uint32(buf[n:])
-	n += 4
-	x.INT64 = int64(binary.LittleEndian.Uint64(buf[n:]))
-	n += 8
-	x.SING64 = int64(binary.LittleEndian.Uint64(buf[n:]))
-	n += 8
-	x.UINT64 = binary.LittleEndian.Uint64(buf[n:])
-	n += 8
-	x.SFIXED32 = int32(binary.LittleEndian.Uint32(buf[n:]))
-	n += 4
-	x.FIXED32 = binary.LittleEndian.Uint32(buf[n:])
-	n += 4
-	x.FLOAT = float32(math.Float32frombits(binary.LittleEndian.Uint32(buf[n:])))
-	n += 4
-	x.SFIXED64 = int64(binary.LittleEndian.Uint64(buf[n:]))
-	n += 8
-	x.FIXED64 = binary.LittleEndian.Uint64(buf[n:])
-	n += 8
-	x.DOUBLE = float64(math.Float64frombits(binary.LittleEndian.Uint64(buf[n:])))
-	n += 8
-	len_14 := int(binary.LittleEndian.Uint16(buf[n:]))
-	n += 2
-	x.STRING = string(buf[n : n+len_14])
-	n += len_14
-	len_15 := int(binary.LittleEndian.Uint16(buf[n:]))
-	n += 2
-	x.BYTES = append([]byte{}, buf[n:n+len_15]...)
-	n += len_15
+	x.INT32 = int32(binary.LittleEndian.Uint32(buf[n+8:]))
+	x.SINT32 = int32(binary.LittleEndian.Uint32(buf[n+12:]))
+	x.UINT32 = binary.LittleEndian.Uint32(buf[n+16:])
+	x.INT64 = int64(binary.LittleEndian.Uint64(buf[n+20:]))
+	x.SING64 = int64(binary.LittleEndian.Uint64(buf[n+28:]))
+	x.UINT64 = binary.LittleEndian.Uint64(buf[n+36:])
+	x.SFIXED32 = int32(binary.LittleEndian.Uint32(buf[n+44:]))
+	x.FIXED32 = binary.LittleEndian.Uint32(buf[n+48:])
+	x.FLOAT = float32(math.Float32frombits(binary.LittleEndian.Uint32(buf[n+52:])))
+	x.SFIXED64 = int64(binary.LittleEndian.Uint64(buf[n+56:]))
+	x.FIXED64 = binary.LittleEndian.Uint64(buf[n+64:])
+	x.DOUBLE = float64(math.Float64frombits(binary.LittleEndian.Uint64(buf[n+72:])))
+	n_14, len_14 := zeropb.ReadSlice(buf, n+80)
+	x.STRING = string(buf[n_14 : n_14+len_14])
+	n_15, len_15 := zeropb.ReadSlice(buf, n+84)
+	x.BYTES = append([]byte{}, buf[n_15:n_15+len_15]...)
 	x.MESSAGE = new(B)
-	n_16, err := x.MESSAGE.UnmarshalZeroPB(buf[n:])
-	n += n_16
-	if err != nil {
-		return n, err
-	}
-	len_17 := int(binary.LittleEndian.Uint16(buf[n:]))
-	n += 2
+	x.MESSAGE.unmarshalZeroPB(buf, n+88)
+	n_17, len_17 := zeropb.ReadSlice(buf, n+92)
 	x.MAP = make(map[string]*B, len_17)
-	for i := 0; i < len_17; i++ {
-		var k string
-		var v *B
-		len_0 := int(binary.LittleEndian.Uint16(buf[n:]))
-		n += 2
-		k = string(buf[n : n+len_0])
-		n += len_0
-		v = new(B)
-		n_1, err := v.UnmarshalZeroPB(buf[n:])
-		n += n_1
-		if err != nil {
-			return n, err
-		}
-		x.MAP[k] = v
-	}
-	len_18 := int(binary.LittleEndian.Uint16(buf[n:]))
-	n += 2
+	{
+		n := n_17
+		for i := uint16(0); i < len_17; i++ {
+			var k string
+			var v *B
+			n_0, len_0 := zeropb.ReadSlice(buf, n)
+			k = string(buf[n_0 : n_0+len_0])
+			n += 4
+			v = new(B)
+			v.unmarshalZeroPB(buf, n)
+			n += 4
+			x.MAP[k] = v
+		}
+	}
+	n_18, len_18 := zeropb.ReadSlice(buf, n+96)
 	x.LIST = make([]*B, len_18)
-	for i := range x.LIST {
-		x.LIST[i] = new(B)
-		n_18, err := x.LIST[i].UnmarshalZeroPB(buf[n:])
-		n += n_18
-		if err != nil {
-			return n, err
+	{
+		for i := range x.LIST {
+			x.LIST[i] = new(B)
+			x.LIST[i].unmarshalZeroPB(buf, n_18+4+uint16(i)*4)
 		}
 	}
 	// TODO: field ONEOF_B
 	// TODO: field ONEOF_STRING
-	len_21 := int(binary.LittleEndian.Uint16(buf[n:]))
-	n += 2
+	n_21, len_21 := zeropb.ReadSlice(buf, n+108)
 	x.LIST_ENUM = make([]Enumeration, len_21)
-	for i := range x.LIST_ENUM {
-		x.LIST_ENUM[i] = Enumeration(binary.LittleEndian.Uint32(buf[n:]))
-		n += 4
+	{
+		for i := range x.LIST_ENUM {
+			x.LIST_ENUM[i] = Enumeration(binary.LittleEndian.Uint32(buf[n_21+4+uint16(i)*4:]))
+		}
 	}
 	x.Imported = new(ImportedMessage)
-	n_22, err := x.Imported.UnmarshalZeroPB(buf[n:])
-	n += n_22
-	if err != nil {
-		return n, err
-	}
-	len_23 := int(binary.LittleEndian.Uint16(buf[n:]))
-	n += 2
-	x.Type_ = string(buf[n : n+len_23])
-	n += len_23
-	return n, nil
+	x.Imported.unmarshalZeroPB(buf, n+112)
+	n_23, len_23 := zeropb.ReadSlice(buf, n+112)
+	x.Type_ = string(buf[n_23 : n_23+len_23])
 }
 func (x *B) MarshalZeroPB(buf []byte) (n int, err error) {
 	defer func() {
@@ -3353,25 +3278,26 @@ func (x *B) MarshalZeroPB(buf []byte) (n int, err error) {
 			err = errors.New("buffer overflow")
 		}
 	}()
-	len_0 := uint16(len(x.X))
-	if len(x.X) != int(len_0) {
-		return n, errors.New("field x.X is too long")
-	}
-	binary.LittleEndian.PutUint16(buf[n:], len_0)
-	n += 2
-	copy(buf[n:n+len(x.X)], x.X)
-	n += len(x.X)
-	return n, nil
+	b := zeropb.NewBuffer(buf)
+	x.marshalZeroPB(b, b.Alloc(4))
+	return int(b.Allocated()), nil
+}
+
+func (x *B) marshalZeroPB(b *zeropb.Buffer, buf zeropb.Allocation) {
+	buf_0 := b.AllocRel(len(x.X), buf, 0, uint16(len(x.X)))
+	copy(buf_0.Buf, x.X)
 }
-func (x *B) UnmarshalZeroPB(buf []byte) (n int, err error) {
+func (x *B) UnmarshalZeroPB(buf []byte) (err error) {
 	defer func() {
 		if e := recover(); e != nil {
 			err = errors.New("buffer underflow")
 		}
 	}()
-	len_0 := int(binary.LittleEndian.Uint16(buf[n:]))
-	n += 2
-	x.X = string(buf[n : n+len_0])
-	n += len_0
-	return n, nil
+	x.unmarshalZeroPB(buf, 0)
+	return nil
+}
+
+func (x *B) unmarshalZeroPB(buf []byte, n uint16) {
+	n_0, len_0 := zeropb.ReadSlice(buf, n+0)
+	x.X = string(buf[n_0 : n_0+len_0])
 }
diff --git a/testpb/2.pulsar.go b/testpb/2.pulsar.go
index 031be62..acb55f5 100644
--- a/testpb/2.pulsar.go
+++ b/testpb/2.pulsar.go
@@ -5,6 +5,7 @@ import (
 	errors "errors"
 	fmt "fmt"
 	runtime "github.com/cosmos/cosmos-proto/runtime"
+	zeropb "github.com/cosmos/cosmos-proto/runtime/zeropb"
 	protoreflect "google.golang.org/protobuf/reflect/protoreflect"
 	protoiface "google.golang.org/protobuf/runtime/protoiface"
 	protoimpl "google.golang.org/protobuf/runtime/protoimpl"
@@ -487,13 +488,22 @@ func (x *ImportedMessage) MarshalZeroPB(buf []byte) (n int, err error) {
 			err = errors.New("buffer overflow")
 		}
 	}()
-	return n, nil
+	b := zeropb.NewBuffer(buf)
+	x.marshalZeroPB(b, b.Alloc(0))
+	return int(b.Allocated()), nil
 }
-func (x *ImportedMessage) UnmarshalZeroPB(buf []byte) (n int, err error) {
+
+func (x *ImportedMessage) marshalZeroPB(b *zeropb.Buffer, buf zeropb.Allocation) {
+}
+func (x *ImportedMessage) UnmarshalZeroPB(buf []byte) (err error) {
 	defer func() {
 		if e := recover(); e != nil {
 			err = errors.New("buffer underflow")
 		}
 	}()
-	return n, nil
+	x.unmarshalZeroPB(buf, 0)
+	return nil
+}
+
+func (x *ImportedMessage) unmarshalZeroPB(buf []byte, n uint16) {
 }
diff --git a/testpb/codec_test.go b/testpb/codec_test.go
index b79d39a..541ee6a 100644
--- a/testpb/codec_test.go
+++ b/testpb/codec_test.go
@@ -72,9 +72,8 @@ func TestMarshal(t *testing.T) {
 	n, err := testA.MarshalZeroPB(zpbBuf)
 	require.NoError(t, err)
 	var msg2 A
-	n2, err := msg2.UnmarshalZeroPB(zpbBuf[:n])
+	err = msg2.UnmarshalZeroPB(zpbBuf[:n])
 	require.NoError(t, err)
-	require.Equal(t, n, n2)
 	zpbGot, err := proto.MarshalOptions{Deterministic: true}.Marshal(&msg2)
 	require.NoError(t, err)
 	require.Equal(t, expected, zpbGot)

From eca656e5647e50027fe1abdd24936fd7206996af Mon Sep 17 00:00:00 2001
From: Elias Naur <elias@orijtech.com>
Date: Fri, 1 Dec 2023 08:49:38 -0600
Subject: [PATCH 6/9] wip one-alloc unmarshal

---
 features/protoc/main.go   | 25 ++++++++++-----
 features/zeropb/zeropb.go | 52 +++++++++++++++++++++++--------
 go.mod                    |  2 +-
 runtime/zeropb/zeropb.go  |  9 ++----
 testpb/1.pulsar.go        | 64 +++++++++++++++++++++++++++------------
 testpb/2.pulsar.go        |  6 ++--
 6 files changed, 109 insertions(+), 49 deletions(-)

diff --git a/features/protoc/main.go b/features/protoc/main.go
index e7d8949..62aad90 100644
--- a/features/protoc/main.go
+++ b/features/protoc/main.go
@@ -1683,6 +1683,23 @@ func genMessageSetterMethods(g *generator.GeneratedFile, f *fileInfo, m *message
 //
 // If it returns pointer=true, the struct field is a pointer to the type.
 func FieldGoType(g *generator.GeneratedFile, field *protogen.Field) (goType string, pointer bool) {
+	switch {
+	case field.Desc.IsList():
+		typ, _ := FieldGoElemType(g, field)
+		return "[]" + typ, false
+	case field.Desc.IsMap():
+		keyType, _ := FieldGoType(g, field.Message.Fields[0])
+		valType, _ := FieldGoType(g, field.Message.Fields[1])
+		return fmt.Sprintf("map[%v]%v", keyType, valType), false
+	}
+	return FieldGoElemType(g, field)
+}
+
+// FieldElemGoType returns the Go element type used for a field. It is not valid for
+// map fields.
+//
+// If it returns pointer=true, the struct field is a pointer to the type.
+func FieldGoElemType(g *generator.GeneratedFile, field *protogen.Field) (goType string, pointer bool) {
 	if field.Desc.IsWeak() {
 		return "struct{}", false
 	}
@@ -1714,14 +1731,6 @@ func FieldGoType(g *generator.GeneratedFile, field *protogen.Field) (goType stri
 		goType = "*" + g.QualifiedGoIdent(field.Message.GoIdent)
 		pointer = false // pointer captured as part of the type
 	}
-	switch {
-	case field.Desc.IsList():
-		return "[]" + goType, false
-	case field.Desc.IsMap():
-		keyType, _ := FieldGoType(g, field.Message.Fields[0])
-		valType, _ := FieldGoType(g, field.Message.Fields[1])
-		return fmt.Sprintf("map[%v]%v", keyType, valType), false
-	}
 	return goType, pointer
 }
 
diff --git a/features/zeropb/zeropb.go b/features/zeropb/zeropb.go
index 45da39c..4ace5ba 100644
--- a/features/zeropb/zeropb.go
+++ b/features/zeropb/zeropb.go
@@ -14,6 +14,7 @@ const (
 	errorsPackage = protogen.GoImportPath("errors")
 	mathPackage   = protogen.GoImportPath("math")
 	binaryPackage = protogen.GoImportPath("encoding/binary")
+	unsafePackage = protogen.GoImportPath("unsafe")
 
 	runtimePackage = protogen.GoImportPath("github.com/cosmos/cosmos-proto/runtime/zeropb")
 )
@@ -178,6 +179,8 @@ func (g zeropbFeature) generateMarshalPrimitive(f *protogen.Field, name, offset
 	}
 }
 
+const unsafeOptimizations = true
+
 func (g zeropbFeature) generateUnmarshal(m *protogen.Message) {
 	g.gen.P("func (x *", m.GoIdent, ") UnmarshalZeroPB(buf []byte) (err error) {")
 	g.gen.P("    defer func() {")
@@ -185,11 +188,15 @@ func (g zeropbFeature) generateUnmarshal(m *protogen.Message) {
 	g.gen.P("            err = ", errorsPackage.Ident("New"), `("buffer underflow")`)
 	g.gen.P("        }")
 	g.gen.P("    }()")
-	g.gen.P("    x.unmarshalZeroPB(buf, 0)")
+	g.gen.P("    var mem []byte")
+	if unsafeOptimizations {
+		g.gen.P("mem = make([]byte, 251)")
+	}
+	g.gen.P("    x.unmarshalZeroPB(buf, 0, ", runtimePackage.Ident("NewBuffer"), "(mem))")
 	g.gen.P("    return nil")
 	g.gen.P("}")
 	g.gen.P()
-	g.gen.P("func (x *", m.GoIdent, ") unmarshalZeroPB(buf []byte, n uint16) {")
+	g.gen.P("func (x *", m.GoIdent, ") unmarshalZeroPB(buf []byte, n uint16, mem *", runtimePackage.Ident("Buffer"), ") {")
 	offset := 0
 	for _, f := range m.Fields {
 		g.generateUnmarshalField(f, offset)
@@ -203,11 +210,13 @@ func (g zeropbFeature) generateUnmarshalField(f *protogen.Field, offset int) {
 	switch {
 	case d.IsList():
 		g.gen.P("n_", d.Index(), ", len_", d.Index(), " := ", runtimePackage.Ident("ReadSlice"), "(buf, n+", offset, ")")
-		typ, pointer := protoc.FieldGoType(g.gen, f)
-		if pointer {
-			typ = "*" + typ
+		typ, _ := protoc.FieldGoElemType(g.gen, f)
+		if unsafeOptimizations {
+			g.gen.P("mem_", d.Index(), " := mem.Alloc(int(len_", d.Index(), ")*int(", unsafePackage.Ident("Sizeof"), "(x.", f.GoName, "[0])))")
+			g.gen.P("x.", f.GoName, " = ", unsafePackage.Ident("Slice"), "((*", typ, ")(", unsafePackage.Ident("Pointer"), "(", unsafePackage.Ident("SliceData"), "(mem_", d.Index(), ".Buf))), len_", d.Index(), ")")
+		} else {
+			g.gen.P("x.", f.GoName, " = make([]", typ, ", len_", d.Index(), ")")
 		}
-		g.gen.P("x.", f.GoName, " = make(", typ, ", len_", d.Index(), ")")
 		g.gen.P("{")
 		g.gen.P("    for i := range x.", f.GoName, "{")
 		// Skip segment header.
@@ -262,16 +271,33 @@ func (g zeropbFeature) generateUnmarshalPrimitive(f *protogen.Field, name, offse
 		g.gen.P("if bool_", d.Index(), " != 0 {")
 		g.gen.P("    ", name, " = true")
 		g.gen.P("}")
-	case protoreflect.StringKind:
-		g.gen.P("n_", d.Index(), ", len_", d.Index(), " := ", runtimePackage.Ident("ReadSlice"), "(buf, ", offset, ")")
-		g.gen.P(name, " = string(buf[n_", d.Index(), ":n_", d.Index(), "+len_", d.Index(), "])")
-	case protoreflect.BytesKind:
+	case protoreflect.StringKind, protoreflect.BytesKind:
 		g.gen.P("n_", d.Index(), ", len_", d.Index(), " := ", runtimePackage.Ident("ReadSlice"), "(buf, ", offset, ")")
-		g.gen.P(name, " = append([]byte{}, buf[n_", d.Index(), ":n_", d.Index(), "+len_", d.Index(), "]...)")
+		g.gen.P("buf_", d.Index(), " := buf[n_", d.Index(), ":n_", d.Index(), "+len_", d.Index(), "]")
+		if unsafeOptimizations {
+			g.gen.P("mem_", d.Index(), " := mem.Alloc(int(len_", d.Index(), ")*int(", unsafePackage.Ident("Sizeof"), "(", name, "[0])))")
+			g.gen.P("copy(mem_", d.Index(), ".Buf, buf_", d.Index(), ")")
+			if d.Kind() == protoreflect.BytesKind {
+				g.gen.P(name, " = mem_", d.Index(), ".Buf")
+			} else {
+				g.gen.P(name, " = ", unsafePackage.Ident("String"), "(unsafe.SliceData(mem_", d.Index(), ".Buf), len(mem_", d.Index(), ".Buf))")
+			}
+		} else {
+			if d.Kind() == protoreflect.BytesKind {
+				g.gen.P(name, " = append([]byte{}, buf_", d.Index(), "...)")
+			} else {
+				g.gen.P(name, " = string(buf_", d.Index(), ")")
+			}
+		}
 	case protoreflect.MessageKind:
 		typ := g.gen.QualifiedGoIdent(f.Message.GoIdent)
-		g.gen.P(name, " = new(", typ, ")")
-		g.gen.P(name, ".unmarshalZeroPB(buf, ", offset, ")")
+		if unsafeOptimizations {
+			g.gen.P("mem_", d.Index(), " := mem.Alloc(int(", unsafePackage.Ident("Sizeof"), "(*", name, ")))")
+			g.gen.P(name, " = (*", typ, ")(", unsafePackage.Ident("Pointer"), "(", unsafePackage.Ident("SliceData"), "(mem_", d.Index(), ".Buf)))")
+		} else {
+			g.gen.P(name, " = new(", typ, ")")
+		}
+		g.gen.P(name, ".unmarshalZeroPB(buf, ", offset, ", mem)")
 	default:
 		g.gen.P("// TODO: field ", name)
 	}
diff --git a/go.mod b/go.mod
index e78f2bf..f68ef66 100644
--- a/go.mod
+++ b/go.mod
@@ -1,6 +1,6 @@
 module github.com/cosmos/cosmos-proto
 
-go 1.18
+go 1.20
 
 require (
 	github.com/google/go-cmp v0.6.0
diff --git a/runtime/zeropb/zeropb.go b/runtime/zeropb/zeropb.go
index b3f502e..ee7e691 100644
--- a/runtime/zeropb/zeropb.go
+++ b/runtime/zeropb/zeropb.go
@@ -2,7 +2,6 @@ package zeropb
 
 import (
 	"encoding/binary"
-	"fmt"
 	"math"
 )
 
@@ -26,14 +25,12 @@ func NewBuffer(b []byte) *Buffer {
 }
 
 func (b *Buffer) Alloc(n int) Allocation {
-	n16 := uint16(n)
-	if int(n16) != n {
-		panic(fmt.Errorf("allocation %d too large", n))
-	}
 	a := Allocation{
-		Buf:    b.buf[:n16],
+		Buf:    b.buf[:n],
 		offset: b.allocated,
 	}
+	// The slice above would have paniced if n would not fit a uint16.
+	n16 := uint16(n)
 	b.buf = b.buf[n16:]
 	b.allocated += n16
 	return a
diff --git a/testpb/1.pulsar.go b/testpb/1.pulsar.go
index 249822b..56b109b 100644
--- a/testpb/1.pulsar.go
+++ b/testpb/1.pulsar.go
@@ -15,6 +15,7 @@ import (
 	reflect "reflect"
 	sort "sort"
 	sync "sync"
+	unsafe "unsafe"
 )
 
 var _ protoreflect.Map = (*_A_18_map)(nil)
@@ -3205,11 +3206,13 @@ func (x *A) UnmarshalZeroPB(buf []byte) (err error) {
 			err = errors.New("buffer underflow")
 		}
 	}()
-	x.unmarshalZeroPB(buf, 0)
+	var mem []byte
+	mem = make([]byte, 251)
+	x.unmarshalZeroPB(buf, 0, zeropb.NewBuffer(mem))
 	return nil
 }
 
-func (x *A) unmarshalZeroPB(buf []byte, n uint16) {
+func (x *A) unmarshalZeroPB(buf []byte, n uint16, mem *zeropb.Buffer) {
 	x.Enum = Enumeration(binary.LittleEndian.Uint32(buf[n+0:]))
 	bool_1 := binary.LittleEndian.Uint32(buf[n+4:])
 	x.SomeBoolean = false
@@ -3229,11 +3232,18 @@ func (x *A) unmarshalZeroPB(buf []byte, n uint16) {
 	x.FIXED64 = binary.LittleEndian.Uint64(buf[n+64:])
 	x.DOUBLE = float64(math.Float64frombits(binary.LittleEndian.Uint64(buf[n+72:])))
 	n_14, len_14 := zeropb.ReadSlice(buf, n+80)
-	x.STRING = string(buf[n_14 : n_14+len_14])
+	buf_14 := buf[n_14 : n_14+len_14]
+	mem_14 := mem.Alloc(int(len_14) * int(unsafe.Sizeof(x.STRING[0])))
+	copy(mem_14.Buf, buf_14)
+	x.STRING = unsafe.String(unsafe.SliceData(mem_14.Buf), len(mem_14.Buf))
 	n_15, len_15 := zeropb.ReadSlice(buf, n+84)
-	x.BYTES = append([]byte{}, buf[n_15:n_15+len_15]...)
-	x.MESSAGE = new(B)
-	x.MESSAGE.unmarshalZeroPB(buf, n+88)
+	buf_15 := buf[n_15 : n_15+len_15]
+	mem_15 := mem.Alloc(int(len_15) * int(unsafe.Sizeof(x.BYTES[0])))
+	copy(mem_15.Buf, buf_15)
+	x.BYTES = mem_15.Buf
+	mem_16 := mem.Alloc(int(unsafe.Sizeof(*x.MESSAGE)))
+	x.MESSAGE = (*B)(unsafe.Pointer(unsafe.SliceData(mem_16.Buf)))
+	x.MESSAGE.unmarshalZeroPB(buf, n+88, mem)
 	n_17, len_17 := zeropb.ReadSlice(buf, n+92)
 	x.MAP = make(map[string]*B, len_17)
 	{
@@ -3242,35 +3252,46 @@ func (x *A) unmarshalZeroPB(buf []byte, n uint16) {
 			var k string
 			var v *B
 			n_0, len_0 := zeropb.ReadSlice(buf, n)
-			k = string(buf[n_0 : n_0+len_0])
+			buf_0 := buf[n_0 : n_0+len_0]
+			mem_0 := mem.Alloc(int(len_0) * int(unsafe.Sizeof(k[0])))
+			copy(mem_0.Buf, buf_0)
+			k = unsafe.String(unsafe.SliceData(mem_0.Buf), len(mem_0.Buf))
 			n += 4
-			v = new(B)
-			v.unmarshalZeroPB(buf, n)
+			mem_1 := mem.Alloc(int(unsafe.Sizeof(*v)))
+			v = (*B)(unsafe.Pointer(unsafe.SliceData(mem_1.Buf)))
+			v.unmarshalZeroPB(buf, n, mem)
 			n += 4
 			x.MAP[k] = v
 		}
 	}
 	n_18, len_18 := zeropb.ReadSlice(buf, n+96)
-	x.LIST = make([]*B, len_18)
+	mem_18 := mem.Alloc(int(len_18) * int(unsafe.Sizeof(x.LIST[0])))
+	x.LIST = unsafe.Slice((**B)(unsafe.Pointer(unsafe.SliceData(mem_18.Buf))), len_18)
 	{
 		for i := range x.LIST {
-			x.LIST[i] = new(B)
-			x.LIST[i].unmarshalZeroPB(buf, n_18+4+uint16(i)*4)
+			mem_18 := mem.Alloc(int(unsafe.Sizeof(*x.LIST[i])))
+			x.LIST[i] = (*B)(unsafe.Pointer(unsafe.SliceData(mem_18.Buf)))
+			x.LIST[i].unmarshalZeroPB(buf, n_18+4+uint16(i)*4, mem)
 		}
 	}
 	// TODO: field ONEOF_B
 	// TODO: field ONEOF_STRING
 	n_21, len_21 := zeropb.ReadSlice(buf, n+108)
-	x.LIST_ENUM = make([]Enumeration, len_21)
+	mem_21 := mem.Alloc(int(len_21) * int(unsafe.Sizeof(x.LIST_ENUM[0])))
+	x.LIST_ENUM = unsafe.Slice((*Enumeration)(unsafe.Pointer(unsafe.SliceData(mem_21.Buf))), len_21)
 	{
 		for i := range x.LIST_ENUM {
 			x.LIST_ENUM[i] = Enumeration(binary.LittleEndian.Uint32(buf[n_21+4+uint16(i)*4:]))
 		}
 	}
-	x.Imported = new(ImportedMessage)
-	x.Imported.unmarshalZeroPB(buf, n+112)
+	mem_22 := mem.Alloc(int(unsafe.Sizeof(*x.Imported)))
+	x.Imported = (*ImportedMessage)(unsafe.Pointer(unsafe.SliceData(mem_22.Buf)))
+	x.Imported.unmarshalZeroPB(buf, n+112, mem)
 	n_23, len_23 := zeropb.ReadSlice(buf, n+112)
-	x.Type_ = string(buf[n_23 : n_23+len_23])
+	buf_23 := buf[n_23 : n_23+len_23]
+	mem_23 := mem.Alloc(int(len_23) * int(unsafe.Sizeof(x.Type_[0])))
+	copy(mem_23.Buf, buf_23)
+	x.Type_ = unsafe.String(unsafe.SliceData(mem_23.Buf), len(mem_23.Buf))
 }
 func (x *B) MarshalZeroPB(buf []byte) (n int, err error) {
 	defer func() {
@@ -3293,11 +3314,16 @@ func (x *B) UnmarshalZeroPB(buf []byte) (err error) {
 			err = errors.New("buffer underflow")
 		}
 	}()
-	x.unmarshalZeroPB(buf, 0)
+	var mem []byte
+	mem = make([]byte, 251)
+	x.unmarshalZeroPB(buf, 0, zeropb.NewBuffer(mem))
 	return nil
 }
 
-func (x *B) unmarshalZeroPB(buf []byte, n uint16) {
+func (x *B) unmarshalZeroPB(buf []byte, n uint16, mem *zeropb.Buffer) {
 	n_0, len_0 := zeropb.ReadSlice(buf, n+0)
-	x.X = string(buf[n_0 : n_0+len_0])
+	buf_0 := buf[n_0 : n_0+len_0]
+	mem_0 := mem.Alloc(int(len_0) * int(unsafe.Sizeof(x.X[0])))
+	copy(mem_0.Buf, buf_0)
+	x.X = unsafe.String(unsafe.SliceData(mem_0.Buf), len(mem_0.Buf))
 }
diff --git a/testpb/2.pulsar.go b/testpb/2.pulsar.go
index acb55f5..5e12ed8 100644
--- a/testpb/2.pulsar.go
+++ b/testpb/2.pulsar.go
@@ -501,9 +501,11 @@ func (x *ImportedMessage) UnmarshalZeroPB(buf []byte) (err error) {
 			err = errors.New("buffer underflow")
 		}
 	}()
-	x.unmarshalZeroPB(buf, 0)
+	var mem []byte
+	mem = make([]byte, 251)
+	x.unmarshalZeroPB(buf, 0, zeropb.NewBuffer(mem))
 	return nil
 }
 
-func (x *ImportedMessage) unmarshalZeroPB(buf []byte, n uint16) {
+func (x *ImportedMessage) unmarshalZeroPB(buf []byte, n uint16, mem *zeropb.Buffer) {
 }

From 8119ed2342c7c38e5d6fcf018341f2f86d6af3df Mon Sep 17 00:00:00 2001
From: Elias Naur <elias@orijtech.com>
Date: Fri, 1 Dec 2023 10:25:44 -0600
Subject: [PATCH 7/9] wip one-alloc unmarshal

---
 features/zeropb/zeropb.go | 67 +++++++++++++++++++++++++++++++++------
 testpb/1.pulsar.go        | 67 ++++++++++++++++++++++++++++++++-------
 testpb/2.pulsar.go        |  6 +++-
 3 files changed, 117 insertions(+), 23 deletions(-)

diff --git a/features/zeropb/zeropb.go b/features/zeropb/zeropb.go
index 4ace5ba..b2b8798 100644
--- a/features/zeropb/zeropb.go
+++ b/features/zeropb/zeropb.go
@@ -19,6 +19,8 @@ const (
 	runtimePackage = protogen.GoImportPath("github.com/cosmos/cosmos-proto/runtime/zeropb")
 )
 
+const unsafeOptimizations = true
+
 func init() {
 	generator.RegisterFeature("zeropb", func(gen *generator.GeneratedFile, _ *protogen.Plugin) generator.FeatureGenerator {
 		return zeropbFeature{
@@ -179,8 +181,6 @@ func (g zeropbFeature) generateMarshalPrimitive(f *protogen.Field, name, offset
 	}
 }
 
-const unsafeOptimizations = true
-
 func (g zeropbFeature) generateUnmarshal(m *protogen.Message) {
 	g.gen.P("func (x *", m.GoIdent, ") UnmarshalZeroPB(buf []byte) (err error) {")
 	g.gen.P("    defer func() {")
@@ -190,14 +190,23 @@ func (g zeropbFeature) generateUnmarshal(m *protogen.Message) {
 	g.gen.P("    }()")
 	g.gen.P("    var mem []byte")
 	if unsafeOptimizations {
-		g.gen.P("mem = make([]byte, 251)")
+		g.gen.P("memSize := _", m.GoIdent, "UnmarshalZeroPBSize(buf, 0)")
+		g.gen.P("mem = make([]byte, memSize)")
 	}
 	g.gen.P("    x.unmarshalZeroPB(buf, 0, ", runtimePackage.Ident("NewBuffer"), "(mem))")
 	g.gen.P("    return nil")
 	g.gen.P("}")
 	g.gen.P()
-	g.gen.P("func (x *", m.GoIdent, ") unmarshalZeroPB(buf []byte, n uint16, mem *", runtimePackage.Ident("Buffer"), ") {")
+	g.gen.P("func _", m.GoIdent, "UnmarshalZeroPBSize(buf []byte, n uint16) (size uint16) {")
 	offset := 0
+	for _, f := range m.Fields {
+		g.generateUnmarshalFieldSize(f, m.GoIdent, offset)
+		offset += fieldSize(f)
+	}
+	g.gen.P("    return")
+	g.gen.P("}")
+	g.gen.P("func (x *", m.GoIdent, ") unmarshalZeroPB(buf []byte, n uint16, mem *", runtimePackage.Ident("Buffer"), ") {")
+	offset = 0
 	for _, f := range m.Fields {
 		g.generateUnmarshalField(f, offset)
 		offset += fieldSize(f)
@@ -205,6 +214,47 @@ func (g zeropbFeature) generateUnmarshal(m *protogen.Message) {
 	g.gen.P("}")
 }
 
+func (g zeropbFeature) generateUnmarshalFieldSize(f *protogen.Field, typ protogen.GoIdent, offset int) {
+	d := f.Desc
+	switch {
+	case d.IsList():
+		g.gen.P("n_", d.Index(), ", len_", d.Index(), " := ", runtimePackage.Ident("ReadSlice"), "(buf, n+", offset, ")")
+		g.gen.P("_ = n_", d.Index())
+		g.gen.P("size += len_", d.Index(), "*uint16(", unsafePackage.Ident("Sizeof"), "((", typ, "{}).", f.GoName, "[0]))")
+		g.gen.P("for i := uint16(0); i < len_", d.Index(), "; i++ {")
+		// Skip segment header.
+		g.generateUnmarshalPrimitiveSize(f, fmt.Sprintf("n_%d+%d+uint16(i)*%d", d.Index(), segmentHeaderSize, fieldElemSize(f)))
+		g.gen.P("}")
+	case d.IsMap():
+		g.gen.P("n_", d.Index(), ", len_", d.Index(), " := ", runtimePackage.Ident("ReadSlice"), "(buf, n+", offset, ")")
+		g.gen.P("{")
+		g.gen.P("    n := n_", d.Index(), "; _ = n")
+		g.gen.P("    for i := uint16(0); i < len_", d.Index(), "; i++ {")
+		g.generateUnmarshalPrimitiveSize(f.Message.Fields[0], "n")
+		g.gen.P("        n += ", fieldSize(f.Message.Fields[0]))
+		g.generateUnmarshalPrimitiveSize(f.Message.Fields[1], "n")
+		g.gen.P("        n += ", fieldSize(f.Message.Fields[1]))
+		g.gen.P("    }")
+		g.gen.P("}")
+	case d.ContainingOneof() != nil:
+		g.gen.P("// TODO: field ", f.GoName)
+	default:
+		g.generateUnmarshalPrimitiveSize(f, fmt.Sprintf("n+%d", offset))
+	}
+}
+
+func (g zeropbFeature) generateUnmarshalPrimitiveSize(f *protogen.Field, offset string) {
+	switch d := f.Desc; d.Kind() {
+	case protoreflect.StringKind, protoreflect.BytesKind:
+		g.gen.P("_, len_", d.Index(), " := ", runtimePackage.Ident("ReadSlice"), "(buf, ", offset, ")")
+		g.gen.P("size += len_", d.Index(), "*uint16(", unsafePackage.Ident("Sizeof"), "(byte(0)))")
+	case protoreflect.MessageKind:
+		g.gen.P("size += uint16(", unsafePackage.Ident("Sizeof"), "(", f.Message.GoIdent, "{}))")
+		g.gen.P("size += _", f.Message.GoIdent, "UnmarshalZeroPBSize(buf, ", offset, ")")
+	default:
+	}
+}
+
 func (g zeropbFeature) generateUnmarshalField(f *protogen.Field, offset int) {
 	d := f.Desc
 	switch {
@@ -217,11 +267,8 @@ func (g zeropbFeature) generateUnmarshalField(f *protogen.Field, offset int) {
 		} else {
 			g.gen.P("x.", f.GoName, " = make([]", typ, ", len_", d.Index(), ")")
 		}
-		g.gen.P("{")
-		g.gen.P("    for i := range x.", f.GoName, "{")
-		// Skip segment header.
-		g.generateUnmarshalPrimitive(f, "x."+f.GoName+"[i]", fmt.Sprintf("n_%d+4+uint16(i)*%d", d.Index(), fieldElemSize(f)))
-		g.gen.P("    }")
+		g.gen.P("for i := range x.", f.GoName, "{")
+		g.generateUnmarshalPrimitive(f, "x."+f.GoName+"[i]", fmt.Sprintf("n_%d+%d+uint16(i)*%d", d.Index(), segmentHeaderSize, fieldElemSize(f)))
 		g.gen.P("}")
 	case d.IsMap():
 		g.gen.P("n_", d.Index(), ", len_", d.Index(), " := ", runtimePackage.Ident("ReadSlice"), "(buf, n+", offset, ")")
@@ -280,7 +327,7 @@ func (g zeropbFeature) generateUnmarshalPrimitive(f *protogen.Field, name, offse
 			if d.Kind() == protoreflect.BytesKind {
 				g.gen.P(name, " = mem_", d.Index(), ".Buf")
 			} else {
-				g.gen.P(name, " = ", unsafePackage.Ident("String"), "(unsafe.SliceData(mem_", d.Index(), ".Buf), len(mem_", d.Index(), ".Buf))")
+				g.gen.P(name, " = ", unsafePackage.Ident("String"), "(", unsafePackage.Ident("SliceData"), "(mem_", d.Index(), ".Buf), len(mem_", d.Index(), ".Buf))")
 			}
 		} else {
 			if d.Kind() == protoreflect.BytesKind {
diff --git a/testpb/1.pulsar.go b/testpb/1.pulsar.go
index 56b109b..825b7bc 100644
--- a/testpb/1.pulsar.go
+++ b/testpb/1.pulsar.go
@@ -3207,11 +3207,52 @@ func (x *A) UnmarshalZeroPB(buf []byte) (err error) {
 		}
 	}()
 	var mem []byte
-	mem = make([]byte, 251)
+	memSize := _AUnmarshalZeroPBSize(buf, 0)
+	mem = make([]byte, memSize)
 	x.unmarshalZeroPB(buf, 0, zeropb.NewBuffer(mem))
 	return nil
 }
 
+func _AUnmarshalZeroPBSize(buf []byte, n uint16) (size uint16) {
+	_, len_14 := zeropb.ReadSlice(buf, n+80)
+	size += len_14 * uint16(unsafe.Sizeof(byte(0)))
+	_, len_15 := zeropb.ReadSlice(buf, n+84)
+	size += len_15 * uint16(unsafe.Sizeof(byte(0)))
+	size += uint16(unsafe.Sizeof(B{}))
+	size += _BUnmarshalZeroPBSize(buf, n+88)
+	n_17, len_17 := zeropb.ReadSlice(buf, n+92)
+	{
+		n := n_17
+		_ = n
+		for i := uint16(0); i < len_17; i++ {
+			_, len_0 := zeropb.ReadSlice(buf, n)
+			size += len_0 * uint16(unsafe.Sizeof(byte(0)))
+			n += 4
+			size += uint16(unsafe.Sizeof(B{}))
+			size += _BUnmarshalZeroPBSize(buf, n)
+			n += 4
+		}
+	}
+	n_18, len_18 := zeropb.ReadSlice(buf, n+96)
+	_ = n_18
+	size += len_18 * uint16(unsafe.Sizeof((A{}).LIST[0]))
+	for i := uint16(0); i < len_18; i++ {
+		size += uint16(unsafe.Sizeof(B{}))
+		size += _BUnmarshalZeroPBSize(buf, n_18+4+uint16(i)*4)
+	}
+	// TODO: field ONEOF_B
+	// TODO: field ONEOF_STRING
+	n_21, len_21 := zeropb.ReadSlice(buf, n+108)
+	_ = n_21
+	size += len_21 * uint16(unsafe.Sizeof((A{}).LIST_ENUM[0]))
+	for i := uint16(0); i < len_21; i++ {
+	}
+	size += uint16(unsafe.Sizeof(ImportedMessage{}))
+	size += _ImportedMessageUnmarshalZeroPBSize(buf, n+112)
+	_, len_23 := zeropb.ReadSlice(buf, n+112)
+	size += len_23 * uint16(unsafe.Sizeof(byte(0)))
+	return
+}
 func (x *A) unmarshalZeroPB(buf []byte, n uint16, mem *zeropb.Buffer) {
 	x.Enum = Enumeration(binary.LittleEndian.Uint32(buf[n+0:]))
 	bool_1 := binary.LittleEndian.Uint32(buf[n+4:])
@@ -3267,22 +3308,18 @@ func (x *A) unmarshalZeroPB(buf []byte, n uint16, mem *zeropb.Buffer) {
 	n_18, len_18 := zeropb.ReadSlice(buf, n+96)
 	mem_18 := mem.Alloc(int(len_18) * int(unsafe.Sizeof(x.LIST[0])))
 	x.LIST = unsafe.Slice((**B)(unsafe.Pointer(unsafe.SliceData(mem_18.Buf))), len_18)
-	{
-		for i := range x.LIST {
-			mem_18 := mem.Alloc(int(unsafe.Sizeof(*x.LIST[i])))
-			x.LIST[i] = (*B)(unsafe.Pointer(unsafe.SliceData(mem_18.Buf)))
-			x.LIST[i].unmarshalZeroPB(buf, n_18+4+uint16(i)*4, mem)
-		}
+	for i := range x.LIST {
+		mem_18 := mem.Alloc(int(unsafe.Sizeof(*x.LIST[i])))
+		x.LIST[i] = (*B)(unsafe.Pointer(unsafe.SliceData(mem_18.Buf)))
+		x.LIST[i].unmarshalZeroPB(buf, n_18+4+uint16(i)*4, mem)
 	}
 	// TODO: field ONEOF_B
 	// TODO: field ONEOF_STRING
 	n_21, len_21 := zeropb.ReadSlice(buf, n+108)
 	mem_21 := mem.Alloc(int(len_21) * int(unsafe.Sizeof(x.LIST_ENUM[0])))
 	x.LIST_ENUM = unsafe.Slice((*Enumeration)(unsafe.Pointer(unsafe.SliceData(mem_21.Buf))), len_21)
-	{
-		for i := range x.LIST_ENUM {
-			x.LIST_ENUM[i] = Enumeration(binary.LittleEndian.Uint32(buf[n_21+4+uint16(i)*4:]))
-		}
+	for i := range x.LIST_ENUM {
+		x.LIST_ENUM[i] = Enumeration(binary.LittleEndian.Uint32(buf[n_21+4+uint16(i)*4:]))
 	}
 	mem_22 := mem.Alloc(int(unsafe.Sizeof(*x.Imported)))
 	x.Imported = (*ImportedMessage)(unsafe.Pointer(unsafe.SliceData(mem_22.Buf)))
@@ -3315,11 +3352,17 @@ func (x *B) UnmarshalZeroPB(buf []byte) (err error) {
 		}
 	}()
 	var mem []byte
-	mem = make([]byte, 251)
+	memSize := _BUnmarshalZeroPBSize(buf, 0)
+	mem = make([]byte, memSize)
 	x.unmarshalZeroPB(buf, 0, zeropb.NewBuffer(mem))
 	return nil
 }
 
+func _BUnmarshalZeroPBSize(buf []byte, n uint16) (size uint16) {
+	_, len_0 := zeropb.ReadSlice(buf, n+0)
+	size += len_0 * uint16(unsafe.Sizeof(byte(0)))
+	return
+}
 func (x *B) unmarshalZeroPB(buf []byte, n uint16, mem *zeropb.Buffer) {
 	n_0, len_0 := zeropb.ReadSlice(buf, n+0)
 	buf_0 := buf[n_0 : n_0+len_0]
diff --git a/testpb/2.pulsar.go b/testpb/2.pulsar.go
index 5e12ed8..4513cf8 100644
--- a/testpb/2.pulsar.go
+++ b/testpb/2.pulsar.go
@@ -502,10 +502,14 @@ func (x *ImportedMessage) UnmarshalZeroPB(buf []byte) (err error) {
 		}
 	}()
 	var mem []byte
-	mem = make([]byte, 251)
+	memSize := _ImportedMessageUnmarshalZeroPBSize(buf, 0)
+	mem = make([]byte, memSize)
 	x.unmarshalZeroPB(buf, 0, zeropb.NewBuffer(mem))
 	return nil
 }
 
+func _ImportedMessageUnmarshalZeroPBSize(buf []byte, n uint16) (size uint16) {
+	return
+}
 func (x *ImportedMessage) unmarshalZeroPB(buf []byte, n uint16, mem *zeropb.Buffer) {
 }

From ace3fd4cc00eea00ccef9274c5a541fa89f20dea Mon Sep 17 00:00:00 2001
From: Elias Naur <elias@orijtech.com>
Date: Sat, 9 Dec 2023 11:42:56 -0600
Subject: [PATCH 8/9] add nil check; test segments

---
 features/zeropb/zeropb.go |  3 +++
 testpb/1.pulsar.go        |  6 ++++++
 testpb/2.pulsar.go        |  3 +++
 testpb/codec_test.go      | 17 +++++++++++++++++
 4 files changed, 29 insertions(+)

diff --git a/features/zeropb/zeropb.go b/features/zeropb/zeropb.go
index b2b8798..02c9658 100644
--- a/features/zeropb/zeropb.go
+++ b/features/zeropb/zeropb.go
@@ -60,6 +60,9 @@ func (g zeropbFeature) generateMarshal(m *protogen.Message) {
 	g.gen.P("}")
 	g.gen.P()
 	g.gen.P("func (x *", m.GoIdent, ") marshalZeroPB(b *", runtimePackage.Ident("Buffer"), ", buf ", runtimePackage.Ident("Allocation"), ") {")
+	g.gen.P("    if x == nil {")
+	g.gen.P("        return")
+	g.gen.P("    }")
 	offset := 0
 	for _, f := range m.Fields {
 		g.generateMarshalField(f, offset)
diff --git a/testpb/1.pulsar.go b/testpb/1.pulsar.go
index 825b7bc..40b5190 100644
--- a/testpb/1.pulsar.go
+++ b/testpb/1.pulsar.go
@@ -3139,6 +3139,9 @@ func (x *A) MarshalZeroPB(buf []byte) (n int, err error) {
 }
 
 func (x *A) marshalZeroPB(b *zeropb.Buffer, buf zeropb.Allocation) {
+	if x == nil {
+		return
+	}
 	binary.LittleEndian.PutUint32(buf.Buf[0:], uint32(x.Enum))
 	bool_1 := uint32(0)
 	if x.SomeBoolean {
@@ -3342,6 +3345,9 @@ func (x *B) MarshalZeroPB(buf []byte) (n int, err error) {
 }
 
 func (x *B) marshalZeroPB(b *zeropb.Buffer, buf zeropb.Allocation) {
+	if x == nil {
+		return
+	}
 	buf_0 := b.AllocRel(len(x.X), buf, 0, uint16(len(x.X)))
 	copy(buf_0.Buf, x.X)
 }
diff --git a/testpb/2.pulsar.go b/testpb/2.pulsar.go
index 4513cf8..cbc03cc 100644
--- a/testpb/2.pulsar.go
+++ b/testpb/2.pulsar.go
@@ -494,6 +494,9 @@ func (x *ImportedMessage) MarshalZeroPB(buf []byte) (n int, err error) {
 }
 
 func (x *ImportedMessage) marshalZeroPB(b *zeropb.Buffer, buf zeropb.Allocation) {
+	if x == nil {
+		return
+	}
 }
 func (x *ImportedMessage) UnmarshalZeroPB(buf []byte) (err error) {
 	defer func() {
diff --git a/testpb/codec_test.go b/testpb/codec_test.go
index 541ee6a..cca0304 100644
--- a/testpb/codec_test.go
+++ b/testpb/codec_test.go
@@ -1,6 +1,7 @@
 package testpb
 
 import (
+	fmt "fmt"
 	"testing"
 
 	"github.com/stretchr/testify/require"
@@ -79,6 +80,22 @@ func TestMarshal(t *testing.T) {
 	require.Equal(t, expected, zpbGot)
 }
 
+func TestZeroPBSegments(t *testing.T) {
+	var longList A
+	for i := 0; i < 256; i++ {
+		longList.LIST = append(longList.LIST, &B{
+			X: fmt.Sprintf("list%d", i),
+		})
+	}
+	buf := make([]byte, 64*1024)
+	n, err := longList.MarshalZeroPB(buf)
+	require.NoError(t, err)
+	var got A
+	err = got.UnmarshalZeroPB(buf[:n])
+	require.NoError(t, err)
+	require.Equal(t, longList.LIST, got.LIST)
+}
+
 func BenchmarkMarshalA(b *testing.B) {
 	protoOpts := proto.MarshalOptions{}
 	b.Run("proto", func(b *testing.B) {

From 2e9e8e97b101ec93de0daab7859d7ac6225b1265 Mon Sep 17 00:00:00 2001
From: Elias Naur <elias@orijtech.com>
Date: Sat, 9 Dec 2023 12:32:55 -0600
Subject: [PATCH 9/9] implement segments

---
 features/zeropb/zeropb.go |  67 +++++++++++++++----
 runtime/zeropb/zeropb.go  |  16 +++--
 testpb/1.pulsar.go        | 134 +++++++++++++++++++++++++++++---------
 3 files changed, 168 insertions(+), 49 deletions(-)

diff --git a/features/zeropb/zeropb.go b/features/zeropb/zeropb.go
index 02c9658..bf1aa72 100644
--- a/features/zeropb/zeropb.go
+++ b/features/zeropb/zeropb.go
@@ -124,15 +124,32 @@ func (g zeropbFeature) generateMarshalField(f *protogen.Field, offset int) {
 	d := f.Desc
 	switch {
 	case d.IsList():
-		g.gen.P("buf_", d.Index(), " := b.AllocRel(len(x.", f.GoName, ")*", fieldElemSize(f), " + ", segmentHeaderSize, ", buf, ", offset, ", uint16(len(x.", f.GoName, ")))")
-		g.gen.P("{")
-		g.gen.P("    buf := buf_", d.Index())
+		g.gen.P("rem_", d.Index(), " := x.", f.GoName)
+		g.gen.P("var link_", d.Index(), " zeropb.Allocation")
+		g.gen.P("for len(rem_", d.Index(), ") > 0 {")
+		g.gen.P("    seg := rem_", d.Index())
+		g.gen.P("    if len(seg) > 255 {")
+		g.gen.P("        seg = seg[:255]")
+		g.gen.P("    }")
+		g.gen.P("    rem_", d.Index(), " = rem_", d.Index(), "[len(seg):]")
+		g.gen.P("    buf_", d.Index(), " := b.Alloc(len(seg)*", fieldElemSize(f), "+", segmentHeaderSize, ")")
+		g.gen.P("    if link_", d.Index(), ".Buf == nil {")
+		// Write relative offset and len.
+		g.gen.P(binaryPackage.Ident("LittleEndian"), ".PutUint16(buf.Buf[", offset, ":], buf_", d.Index(), ".Offset-buf.Offset-", offset, ")")
+		g.gen.P(binaryPackage.Ident("LittleEndian"), ".PutUint16(buf.Buf[", offset, "+2:], uint16(len(x.", f.GoName, ")))")
+		g.gen.P("    } else {")
+		// Write segment link offset.
+		g.gen.P(binaryPackage.Ident("LittleEndian"), ".PutUint16(link_", d.Index(), ".Buf[2:], buf_", d.Index(), ".Offset-link_", d.Index(), ".Offset-2)")
+		g.gen.P("    }")
+		g.gen.P("    {")
+		g.gen.P("        buf := buf_", d.Index())
 		// Write a segment header.
-		g.gen.P("    buf.Buf[0] = byte(len(x.", f.GoName, "))")
-		g.gen.P("    buf.Buf[1] = byte(len(x.", f.GoName, "))")
-		g.gen.P(binaryPackage.Ident("LittleEndian"), ".PutUint16(buf.Buf[2:], 0)")
-		g.gen.P("    for i, e := range x.", f.GoName, " {")
+		g.gen.P("        buf.Buf[0] = byte(len(seg))")
+		g.gen.P("        buf.Buf[1] = byte(len(seg))")
+		g.gen.P("        link_", d.Index(), " = buf")
+		g.gen.P("        for i, e := range seg {")
 		g.generateMarshalPrimitive(f, "e", fmt.Sprintf("uint16(i)*%d+4", fieldElemSize(f)))
+		g.gen.P("        }")
 		g.gen.P("    }")
 		g.gen.P("}")
 	case d.IsMap():
@@ -222,11 +239,21 @@ func (g zeropbFeature) generateUnmarshalFieldSize(f *protogen.Field, typ protoge
 	switch {
 	case d.IsList():
 		g.gen.P("n_", d.Index(), ", len_", d.Index(), " := ", runtimePackage.Ident("ReadSlice"), "(buf, n+", offset, ")")
-		g.gen.P("_ = n_", d.Index())
-		g.gen.P("size += len_", d.Index(), "*uint16(", unsafePackage.Ident("Sizeof"), "((", typ, "{}).", f.GoName, "[0]))")
-		g.gen.P("for i := uint16(0); i < len_", d.Index(), "; i++ {")
-		// Skip segment header.
-		g.generateUnmarshalPrimitiveSize(f, fmt.Sprintf("n_%d+%d+uint16(i)*%d", d.Index(), segmentHeaderSize, fieldElemSize(f)))
+		g.gen.P("{")
+		g.gen.P("    n, len := n_", d.Index(), ", len_", d.Index())
+		g.gen.P("    size += len*uint16(", unsafePackage.Ident("Sizeof"), "((", typ, "{}).", f.GoName, "[0]))")
+		g.gen.P("    i8 := byte(0)")
+		g.gen.P("    segLen := buf[n]")
+		g.gen.P("    for i := uint16(0); i < len; i++ {")
+		g.gen.P("        if i8 == segLen {")
+		g.gen.P("            i8 = 0")
+		// Read continuation link.
+		g.gen.P("            n = ", runtimePackage.Ident("ReadOffset"), "(buf, n+2)")
+		g.gen.P("            segLen = buf[n]")
+		g.gen.P("        }")
+		g.generateUnmarshalPrimitiveSize(f, fmt.Sprintf("n+%d+uint16(i8)*%d", segmentHeaderSize, fieldElemSize(f)))
+		g.gen.P("        i8++")
+		g.gen.P("    }")
 		g.gen.P("}")
 	case d.IsMap():
 		g.gen.P("n_", d.Index(), ", len_", d.Index(), " := ", runtimePackage.Ident("ReadSlice"), "(buf, n+", offset, ")")
@@ -270,8 +297,20 @@ func (g zeropbFeature) generateUnmarshalField(f *protogen.Field, offset int) {
 		} else {
 			g.gen.P("x.", f.GoName, " = make([]", typ, ", len_", d.Index(), ")")
 		}
-		g.gen.P("for i := range x.", f.GoName, "{")
-		g.generateUnmarshalPrimitive(f, "x."+f.GoName+"[i]", fmt.Sprintf("n_%d+%d+uint16(i)*%d", d.Index(), segmentHeaderSize, fieldElemSize(f)))
+		g.gen.P("{")
+		g.gen.P("    n, len := n_", d.Index(), ", len_", d.Index(), "; _ = len")
+		g.gen.P("    i8 := byte(0)")
+		g.gen.P("    segLen := buf[n]")
+		g.gen.P("    for i := range x.", f.GoName, "{")
+		g.gen.P("        if i8 == segLen {")
+		g.gen.P("            i8 = 0")
+		// Read continuation link.
+		g.gen.P("            n = ", runtimePackage.Ident("ReadOffset"), "(buf, n+2)")
+		g.gen.P("            segLen = buf[n]")
+		g.gen.P("        }")
+		g.generateUnmarshalPrimitive(f, "x."+f.GoName+"[i]", fmt.Sprintf("n+%d+uint16(i8)*%d", segmentHeaderSize, fieldElemSize(f)))
+		g.gen.P("        i8++")
+		g.gen.P("    }")
 		g.gen.P("}")
 	case d.IsMap():
 		g.gen.P("n_", d.Index(), ", len_", d.Index(), " := ", runtimePackage.Ident("ReadSlice"), "(buf, n+", offset, ")")
diff --git a/runtime/zeropb/zeropb.go b/runtime/zeropb/zeropb.go
index ee7e691..e7384dd 100644
--- a/runtime/zeropb/zeropb.go
+++ b/runtime/zeropb/zeropb.go
@@ -12,7 +12,7 @@ type Buffer struct {
 
 type Allocation struct {
 	Buf    []byte
-	offset uint16
+	Offset uint16
 }
 
 func NewBuffer(b []byte) *Buffer {
@@ -27,7 +27,7 @@ func NewBuffer(b []byte) *Buffer {
 func (b *Buffer) Alloc(n int) Allocation {
 	a := Allocation{
 		Buf:    b.buf[:n],
-		offset: b.allocated,
+		Offset: b.allocated,
 	}
 	// The slice above would have paniced if n would not fit a uint16.
 	n16 := uint16(n)
@@ -40,7 +40,7 @@ func (b *Buffer) AllocRel(n int, dst Allocation, offset, len uint16) Allocation
 	a := b.Alloc(n)
 	bo := binary.LittleEndian
 	// Write relative offset and len.
-	bo.PutUint16(dst.Buf[offset:], a.offset-dst.offset-offset)
+	bo.PutUint16(dst.Buf[offset:], a.Offset-dst.Offset-offset)
 	bo.PutUint16(dst.Buf[offset+2:], len)
 	return a
 }
@@ -52,13 +52,19 @@ func (b *Buffer) Allocated() uint16 {
 func (a Allocation) Slice(offset uint16) Allocation {
 	return Allocation{
 		Buf:    a.Buf[offset:],
-		offset: a.offset + offset,
+		Offset: a.Offset + offset,
 	}
 }
 
 func ReadSlice(buf []byte, offset uint16) (off, len uint16) {
 	bo := binary.LittleEndian
-	off = offset + bo.Uint16(buf[offset:])
+	off = ReadOffset(buf, offset)
 	len = bo.Uint16(buf[offset+2:])
 	return
 }
+
+func ReadOffset(buf []byte, offset uint16) (off uint16) {
+	bo := binary.LittleEndian
+	off = offset + bo.Uint16(buf[offset:])
+	return
+}
diff --git a/testpb/1.pulsar.go b/testpb/1.pulsar.go
index 40b5190..a532d29 100644
--- a/testpb/1.pulsar.go
+++ b/testpb/1.pulsar.go
@@ -3177,26 +3177,56 @@ func (x *A) marshalZeroPB(b *zeropb.Buffer, buf zeropb.Allocation) {
 			n += 4
 		}
 	}
-	buf_18 := b.AllocRel(len(x.LIST)*4+4, buf, 96, uint16(len(x.LIST)))
-	{
-		buf := buf_18
-		buf.Buf[0] = byte(len(x.LIST))
-		buf.Buf[1] = byte(len(x.LIST))
-		binary.LittleEndian.PutUint16(buf.Buf[2:], 0)
-		for i, e := range x.LIST {
-			e.marshalZeroPB(b, buf.Slice(uint16(i)*4+4))
+	rem_18 := x.LIST
+	var link_18 zeropb.Allocation
+	for len(rem_18) > 0 {
+		seg := rem_18
+		if len(seg) > 255 {
+			seg = seg[:255]
+		}
+		rem_18 = rem_18[len(seg):]
+		buf_18 := b.Alloc(len(seg)*4 + 4)
+		if link_18.Buf == nil {
+			binary.LittleEndian.PutUint16(buf.Buf[96:], buf_18.Offset-buf.Offset-96)
+			binary.LittleEndian.PutUint16(buf.Buf[96+2:], uint16(len(x.LIST)))
+		} else {
+			binary.LittleEndian.PutUint16(link_18.Buf[2:], buf_18.Offset-link_18.Offset-2)
+		}
+		{
+			buf := buf_18
+			buf.Buf[0] = byte(len(seg))
+			buf.Buf[1] = byte(len(seg))
+			link_18 = buf
+			for i, e := range seg {
+				e.marshalZeroPB(b, buf.Slice(uint16(i)*4+4))
+			}
 		}
 	}
 	// TODO: field ONEOF_B
 	// TODO: field ONEOF_STRING
-	buf_21 := b.AllocRel(len(x.LIST_ENUM)*4+4, buf, 108, uint16(len(x.LIST_ENUM)))
-	{
-		buf := buf_21
-		buf.Buf[0] = byte(len(x.LIST_ENUM))
-		buf.Buf[1] = byte(len(x.LIST_ENUM))
-		binary.LittleEndian.PutUint16(buf.Buf[2:], 0)
-		for i, e := range x.LIST_ENUM {
-			binary.LittleEndian.PutUint32(buf.Buf[uint16(i)*4+4:], uint32(e))
+	rem_21 := x.LIST_ENUM
+	var link_21 zeropb.Allocation
+	for len(rem_21) > 0 {
+		seg := rem_21
+		if len(seg) > 255 {
+			seg = seg[:255]
+		}
+		rem_21 = rem_21[len(seg):]
+		buf_21 := b.Alloc(len(seg)*4 + 4)
+		if link_21.Buf == nil {
+			binary.LittleEndian.PutUint16(buf.Buf[108:], buf_21.Offset-buf.Offset-108)
+			binary.LittleEndian.PutUint16(buf.Buf[108+2:], uint16(len(x.LIST_ENUM)))
+		} else {
+			binary.LittleEndian.PutUint16(link_21.Buf[2:], buf_21.Offset-link_21.Offset-2)
+		}
+		{
+			buf := buf_21
+			buf.Buf[0] = byte(len(seg))
+			buf.Buf[1] = byte(len(seg))
+			link_21 = buf
+			for i, e := range seg {
+				binary.LittleEndian.PutUint32(buf.Buf[uint16(i)*4+4:], uint32(e))
+			}
 		}
 	}
 	x.Imported.marshalZeroPB(b, buf.Slice(112))
@@ -3237,18 +3267,38 @@ func _AUnmarshalZeroPBSize(buf []byte, n uint16) (size uint16) {
 		}
 	}
 	n_18, len_18 := zeropb.ReadSlice(buf, n+96)
-	_ = n_18
-	size += len_18 * uint16(unsafe.Sizeof((A{}).LIST[0]))
-	for i := uint16(0); i < len_18; i++ {
-		size += uint16(unsafe.Sizeof(B{}))
-		size += _BUnmarshalZeroPBSize(buf, n_18+4+uint16(i)*4)
+	{
+		n, len := n_18, len_18
+		size += len * uint16(unsafe.Sizeof((A{}).LIST[0]))
+		i8 := byte(0)
+		segLen := buf[n]
+		for i := uint16(0); i < len; i++ {
+			if i8 == segLen {
+				i8 = 0
+				n = zeropb.ReadOffset(buf, n+2)
+				segLen = buf[n]
+			}
+			size += uint16(unsafe.Sizeof(B{}))
+			size += _BUnmarshalZeroPBSize(buf, n+4+uint16(i8)*4)
+			i8++
+		}
 	}
 	// TODO: field ONEOF_B
 	// TODO: field ONEOF_STRING
 	n_21, len_21 := zeropb.ReadSlice(buf, n+108)
-	_ = n_21
-	size += len_21 * uint16(unsafe.Sizeof((A{}).LIST_ENUM[0]))
-	for i := uint16(0); i < len_21; i++ {
+	{
+		n, len := n_21, len_21
+		size += len * uint16(unsafe.Sizeof((A{}).LIST_ENUM[0]))
+		i8 := byte(0)
+		segLen := buf[n]
+		for i := uint16(0); i < len; i++ {
+			if i8 == segLen {
+				i8 = 0
+				n = zeropb.ReadOffset(buf, n+2)
+				segLen = buf[n]
+			}
+			i8++
+		}
 	}
 	size += uint16(unsafe.Sizeof(ImportedMessage{}))
 	size += _ImportedMessageUnmarshalZeroPBSize(buf, n+112)
@@ -3311,18 +3361,42 @@ func (x *A) unmarshalZeroPB(buf []byte, n uint16, mem *zeropb.Buffer) {
 	n_18, len_18 := zeropb.ReadSlice(buf, n+96)
 	mem_18 := mem.Alloc(int(len_18) * int(unsafe.Sizeof(x.LIST[0])))
 	x.LIST = unsafe.Slice((**B)(unsafe.Pointer(unsafe.SliceData(mem_18.Buf))), len_18)
-	for i := range x.LIST {
-		mem_18 := mem.Alloc(int(unsafe.Sizeof(*x.LIST[i])))
-		x.LIST[i] = (*B)(unsafe.Pointer(unsafe.SliceData(mem_18.Buf)))
-		x.LIST[i].unmarshalZeroPB(buf, n_18+4+uint16(i)*4, mem)
+	{
+		n, len := n_18, len_18
+		_ = len
+		i8 := byte(0)
+		segLen := buf[n]
+		for i := range x.LIST {
+			if i8 == segLen {
+				i8 = 0
+				n = zeropb.ReadOffset(buf, n+2)
+				segLen = buf[n]
+			}
+			mem_18 := mem.Alloc(int(unsafe.Sizeof(*x.LIST[i])))
+			x.LIST[i] = (*B)(unsafe.Pointer(unsafe.SliceData(mem_18.Buf)))
+			x.LIST[i].unmarshalZeroPB(buf, n+4+uint16(i8)*4, mem)
+			i8++
+		}
 	}
 	// TODO: field ONEOF_B
 	// TODO: field ONEOF_STRING
 	n_21, len_21 := zeropb.ReadSlice(buf, n+108)
 	mem_21 := mem.Alloc(int(len_21) * int(unsafe.Sizeof(x.LIST_ENUM[0])))
 	x.LIST_ENUM = unsafe.Slice((*Enumeration)(unsafe.Pointer(unsafe.SliceData(mem_21.Buf))), len_21)
-	for i := range x.LIST_ENUM {
-		x.LIST_ENUM[i] = Enumeration(binary.LittleEndian.Uint32(buf[n_21+4+uint16(i)*4:]))
+	{
+		n, len := n_21, len_21
+		_ = len
+		i8 := byte(0)
+		segLen := buf[n]
+		for i := range x.LIST_ENUM {
+			if i8 == segLen {
+				i8 = 0
+				n = zeropb.ReadOffset(buf, n+2)
+				segLen = buf[n]
+			}
+			x.LIST_ENUM[i] = Enumeration(binary.LittleEndian.Uint32(buf[n+4+uint16(i8)*4:]))
+			i8++
+		}
 	}
 	mem_22 := mem.Alloc(int(unsafe.Sizeof(*x.Imported)))
 	x.Imported = (*ImportedMessage)(unsafe.Pointer(unsafe.SliceData(mem_22.Buf)))