123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818 |
- #ifndef DINIT_UTIL_H_INCLUDED
- #define DINIT_UTIL_H_INCLUDED 1
- #include <string>
- #include <functional>
- #include <list>
- #include <vector>
- #include <algorithm>
- #include <stdexcept>
- #include <cstring>
- #include <cstddef>
- #include <cerrno>
- #include <sys/types.h>
- #include <unistd.h>
- #include <baseproc-sys.h>
- // Check if a value is one of several possible values.
- // Use like: value(x).is_in(1,2,3)
- template <typename T>
- class value_cls {
- const T &v;
- public:
- value_cls(const T &v) : v(v) {}
- template <typename U>
- bool is_in(U&& val)
- {
- return v == val;
- }
- template <typename U, typename ...V>
- bool is_in(U&&val, V&&... vals) {
- if (v == val)
- return true;
- return is_in(vals...);
- }
- };
- template <typename T>
- value_cls<T> value(const T &v)
- {
- return value_cls<T>(v);
- }
- // Since we are C++11, we don't have a std::string_view.
- class string_view
- {
- const char *s = nullptr;
- size_t count = 0;
- public:
- string_view() = default;
- string_view(const string_view &other) = default;
- string_view(const char *s_p, size_t count_p) noexcept : s(s_p), count(count_p) { }
- string_view(const char *s_p) noexcept : s(s_p), count(strlen(s_p)) { }
- string_view(const std::string &str) : s(str.data()), count(str.length()) { }
- string_view &operator=(const string_view &other) = default;
- bool operator==(const string_view &other) const noexcept
- {
- if (count != other.count) return false;
- return memcmp(s, other.s, count) == 0;
- }
- bool operator==(const char *other) const noexcept
- {
- if (strncmp(s, other, count) == 0) {
- if (other[count] == '\0') {
- return true;
- }
- }
- return false;
- }
- const char *data() const noexcept { return s; }
- size_t size() const noexcept { return count; }
- size_t length() const noexcept { return count; }
- bool empty() const noexcept { return count == 0; }
- string_view substr(size_t begin, size_t len) const noexcept
- {
- return string_view(s + begin, std::min(len, count - begin));
- }
- const char *begin() const noexcept { return s; }
- const char *end() const noexcept { return s + count; }
- std::reverse_iterator<const char *> rbegin() const noexcept { return std::reverse_iterator<const char *>(end()); }
- std::reverse_iterator<const char *> rend() const noexcept { return std::reverse_iterator<const char *>(begin()); }
- size_t find(char c) const noexcept {
- auto it = std::find(begin(), end(), c);
- if (it == end()) return std::string::npos;
- return (it - begin());
- }
- size_t rfind(char c) const noexcept {
- auto it = std::find(rbegin(), rend(), c);
- if (it == rend()) return std::string::npos;
- return (&(*it) - begin());
- }
- operator std::string() const
- {
- return std::string(s, count);
- }
- };
- // Complete read - read the specified size until end-of-file or error; continue read if
- // interrupted by signal.
- inline ssize_t complete_read(int fd, void * buf, size_t n)
- {
- char * cbuf = static_cast<char *>(buf);
- ssize_t r = 0;
- while ((size_t)r < n) {
- ssize_t res = bp_sys::read(fd, cbuf + r, n - r);
- if (res == 0) {
- return r;
- }
- if (res < 0) {
- if (errno == EINTR) {
- continue;
- }
- // If any other error, and we have successfully read some, return it:
- if (r == 0) {
- return -1;
- }
- else {
- return r;
- }
- }
- r += res;
- }
- return n;
- }
- // Combine two paths to produce a path. If the second path is absolute, it is returned unmodified;
- // otherwise, it is appended to the first path (with a slash separator added if needed).
- inline std::string combine_paths(string_view p1, const char * p2)
- {
- if (*p2 == 0) return (std::string)p1;
- if (p1.empty()) return std::string(p2);
- if (p2[0] == '/') return p2;
- if (*(p1.rbegin()) == '/') return (std::string)p1 + p2;
- return (std::string)p1 + '/' + p2;
- }
- // Find the parent path of a given path, which should refer to a named file or directory (not . or ..).
- // If the path contains no directory, returns the empty string.
- inline string_view parent_path(string_view p)
- {
- auto spos = p.rfind('/');
- if (spos == std::string::npos) {
- return string_view {};
- }
- return p.substr(0, spos + 1);
- }
- // Find the base name of a path (the name after the final '/').
- inline const char *base_name(const char *path) noexcept
- {
- const char * basen = path;
- const char * s = path;
- while (*s != 0) {
- if (*s == '/') basen = s + 1;
- s++;
- }
- return basen;
- }
- // Check if one string starts with another
- inline bool starts_with(const std::string &s, const char *prefix) noexcept
- {
- const char *sp = s.c_str();
- while (*sp != 0 && *prefix != 0) {
- if (*sp != *prefix) return false;
- sp++; prefix++;
- }
- return *prefix == 0;
- }
- // An allocator that doesn't value-initialise for construction. Eg for containers of primitive types this
- // allocator avoids the overhead of initialising new elements to 0.
- template <typename T>
- class default_init_allocator : public std::allocator<T>
- {
- using std::allocator<T>::allocator;
- public:
- template <typename U>
- struct rebind {
- using other = default_init_allocator<U>;
- };
- template <typename U = std::enable_if<std::is_default_constructible<T>::value>>
- void construct(T *obj)
- {
- // avoid value-initialisation:
- ::new(obj) T;
- }
- template <typename ...Args>
- void construct(T *obj, Args&&... args)
- {
- std::allocator<T>::construct(obj, std::forward<Args>(args)...);
- }
- };
- inline size_t hash(const string_view &str)
- {
- size_t end_pos = str.length();
- size_t hash_val = 0;
- for (size_t i = 0; i < end_pos; i += sizeof(size_t)) {
- // collect as many characters as will fit into size_t
- size_t hash_unit = 0;
- std::memcpy(&hash_unit, str.data() + i, std::min(sizeof(size_t), end_pos - i));
- // then incorporate the collected characters into the hash value
- hash_val *= 31;
- hash_val += hash_unit;
- }
- return hash_val;
- }
- struct hash_sv
- {
- size_t operator()(const string_view &str) const
- {
- return hash(str);
- }
- };
- inline bool operator==(string_view str1, const std::string &str2)
- {
- return str1 == string_view(str2);
- }
- inline bool operator==(const std::string &str2, string_view str1)
- {
- return str1 == string_view(str2);
- }
- // An equivalent to std::equal_to<void> (which is C++14)
- class dinit_equal_to
- {
- public:
- template <typename A, typename B>
- bool operator()(A &&a, B &&b)
- {
- return std::forward<A>(a) == std::forward<B>(b);
- }
- };
- // A set where we can check for membership via other-than-key-type values
- template <typename K, typename Hash = std::hash<K>, typename Equal = std::equal_to<K>>
- class dinit_unordered_set {
- using key_type = K;
- using value_type = K;
- using size_type = size_t;
- using hasher = Hash;
- using key_equal = Equal;
- hasher hash_f;
- key_equal key_equal_f;
- using bucket_vec = std::vector<std::list<K>>;
- bucket_vec buckets {};
- size_t current_size = 0;
- size_t current_limit = 0; // size limit before we need more buckets
- size_t buckets_to_max(size_t buckets) noexcept
- {
- // calculate "buckets * 3 / 4" but without risk of overflow at the multiply stage
- size_t base = buckets / 4 * 3;
- size_t extra = (buckets % 4) * 3 / 4;
- return base + extra;
- }
- void do_rehash(size_t new_buckets) noexcept
- {
- // note, buckets vector is already at least new_buckets in size
- size_t old_buckets = buckets.size();
- // first splice all nodes from all buckets into a single list
- std::list<K> all_nodes;
- for (size_t i = 0; i < old_buckets; ++i) {
- all_nodes.splice(all_nodes.end(), buckets[i]);
- }
- // now put all nodes into the correct bucket
- auto node_i = all_nodes.begin();
- while (node_i != all_nodes.end()) {
- auto next_node_i = std::next(node_i);
- size_t hashval = hash_f(*node_i);
- size_t bucket_num = hashval % new_buckets;
- buckets[bucket_num].splice(buckets[bucket_num].end(), all_nodes, node_i);
- node_i = next_node_i;
- }
- }
- public:
- class iterator
- {
- friend class dinit_unordered_set;
- public:
- using iterator_category = std::forward_iterator_tag;
- using difference_type = std::ptrdiff_t;
- using value_type = dinit_unordered_set::value_type;
- using pointer = value_type*;
- using reference = value_type&;
- protected:
- using list_iterator_t = typename std::list<K>::iterator;
- bucket_vec *buckets;
- list_iterator_t list_it;
- size_t bucket_num;
- public:
- iterator() noexcept : buckets(nullptr), list_it(), bucket_num(-1) { }
- iterator(bucket_vec *buckets_p, list_iterator_t lit, size_t bucket_p) noexcept
- : buckets(buckets_p), list_it(lit), bucket_num(bucket_p) { }
- iterator(const iterator &) noexcept = default;
- bool operator==(const iterator &other) noexcept
- {
- return other.bucket_num == bucket_num && other.list_it == list_it;
- }
- bool operator!=(const iterator &other) noexcept
- {
- return !(*this == other);
- }
- value_type &operator*() noexcept
- {
- return *list_it;
- }
- const value_type *operator->() noexcept
- {
- return &(*list_it);
- }
- iterator &operator++() noexcept
- {
- if (++list_it == (*buckets)[bucket_num].end()) {
- for (size_type i = bucket_num + 1; i < buckets->size(); ++i) {
- if (!(*buckets)[i].empty()) {
- list_it = (*buckets)[i].begin();
- bucket_num = i;
- return *this;
- }
- }
- list_it = {};
- bucket_num = -1;
- }
- return *this;
- }
- };
- class const_iterator : public iterator
- {
- public:
- using iterator::iterator;
- using pointer = const typename iterator::value_type*;
- using reference = const typename iterator::value_type&;
- const_iterator(const const_iterator &) noexcept = default;
- const_iterator(const iterator &other) noexcept : iterator(other) { }
- const value_type &operator*() noexcept
- {
- return *iterator::list_it;
- }
- const value_type *operator->() noexcept
- {
- return &(*iterator::list_it);
- }
- };
- private:
- // implement insert as a template so we can use it for rvalues and lvalues alike
- template <typename V>
- std::pair<iterator,bool> do_insert(V &&value)
- {
- size_t hashval = hash_f(value);
- size_t bucket_num;
- if (buckets.empty()) {
- buckets.resize(4); // good enough starting point
- current_limit = 3;
- bucket_num = hashval % buckets.size();
- }
- else {
- // First, check if the value is already present
- bucket_num = hashval % buckets.size();
- auto list_it = std::find_if(buckets[bucket_num].begin(), buckets[bucket_num].end(),
- [&](const key_type &k) { return key_equal_f(k,std::forward<V>(value)); });
- if (list_it != buckets[bucket_num].end()) {
- return { { &buckets, list_it, bucket_num }, false };
- }
- // Not present. Check if we need to expand.
- if (current_size >= current_limit) {
- if (buckets.size() <= (buckets.max_size() / 2)) {
- rehash(buckets.size() * 2);
- current_limit *= 2;
- bucket_num = hashval % buckets.size();
- }
- else {
- // in the unlikely event that we have hit the max_size, let's just become overloaded
- current_limit = -1;
- }
- }
- }
- auto list_it = buckets[bucket_num].insert(buckets[bucket_num].end(), std::forward<V>(value));
- ++current_size;
- return { { &buckets, list_it, bucket_num }, true };
- }
- public:
- dinit_unordered_set() noexcept = default;
- iterator end() noexcept
- {
- return iterator();
- }
- const_iterator end() const noexcept
- {
- return const_iterator();
- }
- iterator begin() noexcept
- {
- if (current_size == 0) return end();
- for (size_t i = 0; i < buckets.size(); ++i) {
- if (!buckets[i].empty()) {
- return { &buckets, buckets[i].begin(), i };
- }
- }
- return end(); // (should not be reachable)
- }
- const_iterator begin() const noexcept
- {
- auto *non_const_this = const_cast<dinit_unordered_set *>(this);
- return non_const_this->begin();
- }
- std::pair<iterator,bool> insert(const value_type &value)
- {
- return do_insert(value);
- }
- std::pair<iterator,bool> insert(value_type &&value)
- {
- return do_insert(std::move(value));
- }
- // Not available in C++: insert a different type of value; assumes that hash and equals
- // predicate can deal with the argument type, and that it can be converted to the actual
- // value.
- template <typename V>
- std::pair<iterator,bool> insert_byval(V &&value)
- {
- return do_insert(std::forward<V>(value));
- }
- void rehash(size_type new_bucket_count)
- {
- // calculate minimum bucket count, limited by maximum possible count.
- size_t max_buckets = buckets.max_size();
- size_t max_count = buckets_to_max(max_buckets);
- size_t min_buckets;
- if (current_size > max_count) {
- min_buckets = max_buckets;
- }
- else {
- size_t base = current_size / 3 * 4;
- size_t extra = (current_size % 3) * 4 / 3;
- min_buckets = base + extra;
- }
- new_bucket_count = std::max(new_bucket_count, min_buckets);
- if (new_bucket_count < buckets.size()) {
- // buckets vector will shrink: we need to reallocate entries into their new buckets first
- do_rehash(new_bucket_count);
- buckets.resize(new_bucket_count);
- }
- else {
- // grow the vector, then rehash
- buckets.resize(new_bucket_count);
- do_rehash(new_bucket_count);
- }
- }
- template <typename V>
- iterator find(const V &value) noexcept
- {
- if (buckets.empty()) return end();
- size_t hashval = hash_f(value);
- size_t bucket_num = hashval % buckets.size();
- for (auto list_it = buckets[bucket_num].begin(); list_it != buckets[bucket_num].end(); ++list_it) {
- if (key_equal_f(*list_it, value)) {
- return { &buckets, list_it, bucket_num };
- }
- }
- return end();
- }
- template <typename V>
- const_iterator find(const V &value) const noexcept
- {
- auto *non_const_this = const_cast<dinit_unordered_set *>(this);
- return non_const_this->find(value);
- }
- iterator erase(iterator pos) noexcept
- {
- auto new_it = std::next(pos);
- buckets[pos.bucket_num].erase(pos.list_it);
- return new_it;
- }
- template <typename V>
- size_type erase(const V &value) noexcept
- {
- auto it = find(value);
- if (it != end()) {
- buckets[it.bucket_num].erase(it.list_it);
- return 1;
- }
- return 0;
- }
- template <typename V>
- bool contains(const V &value) const noexcept
- {
- return find(value) != end();
- }
- size_type size() const noexcept
- {
- return current_size;
- }
- bool empty() const noexcept
- {
- return current_size == 0;
- }
- void clear() noexcept
- {
- for (auto &bucket : buckets) {
- bucket.clear();
- }
- current_size = 0;
- current_limit = 0;
- buckets.clear();
- try {
- buckets.shrink_to_fit();
- }
- catch (std::bad_alloc &) {
- // ignore any (unlikely) error
- }
- }
- };
- // a linked-list-set, i.e. a set that tracks insertion order
- template <typename K, typename Hash = std::hash<K>, typename Equal = std::equal_to<K>>
- class linked_uo_set
- {
- using key_type = K;
- using value_type = K;
- using size_type = size_t;
- using hasher = Hash;
- using key_equal = Equal;
- struct linked_record
- {
- K value;
- linked_record *next;
- };
- struct lr_hash
- {
- hasher lr_hash_f;
- template <typename T>
- size_t operator()(const T &hval)
- {
- return lr_hash_f(hval);
- }
- size_t operator()(const linked_record &r)
- {
- return lr_hash_f(r.value);
- }
- };
- struct lr_equ
- {
- key_equal lr_equal_f;
- template <typename T>
- bool operator()(const T &hval, const linked_record &rec)
- {
- return lr_equal_f(hval, rec.value);
- }
- template <typename T>
- bool operator()(const linked_record &rec, const T &hval)
- {
- return lr_equal_f(hval, rec.value);
- }
- bool operator()(const linked_record &rec1, const linked_record &rec2)
- {
- return lr_equal_f(rec1.value, rec2.value);
- }
- };
- dinit_unordered_set<linked_record, lr_hash, lr_equ> backing;
- linked_record *first = nullptr;
- linked_record *last = nullptr;
- public:
- // Add to the back of the linked set, if not already in the set.
- // Return true if added, false if was already in the set.
- bool add_back(const value_type &value)
- {
- auto it_b = backing.insert({value, nullptr});
- if (it_b.second) {
- auto *new_ent = &(*it_b.first);
- if (last != nullptr) {
- last->next = new_ent;
- }
- last = new_ent;
- if (first == nullptr) {
- first = new_ent;
- }
- return true;
- }
- return false;
- }
- class iterator
- {
- linked_record *current = nullptr;
- public:
- using iterator_category = std::forward_iterator_tag;
- using difference_type = std::ptrdiff_t;
- using value_type = linked_uo_set::value_type;
- using pointer = value_type*;
- using reference = value_type&;
- iterator(linked_record *record) : current(record) { }
- bool operator==(const iterator &other) noexcept
- {
- return other.current == current;
- }
- bool operator!=(const iterator &other) noexcept
- {
- return !(*this == other);
- }
- value_type &operator*() noexcept
- {
- return current->value;
- }
- const value_type *operator->() noexcept
- {
- return &(current->value);
- }
- iterator &operator++() noexcept
- {
- current = current->next;
- return *this;
- }
- };
- iterator begin()
- {
- return iterator(first);
- }
- iterator end()
- {
- return iterator(nullptr);
- }
- };
- // string that maintains a heap allocation always. Moving a ha_string does not invalidate pointers
- // to characters within the string.
- class ha_string {
- char *str_data = nullptr;
- size_t str_len = 0;
- public:
- ha_string()
- {
- }
- ha_string(const char *cstr, size_t clen)
- {
- str_data = new char[clen + 1];
- memcpy(str_data, cstr, clen + 1);
- str_len = clen;
- }
- ha_string(const char *cstr) : ha_string(cstr, strlen(cstr))
- {
- }
- ha_string(const ha_string &other)
- {
- operator=(other);
- }
- ha_string(const ha_string &&other)
- {
- operator=(std::move(other));
- }
- ha_string &operator=(const ha_string &other)
- {
- char *new_str_data = new char[other.str_len + 1];
- delete[] str_data;
- memcpy(new_str_data, other.str_data, other.str_len + 1);
- str_data = new_str_data;
- str_len = other.str_len;
- return *this;
- }
- ha_string &operator=(ha_string &&other) noexcept
- {
- delete[] str_data;
- str_data = other.str_data;
- str_len = other.str_len;
- other.str_data = nullptr;
- other.str_len = 0;
- return *this;
- }
- ha_string &operator=(const std::string &other)
- {
- char *new_str_data = new char[other.length() + 1];
- delete[] str_data;
- memcpy(new_str_data, other.data(), other.length() + 1);
- str_data = new_str_data;
- str_len = other.length();
- return *this;
- }
- ~ha_string()
- {
- delete[] str_data;
- }
- char &operator[](size_t index) noexcept
- {
- return str_data[index];
- }
- bool operator==(const char *other) noexcept
- {
- return strncmp(str_data, other, str_len) == 0;
- }
- char *c_str() noexcept
- {
- return str_data;
- }
- const char *c_str() const noexcept
- {
- return str_data;
- }
- bool empty() const noexcept
- {
- return str_len == 0;
- }
- size_t length() const noexcept
- {
- return str_len;
- }
- std::string substr(size_t pos, size_t count = (size_t)-1)
- {
- if (pos > str_len) throw std::out_of_range("pos exceeds string length");
- size_t sub_len = std::min(count, str_len - pos);
- return std::string(str_data + pos, sub_len);
- }
- };
- #endif
|