ImGuiTextBuffer: Avoid heap allocation when empty.
diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt
index 9999e8d..c989938 100644
--- a/docs/CHANGELOG.txt
+++ b/docs/CHANGELOG.txt
@@ -59,6 +59,7 @@
- BeginMenu(): Fixed menu popup horizontal offset being off the item in the menu bar when WindowPadding=0.0f.
- ArrowButton(): Fixed arrow shape being horizontally misaligned by (FramePadding.y-FramePadding.x) if they are different.
- Drag and Drop: Added GetDragDropPayload() to peek directly into the payload (if any) from anywhere. (#143)
+- ImGuiTextBuffer: Avoid heap allocation when empty.
- ImDrawList: Fixed AddConvexPolyFilled() undefined behavior when passing points_count smaller than 3,
in particular, points_count==0 could lead to a memory stomp if the draw list was previously empty.
- Examples: DirectX10, DirectX11: Removed seemingly unnecessary calls to invalidate and recreate device objects
diff --git a/imgui.cpp b/imgui.cpp
index ba92323..aef64e0 100644
--- a/imgui.cpp
+++ b/imgui.cpp
@@ -1956,6 +1956,8 @@
#endif
#endif
+char ImGuiTextBuffer::EmptyString[1] = { 0 };
+
// Helper: Text buffer for logging/accumulating text
void ImGuiTextBuffer::appendfv(const char* fmt, va_list args)
{
@@ -1969,7 +1971,8 @@
return;
}
- const int write_off = Buf.Size;
+ // Add zero-terminator the first time
+ const int write_off = (Buf.Size != 0) ? Buf.Size : 1;
const int needed_sz = write_off + len;
if (write_off + len >= Buf.Capacity)
{
diff --git a/imgui.h b/imgui.h
index f4e8996..7e3ebcf 100644
--- a/imgui.h
+++ b/imgui.h
@@ -1366,25 +1366,27 @@
int CountGrep;
};
-// Helper: Text buffer for logging/accumulating text
+// Helper: Growable text buffer for logging/accumulating text
+// (this could be called 'ImGuiTextBuilder' / 'ImGuiStringBuilder')
struct ImGuiTextBuffer
{
ImVector<char> Buf;
+ static char EmptyString[1];
- ImGuiTextBuffer() { Buf.push_back(0); }
- inline char operator[](int i) { return Buf.Data[i]; }
- const char* begin() const { return &Buf.front(); }
- const char* end() const { return &Buf.back(); } // Buf is zero-terminated, so end() will point on the zero-terminator
- int size() const { return Buf.Size - 1; }
- bool empty() { return Buf.Size <= 1; }
- void clear() { Buf.clear(); Buf.push_back(0); }
- void reserve(int capacity) { Buf.reserve(capacity); }
- const char* c_str() const { return Buf.Data; }
+ ImGuiTextBuffer() { }
+ inline char operator[](int i) { IM_ASSERT(Buf.Data != NULL); return Buf.Data[i]; }
+ const char* begin() const { return Buf.Data ? &Buf.front() : EmptyString; }
+ const char* end() const { return Buf.Data ? &Buf.back() : EmptyString; } // Buf is zero-terminated, so end() will point on the zero-terminator
+ int size() const { return Buf.Data ? Buf.Size - 1 : 0; }
+ bool empty() { return Buf.Size <= 1; }
+ void clear() { Buf.clear(); }
+ void reserve(int capacity) { Buf.reserve(capacity); }
+ const char* c_str() const { return Buf.Data ? Buf.Data : EmptyString; }
IMGUI_API void appendf(const char* fmt, ...) IM_FMTARGS(2);
IMGUI_API void appendfv(const char* fmt, va_list args) IM_FMTLIST(2);
};
-// Helper: key->value storage
+// Helper: Key->Value storage
// Typically you don't have to worry about this since a storage is held within each Window.
// We use it to e.g. store collapse state for a tree (Int 0/1)
// This is optimized for efficient lookup (dichotomy into a contiguous buffer) and rare insertion (typically tied to user interactions aka max once a frame)