mirror of
https://github.com/openappsec/openappsec.git
synced 2025-09-29 11:16:30 +03:00
First release of open-appsec source code
This commit is contained in:
3
core/buffers/CMakeLists.txt
Normal file
3
core/buffers/CMakeLists.txt
Normal file
@@ -0,0 +1,3 @@
|
||||
add_library(buffers buffer.cc char_iterator.cc data_container.cc segment.cc buffer_eval.cc)
|
||||
|
||||
add_subdirectory(buffers_ut)
|
530
core/buffers/buffer.cc
Executable file
530
core/buffers/buffer.cc
Executable file
@@ -0,0 +1,530 @@
|
||||
// Copyright (C) 2022 Check Point Software Technologies Ltd. All rights reserved.
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "buffer.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
using namespace std;
|
||||
|
||||
Buffer::Buffer(vector<u_char> &&vec)
|
||||
:
|
||||
len(vec.size())
|
||||
{
|
||||
if (len != 0) {
|
||||
segs.push_back(Segment(move(vec)));
|
||||
evalFastPath();
|
||||
}
|
||||
}
|
||||
|
||||
Buffer::Buffer(const vector<u_char> &vec)
|
||||
:
|
||||
Buffer(vec.data(), vec.size(), MemoryType::OWNED)
|
||||
{}
|
||||
|
||||
Buffer::Buffer(const vector<char> &vec)
|
||||
:
|
||||
Buffer(reinterpret_cast<const u_char *>(vec.data()), vec.size(), MemoryType::OWNED)
|
||||
{}
|
||||
|
||||
Buffer::Buffer(const string &str)
|
||||
:
|
||||
Buffer(reinterpret_cast<const u_char *>(str.data()), str.size(), MemoryType::OWNED)
|
||||
{}
|
||||
|
||||
Buffer::Buffer(const u_char *_ptr, uint _len, MemoryType type)
|
||||
:
|
||||
len(_len)
|
||||
{
|
||||
if (len != 0) {
|
||||
segs.emplace_back(_ptr, _len, type);
|
||||
evalFastPath();
|
||||
}
|
||||
}
|
||||
|
||||
Buffer::Buffer(const char *_ptr, uint _len, MemoryType type)
|
||||
:
|
||||
Buffer(reinterpret_cast<const u_char *>(_ptr), _len, type)
|
||||
{}
|
||||
|
||||
Buffer::Buffer(const Buffer &buf)
|
||||
:
|
||||
segs(buf.segs),
|
||||
len(buf.len)
|
||||
{
|
||||
evalFastPath();
|
||||
}
|
||||
|
||||
Buffer::Buffer(Buffer &&buf)
|
||||
:
|
||||
segs(move(buf.segs))
|
||||
{
|
||||
len = buf.len;
|
||||
evalFastPath();
|
||||
|
||||
buf.len = 0;
|
||||
buf.evalFastPath();
|
||||
}
|
||||
|
||||
Buffer &
|
||||
Buffer::operator=(const Buffer &buf)
|
||||
{
|
||||
segs = buf.segs;
|
||||
len = buf.len;
|
||||
evalFastPath();
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
Buffer &
|
||||
Buffer::operator=(Buffer &&buf)
|
||||
{
|
||||
segs = move(buf.segs);
|
||||
len = buf.len;
|
||||
evalFastPath();
|
||||
|
||||
buf.len = 0;
|
||||
buf.evalFastPath();
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool
|
||||
Buffer::contains(char ch) const
|
||||
{
|
||||
for (const auto &iter : *this) {
|
||||
if (iter == ch) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
uint
|
||||
Buffer::segmentsNumber() const
|
||||
{
|
||||
return segs.size();
|
||||
}
|
||||
|
||||
void
|
||||
Buffer::operator+=(const Buffer &other)
|
||||
{
|
||||
if (other.len == 0) return;
|
||||
segs.insert(segs.end(), other.segs.begin(), other.segs.end());
|
||||
len += other.len;
|
||||
|
||||
// If the buffer was originally empty (and had no segments), fast path needs to be evaluated.
|
||||
// This can be detected by the fact that the length of the buffer is equal to the length of `other`.
|
||||
if (len == other.len) evalFastPath();
|
||||
}
|
||||
|
||||
Buffer
|
||||
Buffer::operator+(const Buffer &other) const
|
||||
{
|
||||
Buffer res;
|
||||
res.segs.reserve(segmentsNumber() + other.segmentsNumber());
|
||||
res.segs.insert(res.segs.end(), segs.begin(), segs.end());
|
||||
res.segs.insert(res.segs.end(), other.segs.begin(), other.segs.end());
|
||||
res.len = len + other.len;
|
||||
res.evalFastPath();
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
Buffer
|
||||
Buffer::getSubBuffer(uint start, uint end) const
|
||||
{
|
||||
dbgAssert(start<=end && end<=len) << "Buffer::getSubBuffer() returned: Illegal scoping of buffer";
|
||||
if (start == end) return Buffer();
|
||||
|
||||
Buffer res;
|
||||
uint offset = 0;
|
||||
for (const auto &seg : segs) {
|
||||
uint seg_end = offset + seg.size();
|
||||
if (seg_end <= start) {
|
||||
offset = seg_end;
|
||||
continue;
|
||||
}
|
||||
res.segs.push_back(seg);
|
||||
if (offset < start) {
|
||||
auto remove = start - offset;
|
||||
res.segs.back().len -= remove;
|
||||
res.segs.back().offset += remove;
|
||||
res.segs.back().ptr += remove;
|
||||
}
|
||||
if (seg_end > end) {
|
||||
auto remove = seg_end - end;
|
||||
res.segs.back().len -= remove;
|
||||
}
|
||||
if (seg_end >= end) break;
|
||||
offset = seg_end;
|
||||
}
|
||||
res.len = end - start;
|
||||
|
||||
res.evalFastPath();
|
||||
return res;
|
||||
}
|
||||
|
||||
Maybe<uint>
|
||||
Buffer::findFirstOf(char ch, uint start) const
|
||||
{
|
||||
dbgAssert(start <= len) << "Buffer::findFirstOf() returned: Cannot set a start point after buffer's end";
|
||||
|
||||
for (; start < len; ++start) {
|
||||
if ((*this)[start] == ch) return start;
|
||||
}
|
||||
return genError("Not located");
|
||||
}
|
||||
|
||||
Maybe<uint>
|
||||
Buffer::findFirstOf(const Buffer &buf, uint start) const
|
||||
{
|
||||
dbgAssert(start <= len) << "Buffer::findFirstOf() returned: Cannot set a start point after buffer's end";
|
||||
|
||||
for (; start + buf.size() <= len; ++start) {
|
||||
auto sub_buffer = getSubBuffer(start, start + buf.size());
|
||||
if (sub_buffer == buf) return start;
|
||||
}
|
||||
return genError("Not located");
|
||||
}
|
||||
|
||||
Maybe<uint>
|
||||
Buffer::findFirstNotOf(char ch, uint start) const
|
||||
{
|
||||
dbgAssert(start <= len) << "Buffer::findFirstNotOf() returned: Cannot set a start point after buffer's end";
|
||||
for (; start < len; ++start) {
|
||||
if ((*this)[start] != ch) return start;
|
||||
}
|
||||
return genError("Everything is the same ch");
|
||||
}
|
||||
|
||||
Maybe<uint>
|
||||
Buffer::findLastOf(char ch, uint start) const
|
||||
{
|
||||
dbgAssert(start <= len) << "Buffer::findLastOf() returned: Cannot set a start point after buffer's end";
|
||||
for (; 0 < start; --start) {
|
||||
if ((*this)[start - 1] == ch) return start - 1;
|
||||
}
|
||||
return genError("Not located");
|
||||
}
|
||||
|
||||
Maybe<uint>
|
||||
Buffer::findLastNotOf(char ch, uint start) const
|
||||
{
|
||||
dbgAssert(start <= len) << "Buffer::findLastNotOf() returned: Cannot set a start point after buffer's end";
|
||||
for (; 0 < start; --start) {
|
||||
if ((*this)[start - 1] != ch) return start - 1;
|
||||
}
|
||||
return genError("Everything is the same ch");
|
||||
}
|
||||
|
||||
void
|
||||
Buffer::truncateHead(uint size)
|
||||
{
|
||||
dbgAssert(size <= len) << "Cannot set a new start of buffer after the buffer's end";
|
||||
if (size == 0) return;
|
||||
if (size == len) {
|
||||
clear();
|
||||
return;
|
||||
}
|
||||
|
||||
while (segs.front().size() <= size) {
|
||||
size -= segs.front().size();
|
||||
len -= segs.front().size();
|
||||
segs.erase(segs.begin());
|
||||
}
|
||||
|
||||
if (size > 0) {
|
||||
len -= size;
|
||||
segs.front().offset += size;
|
||||
segs.front().len -= size;
|
||||
segs.front().ptr += size;
|
||||
}
|
||||
|
||||
evalFastPath();
|
||||
}
|
||||
|
||||
void
|
||||
Buffer::truncateTail(uint size)
|
||||
{
|
||||
dbgAssert(size <= len) << "Cannot set a new end of buffer after the buffer's end";
|
||||
if (size == 0) return;
|
||||
if (size == len) {
|
||||
clear();
|
||||
return;
|
||||
}
|
||||
|
||||
while (segs.back().size() <= size) {
|
||||
size -= segs.back().size();
|
||||
len -= segs.back().size();
|
||||
segs.pop_back();
|
||||
}
|
||||
|
||||
if (size > 0) {
|
||||
len -= size;
|
||||
segs.back().len -= size;
|
||||
}
|
||||
|
||||
// Special case in which fixing fast path is really easy (as the first segment didn't change)
|
||||
if (len < fast_path_len) fast_path_len = len;
|
||||
}
|
||||
void
|
||||
Buffer::keepHead(uint size)
|
||||
{
|
||||
dbgAssert(size <= len) << "Cannot set a new end of buffer before the buffer's start";
|
||||
truncateTail(len - size);
|
||||
}
|
||||
|
||||
void
|
||||
Buffer::keepTail(uint size)
|
||||
{
|
||||
dbgAssert(size <= len) << "Cannot set a new start of buffer after the buffer's end";
|
||||
truncateHead(len - size);
|
||||
}
|
||||
|
||||
void
|
||||
Buffer::clear()
|
||||
{
|
||||
segs.clear();
|
||||
len = 0;
|
||||
evalFastPath();
|
||||
}
|
||||
|
||||
bool
|
||||
Buffer::operator==(const Buffer &buf) const
|
||||
{
|
||||
if (len != buf.len) return false;
|
||||
if (len == 0) return true;
|
||||
|
||||
// Initializing the iteration over `this` segments
|
||||
// Since the segments of the buffer may be unaliagned, `l_ptr` and `l_size` hold the current place in the
|
||||
// current segment and the size left in the segment.
|
||||
auto l_iter = segs.begin();
|
||||
auto l_ptr = l_iter->data();
|
||||
auto l_size = l_iter->size();
|
||||
// Same for `buf`
|
||||
auto r_iter = buf.segs.begin();
|
||||
auto r_ptr = r_iter->data();
|
||||
auto r_size = r_iter->size();
|
||||
// `offset` is the current offset in both buffers and is used to track the progess of the loop.
|
||||
uint offset = 0;
|
||||
|
||||
while (true) {
|
||||
uint curr_size = min(l_size, r_size); // Size to compare
|
||||
if (memcmp(l_ptr, r_ptr, curr_size) != 0) return false;
|
||||
offset += curr_size;
|
||||
|
||||
if (offset >= len) break;
|
||||
|
||||
if (l_size <= curr_size) { // Finished a segment of `this`
|
||||
l_iter++;
|
||||
l_ptr = l_iter->data();
|
||||
l_size = l_iter->size();
|
||||
} else { // Progress within the segment
|
||||
l_size -= curr_size;
|
||||
l_ptr += curr_size;
|
||||
}
|
||||
|
||||
if (r_size <= curr_size) { // Finished a segment of `buf`
|
||||
r_iter++;
|
||||
r_ptr = r_iter->data();
|
||||
r_size = r_iter->size();
|
||||
} else { // Progress within the segment
|
||||
r_size -= curr_size;
|
||||
r_ptr += curr_size;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
Buffer::isEqual(const u_char *ptr, uint size) const
|
||||
{
|
||||
if (len != size) return false;
|
||||
for (const auto &seg : segs) {
|
||||
if (memcmp(ptr, seg.data(), seg.size()) != 0) return false;
|
||||
ptr += seg.size();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
Buffer::isEqualLowerCase(const Buffer &buf) const
|
||||
{
|
||||
if (len != buf.size()) return false;
|
||||
for (uint i = 0; i < len; i++) {
|
||||
if (tolower((*this)[i]) != buf[i]) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
const u_char &
|
||||
Buffer::operator[](uint offset) const
|
||||
{
|
||||
if (offset < fast_path_len) {
|
||||
if (is_owned!=nullptr && *is_owned) {
|
||||
evalFastPath();
|
||||
}
|
||||
return fast_path_ptr[offset];
|
||||
}
|
||||
dbgAssert(offset < len) << "Buffer::operator returned: attempted an access outside the buffer";
|
||||
return *(begin() + offset);
|
||||
}
|
||||
|
||||
Buffer::operator string() const
|
||||
{
|
||||
serialize();
|
||||
return string(reinterpret_cast<const char *>(fast_path_ptr), fast_path_len);
|
||||
}
|
||||
|
||||
Maybe<Buffer::InternalPtr<u_char>>
|
||||
Buffer::getPtr(uint start, uint get_len) const
|
||||
{
|
||||
auto end = start + get_len;
|
||||
if (end > len) return genError("Cannot get internal pointer beyond the buffer limits");
|
||||
if (start >= end) return genError("Invalid length ('start' is not smaller than 'end')");
|
||||
|
||||
if (end <= fast_path_len) {
|
||||
if (is_owned!=nullptr && *is_owned) {
|
||||
evalFastPath();
|
||||
}
|
||||
return Buffer::InternalPtr<u_char>(fast_path_ptr + start, segs.front().data_container);
|
||||
}
|
||||
|
||||
// Search the segments for the one that contains the requested data.
|
||||
uint offset = 0;
|
||||
for (auto &seg : segs) {
|
||||
uint seg_end = offset + seg.size();
|
||||
if (seg_end < start) { // We haven't reached the segment yet
|
||||
offset = seg_end;
|
||||
continue;
|
||||
}
|
||||
if (seg_end < end) break; // The data isn't contained entirely in one segment, serialization is needed.
|
||||
|
||||
return Buffer::InternalPtr<u_char>(seg.ptr + start - offset, seg.data_container);
|
||||
}
|
||||
|
||||
serialize();
|
||||
return Buffer::InternalPtr<u_char>(fast_path_ptr + start, segs.front().data_container);
|
||||
}
|
||||
|
||||
void
|
||||
Buffer::serialize() const
|
||||
{
|
||||
if (segmentsNumber() < 2) {
|
||||
evalFastPath();
|
||||
return;
|
||||
}
|
||||
// While `serialize` doesn't change the content of the buffer, it does changes the way it is organized internally.
|
||||
// Since we want to allow accesors be `const`, and hide from the user the fact that they require changing the
|
||||
// internal structure of the buffer - `serialize` needs to be able to change a `const` buffer, hence the dangarous
|
||||
// `const_cast` on `this`.
|
||||
auto &segments = const_cast<Buffer *>(this)->segs;
|
||||
vector<u_char> vec;
|
||||
vec.reserve(len);
|
||||
for (auto iter : segments) {
|
||||
vec.insert(vec.end(), iter.data(), iter.data() + iter.size());
|
||||
}
|
||||
segments.clear();
|
||||
segments.push_back(Segment(move(vec)));
|
||||
evalFastPath();
|
||||
}
|
||||
|
||||
Buffer::CharIterator
|
||||
Buffer::begin() const
|
||||
{
|
||||
return len == 0 ? CharIterator(segs.end()) : CharIterator(segs.begin(), segs.end(), 0);
|
||||
}
|
||||
|
||||
Buffer::CharIterator
|
||||
Buffer::end() const
|
||||
{
|
||||
return CharIterator(segs.end());
|
||||
}
|
||||
|
||||
Buffer::SegRange
|
||||
Buffer::segRange() const
|
||||
{
|
||||
return SegRange(segs.begin(), segs.end());
|
||||
}
|
||||
|
||||
void
|
||||
Buffer::evalFastPath() const
|
||||
{
|
||||
// While `evalFastPath` doesn't change the content of the buffer, it does re-evaluate the fast path elements.
|
||||
// Since we can detect such change in a `const` method, `evalFastPath` needs to be able to change fast path
|
||||
// parameters of a `const` buffer - hence the dangarous `const_cast` on `this`.
|
||||
auto non_const_this = const_cast<Buffer *>(this);
|
||||
|
||||
if (segs.size() != 0) {
|
||||
auto &seg = segs.front();
|
||||
non_const_this->fast_path_len = seg.size();
|
||||
non_const_this->fast_path_ptr = seg.data();
|
||||
non_const_this->type = seg.type;
|
||||
non_const_this->is_owned = seg.is_owned;
|
||||
} else {
|
||||
non_const_this->fast_path_len = 0;
|
||||
non_const_this->fast_path_ptr = nullptr;
|
||||
non_const_this->type = Volatility::NONE;
|
||||
non_const_this->is_owned = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
Buffer::operator<(const Buffer &buf) const
|
||||
{
|
||||
if (len != buf.len) return len < buf.len;
|
||||
if (len == 0) return false;
|
||||
|
||||
// Initializing the iteration over `this` segments
|
||||
// Since the segments of the buffer may be unaliagned, `l_ptr` and `l_size` hold the current place in the
|
||||
// current segment and the size left in the segment.
|
||||
auto l_iter = segs.begin();
|
||||
auto l_ptr = l_iter->data();
|
||||
auto l_size = l_iter->size();
|
||||
// Same for `buf`
|
||||
auto r_iter = buf.segs.begin();
|
||||
auto r_ptr = r_iter->data();
|
||||
auto r_size = r_iter->size();
|
||||
// `offset` is the current offset in both buffers and is used to track the progess of the loop.
|
||||
uint offset = 0;
|
||||
|
||||
while (true) {
|
||||
uint curr_size = min(l_size, r_size); // Size to compare
|
||||
int compare_result = memcmp(l_ptr, r_ptr, curr_size);
|
||||
if (compare_result < 0) return true;
|
||||
if (compare_result > 0) return false;
|
||||
offset += curr_size;
|
||||
|
||||
if (offset >= len) break;
|
||||
|
||||
if (l_size <= curr_size) { // Finished a segment of `this`
|
||||
l_iter++;
|
||||
l_ptr = l_iter->data();
|
||||
l_size = l_iter->size();
|
||||
} else { // Progress within the segment
|
||||
l_size -= curr_size;
|
||||
l_ptr += curr_size;
|
||||
}
|
||||
|
||||
if (r_size <= curr_size) { // Finished a segment of `buf`
|
||||
r_iter++;
|
||||
r_ptr = r_iter->data();
|
||||
r_size = r_iter->size();
|
||||
} else { // Progress within the segment
|
||||
r_size -= curr_size;
|
||||
r_ptr += curr_size;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
43
core/buffers/buffer_eval.cc
Normal file
43
core/buffers/buffer_eval.cc
Normal file
@@ -0,0 +1,43 @@
|
||||
// Copyright (C) 2022 Check Point Software Technologies Ltd. All rights reserved.
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "buffer.h"
|
||||
#include "environment/evaluator_templates.h"
|
||||
|
||||
using namespace EnvironmentHelper;
|
||||
|
||||
class ConstantBuffer : public Constant<Buffer>
|
||||
{
|
||||
public:
|
||||
ConstantBuffer(const std::vector<std::string> ¶ms)
|
||||
:
|
||||
Constant<Buffer>(
|
||||
[] (const std::string &str) { return Buffer(str); },
|
||||
params
|
||||
) {}
|
||||
static std::string getName() { return Constant<Buffer>::getName() + "Buffer"; }
|
||||
};
|
||||
|
||||
class EqualBuffer : public Equal<Buffer>
|
||||
{
|
||||
public:
|
||||
EqualBuffer(const std::vector<std::string> ¶ms) : Equal<Buffer>(params) {}
|
||||
static std::string getName() { return Equal<Buffer>::getName() + "Buffer"; }
|
||||
};
|
||||
|
||||
void
|
||||
Buffer::preload()
|
||||
{
|
||||
addMatcher<ConstantBuffer>();
|
||||
addMatcher<EqualBuffer>();
|
||||
}
|
7
core/buffers/buffers_ut/CMakeLists.txt
Normal file
7
core/buffers/buffers_ut/CMakeLists.txt
Normal file
@@ -0,0 +1,7 @@
|
||||
link_directories(${BOOST_ROOT}/lib)
|
||||
|
||||
add_unit_test(
|
||||
buffers_ut
|
||||
"buffers_ut.cc;buffer_eval_ut.cc"
|
||||
"buffers;event_is;metric;-lboost_regex"
|
||||
)
|
54
core/buffers/buffers_ut/buffer_eval_ut.cc
Normal file
54
core/buffers/buffers_ut/buffer_eval_ut.cc
Normal file
@@ -0,0 +1,54 @@
|
||||
#include "buffer.h"
|
||||
#include "cptest.h"
|
||||
#include "cptest.h"
|
||||
#include "environment.h"
|
||||
#include "singleton.h"
|
||||
#include "environment_evaluator.h"
|
||||
#include "config.h"
|
||||
#include "config_component.h"
|
||||
#include "mock/mock_mainloop.h"
|
||||
#include "mock/mock_time_get.h"
|
||||
|
||||
using namespace std;
|
||||
using namespace testing;
|
||||
|
||||
class BuffferEval : public Test
|
||||
{
|
||||
public:
|
||||
BuffferEval()
|
||||
{
|
||||
env.preload();
|
||||
Buffer::preload();
|
||||
env.init();
|
||||
auto i_env = Singleton::Consume<I_Environment>::from(env);
|
||||
i_env->getConfigurationContext().registerValue("buf_a", buf_a);
|
||||
i_env->getConfigurationContext().registerValue("buf_b", buf_b);
|
||||
}
|
||||
|
||||
NiceMock<MockMainLoop> mock_mainloop;
|
||||
NiceMock<MockTimeGet> mock_timer;
|
||||
ConfigComponent conf;
|
||||
::Environment env;
|
||||
Buffer buf_a{"aaa"};
|
||||
Buffer buf_b{"bbb"};
|
||||
};
|
||||
|
||||
static ostream & operator<<(ostream &os, const Context::Error &) { return os; }
|
||||
|
||||
TEST_F(BuffferEval, compare)
|
||||
{
|
||||
auto eval_eq = genEvaluator<bool>("EqualBuffer(Get(buf_a), Get(buf_a))");
|
||||
EXPECT_TRUE(eval_eq.ok());
|
||||
EXPECT_THAT((*eval_eq)(), IsValue(true));
|
||||
|
||||
auto eval_nq = genEvaluator<bool>("EqualBuffer(Get(buf_a), Get(buf_b))");
|
||||
EXPECT_TRUE(eval_nq.ok());
|
||||
EXPECT_THAT((*eval_nq)(), IsValue(false));
|
||||
}
|
||||
|
||||
TEST_F(BuffferEval, constant)
|
||||
{
|
||||
auto const_a = genEvaluator<Buffer>("ConstantBuffer(aaa)");
|
||||
EXPECT_TRUE(const_a.ok());
|
||||
EXPECT_THAT((*const_a)(), IsValue(buf_a));
|
||||
}
|
733
core/buffers/buffers_ut/buffers_ut.cc
Normal file
733
core/buffers/buffers_ut/buffers_ut.cc
Normal file
@@ -0,0 +1,733 @@
|
||||
#include <string>
|
||||
|
||||
#include "cereal/archives/json.hpp"
|
||||
|
||||
#include "cptest.h"
|
||||
#include "debug.h"
|
||||
#include "buffer.h"
|
||||
|
||||
using namespace std;
|
||||
using namespace testing;
|
||||
|
||||
class BuffersTest : public Test
|
||||
{
|
||||
public:
|
||||
Buffer
|
||||
genBuf(string s1, string s2, string s3)
|
||||
{
|
||||
Buffer b1(s1), b2(s2), b3(s3);
|
||||
return b1 + b2 + b3;
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(BuffersTest, empty_buffer_dump)
|
||||
{
|
||||
Buffer buf;
|
||||
EXPECT_EQ(buf.size(), 0u);
|
||||
EXPECT_EQ(dumpHex(buf), "");
|
||||
}
|
||||
|
||||
TEST_F(BuffersTest, empty_buffer)
|
||||
{
|
||||
Buffer buf;
|
||||
EXPECT_EQ(buf.size(), 0u);
|
||||
}
|
||||
|
||||
TEST_F(BuffersTest, basic_content_string)
|
||||
{
|
||||
Buffer buf("123456789");
|
||||
EXPECT_EQ(buf.size(), 9u);
|
||||
EXPECT_EQ(buf[0], '1');
|
||||
EXPECT_EQ(buf[1], '2');
|
||||
EXPECT_EQ(buf[2], '3');
|
||||
EXPECT_EQ(buf[3], '4');
|
||||
EXPECT_EQ(buf[4], '5');
|
||||
EXPECT_EQ(buf[5], '6');
|
||||
EXPECT_EQ(buf[6], '7');
|
||||
EXPECT_EQ(buf[7], '8');
|
||||
EXPECT_EQ(buf[8], '9');
|
||||
}
|
||||
|
||||
TEST_F(BuffersTest, basic_content_uchar_vec)
|
||||
{
|
||||
vector<u_char> vec = { '1', '2', '3', '4', '5', '6', '7', '8', '9' };
|
||||
Buffer buf(vec);
|
||||
EXPECT_EQ(buf.size(), 9u);
|
||||
EXPECT_EQ(buf[0], '1');
|
||||
EXPECT_EQ(buf[1], '2');
|
||||
EXPECT_EQ(buf[2], '3');
|
||||
EXPECT_EQ(buf[3], '4');
|
||||
EXPECT_EQ(buf[4], '5');
|
||||
EXPECT_EQ(buf[5], '6');
|
||||
EXPECT_EQ(buf[6], '7');
|
||||
EXPECT_EQ(buf[7], '8');
|
||||
EXPECT_EQ(buf[8], '9');
|
||||
}
|
||||
|
||||
TEST_F(BuffersTest, basic_content_char_vec)
|
||||
{
|
||||
vector<char> vec = { '1', '2', '3', '4', '5', '6', '7', '8', '9' };
|
||||
Buffer buf(vec);
|
||||
EXPECT_EQ(buf.size(), 9u);
|
||||
EXPECT_EQ(buf[0], '1');
|
||||
EXPECT_EQ(buf[1], '2');
|
||||
EXPECT_EQ(buf[2], '3');
|
||||
EXPECT_EQ(buf[3], '4');
|
||||
EXPECT_EQ(buf[4], '5');
|
||||
EXPECT_EQ(buf[5], '6');
|
||||
EXPECT_EQ(buf[6], '7');
|
||||
EXPECT_EQ(buf[7], '8');
|
||||
EXPECT_EQ(buf[8], '9');
|
||||
}
|
||||
|
||||
TEST_F(BuffersTest, compare)
|
||||
{
|
||||
Buffer buf1("123456789");
|
||||
Buffer buf2("123456789");
|
||||
EXPECT_TRUE(buf1 == buf2);
|
||||
EXPECT_FALSE(buf1 != buf2);
|
||||
EXPECT_FALSE(buf1 < buf2);
|
||||
EXPECT_TRUE(buf1 <= buf2);
|
||||
EXPECT_FALSE(buf1 > buf2);
|
||||
EXPECT_TRUE(buf1 >= buf2);
|
||||
|
||||
Buffer buf3("12345678");
|
||||
EXPECT_FALSE(buf1 == buf3);
|
||||
EXPECT_TRUE(buf1 != buf3);
|
||||
EXPECT_FALSE(buf1 < buf3);
|
||||
EXPECT_TRUE(buf3 < buf1);
|
||||
EXPECT_FALSE(buf1 <= buf3);
|
||||
EXPECT_TRUE(buf3 <= buf1);
|
||||
EXPECT_TRUE(buf1 > buf3);
|
||||
EXPECT_FALSE(buf3 > buf1);
|
||||
EXPECT_TRUE(buf1 >= buf3);
|
||||
EXPECT_FALSE(buf3 >= buf1);
|
||||
|
||||
Buffer buf4("1234*6789");
|
||||
EXPECT_FALSE(buf1 == buf4);
|
||||
EXPECT_TRUE(buf1 != buf4);
|
||||
EXPECT_FALSE(buf1 < buf4);
|
||||
EXPECT_TRUE(buf4 < buf1);
|
||||
EXPECT_FALSE(buf1 <= buf4);
|
||||
EXPECT_TRUE(buf4 <= buf1);
|
||||
EXPECT_TRUE(buf1 > buf4);
|
||||
EXPECT_FALSE(buf4 > buf1);
|
||||
EXPECT_TRUE(buf1 >= buf4);
|
||||
EXPECT_FALSE(buf4 >= buf1);
|
||||
|
||||
Buffer buf5("1234067890");
|
||||
EXPECT_FALSE(buf1 == buf5);
|
||||
EXPECT_TRUE(buf1 != buf5);
|
||||
EXPECT_TRUE(buf1 < buf5);
|
||||
EXPECT_FALSE(buf5 < buf1);
|
||||
EXPECT_TRUE(buf1 <= buf5);
|
||||
EXPECT_FALSE(buf5 <= buf1);
|
||||
EXPECT_FALSE(buf1 > buf5);
|
||||
EXPECT_TRUE(buf5 > buf1);
|
||||
EXPECT_FALSE(buf1 >= buf5);
|
||||
EXPECT_TRUE(buf5 >= buf1);
|
||||
|
||||
Buffer buf6("");
|
||||
EXPECT_FALSE(buf1 < buf6);
|
||||
EXPECT_TRUE(buf6 < buf1);
|
||||
EXPECT_FALSE(buf1 <= buf6);
|
||||
EXPECT_TRUE(buf6 <= buf1);
|
||||
EXPECT_TRUE(buf1 > buf6);
|
||||
EXPECT_FALSE(buf6 > buf1);
|
||||
EXPECT_TRUE(buf1 >= buf6);
|
||||
EXPECT_FALSE(buf6 >= buf1);
|
||||
|
||||
Buffer buf7("");
|
||||
EXPECT_FALSE(buf7 < buf6);
|
||||
EXPECT_FALSE(buf6 < buf7);
|
||||
EXPECT_TRUE(buf7 <= buf6);
|
||||
EXPECT_TRUE(buf6 <= buf7);
|
||||
EXPECT_FALSE(buf7 > buf6);
|
||||
EXPECT_FALSE(buf6 > buf7);
|
||||
EXPECT_TRUE(buf7 >= buf6);
|
||||
EXPECT_TRUE(buf6 >= buf7);
|
||||
|
||||
}
|
||||
|
||||
TEST_F(BuffersTest, truncate_head)
|
||||
{
|
||||
Buffer buf("123456789");
|
||||
buf.truncateHead(6);
|
||||
EXPECT_EQ(buf, Buffer("789"));
|
||||
}
|
||||
|
||||
TEST_F(BuffersTest, truncate_tail)
|
||||
{
|
||||
Buffer buf("123456789");
|
||||
buf.truncateTail(4);
|
||||
EXPECT_EQ(buf, Buffer("12345"));
|
||||
}
|
||||
|
||||
TEST_F(BuffersTest, keep_head)
|
||||
{
|
||||
Buffer buf("123456789");
|
||||
buf.keepHead(6);
|
||||
EXPECT_EQ(buf, Buffer("123456"));
|
||||
}
|
||||
|
||||
TEST_F(BuffersTest, keep_tail)
|
||||
{
|
||||
Buffer buf("123456789");
|
||||
buf.keepTail(4);
|
||||
EXPECT_EQ(buf, Buffer("6789"));
|
||||
}
|
||||
|
||||
TEST_F(BuffersTest, slicing_final)
|
||||
{
|
||||
Buffer buf("123456789");
|
||||
Buffer b1 = buf, b2 = buf;
|
||||
b1.truncateHead(3); // "456789"
|
||||
b1.truncateTail(3); // "456"
|
||||
b2.truncateTail(3); // "123456"
|
||||
b2.truncateHead(3); // "456"
|
||||
EXPECT_EQ(b1, b2);
|
||||
b2.truncateHead(1); // "45"
|
||||
EXPECT_NE(b1, b2);
|
||||
}
|
||||
|
||||
TEST_F(BuffersTest, data)
|
||||
{
|
||||
Buffer buf("123456789");
|
||||
EXPECT_EQ(bcmp(buf.data(), "123456789", 9), 0);
|
||||
}
|
||||
|
||||
struct TestStruct
|
||||
{
|
||||
char first;
|
||||
char second;
|
||||
};
|
||||
|
||||
TEST_F(BuffersTest, casting)
|
||||
{
|
||||
Buffer buf("123456789");
|
||||
auto test = buf.getTypePtr<struct TestStruct>(2).unpack();
|
||||
EXPECT_EQ(test->first, '3');
|
||||
EXPECT_EQ(test->second, '4');
|
||||
}
|
||||
|
||||
TEST_F(BuffersTest, casting_fail)
|
||||
{
|
||||
Buffer buf("123456789");
|
||||
auto test = buf.getTypePtr<struct TestStruct>(8);
|
||||
EXPECT_THAT(test, IsError("Cannot get internal pointer beyond the buffer limits"));
|
||||
test = buf.getTypePtr<struct TestStruct>(-1);
|
||||
EXPECT_THAT(test, IsError("Invalid length ('start' is not smaller than 'end')"));
|
||||
test = buf.getTypePtr<struct TestStruct>(9);
|
||||
EXPECT_THAT(test, IsError("Cannot get internal pointer beyond the buffer limits"));
|
||||
}
|
||||
|
||||
TEST_F(BuffersTest, death_on_asserts)
|
||||
{
|
||||
cptestPrepareToDie();
|
||||
|
||||
Buffer buf1("123456789");
|
||||
EXPECT_DEATH(buf1[10], "Buffer::operator returned: attempted an access outside the buffer");
|
||||
EXPECT_DEATH(buf1[-1], "Buffer::operator returned: attempted an access outside the buffer");
|
||||
EXPECT_DEATH(buf1.truncateHead(10), "Cannot set a new start of buffer after the buffer's end");
|
||||
EXPECT_DEATH(buf1.truncateTail(10), "Cannot set a new end of buffer after the buffer's end");
|
||||
EXPECT_DEATH(buf1.keepHead(10), "Cannot set a new end of buffer before the buffer's start");
|
||||
EXPECT_DEATH(buf1.keepTail(10), "Cannot set a new start of buffer after the buffer's end");
|
||||
}
|
||||
|
||||
TEST_F(BuffersTest, basic_content2)
|
||||
{
|
||||
auto buf = genBuf("123", "456", "789");
|
||||
EXPECT_EQ(buf.size(), 9u);
|
||||
EXPECT_EQ(buf[0], '1');
|
||||
EXPECT_EQ(buf[1], '2');
|
||||
EXPECT_EQ(buf[2], '3');
|
||||
EXPECT_EQ(buf[3], '4');
|
||||
EXPECT_EQ(buf[4], '5');
|
||||
EXPECT_EQ(buf[5], '6');
|
||||
EXPECT_EQ(buf[6], '7');
|
||||
EXPECT_EQ(buf[7], '8');
|
||||
EXPECT_EQ(buf[8], '9');
|
||||
}
|
||||
|
||||
TEST_F(BuffersTest, compare_buffers)
|
||||
{
|
||||
auto buf1 = genBuf("123", "456", "789");
|
||||
auto buf2 = genBuf("12", "3456", "789");
|
||||
EXPECT_TRUE(buf1 == buf2);
|
||||
EXPECT_FALSE(buf1 != buf2);
|
||||
|
||||
auto buf3 = genBuf("123", "46", "789");
|
||||
EXPECT_FALSE(buf1 == buf3);
|
||||
EXPECT_TRUE(buf1 != buf3);
|
||||
|
||||
auto buf4 = genBuf("123", "406", "789");
|
||||
EXPECT_FALSE(buf1 == buf4);
|
||||
EXPECT_TRUE(buf1 != buf4);
|
||||
|
||||
auto buf5 = genBuf("123", "456", "7890");
|
||||
EXPECT_FALSE(buf1 == buf5);
|
||||
EXPECT_TRUE(buf1 != buf5);
|
||||
}
|
||||
|
||||
TEST_F(BuffersTest, truncate_head2)
|
||||
{
|
||||
auto buf = genBuf("123", "456", "789");
|
||||
buf.truncateHead(5);
|
||||
EXPECT_EQ(buf, Buffer("6789"));
|
||||
}
|
||||
|
||||
TEST_F(BuffersTest, truncate_tail2)
|
||||
{
|
||||
auto buf = genBuf("123", "456", "789");
|
||||
buf.truncateTail(4);
|
||||
EXPECT_EQ(buf, Buffer("12345"));
|
||||
}
|
||||
|
||||
TEST_F(BuffersTest, sub_buffer)
|
||||
{
|
||||
auto origbuf = genBuf("123", "456", "789");
|
||||
auto subbuf = origbuf.getSubBuffer(4, 7);
|
||||
EXPECT_EQ(subbuf, Buffer("567"));
|
||||
}
|
||||
|
||||
TEST_F(BuffersTest, add_compound)
|
||||
{
|
||||
auto buf = genBuf("1", "2", "3");
|
||||
// Testing adding a buffer to itself, which is an extreme case.
|
||||
buf += buf;
|
||||
EXPECT_EQ(buf, Buffer("123123"));
|
||||
}
|
||||
|
||||
string
|
||||
iterToStr(const Buffer::SegIterator &iter)
|
||||
{
|
||||
return string(reinterpret_cast<const char *>(iter->data()), iter->size());
|
||||
}
|
||||
|
||||
TEST_F(BuffersTest, add_operator_of_iterator)
|
||||
{
|
||||
auto buf = genBuf("12", "3456", "789");
|
||||
auto iter = buf.segRange().begin();
|
||||
EXPECT_EQ(iterToStr(iter), "12");
|
||||
iter++;
|
||||
EXPECT_EQ(iterToStr(iter), "3456");
|
||||
iter++;
|
||||
EXPECT_EQ(iterToStr(iter), "789");
|
||||
iter++;
|
||||
EXPECT_TRUE(iter == buf.segRange().end());
|
||||
}
|
||||
|
||||
bool
|
||||
operator==(const vector<string> &vec, const Buffer &buf)
|
||||
{
|
||||
auto vec_iter = vec.begin();
|
||||
for (auto &iter : buf.segRange()) {
|
||||
if (vec_iter == vec.end()) return false;
|
||||
if (iter != *vec_iter) return false;
|
||||
vec_iter++;
|
||||
}
|
||||
return vec_iter == vec.end();
|
||||
}
|
||||
|
||||
bool
|
||||
operator==(const string &str, const Buffer::Segment &seg)
|
||||
{
|
||||
string tmp(reinterpret_cast<const char *>(seg.data()), seg.size());
|
||||
return str == tmp;
|
||||
}
|
||||
|
||||
TEST_F(BuffersTest, iterator_loop)
|
||||
{
|
||||
auto buf = genBuf("12", "3456", "789");
|
||||
vector<string> vec = { "12", "3456", "789" };
|
||||
|
||||
auto vec_iter = vec.begin();
|
||||
for (auto seg : buf.segRange()) {
|
||||
EXPECT_EQ(*vec_iter, seg);
|
||||
vec_iter++;
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(BuffersTest, flatten)
|
||||
{
|
||||
auto buf = genBuf("12", "3456", "789");
|
||||
EXPECT_EQ((vector<string>{ "12", "3456", "789" }), buf);
|
||||
|
||||
buf.serialize();
|
||||
EXPECT_EQ((vector<string>{ "123456789" }), buf);
|
||||
|
||||
auto buf2 = genBuf("12", "3456", "789");
|
||||
buf2.truncateHead(1); // "23456789"
|
||||
buf2.truncateTail(1); // "2345678"
|
||||
|
||||
buf2.serialize();
|
||||
EXPECT_EQ((vector<string>{ "2345678" }), buf2);
|
||||
}
|
||||
|
||||
TEST_F(BuffersTest, get_pointer)
|
||||
{
|
||||
auto buf = genBuf("12", "3456", "789");
|
||||
auto ptr1 = buf.getPtr(3, 3);
|
||||
ASSERT_TRUE(ptr1.ok());
|
||||
// Internal Structure of the buffer didn't change.
|
||||
EXPECT_EQ((vector<string>{ "12", "3456", "789" }), buf);
|
||||
// Get the currect segment
|
||||
auto iter = buf.segRange().begin();
|
||||
iter++;
|
||||
// Check that the internal pointer points to the segment in the correct location.
|
||||
EXPECT_EQ(iter->data() + 1, ptr1.unpack());
|
||||
|
||||
auto ptr2 = buf.getPtr(5, 2);
|
||||
ASSERT_TRUE(ptr2.ok());
|
||||
// Buffer had to be serialized
|
||||
EXPECT_EQ((vector<string>{ "123456789" }), buf);
|
||||
// Check that the internal pointer points to the correct point in the serialized data.
|
||||
EXPECT_EQ(buf.data() + 5, ptr2.unpack());
|
||||
|
||||
auto ptr3 = buf.getPtr(5, 25);
|
||||
EXPECT_THAT(ptr3, IsError("Cannot get internal pointer beyond the buffer limits"));
|
||||
}
|
||||
|
||||
TEST_F(BuffersTest, InternalPtr_assign)
|
||||
{
|
||||
auto buf = genBuf("12", "3456", "789");
|
||||
auto ptr1 = buf.getPtr(3, 3);
|
||||
ASSERT_TRUE(ptr1.ok());
|
||||
auto ptr2 = ptr1;
|
||||
ASSERT_TRUE(ptr1.ok());
|
||||
ASSERT_TRUE(ptr2.ok());
|
||||
EXPECT_EQ(ptr1.unpack(), ptr2.unpack());
|
||||
}
|
||||
|
||||
TEST_F(BuffersTest, InternalPtr_move)
|
||||
{
|
||||
auto buf = genBuf("12", "3456", "789");
|
||||
auto ptr1 = buf.getPtr(3, 6);
|
||||
ASSERT_TRUE(ptr1.ok());
|
||||
auto ptr2 = buf.getPtr(2, 5);
|
||||
ASSERT_TRUE(ptr2.ok());
|
||||
ptr2 = move(ptr1);
|
||||
//Move assignment operator takes over the resources of ptr1.
|
||||
EXPECT_EQ(ptr1.unpack(), nullptr);
|
||||
ASSERT_TRUE(ptr2.ok());
|
||||
EXPECT_EQ(buf.data() + 3, ptr2.unpack());
|
||||
}
|
||||
|
||||
TEST_F(BuffersTest, death_on_asserts2)
|
||||
{
|
||||
cptestPrepareToDie();
|
||||
|
||||
auto buf = genBuf("123", "456", "789");
|
||||
EXPECT_DEATH(buf[10], "Buffer::operator returned: attempted an access outside the buffer");
|
||||
EXPECT_DEATH(buf[-1], "Buffer::operator returned: attempted an access outside the buffer");
|
||||
EXPECT_DEATH(buf.truncateTail(10), "Cannot set a new end of buffer after the buffer's end");
|
||||
EXPECT_DEATH(buf.truncateHead(10), "Cannot set a new start of buffer after the buffer's end");
|
||||
}
|
||||
|
||||
TEST_F(BuffersTest, owned_data)
|
||||
{
|
||||
string str("0");
|
||||
auto ptr = reinterpret_cast<const u_char *>(str.data());
|
||||
Buffer b;
|
||||
{
|
||||
// OWNED memory copies the memory, so changes to the original pointer don't impact it.
|
||||
Buffer c(str.data(), 1, Buffer::MemoryType::OWNED);
|
||||
b = c;
|
||||
str[0] = '1';
|
||||
EXPECT_EQ(Buffer("0"), b);
|
||||
EXPECT_NE(ptr, b.data());
|
||||
}
|
||||
EXPECT_NE(ptr, b.data());
|
||||
str[0] = '2';
|
||||
EXPECT_EQ(Buffer("0"), b);
|
||||
}
|
||||
|
||||
TEST_F(BuffersTest, static_data)
|
||||
{
|
||||
string str("0");
|
||||
auto ptr = reinterpret_cast<const u_char *>(str.data());
|
||||
Buffer b;
|
||||
{
|
||||
// STATIC always points to the original pointer.
|
||||
// In real scenarios `str` should be `static const` string and not a local changing varialbe, we absue it in
|
||||
// this case specifically so the behavoir of the memory can be shown.
|
||||
Buffer c(str.data(), 1, Buffer::MemoryType::STATIC);
|
||||
b = c;
|
||||
str[0] = '1';
|
||||
EXPECT_EQ(Buffer("1"), b);
|
||||
EXPECT_EQ(ptr, b.data());
|
||||
}
|
||||
str[0] = '2';
|
||||
EXPECT_EQ(Buffer("2"), b);
|
||||
}
|
||||
|
||||
TEST_F(BuffersTest, volatile_data)
|
||||
{
|
||||
string str("0");
|
||||
auto ptr = reinterpret_cast<const u_char *>(str.data());
|
||||
Buffer b;
|
||||
{
|
||||
// VOLATILE memory only pointers to the original pointer while the initial instance lives.
|
||||
Buffer c(str.data(), 1, Buffer::MemoryType::VOLATILE);
|
||||
b = c;
|
||||
str[0] = '1';
|
||||
EXPECT_EQ(Buffer("1"), b);
|
||||
EXPECT_EQ(ptr, b.data());
|
||||
}
|
||||
EXPECT_NE(ptr, b.data());
|
||||
// Memory was copied, so further changes don't impact it.
|
||||
str[0] = '2';
|
||||
EXPECT_EQ(Buffer("1"), b);
|
||||
}
|
||||
|
||||
TEST_F(BuffersTest, truncate_volatile_data)
|
||||
{
|
||||
string str("123");
|
||||
Buffer b;
|
||||
{
|
||||
Buffer c(str.data(), 3, Buffer::MemoryType::VOLATILE);
|
||||
b = c;
|
||||
b.truncateHead(1);
|
||||
}
|
||||
EXPECT_EQ(Buffer("23"), b);
|
||||
}
|
||||
|
||||
TEST_F(BuffersTest, clear)
|
||||
{
|
||||
auto buf = genBuf("123", "456", "789");
|
||||
EXPECT_EQ(buf.size(), 9u);
|
||||
buf.clear();
|
||||
EXPECT_EQ(buf.size(), 0u);
|
||||
Buffer test = buf;
|
||||
EXPECT_EQ(test.size(), 0u);
|
||||
}
|
||||
|
||||
TEST_F(BuffersTest, access_after_clear)
|
||||
{
|
||||
auto buf = genBuf("123", "456", "789");
|
||||
buf.clear();
|
||||
cptestPrepareToDie();
|
||||
EXPECT_DEATH(buf[1], "Buffer::operator() returned: attempted an access outside the buffer");
|
||||
EXPECT_DEATH(buf[0], "Buffer::operator() returned: attempted an access outside the buffer");
|
||||
}
|
||||
|
||||
TEST_F(BuffersTest, isEmpty)
|
||||
{
|
||||
auto b = genBuf("123", "456", "789");
|
||||
EXPECT_FALSE(b.isEmpty());
|
||||
b.clear();
|
||||
EXPECT_TRUE(b.isEmpty());
|
||||
Buffer c = b;
|
||||
EXPECT_TRUE(c.isEmpty());
|
||||
}
|
||||
|
||||
TEST_F(BuffersTest, contains)
|
||||
{
|
||||
vector<u_char> vec1 = { '1', '3', '5' };
|
||||
auto b1 = Buffer(vec1);
|
||||
|
||||
for (const char ch : vec1) {
|
||||
EXPECT_TRUE(b1.contains(ch));
|
||||
}
|
||||
|
||||
EXPECT_FALSE(b1.contains('?'));
|
||||
}
|
||||
|
||||
TEST_F(BuffersTest, segmentsNumber)
|
||||
{
|
||||
vector<u_char> vec1 = { '1', '3', '7' };
|
||||
vector<u_char> vec2 = { '1', '3', '7' };
|
||||
auto b = Buffer(vec1) + Buffer(vec2);
|
||||
EXPECT_EQ(b.segmentsNumber(), 2u);
|
||||
EXPECT_EQ(b.size(), 6u);
|
||||
auto sub_b = b.getSubBuffer(0, 2);
|
||||
EXPECT_EQ(sub_b.segmentsNumber(), 1u);
|
||||
sub_b.clear();
|
||||
EXPECT_EQ(sub_b.segmentsNumber(), 0u);
|
||||
}
|
||||
|
||||
TEST_F(BuffersTest, Equl_buffers)
|
||||
{
|
||||
auto buf = genBuf("123", "456", "789");
|
||||
const char* str = "1234567890";
|
||||
const u_char* u_str = reinterpret_cast<const u_char *>("1234567890");
|
||||
EXPECT_TRUE(buf.isEqual(str, 9));
|
||||
EXPECT_FALSE(buf.isEqual(str, 10));
|
||||
EXPECT_TRUE(buf.isEqual(u_str, 9));
|
||||
EXPECT_FALSE(buf.isEqual(u_str, 10));
|
||||
}
|
||||
|
||||
TEST_F(BuffersTest, string_casting)
|
||||
{
|
||||
auto buf = genBuf("123", "456", "789");
|
||||
EXPECT_EQ(static_cast<string>(buf), string("123456789"));
|
||||
}
|
||||
|
||||
TEST_F (BuffersTest, CharIterator)
|
||||
{
|
||||
auto buf = genBuf("123", "456", "789");
|
||||
vector<u_char> test_vec;
|
||||
for (auto iter : buf) {
|
||||
test_vec.push_back(iter);
|
||||
}
|
||||
vector<u_char> expect_vec = { '1', '2', '3', '4', '5', '6', '7', '8', '9' };
|
||||
EXPECT_EQ(test_vec, expect_vec);
|
||||
|
||||
auto it = buf.begin() + 2;
|
||||
EXPECT_EQ(*(it), '3');
|
||||
it += 2;
|
||||
EXPECT_EQ(*(it), '5');
|
||||
++it;
|
||||
EXPECT_EQ(*(it), '6');
|
||||
}
|
||||
|
||||
TEST_F (BuffersTest, empty_CharIterator)
|
||||
{
|
||||
cptestPrepareToDie();
|
||||
auto it = Buffer::CharIterator();
|
||||
EXPECT_DEATH(*(it), "Buffer::CharIterator is not pointing to a real value");
|
||||
}
|
||||
|
||||
TEST_F(BuffersTest, serialization)
|
||||
{
|
||||
stringstream stream;
|
||||
{
|
||||
cereal::JSONOutputArchive ar(stream);
|
||||
ar(genBuf("aaa", "bb", "c"));
|
||||
}
|
||||
Buffer buf;
|
||||
{
|
||||
cereal::JSONInputArchive ar(stream);
|
||||
ar(buf);
|
||||
}
|
||||
EXPECT_EQ(buf, Buffer("aaabbc"));
|
||||
}
|
||||
|
||||
TEST_F (BuffersTest, find_first_of_ch)
|
||||
{
|
||||
Buffer b1("boundary=Heeelllo;extrastuff;");
|
||||
uint index = b1.findFirstOf('=').unpack();
|
||||
EXPECT_TRUE(b1[index] == '=');
|
||||
EXPECT_TRUE(index == 8);
|
||||
Buffer b2("boundary");
|
||||
EXPECT_TRUE(b2 == b1.getSubBuffer(0, index));
|
||||
}
|
||||
|
||||
TEST_F (BuffersTest, find_first_of_buf)
|
||||
{
|
||||
Buffer b1("boundary=Heeelllo;extrastuff;");
|
||||
Buffer find("=Heeel");
|
||||
uint index = b1.findFirstOf(find).unpack();
|
||||
EXPECT_TRUE(b1[index] == '=');
|
||||
EXPECT_TRUE(index == 8);
|
||||
Buffer b2("boundary");
|
||||
EXPECT_TRUE(b2 == b1.getSubBuffer(0, index));
|
||||
}
|
||||
|
||||
TEST_F (BuffersTest, find_last_of)
|
||||
{
|
||||
Buffer b1("boundary=Heeelllo;extrastuff;");
|
||||
auto index = b1.findLastOf('u');
|
||||
EXPECT_TRUE(index.ok());
|
||||
EXPECT_TRUE(b1[index.unpack()] == 'u');
|
||||
EXPECT_TRUE(index.unpack() == 25);
|
||||
Buffer b2("boundary=Heeelllo;extrast");
|
||||
EXPECT_TRUE(b2 == b1.getSubBuffer(0, index.unpack()));
|
||||
}
|
||||
|
||||
TEST_F (BuffersTest, find_first_not_of)
|
||||
{
|
||||
Buffer b1(" boundary ");
|
||||
auto index = b1.findFirstNotOf(' ');
|
||||
EXPECT_TRUE(index.ok());
|
||||
EXPECT_TRUE(b1[index.unpack()] == 'b');
|
||||
EXPECT_TRUE(index.unpack() == 4);
|
||||
Buffer b2(" ");
|
||||
EXPECT_TRUE(b2 == b1.getSubBuffer(0, index.unpack()));
|
||||
}
|
||||
|
||||
TEST_F (BuffersTest, find_last_not_of)
|
||||
{
|
||||
Buffer b1(" boundary ");
|
||||
auto index = b1.findLastNotOf(' ');
|
||||
EXPECT_TRUE(index.ok());
|
||||
EXPECT_TRUE(b1[index.unpack()] == 'y');
|
||||
EXPECT_TRUE(index.unpack() == 11);
|
||||
Buffer b2(" boundar");
|
||||
EXPECT_TRUE(b2 == b1.getSubBuffer(0, index.unpack()));
|
||||
}
|
||||
|
||||
class SegmentsTest: public Test
|
||||
{
|
||||
public:
|
||||
Buffer::Segment
|
||||
genSeg(const string &str, Buffer::MemoryType type = Buffer::MemoryType::OWNED)
|
||||
{
|
||||
Buffer::Segment seg(reinterpret_cast<const u_char *>(str.c_str()), str.length(), type);
|
||||
return seg;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
TEST_F(SegmentsTest, empty_segmnet)
|
||||
{
|
||||
Buffer::Segment seg;
|
||||
EXPECT_EQ(seg.size(), 0u);
|
||||
}
|
||||
|
||||
TEST_F (SegmentsTest, assign)
|
||||
{
|
||||
Buffer::Segment seg1 = genSeg("123456789");
|
||||
Buffer::Segment seg2 = seg1;
|
||||
EXPECT_EQ(seg1.size(), seg2.size());
|
||||
EXPECT_EQ(bcmp(seg1.data(), seg2.data(), 9), 0);
|
||||
Buffer::Segment seg3;
|
||||
seg3 = seg2;
|
||||
EXPECT_EQ(seg3.size(), 9u);
|
||||
EXPECT_EQ(seg2.size(), 9u);
|
||||
EXPECT_EQ(seg2.data(), seg3.data());
|
||||
EXPECT_EQ(seg1.size(), seg3.size());
|
||||
}
|
||||
|
||||
TEST_F (SegmentsTest, move)
|
||||
{
|
||||
Buffer::Segment seg1 = genSeg("123456789");
|
||||
EXPECT_EQ(seg1.size(), 9u);
|
||||
Buffer::Segment seg2 = std::move(seg1);
|
||||
EXPECT_EQ(seg1.size(), 0u);
|
||||
EXPECT_EQ(seg2.size(), 9u);
|
||||
EXPECT_EQ(seg1.data(), nullptr);
|
||||
EXPECT_EQ(bcmp(seg2.data(), "123456789", 9), 0);
|
||||
Buffer::Segment seg3;
|
||||
seg3 = (std::move(seg2));
|
||||
EXPECT_EQ(seg2.size(), 0u);
|
||||
EXPECT_EQ(seg3.size(), 9u);
|
||||
EXPECT_EQ(seg2.data(), nullptr);
|
||||
EXPECT_EQ(bcmp(seg3.data(), "123456789", 9), 0);
|
||||
}
|
||||
|
||||
TEST_F(SegmentsTest, data)
|
||||
{
|
||||
Buffer::Segment seg1 = genSeg("123456789");
|
||||
Buffer::Segment seg2 = genSeg("123456789");
|
||||
vector<u_char> vec = { '1', '2', '3', '4', '5', '6', '7', '8', '9' };
|
||||
Buffer::Segment seg3 = Buffer::Segment(std::move(vec));
|
||||
Buffer::Segment seg4 = Buffer::Segment(seg3);
|
||||
EXPECT_EQ(seg1.size(), 9u);
|
||||
EXPECT_EQ(seg3.size(), 9u);
|
||||
EXPECT_EQ(seg4.size(), 9u);
|
||||
EXPECT_EQ(bcmp(seg1.data(), "123456789", 9), 0);
|
||||
EXPECT_EQ(bcmp(seg1.data(), seg2.data(), 9), 0);
|
||||
EXPECT_EQ(bcmp(seg1.data(), seg3.data(), 9), 0);
|
||||
EXPECT_EQ(bcmp(seg4.data(), seg3.data(), 9), 0);
|
||||
}
|
||||
|
||||
TEST_F(SegmentsTest, move_volatile)
|
||||
{
|
||||
Buffer::Segment seg1;
|
||||
{
|
||||
Buffer::Segment seg2 = genSeg("123456789", Buffer::MemoryType::VOLATILE);
|
||||
seg1 = move(seg2);
|
||||
}
|
||||
EXPECT_EQ(bcmp(seg1.data(), "123456789", 9), 0);
|
||||
}
|
93
core/buffers/char_iterator.cc
Executable file
93
core/buffers/char_iterator.cc
Executable file
@@ -0,0 +1,93 @@
|
||||
// Copyright (C) 2022 Check Point Software Technologies Ltd. All rights reserved.
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "buffer.h"
|
||||
|
||||
void
|
||||
Buffer::CharIterator::operator++()
|
||||
{
|
||||
if (cur_seg == end_seg) return; // We don't progress past the last segment.
|
||||
offset++;
|
||||
if (offset < size) return;
|
||||
// We've reached the end of the segment, need to move to the next one
|
||||
cur_seg++;
|
||||
offset = 0;
|
||||
if (cur_seg != end_seg) {
|
||||
// New segment, read its values of easy access
|
||||
size = cur_seg->size();
|
||||
ptr = cur_seg->data();
|
||||
} else {
|
||||
// We've reached the end of the buffer, set values accordingly
|
||||
size = 0;
|
||||
ptr = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Buffer::CharIterator::operator+=(uint steps)
|
||||
{
|
||||
while (offset + steps >= cur_seg->size()) { // What we look for is beyond this segment, move to the next one
|
||||
auto skip = cur_seg->size() - offset;
|
||||
steps -= skip;
|
||||
cur_seg++;
|
||||
offset = 0;
|
||||
if (cur_seg == end_seg) {
|
||||
// We've reached the end of the buffer, set values accordingly
|
||||
size = 0;
|
||||
ptr = nullptr;
|
||||
return;
|
||||
}
|
||||
}
|
||||
offset += steps;
|
||||
size = cur_seg->size();
|
||||
ptr = cur_seg->data();
|
||||
}
|
||||
|
||||
Buffer::CharIterator
|
||||
Buffer::CharIterator::operator+(uint steps) const
|
||||
{
|
||||
Buffer::CharIterator res = *this;
|
||||
res += steps;
|
||||
return res;
|
||||
}
|
||||
|
||||
bool
|
||||
Buffer::CharIterator::operator==(const CharIterator &other) const
|
||||
{
|
||||
return (cur_seg == other.cur_seg) && (offset == other.offset);
|
||||
}
|
||||
|
||||
const u_char &
|
||||
Buffer::CharIterator::operator*() const
|
||||
{
|
||||
dbgAssert(ptr != nullptr) << "Buffer::CharIterator is not pointing to a real value";
|
||||
return ptr[offset];
|
||||
}
|
||||
|
||||
Buffer::CharIterator::CharIterator(const SegIterator &_cur, const SegIterator &_end, uint _offset)
|
||||
:
|
||||
cur_seg(_cur),
|
||||
end_seg(_end),
|
||||
ptr(_cur->data()),
|
||||
offset(_offset),
|
||||
size(_cur->size())
|
||||
{}
|
||||
|
||||
Buffer::CharIterator::CharIterator(const SegIterator &_end)
|
||||
:
|
||||
cur_seg(_end),
|
||||
end_seg(_end),
|
||||
ptr(nullptr),
|
||||
offset(0),
|
||||
size(0)
|
||||
{}
|
35
core/buffers/data_container.cc
Executable file
35
core/buffers/data_container.cc
Executable file
@@ -0,0 +1,35 @@
|
||||
// Copyright (C) 2022 Check Point Software Technologies Ltd. All rights reserved.
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "buffer.h"
|
||||
|
||||
Buffer::DataContainer::DataContainer(std::vector<u_char> &&_vec)
|
||||
:
|
||||
vec(std::move(_vec)),
|
||||
ptr(vec.data()),
|
||||
len(vec.size())
|
||||
{
|
||||
}
|
||||
|
||||
Buffer::DataContainer::DataContainer(const u_char *_ptr, uint _len, MemoryType _type)
|
||||
:
|
||||
len(_len)
|
||||
{
|
||||
if (_type == MemoryType::OWNED) {
|
||||
vec = std::vector<u_char>(_ptr, _ptr + len);
|
||||
ptr = vec.data();
|
||||
} else {
|
||||
ptr = _ptr;
|
||||
is_owned = false;
|
||||
}
|
||||
}
|
162
core/buffers/segment.cc
Executable file
162
core/buffers/segment.cc
Executable file
@@ -0,0 +1,162 @@
|
||||
// Copyright (C) 2022 Check Point Software Technologies Ltd. All rights reserved.
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "buffer.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
Buffer::Segment::~Segment()
|
||||
{
|
||||
if (type==Volatility::PRIMARY && !data_container.unique()) {
|
||||
// The segment is the PRIMARY holder of the memory, and there are SECONDARY ones of well.
|
||||
// Since the PRIMARY has reached its end-of-life, since the memory is only guaranteed to exist as long as the
|
||||
// primary is alive it needs to be copied so that the other instances could access it later.
|
||||
data_container->takeOwnership();
|
||||
}
|
||||
}
|
||||
|
||||
Buffer::Segment::Segment(const Buffer::Segment &seg)
|
||||
:
|
||||
data_container(seg.data_container),
|
||||
offset(seg.offset),
|
||||
len(seg.len),
|
||||
ptr(seg.ptr)
|
||||
{
|
||||
if (seg.type == Volatility::PRIMARY) {
|
||||
type = Volatility::SECONDARY;
|
||||
is_owned = data_container->checkOnwership();
|
||||
} else {
|
||||
type = seg.type;
|
||||
is_owned = seg.is_owned;
|
||||
}
|
||||
}
|
||||
|
||||
Buffer::Segment::Segment(Segment &&seg)
|
||||
:
|
||||
data_container(move(seg.data_container)),
|
||||
offset(seg.offset),
|
||||
len(seg.len)
|
||||
{
|
||||
if (seg.type == Volatility::PRIMARY) {
|
||||
// The PRIMARY is being moved, meaning it reached its end-of-life. For the current instance to have access to
|
||||
// the memory, the ownership over it must be taken.
|
||||
data_container->takeOwnership();
|
||||
type = Volatility::NONE;
|
||||
is_owned = nullptr;
|
||||
} else {
|
||||
type = seg.type;
|
||||
is_owned = seg.is_owned;
|
||||
}
|
||||
ptr = data_container->data() + offset;
|
||||
|
||||
seg.offset = 0;
|
||||
seg.len = 0;
|
||||
seg.type = Volatility::NONE;
|
||||
seg.ptr = nullptr;
|
||||
seg.is_owned = nullptr;
|
||||
}
|
||||
|
||||
Buffer::Segment &
|
||||
Buffer::Segment::operator=(const Buffer::Segment &seg)
|
||||
{
|
||||
// Copy-assingment is made of two parts:
|
||||
// 1. Destructor logic
|
||||
if (type==Volatility::PRIMARY && !data_container.unique()) {
|
||||
data_container->takeOwnership();
|
||||
}
|
||||
|
||||
// 2. Copy-constructor logic
|
||||
data_container = seg.data_container;
|
||||
offset = seg.offset;
|
||||
len = seg.len;
|
||||
|
||||
ptr = seg.ptr;
|
||||
if (seg.type == Volatility::PRIMARY) {
|
||||
type = Volatility::SECONDARY;
|
||||
is_owned = data_container->checkOnwership();
|
||||
} else {
|
||||
type = seg.type;
|
||||
is_owned = seg.is_owned;
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
Buffer::Segment &
|
||||
Buffer::Segment::operator=(Segment &&seg)
|
||||
{
|
||||
// Move-assingment is made of two parts:
|
||||
// 1. Destructor logic
|
||||
if (type==Volatility::PRIMARY && !data_container.unique()) {
|
||||
data_container->takeOwnership();
|
||||
}
|
||||
|
||||
// 2. Move-constructor logic
|
||||
data_container = move(seg.data_container);
|
||||
offset = seg.offset;
|
||||
seg.offset = 0;
|
||||
len = seg.len;
|
||||
seg.len = 0;
|
||||
|
||||
if (seg.type == Volatility::PRIMARY) {
|
||||
data_container->takeOwnership();
|
||||
type = Volatility::NONE;
|
||||
is_owned = nullptr;
|
||||
} else {
|
||||
type = seg.type;
|
||||
is_owned = seg.is_owned;
|
||||
}
|
||||
seg.is_owned = nullptr;
|
||||
ptr = data_container->data() + offset;
|
||||
seg.ptr = nullptr;
|
||||
seg.type = Volatility::NONE;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
Buffer::Segment::Segment(vector<u_char> &&_vec)
|
||||
:
|
||||
data_container(make_shared<DataContainer>(move(_vec))),
|
||||
offset(0),
|
||||
len(data_container->size()),
|
||||
is_owned(nullptr)
|
||||
{
|
||||
type = Volatility::NONE;
|
||||
ptr = data_container->data();
|
||||
}
|
||||
|
||||
Buffer::Segment::Segment(const u_char *_ptr, uint _len, Buffer::MemoryType _type)
|
||||
:
|
||||
data_container(make_shared<DataContainer>(_ptr, _len, _type)),
|
||||
offset(0),
|
||||
len(_len),
|
||||
is_owned(nullptr)
|
||||
{
|
||||
type = _type==MemoryType::VOLATILE ? Volatility::PRIMARY : Volatility::NONE;
|
||||
ptr = data_container->data();
|
||||
}
|
||||
|
||||
const u_char *
|
||||
Buffer::Segment::data() const
|
||||
{
|
||||
// Check if a copy-in of the memory happened, and if so - recalulate the short circuit.
|
||||
if (is_owned!=nullptr && *is_owned) {
|
||||
// The data has been moved (due to taking ownership), so `ptr` and other members need to be updated.
|
||||
// Since those changes need to happen in a `const` context, the dangerous `const_cast` is used.
|
||||
auto non_const_this = const_cast<Segment *>(this);
|
||||
non_const_this->ptr = data_container->data() + offset;
|
||||
non_const_this->type = Volatility::NONE;
|
||||
non_const_this->is_owned = nullptr;
|
||||
}
|
||||
return ptr;
|
||||
}
|
Reference in New Issue
Block a user