/** * @ingroup DBusMemoryInternals * Unit test for DBusMemory * @returns #TRUE on success. */ dbus_bool_t _dbus_memory_test (void) { dbus_bool_t old_guards; void *p; size_t size; old_guards = guards; guards = TRUE; p = dbus_malloc (4); if (p == NULL) _dbus_assert_not_reached ("no memory"); for (size = 4; size < 256; size += 4) { p = dbus_realloc (p, size); if (p == NULL) _dbus_assert_not_reached ("no memory"); } for (size = 256; size != 0; size -= 4) { p = dbus_realloc (p, size); if (p == NULL) _dbus_assert_not_reached ("no memory"); } dbus_free (p); guards = old_guards; return TRUE; }
dbus_bool_t _dbus_directory_get_next_file (DBusDirIter *iter, DBusString *filename, DBusError *error) { struct dirent *d, *ent; size_t buf_size; int err; _DBUS_ASSERT_ERROR_IS_CLEAR (error); if (!dirent_buf_size (iter->d, &buf_size)) { dbus_set_error (error, DBUS_ERROR_FAILED, "Can't calculate buffer size when reading directory"); return FALSE; } d = (struct dirent *)dbus_malloc (buf_size); if (!d) { dbus_set_error (error, DBUS_ERROR_NO_MEMORY, "No memory to read directory entry"); return FALSE; } again: err = readdir_r (iter->d, d, &ent); if (err || !ent) { if (err != 0) dbus_set_error (error, _dbus_error_from_errno (err), "%s", _dbus_strerror (err)); dbus_free (d); return FALSE; } else if (ent->d_name[0] == '.' && (ent->d_name[1] == '\0' || (ent->d_name[1] == '.' && ent->d_name[2] == '\0'))) goto again; else { _dbus_string_set_length (filename, 0); if (!_dbus_string_append (filename, ent->d_name)) { dbus_set_error (error, DBUS_ERROR_NO_MEMORY, "No memory to read directory entry"); dbus_free (d); return FALSE; } else { dbus_free (d); return TRUE; } } }
static dbus_bool_t add_timeout(DBusTimeout *timeout, void *data){ /* add expiration data to timeout */ struct timeval *expires = dbus_malloc(sizeof(struct timeval)); if (!expires) return FALSE; dbus_timeout_set_data(timeout, expires, dbus_free); asdbus_set_dbus_timer (expires, timeout); return TRUE; }
/** * Duplicates a block of memory. Returns * #NULL on failure. * * @param mem memory to copy * @param n_bytes number of bytes to copy * @returns the copy */ void* _dbus_memdup (const void *mem, size_t n_bytes) { void *copy; copy = dbus_malloc (n_bytes); if (copy == NULL) return NULL; memcpy (copy, mem, n_bytes); return copy; }
/** * Duplicates a string. Result must be freed with * dbus_free(). Returns #NULL if memory allocation fails. * If the string to be duplicated is #NULL, returns #NULL. * * @param str string to duplicate. * @returns newly-allocated copy. */ char* _dbus_strdup (const char *str) { size_t len; char *copy; if (str == NULL) return NULL; len = strlen (str); copy = dbus_malloc (len + 1); if (copy == NULL) return NULL; memcpy (copy, str, len + 1); return copy; }
/* protect_argv lifted from GLib, relicensed by author, Tor Lillqvist */ static int protect_argv (char **argv, char ***new_argv) { int i; int argc = 0; while (argv[argc]) ++argc; *new_argv = dbus_malloc ((argc + 1) * sizeof (char *)); if (*new_argv == NULL) return -1; for (i = 0; i < argc; i++) (*new_argv)[i] = NULL; /* Quote each argv element if necessary, so that it will get * reconstructed correctly in the C runtime startup code. Note that * the unquoting algorithm in the C runtime is really weird, and * rather different than what Unix shells do. See stdargv.c in the C * runtime sources (in the Platform SDK, in src/crt). * * Note that an new_argv[0] constructed by this function should * *not* be passed as the filename argument to a spawn* or exec* * family function. That argument should be the real file name * without any quoting. */ for (i = 0; i < argc; i++) { char *p = argv[i]; char *q; int len = 0; int need_dblquotes = FALSE; while (*p) { if (*p == ' ' || *p == '\t') need_dblquotes = TRUE; else if (*p == '"') len++; else if (*p == '\\') { char *pp = p; while (*pp && *pp == '\\') pp++; if (*pp == '"') len++; } len++; p++; } q = (*new_argv)[i] = dbus_malloc (len + need_dblquotes*2 + 1); if (q == NULL) return -1; p = argv[i]; if (need_dblquotes) *q++ = '"'; while (*p) { if (*p == '"') *q++ = '\\'; else if (*p == '\\') { char *pp = p; while (*pp && *pp == '\\') pp++; if (*pp == '"') *q++ = '\\'; } *q++ = *p; p++; } if (need_dblquotes) *q++ = '"'; *q++ = '\0'; /* printf ("argv[%d]:%s, need_dblquotes:%s len:%d => %s\n", i, argv[i], need_dblquotes?"TRUE":"FALSE", len, (*new_argv)[i]); */ } (*new_argv)[argc] = NULL; return argc; }
static dbus_bool_t fill_group_info (DBusGroupInfo *info, dbus_gid_t gid, const DBusString *groupname, DBusError *error) { const char *group_c_str; _dbus_assert (groupname != NULL || gid != DBUS_GID_UNSET); _dbus_assert (groupname == NULL || gid == DBUS_GID_UNSET); if (groupname) group_c_str = _dbus_string_get_const_data (groupname); else group_c_str = NULL; /* For now assuming that the getgrnam() and getgrgid() flavors * always correspond to the pwnam flavors, if not we have * to add more configure checks. */ #if defined (HAVE_POSIX_GETPWNAM_R) || defined (HAVE_NONPOSIX_GETPWNAM_R) { struct group *g; int result; size_t buflen; char *buf; struct group g_str; dbus_bool_t b; /* retrieve maximum needed size for buf */ buflen = sysconf (_SC_GETGR_R_SIZE_MAX); /* sysconf actually returns a long, but everything else expects size_t, * so just recast here. * https://bugs.freedesktop.org/show_bug.cgi?id=17061 */ if ((long) buflen <= 0) buflen = 1024; result = -1; while (1) { buf = dbus_malloc (buflen); if (buf == NULL) { dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); return FALSE; } g = NULL; #ifdef HAVE_POSIX_GETPWNAM_R if (group_c_str) result = getgrnam_r (group_c_str, &g_str, buf, buflen, &g); else result = getgrgid_r (gid, &g_str, buf, buflen, &g); #else g = getgrnam_r (group_c_str, &g_str, buf, buflen); result = 0; #endif /* !HAVE_POSIX_GETPWNAM_R */ /* Try a bigger buffer if ERANGE was returned: https://bugs.freedesktop.org/show_bug.cgi?id=16727 */ if (result == ERANGE && buflen < 512 * 1024) { dbus_free (buf); buflen *= 2; } else { break; } } if (result == 0 && g == &g_str) { b = fill_user_info_from_group (g, info, error); dbus_free (buf); return b; } else { dbus_set_error (error, _dbus_error_from_errno (errno), "Group %s unknown or failed to look it up\n", group_c_str ? group_c_str : "???"); dbus_free (buf); return FALSE; } } #else /* ! HAVE_GETPWNAM_R */ { /* I guess we're screwed on thread safety here */ struct group *g; g = getgrnam (group_c_str); if (g != NULL) { return fill_user_info_from_group (g, info, error); } else { dbus_set_error (error, _dbus_error_from_errno (errno), "Group %s unknown or failed to look it up\n", group_c_str ? group_c_str : "???"); return FALSE; } } #endif /* ! HAVE_GETPWNAM_R */ }
static char * unescape_string (BusDesktopFileParser *parser, const DBusString *str, int pos, int end_pos, DBusError *error) { char *retval, *q; _DBUS_ASSERT_ERROR_IS_CLEAR (error); /* len + 1 is enough, because unescaping never makes the * string longer */ retval = dbus_malloc (end_pos - pos + 1); if (retval == NULL) { BUS_SET_OOM (error); return NULL; } q = retval; while (pos < end_pos) { if (_dbus_string_get_byte (str, pos) == 0) { /* Found an embedded null */ dbus_free (retval); report_error (parser, "Text to be unescaped contains embedded nul", BUS_DESKTOP_PARSE_ERROR_INVALID_ESCAPES, error); return NULL; } if (_dbus_string_get_byte (str, pos) == '\\') { pos ++; if (pos >= end_pos) { /* Escape at end of string */ dbus_free (retval); report_error (parser, "Text to be unescaped ended in \\", BUS_DESKTOP_PARSE_ERROR_INVALID_ESCAPES, error); return NULL; } switch (_dbus_string_get_byte (str, pos)) { case 's': *q++ = ' '; break; case 't': *q++ = '\t'; break; case 'n': *q++ = '\n'; break; case 'r': *q++ = '\r'; break; case '\\': *q++ = '\\'; break; default: /* Invalid escape code */ dbus_free (retval); report_error (parser, "Text to be unescaped had invalid escape sequence", BUS_DESKTOP_PARSE_ERROR_INVALID_ESCAPES, error); return NULL; } pos++; } else { *q++ =_dbus_string_get_byte (str, pos); pos++; } } *q = 0; return retval; }
/* Calls strtod in a locale-independent fashion, by looking at * the locale data and patching the decimal comma to a point. * * Relicensed from glib. */ static double ascii_strtod (const char *nptr, char **endptr) { /* FIXME: The Win32 C library's strtod() doesn't handle hex. * Presumably many Unixes don't either. */ char *fail_pos; double val; struct lconv *locale_data; const char *decimal_point; int decimal_point_len; const char *p, *decimal_point_pos; const char *end = NULL; /* Silence gcc */ fail_pos = NULL; locale_data = localeconv (); decimal_point = locale_data->decimal_point; decimal_point_len = strlen (decimal_point); _dbus_assert (decimal_point_len != 0); decimal_point_pos = NULL; if (decimal_point[0] != '.' || decimal_point[1] != 0) { p = nptr; /* Skip leading space */ while (ascii_isspace (*p)) p++; /* Skip leading optional sign */ if (*p == '+' || *p == '-') p++; if (p[0] == '0' && (p[1] == 'x' || p[1] == 'X')) { p += 2; /* HEX - find the (optional) decimal point */ while (ascii_isxdigit (*p)) p++; if (*p == '.') { decimal_point_pos = p++; while (ascii_isxdigit (*p)) p++; if (*p == 'p' || *p == 'P') p++; if (*p == '+' || *p == '-') p++; while (ascii_isdigit (*p)) p++; end = p; } } else { while (ascii_isdigit (*p)) p++; if (*p == '.') { decimal_point_pos = p++; while (ascii_isdigit (*p)) p++; if (*p == 'e' || *p == 'E') p++; if (*p == '+' || *p == '-') p++; while (ascii_isdigit (*p)) p++; end = p; } } /* For the other cases, we need not convert the decimal point */ } /* Set errno to zero, so that we can distinguish zero results and underflows */ errno = 0; if (decimal_point_pos) { char *copy, *c; /* We need to convert the '.' to the locale specific decimal point */ copy = dbus_malloc (end - nptr + 1 + decimal_point_len); c = copy; memcpy (c, nptr, decimal_point_pos - nptr); c += decimal_point_pos - nptr; memcpy (c, decimal_point, decimal_point_len); c += decimal_point_len; memcpy (c, decimal_point_pos + 1, end - (decimal_point_pos + 1)); c += end - (decimal_point_pos + 1); *c = 0; val = strtod (copy, &fail_pos); if (fail_pos) { if (fail_pos > decimal_point_pos) fail_pos = (char *)nptr + (fail_pos - copy) - (decimal_point_len - 1); else fail_pos = (char *)nptr + (fail_pos - copy); } dbus_free (copy); } else val = strtod (nptr, &fail_pos); if (endptr) *endptr = fail_pos; return val; }
static void time_for_size (int size) { int i; int j; clock_t start; clock_t end; #define FREE_ARRAY_SIZE 512 #define N_ITERATIONS FREE_ARRAY_SIZE * 512 void *to_free[FREE_ARRAY_SIZE]; DBusMemPool *pool; _dbus_verbose ("Timings for size %d\n", size); _dbus_verbose (" malloc\n"); start = clock (); i = 0; j = 0; while (i < N_ITERATIONS) { to_free[j] = dbus_malloc (size); _dbus_assert (to_free[j] != NULL); /* in a real app of course this is wrong */ ++j; if (j == FREE_ARRAY_SIZE) { j = 0; while (j < FREE_ARRAY_SIZE) { dbus_free (to_free[j]); ++j; } j = 0; } ++i; } end = clock (); _dbus_verbose (" created/destroyed %d elements in %g seconds\n", N_ITERATIONS, (end - start) / (double) CLOCKS_PER_SEC); _dbus_verbose (" mempools\n"); start = clock (); pool = _dbus_mem_pool_new (size, FALSE); i = 0; j = 0; while (i < N_ITERATIONS) { to_free[j] = _dbus_mem_pool_alloc (pool); _dbus_assert (to_free[j] != NULL); /* in a real app of course this is wrong */ ++j; if (j == FREE_ARRAY_SIZE) { j = 0; while (j < FREE_ARRAY_SIZE) { _dbus_mem_pool_dealloc (pool, to_free[j]); ++j; } j = 0; } ++i; } _dbus_mem_pool_free (pool); end = clock (); _dbus_verbose (" created/destroyed %d elements in %g seconds\n", N_ITERATIONS, (end - start) / (double) CLOCKS_PER_SEC); _dbus_verbose (" zeroed malloc\n"); start = clock (); i = 0; j = 0; while (i < N_ITERATIONS) { to_free[j] = dbus_malloc0 (size); _dbus_assert (to_free[j] != NULL); /* in a real app of course this is wrong */ ++j; if (j == FREE_ARRAY_SIZE) { j = 0; while (j < FREE_ARRAY_SIZE) { dbus_free (to_free[j]); ++j; } j = 0; } ++i; } end = clock (); _dbus_verbose (" created/destroyed %d elements in %g seconds\n", N_ITERATIONS, (end - start) / (double) CLOCKS_PER_SEC); _dbus_verbose (" zeroed mempools\n"); start = clock (); pool = _dbus_mem_pool_new (size, TRUE); i = 0; j = 0; while (i < N_ITERATIONS) { to_free[j] = _dbus_mem_pool_alloc (pool); _dbus_assert (to_free[j] != NULL); /* in a real app of course this is wrong */ ++j; if (j == FREE_ARRAY_SIZE) { j = 0; while (j < FREE_ARRAY_SIZE) { _dbus_mem_pool_dealloc (pool, to_free[j]); ++j; } j = 0; } ++i; } _dbus_mem_pool_free (pool); end = clock (); _dbus_verbose (" created/destroyed %d elements in %g seconds\n", N_ITERATIONS, (end - start) / (double) CLOCKS_PER_SEC); }
/** * Allocates an object from the memory pool. * The object must be freed with _dbus_mem_pool_dealloc(). * * @param pool the memory pool * @returns the allocated object or #NULL if no memory. */ void* _dbus_mem_pool_alloc (DBusMemPool *pool) { #ifdef DBUS_BUILD_TESTS if (_dbus_disable_mem_pools ()) { DBusMemBlock *block; int alloc_size; /* This is obviously really silly, but it's * debug-mode-only code that is compiled out * when tests are disabled (_dbus_disable_mem_pools() * is a constant expression FALSE so this block * should vanish) */ alloc_size = sizeof (DBusMemBlock) - ELEMENT_PADDING + pool->element_size; if (pool->zero_elements) block = dbus_malloc0 (alloc_size); else block = dbus_malloc (alloc_size); if (block != NULL) { block->next = pool->blocks; pool->blocks = block; pool->allocated_elements += 1; return (void*) &block->elements[0]; } else return NULL; } else #endif { if (_dbus_decrement_fail_alloc_counter ()) { _dbus_verbose (" FAILING mempool alloc\n"); return NULL; } else if (pool->free_elements) { DBusFreedElement *element = pool->free_elements; pool->free_elements = pool->free_elements->next; if (pool->zero_elements) memset (element, '\0', pool->element_size); pool->allocated_elements += 1; return element; } else { void *element; if (pool->blocks == NULL || pool->blocks->used_so_far == pool->block_size) { /* Need a new block */ DBusMemBlock *block; int alloc_size; #ifdef DBUS_BUILD_TESTS int saved_counter; #endif if (pool->block_size <= _DBUS_INT_MAX / 4) /* avoid overflow */ { /* use a larger block size for our next block */ pool->block_size *= 2; _dbus_assert ((pool->block_size % pool->element_size) == 0); } alloc_size = sizeof (DBusMemBlock) - ELEMENT_PADDING + pool->block_size; #ifdef DBUS_BUILD_TESTS /* We save/restore the counter, so that memory pools won't * cause a given function to have different number of * allocations on different invocations. i.e. when testing * we want consistent alloc patterns. So we skip our * malloc here for purposes of failed alloc simulation. */ saved_counter = _dbus_get_fail_alloc_counter (); _dbus_set_fail_alloc_counter (_DBUS_INT_MAX); #endif if (pool->zero_elements) block = dbus_malloc0 (alloc_size); else block = dbus_malloc (alloc_size); #ifdef DBUS_BUILD_TESTS _dbus_set_fail_alloc_counter (saved_counter); _dbus_assert (saved_counter == _dbus_get_fail_alloc_counter ()); #endif if (block == NULL) return NULL; block->used_so_far = 0; block->next = pool->blocks; pool->blocks = block; } element = &pool->blocks->elements[pool->blocks->used_so_far]; pool->blocks->used_so_far += pool->element_size; pool->allocated_elements += 1; return element; } } }