Skip to content

Commit ed2b442

Browse files
authored
Fix(runner): thread array does not resize even if allocated (#144)
`runner_impl` uses `set` which does not support resize; therefore, resize logic was added iff the thread array is full. fixes: #142
1 parent 6913c51 commit ed2b442

File tree

11 files changed

+200
-44
lines changed

11 files changed

+200
-44
lines changed

CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ set(CMAKE_INSTALL_LIBRARY_DIR lib)
4848
set(CMAKE_INSTALL_INCLUDE_DIR include)
4949
if(MSVC)
5050
# disable: aligment problems non explicit switch case
51-
add_compile_options(/W4 /wd4820 /wd4061)
51+
add_compile_options(/W4 /wd4820 /wd4061 -D_CRT_SECURE_NO_WARNINGS)
5252
else()
5353
add_compile_options(-Wall -Wextra -Wpedantic)
5454
endif()

inkcpp/array.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -464,7 +464,7 @@ class fixed_restorable_array : public basic_restorable_array<T>
464464
};
465465

466466
template<typename T>
467-
class allocated_restorable_array final : public basic_restorable_array<T>
467+
class allocated_restorable_array : public basic_restorable_array<T>
468468
{
469469
using base = basic_restorable_array<T>;
470470

inkcpp/runner_impl.h

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -273,7 +273,14 @@ class runner_impl
273273
_threadDone.forget();
274274
}
275275

276-
void set(size_t index, const ip_t& value) { _threadDone.set(index, value); }
276+
void set(size_t index, const ip_t& value)
277+
{
278+
if (index >= _threadDone.capacity()) {
279+
inkAssert(index == _threadDone.capacity(), "Threads should only be created incremental");
280+
_threadDone.resize(static_cast<size_t>(_threadDone.capacity() * 1.5));
281+
}
282+
_threadDone.set(index, value);
283+
}
277284

278285
const ip_t& get(size_t index) const { return _threadDone.get(index); }
279286

inkcpp_c/include/inkcpp.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
#include <stdbool.h>
55
#include <stdint.h>
6-
#include <stdlib.h>
6+
#include <stddef.h>
77

88
#ifdef __cplusplus
99
extern "C" {

inkcpp_c/inkcpp.cpp

Lines changed: 21 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -74,13 +74,16 @@ extern "C" {
7474
FILE* file = fopen(filename, "rb");
7575
fseek(file, 0, SEEK_END);
7676
long file_length = ftell(file);
77+
if (file_length < 0) {
78+
return NULL;
79+
}
7780
fseek(file, 0, SEEK_SET);
7881
unsigned char* data = static_cast<unsigned char*>(malloc(file_length));
7982
inkAssert(data, "Malloc of size %u failed", file_length);
80-
unsigned length = fread(data, sizeof(unsigned char), file_length, file);
83+
size_t length = fread(data, sizeof(unsigned char), file_length, file);
8184
inkAssert(
82-
file_length == length, "Expected to read file of size %u, but only read %u", file_length,
83-
length
85+
static_cast<size_t>(file_length) == length,
86+
"Expected to read file of size %u, but only read %u", file_length, length
8487
);
8588
fclose(file);
8689
return reinterpret_cast<HInkStory*>(story::from_binary(data, file_length));
@@ -91,13 +94,16 @@ extern "C" {
9194
FILE* file = fopen(filename, "rb");
9295
fseek(file, 0, SEEK_END);
9396
long file_length = ftell(file);
97+
if (file_length < 0) {
98+
return NULL;
99+
}
94100
fseek(file, 0, SEEK_SET);
95101
unsigned char* data = static_cast<unsigned char*>(malloc(file_length));
96102
inkAssert(data, "Malloc of size %u failed", file_length);
97-
unsigned length = fread(data, sizeof(unsigned char), file_length, file);
103+
size_t length = fread(data, sizeof(unsigned char), file_length, file);
98104
inkAssert(
99-
file_length == length, "Expected to read file of size %u, but only read %u", file_length,
100-
length
105+
static_cast<size_t>(file_length) == length,
106+
"Expected to read file of size %u, but only read %u", file_length, length
101107
);
102108
fclose(file);
103109
return reinterpret_cast<HInkSnapshot*>(snapshot::from_binary(data, file_length));
@@ -107,7 +113,7 @@ extern "C" {
107113
{
108114
FILE* file = fopen(filename, "wb");
109115
const snapshot& snap = *reinterpret_cast<const snapshot*>(self);
110-
unsigned length = fwrite(snap.get_data(), sizeof(unsigned char), snap.get_data_len(), file);
116+
size_t length = fwrite(snap.get_data(), sizeof(unsigned char), snap.get_data_len(), file);
111117
inkAssert(
112118
length == snap.get_data_len(),
113119
"Snapshot write failed, snapshot of size %u, but only %u bytes where written.",
@@ -119,13 +125,17 @@ extern "C" {
119125

120126
HInkStory* ink_story_from_binary(const unsigned char* data, size_t length, bool freeOnDestroy)
121127
{
122-
return reinterpret_cast<HInkStory*>(story::from_binary(data, length, freeOnDestroy));
128+
return reinterpret_cast<HInkStory*>(
129+
story::from_binary(data, static_cast<ink::size_t>(length), freeOnDestroy)
130+
);
123131
}
124132

125133
HInkSnapshot*
126134
ink_snapshot_from_binary(const unsigned char* data, size_t length, bool freeOnDestroy)
127135
{
128-
return reinterpret_cast<HInkSnapshot*>(snapshot::from_binary(data, length, freeOnDestroy));
136+
return reinterpret_cast<HInkSnapshot*>(
137+
snapshot::from_binary(data, static_cast<ink::size_t>(length), freeOnDestroy)
138+
);
129139
}
130140

131141
void ink_snapshot_get_binary(
@@ -291,7 +301,7 @@ extern "C" {
291301
function_name,
292302
[callback](size_t len, const value* vals) {
293303
InkValue* c_vals = reinterpret_cast<InkValue*>(const_cast<value*>(vals));
294-
int c_len = len;
304+
int c_len = static_cast<int>(len);
295305
for (int i = 0; i < c_len; ++i) {
296306
c_vals[i] = inkvar_to_c(const_cast<value&>(vals[i]));
297307
}
@@ -310,7 +320,7 @@ extern "C" {
310320
function_name,
311321
[callback](size_t len, const value* vals) -> value {
312322
InkValue* c_vals = reinterpret_cast<InkValue*>(const_cast<value*>(vals));
313-
int c_len = len;
323+
int c_len = static_cast<int>(len);
314324
for (int i = 0; i < c_len; ++i) {
315325
c_vals[i] = inkvar_to_c(const_cast<value&>(vals[i]));
316326
}

inkcpp_c/tests/ExternalFunction.c

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,17 +17,18 @@ InkValue my_sqrt(int argc, const InkValue argv[])
1717
InkValue v = argv[0];
1818
switch (v.type) {
1919
case ValueTypeFloat: v.float_v = sqrtf(v.float_v); break;
20-
case ValueTypeInt32: v.int32_v = sqrt(v.int32_v); break;
21-
case ValueTypeUint32: v.uint32_v = sqrtf(v.uint32_v); break;
20+
case ValueTypeInt32: v.int32_v = ( int32_t ) sqrt(v.int32_v); break;
21+
case ValueTypeUint32: v.uint32_v = ( uint32_t ) sqrt(v.uint32_v); break;
2222
default: assert(0);
2323
}
2424
return v;
2525
}
2626

2727
int cnt_greeting = 0;
2828

29-
InkValue greeting(int argc, const InkValue argv[])
29+
InkValue greeting(int argc, const InkValue* argv)
3030
{
31+
( void ) argv;
3132
cnt_greeting += 1;
3233
assert(argc == 0);
3334
InkValue v;
@@ -38,6 +39,8 @@ InkValue greeting(int argc, const InkValue argv[])
3839

3940
int main(int argc, const char* argv[])
4041
{
42+
( void ) argc;
43+
( void ) argv;
4144
HInkStory* story = ink_story_from_file(INK_TEST_RESOURCE_DIR "FallBack.bin");
4245
HInkRunner* runner = ink_story_new_runner(story, NULL);
4346
ink_runner_bind(runner, "greeting", greeting, 0);

inkcpp_test/Array.cpp

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,11 @@ SCENARIO("a restorable array can hold values", "[array]")
1212
GIVEN("an empty array")
1313
{
1414
const ink::size_t length = 10;
15-
test_array array = test_array(length, 0, ~0);
15+
test_array array = test_array(length, 0U, ~0U);
1616

1717
THEN("the default values should be zero")
1818
{
19-
for (ink::size_t i = 0; i < length; i++)
20-
{
19+
for (ink::size_t i = 0; i < length; i++) {
2120
REQUIRE(array[i] == 0);
2221
}
2322
}
@@ -26,15 +25,11 @@ SCENARIO("a restorable array can hold values", "[array]")
2625
{
2726
array.set(3, 15);
2827

29-
THEN("the value should be set")
30-
{
31-
REQUIRE(array[3] == 15);
32-
}
28+
THEN("the value should be set") { REQUIRE(array[3] == 15); }
3329

3430
THEN("the other values should be zero still")
3531
{
36-
for (ink::size_t i = 0; i < length; i++)
37-
{
32+
for (ink::size_t i = 0; i < length; i++) {
3833
if (i == 3)
3934
continue;
4035

@@ -50,7 +45,7 @@ SCENARIO("a restorable array can save/restore/forget", "[array]")
5045
GIVEN("a saved array with a few values")
5146
{
5247
// Load up the array
53-
test_array array = test_array(5, 0, ~0);
48+
test_array array = test_array(5, 0U, ~0U);
5449
array.set(0, 0);
5550
array.set(1, 1);
5651
array.set(2, 2);

inkcpp_test/Callstack.cpp

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ SCENARIO("threading with the callstack", "[callstack]")
5858

5959
WHEN("we collapse to the main thraed")
6060
{
61-
stack.collapse_to_thread(~0);
61+
stack.collapse_to_thread(~0U);
6262

6363
THEN("we should have the value from the original thread")
6464
{
@@ -91,7 +91,7 @@ SCENARIO("threading with the callstack", "[callstack]")
9191

9292
THEN("we can still collapse to the main thread")
9393
{
94-
stack.collapse_to_thread(~0);
94+
stack.collapse_to_thread(~0U);
9595
REQUIRE(stack.get(X)->get<value_type::int32>() == 100);
9696
REQUIRE(stack.get(Y)->get<value_type::int32>() == 200);
9797
}
@@ -140,7 +140,7 @@ SCENARIO("threading with the callstack", "[callstack]")
140140

141141
WHEN("we collapse to the main thraed")
142142
{
143-
stack.collapse_to_thread(~0);
143+
stack.collapse_to_thread(~0U);
144144

145145
THEN("we should have the value from the original thread")
146146
{
@@ -214,7 +214,7 @@ SCENARIO("threading with the callstack", "[callstack]")
214214

215215
WHEN("we collapse back to the main thread")
216216
{
217-
stack.collapse_to_thread(~0);
217+
stack.collapse_to_thread(~0U);
218218
THEN("the stack should be inside the tunnel")
219219
{
220220
REQUIRE(stack.get(X)->type() == value_type::int32);
@@ -225,13 +225,13 @@ SCENARIO("threading with the callstack", "[callstack]")
225225

226226
WHEN("we do a tunnel return")
227227
{
228-
frame_type type;
229-
auto offset = stack.pop_frame(&type, eval_mode);
228+
frame_type ftype;
229+
auto foffset = stack.pop_frame(&ftype, eval_mode);
230230

231231
THEN("we should be back outside")
232232
{
233-
REQUIRE(type == frame_type::tunnel);
234-
REQUIRE(offset == 505);
233+
REQUIRE(ftype == frame_type::tunnel);
234+
REQUIRE(foffset == 505);
235235
REQUIRE(stack.get(X)->type() == value_type::int32);
236236
REQUIRE(stack.get(X)->get<value_type::int32>() == 100);
237237
REQUIRE(stack.get(Y)->type() == value_type::int32);

inkcpp_test/FallbackFunction.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ SCENARIO("run a story with external function and fallback function", "[external
2222
int cnt_sqrt = 0;
2323
auto fn_sqrt = [&cnt_sqrt](int x) -> int {
2424
++cnt_sqrt;
25-
return sqrt(x);
25+
return static_cast<int>(sqrt(x));
2626
};
2727
int cnt_greeting = 0;
2828
auto fn_greeting = [&cnt_greeting]() -> const char* {
@@ -51,7 +51,7 @@ SCENARIO("run a story with external function and fallback function", "[external
5151
int cnt_sqrt = 0;
5252
auto fn_sqrt = [&cnt_sqrt](int x) -> int {
5353
++cnt_sqrt;
54-
return sqrt(x);
54+
return static_cast<int>(sqrt(x));
5555
};
5656

5757
thread->bind("sqrt", fn_sqrt);

inkcpp_test/Fixes.cpp

Lines changed: 52 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,8 @@ SCENARIO("unknown command _ #109", "[fixes]")
6363
for (size_t i = 0; i < out_str.size(); ++i) {
6464
data[i] = out_str[i];
6565
}
66-
std::unique_ptr<story> ink{story::from_binary(data, out_str.size())};
66+
std::unique_ptr<story> ink{story::from_binary(data, static_cast<ink::size_t>(out_str.size()))
67+
};
6768
globals globStore = ink->new_globals();
6869
runner main = ink->new_runner(globStore);
6970
std::string story = main->getall();
@@ -158,17 +159,18 @@ SCENARIO(
158159
{
159160
thread->getall();
160161
std::unique_ptr<snapshot> snap{thread->create_snapshot()};
161-
runner thread = ink->new_runner_from_snapshot(*snap);
162+
runner thread2 = ink->new_runner_from_snapshot(*snap);
162163
const size_t s = reinterpret_cast<internal::snapshot_impl*>(snap.get())->strings().size();
163164
THEN("loading it again will not change the string_table size")
164165
{
165-
runner thread2 = ink->new_runner_from_snapshot(*snap);
166+
runner thread3 = ink->new_runner_from_snapshot(*snap);
166167
const size_t s2 = reinterpret_cast<internal::snapshot_impl*>(snap.get())->strings().size();
167168
REQUIRE(s == s2);
168169
}
169170
}
170171
}
171172
}
173+
172174
SCENARIO("Casting during redefinition is too strict _ #134", "[fixes]")
173175
{
174176
GIVEN("story with problematic text")
@@ -226,9 +228,9 @@ SCENARIO("Using knot visit count as condition _ #139", "[fixes]")
226228
WHEN("go to 'one' twice")
227229
{
228230
thread->choose(1);
229-
std::string content = thread->getall();
231+
std::string content2 = thread->getall();
230232
REQUIRE(thread->num_choices() == 3);
231-
THEN("get both one strings") { REQUIRE(content == "Check\nBeen here before\n"); }
233+
THEN("get both one strings") { REQUIRE(content2 == "Check\nBeen here before\n"); }
232234
}
233235
}
234236
}
@@ -248,4 +250,48 @@ SCENARIO("Using knot visit count as condition _ #139", "[fixes]")
248250
}
249251
}
250252
}
251-
}
253+
}
254+
255+
SCENARIO("Provoke thread array expension _ #142", "[fixes]")
256+
{
257+
GIVEN("story with 15 threads in one know")
258+
{
259+
std::unique_ptr<story> ink{story::from_file(INK_TEST_RESOURCE_DIR "142_many_threads.bin")};
260+
runner thread = ink->new_runner();
261+
WHEN("just go to choice")
262+
{
263+
std::string content = thread->getall();
264+
REQUIRE(content == "At the top\n");
265+
THEN("expect to see 15 choices")
266+
{
267+
REQUIRE(thread->num_choices() == 15);
268+
const char options[] = "abcdefghijklmno";
269+
for (const char* c = options; *c; ++c) {
270+
CHECK(thread->get_choice(static_cast<size_t>(c - options))->text()[0] == *c);
271+
}
272+
}
273+
}
274+
WHEN("choose 5 options")
275+
{
276+
std::string content = thread->getall();
277+
for (int i = 0; i < 5; ++i) {
278+
REQUIRE_FALSE(thread->can_continue());
279+
thread->choose(i);
280+
content += thread->getall();
281+
}
282+
REQUIRE(
283+
content
284+
== "At the top\na\nAt the top\nc\nAt the top\ne\nAt the top\ng\nAt the top\ni\nAt the "
285+
"top\n"
286+
);
287+
THEN("only 11 choices are left")
288+
{
289+
REQUIRE(thread->num_choices() == 10);
290+
const char* options = "bdfhjklmno";
291+
for (const char* c = options; *c; ++c) {
292+
CHECK(thread->get_choice(static_cast<size_t>(c - options))->text()[0] == *c);
293+
}
294+
}
295+
}
296+
}
297+
}

0 commit comments

Comments
 (0)