Skip to content

Commit 7cdea48

Browse files
committed
Updated Str helper to return a more standard value for capacity() - used when forward to InputText() buf_size fields.
1 parent 3e9a721 commit 7cdea48

File tree

4 files changed

+72
-53
lines changed

4 files changed

+72
-53
lines changed

imgui_test_engine/imgui_te_utils.cpp

+8-5
Original file line numberDiff line numberDiff line change
@@ -452,7 +452,7 @@ void ImStrReplace(Str* s, const char* find, const char* repl)
452452
if (repl_diff > 0)
453453
{
454454
num_matches = 0;
455-
need_capacity = s->length() + 1;
455+
need_capacity = s->length();
456456
for (char* p = s->c_str(), *end = s->c_str() + s->length(); p != NULL && p < end;)
457457
{
458458
p = (char*)ImStrStr(p, end - p, find, find_len);
@@ -665,11 +665,14 @@ void ImTimestampToISO8601(uint64_t timestamp, Str* out_date)
665665
time_t unix_time = (time_t)(timestamp / 1000000); // Convert to seconds.
666666
tm* time = gmtime(&unix_time);
667667
const char* time_format = "%Y-%m-%dT%H:%M:%S";
668-
size_t size_req = strftime(out_date->c_str(), out_date->capacity(), time_format, time);
669-
if (size_req >= (size_t)out_date->capacity())
668+
669+
// max_size "maximum number of characters to be copied to ptr, including the terminating null-character."
670+
// return "returns the total number of characters copied to ptr (not including the terminating null-character)"
671+
size_t size_req = strftime(out_date->c_str(), out_date->capacity() + 1, time_format, time);
672+
if (size_req > (size_t)out_date->capacity())
670673
{
671674
out_date->reserve((int)size_req);
672-
strftime(out_date->c_str(), out_date->capacity(), time_format, time);
675+
strftime(out_date->c_str(), out_date->capacity() + 1, time_format, time);
673676
}
674677
}
675678

@@ -1061,7 +1064,7 @@ static int InputTextCallbackStr(ImGuiInputTextCallbackData* data)
10611064
// If for some reason we refuse the new length (BufTextLen) and/or capacity (BufSize) we need to set them back to what we want.
10621065
Str* str = user_data->StrObj;
10631066
IM_ASSERT(data->Buf == str->c_str());
1064-
str->reserve(data->BufTextLen + 1);
1067+
str->reserve(data->BufTextLen);
10651068
data->Buf = (char*)str->c_str();
10661069
}
10671070
else if (user_data->ChainCallback)

imgui_test_engine/thirdparty/Str/Str.h

+46-44
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Str v0.32
1+
// Str v0.33
22
// Simple C++ string type with an optional local buffer, by Omar Cornut
33
// https://github.com/ocornut/str
44

@@ -70,6 +70,7 @@ All StrXXX types derives from Str and instance hold the local buffer capacity. S
7070

7171
/*
7272
CHANGELOG
73+
0.33 - fixed capacity() return value to match standard. e.g. a Str256's capacity() now returns 255, not 256.
7374
0.32 - added owned() accessor.
7475
0.31 - fixed various warnings.
7576
0.30 - turned into a single header file, removed Str.cpp.
@@ -138,7 +139,7 @@ TODO
138139
class STR_API Str
139140
{
140141
char* Data; // Point to LocalBuf() or heap allocated
141-
int Capacity : 21; // Max 2 MB
142+
int Capacity : 21; // Max 2 MB. Exclude zero terminator.
142143
int LocalBufSize : 10; // Max 1023 bytes
143144
unsigned int Owned : 1; // Set when we have ownership of the pointed data (most common, unless using set_ref() method or StrRef constructor)
144145

@@ -212,7 +213,7 @@ class STR_API Str
212213
STR_ASSERT(local_buf_size < 1024);
213214
Data = local_buf();
214215
Data[0] = '\0';
215-
Capacity = local_buf_size;
216+
Capacity = local_buf_size ? local_buf_size - 1 : 0;
216217
LocalBufSize = local_buf_size;
217218
Owned = 1;
218219
}
@@ -226,40 +227,40 @@ void Str::set(const char* src)
226227
clear();
227228
return;
228229
}
229-
int buf_len = (int)strlen(src)+1;
230+
int buf_len = (int)strlen(src);
230231
if (Capacity < buf_len)
231232
reserve_discard(buf_len);
232-
memcpy(Data, src, (size_t)buf_len);
233+
memcpy(Data, src, (size_t)(buf_len + 1));
233234
Owned = 1;
234235
}
235236

236237
void Str::set(const char* src, const char* src_end)
237238
{
238239
STR_ASSERT(src != NULL && src_end >= src);
239-
int buf_len = (int)(src_end-src)+1;
240+
int buf_len = (int)(src_end - src);
240241
if ((int)Capacity < buf_len)
241242
reserve_discard(buf_len);
242-
memcpy(Data, src, (size_t)(buf_len - 1));
243-
Data[buf_len-1] = 0;
243+
memcpy(Data, src, (size_t)buf_len);
244+
Data[buf_len] = 0;
244245
Owned = 1;
245246
}
246247

247248
void Str::set(const Str& src)
248249
{
249-
int buf_len = (int)strlen(src.c_str())+1;
250+
int buf_len = (int)strlen(src.c_str());
250251
if ((int)Capacity < buf_len)
251252
reserve_discard(buf_len);
252-
memcpy(Data, src.c_str(), (size_t)buf_len);
253+
memcpy(Data, src.c_str(), (size_t)(buf_len + 1));
253254
Owned = 1;
254255
}
255256

256257
#if STR_SUPPORT_STD_STRING
257258
void Str::set(const std::string& src)
258259
{
259-
int buf_len = (int)src.length()+1;
260+
int buf_len = (int)src.length();
260261
if ((int)Capacity < buf_len)
261262
reserve_discard(buf_len);
262-
memcpy(Data, src.c_str(), (size_t)buf_len);
263+
memcpy(Data, src.c_str(), (size_t)(buf_len + 1));
263264
Owned = 1;
264265
}
265266
#endif
@@ -413,7 +414,7 @@ void Str::clear()
413414
{
414415
Data = local_buf();
415416
Data[0] = '\0';
416-
Capacity = LocalBufSize;
417+
Capacity = LocalBufSize - 1;
417418
Owned = 1;
418419
}
419420
else
@@ -425,31 +426,32 @@ void Str::clear()
425426
}
426427

427428
// Reserve memory, preserving the current of the buffer
429+
// Capacity doesn't include the zero terminator, so reserve(5) is enough to store "hello".
428430
void Str::reserve(int new_capacity)
429431
{
430432
if (new_capacity <= Capacity)
431433
return;
432434

433435
char* new_data;
434-
if (new_capacity < LocalBufSize)
436+
if (new_capacity <= LocalBufSize - 1)
435437
{
436438
// Disowned -> LocalBuf
437439
new_data = local_buf();
438-
new_capacity = LocalBufSize;
440+
new_capacity = LocalBufSize - 1;
439441
}
440442
else
441443
{
442444
// Disowned or LocalBuf -> Heap
443-
new_data = (char*)STR_MEMALLOC((size_t)new_capacity * sizeof(char));
445+
new_data = (char*)STR_MEMALLOC((size_t)(new_capacity + 1) * sizeof(char));
444446
}
445447

446448
// string in Data might be longer than new_capacity if it wasn't owned, don't copy too much
447449
#ifdef _MSC_VER
448-
strncpy_s(new_data, (size_t)new_capacity, Data, (size_t)new_capacity - 1);
450+
strncpy_s(new_data, (size_t)new_capacity + 1, Data, (size_t)new_capacity);
449451
#else
450-
strncpy(new_data, Data, (size_t)new_capacity - 1);
452+
strncpy(new_data, Data, (size_t)new_capacity);
451453
#endif
452-
new_data[new_capacity - 1] = 0;
454+
new_data[new_capacity] = 0;
453455

454456
if (Owned && !is_using_local_buf())
455457
STR_MEMFREE(Data);
@@ -468,16 +470,16 @@ void Str::reserve_discard(int new_capacity)
468470
if (Owned && !is_using_local_buf())
469471
STR_MEMFREE(Data);
470472

471-
if (new_capacity < LocalBufSize)
473+
if (new_capacity <= LocalBufSize - 1)
472474
{
473475
// Disowned -> LocalBuf
474476
Data = local_buf();
475-
Capacity = LocalBufSize;
477+
Capacity = LocalBufSize - 1;
476478
}
477479
else
478480
{
479481
// Disowned or LocalBuf -> Heap
480-
Data = (char*)STR_MEMALLOC((size_t)new_capacity * sizeof(char));
482+
Data = (char*)STR_MEMALLOC((size_t)(new_capacity + 1) * sizeof(char));
481483
Capacity = new_capacity;
482484
}
483485
Owned = 1;
@@ -487,12 +489,12 @@ void Str::shrink_to_fit()
487489
{
488490
if (!Owned || is_using_local_buf())
489491
return;
490-
int new_capacity = length() + 1;
492+
int new_capacity = length();
491493
if (Capacity <= new_capacity)
492494
return;
493495

494-
char* new_data = (char*)STR_MEMALLOC((size_t)new_capacity * sizeof(char));
495-
memcpy(new_data, Data, (size_t)new_capacity);
496+
char* new_data = (char*)STR_MEMALLOC((size_t)(new_capacity + 1) * sizeof(char));
497+
memcpy(new_data, Data, (size_t)(new_capacity + 1));
496498
STR_MEMFREE(Data);
497499
Data = new_data;
498500
Capacity = new_capacity;
@@ -511,17 +513,17 @@ int Str::setfv(const char* fmt, va_list args)
511513
int len = vsnprintf(NULL, 0, fmt, args);
512514
STR_ASSERT(len >= 0);
513515

514-
if (Capacity < len + 1)
515-
reserve_discard(len + 1);
516-
len = vsnprintf(Data, len + 1, fmt, args2);
516+
if (Capacity < len)
517+
reserve_discard(len);
518+
len = vsnprintf(Data, (size_t)len + 1, fmt, args2);
517519
#else
518520
// First try
519-
int len = vsnprintf(Owned ? Data : NULL, Owned ? (size_t)Capacity : 0, fmt, args);
521+
int len = vsnprintf(Owned ? Data : NULL, Owned ? (size_t)(Capacity + 1): 0, fmt, args);
520522
STR_ASSERT(len >= 0);
521523

522-
if (Capacity < len + 1)
524+
if (Capacity < len)
523525
{
524-
reserve_discard(len + 1);
526+
reserve_discard(len);
525527
len = vsnprintf(Data, (size_t)len + 1, fmt, args2);
526528
}
527529
#endif
@@ -546,10 +548,10 @@ int Str::setfv_nogrow(const char* fmt, va_list args)
546548
if (Capacity == 0)
547549
return 0;
548550

549-
int w = vsnprintf(Data, (size_t)Capacity, fmt, args);
550-
Data[Capacity - 1] = 0;
551+
int w = vsnprintf(Data, (size_t)(Capacity + 1), fmt, args);
552+
Data[Capacity] = 0;
551553
Owned = 1;
552-
return (w == -1) ? Capacity - 1 : w;
554+
return (w == -1) ? Capacity : w;
553555
}
554556

555557
int Str::setf_nogrow(const char* fmt, ...)
@@ -564,8 +566,8 @@ int Str::setf_nogrow(const char* fmt, ...)
564566
int Str::append_from(int idx, char c)
565567
{
566568
int add_len = 1;
567-
if (Capacity < idx + add_len + 1)
568-
reserve(idx + add_len + 1);
569+
if (Capacity < idx + add_len)
570+
reserve(idx + add_len);
569571
Data[idx] = c;
570572
Data[idx + add_len] = 0;
571573
STR_ASSERT(Owned);
@@ -577,10 +579,10 @@ int Str::append_from(int idx, const char* s, const char* s_end)
577579
if (!s_end)
578580
s_end = s + strlen(s);
579581
int add_len = (int)(s_end - s);
580-
if (Capacity < idx + add_len + 1)
581-
reserve(idx + add_len + 1);
582+
if (Capacity < idx + add_len)
583+
reserve(idx + add_len);
582584
memcpy(Data + idx, (const void*)s, (size_t)add_len);
583-
Data[idx + add_len] = 0; // Our source data isn't necessarily zero-terminated
585+
Data[idx + add_len] = 0; // Our source data isn't necessarily zero terminated
584586
STR_ASSERT(Owned);
585587
return add_len;
586588
}
@@ -598,17 +600,17 @@ int Str::appendfv_from(int idx, const char* fmt, va_list args)
598600
int add_len = vsnprintf(NULL, 0, fmt, args);
599601
STR_ASSERT(add_len >= 0);
600602

601-
if (Capacity < idx + add_len + 1)
602-
reserve(idx + add_len + 1);
603+
if (Capacity < idx + add_len)
604+
reserve(idx + add_len);
603605
add_len = vsnprintf(Data + idx, add_len + 1, fmt, args2);
604606
#else
605607
// First try
606-
int add_len = vsnprintf(Owned ? Data + idx : NULL, Owned ? (size_t)(Capacity - idx) : 0, fmt, args);
608+
int add_len = vsnprintf(Owned ? Data + idx : NULL, Owned ? (size_t)(Capacity + 1 - idx) : 0, fmt, args);
607609
STR_ASSERT(add_len >= 0);
608610

609-
if (Capacity < idx + add_len + 1)
611+
if (Capacity < idx + add_len)
610612
{
611-
reserve(idx + add_len + 1);
613+
reserve(idx + add_len);
612614
add_len = vsnprintf(Data + idx, (size_t)add_len + 1, fmt, args2);
613615
}
614616
#endif

imgui_test_suite/imgui_tests_core.cpp

+14
Original file line numberDiff line numberDiff line change
@@ -4908,6 +4908,20 @@ void RegisterTests_Misc(ImGuiTestEngine* e)
49084908
};
49094909
#endif
49104910

4911+
// ## Various tests for Str helper class
4912+
t = IM_REGISTER_TEST(e, "misc", "misc_str");
4913+
t->TestFunc = [](ImGuiTestContext* ctx)
4914+
{
4915+
Str16 text;
4916+
IM_CHECK(text.capacity() == 15);
4917+
text.set("0123456789ABCDE");
4918+
IM_CHECK(text.length() == 15);
4919+
IM_CHECK(text.capacity() == 15);
4920+
text.append("F");
4921+
IM_CHECK(text.length() == 16);
4922+
IM_CHECK(text.capacity() >= 16);
4923+
};
4924+
49114925
// ## Test ImStrReplace() and ImStrXmlEscape().
49124926
t = IM_REGISTER_TEST(e, "misc", "misc_str_replace");
49134927
t->TestFunc = [](ImGuiTestContext* ctx)

imgui_test_suite/imgui_tests_widgets_inputtext.cpp

+4-4
Original file line numberDiff line numberDiff line change
@@ -1218,13 +1218,13 @@ void RegisterTests_WidgetsInputText(ImGuiTestEngine* e)
12181218
ImGui::Begin("Test Window", NULL, ImGuiWindowFlags_NoSavedSettings);
12191219
if (ImGui::InputText("Field1", &vars.str, ImGuiInputTextFlags_EnterReturnsTrue))
12201220
{
1221-
IM_CHECK_EQ(vars.str.capacity(), 4 + 5 + 1);
1221+
IM_CHECK_EQ(vars.str.capacity(), 4 + 5);
12221222
IM_CHECK_STR_EQ(vars.str.c_str(), "abcdhello");
12231223
}
12241224
Str str_local_unsaved = "abcd";
12251225
if (ImGui::InputText("Field2", &str_local_unsaved, ImGuiInputTextFlags_EnterReturnsTrue))
12261226
{
1227-
IM_CHECK_EQ(str_local_unsaved.capacity(), 4 + 5 + 1);
1227+
IM_CHECK_EQ(str_local_unsaved.capacity(), 4 + 5);
12281228
IM_CHECK_STR_EQ(str_local_unsaved.c_str(), "abcdhello");
12291229
}
12301230
ImGui::End();
@@ -1234,7 +1234,7 @@ void RegisterTests_WidgetsInputText(ImGuiTestEngine* e)
12341234
{
12351235
StrVars& vars = ctx->GetVars<StrVars>();
12361236
vars.str.set("abcd");
1237-
IM_CHECK_EQ(vars.str.capacity(), 4 + 1);
1237+
IM_CHECK_EQ(vars.str.capacity(), 4);
12381238
ctx->SetRef("Test Window");
12391239
ctx->ItemInput("Field1");
12401240
ctx->KeyCharsAppendEnter("hello");
@@ -1265,7 +1265,7 @@ void RegisterTests_WidgetsInputText(ImGuiTestEngine* e)
12651265

12661266
StrVars& vars = ctx->GetVars<StrVars>();
12671267
ImGui::Begin("Test Window", NULL, ImGuiWindowFlags_NoSavedSettings);
1268-
ImGui::InputText("Field1", vars.str.c_str(), vars.str.capacity(), ImGuiInputTextFlags_CallbackHistory | ImGuiInputTextFlags_CallbackResize, callback, (void*)&vars.str);
1268+
ImGui::InputText("Field1", vars.str.c_str(), vars.str.capacity() + 1, ImGuiInputTextFlags_CallbackHistory | ImGuiInputTextFlags_CallbackResize, callback, (void*)&vars.str);
12691269
ImGui::End();
12701270
};
12711271
t->TestFunc = [](ImGuiTestContext* ctx)

0 commit comments

Comments
 (0)