liquid-c 4.0.1 → 4.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/workflows/liquid.yml +24 -2
- data/.gitignore +4 -0
- data/.rubocop.yml +14 -0
- data/Gemfile +14 -5
- data/README.md +29 -5
- data/Rakefile +13 -62
- data/ext/liquid_c/block.c +488 -60
- data/ext/liquid_c/block.h +28 -2
- data/ext/liquid_c/c_buffer.c +42 -0
- data/ext/liquid_c/c_buffer.h +76 -0
- data/ext/liquid_c/context.c +233 -0
- data/ext/liquid_c/context.h +70 -0
- data/ext/liquid_c/document_body.c +89 -0
- data/ext/liquid_c/document_body.h +59 -0
- data/ext/liquid_c/expression.c +116 -0
- data/ext/liquid_c/expression.h +24 -0
- data/ext/liquid_c/extconf.rb +19 -9
- data/ext/liquid_c/intutil.h +22 -0
- data/ext/liquid_c/lexer.c +6 -2
- data/ext/liquid_c/lexer.h +18 -3
- data/ext/liquid_c/liquid.c +76 -6
- data/ext/liquid_c/liquid.h +24 -1
- data/ext/liquid_c/parse_context.c +76 -0
- data/ext/liquid_c/parse_context.h +13 -0
- data/ext/liquid_c/parser.c +141 -65
- data/ext/liquid_c/parser.h +4 -2
- data/ext/liquid_c/raw.c +110 -0
- data/ext/liquid_c/raw.h +6 -0
- data/ext/liquid_c/resource_limits.c +279 -0
- data/ext/liquid_c/resource_limits.h +23 -0
- data/ext/liquid_c/stringutil.h +44 -0
- data/ext/liquid_c/tokenizer.c +149 -35
- data/ext/liquid_c/tokenizer.h +20 -9
- data/ext/liquid_c/usage.c +18 -0
- data/ext/liquid_c/usage.h +9 -0
- data/ext/liquid_c/variable.c +196 -20
- data/ext/liquid_c/variable.h +18 -1
- data/ext/liquid_c/variable_lookup.c +44 -0
- data/ext/liquid_c/variable_lookup.h +8 -0
- data/ext/liquid_c/vm.c +588 -0
- data/ext/liquid_c/vm.h +25 -0
- data/ext/liquid_c/vm_assembler.c +491 -0
- data/ext/liquid_c/vm_assembler.h +240 -0
- data/ext/liquid_c/vm_assembler_pool.c +97 -0
- data/ext/liquid_c/vm_assembler_pool.h +27 -0
- data/lib/liquid/c/compile_ext.rb +44 -0
- data/lib/liquid/c/version.rb +3 -1
- data/lib/liquid/c.rb +225 -46
- data/liquid-c.gemspec +16 -10
- data/performance/c_profile.rb +23 -0
- data/performance.rb +6 -4
- data/rakelib/compile.rake +15 -0
- data/rakelib/integration_test.rake +43 -0
- data/rakelib/performance.rake +43 -0
- data/rakelib/rubocop.rake +6 -0
- data/rakelib/unit_test.rake +14 -0
- data/test/integration_test.rb +11 -0
- data/test/liquid_test_helper.rb +21 -0
- data/test/test_helper.rb +14 -2
- data/test/unit/block_test.rb +130 -0
- data/test/unit/context_test.rb +83 -0
- data/test/unit/expression_test.rb +186 -0
- data/test/unit/gc_stress_test.rb +28 -0
- data/test/unit/raw_test.rb +19 -0
- data/test/unit/resource_limits_test.rb +50 -0
- data/test/unit/tokenizer_test.rb +90 -20
- data/test/unit/variable_test.rb +212 -60
- metadata +59 -11
- data/test/liquid_test.rb +0 -11
data/ext/liquid_c/raw.c
ADDED
@@ -0,0 +1,110 @@
|
|
1
|
+
#include "liquid.h"
|
2
|
+
#include "raw.h"
|
3
|
+
#include "stringutil.h"
|
4
|
+
#include "tokenizer.h"
|
5
|
+
|
6
|
+
static VALUE id_block_name, id_raise_tag_never_closed, id_block_delimiter, id_ivar_body;
|
7
|
+
static VALUE cLiquidRaw;
|
8
|
+
|
9
|
+
struct full_token_possibly_invalid_t {
|
10
|
+
long body_len;
|
11
|
+
const char *delimiter_start;
|
12
|
+
long delimiter_len;
|
13
|
+
};
|
14
|
+
|
15
|
+
static bool match_full_token_possibly_invalid(token_t *token, struct full_token_possibly_invalid_t *match)
|
16
|
+
{
|
17
|
+
const char *str = token->str_full;
|
18
|
+
long len = token->len_full;
|
19
|
+
|
20
|
+
match->body_len = 0;
|
21
|
+
match->delimiter_start = NULL;
|
22
|
+
match->delimiter_len = 0;
|
23
|
+
|
24
|
+
if (len < 5) return false; // Must be at least 5 characters: \{%\w%\}
|
25
|
+
if (str[len - 1] != '}' || str[len - 2] != '%') return false;
|
26
|
+
|
27
|
+
const char *curr_delimiter_start;
|
28
|
+
long curr_delimiter_len = 0;
|
29
|
+
|
30
|
+
for (long i = len - 3; i >= 0; i--) {
|
31
|
+
char c = str[i];
|
32
|
+
|
33
|
+
if (is_word_char(c)) {
|
34
|
+
curr_delimiter_start = str + i;
|
35
|
+
curr_delimiter_len++;
|
36
|
+
} else {
|
37
|
+
if (curr_delimiter_len > 0) {
|
38
|
+
match->delimiter_start = curr_delimiter_start;
|
39
|
+
match->delimiter_len = curr_delimiter_len;
|
40
|
+
}
|
41
|
+
curr_delimiter_start = NULL;
|
42
|
+
curr_delimiter_len = 0;
|
43
|
+
}
|
44
|
+
|
45
|
+
if (c == '%' && match->delimiter_len > 0 &&
|
46
|
+
i - 1 >= 0 && str[i - 1] == '{') {
|
47
|
+
match->body_len = i - 1;
|
48
|
+
return true;
|
49
|
+
}
|
50
|
+
}
|
51
|
+
|
52
|
+
return false;
|
53
|
+
}
|
54
|
+
|
55
|
+
static VALUE raw_parse_method(VALUE self, VALUE tokens)
|
56
|
+
{
|
57
|
+
tokenizer_t *tokenizer;
|
58
|
+
Tokenizer_Get_Struct(tokens, tokenizer);
|
59
|
+
|
60
|
+
token_t token;
|
61
|
+
struct full_token_possibly_invalid_t match;
|
62
|
+
|
63
|
+
VALUE block_delimiter = rb_funcall(self, id_block_delimiter, 0);
|
64
|
+
Check_Type(block_delimiter, T_STRING);
|
65
|
+
char *block_delimiter_str = RSTRING_PTR(block_delimiter);
|
66
|
+
long block_delimiter_len = RSTRING_LEN(block_delimiter);
|
67
|
+
|
68
|
+
const char *body = NULL;
|
69
|
+
long body_len = 0;
|
70
|
+
|
71
|
+
while (true) {
|
72
|
+
tokenizer_next(tokenizer, &token);
|
73
|
+
|
74
|
+
if (!token.type) break;
|
75
|
+
|
76
|
+
if (body == NULL) {
|
77
|
+
body = token.str_full;
|
78
|
+
}
|
79
|
+
|
80
|
+
if (match_full_token_possibly_invalid(&token, &match)
|
81
|
+
&& match.delimiter_len == block_delimiter_len
|
82
|
+
&& memcmp(match.delimiter_start, block_delimiter_str, block_delimiter_len) == 0) {
|
83
|
+
body_len += match.body_len;
|
84
|
+
VALUE body_str = rb_enc_str_new(body, body_len, utf8_encoding);
|
85
|
+
rb_ivar_set(self, id_ivar_body, body_str);
|
86
|
+
if (RBASIC_CLASS(self) == cLiquidRaw) {
|
87
|
+
tokenizer->raw_tag_body = RSTRING_PTR(body_str);
|
88
|
+
tokenizer->raw_tag_body_len = (unsigned int)body_len;
|
89
|
+
}
|
90
|
+
return Qnil;
|
91
|
+
}
|
92
|
+
|
93
|
+
body_len += token.len_full;
|
94
|
+
}
|
95
|
+
|
96
|
+
rb_funcall(self, id_raise_tag_never_closed, 1, rb_funcall(self, id_block_name, 0));
|
97
|
+
return Qnil;
|
98
|
+
}
|
99
|
+
|
100
|
+
void liquid_define_raw(void)
|
101
|
+
{
|
102
|
+
id_block_name = rb_intern("block_name");
|
103
|
+
id_raise_tag_never_closed = rb_intern("raise_tag_never_closed");
|
104
|
+
id_block_delimiter = rb_intern("block_delimiter");
|
105
|
+
id_ivar_body = rb_intern("@body");
|
106
|
+
|
107
|
+
cLiquidRaw = rb_const_get(mLiquid, rb_intern("Raw"));
|
108
|
+
|
109
|
+
rb_define_method(cLiquidRaw, "c_parse", raw_parse_method, 1);
|
110
|
+
}
|
data/ext/liquid_c/raw.h
ADDED
@@ -0,0 +1,279 @@
|
|
1
|
+
#include "liquid.h"
|
2
|
+
#include "resource_limits.h"
|
3
|
+
|
4
|
+
VALUE cLiquidResourceLimits;
|
5
|
+
|
6
|
+
static void resource_limits_free(void *ptr)
|
7
|
+
{
|
8
|
+
resource_limits_t *resource_limits = ptr;
|
9
|
+
xfree(resource_limits);
|
10
|
+
}
|
11
|
+
|
12
|
+
static size_t resource_limits_memsize(const void *ptr)
|
13
|
+
{
|
14
|
+
return sizeof(resource_limits_t);
|
15
|
+
}
|
16
|
+
|
17
|
+
const rb_data_type_t resource_limits_data_type = {
|
18
|
+
"liquid_resource_limits",
|
19
|
+
{ NULL, resource_limits_free, resource_limits_memsize },
|
20
|
+
NULL, NULL, RUBY_TYPED_FREE_IMMEDIATELY
|
21
|
+
};
|
22
|
+
|
23
|
+
static void resource_limits_reset(resource_limits_t *resource_limit)
|
24
|
+
{
|
25
|
+
resource_limit->reached_limit = true;
|
26
|
+
resource_limit->last_capture_length = -1;
|
27
|
+
resource_limit->render_score = 0;
|
28
|
+
resource_limit->assign_score = 0;
|
29
|
+
}
|
30
|
+
|
31
|
+
static VALUE resource_limits_allocate(VALUE klass)
|
32
|
+
{
|
33
|
+
resource_limits_t *resource_limits;
|
34
|
+
|
35
|
+
VALUE obj = TypedData_Make_Struct(klass, resource_limits_t, &resource_limits_data_type, resource_limits);
|
36
|
+
|
37
|
+
resource_limits_reset(resource_limits);
|
38
|
+
|
39
|
+
return obj;
|
40
|
+
}
|
41
|
+
|
42
|
+
static VALUE resource_limits_render_length_limit_method(VALUE self)
|
43
|
+
{
|
44
|
+
resource_limits_t *resource_limits;
|
45
|
+
ResourceLimits_Get_Struct(self, resource_limits);
|
46
|
+
|
47
|
+
return LONG2NUM(resource_limits->render_length_limit);
|
48
|
+
}
|
49
|
+
|
50
|
+
static VALUE resource_limits_set_render_length_limit_method(VALUE self, VALUE render_length_limit)
|
51
|
+
{
|
52
|
+
resource_limits_t *resource_limits;
|
53
|
+
ResourceLimits_Get_Struct(self, resource_limits);
|
54
|
+
|
55
|
+
if (render_length_limit == Qnil) {
|
56
|
+
resource_limits->render_length_limit = LONG_MAX;
|
57
|
+
} else {
|
58
|
+
resource_limits->render_length_limit = NUM2LONG(render_length_limit);
|
59
|
+
}
|
60
|
+
|
61
|
+
return Qnil;
|
62
|
+
}
|
63
|
+
|
64
|
+
static VALUE resource_limits_render_score_limit_method(VALUE self)
|
65
|
+
{
|
66
|
+
resource_limits_t *resource_limits;
|
67
|
+
ResourceLimits_Get_Struct(self, resource_limits);
|
68
|
+
|
69
|
+
return LONG2NUM(resource_limits->render_score_limit);
|
70
|
+
}
|
71
|
+
|
72
|
+
static VALUE resource_limits_set_render_score_limit_method(VALUE self, VALUE render_score_limit)
|
73
|
+
{
|
74
|
+
resource_limits_t *resource_limits;
|
75
|
+
ResourceLimits_Get_Struct(self, resource_limits);
|
76
|
+
|
77
|
+
if (render_score_limit == Qnil) {
|
78
|
+
resource_limits->render_score_limit = LONG_MAX;
|
79
|
+
} else {
|
80
|
+
resource_limits->render_score_limit = NUM2LONG(render_score_limit);
|
81
|
+
}
|
82
|
+
|
83
|
+
return Qnil;
|
84
|
+
}
|
85
|
+
|
86
|
+
static VALUE resource_limits_assign_score_limit_method(VALUE self)
|
87
|
+
{
|
88
|
+
resource_limits_t *resource_limits;
|
89
|
+
ResourceLimits_Get_Struct(self, resource_limits);
|
90
|
+
|
91
|
+
return LONG2NUM(resource_limits->assign_score_limit);
|
92
|
+
}
|
93
|
+
|
94
|
+
static VALUE resource_limits_set_assign_score_limit_method(VALUE self, VALUE assign_score_limit)
|
95
|
+
{
|
96
|
+
resource_limits_t *resource_limits;
|
97
|
+
ResourceLimits_Get_Struct(self, resource_limits);
|
98
|
+
|
99
|
+
if (assign_score_limit == Qnil) {
|
100
|
+
resource_limits->assign_score_limit = LONG_MAX;
|
101
|
+
} else {
|
102
|
+
resource_limits->assign_score_limit = NUM2LONG(assign_score_limit);
|
103
|
+
}
|
104
|
+
|
105
|
+
return Qnil;
|
106
|
+
}
|
107
|
+
|
108
|
+
static VALUE resource_limits_render_score_method(VALUE self)
|
109
|
+
{
|
110
|
+
resource_limits_t *resource_limits;
|
111
|
+
ResourceLimits_Get_Struct(self, resource_limits);
|
112
|
+
|
113
|
+
return LONG2NUM(resource_limits->render_score);
|
114
|
+
}
|
115
|
+
|
116
|
+
static VALUE resource_limits_assign_score_method(VALUE self)
|
117
|
+
{
|
118
|
+
resource_limits_t *resource_limits;
|
119
|
+
ResourceLimits_Get_Struct(self, resource_limits);
|
120
|
+
|
121
|
+
return LONG2NUM(resource_limits->assign_score);
|
122
|
+
}
|
123
|
+
|
124
|
+
static VALUE resource_limits_initialize_method(VALUE self, VALUE render_length_limit,
|
125
|
+
VALUE render_score_limit, VALUE assign_score_limit)
|
126
|
+
{
|
127
|
+
resource_limits_set_render_length_limit_method(self, render_length_limit);
|
128
|
+
resource_limits_set_render_score_limit_method(self, render_score_limit);
|
129
|
+
resource_limits_set_assign_score_limit_method(self, assign_score_limit);
|
130
|
+
|
131
|
+
return Qnil;
|
132
|
+
}
|
133
|
+
|
134
|
+
__attribute__((noreturn))
|
135
|
+
void resource_limits_raise_limits_reached(resource_limits_t *resource_limit)
|
136
|
+
{
|
137
|
+
resource_limit->reached_limit = true;
|
138
|
+
rb_raise(cMemoryError, "Memory limits exceeded");
|
139
|
+
}
|
140
|
+
|
141
|
+
void resource_limits_increment_render_score(resource_limits_t *resource_limits, long amount)
|
142
|
+
{
|
143
|
+
resource_limits->render_score = resource_limits->render_score + amount;
|
144
|
+
|
145
|
+
if (resource_limits->render_score > resource_limits->render_score_limit) {
|
146
|
+
resource_limits_raise_limits_reached(resource_limits);
|
147
|
+
}
|
148
|
+
}
|
149
|
+
|
150
|
+
static VALUE resource_limits_increment_render_score_method(VALUE self, VALUE amount)
|
151
|
+
{
|
152
|
+
resource_limits_t *resource_limits;
|
153
|
+
ResourceLimits_Get_Struct(self, resource_limits);
|
154
|
+
|
155
|
+
resource_limits_increment_render_score(resource_limits, NUM2LONG(amount));
|
156
|
+
|
157
|
+
return Qnil;
|
158
|
+
}
|
159
|
+
|
160
|
+
static void resource_limits_increment_assign_score(resource_limits_t *resource_limits, long amount)
|
161
|
+
{
|
162
|
+
resource_limits->assign_score = resource_limits->assign_score + amount;
|
163
|
+
|
164
|
+
if (resource_limits->assign_score > resource_limits->assign_score_limit) {
|
165
|
+
resource_limits_raise_limits_reached(resource_limits);
|
166
|
+
}
|
167
|
+
}
|
168
|
+
|
169
|
+
static VALUE resource_limits_increment_assign_score_method(VALUE self, VALUE amount)
|
170
|
+
{
|
171
|
+
resource_limits_t *resource_limits;
|
172
|
+
ResourceLimits_Get_Struct(self, resource_limits);
|
173
|
+
|
174
|
+
resource_limits_increment_assign_score(resource_limits, NUM2LONG(amount));
|
175
|
+
|
176
|
+
return Qnil;
|
177
|
+
}
|
178
|
+
|
179
|
+
void resource_limits_increment_write_score(resource_limits_t *resource_limits, VALUE output)
|
180
|
+
{
|
181
|
+
long captured = RSTRING_LEN(output);
|
182
|
+
|
183
|
+
if (resource_limits->last_capture_length >= 0) {
|
184
|
+
long increment = captured - resource_limits->last_capture_length;
|
185
|
+
resource_limits->last_capture_length = captured;
|
186
|
+
resource_limits_increment_assign_score(resource_limits, increment);
|
187
|
+
} else if (captured > resource_limits->render_length_limit) {
|
188
|
+
resource_limits_raise_limits_reached(resource_limits);
|
189
|
+
}
|
190
|
+
}
|
191
|
+
|
192
|
+
static VALUE resource_limits_increment_write_score_method(VALUE self, VALUE output)
|
193
|
+
{
|
194
|
+
Check_Type(output, T_STRING);
|
195
|
+
|
196
|
+
resource_limits_t *resource_limits;
|
197
|
+
ResourceLimits_Get_Struct(self, resource_limits);
|
198
|
+
|
199
|
+
resource_limits_increment_write_score(resource_limits, output);
|
200
|
+
|
201
|
+
return Qnil;
|
202
|
+
}
|
203
|
+
|
204
|
+
static VALUE resource_limits_raise_limits_reached_method(VALUE self)
|
205
|
+
{
|
206
|
+
resource_limits_t *resource_limits;
|
207
|
+
ResourceLimits_Get_Struct(self, resource_limits);
|
208
|
+
|
209
|
+
resource_limits_raise_limits_reached(resource_limits);
|
210
|
+
}
|
211
|
+
|
212
|
+
static VALUE resource_limits_reached_method(VALUE self)
|
213
|
+
{
|
214
|
+
resource_limits_t *resource_limits;
|
215
|
+
ResourceLimits_Get_Struct(self, resource_limits);
|
216
|
+
|
217
|
+
return resource_limits->reached_limit ? Qtrue : Qfalse;
|
218
|
+
}
|
219
|
+
|
220
|
+
struct capture_ensure_t {
|
221
|
+
resource_limits_t *resource_limits;
|
222
|
+
long old_capture_length;
|
223
|
+
};
|
224
|
+
|
225
|
+
static VALUE capture_ensure(VALUE data)
|
226
|
+
{
|
227
|
+
struct capture_ensure_t *ensure_data = (struct capture_ensure_t *)data;
|
228
|
+
ensure_data->resource_limits->last_capture_length = ensure_data->old_capture_length;
|
229
|
+
|
230
|
+
return Qnil;
|
231
|
+
}
|
232
|
+
|
233
|
+
static VALUE resource_limits_with_capture_method(VALUE self)
|
234
|
+
{
|
235
|
+
resource_limits_t *resource_limits;
|
236
|
+
ResourceLimits_Get_Struct(self, resource_limits);
|
237
|
+
|
238
|
+
struct capture_ensure_t ensure_data = {
|
239
|
+
.resource_limits = resource_limits,
|
240
|
+
.old_capture_length = resource_limits->last_capture_length
|
241
|
+
};
|
242
|
+
|
243
|
+
resource_limits->last_capture_length = 0;
|
244
|
+
|
245
|
+
return rb_ensure(rb_yield, Qundef, capture_ensure, (VALUE)&ensure_data);
|
246
|
+
}
|
247
|
+
|
248
|
+
|
249
|
+
static VALUE resource_limits_reset_method(VALUE self)
|
250
|
+
{
|
251
|
+
resource_limits_t *resource_limits;
|
252
|
+
ResourceLimits_Get_Struct(self, resource_limits);
|
253
|
+
resource_limits_reset(resource_limits);
|
254
|
+
return Qnil;
|
255
|
+
}
|
256
|
+
|
257
|
+
void liquid_define_resource_limits(void)
|
258
|
+
{
|
259
|
+
cLiquidResourceLimits = rb_define_class_under(mLiquidC, "ResourceLimits", rb_cObject);
|
260
|
+
rb_global_variable(&cLiquidResourceLimits);
|
261
|
+
|
262
|
+
rb_define_alloc_func(cLiquidResourceLimits, resource_limits_allocate);
|
263
|
+
rb_define_method(cLiquidResourceLimits, "initialize", resource_limits_initialize_method, 3);
|
264
|
+
rb_define_method(cLiquidResourceLimits, "render_length_limit", resource_limits_render_length_limit_method, 0);
|
265
|
+
rb_define_method(cLiquidResourceLimits, "render_length_limit=", resource_limits_set_render_length_limit_method, 1);
|
266
|
+
rb_define_method(cLiquidResourceLimits, "render_score_limit", resource_limits_render_score_limit_method, 0);
|
267
|
+
rb_define_method(cLiquidResourceLimits, "render_score_limit=", resource_limits_set_render_score_limit_method, 1);
|
268
|
+
rb_define_method(cLiquidResourceLimits, "assign_score_limit", resource_limits_assign_score_limit_method, 0);
|
269
|
+
rb_define_method(cLiquidResourceLimits, "assign_score_limit=", resource_limits_set_assign_score_limit_method, 1);
|
270
|
+
rb_define_method(cLiquidResourceLimits, "render_score", resource_limits_render_score_method, 0);
|
271
|
+
rb_define_method(cLiquidResourceLimits, "assign_score", resource_limits_assign_score_method, 0);
|
272
|
+
rb_define_method(cLiquidResourceLimits, "increment_render_score", resource_limits_increment_render_score_method, 1);
|
273
|
+
rb_define_method(cLiquidResourceLimits, "increment_assign_score", resource_limits_increment_assign_score_method, 1);
|
274
|
+
rb_define_method(cLiquidResourceLimits, "increment_write_score", resource_limits_increment_write_score_method, 1);
|
275
|
+
rb_define_method(cLiquidResourceLimits, "raise_limits_reached", resource_limits_raise_limits_reached_method, 0);
|
276
|
+
rb_define_method(cLiquidResourceLimits, "reached?", resource_limits_reached_method, 0);
|
277
|
+
rb_define_method(cLiquidResourceLimits, "reset", resource_limits_reset_method, 0);
|
278
|
+
rb_define_method(cLiquidResourceLimits, "with_capture", resource_limits_with_capture_method, 0);
|
279
|
+
}
|
@@ -0,0 +1,23 @@
|
|
1
|
+
#ifndef LIQUID_RESOURCE_LIMITS
|
2
|
+
#define LIQUID_RESOURCE_LIMITS
|
3
|
+
|
4
|
+
typedef struct resource_limits {
|
5
|
+
long render_length_limit;
|
6
|
+
long render_score_limit;
|
7
|
+
long assign_score_limit;
|
8
|
+
bool reached_limit;
|
9
|
+
long last_capture_length;
|
10
|
+
long render_score;
|
11
|
+
long assign_score;
|
12
|
+
} resource_limits_t;
|
13
|
+
|
14
|
+
extern VALUE cLiquidResourceLimits;
|
15
|
+
extern const rb_data_type_t resource_limits_data_type;
|
16
|
+
#define ResourceLimits_Get_Struct(obj, sval) TypedData_Get_Struct(obj, resource_limits_t, &resource_limits_data_type, sval)
|
17
|
+
|
18
|
+
void liquid_define_resource_limits(void);
|
19
|
+
void resource_limits_raise_limits_reached(resource_limits_t *resource_limit);
|
20
|
+
void resource_limits_increment_render_score(resource_limits_t *resource_limits, long amount);
|
21
|
+
void resource_limits_increment_write_score(resource_limits_t *resource_limits, VALUE output);
|
22
|
+
|
23
|
+
#endif
|
@@ -0,0 +1,44 @@
|
|
1
|
+
#if !defined(LIQUID_UTIL_H)
|
2
|
+
#define LIQUID_UTIL_H
|
3
|
+
|
4
|
+
inline static const char *read_while(const char *start, const char *end, int (func)(int))
|
5
|
+
{
|
6
|
+
while (start < end && func((unsigned char) *start)) start++;
|
7
|
+
return start;
|
8
|
+
}
|
9
|
+
|
10
|
+
inline static const char *read_while_reverse(const char *start, const char *end, int (func)(int))
|
11
|
+
{
|
12
|
+
end--;
|
13
|
+
while (start <= end && func((unsigned char) *end)) end--;
|
14
|
+
end++;
|
15
|
+
return end;
|
16
|
+
}
|
17
|
+
|
18
|
+
inline static int count_newlines(const char *start, const char *end)
|
19
|
+
{
|
20
|
+
int count = 0;
|
21
|
+
while (start < end) {
|
22
|
+
if (*start == '\n') count++;
|
23
|
+
start++;
|
24
|
+
}
|
25
|
+
return count;
|
26
|
+
}
|
27
|
+
|
28
|
+
inline static int is_non_newline_space(int c)
|
29
|
+
{
|
30
|
+
return rb_isspace(c) && c != '\n';
|
31
|
+
}
|
32
|
+
|
33
|
+
inline static int not_newline(int c)
|
34
|
+
{
|
35
|
+
return c != '\n';
|
36
|
+
}
|
37
|
+
|
38
|
+
inline static bool is_word_char(char c)
|
39
|
+
{
|
40
|
+
return ISALNUM(c) || c == '_';
|
41
|
+
}
|
42
|
+
|
43
|
+
#endif
|
44
|
+
|