Commit 21ff3fdd authored by octal's avatar octal

Added a non-owning View<T> that stores a sequence of contiguous memory

parent 4035c5f1
/* view.h
Mathieu Stefani, 19 janvier 2016
A non-owning range of contiguous memory that is represented by
a pointer to the beginning of the memory and the size.
*/
#pragma once
#include <string>
#include <vector>
#include <array>
#include <cstring>
#include <stdexcept>
template<typename T>
class ViewBase {
public:
typedef T value_type;
typedef T& reference;
typedef T* pointer;
typedef const T* const_pointer;
typedef const T& const_reference;
typedef const_pointer iterator;
typedef const_pointer const_iterator;
explicit ViewBase(std::nullptr_t)
: begin_(nullptr)
, size_(0)
{ }
explicit ViewBase(std::nullptr_t, size_t)
: begin_(nullptr)
, size_(0)
{ }
explicit ViewBase(const T* begin, size_t size)
: begin_(begin)
, size_(size)
{ }
explicit ViewBase(const T* begin, const T* end) {
if (begin > end) {
throw std::invalid_argument("begin > end");
}
begin_ = begin;
size_ = std::distance(begin, end);
}
size_t size() const {
return size_;
}
const T& operator[](size_t index) {
return begin_[index];
}
const T& operator[](size_t index) const {
return begin_[index];
}
const T& at(size_t index) {
if (index >= size_)
throw std::invalid_argument("index > size");
return begin_[index];
}
const T& at(size_t index) const {
if (index >= size_)
throw std::invalid_argument("index > size");
return begin_[index];
}
const_iterator begin() const {
return const_iterator(begin_);
}
const_iterator end() const {
return const_iterator(begin_ + size_);
}
bool operator==(const ViewBase<T>& other) const {
return size() == other.size() &&
std::equal(begin(), end(), other.begin());
}
bool operator!=(const ViewBase<T>& other) const {
return !(*this == other);
}
bool empty() const {
return begin_ == nullptr || size_ == 0;
}
const T* data() const {
return begin_;
}
private:
const T* begin_;
size_t size_;
};
template<typename T>
struct View : public ViewBase<T> {
explicit View(std::nullptr_t)
: ViewBase<T>(nullptr)
{ }
explicit View(std::nullptr_t, size_t)
: ViewBase<T>(nullptr, 0)
{ }
explicit View(const T* begin, size_t size)
: ViewBase<T>(begin, size)
{ }
explicit View(const T* begin, const T* end)
: ViewBase<T>(begin, end)
{ }
};
template<>
struct View<std::string> : public ViewBase<char> {
typedef ViewBase<char> Base;
explicit View(const char* begin, size_t end)
: Base(begin, end)
{ }
explicit View(const char* begin, const char* end)
: Base(begin, end)
{ }
using Base::operator==;
bool operator==(const char* str) const {
return equals(str, std::strlen(str));
}
bool operator==(const std::string& str) const {
return equals(str.c_str(), str.size());
}
template<size_t N>
bool operator==(const char (&arr)[N]) const {
return equals(arr, N);
}
std::string toString() const {
return std::string(data(), size());
}
operator std::string() const {
return toString();
}
private:
bool equals(const char* str, size_t length) const {
if (length != size()) return false;
return std::equal(begin(), end(), str);
}
};
typedef View<std::string> StringView;
namespace impl {
template<typename T> struct ViewBuilder;
template<typename T, size_t N>
struct ViewBuilder<std::array<T, N>> {
typedef T Type;
static View<T> build(const std::array<T, N>& arr) {
return buildSized(arr, arr.size());
}
static View<T> buildSized(const std::array<T, N>& arr, size_t size) {
if (size > arr.size())
throw std::invalid_argument("out of bounds size");
return View<T>(&arr[0], size);
}
};
template<typename T, size_t N>
struct ViewBuilder<T (&)[N]> {
typedef T Type;
static View<T> build(T (&arr)[N]) {
return buildSize(arr, N);
}
static View<T> buildSize(T (&arr)[N], size_t size) {
if (size > N)
throw std::invalid_argument("out of bounds size");
return View<T>(&arr[0], size);
}
};
template<typename T>
struct ViewBuilder<std::vector<T>> {
typedef T Type;
static View<T> build(const std::vector<T>& vec) {
return buildSized(vec, vec.size());
}
static View<T> buildSized(const std::vector<T>& vec, size_t size) {
if (size > vec.size())
throw std::invalid_argument("out of bounds size");
return View<T>(&vec[0], size);
}
};
template<>
struct ViewBuilder<std::string> {
typedef std::string Type;
static StringView build(const std::string& str) {
return buildSized(str, str.size());
}
static StringView buildSized(const std::string& str, size_t size) {
if (size > str.size())
throw std::invalid_argument("out of bounds size");
return StringView(str.data(), size);
}
};
};
template<typename Cont>
View<typename impl::ViewBuilder<Cont>::Type> make_view(const Cont& cont)
{
return impl::ViewBuilder<Cont>::build(cont);
}
template<typename Cont>
View<typename impl::ViewBuilder<Cont>::Type> make_view(const Cont& cont, size_t size)
{
return impl::ViewBuilder<Cont>::buildSized(cont, size);
}
......@@ -21,3 +21,7 @@ add_test( router_test run_router_test )
add_executable( run_cookie_test cookie_test.cc )
target_link_libraries(run_cookie_test gtest gtest_main net)
add_test( cookie_test run_cookie_test )
add_executable( run_view_test view_test.cc )
target_link_libraries(run_view_test gtest gtest_main net)
add_test( view_test run_view_test )
#include "gtest/gtest.h"
#include "cookie.h"
using namespace Net;
using namespace Net::Http;
void parse(const char* str, std::function<void (const Cookie&)> testFunc)
{
auto c1 = Cookie::fromString(std::string(str));
testFunc(c1);
auto c2 = Cookie::fromRaw(str, strlen(str));
testFunc(c2);
}
TEST(cookie_test, basic_test) {
parse("SID=31d4d96e407aad42", [](const Cookie& cookie) {
ASSERT_EQ(cookie.name, "SID");
ASSERT_EQ(cookie.value, "31d4d96e407aad42");
});
}
TEST(cookie_test, attributes_test) {
parse("SID=31d4d96e407aad42; Path=/", [](const Cookie& c) {
ASSERT_EQ(c.name, "SID");
ASSERT_EQ(c.value, "31d4d96e407aad42");
ASSERT_EQ(c.path.getOrElse(""), "/");
});
parse("SID=31d4d96e407aad42; Path=/; Domain=example.com", [](const Cookie& c) {
ASSERT_EQ(c.path.getOrElse(""), "/");
ASSERT_EQ(c.domain.getOrElse(""), "example.com");
});
parse("lang=en-US; Path=/; Domain=example.com; Max-Age=10", [](const Cookie& c) {
ASSERT_EQ(c.name, "lang");
ASSERT_EQ(c.value, "en-US");
ASSERT_EQ(c.path.getOrElse(""), "/");
ASSERT_EQ(c.domain.getOrElse(""), "example.com");
ASSERT_EQ(c.maxAge.getOrElse(0), 10);
});
parse("lang=en-US; Expires=Wed, 09 Jun 2021 10:18:14 GMT", [](const Cookie& c) {
ASSERT_EQ(c.name, "lang");
ASSERT_EQ(c.value, "en-US");
auto expires = c.expires.getOrElse(FullDate());
auto date = expires.date();
ASSERT_EQ(date.tm_year, 121);
ASSERT_EQ(date.tm_mon, 5);
ASSERT_EQ(date.tm_mday, 9);
ASSERT_EQ(date.tm_hour, 10);
ASSERT_EQ(date.tm_min, 18);
ASSERT_EQ(date.tm_sec, 14);
});
parse("lang=en-US; Path=/; Domain=example.com;", [](const Cookie& c) {
ASSERT_EQ(c.name, "lang");
ASSERT_EQ(c.value, "en-US");
ASSERT_EQ(c.domain.getOrElse(""), "example.com");
});
}
TEST(cookie_test, bool_test) {
parse("SID=31d4d96e407aad42; Path=/; Secure", [](const Cookie& c) {
ASSERT_EQ(c.name, "SID");
ASSERT_EQ(c.value, "31d4d96e407aad42");
ASSERT_EQ(c.path.getOrElse(""), "/");
ASSERT_TRUE(c.secure);
ASSERT_FALSE(c.httpOnly);
});
parse("SID=31d4d96e407aad42; Path=/; Secure; HttpOnly", [](const Cookie& c) {
ASSERT_EQ(c.name, "SID");
ASSERT_EQ(c.value, "31d4d96e407aad42");
ASSERT_EQ(c.path.getOrElse(""), "/");
ASSERT_TRUE(c.secure);
ASSERT_TRUE(c.httpOnly);
});
}
TEST(cookie_test, ext_test) {
parse("lang=en-US; Path=/; Scope=Private", [](const Cookie& c) {
ASSERT_EQ(c.name, "lang");
ASSERT_EQ(c.value, "en-US");
ASSERT_EQ(c.path.getOrElse(""), "/");
auto fooIt = c.ext.find("Scope");
ASSERT_TRUE(fooIt != std::end(c.ext));
ASSERT_EQ(fooIt->second, "Private");
});
}
TEST(cookie_test, write_test) {
Cookie c1("lang", "fr-FR");
c1.path = Some(std::string("/"));
c1.domain = Some(std::string("example.com"));
std::ostringstream oss;
c1.write(oss);
ASSERT_EQ(oss.str(), "lang=fr-FR; Path=/; Domain=example.com");
Cookie c2("lang", "en-US");
std::tm expires;
std::memset(&expires, 0, sizeof expires);
expires.tm_isdst = 1;
expires.tm_mon = 2;
expires.tm_year = 118;
expires.tm_mday = 16;
expires.tm_hour = 17;
expires.tm_min = 0;
expires.tm_sec = 0;
c2.path = Some(std::string("/"));
c2.expires = Some(FullDate(expires));
oss.str("");
c2.write(oss);
Cookie c3("lang", "en-US");
c3.secure = true;
c3.ext.insert(std::make_pair("Scope", "Private"));
oss.str("");
c3.write(oss);
ASSERT_EQ(oss.str(), "lang=en-US; Secure; Scope=Private");
};
TEST(cookie_test, invalid_test) {
ASSERT_THROW(Cookie::fromString("lang"), std::runtime_error);
ASSERT_THROW(Cookie::fromString("lang=en-US; Expires"), std::runtime_error);
ASSERT_THROW(Cookie::fromString("lang=en-US; Path=/; Domain"), std::runtime_error);
ASSERT_THROW(Cookie::fromString("lang=en-US; Max-Age=12ab"), std::invalid_argument);
}
#include "gtest/gtest.h"
#include "view.h"
template<typename T>
std::vector<T> make_vec(std::initializer_list<T> list) {
return std::vector<T>(list);
}
TEST(view_test, test_vector) {
auto vec1 = make_vec({1, 2, 3, 4});
auto v1 = make_view(vec1);
ASSERT_EQ(v1.size(), 4);
ASSERT_EQ(v1[0], 1);
ASSERT_EQ(v1[3], 4);
auto v2(v1);
ASSERT_EQ(v2.size(), 4);
ASSERT_EQ(v2[0], 1);
ASSERT_EQ(v2[3], 4);
auto vec2 = make_vec({2, 4, 6, 8, 10});
auto v3 = make_view(vec2, 4);
ASSERT_EQ(v3.size(), 4);
ASSERT_EQ(v3[0], 2);
ASSERT_EQ(v3[3], 8);
ASSERT_THROW(v3.at(4), std::invalid_argument);
ASSERT_EQ(v1, v2);
ASSERT_NE(v1, v3);
auto vec3 = make_vec({4, 3, 2, 1});
auto v4 = make_view(vec3);
ASSERT_NE(v4, v1);
std::vector<int> vec4;
auto v5 = make_view(vec4);
ASSERT_TRUE(v5.empty());
auto v6 = make_view(vec3, 0);
ASSERT_TRUE(v6.empty());
}
TEST(view_test, test_array) {
std::array<int, 4> arr1 { 4, 5, 6, 7 };
auto v1 = make_view(arr1);
ASSERT_EQ(v1.size(), 4);
ASSERT_EQ(v1[0], 4);
ASSERT_EQ(v1[3], 7);
auto v2 = make_view(arr1, 2);
ASSERT_EQ(v2.size(), 2);
ASSERT_EQ(v2[1], 5);
ASSERT_THROW(v2.at(3), std::invalid_argument);
std::array<int, 4> arr2 { 6, 8, 1, 2 };
ASSERT_NE(make_view(arr2), v1);
std::array<int, 4> arr3 { 4, 5, 6, 7 };
ASSERT_EQ(v1, make_view(arr3));
}
TEST(view_test, string_test) {
std::string s1("Hello");
auto v1 = make_view(s1);
ASSERT_EQ(v1.size(), 5);
ASSERT_EQ(v1[0], 'H');
ASSERT_EQ(v1[4], 'o');
ASSERT_EQ(v1, "Hello");
auto v2 = make_view(s1, 3);
ASSERT_TRUE(std::strcmp(v2.data(), "Hel"));
ASSERT_EQ(v2, "Hel");
std::string s3(s1);
auto v3 = make_view(s3);
ASSERT_EQ(v1, v3);
ASSERT_EQ(v3, s1);
const char* hello = "Hello";
ASSERT_EQ(v1, hello);
ASSERT_EQ(v3, hello);
std::string s4 = v3;
ASSERT_EQ(s4, "Hello");
}
TEST(view_test, null_test) {
View<int> v1(nullptr);
ASSERT_TRUE(v1.empty());
View<int> v2(nullptr, 12);
ASSERT_TRUE(v2.empty());
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment