Skip to content
This repository was archived by the owner on Jul 31, 2023. It is now read-only.

Commit a82f154

Browse files
authored
Fix varint decode. (#424)
* Split out varint code. * Add tests.
1 parent 9a508e8 commit a82f154

9 files changed

Lines changed: 219 additions & 41 deletions

File tree

opencensus/common/internal/BUILD

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,14 @@ cc_library(
7070
],
7171
)
7272

73+
cc_library(
74+
name = "varint",
75+
srcs = ["varint.cc"],
76+
hdrs = ["varint.h"],
77+
copts = DEFAULT_COPTS,
78+
deps = ["@com_google_absl//absl/strings"],
79+
)
80+
7381
# Tests
7482
# ========================================================================= #
7583

@@ -142,3 +150,14 @@ cc_test(
142150
"@com_google_protobuf//:protobuf",
143151
],
144152
)
153+
154+
cc_test(
155+
name = "varint_test",
156+
srcs = ["varint_test.cc"],
157+
copts = TEST_COPTS,
158+
deps = [
159+
":varint",
160+
"@com_google_absl//absl/strings",
161+
"@com_google_googletest//:gtest_main",
162+
],
163+
)

opencensus/common/internal/CMakeLists.txt

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,11 +31,19 @@ target_compile_definitions(opencensus_common_stats_object INTERFACE
3131

3232
opencensus_lib(common_string_vector_hash DEPS absl::hash)
3333

34+
opencensus_lib(common_varint SRCS varint.cc DEPS absl::strings)
35+
36+
# Tests.
37+
3438
opencensus_test(common_hostname_test hostname_test.cc common_hostname)
3539

3640
opencensus_test(common_random_test random_test.cc common_random)
3741

38-
opencensus_benchmark(common_random_benchmark random_benchmark.cc common_random)
39-
4042
opencensus_test(common_stats_object_test stats_object_test.cc
4143
common_stats_object absl::strings absl::span)
44+
45+
opencensus_test(common_varint_test varint_test.cc common_varint)
46+
47+
# Benchmarks.
48+
49+
opencensus_benchmark(common_random_benchmark random_benchmark.cc common_random)
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
// Copyright 2019, OpenCensus Authors
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
#include <cstdint>
16+
#include <string>
17+
18+
#include "absl/strings/string_view.h"
19+
#include "opencensus/common/internal/varint.h"
20+
21+
namespace opencensus {
22+
namespace common {
23+
24+
void AppendVarint32(uint32_t i, std::string* out) {
25+
do {
26+
// Encode 7 bits.
27+
uint8_t c = i & 0x7F;
28+
i = i >> 7;
29+
if (i != 0) {
30+
c |= 0x80;
31+
}
32+
out->push_back(c);
33+
} while (i != 0);
34+
}
35+
36+
bool ParseVarint32(absl::string_view* input, uint32_t* out) {
37+
absl::string_view s = *input;
38+
uint32_t i = 0;
39+
uint8_t c;
40+
int shift = 0;
41+
do {
42+
if (s.empty()) {
43+
return false; // Too short.
44+
}
45+
c = s[0];
46+
s = s.substr(1);
47+
if (shift == 28 && c > 0x0f) {
48+
return false; // Out of range for uint32_t.
49+
}
50+
i |= (c & 0x7F) << shift;
51+
shift += 7;
52+
} while (c & 0x80);
53+
*input = s;
54+
*out = i;
55+
return true;
56+
}
57+
58+
} // namespace common
59+
} // namespace opencensus
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
// Copyright 2019, OpenCensus Authors
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
#ifndef OPENCENSUS_COMMON_INTERNAL_VARINT_H_
16+
#define OPENCENSUS_COMMON_INTERNAL_VARINT_H_
17+
18+
#include <cstdint>
19+
#include <string>
20+
21+
#include "absl/strings/string_view.h"
22+
23+
namespace opencensus {
24+
namespace common {
25+
26+
// Appends a variable-length encoded integer to the destination string.
27+
void AppendVarint32(uint32_t i, std::string* out);
28+
29+
// Parses a variable-length encoded integer from the input. Returns false on
30+
// failure. Returns true and consumes the bytes from the input, on success.
31+
bool ParseVarint32(absl::string_view* input, uint32_t* out);
32+
33+
} // namespace common
34+
} // namespace opencensus
35+
36+
#endif // OPENCENSUS_COMMON_INTERNAL_VARINT_H_
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
// Copyright 2019, OpenCensus Authors
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
#include "opencensus/common/internal/varint.h"
16+
17+
#include <iostream>
18+
19+
#include "absl/strings/escaping.h"
20+
#include "absl/strings/string_view.h"
21+
#include "gtest/gtest.h"
22+
23+
namespace opencensus {
24+
namespace common {
25+
namespace {
26+
27+
TEST(Varint, EncodeDecode) {
28+
auto test = [](uint32_t i) {
29+
std::string s;
30+
AppendVarint32(i, &s);
31+
std::cout << " int " << i << " encoded to hex " << absl::BytesToHexString(s)
32+
<< "\n";
33+
absl::string_view sv(s);
34+
uint32_t j = i + 1;
35+
EXPECT_TRUE(ParseVarint32(&sv, &j));
36+
EXPECT_EQ(i, j);
37+
};
38+
test(0);
39+
test(1);
40+
test(10);
41+
test(100);
42+
43+
test(127);
44+
test(128);
45+
test(129);
46+
47+
test(255);
48+
test(256);
49+
test(257);
50+
51+
test(16383);
52+
test(16384);
53+
test(16385);
54+
55+
test(2097151);
56+
test(2097152);
57+
test(2097153);
58+
59+
test(268435455);
60+
test(268435456);
61+
test(268435457);
62+
63+
test(4294967295);
64+
}
65+
66+
TEST(Varint, DecodeOutOfRange) {
67+
constexpr uint8_t input[] = {0xff, 0xff, 0xff, 0xff, 0x10};
68+
absl::string_view sv(reinterpret_cast<const char*>(input), sizeof(input));
69+
uint32_t i;
70+
EXPECT_FALSE(ParseVarint32(&sv, &i));
71+
}
72+
73+
} // namespace
74+
} // namespace common
75+
} // namespace opencensus

opencensus/tags/BUILD

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ cc_library(
6363
visibility = ["//visibility:public"],
6464
deps = [
6565
":tags",
66+
"//opencensus/common/internal:varint",
6667
"@com_google_absl//absl/strings",
6768
],
6869
)

opencensus/tags/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ opencensus_lib(
4040
internal/grpc_tags_bin.cc
4141
DEPS
4242
tags
43+
common_varint
4344
absl::strings)
4445

4546
opencensus_lib(

opencensus/tags/internal/grpc_tags_bin.cc

Lines changed: 10 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -20,51 +20,22 @@
2020
#include <vector>
2121

2222
#include "absl/strings/string_view.h"
23+
#include "opencensus/common/internal/varint.h"
2324
#include "opencensus/tags/tag_key.h"
2425
#include "opencensus/tags/tag_map.h"
2526

27+
using opencensus::common::AppendVarint32;
28+
using opencensus::common::ParseVarint32;
29+
2630
namespace opencensus {
2731
namespace tags {
2832
namespace propagation {
29-
3033
namespace {
3134

3235
constexpr char kVersionId = '\0';
3336
constexpr char kTagFieldId = '\0';
3437
constexpr int kMaxLen = 8192;
3538

36-
// Appends a variable-length encoded integer to the destination string.
37-
void AppendVarint(unsigned int i, std::string* out) {
38-
do {
39-
// Encode 7 bits.
40-
uint8_t c = i & 0x7F;
41-
i = i >> 7;
42-
if (i != 0) {
43-
c |= 0x80;
44-
}
45-
out->push_back(c);
46-
} while (i != 0);
47-
}
48-
49-
// Parses a variable-length encoded integer from the input. Returns false on
50-
// failure. Returns true and consumes the bytes from the input, on success.
51-
bool ParseVarint(absl::string_view* input, int* out) {
52-
absl::string_view s = *input;
53-
int i = 0;
54-
uint8_t c;
55-
do {
56-
if (s.empty()) {
57-
return false; // Too short.
58-
}
59-
c = s[0];
60-
s = s.substr(1);
61-
i = (i << 7) | (c & 0x7F);
62-
} while (c & 0x80);
63-
*input = s;
64-
*out = i;
65-
return true;
66-
}
67-
6839
} // namespace
6940

7041
bool FromGrpcTagsBinHeader(absl::string_view header, TagMap* out) {
@@ -89,8 +60,8 @@ bool FromGrpcTagsBinHeader(absl::string_view header, TagMap* out) {
8960
// Parse key.
9061
absl::string_view key;
9162
{
92-
int key_len;
93-
if (!ParseVarint(&header, &key_len)) {
63+
uint32_t key_len;
64+
if (!ParseVarint32(&header, &key_len)) {
9465
return false; // Invalid key_len.
9566
}
9667
if (key_len > header.length()) {
@@ -103,8 +74,8 @@ bool FromGrpcTagsBinHeader(absl::string_view header, TagMap* out) {
10374
// Parse val.
10475
absl::string_view val;
10576
{
106-
int val_len;
107-
if (!ParseVarint(&header, &val_len)) {
77+
uint32_t val_len;
78+
if (!ParseVarint32(&header, &val_len)) {
10879
return false; // Invalid val_len.
10980
}
11081
if (val_len > header.length()) {
@@ -138,9 +109,9 @@ std::string ToGrpcTagsBinHeader(const TagMap& tags) {
138109
const auto& key = key_val.first;
139110
const auto& val = key_val.second;
140111
out.push_back(kTagFieldId);
141-
AppendVarint(key.name().length(), &out);
112+
AppendVarint32(key.name().length(), &out);
142113
out.append(key.name());
143-
AppendVarint(val.length(), &out);
114+
AppendVarint32(val.length(), &out);
144115
// Encoded value must be UTF-8.
145116
out.append(val);
146117
if (out.size() > kMaxLen) {

opencensus/tags/internal/grpc_tags_bin_test.cc

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,14 @@ TEST(GrpcTagsBinTest, Serialize) {
140140
ToGrpcTagsBinHeader(m));
141141
}
142142

143+
TEST(GrpcTagsBinTest, SerializeLong) {
144+
TagMap m1({{TagKey::Register(std::string(300, 'A')), std::string(400, 'B')}});
145+
const std::string s = ToGrpcTagsBinHeader(m1);
146+
TagMap m2({});
147+
EXPECT_TRUE(FromGrpcTagsBinHeader(s, &m2));
148+
EXPECT_THAT(m1.tags(), ::testing::ContainerEq(m2.tags()));
149+
}
150+
143151
TEST(GrpcTagsBinTest, SerializeTooLong) {
144152
std::vector<std::pair<opencensus::tags::TagKey, std::string>> tags;
145153
constexpr int kValLen = 20;

0 commit comments

Comments
 (0)