Reactos
at master 232 lines 8.1 kB view raw
1// 2// strnlen.cpp 3// 4// Copyright (c) Microsoft Corporation. All rights reserved. 5// 6// Defines strnlen and wcsnlen, which return the length of a null-terminated 7// string, not including the null terminator itself, up to the specified maximum 8// number of characters. 9// 10#include <corecrt_internal.h> 11#include <corecrt_internal_simd.h> 12#include <stdlib.h> 13#include <string.h> 14 15 16 17// Disable "warning C4752: found Intel(R) Advanced Vector Extensions; consider 18// using /arch:AVX." We verify that we can use AVX2 before we execute those 19// instructions. 20#pragma warning(disable: 4752) 21 22 23 24//namespace // clang doesn't like this! 25//{ 26 enum strnlen_mode 27 { 28 bounded, // strnlen mode; maximum_count is respected 29 unbounded, // strlen mode; maximum_count is ignored 30 }; 31//} 32 33// This function returns true if we have reached the end of the range to be 34// searched for a terminator. For the bounded strnlen functions, we must 35// test to see whether 36template <strnlen_mode Mode> 37static __forceinline bool __cdecl last_reached( 38 void const* const it, 39 void const* const last 40 ) throw() 41{ 42 return it == last; 43} 44 45template <> 46__forceinline bool __cdecl last_reached<unbounded>( 47 void const* const it, 48 void const* const last 49 ) throw() 50{ 51 UNREFERENCED_PARAMETER(it); 52 UNREFERENCED_PARAMETER(last); 53 54 return false; 55} 56 57 58 59// An implementation of strnlen using plain C, suitable for any architecture: 60template <strnlen_mode Mode, typename Element> 61_Check_return_ 62_When_(maximum_count > _String_length_(string), _Post_satisfies_(return == _String_length_(string))) 63_When_(maximum_count <= _String_length_(string), _Post_satisfies_(return == maximum_count)) 64static __forceinline size_t __cdecl common_strnlen_c( 65 Element const* const string, 66 size_t const maximum_count 67 ) throw() 68{ 69 Element const* const last = string + maximum_count; 70 Element const* it = string; 71 72 for (; !last_reached<Mode>(it, last) && *it != '\0'; ++it) 73 { 74 } 75 76 return static_cast<size_t>(it - string); 77} 78 79#ifdef _CRT_SIMD_SUPPORT_AVAILABLE 80 81 82 template <strnlen_mode Mode, __crt_simd_isa Isa, typename Element> 83 _Check_return_ 84 _When_(maximum_count > _String_length_(string), _Post_satisfies_(return == _String_length_(string))) 85 _When_(maximum_count <= _String_length_(string), _Post_satisfies_(return == maximum_count)) 86 size_t __cdecl common_strnlen_simd( 87 Element const* const string, 88 size_t const maximum_count 89 ) throw() 90#if (defined(__GNUC__) || defined(__clang__)) && !defined(_UCRT_BUILD_SSE2) && !defined(_UCRT_BUILD_AVX2) 91 ; 92#else 93 { 94 using traits = __crt_simd_traits<Isa, Element>; 95 96 // For efficient SIMD processing of the string, we will use a typical three- 97 // -phase computation: 98 // 99 // [1] We compute the number of bytes from the start of the string to the 100 // next element_size boundary and process these bytes individually. If 101 // we find a \0 we return immediately. 102 // 103 // [2] At this point, we now have a pointer to an aligned block of bytes. 104 // We process bytes in element_size chunks until there are fewer than 105 // element_size bytes remaining to be examined. If we find a chunk 106 // that contains a \0 we break out of the loop without advancing to 107 // the next chunk, to let the phase 3 loop reexamine the chunk. 108 // 109 // [3] We process the remaining bytes individually. If we find a \0 we 110 // return immediately. 111 // 112 // Note that in phase [2] we may read bytes beyond the terminator (and thus 113 // beyond the end of the string). This is okay, because we are reading 114 // aligned chunks, so a chunk will never straddle a page boundary and if we 115 // can read any byte from the chunk we can read all bytes from the chunk. 116 // 117 // Here we go... 118 uintptr_t const string_integer = reinterpret_cast<uintptr_t>(string); 119 if (string_integer % traits::element_size != 0) 120 { 121 // If the input string is itself unaligned (e.g. if it is a wchar_t* 122 // with an odd address), we can't align for vector processing. Switch 123 // back to the slow implementation: 124 return common_strnlen_c<Mode>(string, maximum_count); 125 } 126 127 // [1] Alignment Loop (Prefix) 128 uintptr_t const prefix_forward_offset = string_integer % traits::pack_size; 129 uintptr_t const prefix_reverse_offset = prefix_forward_offset == 0 130 ? 0 131 : traits::pack_size - prefix_forward_offset; 132 133 size_t const prefix_count = __min(maximum_count, prefix_reverse_offset / traits::element_size); 134 size_t const prefix_result = common_strnlen_c<bounded>(string, prefix_count); 135 if (prefix_result != prefix_count) 136 { 137 return prefix_result; 138 } 139 140 Element const* it = string + prefix_result; 141 142 // [2] Aligned Vector Loop (Middle) 143 __crt_simd_cleanup_guard<Isa> const simd_cleanup; 144 145 typename traits::pack_type const zero = traits::get_zero_pack(); 146 147 size_t const middle_and_suffix_count = maximum_count - prefix_count; 148 size_t const suffix_count = middle_and_suffix_count % traits::pack_size; 149 size_t const middle_count = middle_and_suffix_count - suffix_count; 150 151 Element const* const middle_last = it + middle_count; 152 while (!last_reached<Mode>(it, middle_last)) 153 { 154 auto const element_it = reinterpret_cast<typename traits::pack_type const*>(it); 155 156 bool const element_has_terminator = traits::compute_byte_mask(traits::compare_equals(*element_it, zero)) != 0; 157 if (element_has_terminator) 158 { 159 break; 160 } 161 162 it += traits::elements_per_pack; 163 } 164 165 // [3] Remainder Loop (Suffix) 166 Element const* const suffix_last = string + maximum_count; 167 for (; !last_reached<Mode>(it, suffix_last) && *it != '\0'; ++it) 168 { 169 } 170 171 // Either we have exhausted the buffer or we have found the terminator: 172 return static_cast<size_t>(it - string); 173 } 174 175#endif // (defined(__GNUC__) || defined(__clang__)) && !defined(_UCRT_BUILD_SSE2) && !defined(_UCRT_BUILD_AVX2) 176 177#endif // _CRT_SIMD_SUPPORT_AVAILABLE 178 179#if !defined(_UCRT_BUILD_SSE2) && !defined(_UCRT_BUILD_AVX2) 180 181template <strnlen_mode Mode, typename Element> 182_Check_return_ 183_When_(maximum_count > _String_length_(string), _Post_satisfies_(return == _String_length_(string))) 184_When_(maximum_count <= _String_length_(string), _Post_satisfies_(return == maximum_count)) 185static __forceinline size_t __cdecl common_strnlen( 186 Element const* const string, 187 size_t const maximum_count 188 ) throw() 189{ 190 #ifdef _CRT_SIMD_SUPPORT_AVAILABLE 191 if (__isa_available >= __ISA_AVAILABLE_AVX2) 192 { 193 return common_strnlen_simd<Mode, __crt_simd_isa::avx2>(string, maximum_count); 194 } 195 else if (__isa_available >= __ISA_AVAILABLE_SSE2) 196 { 197 return common_strnlen_simd<Mode, __crt_simd_isa::sse2>(string, maximum_count); 198 } 199 #endif 200 201 return common_strnlen_c<Mode>(string, maximum_count); 202} 203 204#if !defined(_M_ARM64) && !defined(_M_ARM64EC) 205 206extern "C" size_t __cdecl strnlen( 207 char const* const string, 208 size_t const maximum_count 209 ) 210{ 211 return common_strnlen<bounded>(reinterpret_cast<uint8_t const*>(string), maximum_count); 212} 213 214extern "C" size_t __cdecl wcsnlen( 215 wchar_t const* const string, 216 size_t const maximum_count 217 ) 218{ 219 return common_strnlen<bounded>(reinterpret_cast<uint16_t const*>(string), maximum_count); 220} 221 222#pragma function(wcslen) 223 224extern "C" size_t __cdecl wcslen( 225 wchar_t const* const string 226 ) 227{ 228 return common_strnlen<unbounded>(reinterpret_cast<uint16_t const*>(string), _CRT_UNBOUNDED_BUFFER_SIZE); 229} 230 231#endif // _M_ARM64 232#endif // !defined(_UCRT_BUILD_SSE2) && !defined(_UCRT_BUILD_AVX2)