Add concurrency handling implementation with ticket management and file locking
This commit is contained in:
30
cpp/src/Public/sweepstore/concurrency.h
Normal file
30
cpp/src/Public/sweepstore/concurrency.h
Normal file
@@ -0,0 +1,30 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
#include <iosfwd>
|
||||
#include <thread>
|
||||
|
||||
#define STALE_HEARTBEAT_THRESHOLD_MS 5000
|
||||
|
||||
enum SweepstoreTicketOperation : int;
|
||||
|
||||
namespace SweepstoreConcurrency {
|
||||
|
||||
void spawnTicket(std::string filePath,
|
||||
const SweepstoreTicketOperation& operation,
|
||||
const uint32_t keyHash,
|
||||
const uint32_t targetSize,
|
||||
const std::function<void()> onApproved,
|
||||
std::string debugLabel = ""
|
||||
);
|
||||
|
||||
void initialiseMaster(std::string filePath);
|
||||
|
||||
inline void initialiseMasterAsync(std::string filePath) {
|
||||
std::thread([&filePath]() {
|
||||
initialiseMaster(filePath);
|
||||
}).detach();
|
||||
}
|
||||
|
||||
}
|
||||
143
cpp/src/Public/sweepstore/header.h
Normal file
143
cpp/src/Public/sweepstore/header.h
Normal file
@@ -0,0 +1,143 @@
|
||||
#pragma once
|
||||
|
||||
#include <iosfwd>
|
||||
|
||||
#include "structures.h"
|
||||
#include "utils/file_handle.h"
|
||||
|
||||
constexpr int roundToNearest16(int number) {
|
||||
return (number + 15) & ~15;
|
||||
}
|
||||
|
||||
class SweepstoreHeader {
|
||||
|
||||
private:
|
||||
SweepstoreFileHandle& file;
|
||||
|
||||
public:
|
||||
explicit SweepstoreHeader(SweepstoreFileHandle &fileStream) : file(fileStream) {}
|
||||
|
||||
// Offset 0 - 4 bytes
|
||||
std::string readMagicNumber();
|
||||
void writeMagicNumber(const std::string& magicNumber);
|
||||
|
||||
// Offset 4 - 12 bytes
|
||||
std::string readVersion();
|
||||
void writeVersion(const std::string& version);
|
||||
|
||||
// Offset 16 - 8 bytes
|
||||
SweepstorePointer readAddressTablePointer();
|
||||
void writeAddressTablePointer(const SweepstorePointer& ptr);
|
||||
|
||||
// Offset 24 - 4 bytes
|
||||
uint32_t readFreeListCount();
|
||||
void writeFreeListCount(uint32_t count);
|
||||
|
||||
// Offset 28 - 1 byte
|
||||
bool readIsFreeListLifted();
|
||||
void writeIsFreeListLifted(bool isLifted);
|
||||
|
||||
/**
|
||||
* Initialises the header with default values.
|
||||
*/
|
||||
void initialise();
|
||||
|
||||
};
|
||||
|
||||
constexpr int SWEEPSTORE_COMBINED_STATIC_HEADER_SIZE = roundToNearest16(46);
|
||||
|
||||
struct SweepstoreWorkerTicketSnapshot {
|
||||
|
||||
SweepstoreWorkerTicketSnapshot() :
|
||||
identifier(0),
|
||||
workerHeartbeat(0),
|
||||
state(SweepstoreTicketState::FREE),
|
||||
operation(SweepstoreTicketOperation::NONE),
|
||||
keyHash(0),
|
||||
targetAddress(SweepstorePointer::NULL_PTR),
|
||||
targetSize(0) {}
|
||||
|
||||
// Offset 0 - 4 bytes
|
||||
uint32_t identifier;
|
||||
|
||||
// Offset 4 - 4 bytes
|
||||
uint32_t workerHeartbeat;
|
||||
|
||||
// Offset 8 - 1 byte
|
||||
SweepstoreTicketState state;
|
||||
|
||||
// Offset 9 - 1 byte
|
||||
SweepstoreTicketOperation operation;
|
||||
|
||||
// Offset 10 - 8 bytes
|
||||
uint64_t keyHash;
|
||||
|
||||
// Offset 18 - 8 bytes
|
||||
SweepstorePointer targetAddress;
|
||||
|
||||
// Offset 26 - 4 bytes
|
||||
uint32_t targetSize;
|
||||
};
|
||||
|
||||
class SweepstoreWorkerTicket {
|
||||
|
||||
SweepstoreFileHandle& file;
|
||||
uint32_t ticketIndex;
|
||||
|
||||
uint64_t getOffset() const {
|
||||
return SWEEPSTORE_COMBINED_STATIC_HEADER_SIZE + (ticketIndex * TICKET_SIZE);
|
||||
}
|
||||
|
||||
public:
|
||||
static constexpr int TICKET_SIZE = roundToNearest16(29);
|
||||
|
||||
SweepstoreWorkerTicket(const uint32_t index, SweepstoreFileHandle& fileStream) :
|
||||
file(fileStream),
|
||||
ticketIndex(index) {}
|
||||
|
||||
int getTicketIndex() const {
|
||||
return ticketIndex;
|
||||
}
|
||||
|
||||
void write(SweepstoreWorkerTicketSnapshot &snapshot);
|
||||
|
||||
bool writable();
|
||||
|
||||
SweepstoreWorkerTicketSnapshot snapshot();
|
||||
|
||||
};
|
||||
|
||||
class SweepstoreConcurrencyHeader {
|
||||
|
||||
private:
|
||||
SweepstoreFileHandle& file;
|
||||
|
||||
public:
|
||||
explicit SweepstoreConcurrencyHeader(SweepstoreFileHandle &fileStream) : file(fileStream) {}
|
||||
|
||||
// Offset 29 - 8 bytes
|
||||
uint64_t readMasterIdentifier();
|
||||
void writeMasterIdentifier(uint64_t identifier);
|
||||
|
||||
// Offset 37 - 4 bytes
|
||||
uint32_t readMasterHeartbeat();
|
||||
void writeMasterHeartbeat(uint32_t heartbeat);
|
||||
|
||||
// Offset 41 - 4 bytes
|
||||
uint32_t readNumberOfWorkers();
|
||||
void writeNumberOfWorkers(uint32_t numWorkers);
|
||||
|
||||
// Offset 45 - 1 byte
|
||||
bool readIsReadAllowed();
|
||||
void writeIsReadAllowed(bool isAllowed);
|
||||
|
||||
/**
|
||||
* Initialises the concurrency header with default values.
|
||||
*/
|
||||
void initialise(int concurrentWorkers);
|
||||
|
||||
SweepstoreWorkerTicket operator[](const uint32_t index) const {
|
||||
return SweepstoreWorkerTicket(index, file);
|
||||
}
|
||||
|
||||
};
|
||||
39
cpp/src/Public/sweepstore/structures.h
Normal file
39
cpp/src/Public/sweepstore/structures.h
Normal file
@@ -0,0 +1,39 @@
|
||||
#pragma once
|
||||
#include <cstdint>
|
||||
|
||||
class SweepstorePointer {
|
||||
|
||||
private:
|
||||
uint64_t address;
|
||||
|
||||
public:
|
||||
static const SweepstorePointer NULL_PTR;
|
||||
|
||||
SweepstorePointer(int64_t addr) : address(addr) {}
|
||||
|
||||
bool isNull() {
|
||||
return this->address == UINT64_MAX;
|
||||
}
|
||||
|
||||
bool operator==(const SweepstorePointer &p);
|
||||
|
||||
// Implicit conversion to uint64_t
|
||||
operator uint64_t() const {
|
||||
return address;
|
||||
}
|
||||
};
|
||||
|
||||
enum SweepstoreTicketState {
|
||||
FREE,
|
||||
WAITING,
|
||||
APPROVED,
|
||||
EXECUTING,
|
||||
COMPLETED,
|
||||
};
|
||||
|
||||
enum SweepstoreTicketOperation : int {
|
||||
NONE,
|
||||
READ,
|
||||
MODIFY,
|
||||
WRITE
|
||||
};
|
||||
67
cpp/src/Public/sweepstore/sweepstore.h
Normal file
67
cpp/src/Public/sweepstore/sweepstore.h
Normal file
@@ -0,0 +1,67 @@
|
||||
#pragma once
|
||||
|
||||
#include <iosfwd>
|
||||
|
||||
#include "concurrency.h"
|
||||
#include "header.h"
|
||||
#include "utils/helpers.h"
|
||||
#include "utils/file_handle.h"
|
||||
|
||||
class Sweepstore {
|
||||
|
||||
private:
|
||||
std::string filePath;
|
||||
SweepstoreFileHandle file;
|
||||
SweepstoreHeader* header;
|
||||
SweepstoreConcurrencyHeader* concurrencyHeader;
|
||||
|
||||
public:
|
||||
|
||||
Sweepstore(const std::string& filePath) : filePath(filePath), file(filePath, std::ios::binary | std::ios::in | std::ios::out | std::ios::trunc) {
|
||||
header = new SweepstoreHeader(file);
|
||||
concurrencyHeader = new SweepstoreConcurrencyHeader(file);
|
||||
}
|
||||
|
||||
~Sweepstore() {
|
||||
delete header;
|
||||
delete concurrencyHeader;
|
||||
file.close();
|
||||
}
|
||||
|
||||
void initialise(int concurrentWorkers = 4);
|
||||
|
||||
SweepstoreConcurrencyHeader* getConcurrencyHeader() {
|
||||
return concurrencyHeader;
|
||||
}
|
||||
|
||||
class Proxy {
|
||||
Sweepstore* sweepstore;
|
||||
std::string key;
|
||||
public:
|
||||
Proxy(Sweepstore* sweepstoreIn, const std::string& keyIn)
|
||||
: sweepstore(sweepstoreIn), key(keyIn) {}
|
||||
|
||||
template <typename T>
|
||||
operator T() {
|
||||
// Get value from sweepstore
|
||||
throw std::runtime_error("Not implemented: Reading values from Sweepstore by key is not implemented.");
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void operator=(const T& value) {
|
||||
SweepstoreConcurrency::spawnTicket(sweepstore->filePath,
|
||||
SweepstoreTicketOperation::WRITE,
|
||||
bt_hash(key),
|
||||
sizeof(T),
|
||||
[this, key = this->key, &value]() {
|
||||
|
||||
}
|
||||
);
|
||||
};
|
||||
};
|
||||
|
||||
Proxy operator[](const std::string& key) {
|
||||
return Proxy(this, key);
|
||||
}
|
||||
|
||||
};
|
||||
49
cpp/src/Public/sweepstore/utils/file_handle.h
Normal file
49
cpp/src/Public/sweepstore/utils/file_handle.h
Normal file
@@ -0,0 +1,49 @@
|
||||
#pragma once
|
||||
|
||||
#include <fstream>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
|
||||
class SweepstoreFileHandle {
|
||||
private:
|
||||
std::string path;
|
||||
std::unique_ptr<std::fstream> stream;
|
||||
|
||||
public:
|
||||
SweepstoreFileHandle(const std::string& p, std::ios::openmode mode = std::ios::in | std::ios::out | std::ios::binary)
|
||||
: path(p), stream(std::make_unique<std::fstream>(p, mode)) {
|
||||
if (!stream->is_open()) {
|
||||
throw std::runtime_error("Failed to open file: " + path);
|
||||
}
|
||||
}
|
||||
|
||||
const std::string& getPath() const { return path; }
|
||||
|
||||
std::fstream& getStream() { return *stream; }
|
||||
const std::fstream& getStream() const { return *stream; }
|
||||
|
||||
// Smart pointer-like interface
|
||||
std::fstream* operator->() { return stream.get(); }
|
||||
const std::fstream* operator->() const { return stream.get(); }
|
||||
|
||||
std::fstream& operator*() { return *stream; }
|
||||
const std::fstream& operator*() const { return *stream; }
|
||||
|
||||
bool isOpen() const { return stream && stream->is_open(); }
|
||||
|
||||
void close() {
|
||||
if (stream) {
|
||||
stream->close();
|
||||
}
|
||||
}
|
||||
|
||||
SweepstoreFileHandle(SweepstoreFileHandle&&) noexcept = default;
|
||||
SweepstoreFileHandle& operator=(SweepstoreFileHandle&&) noexcept = default;
|
||||
|
||||
SweepstoreFileHandle(const SweepstoreFileHandle&) = delete;
|
||||
SweepstoreFileHandle& operator=(const SweepstoreFileHandle&) = delete;
|
||||
|
||||
~SweepstoreFileHandle() {
|
||||
close();
|
||||
}
|
||||
};
|
||||
169
cpp/src/Public/sweepstore/utils/file_lock.h
Normal file
169
cpp/src/Public/sweepstore/utils/file_lock.h
Normal file
@@ -0,0 +1,169 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#else
|
||||
#include <sys/file.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
class SweepstoreFileLock {
|
||||
public:
|
||||
enum class Mode {
|
||||
Shared,
|
||||
Exclusive
|
||||
};
|
||||
|
||||
private:
|
||||
#ifdef _WIN32
|
||||
HANDLE handle;
|
||||
OVERLAPPED overlapped;
|
||||
#else
|
||||
int fd;
|
||||
#endif
|
||||
std::string path;
|
||||
Mode mode;
|
||||
bool locked;
|
||||
|
||||
void acquire() {
|
||||
#ifdef _WIN32
|
||||
handle = CreateFileA(path.c_str(), GENERIC_READ | GENERIC_WRITE,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
|
||||
FILE_ATTRIBUTE_NORMAL, NULL);
|
||||
|
||||
if (handle == INVALID_HANDLE_VALUE) {
|
||||
throw std::runtime_error("Failed to open file");
|
||||
}
|
||||
|
||||
memset(&overlapped, 0, sizeof(overlapped));
|
||||
DWORD flags = (mode == Mode::Exclusive) ? LOCKFILE_EXCLUSIVE_LOCK : 0;
|
||||
|
||||
if (!LockFileEx(handle, flags, 0, MAXDWORD, MAXDWORD, &overlapped)) {
|
||||
CloseHandle(handle);
|
||||
throw std::runtime_error("Failed to lock");
|
||||
}
|
||||
#else
|
||||
fd = open(path.c_str(), O_RDWR);
|
||||
if (fd == -1) throw std::runtime_error("Failed to open file");
|
||||
|
||||
int op = (mode == Mode::Exclusive) ? LOCK_EX : LOCK_SH;
|
||||
if (flock(fd, op) != 0) {
|
||||
close(fd);
|
||||
throw std::runtime_error("Failed to lock");
|
||||
}
|
||||
#endif
|
||||
locked = true;
|
||||
}
|
||||
|
||||
void release() {
|
||||
if (locked) {
|
||||
#ifdef _WIN32
|
||||
UnlockFileEx(handle, 0, MAXDWORD, MAXDWORD, &overlapped);
|
||||
CloseHandle(handle);
|
||||
#else
|
||||
flock(fd, LOCK_UN);
|
||||
close(fd);
|
||||
#endif
|
||||
locked = false;
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
SweepstoreFileLock(const std::string& p, Mode m)
|
||||
: path(p), mode(m), locked(false) {
|
||||
#ifdef _WIN32
|
||||
handle = INVALID_HANDLE_VALUE;
|
||||
memset(&overlapped, 0, sizeof(overlapped));
|
||||
#else
|
||||
fd = -1;
|
||||
#endif
|
||||
}
|
||||
|
||||
~SweepstoreFileLock() {
|
||||
release();
|
||||
}
|
||||
|
||||
void lock() {
|
||||
if (!locked) {
|
||||
acquire();
|
||||
}
|
||||
}
|
||||
|
||||
void unlock() {
|
||||
release();
|
||||
}
|
||||
|
||||
// Check if THIS instance holds the lock
|
||||
bool holdsLock() const { return locked; }
|
||||
|
||||
// Check if the file is locked by ANYONE (including this instance)
|
||||
bool isLocked() const {
|
||||
#ifdef _WIN32
|
||||
HANDLE testHandle = CreateFileA(path.c_str(), GENERIC_READ | GENERIC_WRITE,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
|
||||
FILE_ATTRIBUTE_NORMAL, NULL);
|
||||
|
||||
if (testHandle == INVALID_HANDLE_VALUE) {
|
||||
return false; // Can't even open file
|
||||
}
|
||||
|
||||
OVERLAPPED testOverlapped;
|
||||
memset(&testOverlapped, 0, sizeof(testOverlapped));
|
||||
DWORD flags = (mode == Mode::Exclusive) ? LOCKFILE_EXCLUSIVE_LOCK : 0;
|
||||
flags |= LOCKFILE_FAIL_IMMEDIATELY; // Non-blocking
|
||||
|
||||
bool isLocked = !LockFileEx(testHandle, flags, 0, MAXDWORD, MAXDWORD, &testOverlapped);
|
||||
|
||||
if (!isLocked) {
|
||||
// We got the lock, release it
|
||||
UnlockFileEx(testHandle, 0, MAXDWORD, MAXDWORD, &testOverlapped);
|
||||
}
|
||||
|
||||
CloseHandle(testHandle);
|
||||
return isLocked;
|
||||
#else
|
||||
int testFd = open(path.c_str(), O_RDWR);
|
||||
if (testFd == -1) {
|
||||
return false; // Can't open file
|
||||
}
|
||||
|
||||
int op = (mode == Mode::Exclusive) ? LOCK_EX : LOCK_SH;
|
||||
op |= LOCK_NB; // Non-blocking
|
||||
|
||||
bool isLocked = (flock(testFd, op) != 0);
|
||||
|
||||
if (!isLocked) {
|
||||
// We got the lock, release it
|
||||
flock(testFd, LOCK_UN);
|
||||
}
|
||||
|
||||
close(testFd);
|
||||
return isLocked;
|
||||
#endif
|
||||
}
|
||||
|
||||
SweepstoreFileLock(const SweepstoreFileLock&) = delete;
|
||||
SweepstoreFileLock& operator=(const SweepstoreFileLock&) = delete;
|
||||
SweepstoreFileLock(SweepstoreFileLock&&) = default;
|
||||
SweepstoreFileLock& operator=(SweepstoreFileLock&&) = default;
|
||||
|
||||
class Scoped {
|
||||
private:
|
||||
SweepstoreFileLock& lock;
|
||||
|
||||
public:
|
||||
Scoped(SweepstoreFileLock& l) : lock(l) {
|
||||
lock.lock();
|
||||
}
|
||||
|
||||
~Scoped() {
|
||||
lock.unlock();
|
||||
}
|
||||
|
||||
Scoped(const Scoped&) = delete;
|
||||
Scoped& operator=(const Scoped&) = delete;
|
||||
};
|
||||
};
|
||||
381
cpp/src/Public/sweepstore/utils/helpers.h
Normal file
381
cpp/src/Public/sweepstore/utils/helpers.h
Normal file
@@ -0,0 +1,381 @@
|
||||
#pragma once
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <iomanip>
|
||||
#include <vector>
|
||||
#include <cstdint>
|
||||
#include <fstream>
|
||||
#include <chrono>
|
||||
#include <thread>
|
||||
|
||||
// Toggleable debug printing via preprocessor
|
||||
#ifndef SWEEPSTORE_DEBUG
|
||||
#define SWEEPSTORE_DEBUG 0
|
||||
#endif
|
||||
|
||||
#if SWEEPSTORE_DEBUG
|
||||
#define debugPrint(msg) std::cout << msg << std::endl
|
||||
#else
|
||||
#define debugPrint(msg) ((void)0)
|
||||
#endif
|
||||
|
||||
|
||||
inline void print(const char* message) {
|
||||
// Print the message to the console
|
||||
std::cout << message << std::endl;
|
||||
}
|
||||
|
||||
inline std::string trim(const std::string& str) {
|
||||
size_t start = str.find_first_not_of(" \t\n\r");
|
||||
if (start == std::string::npos) return ""; // all whitespace
|
||||
|
||||
size_t end = str.find_last_not_of(" \t\n\r");
|
||||
return str.substr(start, end - start + 1);
|
||||
}
|
||||
|
||||
inline std::string binaryDump(const std::vector<uint8_t>& data) {
|
||||
std::ostringstream buffer;
|
||||
|
||||
for (size_t i = 0; i < data.size(); i += 16) {
|
||||
// Address
|
||||
buffer << "0x"
|
||||
<< std::setfill('0') << std::setw(4) << std::uppercase << std::hex << i
|
||||
<< " (" << std::dec << std::setw(4) << i << ") | ";
|
||||
|
||||
// Hex bytes
|
||||
for (size_t j = 0; j < 16; j++) {
|
||||
if (i + j < data.size()) {
|
||||
buffer << std::setfill('0') << std::setw(2) << std::uppercase << std::hex
|
||||
<< static_cast<int>(data[i + j]) << " ";
|
||||
} else {
|
||||
buffer << " ";
|
||||
}
|
||||
}
|
||||
|
||||
buffer << " | ";
|
||||
|
||||
// Integer representation
|
||||
for (size_t j = 0; j < 16; j++) {
|
||||
if (i + j < data.size()) {
|
||||
buffer << std::dec << std::setw(3) << static_cast<int>(data[i + j]) << " ";
|
||||
} else {
|
||||
buffer << " ";
|
||||
}
|
||||
}
|
||||
|
||||
buffer << " | ";
|
||||
|
||||
// ASCII representation
|
||||
for (size_t j = 0; j < 16; j++) {
|
||||
if (i + j < data.size()) {
|
||||
uint8_t byte = data[i + j];
|
||||
if (byte >= 32 && byte <= 126) {
|
||||
buffer << static_cast<char>(byte);
|
||||
} else {
|
||||
buffer << '.';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
buffer << " | ";
|
||||
if (i + 16 < data.size()) buffer << '\n';
|
||||
}
|
||||
|
||||
return buffer.str();
|
||||
}
|
||||
|
||||
inline std::vector<uint8_t> loadFile(const std::string& filename) {
|
||||
std::ifstream file(filename, std::ios::binary | std::ios::ate);
|
||||
|
||||
if (!file) {
|
||||
throw std::runtime_error("Failed to open file: " + filename);
|
||||
}
|
||||
|
||||
// Get file size
|
||||
std::streamsize size = file.tellg();
|
||||
file.seekg(0, std::ios::beg);
|
||||
|
||||
// Pre-allocate vector and read
|
||||
std::vector<uint8_t> buffer(size);
|
||||
if (!file.read(reinterpret_cast<char*>(buffer.data()), size)) {
|
||||
throw std::runtime_error("Failed to read file: " + filename);
|
||||
}
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
enum class Endian {
|
||||
Little,
|
||||
Big
|
||||
};
|
||||
|
||||
class RandomAccessMemory {
|
||||
private:
|
||||
std::vector<uint8_t> _buffer;
|
||||
size_t _position;
|
||||
|
||||
public:
|
||||
// Constructors
|
||||
RandomAccessMemory() : _position(0) {}
|
||||
|
||||
explicit RandomAccessMemory(const std::vector<uint8_t>& initialData)
|
||||
: _buffer(initialData), _position(0) {}
|
||||
|
||||
explicit RandomAccessMemory(const uint8_t* data, size_t size)
|
||||
: _buffer(data, data + size), _position(0) {}
|
||||
|
||||
// Position management
|
||||
size_t positionSync() const {
|
||||
return _position;
|
||||
}
|
||||
|
||||
void setPositionSync(size_t position) {
|
||||
_position = position;
|
||||
}
|
||||
|
||||
size_t length() const {
|
||||
return _buffer.size();
|
||||
}
|
||||
|
||||
// Read bytes
|
||||
std::vector<uint8_t> readSync(size_t count) {
|
||||
if (_position + count > _buffer.size()) {
|
||||
throw std::range_error("Not enough bytes to read");
|
||||
}
|
||||
|
||||
std::vector<uint8_t> result(_buffer.begin() + _position,
|
||||
_buffer.begin() + _position + count);
|
||||
_position += count;
|
||||
return result;
|
||||
}
|
||||
|
||||
// Write bytes
|
||||
void writeFromSync(const std::vector<uint8_t>& bytes) {
|
||||
for (size_t i = 0; i < bytes.size(); i++) {
|
||||
if (_position + i >= _buffer.size()) {
|
||||
_buffer.push_back(bytes[i]);
|
||||
} else {
|
||||
_buffer[_position + i] = bytes[i];
|
||||
}
|
||||
}
|
||||
_position += bytes.size();
|
||||
}
|
||||
|
||||
// Read/Write Int Dynamic
|
||||
int64_t readIntSync(int size = 4, Endian endianness = Endian::Little) {
|
||||
if (size < 1 || size > 8) {
|
||||
throw std::invalid_argument("Size must be between 1 and 8 bytes");
|
||||
}
|
||||
|
||||
std::vector<uint8_t> bytes = readSync(size);
|
||||
|
||||
// Build integer from bytes with proper endianness
|
||||
int64_t result = 0;
|
||||
if (endianness == Endian::Little) {
|
||||
for (int i = size - 1; i >= 0; i--) {
|
||||
result = (result << 8) | bytes[i];
|
||||
}
|
||||
} else {
|
||||
for (int i = 0; i < size; i++) {
|
||||
result = (result << 8) | bytes[i];
|
||||
}
|
||||
}
|
||||
|
||||
// Sign extend if MSB is set
|
||||
int64_t signBit = 1LL << (size * 8 - 1);
|
||||
if (result & signBit) {
|
||||
result -= 1LL << (size * 8);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
uint64_t readUIntSync(int size = 4, Endian endianness = Endian::Little) {
|
||||
if (size < 1 || size > 8) {
|
||||
throw std::invalid_argument("Size must be between 1 and 8 bytes");
|
||||
}
|
||||
|
||||
std::vector<uint8_t> bytes = readSync(size);
|
||||
|
||||
// Build integer from bytes with proper endianness
|
||||
uint64_t result = 0;
|
||||
if (endianness == Endian::Little) {
|
||||
for (int i = size - 1; i >= 0; i--) {
|
||||
result = (result << 8) | bytes[i];
|
||||
}
|
||||
} else {
|
||||
for (int i = 0; i < size; i++) {
|
||||
result = (result << 8) | bytes[i];
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void writeIntSync(int64_t value, int size = 4, Endian endianness = Endian::Little) {
|
||||
if (size < 1 || size > 8) {
|
||||
throw std::invalid_argument("Size must be between 1 and 8 bytes");
|
||||
}
|
||||
|
||||
std::vector<uint8_t> bytes(size, 0);
|
||||
|
||||
// Extract bytes with proper endianness
|
||||
if (endianness == Endian::Little) {
|
||||
for (int i = 0; i < size; i++) {
|
||||
bytes[i] = (value >> (i * 8)) & 0xFF;
|
||||
}
|
||||
} else {
|
||||
for (int i = 0; i < size; i++) {
|
||||
bytes[size - 1 - i] = (value >> (i * 8)) & 0xFF;
|
||||
}
|
||||
}
|
||||
|
||||
writeFromSync(bytes);
|
||||
}
|
||||
void writeUIntSync(uint64_t value, int size = 4, Endian endianness = Endian::Little) {
|
||||
if (size < 1 || size > 8) {
|
||||
throw std::invalid_argument("Size must be between 1 and 8 bytes");
|
||||
}
|
||||
|
||||
std::vector<uint8_t> bytes(size, 0);
|
||||
|
||||
// Extract bytes with proper endianness
|
||||
if (endianness == Endian::Little) {
|
||||
for (int i = 0; i < size; i++) {
|
||||
bytes[i] = (value >> (i * 8)) & 0xFF;
|
||||
}
|
||||
} else {
|
||||
for (int i = 0; i < size; i++) {
|
||||
bytes[size - 1 - i] = (value >> (i * 8)) & 0xFF;
|
||||
}
|
||||
}
|
||||
|
||||
writeFromSync(bytes);
|
||||
}
|
||||
|
||||
// Read/Write Pointers (assuming POINTER size is 8 bytes)
|
||||
SweepstorePointer readPointerSync(int pointerSize = 8) {
|
||||
int64_t offset = readUIntSync(pointerSize);
|
||||
return SweepstorePointer(offset);
|
||||
}
|
||||
|
||||
void writePointerSync(const SweepstorePointer& pointer, int pointerSize = 8) {
|
||||
writeUIntSync(pointer, pointerSize);
|
||||
}
|
||||
|
||||
// Read/Write Float32
|
||||
float readFloat32Sync(Endian endianness = Endian::Little) {
|
||||
std::vector<uint8_t> bytes = readSync(4);
|
||||
float value;
|
||||
|
||||
if (endianness == Endian::Little) {
|
||||
std::memcpy(&value, bytes.data(), 4);
|
||||
} else {
|
||||
std::vector<uint8_t> reversed(bytes.rbegin(), bytes.rend());
|
||||
std::memcpy(&value, reversed.data(), 4);
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
void writeFloat32Sync(float value, Endian endianness = Endian::Little) {
|
||||
std::vector<uint8_t> bytes(4);
|
||||
std::memcpy(bytes.data(), &value, 4);
|
||||
|
||||
if (endianness == Endian::Big) {
|
||||
std::reverse(bytes.begin(), bytes.end());
|
||||
}
|
||||
|
||||
writeFromSync(bytes);
|
||||
}
|
||||
|
||||
// Read/Write Float64 (Double)
|
||||
double readFloat64Sync(Endian endianness = Endian::Little) {
|
||||
std::vector<uint8_t> bytes = readSync(8);
|
||||
double value;
|
||||
|
||||
if (endianness == Endian::Little) {
|
||||
std::memcpy(&value, bytes.data(), 8);
|
||||
} else {
|
||||
std::vector<uint8_t> reversed(bytes.rbegin(), bytes.rend());
|
||||
std::memcpy(&value, reversed.data(), 8);
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
void writeFloat64Sync(double value, Endian endianness = Endian::Little) {
|
||||
std::vector<uint8_t> bytes(8);
|
||||
std::memcpy(bytes.data(), &value, 8);
|
||||
|
||||
if (endianness == Endian::Big) {
|
||||
std::reverse(bytes.begin(), bytes.end());
|
||||
}
|
||||
|
||||
writeFromSync(bytes);
|
||||
}
|
||||
|
||||
// Conversion methods
|
||||
std::vector<uint8_t> toVector() const {
|
||||
return _buffer;
|
||||
}
|
||||
|
||||
const uint8_t* data() const {
|
||||
return _buffer.data();
|
||||
}
|
||||
|
||||
uint8_t* data() {
|
||||
return _buffer.data();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
inline void preciseSleep(std::chrono::nanoseconds duration) {
|
||||
auto start = std::chrono::high_resolution_clock::now();
|
||||
|
||||
#ifdef _WIN32
|
||||
const auto windowsMinSleepTime = std::chrono::milliseconds(1);
|
||||
|
||||
if (duration < windowsMinSleepTime) {
|
||||
// Pure busy-wait with high-res timer
|
||||
while (std::chrono::high_resolution_clock::now() - start < duration) {
|
||||
// Optionally use _mm_pause() or YieldProcessor() to be nicer to hyperthreading
|
||||
}
|
||||
} else {
|
||||
// Hybrid: sleep most of it, busy-wait the remainder
|
||||
auto sleepDuration = duration - windowsMinSleepTime;
|
||||
std::this_thread::sleep_for(sleepDuration);
|
||||
while (std::chrono::high_resolution_clock::now() - start < duration) {}
|
||||
}
|
||||
#else
|
||||
std::this_thread::sleep_for(duration);
|
||||
#endif
|
||||
}
|
||||
|
||||
inline int32_t millisecondsSinceEpoch32() {
|
||||
auto now = std::chrono::system_clock::now();
|
||||
auto millis = std::chrono::duration_cast<std::chrono::milliseconds>(now.time_since_epoch()).count();
|
||||
return static_cast<int32_t>((millis / 1000) & 0xFFFFFFFF);
|
||||
}
|
||||
|
||||
inline int64_t millisecondsSinceEpoch64() {
|
||||
auto now = std::chrono::system_clock::now();
|
||||
auto millis = std::chrono::duration_cast<std::chrono::milliseconds>(now.time_since_epoch()).count();
|
||||
return millis;
|
||||
}
|
||||
|
||||
inline uint64_t bt_hash(const std::string& str) {
|
||||
uint64_t hash = 0xcbf29ce484222325ULL; // FNV offset basis
|
||||
|
||||
for (unsigned char byte : str) {
|
||||
hash ^= byte;
|
||||
hash *= 0x100000001b3ULL; // FNV prime
|
||||
}
|
||||
|
||||
return hash;
|
||||
}
|
||||
Reference in New Issue
Block a user