mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
Don't use strcmp to compare the candidate key with the search key, as the search key may not be NUL terminated. Use strncmp and a length check on the candidate key.
189 lines
5.4 KiB
C++
189 lines
5.4 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) {
|
|
// key is NUL terminated but toFind may not be. Check that they have equal
|
|
// contents up to len, and check that key has a terminating NUL at the
|
|
// right point.
|
|
if (strncmp(key, toFind, len) == 0 && key[len] == 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
|