Skip to content

Commit c77ddb0

Browse files
authored
Add circular_queue and time_based_counter classes. (istio#51)
* Add circular_queue and time_based_counter classes. * Fix format
1 parent 95c1cfa commit c77ddb0

6 files changed

+362
-0
lines changed

mixerclient/BUILD

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,9 +37,12 @@ cc_library(
3737
"src/signature.h",
3838
"src/stream_transport.h",
3939
"src/transport.h",
40+
"utils/circular_queue.h",
4041
"utils/md5.cc",
4142
"utils/md5.h",
4243
"utils/status_test_util.h",
44+
"utils/time_based_counter.cc",
45+
"utils/time_based_counter.h",
4346
],
4447
hdrs = [
4548
"include/attribute.h",
@@ -125,6 +128,28 @@ cc_test(
125128
],
126129
)
127130

131+
cc_test(
132+
name = "circular_queue_test",
133+
size = "small",
134+
srcs = ["utils/circular_queue_test.cc"],
135+
linkstatic = 1,
136+
deps = [
137+
":mixer_client_lib",
138+
"//external:googletest_main",
139+
],
140+
)
141+
142+
cc_test(
143+
name = "time_based_counter_test",
144+
size = "small",
145+
srcs = ["utils/time_based_counter_test.cc"],
146+
linkstatic = 1,
147+
deps = [
148+
":mixer_client_lib",
149+
"//external:googletest_main",
150+
],
151+
)
152+
128153
cc_test(
129154
name = "cache_key_set_test",
130155
size = "small",

mixerclient/utils/circular_queue.h

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
/* Copyright 2017 Istio Authors. All Rights Reserved.
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+
16+
#ifndef MIXER_CLIENT_UTILS_CIRCULAR_QUEUE_H_
17+
#define MIXER_CLIENT_UTILS_CIRCULAR_QUEUE_H_
18+
19+
#include <functional>
20+
#include <vector>
21+
22+
namespace istio {
23+
namespace mixer_client {
24+
25+
// Define a circular FIFO queue
26+
// Supported classes should support copy operator.
27+
template <class T>
28+
class CircularQueue {
29+
public:
30+
explicit CircularQueue(int size);
31+
32+
// Push an item to the tail
33+
void Push(const T& v);
34+
35+
// Pop up an item from the head
36+
void Pop();
37+
38+
// Allow modifying the head item.
39+
T* Mutable_Head();
40+
41+
// Calls the fn function for each element from head to tail.
42+
void Iterate(std::function<bool(const T&)> fn) const;
43+
44+
private:
45+
std::vector<T> nodes_;
46+
int head_;
47+
int tail_;
48+
int count_;
49+
};
50+
51+
template <class T>
52+
CircularQueue<T>::CircularQueue(int size)
53+
: nodes_(size), head_(0), tail_(0), count_(0) {}
54+
55+
template <class T>
56+
void CircularQueue<T>::Push(const T& v) {
57+
if (head_ == tail_ && count_ > 0) {
58+
size_t size = nodes_.size();
59+
nodes_.resize(size * 2);
60+
for (int i = 0; i <= head_; i++) {
61+
// Use the copy operator of class T
62+
nodes_[size + i] = nodes_[i];
63+
}
64+
tail_ += size;
65+
}
66+
nodes_[tail_] = v;
67+
tail_ = (tail_ + 1) % nodes_.size();
68+
count_++;
69+
}
70+
71+
template <class T>
72+
void CircularQueue<T>::Pop() {
73+
if (count_ == 0) return;
74+
head_ = (head_ + 1) % nodes_.size();
75+
count_--;
76+
}
77+
78+
template <class T>
79+
T* CircularQueue<T>::Mutable_Head() {
80+
if (count_ == 0) return nullptr;
81+
return &nodes_[head_];
82+
}
83+
84+
template <class T>
85+
void CircularQueue<T>::Iterate(std::function<bool(const T&)> fn) const {
86+
if (count_ == 0) return;
87+
int i = head_;
88+
while (i != tail_) {
89+
if (!fn(nodes_[i])) return;
90+
i = (i + 1) % nodes_.size();
91+
}
92+
}
93+
94+
} // namespace mixer_client
95+
} // namespace istio
96+
97+
#endif // MIXER_CLIENT_CLIENT_UTILS_CIRCULAR_QUEUE_H_
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
/* Copyright 2017 Istio Authors. All Rights Reserved.
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+
16+
#include "circular_queue.h"
17+
#include "gtest/gtest.h"
18+
19+
namespace istio {
20+
namespace mixer_client {
21+
namespace {
22+
23+
void ASSERT_RESULT(const CircularQueue<int>& q,
24+
const std::vector<int>& expected) {
25+
std::vector<int> v;
26+
q.Iterate([&](const int& i) -> bool {
27+
v.push_back(i);
28+
return true;
29+
});
30+
ASSERT_EQ(v, expected);
31+
}
32+
33+
TEST(CircularQueueTest, TestNotResize) {
34+
CircularQueue<int> q(5);
35+
q.Push(1);
36+
q.Push(2);
37+
q.Push(3);
38+
ASSERT_RESULT(q, {1, 2, 3});
39+
40+
q.Pop();
41+
q.Pop();
42+
q.Push(4);
43+
q.Push(5);
44+
q.Push(6);
45+
ASSERT_RESULT(q, {3, 4, 5, 6});
46+
}
47+
48+
TEST(CircularQueueTest, TestResize1) {
49+
CircularQueue<int> q(3);
50+
for (int i = 1; i < 6; i++) {
51+
q.Push(i);
52+
}
53+
ASSERT_RESULT(q, {1, 2, 3, 4, 5});
54+
}
55+
56+
TEST(CircularQueueTest, TestResize2) {
57+
CircularQueue<int> q(3);
58+
59+
// move head and tail
60+
q.Push(1);
61+
q.Push(2);
62+
q.Push(3);
63+
q.Pop();
64+
q.Pop();
65+
66+
for (int i = 4; i < 10; i++) {
67+
q.Push(i);
68+
}
69+
ASSERT_RESULT(q, {3, 4, 5, 6, 7, 8, 9});
70+
}
71+
72+
} // namespace
73+
} // namespace mixer_client
74+
} // namespace istio
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
/* Copyright 2017 Istio Authors. All Rights Reserved.
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+
16+
#include "time_based_counter.h"
17+
18+
namespace istio {
19+
namespace mixer_client {
20+
21+
TimeBasedCounter::TimeBasedCounter(int window_size,
22+
std::chrono::nanoseconds duration, Tick t)
23+
: slots_(window_size),
24+
slot_duration_(duration / window_size),
25+
count_(0),
26+
tail_(0),
27+
last_time_(t) {}
28+
29+
void TimeBasedCounter::Clear(Tick t) {
30+
last_time_ = t;
31+
for (size_t i = 0; i < slots_.size(); i++) {
32+
slots_[i] = 0;
33+
}
34+
tail_ = count_ = 0;
35+
}
36+
37+
void TimeBasedCounter::Roll(Tick t) {
38+
std::chrono::nanoseconds d = t - last_time_;
39+
uint32_t n = uint32_t(d / slot_duration_);
40+
if (n >= slots_.size()) {
41+
Clear(t);
42+
return;
43+
}
44+
45+
for (uint32_t i = 0; i < n; i++) {
46+
tail_ = (tail_ + 1) % slots_.size();
47+
count_ -= slots_[tail_];
48+
slots_[tail_] = 0;
49+
last_time_ += slot_duration_;
50+
}
51+
}
52+
53+
void TimeBasedCounter::Inc(Tick t) {
54+
Roll(t);
55+
slots_[tail_]++;
56+
count_++;
57+
}
58+
59+
int TimeBasedCounter::Count(Tick t) {
60+
Roll(t);
61+
return count_;
62+
}
63+
64+
} // namespace mixer_client
65+
} // namespace istio
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
/* Copyright 2017 Istio Authors. All Rights Reserved.
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+
16+
#ifndef MIXER_CLIENT_UTILS_TIME_BASED_COUNTER_H_
17+
#define MIXER_CLIENT_UTILS_TIME_BASED_COUNTER_H_
18+
19+
#include <chrono>
20+
#include <vector>
21+
22+
namespace istio {
23+
namespace mixer_client {
24+
25+
// Define a counter for the count in a time based window.
26+
// Each count is associated with a time stamp. The count outside
27+
// of the window will not be counted.
28+
class TimeBasedCounter {
29+
public:
30+
// Define a time type
31+
typedef std::chrono::time_point<std::chrono::system_clock> Tick;
32+
33+
TimeBasedCounter(int window_size, std::chrono::nanoseconds duration, Tick t);
34+
35+
// Ideally, Now() timestamp should be inside the functions.
36+
// But for easy unit_test, pass the time in.
37+
// The input time should be always increasing.
38+
39+
// Add a count
40+
void Inc(Tick t);
41+
42+
// Get the count.
43+
int Count(Tick t);
44+
45+
private:
46+
// Clear the whole window
47+
void Clear(Tick t);
48+
// Roll the window
49+
void Roll(Tick t);
50+
51+
std::vector<int> slots_;
52+
std::chrono::nanoseconds slot_duration_;
53+
int count_;
54+
int tail_;
55+
Tick last_time_;
56+
};
57+
58+
} // namespace mixer_client
59+
} // namespace istio
60+
61+
#endif // MIXER_CLIENT_CLIENT_UTILS_TIME_BASED_COUNTER_H_
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
/* Copyright 2017 Istio Authors. All Rights Reserved.
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+
16+
#include "time_based_counter.h"
17+
#include "gtest/gtest.h"
18+
19+
namespace istio {
20+
namespace mixer_client {
21+
namespace {
22+
23+
std::chrono::time_point<std::chrono::system_clock> FakeTime(int t) {
24+
return std::chrono::time_point<std::chrono::system_clock>(
25+
std::chrono::nanoseconds(t));
26+
}
27+
28+
TEST(TimeBasedCounterTest, Test1) {
29+
TimeBasedCounter c(3, std::chrono::nanoseconds(3), FakeTime(0));
30+
c.Inc(FakeTime(4));
31+
c.Inc(FakeTime(5));
32+
c.Inc(FakeTime(7));
33+
34+
// Current slots are 6, 7, 8. and 4 and 5 are out.
35+
ASSERT_EQ(c.Count(FakeTime(8)), 1);
36+
}
37+
38+
} // namespace
39+
} // namespace mixer_client
40+
} // namespace istio

0 commit comments

Comments
 (0)