Simple Directmedia Layer
at main 11 kB view raw
1/* 2 Simple DirectMedia Layer 3 Copyright (C) 1997-2024 Sam Lantinga <slouken@libsdl.org> 4 5 This software is provided 'as-is', without any express or implied 6 warranty. In no event will the authors be held liable for any damages 7 arising from the use of this software. 8 9 Permission is granted to anyone to use this software for any purpose, 10 including commercial applications, and to alter it and redistribute it 11 freely, subject to the following restrictions: 12 13 1. The origin of this software must not be misrepresented; you must not 14 claim that you wrote the original software. If you use this software 15 in a product, an acknowledgment in the product documentation would be 16 appreciated but is not required. 17 2. Altered source versions must be plainly marked as such, and must not be 18 misrepresented as being the original software. 19 3. This notice may not be removed or altered from any source distribution. 20*/ 21#include "SDL_internal.h" 22 23#if defined(HAVE_GETHOSTNAME) && !defined(SDL_PLATFORM_WINDOWS) 24#include <unistd.h> 25#endif 26 27// Common utility functions that aren't in the public API 28 29int SDL_powerof2(int x) 30{ 31 int value; 32 33 if (x <= 0) { 34 // Return some sane value - we shouldn't hit this in our use cases 35 return 1; 36 } 37 38 // This trick works for 32-bit values 39 { 40 SDL_COMPILE_TIME_ASSERT(SDL_powerof2, sizeof(x) == sizeof(Uint32)); 41 } 42 value = x; 43 value -= 1; 44 value |= value >> 1; 45 value |= value >> 2; 46 value |= value >> 4; 47 value |= value >> 8; 48 value |= value >> 16; 49 value += 1; 50 51 return value; 52} 53 54Uint32 SDL_CalculateGCD(Uint32 a, Uint32 b) 55{ 56 if (b == 0) { 57 return a; 58 } 59 return SDL_CalculateGCD(b, (a % b)); 60} 61 62// Algorithm adapted with thanks from John Cook's blog post: 63// http://www.johndcook.com/blog/2010/10/20/best-rational-approximation 64void SDL_CalculateFraction(float x, int *numerator, int *denominator) 65{ 66 const int N = 1000; 67 int a = 0, b = 1; 68 int c = 1, d = 0; 69 70 while (b <= N && d <= N) { 71 float mediant = (float)(a + c) / (b + d); 72 if (x == mediant) { 73 if (b + d <= N) { 74 *numerator = a + c; 75 *denominator = b + d; 76 } else if (d > b) { 77 *numerator = c; 78 *denominator = d; 79 } else { 80 *numerator = a; 81 *denominator = b; 82 } 83 return; 84 } else if (x > mediant) { 85 a = a + c; 86 b = b + d; 87 } else { 88 c = a + c; 89 d = b + d; 90 } 91 } 92 if (b > N) { 93 *numerator = c; 94 *denominator = d; 95 } else { 96 *numerator = a; 97 *denominator = b; 98 } 99} 100 101bool SDL_startswith(const char *string, const char *prefix) 102{ 103 if (SDL_strncmp(string, prefix, SDL_strlen(prefix)) == 0) { 104 return true; 105 } 106 return false; 107} 108 109bool SDL_endswith(const char *string, const char *suffix) 110{ 111 size_t string_length = string ? SDL_strlen(string) : 0; 112 size_t suffix_length = suffix ? SDL_strlen(suffix) : 0; 113 114 if (suffix_length > 0 && suffix_length <= string_length) { 115 if (SDL_memcmp(string + string_length - suffix_length, suffix, suffix_length) == 0) { 116 return true; 117 } 118 } 119 return false; 120} 121 122SDL_COMPILE_TIME_ASSERT(sizeof_object_id, sizeof(int) == sizeof(Uint32)); 123 124Uint32 SDL_GetNextObjectID(void) 125{ 126 static SDL_AtomicInt last_id; 127 128 Uint32 id = (Uint32)SDL_AtomicIncRef(&last_id) + 1; 129 if (id == 0) { 130 id = (Uint32)SDL_AtomicIncRef(&last_id) + 1; 131 } 132 return id; 133} 134 135static SDL_InitState SDL_objects_init; 136static SDL_HashTable *SDL_objects; 137 138static Uint32 SDL_HashObject(const void *key, void *unused) 139{ 140 return (Uint32)(uintptr_t)key; 141} 142 143static bool SDL_KeyMatchObject(const void *a, const void *b, void *unused) 144{ 145 return (a == b); 146} 147 148void SDL_SetObjectValid(void *object, SDL_ObjectType type, bool valid) 149{ 150 SDL_assert(object != NULL); 151 152 if (valid && SDL_ShouldInit(&SDL_objects_init)) { 153 SDL_objects = SDL_CreateHashTable(NULL, 32, SDL_HashObject, SDL_KeyMatchObject, NULL, true, false); 154 if (!SDL_objects) { 155 SDL_SetInitialized(&SDL_objects_init, false); 156 } 157 SDL_SetInitialized(&SDL_objects_init, true); 158 } 159 160 if (valid) { 161 SDL_InsertIntoHashTable(SDL_objects, object, (void *)(uintptr_t)type); 162 } else { 163 SDL_RemoveFromHashTable(SDL_objects, object); 164 } 165} 166 167bool SDL_ObjectValid(void *object, SDL_ObjectType type) 168{ 169 if (!object) { 170 return false; 171 } 172 173 const void *object_type; 174 if (!SDL_FindInHashTable(SDL_objects, object, &object_type)) { 175 return false; 176 } 177 178 return (((SDL_ObjectType)(uintptr_t)object_type) == type); 179} 180 181void SDL_SetObjectsInvalid(void) 182{ 183 if (SDL_ShouldQuit(&SDL_objects_init)) { 184 // Log any leaked objects 185 const void *object, *object_type; 186 void *iter = NULL; 187 while (SDL_IterateHashTable(SDL_objects, &object, &object_type, &iter)) { 188 const char *type; 189 switch ((SDL_ObjectType)(uintptr_t)object_type) { 190 case SDL_OBJECT_TYPE_WINDOW: 191 type = "SDL_Window"; 192 break; 193 case SDL_OBJECT_TYPE_RENDERER: 194 type = "SDL_Renderer"; 195 break; 196 case SDL_OBJECT_TYPE_TEXTURE: 197 type = "SDL_Texture"; 198 break; 199 case SDL_OBJECT_TYPE_JOYSTICK: 200 type = "SDL_Joystick"; 201 break; 202 case SDL_OBJECT_TYPE_GAMEPAD: 203 type = "SDL_Gamepad"; 204 break; 205 case SDL_OBJECT_TYPE_HAPTIC: 206 type = "SDL_Haptic"; 207 break; 208 case SDL_OBJECT_TYPE_SENSOR: 209 type = "SDL_Sensor"; 210 break; 211 case SDL_OBJECT_TYPE_HIDAPI_DEVICE: 212 type = "hidapi device"; 213 break; 214 case SDL_OBJECT_TYPE_HIDAPI_JOYSTICK: 215 type = "hidapi joystick"; 216 break; 217 case SDL_OBJECT_TYPE_THREAD: 218 type = "thread"; 219 break; 220 default: 221 type = "unknown object"; 222 break; 223 } 224 SDL_Log("Leaked %s (%p)\n", type, object); 225 } 226 SDL_assert(SDL_HashTableEmpty(SDL_objects)); 227 228 SDL_DestroyHashTable(SDL_objects); 229 SDL_objects = NULL; 230 231 SDL_SetInitialized(&SDL_objects_init, false); 232 } 233} 234 235static int SDL_URIDecode(const char *src, char *dst, int len) 236{ 237 int ri, wi, di; 238 char decode = '\0'; 239 if (!src || !dst || len < 0) { 240 return -1; 241 } 242 if (len == 0) { 243 len = (int)SDL_strlen(src); 244 } 245 for (ri = 0, wi = 0, di = 0; ri < len && wi < len; ri += 1) { 246 if (di == 0) { 247 // start decoding 248 if (src[ri] == '%') { 249 decode = '\0'; 250 di += 1; 251 continue; 252 } 253 // normal write 254 dst[wi] = src[ri]; 255 wi += 1; 256 } else if (di == 1 || di == 2) { 257 char off = '\0'; 258 char isa = src[ri] >= 'a' && src[ri] <= 'f'; 259 char isA = src[ri] >= 'A' && src[ri] <= 'F'; 260 char isn = src[ri] >= '0' && src[ri] <= '9'; 261 if (!(isa || isA || isn)) { 262 // not a hexadecimal 263 int sri; 264 for (sri = ri - di; sri <= ri; sri += 1) { 265 dst[wi] = src[sri]; 266 wi += 1; 267 } 268 di = 0; 269 continue; 270 } 271 // itsy bitsy magicsy 272 if (isn) { 273 off = 0 - '0'; 274 } else if (isa) { 275 off = 10 - 'a'; 276 } else if (isA) { 277 off = 10 - 'A'; 278 } 279 decode |= (src[ri] + off) << (2 - di) * 4; 280 if (di == 2) { 281 dst[wi] = decode; 282 wi += 1; 283 di = 0; 284 } else { 285 di += 1; 286 } 287 } 288 } 289 dst[wi] = '\0'; 290 return wi; 291} 292 293int SDL_URIToLocal(const char *src, char *dst) 294{ 295 if (SDL_memcmp(src, "file:/", 6) == 0) { 296 src += 6; // local file? 297 } else if (SDL_strstr(src, ":/") != NULL) { 298 return -1; // wrong scheme 299 } 300 301 bool local = src[0] != '/' || (src[0] != '\0' && src[1] == '/'); 302 303 // Check the hostname, if present. RFC 3986 states that the hostname component of a URI is not case-sensitive. 304 if (!local && src[0] == '/' && src[2] != '/') { 305 char *hostname_end = SDL_strchr(src + 1, '/'); 306 if (hostname_end) { 307 const size_t src_len = hostname_end - (src + 1); 308 size_t hostname_len; 309 310#if defined(HAVE_GETHOSTNAME) && !defined(SDL_PLATFORM_WINDOWS) 311 char hostname[257]; 312 if (gethostname(hostname, 255) == 0) { 313 hostname[256] = '\0'; 314 hostname_len = SDL_strlen(hostname); 315 if (hostname_len == src_len && SDL_strncasecmp(src + 1, hostname, src_len) == 0) { 316 src = hostname_end + 1; 317 local = true; 318 } 319 } 320#endif 321 322 if (!local) { 323 static const char *localhost = "localhost"; 324 hostname_len = SDL_strlen(localhost); 325 if (hostname_len == src_len && SDL_strncasecmp(src + 1, localhost, src_len) == 0) { 326 src = hostname_end + 1; 327 local = true; 328 } 329 } 330 } 331 } 332 333 if (local) { 334 // Convert URI escape sequences to real characters 335 if (src[0] == '/') { 336 src++; 337 } else { 338 src--; 339 } 340 return SDL_URIDecode(src, dst, 0); 341 } 342 return -1; 343} 344 345// This is a set of per-thread persistent strings that we can return from the SDL API. 346// This is used for short strings that might persist past the lifetime of the object 347// they are related to. 348 349static SDL_TLSID SDL_string_storage; 350 351static void SDL_FreePersistentStrings( void *value ) 352{ 353 SDL_HashTable *strings = (SDL_HashTable *)value; 354 SDL_DestroyHashTable(strings); 355} 356 357const char *SDL_GetPersistentString(const char *string) 358{ 359 if (!string) { 360 return NULL; 361 } 362 if (!*string) { 363 return ""; 364 } 365 366 SDL_HashTable *strings = (SDL_HashTable *)SDL_GetTLS(&SDL_string_storage); 367 if (!strings) { 368 strings = SDL_CreateHashTable(NULL, 32, SDL_HashString, SDL_KeyMatchString, SDL_NukeFreeValue, false, false); 369 if (!strings) { 370 return NULL; 371 } 372 373 SDL_SetTLS(&SDL_string_storage, strings, SDL_FreePersistentStrings); 374 } 375 376 const char *result; 377 if (!SDL_FindInHashTable(strings, string, (const void **)&result)) { 378 char *new_string = SDL_strdup(string); 379 if (!new_string) { 380 return NULL; 381 } 382 383 // If the hash table insert fails, at least we can return the string we allocated 384 SDL_InsertIntoHashTable(strings, new_string, new_string); 385 result = new_string; 386 } 387 return result; 388}