summaryrefslogtreecommitdiffstats
path: root/vm/Misc.cpp
diff options
context:
space:
mode:
authorBrian Carlstrom <bdc@google.com>2014-08-05 12:46:17 -0700
committerBrian Carlstrom <bdc@google.com>2014-08-05 12:51:13 -0700
commit870b4f2d70d67d6dbb7d0881d101c61bed8caad2 (patch)
tree7487dad3970556a040f88a49852a3dc9ed19bebd /vm/Misc.cpp
parent76e15e367ae1189b6f641ba8d16ca92bd179dac0 (diff)
downloadandroid_dalvik-870b4f2d70d67d6dbb7d0881d101c61bed8caad2.tar.gz
android_dalvik-870b4f2d70d67d6dbb7d0881d101c61bed8caad2.tar.bz2
android_dalvik-870b4f2d70d67d6dbb7d0881d101c61bed8caad2.zip
Dalvik is dead, long live Dalvik!
croot cd dalvik repo start dalvik-is-dead-long-live-dalvik . repo sync -c . git rm -r README.txt git rm -r dexopt git rm -r tools/deadcode.py git rm -r tools/dex-preopt git rm -r tools/dexcheck git rm -r tools/gdbjithelper git rm -r unit-tests git rm -r vm git checkout HEAD vm/Common.h (needed by libdex) git checkout HEAD vm/DalvikVersion.h (needed by libdex) git checkout HEAD vm/Profile.h (needed by dmtracedump) git add Android.mk (after removing vm, dexopt, and unit-tests references) git commit -a -m 'Dalvik is dead, long live Dalvik!' Bug: 14298175 Change-Id: I9dd13053677629d13496d4238af4374452cda415
Diffstat (limited to 'vm/Misc.cpp')
-rw-r--r--vm/Misc.cpp832
1 files changed, 0 insertions, 832 deletions
diff --git a/vm/Misc.cpp b/vm/Misc.cpp
deleted file mode 100644
index 057b90d6d..000000000
--- a/vm/Misc.cpp
+++ /dev/null
@@ -1,832 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-/*
- * Miscellaneous utility functions.
- */
-#include "Dalvik.h"
-
-#include <stdlib.h>
-#include <stddef.h>
-#include <string.h>
-#include <strings.h>
-#include <ctype.h>
-#include <time.h>
-#include <sys/time.h>
-#include <fcntl.h>
-#include <cutils/ashmem.h>
-#include <sys/mman.h>
-
-/*
- * Print a hex dump in this format:
- *
-01234567: 00 11 22 33 44 55 66 77 88 99 aa bb cc dd ee ff 0123456789abcdef\n
- *
- * If "mode" is kHexDumpLocal, we start at offset zero, and show a full
- * 16 bytes on the first line. If it's kHexDumpMem, we make this look
- * like a memory dump, using the actual address, outputting a partial line
- * if "vaddr" isn't aligned on a 16-byte boundary.
- *
- * "priority" and "tag" determine the values passed to the log calls.
- *
- * Does not use printf() or other string-formatting calls.
- */
-void dvmPrintHexDumpEx(int priority, const char* tag, const void* vaddr,
- size_t length, HexDumpMode mode)
-{
- static const char gHexDigit[] = "0123456789abcdef";
- const unsigned char* addr = (const unsigned char*)vaddr;
- char out[77]; /* exact fit */
- unsigned int offset; /* offset to show while printing */
- char* hex;
- char* asc;
- int gap;
- //int trickle = 0;
-
- if (mode == kHexDumpLocal)
- offset = 0;
- else
- offset = (int) addr;
-
- memset(out, ' ', sizeof(out)-1);
- out[8] = ':';
- out[sizeof(out)-2] = '\n';
- out[sizeof(out)-1] = '\0';
-
- gap = (int) offset & 0x0f;
- while (length) {
- unsigned int lineOffset = offset & ~0x0f;
- int i, count;
-
- hex = out;
- asc = out + 59;
-
- for (i = 0; i < 8; i++) {
- *hex++ = gHexDigit[lineOffset >> 28];
- lineOffset <<= 4;
- }
- hex++;
- hex++;
-
- count = ((int)length > 16-gap) ? 16-gap : (int)length; /* cap length */
- assert(count != 0);
- assert(count+gap <= 16);
-
- if (gap) {
- /* only on first line */
- hex += gap * 3;
- asc += gap;
- }
-
- for (i = gap ; i < count+gap; i++) {
- *hex++ = gHexDigit[*addr >> 4];
- *hex++ = gHexDigit[*addr & 0x0f];
- hex++;
- if (*addr >= 0x20 && *addr < 0x7f /*isprint(*addr)*/)
- *asc++ = *addr;
- else
- *asc++ = '.';
- addr++;
- }
- for ( ; i < 16; i++) {
- /* erase extra stuff; only happens on last line */
- *hex++ = ' ';
- *hex++ = ' ';
- hex++;
- *asc++ = ' ';
- }
-
- LOG_PRI(priority, tag, "%s", out);
-#if 0 //def HAVE_ANDROID_OS
- /*
- * We can overrun logcat easily by writing at full speed. On the
- * other hand, we can make Eclipse time out if we're showing
- * packet dumps while debugging JDWP.
- */
- {
- if (trickle++ == 8) {
- trickle = 0;
- usleep(20000);
- }
- }
-#endif
-
- gap = 0;
- length -= count;
- offset += count;
- }
-}
-
-
-/*
- * Fill out a DebugOutputTarget, suitable for printing to the log.
- */
-void dvmCreateLogOutputTarget(DebugOutputTarget* target, int priority,
- const char* tag)
-{
- assert(target != NULL);
- assert(tag != NULL);
-
- target->which = kDebugTargetLog;
- target->data.log.priority = priority;
- target->data.log.tag = tag;
-}
-
-/*
- * Fill out a DebugOutputTarget suitable for printing to a file pointer.
- */
-void dvmCreateFileOutputTarget(DebugOutputTarget* target, FILE* fp)
-{
- assert(target != NULL);
- assert(fp != NULL);
-
- target->which = kDebugTargetFile;
- target->data.file.fp = fp;
-}
-
-/*
- * Free "target" and any associated data.
- */
-void dvmFreeOutputTarget(DebugOutputTarget* target)
-{
- free(target);
-}
-
-/*
- * Print a debug message, to either a file or the log.
- */
-void dvmPrintDebugMessage(const DebugOutputTarget* target, const char* format,
- ...)
-{
- va_list args;
-
- va_start(args, format);
-
- switch (target->which) {
- case kDebugTargetLog:
- LOG_PRI_VA(target->data.log.priority, target->data.log.tag,
- format, args);
- break;
- case kDebugTargetFile:
- vfprintf(target->data.file.fp, format, args);
- break;
- default:
- ALOGE("unexpected 'which' %d", target->which);
- break;
- }
-
- va_end(args);
-}
-
-
-/*
- * Return a newly-allocated string in which all occurrences of '.' have
- * been changed to '/'. If we find a '/' in the original string, NULL
- * is returned to avoid ambiguity.
- */
-char* dvmDotToSlash(const char* str)
-{
- char* newStr = strdup(str);
- char* cp = newStr;
-
- if (newStr == NULL)
- return NULL;
-
- while (*cp != '\0') {
- if (*cp == '/') {
- assert(false);
- return NULL;
- }
- if (*cp == '.')
- *cp = '/';
- cp++;
- }
-
- return newStr;
-}
-
-std::string dvmHumanReadableDescriptor(const char* descriptor) {
- // Count the number of '['s to get the dimensionality.
- const char* c = descriptor;
- size_t dim = 0;
- while (*c == '[') {
- dim++;
- c++;
- }
-
- // Reference or primitive?
- if (*c == 'L') {
- // "[[La/b/C;" -> "a.b.C[][]".
- c++; // Skip the 'L'.
- } else {
- // "[[B" -> "byte[][]".
- // To make life easier, we make primitives look like unqualified
- // reference types.
- switch (*c) {
- case 'B': c = "byte;"; break;
- case 'C': c = "char;"; break;
- case 'D': c = "double;"; break;
- case 'F': c = "float;"; break;
- case 'I': c = "int;"; break;
- case 'J': c = "long;"; break;
- case 'S': c = "short;"; break;
- case 'Z': c = "boolean;"; break;
- default: return descriptor;
- }
- }
-
- // At this point, 'c' is a string of the form "fully/qualified/Type;"
- // or "primitive;". Rewrite the type with '.' instead of '/':
- std::string result;
- const char* p = c;
- while (*p != ';') {
- char ch = *p++;
- if (ch == '/') {
- ch = '.';
- }
- result.push_back(ch);
- }
- // ...and replace the semicolon with 'dim' "[]" pairs:
- while (dim--) {
- result += "[]";
- }
- return result;
-}
-
-std::string dvmHumanReadableType(const Object* obj)
-{
- if (obj == NULL) {
- return "null";
- }
- if (obj->clazz == NULL) {
- /* should only be possible right after a plain dvmMalloc() */
- return "(raw)";
- }
- std::string result(dvmHumanReadableDescriptor(obj->clazz->descriptor));
- if (dvmIsClassObject(obj)) {
- const ClassObject* clazz = reinterpret_cast<const ClassObject*>(obj);
- result += "<" + dvmHumanReadableDescriptor(clazz->descriptor) + ">";
- }
- return result;
-}
-
-std::string dvmHumanReadableField(const Field* field)
-{
- if (field == NULL) {
- return "(null)";
- }
- std::string result(dvmHumanReadableDescriptor(field->clazz->descriptor));
- result += '.';
- result += field->name;
- return result;
-}
-
-std::string dvmHumanReadableMethod(const Method* method, bool withSignature)
-{
- if (method == NULL) {
- return "(null)";
- }
- std::string result(dvmHumanReadableDescriptor(method->clazz->descriptor));
- result += '.';
- result += method->name;
- if (withSignature) {
- // TODO: the types in this aren't human readable!
- char* signature = dexProtoCopyMethodDescriptor(&method->prototype);
- result += signature;
- free(signature);
- }
- return result;
-}
-
-/*
- * Return a newly-allocated string for the "dot version" of the class
- * name for the given type descriptor. That is, The initial "L" and
- * final ";" (if any) have been removed and all occurrences of '/'
- * have been changed to '.'.
- *
- * "Dot version" names are used in the class loading machinery.
- * See also dvmHumanReadableDescriptor.
- */
-char* dvmDescriptorToDot(const char* str)
-{
- size_t at = strlen(str);
- char* newStr;
-
- if ((at >= 2) && (str[0] == 'L') && (str[at - 1] == ';')) {
- at -= 2; /* Two fewer chars to copy. */
- str++; /* Skip the 'L'. */
- }
-
- newStr = (char*)malloc(at + 1); /* Add one for the '\0'. */
- if (newStr == NULL)
- return NULL;
-
- newStr[at] = '\0';
-
- while (at > 0) {
- at--;
- newStr[at] = (str[at] == '/') ? '.' : str[at];
- }
-
- return newStr;
-}
-
-/*
- * Return a newly-allocated string for the type descriptor
- * corresponding to the "dot version" of the given class name. That
- * is, non-array names are surrounded by "L" and ";", and all
- * occurrences of '.' have been changed to '/'.
- *
- * "Dot version" names are used in the class loading machinery.
- */
-char* dvmDotToDescriptor(const char* str)
-{
- size_t length = strlen(str);
- int wrapElSemi = 0;
- char* newStr;
- char* at;
-
- if (str[0] != '[') {
- length += 2; /* for "L" and ";" */
- wrapElSemi = 1;
- }
-
- newStr = at = (char*)malloc(length + 1); /* + 1 for the '\0' */
-
- if (newStr == NULL) {
- return NULL;
- }
-
- if (wrapElSemi) {
- *(at++) = 'L';
- }
-
- while (*str) {
- char c = *(str++);
- if (c == '.') {
- c = '/';
- }
- *(at++) = c;
- }
-
- if (wrapElSemi) {
- *(at++) = ';';
- }
-
- *at = '\0';
- return newStr;
-}
-
-/*
- * Return a newly-allocated string for the internal-form class name for
- * the given type descriptor. That is, the initial "L" and final ";" (if
- * any) have been removed.
- */
-char* dvmDescriptorToName(const char* str)
-{
- if (str[0] == 'L') {
- size_t length = strlen(str) - 1;
- char* newStr = (char*)malloc(length);
-
- if (newStr == NULL) {
- return NULL;
- }
-
- strlcpy(newStr, str + 1, length);
- return newStr;
- }
-
- return strdup(str);
-}
-
-/*
- * Return a newly-allocated string for the type descriptor for the given
- * internal-form class name. That is, a non-array class name will get
- * surrounded by "L" and ";", while array names are left as-is.
- */
-char* dvmNameToDescriptor(const char* str)
-{
- if (str[0] != '[') {
- size_t length = strlen(str);
- char* descriptor = (char*)malloc(length + 3);
-
- if (descriptor == NULL) {
- return NULL;
- }
-
- descriptor[0] = 'L';
- strcpy(descriptor + 1, str);
- descriptor[length + 1] = ';';
- descriptor[length + 2] = '\0';
-
- return descriptor;
- }
-
- return strdup(str);
-}
-
-/*
- * Get a notion of the current time, in nanoseconds. This is meant for
- * computing durations (e.g. "operation X took 52nsec"), so the result
- * should not be used to get the current date/time.
- */
-u8 dvmGetRelativeTimeNsec()
-{
-#ifdef HAVE_POSIX_CLOCKS
- struct timespec now;
- clock_gettime(CLOCK_MONOTONIC, &now);
- return (u8)now.tv_sec*1000000000LL + now.tv_nsec;
-#else
- struct timeval now;
- gettimeofday(&now, NULL);
- return (u8)now.tv_sec*1000000000LL + now.tv_usec * 1000LL;
-#endif
-}
-
-/*
- * Get the per-thread CPU time, in nanoseconds.
- *
- * Only useful for time deltas.
- */
-u8 dvmGetThreadCpuTimeNsec()
-{
-#ifdef HAVE_POSIX_CLOCKS
- struct timespec now;
- clock_gettime(CLOCK_THREAD_CPUTIME_ID, &now);
- return (u8)now.tv_sec*1000000000LL + now.tv_nsec;
-#else
- return (u8) -1;
-#endif
-}
-
-/*
- * Get the per-thread CPU time, in nanoseconds, for the specified thread.
- */
-u8 dvmGetOtherThreadCpuTimeNsec(pthread_t thread)
-{
-#if 0 /*def HAVE_POSIX_CLOCKS*/
- int clockId;
-
- if (pthread_getcpuclockid(thread, &clockId) != 0)
- return (u8) -1;
-
- struct timespec now;
- clock_gettime(clockId, &now);
- return (u8)now.tv_sec*1000000000LL + now.tv_nsec;
-#else
- return (u8) -1;
-#endif
-}
-
-
-/*
- * Call this repeatedly, with successively higher values for "iteration",
- * to sleep for a period of time not to exceed "maxTotalSleep".
- *
- * For example, when called with iteration==0 we will sleep for a very
- * brief time. On the next call we will sleep for a longer time. When
- * the sum total of all sleeps reaches "maxTotalSleep", this returns false.
- *
- * The initial start time value for "relStartTime" MUST come from the
- * dvmGetRelativeTimeUsec call. On the device this must come from the
- * monotonic clock source, not the wall clock.
- *
- * This should be used wherever you might be tempted to call sched_yield()
- * in a loop. The problem with sched_yield is that, for a high-priority
- * thread, the kernel might not actually transfer control elsewhere.
- *
- * Returns "false" if we were unable to sleep because our time was up.
- */
-bool dvmIterativeSleep(int iteration, int maxTotalSleep, u8 relStartTime)
-{
- /*
- * Minimum sleep is one millisecond, it is important to keep this value
- * low to ensure short GC pauses since dvmSuspendAllThreads() uses this
- * function.
- */
- const int minSleep = 1000;
- u8 curTime;
- int curDelay;
-
- /*
- * Get current time, and see if we've already exceeded the limit.
- */
- curTime = dvmGetRelativeTimeUsec();
- if (curTime >= relStartTime + maxTotalSleep) {
- LOGVV("exsl: sleep exceeded (start=%llu max=%d now=%llu)",
- relStartTime, maxTotalSleep, curTime);
- return false;
- }
-
- /*
- * Compute current delay. We're bounded by "maxTotalSleep", so no
- * real risk of overflow assuming "usleep" isn't returning early.
- * (Besides, 2^30 usec is about 18 minutes by itself.)
- *
- * For iteration==0 we just call sched_yield(), so the first sleep
- * at iteration==1 is actually (minSleep * 2).
- */
- curDelay = minSleep;
- while (iteration-- > 0)
- curDelay *= 2;
- assert(curDelay > 0);
-
- if (curTime + curDelay >= relStartTime + maxTotalSleep) {
- LOGVV("exsl: reduced delay from %d to %d",
- curDelay, (int) ((relStartTime + maxTotalSleep) - curTime));
- curDelay = (int) ((relStartTime + maxTotalSleep) - curTime);
- }
-
- if (iteration == 0) {
- LOGVV("exsl: yield");
- sched_yield();
- } else {
- LOGVV("exsl: sleep for %d", curDelay);
- usleep(curDelay);
- }
- return true;
-}
-
-
-/*
- * Set the "close on exec" flag so we don't expose our file descriptors
- * to processes launched by us.
- */
-bool dvmSetCloseOnExec(int fd)
-{
- int flags;
-
- /*
- * There's presently only one flag defined, so getting the previous
- * value of the fd flags is probably unnecessary.
- */
- flags = fcntl(fd, F_GETFD);
- if (flags < 0) {
- ALOGW("Unable to get fd flags for fd %d", fd);
- return false;
- }
- if (fcntl(fd, F_SETFD, flags | FD_CLOEXEC) < 0) {
- ALOGW("Unable to set close-on-exec for fd %d", fd);
- return false;
- }
- return true;
-}
-
-#if (!HAVE_STRLCPY)
-/* Implementation of strlcpy() for platforms that don't already have it. */
-size_t strlcpy(char *dst, const char *src, size_t size) {
- size_t srcLength = strlen(src);
- size_t copyLength = srcLength;
-
- if (srcLength > (size - 1)) {
- copyLength = size - 1;
- }
-
- if (size != 0) {
- strncpy(dst, src, copyLength);
- dst[copyLength] = '\0';
- }
-
- return srcLength;
-}
-#endif
-
-/*
- * Allocates a memory region using ashmem and mmap, initialized to
- * zero. Actual allocation rounded up to page multiple. Returns
- * NULL on failure.
- */
-void *dvmAllocRegion(size_t byteCount, int prot, const char *name) {
- void *base;
- int fd, ret;
-
- byteCount = ALIGN_UP_TO_PAGE_SIZE(byteCount);
- fd = ashmem_create_region(name, byteCount);
- if (fd == -1) {
- return NULL;
- }
- base = mmap(NULL, byteCount, prot, MAP_PRIVATE, fd, 0);
- ret = close(fd);
- if (base == MAP_FAILED) {
- return NULL;
- }
- if (ret == -1) {
- munmap(base, byteCount);
- return NULL;
- }
- return base;
-}
-
-/*
- * Get some per-thread stats.
- *
- * This is currently generated by opening the appropriate "stat" file
- * in /proc and reading the pile of stuff that comes out.
- */
-bool dvmGetThreadStats(ProcStatData* pData, pid_t tid)
-{
- /*
- int pid;
- char comm[128];
- char state;
- int ppid, pgrp, session, tty_nr, tpgid;
- unsigned long flags, minflt, cminflt, majflt, cmajflt, utime, stime;
- long cutime, cstime, priority, nice, zero, itrealvalue;
- unsigned long starttime, vsize;
- long rss;
- unsigned long rlim, startcode, endcode, startstack, kstkesp, kstkeip;
- unsigned long signal, blocked, sigignore, sigcatch, wchan, nswap, cnswap;
- int exit_signal, processor;
- unsigned long rt_priority, policy;
-
- scanf("%d %s %c %d %d %d %d %d %lu %lu %lu %lu %lu %lu %lu %ld %ld %ld "
- "%ld %ld %ld %lu %lu %ld %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu "
- "%lu %lu %lu %d %d %lu %lu",
- &pid, comm, &state, &ppid, &pgrp, &session, &tty_nr, &tpgid,
- &flags, &minflt, &cminflt, &majflt, &cmajflt, &utime, &stime,
- &cutime, &cstime, &priority, &nice, &zero, &itrealvalue,
- &starttime, &vsize, &rss, &rlim, &startcode, &endcode,
- &startstack, &kstkesp, &kstkeip, &signal, &blocked, &sigignore,
- &sigcatch, &wchan, &nswap, &cnswap, &exit_signal, &processor,
- &rt_priority, &policy);
-
- (new: delayacct_blkio_ticks %llu (since Linux 2.6.18))
- */
-
- char nameBuf[64];
- int i, fd;
-
- /*
- * Open and read the appropriate file. This is expected to work on
- * Linux but will fail on other platforms (e.g. Mac sim).
- */
- sprintf(nameBuf, "/proc/self/task/%d/stat", (int) tid);
- fd = open(nameBuf, O_RDONLY);
- if (fd < 0) {
- ALOGV("Unable to open '%s': %s", nameBuf, strerror(errno));
- return false;
- }
-
- char lineBuf[512]; /* > 2x typical */
- int cc = read(fd, lineBuf, sizeof(lineBuf)-1);
- if (cc <= 0) {
- const char* msg = (cc == 0) ? "unexpected EOF" : strerror(errno);
- ALOGI("Unable to read '%s': %s", nameBuf, msg);
- close(fd);
- return false;
- }
- close(fd);
- lineBuf[cc] = '\0';
-
- /*
- * Skip whitespace-separated tokens. For the most part we can assume
- * that tokens do not contain spaces, and are separated by exactly one
- * space character. The only exception is the second field ("comm")
- * which may contain spaces but is surrounded by parenthesis.
- */
- char* cp = strchr(lineBuf, ')');
- if (cp == NULL)
- goto parse_fail;
- cp += 2;
- pData->state = *cp++;
-
- for (i = 3; i < 13; i++) {
- cp = strchr(cp+1, ' ');
- if (cp == NULL)
- goto parse_fail;
- }
-
- /*
- * Grab utime/stime.
- */
- char* endp;
- pData->utime = strtoul(cp+1, &endp, 10);
- if (endp == cp+1)
- ALOGI("Warning: strtoul failed on utime ('%.30s...')", cp);
-
- cp = strchr(cp+1, ' ');
- if (cp == NULL)
- goto parse_fail;
-
- pData->stime = strtoul(cp+1, &endp, 10);
- if (endp == cp+1)
- ALOGI("Warning: strtoul failed on stime ('%.30s...')", cp);
-
- /*
- * Skip more stuff we don't care about.
- */
- for (i = 14; i < 38; i++) {
- cp = strchr(cp+1, ' ');
- if (cp == NULL)
- goto parse_fail;
- }
-
- /*
- * Grab processor number.
- */
- pData->processor = strtol(cp+1, &endp, 10);
- if (endp == cp+1)
- ALOGI("Warning: strtoul failed on processor ('%.30s...')", cp);
-
- return true;
-
-parse_fail:
- ALOGI("stat parse failed (%s)", lineBuf);
- return false;
-}
-
-/* documented in header file */
-const char* dvmPathToAbsolutePortion(const char* path) {
- if (path == NULL) {
- return NULL;
- }
-
- if (path[0] == '/') {
- /* It's a regular absolute path. Return it. */
- return path;
- }
-
- const char* sentinel = strstr(path, "/./");
-
- if (sentinel != NULL) {
- /* It's got the sentinel. Return a pointer to the second slash. */
- return sentinel + 2;
- }
-
- return NULL;
-}
-
-// From RE2.
-void StringAppendV(std::string* dst, const char* format, va_list ap) {
- // First try with a small fixed size buffer
- char space[1024];
-
- // It's possible for methods that use a va_list to invalidate
- // the data in it upon use. The fix is to make a copy
- // of the structure before using it and use that copy instead.
- va_list backup_ap;
- va_copy(backup_ap, ap);
- int result = vsnprintf(space, sizeof(space), format, backup_ap);
- va_end(backup_ap);
-
- if ((result >= 0) && ((size_t) result < sizeof(space))) {
- // It fit
- dst->append(space, result);
- return;
- }
-
- // Repeatedly increase buffer size until it fits
- int length = sizeof(space);
- while (true) {
- if (result < 0) {
- // Older behavior: just try doubling the buffer size
- length *= 2;
- } else {
- // We need exactly "result+1" characters
- length = result+1;
- }
- char* buf = new char[length];
-
- // Restore the va_list before we use it again
- va_copy(backup_ap, ap);
- result = vsnprintf(buf, length, format, backup_ap);
- va_end(backup_ap);
-
- if ((result >= 0) && (result < length)) {
- // It fit
- dst->append(buf, result);
- delete[] buf;
- return;
- }
- delete[] buf;
- }
-}
-
-std::string StringPrintf(const char* fmt, ...) {
- va_list ap;
- va_start(ap, fmt);
- std::string result;
- StringAppendV(&result, fmt, ap);
- va_end(ap);
- return result;
-}
-
-void StringAppendF(std::string* dst, const char* format, ...) {
- va_list ap;
- va_start(ap, format);
- StringAppendV(dst, format, ap);
- va_end(ap);
-}