# HG changeset patch # User Rubidium # Date 2023-05-20 17:45:38 # Node ID 005dbba2ae93f803066524e62d2b44db7b68551d # Parent 066f4ec267dda98f1e6441e9a0df12d7ebed0219 Codechange: replace seprintf with fmt::format for filling the crash log data diff --git a/src/crashlog.cpp b/src/crashlog.cpp --- a/src/crashlog.cpp +++ b/src/crashlog.cpp @@ -76,58 +76,54 @@ /* static */ std::string CrashLog::message{ "" }; -char *CrashLog::LogCompiler(char *buffer, const char *last) const +void CrashLog::LogCompiler(std::back_insert_iterator &output_iterator) const { - buffer += seprintf(buffer, last, " Compiler: " + fmt::format_to(output_iterator, " Compiler: " #if defined(_MSC_VER) - "MSVC %d", _MSC_VER + "MSVC {}", _MSC_VER #elif defined(__ICC) && defined(__GNUC__) - "ICC %d (GCC %d.%d.%d mode)", __ICC, __GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__ + "ICC {} (GCC {}.{}.{} mode)", __ICC, __GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__ #elif defined(__ICC) - "ICC %d", __ICC + "ICC {}", __ICC #elif defined(__GNUC__) - "GCC %d.%d.%d", __GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__ + "GCC {}.{}.{}", __GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__ #elif defined(__WATCOMC__) - "WatcomC %d", __WATCOMC__ + "WatcomC {}", __WATCOMC__ #else "" #endif ); #if defined(__VERSION__) - return buffer + seprintf(buffer, last, " \"" __VERSION__ "\"\n\n"); + fmt::format_to(output_iterator, " \"" __VERSION__ "\"\n\n"); #else - return buffer + seprintf(buffer, last, "\n\n"); + fmt::format_to(output_iterator, "\n\n"); #endif } -/* virtual */ char *CrashLog::LogRegisters(char *buffer, const char *last) const +/* virtual */ void CrashLog::LogRegisters(std::back_insert_iterator &output_iterator) const { /* Stub implementation; not all OSes support this. */ - return buffer; } -/* virtual */ char *CrashLog::LogModules(char *buffer, const char *last) const +/* virtual */ void CrashLog::LogModules(std::back_insert_iterator &output_iterator) const { /* Stub implementation; not all OSes support this. */ - return buffer; } /** * Writes OpenTTD's version to the buffer. - * @param buffer The begin where to write at. - * @param last The last position in the buffer to write to. - * @return the position of the \c '\0' character after the buffer. + * @param output_iterator Iterator to write the output to. */ -char *CrashLog::LogOpenTTDVersion(char *buffer, const char *last) const +void CrashLog::LogOpenTTDVersion(std::back_insert_iterator &output_iterator) const { - return buffer + seprintf(buffer, last, + fmt::format_to(output_iterator, "OpenTTD version:\n" - " Version: %s (%d)\n" - " NewGRF ver: %08x\n" - " Bits: %d\n" - " Endian: %s\n" - " Dedicated: %s\n" - " Build date: %s\n\n", + " Version: {} ({})\n" + " NewGRF ver: {:08x}\n" + " Bits: {}\n" + " Endian: {}\n" + " Dedicated: {}\n" + " Build date: {}\n\n", _openttd_revision, _openttd_revision_modified, _openttd_newgrf_version, @@ -153,83 +149,77 @@ char *CrashLog::LogOpenTTDVersion(char * /** * Writes the (important) configuration settings to the buffer. * E.g. graphics set, sound set, blitter and AIs. - * @param buffer The begin where to write at. - * @param last The last position in the buffer to write to. - * @return the position of the \c '\0' character after the buffer. + * @param output_iterator Iterator to write the output to. */ -char *CrashLog::LogConfiguration(char *buffer, const char *last) const +void CrashLog::LogConfiguration(std::back_insert_iterator &output_iterator) const { - buffer += seprintf(buffer, last, + fmt::format_to(output_iterator, "Configuration:\n" - " Blitter: %s\n" - " Graphics set: %s (%u)\n" - " Language: %s\n" - " Music driver: %s\n" - " Music set: %s (%u)\n" - " Network: %s\n" - " Sound driver: %s\n" - " Sound set: %s (%u)\n" - " Video driver: %s\n\n", + " Blitter: {}\n" + " Graphics set: {} ({})\n" + " Language: {}\n" + " Music driver: {}\n" + " Music set: {} ({})\n" + " Network: {}\n" + " Sound driver: {}\n" + " Sound set: {} ({})\n" + " Video driver: {}\n\n", BlitterFactory::GetCurrentBlitter() == nullptr ? "none" : BlitterFactory::GetCurrentBlitter()->GetName(), - BaseGraphics::GetUsedSet() == nullptr ? "none" : BaseGraphics::GetUsedSet()->name.c_str(), + BaseGraphics::GetUsedSet() == nullptr ? "none" : BaseGraphics::GetUsedSet()->name, BaseGraphics::GetUsedSet() == nullptr ? UINT32_MAX : BaseGraphics::GetUsedSet()->version, _current_language == nullptr ? "none" : _current_language->file, MusicDriver::GetInstance() == nullptr ? "none" : MusicDriver::GetInstance()->GetName(), - BaseMusic::GetUsedSet() == nullptr ? "none" : BaseMusic::GetUsedSet()->name.c_str(), + BaseMusic::GetUsedSet() == nullptr ? "none" : BaseMusic::GetUsedSet()->name, BaseMusic::GetUsedSet() == nullptr ? UINT32_MAX : BaseMusic::GetUsedSet()->version, _networking ? (_network_server ? "server" : "client") : "no", SoundDriver::GetInstance() == nullptr ? "none" : SoundDriver::GetInstance()->GetName(), - BaseSounds::GetUsedSet() == nullptr ? "none" : BaseSounds::GetUsedSet()->name.c_str(), + BaseSounds::GetUsedSet() == nullptr ? "none" : BaseSounds::GetUsedSet()->name, BaseSounds::GetUsedSet() == nullptr ? UINT32_MAX : BaseSounds::GetUsedSet()->version, VideoDriver::GetInstance() == nullptr ? "none" : VideoDriver::GetInstance()->GetInfoString() ); - buffer += seprintf(buffer, last, + fmt::format_to(output_iterator, "Fonts:\n" - " Small: %s\n" - " Medium: %s\n" - " Large: %s\n" - " Mono: %s\n\n", + " Small: {}\n" + " Medium: {}\n" + " Large: {}\n" + " Mono: {}\n\n", FontCache::Get(FS_SMALL)->GetFontName(), FontCache::Get(FS_NORMAL)->GetFontName(), FontCache::Get(FS_LARGE)->GetFontName(), FontCache::Get(FS_MONO)->GetFontName() ); - buffer += seprintf(buffer, last, "AI Configuration (local: %i) (current: %i):\n", (int)_local_company, (int)_current_company); + fmt::format_to(output_iterator, "AI Configuration (local: {}) (current: {}):\n", _local_company, _current_company); for (const Company *c : Company::Iterate()) { if (c->ai_info == nullptr) { - buffer += seprintf(buffer, last, " %2i: Human\n", (int)c->index); + fmt::format_to(output_iterator, " {:2}: Human\n", c->index); } else { - buffer += seprintf(buffer, last, " %2i: %s (v%d)\n", (int)c->index, c->ai_info->GetName().c_str(), c->ai_info->GetVersion()); + fmt::format_to(output_iterator, " {:2}: {} (v{})\n", (int)c->index, c->ai_info->GetName(), c->ai_info->GetVersion()); } } if (Game::GetInfo() != nullptr) { - buffer += seprintf(buffer, last, " GS: %s (v%d)\n", Game::GetInfo()->GetName().c_str(), Game::GetInfo()->GetVersion()); + fmt::format_to(output_iterator, " GS: {} (v{})\n", Game::GetInfo()->GetName(), Game::GetInfo()->GetVersion()); } - buffer += seprintf(buffer, last, "\n"); - - return buffer; + fmt::format_to(output_iterator, "\n"); } /** * Writes information (versions) of the used libraries. - * @param buffer The begin where to write at. - * @param last The last position in the buffer to write to. - * @return the position of the \c '\0' character after the buffer. + * @param output_iterator Iterator to write the output to. */ -char *CrashLog::LogLibraries(char *buffer, const char *last) const +void CrashLog::LogLibraries(std::back_insert_iterator &output_iterator) const { - buffer += seprintf(buffer, last, "Libraries:\n"); + fmt::format_to(output_iterator, "Libraries:\n"); #ifdef WITH_ALLEGRO - buffer += seprintf(buffer, last, " Allegro: %s\n", allegro_id); + fmt::format_to(output_iterator, " Allegro: {}\n", allegro_id); #endif /* WITH_ALLEGRO */ #ifdef WITH_FONTCONFIG int version = FcGetVersion(); - buffer += seprintf(buffer, last, " FontConfig: %d.%d.%d\n", version / 10000, (version / 100) % 100, version % 100); + fmt::format_to(output_iterator, " FontConfig: {}.{}.{}\n", version / 10000, (version / 100) % 100, version % 100); #endif /* WITH_FONTCONFIG */ #ifdef WITH_FREETYPE @@ -238,11 +228,11 @@ char *CrashLog::LogLibraries(char *buffe FT_Init_FreeType(&library); FT_Library_Version(library, &major, &minor, &patch); FT_Done_FreeType(library); - buffer += seprintf(buffer, last, " FreeType: %d.%d.%d\n", major, minor, patch); + fmt::format_to(output_iterator, " FreeType: {}.{}.{}\n", major, minor, patch); #endif /* WITH_FREETYPE */ #if defined(WITH_HARFBUZZ) - buffer += seprintf(buffer, last, " HarfBuzz: %s\n", hb_version_string()); + fmt::format_to(output_iterator, " HarfBuzz: {}\n", hb_version_string()); #endif /* WITH_HARFBUZZ */ #if defined(WITH_ICU_I18N) @@ -251,82 +241,76 @@ char *CrashLog::LogLibraries(char *buffe UVersionInfo ver; u_getVersion(ver); u_versionToString(ver, buf); - buffer += seprintf(buffer, last, " ICU i18n: %s\n", buf); + fmt::format_to(output_iterator, " ICU i18n: {}\n", buf); #endif /* WITH_ICU_I18N */ #ifdef WITH_LIBLZMA - buffer += seprintf(buffer, last, " LZMA: %s\n", lzma_version_string()); + fmt::format_to(output_iterator, " LZMA: {}\n", lzma_version_string()); #endif #ifdef WITH_LZO - buffer += seprintf(buffer, last, " LZO: %s\n", lzo_version_string()); + fmt::format_to(output_iterator, " LZO: {}\n", lzo_version_string()); #endif #ifdef WITH_PNG - buffer += seprintf(buffer, last, " PNG: %s\n", png_get_libpng_ver(nullptr)); + fmt::format_to(output_iterator, " PNG: {}\n", png_get_libpng_ver(nullptr)); #endif /* WITH_PNG */ #ifdef WITH_SDL const SDL_version *sdl_v = SDL_Linked_Version(); - buffer += seprintf(buffer, last, " SDL1: %d.%d.%d\n", sdl_v->major, sdl_v->minor, sdl_v->patch); + fmt::format_to(output_iterator, " SDL1: {}.{}.{}\n", sdl_v->major, sdl_v->minor, sdl_v->patch); #elif defined(WITH_SDL2) SDL_version sdl2_v; SDL_GetVersion(&sdl2_v); - buffer += seprintf(buffer, last, " SDL2: %d.%d.%d\n", sdl2_v.major, sdl2_v.minor, sdl2_v.patch); + fmt::format_to(output_iterator, " SDL2: {}.{}.{}\n", sdl2_v.major, sdl2_v.minor, sdl2_v.patch); #endif #ifdef WITH_ZLIB - buffer += seprintf(buffer, last, " Zlib: %s\n", zlibVersion()); + fmt::format_to(output_iterator, " Zlib: {}\n", zlibVersion()); #endif #ifdef WITH_CURL auto *curl_v = curl_version_info(CURLVERSION_NOW); - buffer += seprintf(buffer, last, " Curl: %s\n", curl_v->version); + fmt::format_to(output_iterator, " Curl: {}\n", curl_v->version); if (curl_v->ssl_version != nullptr) { - buffer += seprintf(buffer, last, " Curl SSL: %s\n", curl_v->ssl_version); + fmt::format_to(output_iterator, " Curl SSL: {}\n", curl_v->ssl_version); } else { - buffer += seprintf(buffer, last, " Curl SSL: none\n"); + fmt::format_to(output_iterator, " Curl SSL: none\n"); } #endif - buffer += seprintf(buffer, last, "\n"); - return buffer; + fmt::format_to(output_iterator, "\n"); } /** * Writes the gamelog data to the buffer. - * @param buffer The begin where to write at. - * @param last The last position in the buffer to write to. - * @return the position of the \c '\0' character after the buffer. + * @param output_iterator Iterator to write the output to. */ -char *CrashLog::LogGamelog(char *buffer, const char *last) const +void CrashLog::LogGamelog(std::back_insert_iterator &output_iterator) const { - _gamelog.Print([&buffer, last](const std::string &s) { - buffer += seprintf(buffer, last, "%s\n", s.c_str()); + _gamelog.Print([&output_iterator](const std::string &s) { + fmt::format_to(output_iterator, "{}\n", s); }); - return buffer + seprintf(buffer, last, "\n"); + fmt::format_to(output_iterator, "\n"); } /** * Writes up to 32 recent news messages to the buffer, with the most recent first. - * @param buffer The begin where to write at. - * @param last The last position in the buffer to write to. - * @return the position of the \c '\0' character after the buffer. + * @param output_iterator Iterator to write the output to. */ -char *CrashLog::LogRecentNews(char *buffer, const char *last) const +void CrashLog::LogRecentNews(std::back_insert_iterator &output_iterator) const { - buffer += seprintf(buffer, last, "Recent news messages:\n"); + fmt::format_to(output_iterator, "Recent news messages:\n"); int i = 0; for (NewsItem *news = _latest_news; i < 32 && news != nullptr; news = news->prev, i++) { TimerGameCalendar::YearMonthDay ymd; TimerGameCalendar::ConvertDateToYMD(news->date, &ymd); - buffer += seprintf(buffer, last, "(%i-%02i-%02i) StringID: %u, Type: %u, Ref1: %u, %u, Ref2: %u, %u\n", + fmt::format_to(output_iterator, "({}-{:02}-{:02}) StringID: {}, Type: {}, Ref1: {}, {}, Ref2: {}, {}\n", ymd.year, ymd.month + 1, ymd.day, news->string_id, news->type, news->reftype1, news->ref1, news->reftype2, news->ref2); } - buffer += seprintf(buffer, last, "\n"); - return buffer; + fmt::format_to(output_iterator, "\n"); } /** @@ -349,34 +333,30 @@ int CrashLog::CreateFileName(char *filen /** * Fill the crash log buffer with all data of a crash log. - * @param buffer The begin where to write at. - * @param last The last position in the buffer to write to. - * @return the position of the \c '\0' character after the buffer. + * @param output_iterator Iterator to write the output to. */ -char *CrashLog::FillCrashLog(char *buffer, const char *last) const +void CrashLog::FillCrashLog(std::back_insert_iterator &output_iterator) const { - buffer += seprintf(buffer, last, "*** OpenTTD Crash Report ***\n\n"); - std::string temp = fmt::format("Crash at: {:%Y-%m-%d %H:%M:%S} (UTC)\n", fmt::gmtime(time(nullptr))); - buffer = strecpy(buffer, temp.c_str(), last); + fmt::format_to(output_iterator, "*** OpenTTD Crash Report ***\n\n"); + fmt::format_to(output_iterator, "Crash at: {:%Y-%m-%d %H:%M:%S} (UTC)\n", fmt::gmtime(time(nullptr))); TimerGameCalendar::YearMonthDay ymd; TimerGameCalendar::ConvertDateToYMD(TimerGameCalendar::date, &ymd); - buffer += seprintf(buffer, last, "In game date: %i-%02i-%02i (%i)\n\n", ymd.year, ymd.month + 1, ymd.day, TimerGameCalendar::date_fract); + fmt::format_to(output_iterator, "In game date: {}-{:02}-{:02} ({})\n\n", ymd.year, ymd.month + 1, ymd.day, TimerGameCalendar::date_fract); - buffer = this->LogError(buffer, last, CrashLog::message.c_str()); - buffer = this->LogOpenTTDVersion(buffer, last); - buffer = this->LogRegisters(buffer, last); - buffer = this->LogStacktrace(buffer, last); - buffer = this->LogOSVersion(buffer, last); - buffer = this->LogCompiler(buffer, last); - buffer = this->LogConfiguration(buffer, last); - buffer = this->LogLibraries(buffer, last); - buffer = this->LogModules(buffer, last); - buffer = this->LogGamelog(buffer, last); - buffer = this->LogRecentNews(buffer, last); + this->LogError(output_iterator, CrashLog::message); + this->LogOpenTTDVersion(output_iterator); + this->LogRegisters(output_iterator); + this->LogStacktrace(output_iterator); + this->LogOSVersion(output_iterator); + this->LogCompiler(output_iterator); + this->LogConfiguration(output_iterator); + this->LogLibraries(output_iterator); + this->LogModules(output_iterator); + this->LogGamelog(output_iterator); + this->LogRecentNews(output_iterator); - buffer += seprintf(buffer, last, "*** End of OpenTTD Crash Report ***\n"); - return buffer; + fmt::format_to(output_iterator, "*** End of OpenTTD Crash Report ***\n"); } /** @@ -475,16 +455,18 @@ bool CrashLog::MakeCrashLog() const crashlogged = true; char filename[MAX_PATH]; - char buffer[65536]; + std::string buffer; + buffer.reserve(65536); + auto output_iterator = std::back_inserter(buffer); bool ret = true; fmt::print("Crash encountered, generating crash log...\n"); - this->FillCrashLog(buffer, lastof(buffer)); + this->FillCrashLog(output_iterator); fmt::print("{}\n", buffer); fmt::print("Crash log generated.\n\n"); fmt::print("Writing crash log to disk...\n"); - bool bret = this->WriteCrashLog(buffer, filename, lastof(filename)); + bool bret = this->WriteCrashLog(buffer.c_str(), filename, lastof(filename)); if (bret) { fmt::print("Crash log written to {}. Please add this file to any bug reports.\n\n", filename); } else { diff --git a/src/crashlog.h b/src/crashlog.h --- a/src/crashlog.h +++ b/src/crashlog.h @@ -20,62 +20,50 @@ private: protected: /** * Writes OS' version to the buffer. - * @param buffer The begin where to write at. - * @param last The last position in the buffer to write to. - * @return the position of the \c '\0' character after the buffer. + * @param output_iterator Iterator to write the output to. */ - virtual char *LogOSVersion(char *buffer, const char *last) const = 0; + virtual void LogOSVersion(std::back_insert_iterator &output_iterator) const = 0; /** * Writes compiler (and its version, if available) to the buffer. - * @param buffer The begin where to write at. - * @param last The last position in the buffer to write to. - * @return the position of the \c '\0' character after the buffer. + * @param output_iterator Iterator to write the output to. */ - virtual char *LogCompiler(char *buffer, const char *last) const; + virtual void LogCompiler(std::back_insert_iterator &output_iterator) const; /** * Writes actually encountered error to the buffer. - * @param buffer The begin where to write at. - * @param last The last position in the buffer to write to. + * @param output_iterator Iterator to write the output to. * @param message Message passed to use for errors. - * @return the position of the \c '\0' character after the buffer. */ - virtual char *LogError(char *buffer, const char *last, const char *message) const = 0; + virtual void LogError(std::back_insert_iterator &output_iterator, const std::string_view message) const = 0; /** * Writes the stack trace to the buffer, if there is information about it * available. - * @param buffer The begin where to write at. - * @param last The last position in the buffer to write to. - * @return the position of the \c '\0' character after the buffer. + * @param output_iterator Iterator to write the output to. */ - virtual char *LogStacktrace(char *buffer, const char *last) const = 0; + virtual void LogStacktrace(std::back_insert_iterator &output_iterator) const = 0; /** * Writes information about the data in the registers, if there is * information about it available. - * @param buffer The begin where to write at. - * @param last The last position in the buffer to write to. - * @return the position of the \c '\0' character after the buffer. + * @param output_iterator Iterator to write the output to. */ - virtual char *LogRegisters(char *buffer, const char *last) const; + virtual void LogRegisters(std::back_insert_iterator &output_iterator) const; /** * Writes the dynamically linked libraries/modules to the buffer, if there * is information about it available. - * @param buffer The begin where to write at. - * @param last The last position in the buffer to write to. - * @return the position of the \c '\0' character after the buffer. + * @param output_iterator Iterator to write the output to. */ - virtual char *LogModules(char *buffer, const char *last) const; + virtual void LogModules(std::back_insert_iterator &output_iterator) const; - char *LogOpenTTDVersion(char *buffer, const char *last) const; - char *LogConfiguration(char *buffer, const char *last) const; - char *LogLibraries(char *buffer, const char *last) const; - char *LogGamelog(char *buffer, const char *last) const; - char *LogRecentNews(char *buffer, const char *list) const; + void LogOpenTTDVersion(std::back_insert_iterator &output_iterator) const; + void LogConfiguration(std::back_insert_iterator &output_iterator) const; + void LogLibraries(std::back_insert_iterator &output_iterator) const; + void LogGamelog(std::back_insert_iterator &output_iterator) const; + void LogRecentNews(std::back_insert_iterator &output_iterator) const; int CreateFileName(char *filename, const char *filename_last, const char *ext, bool with_dir = true) const; @@ -83,7 +71,7 @@ public: /** Stub destructor to silence some compilers. */ virtual ~CrashLog() = default; - char *FillCrashLog(char *buffer, const char *last) const; + void FillCrashLog(std::back_insert_iterator &output_iterator) const; bool WriteCrashLog(const char *buffer, char *filename, const char *filename_last) const; /** diff --git a/src/os/macosx/crashlog_osx.cpp b/src/os/macosx/crashlog_osx.cpp --- a/src/os/macosx/crashlog_osx.cpp +++ b/src/os/macosx/crashlog_osx.cpp @@ -31,13 +31,6 @@ #define IS_ALIGNED(addr) (((uintptr_t)(addr) & 0xf) == 0) #endif -/* printf format specification for 32/64-bit addresses. */ -#ifdef __LP64__ -#define PRINTF_PTR "0x%016lx" -#else -#define PRINTF_PTR "0x%08lx" -#endif - #define MAX_STACK_FRAMES 64 /** @@ -51,20 +44,20 @@ class CrashLogOSX : public CrashLog { char filename_save[MAX_PATH]; ///< Path of crash.sav char filename_screenshot[MAX_PATH]; ///< Path of crash.(png|bmp|pcx) - char *LogOSVersion(char *buffer, const char *last) const override + void LogOSVersion(std::back_insert_iterator &output_iterator) const override { int ver_maj, ver_min, ver_bug; GetMacOSVersion(&ver_maj, &ver_min, &ver_bug); const NXArchInfo *arch = NXGetLocalArchInfo(); - return buffer + seprintf(buffer, last, + fmt::format_to(output_iterator, "Operating system:\n" " Name: Mac OS X\n" - " Release: %d.%d.%d\n" - " Machine: %s\n" - " Min Ver: %d\n" - " Max Ver: %d\n", + " Release: {}.{}.{}\n" + " Machine: {}\n" + " Min Ver: {}\n" + " Max Ver: {}\n", ver_maj, ver_min, ver_bug, arch != nullptr ? arch->description : "unknown", MAC_OS_X_VERSION_MIN_REQUIRED, @@ -72,25 +65,25 @@ class CrashLogOSX : public CrashLog { ); } - char *LogError(char *buffer, const char *last, const char *message) const override + void LogError(std::back_insert_iterator &output_iterator, const std::string_view message) const override { - return buffer + seprintf(buffer, last, + fmt::format_to(output_iterator, "Crash reason:\n" - " Signal: %s (%d)\n" - " Message: %s\n\n", + " Signal: {} ({})\n" + " Message: {}\n\n", strsignal(this->signum), this->signum, message ); } - char *LogStacktrace(char *buffer, const char *last) const override + void LogStacktrace(std::back_insert_iterator &output_iterator) const override { /* As backtrace() is only implemented in 10.5 or later, * we're rolling our own here. Mostly based on * http://stackoverflow.com/questions/289820/getting-the-current-stack-trace-on-mac-os-x * and some details looked up in the Darwin sources. */ - buffer += seprintf(buffer, last, "\nStacktrace:\n"); + fmt::format_to(output_iterator, "\nStacktrace:\n"); void **frame; #if defined(__ppc__) || defined(__ppc64__) @@ -110,7 +103,7 @@ class CrashLogOSX : public CrashLog { if (ip == nullptr) break; /* Print running index. */ - buffer += seprintf(buffer, last, " [%02d]", i); + fmt::format_to(output_iterator, " [{:02}]", i); Dl_info dli; bool dl_valid = dladdr(ip, &dli) != 0; @@ -126,7 +119,7 @@ class CrashLogOSX : public CrashLog { } } /* Print image name and IP. */ - buffer += seprintf(buffer, last, " %-20s " PRINTF_PTR, fname, (uintptr_t)ip); + fmt::format_to(output_iterator, " {:20s} {}", fname, (uintptr_t)ip); /* Print function offset if information is available. */ if (dl_valid && dli.dli_sname != nullptr && dli.dli_saddr != nullptr) { @@ -135,11 +128,11 @@ class CrashLogOSX : public CrashLog { char *func_name = abi::__cxa_demangle(dli.dli_sname, nullptr, 0, &status); long int offset = (intptr_t)ip - (intptr_t)dli.dli_saddr; - buffer += seprintf(buffer, last, " (%s + %ld)", func_name != nullptr ? func_name : dli.dli_sname, offset); + fmt::format_to(output_iterator, " ({} + {})", func_name != nullptr ? func_name : dli.dli_sname, offset); free(func_name); } - buffer += seprintf(buffer, last, "\n"); + fmt::format_to(output_iterator, "\n"); /* Get address of next stack frame. */ void **next = (void **)frame[0]; @@ -148,7 +141,7 @@ class CrashLogOSX : public CrashLog { frame = next; } - return buffer + seprintf(buffer, last, "\n"); + fmt::format_to(output_iterator, "\n"); } public: @@ -163,40 +156,6 @@ public: filename_screenshot[0] = '\0'; } - /** Generate the crash log. */ - bool MakeCrashLog() - { - char buffer[65536]; - bool ret = true; - - fmt::print("Crash encountered, generating crash log...\n"); - this->FillCrashLog(buffer, lastof(buffer)); - fmt::print("{}\n", buffer); - fmt::print("Crash log generated.\n\n"); - - fmt::print("Writing crash log to disk...\n"); - if (!this->WriteCrashLog(buffer, filename_log, lastof(filename_log))) { - filename_log[0] = '\0'; - ret = false; - } - - fmt::print("Writing crash savegame...\n"); - if (!this->WriteSavegame(filename_save, lastof(filename_save))) { - filename_save[0] = '\0'; - ret = false; - } - - fmt::print("Writing crash screenshot...\n"); - if (!this->WriteScreenshot(filename_screenshot, lastof(filename_screenshot))) { - filename_screenshot[0] = '\0'; - ret = false; - } - - this->SendSurvey(); - - return ret; - } - /** Show a dialog with the crash information. */ void DisplayCrashDialog() const { diff --git a/src/os/unix/crashlog_unix.cpp b/src/os/unix/crashlog_unix.cpp --- a/src/os/unix/crashlog_unix.cpp +++ b/src/os/unix/crashlog_unix.cpp @@ -38,19 +38,20 @@ class CrashLogUnix : public CrashLog { /** Signal that has been thrown. */ int signum; - char *LogOSVersion(char *buffer, const char *last) const override + void LogOSVersion(std::back_insert_iterator &output_iterator) const override { struct utsname name; if (uname(&name) < 0) { - return buffer + seprintf(buffer, last, "Could not get OS version: %s\n", strerror(errno)); + fmt::format_to(output_iterator, "Could not get OS version: {}\n", strerror(errno)); + return; } - return buffer + seprintf(buffer, last, + fmt::format_to(output_iterator, "Operating system:\n" - " Name: %s\n" - " Release: %s\n" - " Version: %s\n" - " Machine: %s\n", + " Name: {}\n" + " Release: {}\n" + " Version: {}\n" + " Machine: {}\n", name.sysname, name.release, name.version, @@ -58,12 +59,12 @@ class CrashLogUnix : public CrashLog { ); } - char *LogError(char *buffer, const char *last, const char *message) const override + void LogError(std::back_insert_iterator &output_iterator, const std::string_view message) const override { - return buffer + seprintf(buffer, last, - "Crash reason:\n" - " Signal: %s (%d)\n" - " Message: %s\n\n", + fmt::format_to(output_iterator, + "Crash reason:\n" + " Signal: {} ({})\n" + " Message: {}\n\n", strsignal(this->signum), this->signum, message @@ -73,9 +74,8 @@ class CrashLogUnix : public CrashLog { #if defined(SUNOS) /** Data needed while walking up the stack */ struct StackWalkerParams { - char **bufptr; ///< Buffer - const char *last; ///< End of buffer - int counter; ///< We are at counter-th stack level + std::back_insert_iterator *output_iterator; ///< Buffer + int counter; ///< We are at counter-th stack level }; /** @@ -92,10 +92,10 @@ class CrashLogUnix : public CrashLog { /* Resolve program counter to file and nearest symbol (if possible) */ Dl_info dli; if (dladdr((void *)pc, &dli) != 0) { - *wp->bufptr += seprintf(*wp->bufptr, wp->last, " [%02i] %s(%s+0x%x) [0x%x]\n", + fmt::format_to(*wp->output_iterator, " [{:02}] {}({}+0x{:x}) [0x{:x}]\n", wp->counter, dli.dli_fname, dli.dli_sname, (int)((byte *)pc - (byte *)dli.dli_saddr), (uint)pc); } else { - *wp->bufptr += seprintf(*wp->bufptr, wp->last, " [%02i] [0x%x]\n", wp->counter, (uint)pc); + fmt::format_to(*wp->output_iterator, " [{:02}] [0x{:x}]\n", wp->counter, (uint)pc); } wp->counter++; @@ -103,31 +103,31 @@ class CrashLogUnix : public CrashLog { } #endif - char *LogStacktrace(char *buffer, const char *last) const override + void LogStacktrace(std::back_insert_iterator &output_iterator) const override { - buffer += seprintf(buffer, last, "Stacktrace:\n"); + fmt::format_to(output_iterator, "Stacktrace:\n"); #if defined(__GLIBC__) void *trace[64]; int trace_size = backtrace(trace, lengthof(trace)); char **messages = backtrace_symbols(trace, trace_size); for (int i = 0; i < trace_size; i++) { - buffer += seprintf(buffer, last, " [%02i] %s\n", i, messages[i]); + fmt::format_to(output_iterator, " [{:02}] {}\n", i, messages[i]); } free(messages); #elif defined(SUNOS) ucontext_t uc; if (getcontext(&uc) != 0) { - buffer += seprintf(buffer, last, " getcontext() failed\n\n"); + fmt::format_to(output_iterator, " getcontext() failed\n\n"); return buffer; } - StackWalkerParams wp = { &buffer, last, 0 }; + StackWalkerParams wp = { &output_iterator, 0 }; walkcontext(&uc, &CrashLogUnix::SunOSStackWalker, &wp); #else - buffer += seprintf(buffer, last, " Not supported.\n"); + fmt::format_to(output_iterator, " Not supported.\n"); #endif - return buffer + seprintf(buffer, last, "\n"); + fmt::format_to(output_iterator, "\n"); } public: /** diff --git a/src/os/windows/crashlog_win.cpp b/src/os/windows/crashlog_win.cpp --- a/src/os/windows/crashlog_win.cpp +++ b/src/os/windows/crashlog_win.cpp @@ -25,13 +25,6 @@ #include "../../safeguards.h" -/* printf format specification for 32/64-bit addresses. */ -#ifdef _M_AMD64 -#define PRINTF_PTR "0x%016IX" -#else -#define PRINTF_PTR "0x%08X" -#endif - /** * Windows implementation for the crash logger. */ @@ -39,21 +32,21 @@ class CrashLogWindows : public CrashLog /** Information about the encountered exception */ EXCEPTION_POINTERS *ep; - char *LogOSVersion(char *buffer, const char *last) const override; - char *LogError(char *buffer, const char *last, const char *message) const override; - char *LogStacktrace(char *buffer, const char *last) const override; - char *LogRegisters(char *buffer, const char *last) const override; - char *LogModules(char *buffer, const char *last) const override; + void LogOSVersion(std::back_insert_iterator &output_iterator) const override; + void LogError(std::back_insert_iterator &output_iterator, const std::string_view message) const override; + void LogStacktrace(std::back_insert_iterator &output_iterator) const override; + void LogRegisters(std::back_insert_iterator &output_iterator) const override; + void LogModules(std::back_insert_iterator &output_iterator) const override; public: #if defined(_MSC_VER) int WriteCrashDump(char *filename, const char *filename_last) const override; - char *AppendDecodedStacktrace(char *buffer, const char *last) const; + void AppendDecodedStacktrace(std::back_insert_iterator &output_iterator) const; #else - char *AppendDecodedStacktrace(char *buffer, const char *last) const { return buffer; } + void AppendDecodedStacktrace(std::back_insert_iterator &output_iterator) const {} #endif /* _MSC_VER */ /** Buffer for the generated crash log */ - char crashlog[65536]; + std::string crashlog; /** Buffer for the filename of the crash log */ char crashlog_filename[MAX_PATH]; /** Buffer for the filename of the crash dump */ @@ -68,7 +61,7 @@ public: CrashLogWindows(EXCEPTION_POINTERS *ep = nullptr) : ep(ep) { - this->crashlog[0] = '\0'; + this->crashlog.reserve(65536); this->crashlog_filename[0] = '\0'; this->crashdump_filename[0] = '\0'; this->screenshot_filename[0] = '\0'; @@ -82,36 +75,32 @@ public: /* static */ CrashLogWindows *CrashLogWindows::current = nullptr; -/* virtual */ char *CrashLogWindows::LogOSVersion(char *buffer, const char *last) const +/* virtual */ void CrashLogWindows::LogOSVersion(std::back_insert_iterator &output_iterator) const { _OSVERSIONINFOA os; os.dwOSVersionInfoSize = sizeof(os); GetVersionExA(&os); - return buffer + seprintf(buffer, last, + fmt::format_to(output_iterator, "Operating system:\n" " Name: Windows\n" - " Release: %d.%d.%d (%s)\n", - (int)os.dwMajorVersion, - (int)os.dwMinorVersion, - (int)os.dwBuildNumber, + " Release: {}.{}.{} ({})\n", + os.dwMajorVersion, + os.dwMinorVersion, + os.dwBuildNumber, os.szCSDVersion ); } -/* virtual */ char *CrashLogWindows::LogError(char *buffer, const char *last, const char *message) const +/* virtual */ void CrashLogWindows::LogError(std::back_insert_iterator &output_iterator, const std::string_view message) const { - return buffer + seprintf(buffer, last, + fmt::format_to(output_iterator, "Crash reason:\n" - " Exception: %.8X\n" -#ifdef _M_AMD64 - " Location: %.16IX\n" -#else - " Location: %.8X\n" -#endif - " Message: %s\n\n", - (int)ep->ExceptionRecord->ExceptionCode, + " Exception: {:08X}\n" + " Location: {:X}\n" + " Message: {}\n\n", + ep->ExceptionRecord->ExceptionCode, (size_t)ep->ExceptionRecord->ExceptionAddress, message ); @@ -179,16 +168,16 @@ static void GetFileInfo(DebugFileInfo *d } -static char *PrintModuleInfo(char *output, const char *last, HMODULE mod) +static void PrintModuleInfo(std::back_insert_iterator &output_iterator, HMODULE mod) { wchar_t buffer[MAX_PATH]; DebugFileInfo dfi; GetModuleFileName(mod, buffer, MAX_PATH); GetFileInfo(&dfi, buffer); - output += seprintf(output, last, " %-20s handle: %p size: %d crc: %.8X date: %d-%.2d-%.2d %.2d:%.2d:%.2d\n", - FS2OTTD(buffer).c_str(), - mod, + fmt::format_to(output_iterator, " {:20s} handle: {:X} size: {} crc: {:8X} date: {}-{:02}-{:02} {:02}:{:02}:{:02}\n", + FS2OTTD(buffer), + (size_t)mod, dfi.size, dfi.crc32, dfi.file_time.wYear, @@ -198,14 +187,13 @@ static char *PrintModuleInfo(char *outpu dfi.file_time.wMinute, dfi.file_time.wSecond ); - return output; } -/* virtual */ char *CrashLogWindows::LogModules(char *output, const char *last) const +/* virtual */ void CrashLogWindows::LogModules(std::back_insert_iterator &output_iterator) const { MakeCRCTable(); - output += seprintf(output, last, "Module information:\n"); + fmt::format_to(output_iterator, "Module information:\n"); HANDLE proc = OpenProcess(PROCESS_ALL_ACCESS, FALSE, GetCurrentProcessId()); if (proc != nullptr) { @@ -216,24 +204,25 @@ static char *PrintModuleInfo(char *outpu if (res) { size_t count = std::min(needed / sizeof(HMODULE), lengthof(modules)); - for (size_t i = 0; i != count; i++) output = PrintModuleInfo(output, last, modules[i]); - return output + seprintf(output, last, "\n"); + for (size_t i = 0; i != count; i++) PrintModuleInfo(output_iterator, modules[i]); + fmt::format_to(output_iterator, "\n"); + return; } } - output = PrintModuleInfo(output, last, nullptr); - return output + seprintf(output, last, "\n"); + PrintModuleInfo(output_iterator, nullptr); + fmt::format_to(output_iterator, "\n"); } -/* virtual */ char *CrashLogWindows::LogRegisters(char *buffer, const char *last) const +/* virtual */ void CrashLogWindows::LogRegisters(std::back_insert_iterator &output_iterator) const { - buffer += seprintf(buffer, last, "Registers:\n"); + fmt::format_to(output_iterator, "Registers:\n"); #ifdef _M_AMD64 - buffer += seprintf(buffer, last, - " RAX: %.16I64X RBX: %.16I64X RCX: %.16I64X RDX: %.16I64X\n" - " RSI: %.16I64X RDI: %.16I64X RBP: %.16I64X RSP: %.16I64X\n" - " R8: %.16I64X R9: %.16I64X R10: %.16I64X R11: %.16I64X\n" - " R12: %.16I64X R13: %.16I64X R14: %.16I64X R15: %.16I64X\n" - " RIP: %.16I64X EFLAGS: %.8lX\n", + fmt::format_to(output_iterator, + " RAX: {:016X} RBX: {:016X} RCX: {:016X} RDX: {:016X}\n" + " RSI: {:016X} RDI: {:016X} RBP: {:016X} RSP: {:016X}\n" + " R8: {:016X} R9: {:016X} R10: {:016X} R11: {:016X}\n" + " R12: {:016X} R13: {:016X} R14: {:016X} R15: {:016X}\n" + " RIP: {:016X} EFLAGS: {:08X}\n", ep->ContextRecord->Rax, ep->ContextRecord->Rbx, ep->ContextRecord->Rcx, @@ -254,10 +243,10 @@ static char *PrintModuleInfo(char *outpu ep->ContextRecord->EFlags ); #elif defined(_M_IX86) - buffer += seprintf(buffer, last, - " EAX: %.8X EBX: %.8X ECX: %.8X EDX: %.8X\n" - " ESI: %.8X EDI: %.8X EBP: %.8X ESP: %.8X\n" - " EIP: %.8X EFLAGS: %.8X\n", + fmt::format_to(output_iterator, + " EAX: {:08X} EBX: {:08X} ECX: {:08X} EDX: {:08X}\n" + " ESI: {:08X} EDI: {:08X} EBP: {:08X} ESP: {:08X}\n" + " EIP: {:08X} EFLAGS: {:08X}\n", (int)ep->ContextRecord->Eax, (int)ep->ContextRecord->Ebx, (int)ep->ContextRecord->Ecx, @@ -270,15 +259,15 @@ static char *PrintModuleInfo(char *outpu (int)ep->ContextRecord->EFlags ); #elif defined(_M_ARM64) - buffer += seprintf(buffer, last, - " X0: %.16I64X X1: %.16I64X X2: %.16I64X X3: %.16I64X\n" - " X4: %.16I64X X5: %.16I64X X6: %.16I64X X7: %.16I64X\n" - " X8: %.16I64X X9: %.16I64X X10: %.16I64X X11: %.16I64X\n" - " X12: %.16I64X X13: %.16I64X X14: %.16I64X X15: %.16I64X\n" - " X16: %.16I64X X17: %.16I64X X18: %.16I64X X19: %.16I64X\n" - " X20: %.16I64X X21: %.16I64X X22: %.16I64X X23: %.16I64X\n" - " X24: %.16I64X X25: %.16I64X X26: %.16I64X X27: %.16I64X\n" - " X28: %.16I64X Fp: %.16I64X Lr: %.16I64X\n", + fmt::format_to(output_iterator, + " X0: {:016X} X1: {:016X} X2: {:016X} X3: {:016X}\n" + " X4: {:016X} X5: {:016X} X6: {:016X} X7: {:016X}\n" + " X8: {:016X} X9: {:016X} X10: {:016X} X11: {:016X}\n" + " X12: {:016X} X13: {:016X} X14: {:016X} X15: {:016X}\n" + " X16: {:016X} X17: {:016X} X18: {:016X} X19: {:016X}\n" + " X20: {:016X} X21: {:016X} X22: {:016X} X23: {:016X}\n" + " X24: {:016X} X25: {:016X} X26: {:016X} X27: {:016X}\n" + " X28: {:016X} Fp: {:016X} Lr: {:016X}\n", ep->ContextRecord->X0, ep->ContextRecord->X1, ep->ContextRecord->X2, @@ -313,7 +302,7 @@ static char *PrintModuleInfo(char *outpu ); #endif - buffer += seprintf(buffer, last, "\n Bytes at instruction pointer:\n"); + fmt::format_to(output_iterator, "\n Bytes at instruction pointer:\n"); #ifdef _M_AMD64 byte *b = (byte*)ep->ContextRecord->Rip; #elif defined(_M_IX86) @@ -323,18 +312,18 @@ static char *PrintModuleInfo(char *outpu #endif for (int i = 0; i != 24; i++) { if (IsBadReadPtr(b, 1)) { - buffer += seprintf(buffer, last, " ??"); // OCR: WAS: , 0); + fmt::format_to(output_iterator, " ??"); // OCR: WAS: , 0); } else { - buffer += seprintf(buffer, last, " %.2X", *b); + fmt::format_to(output_iterator, " {:02X}", *b); } b++; } - return buffer + seprintf(buffer, last, "\n\n"); + fmt::format_to(output_iterator, "\n\n"); } -/* virtual */ char *CrashLogWindows::LogStacktrace(char *buffer, const char *last) const +/* virtual */ void CrashLogWindows::LogStacktrace(std::back_insert_iterator &output_iterator) const { - buffer += seprintf(buffer, last, "Stack trace:\n"); + fmt::format_to(output_iterator, "Stack trace:\n"); #ifdef _M_AMD64 uint32 *b = (uint32*)ep->ContextRecord->Rsp; #elif defined(_M_IX86) @@ -345,15 +334,15 @@ static char *PrintModuleInfo(char *outpu for (int j = 0; j != 24; j++) { for (int i = 0; i != 8; i++) { if (IsBadReadPtr(b, sizeof(uint32))) { - buffer += seprintf(buffer, last, " ????????"); // OCR: WAS - , 0); + fmt::format_to(output_iterator, " ????????"); // OCR: WAS - , 0); } else { - buffer += seprintf(buffer, last, " %.8X", *b); + fmt::format_to(output_iterator, " {:08X}", *b); } b++; } - buffer += seprintf(buffer, last, "\n"); + fmt::format_to(output_iterator, "\n"); } - return buffer + seprintf(buffer, last, "\n"); + fmt::format_to(output_iterator, "\n"); } #if defined(_MSC_VER) @@ -364,7 +353,7 @@ static const uint MAX_FRAMES = 64; #include #pragma warning(default:4091) -char *CrashLogWindows::AppendDecodedStacktrace(char *buffer, const char *last) const +void CrashLogWindows::AppendDecodedStacktrace(std::back_insert_iterator &output_iterator) const { DllLoader dbghelp(L"dbghelp.dll"); struct ProcPtrs { @@ -389,7 +378,7 @@ char *CrashLogWindows::AppendDecodedStac dbghelp.GetProcAddress("SymGetLineFromAddr64"), }; - buffer += seprintf(buffer, last, "\nDecoded stack trace:\n"); + fmt::format_to(output_iterator, "\nDecoded stack trace:\n"); /* Try to load the functions from the DLL, if that fails because of a too old dbghelp.dll, just skip it. */ if (dbghelp.Success()) { @@ -440,7 +429,7 @@ char *CrashLogWindows::AppendDecodedStac hCur, GetCurrentThread(), &frame, &ctx, nullptr, proc.pSymFunctionTableAccess64, proc.pSymGetModuleBase64, nullptr)) break; if (frame.AddrPC.Offset == frame.AddrReturn.Offset) { - buffer += seprintf(buffer, last, " \n"); + fmt::format_to(output_iterator, " \n"); break; } @@ -454,27 +443,27 @@ char *CrashLogWindows::AppendDecodedStac } /* Print module and instruction pointer. */ - buffer += seprintf(buffer, last, "[%02d] %-20s " PRINTF_PTR, num, mod_name, frame.AddrPC.Offset); + fmt::format_to(output_iterator, "[{:02}] {:20s} {:X}", num, mod_name, frame.AddrPC.Offset); /* Get symbol name and line info if possible. */ DWORD64 offset; if (proc.pSymGetSymFromAddr64(hCur, frame.AddrPC.Offset, &offset, sym_info)) { - buffer += seprintf(buffer, last, " %s + %I64u", sym_info->Name, offset); + fmt::format_to(output_iterator, " {} + {}", sym_info->Name, offset); DWORD line_offs; IMAGEHLP_LINE64 line; line.SizeOfStruct = sizeof(IMAGEHLP_LINE64); if (proc.pSymGetLineFromAddr64(hCur, frame.AddrPC.Offset, &line_offs, &line)) { - buffer += seprintf(buffer, last, " (%s:%d)", line.FileName, line.LineNumber); + fmt::format_to(output_iterator, " ({}:{})", line.FileName, line.LineNumber); } } - buffer += seprintf(buffer, last, "\n"); + fmt::format_to(output_iterator, "\n"); } proc.pSymCleanup(hCur); } - return buffer + seprintf(buffer, last, "\n*** End of additional info ***\n"); + fmt::format_to(output_iterator, "\n*** End of additional info ***\n"); } /* virtual */ int CrashLogWindows::WriteCrashDump(char *filename, const char *filename_last) const @@ -498,8 +487,8 @@ char *CrashLogWindows::AppendDecodedStac MINIDUMP_USER_STREAM_INFORMATION musi; userstream.Type = LastReservedStream + 1; - userstream.Buffer = (void*)this->crashlog; - userstream.BufferSize = (ULONG)strlen(this->crashlog) + 1; + userstream.Buffer = (void*)this->crashlog.data(); + userstream.BufferSize = (ULONG)this->crashlog.size() + 1; musi.UserStreamCount = 1; musi.UserStreamArray = &userstream; @@ -559,10 +548,11 @@ static LONG WINAPI ExceptionHandler(EXCE CrashLogWindows *log = new CrashLogWindows(ep); CrashLogWindows::current = log; - char *buf = log->FillCrashLog(log->crashlog, lastof(log->crashlog)); + auto output_iterator = std::back_inserter(log->crashlog); + log->FillCrashLog(output_iterator); log->WriteCrashDump(log->crashdump_filename, lastof(log->crashdump_filename)); - log->AppendDecodedStacktrace(buf, lastof(log->crashlog)); - log->WriteCrashLog(log->crashlog, log->crashlog_filename, lastof(log->crashlog_filename)); + log->AppendDecodedStacktrace(output_iterator); + log->WriteCrashLog(log->crashlog.c_str(), log->crashlog_filename, lastof(log->crashlog_filename)); log->WriteScreenshot(log->screenshot_filename, lastof(log->screenshot_filename)); log->SendSurvey(); @@ -680,10 +670,10 @@ static INT_PTR CALLBACK CrashDialogFunc( /* We need to put the crash-log in a separate buffer because the default * buffer in MB_TO_WIDE is not large enough (512 chars) */ wchar_t filenamebuf[MAX_PATH * 2]; - wchar_t crash_msgW[lengthof(CrashLogWindows::current->crashlog)]; + wchar_t crash_msgW[65536]; /* Convert unix -> dos newlines because the edit box only supports that properly :( */ - const char *unix_nl = CrashLogWindows::current->crashlog; - char dos_nl[lengthof(CrashLogWindows::current->crashlog)]; + const char *unix_nl = CrashLogWindows::current->crashlog.data(); + char dos_nl[65536]; char *p = dos_nl; WChar c; while ((c = Utf8Consume(&unix_nl)) && p < lastof(dos_nl) - 4) { // 4 is max number of bytes per character