-
Notifications
You must be signed in to change notification settings - Fork 2.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
storepb: construct labels.Labels directly during unmarshal
Signed-off-by: Giedrius Statkevičius <giedrius.statkevicius@vinted.com>
- Loading branch information
Showing
7 changed files
with
249 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,165 @@ | ||
package labelpb | ||
|
||
import ( | ||
fmt "fmt" | ||
"unsafe" | ||
|
||
"github.com/VictoriaMetrics/easyproto" | ||
"github.com/prometheus/prometheus/model/labels" | ||
) | ||
|
||
type StringLabelsBuilder struct { | ||
buf []byte | ||
} | ||
|
||
func EncodeStringSize(stringLen int) int { | ||
return sizeVarint(uint64(stringLen)) + stringLen | ||
} | ||
|
||
func labelSize(m *Label) (n int) { | ||
// strings are encoded as length followed by contents. | ||
l := len(m.Name) | ||
n += l + sizeVarint(uint64(l)) | ||
l = len(m.Value) | ||
n += l + sizeVarint(uint64(l)) | ||
return n | ||
} | ||
|
||
func encodeSize(data []byte, offset, v int) int { | ||
if v < 1<<7 { | ||
offset-- | ||
data[offset] = uint8(v) | ||
return offset | ||
} | ||
return encodeVarint(data, offset, uint64(v)) | ||
} | ||
|
||
func sizeVarint(x uint64) (n int) { | ||
// Most common case first | ||
if x < 1<<7 { | ||
return 1 | ||
} | ||
if x >= 1<<56 { | ||
return 9 | ||
} | ||
if x >= 1<<28 { | ||
x >>= 28 | ||
n = 4 | ||
} | ||
if x >= 1<<14 { | ||
x >>= 14 | ||
n += 2 | ||
} | ||
if x >= 1<<7 { | ||
n++ | ||
} | ||
return n + 1 | ||
} | ||
|
||
func encodeVarint(data []byte, offset int, v uint64) int { | ||
offset -= sizeVarint(v) | ||
base := offset | ||
for v >= 1<<7 { | ||
data[offset] = uint8(v&0x7f | 0x80) | ||
v >>= 7 | ||
offset++ | ||
} | ||
data[offset] = uint8(v) | ||
return base | ||
} | ||
|
||
func NewStringsLabelsBuilder(size int) *StringLabelsBuilder { | ||
return &StringLabelsBuilder{ | ||
buf: make([]byte, size), | ||
} | ||
} | ||
|
||
func (b *StringLabelsBuilder) Labels() labels.Labels { | ||
return *(*labels.Labels)(unsafe.Pointer(&struct { | ||
data string | ||
}{ | ||
data: unsafe.String(unsafe.SliceData(b.buf), len(b.buf)), | ||
})) | ||
} | ||
|
||
// GetLabelsBufferSize returns how many bytes you will need | ||
// for labels.Labels allocation. fieldNumLabels must be | ||
// repeated Labels foo = fieldNumLabels; (in proto file). | ||
func GetLabelsBufferSize(src []byte, fieldNumLabels uint32) (int, error) { | ||
var ( | ||
fc easyproto.FieldContext | ||
size int | ||
err error | ||
) | ||
|
||
for len(src) > 0 { | ||
src, err = fc.NextField(src) | ||
if err != nil { | ||
return 0, fmt.Errorf("cannot read next field in given byte slice: %w", err) | ||
} | ||
|
||
switch fc.FieldNum { | ||
case fieldNumLabels: | ||
data, ok := fc.MessageData() | ||
if !ok { | ||
return 0, fmt.Errorf("cannot read message data") | ||
} | ||
sz, err := getLabelEncodingProtobufSize(data) | ||
if err != nil { | ||
return 0, fmt.Errorf("getting label encoding size: %w", err) | ||
} | ||
size += sz | ||
} | ||
} | ||
return size, nil | ||
} | ||
|
||
func getLabelEncodingProtobufSize(src []byte) (int, error) { | ||
var ( | ||
fc easyproto.FieldContext | ||
err error | ||
n int | ||
) | ||
for len(src) > 0 { | ||
src, err = fc.NextField(src) | ||
if err != nil { | ||
return 0, fmt.Errorf("cannot read next field in labels") | ||
} | ||
if fc.FieldNum > 2 { | ||
return 0, fmt.Errorf("unexpected field number in labels") | ||
} | ||
val, ok := fc.String() | ||
if !ok { | ||
return 0, fmt.Errorf("cannot read string value in labels") | ||
} | ||
|
||
n += EncodeStringSize(len(val)) | ||
} | ||
return n, nil | ||
} | ||
|
||
func (b *StringLabelsBuilder) Add(name, value string) { | ||
i := len(b.buf) | ||
i -= len(value) | ||
copy(b.buf[i:], value) | ||
i = encodeSize(b.buf, i, len(value)) | ||
|
||
i -= len(name) | ||
copy(b.buf[i:], name) | ||
encodeSize(b.buf, i, len(name)) | ||
} | ||
|
||
func init() { | ||
b := NewStringsLabelsBuilder(labelSize(&Label{ | ||
Name: "name", | ||
Value: "value", | ||
})) | ||
|
||
b.Add("name", "value") | ||
|
||
lbls := b.Labels() | ||
|
||
if lbls.String() != `{name="value"}` { | ||
panic("only stringlabels build is supported in Thanos") | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
package labelpb | ||
|
||
import ( | ||
"testing" | ||
|
||
"github.com/stretchr/testify/require" | ||
) | ||
|
||
func TestStringLabelsBuilder(t *testing.T) { | ||
b := NewStringsLabelsBuilder(labelSize(&Label{ | ||
Name: "name", | ||
Value: "value", | ||
})) | ||
|
||
b.Add("name", "value") | ||
|
||
lbls := b.Labels() | ||
|
||
require.Equal(t, `{name="value"}`, lbls.String()) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.