|
| 1 | + |
| 2 | +#pragma once |
| 3 | + |
| 4 | +#include <cassert> |
| 5 | +#include <algorithm> |
| 6 | +#include <mutex> |
| 7 | +#include "VecSim/utils/vec_utils.h" |
| 8 | + |
| 9 | +template <typename DistType> |
| 10 | +using candidatesList = vecsim_stl::vector<std::pair<DistType, idType>>; |
| 11 | + |
| 12 | +typedef uint16_t linkListSize; |
| 13 | + |
| 14 | +struct ElementLevelData { |
| 15 | + // A list of ids that are pointing to the node where each edge is *unidirectional* |
| 16 | + vecsim_stl::vector<idType> *incomingUnidirectionalEdges; |
| 17 | + linkListSize numLinks; |
| 18 | + // Flexible array member - https://en.wikipedia.org/wiki/Flexible_array_member |
| 19 | + // Using this trick, we can have the links list as part of the ElementLevelData struct, and |
| 20 | + // avoid the need to dereference a pointer to get to the links list. We have to calculate the |
| 21 | + // size of the struct manually, as `sizeof(ElementLevelData)` will not include this member. We |
| 22 | + // do so in the constructor of the index, under the name `levelDataSize` (and |
| 23 | + // `elementGraphDataSize`). Notice that this member must be the last member of the struct and |
| 24 | + // all nesting structs. |
| 25 | + idType links[]; |
| 26 | + |
| 27 | + explicit ElementLevelData(std::shared_ptr<VecSimAllocator> allocator) |
| 28 | + : incomingUnidirectionalEdges(new (allocator) vecsim_stl::vector<idType>(allocator)), |
| 29 | + numLinks(0) {} |
| 30 | + |
| 31 | + linkListSize getNumLinks() const { return this->numLinks; } |
| 32 | + idType getLinkAtPos(size_t pos) const { |
| 33 | + assert(pos < numLinks); |
| 34 | + return this->links[pos]; |
| 35 | + } |
| 36 | + const vecsim_stl::vector<idType> &getIncomingEdges() const { |
| 37 | + return *incomingUnidirectionalEdges; |
| 38 | + } |
| 39 | + std::vector<idType> copyLinks() { |
| 40 | + std::vector<idType> links_copy; |
| 41 | + links_copy.assign(links, links + numLinks); |
| 42 | + return links_copy; |
| 43 | + } |
| 44 | + // Sets the outgoing links of the current element. |
| 45 | + // Assumes that the object has the capacity to hold all the links. |
| 46 | + void setLinks(vecsim_stl::vector<idType> &links) { |
| 47 | + numLinks = links.size(); |
| 48 | + memcpy(this->links, links.data(), numLinks * sizeof(idType)); |
| 49 | + } |
| 50 | + template <typename DistType> |
| 51 | + void setLinks(candidatesList<DistType> &links) { |
| 52 | + numLinks = 0; |
| 53 | + for (auto &link : links) { |
| 54 | + this->links[numLinks++] = link.second; |
| 55 | + } |
| 56 | + } |
| 57 | + void popLink() { this->numLinks--; } |
| 58 | + void setNumLinks(linkListSize num) { this->numLinks = num; } |
| 59 | + void setLinkAtPos(size_t pos, idType node_id) { this->links[pos] = node_id; } |
| 60 | + void appendLink(idType node_id) { this->links[this->numLinks++] = node_id; } |
| 61 | + void newIncomingUnidirectionalEdge(idType node_id) { |
| 62 | + this->incomingUnidirectionalEdges->push_back(node_id); |
| 63 | + } |
| 64 | + bool removeIncomingUnidirectionalEdgeIfExists(idType node_id) { |
| 65 | + return this->incomingUnidirectionalEdges->remove(node_id); |
| 66 | + } |
| 67 | + void swapNodeIdInIncomingEdges(idType id_before, idType id_after) { |
| 68 | + auto it = std::find(this->incomingUnidirectionalEdges->begin(), |
| 69 | + this->incomingUnidirectionalEdges->end(), id_before); |
| 70 | + // This should always succeed |
| 71 | + assert(it != this->incomingUnidirectionalEdges->end()); |
| 72 | + *it = id_after; |
| 73 | + } |
| 74 | +}; |
| 75 | + |
| 76 | +struct ElementGraphData { |
| 77 | + size_t toplevel; |
| 78 | + std::mutex neighborsGuard; |
| 79 | + ElementLevelData *others; |
| 80 | + ElementLevelData level0; |
| 81 | + |
| 82 | + ElementGraphData(size_t maxLevel, size_t high_level_size, |
| 83 | + std::shared_ptr<VecSimAllocator> allocator) |
| 84 | + : toplevel(maxLevel), others(nullptr), level0(allocator) { |
| 85 | + if (toplevel > 0) { |
| 86 | + others = (ElementLevelData *)allocator->callocate(high_level_size * toplevel); |
| 87 | + if (others == nullptr) { |
| 88 | + throw std::runtime_error("VecSim index low memory error"); |
| 89 | + } |
| 90 | + for (size_t i = 0; i < maxLevel; i++) { |
| 91 | + new ((char *)others + i * high_level_size) ElementLevelData(allocator); |
| 92 | + } |
| 93 | + } |
| 94 | + } |
| 95 | + ~ElementGraphData() = delete; // should be destroyed using `destroy' |
| 96 | + |
| 97 | + void destroy(size_t levelDataSize, std::shared_ptr<VecSimAllocator> allocator) { |
| 98 | + delete this->level0.incomingUnidirectionalEdges; |
| 99 | + ElementLevelData *cur_ld = this->others; |
| 100 | + for (size_t i = 0; i < this->toplevel; i++) { |
| 101 | + delete cur_ld->incomingUnidirectionalEdges; |
| 102 | + cur_ld = reinterpret_cast<ElementLevelData *>(reinterpret_cast<char *>(cur_ld) + |
| 103 | + levelDataSize); |
| 104 | + } |
| 105 | + allocator->free_allocation(this->others); |
| 106 | + } |
| 107 | + ElementLevelData &getElementLevelData(size_t level, size_t levelDataSize) { |
| 108 | + assert(level <= this->toplevel); |
| 109 | + if (level == 0) { |
| 110 | + return this->level0; |
| 111 | + } |
| 112 | + return *reinterpret_cast<ElementLevelData *>(reinterpret_cast<char *>(this->others) + |
| 113 | + (level - 1) * levelDataSize); |
| 114 | + } |
| 115 | +}; |
0 commit comments