From 8f489bf2dfa8ce06e5eb81a618842d57e08b99e0 Mon Sep 17 00:00:00 2001 From: fegger Date: Sat, 20 Jun 2026 16:27:49 +0200 Subject: [PATCH] Upload files to "/" --- hash_table.cpp | 229 +++++++++++++++++++++++++++++++++++++++++++++++++ hash_table.h | 62 +++++++++++++ 2 files changed, 291 insertions(+) create mode 100644 hash_table.cpp create mode 100644 hash_table.h diff --git a/hash_table.cpp b/hash_table.cpp new file mode 100644 index 0000000..5075c7b --- /dev/null +++ b/hash_table.cpp @@ -0,0 +1,229 @@ +#include "../include/hash_table.h" + +#include +#include +#include +#include + +#ifdef __AVX2__ +#include +#endif + +HashTable::HashTable ( std::size_t capacity ) : ctrl_ ( capacity, EMPTY ), entries_ ( capacity ), size_ ( 0 ) { + if ( capacity == 0 ) { + throw std::invalid_argument ( "Capacity must be > 0" ); + } +} // HashTable + +/* === Hashing and fingerprinting === */ + +uint64_t HashTable::hashString ( const std::string &key ) noexcept { + uint64_t hash = 14695981039346656037ULL; // FNV-1a offset basis + for ( char c : key ) { + hash ^= static_cast ( c ); + hash *= 1099511628211ULL; // FNV-1a prime + } + return hash; +} + +uint8_t HashTable::fingerprint ( uint64_t hash ) noexcept { + return static_cast ( hash & 0x7F ); // Use lower 7 bits for fingerprint +} + +/* === Probe function === */ + +std::ptrdiff_t HashTable::probe ( const std::string &key, uint64_t hash, bool insert ) const noexcept { + const std::size_t capacity = ctrl_.size(); + const uint8_t fp = fingerprint ( hash ); + +#ifdef __AVX2__ + std::size_t group = ( hash % capacity ) & ~( GROUP_SIZE - 1 ); // Align to group boundary + __m256i fpVec = _mm256_set1_epi8 ( fp ); + __m256i emptyVec = _mm256_set1_epi8 ( static_cast ( EMPTY ) ); + + std::ptrdiff_t firstTombstone = -1; + + for ( ;; ) { + __m256i ctrlVec = _mm256_loadu_si256 ( reinterpret_cast ( &ctrl_[ group ] ) ); + uint32_t matchMask = _mm256_movemask_epi8 ( _mm256_cmpeq_epi8 ( ctrlVec, fpVec ) ); + uint32_t emptyMask = _mm256_movemask_epi8 ( _mm256_cmpeq_epi8 ( ctrlVec, emptyVec ) ); + + // check candidates + for ( uint32_t mask = matchMask; mask != 0; mask &= ( mask - 1 ) ) { + std::ptrdiff_t idx = ( group + static_cast ( __builtin_ctz ( mask ) ) ) % capacity; + + if ( entries_[ idx ].hash == hash && entries_[ idx ].key == key ) { + return static_cast ( idx ); // Found + } + } + // record first tombstone for this group + if ( insert && firstTombstone == -1 ) { + for ( std::size_t i = 0; i < GROUP_SIZE; ++i ) { + std::size_t idx = ( group + i ) % capacity; + if ( ctrl_[ idx ] == DELETED ) { + firstTombstone = static_cast ( idx ); + break; + } + } + } + + if ( emptyMask ) { + if ( !insert ) + return -1; // Not found + std::size_t slot = ( group + static_cast ( __builtin_ctz ( emptyMask ) ) ) % capacity; + return firstTombstone != -1 ? firstTombstone : static_cast ( slot ); // Insert here + } + group = ( group + GROUP_SIZE ) % capacity; // Move to next group + } +#else // Fallback to scalar probing + std::size_t group = ( hash % capacity ) & ~( GROUP_SIZE - 1 ); // Align to group boundary + std::ptrdiff_t firstTombstone = -1; + + for ( ;; ) { + bool foundEmpty = false; + std::size_t emptyIdx = 0; + + for ( std::size_t i = 0; i < GROUP_SIZE; ++i ) { + std::size_t idx = ( group + i ) % capacity; + uint8_t c = ctrl_[ idx ]; + + if ( c == EMPTY ) { + if ( !foundEmpty ) { + foundEmpty = true; + emptyIdx = idx; + } + } else if ( c == DELETED ) { + if ( insert && firstTombstone == -1 ) { + firstTombstone = static_cast ( idx ); + } + } else if ( c == fp && entries_[ idx ].hash == hash && entries_[ idx ].key == key ) { + return static_cast ( idx ); // Found + } + } + + if ( foundEmpty ) { + if ( !insert ) + return -1; // Not found + return firstTombstone != -1 ? firstTombstone : static_cast ( emptyIdx ); // Insert here + } + group = ( group + GROUP_SIZE ) % capacity; // Move to next group + } // end probeloop +#endif +} // probe + +/* === Insert, lookup, delete === */ + +void HashTable::ht_insert ( const std::string &key, Stock *stock ) { + if ( static_cast ( size_ ) / static_cast ( ctrl_.size() ) > MAX_LOAD ) + rehash ( ctrl_.size() * 2 ); + uint64_t hash = hashString ( key ); + auto idx = static_cast ( probe ( key, hash, true ) ); + if ( ctrl_[ idx ] == EMPTY || ctrl_[ idx ] == DELETED ) { + Entry tmp; + tmp.key = key; + tmp.hash = hash; + tmp.stock = stock; + entries_[ idx ] = std::move ( tmp ); + ++size_; + } else { + entries_[ idx ].stock = stock; // Update existing + } + ctrl_[ idx ] = fingerprint ( hash ); +} // ht_insert + +Stock *HashTable::ht_lookup ( const std::string &key ) const { + uint64_t hash = hashString ( key ); + auto idx = probe ( key, hash, false ); + if ( idx != -1 ) { + return entries_[ static_cast ( idx ) ].stock; + } + return nullptr; // Not found +} // ht_lookup + +bool HashTable::ht_delete ( const std::string &key ) { + uint64_t hash = hashString ( key ); + auto raw = probe ( key, hash, false ); + + if ( raw == -1 ) + return false; // Not found + + auto idx = static_cast ( raw ); + ctrl_[ idx ] = DELETED; + entries_[ idx ] = Entry(); // Clear entry + --size_; + return true; +} // ht_delete + +/* === Rehashing === */ + +void HashTable::rehash ( std::size_t newCapacity ) { + HashTable newTable ( newCapacity ); + for ( std::size_t i = 0; i < ctrl_.size(); ++i ) { + if ( ctrl_[ i ] != EMPTY && ctrl_[ i ] != DELETED ) { + const Entry &entry = entries_[ i ]; + newTable.ht_insert ( entry.key, entry.stock ); + } + } + ctrl_ = std::move ( newTable.ctrl_ ); + entries_ = std::move ( newTable.entries_ ); + size_ = newTable.size_; +} // rehash + +// === List all entries === +std::vector HashTable::listAll () const { + std::vector lines; + for ( std::size_t i = 0; i < ctrl_.size(); ++i ) { + if ( ctrl_[ i ] != EMPTY && ctrl_[ i ] != DELETED ) { + const Stock *stock = entries_[ i ].stock; + std::string line = " " + stock->getName() + + " (" + stock->getSymbol() + ", " + stock->getWKN() + ")"; + if ( stock->hasHistory() ) { + line += " close: " + std::to_string ( stock->latest().close ) + + " on " + stock->latest().date; + } + lines.push_back ( line ); + } + } + if ( lines.empty() ) + lines.push_back ( " (no stocks)" ); + return lines; +} // listAll + +/* === Save and load === */ + +void HashTable::save ( const std::string &filename ) const { + std::ofstream file ( filename, std::ios::binary ); + if ( !file ) + throw std::runtime_error ( "Cannot write: " + filename ); + + file << size_ << "\n"; // Save number of entries + + for ( std::size_t i = 0; i < ctrl_.size(); ++i ) { + if ( ctrl_[ i ] != EMPTY && ctrl_[ i ] != DELETED ) { + entries_[ i ].stock->saveToFile ( file ); + } + } + +} // save + +std::vector> HashTable::load ( const std::string &filename ) { + std::ifstream file ( filename ); + if ( !file ) + throw std::runtime_error ( "Cannot read: " + filename ); + std::fill ( ctrl_.begin(), ctrl_.end(), EMPTY ); // Clear existing data + for ( auto &entry : entries_ ) { + entry = Entry {}; // Clear entries + } + size_ = 0; + std::size_t n = 0; + file >> n; // Read number of entries + file.ignore(); + + std::vector> result; + result.reserve ( n ); + for ( std::size_t i = 0; i < n; ++i ) { + result.push_back ( std::make_unique ( Stock::loadFromFile ( file ) ) ); + } + + return result; +} diff --git a/hash_table.h b/hash_table.h new file mode 100644 index 0000000..d2f76f4 --- /dev/null +++ b/hash_table.h @@ -0,0 +1,62 @@ +#ifndef HASH_TABLE_H +#define HASH_TABLE_H + +#include "stock.h" + +#include +#include +#include +#include +#include +#include + +class HashTable { + public: + static constexpr uint8_t EMPTY = 0x80; + static constexpr uint8_t DELETED = 0xFE; + + explicit HashTable ( size_t capacity = 64 ); + + // delete disabled - requires rehash + HashTable ( const HashTable & ) = delete; + HashTable &operator= ( const HashTable & ) = delete; + + HashTable ( HashTable && ) noexcept = default; + HashTable &operator= ( HashTable && ) noexcept = default; + + void ht_insert ( const std::string &key, Stock *stock ); + Stock *ht_lookup ( const std::string &key ) const; + bool ht_delete ( const std::string &key ); + + std::vector listAll () const; + + void save ( const std::string &filename ) const; + std::vector> load ( const std::string &filename ); + + std::size_t size () const noexcept { return size_; } + std::size_t capacity () const noexcept { return ctrl_.size(); } + + private: + static constexpr double MAX_LOAD = 0.75; + static constexpr std::size_t GROUP_SIZE = 32; + + struct Entry { + std::string key; + uint64_t hash = 0; + Stock *stock = nullptr; + }; + + // helpers + static uint64_t hashString ( const std::string &key ) noexcept; + static uint8_t fingerprint ( uint64_t hash ) noexcept; + std::ptrdiff_t probe ( const std::string &key, uint64_t hash, bool insert ) const noexcept; + void rehash ( std::size_t newCapacity ); + + std::vector ctrl_; + std::vector entries_; + std::size_t size_ = 0; + + +}; // Class HashTable + +#endif