| 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 | |