From ae3294c72b799178bc79b312fe43f34c28deca86 Mon Sep 17 00:00:00 2001 From: Jessica James Date: Sun, 18 Dec 2016 01:46:04 -0500 Subject: [PATCH] Removed Jupiter.lib binary file Added generic Hash_Table implementation Simplified and imrpoved Hash.h Replaced Hash.c with Hash.cpp DLList: * Added getHead(), getTail() SLList: * Added getHead(), getTail(), removeHead(), addHead(), erase(), eraseAndDelete(), copy and move assignment operators, move constructor * add() now appends rather than prepends --- Jupiter/DLList.h | 99 +++++---- Jupiter/Hash.c | 49 ----- Jupiter/Hash.cpp | 83 ++++++++ Jupiter/Hash.h | 98 +++++++-- Jupiter/Hash_Table.h | 242 +++++++++++++++++++++ Jupiter/Hash_Table_Imp.h | 317 ++++++++++++++++++++++++++++ Jupiter/Jupiter.vcxproj | 4 +- Jupiter/Jupiter.vcxproj.filters | 21 +- Jupiter/SLList.h | 358 +++++++++++++++++++++++++------- Jupiter/String_Imp.h | 1 - Release/Jupiter.lib | Bin 324110 -> 0 bytes Tester/Test.cpp | 37 +++- 12 files changed, 1114 insertions(+), 195 deletions(-) delete mode 100644 Jupiter/Hash.c create mode 100644 Jupiter/Hash.cpp create mode 100644 Jupiter/Hash_Table.h create mode 100644 Jupiter/Hash_Table_Imp.h delete mode 100644 Release/Jupiter.lib diff --git a/Jupiter/DLList.h b/Jupiter/DLList.h index be9c096..8ca999e 100644 --- a/Jupiter/DLList.h +++ b/Jupiter/DLList.h @@ -46,13 +46,27 @@ namespace Jupiter T *data; }; + /* + * @brief Returns the head of the list + * + * @return Head of the list + */ + Node *getHead() const; + + /* + * @brief Returns the tail of the list + * + * @return Tail of the list + */ + Node *getTail() const; + /** - * @brief Returns the n'th Node in the list. + * @brief Returns the Node at the specified index in the list. * - * @param n Index of the node to return. - * @return n'th Node in the list. + * @param index Index of the node to return. + * @return Node at specified index in the list. */ - Node *getNode(size_t n) const; + Node *getNode(size_t index) const; /** * @brief Gets the data at a specified index. @@ -87,7 +101,7 @@ namespace Jupiter void add(T *data, size_t index); /** - * @brief Adds data to the end of the list. + * @brief Adds data to the tail of the list. * * @param data Data to add to the list. */ @@ -96,7 +110,7 @@ namespace Jupiter /** * @brief Default constructor for the DLList class. */ - DLList(); + DLList() = default; /** * @brief Copy constructor for the DLList class. @@ -111,42 +125,35 @@ namespace Jupiter /** Private members */ private: - Node *head; - Node *end; + Node *m_head = nullptr; + Node *m_tail = nullptr; }; } // Implementation -template Jupiter::DLList::DLList() -{ - Jupiter::DLList::head = nullptr; - Jupiter::DLList::end = nullptr; - Jupiter::List::length = 0; -} - template Jupiter::DLList::DLList(const Jupiter::DLList &source) { Jupiter::List::length = source.length; if (Jupiter::List::length == 0) { - Jupiter::DLList::head = nullptr; - Jupiter::DLList::end = nullptr; + m_head = nullptr; + m_tail = nullptr; } else if (Jupiter::List::length == 1) { Jupiter::DLList::Node *n = new Jupiter::DLList::Node; n->data = source.getNode(0)->data; - Jupiter::DLList::head = n; - Jupiter::DLList::end = n; + m_head = n; + m_tail = n; } else { Jupiter::DLList::Node *sourceNode = source.getNode(0); Jupiter::DLList::Node *n = new Jupiter::DLList::Node; n->data = sourceNode->data; - Jupiter::DLList::head = n; + m_head = n; sourceNode = sourceNode->next; while (sourceNode->next != nullptr) @@ -161,14 +168,14 @@ template Jupiter::DLList::DLList(const Jupiter::DLList &source n = n->next; n->data = sourceNode->data; - Jupiter::DLList::end = n; + m_tail = n; } } template Jupiter::DLList::~DLList() { Jupiter::DLList::Node *p; - Jupiter::DLList::Node *c = Jupiter::DLList::head; + Jupiter::DLList::Node *c = m_head; while (c != nullptr) { p = c; @@ -177,16 +184,26 @@ template Jupiter::DLList::~DLList() } } +template typename Jupiter::DLList::Node *Jupiter::DLList::getHead() const +{ + return m_head; +} + +template typename Jupiter::DLList::Node *Jupiter::DLList::getTail() const +{ + return m_tail; +} + template typename Jupiter::DLList::Node *Jupiter::DLList::getNode(size_t index) const { Jupiter::DLList::Node *r; if (index * 2 < Jupiter::List::length) { - r = Jupiter::DLList::head; + r = m_head; for (size_t i = 0; i < index; i++) r = r->next; return r; } - r = Jupiter::DLList::end; + r = m_tail; for (size_t i = Jupiter::List::length - 1; i > index; i--) r = r->previous; return r; } @@ -203,16 +220,16 @@ template T *Jupiter::DLList::remove(size_t index) template T *Jupiter::DLList::remove(Node *data) { - if (Jupiter::DLList::head == data) + if (m_head == data) { - Jupiter::DLList::head = data->next; + m_head = data->next; if (data->next != nullptr) data->next->previous = data->previous; - else Jupiter::DLList::end = nullptr; + else m_tail = nullptr; } - else if (Jupiter::DLList::end == data) + else if (m_tail == data) { - Jupiter::DLList::end = data->previous; - Jupiter::DLList::end->next = nullptr; + m_tail = data->previous; + m_tail->next = nullptr; } else { @@ -231,16 +248,16 @@ template void Jupiter::DLList::add(T *data, size_t index) node->data = data; if (index == 0) { - node->next = Jupiter::DLList::head; - Jupiter::DLList::head->previous = node; - Jupiter::DLList::head = node; + node->next = m_head; + m_head->previous = node; + m_head = node; node->previous = nullptr; } else if (index == Jupiter::List::length) { - node->previous = Jupiter::DLList::end; - Jupiter::DLList::end->next = node; - Jupiter::DLList::end = node; + node->previous = m_tail; + m_tail->next = node; + m_tail = node; node->next = nullptr; } else @@ -261,15 +278,15 @@ template void Jupiter::DLList::add(T *data) n->next = nullptr; if (Jupiter::List::length == 0) { - Jupiter::DLList::head = n; - Jupiter::DLList::end = n; + m_head = n; + m_tail = n; n->previous = nullptr; } else { - n->previous = Jupiter::DLList::end; - Jupiter::DLList::end->next = n; - Jupiter::DLList::end = n; + n->previous = m_tail; + m_tail->next = n; + m_tail = n; } Jupiter::List::length++; } diff --git a/Jupiter/Hash.c b/Jupiter/Hash.c deleted file mode 100644 index c8358f2..0000000 --- a/Jupiter/Hash.c +++ /dev/null @@ -1,49 +0,0 @@ -/** - * Copyright (C) 2015 Jessica James. - * - * 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. - * - * Written by Jessica James - */ - -#include "Hash.h" -#include - -const uint32_t JUPITER_FNV_1_32_OFFSET_BASIS = 2166136261UL; -const uint64_t JUPITER_FNV_1_64_OFFSET_BASIS = 14695981039346656037ULL; -const uint32_t JUPITER_FNV_1_32_PRIME = 16777619UL; -const uint64_t JUPITER_FNV_1_64_PRIME = 1099511628211ULL; - -uint64_t Jupiter_fnv1(void *data, size_t length) -{ - uint8_t *ptr = (uint8_t *)data; - uint64_t hash = JUPITER_FNV_1_64_OFFSET_BASIS; - while (length-- != 0) - { - hash = hash * JUPITER_FNV_1_64_PRIME; - hash = hash ^ *ptr++; - } - return hash; -} - -uint64_t Jupiter_fnv1a(void *data, size_t length) -{ - uint8_t *ptr = (uint8_t *)data; - uint64_t hash = JUPITER_FNV_1_64_OFFSET_BASIS; - while (length-- != 0) - { - hash = hash ^ *ptr++; - hash = hash * JUPITER_FNV_1_64_PRIME; - } - return hash; -} \ No newline at end of file diff --git a/Jupiter/Hash.cpp b/Jupiter/Hash.cpp new file mode 100644 index 0000000..11e2e10 --- /dev/null +++ b/Jupiter/Hash.cpp @@ -0,0 +1,83 @@ +/** + * Copyright (C) 2015-2016 Jessica James. + * + * 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. + * + * Written by Jessica James + */ + +#include "Hash.h" + +/** Constants */ +constexpr uint32_t JUPITER_FNV_1_32_OFFSET_BASIS = 2166136261UL; +constexpr uint64_t JUPITER_FNV_1_64_OFFSET_BASIS = 14695981039346656037ULL; +constexpr uint32_t JUPITER_FNV_1_32_PRIME = 16777619UL; +constexpr uint64_t JUPITER_FNV_1_64_PRIME = 1099511628211ULL; + +/** Fowler-Noll-Vo */ + +uint64_t Jupiter::fnv1(const uint8_t *data, const uint8_t *end) +{ + uint64_t hash = JUPITER_FNV_1_64_OFFSET_BASIS; + + while (data != end) + { + hash = hash * JUPITER_FNV_1_64_PRIME; + hash = hash ^ *data; + ++data; + } + + return hash; +} + +uint64_t Jupiter::fnv1a(const uint8_t *data, const uint8_t *end) +{ + uint64_t hash = JUPITER_FNV_1_64_OFFSET_BASIS; + + while (data != end) + { + hash = hash ^ *data; + hash = hash * JUPITER_FNV_1_64_PRIME; + ++data; + } + + return hash; +} + +uint32_t Jupiter::fnv1_32(const uint8_t *data, const uint8_t *end) +{ + uint32_t hash = JUPITER_FNV_1_32_OFFSET_BASIS; + + while (data != end) + { + hash = hash * JUPITER_FNV_1_32_PRIME; + hash = hash ^ *data; + ++data; + } + + return hash; +} + +uint32_t Jupiter::fnv1a_32(const uint8_t *data, const uint8_t *end) +{ + uint32_t hash = JUPITER_FNV_1_32_OFFSET_BASIS; + + while (data != end) + { + hash = hash ^ *data; + hash = hash * JUPITER_FNV_1_32_PRIME; + ++data; + } + + return hash; +} \ No newline at end of file diff --git a/Jupiter/Hash.h b/Jupiter/Hash.h index 0b616b2..baefd0f 100644 --- a/Jupiter/Hash.h +++ b/Jupiter/Hash.h @@ -1,5 +1,5 @@ /** - * Copyright (C) 2015 Jessica James. + * Copyright (C) 2015-2016 Jessica James. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -24,53 +24,121 @@ * @brief Defines some hashing algorithms. */ -#include "Jupiter.h" -#if defined __cplusplus #include +#include "Jupiter.h" +#include "Readable_String.h" namespace Jupiter { + /** Sums */ + template inline R calcsum(const T *in_data, size_t in_length); + template inline R calcsum(const Jupiter::Readable_String &str); + /** Fowler-Noll-Vo hash algorithms */ + template inline uint64_t fnv1(const T &data); template inline uint64_t fnv1(const T *str, size_t length); template inline uint64_t fnv1(const Jupiter::Readable_String &str); + template inline uint32_t fnv1_32(const T &data); + template inline uint32_t fnv1_32(const T *str, size_t length); + template inline uint32_t fnv1_32(const Jupiter::Readable_String &str); + + template inline uint64_t fnv1a(const T &data); template inline uint64_t fnv1a(const T *str, size_t length); template inline uint64_t fnv1a(const Jupiter::Readable_String &str); + + template inline uint32_t fnv1a_32(const T &data); + template inline uint32_t fnv1a_32(const T *str, size_t length); + template inline uint32_t fnv1a_32(const Jupiter::Readable_String &str); + + JUPITER_API uint64_t fnv1(const uint8_t *data, const uint8_t *end); + JUPITER_API uint64_t fnv1a(const uint8_t *data, const uint8_t *end); + + JUPITER_API uint32_t fnv1_32(const uint8_t *data, const uint8_t *end); + JUPITER_API uint32_t fnv1a_32(const uint8_t *data, const uint8_t *end); } -extern "C" +/** Calcsum implementation */ + +template inline R Jupiter::calcsum(const T *in_data, size_t in_length) { -#else -#include -#endif // __cplusplus + const uint8_t *itr = reinterpret_cast(in_data); + const uint8_t *end = reinterpret_cast(in_data + in_length); + R sum = 0; -JUPITER_API uint64_t Jupiter_fnv1(void *data, size_t length); -JUPITER_API uint64_t Jupiter_fnv1a(void *data, size_t length); + while (itr != end) + { + sum += *itr; + ++itr; + } -#if defined __cplusplus + return sum; +} + +template inline R Jupiter::calcsum(const Jupiter::Readable_String &str) +{ + return Jupiter::calcsum(str.ptr(), str.size()); } /** fnv1 implementation */ +template inline uint64_t Jupiter::fnv1(const T &data) +{ + return Jupiter::fnv1(&data, &data + 1); +} + template inline uint64_t Jupiter::fnv1(const T *data, size_t length) { - return Jupiter_fnv1(data, length * sizeof(T)); + return Jupiter::fnv1(reinterpret_cast(data), reinterpret_cast(data + length)); } template inline uint64_t Jupiter::fnv1(const Jupiter::Readable_String &data) { - return Jupiter_fnv1(data.ptr(), data.size() * sizeof(T)); + return Jupiter::fnv1(reinterpret_cast(data.ptr()), reinterpret_cast(data.ptr() + data.size())); +} + +template inline uint32_t Jupiter::fnv1_32(const T &data) +{ + return Jupiter::fnv1_32(&data, &data + 1); +} + +template inline uint32_t Jupiter::fnv1_32(const T *data, size_t length) +{ + return Jupiter::fnv1_32(reinterpret_cast(data), reinterpret_cast(data + length)); +} + +template inline uint32_t Jupiter::fnv1_32(const Jupiter::Readable_String &data) +{ + return Jupiter::fnv1_32(reinterpret_cast(data.ptr()), reinterpret_cast(data.ptr() + data.size())); +} + +template inline uint64_t Jupiter::fnv1a(const T &data) +{ + return Jupiter::fnv1a(reinterpret_cast(&data), reinterpret_cast(&data + 1)); } template inline uint64_t Jupiter::fnv1a(const T *data, size_t length) { - return Jupiter_fnv1a(data, length * sizeof(T)); + return Jupiter::fnv1a(reinterpret_cast(data), reinterpret_cast(data + length)); } template inline uint64_t Jupiter::fnv1a(const Jupiter::Readable_String &data) { - return Jupiter_fnv1a(data.ptr(), data.size() * sizeof(T)); + return Jupiter::fnv1a(data.ptr(), data.size()); } -#endif // __cplusplus +template inline uint32_t Jupiter::fnv1a_32(const T &data) +{ + return Jupiter::fnv1a_32(reinterpret_cast(&data), reinterpret_cast(&data + 1)); +} + +template inline uint32_t Jupiter::fnv1a_32(const T *data, size_t length) +{ + return Jupiter::fnv1a_32(reinterpret_cast(data), reinterpret_cast(data + length)); +} + +template inline uint32_t Jupiter::fnv1a_32(const Jupiter::Readable_String &data) +{ + return Jupiter::fnv1a_32(data.ptr(), data.size()); +} #endif // _HASH_H_HEADER \ No newline at end of file diff --git a/Jupiter/Hash_Table.h b/Jupiter/Hash_Table.h new file mode 100644 index 0000000..c307d1f --- /dev/null +++ b/Jupiter/Hash_Table.h @@ -0,0 +1,242 @@ +/** + * Copyright (C) 2016 Jessica James. + * + * 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. + * + * Written by Jessica James + */ + +#if !defined _HASH_TABLE_H_HEADER +#define _HASH_TABLE_H_HEADER + +/** + * @file Hash_Table.h + * @brief Defines a generic hash table structure + */ + +#include "String.h" +#include "SLList.h" + +namespace Jupiter +{ + template inline size_t default_hash_function(const T &in); + + /** + * @brief Provides a generic hash table structure + * + * @param KeyT Type the table will use for keys; must implement following: operator==, move constructor + * @param ValueT Type the table will use for values + * @param InKeyT Type the table will accept for keys (Default: KeyT) + * @param InValueT Type the table will accept for values (Default: KeyT) + * @param HashF Function to be used for generating hashes (Default: Fowler-Noll-Vo 1a) + */ + template> class Hash_Table + { + public: + /** Initial number of buckets to allocate; m_buckets_size is never less than INIT_SIZE. */ + static constexpr const size_t INIT_SIZE = 8; + + /** + * @brief Container for table entries + */ + struct Bucket + { + /** + * @brief An individual entry within the table, including its normal key and value. + */ + struct Entry + { + KeyT key; + ValueT value; + + Entry(const InKeyT &in_key, const InValueT &in_value); + }; + + /** + * @brief Searches for an entry in the bucket and returns its value if it exists. + * + * @param in_key Key of the entry to search for + * @return Pointer to the value of the entry if it exists, nullptr otherwise + */ + ValueT *get(const InKeyT &in_key) const; + + /** + * @brief Sets the value for an entry in the bucket + * + * @param in_key Key of the entry to set + * @param in_value Value to set in the entry + * @return True if a new entry was added, false if an entry was overwritten + */ + bool set(const InKeyT &in_key, const InValueT &in_value); + + /** + * @brief Removes an entry from the bucket + * + * @param in_key Key of the entry to search for + * @return Value of the entry which was removed on success, nullptr otherwise + */ + ValueT *remove(const InKeyT &in_key); + + /** + * @brief Copy assignment operator + * + * @param in_bucket Bucket to copy entries from + * @return Reference to this bucket + */ + Bucket &operator=(const Bucket &in_bucket); + + /** + * @brief Move assignment operator + * + * @param in_bucket Bucket to move entries from + * @return Reference to this bucket + */ + Bucket &operator=(Bucket &&in_bucket); + + /** + * @brief Default constructor for the Bucket class + */ + Bucket() = default; + + /** + * @brief Copy constructor for the Bucket class + */ + Bucket(const Bucket &in_bucket); + + /** + * @brief Move constructor for the Bucket class + */ + Bucket(Bucket &&in_bucket); + + /** + * @brief Destructor for the Bucket class + */ + ~Bucket(); + + /** List of entries in the bucket */ + Jupiter::SLList m_pairs; + }; + + /** + * @brief Fetches the value of an entry based on its key + * + * @param in_key Key of the entry to search for + * @return Value of the entry if it exists, nullptr otherwise + */ + ValueT *get(const InKeyT &in_key) const; + + /** + * @brief Sets the value for an entry in the table + * + * @param in_key Key of the entry to set + * @param in_value Value of the entry to set + */ + void set(const InKeyT &in_key, const InValueT &in_value); + + /** + * @brief Removes an entry from the table and returns its value + * + * @param in_key Key of the entry to remove + * @return Value of the entry that was removed if it exists, nullptr otherwise + */ + ValueT *remove(const InKeyT &in_key); + + /** + * @brief Returns the number of entries in the table + * + * @return Number of entries in the table + */ + size_t size() const; + + /** + * @brief Erases the table's contents + */ + void erase(); + + /** + * @brief Shrinks the table's internal bucket pool to the current number of entries, not to be less than INIT_SIZE. + */ + void shrink(); + + /** + * @brief Copy assignment operator + * + * @param in_table Table to copy entries from + * @return Reference to this table + */ + Hash_Table &operator=(const Hash_Table &in_table); + + /** + * @brief Move assignment operator + * + * @param in_table Table to move entries from + * @return Reference to this table + */ + Hash_Table &operator=(Hash_Table &&in_table); + + /** + * @brief Default constructor for the Hash_Table class + */ + Hash_Table(); + + /** + * @brief Allocation constructor for the Hash_Table class + * + * @param in_buckets_size Number of buckets to initialize to + */ + Hash_Table(size_t in_buckets_size); + + /** + * @brief Copy constructor for the Hash_Table class + * + * @param in_table Table to copy entries from + */ + Hash_Table(const Hash_Table &in_table); + + /** + * @brief Move constructor for the Hash_Table class + * + * @param in_table Table to move entries from + */ + Hash_Table(Hash_Table &&in_table); + + /** + * @brief Destructor for the Hash_Table class + */ + ~Hash_Table(); + + private: + /** + * @brief Doubles the size of m_buckets + */ + void expand(); + + /** + * @brief Copies entries from m_buckets to in_buckets; used when expanding or shrinking m_buckets. + * + * @param in_buckets Array of buckets to copy entries into + * @param in_buckets_size Number of buckets in in_buckets + */ + void copy_to_buckets(Bucket *in_buckets, size_t in_buckets_size); + + Bucket *m_buckets; /** Array of Buckets */ + size_t m_buckets_size; /** Number of buckets */ + size_t m_length = 0; /** Number of entries */ + }; + + typedef Hash_Table> HashTable; +} + +#include "Hash_Table_Imp.h" + +#endif // _HASH_TABLE_H_HEADER \ No newline at end of file diff --git a/Jupiter/Hash_Table_Imp.h b/Jupiter/Hash_Table_Imp.h new file mode 100644 index 0000000..8b49045 --- /dev/null +++ b/Jupiter/Hash_Table_Imp.h @@ -0,0 +1,317 @@ +/** + * Copyright (C) 2016 Jessica James. + * + * 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. + * + * Written by Jessica James + */ + +#if !defined _HASH_TABLE_IMP_H_HEADER +#define _HASH_TABLE_IMP_H_HEADER + +/** +* @file Hash_Table_Imp.h +* @brief Provides the implementation for Hash_Table. +*/ + +#include "Hash_Table.h" +#include "Hash.h" + +/** +* IMPLEMENTATION: +* Hash_Table +*/ + +template inline size_t Jupiter::default_hash_function(const T &in) +{ + if (sizeof(size_t) >= sizeof(uint64_t)) + return static_cast(Jupiter::fnv1a(in)); + + return static_cast(Jupiter::fnv1a_32(in)); +} + +/** Hash_Table::Bucket::Entry */ + +template +Jupiter::Hash_Table::Bucket::Entry::Entry(const InKeyT &in_key, const InValueT &in_value) +{ + key = in_key; + value = in_value; +} + +/** Hash_Table::Bucket */ + +template +ValueT *Jupiter::Hash_Table::Bucket::get(const InKeyT &in_key) const +{ + for (Jupiter::SLList::Node *node = m_pairs.getHead(); node != nullptr; node = node->next) + if (node->data->key == in_key) + return &node->data->value; + + return nullptr; +} + +template +bool Jupiter::Hash_Table::Bucket::set(const InKeyT &in_key, const InValueT &in_value) +{ + for (Jupiter::SLList::Node *node = m_pairs.getHead(); node != nullptr; node = node->next) + if (node->data->key == in_key) + { + node->data->value = in_value; + return false; + } + + m_pairs.add(new Entry(in_key, in_value)); + return true; +} + +template +ValueT *Jupiter::Hash_Table::Bucket::remove(const InKeyT &in_key) +{ + Jupiter::SLList::Node *node = m_pairs.getHead(); + Entry *pair; + ValueT *result; + + // No nodes in the bucket + if (node == nullptr) + return nullptr; + + // Check if the head is the desired node + if (node->data->key == in_key) + { + pair = m_pairs.removeHead(); + result = new ValueT(std::move(pair->value)); + delete pair; + + return result; + } + + // iterate through list + while (node->next != nullptr) + { + if (node->next->data->key == in_key) + { + // The next node is the desired node + pair = m_pairs.removeNext(node); + result = new ValueT(std::move(pair->value)); + delete pair; + + return result; + } + node = node->next; + } + + return nullptr; +} + +template +typename Jupiter::Hash_Table::Bucket &Jupiter::Hash_Table::Bucket::operator=(const Bucket &in_bucket) +{ + m_pairs.eraseAndDelete(); + for (Jupiter::SLList::Node *node = in_bucket.m_pairs.getHead(); node != nullptr; node = node->next) + m_pairs.add(new Entry(node->data->key, node->data->value)); + + return *this; +} + +template +typename Jupiter::Hash_Table::Bucket &Jupiter::Hash_Table::Bucket::operator=(Bucket &&in_bucket) +{ + m_pairs = std::move(in_bucket.m_pairs); + return *this; +} + +template +Jupiter::Hash_Table::Bucket::Bucket(const Bucket &in_bucket) +{ + for (Jupiter::SLList::Node *node = in_bucket.m_pairs.getHead(); node != nullptr; node = node->next) + m_pairs.add(new Entry(node->data->key, node->data->value)); +} + +template +Jupiter::Hash_Table::Bucket::Bucket(Bucket &&in_bucket) +{ + m_pairs = std::move(in_bucket.m_pairs); +} + +template +Jupiter::Hash_Table::Bucket::~Bucket() +{ + m_pairs.eraseAndDelete(); +} + +/** Hash_Table */ + +template +ValueT *Jupiter::Hash_Table::get(const InKeyT &in_key) const +{ + return m_buckets[HashF(in_key) % m_buckets_size].get(in_key); +} + +template +void Jupiter::Hash_Table::set(const InKeyT &in_key, const InValueT &in_value) +{ + if (m_buckets[HashF(in_key) % m_buckets_size].set(in_key, in_value)) + if (++m_length == m_buckets_size) + expand(); +} + +template +ValueT *Jupiter::Hash_Table::remove(const InKeyT &in_key) +{ + ValueT *value = m_buckets[HashF(in_key) % m_buckets_size].remove(in_key); + + if (value != nullptr) + --m_length; + + return value; +} + +template +size_t Jupiter::Hash_Table::size() const +{ + return m_length; +} + +template +void Jupiter::Hash_Table::erase() +{ + m_length = 0; + delete[] m_buckets; + + m_buckets_size = INIT_SIZE; + m_buckets = new Bucket[m_buckets_size]; +} + +template +void Jupiter::Hash_Table::shrink() +{ + size_t buckets_size = m_length < INIT_SIZE ? INIT_SIZE : m_length; + Bucket *buckets = new Bucket[buckets_size]; + + copy_to_buckets(buckets, buckets_size); + + delete[] m_buckets; + m_buckets = buckets; + m_buckets_size = buckets_size; +} + +template +typename Jupiter::Hash_Table &Jupiter::Hash_Table::operator=(const Hash_Table &in_table) +{ + // TODO: Optimize; can overwrite data instead of deleting and allocationg again if m_buckets_size > in.m_buckets_size + delete[] m_buckets; + + m_length = in_table.m_length; + m_buckets_size = in_table.m_buckets_size; + m_buckets = new Bucket[m_buckets_size]; + + for (size_t index = 0; index != m_buckets_size; ++index) + m_buckets[index] = in_table.m_buckets[index]; + + return *this; +} + +template +typename Jupiter::Hash_Table &Jupiter::Hash_Table::operator=(Hash_Table &&in_table) +{ + // TODO: Optimize; can swap empty array instead of always deleting + delete[] m_buckets; + + m_buckets = in_table.m_buckets; + m_buckets_size = in_table.m_buckets_size; + m_length = in_table.m_length; + + in_table.m_buckets = new Bucket[1]; + in_table.m_buckets_size = 1; + in_table.m_length = 0; + return *this; +} + +/** Constructors */ + +template +Jupiter::Hash_Table::Hash_Table() +{ + m_buckets_size = INIT_SIZE; + m_buckets = new Bucket[m_buckets_size]; +} + +template +Jupiter::Hash_Table::Hash_Table(size_t in_buckets_size) +{ + m_buckets_size = in_buckets_size; + m_buckets = new Bucket[m_buckets_size]; +} + +template +Jupiter::Hash_Table::Hash_Table(const Hash_Table &in_table) +{ + m_length = in_table.m_length; + m_buckets_size = in_table.m_buckets_size; + m_buckets = new Bucket[m_buckets_size]; + + for (size_t index = 0; index != m_buckets_size; ++index) + m_buckets[index] = in_table.m_buckets[index]; +} + +template +Jupiter::Hash_Table::Hash_Table(Hash_Table &&in_table) +{ + m_buckets = in_table.m_buckets; + m_buckets_size = in_table.m_buckets_size; + m_length = in_table.m_length; + + in_table.m_buckets = new Bucket[1]; + in_table.m_buckets_size = 1; + in_table.m_length = 0; +} + +template +Jupiter::Hash_Table::~Hash_Table() +{ + delete[] m_buckets; +} + +/** Hash_Table/private */ + +template +void Jupiter::Hash_Table::expand() +{ + size_t buckets_size = m_buckets_size * 2; + Bucket *buckets = new Bucket[buckets_size]; + + copy_to_buckets(buckets, buckets_size); + + delete[] m_buckets; + m_buckets = buckets; + m_buckets_size = buckets_size; +} + +template +void Jupiter::Hash_Table::copy_to_buckets(Bucket *in_buckets, size_t in_buckets_size) +{ + Jupiter::SLList::Node *node; + for (size_t index = 0; index != m_buckets_size; ++index) + { + for (node = m_buckets[index].m_pairs.getHead(); node != nullptr; node = node->next) + in_buckets[HashF(node->data->key) % in_buckets_size].set(node->data->key, node->data->value); + } +} + +/* +template +Jupiter::Hash_Table +*/ + +#endif // _HASH_TABLE_IMP_H_HEADER \ No newline at end of file diff --git a/Jupiter/Jupiter.vcxproj b/Jupiter/Jupiter.vcxproj index 34ccf47..9acbe91 100644 --- a/Jupiter/Jupiter.vcxproj +++ b/Jupiter/Jupiter.vcxproj @@ -182,7 +182,7 @@ - + @@ -210,6 +210,8 @@ + + diff --git a/Jupiter/Jupiter.vcxproj.filters b/Jupiter/Jupiter.vcxproj.filters index aea77b0..bb40027 100644 --- a/Jupiter/Jupiter.vcxproj.filters +++ b/Jupiter/Jupiter.vcxproj.filters @@ -55,6 +55,9 @@ {7a4d818a-481e-467c-a3e3-d6d0e7dde244} + + {7db21243-8b23-4dd2-9888-0bdd3c5a1edd} + @@ -105,9 +108,6 @@ Source Files\IRC - - Source Files - Source Files @@ -123,6 +123,9 @@ Source Files\Object Extensions + + Source Files + @@ -233,9 +236,6 @@ Header Files\Strings - - Header Files - Header Files\Files @@ -260,6 +260,15 @@ Header Files\Object Extensions + + Header Files\Hash + + + Header Files\Hash + + + Header Files\Hash + diff --git a/Jupiter/SLList.h b/Jupiter/SLList.h index a18bf6d..231918c 100644 --- a/Jupiter/SLList.h +++ b/Jupiter/SLList.h @@ -1,5 +1,5 @@ /** - * Copyright (C) 2013-2015 Jessica James. + * Copyright (C) 2013-2016 Jessica James. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -45,13 +45,27 @@ namespace Jupiter T *data = nullptr; }; + /* + * @brief Returns the head of the list + * + * @return Head of the list + */ + Node *getHead() const; + + /* + * @brief Returns the tail of the list + * + * @return Tail of the list + */ + Node *getTail() const; + /** - * @brief Returns the n'th Node in the list. + * @brief Returns the Node at the specified index in the list. * - * @param n Index of the node to return. - * @return n'th Node in the list. + * @param index Index of the node to return. + * @return Node at specified index in the list. */ - Node *getNode(size_t n) const; + Node *getNode(size_t index) const; /** * @brief Gets the data at a specified index. @@ -61,6 +75,13 @@ namespace Jupiter */ T *get(size_t index) const; + /** + * @brief Removes the head of the list + * + * @return Value that was stored in the head of the list + */ + T *removeHead(); + /** * @brief Removes the n'th Node in the list, and returns its contents. * @@ -78,29 +99,58 @@ namespace Jupiter T *removeNext(Node *data); /** - * @brief Adds data to the list at a specified index. + * @brief Adds data to the end of the list. * * @param data Data to add to the list. - * @param index Position in the list to add the data to. + */ + void add(T *data); + + /** + * @brief Inserts data to the specified index in the list. + * + * @param data Data to insert into the list. + * @param index Position in the list to insert data to. */ void add(T *data, size_t index); /** - * @brief Adds data to the front of the list. + * @brief Inserts data to the head of the list. * - * @param data Data to add to the list. + * @param data Data to insert into the list. */ - void add(T *data); + void addHead(T *data); + + /** + * @breif Erases all entries in the list + */ + void erase(); + + /** + * @breif Erases and deletes all entries in the list + */ + void eraseAndDelete(); + + SLList &operator=(const SLList &in_list); + SLList &operator=(SLList &&in_list); /** * @brief Default constructor for the SLList class. */ - SLList(); + SLList() = default; /** * @brief Copy constructor for the SLList class. + * + * @param in_list List to copy data from */ - SLList(const SLList &); + SLList(const SLList &in_list); + + /** + * @brief Move constructor for the SLList class + * + * @param in_list List to move data from + */ + SLList(SLList &&in_list); /** * @brief Destructor for the SLList class. @@ -110,105 +160,200 @@ namespace Jupiter /** Private members */ private: - Node *head; + void copy_from_internal(const SLList &in_list); + + Node *m_head = nullptr; + Node *m_tail = nullptr; }; } // Implementation -template Jupiter::SLList::SLList() +template typename Jupiter::SLList::Node *Jupiter::SLList::getHead() const { - Jupiter::SLList::head = new Jupiter::SLList::Node(); - Jupiter::List::length = 0; + return m_head; } -template Jupiter::SLList::SLList(const Jupiter::SLList &source) +template typename Jupiter::SLList::Node *Jupiter::SLList::getTail() const { - Jupiter::SLList::head = new Jupiter::SLList::Node(); - Jupiter::SLList::Node *sourceNode = source.head; - - head->data = sourceNode->data; - sourceNode = sourceNode->next; + return m_tail; +} + +template typename Jupiter::SLList::Node *Jupiter::SLList::getNode(size_t in_index) const +{ + if (in_index == Jupiter::SLList::length) + return m_tail; - Jupiter::SLList::Node *n = Jupiter::SLList::head; + Jupiter::SLList::Node *node = m_head; - while (sourceNode != nullptr) + while (in_index != 0) { - n->next = new Jupiter::SLList::Node(); - n = n->next; - n->data = sourceNode->data; - sourceNode = sourceNode->next; + node = node->next; + --in_index; } - Jupiter::List::length = source.length; + + return node; } -template Jupiter::SLList::~SLList() +template T *Jupiter::SLList::get(size_t in_index) const { - Jupiter::SLList::Node *p; - Jupiter::SLList::Node *c = head; - do - { - p = c; - c = c->next; - delete p; - } while (c != nullptr); + return Jupiter::SLList::getNode(in_index)->data; } -template typename Jupiter::SLList::Node *Jupiter::SLList::getNode(size_t index) const +template T *Jupiter::SLList::removeHead() { - Jupiter::SLList::Node *t = head->next; - for (size_t i = 0; i != index; i++) t = t->next; - return t; + if (m_head == nullptr) + return nullptr; + + T *result = m_head->data; + + Jupiter::SLList::Node *node = m_head; + m_head = m_head->next; + delete node; + --Jupiter::List::length; + + return result; } -template T *Jupiter::SLList::get(size_t index) const +template T *Jupiter::SLList::remove(size_t in_index) { - return Jupiter::SLList::getNode(index)->data; + if (in_index == 0) + return Jupiter::SLList::removeHead(); + + Jupiter::SLList::Node *node = m_head; + + while (in_index != 1) + { + node = node->next; + --in_index; + } + + Jupiter::SLList::Node *tmp = node->next; + T *result = tmp->data; + + node->next = tmp->next; + delete tmp; + --Jupiter::List::length; + + return result; } -template T *Jupiter::SLList::remove(size_t index) +template T *Jupiter::SLList::removeNext(Node *in_data) { - Jupiter::SLList::Node *t = head; - for (size_t i = 0; i != index; i++) - t = t->next; - Jupiter::SLList::Node *t2 = t->next; - t->next = t2->next; - T *r = t2->data; - delete t2; + Jupiter::SLList::Node *node = in_data->next; + + if (node == nullptr) + return nullptr; + + T *result = node->data; + + in_data->next = node->next; + delete node; --Jupiter::List::length; - return r; + + return result; +} + +template void Jupiter::SLList::add(T *data) +{ + Jupiter::SLList::Node *node = new Jupiter::SLList::Node(); + node->data = data; + + if (m_head == nullptr) + m_head = node; + else + m_tail->next = node; + + m_tail = node; + ++Jupiter::List::length; +} + +template void Jupiter::SLList::add(T *in_data, size_t in_index) +{ + if (in_index == 0) + { + Jupiter::SLList::addHead(in_data); + return; + } + + if (in_index >= Jupiter::List::length) + { + Jupiter::SLList::add(in_data); + return; + } + + Jupiter::SLList::Node *node = new Jupiter::SLList::Node(); + node->data = in_data; + + Jupiter::SLList::Node *itr = m_head; + + while (in_index != 1) + { + itr = itr->next; + --in_index; + } + + node->next = itr->next; + itr->next = node; + + ++Jupiter::List::length; } -template T *Jupiter::SLList::removeNext(Node *data) +template void Jupiter::SLList::addHead(T *data) { - Jupiter::SLList::Node *t = data->next; - if (t == nullptr) return nullptr; - T *r = t->data; - data->next = t->next; - delete t; - Jupiter::List::length--; - return r; + Jupiter::SLList::Node *node = new Jupiter::SLList::Node(); + node->data = data; + node->next = m_head; + m_head = node; + + if (m_tail == nullptr) + m_tail = node; + + ++Jupiter::List::length; } -template void Jupiter::SLList::add(T *data, size_t index) +template void Jupiter::SLList::erase() { - Jupiter::SLList::Node *n = new Jupiter::SLList::Node(); - n->data = data; - Jupiter::SLList::Node *t = Jupiter::SLList::head; - for (size_t i = 0; i < index; i++) t = t->next; - n->next = t->next; - t->next = n; - Jupiter::List::length++; + Jupiter::SLList::Node *node = m_head; + + if (node == nullptr) + return; + + Jupiter::SLList::Node *tmp; + + do + { + tmp = node; + node = node->next; + delete tmp; + } while (node != nullptr); + + m_head = nullptr; + m_tail = nullptr; + Jupiter::List::length = 0; } -template void Jupiter::SLList::add(T *data) +template void Jupiter::SLList::eraseAndDelete() { - Jupiter::SLList::Node *n = new Jupiter::SLList::Node(); - n->data = data; - n->next = Jupiter::SLList::head->next; - Jupiter::SLList::head->next = n; - Jupiter::List::length++; + Jupiter::SLList::Node *node = m_head; + + if (node == nullptr) + return; + + Jupiter::SLList::Node *tmp; + + do + { + tmp = node; + node = node->next; + delete tmp->data; + delete tmp; + } while (node != nullptr); + + m_head = nullptr; + m_tail = nullptr; + Jupiter::List::length = 0; } template<> struct _Jupiter_DataBuffer_partial_specialization_impl @@ -216,7 +361,7 @@ template<> struct _Jupiter_DataBuffer_partial_specialization_impl static void push(Jupiter::DataBuffer *buffer, const Jupiter::SLList *data) { buffer->push(data->size()); - Jupiter::SLList::Node *head = data->getNode(0); + Jupiter::SLList::Node *head = data->getHead(); while (head != nullptr) buffer->push(*head++->data); }; @@ -232,4 +377,65 @@ template<> struct _Jupiter_DataBuffer_partial_specialization_impl Jupiter::SLList &Jupiter::SLList::operator=(const SLList &in_list) +{ + Jupiter::SLList::erase(); + + Jupiter::SLList::copy_from_internal(in_list); + + return *this; +} + +template Jupiter::SLList &Jupiter::SLList::operator=(SLList &&in_list) +{ + m_head = in_list.m_head; + m_tail = in_list.m_tail; + + return *this; +} + +template Jupiter::SLList::SLList(const Jupiter::SLList &in_list) +{ + Jupiter::SLList::copy_from_internal(in_list); +} + +template Jupiter::SLList::SLList(Jupiter::SLList &&in_list) +{ + m_head = in_list.m_head; + m_tail = in_list.m_tail; + + in_list.m_head = nullptr; + in_list.m_tail = nullptr; +} + +template Jupiter::SLList::~SLList() +{ + Jupiter::SLList::erase(); +} + +/** Internal */ + +template void Jupiter::SLList::copy_from_internal(const SLList &in_list) +{ + Jupiter::SLList::Node *source_node = in_list.m_head; + + if (source_node == nullptr) + return; + + Jupiter::SLList::Node *node = new Jupiter::SLList::Node(); + node->data = source_node->data; + source_node = source_node->next; + + while (source_node != nullptr) + { + node->next = new Jupiter::SLList::Node(); + node = node->next; + node->data = source_node->data; + source_node = source_node->next; + } + + m_tail = node; + Jupiter::List::length = in_list.length; +} + #endif // _SLLIST_H_HEADER \ No newline at end of file diff --git a/Jupiter/String_Imp.h b/Jupiter/String_Imp.h index 8fdfd31..f1614d1 100644 --- a/Jupiter/String_Imp.h +++ b/Jupiter/String_Imp.h @@ -22,7 +22,6 @@ /** * @file String_Imp.h * @brief Provides the implementations for String_Strict and String_Loose. -* Note: Modification of this file is not supported in any way. */ #include "String.h" diff --git a/Release/Jupiter.lib b/Release/Jupiter.lib deleted file mode 100644 index 53117fdce2fbbf0b719e058187bde837e7fc1c61..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 324110 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 #include #include +#include #include "Jupiter/Functions.h" #include "Jupiter/CString.h" #include "Jupiter/String.h" @@ -9,6 +10,11 @@ #include "Jupiter/InvalidIndex.h" #include "Jupiter/Reference_String.h" #include "Jupiter/DataBuffer.h" +#include "Jupiter/HTTP.h" +#include "Jupiter/HTTP_Server.h" +#include "Jupiter/HTTP_QueryString.h" +#include "Jupiter/Hash.h" +#include "Jupiter/Hash_Table.h" using namespace Jupiter; using namespace Jupiter::literals; @@ -18,16 +24,35 @@ unsigned int totalTests = 0; void test(bool expr) { - totalTests++; - if (expr) goodTests++; - else printf("Test number %u failed!" ENDL, totalTests); + ++totalTests; + if (expr) + ++goodTests; + else + printf("Test number %u failed!" ENDL, totalTests); +} + +Jupiter::StringS randstr(size_t length) +{ + StringS str; + + while (length != 0) + { + str += ' ' + rand() % ('z' - ' '); + --length; + } + + return str; } int main() { - if (goodTests == totalTests) printf("All %u tests succeeded." ENDL, totalTests); - else printf("ERROR: Only %u/%u tests succeeded. %u tests failed." ENDL, goodTests, totalTests, totalTests - goodTests); + if (goodTests == totalTests) + printf("All %u tests succeeded." ENDL, totalTests); + else + printf("ERROR: Only %u/%u tests succeeded. %u tests failed." ENDL, goodTests, totalTests, totalTests - goodTests); + puts("Press any key to continue..."); fgetc(stdin); + return 0; -} \ No newline at end of file +}