File indexing completed on 2024-11-17 04:55:18

0001 /*
0002     SPDX-License-Identifier: MPL-2.0
0003 */
0004 
0005 /* Copyright (c) 2015 Brian R. Bondy. Distributed under the MPL2 license.
0006  * This Source Code Form is subject to the terms of the Mozilla Public
0007  * License, v. 2.0. If a copy of the MPL was not distributed with this
0008  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
0009 
0010 // You should probably not be using this.  This is only useful in environments
0011 // without std lib and having specific serialization and memory requirements.
0012 // Instead consider using `hash_set` which is a more generic implementation
0013 // with templates.
0014 
0015 #ifndef HASH_SET_H_
0016 #define HASH_SET_H_
0017 
0018 #include <stdint.h>
0019 #include <math.h>
0020 #include <string.h>
0021 #include <stdio.h>
0022 #include <vector>
0023 
0024 #include "./base.h"
0025 #include "./hash_item.h"
0026 
0027 template<class T>
0028 class HashSet {
0029  public:
0030   typedef uint64_t (*HashSetFnPtr)(const T *hash_item);
0031 
0032   /**
0033    * @param bucket_count The number of buckets to create for the hash set
0034    * @param multi_set Allow multiple items with the same hash to be added.
0035    */
0036   explicit HashSet(uint32_t bucket_count, bool multi_set)
0037       : multi_set_(multi_set) {
0038     Init(bucket_count);
0039   }
0040 
0041   ~HashSet() {
0042     Cleanup();
0043   }
0044 
0045   /**
0046    * Adds the specified data if it doesn't exist
0047    * A copy of the data will be created, so the memory used to do the add
0048    * doesn't need to stick around.
0049    *
0050    * @param item_to_add The node to add
0051    * @param update_if_exists true if the item should be updated if it is already there
0052    *   false if the add should fail if it is alraedy there. If multi_set is true and
0053    *   upate_if_exists is false than the same hash'ed item will be added again.
0054    * @return true if the data was added
0055    */
0056   bool Add(const T &item_to_add, bool update_if_exists = true) {
0057     uint64_t hash = item_to_add.GetHash();
0058     HashItem<T> *hash_item = buckets_[hash % bucket_count_];
0059     if (!hash_item) {
0060       hash_item = new HashItem<T>();
0061       hash_item->hash_item_storage_ = new T(item_to_add);
0062       buckets_[hash % bucket_count_] = hash_item;
0063       size_++;
0064       return true;
0065     }
0066 
0067     while (true) {
0068       if (*hash_item->hash_item_storage_ == item_to_add) {
0069         if (update_if_exists) {
0070           hash_item->hash_item_storage_->Update(item_to_add);
0071           return false;
0072         } else if (!multi_set_) {
0073           return false;
0074         }
0075       }
0076       if (!hash_item->next_) {
0077         HashItem<T> *created_hash_item = new HashItem<T>();
0078         created_hash_item->hash_item_storage_ = new T(item_to_add);
0079         hash_item->next_ = created_hash_item;
0080         break;
0081       }
0082       hash_item = hash_item->next_;
0083     }
0084 
0085     size_++;
0086     return true;
0087   }
0088 
0089   /**
0090    * Determines if the specified data exists in the set or not`
0091    * @param data_to_check The data to check
0092    * @return true if the data found
0093    */
0094   bool Exists(const T &data_to_check) {
0095     uint64_t hash = data_to_check.GetHash();
0096     HashItem<T> *hash_item = buckets_[hash % bucket_count_];
0097     if (!hash_item) {
0098       return false;
0099     }
0100 
0101     while (hash_item) {
0102       if (*hash_item->hash_item_storage_ == data_to_check) {
0103         return true;
0104       }
0105       hash_item = hash_item->next_;
0106     }
0107 
0108     return false;
0109   }
0110 
0111   /**
0112    * Determines if the specified data exists in the set or not`
0113    * @param data_to_check The data to check
0114    * @return true if the data found
0115    */
0116   size_t GetMatchingCount(const T &data_to_check) {
0117     size_t count = 0;
0118     uint64_t hash = data_to_check.GetHash();
0119     HashItem<T> *hash_item = buckets_[hash % bucket_count_];
0120     if (!hash_item) {
0121       return count;
0122     }
0123 
0124     while (hash_item) {
0125       if (*hash_item->hash_item_storage_ == data_to_check) {
0126         count++;
0127       }
0128       hash_item = hash_item->next_;
0129     }
0130 
0131     return count;
0132   }
0133 
0134   /**
0135    * Finds the specific data in the hash set.
0136    * This is useful because sometimes it contains more context
0137    * than the object used for the lookup.
0138    * @param data_to_check The data to check
0139    * @return The data stored in the hash set or nullptr if none is found.
0140    */
0141   T * Find(const T &data_to_check) {
0142     uint64_t hash = data_to_check.GetHash();
0143     HashItem<T> *hash_item = buckets_[hash % bucket_count_];
0144     if (!hash_item) {
0145       return nullptr;
0146     }
0147 
0148     while (hash_item) {
0149       if (*hash_item->hash_item_storage_ == data_to_check) {
0150         return hash_item->hash_item_storage_;
0151       }
0152       hash_item = hash_item->next_;
0153     }
0154 
0155     return nullptr;
0156   }
0157 
0158   /**
0159    * Finds the specific data in the hash set.
0160    * This is useful because sometimes it contains more context
0161    * than the object used for the lookup.
0162    * @param data_to_check The data to check
0163    * @return The data stored in the hash set or nullptr if none is found.
0164    */
0165   void FindAll(const T &data_to_check, std::vector<T*> *result) {
0166     uint64_t hash = data_to_check.GetHash();
0167     HashItem<T> *hash_item = buckets_[hash % bucket_count_];
0168     if (!hash_item) {
0169       return;
0170     }
0171 
0172     while (hash_item) {
0173       if (*hash_item->hash_item_storage_ == data_to_check) {
0174         result->push_back(hash_item->hash_item_storage_);
0175       }
0176       hash_item = hash_item->next_;
0177     }
0178   }
0179 
0180   /**
0181    * Removes the specific data in the hash set.
0182    * @param data_to_check The data to remove
0183    * @return true if an item matching the data was removed
0184    */
0185   bool Remove(const T &data_to_check) {
0186     uint64_t hash = data_to_check.GetHash();
0187     HashItem<T> *hash_item = buckets_[hash % bucket_count_];
0188     if (!hash_item) {
0189       return false;
0190     }
0191 
0192     HashItem<T> *last_item = nullptr;
0193     while (hash_item) {
0194       if (*hash_item->hash_item_storage_ == data_to_check) {
0195         if (last_item) {
0196           last_item->next_ = hash_item->next_;
0197           delete hash_item;
0198         } else {
0199           buckets_[hash % bucket_count_] = hash_item->next_;
0200           delete hash_item;
0201         }
0202         size_--;
0203         return true;
0204       }
0205 
0206       last_item = hash_item;
0207       hash_item = hash_item->next_;
0208     }
0209 
0210     return false;
0211   }
0212 
0213 
0214   /**
0215    * Obtains the number of items in the Hash Set
0216    */
0217   uint32_t GetSize() {
0218     return size_;
0219   }
0220 
0221   /**
0222    * Serializes the parsed data and bloom filter data into a single buffer.
0223    * @param size The size is returned in the out parameter if it's needed to
0224    * write to a file.
0225    * @return The returned buffer should be deleted by the caller.
0226    */
0227   char * Serialize(uint32_t *size) {
0228      *size = 0;
0229      *size += SerializeBuckets(nullptr);
0230      char *buffer = new char[*size];
0231      memset(buffer, 0, *size);
0232      SerializeBuckets(buffer);
0233      return buffer;
0234   }
0235 
0236   /**
0237    * Deserializes the buffer.
0238    * Memory passed in will be used by this instance directly without copying
0239    * it in.
0240    * @param buffer The serialized data to deserialize
0241    * @param buffer_size the size of the buffer to deserialize
0242    * @return true if the operation was successful
0243    */
0244   bool Deserialize(char *buffer, uint32_t buffer_size) {
0245     Cleanup();
0246     uint32_t pos = 0;
0247     if (!HasNewlineBefore(buffer, buffer_size)) {
0248       return false;
0249     }
0250 
0251     uint32_t multi_set = 0;
0252     sscanf(buffer + pos, "%x,%x", &bucket_count_, &multi_set);
0253     multi_set_ = multi_set != 0;
0254     buckets_ = new HashItem<T> *[bucket_count_];
0255     memset(buckets_, 0, sizeof(HashItem<T>*) * bucket_count_);
0256     pos += static_cast<uint32_t>(strlen(buffer + pos)) + 1;
0257     if (pos >= buffer_size) {
0258       return false;
0259     }
0260     for (uint32_t i = 0; i < bucket_count_; i++) {
0261       HashItem<T> *last_hash_item = nullptr;
0262       while (*(buffer + pos) != '\0') {
0263         if (pos >= buffer_size) {
0264           return false;
0265         }
0266 
0267         HashItem<T> *hash_item = new HashItem<T>();
0268         hash_item->hash_item_storage_ = new T();
0269         uint32_t deserialize_size =
0270           hash_item->hash_item_storage_->Deserialize(buffer + pos,
0271               buffer_size - pos);
0272         pos += deserialize_size;
0273         if (pos >= buffer_size || deserialize_size == 0) {
0274           return false;
0275         }
0276 
0277         size_++;
0278 
0279         if (last_hash_item) {
0280           last_hash_item->next_ = hash_item;
0281         } else {
0282           buckets_[i] = hash_item;
0283         }
0284         last_hash_item = hash_item;
0285       }
0286       pos++;
0287     }
0288     return true;
0289   }
0290 
0291   /**
0292    * Clears the HashSet back to the original dimensions but
0293    * with no data.
0294    */
0295   void Clear() {
0296     auto old_bucket_count = bucket_count_;
0297     Cleanup();
0298     Init(old_bucket_count);
0299   }
0300 
0301   /**
0302    * Returns true if the hash_set is a multi_set
0303    */
0304   bool IsMultiSet() const {
0305     return multi_set_;
0306   }
0307 
0308  private:
0309   bool HasNewlineBefore(char *buffer, uint32_t buffer_size) {
0310     char *p = buffer;
0311     for (uint32_t i = 0; i < buffer_size; ++i) {
0312       if (*p == '\0')
0313         return true;
0314       p++;
0315     }
0316     return false;
0317   }
0318 
0319   void Init(uint32_t num_buckets) {
0320     bucket_count_ = num_buckets;
0321     buckets_ = nullptr;
0322     size_ = 0;
0323     if (bucket_count_ != 0) {
0324       buckets_ = new HashItem<T>*[bucket_count_];
0325       memset(buckets_, 0, sizeof(HashItem<T>*) * bucket_count_);
0326     }
0327   }
0328 
0329   void Cleanup() {
0330     if (buckets_) {
0331       for (uint32_t i = 0; i < bucket_count_; i++) {
0332         HashItem<T> *hash_item = buckets_[i];
0333         while (hash_item) {
0334           HashItem<T> *temp_hash_item = hash_item;
0335           hash_item = hash_item->next_;
0336           delete temp_hash_item;
0337         }
0338       }
0339       delete[] buckets_;
0340       buckets_ = nullptr;
0341       bucket_count_ = 0;
0342       size_ = 0;
0343     }
0344   }
0345 
0346   uint32_t SerializeBuckets(char *buffer) {
0347     uint32_t total_size = 0;
0348     char sz[512];
0349     total_size += 1 + snprintf(sz, sizeof(sz), "%x,%x",
0350         bucket_count_, multi_set_ ? 1 : 0);
0351     if (buffer) {
0352       memcpy(buffer, sz, total_size);
0353     }
0354     for (uint32_t i = 0; i < bucket_count_; i++) {
0355       HashItem<T> *hash_item = buckets_[i];
0356       while (hash_item) {
0357         if (buffer) {
0358           total_size +=
0359             hash_item->hash_item_storage_->Serialize(buffer + total_size);
0360         } else {
0361           total_size += hash_item->hash_item_storage_->Serialize(nullptr);
0362         }
0363         hash_item = hash_item->next_;
0364       }
0365       if (buffer) {
0366         buffer[total_size] = '\0';
0367       }
0368       // Second null terminator to show next bucket
0369       total_size++;
0370     }
0371     return total_size;
0372   }
0373 
0374  protected:
0375   bool multi_set_;
0376   uint32_t bucket_count_;
0377   HashItem<T> **buckets_;
0378   uint32_t size_;
0379 };
0380 
0381 #endif  // HASH_SET_H_