Skip to content

Commit 3fbfd73

Browse files
committed
Document why Ruby's String#% > Raylib's textFormat
1 parent 6824b18 commit 3fbfd73

File tree

1 file changed

+98
-0
lines changed

1 file changed

+98
-0
lines changed
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
# 13. Native string manipulation vs `Raylib.text_format`
2+
3+
Date: 2023-10-25
4+
5+
## Status
6+
7+
Accepted
8+
9+
## Context
10+
11+
Our project involves frequent use of string formatting for display purposes. We were initially using Raylib's
12+
`TextFormat`/`Raylib.text_format` C function, bound through FFI, for this task. However, as our project primarily relies
13+
on Ruby, we have to decide whether to continue using Raylib.text_format or switch to native Ruby string manipulation
14+
methods, like string interpolation, `sprintf`, and `String#%`.
15+
16+
Here's the C implementation of `TextFormat` from Raylib which our Ruby `binding Raylib.text_format` was based on:
17+
18+
```c
19+
// Formatting of text with variables to 'embed'
20+
// WARNING: String returned will expire after this function is called MAX_TEXTFORMAT_BUFFERS times
21+
const char *TextFormat(const char *text, ...)
22+
{
23+
#ifndef MAX_TEXTFORMAT_BUFFERS
24+
#define MAX_TEXTFORMAT_BUFFERS 4 // Maximum number of static buffers for text formatting
25+
#endif
26+
27+
// We create an array of buffers so strings don't expire until MAX_TEXTFORMAT_BUFFERS invocations
28+
static char buffers[MAX_TEXTFORMAT_BUFFERS][MAX_TEXT_BUFFER_LENGTH] = { 0 };
29+
static int index = 0;
30+
31+
char *currentBuffer = buffers[index];
32+
memset(currentBuffer, 0, MAX_TEXT_BUFFER_LENGTH); // Clear buffer before using
33+
34+
va_list args;
35+
va_start(args, text);
36+
int requiredByteCount = vsnprintf(currentBuffer, MAX_TEXT_BUFFER_LENGTH, text, args);
37+
va_end(args);
38+
39+
// If requiredByteCount is larger than the MAX_TEXT_BUFFER_LENGTH, then overflow occured
40+
if (requiredByteCount >= MAX_TEXT_BUFFER_LENGTH)
41+
{
42+
// Inserting "..." at the end of the string to mark as truncated
43+
char *truncBuffer = buffers[index] + MAX_TEXT_BUFFER_LENGTH - 4; // Adding 4 bytes = "...\0"
44+
sprintf(truncBuffer, "...");
45+
}
46+
47+
index += 1; // Move to next buffer for next function call
48+
if (index >= MAX_TEXTFORMAT_BUFFERS) index = 0;
49+
50+
return currentBuffer;
51+
}
52+
```
53+
54+
__Considerations__: Consistency and Maintainability: Our project predominantly uses Ruby as the programming language.
55+
Utilizing native Ruby methods for string manipulation promotes codebase consistency, making it more straightforward for
56+
developers to maintain and extend the project.
57+
58+
__Platform Independence__: Native Ruby string manipulation is part of the Ruby standard library and ensures
59+
cross-platform consistency, eliminating dependency on Raylib's C libraries for this specific feature.
60+
61+
__Ruby Community and Ecosystem__: Using native Ruby methods for string formatting allows us to tap into the
62+
extensive Ruby community and ecosystem for guidance and best practices, thereby enriching our codebase.
63+
64+
__Performance__: While the difference may not be significant, using native Ruby methods can result in faster
65+
execution time as it avoids the overhead of FFI calls to C libraries.
66+
67+
__Ease of Use__: Ruby’s native string manipulation methods are expressive, readable, and align well with the
68+
language's philosophy.
69+
70+
## Decision
71+
72+
We will use native Ruby methods for string formatting. This approach not only aligns with our language choice but
73+
also promotes code readability and maintainability.
74+
75+
Here are real examples that demonstrate the replacement of `Raylib.text_format` with Ruby’s native string manipulation:
76+
77+
```diff
78+
- Raylib.text_format("%i", :int, rand_value)
79+
+ rand_value.to_s
80+
81+
- Raylib.text_format("GP1: %s", :string, Raylib.get_gamepad_name(0))
82+
+ "GP1: #{Raylib.get_gamepad_name(0)}"
83+
84+
- Raylib.draw_text(Raylib.text_format('Text size: [%02.02f, %02.02f]', :float, text_size.x, :float, text_size.y)
85+
+ 'Text size: [%02.02f, %02.02f]' % [text_size.x, text_size.y]
86+
```
87+
88+
## Consequences
89+
90+
### Negative
91+
92+
- Developers accustomed to Raylib's C-style string formatting may require time to adapt to Ruby's native methods.
93+
94+
### Positive
95+
96+
- Enhanced code readability and maintainability.
97+
- Improved performance by eliminating FFI overhead.
98+
- Alignment with Ruby's best practices and programming philosophy.

0 commit comments

Comments
 (0)