/* * Copyright (C) 2016 Charybdis Development Team * Copyright (C) 2016 Jason Volk * * 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 is present in all copies. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 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. */ #pragma once #define HAVE_IRCD_MODE_TABLE_H #ifdef __cplusplus namespace ircd { IRCD_EXCEPTION(error, mode_filled) /* The mode_table is a generic template to replace the arrays mapping characters to mode flags * (or other structures) like `extern uint umode_table[256]` or `struct chm chmode_table[256]`. * Instead an `mode_table` or `mode_table` etc can be used. * * mask() overloads converting from a character string to a bitmask accept '+' and '-' in the * string to alter the mask. mask() overloads converting from a bitmask to a string DO NOT place * a leading '+' in the string. To do that you need a delta()... * * delta(table, before, after, buf) - Create a character string with '+' and '-' describing * the difference between the before and after bitmasks. It is common to use 0 as the before * argument to just get a complete mask() with the leading '+'. */ template struct mode_table :std::array { using value_t = T; using mask_t = uint64_t; const T &operator[](const uint8_t &c) const; T &operator[](const uint8_t &c); }; template mask_t & mask(const mode_table &table, const char *buf, mask_t &val) { static_assert(std::is_unsigned(), ""); mask_t add(-1); if(buf) for(; *buf; ++buf) switch(*buf) { case '+': add = -1; continue; case '-': add = 0; continue; default: val ^= (table[*buf] & add) ^ (table[*buf] & val); continue; } return val; } template::mask_t> auto mask(const mode_table &table, const char *const &buf, const mask_t &val = 0) { mask_t ret(val); return mask(table, buf, ret); } template char * mask(const mode_table &table, const mask_t &val, char *const &buf) { char *p(buf); for(uint8_t i(0); i < table.size(); ++i) if(table[i] & val) *p++ = char(i); *p = '\0'; return buf; } template void mask(const mode_table &table, const mask_t &val, const std::function &closure) { for(uint8_t i(0); i < table.size(); ++i) if(table[i] & val) closure(i); } template std::ostream & mask(const mode_table &table, const mask_t &val, std::ostream &s) { mask(table, val, [&s](const char &c) { s << c; }); return s; } template char * delta(const mode_table &table, const mask_t &before, const mask_t &after, char *const &buf) { char *p(buf); auto current('\0'); mask(table, before ^ after, [&](const char &c) { const auto what(table[c] & after? '+' : '-'); if(current != what) { current = what; *p++ = what; } *p++ = c; }); if(!current) *p++ = '+'; // no change; still require placeholding character *p = '\0'; return buf; } template char * delta(const mode_table &table, const mask_t &before, const char *const &after, char *const &buf) { return delta(table, before, mask(table, after), buf); } template char * delta(const mode_table &table, const char *const &before, const mask_t &after, char *const &buf) { return delta(table, mask(table, before), after, buf); } template auto delta(const mode_table &table, const mask_t &before, const char *const &after) { return mask(table, after, before); } template char find(const mode_table &table, const std::function &func) { const auto it(std::find_if(begin(table), end(table), func)); return int8_t(std::distance(begin(table), it)); } template auto mask_table(const mode_table &table) { using mask_t = typename mode_table::mask_t; return std::accumulate(begin(table), end(table), mask_t(0), [] (auto mask, const T &elem) { return mask |= static_cast(elem); }); } template char * mask_table(const mode_table &table, char *const &buf) { char *p(buf); for(size_t i(0); i < table.size(); ++i) if(!!table[i]) *p++ = i; *p = '\0'; return buf; } template auto find_slot(const mode_table &table, const std::nothrow_t) { using mask_t = typename mode_table::mask_t; static_assert(std::is_unsigned(), ""); const auto mask(mask_table(table)); for(mask_t i(1); i; i <<= 1) if(~mask & i) return i; return mask_t(0); } template auto find_slot(const mode_table &table) { const auto ret(find_slot(table, std::nothrow)); return ret?: throw mode_filled("No bits left on mode mask"); } template T & mode_table::operator[](const uint8_t &c) { return this->std::array::operator[](c & 0x7f); } template const T & mode_table::operator[](const uint8_t &c) const { return this->std::array::operator[](c & 0x7f); } } // namespace ircd #endif // __cplusplus