Skip to content

Commit e345c85

Browse files
committed
Add some optimized data structures for maps on a small number of
keys, especially enumerated keys.
1 parent c98d93d commit e345c85

File tree

7 files changed

+1174
-0
lines changed

7 files changed

+1174
-0
lines changed

include/swift/Basic/EnumMap.h

Lines changed: 203 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,203 @@
1+
//===--- EnumMap.h - A map optimized for having enum keys -------*- C++ -*-===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2024 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
///
13+
/// This file defines the EnumMap class template, which is a map data
14+
/// structure optimized for working with enumerated keys. It is built on
15+
/// top of SmallMap, but it replaces the default large map with a flat
16+
/// heap-allocated array of indexes into the elements, which is reasonable
17+
/// for small-ish enums.
18+
///
19+
/// Currently the map requires the key type to be an enum type.
20+
/// The expectation is that the enum has a small number of enumerators
21+
/// which are all in the range 0..<NumValues. NumValues must be provided
22+
/// by specializing the EnumTraits class.
23+
///
24+
/// The elements of the map remain insertion-ordered for the lifetime of
25+
/// the map. There are currently no operations to remove elements.
26+
/// Iterators are invalidated by insertion.
27+
///
28+
//===----------------------------------------------------------------------===//
29+
30+
#ifndef SWIFT_BASIC_ENUMMAP_H
31+
#define SWIFT_BASIC_ENUMMAP_H
32+
33+
#include "swift/Basic/EnumTraits.h"
34+
#include "swift/Basic/SmallMap.h"
35+
#include "llvm/ADT/SmallVector.h"
36+
#include <type_traits>
37+
38+
namespace swift {
39+
40+
/// The maximum number of elements that the map can have before
41+
/// it flips from brute-force searching the keys to using a sparse
42+
/// array.
43+
static constexpr size_t DefaultEnumMapDirectSearchLimit =
44+
DefaultSmallMapDirectSearchLimit;
45+
46+
/// The primary customization point for an EnumMap.
47+
///
48+
/// template <>
49+
/// struct EnumMapTraits<MyKey> {
50+
/// using IndexType = <some integer type>;
51+
/// struct LargeMapStorage {
52+
/// std::optional<IndexType> find(IndexType) const;
53+
/// std::pair<IndexType, bool> insert(IndexType key, IndexType value);
54+
/// };
55+
/// };
56+
template <class Key, class KeyTraits = EnumTraits<Key>>
57+
struct EnumMapTraits;
58+
59+
template <class Key, class Value,
60+
size_t DirectSearchLimit = DefaultEnumMapDirectSearchLimit,
61+
class MapTraits = EnumMapTraits<Key>,
62+
class ElementStorage = llvm::SmallVector<Value>>
63+
class EnumMap {
64+
using IndexType = typename MapTraits::IndexType;
65+
66+
// EnumMapTraits is currently designed to be usable directly as a
67+
// SmallMapTraits.
68+
using MapType =
69+
SmallMap<IndexType, Value, DirectSearchLimit, MapTraits, ElementStorage>;
70+
MapType map;
71+
72+
public:
73+
bool empty() const { return map.empty(); }
74+
size_t size() const { return map.size(); }
75+
76+
using iterator = typename MapType::iterator;
77+
iterator begin() { return map.begin(); }
78+
iterator end() { return map.end(); }
79+
80+
using const_iterator = typename MapType::const_iterator;
81+
const_iterator begin() const { return map.begin(); }
82+
const_iterator end() const { return map.end(); }
83+
84+
/// Look up a key in the map. Returns end() if the entry is not found.
85+
const_iterator find(Key key) const {
86+
return map.find(IndexType(key));
87+
}
88+
89+
/// Try to insert the given key/value pair. If there's already an element
90+
/// with this key, return false and an iterator for the existing element.
91+
/// Otherwise, return true and an iterator for the new element.
92+
///
93+
/// The value in the set will be constructed by emplacing it with the
94+
/// given arguments.
95+
template <class... Args>
96+
std::pair<iterator, bool> insert(Key key, Args &&...valueArgs) {
97+
return map.insert(IndexType(key), std::forward<Args>(valueArgs)...);
98+
}
99+
};
100+
101+
namespace EnumMapImpl {
102+
103+
template <size_t N,
104+
bool SmallEnoughForUInt8 = (N < (1U << 8)),
105+
bool SmallEnoughForUInt16 = (N < (1U << 16))>
106+
struct SufficientIntFor;
107+
108+
template <size_t N>
109+
struct SufficientIntFor<N, true, true> {
110+
using type = uint8_t;
111+
};
112+
113+
template <size_t N>
114+
struct SufficientIntFor<N, false, true> {
115+
using type = uint16_t;
116+
};
117+
118+
template <size_t N>
119+
struct SufficientIntFor<N, false, false> {
120+
static_assert(N < (1ULL << 32), "just how large is this \"enum\" exactly");
121+
using type = uint32_t;
122+
};
123+
124+
/// A map from integers in 0..<N to integers in 0..<N, implemented as a
125+
/// flat array of integers in 0...N, with zero meaning a missing entry.
126+
///
127+
/// This is a great implementation for N <= 255, where the
128+
/// entire flat array is <= 256 bytes. It gets increasingly marginal
129+
/// for N up to ~1K or so (unless we really expect to have entries
130+
/// for a large proportion of the enum). Past that, we should probably
131+
/// be falling back on something like a hashtable, because needing tens
132+
/// of kilobytes to hold as few as 17 entries is objectively unreasonable.
133+
template <size_t N>
134+
class FlatMap {
135+
public:
136+
using IndexType = typename SufficientIntFor<N>::type;
137+
using StoredIndexType = typename SufficientIntFor<N + 1>::type;
138+
139+
private:
140+
StoredIndexType *ptr;
141+
142+
public:
143+
FlatMap() : ptr(new StoredIndexType[N]) {
144+
memset(ptr, 0, N * sizeof(StoredIndexType));
145+
}
146+
FlatMap(FlatMap &&other)
147+
: ptr(other.ptr) {
148+
other.ptr = nullptr;
149+
}
150+
FlatMap &operator=(FlatMap &&other) {
151+
delete ptr;
152+
ptr = other.ptr;
153+
other.ptr = nullptr;
154+
}
155+
FlatMap(const FlatMap &other)
156+
: ptr(new StoredIndexType[N]) {
157+
memcpy(ptr, other.ptr, N * sizeof(StoredIndexType));
158+
}
159+
FlatMap &operator=(const FlatMap &other) {
160+
memcpy(ptr, other.ptr, N * sizeof(StoredIndexType));
161+
}
162+
163+
~FlatMap() {
164+
delete ptr;
165+
}
166+
167+
std::pair<IndexType, bool> insert(IndexType key, IndexType value) {
168+
assert(key < N);
169+
StoredIndexType &entry = ptr[key];
170+
if (entry == 0) {
171+
entry = StoredIndexType(value) + 1;
172+
return std::make_pair(value, true);
173+
} else {
174+
return std::make_pair(IndexType(entry - 1), false);
175+
}
176+
}
177+
178+
std::optional<IndexType> find(IndexType key) const {
179+
assert(key < N);
180+
StoredIndexType entry = ptr[key];
181+
if (entry == 0) {
182+
return std::nullopt;
183+
} else {
184+
return IndexType(entry - 1);
185+
}
186+
};
187+
};
188+
189+
} // end namespace EnumMapImpl
190+
191+
/// The default implementation of EnumMapTraits.
192+
template <class Key_, class KeyTraits_>
193+
struct EnumMapTraits {
194+
using Key = Key_;
195+
using KeyTraits = KeyTraits_;
196+
197+
using LargeMapStorage = EnumMapImpl::FlatMap<KeyTraits::NumValues>;
198+
using IndexType = typename LargeMapStorage::IndexType;
199+
};
200+
201+
} // end namespace swift
202+
203+
#endif

include/swift/Basic/EnumTraits.h

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
//===--- EnumTraits.h - Traits for densely-packed enums ---------*- C++ -*-===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2024 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
///
13+
/// This file defines the EnumTraits concept, which can be used to
14+
/// communicate information about an enum type's enumerators that currently
15+
/// can't be recovered from the compiler.
16+
///
17+
//===----------------------------------------------------------------------===//
18+
19+
#ifndef SWIFT_BASIC_ENUMTRAITS_H
20+
#define SWIFT_BASIC_ENUMTRAITS_H
21+
22+
namespace swift {
23+
24+
/// A simple traits concept for recording the number of cases in an enum.
25+
///
26+
/// template <> class EnumTraits<WdigetKind> {
27+
/// static constexpr size_t NumValues = NumWidgetKinds;
28+
/// };
29+
template <class E>
30+
struct EnumTraits;
31+
32+
} // end namespace swift
33+
34+
#endif

0 commit comments

Comments
 (0)