1#include "simdjson/base.h"
2#include <initializer_list>
3
4namespace simdjson {
5
6bool implementation::supported_by_runtime_system() const {
7 uint32_t required_instruction_sets = this->required_instruction_sets();
8 uint32_t supported_instruction_sets = internal::detect_supported_architectures();
9 return ((supported_instruction_sets & required_instruction_sets) == required_instruction_sets);
10}
11
12namespace internal {
13
14// Static array of known implementations. We're hoping these get baked into the executable
15// without requiring a static initializer.
16
17#if SIMDJSON_IMPLEMENTATION_ICELAKE
18static const icelake::implementation* get_icelake_singleton() {
19 static const icelake::implementation icelake_singleton{};
20 return &icelake_singleton;
21}
22#endif
23#if SIMDJSON_IMPLEMENTATION_HASWELL
24static const haswell::implementation* get_haswell_singleton() {
25 static const haswell::implementation haswell_singleton{};
26 return &haswell_singleton;
27}
28#endif
29#if SIMDJSON_IMPLEMENTATION_WESTMERE
30static const westmere::implementation* get_westmere_singleton() {
31 static const westmere::implementation westmere_singleton{};
32 return &westmere_singleton;
33}
34#endif // SIMDJSON_IMPLEMENTATION_WESTMERE
35#if SIMDJSON_IMPLEMENTATION_ARM64
36static const arm64::implementation* get_arm64_singleton() {
37 static const arm64::implementation arm64_singleton{};
38 return &arm64_singleton;
39}
40#endif // SIMDJSON_IMPLEMENTATION_ARM64
41#if SIMDJSON_IMPLEMENTATION_PPC64
42static const ppc64::implementation* get_ppc64_singleton() {
43 static const ppc64::implementation ppc64_singleton{};
44 return &ppc64_singleton;
45}
46#endif // SIMDJSON_IMPLEMENTATION_PPC64
47#if SIMDJSON_IMPLEMENTATION_FALLBACK
48static const fallback::implementation* get_fallback_singleton() {
49 static const fallback::implementation fallback_singleton{};
50 return &fallback_singleton;
51}
52#endif // SIMDJSON_IMPLEMENTATION_FALLBACK
53
54/**
55 * @private Detects best supported implementation on first use, and sets it
56 */
57class detect_best_supported_implementation_on_first_use final : public implementation {
58public:
59 const std::string &name() const noexcept final { return set_best()->name(); }
60 const std::string &description() const noexcept final { return set_best()->description(); }
61 uint32_t required_instruction_sets() const noexcept final { return set_best()->required_instruction_sets(); }
62 simdjson_warn_unused error_code create_dom_parser_implementation(
63 size_t capacity,
64 size_t max_length,
65 std::unique_ptr<internal::dom_parser_implementation>& dst
66 ) const noexcept final {
67 return set_best()->create_dom_parser_implementation(capacity, max_depth: max_length, dst);
68 }
69 simdjson_warn_unused error_code minify(const uint8_t *buf, size_t len, uint8_t *dst, size_t &dst_len) const noexcept final {
70 return set_best()->minify(buf, len, dst, dst_len);
71 }
72 simdjson_warn_unused bool validate_utf8(const char * buf, size_t len) const noexcept final override {
73 return set_best()->validate_utf8(buf, len);
74 }
75 simdjson_inline detect_best_supported_implementation_on_first_use() noexcept : implementation("best_supported_detector", "Detects the best supported implementation and sets it", 0) {}
76private:
77 const implementation *set_best() const noexcept;
78};
79
80static const std::initializer_list<const implementation *>& get_available_implementation_pointers() {
81 static const std::initializer_list<const implementation *> available_implementation_pointers {
82#if SIMDJSON_IMPLEMENTATION_ICELAKE
83 get_icelake_singleton(),
84#endif
85#if SIMDJSON_IMPLEMENTATION_HASWELL
86 get_haswell_singleton(),
87#endif
88#if SIMDJSON_IMPLEMENTATION_WESTMERE
89 get_westmere_singleton(),
90#endif
91#if SIMDJSON_IMPLEMENTATION_ARM64
92 get_arm64_singleton(),
93#endif
94#if SIMDJSON_IMPLEMENTATION_PPC64
95 get_ppc64_singleton(),
96#endif
97#if SIMDJSON_IMPLEMENTATION_FALLBACK
98 get_fallback_singleton(),
99#endif
100 }; // available_implementation_pointers
101 return available_implementation_pointers;
102}
103
104// So we can return UNSUPPORTED_ARCHITECTURE from the parser when there is no support
105class unsupported_implementation final : public implementation {
106public:
107 simdjson_warn_unused error_code create_dom_parser_implementation(
108 size_t,
109 size_t,
110 std::unique_ptr<internal::dom_parser_implementation>&
111 ) const noexcept final {
112 return UNSUPPORTED_ARCHITECTURE;
113 }
114 simdjson_warn_unused error_code minify(const uint8_t *, size_t, uint8_t *, size_t &) const noexcept final override {
115 return UNSUPPORTED_ARCHITECTURE;
116 }
117 simdjson_warn_unused bool validate_utf8(const char *, size_t) const noexcept final override {
118 return false; // Just refuse to validate. Given that we have a fallback implementation
119 // it seems unlikely that unsupported_implementation will ever be used. If it is used,
120 // then it will flag all strings as invalid. The alternative is to return an error_code
121 // from which the user has to figure out whether the string is valid UTF-8... which seems
122 // like a lot of work just to handle the very unlikely case that we have an unsupported
123 // implementation. And, when it does happen (that we have an unsupported implementation),
124 // what are the chances that the programmer has a fallback? Given that *we* provide the
125 // fallback, it implies that the programmer would need a fallback for our fallback.
126 }
127 unsupported_implementation() : implementation("unsupported", "Unsupported CPU (no detected SIMD instructions)", 0) {}
128};
129
130const unsupported_implementation* get_unsupported_singleton() {
131 static const unsupported_implementation unsupported_singleton{};
132 return &unsupported_singleton;
133}
134
135size_t available_implementation_list::size() const noexcept {
136 return internal::get_available_implementation_pointers().size();
137}
138const implementation * const *available_implementation_list::begin() const noexcept {
139 return internal::get_available_implementation_pointers().begin();
140}
141const implementation * const *available_implementation_list::end() const noexcept {
142 return internal::get_available_implementation_pointers().end();
143}
144const implementation *available_implementation_list::detect_best_supported() const noexcept {
145 // They are prelisted in priority order, so we just go down the list
146 uint32_t supported_instruction_sets = internal::detect_supported_architectures();
147 for (const implementation *impl : internal::get_available_implementation_pointers()) {
148 uint32_t required_instruction_sets = impl->required_instruction_sets();
149 if ((supported_instruction_sets & required_instruction_sets) == required_instruction_sets) { return impl; }
150 }
151 return get_unsupported_singleton(); // this should never happen?
152}
153
154const implementation *detect_best_supported_implementation_on_first_use::set_best() const noexcept {
155 SIMDJSON_PUSH_DISABLE_WARNINGS
156 SIMDJSON_DISABLE_DEPRECATED_WARNING // Disable CRT_SECURE warning on MSVC: manually verified this is safe
157 char *force_implementation_name = getenv(name: "SIMDJSON_FORCE_IMPLEMENTATION");
158 SIMDJSON_POP_DISABLE_WARNINGS
159
160 if (force_implementation_name) {
161 auto force_implementation = get_available_implementations()[force_implementation_name];
162 if (force_implementation) {
163 return get_active_implementation() = force_implementation;
164 } else {
165 // Note: abort() and stderr usage within the library is forbidden.
166 return get_active_implementation() = get_unsupported_singleton();
167 }
168 }
169 return get_active_implementation() = get_available_implementations().detect_best_supported();
170}
171
172} // namespace internal
173
174SIMDJSON_DLLIMPORTEXPORT const internal::available_implementation_list& get_available_implementations() {
175 static const internal::available_implementation_list available_implementations{};
176 return available_implementations;
177}
178
179SIMDJSON_DLLIMPORTEXPORT internal::atomic_ptr<const implementation>& get_active_implementation() {
180 static const internal::detect_best_supported_implementation_on_first_use detect_best_supported_implementation_on_first_use_singleton;
181 static internal::atomic_ptr<const implementation> active_implementation{&detect_best_supported_implementation_on_first_use_singleton};
182 return active_implementation;
183}
184
185simdjson_warn_unused error_code minify(const char *buf, size_t len, char *dst, size_t &dst_len) noexcept {
186 return get_active_implementation()->minify(buf: reinterpret_cast<const uint8_t *>(buf), len, dst: reinterpret_cast<uint8_t *>(dst), dst_len);
187}
188simdjson_warn_unused bool validate_utf8(const char *buf, size_t len) noexcept {
189 return get_active_implementation()->validate_utf8(buf, len);
190}
191const implementation * builtin_implementation() {
192 static const implementation * builtin_impl = get_available_implementations()[SIMDJSON_STRINGIFY(SIMDJSON_BUILTIN_IMPLEMENTATION)];
193 assert(builtin_impl);
194 return builtin_impl;
195}
196
197
198} // namespace simdjson
199