Skip to content

Commit 6f2963f

Browse files
authored
Merge pull request #140 from thanos-community/unique_unmarshal
features/unmarshal: implement unmarshal_unique
2 parents 51cc705 + 8d962d7 commit 6f2963f

File tree

8 files changed

+671
-17
lines changed

8 files changed

+671
-17
lines changed

README.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,20 @@ The following features can be generated:
5353

5454
- `func (p *YourProto) CloneMessageVT() proto.Message`: this function behaves like the above `p.CloneVT()`, but provides a uniform signature in order to be accessible via type assertions even if the type is not known at compile time. This allows implementing a generic `func CloneVT(proto.Message)` without reflection. If the receiver `p` is `nil`, a typed `nil` pointer of the message type will be returned inside a `proto.Message` interface.
5555

56+
### Field Options
57+
58+
- `unique` is a field option available on strings. If it is set to `true` then all all strings are interned using [unique.Make](https://pkg.go.dev/unique#Make). Go 1.23+ is needed. `unmarshal_unsafe` takes precendence over `unique`. Example usage:
59+
60+
```
61+
import "github.com/planetscale/vtprotobuf/vtproto/ext.proto";
62+
63+
message Label {
64+
string name = 1 [(vtproto.options).unique = true];
65+
string value = 2 [(vtproto.options).unique = true];
66+
}
67+
```
68+
69+
5670
## Usage
5771

5872
1. Install `protoc-gen-go-vtproto`:

features/unmarshal/unmarshal.go

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,11 @@ import (
1212

1313
"google.golang.org/protobuf/compiler/protogen"
1414
"google.golang.org/protobuf/encoding/protowire"
15+
"google.golang.org/protobuf/proto"
1516
"google.golang.org/protobuf/reflect/protoreflect"
1617

1718
"github.com/planetscale/vtprotobuf/generator"
19+
"github.com/planetscale/vtprotobuf/vtproto"
1820
)
1921

2022
func init() {
@@ -156,6 +158,8 @@ func (p *unmarshal) declareMapField(varName string, nullable bool, field *protog
156158
}
157159

158160
func (p *unmarshal) mapField(varName string, field *protogen.Field) {
161+
unique := proto.GetExtension(field.Desc.Options(), vtproto.E_Options).(*vtproto.Opts).GetUnique()
162+
159163
switch field.Desc.Kind() {
160164
case protoreflect.DoubleKind:
161165
p.P(`var `, varName, `temp uint64`)
@@ -193,13 +197,20 @@ func (p *unmarshal) mapField(varName string, field *protogen.Field) {
193197
p.P(`if postStringIndex`, varName, ` > l {`)
194198
p.P(`return `, p.Ident("io", `ErrUnexpectedEOF`))
195199
p.P(`}`)
196-
if p.unsafe {
200+
switch {
201+
case p.unsafe:
197202
p.P(`if intStringLen`, varName, ` == 0 {`)
198203
p.P(varName, ` = ""`)
199204
p.P(`} else {`)
200205
p.P(varName, ` = `, p.Ident("unsafe", `String`), `(&dAtA[iNdEx], intStringLen`, varName, `)`)
201206
p.P(`}`)
202-
} else {
207+
case unique:
208+
p.P(`if intStringLen`, varName, ` == 0 {`)
209+
p.P(varName, ` = ""`)
210+
p.P(`} else {`)
211+
p.P(varName, ` = `, p.Ident("unique", `Make`), `[string](`, p.Ident("unsafe", `String`), `(&dAtA[iNdEx], intStringLen`, varName, `)).Value()`)
212+
p.P(`}`)
213+
default:
203214
p.P(varName, ` = `, "string", `(dAtA[iNdEx:postStringIndex`, varName, `])`)
204215
}
205216
p.P(`iNdEx = postStringIndex`, varName)
@@ -409,6 +420,8 @@ func (p *unmarshal) fieldItem(field *protogen.Field, fieldname string, message *
409420
p.P(`m.`, fieldname, ` = &b`)
410421
}
411422
case protoreflect.StringKind:
423+
unique := proto.GetExtension(field.Desc.Options(), vtproto.E_Options).(*vtproto.Opts).GetUnique()
424+
412425
p.P(`var stringLen uint64`)
413426
p.decodeVarint("stringLen", "uint64")
414427
p.P(`intStringLen := int(stringLen)`)
@@ -423,12 +436,19 @@ func (p *unmarshal) fieldItem(field *protogen.Field, fieldname string, message *
423436
p.P(`return `, p.Ident("io", `ErrUnexpectedEOF`))
424437
p.P(`}`)
425438
str := "string(dAtA[iNdEx:postIndex])"
426-
if p.unsafe {
439+
switch {
440+
case p.unsafe:
427441
str = "stringValue"
428442
p.P(`var stringValue string`)
429443
p.P(`if intStringLen > 0 {`)
430444
p.P(`stringValue = `, p.Ident("unsafe", `String`), `(&dAtA[iNdEx], intStringLen)`)
431445
p.P(`}`)
446+
case unique:
447+
str = "stringValue"
448+
p.P(`var stringValue string`)
449+
p.P(`if intStringLen > 0 {`)
450+
p.P(`stringValue = `, p.Ident("unique", `Make`), `[string](`, p.Ident("unsafe", `String`), `(&dAtA[iNdEx], intStringLen)).Value()`)
451+
p.P(`}`)
432452
}
433453
if oneof {
434454
p.P(`m.`, fieldname, ` = &`, field.GoIdent, `{`, field.GoName, ": ", str, `}`)

include/github.com/planetscale/vtprotobuf/vtproto/ext.proto

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,14 @@ option go_package = "github.com/planetscale/vtprotobuf/vtproto";
99

1010
extend google.protobuf.MessageOptions {
1111
optional bool mempool = 64101;
12+
}
13+
14+
extend google.protobuf.FieldOptions {
15+
optional Opts options = 64150;
16+
}
17+
18+
// These options should be used during schema definition,
19+
// applying them to some of the fields in protobuf
20+
message Opts {
21+
optional bool unique = 1;
1222
}

testproto/unique/unique.pb.go

Lines changed: 147 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

testproto/unique/unique.proto

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
syntax = "proto3";
2+
option go_package = "testproto/unique";
3+
4+
import "github.com/planetscale/vtprotobuf/vtproto/ext.proto";
5+
6+
message UniqueFieldExtension {
7+
string foo = 1 [(vtproto.options).unique = true];
8+
}

testproto/unique/unique_test.go

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package unique
2+
3+
import (
4+
"testing"
5+
"unsafe"
6+
7+
"github.com/stretchr/testify/require"
8+
)
9+
10+
func TestUnmarshalSameMemory(t *testing.T) {
11+
m := &UniqueFieldExtension{
12+
Foo: "bar",
13+
}
14+
15+
b, err := m.MarshalVTStrict()
16+
require.NoError(t, err)
17+
18+
m2 := &UniqueFieldExtension{}
19+
require.NoError(t, m2.UnmarshalVT(b))
20+
21+
m3 := &UniqueFieldExtension{}
22+
require.NoError(t, m3.UnmarshalVT(b))
23+
24+
require.Equal(t, unsafe.StringData(m2.Foo), unsafe.StringData(m3.Foo))
25+
}

0 commit comments

Comments
 (0)