in cpp/src/hnswalg.h [1280:1418]
tableint addPoint(const data_t *data_point, labeltype label, int level) {
std::shared_lock<std::shared_mutex> lock(resizeLock);
tableint cur_c = 0;
{
// Checking if the element with the same label already exists
// if so, updating it *instead* of creating a new element.
std::unique_lock<std::mutex> templock_curr(cur_element_count_guard_);
auto search = label_lookup_.find(label);
if (search != label_lookup_.end()) {
tableint existingInternalId = search->second;
templock_curr.unlock();
std::unique_lock<std::mutex> lock_el_update(link_list_update_locks_[(
existingInternalId & (max_update_element_locks - 1))]);
if (isMarkedDeleted(existingInternalId)) {
unmarkDeletedInternal(existingInternalId);
}
updatePoint(data_point, existingInternalId, 1.0);
return existingInternalId;
}
if (cur_element_count >= max_elements_) {
throw IndexFullError(
"Cannot insert elements; this index already contains " +
std::to_string(cur_element_count) +
" elements, and its maximum size is " +
std::to_string(max_elements_) +
". Call resizeIndex first to increase the maximum size of the "
"index.");
};
cur_c = cur_element_count;
cur_element_count++;
label_lookup_[label] = cur_c;
}
// Take update lock to prevent race conditions on an element with
// insertion/update at the same time.
std::unique_lock<std::mutex> lock_el_update(
link_list_update_locks_[(cur_c & (max_update_element_locks - 1))]);
std::unique_lock<std::mutex> lock_el(link_list_locks_[cur_c]);
int curlevel = getRandomLevel(mult_);
if (level > 0)
curlevel = level;
element_levels_[cur_c] = curlevel;
std::unique_lock<std::mutex> templock(global);
int maxlevelcopy = maxlevel_;
if (curlevel <= maxlevelcopy)
templock.unlock();
tableint currObj = enterpoint_node_;
tableint enterpoint_copy = enterpoint_node_;
memset(data_level0_memory_ + cur_c * size_data_per_element_ + offsetLevel0_,
0, size_data_per_element_);
// Initialisation of the data and label
memcpy(getExternalLabeLp(cur_c), &label, sizeof(labeltype));
memcpy(getDataByInternalId(cur_c), data_point, data_size_);
if (curlevel) {
linkLists_[cur_c] =
(char *)malloc(size_links_per_element_ * curlevel + 1);
if (linkLists_[cur_c] == nullptr)
throw std::runtime_error(
"Not enough memory: addPoint failed to allocate linklist");
memset(linkLists_[cur_c], 0, size_links_per_element_ * curlevel + 1);
}
if ((signed)currObj != -1) {
if (curlevel < maxlevelcopy) {
dist_t curdist = fstdistfunc_(data_point, getDataByInternalId(currObj),
dist_func_param_);
for (int level = maxlevelcopy; level > curlevel; level--) {
bool changed = true;
while (changed) {
changed = false;
unsigned int *data;
std::unique_lock<std::mutex> lock(link_list_locks_[currObj]);
data = get_linklist(currObj, level);
int size = getListCount(data);
tableint *datal = (tableint *)(data + 1);
for (int i = 0; i < size; i++) {
tableint cand = datal[i];
if (cand < 0 || cand > max_elements_)
throw std::runtime_error("cand error");
dist_t d = fstdistfunc_(data_point, getDataByInternalId(cand),
dist_func_param_);
if (d < curdist) {
curdist = d;
currObj = cand;
changed = true;
}
}
}
}
}
bool epDeleted = isMarkedDeleted(enterpoint_copy);
for (int level = std::min(curlevel, maxlevelcopy); level >= 0; level--) {
if (level > maxlevelcopy || level < 0) // possible?
throw std::runtime_error("Level error");
std::priority_queue<std::pair<dist_t, tableint>,
std::vector<std::pair<dist_t, tableint>>,
CompareByFirst>
top_candidates = searchBaseLayer(currObj, data_point, level);
if (epDeleted) {
top_candidates.emplace(
fstdistfunc_(data_point, getDataByInternalId(enterpoint_copy),
dist_func_param_),
enterpoint_copy);
if (top_candidates.size() > ef_construction_)
top_candidates.pop();
}
currObj = mutuallyConnectNewElement(data_point, cur_c, top_candidates,
level, false);
}
} else {
// Do nothing for the first element
enterpoint_node_ = 0;
maxlevel_ = curlevel;
}
// Releasing lock for the maximum level
if (curlevel > maxlevelcopy) {
enterpoint_node_ = cur_c;
maxlevel_ = curlevel;
}
return cur_c;
};