Files
swift-mirror/include/swift/Runtime/PrebuiltStringMap.h
Mike Ash edb292dda5 [Runtime] Do a proper hash table lookup in the prespecializations library.
We were doing a linear scan of the table contents as a stopgap. Stop doing that, and compute the proper key for the lookup, matching the one used in the builder.
2024-01-26 12:04:02 -05:00

186 lines
5.2 KiB
C++

//===--- PrebuiltStringMap.h - Statically built string map ------*- C++ -*-===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2024 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
#ifndef SWIFT_PREBUILT_STRING_MAP_H
#define SWIFT_PREBUILT_STRING_MAP_H
#include <cassert>
#include <cstddef>
#include <cstdint>
#include <cstring>
namespace swift {
/// A map that can be pre-built out of process. Uses a fixed hash function with
/// no per-process seeding to ensure consistent hashes between builder and user.
///
/// The elements are tail allocated. `byteSize` can be used to calculate the
/// amount of memory needed. The memory must be initialized with all string
/// values set to null. StringTy is opaque for insertion, except for using the
/// provided stringIsNull function to check for null values.
template <typename StringTy, typename ElemTy, bool (*stringIsNull)(StringTy)>
struct PrebuiltStringMap {
uint64_t arraySize;
struct ArrayElement {
StringTy key;
ElemTy value;
};
ArrayElement *array() {
uintptr_t start = (uintptr_t)(&arraySize + 1);
return (ArrayElement *)start;
}
const ArrayElement *array() const {
uintptr_t start = (uintptr_t)(&arraySize + 1);
return (ArrayElement *)start;
}
static size_t byteSize(uint64_t arraySize) {
return sizeof(PrebuiltStringMap) + sizeof(ArrayElement) * arraySize;
}
/// Construct an empty map. Must be constructed in memory at least as large as
/// byteSize(arraySize). The map can hold at most arraySize-1 values.
/// Attempting to insert more than that will result in fatal errors when
/// inserting or retrieving values.
PrebuiltStringMap(uint64_t arraySize) : arraySize(arraySize) {}
// Based on MurmurHash2
uint64_t hash(const void *data, size_t len) const {
uint64_t magic = 0xc6a4a7935bd1e995ULL;
uint64_t salt = 47;
uint64_t hash = len * magic;
const uint8_t *cursor = (const uint8_t *)data;
const uint8_t *bulkEnd = cursor + (len & ~(sizeof(uint64_t) - 1));
size_t remaining = len;
while (cursor != bulkEnd) {
uint64_t value;
memcpy(&value, cursor, sizeof(uint64_t));
cursor += sizeof(uint64_t);
remaining -= sizeof(uint64_t);
value *= magic;
value ^= value >> salt;
value *= magic;
hash ^= value;
hash *= magic;
}
// This is never going to be false, but it's comforting.
static_assert(sizeof(uint64_t) == 8);
// Collect the last few bytes.
switch (remaining & 7) {
case 7:
hash ^= (uint64_t)cursor[6] << 48;
[[fallthrough]];
case 6:
hash ^= (uint64_t)cursor[5] << 40;
[[fallthrough]];
case 5:
hash ^= (uint64_t)cursor[4] << 32;
[[fallthrough]];
case 4:
hash ^= (uint64_t)cursor[3] << 24;
[[fallthrough]];
case 3:
hash ^= (uint64_t)cursor[2] << 16;
[[fallthrough]];
case 2:
hash ^= (uint64_t)cursor[1] << 8;
[[fallthrough]];
case 1:
hash ^= (uint64_t)cursor[0];
}
hash *= magic;
hash ^= hash >> salt;
hash *= magic;
hash ^= hash >> salt;
return hash;
}
/// Perform the search portion of an insertion operation. Returns a pointer to
/// the element where string is to be inserted. The caller is responsible for
/// initializing the element to contain the string/value. It is assumed that
/// the key does not already exist in the map. If it does exist, this will
/// insert a useless duplicate.
ArrayElement *insert(const void *string, size_t len) {
uint64_t hashValue = hash(string, len);
size_t index = hashValue % arraySize;
size_t numSearched = 0;
while (!stringIsNull(array()[index].key)) {
index = index + 1;
if (index >= arraySize)
index = 0;
numSearched++;
if (numSearched > arraySize) {
assert(false &&
"Could not find empty element in PrebuiltStringMap::insert");
return nullptr;
}
}
return &array()[index];
}
ArrayElement *insert(const char *string) {
return insert(string, strlen(string));
}
/// Look up the given string in the table. Requires that StringTy be
/// `const char *`.
const ArrayElement *find(const char *toFind) const {
size_t len = strlen(toFind);
return find(toFind, len);
}
const ArrayElement *find(const char *toFind, size_t len) const {
uint64_t hashValue = hash(toFind, len);
size_t index = hashValue % arraySize;
size_t numSearched = 0;
while (const char *key = array()[index].key) {
if (strcmp(key, toFind) == 0)
return &array()[index];
index = index + 1;
if (index >= arraySize)
index = 0;
numSearched++;
if (numSearched > arraySize) {
assert(
false &&
"Could not find match or empty element in PrebuiltStringMap::find");
return nullptr;
}
}
return nullptr;
}
};
} // namespace swift
#endif // SWIFT_PREBUILT_STRING_MAP_H