1 | #include "simdjson/base.h" |
2 | #include <initializer_list> |
3 | |
4 | namespace simdjson { |
5 | |
6 | bool 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 | |
12 | namespace 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 |
18 | static 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 |
24 | static 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 |
30 | static 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 |
36 | static 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 |
42 | static 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 |
48 | static 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 | */ |
57 | class detect_best_supported_implementation_on_first_use final : public implementation { |
58 | public: |
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) {} |
76 | private: |
77 | const implementation *set_best() const noexcept; |
78 | }; |
79 | |
80 | static 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 |
105 | class unsupported_implementation final : public implementation { |
106 | public: |
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 | |
130 | const unsupported_implementation* get_unsupported_singleton() { |
131 | static const unsupported_implementation unsupported_singleton{}; |
132 | return &unsupported_singleton; |
133 | } |
134 | |
135 | size_t available_implementation_list::size() const noexcept { |
136 | return internal::get_available_implementation_pointers().size(); |
137 | } |
138 | const implementation * const *available_implementation_list::begin() const noexcept { |
139 | return internal::get_available_implementation_pointers().begin(); |
140 | } |
141 | const implementation * const *available_implementation_list::end() const noexcept { |
142 | return internal::get_available_implementation_pointers().end(); |
143 | } |
144 | const 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 | |
154 | const 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 | |
174 | SIMDJSON_DLLIMPORTEXPORT const internal::available_implementation_list& get_available_implementations() { |
175 | static const internal::available_implementation_list available_implementations{}; |
176 | return available_implementations; |
177 | } |
178 | |
179 | SIMDJSON_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 | |
185 | simdjson_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 | } |
188 | simdjson_warn_unused bool validate_utf8(const char *buf, size_t len) noexcept { |
189 | return get_active_implementation()->validate_utf8(buf, len); |
190 | } |
191 | const 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 | |