/* Copyright 2024 Andrew Noyes Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #pragma once #include #ifdef __cplusplus struct __attribute__((__visibility__("default"))) ConflictSet { enum Result { /** The result of a check which does not intersect any conflicting writes */ Commit, /** The result of a check which intersects a write at a version > readVersion */ Conflict, /** The result of a check with a readVersion older than the highest call to `setOldestVersion` */ TooOld }; /** Bytes ordered lexicographically */ struct Key { const uint8_t *p; int len; }; /** Denotes a set of keys to be checked for conflicting writes since * `readVersion` */ struct ReadRange { Key begin; /** `end` having length 0 denotes that this range is the single key {begin}. * Otherwise this denotes the range [begin, end) */ Key end; int64_t readVersion; }; /** Denotes a set of keys to be written at `writeVersion` */ struct WriteRange { Key begin; /** `end` having length 0 denotes that this range is the single key {begin}. * Otherwise this denotes the range [begin, end) */ Key end; }; /** The result of checking reads[i] is written in results[i] */ void check(const ReadRange *reads, Result *results, int count) const; /** `writes` must be sorted ascending, and must not have adjacent or * overlapping ranges. Reads intersecting writes where readVersion < * `writeVersion` will result in `Conflict` (or `TooOld`, eventually). * `writeVersion` must be greater than every write version in all previous * calls to `addWrites` */ void addWrites(const WriteRange *writes, int count, int64_t writeVersion); /** Reads where readVersion < oldestVersion will result in `TooOld`. Must be * greater than any previous oldestVersion */ void setOldestVersion(int64_t oldestVersion); /** Reads where readVersion < oldestVersion will result in `TooOld`. There are * no writes initially */ explicit ConflictSet(int64_t oldestVersion); ~ConflictSet(); #if __cplusplus > 199711L ConflictSet(ConflictSet &&) noexcept; ConflictSet &operator=(ConflictSet &&) noexcept; ConflictSet(const ConflictSet &) = delete; ConflictSet &operator=(const ConflictSet &) = delete; #endif /** @private */ struct Impl; private: Impl *impl; }; #else typedef struct ConflictSet ConflictSet; typedef enum { /** The result of a check which does not intersect any conflicting writes */ ConflictSet_Commit, /** The result of a check which intersects a write at a version > readVersion */ ConflictSet_Conflict, /** The result of a check with a readVersion older than the highest call to `setOldestVersion` */ ConflictSet_TooOld } ConflictSet_Result; /** Bytes ordered lexicographically */ typedef struct { const uint8_t *p; int len; } ConflictSet_Key; /** Denotes a set of keys to be checked for conflicting writes since * `readVersion` */ typedef struct { ConflictSet_Key begin; /** `end` having length 0 denotes that this range is the single key {begin}. * Otherwise this denotes the range [begin, end) */ ConflictSet_Key end; int64_t readVersion; } ConflictSet_ReadRange; /** Denotes a set of keys to be written at `writeVersion` */ typedef struct { ConflictSet_Key begin; /** `end` having length 0 denotes that this range is the single key {begin}. * Otherwise this denotes the range [begin, end) */ ConflictSet_Key end; } ConflictSet_WriteRange; /** The result of checking reads[i] is written in results[i] */ void ConflictSet_check(ConflictSet *cs, const ConflictSet_ReadRange *reads, ConflictSet_Result *results, int count); /** `writes` must be sorted ascending, and must not have adjacent or * overlapping ranges. Reads intersecting writes where readVersion < * `writeVersion` will result in `Conflict` (or `TooOld`, eventually). * `writeVersion` must be greater than all write versions in all previous * calls to `addWrites` */ void ConflictSet_addWrites(ConflictSet *cs, const ConflictSet_WriteRange *writes, int count, int64_t writeVersion); /** Reads where readVersion < oldestVersion will result in `TooOld`. Must be * greater than any previous oldestVersion */ void ConflictSet_setOldestVersion(ConflictSet *cs, int64_t oldestVersion); /** Reads where readVersion < oldestVersion will result in `TooOld`. There are * no writes initially */ ConflictSet *ConflictSet_create(int64_t oldestVersion); void ConflictSet_destroy(ConflictSet *cs); #endif