Add RandomAccessMemory class for in-memory binary operations
This commit is contained in:
440
dart/lib/header.dart
Normal file
440
dart/lib/header.dart
Normal file
@@ -0,0 +1,440 @@
|
||||
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
import 'package:sweepstore/structures.dart';
|
||||
|
||||
import 'helper_extensions.dart';
|
||||
|
||||
void initialiseSweepstoreHeader(RandomAccessFile file, {
|
||||
int concurrentWorkers = 4,
|
||||
}) {
|
||||
|
||||
SweepstoreHeaderWriter header = SweepstoreHeaderWriter(file);
|
||||
|
||||
if (header.magicNumber == 'SWPT') {
|
||||
throw ArgumentError('Sweepstore file is already initialised.');
|
||||
}
|
||||
|
||||
SweepstoreConcurrencyHeaderWriter concurrencyHeader = SweepstoreConcurrencyHeaderWriter(header);
|
||||
|
||||
header.magicNumber = 'SWPT';
|
||||
header.version = "undefined";
|
||||
header.addressTablePointer = SweepstorePointer.nullptr;
|
||||
header.freeListCount = 0;
|
||||
header.isFreeListLifted = false;
|
||||
concurrencyHeader.masterIdentifier = 0;
|
||||
concurrencyHeader.masterHeartbeat = 0;
|
||||
concurrencyHeader.numberOfWorkers = concurrentWorkers;
|
||||
concurrencyHeader.isReadAllowed = false;
|
||||
|
||||
for (int i = 0; i < concurrentWorkers; i++) {
|
||||
SweepstoreWorkerTicket ticket = concurrencyHeader[i];
|
||||
ticket.write(
|
||||
identifier: 0,
|
||||
workerHeartbeat: 0,
|
||||
ticketState: SweepstoreTicketState.FREE,
|
||||
ticketOperation: SweepstoreTicketOperation.NONE,
|
||||
keyHash: 0,
|
||||
writePointer: SweepstorePointer.nullptr,
|
||||
writeSize: 0,
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class SweepstoreHeader {
|
||||
|
||||
final RandomAccessFile _file;
|
||||
|
||||
SweepstoreHeader(this._file);
|
||||
|
||||
// Offset 0 - 4 bytes
|
||||
String get magicNumber {
|
||||
_file.setPositionSync(0);
|
||||
final bytes = _file.readSync(4);
|
||||
return String.fromCharCodes(bytes);
|
||||
}
|
||||
|
||||
// Offset 4 - 12 bytes
|
||||
String get version {
|
||||
_file.setPositionSync(4);
|
||||
String version = utf8.decode(_file.readSync(12)).trim();
|
||||
return version;
|
||||
}
|
||||
|
||||
// Offset 16 - 8 bytes
|
||||
SweepstorePointer get addressTablePointer {
|
||||
_file.setPositionSync(16);
|
||||
final address = _file.readIntSync(8);
|
||||
return SweepstorePointer(address);
|
||||
}
|
||||
|
||||
// Offset 24 - 4 bytes
|
||||
int get freeListCount {
|
||||
_file.setPositionSync(24);
|
||||
int count = _file.readIntSync(4);
|
||||
return count;
|
||||
}
|
||||
|
||||
// Offset 28 - 1 byte
|
||||
bool get isFreeListLifted {
|
||||
_file.setPositionSync(28);
|
||||
int flag = _file.readIntSync(1);
|
||||
return flag != 0;
|
||||
}
|
||||
|
||||
SweepstoreConcurrencyHeader get concurrency => SweepstoreConcurrencyHeader(this);
|
||||
}
|
||||
|
||||
class SweepstoreHeaderWriter extends SweepstoreHeader {
|
||||
|
||||
SweepstoreHeaderWriter(RandomAccessFile file) : super(file);
|
||||
|
||||
// Offset 0 - 4 bytes
|
||||
void set magicNumber(String value) {
|
||||
if (value.length != 4) {
|
||||
throw ArgumentError('Magic number must be exactly 4 characters long');
|
||||
}
|
||||
_file.setPositionSync(0);
|
||||
_file.writeFromSync(value.codeUnits);
|
||||
}
|
||||
|
||||
// Offset 4 - 12 bytes
|
||||
void set version(String value) {
|
||||
if (value.length > 11) {
|
||||
throw ArgumentError('Version string must be at most 11 characters long');
|
||||
}
|
||||
_file.setPositionSync(4);
|
||||
_file.writeFromSync(utf8.encode(" " + value.padRight(11, ' ').substring(0, 11)));
|
||||
}
|
||||
|
||||
// Offset 16 - 8 bytes
|
||||
void set addressTablePointer(SweepstorePointer pointer) {
|
||||
_file.setPositionSync(16);
|
||||
_file.writeIntSync(pointer.address, 8);
|
||||
}
|
||||
|
||||
// Offset 24 - 4 bytes
|
||||
void set freeListCount(int value) {
|
||||
_file.setPositionSync(24);
|
||||
_file.writeIntSync(value, 4);
|
||||
}
|
||||
|
||||
// Offset 28 - 1 byte
|
||||
void set isFreeListLifted(bool lifted) {
|
||||
_file.setPositionSync(28);
|
||||
_file.writeIntSync(lifted ? 1 : 0, 1);
|
||||
}
|
||||
}
|
||||
|
||||
class SweepstoreConcurrencyHeader {
|
||||
|
||||
final SweepstoreHeader _header;
|
||||
|
||||
SweepstoreConcurrencyHeader(this._header);
|
||||
|
||||
// Offset 29 - 8 bytes
|
||||
int get masterIdentifier {
|
||||
_header._file.setPositionSync(29);
|
||||
int id = _header._file.readIntSync(8);
|
||||
return id;
|
||||
}
|
||||
|
||||
// Offset 37 - 4 bytes
|
||||
int get masterHeartbeat {
|
||||
_header._file.setPositionSync(37);
|
||||
int heartbeat = _header._file.readIntSync(4);
|
||||
return heartbeat;
|
||||
}
|
||||
|
||||
// Offset 41 - 4 bytes
|
||||
int get numberOfWorkers {
|
||||
_header._file.setPositionSync(41);
|
||||
int numWorkers = _header._file.readIntSync(4);
|
||||
return numWorkers;
|
||||
}
|
||||
|
||||
// Offset 45 - 1 byte
|
||||
bool get isReadAllowed {
|
||||
_header._file.setPositionSync(45);
|
||||
int flag = _header._file.readIntSync(1);
|
||||
return flag != 0;
|
||||
}
|
||||
|
||||
SweepstoreWorkerTicket operator [](int ticketIndex) {
|
||||
if (ticketIndex < 0 || ticketIndex >= numberOfWorkers) {
|
||||
throw RangeError.index(ticketIndex, this, 'ticketIndex', null, numberOfWorkers);
|
||||
}
|
||||
return SweepstoreWorkerTicket(ticketIndex, this);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class SweepstoreConcurrencyHeaderWriter extends SweepstoreConcurrencyHeader {
|
||||
|
||||
SweepstoreConcurrencyHeaderWriter(SweepstoreHeader header) : super(header);
|
||||
|
||||
// Offset 29 - 8 bytes
|
||||
void set masterIdentifier(int id) {
|
||||
_header._file.setPositionSync(29);
|
||||
_header._file.writeIntSync(id, 8);
|
||||
}
|
||||
|
||||
// Offset 37 - 4 bytes
|
||||
void set masterHeartbeat(int heartbeat) {
|
||||
_header._file.setPositionSync(37);
|
||||
_header._file.writeIntSync(heartbeat, 4);
|
||||
}
|
||||
|
||||
// Offset 41 - 4 bytes
|
||||
void set numberOfWorkers(int numWorkers) {
|
||||
_header._file.setPositionSync(41);
|
||||
_header._file.writeIntSync(numWorkers, 4);
|
||||
}
|
||||
|
||||
// Offset 45 - 1 byte
|
||||
void set isReadAllowed(bool allowed) {
|
||||
_header._file.setPositionSync(45);
|
||||
_header._file.writeIntSync(allowed ? 1 : 0, 1);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
const int endOfStaticHeaderOffset = 46;
|
||||
|
||||
class SweepstoreWorkerTicket {
|
||||
|
||||
static const int _ticketSize = 30;
|
||||
final SweepstoreConcurrencyHeader _concurrencyHeader;
|
||||
final int ticketIndex;
|
||||
|
||||
SweepstoreWorkerTicket(this.ticketIndex, this._concurrencyHeader);
|
||||
|
||||
// All offsets are relative to the start of the workers ticket
|
||||
int get _baseOffset => endOfStaticHeaderOffset + (ticketIndex * _ticketSize);
|
||||
|
||||
// Offset 0 - 4 bytes
|
||||
int get identifier {
|
||||
_concurrencyHeader._header._file.setPositionSync(_baseOffset);
|
||||
int id = _concurrencyHeader._header._file.readIntSync(4);
|
||||
return id;
|
||||
}
|
||||
|
||||
// Offset 4 - 4 bytes
|
||||
int get workerHeartbeat {
|
||||
_concurrencyHeader._header._file.setPositionSync(_baseOffset + 4);
|
||||
int heartbeat = _concurrencyHeader._header._file.readIntSync(4);
|
||||
return heartbeat;
|
||||
}
|
||||
|
||||
// Offset 8 - 1 byte
|
||||
SweepstoreTicketState get ticketState {
|
||||
_concurrencyHeader._header._file.setPositionSync(_baseOffset + 8);
|
||||
int stateValue = _concurrencyHeader._header._file.readIntSync(1);
|
||||
return SweepstoreTicketState.values[stateValue];
|
||||
}
|
||||
|
||||
// Offset 9 - 1 byte
|
||||
SweepstoreTicketOperation get ticketOperation {
|
||||
_concurrencyHeader._header._file.setPositionSync(_baseOffset + 9);
|
||||
int operationValue = _concurrencyHeader._header._file.readIntSync(1);
|
||||
return SweepstoreTicketOperation.values[operationValue];
|
||||
}
|
||||
|
||||
// Offset 10 - 8 bytes
|
||||
int get keyHash {
|
||||
_concurrencyHeader._header._file.setPositionSync(_baseOffset + 10);
|
||||
int hash = _concurrencyHeader._header._file.readIntSync(8);
|
||||
return hash;
|
||||
}
|
||||
|
||||
// Offset 18 - 8 bytes
|
||||
SweepstorePointer get writePointer {
|
||||
_concurrencyHeader._header._file.setPositionSync(_baseOffset + 18);
|
||||
int address = _concurrencyHeader._header._file.readIntSync(8);
|
||||
return SweepstorePointer(address);
|
||||
}
|
||||
|
||||
// Offset 26 - 4 bytes
|
||||
int get writeSize {
|
||||
_concurrencyHeader._header._file.setPositionSync(_baseOffset + 26);
|
||||
int size = _concurrencyHeader._header._file.readIntSync(4);
|
||||
return size;
|
||||
}
|
||||
|
||||
// Writer
|
||||
void write({
|
||||
int? identifier,
|
||||
int? workerHeartbeat,
|
||||
SweepstoreTicketState? ticketState,
|
||||
SweepstoreTicketOperation? ticketOperation,
|
||||
int? keyHash,
|
||||
SweepstorePointer? writePointer,
|
||||
int? writeSize,
|
||||
}) {
|
||||
|
||||
try {
|
||||
|
||||
_concurrencyHeader._header._file.lockSync(FileLock.blockingExclusive, _baseOffset, _baseOffset + _ticketSize);
|
||||
|
||||
_concurrencyHeader._header._file.setPositionSync(_baseOffset);
|
||||
List<int> existingBuffer = _concurrencyHeader._header._file.readSync(_ticketSize);
|
||||
RandomAccessMemory buffer = RandomAccessMemory(existingBuffer);
|
||||
|
||||
if (identifier != null) {
|
||||
buffer.setPositionSync(0);
|
||||
buffer.writeIntSync(identifier, 4);
|
||||
}
|
||||
if (workerHeartbeat != null) {
|
||||
buffer.setPositionSync(4);
|
||||
buffer.writeIntSync(workerHeartbeat, 4);
|
||||
}
|
||||
if (ticketState != null) {
|
||||
buffer.setPositionSync(8);
|
||||
buffer.writeIntSync(ticketState.index, 1);
|
||||
}
|
||||
if (ticketOperation != null) {
|
||||
buffer.setPositionSync(9);
|
||||
buffer.writeIntSync(ticketOperation.index, 1);
|
||||
}
|
||||
if (keyHash != null) {
|
||||
buffer.setPositionSync(10);
|
||||
buffer.writeIntSync(keyHash, 8);
|
||||
}
|
||||
if (writePointer != null) {
|
||||
buffer.setPositionSync(18);
|
||||
buffer.writeIntSync(writePointer.address, 8);
|
||||
}
|
||||
if (writeSize != null) {
|
||||
buffer.setPositionSync(26);
|
||||
buffer.writeIntSync(writeSize, 4);
|
||||
}
|
||||
|
||||
_concurrencyHeader._header._file.setPositionSync(_baseOffset);
|
||||
_concurrencyHeader._header._file.writeFromSync(buffer.toUint8List());
|
||||
|
||||
} finally {
|
||||
_concurrencyHeader._header._file.unlockSync(_baseOffset, _baseOffset + _ticketSize);
|
||||
}
|
||||
}
|
||||
|
||||
bool writable() {
|
||||
try {
|
||||
_concurrencyHeader._header._file.lockSync(
|
||||
FileLock.blockingExclusive,
|
||||
_baseOffset,
|
||||
_baseOffset + _ticketSize
|
||||
);
|
||||
// Successfully locked - immediately unlock and return true
|
||||
_concurrencyHeader._header._file.unlockSync(_baseOffset, _baseOffset + _ticketSize);
|
||||
return true;
|
||||
} catch (e) {
|
||||
// Lock failed - already held by another process
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@deprecated
|
||||
class _SweepstoreWorkerTicket {
|
||||
|
||||
static const int _ticketSize = 30;
|
||||
|
||||
final SweepstoreConcurrencyHeader _concurrencyHeader;
|
||||
final int ticketIndex;
|
||||
|
||||
_SweepstoreWorkerTicket(this.ticketIndex, this._concurrencyHeader);
|
||||
|
||||
// All offsets are relative to the start of the workers ticket
|
||||
int get _baseOffset => endOfStaticHeaderOffset + (ticketIndex * _ticketSize);
|
||||
|
||||
// Offset 0 - 4 bytes
|
||||
int get identifier {
|
||||
_concurrencyHeader._header._file.setPositionSync(_baseOffset);
|
||||
int id = _concurrencyHeader._header._file.readIntSync(4);
|
||||
return id;
|
||||
}
|
||||
set identifier(int id) {
|
||||
_concurrencyHeader._header._file.setPositionSync(_baseOffset);
|
||||
_concurrencyHeader._header._file.writeIntSync(id, 4);
|
||||
_concurrencyHeader._header._file.flushSync();
|
||||
}
|
||||
|
||||
// Offset 4 - 4 bytes
|
||||
int get workerHeartbeat {
|
||||
_concurrencyHeader._header._file.setPositionSync(_baseOffset + 4);
|
||||
int heartbeat = _concurrencyHeader._header._file.readIntSync(4);
|
||||
return heartbeat;
|
||||
}
|
||||
set workerHeartbeat(int heartbeat) {
|
||||
_concurrencyHeader._header._file.setPositionSync(_baseOffset + 4);
|
||||
_concurrencyHeader._header._file.writeIntSync(heartbeat, 4);
|
||||
}
|
||||
|
||||
// Offset 8 - 1 byte
|
||||
SweepstoreTicketState get ticketState {
|
||||
_concurrencyHeader._header._file.setPositionSync(_baseOffset + 8);
|
||||
int stateValue = _concurrencyHeader._header._file.readIntSync(1);
|
||||
return SweepstoreTicketState.values[stateValue];
|
||||
}
|
||||
set ticketState(SweepstoreTicketState state) {
|
||||
_concurrencyHeader._header._file.setPositionSync(_baseOffset + 8);
|
||||
_concurrencyHeader._header._file.writeIntSync(state.index, 1);
|
||||
_concurrencyHeader._header._file.flushSync();
|
||||
}
|
||||
|
||||
// Offset 9 - 1 byte
|
||||
SweepstoreTicketOperation get ticketOperation {
|
||||
_concurrencyHeader._header._file.setPositionSync(_baseOffset + 9);
|
||||
int operationValue = _concurrencyHeader._header._file.readIntSync(1);
|
||||
return SweepstoreTicketOperation.values[operationValue];
|
||||
}
|
||||
set ticketOperation(SweepstoreTicketOperation operation) {
|
||||
_concurrencyHeader._header._file.setPositionSync(_baseOffset + 9);
|
||||
_concurrencyHeader._header._file.writeIntSync(operation.index, 1);
|
||||
}
|
||||
|
||||
// Offset 10 - 8 bytes
|
||||
int get keyHash {
|
||||
_concurrencyHeader._header._file.setPositionSync(_baseOffset + 10);
|
||||
int hash = _concurrencyHeader._header._file.readIntSync(8);
|
||||
return hash;
|
||||
}
|
||||
set keyHash(int hash) {
|
||||
_concurrencyHeader._header._file.setPositionSync(_baseOffset + 10);
|
||||
_concurrencyHeader._header._file.writeIntSync(hash, 8);
|
||||
}
|
||||
|
||||
// Offset 18 - 8 bytes
|
||||
SweepstorePointer get writePointer {
|
||||
_concurrencyHeader._header._file.setPositionSync(_baseOffset + 18);
|
||||
int address = _concurrencyHeader._header._file.readIntSync(8);
|
||||
return SweepstorePointer(address);
|
||||
}
|
||||
set writePointer(SweepstorePointer pointer) {
|
||||
_concurrencyHeader._header._file.setPositionSync(_baseOffset + 18);
|
||||
_concurrencyHeader._header._file.writeIntSync(pointer.address, 8);
|
||||
}
|
||||
|
||||
// Offset 26 - 4 bytes
|
||||
int get writeSize {
|
||||
_concurrencyHeader._header._file.setPositionSync(_baseOffset + 26);
|
||||
int size = _concurrencyHeader._header._file.readIntSync(4);
|
||||
return size;
|
||||
}
|
||||
set writeSize(int size) {
|
||||
_concurrencyHeader._header._file.setPositionSync(_baseOffset + 26);
|
||||
_concurrencyHeader._header._file.writeIntSync(size, 4);
|
||||
}
|
||||
|
||||
// Helpers
|
||||
|
||||
void lock() {
|
||||
_concurrencyHeader._header._file.lockSync(FileLock.exclusive, _baseOffset, _baseOffset + _ticketSize);
|
||||
}
|
||||
|
||||
void unlock() {
|
||||
_concurrencyHeader._header._file.unlockSync(_baseOffset, _baseOffset + _ticketSize);
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user