Mercurial > code
changeset 334:0b576ee64d45
* Create brand new hierarchy
* Rename DynLib to Dynlib
* Remove some warnings
line wrap: on
line diff
--- a/C++/Base64.cpp Sun Mar 08 11:07:36 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,66 +0,0 @@ -/* - * Base64.cpp -- base64 encoding and decoding - * - * Copyright (c) 2013, 2014 David Demelier <markand@malikania.fr> - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include <iterator> -#include <sstream> - -#include "Base64.h" - -char Base64::lookup(int value) noexcept -{ - static const char table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; - - return table[value]; -} - -int Base64::rlookup(char ch) -{ - if (ch == '+') - return 62; - if (ch == '/') - return 63; - - if (ch >= '0' && ch <= '9') - return ch + 4; - if (ch >= 'A' && ch <= 'Z') - return ch - 65; - if (ch >= 'a' && ch <= 'z') - return ch - 71; - - throw std::invalid_argument("not a valid base64 string"); -} - -std::string Base64::encode(const std::string &input) -{ - std::string result; - std::istringstream iss(input, std::istringstream::in); - - encode(std::istreambuf_iterator<char>(iss), std::istreambuf_iterator<char>(), std::back_inserter(result)); - - return result; -} - -std::string Base64::decode(const std::string &input) -{ - std::string result; - std::istringstream iss(input, std::istringstream::in); - - decode(std::istreambuf_iterator<char>(iss), std::istreambuf_iterator<char>(), std::back_inserter(result)); - - return result; -} \ No newline at end of file
--- a/C++/Base64.h Sun Mar 08 11:07:36 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,134 +0,0 @@ -/* - * Base64.h -- base64 encoding and decoding - * - * Copyright (c) 2013, 2014 David Demelier <markand@malikania.fr> - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#ifndef _BASE_64_H_ -#define _BASE_64_H_ - -/** - * @file Base64.h - * @brief Base64 encoding and decoding - */ - -#include <stdexcept> -#include <string> - -/** - * @class Base64 - * @brief Encode and decode Base64 data - */ -class Base64 { -public: - /** - * Get the base 64 character from the 6-bits value. - * - * @param value the value - */ - static char lookup(int value) noexcept; - - /** - * Get the integer value from the base 64 character. - * - * @param ch the base64 character - */ - static int rlookup(char ch); - - /** - * Encode the input to the output. Requirements: - * InputIt must be InputIterator - * OutputIt must be OutputIterator - * - * @param input the beginning - * @param end the end of the data - * @param output the output destination - * @return output - */ - template <typename InputIt, typename OutputIt> - static OutputIt encode(InputIt input, InputIt end, OutputIt output) - { - while (input != end) { - char inputbuf[3] = { 0, 0, 0 }; - int count; - - for (count = 0; count < 3 && input != end; ++count) - inputbuf[count] = *input++; - - *output++ = lookup(inputbuf[0] >> 2 & 0x3f); - *output++ = lookup((inputbuf[0] << 4 & 0x3f) | (inputbuf[1] >> 4 & 0x0f)); - *output++ = (count < 2) ? '=' : lookup((inputbuf[1] << 2 & 0x3c) | (inputbuf[2] >> 6 & 0x03)); - *output++ = (count < 3) ? '=' : lookup(inputbuf[2] & 0x3f); - } - - return output; - } - - /** - * Decode the input to the output. Requirements: - * InputIt must be InputIterator - * OutputIt must be OutputIterator - * - * @param input the beginning - * @param end the end of the data - * @param output the output destination - * @return output - * @throw std::invalid_argument on bad base64 string - */ - template <typename InputIt, typename OutputIt> - static OutputIt decode(InputIt input, InputIt end, OutputIt output) - { - while (input != end) { - char inputbuf[4] = { 0, 0, 0, 0 }; - int count; - - for (count = 0; count < 4 && input != end; ++count) { - inputbuf[count] = (*input == '=') ? '=' : rlookup(*input); - input++; - } - - if (count != 4) - throw std::invalid_argument("truncated string"); - - *output++ = (inputbuf[0] << 2 & 0xfc) | (inputbuf[1] >> 4 & 0x03); - - if (inputbuf[2] != '=') - *output++ = (inputbuf[1] << 4 & 0xf0) | (inputbuf[2] >> 2 & 0x0f); - if (inputbuf[3] != '=') - *output++ = (inputbuf[2] << 6 & 0xc0) | (inputbuf[3] & 0x3f); - } - - return output; - } - - /** - * Encode a string. - * - * @param input the input string - * @return the base64 formatted string - */ - static std::string encode(const std::string &input); - - /** - * Decode a string. - * - * @param input the base64 formatted string - * @return the original string - * @throw std::invalid_argument on bad base64 string - */ - static std::string decode(const std::string &input); -}; - -#endif // !_BASE_64_H_
--- a/C++/Converter.cpp Sun Mar 08 11:07:36 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,92 +0,0 @@ -/* - * Converter.cpp -- iconv based converter - * - * Copyright (c) 2013, 2014 David Demelier <markand@malikania.fr> - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include <cerrno> -#include <cstring> -#include <string> -#include <iterator> -#include <memory> -#include <string> -#include <stdexcept> -#include <vector> - -#include <iconv.h> - -#include "Converter.h" - -struct Deleter { - void operator()(iconv_t desc) - { - iconv_close(desc); - } -}; - -using Iconv = std::unique_ptr<std::remove_pointer<iconv_t>::type, Deleter>; - -std::string Converter::convert(const char *from, - const char *to, - const std::string &input) -{ - // No conversion if from and to are identical - if (std::strcmp(from, to) == 0) - return input; - - // Try to open the conversion descriptor - auto cd = iconv_open(to, from); - - if (cd == (iconv_t)-1) - throw std::invalid_argument(std::strerror(errno)); - - Iconv cv(cd); - std::size_t insize(input.size()); - std::size_t outsize(insize); - std::vector<char> result(insize + 1); - - auto *b = &input[0]; - auto *p = &result[0]; - - while (insize > 0) { - /* Convert */ - auto r = iconv(cv.get(), &b, &insize, &p, &outsize); - - if (r == (size_t)-1) { - switch (errno) { - case EBADF: - case EILSEQ: - case EINVAL: - throw std::invalid_argument(std::strerror(errno)); - case E2BIG: - /* - * Here, we need to reallocate more data because the output - * string may need more space. - * - * We use 16 as an optimistic value. - */ - - result.reserve(result.size() + 16 + 1); - p = &result[result.size()]; - outsize += 16; - default: - break; - } - } - - } - - return std::string(&result[0], (p - &result[0])); -}
--- a/C++/Converter.h Sun Mar 08 11:07:36 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,49 +0,0 @@ -/* - * Converter.h -- iconv based converter - * - * Copyright (c) 2013, 2014 David Demelier <markand@malikania.fr> - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#ifndef _CONVERTER_H_ -#define _CONVERTER_H_ - -/** - * @file Converter.h - * @brief Converter using libiconv - */ - -#include <string> - -/** - * @class Converter - * @brief Convert string between different encodings - */ -class Converter { -public: - /** - * Convert the string into a different encoding. - * - * @param from the from encoding - * @param to the destination encoding - * @param input the string to convert - * @return the converted string - * @throw std::invalid_argument on invalid sequence - */ - static std::string convert(const char *from, - const char *to, - const std::string &input); -}; - -#endif // !_CONVERTER_H_
--- a/C++/Date.cpp Sun Mar 08 11:07:36 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,59 +0,0 @@ -/* - * Date.cpp -- date and time manipulation - * - * Copyright (c) 2011, 2012, 2013 David Demelier <markand@malikania.fr> - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include "Date.h" - -Date::Date() -{ - m_timestamp = time(NULL); -} - -Date::Date(time_t timestamp) -{ - m_timestamp = timestamp; -} - -Date::~Date() -{ -} - -time_t Date::getTimestamp() const -{ - return m_timestamp; -} - -std::string Date::format(const std::string &format) -{ - char buffer[512]; - struct tm *tm; - - tm = localtime(&m_timestamp); - strftime(buffer, sizeof (buffer), format.c_str(), tm); - - return std::string(buffer); -} - -bool operator==(const Date &d1, const Date &d2) -{ - return d1.getTimestamp() == d2.getTimestamp(); -} - -bool operator<=(const Date &d1, const Date &d2) -{ - return d1.getTimestamp() <= d2.getTimestamp(); -}
--- a/C++/Date.h Sun Mar 08 11:07:36 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,55 +0,0 @@ -/* - * Date.h -- date and time manipulation - * - * Copyright (c) 2011, 2012, 2013 David Demelier <markand@malikania.fr> - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#ifndef _DATE_H_ -#define _DATE_H_ - -#include <cstdint> -#include <ctime> -#include <string> - -struct Date -{ - time_t m_timestamp; //! time epoch - - Date(); - Date(time_t timestamp); - ~Date(); - - /** - * Get the timestamp. - * - * @return the timestamp - */ - time_t getTimestamp() const; - - /** - * Format the current that in the specified format, - * see strftime(3) for patterns. - * - * @param format the format - * @return the date formated - */ - std::string format(const std::string &format); -}; - -bool operator==(const Date &, const Date &); - -bool operator<=(const Date &, const Date &); - -#endif // !_DATE_H_
--- a/C++/Directory.cpp Sun Mar 08 11:07:36 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,189 +0,0 @@ -/* - * Directory.cpp -- open and read directories - * - * Copyright (c) 2013, 2014 David Demelier <markand@malikania.fr> - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include <sstream> -#include <stdexcept> - -#include "Directory.h" - -#if defined(_WIN32) -# include <Windows.h> -#else -# include <cstring> -# include <cerrno> - -# include <sys/types.h> -# include <dirent.h> -#endif - -#if defined(_WIN32) - -namespace { - -std::string systemError() -{ - LPSTR error = nullptr; - std::string errmsg = "Unknown error"; - - FormatMessageA( - FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, - NULL, - GetLastError(), - MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), - (LPSTR)&error, 0, NULL); - - if (error) { - errmsg = std::string(error); - LocalFree(error); - } - - return errmsg; -} - -} - -void Directory::systemLoad(const std::string &path, int flags) -{ - std::ostringstream oss; - HANDLE handle; - WIN32_FIND_DATA fdata; - - oss << path << "\\*"; - handle = FindFirstFile(oss.str().c_str(), &fdata); - - if (handle == nullptr) - throw std::runtime_error(systemError()); - - do { - Entry entry; - - entry.name = fdata.cFileName; - if ((flags & Directory::NotDot) && entry.name == ".") - continue; - if ((flags & Directory::NotDotDot) && entry.name == "..") - continue; - - switch (fdata.dwFileAttributes) { - case FILE_ATTRIBUTE_DIRECTORY: - entry.type = Dir; - break; - case FILE_ATTRIBUTE_NORMAL: - entry.type = File; - break; - case FILE_ATTRIBUTE_REPARSE_POINT: - entry.type = Link; - break; - default: - break; - } - - m_list.push_back(entry); - } while (FindNextFile(handle, &fdata) != 0); - - FindClose(handle); -} - -#else - -void Directory::systemLoad(const std::string &path, int flags) -{ - DIR *dp; - struct dirent *ent; - - if ((dp = opendir(path.c_str())) == nullptr) - throw std::runtime_error(strerror(errno)); - - while ((ent = readdir(dp)) != nullptr) { - Entry entry; - - entry.name = ent->d_name; - if ((flags & Directory::NotDot) && entry.name == ".") - continue; - if ((flags & Directory::NotDotDot) && entry.name == "..") - continue; - - switch (ent->d_type) { - case DT_DIR: - entry.type = Dir; - break; - case DT_REG: - entry.type = File; - break; - case DT_LNK: - entry.type = Link; - break; - default: - break; - } - - m_list.push_back(entry); - } - - closedir(dp); -} - -#endif - -Directory::Entry::Entry() - : type(Unknown) -{ -} - -bool operator==(const Directory::Entry &e1, const Directory::Entry &e2) -{ - return e1.name == e2.name && e1.type == e2.type; -} - -Directory::Directory() -{ -} - -Directory::Directory(const std::string &path, int flags) -{ - systemLoad(path, flags); -} - -Directory::List::iterator Directory::begin() -{ - return m_list.begin(); -} - -Directory::List::const_iterator Directory::cbegin() const -{ - return m_list.cbegin(); -} - -Directory::List::iterator Directory::end() -{ - return m_list.end(); -} - -Directory::List::const_iterator Directory::cend() const -{ - return m_list.cend(); -} - -int Directory::count() const -{ - return m_list.size(); -} - -bool operator==(const Directory &d1, const Directory &d2) -{ - return d1.m_list == d2.m_list; -}
--- a/C++/Directory.h Sun Mar 08 11:07:36 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,131 +0,0 @@ -/* - * Directory.h -- open and read directories - * - * Copyright (c) 2013, 2014 David Demelier <markand@malikania.fr> - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#ifndef _DIRECTORY_H_ -#define _DIRECTORY_H_ - -#include <cstddef> -#include <string> -#include <vector> - -/** - * @class Directory - * @brief class to manipulate directories - * - * This class allow the user to iterate directories in a for range based - * loop using iterators. - */ -class Directory { -public: - /** - * @enum Flags - * @brief optional flags to read directories - */ - enum Flags { - NotDot = (1 << 0), - NotDotDot = (1 << 1) - }; - - /** - * @enum Type - * @brief Describe the type of an entry - */ - enum Type { - Unknown = 0, - File, - Dir, - Link - }; - - /** - * @struct Entry - * @brief entry in the directory list - */ - struct Entry { - std::string name; //! name of entry (base name) - Type type; //! type of file - - Entry(); - - friend bool operator==(const Entry &e1, const Entry &e2); - }; - - using List = std::vector<Entry>; - - // C++ Container compatibility - using value_type = List::value_type; - using iterator = List::iterator; - using const_iterator = List::const_iterator; - -private: - List m_list; - - void systemLoad(const std::string &path, int flags); - -public: - /** - * Default constructor, does nothing. - */ - Directory(); - - /** - * Open a directory and read all its content. - * @param path the path - * @param flags the optional flags - */ - Directory(const std::string &path, int flags = 0); - - /** - * Return an iterator the beginning. - * - * @return the iterator - */ - List::iterator begin(); - - /** - * Return a const iterator the beginning. - * - * @return the iterator - */ - List::const_iterator cbegin() const; - - /** - * Return an iterator to past the end. - * - * @return the iterator - */ - List::iterator end(); - - /** - * Return a const iterator to past the end. - * - * @return the iterator - */ - List::const_iterator cend() const; - - /** - * Get the number of entries in the directory. - * - * @return the number - */ - int count() const; - - friend bool operator==(const Directory &d1, const Directory &d2); -}; - -#endif // !_DIRECTORY_H_
--- a/C++/Driver.cpp Sun Mar 08 11:07:36 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,155 +0,0 @@ -/* - * Driver.cpp -- generic SQL driver access - * - * Copyright (c) 2013, 2014 David Demelier <markand@malikania.fr> - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include <stdexcept> -#include <sstream> - -#include "Driver.h" - -/* --------------------------------------------------------- - * DriverQuery class - * ---------------------------------------------------------*/ - -DriverQuery::DriverQuery(Ptr impl) - : m_impl(impl) -{ -} - -void DriverQuery::assertRequest(int row, const std::string &column, DriverColumn wanted) -{ - std::ostringstream oss; - - // Out of bounds? - if (row < 0 || row >= countRows()) { - oss << "Invalid row index " << row; - oss << ", expected [0.." << countRows() << "]"; - - throw std::runtime_error(oss.str()); - } - - // Not found or bad column? - if (type(column) != wanted) { - oss << "Invalid or not found column `" << column << "'"; - - throw std::runtime_error(oss.str()); - } -} - -DriverQuery::~DriverQuery() -{ -} - -DriverColumn DriverQuery::type(const std::string &column) const -{ - return m_impl->type(column); -} - -int DriverQuery::countRows() -{ - return m_impl->countRows(); -} - -int DriverQuery::countColumns() -{ - return m_impl->countColumns(); -} - -bool DriverQuery::isNull(int row, const std::string &column) -{ - return m_impl->isNull(row, column); -} - -void DriverQuery::dump() -{ - m_impl->dump(); -} - -/* -------------------------------------------------------- - * DriverRequest class - * -------------------------------------------------------- */ - -DriverRequest::DriverRequest(Ptr impl, const std::string &command) - : m_pos(0) - , m_params(0) - , m_command(command) - , m_impl(impl) -{ - int i = -1; - - while ((i = command.find('#', i + 1)) != std::string::npos) - ++ m_params; -} - -DriverRequest::~DriverRequest() -{ -} - -void DriverRequest::assertCorrect() -{ - if (m_params <= 0) - throw std::runtime_error("no more arguments to set"); -} - -void DriverRequest::setValue(const std::string &value) -{ - assertCorrect(); - - m_pos = m_command.find('#', m_pos); - m_command.replace(m_pos, 1, value); - m_pos += value.length(); - m_params --; -} - -DriverRequest::operator std::string() -{ - return m_command; -} - -/* -------------------------------------------------------- - * Driver class - * -------------------------------------------------------- */ - -void Driver::connect(const Params ¶ms) -{ - m_impl->connect(params); -} - -DriverRequest Driver::prepare(const std::string &command) -{ - return m_impl->prepare(command); -} - -DriverQuery Driver::query(const std::string &sql) -{ - return m_impl->query(sql); -} - -DriverQuery Driver::query(DriverRequest request) -{ - return query(static_cast<std::string>(request)); -} - -std::string Driver::description() const -{ - return m_impl->description(); -} - -std::string Driver::version() const -{ - return m_impl->version(); -}
--- a/C++/Driver.h Sun Mar 08 11:07:36 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,529 +0,0 @@ -/* - * Driver.h -- generic SQL driver access - * - * Copyright (c) 2013, 2014 David Demelier <markand@malikania.fr> - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#ifndef _DRIVER_H_ -#define _DRIVER_H_ - -#include <ctime> -#include <memory> -#include <string> -#include <unordered_map> -#include <type_traits> - -/** - * @enum DriverColumn - * @brief The column type request - * - * Used for the drivers. - */ -enum class DriverColumn { - Invalid, //! not found - Boolean, //! bool or 0 / 1 - Date, //! date see Common/Date.h - Double, //! double - Integer, //! 32 or 64 bit int - String, //! varchar to std::string -}; - -template <typename T> -struct DriverTypeInfo : std::false_type { }; - -/** - * @class Query - * @brief Class for querying the database - * - * That class is returned when a SQL query succeed. It can retrieve the - * number of rows, columns and retrieve the results independantly from the - * driver. - * - * @see Driver::query - */ -class DriverQuery { -public: - friend struct DriverTypeInfo<bool>; - friend struct DriverTypeInfo<time_t>; - friend struct DriverTypeInfo<double>; - friend struct DriverTypeInfo<int>; - friend struct DriverTypeInfo<std::string>; - - class Impl { - public: - /** - * Get a bool. - * - * @param row the row number - * @param column the column - * @return the value - */ - virtual bool getBoolean(int row, const std::string &column) = 0; - - /** - * Get a Date. - * - * @param row the row number - * @param column the column - * @return the value - */ - virtual time_t getDate(int row, const std::string &column) = 0; - - /** - * Get a double. - * - * @param row the row number - * @param column the column - * @return the value - */ - virtual double getDouble(int row, const std::string &column) = 0; - - /** - * Get a integer. - * - * @param row the row number - * @param column the column - * @return the value - */ - virtual int getInt(int row, const std::string &column) = 0; - - /** - * Get a string. - * - * @param row the row number - * @param column the column - * @return the value - */ - virtual std::string getString(int row, const std::string &column) = 0; - - /** - * Returns the type of a named column. - * - * @param column the column name - * @return the type - */ - virtual DriverColumn type(const std::string &column) const = 0; - - /** - * Tells how many rows has been fetched. - * - * @return the number of rows - */ - virtual int countRows() = 0; - - /** - * Tells how many number of columns are present for each - * row. - * - * @return the number of columns - */ - virtual int countColumns() = 0; - - /** - * Tells if the column is null or not. - * - * @param row the row number - * @param column the column - * @return an true if null - */ - virtual bool isNull(int row, const std::string &column) = 0; - - /** - * Dump all rows and columns. - */ - virtual void dump() = 0; - }; - - using Ptr = std::shared_ptr<Impl>; - -private: - /** - * Check if the request is valid and throws an exception - * on error. - * - * @param row the row number - * @param column the column name - * @param type - * @throw Error on error - */ - void assertRequest(int row, const std::string &column, DriverColumn type); - -protected: - Ptr m_impl; - -public: - DriverQuery(Ptr impl); - - /** - * Default destructor. - */ - virtual ~DriverQuery(); - - /** - * Get a variable from a row and column. - * - * Specialization available: - * - bool - * - Date - * - double - * - int - * - std::string - * - * @param row the row number (starts from 0) - * @param column the the column name - * @return the value - * @throw Query::Error on error - */ - template <class T> - T get(int row, const std::string &column) - { - static_assert(DriverTypeInfo<T>::value, "unsupported type"); - - assertRequest(row, column, DriverTypeInfo<T>::type); - - return DriverTypeInfo<T>::get(*this, row, column); - } - - /** - * Returns the type of a named column. - * - * @param column the column name - * @return the type - */ - DriverColumn type(const std::string &column) const; - - /** - * Tells how many rows has been fetched. - * - * @return the number of rows - */ - int countRows(); - - /** - * Tells how many number of columns are present for each - * row. - * - * @return the number of columns - */ - int countColumns(); - - /** - * Tells if the column is null or not. - * - * @param row the row number - * @param column the column - * @return an true if null - */ - bool isNull(int row, const std::string &column); - - /** - * Dump all rows and columns. - */ - void dump(); -}; - -/** - * @class Request - * @brief A secure helper for creating requests - * - * This helps creating class with SQL injection security and such. - */ -class DriverRequest { -public: - friend struct DriverTypeInfo<bool>; - friend struct DriverTypeInfo<time_t>; - friend struct DriverTypeInfo<double>; - friend struct DriverTypeInfo<int>; - friend struct DriverTypeInfo<std::string>; - - class Impl { - public: - /** - * Bind a boolean. - * - * @param value the boolean - * @return the string to use - */ - virtual std::string bindBoolean(bool value) = 0; - - /** - * Bind a date. - * - * @param value the date - * @return the string to use - */ - virtual std::string bindDate(time_t value) = 0; - - /** - * Bind a double. - * - * @param value the double - * @return the string to use - */ - virtual std::string bindDouble(double value) = 0; - - /** - * Bind an integer. - * - * @param value the integer - * @return the string to use - */ - virtual std::string bindInteger(int value) = 0; - - /** - * Bind a string. - * - * @param value the string - * @return the string to use - */ - virtual std::string bindString(std::string value) = 0; - }; - - using Ptr = std::shared_ptr<Impl>; - -private: - size_t m_pos; - int m_params; - std::string m_command; - Ptr m_impl; - - void assertCorrect(); - - void setValue(const std::string &value); - -public: - DriverRequest(Ptr impl, const std::string &command); - - /** - * Default destructor. - */ - virtual ~DriverRequest(); - - /** - * Bind a value. - * - * @param value the value - */ - template <typename T> - void bind(T value) - { - static_assert(DriverTypeInfo<T>::value, "unsupported type"); - - setValue(DriverTypeInfo<T>::bind(value)); - } - - /** - * Convert the request to string. - * - * @return the request as a string - */ - operator std::string(); -}; - -/** - * @class Driver - * @brief A generic SQL driver - * - * This class is used to connect to a database and execute SQL queries. It - * does not include any DBMS code and just call virtual functions for - * a simpler integration of new DBMS drivers. - */ -class Driver { -public: - class Impl; - - using Params = std::unordered_map<std::string, std::string>; - using Ptr = std::shared_ptr<Impl>; - - class Impl { - public: - /** - * Create a synchronous connection, it waits and block until - * the connection is made up to a specified timeout max. - * - * @param params a list of parameters. - */ - virtual void connect(const Params ¶ms) = 0; - - /** - * Prepare a request with the specified SQL command. - * - * @param command the SQL command to parse and bind - * @return a request to use with query - * @see query - */ - virtual DriverRequest prepare(const std::string &command) = 0; - - /** - * Execute a query. - * - * @param query the SQL command - * @return a result - * @throw Query::Error on failure - */ - virtual DriverQuery query(const std::string &command) = 0; - - /** - * Get the driver connection description. - * - * @return the description - */ - virtual std::string description() const = 0; - - /** - * Get the driver version as a string. - * - * @return the version - */ - virtual std::string version() const = 0; - }; - -protected: - Ptr m_impl; - -public: - Driver() = default; - - /** - * Virtual destructor. - */ - virtual ~Driver() = default; - - /** - * Wrapper for std::string variant. - * - * @param request the request to use - * @return a result - * @throw Query::Error on failure - */ - DriverQuery query(DriverRequest request); - - /** - * Create a synchronous connection, it waits and block until - * the connection is made up to a specified timeout max. - * - * @param params a list of parameters. - */ - void connect(const Params ¶ms); - - /** - * Prepare a request with the specified SQL command. - * - * @param command the SQL command to parse and bind - * @return a request to use with query - * @see query - */ - DriverRequest prepare(const std::string &command); - - /** - * Execute a query. - * - * @param query the SQL command - * @return a result - * @throw Query::Error on failure - */ - DriverQuery query(const std::string &command); - - /** - * Get the driver connection description. - * - * @return the description - */ - std::string description() const; - - /** - * Get the driver version as a string. - * - * @return the version - */ - std::string version() const; -}; - -template <> -struct DriverTypeInfo<bool> : std::true_type { - static const DriverColumn type = DriverColumn::Boolean; - - static bool get(DriverQuery &query, int row, const std::string &column) - { - return query.m_impl->getBoolean(row, column); - } - - static void bind(DriverRequest &request, bool value) - { - request.m_impl->bindBoolean(value); - } -}; - -template <> -struct DriverTypeInfo<time_t> : std::true_type { - static const DriverColumn type = DriverColumn::Date; - - static time_t get(DriverQuery &query, int row, const std::string &column) - { - return query.m_impl->getDate(row, column); - } - - static void bind(DriverRequest &request, time_t value) - { - request.m_impl->bindDate(value); - } -}; - -template <> -struct DriverTypeInfo<double> : std::true_type { - static const DriverColumn type = DriverColumn::Double; - - static double get(DriverQuery &query, int row, const std::string &column) - { - return query.m_impl->getDouble(row, column); - } - - static void bind(DriverRequest &request, double value) - { - request.m_impl->bindDouble(value); - } -}; - -template <> -struct DriverTypeInfo<int> : std::true_type { - static const DriverColumn type = DriverColumn::Integer; - - static int get(DriverQuery &query, int row, const std::string &column) - { - return query.m_impl->getInt(row, column); - } - - static void bind(DriverRequest &request, int value) - { - request.m_impl->bindInteger(value); - } -}; - -template <> -struct DriverTypeInfo<std::string> : std::true_type { - static const DriverColumn type = DriverColumn::String; - - static std::string get(DriverQuery &query, int row, const std::string &column) - { - return query.m_impl->getString(row, column); - } - - static void bind(DriverRequest &request, const std::string &value) - { - request.m_impl->bindString(value); - } -}; - -#endif // !_DRIVER_H_
--- a/C++/DriverPostgres.cpp Sun Mar 08 11:07:36 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,482 +0,0 @@ -/* - * DriverPostgres.cpp -- PostgreSQL driver - * - * Copyright (c) 2013, 2014 David Demelier <markand@malikania.fr> - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include <cerrno> -#include <cstring> -#include <iostream> -#include <stdexcept> -#include <sstream> -#include <vector> - -#include <libpq-fe.h> - -#include "DriverPostgres.h" - -namespace { - -using PostgresResult = std::shared_ptr<PGresult>; -using PostgresConn = std::shared_ptr<PGconn>; - -} - -/** - * @class QueryPostgres - * @brief Query implementation for PostgreSQL. - */ -class QueryPostgresImpl : public DriverQuery::Impl { -private: - PostgresConn m_connection; - PostgresResult m_result; - -public: - /** - * Constructor used by DriverPostgres - * - * @param result the result - */ - QueryPostgresImpl(PostgresConn conn, PostgresResult result); - - /** - * @copydoc Query::getBoolean - */ - virtual bool getBoolean(int row, const std::string &column); - - /** - * @copydoc Query::getDate - */ - virtual time_t getDate(int row, const std::string &column); - - /** - * @copydoc Query::getDouble - */ - virtual double getDouble(int row, const std::string &column); - - /** - * @copydoc Query::getInt - */ - virtual int getInt(int row, const std::string &column); - - /** - * @copydoc Query::getString - */ - virtual std::string getString(int row, const std::string &column); - - /** - * @copydoc Query::type - */ - virtual DriverColumn type(const std::string &column) const; - - /** - * @copydoc Query::countRows - */ - virtual int countRows(); - - /** - * @copydoc Query::countColumns - */ - virtual int countColumns(); - - /** - * @copydoc Query::isNull - */ - virtual bool isNull(int row, const std::string &column); - - /** - * @copydoc Query::dump - */ - virtual void dump(); -}; - -QueryPostgresImpl::QueryPostgresImpl(PostgresConn conn, PostgresResult result) - : m_connection(conn) - , m_result(result) -{ -} - -DriverColumn QueryPostgresImpl::type(const std::string &column) const -{ - DriverColumn type; - int pqType, index; - - index = PQfnumber(m_result.get(), column.c_str()); - pqType = PQftype(m_result.get(), index); - switch (pqType) { - case 16: - type = DriverColumn::Boolean; - break; - case 1082: - case 1083: - case 1114: - case 1184: - type = DriverColumn::Date; - break; - case 1700: - case 700: - case 701: - type = DriverColumn::Double; - break; - case 20: - case 21: - case 23: - type = DriverColumn::Integer; - break; - case 25: - case 1042: - case 1043: - type = DriverColumn::String; - break; - default: - type = DriverColumn::Invalid; - } - - return type; -} - -int QueryPostgresImpl::countRows() -{ - return PQntuples(m_result.get()); -} - -int QueryPostgresImpl::countColumns() -{ - return PQnfields(m_result.get()); -} - -bool QueryPostgresImpl::isNull(int row, const std::string &column) -{ - int idx = PQfnumber(m_result.get(), column.c_str()); - - return PQgetisnull(m_result.get(), row, idx) == 1; -} - -void QueryPostgresImpl::dump(void) -{ - std::cout << "Dumping PostgreSQL result, "; - std::cout << countRows() << " rows, "; - std::cout << countColumns() << " columns" << std::endl; - - for (int r = 0; r < countRows(); ++r) { - std::cout << "Dumping row " << r << std::endl; - std::cout << "==============================" << std::endl; - - for (int c = 0; c < countColumns(); ++c) { - std::cout << "\t" << PQfname(m_result.get(), c); - std::cout << " = " << PQgetvalue(m_result.get(), r, c) << std::endl; - } - } -} - -bool QueryPostgresImpl::getBoolean(int row, const std::string &column) -{ - int idx = PQfnumber(m_result.get(), column.c_str()); - std::string code = PQgetvalue(m_result.get(), row, idx); - - return code[0] == 't'; -} - -time_t QueryPostgresImpl::getDate(int row, const std::string &column) -{ - int idx = PQfnumber(m_result.get(), column.c_str()); - time_t timestamp = std::time(nullptr); - - try { - std::ostringstream oss; - std::string value, req; - - value = PQgetvalue(m_result.get(), row, idx); - - /* - * Convert the date using the SQL function so that user does - * not require any explicit conversion itself. - */ - printf("%s\n", value.c_str()); - oss << "Select EXTRACT(EPOCH FROM TIMESTAMP WITH TIME ZONE '"; - oss << value; - oss << "') AS RESULT"; - req = oss.str(); - - auto info = PQexec(m_connection.get(), oss.str().c_str()); - auto status = PQresultStatus(info); - - if (info != nullptr && (status == PGRES_COMMAND_OK || - status == PGRES_TUPLES_OK)) - { - timestamp = atoi(PQgetvalue(info, 0, 0)); - PQclear(info); - } - } catch (...) { } - - return timestamp; -} - -double QueryPostgresImpl::getDouble(int row, const std::string &column) -{ - int idx = PQfnumber(m_result.get(), column.c_str()); - - return std::stod(PQgetvalue(m_result.get(), row, idx)); -} - -int QueryPostgresImpl::getInt(int row, const std::string &column) -{ - int idx = PQfnumber(m_result.get(), column.c_str()); - - return std::stoi(PQgetvalue(m_result.get(), row, idx)); -} - -std::string QueryPostgresImpl::getString(int row, const std::string &column) -{ - int idx = PQfnumber(m_result.get(), column.c_str()); - - return std::string(PQgetvalue(m_result.get(), row, idx)); -} - -/* -------------------------------------------------------- - * Request PostgreSQL Impl - * -------------------------------------------------------- */ - -/** - * @class RequestPostgres - * @brief Request implementation for PostgreSQL. - */ -class RequestPostgres : public DriverRequest::Impl { -private: - std::shared_ptr<PGconn> m_connection; - -protected: - /** - * @copydoc Request::bindBoolean - */ - virtual std::string bindBoolean(bool value); - - /** - * @copydoc Request::bindDate - */ - virtual std::string bindDate(time_t value); - - /** - * @copydoc Request::bindDouble - */ - virtual std::string bindDouble(double value); - - /** - * @copydoc Request::bindInteger - */ - virtual std::string bindInteger(int value); - - /** - * @copydoc Request::bindString - */ - virtual std::string bindString(std::string value); - -public: - /** - * Construct a request for PostgreSQL. - * - * @param conn the connection - * @param command the command - */ - RequestPostgres(PostgresConn &conn); -}; - -RequestPostgres::RequestPostgres(PostgresConn &conn) -{ - m_connection = conn; -} - -std::string RequestPostgres::bindBoolean(bool value) -{ - return (value) ? "'t'" : "'f'"; -} - -std::string RequestPostgres::bindDate(time_t value) -{ - std::ostringstream oss; - - oss << "to_timestamp(" << value << ")"; - - return oss.str(); -} - -std::string RequestPostgres::bindDouble(double value) -{ - return std::to_string(value); -} - -std::string RequestPostgres::bindInteger(int value) -{ - return std::to_string(value); -} - -std::string RequestPostgres::bindString(std::string value) -{ - std::string result; - char *tmp; - - tmp = PQescapeLiteral(m_connection.get(), value.c_str(), value.length()); - if (tmp == nullptr) - return ""; - - result = std::string(tmp); - PQfreemem(tmp); - - return result; -} - -/* -------------------------------------------------------- - * Driver PostgreSQL impl - * -------------------------------------------------------- */ - -/** - * @class DriverPostgres - * @brief Driver implementation for PostgreSQL. - */ -class DriverPostgresImpl : public Driver::Impl { -private: - PostgresConn m_connection; - - /** - * Convert the Params from the config - * to the PostgreSQL string. - * - * @param settings the table - * @return a string to be used - */ - std::string convert(Driver::Params &settings); - -public: - /** - * @copydoc Driver::connect - */ - virtual void connect(const Driver::Params ¶ms); - - /** - * @copydoc Driver::prepare - */ - virtual DriverRequest prepare(const std::string &command); - - /** - * @copydoc Driver::query - */ - virtual DriverQuery query(const std::string &command); - - /** - * @copydoc Driver::description - */ - virtual std::string description() const; - - /** - * @copydoc Driver::version - */ - virtual std::string version() const; -}; - -std::string DriverPostgresImpl::convert(Driver::Params ¶ms) -{ - std::ostringstream oss; - std::vector<std::string> required { "host", "port", "user", "database", "password" }; - - for (auto s : required) - if (params.count(s) <= 0) - throw std::runtime_error("missing parameter " + s); - - oss << "host = " << params["host"] << " "; - oss << "port = " << params["port"] << " "; - oss << "user = " << params["user"] << " "; - oss << "dbname = " << params["database"] << " "; - oss << "password = " << params["password"]; - - return oss.str(); -} - -void DriverPostgresImpl::connect(const Driver::Params ¶ms) -{ - auto copy = params; - auto conn = PQconnectdb(convert(copy).c_str()); - - if (conn == nullptr) - throw std::runtime_error(std::strerror(ENOMEM)); - - if (PQstatus(conn) == CONNECTION_BAD) { - auto error = PQerrorMessage(conn); - PQfinish(conn); - - throw std::runtime_error(error); - } - - m_connection = std::shared_ptr<PGconn>(conn, PQfinish); -} - -DriverRequest DriverPostgresImpl::prepare(const std::string &command) -{ - return DriverRequest(std::make_shared<RequestPostgres>(m_connection), command); -} - -DriverQuery DriverPostgresImpl::query(const std::string &cmd) -{ - PGresult *info; - - // If NULL, the libpq said no memory - info = PQexec(m_connection.get(), cmd.c_str()); - if (info == nullptr) - throw std::runtime_error(strerror(ENOMEM)); - - // If an error occured - int errorCode = PQresultStatus(info); - if (errorCode != PGRES_COMMAND_OK && errorCode != PGRES_TUPLES_OK) { - auto error = PQresultErrorMessage(info); - PQclear(info); - - throw std::runtime_error(error); - } - - auto result = std::shared_ptr<PGresult>(info, PQclear); - auto impl = std::make_shared<QueryPostgresImpl>(m_connection, result); - - return DriverQuery(impl); -} - -std::string DriverPostgresImpl::description() const -{ - std::ostringstream oss; - - oss << "Connected on PostgreSQL database: " << std::endl; - oss << " host: " << PQhost(m_connection.get()) << std::endl; - oss << " port: " << PQport(m_connection.get()) << std::endl; - oss << " user: " << PQuser(m_connection.get()) << std::endl; - oss << " database: " << PQdb(m_connection.get()) << std::endl; - - return oss.str(); -} - -std::string DriverPostgresImpl::version() const -{ - std::ostringstream oss; - - oss << "PostgreSQL driver (version " << PQlibVersion() << ")"; - - return oss.str(); -} - -/* -------------------------------------------------------- - * Driver PostgreSQL - * -------------------------------------------------------- */ - -DriverPostgres::DriverPostgres() -{ - m_impl = std::make_shared<DriverPostgresImpl>(); -}
--- a/C++/DriverPostgres.h Sun Mar 08 11:07:36 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,33 +0,0 @@ -/* - * DriverPostgres.h -- PostgreSQL driver - * - * Copyright (c) 2013, 2014 David Demelier <markand@malikania.fr> - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -/* - * http://www.postgresql.org/docs/9.2/static/libpq-connect.html#LIBPQ-PARAMKEYWORDS - */ - -#ifndef _DRIVER_PG_H_ -#define _DRIVER_PG_H_ - -#include "Driver.h" - -class DriverPostgres : public Driver { -public: - DriverPostgres(); -}; - -#endif // !_DRIVER_PG_H_
--- a/C++/DynLib.cpp Sun Mar 08 11:07:36 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,144 +0,0 @@ -/* - * DynLib.cpp -- portable shared library loader - * - * Copyright (c) 2013, 2014 David Demelier <markand@malikania.fr> - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include <stdexcept> - -#if defined(_WIN32) -# include <Windows.h> -#else -# include <dlfcn.h> -#endif - -#include "DynLib.h" - -#if defined(_WIN32) - -namespace { - -std::string systemError() -{ - LPSTR error = nullptr; - std::string errmsg = "Unknown error"; - - FormatMessageA( - FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, - NULL, - GetLastError(), - MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), - (LPSTR)&error, 0, NULL); - - if (error) { - errmsg = std::string(error); - LocalFree(error); - } - - return errmsg; -} - -} - -void DynLib::systemInit() -{ - m_handle = nullptr; -} - -DynLib::Handle DynLib::systemLoad(const std::string &path, Policy policy) const -{ - Handle handle = LoadLibraryA(path.c_str()); - - if (handle == nullptr) - throw std::runtime_error(systemError()); - - return handle; -} - -DynLib::Sym DynLib::systemSym(const std::string &name) -{ - Sym sym; - - if (m_handle == nullptr) - throw std::runtime_error("library not loaded"); - - sym = GetProcAddress(m_handle, name.c_str()); - if (sym == nullptr) - throw std::out_of_range(systemError()); - - return sym; -} - -void DynLib::systemClose() -{ - if (m_handle != nullptr) - FreeLibrary(m_handle); -} - -#else - -void DynLib::systemInit() -{ - m_handle = nullptr; -} - -DynLib::Handle DynLib::systemLoad(const std::string &path, Policy policy) const -{ - int mode = (policy == Immediately) ? RTLD_NOW : RTLD_LAZY; - Handle handle; - - handle = dlopen(path.c_str(), mode); - if (handle == nullptr) - throw std::runtime_error(dlerror()); - - return handle; -} - -DynLib::Sym DynLib::systemSym(const std::string &name) -{ - Sym sym; - - if (m_handle == nullptr) - throw std::runtime_error("library not loaded"); - - sym = dlsym(m_handle, name.c_str()); - if (sym == nullptr) - throw std::out_of_range(dlerror()); - - return sym; -} - -void DynLib::systemClose() -{ - if (m_handle != nullptr) - dlclose(m_handle); -} - -#endif - -DynLib::DynLib() -{ - systemInit(); -} - -DynLib::DynLib(const std::string &path, Policy policy) -{ - m_handle = systemLoad(path, policy); -} - -DynLib::~DynLib() -{ - systemClose(); -}
--- a/C++/DynLib.h Sun Mar 08 11:07:36 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,104 +0,0 @@ -/* - * DynLib.h -- portable shared library loader - * - * Copyright (c) 2013, 2014 David Demelier <markand@malikania.fr> - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#ifndef _DYN_LIB_H_ -#define _DYN_LIB_H_ - -#include <string> - -#if defined(_WIN32) -# include <Windows.h> -# define DYNLIB_EXPORT __declspec(dllexport) -#else -# define DYNLIB_EXPORT -#endif - -/** - * @class DynLib - * @brief Load a dynamic module - * - * This class is a portable wrapper to load shared libraries on - * supported systems. - */ -class DynLib { -public: -#if defined(_WIN32) - using Handle = HMODULE; - using Sym = FARPROC; -#else - using Handle = void *; - using Sym = void *; -#endif - - enum Policy { - Immediately, //! load symbols immediately - Lazy //! load symbols when needed - }; - -private: - Handle m_handle; - - void systemInit(); - Handle systemLoad(const std::string &path, Policy policy) const; - Sym systemSym(const std::string &name); - void systemClose(); - -public: - /** - * Copy is forbidden. - */ - DynLib(const DynLib &) = delete; - DynLib &operator=(const DynLib &) = delete; - - /** - * Default constructor. - */ - DynLib(); - - /** - * Constructor to load a shared module. The path must - * be absolute. - * - * @param path the absolute path - * @param policy the policy to load - * @throw std::runtime_error on error - */ - DynLib(const std::string &path, - Policy policy = Immediately); - - /** - * Close the library automatically. - */ - ~DynLib(); - - /** - * Get a symbol from the library. - * - * @param name the symbol - * @return the symbol - * @throw std::runtime_error on error - * @throw std::out_of_range if not found - */ - template <typename T> - T sym(const std::string &name) - { - return reinterpret_cast<T>(systemSym(name)); - } -}; - -#endif // !_DYN_LIB_H_
--- a/C++/Flags.h Sun Mar 08 11:07:36 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,341 +0,0 @@ -/* - * Flags.h -- safe wrapper for enum flags - * - * Copyright (c) 2013, 2014 David Demelier <markand@malikania.fr> - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#ifndef _FLAGS_H_ -#define _FLAGS_H_ - -/** - * @file Flags.h - * @brief Provide template class Flags and operators for enum classes - */ - -#include <type_traits> - -/** - * @class Flags - * @brief Wrapper that store enum flags - * - * This class is used to store an enum and make bitwise operations on it. User do not need - * to supply these operators because they are implemented in the class. - * - * This class is very cheap and store the real underlying type from the enum and all functions - * are inlined. - */ -template <typename Enum, typename Type = std::underlying_type_t<Enum>> -class Flags final { -private: - Type m_value { 0 }; - -public: - /** - * Construct flags to 0. - */ - constexpr Flags() noexcept = default; - - /** - * Construct flags with the enum value. - * - * @param m the mask - */ - constexpr Flags(Enum m) noexcept - : m_value(static_cast<Type>(m)) - { - } - - /** - * Construct flags with the enum value. - * - * @param m the mask - */ - constexpr Flags(Type m) noexcept - : m_value(m) - { - } - - /** - * Move constructor. - * - * @param other the other - */ - inline Flags(Flags &&other) noexcept = default; - - /** - * Copy constructor. - * - * @param other the other - */ - inline Flags(const Flags &other) noexcept = default; - - /** - * Move operator. - * - * @param other the other - */ - inline Flags &operator=(Flags &&other) noexcept = default; - - /** - * Copy operator. - * - * @param other the other - */ - inline Flags &operator=(const Flags &other) noexcept = default; - - /** - * &= operator. - * - * @param m the mask - * @return *this - */ - inline Flags &operator&=(Enum m) noexcept - { - m_value &= static_cast<Type>(m); - - return *this; - } - - /** - * &= operator. - * - * @param m the mask - * @return *this - */ - inline Flags &operator&=(Type m) noexcept - { - m_value &= m; - - return *this; - } - - /** - * |= operator. - * - * @param m the mask - * @return *this - */ - inline Flags &operator|=(Enum m) noexcept - { - m_value |= static_cast<Type>(m); - - return *this; - } - - /** - * |= operator. - * - * @param m the mask - * @return *this - */ - inline Flags &operator|=(Type m) noexcept - { - m_value |= m; - - return *this; - } - - /** - * ^= operator. - * - * @param m the mask - * @return *this - */ - inline Flags &operator^=(Enum m) noexcept - { - m_value ^= static_cast<Type>(m); - - return *this; - } - - /** - * ^= operator. - * - * @param m the mask - * @return *this - */ - inline Flags &operator^=(Type m) noexcept - { - m_value ^= m; - - return *this; - } - - /** - * & operator. - * - * @param m the mask - * @return & combination - */ - constexpr Flags operator&(Enum m) const noexcept - { - return m_value & static_cast<Type>(m); - } - - /** - * & operator. - * - * @param m the mask - * @return & combination - */ - constexpr Flags operator&(Type m) const noexcept - { - return m_value & m; - } - - /** - * | operator. - * - * @param m the mask - * @return | combination - */ - constexpr Flags operator|(Enum m) const noexcept - { - return m_value | static_cast<Type>(m); - } - - /** - * | operator. - * - * @param m the mask - * @return | combination - */ - constexpr Flags operator|(Type m) const noexcept - { - return m_value | m; - } - - /** - * ^ operator. - * - * @param m the mask - * @return ^ combination - */ - constexpr Flags operator^(Enum m) const noexcept - { - return m_value & static_cast<Type>(m); - } - - /** - * ^ operator. - * - * @param m the mask - * @return ^ combination - */ - constexpr Flags operator^(Type m) const noexcept - { - return m_value & m; - } - - /** - * ~ unary operator. - * - * @return ~ - */ - constexpr Flags operator~() const noexcept - { - return ~m_value; - } - - /** - * Convert to bool. Simply check if value is not 0. - * - * @return true if value != 0 - */ - constexpr operator bool() const noexcept - { - return m_value != 0; - } - - /** - * Operator !. Simply check if value is 0. - * - * @return true if value == 0 - */ - constexpr bool operator!() const noexcept - { - return m_value == 0; - } - - /** - * Test comparison with other mask. - * - * @return true if this value == m - */ - constexpr bool operator==(Enum m) const noexcept - { - return m_value == static_cast<Type>(m); - } - - /** - * Test comparison with other mask. - * - * @return true if this value == m - */ - constexpr bool operator==(Type m) const noexcept - { - return m_value == m; - } -}; - -/** - * Apply & operator on the enum. - * - * @param x1 the first value - * @param x2 the second value - */ -template <typename Value, typename Type = std::enable_if_t<std::is_enum<Value>::value, std::underlying_type_t<Value>>> -constexpr Value operator&(Value x1, Value x2) noexcept -{ - return static_cast<Value>(static_cast<Type>(x1) & static_cast<Type>(x2)); -} - -/** - * Apply | operator on the enum. - * - * @param x1 the first value - * @param x2 the second value - */ -template <typename Value, typename Type = std::enable_if_t<std::is_enum<Value>::value, std::underlying_type_t<Value>>> -constexpr Value operator|(Value x1, Value x2) noexcept -{ - return static_cast<Value>(static_cast<Type>(x1) | static_cast<Type>(x2)); -} - -/** - * Apply ^ operator on the enum. - * - * @param x1 the first value - * @param x2 the second value - */ -template <typename Value, typename Type = std::enable_if_t<std::is_enum<Value>::value, std::underlying_type_t<Value>>> -constexpr Value operator^(Value x1, Value x2) noexcept -{ - return static_cast<Value>(static_cast<Type>(x1) ^ static_cast<Type>(x2)); -} - -/** - * Apply ~ operator on the enum. - * - * @param x1 the first value - * @param x2 the second value - */ -template <typename Value, typename Type = std::enable_if_t<std::is_enum<Value>::value, std::underlying_type_t<Value>>> -constexpr Value operator~(Value x) noexcept -{ - return static_cast<Value>(~static_cast<Type>(x)); -} - -#endif // !_FLAGS_H_ \ No newline at end of file
--- a/C++/Hash.cpp Sun Mar 08 11:07:36 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,42 +0,0 @@ -/* - * Hash.cpp -- hash functions - * - * Copyright (c) 2013, 2014 David Demelier <markand@malikania.fr> - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include "Hash.h" - -#include <openssl/sha.h> -#include <openssl/md5.h> - -std::string Hash::md5(const std::string &input) -{ - return convert<MD5_CTX, MD5_DIGEST_LENGTH>(input, MD5_Init, MD5_Update, MD5_Final); -} - -std::string Hash::sha1(const std::string &input) -{ - return convert<SHA_CTX, SHA_DIGEST_LENGTH>(input, SHA1_Init, SHA1_Update, SHA1_Final); -} - -std::string Hash::sha256(const std::string &input) -{ - return convert<SHA256_CTX, SHA256_DIGEST_LENGTH>(input, SHA256_Init, SHA256_Update, SHA256_Final); -} - -std::string Hash::sha512(const std::string &input) -{ - return convert<SHA512_CTX, SHA512_DIGEST_LENGTH>(input, SHA512_Init, SHA512_Update, SHA512_Final); -}
--- a/C++/Hash.h Sun Mar 08 11:07:36 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,100 +0,0 @@ -/* - * Hash.h -- hash functions - * - * Copyright (c) 2013, 2014 David Demelier <markand@malikania.fr> - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#ifndef _HASH_H_ -#define _HASH_H_ - -/** - * @file Hash.h - * @brief Hash functions - */ - -#include <string> - -/** - * @class Hash - * @brief Hash functions - * - * Provide support for MD5, SHA1, SHA256 and SHA512. - */ -class Hash { -private: - template <typename Context> - using Init = int (*)(Context *); - - template <typename Context> - using Update = int (*)(Context *, const void *, size_t); - - template <typename Context> - using Final = int (*)(unsigned char *, Context *); - - template <typename Context, size_t Length> - static std::string convert(const std::string &input, - Init<Context> init, - Update<Context> update, - Final<Context> finalize) - { - unsigned char digest[Length]; - char hash[Length * 2 + 1]; - - Context ctx; - init(&ctx); - update(&ctx, input.c_str(), input.length()); - finalize(digest, &ctx); - - for (unsigned long i = 0; i < Length; i++) - sprintf(&hash[i * 2], "%02x", (unsigned int)digest[i]); - - return std::string(hash); - } - -public: - /** - * Hash using MD5. - * - * @param input the input string - * @return the hashed string - */ - static std::string md5(const std::string &input); - - /** - * Hash using SHA1. - * - * @param input the input string - * @return the hashed string - */ - static std::string sha1(const std::string &input); - - /** - * Hash using SHA256. - * - * @param input the input string - * @return the hashed string - */ - static std::string sha256(const std::string &input); - - /** - * Hash using SHA512. - * - * @param input the input string - * @return the hashed string - */ - static std::string sha512(const std::string &input); -}; - -#endif // !_HASH_H_
--- a/C++/Ini.cpp Sun Mar 08 11:07:36 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,481 +0,0 @@ -/* - * Ini.cpp -- .ini file parsing - * - * Copyright (c) 2013, 2014 David Demelier <markand@malikania.fr> - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include <cctype> -#include <cerrno> -#include <cstring> -#include <fstream> -#include <iostream> -#include <iterator> -#include <memory> -#include <ostream> -#include <sstream> -#include <vector> - -#if defined(_WIN32) -# include <Shlwapi.h> // for PathIsRelative -#endif - -#include "Ini.h" - -namespace { - -/* -------------------------------------------------------- - * Tokens - * -------------------------------------------------------- */ - -enum class TokenType { - Comment = '#', - SectionBegin = '[', - SectionEnd = ']', - Escape = '\\', - QuoteSimple = '\'', - QuoteDouble = '"', - NewLine = '\n', - Assign = '=', - Include = '@', - Word, - Space -}; - -std::ostream &operator<<(std::ostream &out, const TokenType &type) -{ - switch (type) { - case TokenType::Comment: - out << "Comment"; - break; - case TokenType::SectionBegin: - out << "SectionBegin"; - break; - case TokenType::SectionEnd: - out << "SectionEnd"; - break; - case TokenType::Escape: - out << "Escape"; - break; - case TokenType::QuoteSimple: - out << "QuoteSimple"; - break; - case TokenType::QuoteDouble: - out << "QuoteDouble"; - break; - case TokenType::NewLine: - out << "NewLine"; - break; - case TokenType::Assign: - out << "Assign"; - break; - case TokenType::Include: - out << "Include"; - break; - case TokenType::Word: - out << "Word"; - break; - case TokenType::Space: - out << "Space"; - break; - default: - break; - } - - return out; -} - -class Token { -private: - TokenType m_type; - int m_line; - int m_position; - std::string m_value; - -public: - inline Token(TokenType type, int line, int position, std::string value = "") - : m_type(type) - , m_line(line) - , m_position(position) - , m_value(std::move(value)) - { - } - - inline TokenType type() const noexcept - { - return m_type; - } - - inline int line() const noexcept - { - return m_line; - } - - inline int position() const noexcept - { - return m_position; - } - - inline std::string value() const - { - switch (m_type) { - case TokenType::Comment: - return "#"; - case TokenType::SectionBegin: - return "["; - case TokenType::SectionEnd: - return "]"; - case TokenType::QuoteSimple: - return "'"; - case TokenType::QuoteDouble: - return "\""; - case TokenType::NewLine: - return "\n"; - case TokenType::Assign: - return "="; - case TokenType::Include: - return "@"; - case TokenType::Space: - return m_value; - case TokenType::Word: - return m_value; - default: - break; - } - - return ""; - } - - inline std::string toString() const - { - switch (m_type) { - case TokenType::Comment: - return "'#'"; - case TokenType::SectionBegin: - return "'['"; - case TokenType::SectionEnd: - return "']'"; - case TokenType::QuoteSimple: - return "'"; - case TokenType::QuoteDouble: - return "\""; - case TokenType::NewLine: - return "<newline>"; - case TokenType::Assign: - return "="; - case TokenType::Include: - return "@"; - case TokenType::Space: - return "<blank>"; - case TokenType::Word: - return "`" + m_value + "'"; - default: - break; - } - - return ""; - } -}; - -std::ostream &operator<<(std::ostream &out, const Token &token) -{ - out << token.type(); - - if (token.type() == TokenType::Space) { - out << ": size = " << token.value().size(); - } else if (token.type() == TokenType::Word) { - out << ": value = [" << token.value() << "]"; - } - - return out; -} - -using TokenStack = std::vector<Token>; - -/* -------------------------------------------------------- - * IniBuilder - * -------------------------------------------------------- */ - -class IniBuilder { -private: - std::string m_path; - std::string m_base; - Ini &m_ini; - -private: - inline bool isReserved(char c) const noexcept - { - return c == '\n' || c == '#' || c == '"' || c == '\'' || c == '=' || c == '[' || c == ']' || c == '@'; - } - - std::string base(std::string path) - { - auto pos = path.find_last_of("/\\"); - - if (pos != std::string::npos) { - path.erase(pos); - } else { - path = "."; - } - - return path; - } - -#if defined(_WIN32) - bool isAbsolute(const std::string &path) - { - return !PathIsRelative(path.c_str()); - } -#else - bool isAbsolute(const std::string &path) - { - return path.size() > 0 && path[0] == '/'; - } -#endif - - std::vector<Token> analyze(std::istream &stream) const - { - std::istreambuf_iterator<char> it(stream); - std::istreambuf_iterator<char> end; - std::vector<Token> tokens; - - int lineno{1}; - int position{0}; - - while (it != end) { - std::string value; - - if (isReserved(*it)) { - while (it != end && isReserved(*it)) { - // Single character tokens - switch (*it) { - case '\n': - ++lineno; - position = 0; - case '#': - case '[': - case ']': - case '\'': - case '"': - case '=': - case '@': - tokens.push_back({ static_cast<TokenType>(*it), lineno, position }); - ++it; - ++position; - default: - break; - } - } - } else if (std::isspace(*it)) { - while (it != end && std::isspace(*it) && *it != '\n') { - value.push_back(*it++); - } - - tokens.push_back({ TokenType::Space, lineno, position, std::move(value) }); - } else { - while (it != end && !std::isspace(*it) && !isReserved(*it)) { - value.push_back(*it++); - } - - tokens.push_back({ TokenType::Word, lineno, position, std::move(value) }); - } - } - - return tokens; - } - - void readComment(TokenStack::iterator &it, TokenStack::iterator end) - { - while (it != end && it->type() != TokenType::NewLine) { - ++ it; - } - - // remove new line - ++ it; - } - - void readSpace(TokenStack::iterator &it, TokenStack::iterator end) - { - while (it != end && it->type() == TokenType::Space) { - ++ it; - } - } - - void readNewLine(TokenStack::iterator &it, TokenStack::iterator end) - { - while (it != end && it->type() == TokenType::NewLine) { - ++ it; - } - } - - IniSection readSection(TokenStack::iterator &it, TokenStack::iterator end) - { - if (++it == end || it->type() != TokenType::Word) { - throw IniError(it->line(), it->position(), "word expected after [, got " + it->toString()); - } - - IniSection section(it->value()); - - if (++it == end || it->type() != TokenType::SectionEnd) { - throw IniError(it->line(), it->position(), "] expected, got " + it->toString()); - } - - // Remove ] - ++ it; - - if (it == end) { - return section; - } - - while (it != end && it->type() != TokenType::SectionBegin) { - if (it->type() == TokenType::Space) { - readSpace(it, end); - } else if (it->type() == TokenType::NewLine) { - readNewLine(it, end); - } else if (it->type() == TokenType::Comment) { - readComment(it, end); - } else if (it->type() == TokenType::Word) { - section.push_back(readOption(it, end)); - } else { - throw IniError(it->line(), it->position(), "unexpected token " + it->toString()); - } - } - - return section; - } - - IniOption readOption(TokenStack::iterator &it, TokenStack::iterator end) - { - std::string key = it->value(); - - if (++it == end) { - throw IniError(it->line(), it->position(), "expected '=' after option declaration, got <EOF>"); - } - - readSpace(it, end); - - if (it == end || it->type() != TokenType::Assign) { - throw IniError(it->line(), it->position(), "expected '=' after option declaration, got " + it++->toString()); - } - - readSpace(++it, end); - - std::ostringstream oss; - - if (it->type() == TokenType::QuoteSimple || it->type() == TokenType::QuoteDouble) { - TokenStack::iterator save = it++; - - while (it != end && it->type() != save->type()) { - oss << it++->value(); - } - - if (it == end) - throw IniError(save->line(), save->position(), "undisclosed quote: " + save->toString() + " expected"); - - ++ it; - } else if (it->type() == TokenType::Word) { - oss << it++->value(); - } else if (it->type() != TokenType::NewLine && it->type() != TokenType::Comment) { - // No value requested, must be NewLine or comment - throw IniError(it->line(), it->position(), "expected option value after '=', got " + it->toString()); - } - - - return IniOption(std::move(key), oss.str()); - } - - void readInclude(TokenStack::iterator &it, TokenStack::iterator end) - { - if (++it == end || (it->type() != TokenType::Word || it->value() != "include")) { - throw IniError(it->line(), it->position(), "expected `include' after '@' token, got " + it->toString()); - } - - readSpace(++it, end); - - // Quotes mandatory - TokenStack::iterator save = it; - - if (it == end || (it->type() != TokenType::QuoteSimple && it->type() != TokenType::QuoteDouble)) { - throw IniError(it->line(), it->position(), "expected filename after @include statement"); - } - - // Filename - if (++it == end || it->type() != TokenType::Word) { - throw IniError(it->line(), it->position(), "expected filename after @include statement"); - } - - std::string value = it->value(); - std::string fullpath; - - if (isAbsolute(value)) { - fullpath = value; - } else { - fullpath = m_base + "/" + it->value(); - } - - // Must be closed with the same quote - if (++it == end || it->type() != save->type()) { - throw IniError(save->line(), save->position(), "undiclosed quote: " + save->toString() + " expected"); - } - - // Remove quote - ++ it; - - IniBuilder(m_ini, fullpath); - } - -public: - IniBuilder(Ini &ini, std::string path) - : m_path(path) - , m_base(base(std::move(path))) - , m_ini(ini) - { - std::ifstream file(m_path); - - if (!file.is_open()) - throw std::runtime_error(std::strerror(errno)); - - std::vector<Token> ts = analyze(file); - - auto it = ts.begin(); - auto end = ts.end(); - - while (it != end) { - if (it->type() == TokenType::Space) { - readSpace(it, end); - } else if (it->type() == TokenType::NewLine) { - readNewLine(it, end); - } else if (it->type() == TokenType::Comment) { - readComment(it, end); - } else if (it->type() == TokenType::Include) { - readInclude(it, end); - } else if (it->type() == TokenType::SectionBegin) { - m_ini.push_back(readSection(it, end)); - } else { - throw IniError(it->line(), it->position(), "unexpected " + it->toString() + " on root document"); - } - } - } -}; - -} // !namespace - -/* -------------------------------------------------------- - * Ini - * -------------------------------------------------------- */ - -Ini::Ini(const std::string &path) -{ - IniBuilder(*this, path); -}
--- a/C++/Ini.h Sun Mar 08 11:07:36 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,494 +0,0 @@ -/* - * Ini.h -- .ini file parsing - * - * Copyright (c) 2013, 2014 David Demelier <markand@malikania.fr> - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#ifndef _INI_H_ -#define _INI_H_ - -/** - * @file Ini.h - * @brief Configuration file parser - */ - -#include <algorithm> -#include <deque> -#include <stdexcept> -#include <string> - -/** - * @class IniError - * @brief Error in a file - */ -class IniError : public std::exception { -private: - int m_line; - int m_position; - std::string m_error; - -public: - /** - * Construct an error. - * - * @param line the line - * @param position the position - * @param error the error - */ - inline IniError(int line, int position, std::string error) - : m_line(line) - , m_position(position) - , m_error(std::move(error)) - { - } - - /** - * Return the line number. - * - * @return the line - */ - inline int line() const noexcept - { - return m_line; - } - - /** - * Return the position in the current line. - * - * @return the position - */ - inline int position() const noexcept - { - return m_position; - } - - /** - * Get the error string. - * - * @return the string - */ - inline const char *what() const noexcept - { - return m_error.c_str(); - } -}; - -/** - * @class IniOption - * @brief Option definition - */ -class IniOption { -private: - std::string m_key; - std::string m_value; - -public: - /** - * Construct an option. - * - * @param key the key - * @param value the value - */ - inline IniOption(std::string key, std::string value) - : m_key(std::move(key)) - , m_value(std::move(value)) - { - } - - /** - * Get the option key. - * - * @return the key - */ - inline const std::string &key() const noexcept - { - return m_key; - } - - /** - * Get the option value. - * - * @return the value - */ - inline const std::string &value() const noexcept - { - return m_value; - } -}; - -/** - * @class IniSection - * @brief Section that contains one or more options - */ -class IniSection { -private: - std::string m_key; - std::deque<IniOption> m_options; - - template <typename T> - T find(const std::string &key) const - { - auto it = std::find_if(m_options.begin(), m_options.end(), [&] (const IniOption &o) { - return o.key() == key; - }); - - if (it == m_options.end()) - throw std::out_of_range("option " + key + " not found"); - - return const_cast<T>(*it); - } - -public: - /** - * Default constructor has no sections and no values. - */ - IniSection() = default; - - /** - * Construct a section with a set of options. - * - * @param key the section name - * @param options the list of options - */ - inline IniSection(std::string key, std::deque<IniOption> options = {}) noexcept - : m_key(std::move(key)) - , m_options(std::move(options)) - { - } - - /** - * Get the section key. - * - * @return the key - */ - inline const std::string &key() const noexcept - { - return m_key; - } - - /** - * Get an iterator to the beginning. - * - * @return the iterator - */ - inline auto begin() noexcept - { - return m_options.begin(); - } - - /** - * Overloaded function. - * - * @return the iterator - */ - inline auto begin() const noexcept - { - return m_options.begin(); - } - - /** - * Overloaded function. - * - * @return the iterator - */ - inline auto cbegin() const noexcept - { - return m_options.cbegin(); - } - - /** - * Get an iterator to the end. - * - * @return the iterator - */ - inline auto end() noexcept - { - return m_options.end(); - } - - /** - * Overloaded function. - * - * @return the iterator - */ - inline auto end() const noexcept - { - return m_options.end(); - } - - /** - * Overloaded function. - * - * @return the iterator - */ - inline auto cend() const noexcept - { - return m_options.cend(); - } - - /** - * Append an option. - * - * @param option the option to add - */ - inline void push_back(IniOption option) - { - m_options.push_back(std::move(option)); - } - - /** - * Push an option to the beginning. - * - * @param option the option to add - */ - inline void push_front(IniOption option) - { - m_options.push_front(std::move(option)); - } - - /** - * Get the number of options in that section. - * - * @return the size - */ - inline unsigned size() const noexcept - { - return m_options.size(); - } - - /** - * Access an option at the specified index. - * - * @param index the index - * @return the option - * @warning No bounds checking is performed - */ - inline IniOption &operator[](int index) noexcept - { - return m_options[index]; - } - - /** - * Access an option at the specified index. - * - * @param index the index - * @return the option - * @warning No bounds checking is performed - */ - inline const IniOption &operator[](int index) const noexcept - { - return m_options[index]; - } - - /** - * Access an option at the specified key. - * - * @param key the key - * @return the option - * @warning No bounds checking is performed - */ - inline IniOption &operator[](const std::string &key) - { - return find<IniOption &>(key); - } - - /** - * Access an option at the specified key. - * - * @param key the key - * @return the option - * @warning No bounds checking is performed - */ - inline const IniOption &operator[](const std::string &key) const - { - return find<const IniOption &>(key); - } -}; - -/** - * @class Ini - * @brief Ini config file loader - */ -class Ini { -private: - std::deque<IniSection> m_sections; - - template <typename T> - T find(const std::string &key) const - { - auto it = std::find_if(m_sections.begin(), m_sections.end(), [&] (const IniSection &s) { - return s.key() == key; - }); - - if (it == m_sections.end()) - throw std::out_of_range("section " + key + " not found"); - - return const_cast<T>(*it); - } - -public: - /** - * Default constructor with an empty configuration. - */ - Ini() = default; - - /** - * Open the path as the configuration file. - * - * @param path the path - * @throw IniError on any error - */ - Ini(const std::string &path); - - /** - * Get an iterator to the beginning. - * - * @return the iterator - */ - inline auto begin() noexcept - { - return m_sections.begin(); - } - - /** - * Overloaded function. - * - * @return the iterator - */ - inline auto begin() const noexcept - { - return m_sections.begin(); - } - - /** - * Overloaded function. - * - * @return the iterator - */ - inline auto cbegin() const noexcept - { - return m_sections.cbegin(); - } - - /** - * Get an iterator to the end. - * - * @return the iterator - */ - inline auto end() noexcept - { - return m_sections.end(); - } - - /** - * Overloaded function. - * - * @return the iterator - */ - inline auto end() const noexcept - { - return m_sections.end(); - } - - /** - * Overloaded function. - * - * @return the iterator - */ - inline auto cend() const noexcept - { - return m_sections.cend(); - } - - /** - * Get the number of sections in the configuration. - * - * @return the size - */ - inline unsigned size() const noexcept - { - return m_sections.size(); - } - - /** - * Append a section to the end. - * - * @param section the section to add - */ - inline void push_back(IniSection section) - { - m_sections.push_back(std::move(section)); - } - - /** - * Add a section to the beginning. - * - * @param section the section to add - */ - inline void push_front(IniSection section) - { - m_sections.push_front(std::move(section)); - } - - /** - * Access a section at the specified index. - * - * @param index the index - * @return the section - * @warning No bounds checking is performed - */ - inline IniSection &operator[](int index) noexcept - { - return m_sections[index]; - } - - /** - * Access a section at the specified index. - * - * @param index the index - * @return the section - * @warning No bounds checking is performed - */ - inline const IniSection &operator[](int index) const noexcept - { - return m_sections[index]; - } - - /** - * Access a section at the specified key. - * - * @param key the key - * @return the section - * @warning No bounds checking is performed - */ - inline IniSection &operator[](const std::string &key) - { - return find<IniSection &>(key); - } - - /** - * Access a section at the specified key. - * - * @param key the key - * @return the section - * @warning No bounds checking is performed - */ - inline const IniSection &operator[](const std::string &key) const - { - return find<IniSection &>(key); - } -}; - -#endif // !_INI_H_
--- a/C++/Json.cpp Sun Mar 08 11:07:36 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,158 +0,0 @@ -/* - * Json.cpp -- jansson C++11 wrapper - * - * Copyright (c) 2013, 2014 David Demelier <markand@malikania.fr> - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include <stdexcept> - -#include "Json.h" - -/* -------------------------------------------------------- - * JsonObject - * -------------------------------------------------------- */ - -JsonObject JsonValue::toObject() const noexcept -{ - json_incref(m_handle.get()); - - return JsonObject(m_handle.get()); -} - -JsonArray JsonValue::toArray() const noexcept -{ - json_incref(m_handle.get()); - - return JsonArray(m_handle.get()); -} - -/* -------------------------------------------------------- - * JsonArray - * -------------------------------------------------------- */ - -JsonValue JsonArray::at(int index) const -{ - auto value = json_array_get(m_handle.get(), index); - - if (value == nullptr) - throw JsonError("index out of bounds"); - - json_incref(value); - - return JsonValue{value}; -} - -JsonValue JsonArray::operator[](int index) const noexcept -{ - auto value = json_array_get(m_handle.get(), index); - - if (value == nullptr) - return JsonValue(); - - json_incref(value); - - return JsonValue(value); -} - -JsonArray::Ref JsonArray::operator[](int index) noexcept -{ - auto value = json_array_get(m_handle.get(), index); - - if (value == nullptr) - value = json_null(); - else - json_incref(value); - - return Ref(value, *this, index); -} - -/* -------------------------------------------------------- - * JsonObject - * -------------------------------------------------------- */ - -JsonObject::Ref JsonObject::operator[](const std::string &name) -{ - if (typeOf() != JsonType::Object) - return Ref(JsonValue(), *this, name); - - auto value = json_object_get(m_handle.get(), name.c_str()); - - json_incref(value); - - return Ref(value, *this, name); -} - -JsonValue JsonObject::operator[](const std::string &name) const -{ - if (typeOf() != JsonType::Object) - return JsonValue(); - - auto value = json_object_get(m_handle.get(), name.c_str()); - - if (value == nullptr) - return JsonValue(); - - json_incref(value); - - return JsonValue(value); -} - -/* -------------------------------------------------------- - * JsonDocument - * -------------------------------------------------------- */ - -JsonValue JsonDocument::read(std::string content, int flags) const -{ - json_error_t error; - json_t *json = json_loads(content.c_str(), flags, &error); - - if (json == nullptr) - throw JsonError(error); - - return JsonValue(json); -} - -JsonValue JsonDocument::read(std::ifstream &stream, int flags) const -{ - if (!stream.is_open()) - throw JsonError("File not opened"); - - stream.seekg(0, stream.end); - auto length = stream.tellg(); - stream.seekg(0, stream.beg); - - std::string buffer; - buffer.resize(length, ' '); - - stream.read(&buffer[0], length); - stream.close(); - - return read(std::move(buffer), flags); -} - -JsonDocument::JsonDocument(std::ifstream &stream, int flags) -{ - m_value = read(stream, flags); -} - -JsonDocument::JsonDocument(std::ifstream &&stream, int flags) -{ - m_value = read(stream, flags); -} - -JsonDocument::JsonDocument(std::string content, int flags) -{ - m_value = read(std::move(content), flags); -} \ No newline at end of file
--- a/C++/Json.h Sun Mar 08 11:07:36 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1603 +0,0 @@ -/* - * Json.h -- jansson C++11 wrapper - * - * Copyright (c) 2013, 2014 David Demelier <markand@malikania.fr> - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#ifndef _JSON_H_ -#define _JSON_H_ - -#include <algorithm> -#include <cerrno> -#include <cstdlib> -#include <cstring> -#include <initializer_list> -#include <fstream> -#include <iterator> -#include <memory> -#include <string> -#include <utility> - -#include <jansson.h> - -/** - * @file Json.h - * @brief A jansson C++ modern wrapper - * - * Because of the Jansson implementation, all these classes are implicitly - * shared. - * - * This means that you can't set any value to an existing value as it would - * change a value which may be used somewhere else, instead you must set - * or replace elements in JsonObject and JsonArray respectively. - * - * However, copy constructors are implemented as deep copy so take care of - * not copying values mistakenly. - */ - -/** - * @class JsonType - * @brief Json value type - */ -enum class JsonType { - Object = JSON_OBJECT, //!< Object - Array = JSON_ARRAY, //!< Array - String = JSON_STRING, //!< String - Integer = JSON_INTEGER, //!< Integer - Real = JSON_REAL, //!< Floating point - True = JSON_TRUE, //!< Boolean true - False = JSON_FALSE, //!< Boolean false - Null = JSON_NULL //!< Empty or null -}; - -/** - * @class JsonError - * @brief Error thrown for any error - */ -class JsonError final : public std::exception { -private: - std::string m_text; - std::string m_source; - int m_line{}; - int m_column{}; - int m_position{}; - -public: - /** - * Custom error with no line, no column and no position. - * - * @param error the error message - */ - inline JsonError(std::string error) - : m_text(std::move(error)) - { - } - - /** - * Error from a json_error_t. - * - * @param error the error - */ - inline JsonError(const json_error_t &error) - : m_text(error.text) - , m_source(error.source) - , m_line(error.line) - , m_column(error.column) - , m_position(error.position) - { - } - - /** - * Get the error message. - * - * @return the message - */ - const char *what() const noexcept override - { - return m_text.c_str(); - } - - /** - * Get the text message. - * - * @return the text - */ - inline const std::string &text() const noexcept - { - return m_text; - } - - /** - * Get the source. - * - * @return the source - */ - inline const std::string &source() const noexcept - { - return m_source; - } - - /** - * Get the line. - * - * @return the line - */ - inline int line() const noexcept - { - return m_line; - } - - /** - * Get the column. - * - * @return the column - */ - inline int column() const noexcept - { - return m_column; - } - - /** - * Get the position. - * - * @return the position - */ - inline int position() const noexcept - { - return m_position; - } -}; - -class JsonObject; -class JsonArray; - -/** - * @class JsonValue - * @brief Encapsulate any JSON value - */ -class JsonValue { -public: - using Handle = std::unique_ptr<json_t, void (*)(json_t *)>; - - friend class JsonObject; - friend class JsonArray; - -protected: - /** - * The unique_ptr handle of json_t, will automatically decrease - * the reference count in its deleter. - */ - Handle m_handle; - - inline void check() const - { - if (m_handle == nullptr) - throw JsonError(std::strerror(errno)); - } - -public: - /** - * Deep copy of that element. - * - * @param value the other value - * @throw JsonError on allocation error - */ - inline JsonValue(const JsonValue &value) - : m_handle(json_deep_copy(value.m_handle.get()), json_decref) - { - check(); - } - - /** - * Assign a deep copy of the other element. - * - * @return *this - * @throw JsonError on allocation error - */ - inline JsonValue &operator=(const JsonValue &value) - { - m_handle = Handle(json_deep_copy(value.m_handle.get()), json_decref); - - check(); - - return *this; - } - - /** - * Move constructor, the other value is left empty (JsonType::Null). - * - * @param other the other value - */ - inline JsonValue(JsonValue &&other) noexcept - : m_handle(std::move(other.m_handle)) - { - other.m_handle = Handle(json_null(), json_decref); - } - - /** - * Move assignment, the other value is left empty (JsonType::Null). - * - * @param other the other value - */ - inline JsonValue &operator=(JsonValue &&other) noexcept - { - m_handle = std::move(other.m_handle); - other.m_handle = Handle(json_null(), json_decref); - - return *this; - } - - /** - * Create a JsonValue from a native Jansson type. This function - * will increment the json_t reference count. - * - * @param json the value - */ - inline JsonValue(json_t *json) noexcept - : m_handle(json, json_decref) - { - } - - /** - * Construct a null value from a nullptr argument. - */ - inline JsonValue(std::nullptr_t) noexcept - : m_handle(json_null(), json_decref) - { - } - - /** - * Create an empty value (JsonType::Null). - */ - inline JsonValue() noexcept - : m_handle(json_null(), json_decref) - { - } - - /** - * Create a boolean value. - * - * @param value the value - */ - inline JsonValue(bool value) noexcept - : m_handle(json_boolean(value), json_decref) - { - } - - /** - * Create a integer value (JsonType::Integer). - * - * @param value the value - * @throw JsonError on allocation error - */ - inline JsonValue(int value) - : m_handle(json_integer(value), json_decref) - { - check(); - } - - /** - * Create a real value (JsonType::Real). - * - * @param value the value - * @throw JsonError on allocation error - */ - inline JsonValue(double value) - : m_handle(json_real(value), json_decref) - { - check(); - } - - /** - * Create a string value (JsonType::String). - * - * @param value the value - * @throw JsonError on allocation error - */ - inline JsonValue(std::string value) - : m_handle(json_string(value.c_str()), json_decref) - { - check(); - } - - /** - * Create from a C string (JsonType::String). - * - * @param value the string - * @throw JsonError on allocation error - */ - inline JsonValue(const char *value) - : m_handle(json_string(value), json_decref) - { - check(); - } - - /** - * Create from a string literal (JsonType::String). - * - * @param value the value - * @throw JsonError on allocation error - */ - template <size_t Size> - inline JsonValue(char (&value)[Size]) - : m_handle(json_string(value), json_decref) - { - check(); - } - - /** - * Default destructor. - */ - virtual ~JsonValue() = default; - - /** - * Get the type of value. - * - * @return the type - */ - inline JsonType typeOf() const noexcept - { - return static_cast<JsonType>(json_typeof(m_handle.get())); - } - - /** - * Tells if the json value is an JSON_OBJECT. - * - * @return true or false - */ - inline bool isObject() const noexcept - { - return json_is_object(m_handle.get()); - } - - /** - * Tells if the json value is an JSON_ARRAY. - * - * @return true or false - */ - inline bool isArray() const noexcept - { - return json_is_array(m_handle.get()); - } - - /** - * Tells if the json value is an JSON_STRING. - * - * @return true or false - */ - inline bool isString() const noexcept - { - return json_is_string(m_handle.get()); - } - - /** - * Tells if the json value is an JSON_REAL. - * - * @return true or false - */ - inline bool isReal() const noexcept - { - return json_is_real(m_handle.get()); - } - - /** - * Tells if the json value is an JSON_TRUE. - * - * @return true or false - */ - inline bool isTrue() const noexcept - { - return json_is_true(m_handle.get()); - } - - /** - * Tells if the json value is an JSON_FALSE. - * - * @return true or false - */ - inline bool isFalse() const noexcept - { - return json_is_false(m_handle.get()); - } - - /** - * Tells if the json value is an JSON_NULL. - * - * @return true or false - */ - inline bool isNull() const noexcept - { - return json_is_null(m_handle.get()); - } - - /** - * Tells if the json value is an JSON_INTEGER or JSON_REAL. - * - * @return true or false - */ - inline bool isNumber() const noexcept - { - return json_is_number(m_handle.get()); - } - - /** - * Tells if the json value is an JSON_INTEGER. - * - * @return true or false - */ - inline bool isInteger() const noexcept - { - return json_is_integer(m_handle.get()); - } - - /** - * Tells if the json value is an JSON_TRUE or JSON_FALSE. - * - * @return true or false - */ - inline bool isBoolean() const noexcept - { - return json_is_boolean(m_handle.get()); - } - - /** - * Get the string value. - * - * @return the string - */ - inline std::string toString() const noexcept - { - auto value = json_string_value(m_handle.get()); - - return (value == nullptr) ? "" : value; - } - - /** - * Get the integer value. - * - * @return the value or 0 - */ - inline int toInteger() const noexcept - { - return json_integer_value(m_handle.get()); - } - - /** - * Get the real value. - * - * @return the value or 0 - */ - inline double toReal() const noexcept - { - return json_real_value(m_handle.get()); - } - - /** - * Convert to object. - * - * @return an object - */ - JsonObject toObject() const noexcept; - - /** - * Convert to array. - * - * @return an array - */ - JsonArray toArray() const noexcept; - - /** - * Write to a stream. - * - * @param out the out - * @param flags the optional Jansson flags - */ - inline void write(std::ofstream &out, int flags = 0) const - { - auto content = dump(flags); - - std::copy(std::begin(content), std::end(content), std::ostreambuf_iterator<char>(out)); - } - - /** - * Overloaded function. - * - * @param out the out - * @param flags the optional Jansson flags - */ - inline void write(std::ofstream &&out, int flags = 0) const - { - write(out, flags); - } - - /** - * Convert the Json value as a string. - * - * @return the string - * @param flags the optional Jansson flags - */ - inline std::string dump(int flags = 0) const - { - auto str = json_dumps(m_handle.get(), flags); - - if (str == nullptr) - return ""; - - std::string ret(str); - std::free(str); - - return ret; - } - - /** - * Convert to native Jansson type. - * - * You should not call json_incref or json_decref on it as it is - * automatically done. - * - * @return the json_t handle - * @warning use this function with care - */ - inline operator json_t *() noexcept - { - return m_handle.get(); - } - - /** - * Overloaded function. - * - * @return the json_t handle - */ - inline operator const json_t *() const noexcept - { - return m_handle.get(); - } - - /** - * Equality operator. - */ - inline bool operator==(const JsonValue &other) const noexcept - { - return json_equal(m_handle.get(), other.m_handle.get()); - } -}; - -/** - * @class JsonArray - * @brief Manipulate JSON arrays - */ -class JsonArray final : public JsonValue { -public: - /** - * @class Ref - * @brief Reference wrapper to be assignable - */ - class Ref final : public JsonValue { - private: - JsonArray &m_array; - int m_index; - - public: - explicit inline Ref(JsonValue value, JsonArray &array, int index) - : JsonValue(std::move(value)) - , m_array(array) - , m_index(index) - { - } - - inline operator JsonValue() const noexcept - { - return *this; - } - - inline JsonValue &operator*() noexcept - { - return *this; - } - - inline JsonValue *operator->() noexcept - { - return this; - } - - inline Ref &operator=(const JsonValue &value) - { - m_array.replace(value, m_index); - - return *this; - } - - inline Ref &operator=(JsonValue &&value) - { - m_array.replace(std::move(value), m_index); - - return *this; - } - }; - - /** - * @class Ptr - * @brief Pointer wrapper for JsonValue iterators - */ - class Ptr final : public JsonValue { - public: - explicit Ptr(JsonValue value) noexcept - : JsonValue(std::move(value)) - { - } - - inline JsonValue &operator*() noexcept - { - return *this; - } - - inline JsonValue *operator->() noexcept - { - return this; - } - }; - - class iterator final { - public: - using iterator_category = std::random_access_iterator_tag; - using difference_type = int; - using value_type = JsonValue; - using reference = Ref; - using pointer = Ptr; - - friend class JsonArray; - - private: - JsonArray &m_array; - int m_index; - - public: - explicit inline iterator(JsonArray &array, int index = 0) noexcept - : m_array(array) - , m_index(index) - { - } - - inline Ref operator*() const - { - return Ref(m_array.at(m_index), m_array, m_index); - } - - inline Ptr operator->() const - { - return Ptr(m_array.at(m_index)); - } - - inline Ref operator[](int nindex) const noexcept - { - return Ref(m_array.at(m_index + nindex), m_array, m_index + nindex); - } - - inline bool operator==(const iterator &other) const noexcept - { - return m_index == other.m_index; - } - - inline bool operator!=(const iterator &other) const noexcept - { - return m_index != other.m_index; - } - - inline bool operator<(const iterator &other) const noexcept - { - return m_index < other.m_index; - } - - inline bool operator<=(const iterator &other) const noexcept - { - return m_index <= other.m_index; - } - - inline bool operator>(const iterator &other) const noexcept - { - return m_index > other.m_index; - } - - inline bool operator>=(const iterator &other) const noexcept - { - return m_index >= other.m_index; - } - - inline iterator &operator++() noexcept - { - ++m_index; - - return *this; - } - - inline iterator operator++(int) noexcept - { - iterator save = *this; - - ++m_index; - - return save; - } - - inline iterator &operator--() noexcept - { - m_index--; - - return *this; - } - - inline iterator operator--(int) noexcept - { - iterator save = *this; - - m_index--; - - return save; - } - - inline iterator &operator+=(int nindex) noexcept - { - m_index += nindex; - - return *this; - } - - inline iterator &operator-=(int nindex) noexcept - { - m_index -= nindex; - - return *this; - } - - inline iterator operator+(int nindex) const noexcept - { - return iterator(m_array, m_index + nindex); - } - - inline iterator operator-(int nindex) const noexcept - { - return iterator(m_array, m_index - nindex); - } - - inline int operator-(iterator other) const noexcept - { - return m_index - other.m_index; - } - }; - - class const_iterator final { - public: - using iterator_category = std::random_access_iterator_tag; - using difference_type = int; - using value_type = JsonValue; - using reference = JsonValue; - using pointer = Ptr; - - friend class JsonArray; - - private: - const JsonArray &m_array; - int m_index; - - public: - explicit inline const_iterator(const JsonArray &array, int index = 0) noexcept - : m_array(array) - , m_index(index) - { - } - - inline JsonValue operator*() const - { - return m_array.at(m_index); - } - - inline Ptr operator->() const - { - return Ptr(m_array.at(m_index)); - } - - inline JsonValue operator[](int nindex) const noexcept - { - return m_array.at(m_index + nindex); - } - - inline bool operator==(const const_iterator &other) const noexcept - { - return m_index == other.m_index; - } - - inline bool operator!=(const const_iterator &other) const noexcept - { - return m_index != other.m_index; - } - - inline bool operator<(const const_iterator &other) const noexcept - { - return m_index < other.m_index; - } - - inline bool operator<=(const const_iterator &other) const noexcept - { - return m_index <= other.m_index; - } - - inline bool operator>(const const_iterator &other) const noexcept - { - return m_index > other.m_index; - } - - inline bool operator>=(const const_iterator &other) const noexcept - { - return m_index >= other.m_index; - } - - inline const_iterator &operator++() noexcept - { - ++m_index; - - return *this; - } - - inline const_iterator operator++(int) noexcept - { - const_iterator save = *this; - - ++m_index; - - return save; - } - - inline const_iterator &operator--() noexcept - { - m_index--; - - return *this; - } - - inline const_iterator operator--(int) noexcept - { - const_iterator save = *this; - - m_index--; - - return save; - } - - inline const_iterator &operator+=(int nindex) noexcept - { - m_index += nindex; - - return *this; - } - - inline const_iterator &operator-=(int nindex) noexcept - { - m_index -= nindex; - - return *this; - } - - inline const_iterator operator+(int nindex) const noexcept - { - return const_iterator(m_array, m_index + nindex); - } - - inline const_iterator operator-(int nindex) const noexcept - { - return const_iterator(m_array, m_index - nindex); - } - - inline int operator-(const_iterator other) const noexcept - { - return m_index - other.m_index; - } - }; - - using size_type = int; - using value_type = JsonValue; - using const_pointer = const value_type *; - using reference = JsonValue &; - using const_reference = const JsonValue &; - using difference_type = int; - -protected: - using JsonValue::JsonValue; - -public: - /** - * Create an empty array. - * - * @throw JsonError on allocation error - */ - inline JsonArray() - : JsonValue(json_array()) - { - check(); - } - - /** - * Create an array from a list of values. - * - * @param list the list - * @throw JsonError on allocation error - */ - inline JsonArray(std::initializer_list<value_type> list) - : JsonArray() - { - for (auto &v : list) - append(std::move(v)); - } - - /** - * Returns an iterator to the beginning. - * - * @return the iterator - */ - inline iterator begin() noexcept - { - return iterator(*this, 0); - } - - /** - * Overloaded function. - * - * @return the iterator - */ - inline const_iterator begin() const noexcept - { - return const_iterator(*this, 0); - } - - /** - * Overloaded function. - * - * @return the iterator - */ - inline const_iterator cbegin() const noexcept - { - return const_iterator(*this, 0); - } - - /** - * Returns an iterator to the end. - * - * @return the iterator - */ - inline iterator end() noexcept - { - return iterator(*this, size()); - } - - /** - * Overloaded function. - * - * @return the iterator - */ - inline const_iterator end() const noexcept - { - return const_iterator(*this, size()); - } - - /** - * Overloaded function. - * - * @return the iterator - */ - inline const_iterator cend() const noexcept - { - return const_iterator(*this, size()); - } - - /** - * Get a value. - * - * @param index the index - * @throw JsonError on error - */ - JsonValue at(int index) const; - - /** - * Erase the array content. - */ - inline void clear() noexcept - { - json_array_clear(m_handle.get()); - } - - /** - * Remove the element at the specified index. - * - * @param index the index - */ - inline void erase(int index) noexcept - { - json_array_remove(m_handle.get(), index); - } - - /** - * Overloaded function. - * - * @param it the iterator - */ - inline void erase(iterator it) noexcept - { - erase(it.m_index); - } - - /** - * Overloaded function. - * - * @param it the iterator - */ - inline void erase(const_iterator it) noexcept - { - erase(it.m_index); - } - - /** - * Get the number of values in the array - * - * @return the number or 0 - */ - inline unsigned size() const noexcept - { - return json_array_size(m_handle.get()); - } - - /** - * Insert the value at the beginning. - * - * @param value the value - */ - inline void push(const JsonValue &value) noexcept - { - json_array_insert(m_handle.get(), 0, value.m_handle.get()); - } - - /** - * Insert a copy of the value at the end. - * - * @param value the value to insert - */ - inline void append(const JsonValue &value) - { - json_array_append(m_handle.get(), value.m_handle.get()); - } - - /** - * Insert a copy of the value at the specified index. - * - * @param value the value to insert - * @param index the position - */ - inline void insert(const JsonValue &value, int index) - { - json_array_insert(m_handle.get(), index, value.m_handle.get()); - } - - /** - * Replace the value at the given index. - * - * @param value the value - * @param index the index - */ - inline void replace(const JsonValue &value, int index) - { - json_array_set(m_handle.get(), index, value.m_handle.get()); - } - - /** - * Get the value at the specified index. - * - * @param index the position - * @return the value - */ - JsonValue operator[](int index) const noexcept; - - /** - * Access a value as a reference wrapper. - * - * @param index the position - * @return the reference wrapper over the value - */ - Ref operator[](int index) noexcept; -}; - -/** - * @class JsonObject - * @brief Object wrapper - */ -class JsonObject final : public JsonValue { -public: - using key_type = std::string; - using mapped_type = JsonValue; - using size_type = int; - using value_type = std::pair<key_type, mapped_type>; - using const_pointer = const value_type *; - using reference = JsonValue &; - using const_reference = const JsonValue &; - using difference_type = int; - - /** - * @class Ref - * @brief Wrapper for updating JsonObject - * - * This class is only used for the following functions: - * - * JsonObject::operator[] - */ - class Ref final : public JsonValue { - private: - JsonObject &m_object; - std::string m_key; - - public: - explicit inline Ref(JsonValue value, JsonObject &object, std::string key) - : JsonValue(std::move(value)) - , m_object(object) - , m_key(std::move(key)) - { - } - - inline operator JsonValue() const noexcept - { - return *this; - } - - inline JsonValue &operator*() noexcept - { - return *this; - } - - inline JsonValue *operator->() noexcept - { - return this; - } - - inline Ref &operator=(const JsonValue &value) - { - m_object.set(m_key, value); - - return *this; - } - - inline Ref &operator=(JsonValue &&value) - { - m_object.set(m_key, std::move(value)); - - return *this; - } - }; - - /** - * @class Ptr - * @brief Pointer wrapper for JsonValue iterators - * - * For const iterators, the real type is a JsonValue, for non const - * iterators it's a ref so that user can edit it. - */ - template <typename Type> - class Ptr final { - private: - std::pair<std::string, Type> m_pair; - - public: - inline Ptr(std::pair<std::string, Type> value) noexcept - : m_pair(std::move(value)) - { - } - - inline std::pair<std::string, Type> &operator*() noexcept - { - return m_pair; - } - - inline std::pair<std::string, Type> *operator->() noexcept - { - return &m_pair; - } - }; - - /** - * @class iterator - * @brief Forward iterator - */ - class iterator { - public: - using value_type = std::pair<std::string, Ref>; - - friend class JsonObject; - - private: - JsonObject &m_object; - void *m_keyIt; - - inline std::string key() const noexcept - { - return json_object_iter_key(m_keyIt); - } - - public: - explicit inline iterator(JsonObject &object, void *keyIt) noexcept - : m_object(object) - , m_keyIt(keyIt) - { - } - - inline value_type operator*() const - { - auto k = key(); - - return value_type(k, Ref(m_object[k], m_object, k)); - } - - inline Ptr<Ref> operator->() const - { - auto k = key(); - - return Ptr<Ref>({k, Ref(m_object[k], m_object, k)}); - } - - inline iterator &operator++() noexcept - { - m_keyIt = json_object_iter_next(m_object.m_handle.get(), m_keyIt); - - return *this; - } - - inline iterator operator++(int) noexcept - { - iterator save = *this; - - m_keyIt = json_object_iter_next(m_object.m_handle.get(), m_keyIt); - - return save; - } - - inline bool operator==(iterator other) const noexcept - { - return m_keyIt == other.m_keyIt; - } - - inline bool operator!=(iterator other) const noexcept - { - return m_keyIt != other.m_keyIt; - } - }; - - /** - * @class const_iterator - * @brief Forward iterator - */ - class const_iterator { - public: - using value_type = std::pair<std::string, JsonValue>; - - friend class JsonObject; - - private: - const JsonObject &m_object; - void *m_keyIt; - - inline std::string key() const noexcept - { - return json_object_iter_key(m_keyIt); - } - - public: - explicit inline const_iterator(const JsonObject &object, void *keyIt) noexcept - : m_object(object) - , m_keyIt(keyIt) - { - } - - inline value_type operator*() const - { - auto k = key(); - - return value_type(k, m_object[k]); - } - - inline Ptr<JsonValue> operator->() const - { - auto k = key(); - - return Ptr<JsonValue>({k, m_object[k]}); - } - - inline const_iterator &operator++() noexcept - { - m_keyIt = json_object_iter_next(m_object.m_handle.get(), m_keyIt); - - return *this; - } - - inline const_iterator operator++(int) noexcept - { - const_iterator save = *this; - - m_keyIt = json_object_iter_next(m_object.m_handle.get(), m_keyIt); - - return save; - } - - inline bool operator==(const_iterator other) const noexcept - { - return m_keyIt == other.m_keyIt; - } - - inline bool operator!=(const_iterator other) const noexcept - { - return m_keyIt != other.m_keyIt; - } - }; - -protected: - using JsonValue::JsonValue; - -public: - /** - * Create empty object. - * - * @throw JsonError on allocation error - */ - inline JsonObject() - : JsonValue(json_object()) - { - check(); - } - - /** - * Create a JsonObject from an initializer_list. - * - * @param list the list of key-value pairs - * @throw JsonError on allocation error - */ - inline JsonObject(std::initializer_list<value_type> list) - : JsonObject() - { - for (auto &v : list) - set(v.first, std::move(v.second)); - } - - /** - * Returns an iterator to the beginning. - * - * @return the iterator - */ - inline iterator begin() noexcept - { - return iterator(*this, json_object_iter(m_handle.get())); - } - - /** - * Overloaded function. - * - * @return the iterator - */ - inline const_iterator begin() const noexcept - { - return const_iterator(*this, json_object_iter(m_handle.get())); - } - - /** - * Overloaded function. - * - * @return the iterator - */ - inline const_iterator cbegin() const noexcept - { - return const_iterator(*this, json_object_iter(m_handle.get())); - } - - /** - * Returns an iterator to the end. - * - * @return the iterator - */ - inline iterator end() noexcept - { - return iterator(*this, nullptr); - } - - /** - * Overloaded function. - * - * @return the iterator - */ - inline const_iterator end() const noexcept - { - return const_iterator(*this, nullptr); - } - - /** - * Overloaded function. - * - * @return the iterator - */ - inline const_iterator cend() const noexcept - { - return const_iterator(*this, nullptr); - } - - /** - * Check if the object contains a specific property. - * - * @param key the key - * @return true if the object contains the key - */ - inline bool contains(const std::string &key) const noexcept - { - return json_object_get(m_handle.get(), key.c_str()) != nullptr; - } - - /** - * Get the number of items in the object. - * - * @return the number of items - */ - inline unsigned size() const noexcept - { - return json_object_size(m_handle.get()); - } - - /** - * Remove all elements from the object. - */ - inline void clear() noexcept - { - json_object_clear(m_handle.get()); - } - - /** - * Remove element `key' if exists. - * - * @param key the key - */ - inline void erase(const std::string &key) noexcept - { - json_object_del(m_handle.get(), key.c_str()); - } - - /** - * Overloaded function. - * - * @param it the iterator - */ - inline void erase(iterator it) noexcept - { - erase(it.key()); - } - - /** - * Overloaded function. - * - * @param it the iterator - */ - inline void erase(const_iterator it) noexcept - { - erase(it.key()); - } - - /** - * Set the value as key in the object. - * - * @param key the key - * @param value the value - */ - inline void set(const std::string &key, const JsonValue &value) noexcept - { - json_object_set(m_handle.get(), key.c_str(), value.m_handle.get()); - } - - /** - * Access an object as a wrapper so that you can update its content - * with convenience. - * - * @param key the key - */ - Ref operator[](const std::string &key); - - /** - * Get the value at the specified key. If the value is not found, an - * empty value is returned - * - * @param key the key - * @return the value - */ - JsonValue operator[](const std::string &key) const; -}; - -/** - * @class JsonDocument - * @brief Read files and strings to create Json values - */ -class JsonDocument final { -private: - JsonValue m_value; - - JsonValue read(std::string, int flags) const; - JsonValue read(std::ifstream &stream, int flags) const; - -public: - /** - * Construct a document from a file. - * - * @param stream the stream - * @param flags the optional Jansson flags - * @throw JsonError on errors - */ - JsonDocument(std::ifstream &fstream, int flags = 0); - - /** - * Construct a document from a file. - * - * @param stream the stream - * @param flags the optional Jansson flags - * @throw JsonError on errors - */ - JsonDocument(std::ifstream &&stream, int flags = 0); - - /** - * Construct a document from a file. - * - * @param stream the stream - * @param flags the optional Jansson flags - * @throw JsonError on errors - */ - JsonDocument(std::string content, int flags = 0); - - /** - * Check if the document contains an object. - * - * @return true if object - */ - inline bool isObject() const noexcept - { - return m_value.isObject(); - } - - /** - * Check if the document contains an array. - * - * @return true if array - */ - inline bool isArray() const noexcept - { - return m_value.isArray(); - } - - /** - * Convert the document value to object - * - * @return the value as object - */ - inline JsonObject toObject() const noexcept - { - return m_value.toObject(); - } - - /** - * Convert the document value to array - * - * @return the value as array - */ - inline JsonArray toArray() const noexcept - { - return m_value.toArray(); - } -}; - -#endif // !_JSON_H_
--- a/C++/OptionParser.cpp Sun Mar 08 11:07:36 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,211 +0,0 @@ -/* - * OptionParser.cpp -- command line option parser - * - * Copyright (c) 2013, 2014 David Demelier <markand@malikania.fr> - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include <algorithm> - -#include "OptionParser.h" - -bool OptionParser::isShort(const std::string &arg) const -{ - return arg.size() >= 2 && arg[0] == '-' && arg[1] != '-'; -} - -bool OptionParser::isLong(const std::string &arg) const -{ - return arg.size() >= 3 && arg[0] == '-' && arg[1] == '-' && arg[2] != '-'; -} - -bool OptionParser::isOption(const std::string &arg) const -{ - return isShort(arg) || isLong(arg); -} - -std::string OptionParser::key(const std::string &arg) const -{ - if (isShort(arg)) - return arg.substr(1, 1); - - return arg.substr(2); -} - -bool OptionParser::isShortCompacted(const std::string &arg) const -{ - return arg.size() >= 3; -} - -bool OptionParser::isDefined(const std::string &arg) const -{ - auto n = key(arg); - auto it = std::find_if(m_options.begin(), m_options.end(), [&] (const Option &o) -> bool { - return o.key() == n || o.full() == n; - }); - - return it != m_options.end(); -} - -const Option &OptionParser::get(const std::string &arg) const -{ - std::string n = key(arg); - - return *std::find_if(m_options.begin(), m_options.end(), [&] (const Option &o) -> bool { - return o.key() == n || o.full() == n; - }); -} - -bool OptionParser::isToggle(const std::string &arg) const -{ - return (get(arg).flags() & Option::NoArg); -} - -void OptionParser::readShort(OptionPack &pack, Args::const_iterator &it, Args::const_iterator end) const -{ - /* - * There are many options when passing a short option: - * - * 1. -cmyconfig Takes on argument but parsed as unique, - * 2. -c myconfig Takes on argument but parsed as two strings - * 3. -abc If a is not a toggle option, its argument is `bc' - * 4. -abc If a is a toggle option and b, c are toggle, they are added - */ - - std::string v = it->substr(2); - std::string k = key(*it); - const Option &option = get(std::string("-") + k); - - if (isToggle(*it)) { - // 3. and optionally 4. - pack.push_back(OptionValue(option, "")); - pack.m_argsParsed += 1; - - if (isShortCompacted(*it)) { - for (char c : v) { - if (!isDefined("-" + std::string(1, c))) { - pack.m_error = "-" + std::string(1, c) + " is not a valid option"; - break; - } - - const Option &sub = get("-" + std::string(1, c)); - - pack.push_back(OptionValue(sub, "")); - } - } - - ++ it; - } else { - // 1. - if (isShortCompacted(*it++)) { - pack.push_back(OptionValue(option, v)); - pack.m_argsParsed += 1; - } else { - // 2. - if (it == end) { - pack.m_error = option.key() + " requires an option"; - } else { - pack.push_back(OptionValue(option, *it++)); - pack.m_argsParsed += 2; - } - } - } -} - -void OptionParser::readFull(OptionPack &pack, Args::const_iterator &it, Args::const_iterator end) const -{ - /* - * Long options can't be compacted, there are only two possibilities: - * - * 1. --fullscreen No argument - * 2. --config foo One argument - */ - const Option &option = get(*it); - - if (!isToggle(*it)) { - // 2. - if (++it == end) { - pack.m_error = "--" + option.full() + " requires an option"; - } else { - pack.push_back(OptionValue(option, *it++)); - pack.m_argsParsed += 2; - } - } else { - pack.push_back(OptionValue(option, "")); - pack.m_argsParsed ++; - - ++ it; - } -} - -OptionParser::OptionParser(std::initializer_list<Option> options) - : m_options(options.begin(), options.end()) -{ -} - -OptionParser::OptionParser(std::vector<Option> options) - : m_options(std::move(options)) -{ -} - -OptionPack OptionParser::parse(Args::const_iterator it, Args::const_iterator end, int flags) const -{ - OptionPack pack; - - while (it != end) { - if (!isOption(*it)) { - if (flags & Unstrict) { - pack.m_argsParsed ++; - it ++; - continue; - } else { - pack.m_error = *it + " is not an option"; - return pack; - } - } - - if (!isDefined(*it)) { - pack.m_error = "Invalid option"; - return pack; - } - - if (isShort(*it)) { - readShort(pack, it, end); - } else { - readFull(pack, it, end); - } - - // Read failure - if (pack.m_error.size() > 0) { - return pack; - } - } - - return pack; -} - -OptionPack OptionParser::parse(int argc, char **argv, int flags) const -{ - std::vector<std::string> args; - - for (int i = 0; i < argc; ++i) - args.push_back(argv[i]); - - return parse(args, flags); -} - -OptionPack OptionParser::parse(const std::vector<std::string> &args, int flags) const -{ - return parse(args.begin(), args.end(), flags); -}
--- a/C++/OptionParser.h Sun Mar 08 11:07:36 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,275 +0,0 @@ -/* - * OptionParser.h -- command line option parser - * - * Copyright (c) 2013, 2014 David Demelier <markand@malikania.fr> - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#ifndef _OPTION_PARSER_H_ -#define _OPTION_PARSER_H_ - -/** - * @file OptionParser.h - * @brief Command line option parser - */ - -#include <initializer_list> -#include <string> -#include <vector> - -/** - * @class Option - * @brief Option definition - */ -class Option { -public: - enum Flags { - NoArg = (1 << 0), - }; - -private: - std::string m_key; - std::string m_full; - int m_flags; - -public: - /** - * Construct an option. By default, an option requires an argument - * unless flags is set to NoArg. - * - * You <strong>must</strong> not prepend dashes to the option names. - * - * You don't need to set both short and long names, but you need at - * least one. - * - * @param key the short name (e.g v) - * @param full the long name (e.g verbose) - * @param flags the optional flags - * @see Flags - */ - inline Option(std::string key, std::string full, int flags = 0) - : m_key(std::move(key)) - , m_full(std::move(full)) - , m_flags(flags) - { - } - - /** - * Get the short name (e.g v) - * - * @return the short name - */ - inline const std::string &key() const noexcept - { - return m_key; - } - - /** - * Get the long name (e.g verbose) - * - * @return the long name - */ - inline const std::string &full() const noexcept - { - return m_full; - } - - /** - * Get the flags. - * - * @return the flags - * @see Flags - */ - inline int flags() const noexcept - { - return m_flags; - } -}; - -/** - * @class OptionValue - * @brief Result of an option parse - */ -class OptionValue { -private: - std::string m_key; - std::string m_full; - std::string m_value; - -public: - /** - * Construct an option value - * - * @param option the option - * @param value the value - */ - inline OptionValue(const Option &option, std::string value) - : m_key(option.key()) - , m_full(option.full()) - , m_value(std::move(value)) - { - } - - /** - * Get the value (if the option requires an argument). - * - * @return the value - */ - inline const std::string &value() const noexcept - { - return m_value; - } - - friend bool operator==(const OptionValue &o1, const std::string &name); -}; - -/** - * Test the option value with the specified option name. - * - * You can use both the short option or the long option name depending - * on what you have registered to the OptionParser class. - * - * @param o the option - * @param name the short or the full name - * @return true if matches - */ -inline bool operator==(const OptionValue &o, const std::string &name) -{ - return o.m_key == name || o.m_full == name; -} - -/** - * @class OptionPack - * @brief Object containing results of a parse - * - * Because parsing bad options does not throw exceptions, this class is - * convertible to bool and has the error contained. - * - * It also have the number of arguments parsed so you can cut your options - * depending on the full command line. - * - * Example: - * -y install -d foo - * -y remove -f - * - * In that case, you can do two parsing, it will stops (unless Unstrict is set) - * until install or remove. - */ -class OptionPack : public std::vector<OptionValue> { -private: - friend class OptionParser; - - std::string m_error{"No error"}; - int m_argsParsed{0}; - -public: - /** - * Get the error. - * - * @return the error - */ - inline const std::string &error() const noexcept - { - return m_error; - } - - /** - * Get the number of arguments parsed <strong>not the number of - * options</strong>. - * - * @return the number of arguments parsed - */ - inline int parsed() const noexcept - { - return m_argsParsed; - } - - /** - * Convert to true on success. - * - * @return true on success - */ - inline operator bool() const noexcept - { - return m_error == "No error"; - } -}; - -/** - * @class OptionParser - * @brief Base class for parsing command line options - * - * The option parser is a replacement for getopt(3) which is reentrant - * and does not use globals. - */ -class OptionParser { -public: - using Map = std::vector<Option>; - using Args = std::vector<std::string>; - - enum Flags { - Unstrict = (1 << 0) - }; - -private: - Map m_options; - - const Option &get(const std::string &arg) const; - std::string key(const std::string &arg) const; - bool isDefined(const std::string &arg) const; - bool isToggle(const std::string &arg) const; - bool isShortCompacted(const std::string &arg) const; - bool isShort(const std::string &arg) const; - bool isLong(const std::string &arg) const; - bool isOption(const std::string &arg) const; - void readShort(OptionPack &pack, Args::const_iterator &it, Args::const_iterator end) const; - void readFull(OptionPack &pack, Args::const_iterator &it, Args::const_iterator end) const; - OptionPack parse(Args::const_iterator it, Args::const_iterator end, int flags) const; - -public: - /** - * Construct an option parser from an initializer_list of options. - * - * @param options the list of options - */ - OptionParser(std::initializer_list<Option> options); - - /** - * Construct an option parser from a vector of options. - * - * @param options the options - */ - OptionParser(std::vector<Option> options); - - /** - * Parse the arguments from main arguments. - * - * @param argc the number of arguments - * @param argv the arguments - * @param flags the optional flags - * @return the packed result - */ - OptionPack parse(int argc, char **argv, int flags = 0) const; - - /** - * Parse the arguments from a vector. - * - * @param args the arguments - * @param flags the optional flags - * @return the packed result - */ - OptionPack parse(const std::vector<std::string> &args, int flags = 0) const; -}; - -#endif // !_OPTION_PARSER_H_
--- a/C++/Pack.cpp Sun Mar 08 11:07:36 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,115 +0,0 @@ -/* - * Pack.cpp -- binary data serialization - * - * Copyright (c) 2013, 2014 David Demelier <markand@malikania.fr> - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include "Pack.h" - -namespace { - -Pack::Endian checkMode() -{ - int i = 1; - unsigned char *ptr = reinterpret_cast<unsigned char *>(&i); - - return (ptr[0] == 1) ? Pack::Little : Pack::Big; -} - -} // !namespace - -const Pack::Endian Pack::mode = checkMode(); - -/* -------------------------------------------------------- - * PackReader - * -------------------------------------------------------- */ - -PackReader::PackReader(Pack::Endian endian) - : m_endian(endian) -{ -} - -/* -------------------------------------------------------- - * PackFileReader - * -------------------------------------------------------- */ - -PackFileReader::PackFileReader(const std::string &path, Pack::Endian endian) - : PackReader(endian) -{ - m_in.open(path, std::ifstream::in); -} - -std::istream &PackFileReader::stream() -{ - return m_in; -} - -/* -------------------------------------------------------- - * PackStringReader - * -------------------------------------------------------- */ - -PackStringReader::PackStringReader(std::string input, Pack::Endian endian) - : PackReader(endian) - , m_in(std::move(input)) -{ -} - -std::istream &PackStringReader::stream() -{ - return m_in; -} - -/* -------------------------------------------------------- - * PackWriter - * -------------------------------------------------------- */ - -PackWriter::PackWriter(Pack::Endian endian) - : m_endian(endian) -{ -} - -/* -------------------------------------------------------- - * PackFileWriter - * -------------------------------------------------------- */ - -PackFileWriter::PackFileWriter(const std::string &path, Pack::Endian endian) - : PackWriter(endian) -{ - m_out.open(path, std::ofstream::out); -} - -std::ostream &PackFileWriter::stream() -{ - return m_out; -} - -/* -------------------------------------------------------- - * PackStringWriter - * -------------------------------------------------------- */ - -PackStringWriter::PackStringWriter(Pack::Endian endian) - : PackWriter(endian) -{ -} - -std::ostream &PackStringWriter::stream() -{ - return m_out; -} - -std::string PackStringWriter::buffer() const -{ - return m_out.str(); -}
--- a/C++/Pack.h Sun Mar 08 11:07:36 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,408 +0,0 @@ -/* - * Pack.h -- binary data serialization - * - * Copyright (c) 2013, 2014 David Demelier <markand@malikania.fr> - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#ifndef _PACK_H_ -#define _PACK_H_ - -#include <cstdint> -#include <fstream> -#include <memory> -#include <sstream> -#include <string> - -/** - * @class Pack - * @brief Serialize binary data to files - * - * This class write and read binary data from files. It currently - * support: - * uint8_t, - * uint16_t, - * uint32_t, - * uint64_t - */ -class Pack { -private: - template <typename T> - struct IsContainer { - using Yes = char [1]; - using No = char [2]; - - template <typename U> - static constexpr Yes &test(typename U::value_type *); - - template <typename U> - static constexpr No &test(...); - - static constexpr const bool value = sizeof (test<T>(0)) == sizeof (Yes); - }; - - friend class PackWriter; - friend class PackReader; - -public: - /** - * @enum Endian - * @brief Endian mode - */ - enum Endian { - Little, //! Little endian - Big //! Big endian - }; - -public: - /** - * Host system endian mode. - */ - static const Endian mode; - - /** - * @struct TypeInfo - * @brief Type information - * - * Used for conversions. - */ - template <typename T> - struct TypeInfo { - static constexpr const bool convertible{false}; - static constexpr const bool serializable{false}; - }; - - /** - * Helper to mark a specialization convertible. - * - * Already done for: - * uint8_t - * uint16_t - * uint32_t - * uint64_t - * - * The specialization must have the following function: - * - * static void convert(T &value) noexcept - */ - struct Convertible { - static constexpr const bool convertible{true}; - }; - - /** - * Helper to mark a specialization serializable. - * - * The specialisation must have the following functions: - * - * static void serialize(PackWriter &writer, const T &) - * static void unserialize(PackReader &reader, T &) - */ - struct Serializable { - static constexpr const bool serializable{true}; - }; - - /** - * Convert data inplace. - * - * @param value the value - */ - template <typename T> - static inline void convert(T &value) noexcept - { - static_assert(TypeInfo<T>::convertible, "unsupported type"); - - TypeInfo<T>::convert(value); - } -}; - -/** - * @class PackReader - * @brief Base abstract reader class - */ -class PackReader { -protected: - Pack::Endian m_endian; - - PackReader(Pack::Endian endian); - - virtual std::istream &stream() = 0; - -public: - /** - * Default destructor. - */ - virtual ~PackReader() = default; - - /** - * Read a primitive convertible type. - * - * @param value the value destination - * @return *this - */ - template <typename T, typename std::enable_if<Pack::TypeInfo<T>::convertible>::type * = nullptr> - PackReader &operator>>(T &value) - { - stream().read(reinterpret_cast<char *>(&value), sizeof (T)); - - if (m_endian != Pack::mode) - Pack::convert(value); - - return *this; - } - - /** - * Read a serializable type. - * - * @param value the value destination - * @return *this - */ - template <typename T, typename std::enable_if<Pack::TypeInfo<T>::serializable>::type * = nullptr> - PackReader &operator>>(T &value) - { - Pack::TypeInfo<T>::unserialize(*this, value); - - return *this; - } - - /** - * Read an array. - * - * This operator is a little bit tricky because you don't know in - * advance how much data you want to read. Because of that, this - * function looks the capacity of the container and reads that number - * of data. - * - * Because it looks for capacity, you can't use a container which - * already have some data, they will be overriden. - * - * If this is a concern, you should roll your own loop to fill up - * your container. - * - * @param container the container (all previous data will be lost) - * @return *this - */ - template <typename T, typename std::enable_if<Pack::IsContainer<T>::value>::type * = nullptr> - PackReader &operator>>(T &container) - { - typename T::value_type v; - - T copy; - - for (size_t i = 0; i < container.capacity(); ++i) { - (*this) >> v; - copy.push_back(v); - } - - container = std::move(copy); - - return *this; - } -}; - -/** - * @class PackWriter - * @brief Base abstract writer class - */ -class PackWriter { -protected: - Pack::Endian m_endian; - - PackWriter(Pack::Endian endian); - - virtual std::ostream &stream() = 0; - -public: - /** - * Default destructor. - */ - virtual ~PackWriter() = default; - - /** - * Write a convertible type to the stream. - * - * @param value the value - * @return *this - */ - template <typename T, typename std::enable_if<Pack::TypeInfo<T>::convertible>::type * = nullptr> - PackWriter &operator<<(T value) - { - if (m_endian != Pack::mode) - Pack::convert(value); - - stream().write(reinterpret_cast<const char *>(&value), sizeof (T)); - - return *this; - } - - /** - * Write a serializable type to the stream. - * - * @param value the value - * @return *this - */ - template <typename T, typename std::enable_if<Pack::TypeInfo<T>::serializable>::type * = nullptr> - PackWriter &operator<<(const T &value) - { - Pack::TypeInfo<T>::serialize(*this, value); - - return *this; - } - - /** - * Write a container to the stream. - * - * @param container the container - * @return *this - */ - template <typename T, typename std::enable_if<Pack::IsContainer<T>::value>::type * = nullptr> - PackWriter &operator<<(const T &container) - { - for (const auto &v : container) - (*this) << v; - - return *this; - } -}; - -/** - * @class PackFileReader - * @brief Extract binary data from a file - */ -class PackFileReader : public PackReader { -private: - std::ifstream m_in; - -protected: - std::istream &stream() override; - -public: - /** - * Read a file. - * - * @param path the path - * @param endian the endian requested - */ - PackFileReader(const std::string &path, Pack::Endian endian); -}; - -/** - * @class PackStringReader - * @brief Extract binary data from a string - */ -class PackStringReader : public PackReader { -private: - std::istringstream m_in; - - std::istream &stream() override; - -public: - /** - * Read a string. - * - * @param input the input string - * @param endian the endian requested - */ - PackStringReader(std::string input, Pack::Endian endian); -}; - -/** - * @class PackFileWriter - * @brief Write binary data to a string - */ -class PackFileWriter : public PackWriter { -private: - std::ofstream m_out; - -protected: - std::ostream &stream() override; - -public: - /** - * Write to a file. - * - * @param path the path - * @param endian the endian requested - */ - PackFileWriter(const std::string &path, Pack::Endian endian); -}; - -/** - * @class PackStringWriter - * @brief Write binary data to a string - */ -class PackStringWriter : public PackWriter { -private: - std::ostringstream m_out; - - std::ostream &stream() override; - -public: - /** - * Write to a string - * - * @param endian the endian requested - */ - PackStringWriter(Pack::Endian endian); - - /** - * The current buffer. Returns a copy of the string. - * - * @return the string - */ - std::string buffer() const; -}; - -template <> -struct Pack::TypeInfo<uint8_t> : public Pack::Convertible { - static inline void convert(uint8_t &) noexcept - { - // uint8_t are endian independent - } -}; - -template <> -struct Pack::TypeInfo<uint16_t> : public Pack::Convertible { - static inline void convert(uint16_t &v) - { - v = (((v >> 8) & 0x00FFL) | ((v << 8) & 0xFF00L)); - } -}; - -template <> -struct Pack::TypeInfo<uint32_t> : public Pack::Convertible { - static inline void convert(uint32_t &v) - { - v = ((((v) >> 24) & 0x000000FFL) - | (((v) >> 8) & 0x0000FF00L) - | (((v) << 8) & 0x00FF0000L) - | (((v) << 24) & 0xFF000000L)); - } -}; - -template <> -struct Pack::TypeInfo<uint64_t> : public Pack::Convertible { - static inline void convert(uint64_t &v) - { - v = ((((v) & 0xff00000000000000ull) >> 56) - | (((v) & 0x00ff000000000000ull) >> 40) - | (((v) & 0x0000ff0000000000ull) >> 24) - | (((v) & 0x000000ff00000000ull) >> 8 ) - | (((v) & 0x00000000ff000000ull) << 8 ) - | (((v) & 0x0000000000ff0000ull) << 24) - | (((v) & 0x000000000000ff00ull) << 40) - | (((v) & 0x00000000000000ffull) << 56)); - } -}; - -#endif // !_PACK_H_
--- a/C++/Parser.cpp Sun Mar 08 11:07:36 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,299 +0,0 @@ -/* - * Parser.h -- config file parser - * - * Copyright (c) 2013, 2014 David Demelier <markand@malikania.fr> - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include <cstring> -#include <cerrno> -#include <iostream> -#include <fstream> - -#include "Parser.h" - -/* -------------------------------------------------------- - * Section public members - * -------------------------------------------------------- */ - -Section::Section() - : m_allowed(true) -{ -} - -Section::Section(const std::string &name, bool allowed) - : m_name(name) - , m_allowed(allowed) -{ - -} - -const std::string &Section::getName() const -{ - return m_name; -} - -bool Section::hasOption(const std::string &name) const -{ - return m_options.count(name) >= 1; -} - -Section::Map::iterator Section::begin() -{ - return m_options.begin(); -} - -Section::Map::const_iterator Section::cbegin() const -{ - return m_options.cbegin(); -} - -Section::Map::iterator Section::end() -{ - return m_options.end(); -} - -Section::Map::const_iterator Section::cend() const -{ - return m_options.end(); -} - -bool operator==(const Section &s1, const Section &s2) -{ - return s1.m_name == s2.m_name && s1.m_options == s2.m_options; -} - -/* -------------------------------------------------------- - * Parser private members - * -------------------------------------------------------- */ - -void Parser::addOption(const std::string &key, const std::string &value) -{ - m_sections.back().m_options.insert(std::make_pair(key, value)); -} - -void Parser::readSection(int lineno, const std::string &line) -{ - size_t end; - - if ((end = line.find_first_of(']')) != std::string::npos) { - if (end > 1) { - auto name = line.substr(1, end - 1); - - /* - * Check if we can add a section, if redefinition is - * disabled, we must disable the previous section so the - * further read options should not be enabled until - * a correct section is found again. - */ - if (hasSection(name) && (m_tuning & DisableRedefinition)) { - if (!(m_tuning & DisableVerbosity)) - log(lineno, name, "redefinition not allowed"); - m_sections.back().m_allowed = false; - } else { - m_sections.push_back(Section(name)); - } - } else if (!(m_tuning & DisableVerbosity)) { - /* - * Do not add options at this step because it will - * corrupt the previous one. - */ - m_sections.back().m_allowed = false; - log(lineno, "", "empty section name"); - } - } -} - -void Parser::readOption(int lineno, const std::string &line) -{ - auto ¤t = m_sections.back(); - size_t epos; - std::string key, value; - - // Error on last section? - if (!current.m_allowed) { - /* - * If it is the root section, this has been probably set by - * DisableRootSection flag, otherwise an error has occured - * so no need to log. - */ - if (current.m_name == "" && !(m_tuning == DisableVerbosity)) - log(lineno, "", "option not allowed in that scope"); - - return; - } - - if ((epos = line.find_first_of('=')) == std::string::npos) { - if (!(m_tuning & DisableVerbosity)) - log(lineno, current.m_name, "missing `=' keyword"); - return; - } - - if (epos > 0) { - size_t i, begin, last; - char c; - - key = line.substr(0, epos); - value = line.substr(epos + 1); - - // clean option key - for (i = 0; !isspace(key[i]) && i < key.length(); ++i) - continue; - key = key.substr(0, i); - - // clean option value - for (begin = 0; isspace(value[begin]) && begin < value.length(); ++begin) - continue; - value = value.substr(begin); - - c = value[0]; - begin = 0; - if (c == '\'' || c == '"') { - for (last = begin = 1; value[last] != c && last < value.length(); ++last) - continue; - if (value[last] != c && !(m_tuning & DisableVerbosity)) - if (!(m_tuning & DisableVerbosity)) - log(lineno, current.m_name, "undisclosed std::string"); - } else { - for (last = begin; !isspace(value[last]) && last < value.length(); ++last) - continue; - } - - if (last - begin > 0) - value = value.substr(begin, last - begin); - else - value.clear(); - - // Add the option if the key is not empty - if (key.length() > 0) - addOption(key, value); - } -} - -void Parser::readLine(int lineno, const std::string &line) -{ - size_t i; - std::string buffer; - - // Skip default spaces - for (i = 0; isspace(line[i]) && i < line.length(); ++i) - continue; - - buffer = line.substr(i); - if (buffer.length() > 0) { - if (buffer[0] != m_commentChar) { - if (buffer[0] == '[') - readSection(lineno, buffer); - else - readOption(lineno, buffer); - } - } -} - -/* -------------------------------------------------------- - * Parser public methods - * -------------------------------------------------------- */ - -const char Parser::DEFAULT_COMMENT_CHAR = '#'; - -void Parser::open() -{ - std::ifstream file; - std::string line; - int lineno = 1; - - file.open(m_path.c_str()); - if (!file.is_open()) - throw std::runtime_error(m_path + ": " + std::string(std::strerror(errno))); - - while (std::getline(file, line)) - readLine(lineno++, line); - - file.close(); -} - -Parser::Parser() -{ -} - -Parser::Parser(const std::string &path, int tuning, char commentToken) - : m_path(path) - , m_tuning(tuning) - , m_commentChar(commentToken) -{ - m_sections.push_back(Section("", (tuning & DisableRootSection) ? false : true)); - open(); -} - -Parser::~Parser() -{ -} - -Parser::List::iterator Parser::begin() -{ - return m_sections.begin(); -} - -Parser::List::const_iterator Parser::cbegin() const -{ - return m_sections.cbegin(); -} - -Parser::List::iterator Parser::end() -{ - return m_sections.end(); -} - -Parser::List::const_iterator Parser::cend() const -{ - return m_sections.end(); -} - -void Parser::findSections(const std::string &name, FindFunc func) const -{ - for (const auto &s : m_sections) - if (s.m_name == name) - func(s); -} - -bool Parser::hasSection(const std::string &name) const -{ - for (const auto &s : m_sections) - if (s.m_name == name) - return true; - - return false; -} - -const Section &Parser::getSection(const std::string &name) const -{ - for (const auto &s : m_sections) - if (s.m_name == name) - return s; - - throw std::out_of_range(name + " not found"); -} - -void Parser::log(int number, const std::string &, const std::string &message) -{ - std::cout << "line " << number << ": " << message << std::endl; -} - -bool operator==(const Parser &p1, const Parser &p2) -{ - return p1.m_sections == p2.m_sections && - p1.m_path == p2.m_path && - p1.m_tuning == p2.m_tuning && - p1.m_commentChar == p2.m_commentChar; -}
--- a/C++/Parser.h Sun Mar 08 11:07:36 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,336 +0,0 @@ -/* - * Parser.h -- config file parser - * - * Copyright (c) 2013, 2014 David Demelier <markand@malikania.fr> - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#ifndef _PARSER_H_ -#define _PARSER_H_ - -#include <cstdlib> -#include <functional> -#include <stdexcept> -#include <string> -#include <unordered_map> -#include <utility> -#include <vector> - -/** - * @class Section - * @brief The option container - * - * A list of section found in the file. If root - * options are allowed (default behavior), the root - * section is "". - */ -class Section { -public: - friend class Parser; - - using Map = std::unordered_map<std::string, std::string>; - - template <typename T> - struct Converter { - static const bool supported = false; - }; - -private: - std::string m_name; /*! name of section */ - Map m_options; /*! list of options inside */ - bool m_allowed; /*! is authorized to push */ - -public: - /** - * Default constructor. - */ - Section(); - - /** - * Named constructor. - * - * @param name the section name - * @param allowed is allowed to push - */ - Section(const std::string &name, bool allowed = true); - - /** - * Tells if that section has the specified option name. - * - * @param name the option name - * @return true if has - */ - bool hasOption(const std::string &name) const; - - /** - * Get the section name - * - * @return the section name - */ - const std::string &getName() const; - - /** - * Return an iterator to the beginning. - * - * @return the iterator. - */ - Map::iterator begin(); - - /** - * Return a const iterator to the beginning. - * - * @return the iterator. - */ - Map::const_iterator cbegin() const; - - /** - * Return an iterator to the end. - * - * @return the iterator. - */ - Map::iterator end(); - - /** - * Return a const iterator to the end. - * - * @return the iterator. - */ - Map::const_iterator cend() const; - - /** - * Template all functions for retrieving options value. - * - * @param name the option name - * @return the value if found - */ - template <typename T> - T getOption(const std::string &name) const - { - try { - return requireOption<T>(name); - } catch (...) { - // Catch any conversion error. - } - - return T(); - } - - /** - * Requires an option, this works like getOption except - * that if an option is not found, an exception is - * thrown. - * - * @param name the name - * @return the value - * @throw std::out_of_range if not found - * @throw std::invalid_argument on conversion failures - */ - template <typename T> - T requireOption(const std::string &name) const - { - static_assert(Converter<T>::supported, "invalid type requested"); - - return Converter<T>::convert(m_options.at(name)); - } - - friend bool operator==(const Section &s1, const Section &s2); -}; - -template <> -struct Section::Converter<bool> { - static const bool supported = true; - - static bool convert(const std::string &value) - { - bool result(false); - - if (value == "yes" || value == "true"|| value == "1") - result = true; - else if (value == "no" || value == "false" || value == "0") - result = false; - - return result; - } -}; - -template <> -struct Section::Converter<int> { - static const bool supported = true; - - static int convert(const std::string &value) - { - return std::stoi(value); - } -}; - -template <> -struct Section::Converter<float> { - static const bool supported = true; - - static float convert(const std::string &value) - { - return std::stof(value); - } -}; - -template <> -struct Section::Converter<double> { - static const bool supported = true; - - static double convert(const std::string &value) - { - return std::stod(value); - } -}; - -template <> -struct Section::Converter<std::string> { - static const bool supported = true; - - static std::string convert(const std::string &value) - { - return value; - } -}; - -/** - * @class Parser - * @brief Config file parser - * - * Open and read .ini files. - */ -class Parser { -public: - /** - * Options available for the parser. - */ - enum Tuning { - DisableRootSection = 1, /*! disable options on root */ - DisableRedefinition = 2, /*! disable multiple redefinition */ - DisableVerbosity = 4 /*! be verbose by method */ - }; - - using FindFunc = std::function<void (const Section &)>; - using List = std::vector<Section>; - -private: - List m_sections; /*! list of sections found */ - std::string m_path; /*! path file */ - int m_tuning; /*! options for parsing */ - char m_commentChar; /*! the comment token default (#) */ - - void addSection(const std::string &name); - void addOption(const std::string &key, const std::string &value); - - void readSection(int lineno, const std::string &line); - void readOption(int lineno, const std::string &line); - - void readLine(int lineno, const std::string &line); - - void open(); - -public: - static const char DEFAULT_COMMENT_CHAR; - - /** - * Create a parser at the specified file path. Optional - * options may be added. - * - * @param path the file path - * @param tuning optional tuning flags - * @param commentToken an optional comment delimiter - * @throw std::runtime_error on errors - * @see Tuning - */ - Parser(const std::string &path, int tuning = 0, char commentToken = Parser::DEFAULT_COMMENT_CHAR); - - /** - * Default constructor. - */ - Parser(); - - /** - * Default destructor. - */ - virtual ~Parser(); - - /** - * Return an iterator to the beginning. - * - * @return the iterator. - */ - List::iterator begin(); - - /** - * Return a const iterator to the beginning. - * - * @return the iterator. - */ - List::const_iterator cbegin() const; - - /** - * Return an iterator to the end. - * - * @return the iterator. - */ - List::iterator end(); - - /** - * Return a const iterator to the end. - * - * @return the iterator. - */ - List::const_iterator cend() const; - - /** - * Find all sections matching the name. - * - * @param name the sections name - * @param func the function - * @return a list of section with the options - */ - void findSections(const std::string &name, FindFunc func) const; - - /** - * Tell if a section is existing. - * - * @return true if exists - */ - bool hasSection(const std::string &name) const; - - /** - * Get a specified section. - * - * @param name the section name - * @return a section - * @throw std::out_of_range if not found - */ - const Section &getSection(const std::string &name) const; - - /** - * Logging function, used only if DisableVerbosity is not set. The - * default behavior is to print to stdout something like: - * line 10: syntax error - * line 8: missing = - * - * @param number the line number - * @param section the current section worked on - * @param message the message - */ - virtual void log(int number, const std::string §ion, const std::string &message); - - friend bool operator==(const Parser &p1, const Parser &p2); -}; - -#endif // !_PARSER_H_
--- a/C++/Socket.cpp Sun Mar 08 11:07:36 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,173 +0,0 @@ -/* - * Socket.cpp -- portable C++ socket wrappers - * - * Copyright (c) 2013, 2014 David Demelier <markand@malikania.fr> - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include <cstring> - -#include "Socket.h" -#include "SocketAddress.h" - -/* -------------------------------------------------------- - * System dependent code - * -------------------------------------------------------- */ - -#if defined(_WIN32) - -std::string Socket::syserror(int errn) -{ - LPSTR str = nullptr; - std::string errmsg = "Unknown error"; - - FormatMessageA( - FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, - NULL, - errn, - MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), - (LPSTR)&str, 0, NULL); - - - if (str) { - errmsg = std::string(str); - LocalFree(str); - } - - return errmsg; -} - -#else - -#include <cerrno> - -std::string Socket::syserror(int errn) -{ - return strerror(errn); -} - -#endif - -std::string Socket::syserror() -{ -#if defined(_WIN32) - return syserror(WSAGetLastError()); -#else - return syserror(errno); -#endif -} - -/* -------------------------------------------------------- - * SocketError class - * -------------------------------------------------------- */ - -SocketError::SocketError(Code code, std::string function) - : m_code(code) - , m_function(std::move(function)) - , m_error(Socket::syserror()) -{ -} - -SocketError::SocketError(Code code, std::string function, int error) - : m_code(code) - , m_function(std::move(function)) - , m_error(Socket::syserror(error)) -{ -} - -SocketError::SocketError(Code code, std::string function, std::string error) - : m_code(code) - , m_function(std::move(function)) - , m_error(std::move(error)) -{ -} - -/* -------------------------------------------------------- - * Socket class - * -------------------------------------------------------- */ - -#if defined(_WIN32) -std::mutex Socket::s_mutex; -std::atomic<bool> Socket::s_initialized{false}; -#endif - -Socket::Socket(int domain, int type, int protocol) -{ -#if defined(_WIN32) && !defined(SOCKET_NO_WSA_INIT) - if (!s_initialized) - initialize(); -#endif - - m_handle = ::socket(domain, type, protocol); - - if (m_handle == Invalid) - throw SocketError(SocketError::System, "socket"); - - m_state = SocketState::Opened; -} - -void Socket::bind(const SocketAddress &address) -{ - const auto &sa = address.address(); - const auto addrlen = address.length(); - - if (::bind(m_handle, reinterpret_cast<const sockaddr *>(&sa), addrlen) == Error) - throw SocketError(SocketError::System, "bind"); - - m_state = SocketState::Bound; -} - -void Socket::close() -{ -#if defined(_WIN32) - ::closesocket(m_handle); -#else - ::close(m_handle); -#endif - - SocketState::Closed; -} - -void Socket::setBlockMode(bool block) -{ -#if defined(O_NONBLOCK) && !defined(_WIN32) - int flags; - - if ((flags = fcntl(m_handle, F_GETFL, 0)) == -1) - flags = 0; - - if (block) - flags &= ~(O_NONBLOCK); - else - flags |= O_NONBLOCK; - - if (fcntl(m_handle, F_SETFL, flags) == Error) - throw SocketError(SocketError::System, "setBlockMode"); -#else - unsigned long flags = (block) ? 0 : 1; - - if (ioctlsocket(m_handle, FIONBIO, &flags) == Error) - throw SocketError(SocketError::System, "setBlockMode"); -#endif -} - -bool operator==(const Socket &s1, const Socket &s2) -{ - return s1.handle() == s2.handle(); -} - -bool operator<(const Socket &s1, const Socket &s2) -{ - return s1.handle() < s2.handle(); -}
--- a/C++/Socket.h Sun Mar 08 11:07:36 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,404 +0,0 @@ -/* - * Socket.h -- portable C++ socket wrappers - * - * Copyright (c) 2013, 2014 David Demelier <markand@malikania.fr> - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#ifndef _SOCKET_NG_H_ -#define _SOCKET_NG_H_ - -/** - * @file Socket.h - * @brief Portable socket abstraction - * - * User may set the following variables before compiling these files: - * - * SOCKET_NO_WSA_INIT - (bool) Set to false if you don't want Socket class to - * automatically calls WSAStartup() when creating sockets. - * - * Otherwise, you will need to call Socket::init, - * Socket::finish yourself. - * - * SOCKET_NO_SSL_INIT - (bool) Set to false if you don't want OpenSSL to be - * initialized when the first SocketSsl object is created. - * - * SOCKET_HAVE_POLL - (bool) Set to true if poll(2) function is available. - * - * Note: on Windows, this is automatically set if the - * _WIN32_WINNT variable is greater or equal to 0x0600. - */ - -#include <cstring> -#include <exception> -#include <string> - -#if defined(_WIN32) -# include <atomic> -# include <cstdlib> -# include <mutex> - -# include <WinSock2.h> -# include <WS2tcpip.h> -#else -# include <cerrno> - -# include <sys/ioctl.h> -# include <sys/socket.h> -# include <sys/types.h> - -# include <arpa/inet.h> - -# include <netinet/in.h> - -# include <fcntl.h> -# include <netdb.h> -# include <unistd.h> -#endif - -class SocketAddress; - -/** - * @class SocketError - * @brief Base class for sockets error - */ -class SocketError : public std::exception { -public: - enum Code { - WouldBlockRead, ///!< The operation would block for reading - WouldBlockWrite, ///!< The operation would block for writing - Timeout, ///!< The action did timeout - System ///!< There is a system error - }; - - Code m_code; - std::string m_function; - std::string m_error; - - /** - * Constructor that use the last system error. - * - * @param code which kind of error - * @param function the function name - */ - SocketError(Code code, std::string function); - - /** - * Constructor that use the system error set by the user. - * - * @param code which kind of error - * @param function the function name - * @param error the error - */ - SocketError(Code code, std::string function, int error); - - /** - * Constructor that set the error specified by the user. - * - * @param code which kind of error - * @param function the function name - * @param error the error - */ - SocketError(Code code, std::string function, std::string error); - - /** - * Get which function has triggered the error. - * - * @return the function name (e.g connect) - */ - inline const std::string &function() const noexcept - { - return m_function; - } - - /** - * The error code. - * - * @return the code - */ - inline Code code() const noexcept - { - return m_code; - } - - /** - * Get the error (only the error content). - * - * @return the error - */ - const char *what() const noexcept - { - return m_error.c_str(); - } -}; - -/** - * @enum SocketState - * @brief Category of error - */ -enum class SocketState { - Opened, ///!< Socket is opened - Closed, ///!< Socket has been closed - Bound, ///!< Socket is bound to address - Connected, ///!< Socket is connected to an end point - Disconnected, ///!< Socket is disconnected - Timeout ///!< Timeout has occured in a waiting operation -}; - -/** - * @class Socket - * @brief Base socket class for socket operations - */ -class Socket { -public: - /* {{{ Portable types */ - - /* - * The following types are defined differently between Unix - * and Windows. - */ -#if defined(_WIN32) - using Handle = SOCKET; - using ConstArg = const char *; - using Arg = char *; -#else - using Handle = int; - using ConstArg = const void *; - using Arg = void *; -#endif - - /* }}} */ - - /* {{{ Portable constants */ - - /* - * The following constants are defined differently from Unix - * to Windows. - */ -#if defined(_WIN32) - static constexpr const int Invalid = INVALID_SOCKET; - static constexpr const int Error = SOCKET_ERROR; -#else - static constexpr const int Invalid = -1; - static constexpr const int Error = -1; -#endif - - /* }}} */ - - /* {{{ Portable initialization */ - - /* - * Initialization stuff. - * - * The function init and finish are threadsafe. - */ -#if defined(_WIN32) -private: - static std::mutex s_mutex; - static std::atomic<bool> s_initialized; - -public: - static inline void finish() noexcept - { - WSACleanup(); - } - - static inline void init() noexcept - { - std::lock_guard<std::mutex> lock(s_mutex); - - if (!s_initialized) { - s_initialized = true; - - WSADATA wsa; - WSAStartup(MAKEWORD(2, 2), &wsa); - - /* - * If SOCKET_WSA_NO_INIT is not set then the user - * must also call finish himself. - */ -#if !defined(SOCKET_WSA_NO_INIT) - std::atexit(finish); -#endif - } - } -#else -public: - /** - * no-op. - */ - static inline void init() noexcept {} - - /** - * no-op. - */ - static inline void finish() noexcept {} -#endif - - /* }}} */ - -protected: - Handle m_handle; - SocketState m_state{SocketState::Opened}; - -public: - /** - * Get the last socket system error. The error is set from errno or from - * WSAGetLastError on Windows. - * - * @return a string message - */ - static std::string syserror(); - - /** - * Get the last system error. - * - * @param errn the error number (errno or WSAGetLastError) - * @return the error - */ - static std::string syserror(int errn); - - /** - * Construct a socket with an already created descriptor. - * - * @param handle the native descriptor - */ - inline Socket(Handle handle) - : m_handle(handle) - , m_state(SocketState::Opened) - { - } - - /** - * Create a socket handle. - * - * @param domain the domain AF_* - * @param type the type SOCK_* - * @param protocol the protocol - * @throw SocketError on failures - */ - Socket(int domain, int type, int protocol); - - /** - * Default destructor. - */ - virtual ~Socket() = default; - - /** - * Set an option for the socket. - * - * @param level the setting level - * @param name the name - * @param arg the value - * @throw SocketError on error - */ - template <typename Argument> - inline void set(int level, int name, const Argument &arg) - { -#if defined(_WIN32) - if (setsockopt(m_handle, level, name, (Socket::ConstArg)&arg, sizeof (arg)) == SOCKET_ERROR) -#else - if (setsockopt(m_handle, level, name, (Socket::ConstArg)&arg, sizeof (arg)) < 0) -#endif - throw SocketError(SocketError::System, "set"); - } - - /** - * Get an option for the socket. - * - * @param level the setting level - * @param name the name - * @throw SocketError on error - */ - template <typename Argument> - inline Argument get(int level, int name) - { - Argument desired, result{}; - socklen_t size = sizeof (result); - -#if defined(_WIN32) - if (getsockopt(m_handle, level, name, (Socket::Arg)&desired, &size) == SOCKET_ERROR) -#else - if (getsockopt(m_handle, level, name, (Socket::Arg)&desired, &size) < 0) -#endif - throw SocketError(SocketError::System, "get"); - - std::memcpy(&result, &desired, size); - - return result; - } - - /** - * Get the native handle. - * - * @return the handle - * @warning Not portable - */ - inline Handle handle() const noexcept - { - return m_handle; - } - - /** - * Get the socket state. - * - * @return - */ - inline SocketState state() const noexcept - { - return m_state; - } - - /** - * Bind to an address. - * - * @param address the address - * @throw SocketError on any error - */ - void bind(const SocketAddress &address); - - /** - * Set the blocking mode, if set to false, the socket will be marked - * **non-blocking**. - * - * @param block set to false to mark **non-blocking** - * @throw SocketError on any error - */ - void setBlockMode(bool block); - - /** - * Close the socket. - */ - virtual void close(); -}; - -/** - * Compare two sockets. - * - * @param s1 the first socket - * @param s2 the second socket - * @return true if they equals - */ -bool operator==(const Socket &s1, const Socket &s2); - -/** - * Compare two sockets, ideal for putting in a std::map. - * - * @param s1 the first socket - * @param s2 the second socket - * @return true if s1 < s2 - */ -bool operator<(const Socket &s1, const Socket &s2); - -#endif // !_SOCKET_NG_H_
--- a/C++/SocketAddress.cpp Sun Mar 08 11:07:36 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,136 +0,0 @@ -/* - * SocketAddress.cpp -- socket addresses management - * - * Copyright (c) 2013, 2014 David Demelier <markand@malikania.fr> - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include <algorithm> -#include <cstring> - -#include "Socket.h" -#include "SocketAddress.h" - -namespace address { - -/* -------------------------------------------------------- - * Internet implementation - * -------------------------------------------------------- */ - -Internet::Internet(const std::string &host, unsigned port, int domain) -{ - if (host == "*") { - if (domain == AF_INET6) { - sockaddr_in6 *ptr = (sockaddr_in6 *)&m_addr; - - ptr->sin6_addr = in6addr_any; - ptr->sin6_family = AF_INET6; - ptr->sin6_port = htons(port); - - m_addrlen = sizeof (sockaddr_in6); - } else { - sockaddr_in *ptr = (sockaddr_in *)&m_addr; - - ptr->sin_addr.s_addr = INADDR_ANY; - ptr->sin_family = AF_INET; - ptr->sin_port = htons(port); - - m_addrlen = sizeof (sockaddr_in); - } - } else { - addrinfo hints, *res; - - std::memset(&hints, 0, sizeof (addrinfo)); - hints.ai_family = domain; - - auto error = getaddrinfo(host.c_str(), std::to_string(port).c_str(), &hints, &res); - if (error != 0) - throw SocketError(SocketError::System, "getaddrinfo", gai_strerror(error)); - - std::memcpy(&m_addr, res->ai_addr, res->ai_addrlen); - m_addrlen = res->ai_addrlen; - freeaddrinfo(res); - } -} - -/* -------------------------------------------------------- - * Unix implementation - * -------------------------------------------------------- */ - -#if !defined(_WIN32) - -#include <sys/un.h> - -Unix::Unix(const std::string &path, bool rm) -{ - sockaddr_un *sun = (sockaddr_un *)&m_addr; - - // Silently remove the file even if it fails - if (rm) - ::remove(path.c_str()); - - // Copy the path - memset(sun->sun_path, 0, sizeof (sun->sun_path)); - strncpy(sun->sun_path, path.c_str(), sizeof (sun->sun_path) - 1); - - // Set the parameters - sun->sun_family = AF_UNIX; - m_addrlen = SUN_LEN(sun); -} - -#endif // _WIN32 - -} // !address - -/* -------------------------------------------------------- - * SocketAddress implementation - * -------------------------------------------------------- */ - -SocketAddress::SocketAddress() - : m_addrlen(0) -{ - memset(&m_addr, 0, sizeof (m_addr)); -} - -SocketAddress::SocketAddress(const sockaddr_storage &addr, socklen_t length) - : m_addr(addr) - , m_addrlen(length) -{ -} - -const sockaddr_storage &SocketAddress::address() const -{ - return m_addr; -} - -socklen_t SocketAddress::length() const -{ - return m_addrlen; -} - -bool operator<(const SocketAddress &s1, const SocketAddress &s2) -{ - const auto &array1 = reinterpret_cast<const unsigned char *>(&s1.address()); - const auto &array2 = reinterpret_cast<const unsigned char *>(&s2.address()); - - return std::lexicographical_compare(array1, array1 + s1.length(), array2, array2 + s2.length()); -} - -bool operator==(const SocketAddress &s1, const SocketAddress &s2) -{ - const auto &array1 = reinterpret_cast<const unsigned char *>(&s1.address()); - const auto &array2 = reinterpret_cast<const unsigned char *>(&s2.address()); - - return std::equal(array1, array1 + s1.length(), array2, array2 + s2.length()); -}
--- a/C++/SocketAddress.h Sun Mar 08 11:07:36 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,143 +0,0 @@ -/* - * SocketAddress.h -- socket addresses management - * - * Copyright (c) 2013, 2014 David Demelier <markand@malikania.fr> - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#ifndef _SOCKET_ADDRESS_NG_H_ -#define _SOCKET_ADDRESS_NG_H_ - -#include <string> - -#if defined(_WIN32) -# include <Winsock2.h> -# include <Ws2tcpip.h> -#else -# include <sys/socket.h> -#endif - -/** - * @class SocketAddress - * @brief base class for socket addresses - * - * This class is mostly used to bind, connect or getting information - * on socket clients. - * - * @see Internet - * @see Unix - */ -class SocketAddress { -protected: - sockaddr_storage m_addr; - socklen_t m_addrlen; - -public: - /** - * Default constructor. - */ - SocketAddress(); - - /** - * Constructor with address and size. - * - * @param addr the address - * @param length the address length - */ - SocketAddress(const sockaddr_storage &addr, socklen_t length); - - /** - * Default destructor. - */ - virtual ~SocketAddress() = default; - - /** - * Get the address length - * - * @return the length - */ - socklen_t length() const; - - /** - * Get the address. - * - * @return the address - */ - const sockaddr_storage &address() const; - - /** - * Compare the addresses. The check is lexicographical. - * - * @param s1 the first address - * @param s2 the second address - * @return true if s1 is less than s2 - */ - friend bool operator<(const SocketAddress &s1, const SocketAddress &s2); - - /** - * Compare the addresses. - * - * @param s1 the first address - * @param s2 the second address - * @return true if s1 == s2 - */ - friend bool operator==(const SocketAddress &s1, const SocketAddress &s2); -}; - -namespace address { - -/** - * @class Internet - * @brief internet protocol connect class - * - * Create a connect address for internet protocol, - * using getaddrinfo(3). - */ -class Internet : public SocketAddress { -public: - /** - * Create an IPv4 or IPV6 end point. - * - * @param host the hostname - * @param port the port - * @param family AF_INET, AF_INET6, ... - * @throw SocketError on error - */ - Internet(const std::string &host, unsigned port, int family); -}; - -#if !defined(_WIN32) - -/** - * @class Unix - * @brief unix family sockets - * - * Create an address to a specific path. Only available on Unix. - */ -class Unix : public SocketAddress { -public: - /** - * Construct an address to a path. - * - * @param path the path - * @param rm remove the file before (default: false) - */ - Unix(const std::string &path, bool rm = false); -}; - -#endif // ! !_WIN32 - -} // !address - -#endif // !_SOCKET_ADDRESS_NG_H_
--- a/C++/SocketListener.cpp Sun Mar 08 11:07:36 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,330 +0,0 @@ -/* - * SocketListener.cpp -- portable select() wrapper - * - * Copyright (c) 2013, 2014 David Demelier <markand@malikania.fr> - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include <algorithm> -#include <map> -#include <set> -#include <utility> -#include <vector> - -#include "SocketListener.h" - -/* -------------------------------------------------------- - * Select implementation - * -------------------------------------------------------- */ - -namespace { - -/** - * @class SelectMethod - * @brief Implements select(2) - * - * This class is the fallback of any other method, it is not preferred at all for many reasons. - */ -class SelectMethod final : public SocketListenerInterface { -private: - std::map<Socket::Handle, std::pair<std::reference_wrapper<Socket>, int>> m_table; - -public: - void set(Socket &s, int direction) override - { - if (m_table.count(s.handle()) > 0) { - m_table.at(s.handle()).second |= direction; - } else { - m_table.insert({s.handle(), {s, direction}}); - } - - } - - void unset(Socket &s, int direction) override - { - if (m_table.count(s.handle()) != 0) { - m_table.at(s.handle()).second &= ~(direction); - - // If no read, no write is requested, remove it - if (m_table.at(s.handle()).second == 0) { - m_table.erase(s.handle()); - } - } - } - - void remove(Socket &sc) override - { - m_table.erase(sc.handle()); - } - - void clear() override - { - m_table.clear(); - } - - SocketStatus select(int ms) override - { - auto result = selectMultiple(ms); - - if (result.size() == 0) { - throw SocketError(SocketError::System, "select", "No socket found"); - } - - return result[0]; - } - - std::vector<SocketStatus> selectMultiple(int ms) override - { - timeval maxwait, *towait; - fd_set readset; - fd_set writeset; - - FD_ZERO(&readset); - FD_ZERO(&writeset); - - Socket::Handle max = 0; - - for (auto &s : m_table) { - if (s.second.second & SocketListener::Read) { - FD_SET(s.first, &readset); - } - if (s.second.second & SocketListener::Write) { - FD_SET(s.first, &writeset); - } - - if (s.first > max) { - max = s.first; - } - } - - maxwait.tv_sec = 0; - maxwait.tv_usec = ms * 1000; - - // Set to nullptr for infinite timeout. - towait = (ms < 0) ? nullptr : &maxwait; - - auto error = ::select(max + 1, &readset, &writeset, nullptr, towait); - if (error == Socket::Error) { - throw SocketError(SocketError::System, "select"); - } - if (error == 0) { - throw SocketError(SocketError::Timeout, "select", "Timeout while listening"); - } - - std::vector<SocketStatus> sockets; - - for (auto &c : m_table) { - if (FD_ISSET(c.first, &readset)) { - sockets.push_back({ c.second.first, SocketListener::Read }); - } - if (FD_ISSET(c.first, &writeset)) { - sockets.push_back({ c.second.first, SocketListener::Write }); - } - } - - return sockets; - } -}; - -} // !namespace - -/* -------------------------------------------------------- - * Poll implementation - * -------------------------------------------------------- */ - -#if defined(SOCKET_HAVE_POLL) - -#if defined(_WIN32) -# include <Winsock2.h> -# define poll WSAPoll -#else -# include <poll.h> -#endif - -namespace { - -class PollMethod final : public SocketListenerInterface { -private: - std::vector<pollfd> m_fds; - std::map<Socket::Handle, std::reference_wrapper<Socket>> m_lookup; - - inline short topoll(int direction) - { - short result(0); - - if (direction & SocketListener::Read) - result |= POLLIN; - if (direction & SocketListener::Write) - result |= POLLOUT; - - return result; - } - - inline int todirection(short event) - { - int direction{}; - - /* - * Poll implementations mark the socket differently regarding - * the disconnection of a socket. - * - * At least, even if POLLHUP or POLLIN is set, recv() always - * return 0 so we mark the socket as readable. - */ - if ((event & POLLIN) || (event & POLLHUP)) - direction |= SocketListener::Read; - if (event & POLLOUT) - direction |= SocketListener::Write; - - return direction; - } - -public: - void set(Socket &s, int direction) override - { - auto it = std::find_if(m_fds.begin(), m_fds.end(), [&] (const auto &pfd) { return pfd.fd == s.handle(); }); - - // If found, add the new direction, otherwise add a new socket - if (it != m_fds.end()) - it->events |= topoll(direction); - else { - m_lookup.insert({s.handle(), s}); - m_fds.push_back({ s.handle(), topoll(direction), 0 }); - } - } - - void unset(Socket &s, int direction) override - { - for (auto i = m_fds.begin(); i != m_fds.end();) { - if (i->fd == s.handle()) { - i->events &= ~(topoll(direction)); - - if (i->events == 0) { - m_lookup.erase(i->fd); - i = m_fds.erase(i); - } else { - ++i; - } - } else - ++i; - } - } - - void remove(Socket &s) override - { - auto it = std::find_if(m_fds.begin(), m_fds.end(), [&] (const auto &pfd) { return pfd.fd == s.handle(); }); - - if (it != m_fds.end()) { - m_fds.erase(it); - m_lookup.erase(s.handle()); - } - } - - void clear() override - { - m_fds.clear(); - m_lookup.clear(); - } - - SocketStatus select(int ms) override - { - auto result = poll(m_fds.data(), m_fds.size(), ms); - if (result == 0) - throw SocketError(SocketError::Timeout, "select", "Timeout while listening"); - if (result < 0) - throw SocketError(SocketError::System, "poll"); - - for (auto &fd : m_fds) { - if (fd.revents != 0) { - return { m_lookup.at(fd.fd), todirection(fd.revents) }; - } - } - - throw SocketError(SocketError::System, "select", "No socket found"); - } - - std::vector<SocketStatus> selectMultiple(int ms) override - { - auto result = poll(m_fds.data(), m_fds.size(), ms); - if (result == 0) { - throw SocketError(SocketError::Timeout, "select", "Timeout while listening"); - } - if (result < 0) { - throw SocketError(SocketError::System, "poll"); - } - - std::vector<SocketStatus> sockets; - for (auto &fd : m_fds) { - if (fd.revents != 0) { - sockets.push_back({ m_lookup.at(fd.fd), todirection(fd.revents) }); - } - } - - return sockets; - } -}; - -} // !namespace - -#endif // !_SOCKET_HAVE_POLL - -/* -------------------------------------------------------- - * SocketListener - * -------------------------------------------------------- */ - -const int SocketListener::Read{1 << 0}; -const int SocketListener::Write{1 << 1}; - -SocketListener::SocketListener(std::initializer_list<std::pair<std::reference_wrapper<Socket>, int>> list) - : SocketListener() -{ - for (const auto &p : list) - set(p.first, p.second); -} - -SocketListener::SocketListener(SocketMethod method) -{ -#if defined(SOCKET_HAVE_POLL) - if (method == SocketMethod::Poll) - m_interface = std::make_unique<PollMethod>(); - else -#endif - m_interface = std::make_unique<SelectMethod>(); - - (void)method; -} - -void SocketListener::set(Socket &sc, int flags) -{ - if (m_map.count(sc) > 0) { - m_map[sc] |= flags; - m_interface->set(sc, flags); - } else { - m_map.insert({sc, flags}); - m_interface->set(sc, flags); - } -} - -void SocketListener::unset(Socket &sc, int flags) noexcept -{ - if (m_map.count(sc) > 0) { - m_map[sc] &= ~(flags); - m_interface->unset(sc, flags); - - // No more flags, remove it - if (m_map[sc] == 0) { - m_map.erase(sc); - } - } -}
--- a/C++/SocketListener.h Sun Mar 08 11:07:36 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,347 +0,0 @@ -/* - * SocketListener.h -- portable select() wrapper - * - * Copyright (c) 2013, 2014 David Demelier <markand@malikania.fr> - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#ifndef _SOCKET_LISTENER_NG_H_ -#define _SOCKET_LISTENER_NG_H_ - -#include <chrono> -#include <functional> -#include <initializer_list> -#include <map> -#include <memory> -#include <utility> -#include <vector> - -#include "Socket.h" - -#if defined(_WIN32) -# if _WIN32_WINNT >= 0x0600 -# define SOCKET_HAVE_POLL -# endif -#else -# define SOCKET_HAVE_POLL -#endif - -/** - * @enum SocketMethod - * @brief The SocketMethod enum - * - * Select the method of polling. It is only a preferred method, for example if you - * request for poll but it is not available, select will be used. - */ -enum class SocketMethod { - Select, //!< select(2) method, fallback - Poll //!< poll(2), everywhere possible -}; - -/** - * @struct SocketStatus - * @brief The SocketStatus struct - * - * Result of a select call, returns the first ready socket found with its - * direction. - */ -class SocketStatus { -public: - Socket &socket; //!< which socket is ready - int direction; //!< the direction -}; - -/** - * @class SocketListenerInterface - * @brief Implement the polling method - */ -class SocketListenerInterface { -public: - /** - * Default destructor. - */ - virtual ~SocketListenerInterface() = default; - - /** - * Add a socket with a specified direction. - * - * @param s the socket - * @param direction the direction - */ - virtual void set(Socket &sc, int direction) = 0; - - /** - * Remove a socket with a specified direction. - * - * @param s the socket - * @param direction the direction - */ - virtual void unset(Socket &sc, int direction) = 0; - - /** - * Remove completely a socket. - * - * @param sc the socket to remove - */ - virtual void remove(Socket &sc) = 0; - - /** - * Remove all sockets. - */ - virtual void clear() = 0; - - /** - * Select one socket. - * - * @param ms the number of milliseconds to wait, -1 means forever - * @return the socket status - * @throw error::Failure on failure - * @throw error::Timeout on timeout - */ - virtual SocketStatus select(int ms) = 0; - - /** - * Select many sockets. - * - * @param ms the number of milliseconds to wait, -1 means forever - * @return a vector of ready sockets - * @throw error::Failure on failure - * @throw error::Timeout on timeout - */ - virtual std::vector<SocketStatus> selectMultiple(int ms) = 0; -}; - -/** - * @class SocketListener - * @brief Synchronous multiplexing - * - * Convenient wrapper around the select() system call. - * - * This class is implemented using a bridge pattern to allow different uses - * of listener implementation. - * - * Currently, poll and select() are available. - * - * This wrappers takes abstract sockets as non-const reference but it does not - * own them so you must take care that sockets are still alive until the - * SocketListener is destroyed. - */ -class SocketListener final { -public: -#if defined(SOCKET_HAVE_POLL) - static constexpr const SocketMethod PreferredMethod = SocketMethod::Poll; -#else - static constexpr const SocketMethod PreferredMethod = SocketMethod::Select; -#endif - - static const int Read; - static const int Write; - - using Map = std::map<std::reference_wrapper<Socket>, int>; - using Iface = std::unique_ptr<SocketListenerInterface>; - -private: - Map m_map; - Iface m_interface; - -public: - /** - * Move constructor. - * - * @param other the other object - */ - SocketListener(SocketListener &&other) = default; - - /** - * Move operator. - * - * @param other the other object - * @return this - */ - SocketListener &operator=(SocketListener &&other) = default; - - /** - * Create a socket listener. - * - * @param method the preferred method - */ - SocketListener(SocketMethod method = PreferredMethod); - - /** - * Create a listener from a list of sockets. - * - * @param list the list - */ - SocketListener(std::initializer_list<std::pair<std::reference_wrapper<Socket>, int>> list); - - /** - * Return an iterator to the beginning. - * - * @return the iterator - */ - inline auto begin() noexcept - { - return m_map.begin(); - } - - /** - * Overloaded function. - * - * @return the iterator - */ - inline auto begin() const noexcept - { - return m_map.begin(); - } - - /** - * Overloaded function. - * - * @return the iterator - */ - inline auto cbegin() const noexcept - { - return m_map.cbegin(); - } - - /** - * Return an iterator to the end. - * - * @return the iterator - */ - inline auto end() noexcept - { - return m_map.end(); - } - - /** - * Overloaded function. - * - * @return the iterator - */ - inline auto end() const noexcept - { - return m_map.end(); - } - - /** - * Overloaded function. - * - * @return the iterator - */ - inline auto cend() const noexcept - { - return m_map.cend(); - } - - /** - * Add a socket to the listener. - * - * @param sc the socket - * @param direction (may be OR'ed) - */ - void set(Socket &sc, int direction); - - /** - * Unset a socket from the listener, only the direction is removed - * unless the two directions are requested. - * - * For example, if you added a socket for both reading and writing, - * unsetting the write direction will keep the socket for reading. - * - * @param sc the socket - * @param direction the direction (may be OR'ed) - * @see remove - */ - void unset(Socket &sc, int direction) noexcept; - - /** - * Remove completely the socket from the listener. - * - * @param sc the socket - */ - inline void remove(Socket &sc) noexcept - { - m_map.erase(sc); - m_interface->remove(sc); - } - - /** - * Remove all sockets. - */ - inline void clear() noexcept - { - m_map.clear(); - m_interface->clear(); - } - - /** - * Get the number of sockets in the listener. - */ - unsigned size() const noexcept - { - return m_map.size(); - } - - /** - * Select a socket. Waits for a specific amount of time specified as the duration. - * - * @param duration the duration - * @return the socket ready - */ - template <typename Rep, typename Ratio> - inline SocketStatus select(const std::chrono::duration<Rep, Ratio> &duration) - { - auto cvt = std::chrono::duration_cast<std::chrono::milliseconds>(duration); - - return m_interface->select(cvt.count()); - } - - /** - * Overload with milliseconds. - * - * @param timeout the optional timeout in milliseconds - * @return the socket ready - */ - inline SocketStatus select(int timeout = -1) - { - return m_interface->select(timeout); - } - - /** - * Select multiple sockets. - * - * @param duration the duration - * @return the socket ready - */ - template <typename Rep, typename Ratio> - inline std::vector<SocketStatus> selectMultiple(const std::chrono::duration<Rep, Ratio> &duration) - { - auto cvt = std::chrono::duration_cast<std::chrono::milliseconds>(duration); - - return m_interface->selectMultiple(cvt.count()); - } - - /** - * Overload with milliseconds. - * - * @return the socket ready - */ - inline std::vector<SocketStatus> selectMultiple(int timeout = -1) - { - return m_interface->selectMultiple(timeout); - } -}; - -#endif // !_SOCKET_LISTENER_NG_H_
--- a/C++/SocketSsl.cpp Sun Mar 08 11:07:36 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,228 +0,0 @@ -/* - * SocketSsl.cpp -- OpenSSL extension for sockets - * - * Copyright (c) 2013, David Demelier <markand@malikania.fr> - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include "SocketAddress.h" -#include "SocketListener.h" -#include "SocketSsl.h" - -namespace { - -const SSL_METHOD *sslMethod(int mflags) -{ - if (mflags & SocketSslOptions::All) - return SSLv23_method(); - if (mflags & SocketSslOptions::SSLv3) - return SSLv3_method(); - if (mflags & SocketSslOptions::TLSv1) - return TLSv1_method(); - - return SSLv23_method(); -} - -inline std::string sslError(int error) -{ - return ERR_reason_error_string(error); -} - -inline int toDirection(int error) -{ - if (error == SocketError::WouldBlockRead) - return SocketListener::Read; - if (error == SocketError::WouldBlockWrite) - return SocketListener::Write; - - return 0; -} - -} // !namespace - -std::mutex SocketSsl::s_sslMutex; -std::atomic<bool> SocketSsl::s_sslInitialized{false}; - -SocketSsl::SocketSsl(Socket::Handle handle, SSL_CTX *context, SSL *ssl) - : SocketAbstractTcp(handle) - , m_context(context, SSL_CTX_free) - , m_ssl(ssl, SSL_free) -{ -#if !defined(SOCKET_NO_SSL_INIT) - if (!s_sslInitialized) - sslInitialize(); -#endif -} - -SocketSsl::SocketSsl(int family, int protocol, SocketSslOptions options) - : SocketAbstractTcp(family, protocol) - , m_options(std::move(options)) -{ -#if !defined(SOCKET_NO_SSL_INIT) - if (!s_sslInitialized) - sslInitialize(); -#endif -} - -void SocketSsl::connect(const SocketAddress &address) -{ - standardConnect(address); - - // Context first - auto context = SSL_CTX_new(sslMethod(m_options.method)); - - m_context = ContextHandle(context, SSL_CTX_free); - - // SSL object then - auto ssl = SSL_new(context); - - m_ssl = SslHandle(ssl, SSL_free); - - SSL_set_fd(ssl, m_handle); - - auto ret = SSL_connect(ssl); - - if (ret <= 0) { - auto error = SSL_get_error(ssl, ret); - - if (error == SSL_ERROR_WANT_READ) { - throw SocketError(SocketError::WouldBlockRead, "connect", "Operation in progress"); - } else if (error == SSL_ERROR_WANT_WRITE) { - throw SocketError(SocketError::WouldBlockWrite, "connect", "Operation in progress"); - } else { - throw SocketError(SocketError::System, "connect", sslError(error)); - } - } - - m_state = SocketState::Connected; -} - -void SocketSsl::waitConnect(const SocketAddress &address, int timeout) -{ - try { - // Initial try - connect(address); - } catch (const SocketError &ex) { - if (ex.code() == SocketError::WouldBlockRead || ex.code() == SocketError::WouldBlockWrite) { - SocketListener listener{{*this, toDirection(ex.code())}}; - - listener.select(timeout); - - // Second try - connect(address); - } else { - throw; - } - } -} - -SocketSsl SocketSsl::accept() -{ - SocketAddress dummy; - - return accept(dummy); -} - -SocketSsl SocketSsl::accept(SocketAddress &info) -{ - auto client = standardAccept(info); - auto context = SSL_CTX_new(sslMethod(m_options.method)); - - if (m_options.certificate.size() > 0) - SSL_CTX_use_certificate_file(context, m_options.certificate.c_str(), SSL_FILETYPE_PEM); - if (m_options.privateKey.size() > 0) - SSL_CTX_use_PrivateKey_file(context, m_options.privateKey.c_str(), SSL_FILETYPE_PEM); - if (m_options.verify && !SSL_CTX_check_private_key(context)) { - client.close(); - throw SocketError(SocketError::System, "accept", "certificate failure"); - } - - // SSL object - auto ssl = SSL_new(context); - - SSL_set_fd(ssl, client.handle()); - - auto ret = SSL_accept(ssl); - - if (ret <= 0) { - auto error = SSL_get_error(ssl, ret); - - if (error == SSL_ERROR_WANT_READ) { - throw SocketError(SocketError::WouldBlockRead, "accept", "Operation would block"); - } else if (error == SSL_ERROR_WANT_WRITE) { - throw SocketError(SocketError::WouldBlockWrite, "accept", "Operation would block"); - } else { - throw SocketError(SocketError::System, "accept", sslError(error)); - } - } - - return SocketSsl(client.handle(), context, ssl); -} - -unsigned SocketSsl::recv(void *data, unsigned len) -{ - auto nbread = SSL_read(m_ssl.get(), data, len); - - if (nbread <= 0) { - auto error = SSL_get_error(m_ssl.get(), nbread); - - if (error == SSL_ERROR_WANT_READ) { - throw SocketError(SocketError::WouldBlockRead, "recv", "Operation would block"); - } else if (error == SSL_ERROR_WANT_WRITE) { - throw SocketError(SocketError::WouldBlockWrite, "recv", "Operation would block"); - } else { - throw SocketError(SocketError::System, "recv", sslError(error)); - } - } - - return nbread; -} - -unsigned SocketSsl::waitRecv(void *data, unsigned len, int timeout) -{ - SocketListener listener{{*this, SocketListener::Read}}; - - listener.select(timeout); - - return recv(data, len); -} - -unsigned SocketSsl::send(const void *data, unsigned len) -{ - auto nbread = SSL_write(m_ssl.get(), data, len); - - if (nbread <= 0) { - auto error = SSL_get_error(m_ssl.get(), nbread); - - if (error == SSL_ERROR_WANT_READ) { - throw SocketError(SocketError::WouldBlockRead, "send", "Operation would block"); - } else if (error == SSL_ERROR_WANT_WRITE) { - throw SocketError(SocketError::WouldBlockWrite, "send", "Operation would block"); - } else { - throw SocketError(SocketError::System, "send", sslError(error)); - } - } - - return nbread; -} - -unsigned SocketSsl::waitSend(const void *data, unsigned len, int timeout) -{ - SocketListener listener{{*this, SocketListener::Write}}; - - listener.select(timeout); - - return send(data, len); -} -
--- a/C++/SocketSsl.h Sun Mar 08 11:07:36 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,215 +0,0 @@ -/* - * SocketSsl.h -- OpenSSL extension for sockets - * - * Copyright (c) 2013, David Demelier <markand@malikania.fr> - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#ifndef _SOCKET_SSL_NG_H_ -#define _SOCKET_SSL_NG_H_ - -#include <atomic> -#include <mutex> - -#include <openssl/err.h> -#include <openssl/evp.h> -#include <openssl/ssl.h> - -#include "SocketTcp.h" - -/** - * @class SocketSslOptions - * @brief Options for SocketSsl - */ -class SocketSslOptions { -public: - /** - * @brief Method - */ - enum { - SSLv3 = (1 << 0), - TLSv1 = (1 << 1), - All = (0xf) - }; - - int method{All}; //!< The method - std::string certificate; //!< The certificate path - std::string privateKey; //!< The private key file - bool verify{false}; //!< Verify or not - - /** - * Default constructor. - */ - SocketSslOptions() = default; - - /** - * More advanced constructor. - * - * @param method the method requested - * @param certificate the certificate file - * @param key the key file - * @param verify set to true to verify - */ - SocketSslOptions(int method, std::string certificate, std::string key, bool verify = false) - : method(method) - , certificate(std::move(certificate)) - , privateKey(std::move(key)) - , verify(verify) - { - } -}; - -/** - * @class SocketSsl - * @brief SSL interface for sockets - * - * This class derives from SocketAbstractTcp and provide SSL support through OpenSSL. - */ -class SocketSsl : public SocketAbstractTcp { -public: - using ContextHandle = std::unique_ptr<SSL_CTX, void (*)(SSL_CTX *)>; - using SslHandle = std::unique_ptr<SSL, void (*)(SSL *)>; - -private: - static std::mutex s_sslMutex; - static std::atomic<bool> s_sslInitialized; - - ContextHandle m_context{nullptr, nullptr}; - SslHandle m_ssl{nullptr, nullptr}; - SocketSslOptions m_options; - -public: - using SocketAbstractTcp::recv; - using SocketAbstractTcp::waitRecv; - using SocketAbstractTcp::send; - using SocketAbstractTcp::waitSend; - - /** - * Close OpenSSL library. - */ - static inline void sslTerminate() - { - ERR_free_strings(); - } - - /** - * Open SSL library. - */ - static inline void sslInitialize() - { - std::lock_guard<std::mutex> lock(s_sslMutex); - - if (!s_sslInitialized) { - s_sslInitialized = true; - - SSL_library_init(); - SSL_load_error_strings(); - - std::atexit(sslTerminate); - } - } - - /** - * Create a SocketSsl from an already created one. - * - * @param handle the native handle - * @param context the context - * @param ssl the ssl object - */ - SocketSsl(Socket::Handle handle, SSL_CTX *context, SSL *ssl); - - /** - * Open a SSL socket with the specified family. Automatically - * use SOCK_STREAM as the type. - * - * @param family the family - * @param options the options - */ - SocketSsl(int family, int protocol, SocketSslOptions options = {}); - - /** - * Accept a SSL TCP socket. - * - * @return the socket - * @throw SocketError on error - */ - SocketSsl accept(); - - /** - * Accept a SSL TCP socket. - * - * @param info the client information - * @return the socket - * @throw SocketError on error - */ - SocketSsl accept(SocketAddress &info); - - /** - * Accept a SSL TCP socket. - * - * @param timeout the maximum timeout in milliseconds - * @return the socket - * @throw SocketError on error - */ - SocketSsl waitAccept(int timeout); - - /** - * Accept a SSL TCP socket. - * - * @param info the client information - * @param timeout the maximum timeout in milliseconds - * @return the socket - * @throw SocketError on error - */ - SocketSsl waitAccept(SocketAddress &info, int timeout); - - /** - * Connect to an end point. - * - * @param address the address - * @throw SocketError on error - */ - void connect(const SocketAddress &address); - - /** - * Connect to an end point. - * - * @param timeout the maximum timeout in milliseconds - * @param address the address - * @throw SocketError on error - */ - void waitConnect(const SocketAddress &address, int timeout); - - /** - * @copydoc SocketAbstractTcp::recv - */ - unsigned recv(void *data, unsigned length) override; - - /** - * @copydoc SocketAbstractTcp::recv - */ - unsigned waitRecv(void *data, unsigned length, int timeout) override; - - /** - * @copydoc SocketAbstractTcp::recv - */ - unsigned send(const void *data, unsigned length) override; - - /** - * @copydoc SocketAbstractTcp::recv - */ - unsigned waitSend(const void *data, unsigned length, int timeout) override; -}; - -#endif // !_SOCKET_SSL_NG_H_
--- a/C++/SocketTcp.cpp Sun Mar 08 11:07:36 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,227 +0,0 @@ -/* - * SocketTcp.cpp -- portable C++ socket wrappers - * - * Copyright (c) 2013, 2014 David Demelier <markand@malikania.fr> - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include "SocketAddress.h" -#include "SocketListener.h" -#include "SocketTcp.h" - -/* -------------------------------------------------------- - * SocketAbstractTcp - * -------------------------------------------------------- */ - -void SocketAbstractTcp::listen(int max) -{ - if (::listen(m_handle, max) == Error) - throw SocketError(SocketError::System, "listen"); -} - -Socket SocketAbstractTcp::standardAccept(SocketAddress &info) -{ - Socket::Handle handle; - - // Store the information - sockaddr_storage address; - socklen_t addrlen; - - addrlen = sizeof (sockaddr_storage); - handle = ::accept(m_handle, reinterpret_cast<sockaddr *>(&address), &addrlen); - - if (handle == Invalid) { -#if defined(_WIN32) - int error = WSAGetLastError(); - - if (error == WSAEWOULDBLOCK) - throw SocketError(SocketError::WouldBlockRead, "accept", error); - - throw SocketError(SocketError::System, "accept", error); -#else - if (errno == EAGAIN || errno == EWOULDBLOCK) - throw SocketError(SocketError::WouldBlockRead, "accept"); - - throw SocketError(SocketError::System, "accept"); -#endif - } - - info = SocketAddress(address, addrlen); - - return Socket(handle); -} - -void SocketAbstractTcp::standardConnect(const SocketAddress &address) -{ - if (m_state == SocketState::Connected) - return; - - auto &sa = address.address(); - auto addrlen = address.length(); - - if (::connect(m_handle, reinterpret_cast<const sockaddr *>(&sa), addrlen) == Error) { - /* - * Determine if the error comes from a non-blocking connect that cannot be - * accomplished yet. - */ -#if defined(_WIN32) - int error = WSAGetLastError(); - - if (error == WSAEWOULDBLOCK) - throw SocketError(SocketError::WouldBlockWrite, "connect", error); - - throw SocketError(SocketError::System, "connect", error); -#else - if (errno == EINPROGRESS) - throw SocketError(SocketError::WouldBlockWrite, "connect"); - - throw SocketError(SocketError::System, "connect"); -#endif - } - - m_state = SocketState::Connected; -} - -/* -------------------------------------------------------- - * SocketTcp - * -------------------------------------------------------- */ - -SocketTcp SocketTcp::accept() -{ - SocketAddress dummy; - - return accept(dummy); -} - -SocketTcp SocketTcp::accept(SocketAddress &info) -{ - return standardAccept(info); -} - -void SocketTcp::connect(const SocketAddress &address) -{ - return standardConnect(address); -} - -void SocketTcp::waitConnect(const SocketAddress &address, int timeout) -{ - if (m_state == SocketState::Connected) - return; - - // Initial try - try { - connect(address); - } catch (const SocketError &ex) { - if (ex.code() == SocketError::WouldBlockWrite) { - SocketListener listener{{*this, SocketListener::Write}}; - - listener.select(timeout); - - // Socket is writable? Check if there is an error - - int error = get<int>(SOL_SOCKET, SO_ERROR); - - if (error) { - throw SocketError(SocketError::System, "connect", error); - } - } else { - throw; - } - } - - m_state = SocketState::Connected; -} - -SocketTcp SocketTcp::waitAccept(int timeout) -{ - SocketAddress dummy; - - return waitAccept(dummy, timeout); -} - -SocketTcp SocketTcp::waitAccept(SocketAddress &info, int timeout) -{ - SocketListener listener{{*this, SocketListener::Read}}; - - listener.select(timeout); - - return accept(info); -} - -unsigned SocketTcp::recv(void *data, unsigned dataLen) -{ - int nbread; - - nbread = ::recv(m_handle, (Socket::Arg)data, dataLen, 0); - if (nbread == Error) { -#if defined(_WIN32) - int error = WSAGetLastError(); - - if (error == WSAEWOULDBLOCK) - throw SocketError(SocketError::WouldBlockRead, "recv", error) - - throw SocketError(SocketError::System, "recv", error); -#else - if (errno == EAGAIN || errno == EWOULDBLOCK) - throw SocketError(SocketError::WouldBlockRead, "recv"); - - throw SocketError(SocketError::System, "recv"); -#endif - } else if (nbread == 0) - m_state = SocketState::Closed; - - return (unsigned)nbread; -} - -unsigned SocketTcp::waitRecv(void *data, unsigned length, int timeout) -{ - SocketListener listener{{*this, SocketListener::Read}}; - - listener.select(timeout); - - return recv(data, length); -} - -unsigned SocketTcp::send(const void *data, unsigned length) -{ - int nbsent; - - nbsent = ::send(m_handle, (Socket::ConstArg)data, length, 0); - if (nbsent == Error) { -#if defined(_WIN32) - int error = WSAGetLastError(); - - if (error == WSAEWOULDBLOCK) - throw SocketError(SocketError::WouldBlockWrite, "send", error); - - throw SocketError(SocketError::System, "send", error); -#else - if (errno == EAGAIN || errno == EWOULDBLOCK) - throw SocketError(SocketError::WouldBlockWrite, "send"); - - throw SocketError(SocketError::System, "send"); -#endif - } - - return (unsigned)nbsent; -} - -unsigned SocketTcp::waitSend(const void *data, unsigned length, int timeout) -{ - SocketListener listener{{*this, SocketListener::Write}}; - - listener.select(timeout); - - return send(data, length); -}
--- a/C++/SocketTcp.h Sun Mar 08 11:07:36 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,261 +0,0 @@ -/* - * SocketTcp.h -- portable C++ socket wrappers - * - * Copyright (c) 2013, 2014 David Demelier <markand@malikania.fr> - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#ifndef _SOCKET_TCP_NG_H_ -#define _SOCKET_TCP_NG_H_ - -#include "Socket.h" - -/** - * @class SocketAbstractTcp - * @brief Base class for TCP sockets - * - * This abstract class provides standard TCP functions for both clear - * and SSL implementation. - * - * It does not contain default accept() and connect() because they varies too - * much between standard and SSL. Also, the accept() function return different - * types. - */ -class SocketAbstractTcp : public Socket { -protected: - Socket standardAccept(SocketAddress &address); - void standardConnect(const SocketAddress &address); - -public: - /** - * Construct an abstract socket from an already made socket. - * - * @param s the socket - */ - inline SocketAbstractTcp(Socket s) - : Socket(s) - { - } - - /** - * Construct a standard TCP socket. The type is automatically - * set to SOCK_STREAM. - * - * @param domain the domain - * @param protocol the protocol - * @throw SocketError on error - */ - inline SocketAbstractTcp(int domain, int protocol) - : Socket(domain, SOCK_STREAM, protocol) - { - } - - /** - * Listen for pending connection. - * - * @param max the maximum number - */ - void listen(int max = 128); - - /** - * Overloaded function. - * - * @param count the number of bytes to receive - * @return the string - * @throw SocketError on error - */ - inline std::string recv(unsigned count) - { - std::string result; - - result.resize(count); - auto n = recv(const_cast<char *>(result.data()), count); - result.resize(n); - - return result; - } - - /** - * Overloaded function. - * - * @param count the number of bytes to receive - * @param timeout the maximum timeout in milliseconds - * @return the string - * @throw SocketError on error - */ - inline std::string waitRecv(unsigned count, int timeout) - { - std::string result; - - result.resize(count); - auto n = waitRecv(const_cast<char *>(result.data()), count, timeout); - result.resize(n); - - return result; - } - - /** - * Overloaded function. - * - * @param data the string to send - * @return the number of bytes sent - * @throw SocketError on error - */ - inline unsigned send(const std::string &data) - { - return send(data.c_str(), data.size()); - } - - /** - * Overloaded function. - * - * @param data the string to send - * @param timeout the maximum timeout in milliseconds - * @return the number of bytes sent - * @throw SocketError on error - */ - inline unsigned waitSend(const std::string &data, int timeout) - { - return waitSend(data.c_str(), data.size(), timeout); - } - - /** - * Receive data. - * - * @param data the destination buffer - * @param length the buffer length - * @return the number of bytes received - * @throw SocketError on error - */ - virtual unsigned recv(void *data, unsigned length) = 0; - - /** - * Receive data. - * - * @param data the destination buffer - * @param length the buffer length - * @param timeout the maximum timeout in milliseconds - * @return the number of bytes received - * @throw SocketError on error - */ - virtual unsigned waitRecv(void *data, unsigned length, int timeout) = 0; - - /** - * Send data. - * - * @param data the buffer - * @param length the buffer length - * @return the number of bytes sent - * @throw SocketError on error - */ - virtual unsigned send(const void *data, unsigned length) = 0; - - /** - * Send data. - * - * @param data the buffer - * @param length the buffer length - * @return the number of bytes sent - * @throw SocketError on error - */ - virtual unsigned waitSend(const void *data, unsigned length, int timeout) = 0; -}; - -/** - * @class SocketTcp - * @brief End-user class for TCP sockets - */ -class SocketTcp : public SocketAbstractTcp { -public: - using SocketAbstractTcp::SocketAbstractTcp; - using SocketAbstractTcp::recv; - using SocketAbstractTcp::waitRecv; - using SocketAbstractTcp::send; - using SocketAbstractTcp::waitSend; - - /** - * Accept a clear TCP socket. - * - * @return the socket - * @throw SocketError on error - */ - SocketTcp accept(); - - /** - * Accept a clear TCP socket. - * - * @param info the client information - * @return the socket - * @throw SocketError on error - */ - SocketTcp accept(SocketAddress &info); - - /** - * Accept a clear TCP socket. - * - * @param timeout the maximum timeout in milliseconds - * @return the socket - * @throw SocketError on error - */ - SocketTcp waitAccept(int timeout); - - /** - * Accept a clear TCP socket. - * - * @param info the client information - * @param timeout the maximum timeout in milliseconds - * @return the socket - * @throw SocketError on error - */ - SocketTcp waitAccept(SocketAddress &info, int timeout); - - /** - * Connect to an end point. - * - * @param address the address - * @throw SocketError on error - */ - void connect(const SocketAddress &address); - - /** - * Connect to an end point. - * - * @param timeout the maximum timeout in milliseconds - * @param address the address - * @throw SocketError on error - */ - void waitConnect(const SocketAddress &address, int timeout); - - /** - * @copydoc SocketAbstractTcp::recv - */ - unsigned recv(void *data, unsigned length) override; - - /** - * @copydoc SocketAbstractTcp::waitRecv - */ - unsigned waitRecv(void *data, unsigned length, int timeout) override; - - /** - * @copydoc SocketAbstractTcp::send - */ - unsigned send(const void *data, unsigned length) override; - - /** - * @copydoc SocketAbstractTcp::waitSend - */ - unsigned waitSend(const void *data, unsigned length, int timeout) override; -}; - -#endif // !_SOCKET_TCP_NG_H_
--- a/C++/SocketUdp.cpp Sun Mar 08 11:07:36 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,81 +0,0 @@ -/* - * SocketUdp.cpp -- portable C++ socket wrappers - * - * Copyright (c) 2013, 2014 David Demelier <markand@malikania.fr> - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include "SocketAddress.h" -#include "SocketUdp.h" - -SocketUdp::SocketUdp(int domain, int protocol) - : Socket(domain, SOCK_DGRAM, protocol) -{ -} - -unsigned SocketUdp::recvfrom(void *data, unsigned length, SocketAddress &info) -{ - int nbread; - - // Store information - sockaddr_storage address; - socklen_t addrlen; - - addrlen = sizeof (struct sockaddr_storage); - nbread = ::recvfrom(m_handle, (Socket::Arg)data, length, 0, (sockaddr *)&address, &addrlen); - - info = SocketAddress(address, addrlen); - - if (nbread == Error) { -#if defined(_WIN32) - int error = WSAGetLastError(); - - if (error == WSAEWOULDBLOCK) - throw SocketError(SocketError::WouldBlockRead, "recvfrom", error); - - throw SocketError(SocketError::System, "recvfrom", error); -#else - if (errno == EAGAIN || errno == EWOULDBLOCK) - throw SocketError(SocketError::WouldBlockRead, "recvfrom"); - - throw SocketError(SocketError::System, "recvfrom"); -#endif - } - - return (unsigned)nbread; -} - -unsigned SocketUdp::sendto(const void *data, unsigned length, const SocketAddress &info) -{ - int nbsent; - - nbsent = ::sendto(m_handle, (Socket::ConstArg)data, length, 0, (const sockaddr *)&info.address(), info.length()); - if (nbsent == Error) { -#if defined(_WIN32) - int error = WSAGetLastError(); - - if (error == WSAEWOULDBLOCK) - throw SocketError(SocketError::WouldBlockWrite, "sendto", error); - - throw SocketError(SocketError::System, "sendto", error); -#else - if (errno == EAGAIN || errno == EWOULDBLOCK) - throw SocketError(SocketError::WouldBlockWrite, "sendto"); - - throw SocketError(SocketError::System, "sendto"); -#endif - } - - return (unsigned)nbsent; -}
--- a/C++/SocketUdp.h Sun Mar 08 11:07:36 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,93 +0,0 @@ -/* - * SocketUdp.h -- portable C++ socket wrappers - * - * Copyright (c) 2013, 2014 David Demelier <markand@malikania.fr> - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#ifndef _SOCKET_UDP_NG_H_ -#define _SOCKET_UDP_NG_H_ - -#include "Socket.h" - -/** - * @class SocketUdp - * @brief UDP implementation for sockets - */ -class SocketUdp : public Socket { -public: - /** - * Construct a UDP socket. The type is automatically set to SOCK_DGRAM. - * - * @param domain the domain (e.g AF_INET) - * @param protocol the protocol (usually 0) - */ - SocketUdp(int domain, int protocol); - - /** - * Overloaded function. - * - * @param data the data - * @param address the address - * @return the number of bytes sent - * @throw SocketError on error - */ - inline unsigned sendto(const std::string &data, const SocketAddress &address) - { - return sendto(data.c_str(), data.length(), address); - } - - /** - * Overloaded function. - * - * @param data the data - * @param info the client information - * @return the string - * @throw SocketError on error - */ - inline std::string recvfrom(unsigned count, SocketAddress &info) - { - std::string result; - - result.resize(count); - auto n = recvfrom(const_cast<char *>(result.data()), count, info); - result.resize(n); - - return result; - } - - /** - * Receive data from an end point. - * - * @param data the destination buffer - * @param length the buffer length - * @param info the client information - * @return the number of bytes received - * @throw SocketError on error - */ - virtual unsigned recvfrom(void *data, unsigned length, SocketAddress &info); - - /** - * Send data to an end point. - * - * @param data the buffer - * @param length the buffer length - * @param address the client address - * @return the number of bytes sent - * @throw SocketError on error - */ - virtual unsigned sendto(const void *data, unsigned length, const SocketAddress &address); -}; - -#endif // !_SOCKET_UDP_NG_H_
--- a/C++/Tests/Base64/CMakeLists.txt Sun Mar 08 11:07:36 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,27 +0,0 @@ -# -# CMakeLists.txt -- tests for Base64 -# -# Copyright (c) 2013, 2014 David Demelier <markand@malikania.fr> -# -# Permission to use, copy, modify, and/or distribute this software for any -# purpose with or without fee is hereby granted, provided that the above -# copyright notice and this permission notice appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF -# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -# - -set( - SOURCES - ${code_SOURCE_DIR}/C++/Base64.cpp - ${code_SOURCE_DIR}/C++/Base64.h - main.cpp -) - -define_test(base64 "${SOURCES}") -
--- a/C++/Tests/Base64/main.cpp Sun Mar 08 11:07:36 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,184 +0,0 @@ -/* - * main.cpp -- main test file for Base64 - * - * Copyright (c) 2013, 2014 David Demelier <markand@malikania.fr> - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include <gtest/gtest.h> - -#include <Base64.h> - -TEST(Lookup, lookup) -{ - ASSERT_EQ('A', Base64::lookup(0b000000)); - ASSERT_EQ('B', Base64::lookup(0b000001)); - ASSERT_EQ('C', Base64::lookup(0b000010)); - ASSERT_EQ('D', Base64::lookup(0b000011)); - ASSERT_EQ('E', Base64::lookup(0b000100)); - ASSERT_EQ('F', Base64::lookup(0b000101)); - ASSERT_EQ('G', Base64::lookup(0b000110)); - ASSERT_EQ('H', Base64::lookup(0b000111)); - ASSERT_EQ('I', Base64::lookup(0b001000)); - ASSERT_EQ('J', Base64::lookup(0b001001)); - ASSERT_EQ('K', Base64::lookup(0b001010)); - ASSERT_EQ('L', Base64::lookup(0b001011)); - ASSERT_EQ('M', Base64::lookup(0b001100)); - ASSERT_EQ('N', Base64::lookup(0b001101)); - ASSERT_EQ('O', Base64::lookup(0b001110)); - ASSERT_EQ('P', Base64::lookup(0b001111)); - ASSERT_EQ('Q', Base64::lookup(0b010000)); - ASSERT_EQ('R', Base64::lookup(0b010001)); - ASSERT_EQ('S', Base64::lookup(0b010010)); - ASSERT_EQ('T', Base64::lookup(0b010011)); - ASSERT_EQ('U', Base64::lookup(0b010100)); - ASSERT_EQ('V', Base64::lookup(0b010101)); - ASSERT_EQ('W', Base64::lookup(0b010110)); - ASSERT_EQ('X', Base64::lookup(0b010111)); - ASSERT_EQ('Y', Base64::lookup(0b011000)); - ASSERT_EQ('Z', Base64::lookup(0b011001)); - ASSERT_EQ('a', Base64::lookup(0b011010)); - ASSERT_EQ('b', Base64::lookup(0b011011)); - ASSERT_EQ('c', Base64::lookup(0b011100)); - ASSERT_EQ('d', Base64::lookup(0b011101)); - ASSERT_EQ('e', Base64::lookup(0b011110)); - ASSERT_EQ('f', Base64::lookup(0b011111)); - ASSERT_EQ('g', Base64::lookup(0b100000)); - ASSERT_EQ('h', Base64::lookup(0b100001)); - ASSERT_EQ('i', Base64::lookup(0b100010)); - ASSERT_EQ('j', Base64::lookup(0b100011)); - ASSERT_EQ('k', Base64::lookup(0b100100)); - ASSERT_EQ('l', Base64::lookup(0b100101)); - ASSERT_EQ('m', Base64::lookup(0b100110)); - ASSERT_EQ('n', Base64::lookup(0b100111)); - ASSERT_EQ('o', Base64::lookup(0b101000)); - ASSERT_EQ('p', Base64::lookup(0b101001)); - ASSERT_EQ('q', Base64::lookup(0b101010)); - ASSERT_EQ('r', Base64::lookup(0b101011)); - ASSERT_EQ('s', Base64::lookup(0b101100)); - ASSERT_EQ('t', Base64::lookup(0b101101)); - ASSERT_EQ('u', Base64::lookup(0b101110)); - ASSERT_EQ('v', Base64::lookup(0b101111)); - ASSERT_EQ('w', Base64::lookup(0b110000)); - ASSERT_EQ('x', Base64::lookup(0b110001)); - ASSERT_EQ('y', Base64::lookup(0b110010)); - ASSERT_EQ('z', Base64::lookup(0b110011)); - ASSERT_EQ('0', Base64::lookup(0b110100)); - ASSERT_EQ('1', Base64::lookup(0b110101)); - ASSERT_EQ('2', Base64::lookup(0b110110)); - ASSERT_EQ('3', Base64::lookup(0b110111)); - ASSERT_EQ('4', Base64::lookup(0b111000)); - ASSERT_EQ('5', Base64::lookup(0b111001)); - ASSERT_EQ('6', Base64::lookup(0b111010)); - ASSERT_EQ('7', Base64::lookup(0b111011)); - ASSERT_EQ('8', Base64::lookup(0b111100)); - ASSERT_EQ('9', Base64::lookup(0b111101)); - ASSERT_EQ('+', Base64::lookup(0b111110)); - ASSERT_EQ('/', Base64::lookup(0b111111)); -} - -TEST(Lookup, rlookup) -{ - ASSERT_EQ(0b000000, Base64::rlookup('A')); - ASSERT_EQ(0b000001, Base64::rlookup('B')); - ASSERT_EQ(0b000010, Base64::rlookup('C')); - ASSERT_EQ(0b000011, Base64::rlookup('D')); - ASSERT_EQ(0b000100, Base64::rlookup('E')); - ASSERT_EQ(0b000101, Base64::rlookup('F')); - ASSERT_EQ(0b000110, Base64::rlookup('G')); - ASSERT_EQ(0b000111, Base64::rlookup('H')); - ASSERT_EQ(0b001000, Base64::rlookup('I')); - ASSERT_EQ(0b001001, Base64::rlookup('J')); - ASSERT_EQ(0b001010, Base64::rlookup('K')); - ASSERT_EQ(0b001011, Base64::rlookup('L')); - ASSERT_EQ(0b001100, Base64::rlookup('M')); - ASSERT_EQ(0b001101, Base64::rlookup('N')); - ASSERT_EQ(0b001110, Base64::rlookup('O')); - ASSERT_EQ(0b001111, Base64::rlookup('P')); - ASSERT_EQ(0b010000, Base64::rlookup('Q')); - ASSERT_EQ(0b010001, Base64::rlookup('R')); - ASSERT_EQ(0b010010, Base64::rlookup('S')); - ASSERT_EQ(0b010011, Base64::rlookup('T')); - ASSERT_EQ(0b010100, Base64::rlookup('U')); - ASSERT_EQ(0b010101, Base64::rlookup('V')); - ASSERT_EQ(0b010110, Base64::rlookup('W')); - ASSERT_EQ(0b010111, Base64::rlookup('X')); - ASSERT_EQ(0b011000, Base64::rlookup('Y')); - ASSERT_EQ(0b011001, Base64::rlookup('Z')); - ASSERT_EQ(0b011010, Base64::rlookup('a')); - ASSERT_EQ(0b011011, Base64::rlookup('b')); - ASSERT_EQ(0b011100, Base64::rlookup('c')); - ASSERT_EQ(0b011101, Base64::rlookup('d')); - ASSERT_EQ(0b011110, Base64::rlookup('e')); - ASSERT_EQ(0b011111, Base64::rlookup('f')); - ASSERT_EQ(0b100000, Base64::rlookup('g')); - ASSERT_EQ(0b100001, Base64::rlookup('h')); - ASSERT_EQ(0b100010, Base64::rlookup('i')); - ASSERT_EQ(0b100011, Base64::rlookup('j')); - ASSERT_EQ(0b100100, Base64::rlookup('k')); - ASSERT_EQ(0b100101, Base64::rlookup('l')); - ASSERT_EQ(0b100110, Base64::rlookup('m')); - ASSERT_EQ(0b100111, Base64::rlookup('n')); - ASSERT_EQ(0b101000, Base64::rlookup('o')); - ASSERT_EQ(0b101001, Base64::rlookup('p')); - ASSERT_EQ(0b101010, Base64::rlookup('q')); - ASSERT_EQ(0b101011, Base64::rlookup('r')); - ASSERT_EQ(0b101100, Base64::rlookup('s')); - ASSERT_EQ(0b101101, Base64::rlookup('t')); - ASSERT_EQ(0b101110, Base64::rlookup('u')); - ASSERT_EQ(0b101111, Base64::rlookup('v')); - ASSERT_EQ(0b110000, Base64::rlookup('w')); - ASSERT_EQ(0b110001, Base64::rlookup('x')); - ASSERT_EQ(0b110010, Base64::rlookup('y')); - ASSERT_EQ(0b110011, Base64::rlookup('z')); - ASSERT_EQ(0b110100, Base64::rlookup('0')); - ASSERT_EQ(0b110101, Base64::rlookup('1')); - ASSERT_EQ(0b110110, Base64::rlookup('2')); - ASSERT_EQ(0b110111, Base64::rlookup('3')); - ASSERT_EQ(0b111000, Base64::rlookup('4')); - ASSERT_EQ(0b111001, Base64::rlookup('5')); - ASSERT_EQ(0b111010, Base64::rlookup('6')); - ASSERT_EQ(0b111011, Base64::rlookup('7')); - ASSERT_EQ(0b111100, Base64::rlookup('8')); - ASSERT_EQ(0b111101, Base64::rlookup('9')); - ASSERT_EQ(0b111110, Base64::rlookup('+')); - ASSERT_EQ(0b111111, Base64::rlookup('/')); -} - -TEST(Encode, basic) -{ - ASSERT_EQ("YQ==", Base64::encode("a")); - ASSERT_EQ("YWI=", Base64::encode("ab")); - ASSERT_EQ("YWJj", Base64::encode("abc")); - - ASSERT_EQ("aGVsbG8=", Base64::encode("hello")); - ASSERT_EQ("dGhpcyBpcyBhIGxvbmcgc2VudGVuY2U=", Base64::encode("this is a long sentence")); -} - -TEST(Decode, basic) -{ - ASSERT_EQ("a", Base64::decode("YQ==")); - ASSERT_EQ("ab", Base64::decode("YWI=")); - ASSERT_EQ("abc", Base64::decode("YWJj")); - - ASSERT_EQ("hello", Base64::decode("aGVsbG8=")); - ASSERT_EQ("this is a long sentence", Base64::decode("dGhpcyBpcyBhIGxvbmcgc2VudGVuY2U=")); -} - -int main(int argc, char **argv) -{ - testing::InitGoogleTest(&argc, argv); - - return RUN_ALL_TESTS(); -}
--- a/C++/Tests/Directory/CMakeLists.txt Sun Mar 08 11:07:36 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,26 +0,0 @@ -# -# CMakeLists.txt -- tests for Directory -# -# Copyright (c) 2013, 2014 David Demelier <markand@malikania.fr> -# -# Permission to use, copy, modify, and/or distribute this software for any -# purpose with or without fee is hereby granted, provided that the above -# copyright notice and this permission notice appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF -# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -# - -set( - SOURCES - ${code_SOURCE_DIR}/C++/Directory.cpp - ${code_SOURCE_DIR}/C++/Directory.h - main.cpp -) - -define_test(directory "${SOURCES}")
--- a/C++/Tests/Directory/main.cpp Sun Mar 08 11:07:36 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,115 +0,0 @@ -/* - * main.cpp -- test directory - * - * Copyright (c) 2013, 2014 David Demelier <markand@malikania.fr> - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include <gtest/gtest.h> - -#include <Directory.h> - -TEST(Filter, withDot) -{ - try { - auto flags = Directory::NotDotDot; - Directory directory(".", flags); - bool dot(false); - bool dotdot(false); - - for (const auto &entry : directory) { - if (entry.name == ".") - dot = true; - if (entry.name == "..") - dotdot = true; - } - - ASSERT_TRUE(dot); - ASSERT_FALSE(dotdot); - } catch (const std::runtime_error &error) { - FAIL() << "skipping test because: " << error.what(); - } -} - -TEST(Filter, withDotDot) -{ - try { - auto flags = Directory::NotDot; - Directory directory(".", flags); - bool dot(false); - bool dotdot(false); - - for (const auto &entry : directory) { - if (entry.name == ".") - dot = true; - if (entry.name == "..") - dotdot = true; - } - - ASSERT_FALSE(dot); - ASSERT_TRUE(dotdot); - } catch (const std::runtime_error &error) { - FAIL() << "skipping test because: " << error.what(); - } -} - -TEST(Filter, withBothDots) -{ - try { - Directory directory("."); - bool dot(false); - bool dotdot(false); - - for (const auto &entry : directory) { - if (entry.name == ".") - dot = true; - if (entry.name == "..") - dotdot = true; - } - - ASSERT_TRUE(dot); - ASSERT_TRUE(dotdot); - } catch (const std::runtime_error &error) { - FAIL() << "skipping test because: " << error.what(); - } -} - -TEST(Filter, withoutDots) -{ - try { - auto flags = Directory::NotDot | Directory::NotDotDot; - Directory directory(".", flags); - bool dot(false); - bool dotdot(false); - - for (const auto &entry : directory) { - if (entry.name == ".") - dot = true; - if (entry.name == "..") - dotdot = true; - } - - ASSERT_FALSE(dot); - ASSERT_FALSE(dotdot); - } catch (const std::runtime_error &error) { - FAIL() << "skipping test because: " << error.what(); - } -} - -int main(int argc, char **argv) -{ - testing::InitGoogleTest(&argc, argv); - - return RUN_ALL_TESTS(); -} \ No newline at end of file
--- a/C++/Tests/DynLib/CMakeLists.txt Sun Mar 08 11:07:36 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,51 +0,0 @@ -# -# CMakeLists.txt -- tests for DynLib -# -# Copyright (c) 2013, 2014 David Demelier <markand@malikania.fr> -# -# Permission to use, copy, modify, and/or distribute this software for any -# purpose with or without fee is hereby granted, provided that the above -# copyright notice and this permission notice appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF -# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -# - -# Plugin module -add_library(Plugin MODULE Plugin.cpp) -set_target_properties( - Plugin - PROPERTIES - PREFIX "" -) - -set( - SOURCES - ${code_SOURCE_DIR}/C++/DynLib.cpp - ${code_SOURCE_DIR}/C++/DynLib.h - main.cpp -) - -define_test(dynlib "${SOURCES}") - -if (CMAKE_SYSTEM_NAME MATCHES "Linux") - target_link_libraries(dynlib dl) -endif () - -# The extension for the system -if(WIN32) - set(EXTENSION ".dll") -elseif(UNIX) - set(EXTENSION ".so") -elseif(APPLE) - set(EXTENSION ".dylib") -else() - message(FATAL_ERROR "Unsupported platform") -endif() - -target_compile_definitions(dynlib PRIVATE "EXTENSION=\"${EXTENSION}\"")
--- a/C++/Tests/DynLib/Plugin.cpp Sun Mar 08 11:07:36 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,12 +0,0 @@ -#include <string> - -#include <DynLib.h> - -extern "C" { - -void DYNLIB_EXPORT initialize(std::string &result) -{ - result = "Hello World"; -} - -}
--- a/C++/Tests/DynLib/main.cpp Sun Mar 08 11:07:36 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,68 +0,0 @@ -/* - * TestDynLib.cpp -- test the dynamic library loader - * - * Copyright (c) 2013, 2014 David Demelier <markand@malikania.fr> - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include <iostream> - -#include <gtest/gtest.h> - -#include <DynLib.h> - -/* - * NOTE: the EXTENSION is defined by CMake in the form of a string - * literal containing the appropriate extension for the given system. - */ - -using Initialize = void (*)(std::string &s); - -TEST(Basic, initialize) -{ - try { - DynLib library("./Plugin" EXTENSION); - Initialize init = library.sym<Initialize>("initialize"); - - std::string expected("Hello World"); - std::string result; - - init(result); - - ASSERT_EQ(expected, result); - } catch (const std::runtime_error &error) { - FAIL() << error.what(); - } catch (const std::out_of_range &error) { - FAIL() << error.what(); - } -} - -TEST(Basic, absent) -{ - try { - DynLib library("./Plugin" EXTENSION); - library.sym<Initialize>("initialize_typo"); - } catch (const std::out_of_range &error) { - return; - } - - FAIL() << "Expected a failure but succeed"; -} - -int main(int argc, char **argv) -{ - testing::InitGoogleTest(&argc, argv); - - return RUN_ALL_TESTS(); -} \ No newline at end of file
--- a/C++/Tests/Flags/CMakeLists.txt Sun Mar 08 11:07:36 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,25 +0,0 @@ -# -# CMakeLists.txt -- tests for Flags -# -# Copyright (c) 2013, 2014 David Demelier <markand@malikania.fr> -# -# Permission to use, copy, modify, and/or distribute this software for any -# purpose with or without fee is hereby granted, provided that the above -# copyright notice and this permission notice appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF -# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -# - -set( - SOURCES - ${code_SOURCE_DIR}/C++/Flags.h - main.cpp -) - -define_test(flags "${SOURCES}") \ No newline at end of file
--- a/C++/Tests/Flags/main.cpp Sun Mar 08 11:07:36 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,298 +0,0 @@ -/* - * main.cpp -- main test file for Flags - * - * Copyright (c) 2013, 2014 David Demelier <markand@malikania.fr> - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include <cstdint> - -#include <gtest/gtest.h> - -#include <Flags.h> - -enum Standard : uint8_t { - Fullscreen = (1 << 0), - Audio = (1 << 1) -}; - -enum class Strong : uint8_t { - NoLog = (1 << 0), - NoCheck = (1 << 1) -}; - -/* -------------------------------------------------------- - * Global operators on standard enum - * -------------------------------------------------------- */ - -TEST(OperatorsStandard, opand) -{ - Standard s(Fullscreen | Audio); - - ASSERT_EQ(Fullscreen, (s & Fullscreen)); - ASSERT_EQ(Audio, (s & Audio)); -} - -TEST(OperatorsStandard, opor) -{ - Standard s(Fullscreen); - - ASSERT_EQ(3, (s | Audio)); -} - -TEST(OperatorsStandard, opxor) -{ - Standard s(Fullscreen); - - ASSERT_EQ(3, (s ^ Audio)); -} - -TEST(OperatorsStandard, opnot) -{ - Standard s(Fullscreen); - - ASSERT_EQ(254, ~s); -} - -/* -------------------------------------------------------- - * Global operators on strong enum - * -------------------------------------------------------- */ - -TEST(OperatorsStrong, opand) -{ - Strong s(Strong::NoLog | Strong::NoCheck); - - ASSERT_EQ(Strong::NoLog, (s & Strong::NoLog)); - ASSERT_EQ(Strong::NoCheck, (s & Strong::NoCheck)); -} - -TEST(OperatorsStrong, opor) -{ - Strong s(Strong::NoLog); - - ASSERT_EQ(3, static_cast<int>((s | Strong::NoCheck))); -} - -TEST(OperatorsStrong, opxor) -{ - Strong s(Strong::NoLog); - - ASSERT_EQ(3, static_cast<int>((s ^ Strong::NoCheck))); -} - -TEST(OperatorsStrong, opnot) -{ - Strong s(Strong::NoLog); - - ASSERT_EQ(254, static_cast<int>(~s)); -} - -/* -------------------------------------------------------- - * Flags with standard enums - * -------------------------------------------------------- */ - -TEST(Standard, construct) -{ - Flags<Standard> s; - - ASSERT_FALSE(s); - ASSERT_TRUE(!s); - - Flags <Standard> s2(Fullscreen | Audio); - - ASSERT_TRUE(s2); - ASSERT_FALSE(!s2); - ASSERT_TRUE(s2 & Fullscreen); - ASSERT_TRUE(s2 & Audio); - ASSERT_TRUE((s2 & Fullscreen) == Fullscreen); - ASSERT_TRUE((s2 & Audio) == Audio); - ASSERT_TRUE(s2 == (Audio | Fullscreen)); -} - -TEST(Standard, addByOne) -{ - Flags<Standard> s; - - ASSERT_FALSE(s); - ASSERT_TRUE(!s); - ASSERT_TRUE(!(s & Fullscreen)); - ASSERT_TRUE(!(s & Audio)); - - s |= Fullscreen; - ASSERT_TRUE(s); - ASSERT_FALSE(!s); - ASSERT_TRUE(s & Fullscreen); - ASSERT_TRUE((s & Fullscreen) == Fullscreen); - ASSERT_FALSE(s & Audio); - ASSERT_FALSE((s & Audio) == Audio); - ASSERT_TRUE(s == Fullscreen); - - s |= Audio; - ASSERT_TRUE(s); - ASSERT_FALSE(!s); - ASSERT_TRUE(s & Fullscreen); - ASSERT_TRUE((s & Fullscreen) == Fullscreen); - ASSERT_TRUE(s & Audio); - ASSERT_TRUE((s & Audio) == Audio); - ASSERT_TRUE(s == (Fullscreen | Audio)); -} - -TEST(Standard, add) -{ - Flags<Standard> s; - - s |= Fullscreen | Audio; - ASSERT_TRUE(s & (Fullscreen | Audio)); - ASSERT_TRUE((s & (Fullscreen | Audio)) == (Fullscreen | Audio)); -} - -TEST(Standard, removeByOne) -{ - Flags<Standard> s(Fullscreen | Audio); - - s &= ~(Fullscreen); - ASSERT_TRUE(s); - ASSERT_FALSE(!s); - ASSERT_FALSE(s & Fullscreen); - ASSERT_FALSE((s & Fullscreen) == Fullscreen); - ASSERT_TRUE(s & Audio); - ASSERT_TRUE((s & Audio) == Audio); - - s &= ~(Audio); - ASSERT_FALSE(s); - ASSERT_TRUE(!s); -} - -TEST(Standard, remove) -{ - Flags<Standard> s(Fullscreen | Audio); - - s &= ~(Fullscreen | Audio); - ASSERT_FALSE(s); - ASSERT_TRUE(!s); -} - -TEST(Standard, xorAdd) -{ - Flags<Standard> s(Fullscreen | Audio); - - s ^= Audio; - ASSERT_TRUE(s & Fullscreen); - ASSERT_TRUE((s & Fullscreen) == Fullscreen); - ASSERT_FALSE(s & Audio); - ASSERT_FALSE((s & Audio) == Audio); -} - -/* -------------------------------------------------------- - * Flags with strong enums - * -------------------------------------------------------- */ - -TEST(Strong, construct) -{ - Flags<Strong> s; - - ASSERT_FALSE(s); - ASSERT_TRUE(!s); - - Flags <Strong> s2(Strong::NoLog | Strong::NoCheck); - - ASSERT_TRUE(s2); - ASSERT_FALSE(!s2); - ASSERT_TRUE(s2 & Strong::NoLog); - ASSERT_TRUE(s2 & Strong::NoCheck); - ASSERT_TRUE((s2 & Strong::NoLog) == Strong::NoLog); - ASSERT_TRUE((s2 & Strong::NoCheck) == Strong::NoCheck); - ASSERT_TRUE(s2 == (Strong::NoLog | Strong::NoCheck)); -} - -TEST(Strong, addByOne) -{ - Flags<Strong> s; - - ASSERT_FALSE(s); - ASSERT_TRUE(!s); - ASSERT_TRUE(!(s & Strong::NoLog)); - ASSERT_TRUE(!(s & Strong::NoCheck)); - - s |= Strong::NoLog; - ASSERT_TRUE(s); - ASSERT_FALSE(!s); - ASSERT_TRUE(s & Strong::NoLog); - ASSERT_TRUE((s & Strong::NoLog) == Strong::NoLog); - ASSERT_FALSE(s & Strong::NoCheck); - ASSERT_FALSE((s & Strong::NoCheck) == Strong::NoCheck); - ASSERT_TRUE(s == Strong::NoLog); - - s |= Strong::NoCheck; - ASSERT_TRUE(s); - ASSERT_FALSE(!s); - ASSERT_TRUE(s & Strong::NoLog); - ASSERT_TRUE((s & Strong::NoLog) == Strong::NoLog); - ASSERT_TRUE(s & Strong::NoCheck); - ASSERT_TRUE((s & Strong::NoCheck) == Strong::NoCheck); - ASSERT_TRUE(s == (Strong::NoLog | Strong::NoCheck)); -} - -TEST(Strong, add) -{ - Flags<Strong> s; - - s |= Strong::NoLog | Strong::NoCheck; - ASSERT_TRUE(s & (Strong::NoLog | Strong::NoCheck)); - ASSERT_TRUE((s & (Strong::NoLog | Strong::NoCheck)) == (Strong::NoLog | Strong::NoCheck)); -} - -TEST(Strong, removeByOne) -{ - Flags<Strong> s(Strong::NoLog | Strong::NoCheck); - - s &= ~(Strong::NoLog); - ASSERT_TRUE(s); - ASSERT_FALSE(!s); - ASSERT_FALSE(s & Strong::NoLog); - ASSERT_FALSE((s & Strong::NoLog) == Strong::NoLog); - ASSERT_TRUE(s & Strong::NoCheck); - ASSERT_TRUE((s & Strong::NoCheck) == Strong::NoCheck); - - s &= ~(Strong::NoCheck); - ASSERT_FALSE(s); - ASSERT_TRUE(!s); -} - -TEST(Strong, remove) -{ - Flags<Strong> s(Strong::NoLog | Strong::NoCheck); - - s &= ~(Strong::NoLog | Strong::NoCheck); - ASSERT_FALSE(s); - ASSERT_TRUE(!s); -} - -TEST(Strong, xorAdd) -{ - Flags<Strong> s(Strong::NoLog | Strong::NoCheck); - - s ^= Strong::NoCheck; - ASSERT_TRUE(s & Strong::NoLog); - ASSERT_TRUE((s & Strong::NoLog) == Strong::NoLog); - ASSERT_FALSE(s & Strong::NoCheck); - ASSERT_FALSE((s & Strong::NoCheck) == Strong::NoCheck); -} - -int main(int argc, char **argv) -{ - testing::InitGoogleTest(&argc, argv); - - return RUN_ALL_TESTS(); -} \ No newline at end of file
--- a/C++/Tests/Hash/CMakeLists.txt Sun Mar 08 11:07:36 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,31 +0,0 @@ -# -# CMakeLists.txt -- tests for Hash -# -# Copyright (c) 2013, 2014 David Demelier <markand@malikania.fr> -# -# Permission to use, copy, modify, and/or distribute this software for any -# purpose with or without fee is hereby granted, provided that the above -# copyright notice and this permission notice appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF -# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -# - -find_package(OpenSSL REQUIRED) - -set( - SOURCES - ${code_SOURCE_DIR}/C++/Hash.cpp - ${code_SOURCE_DIR}/C++/Hash.h - main.cpp -) - -define_test(hash "${SOURCES}") - -target_include_directories(hash PRIVATE ${OPENSSL_INCLUDE_DIR}) -target_link_libraries(hash ${OPENSSL_LIBRARIES})
--- a/C++/Tests/Hash/main.cpp Sun Mar 08 11:07:36 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,64 +0,0 @@ -/* - * main.cpp -- test the hash cryptographic functions - * - * Copyright (c) 2013, 2014 David Demelier <markand@malikania.fr> - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include <gtest/gtest.h> - -#include <Hash.h> - -/* - * We test the "Hello World" message in all cryptographic functions. - */ - -TEST(Hash, md5) -{ - std::string expected = "b10a8db164e0754105b7a99be72e3fe5"; - std::string output = Hash::md5("Hello World"); - - ASSERT_EQ(expected, output); -} - -TEST(Hash, sha1) -{ - std::string expected = "0a4d55a8d778e5022fab701977c5d840bbc486d0"; - std::string output = Hash::sha1("Hello World"); - - ASSERT_EQ(expected, output); -} - -TEST(Hash, sha256) -{ - std::string expected = "a591a6d40bf420404a011733cfb7b190d62c65bf0bcda32b57b277d9ad9f146e"; - std::string output = Hash::sha256("Hello World"); - - ASSERT_EQ(expected, output); -} - -TEST(Hash, sha512) -{ - std::string expected = "2c74fd17edafd80e8447b0d46741ee243b7eb74dd2149a0ab1b9246fb30382f27e853d8585719e0e67cbda0daa8f51671064615d645ae27acb15bfb1447f459b"; - std::string output = Hash::sha512("Hello World"); - - ASSERT_EQ(expected, output); -} - -int main(int argc, char **argv) -{ - testing::InitGoogleTest(&argc, argv); - - return RUN_ALL_TESTS(); -}
--- a/C++/Tests/Ini/CMakeLists.txt Sun Mar 08 11:07:36 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,42 +0,0 @@ -# -# CMakeLists.txt -- tests for Ini -# -# Copyright (c) 2013, 2014 David Demelier <markand@malikania.fr> -# -# Permission to use, copy, modify, and/or distribute this software for any -# purpose with or without fee is hereby granted, provided that the above -# copyright notice and this permission notice appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF -# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -# - -set( - SOURCES - ${code_SOURCE_DIR}/C++/Ini.cpp - ${code_SOURCE_DIR}/C++/Ini.h - main.cpp - configs/simple.conf - configs/multi.conf - configs/novalue.conf - configs/compact.conf - configs/includes.conf - configs/error-badcomment.conf - configs/error-lineassigment.conf - configs/error-nosection.conf -) - -define_test(ini "${SOURCES}") - -add_custom_command( - TARGET ini - POST_BUILD - COMMENT "Copying examples files" - COMMAND - ${CMAKE_COMMAND} -E copy_directory ${CMAKE_CURRENT_SOURCE_DIR}/configs $<TARGET_FILE_DIR:ini> -)
--- a/C++/Tests/Ini/configs/compact.conf Sun Mar 08 11:07:36 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1 +0,0 @@ -[general]verbose=true foreground=false[server]host=google.fr
--- a/C++/Tests/Ini/configs/error-badcomment.conf Sun Mar 08 11:07:36 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,2 +0,0 @@ -[general] -verbose #hello = xyz
--- a/C++/Tests/Ini/configs/error-badsection.conf Sun Mar 08 11:07:36 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,2 +0,0 @@ -[[general] -verbose = false
--- a/C++/Tests/Ini/configs/error-lineassigment.conf Sun Mar 08 11:07:36 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,4 +0,0 @@ -[general] -host -= -google.fr
--- a/C++/Tests/Ini/configs/error-nosection.conf Sun Mar 08 11:07:36 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1 +0,0 @@ -option = value
--- a/C++/Tests/Ini/configs/includes.conf Sun Mar 08 11:07:36 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,5 +0,0 @@ -# With some includes -@include "simple.conf" # comments also work here - -[standard] -verbose = false
--- a/C++/Tests/Ini/configs/multi.conf Sun Mar 08 11:07:36 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,7 +0,0 @@ -[entity] -name = "Player" -version = 1.0 - -[entity] -name = "Subwinner" -version = 2.0 \ No newline at end of file
--- a/C++/Tests/Ini/configs/novalue.conf Sun Mar 08 11:07:36 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,6 +0,0 @@ -[plugins] -histedit= -highlight= #empty -general = - -
--- a/C++/Tests/Ini/configs/simple.conf Sun Mar 08 11:07:36 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,4 +0,0 @@ -[general] -option1=1 -option2 =2 -option3 = 3
--- a/C++/Tests/Ini/configs/tokens.conf Sun Mar 08 11:07:36 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,3 +0,0 @@ -[tokens] -bracket = "I have [brackets]" -at = "I have foo@at"
--- a/C++/Tests/Ini/main.cpp Sun Mar 08 11:07:36 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,251 +0,0 @@ -/* - * main.cpp -- main test file for Ini - * - * Copyright (c) 2013, 2014 David Demelier <markand@malikania.fr> - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include <iostream> - -#include <gtest/gtest.h> - -#include <Ini.h> - -class BasicTest : public testing::Test { -protected: - Ini m_ini; - -public: - BasicTest() - : m_ini("simple.conf") - { - } - -}; - -TEST_F(BasicTest, simple) -{ - ASSERT_EQ(1, static_cast<int>(m_ini.size())); -} - -TEST_F(BasicTest, iniOperators) -{ - try { - ASSERT_EQ(3, static_cast<int>(m_ini[0].size())); - ASSERT_EQ("general", m_ini[0].key()); - ASSERT_EQ("general", m_ini["general"].key()); - } catch (const std::exception &ex) { - FAIL() << ex.what(); - } -} - -TEST_F(BasicTest, iniSectionOperators) -{ - try { - // option1=1 (indexes) - ASSERT_EQ("option1", m_ini[0][0].key()); - ASSERT_EQ("1", m_ini[0][0].value()); - - // option1=1 (keys) - ASSERT_EQ("option1", m_ini["general"]["option1"].key()); - ASSERT_EQ("1", m_ini["general"]["option1"].value()); - - // option2 =2 (indexes) - ASSERT_EQ("option2", m_ini[0][1].key()); - ASSERT_EQ("2", m_ini[0][1].value()); - - // option2 =2 (keys) - ASSERT_EQ("option2", m_ini["general"]["option2"].key()); - ASSERT_EQ("2", m_ini["general"]["option2"].value()); - - // option3 = 3 (indexes) - ASSERT_EQ("option3", m_ini[0][2].key()); - ASSERT_EQ("3", m_ini[0][2].value()); - - // option3 = 3 (keys) - ASSERT_EQ("option3", m_ini["general"]["option3"].key()); - ASSERT_EQ("3", m_ini["general"]["option3"].value()); - } catch (const std::exception &ex) { - FAIL() << ex.what(); - } -} - -/* -------------------------------------------------------- - * Reserved tokens in words - * -------------------------------------------------------- */ - -TEST(Tokens, iniReserved) -{ - try { - Ini ini("tokens.conf"); - - ASSERT_EQ("I have [brackets]", ini["tokens"]["bracket"].value()); - ASSERT_EQ("I have foo@at", ini["tokens"]["at"].value()); - } catch (const std::exception &ex) { - FAIL() << ex.what(); - } -} - -/* -------------------------------------------------------- - * Multiple definition - * -------------------------------------------------------- */ - -class MultiTest : public testing::Test { -protected: - Ini m_ini; - -public: - MultiTest() - : m_ini("multi.conf") - { - } -}; - -TEST_F(MultiTest, defined) -{ - ASSERT_EQ(2, static_cast<int>(m_ini.size())); - ASSERT_EQ("name", m_ini[0]["name"].key()); - ASSERT_EQ("Player", m_ini[0]["name"].value()); - ASSERT_EQ("version", m_ini[0]["version"].key()); - ASSERT_EQ("1.0", m_ini[0]["version"].value()); - ASSERT_EQ("name", m_ini[1]["name"].key()); - ASSERT_EQ("Subwinner", m_ini[1]["name"].value()); - ASSERT_EQ("version", m_ini[1]["version"].key()); - ASSERT_EQ("2.0", m_ini[1]["version"].value()); -} - -/* -------------------------------------------------------- - * Option with no values - * -------------------------------------------------------- */ - -class NoValueTest : public testing::Test { -protected: - Ini m_ini; - -public: - NoValueTest() - : m_ini("novalue.conf") - { - } -}; - -TEST_F(NoValueTest, isDefined) -{ - ASSERT_EQ("plugins", m_ini[0].key()); - ASSERT_EQ("", m_ini["plugins"]["histedit"].value()); - ASSERT_EQ("", m_ini["plugins"]["highlight"].value()); - ASSERT_EQ("", m_ini["plugins"]["general"].value()); -} - -/* -------------------------------------------------------- - * Include tests - * -------------------------------------------------------- */ - -class IncludeTest : public testing::Test { -protected: - Ini m_ini; - -public: - IncludeTest() - : m_ini("includes.conf") - { - } -}; - -TEST_F(IncludeTest, all) -{ - ASSERT_EQ(2, static_cast<int>(m_ini.size())); - - // from include - ASSERT_EQ("1", m_ini[0][0].value()); - ASSERT_EQ("2", m_ini[0][1].value()); - ASSERT_EQ("3", m_ini[0][2].value()); - - // from standard - ASSERT_EQ("false", m_ini[1][0].value()); -} - -/* -------------------------------------------------------- - * Compact - * -------------------------------------------------------- */ - -TEST(Compact, test) -{ - try { - Ini ini("compact.conf"); - - ASSERT_EQ(2, static_cast<int>(ini.size())); - ASSERT_EQ("true", ini["general"]["verbose"].value()); - ASSERT_EQ("false", ini["general"]["foreground"].value()); - ASSERT_EQ("google.fr", ini["server"]["host"].value()); - } catch (const std::exception &ex) { - FAIL() << ex.what(); - } -} - -/* -------------------------------------------------------- - * Errors - * -------------------------------------------------------- */ - -TEST(Errors, nosection) -{ - // An option outside a section is not allowed - try { - Ini ini("error-nosection.conf"); - - FAIL() << "Failure expected, got success"; - } catch (const std::exception &ex) { - } -} - -TEST(Errors, lineassigment) -{ - // The = assignment must be on the same line as the option key - try { - Ini ini("error-lineassigment.conf"); - - FAIL() << "Failure expected, got success"; - } catch (const std::exception &ex) { - } -} - -TEST(Errors, badcomment) -{ - // Comment can't between option-key and = assigment - try { - Ini ini("error-badcomment.conf"); - - FAIL() << "Failure expected, got success"; - } catch (const std::exception &ex) { - } -} - -TEST(Errors, badsection) -{ - // Bad section naming - try { - Ini ini("error-badsection.conf"); - - FAIL() << "Failure expected, got success"; - } catch (const std::exception &ex) { - std::cout << ex.what() << std::endl; - } -} - -int main(int argc, char **argv) -{ - testing::InitGoogleTest(&argc, argv); - - return RUN_ALL_TESTS(); -}
--- a/C++/Tests/Json/CMakeLists.txt Sun Mar 08 11:07:36 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,39 +0,0 @@ -# -# CMakeLists.txt -- tests for Json -# -# Copyright (c) 2013, 2014 David Demelier <markand@malikania.fr> -# -# Permission to use, copy, modify, and/or distribute this software for any -# purpose with or without fee is hereby granted, provided that the above -# copyright notice and this permission notice appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF -# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -# - -project(json) - -find_package(Jansson REQUIRED) - -set( - SOURCES - ${code_SOURCE_DIR}/C++/Json.cpp - ${code_SOURCE_DIR}/C++/Json.h - ${json_SOURCE_DIR}/data/array.json - ${json_SOURCE_DIR}/data/array-all.json - ${json_SOURCE_DIR}/data/object.json - ${json_SOURCE_DIR}/data/object-all.json - ${json_SOURCE_DIR}/data/simple.json - main.cpp -) - -define_test(json "${SOURCES}") - -target_include_directories(json PRIVATE ${Jansson_INCLUDE_DIRS}) -target_link_libraries(json ${Jansson_LIBRARIES}) -target_compile_definitions(json PRIVATE "BINARY=\"${json_BINARY_DIR}\"" "SOURCE=\"${json_SOURCE_DIR}\"")
--- a/C++/Tests/Json/data/array-all.json Sun Mar 08 11:07:36 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,9 +0,0 @@ -[ - 123, - 9.2, - false, - true, - null, - {}, - [] -]
--- a/C++/Tests/Json/data/array.json Sun Mar 08 11:07:36 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1 +0,0 @@ -[1, 2, 3]
--- a/C++/Tests/Json/data/object-all.json Sun Mar 08 11:07:36 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,9 +0,0 @@ -{ - "integer": 123, - "real": 9.2, - "false": false, - "true": true, - "null": null, - "object": {}, - "array": [] -}
--- a/C++/Tests/Json/data/object.json Sun Mar 08 11:07:36 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,4 +0,0 @@ -{ - "name": "simple", - "description": "basic JSON file" -}
--- a/C++/Tests/Json/data/simple.json Sun Mar 08 11:07:36 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,2 +0,0 @@ -{ -}
--- a/C++/Tests/Json/main.cpp Sun Mar 08 11:07:36 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,877 +0,0 @@ -/* - * main.cpp -- test the jansson wrapper - * - * Copyright (c) 2013, 2014 David Demelier <markand@malikania.fr> - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include <set> -#include <unordered_map> - -#include <gtest/gtest.h> - -#include "Json.h" - -/* -------------------------------------------------------- - * Miscellaneous - * -------------------------------------------------------- */ - -TEST(Misc, copy) -{ - JsonObject object; - - object.set("integer", 123); - object.set("true", true); - - JsonObject object2{object}; - - ASSERT_TRUE(object2.isObject()); - ASSERT_EQ(123, object2["integer"].toInteger()); - ASSERT_TRUE(object2["true"].isTrue()); -} - -TEST(Misc, copyAssign) -{ - JsonObject object; - - { - JsonObject tmp; - - tmp.set("integer", 123); - tmp.set("true", true); - - object = tmp; - } - - ASSERT_TRUE(object.isObject()); - ASSERT_EQ(123, object["integer"].toInteger()); - ASSERT_TRUE(object["true"].isTrue()); -} - -TEST(Misc, move) -{ - JsonObject object(123); - JsonObject object2(std::move(object)); - - ASSERT_TRUE(object.isNull()); - ASSERT_TRUE(object2.isInteger()); - ASSERT_EQ(123, object2.toInteger()); -} - -TEST(Misc, moveAssign) -{ - JsonObject object(123); - JsonObject object2; - - object2 = std::move(object); - - ASSERT_TRUE(object.isNull()); - ASSERT_TRUE(object2.isInteger()); - ASSERT_EQ(123, object2.toInteger()); -} - -/* -------------------------------------------------------- - * JsonValue constructors - * -------------------------------------------------------- */ - -TEST(Constructors, null) -{ - try { - JsonValue value; - - ASSERT_TRUE(value.isNull()); - } catch (const JsonError &ex) { - FAIL() << ex.what(); - } -} - -TEST(Constructors, boolean) -{ - try { - JsonValue value{true}; - - ASSERT_TRUE(value.isTrue()); - ASSERT_TRUE(value.isBoolean()); - ASSERT_FALSE(value.isFalse()); - } catch (const JsonError &ex) { - FAIL() << ex.what(); - } -} - -TEST(Constructors, integer) -{ - try { - JsonValue value{123}; - - ASSERT_TRUE(value.isInteger()); - ASSERT_TRUE(value.isNumber()); - ASSERT_FALSE(value.isReal()); - ASSERT_EQ(123, value.toInteger()); - } catch (const JsonError &ex) { - FAIL() << ex.what(); - } -} - -TEST(Constructors, real) -{ - try { - JsonValue value{9.2}; - - ASSERT_TRUE(value.isReal()); - ASSERT_TRUE(value.isNumber()); - ASSERT_FALSE(value.isInteger()); - ASSERT_EQ(9.2, value.toReal()); - } catch (const JsonError &ex) { - FAIL() << ex.what(); - } -} - -TEST(Constructors, string) -{ - try { - JsonValue value("hello"); - - ASSERT_TRUE(value.isString()); - ASSERT_EQ("hello", value.toString()); - } catch (const JsonError &ex) { - FAIL() << ex.what(); - } -} - -/* -------------------------------------------------------- - * Object - * -------------------------------------------------------- */ - -TEST(Object, set) -{ - try { - JsonObject object; - - object.set("integer", 123); - object.set("string", "hello"); - object.set("true", true); - - ASSERT_EQ(123, object["integer"].toInteger()); - ASSERT_EQ("hello", object["string"].toString()); - ASSERT_TRUE(object["true"].isTrue()); - } catch (const JsonError &ex) { - FAIL() << ex.what(); - } -} - -TEST(Object, clear) -{ - try { - JsonObject object; - - object.set("integer", 123); - object.set("string", "hello"); - object.set("true", true); - - object.clear(); - - ASSERT_EQ(0, static_cast<int>(object.size())); - ASSERT_FALSE(object.contains("integer")); - ASSERT_FALSE(object.contains("string")); - ASSERT_FALSE(object.contains("true")); - } catch (const JsonError &ex) { - FAIL() << ex.what(); - } -} - -TEST(Object, erase) -{ - try { - JsonObject object; - - object.set("integer", 123); - object.set("string", "hello"); - object.set("true", true); - - object.erase("integer"); - - ASSERT_EQ(2, static_cast<int>(object.size())); - ASSERT_FALSE(object.contains("integer")); - ASSERT_TRUE(object.contains("string")); - ASSERT_TRUE(object.contains("true")); - } catch (const JsonError &ex) { - FAIL() << ex.what(); - } -} - -TEST(ObjectInitializer, simple) -{ - try { - JsonObject object{ - { "username", "jean" }, - { "age", 99 } - }; - - ASSERT_EQ(2, static_cast<int>(object.size())); - ASSERT_EQ("jean", object["username"].toString()); - ASSERT_EQ(99, object["age"].toInteger()); - } catch (const std::exception &ex) { - FAIL() << ex.what(); - } -} - -TEST(ObjectInitializer, deep) -{ - try { - JsonObject object{ - { "username", "jean" }, - { "age", 99 }, - { "network", JsonObject{ - { "port", 9999 }, - { "host", "localhost" } - } - } - }; - - // First - ASSERT_EQ(3, static_cast<int>(object.size())); - ASSERT_EQ("jean", object["username"].toString()); - ASSERT_EQ(99, object["age"].toInteger()); - - // Second - JsonObject network = object["network"].toObject(); - ASSERT_TRUE(network.isObject()); - ASSERT_EQ(2, static_cast<int>(network.size())); - ASSERT_EQ(9999, network["port"].toInteger()); - ASSERT_EQ("localhost", network["host"].toString()); - } catch (const std::exception &ex) { - FAIL() << ex.what(); - } -} - -/* -------------------------------------------------------- - * Array - * -------------------------------------------------------- */ - -TEST(Array, push) -{ - try { - JsonArray array; - - ASSERT_TRUE(array.isArray()); - - array.push(1); - array.push("hello"); - array.push(true); - - ASSERT_EQ(3, static_cast<int>(array.size())); - ASSERT_TRUE(array[0].isTrue()); - ASSERT_EQ("hello", array[1].toString()); - ASSERT_EQ(1, array[2].toInteger()); - } catch (const JsonError &ex) { - FAIL() << ex.what(); - } -} - -TEST(Array, append) -{ - try { - JsonArray array; - - ASSERT_TRUE(array.isArray()); - - array.append(1); - array.append("hello"); - array.append(true); - - ASSERT_EQ(3, static_cast<int>(array.size())); - ASSERT_EQ(1, array[0].toInteger()); - ASSERT_EQ("hello", array[1].toString()); - ASSERT_TRUE(array[2].isTrue()); - } catch (const JsonError &ex) { - FAIL() << ex.what(); - } -} - -TEST(Array, insert) -{ - try { - JsonArray array; - - ASSERT_TRUE(array.isArray()); - - array.insert(1, 0); - array.insert("hello", 1); - array.insert(true, 0); - - ASSERT_EQ(3, static_cast<int>(array.size())); - ASSERT_TRUE(array[0].isTrue()); - ASSERT_EQ(1, array[1].toInteger()); - ASSERT_EQ("hello", array[2].toString()); - } catch (const JsonError &ex) { - FAIL() << ex.what(); - } -} - -TEST(Array, clear) -{ - try { - JsonArray array; - - array.append(1); - array.append("hello"); - array.append(true); - - array.clear(); - - ASSERT_EQ(0, static_cast<int>(array.size())); - } catch (const JsonError &ex) { - FAIL() << ex.what(); - } -} - -TEST(Array, erase) -{ - try { - JsonArray array; - - array.append(1); - array.append("hello"); - array.append(true); - - array.erase(0); - - ASSERT_EQ(2, static_cast<int>(array.size())); - ASSERT_EQ("hello", array[0].toString()); - ASSERT_TRUE(array[1].isTrue()); - } catch (const JsonError &ex) { - FAIL() << ex.what(); - } -} - -TEST(Array, eraseIterator) -{ - try { - JsonArray array; - - array.append(1); - array.append("hello"); - array.append(true); - - array.erase(array.begin()); - - ASSERT_EQ(2, static_cast<int>(array.size())); - ASSERT_EQ("hello", array[0].toString()); - ASSERT_TRUE(array[1].isTrue()); - } catch (const JsonError &ex) { - FAIL() << ex.what(); - } -} - -TEST(Array, eraseConstIterator) -{ - try { - JsonArray array; - - array.append(1); - array.append("hello"); - array.append(true); - - array.erase(array.cbegin()); - - ASSERT_EQ(2, static_cast<int>(array.size())); - ASSERT_EQ("hello", array[0].toString()); - ASSERT_TRUE(array[1].isTrue()); - } catch (const JsonError &ex) { - FAIL() << ex.what(); - } -} - -TEST(ArrayInitializer, simple) -{ - try { - JsonArray array{123, true, "hello"}; - - ASSERT_EQ(3, static_cast<int>(array.size())); - ASSERT_EQ(123, array[0].toInteger()); - ASSERT_TRUE(array[1].isTrue()); - ASSERT_EQ("hello", array[2].toString()); - } catch (const JsonError &ex) { - FAIL() << ex.what(); - } -} - -TEST(ArrayInitializer, deep) -{ - try { - JsonArray array{ - 123, - true, - "hello", - JsonArray{ - 321, - false, - "olleh" - } - }; - - // First - ASSERT_EQ(4, static_cast<int>(array.size())); - ASSERT_EQ(123, array[0].toInteger()); - ASSERT_TRUE(array[1].isTrue()); - ASSERT_EQ("hello", array[2].toString()); - - // Second - JsonArray array2 = array[3].toArray(); - ASSERT_TRUE(array.isArray()); - ASSERT_EQ(3, static_cast<int>(array2.size())); - ASSERT_EQ(321, array2[0].toInteger()); - ASSERT_TRUE(array2[1].isFalse()); - ASSERT_EQ("olleh", array2[2].toString()); - } catch (const JsonError &ex) { - FAIL() << ex.what(); - } -} - -/* -------------------------------------------------------- - * I/O - * -------------------------------------------------------- */ - -TEST(FileRead, simple) -{ - try { - JsonDocument doc(std::ifstream(SOURCE "/data/simple.json")); - - ASSERT_TRUE(doc.isObject()); - ASSERT_FALSE(doc.isArray()); - - JsonObject object = doc.toObject(); - JsonArray array = doc.toArray(); - - ASSERT_TRUE(object.isObject()); - ASSERT_FALSE(array.isArray()); - } catch (const JsonError &ex) { - FAIL() << ex.what(); - } -} - -TEST(FileRead, fail) -{ - try { - JsonDocument(std::ifstream(SOURCE "/data/notexist.json")); - - FAIL() << "Exception expected"; - } catch (const JsonError &) { - } -} - -TEST(FileWrite, simple) -{ - try { - JsonObject object{ - { "name", "jean" }, - { "age", 99 } - }; - - object.write(std::ofstream(BINARY "/object-write.json")); - - JsonObject object2 = JsonDocument(std::ifstream(BINARY "/object-write.json")).toObject(); - - ASSERT_EQ(object2, object); - } catch (const JsonError &ex) { - FAIL() << ex.what(); - } -} - -TEST(StringRead, simple) -{ - try { - JsonDocument doc("{ \"license\": \"ISC\" }"); - - ASSERT_TRUE(doc.isObject()); - ASSERT_FALSE(doc.isArray()); - - JsonObject object = doc.toObject(); - JsonArray array = doc.toArray(); - - ASSERT_TRUE(object.isObject()); - ASSERT_FALSE(array.isArray()); - } catch (const JsonError &ex) { - FAIL() << ex.what(); - } -} - -TEST(StringRead, fail) -{ - try { - JsonDocument("{ \"license\": ISC }"); - - FAIL() << "Exception expected"; - } catch (const JsonError &ex) { - } -} - -TEST(StringWrite, simple) -{ - try { - JsonObject object{ - { "name", "jean" }, - { "age", 99 } - }; - - JsonObject object2 = JsonDocument(object.dump()).toObject(); - - ASSERT_EQ(object2, object); - } catch (const JsonError &ex) { - FAIL() << ex.what(); - } -} - -/* -------------------------------------------------------- - * Object read - * -------------------------------------------------------- */ - -class ObjectRead : public testing::Test { -protected: - JsonObject m_object; - JsonObject m_objectAll; - -public: - ObjectRead() - { - m_object = JsonDocument(std::ifstream(SOURCE "/data/object.json")).toObject(); - m_objectAll = JsonDocument(std::ifstream(SOURCE "/data/object-all.json")).toObject(); - } -}; - -TEST_F(ObjectRead, simple) -{ - try { - JsonValue name = m_object["name"]; - JsonValue description = m_object["description"]; - - ASSERT_TRUE(name.isString()); - ASSERT_TRUE(description.isString()); - ASSERT_EQ("simple", name.toString()); - ASSERT_EQ("basic JSON file", description.toString()); - } catch (const JsonError &ex) { - FAIL() << ex.what(); - } -} - -TEST_F(ObjectRead, all) -{ - try { - ASSERT_TRUE(m_objectAll.contains("integer")); - ASSERT_FALSE(m_objectAll.contains("unexistant")); - - ASSERT_TRUE(m_objectAll["integer"].isInteger()); - ASSERT_TRUE(m_objectAll["integer"].isNumber()); - ASSERT_EQ(123, m_objectAll["integer"].toInteger()); - - ASSERT_TRUE(m_objectAll["real"].isReal()); - ASSERT_TRUE(m_objectAll["real"].isNumber()); - ASSERT_EQ(9.2, m_objectAll["real"].toReal()); - - ASSERT_TRUE(m_objectAll["false"].isBoolean()); - ASSERT_TRUE(m_objectAll["false"].isFalse()); - ASSERT_FALSE(m_objectAll["false"].isTrue()); - - ASSERT_TRUE(m_objectAll["true"].isBoolean()); - ASSERT_TRUE(m_objectAll["true"].isTrue()); - ASSERT_FALSE(m_objectAll["true"].isFalse()); - - ASSERT_TRUE(m_objectAll["null"].isNull()); - - ASSERT_TRUE(m_objectAll["object"].isObject()); - ASSERT_TRUE(m_objectAll["array"].isArray()); - } catch (const JsonError &ex) { - FAIL() << ex.what(); - } -} - -/* -------------------------------------------------------- - * Array read - * -------------------------------------------------------- */ - -class ArrayRead : public testing::Test { -protected: - JsonArray m_array; - JsonArray m_arrayAll; - -public: - ArrayRead() - { - m_array = JsonDocument(std::ifstream(SOURCE "/data/array.json")).toArray(); - m_arrayAll = JsonDocument(std::ifstream(SOURCE "/data/array-all.json")).toArray(); - } -}; - -TEST_F(ArrayRead, simple) -{ - try { - ASSERT_EQ(3, static_cast<int>(m_array.size())); - ASSERT_EQ(1, m_array[0].toInteger()); - ASSERT_EQ(2, m_array[1].toInteger()); - ASSERT_EQ(3, m_array[2].toInteger()); - } catch (const JsonError &ex) { - FAIL() << ex.what(); - } -} - -TEST_F(ArrayRead, all) -{ - try { - ASSERT_TRUE(m_arrayAll[0].isInteger()); - ASSERT_TRUE(m_arrayAll[0].isNumber()); - ASSERT_EQ(123, m_arrayAll[0].toInteger()); - - ASSERT_TRUE(m_arrayAll[1].isReal()); - ASSERT_TRUE(m_arrayAll[1].isNumber()); - ASSERT_EQ(9.2, m_arrayAll[1].toReal()); - - ASSERT_TRUE(m_arrayAll[2].isBoolean()); - ASSERT_TRUE(m_arrayAll[2].isFalse()); - ASSERT_FALSE(m_arrayAll[2].isTrue()); - - ASSERT_TRUE(m_arrayAll[3].isBoolean()); - ASSERT_TRUE(m_arrayAll[3].isTrue()); - ASSERT_FALSE(m_arrayAll[3].isFalse()); - - ASSERT_TRUE(m_arrayAll[4].isNull()); - - ASSERT_TRUE(m_arrayAll[5].isObject()); - ASSERT_TRUE(m_arrayAll[6].isArray()); - } catch (const JsonError &ex) { - FAIL() << ex.what(); - } -} - -/* -------------------------------------------------------- - * Object iterators - * -------------------------------------------------------- */ - -class ObjectIteratorsTest : public testing::Test { -protected: - JsonObject m_object; - -public: - ObjectIteratorsTest() - { - m_object.set("integer", 1); - m_object.set("string", "hello"); - m_object.set("boolean", true); - } -}; - -TEST_F(ObjectIteratorsTest, operators) -{ - // Read only (non const) - { - std::set<std::string> expected{"boolean", "integer", "string"}; - std::set<std::string> result; - std::unordered_map<std::string, JsonValue> values; - JsonObject::iterator it = m_object.begin(); - JsonObject::iterator end = m_object.end(); - - while (it != end) { - values.insert({it->first, it->second}); - result.insert((*it++).first); - } - - ASSERT_EQ(expected, result); - ASSERT_EQ(1, values["integer"].toInteger()); - ASSERT_EQ("hello", values["string"].toString()); - ASSERT_TRUE(values["boolean"].isTrue()); - } - - // Read only (const) - { - std::set<std::string> expected{"boolean", "integer", "string"}; - std::set<std::string> result; - std::unordered_map<std::string, JsonValue> values; - JsonObject::const_iterator it = m_object.cbegin(); - JsonObject::const_iterator end = m_object.cend(); - - while (it != end) { - values.insert({it->first, it->second}); - result.insert((*it++).first); - } - - ASSERT_EQ(expected, result); - ASSERT_EQ(1, values["integer"].toInteger()); - ASSERT_EQ("hello", values["string"].toString()); - ASSERT_TRUE(values["boolean"].isTrue()); - } -} - -TEST_F(ObjectIteratorsTest, assign) -{ - // Assign (non const) - { - JsonObject::iterator it = m_object.begin(); - std::string key = it->first; - - it->second = JsonValue("CHANGED"); - - ASSERT_EQ("CHANGED", m_object[key].toString()); - ASSERT_EQ("CHANGED", it->second.toString()); - ASSERT_EQ(3, static_cast<int>(m_object.size())); - } -} - -TEST_F(ObjectIteratorsTest, assignConst) -{ - // Assign (const) - { - JsonObject::const_iterator it = m_object.cbegin(); - std::string key = it->first; - JsonValue orig = it->second; - - it->second = JsonValue("CHANGED"); - - ASSERT_TRUE(m_object.contains(key)); - ASSERT_EQ(orig, m_object[key]); - ASSERT_EQ(3, static_cast<int>(m_object.size())); - } -} - -/* -------------------------------------------------------- - * Array iterators - * -------------------------------------------------------- */ - -class ArrayIteratorsTest : public testing::Test { -protected: - JsonArray m_array; - -public: - ArrayIteratorsTest() - { - m_array.append(1); - m_array.append("hello"); - m_array.append(true); - } -}; - -TEST_F(ArrayIteratorsTest, operators) -{ - // Read only (non const) - { - JsonArray::iterator it = m_array.begin(); - - ASSERT_EQ(1, (*it).toInteger()); - ASSERT_EQ(1, it->toInteger()); - ASSERT_EQ("hello", it[1].toString()); - ASSERT_TRUE(it[2].isTrue()); - - JsonArray::iterator it2 = it + 1; - ASSERT_EQ(1, it2[-1].toInteger()); - ASSERT_EQ("hello", it2->toString()); - ASSERT_TRUE(it2[1].isTrue()); - - JsonArray::iterator it3 = it; - ASSERT_TRUE(it2 != it); - ASSERT_FALSE(it3 != it); - - ASSERT_FALSE(it2 == it); - ASSERT_TRUE(it3 == it); - - ASSERT_TRUE(it3 >= it); - ASSERT_TRUE(it2 >= it); - - ASSERT_TRUE(it2 > it); - ASSERT_FALSE(it3 > it); - - ASSERT_FALSE(it2 <= it); - ASSERT_TRUE(it3 <= it); - - ASSERT_FALSE(it2 < it); - ASSERT_FALSE(it3 < it); - } - - // Read only (const) - { - JsonArray::const_iterator it = m_array.cbegin(); - - ASSERT_EQ(1, (*it).toInteger()); - ASSERT_EQ(1, it->toInteger()); - ASSERT_EQ("hello", it[1].toString()); - ASSERT_TRUE(it[2].isTrue()); - - JsonArray::const_iterator it2 = it + 1; - ASSERT_EQ(1, it2[-1].toInteger()); - ASSERT_EQ("hello", it2->toString()); - ASSERT_TRUE(it2[1].isTrue()); - - JsonArray::const_iterator it3 = it; - ASSERT_TRUE(it2 != it); - ASSERT_FALSE(it3 != it); - - ASSERT_FALSE(it2 == it); - ASSERT_TRUE(it3 == it); - - ASSERT_TRUE(it3 >= it); - ASSERT_TRUE(it2 >= it); - - ASSERT_TRUE(it2 > it); - ASSERT_FALSE(it3 > it); - - ASSERT_FALSE(it2 <= it); - ASSERT_TRUE(it3 <= it); - - ASSERT_FALSE(it2 < it); - ASSERT_FALSE(it3 < it); - } -} - -TEST_F(ArrayIteratorsTest, assign) -{ - // Assign (non const) - { - JsonArray::iterator it = m_array.begin(); - - *it = JsonValue(9999); - - ASSERT_EQ(3, static_cast<int>(m_array.size())); - ASSERT_EQ(9999, it->toInteger()); - ASSERT_EQ(9999, m_array[0].toInteger()); - } -} - -TEST_F(ArrayIteratorsTest, assignConst) -{ - // Assign (const) - { - JsonArray::const_iterator it = m_array.cbegin(); - - *it = JsonValue(9999); - - ASSERT_EQ(3, static_cast<int>(m_array.size())); - ASSERT_EQ(1, it->toInteger()); - ASSERT_EQ(1, m_array[0].toInteger()); - } -} - -TEST_F(ArrayIteratorsTest, castToRef) -{ - JsonArray array{1, 2, 3}; - int i = 1; - - for (const JsonValue &v : array) - { - ASSERT_EQ(i++, v.toInteger()); - } -} - -int main(int argc, char **argv) -{ - testing::InitGoogleTest(&argc, argv); - - return RUN_ALL_TESTS(); -}
--- a/C++/Tests/OptionParser/CMakeLists.txt Sun Mar 08 11:07:36 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,26 +0,0 @@ -# -# CMakeLists.txt -- tests for OptionParser -# -# Copyright (c) 2013, 2014 David Demelier <markand@malikania.fr> -# -# Permission to use, copy, modify, and/or distribute this software for any -# purpose with or without fee is hereby granted, provided that the above -# copyright notice and this permission notice appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF -# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -# - -set( - SOURCES - ${code_SOURCE_DIR}/C++/OptionParser.cpp - ${code_SOURCE_DIR}/C++/OptionParser.h - main.cpp -) - -define_test(optionparser "${SOURCES}")
--- a/C++/Tests/OptionParser/main.cpp Sun Mar 08 11:07:36 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,229 +0,0 @@ -/* - * main.cpp -- main test file for OptionParser - * - * Copyright (c) 2013, 2014 David Demelier <markand@malikania.fr> - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include <gtest/gtest.h> - -#include <OptionParser.h> - -/* -------------------------------------------------------- - * Short options - * -------------------------------------------------------- */ - -TEST(Short, simpleNoArg) -{ - OptionParser parser{ - { "a", "", Option::NoArg }, - { "b", "", Option::NoArg } - }; - - OptionPack pack = parser.parse({ "-a", "-b" }); - - ASSERT_TRUE(pack); - - ASSERT_EQ(2, static_cast<int>(pack.size())); - ASSERT_EQ(2, pack.parsed()); - - ASSERT_TRUE(pack[0] == "a"); - ASSERT_TRUE(pack[1] == "b"); -} - -TEST(Short, simpleArg) -{ - OptionParser parser{ - { "v", "", Option::NoArg }, - { "c", "", } - }; - - OptionPack pack = parser.parse({ "-v", "-cfoo.conf" }); - - ASSERT_TRUE(pack); - - ASSERT_EQ(2, static_cast<int>(pack.size())); - ASSERT_EQ(2, pack.parsed()); - - ASSERT_TRUE(pack[0] == "v"); - ASSERT_TRUE(pack[1] == "c"); - ASSERT_EQ("foo.conf", pack[1].value()); -} - -TEST(Short, spacedArg) -{ - OptionParser parser{ - { "v", "", Option::NoArg }, - { "c", "", } - }; - - OptionPack pack = parser.parse({ "-v", "-c", "foo.conf" }); - - ASSERT_TRUE(pack); - - ASSERT_EQ(2, static_cast<int>(pack.size())); - ASSERT_EQ(3, pack.parsed()); - - ASSERT_TRUE(pack[0] == "v"); - ASSERT_TRUE(pack[1] == "c"); - ASSERT_EQ("foo.conf", pack[1].value()); -} - -TEST(Short, compacted) -{ - OptionParser parser{ - { "a", "", Option::NoArg }, - { "b", "", Option::NoArg }, - { "c", "", Option::NoArg }, - }; - - OptionPack pack = parser.parse({ "-abc" }); - - ASSERT_TRUE(pack); - - ASSERT_EQ(3, static_cast<int>(pack.size())); - ASSERT_EQ(1, pack.parsed()); - - ASSERT_TRUE(pack[0] == "a"); - ASSERT_TRUE(pack[1] == "b"); - ASSERT_TRUE(pack[2] == "c"); -} - -TEST(Short, compactedArg) -{ - OptionParser parser{ - { "a", "", }, - { "b", "", }, - { "c", "", }, - }; - - OptionPack pack = parser.parse({ "-abc" }); - - ASSERT_TRUE(pack); - - ASSERT_EQ(1, static_cast<int>(pack.size())); - ASSERT_EQ(1, pack.parsed()); - - ASSERT_TRUE(pack[0] == "a"); -} - -/* -------------------------------------------------------- - * Long options - * -------------------------------------------------------- */ - -TEST(Long, simple) -{ - OptionParser parser{ - { "", "verbose", Option::NoArg }, - { "", "fullscreen", Option::NoArg } - }; - - OptionPack pack = parser.parse({ "--fullscreen" }); - - ASSERT_TRUE(pack); - - ASSERT_EQ(1, static_cast<int>(pack.size())); - ASSERT_EQ(1, pack.parsed()); - - ASSERT_TRUE(pack[0] == "fullscreen"); -} - -TEST(Long, simpleArg) -{ - OptionParser parser{ - { "", "config", }, - { "", "level", } - }; - - OptionPack pack = parser.parse({ "--config", "config.conf", "--level", "2" }); - - ASSERT_TRUE(pack); - - ASSERT_EQ(2, static_cast<int>(pack.size())); - ASSERT_EQ(4, pack.parsed()); - - ASSERT_TRUE(pack[0] == "config"); - ASSERT_EQ("config.conf", pack[0].value()); - ASSERT_TRUE(pack[1] == "level"); - ASSERT_EQ("2", pack[1].value()); -} - -/* -------------------------------------------------------- - * Combined - * -------------------------------------------------------- */ - -TEST(Combined, simple) -{ - OptionParser parser{ - { "v", "verbose", Option::NoArg }, - { "l", "level", Option::NoArg } - }; - - OptionPack pack = parser.parse({ "-v", "-l", "--verbose", "--level" }); - - ASSERT_TRUE(pack); - - ASSERT_TRUE(pack[0] == "v" && pack[0] == "verbose"); - ASSERT_TRUE(pack[1] == "l" && pack[1] == "level"); - ASSERT_TRUE(pack[2] == "v" && pack[2] == "verbose"); - ASSERT_TRUE(pack[3] == "l" && pack[3] == "level"); -} - -/* -------------------------------------------------------- - * Flags - * -------------------------------------------------------- */ - -TEST(Flags, standard) -{ - // No flags, parse unless there is an argument which is not an option - OptionParser parser{ - { "v", "", Option::NoArg } - }; - - OptionPack pack = parser.parse({ "-v", "install", "malikania" }); - - ASSERT_FALSE(pack); - - ASSERT_EQ(1, static_cast<int>(pack.size())); - ASSERT_EQ(1, pack.parsed()); - - ASSERT_TRUE(pack[0] == "v"); -} - -TEST(Flags, unstrict) -{ - // No flags, parse unless there is an argument which is not an option - OptionParser parser{ - { "v", "", Option::NoArg }, - { "d", "", } - }; - - OptionPack pack = parser.parse({ "-v", "install", "malikania", "-d", "/usr/local" }, OptionParser::Unstrict); - - ASSERT_TRUE(pack); - - ASSERT_EQ(2, static_cast<int>(pack.size())); - ASSERT_EQ(5, pack.parsed()); - - ASSERT_TRUE(pack[0] == "v"); - ASSERT_TRUE(pack[1] == "d"); - ASSERT_EQ("/usr/local", pack[1].value()); -} - -int main(int argc, char **argv) -{ - testing::InitGoogleTest(&argc, argv); - - return RUN_ALL_TESTS(); -}
--- a/C++/Tests/Pack/CMakeLists.txt Sun Mar 08 11:07:36 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,26 +0,0 @@ -# -# CMakeLists.txt -- tests for Pack -# -# Copyright (c) 2013, 2014 David Demelier <markand@malikania.fr> -# -# Permission to use, copy, modify, and/or distribute this software for any -# purpose with or without fee is hereby granted, provided that the above -# copyright notice and this permission notice appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF -# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -# - -set( - SOURCES - ${code_SOURCE_DIR}/C++/Pack.cpp - ${code_SOURCE_DIR}/C++/Pack.h - main.cpp -) - -define_test(pack "${SOURCES}")
--- a/C++/Tests/Pack/main.cpp Sun Mar 08 11:07:36 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,508 +0,0 @@ -/* - * TestPack.cpp -- test the pack serializer - * - * Copyright (c) 2013, 2014 David Demelier <markand@malikania.fr> - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include <sstream> -#include <vector> - -#include <gtest/gtest.h> - -#include <Pack.h> - -struct Point -{ - uint32_t width{}; - uint32_t height{}; - - Point() = default; - - Point(uint32_t width, uint32_t height) - : width(width) - , height(height) - { - } - - inline bool operator==(const Point &other) const - { - return width == other.width && height == other.height; - } -}; - -template <> -struct Pack::TypeInfo<Point> : public Pack::Serializable -{ - static void serialize(PackWriter &writer, const Point &point) - { - writer << point.width << point.height; - } - - static void unserialize(PackReader &reader, Point &point) - { - reader >> point.width >> point.height; - } -}; - -TEST(File, simpleLittleEndian) -{ - uint8_t u8(1), r8; - uint16_t u16(2), r16; - uint32_t u32(3), r32; - uint64_t u64(4), r64; - - remove("output.bin"); - - try { - { - PackFileWriter writer{"output.bin", Pack::Little}; - writer << u8 << u16 << u32 << u64; - } - - PackFileReader reader{"output.bin", Pack::Little}; - reader >> r8 >> r16 >> r32 >> r64; - - ASSERT_EQ(u8, r8); - ASSERT_EQ(u16, r16); - ASSERT_EQ(u32, r32); - ASSERT_EQ(u64, r64); - } catch (const std::exception &error) { - FAIL() << error.what(); - } -} - -TEST(File, simpleBigEndian) -{ - uint8_t u8(1), r8; - uint16_t u16(2), r16; - uint32_t u32(3), r32; - uint64_t u64(4), r64; - - remove("output.bin"); - - try { - { - PackFileWriter writer{"output.bin", Pack::Big}; - writer << u8 << u16 << u32 << u64; - } - - PackFileReader reader{"output.bin", Pack::Big}; - reader >> r8 >> r16 >> r32 >> r64; - - ASSERT_EQ(u8, r8); - ASSERT_EQ(u16, r16); - ASSERT_EQ(u32, r32); - ASSERT_EQ(u64, r64); - } catch (const std::exception &error) { - FAIL() << error.what(); - } -} - -TEST(File, arrayLittleEndian) -{ - std::vector<uint8_t> u8 { 1, 2, 3, 4, 5, 6, 7, 8 }; - std::vector<uint16_t> u16 { 10, 20, 30, 40, 50, 60, 70, 80 }; - std::vector<uint32_t> u32 { 100, 200, 300, 400, 500, 600, 700, 800 }; - std::vector<uint64_t> u64 { 1000, 2000, 3000, 4000, 5000, 6000, 7000, 8000 }; - - std::vector<uint8_t> r8(8); - std::vector<uint16_t> r16(8); - std::vector<uint32_t> r32(8); - std::vector<uint64_t> r64(8); - - remove("output.bin"); - - try { - { - PackFileWriter writer{"output.bin", Pack::Little}; - - writer << u8 << u16 << u32 << u64; - } - - PackFileReader reader{"output.bin", Pack::Little}; - - reader >> r8 >> r16 >> r32 >> r64; - - ASSERT_EQ(u8, r8); - ASSERT_EQ(u16, r16); - ASSERT_EQ(u32, r32); - ASSERT_EQ(u64, r64); - } catch (const std::exception &error) { - FAIL() << error.what(); - } -} - -TEST(File, arrayBigEndian) -{ - std::vector<uint8_t> u8 { 1, 2, 3, 4, 5, 6, 7, 8 }; - std::vector<uint16_t> u16 { 10, 20, 30, 40, 50, 60, 70, 80 }; - std::vector<uint32_t> u32 { 100, 200, 300, 400, 500, 600, 700, 800 }; - std::vector<uint64_t> u64 { 1000, 2000, 3000, 4000, 5000, 6000, 7000, 8000 }; - - std::vector<uint8_t> r8(8); - std::vector<uint16_t> r16(8); - std::vector<uint32_t> r32(8); - std::vector<uint64_t> r64(8); - - remove("output.bin"); - - try { - { - PackFileWriter writer{"output.bin", Pack::Big}; - - writer << u8 << u16 << u32 << u64; - } - - PackFileReader reader{"output.bin", Pack::Big}; - - reader >> r8 >> r16 >> r32 >> r64; - - ASSERT_EQ(u8, r8); - ASSERT_EQ(u16, r16); - ASSERT_EQ(u32, r32); - ASSERT_EQ(u64, r64); - } catch (const std::exception &error) { - FAIL() << error.what(); - } -} - -TEST(File, serializeSimpleLittleEndian) -{ - Point point{200, 400}; - Point result; - - remove("output.bin"); - - try { - { - PackFileWriter writer{"output.bin", Pack::Little}; - - writer << point; - } - - PackFileReader reader{"output.bin", Pack::Little}; - - reader >> result; - - ASSERT_EQ(point, result); - } catch (const std::exception &ex) { - std::cerr << "warning: " << ex.what() << std::endl; - } -} - -TEST(File, serializeSimpleBigEndian) -{ - Point point{200, 400}; - Point result; - - remove("output.bin"); - - try { - { - PackFileWriter writer{"output.bin", Pack::Big}; - - writer << point; - } - - PackFileReader reader{"output.bin", Pack::Big}; - - reader >> result; - - ASSERT_EQ(point, result); - } catch (const std::exception &ex) { - std::cerr << "warning: " << ex.what() << std::endl; - } -} - -TEST(File, serializeArrayLittleEndian) -{ - std::vector<Point> points{{10, 20}, {30, 40}}; - std::vector<Point> rpoints(2); - - remove("output.bin"); - - try { - { - PackFileWriter writer{"output.bin", Pack::Little}; - - writer << points; - } - - PackFileReader reader{"output.bin", Pack::Little}; - - reader >> rpoints; - - ASSERT_EQ(points, rpoints); - } catch (const std::exception &ex) { - std::cerr << "warning: " << ex.what() << std::endl; - } -} - -TEST(File, serializeArrayBigEndian) -{ - std::vector<Point> points{{10, 20}, {30, 40}}; - std::vector<Point> rpoints(2); - - remove("output.bin"); - - try { - { - PackFileWriter writer{"output.bin", Pack::Big}; - - writer << points; - } - - PackFileReader reader{"output.bin", Pack::Big}; - - reader >> rpoints; - - ASSERT_EQ(points, rpoints); - } catch (const std::exception &ex) { - std::cerr << "warning: " << ex.what() << std::endl; - } -} - -TEST(String, simpleLittleEndian) -{ - uint8_t u8(1), r8; - uint16_t u16(2), r16; - uint32_t u32(3), r32; - uint64_t u64(4), r64; - - try { - std::string input; - - { - PackStringWriter writer{Pack::Little}; - writer << u8 << u16 << u32 << u64; - input = writer.buffer(); - } - - PackStringReader reader{std::move(input), Pack::Little}; - reader >> r8 >> r16 >> r32 >> r64; - - ASSERT_EQ(u8, r8); - ASSERT_EQ(u16, r16); - ASSERT_EQ(u32, r32); - ASSERT_EQ(u64, r64); - } catch (const std::exception &error) { - FAIL() << error.what(); - } -} - -TEST(String, simpleBigEndian) -{ - uint8_t u8(1), r8; - uint16_t u16(2), r16; - uint32_t u32(3), r32; - uint64_t u64(4), r64; - - try { - std::string input; - - { - PackStringWriter writer{Pack::Big}; - writer << u8 << u16 << u32 << u64; - input = writer.buffer(); - } - - PackStringReader reader{std::move(input), Pack::Big}; - reader >> r8 >> r16 >> r32 >> r64; - - ASSERT_EQ(u8, r8); - ASSERT_EQ(u16, r16); - ASSERT_EQ(u32, r32); - ASSERT_EQ(u64, r64); - } catch (const std::exception &error) { - FAIL() << error.what(); - } -} - -TEST(String, arrayLittleEndian) -{ - std::vector<uint8_t> u8 { 1, 2, 3, 4, 5, 6, 7, 8 }; - std::vector<uint16_t> u16 { 10, 20, 30, 40, 50, 60, 70, 80 }; - std::vector<uint32_t> u32 { 100, 200, 300, 400, 500, 600, 700, 800 }; - std::vector<uint64_t> u64 { 1000, 2000, 3000, 4000, 5000, 6000, 7000, 8000 }; - - std::vector<uint8_t> r8(8); - std::vector<uint16_t> r16(8); - std::vector<uint32_t> r32(8); - std::vector<uint64_t> r64(8); - - try { - std::string input; - - { - PackStringWriter writer{Pack::Little}; - - writer << u8 << u16 << u32 << u64; - input = writer.buffer(); - } - - PackStringReader reader{std::move(input), Pack::Little}; - - reader >> r8 >> r16 >> r32 >> r64; - - ASSERT_EQ(u8, r8); - ASSERT_EQ(u16, r16); - ASSERT_EQ(u32, r32); - ASSERT_EQ(u64, r64); - } catch (const std::exception &error) { - FAIL() << error.what(); - } -} - -TEST(String, arrayBigEndian) -{ - std::vector<uint8_t> u8 { 1, 2, 3, 4, 5, 6, 7, 8 }; - std::vector<uint16_t> u16 { 10, 20, 30, 40, 50, 60, 70, 80 }; - std::vector<uint32_t> u32 { 100, 200, 300, 400, 500, 600, 700, 800 }; - std::vector<uint64_t> u64 { 1000, 2000, 3000, 4000, 5000, 6000, 7000, 8000 }; - - std::vector<uint8_t> r8(8); - std::vector<uint16_t> r16(8); - std::vector<uint32_t> r32(8); - std::vector<uint64_t> r64(8); - - try { - std::string input; - - { - PackStringWriter writer{Pack::Big}; - - writer << u8 << u16 << u32 << u64; - input = writer.buffer(); - } - - PackStringReader reader{std::move(input), Pack::Big}; - - reader >> r8 >> r16 >> r32 >> r64; - - ASSERT_EQ(u8, r8); - ASSERT_EQ(u16, r16); - ASSERT_EQ(u32, r32); - ASSERT_EQ(u64, r64); - } catch (const std::exception &error) { - FAIL() << error.what(); - } -} - -TEST(String, serializeSimpleLittleEndian) -{ - Point point{200, 400}; - Point result; - - try { - std::string input; - - { - PackStringWriter writer{Pack::Little}; - - writer << point; - input = writer.buffer(); - } - - PackStringReader reader{std::move(input), Pack::Little}; - - reader >> result; - - ASSERT_EQ(point, result); - } catch (const std::exception &ex) { - std::cerr << "warning: " << ex.what() << std::endl; - } -} - -TEST(String, serializeSimpleBigEndian) -{ - Point point{200, 400}; - Point result; - - try { - std::string input; - - { - PackStringWriter writer{Pack::Big}; - - writer << point; - input = writer.buffer(); - } - - PackStringReader reader{std::move(input), Pack::Big}; - - reader >> result; - - ASSERT_EQ(point, result); - } catch (const std::exception &ex) { - std::cerr << "warning: " << ex.what() << std::endl; - } -} - -TEST(String, serializeArrayLittleEndian) -{ - std::vector<Point> points{{10, 20}, {30, 40}}; - std::vector<Point> rpoints(2); - - try { - std::string input; - - { - PackStringWriter writer{Pack::Little}; - - writer << points; - input = writer.buffer(); - } - - PackStringReader reader{std::move(input), Pack::Little}; - - reader >> rpoints; - - ASSERT_EQ(points, rpoints); - } catch (const std::exception &ex) { - std::cerr << "warning: " << ex.what() << std::endl; - } -} - -TEST(String, serializeArrayBigEndian) -{ - std::vector<Point> points{{10, 20}, {30, 40}}; - std::vector<Point> rpoints(2); - - try { - std::string input; - - { - PackStringWriter writer{Pack::Big}; - - writer << points; - input = writer.buffer(); - } - - PackStringReader reader{std::move(input), Pack::Big}; - - reader >> rpoints; - - ASSERT_EQ(points, rpoints); - } catch (const std::exception &ex) { - std::cerr << "warning: " << ex.what() << std::endl; - } -} - -int main(int argc, char **argv) -{ - testing::InitGoogleTest(&argc, argv); - - return RUN_ALL_TESTS(); -}
--- a/C++/Tests/Parser/CMakeLists.txt Sun Mar 08 11:07:36 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,36 +0,0 @@ -# -# CMakeLists.txt -- tests for Parser -# -# Copyright (c) 2013, 2014 David Demelier <markand@malikania.fr> -# -# Permission to use, copy, modify, and/or distribute this software for any -# purpose with or without fee is hereby granted, provided that the above -# copyright notice and this permission notice appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF -# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -# - -set( - SOURCES - ${code_SOURCE_DIR}/C++/Parser.cpp - ${code_SOURCE_DIR}/C++/Parser.h - main.cpp - configs/simple.conf - configs/multi.conf -) - -define_test(parser "${SOURCES}") - -add_custom_command( - TARGET parser - POST_BUILD - COMMENT "Copying examples files" - COMMAND - ${CMAKE_COMMAND} -E copy_directory ${CMAKE_CURRENT_SOURCE_DIR}/configs $<TARGET_FILE_DIR:parser> -)
--- a/C++/Tests/Parser/configs/multi.conf Sun Mar 08 11:07:36 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,7 +0,0 @@ -[entity] -name = "Player" -version = 1.0 - -[entity] -name = "Subwinner" -version = 2.0 \ No newline at end of file
--- a/C++/Tests/Parser/configs/simple.conf Sun Mar 08 11:07:36 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,4 +0,0 @@ -[general] -option1=1 -option2 =2 -option3 = 3
--- a/C++/Tests/Parser/main.cpp Sun Mar 08 11:07:36 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,98 +0,0 @@ -/* - * TestParser.cpp -- test the config file parser - * - * Copyright (c) 2013, 2014 David Demelier <markand@malikania.fr> - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include <iostream> - -#include <gtest/gtest.h> - -#include <Parser.h> - -TEST(Basic, simple) -{ - try { - Parser parser("simple.conf"); - - const auto &s = parser.getSection("general"); - ASSERT_EQ("general", s.getName()); - - const auto &o1 = s.getOption<std::string>("option1"); - ASSERT_EQ("1", o1); - - const auto &o2 = s.getOption<std::string>("option2"); - ASSERT_EQ("2", o2); - - const auto &o3 = s.getOption<std::string>("option3"); - ASSERT_EQ("3", o3); - } catch (const std::out_of_range &error) { - FAIL(); - } catch (const std::runtime_error &error) { - FAIL() << error.what(); - } -} - -TEST(Basic, multi) -{ - try { - Parser parser("multi.conf"); - int i(0); - - parser.findSections("entity", [&] (const Section &s) { - if (i++ == 0) { - ASSERT_EQ("Player", s.getOption<std::string>("name")); - ASSERT_EQ("1.0", s.getOption<std::string>("version")); - } else { - ASSERT_EQ("Subwinner", s.getOption<std::string>("name")); - ASSERT_EQ("2.0", s.getOption<std::string>("version")); - } - }); - - ASSERT_EQ(2, i); - } catch (const std::out_of_range &error) { - FAIL(); - } catch (const std::runtime_error &error) { - FAIL() << error.what(); - } -} - -TEST(Basic, multiNoredef) -{ - try { - Parser parser("multi.conf", Parser::DisableRedefinition); - int i(0); - - parser.findSections("entity", [&] (const Section &s) { - if (i++ == 0) { - ASSERT_EQ("Player", s.getOption<std::string>("name")); - ASSERT_EQ("1.0", s.getOption<std::string>("version")); - } - }); - - ASSERT_EQ(1, i); - } catch (const std::out_of_range &error) { - FAIL(); - } catch (const std::runtime_error &error) { - FAIL() << error.what(); - } -} - -int main(int argc, char **argv) -{ - testing::InitGoogleTest(&argc, argv); - - return RUN_ALL_TESTS(); -} \ No newline at end of file
--- a/C++/Tests/Sockets/CMakeLists.txt Sun Mar 08 11:07:36 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,45 +0,0 @@ -# -# CMakeLists.txt -- tests for sockets -# -# Copyright (c) 2013, 2014 David Demelier <markand@malikania.fr> -# -# Permission to use, copy, modify, and/or distribute this software for any -# purpose with or without fee is hereby granted, provided that the above -# copyright notice and this permission notice appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF -# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -# - -find_package(OpenSSL REQUIRED) - -set( - SOURCES - ${code_SOURCE_DIR}/C++/Socket.cpp - ${code_SOURCE_DIR}/C++/Socket.h - ${code_SOURCE_DIR}/C++/SocketTcp.cpp - ${code_SOURCE_DIR}/C++/SocketTcp.h - ${code_SOURCE_DIR}/C++/SocketUdp.cpp - ${code_SOURCE_DIR}/C++/SocketUdp.h - ${code_SOURCE_DIR}/C++/SocketAddress.cpp - ${code_SOURCE_DIR}/C++/SocketAddress.h - ${code_SOURCE_DIR}/C++/SocketListener.cpp - ${code_SOURCE_DIR}/C++/SocketListener.h - ${code_SOURCE_DIR}/C++/SocketSsl.cpp - ${code_SOURCE_DIR}/C++/SocketSsl.h - main.cpp -) - -define_test(socket "${SOURCES}") - -if (WIN32) - target_link_libraries(socket ws2_32) -endif () - -target_include_directories(socket PRIVATE ${OPENSSL_INCLUDE_DIR}) -target_link_libraries(socket ${OPENSSL_LIBRARIES})
--- a/C++/Tests/Sockets/main.cpp Sun Mar 08 11:07:36 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,709 +0,0 @@ -/* - * main.cpp -- test sockets - * - * Copyright (c) 2013, 2014 David Demelier <markand@malikania.fr> - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include <chrono> -#include <iostream> -#include <sstream> -#include <string> -#include <thread> - -#include <gtest/gtest.h> - -#include "Socket.h" -#include "SocketAddress.h" -#include "SocketListener.h" -#include "SocketSsl.h" -#include "SocketTcp.h" -#include "SocketUdp.h" - -using namespace address; -using namespace std::literals::chrono_literals; - -/* -------------------------------------------------------- - * TCP tests - * -------------------------------------------------------- */ - -class TcpServerTest : public testing::Test { -protected: - SocketTcp m_server{AF_INET, 0}; - SocketTcp m_client{AF_INET, 0}; - - std::thread m_tserver; - std::thread m_tclient; - -public: - TcpServerTest() - { - m_server.set(SOL_SOCKET, SO_REUSEADDR, 1); - } - - ~TcpServerTest() - { - if (m_tserver.joinable()) - m_tserver.join(); - if (m_tclient.joinable()) - m_tclient.join(); - } -}; - -TEST_F(TcpServerTest, connect) -{ - m_tserver = std::thread([this] () { - m_server.bind(Internet("*", 16000, AF_INET)); - - ASSERT_EQ(SocketState::Bound, m_server.state()); - - m_server.listen(); - m_server.accept(); - m_server.close(); - }); - - std::this_thread::sleep_for(100ms); - - m_tclient = std::thread([this] () { - m_client.connect(Internet("127.0.0.1", 16000, AF_INET)); - - ASSERT_EQ(SocketState::Connected, m_client.state()); - - m_client.close(); - }); -} - -TEST_F(TcpServerTest, io) -{ - m_tserver = std::thread([this] () { - m_server.bind(Internet("*", 16000, AF_INET)); - m_server.listen(); - - auto client = m_server.accept(); - auto msg = client.recv(512); - - ASSERT_EQ("hello world", msg); - - client.send(msg); - client.close(); - - m_server.close(); - }); - - std::this_thread::sleep_for(100ms); - - m_tclient = std::thread([this] () { - m_client.connect(Internet("127.0.0.1", 16000, AF_INET)); - m_client.send("hello world"); - - ASSERT_EQ("hello world", m_client.recv(512)); - - m_client.close(); - }); -} - -/* -------------------------------------------------------- - * UDP tests - * -------------------------------------------------------- */ - -class UdpServerTest : public testing::Test { -protected: - SocketUdp m_server{AF_INET, 0}; - SocketUdp m_client{AF_INET, 0}; - - std::thread m_tserver; - std::thread m_tclient; - -public: - UdpServerTest() - { - m_server.set(SOL_SOCKET, SO_REUSEADDR, 1); - } - - ~UdpServerTest() - { - if (m_tserver.joinable()) - m_tserver.join(); - if (m_tclient.joinable()) - m_tclient.join(); - } -}; - -TEST_F(UdpServerTest, io) -{ - m_tserver = std::thread([this] () { - SocketAddress info; - - m_server.bind(Internet("*", 16000, AF_INET)); - - auto msg = m_server.recvfrom(512, info); - - ASSERT_EQ("hello world", msg); - - m_server.sendto(msg, info); - m_server.close(); - }); - - std::this_thread::sleep_for(100ms); - - m_tclient = std::thread([this] () { - Internet info("127.0.0.1", 16000, AF_INET); - - m_client.sendto("hello world", info); - - ASSERT_EQ("hello world", m_client.recvfrom(512, info)); - - m_client.close(); - }); -} - -/* -------------------------------------------------------- - * Listener tests (standard) - * -------------------------------------------------------- */ - -class ListenerTest : public testing::Test { -protected: - SocketListener m_listener; - SocketTcp socket1{AF_INET, 0}; - SocketUdp socket2{AF_INET, 0}; - -public: - ~ListenerTest() - { - socket1.close(); - socket2.close(); - } -}; - -TEST_F(ListenerTest, set) -{ - m_listener.set(socket1, SocketListener::Read); - - ASSERT_EQ(1, static_cast<int>(m_listener.size())); - ASSERT_EQ(SocketListener::Read, m_listener.begin()->second); - - m_listener.set(socket1, SocketListener::Write); - - ASSERT_EQ(1, static_cast<int>(m_listener.size())); - ASSERT_EQ(0x3, m_listener.begin()->second); - - // Fake a re-insert of the same socket - m_listener.set(socket1, SocketListener::Write); - - ASSERT_EQ(1, static_cast<int>(m_listener.size())); - ASSERT_EQ(0x3, m_listener.begin()->second); - - // Add an other socket now - m_listener.set(socket2, SocketListener::Read | SocketListener::Write); - - ASSERT_EQ(2, static_cast<int>(m_listener.size())); - - for (auto &pair : m_listener) { - ASSERT_EQ(0x3, pair.second); - ASSERT_TRUE(pair.first == socket1 || pair.first == socket2); - } -} - -TEST_F(ListenerTest, unset) -{ - m_listener.set(socket1, SocketListener::Read | SocketListener::Write); - m_listener.set(socket2, SocketListener::Read | SocketListener::Write); - - m_listener.unset(socket1, SocketListener::Read); - - ASSERT_EQ(2, static_cast<int>(m_listener.size())); - - // Use a for loop since it can be ordered differently - for (auto &pair : m_listener) { - if (pair.first == socket1) { - ASSERT_EQ(0x2, pair.second); - } else if (pair.first == socket2) { - ASSERT_EQ(0x3, pair.second); - } - } - - m_listener.unset(socket1, SocketListener::Write); - - ASSERT_EQ(1, static_cast<int>(m_listener.size())); - ASSERT_EQ(0x3, m_listener.begin()->second); -} - -TEST_F(ListenerTest, remove) -{ - m_listener.set(socket1, SocketListener::Read | SocketListener::Write); - m_listener.set(socket2, SocketListener::Read | SocketListener::Write); - m_listener.remove(socket1); - - ASSERT_EQ(1, static_cast<int>(m_listener.size())); - ASSERT_EQ(0x3, m_listener.begin()->second); -} - -TEST_F(ListenerTest, clear) -{ - m_listener.set(socket1, SocketListener::Read | SocketListener::Write); - m_listener.set(socket2, SocketListener::Read | SocketListener::Write); - m_listener.clear(); - - ASSERT_EQ(0, static_cast<int>(m_listener.size())); -} - -/* -------------------------------------------------------- - * Listener: poll - * -------------------------------------------------------- */ - -#if defined(SOCKET_HAVE_POLL) - -class ListenerPollTest : public testing::Test { -protected: - SocketListener m_listener{SocketMethod::Poll}; - SocketTcp m_masterTcp{AF_INET, 0}; - SocketTcp m_clientTcp{AF_INET, 0}; - - std::thread m_tserver; - std::thread m_tclient; - -public: - ListenerPollTest() - { - m_masterTcp.set(SOL_SOCKET, SO_REUSEADDR, 1); - m_masterTcp.bind(Internet("*", 16000, AF_INET)); - m_masterTcp.listen(); - } - - ~ListenerPollTest() - { - if (m_tserver.joinable()) { - m_tserver.join(); - } - if (m_tclient.joinable()) { - m_tclient.join(); - } - } -}; - -TEST_F(ListenerPollTest, accept) -{ - m_tserver = std::thread([this] () { - try { - m_listener.set(m_masterTcp, SocketListener::Read); - m_listener.select(); - m_masterTcp.accept(); - m_masterTcp.close(); - } catch (const std::exception &ex) { - FAIL() << ex.what(); - } - }); - - std::this_thread::sleep_for(100ms); - - m_tclient = std::thread([this] () { - m_clientTcp.connect(Internet("127.0.0.1", 16000, AF_INET)); - }); -} - -TEST_F(ListenerPollTest, recv) -{ - m_tserver = std::thread([this] () { - try { - m_listener.set(m_masterTcp, SocketListener::Read); - m_listener.select(); - - auto sc = m_masterTcp.accept(); - - ASSERT_EQ("hello", sc.recv(512)); - - m_masterTcp.close(); - } catch (const std::exception &ex) { - FAIL() << ex.what(); - } - }); - - std::this_thread::sleep_for(100ms); - - m_tclient = std::thread([this] () { - m_clientTcp.connect(Internet("127.0.0.1", 16000, AF_INET)); - m_clientTcp.send("hello"); - }); -} - -#endif - -/* -------------------------------------------------------- - * Listener: select - * -------------------------------------------------------- */ - -class ListenerSelectTest : public testing::Test { -protected: - SocketListener m_listener{SocketMethod::Select}; - SocketTcp m_masterTcp{AF_INET, 0}; - SocketTcp m_clientTcp{AF_INET, 0}; - - std::thread m_tserver; - std::thread m_tclient; - -public: - ListenerSelectTest() - { - m_masterTcp.set(SOL_SOCKET, SO_REUSEADDR, 1); - m_masterTcp.bind(Internet("*", 16000, AF_INET)); - m_masterTcp.listen(); - } - - ~ListenerSelectTest() - { - if (m_tserver.joinable()) { - m_tserver.join(); - } - if (m_tclient.joinable()) { - m_tclient.join(); - } - } -}; - -TEST_F(ListenerSelectTest, accept) -{ - m_tserver = std::thread([this] () { - try { - m_listener.set(m_masterTcp, SocketListener::Read); - m_listener.select(); - m_masterTcp.accept(); - m_masterTcp.close(); - } catch (const std::exception &ex) { - FAIL() << ex.what(); - } - }); - - std::this_thread::sleep_for(100ms); - - m_tclient = std::thread([this] () { - m_clientTcp.connect(Internet("127.0.0.1", 16000, AF_INET)); - }); -} - -TEST_F(ListenerSelectTest, recv) -{ - m_tserver = std::thread([this] () { - try { - m_listener.set(m_masterTcp, SocketListener::Read); - m_listener.select(); - - auto sc = m_masterTcp.accept(); - - ASSERT_EQ("hello", sc.recv(512)); - - m_masterTcp.close(); - } catch (const std::exception &ex) { - FAIL() << ex.what(); - } - }); - - std::this_thread::sleep_for(100ms); - - m_tclient = std::thread([this] () { - m_clientTcp.connect(Internet("127.0.0.1", 16000, AF_INET)); - m_clientTcp.send("hello"); - }); -} - -/* -------------------------------------------------------- - * Non-blocking connect - * -------------------------------------------------------- */ - -class NonBlockingConnectTest : public testing::Test { -protected: - SocketTcp m_server{AF_INET, 0}; - SocketTcp m_client{AF_INET, 0}; - - std::thread m_tserver; - std::thread m_tclient; - -public: - NonBlockingConnectTest() - { - m_client.setBlockMode(false); - } - - ~NonBlockingConnectTest() - { - if (m_tserver.joinable()) - m_tserver.join(); - if (m_tclient.joinable()) - m_tclient.join(); - } -}; - -TEST_F(NonBlockingConnectTest, success) -{ - m_server.set(SOL_SOCKET, SO_REUSEADDR, 1); - m_server.bind(Internet("*", 16000, AF_INET)); - m_server.listen(); - - m_tserver = std::thread([this] () { - SocketTcp client = m_server.accept(); - - m_server.close(); - client.close(); - }); - - std::this_thread::sleep_for(100ms); - - m_tclient = std::thread([this] () { - try { - m_client.waitConnect(Internet("127.0.0.1", 16000, AF_INET), 3000); - } catch (const SocketError &error) { - FAIL() << error.what(); - } - - ASSERT_EQ(SocketState::Connected, m_client.state()); - - m_client.close(); - }); -} - -TEST_F(NonBlockingConnectTest, fail) -{ - /* - * /!\ If you find a way to test this locally please tell me /!\ - */ - m_tclient = std::thread([this] () { - try { - m_client.waitConnect(Internet("google.fr", 9000, AF_INET), 100); - - FAIL() << "Expected exception, got success"; - } catch (const SocketError &error) { - ASSERT_EQ(SocketError::Timeout, error.code()); - } - - m_client.close(); - }); -} - -/* -------------------------------------------------------- - * TCP accept - * -------------------------------------------------------- */ - -class TcpAcceptTest : public testing::Test { -protected: - SocketTcp m_server{AF_INET, 0}; - SocketTcp m_client{AF_INET, 0}; - - std::thread m_tserver; - std::thread m_tclient; - -public: - TcpAcceptTest() - { - m_server.set(SOL_SOCKET, SO_REUSEADDR, 1); - m_server.bind(Internet("*", 16000, AF_INET)); - m_server.listen(); - } - - ~TcpAcceptTest() - { - if (m_tserver.joinable()) - m_tserver.join(); - if (m_tclient.joinable()) - m_tclient.join(); - } -}; - -TEST_F(TcpAcceptTest, blockingWaitSuccess) -{ - m_tserver = std::thread([this] () { - try { - m_server.waitAccept(3000).close(); - } catch (const SocketError &error) { - FAIL() << error.what(); - } - - m_server.close(); - }); - - std::this_thread::sleep_for(100ms); - - m_tclient = std::thread([this] () { - m_client.connect(Internet("127.0.0.1", 16000, AF_INET)); - m_client.close(); - }); -} - -TEST_F(TcpAcceptTest, nonBlockingWaitSuccess) -{ - m_tserver = std::thread([this] () { - try { - m_server.setBlockMode(false); - m_server.waitAccept(3000).close(); - } catch (const SocketError &error) { - FAIL() << error.what(); - } - - m_server.close(); - }); - - std::this_thread::sleep_for(100ms); - - m_tclient = std::thread([this] () { - m_client.connect(Internet("127.0.0.1", 16000, AF_INET)); - m_client.close(); - }); -} - -TEST_F(TcpAcceptTest, nonBlockingWaitFail) -{ - // No client, no accept - try { - m_server.setBlockMode(false); - m_server.waitAccept(100).close(); - - FAIL() << "Expected exception, got success"; - } catch (const SocketError &error) { - ASSERT_EQ(SocketError::Timeout, error.code()); - } - - m_server.close(); -} - -/* -------------------------------------------------------- - * TCP recv - * -------------------------------------------------------- */ - -class TcpRecvTest : public testing::Test { -protected: - SocketTcp m_server{AF_INET, 0}; - SocketTcp m_client{AF_INET, 0}; - - std::thread m_tserver; - std::thread m_tclient; - -public: - TcpRecvTest() - { - m_server.set(SOL_SOCKET, SO_REUSEADDR, 1); - m_server.bind(Internet("*", 16000, AF_INET)); - m_server.listen(); - } - - ~TcpRecvTest() - { - if (m_tserver.joinable()) - m_tserver.join(); - if (m_tclient.joinable()) - m_tclient.join(); - } -}; - -TEST_F(TcpRecvTest, blockingSuccess) -{ - m_tserver = std::thread([this] () { - SocketTcp client = m_server.accept(); - - ASSERT_EQ("hello", client.recv(32)); - - client.close(); - m_server.close(); - }); - - std::this_thread::sleep_for(100ms); - - m_tclient = std::thread([this] () { - m_client.connect(Internet("127.0.0.1", 16000, AF_INET)); - m_client.send("hello"); - m_client.close(); - }); -} - -TEST_F(TcpRecvTest, blockingWaitSuccess) -{ - m_tserver = std::thread([this] () { - SocketTcp client = m_server.accept(); - - ASSERT_EQ("hello", client.waitRecv(32, 3000)); - - client.close(); - m_server.close(); - }); - - std::this_thread::sleep_for(100ms); - - m_tclient = std::thread([this] () { - m_client.connect(Internet("127.0.0.1", 16000, AF_INET)); - m_client.send("hello"); - m_client.close(); - }); -} - -TEST_F(TcpRecvTest, nonBlockingWaitSuccess) -{ - m_tserver = std::thread([this] () { - SocketTcp client = m_server.accept(); - - client.setBlockMode(false); - - ASSERT_EQ("hello", client.waitRecv(32, 3000)); - - client.close(); - m_server.close(); - }); - - std::this_thread::sleep_for(100ms); - - m_tclient = std::thread([this] () { - m_client.connect(Internet("127.0.0.1", 16000, AF_INET)); - m_client.send("hello"); - m_client.close(); - }); -} - -/* -------------------------------------------------------- - * Socket SSL - * -------------------------------------------------------- */ - -class SslTest : public testing::Test { -protected: - SocketSsl client{AF_INET, 0}; -}; - -TEST_F(SslTest, connect) -{ - try { - client.connect(Internet("google.fr", 443, AF_INET)); - client.close(); - } catch (const SocketError &error) { - FAIL() << error.what(); - } -} - -TEST_F(SslTest, recv) -{ - try { - client.connect(Internet("google.fr", 443, AF_INET)); - client.send("GET / HTTP/1.0\r\n\r\n"); - - std::string msg = client.recv(512); - std::string content = msg.substr(0, 18); - - ASSERT_EQ("HTTP/1.0 302 Found", content); - - client.close(); - } catch (const SocketError &error) { - FAIL() << error.what(); - } -} - -int main(int argc, char **argv) -{ - testing::InitGoogleTest(&argc, argv); - - return RUN_ALL_TESTS(); -}
--- a/C++/Tests/TreeNode/CMakeLists.txt Sun Mar 08 11:07:36 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,25 +0,0 @@ -# -# CMakeLists.txt -- tests for TreeNode -# -# Copyright (c) 2013, 2014 David Demelier <markand@malikania.fr> -# -# Permission to use, copy, modify, and/or distribute this software for any -# purpose with or without fee is hereby granted, provided that the above -# copyright notice and this permission notice appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF -# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -# - -set( - SOURCES - ${code_SOURCE_DIR}/C++/TreeNode.h - main.cpp -) - -define_test(treenode "${SOURCES}")
--- a/C++/Tests/TreeNode/main.cpp Sun Mar 08 11:07:36 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1561 +0,0 @@ -/* - * main.cpp -- main test file for TreeNode - * - * Copyright (c) 2013, 2014 David Demelier <markand@malikania.fr> - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include <chrono> -#include <iostream> -#include <iterator> -#include <string> - -#include <gtest/gtest.h> - -#include <TreeNode.h> - -class Object final : public TreeNode<Object> { -private: - std::string m_name; - -public: - Object(std::string name) - : TreeNode() - { - m_name = std::move(name); - } - - const std::string &name() const - { - return m_name; - } - - friend bool operator==(const Object &o1, const Object &o2); -}; - -bool operator==(const Object &o1, const Object &o2) -{ - return o1.name() == o2.name(); -} - -/* - * Random trees are created and then we test each node with the following preferred order: - * - * ASSERT_(TRUE|FALSE) isRoot() - * ASSERT_(TRUE|FALSE) isLeaf() - * ASSERT_EQ countChildren() - * ASSERT_EQ() name() - * ASSERT_TRUE & addresses - */ - -/* -------------------------------------------------------- - * Basic construction and insertion - * -------------------------------------------------------- */ - -/* - * @root - */ -TEST(Basic, construct) -{ - Object object("root"); - - ASSERT_TRUE(object.isRoot()); - ASSERT_TRUE(object.isLeaf()); - ASSERT_EQ(0, object.countChildren()); - ASSERT_EQ("root", object.name()); -} - -/* - * @root -> append move -> @root - * | - * @a - */ -TEST(Insertion, simpleAppendMove) -{ - Object object("root"); - Object a("a"); - - object.append(std::move(a)); - - // test root - ASSERT_TRUE(object.isRoot()); - ASSERT_FALSE(object.isLeaf()); - ASSERT_EQ(1, object.countChildren()); - ASSERT_EQ("root", object.name()); - - // test moved a - ASSERT_FALSE(object[0].isRoot()); - ASSERT_TRUE(object[0].isLeaf()); - ASSERT_EQ("a", object[0].name()); - ASSERT_TRUE(&object == &object[0].parent()); - - // test original a - ASSERT_TRUE(a.isRoot()); - ASSERT_TRUE(a.isLeaf()); - ASSERT_EQ(0, a.countChildren()); -} - -/* - * @root -> append copy -> @root - * | - * @a - */ -TEST(Insertion, simpleAppendCopy) -{ - Object object("root"); - Object copy("b"); - - object.append(copy); - - // test root - ASSERT_TRUE(object.isRoot()); - ASSERT_FALSE(object.isLeaf()); - ASSERT_EQ(1, object.countChildren()); - ASSERT_EQ("root", object.name()); - - // test copied b - ASSERT_FALSE(object[0].isRoot()); - ASSERT_TRUE(object[0].isLeaf()); - ASSERT_EQ("b", object[0].name()); - ASSERT_TRUE(&object == &object[0].parent()); - - // test original b - ASSERT_TRUE(copy.isRoot()); - ASSERT_TRUE(copy.isLeaf()); - ASSERT_EQ(0, copy.countChildren()); - ASSERT_EQ("b", copy.name()); -} - -/* - * @root -> append move -> @root - * / \ - * 1@ @2 - */ -TEST(Insertion, doubleAppendMove) -{ - Object object("root"); - Object o1("1"); - Object o2("2"); - - object.append(std::move(o1)); - object.append(std::move(o2)); - - // test root - ASSERT_TRUE(object.isRoot()); - ASSERT_FALSE(object.isLeaf()); - ASSERT_EQ(2, object.countChildren()); - ASSERT_EQ("root", object.name()); - - // test moved 1 - ASSERT_FALSE(object[0].isRoot()); - ASSERT_TRUE(object[0].isLeaf()); - ASSERT_EQ(0, object[0].countChildren()); - ASSERT_EQ("1", object[0].name()); - ASSERT_TRUE(&object == &object[0].parent()); - - // test moved 2 - ASSERT_FALSE(object[1].isRoot()); - ASSERT_TRUE(object[1].isLeaf()); - ASSERT_EQ("2", object[1].name()); - ASSERT_EQ(0, object[1].countChildren()); - ASSERT_TRUE(&object == &object[1].parent()); - - // test original 1 - ASSERT_TRUE(o1.isRoot()); - ASSERT_TRUE(o1.isLeaf()); - ASSERT_EQ(0, o1.countChildren()); - - // test original 2 - ASSERT_TRUE(o2.isRoot()); - ASSERT_TRUE(o2.isLeaf()); - ASSERT_EQ(0, o2.countChildren()); -} - -/* - * @root -> append copy -> @root - * / \ - * 1@ @2 - */ -TEST(Insertion, doubleAppendCopy) -{ - Object object("root"); - Object o1("1"); - Object o2("2"); - - object.append(o1); - object.append(o2); - - // test root - ASSERT_TRUE(object.isRoot()); - ASSERT_FALSE(object.isLeaf()); - ASSERT_EQ(2, object.countChildren()); - ASSERT_EQ("root", object.name()); - - // test copied 1 - ASSERT_FALSE(object[0].isRoot()); - ASSERT_TRUE(object[0].isLeaf()); - ASSERT_EQ(0, object[0].countChildren()); - ASSERT_EQ("1", object[0].name()); - ASSERT_TRUE(&object == &object[0].parent()); - - // test copied 2 - ASSERT_FALSE(object[1].isRoot()); - ASSERT_TRUE(object[1].isLeaf()); - ASSERT_EQ(0, object[1].countChildren()); - ASSERT_EQ("2", object[1].name()); - ASSERT_TRUE(&object == &object[1].parent()); - - // test original 1 - ASSERT_TRUE(o1.isRoot()); - ASSERT_TRUE(o1.isLeaf()); - ASSERT_EQ(0, o1.countChildren()); - ASSERT_EQ("1", o1.name()); - - // test original 2 - ASSERT_TRUE(o2.isRoot()); - ASSERT_TRUE(o2.isLeaf()); - ASSERT_EQ(0, o2.countChildren()); - ASSERT_EQ("2", o2.name()); -} - -/* - * @root -> push move -> @root - * | - * @a - */ -TEST(Insertion, simplePushMove) -{ - Object object("root"); - Object a("a"); - - object.push(std::move(a)); - - // test root - ASSERT_TRUE(object.isRoot()); - ASSERT_FALSE(object.isLeaf()); - ASSERT_EQ(1, object.countChildren()); - ASSERT_EQ("a", object[0].name()); - - // test moved a - ASSERT_FALSE(object[0].isRoot()); - ASSERT_TRUE(object[0].isLeaf()); - ASSERT_EQ(0, object[0].countChildren()); - ASSERT_EQ("a", object[0].name()); - ASSERT_TRUE(&object == &object[0].parent()); - - // test original a - ASSERT_TRUE(a.isRoot()); - ASSERT_TRUE(a.isLeaf()); - ASSERT_EQ(0, a.countChildren()); -} - -/* - * @root -> push copy -> @root - * | - * @a - */ -TEST(Insertion, simplePushCopy) -{ - Object object("root"); - Object copy("a"); - - object.push(copy); - - // test root - ASSERT_TRUE(object.isRoot()); - ASSERT_FALSE(object.isLeaf()); - ASSERT_EQ(1, object.countChildren()); - ASSERT_EQ("a", object[0].name()); - - // test copied a - ASSERT_FALSE(object[0].isRoot()); - ASSERT_TRUE(object[0].isLeaf()); - ASSERT_EQ(0, object[0].countChildren()); - ASSERT_EQ("a", object[0].name()); - ASSERT_TRUE(&object == &object[0].parent()); - - // test original b - ASSERT_TRUE(copy.isRoot()); - ASSERT_TRUE(copy.isLeaf()); - ASSERT_EQ(0, copy.countChildren()); - ASSERT_EQ("a", copy.name()); -} - -/* - * @root -> push move -> @root - * / \ - * 2@ @1 - */ -TEST(Insertion, doublePushMove) -{ - Object object("root"); - Object o1("1"); - Object o2("2"); - - object.push(std::move(o1)); - object.push(std::move(o2)); - - // test root - ASSERT_TRUE(object.isRoot()); - ASSERT_FALSE(object.isLeaf()); - ASSERT_EQ(2, object.countChildren()); - ASSERT_EQ("root", object.name()); - - // test moved 2 - ASSERT_FALSE(object[0].isRoot()); - ASSERT_TRUE(object[0].isLeaf()); - ASSERT_EQ(0, object[0].countChildren()); - ASSERT_EQ("2", object[0].name()); - ASSERT_TRUE(&object == &object[0].parent()); - - // test moved 1 - ASSERT_FALSE(object[1].isRoot()); - ASSERT_TRUE(object[1].isLeaf()); - ASSERT_EQ("1", object[1].name()); - ASSERT_EQ(0, object[1].countChildren()); - ASSERT_TRUE(&object == &object[1].parent()); - - // test original 1 - ASSERT_TRUE(o1.isRoot()); - ASSERT_TRUE(o1.isLeaf()); - ASSERT_EQ(0, o1.countChildren()); - - // test original 2 - ASSERT_TRUE(o2.isRoot()); - ASSERT_TRUE(o2.isLeaf()); - ASSERT_EQ(0, o2.countChildren()); -} - -/* - * @root -> push copy -> @root - * / \ - * 2@ @1 - */ -TEST(Insertion, doublePushCopy) -{ - Object object("root"); - Object o1("1"); - Object o2("2"); - - object.push(o1); - object.push(o2); - - // test root - ASSERT_TRUE(object.isRoot()); - ASSERT_FALSE(object.isLeaf()); - ASSERT_EQ(2, object.countChildren()); - ASSERT_EQ("root", object.name()); - - // test copied 2 - ASSERT_FALSE(object[0].isRoot()); - ASSERT_TRUE(object[0].isLeaf()); - ASSERT_EQ(0, object[0].countChildren()); - ASSERT_EQ("2", object[0].name()); - ASSERT_TRUE(&object == &object[0].parent()); - - // test copied 1 - ASSERT_FALSE(object[1].isRoot()); - ASSERT_TRUE(object[1].isLeaf()); - ASSERT_EQ(0, object[1].countChildren()); - ASSERT_EQ("1", object[1].name()); - ASSERT_TRUE(&object == &object[1].parent()); - - // test original 1 - ASSERT_TRUE(o1.isRoot()); - ASSERT_TRUE(o1.isLeaf()); - ASSERT_EQ(0, o1.countChildren()); - ASSERT_EQ("1", o1.name()); - - // test original 2 - ASSERT_TRUE(o2.isRoot()); - ASSERT_TRUE(o2.isLeaf()); - ASSERT_EQ(0, o2.countChildren()); - ASSERT_EQ("2", o2.name()); -} - -/* -------------------------------------------------------- - * Sub insertion - * -------------------------------------------------------- */ - -/* - * @root - * / \ - * / \ - * a@ @b - * / \ / \ - * c@ d@ @e @f - */ -TEST(SubInsert, append) -{ - Object object("root"); - - object.append(Object("a")); - object.append(Object("b")); - - object[0].append(Object("c")); - object[0].append(Object("d")); - object[1].append(Object("e")); - object[1].append(Object("f")); - - // test root - ASSERT_TRUE(object.isRoot()); - ASSERT_FALSE(object.isLeaf()); - ASSERT_EQ(2, object.countChildren()); - ASSERT_EQ("root", object.name()); - - // test a - ASSERT_FALSE(object[0].isRoot()); - ASSERT_FALSE(object[0].isLeaf()); - ASSERT_EQ(2, object[0].countChildren()); - ASSERT_EQ("a", object[0].name()); - ASSERT_TRUE(&object == &object[0].parent()); - - // test b - ASSERT_FALSE(object[1].isRoot()); - ASSERT_FALSE(object[1].isLeaf()); - ASSERT_EQ(2, object[1].countChildren()); - ASSERT_EQ("b", object[1].name()); - ASSERT_TRUE(&object == &object[1].parent()); - - // test c - ASSERT_FALSE(object[0][0].isRoot()); - ASSERT_TRUE(object[0][0].isLeaf()); - ASSERT_EQ(0, object[0][0].countChildren()); - ASSERT_EQ("c", object[0][0].name()); - ASSERT_TRUE(&object[0] == &object[0][0].parent()); - - // test d - ASSERT_FALSE(object[0][1].isRoot()); - ASSERT_TRUE(object[0][1].isLeaf()); - ASSERT_EQ(0, object[0][1].countChildren()); - ASSERT_EQ("d", object[0][1].name()); - ASSERT_TRUE(&object[0] == &object[0][1].parent()); - - // test e - ASSERT_FALSE(object[1][0].isRoot()); - ASSERT_TRUE(object[1][0].isLeaf()); - ASSERT_EQ(0, object[1][0].countChildren()); - ASSERT_EQ("e", object[1][0].name()); - ASSERT_TRUE(&object[1] == &object[1][0].parent()); - - // test f - ASSERT_FALSE(object[1][1].isRoot()); - ASSERT_TRUE(object[1][1].isLeaf()); - ASSERT_EQ(0, object[1][1].countChildren()); - ASSERT_EQ("f", object[1][1].name()); - ASSERT_TRUE(&object[1] == &object[1][1].parent()); -} - -/* - * @root - * / \ - * / \ - * b@ @a - * / \ / \ - * d@ c@ @f @e - */ -TEST(SubInsert, push) -{ - Object object("root"); - - object.push(Object("a")); - object.push(Object("b")); - - object[0].push(Object("c")); - object[0].push(Object("d")); - object[1].push(Object("e")); - object[1].push(Object("f")); - - // test root - ASSERT_TRUE(object.isRoot()); - ASSERT_FALSE(object.isLeaf()); - ASSERT_EQ(2, object.countChildren()); - ASSERT_EQ("root", object.name()); - - // test b - ASSERT_FALSE(object[0].isRoot()); - ASSERT_FALSE(object[0].isLeaf()); - ASSERT_EQ(2, object[0].countChildren()); - ASSERT_EQ("b", object[0].name()); - ASSERT_TRUE(&object == &object[0].parent()); - - // test a - ASSERT_FALSE(object[1].isRoot()); - ASSERT_FALSE(object[1].isLeaf()); - ASSERT_EQ(2, object[1].countChildren()); - ASSERT_EQ("a", object[1].name()); - ASSERT_TRUE(&object == &object[1].parent()); - - // test d - ASSERT_FALSE(object[0][0].isRoot()); - ASSERT_TRUE(object[0][0].isLeaf()); - ASSERT_EQ(0, object[0][0].countChildren()); - ASSERT_EQ("d", object[0][0].name()); - ASSERT_TRUE(&object[0] == &object[0][0].parent()); - - // test c - ASSERT_FALSE(object[0][1].isRoot()); - ASSERT_TRUE(object[0][1].isLeaf()); - ASSERT_EQ(0, object[0][1].countChildren()); - ASSERT_EQ("c", object[0][1].name()); - ASSERT_TRUE(&object[0] == &object[0][1].parent()); - - // test f - ASSERT_FALSE(object[1][0].isRoot()); - ASSERT_TRUE(object[1][0].isLeaf()); - ASSERT_EQ(0, object[1][0].countChildren()); - ASSERT_EQ("f", object[1][0].name()); - ASSERT_TRUE(&object[1] == &object[1][0].parent()); - - // test e - ASSERT_FALSE(object[1][1].isRoot()); - ASSERT_TRUE(object[1][1].isLeaf()); - ASSERT_EQ(0, object[1][1].countChildren()); - ASSERT_EQ("e", object[1][1].name()); - ASSERT_TRUE(&object[1] == &object[1][1].parent()); -} - -/* -------------------------------------------------------- - * Move constructor - * -------------------------------------------------------- */ - -TEST(MoveConstructor, simple) -{ - Object root("root"); - Object moved(std::move(root)); - - // test root - ASSERT_TRUE(root.isRoot()); - ASSERT_TRUE(root.isLeaf()); - ASSERT_EQ(0, root.countChildren()); - - // test moved - ASSERT_TRUE(moved.isRoot()); - ASSERT_TRUE(moved.isLeaf()); - ASSERT_EQ(0, moved.countChildren()); - ASSERT_EQ("root", moved.name()); -} - -/* - * @root -> copy - * / \ - * / \ - * a@ @b - */ -TEST(MoveConstructor, oneLevel) -{ - Object root("root"); - - root.append(Object("a")); - root.append(Object("b")); - - Object moved(std::move(root)); - ASSERT_TRUE(moved.isRoot()); - ASSERT_EQ(2, moved.countChildren()); - ASSERT_EQ("a", moved[0].name()); - ASSERT_EQ("b", moved[1].name()); - ASSERT_EQ("root", moved[0].parent().name()); - ASSERT_EQ("root", moved[1].parent().name()); - ASSERT_TRUE(&moved == &moved[0].parent()); - ASSERT_TRUE(&moved == &moved[1].parent()); -} - -/* - * @root -> copy - * / \ - * / \ - * a@ @b - * / \ / \ - * c@ d@ @e @f - * - */ -TEST(MoveConstructor, twoLevels) -{ - Object root("root"); - - root.append(Object("a")); - root.append(Object("b")); - - root[0].append(Object("c")); - root[0].append(Object("d")); - root[1].append(Object("e")); - root[1].append(Object("f")); - - Object moved(std::move(root)); - - // test root - ASSERT_TRUE(moved.isRoot()); - ASSERT_EQ(2, moved.countChildren()); - - // test a, b - ASSERT_EQ("a", moved[0].name()); - ASSERT_EQ("b", moved[1].name()); - ASSERT_EQ(2, moved[0].countChildren()); - ASSERT_EQ(2, moved[1].countChildren()); - ASSERT_EQ("root", moved[0].parent().name()); - ASSERT_EQ("root", moved[1].parent().name()); - ASSERT_TRUE(&moved == &moved[0].parent()); - ASSERT_TRUE(&moved == &moved[1].parent()); - - // test c, d - ASSERT_EQ("c", moved[0][0].name()); - ASSERT_EQ("d", moved[0][1].name()); - ASSERT_EQ("a", moved[0][0].parent().name()); - ASSERT_EQ("a", moved[0][1].parent().name()); - ASSERT_TRUE(&moved[0] == &moved[0][0].parent()); - ASSERT_TRUE(&moved[0] == &moved[0][1].parent()); - ASSERT_TRUE(moved[0][0].isLeaf()); - ASSERT_TRUE(moved[0][1].isLeaf()); - ASSERT_FALSE(moved[0][0].isRoot()); - ASSERT_FALSE(moved[0][1].isRoot()); - - // test e, f - ASSERT_EQ("e", moved[1][0].name()); - ASSERT_EQ("f", moved[1][1].name()); - ASSERT_EQ("b", moved[1][0].parent().name()); - ASSERT_EQ("b", moved[1][1].parent().name()); - ASSERT_TRUE(&moved[1] == &moved[1][0].parent()); - ASSERT_TRUE(&moved[1] == &moved[1][1].parent()); - ASSERT_TRUE(moved[1][0].isLeaf()); - ASSERT_TRUE(moved[1][1].isLeaf()); - ASSERT_FALSE(moved[1][0].isRoot()); - ASSERT_FALSE(moved[1][1].isRoot()); -} - -/* -------------------------------------------------------- - * Copy constructor - * -------------------------------------------------------- */ - -TEST(CopyConstructor, simple) -{ - Object root("root"); - Object moved(root); - - // test root - ASSERT_TRUE(root.isRoot()); - ASSERT_TRUE(root.isLeaf()); - ASSERT_EQ(0, root.countChildren()); - ASSERT_EQ("root", root.name()); - - // test copy - ASSERT_TRUE(moved.isRoot()); - ASSERT_TRUE(moved.isLeaf()); - ASSERT_EQ(0, moved.countChildren()); - ASSERT_EQ("root", moved.name()); -} - -/* - * @root -> copy - * / \ - * / \ - * a@ @b - */ -TEST(CopyConstructor, oneLevel) -{ - Object root("root"); - - root.append(Object("a")); - root.append(Object("b")); - - Object copy(root); - - ASSERT_TRUE(root.isRoot()); - ASSERT_FALSE(root.isLeaf()); - ASSERT_TRUE(copy.isRoot()); - ASSERT_FALSE(copy.isLeaf()); - ASSERT_EQ("root", root.name()); - ASSERT_EQ("root", copy.name()); - ASSERT_EQ("a", root[0].name()); - ASSERT_EQ("b", root[1].name()); - ASSERT_EQ("a", copy[0].name()); - ASSERT_EQ("b", copy[1].name()); - ASSERT_TRUE(&root == &root[0].parent()); - ASSERT_TRUE(&root == &root[1].parent()); - ASSERT_TRUE(© == ©[0].parent()); - ASSERT_TRUE(© == ©[1].parent()); -} - -/* - * @root -> copy - * / \ - * / \ - * a@ @b - * / \ / \ - * c@ d@ @e @f - * - */ -TEST(CopyConstructor, twoLevels) -{ - Object root("root"); - - root.append(Object("a")); - root.append(Object("b")); - - root[0].append(Object("c")); - root[0].append(Object("d")); - root[1].append(Object("e")); - root[1].append(Object("f")); - - Object copy(root); - - ASSERT_FALSE(root.isLeaf()); - ASSERT_FALSE(copy.isLeaf()); - ASSERT_EQ("root", root.name()); - ASSERT_EQ("root", copy.name()); - ASSERT_EQ("a", root[0].name()); - ASSERT_EQ("a", copy[0].name()); - ASSERT_EQ("c", root[0][0].name()); - ASSERT_EQ("c", copy[0][0].name()); - ASSERT_EQ("d", root[0][1].name()); - ASSERT_EQ("d", copy[0][1].name()); - ASSERT_EQ("b", root[1].name()); - ASSERT_EQ("b", copy[1].name()); - ASSERT_EQ("e", root[1][0].name()); - ASSERT_EQ("e", copy[1][0].name()); - ASSERT_EQ("f", root[1][1].name()); - ASSERT_EQ("f", copy[1][1].name()); - ASSERT_TRUE(&root == &root[0].parent()); - ASSERT_TRUE(&root == &root[1].parent()); - ASSERT_TRUE(© == ©[0].parent()); - ASSERT_TRUE(© == ©[1].parent()); - ASSERT_TRUE(&root[0] == &root[0][0].parent()); - ASSERT_TRUE(&root[0] == &root[0][1].parent()); - ASSERT_TRUE(©[0] == ©[0][0].parent()); - ASSERT_TRUE(©[0] == ©[0][1].parent()); - ASSERT_TRUE(&root[1] == &root[1][0].parent()); - ASSERT_TRUE(&root[1] == &root[1][1].parent()); - ASSERT_TRUE(©[1] == ©[1][0].parent()); - ASSERT_TRUE(©[1] == ©[1][1].parent()); -} - -/* -------------------------------------------------------- - * Move extraction - * -------------------------------------------------------- */ - -/* - * @root - * / \ - * / \ - * a@ @b -> move -> @b - */ -TEST(MoveExtraction, simple) -{ - Object root("root"); - - root.append(Object("a")); - root.append(Object("b")); - - Object moved(std::move(root[1])); - - // test root - ASSERT_TRUE(root.isRoot()); - ASSERT_EQ(2, root.countChildren()); - ASSERT_EQ("root", root.name()); - - // test a - ASSERT_TRUE(root[0].isLeaf()); - ASSERT_FALSE(root[0].isRoot()); - ASSERT_EQ("a", root[0].name()); - ASSERT_EQ(0, root[0].countChildren()); - ASSERT_TRUE(&root == &root[0].parent()); - - // test b - ASSERT_TRUE(root[1].isLeaf()); - ASSERT_FALSE(root[1].isRoot()); - ASSERT_EQ(0, root[1].countChildren()); - ASSERT_TRUE(&root == &root[1].parent()); - - // test moved b - ASSERT_TRUE(moved.isRoot()); - ASSERT_TRUE(moved.isLeaf()); - ASSERT_EQ(0, moved.countChildren()); - ASSERT_EQ("b", moved.name()); -} - -/* - * @root - * / \ - * / \ - * a@ @b -> move - * / \ / \ - * c@ d@ @e @f - * \ - * @g - */ -TEST(MoveExtraction, bigger) -{ - Object root("root"); - - root.append(Object("a")); - root.append(Object("b")); - - root[0].append(Object("c")); - root[0].append(Object("d")); - root[1].append(Object("e")); - root[1].append(Object("f")); - root[1][1].append(Object("g")); - - Object moved(std::move(root[1])); - - // test root - ASSERT_TRUE(root.isRoot()); - ASSERT_FALSE(root.isLeaf()); - ASSERT_EQ(2, root.countChildren()); - ASSERT_EQ("root", root.name()); - - // test a - ASSERT_FALSE(root[0].isRoot()); - ASSERT_FALSE(root[0].isLeaf()); - ASSERT_EQ(2, root[0].countChildren()); - ASSERT_EQ("a", root[0].name()); - ASSERT_TRUE(&root == &root[0].parent()); - - // test c - ASSERT_FALSE(root[0][0].isRoot()); - ASSERT_TRUE(root[0][0].isLeaf()); - ASSERT_EQ(0, root[0][0].countChildren()); - ASSERT_EQ("c", root[0][0].name()); - ASSERT_TRUE(&root[0] == &root[0][0].parent()); - - // test d - ASSERT_FALSE(root[0][1].isRoot()); - ASSERT_TRUE(root[0][1].isLeaf()); - ASSERT_EQ(0, root[0][1].countChildren()); - ASSERT_EQ("d", root[0][1].name()); - ASSERT_TRUE(&root[0] == &root[0][1].parent()); - - // test b - ASSERT_FALSE(root[1].isRoot()); - ASSERT_TRUE(root[1].isLeaf()); - ASSERT_EQ(0, root[1].countChildren()); - ASSERT_TRUE(&root == &root[1].parent()); - - // test moved b - ASSERT_TRUE(moved.isRoot()); - ASSERT_FALSE(moved.isLeaf()); - ASSERT_EQ(2, moved.countChildren()); - ASSERT_EQ("b", moved.name()); - - // test moved b-e - ASSERT_FALSE(moved[0].isRoot()); - ASSERT_TRUE(moved[0].isLeaf()); - ASSERT_EQ(0, moved[0].countChildren()); - ASSERT_EQ("e", moved[0].name()); - ASSERT_TRUE(&moved == &moved[0].parent()); - - // test moved b-f - ASSERT_FALSE(moved[1].isRoot()); - ASSERT_FALSE(moved[1].isLeaf()); - ASSERT_EQ(1, moved[1].countChildren()); - ASSERT_EQ("f", moved[1].name()); - ASSERT_TRUE(&moved == &moved[1].parent()); - - // test moved b-g - ASSERT_FALSE(moved[1][0].isRoot()); - ASSERT_TRUE(moved[1][0].isLeaf()); - ASSERT_EQ(0, moved[1][0].countChildren()); - ASSERT_EQ("g", moved[1][0].name()); - ASSERT_TRUE(&moved[1] == &moved[1][0].parent()); -} - -/* -------------------------------------------------------- - * Copy extraction - * -------------------------------------------------------- */ - -/* - * @root - * / \ - * / \ - * a@ @b -> copy -> @b - */ -TEST(CopyExtraction, simple) -{ - Object root("root"); - - root.append(Object("a")); - root.append(Object("b")); - - Object copy(root[1]); - - // test root - ASSERT_TRUE(root.isRoot()); - ASSERT_EQ(2, root.countChildren()); - ASSERT_EQ("root", root.name()); - - // test a - ASSERT_TRUE(root[0].isLeaf()); - ASSERT_FALSE(root[0].isRoot()); - ASSERT_EQ(0, root[0].countChildren()); - ASSERT_EQ("a", root[0].name()); - ASSERT_TRUE(&root == &root[0].parent()); - - // test b - ASSERT_TRUE(root[1].isLeaf()); - ASSERT_FALSE(root[1].isRoot()); - ASSERT_EQ(0, root[1].countChildren()); - ASSERT_EQ("b", root[1].name()); - ASSERT_TRUE(&root == &root[1].parent()); - - // test copied b - ASSERT_TRUE(copy.isRoot()); - ASSERT_TRUE(copy.isLeaf()); - ASSERT_EQ(0, copy.countChildren()); - ASSERT_EQ("b", copy.name()); -} - -/* - * @root - * / \ - * / \ - * a@ @b -> copy - * / \ / \ - * c@ d@ @e @f - * \ - * @g - */ -TEST(CopyExtraction, bigger) -{ - Object root("root"); - - root.append(Object("a")); - root.append(Object("b")); - - root[0].append(Object("c")); - root[0].append(Object("d")); - root[1].append(Object("e")); - root[1].append(Object("f")); - root[1][1].append(Object("g")); - - Object copy(root[1]); - - // test root - ASSERT_TRUE(root.isRoot()); - ASSERT_FALSE(root.isLeaf()); - ASSERT_EQ(2, root.countChildren()); - ASSERT_EQ("root", root.name()); - - // test a - ASSERT_FALSE(root[0].isRoot()); - ASSERT_FALSE(root[0].isLeaf()); - ASSERT_EQ(2, root[0].countChildren()); - ASSERT_EQ("a", root[0].name()); - ASSERT_TRUE(&root == &root[0].parent()); - - // test c - ASSERT_FALSE(root[0][0].isRoot()); - ASSERT_TRUE(root[0][0].isLeaf()); - ASSERT_EQ(0, root[0][0].countChildren()); - ASSERT_EQ("c", root[0][0].name()); - ASSERT_TRUE(&root[0] == &root[0][0].parent()); - - // test d - ASSERT_FALSE(root[0][1].isRoot()); - ASSERT_TRUE(root[0][1].isLeaf()); - ASSERT_EQ(0, root[0][1].countChildren()); - ASSERT_EQ("d", root[0][1].name()); - ASSERT_TRUE(&root[0] == &root[0][1].parent()); - - // test b - ASSERT_FALSE(root[1].isRoot()); - ASSERT_FALSE(root[1].isLeaf()); - ASSERT_EQ(2, root[1].countChildren()); - ASSERT_TRUE(&root == &root[1].parent()); - - // test e - ASSERT_FALSE(root[1][0].isRoot()); - ASSERT_TRUE(root[1][0].isLeaf()); - ASSERT_EQ(0, root[1][0].countChildren()); - ASSERT_EQ("e", root[1][0].name()); - ASSERT_TRUE(&root[1] == &root[1][0].parent()); - - // test f - ASSERT_FALSE(root[1][1].isRoot()); - ASSERT_FALSE(root[1][1].isLeaf()); - ASSERT_EQ(1, root[1][1].countChildren()); - ASSERT_EQ("f", root[1][1].name()); - ASSERT_TRUE(&root[1] == &root[1][1].parent()); - - // test g - ASSERT_FALSE(root[1][1][0].isRoot()); - ASSERT_TRUE(root[1][1][0].isLeaf()); - ASSERT_EQ(0, root[1][1][0].countChildren()); - ASSERT_EQ("g", root[1][1][0].name()); - ASSERT_TRUE(&root[1][1] == &root[1][1][0].parent()); - - // test copied b - ASSERT_TRUE(copy.isRoot()); - ASSERT_FALSE(copy.isLeaf()); - ASSERT_EQ(2, copy.countChildren()); - ASSERT_EQ("b", copy.name()); - - // test copied b-e - ASSERT_FALSE(copy[0].isRoot()); - ASSERT_TRUE(copy[0].isLeaf()); - ASSERT_EQ(0, copy[0].countChildren()); - ASSERT_EQ("e", copy[0].name()); - ASSERT_TRUE(© == ©[0].parent()); - - // test copied b-f - ASSERT_FALSE(copy[1].isRoot()); - ASSERT_FALSE(copy[1].isLeaf()); - ASSERT_EQ(1, copy[1].countChildren()); - ASSERT_EQ("f", copy[1].name()); - ASSERT_TRUE(© == ©[1].parent()); - - // test copied b-g - ASSERT_FALSE(copy[1][0].isRoot()); - ASSERT_TRUE(copy[1][0].isLeaf()); - ASSERT_EQ(0, copy[1][0].countChildren()); - ASSERT_EQ("g", copy[1][0].name()); - ASSERT_TRUE(©[1] == ©[1][0].parent()); -} - -/* -------------------------------------------------------- - * Move assignment - * -------------------------------------------------------- */ - -/* - * @root -> @moved - */ -TEST(MoveAssignment, simple) -{ - Object root("root"); - Object moved("dummy"); - - moved = std::move(root); - - // test root - ASSERT_TRUE(root.isRoot()); - ASSERT_TRUE(root.isLeaf()); - ASSERT_EQ(0, root.countChildren()); - - // test moved - ASSERT_TRUE(moved.isRoot()); - ASSERT_TRUE(moved.isLeaf()); - ASSERT_EQ(0, moved.countChildren()); - ASSERT_EQ("root", moved.name()); -} - -/* - * @root - * / \ - * / \ - * a@ @b <- move <- @x - * / \ / \ / \ - * c@ d@ @e @f y@ @z - * \ - * @g - */ -TEST(MoveAssignment, bigger) -{ - Object root("root"); - - root.append(Object("a")); - root.append(Object("b")); - - root[0].append(Object("c")); - root[0].append(Object("d")); - root[1].append(Object("e")); - root[1].append(Object("f")); - root[1][1].append(Object("g")); - - Object moved("x"); - - moved.append(Object("y")); - moved.append(Object("z")); - - root[1] = std::move(moved); - - // test root - ASSERT_TRUE(root.isRoot()); - ASSERT_FALSE(root.isLeaf()); - ASSERT_EQ(2, root.countChildren()); - ASSERT_EQ("root", root.name()); - - // test a - ASSERT_FALSE(root[0].isRoot()); - ASSERT_FALSE(root[0].isLeaf()); - ASSERT_EQ(2, root[0].countChildren()); - ASSERT_EQ("a", root[0].name()); - ASSERT_TRUE(&root == &root[0].parent()); - - // test c - ASSERT_FALSE(root[0][0].isRoot()); - ASSERT_TRUE(root[0][0].isLeaf()); - ASSERT_EQ(0, root[0][0].countChildren()); - ASSERT_EQ("c", root[0][0].name()); - ASSERT_TRUE(&root[0] == &root[0][0].parent()); - - // test copied x to b - ASSERT_FALSE(root[1].isRoot()); - ASSERT_FALSE(root[1].isLeaf()); - ASSERT_EQ(2, root[1].countChildren()); - ASSERT_EQ("x", root[1].name()); - ASSERT_TRUE(&root == &root[1].parent()); - - // test y - ASSERT_FALSE(root[1][0].isRoot()); - ASSERT_TRUE(root[1][0].isLeaf()); - ASSERT_EQ(0, root[1][0].countChildren()); - ASSERT_EQ("y", root[1][0].name()); - ASSERT_TRUE(&root[1] == &root[1][0].parent()); - - // test z - ASSERT_FALSE(root[1][1].isRoot()); - ASSERT_TRUE(root[1][1].isLeaf()); - ASSERT_EQ(0, root[1][1].countChildren()); - ASSERT_EQ("z", root[1][1].name()); - ASSERT_TRUE(&root[1] == &root[1][1].parent()); - - // test moved - ASSERT_TRUE(moved.isRoot()); - ASSERT_TRUE(moved.isLeaf()); - ASSERT_EQ(0, moved.countChildren()); -} - -/* -------------------------------------------------------- - * Copy assignment - * -------------------------------------------------------- */ - -/* - * @root -> @copy - */ -TEST(CopyAssignment, simple) -{ - Object root("root"); - Object copy("copy"); - - copy = root; - - // test root - ASSERT_TRUE(root.isRoot()); - ASSERT_TRUE(root.isLeaf()); - ASSERT_EQ(0, root.countChildren()); - ASSERT_EQ("root", root.name()); - - // test copied - ASSERT_TRUE(copy.isRoot()); - ASSERT_TRUE(copy.isLeaf()); - ASSERT_EQ(0, copy.countChildren()); - ASSERT_EQ("root", copy.name()); -} - -/* - * @root - * / \ - * / \ - * a@ @b <- copy <- @x - * / \ / \ / \ - * c@ d@ @e @f y@ @z - * \ - * @g - */ -TEST(CopyAssignment, bigger) -{ - Object root("root"); - - root.append(Object("a")); - root.append(Object("b")); - - root[0].append(Object("c")); - root[0].append(Object("d")); - root[1].append(Object("e")); - root[1].append(Object("f")); - root[1][1].append(Object("g")); - - Object copy("x"); - - copy.append(Object("y")); - copy.append(Object("z")); - - root[1] = copy; - - // test root - ASSERT_TRUE(root.isRoot()); - ASSERT_FALSE(root.isLeaf()); - ASSERT_EQ(2, root.countChildren()); - ASSERT_EQ("root", root.name()); - - // test a - ASSERT_FALSE(root[0].isRoot()); - ASSERT_FALSE(root[0].isLeaf()); - ASSERT_EQ(2, root[0].countChildren()); - ASSERT_EQ("a", root[0].name()); - ASSERT_TRUE(&root == &root[0].parent()); - - // test c - ASSERT_FALSE(root[0][0].isRoot()); - ASSERT_TRUE(root[0][0].isLeaf()); - ASSERT_EQ(0, root[0][0].countChildren()); - ASSERT_EQ("c", root[0][0].name()); - ASSERT_TRUE(&root[0] == &root[0][0].parent()); - - // test copied x to b - ASSERT_FALSE(root[1].isRoot()); - ASSERT_FALSE(root[1].isLeaf()); - ASSERT_EQ(2, root[1].countChildren()); - ASSERT_EQ("x", root[1].name()); - ASSERT_TRUE(&root == &root[1].parent()); - - // test y - ASSERT_FALSE(root[1][0].isRoot()); - ASSERT_TRUE(root[1][0].isLeaf()); - ASSERT_EQ(0, root[1][0].countChildren()); - ASSERT_EQ("y", root[1][0].name()); - ASSERT_TRUE(&root[1] == &root[1][0].parent()); - - // test z - ASSERT_FALSE(root[1][1].isRoot()); - ASSERT_TRUE(root[1][1].isLeaf()); - ASSERT_EQ(0, root[1][1].countChildren()); - ASSERT_EQ("z", root[1][1].name()); - ASSERT_TRUE(&root[1] == &root[1][1].parent()); - - // test original x - ASSERT_TRUE(copy.isRoot()); - ASSERT_FALSE(copy.isLeaf()); - ASSERT_EQ(2, copy.countChildren()); - ASSERT_EQ("x", copy.name()); - - // test original y - ASSERT_FALSE(copy[0].isRoot()); - ASSERT_TRUE(copy[0].isLeaf()); - ASSERT_EQ(0, copy[0].countChildren()); - ASSERT_EQ("y", copy[0].name()); - ASSERT_TRUE(© == ©[0].parent()); - - // test original z - ASSERT_FALSE(copy[1].isRoot()); - ASSERT_TRUE(copy[1].isLeaf()); - ASSERT_EQ(0, copy[1].countChildren()); - ASSERT_EQ("z", copy[1].name()); - ASSERT_TRUE(© == ©[1].parent()); -} - -/* -------------------------------------------------------- - * Remove functions - * -------------------------------------------------------- */ - -TEST(Remove, clear) -{ - Object root("root"); - - root.append(Object("a")); - root.append(Object("b")); - root.append(Object("c")); - root.clear(); - - ASSERT_EQ(0, root.countChildren()); -} - -TEST(Remove, index) -{ - Object root("root"); - - root.append(Object("a")); - root.append(Object("b")); - root.append(Object("c")); - root.remove(2); - - ASSERT_EQ(2, root.countChildren()); - ASSERT_EQ("a", root[0].name()); - ASSERT_EQ("b", root[1].name()); -} - -TEST(Remove, ref) -{ - Object root("root"); - - root.append(Object("a")); - root.append(Object("b")); - root.append(Object("c")); - root.remove(root[2]); - - ASSERT_EQ(2, root.countChildren()); - ASSERT_EQ("a", root[0].name()); - ASSERT_EQ("b", root[1].name()); -} - -TEST(Remove, same) -{ - Object root("root"); - - root.append(Object("a")); - root.append(Object("b")); - root.append(Object("c")); - root.removeSame(Object("c")); - - ASSERT_EQ(2, root.countChildren()); - ASSERT_EQ("a", root[0].name()); - ASSERT_EQ("b", root[1].name()); -} - -/* -------------------------------------------------------- - * Miscellaneous - * -------------------------------------------------------- */ - -TEST(Misc, indexOf) -{ - Object root("root"); - - root.append(Object("a")); - root.append(Object("b")); - root.append(Object("c")); - - ASSERT_EQ(1, root.indexOf(root[1])); -} - -TEST(Misc, indexOfFail) -{ - Object root("root"); - Object notin("c"); - - root.append(Object("a")); - root.append(Object("b")); - root.append(Object("c")); - - ASSERT_EQ(-1, root.indexOf(notin)); -} - -TEST(Misc, indexOfSame) -{ - Object root("root"); - Object same("a"); - - root.append(Object("a")); - root.append(Object("b")); - root.append(Object("c")); - - ASSERT_EQ(0, root.indexOfSame(same)); -} - -TEST(Misc, indexOfSameFail) -{ - Object root("root"); - Object same("xyz"); - - root.append(Object("a")); - root.append(Object("b")); - root.append(Object("c")); - - ASSERT_EQ(-1, root.indexOfSame(same)); -} - -TEST(Misc, inplaceFront) -{ - Object root("root"); - - root.pushNew("a"); - root.pushNew("b"); - - ASSERT_TRUE(root.isRoot()); - ASSERT_FALSE(root.isLeaf()); - ASSERT_EQ(2, root.countChildren()); - ASSERT_EQ("b", root[0].name()); - ASSERT_EQ("a", root[1].name()); -} - -TEST(Misc, inplaceBack) -{ - Object root("root"); - - root.appendNew("a"); - root.appendNew("b"); - - ASSERT_TRUE(root.isRoot()); - ASSERT_FALSE(root.isLeaf()); - ASSERT_EQ(2, root.countChildren()); - ASSERT_EQ("a", root[0].name()); - ASSERT_EQ("b", root[1].name()); -} - -TEST(Misc, map) -{ - Object root("root"); - std::vector<Object> list; - - root.appendNew("a"); - root.appendNew("b"); - - root[0].appendNew("c"); - - root.map([&] (const auto &o) { - list.push_back(o); - }); - - ASSERT_EQ(4, list.size()); - ASSERT_EQ("root", list[0].name()); - ASSERT_EQ("a", list[1].name()); - ASSERT_EQ("c", list[2].name()); - ASSERT_EQ("b", list[3].name()); -} - -TEST(Misc, flat) -{ - Object root("root"); - std::vector<Object> list; - - root.appendNew("a"); - root.appendNew("b"); - - root[0].appendNew("c"); - root.flat(std::back_inserter(list)); - - ASSERT_EQ(4, list.size()); - ASSERT_EQ("root", list[0].name()); - ASSERT_EQ("a", list[1].name()); - ASSERT_EQ("c", list[2].name()); - ASSERT_EQ("b", list[3].name()); -} - -/* -------------------------------------------------------- - * Search - * -------------------------------------------------------- */ - -TEST(Search, returnsValue) -{ - Object root("root"); - - root.appendNew("a"); - root.appendNew("b"); - - root[0].appendNew("c"); - - auto dummy = [] (Object &) {}; - - ASSERT_TRUE(root.search([] (const auto &v) { return v.name() == "a"; }, dummy)); - ASSERT_TRUE(root.search([] (const auto &v) { return v.name() == "b"; }, dummy)); - ASSERT_TRUE(root.search([] (const auto &v) { return v.name() == "c"; }, dummy)); - ASSERT_FALSE(root.search([] (const auto &v) { return v.name() == "notavail"; }, dummy)); -} - -TEST(Search, returns) -{ - Object root("root"); - - root.appendNew("a"); - root.appendNew("b"); - - root[0].appendNew("c"); - - try { - auto &a = root.search([] (const auto &o) { - return o.name() == "a"; - }); - auto &b = root.search([] (const auto &o) { - return o.name() == "b"; - }); - auto &c = root.search([] (const auto &o) { - return o.name() == "c"; - }); - - ASSERT_EQ("a", a.name()); - ASSERT_EQ("b", b.name()); - ASSERT_EQ("c", c.name()); - - ASSERT_TRUE(&a == &root[0]); - ASSERT_TRUE(&b == &root[1]); - ASSERT_TRUE(&c == &root[0][0]); - } catch (const std::exception &ex) { - FAIL() << ex.what(); - } -} - -TEST(Search, returnsFirst) -{ - Object root("root"); - - root.appendNew("a"); - root.appendNew("a"); - root.appendNew("a"); - - try { - auto &value = root.search([] (const auto &v) { - return v.name() == "a"; - }); - - ASSERT_EQ("a", value.name()); - ASSERT_TRUE(&value == &root[0]); - } catch (const std::exception &ex) { - FAIL() << ex.what(); - } -} - -/* -------------------------------------------------------- - * Test inheritance - * -------------------------------------------------------- */ - -class Animal : public TreeNode<Animal> { -public: - virtual ~Animal() = default; - virtual std::string noise() const - { - return "standard"; - } -}; - -class Cat final : public Animal { -public: - std::string noise() const override - { - return "miaou"; - } -}; - -class Dog final : public Animal { -public: - std::string noise() const override - { - return "waouf"; - } -}; - -TEST(Inheritance, basic) -{ - Animal root; - - root.append(Animal()); - root.append(Cat()); - root.append(Dog()); - - ASSERT_EQ("standard", root[0].noise()); - ASSERT_EQ("miaou", root[1].noise()); - ASSERT_EQ("waouf", root[2].noise()); -} - -TEST(Inheritance, copy) -{ - Animal root; - - root.append(Animal()); - root.append(Cat()); - root.append(Dog()); - - ASSERT_EQ("standard", root[0].noise()); - ASSERT_EQ("miaou", root[1].noise()); - ASSERT_EQ("waouf", root[2].noise()); - - Animal copy(root); - - ASSERT_EQ("standard", copy[0].noise()); - ASSERT_EQ("miaou", copy[1].noise()); - ASSERT_EQ("waouf", copy[2].noise()); -} - -int main(int argc, char **argv) -{ - testing::InitGoogleTest(&argc, argv); - - return RUN_ALL_TESTS(); -}
--- a/C++/Tests/Utf8/CMakeLists.txt Sun Mar 08 11:07:36 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,26 +0,0 @@ -# -# CMakeLists.txt -- tests for Utf8 -# -# Copyright (c) 2013, 2014 David Demelier <markand@malikania.fr> -# -# Permission to use, copy, modify, and/or distribute this software for any -# purpose with or without fee is hereby granted, provided that the above -# copyright notice and this permission notice appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF -# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -# - -set( - SOURCES - ${code_SOURCE_DIR}/C++/Utf8.cpp - ${code_SOURCE_DIR}/C++/Utf8.h - main.cpp -) - -define_test(utf8 "${SOURCES}")
--- a/C++/Tests/Utf8/main.cpp Sun Mar 08 11:07:36 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,327 +0,0 @@ -/* - * main.cpp -- main test file for Utf8 - * - * Copyright (c) 2013, 2014 David Demelier <markand@malikania.fr> - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -/* - * /!\ Be sure to keep this file with UTF-8 encoding /!\ - */ - -#include <gtest/gtest.h> - -#include <Utf8.h> - -using namespace testing; - -/* -------------------------------------------------------- - * Conversion UTF32 -> UTF8 - * -------------------------------------------------------- */ - -TEST(Conversion32to8, ascii) -{ - try { - std::u32string u32{'a', 'b', 'c'}; - std::string s = Utf8::toutf8(u32); - - ASSERT_EQ("abc", s); - } catch (const std::exception &ex) { - FAIL() << ex.what(); - } -} - -TEST(Conversion32to8, valid) -{ - try { - std::u32string u32{'a', /* é */ 233, 'c'}; - std::string s = Utf8::toutf8(u32); - - ASSERT_EQ("aéc", s); - } catch (const std::exception &ex) { - FAIL() << ex.what(); - } -} - -TEST(Conversion32to8, invalid) -{ - try { - std::u32string u32{'a', 0xFFFFFFFF, 'c'}; - std::string s = Utf8::toutf8(u32); - - FAIL() << "expected a failure"; - } catch (const std::exception &ex) { - SUCCEED(); - } -} - -/* -------------------------------------------------------- - * Conversion UTF8 -> UTF32 - * -------------------------------------------------------- */ - -TEST(Conversion8to32, ascii) -{ - try { - std::string s{"abc"}; - std::u32string expected{'a', 'b', 'c'}; - std::u32string result = Utf8::toutf32(s); - - ASSERT_EQ(expected, result); - } catch (const std::exception &ex) { - FAIL() << ex.what(); - } -} - -TEST(Conversion8to32, valid) -{ - try { - std::string s{"aéc"}; - std::u32string expected{'a', /* é */ 233, 'c'}; - std::u32string result = Utf8::toutf32(s); - - ASSERT_EQ(expected, result); - } catch (const std::exception &ex) { - FAIL() << ex.what(); - } -} - -/* -------------------------------------------------------- - * UTF32 to upper - * -------------------------------------------------------- */ - -TEST(Toupper32, ascii) -{ - try { - std::u32string u32{'a', 'b', 'c'}; - std::u32string expected{'A', 'B', 'C'}; - std::u32string result = Utf8::toupper(u32); - - ASSERT_EQ(expected, result); - } catch (const std::exception &ex) { - FAIL() << ex.what(); - } -} - -TEST(Toupper32, valid) -{ - try { - std::u32string u32{/* ä */ 228, /* ç */ 231, /* ë */ 235}; - std::u32string expected{/* Ä */ 196, /* Ç */ 199, /* Ë */ 203}; - std::u32string result = Utf8::toupper(u32); - - ASSERT_EQ(expected, result); - } catch (const std::exception &ex) { - FAIL() << ex.what(); - } -} - -TEST(Toupper32, invalid) -{ - try { - std::u32string u32{'a', 0xFFFFFFFF, 'b'}; - std::u32string expected{'A', 0xFFFFFFFF, 'B'}; - std::u32string result = Utf8::toupper(u32); - - ASSERT_EQ(expected, result); - } catch (const std::exception &ex) { - FAIL() << ex.what(); - } -} - -/* -------------------------------------------------------- - * UTF32 to lower - * -------------------------------------------------------- */ - -TEST(Tolower32, ascii) -{ - try { - std::u32string u32{'A', 'B', 'C'}; - std::u32string expected{'a', 'b', 'c'}; - std::u32string result = Utf8::tolower(u32); - - ASSERT_EQ(expected, result); - } catch (const std::exception &ex) { - FAIL() << ex.what(); - } -} - -TEST(Tolower32, valid) -{ - try { - std::u32string u32{/* Ä */ 196, /* Ç */ 199, /* Ë */ 203}; - std::u32string expected{/* ä */ 228, /* ç */ 231, /* ë */ 235}; - std::u32string result = Utf8::tolower(u32); - - ASSERT_EQ(expected, result); - } catch (const std::exception &ex) { - FAIL() << ex.what(); - } -} - -TEST(Tolower32, invalid) -{ - try { - std::u32string u32{'A', 0xFFFFFFFF, 'B'}; - std::u32string expected{'a', 0xFFFFFFFF, 'b'}; - std::u32string result = Utf8::tolower(u32); - - ASSERT_EQ(expected, result); - } catch (const std::exception &ex) { - FAIL() << ex.what(); - } -} - -/* -------------------------------------------------------- - * UTF8 to upper - * -------------------------------------------------------- */ - -TEST(Toupper8, ascii) -{ - try { - std::string s{"abc"}; - std::string r = Utf8::toupper(s); - - ASSERT_EQ("ABC", r); - } catch (const std::exception &ex) { - FAIL() << ex.what(); - } -} - -TEST(Toupper8, valid) -{ - try { - std::string s{"aéc"}; - std::string r = Utf8::toupper(s); - - ASSERT_EQ("AÉC", r); - } catch (const std::exception &ex) { - FAIL() << ex.what(); - } -} - -TEST(Toupper8, invalid) -{ - try { - std::string s{"a" "\xFF""b"}; - std::string r = Utf8::toupper(s); - - FAIL() << "expected a failure"; - } catch (const std::exception &ex) { - SUCCEED(); - } -} - -/* -------------------------------------------------------- - * UTF8 to lower - * -------------------------------------------------------- */ - -TEST(Tolower8, ascii) -{ - try { - std::string s{"ABC"}; - std::string r = Utf8::tolower(s); - - ASSERT_EQ("abc", r); - } catch (const std::exception &ex) { - FAIL() << ex.what(); - } -} - -TEST(Tolower8, valid) -{ - try { - std::string s{"AÉC"}; - std::string r = Utf8::tolower(s); - - ASSERT_EQ("aéc", r); - } catch (const std::exception &ex) { - FAIL() << ex.what(); - } -} - -TEST(Tolower8, invalid) -{ - try { - std::string s{"A" "\xFF""B"}; - std::string r = Utf8::tolower(s); - - FAIL() << "expected a failure"; - } catch (const std::exception &ex) { - SUCCEED(); - } -} - -/* -------------------------------------------------------- - * Check functions - * -------------------------------------------------------- */ - -TEST(Check, isspace) -{ - ASSERT_TRUE(Utf8::isspace(' ')); - ASSERT_FALSE(Utf8::isspace(/* é */ 233)); -} - -TEST(Check, isletter) -{ - ASSERT_TRUE(Utf8::isletter(/* é */ 233)); - ASSERT_FALSE(Utf8::isletter(/* € */ 8364)); -} - -TEST(Check, isupper) -{ - ASSERT_FALSE(Utf8::isupper('a')); - ASSERT_FALSE(Utf8::isupper(/* é */ 233)); - ASSERT_TRUE(Utf8::isupper('A')); - ASSERT_TRUE(Utf8::isupper(/* É */ 201)); -} - -TEST(Check, islower) -{ - ASSERT_TRUE(Utf8::islower('a')); - ASSERT_TRUE(Utf8::islower(/* é */ 233)); - ASSERT_FALSE(Utf8::islower('A')); - ASSERT_FALSE(Utf8::islower(/* É */ 201)); -} - -/* -------------------------------------------------------- - * Miscellaneous - * -------------------------------------------------------- */ - -TEST(Misc, nbytesPoint) -{ - ASSERT_EQ(1, Utf8::nbytesPoint('a')); - ASSERT_EQ(2, Utf8::nbytesPoint(/* é */ 233)); - ASSERT_EQ(3, Utf8::nbytesPoint(/* € */ 8364)); - ASSERT_EQ(4, Utf8::nbytesPoint(/* 𠀀 */ 131072)); -} - -TEST(Misc, nbytesUtf8) -{ - std::string s1{"a"}; - std::string s2{"é"}; - std::string s3{"€"}; - std::string s4{"𠀀"}; - - ASSERT_EQ(1, Utf8::nbytesUtf8(s1[0])); - ASSERT_EQ(2, Utf8::nbytesUtf8(s2[0])); - ASSERT_EQ(3, Utf8::nbytesUtf8(s3[0])); - ASSERT_EQ(4, Utf8::nbytesUtf8(s4[0])); -} - -int main(int argc, char **argv) -{ - InitGoogleTest(&argc, argv); - - return RUN_ALL_TESTS(); -}
--- a/C++/Tests/Xdg/CMakeLists.txt Sun Mar 08 11:07:36 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,26 +0,0 @@ -# -# CMakeLists.txt -- tests for XDG -# -# Copyright (c) 2013, 2014 David Demelier <markand@malikania.fr> -# -# Permission to use, copy, modify, and/or distribute this software for any -# purpose with or without fee is hereby granted, provided that the above -# copyright notice and this permission notice appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF -# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -# - -set( - SOURCES - ${code_SOURCE_DIR}/C++/Xdg.cpp - ${code_SOURCE_DIR}/C++/Xdg.h - main.cpp -) - -define_test(xdg "${SOURCES}")
--- a/C++/Tests/Xdg/main.cpp Sun Mar 08 11:07:36 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,340 +0,0 @@ -/* - * main.cpp -- main test file for XDG - * - * Copyright (c) 2013, 2014 David Demelier <markand@malikania.fr> - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include <gtest/gtest.h> - -#include <Xdg.h> - -using namespace testing; - -namespace { - -std::string myhome; - -} - -TEST(HomeEmpty, config) -{ - ASSERT_TRUE(unsetenv("XDG_CONFIG_HOME") == 0); - - try { - Xdg xdg; - - ASSERT_EQ(myhome + "/.config", xdg.configHome()); - } catch (const std::exception &) { - ASSERT_TRUE(false); - } -} - -TEST(HomeEmpty, data) -{ - ASSERT_TRUE(unsetenv("XDG_DATA_HOME") == 0); - - try { - Xdg xdg; - - ASSERT_EQ(myhome + "/.local/share", xdg.dataHome()); - } catch (const std::exception &) { - ASSERT_TRUE(false); - } -} - -TEST(HomeEmpty, cache) -{ - ASSERT_TRUE(unsetenv("XDG_CACHE_HOME") == 0); - - try { - Xdg xdg; - - ASSERT_EQ(myhome + "/.cache", xdg.cacheHome()); - } catch (const std::exception &) { - ASSERT_TRUE(false); - } -} - -TEST(HomeEmpty, runtime) -{ - ASSERT_TRUE(unsetenv("XDG_RUNTIME_DIR") == 0); - - try { - Xdg xdg; - - try { - xdg.runtimeDir(); - - ASSERT_TRUE(false); - } catch (const std::exception &) { } - } catch (const std::exception &) { - ASSERT_TRUE(false); - } -} - -TEST(HomeValid, config) -{ - ASSERT_TRUE(setenv("XDG_CONFIG_HOME", "/test/config", true) == 0); - - try { - Xdg xdg; - - ASSERT_EQ("/test/config", xdg.configHome()); - } catch (const std::exception &) { - ASSERT_TRUE(false); - } -} - -TEST(HomeValid, data) -{ - ASSERT_TRUE(setenv("XDG_DATA_HOME", "/test/data", true) == 0); - - try { - Xdg xdg; - - ASSERT_EQ("/test/data", xdg.dataHome()); - } catch (const std::exception &) { - ASSERT_TRUE(false); - } -} - -TEST(HomeValid, cache) -{ - ASSERT_TRUE(setenv("XDG_CACHE_HOME", "/test/cache", true) == 0); - - try { - Xdg xdg; - - ASSERT_EQ("/test/cache", xdg.cacheHome()); - } catch (const std::exception &) { - ASSERT_TRUE(false); - } -} - -TEST(HomeValid, runtime) -{ - ASSERT_TRUE(setenv("XDG_RUNTIME_DIR", "/test/runtime", true) == 0); - - try { - Xdg xdg; - - ASSERT_EQ("/test/runtime", xdg.runtimeDir()); - } catch (const std::exception &) { - ASSERT_TRUE(false); - } -} - -TEST(HomeInvalid, config) -{ - ASSERT_TRUE(setenv("XDG_CONFIG_HOME", "invalid", true) == 0); - - try { - Xdg xdg; - - ASSERT_EQ(myhome + "/.config", xdg.configHome()); - } catch (const std::exception &) { - ASSERT_TRUE(false); - } -} - -TEST(HomeInvalid, data) -{ - ASSERT_TRUE(setenv("XDG_DATA_HOME", "invalid", true) == 0); - - try { - Xdg xdg; - - ASSERT_EQ(myhome + "/.local/share", xdg.dataHome()); - } catch (const std::exception &) { - ASSERT_TRUE(false); - } -} - -TEST(HomeInvalid, cache) -{ - ASSERT_TRUE(setenv("XDG_CACHE_HOME", "invalid", true) == 0); - - try { - Xdg xdg; - - ASSERT_EQ(myhome + "/.cache", xdg.cacheHome()); - } catch (const std::exception &) { - ASSERT_TRUE(false); - } -} - -TEST(HomeInvalid, runtime) -{ - ASSERT_TRUE(setenv("XDG_RUNTIME_DIR", "invalid", true) == 0); - - try { - Xdg xdg; - - try { - xdg.runtimeDir(); - - ASSERT_TRUE(false); - } catch (const std::exception &) { } - } catch (const std::exception &) { - ASSERT_TRUE(false); - } -} - -TEST(DirectoriesEmpty, config) -{ - ASSERT_TRUE(unsetenv("XDG_CONFIG_DIRS") == 0); - - try { - Xdg xdg; - - const auto &list = xdg.configDirs(); - - ASSERT_EQ((size_t)1, list.size()); - ASSERT_EQ("/etc/xdg", list[0]); - } catch (const std::exception &) { - ASSERT_TRUE(false); - } -} - -TEST(DirectoriesEmpty, data) -{ - ASSERT_TRUE(unsetenv("XDG_DATA_DIRS") == 0); - - try { - Xdg xdg; - - const auto &list = xdg.dataDirs(); - - ASSERT_EQ((size_t)2, list.size()); - ASSERT_EQ("/usr/local/share", list[0]); - ASSERT_EQ("/usr/share", list[1]); - } catch (const std::exception &) { - ASSERT_TRUE(false); - } -} - -TEST(DirectoriesValid, config) -{ - ASSERT_TRUE(setenv("XDG_CONFIG_DIRS", "/config1:/config2", true) == 0); - - try { - Xdg xdg; - - const auto &list = xdg.configDirs(); - - ASSERT_EQ((size_t)2, list.size()); - ASSERT_EQ("/config1", list[0]); - ASSERT_EQ("/config2", list[1]); - } catch (const std::exception &) { - ASSERT_TRUE(false); - } -} - -TEST(DirectoriesValid, data) -{ - ASSERT_TRUE(setenv("XDG_DATA_DIRS", "/data1:/data2", true) == 0); - - try { - Xdg xdg; - - const auto &list = xdg.dataDirs(); - - ASSERT_EQ((size_t)2, list.size()); - ASSERT_EQ("/data1", list[0]); - ASSERT_EQ("/data2", list[1]); - } catch (const std::exception &) { - ASSERT_TRUE(false); - } -} - -TEST(DirectoriesInvalid, config) -{ - ASSERT_TRUE(setenv("XDG_CONFIG_DIRS", "bad1:bad2", true) == 0); - - try { - Xdg xdg; - - const auto &list = xdg.configDirs(); - - ASSERT_EQ((size_t)1, list.size()); - ASSERT_EQ("/etc/xdg", list[0]); - } catch (const std::exception &) { - ASSERT_TRUE(false); - } -} - -TEST(DirectoriesInvalid, data) -{ - ASSERT_TRUE(setenv("XDG_DATA_DIRS", "bad1:bad2", true) == 0); - - try { - Xdg xdg; - - const auto &list = xdg.dataDirs(); - - ASSERT_EQ((size_t)2, list.size()); - ASSERT_EQ("/usr/local/share", list[0]); - ASSERT_EQ("/usr/share", list[1]); - } catch (const std::exception &) { - ASSERT_TRUE(false); - } -} - -TEST(DirectoriesMixed, config) -{ - ASSERT_TRUE(setenv("XDG_CONFIG_DIRS", "/config1:bad:/config2", true) == 0); - - try { - Xdg xdg; - - const auto &list = xdg.configDirs(); - - ASSERT_EQ((size_t)2, list.size()); - ASSERT_EQ("/config1", list[0]); - ASSERT_EQ("/config2", list[1]); - } catch (const std::exception &) { - ASSERT_TRUE(false); - } -} - -TEST(DirectoriesMixed, data) -{ - ASSERT_TRUE(setenv("XDG_DATA_DIRS", "/data1:bad:/data2", true) == 0); - - try { - Xdg xdg; - - const auto &list = xdg.dataDirs(); - - ASSERT_EQ((size_t)2, list.size()); - ASSERT_EQ("/data1", list[0]); - ASSERT_EQ("/data2", list[1]); - } catch (const std::exception &) { - ASSERT_TRUE(false); - } -} - -int main(int argc, char **argv) -{ - auto home = getenv("HOME"); - - if (home == nullptr) - return 0; - - myhome = home; - InitGoogleTest(&argc, argv); - - return RUN_ALL_TESTS(); -}
--- a/C++/Tests/Zip/CMakeLists.txt Sun Mar 08 11:07:36 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,35 +0,0 @@ -# -# CMakeLists.txt -- tests for Zip -# -# Copyright (c) 2013, 2014 David Demelier <markand@malikania.fr> -# -# Permission to use, copy, modify, and/or distribute this software for any -# purpose with or without fee is hereby granted, provided that the above -# copyright notice and this permission notice appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF -# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -# - -project(zip) - -find_package(ZIP REQUIRED) - -set( - SOURCES - ${code_SOURCE_DIR}/C++/ZipArchive.cpp - ${code_SOURCE_DIR}/C++/ZipArchive.h - ${zip_SOURCE_DIR}/data/data.txt - main.cpp -) - -define_test(zip "${SOURCES}") - -target_include_directories(zip PRIVATE ${ZIP_INCLUDE_DIRS}) -target_link_libraries(zip ${ZIP_LIBRARIES}) -target_compile_definitions(zip PRIVATE "SOURCE=\"${zip_SOURCE_DIR}\"" "BINARY=\"${zip_BINARY_DIR}\"")
--- a/C++/Tests/Zip/data/data.txt Sun Mar 08 11:07:36 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1 +0,0 @@ -abcdef
--- a/C++/Tests/Zip/main.cpp Sun Mar 08 11:07:36 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,270 +0,0 @@ -/* - * main.cpp -- test the zip wrapper functions - * - * Copyright (c) 2013, 2014 David Demelier <markand@malikania.fr> - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include <gtest/gtest.h> - -#include <ZipArchive.h> - -using namespace source; - -/* -------------------------------------------------------- - * Sources - * -------------------------------------------------------- */ - -TEST(Source, file) -{ - remove(BINARY "/output.zip"); - - try { - ZipArchive archive{BINARY "/output.zip", ZIP_CREATE}; - - archive.add(File{SOURCE "/data/data.txt"}, "data.txt"); - } catch (const std::exception &ex) { - std::cerr << ex.what() << std::endl; - } - - try { - ZipArchive archive{BINARY "/output.zip"}; - - auto stats = archive.stat("data.txt"); - auto file = archive.open("data.txt"); - auto content = file.read(stats.size); - - ASSERT_EQ("abcdef\n", content); - } catch (const std::exception &ex) { - std::cerr << ex.what() << std::endl; - } -} - -TEST(Source, buffer) -{ - remove(BINARY "/output.zip"); - - try { - ZipArchive archive{BINARY "/output.zip", ZIP_CREATE}; - - archive.add(Buffer{"abcdef"}, "data.txt"); - } catch (const std::exception &ex) { - std::cerr << ex.what() << std::endl; - } - - try { - ZipArchive archive{BINARY "/output.zip"}; - - auto stats = archive.stat("data.txt"); - auto file = archive.open("data.txt"); - auto content = file.read(stats.size); - - ASSERT_EQ("abcdef", content); - } catch (const std::exception &ex) { - std::cerr << ex.what() << std::endl; - } -} - -/* -------------------------------------------------------- - * Write - * -------------------------------------------------------- */ - -TEST(Write, simple) -{ - remove(BINARY "/output.zip"); - - // Open first and save some data - try { - ZipArchive archive{BINARY "/output.zip", ZIP_CREATE}; - - archive.add(Buffer{"hello world!"}, "DATA"); - } catch (const std::exception &ex) { - std::cerr << "warning: " << ex.what() << std::endl; - } - - try { - ZipArchive archive{BINARY "/output.zip"}; - - auto stats = archive.stat("DATA"); - auto file = archive.open("DATA"); - auto content = file.read(stats.size); - - ASSERT_EQ(static_cast<decltype(stats.size)>(12), stats.size); - ASSERT_EQ("hello world!", content); - } catch (const std::exception &ex) { - std::cerr << "warning: " << ex.what() << std::endl; - } -} - -/* -------------------------------------------------------- - * Reading - * -------------------------------------------------------- */ - -class ReadingTest : public testing::Test { -protected: - ZipArchive m_archive; - -public: - ReadingTest() - : m_archive(SOURCE "/data/stats.zip") - { - } -}; - -TEST_F(ReadingTest, numEntries) -{ - ASSERT_EQ(static_cast<ZipInt64>(4), m_archive.numEntries()); -} - -TEST_F(ReadingTest, stat) -{ - try { - ZipStat stats = m_archive.stat("README"); - - ASSERT_EQ(static_cast<decltype(stats.size)>(15), stats.size); - ASSERT_STREQ("README", stats.name); - } catch (const std::exception &ex) { - std::cerr << ex.what() << std::endl; - } -} - -TEST_F(ReadingTest, read) -{ - try { - auto file = m_archive.open("README"); - auto stats = m_archive.stat("README"); - auto text = file.read(stats.size); - - ASSERT_EQ("This is a test\n", text); - } catch (const std::exception &ex) { - std::cerr << "warning: " << ex.what() << std::endl; - } -} - -TEST_F(ReadingTest, increment) -{ - { - ZipArchive::iterator it = m_archive.begin(); - - ASSERT_STREQ("README", (*it++).name); - } - - { - ZipArchive::iterator it = m_archive.begin(); - - ASSERT_STREQ("INSTALL", (*++it).name); - } - - { - ZipArchive::iterator it = m_archive.begin() + 1; - - ASSERT_STREQ("INSTALL", (*it).name); - } -} - -TEST_F(ReadingTest, decrement) -{ - { - ZipArchive::iterator it = m_archive.begin() + 1; - - ASSERT_STREQ("INSTALL", (*it--).name); - } - - { - ZipArchive::iterator it = m_archive.begin() + 1; - - ASSERT_STREQ("README", (*--it).name); - } - - { - ZipArchive::iterator it = m_archive.end() - 1; - - ASSERT_STREQ("doc/REFMAN", (*it).name); - } -} - -TEST_F(ReadingTest, constIncrement) -{ - { - ZipArchive::const_iterator it = m_archive.cbegin(); - - ASSERT_STREQ("README", (*it++).name); - } - - { - ZipArchive::const_iterator it = m_archive.cbegin(); - - ASSERT_STREQ("INSTALL", (*++it).name); - } - - { - ZipArchive::const_iterator it = m_archive.cbegin() + 1; - - ASSERT_STREQ("INSTALL", (*it).name); - } -} - -TEST_F(ReadingTest, constDecrement) -{ - { - ZipArchive::const_iterator it = m_archive.cbegin() + 1; - - ASSERT_STREQ("INSTALL", (*it--).name); - } - - { - ZipArchive::const_iterator it = m_archive.cbegin() + 1; - - ASSERT_STREQ("README", (*--it).name); - } - - { - ZipArchive::const_iterator it = m_archive.cend() - 1; - - ASSERT_STREQ("doc/REFMAN", (*it).name); - } -} - -TEST_F(ReadingTest, access) -{ - { - ZipArchive::iterator it = m_archive.begin(); - - ASSERT_STREQ("README", it->name); - ASSERT_STREQ("INSTALL", it[1].name); - } - - { - ZipArchive::const_iterator it = m_archive.cbegin(); - - ASSERT_STREQ("README", it->name); - ASSERT_STREQ("INSTALL", it[1].name); - } -} - -TEST_F(ReadingTest, loop) -{ - std::vector<std::string> names{"README", "INSTALL", "doc/", "doc/REFMAN"}; - int i = 0; - - for (const ZipStat &s : m_archive) - ASSERT_STREQ(names[i++].c_str(), s.name); -} - -int main(int argc, char **argv) -{ - testing::InitGoogleTest(&argc, argv); - - return RUN_ALL_TESTS(); -}
--- a/C++/TreeNode.h Sun Mar 08 11:07:36 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,524 +0,0 @@ -/* - * TreeNode.h -- C++11 pointer-free N-ary tree - * - * Copyright (c) 2013, 2014 David Demelier <markand@malikania.fr> - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#ifndef _TREE_NODE_H_ -#define _TREE_NODE_H_ - -/** - * @file TreeNode.h - * @brief N-ary tree without pointers - */ - -#include <algorithm> -#include <deque> -#include <memory> -#include <stdexcept> -#include <type_traits> - -namespace { - -/** - * @class TypeTraits - * @brief Some checks on the type - * - * Provides the following members depending on the type: - * - * equalityComparable - If == comparison can be performed - */ -template <typename T> -class TypeTraits { -private: - /** - * @class HasEqualsTo - * @brief Check if the type is comparable - * - * Sets to true if the type T can is equality comparable. - */ - template <typename U> - class HasEqualsTo { - public: - using Yes = char [2]; - using No = char [1]; - - static_assert(sizeof (char) != sizeof (long), "buy a new compiler"); - - template <typename Value> - static constexpr Yes &check(Value *u, decltype(*u == *u) * = nullptr); - - static constexpr No &check(...); - - static constexpr const bool value = sizeof (check((U *)0)) == sizeof (Yes); - }; - -public: - static constexpr const bool equalityComparable = HasEqualsTo<T>::value; -}; - -} // !namespace - -/** - * @class TreeNode - * @brief Safe C++11 N-ary tree - * - * This class use a std::deque as the container, it allocate object on the heap to avoid useless - * copy and move when adding new elements. - */ -template <typename T> -class TreeNode { -private: - class Object { - public: - virtual T &get() = 0; - virtual std::unique_ptr<Object> clone() const = 0; - }; - - template <typename Value> - struct Proxy final : public Object { - private: - Value m_value; - - Proxy(const Proxy &) = delete; - Proxy &operator=(const Proxy &) = delete; - Proxy(Proxy &&) = delete; - Proxy &operator=(Proxy &&) = delete; - - public: - inline Proxy(Value &&value) - : m_value(std::move(value)) - { - } - - inline Proxy(const Value &value) - : m_value(value) - { - } - - template <typename... Args> - inline Proxy(Args&&... args) - : m_value(std::forward<Args>(args)...) - { - } - - T &get() override - { - return m_value; - } - - std::unique_ptr<Object> clone() const override - { - return std::make_unique<Proxy<Value>>(m_value); - } - }; - - using Container = std::deque<std::unique_ptr<Object>>; - - TreeNode *m_parent{nullptr}; - Container m_children; - -public: - /** - * Default constructor. - */ - TreeNode() = default; - - /** - * Default destructor. - */ - virtual ~TreeNode() = default; - - /** - * Copy constructor. - * - * @param other the other node - */ - TreeNode(const TreeNode &other) - : m_parent(nullptr) - { - for (const auto &c : other.m_children) { - m_children.push_back(c->clone()); - m_children.back()->get().m_parent = this; - } - } - - /** - * Move constructor. - * - * @param other the other node - */ - TreeNode(TreeNode &&other) - : m_parent(nullptr) - , m_children(std::move(other.m_children)) - { - // Update children to update to *this - for (auto &c : m_children) - c->get().m_parent = this; - - other.m_children.clear(); - } - - /** - * Copy assignment operator. - * - * @param other the other node - */ - TreeNode &operator=(const TreeNode &other) - { - m_children.clear(); - - for (const auto &c : other.m_children) { - m_children.push_back(c->clone()); - m_children.back()->get().m_parent = this; - } - - return *this; - } - - /** - * Move assignment operator. - * - * @param other the other node - */ - TreeNode &operator=(TreeNode &&other) - { - m_children = std::move(other.m_children); - - // Update children to update to *this - for (auto &c : m_children) - c->get().m_parent = this; - - other.m_children.clear(); - - return *this; - } - - /** - * Add a child node to the beginning. - * - * @param child the children - */ - template <typename Value> - inline void push(const Value &child) - { - m_children.push_front(std::make_unique<Proxy<Value>>(child)); - m_children.front()->get().m_parent = this; - } - - /** - * Move to the beginning. - * - * @param child the children - */ - template <typename Value> - inline void push(Value &&child, typename std::enable_if<std::is_rvalue_reference<Value &&>::value>::type * = nullptr) - { - using Type = typename std::decay<Value>::type; - - m_children.push_front(std::make_unique<Proxy<Type>>(std::move(child))); - m_children.front()->get().m_parent = this; - } - - /** - * Construct an element at the beginning in place. - * - * @param args the arguments - */ - template <typename... Args> - void pushNew(Args&&... args) - { - m_children.emplace_front(std::make_unique<Proxy<T>>(std::forward<Args>(args)...)); - m_children.front()->get().m_parent = this; - } - - /** - * Add a child node to the end - * - * @param child the children - */ - template <typename Value> - inline void append(const Value &child) - { - m_children.push_back(std::make_unique<Proxy<Value>>(child)); - m_children.back()->get().m_parent = this; - } - - /** - * Move a child node to the end - * - * @param child the children - */ - template <typename Value> - inline void append(Value &&child, typename std::enable_if<std::is_rvalue_reference<Value &&>::value>::type * = nullptr) - { - using Type = typename std::decay<Value>::type; - - m_children.push_back(std::make_unique<Proxy<Type>>(std::move(child))); - m_children.back()->get().m_parent = this; - } - - /** - * Construct an element at the end in place. - * - * @param args the arguments - */ - template <typename... Args> - void appendNew(Args&&... args) - { - m_children.emplace_back(std::make_unique<Proxy<T>>(std::forward<Args>(args)...)); - m_children.back()->get().m_parent = this; - } - - /** - * Count the number of children in this node. - * - * @return the number of children - */ - unsigned countChildren() const noexcept - { - return static_cast<unsigned>(m_children.size()); - } - - /** - * Get the parent node. - * - * @return the parent node - * @throw std::out_of_range if there is no parent - */ - T &parent() - { - if (!m_parent) - throw std::out_of_range("no parent"); - - return static_cast<T &>(*m_parent); - } - - /** - * Get the parent node. - * - * @return the parent node - * @throw std::out_of_range if there is no parent - */ - const T &parent() const - { - if (!m_parent) - throw std::out_of_range("no parent"); - - return static_cast<const T &>(*m_parent); - } - - /** - * Check if the node is root (no parent). - * - * @return true if root - */ - bool isRoot() const noexcept - { - return m_parent == nullptr; - } - - /** - * Check if the node is leaf (no children). - * - * @return true if leaf - */ - bool isLeaf() const noexcept - { - return m_children.size() == 0; - } - - /** - * Remove a child from the node at the given index. - * - * @param index the position index - * @throw std::out_of_range if index is out of bounds - */ - void remove(int index) - { - if (index < 0 || index >= m_children.size()) - throw std::out_of_range("index is out of range"); - - m_children.erase(m_children.begin() + index); - } - - /** - * Remove a child from the node, the child must exists and no comparison test is performed, only - * object addresses are compared. - * - * @param value the value that exists in the node - * @warn the removed object must not be used after the call - */ - void remove(T &value) - { - m_children.erase(std::remove_if(m_children.begin(), m_children.end(), [&] (auto &p) { - return &p->get() == &value; - }), m_children.end()); - } - - /** - * Remove a child from the node, the value is tested using operator== and therefore may not exist in the container. - * - * @param value the value that can be compared - * @warn the removed object must not be used after the call - */ - template <typename Value> - void removeSame(const Value &value, typename std::enable_if<TypeTraits<Value>::equalityComparable>::type * = nullptr) - { - m_children.erase(std::remove_if(m_children.begin(), m_children.end(), [&] (auto &p) { - return p->get() == value; - }), m_children.end()); - } - - /** - * Remove all children. - */ - void clear() - { - m_children.clear(); - } - - /** - * Find a child in this node, the child address is used as the comparison so no equality operator is even called. - * - * @param child the child - * @return the index or -1 if not found - * @see indexOfSame - */ - int indexOf(const T &child) const noexcept - { - for (unsigned i = 0; i < m_children.size(); ++i) - if (&m_children[i]->get() == &child) - return i; - - return -1; - } - - /** - * Find the index of a node that is equality comparable to value but may be not in the node. - * - * @param value the value to compare - * @return the index or -1 if not found - * @see indexOf - */ - template <typename Value> - int indexOfSame(const Value &value, typename std::enable_if<TypeTraits<Value>::equalityComparable>::type * = nullptr) const noexcept - { - for (unsigned i = 0; i < m_children.size(); ++i) - if (m_children[i]->get() == value) - return i; - - return -1; - } - - /** - * Iterate over all the nodes. The first node is also passed through - * the callback. - * - * @param callback the callback - */ - template <typename Callback> - void map(Callback callback) - { - callback(static_cast<T &>(*this)); - - for (auto &v : m_children) - v->get().map(callback); - } - - /** - * Convert the tree to a flat list by appending to the output iterator. - * - * @param dest the destination iterator - */ - template <typename OutputIt> - void flat(OutputIt dest) - { - map([&] (const auto &value) { - *dest++ = value; - }); - } - - /** - * Iterate all values and call the function when found. - * - * @param predicate the predicate - * @param callable the callable to call when found - * @return true if found - */ - template <typename UnaryPredicate, typename Callable> - bool search(UnaryPredicate predicate, Callable callable) - { - if (predicate(static_cast<const T &>(*this))) { - callable(static_cast<T &>(*this)); - return true; - } - - for (auto &v : m_children) { - if (v->get().search(predicate, callable)) - return true; - } - - return false; - } - - /** - * Search and return the first value matching the predicate. - * - * @param predicate the predicate - * @return the reference to the node - * @throw std::out_of_range if not found - */ - template <typename UnaryPredicate> - T &search(UnaryPredicate predicate) - { - T *value{nullptr}; - - search(predicate, [&] (auto &ptr) { - value = &ptr; - }); - - if (value == nullptr) - throw std::out_of_range("node not found"); - - return *value; - } - - /** - * Access a child. - * - * @param index the index - * @return the reference to the children node - * @throw std::out_of_range on out of bounds - */ - T &operator[](int index) - { - return static_cast<T &>(m_children.at(index)->get()); - } - - /** - * Access a child. - * - * @param index the index - * @return the reference to the children node - * @throw std::out_of_range on out of bounds - */ - const T &operator[](int index) const - { - return static_cast<const T &>(m_children.at(index)->get()); - } -}; - -#endif // !_TREE_NODE_H_
--- a/C++/Utf8.cpp Sun Mar 08 11:07:36 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,4619 +0,0 @@ -/* - * Utf8.cpp -- UTF-8 to UTF-32 conversions - * - * Copyright (c) 2013, 2014 David Demelier <markand@malikania.fr> - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include "Utf8.h" - -namespace { - -#define LEN(x) (sizeof (x) / sizeof (x[0])) - -static uint32_t * -rbsearch(uint32_t c, uint32_t *t, int n, int ne) -{ - uint32_t *p; - int m; - - while (n > 1) { - m = n >> 1; - p = t + m * ne; - - if (c >= p[0]) { - t = p; - n = n - m; - } else - n = m; - } - - if (n && c >= t[0]) - return t; - return 0; -} - -/* - * The following values have been generated from Go mkrunetype.c - * - * http://golang.org/src/lib9/utf/mkrunetype.c - */ - -/* {{{ Spaces */ - -static uint32_t isspacer[] = { - 0x0009, 0x000d, - 0x0020, 0x0020, - 0x0085, 0x0085, - 0x00a0, 0x00a0, - 0x1680, 0x1680, - 0x2000, 0x200a, - 0x2028, 0x2029, - 0x202f, 0x202f, - 0x205f, 0x205f, - 0x3000, 0x3000, - 0xfeff, 0xfeff, -}; - -/* }}} */ - -/* {{{ Digits */ - -static uint32_t isdigitr[] = { - 0x0030, 0x0039, - 0x0660, 0x0669, - 0x06f0, 0x06f9, - 0x07c0, 0x07c9, - 0x0966, 0x096f, - 0x09e6, 0x09ef, - 0x0a66, 0x0a6f, - 0x0ae6, 0x0aef, - 0x0b66, 0x0b6f, - 0x0be6, 0x0bef, - 0x0c66, 0x0c6f, - 0x0ce6, 0x0cef, - 0x0d66, 0x0d6f, - 0x0e50, 0x0e59, - 0x0ed0, 0x0ed9, - 0x0f20, 0x0f29, - 0x1040, 0x1049, - 0x1090, 0x1099, - 0x17e0, 0x17e9, - 0x1810, 0x1819, - 0x1946, 0x194f, - 0x19d0, 0x19d9, - 0x1a80, 0x1a89, - 0x1a90, 0x1a99, - 0x1b50, 0x1b59, - 0x1bb0, 0x1bb9, - 0x1c40, 0x1c49, - 0x1c50, 0x1c59, - 0xa620, 0xa629, - 0xa8d0, 0xa8d9, - 0xa900, 0xa909, - 0xa9d0, 0xa9d9, - 0xaa50, 0xaa59, - 0xabf0, 0xabf9, - 0xff10, 0xff19, - 0x104a0, 0x104a9, - 0x11066, 0x1106f, - 0x110f0, 0x110f9, - 0x11136, 0x1113f, - 0x111d0, 0x111d9, - 0x116c0, 0x116c9, - 0x1d7ce, 0x1d7ff, -}; - -/* }}} */ - -/* {{{ Unicode letters */ - -static uint32_t isalphar[] = { - 0x0041, 0x005a, - 0x0061, 0x007a, - 0x00c0, 0x00d6, - 0x00d8, 0x00f6, - 0x00f8, 0x02c1, - 0x02c6, 0x02d1, - 0x02e0, 0x02e4, - 0x0370, 0x0374, - 0x0376, 0x0377, - 0x037a, 0x037d, - 0x0388, 0x038a, - 0x038e, 0x03a1, - 0x03a3, 0x03f5, - 0x03f7, 0x0481, - 0x048a, 0x0527, - 0x0531, 0x0556, - 0x0561, 0x0587, - 0x05d0, 0x05ea, - 0x05f0, 0x05f2, - 0x0620, 0x064a, - 0x066e, 0x066f, - 0x0671, 0x06d3, - 0x06e5, 0x06e6, - 0x06ee, 0x06ef, - 0x06fa, 0x06fc, - 0x0712, 0x072f, - 0x074d, 0x07a5, - 0x07ca, 0x07ea, - 0x07f4, 0x07f5, - 0x0800, 0x0815, - 0x0840, 0x0858, - 0x08a2, 0x08ac, - 0x0904, 0x0939, - 0x0958, 0x0961, - 0x0971, 0x0977, - 0x0979, 0x097f, - 0x0985, 0x098c, - 0x098f, 0x0990, - 0x0993, 0x09a8, - 0x09aa, 0x09b0, - 0x09b6, 0x09b9, - 0x09dc, 0x09dd, - 0x09df, 0x09e1, - 0x09f0, 0x09f1, - 0x0a05, 0x0a0a, - 0x0a0f, 0x0a10, - 0x0a13, 0x0a28, - 0x0a2a, 0x0a30, - 0x0a32, 0x0a33, - 0x0a35, 0x0a36, - 0x0a38, 0x0a39, - 0x0a59, 0x0a5c, - 0x0a72, 0x0a74, - 0x0a85, 0x0a8d, - 0x0a8f, 0x0a91, - 0x0a93, 0x0aa8, - 0x0aaa, 0x0ab0, - 0x0ab2, 0x0ab3, - 0x0ab5, 0x0ab9, - 0x0ae0, 0x0ae1, - 0x0b05, 0x0b0c, - 0x0b0f, 0x0b10, - 0x0b13, 0x0b28, - 0x0b2a, 0x0b30, - 0x0b32, 0x0b33, - 0x0b35, 0x0b39, - 0x0b5c, 0x0b5d, - 0x0b5f, 0x0b61, - 0x0b85, 0x0b8a, - 0x0b8e, 0x0b90, - 0x0b92, 0x0b95, - 0x0b99, 0x0b9a, - 0x0b9e, 0x0b9f, - 0x0ba3, 0x0ba4, - 0x0ba8, 0x0baa, - 0x0bae, 0x0bb9, - 0x0c05, 0x0c0c, - 0x0c0e, 0x0c10, - 0x0c12, 0x0c28, - 0x0c2a, 0x0c33, - 0x0c35, 0x0c39, - 0x0c58, 0x0c59, - 0x0c60, 0x0c61, - 0x0c85, 0x0c8c, - 0x0c8e, 0x0c90, - 0x0c92, 0x0ca8, - 0x0caa, 0x0cb3, - 0x0cb5, 0x0cb9, - 0x0ce0, 0x0ce1, - 0x0cf1, 0x0cf2, - 0x0d05, 0x0d0c, - 0x0d0e, 0x0d10, - 0x0d12, 0x0d3a, - 0x0d60, 0x0d61, - 0x0d7a, 0x0d7f, - 0x0d85, 0x0d96, - 0x0d9a, 0x0db1, - 0x0db3, 0x0dbb, - 0x0dc0, 0x0dc6, - 0x0e01, 0x0e30, - 0x0e32, 0x0e33, - 0x0e40, 0x0e46, - 0x0e81, 0x0e82, - 0x0e87, 0x0e88, - 0x0e94, 0x0e97, - 0x0e99, 0x0e9f, - 0x0ea1, 0x0ea3, - 0x0eaa, 0x0eab, - 0x0ead, 0x0eb0, - 0x0eb2, 0x0eb3, - 0x0ec0, 0x0ec4, - 0x0edc, 0x0edf, - 0x0f40, 0x0f47, - 0x0f49, 0x0f6c, - 0x0f88, 0x0f8c, - 0x1000, 0x102a, - 0x1050, 0x1055, - 0x105a, 0x105d, - 0x1065, 0x1066, - 0x106e, 0x1070, - 0x1075, 0x1081, - 0x10a0, 0x10c5, - 0x10d0, 0x10fa, - 0x10fc, 0x1248, - 0x124a, 0x124d, - 0x1250, 0x1256, - 0x125a, 0x125d, - 0x1260, 0x1288, - 0x128a, 0x128d, - 0x1290, 0x12b0, - 0x12b2, 0x12b5, - 0x12b8, 0x12be, - 0x12c2, 0x12c5, - 0x12c8, 0x12d6, - 0x12d8, 0x1310, - 0x1312, 0x1315, - 0x1318, 0x135a, - 0x1380, 0x138f, - 0x13a0, 0x13f4, - 0x1401, 0x166c, - 0x166f, 0x167f, - 0x1681, 0x169a, - 0x16a0, 0x16ea, - 0x1700, 0x170c, - 0x170e, 0x1711, - 0x1720, 0x1731, - 0x1740, 0x1751, - 0x1760, 0x176c, - 0x176e, 0x1770, - 0x1780, 0x17b3, - 0x1820, 0x1877, - 0x1880, 0x18a8, - 0x18b0, 0x18f5, - 0x1900, 0x191c, - 0x1950, 0x196d, - 0x1970, 0x1974, - 0x1980, 0x19ab, - 0x19c1, 0x19c7, - 0x1a00, 0x1a16, - 0x1a20, 0x1a54, - 0x1b05, 0x1b33, - 0x1b45, 0x1b4b, - 0x1b83, 0x1ba0, - 0x1bae, 0x1baf, - 0x1bba, 0x1be5, - 0x1c00, 0x1c23, - 0x1c4d, 0x1c4f, - 0x1c5a, 0x1c7d, - 0x1ce9, 0x1cec, - 0x1cee, 0x1cf1, - 0x1cf5, 0x1cf6, - 0x1d00, 0x1dbf, - 0x1e00, 0x1f15, - 0x1f18, 0x1f1d, - 0x1f20, 0x1f45, - 0x1f48, 0x1f4d, - 0x1f50, 0x1f57, - 0x1f5f, 0x1f7d, - 0x1f80, 0x1fb4, - 0x1fb6, 0x1fbc, - 0x1fc2, 0x1fc4, - 0x1fc6, 0x1fcc, - 0x1fd0, 0x1fd3, - 0x1fd6, 0x1fdb, - 0x1fe0, 0x1fec, - 0x1ff2, 0x1ff4, - 0x1ff6, 0x1ffc, - 0x2090, 0x209c, - 0x210a, 0x2113, - 0x2119, 0x211d, - 0x212a, 0x212d, - 0x212f, 0x2139, - 0x213c, 0x213f, - 0x2145, 0x2149, - 0x2183, 0x2184, - 0x2c00, 0x2c2e, - 0x2c30, 0x2c5e, - 0x2c60, 0x2ce4, - 0x2ceb, 0x2cee, - 0x2cf2, 0x2cf3, - 0x2d00, 0x2d25, - 0x2d30, 0x2d67, - 0x2d80, 0x2d96, - 0x2da0, 0x2da6, - 0x2da8, 0x2dae, - 0x2db0, 0x2db6, - 0x2db8, 0x2dbe, - 0x2dc0, 0x2dc6, - 0x2dc8, 0x2dce, - 0x2dd0, 0x2dd6, - 0x2dd8, 0x2dde, - 0x3005, 0x3006, - 0x3031, 0x3035, - 0x303b, 0x303c, - 0x3041, 0x3096, - 0x309d, 0x309f, - 0x30a1, 0x30fa, - 0x30fc, 0x30ff, - 0x3105, 0x312d, - 0x3131, 0x318e, - 0x31a0, 0x31ba, - 0x31f0, 0x31ff, - 0x3400, 0x4db5, - 0x4e00, 0x9fcc, - 0xa000, 0xa48c, - 0xa4d0, 0xa4fd, - 0xa500, 0xa60c, - 0xa610, 0xa61f, - 0xa62a, 0xa62b, - 0xa640, 0xa66e, - 0xa67f, 0xa697, - 0xa6a0, 0xa6e5, - 0xa717, 0xa71f, - 0xa722, 0xa788, - 0xa78b, 0xa78e, - 0xa790, 0xa793, - 0xa7a0, 0xa7aa, - 0xa7f8, 0xa801, - 0xa803, 0xa805, - 0xa807, 0xa80a, - 0xa80c, 0xa822, - 0xa840, 0xa873, - 0xa882, 0xa8b3, - 0xa8f2, 0xa8f7, - 0xa90a, 0xa925, - 0xa930, 0xa946, - 0xa960, 0xa97c, - 0xa984, 0xa9b2, - 0xaa00, 0xaa28, - 0xaa40, 0xaa42, - 0xaa44, 0xaa4b, - 0xaa60, 0xaa76, - 0xaa80, 0xaaaf, - 0xaab5, 0xaab6, - 0xaab9, 0xaabd, - 0xaadb, 0xaadd, - 0xaae0, 0xaaea, - 0xaaf2, 0xaaf4, - 0xab01, 0xab06, - 0xab09, 0xab0e, - 0xab11, 0xab16, - 0xab20, 0xab26, - 0xab28, 0xab2e, - 0xabc0, 0xabe2, - 0xac00, 0xd7a3, - 0xd7b0, 0xd7c6, - 0xd7cb, 0xd7fb, - 0xf900, 0xfa6d, - 0xfa70, 0xfad9, - 0xfb00, 0xfb06, - 0xfb13, 0xfb17, - 0xfb1f, 0xfb28, - 0xfb2a, 0xfb36, - 0xfb38, 0xfb3c, - 0xfb40, 0xfb41, - 0xfb43, 0xfb44, - 0xfb46, 0xfbb1, - 0xfbd3, 0xfd3d, - 0xfd50, 0xfd8f, - 0xfd92, 0xfdc7, - 0xfdf0, 0xfdfb, - 0xfe70, 0xfe74, - 0xfe76, 0xfefc, - 0xff21, 0xff3a, - 0xff41, 0xff5a, - 0xff66, 0xffbe, - 0xffc2, 0xffc7, - 0xffca, 0xffcf, - 0xffd2, 0xffd7, - 0xffda, 0xffdc, - 0x10000, 0x1000b, - 0x1000d, 0x10026, - 0x10028, 0x1003a, - 0x1003c, 0x1003d, - 0x1003f, 0x1004d, - 0x10050, 0x1005d, - 0x10080, 0x100fa, - 0x10280, 0x1029c, - 0x102a0, 0x102d0, - 0x10300, 0x1031e, - 0x10330, 0x10340, - 0x10342, 0x10349, - 0x10380, 0x1039d, - 0x103a0, 0x103c3, - 0x103c8, 0x103cf, - 0x10400, 0x1049d, - 0x10800, 0x10805, - 0x1080a, 0x10835, - 0x10837, 0x10838, - 0x1083f, 0x10855, - 0x10900, 0x10915, - 0x10920, 0x10939, - 0x10980, 0x109b7, - 0x109be, 0x109bf, - 0x10a10, 0x10a13, - 0x10a15, 0x10a17, - 0x10a19, 0x10a33, - 0x10a60, 0x10a7c, - 0x10b00, 0x10b35, - 0x10b40, 0x10b55, - 0x10b60, 0x10b72, - 0x10c00, 0x10c48, - 0x11003, 0x11037, - 0x11083, 0x110af, - 0x110d0, 0x110e8, - 0x11103, 0x11126, - 0x11183, 0x111b2, - 0x111c1, 0x111c4, - 0x11680, 0x116aa, - 0x12000, 0x1236e, - 0x13000, 0x1342e, - 0x16800, 0x16a38, - 0x16f00, 0x16f44, - 0x16f93, 0x16f9f, - 0x1b000, 0x1b001, - 0x1d400, 0x1d454, - 0x1d456, 0x1d49c, - 0x1d49e, 0x1d49f, - 0x1d4a5, 0x1d4a6, - 0x1d4a9, 0x1d4ac, - 0x1d4ae, 0x1d4b9, - 0x1d4bd, 0x1d4c3, - 0x1d4c5, 0x1d505, - 0x1d507, 0x1d50a, - 0x1d50d, 0x1d514, - 0x1d516, 0x1d51c, - 0x1d51e, 0x1d539, - 0x1d53b, 0x1d53e, - 0x1d540, 0x1d544, - 0x1d54a, 0x1d550, - 0x1d552, 0x1d6a5, - 0x1d6a8, 0x1d6c0, - 0x1d6c2, 0x1d6da, - 0x1d6dc, 0x1d6fa, - 0x1d6fc, 0x1d714, - 0x1d716, 0x1d734, - 0x1d736, 0x1d74e, - 0x1d750, 0x1d76e, - 0x1d770, 0x1d788, - 0x1d78a, 0x1d7a8, - 0x1d7aa, 0x1d7c2, - 0x1d7c4, 0x1d7cb, - 0x1ee00, 0x1ee03, - 0x1ee05, 0x1ee1f, - 0x1ee21, 0x1ee22, - 0x1ee29, 0x1ee32, - 0x1ee34, 0x1ee37, - 0x1ee4d, 0x1ee4f, - 0x1ee51, 0x1ee52, - 0x1ee61, 0x1ee62, - 0x1ee67, 0x1ee6a, - 0x1ee6c, 0x1ee72, - 0x1ee74, 0x1ee77, - 0x1ee79, 0x1ee7c, - 0x1ee80, 0x1ee89, - 0x1ee8b, 0x1ee9b, - 0x1eea1, 0x1eea3, - 0x1eea5, 0x1eea9, - 0x1eeab, 0x1eebb, - 0x20000, 0x2a6d6, - 0x2a700, 0x2b734, - 0x2b740, 0x2b81d, - 0x2f800, 0x2fa1d, -}; - -/* }}} */ - -/* {{{ Letters */ - -static uint32_t isalphas[] = { - 0x00aa, - 0x00b5, - 0x00ba, - 0x02ec, - 0x02ee, - 0x0386, - 0x038c, - 0x0559, - 0x06d5, - 0x06ff, - 0x0710, - 0x07b1, - 0x07fa, - 0x081a, - 0x0824, - 0x0828, - 0x08a0, - 0x093d, - 0x0950, - 0x09b2, - 0x09bd, - 0x09ce, - 0x0a5e, - 0x0abd, - 0x0ad0, - 0x0b3d, - 0x0b71, - 0x0b83, - 0x0b9c, - 0x0bd0, - 0x0c3d, - 0x0cbd, - 0x0cde, - 0x0d3d, - 0x0d4e, - 0x0dbd, - 0x0e84, - 0x0e8a, - 0x0e8d, - 0x0ea5, - 0x0ea7, - 0x0ebd, - 0x0ec6, - 0x0f00, - 0x103f, - 0x1061, - 0x108e, - 0x10c7, - 0x10cd, - 0x1258, - 0x12c0, - 0x17d7, - 0x17dc, - 0x18aa, - 0x1aa7, - 0x1f59, - 0x1f5b, - 0x1f5d, - 0x1fbe, - 0x2071, - 0x207f, - 0x2102, - 0x2107, - 0x2115, - 0x2124, - 0x2126, - 0x2128, - 0x214e, - 0x2d27, - 0x2d2d, - 0x2d6f, - 0x2e2f, - 0xa8fb, - 0xa9cf, - 0xaa7a, - 0xaab1, - 0xaac0, - 0xaac2, - 0xfb1d, - 0xfb3e, - 0x10808, - 0x1083c, - 0x10a00, - 0x16f50, - 0x1d4a2, - 0x1d4bb, - 0x1d546, - 0x1ee24, - 0x1ee27, - 0x1ee39, - 0x1ee3b, - 0x1ee42, - 0x1ee47, - 0x1ee49, - 0x1ee4b, - 0x1ee54, - 0x1ee57, - 0x1ee59, - 0x1ee5b, - 0x1ee5d, - 0x1ee5f, - 0x1ee64, - 0x1ee7e, -}; - -/* }}} */ - -/* {{{ Unicode upper */ - -static uint32_t isupperr[] = { - 0x0041, 0x005a, - 0x00c0, 0x00d6, - 0x00d8, 0x00de, - 0x0178, 0x0179, - 0x0181, 0x0182, - 0x0186, 0x0187, - 0x0189, 0x018b, - 0x018e, 0x0191, - 0x0193, 0x0194, - 0x0196, 0x0198, - 0x019c, 0x019d, - 0x019f, 0x01a0, - 0x01a6, 0x01a7, - 0x01ae, 0x01af, - 0x01b1, 0x01b3, - 0x01b7, 0x01b8, - 0x01f6, 0x01f8, - 0x023a, 0x023b, - 0x023d, 0x023e, - 0x0243, 0x0246, - 0x0388, 0x038a, - 0x038e, 0x038f, - 0x0391, 0x03a1, - 0x03a3, 0x03ab, - 0x03d2, 0x03d4, - 0x03f9, 0x03fa, - 0x03fd, 0x042f, - 0x04c0, 0x04c1, - 0x0531, 0x0556, - 0x10a0, 0x10c5, - 0x1f08, 0x1f0f, - 0x1f18, 0x1f1d, - 0x1f28, 0x1f2f, - 0x1f38, 0x1f3f, - 0x1f48, 0x1f4d, - 0x1f68, 0x1f6f, - 0x1f88, 0x1f8f, - 0x1f98, 0x1f9f, - 0x1fa8, 0x1faf, - 0x1fb8, 0x1fbc, - 0x1fc8, 0x1fcc, - 0x1fd8, 0x1fdb, - 0x1fe8, 0x1fec, - 0x1ff8, 0x1ffc, - 0x210b, 0x210d, - 0x2110, 0x2112, - 0x2119, 0x211d, - 0x212a, 0x212d, - 0x2130, 0x2133, - 0x213e, 0x213f, - 0x2160, 0x216f, - 0x24b6, 0x24cf, - 0x2c00, 0x2c2e, - 0x2c62, 0x2c64, - 0x2c6d, 0x2c70, - 0x2c7e, 0x2c80, - 0xa77d, 0xa77e, - 0xff21, 0xff3a, - 0x10400, 0x10427, - 0x1d400, 0x1d419, - 0x1d434, 0x1d44d, - 0x1d468, 0x1d481, - 0x1d49e, 0x1d49f, - 0x1d4a5, 0x1d4a6, - 0x1d4a9, 0x1d4ac, - 0x1d4ae, 0x1d4b5, - 0x1d4d0, 0x1d4e9, - 0x1d504, 0x1d505, - 0x1d507, 0x1d50a, - 0x1d50d, 0x1d514, - 0x1d516, 0x1d51c, - 0x1d538, 0x1d539, - 0x1d53b, 0x1d53e, - 0x1d540, 0x1d544, - 0x1d54a, 0x1d550, - 0x1d56c, 0x1d585, - 0x1d5a0, 0x1d5b9, - 0x1d5d4, 0x1d5ed, - 0x1d608, 0x1d621, - 0x1d63c, 0x1d655, - 0x1d670, 0x1d689, - 0x1d6a8, 0x1d6c0, - 0x1d6e2, 0x1d6fa, - 0x1d71c, 0x1d734, - 0x1d756, 0x1d76e, - 0x1d790, 0x1d7a8, -}; - -/* }}} */ - -/* {{{ Upper */ - -static uint32_t isuppers[] = { - 0x0100, - 0x0102, - 0x0104, - 0x0106, - 0x0108, - 0x010a, - 0x010c, - 0x010e, - 0x0110, - 0x0112, - 0x0114, - 0x0116, - 0x0118, - 0x011a, - 0x011c, - 0x011e, - 0x0120, - 0x0122, - 0x0124, - 0x0126, - 0x0128, - 0x012a, - 0x012c, - 0x012e, - 0x0130, - 0x0132, - 0x0134, - 0x0136, - 0x0139, - 0x013b, - 0x013d, - 0x013f, - 0x0141, - 0x0143, - 0x0145, - 0x0147, - 0x014a, - 0x014c, - 0x014e, - 0x0150, - 0x0152, - 0x0154, - 0x0156, - 0x0158, - 0x015a, - 0x015c, - 0x015e, - 0x0160, - 0x0162, - 0x0164, - 0x0166, - 0x0168, - 0x016a, - 0x016c, - 0x016e, - 0x0170, - 0x0172, - 0x0174, - 0x0176, - 0x017b, - 0x017d, - 0x0184, - 0x01a2, - 0x01a4, - 0x01a9, - 0x01ac, - 0x01b5, - 0x01bc, - 0x01c4, - 0x01c7, - 0x01ca, - 0x01cd, - 0x01cf, - 0x01d1, - 0x01d3, - 0x01d5, - 0x01d7, - 0x01d9, - 0x01db, - 0x01de, - 0x01e0, - 0x01e2, - 0x01e4, - 0x01e6, - 0x01e8, - 0x01ea, - 0x01ec, - 0x01ee, - 0x01f1, - 0x01f4, - 0x01fa, - 0x01fc, - 0x01fe, - 0x0200, - 0x0202, - 0x0204, - 0x0206, - 0x0208, - 0x020a, - 0x020c, - 0x020e, - 0x0210, - 0x0212, - 0x0214, - 0x0216, - 0x0218, - 0x021a, - 0x021c, - 0x021e, - 0x0220, - 0x0222, - 0x0224, - 0x0226, - 0x0228, - 0x022a, - 0x022c, - 0x022e, - 0x0230, - 0x0232, - 0x0241, - 0x0248, - 0x024a, - 0x024c, - 0x024e, - 0x0370, - 0x0372, - 0x0376, - 0x0386, - 0x038c, - 0x03cf, - 0x03d8, - 0x03da, - 0x03dc, - 0x03de, - 0x03e0, - 0x03e2, - 0x03e4, - 0x03e6, - 0x03e8, - 0x03ea, - 0x03ec, - 0x03ee, - 0x03f4, - 0x03f7, - 0x0460, - 0x0462, - 0x0464, - 0x0466, - 0x0468, - 0x046a, - 0x046c, - 0x046e, - 0x0470, - 0x0472, - 0x0474, - 0x0476, - 0x0478, - 0x047a, - 0x047c, - 0x047e, - 0x0480, - 0x048a, - 0x048c, - 0x048e, - 0x0490, - 0x0492, - 0x0494, - 0x0496, - 0x0498, - 0x049a, - 0x049c, - 0x049e, - 0x04a0, - 0x04a2, - 0x04a4, - 0x04a6, - 0x04a8, - 0x04aa, - 0x04ac, - 0x04ae, - 0x04b0, - 0x04b2, - 0x04b4, - 0x04b6, - 0x04b8, - 0x04ba, - 0x04bc, - 0x04be, - 0x04c3, - 0x04c5, - 0x04c7, - 0x04c9, - 0x04cb, - 0x04cd, - 0x04d0, - 0x04d2, - 0x04d4, - 0x04d6, - 0x04d8, - 0x04da, - 0x04dc, - 0x04de, - 0x04e0, - 0x04e2, - 0x04e4, - 0x04e6, - 0x04e8, - 0x04ea, - 0x04ec, - 0x04ee, - 0x04f0, - 0x04f2, - 0x04f4, - 0x04f6, - 0x04f8, - 0x04fa, - 0x04fc, - 0x04fe, - 0x0500, - 0x0502, - 0x0504, - 0x0506, - 0x0508, - 0x050a, - 0x050c, - 0x050e, - 0x0510, - 0x0512, - 0x0514, - 0x0516, - 0x0518, - 0x051a, - 0x051c, - 0x051e, - 0x0520, - 0x0522, - 0x0524, - 0x0526, - 0x10c7, - 0x10cd, - 0x1e00, - 0x1e02, - 0x1e04, - 0x1e06, - 0x1e08, - 0x1e0a, - 0x1e0c, - 0x1e0e, - 0x1e10, - 0x1e12, - 0x1e14, - 0x1e16, - 0x1e18, - 0x1e1a, - 0x1e1c, - 0x1e1e, - 0x1e20, - 0x1e22, - 0x1e24, - 0x1e26, - 0x1e28, - 0x1e2a, - 0x1e2c, - 0x1e2e, - 0x1e30, - 0x1e32, - 0x1e34, - 0x1e36, - 0x1e38, - 0x1e3a, - 0x1e3c, - 0x1e3e, - 0x1e40, - 0x1e42, - 0x1e44, - 0x1e46, - 0x1e48, - 0x1e4a, - 0x1e4c, - 0x1e4e, - 0x1e50, - 0x1e52, - 0x1e54, - 0x1e56, - 0x1e58, - 0x1e5a, - 0x1e5c, - 0x1e5e, - 0x1e60, - 0x1e62, - 0x1e64, - 0x1e66, - 0x1e68, - 0x1e6a, - 0x1e6c, - 0x1e6e, - 0x1e70, - 0x1e72, - 0x1e74, - 0x1e76, - 0x1e78, - 0x1e7a, - 0x1e7c, - 0x1e7e, - 0x1e80, - 0x1e82, - 0x1e84, - 0x1e86, - 0x1e88, - 0x1e8a, - 0x1e8c, - 0x1e8e, - 0x1e90, - 0x1e92, - 0x1e94, - 0x1e9e, - 0x1ea0, - 0x1ea2, - 0x1ea4, - 0x1ea6, - 0x1ea8, - 0x1eaa, - 0x1eac, - 0x1eae, - 0x1eb0, - 0x1eb2, - 0x1eb4, - 0x1eb6, - 0x1eb8, - 0x1eba, - 0x1ebc, - 0x1ebe, - 0x1ec0, - 0x1ec2, - 0x1ec4, - 0x1ec6, - 0x1ec8, - 0x1eca, - 0x1ecc, - 0x1ece, - 0x1ed0, - 0x1ed2, - 0x1ed4, - 0x1ed6, - 0x1ed8, - 0x1eda, - 0x1edc, - 0x1ede, - 0x1ee0, - 0x1ee2, - 0x1ee4, - 0x1ee6, - 0x1ee8, - 0x1eea, - 0x1eec, - 0x1eee, - 0x1ef0, - 0x1ef2, - 0x1ef4, - 0x1ef6, - 0x1ef8, - 0x1efa, - 0x1efc, - 0x1efe, - 0x1f59, - 0x1f5b, - 0x1f5d, - 0x1f5f, - 0x2102, - 0x2107, - 0x2115, - 0x2124, - 0x2126, - 0x2128, - 0x2145, - 0x2183, - 0x2c60, - 0x2c67, - 0x2c69, - 0x2c6b, - 0x2c72, - 0x2c75, - 0x2c82, - 0x2c84, - 0x2c86, - 0x2c88, - 0x2c8a, - 0x2c8c, - 0x2c8e, - 0x2c90, - 0x2c92, - 0x2c94, - 0x2c96, - 0x2c98, - 0x2c9a, - 0x2c9c, - 0x2c9e, - 0x2ca0, - 0x2ca2, - 0x2ca4, - 0x2ca6, - 0x2ca8, - 0x2caa, - 0x2cac, - 0x2cae, - 0x2cb0, - 0x2cb2, - 0x2cb4, - 0x2cb6, - 0x2cb8, - 0x2cba, - 0x2cbc, - 0x2cbe, - 0x2cc0, - 0x2cc2, - 0x2cc4, - 0x2cc6, - 0x2cc8, - 0x2cca, - 0x2ccc, - 0x2cce, - 0x2cd0, - 0x2cd2, - 0x2cd4, - 0x2cd6, - 0x2cd8, - 0x2cda, - 0x2cdc, - 0x2cde, - 0x2ce0, - 0x2ce2, - 0x2ceb, - 0x2ced, - 0x2cf2, - 0xa640, - 0xa642, - 0xa644, - 0xa646, - 0xa648, - 0xa64a, - 0xa64c, - 0xa64e, - 0xa650, - 0xa652, - 0xa654, - 0xa656, - 0xa658, - 0xa65a, - 0xa65c, - 0xa65e, - 0xa660, - 0xa662, - 0xa664, - 0xa666, - 0xa668, - 0xa66a, - 0xa66c, - 0xa680, - 0xa682, - 0xa684, - 0xa686, - 0xa688, - 0xa68a, - 0xa68c, - 0xa68e, - 0xa690, - 0xa692, - 0xa694, - 0xa696, - 0xa722, - 0xa724, - 0xa726, - 0xa728, - 0xa72a, - 0xa72c, - 0xa72e, - 0xa732, - 0xa734, - 0xa736, - 0xa738, - 0xa73a, - 0xa73c, - 0xa73e, - 0xa740, - 0xa742, - 0xa744, - 0xa746, - 0xa748, - 0xa74a, - 0xa74c, - 0xa74e, - 0xa750, - 0xa752, - 0xa754, - 0xa756, - 0xa758, - 0xa75a, - 0xa75c, - 0xa75e, - 0xa760, - 0xa762, - 0xa764, - 0xa766, - 0xa768, - 0xa76a, - 0xa76c, - 0xa76e, - 0xa779, - 0xa77b, - 0xa780, - 0xa782, - 0xa784, - 0xa786, - 0xa78b, - 0xa78d, - 0xa790, - 0xa792, - 0xa7a0, - 0xa7a2, - 0xa7a4, - 0xa7a6, - 0xa7a8, - 0xa7aa, - 0x1d49c, - 0x1d4a2, - 0x1d546, - 0x1d7ca, -}; - -/* }}} */ - -/* {{{ Unicode lower */ - -static uint32_t islowerr[] = { - 0x0061, 0x007a, - 0x00df, 0x00f6, - 0x00f8, 0x00ff, - 0x0137, 0x0138, - 0x0148, 0x0149, - 0x017e, 0x0180, - 0x018c, 0x018d, - 0x0199, 0x019b, - 0x01aa, 0x01ab, - 0x01b9, 0x01ba, - 0x01bd, 0x01bf, - 0x01dc, 0x01dd, - 0x01ef, 0x01f0, - 0x0233, 0x0239, - 0x023f, 0x0240, - 0x024f, 0x0293, - 0x0295, 0x02af, - 0x037b, 0x037d, - 0x03ac, 0x03ce, - 0x03d0, 0x03d1, - 0x03d5, 0x03d7, - 0x03ef, 0x03f3, - 0x03fb, 0x03fc, - 0x0430, 0x045f, - 0x04ce, 0x04cf, - 0x0561, 0x0587, - 0x1d00, 0x1d2b, - 0x1d6b, 0x1d77, - 0x1d79, 0x1d9a, - 0x1e95, 0x1e9d, - 0x1eff, 0x1f07, - 0x1f10, 0x1f15, - 0x1f20, 0x1f27, - 0x1f30, 0x1f37, - 0x1f40, 0x1f45, - 0x1f50, 0x1f57, - 0x1f60, 0x1f67, - 0x1f70, 0x1f7d, - 0x1f80, 0x1f87, - 0x1f90, 0x1f97, - 0x1fa0, 0x1fa7, - 0x1fb0, 0x1fb4, - 0x1fb6, 0x1fb7, - 0x1fc2, 0x1fc4, - 0x1fc6, 0x1fc7, - 0x1fd0, 0x1fd3, - 0x1fd6, 0x1fd7, - 0x1fe0, 0x1fe7, - 0x1ff2, 0x1ff4, - 0x1ff6, 0x1ff7, - 0x210e, 0x210f, - 0x213c, 0x213d, - 0x2146, 0x2149, - 0x2170, 0x217f, - 0x24d0, 0x24e9, - 0x2c30, 0x2c5e, - 0x2c65, 0x2c66, - 0x2c73, 0x2c74, - 0x2c76, 0x2c7b, - 0x2ce3, 0x2ce4, - 0x2d00, 0x2d25, - 0xa72f, 0xa731, - 0xa771, 0xa778, - 0xfb00, 0xfb06, - 0xfb13, 0xfb17, - 0xff41, 0xff5a, - 0x10428, 0x1044f, - 0x1d41a, 0x1d433, - 0x1d44e, 0x1d454, - 0x1d456, 0x1d467, - 0x1d482, 0x1d49b, - 0x1d4b6, 0x1d4b9, - 0x1d4bd, 0x1d4c3, - 0x1d4c5, 0x1d4cf, - 0x1d4ea, 0x1d503, - 0x1d51e, 0x1d537, - 0x1d552, 0x1d56b, - 0x1d586, 0x1d59f, - 0x1d5ba, 0x1d5d3, - 0x1d5ee, 0x1d607, - 0x1d622, 0x1d63b, - 0x1d656, 0x1d66f, - 0x1d68a, 0x1d6a5, - 0x1d6c2, 0x1d6da, - 0x1d6dc, 0x1d6e1, - 0x1d6fc, 0x1d714, - 0x1d716, 0x1d71b, - 0x1d736, 0x1d74e, - 0x1d750, 0x1d755, - 0x1d770, 0x1d788, - 0x1d78a, 0x1d78f, - 0x1d7aa, 0x1d7c2, - 0x1d7c4, 0x1d7c9, -}; - -/* }}} */ - -/* {{{ Lower */ - -static uint32_t islowers[] = { - 0x00b5, - 0x0101, - 0x0103, - 0x0105, - 0x0107, - 0x0109, - 0x010b, - 0x010d, - 0x010f, - 0x0111, - 0x0113, - 0x0115, - 0x0117, - 0x0119, - 0x011b, - 0x011d, - 0x011f, - 0x0121, - 0x0123, - 0x0125, - 0x0127, - 0x0129, - 0x012b, - 0x012d, - 0x012f, - 0x0131, - 0x0133, - 0x0135, - 0x013a, - 0x013c, - 0x013e, - 0x0140, - 0x0142, - 0x0144, - 0x0146, - 0x014b, - 0x014d, - 0x014f, - 0x0151, - 0x0153, - 0x0155, - 0x0157, - 0x0159, - 0x015b, - 0x015d, - 0x015f, - 0x0161, - 0x0163, - 0x0165, - 0x0167, - 0x0169, - 0x016b, - 0x016d, - 0x016f, - 0x0171, - 0x0173, - 0x0175, - 0x0177, - 0x017a, - 0x017c, - 0x0183, - 0x0185, - 0x0188, - 0x0192, - 0x0195, - 0x019e, - 0x01a1, - 0x01a3, - 0x01a5, - 0x01a8, - 0x01ad, - 0x01b0, - 0x01b4, - 0x01b6, - 0x01c6, - 0x01c9, - 0x01cc, - 0x01ce, - 0x01d0, - 0x01d2, - 0x01d4, - 0x01d6, - 0x01d8, - 0x01da, - 0x01df, - 0x01e1, - 0x01e3, - 0x01e5, - 0x01e7, - 0x01e9, - 0x01eb, - 0x01ed, - 0x01f3, - 0x01f5, - 0x01f9, - 0x01fb, - 0x01fd, - 0x01ff, - 0x0201, - 0x0203, - 0x0205, - 0x0207, - 0x0209, - 0x020b, - 0x020d, - 0x020f, - 0x0211, - 0x0213, - 0x0215, - 0x0217, - 0x0219, - 0x021b, - 0x021d, - 0x021f, - 0x0221, - 0x0223, - 0x0225, - 0x0227, - 0x0229, - 0x022b, - 0x022d, - 0x022f, - 0x0231, - 0x023c, - 0x0242, - 0x0247, - 0x0249, - 0x024b, - 0x024d, - 0x0371, - 0x0373, - 0x0377, - 0x0390, - 0x03d9, - 0x03db, - 0x03dd, - 0x03df, - 0x03e1, - 0x03e3, - 0x03e5, - 0x03e7, - 0x03e9, - 0x03eb, - 0x03ed, - 0x03f5, - 0x03f8, - 0x0461, - 0x0463, - 0x0465, - 0x0467, - 0x0469, - 0x046b, - 0x046d, - 0x046f, - 0x0471, - 0x0473, - 0x0475, - 0x0477, - 0x0479, - 0x047b, - 0x047d, - 0x047f, - 0x0481, - 0x048b, - 0x048d, - 0x048f, - 0x0491, - 0x0493, - 0x0495, - 0x0497, - 0x0499, - 0x049b, - 0x049d, - 0x049f, - 0x04a1, - 0x04a3, - 0x04a5, - 0x04a7, - 0x04a9, - 0x04ab, - 0x04ad, - 0x04af, - 0x04b1, - 0x04b3, - 0x04b5, - 0x04b7, - 0x04b9, - 0x04bb, - 0x04bd, - 0x04bf, - 0x04c2, - 0x04c4, - 0x04c6, - 0x04c8, - 0x04ca, - 0x04cc, - 0x04d1, - 0x04d3, - 0x04d5, - 0x04d7, - 0x04d9, - 0x04db, - 0x04dd, - 0x04df, - 0x04e1, - 0x04e3, - 0x04e5, - 0x04e7, - 0x04e9, - 0x04eb, - 0x04ed, - 0x04ef, - 0x04f1, - 0x04f3, - 0x04f5, - 0x04f7, - 0x04f9, - 0x04fb, - 0x04fd, - 0x04ff, - 0x0501, - 0x0503, - 0x0505, - 0x0507, - 0x0509, - 0x050b, - 0x050d, - 0x050f, - 0x0511, - 0x0513, - 0x0515, - 0x0517, - 0x0519, - 0x051b, - 0x051d, - 0x051f, - 0x0521, - 0x0523, - 0x0525, - 0x0527, - 0x1e01, - 0x1e03, - 0x1e05, - 0x1e07, - 0x1e09, - 0x1e0b, - 0x1e0d, - 0x1e0f, - 0x1e11, - 0x1e13, - 0x1e15, - 0x1e17, - 0x1e19, - 0x1e1b, - 0x1e1d, - 0x1e1f, - 0x1e21, - 0x1e23, - 0x1e25, - 0x1e27, - 0x1e29, - 0x1e2b, - 0x1e2d, - 0x1e2f, - 0x1e31, - 0x1e33, - 0x1e35, - 0x1e37, - 0x1e39, - 0x1e3b, - 0x1e3d, - 0x1e3f, - 0x1e41, - 0x1e43, - 0x1e45, - 0x1e47, - 0x1e49, - 0x1e4b, - 0x1e4d, - 0x1e4f, - 0x1e51, - 0x1e53, - 0x1e55, - 0x1e57, - 0x1e59, - 0x1e5b, - 0x1e5d, - 0x1e5f, - 0x1e61, - 0x1e63, - 0x1e65, - 0x1e67, - 0x1e69, - 0x1e6b, - 0x1e6d, - 0x1e6f, - 0x1e71, - 0x1e73, - 0x1e75, - 0x1e77, - 0x1e79, - 0x1e7b, - 0x1e7d, - 0x1e7f, - 0x1e81, - 0x1e83, - 0x1e85, - 0x1e87, - 0x1e89, - 0x1e8b, - 0x1e8d, - 0x1e8f, - 0x1e91, - 0x1e93, - 0x1e9f, - 0x1ea1, - 0x1ea3, - 0x1ea5, - 0x1ea7, - 0x1ea9, - 0x1eab, - 0x1ead, - 0x1eaf, - 0x1eb1, - 0x1eb3, - 0x1eb5, - 0x1eb7, - 0x1eb9, - 0x1ebb, - 0x1ebd, - 0x1ebf, - 0x1ec1, - 0x1ec3, - 0x1ec5, - 0x1ec7, - 0x1ec9, - 0x1ecb, - 0x1ecd, - 0x1ecf, - 0x1ed1, - 0x1ed3, - 0x1ed5, - 0x1ed7, - 0x1ed9, - 0x1edb, - 0x1edd, - 0x1edf, - 0x1ee1, - 0x1ee3, - 0x1ee5, - 0x1ee7, - 0x1ee9, - 0x1eeb, - 0x1eed, - 0x1eef, - 0x1ef1, - 0x1ef3, - 0x1ef5, - 0x1ef7, - 0x1ef9, - 0x1efb, - 0x1efd, - 0x1fbe, - 0x210a, - 0x2113, - 0x212f, - 0x2134, - 0x2139, - 0x214e, - 0x2184, - 0x2c61, - 0x2c68, - 0x2c6a, - 0x2c6c, - 0x2c71, - 0x2c81, - 0x2c83, - 0x2c85, - 0x2c87, - 0x2c89, - 0x2c8b, - 0x2c8d, - 0x2c8f, - 0x2c91, - 0x2c93, - 0x2c95, - 0x2c97, - 0x2c99, - 0x2c9b, - 0x2c9d, - 0x2c9f, - 0x2ca1, - 0x2ca3, - 0x2ca5, - 0x2ca7, - 0x2ca9, - 0x2cab, - 0x2cad, - 0x2caf, - 0x2cb1, - 0x2cb3, - 0x2cb5, - 0x2cb7, - 0x2cb9, - 0x2cbb, - 0x2cbd, - 0x2cbf, - 0x2cc1, - 0x2cc3, - 0x2cc5, - 0x2cc7, - 0x2cc9, - 0x2ccb, - 0x2ccd, - 0x2ccf, - 0x2cd1, - 0x2cd3, - 0x2cd5, - 0x2cd7, - 0x2cd9, - 0x2cdb, - 0x2cdd, - 0x2cdf, - 0x2ce1, - 0x2cec, - 0x2cee, - 0x2cf3, - 0x2d27, - 0x2d2d, - 0xa641, - 0xa643, - 0xa645, - 0xa647, - 0xa649, - 0xa64b, - 0xa64d, - 0xa64f, - 0xa651, - 0xa653, - 0xa655, - 0xa657, - 0xa659, - 0xa65b, - 0xa65d, - 0xa65f, - 0xa661, - 0xa663, - 0xa665, - 0xa667, - 0xa669, - 0xa66b, - 0xa66d, - 0xa681, - 0xa683, - 0xa685, - 0xa687, - 0xa689, - 0xa68b, - 0xa68d, - 0xa68f, - 0xa691, - 0xa693, - 0xa695, - 0xa697, - 0xa723, - 0xa725, - 0xa727, - 0xa729, - 0xa72b, - 0xa72d, - 0xa733, - 0xa735, - 0xa737, - 0xa739, - 0xa73b, - 0xa73d, - 0xa73f, - 0xa741, - 0xa743, - 0xa745, - 0xa747, - 0xa749, - 0xa74b, - 0xa74d, - 0xa74f, - 0xa751, - 0xa753, - 0xa755, - 0xa757, - 0xa759, - 0xa75b, - 0xa75d, - 0xa75f, - 0xa761, - 0xa763, - 0xa765, - 0xa767, - 0xa769, - 0xa76b, - 0xa76d, - 0xa76f, - 0xa77a, - 0xa77c, - 0xa77f, - 0xa781, - 0xa783, - 0xa785, - 0xa787, - 0xa78c, - 0xa78e, - 0xa791, - 0xa793, - 0xa7a1, - 0xa7a3, - 0xa7a5, - 0xa7a7, - 0xa7a9, - 0xa7fa, - 0x1d4bb, - 0x1d7cb, -}; - -/* }}} */ - -/* {{{ Unicode title */ - -static uint32_t istitler[] = { - 0x0041, 0x005a, - 0x00c0, 0x00d6, - 0x00d8, 0x00de, - 0x0178, 0x0179, - 0x0181, 0x0182, - 0x0186, 0x0187, - 0x0189, 0x018b, - 0x018e, 0x0191, - 0x0193, 0x0194, - 0x0196, 0x0198, - 0x019c, 0x019d, - 0x019f, 0x01a0, - 0x01a6, 0x01a7, - 0x01ae, 0x01af, - 0x01b1, 0x01b3, - 0x01b7, 0x01b8, - 0x01f6, 0x01f8, - 0x023a, 0x023b, - 0x023d, 0x023e, - 0x0243, 0x0246, - 0x0388, 0x038a, - 0x038e, 0x038f, - 0x0391, 0x03a1, - 0x03a3, 0x03ab, - 0x03f9, 0x03fa, - 0x03fd, 0x042f, - 0x04c0, 0x04c1, - 0x0531, 0x0556, - 0x10a0, 0x10c5, - 0x1f08, 0x1f0f, - 0x1f18, 0x1f1d, - 0x1f28, 0x1f2f, - 0x1f38, 0x1f3f, - 0x1f48, 0x1f4d, - 0x1f68, 0x1f6f, - 0x1f88, 0x1f8f, - 0x1f98, 0x1f9f, - 0x1fa8, 0x1faf, - 0x1fb8, 0x1fbc, - 0x1fc8, 0x1fcc, - 0x1fd8, 0x1fdb, - 0x1fe8, 0x1fec, - 0x1ff8, 0x1ffc, - 0x2160, 0x216f, - 0x24b6, 0x24cf, - 0x2c00, 0x2c2e, - 0x2c62, 0x2c64, - 0x2c6d, 0x2c70, - 0x2c7e, 0x2c80, - 0xa77d, 0xa77e, - 0xff21, 0xff3a, - 0x10400, 0x10427, -}; - -/* }}} */ - -/* {{{ Title */ - -static uint32_t istitles[] = { - 0x0100, - 0x0102, - 0x0104, - 0x0106, - 0x0108, - 0x010a, - 0x010c, - 0x010e, - 0x0110, - 0x0112, - 0x0114, - 0x0116, - 0x0118, - 0x011a, - 0x011c, - 0x011e, - 0x0120, - 0x0122, - 0x0124, - 0x0126, - 0x0128, - 0x012a, - 0x012c, - 0x012e, - 0x0132, - 0x0134, - 0x0136, - 0x0139, - 0x013b, - 0x013d, - 0x013f, - 0x0141, - 0x0143, - 0x0145, - 0x0147, - 0x014a, - 0x014c, - 0x014e, - 0x0150, - 0x0152, - 0x0154, - 0x0156, - 0x0158, - 0x015a, - 0x015c, - 0x015e, - 0x0160, - 0x0162, - 0x0164, - 0x0166, - 0x0168, - 0x016a, - 0x016c, - 0x016e, - 0x0170, - 0x0172, - 0x0174, - 0x0176, - 0x017b, - 0x017d, - 0x0184, - 0x01a2, - 0x01a4, - 0x01a9, - 0x01ac, - 0x01b5, - 0x01bc, - 0x01c5, - 0x01c8, - 0x01cb, - 0x01cd, - 0x01cf, - 0x01d1, - 0x01d3, - 0x01d5, - 0x01d7, - 0x01d9, - 0x01db, - 0x01de, - 0x01e0, - 0x01e2, - 0x01e4, - 0x01e6, - 0x01e8, - 0x01ea, - 0x01ec, - 0x01ee, - 0x01f2, - 0x01f4, - 0x01fa, - 0x01fc, - 0x01fe, - 0x0200, - 0x0202, - 0x0204, - 0x0206, - 0x0208, - 0x020a, - 0x020c, - 0x020e, - 0x0210, - 0x0212, - 0x0214, - 0x0216, - 0x0218, - 0x021a, - 0x021c, - 0x021e, - 0x0220, - 0x0222, - 0x0224, - 0x0226, - 0x0228, - 0x022a, - 0x022c, - 0x022e, - 0x0230, - 0x0232, - 0x0241, - 0x0248, - 0x024a, - 0x024c, - 0x024e, - 0x0370, - 0x0372, - 0x0376, - 0x0386, - 0x038c, - 0x03cf, - 0x03d8, - 0x03da, - 0x03dc, - 0x03de, - 0x03e0, - 0x03e2, - 0x03e4, - 0x03e6, - 0x03e8, - 0x03ea, - 0x03ec, - 0x03ee, - 0x03f7, - 0x0460, - 0x0462, - 0x0464, - 0x0466, - 0x0468, - 0x046a, - 0x046c, - 0x046e, - 0x0470, - 0x0472, - 0x0474, - 0x0476, - 0x0478, - 0x047a, - 0x047c, - 0x047e, - 0x0480, - 0x048a, - 0x048c, - 0x048e, - 0x0490, - 0x0492, - 0x0494, - 0x0496, - 0x0498, - 0x049a, - 0x049c, - 0x049e, - 0x04a0, - 0x04a2, - 0x04a4, - 0x04a6, - 0x04a8, - 0x04aa, - 0x04ac, - 0x04ae, - 0x04b0, - 0x04b2, - 0x04b4, - 0x04b6, - 0x04b8, - 0x04ba, - 0x04bc, - 0x04be, - 0x04c3, - 0x04c5, - 0x04c7, - 0x04c9, - 0x04cb, - 0x04cd, - 0x04d0, - 0x04d2, - 0x04d4, - 0x04d6, - 0x04d8, - 0x04da, - 0x04dc, - 0x04de, - 0x04e0, - 0x04e2, - 0x04e4, - 0x04e6, - 0x04e8, - 0x04ea, - 0x04ec, - 0x04ee, - 0x04f0, - 0x04f2, - 0x04f4, - 0x04f6, - 0x04f8, - 0x04fa, - 0x04fc, - 0x04fe, - 0x0500, - 0x0502, - 0x0504, - 0x0506, - 0x0508, - 0x050a, - 0x050c, - 0x050e, - 0x0510, - 0x0512, - 0x0514, - 0x0516, - 0x0518, - 0x051a, - 0x051c, - 0x051e, - 0x0520, - 0x0522, - 0x0524, - 0x0526, - 0x10c7, - 0x10cd, - 0x1e00, - 0x1e02, - 0x1e04, - 0x1e06, - 0x1e08, - 0x1e0a, - 0x1e0c, - 0x1e0e, - 0x1e10, - 0x1e12, - 0x1e14, - 0x1e16, - 0x1e18, - 0x1e1a, - 0x1e1c, - 0x1e1e, - 0x1e20, - 0x1e22, - 0x1e24, - 0x1e26, - 0x1e28, - 0x1e2a, - 0x1e2c, - 0x1e2e, - 0x1e30, - 0x1e32, - 0x1e34, - 0x1e36, - 0x1e38, - 0x1e3a, - 0x1e3c, - 0x1e3e, - 0x1e40, - 0x1e42, - 0x1e44, - 0x1e46, - 0x1e48, - 0x1e4a, - 0x1e4c, - 0x1e4e, - 0x1e50, - 0x1e52, - 0x1e54, - 0x1e56, - 0x1e58, - 0x1e5a, - 0x1e5c, - 0x1e5e, - 0x1e60, - 0x1e62, - 0x1e64, - 0x1e66, - 0x1e68, - 0x1e6a, - 0x1e6c, - 0x1e6e, - 0x1e70, - 0x1e72, - 0x1e74, - 0x1e76, - 0x1e78, - 0x1e7a, - 0x1e7c, - 0x1e7e, - 0x1e80, - 0x1e82, - 0x1e84, - 0x1e86, - 0x1e88, - 0x1e8a, - 0x1e8c, - 0x1e8e, - 0x1e90, - 0x1e92, - 0x1e94, - 0x1ea0, - 0x1ea2, - 0x1ea4, - 0x1ea6, - 0x1ea8, - 0x1eaa, - 0x1eac, - 0x1eae, - 0x1eb0, - 0x1eb2, - 0x1eb4, - 0x1eb6, - 0x1eb8, - 0x1eba, - 0x1ebc, - 0x1ebe, - 0x1ec0, - 0x1ec2, - 0x1ec4, - 0x1ec6, - 0x1ec8, - 0x1eca, - 0x1ecc, - 0x1ece, - 0x1ed0, - 0x1ed2, - 0x1ed4, - 0x1ed6, - 0x1ed8, - 0x1eda, - 0x1edc, - 0x1ede, - 0x1ee0, - 0x1ee2, - 0x1ee4, - 0x1ee6, - 0x1ee8, - 0x1eea, - 0x1eec, - 0x1eee, - 0x1ef0, - 0x1ef2, - 0x1ef4, - 0x1ef6, - 0x1ef8, - 0x1efa, - 0x1efc, - 0x1efe, - 0x1f59, - 0x1f5b, - 0x1f5d, - 0x1f5f, - 0x2132, - 0x2183, - 0x2c60, - 0x2c67, - 0x2c69, - 0x2c6b, - 0x2c72, - 0x2c75, - 0x2c82, - 0x2c84, - 0x2c86, - 0x2c88, - 0x2c8a, - 0x2c8c, - 0x2c8e, - 0x2c90, - 0x2c92, - 0x2c94, - 0x2c96, - 0x2c98, - 0x2c9a, - 0x2c9c, - 0x2c9e, - 0x2ca0, - 0x2ca2, - 0x2ca4, - 0x2ca6, - 0x2ca8, - 0x2caa, - 0x2cac, - 0x2cae, - 0x2cb0, - 0x2cb2, - 0x2cb4, - 0x2cb6, - 0x2cb8, - 0x2cba, - 0x2cbc, - 0x2cbe, - 0x2cc0, - 0x2cc2, - 0x2cc4, - 0x2cc6, - 0x2cc8, - 0x2cca, - 0x2ccc, - 0x2cce, - 0x2cd0, - 0x2cd2, - 0x2cd4, - 0x2cd6, - 0x2cd8, - 0x2cda, - 0x2cdc, - 0x2cde, - 0x2ce0, - 0x2ce2, - 0x2ceb, - 0x2ced, - 0x2cf2, - 0xa640, - 0xa642, - 0xa644, - 0xa646, - 0xa648, - 0xa64a, - 0xa64c, - 0xa64e, - 0xa650, - 0xa652, - 0xa654, - 0xa656, - 0xa658, - 0xa65a, - 0xa65c, - 0xa65e, - 0xa660, - 0xa662, - 0xa664, - 0xa666, - 0xa668, - 0xa66a, - 0xa66c, - 0xa680, - 0xa682, - 0xa684, - 0xa686, - 0xa688, - 0xa68a, - 0xa68c, - 0xa68e, - 0xa690, - 0xa692, - 0xa694, - 0xa696, - 0xa722, - 0xa724, - 0xa726, - 0xa728, - 0xa72a, - 0xa72c, - 0xa72e, - 0xa732, - 0xa734, - 0xa736, - 0xa738, - 0xa73a, - 0xa73c, - 0xa73e, - 0xa740, - 0xa742, - 0xa744, - 0xa746, - 0xa748, - 0xa74a, - 0xa74c, - 0xa74e, - 0xa750, - 0xa752, - 0xa754, - 0xa756, - 0xa758, - 0xa75a, - 0xa75c, - 0xa75e, - 0xa760, - 0xa762, - 0xa764, - 0xa766, - 0xa768, - 0xa76a, - 0xa76c, - 0xa76e, - 0xa779, - 0xa77b, - 0xa780, - 0xa782, - 0xa784, - 0xa786, - 0xa78b, - 0xa78d, - 0xa790, - 0xa792, - 0xa7a0, - 0xa7a2, - 0xa7a4, - 0xa7a6, - 0xa7a8, - 0xa7aa, -}; - -/* }}} */ - -/* {{{ To upper */ - -static uint32_t toupperr[] = { - 0x0061, 0x007a, 1048544, - 0x00e0, 0x00f6, 1048544, - 0x00f8, 0x00fe, 1048544, - 0x023f, 0x0240, 1059391, - 0x0256, 0x0257, 1048371, - 0x028a, 0x028b, 1048359, - 0x037b, 0x037d, 1048706, - 0x03ad, 0x03af, 1048539, - 0x03b1, 0x03c1, 1048544, - 0x03c3, 0x03cb, 1048544, - 0x03cd, 0x03ce, 1048513, - 0x0430, 0x044f, 1048544, - 0x0450, 0x045f, 1048496, - 0x0561, 0x0586, 1048528, - 0x1f00, 0x1f07, 1048584, - 0x1f10, 0x1f15, 1048584, - 0x1f20, 0x1f27, 1048584, - 0x1f30, 0x1f37, 1048584, - 0x1f40, 0x1f45, 1048584, - 0x1f60, 0x1f67, 1048584, - 0x1f70, 0x1f71, 1048650, - 0x1f72, 0x1f75, 1048662, - 0x1f76, 0x1f77, 1048676, - 0x1f78, 0x1f79, 1048704, - 0x1f7a, 0x1f7b, 1048688, - 0x1f7c, 0x1f7d, 1048702, - 0x1f80, 0x1f87, 1048584, - 0x1f90, 0x1f97, 1048584, - 0x1fa0, 0x1fa7, 1048584, - 0x1fb0, 0x1fb1, 1048584, - 0x1fd0, 0x1fd1, 1048584, - 0x1fe0, 0x1fe1, 1048584, - 0x2170, 0x217f, 1048560, - 0x24d0, 0x24e9, 1048550, - 0x2c30, 0x2c5e, 1048528, - 0x2d00, 0x2d25, 1041312, - 0xff41, 0xff5a, 1048544, - 0x10428, 0x1044f, 1048536, -}; - -static uint32_t touppers[] = { - 0x00b5, 1049319, - 0x00ff, 1048697, - 0x0101, 1048575, - 0x0103, 1048575, - 0x0105, 1048575, - 0x0107, 1048575, - 0x0109, 1048575, - 0x010b, 1048575, - 0x010d, 1048575, - 0x010f, 1048575, - 0x0111, 1048575, - 0x0113, 1048575, - 0x0115, 1048575, - 0x0117, 1048575, - 0x0119, 1048575, - 0x011b, 1048575, - 0x011d, 1048575, - 0x011f, 1048575, - 0x0121, 1048575, - 0x0123, 1048575, - 0x0125, 1048575, - 0x0127, 1048575, - 0x0129, 1048575, - 0x012b, 1048575, - 0x012d, 1048575, - 0x012f, 1048575, - 0x0131, 1048344, - 0x0133, 1048575, - 0x0135, 1048575, - 0x0137, 1048575, - 0x013a, 1048575, - 0x013c, 1048575, - 0x013e, 1048575, - 0x0140, 1048575, - 0x0142, 1048575, - 0x0144, 1048575, - 0x0146, 1048575, - 0x0148, 1048575, - 0x014b, 1048575, - 0x014d, 1048575, - 0x014f, 1048575, - 0x0151, 1048575, - 0x0153, 1048575, - 0x0155, 1048575, - 0x0157, 1048575, - 0x0159, 1048575, - 0x015b, 1048575, - 0x015d, 1048575, - 0x015f, 1048575, - 0x0161, 1048575, - 0x0163, 1048575, - 0x0165, 1048575, - 0x0167, 1048575, - 0x0169, 1048575, - 0x016b, 1048575, - 0x016d, 1048575, - 0x016f, 1048575, - 0x0171, 1048575, - 0x0173, 1048575, - 0x0175, 1048575, - 0x0177, 1048575, - 0x017a, 1048575, - 0x017c, 1048575, - 0x017e, 1048575, - 0x017f, 1048276, - 0x0180, 1048771, - 0x0183, 1048575, - 0x0185, 1048575, - 0x0188, 1048575, - 0x018c, 1048575, - 0x0192, 1048575, - 0x0195, 1048673, - 0x0199, 1048575, - 0x019a, 1048739, - 0x019e, 1048706, - 0x01a1, 1048575, - 0x01a3, 1048575, - 0x01a5, 1048575, - 0x01a8, 1048575, - 0x01ad, 1048575, - 0x01b0, 1048575, - 0x01b4, 1048575, - 0x01b6, 1048575, - 0x01b9, 1048575, - 0x01bd, 1048575, - 0x01bf, 1048632, - 0x01c5, 1048575, - 0x01c6, 1048574, - 0x01c8, 1048575, - 0x01c9, 1048574, - 0x01cb, 1048575, - 0x01cc, 1048574, - 0x01ce, 1048575, - 0x01d0, 1048575, - 0x01d2, 1048575, - 0x01d4, 1048575, - 0x01d6, 1048575, - 0x01d8, 1048575, - 0x01da, 1048575, - 0x01dc, 1048575, - 0x01dd, 1048497, - 0x01df, 1048575, - 0x01e1, 1048575, - 0x01e3, 1048575, - 0x01e5, 1048575, - 0x01e7, 1048575, - 0x01e9, 1048575, - 0x01eb, 1048575, - 0x01ed, 1048575, - 0x01ef, 1048575, - 0x01f2, 1048575, - 0x01f3, 1048574, - 0x01f5, 1048575, - 0x01f9, 1048575, - 0x01fb, 1048575, - 0x01fd, 1048575, - 0x01ff, 1048575, - 0x0201, 1048575, - 0x0203, 1048575, - 0x0205, 1048575, - 0x0207, 1048575, - 0x0209, 1048575, - 0x020b, 1048575, - 0x020d, 1048575, - 0x020f, 1048575, - 0x0211, 1048575, - 0x0213, 1048575, - 0x0215, 1048575, - 0x0217, 1048575, - 0x0219, 1048575, - 0x021b, 1048575, - 0x021d, 1048575, - 0x021f, 1048575, - 0x0223, 1048575, - 0x0225, 1048575, - 0x0227, 1048575, - 0x0229, 1048575, - 0x022b, 1048575, - 0x022d, 1048575, - 0x022f, 1048575, - 0x0231, 1048575, - 0x0233, 1048575, - 0x023c, 1048575, - 0x0242, 1048575, - 0x0247, 1048575, - 0x0249, 1048575, - 0x024b, 1048575, - 0x024d, 1048575, - 0x024f, 1048575, - 0x0250, 1059359, - 0x0251, 1059356, - 0x0252, 1059358, - 0x0253, 1048366, - 0x0254, 1048370, - 0x0259, 1048374, - 0x025b, 1048373, - 0x0260, 1048371, - 0x0263, 1048369, - 0x0265, 1090856, - 0x0266, 1090884, - 0x0268, 1048367, - 0x0269, 1048365, - 0x026b, 1059319, - 0x026f, 1048365, - 0x0271, 1059325, - 0x0272, 1048363, - 0x0275, 1048362, - 0x027d, 1059303, - 0x0280, 1048358, - 0x0283, 1048358, - 0x0288, 1048358, - 0x0289, 1048507, - 0x028c, 1048505, - 0x0292, 1048357, - 0x0345, 1048660, - 0x0371, 1048575, - 0x0373, 1048575, - 0x0377, 1048575, - 0x03ac, 1048538, - 0x03c2, 1048545, - 0x03cc, 1048512, - 0x03d0, 1048514, - 0x03d1, 1048519, - 0x03d5, 1048529, - 0x03d6, 1048522, - 0x03d7, 1048568, - 0x03d9, 1048575, - 0x03db, 1048575, - 0x03dd, 1048575, - 0x03df, 1048575, - 0x03e1, 1048575, - 0x03e3, 1048575, - 0x03e5, 1048575, - 0x03e7, 1048575, - 0x03e9, 1048575, - 0x03eb, 1048575, - 0x03ed, 1048575, - 0x03ef, 1048575, - 0x03f0, 1048490, - 0x03f1, 1048496, - 0x03f2, 1048583, - 0x03f5, 1048480, - 0x03f8, 1048575, - 0x03fb, 1048575, - 0x0461, 1048575, - 0x0463, 1048575, - 0x0465, 1048575, - 0x0467, 1048575, - 0x0469, 1048575, - 0x046b, 1048575, - 0x046d, 1048575, - 0x046f, 1048575, - 0x0471, 1048575, - 0x0473, 1048575, - 0x0475, 1048575, - 0x0477, 1048575, - 0x0479, 1048575, - 0x047b, 1048575, - 0x047d, 1048575, - 0x047f, 1048575, - 0x0481, 1048575, - 0x048b, 1048575, - 0x048d, 1048575, - 0x048f, 1048575, - 0x0491, 1048575, - 0x0493, 1048575, - 0x0495, 1048575, - 0x0497, 1048575, - 0x0499, 1048575, - 0x049b, 1048575, - 0x049d, 1048575, - 0x049f, 1048575, - 0x04a1, 1048575, - 0x04a3, 1048575, - 0x04a5, 1048575, - 0x04a7, 1048575, - 0x04a9, 1048575, - 0x04ab, 1048575, - 0x04ad, 1048575, - 0x04af, 1048575, - 0x04b1, 1048575, - 0x04b3, 1048575, - 0x04b5, 1048575, - 0x04b7, 1048575, - 0x04b9, 1048575, - 0x04bb, 1048575, - 0x04bd, 1048575, - 0x04bf, 1048575, - 0x04c2, 1048575, - 0x04c4, 1048575, - 0x04c6, 1048575, - 0x04c8, 1048575, - 0x04ca, 1048575, - 0x04cc, 1048575, - 0x04ce, 1048575, - 0x04cf, 1048561, - 0x04d1, 1048575, - 0x04d3, 1048575, - 0x04d5, 1048575, - 0x04d7, 1048575, - 0x04d9, 1048575, - 0x04db, 1048575, - 0x04dd, 1048575, - 0x04df, 1048575, - 0x04e1, 1048575, - 0x04e3, 1048575, - 0x04e5, 1048575, - 0x04e7, 1048575, - 0x04e9, 1048575, - 0x04eb, 1048575, - 0x04ed, 1048575, - 0x04ef, 1048575, - 0x04f1, 1048575, - 0x04f3, 1048575, - 0x04f5, 1048575, - 0x04f7, 1048575, - 0x04f9, 1048575, - 0x04fb, 1048575, - 0x04fd, 1048575, - 0x04ff, 1048575, - 0x0501, 1048575, - 0x0503, 1048575, - 0x0505, 1048575, - 0x0507, 1048575, - 0x0509, 1048575, - 0x050b, 1048575, - 0x050d, 1048575, - 0x050f, 1048575, - 0x0511, 1048575, - 0x0513, 1048575, - 0x0515, 1048575, - 0x0517, 1048575, - 0x0519, 1048575, - 0x051b, 1048575, - 0x051d, 1048575, - 0x051f, 1048575, - 0x0521, 1048575, - 0x0523, 1048575, - 0x0525, 1048575, - 0x0527, 1048575, - 0x1d79, 1083908, - 0x1d7d, 1052390, - 0x1e01, 1048575, - 0x1e03, 1048575, - 0x1e05, 1048575, - 0x1e07, 1048575, - 0x1e09, 1048575, - 0x1e0b, 1048575, - 0x1e0d, 1048575, - 0x1e0f, 1048575, - 0x1e11, 1048575, - 0x1e13, 1048575, - 0x1e15, 1048575, - 0x1e17, 1048575, - 0x1e19, 1048575, - 0x1e1b, 1048575, - 0x1e1d, 1048575, - 0x1e1f, 1048575, - 0x1e21, 1048575, - 0x1e23, 1048575, - 0x1e25, 1048575, - 0x1e27, 1048575, - 0x1e29, 1048575, - 0x1e2b, 1048575, - 0x1e2d, 1048575, - 0x1e2f, 1048575, - 0x1e31, 1048575, - 0x1e33, 1048575, - 0x1e35, 1048575, - 0x1e37, 1048575, - 0x1e39, 1048575, - 0x1e3b, 1048575, - 0x1e3d, 1048575, - 0x1e3f, 1048575, - 0x1e41, 1048575, - 0x1e43, 1048575, - 0x1e45, 1048575, - 0x1e47, 1048575, - 0x1e49, 1048575, - 0x1e4b, 1048575, - 0x1e4d, 1048575, - 0x1e4f, 1048575, - 0x1e51, 1048575, - 0x1e53, 1048575, - 0x1e55, 1048575, - 0x1e57, 1048575, - 0x1e59, 1048575, - 0x1e5b, 1048575, - 0x1e5d, 1048575, - 0x1e5f, 1048575, - 0x1e61, 1048575, - 0x1e63, 1048575, - 0x1e65, 1048575, - 0x1e67, 1048575, - 0x1e69, 1048575, - 0x1e6b, 1048575, - 0x1e6d, 1048575, - 0x1e6f, 1048575, - 0x1e71, 1048575, - 0x1e73, 1048575, - 0x1e75, 1048575, - 0x1e77, 1048575, - 0x1e79, 1048575, - 0x1e7b, 1048575, - 0x1e7d, 1048575, - 0x1e7f, 1048575, - 0x1e81, 1048575, - 0x1e83, 1048575, - 0x1e85, 1048575, - 0x1e87, 1048575, - 0x1e89, 1048575, - 0x1e8b, 1048575, - 0x1e8d, 1048575, - 0x1e8f, 1048575, - 0x1e91, 1048575, - 0x1e93, 1048575, - 0x1e95, 1048575, - 0x1e9b, 1048517, - 0x1ea1, 1048575, - 0x1ea3, 1048575, - 0x1ea5, 1048575, - 0x1ea7, 1048575, - 0x1ea9, 1048575, - 0x1eab, 1048575, - 0x1ead, 1048575, - 0x1eaf, 1048575, - 0x1eb1, 1048575, - 0x1eb3, 1048575, - 0x1eb5, 1048575, - 0x1eb7, 1048575, - 0x1eb9, 1048575, - 0x1ebb, 1048575, - 0x1ebd, 1048575, - 0x1ebf, 1048575, - 0x1ec1, 1048575, - 0x1ec3, 1048575, - 0x1ec5, 1048575, - 0x1ec7, 1048575, - 0x1ec9, 1048575, - 0x1ecb, 1048575, - 0x1ecd, 1048575, - 0x1ecf, 1048575, - 0x1ed1, 1048575, - 0x1ed3, 1048575, - 0x1ed5, 1048575, - 0x1ed7, 1048575, - 0x1ed9, 1048575, - 0x1edb, 1048575, - 0x1edd, 1048575, - 0x1edf, 1048575, - 0x1ee1, 1048575, - 0x1ee3, 1048575, - 0x1ee5, 1048575, - 0x1ee7, 1048575, - 0x1ee9, 1048575, - 0x1eeb, 1048575, - 0x1eed, 1048575, - 0x1eef, 1048575, - 0x1ef1, 1048575, - 0x1ef3, 1048575, - 0x1ef5, 1048575, - 0x1ef7, 1048575, - 0x1ef9, 1048575, - 0x1efb, 1048575, - 0x1efd, 1048575, - 0x1eff, 1048575, - 0x1f51, 1048584, - 0x1f53, 1048584, - 0x1f55, 1048584, - 0x1f57, 1048584, - 0x1fb3, 1048585, - 0x1fbe, 1041371, - 0x1fc3, 1048585, - 0x1fe5, 1048583, - 0x1ff3, 1048585, - 0x214e, 1048548, - 0x2184, 1048575, - 0x2c61, 1048575, - 0x2c65, 1037781, - 0x2c66, 1037784, - 0x2c68, 1048575, - 0x2c6a, 1048575, - 0x2c6c, 1048575, - 0x2c73, 1048575, - 0x2c76, 1048575, - 0x2c81, 1048575, - 0x2c83, 1048575, - 0x2c85, 1048575, - 0x2c87, 1048575, - 0x2c89, 1048575, - 0x2c8b, 1048575, - 0x2c8d, 1048575, - 0x2c8f, 1048575, - 0x2c91, 1048575, - 0x2c93, 1048575, - 0x2c95, 1048575, - 0x2c97, 1048575, - 0x2c99, 1048575, - 0x2c9b, 1048575, - 0x2c9d, 1048575, - 0x2c9f, 1048575, - 0x2ca1, 1048575, - 0x2ca3, 1048575, - 0x2ca5, 1048575, - 0x2ca7, 1048575, - 0x2ca9, 1048575, - 0x2cab, 1048575, - 0x2cad, 1048575, - 0x2caf, 1048575, - 0x2cb1, 1048575, - 0x2cb3, 1048575, - 0x2cb5, 1048575, - 0x2cb7, 1048575, - 0x2cb9, 1048575, - 0x2cbb, 1048575, - 0x2cbd, 1048575, - 0x2cbf, 1048575, - 0x2cc1, 1048575, - 0x2cc3, 1048575, - 0x2cc5, 1048575, - 0x2cc7, 1048575, - 0x2cc9, 1048575, - 0x2ccb, 1048575, - 0x2ccd, 1048575, - 0x2ccf, 1048575, - 0x2cd1, 1048575, - 0x2cd3, 1048575, - 0x2cd5, 1048575, - 0x2cd7, 1048575, - 0x2cd9, 1048575, - 0x2cdb, 1048575, - 0x2cdd, 1048575, - 0x2cdf, 1048575, - 0x2ce1, 1048575, - 0x2ce3, 1048575, - 0x2cec, 1048575, - 0x2cee, 1048575, - 0x2cf3, 1048575, - 0x2d27, 1041312, - 0x2d2d, 1041312, - 0xa641, 1048575, - 0xa643, 1048575, - 0xa645, 1048575, - 0xa647, 1048575, - 0xa649, 1048575, - 0xa64b, 1048575, - 0xa64d, 1048575, - 0xa64f, 1048575, - 0xa651, 1048575, - 0xa653, 1048575, - 0xa655, 1048575, - 0xa657, 1048575, - 0xa659, 1048575, - 0xa65b, 1048575, - 0xa65d, 1048575, - 0xa65f, 1048575, - 0xa661, 1048575, - 0xa663, 1048575, - 0xa665, 1048575, - 0xa667, 1048575, - 0xa669, 1048575, - 0xa66b, 1048575, - 0xa66d, 1048575, - 0xa681, 1048575, - 0xa683, 1048575, - 0xa685, 1048575, - 0xa687, 1048575, - 0xa689, 1048575, - 0xa68b, 1048575, - 0xa68d, 1048575, - 0xa68f, 1048575, - 0xa691, 1048575, - 0xa693, 1048575, - 0xa695, 1048575, - 0xa697, 1048575, - 0xa723, 1048575, - 0xa725, 1048575, - 0xa727, 1048575, - 0xa729, 1048575, - 0xa72b, 1048575, - 0xa72d, 1048575, - 0xa72f, 1048575, - 0xa733, 1048575, - 0xa735, 1048575, - 0xa737, 1048575, - 0xa739, 1048575, - 0xa73b, 1048575, - 0xa73d, 1048575, - 0xa73f, 1048575, - 0xa741, 1048575, - 0xa743, 1048575, - 0xa745, 1048575, - 0xa747, 1048575, - 0xa749, 1048575, - 0xa74b, 1048575, - 0xa74d, 1048575, - 0xa74f, 1048575, - 0xa751, 1048575, - 0xa753, 1048575, - 0xa755, 1048575, - 0xa757, 1048575, - 0xa759, 1048575, - 0xa75b, 1048575, - 0xa75d, 1048575, - 0xa75f, 1048575, - 0xa761, 1048575, - 0xa763, 1048575, - 0xa765, 1048575, - 0xa767, 1048575, - 0xa769, 1048575, - 0xa76b, 1048575, - 0xa76d, 1048575, - 0xa76f, 1048575, - 0xa77a, 1048575, - 0xa77c, 1048575, - 0xa77f, 1048575, - 0xa781, 1048575, - 0xa783, 1048575, - 0xa785, 1048575, - 0xa787, 1048575, - 0xa78c, 1048575, - 0xa791, 1048575, - 0xa793, 1048575, - 0xa7a1, 1048575, - 0xa7a3, 1048575, - 0xa7a5, 1048575, - 0xa7a7, 1048575, - 0xa7a9, 1048575, -}; - -/* }}} */ - -/* {{{ To lower */ - -static uint32_t tolowerr[] = { - 0x0041, 0x005a, 1048608, - 0x00c0, 0x00d6, 1048608, - 0x00d8, 0x00de, 1048608, - 0x0189, 0x018a, 1048781, - 0x01b1, 0x01b2, 1048793, - 0x0388, 0x038a, 1048613, - 0x038e, 0x038f, 1048639, - 0x0391, 0x03a1, 1048608, - 0x03a3, 0x03ab, 1048608, - 0x03fd, 0x03ff, 1048446, - 0x0400, 0x040f, 1048656, - 0x0410, 0x042f, 1048608, - 0x0531, 0x0556, 1048624, - 0x10a0, 0x10c5, 1055840, - 0x1f08, 0x1f0f, 1048568, - 0x1f18, 0x1f1d, 1048568, - 0x1f28, 0x1f2f, 1048568, - 0x1f38, 0x1f3f, 1048568, - 0x1f48, 0x1f4d, 1048568, - 0x1f68, 0x1f6f, 1048568, - 0x1f88, 0x1f8f, 1048568, - 0x1f98, 0x1f9f, 1048568, - 0x1fa8, 0x1faf, 1048568, - 0x1fb8, 0x1fb9, 1048568, - 0x1fba, 0x1fbb, 1048502, - 0x1fc8, 0x1fcb, 1048490, - 0x1fd8, 0x1fd9, 1048568, - 0x1fda, 0x1fdb, 1048476, - 0x1fe8, 0x1fe9, 1048568, - 0x1fea, 0x1feb, 1048464, - 0x1ff8, 0x1ff9, 1048448, - 0x1ffa, 0x1ffb, 1048450, - 0x2160, 0x216f, 1048592, - 0x24b6, 0x24cf, 1048602, - 0x2c00, 0x2c2e, 1048624, - 0x2c7e, 0x2c7f, 1037761, - 0xff21, 0xff3a, 1048608, - 0x10400, 0x10427, 1048616, -}; - -static uint32_t tolowers[] = { - 0x0100, 1048577, - 0x0102, 1048577, - 0x0104, 1048577, - 0x0106, 1048577, - 0x0108, 1048577, - 0x010a, 1048577, - 0x010c, 1048577, - 0x010e, 1048577, - 0x0110, 1048577, - 0x0112, 1048577, - 0x0114, 1048577, - 0x0116, 1048577, - 0x0118, 1048577, - 0x011a, 1048577, - 0x011c, 1048577, - 0x011e, 1048577, - 0x0120, 1048577, - 0x0122, 1048577, - 0x0124, 1048577, - 0x0126, 1048577, - 0x0128, 1048577, - 0x012a, 1048577, - 0x012c, 1048577, - 0x012e, 1048577, - 0x0130, 1048377, - 0x0132, 1048577, - 0x0134, 1048577, - 0x0136, 1048577, - 0x0139, 1048577, - 0x013b, 1048577, - 0x013d, 1048577, - 0x013f, 1048577, - 0x0141, 1048577, - 0x0143, 1048577, - 0x0145, 1048577, - 0x0147, 1048577, - 0x014a, 1048577, - 0x014c, 1048577, - 0x014e, 1048577, - 0x0150, 1048577, - 0x0152, 1048577, - 0x0154, 1048577, - 0x0156, 1048577, - 0x0158, 1048577, - 0x015a, 1048577, - 0x015c, 1048577, - 0x015e, 1048577, - 0x0160, 1048577, - 0x0162, 1048577, - 0x0164, 1048577, - 0x0166, 1048577, - 0x0168, 1048577, - 0x016a, 1048577, - 0x016c, 1048577, - 0x016e, 1048577, - 0x0170, 1048577, - 0x0172, 1048577, - 0x0174, 1048577, - 0x0176, 1048577, - 0x0178, 1048455, - 0x0179, 1048577, - 0x017b, 1048577, - 0x017d, 1048577, - 0x0181, 1048786, - 0x0182, 1048577, - 0x0184, 1048577, - 0x0186, 1048782, - 0x0187, 1048577, - 0x018b, 1048577, - 0x018e, 1048655, - 0x018f, 1048778, - 0x0190, 1048779, - 0x0191, 1048577, - 0x0193, 1048781, - 0x0194, 1048783, - 0x0196, 1048787, - 0x0197, 1048785, - 0x0198, 1048577, - 0x019c, 1048787, - 0x019d, 1048789, - 0x019f, 1048790, - 0x01a0, 1048577, - 0x01a2, 1048577, - 0x01a4, 1048577, - 0x01a6, 1048794, - 0x01a7, 1048577, - 0x01a9, 1048794, - 0x01ac, 1048577, - 0x01ae, 1048794, - 0x01af, 1048577, - 0x01b3, 1048577, - 0x01b5, 1048577, - 0x01b7, 1048795, - 0x01b8, 1048577, - 0x01bc, 1048577, - 0x01c4, 1048578, - 0x01c5, 1048577, - 0x01c7, 1048578, - 0x01c8, 1048577, - 0x01ca, 1048578, - 0x01cb, 1048577, - 0x01cd, 1048577, - 0x01cf, 1048577, - 0x01d1, 1048577, - 0x01d3, 1048577, - 0x01d5, 1048577, - 0x01d7, 1048577, - 0x01d9, 1048577, - 0x01db, 1048577, - 0x01de, 1048577, - 0x01e0, 1048577, - 0x01e2, 1048577, - 0x01e4, 1048577, - 0x01e6, 1048577, - 0x01e8, 1048577, - 0x01ea, 1048577, - 0x01ec, 1048577, - 0x01ee, 1048577, - 0x01f1, 1048578, - 0x01f2, 1048577, - 0x01f4, 1048577, - 0x01f6, 1048479, - 0x01f7, 1048520, - 0x01f8, 1048577, - 0x01fa, 1048577, - 0x01fc, 1048577, - 0x01fe, 1048577, - 0x0200, 1048577, - 0x0202, 1048577, - 0x0204, 1048577, - 0x0206, 1048577, - 0x0208, 1048577, - 0x020a, 1048577, - 0x020c, 1048577, - 0x020e, 1048577, - 0x0210, 1048577, - 0x0212, 1048577, - 0x0214, 1048577, - 0x0216, 1048577, - 0x0218, 1048577, - 0x021a, 1048577, - 0x021c, 1048577, - 0x021e, 1048577, - 0x0220, 1048446, - 0x0222, 1048577, - 0x0224, 1048577, - 0x0226, 1048577, - 0x0228, 1048577, - 0x022a, 1048577, - 0x022c, 1048577, - 0x022e, 1048577, - 0x0230, 1048577, - 0x0232, 1048577, - 0x023a, 1059371, - 0x023b, 1048577, - 0x023d, 1048413, - 0x023e, 1059368, - 0x0241, 1048577, - 0x0243, 1048381, - 0x0244, 1048645, - 0x0245, 1048647, - 0x0246, 1048577, - 0x0248, 1048577, - 0x024a, 1048577, - 0x024c, 1048577, - 0x024e, 1048577, - 0x0370, 1048577, - 0x0372, 1048577, - 0x0376, 1048577, - 0x0386, 1048614, - 0x038c, 1048640, - 0x03cf, 1048584, - 0x03d8, 1048577, - 0x03da, 1048577, - 0x03dc, 1048577, - 0x03de, 1048577, - 0x03e0, 1048577, - 0x03e2, 1048577, - 0x03e4, 1048577, - 0x03e6, 1048577, - 0x03e8, 1048577, - 0x03ea, 1048577, - 0x03ec, 1048577, - 0x03ee, 1048577, - 0x03f4, 1048516, - 0x03f7, 1048577, - 0x03f9, 1048569, - 0x03fa, 1048577, - 0x0460, 1048577, - 0x0462, 1048577, - 0x0464, 1048577, - 0x0466, 1048577, - 0x0468, 1048577, - 0x046a, 1048577, - 0x046c, 1048577, - 0x046e, 1048577, - 0x0470, 1048577, - 0x0472, 1048577, - 0x0474, 1048577, - 0x0476, 1048577, - 0x0478, 1048577, - 0x047a, 1048577, - 0x047c, 1048577, - 0x047e, 1048577, - 0x0480, 1048577, - 0x048a, 1048577, - 0x048c, 1048577, - 0x048e, 1048577, - 0x0490, 1048577, - 0x0492, 1048577, - 0x0494, 1048577, - 0x0496, 1048577, - 0x0498, 1048577, - 0x049a, 1048577, - 0x049c, 1048577, - 0x049e, 1048577, - 0x04a0, 1048577, - 0x04a2, 1048577, - 0x04a4, 1048577, - 0x04a6, 1048577, - 0x04a8, 1048577, - 0x04aa, 1048577, - 0x04ac, 1048577, - 0x04ae, 1048577, - 0x04b0, 1048577, - 0x04b2, 1048577, - 0x04b4, 1048577, - 0x04b6, 1048577, - 0x04b8, 1048577, - 0x04ba, 1048577, - 0x04bc, 1048577, - 0x04be, 1048577, - 0x04c0, 1048591, - 0x04c1, 1048577, - 0x04c3, 1048577, - 0x04c5, 1048577, - 0x04c7, 1048577, - 0x04c9, 1048577, - 0x04cb, 1048577, - 0x04cd, 1048577, - 0x04d0, 1048577, - 0x04d2, 1048577, - 0x04d4, 1048577, - 0x04d6, 1048577, - 0x04d8, 1048577, - 0x04da, 1048577, - 0x04dc, 1048577, - 0x04de, 1048577, - 0x04e0, 1048577, - 0x04e2, 1048577, - 0x04e4, 1048577, - 0x04e6, 1048577, - 0x04e8, 1048577, - 0x04ea, 1048577, - 0x04ec, 1048577, - 0x04ee, 1048577, - 0x04f0, 1048577, - 0x04f2, 1048577, - 0x04f4, 1048577, - 0x04f6, 1048577, - 0x04f8, 1048577, - 0x04fa, 1048577, - 0x04fc, 1048577, - 0x04fe, 1048577, - 0x0500, 1048577, - 0x0502, 1048577, - 0x0504, 1048577, - 0x0506, 1048577, - 0x0508, 1048577, - 0x050a, 1048577, - 0x050c, 1048577, - 0x050e, 1048577, - 0x0510, 1048577, - 0x0512, 1048577, - 0x0514, 1048577, - 0x0516, 1048577, - 0x0518, 1048577, - 0x051a, 1048577, - 0x051c, 1048577, - 0x051e, 1048577, - 0x0520, 1048577, - 0x0522, 1048577, - 0x0524, 1048577, - 0x0526, 1048577, - 0x10c7, 1055840, - 0x10cd, 1055840, - 0x1e00, 1048577, - 0x1e02, 1048577, - 0x1e04, 1048577, - 0x1e06, 1048577, - 0x1e08, 1048577, - 0x1e0a, 1048577, - 0x1e0c, 1048577, - 0x1e0e, 1048577, - 0x1e10, 1048577, - 0x1e12, 1048577, - 0x1e14, 1048577, - 0x1e16, 1048577, - 0x1e18, 1048577, - 0x1e1a, 1048577, - 0x1e1c, 1048577, - 0x1e1e, 1048577, - 0x1e20, 1048577, - 0x1e22, 1048577, - 0x1e24, 1048577, - 0x1e26, 1048577, - 0x1e28, 1048577, - 0x1e2a, 1048577, - 0x1e2c, 1048577, - 0x1e2e, 1048577, - 0x1e30, 1048577, - 0x1e32, 1048577, - 0x1e34, 1048577, - 0x1e36, 1048577, - 0x1e38, 1048577, - 0x1e3a, 1048577, - 0x1e3c, 1048577, - 0x1e3e, 1048577, - 0x1e40, 1048577, - 0x1e42, 1048577, - 0x1e44, 1048577, - 0x1e46, 1048577, - 0x1e48, 1048577, - 0x1e4a, 1048577, - 0x1e4c, 1048577, - 0x1e4e, 1048577, - 0x1e50, 1048577, - 0x1e52, 1048577, - 0x1e54, 1048577, - 0x1e56, 1048577, - 0x1e58, 1048577, - 0x1e5a, 1048577, - 0x1e5c, 1048577, - 0x1e5e, 1048577, - 0x1e60, 1048577, - 0x1e62, 1048577, - 0x1e64, 1048577, - 0x1e66, 1048577, - 0x1e68, 1048577, - 0x1e6a, 1048577, - 0x1e6c, 1048577, - 0x1e6e, 1048577, - 0x1e70, 1048577, - 0x1e72, 1048577, - 0x1e74, 1048577, - 0x1e76, 1048577, - 0x1e78, 1048577, - 0x1e7a, 1048577, - 0x1e7c, 1048577, - 0x1e7e, 1048577, - 0x1e80, 1048577, - 0x1e82, 1048577, - 0x1e84, 1048577, - 0x1e86, 1048577, - 0x1e88, 1048577, - 0x1e8a, 1048577, - 0x1e8c, 1048577, - 0x1e8e, 1048577, - 0x1e90, 1048577, - 0x1e92, 1048577, - 0x1e94, 1048577, - 0x1e9e, 1040961, - 0x1ea0, 1048577, - 0x1ea2, 1048577, - 0x1ea4, 1048577, - 0x1ea6, 1048577, - 0x1ea8, 1048577, - 0x1eaa, 1048577, - 0x1eac, 1048577, - 0x1eae, 1048577, - 0x1eb0, 1048577, - 0x1eb2, 1048577, - 0x1eb4, 1048577, - 0x1eb6, 1048577, - 0x1eb8, 1048577, - 0x1eba, 1048577, - 0x1ebc, 1048577, - 0x1ebe, 1048577, - 0x1ec0, 1048577, - 0x1ec2, 1048577, - 0x1ec4, 1048577, - 0x1ec6, 1048577, - 0x1ec8, 1048577, - 0x1eca, 1048577, - 0x1ecc, 1048577, - 0x1ece, 1048577, - 0x1ed0, 1048577, - 0x1ed2, 1048577, - 0x1ed4, 1048577, - 0x1ed6, 1048577, - 0x1ed8, 1048577, - 0x1eda, 1048577, - 0x1edc, 1048577, - 0x1ede, 1048577, - 0x1ee0, 1048577, - 0x1ee2, 1048577, - 0x1ee4, 1048577, - 0x1ee6, 1048577, - 0x1ee8, 1048577, - 0x1eea, 1048577, - 0x1eec, 1048577, - 0x1eee, 1048577, - 0x1ef0, 1048577, - 0x1ef2, 1048577, - 0x1ef4, 1048577, - 0x1ef6, 1048577, - 0x1ef8, 1048577, - 0x1efa, 1048577, - 0x1efc, 1048577, - 0x1efe, 1048577, - 0x1f59, 1048568, - 0x1f5b, 1048568, - 0x1f5d, 1048568, - 0x1f5f, 1048568, - 0x1fbc, 1048567, - 0x1fcc, 1048567, - 0x1fec, 1048569, - 0x1ffc, 1048567, - 0x2126, 1041059, - 0x212a, 1040193, - 0x212b, 1040314, - 0x2132, 1048604, - 0x2183, 1048577, - 0x2c60, 1048577, - 0x2c62, 1037833, - 0x2c63, 1044762, - 0x2c64, 1037849, - 0x2c67, 1048577, - 0x2c69, 1048577, - 0x2c6b, 1048577, - 0x2c6d, 1037796, - 0x2c6e, 1037827, - 0x2c6f, 1037793, - 0x2c70, 1037794, - 0x2c72, 1048577, - 0x2c75, 1048577, - 0x2c80, 1048577, - 0x2c82, 1048577, - 0x2c84, 1048577, - 0x2c86, 1048577, - 0x2c88, 1048577, - 0x2c8a, 1048577, - 0x2c8c, 1048577, - 0x2c8e, 1048577, - 0x2c90, 1048577, - 0x2c92, 1048577, - 0x2c94, 1048577, - 0x2c96, 1048577, - 0x2c98, 1048577, - 0x2c9a, 1048577, - 0x2c9c, 1048577, - 0x2c9e, 1048577, - 0x2ca0, 1048577, - 0x2ca2, 1048577, - 0x2ca4, 1048577, - 0x2ca6, 1048577, - 0x2ca8, 1048577, - 0x2caa, 1048577, - 0x2cac, 1048577, - 0x2cae, 1048577, - 0x2cb0, 1048577, - 0x2cb2, 1048577, - 0x2cb4, 1048577, - 0x2cb6, 1048577, - 0x2cb8, 1048577, - 0x2cba, 1048577, - 0x2cbc, 1048577, - 0x2cbe, 1048577, - 0x2cc0, 1048577, - 0x2cc2, 1048577, - 0x2cc4, 1048577, - 0x2cc6, 1048577, - 0x2cc8, 1048577, - 0x2cca, 1048577, - 0x2ccc, 1048577, - 0x2cce, 1048577, - 0x2cd0, 1048577, - 0x2cd2, 1048577, - 0x2cd4, 1048577, - 0x2cd6, 1048577, - 0x2cd8, 1048577, - 0x2cda, 1048577, - 0x2cdc, 1048577, - 0x2cde, 1048577, - 0x2ce0, 1048577, - 0x2ce2, 1048577, - 0x2ceb, 1048577, - 0x2ced, 1048577, - 0x2cf2, 1048577, - 0xa640, 1048577, - 0xa642, 1048577, - 0xa644, 1048577, - 0xa646, 1048577, - 0xa648, 1048577, - 0xa64a, 1048577, - 0xa64c, 1048577, - 0xa64e, 1048577, - 0xa650, 1048577, - 0xa652, 1048577, - 0xa654, 1048577, - 0xa656, 1048577, - 0xa658, 1048577, - 0xa65a, 1048577, - 0xa65c, 1048577, - 0xa65e, 1048577, - 0xa660, 1048577, - 0xa662, 1048577, - 0xa664, 1048577, - 0xa666, 1048577, - 0xa668, 1048577, - 0xa66a, 1048577, - 0xa66c, 1048577, - 0xa680, 1048577, - 0xa682, 1048577, - 0xa684, 1048577, - 0xa686, 1048577, - 0xa688, 1048577, - 0xa68a, 1048577, - 0xa68c, 1048577, - 0xa68e, 1048577, - 0xa690, 1048577, - 0xa692, 1048577, - 0xa694, 1048577, - 0xa696, 1048577, - 0xa722, 1048577, - 0xa724, 1048577, - 0xa726, 1048577, - 0xa728, 1048577, - 0xa72a, 1048577, - 0xa72c, 1048577, - 0xa72e, 1048577, - 0xa732, 1048577, - 0xa734, 1048577, - 0xa736, 1048577, - 0xa738, 1048577, - 0xa73a, 1048577, - 0xa73c, 1048577, - 0xa73e, 1048577, - 0xa740, 1048577, - 0xa742, 1048577, - 0xa744, 1048577, - 0xa746, 1048577, - 0xa748, 1048577, - 0xa74a, 1048577, - 0xa74c, 1048577, - 0xa74e, 1048577, - 0xa750, 1048577, - 0xa752, 1048577, - 0xa754, 1048577, - 0xa756, 1048577, - 0xa758, 1048577, - 0xa75a, 1048577, - 0xa75c, 1048577, - 0xa75e, 1048577, - 0xa760, 1048577, - 0xa762, 1048577, - 0xa764, 1048577, - 0xa766, 1048577, - 0xa768, 1048577, - 0xa76a, 1048577, - 0xa76c, 1048577, - 0xa76e, 1048577, - 0xa779, 1048577, - 0xa77b, 1048577, - 0xa77d, 1013244, - 0xa77e, 1048577, - 0xa780, 1048577, - 0xa782, 1048577, - 0xa784, 1048577, - 0xa786, 1048577, - 0xa78b, 1048577, - 0xa78d, 1006296, - 0xa790, 1048577, - 0xa792, 1048577, - 0xa7a0, 1048577, - 0xa7a2, 1048577, - 0xa7a4, 1048577, - 0xa7a6, 1048577, - 0xa7a8, 1048577, - 0xa7aa, 1006268, -}; - -/* }}} */ - -/* {{{ To title */ - -static uint32_t totitler[] = { - 0x0061, 0x007a, 1048544, - 0x00e0, 0x00f6, 1048544, - 0x00f8, 0x00fe, 1048544, - 0x023f, 0x0240, 1059391, - 0x0256, 0x0257, 1048371, - 0x028a, 0x028b, 1048359, - 0x037b, 0x037d, 1048706, - 0x03ad, 0x03af, 1048539, - 0x03b1, 0x03c1, 1048544, - 0x03c3, 0x03cb, 1048544, - 0x03cd, 0x03ce, 1048513, - 0x0430, 0x044f, 1048544, - 0x0450, 0x045f, 1048496, - 0x0561, 0x0586, 1048528, - 0x1f00, 0x1f07, 1048584, - 0x1f10, 0x1f15, 1048584, - 0x1f20, 0x1f27, 1048584, - 0x1f30, 0x1f37, 1048584, - 0x1f40, 0x1f45, 1048584, - 0x1f60, 0x1f67, 1048584, - 0x1f70, 0x1f71, 1048650, - 0x1f72, 0x1f75, 1048662, - 0x1f76, 0x1f77, 1048676, - 0x1f78, 0x1f79, 1048704, - 0x1f7a, 0x1f7b, 1048688, - 0x1f7c, 0x1f7d, 1048702, - 0x1f80, 0x1f87, 1048584, - 0x1f90, 0x1f97, 1048584, - 0x1fa0, 0x1fa7, 1048584, - 0x1fb0, 0x1fb1, 1048584, - 0x1fd0, 0x1fd1, 1048584, - 0x1fe0, 0x1fe1, 1048584, - 0x2170, 0x217f, 1048560, - 0x24d0, 0x24e9, 1048550, - 0x2c30, 0x2c5e, 1048528, - 0x2d00, 0x2d25, 1041312, - 0xff41, 0xff5a, 1048544, - 0x10428, 0x1044f, 1048536, -}; - -static uint32_t totitles[] = { - 0x00b5, 1049319, - 0x00ff, 1048697, - 0x0101, 1048575, - 0x0103, 1048575, - 0x0105, 1048575, - 0x0107, 1048575, - 0x0109, 1048575, - 0x010b, 1048575, - 0x010d, 1048575, - 0x010f, 1048575, - 0x0111, 1048575, - 0x0113, 1048575, - 0x0115, 1048575, - 0x0117, 1048575, - 0x0119, 1048575, - 0x011b, 1048575, - 0x011d, 1048575, - 0x011f, 1048575, - 0x0121, 1048575, - 0x0123, 1048575, - 0x0125, 1048575, - 0x0127, 1048575, - 0x0129, 1048575, - 0x012b, 1048575, - 0x012d, 1048575, - 0x012f, 1048575, - 0x0131, 1048344, - 0x0133, 1048575, - 0x0135, 1048575, - 0x0137, 1048575, - 0x013a, 1048575, - 0x013c, 1048575, - 0x013e, 1048575, - 0x0140, 1048575, - 0x0142, 1048575, - 0x0144, 1048575, - 0x0146, 1048575, - 0x0148, 1048575, - 0x014b, 1048575, - 0x014d, 1048575, - 0x014f, 1048575, - 0x0151, 1048575, - 0x0153, 1048575, - 0x0155, 1048575, - 0x0157, 1048575, - 0x0159, 1048575, - 0x015b, 1048575, - 0x015d, 1048575, - 0x015f, 1048575, - 0x0161, 1048575, - 0x0163, 1048575, - 0x0165, 1048575, - 0x0167, 1048575, - 0x0169, 1048575, - 0x016b, 1048575, - 0x016d, 1048575, - 0x016f, 1048575, - 0x0171, 1048575, - 0x0173, 1048575, - 0x0175, 1048575, - 0x0177, 1048575, - 0x017a, 1048575, - 0x017c, 1048575, - 0x017e, 1048575, - 0x017f, 1048276, - 0x0180, 1048771, - 0x0183, 1048575, - 0x0185, 1048575, - 0x0188, 1048575, - 0x018c, 1048575, - 0x0192, 1048575, - 0x0195, 1048673, - 0x0199, 1048575, - 0x019a, 1048739, - 0x019e, 1048706, - 0x01a1, 1048575, - 0x01a3, 1048575, - 0x01a5, 1048575, - 0x01a8, 1048575, - 0x01ad, 1048575, - 0x01b0, 1048575, - 0x01b4, 1048575, - 0x01b6, 1048575, - 0x01b9, 1048575, - 0x01bd, 1048575, - 0x01bf, 1048632, - 0x01c4, 1048577, - 0x01c6, 1048575, - 0x01c7, 1048577, - 0x01c9, 1048575, - 0x01ca, 1048577, - 0x01cc, 1048575, - 0x01ce, 1048575, - 0x01d0, 1048575, - 0x01d2, 1048575, - 0x01d4, 1048575, - 0x01d6, 1048575, - 0x01d8, 1048575, - 0x01da, 1048575, - 0x01dc, 1048575, - 0x01dd, 1048497, - 0x01df, 1048575, - 0x01e1, 1048575, - 0x01e3, 1048575, - 0x01e5, 1048575, - 0x01e7, 1048575, - 0x01e9, 1048575, - 0x01eb, 1048575, - 0x01ed, 1048575, - 0x01ef, 1048575, - 0x01f1, 1048577, - 0x01f3, 1048575, - 0x01f5, 1048575, - 0x01f9, 1048575, - 0x01fb, 1048575, - 0x01fd, 1048575, - 0x01ff, 1048575, - 0x0201, 1048575, - 0x0203, 1048575, - 0x0205, 1048575, - 0x0207, 1048575, - 0x0209, 1048575, - 0x020b, 1048575, - 0x020d, 1048575, - 0x020f, 1048575, - 0x0211, 1048575, - 0x0213, 1048575, - 0x0215, 1048575, - 0x0217, 1048575, - 0x0219, 1048575, - 0x021b, 1048575, - 0x021d, 1048575, - 0x021f, 1048575, - 0x0223, 1048575, - 0x0225, 1048575, - 0x0227, 1048575, - 0x0229, 1048575, - 0x022b, 1048575, - 0x022d, 1048575, - 0x022f, 1048575, - 0x0231, 1048575, - 0x0233, 1048575, - 0x023c, 1048575, - 0x0242, 1048575, - 0x0247, 1048575, - 0x0249, 1048575, - 0x024b, 1048575, - 0x024d, 1048575, - 0x024f, 1048575, - 0x0250, 1059359, - 0x0251, 1059356, - 0x0252, 1059358, - 0x0253, 1048366, - 0x0254, 1048370, - 0x0259, 1048374, - 0x025b, 1048373, - 0x0260, 1048371, - 0x0263, 1048369, - 0x0265, 1090856, - 0x0266, 1090884, - 0x0268, 1048367, - 0x0269, 1048365, - 0x026b, 1059319, - 0x026f, 1048365, - 0x0271, 1059325, - 0x0272, 1048363, - 0x0275, 1048362, - 0x027d, 1059303, - 0x0280, 1048358, - 0x0283, 1048358, - 0x0288, 1048358, - 0x0289, 1048507, - 0x028c, 1048505, - 0x0292, 1048357, - 0x0345, 1048660, - 0x0371, 1048575, - 0x0373, 1048575, - 0x0377, 1048575, - 0x03ac, 1048538, - 0x03c2, 1048545, - 0x03cc, 1048512, - 0x03d0, 1048514, - 0x03d1, 1048519, - 0x03d5, 1048529, - 0x03d6, 1048522, - 0x03d7, 1048568, - 0x03d9, 1048575, - 0x03db, 1048575, - 0x03dd, 1048575, - 0x03df, 1048575, - 0x03e1, 1048575, - 0x03e3, 1048575, - 0x03e5, 1048575, - 0x03e7, 1048575, - 0x03e9, 1048575, - 0x03eb, 1048575, - 0x03ed, 1048575, - 0x03ef, 1048575, - 0x03f0, 1048490, - 0x03f1, 1048496, - 0x03f2, 1048583, - 0x03f5, 1048480, - 0x03f8, 1048575, - 0x03fb, 1048575, - 0x0461, 1048575, - 0x0463, 1048575, - 0x0465, 1048575, - 0x0467, 1048575, - 0x0469, 1048575, - 0x046b, 1048575, - 0x046d, 1048575, - 0x046f, 1048575, - 0x0471, 1048575, - 0x0473, 1048575, - 0x0475, 1048575, - 0x0477, 1048575, - 0x0479, 1048575, - 0x047b, 1048575, - 0x047d, 1048575, - 0x047f, 1048575, - 0x0481, 1048575, - 0x048b, 1048575, - 0x048d, 1048575, - 0x048f, 1048575, - 0x0491, 1048575, - 0x0493, 1048575, - 0x0495, 1048575, - 0x0497, 1048575, - 0x0499, 1048575, - 0x049b, 1048575, - 0x049d, 1048575, - 0x049f, 1048575, - 0x04a1, 1048575, - 0x04a3, 1048575, - 0x04a5, 1048575, - 0x04a7, 1048575, - 0x04a9, 1048575, - 0x04ab, 1048575, - 0x04ad, 1048575, - 0x04af, 1048575, - 0x04b1, 1048575, - 0x04b3, 1048575, - 0x04b5, 1048575, - 0x04b7, 1048575, - 0x04b9, 1048575, - 0x04bb, 1048575, - 0x04bd, 1048575, - 0x04bf, 1048575, - 0x04c2, 1048575, - 0x04c4, 1048575, - 0x04c6, 1048575, - 0x04c8, 1048575, - 0x04ca, 1048575, - 0x04cc, 1048575, - 0x04ce, 1048575, - 0x04cf, 1048561, - 0x04d1, 1048575, - 0x04d3, 1048575, - 0x04d5, 1048575, - 0x04d7, 1048575, - 0x04d9, 1048575, - 0x04db, 1048575, - 0x04dd, 1048575, - 0x04df, 1048575, - 0x04e1, 1048575, - 0x04e3, 1048575, - 0x04e5, 1048575, - 0x04e7, 1048575, - 0x04e9, 1048575, - 0x04eb, 1048575, - 0x04ed, 1048575, - 0x04ef, 1048575, - 0x04f1, 1048575, - 0x04f3, 1048575, - 0x04f5, 1048575, - 0x04f7, 1048575, - 0x04f9, 1048575, - 0x04fb, 1048575, - 0x04fd, 1048575, - 0x04ff, 1048575, - 0x0501, 1048575, - 0x0503, 1048575, - 0x0505, 1048575, - 0x0507, 1048575, - 0x0509, 1048575, - 0x050b, 1048575, - 0x050d, 1048575, - 0x050f, 1048575, - 0x0511, 1048575, - 0x0513, 1048575, - 0x0515, 1048575, - 0x0517, 1048575, - 0x0519, 1048575, - 0x051b, 1048575, - 0x051d, 1048575, - 0x051f, 1048575, - 0x0521, 1048575, - 0x0523, 1048575, - 0x0525, 1048575, - 0x0527, 1048575, - 0x1d79, 1083908, - 0x1d7d, 1052390, - 0x1e01, 1048575, - 0x1e03, 1048575, - 0x1e05, 1048575, - 0x1e07, 1048575, - 0x1e09, 1048575, - 0x1e0b, 1048575, - 0x1e0d, 1048575, - 0x1e0f, 1048575, - 0x1e11, 1048575, - 0x1e13, 1048575, - 0x1e15, 1048575, - 0x1e17, 1048575, - 0x1e19, 1048575, - 0x1e1b, 1048575, - 0x1e1d, 1048575, - 0x1e1f, 1048575, - 0x1e21, 1048575, - 0x1e23, 1048575, - 0x1e25, 1048575, - 0x1e27, 1048575, - 0x1e29, 1048575, - 0x1e2b, 1048575, - 0x1e2d, 1048575, - 0x1e2f, 1048575, - 0x1e31, 1048575, - 0x1e33, 1048575, - 0x1e35, 1048575, - 0x1e37, 1048575, - 0x1e39, 1048575, - 0x1e3b, 1048575, - 0x1e3d, 1048575, - 0x1e3f, 1048575, - 0x1e41, 1048575, - 0x1e43, 1048575, - 0x1e45, 1048575, - 0x1e47, 1048575, - 0x1e49, 1048575, - 0x1e4b, 1048575, - 0x1e4d, 1048575, - 0x1e4f, 1048575, - 0x1e51, 1048575, - 0x1e53, 1048575, - 0x1e55, 1048575, - 0x1e57, 1048575, - 0x1e59, 1048575, - 0x1e5b, 1048575, - 0x1e5d, 1048575, - 0x1e5f, 1048575, - 0x1e61, 1048575, - 0x1e63, 1048575, - 0x1e65, 1048575, - 0x1e67, 1048575, - 0x1e69, 1048575, - 0x1e6b, 1048575, - 0x1e6d, 1048575, - 0x1e6f, 1048575, - 0x1e71, 1048575, - 0x1e73, 1048575, - 0x1e75, 1048575, - 0x1e77, 1048575, - 0x1e79, 1048575, - 0x1e7b, 1048575, - 0x1e7d, 1048575, - 0x1e7f, 1048575, - 0x1e81, 1048575, - 0x1e83, 1048575, - 0x1e85, 1048575, - 0x1e87, 1048575, - 0x1e89, 1048575, - 0x1e8b, 1048575, - 0x1e8d, 1048575, - 0x1e8f, 1048575, - 0x1e91, 1048575, - 0x1e93, 1048575, - 0x1e95, 1048575, - 0x1e9b, 1048517, - 0x1ea1, 1048575, - 0x1ea3, 1048575, - 0x1ea5, 1048575, - 0x1ea7, 1048575, - 0x1ea9, 1048575, - 0x1eab, 1048575, - 0x1ead, 1048575, - 0x1eaf, 1048575, - 0x1eb1, 1048575, - 0x1eb3, 1048575, - 0x1eb5, 1048575, - 0x1eb7, 1048575, - 0x1eb9, 1048575, - 0x1ebb, 1048575, - 0x1ebd, 1048575, - 0x1ebf, 1048575, - 0x1ec1, 1048575, - 0x1ec3, 1048575, - 0x1ec5, 1048575, - 0x1ec7, 1048575, - 0x1ec9, 1048575, - 0x1ecb, 1048575, - 0x1ecd, 1048575, - 0x1ecf, 1048575, - 0x1ed1, 1048575, - 0x1ed3, 1048575, - 0x1ed5, 1048575, - 0x1ed7, 1048575, - 0x1ed9, 1048575, - 0x1edb, 1048575, - 0x1edd, 1048575, - 0x1edf, 1048575, - 0x1ee1, 1048575, - 0x1ee3, 1048575, - 0x1ee5, 1048575, - 0x1ee7, 1048575, - 0x1ee9, 1048575, - 0x1eeb, 1048575, - 0x1eed, 1048575, - 0x1eef, 1048575, - 0x1ef1, 1048575, - 0x1ef3, 1048575, - 0x1ef5, 1048575, - 0x1ef7, 1048575, - 0x1ef9, 1048575, - 0x1efb, 1048575, - 0x1efd, 1048575, - 0x1eff, 1048575, - 0x1f51, 1048584, - 0x1f53, 1048584, - 0x1f55, 1048584, - 0x1f57, 1048584, - 0x1fb3, 1048585, - 0x1fbe, 1041371, - 0x1fc3, 1048585, - 0x1fe5, 1048583, - 0x1ff3, 1048585, - 0x214e, 1048548, - 0x2184, 1048575, - 0x2c61, 1048575, - 0x2c65, 1037781, - 0x2c66, 1037784, - 0x2c68, 1048575, - 0x2c6a, 1048575, - 0x2c6c, 1048575, - 0x2c73, 1048575, - 0x2c76, 1048575, - 0x2c81, 1048575, - 0x2c83, 1048575, - 0x2c85, 1048575, - 0x2c87, 1048575, - 0x2c89, 1048575, - 0x2c8b, 1048575, - 0x2c8d, 1048575, - 0x2c8f, 1048575, - 0x2c91, 1048575, - 0x2c93, 1048575, - 0x2c95, 1048575, - 0x2c97, 1048575, - 0x2c99, 1048575, - 0x2c9b, 1048575, - 0x2c9d, 1048575, - 0x2c9f, 1048575, - 0x2ca1, 1048575, - 0x2ca3, 1048575, - 0x2ca5, 1048575, - 0x2ca7, 1048575, - 0x2ca9, 1048575, - 0x2cab, 1048575, - 0x2cad, 1048575, - 0x2caf, 1048575, - 0x2cb1, 1048575, - 0x2cb3, 1048575, - 0x2cb5, 1048575, - 0x2cb7, 1048575, - 0x2cb9, 1048575, - 0x2cbb, 1048575, - 0x2cbd, 1048575, - 0x2cbf, 1048575, - 0x2cc1, 1048575, - 0x2cc3, 1048575, - 0x2cc5, 1048575, - 0x2cc7, 1048575, - 0x2cc9, 1048575, - 0x2ccb, 1048575, - 0x2ccd, 1048575, - 0x2ccf, 1048575, - 0x2cd1, 1048575, - 0x2cd3, 1048575, - 0x2cd5, 1048575, - 0x2cd7, 1048575, - 0x2cd9, 1048575, - 0x2cdb, 1048575, - 0x2cdd, 1048575, - 0x2cdf, 1048575, - 0x2ce1, 1048575, - 0x2ce3, 1048575, - 0x2cec, 1048575, - 0x2cee, 1048575, - 0x2cf3, 1048575, - 0x2d27, 1041312, - 0x2d2d, 1041312, - 0xa641, 1048575, - 0xa643, 1048575, - 0xa645, 1048575, - 0xa647, 1048575, - 0xa649, 1048575, - 0xa64b, 1048575, - 0xa64d, 1048575, - 0xa64f, 1048575, - 0xa651, 1048575, - 0xa653, 1048575, - 0xa655, 1048575, - 0xa657, 1048575, - 0xa659, 1048575, - 0xa65b, 1048575, - 0xa65d, 1048575, - 0xa65f, 1048575, - 0xa661, 1048575, - 0xa663, 1048575, - 0xa665, 1048575, - 0xa667, 1048575, - 0xa669, 1048575, - 0xa66b, 1048575, - 0xa66d, 1048575, - 0xa681, 1048575, - 0xa683, 1048575, - 0xa685, 1048575, - 0xa687, 1048575, - 0xa689, 1048575, - 0xa68b, 1048575, - 0xa68d, 1048575, - 0xa68f, 1048575, - 0xa691, 1048575, - 0xa693, 1048575, - 0xa695, 1048575, - 0xa697, 1048575, - 0xa723, 1048575, - 0xa725, 1048575, - 0xa727, 1048575, - 0xa729, 1048575, - 0xa72b, 1048575, - 0xa72d, 1048575, - 0xa72f, 1048575, - 0xa733, 1048575, - 0xa735, 1048575, - 0xa737, 1048575, - 0xa739, 1048575, - 0xa73b, 1048575, - 0xa73d, 1048575, - 0xa73f, 1048575, - 0xa741, 1048575, - 0xa743, 1048575, - 0xa745, 1048575, - 0xa747, 1048575, - 0xa749, 1048575, - 0xa74b, 1048575, - 0xa74d, 1048575, - 0xa74f, 1048575, - 0xa751, 1048575, - 0xa753, 1048575, - 0xa755, 1048575, - 0xa757, 1048575, - 0xa759, 1048575, - 0xa75b, 1048575, - 0xa75d, 1048575, - 0xa75f, 1048575, - 0xa761, 1048575, - 0xa763, 1048575, - 0xa765, 1048575, - 0xa767, 1048575, - 0xa769, 1048575, - 0xa76b, 1048575, - 0xa76d, 1048575, - 0xa76f, 1048575, - 0xa77a, 1048575, - 0xa77c, 1048575, - 0xa77f, 1048575, - 0xa781, 1048575, - 0xa783, 1048575, - 0xa785, 1048575, - 0xa787, 1048575, - 0xa78c, 1048575, - 0xa791, 1048575, - 0xa793, 1048575, - 0xa7a1, 1048575, - 0xa7a3, 1048575, - 0xa7a5, 1048575, - 0xa7a7, 1048575, - 0xa7a9, 1048575, -}; - -/* }}} */ - -} - -void Utf8::encode(uint32_t c, char res[5]) -{ - switch (nbytesPoint(c)) { - case 1: - res[0] = c; - res[1] = '\0'; - break; - case 2: - res[0] = 0xC0 | ((c >> 6) & 0x1F); - res[1] = 0x80 | (c & 0x3F); - res[2] = '\0'; - break; - case 3: - res[0] = 0xE0 | ((c >> 12) & 0xF ); - res[1] = 0x80 | ((c >> 6) & 0x3F); - res[2] = 0x80 | (c & 0x3F); - res[3] = '\0'; - break; - case 4: - res[0] = 0xF0 | ((c >> 18) & 0x7 ); - res[1] = 0x80 | ((c >> 12) & 0x3F); - res[2] = 0x80 | ((c >> 6) & 0x3F); - res[3] = 0x80 | (c & 0x3F); - res[4] = '\0'; - break; - default: - break; - } -} - -void Utf8::decode(uint32_t &c, const char *res) -{ - switch (nbytesUtf8(res[0])) { - case 1: - c = res[0]; - break; - case 2: - c = res[0] & 0x1F; - c = (c << 6) | (res[1] & 0x3F); - break; - case 3: - c = res[0] & 0x1F; - c = (c << 6) | (res[1] & 0x3F); - c = (c << 6) | (res[2] & 0x3F); - break; - case 4: - c = res[0] & 0x1F; - c = (c << 6) | (res[1] & 0x3F); - c = (c << 6) | (res[2] & 0x3F); - c = (c << 6) | (res[3] & 0x3F); - break; - default: - break; - } -} - -int8_t Utf8::nbytesUtf8(uint8_t c) -{ - if (c <= 0x7F) - return 1; - if ((c & 0xE0) == 0xC0) - return 2; - if ((c & 0xF0) == 0xE0) - return 3; - if ((c & 0xF8) == 0xF0) - return 4; - - return -1; -} - -int8_t Utf8::nbytesPoint(uint32_t c) -{ - if (c <= 0x7F) - return 1; - if (c <= 0x7FF) - return 2; - if (c <= 0xFFFF) - return 3; - if (c <= 0x1FFFFF) - return 4; - - return -1; -} - -size_t Utf8::length(const std::string &str) -{ - size_t total = 0; - - for (size_t i = 0; i < str.size(); ) { - auto size = nbytesUtf8(str[i]); - - if (size < 0) - throw std::invalid_argument("invalid sequence"); - - total ++; - i += size; - } - - return total; -} - -std::string Utf8::toutf8(const std::u32string &array) -{ - std::string res; - - for (size_t i = 0; i < array.size(); ++i) { - char tmp[5]; - auto size = nbytesPoint(array[i]); - - if (size < 0) - throw std::invalid_argument("invalid sequence"); - - encode(array[i], tmp); - res.insert(res.length(), tmp); - } - - return res; -} - -std::u32string Utf8::toutf32(const std::string &str) -{ - std::u32string res; - - for (size_t i = 0; i < str.size(); ) { - uint32_t point; - auto size = nbytesUtf8(str[i]); - - if (size < 0) - throw std::invalid_argument("invalid sequence"); - - decode(point, str.data() + i); - res.push_back(point); - i += size; - } - - return res; -} - -bool Utf8::isspace(uint32_t c) -{ - uint32_t *p; - - p = rbsearch(c, isspacer, LEN(isspacer) / 2, 2); - if (p && c >= p[0] && c <= p[1]) - return true; - - return false; -} - -bool Utf8::isdigit(uint32_t c) -{ - uint32_t *p; - - p = rbsearch(c, isdigitr, LEN(isdigitr) / 2, 2); - if (p && c >= p[0] && c <= p[1]) - return true; - - return false; -} - -bool Utf8::isletter(uint32_t c) -{ - uint32_t *p; - - p = rbsearch(c, isalphar, LEN(isalphar) / 2, 2); - if (p && c >= p[0] && c <= p[1]) - return true; - - p = rbsearch(c, isalphas, LEN(isalphas), 1); - if (p && c == p[0]) - return true; - - return false; -} - -bool Utf8::isupper(uint32_t c) -{ - uint32_t *p; - - p = rbsearch(c, isupperr, LEN(isupperr) / 2, 2); - if (p && c >= p[0] && c <= p[1]) - return true; - - p = rbsearch(c, isuppers, LEN(isuppers), 1); - if (p && c == p[0]) - return true; - - return false; -} - -bool Utf8::islower(uint32_t c) -{ - uint32_t *p; - - p = rbsearch(c, islowerr, LEN(islowerr) / 2, 2); - if (p && c >= p[0] && c <= p[1]) - return true; - - p = rbsearch(c, islowers, LEN(islowers), 1); - if (p && c == p[0]) - return true; - - return false; -} - -bool Utf8::istitle(uint32_t c) -{ - uint32_t *p; - - p = rbsearch(c, istitler, LEN(istitler) / 2, 2); - if (p && c >= p[0] && c <= p[1]) - return true; - - p = rbsearch(c, istitles, LEN(istitles), 1); - if(p && c == p[0]) - return true; - - return false; -} - -uint32_t Utf8::toupper(uint32_t c) -{ - uint32_t *p; - - p = rbsearch(c, toupperr, LEN(toupperr) / 3, 3); - if (p && c >= p[0] && c <= p[1]) - return c + p[2] - 1048576; - - p = rbsearch(c, touppers, LEN(touppers) / 2, 2); - if (p && c == p[0]) - return c + p[1] - 1048576; - - return c; -} - -uint32_t Utf8::tolower(uint32_t c) -{ - uint32_t *p; - - p = rbsearch(c, tolowerr, LEN(tolowerr) / 3, 3); - if (p && c >= p[0] && c <= p[1]) - return c + p[2] - 1048576; - - p = rbsearch(c, tolowers, LEN(tolowers) / 2, 2); - if (p && c == p[0]) - return c + p[1] - 1048576; - - return c; -} - -uint32_t Utf8::totitle(uint32_t c) -{ - uint32_t *p; - - p = rbsearch(c, totitler, LEN(totitler) / 3, 3); - if (p && c >= p[0] && c <= p[1]) - return c + p[2] - 1048576; - - p = rbsearch(c, totitles, LEN(totitles) / 2, 2); - if (p && c == p[0]) - return c + p[1] - 1048576; - - return c; -}
--- a/C++/Utf8.h Sun Mar 08 11:07:36 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,212 +0,0 @@ -/* - * Utf8.h -- UTF-8 to UTF-32 conversions - * - * Copyright (c) 2013, 2014 David Demelier <markand@malikania.fr> - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#ifndef _UTF8_H_ -#define _UTF8_H_ - -/** - * @file Utf8.h - * @brief UTF-8 to UTF-32 conversions - */ - -#include <cstdint> -#include <stdexcept> -#include <string> - -/** - * @class Utf8 - * @brief Conversion between UTF-8 and UTF-32 - */ -class Utf8 { -private: - static void encode(uint32_t point, char res[5]); - static void decode(uint32_t &c, const char *res); - -public: - /** - * Get the number of bytes for the first multi byte character from a - * utf-8 string. - * - * @param c the first multi byte character - * @return the number of bytes [1-4] or -1 on invalid - */ - static int8_t nbytesUtf8(uint8_t c); - - /** - * Get the number of bytes for the unicode point. - * - * @param point the unicode point - * @return the number of bytes [1-4] or -1 on invalid - */ - static int8_t nbytesPoint(uint32_t point); - - /** - * Get real number of character in a string. - * - * @param str the string - * @return the length - * @throw std::invalid_argument on invalid sequence - */ - static size_t length(const std::string &str); - - /** - * Convert a UTF-32 string to UTF-8 string. - * - * @param array the UTF-32 string - * @return the UTF-8 string - * @throw std::invalid_argument on invalid sequence - */ - static std::string toutf8(const std::u32string &array); - - /** - * Convert a UTF-8 string to UTF-32 string. - * - * @param str the UTF-8 string - * @return the UTF-32 string - * @throw std::invalid_argument on invalid sequence - */ - static std::u32string toutf32(const std::string &str); - - /** - * Check if the unicode character is space. - * - * @param c the character - * @return true if space - */ - static bool isspace(uint32_t c); - - /** - * Check if the unicode character is digit. - * - * @param c the character - * @return true if digit - */ - static bool isdigit(uint32_t c); - - /** - * Check if the unicode character is letter. - * - * @param c the character - * @return true if letter - */ - static bool isletter(uint32_t c); - - /** - * Check if the unicode character is upper case. - * - * @param c the character - * @return true if upper case - */ - static bool isupper(uint32_t c); - - /** - * Check if the unicode character is lower case. - * - * @param c the character - * @return true if lower case - */ - static bool islower(uint32_t c); - - /** - * Check if the unicode character is title case. - * - * @param c the character - * @return true if title case - */ - static bool istitle(uint32_t c); - - /** - * Convert to upper case. - * - * @param c the character - * @return the upper case character - */ - static uint32_t toupper(uint32_t c); - - /** - * Convert to lower case. - * - * @param c the character - * @return the lower case character - */ - static uint32_t tolower(uint32_t c); - - /** - * Convert to title case. - * - * @param c the character - * @return the title case character - */ - static uint32_t totitle(uint32_t c); - - /** - * Convert the UTF-8 string to upper case. - * - * @param str the str - * @return the upper case string - */ - static inline std::string toupper(const std::string &str) - { - return toutf8(toupper(toutf32(str))); - } - - /** - * Convert the UTF-32 string to upper case. - * - * @param str the str - * @return the upper case string - */ - static inline std::u32string toupper(const std::u32string &str) - { - auto copy = str; - - for (size_t i = 0; i < str.size(); ++i) - copy[i] = toupper(str[i]); - - return copy; - } - - /** - * Convert the UTF-8 string to lower case. - * - * @param str the str - * @return the lower case string - */ - static inline std::string tolower(const std::string &str) - { - return toutf8(tolower(toutf32(str))); - } - - /** - * Convert the UTF-32 string to lower case. - * - * @param str the str - * @return the lower case string - */ - static inline std::u32string tolower(const std::u32string &str) - { - auto copy = str; - - for (size_t i = 0; i < str.size(); ++i) - copy[i] = tolower(str[i]); - - return copy; - } -}; - -#endif // !_UTF8_H_
--- a/C++/Xdg.cpp Sun Mar 08 11:07:36 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,125 +0,0 @@ -/* - * Xdg.cpp -- XDG directory specifications - * - * Copyright (c) 2013, 2014 David Demelier <markand@malikania.fr> - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include <cstdlib> -#include <stdexcept> -#include <sstream> - -#include "Xdg.h" - -namespace { - -bool isabsolute(const std::string &path) -{ - return path.length() > 0 && path[0] == '/'; -} - -std::vector<std::string> split(const std::string &arg) -{ - std::stringstream iss(arg); - std::string item; - std::vector<std::string> elems; - - while (std::getline(iss, item, ':')) - if (isabsolute(item)) - elems.push_back(item); - - return elems; -} - -std::string envOrHome(const std::string &var, const std::string &repl) -{ - auto value = getenv(var.c_str()); - - if (value == nullptr || !isabsolute(value)) { - auto home = getenv("HOME"); - - if (home == nullptr) - throw std::runtime_error("could not get home directory"); - - return std::string(home) + "/" + repl; - } - - return value; -} - -std::vector<std::string> listOrDefaults(const std::string &var, const std::vector<std::string> &list) -{ - auto value = getenv(var.c_str()); - - if (!value) - return list; - - // No valid item at all? Use defaults - auto result = split(value); - - return (result.size() == 0) ? list : result; -} - -} // !namespace - -Xdg::Xdg() -{ - m_configHome = envOrHome("XDG_CONFIG_HOME", ".config"); - m_dataHome = envOrHome("XDG_DATA_HOME", ".local/share"); - m_cacheHome = envOrHome("XDG_CACHE_HOME", ".cache"); - - m_configDirs = listOrDefaults("XDG_CONFIG_DIRS", { "/etc/xdg" }); - m_dataDirs = listOrDefaults("XDG_DATA_DIRS", { "/usr/local/share", "/usr/share" }); - - /* - * Runtime directory is a special case and does not have a replacement, the - * application should manage this by itself. - */ - auto runtime = getenv("XDG_RUNTIME_DIR"); - if (runtime && isabsolute(runtime)) - m_runtimeDir = runtime; -} - -const std::string &Xdg::configHome() const noexcept -{ - return m_configHome; -} - -const std::string &Xdg::dataHome() const noexcept -{ - return m_dataHome; -} - -const std::string &Xdg::cacheHome() const noexcept -{ - return m_cacheHome; -} - -const std::string &Xdg::runtimeDir() const -{ - if (m_runtimeDir.size() == 0) - throw std::runtime_error("XDG_RUNTIME_DIR is not set"); - - return m_runtimeDir; -} - -const Xdg::List &Xdg::configDirs() const noexcept -{ - return m_configDirs; -} - -const Xdg::List &Xdg::dataDirs() const noexcept -{ - return m_dataDirs; -}
--- a/C++/Xdg.h Sun Mar 08 11:07:36 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,110 +0,0 @@ -/* - * Xdg.h -- XDG directory specifications - * - * Copyright (c) 2013, 2014 David Demelier <markand@malikania.fr> - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#ifndef _XDG_H_ -#define _XDG_H_ - -#include <vector> -#include <string> - -#if defined(_WIN32) -# if defined(BUILDING_DLL) -# define EXPORT __declspec(dllexport) -# else -# define EXPORT __declspec(dllimport) -# endif -#else -# define EXPORT -#endif - -/** - * @class Xdg - * @brief XDG specifications - * - * Read and get XDG directories. This file contains exports thingies so it can - * compiles successfully on Windows but its usage is discouraged. - */ -class EXPORT Xdg { -public: - using List = std::vector<std::string>; - -private: - std::string m_configHome; - std::string m_dataHome; - std::string m_cacheHome; - std::string m_runtimeDir; - List m_configDirs; - List m_dataDirs; - -public: - /** - * Open an xdg instance and load directories. - * - * @throw std::runtime_error on failures - */ - Xdg(); - - /** - * Get the config directory. ${XDG_CONFIG_HOME} or ${HOME}/.config - * - * @return the config directory - */ - const std::string &configHome() const noexcept; - - /** - * Get the data directory. ${XDG_DATA_HOME} or ${HOME}/.local/share - * - * @return the data directory - */ - const std::string &dataHome() const noexcept; - - /** - * Get the cache directory. ${XDG_CACHE_HOME} or ${HOME}/.cache - * - * @return the cache directory - */ - const std::string &cacheHome() const noexcept; - - /** - * Get the runtime directory. ${XDG_RUNTIME_DIR} must be set, - * if not, it throws an exception. - * - * The XDG standard says that application should handle XDG_RUNTIME_DIR by - * themselves. - * - * @return the runtime directory - * @throw std::runtime_error on error - */ - const std::string &runtimeDir() const; - - /** - * Get the standard config directories. ${XDG_CONFIG_DIRS} or { "/etc/xdg" } - * - * @return the list of config directories - */ - const List &configDirs() const noexcept; - - /** - * Get the data directories. ${XDG_DATA_DIRS} or { "/usr/local/share", "/usr/share" } - * - * @return the list of data directories - */ - const List &dataDirs() const noexcept; -}; - -#endif // !_XDG_H_
--- a/C++/ZipArchive.cpp Sun Mar 08 11:07:36 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,295 +0,0 @@ -/* - * ZipArchive.cpp -- wrapper around libzip - * - * Copyright (c) 2013, 2014 David Demelier <markand@malikania.fr> - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include <cerrno> -#include <cstdlib> -#include <cstring> -#include <stdexcept> - -#include "ZipArchive.h" - -namespace source { - -/* -------------------------------------------------------- - * Buffer (zip_source_buffer) - * -------------------------------------------------------- */ - -Buffer::Buffer(std::string data) - : m_data(std::move(data)) -{ -} - -struct zip_source *Buffer::source(struct zip *archive) const -{ - auto size = m_data.size(); - auto data = static_cast<char *>(std::malloc(size)); - - if (data == nullptr) - throw std::runtime_error(std::strerror(errno)); - - std::memcpy(data, m_data.data(), size); - - auto src = zip_source_buffer(archive, data, size, 1); - - if (src == nullptr) { - std::free(data); - throw std::runtime_error(zip_strerror(archive)); - } - - return src; -} - -/* -------------------------------------------------------- - * File (zip_source_file) - * -------------------------------------------------------- */ - -File::File(std::string path, ZipUint64 start, ZipInt64 length) - : m_path(std::move(path)) - , m_start(start) - , m_length(length) -{ -} - -struct zip_source *File::source(struct zip *archive) const -{ - auto src = zip_source_file(archive, m_path.c_str(), m_start, m_length); - - if (src == nullptr) - throw std::runtime_error(zip_strerror(archive)); - - return src; -} - -} // !source - -/* -------------------------------------------------------- - * ZipArchive - * ------------------------------------------------------- */ - -ZipArchive::ZipArchive(const std::string &path, ZipFlags flags) - : m_handle(nullptr, nullptr) -{ - int error; - struct zip *archive = zip_open(path.c_str(), flags, &error); - - if (archive == nullptr) - { - char buf[128]{}; - - zip_error_to_str(buf, sizeof (buf), error, errno); - - throw std::runtime_error(buf); - } - - m_handle = { archive, zip_close }; -} - -void ZipArchive::setFileComment(ZipUint64 index, const std::string &text, ZipFlags flags) -{ - auto size = text.size(); - auto cstr = (size == 0) ? nullptr : text.c_str(); - - if (zip_file_set_comment(m_handle.get(), index, cstr, size, flags) < 0) - throw std::runtime_error(zip_strerror(m_handle.get())); -} - -std::string ZipArchive::getFileComment(ZipUint64 index, ZipFlags flags) const -{ - zip_uint32_t length{}; - auto text = zip_file_get_comment(m_handle.get(), index, &length, flags); - - if (text == nullptr) - throw std::runtime_error(zip_strerror(m_handle.get())); - - return { text, length }; -} - -void ZipArchive::setComment(const std::string &comment) -{ - if (zip_set_archive_comment(m_handle.get(), comment.c_str(), comment.size()) < 0) - throw std::runtime_error(zip_strerror(m_handle.get())); -} - -std::string ZipArchive::getComment(ZipFlags flags) const -{ - int length{}; - auto text = zip_get_archive_comment(m_handle.get(), &length, flags); - - if (text == nullptr) - throw std::runtime_error(zip_strerror(m_handle.get())); - - return { text, static_cast<size_t>(length) }; -} - -ZipInt64 ZipArchive::find(const std::string &name, ZipFlags flags) -{ - auto index = zip_name_locate(m_handle.get(), name.c_str(), flags); - - if (index < 0) - throw std::runtime_error(zip_strerror(m_handle.get())); - - return index; -} - -ZipStat ZipArchive::stat(const std::string &name, ZipFlags flags) const -{ - ZipStat st; - - if (zip_stat(m_handle.get(), name.c_str(), flags, &st) < 0) - throw std::runtime_error(zip_strerror(m_handle.get())); - - return st; -} - -ZipStat ZipArchive::stat(ZipUint64 index, ZipFlags flags) const -{ - ZipStat st; - - if (zip_stat_index(m_handle.get(), index, flags, &st) < 0) - throw std::runtime_error(zip_strerror(m_handle.get())); - - return st; -} - -ZipInt64 ZipArchive::add(const ZipSource &source, const std::string &name, ZipFlags flags) -{ - auto src = source.source(m_handle.get()); - auto ret = zip_file_add(m_handle.get(), name.c_str(), src, flags); - - if (ret < 0) { - zip_source_free(src); - throw std::runtime_error(zip_strerror(m_handle.get())); - } - - return ret; -} - -ZipInt64 ZipArchive::addDirectory(const std::string &directory, ZipFlags flags) -{ - auto ret = zip_dir_add(m_handle.get(), directory.c_str(), flags); - - if (ret < 0) - throw std::runtime_error(zip_strerror(m_handle.get())); - - return ret; -} - -void ZipArchive::replace(const ZipSource &source, ZipUint64 index, ZipFlags flags) -{ - auto src = source.source(m_handle.get()); - - if (zip_file_replace(m_handle.get(), index, src, flags) < 0) { - zip_source_free(src); - throw std::runtime_error(zip_strerror(m_handle.get())); - } -} - -ZipFile ZipArchive::open(const std::string &name, ZipFlags flags, const std::string &password) -{ - struct zip_file *file; - - if (password.size() > 0) - file = zip_fopen_encrypted(m_handle.get(), name.c_str(), flags, password.c_str()); - else - file = zip_fopen(m_handle.get(), name.c_str(), flags); - - if (file == nullptr) - throw std::runtime_error(zip_strerror(m_handle.get())); - - return file; -} - -ZipFile ZipArchive::open(ZipUint64 index, ZipFlags flags, const std::string &password) -{ - struct zip_file *file; - - if (password.size() > 0) - file = zip_fopen_index_encrypted(m_handle.get(), index, flags, password.c_str()); - else - file = zip_fopen_index(m_handle.get(), index, flags); - - if (file == nullptr) - throw std::runtime_error(zip_strerror(m_handle.get())); - - return file; -} - -void ZipArchive::rename(ZipUint64 index, const std::string &name, ZipFlags flags) -{ - if (zip_file_rename(m_handle.get(), index, name.c_str(), flags) < 0) - throw std::runtime_error(zip_strerror(m_handle.get())); -} - -void ZipArchive::setFileCompression(ZipUint64 index, ZipInt32 comp, ZipUint32 flags) -{ - if (zip_set_file_compression(m_handle.get(), index, comp, flags) < 0) - throw std::runtime_error(zip_strerror(m_handle.get())); -} - -void ZipArchive::remove(ZipUint64 index) -{ - if (zip_delete(m_handle.get(), index) < 0) - throw std::runtime_error(zip_strerror(m_handle.get())); -} - -ZipInt64 ZipArchive::numEntries(ZipFlags flags) const noexcept -{ - return zip_get_num_entries(m_handle.get(), flags); -} - -void ZipArchive::unchange(ZipUint64 index) -{ - if (zip_unchange(m_handle.get(), index) < 0) - throw std::runtime_error(zip_strerror(m_handle.get())); -} - -void ZipArchive::unchangeAll() -{ - if (zip_unchange_all(m_handle.get()) < 0) - throw std::runtime_error(zip_strerror(m_handle.get())); -} - -void ZipArchive::unchangeArchive() -{ - if (zip_unchange_archive(m_handle.get()) < 0) - throw std::runtime_error(zip_strerror(m_handle.get())); -} - -void ZipArchive::setDefaultPassword(const std::string &password) -{ - auto cstr = (password.size() > 0) ? password.c_str() : nullptr; - - if (zip_set_default_password(m_handle.get(), cstr) < 0) - throw std::runtime_error(zip_strerror(m_handle.get())); -} - -void ZipArchive::setFlag(ZipFlags flags, int value) -{ - if (zip_set_archive_flag(m_handle.get(), flags, value) < 0) - throw std::runtime_error(zip_strerror(m_handle.get())); -} - -int ZipArchive::getFlag(ZipFlags which, ZipFlags flags) const -{ - auto ret = zip_get_archive_flag(m_handle.get(), which, flags); - - if (ret < 0) - throw std::runtime_error(zip_strerror(m_handle.get())); - - return ret; -}
--- a/C++/ZipArchive.h Sun Mar 08 11:07:36 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,727 +0,0 @@ -/* - * ZipArchive.h -- wrapper around libzip - * - * Copyright (c) 2013, 2014 David Demelier <markand@malikania.fr> - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#ifndef _ZIP_ARCHIVE_H_ -#define _ZIP_ARCHIVE_H_ - -#include <iterator> -#include <memory> -#include <string> - -#include <zip.h> - -using ZipStat = struct zip_stat; -using ZipSourceCommand = enum zip_source_cmd; -using ZipCallback = zip_source_callback; - -using ZipFlags = zip_flags_t; -using ZipInt8 = zip_int8_t; -using ZipUint8 = zip_uint8_t; -using ZipInt16 = zip_int16_t; -using ZipUint16 = zip_uint16_t; -using ZipInt32 = zip_int32_t; -using ZipUint32 = zip_uint32_t; -using ZipInt64 = zip_int64_t; -using ZipUint64 = zip_uint64_t; - -/** - * @class ZipSource - * @brief Source for adding file - */ -class ZipSource { -public: - /** - * Default constructor. - */ - ZipSource() = default; - - /** - * Virtual destructor. - */ - virtual ~ZipSource() = default; - - /** - * Create a zip_source structure. Must not be null, throw an exception - * instead. - * - * @return a zip_source ready to be used - * @throw std::runtime_error on errors - * @post must not return null - */ - virtual struct zip_source *source(struct zip *zip) const = 0; -}; - -/** - * @class ZipFile - * @brief File for reading - */ -class ZipFile { -private: - std::unique_ptr<struct zip_file, int (*)(struct zip_file *)> m_handle; - - ZipFile(const ZipFile &) = delete; - ZipFile &operator=(const ZipFile &) = delete; - -public: - /** - * Create a ZipFile with a zip_file structure. - * - * @param file the file ready to be used - */ - inline ZipFile(struct zip_file *file) - : m_handle(file, zip_fclose) - { - } - - /** - * Move constructor defaulted. - * - * @param other the other ZipFile - */ - ZipFile(ZipFile &&other) noexcept = default; - - /** - * Move operator defaulted. - * - * @param other the other ZipFile - * @return *this - */ - ZipFile &operator=(ZipFile &&) noexcept = default; - - /** - * Read some data. - * - * @param data the destination buffer - * @param length the length - * @return the number of bytes written or -1 on failure - */ - inline int read(void *data, ZipUint64 length) noexcept - { - return zip_fread(m_handle.get(), data, length); - } - - /** - * Read some data to a fixed size array. - * - * @param data the array - * @return the number of bytes written or -1 on failure - */ - template <size_t Size> - inline int read(char (&data)[Size]) noexcept - { - return read(data, Size); - } - - /** - * Optimized function for reading all characters with only one allocation. - * Ideal for combining with ZipArchive::stat. - * - * @param length the length of the file - * @return the whole string - * @see ZipArchive::stat - */ - std::string read(unsigned length) - { - std::string result; - - result.resize(length); - auto count = read(&result[0], length); - - if (count < 0) - return ""; - - result.resize(count); - - return result; - } -}; - -namespace source { - -/** - * @class Buffer - * @brief Create a source from a buffer - */ -class Buffer : public ZipSource { -private: - std::string m_data; - -public: - /** - * Buffer constructor. Moves the data. - * - * @param data the data - */ - Buffer(std::string data); - - /** - * @copydoc ZipSource::source - */ - struct zip_source *source(struct zip *archive) const override; -}; - -/** - * @class File - * @brief Create a source from a file on the disk - */ -class File : public ZipSource { -private: - std::string m_path; - ZipUint64 m_start; - ZipInt64 m_length; - -public: - /** - * File constructor. - * - * @param path the path to the file - * @param start the beginning in the file - * @param length the maximum length - */ - File(std::string path, ZipUint64 start = 0, ZipInt64 length = -1); - - /** - * @copydoc ZipSource::source - */ - struct zip_source *source(struct zip *archive) const override; -}; - -} // !source - -/** - * @class ZipStatPtr - * @brief Wrapper for ZipStat as pointer - */ -class ZipStatPtr { -private: - ZipStat &m_stat; - -public: - /** - * Constructor. - * - * @param stat the file stat - */ - inline ZipStatPtr(ZipStat stat) noexcept - : m_stat(stat) - { - } - - /** - * Get the reference. - * - * @return the reference - */ - inline ZipStat &operator*() const noexcept - { - return m_stat; - } - - /** - * Access the object. - * - * @return the pointer - */ - inline ZipStat *operator->() const noexcept - { - return &m_stat; - } -}; - -/** - * @class ZipArchive - * @brief Safe wrapper on the struct zip structure - */ -class ZipArchive { -private: - using Handle = std::unique_ptr<struct zip, int (*)(struct zip *)>; - - Handle m_handle; - - ZipArchive(const ZipArchive &) = delete; - ZipArchive &operator=(const ZipArchive &) = delete; - -public: - using value_type = ZipStat; - using reference = ZipStat; - using const_refernce = ZipStat; - using pointer = ZipStatPtr; - using size_type = unsigned; - - /** - * @class iterator - * @brief Iterator - */ - class iterator : public std::iterator<std::random_access_iterator_tag, ZipArchive> { - private: - ZipArchive &m_archive; - ZipUint64 m_index; - - public: - explicit inline iterator(ZipArchive &archive, ZipUint64 index = 0) noexcept - : m_archive(archive) - , m_index(index) - { - } - - inline ZipStat operator*() const - { - return m_archive.stat(m_index); - } - - inline ZipStatPtr operator->() const - { - return ZipStatPtr(m_archive.stat(m_index)); - } - - inline iterator &operator++() noexcept - { - ++ m_index; - - return *this; - } - - inline iterator operator++(int) noexcept - { - iterator save = *this; - - ++ m_index; - - return save; - } - - inline iterator &operator--() noexcept - { - -- m_index; - - return *this; - } - - inline iterator operator--(int) noexcept - { - iterator save = *this; - - -- m_index; - - return save; - } - - inline iterator operator+(int inc) const noexcept - { - return iterator(m_archive, m_index + inc); - } - - inline iterator operator-(int dec) const noexcept - { - return iterator(m_archive, m_index - dec); - } - - inline bool operator==(const iterator &other) const noexcept - { - return m_index == other.m_index; - } - - inline bool operator!=(const iterator &other) const noexcept - { - return m_index != other.m_index; - } - - inline ZipStat operator[](int index) const - { - return m_archive.stat(index); - } - }; - - /** - * @class const_iterator - * @brief Const iterator - */ - class const_iterator : public std::iterator<std::random_access_iterator_tag, ZipArchive> { - private: - const ZipArchive &m_archive; - ZipUint64 m_index; - - public: - explicit inline const_iterator(const ZipArchive &archive, ZipUint64 index = 0) noexcept - : m_archive(archive) - , m_index(index) - { - } - - inline ZipStat operator*() const - { - return m_archive.stat(m_index); - } - - inline ZipStatPtr operator->() const - { - return ZipStatPtr(m_archive.stat(m_index)); - } - - inline const_iterator &operator++() noexcept - { - ++ m_index; - - return *this; - } - - inline const_iterator operator++(int) noexcept - { - const_iterator save = *this; - - ++ m_index; - - return save; - } - - inline const_iterator &operator--() noexcept - { - -- m_index; - - return *this; - } - - inline const_iterator operator--(int) noexcept - { - const_iterator save = *this; - - -- m_index; - - return save; - } - - inline const_iterator operator+(int inc) const noexcept - { - return const_iterator(m_archive, m_index + inc); - } - - inline const_iterator operator-(int dec) const noexcept - { - return const_iterator(m_archive, m_index - dec); - } - - inline bool operator==(const const_iterator &other) const noexcept - { - return m_index == other.m_index; - } - - inline bool operator!=(const const_iterator &other) const noexcept - { - return m_index != other.m_index; - } - - inline ZipStat operator[](int index) const - { - return m_archive.stat(index); - } - }; - -public: - /** - * Open an archive on the disk. - * - * @param path the path - * @param flags the optional flags - * @throw std::runtime_error on errors - */ - ZipArchive(const std::string &path, ZipFlags flags = 0); - - /** - * Move constructor defaulted. - * - * @param other the other ZipArchive - */ - ZipArchive(ZipArchive &&other) noexcept = default; - - /** - * Move operator defaulted. - * - * @param other the other ZipArchive - * @return *this - */ - ZipArchive &operator=(ZipArchive &&other) noexcept = default; - - /** - * Get an iterator to the beginning. - * - * @return the iterator - */ - inline iterator begin() noexcept - { - return iterator(*this); - } - - /** - * Overloaded function. - * - * @return the iterator - */ - inline const_iterator begin() const noexcept - { - return const_iterator(*this); - } - - /** - * Overloaded function. - * - * @return the iterator - */ - inline const_iterator cbegin() const noexcept - { - return const_iterator(*this); - } - - /** - * Get an iterator to the end. - * - * @return the iterator - */ - inline iterator end() noexcept - { - return iterator(*this, numEntries()); - } - - /** - * Overloaded function. - * - * @return the iterator - */ - inline const_iterator end() const noexcept - { - return const_iterator(*this, numEntries()); - } - - /** - * Overloaded function. - * - * @return the iterator - */ - inline const_iterator cend() const noexcept - { - return const_iterator(*this, numEntries()); - } - - /** - * Set a comment on a file. - * - * @param index the file index in the archive - * @param text the text or empty to remove the comment - * @param flags the optional flags - * @throw std::runtime_error on errors - */ - void setFileComment(ZipUint64 index, const std::string &text = "", ZipFlags flags = 0); - - /** - * Get a comment from a file. - * - * @param index the file index in the archive - * @param flags the optional flags - * @return the comment - * @throw std::runtime_error on errors - */ - std::string getFileComment(ZipUint64 index, ZipFlags flags = 0) const; - - /** - * Set the archive comment. - * - * @param comment the comment - * @throw std::runtime_error on errors - */ - void setComment(const std::string &comment); - - /** - * Get the archive comment. - * - * @param flags the optional flags - * @return the comment - * @throw std::runtime_error on errors - */ - std::string getComment(ZipFlags flags = 0) const; - - /** - * Locate a file on the archive. - * - * @param name the name - * @param flags the optional flags - * @return the index - * @throw std::runtime_error on errors - */ - ZipInt64 find(const std::string &name, ZipFlags flags = 0); - - /** - * Get information about a file. - * - * @param name the name - * @param flags the optional flags - * @return the structure - * @throw std::runtime_error on errors - */ - ZipStat stat(const std::string &name, ZipFlags flags = 0) const; - - /** - * Get information about a file. Overloaded function. - * - * @param index the file index in the archive - * @param flags the optional flags - * @return the structure - * @throw std::runtime_error on errors - */ - ZipStat stat(ZipUint64 index, ZipFlags flags = 0) const; - - /** - * Add a file to the archive. - * - * @param source the source - * @param name the name entry in the archive - * @param flags the optional flags - * @return the new index in the archive - * @throw std::runtime_error on errors - * @see source::File - * @see source::Buffer - */ - ZipInt64 add(const ZipSource &source, const std::string &name, ZipFlags flags = 0); - - /** - * Add a directory to the archive. Not a directory from the disk. - * - * @param directory the directory name - * @param flags the optional flags - * @return the new index in the archive - * @throw std::runtime_error on errors - */ - ZipInt64 addDirectory(const std::string &directory, ZipFlags flags = 0); - - /** - * Replace an existing file in the archive. - * - * @param source the source - * @param index the file index in the archiev - * @param flags the optional flags - * @throw std::runtime_error on errors - */ - void replace(const ZipSource &source, ZipUint64 index, ZipFlags flags = 0); - - /** - * Open a file in the archive. - * - * @param name the name - * @param flags the optional flags - * @param password the optional password - * @return the opened file - * @throw std::runtime_error on errors - */ - ZipFile open(const std::string &name, ZipFlags flags = 0, const std::string &password = ""); - - /** - * Open a file in the archive. Overloaded function. - * - * @param index the file index in the archive - * @param flags the optional flags - * @param password the optional password - * @return the opened file - * @throw std::runtime_error on errors - */ - ZipFile open(ZipUint64 index, ZipFlags flags = 0, const std::string &password = ""); - - /** - * Rename an existing entry in the archive. - * - * @param index the file index in the archive - * @param name the new name - * @param flags the optional flags - * @throw std::runtime_error on errors - */ - void rename(ZipUint64 index, const std::string &name, ZipFlags flags = 0); - - /** - * Set file compression. - * - * @param index the file index in the archive - * @param comp the compression - * @param flags the optional flags - * @throw std::runtime_error on errors - */ - void setFileCompression(ZipUint64 index, ZipInt32 comp, ZipUint32 flags = 0); - - /** - * Delete a file from the archive. - * - * @param index the file index in the archive - * @throw std::runtime_error on errors - */ - void remove(ZipUint64 index); - - /** - * Get the number of entries in the archive. - * - * @param flags the optional flags - * @return the number of entries - */ - ZipInt64 numEntries(ZipFlags flags = 0) const noexcept; - - /** - * Revert changes on the file. - * - * @param index the index - * @throw std::runtime_error on errors - */ - void unchange(ZipUint64 index); - - /** - * Revert all changes. - * - * @throw std::runtime_error on errors - */ - void unchangeAll(); - - /** - * Revert changes to archive. - * - * @throw std::runtime_error on errors - */ - void unchangeArchive(); - - /** - * Set the defaut password. - * - * @param password the password or empty to unset it - * @throw std::runtime_error on errors - */ - void setDefaultPassword(const std::string &password = ""); - - /** - * Set an archive flag. - * - * @param flag the flag to set - * @param value the value - * @throw std::runtime_error on errors - */ - void setFlag(ZipFlags flag, int value); - - /** - * Get an archive flag. - * - * @param which which flag - * @param flags the optional flags - * @return the value - * @throw std::runtime_error on errors - */ - int getFlag(ZipFlags which, ZipFlags flags = 0) const; -}; - -#endif // !_ZIP_ARCHIVE_H_
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/C++/doc/Dynlib/Home.md Sun Mar 08 14:26:33 2015 +0100 @@ -0,0 +1,69 @@ +# DynLib + +This class provides routines to load shared libraries in a portable manner. + +## Operating system support + +| System | Support | Remarks | +|---------|----------|-------------------------------------| +| Linux | Complete | Linker needs -ldl linker flags | +| FreeBSD | Complete | | +| Windows | Complete | Immediately policy is not available | + +## API Reference + +### Macros constants + +- [DYNLIB_EXPORT](macro/DYNLIB_EXPORT.md) + +### Classes + +- [DynLib](class/Dynlib.md) + +## Tutorial + +This is the library code to be compiled as a shared module. + +**Library.cpp** + +````cpp +#include <iostream> +#include <string> + +#include "DynLib.h" + +extern "C" { + +DYNLIB_EXPORT void a_sayHello(const std::string &name) +{ + std::cout << "Hello " << name << "!" << std::endl; +} + +} +```` + +And this is the code that load the shared module. + +**Main.cpp** + +````cpp +#include <iostream> + +#include "DynLib.h" + +using HelloFunc = void (*)(const std::string &); + +int main(void) +{ + try { + DynLib library("./liba.so"); + + HelloFunc hello = library.sym<HelloFunc>("a_sayHello"); + hello("Test"); + } catch (const std::exception &error) { + std::cerr << error.what() << std::endl; + } + + return 0; +} +````
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/C++/doc/Dynlib/class/Dynlib.md Sun Mar 08 14:26:33 2015 +0100 @@ -0,0 +1,19 @@ +# DynLib + +Dynamic loading library. + +## Class + +````cpp +class DynLib; +```` + +### Public member functions + +- [(Constructor)](Dynlib/Constructor.md) +- [(Destructor)](Dynlib/Destructor.md) +- [sym (template)](Dynlib/sym.md) + +### Public member types + +- [Policy](Dynlib/Policy.md)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/C++/doc/Dynlib/class/Dynlib/Constructor.md Sun Mar 08 14:26:33 2015 +0100 @@ -0,0 +1,44 @@ +# Constructor + +Construct an empty DynLib. + +## Function + +### SYNOPSIS + +````cpp +DynLib(); +```` + +# Constructor + +Open the library at the specified path. + +## Function + +### SYNOPSIS + +````cpp +DynLib(const std::string &path, Policy policy = Immediately); +```` + +### ARGUMENTS + +- path, the path. Must be absolute +- policy, the policy (see [Policy](Policy.md)) + +### THROWS + +- [std::runtime_error](http://en.cppreference.com/w/cpp/error/runtime_error) on failures. + +# Constructor (copy) + +The copy constructor is disabled. + +## Function + +### SYNOPSIS + +````cpp +DynLib(const DynLib &) = delete; +```` \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/C++/doc/Dynlib/class/Dynlib/Destructor.md Sun Mar 08 14:26:33 2015 +0100 @@ -0,0 +1,11 @@ +# Destructor + +The destructor close any handle and further calls to loaded symbols is undefined behaviour. + +## Function + +### SYNOPSIS + +````cpp +~DynLib(); +```` \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/C++/doc/Dynlib/class/Dynlib/Policy.md Sun Mar 08 14:26:33 2015 +0100 @@ -0,0 +1,19 @@ +# Policy + +The policy set the opening settings. Not all policies are available. + +## Enum + +### SYNOPSIS + +````cpp +enum Policy { + Immediately, + Lazy +}; +```` + +### VALUES + +- Immediately, load all symbols immediately +- Lazy, load symbols when needed \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/C++/doc/Dynlib/class/Dynlib/sym.md Sun Mar 08 14:26:33 2015 +0100 @@ -0,0 +1,33 @@ +# sym (template) + +Retrieves a symbol from the shared library. + +## Function + +### SYNOPSIS + +````cpp +template <typename T> +T sym(const std::string &name) +```` + +### ARGUMENTS + +- name, the symbol name + +### RETURNS + +The symbol cast to the template type. + +### THROWS + +- [std::runtime_error](http://en.cppreference.com/w/cpp/error/runtime_error) on errors +- [std::out_of_range](http://en.cppreference.com/w/cpp/error/out_of_range) when symbol is not found + +### NOTES + +On some systems, if you forgot to add [DYNLIB_EXPORT](../../macro/DYNLIB_EXPORT.md) attribute to the function, it won't find the symbol. + +### REMARKS + +Be sure that the DynLib object is not destroyed when you use the symbol, otherwise it is undefined behaviour.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/C++/doc/Dynlib/macro/DYNLIB_EXPORT.md Sun Mar 08 14:26:33 2015 +0100 @@ -0,0 +1,23 @@ +# DYNLIB_EXPORT + +This macro is needed on some systems as a function attribute to export the symbol. If you omit it, some symbols will not be available. + +## Macro + +### SYNOPSIS + +````cpp +#define DYNLIB_EXPORT /* implementation-defined */ +```` + +### EXAMPLE + +````cppp +extern "C" { + +DYNLIB_EXPORT int myfunction(void) { + return 0; +} + +} +```` \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/C++/modules/Base64/Base64.cpp Sun Mar 08 14:26:33 2015 +0100 @@ -0,0 +1,66 @@ +/* + * Base64.cpp -- base64 encoding and decoding + * + * Copyright (c) 2013, 2014 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <iterator> +#include <sstream> + +#include "Base64.h" + +char Base64::lookup(int value) noexcept +{ + static const char table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + + return table[value]; +} + +int Base64::rlookup(char ch) +{ + if (ch == '+') + return 62; + if (ch == '/') + return 63; + + if (ch >= '0' && ch <= '9') + return ch + 4; + if (ch >= 'A' && ch <= 'Z') + return ch - 65; + if (ch >= 'a' && ch <= 'z') + return ch - 71; + + throw std::invalid_argument("not a valid base64 string"); +} + +std::string Base64::encode(const std::string &input) +{ + std::string result; + std::istringstream iss(input, std::istringstream::in); + + encode(std::istreambuf_iterator<char>(iss), std::istreambuf_iterator<char>(), std::back_inserter(result)); + + return result; +} + +std::string Base64::decode(const std::string &input) +{ + std::string result; + std::istringstream iss(input, std::istringstream::in); + + decode(std::istreambuf_iterator<char>(iss), std::istreambuf_iterator<char>(), std::back_inserter(result)); + + return result; +} \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/C++/modules/Base64/Base64.h Sun Mar 08 14:26:33 2015 +0100 @@ -0,0 +1,134 @@ +/* + * Base64.h -- base64 encoding and decoding + * + * Copyright (c) 2013, 2014 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _BASE_64_H_ +#define _BASE_64_H_ + +/** + * @file Base64.h + * @brief Base64 encoding and decoding + */ + +#include <stdexcept> +#include <string> + +/** + * @class Base64 + * @brief Encode and decode Base64 data + */ +class Base64 { +public: + /** + * Get the base 64 character from the 6-bits value. + * + * @param value the value + */ + static char lookup(int value) noexcept; + + /** + * Get the integer value from the base 64 character. + * + * @param ch the base64 character + */ + static int rlookup(char ch); + + /** + * Encode the input to the output. Requirements: + * InputIt must be InputIterator + * OutputIt must be OutputIterator + * + * @param input the beginning + * @param end the end of the data + * @param output the output destination + * @return output + */ + template <typename InputIt, typename OutputIt> + static OutputIt encode(InputIt input, InputIt end, OutputIt output) + { + while (input != end) { + char inputbuf[3] = { 0, 0, 0 }; + int count; + + for (count = 0; count < 3 && input != end; ++count) + inputbuf[count] = *input++; + + *output++ = lookup(inputbuf[0] >> 2 & 0x3f); + *output++ = lookup((inputbuf[0] << 4 & 0x3f) | (inputbuf[1] >> 4 & 0x0f)); + *output++ = (count < 2) ? '=' : lookup((inputbuf[1] << 2 & 0x3c) | (inputbuf[2] >> 6 & 0x03)); + *output++ = (count < 3) ? '=' : lookup(inputbuf[2] & 0x3f); + } + + return output; + } + + /** + * Decode the input to the output. Requirements: + * InputIt must be InputIterator + * OutputIt must be OutputIterator + * + * @param input the beginning + * @param end the end of the data + * @param output the output destination + * @return output + * @throw std::invalid_argument on bad base64 string + */ + template <typename InputIt, typename OutputIt> + static OutputIt decode(InputIt input, InputIt end, OutputIt output) + { + while (input != end) { + char inputbuf[4] = { 0, 0, 0, 0 }; + int count; + + for (count = 0; count < 4 && input != end; ++count) { + inputbuf[count] = (*input == '=') ? '=' : rlookup(*input); + input++; + } + + if (count != 4) + throw std::invalid_argument("truncated string"); + + *output++ = (inputbuf[0] << 2 & 0xfc) | (inputbuf[1] >> 4 & 0x03); + + if (inputbuf[2] != '=') + *output++ = (inputbuf[1] << 4 & 0xf0) | (inputbuf[2] >> 2 & 0x0f); + if (inputbuf[3] != '=') + *output++ = (inputbuf[2] << 6 & 0xc0) | (inputbuf[3] & 0x3f); + } + + return output; + } + + /** + * Encode a string. + * + * @param input the input string + * @return the base64 formatted string + */ + static std::string encode(const std::string &input); + + /** + * Decode a string. + * + * @param input the base64 formatted string + * @return the original string + * @throw std::invalid_argument on bad base64 string + */ + static std::string decode(const std::string &input); +}; + +#endif // !_BASE_64_H_
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/C++/modules/Converter/Converter.cpp Sun Mar 08 14:26:33 2015 +0100 @@ -0,0 +1,92 @@ +/* + * Converter.cpp -- iconv based converter + * + * Copyright (c) 2013, 2014 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <cerrno> +#include <cstring> +#include <string> +#include <iterator> +#include <memory> +#include <string> +#include <stdexcept> +#include <vector> + +#include <iconv.h> + +#include "Converter.h" + +struct Deleter { + void operator()(iconv_t desc) + { + iconv_close(desc); + } +}; + +using Iconv = std::unique_ptr<std::remove_pointer<iconv_t>::type, Deleter>; + +std::string Converter::convert(const char *from, + const char *to, + const std::string &input) +{ + // No conversion if from and to are identical + if (std::strcmp(from, to) == 0) + return input; + + // Try to open the conversion descriptor + auto cd = iconv_open(to, from); + + if (cd == (iconv_t)-1) + throw std::invalid_argument(std::strerror(errno)); + + Iconv cv(cd); + std::size_t insize(input.size()); + std::size_t outsize(insize); + std::vector<char> result(insize + 1); + + auto *b = &input[0]; + auto *p = &result[0]; + + while (insize > 0) { + /* Convert */ + auto r = iconv(cv.get(), &b, &insize, &p, &outsize); + + if (r == (size_t)-1) { + switch (errno) { + case EBADF: + case EILSEQ: + case EINVAL: + throw std::invalid_argument(std::strerror(errno)); + case E2BIG: + /* + * Here, we need to reallocate more data because the output + * string may need more space. + * + * We use 16 as an optimistic value. + */ + + result.reserve(result.size() + 16 + 1); + p = &result[result.size()]; + outsize += 16; + default: + break; + } + } + + } + + return std::string(&result[0], (p - &result[0])); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/C++/modules/Converter/Converter.h Sun Mar 08 14:26:33 2015 +0100 @@ -0,0 +1,49 @@ +/* + * Converter.h -- iconv based converter + * + * Copyright (c) 2013, 2014 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _CONVERTER_H_ +#define _CONVERTER_H_ + +/** + * @file Converter.h + * @brief Converter using libiconv + */ + +#include <string> + +/** + * @class Converter + * @brief Convert string between different encodings + */ +class Converter { +public: + /** + * Convert the string into a different encoding. + * + * @param from the from encoding + * @param to the destination encoding + * @param input the string to convert + * @return the converted string + * @throw std::invalid_argument on invalid sequence + */ + static std::string convert(const char *from, + const char *to, + const std::string &input); +}; + +#endif // !_CONVERTER_H_
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/C++/modules/Date/Date.cpp Sun Mar 08 14:26:33 2015 +0100 @@ -0,0 +1,59 @@ +/* + * Date.cpp -- date and time manipulation + * + * Copyright (c) 2011, 2012, 2013 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "Date.h" + +Date::Date() +{ + m_timestamp = time(NULL); +} + +Date::Date(time_t timestamp) +{ + m_timestamp = timestamp; +} + +Date::~Date() +{ +} + +time_t Date::getTimestamp() const +{ + return m_timestamp; +} + +std::string Date::format(const std::string &format) +{ + char buffer[512]; + struct tm *tm; + + tm = localtime(&m_timestamp); + strftime(buffer, sizeof (buffer), format.c_str(), tm); + + return std::string(buffer); +} + +bool operator==(const Date &d1, const Date &d2) +{ + return d1.getTimestamp() == d2.getTimestamp(); +} + +bool operator<=(const Date &d1, const Date &d2) +{ + return d1.getTimestamp() <= d2.getTimestamp(); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/C++/modules/Date/Date.h Sun Mar 08 14:26:33 2015 +0100 @@ -0,0 +1,55 @@ +/* + * Date.h -- date and time manipulation + * + * Copyright (c) 2011, 2012, 2013 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _DATE_H_ +#define _DATE_H_ + +#include <cstdint> +#include <ctime> +#include <string> + +struct Date +{ + time_t m_timestamp; //! time epoch + + Date(); + Date(time_t timestamp); + ~Date(); + + /** + * Get the timestamp. + * + * @return the timestamp + */ + time_t getTimestamp() const; + + /** + * Format the current that in the specified format, + * see strftime(3) for patterns. + * + * @param format the format + * @return the date formated + */ + std::string format(const std::string &format); +}; + +bool operator==(const Date &, const Date &); + +bool operator<=(const Date &, const Date &); + +#endif // !_DATE_H_
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/C++/modules/Directory/Directory.cpp Sun Mar 08 14:26:33 2015 +0100 @@ -0,0 +1,189 @@ +/* + * Directory.cpp -- open and read directories + * + * Copyright (c) 2013, 2014 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sstream> +#include <stdexcept> + +#include "Directory.h" + +#if defined(_WIN32) +# include <Windows.h> +#else +# include <cstring> +# include <cerrno> + +# include <sys/types.h> +# include <dirent.h> +#endif + +#if defined(_WIN32) + +namespace { + +std::string systemError() +{ + LPSTR error = nullptr; + std::string errmsg = "Unknown error"; + + FormatMessageA( + FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, + NULL, + GetLastError(), + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPSTR)&error, 0, NULL); + + if (error) { + errmsg = std::string(error); + LocalFree(error); + } + + return errmsg; +} + +} + +void Directory::systemLoad(const std::string &path, int flags) +{ + std::ostringstream oss; + HANDLE handle; + WIN32_FIND_DATA fdata; + + oss << path << "\\*"; + handle = FindFirstFile(oss.str().c_str(), &fdata); + + if (handle == nullptr) + throw std::runtime_error(systemError()); + + do { + Entry entry; + + entry.name = fdata.cFileName; + if ((flags & Directory::NotDot) && entry.name == ".") + continue; + if ((flags & Directory::NotDotDot) && entry.name == "..") + continue; + + switch (fdata.dwFileAttributes) { + case FILE_ATTRIBUTE_DIRECTORY: + entry.type = Dir; + break; + case FILE_ATTRIBUTE_NORMAL: + entry.type = File; + break; + case FILE_ATTRIBUTE_REPARSE_POINT: + entry.type = Link; + break; + default: + break; + } + + m_list.push_back(entry); + } while (FindNextFile(handle, &fdata) != 0); + + FindClose(handle); +} + +#else + +void Directory::systemLoad(const std::string &path, int flags) +{ + DIR *dp; + struct dirent *ent; + + if ((dp = opendir(path.c_str())) == nullptr) + throw std::runtime_error(strerror(errno)); + + while ((ent = readdir(dp)) != nullptr) { + Entry entry; + + entry.name = ent->d_name; + if ((flags & Directory::NotDot) && entry.name == ".") + continue; + if ((flags & Directory::NotDotDot) && entry.name == "..") + continue; + + switch (ent->d_type) { + case DT_DIR: + entry.type = Dir; + break; + case DT_REG: + entry.type = File; + break; + case DT_LNK: + entry.type = Link; + break; + default: + break; + } + + m_list.push_back(entry); + } + + closedir(dp); +} + +#endif + +Directory::Entry::Entry() + : type(Unknown) +{ +} + +bool operator==(const Directory::Entry &e1, const Directory::Entry &e2) +{ + return e1.name == e2.name && e1.type == e2.type; +} + +Directory::Directory() +{ +} + +Directory::Directory(const std::string &path, int flags) +{ + systemLoad(path, flags); +} + +Directory::List::iterator Directory::begin() +{ + return m_list.begin(); +} + +Directory::List::const_iterator Directory::cbegin() const +{ + return m_list.cbegin(); +} + +Directory::List::iterator Directory::end() +{ + return m_list.end(); +} + +Directory::List::const_iterator Directory::cend() const +{ + return m_list.cend(); +} + +int Directory::count() const +{ + return m_list.size(); +} + +bool operator==(const Directory &d1, const Directory &d2) +{ + return d1.m_list == d2.m_list; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/C++/modules/Directory/Directory.h Sun Mar 08 14:26:33 2015 +0100 @@ -0,0 +1,131 @@ +/* + * Directory.h -- open and read directories + * + * Copyright (c) 2013, 2014 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _DIRECTORY_H_ +#define _DIRECTORY_H_ + +#include <cstddef> +#include <string> +#include <vector> + +/** + * @class Directory + * @brief class to manipulate directories + * + * This class allow the user to iterate directories in a for range based + * loop using iterators. + */ +class Directory { +public: + /** + * @enum Flags + * @brief optional flags to read directories + */ + enum Flags { + NotDot = (1 << 0), + NotDotDot = (1 << 1) + }; + + /** + * @enum Type + * @brief Describe the type of an entry + */ + enum Type { + Unknown = 0, + File, + Dir, + Link + }; + + /** + * @struct Entry + * @brief entry in the directory list + */ + struct Entry { + std::string name; //! name of entry (base name) + Type type; //! type of file + + Entry(); + + friend bool operator==(const Entry &e1, const Entry &e2); + }; + + using List = std::vector<Entry>; + + // C++ Container compatibility + using value_type = List::value_type; + using iterator = List::iterator; + using const_iterator = List::const_iterator; + +private: + List m_list; + + void systemLoad(const std::string &path, int flags); + +public: + /** + * Default constructor, does nothing. + */ + Directory(); + + /** + * Open a directory and read all its content. + * @param path the path + * @param flags the optional flags + */ + Directory(const std::string &path, int flags = 0); + + /** + * Return an iterator the beginning. + * + * @return the iterator + */ + List::iterator begin(); + + /** + * Return a const iterator the beginning. + * + * @return the iterator + */ + List::const_iterator cbegin() const; + + /** + * Return an iterator to past the end. + * + * @return the iterator + */ + List::iterator end(); + + /** + * Return a const iterator to past the end. + * + * @return the iterator + */ + List::const_iterator cend() const; + + /** + * Get the number of entries in the directory. + * + * @return the number + */ + int count() const; + + friend bool operator==(const Directory &d1, const Directory &d2); +}; + +#endif // !_DIRECTORY_H_
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/C++/modules/Driver/Driver.cpp Sun Mar 08 14:26:33 2015 +0100 @@ -0,0 +1,155 @@ +/* + * Driver.cpp -- generic SQL driver access + * + * Copyright (c) 2013, 2014 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <stdexcept> +#include <sstream> + +#include "Driver.h" + +/* --------------------------------------------------------- + * DriverQuery class + * ---------------------------------------------------------*/ + +DriverQuery::DriverQuery(Ptr impl) + : m_impl(impl) +{ +} + +void DriverQuery::assertRequest(int row, const std::string &column, DriverColumn wanted) +{ + std::ostringstream oss; + + // Out of bounds? + if (row < 0 || row >= countRows()) { + oss << "Invalid row index " << row; + oss << ", expected [0.." << countRows() << "]"; + + throw std::runtime_error(oss.str()); + } + + // Not found or bad column? + if (type(column) != wanted) { + oss << "Invalid or not found column `" << column << "'"; + + throw std::runtime_error(oss.str()); + } +} + +DriverQuery::~DriverQuery() +{ +} + +DriverColumn DriverQuery::type(const std::string &column) const +{ + return m_impl->type(column); +} + +int DriverQuery::countRows() +{ + return m_impl->countRows(); +} + +int DriverQuery::countColumns() +{ + return m_impl->countColumns(); +} + +bool DriverQuery::isNull(int row, const std::string &column) +{ + return m_impl->isNull(row, column); +} + +void DriverQuery::dump() +{ + m_impl->dump(); +} + +/* -------------------------------------------------------- + * DriverRequest class + * -------------------------------------------------------- */ + +DriverRequest::DriverRequest(Ptr impl, const std::string &command) + : m_pos(0) + , m_params(0) + , m_command(command) + , m_impl(impl) +{ + int i = -1; + + while ((i = command.find('#', i + 1)) != std::string::npos) + ++ m_params; +} + +DriverRequest::~DriverRequest() +{ +} + +void DriverRequest::assertCorrect() +{ + if (m_params <= 0) + throw std::runtime_error("no more arguments to set"); +} + +void DriverRequest::setValue(const std::string &value) +{ + assertCorrect(); + + m_pos = m_command.find('#', m_pos); + m_command.replace(m_pos, 1, value); + m_pos += value.length(); + m_params --; +} + +DriverRequest::operator std::string() +{ + return m_command; +} + +/* -------------------------------------------------------- + * Driver class + * -------------------------------------------------------- */ + +void Driver::connect(const Params ¶ms) +{ + m_impl->connect(params); +} + +DriverRequest Driver::prepare(const std::string &command) +{ + return m_impl->prepare(command); +} + +DriverQuery Driver::query(const std::string &sql) +{ + return m_impl->query(sql); +} + +DriverQuery Driver::query(DriverRequest request) +{ + return query(static_cast<std::string>(request)); +} + +std::string Driver::description() const +{ + return m_impl->description(); +} + +std::string Driver::version() const +{ + return m_impl->version(); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/C++/modules/Driver/Driver.h Sun Mar 08 14:26:33 2015 +0100 @@ -0,0 +1,529 @@ +/* + * Driver.h -- generic SQL driver access + * + * Copyright (c) 2013, 2014 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _DRIVER_H_ +#define _DRIVER_H_ + +#include <ctime> +#include <memory> +#include <string> +#include <unordered_map> +#include <type_traits> + +/** + * @enum DriverColumn + * @brief The column type request + * + * Used for the drivers. + */ +enum class DriverColumn { + Invalid, //! not found + Boolean, //! bool or 0 / 1 + Date, //! date see Common/Date.h + Double, //! double + Integer, //! 32 or 64 bit int + String, //! varchar to std::string +}; + +template <typename T> +struct DriverTypeInfo : std::false_type { }; + +/** + * @class Query + * @brief Class for querying the database + * + * That class is returned when a SQL query succeed. It can retrieve the + * number of rows, columns and retrieve the results independantly from the + * driver. + * + * @see Driver::query + */ +class DriverQuery { +public: + friend struct DriverTypeInfo<bool>; + friend struct DriverTypeInfo<time_t>; + friend struct DriverTypeInfo<double>; + friend struct DriverTypeInfo<int>; + friend struct DriverTypeInfo<std::string>; + + class Impl { + public: + /** + * Get a bool. + * + * @param row the row number + * @param column the column + * @return the value + */ + virtual bool getBoolean(int row, const std::string &column) = 0; + + /** + * Get a Date. + * + * @param row the row number + * @param column the column + * @return the value + */ + virtual time_t getDate(int row, const std::string &column) = 0; + + /** + * Get a double. + * + * @param row the row number + * @param column the column + * @return the value + */ + virtual double getDouble(int row, const std::string &column) = 0; + + /** + * Get a integer. + * + * @param row the row number + * @param column the column + * @return the value + */ + virtual int getInt(int row, const std::string &column) = 0; + + /** + * Get a string. + * + * @param row the row number + * @param column the column + * @return the value + */ + virtual std::string getString(int row, const std::string &column) = 0; + + /** + * Returns the type of a named column. + * + * @param column the column name + * @return the type + */ + virtual DriverColumn type(const std::string &column) const = 0; + + /** + * Tells how many rows has been fetched. + * + * @return the number of rows + */ + virtual int countRows() = 0; + + /** + * Tells how many number of columns are present for each + * row. + * + * @return the number of columns + */ + virtual int countColumns() = 0; + + /** + * Tells if the column is null or not. + * + * @param row the row number + * @param column the column + * @return an true if null + */ + virtual bool isNull(int row, const std::string &column) = 0; + + /** + * Dump all rows and columns. + */ + virtual void dump() = 0; + }; + + using Ptr = std::shared_ptr<Impl>; + +private: + /** + * Check if the request is valid and throws an exception + * on error. + * + * @param row the row number + * @param column the column name + * @param type + * @throw Error on error + */ + void assertRequest(int row, const std::string &column, DriverColumn type); + +protected: + Ptr m_impl; + +public: + DriverQuery(Ptr impl); + + /** + * Default destructor. + */ + virtual ~DriverQuery(); + + /** + * Get a variable from a row and column. + * + * Specialization available: + * - bool + * - Date + * - double + * - int + * - std::string + * + * @param row the row number (starts from 0) + * @param column the the column name + * @return the value + * @throw Query::Error on error + */ + template <class T> + T get(int row, const std::string &column) + { + static_assert(DriverTypeInfo<T>::value, "unsupported type"); + + assertRequest(row, column, DriverTypeInfo<T>::type); + + return DriverTypeInfo<T>::get(*this, row, column); + } + + /** + * Returns the type of a named column. + * + * @param column the column name + * @return the type + */ + DriverColumn type(const std::string &column) const; + + /** + * Tells how many rows has been fetched. + * + * @return the number of rows + */ + int countRows(); + + /** + * Tells how many number of columns are present for each + * row. + * + * @return the number of columns + */ + int countColumns(); + + /** + * Tells if the column is null or not. + * + * @param row the row number + * @param column the column + * @return an true if null + */ + bool isNull(int row, const std::string &column); + + /** + * Dump all rows and columns. + */ + void dump(); +}; + +/** + * @class Request + * @brief A secure helper for creating requests + * + * This helps creating class with SQL injection security and such. + */ +class DriverRequest { +public: + friend struct DriverTypeInfo<bool>; + friend struct DriverTypeInfo<time_t>; + friend struct DriverTypeInfo<double>; + friend struct DriverTypeInfo<int>; + friend struct DriverTypeInfo<std::string>; + + class Impl { + public: + /** + * Bind a boolean. + * + * @param value the boolean + * @return the string to use + */ + virtual std::string bindBoolean(bool value) = 0; + + /** + * Bind a date. + * + * @param value the date + * @return the string to use + */ + virtual std::string bindDate(time_t value) = 0; + + /** + * Bind a double. + * + * @param value the double + * @return the string to use + */ + virtual std::string bindDouble(double value) = 0; + + /** + * Bind an integer. + * + * @param value the integer + * @return the string to use + */ + virtual std::string bindInteger(int value) = 0; + + /** + * Bind a string. + * + * @param value the string + * @return the string to use + */ + virtual std::string bindString(std::string value) = 0; + }; + + using Ptr = std::shared_ptr<Impl>; + +private: + size_t m_pos; + int m_params; + std::string m_command; + Ptr m_impl; + + void assertCorrect(); + + void setValue(const std::string &value); + +public: + DriverRequest(Ptr impl, const std::string &command); + + /** + * Default destructor. + */ + virtual ~DriverRequest(); + + /** + * Bind a value. + * + * @param value the value + */ + template <typename T> + void bind(T value) + { + static_assert(DriverTypeInfo<T>::value, "unsupported type"); + + setValue(DriverTypeInfo<T>::bind(value)); + } + + /** + * Convert the request to string. + * + * @return the request as a string + */ + operator std::string(); +}; + +/** + * @class Driver + * @brief A generic SQL driver + * + * This class is used to connect to a database and execute SQL queries. It + * does not include any DBMS code and just call virtual functions for + * a simpler integration of new DBMS drivers. + */ +class Driver { +public: + class Impl; + + using Params = std::unordered_map<std::string, std::string>; + using Ptr = std::shared_ptr<Impl>; + + class Impl { + public: + /** + * Create a synchronous connection, it waits and block until + * the connection is made up to a specified timeout max. + * + * @param params a list of parameters. + */ + virtual void connect(const Params ¶ms) = 0; + + /** + * Prepare a request with the specified SQL command. + * + * @param command the SQL command to parse and bind + * @return a request to use with query + * @see query + */ + virtual DriverRequest prepare(const std::string &command) = 0; + + /** + * Execute a query. + * + * @param query the SQL command + * @return a result + * @throw Query::Error on failure + */ + virtual DriverQuery query(const std::string &command) = 0; + + /** + * Get the driver connection description. + * + * @return the description + */ + virtual std::string description() const = 0; + + /** + * Get the driver version as a string. + * + * @return the version + */ + virtual std::string version() const = 0; + }; + +protected: + Ptr m_impl; + +public: + Driver() = default; + + /** + * Virtual destructor. + */ + virtual ~Driver() = default; + + /** + * Wrapper for std::string variant. + * + * @param request the request to use + * @return a result + * @throw Query::Error on failure + */ + DriverQuery query(DriverRequest request); + + /** + * Create a synchronous connection, it waits and block until + * the connection is made up to a specified timeout max. + * + * @param params a list of parameters. + */ + void connect(const Params ¶ms); + + /** + * Prepare a request with the specified SQL command. + * + * @param command the SQL command to parse and bind + * @return a request to use with query + * @see query + */ + DriverRequest prepare(const std::string &command); + + /** + * Execute a query. + * + * @param query the SQL command + * @return a result + * @throw Query::Error on failure + */ + DriverQuery query(const std::string &command); + + /** + * Get the driver connection description. + * + * @return the description + */ + std::string description() const; + + /** + * Get the driver version as a string. + * + * @return the version + */ + std::string version() const; +}; + +template <> +struct DriverTypeInfo<bool> : std::true_type { + static const DriverColumn type = DriverColumn::Boolean; + + static bool get(DriverQuery &query, int row, const std::string &column) + { + return query.m_impl->getBoolean(row, column); + } + + static void bind(DriverRequest &request, bool value) + { + request.m_impl->bindBoolean(value); + } +}; + +template <> +struct DriverTypeInfo<time_t> : std::true_type { + static const DriverColumn type = DriverColumn::Date; + + static time_t get(DriverQuery &query, int row, const std::string &column) + { + return query.m_impl->getDate(row, column); + } + + static void bind(DriverRequest &request, time_t value) + { + request.m_impl->bindDate(value); + } +}; + +template <> +struct DriverTypeInfo<double> : std::true_type { + static const DriverColumn type = DriverColumn::Double; + + static double get(DriverQuery &query, int row, const std::string &column) + { + return query.m_impl->getDouble(row, column); + } + + static void bind(DriverRequest &request, double value) + { + request.m_impl->bindDouble(value); + } +}; + +template <> +struct DriverTypeInfo<int> : std::true_type { + static const DriverColumn type = DriverColumn::Integer; + + static int get(DriverQuery &query, int row, const std::string &column) + { + return query.m_impl->getInt(row, column); + } + + static void bind(DriverRequest &request, int value) + { + request.m_impl->bindInteger(value); + } +}; + +template <> +struct DriverTypeInfo<std::string> : std::true_type { + static const DriverColumn type = DriverColumn::String; + + static std::string get(DriverQuery &query, int row, const std::string &column) + { + return query.m_impl->getString(row, column); + } + + static void bind(DriverRequest &request, const std::string &value) + { + request.m_impl->bindString(value); + } +}; + +#endif // !_DRIVER_H_
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/C++/modules/Driver/DriverPostgres.cpp Sun Mar 08 14:26:33 2015 +0100 @@ -0,0 +1,482 @@ +/* + * DriverPostgres.cpp -- PostgreSQL driver + * + * Copyright (c) 2013, 2014 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <cerrno> +#include <cstring> +#include <iostream> +#include <stdexcept> +#include <sstream> +#include <vector> + +#include <libpq-fe.h> + +#include "DriverPostgres.h" + +namespace { + +using PostgresResult = std::shared_ptr<PGresult>; +using PostgresConn = std::shared_ptr<PGconn>; + +} + +/** + * @class QueryPostgres + * @brief Query implementation for PostgreSQL. + */ +class QueryPostgresImpl : public DriverQuery::Impl { +private: + PostgresConn m_connection; + PostgresResult m_result; + +public: + /** + * Constructor used by DriverPostgres + * + * @param result the result + */ + QueryPostgresImpl(PostgresConn conn, PostgresResult result); + + /** + * @copydoc Query::getBoolean + */ + virtual bool getBoolean(int row, const std::string &column); + + /** + * @copydoc Query::getDate + */ + virtual time_t getDate(int row, const std::string &column); + + /** + * @copydoc Query::getDouble + */ + virtual double getDouble(int row, const std::string &column); + + /** + * @copydoc Query::getInt + */ + virtual int getInt(int row, const std::string &column); + + /** + * @copydoc Query::getString + */ + virtual std::string getString(int row, const std::string &column); + + /** + * @copydoc Query::type + */ + virtual DriverColumn type(const std::string &column) const; + + /** + * @copydoc Query::countRows + */ + virtual int countRows(); + + /** + * @copydoc Query::countColumns + */ + virtual int countColumns(); + + /** + * @copydoc Query::isNull + */ + virtual bool isNull(int row, const std::string &column); + + /** + * @copydoc Query::dump + */ + virtual void dump(); +}; + +QueryPostgresImpl::QueryPostgresImpl(PostgresConn conn, PostgresResult result) + : m_connection(conn) + , m_result(result) +{ +} + +DriverColumn QueryPostgresImpl::type(const std::string &column) const +{ + DriverColumn type; + int pqType, index; + + index = PQfnumber(m_result.get(), column.c_str()); + pqType = PQftype(m_result.get(), index); + switch (pqType) { + case 16: + type = DriverColumn::Boolean; + break; + case 1082: + case 1083: + case 1114: + case 1184: + type = DriverColumn::Date; + break; + case 1700: + case 700: + case 701: + type = DriverColumn::Double; + break; + case 20: + case 21: + case 23: + type = DriverColumn::Integer; + break; + case 25: + case 1042: + case 1043: + type = DriverColumn::String; + break; + default: + type = DriverColumn::Invalid; + } + + return type; +} + +int QueryPostgresImpl::countRows() +{ + return PQntuples(m_result.get()); +} + +int QueryPostgresImpl::countColumns() +{ + return PQnfields(m_result.get()); +} + +bool QueryPostgresImpl::isNull(int row, const std::string &column) +{ + int idx = PQfnumber(m_result.get(), column.c_str()); + + return PQgetisnull(m_result.get(), row, idx) == 1; +} + +void QueryPostgresImpl::dump(void) +{ + std::cout << "Dumping PostgreSQL result, "; + std::cout << countRows() << " rows, "; + std::cout << countColumns() << " columns" << std::endl; + + for (int r = 0; r < countRows(); ++r) { + std::cout << "Dumping row " << r << std::endl; + std::cout << "==============================" << std::endl; + + for (int c = 0; c < countColumns(); ++c) { + std::cout << "\t" << PQfname(m_result.get(), c); + std::cout << " = " << PQgetvalue(m_result.get(), r, c) << std::endl; + } + } +} + +bool QueryPostgresImpl::getBoolean(int row, const std::string &column) +{ + int idx = PQfnumber(m_result.get(), column.c_str()); + std::string code = PQgetvalue(m_result.get(), row, idx); + + return code[0] == 't'; +} + +time_t QueryPostgresImpl::getDate(int row, const std::string &column) +{ + int idx = PQfnumber(m_result.get(), column.c_str()); + time_t timestamp = std::time(nullptr); + + try { + std::ostringstream oss; + std::string value, req; + + value = PQgetvalue(m_result.get(), row, idx); + + /* + * Convert the date using the SQL function so that user does + * not require any explicit conversion itself. + */ + printf("%s\n", value.c_str()); + oss << "Select EXTRACT(EPOCH FROM TIMESTAMP WITH TIME ZONE '"; + oss << value; + oss << "') AS RESULT"; + req = oss.str(); + + auto info = PQexec(m_connection.get(), oss.str().c_str()); + auto status = PQresultStatus(info); + + if (info != nullptr && (status == PGRES_COMMAND_OK || + status == PGRES_TUPLES_OK)) + { + timestamp = atoi(PQgetvalue(info, 0, 0)); + PQclear(info); + } + } catch (...) { } + + return timestamp; +} + +double QueryPostgresImpl::getDouble(int row, const std::string &column) +{ + int idx = PQfnumber(m_result.get(), column.c_str()); + + return std::stod(PQgetvalue(m_result.get(), row, idx)); +} + +int QueryPostgresImpl::getInt(int row, const std::string &column) +{ + int idx = PQfnumber(m_result.get(), column.c_str()); + + return std::stoi(PQgetvalue(m_result.get(), row, idx)); +} + +std::string QueryPostgresImpl::getString(int row, const std::string &column) +{ + int idx = PQfnumber(m_result.get(), column.c_str()); + + return std::string(PQgetvalue(m_result.get(), row, idx)); +} + +/* -------------------------------------------------------- + * Request PostgreSQL Impl + * -------------------------------------------------------- */ + +/** + * @class RequestPostgres + * @brief Request implementation for PostgreSQL. + */ +class RequestPostgres : public DriverRequest::Impl { +private: + std::shared_ptr<PGconn> m_connection; + +protected: + /** + * @copydoc Request::bindBoolean + */ + virtual std::string bindBoolean(bool value); + + /** + * @copydoc Request::bindDate + */ + virtual std::string bindDate(time_t value); + + /** + * @copydoc Request::bindDouble + */ + virtual std::string bindDouble(double value); + + /** + * @copydoc Request::bindInteger + */ + virtual std::string bindInteger(int value); + + /** + * @copydoc Request::bindString + */ + virtual std::string bindString(std::string value); + +public: + /** + * Construct a request for PostgreSQL. + * + * @param conn the connection + * @param command the command + */ + RequestPostgres(PostgresConn &conn); +}; + +RequestPostgres::RequestPostgres(PostgresConn &conn) +{ + m_connection = conn; +} + +std::string RequestPostgres::bindBoolean(bool value) +{ + return (value) ? "'t'" : "'f'"; +} + +std::string RequestPostgres::bindDate(time_t value) +{ + std::ostringstream oss; + + oss << "to_timestamp(" << value << ")"; + + return oss.str(); +} + +std::string RequestPostgres::bindDouble(double value) +{ + return std::to_string(value); +} + +std::string RequestPostgres::bindInteger(int value) +{ + return std::to_string(value); +} + +std::string RequestPostgres::bindString(std::string value) +{ + std::string result; + char *tmp; + + tmp = PQescapeLiteral(m_connection.get(), value.c_str(), value.length()); + if (tmp == nullptr) + return ""; + + result = std::string(tmp); + PQfreemem(tmp); + + return result; +} + +/* -------------------------------------------------------- + * Driver PostgreSQL impl + * -------------------------------------------------------- */ + +/** + * @class DriverPostgres + * @brief Driver implementation for PostgreSQL. + */ +class DriverPostgresImpl : public Driver::Impl { +private: + PostgresConn m_connection; + + /** + * Convert the Params from the config + * to the PostgreSQL string. + * + * @param settings the table + * @return a string to be used + */ + std::string convert(Driver::Params &settings); + +public: + /** + * @copydoc Driver::connect + */ + virtual void connect(const Driver::Params ¶ms); + + /** + * @copydoc Driver::prepare + */ + virtual DriverRequest prepare(const std::string &command); + + /** + * @copydoc Driver::query + */ + virtual DriverQuery query(const std::string &command); + + /** + * @copydoc Driver::description + */ + virtual std::string description() const; + + /** + * @copydoc Driver::version + */ + virtual std::string version() const; +}; + +std::string DriverPostgresImpl::convert(Driver::Params ¶ms) +{ + std::ostringstream oss; + std::vector<std::string> required { "host", "port", "user", "database", "password" }; + + for (auto s : required) + if (params.count(s) <= 0) + throw std::runtime_error("missing parameter " + s); + + oss << "host = " << params["host"] << " "; + oss << "port = " << params["port"] << " "; + oss << "user = " << params["user"] << " "; + oss << "dbname = " << params["database"] << " "; + oss << "password = " << params["password"]; + + return oss.str(); +} + +void DriverPostgresImpl::connect(const Driver::Params ¶ms) +{ + auto copy = params; + auto conn = PQconnectdb(convert(copy).c_str()); + + if (conn == nullptr) + throw std::runtime_error(std::strerror(ENOMEM)); + + if (PQstatus(conn) == CONNECTION_BAD) { + auto error = PQerrorMessage(conn); + PQfinish(conn); + + throw std::runtime_error(error); + } + + m_connection = std::shared_ptr<PGconn>(conn, PQfinish); +} + +DriverRequest DriverPostgresImpl::prepare(const std::string &command) +{ + return DriverRequest(std::make_shared<RequestPostgres>(m_connection), command); +} + +DriverQuery DriverPostgresImpl::query(const std::string &cmd) +{ + PGresult *info; + + // If NULL, the libpq said no memory + info = PQexec(m_connection.get(), cmd.c_str()); + if (info == nullptr) + throw std::runtime_error(strerror(ENOMEM)); + + // If an error occured + int errorCode = PQresultStatus(info); + if (errorCode != PGRES_COMMAND_OK && errorCode != PGRES_TUPLES_OK) { + auto error = PQresultErrorMessage(info); + PQclear(info); + + throw std::runtime_error(error); + } + + auto result = std::shared_ptr<PGresult>(info, PQclear); + auto impl = std::make_shared<QueryPostgresImpl>(m_connection, result); + + return DriverQuery(impl); +} + +std::string DriverPostgresImpl::description() const +{ + std::ostringstream oss; + + oss << "Connected on PostgreSQL database: " << std::endl; + oss << " host: " << PQhost(m_connection.get()) << std::endl; + oss << " port: " << PQport(m_connection.get()) << std::endl; + oss << " user: " << PQuser(m_connection.get()) << std::endl; + oss << " database: " << PQdb(m_connection.get()) << std::endl; + + return oss.str(); +} + +std::string DriverPostgresImpl::version() const +{ + std::ostringstream oss; + + oss << "PostgreSQL driver (version " << PQlibVersion() << ")"; + + return oss.str(); +} + +/* -------------------------------------------------------- + * Driver PostgreSQL + * -------------------------------------------------------- */ + +DriverPostgres::DriverPostgres() +{ + m_impl = std::make_shared<DriverPostgresImpl>(); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/C++/modules/Driver/DriverPostgres.h Sun Mar 08 14:26:33 2015 +0100 @@ -0,0 +1,33 @@ +/* + * DriverPostgres.h -- PostgreSQL driver + * + * Copyright (c) 2013, 2014 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * http://www.postgresql.org/docs/9.2/static/libpq-connect.html#LIBPQ-PARAMKEYWORDS + */ + +#ifndef _DRIVER_PG_H_ +#define _DRIVER_PG_H_ + +#include "Driver.h" + +class DriverPostgres : public Driver { +public: + DriverPostgres(); +}; + +#endif // !_DRIVER_PG_H_
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/C++/modules/Dynlib/Dynlib.cpp Sun Mar 08 14:26:33 2015 +0100 @@ -0,0 +1,144 @@ +/* + * DynLib.cpp -- portable shared library loader + * + * Copyright (c) 2013, 2014 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <stdexcept> + +#if defined(_WIN32) +# include <Windows.h> +#else +# include <dlfcn.h> +#endif + +#include "Dynlib.h" + +#if defined(_WIN32) + +namespace { + +std::string systemError() +{ + LPSTR error = nullptr; + std::string errmsg = "Unknown error"; + + FormatMessageA( + FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, + NULL, + GetLastError(), + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPSTR)&error, 0, NULL); + + if (error) { + errmsg = std::string(error); + LocalFree(error); + } + + return errmsg; +} + +} + +void Dynlib::systemInit() +{ + m_handle = nullptr; +} + +Dynlib::Handle Dynlib::systemLoad(const std::string &path, Policy policy) const +{ + Handle handle = LoadLibraryA(path.c_str()); + + if (handle == nullptr) + throw std::runtime_error(systemError()); + + return handle; +} + +Dynlib::Sym Dynlib::systemSym(const std::string &name) +{ + Sym sym; + + if (m_handle == nullptr) + throw std::runtime_error("library not loaded"); + + sym = GetProcAddress(m_handle, name.c_str()); + if (sym == nullptr) + throw std::out_of_range(systemError()); + + return sym; +} + +void Dynlib::systemClose() +{ + if (m_handle != nullptr) + FreeLibrary(m_handle); +} + +#else + +void Dynlib::systemInit() +{ + m_handle = nullptr; +} + +Dynlib::Handle Dynlib::systemLoad(const std::string &path, Policy policy) const +{ + int mode = (policy == Immediately) ? RTLD_NOW : RTLD_LAZY; + Handle handle; + + handle = dlopen(path.c_str(), mode); + if (handle == nullptr) + throw std::runtime_error(dlerror()); + + return handle; +} + +Dynlib::Sym Dynlib::systemSym(const std::string &name) +{ + Sym sym; + + if (m_handle == nullptr) + throw std::runtime_error("library not loaded"); + + sym = dlsym(m_handle, name.c_str()); + if (sym == nullptr) + throw std::out_of_range(dlerror()); + + return sym; +} + +void Dynlib::systemClose() +{ + if (m_handle != nullptr) + dlclose(m_handle); +} + +#endif + +Dynlib::Dynlib() +{ + systemInit(); +} + +Dynlib::Dynlib(const std::string &path, Policy policy) +{ + m_handle = systemLoad(path, policy); +} + +Dynlib::~Dynlib() +{ + systemClose(); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/C++/modules/Dynlib/Dynlib.h Sun Mar 08 14:26:33 2015 +0100 @@ -0,0 +1,103 @@ +/* + * DynLib.h -- portable shared library loader + * + * Copyright (c) 2013, 2014 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _DYNLIB_H_ +#define _DYNLIB_H_ + +#include <string> + +#if defined(_WIN32) +# include <Windows.h> +# define DYNLIB_EXPORT __declspec(dllexport) +#else +# define DYNLIB_EXPORT +#endif + +/** + * @class Dynlib + * @brief Load a dynamic module + * + * This class is a portable wrapper to load shared libraries on + * supported systems. + */ +class Dynlib { +public: +#if defined(_WIN32) + using Handle = HMODULE; + using Sym = FARPROC; +#else + using Handle = void *; + using Sym = void *; +#endif + + enum Policy { + Immediately, //! load symbols immediately + Lazy //! load symbols when needed + }; + +private: + Handle m_handle; + + void systemInit(); + Handle systemLoad(const std::string &path, Policy policy) const; + Sym systemSym(const std::string &name); + void systemClose(); + +public: + /** + * Copy is forbidden. + */ + Dynlib(const Dynlib &) = delete; + Dynlib &operator=(const Dynlib &) = delete; + + /** + * Default constructor. + */ + Dynlib(); + + /** + * Constructor to load a shared module. The path must + * be absolute. + * + * @param path the absolute path + * @param policy the policy to load + * @throw std::runtime_error on error + */ + Dynlib(const std::string &path, Policy policy = Immediately); + + /** + * Close the library automatically. + */ + ~Dynlib(); + + /** + * Get a symbol from the library. + * + * @param name the symbol + * @return the symbol + * @throw std::runtime_error on error + * @throw std::out_of_range if not found + */ + template <typename T> + T sym(const std::string &name) + { + return reinterpret_cast<T>(systemSym(name)); + } +}; + +#endif // !_DYNLIB_H_
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/C++/modules/Flags/Flags.h Sun Mar 08 14:26:33 2015 +0100 @@ -0,0 +1,341 @@ +/* + * Flags.h -- safe wrapper for enum flags + * + * Copyright (c) 2013, 2014 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _FLAGS_H_ +#define _FLAGS_H_ + +/** + * @file Flags.h + * @brief Provide template class Flags and operators for enum classes + */ + +#include <type_traits> + +/** + * @class Flags + * @brief Wrapper that store enum flags + * + * This class is used to store an enum and make bitwise operations on it. User do not need + * to supply these operators because they are implemented in the class. + * + * This class is very cheap and store the real underlying type from the enum and all functions + * are inlined. + */ +template <typename Enum, typename Type = std::underlying_type_t<Enum>> +class Flags final { +private: + Type m_value { 0 }; + +public: + /** + * Construct flags to 0. + */ + constexpr Flags() noexcept = default; + + /** + * Construct flags with the enum value. + * + * @param m the mask + */ + constexpr Flags(Enum m) noexcept + : m_value(static_cast<Type>(m)) + { + } + + /** + * Construct flags with the enum value. + * + * @param m the mask + */ + constexpr Flags(Type m) noexcept + : m_value(m) + { + } + + /** + * Move constructor. + * + * @param other the other + */ + inline Flags(Flags &&other) noexcept = default; + + /** + * Copy constructor. + * + * @param other the other + */ + inline Flags(const Flags &other) noexcept = default; + + /** + * Move operator. + * + * @param other the other + */ + inline Flags &operator=(Flags &&other) noexcept = default; + + /** + * Copy operator. + * + * @param other the other + */ + inline Flags &operator=(const Flags &other) noexcept = default; + + /** + * &= operator. + * + * @param m the mask + * @return *this + */ + inline Flags &operator&=(Enum m) noexcept + { + m_value &= static_cast<Type>(m); + + return *this; + } + + /** + * &= operator. + * + * @param m the mask + * @return *this + */ + inline Flags &operator&=(Type m) noexcept + { + m_value &= m; + + return *this; + } + + /** + * |= operator. + * + * @param m the mask + * @return *this + */ + inline Flags &operator|=(Enum m) noexcept + { + m_value |= static_cast<Type>(m); + + return *this; + } + + /** + * |= operator. + * + * @param m the mask + * @return *this + */ + inline Flags &operator|=(Type m) noexcept + { + m_value |= m; + + return *this; + } + + /** + * ^= operator. + * + * @param m the mask + * @return *this + */ + inline Flags &operator^=(Enum m) noexcept + { + m_value ^= static_cast<Type>(m); + + return *this; + } + + /** + * ^= operator. + * + * @param m the mask + * @return *this + */ + inline Flags &operator^=(Type m) noexcept + { + m_value ^= m; + + return *this; + } + + /** + * & operator. + * + * @param m the mask + * @return & combination + */ + constexpr Flags operator&(Enum m) const noexcept + { + return m_value & static_cast<Type>(m); + } + + /** + * & operator. + * + * @param m the mask + * @return & combination + */ + constexpr Flags operator&(Type m) const noexcept + { + return m_value & m; + } + + /** + * | operator. + * + * @param m the mask + * @return | combination + */ + constexpr Flags operator|(Enum m) const noexcept + { + return m_value | static_cast<Type>(m); + } + + /** + * | operator. + * + * @param m the mask + * @return | combination + */ + constexpr Flags operator|(Type m) const noexcept + { + return m_value | m; + } + + /** + * ^ operator. + * + * @param m the mask + * @return ^ combination + */ + constexpr Flags operator^(Enum m) const noexcept + { + return m_value & static_cast<Type>(m); + } + + /** + * ^ operator. + * + * @param m the mask + * @return ^ combination + */ + constexpr Flags operator^(Type m) const noexcept + { + return m_value & m; + } + + /** + * ~ unary operator. + * + * @return ~ + */ + constexpr Flags operator~() const noexcept + { + return ~m_value; + } + + /** + * Convert to bool. Simply check if value is not 0. + * + * @return true if value != 0 + */ + constexpr operator bool() const noexcept + { + return m_value != 0; + } + + /** + * Operator !. Simply check if value is 0. + * + * @return true if value == 0 + */ + constexpr bool operator!() const noexcept + { + return m_value == 0; + } + + /** + * Test comparison with other mask. + * + * @return true if this value == m + */ + constexpr bool operator==(Enum m) const noexcept + { + return m_value == static_cast<Type>(m); + } + + /** + * Test comparison with other mask. + * + * @return true if this value == m + */ + constexpr bool operator==(Type m) const noexcept + { + return m_value == m; + } +}; + +/** + * Apply & operator on the enum. + * + * @param x1 the first value + * @param x2 the second value + */ +template <typename Value, typename Type = std::enable_if_t<std::is_enum<Value>::value, std::underlying_type_t<Value>>> +constexpr Value operator&(Value x1, Value x2) noexcept +{ + return static_cast<Value>(static_cast<Type>(x1) & static_cast<Type>(x2)); +} + +/** + * Apply | operator on the enum. + * + * @param x1 the first value + * @param x2 the second value + */ +template <typename Value, typename Type = std::enable_if_t<std::is_enum<Value>::value, std::underlying_type_t<Value>>> +constexpr Value operator|(Value x1, Value x2) noexcept +{ + return static_cast<Value>(static_cast<Type>(x1) | static_cast<Type>(x2)); +} + +/** + * Apply ^ operator on the enum. + * + * @param x1 the first value + * @param x2 the second value + */ +template <typename Value, typename Type = std::enable_if_t<std::is_enum<Value>::value, std::underlying_type_t<Value>>> +constexpr Value operator^(Value x1, Value x2) noexcept +{ + return static_cast<Value>(static_cast<Type>(x1) ^ static_cast<Type>(x2)); +} + +/** + * Apply ~ operator on the enum. + * + * @param x1 the first value + * @param x2 the second value + */ +template <typename Value, typename Type = std::enable_if_t<std::is_enum<Value>::value, std::underlying_type_t<Value>>> +constexpr Value operator~(Value x) noexcept +{ + return static_cast<Value>(~static_cast<Type>(x)); +} + +#endif // !_FLAGS_H_ \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/C++/modules/Hash/Hash.cpp Sun Mar 08 14:26:33 2015 +0100 @@ -0,0 +1,42 @@ +/* + * Hash.cpp -- hash functions + * + * Copyright (c) 2013, 2014 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "Hash.h" + +#include <openssl/sha.h> +#include <openssl/md5.h> + +std::string Hash::md5(const std::string &input) +{ + return convert<MD5_CTX, MD5_DIGEST_LENGTH>(input, MD5_Init, MD5_Update, MD5_Final); +} + +std::string Hash::sha1(const std::string &input) +{ + return convert<SHA_CTX, SHA_DIGEST_LENGTH>(input, SHA1_Init, SHA1_Update, SHA1_Final); +} + +std::string Hash::sha256(const std::string &input) +{ + return convert<SHA256_CTX, SHA256_DIGEST_LENGTH>(input, SHA256_Init, SHA256_Update, SHA256_Final); +} + +std::string Hash::sha512(const std::string &input) +{ + return convert<SHA512_CTX, SHA512_DIGEST_LENGTH>(input, SHA512_Init, SHA512_Update, SHA512_Final); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/C++/modules/Hash/Hash.h Sun Mar 08 14:26:33 2015 +0100 @@ -0,0 +1,100 @@ +/* + * Hash.h -- hash functions + * + * Copyright (c) 2013, 2014 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _HASH_H_ +#define _HASH_H_ + +/** + * @file Hash.h + * @brief Hash functions + */ + +#include <string> + +/** + * @class Hash + * @brief Hash functions + * + * Provide support for MD5, SHA1, SHA256 and SHA512. + */ +class Hash { +private: + template <typename Context> + using Init = int (*)(Context *); + + template <typename Context> + using Update = int (*)(Context *, const void *, size_t); + + template <typename Context> + using Final = int (*)(unsigned char *, Context *); + + template <typename Context, size_t Length> + static std::string convert(const std::string &input, + Init<Context> init, + Update<Context> update, + Final<Context> finalize) + { + unsigned char digest[Length]; + char hash[Length * 2 + 1]; + + Context ctx; + init(&ctx); + update(&ctx, input.c_str(), input.length()); + finalize(digest, &ctx); + + for (unsigned long i = 0; i < Length; i++) + sprintf(&hash[i * 2], "%02x", (unsigned int)digest[i]); + + return std::string(hash); + } + +public: + /** + * Hash using MD5. + * + * @param input the input string + * @return the hashed string + */ + static std::string md5(const std::string &input); + + /** + * Hash using SHA1. + * + * @param input the input string + * @return the hashed string + */ + static std::string sha1(const std::string &input); + + /** + * Hash using SHA256. + * + * @param input the input string + * @return the hashed string + */ + static std::string sha256(const std::string &input); + + /** + * Hash using SHA512. + * + * @param input the input string + * @return the hashed string + */ + static std::string sha512(const std::string &input); +}; + +#endif // !_HASH_H_
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/C++/modules/Ini/Ini.cpp Sun Mar 08 14:26:33 2015 +0100 @@ -0,0 +1,425 @@ +/* + * Ini.cpp -- .ini file parsing + * + * Copyright (c) 2013, 2014 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <cctype> +#include <cerrno> +#include <cstring> +#include <fstream> +#include <iostream> +#include <iterator> +#include <memory> +#include <ostream> +#include <sstream> +#include <vector> + +#if defined(_WIN32) +# include <Shlwapi.h> // for PathIsRelative +#endif + +#include "Ini.h" + +namespace { + +/* -------------------------------------------------------- + * Tokens + * -------------------------------------------------------- */ + +enum class TokenType { + Comment = '#', + SectionBegin = '[', + SectionEnd = ']', + Escape = '\\', + QuoteSimple = '\'', + QuoteDouble = '"', + NewLine = '\n', + Assign = '=', + Include = '@', + Word, + Space +}; + +class Token { +private: + TokenType m_type; + int m_line; + int m_position; + std::string m_value; + +public: + inline Token(TokenType type, int line, int position, std::string value = "") + : m_type(type) + , m_line(line) + , m_position(position) + , m_value(std::move(value)) + { + } + + inline TokenType type() const noexcept + { + return m_type; + } + + inline int line() const noexcept + { + return m_line; + } + + inline int position() const noexcept + { + return m_position; + } + + inline std::string value() const + { + switch (m_type) { + case TokenType::Comment: + return "#"; + case TokenType::SectionBegin: + return "["; + case TokenType::SectionEnd: + return "]"; + case TokenType::QuoteSimple: + return "'"; + case TokenType::QuoteDouble: + return "\""; + case TokenType::NewLine: + return "\n"; + case TokenType::Assign: + return "="; + case TokenType::Include: + return "@"; + case TokenType::Space: + return m_value; + case TokenType::Word: + return m_value; + default: + break; + } + + return ""; + } + + inline std::string toString() const + { + switch (m_type) { + case TokenType::Comment: + return "'#'"; + case TokenType::SectionBegin: + return "'['"; + case TokenType::SectionEnd: + return "']'"; + case TokenType::QuoteSimple: + return "'"; + case TokenType::QuoteDouble: + return "\""; + case TokenType::NewLine: + return "<newline>"; + case TokenType::Assign: + return "="; + case TokenType::Include: + return "@"; + case TokenType::Space: + return "<blank>"; + case TokenType::Word: + return "`" + m_value + "'"; + default: + break; + } + + return ""; + } +}; + +using TokenStack = std::vector<Token>; + +/* -------------------------------------------------------- + * IniBuilder + * -------------------------------------------------------- */ + +class IniBuilder { +private: + std::string m_path; + std::string m_base; + Ini &m_ini; + +private: + inline bool isReserved(char c) const noexcept + { + return c == '\n' || c == '#' || c == '"' || c == '\'' || c == '=' || c == '[' || c == ']' || c == '@'; + } + + std::string base(std::string path) + { + auto pos = path.find_last_of("/\\"); + + if (pos != std::string::npos) { + path.erase(pos); + } else { + path = "."; + } + + return path; + } + +#if defined(_WIN32) + bool isAbsolute(const std::string &path) + { + return !PathIsRelative(path.c_str()); + } +#else + bool isAbsolute(const std::string &path) + { + return path.size() > 0 && path[0] == '/'; + } +#endif + + std::vector<Token> analyze(std::istream &stream) const + { + std::istreambuf_iterator<char> it(stream); + std::istreambuf_iterator<char> end; + std::vector<Token> tokens; + + int lineno{1}; + int position{0}; + + while (it != end) { + std::string value; + + if (isReserved(*it)) { + while (it != end && isReserved(*it)) { + // Single character tokens + switch (*it) { + case '\n': + ++lineno; + position = 0; + case '#': + case '[': + case ']': + case '\'': + case '"': + case '=': + case '@': + tokens.push_back({ static_cast<TokenType>(*it), lineno, position }); + ++it; + ++position; + default: + break; + } + } + } else if (std::isspace(*it)) { + while (it != end && std::isspace(*it) && *it != '\n') { + value.push_back(*it++); + } + + tokens.push_back({ TokenType::Space, lineno, position, std::move(value) }); + } else { + while (it != end && !std::isspace(*it) && !isReserved(*it)) { + value.push_back(*it++); + } + + tokens.push_back({ TokenType::Word, lineno, position, std::move(value) }); + } + } + + return tokens; + } + + void readComment(TokenStack::iterator &it, TokenStack::iterator end) + { + while (it != end && it->type() != TokenType::NewLine) { + ++ it; + } + + // remove new line + ++ it; + } + + void readSpace(TokenStack::iterator &it, TokenStack::iterator end) + { + while (it != end && it->type() == TokenType::Space) { + ++ it; + } + } + + void readNewLine(TokenStack::iterator &it, TokenStack::iterator end) + { + while (it != end && it->type() == TokenType::NewLine) { + ++ it; + } + } + + IniSection readSection(TokenStack::iterator &it, TokenStack::iterator end) + { + if (++it == end || it->type() != TokenType::Word) { + throw IniError(it->line(), it->position(), "word expected after [, got " + it->toString()); + } + + IniSection section(it->value()); + + if (++it == end || it->type() != TokenType::SectionEnd) { + throw IniError(it->line(), it->position(), "] expected, got " + it->toString()); + } + + // Remove ] + ++ it; + + if (it == end) { + return section; + } + + while (it != end && it->type() != TokenType::SectionBegin) { + if (it->type() == TokenType::Space) { + readSpace(it, end); + } else if (it->type() == TokenType::NewLine) { + readNewLine(it, end); + } else if (it->type() == TokenType::Comment) { + readComment(it, end); + } else if (it->type() == TokenType::Word) { + section.push_back(readOption(it, end)); + } else { + throw IniError(it->line(), it->position(), "unexpected token " + it->toString()); + } + } + + return section; + } + + IniOption readOption(TokenStack::iterator &it, TokenStack::iterator end) + { + std::string key = it->value(); + + if (++it == end) { + throw IniError(it->line(), it->position(), "expected '=' after option declaration, got <EOF>"); + } + + readSpace(it, end); + + if (it == end || it->type() != TokenType::Assign) { + throw IniError(it->line(), it->position(), "expected '=' after option declaration, got " + it++->toString()); + } + + readSpace(++it, end); + + std::ostringstream oss; + + if (it->type() == TokenType::QuoteSimple || it->type() == TokenType::QuoteDouble) { + TokenStack::iterator save = it++; + + while (it != end && it->type() != save->type()) { + oss << it++->value(); + } + + if (it == end) + throw IniError(save->line(), save->position(), "undisclosed quote: " + save->toString() + " expected"); + + ++ it; + } else if (it->type() == TokenType::Word) { + oss << it++->value(); + } else if (it->type() != TokenType::NewLine && it->type() != TokenType::Comment) { + // No value requested, must be NewLine or comment + throw IniError(it->line(), it->position(), "expected option value after '=', got " + it->toString()); + } + + + return IniOption(std::move(key), oss.str()); + } + + void readInclude(TokenStack::iterator &it, TokenStack::iterator end) + { + if (++it == end || (it->type() != TokenType::Word || it->value() != "include")) { + throw IniError(it->line(), it->position(), "expected `include' after '@' token, got " + it->toString()); + } + + readSpace(++it, end); + + // Quotes mandatory + TokenStack::iterator save = it; + + if (it == end || (it->type() != TokenType::QuoteSimple && it->type() != TokenType::QuoteDouble)) { + throw IniError(it->line(), it->position(), "expected filename after @include statement"); + } + + // Filename + if (++it == end || it->type() != TokenType::Word) { + throw IniError(it->line(), it->position(), "expected filename after @include statement"); + } + + std::string value = it->value(); + std::string fullpath; + + if (isAbsolute(value)) { + fullpath = value; + } else { + fullpath = m_base + "/" + it->value(); + } + + // Must be closed with the same quote + if (++it == end || it->type() != save->type()) { + throw IniError(save->line(), save->position(), "undiclosed quote: " + save->toString() + " expected"); + } + + // Remove quote + ++ it; + + IniBuilder(m_ini, fullpath); + } + +public: + IniBuilder(Ini &ini, std::string path) + : m_path(path) + , m_base(base(std::move(path))) + , m_ini(ini) + { + std::ifstream file(m_path); + + if (!file.is_open()) + throw std::runtime_error(std::strerror(errno)); + + std::vector<Token> ts = analyze(file); + + auto it = ts.begin(); + auto end = ts.end(); + + while (it != end) { + if (it->type() == TokenType::Space) { + readSpace(it, end); + } else if (it->type() == TokenType::NewLine) { + readNewLine(it, end); + } else if (it->type() == TokenType::Comment) { + readComment(it, end); + } else if (it->type() == TokenType::Include) { + readInclude(it, end); + } else if (it->type() == TokenType::SectionBegin) { + m_ini.push_back(readSection(it, end)); + } else { + throw IniError(it->line(), it->position(), "unexpected " + it->toString() + " on root document"); + } + } + } +}; + +} // !namespace + +/* -------------------------------------------------------- + * Ini + * -------------------------------------------------------- */ + +Ini::Ini(const std::string &path) +{ + IniBuilder(*this, path); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/C++/modules/Ini/Ini.h Sun Mar 08 14:26:33 2015 +0100 @@ -0,0 +1,494 @@ +/* + * Ini.h -- .ini file parsing + * + * Copyright (c) 2013, 2014 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _INI_H_ +#define _INI_H_ + +/** + * @file Ini.h + * @brief Configuration file parser + */ + +#include <algorithm> +#include <deque> +#include <stdexcept> +#include <string> + +/** + * @class IniError + * @brief Error in a file + */ +class IniError : public std::exception { +private: + int m_line; + int m_position; + std::string m_error; + +public: + /** + * Construct an error. + * + * @param line the line + * @param position the position + * @param error the error + */ + inline IniError(int line, int position, std::string error) + : m_line(line) + , m_position(position) + , m_error(std::move(error)) + { + } + + /** + * Return the line number. + * + * @return the line + */ + inline int line() const noexcept + { + return m_line; + } + + /** + * Return the position in the current line. + * + * @return the position + */ + inline int position() const noexcept + { + return m_position; + } + + /** + * Get the error string. + * + * @return the string + */ + inline const char *what() const noexcept + { + return m_error.c_str(); + } +}; + +/** + * @class IniOption + * @brief Option definition + */ +class IniOption { +private: + std::string m_key; + std::string m_value; + +public: + /** + * Construct an option. + * + * @param key the key + * @param value the value + */ + inline IniOption(std::string key, std::string value) + : m_key(std::move(key)) + , m_value(std::move(value)) + { + } + + /** + * Get the option key. + * + * @return the key + */ + inline const std::string &key() const noexcept + { + return m_key; + } + + /** + * Get the option value. + * + * @return the value + */ + inline const std::string &value() const noexcept + { + return m_value; + } +}; + +/** + * @class IniSection + * @brief Section that contains one or more options + */ +class IniSection { +private: + std::string m_key; + std::deque<IniOption> m_options; + + template <typename T> + T find(const std::string &key) const + { + auto it = std::find_if(m_options.begin(), m_options.end(), [&] (const IniOption &o) { + return o.key() == key; + }); + + if (it == m_options.end()) + throw std::out_of_range("option " + key + " not found"); + + return const_cast<T>(*it); + } + +public: + /** + * Default constructor has no sections and no values. + */ + IniSection() = default; + + /** + * Construct a section with a set of options. + * + * @param key the section name + * @param options the list of options + */ + inline IniSection(std::string key, std::deque<IniOption> options = {}) noexcept + : m_key(std::move(key)) + , m_options(std::move(options)) + { + } + + /** + * Get the section key. + * + * @return the key + */ + inline const std::string &key() const noexcept + { + return m_key; + } + + /** + * Get an iterator to the beginning. + * + * @return the iterator + */ + inline auto begin() noexcept + { + return m_options.begin(); + } + + /** + * Overloaded function. + * + * @return the iterator + */ + inline auto begin() const noexcept + { + return m_options.begin(); + } + + /** + * Overloaded function. + * + * @return the iterator + */ + inline auto cbegin() const noexcept + { + return m_options.cbegin(); + } + + /** + * Get an iterator to the end. + * + * @return the iterator + */ + inline auto end() noexcept + { + return m_options.end(); + } + + /** + * Overloaded function. + * + * @return the iterator + */ + inline auto end() const noexcept + { + return m_options.end(); + } + + /** + * Overloaded function. + * + * @return the iterator + */ + inline auto cend() const noexcept + { + return m_options.cend(); + } + + /** + * Append an option. + * + * @param option the option to add + */ + inline void push_back(IniOption option) + { + m_options.push_back(std::move(option)); + } + + /** + * Push an option to the beginning. + * + * @param option the option to add + */ + inline void push_front(IniOption option) + { + m_options.push_front(std::move(option)); + } + + /** + * Get the number of options in that section. + * + * @return the size + */ + inline unsigned size() const noexcept + { + return m_options.size(); + } + + /** + * Access an option at the specified index. + * + * @param index the index + * @return the option + * @warning No bounds checking is performed + */ + inline IniOption &operator[](int index) noexcept + { + return m_options[index]; + } + + /** + * Access an option at the specified index. + * + * @param index the index + * @return the option + * @warning No bounds checking is performed + */ + inline const IniOption &operator[](int index) const noexcept + { + return m_options[index]; + } + + /** + * Access an option at the specified key. + * + * @param key the key + * @return the option + * @warning No bounds checking is performed + */ + inline IniOption &operator[](const std::string &key) + { + return find<IniOption &>(key); + } + + /** + * Access an option at the specified key. + * + * @param key the key + * @return the option + * @warning No bounds checking is performed + */ + inline const IniOption &operator[](const std::string &key) const + { + return find<const IniOption &>(key); + } +}; + +/** + * @class Ini + * @brief Ini config file loader + */ +class Ini { +private: + std::deque<IniSection> m_sections; + + template <typename T> + T find(const std::string &key) const + { + auto it = std::find_if(m_sections.begin(), m_sections.end(), [&] (const IniSection &s) { + return s.key() == key; + }); + + if (it == m_sections.end()) + throw std::out_of_range("section " + key + " not found"); + + return const_cast<T>(*it); + } + +public: + /** + * Default constructor with an empty configuration. + */ + Ini() = default; + + /** + * Open the path as the configuration file. + * + * @param path the path + * @throw IniError on any error + */ + Ini(const std::string &path); + + /** + * Get an iterator to the beginning. + * + * @return the iterator + */ + inline auto begin() noexcept + { + return m_sections.begin(); + } + + /** + * Overloaded function. + * + * @return the iterator + */ + inline auto begin() const noexcept + { + return m_sections.begin(); + } + + /** + * Overloaded function. + * + * @return the iterator + */ + inline auto cbegin() const noexcept + { + return m_sections.cbegin(); + } + + /** + * Get an iterator to the end. + * + * @return the iterator + */ + inline auto end() noexcept + { + return m_sections.end(); + } + + /** + * Overloaded function. + * + * @return the iterator + */ + inline auto end() const noexcept + { + return m_sections.end(); + } + + /** + * Overloaded function. + * + * @return the iterator + */ + inline auto cend() const noexcept + { + return m_sections.cend(); + } + + /** + * Get the number of sections in the configuration. + * + * @return the size + */ + inline unsigned size() const noexcept + { + return m_sections.size(); + } + + /** + * Append a section to the end. + * + * @param section the section to add + */ + inline void push_back(IniSection section) + { + m_sections.push_back(std::move(section)); + } + + /** + * Add a section to the beginning. + * + * @param section the section to add + */ + inline void push_front(IniSection section) + { + m_sections.push_front(std::move(section)); + } + + /** + * Access a section at the specified index. + * + * @param index the index + * @return the section + * @warning No bounds checking is performed + */ + inline IniSection &operator[](int index) noexcept + { + return m_sections[index]; + } + + /** + * Access a section at the specified index. + * + * @param index the index + * @return the section + * @warning No bounds checking is performed + */ + inline const IniSection &operator[](int index) const noexcept + { + return m_sections[index]; + } + + /** + * Access a section at the specified key. + * + * @param key the key + * @return the section + * @warning No bounds checking is performed + */ + inline IniSection &operator[](const std::string &key) + { + return find<IniSection &>(key); + } + + /** + * Access a section at the specified key. + * + * @param key the key + * @return the section + * @warning No bounds checking is performed + */ + inline const IniSection &operator[](const std::string &key) const + { + return find<IniSection &>(key); + } +}; + +#endif // !_INI_H_
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/C++/modules/Json/Json.cpp Sun Mar 08 14:26:33 2015 +0100 @@ -0,0 +1,158 @@ +/* + * Json.cpp -- jansson C++11 wrapper + * + * Copyright (c) 2013, 2014 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <stdexcept> + +#include "Json.h" + +/* -------------------------------------------------------- + * JsonObject + * -------------------------------------------------------- */ + +JsonObject JsonValue::toObject() const noexcept +{ + json_incref(m_handle.get()); + + return JsonObject(m_handle.get()); +} + +JsonArray JsonValue::toArray() const noexcept +{ + json_incref(m_handle.get()); + + return JsonArray(m_handle.get()); +} + +/* -------------------------------------------------------- + * JsonArray + * -------------------------------------------------------- */ + +JsonValue JsonArray::at(int index) const +{ + auto value = json_array_get(m_handle.get(), index); + + if (value == nullptr) + throw JsonError("index out of bounds"); + + json_incref(value); + + return JsonValue{value}; +} + +JsonValue JsonArray::operator[](int index) const noexcept +{ + auto value = json_array_get(m_handle.get(), index); + + if (value == nullptr) + return JsonValue(); + + json_incref(value); + + return JsonValue(value); +} + +JsonArray::Ref JsonArray::operator[](int index) noexcept +{ + auto value = json_array_get(m_handle.get(), index); + + if (value == nullptr) + value = json_null(); + else + json_incref(value); + + return Ref(value, *this, index); +} + +/* -------------------------------------------------------- + * JsonObject + * -------------------------------------------------------- */ + +JsonObject::Ref JsonObject::operator[](const std::string &name) +{ + if (typeOf() != JsonType::Object) + return Ref(JsonValue(), *this, name); + + auto value = json_object_get(m_handle.get(), name.c_str()); + + json_incref(value); + + return Ref(value, *this, name); +} + +JsonValue JsonObject::operator[](const std::string &name) const +{ + if (typeOf() != JsonType::Object) + return JsonValue(); + + auto value = json_object_get(m_handle.get(), name.c_str()); + + if (value == nullptr) + return JsonValue(); + + json_incref(value); + + return JsonValue(value); +} + +/* -------------------------------------------------------- + * JsonDocument + * -------------------------------------------------------- */ + +JsonValue JsonDocument::read(std::string content, int flags) const +{ + json_error_t error; + json_t *json = json_loads(content.c_str(), flags, &error); + + if (json == nullptr) + throw JsonError(error); + + return JsonValue(json); +} + +JsonValue JsonDocument::read(std::ifstream &stream, int flags) const +{ + if (!stream.is_open()) + throw JsonError("File not opened"); + + stream.seekg(0, stream.end); + auto length = stream.tellg(); + stream.seekg(0, stream.beg); + + std::string buffer; + buffer.resize(length, ' '); + + stream.read(&buffer[0], length); + stream.close(); + + return read(std::move(buffer), flags); +} + +JsonDocument::JsonDocument(std::ifstream &stream, int flags) +{ + m_value = read(stream, flags); +} + +JsonDocument::JsonDocument(std::ifstream &&stream, int flags) +{ + m_value = read(stream, flags); +} + +JsonDocument::JsonDocument(std::string content, int flags) +{ + m_value = read(std::move(content), flags); +} \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/C++/modules/Json/Json.h Sun Mar 08 14:26:33 2015 +0100 @@ -0,0 +1,1603 @@ +/* + * Json.h -- jansson C++11 wrapper + * + * Copyright (c) 2013, 2014 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _JSON_H_ +#define _JSON_H_ + +#include <algorithm> +#include <cerrno> +#include <cstdlib> +#include <cstring> +#include <initializer_list> +#include <fstream> +#include <iterator> +#include <memory> +#include <string> +#include <utility> + +#include <jansson.h> + +/** + * @file Json.h + * @brief A jansson C++ modern wrapper + * + * Because of the Jansson implementation, all these classes are implicitly + * shared. + * + * This means that you can't set any value to an existing value as it would + * change a value which may be used somewhere else, instead you must set + * or replace elements in JsonObject and JsonArray respectively. + * + * However, copy constructors are implemented as deep copy so take care of + * not copying values mistakenly. + */ + +/** + * @class JsonType + * @brief Json value type + */ +enum class JsonType { + Object = JSON_OBJECT, //!< Object + Array = JSON_ARRAY, //!< Array + String = JSON_STRING, //!< String + Integer = JSON_INTEGER, //!< Integer + Real = JSON_REAL, //!< Floating point + True = JSON_TRUE, //!< Boolean true + False = JSON_FALSE, //!< Boolean false + Null = JSON_NULL //!< Empty or null +}; + +/** + * @class JsonError + * @brief Error thrown for any error + */ +class JsonError final : public std::exception { +private: + std::string m_text; + std::string m_source; + int m_line{}; + int m_column{}; + int m_position{}; + +public: + /** + * Custom error with no line, no column and no position. + * + * @param error the error message + */ + inline JsonError(std::string error) + : m_text(std::move(error)) + { + } + + /** + * Error from a json_error_t. + * + * @param error the error + */ + inline JsonError(const json_error_t &error) + : m_text(error.text) + , m_source(error.source) + , m_line(error.line) + , m_column(error.column) + , m_position(error.position) + { + } + + /** + * Get the error message. + * + * @return the message + */ + const char *what() const noexcept override + { + return m_text.c_str(); + } + + /** + * Get the text message. + * + * @return the text + */ + inline const std::string &text() const noexcept + { + return m_text; + } + + /** + * Get the source. + * + * @return the source + */ + inline const std::string &source() const noexcept + { + return m_source; + } + + /** + * Get the line. + * + * @return the line + */ + inline int line() const noexcept + { + return m_line; + } + + /** + * Get the column. + * + * @return the column + */ + inline int column() const noexcept + { + return m_column; + } + + /** + * Get the position. + * + * @return the position + */ + inline int position() const noexcept + { + return m_position; + } +}; + +class JsonObject; +class JsonArray; + +/** + * @class JsonValue + * @brief Encapsulate any JSON value + */ +class JsonValue { +public: + using Handle = std::unique_ptr<json_t, void (*)(json_t *)>; + + friend class JsonObject; + friend class JsonArray; + +protected: + /** + * The unique_ptr handle of json_t, will automatically decrease + * the reference count in its deleter. + */ + Handle m_handle; + + inline void check() const + { + if (m_handle == nullptr) + throw JsonError(std::strerror(errno)); + } + +public: + /** + * Deep copy of that element. + * + * @param value the other value + * @throw JsonError on allocation error + */ + inline JsonValue(const JsonValue &value) + : m_handle(json_deep_copy(value.m_handle.get()), json_decref) + { + check(); + } + + /** + * Assign a deep copy of the other element. + * + * @return *this + * @throw JsonError on allocation error + */ + inline JsonValue &operator=(const JsonValue &value) + { + m_handle = Handle(json_deep_copy(value.m_handle.get()), json_decref); + + check(); + + return *this; + } + + /** + * Move constructor, the other value is left empty (JsonType::Null). + * + * @param other the other value + */ + inline JsonValue(JsonValue &&other) noexcept + : m_handle(std::move(other.m_handle)) + { + other.m_handle = Handle(json_null(), json_decref); + } + + /** + * Move assignment, the other value is left empty (JsonType::Null). + * + * @param other the other value + */ + inline JsonValue &operator=(JsonValue &&other) noexcept + { + m_handle = std::move(other.m_handle); + other.m_handle = Handle(json_null(), json_decref); + + return *this; + } + + /** + * Create a JsonValue from a native Jansson type. This function + * will increment the json_t reference count. + * + * @param json the value + */ + inline JsonValue(json_t *json) noexcept + : m_handle(json, json_decref) + { + } + + /** + * Construct a null value from a nullptr argument. + */ + inline JsonValue(std::nullptr_t) noexcept + : m_handle(json_null(), json_decref) + { + } + + /** + * Create an empty value (JsonType::Null). + */ + inline JsonValue() noexcept + : m_handle(json_null(), json_decref) + { + } + + /** + * Create a boolean value. + * + * @param value the value + */ + inline JsonValue(bool value) noexcept + : m_handle(json_boolean(value), json_decref) + { + } + + /** + * Create a integer value (JsonType::Integer). + * + * @param value the value + * @throw JsonError on allocation error + */ + inline JsonValue(int value) + : m_handle(json_integer(value), json_decref) + { + check(); + } + + /** + * Create a real value (JsonType::Real). + * + * @param value the value + * @throw JsonError on allocation error + */ + inline JsonValue(double value) + : m_handle(json_real(value), json_decref) + { + check(); + } + + /** + * Create a string value (JsonType::String). + * + * @param value the value + * @throw JsonError on allocation error + */ + inline JsonValue(std::string value) + : m_handle(json_string(value.c_str()), json_decref) + { + check(); + } + + /** + * Create from a C string (JsonType::String). + * + * @param value the string + * @throw JsonError on allocation error + */ + inline JsonValue(const char *value) + : m_handle(json_string(value), json_decref) + { + check(); + } + + /** + * Create from a string literal (JsonType::String). + * + * @param value the value + * @throw JsonError on allocation error + */ + template <size_t Size> + inline JsonValue(char (&value)[Size]) + : m_handle(json_string(value), json_decref) + { + check(); + } + + /** + * Default destructor. + */ + virtual ~JsonValue() = default; + + /** + * Get the type of value. + * + * @return the type + */ + inline JsonType typeOf() const noexcept + { + return static_cast<JsonType>(json_typeof(m_handle.get())); + } + + /** + * Tells if the json value is an JSON_OBJECT. + * + * @return true or false + */ + inline bool isObject() const noexcept + { + return json_is_object(m_handle.get()); + } + + /** + * Tells if the json value is an JSON_ARRAY. + * + * @return true or false + */ + inline bool isArray() const noexcept + { + return json_is_array(m_handle.get()); + } + + /** + * Tells if the json value is an JSON_STRING. + * + * @return true or false + */ + inline bool isString() const noexcept + { + return json_is_string(m_handle.get()); + } + + /** + * Tells if the json value is an JSON_REAL. + * + * @return true or false + */ + inline bool isReal() const noexcept + { + return json_is_real(m_handle.get()); + } + + /** + * Tells if the json value is an JSON_TRUE. + * + * @return true or false + */ + inline bool isTrue() const noexcept + { + return json_is_true(m_handle.get()); + } + + /** + * Tells if the json value is an JSON_FALSE. + * + * @return true or false + */ + inline bool isFalse() const noexcept + { + return json_is_false(m_handle.get()); + } + + /** + * Tells if the json value is an JSON_NULL. + * + * @return true or false + */ + inline bool isNull() const noexcept + { + return json_is_null(m_handle.get()); + } + + /** + * Tells if the json value is an JSON_INTEGER or JSON_REAL. + * + * @return true or false + */ + inline bool isNumber() const noexcept + { + return json_is_number(m_handle.get()); + } + + /** + * Tells if the json value is an JSON_INTEGER. + * + * @return true or false + */ + inline bool isInteger() const noexcept + { + return json_is_integer(m_handle.get()); + } + + /** + * Tells if the json value is an JSON_TRUE or JSON_FALSE. + * + * @return true or false + */ + inline bool isBoolean() const noexcept + { + return json_is_boolean(m_handle.get()); + } + + /** + * Get the string value. + * + * @return the string + */ + inline std::string toString() const noexcept + { + auto value = json_string_value(m_handle.get()); + + return (value == nullptr) ? "" : value; + } + + /** + * Get the integer value. + * + * @return the value or 0 + */ + inline int toInteger() const noexcept + { + return json_integer_value(m_handle.get()); + } + + /** + * Get the real value. + * + * @return the value or 0 + */ + inline double toReal() const noexcept + { + return json_real_value(m_handle.get()); + } + + /** + * Convert to object. + * + * @return an object + */ + JsonObject toObject() const noexcept; + + /** + * Convert to array. + * + * @return an array + */ + JsonArray toArray() const noexcept; + + /** + * Write to a stream. + * + * @param out the out + * @param flags the optional Jansson flags + */ + inline void write(std::ofstream &out, int flags = 0) const + { + auto content = dump(flags); + + std::copy(std::begin(content), std::end(content), std::ostreambuf_iterator<char>(out)); + } + + /** + * Overloaded function. + * + * @param out the out + * @param flags the optional Jansson flags + */ + inline void write(std::ofstream &&out, int flags = 0) const + { + write(out, flags); + } + + /** + * Convert the Json value as a string. + * + * @return the string + * @param flags the optional Jansson flags + */ + inline std::string dump(int flags = 0) const + { + auto str = json_dumps(m_handle.get(), flags); + + if (str == nullptr) + return ""; + + std::string ret(str); + std::free(str); + + return ret; + } + + /** + * Convert to native Jansson type. + * + * You should not call json_incref or json_decref on it as it is + * automatically done. + * + * @return the json_t handle + * @warning use this function with care + */ + inline operator json_t *() noexcept + { + return m_handle.get(); + } + + /** + * Overloaded function. + * + * @return the json_t handle + */ + inline operator const json_t *() const noexcept + { + return m_handle.get(); + } + + /** + * Equality operator. + */ + inline bool operator==(const JsonValue &other) const noexcept + { + return json_equal(m_handle.get(), other.m_handle.get()); + } +}; + +/** + * @class JsonArray + * @brief Manipulate JSON arrays + */ +class JsonArray final : public JsonValue { +public: + /** + * @class Ref + * @brief Reference wrapper to be assignable + */ + class Ref final : public JsonValue { + private: + JsonArray &m_array; + int m_index; + + public: + explicit inline Ref(JsonValue value, JsonArray &array, int index) + : JsonValue(std::move(value)) + , m_array(array) + , m_index(index) + { + } + + inline operator JsonValue() const noexcept + { + return *this; + } + + inline JsonValue &operator*() noexcept + { + return *this; + } + + inline JsonValue *operator->() noexcept + { + return this; + } + + inline Ref &operator=(const JsonValue &value) + { + m_array.replace(value, m_index); + + return *this; + } + + inline Ref &operator=(JsonValue &&value) + { + m_array.replace(std::move(value), m_index); + + return *this; + } + }; + + /** + * @class Ptr + * @brief Pointer wrapper for JsonValue iterators + */ + class Ptr final : public JsonValue { + public: + explicit Ptr(JsonValue value) noexcept + : JsonValue(std::move(value)) + { + } + + inline JsonValue &operator*() noexcept + { + return *this; + } + + inline JsonValue *operator->() noexcept + { + return this; + } + }; + + class iterator final { + public: + using iterator_category = std::random_access_iterator_tag; + using difference_type = int; + using value_type = JsonValue; + using reference = Ref; + using pointer = Ptr; + + friend class JsonArray; + + private: + JsonArray &m_array; + int m_index; + + public: + explicit inline iterator(JsonArray &array, int index = 0) noexcept + : m_array(array) + , m_index(index) + { + } + + inline Ref operator*() const + { + return Ref(m_array.at(m_index), m_array, m_index); + } + + inline Ptr operator->() const + { + return Ptr(m_array.at(m_index)); + } + + inline Ref operator[](int nindex) const noexcept + { + return Ref(m_array.at(m_index + nindex), m_array, m_index + nindex); + } + + inline bool operator==(const iterator &other) const noexcept + { + return m_index == other.m_index; + } + + inline bool operator!=(const iterator &other) const noexcept + { + return m_index != other.m_index; + } + + inline bool operator<(const iterator &other) const noexcept + { + return m_index < other.m_index; + } + + inline bool operator<=(const iterator &other) const noexcept + { + return m_index <= other.m_index; + } + + inline bool operator>(const iterator &other) const noexcept + { + return m_index > other.m_index; + } + + inline bool operator>=(const iterator &other) const noexcept + { + return m_index >= other.m_index; + } + + inline iterator &operator++() noexcept + { + ++m_index; + + return *this; + } + + inline iterator operator++(int) noexcept + { + iterator save = *this; + + ++m_index; + + return save; + } + + inline iterator &operator--() noexcept + { + m_index--; + + return *this; + } + + inline iterator operator--(int) noexcept + { + iterator save = *this; + + m_index--; + + return save; + } + + inline iterator &operator+=(int nindex) noexcept + { + m_index += nindex; + + return *this; + } + + inline iterator &operator-=(int nindex) noexcept + { + m_index -= nindex; + + return *this; + } + + inline iterator operator+(int nindex) const noexcept + { + return iterator(m_array, m_index + nindex); + } + + inline iterator operator-(int nindex) const noexcept + { + return iterator(m_array, m_index - nindex); + } + + inline int operator-(iterator other) const noexcept + { + return m_index - other.m_index; + } + }; + + class const_iterator final { + public: + using iterator_category = std::random_access_iterator_tag; + using difference_type = int; + using value_type = JsonValue; + using reference = JsonValue; + using pointer = Ptr; + + friend class JsonArray; + + private: + const JsonArray &m_array; + int m_index; + + public: + explicit inline const_iterator(const JsonArray &array, int index = 0) noexcept + : m_array(array) + , m_index(index) + { + } + + inline JsonValue operator*() const + { + return m_array.at(m_index); + } + + inline Ptr operator->() const + { + return Ptr(m_array.at(m_index)); + } + + inline JsonValue operator[](int nindex) const noexcept + { + return m_array.at(m_index + nindex); + } + + inline bool operator==(const const_iterator &other) const noexcept + { + return m_index == other.m_index; + } + + inline bool operator!=(const const_iterator &other) const noexcept + { + return m_index != other.m_index; + } + + inline bool operator<(const const_iterator &other) const noexcept + { + return m_index < other.m_index; + } + + inline bool operator<=(const const_iterator &other) const noexcept + { + return m_index <= other.m_index; + } + + inline bool operator>(const const_iterator &other) const noexcept + { + return m_index > other.m_index; + } + + inline bool operator>=(const const_iterator &other) const noexcept + { + return m_index >= other.m_index; + } + + inline const_iterator &operator++() noexcept + { + ++m_index; + + return *this; + } + + inline const_iterator operator++(int) noexcept + { + const_iterator save = *this; + + ++m_index; + + return save; + } + + inline const_iterator &operator--() noexcept + { + m_index--; + + return *this; + } + + inline const_iterator operator--(int) noexcept + { + const_iterator save = *this; + + m_index--; + + return save; + } + + inline const_iterator &operator+=(int nindex) noexcept + { + m_index += nindex; + + return *this; + } + + inline const_iterator &operator-=(int nindex) noexcept + { + m_index -= nindex; + + return *this; + } + + inline const_iterator operator+(int nindex) const noexcept + { + return const_iterator(m_array, m_index + nindex); + } + + inline const_iterator operator-(int nindex) const noexcept + { + return const_iterator(m_array, m_index - nindex); + } + + inline int operator-(const_iterator other) const noexcept + { + return m_index - other.m_index; + } + }; + + using size_type = int; + using value_type = JsonValue; + using const_pointer = const value_type *; + using reference = JsonValue &; + using const_reference = const JsonValue &; + using difference_type = int; + +protected: + using JsonValue::JsonValue; + +public: + /** + * Create an empty array. + * + * @throw JsonError on allocation error + */ + inline JsonArray() + : JsonValue(json_array()) + { + check(); + } + + /** + * Create an array from a list of values. + * + * @param list the list + * @throw JsonError on allocation error + */ + inline JsonArray(std::initializer_list<value_type> list) + : JsonArray() + { + for (auto &v : list) + append(std::move(v)); + } + + /** + * Returns an iterator to the beginning. + * + * @return the iterator + */ + inline iterator begin() noexcept + { + return iterator(*this, 0); + } + + /** + * Overloaded function. + * + * @return the iterator + */ + inline const_iterator begin() const noexcept + { + return const_iterator(*this, 0); + } + + /** + * Overloaded function. + * + * @return the iterator + */ + inline const_iterator cbegin() const noexcept + { + return const_iterator(*this, 0); + } + + /** + * Returns an iterator to the end. + * + * @return the iterator + */ + inline iterator end() noexcept + { + return iterator(*this, size()); + } + + /** + * Overloaded function. + * + * @return the iterator + */ + inline const_iterator end() const noexcept + { + return const_iterator(*this, size()); + } + + /** + * Overloaded function. + * + * @return the iterator + */ + inline const_iterator cend() const noexcept + { + return const_iterator(*this, size()); + } + + /** + * Get a value. + * + * @param index the index + * @throw JsonError on error + */ + JsonValue at(int index) const; + + /** + * Erase the array content. + */ + inline void clear() noexcept + { + json_array_clear(m_handle.get()); + } + + /** + * Remove the element at the specified index. + * + * @param index the index + */ + inline void erase(int index) noexcept + { + json_array_remove(m_handle.get(), index); + } + + /** + * Overloaded function. + * + * @param it the iterator + */ + inline void erase(iterator it) noexcept + { + erase(it.m_index); + } + + /** + * Overloaded function. + * + * @param it the iterator + */ + inline void erase(const_iterator it) noexcept + { + erase(it.m_index); + } + + /** + * Get the number of values in the array + * + * @return the number or 0 + */ + inline unsigned size() const noexcept + { + return json_array_size(m_handle.get()); + } + + /** + * Insert the value at the beginning. + * + * @param value the value + */ + inline void push(const JsonValue &value) noexcept + { + json_array_insert(m_handle.get(), 0, value.m_handle.get()); + } + + /** + * Insert a copy of the value at the end. + * + * @param value the value to insert + */ + inline void append(const JsonValue &value) + { + json_array_append(m_handle.get(), value.m_handle.get()); + } + + /** + * Insert a copy of the value at the specified index. + * + * @param value the value to insert + * @param index the position + */ + inline void insert(const JsonValue &value, int index) + { + json_array_insert(m_handle.get(), index, value.m_handle.get()); + } + + /** + * Replace the value at the given index. + * + * @param value the value + * @param index the index + */ + inline void replace(const JsonValue &value, int index) + { + json_array_set(m_handle.get(), index, value.m_handle.get()); + } + + /** + * Get the value at the specified index. + * + * @param index the position + * @return the value + */ + JsonValue operator[](int index) const noexcept; + + /** + * Access a value as a reference wrapper. + * + * @param index the position + * @return the reference wrapper over the value + */ + Ref operator[](int index) noexcept; +}; + +/** + * @class JsonObject + * @brief Object wrapper + */ +class JsonObject final : public JsonValue { +public: + using key_type = std::string; + using mapped_type = JsonValue; + using size_type = int; + using value_type = std::pair<key_type, mapped_type>; + using const_pointer = const value_type *; + using reference = JsonValue &; + using const_reference = const JsonValue &; + using difference_type = int; + + /** + * @class Ref + * @brief Wrapper for updating JsonObject + * + * This class is only used for the following functions: + * + * JsonObject::operator[] + */ + class Ref final : public JsonValue { + private: + JsonObject &m_object; + std::string m_key; + + public: + explicit inline Ref(JsonValue value, JsonObject &object, std::string key) + : JsonValue(std::move(value)) + , m_object(object) + , m_key(std::move(key)) + { + } + + inline operator JsonValue() const noexcept + { + return *this; + } + + inline JsonValue &operator*() noexcept + { + return *this; + } + + inline JsonValue *operator->() noexcept + { + return this; + } + + inline Ref &operator=(const JsonValue &value) + { + m_object.set(m_key, value); + + return *this; + } + + inline Ref &operator=(JsonValue &&value) + { + m_object.set(m_key, std::move(value)); + + return *this; + } + }; + + /** + * @class Ptr + * @brief Pointer wrapper for JsonValue iterators + * + * For const iterators, the real type is a JsonValue, for non const + * iterators it's a ref so that user can edit it. + */ + template <typename Type> + class Ptr final { + private: + std::pair<std::string, Type> m_pair; + + public: + inline Ptr(std::pair<std::string, Type> value) noexcept + : m_pair(std::move(value)) + { + } + + inline std::pair<std::string, Type> &operator*() noexcept + { + return m_pair; + } + + inline std::pair<std::string, Type> *operator->() noexcept + { + return &m_pair; + } + }; + + /** + * @class iterator + * @brief Forward iterator + */ + class iterator { + public: + using value_type = std::pair<std::string, Ref>; + + friend class JsonObject; + + private: + JsonObject &m_object; + void *m_keyIt; + + inline std::string key() const noexcept + { + return json_object_iter_key(m_keyIt); + } + + public: + explicit inline iterator(JsonObject &object, void *keyIt) noexcept + : m_object(object) + , m_keyIt(keyIt) + { + } + + inline value_type operator*() const + { + auto k = key(); + + return value_type(k, Ref(m_object[k], m_object, k)); + } + + inline Ptr<Ref> operator->() const + { + auto k = key(); + + return Ptr<Ref>({k, Ref(m_object[k], m_object, k)}); + } + + inline iterator &operator++() noexcept + { + m_keyIt = json_object_iter_next(m_object.m_handle.get(), m_keyIt); + + return *this; + } + + inline iterator operator++(int) noexcept + { + iterator save = *this; + + m_keyIt = json_object_iter_next(m_object.m_handle.get(), m_keyIt); + + return save; + } + + inline bool operator==(iterator other) const noexcept + { + return m_keyIt == other.m_keyIt; + } + + inline bool operator!=(iterator other) const noexcept + { + return m_keyIt != other.m_keyIt; + } + }; + + /** + * @class const_iterator + * @brief Forward iterator + */ + class const_iterator { + public: + using value_type = std::pair<std::string, JsonValue>; + + friend class JsonObject; + + private: + const JsonObject &m_object; + void *m_keyIt; + + inline std::string key() const noexcept + { + return json_object_iter_key(m_keyIt); + } + + public: + explicit inline const_iterator(const JsonObject &object, void *keyIt) noexcept + : m_object(object) + , m_keyIt(keyIt) + { + } + + inline value_type operator*() const + { + auto k = key(); + + return value_type(k, m_object[k]); + } + + inline Ptr<JsonValue> operator->() const + { + auto k = key(); + + return Ptr<JsonValue>({k, m_object[k]}); + } + + inline const_iterator &operator++() noexcept + { + m_keyIt = json_object_iter_next(m_object.m_handle.get(), m_keyIt); + + return *this; + } + + inline const_iterator operator++(int) noexcept + { + const_iterator save = *this; + + m_keyIt = json_object_iter_next(m_object.m_handle.get(), m_keyIt); + + return save; + } + + inline bool operator==(const_iterator other) const noexcept + { + return m_keyIt == other.m_keyIt; + } + + inline bool operator!=(const_iterator other) const noexcept + { + return m_keyIt != other.m_keyIt; + } + }; + +protected: + using JsonValue::JsonValue; + +public: + /** + * Create empty object. + * + * @throw JsonError on allocation error + */ + inline JsonObject() + : JsonValue(json_object()) + { + check(); + } + + /** + * Create a JsonObject from an initializer_list. + * + * @param list the list of key-value pairs + * @throw JsonError on allocation error + */ + inline JsonObject(std::initializer_list<value_type> list) + : JsonObject() + { + for (auto &v : list) + set(v.first, std::move(v.second)); + } + + /** + * Returns an iterator to the beginning. + * + * @return the iterator + */ + inline iterator begin() noexcept + { + return iterator(*this, json_object_iter(m_handle.get())); + } + + /** + * Overloaded function. + * + * @return the iterator + */ + inline const_iterator begin() const noexcept + { + return const_iterator(*this, json_object_iter(m_handle.get())); + } + + /** + * Overloaded function. + * + * @return the iterator + */ + inline const_iterator cbegin() const noexcept + { + return const_iterator(*this, json_object_iter(m_handle.get())); + } + + /** + * Returns an iterator to the end. + * + * @return the iterator + */ + inline iterator end() noexcept + { + return iterator(*this, nullptr); + } + + /** + * Overloaded function. + * + * @return the iterator + */ + inline const_iterator end() const noexcept + { + return const_iterator(*this, nullptr); + } + + /** + * Overloaded function. + * + * @return the iterator + */ + inline const_iterator cend() const noexcept + { + return const_iterator(*this, nullptr); + } + + /** + * Check if the object contains a specific property. + * + * @param key the key + * @return true if the object contains the key + */ + inline bool contains(const std::string &key) const noexcept + { + return json_object_get(m_handle.get(), key.c_str()) != nullptr; + } + + /** + * Get the number of items in the object. + * + * @return the number of items + */ + inline unsigned size() const noexcept + { + return json_object_size(m_handle.get()); + } + + /** + * Remove all elements from the object. + */ + inline void clear() noexcept + { + json_object_clear(m_handle.get()); + } + + /** + * Remove element `key' if exists. + * + * @param key the key + */ + inline void erase(const std::string &key) noexcept + { + json_object_del(m_handle.get(), key.c_str()); + } + + /** + * Overloaded function. + * + * @param it the iterator + */ + inline void erase(iterator it) noexcept + { + erase(it.key()); + } + + /** + * Overloaded function. + * + * @param it the iterator + */ + inline void erase(const_iterator it) noexcept + { + erase(it.key()); + } + + /** + * Set the value as key in the object. + * + * @param key the key + * @param value the value + */ + inline void set(const std::string &key, const JsonValue &value) noexcept + { + json_object_set(m_handle.get(), key.c_str(), value.m_handle.get()); + } + + /** + * Access an object as a wrapper so that you can update its content + * with convenience. + * + * @param key the key + */ + Ref operator[](const std::string &key); + + /** + * Get the value at the specified key. If the value is not found, an + * empty value is returned + * + * @param key the key + * @return the value + */ + JsonValue operator[](const std::string &key) const; +}; + +/** + * @class JsonDocument + * @brief Read files and strings to create Json values + */ +class JsonDocument final { +private: + JsonValue m_value; + + JsonValue read(std::string, int flags) const; + JsonValue read(std::ifstream &stream, int flags) const; + +public: + /** + * Construct a document from a file. + * + * @param stream the stream + * @param flags the optional Jansson flags + * @throw JsonError on errors + */ + JsonDocument(std::ifstream &fstream, int flags = 0); + + /** + * Construct a document from a file. + * + * @param stream the stream + * @param flags the optional Jansson flags + * @throw JsonError on errors + */ + JsonDocument(std::ifstream &&stream, int flags = 0); + + /** + * Construct a document from a file. + * + * @param stream the stream + * @param flags the optional Jansson flags + * @throw JsonError on errors + */ + JsonDocument(std::string content, int flags = 0); + + /** + * Check if the document contains an object. + * + * @return true if object + */ + inline bool isObject() const noexcept + { + return m_value.isObject(); + } + + /** + * Check if the document contains an array. + * + * @return true if array + */ + inline bool isArray() const noexcept + { + return m_value.isArray(); + } + + /** + * Convert the document value to object + * + * @return the value as object + */ + inline JsonObject toObject() const noexcept + { + return m_value.toObject(); + } + + /** + * Convert the document value to array + * + * @return the value as array + */ + inline JsonArray toArray() const noexcept + { + return m_value.toArray(); + } +}; + +#endif // !_JSON_H_
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/C++/modules/OptionParser/OptionParser.cpp Sun Mar 08 14:26:33 2015 +0100 @@ -0,0 +1,211 @@ +/* + * OptionParser.cpp -- command line option parser + * + * Copyright (c) 2013, 2014 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <algorithm> + +#include "OptionParser.h" + +bool OptionParser::isShort(const std::string &arg) const +{ + return arg.size() >= 2 && arg[0] == '-' && arg[1] != '-'; +} + +bool OptionParser::isLong(const std::string &arg) const +{ + return arg.size() >= 3 && arg[0] == '-' && arg[1] == '-' && arg[2] != '-'; +} + +bool OptionParser::isOption(const std::string &arg) const +{ + return isShort(arg) || isLong(arg); +} + +std::string OptionParser::key(const std::string &arg) const +{ + if (isShort(arg)) + return arg.substr(1, 1); + + return arg.substr(2); +} + +bool OptionParser::isShortCompacted(const std::string &arg) const +{ + return arg.size() >= 3; +} + +bool OptionParser::isDefined(const std::string &arg) const +{ + auto n = key(arg); + auto it = std::find_if(m_options.begin(), m_options.end(), [&] (const Option &o) -> bool { + return o.key() == n || o.full() == n; + }); + + return it != m_options.end(); +} + +const Option &OptionParser::get(const std::string &arg) const +{ + std::string n = key(arg); + + return *std::find_if(m_options.begin(), m_options.end(), [&] (const Option &o) -> bool { + return o.key() == n || o.full() == n; + }); +} + +bool OptionParser::isToggle(const std::string &arg) const +{ + return (get(arg).flags() & Option::NoArg); +} + +void OptionParser::readShort(OptionPack &pack, Args::const_iterator &it, Args::const_iterator end) const +{ + /* + * There are many options when passing a short option: + * + * 1. -cmyconfig Takes on argument but parsed as unique, + * 2. -c myconfig Takes on argument but parsed as two strings + * 3. -abc If a is not a toggle option, its argument is `bc' + * 4. -abc If a is a toggle option and b, c are toggle, they are added + */ + + std::string v = it->substr(2); + std::string k = key(*it); + const Option &option = get(std::string("-") + k); + + if (isToggle(*it)) { + // 3. and optionally 4. + pack.push_back(OptionValue(option, "")); + pack.m_argsParsed += 1; + + if (isShortCompacted(*it)) { + for (char c : v) { + if (!isDefined("-" + std::string(1, c))) { + pack.m_error = "-" + std::string(1, c) + " is not a valid option"; + break; + } + + const Option &sub = get("-" + std::string(1, c)); + + pack.push_back(OptionValue(sub, "")); + } + } + + ++ it; + } else { + // 1. + if (isShortCompacted(*it++)) { + pack.push_back(OptionValue(option, v)); + pack.m_argsParsed += 1; + } else { + // 2. + if (it == end) { + pack.m_error = option.key() + " requires an option"; + } else { + pack.push_back(OptionValue(option, *it++)); + pack.m_argsParsed += 2; + } + } + } +} + +void OptionParser::readFull(OptionPack &pack, Args::const_iterator &it, Args::const_iterator end) const +{ + /* + * Long options can't be compacted, there are only two possibilities: + * + * 1. --fullscreen No argument + * 2. --config foo One argument + */ + const Option &option = get(*it); + + if (!isToggle(*it)) { + // 2. + if (++it == end) { + pack.m_error = "--" + option.full() + " requires an option"; + } else { + pack.push_back(OptionValue(option, *it++)); + pack.m_argsParsed += 2; + } + } else { + pack.push_back(OptionValue(option, "")); + pack.m_argsParsed ++; + + ++ it; + } +} + +OptionParser::OptionParser(std::initializer_list<Option> options) + : m_options(options.begin(), options.end()) +{ +} + +OptionParser::OptionParser(std::vector<Option> options) + : m_options(std::move(options)) +{ +} + +OptionPack OptionParser::parse(Args::const_iterator it, Args::const_iterator end, int flags) const +{ + OptionPack pack; + + while (it != end) { + if (!isOption(*it)) { + if (flags & Unstrict) { + pack.m_argsParsed ++; + it ++; + continue; + } else { + pack.m_error = *it + " is not an option"; + return pack; + } + } + + if (!isDefined(*it)) { + pack.m_error = "Invalid option"; + return pack; + } + + if (isShort(*it)) { + readShort(pack, it, end); + } else { + readFull(pack, it, end); + } + + // Read failure + if (pack.m_error != "No error") { + return pack; + } + } + + return pack; +} + +OptionPack OptionParser::parse(int argc, char **argv, int flags) const +{ + std::vector<std::string> args; + + for (int i = 0; i < argc; ++i) + args.push_back(argv[i]); + + return parse(args, flags); +} + +OptionPack OptionParser::parse(const std::vector<std::string> &args, int flags) const +{ + return parse(args.begin(), args.end(), flags); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/C++/modules/OptionParser/OptionParser.h Sun Mar 08 14:26:33 2015 +0100 @@ -0,0 +1,275 @@ +/* + * OptionParser.h -- command line option parser + * + * Copyright (c) 2013, 2014 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _OPTION_PARSER_H_ +#define _OPTION_PARSER_H_ + +/** + * @file OptionParser.h + * @brief Command line option parser + */ + +#include <initializer_list> +#include <string> +#include <vector> + +/** + * @class Option + * @brief Option definition + */ +class Option { +public: + enum Flags { + NoArg = (1 << 0), + }; + +private: + std::string m_key; + std::string m_full; + int m_flags; + +public: + /** + * Construct an option. By default, an option requires an argument + * unless flags is set to NoArg. + * + * You <strong>must</strong> not prepend dashes to the option names. + * + * You don't need to set both short and long names, but you need at + * least one. + * + * @param key the short name (e.g v) + * @param full the long name (e.g verbose) + * @param flags the optional flags + * @see Flags + */ + inline Option(std::string key, std::string full, int flags = 0) + : m_key(std::move(key)) + , m_full(std::move(full)) + , m_flags(flags) + { + } + + /** + * Get the short name (e.g v) + * + * @return the short name + */ + inline const std::string &key() const noexcept + { + return m_key; + } + + /** + * Get the long name (e.g verbose) + * + * @return the long name + */ + inline const std::string &full() const noexcept + { + return m_full; + } + + /** + * Get the flags. + * + * @return the flags + * @see Flags + */ + inline int flags() const noexcept + { + return m_flags; + } +}; + +/** + * @class OptionValue + * @brief Result of an option parse + */ +class OptionValue { +private: + std::string m_key; + std::string m_full; + std::string m_value; + +public: + /** + * Construct an option value + * + * @param option the option + * @param value the value + */ + inline OptionValue(const Option &option, std::string value) + : m_key(option.key()) + , m_full(option.full()) + , m_value(std::move(value)) + { + } + + /** + * Get the value (if the option requires an argument). + * + * @return the value + */ + inline const std::string &value() const noexcept + { + return m_value; + } + + friend bool operator==(const OptionValue &o1, const std::string &name); +}; + +/** + * Test the option value with the specified option name. + * + * You can use both the short option or the long option name depending + * on what you have registered to the OptionParser class. + * + * @param o the option + * @param name the short or the full name + * @return true if matches + */ +inline bool operator==(const OptionValue &o, const std::string &name) +{ + return o.m_key == name || o.m_full == name; +} + +/** + * @class OptionPack + * @brief Object containing results of a parse + * + * Because parsing bad options does not throw exceptions, this class is + * convertible to bool and has the error contained. + * + * It also have the number of arguments parsed so you can cut your options + * depending on the full command line. + * + * Example: + * -y install -d foo + * -y remove -f + * + * In that case, you can do two parsing, it will stops (unless Unstrict is set) + * until install or remove. + */ +class OptionPack : public std::vector<OptionValue> { +private: + friend class OptionParser; + + std::string m_error{"No error"}; + int m_argsParsed{0}; + +public: + /** + * Get the error. + * + * @return the error + */ + inline const std::string &error() const noexcept + { + return m_error; + } + + /** + * Get the number of arguments parsed <strong>not the number of + * options</strong>. + * + * @return the number of arguments parsed + */ + inline int parsed() const noexcept + { + return m_argsParsed; + } + + /** + * Convert to true on success. + * + * @return true on success + */ + inline operator bool() const noexcept + { + return m_error == "No error"; + } +}; + +/** + * @class OptionParser + * @brief Base class for parsing command line options + * + * The option parser is a replacement for getopt(3) which is reentrant + * and does not use globals. + */ +class OptionParser { +public: + using Map = std::vector<Option>; + using Args = std::vector<std::string>; + + enum Flags { + Unstrict = (1 << 0) + }; + +private: + Map m_options; + + const Option &get(const std::string &arg) const; + std::string key(const std::string &arg) const; + bool isDefined(const std::string &arg) const; + bool isToggle(const std::string &arg) const; + bool isShortCompacted(const std::string &arg) const; + bool isShort(const std::string &arg) const; + bool isLong(const std::string &arg) const; + bool isOption(const std::string &arg) const; + void readShort(OptionPack &pack, Args::const_iterator &it, Args::const_iterator end) const; + void readFull(OptionPack &pack, Args::const_iterator &it, Args::const_iterator end) const; + OptionPack parse(Args::const_iterator it, Args::const_iterator end, int flags) const; + +public: + /** + * Construct an option parser from an initializer_list of options. + * + * @param options the list of options + */ + OptionParser(std::initializer_list<Option> options); + + /** + * Construct an option parser from a vector of options. + * + * @param options the options + */ + OptionParser(std::vector<Option> options); + + /** + * Parse the arguments from main arguments. + * + * @param argc the number of arguments + * @param argv the arguments + * @param flags the optional flags + * @return the packed result + */ + OptionPack parse(int argc, char **argv, int flags = 0) const; + + /** + * Parse the arguments from a vector. + * + * @param args the arguments + * @param flags the optional flags + * @return the packed result + */ + OptionPack parse(const std::vector<std::string> &args, int flags = 0) const; +}; + +#endif // !_OPTION_PARSER_H_
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/C++/modules/Pack/Pack.cpp Sun Mar 08 14:26:33 2015 +0100 @@ -0,0 +1,115 @@ +/* + * Pack.cpp -- binary data serialization + * + * Copyright (c) 2013, 2014 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "Pack.h" + +namespace { + +Pack::Endian checkMode() +{ + int i = 1; + unsigned char *ptr = reinterpret_cast<unsigned char *>(&i); + + return (ptr[0] == 1) ? Pack::Little : Pack::Big; +} + +} // !namespace + +const Pack::Endian Pack::mode = checkMode(); + +/* -------------------------------------------------------- + * PackReader + * -------------------------------------------------------- */ + +PackReader::PackReader(Pack::Endian endian) + : m_endian(endian) +{ +} + +/* -------------------------------------------------------- + * PackFileReader + * -------------------------------------------------------- */ + +PackFileReader::PackFileReader(const std::string &path, Pack::Endian endian) + : PackReader(endian) +{ + m_in.open(path, std::ifstream::in); +} + +std::istream &PackFileReader::stream() +{ + return m_in; +} + +/* -------------------------------------------------------- + * PackStringReader + * -------------------------------------------------------- */ + +PackStringReader::PackStringReader(std::string input, Pack::Endian endian) + : PackReader(endian) + , m_in(std::move(input)) +{ +} + +std::istream &PackStringReader::stream() +{ + return m_in; +} + +/* -------------------------------------------------------- + * PackWriter + * -------------------------------------------------------- */ + +PackWriter::PackWriter(Pack::Endian endian) + : m_endian(endian) +{ +} + +/* -------------------------------------------------------- + * PackFileWriter + * -------------------------------------------------------- */ + +PackFileWriter::PackFileWriter(const std::string &path, Pack::Endian endian) + : PackWriter(endian) +{ + m_out.open(path, std::ofstream::out); +} + +std::ostream &PackFileWriter::stream() +{ + return m_out; +} + +/* -------------------------------------------------------- + * PackStringWriter + * -------------------------------------------------------- */ + +PackStringWriter::PackStringWriter(Pack::Endian endian) + : PackWriter(endian) +{ +} + +std::ostream &PackStringWriter::stream() +{ + return m_out; +} + +std::string PackStringWriter::buffer() const +{ + return m_out.str(); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/C++/modules/Pack/Pack.h Sun Mar 08 14:26:33 2015 +0100 @@ -0,0 +1,408 @@ +/* + * Pack.h -- binary data serialization + * + * Copyright (c) 2013, 2014 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _PACK_H_ +#define _PACK_H_ + +#include <cstdint> +#include <fstream> +#include <memory> +#include <sstream> +#include <string> + +/** + * @class Pack + * @brief Serialize binary data to files + * + * This class write and read binary data from files. It currently + * support: + * uint8_t, + * uint16_t, + * uint32_t, + * uint64_t + */ +class Pack { +private: + template <typename T> + struct IsContainer { + using Yes = char [1]; + using No = char [2]; + + template <typename U> + static constexpr Yes &test(typename U::value_type *); + + template <typename U> + static constexpr No &test(...); + + static constexpr const bool value = sizeof (test<T>(0)) == sizeof (Yes); + }; + + friend class PackWriter; + friend class PackReader; + +public: + /** + * @enum Endian + * @brief Endian mode + */ + enum Endian { + Little, //! Little endian + Big //! Big endian + }; + +public: + /** + * Host system endian mode. + */ + static const Endian mode; + + /** + * @struct TypeInfo + * @brief Type information + * + * Used for conversions. + */ + template <typename T> + struct TypeInfo { + static constexpr const bool convertible{false}; + static constexpr const bool serializable{false}; + }; + + /** + * Helper to mark a specialization convertible. + * + * Already done for: + * uint8_t + * uint16_t + * uint32_t + * uint64_t + * + * The specialization must have the following function: + * + * static void convert(T &value) noexcept + */ + struct Convertible { + static constexpr const bool convertible{true}; + }; + + /** + * Helper to mark a specialization serializable. + * + * The specialisation must have the following functions: + * + * static void serialize(PackWriter &writer, const T &) + * static void unserialize(PackReader &reader, T &) + */ + struct Serializable { + static constexpr const bool serializable{true}; + }; + + /** + * Convert data inplace. + * + * @param value the value + */ + template <typename T> + static inline void convert(T &value) noexcept + { + static_assert(TypeInfo<T>::convertible, "unsupported type"); + + TypeInfo<T>::convert(value); + } +}; + +/** + * @class PackReader + * @brief Base abstract reader class + */ +class PackReader { +protected: + Pack::Endian m_endian; + + PackReader(Pack::Endian endian); + + virtual std::istream &stream() = 0; + +public: + /** + * Default destructor. + */ + virtual ~PackReader() = default; + + /** + * Read a primitive convertible type. + * + * @param value the value destination + * @return *this + */ + template <typename T, typename std::enable_if<Pack::TypeInfo<T>::convertible>::type * = nullptr> + PackReader &operator>>(T &value) + { + stream().read(reinterpret_cast<char *>(&value), sizeof (T)); + + if (m_endian != Pack::mode) + Pack::convert(value); + + return *this; + } + + /** + * Read a serializable type. + * + * @param value the value destination + * @return *this + */ + template <typename T, typename std::enable_if<Pack::TypeInfo<T>::serializable>::type * = nullptr> + PackReader &operator>>(T &value) + { + Pack::TypeInfo<T>::unserialize(*this, value); + + return *this; + } + + /** + * Read an array. + * + * This operator is a little bit tricky because you don't know in + * advance how much data you want to read. Because of that, this + * function looks the capacity of the container and reads that number + * of data. + * + * Because it looks for capacity, you can't use a container which + * already have some data, they will be overriden. + * + * If this is a concern, you should roll your own loop to fill up + * your container. + * + * @param container the container (all previous data will be lost) + * @return *this + */ + template <typename T, typename std::enable_if<Pack::IsContainer<T>::value>::type * = nullptr> + PackReader &operator>>(T &container) + { + typename T::value_type v; + + T copy; + + for (size_t i = 0; i < container.capacity(); ++i) { + (*this) >> v; + copy.push_back(v); + } + + container = std::move(copy); + + return *this; + } +}; + +/** + * @class PackWriter + * @brief Base abstract writer class + */ +class PackWriter { +protected: + Pack::Endian m_endian; + + PackWriter(Pack::Endian endian); + + virtual std::ostream &stream() = 0; + +public: + /** + * Default destructor. + */ + virtual ~PackWriter() = default; + + /** + * Write a convertible type to the stream. + * + * @param value the value + * @return *this + */ + template <typename T, typename std::enable_if<Pack::TypeInfo<T>::convertible>::type * = nullptr> + PackWriter &operator<<(T value) + { + if (m_endian != Pack::mode) + Pack::convert(value); + + stream().write(reinterpret_cast<const char *>(&value), sizeof (T)); + + return *this; + } + + /** + * Write a serializable type to the stream. + * + * @param value the value + * @return *this + */ + template <typename T, typename std::enable_if<Pack::TypeInfo<T>::serializable>::type * = nullptr> + PackWriter &operator<<(const T &value) + { + Pack::TypeInfo<T>::serialize(*this, value); + + return *this; + } + + /** + * Write a container to the stream. + * + * @param container the container + * @return *this + */ + template <typename T, typename std::enable_if<Pack::IsContainer<T>::value>::type * = nullptr> + PackWriter &operator<<(const T &container) + { + for (const auto &v : container) + (*this) << v; + + return *this; + } +}; + +/** + * @class PackFileReader + * @brief Extract binary data from a file + */ +class PackFileReader : public PackReader { +private: + std::ifstream m_in; + +protected: + std::istream &stream() override; + +public: + /** + * Read a file. + * + * @param path the path + * @param endian the endian requested + */ + PackFileReader(const std::string &path, Pack::Endian endian); +}; + +/** + * @class PackStringReader + * @brief Extract binary data from a string + */ +class PackStringReader : public PackReader { +private: + std::istringstream m_in; + + std::istream &stream() override; + +public: + /** + * Read a string. + * + * @param input the input string + * @param endian the endian requested + */ + PackStringReader(std::string input, Pack::Endian endian); +}; + +/** + * @class PackFileWriter + * @brief Write binary data to a string + */ +class PackFileWriter : public PackWriter { +private: + std::ofstream m_out; + +protected: + std::ostream &stream() override; + +public: + /** + * Write to a file. + * + * @param path the path + * @param endian the endian requested + */ + PackFileWriter(const std::string &path, Pack::Endian endian); +}; + +/** + * @class PackStringWriter + * @brief Write binary data to a string + */ +class PackStringWriter : public PackWriter { +private: + std::ostringstream m_out; + + std::ostream &stream() override; + +public: + /** + * Write to a string + * + * @param endian the endian requested + */ + PackStringWriter(Pack::Endian endian); + + /** + * The current buffer. Returns a copy of the string. + * + * @return the string + */ + std::string buffer() const; +}; + +template <> +struct Pack::TypeInfo<uint8_t> : public Pack::Convertible { + static inline void convert(uint8_t &) noexcept + { + // uint8_t are endian independent + } +}; + +template <> +struct Pack::TypeInfo<uint16_t> : public Pack::Convertible { + static inline void convert(uint16_t &v) + { + v = (((v >> 8) & 0x00FFL) | ((v << 8) & 0xFF00L)); + } +}; + +template <> +struct Pack::TypeInfo<uint32_t> : public Pack::Convertible { + static inline void convert(uint32_t &v) + { + v = ((((v) >> 24) & 0x000000FFL) + | (((v) >> 8) & 0x0000FF00L) + | (((v) << 8) & 0x00FF0000L) + | (((v) << 24) & 0xFF000000L)); + } +}; + +template <> +struct Pack::TypeInfo<uint64_t> : public Pack::Convertible { + static inline void convert(uint64_t &v) + { + v = ((((v) & 0xff00000000000000ull) >> 56) + | (((v) & 0x00ff000000000000ull) >> 40) + | (((v) & 0x0000ff0000000000ull) >> 24) + | (((v) & 0x000000ff00000000ull) >> 8 ) + | (((v) & 0x00000000ff000000ull) << 8 ) + | (((v) & 0x0000000000ff0000ull) << 24) + | (((v) & 0x000000000000ff00ull) << 40) + | (((v) & 0x00000000000000ffull) << 56)); + } +}; + +#endif // !_PACK_H_
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/C++/modules/Parser/Parser.cpp Sun Mar 08 14:26:33 2015 +0100 @@ -0,0 +1,299 @@ +/* + * Parser.h -- config file parser + * + * Copyright (c) 2013, 2014 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <cstring> +#include <cerrno> +#include <iostream> +#include <fstream> + +#include "Parser.h" + +/* -------------------------------------------------------- + * Section public members + * -------------------------------------------------------- */ + +Section::Section() + : m_allowed(true) +{ +} + +Section::Section(const std::string &name, bool allowed) + : m_name(name) + , m_allowed(allowed) +{ + +} + +const std::string &Section::getName() const +{ + return m_name; +} + +bool Section::hasOption(const std::string &name) const +{ + return m_options.count(name) >= 1; +} + +Section::Map::iterator Section::begin() +{ + return m_options.begin(); +} + +Section::Map::const_iterator Section::cbegin() const +{ + return m_options.cbegin(); +} + +Section::Map::iterator Section::end() +{ + return m_options.end(); +} + +Section::Map::const_iterator Section::cend() const +{ + return m_options.end(); +} + +bool operator==(const Section &s1, const Section &s2) +{ + return s1.m_name == s2.m_name && s1.m_options == s2.m_options; +} + +/* -------------------------------------------------------- + * Parser private members + * -------------------------------------------------------- */ + +void Parser::addOption(const std::string &key, const std::string &value) +{ + m_sections.back().m_options.insert(std::make_pair(key, value)); +} + +void Parser::readSection(int lineno, const std::string &line) +{ + size_t end; + + if ((end = line.find_first_of(']')) != std::string::npos) { + if (end > 1) { + auto name = line.substr(1, end - 1); + + /* + * Check if we can add a section, if redefinition is + * disabled, we must disable the previous section so the + * further read options should not be enabled until + * a correct section is found again. + */ + if (hasSection(name) && (m_tuning & DisableRedefinition)) { + if (!(m_tuning & DisableVerbosity)) + log(lineno, name, "redefinition not allowed"); + m_sections.back().m_allowed = false; + } else { + m_sections.push_back(Section(name)); + } + } else if (!(m_tuning & DisableVerbosity)) { + /* + * Do not add options at this step because it will + * corrupt the previous one. + */ + m_sections.back().m_allowed = false; + log(lineno, "", "empty section name"); + } + } +} + +void Parser::readOption(int lineno, const std::string &line) +{ + auto ¤t = m_sections.back(); + size_t epos; + std::string key, value; + + // Error on last section? + if (!current.m_allowed) { + /* + * If it is the root section, this has been probably set by + * DisableRootSection flag, otherwise an error has occured + * so no need to log. + */ + if (current.m_name == "" && !(m_tuning == DisableVerbosity)) + log(lineno, "", "option not allowed in that scope"); + + return; + } + + if ((epos = line.find_first_of('=')) == std::string::npos) { + if (!(m_tuning & DisableVerbosity)) + log(lineno, current.m_name, "missing `=' keyword"); + return; + } + + if (epos > 0) { + size_t i, begin, last; + char c; + + key = line.substr(0, epos); + value = line.substr(epos + 1); + + // clean option key + for (i = 0; !isspace(key[i]) && i < key.length(); ++i) + continue; + key = key.substr(0, i); + + // clean option value + for (begin = 0; isspace(value[begin]) && begin < value.length(); ++begin) + continue; + value = value.substr(begin); + + c = value[0]; + begin = 0; + if (c == '\'' || c == '"') { + for (last = begin = 1; value[last] != c && last < value.length(); ++last) + continue; + if (value[last] != c && !(m_tuning & DisableVerbosity)) + if (!(m_tuning & DisableVerbosity)) + log(lineno, current.m_name, "undisclosed std::string"); + } else { + for (last = begin; !isspace(value[last]) && last < value.length(); ++last) + continue; + } + + if (last - begin > 0) + value = value.substr(begin, last - begin); + else + value.clear(); + + // Add the option if the key is not empty + if (key.length() > 0) + addOption(key, value); + } +} + +void Parser::readLine(int lineno, const std::string &line) +{ + size_t i; + std::string buffer; + + // Skip default spaces + for (i = 0; isspace(line[i]) && i < line.length(); ++i) + continue; + + buffer = line.substr(i); + if (buffer.length() > 0) { + if (buffer[0] != m_commentChar) { + if (buffer[0] == '[') + readSection(lineno, buffer); + else + readOption(lineno, buffer); + } + } +} + +/* -------------------------------------------------------- + * Parser public methods + * -------------------------------------------------------- */ + +const char Parser::DEFAULT_COMMENT_CHAR = '#'; + +void Parser::open() +{ + std::ifstream file; + std::string line; + int lineno = 1; + + file.open(m_path.c_str()); + if (!file.is_open()) + throw std::runtime_error(m_path + ": " + std::string(std::strerror(errno))); + + while (std::getline(file, line)) + readLine(lineno++, line); + + file.close(); +} + +Parser::Parser() +{ +} + +Parser::Parser(const std::string &path, int tuning, char commentToken) + : m_path(path) + , m_tuning(tuning) + , m_commentChar(commentToken) +{ + m_sections.push_back(Section("", (tuning & DisableRootSection) ? false : true)); + open(); +} + +Parser::~Parser() +{ +} + +Parser::List::iterator Parser::begin() +{ + return m_sections.begin(); +} + +Parser::List::const_iterator Parser::cbegin() const +{ + return m_sections.cbegin(); +} + +Parser::List::iterator Parser::end() +{ + return m_sections.end(); +} + +Parser::List::const_iterator Parser::cend() const +{ + return m_sections.end(); +} + +void Parser::findSections(const std::string &name, FindFunc func) const +{ + for (const auto &s : m_sections) + if (s.m_name == name) + func(s); +} + +bool Parser::hasSection(const std::string &name) const +{ + for (const auto &s : m_sections) + if (s.m_name == name) + return true; + + return false; +} + +const Section &Parser::getSection(const std::string &name) const +{ + for (const auto &s : m_sections) + if (s.m_name == name) + return s; + + throw std::out_of_range(name + " not found"); +} + +void Parser::log(int number, const std::string &, const std::string &message) +{ + std::cout << "line " << number << ": " << message << std::endl; +} + +bool operator==(const Parser &p1, const Parser &p2) +{ + return p1.m_sections == p2.m_sections && + p1.m_path == p2.m_path && + p1.m_tuning == p2.m_tuning && + p1.m_commentChar == p2.m_commentChar; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/C++/modules/Parser/Parser.h Sun Mar 08 14:26:33 2015 +0100 @@ -0,0 +1,336 @@ +/* + * Parser.h -- config file parser + * + * Copyright (c) 2013, 2014 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _PARSER_H_ +#define _PARSER_H_ + +#include <cstdlib> +#include <functional> +#include <stdexcept> +#include <string> +#include <unordered_map> +#include <utility> +#include <vector> + +/** + * @class Section + * @brief The option container + * + * A list of section found in the file. If root + * options are allowed (default behavior), the root + * section is "". + */ +class Section { +public: + friend class Parser; + + using Map = std::unordered_map<std::string, std::string>; + + template <typename T> + struct Converter { + static const bool supported = false; + }; + +private: + std::string m_name; /*! name of section */ + Map m_options; /*! list of options inside */ + bool m_allowed; /*! is authorized to push */ + +public: + /** + * Default constructor. + */ + Section(); + + /** + * Named constructor. + * + * @param name the section name + * @param allowed is allowed to push + */ + Section(const std::string &name, bool allowed = true); + + /** + * Tells if that section has the specified option name. + * + * @param name the option name + * @return true if has + */ + bool hasOption(const std::string &name) const; + + /** + * Get the section name + * + * @return the section name + */ + const std::string &getName() const; + + /** + * Return an iterator to the beginning. + * + * @return the iterator. + */ + Map::iterator begin(); + + /** + * Return a const iterator to the beginning. + * + * @return the iterator. + */ + Map::const_iterator cbegin() const; + + /** + * Return an iterator to the end. + * + * @return the iterator. + */ + Map::iterator end(); + + /** + * Return a const iterator to the end. + * + * @return the iterator. + */ + Map::const_iterator cend() const; + + /** + * Template all functions for retrieving options value. + * + * @param name the option name + * @return the value if found + */ + template <typename T> + T getOption(const std::string &name) const + { + try { + return requireOption<T>(name); + } catch (...) { + // Catch any conversion error. + } + + return T(); + } + + /** + * Requires an option, this works like getOption except + * that if an option is not found, an exception is + * thrown. + * + * @param name the name + * @return the value + * @throw std::out_of_range if not found + * @throw std::invalid_argument on conversion failures + */ + template <typename T> + T requireOption(const std::string &name) const + { + static_assert(Converter<T>::supported, "invalid type requested"); + + return Converter<T>::convert(m_options.at(name)); + } + + friend bool operator==(const Section &s1, const Section &s2); +}; + +template <> +struct Section::Converter<bool> { + static const bool supported = true; + + static bool convert(const std::string &value) + { + bool result(false); + + if (value == "yes" || value == "true"|| value == "1") + result = true; + else if (value == "no" || value == "false" || value == "0") + result = false; + + return result; + } +}; + +template <> +struct Section::Converter<int> { + static const bool supported = true; + + static int convert(const std::string &value) + { + return std::stoi(value); + } +}; + +template <> +struct Section::Converter<float> { + static const bool supported = true; + + static float convert(const std::string &value) + { + return std::stof(value); + } +}; + +template <> +struct Section::Converter<double> { + static const bool supported = true; + + static double convert(const std::string &value) + { + return std::stod(value); + } +}; + +template <> +struct Section::Converter<std::string> { + static const bool supported = true; + + static std::string convert(const std::string &value) + { + return value; + } +}; + +/** + * @class Parser + * @brief Config file parser + * + * Open and read .ini files. + */ +class Parser { +public: + /** + * Options available for the parser. + */ + enum Tuning { + DisableRootSection = 1, /*! disable options on root */ + DisableRedefinition = 2, /*! disable multiple redefinition */ + DisableVerbosity = 4 /*! be verbose by method */ + }; + + using FindFunc = std::function<void (const Section &)>; + using List = std::vector<Section>; + +private: + List m_sections; /*! list of sections found */ + std::string m_path; /*! path file */ + int m_tuning; /*! options for parsing */ + char m_commentChar; /*! the comment token default (#) */ + + void addSection(const std::string &name); + void addOption(const std::string &key, const std::string &value); + + void readSection(int lineno, const std::string &line); + void readOption(int lineno, const std::string &line); + + void readLine(int lineno, const std::string &line); + + void open(); + +public: + static const char DEFAULT_COMMENT_CHAR; + + /** + * Create a parser at the specified file path. Optional + * options may be added. + * + * @param path the file path + * @param tuning optional tuning flags + * @param commentToken an optional comment delimiter + * @throw std::runtime_error on errors + * @see Tuning + */ + Parser(const std::string &path, int tuning = 0, char commentToken = Parser::DEFAULT_COMMENT_CHAR); + + /** + * Default constructor. + */ + Parser(); + + /** + * Default destructor. + */ + virtual ~Parser(); + + /** + * Return an iterator to the beginning. + * + * @return the iterator. + */ + List::iterator begin(); + + /** + * Return a const iterator to the beginning. + * + * @return the iterator. + */ + List::const_iterator cbegin() const; + + /** + * Return an iterator to the end. + * + * @return the iterator. + */ + List::iterator end(); + + /** + * Return a const iterator to the end. + * + * @return the iterator. + */ + List::const_iterator cend() const; + + /** + * Find all sections matching the name. + * + * @param name the sections name + * @param func the function + * @return a list of section with the options + */ + void findSections(const std::string &name, FindFunc func) const; + + /** + * Tell if a section is existing. + * + * @return true if exists + */ + bool hasSection(const std::string &name) const; + + /** + * Get a specified section. + * + * @param name the section name + * @return a section + * @throw std::out_of_range if not found + */ + const Section &getSection(const std::string &name) const; + + /** + * Logging function, used only if DisableVerbosity is not set. The + * default behavior is to print to stdout something like: + * line 10: syntax error + * line 8: missing = + * + * @param number the line number + * @param section the current section worked on + * @param message the message + */ + virtual void log(int number, const std::string §ion, const std::string &message); + + friend bool operator==(const Parser &p1, const Parser &p2); +}; + +#endif // !_PARSER_H_
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/C++/modules/Socket/Socket.cpp Sun Mar 08 14:26:33 2015 +0100 @@ -0,0 +1,173 @@ +/* + * Socket.cpp -- portable C++ socket wrappers + * + * Copyright (c) 2013, 2014 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <cstring> + +#include "Socket.h" +#include "SocketAddress.h" + +/* -------------------------------------------------------- + * System dependent code + * -------------------------------------------------------- */ + +#if defined(_WIN32) + +std::string Socket::syserror(int errn) +{ + LPSTR str = nullptr; + std::string errmsg = "Unknown error"; + + FormatMessageA( + FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, + NULL, + errn, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPSTR)&str, 0, NULL); + + + if (str) { + errmsg = std::string(str); + LocalFree(str); + } + + return errmsg; +} + +#else + +#include <cerrno> + +std::string Socket::syserror(int errn) +{ + return strerror(errn); +} + +#endif + +std::string Socket::syserror() +{ +#if defined(_WIN32) + return syserror(WSAGetLastError()); +#else + return syserror(errno); +#endif +} + +/* -------------------------------------------------------- + * SocketError class + * -------------------------------------------------------- */ + +SocketError::SocketError(Code code, std::string function) + : m_code(code) + , m_function(std::move(function)) + , m_error(Socket::syserror()) +{ +} + +SocketError::SocketError(Code code, std::string function, int error) + : m_code(code) + , m_function(std::move(function)) + , m_error(Socket::syserror(error)) +{ +} + +SocketError::SocketError(Code code, std::string function, std::string error) + : m_code(code) + , m_function(std::move(function)) + , m_error(std::move(error)) +{ +} + +/* -------------------------------------------------------- + * Socket class + * -------------------------------------------------------- */ + +#if defined(_WIN32) +std::mutex Socket::s_mutex; +std::atomic<bool> Socket::s_initialized{false}; +#endif + +Socket::Socket(int domain, int type, int protocol) +{ +#if defined(_WIN32) && !defined(SOCKET_NO_WSA_INIT) + if (!s_initialized) + initialize(); +#endif + + m_handle = ::socket(domain, type, protocol); + + if (m_handle == Invalid) + throw SocketError(SocketError::System, "socket"); + + m_state = SocketState::Opened; +} + +void Socket::bind(const SocketAddress &address) +{ + const auto &sa = address.address(); + const auto addrlen = address.length(); + + if (::bind(m_handle, reinterpret_cast<const sockaddr *>(&sa), addrlen) == Error) + throw SocketError(SocketError::System, "bind"); + + m_state = SocketState::Bound; +} + +void Socket::close() +{ +#if defined(_WIN32) + ::closesocket(m_handle); +#else + ::close(m_handle); +#endif + + m_state = SocketState::Closed; +} + +void Socket::setBlockMode(bool block) +{ +#if defined(O_NONBLOCK) && !defined(_WIN32) + int flags; + + if ((flags = fcntl(m_handle, F_GETFL, 0)) == -1) + flags = 0; + + if (block) + flags &= ~(O_NONBLOCK); + else + flags |= O_NONBLOCK; + + if (fcntl(m_handle, F_SETFL, flags) == Error) + throw SocketError(SocketError::System, "setBlockMode"); +#else + unsigned long flags = (block) ? 0 : 1; + + if (ioctlsocket(m_handle, FIONBIO, &flags) == Error) + throw SocketError(SocketError::System, "setBlockMode"); +#endif +} + +bool operator==(const Socket &s1, const Socket &s2) +{ + return s1.handle() == s2.handle(); +} + +bool operator<(const Socket &s1, const Socket &s2) +{ + return s1.handle() < s2.handle(); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/C++/modules/Socket/Socket.h Sun Mar 08 14:26:33 2015 +0100 @@ -0,0 +1,404 @@ +/* + * Socket.h -- portable C++ socket wrappers + * + * Copyright (c) 2013, 2014 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _SOCKET_NG_H_ +#define _SOCKET_NG_H_ + +/** + * @file Socket.h + * @brief Portable socket abstraction + * + * User may set the following variables before compiling these files: + * + * SOCKET_NO_WSA_INIT - (bool) Set to false if you don't want Socket class to + * automatically calls WSAStartup() when creating sockets. + * + * Otherwise, you will need to call Socket::init, + * Socket::finish yourself. + * + * SOCKET_NO_SSL_INIT - (bool) Set to false if you don't want OpenSSL to be + * initialized when the first SocketSsl object is created. + * + * SOCKET_HAVE_POLL - (bool) Set to true if poll(2) function is available. + * + * Note: on Windows, this is automatically set if the + * _WIN32_WINNT variable is greater or equal to 0x0600. + */ + +#include <cstring> +#include <exception> +#include <string> + +#if defined(_WIN32) +# include <atomic> +# include <cstdlib> +# include <mutex> + +# include <WinSock2.h> +# include <WS2tcpip.h> +#else +# include <cerrno> + +# include <sys/ioctl.h> +# include <sys/socket.h> +# include <sys/types.h> + +# include <arpa/inet.h> + +# include <netinet/in.h> + +# include <fcntl.h> +# include <netdb.h> +# include <unistd.h> +#endif + +class SocketAddress; + +/** + * @class SocketError + * @brief Base class for sockets error + */ +class SocketError : public std::exception { +public: + enum Code { + WouldBlockRead, ///!< The operation would block for reading + WouldBlockWrite, ///!< The operation would block for writing + Timeout, ///!< The action did timeout + System ///!< There is a system error + }; + + Code m_code; + std::string m_function; + std::string m_error; + + /** + * Constructor that use the last system error. + * + * @param code which kind of error + * @param function the function name + */ + SocketError(Code code, std::string function); + + /** + * Constructor that use the system error set by the user. + * + * @param code which kind of error + * @param function the function name + * @param error the error + */ + SocketError(Code code, std::string function, int error); + + /** + * Constructor that set the error specified by the user. + * + * @param code which kind of error + * @param function the function name + * @param error the error + */ + SocketError(Code code, std::string function, std::string error); + + /** + * Get which function has triggered the error. + * + * @return the function name (e.g connect) + */ + inline const std::string &function() const noexcept + { + return m_function; + } + + /** + * The error code. + * + * @return the code + */ + inline Code code() const noexcept + { + return m_code; + } + + /** + * Get the error (only the error content). + * + * @return the error + */ + const char *what() const noexcept + { + return m_error.c_str(); + } +}; + +/** + * @enum SocketState + * @brief Category of error + */ +enum class SocketState { + Opened, ///!< Socket is opened + Closed, ///!< Socket has been closed + Bound, ///!< Socket is bound to address + Connected, ///!< Socket is connected to an end point + Disconnected, ///!< Socket is disconnected + Timeout ///!< Timeout has occured in a waiting operation +}; + +/** + * @class Socket + * @brief Base socket class for socket operations + */ +class Socket { +public: + /* {{{ Portable types */ + + /* + * The following types are defined differently between Unix + * and Windows. + */ +#if defined(_WIN32) + using Handle = SOCKET; + using ConstArg = const char *; + using Arg = char *; +#else + using Handle = int; + using ConstArg = const void *; + using Arg = void *; +#endif + + /* }}} */ + + /* {{{ Portable constants */ + + /* + * The following constants are defined differently from Unix + * to Windows. + */ +#if defined(_WIN32) + static constexpr const int Invalid = INVALID_SOCKET; + static constexpr const int Error = SOCKET_ERROR; +#else + static constexpr const int Invalid = -1; + static constexpr const int Error = -1; +#endif + + /* }}} */ + + /* {{{ Portable initialization */ + + /* + * Initialization stuff. + * + * The function init and finish are threadsafe. + */ +#if defined(_WIN32) +private: + static std::mutex s_mutex; + static std::atomic<bool> s_initialized; + +public: + static inline void finish() noexcept + { + WSACleanup(); + } + + static inline void init() noexcept + { + std::lock_guard<std::mutex> lock(s_mutex); + + if (!s_initialized) { + s_initialized = true; + + WSADATA wsa; + WSAStartup(MAKEWORD(2, 2), &wsa); + + /* + * If SOCKET_WSA_NO_INIT is not set then the user + * must also call finish himself. + */ +#if !defined(SOCKET_WSA_NO_INIT) + std::atexit(finish); +#endif + } + } +#else +public: + /** + * no-op. + */ + static inline void init() noexcept {} + + /** + * no-op. + */ + static inline void finish() noexcept {} +#endif + + /* }}} */ + +protected: + Handle m_handle; + SocketState m_state{SocketState::Opened}; + +public: + /** + * Get the last socket system error. The error is set from errno or from + * WSAGetLastError on Windows. + * + * @return a string message + */ + static std::string syserror(); + + /** + * Get the last system error. + * + * @param errn the error number (errno or WSAGetLastError) + * @return the error + */ + static std::string syserror(int errn); + + /** + * Construct a socket with an already created descriptor. + * + * @param handle the native descriptor + */ + inline Socket(Handle handle) + : m_handle(handle) + , m_state(SocketState::Opened) + { + } + + /** + * Create a socket handle. + * + * @param domain the domain AF_* + * @param type the type SOCK_* + * @param protocol the protocol + * @throw SocketError on failures + */ + Socket(int domain, int type, int protocol); + + /** + * Default destructor. + */ + virtual ~Socket() = default; + + /** + * Set an option for the socket. + * + * @param level the setting level + * @param name the name + * @param arg the value + * @throw SocketError on error + */ + template <typename Argument> + inline void set(int level, int name, const Argument &arg) + { +#if defined(_WIN32) + if (setsockopt(m_handle, level, name, (Socket::ConstArg)&arg, sizeof (arg)) == SOCKET_ERROR) +#else + if (setsockopt(m_handle, level, name, (Socket::ConstArg)&arg, sizeof (arg)) < 0) +#endif + throw SocketError(SocketError::System, "set"); + } + + /** + * Get an option for the socket. + * + * @param level the setting level + * @param name the name + * @throw SocketError on error + */ + template <typename Argument> + inline Argument get(int level, int name) + { + Argument desired, result{}; + socklen_t size = sizeof (result); + +#if defined(_WIN32) + if (getsockopt(m_handle, level, name, (Socket::Arg)&desired, &size) == SOCKET_ERROR) +#else + if (getsockopt(m_handle, level, name, (Socket::Arg)&desired, &size) < 0) +#endif + throw SocketError(SocketError::System, "get"); + + std::memcpy(&result, &desired, size); + + return result; + } + + /** + * Get the native handle. + * + * @return the handle + * @warning Not portable + */ + inline Handle handle() const noexcept + { + return m_handle; + } + + /** + * Get the socket state. + * + * @return + */ + inline SocketState state() const noexcept + { + return m_state; + } + + /** + * Bind to an address. + * + * @param address the address + * @throw SocketError on any error + */ + void bind(const SocketAddress &address); + + /** + * Set the blocking mode, if set to false, the socket will be marked + * **non-blocking**. + * + * @param block set to false to mark **non-blocking** + * @throw SocketError on any error + */ + void setBlockMode(bool block); + + /** + * Close the socket. + */ + virtual void close(); +}; + +/** + * Compare two sockets. + * + * @param s1 the first socket + * @param s2 the second socket + * @return true if they equals + */ +bool operator==(const Socket &s1, const Socket &s2); + +/** + * Compare two sockets, ideal for putting in a std::map. + * + * @param s1 the first socket + * @param s2 the second socket + * @return true if s1 < s2 + */ +bool operator<(const Socket &s1, const Socket &s2); + +#endif // !_SOCKET_NG_H_
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/C++/modules/Socket/SocketAddress.cpp Sun Mar 08 14:26:33 2015 +0100 @@ -0,0 +1,136 @@ +/* + * SocketAddress.cpp -- socket addresses management + * + * Copyright (c) 2013, 2014 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <algorithm> +#include <cstring> + +#include "Socket.h" +#include "SocketAddress.h" + +namespace address { + +/* -------------------------------------------------------- + * Internet implementation + * -------------------------------------------------------- */ + +Internet::Internet(const std::string &host, unsigned port, int domain) +{ + if (host == "*") { + if (domain == AF_INET6) { + sockaddr_in6 *ptr = (sockaddr_in6 *)&m_addr; + + ptr->sin6_addr = in6addr_any; + ptr->sin6_family = AF_INET6; + ptr->sin6_port = htons(port); + + m_addrlen = sizeof (sockaddr_in6); + } else { + sockaddr_in *ptr = (sockaddr_in *)&m_addr; + + ptr->sin_addr.s_addr = INADDR_ANY; + ptr->sin_family = AF_INET; + ptr->sin_port = htons(port); + + m_addrlen = sizeof (sockaddr_in); + } + } else { + addrinfo hints, *res; + + std::memset(&hints, 0, sizeof (addrinfo)); + hints.ai_family = domain; + + auto error = getaddrinfo(host.c_str(), std::to_string(port).c_str(), &hints, &res); + if (error != 0) + throw SocketError(SocketError::System, "getaddrinfo", gai_strerror(error)); + + std::memcpy(&m_addr, res->ai_addr, res->ai_addrlen); + m_addrlen = res->ai_addrlen; + freeaddrinfo(res); + } +} + +/* -------------------------------------------------------- + * Unix implementation + * -------------------------------------------------------- */ + +#if !defined(_WIN32) + +#include <sys/un.h> + +Unix::Unix(const std::string &path, bool rm) +{ + sockaddr_un *sun = (sockaddr_un *)&m_addr; + + // Silently remove the file even if it fails + if (rm) + ::remove(path.c_str()); + + // Copy the path + memset(sun->sun_path, 0, sizeof (sun->sun_path)); + strncpy(sun->sun_path, path.c_str(), sizeof (sun->sun_path) - 1); + + // Set the parameters + sun->sun_family = AF_UNIX; + m_addrlen = SUN_LEN(sun); +} + +#endif // _WIN32 + +} // !address + +/* -------------------------------------------------------- + * SocketAddress implementation + * -------------------------------------------------------- */ + +SocketAddress::SocketAddress() + : m_addrlen(0) +{ + memset(&m_addr, 0, sizeof (m_addr)); +} + +SocketAddress::SocketAddress(const sockaddr_storage &addr, socklen_t length) + : m_addr(addr) + , m_addrlen(length) +{ +} + +const sockaddr_storage &SocketAddress::address() const +{ + return m_addr; +} + +socklen_t SocketAddress::length() const +{ + return m_addrlen; +} + +bool operator<(const SocketAddress &s1, const SocketAddress &s2) +{ + const auto &array1 = reinterpret_cast<const unsigned char *>(&s1.address()); + const auto &array2 = reinterpret_cast<const unsigned char *>(&s2.address()); + + return std::lexicographical_compare(array1, array1 + s1.length(), array2, array2 + s2.length()); +} + +bool operator==(const SocketAddress &s1, const SocketAddress &s2) +{ + const auto &array1 = reinterpret_cast<const unsigned char *>(&s1.address()); + const auto &array2 = reinterpret_cast<const unsigned char *>(&s2.address()); + + return std::equal(array1, array1 + s1.length(), array2, array2 + s2.length()); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/C++/modules/Socket/SocketAddress.h Sun Mar 08 14:26:33 2015 +0100 @@ -0,0 +1,143 @@ +/* + * SocketAddress.h -- socket addresses management + * + * Copyright (c) 2013, 2014 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _SOCKET_ADDRESS_NG_H_ +#define _SOCKET_ADDRESS_NG_H_ + +#include <string> + +#if defined(_WIN32) +# include <Winsock2.h> +# include <Ws2tcpip.h> +#else +# include <sys/socket.h> +#endif + +/** + * @class SocketAddress + * @brief base class for socket addresses + * + * This class is mostly used to bind, connect or getting information + * on socket clients. + * + * @see Internet + * @see Unix + */ +class SocketAddress { +protected: + sockaddr_storage m_addr; + socklen_t m_addrlen; + +public: + /** + * Default constructor. + */ + SocketAddress(); + + /** + * Constructor with address and size. + * + * @param addr the address + * @param length the address length + */ + SocketAddress(const sockaddr_storage &addr, socklen_t length); + + /** + * Default destructor. + */ + virtual ~SocketAddress() = default; + + /** + * Get the address length + * + * @return the length + */ + socklen_t length() const; + + /** + * Get the address. + * + * @return the address + */ + const sockaddr_storage &address() const; + + /** + * Compare the addresses. The check is lexicographical. + * + * @param s1 the first address + * @param s2 the second address + * @return true if s1 is less than s2 + */ + friend bool operator<(const SocketAddress &s1, const SocketAddress &s2); + + /** + * Compare the addresses. + * + * @param s1 the first address + * @param s2 the second address + * @return true if s1 == s2 + */ + friend bool operator==(const SocketAddress &s1, const SocketAddress &s2); +}; + +namespace address { + +/** + * @class Internet + * @brief internet protocol connect class + * + * Create a connect address for internet protocol, + * using getaddrinfo(3). + */ +class Internet : public SocketAddress { +public: + /** + * Create an IPv4 or IPV6 end point. + * + * @param host the hostname + * @param port the port + * @param family AF_INET, AF_INET6, ... + * @throw SocketError on error + */ + Internet(const std::string &host, unsigned port, int family); +}; + +#if !defined(_WIN32) + +/** + * @class Unix + * @brief unix family sockets + * + * Create an address to a specific path. Only available on Unix. + */ +class Unix : public SocketAddress { +public: + /** + * Construct an address to a path. + * + * @param path the path + * @param rm remove the file before (default: false) + */ + Unix(const std::string &path, bool rm = false); +}; + +#endif // ! !_WIN32 + +} // !address + +#endif // !_SOCKET_ADDRESS_NG_H_
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/C++/modules/Socket/SocketListener.cpp Sun Mar 08 14:26:33 2015 +0100 @@ -0,0 +1,330 @@ +/* + * SocketListener.cpp -- portable select() wrapper + * + * Copyright (c) 2013, 2014 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <algorithm> +#include <map> +#include <set> +#include <utility> +#include <vector> + +#include "SocketListener.h" + +/* -------------------------------------------------------- + * Select implementation + * -------------------------------------------------------- */ + +namespace { + +/** + * @class SelectMethod + * @brief Implements select(2) + * + * This class is the fallback of any other method, it is not preferred at all for many reasons. + */ +class SelectMethod final : public SocketListenerInterface { +private: + std::map<Socket::Handle, std::pair<std::reference_wrapper<Socket>, int>> m_table; + +public: + void set(Socket &s, int direction) override + { + if (m_table.count(s.handle()) > 0) { + m_table.at(s.handle()).second |= direction; + } else { + m_table.insert({s.handle(), {s, direction}}); + } + + } + + void unset(Socket &s, int direction) override + { + if (m_table.count(s.handle()) != 0) { + m_table.at(s.handle()).second &= ~(direction); + + // If no read, no write is requested, remove it + if (m_table.at(s.handle()).second == 0) { + m_table.erase(s.handle()); + } + } + } + + void remove(Socket &sc) override + { + m_table.erase(sc.handle()); + } + + void clear() override + { + m_table.clear(); + } + + SocketStatus select(int ms) override + { + auto result = selectMultiple(ms); + + if (result.size() == 0) { + throw SocketError(SocketError::System, "select", "No socket found"); + } + + return result[0]; + } + + std::vector<SocketStatus> selectMultiple(int ms) override + { + timeval maxwait, *towait; + fd_set readset; + fd_set writeset; + + FD_ZERO(&readset); + FD_ZERO(&writeset); + + Socket::Handle max = 0; + + for (auto &s : m_table) { + if (s.second.second & SocketListener::Read) { + FD_SET(s.first, &readset); + } + if (s.second.second & SocketListener::Write) { + FD_SET(s.first, &writeset); + } + + if (s.first > max) { + max = s.first; + } + } + + maxwait.tv_sec = 0; + maxwait.tv_usec = ms * 1000; + + // Set to nullptr for infinite timeout. + towait = (ms < 0) ? nullptr : &maxwait; + + auto error = ::select(max + 1, &readset, &writeset, nullptr, towait); + if (error == Socket::Error) { + throw SocketError(SocketError::System, "select"); + } + if (error == 0) { + throw SocketError(SocketError::Timeout, "select", "Timeout while listening"); + } + + std::vector<SocketStatus> sockets; + + for (auto &c : m_table) { + if (FD_ISSET(c.first, &readset)) { + sockets.push_back({ c.second.first, SocketListener::Read }); + } + if (FD_ISSET(c.first, &writeset)) { + sockets.push_back({ c.second.first, SocketListener::Write }); + } + } + + return sockets; + } +}; + +} // !namespace + +/* -------------------------------------------------------- + * Poll implementation + * -------------------------------------------------------- */ + +#if defined(SOCKET_HAVE_POLL) + +#if defined(_WIN32) +# include <Winsock2.h> +# define poll WSAPoll +#else +# include <poll.h> +#endif + +namespace { + +class PollMethod final : public SocketListenerInterface { +private: + std::vector<pollfd> m_fds; + std::map<Socket::Handle, std::reference_wrapper<Socket>> m_lookup; + + inline short topoll(int direction) + { + short result(0); + + if (direction & SocketListener::Read) + result |= POLLIN; + if (direction & SocketListener::Write) + result |= POLLOUT; + + return result; + } + + inline int todirection(short event) + { + int direction{}; + + /* + * Poll implementations mark the socket differently regarding + * the disconnection of a socket. + * + * At least, even if POLLHUP or POLLIN is set, recv() always + * return 0 so we mark the socket as readable. + */ + if ((event & POLLIN) || (event & POLLHUP)) + direction |= SocketListener::Read; + if (event & POLLOUT) + direction |= SocketListener::Write; + + return direction; + } + +public: + void set(Socket &s, int direction) override + { + auto it = std::find_if(m_fds.begin(), m_fds.end(), [&] (const auto &pfd) { return pfd.fd == s.handle(); }); + + // If found, add the new direction, otherwise add a new socket + if (it != m_fds.end()) + it->events |= topoll(direction); + else { + m_lookup.insert({s.handle(), s}); + m_fds.push_back({ s.handle(), topoll(direction), 0 }); + } + } + + void unset(Socket &s, int direction) override + { + for (auto i = m_fds.begin(); i != m_fds.end();) { + if (i->fd == s.handle()) { + i->events &= ~(topoll(direction)); + + if (i->events == 0) { + m_lookup.erase(i->fd); + i = m_fds.erase(i); + } else { + ++i; + } + } else + ++i; + } + } + + void remove(Socket &s) override + { + auto it = std::find_if(m_fds.begin(), m_fds.end(), [&] (const auto &pfd) { return pfd.fd == s.handle(); }); + + if (it != m_fds.end()) { + m_fds.erase(it); + m_lookup.erase(s.handle()); + } + } + + void clear() override + { + m_fds.clear(); + m_lookup.clear(); + } + + SocketStatus select(int ms) override + { + auto result = poll(m_fds.data(), m_fds.size(), ms); + if (result == 0) + throw SocketError(SocketError::Timeout, "select", "Timeout while listening"); + if (result < 0) + throw SocketError(SocketError::System, "poll"); + + for (auto &fd : m_fds) { + if (fd.revents != 0) { + return { m_lookup.at(fd.fd), todirection(fd.revents) }; + } + } + + throw SocketError(SocketError::System, "select", "No socket found"); + } + + std::vector<SocketStatus> selectMultiple(int ms) override + { + auto result = poll(m_fds.data(), m_fds.size(), ms); + if (result == 0) { + throw SocketError(SocketError::Timeout, "select", "Timeout while listening"); + } + if (result < 0) { + throw SocketError(SocketError::System, "poll"); + } + + std::vector<SocketStatus> sockets; + for (auto &fd : m_fds) { + if (fd.revents != 0) { + sockets.push_back({ m_lookup.at(fd.fd), todirection(fd.revents) }); + } + } + + return sockets; + } +}; + +} // !namespace + +#endif // !_SOCKET_HAVE_POLL + +/* -------------------------------------------------------- + * SocketListener + * -------------------------------------------------------- */ + +const int SocketListener::Read{1 << 0}; +const int SocketListener::Write{1 << 1}; + +SocketListener::SocketListener(std::initializer_list<std::pair<std::reference_wrapper<Socket>, int>> list) + : SocketListener() +{ + for (const auto &p : list) + set(p.first, p.second); +} + +SocketListener::SocketListener(SocketMethod method) +{ +#if defined(SOCKET_HAVE_POLL) + if (method == SocketMethod::Poll) + m_interface = std::make_unique<PollMethod>(); + else +#endif + m_interface = std::make_unique<SelectMethod>(); + + (void)method; +} + +void SocketListener::set(Socket &sc, int flags) +{ + if (m_map.count(sc) > 0) { + m_map[sc] |= flags; + m_interface->set(sc, flags); + } else { + m_map.insert({sc, flags}); + m_interface->set(sc, flags); + } +} + +void SocketListener::unset(Socket &sc, int flags) noexcept +{ + if (m_map.count(sc) > 0) { + m_map[sc] &= ~(flags); + m_interface->unset(sc, flags); + + // No more flags, remove it + if (m_map[sc] == 0) { + m_map.erase(sc); + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/C++/modules/Socket/SocketListener.h Sun Mar 08 14:26:33 2015 +0100 @@ -0,0 +1,347 @@ +/* + * SocketListener.h -- portable select() wrapper + * + * Copyright (c) 2013, 2014 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _SOCKET_LISTENER_NG_H_ +#define _SOCKET_LISTENER_NG_H_ + +#include <chrono> +#include <functional> +#include <initializer_list> +#include <map> +#include <memory> +#include <utility> +#include <vector> + +#include "Socket.h" + +#if defined(_WIN32) +# if _WIN32_WINNT >= 0x0600 +# define SOCKET_HAVE_POLL +# endif +#else +# define SOCKET_HAVE_POLL +#endif + +/** + * @enum SocketMethod + * @brief The SocketMethod enum + * + * Select the method of polling. It is only a preferred method, for example if you + * request for poll but it is not available, select will be used. + */ +enum class SocketMethod { + Select, //!< select(2) method, fallback + Poll //!< poll(2), everywhere possible +}; + +/** + * @struct SocketStatus + * @brief The SocketStatus struct + * + * Result of a select call, returns the first ready socket found with its + * direction. + */ +class SocketStatus { +public: + Socket &socket; //!< which socket is ready + int direction; //!< the direction +}; + +/** + * @class SocketListenerInterface + * @brief Implement the polling method + */ +class SocketListenerInterface { +public: + /** + * Default destructor. + */ + virtual ~SocketListenerInterface() = default; + + /** + * Add a socket with a specified direction. + * + * @param s the socket + * @param direction the direction + */ + virtual void set(Socket &sc, int direction) = 0; + + /** + * Remove a socket with a specified direction. + * + * @param s the socket + * @param direction the direction + */ + virtual void unset(Socket &sc, int direction) = 0; + + /** + * Remove completely a socket. + * + * @param sc the socket to remove + */ + virtual void remove(Socket &sc) = 0; + + /** + * Remove all sockets. + */ + virtual void clear() = 0; + + /** + * Select one socket. + * + * @param ms the number of milliseconds to wait, -1 means forever + * @return the socket status + * @throw error::Failure on failure + * @throw error::Timeout on timeout + */ + virtual SocketStatus select(int ms) = 0; + + /** + * Select many sockets. + * + * @param ms the number of milliseconds to wait, -1 means forever + * @return a vector of ready sockets + * @throw error::Failure on failure + * @throw error::Timeout on timeout + */ + virtual std::vector<SocketStatus> selectMultiple(int ms) = 0; +}; + +/** + * @class SocketListener + * @brief Synchronous multiplexing + * + * Convenient wrapper around the select() system call. + * + * This class is implemented using a bridge pattern to allow different uses + * of listener implementation. + * + * Currently, poll and select() are available. + * + * This wrappers takes abstract sockets as non-const reference but it does not + * own them so you must take care that sockets are still alive until the + * SocketListener is destroyed. + */ +class SocketListener final { +public: +#if defined(SOCKET_HAVE_POLL) + static constexpr const SocketMethod PreferredMethod = SocketMethod::Poll; +#else + static constexpr const SocketMethod PreferredMethod = SocketMethod::Select; +#endif + + static const int Read; + static const int Write; + + using Map = std::map<std::reference_wrapper<Socket>, int>; + using Iface = std::unique_ptr<SocketListenerInterface>; + +private: + Map m_map; + Iface m_interface; + +public: + /** + * Move constructor. + * + * @param other the other object + */ + SocketListener(SocketListener &&other) = default; + + /** + * Move operator. + * + * @param other the other object + * @return this + */ + SocketListener &operator=(SocketListener &&other) = default; + + /** + * Create a socket listener. + * + * @param method the preferred method + */ + SocketListener(SocketMethod method = PreferredMethod); + + /** + * Create a listener from a list of sockets. + * + * @param list the list + */ + SocketListener(std::initializer_list<std::pair<std::reference_wrapper<Socket>, int>> list); + + /** + * Return an iterator to the beginning. + * + * @return the iterator + */ + inline auto begin() noexcept + { + return m_map.begin(); + } + + /** + * Overloaded function. + * + * @return the iterator + */ + inline auto begin() const noexcept + { + return m_map.begin(); + } + + /** + * Overloaded function. + * + * @return the iterator + */ + inline auto cbegin() const noexcept + { + return m_map.cbegin(); + } + + /** + * Return an iterator to the end. + * + * @return the iterator + */ + inline auto end() noexcept + { + return m_map.end(); + } + + /** + * Overloaded function. + * + * @return the iterator + */ + inline auto end() const noexcept + { + return m_map.end(); + } + + /** + * Overloaded function. + * + * @return the iterator + */ + inline auto cend() const noexcept + { + return m_map.cend(); + } + + /** + * Add a socket to the listener. + * + * @param sc the socket + * @param direction (may be OR'ed) + */ + void set(Socket &sc, int direction); + + /** + * Unset a socket from the listener, only the direction is removed + * unless the two directions are requested. + * + * For example, if you added a socket for both reading and writing, + * unsetting the write direction will keep the socket for reading. + * + * @param sc the socket + * @param direction the direction (may be OR'ed) + * @see remove + */ + void unset(Socket &sc, int direction) noexcept; + + /** + * Remove completely the socket from the listener. + * + * @param sc the socket + */ + inline void remove(Socket &sc) noexcept + { + m_map.erase(sc); + m_interface->remove(sc); + } + + /** + * Remove all sockets. + */ + inline void clear() noexcept + { + m_map.clear(); + m_interface->clear(); + } + + /** + * Get the number of sockets in the listener. + */ + unsigned size() const noexcept + { + return m_map.size(); + } + + /** + * Select a socket. Waits for a specific amount of time specified as the duration. + * + * @param duration the duration + * @return the socket ready + */ + template <typename Rep, typename Ratio> + inline SocketStatus select(const std::chrono::duration<Rep, Ratio> &duration) + { + auto cvt = std::chrono::duration_cast<std::chrono::milliseconds>(duration); + + return m_interface->select(cvt.count()); + } + + /** + * Overload with milliseconds. + * + * @param timeout the optional timeout in milliseconds + * @return the socket ready + */ + inline SocketStatus select(int timeout = -1) + { + return m_interface->select(timeout); + } + + /** + * Select multiple sockets. + * + * @param duration the duration + * @return the socket ready + */ + template <typename Rep, typename Ratio> + inline std::vector<SocketStatus> selectMultiple(const std::chrono::duration<Rep, Ratio> &duration) + { + auto cvt = std::chrono::duration_cast<std::chrono::milliseconds>(duration); + + return m_interface->selectMultiple(cvt.count()); + } + + /** + * Overload with milliseconds. + * + * @return the socket ready + */ + inline std::vector<SocketStatus> selectMultiple(int timeout = -1) + { + return m_interface->selectMultiple(timeout); + } +}; + +#endif // !_SOCKET_LISTENER_NG_H_
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/C++/modules/Socket/SocketSsl.cpp Sun Mar 08 14:26:33 2015 +0100 @@ -0,0 +1,228 @@ +/* + * SocketSsl.cpp -- OpenSSL extension for sockets + * + * Copyright (c) 2013, David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "SocketAddress.h" +#include "SocketListener.h" +#include "SocketSsl.h" + +namespace { + +const SSL_METHOD *sslMethod(int mflags) +{ + if (mflags & SocketSslOptions::All) + return SSLv23_method(); + if (mflags & SocketSslOptions::SSLv3) + return SSLv3_method(); + if (mflags & SocketSslOptions::TLSv1) + return TLSv1_method(); + + return SSLv23_method(); +} + +inline std::string sslError(int error) +{ + return ERR_reason_error_string(error); +} + +inline int toDirection(int error) +{ + if (error == SocketError::WouldBlockRead) + return SocketListener::Read; + if (error == SocketError::WouldBlockWrite) + return SocketListener::Write; + + return 0; +} + +} // !namespace + +std::mutex SocketSsl::s_sslMutex; +std::atomic<bool> SocketSsl::s_sslInitialized{false}; + +SocketSsl::SocketSsl(Socket::Handle handle, SSL_CTX *context, SSL *ssl) + : SocketAbstractTcp(handle) + , m_context(context, SSL_CTX_free) + , m_ssl(ssl, SSL_free) +{ +#if !defined(SOCKET_NO_SSL_INIT) + if (!s_sslInitialized) + sslInitialize(); +#endif +} + +SocketSsl::SocketSsl(int family, int protocol, SocketSslOptions options) + : SocketAbstractTcp(family, protocol) + , m_options(std::move(options)) +{ +#if !defined(SOCKET_NO_SSL_INIT) + if (!s_sslInitialized) + sslInitialize(); +#endif +} + +void SocketSsl::connect(const SocketAddress &address) +{ + standardConnect(address); + + // Context first + auto context = SSL_CTX_new(sslMethod(m_options.method)); + + m_context = ContextHandle(context, SSL_CTX_free); + + // SSL object then + auto ssl = SSL_new(context); + + m_ssl = SslHandle(ssl, SSL_free); + + SSL_set_fd(ssl, m_handle); + + auto ret = SSL_connect(ssl); + + if (ret <= 0) { + auto error = SSL_get_error(ssl, ret); + + if (error == SSL_ERROR_WANT_READ) { + throw SocketError(SocketError::WouldBlockRead, "connect", "Operation in progress"); + } else if (error == SSL_ERROR_WANT_WRITE) { + throw SocketError(SocketError::WouldBlockWrite, "connect", "Operation in progress"); + } else { + throw SocketError(SocketError::System, "connect", sslError(error)); + } + } + + m_state = SocketState::Connected; +} + +void SocketSsl::waitConnect(const SocketAddress &address, int timeout) +{ + try { + // Initial try + connect(address); + } catch (const SocketError &ex) { + if (ex.code() == SocketError::WouldBlockRead || ex.code() == SocketError::WouldBlockWrite) { + SocketListener listener{{*this, toDirection(ex.code())}}; + + listener.select(timeout); + + // Second try + connect(address); + } else { + throw; + } + } +} + +SocketSsl SocketSsl::accept() +{ + SocketAddress dummy; + + return accept(dummy); +} + +SocketSsl SocketSsl::accept(SocketAddress &info) +{ + auto client = standardAccept(info); + auto context = SSL_CTX_new(sslMethod(m_options.method)); + + if (m_options.certificate.size() > 0) + SSL_CTX_use_certificate_file(context, m_options.certificate.c_str(), SSL_FILETYPE_PEM); + if (m_options.privateKey.size() > 0) + SSL_CTX_use_PrivateKey_file(context, m_options.privateKey.c_str(), SSL_FILETYPE_PEM); + if (m_options.verify && !SSL_CTX_check_private_key(context)) { + client.close(); + throw SocketError(SocketError::System, "accept", "certificate failure"); + } + + // SSL object + auto ssl = SSL_new(context); + + SSL_set_fd(ssl, client.handle()); + + auto ret = SSL_accept(ssl); + + if (ret <= 0) { + auto error = SSL_get_error(ssl, ret); + + if (error == SSL_ERROR_WANT_READ) { + throw SocketError(SocketError::WouldBlockRead, "accept", "Operation would block"); + } else if (error == SSL_ERROR_WANT_WRITE) { + throw SocketError(SocketError::WouldBlockWrite, "accept", "Operation would block"); + } else { + throw SocketError(SocketError::System, "accept", sslError(error)); + } + } + + return SocketSsl(client.handle(), context, ssl); +} + +unsigned SocketSsl::recv(void *data, unsigned len) +{ + auto nbread = SSL_read(m_ssl.get(), data, len); + + if (nbread <= 0) { + auto error = SSL_get_error(m_ssl.get(), nbread); + + if (error == SSL_ERROR_WANT_READ) { + throw SocketError(SocketError::WouldBlockRead, "recv", "Operation would block"); + } else if (error == SSL_ERROR_WANT_WRITE) { + throw SocketError(SocketError::WouldBlockWrite, "recv", "Operation would block"); + } else { + throw SocketError(SocketError::System, "recv", sslError(error)); + } + } + + return nbread; +} + +unsigned SocketSsl::waitRecv(void *data, unsigned len, int timeout) +{ + SocketListener listener{{*this, SocketListener::Read}}; + + listener.select(timeout); + + return recv(data, len); +} + +unsigned SocketSsl::send(const void *data, unsigned len) +{ + auto nbread = SSL_write(m_ssl.get(), data, len); + + if (nbread <= 0) { + auto error = SSL_get_error(m_ssl.get(), nbread); + + if (error == SSL_ERROR_WANT_READ) { + throw SocketError(SocketError::WouldBlockRead, "send", "Operation would block"); + } else if (error == SSL_ERROR_WANT_WRITE) { + throw SocketError(SocketError::WouldBlockWrite, "send", "Operation would block"); + } else { + throw SocketError(SocketError::System, "send", sslError(error)); + } + } + + return nbread; +} + +unsigned SocketSsl::waitSend(const void *data, unsigned len, int timeout) +{ + SocketListener listener{{*this, SocketListener::Write}}; + + listener.select(timeout); + + return send(data, len); +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/C++/modules/Socket/SocketSsl.h Sun Mar 08 14:26:33 2015 +0100 @@ -0,0 +1,215 @@ +/* + * SocketSsl.h -- OpenSSL extension for sockets + * + * Copyright (c) 2013, David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _SOCKET_SSL_NG_H_ +#define _SOCKET_SSL_NG_H_ + +#include <atomic> +#include <mutex> + +#include <openssl/err.h> +#include <openssl/evp.h> +#include <openssl/ssl.h> + +#include "SocketTcp.h" + +/** + * @class SocketSslOptions + * @brief Options for SocketSsl + */ +class SocketSslOptions { +public: + /** + * @brief Method + */ + enum { + SSLv3 = (1 << 0), + TLSv1 = (1 << 1), + All = (0xf) + }; + + int method{All}; //!< The method + std::string certificate; //!< The certificate path + std::string privateKey; //!< The private key file + bool verify{false}; //!< Verify or not + + /** + * Default constructor. + */ + SocketSslOptions() = default; + + /** + * More advanced constructor. + * + * @param method the method requested + * @param certificate the certificate file + * @param key the key file + * @param verify set to true to verify + */ + SocketSslOptions(int method, std::string certificate, std::string key, bool verify = false) + : method(method) + , certificate(std::move(certificate)) + , privateKey(std::move(key)) + , verify(verify) + { + } +}; + +/** + * @class SocketSsl + * @brief SSL interface for sockets + * + * This class derives from SocketAbstractTcp and provide SSL support through OpenSSL. + */ +class SocketSsl : public SocketAbstractTcp { +public: + using ContextHandle = std::unique_ptr<SSL_CTX, void (*)(SSL_CTX *)>; + using SslHandle = std::unique_ptr<SSL, void (*)(SSL *)>; + +private: + static std::mutex s_sslMutex; + static std::atomic<bool> s_sslInitialized; + + ContextHandle m_context{nullptr, nullptr}; + SslHandle m_ssl{nullptr, nullptr}; + SocketSslOptions m_options; + +public: + using SocketAbstractTcp::recv; + using SocketAbstractTcp::waitRecv; + using SocketAbstractTcp::send; + using SocketAbstractTcp::waitSend; + + /** + * Close OpenSSL library. + */ + static inline void sslTerminate() + { + ERR_free_strings(); + } + + /** + * Open SSL library. + */ + static inline void sslInitialize() + { + std::lock_guard<std::mutex> lock(s_sslMutex); + + if (!s_sslInitialized) { + s_sslInitialized = true; + + SSL_library_init(); + SSL_load_error_strings(); + + std::atexit(sslTerminate); + } + } + + /** + * Create a SocketSsl from an already created one. + * + * @param handle the native handle + * @param context the context + * @param ssl the ssl object + */ + SocketSsl(Socket::Handle handle, SSL_CTX *context, SSL *ssl); + + /** + * Open a SSL socket with the specified family. Automatically + * use SOCK_STREAM as the type. + * + * @param family the family + * @param options the options + */ + SocketSsl(int family, int protocol, SocketSslOptions options = {}); + + /** + * Accept a SSL TCP socket. + * + * @return the socket + * @throw SocketError on error + */ + SocketSsl accept(); + + /** + * Accept a SSL TCP socket. + * + * @param info the client information + * @return the socket + * @throw SocketError on error + */ + SocketSsl accept(SocketAddress &info); + + /** + * Accept a SSL TCP socket. + * + * @param timeout the maximum timeout in milliseconds + * @return the socket + * @throw SocketError on error + */ + SocketSsl waitAccept(int timeout); + + /** + * Accept a SSL TCP socket. + * + * @param info the client information + * @param timeout the maximum timeout in milliseconds + * @return the socket + * @throw SocketError on error + */ + SocketSsl waitAccept(SocketAddress &info, int timeout); + + /** + * Connect to an end point. + * + * @param address the address + * @throw SocketError on error + */ + void connect(const SocketAddress &address); + + /** + * Connect to an end point. + * + * @param timeout the maximum timeout in milliseconds + * @param address the address + * @throw SocketError on error + */ + void waitConnect(const SocketAddress &address, int timeout); + + /** + * @copydoc SocketAbstractTcp::recv + */ + unsigned recv(void *data, unsigned length) override; + + /** + * @copydoc SocketAbstractTcp::recv + */ + unsigned waitRecv(void *data, unsigned length, int timeout) override; + + /** + * @copydoc SocketAbstractTcp::recv + */ + unsigned send(const void *data, unsigned length) override; + + /** + * @copydoc SocketAbstractTcp::recv + */ + unsigned waitSend(const void *data, unsigned length, int timeout) override; +}; + +#endif // !_SOCKET_SSL_NG_H_
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/C++/modules/Socket/SocketTcp.cpp Sun Mar 08 14:26:33 2015 +0100 @@ -0,0 +1,227 @@ +/* + * SocketTcp.cpp -- portable C++ socket wrappers + * + * Copyright (c) 2013, 2014 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "SocketAddress.h" +#include "SocketListener.h" +#include "SocketTcp.h" + +/* -------------------------------------------------------- + * SocketAbstractTcp + * -------------------------------------------------------- */ + +void SocketAbstractTcp::listen(int max) +{ + if (::listen(m_handle, max) == Error) + throw SocketError(SocketError::System, "listen"); +} + +Socket SocketAbstractTcp::standardAccept(SocketAddress &info) +{ + Socket::Handle handle; + + // Store the information + sockaddr_storage address; + socklen_t addrlen; + + addrlen = sizeof (sockaddr_storage); + handle = ::accept(m_handle, reinterpret_cast<sockaddr *>(&address), &addrlen); + + if (handle == Invalid) { +#if defined(_WIN32) + int error = WSAGetLastError(); + + if (error == WSAEWOULDBLOCK) + throw SocketError(SocketError::WouldBlockRead, "accept", error); + + throw SocketError(SocketError::System, "accept", error); +#else + if (errno == EAGAIN || errno == EWOULDBLOCK) + throw SocketError(SocketError::WouldBlockRead, "accept"); + + throw SocketError(SocketError::System, "accept"); +#endif + } + + info = SocketAddress(address, addrlen); + + return Socket(handle); +} + +void SocketAbstractTcp::standardConnect(const SocketAddress &address) +{ + if (m_state == SocketState::Connected) + return; + + auto &sa = address.address(); + auto addrlen = address.length(); + + if (::connect(m_handle, reinterpret_cast<const sockaddr *>(&sa), addrlen) == Error) { + /* + * Determine if the error comes from a non-blocking connect that cannot be + * accomplished yet. + */ +#if defined(_WIN32) + int error = WSAGetLastError(); + + if (error == WSAEWOULDBLOCK) + throw SocketError(SocketError::WouldBlockWrite, "connect", error); + + throw SocketError(SocketError::System, "connect", error); +#else + if (errno == EINPROGRESS) + throw SocketError(SocketError::WouldBlockWrite, "connect"); + + throw SocketError(SocketError::System, "connect"); +#endif + } + + m_state = SocketState::Connected; +} + +/* -------------------------------------------------------- + * SocketTcp + * -------------------------------------------------------- */ + +SocketTcp SocketTcp::accept() +{ + SocketAddress dummy; + + return accept(dummy); +} + +SocketTcp SocketTcp::accept(SocketAddress &info) +{ + return standardAccept(info); +} + +void SocketTcp::connect(const SocketAddress &address) +{ + return standardConnect(address); +} + +void SocketTcp::waitConnect(const SocketAddress &address, int timeout) +{ + if (m_state == SocketState::Connected) + return; + + // Initial try + try { + connect(address); + } catch (const SocketError &ex) { + if (ex.code() == SocketError::WouldBlockWrite) { + SocketListener listener{{*this, SocketListener::Write}}; + + listener.select(timeout); + + // Socket is writable? Check if there is an error + + int error = get<int>(SOL_SOCKET, SO_ERROR); + + if (error) { + throw SocketError(SocketError::System, "connect", error); + } + } else { + throw; + } + } + + m_state = SocketState::Connected; +} + +SocketTcp SocketTcp::waitAccept(int timeout) +{ + SocketAddress dummy; + + return waitAccept(dummy, timeout); +} + +SocketTcp SocketTcp::waitAccept(SocketAddress &info, int timeout) +{ + SocketListener listener{{*this, SocketListener::Read}}; + + listener.select(timeout); + + return accept(info); +} + +unsigned SocketTcp::recv(void *data, unsigned dataLen) +{ + int nbread; + + nbread = ::recv(m_handle, (Socket::Arg)data, dataLen, 0); + if (nbread == Error) { +#if defined(_WIN32) + int error = WSAGetLastError(); + + if (error == WSAEWOULDBLOCK) + throw SocketError(SocketError::WouldBlockRead, "recv", error) + + throw SocketError(SocketError::System, "recv", error); +#else + if (errno == EAGAIN || errno == EWOULDBLOCK) + throw SocketError(SocketError::WouldBlockRead, "recv"); + + throw SocketError(SocketError::System, "recv"); +#endif + } else if (nbread == 0) + m_state = SocketState::Closed; + + return (unsigned)nbread; +} + +unsigned SocketTcp::waitRecv(void *data, unsigned length, int timeout) +{ + SocketListener listener{{*this, SocketListener::Read}}; + + listener.select(timeout); + + return recv(data, length); +} + +unsigned SocketTcp::send(const void *data, unsigned length) +{ + int nbsent; + + nbsent = ::send(m_handle, (Socket::ConstArg)data, length, 0); + if (nbsent == Error) { +#if defined(_WIN32) + int error = WSAGetLastError(); + + if (error == WSAEWOULDBLOCK) + throw SocketError(SocketError::WouldBlockWrite, "send", error); + + throw SocketError(SocketError::System, "send", error); +#else + if (errno == EAGAIN || errno == EWOULDBLOCK) + throw SocketError(SocketError::WouldBlockWrite, "send"); + + throw SocketError(SocketError::System, "send"); +#endif + } + + return (unsigned)nbsent; +} + +unsigned SocketTcp::waitSend(const void *data, unsigned length, int timeout) +{ + SocketListener listener{{*this, SocketListener::Write}}; + + listener.select(timeout); + + return send(data, length); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/C++/modules/Socket/SocketTcp.h Sun Mar 08 14:26:33 2015 +0100 @@ -0,0 +1,261 @@ +/* + * SocketTcp.h -- portable C++ socket wrappers + * + * Copyright (c) 2013, 2014 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _SOCKET_TCP_NG_H_ +#define _SOCKET_TCP_NG_H_ + +#include "Socket.h" + +/** + * @class SocketAbstractTcp + * @brief Base class for TCP sockets + * + * This abstract class provides standard TCP functions for both clear + * and SSL implementation. + * + * It does not contain default accept() and connect() because they varies too + * much between standard and SSL. Also, the accept() function return different + * types. + */ +class SocketAbstractTcp : public Socket { +protected: + Socket standardAccept(SocketAddress &address); + void standardConnect(const SocketAddress &address); + +public: + /** + * Construct an abstract socket from an already made socket. + * + * @param s the socket + */ + inline SocketAbstractTcp(Socket s) + : Socket(s) + { + } + + /** + * Construct a standard TCP socket. The type is automatically + * set to SOCK_STREAM. + * + * @param domain the domain + * @param protocol the protocol + * @throw SocketError on error + */ + inline SocketAbstractTcp(int domain, int protocol) + : Socket(domain, SOCK_STREAM, protocol) + { + } + + /** + * Listen for pending connection. + * + * @param max the maximum number + */ + void listen(int max = 128); + + /** + * Overloaded function. + * + * @param count the number of bytes to receive + * @return the string + * @throw SocketError on error + */ + inline std::string recv(unsigned count) + { + std::string result; + + result.resize(count); + auto n = recv(const_cast<char *>(result.data()), count); + result.resize(n); + + return result; + } + + /** + * Overloaded function. + * + * @param count the number of bytes to receive + * @param timeout the maximum timeout in milliseconds + * @return the string + * @throw SocketError on error + */ + inline std::string waitRecv(unsigned count, int timeout) + { + std::string result; + + result.resize(count); + auto n = waitRecv(const_cast<char *>(result.data()), count, timeout); + result.resize(n); + + return result; + } + + /** + * Overloaded function. + * + * @param data the string to send + * @return the number of bytes sent + * @throw SocketError on error + */ + inline unsigned send(const std::string &data) + { + return send(data.c_str(), data.size()); + } + + /** + * Overloaded function. + * + * @param data the string to send + * @param timeout the maximum timeout in milliseconds + * @return the number of bytes sent + * @throw SocketError on error + */ + inline unsigned waitSend(const std::string &data, int timeout) + { + return waitSend(data.c_str(), data.size(), timeout); + } + + /** + * Receive data. + * + * @param data the destination buffer + * @param length the buffer length + * @return the number of bytes received + * @throw SocketError on error + */ + virtual unsigned recv(void *data, unsigned length) = 0; + + /** + * Receive data. + * + * @param data the destination buffer + * @param length the buffer length + * @param timeout the maximum timeout in milliseconds + * @return the number of bytes received + * @throw SocketError on error + */ + virtual unsigned waitRecv(void *data, unsigned length, int timeout) = 0; + + /** + * Send data. + * + * @param data the buffer + * @param length the buffer length + * @return the number of bytes sent + * @throw SocketError on error + */ + virtual unsigned send(const void *data, unsigned length) = 0; + + /** + * Send data. + * + * @param data the buffer + * @param length the buffer length + * @return the number of bytes sent + * @throw SocketError on error + */ + virtual unsigned waitSend(const void *data, unsigned length, int timeout) = 0; +}; + +/** + * @class SocketTcp + * @brief End-user class for TCP sockets + */ +class SocketTcp : public SocketAbstractTcp { +public: + using SocketAbstractTcp::SocketAbstractTcp; + using SocketAbstractTcp::recv; + using SocketAbstractTcp::waitRecv; + using SocketAbstractTcp::send; + using SocketAbstractTcp::waitSend; + + /** + * Accept a clear TCP socket. + * + * @return the socket + * @throw SocketError on error + */ + SocketTcp accept(); + + /** + * Accept a clear TCP socket. + * + * @param info the client information + * @return the socket + * @throw SocketError on error + */ + SocketTcp accept(SocketAddress &info); + + /** + * Accept a clear TCP socket. + * + * @param timeout the maximum timeout in milliseconds + * @return the socket + * @throw SocketError on error + */ + SocketTcp waitAccept(int timeout); + + /** + * Accept a clear TCP socket. + * + * @param info the client information + * @param timeout the maximum timeout in milliseconds + * @return the socket + * @throw SocketError on error + */ + SocketTcp waitAccept(SocketAddress &info, int timeout); + + /** + * Connect to an end point. + * + * @param address the address + * @throw SocketError on error + */ + void connect(const SocketAddress &address); + + /** + * Connect to an end point. + * + * @param timeout the maximum timeout in milliseconds + * @param address the address + * @throw SocketError on error + */ + void waitConnect(const SocketAddress &address, int timeout); + + /** + * @copydoc SocketAbstractTcp::recv + */ + unsigned recv(void *data, unsigned length) override; + + /** + * @copydoc SocketAbstractTcp::waitRecv + */ + unsigned waitRecv(void *data, unsigned length, int timeout) override; + + /** + * @copydoc SocketAbstractTcp::send + */ + unsigned send(const void *data, unsigned length) override; + + /** + * @copydoc SocketAbstractTcp::waitSend + */ + unsigned waitSend(const void *data, unsigned length, int timeout) override; +}; + +#endif // !_SOCKET_TCP_NG_H_
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/C++/modules/Socket/SocketUdp.cpp Sun Mar 08 14:26:33 2015 +0100 @@ -0,0 +1,81 @@ +/* + * SocketUdp.cpp -- portable C++ socket wrappers + * + * Copyright (c) 2013, 2014 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "SocketAddress.h" +#include "SocketUdp.h" + +SocketUdp::SocketUdp(int domain, int protocol) + : Socket(domain, SOCK_DGRAM, protocol) +{ +} + +unsigned SocketUdp::recvfrom(void *data, unsigned length, SocketAddress &info) +{ + int nbread; + + // Store information + sockaddr_storage address; + socklen_t addrlen; + + addrlen = sizeof (struct sockaddr_storage); + nbread = ::recvfrom(m_handle, (Socket::Arg)data, length, 0, (sockaddr *)&address, &addrlen); + + info = SocketAddress(address, addrlen); + + if (nbread == Error) { +#if defined(_WIN32) + int error = WSAGetLastError(); + + if (error == WSAEWOULDBLOCK) + throw SocketError(SocketError::WouldBlockRead, "recvfrom", error); + + throw SocketError(SocketError::System, "recvfrom", error); +#else + if (errno == EAGAIN || errno == EWOULDBLOCK) + throw SocketError(SocketError::WouldBlockRead, "recvfrom"); + + throw SocketError(SocketError::System, "recvfrom"); +#endif + } + + return (unsigned)nbread; +} + +unsigned SocketUdp::sendto(const void *data, unsigned length, const SocketAddress &info) +{ + int nbsent; + + nbsent = ::sendto(m_handle, (Socket::ConstArg)data, length, 0, (const sockaddr *)&info.address(), info.length()); + if (nbsent == Error) { +#if defined(_WIN32) + int error = WSAGetLastError(); + + if (error == WSAEWOULDBLOCK) + throw SocketError(SocketError::WouldBlockWrite, "sendto", error); + + throw SocketError(SocketError::System, "sendto", error); +#else + if (errno == EAGAIN || errno == EWOULDBLOCK) + throw SocketError(SocketError::WouldBlockWrite, "sendto"); + + throw SocketError(SocketError::System, "sendto"); +#endif + } + + return (unsigned)nbsent; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/C++/modules/Socket/SocketUdp.h Sun Mar 08 14:26:33 2015 +0100 @@ -0,0 +1,93 @@ +/* + * SocketUdp.h -- portable C++ socket wrappers + * + * Copyright (c) 2013, 2014 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _SOCKET_UDP_NG_H_ +#define _SOCKET_UDP_NG_H_ + +#include "Socket.h" + +/** + * @class SocketUdp + * @brief UDP implementation for sockets + */ +class SocketUdp : public Socket { +public: + /** + * Construct a UDP socket. The type is automatically set to SOCK_DGRAM. + * + * @param domain the domain (e.g AF_INET) + * @param protocol the protocol (usually 0) + */ + SocketUdp(int domain, int protocol); + + /** + * Overloaded function. + * + * @param data the data + * @param address the address + * @return the number of bytes sent + * @throw SocketError on error + */ + inline unsigned sendto(const std::string &data, const SocketAddress &address) + { + return sendto(data.c_str(), data.length(), address); + } + + /** + * Overloaded function. + * + * @param data the data + * @param info the client information + * @return the string + * @throw SocketError on error + */ + inline std::string recvfrom(unsigned count, SocketAddress &info) + { + std::string result; + + result.resize(count); + auto n = recvfrom(const_cast<char *>(result.data()), count, info); + result.resize(n); + + return result; + } + + /** + * Receive data from an end point. + * + * @param data the destination buffer + * @param length the buffer length + * @param info the client information + * @return the number of bytes received + * @throw SocketError on error + */ + virtual unsigned recvfrom(void *data, unsigned length, SocketAddress &info); + + /** + * Send data to an end point. + * + * @param data the buffer + * @param length the buffer length + * @param address the client address + * @return the number of bytes sent + * @throw SocketError on error + */ + virtual unsigned sendto(const void *data, unsigned length, const SocketAddress &address); +}; + +#endif // !_SOCKET_UDP_NG_H_
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/C++/modules/Treenode/TreeNode.h Sun Mar 08 14:26:33 2015 +0100 @@ -0,0 +1,524 @@ +/* + * TreeNode.h -- C++11 pointer-free N-ary tree + * + * Copyright (c) 2013, 2014 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _TREE_NODE_H_ +#define _TREE_NODE_H_ + +/** + * @file TreeNode.h + * @brief N-ary tree without pointers + */ + +#include <algorithm> +#include <deque> +#include <memory> +#include <stdexcept> +#include <type_traits> + +namespace { + +/** + * @class TypeTraits + * @brief Some checks on the type + * + * Provides the following members depending on the type: + * + * equalityComparable - If == comparison can be performed + */ +template <typename T> +class TypeTraits { +private: + /** + * @class HasEqualsTo + * @brief Check if the type is comparable + * + * Sets to true if the type T can is equality comparable. + */ + template <typename U> + class HasEqualsTo { + public: + using Yes = char [2]; + using No = char [1]; + + static_assert(sizeof (char) != sizeof (long), "buy a new compiler"); + + template <typename Value> + static constexpr Yes &check(Value *u, decltype(*u == *u) * = nullptr); + + static constexpr No &check(...); + + static constexpr const bool value = sizeof (check((U *)0)) == sizeof (Yes); + }; + +public: + static constexpr const bool equalityComparable = HasEqualsTo<T>::value; +}; + +} // !namespace + +/** + * @class TreeNode + * @brief Safe C++11 N-ary tree + * + * This class use a std::deque as the container, it allocate object on the heap to avoid useless + * copy and move when adding new elements. + */ +template <typename T> +class TreeNode { +private: + class Object { + public: + virtual T &get() = 0; + virtual std::unique_ptr<Object> clone() const = 0; + }; + + template <typename Value> + struct Proxy final : public Object { + private: + Value m_value; + + Proxy(const Proxy &) = delete; + Proxy &operator=(const Proxy &) = delete; + Proxy(Proxy &&) = delete; + Proxy &operator=(Proxy &&) = delete; + + public: + inline Proxy(Value &&value) + : m_value(std::move(value)) + { + } + + inline Proxy(const Value &value) + : m_value(value) + { + } + + template <typename... Args> + inline Proxy(Args&&... args) + : m_value(std::forward<Args>(args)...) + { + } + + T &get() override + { + return m_value; + } + + std::unique_ptr<Object> clone() const override + { + return std::make_unique<Proxy<Value>>(m_value); + } + }; + + using Container = std::deque<std::unique_ptr<Object>>; + + TreeNode *m_parent{nullptr}; + Container m_children; + +public: + /** + * Default constructor. + */ + TreeNode() = default; + + /** + * Default destructor. + */ + virtual ~TreeNode() = default; + + /** + * Copy constructor. + * + * @param other the other node + */ + TreeNode(const TreeNode &other) + : m_parent(nullptr) + { + for (const auto &c : other.m_children) { + m_children.push_back(c->clone()); + m_children.back()->get().m_parent = this; + } + } + + /** + * Move constructor. + * + * @param other the other node + */ + TreeNode(TreeNode &&other) + : m_parent(nullptr) + , m_children(std::move(other.m_children)) + { + // Update children to update to *this + for (auto &c : m_children) + c->get().m_parent = this; + + other.m_children.clear(); + } + + /** + * Copy assignment operator. + * + * @param other the other node + */ + TreeNode &operator=(const TreeNode &other) + { + m_children.clear(); + + for (const auto &c : other.m_children) { + m_children.push_back(c->clone()); + m_children.back()->get().m_parent = this; + } + + return *this; + } + + /** + * Move assignment operator. + * + * @param other the other node + */ + TreeNode &operator=(TreeNode &&other) + { + m_children = std::move(other.m_children); + + // Update children to update to *this + for (auto &c : m_children) + c->get().m_parent = this; + + other.m_children.clear(); + + return *this; + } + + /** + * Add a child node to the beginning. + * + * @param child the children + */ + template <typename Value> + inline void push(const Value &child) + { + m_children.push_front(std::make_unique<Proxy<Value>>(child)); + m_children.front()->get().m_parent = this; + } + + /** + * Move to the beginning. + * + * @param child the children + */ + template <typename Value> + inline void push(Value &&child, typename std::enable_if<std::is_rvalue_reference<Value &&>::value>::type * = nullptr) + { + using Type = typename std::decay<Value>::type; + + m_children.push_front(std::make_unique<Proxy<Type>>(std::move(child))); + m_children.front()->get().m_parent = this; + } + + /** + * Construct an element at the beginning in place. + * + * @param args the arguments + */ + template <typename... Args> + void pushNew(Args&&... args) + { + m_children.emplace_front(std::make_unique<Proxy<T>>(std::forward<Args>(args)...)); + m_children.front()->get().m_parent = this; + } + + /** + * Add a child node to the end + * + * @param child the children + */ + template <typename Value> + inline void append(const Value &child) + { + m_children.push_back(std::make_unique<Proxy<Value>>(child)); + m_children.back()->get().m_parent = this; + } + + /** + * Move a child node to the end + * + * @param child the children + */ + template <typename Value> + inline void append(Value &&child, typename std::enable_if<std::is_rvalue_reference<Value &&>::value>::type * = nullptr) + { + using Type = typename std::decay<Value>::type; + + m_children.push_back(std::make_unique<Proxy<Type>>(std::move(child))); + m_children.back()->get().m_parent = this; + } + + /** + * Construct an element at the end in place. + * + * @param args the arguments + */ + template <typename... Args> + void appendNew(Args&&... args) + { + m_children.emplace_back(std::make_unique<Proxy<T>>(std::forward<Args>(args)...)); + m_children.back()->get().m_parent = this; + } + + /** + * Count the number of children in this node. + * + * @return the number of children + */ + unsigned countChildren() const noexcept + { + return static_cast<unsigned>(m_children.size()); + } + + /** + * Get the parent node. + * + * @return the parent node + * @throw std::out_of_range if there is no parent + */ + T &parent() + { + if (!m_parent) + throw std::out_of_range("no parent"); + + return static_cast<T &>(*m_parent); + } + + /** + * Get the parent node. + * + * @return the parent node + * @throw std::out_of_range if there is no parent + */ + const T &parent() const + { + if (!m_parent) + throw std::out_of_range("no parent"); + + return static_cast<const T &>(*m_parent); + } + + /** + * Check if the node is root (no parent). + * + * @return true if root + */ + bool isRoot() const noexcept + { + return m_parent == nullptr; + } + + /** + * Check if the node is leaf (no children). + * + * @return true if leaf + */ + bool isLeaf() const noexcept + { + return m_children.size() == 0; + } + + /** + * Remove a child from the node at the given index. + * + * @param index the position index + * @throw std::out_of_range if index is out of bounds + */ + void remove(int index) + { + if (index < 0 || index >= static_cast<int>(m_children.size())) + throw std::out_of_range("index is out of range"); + + m_children.erase(m_children.begin() + index); + } + + /** + * Remove a child from the node, the child must exists and no comparison test is performed, only + * object addresses are compared. + * + * @param value the value that exists in the node + * @warn the removed object must not be used after the call + */ + void remove(T &value) + { + m_children.erase(std::remove_if(m_children.begin(), m_children.end(), [&] (auto &p) { + return &p->get() == &value; + }), m_children.end()); + } + + /** + * Remove a child from the node, the value is tested using operator== and therefore may not exist in the container. + * + * @param value the value that can be compared + * @warn the removed object must not be used after the call + */ + template <typename Value> + void removeSame(const Value &value, typename std::enable_if<TypeTraits<Value>::equalityComparable>::type * = nullptr) + { + m_children.erase(std::remove_if(m_children.begin(), m_children.end(), [&] (auto &p) { + return p->get() == value; + }), m_children.end()); + } + + /** + * Remove all children. + */ + void clear() + { + m_children.clear(); + } + + /** + * Find a child in this node, the child address is used as the comparison so no equality operator is even called. + * + * @param child the child + * @return the index or -1 if not found + * @see indexOfSame + */ + int indexOf(const T &child) const noexcept + { + for (unsigned i = 0; i < m_children.size(); ++i) + if (&m_children[i]->get() == &child) + return i; + + return -1; + } + + /** + * Find the index of a node that is equality comparable to value but may be not in the node. + * + * @param value the value to compare + * @return the index or -1 if not found + * @see indexOf + */ + template <typename Value> + int indexOfSame(const Value &value, typename std::enable_if<TypeTraits<Value>::equalityComparable>::type * = nullptr) const noexcept + { + for (unsigned i = 0; i < m_children.size(); ++i) + if (m_children[i]->get() == value) + return i; + + return -1; + } + + /** + * Iterate over all the nodes. The first node is also passed through + * the callback. + * + * @param callback the callback + */ + template <typename Callback> + void map(Callback callback) + { + callback(static_cast<T &>(*this)); + + for (auto &v : m_children) + v->get().map(callback); + } + + /** + * Convert the tree to a flat list by appending to the output iterator. + * + * @param dest the destination iterator + */ + template <typename OutputIt> + void flat(OutputIt dest) + { + map([&] (const auto &value) { + *dest++ = value; + }); + } + + /** + * Iterate all values and call the function when found. + * + * @param predicate the predicate + * @param callable the callable to call when found + * @return true if found + */ + template <typename UnaryPredicate, typename Callable> + bool search(UnaryPredicate predicate, Callable callable) + { + if (predicate(static_cast<const T &>(*this))) { + callable(static_cast<T &>(*this)); + return true; + } + + for (auto &v : m_children) { + if (v->get().search(predicate, callable)) + return true; + } + + return false; + } + + /** + * Search and return the first value matching the predicate. + * + * @param predicate the predicate + * @return the reference to the node + * @throw std::out_of_range if not found + */ + template <typename UnaryPredicate> + T &search(UnaryPredicate predicate) + { + T *value{nullptr}; + + search(predicate, [&] (auto &ptr) { + value = &ptr; + }); + + if (value == nullptr) + throw std::out_of_range("node not found"); + + return *value; + } + + /** + * Access a child. + * + * @param index the index + * @return the reference to the children node + * @throw std::out_of_range on out of bounds + */ + T &operator[](int index) + { + return static_cast<T &>(m_children.at(index)->get()); + } + + /** + * Access a child. + * + * @param index the index + * @return the reference to the children node + * @throw std::out_of_range on out of bounds + */ + const T &operator[](int index) const + { + return static_cast<const T &>(m_children.at(index)->get()); + } +}; + +#endif // !_TREE_NODE_H_
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/C++/modules/Utf8/Utf8.cpp Sun Mar 08 14:26:33 2015 +0100 @@ -0,0 +1,4619 @@ +/* + * Utf8.cpp -- UTF-8 to UTF-32 conversions + * + * Copyright (c) 2013, 2014 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "Utf8.h" + +namespace { + +#define LEN(x) (sizeof (x) / sizeof (x[0])) + +static uint32_t * +rbsearch(uint32_t c, uint32_t *t, int n, int ne) +{ + uint32_t *p; + int m; + + while (n > 1) { + m = n >> 1; + p = t + m * ne; + + if (c >= p[0]) { + t = p; + n = n - m; + } else + n = m; + } + + if (n && c >= t[0]) + return t; + return 0; +} + +/* + * The following values have been generated from Go mkrunetype.c + * + * http://golang.org/src/lib9/utf/mkrunetype.c + */ + +/* {{{ Spaces */ + +static uint32_t isspacer[] = { + 0x0009, 0x000d, + 0x0020, 0x0020, + 0x0085, 0x0085, + 0x00a0, 0x00a0, + 0x1680, 0x1680, + 0x2000, 0x200a, + 0x2028, 0x2029, + 0x202f, 0x202f, + 0x205f, 0x205f, + 0x3000, 0x3000, + 0xfeff, 0xfeff, +}; + +/* }}} */ + +/* {{{ Digits */ + +static uint32_t isdigitr[] = { + 0x0030, 0x0039, + 0x0660, 0x0669, + 0x06f0, 0x06f9, + 0x07c0, 0x07c9, + 0x0966, 0x096f, + 0x09e6, 0x09ef, + 0x0a66, 0x0a6f, + 0x0ae6, 0x0aef, + 0x0b66, 0x0b6f, + 0x0be6, 0x0bef, + 0x0c66, 0x0c6f, + 0x0ce6, 0x0cef, + 0x0d66, 0x0d6f, + 0x0e50, 0x0e59, + 0x0ed0, 0x0ed9, + 0x0f20, 0x0f29, + 0x1040, 0x1049, + 0x1090, 0x1099, + 0x17e0, 0x17e9, + 0x1810, 0x1819, + 0x1946, 0x194f, + 0x19d0, 0x19d9, + 0x1a80, 0x1a89, + 0x1a90, 0x1a99, + 0x1b50, 0x1b59, + 0x1bb0, 0x1bb9, + 0x1c40, 0x1c49, + 0x1c50, 0x1c59, + 0xa620, 0xa629, + 0xa8d0, 0xa8d9, + 0xa900, 0xa909, + 0xa9d0, 0xa9d9, + 0xaa50, 0xaa59, + 0xabf0, 0xabf9, + 0xff10, 0xff19, + 0x104a0, 0x104a9, + 0x11066, 0x1106f, + 0x110f0, 0x110f9, + 0x11136, 0x1113f, + 0x111d0, 0x111d9, + 0x116c0, 0x116c9, + 0x1d7ce, 0x1d7ff, +}; + +/* }}} */ + +/* {{{ Unicode letters */ + +static uint32_t isalphar[] = { + 0x0041, 0x005a, + 0x0061, 0x007a, + 0x00c0, 0x00d6, + 0x00d8, 0x00f6, + 0x00f8, 0x02c1, + 0x02c6, 0x02d1, + 0x02e0, 0x02e4, + 0x0370, 0x0374, + 0x0376, 0x0377, + 0x037a, 0x037d, + 0x0388, 0x038a, + 0x038e, 0x03a1, + 0x03a3, 0x03f5, + 0x03f7, 0x0481, + 0x048a, 0x0527, + 0x0531, 0x0556, + 0x0561, 0x0587, + 0x05d0, 0x05ea, + 0x05f0, 0x05f2, + 0x0620, 0x064a, + 0x066e, 0x066f, + 0x0671, 0x06d3, + 0x06e5, 0x06e6, + 0x06ee, 0x06ef, + 0x06fa, 0x06fc, + 0x0712, 0x072f, + 0x074d, 0x07a5, + 0x07ca, 0x07ea, + 0x07f4, 0x07f5, + 0x0800, 0x0815, + 0x0840, 0x0858, + 0x08a2, 0x08ac, + 0x0904, 0x0939, + 0x0958, 0x0961, + 0x0971, 0x0977, + 0x0979, 0x097f, + 0x0985, 0x098c, + 0x098f, 0x0990, + 0x0993, 0x09a8, + 0x09aa, 0x09b0, + 0x09b6, 0x09b9, + 0x09dc, 0x09dd, + 0x09df, 0x09e1, + 0x09f0, 0x09f1, + 0x0a05, 0x0a0a, + 0x0a0f, 0x0a10, + 0x0a13, 0x0a28, + 0x0a2a, 0x0a30, + 0x0a32, 0x0a33, + 0x0a35, 0x0a36, + 0x0a38, 0x0a39, + 0x0a59, 0x0a5c, + 0x0a72, 0x0a74, + 0x0a85, 0x0a8d, + 0x0a8f, 0x0a91, + 0x0a93, 0x0aa8, + 0x0aaa, 0x0ab0, + 0x0ab2, 0x0ab3, + 0x0ab5, 0x0ab9, + 0x0ae0, 0x0ae1, + 0x0b05, 0x0b0c, + 0x0b0f, 0x0b10, + 0x0b13, 0x0b28, + 0x0b2a, 0x0b30, + 0x0b32, 0x0b33, + 0x0b35, 0x0b39, + 0x0b5c, 0x0b5d, + 0x0b5f, 0x0b61, + 0x0b85, 0x0b8a, + 0x0b8e, 0x0b90, + 0x0b92, 0x0b95, + 0x0b99, 0x0b9a, + 0x0b9e, 0x0b9f, + 0x0ba3, 0x0ba4, + 0x0ba8, 0x0baa, + 0x0bae, 0x0bb9, + 0x0c05, 0x0c0c, + 0x0c0e, 0x0c10, + 0x0c12, 0x0c28, + 0x0c2a, 0x0c33, + 0x0c35, 0x0c39, + 0x0c58, 0x0c59, + 0x0c60, 0x0c61, + 0x0c85, 0x0c8c, + 0x0c8e, 0x0c90, + 0x0c92, 0x0ca8, + 0x0caa, 0x0cb3, + 0x0cb5, 0x0cb9, + 0x0ce0, 0x0ce1, + 0x0cf1, 0x0cf2, + 0x0d05, 0x0d0c, + 0x0d0e, 0x0d10, + 0x0d12, 0x0d3a, + 0x0d60, 0x0d61, + 0x0d7a, 0x0d7f, + 0x0d85, 0x0d96, + 0x0d9a, 0x0db1, + 0x0db3, 0x0dbb, + 0x0dc0, 0x0dc6, + 0x0e01, 0x0e30, + 0x0e32, 0x0e33, + 0x0e40, 0x0e46, + 0x0e81, 0x0e82, + 0x0e87, 0x0e88, + 0x0e94, 0x0e97, + 0x0e99, 0x0e9f, + 0x0ea1, 0x0ea3, + 0x0eaa, 0x0eab, + 0x0ead, 0x0eb0, + 0x0eb2, 0x0eb3, + 0x0ec0, 0x0ec4, + 0x0edc, 0x0edf, + 0x0f40, 0x0f47, + 0x0f49, 0x0f6c, + 0x0f88, 0x0f8c, + 0x1000, 0x102a, + 0x1050, 0x1055, + 0x105a, 0x105d, + 0x1065, 0x1066, + 0x106e, 0x1070, + 0x1075, 0x1081, + 0x10a0, 0x10c5, + 0x10d0, 0x10fa, + 0x10fc, 0x1248, + 0x124a, 0x124d, + 0x1250, 0x1256, + 0x125a, 0x125d, + 0x1260, 0x1288, + 0x128a, 0x128d, + 0x1290, 0x12b0, + 0x12b2, 0x12b5, + 0x12b8, 0x12be, + 0x12c2, 0x12c5, + 0x12c8, 0x12d6, + 0x12d8, 0x1310, + 0x1312, 0x1315, + 0x1318, 0x135a, + 0x1380, 0x138f, + 0x13a0, 0x13f4, + 0x1401, 0x166c, + 0x166f, 0x167f, + 0x1681, 0x169a, + 0x16a0, 0x16ea, + 0x1700, 0x170c, + 0x170e, 0x1711, + 0x1720, 0x1731, + 0x1740, 0x1751, + 0x1760, 0x176c, + 0x176e, 0x1770, + 0x1780, 0x17b3, + 0x1820, 0x1877, + 0x1880, 0x18a8, + 0x18b0, 0x18f5, + 0x1900, 0x191c, + 0x1950, 0x196d, + 0x1970, 0x1974, + 0x1980, 0x19ab, + 0x19c1, 0x19c7, + 0x1a00, 0x1a16, + 0x1a20, 0x1a54, + 0x1b05, 0x1b33, + 0x1b45, 0x1b4b, + 0x1b83, 0x1ba0, + 0x1bae, 0x1baf, + 0x1bba, 0x1be5, + 0x1c00, 0x1c23, + 0x1c4d, 0x1c4f, + 0x1c5a, 0x1c7d, + 0x1ce9, 0x1cec, + 0x1cee, 0x1cf1, + 0x1cf5, 0x1cf6, + 0x1d00, 0x1dbf, + 0x1e00, 0x1f15, + 0x1f18, 0x1f1d, + 0x1f20, 0x1f45, + 0x1f48, 0x1f4d, + 0x1f50, 0x1f57, + 0x1f5f, 0x1f7d, + 0x1f80, 0x1fb4, + 0x1fb6, 0x1fbc, + 0x1fc2, 0x1fc4, + 0x1fc6, 0x1fcc, + 0x1fd0, 0x1fd3, + 0x1fd6, 0x1fdb, + 0x1fe0, 0x1fec, + 0x1ff2, 0x1ff4, + 0x1ff6, 0x1ffc, + 0x2090, 0x209c, + 0x210a, 0x2113, + 0x2119, 0x211d, + 0x212a, 0x212d, + 0x212f, 0x2139, + 0x213c, 0x213f, + 0x2145, 0x2149, + 0x2183, 0x2184, + 0x2c00, 0x2c2e, + 0x2c30, 0x2c5e, + 0x2c60, 0x2ce4, + 0x2ceb, 0x2cee, + 0x2cf2, 0x2cf3, + 0x2d00, 0x2d25, + 0x2d30, 0x2d67, + 0x2d80, 0x2d96, + 0x2da0, 0x2da6, + 0x2da8, 0x2dae, + 0x2db0, 0x2db6, + 0x2db8, 0x2dbe, + 0x2dc0, 0x2dc6, + 0x2dc8, 0x2dce, + 0x2dd0, 0x2dd6, + 0x2dd8, 0x2dde, + 0x3005, 0x3006, + 0x3031, 0x3035, + 0x303b, 0x303c, + 0x3041, 0x3096, + 0x309d, 0x309f, + 0x30a1, 0x30fa, + 0x30fc, 0x30ff, + 0x3105, 0x312d, + 0x3131, 0x318e, + 0x31a0, 0x31ba, + 0x31f0, 0x31ff, + 0x3400, 0x4db5, + 0x4e00, 0x9fcc, + 0xa000, 0xa48c, + 0xa4d0, 0xa4fd, + 0xa500, 0xa60c, + 0xa610, 0xa61f, + 0xa62a, 0xa62b, + 0xa640, 0xa66e, + 0xa67f, 0xa697, + 0xa6a0, 0xa6e5, + 0xa717, 0xa71f, + 0xa722, 0xa788, + 0xa78b, 0xa78e, + 0xa790, 0xa793, + 0xa7a0, 0xa7aa, + 0xa7f8, 0xa801, + 0xa803, 0xa805, + 0xa807, 0xa80a, + 0xa80c, 0xa822, + 0xa840, 0xa873, + 0xa882, 0xa8b3, + 0xa8f2, 0xa8f7, + 0xa90a, 0xa925, + 0xa930, 0xa946, + 0xa960, 0xa97c, + 0xa984, 0xa9b2, + 0xaa00, 0xaa28, + 0xaa40, 0xaa42, + 0xaa44, 0xaa4b, + 0xaa60, 0xaa76, + 0xaa80, 0xaaaf, + 0xaab5, 0xaab6, + 0xaab9, 0xaabd, + 0xaadb, 0xaadd, + 0xaae0, 0xaaea, + 0xaaf2, 0xaaf4, + 0xab01, 0xab06, + 0xab09, 0xab0e, + 0xab11, 0xab16, + 0xab20, 0xab26, + 0xab28, 0xab2e, + 0xabc0, 0xabe2, + 0xac00, 0xd7a3, + 0xd7b0, 0xd7c6, + 0xd7cb, 0xd7fb, + 0xf900, 0xfa6d, + 0xfa70, 0xfad9, + 0xfb00, 0xfb06, + 0xfb13, 0xfb17, + 0xfb1f, 0xfb28, + 0xfb2a, 0xfb36, + 0xfb38, 0xfb3c, + 0xfb40, 0xfb41, + 0xfb43, 0xfb44, + 0xfb46, 0xfbb1, + 0xfbd3, 0xfd3d, + 0xfd50, 0xfd8f, + 0xfd92, 0xfdc7, + 0xfdf0, 0xfdfb, + 0xfe70, 0xfe74, + 0xfe76, 0xfefc, + 0xff21, 0xff3a, + 0xff41, 0xff5a, + 0xff66, 0xffbe, + 0xffc2, 0xffc7, + 0xffca, 0xffcf, + 0xffd2, 0xffd7, + 0xffda, 0xffdc, + 0x10000, 0x1000b, + 0x1000d, 0x10026, + 0x10028, 0x1003a, + 0x1003c, 0x1003d, + 0x1003f, 0x1004d, + 0x10050, 0x1005d, + 0x10080, 0x100fa, + 0x10280, 0x1029c, + 0x102a0, 0x102d0, + 0x10300, 0x1031e, + 0x10330, 0x10340, + 0x10342, 0x10349, + 0x10380, 0x1039d, + 0x103a0, 0x103c3, + 0x103c8, 0x103cf, + 0x10400, 0x1049d, + 0x10800, 0x10805, + 0x1080a, 0x10835, + 0x10837, 0x10838, + 0x1083f, 0x10855, + 0x10900, 0x10915, + 0x10920, 0x10939, + 0x10980, 0x109b7, + 0x109be, 0x109bf, + 0x10a10, 0x10a13, + 0x10a15, 0x10a17, + 0x10a19, 0x10a33, + 0x10a60, 0x10a7c, + 0x10b00, 0x10b35, + 0x10b40, 0x10b55, + 0x10b60, 0x10b72, + 0x10c00, 0x10c48, + 0x11003, 0x11037, + 0x11083, 0x110af, + 0x110d0, 0x110e8, + 0x11103, 0x11126, + 0x11183, 0x111b2, + 0x111c1, 0x111c4, + 0x11680, 0x116aa, + 0x12000, 0x1236e, + 0x13000, 0x1342e, + 0x16800, 0x16a38, + 0x16f00, 0x16f44, + 0x16f93, 0x16f9f, + 0x1b000, 0x1b001, + 0x1d400, 0x1d454, + 0x1d456, 0x1d49c, + 0x1d49e, 0x1d49f, + 0x1d4a5, 0x1d4a6, + 0x1d4a9, 0x1d4ac, + 0x1d4ae, 0x1d4b9, + 0x1d4bd, 0x1d4c3, + 0x1d4c5, 0x1d505, + 0x1d507, 0x1d50a, + 0x1d50d, 0x1d514, + 0x1d516, 0x1d51c, + 0x1d51e, 0x1d539, + 0x1d53b, 0x1d53e, + 0x1d540, 0x1d544, + 0x1d54a, 0x1d550, + 0x1d552, 0x1d6a5, + 0x1d6a8, 0x1d6c0, + 0x1d6c2, 0x1d6da, + 0x1d6dc, 0x1d6fa, + 0x1d6fc, 0x1d714, + 0x1d716, 0x1d734, + 0x1d736, 0x1d74e, + 0x1d750, 0x1d76e, + 0x1d770, 0x1d788, + 0x1d78a, 0x1d7a8, + 0x1d7aa, 0x1d7c2, + 0x1d7c4, 0x1d7cb, + 0x1ee00, 0x1ee03, + 0x1ee05, 0x1ee1f, + 0x1ee21, 0x1ee22, + 0x1ee29, 0x1ee32, + 0x1ee34, 0x1ee37, + 0x1ee4d, 0x1ee4f, + 0x1ee51, 0x1ee52, + 0x1ee61, 0x1ee62, + 0x1ee67, 0x1ee6a, + 0x1ee6c, 0x1ee72, + 0x1ee74, 0x1ee77, + 0x1ee79, 0x1ee7c, + 0x1ee80, 0x1ee89, + 0x1ee8b, 0x1ee9b, + 0x1eea1, 0x1eea3, + 0x1eea5, 0x1eea9, + 0x1eeab, 0x1eebb, + 0x20000, 0x2a6d6, + 0x2a700, 0x2b734, + 0x2b740, 0x2b81d, + 0x2f800, 0x2fa1d, +}; + +/* }}} */ + +/* {{{ Letters */ + +static uint32_t isalphas[] = { + 0x00aa, + 0x00b5, + 0x00ba, + 0x02ec, + 0x02ee, + 0x0386, + 0x038c, + 0x0559, + 0x06d5, + 0x06ff, + 0x0710, + 0x07b1, + 0x07fa, + 0x081a, + 0x0824, + 0x0828, + 0x08a0, + 0x093d, + 0x0950, + 0x09b2, + 0x09bd, + 0x09ce, + 0x0a5e, + 0x0abd, + 0x0ad0, + 0x0b3d, + 0x0b71, + 0x0b83, + 0x0b9c, + 0x0bd0, + 0x0c3d, + 0x0cbd, + 0x0cde, + 0x0d3d, + 0x0d4e, + 0x0dbd, + 0x0e84, + 0x0e8a, + 0x0e8d, + 0x0ea5, + 0x0ea7, + 0x0ebd, + 0x0ec6, + 0x0f00, + 0x103f, + 0x1061, + 0x108e, + 0x10c7, + 0x10cd, + 0x1258, + 0x12c0, + 0x17d7, + 0x17dc, + 0x18aa, + 0x1aa7, + 0x1f59, + 0x1f5b, + 0x1f5d, + 0x1fbe, + 0x2071, + 0x207f, + 0x2102, + 0x2107, + 0x2115, + 0x2124, + 0x2126, + 0x2128, + 0x214e, + 0x2d27, + 0x2d2d, + 0x2d6f, + 0x2e2f, + 0xa8fb, + 0xa9cf, + 0xaa7a, + 0xaab1, + 0xaac0, + 0xaac2, + 0xfb1d, + 0xfb3e, + 0x10808, + 0x1083c, + 0x10a00, + 0x16f50, + 0x1d4a2, + 0x1d4bb, + 0x1d546, + 0x1ee24, + 0x1ee27, + 0x1ee39, + 0x1ee3b, + 0x1ee42, + 0x1ee47, + 0x1ee49, + 0x1ee4b, + 0x1ee54, + 0x1ee57, + 0x1ee59, + 0x1ee5b, + 0x1ee5d, + 0x1ee5f, + 0x1ee64, + 0x1ee7e, +}; + +/* }}} */ + +/* {{{ Unicode upper */ + +static uint32_t isupperr[] = { + 0x0041, 0x005a, + 0x00c0, 0x00d6, + 0x00d8, 0x00de, + 0x0178, 0x0179, + 0x0181, 0x0182, + 0x0186, 0x0187, + 0x0189, 0x018b, + 0x018e, 0x0191, + 0x0193, 0x0194, + 0x0196, 0x0198, + 0x019c, 0x019d, + 0x019f, 0x01a0, + 0x01a6, 0x01a7, + 0x01ae, 0x01af, + 0x01b1, 0x01b3, + 0x01b7, 0x01b8, + 0x01f6, 0x01f8, + 0x023a, 0x023b, + 0x023d, 0x023e, + 0x0243, 0x0246, + 0x0388, 0x038a, + 0x038e, 0x038f, + 0x0391, 0x03a1, + 0x03a3, 0x03ab, + 0x03d2, 0x03d4, + 0x03f9, 0x03fa, + 0x03fd, 0x042f, + 0x04c0, 0x04c1, + 0x0531, 0x0556, + 0x10a0, 0x10c5, + 0x1f08, 0x1f0f, + 0x1f18, 0x1f1d, + 0x1f28, 0x1f2f, + 0x1f38, 0x1f3f, + 0x1f48, 0x1f4d, + 0x1f68, 0x1f6f, + 0x1f88, 0x1f8f, + 0x1f98, 0x1f9f, + 0x1fa8, 0x1faf, + 0x1fb8, 0x1fbc, + 0x1fc8, 0x1fcc, + 0x1fd8, 0x1fdb, + 0x1fe8, 0x1fec, + 0x1ff8, 0x1ffc, + 0x210b, 0x210d, + 0x2110, 0x2112, + 0x2119, 0x211d, + 0x212a, 0x212d, + 0x2130, 0x2133, + 0x213e, 0x213f, + 0x2160, 0x216f, + 0x24b6, 0x24cf, + 0x2c00, 0x2c2e, + 0x2c62, 0x2c64, + 0x2c6d, 0x2c70, + 0x2c7e, 0x2c80, + 0xa77d, 0xa77e, + 0xff21, 0xff3a, + 0x10400, 0x10427, + 0x1d400, 0x1d419, + 0x1d434, 0x1d44d, + 0x1d468, 0x1d481, + 0x1d49e, 0x1d49f, + 0x1d4a5, 0x1d4a6, + 0x1d4a9, 0x1d4ac, + 0x1d4ae, 0x1d4b5, + 0x1d4d0, 0x1d4e9, + 0x1d504, 0x1d505, + 0x1d507, 0x1d50a, + 0x1d50d, 0x1d514, + 0x1d516, 0x1d51c, + 0x1d538, 0x1d539, + 0x1d53b, 0x1d53e, + 0x1d540, 0x1d544, + 0x1d54a, 0x1d550, + 0x1d56c, 0x1d585, + 0x1d5a0, 0x1d5b9, + 0x1d5d4, 0x1d5ed, + 0x1d608, 0x1d621, + 0x1d63c, 0x1d655, + 0x1d670, 0x1d689, + 0x1d6a8, 0x1d6c0, + 0x1d6e2, 0x1d6fa, + 0x1d71c, 0x1d734, + 0x1d756, 0x1d76e, + 0x1d790, 0x1d7a8, +}; + +/* }}} */ + +/* {{{ Upper */ + +static uint32_t isuppers[] = { + 0x0100, + 0x0102, + 0x0104, + 0x0106, + 0x0108, + 0x010a, + 0x010c, + 0x010e, + 0x0110, + 0x0112, + 0x0114, + 0x0116, + 0x0118, + 0x011a, + 0x011c, + 0x011e, + 0x0120, + 0x0122, + 0x0124, + 0x0126, + 0x0128, + 0x012a, + 0x012c, + 0x012e, + 0x0130, + 0x0132, + 0x0134, + 0x0136, + 0x0139, + 0x013b, + 0x013d, + 0x013f, + 0x0141, + 0x0143, + 0x0145, + 0x0147, + 0x014a, + 0x014c, + 0x014e, + 0x0150, + 0x0152, + 0x0154, + 0x0156, + 0x0158, + 0x015a, + 0x015c, + 0x015e, + 0x0160, + 0x0162, + 0x0164, + 0x0166, + 0x0168, + 0x016a, + 0x016c, + 0x016e, + 0x0170, + 0x0172, + 0x0174, + 0x0176, + 0x017b, + 0x017d, + 0x0184, + 0x01a2, + 0x01a4, + 0x01a9, + 0x01ac, + 0x01b5, + 0x01bc, + 0x01c4, + 0x01c7, + 0x01ca, + 0x01cd, + 0x01cf, + 0x01d1, + 0x01d3, + 0x01d5, + 0x01d7, + 0x01d9, + 0x01db, + 0x01de, + 0x01e0, + 0x01e2, + 0x01e4, + 0x01e6, + 0x01e8, + 0x01ea, + 0x01ec, + 0x01ee, + 0x01f1, + 0x01f4, + 0x01fa, + 0x01fc, + 0x01fe, + 0x0200, + 0x0202, + 0x0204, + 0x0206, + 0x0208, + 0x020a, + 0x020c, + 0x020e, + 0x0210, + 0x0212, + 0x0214, + 0x0216, + 0x0218, + 0x021a, + 0x021c, + 0x021e, + 0x0220, + 0x0222, + 0x0224, + 0x0226, + 0x0228, + 0x022a, + 0x022c, + 0x022e, + 0x0230, + 0x0232, + 0x0241, + 0x0248, + 0x024a, + 0x024c, + 0x024e, + 0x0370, + 0x0372, + 0x0376, + 0x0386, + 0x038c, + 0x03cf, + 0x03d8, + 0x03da, + 0x03dc, + 0x03de, + 0x03e0, + 0x03e2, + 0x03e4, + 0x03e6, + 0x03e8, + 0x03ea, + 0x03ec, + 0x03ee, + 0x03f4, + 0x03f7, + 0x0460, + 0x0462, + 0x0464, + 0x0466, + 0x0468, + 0x046a, + 0x046c, + 0x046e, + 0x0470, + 0x0472, + 0x0474, + 0x0476, + 0x0478, + 0x047a, + 0x047c, + 0x047e, + 0x0480, + 0x048a, + 0x048c, + 0x048e, + 0x0490, + 0x0492, + 0x0494, + 0x0496, + 0x0498, + 0x049a, + 0x049c, + 0x049e, + 0x04a0, + 0x04a2, + 0x04a4, + 0x04a6, + 0x04a8, + 0x04aa, + 0x04ac, + 0x04ae, + 0x04b0, + 0x04b2, + 0x04b4, + 0x04b6, + 0x04b8, + 0x04ba, + 0x04bc, + 0x04be, + 0x04c3, + 0x04c5, + 0x04c7, + 0x04c9, + 0x04cb, + 0x04cd, + 0x04d0, + 0x04d2, + 0x04d4, + 0x04d6, + 0x04d8, + 0x04da, + 0x04dc, + 0x04de, + 0x04e0, + 0x04e2, + 0x04e4, + 0x04e6, + 0x04e8, + 0x04ea, + 0x04ec, + 0x04ee, + 0x04f0, + 0x04f2, + 0x04f4, + 0x04f6, + 0x04f8, + 0x04fa, + 0x04fc, + 0x04fe, + 0x0500, + 0x0502, + 0x0504, + 0x0506, + 0x0508, + 0x050a, + 0x050c, + 0x050e, + 0x0510, + 0x0512, + 0x0514, + 0x0516, + 0x0518, + 0x051a, + 0x051c, + 0x051e, + 0x0520, + 0x0522, + 0x0524, + 0x0526, + 0x10c7, + 0x10cd, + 0x1e00, + 0x1e02, + 0x1e04, + 0x1e06, + 0x1e08, + 0x1e0a, + 0x1e0c, + 0x1e0e, + 0x1e10, + 0x1e12, + 0x1e14, + 0x1e16, + 0x1e18, + 0x1e1a, + 0x1e1c, + 0x1e1e, + 0x1e20, + 0x1e22, + 0x1e24, + 0x1e26, + 0x1e28, + 0x1e2a, + 0x1e2c, + 0x1e2e, + 0x1e30, + 0x1e32, + 0x1e34, + 0x1e36, + 0x1e38, + 0x1e3a, + 0x1e3c, + 0x1e3e, + 0x1e40, + 0x1e42, + 0x1e44, + 0x1e46, + 0x1e48, + 0x1e4a, + 0x1e4c, + 0x1e4e, + 0x1e50, + 0x1e52, + 0x1e54, + 0x1e56, + 0x1e58, + 0x1e5a, + 0x1e5c, + 0x1e5e, + 0x1e60, + 0x1e62, + 0x1e64, + 0x1e66, + 0x1e68, + 0x1e6a, + 0x1e6c, + 0x1e6e, + 0x1e70, + 0x1e72, + 0x1e74, + 0x1e76, + 0x1e78, + 0x1e7a, + 0x1e7c, + 0x1e7e, + 0x1e80, + 0x1e82, + 0x1e84, + 0x1e86, + 0x1e88, + 0x1e8a, + 0x1e8c, + 0x1e8e, + 0x1e90, + 0x1e92, + 0x1e94, + 0x1e9e, + 0x1ea0, + 0x1ea2, + 0x1ea4, + 0x1ea6, + 0x1ea8, + 0x1eaa, + 0x1eac, + 0x1eae, + 0x1eb0, + 0x1eb2, + 0x1eb4, + 0x1eb6, + 0x1eb8, + 0x1eba, + 0x1ebc, + 0x1ebe, + 0x1ec0, + 0x1ec2, + 0x1ec4, + 0x1ec6, + 0x1ec8, + 0x1eca, + 0x1ecc, + 0x1ece, + 0x1ed0, + 0x1ed2, + 0x1ed4, + 0x1ed6, + 0x1ed8, + 0x1eda, + 0x1edc, + 0x1ede, + 0x1ee0, + 0x1ee2, + 0x1ee4, + 0x1ee6, + 0x1ee8, + 0x1eea, + 0x1eec, + 0x1eee, + 0x1ef0, + 0x1ef2, + 0x1ef4, + 0x1ef6, + 0x1ef8, + 0x1efa, + 0x1efc, + 0x1efe, + 0x1f59, + 0x1f5b, + 0x1f5d, + 0x1f5f, + 0x2102, + 0x2107, + 0x2115, + 0x2124, + 0x2126, + 0x2128, + 0x2145, + 0x2183, + 0x2c60, + 0x2c67, + 0x2c69, + 0x2c6b, + 0x2c72, + 0x2c75, + 0x2c82, + 0x2c84, + 0x2c86, + 0x2c88, + 0x2c8a, + 0x2c8c, + 0x2c8e, + 0x2c90, + 0x2c92, + 0x2c94, + 0x2c96, + 0x2c98, + 0x2c9a, + 0x2c9c, + 0x2c9e, + 0x2ca0, + 0x2ca2, + 0x2ca4, + 0x2ca6, + 0x2ca8, + 0x2caa, + 0x2cac, + 0x2cae, + 0x2cb0, + 0x2cb2, + 0x2cb4, + 0x2cb6, + 0x2cb8, + 0x2cba, + 0x2cbc, + 0x2cbe, + 0x2cc0, + 0x2cc2, + 0x2cc4, + 0x2cc6, + 0x2cc8, + 0x2cca, + 0x2ccc, + 0x2cce, + 0x2cd0, + 0x2cd2, + 0x2cd4, + 0x2cd6, + 0x2cd8, + 0x2cda, + 0x2cdc, + 0x2cde, + 0x2ce0, + 0x2ce2, + 0x2ceb, + 0x2ced, + 0x2cf2, + 0xa640, + 0xa642, + 0xa644, + 0xa646, + 0xa648, + 0xa64a, + 0xa64c, + 0xa64e, + 0xa650, + 0xa652, + 0xa654, + 0xa656, + 0xa658, + 0xa65a, + 0xa65c, + 0xa65e, + 0xa660, + 0xa662, + 0xa664, + 0xa666, + 0xa668, + 0xa66a, + 0xa66c, + 0xa680, + 0xa682, + 0xa684, + 0xa686, + 0xa688, + 0xa68a, + 0xa68c, + 0xa68e, + 0xa690, + 0xa692, + 0xa694, + 0xa696, + 0xa722, + 0xa724, + 0xa726, + 0xa728, + 0xa72a, + 0xa72c, + 0xa72e, + 0xa732, + 0xa734, + 0xa736, + 0xa738, + 0xa73a, + 0xa73c, + 0xa73e, + 0xa740, + 0xa742, + 0xa744, + 0xa746, + 0xa748, + 0xa74a, + 0xa74c, + 0xa74e, + 0xa750, + 0xa752, + 0xa754, + 0xa756, + 0xa758, + 0xa75a, + 0xa75c, + 0xa75e, + 0xa760, + 0xa762, + 0xa764, + 0xa766, + 0xa768, + 0xa76a, + 0xa76c, + 0xa76e, + 0xa779, + 0xa77b, + 0xa780, + 0xa782, + 0xa784, + 0xa786, + 0xa78b, + 0xa78d, + 0xa790, + 0xa792, + 0xa7a0, + 0xa7a2, + 0xa7a4, + 0xa7a6, + 0xa7a8, + 0xa7aa, + 0x1d49c, + 0x1d4a2, + 0x1d546, + 0x1d7ca, +}; + +/* }}} */ + +/* {{{ Unicode lower */ + +static uint32_t islowerr[] = { + 0x0061, 0x007a, + 0x00df, 0x00f6, + 0x00f8, 0x00ff, + 0x0137, 0x0138, + 0x0148, 0x0149, + 0x017e, 0x0180, + 0x018c, 0x018d, + 0x0199, 0x019b, + 0x01aa, 0x01ab, + 0x01b9, 0x01ba, + 0x01bd, 0x01bf, + 0x01dc, 0x01dd, + 0x01ef, 0x01f0, + 0x0233, 0x0239, + 0x023f, 0x0240, + 0x024f, 0x0293, + 0x0295, 0x02af, + 0x037b, 0x037d, + 0x03ac, 0x03ce, + 0x03d0, 0x03d1, + 0x03d5, 0x03d7, + 0x03ef, 0x03f3, + 0x03fb, 0x03fc, + 0x0430, 0x045f, + 0x04ce, 0x04cf, + 0x0561, 0x0587, + 0x1d00, 0x1d2b, + 0x1d6b, 0x1d77, + 0x1d79, 0x1d9a, + 0x1e95, 0x1e9d, + 0x1eff, 0x1f07, + 0x1f10, 0x1f15, + 0x1f20, 0x1f27, + 0x1f30, 0x1f37, + 0x1f40, 0x1f45, + 0x1f50, 0x1f57, + 0x1f60, 0x1f67, + 0x1f70, 0x1f7d, + 0x1f80, 0x1f87, + 0x1f90, 0x1f97, + 0x1fa0, 0x1fa7, + 0x1fb0, 0x1fb4, + 0x1fb6, 0x1fb7, + 0x1fc2, 0x1fc4, + 0x1fc6, 0x1fc7, + 0x1fd0, 0x1fd3, + 0x1fd6, 0x1fd7, + 0x1fe0, 0x1fe7, + 0x1ff2, 0x1ff4, + 0x1ff6, 0x1ff7, + 0x210e, 0x210f, + 0x213c, 0x213d, + 0x2146, 0x2149, + 0x2170, 0x217f, + 0x24d0, 0x24e9, + 0x2c30, 0x2c5e, + 0x2c65, 0x2c66, + 0x2c73, 0x2c74, + 0x2c76, 0x2c7b, + 0x2ce3, 0x2ce4, + 0x2d00, 0x2d25, + 0xa72f, 0xa731, + 0xa771, 0xa778, + 0xfb00, 0xfb06, + 0xfb13, 0xfb17, + 0xff41, 0xff5a, + 0x10428, 0x1044f, + 0x1d41a, 0x1d433, + 0x1d44e, 0x1d454, + 0x1d456, 0x1d467, + 0x1d482, 0x1d49b, + 0x1d4b6, 0x1d4b9, + 0x1d4bd, 0x1d4c3, + 0x1d4c5, 0x1d4cf, + 0x1d4ea, 0x1d503, + 0x1d51e, 0x1d537, + 0x1d552, 0x1d56b, + 0x1d586, 0x1d59f, + 0x1d5ba, 0x1d5d3, + 0x1d5ee, 0x1d607, + 0x1d622, 0x1d63b, + 0x1d656, 0x1d66f, + 0x1d68a, 0x1d6a5, + 0x1d6c2, 0x1d6da, + 0x1d6dc, 0x1d6e1, + 0x1d6fc, 0x1d714, + 0x1d716, 0x1d71b, + 0x1d736, 0x1d74e, + 0x1d750, 0x1d755, + 0x1d770, 0x1d788, + 0x1d78a, 0x1d78f, + 0x1d7aa, 0x1d7c2, + 0x1d7c4, 0x1d7c9, +}; + +/* }}} */ + +/* {{{ Lower */ + +static uint32_t islowers[] = { + 0x00b5, + 0x0101, + 0x0103, + 0x0105, + 0x0107, + 0x0109, + 0x010b, + 0x010d, + 0x010f, + 0x0111, + 0x0113, + 0x0115, + 0x0117, + 0x0119, + 0x011b, + 0x011d, + 0x011f, + 0x0121, + 0x0123, + 0x0125, + 0x0127, + 0x0129, + 0x012b, + 0x012d, + 0x012f, + 0x0131, + 0x0133, + 0x0135, + 0x013a, + 0x013c, + 0x013e, + 0x0140, + 0x0142, + 0x0144, + 0x0146, + 0x014b, + 0x014d, + 0x014f, + 0x0151, + 0x0153, + 0x0155, + 0x0157, + 0x0159, + 0x015b, + 0x015d, + 0x015f, + 0x0161, + 0x0163, + 0x0165, + 0x0167, + 0x0169, + 0x016b, + 0x016d, + 0x016f, + 0x0171, + 0x0173, + 0x0175, + 0x0177, + 0x017a, + 0x017c, + 0x0183, + 0x0185, + 0x0188, + 0x0192, + 0x0195, + 0x019e, + 0x01a1, + 0x01a3, + 0x01a5, + 0x01a8, + 0x01ad, + 0x01b0, + 0x01b4, + 0x01b6, + 0x01c6, + 0x01c9, + 0x01cc, + 0x01ce, + 0x01d0, + 0x01d2, + 0x01d4, + 0x01d6, + 0x01d8, + 0x01da, + 0x01df, + 0x01e1, + 0x01e3, + 0x01e5, + 0x01e7, + 0x01e9, + 0x01eb, + 0x01ed, + 0x01f3, + 0x01f5, + 0x01f9, + 0x01fb, + 0x01fd, + 0x01ff, + 0x0201, + 0x0203, + 0x0205, + 0x0207, + 0x0209, + 0x020b, + 0x020d, + 0x020f, + 0x0211, + 0x0213, + 0x0215, + 0x0217, + 0x0219, + 0x021b, + 0x021d, + 0x021f, + 0x0221, + 0x0223, + 0x0225, + 0x0227, + 0x0229, + 0x022b, + 0x022d, + 0x022f, + 0x0231, + 0x023c, + 0x0242, + 0x0247, + 0x0249, + 0x024b, + 0x024d, + 0x0371, + 0x0373, + 0x0377, + 0x0390, + 0x03d9, + 0x03db, + 0x03dd, + 0x03df, + 0x03e1, + 0x03e3, + 0x03e5, + 0x03e7, + 0x03e9, + 0x03eb, + 0x03ed, + 0x03f5, + 0x03f8, + 0x0461, + 0x0463, + 0x0465, + 0x0467, + 0x0469, + 0x046b, + 0x046d, + 0x046f, + 0x0471, + 0x0473, + 0x0475, + 0x0477, + 0x0479, + 0x047b, + 0x047d, + 0x047f, + 0x0481, + 0x048b, + 0x048d, + 0x048f, + 0x0491, + 0x0493, + 0x0495, + 0x0497, + 0x0499, + 0x049b, + 0x049d, + 0x049f, + 0x04a1, + 0x04a3, + 0x04a5, + 0x04a7, + 0x04a9, + 0x04ab, + 0x04ad, + 0x04af, + 0x04b1, + 0x04b3, + 0x04b5, + 0x04b7, + 0x04b9, + 0x04bb, + 0x04bd, + 0x04bf, + 0x04c2, + 0x04c4, + 0x04c6, + 0x04c8, + 0x04ca, + 0x04cc, + 0x04d1, + 0x04d3, + 0x04d5, + 0x04d7, + 0x04d9, + 0x04db, + 0x04dd, + 0x04df, + 0x04e1, + 0x04e3, + 0x04e5, + 0x04e7, + 0x04e9, + 0x04eb, + 0x04ed, + 0x04ef, + 0x04f1, + 0x04f3, + 0x04f5, + 0x04f7, + 0x04f9, + 0x04fb, + 0x04fd, + 0x04ff, + 0x0501, + 0x0503, + 0x0505, + 0x0507, + 0x0509, + 0x050b, + 0x050d, + 0x050f, + 0x0511, + 0x0513, + 0x0515, + 0x0517, + 0x0519, + 0x051b, + 0x051d, + 0x051f, + 0x0521, + 0x0523, + 0x0525, + 0x0527, + 0x1e01, + 0x1e03, + 0x1e05, + 0x1e07, + 0x1e09, + 0x1e0b, + 0x1e0d, + 0x1e0f, + 0x1e11, + 0x1e13, + 0x1e15, + 0x1e17, + 0x1e19, + 0x1e1b, + 0x1e1d, + 0x1e1f, + 0x1e21, + 0x1e23, + 0x1e25, + 0x1e27, + 0x1e29, + 0x1e2b, + 0x1e2d, + 0x1e2f, + 0x1e31, + 0x1e33, + 0x1e35, + 0x1e37, + 0x1e39, + 0x1e3b, + 0x1e3d, + 0x1e3f, + 0x1e41, + 0x1e43, + 0x1e45, + 0x1e47, + 0x1e49, + 0x1e4b, + 0x1e4d, + 0x1e4f, + 0x1e51, + 0x1e53, + 0x1e55, + 0x1e57, + 0x1e59, + 0x1e5b, + 0x1e5d, + 0x1e5f, + 0x1e61, + 0x1e63, + 0x1e65, + 0x1e67, + 0x1e69, + 0x1e6b, + 0x1e6d, + 0x1e6f, + 0x1e71, + 0x1e73, + 0x1e75, + 0x1e77, + 0x1e79, + 0x1e7b, + 0x1e7d, + 0x1e7f, + 0x1e81, + 0x1e83, + 0x1e85, + 0x1e87, + 0x1e89, + 0x1e8b, + 0x1e8d, + 0x1e8f, + 0x1e91, + 0x1e93, + 0x1e9f, + 0x1ea1, + 0x1ea3, + 0x1ea5, + 0x1ea7, + 0x1ea9, + 0x1eab, + 0x1ead, + 0x1eaf, + 0x1eb1, + 0x1eb3, + 0x1eb5, + 0x1eb7, + 0x1eb9, + 0x1ebb, + 0x1ebd, + 0x1ebf, + 0x1ec1, + 0x1ec3, + 0x1ec5, + 0x1ec7, + 0x1ec9, + 0x1ecb, + 0x1ecd, + 0x1ecf, + 0x1ed1, + 0x1ed3, + 0x1ed5, + 0x1ed7, + 0x1ed9, + 0x1edb, + 0x1edd, + 0x1edf, + 0x1ee1, + 0x1ee3, + 0x1ee5, + 0x1ee7, + 0x1ee9, + 0x1eeb, + 0x1eed, + 0x1eef, + 0x1ef1, + 0x1ef3, + 0x1ef5, + 0x1ef7, + 0x1ef9, + 0x1efb, + 0x1efd, + 0x1fbe, + 0x210a, + 0x2113, + 0x212f, + 0x2134, + 0x2139, + 0x214e, + 0x2184, + 0x2c61, + 0x2c68, + 0x2c6a, + 0x2c6c, + 0x2c71, + 0x2c81, + 0x2c83, + 0x2c85, + 0x2c87, + 0x2c89, + 0x2c8b, + 0x2c8d, + 0x2c8f, + 0x2c91, + 0x2c93, + 0x2c95, + 0x2c97, + 0x2c99, + 0x2c9b, + 0x2c9d, + 0x2c9f, + 0x2ca1, + 0x2ca3, + 0x2ca5, + 0x2ca7, + 0x2ca9, + 0x2cab, + 0x2cad, + 0x2caf, + 0x2cb1, + 0x2cb3, + 0x2cb5, + 0x2cb7, + 0x2cb9, + 0x2cbb, + 0x2cbd, + 0x2cbf, + 0x2cc1, + 0x2cc3, + 0x2cc5, + 0x2cc7, + 0x2cc9, + 0x2ccb, + 0x2ccd, + 0x2ccf, + 0x2cd1, + 0x2cd3, + 0x2cd5, + 0x2cd7, + 0x2cd9, + 0x2cdb, + 0x2cdd, + 0x2cdf, + 0x2ce1, + 0x2cec, + 0x2cee, + 0x2cf3, + 0x2d27, + 0x2d2d, + 0xa641, + 0xa643, + 0xa645, + 0xa647, + 0xa649, + 0xa64b, + 0xa64d, + 0xa64f, + 0xa651, + 0xa653, + 0xa655, + 0xa657, + 0xa659, + 0xa65b, + 0xa65d, + 0xa65f, + 0xa661, + 0xa663, + 0xa665, + 0xa667, + 0xa669, + 0xa66b, + 0xa66d, + 0xa681, + 0xa683, + 0xa685, + 0xa687, + 0xa689, + 0xa68b, + 0xa68d, + 0xa68f, + 0xa691, + 0xa693, + 0xa695, + 0xa697, + 0xa723, + 0xa725, + 0xa727, + 0xa729, + 0xa72b, + 0xa72d, + 0xa733, + 0xa735, + 0xa737, + 0xa739, + 0xa73b, + 0xa73d, + 0xa73f, + 0xa741, + 0xa743, + 0xa745, + 0xa747, + 0xa749, + 0xa74b, + 0xa74d, + 0xa74f, + 0xa751, + 0xa753, + 0xa755, + 0xa757, + 0xa759, + 0xa75b, + 0xa75d, + 0xa75f, + 0xa761, + 0xa763, + 0xa765, + 0xa767, + 0xa769, + 0xa76b, + 0xa76d, + 0xa76f, + 0xa77a, + 0xa77c, + 0xa77f, + 0xa781, + 0xa783, + 0xa785, + 0xa787, + 0xa78c, + 0xa78e, + 0xa791, + 0xa793, + 0xa7a1, + 0xa7a3, + 0xa7a5, + 0xa7a7, + 0xa7a9, + 0xa7fa, + 0x1d4bb, + 0x1d7cb, +}; + +/* }}} */ + +/* {{{ Unicode title */ + +static uint32_t istitler[] = { + 0x0041, 0x005a, + 0x00c0, 0x00d6, + 0x00d8, 0x00de, + 0x0178, 0x0179, + 0x0181, 0x0182, + 0x0186, 0x0187, + 0x0189, 0x018b, + 0x018e, 0x0191, + 0x0193, 0x0194, + 0x0196, 0x0198, + 0x019c, 0x019d, + 0x019f, 0x01a0, + 0x01a6, 0x01a7, + 0x01ae, 0x01af, + 0x01b1, 0x01b3, + 0x01b7, 0x01b8, + 0x01f6, 0x01f8, + 0x023a, 0x023b, + 0x023d, 0x023e, + 0x0243, 0x0246, + 0x0388, 0x038a, + 0x038e, 0x038f, + 0x0391, 0x03a1, + 0x03a3, 0x03ab, + 0x03f9, 0x03fa, + 0x03fd, 0x042f, + 0x04c0, 0x04c1, + 0x0531, 0x0556, + 0x10a0, 0x10c5, + 0x1f08, 0x1f0f, + 0x1f18, 0x1f1d, + 0x1f28, 0x1f2f, + 0x1f38, 0x1f3f, + 0x1f48, 0x1f4d, + 0x1f68, 0x1f6f, + 0x1f88, 0x1f8f, + 0x1f98, 0x1f9f, + 0x1fa8, 0x1faf, + 0x1fb8, 0x1fbc, + 0x1fc8, 0x1fcc, + 0x1fd8, 0x1fdb, + 0x1fe8, 0x1fec, + 0x1ff8, 0x1ffc, + 0x2160, 0x216f, + 0x24b6, 0x24cf, + 0x2c00, 0x2c2e, + 0x2c62, 0x2c64, + 0x2c6d, 0x2c70, + 0x2c7e, 0x2c80, + 0xa77d, 0xa77e, + 0xff21, 0xff3a, + 0x10400, 0x10427, +}; + +/* }}} */ + +/* {{{ Title */ + +static uint32_t istitles[] = { + 0x0100, + 0x0102, + 0x0104, + 0x0106, + 0x0108, + 0x010a, + 0x010c, + 0x010e, + 0x0110, + 0x0112, + 0x0114, + 0x0116, + 0x0118, + 0x011a, + 0x011c, + 0x011e, + 0x0120, + 0x0122, + 0x0124, + 0x0126, + 0x0128, + 0x012a, + 0x012c, + 0x012e, + 0x0132, + 0x0134, + 0x0136, + 0x0139, + 0x013b, + 0x013d, + 0x013f, + 0x0141, + 0x0143, + 0x0145, + 0x0147, + 0x014a, + 0x014c, + 0x014e, + 0x0150, + 0x0152, + 0x0154, + 0x0156, + 0x0158, + 0x015a, + 0x015c, + 0x015e, + 0x0160, + 0x0162, + 0x0164, + 0x0166, + 0x0168, + 0x016a, + 0x016c, + 0x016e, + 0x0170, + 0x0172, + 0x0174, + 0x0176, + 0x017b, + 0x017d, + 0x0184, + 0x01a2, + 0x01a4, + 0x01a9, + 0x01ac, + 0x01b5, + 0x01bc, + 0x01c5, + 0x01c8, + 0x01cb, + 0x01cd, + 0x01cf, + 0x01d1, + 0x01d3, + 0x01d5, + 0x01d7, + 0x01d9, + 0x01db, + 0x01de, + 0x01e0, + 0x01e2, + 0x01e4, + 0x01e6, + 0x01e8, + 0x01ea, + 0x01ec, + 0x01ee, + 0x01f2, + 0x01f4, + 0x01fa, + 0x01fc, + 0x01fe, + 0x0200, + 0x0202, + 0x0204, + 0x0206, + 0x0208, + 0x020a, + 0x020c, + 0x020e, + 0x0210, + 0x0212, + 0x0214, + 0x0216, + 0x0218, + 0x021a, + 0x021c, + 0x021e, + 0x0220, + 0x0222, + 0x0224, + 0x0226, + 0x0228, + 0x022a, + 0x022c, + 0x022e, + 0x0230, + 0x0232, + 0x0241, + 0x0248, + 0x024a, + 0x024c, + 0x024e, + 0x0370, + 0x0372, + 0x0376, + 0x0386, + 0x038c, + 0x03cf, + 0x03d8, + 0x03da, + 0x03dc, + 0x03de, + 0x03e0, + 0x03e2, + 0x03e4, + 0x03e6, + 0x03e8, + 0x03ea, + 0x03ec, + 0x03ee, + 0x03f7, + 0x0460, + 0x0462, + 0x0464, + 0x0466, + 0x0468, + 0x046a, + 0x046c, + 0x046e, + 0x0470, + 0x0472, + 0x0474, + 0x0476, + 0x0478, + 0x047a, + 0x047c, + 0x047e, + 0x0480, + 0x048a, + 0x048c, + 0x048e, + 0x0490, + 0x0492, + 0x0494, + 0x0496, + 0x0498, + 0x049a, + 0x049c, + 0x049e, + 0x04a0, + 0x04a2, + 0x04a4, + 0x04a6, + 0x04a8, + 0x04aa, + 0x04ac, + 0x04ae, + 0x04b0, + 0x04b2, + 0x04b4, + 0x04b6, + 0x04b8, + 0x04ba, + 0x04bc, + 0x04be, + 0x04c3, + 0x04c5, + 0x04c7, + 0x04c9, + 0x04cb, + 0x04cd, + 0x04d0, + 0x04d2, + 0x04d4, + 0x04d6, + 0x04d8, + 0x04da, + 0x04dc, + 0x04de, + 0x04e0, + 0x04e2, + 0x04e4, + 0x04e6, + 0x04e8, + 0x04ea, + 0x04ec, + 0x04ee, + 0x04f0, + 0x04f2, + 0x04f4, + 0x04f6, + 0x04f8, + 0x04fa, + 0x04fc, + 0x04fe, + 0x0500, + 0x0502, + 0x0504, + 0x0506, + 0x0508, + 0x050a, + 0x050c, + 0x050e, + 0x0510, + 0x0512, + 0x0514, + 0x0516, + 0x0518, + 0x051a, + 0x051c, + 0x051e, + 0x0520, + 0x0522, + 0x0524, + 0x0526, + 0x10c7, + 0x10cd, + 0x1e00, + 0x1e02, + 0x1e04, + 0x1e06, + 0x1e08, + 0x1e0a, + 0x1e0c, + 0x1e0e, + 0x1e10, + 0x1e12, + 0x1e14, + 0x1e16, + 0x1e18, + 0x1e1a, + 0x1e1c, + 0x1e1e, + 0x1e20, + 0x1e22, + 0x1e24, + 0x1e26, + 0x1e28, + 0x1e2a, + 0x1e2c, + 0x1e2e, + 0x1e30, + 0x1e32, + 0x1e34, + 0x1e36, + 0x1e38, + 0x1e3a, + 0x1e3c, + 0x1e3e, + 0x1e40, + 0x1e42, + 0x1e44, + 0x1e46, + 0x1e48, + 0x1e4a, + 0x1e4c, + 0x1e4e, + 0x1e50, + 0x1e52, + 0x1e54, + 0x1e56, + 0x1e58, + 0x1e5a, + 0x1e5c, + 0x1e5e, + 0x1e60, + 0x1e62, + 0x1e64, + 0x1e66, + 0x1e68, + 0x1e6a, + 0x1e6c, + 0x1e6e, + 0x1e70, + 0x1e72, + 0x1e74, + 0x1e76, + 0x1e78, + 0x1e7a, + 0x1e7c, + 0x1e7e, + 0x1e80, + 0x1e82, + 0x1e84, + 0x1e86, + 0x1e88, + 0x1e8a, + 0x1e8c, + 0x1e8e, + 0x1e90, + 0x1e92, + 0x1e94, + 0x1ea0, + 0x1ea2, + 0x1ea4, + 0x1ea6, + 0x1ea8, + 0x1eaa, + 0x1eac, + 0x1eae, + 0x1eb0, + 0x1eb2, + 0x1eb4, + 0x1eb6, + 0x1eb8, + 0x1eba, + 0x1ebc, + 0x1ebe, + 0x1ec0, + 0x1ec2, + 0x1ec4, + 0x1ec6, + 0x1ec8, + 0x1eca, + 0x1ecc, + 0x1ece, + 0x1ed0, + 0x1ed2, + 0x1ed4, + 0x1ed6, + 0x1ed8, + 0x1eda, + 0x1edc, + 0x1ede, + 0x1ee0, + 0x1ee2, + 0x1ee4, + 0x1ee6, + 0x1ee8, + 0x1eea, + 0x1eec, + 0x1eee, + 0x1ef0, + 0x1ef2, + 0x1ef4, + 0x1ef6, + 0x1ef8, + 0x1efa, + 0x1efc, + 0x1efe, + 0x1f59, + 0x1f5b, + 0x1f5d, + 0x1f5f, + 0x2132, + 0x2183, + 0x2c60, + 0x2c67, + 0x2c69, + 0x2c6b, + 0x2c72, + 0x2c75, + 0x2c82, + 0x2c84, + 0x2c86, + 0x2c88, + 0x2c8a, + 0x2c8c, + 0x2c8e, + 0x2c90, + 0x2c92, + 0x2c94, + 0x2c96, + 0x2c98, + 0x2c9a, + 0x2c9c, + 0x2c9e, + 0x2ca0, + 0x2ca2, + 0x2ca4, + 0x2ca6, + 0x2ca8, + 0x2caa, + 0x2cac, + 0x2cae, + 0x2cb0, + 0x2cb2, + 0x2cb4, + 0x2cb6, + 0x2cb8, + 0x2cba, + 0x2cbc, + 0x2cbe, + 0x2cc0, + 0x2cc2, + 0x2cc4, + 0x2cc6, + 0x2cc8, + 0x2cca, + 0x2ccc, + 0x2cce, + 0x2cd0, + 0x2cd2, + 0x2cd4, + 0x2cd6, + 0x2cd8, + 0x2cda, + 0x2cdc, + 0x2cde, + 0x2ce0, + 0x2ce2, + 0x2ceb, + 0x2ced, + 0x2cf2, + 0xa640, + 0xa642, + 0xa644, + 0xa646, + 0xa648, + 0xa64a, + 0xa64c, + 0xa64e, + 0xa650, + 0xa652, + 0xa654, + 0xa656, + 0xa658, + 0xa65a, + 0xa65c, + 0xa65e, + 0xa660, + 0xa662, + 0xa664, + 0xa666, + 0xa668, + 0xa66a, + 0xa66c, + 0xa680, + 0xa682, + 0xa684, + 0xa686, + 0xa688, + 0xa68a, + 0xa68c, + 0xa68e, + 0xa690, + 0xa692, + 0xa694, + 0xa696, + 0xa722, + 0xa724, + 0xa726, + 0xa728, + 0xa72a, + 0xa72c, + 0xa72e, + 0xa732, + 0xa734, + 0xa736, + 0xa738, + 0xa73a, + 0xa73c, + 0xa73e, + 0xa740, + 0xa742, + 0xa744, + 0xa746, + 0xa748, + 0xa74a, + 0xa74c, + 0xa74e, + 0xa750, + 0xa752, + 0xa754, + 0xa756, + 0xa758, + 0xa75a, + 0xa75c, + 0xa75e, + 0xa760, + 0xa762, + 0xa764, + 0xa766, + 0xa768, + 0xa76a, + 0xa76c, + 0xa76e, + 0xa779, + 0xa77b, + 0xa780, + 0xa782, + 0xa784, + 0xa786, + 0xa78b, + 0xa78d, + 0xa790, + 0xa792, + 0xa7a0, + 0xa7a2, + 0xa7a4, + 0xa7a6, + 0xa7a8, + 0xa7aa, +}; + +/* }}} */ + +/* {{{ To upper */ + +static uint32_t toupperr[] = { + 0x0061, 0x007a, 1048544, + 0x00e0, 0x00f6, 1048544, + 0x00f8, 0x00fe, 1048544, + 0x023f, 0x0240, 1059391, + 0x0256, 0x0257, 1048371, + 0x028a, 0x028b, 1048359, + 0x037b, 0x037d, 1048706, + 0x03ad, 0x03af, 1048539, + 0x03b1, 0x03c1, 1048544, + 0x03c3, 0x03cb, 1048544, + 0x03cd, 0x03ce, 1048513, + 0x0430, 0x044f, 1048544, + 0x0450, 0x045f, 1048496, + 0x0561, 0x0586, 1048528, + 0x1f00, 0x1f07, 1048584, + 0x1f10, 0x1f15, 1048584, + 0x1f20, 0x1f27, 1048584, + 0x1f30, 0x1f37, 1048584, + 0x1f40, 0x1f45, 1048584, + 0x1f60, 0x1f67, 1048584, + 0x1f70, 0x1f71, 1048650, + 0x1f72, 0x1f75, 1048662, + 0x1f76, 0x1f77, 1048676, + 0x1f78, 0x1f79, 1048704, + 0x1f7a, 0x1f7b, 1048688, + 0x1f7c, 0x1f7d, 1048702, + 0x1f80, 0x1f87, 1048584, + 0x1f90, 0x1f97, 1048584, + 0x1fa0, 0x1fa7, 1048584, + 0x1fb0, 0x1fb1, 1048584, + 0x1fd0, 0x1fd1, 1048584, + 0x1fe0, 0x1fe1, 1048584, + 0x2170, 0x217f, 1048560, + 0x24d0, 0x24e9, 1048550, + 0x2c30, 0x2c5e, 1048528, + 0x2d00, 0x2d25, 1041312, + 0xff41, 0xff5a, 1048544, + 0x10428, 0x1044f, 1048536, +}; + +static uint32_t touppers[] = { + 0x00b5, 1049319, + 0x00ff, 1048697, + 0x0101, 1048575, + 0x0103, 1048575, + 0x0105, 1048575, + 0x0107, 1048575, + 0x0109, 1048575, + 0x010b, 1048575, + 0x010d, 1048575, + 0x010f, 1048575, + 0x0111, 1048575, + 0x0113, 1048575, + 0x0115, 1048575, + 0x0117, 1048575, + 0x0119, 1048575, + 0x011b, 1048575, + 0x011d, 1048575, + 0x011f, 1048575, + 0x0121, 1048575, + 0x0123, 1048575, + 0x0125, 1048575, + 0x0127, 1048575, + 0x0129, 1048575, + 0x012b, 1048575, + 0x012d, 1048575, + 0x012f, 1048575, + 0x0131, 1048344, + 0x0133, 1048575, + 0x0135, 1048575, + 0x0137, 1048575, + 0x013a, 1048575, + 0x013c, 1048575, + 0x013e, 1048575, + 0x0140, 1048575, + 0x0142, 1048575, + 0x0144, 1048575, + 0x0146, 1048575, + 0x0148, 1048575, + 0x014b, 1048575, + 0x014d, 1048575, + 0x014f, 1048575, + 0x0151, 1048575, + 0x0153, 1048575, + 0x0155, 1048575, + 0x0157, 1048575, + 0x0159, 1048575, + 0x015b, 1048575, + 0x015d, 1048575, + 0x015f, 1048575, + 0x0161, 1048575, + 0x0163, 1048575, + 0x0165, 1048575, + 0x0167, 1048575, + 0x0169, 1048575, + 0x016b, 1048575, + 0x016d, 1048575, + 0x016f, 1048575, + 0x0171, 1048575, + 0x0173, 1048575, + 0x0175, 1048575, + 0x0177, 1048575, + 0x017a, 1048575, + 0x017c, 1048575, + 0x017e, 1048575, + 0x017f, 1048276, + 0x0180, 1048771, + 0x0183, 1048575, + 0x0185, 1048575, + 0x0188, 1048575, + 0x018c, 1048575, + 0x0192, 1048575, + 0x0195, 1048673, + 0x0199, 1048575, + 0x019a, 1048739, + 0x019e, 1048706, + 0x01a1, 1048575, + 0x01a3, 1048575, + 0x01a5, 1048575, + 0x01a8, 1048575, + 0x01ad, 1048575, + 0x01b0, 1048575, + 0x01b4, 1048575, + 0x01b6, 1048575, + 0x01b9, 1048575, + 0x01bd, 1048575, + 0x01bf, 1048632, + 0x01c5, 1048575, + 0x01c6, 1048574, + 0x01c8, 1048575, + 0x01c9, 1048574, + 0x01cb, 1048575, + 0x01cc, 1048574, + 0x01ce, 1048575, + 0x01d0, 1048575, + 0x01d2, 1048575, + 0x01d4, 1048575, + 0x01d6, 1048575, + 0x01d8, 1048575, + 0x01da, 1048575, + 0x01dc, 1048575, + 0x01dd, 1048497, + 0x01df, 1048575, + 0x01e1, 1048575, + 0x01e3, 1048575, + 0x01e5, 1048575, + 0x01e7, 1048575, + 0x01e9, 1048575, + 0x01eb, 1048575, + 0x01ed, 1048575, + 0x01ef, 1048575, + 0x01f2, 1048575, + 0x01f3, 1048574, + 0x01f5, 1048575, + 0x01f9, 1048575, + 0x01fb, 1048575, + 0x01fd, 1048575, + 0x01ff, 1048575, + 0x0201, 1048575, + 0x0203, 1048575, + 0x0205, 1048575, + 0x0207, 1048575, + 0x0209, 1048575, + 0x020b, 1048575, + 0x020d, 1048575, + 0x020f, 1048575, + 0x0211, 1048575, + 0x0213, 1048575, + 0x0215, 1048575, + 0x0217, 1048575, + 0x0219, 1048575, + 0x021b, 1048575, + 0x021d, 1048575, + 0x021f, 1048575, + 0x0223, 1048575, + 0x0225, 1048575, + 0x0227, 1048575, + 0x0229, 1048575, + 0x022b, 1048575, + 0x022d, 1048575, + 0x022f, 1048575, + 0x0231, 1048575, + 0x0233, 1048575, + 0x023c, 1048575, + 0x0242, 1048575, + 0x0247, 1048575, + 0x0249, 1048575, + 0x024b, 1048575, + 0x024d, 1048575, + 0x024f, 1048575, + 0x0250, 1059359, + 0x0251, 1059356, + 0x0252, 1059358, + 0x0253, 1048366, + 0x0254, 1048370, + 0x0259, 1048374, + 0x025b, 1048373, + 0x0260, 1048371, + 0x0263, 1048369, + 0x0265, 1090856, + 0x0266, 1090884, + 0x0268, 1048367, + 0x0269, 1048365, + 0x026b, 1059319, + 0x026f, 1048365, + 0x0271, 1059325, + 0x0272, 1048363, + 0x0275, 1048362, + 0x027d, 1059303, + 0x0280, 1048358, + 0x0283, 1048358, + 0x0288, 1048358, + 0x0289, 1048507, + 0x028c, 1048505, + 0x0292, 1048357, + 0x0345, 1048660, + 0x0371, 1048575, + 0x0373, 1048575, + 0x0377, 1048575, + 0x03ac, 1048538, + 0x03c2, 1048545, + 0x03cc, 1048512, + 0x03d0, 1048514, + 0x03d1, 1048519, + 0x03d5, 1048529, + 0x03d6, 1048522, + 0x03d7, 1048568, + 0x03d9, 1048575, + 0x03db, 1048575, + 0x03dd, 1048575, + 0x03df, 1048575, + 0x03e1, 1048575, + 0x03e3, 1048575, + 0x03e5, 1048575, + 0x03e7, 1048575, + 0x03e9, 1048575, + 0x03eb, 1048575, + 0x03ed, 1048575, + 0x03ef, 1048575, + 0x03f0, 1048490, + 0x03f1, 1048496, + 0x03f2, 1048583, + 0x03f5, 1048480, + 0x03f8, 1048575, + 0x03fb, 1048575, + 0x0461, 1048575, + 0x0463, 1048575, + 0x0465, 1048575, + 0x0467, 1048575, + 0x0469, 1048575, + 0x046b, 1048575, + 0x046d, 1048575, + 0x046f, 1048575, + 0x0471, 1048575, + 0x0473, 1048575, + 0x0475, 1048575, + 0x0477, 1048575, + 0x0479, 1048575, + 0x047b, 1048575, + 0x047d, 1048575, + 0x047f, 1048575, + 0x0481, 1048575, + 0x048b, 1048575, + 0x048d, 1048575, + 0x048f, 1048575, + 0x0491, 1048575, + 0x0493, 1048575, + 0x0495, 1048575, + 0x0497, 1048575, + 0x0499, 1048575, + 0x049b, 1048575, + 0x049d, 1048575, + 0x049f, 1048575, + 0x04a1, 1048575, + 0x04a3, 1048575, + 0x04a5, 1048575, + 0x04a7, 1048575, + 0x04a9, 1048575, + 0x04ab, 1048575, + 0x04ad, 1048575, + 0x04af, 1048575, + 0x04b1, 1048575, + 0x04b3, 1048575, + 0x04b5, 1048575, + 0x04b7, 1048575, + 0x04b9, 1048575, + 0x04bb, 1048575, + 0x04bd, 1048575, + 0x04bf, 1048575, + 0x04c2, 1048575, + 0x04c4, 1048575, + 0x04c6, 1048575, + 0x04c8, 1048575, + 0x04ca, 1048575, + 0x04cc, 1048575, + 0x04ce, 1048575, + 0x04cf, 1048561, + 0x04d1, 1048575, + 0x04d3, 1048575, + 0x04d5, 1048575, + 0x04d7, 1048575, + 0x04d9, 1048575, + 0x04db, 1048575, + 0x04dd, 1048575, + 0x04df, 1048575, + 0x04e1, 1048575, + 0x04e3, 1048575, + 0x04e5, 1048575, + 0x04e7, 1048575, + 0x04e9, 1048575, + 0x04eb, 1048575, + 0x04ed, 1048575, + 0x04ef, 1048575, + 0x04f1, 1048575, + 0x04f3, 1048575, + 0x04f5, 1048575, + 0x04f7, 1048575, + 0x04f9, 1048575, + 0x04fb, 1048575, + 0x04fd, 1048575, + 0x04ff, 1048575, + 0x0501, 1048575, + 0x0503, 1048575, + 0x0505, 1048575, + 0x0507, 1048575, + 0x0509, 1048575, + 0x050b, 1048575, + 0x050d, 1048575, + 0x050f, 1048575, + 0x0511, 1048575, + 0x0513, 1048575, + 0x0515, 1048575, + 0x0517, 1048575, + 0x0519, 1048575, + 0x051b, 1048575, + 0x051d, 1048575, + 0x051f, 1048575, + 0x0521, 1048575, + 0x0523, 1048575, + 0x0525, 1048575, + 0x0527, 1048575, + 0x1d79, 1083908, + 0x1d7d, 1052390, + 0x1e01, 1048575, + 0x1e03, 1048575, + 0x1e05, 1048575, + 0x1e07, 1048575, + 0x1e09, 1048575, + 0x1e0b, 1048575, + 0x1e0d, 1048575, + 0x1e0f, 1048575, + 0x1e11, 1048575, + 0x1e13, 1048575, + 0x1e15, 1048575, + 0x1e17, 1048575, + 0x1e19, 1048575, + 0x1e1b, 1048575, + 0x1e1d, 1048575, + 0x1e1f, 1048575, + 0x1e21, 1048575, + 0x1e23, 1048575, + 0x1e25, 1048575, + 0x1e27, 1048575, + 0x1e29, 1048575, + 0x1e2b, 1048575, + 0x1e2d, 1048575, + 0x1e2f, 1048575, + 0x1e31, 1048575, + 0x1e33, 1048575, + 0x1e35, 1048575, + 0x1e37, 1048575, + 0x1e39, 1048575, + 0x1e3b, 1048575, + 0x1e3d, 1048575, + 0x1e3f, 1048575, + 0x1e41, 1048575, + 0x1e43, 1048575, + 0x1e45, 1048575, + 0x1e47, 1048575, + 0x1e49, 1048575, + 0x1e4b, 1048575, + 0x1e4d, 1048575, + 0x1e4f, 1048575, + 0x1e51, 1048575, + 0x1e53, 1048575, + 0x1e55, 1048575, + 0x1e57, 1048575, + 0x1e59, 1048575, + 0x1e5b, 1048575, + 0x1e5d, 1048575, + 0x1e5f, 1048575, + 0x1e61, 1048575, + 0x1e63, 1048575, + 0x1e65, 1048575, + 0x1e67, 1048575, + 0x1e69, 1048575, + 0x1e6b, 1048575, + 0x1e6d, 1048575, + 0x1e6f, 1048575, + 0x1e71, 1048575, + 0x1e73, 1048575, + 0x1e75, 1048575, + 0x1e77, 1048575, + 0x1e79, 1048575, + 0x1e7b, 1048575, + 0x1e7d, 1048575, + 0x1e7f, 1048575, + 0x1e81, 1048575, + 0x1e83, 1048575, + 0x1e85, 1048575, + 0x1e87, 1048575, + 0x1e89, 1048575, + 0x1e8b, 1048575, + 0x1e8d, 1048575, + 0x1e8f, 1048575, + 0x1e91, 1048575, + 0x1e93, 1048575, + 0x1e95, 1048575, + 0x1e9b, 1048517, + 0x1ea1, 1048575, + 0x1ea3, 1048575, + 0x1ea5, 1048575, + 0x1ea7, 1048575, + 0x1ea9, 1048575, + 0x1eab, 1048575, + 0x1ead, 1048575, + 0x1eaf, 1048575, + 0x1eb1, 1048575, + 0x1eb3, 1048575, + 0x1eb5, 1048575, + 0x1eb7, 1048575, + 0x1eb9, 1048575, + 0x1ebb, 1048575, + 0x1ebd, 1048575, + 0x1ebf, 1048575, + 0x1ec1, 1048575, + 0x1ec3, 1048575, + 0x1ec5, 1048575, + 0x1ec7, 1048575, + 0x1ec9, 1048575, + 0x1ecb, 1048575, + 0x1ecd, 1048575, + 0x1ecf, 1048575, + 0x1ed1, 1048575, + 0x1ed3, 1048575, + 0x1ed5, 1048575, + 0x1ed7, 1048575, + 0x1ed9, 1048575, + 0x1edb, 1048575, + 0x1edd, 1048575, + 0x1edf, 1048575, + 0x1ee1, 1048575, + 0x1ee3, 1048575, + 0x1ee5, 1048575, + 0x1ee7, 1048575, + 0x1ee9, 1048575, + 0x1eeb, 1048575, + 0x1eed, 1048575, + 0x1eef, 1048575, + 0x1ef1, 1048575, + 0x1ef3, 1048575, + 0x1ef5, 1048575, + 0x1ef7, 1048575, + 0x1ef9, 1048575, + 0x1efb, 1048575, + 0x1efd, 1048575, + 0x1eff, 1048575, + 0x1f51, 1048584, + 0x1f53, 1048584, + 0x1f55, 1048584, + 0x1f57, 1048584, + 0x1fb3, 1048585, + 0x1fbe, 1041371, + 0x1fc3, 1048585, + 0x1fe5, 1048583, + 0x1ff3, 1048585, + 0x214e, 1048548, + 0x2184, 1048575, + 0x2c61, 1048575, + 0x2c65, 1037781, + 0x2c66, 1037784, + 0x2c68, 1048575, + 0x2c6a, 1048575, + 0x2c6c, 1048575, + 0x2c73, 1048575, + 0x2c76, 1048575, + 0x2c81, 1048575, + 0x2c83, 1048575, + 0x2c85, 1048575, + 0x2c87, 1048575, + 0x2c89, 1048575, + 0x2c8b, 1048575, + 0x2c8d, 1048575, + 0x2c8f, 1048575, + 0x2c91, 1048575, + 0x2c93, 1048575, + 0x2c95, 1048575, + 0x2c97, 1048575, + 0x2c99, 1048575, + 0x2c9b, 1048575, + 0x2c9d, 1048575, + 0x2c9f, 1048575, + 0x2ca1, 1048575, + 0x2ca3, 1048575, + 0x2ca5, 1048575, + 0x2ca7, 1048575, + 0x2ca9, 1048575, + 0x2cab, 1048575, + 0x2cad, 1048575, + 0x2caf, 1048575, + 0x2cb1, 1048575, + 0x2cb3, 1048575, + 0x2cb5, 1048575, + 0x2cb7, 1048575, + 0x2cb9, 1048575, + 0x2cbb, 1048575, + 0x2cbd, 1048575, + 0x2cbf, 1048575, + 0x2cc1, 1048575, + 0x2cc3, 1048575, + 0x2cc5, 1048575, + 0x2cc7, 1048575, + 0x2cc9, 1048575, + 0x2ccb, 1048575, + 0x2ccd, 1048575, + 0x2ccf, 1048575, + 0x2cd1, 1048575, + 0x2cd3, 1048575, + 0x2cd5, 1048575, + 0x2cd7, 1048575, + 0x2cd9, 1048575, + 0x2cdb, 1048575, + 0x2cdd, 1048575, + 0x2cdf, 1048575, + 0x2ce1, 1048575, + 0x2ce3, 1048575, + 0x2cec, 1048575, + 0x2cee, 1048575, + 0x2cf3, 1048575, + 0x2d27, 1041312, + 0x2d2d, 1041312, + 0xa641, 1048575, + 0xa643, 1048575, + 0xa645, 1048575, + 0xa647, 1048575, + 0xa649, 1048575, + 0xa64b, 1048575, + 0xa64d, 1048575, + 0xa64f, 1048575, + 0xa651, 1048575, + 0xa653, 1048575, + 0xa655, 1048575, + 0xa657, 1048575, + 0xa659, 1048575, + 0xa65b, 1048575, + 0xa65d, 1048575, + 0xa65f, 1048575, + 0xa661, 1048575, + 0xa663, 1048575, + 0xa665, 1048575, + 0xa667, 1048575, + 0xa669, 1048575, + 0xa66b, 1048575, + 0xa66d, 1048575, + 0xa681, 1048575, + 0xa683, 1048575, + 0xa685, 1048575, + 0xa687, 1048575, + 0xa689, 1048575, + 0xa68b, 1048575, + 0xa68d, 1048575, + 0xa68f, 1048575, + 0xa691, 1048575, + 0xa693, 1048575, + 0xa695, 1048575, + 0xa697, 1048575, + 0xa723, 1048575, + 0xa725, 1048575, + 0xa727, 1048575, + 0xa729, 1048575, + 0xa72b, 1048575, + 0xa72d, 1048575, + 0xa72f, 1048575, + 0xa733, 1048575, + 0xa735, 1048575, + 0xa737, 1048575, + 0xa739, 1048575, + 0xa73b, 1048575, + 0xa73d, 1048575, + 0xa73f, 1048575, + 0xa741, 1048575, + 0xa743, 1048575, + 0xa745, 1048575, + 0xa747, 1048575, + 0xa749, 1048575, + 0xa74b, 1048575, + 0xa74d, 1048575, + 0xa74f, 1048575, + 0xa751, 1048575, + 0xa753, 1048575, + 0xa755, 1048575, + 0xa757, 1048575, + 0xa759, 1048575, + 0xa75b, 1048575, + 0xa75d, 1048575, + 0xa75f, 1048575, + 0xa761, 1048575, + 0xa763, 1048575, + 0xa765, 1048575, + 0xa767, 1048575, + 0xa769, 1048575, + 0xa76b, 1048575, + 0xa76d, 1048575, + 0xa76f, 1048575, + 0xa77a, 1048575, + 0xa77c, 1048575, + 0xa77f, 1048575, + 0xa781, 1048575, + 0xa783, 1048575, + 0xa785, 1048575, + 0xa787, 1048575, + 0xa78c, 1048575, + 0xa791, 1048575, + 0xa793, 1048575, + 0xa7a1, 1048575, + 0xa7a3, 1048575, + 0xa7a5, 1048575, + 0xa7a7, 1048575, + 0xa7a9, 1048575, +}; + +/* }}} */ + +/* {{{ To lower */ + +static uint32_t tolowerr[] = { + 0x0041, 0x005a, 1048608, + 0x00c0, 0x00d6, 1048608, + 0x00d8, 0x00de, 1048608, + 0x0189, 0x018a, 1048781, + 0x01b1, 0x01b2, 1048793, + 0x0388, 0x038a, 1048613, + 0x038e, 0x038f, 1048639, + 0x0391, 0x03a1, 1048608, + 0x03a3, 0x03ab, 1048608, + 0x03fd, 0x03ff, 1048446, + 0x0400, 0x040f, 1048656, + 0x0410, 0x042f, 1048608, + 0x0531, 0x0556, 1048624, + 0x10a0, 0x10c5, 1055840, + 0x1f08, 0x1f0f, 1048568, + 0x1f18, 0x1f1d, 1048568, + 0x1f28, 0x1f2f, 1048568, + 0x1f38, 0x1f3f, 1048568, + 0x1f48, 0x1f4d, 1048568, + 0x1f68, 0x1f6f, 1048568, + 0x1f88, 0x1f8f, 1048568, + 0x1f98, 0x1f9f, 1048568, + 0x1fa8, 0x1faf, 1048568, + 0x1fb8, 0x1fb9, 1048568, + 0x1fba, 0x1fbb, 1048502, + 0x1fc8, 0x1fcb, 1048490, + 0x1fd8, 0x1fd9, 1048568, + 0x1fda, 0x1fdb, 1048476, + 0x1fe8, 0x1fe9, 1048568, + 0x1fea, 0x1feb, 1048464, + 0x1ff8, 0x1ff9, 1048448, + 0x1ffa, 0x1ffb, 1048450, + 0x2160, 0x216f, 1048592, + 0x24b6, 0x24cf, 1048602, + 0x2c00, 0x2c2e, 1048624, + 0x2c7e, 0x2c7f, 1037761, + 0xff21, 0xff3a, 1048608, + 0x10400, 0x10427, 1048616, +}; + +static uint32_t tolowers[] = { + 0x0100, 1048577, + 0x0102, 1048577, + 0x0104, 1048577, + 0x0106, 1048577, + 0x0108, 1048577, + 0x010a, 1048577, + 0x010c, 1048577, + 0x010e, 1048577, + 0x0110, 1048577, + 0x0112, 1048577, + 0x0114, 1048577, + 0x0116, 1048577, + 0x0118, 1048577, + 0x011a, 1048577, + 0x011c, 1048577, + 0x011e, 1048577, + 0x0120, 1048577, + 0x0122, 1048577, + 0x0124, 1048577, + 0x0126, 1048577, + 0x0128, 1048577, + 0x012a, 1048577, + 0x012c, 1048577, + 0x012e, 1048577, + 0x0130, 1048377, + 0x0132, 1048577, + 0x0134, 1048577, + 0x0136, 1048577, + 0x0139, 1048577, + 0x013b, 1048577, + 0x013d, 1048577, + 0x013f, 1048577, + 0x0141, 1048577, + 0x0143, 1048577, + 0x0145, 1048577, + 0x0147, 1048577, + 0x014a, 1048577, + 0x014c, 1048577, + 0x014e, 1048577, + 0x0150, 1048577, + 0x0152, 1048577, + 0x0154, 1048577, + 0x0156, 1048577, + 0x0158, 1048577, + 0x015a, 1048577, + 0x015c, 1048577, + 0x015e, 1048577, + 0x0160, 1048577, + 0x0162, 1048577, + 0x0164, 1048577, + 0x0166, 1048577, + 0x0168, 1048577, + 0x016a, 1048577, + 0x016c, 1048577, + 0x016e, 1048577, + 0x0170, 1048577, + 0x0172, 1048577, + 0x0174, 1048577, + 0x0176, 1048577, + 0x0178, 1048455, + 0x0179, 1048577, + 0x017b, 1048577, + 0x017d, 1048577, + 0x0181, 1048786, + 0x0182, 1048577, + 0x0184, 1048577, + 0x0186, 1048782, + 0x0187, 1048577, + 0x018b, 1048577, + 0x018e, 1048655, + 0x018f, 1048778, + 0x0190, 1048779, + 0x0191, 1048577, + 0x0193, 1048781, + 0x0194, 1048783, + 0x0196, 1048787, + 0x0197, 1048785, + 0x0198, 1048577, + 0x019c, 1048787, + 0x019d, 1048789, + 0x019f, 1048790, + 0x01a0, 1048577, + 0x01a2, 1048577, + 0x01a4, 1048577, + 0x01a6, 1048794, + 0x01a7, 1048577, + 0x01a9, 1048794, + 0x01ac, 1048577, + 0x01ae, 1048794, + 0x01af, 1048577, + 0x01b3, 1048577, + 0x01b5, 1048577, + 0x01b7, 1048795, + 0x01b8, 1048577, + 0x01bc, 1048577, + 0x01c4, 1048578, + 0x01c5, 1048577, + 0x01c7, 1048578, + 0x01c8, 1048577, + 0x01ca, 1048578, + 0x01cb, 1048577, + 0x01cd, 1048577, + 0x01cf, 1048577, + 0x01d1, 1048577, + 0x01d3, 1048577, + 0x01d5, 1048577, + 0x01d7, 1048577, + 0x01d9, 1048577, + 0x01db, 1048577, + 0x01de, 1048577, + 0x01e0, 1048577, + 0x01e2, 1048577, + 0x01e4, 1048577, + 0x01e6, 1048577, + 0x01e8, 1048577, + 0x01ea, 1048577, + 0x01ec, 1048577, + 0x01ee, 1048577, + 0x01f1, 1048578, + 0x01f2, 1048577, + 0x01f4, 1048577, + 0x01f6, 1048479, + 0x01f7, 1048520, + 0x01f8, 1048577, + 0x01fa, 1048577, + 0x01fc, 1048577, + 0x01fe, 1048577, + 0x0200, 1048577, + 0x0202, 1048577, + 0x0204, 1048577, + 0x0206, 1048577, + 0x0208, 1048577, + 0x020a, 1048577, + 0x020c, 1048577, + 0x020e, 1048577, + 0x0210, 1048577, + 0x0212, 1048577, + 0x0214, 1048577, + 0x0216, 1048577, + 0x0218, 1048577, + 0x021a, 1048577, + 0x021c, 1048577, + 0x021e, 1048577, + 0x0220, 1048446, + 0x0222, 1048577, + 0x0224, 1048577, + 0x0226, 1048577, + 0x0228, 1048577, + 0x022a, 1048577, + 0x022c, 1048577, + 0x022e, 1048577, + 0x0230, 1048577, + 0x0232, 1048577, + 0x023a, 1059371, + 0x023b, 1048577, + 0x023d, 1048413, + 0x023e, 1059368, + 0x0241, 1048577, + 0x0243, 1048381, + 0x0244, 1048645, + 0x0245, 1048647, + 0x0246, 1048577, + 0x0248, 1048577, + 0x024a, 1048577, + 0x024c, 1048577, + 0x024e, 1048577, + 0x0370, 1048577, + 0x0372, 1048577, + 0x0376, 1048577, + 0x0386, 1048614, + 0x038c, 1048640, + 0x03cf, 1048584, + 0x03d8, 1048577, + 0x03da, 1048577, + 0x03dc, 1048577, + 0x03de, 1048577, + 0x03e0, 1048577, + 0x03e2, 1048577, + 0x03e4, 1048577, + 0x03e6, 1048577, + 0x03e8, 1048577, + 0x03ea, 1048577, + 0x03ec, 1048577, + 0x03ee, 1048577, + 0x03f4, 1048516, + 0x03f7, 1048577, + 0x03f9, 1048569, + 0x03fa, 1048577, + 0x0460, 1048577, + 0x0462, 1048577, + 0x0464, 1048577, + 0x0466, 1048577, + 0x0468, 1048577, + 0x046a, 1048577, + 0x046c, 1048577, + 0x046e, 1048577, + 0x0470, 1048577, + 0x0472, 1048577, + 0x0474, 1048577, + 0x0476, 1048577, + 0x0478, 1048577, + 0x047a, 1048577, + 0x047c, 1048577, + 0x047e, 1048577, + 0x0480, 1048577, + 0x048a, 1048577, + 0x048c, 1048577, + 0x048e, 1048577, + 0x0490, 1048577, + 0x0492, 1048577, + 0x0494, 1048577, + 0x0496, 1048577, + 0x0498, 1048577, + 0x049a, 1048577, + 0x049c, 1048577, + 0x049e, 1048577, + 0x04a0, 1048577, + 0x04a2, 1048577, + 0x04a4, 1048577, + 0x04a6, 1048577, + 0x04a8, 1048577, + 0x04aa, 1048577, + 0x04ac, 1048577, + 0x04ae, 1048577, + 0x04b0, 1048577, + 0x04b2, 1048577, + 0x04b4, 1048577, + 0x04b6, 1048577, + 0x04b8, 1048577, + 0x04ba, 1048577, + 0x04bc, 1048577, + 0x04be, 1048577, + 0x04c0, 1048591, + 0x04c1, 1048577, + 0x04c3, 1048577, + 0x04c5, 1048577, + 0x04c7, 1048577, + 0x04c9, 1048577, + 0x04cb, 1048577, + 0x04cd, 1048577, + 0x04d0, 1048577, + 0x04d2, 1048577, + 0x04d4, 1048577, + 0x04d6, 1048577, + 0x04d8, 1048577, + 0x04da, 1048577, + 0x04dc, 1048577, + 0x04de, 1048577, + 0x04e0, 1048577, + 0x04e2, 1048577, + 0x04e4, 1048577, + 0x04e6, 1048577, + 0x04e8, 1048577, + 0x04ea, 1048577, + 0x04ec, 1048577, + 0x04ee, 1048577, + 0x04f0, 1048577, + 0x04f2, 1048577, + 0x04f4, 1048577, + 0x04f6, 1048577, + 0x04f8, 1048577, + 0x04fa, 1048577, + 0x04fc, 1048577, + 0x04fe, 1048577, + 0x0500, 1048577, + 0x0502, 1048577, + 0x0504, 1048577, + 0x0506, 1048577, + 0x0508, 1048577, + 0x050a, 1048577, + 0x050c, 1048577, + 0x050e, 1048577, + 0x0510, 1048577, + 0x0512, 1048577, + 0x0514, 1048577, + 0x0516, 1048577, + 0x0518, 1048577, + 0x051a, 1048577, + 0x051c, 1048577, + 0x051e, 1048577, + 0x0520, 1048577, + 0x0522, 1048577, + 0x0524, 1048577, + 0x0526, 1048577, + 0x10c7, 1055840, + 0x10cd, 1055840, + 0x1e00, 1048577, + 0x1e02, 1048577, + 0x1e04, 1048577, + 0x1e06, 1048577, + 0x1e08, 1048577, + 0x1e0a, 1048577, + 0x1e0c, 1048577, + 0x1e0e, 1048577, + 0x1e10, 1048577, + 0x1e12, 1048577, + 0x1e14, 1048577, + 0x1e16, 1048577, + 0x1e18, 1048577, + 0x1e1a, 1048577, + 0x1e1c, 1048577, + 0x1e1e, 1048577, + 0x1e20, 1048577, + 0x1e22, 1048577, + 0x1e24, 1048577, + 0x1e26, 1048577, + 0x1e28, 1048577, + 0x1e2a, 1048577, + 0x1e2c, 1048577, + 0x1e2e, 1048577, + 0x1e30, 1048577, + 0x1e32, 1048577, + 0x1e34, 1048577, + 0x1e36, 1048577, + 0x1e38, 1048577, + 0x1e3a, 1048577, + 0x1e3c, 1048577, + 0x1e3e, 1048577, + 0x1e40, 1048577, + 0x1e42, 1048577, + 0x1e44, 1048577, + 0x1e46, 1048577, + 0x1e48, 1048577, + 0x1e4a, 1048577, + 0x1e4c, 1048577, + 0x1e4e, 1048577, + 0x1e50, 1048577, + 0x1e52, 1048577, + 0x1e54, 1048577, + 0x1e56, 1048577, + 0x1e58, 1048577, + 0x1e5a, 1048577, + 0x1e5c, 1048577, + 0x1e5e, 1048577, + 0x1e60, 1048577, + 0x1e62, 1048577, + 0x1e64, 1048577, + 0x1e66, 1048577, + 0x1e68, 1048577, + 0x1e6a, 1048577, + 0x1e6c, 1048577, + 0x1e6e, 1048577, + 0x1e70, 1048577, + 0x1e72, 1048577, + 0x1e74, 1048577, + 0x1e76, 1048577, + 0x1e78, 1048577, + 0x1e7a, 1048577, + 0x1e7c, 1048577, + 0x1e7e, 1048577, + 0x1e80, 1048577, + 0x1e82, 1048577, + 0x1e84, 1048577, + 0x1e86, 1048577, + 0x1e88, 1048577, + 0x1e8a, 1048577, + 0x1e8c, 1048577, + 0x1e8e, 1048577, + 0x1e90, 1048577, + 0x1e92, 1048577, + 0x1e94, 1048577, + 0x1e9e, 1040961, + 0x1ea0, 1048577, + 0x1ea2, 1048577, + 0x1ea4, 1048577, + 0x1ea6, 1048577, + 0x1ea8, 1048577, + 0x1eaa, 1048577, + 0x1eac, 1048577, + 0x1eae, 1048577, + 0x1eb0, 1048577, + 0x1eb2, 1048577, + 0x1eb4, 1048577, + 0x1eb6, 1048577, + 0x1eb8, 1048577, + 0x1eba, 1048577, + 0x1ebc, 1048577, + 0x1ebe, 1048577, + 0x1ec0, 1048577, + 0x1ec2, 1048577, + 0x1ec4, 1048577, + 0x1ec6, 1048577, + 0x1ec8, 1048577, + 0x1eca, 1048577, + 0x1ecc, 1048577, + 0x1ece, 1048577, + 0x1ed0, 1048577, + 0x1ed2, 1048577, + 0x1ed4, 1048577, + 0x1ed6, 1048577, + 0x1ed8, 1048577, + 0x1eda, 1048577, + 0x1edc, 1048577, + 0x1ede, 1048577, + 0x1ee0, 1048577, + 0x1ee2, 1048577, + 0x1ee4, 1048577, + 0x1ee6, 1048577, + 0x1ee8, 1048577, + 0x1eea, 1048577, + 0x1eec, 1048577, + 0x1eee, 1048577, + 0x1ef0, 1048577, + 0x1ef2, 1048577, + 0x1ef4, 1048577, + 0x1ef6, 1048577, + 0x1ef8, 1048577, + 0x1efa, 1048577, + 0x1efc, 1048577, + 0x1efe, 1048577, + 0x1f59, 1048568, + 0x1f5b, 1048568, + 0x1f5d, 1048568, + 0x1f5f, 1048568, + 0x1fbc, 1048567, + 0x1fcc, 1048567, + 0x1fec, 1048569, + 0x1ffc, 1048567, + 0x2126, 1041059, + 0x212a, 1040193, + 0x212b, 1040314, + 0x2132, 1048604, + 0x2183, 1048577, + 0x2c60, 1048577, + 0x2c62, 1037833, + 0x2c63, 1044762, + 0x2c64, 1037849, + 0x2c67, 1048577, + 0x2c69, 1048577, + 0x2c6b, 1048577, + 0x2c6d, 1037796, + 0x2c6e, 1037827, + 0x2c6f, 1037793, + 0x2c70, 1037794, + 0x2c72, 1048577, + 0x2c75, 1048577, + 0x2c80, 1048577, + 0x2c82, 1048577, + 0x2c84, 1048577, + 0x2c86, 1048577, + 0x2c88, 1048577, + 0x2c8a, 1048577, + 0x2c8c, 1048577, + 0x2c8e, 1048577, + 0x2c90, 1048577, + 0x2c92, 1048577, + 0x2c94, 1048577, + 0x2c96, 1048577, + 0x2c98, 1048577, + 0x2c9a, 1048577, + 0x2c9c, 1048577, + 0x2c9e, 1048577, + 0x2ca0, 1048577, + 0x2ca2, 1048577, + 0x2ca4, 1048577, + 0x2ca6, 1048577, + 0x2ca8, 1048577, + 0x2caa, 1048577, + 0x2cac, 1048577, + 0x2cae, 1048577, + 0x2cb0, 1048577, + 0x2cb2, 1048577, + 0x2cb4, 1048577, + 0x2cb6, 1048577, + 0x2cb8, 1048577, + 0x2cba, 1048577, + 0x2cbc, 1048577, + 0x2cbe, 1048577, + 0x2cc0, 1048577, + 0x2cc2, 1048577, + 0x2cc4, 1048577, + 0x2cc6, 1048577, + 0x2cc8, 1048577, + 0x2cca, 1048577, + 0x2ccc, 1048577, + 0x2cce, 1048577, + 0x2cd0, 1048577, + 0x2cd2, 1048577, + 0x2cd4, 1048577, + 0x2cd6, 1048577, + 0x2cd8, 1048577, + 0x2cda, 1048577, + 0x2cdc, 1048577, + 0x2cde, 1048577, + 0x2ce0, 1048577, + 0x2ce2, 1048577, + 0x2ceb, 1048577, + 0x2ced, 1048577, + 0x2cf2, 1048577, + 0xa640, 1048577, + 0xa642, 1048577, + 0xa644, 1048577, + 0xa646, 1048577, + 0xa648, 1048577, + 0xa64a, 1048577, + 0xa64c, 1048577, + 0xa64e, 1048577, + 0xa650, 1048577, + 0xa652, 1048577, + 0xa654, 1048577, + 0xa656, 1048577, + 0xa658, 1048577, + 0xa65a, 1048577, + 0xa65c, 1048577, + 0xa65e, 1048577, + 0xa660, 1048577, + 0xa662, 1048577, + 0xa664, 1048577, + 0xa666, 1048577, + 0xa668, 1048577, + 0xa66a, 1048577, + 0xa66c, 1048577, + 0xa680, 1048577, + 0xa682, 1048577, + 0xa684, 1048577, + 0xa686, 1048577, + 0xa688, 1048577, + 0xa68a, 1048577, + 0xa68c, 1048577, + 0xa68e, 1048577, + 0xa690, 1048577, + 0xa692, 1048577, + 0xa694, 1048577, + 0xa696, 1048577, + 0xa722, 1048577, + 0xa724, 1048577, + 0xa726, 1048577, + 0xa728, 1048577, + 0xa72a, 1048577, + 0xa72c, 1048577, + 0xa72e, 1048577, + 0xa732, 1048577, + 0xa734, 1048577, + 0xa736, 1048577, + 0xa738, 1048577, + 0xa73a, 1048577, + 0xa73c, 1048577, + 0xa73e, 1048577, + 0xa740, 1048577, + 0xa742, 1048577, + 0xa744, 1048577, + 0xa746, 1048577, + 0xa748, 1048577, + 0xa74a, 1048577, + 0xa74c, 1048577, + 0xa74e, 1048577, + 0xa750, 1048577, + 0xa752, 1048577, + 0xa754, 1048577, + 0xa756, 1048577, + 0xa758, 1048577, + 0xa75a, 1048577, + 0xa75c, 1048577, + 0xa75e, 1048577, + 0xa760, 1048577, + 0xa762, 1048577, + 0xa764, 1048577, + 0xa766, 1048577, + 0xa768, 1048577, + 0xa76a, 1048577, + 0xa76c, 1048577, + 0xa76e, 1048577, + 0xa779, 1048577, + 0xa77b, 1048577, + 0xa77d, 1013244, + 0xa77e, 1048577, + 0xa780, 1048577, + 0xa782, 1048577, + 0xa784, 1048577, + 0xa786, 1048577, + 0xa78b, 1048577, + 0xa78d, 1006296, + 0xa790, 1048577, + 0xa792, 1048577, + 0xa7a0, 1048577, + 0xa7a2, 1048577, + 0xa7a4, 1048577, + 0xa7a6, 1048577, + 0xa7a8, 1048577, + 0xa7aa, 1006268, +}; + +/* }}} */ + +/* {{{ To title */ + +static uint32_t totitler[] = { + 0x0061, 0x007a, 1048544, + 0x00e0, 0x00f6, 1048544, + 0x00f8, 0x00fe, 1048544, + 0x023f, 0x0240, 1059391, + 0x0256, 0x0257, 1048371, + 0x028a, 0x028b, 1048359, + 0x037b, 0x037d, 1048706, + 0x03ad, 0x03af, 1048539, + 0x03b1, 0x03c1, 1048544, + 0x03c3, 0x03cb, 1048544, + 0x03cd, 0x03ce, 1048513, + 0x0430, 0x044f, 1048544, + 0x0450, 0x045f, 1048496, + 0x0561, 0x0586, 1048528, + 0x1f00, 0x1f07, 1048584, + 0x1f10, 0x1f15, 1048584, + 0x1f20, 0x1f27, 1048584, + 0x1f30, 0x1f37, 1048584, + 0x1f40, 0x1f45, 1048584, + 0x1f60, 0x1f67, 1048584, + 0x1f70, 0x1f71, 1048650, + 0x1f72, 0x1f75, 1048662, + 0x1f76, 0x1f77, 1048676, + 0x1f78, 0x1f79, 1048704, + 0x1f7a, 0x1f7b, 1048688, + 0x1f7c, 0x1f7d, 1048702, + 0x1f80, 0x1f87, 1048584, + 0x1f90, 0x1f97, 1048584, + 0x1fa0, 0x1fa7, 1048584, + 0x1fb0, 0x1fb1, 1048584, + 0x1fd0, 0x1fd1, 1048584, + 0x1fe0, 0x1fe1, 1048584, + 0x2170, 0x217f, 1048560, + 0x24d0, 0x24e9, 1048550, + 0x2c30, 0x2c5e, 1048528, + 0x2d00, 0x2d25, 1041312, + 0xff41, 0xff5a, 1048544, + 0x10428, 0x1044f, 1048536, +}; + +static uint32_t totitles[] = { + 0x00b5, 1049319, + 0x00ff, 1048697, + 0x0101, 1048575, + 0x0103, 1048575, + 0x0105, 1048575, + 0x0107, 1048575, + 0x0109, 1048575, + 0x010b, 1048575, + 0x010d, 1048575, + 0x010f, 1048575, + 0x0111, 1048575, + 0x0113, 1048575, + 0x0115, 1048575, + 0x0117, 1048575, + 0x0119, 1048575, + 0x011b, 1048575, + 0x011d, 1048575, + 0x011f, 1048575, + 0x0121, 1048575, + 0x0123, 1048575, + 0x0125, 1048575, + 0x0127, 1048575, + 0x0129, 1048575, + 0x012b, 1048575, + 0x012d, 1048575, + 0x012f, 1048575, + 0x0131, 1048344, + 0x0133, 1048575, + 0x0135, 1048575, + 0x0137, 1048575, + 0x013a, 1048575, + 0x013c, 1048575, + 0x013e, 1048575, + 0x0140, 1048575, + 0x0142, 1048575, + 0x0144, 1048575, + 0x0146, 1048575, + 0x0148, 1048575, + 0x014b, 1048575, + 0x014d, 1048575, + 0x014f, 1048575, + 0x0151, 1048575, + 0x0153, 1048575, + 0x0155, 1048575, + 0x0157, 1048575, + 0x0159, 1048575, + 0x015b, 1048575, + 0x015d, 1048575, + 0x015f, 1048575, + 0x0161, 1048575, + 0x0163, 1048575, + 0x0165, 1048575, + 0x0167, 1048575, + 0x0169, 1048575, + 0x016b, 1048575, + 0x016d, 1048575, + 0x016f, 1048575, + 0x0171, 1048575, + 0x0173, 1048575, + 0x0175, 1048575, + 0x0177, 1048575, + 0x017a, 1048575, + 0x017c, 1048575, + 0x017e, 1048575, + 0x017f, 1048276, + 0x0180, 1048771, + 0x0183, 1048575, + 0x0185, 1048575, + 0x0188, 1048575, + 0x018c, 1048575, + 0x0192, 1048575, + 0x0195, 1048673, + 0x0199, 1048575, + 0x019a, 1048739, + 0x019e, 1048706, + 0x01a1, 1048575, + 0x01a3, 1048575, + 0x01a5, 1048575, + 0x01a8, 1048575, + 0x01ad, 1048575, + 0x01b0, 1048575, + 0x01b4, 1048575, + 0x01b6, 1048575, + 0x01b9, 1048575, + 0x01bd, 1048575, + 0x01bf, 1048632, + 0x01c4, 1048577, + 0x01c6, 1048575, + 0x01c7, 1048577, + 0x01c9, 1048575, + 0x01ca, 1048577, + 0x01cc, 1048575, + 0x01ce, 1048575, + 0x01d0, 1048575, + 0x01d2, 1048575, + 0x01d4, 1048575, + 0x01d6, 1048575, + 0x01d8, 1048575, + 0x01da, 1048575, + 0x01dc, 1048575, + 0x01dd, 1048497, + 0x01df, 1048575, + 0x01e1, 1048575, + 0x01e3, 1048575, + 0x01e5, 1048575, + 0x01e7, 1048575, + 0x01e9, 1048575, + 0x01eb, 1048575, + 0x01ed, 1048575, + 0x01ef, 1048575, + 0x01f1, 1048577, + 0x01f3, 1048575, + 0x01f5, 1048575, + 0x01f9, 1048575, + 0x01fb, 1048575, + 0x01fd, 1048575, + 0x01ff, 1048575, + 0x0201, 1048575, + 0x0203, 1048575, + 0x0205, 1048575, + 0x0207, 1048575, + 0x0209, 1048575, + 0x020b, 1048575, + 0x020d, 1048575, + 0x020f, 1048575, + 0x0211, 1048575, + 0x0213, 1048575, + 0x0215, 1048575, + 0x0217, 1048575, + 0x0219, 1048575, + 0x021b, 1048575, + 0x021d, 1048575, + 0x021f, 1048575, + 0x0223, 1048575, + 0x0225, 1048575, + 0x0227, 1048575, + 0x0229, 1048575, + 0x022b, 1048575, + 0x022d, 1048575, + 0x022f, 1048575, + 0x0231, 1048575, + 0x0233, 1048575, + 0x023c, 1048575, + 0x0242, 1048575, + 0x0247, 1048575, + 0x0249, 1048575, + 0x024b, 1048575, + 0x024d, 1048575, + 0x024f, 1048575, + 0x0250, 1059359, + 0x0251, 1059356, + 0x0252, 1059358, + 0x0253, 1048366, + 0x0254, 1048370, + 0x0259, 1048374, + 0x025b, 1048373, + 0x0260, 1048371, + 0x0263, 1048369, + 0x0265, 1090856, + 0x0266, 1090884, + 0x0268, 1048367, + 0x0269, 1048365, + 0x026b, 1059319, + 0x026f, 1048365, + 0x0271, 1059325, + 0x0272, 1048363, + 0x0275, 1048362, + 0x027d, 1059303, + 0x0280, 1048358, + 0x0283, 1048358, + 0x0288, 1048358, + 0x0289, 1048507, + 0x028c, 1048505, + 0x0292, 1048357, + 0x0345, 1048660, + 0x0371, 1048575, + 0x0373, 1048575, + 0x0377, 1048575, + 0x03ac, 1048538, + 0x03c2, 1048545, + 0x03cc, 1048512, + 0x03d0, 1048514, + 0x03d1, 1048519, + 0x03d5, 1048529, + 0x03d6, 1048522, + 0x03d7, 1048568, + 0x03d9, 1048575, + 0x03db, 1048575, + 0x03dd, 1048575, + 0x03df, 1048575, + 0x03e1, 1048575, + 0x03e3, 1048575, + 0x03e5, 1048575, + 0x03e7, 1048575, + 0x03e9, 1048575, + 0x03eb, 1048575, + 0x03ed, 1048575, + 0x03ef, 1048575, + 0x03f0, 1048490, + 0x03f1, 1048496, + 0x03f2, 1048583, + 0x03f5, 1048480, + 0x03f8, 1048575, + 0x03fb, 1048575, + 0x0461, 1048575, + 0x0463, 1048575, + 0x0465, 1048575, + 0x0467, 1048575, + 0x0469, 1048575, + 0x046b, 1048575, + 0x046d, 1048575, + 0x046f, 1048575, + 0x0471, 1048575, + 0x0473, 1048575, + 0x0475, 1048575, + 0x0477, 1048575, + 0x0479, 1048575, + 0x047b, 1048575, + 0x047d, 1048575, + 0x047f, 1048575, + 0x0481, 1048575, + 0x048b, 1048575, + 0x048d, 1048575, + 0x048f, 1048575, + 0x0491, 1048575, + 0x0493, 1048575, + 0x0495, 1048575, + 0x0497, 1048575, + 0x0499, 1048575, + 0x049b, 1048575, + 0x049d, 1048575, + 0x049f, 1048575, + 0x04a1, 1048575, + 0x04a3, 1048575, + 0x04a5, 1048575, + 0x04a7, 1048575, + 0x04a9, 1048575, + 0x04ab, 1048575, + 0x04ad, 1048575, + 0x04af, 1048575, + 0x04b1, 1048575, + 0x04b3, 1048575, + 0x04b5, 1048575, + 0x04b7, 1048575, + 0x04b9, 1048575, + 0x04bb, 1048575, + 0x04bd, 1048575, + 0x04bf, 1048575, + 0x04c2, 1048575, + 0x04c4, 1048575, + 0x04c6, 1048575, + 0x04c8, 1048575, + 0x04ca, 1048575, + 0x04cc, 1048575, + 0x04ce, 1048575, + 0x04cf, 1048561, + 0x04d1, 1048575, + 0x04d3, 1048575, + 0x04d5, 1048575, + 0x04d7, 1048575, + 0x04d9, 1048575, + 0x04db, 1048575, + 0x04dd, 1048575, + 0x04df, 1048575, + 0x04e1, 1048575, + 0x04e3, 1048575, + 0x04e5, 1048575, + 0x04e7, 1048575, + 0x04e9, 1048575, + 0x04eb, 1048575, + 0x04ed, 1048575, + 0x04ef, 1048575, + 0x04f1, 1048575, + 0x04f3, 1048575, + 0x04f5, 1048575, + 0x04f7, 1048575, + 0x04f9, 1048575, + 0x04fb, 1048575, + 0x04fd, 1048575, + 0x04ff, 1048575, + 0x0501, 1048575, + 0x0503, 1048575, + 0x0505, 1048575, + 0x0507, 1048575, + 0x0509, 1048575, + 0x050b, 1048575, + 0x050d, 1048575, + 0x050f, 1048575, + 0x0511, 1048575, + 0x0513, 1048575, + 0x0515, 1048575, + 0x0517, 1048575, + 0x0519, 1048575, + 0x051b, 1048575, + 0x051d, 1048575, + 0x051f, 1048575, + 0x0521, 1048575, + 0x0523, 1048575, + 0x0525, 1048575, + 0x0527, 1048575, + 0x1d79, 1083908, + 0x1d7d, 1052390, + 0x1e01, 1048575, + 0x1e03, 1048575, + 0x1e05, 1048575, + 0x1e07, 1048575, + 0x1e09, 1048575, + 0x1e0b, 1048575, + 0x1e0d, 1048575, + 0x1e0f, 1048575, + 0x1e11, 1048575, + 0x1e13, 1048575, + 0x1e15, 1048575, + 0x1e17, 1048575, + 0x1e19, 1048575, + 0x1e1b, 1048575, + 0x1e1d, 1048575, + 0x1e1f, 1048575, + 0x1e21, 1048575, + 0x1e23, 1048575, + 0x1e25, 1048575, + 0x1e27, 1048575, + 0x1e29, 1048575, + 0x1e2b, 1048575, + 0x1e2d, 1048575, + 0x1e2f, 1048575, + 0x1e31, 1048575, + 0x1e33, 1048575, + 0x1e35, 1048575, + 0x1e37, 1048575, + 0x1e39, 1048575, + 0x1e3b, 1048575, + 0x1e3d, 1048575, + 0x1e3f, 1048575, + 0x1e41, 1048575, + 0x1e43, 1048575, + 0x1e45, 1048575, + 0x1e47, 1048575, + 0x1e49, 1048575, + 0x1e4b, 1048575, + 0x1e4d, 1048575, + 0x1e4f, 1048575, + 0x1e51, 1048575, + 0x1e53, 1048575, + 0x1e55, 1048575, + 0x1e57, 1048575, + 0x1e59, 1048575, + 0x1e5b, 1048575, + 0x1e5d, 1048575, + 0x1e5f, 1048575, + 0x1e61, 1048575, + 0x1e63, 1048575, + 0x1e65, 1048575, + 0x1e67, 1048575, + 0x1e69, 1048575, + 0x1e6b, 1048575, + 0x1e6d, 1048575, + 0x1e6f, 1048575, + 0x1e71, 1048575, + 0x1e73, 1048575, + 0x1e75, 1048575, + 0x1e77, 1048575, + 0x1e79, 1048575, + 0x1e7b, 1048575, + 0x1e7d, 1048575, + 0x1e7f, 1048575, + 0x1e81, 1048575, + 0x1e83, 1048575, + 0x1e85, 1048575, + 0x1e87, 1048575, + 0x1e89, 1048575, + 0x1e8b, 1048575, + 0x1e8d, 1048575, + 0x1e8f, 1048575, + 0x1e91, 1048575, + 0x1e93, 1048575, + 0x1e95, 1048575, + 0x1e9b, 1048517, + 0x1ea1, 1048575, + 0x1ea3, 1048575, + 0x1ea5, 1048575, + 0x1ea7, 1048575, + 0x1ea9, 1048575, + 0x1eab, 1048575, + 0x1ead, 1048575, + 0x1eaf, 1048575, + 0x1eb1, 1048575, + 0x1eb3, 1048575, + 0x1eb5, 1048575, + 0x1eb7, 1048575, + 0x1eb9, 1048575, + 0x1ebb, 1048575, + 0x1ebd, 1048575, + 0x1ebf, 1048575, + 0x1ec1, 1048575, + 0x1ec3, 1048575, + 0x1ec5, 1048575, + 0x1ec7, 1048575, + 0x1ec9, 1048575, + 0x1ecb, 1048575, + 0x1ecd, 1048575, + 0x1ecf, 1048575, + 0x1ed1, 1048575, + 0x1ed3, 1048575, + 0x1ed5, 1048575, + 0x1ed7, 1048575, + 0x1ed9, 1048575, + 0x1edb, 1048575, + 0x1edd, 1048575, + 0x1edf, 1048575, + 0x1ee1, 1048575, + 0x1ee3, 1048575, + 0x1ee5, 1048575, + 0x1ee7, 1048575, + 0x1ee9, 1048575, + 0x1eeb, 1048575, + 0x1eed, 1048575, + 0x1eef, 1048575, + 0x1ef1, 1048575, + 0x1ef3, 1048575, + 0x1ef5, 1048575, + 0x1ef7, 1048575, + 0x1ef9, 1048575, + 0x1efb, 1048575, + 0x1efd, 1048575, + 0x1eff, 1048575, + 0x1f51, 1048584, + 0x1f53, 1048584, + 0x1f55, 1048584, + 0x1f57, 1048584, + 0x1fb3, 1048585, + 0x1fbe, 1041371, + 0x1fc3, 1048585, + 0x1fe5, 1048583, + 0x1ff3, 1048585, + 0x214e, 1048548, + 0x2184, 1048575, + 0x2c61, 1048575, + 0x2c65, 1037781, + 0x2c66, 1037784, + 0x2c68, 1048575, + 0x2c6a, 1048575, + 0x2c6c, 1048575, + 0x2c73, 1048575, + 0x2c76, 1048575, + 0x2c81, 1048575, + 0x2c83, 1048575, + 0x2c85, 1048575, + 0x2c87, 1048575, + 0x2c89, 1048575, + 0x2c8b, 1048575, + 0x2c8d, 1048575, + 0x2c8f, 1048575, + 0x2c91, 1048575, + 0x2c93, 1048575, + 0x2c95, 1048575, + 0x2c97, 1048575, + 0x2c99, 1048575, + 0x2c9b, 1048575, + 0x2c9d, 1048575, + 0x2c9f, 1048575, + 0x2ca1, 1048575, + 0x2ca3, 1048575, + 0x2ca5, 1048575, + 0x2ca7, 1048575, + 0x2ca9, 1048575, + 0x2cab, 1048575, + 0x2cad, 1048575, + 0x2caf, 1048575, + 0x2cb1, 1048575, + 0x2cb3, 1048575, + 0x2cb5, 1048575, + 0x2cb7, 1048575, + 0x2cb9, 1048575, + 0x2cbb, 1048575, + 0x2cbd, 1048575, + 0x2cbf, 1048575, + 0x2cc1, 1048575, + 0x2cc3, 1048575, + 0x2cc5, 1048575, + 0x2cc7, 1048575, + 0x2cc9, 1048575, + 0x2ccb, 1048575, + 0x2ccd, 1048575, + 0x2ccf, 1048575, + 0x2cd1, 1048575, + 0x2cd3, 1048575, + 0x2cd5, 1048575, + 0x2cd7, 1048575, + 0x2cd9, 1048575, + 0x2cdb, 1048575, + 0x2cdd, 1048575, + 0x2cdf, 1048575, + 0x2ce1, 1048575, + 0x2ce3, 1048575, + 0x2cec, 1048575, + 0x2cee, 1048575, + 0x2cf3, 1048575, + 0x2d27, 1041312, + 0x2d2d, 1041312, + 0xa641, 1048575, + 0xa643, 1048575, + 0xa645, 1048575, + 0xa647, 1048575, + 0xa649, 1048575, + 0xa64b, 1048575, + 0xa64d, 1048575, + 0xa64f, 1048575, + 0xa651, 1048575, + 0xa653, 1048575, + 0xa655, 1048575, + 0xa657, 1048575, + 0xa659, 1048575, + 0xa65b, 1048575, + 0xa65d, 1048575, + 0xa65f, 1048575, + 0xa661, 1048575, + 0xa663, 1048575, + 0xa665, 1048575, + 0xa667, 1048575, + 0xa669, 1048575, + 0xa66b, 1048575, + 0xa66d, 1048575, + 0xa681, 1048575, + 0xa683, 1048575, + 0xa685, 1048575, + 0xa687, 1048575, + 0xa689, 1048575, + 0xa68b, 1048575, + 0xa68d, 1048575, + 0xa68f, 1048575, + 0xa691, 1048575, + 0xa693, 1048575, + 0xa695, 1048575, + 0xa697, 1048575, + 0xa723, 1048575, + 0xa725, 1048575, + 0xa727, 1048575, + 0xa729, 1048575, + 0xa72b, 1048575, + 0xa72d, 1048575, + 0xa72f, 1048575, + 0xa733, 1048575, + 0xa735, 1048575, + 0xa737, 1048575, + 0xa739, 1048575, + 0xa73b, 1048575, + 0xa73d, 1048575, + 0xa73f, 1048575, + 0xa741, 1048575, + 0xa743, 1048575, + 0xa745, 1048575, + 0xa747, 1048575, + 0xa749, 1048575, + 0xa74b, 1048575, + 0xa74d, 1048575, + 0xa74f, 1048575, + 0xa751, 1048575, + 0xa753, 1048575, + 0xa755, 1048575, + 0xa757, 1048575, + 0xa759, 1048575, + 0xa75b, 1048575, + 0xa75d, 1048575, + 0xa75f, 1048575, + 0xa761, 1048575, + 0xa763, 1048575, + 0xa765, 1048575, + 0xa767, 1048575, + 0xa769, 1048575, + 0xa76b, 1048575, + 0xa76d, 1048575, + 0xa76f, 1048575, + 0xa77a, 1048575, + 0xa77c, 1048575, + 0xa77f, 1048575, + 0xa781, 1048575, + 0xa783, 1048575, + 0xa785, 1048575, + 0xa787, 1048575, + 0xa78c, 1048575, + 0xa791, 1048575, + 0xa793, 1048575, + 0xa7a1, 1048575, + 0xa7a3, 1048575, + 0xa7a5, 1048575, + 0xa7a7, 1048575, + 0xa7a9, 1048575, +}; + +/* }}} */ + +} + +void Utf8::encode(uint32_t c, char res[5]) +{ + switch (nbytesPoint(c)) { + case 1: + res[0] = c; + res[1] = '\0'; + break; + case 2: + res[0] = 0xC0 | ((c >> 6) & 0x1F); + res[1] = 0x80 | (c & 0x3F); + res[2] = '\0'; + break; + case 3: + res[0] = 0xE0 | ((c >> 12) & 0xF ); + res[1] = 0x80 | ((c >> 6) & 0x3F); + res[2] = 0x80 | (c & 0x3F); + res[3] = '\0'; + break; + case 4: + res[0] = 0xF0 | ((c >> 18) & 0x7 ); + res[1] = 0x80 | ((c >> 12) & 0x3F); + res[2] = 0x80 | ((c >> 6) & 0x3F); + res[3] = 0x80 | (c & 0x3F); + res[4] = '\0'; + break; + default: + break; + } +} + +void Utf8::decode(uint32_t &c, const char *res) +{ + switch (nbytesUtf8(res[0])) { + case 1: + c = res[0]; + break; + case 2: + c = res[0] & 0x1F; + c = (c << 6) | (res[1] & 0x3F); + break; + case 3: + c = res[0] & 0x1F; + c = (c << 6) | (res[1] & 0x3F); + c = (c << 6) | (res[2] & 0x3F); + break; + case 4: + c = res[0] & 0x1F; + c = (c << 6) | (res[1] & 0x3F); + c = (c << 6) | (res[2] & 0x3F); + c = (c << 6) | (res[3] & 0x3F); + break; + default: + break; + } +} + +int8_t Utf8::nbytesUtf8(uint8_t c) +{ + if (c <= 0x7F) + return 1; + if ((c & 0xE0) == 0xC0) + return 2; + if ((c & 0xF0) == 0xE0) + return 3; + if ((c & 0xF8) == 0xF0) + return 4; + + return -1; +} + +int8_t Utf8::nbytesPoint(uint32_t c) +{ + if (c <= 0x7F) + return 1; + if (c <= 0x7FF) + return 2; + if (c <= 0xFFFF) + return 3; + if (c <= 0x1FFFFF) + return 4; + + return -1; +} + +size_t Utf8::length(const std::string &str) +{ + size_t total = 0; + + for (size_t i = 0; i < str.size(); ) { + auto size = nbytesUtf8(str[i]); + + if (size < 0) + throw std::invalid_argument("invalid sequence"); + + total ++; + i += size; + } + + return total; +} + +std::string Utf8::toutf8(const std::u32string &array) +{ + std::string res; + + for (size_t i = 0; i < array.size(); ++i) { + char tmp[5]; + auto size = nbytesPoint(array[i]); + + if (size < 0) + throw std::invalid_argument("invalid sequence"); + + encode(array[i], tmp); + res.insert(res.length(), tmp); + } + + return res; +} + +std::u32string Utf8::toutf32(const std::string &str) +{ + std::u32string res; + + for (size_t i = 0; i < str.size(); ) { + uint32_t point; + auto size = nbytesUtf8(str[i]); + + if (size < 0) + throw std::invalid_argument("invalid sequence"); + + decode(point, str.data() + i); + res.push_back(point); + i += size; + } + + return res; +} + +bool Utf8::isspace(uint32_t c) +{ + uint32_t *p; + + p = rbsearch(c, isspacer, LEN(isspacer) / 2, 2); + if (p && c >= p[0] && c <= p[1]) + return true; + + return false; +} + +bool Utf8::isdigit(uint32_t c) +{ + uint32_t *p; + + p = rbsearch(c, isdigitr, LEN(isdigitr) / 2, 2); + if (p && c >= p[0] && c <= p[1]) + return true; + + return false; +} + +bool Utf8::isletter(uint32_t c) +{ + uint32_t *p; + + p = rbsearch(c, isalphar, LEN(isalphar) / 2, 2); + if (p && c >= p[0] && c <= p[1]) + return true; + + p = rbsearch(c, isalphas, LEN(isalphas), 1); + if (p && c == p[0]) + return true; + + return false; +} + +bool Utf8::isupper(uint32_t c) +{ + uint32_t *p; + + p = rbsearch(c, isupperr, LEN(isupperr) / 2, 2); + if (p && c >= p[0] && c <= p[1]) + return true; + + p = rbsearch(c, isuppers, LEN(isuppers), 1); + if (p && c == p[0]) + return true; + + return false; +} + +bool Utf8::islower(uint32_t c) +{ + uint32_t *p; + + p = rbsearch(c, islowerr, LEN(islowerr) / 2, 2); + if (p && c >= p[0] && c <= p[1]) + return true; + + p = rbsearch(c, islowers, LEN(islowers), 1); + if (p && c == p[0]) + return true; + + return false; +} + +bool Utf8::istitle(uint32_t c) +{ + uint32_t *p; + + p = rbsearch(c, istitler, LEN(istitler) / 2, 2); + if (p && c >= p[0] && c <= p[1]) + return true; + + p = rbsearch(c, istitles, LEN(istitles), 1); + if(p && c == p[0]) + return true; + + return false; +} + +uint32_t Utf8::toupper(uint32_t c) +{ + uint32_t *p; + + p = rbsearch(c, toupperr, LEN(toupperr) / 3, 3); + if (p && c >= p[0] && c <= p[1]) + return c + p[2] - 1048576; + + p = rbsearch(c, touppers, LEN(touppers) / 2, 2); + if (p && c == p[0]) + return c + p[1] - 1048576; + + return c; +} + +uint32_t Utf8::tolower(uint32_t c) +{ + uint32_t *p; + + p = rbsearch(c, tolowerr, LEN(tolowerr) / 3, 3); + if (p && c >= p[0] && c <= p[1]) + return c + p[2] - 1048576; + + p = rbsearch(c, tolowers, LEN(tolowers) / 2, 2); + if (p && c == p[0]) + return c + p[1] - 1048576; + + return c; +} + +uint32_t Utf8::totitle(uint32_t c) +{ + uint32_t *p; + + p = rbsearch(c, totitler, LEN(totitler) / 3, 3); + if (p && c >= p[0] && c <= p[1]) + return c + p[2] - 1048576; + + p = rbsearch(c, totitles, LEN(totitles) / 2, 2); + if (p && c == p[0]) + return c + p[1] - 1048576; + + return c; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/C++/modules/Utf8/Utf8.h Sun Mar 08 14:26:33 2015 +0100 @@ -0,0 +1,212 @@ +/* + * Utf8.h -- UTF-8 to UTF-32 conversions + * + * Copyright (c) 2013, 2014 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _UTF8_H_ +#define _UTF8_H_ + +/** + * @file Utf8.h + * @brief UTF-8 to UTF-32 conversions + */ + +#include <cstdint> +#include <stdexcept> +#include <string> + +/** + * @class Utf8 + * @brief Conversion between UTF-8 and UTF-32 + */ +class Utf8 { +private: + static void encode(uint32_t point, char res[5]); + static void decode(uint32_t &c, const char *res); + +public: + /** + * Get the number of bytes for the first multi byte character from a + * utf-8 string. + * + * @param c the first multi byte character + * @return the number of bytes [1-4] or -1 on invalid + */ + static int8_t nbytesUtf8(uint8_t c); + + /** + * Get the number of bytes for the unicode point. + * + * @param point the unicode point + * @return the number of bytes [1-4] or -1 on invalid + */ + static int8_t nbytesPoint(uint32_t point); + + /** + * Get real number of character in a string. + * + * @param str the string + * @return the length + * @throw std::invalid_argument on invalid sequence + */ + static size_t length(const std::string &str); + + /** + * Convert a UTF-32 string to UTF-8 string. + * + * @param array the UTF-32 string + * @return the UTF-8 string + * @throw std::invalid_argument on invalid sequence + */ + static std::string toutf8(const std::u32string &array); + + /** + * Convert a UTF-8 string to UTF-32 string. + * + * @param str the UTF-8 string + * @return the UTF-32 string + * @throw std::invalid_argument on invalid sequence + */ + static std::u32string toutf32(const std::string &str); + + /** + * Check if the unicode character is space. + * + * @param c the character + * @return true if space + */ + static bool isspace(uint32_t c); + + /** + * Check if the unicode character is digit. + * + * @param c the character + * @return true if digit + */ + static bool isdigit(uint32_t c); + + /** + * Check if the unicode character is letter. + * + * @param c the character + * @return true if letter + */ + static bool isletter(uint32_t c); + + /** + * Check if the unicode character is upper case. + * + * @param c the character + * @return true if upper case + */ + static bool isupper(uint32_t c); + + /** + * Check if the unicode character is lower case. + * + * @param c the character + * @return true if lower case + */ + static bool islower(uint32_t c); + + /** + * Check if the unicode character is title case. + * + * @param c the character + * @return true if title case + */ + static bool istitle(uint32_t c); + + /** + * Convert to upper case. + * + * @param c the character + * @return the upper case character + */ + static uint32_t toupper(uint32_t c); + + /** + * Convert to lower case. + * + * @param c the character + * @return the lower case character + */ + static uint32_t tolower(uint32_t c); + + /** + * Convert to title case. + * + * @param c the character + * @return the title case character + */ + static uint32_t totitle(uint32_t c); + + /** + * Convert the UTF-8 string to upper case. + * + * @param str the str + * @return the upper case string + */ + static inline std::string toupper(const std::string &str) + { + return toutf8(toupper(toutf32(str))); + } + + /** + * Convert the UTF-32 string to upper case. + * + * @param str the str + * @return the upper case string + */ + static inline std::u32string toupper(const std::u32string &str) + { + auto copy = str; + + for (size_t i = 0; i < str.size(); ++i) + copy[i] = toupper(str[i]); + + return copy; + } + + /** + * Convert the UTF-8 string to lower case. + * + * @param str the str + * @return the lower case string + */ + static inline std::string tolower(const std::string &str) + { + return toutf8(tolower(toutf32(str))); + } + + /** + * Convert the UTF-32 string to lower case. + * + * @param str the str + * @return the lower case string + */ + static inline std::u32string tolower(const std::u32string &str) + { + auto copy = str; + + for (size_t i = 0; i < str.size(); ++i) + copy[i] = tolower(str[i]); + + return copy; + } +}; + +#endif // !_UTF8_H_
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/C++/modules/Xdg/Xdg.cpp Sun Mar 08 14:26:33 2015 +0100 @@ -0,0 +1,125 @@ +/* + * Xdg.cpp -- XDG directory specifications + * + * Copyright (c) 2013, 2014 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <cstdlib> +#include <stdexcept> +#include <sstream> + +#include "Xdg.h" + +namespace { + +bool isabsolute(const std::string &path) +{ + return path.length() > 0 && path[0] == '/'; +} + +std::vector<std::string> split(const std::string &arg) +{ + std::stringstream iss(arg); + std::string item; + std::vector<std::string> elems; + + while (std::getline(iss, item, ':')) + if (isabsolute(item)) + elems.push_back(item); + + return elems; +} + +std::string envOrHome(const std::string &var, const std::string &repl) +{ + auto value = getenv(var.c_str()); + + if (value == nullptr || !isabsolute(value)) { + auto home = getenv("HOME"); + + if (home == nullptr) + throw std::runtime_error("could not get home directory"); + + return std::string(home) + "/" + repl; + } + + return value; +} + +std::vector<std::string> listOrDefaults(const std::string &var, const std::vector<std::string> &list) +{ + auto value = getenv(var.c_str()); + + if (!value) + return list; + + // No valid item at all? Use defaults + auto result = split(value); + + return (result.size() == 0) ? list : result; +} + +} // !namespace + +Xdg::Xdg() +{ + m_configHome = envOrHome("XDG_CONFIG_HOME", ".config"); + m_dataHome = envOrHome("XDG_DATA_HOME", ".local/share"); + m_cacheHome = envOrHome("XDG_CACHE_HOME", ".cache"); + + m_configDirs = listOrDefaults("XDG_CONFIG_DIRS", { "/etc/xdg" }); + m_dataDirs = listOrDefaults("XDG_DATA_DIRS", { "/usr/local/share", "/usr/share" }); + + /* + * Runtime directory is a special case and does not have a replacement, the + * application should manage this by itself. + */ + auto runtime = getenv("XDG_RUNTIME_DIR"); + if (runtime && isabsolute(runtime)) + m_runtimeDir = runtime; +} + +const std::string &Xdg::configHome() const noexcept +{ + return m_configHome; +} + +const std::string &Xdg::dataHome() const noexcept +{ + return m_dataHome; +} + +const std::string &Xdg::cacheHome() const noexcept +{ + return m_cacheHome; +} + +const std::string &Xdg::runtimeDir() const +{ + if (m_runtimeDir.size() == 0) + throw std::runtime_error("XDG_RUNTIME_DIR is not set"); + + return m_runtimeDir; +} + +const Xdg::List &Xdg::configDirs() const noexcept +{ + return m_configDirs; +} + +const Xdg::List &Xdg::dataDirs() const noexcept +{ + return m_dataDirs; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/C++/modules/Xdg/Xdg.h Sun Mar 08 14:26:33 2015 +0100 @@ -0,0 +1,110 @@ +/* + * Xdg.h -- XDG directory specifications + * + * Copyright (c) 2013, 2014 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _XDG_H_ +#define _XDG_H_ + +#include <vector> +#include <string> + +#if defined(_WIN32) +# if defined(BUILDING_DLL) +# define EXPORT __declspec(dllexport) +# else +# define EXPORT __declspec(dllimport) +# endif +#else +# define EXPORT +#endif + +/** + * @class Xdg + * @brief XDG specifications + * + * Read and get XDG directories. This file contains exports thingies so it can + * compiles successfully on Windows but its usage is discouraged. + */ +class EXPORT Xdg { +public: + using List = std::vector<std::string>; + +private: + std::string m_configHome; + std::string m_dataHome; + std::string m_cacheHome; + std::string m_runtimeDir; + List m_configDirs; + List m_dataDirs; + +public: + /** + * Open an xdg instance and load directories. + * + * @throw std::runtime_error on failures + */ + Xdg(); + + /** + * Get the config directory. ${XDG_CONFIG_HOME} or ${HOME}/.config + * + * @return the config directory + */ + const std::string &configHome() const noexcept; + + /** + * Get the data directory. ${XDG_DATA_HOME} or ${HOME}/.local/share + * + * @return the data directory + */ + const std::string &dataHome() const noexcept; + + /** + * Get the cache directory. ${XDG_CACHE_HOME} or ${HOME}/.cache + * + * @return the cache directory + */ + const std::string &cacheHome() const noexcept; + + /** + * Get the runtime directory. ${XDG_RUNTIME_DIR} must be set, + * if not, it throws an exception. + * + * The XDG standard says that application should handle XDG_RUNTIME_DIR by + * themselves. + * + * @return the runtime directory + * @throw std::runtime_error on error + */ + const std::string &runtimeDir() const; + + /** + * Get the standard config directories. ${XDG_CONFIG_DIRS} or { "/etc/xdg" } + * + * @return the list of config directories + */ + const List &configDirs() const noexcept; + + /** + * Get the data directories. ${XDG_DATA_DIRS} or { "/usr/local/share", "/usr/share" } + * + * @return the list of data directories + */ + const List &dataDirs() const noexcept; +}; + +#endif // !_XDG_H_
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/C++/modules/Zip/ZipArchive.cpp Sun Mar 08 14:26:33 2015 +0100 @@ -0,0 +1,295 @@ +/* + * ZipArchive.cpp -- wrapper around libzip + * + * Copyright (c) 2013, 2014 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <cerrno> +#include <cstdlib> +#include <cstring> +#include <stdexcept> + +#include "ZipArchive.h" + +namespace source { + +/* -------------------------------------------------------- + * Buffer (zip_source_buffer) + * -------------------------------------------------------- */ + +Buffer::Buffer(std::string data) + : m_data(std::move(data)) +{ +} + +struct zip_source *Buffer::source(struct zip *archive) const +{ + auto size = m_data.size(); + auto data = static_cast<char *>(std::malloc(size)); + + if (data == nullptr) + throw std::runtime_error(std::strerror(errno)); + + std::memcpy(data, m_data.data(), size); + + auto src = zip_source_buffer(archive, data, size, 1); + + if (src == nullptr) { + std::free(data); + throw std::runtime_error(zip_strerror(archive)); + } + + return src; +} + +/* -------------------------------------------------------- + * File (zip_source_file) + * -------------------------------------------------------- */ + +File::File(std::string path, ZipUint64 start, ZipInt64 length) + : m_path(std::move(path)) + , m_start(start) + , m_length(length) +{ +} + +struct zip_source *File::source(struct zip *archive) const +{ + auto src = zip_source_file(archive, m_path.c_str(), m_start, m_length); + + if (src == nullptr) + throw std::runtime_error(zip_strerror(archive)); + + return src; +} + +} // !source + +/* -------------------------------------------------------- + * ZipArchive + * ------------------------------------------------------- */ + +ZipArchive::ZipArchive(const std::string &path, ZipFlags flags) + : m_handle(nullptr, nullptr) +{ + int error; + struct zip *archive = zip_open(path.c_str(), flags, &error); + + if (archive == nullptr) + { + char buf[128]{}; + + zip_error_to_str(buf, sizeof (buf), error, errno); + + throw std::runtime_error(buf); + } + + m_handle = { archive, zip_close }; +} + +void ZipArchive::setFileComment(ZipUint64 index, const std::string &text, ZipFlags flags) +{ + auto size = text.size(); + auto cstr = (size == 0) ? nullptr : text.c_str(); + + if (zip_file_set_comment(m_handle.get(), index, cstr, size, flags) < 0) + throw std::runtime_error(zip_strerror(m_handle.get())); +} + +std::string ZipArchive::getFileComment(ZipUint64 index, ZipFlags flags) const +{ + zip_uint32_t length{}; + auto text = zip_file_get_comment(m_handle.get(), index, &length, flags); + + if (text == nullptr) + throw std::runtime_error(zip_strerror(m_handle.get())); + + return { text, length }; +} + +void ZipArchive::setComment(const std::string &comment) +{ + if (zip_set_archive_comment(m_handle.get(), comment.c_str(), comment.size()) < 0) + throw std::runtime_error(zip_strerror(m_handle.get())); +} + +std::string ZipArchive::getComment(ZipFlags flags) const +{ + int length{}; + auto text = zip_get_archive_comment(m_handle.get(), &length, flags); + + if (text == nullptr) + throw std::runtime_error(zip_strerror(m_handle.get())); + + return { text, static_cast<size_t>(length) }; +} + +ZipInt64 ZipArchive::find(const std::string &name, ZipFlags flags) +{ + auto index = zip_name_locate(m_handle.get(), name.c_str(), flags); + + if (index < 0) + throw std::runtime_error(zip_strerror(m_handle.get())); + + return index; +} + +ZipStat ZipArchive::stat(const std::string &name, ZipFlags flags) const +{ + ZipStat st; + + if (zip_stat(m_handle.get(), name.c_str(), flags, &st) < 0) + throw std::runtime_error(zip_strerror(m_handle.get())); + + return st; +} + +ZipStat ZipArchive::stat(ZipUint64 index, ZipFlags flags) const +{ + ZipStat st; + + if (zip_stat_index(m_handle.get(), index, flags, &st) < 0) + throw std::runtime_error(zip_strerror(m_handle.get())); + + return st; +} + +ZipInt64 ZipArchive::add(const ZipSource &source, const std::string &name, ZipFlags flags) +{ + auto src = source.source(m_handle.get()); + auto ret = zip_file_add(m_handle.get(), name.c_str(), src, flags); + + if (ret < 0) { + zip_source_free(src); + throw std::runtime_error(zip_strerror(m_handle.get())); + } + + return ret; +} + +ZipInt64 ZipArchive::addDirectory(const std::string &directory, ZipFlags flags) +{ + auto ret = zip_dir_add(m_handle.get(), directory.c_str(), flags); + + if (ret < 0) + throw std::runtime_error(zip_strerror(m_handle.get())); + + return ret; +} + +void ZipArchive::replace(const ZipSource &source, ZipUint64 index, ZipFlags flags) +{ + auto src = source.source(m_handle.get()); + + if (zip_file_replace(m_handle.get(), index, src, flags) < 0) { + zip_source_free(src); + throw std::runtime_error(zip_strerror(m_handle.get())); + } +} + +ZipFile ZipArchive::open(const std::string &name, ZipFlags flags, const std::string &password) +{ + struct zip_file *file; + + if (password.size() > 0) + file = zip_fopen_encrypted(m_handle.get(), name.c_str(), flags, password.c_str()); + else + file = zip_fopen(m_handle.get(), name.c_str(), flags); + + if (file == nullptr) + throw std::runtime_error(zip_strerror(m_handle.get())); + + return file; +} + +ZipFile ZipArchive::open(ZipUint64 index, ZipFlags flags, const std::string &password) +{ + struct zip_file *file; + + if (password.size() > 0) + file = zip_fopen_index_encrypted(m_handle.get(), index, flags, password.c_str()); + else + file = zip_fopen_index(m_handle.get(), index, flags); + + if (file == nullptr) + throw std::runtime_error(zip_strerror(m_handle.get())); + + return file; +} + +void ZipArchive::rename(ZipUint64 index, const std::string &name, ZipFlags flags) +{ + if (zip_file_rename(m_handle.get(), index, name.c_str(), flags) < 0) + throw std::runtime_error(zip_strerror(m_handle.get())); +} + +void ZipArchive::setFileCompression(ZipUint64 index, ZipInt32 comp, ZipUint32 flags) +{ + if (zip_set_file_compression(m_handle.get(), index, comp, flags) < 0) + throw std::runtime_error(zip_strerror(m_handle.get())); +} + +void ZipArchive::remove(ZipUint64 index) +{ + if (zip_delete(m_handle.get(), index) < 0) + throw std::runtime_error(zip_strerror(m_handle.get())); +} + +ZipInt64 ZipArchive::numEntries(ZipFlags flags) const noexcept +{ + return zip_get_num_entries(m_handle.get(), flags); +} + +void ZipArchive::unchange(ZipUint64 index) +{ + if (zip_unchange(m_handle.get(), index) < 0) + throw std::runtime_error(zip_strerror(m_handle.get())); +} + +void ZipArchive::unchangeAll() +{ + if (zip_unchange_all(m_handle.get()) < 0) + throw std::runtime_error(zip_strerror(m_handle.get())); +} + +void ZipArchive::unchangeArchive() +{ + if (zip_unchange_archive(m_handle.get()) < 0) + throw std::runtime_error(zip_strerror(m_handle.get())); +} + +void ZipArchive::setDefaultPassword(const std::string &password) +{ + auto cstr = (password.size() > 0) ? password.c_str() : nullptr; + + if (zip_set_default_password(m_handle.get(), cstr) < 0) + throw std::runtime_error(zip_strerror(m_handle.get())); +} + +void ZipArchive::setFlag(ZipFlags flags, int value) +{ + if (zip_set_archive_flag(m_handle.get(), flags, value) < 0) + throw std::runtime_error(zip_strerror(m_handle.get())); +} + +int ZipArchive::getFlag(ZipFlags which, ZipFlags flags) const +{ + auto ret = zip_get_archive_flag(m_handle.get(), which, flags); + + if (ret < 0) + throw std::runtime_error(zip_strerror(m_handle.get())); + + return ret; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/C++/modules/Zip/ZipArchive.h Sun Mar 08 14:26:33 2015 +0100 @@ -0,0 +1,727 @@ +/* + * ZipArchive.h -- wrapper around libzip + * + * Copyright (c) 2013, 2014 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _ZIP_ARCHIVE_H_ +#define _ZIP_ARCHIVE_H_ + +#include <iterator> +#include <memory> +#include <string> + +#include <zip.h> + +using ZipStat = struct zip_stat; +using ZipSourceCommand = enum zip_source_cmd; +using ZipCallback = zip_source_callback; + +using ZipFlags = zip_flags_t; +using ZipInt8 = zip_int8_t; +using ZipUint8 = zip_uint8_t; +using ZipInt16 = zip_int16_t; +using ZipUint16 = zip_uint16_t; +using ZipInt32 = zip_int32_t; +using ZipUint32 = zip_uint32_t; +using ZipInt64 = zip_int64_t; +using ZipUint64 = zip_uint64_t; + +/** + * @class ZipSource + * @brief Source for adding file + */ +class ZipSource { +public: + /** + * Default constructor. + */ + ZipSource() = default; + + /** + * Virtual destructor. + */ + virtual ~ZipSource() = default; + + /** + * Create a zip_source structure. Must not be null, throw an exception + * instead. + * + * @return a zip_source ready to be used + * @throw std::runtime_error on errors + * @post must not return null + */ + virtual struct zip_source *source(struct zip *zip) const = 0; +}; + +/** + * @class ZipFile + * @brief File for reading + */ +class ZipFile { +private: + std::unique_ptr<struct zip_file, int (*)(struct zip_file *)> m_handle; + + ZipFile(const ZipFile &) = delete; + ZipFile &operator=(const ZipFile &) = delete; + +public: + /** + * Create a ZipFile with a zip_file structure. + * + * @param file the file ready to be used + */ + inline ZipFile(struct zip_file *file) + : m_handle(file, zip_fclose) + { + } + + /** + * Move constructor defaulted. + * + * @param other the other ZipFile + */ + ZipFile(ZipFile &&other) noexcept = default; + + /** + * Move operator defaulted. + * + * @param other the other ZipFile + * @return *this + */ + ZipFile &operator=(ZipFile &&) noexcept = default; + + /** + * Read some data. + * + * @param data the destination buffer + * @param length the length + * @return the number of bytes written or -1 on failure + */ + inline int read(void *data, ZipUint64 length) noexcept + { + return zip_fread(m_handle.get(), data, length); + } + + /** + * Read some data to a fixed size array. + * + * @param data the array + * @return the number of bytes written or -1 on failure + */ + template <size_t Size> + inline int read(char (&data)[Size]) noexcept + { + return read(data, Size); + } + + /** + * Optimized function for reading all characters with only one allocation. + * Ideal for combining with ZipArchive::stat. + * + * @param length the length of the file + * @return the whole string + * @see ZipArchive::stat + */ + std::string read(unsigned length) + { + std::string result; + + result.resize(length); + auto count = read(&result[0], length); + + if (count < 0) + return ""; + + result.resize(count); + + return result; + } +}; + +namespace source { + +/** + * @class Buffer + * @brief Create a source from a buffer + */ +class Buffer : public ZipSource { +private: + std::string m_data; + +public: + /** + * Buffer constructor. Moves the data. + * + * @param data the data + */ + Buffer(std::string data); + + /** + * @copydoc ZipSource::source + */ + struct zip_source *source(struct zip *archive) const override; +}; + +/** + * @class File + * @brief Create a source from a file on the disk + */ +class File : public ZipSource { +private: + std::string m_path; + ZipUint64 m_start; + ZipInt64 m_length; + +public: + /** + * File constructor. + * + * @param path the path to the file + * @param start the beginning in the file + * @param length the maximum length + */ + File(std::string path, ZipUint64 start = 0, ZipInt64 length = -1); + + /** + * @copydoc ZipSource::source + */ + struct zip_source *source(struct zip *archive) const override; +}; + +} // !source + +/** + * @class ZipStatPtr + * @brief Wrapper for ZipStat as pointer + */ +class ZipStatPtr { +private: + ZipStat &m_stat; + +public: + /** + * Constructor. + * + * @param stat the file stat + */ + inline ZipStatPtr(ZipStat stat) noexcept + : m_stat(stat) + { + } + + /** + * Get the reference. + * + * @return the reference + */ + inline ZipStat &operator*() const noexcept + { + return m_stat; + } + + /** + * Access the object. + * + * @return the pointer + */ + inline ZipStat *operator->() const noexcept + { + return &m_stat; + } +}; + +/** + * @class ZipArchive + * @brief Safe wrapper on the struct zip structure + */ +class ZipArchive { +private: + using Handle = std::unique_ptr<struct zip, int (*)(struct zip *)>; + + Handle m_handle; + + ZipArchive(const ZipArchive &) = delete; + ZipArchive &operator=(const ZipArchive &) = delete; + +public: + using value_type = ZipStat; + using reference = ZipStat; + using const_refernce = ZipStat; + using pointer = ZipStatPtr; + using size_type = unsigned; + + /** + * @class iterator + * @brief Iterator + */ + class iterator : public std::iterator<std::random_access_iterator_tag, ZipArchive> { + private: + ZipArchive &m_archive; + ZipUint64 m_index; + + public: + explicit inline iterator(ZipArchive &archive, ZipUint64 index = 0) noexcept + : m_archive(archive) + , m_index(index) + { + } + + inline ZipStat operator*() const + { + return m_archive.stat(m_index); + } + + inline ZipStatPtr operator->() const + { + return ZipStatPtr(m_archive.stat(m_index)); + } + + inline iterator &operator++() noexcept + { + ++ m_index; + + return *this; + } + + inline iterator operator++(int) noexcept + { + iterator save = *this; + + ++ m_index; + + return save; + } + + inline iterator &operator--() noexcept + { + -- m_index; + + return *this; + } + + inline iterator operator--(int) noexcept + { + iterator save = *this; + + -- m_index; + + return save; + } + + inline iterator operator+(int inc) const noexcept + { + return iterator(m_archive, m_index + inc); + } + + inline iterator operator-(int dec) const noexcept + { + return iterator(m_archive, m_index - dec); + } + + inline bool operator==(const iterator &other) const noexcept + { + return m_index == other.m_index; + } + + inline bool operator!=(const iterator &other) const noexcept + { + return m_index != other.m_index; + } + + inline ZipStat operator[](int index) const + { + return m_archive.stat(index); + } + }; + + /** + * @class const_iterator + * @brief Const iterator + */ + class const_iterator : public std::iterator<std::random_access_iterator_tag, ZipArchive> { + private: + const ZipArchive &m_archive; + ZipUint64 m_index; + + public: + explicit inline const_iterator(const ZipArchive &archive, ZipUint64 index = 0) noexcept + : m_archive(archive) + , m_index(index) + { + } + + inline ZipStat operator*() const + { + return m_archive.stat(m_index); + } + + inline ZipStatPtr operator->() const + { + return ZipStatPtr(m_archive.stat(m_index)); + } + + inline const_iterator &operator++() noexcept + { + ++ m_index; + + return *this; + } + + inline const_iterator operator++(int) noexcept + { + const_iterator save = *this; + + ++ m_index; + + return save; + } + + inline const_iterator &operator--() noexcept + { + -- m_index; + + return *this; + } + + inline const_iterator operator--(int) noexcept + { + const_iterator save = *this; + + -- m_index; + + return save; + } + + inline const_iterator operator+(int inc) const noexcept + { + return const_iterator(m_archive, m_index + inc); + } + + inline const_iterator operator-(int dec) const noexcept + { + return const_iterator(m_archive, m_index - dec); + } + + inline bool operator==(const const_iterator &other) const noexcept + { + return m_index == other.m_index; + } + + inline bool operator!=(const const_iterator &other) const noexcept + { + return m_index != other.m_index; + } + + inline ZipStat operator[](int index) const + { + return m_archive.stat(index); + } + }; + +public: + /** + * Open an archive on the disk. + * + * @param path the path + * @param flags the optional flags + * @throw std::runtime_error on errors + */ + ZipArchive(const std::string &path, ZipFlags flags = 0); + + /** + * Move constructor defaulted. + * + * @param other the other ZipArchive + */ + ZipArchive(ZipArchive &&other) noexcept = default; + + /** + * Move operator defaulted. + * + * @param other the other ZipArchive + * @return *this + */ + ZipArchive &operator=(ZipArchive &&other) noexcept = default; + + /** + * Get an iterator to the beginning. + * + * @return the iterator + */ + inline iterator begin() noexcept + { + return iterator(*this); + } + + /** + * Overloaded function. + * + * @return the iterator + */ + inline const_iterator begin() const noexcept + { + return const_iterator(*this); + } + + /** + * Overloaded function. + * + * @return the iterator + */ + inline const_iterator cbegin() const noexcept + { + return const_iterator(*this); + } + + /** + * Get an iterator to the end. + * + * @return the iterator + */ + inline iterator end() noexcept + { + return iterator(*this, numEntries()); + } + + /** + * Overloaded function. + * + * @return the iterator + */ + inline const_iterator end() const noexcept + { + return const_iterator(*this, numEntries()); + } + + /** + * Overloaded function. + * + * @return the iterator + */ + inline const_iterator cend() const noexcept + { + return const_iterator(*this, numEntries()); + } + + /** + * Set a comment on a file. + * + * @param index the file index in the archive + * @param text the text or empty to remove the comment + * @param flags the optional flags + * @throw std::runtime_error on errors + */ + void setFileComment(ZipUint64 index, const std::string &text = "", ZipFlags flags = 0); + + /** + * Get a comment from a file. + * + * @param index the file index in the archive + * @param flags the optional flags + * @return the comment + * @throw std::runtime_error on errors + */ + std::string getFileComment(ZipUint64 index, ZipFlags flags = 0) const; + + /** + * Set the archive comment. + * + * @param comment the comment + * @throw std::runtime_error on errors + */ + void setComment(const std::string &comment); + + /** + * Get the archive comment. + * + * @param flags the optional flags + * @return the comment + * @throw std::runtime_error on errors + */ + std::string getComment(ZipFlags flags = 0) const; + + /** + * Locate a file on the archive. + * + * @param name the name + * @param flags the optional flags + * @return the index + * @throw std::runtime_error on errors + */ + ZipInt64 find(const std::string &name, ZipFlags flags = 0); + + /** + * Get information about a file. + * + * @param name the name + * @param flags the optional flags + * @return the structure + * @throw std::runtime_error on errors + */ + ZipStat stat(const std::string &name, ZipFlags flags = 0) const; + + /** + * Get information about a file. Overloaded function. + * + * @param index the file index in the archive + * @param flags the optional flags + * @return the structure + * @throw std::runtime_error on errors + */ + ZipStat stat(ZipUint64 index, ZipFlags flags = 0) const; + + /** + * Add a file to the archive. + * + * @param source the source + * @param name the name entry in the archive + * @param flags the optional flags + * @return the new index in the archive + * @throw std::runtime_error on errors + * @see source::File + * @see source::Buffer + */ + ZipInt64 add(const ZipSource &source, const std::string &name, ZipFlags flags = 0); + + /** + * Add a directory to the archive. Not a directory from the disk. + * + * @param directory the directory name + * @param flags the optional flags + * @return the new index in the archive + * @throw std::runtime_error on errors + */ + ZipInt64 addDirectory(const std::string &directory, ZipFlags flags = 0); + + /** + * Replace an existing file in the archive. + * + * @param source the source + * @param index the file index in the archiev + * @param flags the optional flags + * @throw std::runtime_error on errors + */ + void replace(const ZipSource &source, ZipUint64 index, ZipFlags flags = 0); + + /** + * Open a file in the archive. + * + * @param name the name + * @param flags the optional flags + * @param password the optional password + * @return the opened file + * @throw std::runtime_error on errors + */ + ZipFile open(const std::string &name, ZipFlags flags = 0, const std::string &password = ""); + + /** + * Open a file in the archive. Overloaded function. + * + * @param index the file index in the archive + * @param flags the optional flags + * @param password the optional password + * @return the opened file + * @throw std::runtime_error on errors + */ + ZipFile open(ZipUint64 index, ZipFlags flags = 0, const std::string &password = ""); + + /** + * Rename an existing entry in the archive. + * + * @param index the file index in the archive + * @param name the new name + * @param flags the optional flags + * @throw std::runtime_error on errors + */ + void rename(ZipUint64 index, const std::string &name, ZipFlags flags = 0); + + /** + * Set file compression. + * + * @param index the file index in the archive + * @param comp the compression + * @param flags the optional flags + * @throw std::runtime_error on errors + */ + void setFileCompression(ZipUint64 index, ZipInt32 comp, ZipUint32 flags = 0); + + /** + * Delete a file from the archive. + * + * @param index the file index in the archive + * @throw std::runtime_error on errors + */ + void remove(ZipUint64 index); + + /** + * Get the number of entries in the archive. + * + * @param flags the optional flags + * @return the number of entries + */ + ZipInt64 numEntries(ZipFlags flags = 0) const noexcept; + + /** + * Revert changes on the file. + * + * @param index the index + * @throw std::runtime_error on errors + */ + void unchange(ZipUint64 index); + + /** + * Revert all changes. + * + * @throw std::runtime_error on errors + */ + void unchangeAll(); + + /** + * Revert changes to archive. + * + * @throw std::runtime_error on errors + */ + void unchangeArchive(); + + /** + * Set the defaut password. + * + * @param password the password or empty to unset it + * @throw std::runtime_error on errors + */ + void setDefaultPassword(const std::string &password = ""); + + /** + * Set an archive flag. + * + * @param flag the flag to set + * @param value the value + * @throw std::runtime_error on errors + */ + void setFlag(ZipFlags flag, int value); + + /** + * Get an archive flag. + * + * @param which which flag + * @param flags the optional flags + * @return the value + * @throw std::runtime_error on errors + */ + int getFlag(ZipFlags which, ZipFlags flags = 0) const; +}; + +#endif // !_ZIP_ARCHIVE_H_
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/C++/tests/Base64/main.cpp Sun Mar 08 14:26:33 2015 +0100 @@ -0,0 +1,184 @@ +/* + * main.cpp -- main test file for Base64 + * + * Copyright (c) 2013, 2014 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <gtest/gtest.h> + +#include <Base64.h> + +TEST(Lookup, lookup) +{ + ASSERT_EQ('A', Base64::lookup(0b000000)); + ASSERT_EQ('B', Base64::lookup(0b000001)); + ASSERT_EQ('C', Base64::lookup(0b000010)); + ASSERT_EQ('D', Base64::lookup(0b000011)); + ASSERT_EQ('E', Base64::lookup(0b000100)); + ASSERT_EQ('F', Base64::lookup(0b000101)); + ASSERT_EQ('G', Base64::lookup(0b000110)); + ASSERT_EQ('H', Base64::lookup(0b000111)); + ASSERT_EQ('I', Base64::lookup(0b001000)); + ASSERT_EQ('J', Base64::lookup(0b001001)); + ASSERT_EQ('K', Base64::lookup(0b001010)); + ASSERT_EQ('L', Base64::lookup(0b001011)); + ASSERT_EQ('M', Base64::lookup(0b001100)); + ASSERT_EQ('N', Base64::lookup(0b001101)); + ASSERT_EQ('O', Base64::lookup(0b001110)); + ASSERT_EQ('P', Base64::lookup(0b001111)); + ASSERT_EQ('Q', Base64::lookup(0b010000)); + ASSERT_EQ('R', Base64::lookup(0b010001)); + ASSERT_EQ('S', Base64::lookup(0b010010)); + ASSERT_EQ('T', Base64::lookup(0b010011)); + ASSERT_EQ('U', Base64::lookup(0b010100)); + ASSERT_EQ('V', Base64::lookup(0b010101)); + ASSERT_EQ('W', Base64::lookup(0b010110)); + ASSERT_EQ('X', Base64::lookup(0b010111)); + ASSERT_EQ('Y', Base64::lookup(0b011000)); + ASSERT_EQ('Z', Base64::lookup(0b011001)); + ASSERT_EQ('a', Base64::lookup(0b011010)); + ASSERT_EQ('b', Base64::lookup(0b011011)); + ASSERT_EQ('c', Base64::lookup(0b011100)); + ASSERT_EQ('d', Base64::lookup(0b011101)); + ASSERT_EQ('e', Base64::lookup(0b011110)); + ASSERT_EQ('f', Base64::lookup(0b011111)); + ASSERT_EQ('g', Base64::lookup(0b100000)); + ASSERT_EQ('h', Base64::lookup(0b100001)); + ASSERT_EQ('i', Base64::lookup(0b100010)); + ASSERT_EQ('j', Base64::lookup(0b100011)); + ASSERT_EQ('k', Base64::lookup(0b100100)); + ASSERT_EQ('l', Base64::lookup(0b100101)); + ASSERT_EQ('m', Base64::lookup(0b100110)); + ASSERT_EQ('n', Base64::lookup(0b100111)); + ASSERT_EQ('o', Base64::lookup(0b101000)); + ASSERT_EQ('p', Base64::lookup(0b101001)); + ASSERT_EQ('q', Base64::lookup(0b101010)); + ASSERT_EQ('r', Base64::lookup(0b101011)); + ASSERT_EQ('s', Base64::lookup(0b101100)); + ASSERT_EQ('t', Base64::lookup(0b101101)); + ASSERT_EQ('u', Base64::lookup(0b101110)); + ASSERT_EQ('v', Base64::lookup(0b101111)); + ASSERT_EQ('w', Base64::lookup(0b110000)); + ASSERT_EQ('x', Base64::lookup(0b110001)); + ASSERT_EQ('y', Base64::lookup(0b110010)); + ASSERT_EQ('z', Base64::lookup(0b110011)); + ASSERT_EQ('0', Base64::lookup(0b110100)); + ASSERT_EQ('1', Base64::lookup(0b110101)); + ASSERT_EQ('2', Base64::lookup(0b110110)); + ASSERT_EQ('3', Base64::lookup(0b110111)); + ASSERT_EQ('4', Base64::lookup(0b111000)); + ASSERT_EQ('5', Base64::lookup(0b111001)); + ASSERT_EQ('6', Base64::lookup(0b111010)); + ASSERT_EQ('7', Base64::lookup(0b111011)); + ASSERT_EQ('8', Base64::lookup(0b111100)); + ASSERT_EQ('9', Base64::lookup(0b111101)); + ASSERT_EQ('+', Base64::lookup(0b111110)); + ASSERT_EQ('/', Base64::lookup(0b111111)); +} + +TEST(Lookup, rlookup) +{ + ASSERT_EQ(0b000000, Base64::rlookup('A')); + ASSERT_EQ(0b000001, Base64::rlookup('B')); + ASSERT_EQ(0b000010, Base64::rlookup('C')); + ASSERT_EQ(0b000011, Base64::rlookup('D')); + ASSERT_EQ(0b000100, Base64::rlookup('E')); + ASSERT_EQ(0b000101, Base64::rlookup('F')); + ASSERT_EQ(0b000110, Base64::rlookup('G')); + ASSERT_EQ(0b000111, Base64::rlookup('H')); + ASSERT_EQ(0b001000, Base64::rlookup('I')); + ASSERT_EQ(0b001001, Base64::rlookup('J')); + ASSERT_EQ(0b001010, Base64::rlookup('K')); + ASSERT_EQ(0b001011, Base64::rlookup('L')); + ASSERT_EQ(0b001100, Base64::rlookup('M')); + ASSERT_EQ(0b001101, Base64::rlookup('N')); + ASSERT_EQ(0b001110, Base64::rlookup('O')); + ASSERT_EQ(0b001111, Base64::rlookup('P')); + ASSERT_EQ(0b010000, Base64::rlookup('Q')); + ASSERT_EQ(0b010001, Base64::rlookup('R')); + ASSERT_EQ(0b010010, Base64::rlookup('S')); + ASSERT_EQ(0b010011, Base64::rlookup('T')); + ASSERT_EQ(0b010100, Base64::rlookup('U')); + ASSERT_EQ(0b010101, Base64::rlookup('V')); + ASSERT_EQ(0b010110, Base64::rlookup('W')); + ASSERT_EQ(0b010111, Base64::rlookup('X')); + ASSERT_EQ(0b011000, Base64::rlookup('Y')); + ASSERT_EQ(0b011001, Base64::rlookup('Z')); + ASSERT_EQ(0b011010, Base64::rlookup('a')); + ASSERT_EQ(0b011011, Base64::rlookup('b')); + ASSERT_EQ(0b011100, Base64::rlookup('c')); + ASSERT_EQ(0b011101, Base64::rlookup('d')); + ASSERT_EQ(0b011110, Base64::rlookup('e')); + ASSERT_EQ(0b011111, Base64::rlookup('f')); + ASSERT_EQ(0b100000, Base64::rlookup('g')); + ASSERT_EQ(0b100001, Base64::rlookup('h')); + ASSERT_EQ(0b100010, Base64::rlookup('i')); + ASSERT_EQ(0b100011, Base64::rlookup('j')); + ASSERT_EQ(0b100100, Base64::rlookup('k')); + ASSERT_EQ(0b100101, Base64::rlookup('l')); + ASSERT_EQ(0b100110, Base64::rlookup('m')); + ASSERT_EQ(0b100111, Base64::rlookup('n')); + ASSERT_EQ(0b101000, Base64::rlookup('o')); + ASSERT_EQ(0b101001, Base64::rlookup('p')); + ASSERT_EQ(0b101010, Base64::rlookup('q')); + ASSERT_EQ(0b101011, Base64::rlookup('r')); + ASSERT_EQ(0b101100, Base64::rlookup('s')); + ASSERT_EQ(0b101101, Base64::rlookup('t')); + ASSERT_EQ(0b101110, Base64::rlookup('u')); + ASSERT_EQ(0b101111, Base64::rlookup('v')); + ASSERT_EQ(0b110000, Base64::rlookup('w')); + ASSERT_EQ(0b110001, Base64::rlookup('x')); + ASSERT_EQ(0b110010, Base64::rlookup('y')); + ASSERT_EQ(0b110011, Base64::rlookup('z')); + ASSERT_EQ(0b110100, Base64::rlookup('0')); + ASSERT_EQ(0b110101, Base64::rlookup('1')); + ASSERT_EQ(0b110110, Base64::rlookup('2')); + ASSERT_EQ(0b110111, Base64::rlookup('3')); + ASSERT_EQ(0b111000, Base64::rlookup('4')); + ASSERT_EQ(0b111001, Base64::rlookup('5')); + ASSERT_EQ(0b111010, Base64::rlookup('6')); + ASSERT_EQ(0b111011, Base64::rlookup('7')); + ASSERT_EQ(0b111100, Base64::rlookup('8')); + ASSERT_EQ(0b111101, Base64::rlookup('9')); + ASSERT_EQ(0b111110, Base64::rlookup('+')); + ASSERT_EQ(0b111111, Base64::rlookup('/')); +} + +TEST(Encode, basic) +{ + ASSERT_EQ("YQ==", Base64::encode("a")); + ASSERT_EQ("YWI=", Base64::encode("ab")); + ASSERT_EQ("YWJj", Base64::encode("abc")); + + ASSERT_EQ("aGVsbG8=", Base64::encode("hello")); + ASSERT_EQ("dGhpcyBpcyBhIGxvbmcgc2VudGVuY2U=", Base64::encode("this is a long sentence")); +} + +TEST(Decode, basic) +{ + ASSERT_EQ("a", Base64::decode("YQ==")); + ASSERT_EQ("ab", Base64::decode("YWI=")); + ASSERT_EQ("abc", Base64::decode("YWJj")); + + ASSERT_EQ("hello", Base64::decode("aGVsbG8=")); + ASSERT_EQ("this is a long sentence", Base64::decode("dGhpcyBpcyBhIGxvbmcgc2VudGVuY2U=")); +} + +int main(int argc, char **argv) +{ + testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/C++/tests/Directory/main.cpp Sun Mar 08 14:26:33 2015 +0100 @@ -0,0 +1,115 @@ +/* + * main.cpp -- test directory + * + * Copyright (c) 2013, 2014 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <gtest/gtest.h> + +#include <Directory.h> + +TEST(Filter, withDot) +{ + try { + auto flags = Directory::NotDotDot; + Directory directory(".", flags); + bool dot(false); + bool dotdot(false); + + for (const auto &entry : directory) { + if (entry.name == ".") + dot = true; + if (entry.name == "..") + dotdot = true; + } + + ASSERT_TRUE(dot); + ASSERT_FALSE(dotdot); + } catch (const std::runtime_error &error) { + FAIL() << "skipping test because: " << error.what(); + } +} + +TEST(Filter, withDotDot) +{ + try { + auto flags = Directory::NotDot; + Directory directory(".", flags); + bool dot(false); + bool dotdot(false); + + for (const auto &entry : directory) { + if (entry.name == ".") + dot = true; + if (entry.name == "..") + dotdot = true; + } + + ASSERT_FALSE(dot); + ASSERT_TRUE(dotdot); + } catch (const std::runtime_error &error) { + FAIL() << "skipping test because: " << error.what(); + } +} + +TEST(Filter, withBothDots) +{ + try { + Directory directory("."); + bool dot(false); + bool dotdot(false); + + for (const auto &entry : directory) { + if (entry.name == ".") + dot = true; + if (entry.name == "..") + dotdot = true; + } + + ASSERT_TRUE(dot); + ASSERT_TRUE(dotdot); + } catch (const std::runtime_error &error) { + FAIL() << "skipping test because: " << error.what(); + } +} + +TEST(Filter, withoutDots) +{ + try { + auto flags = Directory::NotDot | Directory::NotDotDot; + Directory directory(".", flags); + bool dot(false); + bool dotdot(false); + + for (const auto &entry : directory) { + if (entry.name == ".") + dot = true; + if (entry.name == "..") + dotdot = true; + } + + ASSERT_FALSE(dot); + ASSERT_FALSE(dotdot); + } catch (const std::runtime_error &error) { + FAIL() << "skipping test because: " << error.what(); + } +} + +int main(int argc, char **argv) +{ + testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/C++/tests/Dynlib/Plugin.cpp Sun Mar 08 14:26:33 2015 +0100 @@ -0,0 +1,12 @@ +#include <string> + +#include <DynLib.h> + +extern "C" { + +void DYNLIB_EXPORT initialize(std::string &result) +{ + result = "Hello World"; +} + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/C++/tests/Dynlib/main.cpp Sun Mar 08 14:26:33 2015 +0100 @@ -0,0 +1,68 @@ +/* + * TestDynLib.cpp -- test the dynamic library loader + * + * Copyright (c) 2013, 2014 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <iostream> + +#include <gtest/gtest.h> + +#include <Dynlib.h> + +/* + * NOTE: the EXTENSION is defined by CMake in the form of a string + * literal containing the appropriate extension for the given system. + */ + +using Initialize = void (*)(std::string &s); + +TEST(Basic, initialize) +{ + try { + Dynlib library("./dynlib-plugin" EXTENSION); + Initialize init = library.sym<Initialize>("initialize"); + + std::string expected("Hello World"); + std::string result; + + init(result); + + ASSERT_EQ(expected, result); + } catch (const std::runtime_error &error) { + FAIL() << error.what(); + } catch (const std::out_of_range &error) { + FAIL() << error.what(); + } +} + +TEST(Basic, absent) +{ + try { + Dynlib library("./dynlib-plugin" EXTENSION); + library.sym<Initialize>("initialize_typo"); + } catch (const std::out_of_range &error) { + return; + } + + FAIL() << "Expected a failure but succeed"; +} + +int main(int argc, char **argv) +{ + testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/C++/tests/Flags/main.cpp Sun Mar 08 14:26:33 2015 +0100 @@ -0,0 +1,298 @@ +/* + * main.cpp -- main test file for Flags + * + * Copyright (c) 2013, 2014 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <cstdint> + +#include <gtest/gtest.h> + +#include <Flags.h> + +enum Standard : uint8_t { + Fullscreen = (1 << 0), + Audio = (1 << 1) +}; + +enum class Strong : uint8_t { + NoLog = (1 << 0), + NoCheck = (1 << 1) +}; + +/* -------------------------------------------------------- + * Global operators on standard enum + * -------------------------------------------------------- */ + +TEST(OperatorsStandard, opand) +{ + Standard s(Fullscreen | Audio); + + ASSERT_EQ(Fullscreen, (s & Fullscreen)); + ASSERT_EQ(Audio, (s & Audio)); +} + +TEST(OperatorsStandard, opor) +{ + Standard s(Fullscreen); + + ASSERT_EQ(3, (s | Audio)); +} + +TEST(OperatorsStandard, opxor) +{ + Standard s(Fullscreen); + + ASSERT_EQ(3, (s ^ Audio)); +} + +TEST(OperatorsStandard, opnot) +{ + Standard s(Fullscreen); + + ASSERT_EQ(254, ~s); +} + +/* -------------------------------------------------------- + * Global operators on strong enum + * -------------------------------------------------------- */ + +TEST(OperatorsStrong, opand) +{ + Strong s(Strong::NoLog | Strong::NoCheck); + + ASSERT_EQ(Strong::NoLog, (s & Strong::NoLog)); + ASSERT_EQ(Strong::NoCheck, (s & Strong::NoCheck)); +} + +TEST(OperatorsStrong, opor) +{ + Strong s(Strong::NoLog); + + ASSERT_EQ(3, static_cast<int>((s | Strong::NoCheck))); +} + +TEST(OperatorsStrong, opxor) +{ + Strong s(Strong::NoLog); + + ASSERT_EQ(3, static_cast<int>((s ^ Strong::NoCheck))); +} + +TEST(OperatorsStrong, opnot) +{ + Strong s(Strong::NoLog); + + ASSERT_EQ(254, static_cast<int>(~s)); +} + +/* -------------------------------------------------------- + * Flags with standard enums + * -------------------------------------------------------- */ + +TEST(Standard, construct) +{ + Flags<Standard> s; + + ASSERT_FALSE(s); + ASSERT_TRUE(!s); + + Flags <Standard> s2(Fullscreen | Audio); + + ASSERT_TRUE(s2); + ASSERT_FALSE(!s2); + ASSERT_TRUE(s2 & Fullscreen); + ASSERT_TRUE(s2 & Audio); + ASSERT_TRUE((s2 & Fullscreen) == Fullscreen); + ASSERT_TRUE((s2 & Audio) == Audio); + ASSERT_TRUE(s2 == (Audio | Fullscreen)); +} + +TEST(Standard, addByOne) +{ + Flags<Standard> s; + + ASSERT_FALSE(s); + ASSERT_TRUE(!s); + ASSERT_TRUE(!(s & Fullscreen)); + ASSERT_TRUE(!(s & Audio)); + + s |= Fullscreen; + ASSERT_TRUE(s); + ASSERT_FALSE(!s); + ASSERT_TRUE(s & Fullscreen); + ASSERT_TRUE((s & Fullscreen) == Fullscreen); + ASSERT_FALSE(s & Audio); + ASSERT_FALSE((s & Audio) == Audio); + ASSERT_TRUE(s == Fullscreen); + + s |= Audio; + ASSERT_TRUE(s); + ASSERT_FALSE(!s); + ASSERT_TRUE(s & Fullscreen); + ASSERT_TRUE((s & Fullscreen) == Fullscreen); + ASSERT_TRUE(s & Audio); + ASSERT_TRUE((s & Audio) == Audio); + ASSERT_TRUE(s == (Fullscreen | Audio)); +} + +TEST(Standard, add) +{ + Flags<Standard> s; + + s |= Fullscreen | Audio; + ASSERT_TRUE(s & (Fullscreen | Audio)); + ASSERT_TRUE((s & (Fullscreen | Audio)) == (Fullscreen | Audio)); +} + +TEST(Standard, removeByOne) +{ + Flags<Standard> s(Fullscreen | Audio); + + s &= ~(Fullscreen); + ASSERT_TRUE(s); + ASSERT_FALSE(!s); + ASSERT_FALSE(s & Fullscreen); + ASSERT_FALSE((s & Fullscreen) == Fullscreen); + ASSERT_TRUE(s & Audio); + ASSERT_TRUE((s & Audio) == Audio); + + s &= ~(Audio); + ASSERT_FALSE(s); + ASSERT_TRUE(!s); +} + +TEST(Standard, remove) +{ + Flags<Standard> s(Fullscreen | Audio); + + s &= ~(Fullscreen | Audio); + ASSERT_FALSE(s); + ASSERT_TRUE(!s); +} + +TEST(Standard, xorAdd) +{ + Flags<Standard> s(Fullscreen | Audio); + + s ^= Audio; + ASSERT_TRUE(s & Fullscreen); + ASSERT_TRUE((s & Fullscreen) == Fullscreen); + ASSERT_FALSE(s & Audio); + ASSERT_FALSE((s & Audio) == Audio); +} + +/* -------------------------------------------------------- + * Flags with strong enums + * -------------------------------------------------------- */ + +TEST(Strong, construct) +{ + Flags<Strong> s; + + ASSERT_FALSE(s); + ASSERT_TRUE(!s); + + Flags <Strong> s2(Strong::NoLog | Strong::NoCheck); + + ASSERT_TRUE(s2); + ASSERT_FALSE(!s2); + ASSERT_TRUE(s2 & Strong::NoLog); + ASSERT_TRUE(s2 & Strong::NoCheck); + ASSERT_TRUE((s2 & Strong::NoLog) == Strong::NoLog); + ASSERT_TRUE((s2 & Strong::NoCheck) == Strong::NoCheck); + ASSERT_TRUE(s2 == (Strong::NoLog | Strong::NoCheck)); +} + +TEST(Strong, addByOne) +{ + Flags<Strong> s; + + ASSERT_FALSE(s); + ASSERT_TRUE(!s); + ASSERT_TRUE(!(s & Strong::NoLog)); + ASSERT_TRUE(!(s & Strong::NoCheck)); + + s |= Strong::NoLog; + ASSERT_TRUE(s); + ASSERT_FALSE(!s); + ASSERT_TRUE(s & Strong::NoLog); + ASSERT_TRUE((s & Strong::NoLog) == Strong::NoLog); + ASSERT_FALSE(s & Strong::NoCheck); + ASSERT_FALSE((s & Strong::NoCheck) == Strong::NoCheck); + ASSERT_TRUE(s == Strong::NoLog); + + s |= Strong::NoCheck; + ASSERT_TRUE(s); + ASSERT_FALSE(!s); + ASSERT_TRUE(s & Strong::NoLog); + ASSERT_TRUE((s & Strong::NoLog) == Strong::NoLog); + ASSERT_TRUE(s & Strong::NoCheck); + ASSERT_TRUE((s & Strong::NoCheck) == Strong::NoCheck); + ASSERT_TRUE(s == (Strong::NoLog | Strong::NoCheck)); +} + +TEST(Strong, add) +{ + Flags<Strong> s; + + s |= Strong::NoLog | Strong::NoCheck; + ASSERT_TRUE(s & (Strong::NoLog | Strong::NoCheck)); + ASSERT_TRUE((s & (Strong::NoLog | Strong::NoCheck)) == (Strong::NoLog | Strong::NoCheck)); +} + +TEST(Strong, removeByOne) +{ + Flags<Strong> s(Strong::NoLog | Strong::NoCheck); + + s &= ~(Strong::NoLog); + ASSERT_TRUE(s); + ASSERT_FALSE(!s); + ASSERT_FALSE(s & Strong::NoLog); + ASSERT_FALSE((s & Strong::NoLog) == Strong::NoLog); + ASSERT_TRUE(s & Strong::NoCheck); + ASSERT_TRUE((s & Strong::NoCheck) == Strong::NoCheck); + + s &= ~(Strong::NoCheck); + ASSERT_FALSE(s); + ASSERT_TRUE(!s); +} + +TEST(Strong, remove) +{ + Flags<Strong> s(Strong::NoLog | Strong::NoCheck); + + s &= ~(Strong::NoLog | Strong::NoCheck); + ASSERT_FALSE(s); + ASSERT_TRUE(!s); +} + +TEST(Strong, xorAdd) +{ + Flags<Strong> s(Strong::NoLog | Strong::NoCheck); + + s ^= Strong::NoCheck; + ASSERT_TRUE(s & Strong::NoLog); + ASSERT_TRUE((s & Strong::NoLog) == Strong::NoLog); + ASSERT_FALSE(s & Strong::NoCheck); + ASSERT_FALSE((s & Strong::NoCheck) == Strong::NoCheck); +} + +int main(int argc, char **argv) +{ + testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/C++/tests/Hash/main.cpp Sun Mar 08 14:26:33 2015 +0100 @@ -0,0 +1,64 @@ +/* + * main.cpp -- test the hash cryptographic functions + * + * Copyright (c) 2013, 2014 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <gtest/gtest.h> + +#include <Hash.h> + +/* + * We test the "Hello World" message in all cryptographic functions. + */ + +TEST(Hash, md5) +{ + std::string expected = "b10a8db164e0754105b7a99be72e3fe5"; + std::string output = Hash::md5("Hello World"); + + ASSERT_EQ(expected, output); +} + +TEST(Hash, sha1) +{ + std::string expected = "0a4d55a8d778e5022fab701977c5d840bbc486d0"; + std::string output = Hash::sha1("Hello World"); + + ASSERT_EQ(expected, output); +} + +TEST(Hash, sha256) +{ + std::string expected = "a591a6d40bf420404a011733cfb7b190d62c65bf0bcda32b57b277d9ad9f146e"; + std::string output = Hash::sha256("Hello World"); + + ASSERT_EQ(expected, output); +} + +TEST(Hash, sha512) +{ + std::string expected = "2c74fd17edafd80e8447b0d46741ee243b7eb74dd2149a0ab1b9246fb30382f27e853d8585719e0e67cbda0daa8f51671064615d645ae27acb15bfb1447f459b"; + std::string output = Hash::sha512("Hello World"); + + ASSERT_EQ(expected, output); +} + +int main(int argc, char **argv) +{ + testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/C++/tests/Ini/configs/compact.conf Sun Mar 08 14:26:33 2015 +0100 @@ -0,0 +1,1 @@ +[general]verbose=true foreground=false[server]host=google.fr
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/C++/tests/Ini/configs/error-badcomment.conf Sun Mar 08 14:26:33 2015 +0100 @@ -0,0 +1,2 @@ +[general] +verbose #hello = xyz
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/C++/tests/Ini/configs/error-badsection.conf Sun Mar 08 14:26:33 2015 +0100 @@ -0,0 +1,2 @@ +[[general] +verbose = false
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/C++/tests/Ini/configs/error-lineassigment.conf Sun Mar 08 14:26:33 2015 +0100 @@ -0,0 +1,4 @@ +[general] +host += +google.fr
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/C++/tests/Ini/configs/error-nosection.conf Sun Mar 08 14:26:33 2015 +0100 @@ -0,0 +1,1 @@ +option = value
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/C++/tests/Ini/configs/includes.conf Sun Mar 08 14:26:33 2015 +0100 @@ -0,0 +1,5 @@ +# With some includes +@include "simple.conf" # comments also work here + +[standard] +verbose = false
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/C++/tests/Ini/configs/multi.conf Sun Mar 08 14:26:33 2015 +0100 @@ -0,0 +1,7 @@ +[entity] +name = "Player" +version = 1.0 + +[entity] +name = "Subwinner" +version = 2.0 \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/C++/tests/Ini/configs/novalue.conf Sun Mar 08 14:26:33 2015 +0100 @@ -0,0 +1,6 @@ +[plugins] +histedit= +highlight= #empty +general = + +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/C++/tests/Ini/configs/simple.conf Sun Mar 08 14:26:33 2015 +0100 @@ -0,0 +1,4 @@ +[general] +option1=1 +option2 =2 +option3 = 3
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/C++/tests/Ini/configs/tokens.conf Sun Mar 08 14:26:33 2015 +0100 @@ -0,0 +1,3 @@ +[tokens] +bracket = "I have [brackets]" +at = "I have foo@at"
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/C++/tests/Ini/main.cpp Sun Mar 08 14:26:33 2015 +0100 @@ -0,0 +1,250 @@ +/* + * main.cpp -- main test file for Ini + * + * Copyright (c) 2013, 2014 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <iostream> + +#include <gtest/gtest.h> + +#include <Ini.h> + +class BasicTest : public testing::Test { +protected: + Ini m_ini; + +public: + BasicTest() + : m_ini("Ini/simple.conf") + { + } + +}; + +TEST_F(BasicTest, simple) +{ + ASSERT_EQ(1, static_cast<int>(m_ini.size())); +} + +TEST_F(BasicTest, iniOperators) +{ + try { + ASSERT_EQ(3, static_cast<int>(m_ini[0].size())); + ASSERT_EQ("general", m_ini[0].key()); + ASSERT_EQ("general", m_ini["general"].key()); + } catch (const std::exception &ex) { + FAIL() << ex.what(); + } +} + +TEST_F(BasicTest, iniSectionOperators) +{ + try { + // option1=1 (indexes) + ASSERT_EQ("option1", m_ini[0][0].key()); + ASSERT_EQ("1", m_ini[0][0].value()); + + // option1=1 (keys) + ASSERT_EQ("option1", m_ini["general"]["option1"].key()); + ASSERT_EQ("1", m_ini["general"]["option1"].value()); + + // option2 =2 (indexes) + ASSERT_EQ("option2", m_ini[0][1].key()); + ASSERT_EQ("2", m_ini[0][1].value()); + + // option2 =2 (keys) + ASSERT_EQ("option2", m_ini["general"]["option2"].key()); + ASSERT_EQ("2", m_ini["general"]["option2"].value()); + + // option3 = 3 (indexes) + ASSERT_EQ("option3", m_ini[0][2].key()); + ASSERT_EQ("3", m_ini[0][2].value()); + + // option3 = 3 (keys) + ASSERT_EQ("option3", m_ini["general"]["option3"].key()); + ASSERT_EQ("3", m_ini["general"]["option3"].value()); + } catch (const std::exception &ex) { + FAIL() << ex.what(); + } +} + +/* -------------------------------------------------------- + * Reserved tokens in words + * -------------------------------------------------------- */ + +TEST(Tokens, iniReserved) +{ + try { + Ini ini("Ini/tokens.conf"); + + ASSERT_EQ("I have [brackets]", ini["tokens"]["bracket"].value()); + ASSERT_EQ("I have foo@at", ini["tokens"]["at"].value()); + } catch (const std::exception &ex) { + FAIL() << ex.what(); + } +} + +/* -------------------------------------------------------- + * Multiple definition + * -------------------------------------------------------- */ + +class MultiTest : public testing::Test { +protected: + Ini m_ini; + +public: + MultiTest() + : m_ini("Ini/multi.conf") + { + } +}; + +TEST_F(MultiTest, defined) +{ + ASSERT_EQ(2, static_cast<int>(m_ini.size())); + ASSERT_EQ("name", m_ini[0]["name"].key()); + ASSERT_EQ("Player", m_ini[0]["name"].value()); + ASSERT_EQ("version", m_ini[0]["version"].key()); + ASSERT_EQ("1.0", m_ini[0]["version"].value()); + ASSERT_EQ("name", m_ini[1]["name"].key()); + ASSERT_EQ("Subwinner", m_ini[1]["name"].value()); + ASSERT_EQ("version", m_ini[1]["version"].key()); + ASSERT_EQ("2.0", m_ini[1]["version"].value()); +} + +/* -------------------------------------------------------- + * Option with no values + * -------------------------------------------------------- */ + +class NoValueTest : public testing::Test { +protected: + Ini m_ini; + +public: + NoValueTest() + : m_ini("Ini/novalue.conf") + { + } +}; + +TEST_F(NoValueTest, isDefined) +{ + ASSERT_EQ("plugins", m_ini[0].key()); + ASSERT_EQ("", m_ini["plugins"]["histedit"].value()); + ASSERT_EQ("", m_ini["plugins"]["highlight"].value()); + ASSERT_EQ("", m_ini["plugins"]["general"].value()); +} + +/* -------------------------------------------------------- + * Include tests + * -------------------------------------------------------- */ + +class IncludeTest : public testing::Test { +protected: + Ini m_ini; + +public: + IncludeTest() + : m_ini("Ini/includes.conf") + { + } +}; + +TEST_F(IncludeTest, all) +{ + ASSERT_EQ(2, static_cast<int>(m_ini.size())); + + // from include + ASSERT_EQ("1", m_ini[0][0].value()); + ASSERT_EQ("2", m_ini[0][1].value()); + ASSERT_EQ("3", m_ini[0][2].value()); + + // from standard + ASSERT_EQ("false", m_ini[1][0].value()); +} + +/* -------------------------------------------------------- + * Compact + * -------------------------------------------------------- */ + +TEST(Compact, test) +{ + try { + Ini ini("Ini/compact.conf"); + + ASSERT_EQ(2, static_cast<int>(ini.size())); + ASSERT_EQ("true", ini["general"]["verbose"].value()); + ASSERT_EQ("false", ini["general"]["foreground"].value()); + ASSERT_EQ("google.fr", ini["server"]["host"].value()); + } catch (const std::exception &ex) { + FAIL() << ex.what(); + } +} + +/* -------------------------------------------------------- + * Errors + * -------------------------------------------------------- */ + +TEST(Errors, nosection) +{ + // An option outside a section is not allowed + try { + Ini ini("Ini/error-nosection.conf"); + + FAIL() << "Failure expected, got success"; + } catch (const std::exception &ex) { + } +} + +TEST(Errors, lineassigment) +{ + // The = assignment must be on the same line as the option key + try { + Ini ini("Ini/error-lineassigment.conf"); + + FAIL() << "Failure expected, got success"; + } catch (const std::exception &ex) { + } +} + +TEST(Errors, badcomment) +{ + // Comment can't between option-key and = assigment + try { + Ini ini("Ini/error-badcomment.conf"); + + FAIL() << "Failure expected, got success"; + } catch (const std::exception &ex) { + } +} + +TEST(Errors, badsection) +{ + // Bad section naming + try { + Ini ini("Ini/error-badsection.conf"); + + FAIL() << "Failure expected, got success"; + } catch (const std::exception &ex) { + } +} + +int main(int argc, char **argv) +{ + testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/C++/tests/Json/data/array-all.json Sun Mar 08 14:26:33 2015 +0100 @@ -0,0 +1,9 @@ +[ + 123, + 9.2, + false, + true, + null, + {}, + [] +]
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/C++/tests/Json/data/array.json Sun Mar 08 14:26:33 2015 +0100 @@ -0,0 +1,1 @@ +[1, 2, 3]
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/C++/tests/Json/data/object-all.json Sun Mar 08 14:26:33 2015 +0100 @@ -0,0 +1,9 @@ +{ + "integer": 123, + "real": 9.2, + "false": false, + "true": true, + "null": null, + "object": {}, + "array": [] +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/C++/tests/Json/data/object.json Sun Mar 08 14:26:33 2015 +0100 @@ -0,0 +1,4 @@ +{ + "name": "simple", + "description": "basic JSON file" +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/C++/tests/Json/data/simple.json Sun Mar 08 14:26:33 2015 +0100 @@ -0,0 +1,2 @@ +{ +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/C++/tests/Json/main.cpp Sun Mar 08 14:26:33 2015 +0100 @@ -0,0 +1,877 @@ +/* + * main.cpp -- test the jansson wrapper + * + * Copyright (c) 2013, 2014 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <set> +#include <unordered_map> + +#include <gtest/gtest.h> + +#include "Json.h" + +/* -------------------------------------------------------- + * Miscellaneous + * -------------------------------------------------------- */ + +TEST(Misc, copy) +{ + JsonObject object; + + object.set("integer", 123); + object.set("true", true); + + JsonObject object2{object}; + + ASSERT_TRUE(object2.isObject()); + ASSERT_EQ(123, object2["integer"].toInteger()); + ASSERT_TRUE(object2["true"].isTrue()); +} + +TEST(Misc, copyAssign) +{ + JsonObject object; + + { + JsonObject tmp; + + tmp.set("integer", 123); + tmp.set("true", true); + + object = tmp; + } + + ASSERT_TRUE(object.isObject()); + ASSERT_EQ(123, object["integer"].toInteger()); + ASSERT_TRUE(object["true"].isTrue()); +} + +TEST(Misc, move) +{ + JsonObject object(123); + JsonObject object2(std::move(object)); + + ASSERT_TRUE(object.isNull()); + ASSERT_TRUE(object2.isInteger()); + ASSERT_EQ(123, object2.toInteger()); +} + +TEST(Misc, moveAssign) +{ + JsonObject object(123); + JsonObject object2; + + object2 = std::move(object); + + ASSERT_TRUE(object.isNull()); + ASSERT_TRUE(object2.isInteger()); + ASSERT_EQ(123, object2.toInteger()); +} + +/* -------------------------------------------------------- + * JsonValue constructors + * -------------------------------------------------------- */ + +TEST(Constructors, null) +{ + try { + JsonValue value; + + ASSERT_TRUE(value.isNull()); + } catch (const JsonError &ex) { + FAIL() << ex.what(); + } +} + +TEST(Constructors, boolean) +{ + try { + JsonValue value{true}; + + ASSERT_TRUE(value.isTrue()); + ASSERT_TRUE(value.isBoolean()); + ASSERT_FALSE(value.isFalse()); + } catch (const JsonError &ex) { + FAIL() << ex.what(); + } +} + +TEST(Constructors, integer) +{ + try { + JsonValue value{123}; + + ASSERT_TRUE(value.isInteger()); + ASSERT_TRUE(value.isNumber()); + ASSERT_FALSE(value.isReal()); + ASSERT_EQ(123, value.toInteger()); + } catch (const JsonError &ex) { + FAIL() << ex.what(); + } +} + +TEST(Constructors, real) +{ + try { + JsonValue value{9.2}; + + ASSERT_TRUE(value.isReal()); + ASSERT_TRUE(value.isNumber()); + ASSERT_FALSE(value.isInteger()); + ASSERT_EQ(9.2, value.toReal()); + } catch (const JsonError &ex) { + FAIL() << ex.what(); + } +} + +TEST(Constructors, string) +{ + try { + JsonValue value("hello"); + + ASSERT_TRUE(value.isString()); + ASSERT_EQ("hello", value.toString()); + } catch (const JsonError &ex) { + FAIL() << ex.what(); + } +} + +/* -------------------------------------------------------- + * Object + * -------------------------------------------------------- */ + +TEST(Object, set) +{ + try { + JsonObject object; + + object.set("integer", 123); + object.set("string", "hello"); + object.set("true", true); + + ASSERT_EQ(123, object["integer"].toInteger()); + ASSERT_EQ("hello", object["string"].toString()); + ASSERT_TRUE(object["true"].isTrue()); + } catch (const JsonError &ex) { + FAIL() << ex.what(); + } +} + +TEST(Object, clear) +{ + try { + JsonObject object; + + object.set("integer", 123); + object.set("string", "hello"); + object.set("true", true); + + object.clear(); + + ASSERT_EQ(0, static_cast<int>(object.size())); + ASSERT_FALSE(object.contains("integer")); + ASSERT_FALSE(object.contains("string")); + ASSERT_FALSE(object.contains("true")); + } catch (const JsonError &ex) { + FAIL() << ex.what(); + } +} + +TEST(Object, erase) +{ + try { + JsonObject object; + + object.set("integer", 123); + object.set("string", "hello"); + object.set("true", true); + + object.erase("integer"); + + ASSERT_EQ(2, static_cast<int>(object.size())); + ASSERT_FALSE(object.contains("integer")); + ASSERT_TRUE(object.contains("string")); + ASSERT_TRUE(object.contains("true")); + } catch (const JsonError &ex) { + FAIL() << ex.what(); + } +} + +TEST(ObjectInitializer, simple) +{ + try { + JsonObject object{ + { "username", "jean" }, + { "age", 99 } + }; + + ASSERT_EQ(2, static_cast<int>(object.size())); + ASSERT_EQ("jean", object["username"].toString()); + ASSERT_EQ(99, object["age"].toInteger()); + } catch (const std::exception &ex) { + FAIL() << ex.what(); + } +} + +TEST(ObjectInitializer, deep) +{ + try { + JsonObject object{ + { "username", "jean" }, + { "age", 99 }, + { "network", JsonObject{ + { "port", 9999 }, + { "host", "localhost" } + } + } + }; + + // First + ASSERT_EQ(3, static_cast<int>(object.size())); + ASSERT_EQ("jean", object["username"].toString()); + ASSERT_EQ(99, object["age"].toInteger()); + + // Second + JsonObject network = object["network"].toObject(); + ASSERT_TRUE(network.isObject()); + ASSERT_EQ(2, static_cast<int>(network.size())); + ASSERT_EQ(9999, network["port"].toInteger()); + ASSERT_EQ("localhost", network["host"].toString()); + } catch (const std::exception &ex) { + FAIL() << ex.what(); + } +} + +/* -------------------------------------------------------- + * Array + * -------------------------------------------------------- */ + +TEST(Array, push) +{ + try { + JsonArray array; + + ASSERT_TRUE(array.isArray()); + + array.push(1); + array.push("hello"); + array.push(true); + + ASSERT_EQ(3, static_cast<int>(array.size())); + ASSERT_TRUE(array[0].isTrue()); + ASSERT_EQ("hello", array[1].toString()); + ASSERT_EQ(1, array[2].toInteger()); + } catch (const JsonError &ex) { + FAIL() << ex.what(); + } +} + +TEST(Array, append) +{ + try { + JsonArray array; + + ASSERT_TRUE(array.isArray()); + + array.append(1); + array.append("hello"); + array.append(true); + + ASSERT_EQ(3, static_cast<int>(array.size())); + ASSERT_EQ(1, array[0].toInteger()); + ASSERT_EQ("hello", array[1].toString()); + ASSERT_TRUE(array[2].isTrue()); + } catch (const JsonError &ex) { + FAIL() << ex.what(); + } +} + +TEST(Array, insert) +{ + try { + JsonArray array; + + ASSERT_TRUE(array.isArray()); + + array.insert(1, 0); + array.insert("hello", 1); + array.insert(true, 0); + + ASSERT_EQ(3, static_cast<int>(array.size())); + ASSERT_TRUE(array[0].isTrue()); + ASSERT_EQ(1, array[1].toInteger()); + ASSERT_EQ("hello", array[2].toString()); + } catch (const JsonError &ex) { + FAIL() << ex.what(); + } +} + +TEST(Array, clear) +{ + try { + JsonArray array; + + array.append(1); + array.append("hello"); + array.append(true); + + array.clear(); + + ASSERT_EQ(0, static_cast<int>(array.size())); + } catch (const JsonError &ex) { + FAIL() << ex.what(); + } +} + +TEST(Array, erase) +{ + try { + JsonArray array; + + array.append(1); + array.append("hello"); + array.append(true); + + array.erase(0); + + ASSERT_EQ(2, static_cast<int>(array.size())); + ASSERT_EQ("hello", array[0].toString()); + ASSERT_TRUE(array[1].isTrue()); + } catch (const JsonError &ex) { + FAIL() << ex.what(); + } +} + +TEST(Array, eraseIterator) +{ + try { + JsonArray array; + + array.append(1); + array.append("hello"); + array.append(true); + + array.erase(array.begin()); + + ASSERT_EQ(2, static_cast<int>(array.size())); + ASSERT_EQ("hello", array[0].toString()); + ASSERT_TRUE(array[1].isTrue()); + } catch (const JsonError &ex) { + FAIL() << ex.what(); + } +} + +TEST(Array, eraseConstIterator) +{ + try { + JsonArray array; + + array.append(1); + array.append("hello"); + array.append(true); + + array.erase(array.cbegin()); + + ASSERT_EQ(2, static_cast<int>(array.size())); + ASSERT_EQ("hello", array[0].toString()); + ASSERT_TRUE(array[1].isTrue()); + } catch (const JsonError &ex) { + FAIL() << ex.what(); + } +} + +TEST(ArrayInitializer, simple) +{ + try { + JsonArray array{123, true, "hello"}; + + ASSERT_EQ(3, static_cast<int>(array.size())); + ASSERT_EQ(123, array[0].toInteger()); + ASSERT_TRUE(array[1].isTrue()); + ASSERT_EQ("hello", array[2].toString()); + } catch (const JsonError &ex) { + FAIL() << ex.what(); + } +} + +TEST(ArrayInitializer, deep) +{ + try { + JsonArray array{ + 123, + true, + "hello", + JsonArray{ + 321, + false, + "olleh" + } + }; + + // First + ASSERT_EQ(4, static_cast<int>(array.size())); + ASSERT_EQ(123, array[0].toInteger()); + ASSERT_TRUE(array[1].isTrue()); + ASSERT_EQ("hello", array[2].toString()); + + // Second + JsonArray array2 = array[3].toArray(); + ASSERT_TRUE(array.isArray()); + ASSERT_EQ(3, static_cast<int>(array2.size())); + ASSERT_EQ(321, array2[0].toInteger()); + ASSERT_TRUE(array2[1].isFalse()); + ASSERT_EQ("olleh", array2[2].toString()); + } catch (const JsonError &ex) { + FAIL() << ex.what(); + } +} + +/* -------------------------------------------------------- + * I/O + * -------------------------------------------------------- */ + +TEST(FileRead, simple) +{ + try { + JsonDocument doc(std::ifstream("Json/simple.json")); + + ASSERT_TRUE(doc.isObject()); + ASSERT_FALSE(doc.isArray()); + + JsonObject object = doc.toObject(); + JsonArray array = doc.toArray(); + + ASSERT_TRUE(object.isObject()); + ASSERT_FALSE(array.isArray()); + } catch (const JsonError &ex) { + FAIL() << ex.what(); + } +} + +TEST(FileRead, fail) +{ + try { + JsonDocument(std::ifstream("Json/notexist.json")); + + FAIL() << "Exception expected"; + } catch (const JsonError &) { + } +} + +TEST(FileWrite, simple) +{ + try { + JsonObject object{ + { "name", "jean" }, + { "age", 99 } + }; + + object.write(std::ofstream("object-write.json")); + + JsonObject object2 = JsonDocument(std::ifstream("object-write.json")).toObject(); + + ASSERT_EQ(object2, object); + } catch (const JsonError &ex) { + FAIL() << ex.what(); + } +} + +TEST(StringRead, simple) +{ + try { + JsonDocument doc("{ \"license\": \"ISC\" }"); + + ASSERT_TRUE(doc.isObject()); + ASSERT_FALSE(doc.isArray()); + + JsonObject object = doc.toObject(); + JsonArray array = doc.toArray(); + + ASSERT_TRUE(object.isObject()); + ASSERT_FALSE(array.isArray()); + } catch (const JsonError &ex) { + FAIL() << ex.what(); + } +} + +TEST(StringRead, fail) +{ + try { + JsonDocument("{ \"license\": ISC }"); + + FAIL() << "Exception expected"; + } catch (const JsonError &ex) { + } +} + +TEST(StringWrite, simple) +{ + try { + JsonObject object{ + { "name", "jean" }, + { "age", 99 } + }; + + JsonObject object2 = JsonDocument(object.dump()).toObject(); + + ASSERT_EQ(object2, object); + } catch (const JsonError &ex) { + FAIL() << ex.what(); + } +} + +/* -------------------------------------------------------- + * Object read + * -------------------------------------------------------- */ + +class ObjectRead : public testing::Test { +protected: + JsonObject m_object; + JsonObject m_objectAll; + +public: + ObjectRead() + { + m_object = JsonDocument(std::ifstream("Json/object.json")).toObject(); + m_objectAll = JsonDocument(std::ifstream("Json/object-all.json")).toObject(); + } +}; + +TEST_F(ObjectRead, simple) +{ + try { + JsonValue name = m_object["name"]; + JsonValue description = m_object["description"]; + + ASSERT_TRUE(name.isString()); + ASSERT_TRUE(description.isString()); + ASSERT_EQ("simple", name.toString()); + ASSERT_EQ("basic JSON file", description.toString()); + } catch (const JsonError &ex) { + FAIL() << ex.what(); + } +} + +TEST_F(ObjectRead, all) +{ + try { + ASSERT_TRUE(m_objectAll.contains("integer")); + ASSERT_FALSE(m_objectAll.contains("unexistant")); + + ASSERT_TRUE(m_objectAll["integer"].isInteger()); + ASSERT_TRUE(m_objectAll["integer"].isNumber()); + ASSERT_EQ(123, m_objectAll["integer"].toInteger()); + + ASSERT_TRUE(m_objectAll["real"].isReal()); + ASSERT_TRUE(m_objectAll["real"].isNumber()); + ASSERT_EQ(9.2, m_objectAll["real"].toReal()); + + ASSERT_TRUE(m_objectAll["false"].isBoolean()); + ASSERT_TRUE(m_objectAll["false"].isFalse()); + ASSERT_FALSE(m_objectAll["false"].isTrue()); + + ASSERT_TRUE(m_objectAll["true"].isBoolean()); + ASSERT_TRUE(m_objectAll["true"].isTrue()); + ASSERT_FALSE(m_objectAll["true"].isFalse()); + + ASSERT_TRUE(m_objectAll["null"].isNull()); + + ASSERT_TRUE(m_objectAll["object"].isObject()); + ASSERT_TRUE(m_objectAll["array"].isArray()); + } catch (const JsonError &ex) { + FAIL() << ex.what(); + } +} + +/* -------------------------------------------------------- + * Array read + * -------------------------------------------------------- */ + +class ArrayRead : public testing::Test { +protected: + JsonArray m_array; + JsonArray m_arrayAll; + +public: + ArrayRead() + { + m_array = JsonDocument(std::ifstream("Json/array.json")).toArray(); + m_arrayAll = JsonDocument(std::ifstream("Json/array-all.json")).toArray(); + } +}; + +TEST_F(ArrayRead, simple) +{ + try { + ASSERT_EQ(3, static_cast<int>(m_array.size())); + ASSERT_EQ(1, m_array[0].toInteger()); + ASSERT_EQ(2, m_array[1].toInteger()); + ASSERT_EQ(3, m_array[2].toInteger()); + } catch (const JsonError &ex) { + FAIL() << ex.what(); + } +} + +TEST_F(ArrayRead, all) +{ + try { + ASSERT_TRUE(m_arrayAll[0].isInteger()); + ASSERT_TRUE(m_arrayAll[0].isNumber()); + ASSERT_EQ(123, m_arrayAll[0].toInteger()); + + ASSERT_TRUE(m_arrayAll[1].isReal()); + ASSERT_TRUE(m_arrayAll[1].isNumber()); + ASSERT_EQ(9.2, m_arrayAll[1].toReal()); + + ASSERT_TRUE(m_arrayAll[2].isBoolean()); + ASSERT_TRUE(m_arrayAll[2].isFalse()); + ASSERT_FALSE(m_arrayAll[2].isTrue()); + + ASSERT_TRUE(m_arrayAll[3].isBoolean()); + ASSERT_TRUE(m_arrayAll[3].isTrue()); + ASSERT_FALSE(m_arrayAll[3].isFalse()); + + ASSERT_TRUE(m_arrayAll[4].isNull()); + + ASSERT_TRUE(m_arrayAll[5].isObject()); + ASSERT_TRUE(m_arrayAll[6].isArray()); + } catch (const JsonError &ex) { + FAIL() << ex.what(); + } +} + +/* -------------------------------------------------------- + * Object iterators + * -------------------------------------------------------- */ + +class ObjectIteratorsTest : public testing::Test { +protected: + JsonObject m_object; + +public: + ObjectIteratorsTest() + { + m_object.set("integer", 1); + m_object.set("string", "hello"); + m_object.set("boolean", true); + } +}; + +TEST_F(ObjectIteratorsTest, operators) +{ + // Read only (non const) + { + std::set<std::string> expected{"boolean", "integer", "string"}; + std::set<std::string> result; + std::unordered_map<std::string, JsonValue> values; + JsonObject::iterator it = m_object.begin(); + JsonObject::iterator end = m_object.end(); + + while (it != end) { + values.insert({it->first, it->second}); + result.insert((*it++).first); + } + + ASSERT_EQ(expected, result); + ASSERT_EQ(1, values["integer"].toInteger()); + ASSERT_EQ("hello", values["string"].toString()); + ASSERT_TRUE(values["boolean"].isTrue()); + } + + // Read only (const) + { + std::set<std::string> expected{"boolean", "integer", "string"}; + std::set<std::string> result; + std::unordered_map<std::string, JsonValue> values; + JsonObject::const_iterator it = m_object.cbegin(); + JsonObject::const_iterator end = m_object.cend(); + + while (it != end) { + values.insert({it->first, it->second}); + result.insert((*it++).first); + } + + ASSERT_EQ(expected, result); + ASSERT_EQ(1, values["integer"].toInteger()); + ASSERT_EQ("hello", values["string"].toString()); + ASSERT_TRUE(values["boolean"].isTrue()); + } +} + +TEST_F(ObjectIteratorsTest, assign) +{ + // Assign (non const) + { + JsonObject::iterator it = m_object.begin(); + std::string key = it->first; + + it->second = JsonValue("CHANGED"); + + ASSERT_EQ("CHANGED", m_object[key].toString()); + ASSERT_EQ("CHANGED", it->second.toString()); + ASSERT_EQ(3, static_cast<int>(m_object.size())); + } +} + +TEST_F(ObjectIteratorsTest, assignConst) +{ + // Assign (const) + { + JsonObject::const_iterator it = m_object.cbegin(); + std::string key = it->first; + JsonValue orig = it->second; + + it->second = JsonValue("CHANGED"); + + ASSERT_TRUE(m_object.contains(key)); + ASSERT_EQ(orig, m_object[key]); + ASSERT_EQ(3, static_cast<int>(m_object.size())); + } +} + +/* -------------------------------------------------------- + * Array iterators + * -------------------------------------------------------- */ + +class ArrayIteratorsTest : public testing::Test { +protected: + JsonArray m_array; + +public: + ArrayIteratorsTest() + { + m_array.append(1); + m_array.append("hello"); + m_array.append(true); + } +}; + +TEST_F(ArrayIteratorsTest, operators) +{ + // Read only (non const) + { + JsonArray::iterator it = m_array.begin(); + + ASSERT_EQ(1, (*it).toInteger()); + ASSERT_EQ(1, it->toInteger()); + ASSERT_EQ("hello", it[1].toString()); + ASSERT_TRUE(it[2].isTrue()); + + JsonArray::iterator it2 = it + 1; + ASSERT_EQ(1, it2[-1].toInteger()); + ASSERT_EQ("hello", it2->toString()); + ASSERT_TRUE(it2[1].isTrue()); + + JsonArray::iterator it3 = it; + ASSERT_TRUE(it2 != it); + ASSERT_FALSE(it3 != it); + + ASSERT_FALSE(it2 == it); + ASSERT_TRUE(it3 == it); + + ASSERT_TRUE(it3 >= it); + ASSERT_TRUE(it2 >= it); + + ASSERT_TRUE(it2 > it); + ASSERT_FALSE(it3 > it); + + ASSERT_FALSE(it2 <= it); + ASSERT_TRUE(it3 <= it); + + ASSERT_FALSE(it2 < it); + ASSERT_FALSE(it3 < it); + } + + // Read only (const) + { + JsonArray::const_iterator it = m_array.cbegin(); + + ASSERT_EQ(1, (*it).toInteger()); + ASSERT_EQ(1, it->toInteger()); + ASSERT_EQ("hello", it[1].toString()); + ASSERT_TRUE(it[2].isTrue()); + + JsonArray::const_iterator it2 = it + 1; + ASSERT_EQ(1, it2[-1].toInteger()); + ASSERT_EQ("hello", it2->toString()); + ASSERT_TRUE(it2[1].isTrue()); + + JsonArray::const_iterator it3 = it; + ASSERT_TRUE(it2 != it); + ASSERT_FALSE(it3 != it); + + ASSERT_FALSE(it2 == it); + ASSERT_TRUE(it3 == it); + + ASSERT_TRUE(it3 >= it); + ASSERT_TRUE(it2 >= it); + + ASSERT_TRUE(it2 > it); + ASSERT_FALSE(it3 > it); + + ASSERT_FALSE(it2 <= it); + ASSERT_TRUE(it3 <= it); + + ASSERT_FALSE(it2 < it); + ASSERT_FALSE(it3 < it); + } +} + +TEST_F(ArrayIteratorsTest, assign) +{ + // Assign (non const) + { + JsonArray::iterator it = m_array.begin(); + + *it = JsonValue(9999); + + ASSERT_EQ(3, static_cast<int>(m_array.size())); + ASSERT_EQ(9999, it->toInteger()); + ASSERT_EQ(9999, m_array[0].toInteger()); + } +} + +TEST_F(ArrayIteratorsTest, assignConst) +{ + // Assign (const) + { + JsonArray::const_iterator it = m_array.cbegin(); + + *it = JsonValue(9999); + + ASSERT_EQ(3, static_cast<int>(m_array.size())); + ASSERT_EQ(1, it->toInteger()); + ASSERT_EQ(1, m_array[0].toInteger()); + } +} + +TEST_F(ArrayIteratorsTest, castToRef) +{ + JsonArray array{1, 2, 3}; + int i = 1; + + for (const JsonValue &v : array) + { + ASSERT_EQ(i++, v.toInteger()); + } +} + +int main(int argc, char **argv) +{ + testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/C++/tests/OptionParser/main.cpp Sun Mar 08 14:26:33 2015 +0100 @@ -0,0 +1,229 @@ +/* + * main.cpp -- main test file for OptionParser + * + * Copyright (c) 2013, 2014 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <gtest/gtest.h> + +#include <OptionParser.h> + +/* -------------------------------------------------------- + * Short options + * -------------------------------------------------------- */ + +TEST(Short, simpleNoArg) +{ + OptionParser parser{ + { "a", "", Option::NoArg }, + { "b", "", Option::NoArg } + }; + + OptionPack pack = parser.parse({ "-a", "-b" }); + + ASSERT_TRUE(pack); + + ASSERT_EQ(2, static_cast<int>(pack.size())); + ASSERT_EQ(2, pack.parsed()); + + ASSERT_TRUE(pack[0] == "a"); + ASSERT_TRUE(pack[1] == "b"); +} + +TEST(Short, simpleArg) +{ + OptionParser parser{ + { "v", "", Option::NoArg }, + { "c", "", } + }; + + OptionPack pack = parser.parse({ "-v", "-cfoo.conf" }); + + ASSERT_TRUE(pack); + + ASSERT_EQ(2, static_cast<int>(pack.size())); + ASSERT_EQ(2, pack.parsed()); + + ASSERT_TRUE(pack[0] == "v"); + ASSERT_TRUE(pack[1] == "c"); + ASSERT_EQ("foo.conf", pack[1].value()); +} + +TEST(Short, spacedArg) +{ + OptionParser parser{ + { "v", "", Option::NoArg }, + { "c", "", } + }; + + OptionPack pack = parser.parse({ "-v", "-c", "foo.conf" }); + + ASSERT_TRUE(pack); + + ASSERT_EQ(2, static_cast<int>(pack.size())); + ASSERT_EQ(3, pack.parsed()); + + ASSERT_TRUE(pack[0] == "v"); + ASSERT_TRUE(pack[1] == "c"); + ASSERT_EQ("foo.conf", pack[1].value()); +} + +TEST(Short, compacted) +{ + OptionParser parser{ + { "a", "", Option::NoArg }, + { "b", "", Option::NoArg }, + { "c", "", Option::NoArg }, + }; + + OptionPack pack = parser.parse({ "-abc" }); + + ASSERT_TRUE(pack); + + ASSERT_EQ(3, static_cast<int>(pack.size())); + ASSERT_EQ(1, pack.parsed()); + + ASSERT_TRUE(pack[0] == "a"); + ASSERT_TRUE(pack[1] == "b"); + ASSERT_TRUE(pack[2] == "c"); +} + +TEST(Short, compactedArg) +{ + OptionParser parser{ + { "a", "", }, + { "b", "", }, + { "c", "", }, + }; + + OptionPack pack = parser.parse({ "-abc" }); + + ASSERT_TRUE(pack); + + ASSERT_EQ(1, static_cast<int>(pack.size())); + ASSERT_EQ(1, pack.parsed()); + + ASSERT_TRUE(pack[0] == "a"); +} + +/* -------------------------------------------------------- + * Long options + * -------------------------------------------------------- */ + +TEST(Long, simple) +{ + OptionParser parser{ + { "", "verbose", Option::NoArg }, + { "", "fullscreen", Option::NoArg } + }; + + OptionPack pack = parser.parse({ "--fullscreen" }); + + ASSERT_TRUE(pack); + + ASSERT_EQ(1, static_cast<int>(pack.size())); + ASSERT_EQ(1, pack.parsed()); + + ASSERT_TRUE(pack[0] == "fullscreen"); +} + +TEST(Long, simpleArg) +{ + OptionParser parser{ + { "", "config", }, + { "", "level", } + }; + + OptionPack pack = parser.parse({ "--config", "config.conf", "--level", "2" }); + + ASSERT_TRUE(pack); + + ASSERT_EQ(2, static_cast<int>(pack.size())); + ASSERT_EQ(4, pack.parsed()); + + ASSERT_TRUE(pack[0] == "config"); + ASSERT_EQ("config.conf", pack[0].value()); + ASSERT_TRUE(pack[1] == "level"); + ASSERT_EQ("2", pack[1].value()); +} + +/* -------------------------------------------------------- + * Combined + * -------------------------------------------------------- */ + +TEST(Combined, simple) +{ + OptionParser parser{ + { "v", "verbose", Option::NoArg }, + { "l", "level", Option::NoArg } + }; + + OptionPack pack = parser.parse({ "-v", "-l", "--verbose", "--level" }); + + ASSERT_TRUE(pack); + + ASSERT_TRUE(pack[0] == "v" && pack[0] == "verbose"); + ASSERT_TRUE(pack[1] == "l" && pack[1] == "level"); + ASSERT_TRUE(pack[2] == "v" && pack[2] == "verbose"); + ASSERT_TRUE(pack[3] == "l" && pack[3] == "level"); +} + +/* -------------------------------------------------------- + * Flags + * -------------------------------------------------------- */ + +TEST(Flags, standard) +{ + // No flags, parse unless there is an argument which is not an option + OptionParser parser{ + { "v", "", Option::NoArg } + }; + + OptionPack pack = parser.parse({ "-v", "install", "malikania" }); + + ASSERT_FALSE(pack); + + ASSERT_EQ(1, static_cast<int>(pack.size())); + ASSERT_EQ(1, pack.parsed()); + + ASSERT_TRUE(pack[0] == "v"); +} + +TEST(Flags, unstrict) +{ + // No flags, parse unless there is an argument which is not an option + OptionParser parser{ + { "v", "", Option::NoArg }, + { "d", "", } + }; + + OptionPack pack = parser.parse({ "-v", "install", "malikania", "-d", "/usr/local" }, OptionParser::Unstrict); + + ASSERT_TRUE(pack); + + ASSERT_EQ(2, static_cast<int>(pack.size())); + ASSERT_EQ(5, pack.parsed()); + + ASSERT_TRUE(pack[0] == "v"); + ASSERT_TRUE(pack[1] == "d"); + ASSERT_EQ("/usr/local", pack[1].value()); +} + +int main(int argc, char **argv) +{ + testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/C++/tests/Pack/main.cpp Sun Mar 08 14:26:33 2015 +0100 @@ -0,0 +1,508 @@ +/* + * TestPack.cpp -- test the pack serializer + * + * Copyright (c) 2013, 2014 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sstream> +#include <vector> + +#include <gtest/gtest.h> + +#include <Pack.h> + +struct Point +{ + uint32_t width{}; + uint32_t height{}; + + Point() = default; + + Point(uint32_t width, uint32_t height) + : width(width) + , height(height) + { + } + + inline bool operator==(const Point &other) const + { + return width == other.width && height == other.height; + } +}; + +template <> +struct Pack::TypeInfo<Point> : public Pack::Serializable +{ + static void serialize(PackWriter &writer, const Point &point) + { + writer << point.width << point.height; + } + + static void unserialize(PackReader &reader, Point &point) + { + reader >> point.width >> point.height; + } +}; + +TEST(File, simpleLittleEndian) +{ + uint8_t u8(1), r8; + uint16_t u16(2), r16; + uint32_t u32(3), r32; + uint64_t u64(4), r64; + + remove("output.bin"); + + try { + { + PackFileWriter writer{"output.bin", Pack::Little}; + writer << u8 << u16 << u32 << u64; + } + + PackFileReader reader{"output.bin", Pack::Little}; + reader >> r8 >> r16 >> r32 >> r64; + + ASSERT_EQ(u8, r8); + ASSERT_EQ(u16, r16); + ASSERT_EQ(u32, r32); + ASSERT_EQ(u64, r64); + } catch (const std::exception &error) { + FAIL() << error.what(); + } +} + +TEST(File, simpleBigEndian) +{ + uint8_t u8(1), r8; + uint16_t u16(2), r16; + uint32_t u32(3), r32; + uint64_t u64(4), r64; + + remove("output.bin"); + + try { + { + PackFileWriter writer{"output.bin", Pack::Big}; + writer << u8 << u16 << u32 << u64; + } + + PackFileReader reader{"output.bin", Pack::Big}; + reader >> r8 >> r16 >> r32 >> r64; + + ASSERT_EQ(u8, r8); + ASSERT_EQ(u16, r16); + ASSERT_EQ(u32, r32); + ASSERT_EQ(u64, r64); + } catch (const std::exception &error) { + FAIL() << error.what(); + } +} + +TEST(File, arrayLittleEndian) +{ + std::vector<uint8_t> u8 { 1, 2, 3, 4, 5, 6, 7, 8 }; + std::vector<uint16_t> u16 { 10, 20, 30, 40, 50, 60, 70, 80 }; + std::vector<uint32_t> u32 { 100, 200, 300, 400, 500, 600, 700, 800 }; + std::vector<uint64_t> u64 { 1000, 2000, 3000, 4000, 5000, 6000, 7000, 8000 }; + + std::vector<uint8_t> r8(8); + std::vector<uint16_t> r16(8); + std::vector<uint32_t> r32(8); + std::vector<uint64_t> r64(8); + + remove("output.bin"); + + try { + { + PackFileWriter writer{"output.bin", Pack::Little}; + + writer << u8 << u16 << u32 << u64; + } + + PackFileReader reader{"output.bin", Pack::Little}; + + reader >> r8 >> r16 >> r32 >> r64; + + ASSERT_EQ(u8, r8); + ASSERT_EQ(u16, r16); + ASSERT_EQ(u32, r32); + ASSERT_EQ(u64, r64); + } catch (const std::exception &error) { + FAIL() << error.what(); + } +} + +TEST(File, arrayBigEndian) +{ + std::vector<uint8_t> u8 { 1, 2, 3, 4, 5, 6, 7, 8 }; + std::vector<uint16_t> u16 { 10, 20, 30, 40, 50, 60, 70, 80 }; + std::vector<uint32_t> u32 { 100, 200, 300, 400, 500, 600, 700, 800 }; + std::vector<uint64_t> u64 { 1000, 2000, 3000, 4000, 5000, 6000, 7000, 8000 }; + + std::vector<uint8_t> r8(8); + std::vector<uint16_t> r16(8); + std::vector<uint32_t> r32(8); + std::vector<uint64_t> r64(8); + + remove("output.bin"); + + try { + { + PackFileWriter writer{"output.bin", Pack::Big}; + + writer << u8 << u16 << u32 << u64; + } + + PackFileReader reader{"output.bin", Pack::Big}; + + reader >> r8 >> r16 >> r32 >> r64; + + ASSERT_EQ(u8, r8); + ASSERT_EQ(u16, r16); + ASSERT_EQ(u32, r32); + ASSERT_EQ(u64, r64); + } catch (const std::exception &error) { + FAIL() << error.what(); + } +} + +TEST(File, serializeSimpleLittleEndian) +{ + Point point{200, 400}; + Point result; + + remove("output.bin"); + + try { + { + PackFileWriter writer{"output.bin", Pack::Little}; + + writer << point; + } + + PackFileReader reader{"output.bin", Pack::Little}; + + reader >> result; + + ASSERT_EQ(point, result); + } catch (const std::exception &ex) { + std::cerr << "warning: " << ex.what() << std::endl; + } +} + +TEST(File, serializeSimpleBigEndian) +{ + Point point{200, 400}; + Point result; + + remove("output.bin"); + + try { + { + PackFileWriter writer{"output.bin", Pack::Big}; + + writer << point; + } + + PackFileReader reader{"output.bin", Pack::Big}; + + reader >> result; + + ASSERT_EQ(point, result); + } catch (const std::exception &ex) { + std::cerr << "warning: " << ex.what() << std::endl; + } +} + +TEST(File, serializeArrayLittleEndian) +{ + std::vector<Point> points{{10, 20}, {30, 40}}; + std::vector<Point> rpoints(2); + + remove("output.bin"); + + try { + { + PackFileWriter writer{"output.bin", Pack::Little}; + + writer << points; + } + + PackFileReader reader{"output.bin", Pack::Little}; + + reader >> rpoints; + + ASSERT_EQ(points, rpoints); + } catch (const std::exception &ex) { + std::cerr << "warning: " << ex.what() << std::endl; + } +} + +TEST(File, serializeArrayBigEndian) +{ + std::vector<Point> points{{10, 20}, {30, 40}}; + std::vector<Point> rpoints(2); + + remove("output.bin"); + + try { + { + PackFileWriter writer{"output.bin", Pack::Big}; + + writer << points; + } + + PackFileReader reader{"output.bin", Pack::Big}; + + reader >> rpoints; + + ASSERT_EQ(points, rpoints); + } catch (const std::exception &ex) { + std::cerr << "warning: " << ex.what() << std::endl; + } +} + +TEST(String, simpleLittleEndian) +{ + uint8_t u8(1), r8; + uint16_t u16(2), r16; + uint32_t u32(3), r32; + uint64_t u64(4), r64; + + try { + std::string input; + + { + PackStringWriter writer{Pack::Little}; + writer << u8 << u16 << u32 << u64; + input = writer.buffer(); + } + + PackStringReader reader{std::move(input), Pack::Little}; + reader >> r8 >> r16 >> r32 >> r64; + + ASSERT_EQ(u8, r8); + ASSERT_EQ(u16, r16); + ASSERT_EQ(u32, r32); + ASSERT_EQ(u64, r64); + } catch (const std::exception &error) { + FAIL() << error.what(); + } +} + +TEST(String, simpleBigEndian) +{ + uint8_t u8(1), r8; + uint16_t u16(2), r16; + uint32_t u32(3), r32; + uint64_t u64(4), r64; + + try { + std::string input; + + { + PackStringWriter writer{Pack::Big}; + writer << u8 << u16 << u32 << u64; + input = writer.buffer(); + } + + PackStringReader reader{std::move(input), Pack::Big}; + reader >> r8 >> r16 >> r32 >> r64; + + ASSERT_EQ(u8, r8); + ASSERT_EQ(u16, r16); + ASSERT_EQ(u32, r32); + ASSERT_EQ(u64, r64); + } catch (const std::exception &error) { + FAIL() << error.what(); + } +} + +TEST(String, arrayLittleEndian) +{ + std::vector<uint8_t> u8 { 1, 2, 3, 4, 5, 6, 7, 8 }; + std::vector<uint16_t> u16 { 10, 20, 30, 40, 50, 60, 70, 80 }; + std::vector<uint32_t> u32 { 100, 200, 300, 400, 500, 600, 700, 800 }; + std::vector<uint64_t> u64 { 1000, 2000, 3000, 4000, 5000, 6000, 7000, 8000 }; + + std::vector<uint8_t> r8(8); + std::vector<uint16_t> r16(8); + std::vector<uint32_t> r32(8); + std::vector<uint64_t> r64(8); + + try { + std::string input; + + { + PackStringWriter writer{Pack::Little}; + + writer << u8 << u16 << u32 << u64; + input = writer.buffer(); + } + + PackStringReader reader{std::move(input), Pack::Little}; + + reader >> r8 >> r16 >> r32 >> r64; + + ASSERT_EQ(u8, r8); + ASSERT_EQ(u16, r16); + ASSERT_EQ(u32, r32); + ASSERT_EQ(u64, r64); + } catch (const std::exception &error) { + FAIL() << error.what(); + } +} + +TEST(String, arrayBigEndian) +{ + std::vector<uint8_t> u8 { 1, 2, 3, 4, 5, 6, 7, 8 }; + std::vector<uint16_t> u16 { 10, 20, 30, 40, 50, 60, 70, 80 }; + std::vector<uint32_t> u32 { 100, 200, 300, 400, 500, 600, 700, 800 }; + std::vector<uint64_t> u64 { 1000, 2000, 3000, 4000, 5000, 6000, 7000, 8000 }; + + std::vector<uint8_t> r8(8); + std::vector<uint16_t> r16(8); + std::vector<uint32_t> r32(8); + std::vector<uint64_t> r64(8); + + try { + std::string input; + + { + PackStringWriter writer{Pack::Big}; + + writer << u8 << u16 << u32 << u64; + input = writer.buffer(); + } + + PackStringReader reader{std::move(input), Pack::Big}; + + reader >> r8 >> r16 >> r32 >> r64; + + ASSERT_EQ(u8, r8); + ASSERT_EQ(u16, r16); + ASSERT_EQ(u32, r32); + ASSERT_EQ(u64, r64); + } catch (const std::exception &error) { + FAIL() << error.what(); + } +} + +TEST(String, serializeSimpleLittleEndian) +{ + Point point{200, 400}; + Point result; + + try { + std::string input; + + { + PackStringWriter writer{Pack::Little}; + + writer << point; + input = writer.buffer(); + } + + PackStringReader reader{std::move(input), Pack::Little}; + + reader >> result; + + ASSERT_EQ(point, result); + } catch (const std::exception &ex) { + std::cerr << "warning: " << ex.what() << std::endl; + } +} + +TEST(String, serializeSimpleBigEndian) +{ + Point point{200, 400}; + Point result; + + try { + std::string input; + + { + PackStringWriter writer{Pack::Big}; + + writer << point; + input = writer.buffer(); + } + + PackStringReader reader{std::move(input), Pack::Big}; + + reader >> result; + + ASSERT_EQ(point, result); + } catch (const std::exception &ex) { + std::cerr << "warning: " << ex.what() << std::endl; + } +} + +TEST(String, serializeArrayLittleEndian) +{ + std::vector<Point> points{{10, 20}, {30, 40}}; + std::vector<Point> rpoints(2); + + try { + std::string input; + + { + PackStringWriter writer{Pack::Little}; + + writer << points; + input = writer.buffer(); + } + + PackStringReader reader{std::move(input), Pack::Little}; + + reader >> rpoints; + + ASSERT_EQ(points, rpoints); + } catch (const std::exception &ex) { + std::cerr << "warning: " << ex.what() << std::endl; + } +} + +TEST(String, serializeArrayBigEndian) +{ + std::vector<Point> points{{10, 20}, {30, 40}}; + std::vector<Point> rpoints(2); + + try { + std::string input; + + { + PackStringWriter writer{Pack::Big}; + + writer << points; + input = writer.buffer(); + } + + PackStringReader reader{std::move(input), Pack::Big}; + + reader >> rpoints; + + ASSERT_EQ(points, rpoints); + } catch (const std::exception &ex) { + std::cerr << "warning: " << ex.what() << std::endl; + } +} + +int main(int argc, char **argv) +{ + testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/C++/tests/Parser/configs/multi.conf Sun Mar 08 14:26:33 2015 +0100 @@ -0,0 +1,7 @@ +[entity] +name = "Player" +version = 1.0 + +[entity] +name = "Subwinner" +version = 2.0 \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/C++/tests/Parser/configs/simple.conf Sun Mar 08 14:26:33 2015 +0100 @@ -0,0 +1,4 @@ +[general] +option1=1 +option2 =2 +option3 = 3
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/C++/tests/Parser/main.cpp Sun Mar 08 14:26:33 2015 +0100 @@ -0,0 +1,98 @@ +/* + * TestParser.cpp -- test the config file parser + * + * Copyright (c) 2013, 2014 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <iostream> + +#include <gtest/gtest.h> + +#include <Parser.h> + +TEST(Basic, simple) +{ + try { + Parser parser("Parser/simple.conf"); + + const auto &s = parser.getSection("general"); + ASSERT_EQ("general", s.getName()); + + const auto &o1 = s.getOption<std::string>("option1"); + ASSERT_EQ("1", o1); + + const auto &o2 = s.getOption<std::string>("option2"); + ASSERT_EQ("2", o2); + + const auto &o3 = s.getOption<std::string>("option3"); + ASSERT_EQ("3", o3); + } catch (const std::out_of_range &error) { + FAIL(); + } catch (const std::runtime_error &error) { + FAIL() << error.what(); + } +} + +TEST(Basic, multi) +{ + try { + Parser parser("Parser/multi.conf"); + int i(0); + + parser.findSections("entity", [&] (const Section &s) { + if (i++ == 0) { + ASSERT_EQ("Player", s.getOption<std::string>("name")); + ASSERT_EQ("1.0", s.getOption<std::string>("version")); + } else { + ASSERT_EQ("Subwinner", s.getOption<std::string>("name")); + ASSERT_EQ("2.0", s.getOption<std::string>("version")); + } + }); + + ASSERT_EQ(2, i); + } catch (const std::out_of_range &error) { + FAIL(); + } catch (const std::runtime_error &error) { + FAIL() << error.what(); + } +} + +TEST(Basic, multiNoredef) +{ + try { + Parser parser("Parser/multi.conf", Parser::DisableRedefinition); + int i(0); + + parser.findSections("entity", [&] (const Section &s) { + if (i++ == 0) { + ASSERT_EQ("Player", s.getOption<std::string>("name")); + ASSERT_EQ("1.0", s.getOption<std::string>("version")); + } + }); + + ASSERT_EQ(1, i); + } catch (const std::out_of_range &error) { + FAIL(); + } catch (const std::runtime_error &error) { + FAIL() << error.what(); + } +} + +int main(int argc, char **argv) +{ + testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/C++/tests/Socket/main.cpp Sun Mar 08 14:26:33 2015 +0100 @@ -0,0 +1,709 @@ +/* + * main.cpp -- test sockets + * + * Copyright (c) 2013, 2014 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <chrono> +#include <iostream> +#include <sstream> +#include <string> +#include <thread> + +#include <gtest/gtest.h> + +#include "Socket.h" +#include "SocketAddress.h" +#include "SocketListener.h" +#include "SocketSsl.h" +#include "SocketTcp.h" +#include "SocketUdp.h" + +using namespace address; +using namespace std::literals::chrono_literals; + +/* -------------------------------------------------------- + * TCP tests + * -------------------------------------------------------- */ + +class TcpServerTest : public testing::Test { +protected: + SocketTcp m_server{AF_INET, 0}; + SocketTcp m_client{AF_INET, 0}; + + std::thread m_tserver; + std::thread m_tclient; + +public: + TcpServerTest() + { + m_server.set(SOL_SOCKET, SO_REUSEADDR, 1); + } + + ~TcpServerTest() + { + if (m_tserver.joinable()) + m_tserver.join(); + if (m_tclient.joinable()) + m_tclient.join(); + } +}; + +TEST_F(TcpServerTest, connect) +{ + m_tserver = std::thread([this] () { + m_server.bind(Internet("*", 16000, AF_INET)); + + ASSERT_EQ(SocketState::Bound, m_server.state()); + + m_server.listen(); + m_server.accept(); + m_server.close(); + }); + + std::this_thread::sleep_for(100ms); + + m_tclient = std::thread([this] () { + m_client.connect(Internet("127.0.0.1", 16000, AF_INET)); + + ASSERT_EQ(SocketState::Connected, m_client.state()); + + m_client.close(); + }); +} + +TEST_F(TcpServerTest, io) +{ + m_tserver = std::thread([this] () { + m_server.bind(Internet("*", 16000, AF_INET)); + m_server.listen(); + + auto client = m_server.accept(); + auto msg = client.recv(512); + + ASSERT_EQ("hello world", msg); + + client.send(msg); + client.close(); + + m_server.close(); + }); + + std::this_thread::sleep_for(100ms); + + m_tclient = std::thread([this] () { + m_client.connect(Internet("127.0.0.1", 16000, AF_INET)); + m_client.send("hello world"); + + ASSERT_EQ("hello world", m_client.recv(512)); + + m_client.close(); + }); +} + +/* -------------------------------------------------------- + * UDP tests + * -------------------------------------------------------- */ + +class UdpServerTest : public testing::Test { +protected: + SocketUdp m_server{AF_INET, 0}; + SocketUdp m_client{AF_INET, 0}; + + std::thread m_tserver; + std::thread m_tclient; + +public: + UdpServerTest() + { + m_server.set(SOL_SOCKET, SO_REUSEADDR, 1); + } + + ~UdpServerTest() + { + if (m_tserver.joinable()) + m_tserver.join(); + if (m_tclient.joinable()) + m_tclient.join(); + } +}; + +TEST_F(UdpServerTest, io) +{ + m_tserver = std::thread([this] () { + SocketAddress info; + + m_server.bind(Internet("*", 16000, AF_INET)); + + auto msg = m_server.recvfrom(512, info); + + ASSERT_EQ("hello world", msg); + + m_server.sendto(msg, info); + m_server.close(); + }); + + std::this_thread::sleep_for(100ms); + + m_tclient = std::thread([this] () { + Internet info("127.0.0.1", 16000, AF_INET); + + m_client.sendto("hello world", info); + + ASSERT_EQ("hello world", m_client.recvfrom(512, info)); + + m_client.close(); + }); +} + +/* -------------------------------------------------------- + * Listener tests (standard) + * -------------------------------------------------------- */ + +class ListenerTest : public testing::Test { +protected: + SocketListener m_listener; + SocketTcp socket1{AF_INET, 0}; + SocketUdp socket2{AF_INET, 0}; + +public: + ~ListenerTest() + { + socket1.close(); + socket2.close(); + } +}; + +TEST_F(ListenerTest, set) +{ + m_listener.set(socket1, SocketListener::Read); + + ASSERT_EQ(1, static_cast<int>(m_listener.size())); + ASSERT_EQ(SocketListener::Read, m_listener.begin()->second); + + m_listener.set(socket1, SocketListener::Write); + + ASSERT_EQ(1, static_cast<int>(m_listener.size())); + ASSERT_EQ(0x3, m_listener.begin()->second); + + // Fake a re-insert of the same socket + m_listener.set(socket1, SocketListener::Write); + + ASSERT_EQ(1, static_cast<int>(m_listener.size())); + ASSERT_EQ(0x3, m_listener.begin()->second); + + // Add an other socket now + m_listener.set(socket2, SocketListener::Read | SocketListener::Write); + + ASSERT_EQ(2, static_cast<int>(m_listener.size())); + + for (auto &pair : m_listener) { + ASSERT_EQ(0x3, pair.second); + ASSERT_TRUE(pair.first == socket1 || pair.first == socket2); + } +} + +TEST_F(ListenerTest, unset) +{ + m_listener.set(socket1, SocketListener::Read | SocketListener::Write); + m_listener.set(socket2, SocketListener::Read | SocketListener::Write); + + m_listener.unset(socket1, SocketListener::Read); + + ASSERT_EQ(2, static_cast<int>(m_listener.size())); + + // Use a for loop since it can be ordered differently + for (auto &pair : m_listener) { + if (pair.first == socket1) { + ASSERT_EQ(0x2, pair.second); + } else if (pair.first == socket2) { + ASSERT_EQ(0x3, pair.second); + } + } + + m_listener.unset(socket1, SocketListener::Write); + + ASSERT_EQ(1, static_cast<int>(m_listener.size())); + ASSERT_EQ(0x3, m_listener.begin()->second); +} + +TEST_F(ListenerTest, remove) +{ + m_listener.set(socket1, SocketListener::Read | SocketListener::Write); + m_listener.set(socket2, SocketListener::Read | SocketListener::Write); + m_listener.remove(socket1); + + ASSERT_EQ(1, static_cast<int>(m_listener.size())); + ASSERT_EQ(0x3, m_listener.begin()->second); +} + +TEST_F(ListenerTest, clear) +{ + m_listener.set(socket1, SocketListener::Read | SocketListener::Write); + m_listener.set(socket2, SocketListener::Read | SocketListener::Write); + m_listener.clear(); + + ASSERT_EQ(0, static_cast<int>(m_listener.size())); +} + +/* -------------------------------------------------------- + * Listener: poll + * -------------------------------------------------------- */ + +#if defined(SOCKET_HAVE_POLL) + +class ListenerPollTest : public testing::Test { +protected: + SocketListener m_listener{SocketMethod::Poll}; + SocketTcp m_masterTcp{AF_INET, 0}; + SocketTcp m_clientTcp{AF_INET, 0}; + + std::thread m_tserver; + std::thread m_tclient; + +public: + ListenerPollTest() + { + m_masterTcp.set(SOL_SOCKET, SO_REUSEADDR, 1); + m_masterTcp.bind(Internet("*", 16000, AF_INET)); + m_masterTcp.listen(); + } + + ~ListenerPollTest() + { + if (m_tserver.joinable()) { + m_tserver.join(); + } + if (m_tclient.joinable()) { + m_tclient.join(); + } + } +}; + +TEST_F(ListenerPollTest, accept) +{ + m_tserver = std::thread([this] () { + try { + m_listener.set(m_masterTcp, SocketListener::Read); + m_listener.select(); + m_masterTcp.accept(); + m_masterTcp.close(); + } catch (const std::exception &ex) { + FAIL() << ex.what(); + } + }); + + std::this_thread::sleep_for(100ms); + + m_tclient = std::thread([this] () { + m_clientTcp.connect(Internet("127.0.0.1", 16000, AF_INET)); + }); +} + +TEST_F(ListenerPollTest, recv) +{ + m_tserver = std::thread([this] () { + try { + m_listener.set(m_masterTcp, SocketListener::Read); + m_listener.select(); + + auto sc = m_masterTcp.accept(); + + ASSERT_EQ("hello", sc.recv(512)); + + m_masterTcp.close(); + } catch (const std::exception &ex) { + FAIL() << ex.what(); + } + }); + + std::this_thread::sleep_for(100ms); + + m_tclient = std::thread([this] () { + m_clientTcp.connect(Internet("127.0.0.1", 16000, AF_INET)); + m_clientTcp.send("hello"); + }); +} + +#endif + +/* -------------------------------------------------------- + * Listener: select + * -------------------------------------------------------- */ + +class ListenerSelectTest : public testing::Test { +protected: + SocketListener m_listener{SocketMethod::Select}; + SocketTcp m_masterTcp{AF_INET, 0}; + SocketTcp m_clientTcp{AF_INET, 0}; + + std::thread m_tserver; + std::thread m_tclient; + +public: + ListenerSelectTest() + { + m_masterTcp.set(SOL_SOCKET, SO_REUSEADDR, 1); + m_masterTcp.bind(Internet("*", 16000, AF_INET)); + m_masterTcp.listen(); + } + + ~ListenerSelectTest() + { + if (m_tserver.joinable()) { + m_tserver.join(); + } + if (m_tclient.joinable()) { + m_tclient.join(); + } + } +}; + +TEST_F(ListenerSelectTest, accept) +{ + m_tserver = std::thread([this] () { + try { + m_listener.set(m_masterTcp, SocketListener::Read); + m_listener.select(); + m_masterTcp.accept(); + m_masterTcp.close(); + } catch (const std::exception &ex) { + FAIL() << ex.what(); + } + }); + + std::this_thread::sleep_for(100ms); + + m_tclient = std::thread([this] () { + m_clientTcp.connect(Internet("127.0.0.1", 16000, AF_INET)); + }); +} + +TEST_F(ListenerSelectTest, recv) +{ + m_tserver = std::thread([this] () { + try { + m_listener.set(m_masterTcp, SocketListener::Read); + m_listener.select(); + + auto sc = m_masterTcp.accept(); + + ASSERT_EQ("hello", sc.recv(512)); + + m_masterTcp.close(); + } catch (const std::exception &ex) { + FAIL() << ex.what(); + } + }); + + std::this_thread::sleep_for(100ms); + + m_tclient = std::thread([this] () { + m_clientTcp.connect(Internet("127.0.0.1", 16000, AF_INET)); + m_clientTcp.send("hello"); + }); +} + +/* -------------------------------------------------------- + * Non-blocking connect + * -------------------------------------------------------- */ + +class NonBlockingConnectTest : public testing::Test { +protected: + SocketTcp m_server{AF_INET, 0}; + SocketTcp m_client{AF_INET, 0}; + + std::thread m_tserver; + std::thread m_tclient; + +public: + NonBlockingConnectTest() + { + m_client.setBlockMode(false); + } + + ~NonBlockingConnectTest() + { + if (m_tserver.joinable()) + m_tserver.join(); + if (m_tclient.joinable()) + m_tclient.join(); + } +}; + +TEST_F(NonBlockingConnectTest, success) +{ + m_server.set(SOL_SOCKET, SO_REUSEADDR, 1); + m_server.bind(Internet("*", 16000, AF_INET)); + m_server.listen(); + + m_tserver = std::thread([this] () { + SocketTcp client = m_server.accept(); + + m_server.close(); + client.close(); + }); + + std::this_thread::sleep_for(100ms); + + m_tclient = std::thread([this] () { + try { + m_client.waitConnect(Internet("127.0.0.1", 16000, AF_INET), 3000); + } catch (const SocketError &error) { + FAIL() << error.what(); + } + + ASSERT_EQ(SocketState::Connected, m_client.state()); + + m_client.close(); + }); +} + +TEST_F(NonBlockingConnectTest, fail) +{ + /* + * /!\ If you find a way to test this locally please tell me /!\ + */ + m_tclient = std::thread([this] () { + try { + m_client.waitConnect(Internet("google.fr", 9000, AF_INET), 100); + + FAIL() << "Expected exception, got success"; + } catch (const SocketError &error) { + ASSERT_EQ(SocketError::Timeout, error.code()); + } + + m_client.close(); + }); +} + +/* -------------------------------------------------------- + * TCP accept + * -------------------------------------------------------- */ + +class TcpAcceptTest : public testing::Test { +protected: + SocketTcp m_server{AF_INET, 0}; + SocketTcp m_client{AF_INET, 0}; + + std::thread m_tserver; + std::thread m_tclient; + +public: + TcpAcceptTest() + { + m_server.set(SOL_SOCKET, SO_REUSEADDR, 1); + m_server.bind(Internet("*", 16000, AF_INET)); + m_server.listen(); + } + + ~TcpAcceptTest() + { + if (m_tserver.joinable()) + m_tserver.join(); + if (m_tclient.joinable()) + m_tclient.join(); + } +}; + +TEST_F(TcpAcceptTest, blockingWaitSuccess) +{ + m_tserver = std::thread([this] () { + try { + m_server.waitAccept(3000).close(); + } catch (const SocketError &error) { + FAIL() << error.what(); + } + + m_server.close(); + }); + + std::this_thread::sleep_for(100ms); + + m_tclient = std::thread([this] () { + m_client.connect(Internet("127.0.0.1", 16000, AF_INET)); + m_client.close(); + }); +} + +TEST_F(TcpAcceptTest, nonBlockingWaitSuccess) +{ + m_tserver = std::thread([this] () { + try { + m_server.setBlockMode(false); + m_server.waitAccept(3000).close(); + } catch (const SocketError &error) { + FAIL() << error.what(); + } + + m_server.close(); + }); + + std::this_thread::sleep_for(100ms); + + m_tclient = std::thread([this] () { + m_client.connect(Internet("127.0.0.1", 16000, AF_INET)); + m_client.close(); + }); +} + +TEST_F(TcpAcceptTest, nonBlockingWaitFail) +{ + // No client, no accept + try { + m_server.setBlockMode(false); + m_server.waitAccept(100).close(); + + FAIL() << "Expected exception, got success"; + } catch (const SocketError &error) { + ASSERT_EQ(SocketError::Timeout, error.code()); + } + + m_server.close(); +} + +/* -------------------------------------------------------- + * TCP recv + * -------------------------------------------------------- */ + +class TcpRecvTest : public testing::Test { +protected: + SocketTcp m_server{AF_INET, 0}; + SocketTcp m_client{AF_INET, 0}; + + std::thread m_tserver; + std::thread m_tclient; + +public: + TcpRecvTest() + { + m_server.set(SOL_SOCKET, SO_REUSEADDR, 1); + m_server.bind(Internet("*", 16000, AF_INET)); + m_server.listen(); + } + + ~TcpRecvTest() + { + if (m_tserver.joinable()) + m_tserver.join(); + if (m_tclient.joinable()) + m_tclient.join(); + } +}; + +TEST_F(TcpRecvTest, blockingSuccess) +{ + m_tserver = std::thread([this] () { + SocketTcp client = m_server.accept(); + + ASSERT_EQ("hello", client.recv(32)); + + client.close(); + m_server.close(); + }); + + std::this_thread::sleep_for(100ms); + + m_tclient = std::thread([this] () { + m_client.connect(Internet("127.0.0.1", 16000, AF_INET)); + m_client.send("hello"); + m_client.close(); + }); +} + +TEST_F(TcpRecvTest, blockingWaitSuccess) +{ + m_tserver = std::thread([this] () { + SocketTcp client = m_server.accept(); + + ASSERT_EQ("hello", client.waitRecv(32, 3000)); + + client.close(); + m_server.close(); + }); + + std::this_thread::sleep_for(100ms); + + m_tclient = std::thread([this] () { + m_client.connect(Internet("127.0.0.1", 16000, AF_INET)); + m_client.send("hello"); + m_client.close(); + }); +} + +TEST_F(TcpRecvTest, nonBlockingWaitSuccess) +{ + m_tserver = std::thread([this] () { + SocketTcp client = m_server.accept(); + + client.setBlockMode(false); + + ASSERT_EQ("hello", client.waitRecv(32, 3000)); + + client.close(); + m_server.close(); + }); + + std::this_thread::sleep_for(100ms); + + m_tclient = std::thread([this] () { + m_client.connect(Internet("127.0.0.1", 16000, AF_INET)); + m_client.send("hello"); + m_client.close(); + }); +} + +/* -------------------------------------------------------- + * Socket SSL + * -------------------------------------------------------- */ + +class SslTest : public testing::Test { +protected: + SocketSsl client{AF_INET, 0}; +}; + +TEST_F(SslTest, connect) +{ + try { + client.connect(Internet("google.fr", 443, AF_INET)); + client.close(); + } catch (const SocketError &error) { + FAIL() << error.what(); + } +} + +TEST_F(SslTest, recv) +{ + try { + client.connect(Internet("google.fr", 443, AF_INET)); + client.send("GET / HTTP/1.0\r\n\r\n"); + + std::string msg = client.recv(512); + std::string content = msg.substr(0, 18); + + ASSERT_EQ("HTTP/1.0 302 Found", content); + + client.close(); + } catch (const SocketError &error) { + FAIL() << error.what(); + } +} + +int main(int argc, char **argv) +{ + testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/C++/tests/Treenode/main.cpp Sun Mar 08 14:26:33 2015 +0100 @@ -0,0 +1,1561 @@ +/* + * main.cpp -- main test file for TreeNode + * + * Copyright (c) 2013, 2014 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <chrono> +#include <iostream> +#include <iterator> +#include <string> + +#include <gtest/gtest.h> + +#include <TreeNode.h> + +class Object final : public TreeNode<Object> { +private: + std::string m_name; + +public: + Object(std::string name) + : TreeNode() + { + m_name = std::move(name); + } + + const std::string &name() const + { + return m_name; + } + + friend bool operator==(const Object &o1, const Object &o2); +}; + +bool operator==(const Object &o1, const Object &o2) +{ + return o1.name() == o2.name(); +} + +/* + * Random trees are created and then we test each node with the following preferred order: + * + * ASSERT_(TRUE|FALSE) isRoot() + * ASSERT_(TRUE|FALSE) isLeaf() + * ASSERT_EQ countChildren() + * ASSERT_EQ() name() + * ASSERT_TRUE & addresses + */ + +/* -------------------------------------------------------- + * Basic construction and insertion + * -------------------------------------------------------- */ + +/* + * @root + */ +TEST(Basic, construct) +{ + Object object("root"); + + ASSERT_TRUE(object.isRoot()); + ASSERT_TRUE(object.isLeaf()); + ASSERT_EQ(0, static_cast<int>(object.countChildren())); + ASSERT_EQ("root", object.name()); +} + +/* + * @root -> append move -> @root + * | + * @a + */ +TEST(Insertion, simpleAppendMove) +{ + Object object("root"); + Object a("a"); + + object.append(std::move(a)); + + // test root + ASSERT_TRUE(object.isRoot()); + ASSERT_FALSE(object.isLeaf()); + ASSERT_EQ(1, static_cast<int>(object.countChildren())); + ASSERT_EQ("root", object.name()); + + // test moved a + ASSERT_FALSE(object[0].isRoot()); + ASSERT_TRUE(object[0].isLeaf()); + ASSERT_EQ("a", object[0].name()); + ASSERT_TRUE(&object == &object[0].parent()); + + // test original a + ASSERT_TRUE(a.isRoot()); + ASSERT_TRUE(a.isLeaf()); + ASSERT_EQ(0, static_cast<int>(a.countChildren())); +} + +/* + * @root -> append copy -> @root + * | + * @a + */ +TEST(Insertion, simpleAppendCopy) +{ + Object object("root"); + Object copy("b"); + + object.append(copy); + + // test root + ASSERT_TRUE(object.isRoot()); + ASSERT_FALSE(object.isLeaf()); + ASSERT_EQ(1, static_cast<int>(object.countChildren())); + ASSERT_EQ("root", object.name()); + + // test copied b + ASSERT_FALSE(object[0].isRoot()); + ASSERT_TRUE(object[0].isLeaf()); + ASSERT_EQ("b", object[0].name()); + ASSERT_TRUE(&object == &object[0].parent()); + + // test original b + ASSERT_TRUE(copy.isRoot()); + ASSERT_TRUE(copy.isLeaf()); + ASSERT_EQ(0, static_cast<int>(copy.countChildren())); + ASSERT_EQ("b", copy.name()); +} + +/* + * @root -> append move -> @root + * / \ + * 1@ @2 + */ +TEST(Insertion, doubleAppendMove) +{ + Object object("root"); + Object o1("1"); + Object o2("2"); + + object.append(std::move(o1)); + object.append(std::move(o2)); + + // test root + ASSERT_TRUE(object.isRoot()); + ASSERT_FALSE(object.isLeaf()); + ASSERT_EQ(2, static_cast<int>(object.countChildren())); + ASSERT_EQ("root", object.name()); + + // test moved 1 + ASSERT_FALSE(object[0].isRoot()); + ASSERT_TRUE(object[0].isLeaf()); + ASSERT_EQ(0, static_cast<int>(object[0].countChildren())); + ASSERT_EQ("1", object[0].name()); + ASSERT_TRUE(&object == &object[0].parent()); + + // test moved 2 + ASSERT_FALSE(object[1].isRoot()); + ASSERT_TRUE(object[1].isLeaf()); + ASSERT_EQ("2", object[1].name()); + ASSERT_EQ(0, static_cast<int>(object[1].countChildren())); + ASSERT_TRUE(&object == &object[1].parent()); + + // test original 1 + ASSERT_TRUE(o1.isRoot()); + ASSERT_TRUE(o1.isLeaf()); + ASSERT_EQ(0, static_cast<int>(o1.countChildren())); + + // test original 2 + ASSERT_TRUE(o2.isRoot()); + ASSERT_TRUE(o2.isLeaf()); + ASSERT_EQ(0, static_cast<int>(o2.countChildren())); +} + +/* + * @root -> append copy -> @root + * / \ + * 1@ @2 + */ +TEST(Insertion, doubleAppendCopy) +{ + Object object("root"); + Object o1("1"); + Object o2("2"); + + object.append(o1); + object.append(o2); + + // test root + ASSERT_TRUE(object.isRoot()); + ASSERT_FALSE(object.isLeaf()); + ASSERT_EQ(2, static_cast<int>(object.countChildren())); + ASSERT_EQ("root", object.name()); + + // test copied 1 + ASSERT_FALSE(object[0].isRoot()); + ASSERT_TRUE(object[0].isLeaf()); + ASSERT_EQ(0, static_cast<int>(object[0].countChildren())); + ASSERT_EQ("1", object[0].name()); + ASSERT_TRUE(&object == &object[0].parent()); + + // test copied 2 + ASSERT_FALSE(object[1].isRoot()); + ASSERT_TRUE(object[1].isLeaf()); + ASSERT_EQ(0, static_cast<int>(object[1].countChildren())); + ASSERT_EQ("2", object[1].name()); + ASSERT_TRUE(&object == &object[1].parent()); + + // test original 1 + ASSERT_TRUE(o1.isRoot()); + ASSERT_TRUE(o1.isLeaf()); + ASSERT_EQ(0, static_cast<int>(o1.countChildren())); + ASSERT_EQ("1", o1.name()); + + // test original 2 + ASSERT_TRUE(o2.isRoot()); + ASSERT_TRUE(o2.isLeaf()); + ASSERT_EQ(0, static_cast<int>(o2.countChildren())); + ASSERT_EQ("2", o2.name()); +} + +/* + * @root -> push move -> @root + * | + * @a + */ +TEST(Insertion, simplePushMove) +{ + Object object("root"); + Object a("a"); + + object.push(std::move(a)); + + // test root + ASSERT_TRUE(object.isRoot()); + ASSERT_FALSE(object.isLeaf()); + ASSERT_EQ(1, static_cast<int>(object.countChildren())); + ASSERT_EQ("a", object[0].name()); + + // test moved a + ASSERT_FALSE(object[0].isRoot()); + ASSERT_TRUE(object[0].isLeaf()); + ASSERT_EQ(0, static_cast<int>(object[0].countChildren())); + ASSERT_EQ("a", object[0].name()); + ASSERT_TRUE(&object == &object[0].parent()); + + // test original a + ASSERT_TRUE(a.isRoot()); + ASSERT_TRUE(a.isLeaf()); + ASSERT_EQ(0, static_cast<int>(a.countChildren())); +} + +/* + * @root -> push copy -> @root + * | + * @a + */ +TEST(Insertion, simplePushCopy) +{ + Object object("root"); + Object copy("a"); + + object.push(copy); + + // test root + ASSERT_TRUE(object.isRoot()); + ASSERT_FALSE(object.isLeaf()); + ASSERT_EQ(1, static_cast<int>(object.countChildren())); + ASSERT_EQ("a", object[0].name()); + + // test copied a + ASSERT_FALSE(object[0].isRoot()); + ASSERT_TRUE(object[0].isLeaf()); + ASSERT_EQ(0, static_cast<int>(object[0].countChildren())); + ASSERT_EQ("a", object[0].name()); + ASSERT_TRUE(&object == &object[0].parent()); + + // test original b + ASSERT_TRUE(copy.isRoot()); + ASSERT_TRUE(copy.isLeaf()); + ASSERT_EQ(0, static_cast<int>(copy.countChildren())); + ASSERT_EQ("a", copy.name()); +} + +/* + * @root -> push move -> @root + * / \ + * 2@ @1 + */ +TEST(Insertion, doublePushMove) +{ + Object object("root"); + Object o1("1"); + Object o2("2"); + + object.push(std::move(o1)); + object.push(std::move(o2)); + + // test root + ASSERT_TRUE(object.isRoot()); + ASSERT_FALSE(object.isLeaf()); + ASSERT_EQ(2, static_cast<int>(object.countChildren())); + ASSERT_EQ("root", object.name()); + + // test moved 2 + ASSERT_FALSE(object[0].isRoot()); + ASSERT_TRUE(object[0].isLeaf()); + ASSERT_EQ(0, static_cast<int>(object[0].countChildren())); + ASSERT_EQ("2", object[0].name()); + ASSERT_TRUE(&object == &object[0].parent()); + + // test moved 1 + ASSERT_FALSE(object[1].isRoot()); + ASSERT_TRUE(object[1].isLeaf()); + ASSERT_EQ("1", object[1].name()); + ASSERT_EQ(0, static_cast<int>(object[1].countChildren())); + ASSERT_TRUE(&object == &object[1].parent()); + + // test original 1 + ASSERT_TRUE(o1.isRoot()); + ASSERT_TRUE(o1.isLeaf()); + ASSERT_EQ(0, static_cast<int>(o1.countChildren())); + + // test original 2 + ASSERT_TRUE(o2.isRoot()); + ASSERT_TRUE(o2.isLeaf()); + ASSERT_EQ(0, static_cast<int>(o2.countChildren())); +} + +/* + * @root -> push copy -> @root + * / \ + * 2@ @1 + */ +TEST(Insertion, doublePushCopy) +{ + Object object("root"); + Object o1("1"); + Object o2("2"); + + object.push(o1); + object.push(o2); + + // test root + ASSERT_TRUE(object.isRoot()); + ASSERT_FALSE(object.isLeaf()); + ASSERT_EQ(2, static_cast<int>(object.countChildren())); + ASSERT_EQ("root", object.name()); + + // test copied 2 + ASSERT_FALSE(object[0].isRoot()); + ASSERT_TRUE(object[0].isLeaf()); + ASSERT_EQ(0, static_cast<int>(object[0].countChildren())); + ASSERT_EQ("2", object[0].name()); + ASSERT_TRUE(&object == &object[0].parent()); + + // test copied 1 + ASSERT_FALSE(object[1].isRoot()); + ASSERT_TRUE(object[1].isLeaf()); + ASSERT_EQ(0, static_cast<int>(object[1].countChildren())); + ASSERT_EQ("1", object[1].name()); + ASSERT_TRUE(&object == &object[1].parent()); + + // test original 1 + ASSERT_TRUE(o1.isRoot()); + ASSERT_TRUE(o1.isLeaf()); + ASSERT_EQ(0, static_cast<int>(o1.countChildren())); + ASSERT_EQ("1", o1.name()); + + // test original 2 + ASSERT_TRUE(o2.isRoot()); + ASSERT_TRUE(o2.isLeaf()); + ASSERT_EQ(0, static_cast<int>(o2.countChildren())); + ASSERT_EQ("2", o2.name()); +} + +/* -------------------------------------------------------- + * Sub insertion + * -------------------------------------------------------- */ + +/* + * @root + * / \ + * / \ + * a@ @b + * / \ / \ + * c@ d@ @e @f + */ +TEST(SubInsert, append) +{ + Object object("root"); + + object.append(Object("a")); + object.append(Object("b")); + + object[0].append(Object("c")); + object[0].append(Object("d")); + object[1].append(Object("e")); + object[1].append(Object("f")); + + // test root + ASSERT_TRUE(object.isRoot()); + ASSERT_FALSE(object.isLeaf()); + ASSERT_EQ(2, static_cast<int>(object.countChildren())); + ASSERT_EQ("root", object.name()); + + // test a + ASSERT_FALSE(object[0].isRoot()); + ASSERT_FALSE(object[0].isLeaf()); + ASSERT_EQ(2, static_cast<int>(object[0].countChildren())); + ASSERT_EQ("a", object[0].name()); + ASSERT_TRUE(&object == &object[0].parent()); + + // test b + ASSERT_FALSE(object[1].isRoot()); + ASSERT_FALSE(object[1].isLeaf()); + ASSERT_EQ(2, static_cast<int>(object[1].countChildren())); + ASSERT_EQ("b", object[1].name()); + ASSERT_TRUE(&object == &object[1].parent()); + + // test c + ASSERT_FALSE(object[0][0].isRoot()); + ASSERT_TRUE(object[0][0].isLeaf()); + ASSERT_EQ(0, static_cast<int>(object[0][0].countChildren())); + ASSERT_EQ("c", object[0][0].name()); + ASSERT_TRUE(&object[0] == &object[0][0].parent()); + + // test d + ASSERT_FALSE(object[0][1].isRoot()); + ASSERT_TRUE(object[0][1].isLeaf()); + ASSERT_EQ(0, static_cast<int>(object[0][1].countChildren())); + ASSERT_EQ("d", object[0][1].name()); + ASSERT_TRUE(&object[0] == &object[0][1].parent()); + + // test e + ASSERT_FALSE(object[1][0].isRoot()); + ASSERT_TRUE(object[1][0].isLeaf()); + ASSERT_EQ(0, static_cast<int>(object[1][0].countChildren())); + ASSERT_EQ("e", object[1][0].name()); + ASSERT_TRUE(&object[1] == &object[1][0].parent()); + + // test f + ASSERT_FALSE(object[1][1].isRoot()); + ASSERT_TRUE(object[1][1].isLeaf()); + ASSERT_EQ(0, static_cast<int>(object[1][1].countChildren())); + ASSERT_EQ("f", object[1][1].name()); + ASSERT_TRUE(&object[1] == &object[1][1].parent()); +} + +/* + * @root + * / \ + * / \ + * b@ @a + * / \ / \ + * d@ c@ @f @e + */ +TEST(SubInsert, push) +{ + Object object("root"); + + object.push(Object("a")); + object.push(Object("b")); + + object[0].push(Object("c")); + object[0].push(Object("d")); + object[1].push(Object("e")); + object[1].push(Object("f")); + + // test root + ASSERT_TRUE(object.isRoot()); + ASSERT_FALSE(object.isLeaf()); + ASSERT_EQ(2, static_cast<int>(object.countChildren())); + ASSERT_EQ("root", object.name()); + + // test b + ASSERT_FALSE(object[0].isRoot()); + ASSERT_FALSE(object[0].isLeaf()); + ASSERT_EQ(2, static_cast<int>(object[0].countChildren())); + ASSERT_EQ("b", object[0].name()); + ASSERT_TRUE(&object == &object[0].parent()); + + // test a + ASSERT_FALSE(object[1].isRoot()); + ASSERT_FALSE(object[1].isLeaf()); + ASSERT_EQ(2, static_cast<int>(object[1].countChildren())); + ASSERT_EQ("a", object[1].name()); + ASSERT_TRUE(&object == &object[1].parent()); + + // test d + ASSERT_FALSE(object[0][0].isRoot()); + ASSERT_TRUE(object[0][0].isLeaf()); + ASSERT_EQ(0, static_cast<int>(object[0][0].countChildren())); + ASSERT_EQ("d", object[0][0].name()); + ASSERT_TRUE(&object[0] == &object[0][0].parent()); + + // test c + ASSERT_FALSE(object[0][1].isRoot()); + ASSERT_TRUE(object[0][1].isLeaf()); + ASSERT_EQ(0, static_cast<int>(object[0][1].countChildren())); + ASSERT_EQ("c", object[0][1].name()); + ASSERT_TRUE(&object[0] == &object[0][1].parent()); + + // test f + ASSERT_FALSE(object[1][0].isRoot()); + ASSERT_TRUE(object[1][0].isLeaf()); + ASSERT_EQ(0, static_cast<int>(object[1][0].countChildren())); + ASSERT_EQ("f", object[1][0].name()); + ASSERT_TRUE(&object[1] == &object[1][0].parent()); + + // test e + ASSERT_FALSE(object[1][1].isRoot()); + ASSERT_TRUE(object[1][1].isLeaf()); + ASSERT_EQ(0, static_cast<int>(object[1][1].countChildren())); + ASSERT_EQ("e", object[1][1].name()); + ASSERT_TRUE(&object[1] == &object[1][1].parent()); +} + +/* -------------------------------------------------------- + * Move constructor + * -------------------------------------------------------- */ + +TEST(MoveConstructor, simple) +{ + Object root("root"); + Object moved(std::move(root)); + + // test root + ASSERT_TRUE(root.isRoot()); + ASSERT_TRUE(root.isLeaf()); + ASSERT_EQ(0, static_cast<int>(root.countChildren())); + + // test moved + ASSERT_TRUE(moved.isRoot()); + ASSERT_TRUE(moved.isLeaf()); + ASSERT_EQ(0, static_cast<int>(moved.countChildren())); + ASSERT_EQ("root", moved.name()); +} + +/* + * @root -> copy + * / \ + * / \ + * a@ @b + */ +TEST(MoveConstructor, oneLevel) +{ + Object root("root"); + + root.append(Object("a")); + root.append(Object("b")); + + Object moved(std::move(root)); + ASSERT_TRUE(moved.isRoot()); + ASSERT_EQ(2, static_cast<int>(moved.countChildren())); + ASSERT_EQ("a", moved[0].name()); + ASSERT_EQ("b", moved[1].name()); + ASSERT_EQ("root", moved[0].parent().name()); + ASSERT_EQ("root", moved[1].parent().name()); + ASSERT_TRUE(&moved == &moved[0].parent()); + ASSERT_TRUE(&moved == &moved[1].parent()); +} + +/* + * @root -> copy + * / \ + * / \ + * a@ @b + * / \ / \ + * c@ d@ @e @f + * + */ +TEST(MoveConstructor, twoLevels) +{ + Object root("root"); + + root.append(Object("a")); + root.append(Object("b")); + + root[0].append(Object("c")); + root[0].append(Object("d")); + root[1].append(Object("e")); + root[1].append(Object("f")); + + Object moved(std::move(root)); + + // test root + ASSERT_TRUE(moved.isRoot()); + ASSERT_EQ(2, static_cast<int>(moved.countChildren())); + + // test a, b + ASSERT_EQ("a", moved[0].name()); + ASSERT_EQ("b", moved[1].name()); + ASSERT_EQ(2, static_cast<int>(moved[0].countChildren())); + ASSERT_EQ(2, static_cast<int>(moved[1].countChildren())); + ASSERT_EQ("root", moved[0].parent().name()); + ASSERT_EQ("root", moved[1].parent().name()); + ASSERT_TRUE(&moved == &moved[0].parent()); + ASSERT_TRUE(&moved == &moved[1].parent()); + + // test c, d + ASSERT_EQ("c", moved[0][0].name()); + ASSERT_EQ("d", moved[0][1].name()); + ASSERT_EQ("a", moved[0][0].parent().name()); + ASSERT_EQ("a", moved[0][1].parent().name()); + ASSERT_TRUE(&moved[0] == &moved[0][0].parent()); + ASSERT_TRUE(&moved[0] == &moved[0][1].parent()); + ASSERT_TRUE(moved[0][0].isLeaf()); + ASSERT_TRUE(moved[0][1].isLeaf()); + ASSERT_FALSE(moved[0][0].isRoot()); + ASSERT_FALSE(moved[0][1].isRoot()); + + // test e, f + ASSERT_EQ("e", moved[1][0].name()); + ASSERT_EQ("f", moved[1][1].name()); + ASSERT_EQ("b", moved[1][0].parent().name()); + ASSERT_EQ("b", moved[1][1].parent().name()); + ASSERT_TRUE(&moved[1] == &moved[1][0].parent()); + ASSERT_TRUE(&moved[1] == &moved[1][1].parent()); + ASSERT_TRUE(moved[1][0].isLeaf()); + ASSERT_TRUE(moved[1][1].isLeaf()); + ASSERT_FALSE(moved[1][0].isRoot()); + ASSERT_FALSE(moved[1][1].isRoot()); +} + +/* -------------------------------------------------------- + * Copy constructor + * -------------------------------------------------------- */ + +TEST(CopyConstructor, simple) +{ + Object root("root"); + Object moved(root); + + // test root + ASSERT_TRUE(root.isRoot()); + ASSERT_TRUE(root.isLeaf()); + ASSERT_EQ(0, static_cast<int>(root.countChildren())); + ASSERT_EQ("root", root.name()); + + // test copy + ASSERT_TRUE(moved.isRoot()); + ASSERT_TRUE(moved.isLeaf()); + ASSERT_EQ(0, static_cast<int>(moved.countChildren())); + ASSERT_EQ("root", moved.name()); +} + +/* + * @root -> copy + * / \ + * / \ + * a@ @b + */ +TEST(CopyConstructor, oneLevel) +{ + Object root("root"); + + root.append(Object("a")); + root.append(Object("b")); + + Object copy(root); + + ASSERT_TRUE(root.isRoot()); + ASSERT_FALSE(root.isLeaf()); + ASSERT_TRUE(copy.isRoot()); + ASSERT_FALSE(copy.isLeaf()); + ASSERT_EQ("root", root.name()); + ASSERT_EQ("root", copy.name()); + ASSERT_EQ("a", root[0].name()); + ASSERT_EQ("b", root[1].name()); + ASSERT_EQ("a", copy[0].name()); + ASSERT_EQ("b", copy[1].name()); + ASSERT_TRUE(&root == &root[0].parent()); + ASSERT_TRUE(&root == &root[1].parent()); + ASSERT_TRUE(© == ©[0].parent()); + ASSERT_TRUE(© == ©[1].parent()); +} + +/* + * @root -> copy + * / \ + * / \ + * a@ @b + * / \ / \ + * c@ d@ @e @f + * + */ +TEST(CopyConstructor, twoLevels) +{ + Object root("root"); + + root.append(Object("a")); + root.append(Object("b")); + + root[0].append(Object("c")); + root[0].append(Object("d")); + root[1].append(Object("e")); + root[1].append(Object("f")); + + Object copy(root); + + ASSERT_FALSE(root.isLeaf()); + ASSERT_FALSE(copy.isLeaf()); + ASSERT_EQ("root", root.name()); + ASSERT_EQ("root", copy.name()); + ASSERT_EQ("a", root[0].name()); + ASSERT_EQ("a", copy[0].name()); + ASSERT_EQ("c", root[0][0].name()); + ASSERT_EQ("c", copy[0][0].name()); + ASSERT_EQ("d", root[0][1].name()); + ASSERT_EQ("d", copy[0][1].name()); + ASSERT_EQ("b", root[1].name()); + ASSERT_EQ("b", copy[1].name()); + ASSERT_EQ("e", root[1][0].name()); + ASSERT_EQ("e", copy[1][0].name()); + ASSERT_EQ("f", root[1][1].name()); + ASSERT_EQ("f", copy[1][1].name()); + ASSERT_TRUE(&root == &root[0].parent()); + ASSERT_TRUE(&root == &root[1].parent()); + ASSERT_TRUE(© == ©[0].parent()); + ASSERT_TRUE(© == ©[1].parent()); + ASSERT_TRUE(&root[0] == &root[0][0].parent()); + ASSERT_TRUE(&root[0] == &root[0][1].parent()); + ASSERT_TRUE(©[0] == ©[0][0].parent()); + ASSERT_TRUE(©[0] == ©[0][1].parent()); + ASSERT_TRUE(&root[1] == &root[1][0].parent()); + ASSERT_TRUE(&root[1] == &root[1][1].parent()); + ASSERT_TRUE(©[1] == ©[1][0].parent()); + ASSERT_TRUE(©[1] == ©[1][1].parent()); +} + +/* -------------------------------------------------------- + * Move extraction + * -------------------------------------------------------- */ + +/* + * @root + * / \ + * / \ + * a@ @b -> move -> @b + */ +TEST(MoveExtraction, simple) +{ + Object root("root"); + + root.append(Object("a")); + root.append(Object("b")); + + Object moved(std::move(root[1])); + + // test root + ASSERT_TRUE(root.isRoot()); + ASSERT_EQ(2, static_cast<int>(root.countChildren())); + ASSERT_EQ("root", root.name()); + + // test a + ASSERT_TRUE(root[0].isLeaf()); + ASSERT_FALSE(root[0].isRoot()); + ASSERT_EQ("a", root[0].name()); + ASSERT_EQ(0, static_cast<int>(root[0].countChildren())); + ASSERT_TRUE(&root == &root[0].parent()); + + // test b + ASSERT_TRUE(root[1].isLeaf()); + ASSERT_FALSE(root[1].isRoot()); + ASSERT_EQ(0, static_cast<int>(root[1].countChildren())); + ASSERT_TRUE(&root == &root[1].parent()); + + // test moved b + ASSERT_TRUE(moved.isRoot()); + ASSERT_TRUE(moved.isLeaf()); + ASSERT_EQ(0, static_cast<int>(moved.countChildren())); + ASSERT_EQ("b", moved.name()); +} + +/* + * @root + * / \ + * / \ + * a@ @b -> move + * / \ / \ + * c@ d@ @e @f + * \ + * @g + */ +TEST(MoveExtraction, bigger) +{ + Object root("root"); + + root.append(Object("a")); + root.append(Object("b")); + + root[0].append(Object("c")); + root[0].append(Object("d")); + root[1].append(Object("e")); + root[1].append(Object("f")); + root[1][1].append(Object("g")); + + Object moved(std::move(root[1])); + + // test root + ASSERT_TRUE(root.isRoot()); + ASSERT_FALSE(root.isLeaf()); + ASSERT_EQ(2, static_cast<int>(root.countChildren())); + ASSERT_EQ("root", root.name()); + + // test a + ASSERT_FALSE(root[0].isRoot()); + ASSERT_FALSE(root[0].isLeaf()); + ASSERT_EQ(2, static_cast<int>(root[0].countChildren())); + ASSERT_EQ("a", root[0].name()); + ASSERT_TRUE(&root == &root[0].parent()); + + // test c + ASSERT_FALSE(root[0][0].isRoot()); + ASSERT_TRUE(root[0][0].isLeaf()); + ASSERT_EQ(0, static_cast<int>(root[0][0].countChildren())); + ASSERT_EQ("c", root[0][0].name()); + ASSERT_TRUE(&root[0] == &root[0][0].parent()); + + // test d + ASSERT_FALSE(root[0][1].isRoot()); + ASSERT_TRUE(root[0][1].isLeaf()); + ASSERT_EQ(0, static_cast<int>(root[0][1].countChildren())); + ASSERT_EQ("d", root[0][1].name()); + ASSERT_TRUE(&root[0] == &root[0][1].parent()); + + // test b + ASSERT_FALSE(root[1].isRoot()); + ASSERT_TRUE(root[1].isLeaf()); + ASSERT_EQ(0, static_cast<int>(root[1].countChildren())); + ASSERT_TRUE(&root == &root[1].parent()); + + // test moved b + ASSERT_TRUE(moved.isRoot()); + ASSERT_FALSE(moved.isLeaf()); + ASSERT_EQ(2, static_cast<int>(moved.countChildren())); + ASSERT_EQ("b", moved.name()); + + // test moved b-e + ASSERT_FALSE(moved[0].isRoot()); + ASSERT_TRUE(moved[0].isLeaf()); + ASSERT_EQ(0, static_cast<int>(moved[0].countChildren())); + ASSERT_EQ("e", moved[0].name()); + ASSERT_TRUE(&moved == &moved[0].parent()); + + // test moved b-f + ASSERT_FALSE(moved[1].isRoot()); + ASSERT_FALSE(moved[1].isLeaf()); + ASSERT_EQ(1, static_cast<int>(moved[1].countChildren())); + ASSERT_EQ("f", moved[1].name()); + ASSERT_TRUE(&moved == &moved[1].parent()); + + // test moved b-g + ASSERT_FALSE(moved[1][0].isRoot()); + ASSERT_TRUE(moved[1][0].isLeaf()); + ASSERT_EQ(0, static_cast<int>(moved[1][0].countChildren())); + ASSERT_EQ("g", moved[1][0].name()); + ASSERT_TRUE(&moved[1] == &moved[1][0].parent()); +} + +/* -------------------------------------------------------- + * Copy extraction + * -------------------------------------------------------- */ + +/* + * @root + * / \ + * / \ + * a@ @b -> copy -> @b + */ +TEST(CopyExtraction, simple) +{ + Object root("root"); + + root.append(Object("a")); + root.append(Object("b")); + + Object copy(root[1]); + + // test root + ASSERT_TRUE(root.isRoot()); + ASSERT_EQ(2, static_cast<int>(root.countChildren())); + ASSERT_EQ("root", root.name()); + + // test a + ASSERT_TRUE(root[0].isLeaf()); + ASSERT_FALSE(root[0].isRoot()); + ASSERT_EQ(0, static_cast<int>(root[0].countChildren())); + ASSERT_EQ("a", root[0].name()); + ASSERT_TRUE(&root == &root[0].parent()); + + // test b + ASSERT_TRUE(root[1].isLeaf()); + ASSERT_FALSE(root[1].isRoot()); + ASSERT_EQ(0, static_cast<int>(root[1].countChildren())); + ASSERT_EQ("b", root[1].name()); + ASSERT_TRUE(&root == &root[1].parent()); + + // test copied b + ASSERT_TRUE(copy.isRoot()); + ASSERT_TRUE(copy.isLeaf()); + ASSERT_EQ(0, static_cast<int>(copy.countChildren())); + ASSERT_EQ("b", copy.name()); +} + +/* + * @root + * / \ + * / \ + * a@ @b -> copy + * / \ / \ + * c@ d@ @e @f + * \ + * @g + */ +TEST(CopyExtraction, bigger) +{ + Object root("root"); + + root.append(Object("a")); + root.append(Object("b")); + + root[0].append(Object("c")); + root[0].append(Object("d")); + root[1].append(Object("e")); + root[1].append(Object("f")); + root[1][1].append(Object("g")); + + Object copy(root[1]); + + // test root + ASSERT_TRUE(root.isRoot()); + ASSERT_FALSE(root.isLeaf()); + ASSERT_EQ(2, static_cast<int>(root.countChildren())); + ASSERT_EQ("root", root.name()); + + // test a + ASSERT_FALSE(root[0].isRoot()); + ASSERT_FALSE(root[0].isLeaf()); + ASSERT_EQ(2, static_cast<int>(root[0].countChildren())); + ASSERT_EQ("a", root[0].name()); + ASSERT_TRUE(&root == &root[0].parent()); + + // test c + ASSERT_FALSE(root[0][0].isRoot()); + ASSERT_TRUE(root[0][0].isLeaf()); + ASSERT_EQ(0, static_cast<int>(root[0][0].countChildren())); + ASSERT_EQ("c", root[0][0].name()); + ASSERT_TRUE(&root[0] == &root[0][0].parent()); + + // test d + ASSERT_FALSE(root[0][1].isRoot()); + ASSERT_TRUE(root[0][1].isLeaf()); + ASSERT_EQ(0, static_cast<int>(root[0][1].countChildren())); + ASSERT_EQ("d", root[0][1].name()); + ASSERT_TRUE(&root[0] == &root[0][1].parent()); + + // test b + ASSERT_FALSE(root[1].isRoot()); + ASSERT_FALSE(root[1].isLeaf()); + ASSERT_EQ(2, static_cast<int>(root[1].countChildren())); + ASSERT_TRUE(&root == &root[1].parent()); + + // test e + ASSERT_FALSE(root[1][0].isRoot()); + ASSERT_TRUE(root[1][0].isLeaf()); + ASSERT_EQ(0, static_cast<int>(root[1][0].countChildren())); + ASSERT_EQ("e", root[1][0].name()); + ASSERT_TRUE(&root[1] == &root[1][0].parent()); + + // test f + ASSERT_FALSE(root[1][1].isRoot()); + ASSERT_FALSE(root[1][1].isLeaf()); + ASSERT_EQ(1, static_cast<int>(root[1][1].countChildren())); + ASSERT_EQ("f", root[1][1].name()); + ASSERT_TRUE(&root[1] == &root[1][1].parent()); + + // test g + ASSERT_FALSE(root[1][1][0].isRoot()); + ASSERT_TRUE(root[1][1][0].isLeaf()); + ASSERT_EQ(0, static_cast<int>(root[1][1][0].countChildren())); + ASSERT_EQ("g", root[1][1][0].name()); + ASSERT_TRUE(&root[1][1] == &root[1][1][0].parent()); + + // test copied b + ASSERT_TRUE(copy.isRoot()); + ASSERT_FALSE(copy.isLeaf()); + ASSERT_EQ(2, static_cast<int>(copy.countChildren())); + ASSERT_EQ("b", copy.name()); + + // test copied b-e + ASSERT_FALSE(copy[0].isRoot()); + ASSERT_TRUE(copy[0].isLeaf()); + ASSERT_EQ(0, static_cast<int>(copy[0].countChildren())); + ASSERT_EQ("e", copy[0].name()); + ASSERT_TRUE(© == ©[0].parent()); + + // test copied b-f + ASSERT_FALSE(copy[1].isRoot()); + ASSERT_FALSE(copy[1].isLeaf()); + ASSERT_EQ(1, static_cast<int>(copy[1].countChildren())); + ASSERT_EQ("f", copy[1].name()); + ASSERT_TRUE(© == ©[1].parent()); + + // test copied b-g + ASSERT_FALSE(copy[1][0].isRoot()); + ASSERT_TRUE(copy[1][0].isLeaf()); + ASSERT_EQ(0, static_cast<int>(copy[1][0].countChildren())); + ASSERT_EQ("g", copy[1][0].name()); + ASSERT_TRUE(©[1] == ©[1][0].parent()); +} + +/* -------------------------------------------------------- + * Move assignment + * -------------------------------------------------------- */ + +/* + * @root -> @moved + */ +TEST(MoveAssignment, simple) +{ + Object root("root"); + Object moved("dummy"); + + moved = std::move(root); + + // test root + ASSERT_TRUE(root.isRoot()); + ASSERT_TRUE(root.isLeaf()); + ASSERT_EQ(0, static_cast<int>(root.countChildren())); + + // test moved + ASSERT_TRUE(moved.isRoot()); + ASSERT_TRUE(moved.isLeaf()); + ASSERT_EQ(0, static_cast<int>(moved.countChildren())); + ASSERT_EQ("root", moved.name()); +} + +/* + * @root + * / \ + * / \ + * a@ @b <- move <- @x + * / \ / \ / \ + * c@ d@ @e @f y@ @z + * \ + * @g + */ +TEST(MoveAssignment, bigger) +{ + Object root("root"); + + root.append(Object("a")); + root.append(Object("b")); + + root[0].append(Object("c")); + root[0].append(Object("d")); + root[1].append(Object("e")); + root[1].append(Object("f")); + root[1][1].append(Object("g")); + + Object moved("x"); + + moved.append(Object("y")); + moved.append(Object("z")); + + root[1] = std::move(moved); + + // test root + ASSERT_TRUE(root.isRoot()); + ASSERT_FALSE(root.isLeaf()); + ASSERT_EQ(2, static_cast<int>(root.countChildren())); + ASSERT_EQ("root", root.name()); + + // test a + ASSERT_FALSE(root[0].isRoot()); + ASSERT_FALSE(root[0].isLeaf()); + ASSERT_EQ(2, static_cast<int>(root[0].countChildren())); + ASSERT_EQ("a", root[0].name()); + ASSERT_TRUE(&root == &root[0].parent()); + + // test c + ASSERT_FALSE(root[0][0].isRoot()); + ASSERT_TRUE(root[0][0].isLeaf()); + ASSERT_EQ(0, static_cast<int>(root[0][0].countChildren())); + ASSERT_EQ("c", root[0][0].name()); + ASSERT_TRUE(&root[0] == &root[0][0].parent()); + + // test copied x to b + ASSERT_FALSE(root[1].isRoot()); + ASSERT_FALSE(root[1].isLeaf()); + ASSERT_EQ(2, static_cast<int>(root[1].countChildren())); + ASSERT_EQ("x", root[1].name()); + ASSERT_TRUE(&root == &root[1].parent()); + + // test y + ASSERT_FALSE(root[1][0].isRoot()); + ASSERT_TRUE(root[1][0].isLeaf()); + ASSERT_EQ(0, static_cast<int>(root[1][0].countChildren())); + ASSERT_EQ("y", root[1][0].name()); + ASSERT_TRUE(&root[1] == &root[1][0].parent()); + + // test z + ASSERT_FALSE(root[1][1].isRoot()); + ASSERT_TRUE(root[1][1].isLeaf()); + ASSERT_EQ(0, static_cast<int>(root[1][1].countChildren())); + ASSERT_EQ("z", root[1][1].name()); + ASSERT_TRUE(&root[1] == &root[1][1].parent()); + + // test moved + ASSERT_TRUE(moved.isRoot()); + ASSERT_TRUE(moved.isLeaf()); + ASSERT_EQ(0, static_cast<int>(moved.countChildren())); +} + +/* -------------------------------------------------------- + * Copy assignment + * -------------------------------------------------------- */ + +/* + * @root -> @copy + */ +TEST(CopyAssignment, simple) +{ + Object root("root"); + Object copy("copy"); + + copy = root; + + // test root + ASSERT_TRUE(root.isRoot()); + ASSERT_TRUE(root.isLeaf()); + ASSERT_EQ(0, static_cast<int>(root.countChildren())); + ASSERT_EQ("root", root.name()); + + // test copied + ASSERT_TRUE(copy.isRoot()); + ASSERT_TRUE(copy.isLeaf()); + ASSERT_EQ(0, static_cast<int>(copy.countChildren())); + ASSERT_EQ("root", copy.name()); +} + +/* + * @root + * / \ + * / \ + * a@ @b <- copy <- @x + * / \ / \ / \ + * c@ d@ @e @f y@ @z + * \ + * @g + */ +TEST(CopyAssignment, bigger) +{ + Object root("root"); + + root.append(Object("a")); + root.append(Object("b")); + + root[0].append(Object("c")); + root[0].append(Object("d")); + root[1].append(Object("e")); + root[1].append(Object("f")); + root[1][1].append(Object("g")); + + Object copy("x"); + + copy.append(Object("y")); + copy.append(Object("z")); + + root[1] = copy; + + // test root + ASSERT_TRUE(root.isRoot()); + ASSERT_FALSE(root.isLeaf()); + ASSERT_EQ(2, static_cast<int>(root.countChildren())); + ASSERT_EQ("root", root.name()); + + // test a + ASSERT_FALSE(root[0].isRoot()); + ASSERT_FALSE(root[0].isLeaf()); + ASSERT_EQ(2, static_cast<int>(root[0].countChildren())); + ASSERT_EQ("a", root[0].name()); + ASSERT_TRUE(&root == &root[0].parent()); + + // test c + ASSERT_FALSE(root[0][0].isRoot()); + ASSERT_TRUE(root[0][0].isLeaf()); + ASSERT_EQ(0, static_cast<int>(root[0][0].countChildren())); + ASSERT_EQ("c", root[0][0].name()); + ASSERT_TRUE(&root[0] == &root[0][0].parent()); + + // test copied x to b + ASSERT_FALSE(root[1].isRoot()); + ASSERT_FALSE(root[1].isLeaf()); + ASSERT_EQ(2, static_cast<int>(root[1].countChildren())); + ASSERT_EQ("x", root[1].name()); + ASSERT_TRUE(&root == &root[1].parent()); + + // test y + ASSERT_FALSE(root[1][0].isRoot()); + ASSERT_TRUE(root[1][0].isLeaf()); + ASSERT_EQ(0, static_cast<int>(root[1][0].countChildren())); + ASSERT_EQ("y", root[1][0].name()); + ASSERT_TRUE(&root[1] == &root[1][0].parent()); + + // test z + ASSERT_FALSE(root[1][1].isRoot()); + ASSERT_TRUE(root[1][1].isLeaf()); + ASSERT_EQ(0, static_cast<int>(root[1][1].countChildren())); + ASSERT_EQ("z", root[1][1].name()); + ASSERT_TRUE(&root[1] == &root[1][1].parent()); + + // test original x + ASSERT_TRUE(copy.isRoot()); + ASSERT_FALSE(copy.isLeaf()); + ASSERT_EQ(2, static_cast<int>(copy.countChildren())); + ASSERT_EQ("x", copy.name()); + + // test original y + ASSERT_FALSE(copy[0].isRoot()); + ASSERT_TRUE(copy[0].isLeaf()); + ASSERT_EQ(0, static_cast<int>(copy[0].countChildren())); + ASSERT_EQ("y", copy[0].name()); + ASSERT_TRUE(© == ©[0].parent()); + + // test original z + ASSERT_FALSE(copy[1].isRoot()); + ASSERT_TRUE(copy[1].isLeaf()); + ASSERT_EQ(0, static_cast<int>(copy[1].countChildren())); + ASSERT_EQ("z", copy[1].name()); + ASSERT_TRUE(© == ©[1].parent()); +} + +/* -------------------------------------------------------- + * Remove functions + * -------------------------------------------------------- */ + +TEST(Remove, clear) +{ + Object root("root"); + + root.append(Object("a")); + root.append(Object("b")); + root.append(Object("c")); + root.clear(); + + ASSERT_EQ(0, static_cast<int>(root.countChildren())); +} + +TEST(Remove, index) +{ + Object root("root"); + + root.append(Object("a")); + root.append(Object("b")); + root.append(Object("c")); + root.remove(2); + + ASSERT_EQ(2, static_cast<int>(root.countChildren())); + ASSERT_EQ("a", root[0].name()); + ASSERT_EQ("b", root[1].name()); +} + +TEST(Remove, ref) +{ + Object root("root"); + + root.append(Object("a")); + root.append(Object("b")); + root.append(Object("c")); + root.remove(root[2]); + + ASSERT_EQ(2, static_cast<int>(root.countChildren())); + ASSERT_EQ("a", root[0].name()); + ASSERT_EQ("b", root[1].name()); +} + +TEST(Remove, same) +{ + Object root("root"); + + root.append(Object("a")); + root.append(Object("b")); + root.append(Object("c")); + root.removeSame(Object("c")); + + ASSERT_EQ(2, static_cast<int>(root.countChildren())); + ASSERT_EQ("a", root[0].name()); + ASSERT_EQ("b", root[1].name()); +} + +/* -------------------------------------------------------- + * Miscellaneous + * -------------------------------------------------------- */ + +TEST(Misc, indexOf) +{ + Object root("root"); + + root.append(Object("a")); + root.append(Object("b")); + root.append(Object("c")); + + ASSERT_EQ(1, root.indexOf(root[1])); +} + +TEST(Misc, indexOfFail) +{ + Object root("root"); + Object notin("c"); + + root.append(Object("a")); + root.append(Object("b")); + root.append(Object("c")); + + ASSERT_EQ(-1, root.indexOf(notin)); +} + +TEST(Misc, indexOfSame) +{ + Object root("root"); + Object same("a"); + + root.append(Object("a")); + root.append(Object("b")); + root.append(Object("c")); + + ASSERT_EQ(0, root.indexOfSame(same)); +} + +TEST(Misc, indexOfSameFail) +{ + Object root("root"); + Object same("xyz"); + + root.append(Object("a")); + root.append(Object("b")); + root.append(Object("c")); + + ASSERT_EQ(-1, root.indexOfSame(same)); +} + +TEST(Misc, inplaceFront) +{ + Object root("root"); + + root.pushNew("a"); + root.pushNew("b"); + + ASSERT_TRUE(root.isRoot()); + ASSERT_FALSE(root.isLeaf()); + ASSERT_EQ(2, static_cast<int>(root.countChildren())); + ASSERT_EQ("b", root[0].name()); + ASSERT_EQ("a", root[1].name()); +} + +TEST(Misc, inplaceBack) +{ + Object root("root"); + + root.appendNew("a"); + root.appendNew("b"); + + ASSERT_TRUE(root.isRoot()); + ASSERT_FALSE(root.isLeaf()); + ASSERT_EQ(2, static_cast<int>(root.countChildren())); + ASSERT_EQ("a", root[0].name()); + ASSERT_EQ("b", root[1].name()); +} + +TEST(Misc, map) +{ + Object root("root"); + std::vector<Object> list; + + root.appendNew("a"); + root.appendNew("b"); + + root[0].appendNew("c"); + + root.map([&] (const auto &o) { + list.push_back(o); + }); + + ASSERT_EQ(4, static_cast<int>(list.size())); + ASSERT_EQ("root", list[0].name()); + ASSERT_EQ("a", list[1].name()); + ASSERT_EQ("c", list[2].name()); + ASSERT_EQ("b", list[3].name()); +} + +TEST(Misc, flat) +{ + Object root("root"); + std::vector<Object> list; + + root.appendNew("a"); + root.appendNew("b"); + + root[0].appendNew("c"); + root.flat(std::back_inserter(list)); + + ASSERT_EQ(4, static_cast<int>(list.size())); + ASSERT_EQ("root", list[0].name()); + ASSERT_EQ("a", list[1].name()); + ASSERT_EQ("c", list[2].name()); + ASSERT_EQ("b", list[3].name()); +} + +/* -------------------------------------------------------- + * Search + * -------------------------------------------------------- */ + +TEST(Search, returnsValue) +{ + Object root("root"); + + root.appendNew("a"); + root.appendNew("b"); + + root[0].appendNew("c"); + + auto dummy = [] (Object &) {}; + + ASSERT_TRUE(root.search([] (const auto &v) { return v.name() == "a"; }, dummy)); + ASSERT_TRUE(root.search([] (const auto &v) { return v.name() == "b"; }, dummy)); + ASSERT_TRUE(root.search([] (const auto &v) { return v.name() == "c"; }, dummy)); + ASSERT_FALSE(root.search([] (const auto &v) { return v.name() == "notavail"; }, dummy)); +} + +TEST(Search, returns) +{ + Object root("root"); + + root.appendNew("a"); + root.appendNew("b"); + + root[0].appendNew("c"); + + try { + auto &a = root.search([] (const auto &o) { + return o.name() == "a"; + }); + auto &b = root.search([] (const auto &o) { + return o.name() == "b"; + }); + auto &c = root.search([] (const auto &o) { + return o.name() == "c"; + }); + + ASSERT_EQ("a", a.name()); + ASSERT_EQ("b", b.name()); + ASSERT_EQ("c", c.name()); + + ASSERT_TRUE(&a == &root[0]); + ASSERT_TRUE(&b == &root[1]); + ASSERT_TRUE(&c == &root[0][0]); + } catch (const std::exception &ex) { + FAIL() << ex.what(); + } +} + +TEST(Search, returnsFirst) +{ + Object root("root"); + + root.appendNew("a"); + root.appendNew("a"); + root.appendNew("a"); + + try { + auto &value = root.search([] (const auto &v) { + return v.name() == "a"; + }); + + ASSERT_EQ("a", value.name()); + ASSERT_TRUE(&value == &root[0]); + } catch (const std::exception &ex) { + FAIL() << ex.what(); + } +} + +/* -------------------------------------------------------- + * Test inheritance + * -------------------------------------------------------- */ + +class Animal : public TreeNode<Animal> { +public: + virtual ~Animal() = default; + virtual std::string noise() const + { + return "standard"; + } +}; + +class Cat final : public Animal { +public: + std::string noise() const override + { + return "miaou"; + } +}; + +class Dog final : public Animal { +public: + std::string noise() const override + { + return "waouf"; + } +}; + +TEST(Inheritance, basic) +{ + Animal root; + + root.append(Animal()); + root.append(Cat()); + root.append(Dog()); + + ASSERT_EQ("standard", root[0].noise()); + ASSERT_EQ("miaou", root[1].noise()); + ASSERT_EQ("waouf", root[2].noise()); +} + +TEST(Inheritance, copy) +{ + Animal root; + + root.append(Animal()); + root.append(Cat()); + root.append(Dog()); + + ASSERT_EQ("standard", root[0].noise()); + ASSERT_EQ("miaou", root[1].noise()); + ASSERT_EQ("waouf", root[2].noise()); + + Animal copy(root); + + ASSERT_EQ("standard", copy[0].noise()); + ASSERT_EQ("miaou", copy[1].noise()); + ASSERT_EQ("waouf", copy[2].noise()); +} + +int main(int argc, char **argv) +{ + testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/C++/tests/Utf8/main.cpp Sun Mar 08 14:26:33 2015 +0100 @@ -0,0 +1,327 @@ +/* + * main.cpp -- main test file for Utf8 + * + * Copyright (c) 2013, 2014 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * /!\ Be sure to keep this file with UTF-8 encoding /!\ + */ + +#include <gtest/gtest.h> + +#include <Utf8.h> + +using namespace testing; + +/* -------------------------------------------------------- + * Conversion UTF32 -> UTF8 + * -------------------------------------------------------- */ + +TEST(Conversion32to8, ascii) +{ + try { + std::u32string u32{'a', 'b', 'c'}; + std::string s = Utf8::toutf8(u32); + + ASSERT_EQ("abc", s); + } catch (const std::exception &ex) { + FAIL() << ex.what(); + } +} + +TEST(Conversion32to8, valid) +{ + try { + std::u32string u32{'a', /* é */ 233, 'c'}; + std::string s = Utf8::toutf8(u32); + + ASSERT_EQ("aéc", s); + } catch (const std::exception &ex) { + FAIL() << ex.what(); + } +} + +TEST(Conversion32to8, invalid) +{ + try { + std::u32string u32{'a', 0xFFFFFFFF, 'c'}; + std::string s = Utf8::toutf8(u32); + + FAIL() << "expected a failure"; + } catch (const std::exception &ex) { + SUCCEED(); + } +} + +/* -------------------------------------------------------- + * Conversion UTF8 -> UTF32 + * -------------------------------------------------------- */ + +TEST(Conversion8to32, ascii) +{ + try { + std::string s{"abc"}; + std::u32string expected{'a', 'b', 'c'}; + std::u32string result = Utf8::toutf32(s); + + ASSERT_EQ(expected, result); + } catch (const std::exception &ex) { + FAIL() << ex.what(); + } +} + +TEST(Conversion8to32, valid) +{ + try { + std::string s{"aéc"}; + std::u32string expected{'a', /* é */ 233, 'c'}; + std::u32string result = Utf8::toutf32(s); + + ASSERT_EQ(expected, result); + } catch (const std::exception &ex) { + FAIL() << ex.what(); + } +} + +/* -------------------------------------------------------- + * UTF32 to upper + * -------------------------------------------------------- */ + +TEST(Toupper32, ascii) +{ + try { + std::u32string u32{'a', 'b', 'c'}; + std::u32string expected{'A', 'B', 'C'}; + std::u32string result = Utf8::toupper(u32); + + ASSERT_EQ(expected, result); + } catch (const std::exception &ex) { + FAIL() << ex.what(); + } +} + +TEST(Toupper32, valid) +{ + try { + std::u32string u32{/* ä */ 228, /* ç */ 231, /* ë */ 235}; + std::u32string expected{/* Ä */ 196, /* Ç */ 199, /* Ë */ 203}; + std::u32string result = Utf8::toupper(u32); + + ASSERT_EQ(expected, result); + } catch (const std::exception &ex) { + FAIL() << ex.what(); + } +} + +TEST(Toupper32, invalid) +{ + try { + std::u32string u32{'a', 0xFFFFFFFF, 'b'}; + std::u32string expected{'A', 0xFFFFFFFF, 'B'}; + std::u32string result = Utf8::toupper(u32); + + ASSERT_EQ(expected, result); + } catch (const std::exception &ex) { + FAIL() << ex.what(); + } +} + +/* -------------------------------------------------------- + * UTF32 to lower + * -------------------------------------------------------- */ + +TEST(Tolower32, ascii) +{ + try { + std::u32string u32{'A', 'B', 'C'}; + std::u32string expected{'a', 'b', 'c'}; + std::u32string result = Utf8::tolower(u32); + + ASSERT_EQ(expected, result); + } catch (const std::exception &ex) { + FAIL() << ex.what(); + } +} + +TEST(Tolower32, valid) +{ + try { + std::u32string u32{/* Ä */ 196, /* Ç */ 199, /* Ë */ 203}; + std::u32string expected{/* ä */ 228, /* ç */ 231, /* ë */ 235}; + std::u32string result = Utf8::tolower(u32); + + ASSERT_EQ(expected, result); + } catch (const std::exception &ex) { + FAIL() << ex.what(); + } +} + +TEST(Tolower32, invalid) +{ + try { + std::u32string u32{'A', 0xFFFFFFFF, 'B'}; + std::u32string expected{'a', 0xFFFFFFFF, 'b'}; + std::u32string result = Utf8::tolower(u32); + + ASSERT_EQ(expected, result); + } catch (const std::exception &ex) { + FAIL() << ex.what(); + } +} + +/* -------------------------------------------------------- + * UTF8 to upper + * -------------------------------------------------------- */ + +TEST(Toupper8, ascii) +{ + try { + std::string s{"abc"}; + std::string r = Utf8::toupper(s); + + ASSERT_EQ("ABC", r); + } catch (const std::exception &ex) { + FAIL() << ex.what(); + } +} + +TEST(Toupper8, valid) +{ + try { + std::string s{"aéc"}; + std::string r = Utf8::toupper(s); + + ASSERT_EQ("AÉC", r); + } catch (const std::exception &ex) { + FAIL() << ex.what(); + } +} + +TEST(Toupper8, invalid) +{ + try { + std::string s{"a" "\xFF""b"}; + std::string r = Utf8::toupper(s); + + FAIL() << "expected a failure"; + } catch (const std::exception &ex) { + SUCCEED(); + } +} + +/* -------------------------------------------------------- + * UTF8 to lower + * -------------------------------------------------------- */ + +TEST(Tolower8, ascii) +{ + try { + std::string s{"ABC"}; + std::string r = Utf8::tolower(s); + + ASSERT_EQ("abc", r); + } catch (const std::exception &ex) { + FAIL() << ex.what(); + } +} + +TEST(Tolower8, valid) +{ + try { + std::string s{"AÉC"}; + std::string r = Utf8::tolower(s); + + ASSERT_EQ("aéc", r); + } catch (const std::exception &ex) { + FAIL() << ex.what(); + } +} + +TEST(Tolower8, invalid) +{ + try { + std::string s{"A" "\xFF""B"}; + std::string r = Utf8::tolower(s); + + FAIL() << "expected a failure"; + } catch (const std::exception &ex) { + SUCCEED(); + } +} + +/* -------------------------------------------------------- + * Check functions + * -------------------------------------------------------- */ + +TEST(Check, isspace) +{ + ASSERT_TRUE(Utf8::isspace(' ')); + ASSERT_FALSE(Utf8::isspace(/* é */ 233)); +} + +TEST(Check, isletter) +{ + ASSERT_TRUE(Utf8::isletter(/* é */ 233)); + ASSERT_FALSE(Utf8::isletter(/* € */ 8364)); +} + +TEST(Check, isupper) +{ + ASSERT_FALSE(Utf8::isupper('a')); + ASSERT_FALSE(Utf8::isupper(/* é */ 233)); + ASSERT_TRUE(Utf8::isupper('A')); + ASSERT_TRUE(Utf8::isupper(/* É */ 201)); +} + +TEST(Check, islower) +{ + ASSERT_TRUE(Utf8::islower('a')); + ASSERT_TRUE(Utf8::islower(/* é */ 233)); + ASSERT_FALSE(Utf8::islower('A')); + ASSERT_FALSE(Utf8::islower(/* É */ 201)); +} + +/* -------------------------------------------------------- + * Miscellaneous + * -------------------------------------------------------- */ + +TEST(Misc, nbytesPoint) +{ + ASSERT_EQ(1, Utf8::nbytesPoint('a')); + ASSERT_EQ(2, Utf8::nbytesPoint(/* é */ 233)); + ASSERT_EQ(3, Utf8::nbytesPoint(/* € */ 8364)); + ASSERT_EQ(4, Utf8::nbytesPoint(/* 𠀀 */ 131072)); +} + +TEST(Misc, nbytesUtf8) +{ + std::string s1{"a"}; + std::string s2{"é"}; + std::string s3{"€"}; + std::string s4{"𠀀"}; + + ASSERT_EQ(1, Utf8::nbytesUtf8(s1[0])); + ASSERT_EQ(2, Utf8::nbytesUtf8(s2[0])); + ASSERT_EQ(3, Utf8::nbytesUtf8(s3[0])); + ASSERT_EQ(4, Utf8::nbytesUtf8(s4[0])); +} + +int main(int argc, char **argv) +{ + InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/C++/tests/Xdg/main.cpp Sun Mar 08 14:26:33 2015 +0100 @@ -0,0 +1,340 @@ +/* + * main.cpp -- main test file for XDG + * + * Copyright (c) 2013, 2014 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <gtest/gtest.h> + +#include <Xdg.h> + +using namespace testing; + +namespace { + +std::string myhome; + +} + +TEST(HomeEmpty, config) +{ + ASSERT_TRUE(unsetenv("XDG_CONFIG_HOME") == 0); + + try { + Xdg xdg; + + ASSERT_EQ(myhome + "/.config", xdg.configHome()); + } catch (const std::exception &) { + ASSERT_TRUE(false); + } +} + +TEST(HomeEmpty, data) +{ + ASSERT_TRUE(unsetenv("XDG_DATA_HOME") == 0); + + try { + Xdg xdg; + + ASSERT_EQ(myhome + "/.local/share", xdg.dataHome()); + } catch (const std::exception &) { + ASSERT_TRUE(false); + } +} + +TEST(HomeEmpty, cache) +{ + ASSERT_TRUE(unsetenv("XDG_CACHE_HOME") == 0); + + try { + Xdg xdg; + + ASSERT_EQ(myhome + "/.cache", xdg.cacheHome()); + } catch (const std::exception &) { + ASSERT_TRUE(false); + } +} + +TEST(HomeEmpty, runtime) +{ + ASSERT_TRUE(unsetenv("XDG_RUNTIME_DIR") == 0); + + try { + Xdg xdg; + + try { + xdg.runtimeDir(); + + ASSERT_TRUE(false); + } catch (const std::exception &) { } + } catch (const std::exception &) { + ASSERT_TRUE(false); + } +} + +TEST(HomeValid, config) +{ + ASSERT_TRUE(setenv("XDG_CONFIG_HOME", "/test/config", true) == 0); + + try { + Xdg xdg; + + ASSERT_EQ("/test/config", xdg.configHome()); + } catch (const std::exception &) { + ASSERT_TRUE(false); + } +} + +TEST(HomeValid, data) +{ + ASSERT_TRUE(setenv("XDG_DATA_HOME", "/test/data", true) == 0); + + try { + Xdg xdg; + + ASSERT_EQ("/test/data", xdg.dataHome()); + } catch (const std::exception &) { + ASSERT_TRUE(false); + } +} + +TEST(HomeValid, cache) +{ + ASSERT_TRUE(setenv("XDG_CACHE_HOME", "/test/cache", true) == 0); + + try { + Xdg xdg; + + ASSERT_EQ("/test/cache", xdg.cacheHome()); + } catch (const std::exception &) { + ASSERT_TRUE(false); + } +} + +TEST(HomeValid, runtime) +{ + ASSERT_TRUE(setenv("XDG_RUNTIME_DIR", "/test/runtime", true) == 0); + + try { + Xdg xdg; + + ASSERT_EQ("/test/runtime", xdg.runtimeDir()); + } catch (const std::exception &) { + ASSERT_TRUE(false); + } +} + +TEST(HomeInvalid, config) +{ + ASSERT_TRUE(setenv("XDG_CONFIG_HOME", "invalid", true) == 0); + + try { + Xdg xdg; + + ASSERT_EQ(myhome + "/.config", xdg.configHome()); + } catch (const std::exception &) { + ASSERT_TRUE(false); + } +} + +TEST(HomeInvalid, data) +{ + ASSERT_TRUE(setenv("XDG_DATA_HOME", "invalid", true) == 0); + + try { + Xdg xdg; + + ASSERT_EQ(myhome + "/.local/share", xdg.dataHome()); + } catch (const std::exception &) { + ASSERT_TRUE(false); + } +} + +TEST(HomeInvalid, cache) +{ + ASSERT_TRUE(setenv("XDG_CACHE_HOME", "invalid", true) == 0); + + try { + Xdg xdg; + + ASSERT_EQ(myhome + "/.cache", xdg.cacheHome()); + } catch (const std::exception &) { + ASSERT_TRUE(false); + } +} + +TEST(HomeInvalid, runtime) +{ + ASSERT_TRUE(setenv("XDG_RUNTIME_DIR", "invalid", true) == 0); + + try { + Xdg xdg; + + try { + xdg.runtimeDir(); + + ASSERT_TRUE(false); + } catch (const std::exception &) { } + } catch (const std::exception &) { + ASSERT_TRUE(false); + } +} + +TEST(DirectoriesEmpty, config) +{ + ASSERT_TRUE(unsetenv("XDG_CONFIG_DIRS") == 0); + + try { + Xdg xdg; + + const auto &list = xdg.configDirs(); + + ASSERT_EQ((size_t)1, list.size()); + ASSERT_EQ("/etc/xdg", list[0]); + } catch (const std::exception &) { + ASSERT_TRUE(false); + } +} + +TEST(DirectoriesEmpty, data) +{ + ASSERT_TRUE(unsetenv("XDG_DATA_DIRS") == 0); + + try { + Xdg xdg; + + const auto &list = xdg.dataDirs(); + + ASSERT_EQ((size_t)2, list.size()); + ASSERT_EQ("/usr/local/share", list[0]); + ASSERT_EQ("/usr/share", list[1]); + } catch (const std::exception &) { + ASSERT_TRUE(false); + } +} + +TEST(DirectoriesValid, config) +{ + ASSERT_TRUE(setenv("XDG_CONFIG_DIRS", "/config1:/config2", true) == 0); + + try { + Xdg xdg; + + const auto &list = xdg.configDirs(); + + ASSERT_EQ((size_t)2, list.size()); + ASSERT_EQ("/config1", list[0]); + ASSERT_EQ("/config2", list[1]); + } catch (const std::exception &) { + ASSERT_TRUE(false); + } +} + +TEST(DirectoriesValid, data) +{ + ASSERT_TRUE(setenv("XDG_DATA_DIRS", "/data1:/data2", true) == 0); + + try { + Xdg xdg; + + const auto &list = xdg.dataDirs(); + + ASSERT_EQ((size_t)2, list.size()); + ASSERT_EQ("/data1", list[0]); + ASSERT_EQ("/data2", list[1]); + } catch (const std::exception &) { + ASSERT_TRUE(false); + } +} + +TEST(DirectoriesInvalid, config) +{ + ASSERT_TRUE(setenv("XDG_CONFIG_DIRS", "bad1:bad2", true) == 0); + + try { + Xdg xdg; + + const auto &list = xdg.configDirs(); + + ASSERT_EQ((size_t)1, list.size()); + ASSERT_EQ("/etc/xdg", list[0]); + } catch (const std::exception &) { + ASSERT_TRUE(false); + } +} + +TEST(DirectoriesInvalid, data) +{ + ASSERT_TRUE(setenv("XDG_DATA_DIRS", "bad1:bad2", true) == 0); + + try { + Xdg xdg; + + const auto &list = xdg.dataDirs(); + + ASSERT_EQ((size_t)2, list.size()); + ASSERT_EQ("/usr/local/share", list[0]); + ASSERT_EQ("/usr/share", list[1]); + } catch (const std::exception &) { + ASSERT_TRUE(false); + } +} + +TEST(DirectoriesMixed, config) +{ + ASSERT_TRUE(setenv("XDG_CONFIG_DIRS", "/config1:bad:/config2", true) == 0); + + try { + Xdg xdg; + + const auto &list = xdg.configDirs(); + + ASSERT_EQ((size_t)2, list.size()); + ASSERT_EQ("/config1", list[0]); + ASSERT_EQ("/config2", list[1]); + } catch (const std::exception &) { + ASSERT_TRUE(false); + } +} + +TEST(DirectoriesMixed, data) +{ + ASSERT_TRUE(setenv("XDG_DATA_DIRS", "/data1:bad:/data2", true) == 0); + + try { + Xdg xdg; + + const auto &list = xdg.dataDirs(); + + ASSERT_EQ((size_t)2, list.size()); + ASSERT_EQ("/data1", list[0]); + ASSERT_EQ("/data2", list[1]); + } catch (const std::exception &) { + ASSERT_TRUE(false); + } +} + +int main(int argc, char **argv) +{ + auto home = getenv("HOME"); + + if (home == nullptr) + return 0; + + myhome = home; + InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/C++/tests/Zip/data/data.txt Sun Mar 08 14:26:33 2015 +0100 @@ -0,0 +1,1 @@ +abcdef
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/C++/tests/Zip/main.cpp Sun Mar 08 14:26:33 2015 +0100 @@ -0,0 +1,270 @@ +/* + * main.cpp -- test the zip wrapper functions + * + * Copyright (c) 2013, 2014 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <gtest/gtest.h> + +#include <ZipArchive.h> + +using namespace source; + +/* -------------------------------------------------------- + * Sources + * -------------------------------------------------------- */ + +TEST(Source, file) +{ + remove("output.zip"); + + try { + ZipArchive archive("output.zip", ZIP_CREATE); + + archive.add(File("Zip/data.txt"), "data.txt"); + } catch (const std::exception &ex) { + std::cerr << ex.what() << std::endl; + } + + try { + ZipArchive archive("output.zip"); + + auto stats = archive.stat("data.txt"); + auto file = archive.open("data.txt"); + auto content = file.read(stats.size); + + ASSERT_EQ("abcdef\n", content); + } catch (const std::exception &ex) { + std::cerr << ex.what() << std::endl; + } +} + +TEST(Source, buffer) +{ + remove("output.zip"); + + try { + ZipArchive archive{"output.zip", ZIP_CREATE}; + + archive.add(Buffer{"abcdef"}, "data.txt"); + } catch (const std::exception &ex) { + std::cerr << ex.what() << std::endl; + } + + try { + ZipArchive archive{"output.zip"}; + + auto stats = archive.stat("data.txt"); + auto file = archive.open("data.txt"); + auto content = file.read(stats.size); + + ASSERT_EQ("abcdef", content); + } catch (const std::exception &ex) { + std::cerr << ex.what() << std::endl; + } +} + +/* -------------------------------------------------------- + * Write + * -------------------------------------------------------- */ + +TEST(Write, simple) +{ + remove("output.zip"); + + // Open first and save some data + try { + ZipArchive archive("output.zip", ZIP_CREATE); + + archive.add(Buffer("hello world!"), "DATA"); + } catch (const std::exception &ex) { + std::cerr << "warning: " << ex.what() << std::endl; + } + + try { + ZipArchive archive("output.zip"); + + auto stats = archive.stat("DATA"); + auto file = archive.open("DATA"); + auto content = file.read(stats.size); + + ASSERT_EQ(static_cast<decltype(stats.size)>(12), stats.size); + ASSERT_EQ("hello world!", content); + } catch (const std::exception &ex) { + std::cerr << "warning: " << ex.what() << std::endl; + } +} + +/* -------------------------------------------------------- + * Reading + * -------------------------------------------------------- */ + +class ReadingTest : public testing::Test { +protected: + ZipArchive m_archive; + +public: + ReadingTest() + : m_archive("Zip/stats.zip") + { + } +}; + +TEST_F(ReadingTest, numEntries) +{ + ASSERT_EQ(static_cast<ZipInt64>(4), m_archive.numEntries()); +} + +TEST_F(ReadingTest, stat) +{ + try { + ZipStat stats = m_archive.stat("README"); + + ASSERT_EQ(static_cast<decltype(stats.size)>(15), stats.size); + ASSERT_STREQ("README", stats.name); + } catch (const std::exception &ex) { + std::cerr << ex.what() << std::endl; + } +} + +TEST_F(ReadingTest, read) +{ + try { + auto file = m_archive.open("README"); + auto stats = m_archive.stat("README"); + auto text = file.read(stats.size); + + ASSERT_EQ("This is a test\n", text); + } catch (const std::exception &ex) { + std::cerr << "warning: " << ex.what() << std::endl; + } +} + +TEST_F(ReadingTest, increment) +{ + { + ZipArchive::iterator it = m_archive.begin(); + + ASSERT_STREQ("README", (*it++).name); + } + + { + ZipArchive::iterator it = m_archive.begin(); + + ASSERT_STREQ("INSTALL", (*++it).name); + } + + { + ZipArchive::iterator it = m_archive.begin() + 1; + + ASSERT_STREQ("INSTALL", (*it).name); + } +} + +TEST_F(ReadingTest, decrement) +{ + { + ZipArchive::iterator it = m_archive.begin() + 1; + + ASSERT_STREQ("INSTALL", (*it--).name); + } + + { + ZipArchive::iterator it = m_archive.begin() + 1; + + ASSERT_STREQ("README", (*--it).name); + } + + { + ZipArchive::iterator it = m_archive.end() - 1; + + ASSERT_STREQ("doc/REFMAN", (*it).name); + } +} + +TEST_F(ReadingTest, constIncrement) +{ + { + ZipArchive::const_iterator it = m_archive.cbegin(); + + ASSERT_STREQ("README", (*it++).name); + } + + { + ZipArchive::const_iterator it = m_archive.cbegin(); + + ASSERT_STREQ("INSTALL", (*++it).name); + } + + { + ZipArchive::const_iterator it = m_archive.cbegin() + 1; + + ASSERT_STREQ("INSTALL", (*it).name); + } +} + +TEST_F(ReadingTest, constDecrement) +{ + { + ZipArchive::const_iterator it = m_archive.cbegin() + 1; + + ASSERT_STREQ("INSTALL", (*it--).name); + } + + { + ZipArchive::const_iterator it = m_archive.cbegin() + 1; + + ASSERT_STREQ("README", (*--it).name); + } + + { + ZipArchive::const_iterator it = m_archive.cend() - 1; + + ASSERT_STREQ("doc/REFMAN", (*it).name); + } +} + +TEST_F(ReadingTest, access) +{ + { + ZipArchive::iterator it = m_archive.begin(); + + ASSERT_STREQ("README", it->name); + ASSERT_STREQ("INSTALL", it[1].name); + } + + { + ZipArchive::const_iterator it = m_archive.cbegin(); + + ASSERT_STREQ("README", it->name); + ASSERT_STREQ("INSTALL", it[1].name); + } +} + +TEST_F(ReadingTest, loop) +{ + std::vector<std::string> names{"README", "INSTALL", "doc/", "doc/REFMAN"}; + int i = 0; + + for (const ZipStat &s : m_archive) + ASSERT_STREQ(names[i++].c_str(), s.name); +} + +int main(int argc, char **argv) +{ + testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/C/port/asprintf.c Sun Mar 08 14:26:33 2015 +0100 @@ -0,0 +1,129 @@ +/* + * asprintf.c -- basic port of asprintf / vsprintf functions + * + * Copyright (c) 2011, 2012 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <stdarg.h> + +/* + * The Microsoft implementation relies on the Win32 API function + * _vscprintf which count the number of characters needed. Otherwise the C99 + * function vsnprintf returns the number of character that would be printed. + * + * Finally, if we don't have C99, we use an optimistic function. + * + * The asprintf function is common to every implementations. + */ + +#if defined(_MSC_VER) + +/* + * Windows implementation. + */ +int +vasprintf(char **res, const char *fmt, va_list ap) +{ + int total = _vscprintf(fmt, ap); + + if (total < 0) { + *res = NULL; + return -1; + } + + if ((*res = (char *)malloc(sizeof (total) + 1)) == NULL) + return -1; + + return vsprintf_s(*res, total + 1, fmt, ap); +} + +#elif __STDC_VERSION__ >= 199901L + +/* + * C99 implementation using vsnprintf's return value. + */ +int +vasprintf(char **res, const char *fmt, va_list ap) +{ + int total, nwritten; + va_list copy; + + va_copy(copy, ap); + total = vsnprintf(NULL, 0, fmt, copy); + va_end(copy); + + if (total < 0) { + *res = NULL; + return total; + } + + if ((*res = malloc(total + 1)) == NULL) + return -1; + + if ((nwritten = vsnprintf(*res, total + 1, fmt, ap)) < 0) { + free(*res); + *res = NULL; + + return -1; + } + + return nwritten; +} + +#else + +/* + * Optimistic function fallback. + */ +int +vasprintf(char **res, const char *format, va_list ap) +{ + int rvalue, ok; + size_t base = 80; + + if ((*res = malloc(base)) == NULL) + return -1; + + ok = 0; + do { + rvalue = vsnprintf(*res, base, format, ap); + + if ((signed int)base <= rvalue || rvalue < 0) { + *res = realloc(*res, base * 2); + base *= 2; + } else + ok = 1; + } while (!ok && *res != NULL); + + return rvalue; +} + +#endif + +int +asprintf(char **res, const char *fmt, ...) +{ + va_list ap; + int rvalue; + + va_start(ap, fmt); + rvalue = vasprintf(res, fmt, ap); + va_end(ap); + + return rvalue; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/C/port/asprintf.h Sun Mar 08 14:26:33 2015 +0100 @@ -0,0 +1,36 @@ +/* + * asprintf.h -- basic port of asprintf / vsprintf functions + * + * Copyright (c) 2011, 2012 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <stdarg.h> + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef __GNUC__ +# define _asp_at_printf(i1, i2) __attribute__ ((format (printf, i1, i2))) +#else +# define _asp_at_printf(i1, i2) +#endif + +int asprintf(char **, const char *, ...) _asp_at_printf(2, 3); +int vasprintf(char **, const char *, va_list); + +#ifdef __cplusplus +} +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/C/port/err.c Sun Mar 08 14:26:33 2015 +0100 @@ -0,0 +1,114 @@ +/* + * err.c -- formtted error messages (portable version) + * + * Copyright (c) 2011, 2012, David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include <string.h> +#include <errno.h> + +#include "err.h" + +/* + * These functions implements at least the same functions that can be found + * in the NetBSD err(3) man page without printing the programe name due to + * a portability issue. + */ + +void +err(int val, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + verr(val, fmt, ap); + va_end(ap); +} + +void +verr(int val, const char *fmt, va_list ap) +{ + if (fmt) { + vfprintf(stderr, fmt, ap); + fprintf(stderr, ": "); + } + + fprintf(stderr, "%s\n", strerror(errno)); + exit(val); +} + +void +errx(int val, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + verrx(val, fmt, ap); + va_end(ap); +} + +void +verrx(int val, const char *fmt, va_list ap) +{ + if (fmt) + vfprintf(stderr, fmt, ap); + + fprintf(stderr, "\n"); + + exit(val); +} + +void +warn(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + vwarn(fmt, ap); + va_end(ap); +} + +void +vwarn(const char *fmt, va_list ap) +{ + if (fmt) { + vfprintf(stderr, fmt, ap); + fprintf(stderr, ": "); + } + + fprintf(stderr, "%s\n", strerror(errno)); +} + +void +warnx(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + vwarnx(fmt, ap); + va_end(ap); +} + +void +vwarnx(const char *fmt, va_list ap) +{ + if (fmt) + vfprintf(stderr, fmt, ap); + + fprintf(stderr, "\n"); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/C/port/err.h Sun Mar 08 14:26:33 2015 +0100 @@ -0,0 +1,82 @@ +/* + * err.h -- formtted error messages (portable version) + * + * Copyright (c) 2011, 2012, David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _ERR_H_ +#define _ERR_H_ + +#include <stdarg.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Attribute noreturn may helps. This may produce a warning with GCC 4.5: + * + * int a; + * + * if ((a = getstate() < 0) + * errx(1, "State failed"); + * + * Because compilator does not know that errx will call exit may produce + * a warning like `a may be used uninitialized'. + */ + +#if defined(__GNUC__) +# define __at_noreturn __attribute__ ((noreturn)) +#elif defined(_MSC_VER) +# define __at_noreturn __declspec(noreturn) +#endif + +/* + * err() functions append the format message to the stderr FILE pointer. They + * also append the system error using strerror(errno). Then the functions exit + * with the error code given as first argument. + */ + +void err(int, const char *, ...) __at_noreturn; +void verr(int, const char *, va_list) __at_noreturn; + +/* + * errx() functions are similar to err() except that they do not append the + * system error message. + */ + +void errx(int, const char *, ...) __at_noreturn; +void verrx(int, const char *, va_list) __at_noreturn; + +/* + * warn() functions are similar to err() but they do not call exit(). + */ + +void warn(const char *, ...); +void vwarn(const char *, va_list); + +/* + * warnx() functions are similar to warn() except that they do not append the + * system error message. + */ + +void warnx(const char *, ...); +void vwarnx(const char *, va_list); + +#ifdef __cplusplus +} +#endif + +#endif /* _ERR_H_ */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/C/port/extern/getopt.c Sun Mar 08 14:26:33 2015 +0100 @@ -0,0 +1,124 @@ +/* + * getopt.c -- getopt(3) from FreeBSD (portable version) + */ + +/*- + * Copyright (c) 1987, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +int opterr = 1, /* if error message should be printed */ + optind = 1, /* index into parent argv vector */ + optopt, /* character checked for validity */ + optreset; /* reset getopt */ +char *optarg; /* argument associated with option */ + +#define BADCH (int)'?' +#define BADARG (int)':' +#define EMSG "" + +/* + * getopt -- + * Parse argc/argv argument vector. + */ +int +getopt(nargc, nargv, ostr) + int nargc; + char * const nargv[]; + const char *ostr; +{ + static char *place = EMSG; /* option letter processing */ + char *oli; /* option letter list index */ + + if (optreset || *place == 0) { /* update scanning pointer */ + optreset = 0; + place = nargv[optind]; + if (optind >= nargc || *place++ != '-') { + /* Argument is absent or is not an option */ + place = EMSG; + return (-1); + } + optopt = *place++; + if (optopt == '-' && *place == 0) { + /* "--" => end of options */ + ++optind; + place = EMSG; + return (-1); + } + if (optopt == 0) { + /* Solitary '-', treat as a '-' option + if the program (eg su) is looking for it. */ + place = EMSG; + if (strchr(ostr, '-') == NULL) + return (-1); + optopt = '-'; + } + } else + optopt = *place++; + + /* See if option letter is one the caller wanted... */ + if (optopt == ':' || (oli = strchr(ostr, optopt)) == NULL) { + if (*place == 0) + ++optind; + if (opterr && *ostr != ':') + (void)fprintf(stderr, + "illegal option -- %c\n", optopt); + return (BADCH); + } + + /* Does this option need an argument? */ + if (oli[1] != ':') { + /* don't need argument */ + optarg = NULL; + if (*place == 0) + ++optind; + } else { + /* Option-argument is either the rest of this argument or the + entire next argument. */ + if (*place) + optarg = place; + else if (nargc > ++optind) + optarg = nargv[optind]; + else { + /* option-argument absent */ + place = EMSG; + if (*ostr == ':') + return (BADARG); + if (opterr) + (void)fprintf(stderr, + "option requires an argument -- %c\n", optopt); + return (BADCH); + } + place = EMSG; + ++optind; + } + return (optopt); /* return option letter */ +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/C/port/extern/getopt.h Sun Mar 08 14:26:33 2015 +0100 @@ -0,0 +1,50 @@ +/* + * getopt.h -- getopt(3) from FreeBSD (portable version) + */ + +/*- + * Copyright (c) 2000 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Dieter Baron and Thomas Klausner. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _GETOPT_H_ +#define _GETOPT_H_ + +int getopt(int, char * const [], const char *); + +extern char *optarg; /* getopt(3) external variables */ +extern int optind, opterr, optopt; +extern int optreset; /* getopt(3) external variable */ + +#endif /* !_GETOPT_H_ */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/C/port/extern/queue.h Sun Mar 08 14:26:33 2015 +0100 @@ -0,0 +1,584 @@ +/* + * queue.h -- queue(3) from FreeBSD (portable version) + */ + +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)queue.h 8.5 (Berkeley) 8/20/94 + * $FreeBSD: src/sys/sys/queue.h,v 1.72.2.4 2011/05/24 16:04:35 mdf Exp $ + */ + +#ifndef _SYS_QUEUE_H_ +#define _SYS_QUEUE_H_ + +/* + * This file defines four types of data structures: singly-linked lists, + * singly-linked tail queues, lists and tail queues. + * + * A singly-linked list is headed by a single forward pointer. The elements + * are singly linked for minimum space and pointer manipulation overhead at + * the expense of O(n) removal for arbitrary elements. New elements can be + * added to the list after an existing element or at the head of the list. + * Elements being removed from the head of the list should use the explicit + * macro for this purpose for optimum efficiency. A singly-linked list may + * only be traversed in the forward direction. Singly-linked lists are ideal + * for applications with large datasets and few or no removals or for + * implementing a LIFO queue. + * + * A singly-linked tail queue is headed by a pair of pointers, one to the + * head of the list and the other to the tail of the list. The elements are + * singly linked for minimum space and pointer manipulation overhead at the + * expense of O(n) removal for arbitrary elements. New elements can be added + * to the list after an existing element, at the head of the list, or at the + * end of the list. Elements being removed from the head of the tail queue + * should use the explicit macro for this purpose for optimum efficiency. + * A singly-linked tail queue may only be traversed in the forward direction. + * Singly-linked tail queues are ideal for applications with large datasets + * and few or no removals or for implementing a FIFO queue. + * + * A list is headed by a single forward pointer (or an array of forward + * pointers for a hash table header). The elements are doubly linked + * so that an arbitrary element can be removed without a need to + * traverse the list. New elements can be added to the list before + * or after an existing element or at the head of the list. A list + * may only be traversed in the forward direction. + * + * A tail queue is headed by a pair of pointers, one to the head of the + * list and the other to the tail of the list. The elements are doubly + * linked so that an arbitrary element can be removed without a need to + * traverse the list. New elements can be added to the list before or + * after an existing element, at the head of the list, or at the end of + * the list. A tail queue may be traversed in either direction. + * + * For details on the use of these macros, see the queue(3) manual page. + * + * + * SLIST LIST STAILQ TAILQ + * _HEAD + + + + + * _HEAD_INITIALIZER + + + + + * _ENTRY + + + + + * _INIT + + + + + * _EMPTY + + + + + * _FIRST + + + + + * _NEXT + + + + + * _PREV - - - + + * _LAST - - + + + * _FOREACH + + + + + * _FOREACH_SAFE + + + + + * _FOREACH_REVERSE - - - + + * _FOREACH_REVERSE_SAFE - - - + + * _INSERT_HEAD + + + + + * _INSERT_BEFORE - + - + + * _INSERT_AFTER + + + + + * _INSERT_TAIL - - + + + * _CONCAT - - + + + * _REMOVE_AFTER + - + - + * _REMOVE_HEAD + - + - + * _REMOVE + + + + + * _SWAP + + + + + * + */ + +/* + * winnt.h includes some variables like SLIST_ENTRY, so undef all. If this + * file is used it is probably because the user want is instead of + * windows ones. + */ + +#undef SLIST_EMPTY +#undef SLIST_ENTRY +#undef SLIST_FIRST +#undef SLIST_FOREACH +#undef SLIST_FOREACH_SAFE +#undef SLIST_HEAD +#undef SLIST_HEAD_INITIALIZER +#undef SLIST_INIT +#undef SLIST_INSERT_AFTER +#undef SLIST_INSERT_HEAD +#undef SLIST_NEXT +#undef SLIST_REMOVE_AFTER +#undef SLIST_REMOVE_HEAD +#undef SLIST_REMOVE +#undef SLIST_SWAP +#undef STAILQ_CONCAT +#undef STAILQ_EMPTY +#undef STAILQ_ENTRY +#undef STAILQ_FIRST +#undef STAILQ_FOREACH +#undef STAILQ_FOREACH_SAFE +#undef STAILQ_HEAD +#undef STAILQ_HEAD_INITIALIZER +#undef STAILQ_INIT +#undef STAILQ_INSERT_AFTER +#undef STAILQ_INSERT_HEAD +#undef STAILQ_INSERT_TAIL +#undef STAILQ_LAST +#undef STAILQ_NEXT +#undef STAILQ_REMOVE_AFTER +#undef STAILQ_REMOVE_HEAD +#undef STAILQ_REMOVE +#undef STAILQ_SWAP +#undef LIST_EMPTY +#undef LIST_ENTRY +#undef LIST_FIRST +#undef LIST_FOREACH +#undef LIST_FOREACH_SAFE +#undef LIST_HEAD +#undef LIST_HEAD_INITIALIZER +#undef LIST_INIT +#undef LIST_INSERT_AFTER +#undef LIST_INSERT_BEFORE +#undef LIST_INSERT_HEAD +#undef LIST_NEXT +#undef LIST_REMOVE +#undef LIST_SWAP +#undef TAILQ_CONCAT +#undef TAILQ_EMPTY +#undef TAILQ_ENTRY +#undef TAILQ_FIRST +#undef TAILQ_FOREACH +#undef TAILQ_FOREACH_SAFE +#undef TAILQ_FOREACH_REVERSE +#undef TAILQ_FOREACH_REVERSE_SAFE +#undef TAILQ_HEAD +#undef TAILQ_HEAD_INITIALIZER +#undef TAILQ_INIT +#undef TAILQ_INSERT_AFTER +#undef TAILQ_INSERT_BEFORE +#undef TAILQ_INSERT_HEAD +#undef TAILQ_INSERT_TAIL +#undef TAILQ_LAST +#undef TAILQ_NEXT +#undef TAILQ_PREV +#undef TAILQ_REMOVE +#undef TAILQ_SWAP + +/* + * Singly-linked List declarations. + */ +#define SLIST_HEAD(name, type) \ +struct name { \ + struct type *slh_first; /* first element */ \ +} + +#define SLIST_HEAD_INITIALIZER(head) \ + { NULL } + +#define SLIST_ENTRY(type) \ +struct { \ + struct type *sle_next; /* next element */ \ +} + +/* + * Singly-linked List functions. + */ +#define SLIST_EMPTY(head) ((head)->slh_first == NULL) + +#define SLIST_FIRST(head) ((head)->slh_first) + +#define SLIST_FOREACH(var, head, field) \ + for ((var) = SLIST_FIRST((head)); \ + (var); \ + (var) = SLIST_NEXT((var), field)) + +#define SLIST_FOREACH_SAFE(var, head, field, tvar) \ + for ((var) = SLIST_FIRST((head)); \ + (var) && ((tvar) = SLIST_NEXT((var), field), 1); \ + (var) = (tvar)) + +#define SLIST_FOREACH_PREVPTR(var, varp, head, field) \ + for ((varp) = &SLIST_FIRST((head)); \ + ((var) = *(varp)) != NULL; \ + (varp) = &SLIST_NEXT((var), field)) + +#define SLIST_INIT(head) do { \ + SLIST_FIRST((head)) = NULL; \ +} while (0) + +#define SLIST_INSERT_AFTER(slistelm, elm, field) do { \ + SLIST_NEXT((elm), field) = SLIST_NEXT((slistelm), field); \ + SLIST_NEXT((slistelm), field) = (elm); \ +} while (0) + +#define SLIST_INSERT_HEAD(head, elm, field) do { \ + SLIST_NEXT((elm), field) = SLIST_FIRST((head)); \ + SLIST_FIRST((head)) = (elm); \ +} while (0) + +#define SLIST_NEXT(elm, field) ((elm)->field.sle_next) + +#define SLIST_REMOVE(head, elm, type, field) do { \ + if (SLIST_FIRST((head)) == (elm)) { \ + SLIST_REMOVE_HEAD((head), field); \ + } \ + else { \ + struct type *curelm = SLIST_FIRST((head)); \ + while (SLIST_NEXT(curelm, field) != (elm)) \ + curelm = SLIST_NEXT(curelm, field); \ + SLIST_REMOVE_AFTER(curelm, field); \ + } \ +} while (0) + +#define SLIST_REMOVE_AFTER(elm, field) do { \ + SLIST_NEXT(elm, field) = \ + SLIST_NEXT(SLIST_NEXT(elm, field), field); \ +} while (0) + +#define SLIST_REMOVE_HEAD(head, field) do { \ + SLIST_FIRST((head)) = SLIST_NEXT(SLIST_FIRST((head)), field); \ +} while (0) + +#define SLIST_SWAP(head1, head2, type) do { \ + struct type *swap_first = SLIST_FIRST(head1); \ + SLIST_FIRST(head1) = SLIST_FIRST(head2); \ + SLIST_FIRST(head2) = swap_first; \ +} while (0) + +/* + * Singly-linked Tail queue declarations. + */ +#define STAILQ_HEAD(name, type) \ +struct name { \ + struct type *stqh_first;/* first element */ \ + struct type **stqh_last;/* addr of last next element */ \ +} + +#define STAILQ_HEAD_INITIALIZER(head) \ + { NULL, &(head).stqh_first } + +#define STAILQ_ENTRY(type) \ +struct { \ + struct type *stqe_next; /* next element */ \ +} + +/* + * Singly-linked Tail queue functions. + */ +#define STAILQ_CONCAT(head1, head2) do { \ + if (!STAILQ_EMPTY((head2))) { \ + *(head1)->stqh_last = (head2)->stqh_first; \ + (head1)->stqh_last = (head2)->stqh_last; \ + STAILQ_INIT((head2)); \ + } \ +} while (0) + +#define STAILQ_EMPTY(head) ((head)->stqh_first == NULL) + +#define STAILQ_FIRST(head) ((head)->stqh_first) + +#define STAILQ_FOREACH(var, head, field) \ + for((var) = STAILQ_FIRST((head)); \ + (var); \ + (var) = STAILQ_NEXT((var), field)) + + +#define STAILQ_FOREACH_SAFE(var, head, field, tvar) \ + for ((var) = STAILQ_FIRST((head)); \ + (var) && ((tvar) = STAILQ_NEXT((var), field), 1); \ + (var) = (tvar)) + +#define STAILQ_INIT(head) do { \ + STAILQ_FIRST((head)) = NULL; \ + (head)->stqh_last = &STAILQ_FIRST((head)); \ +} while (0) + +#define STAILQ_INSERT_AFTER(head, tqelm, elm, field) do { \ + if ((STAILQ_NEXT((elm), field) = STAILQ_NEXT((tqelm), field)) == NULL)\ + (head)->stqh_last = &STAILQ_NEXT((elm), field); \ + STAILQ_NEXT((tqelm), field) = (elm); \ +} while (0) + +#define STAILQ_INSERT_HEAD(head, elm, field) do { \ + if ((STAILQ_NEXT((elm), field) = STAILQ_FIRST((head))) == NULL) \ + (head)->stqh_last = &STAILQ_NEXT((elm), field); \ + STAILQ_FIRST((head)) = (elm); \ +} while (0) + +#define STAILQ_INSERT_TAIL(head, elm, field) do { \ + STAILQ_NEXT((elm), field) = NULL; \ + *(head)->stqh_last = (elm); \ + (head)->stqh_last = &STAILQ_NEXT((elm), field); \ +} while (0) + +#define STAILQ_LAST(head, type, field) \ + (STAILQ_EMPTY((head)) ? \ + NULL : \ + ((struct type *)(void *) \ + ((char *)((head)->stqh_last) - __offsetof(struct type, field)))) + +#define STAILQ_NEXT(elm, field) ((elm)->field.stqe_next) + +#define STAILQ_REMOVE(head, elm, type, field) do { \ + if (STAILQ_FIRST((head)) == (elm)) { \ + STAILQ_REMOVE_HEAD((head), field); \ + } \ + else { \ + struct type *curelm = STAILQ_FIRST((head)); \ + while (STAILQ_NEXT(curelm, field) != (elm)) \ + curelm = STAILQ_NEXT(curelm, field); \ + STAILQ_REMOVE_AFTER(head, curelm, field); \ + } \ +} while (0) + +#define STAILQ_REMOVE_AFTER(head, elm, field) do { \ + if ((STAILQ_NEXT(elm, field) = \ + STAILQ_NEXT(STAILQ_NEXT(elm, field), field)) == NULL) \ + (head)->stqh_last = &STAILQ_NEXT((elm), field); \ +} while (0) + +#define STAILQ_REMOVE_HEAD(head, field) do { \ + if ((STAILQ_FIRST((head)) = \ + STAILQ_NEXT(STAILQ_FIRST((head)), field)) == NULL) \ + (head)->stqh_last = &STAILQ_FIRST((head)); \ +} while (0) + +#define STAILQ_SWAP(head1, head2, type) do { \ + struct type *swap_first = STAILQ_FIRST(head1); \ + struct type **swap_last = (head1)->stqh_last; \ + STAILQ_FIRST(head1) = STAILQ_FIRST(head2); \ + (head1)->stqh_last = (head2)->stqh_last; \ + STAILQ_FIRST(head2) = swap_first; \ + (head2)->stqh_last = swap_last; \ + if (STAILQ_EMPTY(head1)) \ + (head1)->stqh_last = &STAILQ_FIRST(head1); \ + if (STAILQ_EMPTY(head2)) \ + (head2)->stqh_last = &STAILQ_FIRST(head2); \ +} while (0) + +/* + * List declarations. + */ +#define LIST_HEAD(name, type) \ +struct name { \ + struct type *lh_first; /* first element */ \ +} + +#define LIST_HEAD_INITIALIZER(head) \ + { NULL } + +#define LIST_ENTRY(type) \ +struct { \ + struct type *le_next; /* next element */ \ + struct type **le_prev; /* address of previous next element */ \ +} + +/* + * List functions. + */ + +#define LIST_EMPTY(head) ((head)->lh_first == NULL) + +#define LIST_FIRST(head) ((head)->lh_first) + +#define LIST_FOREACH(var, head, field) \ + for ((var) = LIST_FIRST((head)); \ + (var); \ + (var) = LIST_NEXT((var), field)) + +#define LIST_FOREACH_SAFE(var, head, field, tvar) \ + for ((var) = LIST_FIRST((head)); \ + (var) && ((tvar) = LIST_NEXT((var), field), 1); \ + (var) = (tvar)) + +#define LIST_INIT(head) do { \ + LIST_FIRST((head)) = NULL; \ +} while (0) + +#define LIST_INSERT_AFTER(listelm, elm, field) do { \ + if ((LIST_NEXT((elm), field) = LIST_NEXT((listelm), field)) != NULL)\ + LIST_NEXT((listelm), field)->field.le_prev = \ + &LIST_NEXT((elm), field); \ + LIST_NEXT((listelm), field) = (elm); \ + (elm)->field.le_prev = &LIST_NEXT((listelm), field); \ +} while (0) + +#define LIST_INSERT_BEFORE(listelm, elm, field) do { \ + (elm)->field.le_prev = (listelm)->field.le_prev; \ + LIST_NEXT((elm), field) = (listelm); \ + *(listelm)->field.le_prev = (elm); \ + (listelm)->field.le_prev = &LIST_NEXT((elm), field); \ +} while (0) + +#define LIST_INSERT_HEAD(head, elm, field) do { \ + if ((LIST_NEXT((elm), field) = LIST_FIRST((head))) != NULL) \ + LIST_FIRST((head))->field.le_prev = &LIST_NEXT((elm), field);\ + LIST_FIRST((head)) = (elm); \ + (elm)->field.le_prev = &LIST_FIRST((head)); \ +} while (0) + +#define LIST_NEXT(elm, field) ((elm)->field.le_next) + +#define LIST_REMOVE(elm, field) do { \ + if (LIST_NEXT((elm), field) != NULL) \ + LIST_NEXT((elm), field)->field.le_prev = \ + (elm)->field.le_prev; \ + *(elm)->field.le_prev = LIST_NEXT((elm), field); \ +} while (0) + +#define LIST_SWAP(head1, head2, type, field) do { \ + struct type *swap_tmp = LIST_FIRST((head1)); \ + LIST_FIRST((head1)) = LIST_FIRST((head2)); \ + LIST_FIRST((head2)) = swap_tmp; \ + if ((swap_tmp = LIST_FIRST((head1))) != NULL) \ + swap_tmp->field.le_prev = &LIST_FIRST((head1)); \ + if ((swap_tmp = LIST_FIRST((head2))) != NULL) \ + swap_tmp->field.le_prev = &LIST_FIRST((head2)); \ +} while (0) + +/* + * Tail queue declarations. + */ +#define TAILQ_HEAD(name, type) \ +struct name { \ + struct type *tqh_first; /* first element */ \ + struct type **tqh_last; /* addr of last next element */ \ +} + +#define TAILQ_HEAD_INITIALIZER(head) \ + { NULL, &(head).tqh_first } + +#define TAILQ_ENTRY(type) \ +struct { \ + struct type *tqe_next; /* next element */ \ + struct type **tqe_prev; /* address of previous next element */ \ +} + +/* + * Tail queue functions. + */ + +#define TAILQ_CONCAT(head1, head2, field) do { \ + if (!TAILQ_EMPTY(head2)) { \ + *(head1)->tqh_last = (head2)->tqh_first; \ + (head2)->tqh_first->field.tqe_prev = (head1)->tqh_last; \ + (head1)->tqh_last = (head2)->tqh_last; \ + TAILQ_INIT((head2)); \ + } \ +} while (0) + +#define TAILQ_EMPTY(head) ((head)->tqh_first == NULL) + +#define TAILQ_FIRST(head) ((head)->tqh_first) + +#define TAILQ_FOREACH(var, head, field) \ + for ((var) = TAILQ_FIRST((head)); \ + (var); \ + (var) = TAILQ_NEXT((var), field)) + +#define TAILQ_FOREACH_SAFE(var, head, field, tvar) \ + for ((var) = TAILQ_FIRST((head)); \ + (var) && ((tvar) = TAILQ_NEXT((var), field), 1); \ + (var) = (tvar)) + +#define TAILQ_FOREACH_REVERSE(var, head, headname, field) \ + for ((var) = TAILQ_LAST((head), headname); \ + (var); \ + (var) = TAILQ_PREV((var), headname, field)) + +#define TAILQ_FOREACH_REVERSE_SAFE(var, head, headname, field, tvar) \ + for ((var) = TAILQ_LAST((head), headname); \ + (var) && ((tvar) = TAILQ_PREV((var), headname, field), 1); \ + (var) = (tvar)) + +#define TAILQ_INIT(head) do { \ + TAILQ_FIRST((head)) = NULL; \ + (head)->tqh_last = &TAILQ_FIRST((head)); \ +} while (0) + +#define TAILQ_INSERT_AFTER(head, listelm, elm, field) do { \ + if ((TAILQ_NEXT((elm), field) = TAILQ_NEXT((listelm), field)) != NULL)\ + TAILQ_NEXT((elm), field)->field.tqe_prev = \ + &TAILQ_NEXT((elm), field); \ + else { \ + (head)->tqh_last = &TAILQ_NEXT((elm), field); \ + } \ + TAILQ_NEXT((listelm), field) = (elm); \ + (elm)->field.tqe_prev = &TAILQ_NEXT((listelm), field); \ +} while (0) + +#define TAILQ_INSERT_BEFORE(listelm, elm, field) do { \ + (elm)->field.tqe_prev = (listelm)->field.tqe_prev; \ + TAILQ_NEXT((elm), field) = (listelm); \ + *(listelm)->field.tqe_prev = (elm); \ + (listelm)->field.tqe_prev = &TAILQ_NEXT((elm), field); \ +} while (0) + +#define TAILQ_INSERT_HEAD(head, elm, field) do { \ + if ((TAILQ_NEXT((elm), field) = TAILQ_FIRST((head))) != NULL) \ + TAILQ_FIRST((head))->field.tqe_prev = \ + &TAILQ_NEXT((elm), field); \ + else \ + (head)->tqh_last = &TAILQ_NEXT((elm), field); \ + TAILQ_FIRST((head)) = (elm); \ + (elm)->field.tqe_prev = &TAILQ_FIRST((head)); \ +} while (0) + +#define TAILQ_INSERT_TAIL(head, elm, field) do { \ + TAILQ_NEXT((elm), field) = NULL; \ + (elm)->field.tqe_prev = (head)->tqh_last; \ + *(head)->tqh_last = (elm); \ + (head)->tqh_last = &TAILQ_NEXT((elm), field); \ +} while (0) + +#define TAILQ_LAST(head, headname) \ + (*(((struct headname *)((head)->tqh_last))->tqh_last)) + +#define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next) + +#define TAILQ_PREV(elm, headname, field) \ + (*(((struct headname *)((elm)->field.tqe_prev))->tqh_last)) + +#define TAILQ_REMOVE(head, elm, field) do { \ + if ((TAILQ_NEXT((elm), field)) != NULL) \ + TAILQ_NEXT((elm), field)->field.tqe_prev = \ + (elm)->field.tqe_prev; \ + else { \ + (head)->tqh_last = (elm)->field.tqe_prev; \ + } \ + *(elm)->field.tqe_prev = TAILQ_NEXT((elm), field); \ +} while (0) + +#define TAILQ_SWAP(head1, head2, type, field) do { \ + struct type *swap_first = (head1)->tqh_first; \ + struct type **swap_last = (head1)->tqh_last; \ + (head1)->tqh_first = (head2)->tqh_first; \ + (head1)->tqh_last = (head2)->tqh_last; \ + (head2)->tqh_first = swap_first; \ + (head2)->tqh_last = swap_last; \ + if ((swap_first = (head1)->tqh_first) != NULL) \ + swap_first->field.tqe_prev = &(head1)->tqh_first; \ + else \ + (head1)->tqh_last = &(head1)->tqh_first; \ + if ((swap_first = (head2)->tqh_first) != NULL) \ + swap_first->field.tqe_prev = &(head2)->tqh_first; \ + else \ + (head2)->tqh_last = &(head2)->tqh_first; \ +} while (0) + +#endif /* !_SYS_QUEUE_H_ */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/C/port/extern/stdbool.h Sun Mar 08 14:26:33 2015 +0100 @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2000 Jeroen Ruigrok van der Werven <asmodai@FreeBSD.org> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD: src/include/stdbool.h,v 1.7.28.1.2.1 2011/11/11 04:20:22 kensmith Exp $ + */ + +#ifndef _STDBOOL_H_ +#define _STDBOOL_H_ + +#define __bool_true_false_are_defined 1 + +#ifndef __cplusplus + +#define false 0 +#define true 1 + +/* Very portable bool version */ +typedef char bool; + +#endif /* !__cplusplus */ + +#endif /* !_STDBOOL_H_ */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/C/port/extern/strlcat.c Sun Mar 08 14:26:33 2015 +0100 @@ -0,0 +1,53 @@ +/* + * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com> + * Copyright (c) 2012 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <stddef.h> + +/* + * Appends src to string dst of size siz (unlike strncat, siz is the + * full size of dst, not space left). At most siz-1 characters + * will be copied. Always NUL terminates (unless siz <= strlen(dst)). + * Returns strlen(src) + MIN(siz, strlen(initial dst)). + * If retval >= siz, truncation occurred. + */ +size_t +strlcat(char *dst, const char *src, size_t siz) +{ + char *d = dst; + const char *s = src; + size_t n = siz; + size_t dlen; + + /* Find the end of dst and adjust bytes left but don't go past end */ + while (n-- != 0 && *d != '\0') + d++; + dlen = d - dst; + n = siz - dlen; + + if (n == 0) + return(dlen + strlen(s)); + while (*s != '\0') { + if (n != 1) { + *d++ = *s; + n--; + } + s++; + } + *d = '\0'; + + return(dlen + (s - src)); /* count does not include NUL */ +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/C/port/extern/strlcpy.c Sun Mar 08 14:26:33 2015 +0100 @@ -0,0 +1,49 @@ +/* + * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com> + * Copyright (c) 2012 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <stddef.h> + +/* + * Copy src to string dst of size siz. At most siz-1 characters + * will be copied. Always NUL terminates (unless siz == 0). + * Returns strlen(src); if retval >= siz, truncation occurred. + */ +size_t +strlcpy(char *dst, const char *src, size_t siz) +{ + char *d = dst; + const char *s = src; + size_t n = siz; + + /* Copy as many bytes as will fit */ + if (n != 0) { + while (--n != 0) { + if ((*d++ = *s++) == '\0') + break; + } + } + + /* Not enough room in dst, add NUL and traverse rest of src */ + if (n == 0) { + if (siz != 0) + *d = '\0'; /* NUL-terminate dst */ + while (*s++) + ; + } + + return(s - src - 1); /* count does not include NUL */ +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/C/port/setprogname.c Sun Mar 08 14:26:33 2015 +0100 @@ -0,0 +1,45 @@ +/* + * setprogname.c -- get or set the program name + * + * Copyright (c) 2011, 2012 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +/* + * Do not return NULL if the developer didn't call setprogname to + * prevent useless segfault. + */ +static const char *g_pname = ""; + +void +setprogname(const char *progname) +{ + const char *p; + + /* Seek last / or \ on windows */ + if ((p = strrchr(progname, '/')) || (p = strrchr(progname, '\\'))) + g_pname = &p[1]; + else + g_pname = progname; +} + +const char * +getprogname(void) +{ + return g_pname; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/C/port/setprogname.h Sun Mar 08 14:26:33 2015 +0100 @@ -0,0 +1,28 @@ +/* + * setprogname.h -- get or set the program name + * + * Copyright (c) 2011, 2012 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifdef __cplusplus +extern "C" { +#endif + +void setprogname(const char *); +const char *getprogname(void); + +#ifdef __cplusplus +} +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/C/port/strdup.c Sun Mar 08 14:26:33 2015 +0100 @@ -0,0 +1,35 @@ +/* + * strdup.c -- duplicate a string + * + * Copyright (c) 2011, 2012, David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +char * +strdup(const char *src) +{ + char *dst; + size_t len; + + len = strlen(src); + + if ((dst = calloc(len + 1, sizeof (char))) == NULL) + return NULL; + + return strcpy(dst, src); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/C/port/strdup.h Sun Mar 08 14:26:33 2015 +0100 @@ -0,0 +1,28 @@ +/* + * strdup.h -- duplicate a string + * + * Copyright (c) 2011, 2012, David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifdef __cplusplus +extern "C" { +#endif + +char *strdup(const char *); + +#ifdef __cplusplus +} +#endif +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/C/port/strndup.c Sun Mar 08 14:26:33 2015 +0100 @@ -0,0 +1,36 @@ +/* + * strndup.c -- duplicate a string + * + * Copyright (c) 2011, 2012, David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +char * +strndup(const char *src, size_t len) +{ + size_t tocopy; + char *dst; + + for (tocopy = 0; tocopy < len && src[tocopy] != '\0'; ++tocopy) + continue; + + if ((dst = calloc(tocopy + 1, 1)) != NULL) + strncpy(dst, src, tocopy); + + return dst; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/C/port/strndup.h Sun Mar 08 14:26:33 2015 +0100 @@ -0,0 +1,27 @@ +/* + * strndup.h -- duplicate a string + * + * Copyright (c) 2011, 2012, David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifdef __cplusplus +extern "C" { +#endif + +char *strndup(const char *, size_t); + +#ifdef __cplusplus +} +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/C/port/strsep.c Sun Mar 08 14:26:33 2015 +0100 @@ -0,0 +1,40 @@ +/* + * strsep.c -- separate a string by delimiters + * + * Copyright (c) 2011, 2012, David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <stdlib.h> +#include <string.h> + +char * +strsep(char **stringp, const char *delim) +{ + char *item, *ptr; + + if (*stringp == NULL || delim[0] == '\0') + return NULL; + + item = *stringp; + if ((ptr = strpbrk(*stringp, delim)) == NULL) { + *stringp = NULL; + return item; + } + + *ptr = '\0'; + *stringp = ptr + 1; + + return item; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/C/port/strsep.h Sun Mar 08 14:26:33 2015 +0100 @@ -0,0 +1,27 @@ +/* + * strsep.h -- separate a string by delimiters + * + * Copyright (c) 2011, 2012, David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifdef __cplusplus +extern "C" { +#endif + +char *strsep(char **, const char *); + +#ifdef __cplusplus +} +#endif
--- a/CMakeLists.txt Sun Mar 08 11:07:36 2015 +0100 +++ b/CMakeLists.txt Sun Mar 08 14:26:33 2015 +0100 @@ -25,103 +25,372 @@ set(CMAKE_MODULE_PATH ${code_SOURCE_DIR}/cmake) +include(CMakeParseArguments) + +add_subdirectory(extern) +add_subdirectory(tools) + enable_testing() -include_directories( - ${code_SOURCE_DIR}/C++ +macro(define_module) + set(oneValueArgs TARGET NAME DIRECTORY) + set(multiValueArgs SOURCES RESOURCES DOCS LIBRARIES INCLUDES) + + cmake_parse_arguments(MOD "" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + + if (NOT MOD_TARGET) + message(FATAL_ERROR "Argument TARGET not set") + elseif (NOT MOD_NAME) + message(FATAL_ERROR "Argument NAME not set") + elseif (NOT MOD_DIRECTORY) + message(FATAL_ERROR "Argument DIRECTORY not set") + endif () + + string(TOUPPER ${MOD_NAME} optionname) + + # Create the option for enabling the test + option(WITH_${optionname} "Enable ${MOD_NAME}" On) + + if (WITH_${optionname}) + # Add the test executable + set(MAIN ${code_SOURCE_DIR}/C++/tests/${MOD_DIRECTORY}/main.cpp) + + if (NOT EXISTS ${MAIN}) + message(FATAL_ERROR "${MAIN} file does not exists") + endif () + + add_executable(${MOD_TARGET} ${MOD_SOURCES} ${MOD_RESOURCES} ${MOD_DOCS} ${MAIN}) + add_test(${MOD_TARGET}-test ${MOD_TARGET}) + target_include_directories( + ${MOD_TARGET} + PRIVATE + ${code_SOURCE_DIR}/C++ + ${code_SOURCE_DIR}/C++/modules/${MOD_DIRECTORY} + ${MOD_INCLUDES} + ) + target_link_libraries(${MOD_TARGET} gtest ${MOD_LIBRARIES}) + + # Copy optional resources + if (MOD_RESOURCES) + foreach (res ${MOD_RESOURCES}) + get_filename_component(inputname ${res} NAME) + set(output ${CMAKE_BINARY_DIR}/${MOD_DIRECTORY}/${inputname}) + list(APPEND outputlist ${output}) + + add_custom_command( + OUTPUT ${output} + COMMENT "Copying ${inputname}" + DEPENDS ${res} + COMMAND ${CMAKE_COMMAND} -E copy ${res} ${output} + ) + endforeach() + + add_custom_target(${MOD_TARGET}-resources DEPENDS ${outputlist}) + add_dependencies(${MOD_TARGET} ${MOD_TARGET}-resources) + endif () + + # Generate documentation locally + if (MOD_DOCS) + foreach (doc ${MOD_DOCS}) + file(RELATIVE_PATH inputbase ${code_SOURCE_DIR}/C++/doc/${MOD_DIRECTORY} ${doc}) + string(REGEX REPLACE "^(.*)\\.md" "\\1.html" outputname ${inputbase}) + set(output ${CMAKE_BINARY_DIR}/doc/${MOD_DIRECTORY}/${outputname}) + + pandoc( + SOURCES ${doc} + OUTPUT ${output} + FROM markdown TO html5 + MAKE_DIRECTORY STANDALONE + FILTER $<TARGET_FILE:mdtohtml> + ) + + list(APPEND docoutputlist ${output}) + endforeach () + + add_custom_target(${MOD_TARGET}-doc DEPENDS ${docoutputlist}) + add_dependencies(${MOD_TARGET} ${MOD_TARGET}-doc) + endif () + endif () +endmacro() + +find_package(Pandoc REQUIRED) +find_package(OpenSSL REQUIRED) +find_package(Jansson REQUIRED) + +# --------------------------------------------------------- +# Base64 +# --------------------------------------------------------- + +define_module( + TARGET base64 + NAME Base64 + DIRECTORY Base64 + SOURCES + ${code_SOURCE_DIR}/C++/modules/Base64/Base64.cpp + ${code_SOURCE_DIR}/C++/modules/Base64/Base64.h ) -# GoogleTest library -add_subdirectory(extern) +# --------------------------------------------------------- +# Converter +# --------------------------------------------------------- + +# No tests yet -function(define_test name sources) - # The executable - add_executable( - ${name} - ${sources} - ) +# --------------------------------------------------------- +# Directory +# --------------------------------------------------------- - target_link_libraries(${name} gtest) - add_test(${name}-test ${name}) -endfunction() +define_module( + TARGET directory + NAME Directory + DIRECTORY Directory + SOURCES + ${code_SOURCE_DIR}/C++/modules/Directory/Directory.cpp + ${code_SOURCE_DIR}/C++/modules/Directory/Directory.h +) -option(WITH_BASE64 "Enable base64 tests" On) -option(WITH_CONVERTER "Enable converter tests" On) -option(WITH_DIRECTORY "Enable directory tests" On) -option(WITH_DRIVER "Enable SQL drivers tests" On) -option(WITH_DYNLIB "Enable DynLib tests" On) -option(WITH_FLAGS "Enable Flags tests" On) -option(WITH_HASH "Enable hash functions tests" On) -option(WITH_INI "Enable .ini parser" On) -option(WITH_JSON "Enable Jansson wrapper tests" On) -option(WITH_OPTIONPARSER "Enable option parser tests" On) -option(WITH_PACK "Enable pack functions" On) -option(WITH_PARSER "Enable parser tests (deprecated)" On) -option(WITH_SOCKETS "Enable sockets tests" On) -option(WITH_TREENODE "Enable treenode tests" On) -option(WITH_UTF8 "Enable Utf8 functions tests" On) -option(WITH_ZIP "Enable ZipArchive tests" On) +# --------------------------------------------------------- +# Driver +# --------------------------------------------------------- + +# No tests yet -if (UNIX) - option(WITH_XDG "Enable XDG standard directories tests" On) +# --------------------------------------------------------- +# Dynlib +# --------------------------------------------------------- + +if (WIN32) + set(EXTENSION ".dll") +elseif (UNIX) + set(EXTENSION ".so") +elseif (APPLE) + set(EXTENSION ".dylib") +else () + message(FATAL_ERROR "Unsupported platform") endif () -if (WITH_BASE64) - add_subdirectory(C++/Tests/Base64) -endif () +define_module( + TARGET dynlib + NAME Dynlib + DIRECTORY Dynlib + SOURCES + ${code_SOURCE_DIR}/C++/modules/Dynlib/Dynlib.cpp + ${code_SOURCE_DIR}/C++/modules/Dynlib/Dynlib.h + DOCS + ${code_SOURCE_DIR}/C++/doc/Dynlib/Home.md + ${code_SOURCE_DIR}/C++/doc/Dynlib/class/Dynlib.md + ${code_SOURCE_DIR}/C++/doc/Dynlib/class/Dynlib/Constructor.md + ${code_SOURCE_DIR}/C++/doc/Dynlib/class/Dynlib/Destructor.md + ${code_SOURCE_DIR}/C++/doc/Dynlib/class/Dynlib/Policy.md + ${code_SOURCE_DIR}/C++/doc/Dynlib/class/Dynlib/sym.md + ${code_SOURCE_DIR}/C++/doc/Dynlib/macro/DYNLIB_EXPORT.md +) -if (WITH_DIRECTORY) - add_subdirectory(C++/Tests/Directory) +if (CMAKE_SYSTEM_NAME MATCHES "Linux") + target_link_libraries(dynlib dl) endif () -if (WITH_DYNLIB) - add_subdirectory(C++/Tests/DynLib) -endif () +target_compile_definitions(dynlib PRIVATE EXTENSION=\"${EXTENSION}\") + +add_library(dynlib-plugin MODULE ${code_SOURCE_DIR}/C++/tests/Dynlib/Plugin.cpp) +set_target_properties(dynlib-plugin PROPERTIES PREFIX "") +target_include_directories(dynlib-plugin PRIVATE ${code_SOURCE_DIR}/C++/modules/Dynlib) + +# --------------------------------------------------------- +# Flags +# --------------------------------------------------------- + +define_module( + TARGET flags + NAME Flags + DIRECTORY Flags + SOURCES ${code_SOURCE_DIR}/C++/modules/Flags/Flags.h +) + +# --------------------------------------------------------- +# Hash +# --------------------------------------------------------- + +define_module( + TARGET hash + NAME Hash + DIRECTORY Hash + LIBRARIES ${OPENSSL_LIBRARIES} + INCLUDES ${OPENSSL_INCLUDE_DIR} + SOURCES + ${code_SOURCE_DIR}/C++/modules/Hash/Hash.cpp + ${code_SOURCE_DIR}/C++/modules/Hash/Hash.h +) + +# --------------------------------------------------------- +# Ini +# --------------------------------------------------------- -if (WITH_FLAGS) - add_subdirectory(C++/Tests/Flags) -endif () +define_module( + TARGET ini + NAME Ini + DIRECTORY Ini + SOURCES + ${code_SOURCE_DIR}/C++/modules/Ini/Ini.cpp + ${code_SOURCE_DIR}/C++/modules/Ini/Ini.h + RESOURCES + ${code_SOURCE_DIR}/C++/tests/Ini/configs/compact.conf + ${code_SOURCE_DIR}/C++/tests/Ini/configs/error-badcomment.conf + ${code_SOURCE_DIR}/C++/tests/Ini/configs/error-badsection.conf + ${code_SOURCE_DIR}/C++/tests/Ini/configs/error-lineassigment.conf + ${code_SOURCE_DIR}/C++/tests/Ini/configs/error-nosection.conf + ${code_SOURCE_DIR}/C++/tests/Ini/configs/includes.conf + ${code_SOURCE_DIR}/C++/tests/Ini/configs/multi.conf + ${code_SOURCE_DIR}/C++/tests/Ini/configs/novalue.conf + ${code_SOURCE_DIR}/C++/tests/Ini/configs/simple.conf + ${code_SOURCE_DIR}/C++/tests/Ini/configs/tokens.conf +) + +# --------------------------------------------------------- +# Json +# --------------------------------------------------------- + +define_module( + TARGET json + NAME Json + DIRECTORY Json + INCLUDES ${Jansson_INCLUDES} + LIBRARIES ${Jansson_LIBRARIES} + SOURCES + ${code_SOURCE_DIR}/C++/modules/Json/Json.cpp + ${code_SOURCE_DIR}/C++/modules/Json/Json.h + RESOURCES + ${code_SOURCE_DIR}/C++/tests/Json/data/array-all.json + ${code_SOURCE_DIR}/C++/tests/Json/data/array.json + ${code_SOURCE_DIR}/C++/tests/Json/data/object-all.json + ${code_SOURCE_DIR}/C++/tests/Json/data/object.json + ${code_SOURCE_DIR}/C++/tests/Json/data/simple.json +) + +# --------------------------------------------------------- +# OptionParser +# --------------------------------------------------------- + +define_module( + TARGET optionparser + NAME OptionParser + DIRECTORY OptionParser + SOURCES + ${code_SOURCE_DIR}/C++/modules/OptionParser/OptionParser.cpp + ${code_SOURCE_DIR}/C++/modules/OptionParser/OptionParser.h +) -if (WITH_HASH) - add_subdirectory(C++/Tests/Hash) -endif () +# --------------------------------------------------------- +# Pack +# --------------------------------------------------------- + +define_module( + TARGET pack + NAME Pack + DIRECTORY Pack + SOURCES + ${code_SOURCE_DIR}/C++/modules/Pack/Pack.cpp + ${code_SOURCE_DIR}/C++/modules/Pack/Pack.h +) + +# --------------------------------------------------------- +# Parser (DEPRECATED) +# --------------------------------------------------------- + +define_module( + TARGET parser + NAME Parser + DIRECTORY Parser + RESOURCES + ${code_SOURCE_DIR}/C++/tests/Parser/configs/simple.conf + ${code_SOURCE_DIR}/C++/tests/Parser/configs/multi.conf + SOURCES + ${code_SOURCE_DIR}/C++/modules/Parser/Parser.cpp + ${code_SOURCE_DIR}/C++/modules/Parser/Parser.h +) + +# --------------------------------------------------------- +# Sockets +# --------------------------------------------------------- -if (WITH_JSON) - add_subdirectory(C++/Tests/Json) -endif () +define_module( + TARGET socket + NAME Socket + DIRECTORY Socket + INCLUDES ${OPENSSL_INCLUDE_DIR} + LIBRARIES ${OPENSSL_LIBRARIES} + SOURCES + ${code_SOURCE_DIR}/C++/modules/Socket/SocketAddress.cpp + ${code_SOURCE_DIR}/C++/modules/Socket/SocketAddress.h + ${code_SOURCE_DIR}/C++/modules/Socket/Socket.cpp + ${code_SOURCE_DIR}/C++/modules/Socket/Socket.h + ${code_SOURCE_DIR}/C++/modules/Socket/SocketListener.cpp + ${code_SOURCE_DIR}/C++/modules/Socket/SocketListener.h + ${code_SOURCE_DIR}/C++/modules/Socket/SocketSsl.cpp + ${code_SOURCE_DIR}/C++/modules/Socket/SocketSsl.h + ${code_SOURCE_DIR}/C++/modules/Socket/SocketTcp.cpp + ${code_SOURCE_DIR}/C++/modules/Socket/SocketTcp.h + ${code_SOURCE_DIR}/C++/modules/Socket/SocketUdp.cpp + ${code_SOURCE_DIR}/C++/modules/Socket/SocketUdp.h +) + +# --------------------------------------------------------- +# Treenode +# --------------------------------------------------------- -if (WITH_INI) - add_subdirectory(C++/Tests/Ini) -endif () +define_module( + TARGET treenode + NAME Treenode + DIRECTORY Treenode + SOURCES + ${code_SOURCE_DIR}/C++/modules/Treenode/TreeNode.h +) + +# --------------------------------------------------------- +# Utf8 +# --------------------------------------------------------- -if (WITH_OPTIONPARSER) - add_subdirectory(C++/Tests/OptionParser) +define_module( + TARGET utf8 + NAME Utf8 + DIRECTORY Utf8 + SOURCES + ${code_SOURCE_DIR}/C++/modules/Utf8/Utf8.cpp + ${code_SOURCE_DIR}/C++/modules/Utf8/Utf8.h +) + +# --------------------------------------------------------- +# Xdg +# --------------------------------------------------------- + +if (UNIX) + define_module( + TARGET xdg + NAME Xdg + DIRECTORY Xdg + + SOURCES + ${code_SOURCE_DIR}/C++/modules/Xdg/Xdg.cpp + ${code_SOURCE_DIR}/C++/modules/Xdg/Xdg.h + ) endif () -if (WITH_PACK) - add_subdirectory(C++/Tests/Pack) -endif () +# --------------------------------------------------------- +# Zip +# --------------------------------------------------------- -if (WITH_PARSER) - add_subdirectory(C++/Tests/Parser) -endif () - -if (WITH_SOCKETS) - add_subdirectory(C++/Tests/Sockets) -endif () +find_package(ZIP REQUIRED) -if (WITH_TREENODE) - add_subdirectory(C++/Tests/TreeNode) -endif () - -if (WITH_UTF8) - add_subdirectory(C++/Tests/Utf8) -endif () - -if (WITH_XDG AND UNIX) - add_subdirectory(C++/Tests/Xdg) -endif () - -if (WITH_ZIP) - add_subdirectory(C++/Tests/Zip) -endif () +define_module( + TARGET zip + NAME Zip + DIRECTORY Zip + INCLUDES ${ZIP_INCLUDE_DIRS} + LIBRARIES ${ZIP_LIBRARIES} + RESOURCES + ${code_SOURCE_DIR}/C++/tests/Zip/data/data.txt + ${code_SOURCE_DIR}/C++/tests/Zip/data/stats.zip + SOURCES + ${code_SOURCE_DIR}/C++/modules/Zip/ZipArchive.cpp + ${code_SOURCE_DIR}/C++/modules/Zip/ZipArchive.h +)
--- a/cmake/FindCppunit.cmake Sun Mar 08 11:07:36 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,30 +0,0 @@ -# Find cppunit, this modules defines: -# CPPUNIT_INCLUDE_DIR, where to find cppunit/TestCase.h -# CPPUNIT_LIBRARY, where to find library -# CPPUNIT_FOUND, if it is found - -# find cppunit/TestCase.h -find_path( - CPPUNIT_INCLUDE_DIR cppunit/TestCase.h - PATHS - /usr/include - /usr/local/include -) - -# find libcppunit.so -find_library( - CPPUNIT_LIBRARY NAMES libcppunit cppunit - PATHS_SUFFIXES lib lib64 - PATHS - /usr/ - /usr/local/ -) - -include(FindPackageHandleStandardArgs) - -find_package_handle_standard_args( - Cppunit - REQUIRED_VARS CPPUNIT_INCLUDE_DIR CPPUNIT_LIBRARY -) - -mark_as_advanced(CPPUNIT_INCLUDE_DIR CPPUNIT_LIBRARY)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/cmake/FindPandoc.cmake Sun Mar 08 14:26:33 2015 +0100 @@ -0,0 +1,231 @@ +# FindPandoc +# ---------- +# +# Find Pandoc executable, this modules defines: +# +# Pandoc_EXECUTABLE, where to find pandoc's executable +# Pandoc_FOUND, if it is found +# Pandoc_VERSION, the version +# +# This module also defines the following macros: +# +# pandoc( +# SOURCES file1 [file2 ...] +# OUTPUT output +# [FROM format] +# [TO format] +# [TARGET target] +# [DEPENDS dependency ...] +# [ALL] +# [TOC] +# [STANDALONE] +# [MAKE_DIRECTORY] +# [TEMPLATE file] +# [FILTER filter] +# [HEADER header ...] +# [FOOTER footer ...] +# [BODY body ...] +# [VARIABLE var ...] +# [METADATA meta ...] +# [ARGS argument ...] +# [WORKING_DIRECTORY directory] +# ) +# +# The sources files are listed in the parameter SOURCES, all files are passed +# in the same order they are passed to that variable. +# +# The OUTPUT file is set with OUTPUT. It is generated only if one of the file +# has changed. +# +# The FROM (-f) and TO (-t) arguments specify respectively the source and +# destinations formats. +# +# If the parameter TARGET is set, then a target named `target` will be added +# with the OUTPUT file as the dependency but not listed as sources files. +# But the SOURCES files will be added as the target sources in the IDE. +# +# Optional dependencies can be added to the output command (not the target) with +# the DEPENDS parameter. +# +# If ALL is set and TARGET is also set, the target will be added to the ALL_BUILD. +# +# If TOC (--toc) is specified, a table of content will be automatically created. +# +# If STANDALONE (-s) is set, the compilation will assume that it is standalone +# and adds the necessary of the output format. +# +# Optional MAKE_DIRECTORY can be set to create the output directory before +# pandoc processes the file (recommended). +# +# The TEMPLATE parameter can be used to specify the formate template file. +# +# You can set a filter with the parameter FILTER. The filter will be added to +# the output dependencies so you can safely use CMake's targets. +# +# The HEADER (-H), FOOTER (-A) and BODY (-B) are copied verbatim before, just +# after and after the body respectively. They can be set more than once. +# +# You can pass variables (-V) and metadata (-M) to the parameters VARIABLE +# and METADATA, be sure to pass the same syntax as pandoc. (e.g VARIABLE foo=1) +# +# ARGS is an optional list of additional arguments to pass to pandoc. +# +# The parameter WORKING_DIRECTORY can be set to change the directory when pandoc +# is invoked. +# + +find_program( + Pandoc_EXECUTABLE + NAMES pandoc + DOC "Pandoc executable" +) + +include(FindPackageHandleStandardArgs) +include(CMakeParseArguments) + +# Extract the version +if (Pandoc_EXECUTABLE) + execute_process( + COMMAND ${Pandoc_EXECUTABLE} --version + OUTPUT_VARIABLE _pandoc_version_tmp + ) + + if (_pandoc_version_tmp MATCHES "^pandoc[^ ]* ([0-9]+\\.[0-9]+\\.[0-9]+)") + set(Pandoc_VERSION "${CMAKE_MATCH_1}") + endif () +endif () + +find_package_handle_standard_args( + Pandoc + FOUND_VAR Pandoc_FOUND + VERSION_VAR Pandoc_VERSION + REQUIRED_VARS Pandoc_EXECUTABLE +) + +if (Pandoc_FOUND) + function(pandoc) + set(options MAKE_DIRECTORY STANDALONE TOC) + set(oneValueArgs FILTER FROM TARGET TEMPLATE TO OUTPUT WORKING_DIRECTORY) + set(multiValueArgs ARGS FOOTER HEADER METADATA SOURCES VARIABLE) + + # + # The following variables will be set in that scope: + # _pandoc_arguments - List of all arguments that will passed to pandoc invocation. + # _pandoc_depends - List of all dependencies attached to the add_custom_command. + # _pandoc_mkdir - The mkdir command if MAKE_DIRECTORY is set + # _pandoc_output_base - The base output directory + # + cmake_parse_arguments(PANDOC "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + + # + # Output and sources are mandatory + # + if (NOT PANDOC_OUTPUT) + message(FATAL_ERROR "Please define OUTPUT") + elseif (NOT PANDOC_SOURCES) + message(FATAL_ERROR "Please defines SOURCES") + endif () + + # + # Handle the filter with care. + # + # 1. If it is a target, depend on it and use a generator + # expression to get its full path on the disk. + # 2. If it is not a target, just use the user provided path. + # + if (PANDOC_FILTER) + # If it is a target, add a dependency so that it is built + if (TARGET ${PANDOC_FILTER}) + list(APPEND _pandoc_arguments --filter "$<TARGET_FILE:${PANDOC_FILTER}>") + list(APPEND _pandoc_depends ${PANDOC_FILTER}) + else () + list(APPEND _pandoc_arguments --filter ${PANDOC_FILTER}) + endif () + endif () + + if (PANDOC_TOC) + list(APPEND _pandoc_arguments --toc) + endif () + if (PANDOC_STANDALONE) + list(APPEND _pandoc_arguments -s) + endif () + if (PANDOC_FROM) + list(APPEND _pandoc_arguments -f ${PANDOC_FROM}) + endif () + if (PANDOC_TO) + list(APPEND _pandoc_arguments -t ${PANDOC_TO}) + endif () + if (PANDOC_TEMPLATE) + list(APPEND _pandoc_arguments --template ${PANDOC_TEMPLATE}) + list(APPEND _pandoc_depends ${PANDOC_TEMPLATE}) + endif () + + # Header, footers and body + foreach (h ${PANDOC_HEADER}) + list(APPEND _pandoc_arguments -H ${h}) + list(APPEND _pandoc_depends ${h}) + endforeach () + foreach (b ${PANDOC_BODY}) + list(APPEND _pandoc_arguments -B ${b}) + list(APPEND _pandoc_depends ${b}) + endforeach () + foreach (f ${PANDOC_FOOTER}) + list(APPEND _pandoc_arguments -A ${f}) + list(APPEND _pandoc_depends ${f}) + endforeach () + + # Variables and metadata + foreach (var ${PANDOC_VARIABLE}) + list(APPEND _pandoc_arguments -V ${var}) + endforeach () + foreach (meta ${PANDOC_METADATA}) + list(APPEND _pandoc_arguments -M ${meta}) + endforeach () + + # Optional list of arguments + foreach (arg ${PANDOC_ARGS}) + list(APPEND _pandoc_arguments ${arg}) + endforeach () + + # Output and sources + list(APPEND _pandoc_arguments -o ${PANDOC_OUTPUT}) + + # + # The following variables are set within the loop: + # + # _pandoc_input - The absolute path to the input file. + # _pandoc_output_base - The base output directory. + # + foreach (s ${PANDOC_SOURCES}) + get_filename_component(_pandoc_input ${s} ABSOLUTE) + get_filename_component(_pandoc_output_base ${PANDOC_OUTPUT} DIRECTORY) + list(APPEND _pandoc_depends ${_pandoc_input}) + list(APPEND _pandoc_arguments ${_pandoc_input}) + endforeach () + + # Create the output directory if requested + if (PANDOC_MAKE_DIRECTORY) + set(_pandoc_mkdir ${CMAKE_COMMAND} -E make_directory ${_pandoc_output_base}) + endif () + + add_custom_command( + OUTPUT ${PANDOC_OUTPUT} + COMMAND ${_pandoc_mkdir} + COMMAND ${Pandoc_EXECUTABLE} ${_pandoc_arguments} + DEPENDS ${_pandoc_depends} ${PANDOC_DEPENDS} + WORKING_DIRECTORY ${PANDOC_WORKING_DIRECTORY} + VERBATIM + ) + + if (PANDOC_TARGET) + add_custom_target( + ${PANDOC_TARGET} ${PANDOC_ALL} + SOURCES ${_pandoc_depends} + DEPENDS ${PANDOC_OUTPUT} + WORKING_DIRECTORY ${PANDOC_WORKING_DIRECTORY} + ) + endif () + endfunction() +endif () + +mark_as_advanced(Pandoc_EXECUTABLE)
--- a/extern/gtest/CMakeLists.txt Sun Mar 08 11:07:36 2015 +0100 +++ b/extern/gtest/CMakeLists.txt Sun Mar 08 14:26:33 2015 +0100 @@ -28,6 +28,8 @@ ${GoogleTest_SOURCE_DIR} ) +target_compile_options(gtest PUBLIC "-Wno-missing-field-initializers") + if (NOT WIN32) target_link_libraries(gtest -pthread) endif ()
--- a/port/asprintf.c Sun Mar 08 11:07:36 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,129 +0,0 @@ -/* - * asprintf.c -- basic port of asprintf / vsprintf functions - * - * Copyright (c) 2011, 2012 David Demelier <markand@malikania.fr> - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <stdarg.h> - -/* - * The Microsoft implementation relies on the Win32 API function - * _vscprintf which count the number of characters needed. Otherwise the C99 - * function vsnprintf returns the number of character that would be printed. - * - * Finally, if we don't have C99, we use an optimistic function. - * - * The asprintf function is common to every implementations. - */ - -#if defined(_MSC_VER) - -/* - * Windows implementation. - */ -int -vasprintf(char **res, const char *fmt, va_list ap) -{ - int total = _vscprintf(fmt, ap); - - if (total < 0) { - *res = NULL; - return -1; - } - - if ((*res = (char *)malloc(sizeof (total) + 1)) == NULL) - return -1; - - return vsprintf_s(*res, total + 1, fmt, ap); -} - -#elif __STDC_VERSION__ >= 199901L - -/* - * C99 implementation using vsnprintf's return value. - */ -int -vasprintf(char **res, const char *fmt, va_list ap) -{ - int total, nwritten; - va_list copy; - - va_copy(copy, ap); - total = vsnprintf(NULL, 0, fmt, copy); - va_end(copy); - - if (total < 0) { - *res = NULL; - return total; - } - - if ((*res = malloc(total + 1)) == NULL) - return -1; - - if ((nwritten = vsnprintf(*res, total + 1, fmt, ap)) < 0) { - free(*res); - *res = NULL; - - return -1; - } - - return nwritten; -} - -#else - -/* - * Optimistic function fallback. - */ -int -vasprintf(char **res, const char *format, va_list ap) -{ - int rvalue, ok; - size_t base = 80; - - if ((*res = malloc(base)) == NULL) - return -1; - - ok = 0; - do { - rvalue = vsnprintf(*res, base, format, ap); - - if ((signed int)base <= rvalue || rvalue < 0) { - *res = realloc(*res, base * 2); - base *= 2; - } else - ok = 1; - } while (!ok && *res != NULL); - - return rvalue; -} - -#endif - -int -asprintf(char **res, const char *fmt, ...) -{ - va_list ap; - int rvalue; - - va_start(ap, fmt); - rvalue = vasprintf(res, fmt, ap); - va_end(ap); - - return rvalue; -}
--- a/port/asprintf.h Sun Mar 08 11:07:36 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,36 +0,0 @@ -/* - * asprintf.h -- basic port of asprintf / vsprintf functions - * - * Copyright (c) 2011, 2012 David Demelier <markand@malikania.fr> - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include <stdarg.h> - -#ifdef __cplusplus -extern "C" { -#endif - -#ifdef __GNUC__ -# define _asp_at_printf(i1, i2) __attribute__ ((format (printf, i1, i2))) -#else -# define _asp_at_printf(i1, i2) -#endif - -int asprintf(char **, const char *, ...) _asp_at_printf(2, 3); -int vasprintf(char **, const char *, va_list); - -#ifdef __cplusplus -} -#endif
--- a/port/err.c Sun Mar 08 11:07:36 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,114 +0,0 @@ -/* - * err.c -- formtted error messages (portable version) - * - * Copyright (c) 2011, 2012, David Demelier <markand@malikania.fr> - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include <stdio.h> -#include <stdlib.h> -#include <stdarg.h> -#include <string.h> -#include <errno.h> - -#include "err.h" - -/* - * These functions implements at least the same functions that can be found - * in the NetBSD err(3) man page without printing the programe name due to - * a portability issue. - */ - -void -err(int val, const char *fmt, ...) -{ - va_list ap; - - va_start(ap, fmt); - verr(val, fmt, ap); - va_end(ap); -} - -void -verr(int val, const char *fmt, va_list ap) -{ - if (fmt) { - vfprintf(stderr, fmt, ap); - fprintf(stderr, ": "); - } - - fprintf(stderr, "%s\n", strerror(errno)); - exit(val); -} - -void -errx(int val, const char *fmt, ...) -{ - va_list ap; - - va_start(ap, fmt); - verrx(val, fmt, ap); - va_end(ap); -} - -void -verrx(int val, const char *fmt, va_list ap) -{ - if (fmt) - vfprintf(stderr, fmt, ap); - - fprintf(stderr, "\n"); - - exit(val); -} - -void -warn(const char *fmt, ...) -{ - va_list ap; - - va_start(ap, fmt); - vwarn(fmt, ap); - va_end(ap); -} - -void -vwarn(const char *fmt, va_list ap) -{ - if (fmt) { - vfprintf(stderr, fmt, ap); - fprintf(stderr, ": "); - } - - fprintf(stderr, "%s\n", strerror(errno)); -} - -void -warnx(const char *fmt, ...) -{ - va_list ap; - - va_start(ap, fmt); - vwarnx(fmt, ap); - va_end(ap); -} - -void -vwarnx(const char *fmt, va_list ap) -{ - if (fmt) - vfprintf(stderr, fmt, ap); - - fprintf(stderr, "\n"); -}
--- a/port/err.h Sun Mar 08 11:07:36 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,82 +0,0 @@ -/* - * err.h -- formtted error messages (portable version) - * - * Copyright (c) 2011, 2012, David Demelier <markand@malikania.fr> - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#ifndef _ERR_H_ -#define _ERR_H_ - -#include <stdarg.h> - -#ifdef __cplusplus -extern "C" { -#endif - -/* - * Attribute noreturn may helps. This may produce a warning with GCC 4.5: - * - * int a; - * - * if ((a = getstate() < 0) - * errx(1, "State failed"); - * - * Because compilator does not know that errx will call exit may produce - * a warning like `a may be used uninitialized'. - */ - -#if defined(__GNUC__) -# define __at_noreturn __attribute__ ((noreturn)) -#elif defined(_MSC_VER) -# define __at_noreturn __declspec(noreturn) -#endif - -/* - * err() functions append the format message to the stderr FILE pointer. They - * also append the system error using strerror(errno). Then the functions exit - * with the error code given as first argument. - */ - -void err(int, const char *, ...) __at_noreturn; -void verr(int, const char *, va_list) __at_noreturn; - -/* - * errx() functions are similar to err() except that they do not append the - * system error message. - */ - -void errx(int, const char *, ...) __at_noreturn; -void verrx(int, const char *, va_list) __at_noreturn; - -/* - * warn() functions are similar to err() but they do not call exit(). - */ - -void warn(const char *, ...); -void vwarn(const char *, va_list); - -/* - * warnx() functions are similar to warn() except that they do not append the - * system error message. - */ - -void warnx(const char *, ...); -void vwarnx(const char *, va_list); - -#ifdef __cplusplus -} -#endif - -#endif /* _ERR_H_ */
--- a/port/extern/getopt.c Sun Mar 08 11:07:36 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,124 +0,0 @@ -/* - * getopt.c -- getopt(3) from FreeBSD (portable version) - */ - -/*- - * Copyright (c) 1987, 1993, 1994 - * The Regents of the University of California. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#include <stdio.h> -#include <stdlib.h> -#include <string.h> - -int opterr = 1, /* if error message should be printed */ - optind = 1, /* index into parent argv vector */ - optopt, /* character checked for validity */ - optreset; /* reset getopt */ -char *optarg; /* argument associated with option */ - -#define BADCH (int)'?' -#define BADARG (int)':' -#define EMSG "" - -/* - * getopt -- - * Parse argc/argv argument vector. - */ -int -getopt(nargc, nargv, ostr) - int nargc; - char * const nargv[]; - const char *ostr; -{ - static char *place = EMSG; /* option letter processing */ - char *oli; /* option letter list index */ - - if (optreset || *place == 0) { /* update scanning pointer */ - optreset = 0; - place = nargv[optind]; - if (optind >= nargc || *place++ != '-') { - /* Argument is absent or is not an option */ - place = EMSG; - return (-1); - } - optopt = *place++; - if (optopt == '-' && *place == 0) { - /* "--" => end of options */ - ++optind; - place = EMSG; - return (-1); - } - if (optopt == 0) { - /* Solitary '-', treat as a '-' option - if the program (eg su) is looking for it. */ - place = EMSG; - if (strchr(ostr, '-') == NULL) - return (-1); - optopt = '-'; - } - } else - optopt = *place++; - - /* See if option letter is one the caller wanted... */ - if (optopt == ':' || (oli = strchr(ostr, optopt)) == NULL) { - if (*place == 0) - ++optind; - if (opterr && *ostr != ':') - (void)fprintf(stderr, - "illegal option -- %c\n", optopt); - return (BADCH); - } - - /* Does this option need an argument? */ - if (oli[1] != ':') { - /* don't need argument */ - optarg = NULL; - if (*place == 0) - ++optind; - } else { - /* Option-argument is either the rest of this argument or the - entire next argument. */ - if (*place) - optarg = place; - else if (nargc > ++optind) - optarg = nargv[optind]; - else { - /* option-argument absent */ - place = EMSG; - if (*ostr == ':') - return (BADARG); - if (opterr) - (void)fprintf(stderr, - "option requires an argument -- %c\n", optopt); - return (BADCH); - } - place = EMSG; - ++optind; - } - return (optopt); /* return option letter */ -}
--- a/port/extern/getopt.h Sun Mar 08 11:07:36 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,50 +0,0 @@ -/* - * getopt.h -- getopt(3) from FreeBSD (portable version) - */ - -/*- - * Copyright (c) 2000 The NetBSD Foundation, Inc. - * All rights reserved. - * - * This code is derived from software contributed to The NetBSD Foundation - * by Dieter Baron and Thomas Klausner. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the NetBSD - * Foundation, Inc. and its contributors. - * 4. Neither the name of The NetBSD Foundation nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS - * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED - * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS - * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef _GETOPT_H_ -#define _GETOPT_H_ - -int getopt(int, char * const [], const char *); - -extern char *optarg; /* getopt(3) external variables */ -extern int optind, opterr, optopt; -extern int optreset; /* getopt(3) external variable */ - -#endif /* !_GETOPT_H_ */
--- a/port/extern/queue.h Sun Mar 08 11:07:36 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,584 +0,0 @@ -/* - * queue.h -- queue(3) from FreeBSD (portable version) - */ - -/*- - * Copyright (c) 1991, 1993 - * The Regents of the University of California. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * @(#)queue.h 8.5 (Berkeley) 8/20/94 - * $FreeBSD: src/sys/sys/queue.h,v 1.72.2.4 2011/05/24 16:04:35 mdf Exp $ - */ - -#ifndef _SYS_QUEUE_H_ -#define _SYS_QUEUE_H_ - -/* - * This file defines four types of data structures: singly-linked lists, - * singly-linked tail queues, lists and tail queues. - * - * A singly-linked list is headed by a single forward pointer. The elements - * are singly linked for minimum space and pointer manipulation overhead at - * the expense of O(n) removal for arbitrary elements. New elements can be - * added to the list after an existing element or at the head of the list. - * Elements being removed from the head of the list should use the explicit - * macro for this purpose for optimum efficiency. A singly-linked list may - * only be traversed in the forward direction. Singly-linked lists are ideal - * for applications with large datasets and few or no removals or for - * implementing a LIFO queue. - * - * A singly-linked tail queue is headed by a pair of pointers, one to the - * head of the list and the other to the tail of the list. The elements are - * singly linked for minimum space and pointer manipulation overhead at the - * expense of O(n) removal for arbitrary elements. New elements can be added - * to the list after an existing element, at the head of the list, or at the - * end of the list. Elements being removed from the head of the tail queue - * should use the explicit macro for this purpose for optimum efficiency. - * A singly-linked tail queue may only be traversed in the forward direction. - * Singly-linked tail queues are ideal for applications with large datasets - * and few or no removals or for implementing a FIFO queue. - * - * A list is headed by a single forward pointer (or an array of forward - * pointers for a hash table header). The elements are doubly linked - * so that an arbitrary element can be removed without a need to - * traverse the list. New elements can be added to the list before - * or after an existing element or at the head of the list. A list - * may only be traversed in the forward direction. - * - * A tail queue is headed by a pair of pointers, one to the head of the - * list and the other to the tail of the list. The elements are doubly - * linked so that an arbitrary element can be removed without a need to - * traverse the list. New elements can be added to the list before or - * after an existing element, at the head of the list, or at the end of - * the list. A tail queue may be traversed in either direction. - * - * For details on the use of these macros, see the queue(3) manual page. - * - * - * SLIST LIST STAILQ TAILQ - * _HEAD + + + + - * _HEAD_INITIALIZER + + + + - * _ENTRY + + + + - * _INIT + + + + - * _EMPTY + + + + - * _FIRST + + + + - * _NEXT + + + + - * _PREV - - - + - * _LAST - - + + - * _FOREACH + + + + - * _FOREACH_SAFE + + + + - * _FOREACH_REVERSE - - - + - * _FOREACH_REVERSE_SAFE - - - + - * _INSERT_HEAD + + + + - * _INSERT_BEFORE - + - + - * _INSERT_AFTER + + + + - * _INSERT_TAIL - - + + - * _CONCAT - - + + - * _REMOVE_AFTER + - + - - * _REMOVE_HEAD + - + - - * _REMOVE + + + + - * _SWAP + + + + - * - */ - -/* - * winnt.h includes some variables like SLIST_ENTRY, so undef all. If this - * file is used it is probably because the user want is instead of - * windows ones. - */ - -#undef SLIST_EMPTY -#undef SLIST_ENTRY -#undef SLIST_FIRST -#undef SLIST_FOREACH -#undef SLIST_FOREACH_SAFE -#undef SLIST_HEAD -#undef SLIST_HEAD_INITIALIZER -#undef SLIST_INIT -#undef SLIST_INSERT_AFTER -#undef SLIST_INSERT_HEAD -#undef SLIST_NEXT -#undef SLIST_REMOVE_AFTER -#undef SLIST_REMOVE_HEAD -#undef SLIST_REMOVE -#undef SLIST_SWAP -#undef STAILQ_CONCAT -#undef STAILQ_EMPTY -#undef STAILQ_ENTRY -#undef STAILQ_FIRST -#undef STAILQ_FOREACH -#undef STAILQ_FOREACH_SAFE -#undef STAILQ_HEAD -#undef STAILQ_HEAD_INITIALIZER -#undef STAILQ_INIT -#undef STAILQ_INSERT_AFTER -#undef STAILQ_INSERT_HEAD -#undef STAILQ_INSERT_TAIL -#undef STAILQ_LAST -#undef STAILQ_NEXT -#undef STAILQ_REMOVE_AFTER -#undef STAILQ_REMOVE_HEAD -#undef STAILQ_REMOVE -#undef STAILQ_SWAP -#undef LIST_EMPTY -#undef LIST_ENTRY -#undef LIST_FIRST -#undef LIST_FOREACH -#undef LIST_FOREACH_SAFE -#undef LIST_HEAD -#undef LIST_HEAD_INITIALIZER -#undef LIST_INIT -#undef LIST_INSERT_AFTER -#undef LIST_INSERT_BEFORE -#undef LIST_INSERT_HEAD -#undef LIST_NEXT -#undef LIST_REMOVE -#undef LIST_SWAP -#undef TAILQ_CONCAT -#undef TAILQ_EMPTY -#undef TAILQ_ENTRY -#undef TAILQ_FIRST -#undef TAILQ_FOREACH -#undef TAILQ_FOREACH_SAFE -#undef TAILQ_FOREACH_REVERSE -#undef TAILQ_FOREACH_REVERSE_SAFE -#undef TAILQ_HEAD -#undef TAILQ_HEAD_INITIALIZER -#undef TAILQ_INIT -#undef TAILQ_INSERT_AFTER -#undef TAILQ_INSERT_BEFORE -#undef TAILQ_INSERT_HEAD -#undef TAILQ_INSERT_TAIL -#undef TAILQ_LAST -#undef TAILQ_NEXT -#undef TAILQ_PREV -#undef TAILQ_REMOVE -#undef TAILQ_SWAP - -/* - * Singly-linked List declarations. - */ -#define SLIST_HEAD(name, type) \ -struct name { \ - struct type *slh_first; /* first element */ \ -} - -#define SLIST_HEAD_INITIALIZER(head) \ - { NULL } - -#define SLIST_ENTRY(type) \ -struct { \ - struct type *sle_next; /* next element */ \ -} - -/* - * Singly-linked List functions. - */ -#define SLIST_EMPTY(head) ((head)->slh_first == NULL) - -#define SLIST_FIRST(head) ((head)->slh_first) - -#define SLIST_FOREACH(var, head, field) \ - for ((var) = SLIST_FIRST((head)); \ - (var); \ - (var) = SLIST_NEXT((var), field)) - -#define SLIST_FOREACH_SAFE(var, head, field, tvar) \ - for ((var) = SLIST_FIRST((head)); \ - (var) && ((tvar) = SLIST_NEXT((var), field), 1); \ - (var) = (tvar)) - -#define SLIST_FOREACH_PREVPTR(var, varp, head, field) \ - for ((varp) = &SLIST_FIRST((head)); \ - ((var) = *(varp)) != NULL; \ - (varp) = &SLIST_NEXT((var), field)) - -#define SLIST_INIT(head) do { \ - SLIST_FIRST((head)) = NULL; \ -} while (0) - -#define SLIST_INSERT_AFTER(slistelm, elm, field) do { \ - SLIST_NEXT((elm), field) = SLIST_NEXT((slistelm), field); \ - SLIST_NEXT((slistelm), field) = (elm); \ -} while (0) - -#define SLIST_INSERT_HEAD(head, elm, field) do { \ - SLIST_NEXT((elm), field) = SLIST_FIRST((head)); \ - SLIST_FIRST((head)) = (elm); \ -} while (0) - -#define SLIST_NEXT(elm, field) ((elm)->field.sle_next) - -#define SLIST_REMOVE(head, elm, type, field) do { \ - if (SLIST_FIRST((head)) == (elm)) { \ - SLIST_REMOVE_HEAD((head), field); \ - } \ - else { \ - struct type *curelm = SLIST_FIRST((head)); \ - while (SLIST_NEXT(curelm, field) != (elm)) \ - curelm = SLIST_NEXT(curelm, field); \ - SLIST_REMOVE_AFTER(curelm, field); \ - } \ -} while (0) - -#define SLIST_REMOVE_AFTER(elm, field) do { \ - SLIST_NEXT(elm, field) = \ - SLIST_NEXT(SLIST_NEXT(elm, field), field); \ -} while (0) - -#define SLIST_REMOVE_HEAD(head, field) do { \ - SLIST_FIRST((head)) = SLIST_NEXT(SLIST_FIRST((head)), field); \ -} while (0) - -#define SLIST_SWAP(head1, head2, type) do { \ - struct type *swap_first = SLIST_FIRST(head1); \ - SLIST_FIRST(head1) = SLIST_FIRST(head2); \ - SLIST_FIRST(head2) = swap_first; \ -} while (0) - -/* - * Singly-linked Tail queue declarations. - */ -#define STAILQ_HEAD(name, type) \ -struct name { \ - struct type *stqh_first;/* first element */ \ - struct type **stqh_last;/* addr of last next element */ \ -} - -#define STAILQ_HEAD_INITIALIZER(head) \ - { NULL, &(head).stqh_first } - -#define STAILQ_ENTRY(type) \ -struct { \ - struct type *stqe_next; /* next element */ \ -} - -/* - * Singly-linked Tail queue functions. - */ -#define STAILQ_CONCAT(head1, head2) do { \ - if (!STAILQ_EMPTY((head2))) { \ - *(head1)->stqh_last = (head2)->stqh_first; \ - (head1)->stqh_last = (head2)->stqh_last; \ - STAILQ_INIT((head2)); \ - } \ -} while (0) - -#define STAILQ_EMPTY(head) ((head)->stqh_first == NULL) - -#define STAILQ_FIRST(head) ((head)->stqh_first) - -#define STAILQ_FOREACH(var, head, field) \ - for((var) = STAILQ_FIRST((head)); \ - (var); \ - (var) = STAILQ_NEXT((var), field)) - - -#define STAILQ_FOREACH_SAFE(var, head, field, tvar) \ - for ((var) = STAILQ_FIRST((head)); \ - (var) && ((tvar) = STAILQ_NEXT((var), field), 1); \ - (var) = (tvar)) - -#define STAILQ_INIT(head) do { \ - STAILQ_FIRST((head)) = NULL; \ - (head)->stqh_last = &STAILQ_FIRST((head)); \ -} while (0) - -#define STAILQ_INSERT_AFTER(head, tqelm, elm, field) do { \ - if ((STAILQ_NEXT((elm), field) = STAILQ_NEXT((tqelm), field)) == NULL)\ - (head)->stqh_last = &STAILQ_NEXT((elm), field); \ - STAILQ_NEXT((tqelm), field) = (elm); \ -} while (0) - -#define STAILQ_INSERT_HEAD(head, elm, field) do { \ - if ((STAILQ_NEXT((elm), field) = STAILQ_FIRST((head))) == NULL) \ - (head)->stqh_last = &STAILQ_NEXT((elm), field); \ - STAILQ_FIRST((head)) = (elm); \ -} while (0) - -#define STAILQ_INSERT_TAIL(head, elm, field) do { \ - STAILQ_NEXT((elm), field) = NULL; \ - *(head)->stqh_last = (elm); \ - (head)->stqh_last = &STAILQ_NEXT((elm), field); \ -} while (0) - -#define STAILQ_LAST(head, type, field) \ - (STAILQ_EMPTY((head)) ? \ - NULL : \ - ((struct type *)(void *) \ - ((char *)((head)->stqh_last) - __offsetof(struct type, field)))) - -#define STAILQ_NEXT(elm, field) ((elm)->field.stqe_next) - -#define STAILQ_REMOVE(head, elm, type, field) do { \ - if (STAILQ_FIRST((head)) == (elm)) { \ - STAILQ_REMOVE_HEAD((head), field); \ - } \ - else { \ - struct type *curelm = STAILQ_FIRST((head)); \ - while (STAILQ_NEXT(curelm, field) != (elm)) \ - curelm = STAILQ_NEXT(curelm, field); \ - STAILQ_REMOVE_AFTER(head, curelm, field); \ - } \ -} while (0) - -#define STAILQ_REMOVE_AFTER(head, elm, field) do { \ - if ((STAILQ_NEXT(elm, field) = \ - STAILQ_NEXT(STAILQ_NEXT(elm, field), field)) == NULL) \ - (head)->stqh_last = &STAILQ_NEXT((elm), field); \ -} while (0) - -#define STAILQ_REMOVE_HEAD(head, field) do { \ - if ((STAILQ_FIRST((head)) = \ - STAILQ_NEXT(STAILQ_FIRST((head)), field)) == NULL) \ - (head)->stqh_last = &STAILQ_FIRST((head)); \ -} while (0) - -#define STAILQ_SWAP(head1, head2, type) do { \ - struct type *swap_first = STAILQ_FIRST(head1); \ - struct type **swap_last = (head1)->stqh_last; \ - STAILQ_FIRST(head1) = STAILQ_FIRST(head2); \ - (head1)->stqh_last = (head2)->stqh_last; \ - STAILQ_FIRST(head2) = swap_first; \ - (head2)->stqh_last = swap_last; \ - if (STAILQ_EMPTY(head1)) \ - (head1)->stqh_last = &STAILQ_FIRST(head1); \ - if (STAILQ_EMPTY(head2)) \ - (head2)->stqh_last = &STAILQ_FIRST(head2); \ -} while (0) - -/* - * List declarations. - */ -#define LIST_HEAD(name, type) \ -struct name { \ - struct type *lh_first; /* first element */ \ -} - -#define LIST_HEAD_INITIALIZER(head) \ - { NULL } - -#define LIST_ENTRY(type) \ -struct { \ - struct type *le_next; /* next element */ \ - struct type **le_prev; /* address of previous next element */ \ -} - -/* - * List functions. - */ - -#define LIST_EMPTY(head) ((head)->lh_first == NULL) - -#define LIST_FIRST(head) ((head)->lh_first) - -#define LIST_FOREACH(var, head, field) \ - for ((var) = LIST_FIRST((head)); \ - (var); \ - (var) = LIST_NEXT((var), field)) - -#define LIST_FOREACH_SAFE(var, head, field, tvar) \ - for ((var) = LIST_FIRST((head)); \ - (var) && ((tvar) = LIST_NEXT((var), field), 1); \ - (var) = (tvar)) - -#define LIST_INIT(head) do { \ - LIST_FIRST((head)) = NULL; \ -} while (0) - -#define LIST_INSERT_AFTER(listelm, elm, field) do { \ - if ((LIST_NEXT((elm), field) = LIST_NEXT((listelm), field)) != NULL)\ - LIST_NEXT((listelm), field)->field.le_prev = \ - &LIST_NEXT((elm), field); \ - LIST_NEXT((listelm), field) = (elm); \ - (elm)->field.le_prev = &LIST_NEXT((listelm), field); \ -} while (0) - -#define LIST_INSERT_BEFORE(listelm, elm, field) do { \ - (elm)->field.le_prev = (listelm)->field.le_prev; \ - LIST_NEXT((elm), field) = (listelm); \ - *(listelm)->field.le_prev = (elm); \ - (listelm)->field.le_prev = &LIST_NEXT((elm), field); \ -} while (0) - -#define LIST_INSERT_HEAD(head, elm, field) do { \ - if ((LIST_NEXT((elm), field) = LIST_FIRST((head))) != NULL) \ - LIST_FIRST((head))->field.le_prev = &LIST_NEXT((elm), field);\ - LIST_FIRST((head)) = (elm); \ - (elm)->field.le_prev = &LIST_FIRST((head)); \ -} while (0) - -#define LIST_NEXT(elm, field) ((elm)->field.le_next) - -#define LIST_REMOVE(elm, field) do { \ - if (LIST_NEXT((elm), field) != NULL) \ - LIST_NEXT((elm), field)->field.le_prev = \ - (elm)->field.le_prev; \ - *(elm)->field.le_prev = LIST_NEXT((elm), field); \ -} while (0) - -#define LIST_SWAP(head1, head2, type, field) do { \ - struct type *swap_tmp = LIST_FIRST((head1)); \ - LIST_FIRST((head1)) = LIST_FIRST((head2)); \ - LIST_FIRST((head2)) = swap_tmp; \ - if ((swap_tmp = LIST_FIRST((head1))) != NULL) \ - swap_tmp->field.le_prev = &LIST_FIRST((head1)); \ - if ((swap_tmp = LIST_FIRST((head2))) != NULL) \ - swap_tmp->field.le_prev = &LIST_FIRST((head2)); \ -} while (0) - -/* - * Tail queue declarations. - */ -#define TAILQ_HEAD(name, type) \ -struct name { \ - struct type *tqh_first; /* first element */ \ - struct type **tqh_last; /* addr of last next element */ \ -} - -#define TAILQ_HEAD_INITIALIZER(head) \ - { NULL, &(head).tqh_first } - -#define TAILQ_ENTRY(type) \ -struct { \ - struct type *tqe_next; /* next element */ \ - struct type **tqe_prev; /* address of previous next element */ \ -} - -/* - * Tail queue functions. - */ - -#define TAILQ_CONCAT(head1, head2, field) do { \ - if (!TAILQ_EMPTY(head2)) { \ - *(head1)->tqh_last = (head2)->tqh_first; \ - (head2)->tqh_first->field.tqe_prev = (head1)->tqh_last; \ - (head1)->tqh_last = (head2)->tqh_last; \ - TAILQ_INIT((head2)); \ - } \ -} while (0) - -#define TAILQ_EMPTY(head) ((head)->tqh_first == NULL) - -#define TAILQ_FIRST(head) ((head)->tqh_first) - -#define TAILQ_FOREACH(var, head, field) \ - for ((var) = TAILQ_FIRST((head)); \ - (var); \ - (var) = TAILQ_NEXT((var), field)) - -#define TAILQ_FOREACH_SAFE(var, head, field, tvar) \ - for ((var) = TAILQ_FIRST((head)); \ - (var) && ((tvar) = TAILQ_NEXT((var), field), 1); \ - (var) = (tvar)) - -#define TAILQ_FOREACH_REVERSE(var, head, headname, field) \ - for ((var) = TAILQ_LAST((head), headname); \ - (var); \ - (var) = TAILQ_PREV((var), headname, field)) - -#define TAILQ_FOREACH_REVERSE_SAFE(var, head, headname, field, tvar) \ - for ((var) = TAILQ_LAST((head), headname); \ - (var) && ((tvar) = TAILQ_PREV((var), headname, field), 1); \ - (var) = (tvar)) - -#define TAILQ_INIT(head) do { \ - TAILQ_FIRST((head)) = NULL; \ - (head)->tqh_last = &TAILQ_FIRST((head)); \ -} while (0) - -#define TAILQ_INSERT_AFTER(head, listelm, elm, field) do { \ - if ((TAILQ_NEXT((elm), field) = TAILQ_NEXT((listelm), field)) != NULL)\ - TAILQ_NEXT((elm), field)->field.tqe_prev = \ - &TAILQ_NEXT((elm), field); \ - else { \ - (head)->tqh_last = &TAILQ_NEXT((elm), field); \ - } \ - TAILQ_NEXT((listelm), field) = (elm); \ - (elm)->field.tqe_prev = &TAILQ_NEXT((listelm), field); \ -} while (0) - -#define TAILQ_INSERT_BEFORE(listelm, elm, field) do { \ - (elm)->field.tqe_prev = (listelm)->field.tqe_prev; \ - TAILQ_NEXT((elm), field) = (listelm); \ - *(listelm)->field.tqe_prev = (elm); \ - (listelm)->field.tqe_prev = &TAILQ_NEXT((elm), field); \ -} while (0) - -#define TAILQ_INSERT_HEAD(head, elm, field) do { \ - if ((TAILQ_NEXT((elm), field) = TAILQ_FIRST((head))) != NULL) \ - TAILQ_FIRST((head))->field.tqe_prev = \ - &TAILQ_NEXT((elm), field); \ - else \ - (head)->tqh_last = &TAILQ_NEXT((elm), field); \ - TAILQ_FIRST((head)) = (elm); \ - (elm)->field.tqe_prev = &TAILQ_FIRST((head)); \ -} while (0) - -#define TAILQ_INSERT_TAIL(head, elm, field) do { \ - TAILQ_NEXT((elm), field) = NULL; \ - (elm)->field.tqe_prev = (head)->tqh_last; \ - *(head)->tqh_last = (elm); \ - (head)->tqh_last = &TAILQ_NEXT((elm), field); \ -} while (0) - -#define TAILQ_LAST(head, headname) \ - (*(((struct headname *)((head)->tqh_last))->tqh_last)) - -#define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next) - -#define TAILQ_PREV(elm, headname, field) \ - (*(((struct headname *)((elm)->field.tqe_prev))->tqh_last)) - -#define TAILQ_REMOVE(head, elm, field) do { \ - if ((TAILQ_NEXT((elm), field)) != NULL) \ - TAILQ_NEXT((elm), field)->field.tqe_prev = \ - (elm)->field.tqe_prev; \ - else { \ - (head)->tqh_last = (elm)->field.tqe_prev; \ - } \ - *(elm)->field.tqe_prev = TAILQ_NEXT((elm), field); \ -} while (0) - -#define TAILQ_SWAP(head1, head2, type, field) do { \ - struct type *swap_first = (head1)->tqh_first; \ - struct type **swap_last = (head1)->tqh_last; \ - (head1)->tqh_first = (head2)->tqh_first; \ - (head1)->tqh_last = (head2)->tqh_last; \ - (head2)->tqh_first = swap_first; \ - (head2)->tqh_last = swap_last; \ - if ((swap_first = (head1)->tqh_first) != NULL) \ - swap_first->field.tqe_prev = &(head1)->tqh_first; \ - else \ - (head1)->tqh_last = &(head1)->tqh_first; \ - if ((swap_first = (head2)->tqh_first) != NULL) \ - swap_first->field.tqe_prev = &(head2)->tqh_first; \ - else \ - (head2)->tqh_last = &(head2)->tqh_first; \ -} while (0) - -#endif /* !_SYS_QUEUE_H_ */
--- a/port/extern/stdbool.h Sun Mar 08 11:07:36 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,44 +0,0 @@ -/* - * Copyright (c) 2000 Jeroen Ruigrok van der Werven <asmodai@FreeBSD.org> - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * $FreeBSD: src/include/stdbool.h,v 1.7.28.1.2.1 2011/11/11 04:20:22 kensmith Exp $ - */ - -#ifndef _STDBOOL_H_ -#define _STDBOOL_H_ - -#define __bool_true_false_are_defined 1 - -#ifndef __cplusplus - -#define false 0 -#define true 1 - -/* Very portable bool version */ -typedef char bool; - -#endif /* !__cplusplus */ - -#endif /* !_STDBOOL_H_ */
--- a/port/extern/strlcat.c Sun Mar 08 11:07:36 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,53 +0,0 @@ -/* - * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com> - * Copyright (c) 2012 David Demelier <markand@malikania.fr> - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include <stddef.h> - -/* - * Appends src to string dst of size siz (unlike strncat, siz is the - * full size of dst, not space left). At most siz-1 characters - * will be copied. Always NUL terminates (unless siz <= strlen(dst)). - * Returns strlen(src) + MIN(siz, strlen(initial dst)). - * If retval >= siz, truncation occurred. - */ -size_t -strlcat(char *dst, const char *src, size_t siz) -{ - char *d = dst; - const char *s = src; - size_t n = siz; - size_t dlen; - - /* Find the end of dst and adjust bytes left but don't go past end */ - while (n-- != 0 && *d != '\0') - d++; - dlen = d - dst; - n = siz - dlen; - - if (n == 0) - return(dlen + strlen(s)); - while (*s != '\0') { - if (n != 1) { - *d++ = *s; - n--; - } - s++; - } - *d = '\0'; - - return(dlen + (s - src)); /* count does not include NUL */ -}
--- a/port/extern/strlcpy.c Sun Mar 08 11:07:36 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,49 +0,0 @@ -/* - * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com> - * Copyright (c) 2012 David Demelier <markand@malikania.fr> - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include <stddef.h> - -/* - * Copy src to string dst of size siz. At most siz-1 characters - * will be copied. Always NUL terminates (unless siz == 0). - * Returns strlen(src); if retval >= siz, truncation occurred. - */ -size_t -strlcpy(char *dst, const char *src, size_t siz) -{ - char *d = dst; - const char *s = src; - size_t n = siz; - - /* Copy as many bytes as will fit */ - if (n != 0) { - while (--n != 0) { - if ((*d++ = *s++) == '\0') - break; - } - } - - /* Not enough room in dst, add NUL and traverse rest of src */ - if (n == 0) { - if (siz != 0) - *d = '\0'; /* NUL-terminate dst */ - while (*s++) - ; - } - - return(s - src - 1); /* count does not include NUL */ -}
--- a/port/setprogname.c Sun Mar 08 11:07:36 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,45 +0,0 @@ -/* - * setprogname.c -- get or set the program name - * - * Copyright (c) 2011, 2012 David Demelier <markand@malikania.fr> - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include <stdio.h> -#include <stdlib.h> -#include <string.h> - -/* - * Do not return NULL if the developer didn't call setprogname to - * prevent useless segfault. - */ -static const char *g_pname = ""; - -void -setprogname(const char *progname) -{ - const char *p; - - /* Seek last / or \ on windows */ - if ((p = strrchr(progname, '/')) || (p = strrchr(progname, '\\'))) - g_pname = &p[1]; - else - g_pname = progname; -} - -const char * -getprogname(void) -{ - return g_pname; -}
--- a/port/setprogname.h Sun Mar 08 11:07:36 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,28 +0,0 @@ -/* - * setprogname.h -- get or set the program name - * - * Copyright (c) 2011, 2012 David Demelier <markand@malikania.fr> - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#ifdef __cplusplus -extern "C" { -#endif - -void setprogname(const char *); -const char *getprogname(void); - -#ifdef __cplusplus -} -#endif
--- a/port/strdup.c Sun Mar 08 11:07:36 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,35 +0,0 @@ -/* - * strdup.c -- duplicate a string - * - * Copyright (c) 2011, 2012, David Demelier <markand@malikania.fr> - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include <stdio.h> -#include <stdlib.h> -#include <string.h> - -char * -strdup(const char *src) -{ - char *dst; - size_t len; - - len = strlen(src); - - if ((dst = calloc(len + 1, sizeof (char))) == NULL) - return NULL; - - return strcpy(dst, src); -}
--- a/port/strdup.h Sun Mar 08 11:07:36 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,28 +0,0 @@ -/* - * strdup.h -- duplicate a string - * - * Copyright (c) 2011, 2012, David Demelier <markand@malikania.fr> - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#ifdef __cplusplus -extern "C" { -#endif - -char *strdup(const char *); - -#ifdef __cplusplus -} -#endif -
--- a/port/strndup.c Sun Mar 08 11:07:36 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,36 +0,0 @@ -/* - * strndup.c -- duplicate a string - * - * Copyright (c) 2011, 2012, David Demelier <markand@malikania.fr> - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include <stdio.h> -#include <stdlib.h> -#include <string.h> - -char * -strndup(const char *src, size_t len) -{ - size_t tocopy; - char *dst; - - for (tocopy = 0; tocopy < len && src[tocopy] != '\0'; ++tocopy) - continue; - - if ((dst = calloc(tocopy + 1, 1)) != NULL) - strncpy(dst, src, tocopy); - - return dst; -}
--- a/port/strndup.h Sun Mar 08 11:07:36 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,27 +0,0 @@ -/* - * strndup.h -- duplicate a string - * - * Copyright (c) 2011, 2012, David Demelier <markand@malikania.fr> - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#ifdef __cplusplus -extern "C" { -#endif - -char *strndup(const char *, size_t); - -#ifdef __cplusplus -} -#endif
--- a/port/strsep.c Sun Mar 08 11:07:36 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,40 +0,0 @@ -/* - * strsep.c -- separate a string by delimiters - * - * Copyright (c) 2011, 2012, David Demelier <markand@malikania.fr> - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include <stdlib.h> -#include <string.h> - -char * -strsep(char **stringp, const char *delim) -{ - char *item, *ptr; - - if (*stringp == NULL || delim[0] == '\0') - return NULL; - - item = *stringp; - if ((ptr = strpbrk(*stringp, delim)) == NULL) { - *stringp = NULL; - return item; - } - - *ptr = '\0'; - *stringp = ptr + 1; - - return item; -}
--- a/port/strsep.h Sun Mar 08 11:07:36 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,27 +0,0 @@ -/* - * strsep.h -- separate a string by delimiters - * - * Copyright (c) 2011, 2012, David Demelier <markand@malikania.fr> - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#ifdef __cplusplus -extern "C" { -#endif - -char *strsep(char **, const char *); - -#ifdef __cplusplus -} -#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/CMakeLists.txt Sun Mar 08 14:26:33 2015 +0100 @@ -0,0 +1,21 @@ +# +# CMakeLists.txt -- code building for common code +# +# Copyright (c) 2013, 2014 David Demelier <markand@malikania.fr> +# +# Permission to use, copy, modify, and/or distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +# + +project(tools) + +add_executable(mdtohtml mdtohtml.cpp)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/mdtohtml.cpp Sun Mar 08 14:26:33 2015 +0100 @@ -0,0 +1,38 @@ +/* + * mdtohtml.cpp -- convert .md links to .html for local documentation + * + * Copyright (c) 2013, 2014 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <algorithm> +#include <iostream> +#include <string> + +using namespace std; + +int main(void) +{ + string content; + string::size_type pos; + + copy(istreambuf_iterator<char>(cin), istreambuf_iterator<char>(), back_inserter(content)); + + while ((pos = content.find(".md")) != string::npos) + content.replace(pos, 3, ".html"); + + copy(content.begin(), content.end(), ostreambuf_iterator<char>(cout)); + + return 0; +}