/src/simdjson/src/implementation.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | #include "simdjson/base.h" |
2 | | #include <initializer_list> |
3 | | |
4 | | namespace simdjson { |
5 | | |
6 | 0 | bool implementation::supported_by_runtime_system() const { |
7 | 0 | uint32_t required_instruction_sets = this->required_instruction_sets(); |
8 | 0 | uint32_t supported_instruction_sets = internal::detect_supported_architectures(); |
9 | 0 | return ((supported_instruction_sets & required_instruction_sets) == required_instruction_sets); |
10 | 0 | } |
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 | 1 | static const icelake::implementation* get_icelake_singleton() { |
19 | 1 | static const icelake::implementation icelake_singleton{}; |
20 | 1 | return &icelake_singleton; |
21 | 1 | } |
22 | | #endif |
23 | | #if SIMDJSON_IMPLEMENTATION_HASWELL |
24 | 1 | static const haswell::implementation* get_haswell_singleton() { |
25 | 1 | static const haswell::implementation haswell_singleton{}; |
26 | 1 | return &haswell_singleton; |
27 | 1 | } |
28 | | #endif |
29 | | #if SIMDJSON_IMPLEMENTATION_WESTMERE |
30 | 1 | static const westmere::implementation* get_westmere_singleton() { |
31 | 1 | static const westmere::implementation westmere_singleton{}; |
32 | 1 | return &westmere_singleton; |
33 | 1 | } |
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 | 1 | static const fallback::implementation* get_fallback_singleton() { |
49 | 1 | static const fallback::implementation fallback_singleton{}; |
50 | 1 | return &fallback_singleton; |
51 | 1 | } |
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 | 0 | const std::string &name() const noexcept final { return set_best()->name(); } |
60 | 0 | const std::string &description() const noexcept final { return set_best()->description(); } |
61 | 0 | 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 | 1 | ) const noexcept final { |
67 | 1 | return set_best()->create_dom_parser_implementation(capacity, max_length, dst); |
68 | 1 | } |
69 | 0 | simdjson_warn_unused error_code minify(const uint8_t *buf, size_t len, uint8_t *dst, size_t &dst_len) const noexcept final { |
70 | 0 | return set_best()->minify(buf, len, dst, dst_len); |
71 | 0 | } |
72 | 0 | simdjson_warn_unused bool validate_utf8(const char * buf, size_t len) const noexcept final override { |
73 | 0 | return set_best()->validate_utf8(buf, len); |
74 | 0 | } |
75 | 1 | 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 | 1 | static const std::initializer_list<const implementation *>& get_available_implementation_pointers() { |
81 | 1 | static const std::initializer_list<const implementation *> available_implementation_pointers { |
82 | 1 | #if SIMDJSON_IMPLEMENTATION_ICELAKE |
83 | 1 | get_icelake_singleton(), |
84 | 1 | #endif |
85 | 1 | #if SIMDJSON_IMPLEMENTATION_HASWELL |
86 | 1 | get_haswell_singleton(), |
87 | 1 | #endif |
88 | 1 | #if SIMDJSON_IMPLEMENTATION_WESTMERE |
89 | 1 | get_westmere_singleton(), |
90 | 1 | #endif |
91 | | #if SIMDJSON_IMPLEMENTATION_ARM64 |
92 | | get_arm64_singleton(), |
93 | | #endif |
94 | | #if SIMDJSON_IMPLEMENTATION_PPC64 |
95 | | get_ppc64_singleton(), |
96 | | #endif |
97 | 1 | #if SIMDJSON_IMPLEMENTATION_FALLBACK |
98 | 1 | get_fallback_singleton(), |
99 | 1 | #endif |
100 | 1 | }; // available_implementation_pointers |
101 | 1 | return available_implementation_pointers; |
102 | 1 | } |
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 | 0 | ) const noexcept final { |
112 | 0 | return UNSUPPORTED_ARCHITECTURE; |
113 | 0 | } |
114 | 0 | simdjson_warn_unused error_code minify(const uint8_t *, size_t, uint8_t *, size_t &) const noexcept final override { |
115 | 0 | return UNSUPPORTED_ARCHITECTURE; |
116 | 0 | } |
117 | 0 | simdjson_warn_unused bool validate_utf8(const char *, size_t) const noexcept final override { |
118 | 0 | 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 | 0 | } |
127 | 0 | unsupported_implementation() : implementation("unsupported", "Unsupported CPU (no detected SIMD instructions)", 0) {} |
128 | | }; |
129 | | |
130 | 0 | const unsupported_implementation* get_unsupported_singleton() { |
131 | 0 | static const unsupported_implementation unsupported_singleton{}; |
132 | 0 | return &unsupported_singleton; |
133 | 0 | } |
134 | | |
135 | 0 | size_t available_implementation_list::size() const noexcept { |
136 | 0 | return internal::get_available_implementation_pointers().size(); |
137 | 0 | } |
138 | 0 | const implementation * const *available_implementation_list::begin() const noexcept { |
139 | 0 | return internal::get_available_implementation_pointers().begin(); |
140 | 0 | } |
141 | 0 | const implementation * const *available_implementation_list::end() const noexcept { |
142 | 0 | return internal::get_available_implementation_pointers().end(); |
143 | 0 | } |
144 | 1 | 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 | 1 | uint32_t supported_instruction_sets = internal::detect_supported_architectures(); |
147 | 2 | for (const implementation *impl : internal::get_available_implementation_pointers()) { |
148 | 2 | uint32_t required_instruction_sets = impl->required_instruction_sets(); |
149 | 2 | if ((supported_instruction_sets & required_instruction_sets) == required_instruction_sets) { return impl; } |
150 | 2 | } |
151 | 0 | return get_unsupported_singleton(); // this should never happen? |
152 | 1 | } |
153 | | |
154 | 1 | const implementation *detect_best_supported_implementation_on_first_use::set_best() const noexcept { |
155 | 1 | SIMDJSON_PUSH_DISABLE_WARNINGS |
156 | | SIMDJSON_DISABLE_DEPRECATED_WARNING // Disable CRT_SECURE warning on MSVC: manually verified this is safe |
157 | 1 | char *force_implementation_name = getenv("SIMDJSON_FORCE_IMPLEMENTATION"); |
158 | 1 | SIMDJSON_POP_DISABLE_WARNINGS |
159 | | |
160 | 1 | if (force_implementation_name) { |
161 | 0 | auto force_implementation = get_available_implementations()[force_implementation_name]; |
162 | 0 | if (force_implementation) { |
163 | 0 | return get_active_implementation() = force_implementation; |
164 | 0 | } else { |
165 | | // Note: abort() and stderr usage within the library is forbidden. |
166 | 0 | return get_active_implementation() = get_unsupported_singleton(); |
167 | 0 | } |
168 | 0 | } |
169 | 1 | return get_active_implementation() = get_available_implementations().detect_best_supported(); |
170 | 1 | } |
171 | | |
172 | | } // namespace internal |
173 | | |
174 | 1 | SIMDJSON_DLLIMPORTEXPORT const internal::available_implementation_list& get_available_implementations() { |
175 | 1 | static const internal::available_implementation_list available_implementations{}; |
176 | 1 | return available_implementations; |
177 | 1 | } |
178 | | |
179 | 11.3k | SIMDJSON_DLLIMPORTEXPORT internal::atomic_ptr<const implementation>& get_active_implementation() { |
180 | 11.3k | static const internal::detect_best_supported_implementation_on_first_use detect_best_supported_implementation_on_first_use_singleton; |
181 | 11.3k | static internal::atomic_ptr<const implementation> active_implementation{&detect_best_supported_implementation_on_first_use_singleton}; |
182 | 11.3k | return active_implementation; |
183 | 11.3k | } |
184 | | |
185 | 0 | simdjson_warn_unused error_code minify(const char *buf, size_t len, char *dst, size_t &dst_len) noexcept { |
186 | 0 | return get_active_implementation()->minify(reinterpret_cast<const uint8_t *>(buf), len, reinterpret_cast<uint8_t *>(dst), dst_len); |
187 | 0 | } |
188 | 0 | simdjson_warn_unused bool validate_utf8(const char *buf, size_t len) noexcept { |
189 | 0 | return get_active_implementation()->validate_utf8(buf, len); |
190 | 0 | } |
191 | 0 | const implementation * builtin_implementation() { |
192 | 0 | static const implementation * builtin_impl = get_available_implementations()[SIMDJSON_STRINGIFY(SIMDJSON_BUILTIN_IMPLEMENTATION)]; |
193 | 0 | assert(builtin_impl); |
194 | 0 | return builtin_impl; |
195 | 0 | } |
196 | | |
197 | | |
198 | | } // namespace simdjson |