| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
#define _GNU_SOURCE |
|
2
|
|
|
|
|
|
|
#include "EXTERN.h" |
|
3
|
|
|
|
|
|
|
#include "perl.h" |
|
4
|
|
|
|
|
|
|
#include "XSUB.h" |
|
5
|
|
|
|
|
|
|
#include "funcutil_compat.h" |
|
6
|
|
|
|
|
|
|
#include "multicall_compat.h" |
|
7
|
|
|
|
|
|
|
#include |
|
8
|
|
|
|
|
|
|
|
|
9
|
|
|
|
|
|
|
/* Portable memmem - use system version if available, else our own */ |
|
10
|
|
|
|
|
|
|
#ifndef HAVE_MEMMEM |
|
11
|
|
|
|
|
|
|
#if defined(__GLIBC__) || defined(__APPLE__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) |
|
12
|
|
|
|
|
|
|
#define HAVE_MEMMEM 1 |
|
13
|
|
|
|
|
|
|
#endif |
|
14
|
|
|
|
|
|
|
#endif |
|
15
|
|
|
|
|
|
|
|
|
16
|
|
|
|
|
|
|
#if HAVE_MEMMEM |
|
17
|
|
|
|
|
|
|
#define util_memmem memmem |
|
18
|
|
|
|
|
|
|
#else |
|
19
|
|
|
|
|
|
|
static void *util_memmem(const void *haystack, size_t haystacklen, |
|
20
|
|
|
|
|
|
|
const void *needle, size_t needlelen) { |
|
21
|
|
|
|
|
|
|
if (needlelen == 0) return (void*)haystack; |
|
22
|
|
|
|
|
|
|
if (needlelen > haystacklen) return NULL; |
|
23
|
|
|
|
|
|
|
|
|
24
|
|
|
|
|
|
|
const char *h = (const char*)haystack; |
|
25
|
|
|
|
|
|
|
const char *n = (const char*)needle; |
|
26
|
|
|
|
|
|
|
const char *end = h + haystacklen - needlelen + 1; |
|
27
|
|
|
|
|
|
|
char first = *n; |
|
28
|
|
|
|
|
|
|
|
|
29
|
|
|
|
|
|
|
for (; h < end; h++) { |
|
30
|
|
|
|
|
|
|
if (*h == first && memcmp(h, n, needlelen) == 0) { |
|
31
|
|
|
|
|
|
|
return (void*)h; |
|
32
|
|
|
|
|
|
|
} |
|
33
|
|
|
|
|
|
|
} |
|
34
|
|
|
|
|
|
|
return NULL; |
|
35
|
|
|
|
|
|
|
} |
|
36
|
|
|
|
|
|
|
#endif |
|
37
|
|
|
|
|
|
|
|
|
38
|
|
|
|
|
|
|
/* ============================================ |
|
39
|
|
|
|
|
|
|
Custom op structures |
|
40
|
|
|
|
|
|
|
============================================ */ |
|
41
|
|
|
|
|
|
|
|
|
42
|
|
|
|
|
|
|
static XOP identity_xop; |
|
43
|
|
|
|
|
|
|
static XOP always_xop; |
|
44
|
|
|
|
|
|
|
static XOP clamp_xop; |
|
45
|
|
|
|
|
|
|
static XOP nvl_xop; |
|
46
|
|
|
|
|
|
|
static XOP coalesce_xop; |
|
47
|
|
|
|
|
|
|
|
|
48
|
|
|
|
|
|
|
/* Type predicate custom ops - blazing fast, single SV flag check */ |
|
49
|
|
|
|
|
|
|
static XOP is_ref_xop; |
|
50
|
|
|
|
|
|
|
static XOP is_array_xop; |
|
51
|
|
|
|
|
|
|
static XOP is_hash_xop; |
|
52
|
|
|
|
|
|
|
static XOP is_code_xop; |
|
53
|
|
|
|
|
|
|
static XOP is_defined_xop; |
|
54
|
|
|
|
|
|
|
|
|
55
|
|
|
|
|
|
|
/* String predicate custom ops - direct SvPV/SvCUR access */ |
|
56
|
|
|
|
|
|
|
static XOP is_empty_xop; |
|
57
|
|
|
|
|
|
|
static XOP starts_with_xop; |
|
58
|
|
|
|
|
|
|
static XOP ends_with_xop; |
|
59
|
|
|
|
|
|
|
/* Boolean/Truthiness custom ops - fast truth checks */ |
|
60
|
|
|
|
|
|
|
static XOP is_true_xop; |
|
61
|
|
|
|
|
|
|
static XOP is_false_xop; |
|
62
|
|
|
|
|
|
|
static XOP bool_xop; |
|
63
|
|
|
|
|
|
|
|
|
64
|
|
|
|
|
|
|
/* Extended type predicate custom ops */ |
|
65
|
|
|
|
|
|
|
static XOP is_num_xop; |
|
66
|
|
|
|
|
|
|
static XOP is_int_xop; |
|
67
|
|
|
|
|
|
|
static XOP is_blessed_xop; |
|
68
|
|
|
|
|
|
|
static XOP is_scalar_ref_xop; |
|
69
|
|
|
|
|
|
|
static XOP is_regex_xop; |
|
70
|
|
|
|
|
|
|
static XOP is_glob_xop; |
|
71
|
|
|
|
|
|
|
static XOP is_string_xop; |
|
72
|
|
|
|
|
|
|
|
|
73
|
|
|
|
|
|
|
/* Numeric predicate custom ops */ |
|
74
|
|
|
|
|
|
|
static XOP is_positive_xop; |
|
75
|
|
|
|
|
|
|
static XOP is_negative_xop; |
|
76
|
|
|
|
|
|
|
static XOP is_zero_xop; |
|
77
|
|
|
|
|
|
|
|
|
78
|
|
|
|
|
|
|
/* Numeric utility custom ops */ |
|
79
|
|
|
|
|
|
|
static XOP is_even_xop; |
|
80
|
|
|
|
|
|
|
static XOP is_odd_xop; |
|
81
|
|
|
|
|
|
|
static XOP is_between_xop; |
|
82
|
|
|
|
|
|
|
|
|
83
|
|
|
|
|
|
|
/* Collection custom ops - direct AvFILL/HvKEYS access */ |
|
84
|
|
|
|
|
|
|
static XOP is_empty_array_xop; |
|
85
|
|
|
|
|
|
|
static XOP is_empty_hash_xop; |
|
86
|
|
|
|
|
|
|
static XOP array_len_xop; |
|
87
|
|
|
|
|
|
|
static XOP hash_size_xop; |
|
88
|
|
|
|
|
|
|
static XOP array_first_xop; |
|
89
|
|
|
|
|
|
|
static XOP array_last_xop; |
|
90
|
|
|
|
|
|
|
|
|
91
|
|
|
|
|
|
|
/* String manipulation custom ops */ |
|
92
|
|
|
|
|
|
|
static XOP trim_xop; |
|
93
|
|
|
|
|
|
|
static XOP ltrim_xop; |
|
94
|
|
|
|
|
|
|
static XOP rtrim_xop; |
|
95
|
|
|
|
|
|
|
|
|
96
|
|
|
|
|
|
|
/* Conditional custom ops */ |
|
97
|
|
|
|
|
|
|
static XOP maybe_xop; |
|
98
|
|
|
|
|
|
|
|
|
99
|
|
|
|
|
|
|
/* Numeric custom ops */ |
|
100
|
|
|
|
|
|
|
static XOP sign_xop; |
|
101
|
|
|
|
|
|
|
static XOP min2_xop; |
|
102
|
|
|
|
|
|
|
static XOP max2_xop; |
|
103
|
|
|
|
|
|
|
|
|
104
|
|
|
|
|
|
|
/* ============================================ |
|
105
|
|
|
|
|
|
|
Memoization structures |
|
106
|
|
|
|
|
|
|
============================================ */ |
|
107
|
|
|
|
|
|
|
|
|
108
|
|
|
|
|
|
|
typedef struct { |
|
109
|
|
|
|
|
|
|
SV *func; /* Original coderef */ |
|
110
|
|
|
|
|
|
|
HV *cache; /* Result cache */ |
|
111
|
|
|
|
|
|
|
IV hits; /* Cache hits (stats) */ |
|
112
|
|
|
|
|
|
|
IV misses; /* Cache misses (stats) */ |
|
113
|
|
|
|
|
|
|
} MemoizedFunc; |
|
114
|
|
|
|
|
|
|
|
|
115
|
|
|
|
|
|
|
static MemoizedFunc *g_memos = NULL; |
|
116
|
|
|
|
|
|
|
static IV g_memo_size = 0; |
|
117
|
|
|
|
|
|
|
static IV g_memo_count = 0; |
|
118
|
|
|
|
|
|
|
|
|
119
|
|
|
|
|
|
|
/* ============================================ |
|
120
|
|
|
|
|
|
|
Lazy evaluation structures |
|
121
|
|
|
|
|
|
|
============================================ */ |
|
122
|
|
|
|
|
|
|
|
|
123
|
|
|
|
|
|
|
typedef struct { |
|
124
|
|
|
|
|
|
|
SV *thunk; /* Deferred computation (coderef) */ |
|
125
|
|
|
|
|
|
|
SV *value; /* Cached result */ |
|
126
|
|
|
|
|
|
|
bool forced; /* Has been evaluated? */ |
|
127
|
|
|
|
|
|
|
} LazyValue; |
|
128
|
|
|
|
|
|
|
|
|
129
|
|
|
|
|
|
|
static LazyValue *g_lazies = NULL; |
|
130
|
|
|
|
|
|
|
static IV g_lazy_size = 0; |
|
131
|
|
|
|
|
|
|
static IV g_lazy_count = 0; |
|
132
|
|
|
|
|
|
|
|
|
133
|
|
|
|
|
|
|
/* ============================================ |
|
134
|
|
|
|
|
|
|
Always (constant) structures |
|
135
|
|
|
|
|
|
|
============================================ */ |
|
136
|
|
|
|
|
|
|
|
|
137
|
|
|
|
|
|
|
static SV **g_always_values = NULL; |
|
138
|
|
|
|
|
|
|
static IV g_always_size = 0; |
|
139
|
|
|
|
|
|
|
static IV g_always_count = 0; |
|
140
|
|
|
|
|
|
|
|
|
141
|
|
|
|
|
|
|
/* ============================================ |
|
142
|
|
|
|
|
|
|
Once (execute once) structures |
|
143
|
|
|
|
|
|
|
============================================ */ |
|
144
|
|
|
|
|
|
|
|
|
145
|
|
|
|
|
|
|
typedef struct { |
|
146
|
|
|
|
|
|
|
SV *func; /* Original function */ |
|
147
|
|
|
|
|
|
|
SV *result; /* Cached result */ |
|
148
|
|
|
|
|
|
|
bool called; /* Has been called? */ |
|
149
|
|
|
|
|
|
|
} OnceFunc; |
|
150
|
|
|
|
|
|
|
|
|
151
|
|
|
|
|
|
|
static OnceFunc *g_onces = NULL; |
|
152
|
|
|
|
|
|
|
static IV g_once_size = 0; |
|
153
|
|
|
|
|
|
|
static IV g_once_count = 0; |
|
154
|
|
|
|
|
|
|
|
|
155
|
|
|
|
|
|
|
/* ============================================ |
|
156
|
|
|
|
|
|
|
Partial application structures |
|
157
|
|
|
|
|
|
|
============================================ */ |
|
158
|
|
|
|
|
|
|
|
|
159
|
|
|
|
|
|
|
typedef struct { |
|
160
|
|
|
|
|
|
|
SV *func; /* Original function */ |
|
161
|
|
|
|
|
|
|
AV *bound_args; /* Pre-bound arguments */ |
|
162
|
|
|
|
|
|
|
} PartialFunc; |
|
163
|
|
|
|
|
|
|
|
|
164
|
|
|
|
|
|
|
static PartialFunc *g_partials = NULL; |
|
165
|
|
|
|
|
|
|
static IV g_partial_size = 0; |
|
166
|
|
|
|
|
|
|
static IV g_partial_count = 0; |
|
167
|
|
|
|
|
|
|
|
|
168
|
|
|
|
|
|
|
/* ============================================ |
|
169
|
|
|
|
|
|
|
Loop callback registry structures |
|
170
|
|
|
|
|
|
|
============================================ */ |
|
171
|
|
|
|
|
|
|
|
|
172
|
|
|
|
|
|
|
/* Function pointer types for loop callbacks */ |
|
173
|
|
|
|
|
|
|
typedef bool (*UtilPredicateFunc)(pTHX_ SV *elem); |
|
174
|
|
|
|
|
|
|
typedef SV* (*UtilMapFunc)(pTHX_ SV *elem); |
|
175
|
|
|
|
|
|
|
typedef SV* (*UtilReduceFunc)(pTHX_ SV *accum, SV *elem); |
|
176
|
|
|
|
|
|
|
|
|
177
|
|
|
|
|
|
|
/* Registered callback entry */ |
|
178
|
|
|
|
|
|
|
typedef struct { |
|
179
|
|
|
|
|
|
|
char *name; /* Callback name (e.g., ":is_positive") */ |
|
180
|
|
|
|
|
|
|
UtilPredicateFunc predicate; /* C function for predicates */ |
|
181
|
|
|
|
|
|
|
UtilMapFunc mapper; /* C function for map */ |
|
182
|
|
|
|
|
|
|
UtilReduceFunc reducer; /* C function for reduce */ |
|
183
|
|
|
|
|
|
|
SV *perl_callback; /* Fallback Perl callback */ |
|
184
|
|
|
|
|
|
|
} RegisteredCallback; |
|
185
|
|
|
|
|
|
|
|
|
186
|
|
|
|
|
|
|
/* Global callback registry */ |
|
187
|
|
|
|
|
|
|
static HV *g_callback_registry = NULL; |
|
188
|
|
|
|
|
|
|
|
|
189
|
|
|
|
|
|
|
/* ============================================ |
|
190
|
|
|
|
|
|
|
Forward declarations |
|
191
|
|
|
|
|
|
|
============================================ */ |
|
192
|
|
|
|
|
|
|
|
|
193
|
|
|
|
|
|
|
XS_INTERNAL(xs_memo_call); |
|
194
|
|
|
|
|
|
|
XS_INTERNAL(xs_compose_call); |
|
195
|
|
|
|
|
|
|
XS_INTERNAL(xs_always_call); |
|
196
|
|
|
|
|
|
|
XS_INTERNAL(xs_negate_call); |
|
197
|
|
|
|
|
|
|
XS_INTERNAL(xs_once_call); |
|
198
|
|
|
|
|
|
|
XS_INTERNAL(xs_partial_call); |
|
199
|
|
|
|
|
|
|
|
|
200
|
|
|
|
|
|
|
/* ============================================ |
|
201
|
|
|
|
|
|
|
Magic destructor infrastructure |
|
202
|
|
|
|
|
|
|
============================================ */ |
|
203
|
|
|
|
|
|
|
|
|
204
|
|
|
|
|
|
|
/* Magic free function for "once" wrappers */ |
|
205
|
1005
|
|
|
|
|
|
static int util_once_free(pTHX_ SV *sv, MAGIC *mg) { |
|
206
|
|
|
|
|
|
|
PERL_UNUSED_ARG(sv); |
|
207
|
1005
|
|
|
|
|
|
IV idx = mg->mg_len; |
|
208
|
1005
|
50
|
|
|
|
|
if (idx >= 0 && idx < g_once_count) { |
|
|
|
50
|
|
|
|
|
|
|
209
|
1005
|
|
|
|
|
|
OnceFunc *of = &g_onces[idx]; |
|
210
|
1005
|
50
|
|
|
|
|
if (of->func) { |
|
211
|
0
|
|
|
|
|
|
SvREFCNT_dec(of->func); |
|
212
|
0
|
|
|
|
|
|
of->func = NULL; |
|
213
|
|
|
|
|
|
|
} |
|
214
|
1005
|
50
|
|
|
|
|
if (of->result) { |
|
215
|
1005
|
|
|
|
|
|
SvREFCNT_dec(of->result); |
|
216
|
1005
|
|
|
|
|
|
of->result = NULL; |
|
217
|
|
|
|
|
|
|
} |
|
218
|
1005
|
|
|
|
|
|
of->called = FALSE; |
|
219
|
|
|
|
|
|
|
} |
|
220
|
1005
|
|
|
|
|
|
return 0; |
|
221
|
|
|
|
|
|
|
} |
|
222
|
|
|
|
|
|
|
|
|
223
|
|
|
|
|
|
|
static MGVTBL util_once_vtbl = { |
|
224
|
|
|
|
|
|
|
NULL, /* get */ |
|
225
|
|
|
|
|
|
|
NULL, /* set */ |
|
226
|
|
|
|
|
|
|
NULL, /* len */ |
|
227
|
|
|
|
|
|
|
NULL, /* clear */ |
|
228
|
|
|
|
|
|
|
util_once_free, /* free */ |
|
229
|
|
|
|
|
|
|
NULL, /* copy */ |
|
230
|
|
|
|
|
|
|
NULL, /* dup */ |
|
231
|
|
|
|
|
|
|
NULL /* local */ |
|
232
|
|
|
|
|
|
|
}; |
|
233
|
|
|
|
|
|
|
|
|
234
|
|
|
|
|
|
|
/* Magic free function for "partial" wrappers */ |
|
235
|
1012
|
|
|
|
|
|
static int util_partial_free(pTHX_ SV *sv, MAGIC *mg) { |
|
236
|
|
|
|
|
|
|
PERL_UNUSED_ARG(sv); |
|
237
|
1012
|
|
|
|
|
|
IV idx = mg->mg_len; |
|
238
|
1012
|
50
|
|
|
|
|
if (idx >= 0 && idx < g_partial_count) { |
|
|
|
50
|
|
|
|
|
|
|
239
|
1012
|
|
|
|
|
|
PartialFunc *pf = &g_partials[idx]; |
|
240
|
1012
|
50
|
|
|
|
|
if (pf->func) { |
|
241
|
1012
|
|
|
|
|
|
SvREFCNT_dec(pf->func); |
|
242
|
1012
|
|
|
|
|
|
pf->func = NULL; |
|
243
|
|
|
|
|
|
|
} |
|
244
|
1012
|
50
|
|
|
|
|
if (pf->bound_args) { |
|
245
|
1012
|
|
|
|
|
|
SvREFCNT_dec((SV*)pf->bound_args); |
|
246
|
1012
|
|
|
|
|
|
pf->bound_args = NULL; |
|
247
|
|
|
|
|
|
|
} |
|
248
|
|
|
|
|
|
|
} |
|
249
|
1012
|
|
|
|
|
|
return 0; |
|
250
|
|
|
|
|
|
|
} |
|
251
|
|
|
|
|
|
|
|
|
252
|
|
|
|
|
|
|
static MGVTBL util_partial_vtbl = { |
|
253
|
|
|
|
|
|
|
NULL, NULL, NULL, NULL, util_partial_free, NULL, NULL, NULL |
|
254
|
|
|
|
|
|
|
}; |
|
255
|
|
|
|
|
|
|
|
|
256
|
|
|
|
|
|
|
/* Magic free function for "memo" wrappers */ |
|
257
|
212
|
|
|
|
|
|
static int util_memo_free(pTHX_ SV *sv, MAGIC *mg) { |
|
258
|
|
|
|
|
|
|
PERL_UNUSED_ARG(sv); |
|
259
|
212
|
|
|
|
|
|
IV idx = mg->mg_len; |
|
260
|
212
|
50
|
|
|
|
|
if (idx >= 0 && idx < g_memo_count) { |
|
|
|
50
|
|
|
|
|
|
|
261
|
212
|
|
|
|
|
|
MemoizedFunc *mf = &g_memos[idx]; |
|
262
|
212
|
50
|
|
|
|
|
if (mf->func) { |
|
263
|
212
|
|
|
|
|
|
SvREFCNT_dec(mf->func); |
|
264
|
212
|
|
|
|
|
|
mf->func = NULL; |
|
265
|
|
|
|
|
|
|
} |
|
266
|
212
|
50
|
|
|
|
|
if (mf->cache) { |
|
267
|
212
|
|
|
|
|
|
SvREFCNT_dec((SV*)mf->cache); |
|
268
|
212
|
|
|
|
|
|
mf->cache = NULL; |
|
269
|
|
|
|
|
|
|
} |
|
270
|
212
|
|
|
|
|
|
mf->hits = 0; |
|
271
|
212
|
|
|
|
|
|
mf->misses = 0; |
|
272
|
|
|
|
|
|
|
} |
|
273
|
212
|
|
|
|
|
|
return 0; |
|
274
|
|
|
|
|
|
|
} |
|
275
|
|
|
|
|
|
|
|
|
276
|
|
|
|
|
|
|
static MGVTBL util_memo_vtbl = { |
|
277
|
|
|
|
|
|
|
NULL, NULL, NULL, NULL, util_memo_free, NULL, NULL, NULL |
|
278
|
|
|
|
|
|
|
}; |
|
279
|
|
|
|
|
|
|
|
|
280
|
|
|
|
|
|
|
/* Magic free function for "lazy" wrappers */ |
|
281
|
1008
|
|
|
|
|
|
static int util_lazy_free(pTHX_ SV *sv, MAGIC *mg) { |
|
282
|
|
|
|
|
|
|
PERL_UNUSED_ARG(sv); |
|
283
|
1008
|
|
|
|
|
|
IV idx = mg->mg_len; |
|
284
|
1008
|
50
|
|
|
|
|
if (idx >= 0 && idx < g_lazy_count) { |
|
|
|
50
|
|
|
|
|
|
|
285
|
1008
|
|
|
|
|
|
LazyValue *lv = &g_lazies[idx]; |
|
286
|
1008
|
50
|
|
|
|
|
if (lv->thunk) { |
|
287
|
0
|
|
|
|
|
|
SvREFCNT_dec(lv->thunk); |
|
288
|
0
|
|
|
|
|
|
lv->thunk = NULL; |
|
289
|
|
|
|
|
|
|
} |
|
290
|
1008
|
50
|
|
|
|
|
if (lv->value) { |
|
291
|
1008
|
|
|
|
|
|
SvREFCNT_dec(lv->value); |
|
292
|
1008
|
|
|
|
|
|
lv->value = NULL; |
|
293
|
|
|
|
|
|
|
} |
|
294
|
1008
|
|
|
|
|
|
lv->forced = FALSE; |
|
295
|
|
|
|
|
|
|
} |
|
296
|
1008
|
|
|
|
|
|
return 0; |
|
297
|
|
|
|
|
|
|
} |
|
298
|
|
|
|
|
|
|
|
|
299
|
|
|
|
|
|
|
static MGVTBL util_lazy_vtbl = { |
|
300
|
|
|
|
|
|
|
NULL, NULL, NULL, NULL, util_lazy_free, NULL, NULL, NULL |
|
301
|
|
|
|
|
|
|
}; |
|
302
|
|
|
|
|
|
|
|
|
303
|
|
|
|
|
|
|
/* Magic free function for "compose" wrappers */ |
|
304
|
1010
|
|
|
|
|
|
static int util_compose_free(pTHX_ SV *sv, MAGIC *mg) { |
|
305
|
|
|
|
|
|
|
PERL_UNUSED_ARG(sv); |
|
306
|
1010
|
|
|
|
|
|
AV *funcs = (AV*)mg->mg_ptr; |
|
307
|
1010
|
50
|
|
|
|
|
if (funcs) { |
|
308
|
1010
|
|
|
|
|
|
SvREFCNT_dec((SV*)funcs); |
|
309
|
|
|
|
|
|
|
} |
|
310
|
1010
|
|
|
|
|
|
return 0; |
|
311
|
|
|
|
|
|
|
} |
|
312
|
|
|
|
|
|
|
|
|
313
|
|
|
|
|
|
|
static MGVTBL util_compose_vtbl = { |
|
314
|
|
|
|
|
|
|
NULL, NULL, NULL, NULL, util_compose_free, NULL, NULL, NULL |
|
315
|
|
|
|
|
|
|
}; |
|
316
|
|
|
|
|
|
|
|
|
317
|
|
|
|
|
|
|
/* Magic free function for "always" wrappers */ |
|
318
|
8
|
|
|
|
|
|
static int util_always_free(pTHX_ SV *sv, MAGIC *mg) { |
|
319
|
|
|
|
|
|
|
PERL_UNUSED_ARG(sv); |
|
320
|
8
|
|
|
|
|
|
IV idx = mg->mg_len; |
|
321
|
8
|
50
|
|
|
|
|
if (idx >= 0 && idx < g_always_count && g_always_values[idx]) { |
|
|
|
50
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
322
|
8
|
|
|
|
|
|
SvREFCNT_dec(g_always_values[idx]); |
|
323
|
8
|
|
|
|
|
|
g_always_values[idx] = NULL; |
|
324
|
|
|
|
|
|
|
} |
|
325
|
8
|
|
|
|
|
|
return 0; |
|
326
|
|
|
|
|
|
|
} |
|
327
|
|
|
|
|
|
|
|
|
328
|
|
|
|
|
|
|
static MGVTBL util_always_vtbl = { |
|
329
|
|
|
|
|
|
|
NULL, NULL, NULL, NULL, util_always_free, NULL, NULL, NULL |
|
330
|
|
|
|
|
|
|
}; |
|
331
|
|
|
|
|
|
|
|
|
332
|
|
|
|
|
|
|
/* ============================================ |
|
333
|
|
|
|
|
|
|
Utility functions |
|
334
|
|
|
|
|
|
|
============================================ */ |
|
335
|
|
|
|
|
|
|
|
|
336
|
213
|
|
|
|
|
|
static void ensure_memo_capacity(IV needed) { |
|
337
|
213
|
100
|
|
|
|
|
if (needed >= g_memo_size) { |
|
338
|
4
|
50
|
|
|
|
|
IV new_size = g_memo_size ? g_memo_size * 2 : 16; |
|
339
|
4
|
50
|
|
|
|
|
while (new_size <= needed) new_size *= 2; |
|
340
|
4
|
50
|
|
|
|
|
Renew(g_memos, new_size, MemoizedFunc); |
|
341
|
4
|
|
|
|
|
|
g_memo_size = new_size; |
|
342
|
|
|
|
|
|
|
} |
|
343
|
213
|
|
|
|
|
|
} |
|
344
|
|
|
|
|
|
|
|
|
345
|
1008
|
|
|
|
|
|
static void ensure_lazy_capacity(IV needed) { |
|
346
|
1008
|
100
|
|
|
|
|
if (needed >= g_lazy_size) { |
|
347
|
6
|
50
|
|
|
|
|
IV new_size = g_lazy_size ? g_lazy_size * 2 : 16; |
|
348
|
6
|
50
|
|
|
|
|
while (new_size <= needed) new_size *= 2; |
|
349
|
6
|
50
|
|
|
|
|
Renew(g_lazies, new_size, LazyValue); |
|
350
|
6
|
|
|
|
|
|
g_lazy_size = new_size; |
|
351
|
|
|
|
|
|
|
} |
|
352
|
1008
|
|
|
|
|
|
} |
|
353
|
|
|
|
|
|
|
|
|
354
|
8
|
|
|
|
|
|
static void ensure_always_capacity(IV needed) { |
|
355
|
8
|
50
|
|
|
|
|
if (needed >= g_always_size) { |
|
356
|
0
|
0
|
|
|
|
|
IV new_size = g_always_size ? g_always_size * 2 : 16; |
|
357
|
0
|
0
|
|
|
|
|
while (new_size <= needed) new_size *= 2; |
|
358
|
0
|
0
|
|
|
|
|
Renew(g_always_values, new_size, SV*); |
|
359
|
0
|
|
|
|
|
|
g_always_size = new_size; |
|
360
|
|
|
|
|
|
|
} |
|
361
|
8
|
|
|
|
|
|
} |
|
362
|
|
|
|
|
|
|
|
|
363
|
1005
|
|
|
|
|
|
static void ensure_once_capacity(IV needed) { |
|
364
|
1005
|
100
|
|
|
|
|
if (needed >= g_once_size) { |
|
365
|
6
|
50
|
|
|
|
|
IV new_size = g_once_size ? g_once_size * 2 : 16; |
|
366
|
6
|
50
|
|
|
|
|
while (new_size <= needed) new_size *= 2; |
|
367
|
6
|
50
|
|
|
|
|
Renew(g_onces, new_size, OnceFunc); |
|
368
|
6
|
|
|
|
|
|
g_once_size = new_size; |
|
369
|
|
|
|
|
|
|
} |
|
370
|
1005
|
|
|
|
|
|
} |
|
371
|
|
|
|
|
|
|
|
|
372
|
1012
|
|
|
|
|
|
static void ensure_partial_capacity(IV needed) { |
|
373
|
1012
|
100
|
|
|
|
|
if (needed >= g_partial_size) { |
|
374
|
6
|
50
|
|
|
|
|
IV new_size = g_partial_size ? g_partial_size * 2 : 16; |
|
375
|
6
|
50
|
|
|
|
|
while (new_size <= needed) new_size *= 2; |
|
376
|
6
|
50
|
|
|
|
|
Renew(g_partials, new_size, PartialFunc); |
|
377
|
6
|
|
|
|
|
|
g_partial_size = new_size; |
|
378
|
|
|
|
|
|
|
} |
|
379
|
1012
|
|
|
|
|
|
} |
|
380
|
|
|
|
|
|
|
|
|
381
|
|
|
|
|
|
|
/* Build cache key from stack arguments */ |
|
382
|
849
|
|
|
|
|
|
static SV* build_cache_key(pTHX_ SV **args, IV count) { |
|
383
|
849
|
|
|
|
|
|
SV *key = newSVpvs(""); |
|
384
|
|
|
|
|
|
|
IV i; |
|
385
|
1699
|
100
|
|
|
|
|
for (i = 0; i < count; i++) { |
|
386
|
850
|
100
|
|
|
|
|
if (i > 0) sv_catpvs(key, "\x00"); |
|
387
|
850
|
100
|
|
|
|
|
if (SvOK(args[i])) { |
|
388
|
|
|
|
|
|
|
STRLEN len; |
|
389
|
846
|
|
|
|
|
|
const char *pv = SvPV(args[i], len); |
|
390
|
846
|
|
|
|
|
|
sv_catpvn(key, pv, len); |
|
391
|
|
|
|
|
|
|
} else { |
|
392
|
4
|
|
|
|
|
|
sv_catpvs(key, "\x01UNDEF\x01"); |
|
393
|
|
|
|
|
|
|
} |
|
394
|
|
|
|
|
|
|
} |
|
395
|
849
|
|
|
|
|
|
return key; |
|
396
|
|
|
|
|
|
|
} |
|
397
|
|
|
|
|
|
|
|
|
398
|
|
|
|
|
|
|
/* ============================================ |
|
399
|
|
|
|
|
|
|
Built-in predicates for loop callbacks |
|
400
|
|
|
|
|
|
|
(prefixed with ':' for built-in names) |
|
401
|
|
|
|
|
|
|
============================================ */ |
|
402
|
|
|
|
|
|
|
|
|
403
|
16147
|
|
|
|
|
|
static bool builtin_is_defined(pTHX_ SV *elem) { |
|
404
|
16147
|
|
|
|
|
|
return SvOK(elem) ? TRUE : FALSE; |
|
405
|
|
|
|
|
|
|
} |
|
406
|
|
|
|
|
|
|
|
|
407
|
4044
|
|
|
|
|
|
static bool builtin_is_true(pTHX_ SV *elem) { |
|
408
|
4044
|
|
|
|
|
|
return SvTRUE(elem) ? TRUE : FALSE; |
|
409
|
|
|
|
|
|
|
} |
|
410
|
|
|
|
|
|
|
|
|
411
|
1042
|
|
|
|
|
|
static bool builtin_is_false(pTHX_ SV *elem) { |
|
412
|
1042
|
|
|
|
|
|
return !SvTRUE(elem) ? TRUE : FALSE; |
|
413
|
|
|
|
|
|
|
} |
|
414
|
|
|
|
|
|
|
|
|
415
|
15044
|
|
|
|
|
|
static bool builtin_is_ref(pTHX_ SV *elem) { |
|
416
|
15044
|
|
|
|
|
|
return SvROK(elem) ? TRUE : FALSE; |
|
417
|
|
|
|
|
|
|
} |
|
418
|
|
|
|
|
|
|
|
|
419
|
6026
|
|
|
|
|
|
static bool builtin_is_array(pTHX_ SV *elem) { |
|
420
|
6026
|
100
|
|
|
|
|
return (SvROK(elem) && SvTYPE(SvRV(elem)) == SVt_PVAV) ? TRUE : FALSE; |
|
|
|
100
|
|
|
|
|
|
|
421
|
|
|
|
|
|
|
} |
|
422
|
|
|
|
|
|
|
|
|
423
|
7027
|
|
|
|
|
|
static bool builtin_is_hash(pTHX_ SV *elem) { |
|
424
|
7027
|
100
|
|
|
|
|
return (SvROK(elem) && SvTYPE(SvRV(elem)) == SVt_PVHV) ? TRUE : FALSE; |
|
|
|
100
|
|
|
|
|
|
|
425
|
|
|
|
|
|
|
} |
|
426
|
|
|
|
|
|
|
|
|
427
|
23
|
|
|
|
|
|
static bool builtin_is_code(pTHX_ SV *elem) { |
|
428
|
23
|
100
|
|
|
|
|
return (SvROK(elem) && SvTYPE(SvRV(elem)) == SVt_PVCV) ? TRUE : FALSE; |
|
|
|
100
|
|
|
|
|
|
|
429
|
|
|
|
|
|
|
} |
|
430
|
|
|
|
|
|
|
|
|
431
|
2041259
|
|
|
|
|
|
static bool builtin_is_positive(pTHX_ SV *elem) { |
|
432
|
2041259
|
50
|
|
|
|
|
if (SvIOK(elem)) return SvIV(elem) > 0; |
|
433
|
0
|
0
|
|
|
|
|
if (SvNOK(elem)) return SvNV(elem) > 0; |
|
434
|
0
|
0
|
|
|
|
|
if (SvPOK(elem) && looks_like_number(elem)) return SvNV(elem) > 0; |
|
|
|
0
|
|
|
|
|
|
|
435
|
0
|
|
|
|
|
|
return FALSE; |
|
436
|
|
|
|
|
|
|
} |
|
437
|
|
|
|
|
|
|
|
|
438
|
19081
|
|
|
|
|
|
static bool builtin_is_negative(pTHX_ SV *elem) { |
|
439
|
19081
|
50
|
|
|
|
|
if (SvIOK(elem)) return SvIV(elem) < 0; |
|
440
|
0
|
0
|
|
|
|
|
if (SvNOK(elem)) return SvNV(elem) < 0; |
|
441
|
0
|
0
|
|
|
|
|
if (SvPOK(elem) && looks_like_number(elem)) return SvNV(elem) < 0; |
|
|
|
0
|
|
|
|
|
|
|
442
|
0
|
|
|
|
|
|
return FALSE; |
|
443
|
|
|
|
|
|
|
} |
|
444
|
|
|
|
|
|
|
|
|
445
|
15069
|
|
|
|
|
|
static bool builtin_is_zero(pTHX_ SV *elem) { |
|
446
|
15069
|
50
|
|
|
|
|
if (SvIOK(elem)) return SvIV(elem) == 0; |
|
447
|
0
|
0
|
|
|
|
|
if (SvNOK(elem)) return SvNV(elem) == 0.0; |
|
448
|
0
|
0
|
|
|
|
|
if (SvPOK(elem) && looks_like_number(elem)) return SvNV(elem) == 0.0; |
|
|
|
0
|
|
|
|
|
|
|
449
|
0
|
|
|
|
|
|
return FALSE; |
|
450
|
|
|
|
|
|
|
} |
|
451
|
|
|
|
|
|
|
|
|
452
|
30113
|
|
|
|
|
|
static bool builtin_is_even(pTHX_ SV *elem) { |
|
453
|
30113
|
50
|
|
|
|
|
if (!SvIOK(elem) && !SvNOK(elem)) { |
|
|
|
0
|
|
|
|
|
|
|
454
|
0
|
0
|
|
|
|
|
if (!SvPOK(elem) || !looks_like_number(elem)) return FALSE; |
|
|
|
0
|
|
|
|
|
|
|
455
|
|
|
|
|
|
|
} |
|
456
|
30113
|
|
|
|
|
|
IV val = SvIV(elem); |
|
457
|
30113
|
|
|
|
|
|
return (val % 2) == 0; |
|
458
|
|
|
|
|
|
|
} |
|
459
|
|
|
|
|
|
|
|
|
460
|
1054
|
|
|
|
|
|
static bool builtin_is_odd(pTHX_ SV *elem) { |
|
461
|
1054
|
50
|
|
|
|
|
if (!SvIOK(elem) && !SvNOK(elem)) { |
|
|
|
0
|
|
|
|
|
|
|
462
|
0
|
0
|
|
|
|
|
if (!SvPOK(elem) || !looks_like_number(elem)) return FALSE; |
|
|
|
0
|
|
|
|
|
|
|
463
|
|
|
|
|
|
|
} |
|
464
|
1054
|
|
|
|
|
|
IV val = SvIV(elem); |
|
465
|
1054
|
|
|
|
|
|
return (val % 2) != 0; |
|
466
|
|
|
|
|
|
|
} |
|
467
|
|
|
|
|
|
|
|
|
468
|
7045
|
|
|
|
|
|
static bool builtin_is_empty(pTHX_ SV *elem) { |
|
469
|
7045
|
100
|
|
|
|
|
if (!SvOK(elem)) return TRUE; |
|
470
|
6038
|
100
|
|
|
|
|
if (SvROK(elem)) { |
|
471
|
2016
|
|
|
|
|
|
SV *rv = SvRV(elem); |
|
472
|
2016
|
100
|
|
|
|
|
if (SvTYPE(rv) == SVt_PVAV) return AvFILL((AV*)rv) < 0; |
|
|
|
50
|
|
|
|
|
|
|
473
|
1008
|
50
|
|
|
|
|
if (SvTYPE(rv) == SVt_PVHV) return HvKEYS((HV*)rv) == 0; |
|
|
|
50
|
|
|
|
|
|
|
474
|
0
|
|
|
|
|
|
return FALSE; |
|
475
|
|
|
|
|
|
|
} |
|
476
|
4022
|
100
|
|
|
|
|
if (SvPOK(elem)) return SvCUR(elem) == 0; |
|
477
|
2000
|
|
|
|
|
|
return FALSE; |
|
478
|
|
|
|
|
|
|
} |
|
479
|
|
|
|
|
|
|
|
|
480
|
20
|
|
|
|
|
|
static bool builtin_is_nonempty(pTHX_ SV *elem) { |
|
481
|
20
|
|
|
|
|
|
return !builtin_is_empty(aTHX_ elem); |
|
482
|
|
|
|
|
|
|
} |
|
483
|
|
|
|
|
|
|
|
|
484
|
2015
|
|
|
|
|
|
static bool builtin_is_string(pTHX_ SV *elem) { |
|
485
|
2015
|
100
|
|
|
|
|
return (SvPOK(elem) && !SvIOK(elem) && !SvNOK(elem) && !SvROK(elem)) ? TRUE : FALSE; |
|
|
|
50
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
486
|
|
|
|
|
|
|
} |
|
487
|
|
|
|
|
|
|
|
|
488
|
9005
|
|
|
|
|
|
static bool builtin_is_number(pTHX_ SV *elem) { |
|
489
|
9005
|
100
|
|
|
|
|
if (SvIOK(elem) || SvNOK(elem)) return TRUE; |
|
|
|
100
|
|
|
|
|
|
|
490
|
2
|
50
|
|
|
|
|
if (SvPOK(elem) && looks_like_number(elem)) return TRUE; |
|
|
|
100
|
|
|
|
|
|
|
491
|
1
|
|
|
|
|
|
return FALSE; |
|
492
|
|
|
|
|
|
|
} |
|
493
|
|
|
|
|
|
|
|
|
494
|
6
|
|
|
|
|
|
static bool builtin_is_integer(pTHX_ SV *elem) { |
|
495
|
6
|
100
|
|
|
|
|
if (SvIOK(elem) && !SvNOK(elem)) return TRUE; |
|
|
|
50
|
|
|
|
|
|
|
496
|
4
|
100
|
|
|
|
|
if (SvNOK(elem)) { |
|
497
|
3
|
|
|
|
|
|
NV val = SvNV(elem); |
|
498
|
3
|
|
|
|
|
|
return val == (NV)(IV)val; |
|
499
|
|
|
|
|
|
|
} |
|
500
|
1
|
50
|
|
|
|
|
if (SvPOK(elem) && looks_like_number(elem)) { |
|
|
|
50
|
|
|
|
|
|
|
501
|
1
|
|
|
|
|
|
NV val = SvNV(elem); |
|
502
|
1
|
|
|
|
|
|
return val == (NV)(IV)val; |
|
503
|
|
|
|
|
|
|
} |
|
504
|
0
|
|
|
|
|
|
return FALSE; |
|
505
|
|
|
|
|
|
|
} |
|
506
|
|
|
|
|
|
|
|
|
507
|
|
|
|
|
|
|
/* ============================================ |
|
508
|
|
|
|
|
|
|
Callback registry functions |
|
509
|
|
|
|
|
|
|
============================================ */ |
|
510
|
|
|
|
|
|
|
|
|
511
|
921
|
|
|
|
|
|
static void init_callback_registry(pTHX) { |
|
512
|
921
|
100
|
|
|
|
|
if (!g_callback_registry) { |
|
513
|
53
|
|
|
|
|
|
g_callback_registry = newHV(); |
|
514
|
|
|
|
|
|
|
} |
|
515
|
921
|
|
|
|
|
|
} |
|
516
|
|
|
|
|
|
|
|
|
517
|
|
|
|
|
|
|
/* Cleanup callback registry during global destruction */ |
|
518
|
53
|
|
|
|
|
|
static void cleanup_callback_registry(pTHX_ void *data) { |
|
519
|
|
|
|
|
|
|
HE *entry; |
|
520
|
|
|
|
|
|
|
PERL_UNUSED_ARG(data); |
|
521
|
|
|
|
|
|
|
|
|
522
|
53
|
50
|
|
|
|
|
if (!g_callback_registry) return; |
|
523
|
|
|
|
|
|
|
|
|
524
|
|
|
|
|
|
|
/* During global destruction, just NULL out the registry pointer. |
|
525
|
|
|
|
|
|
|
* Perl will handle freeing the SVs. Trying to free them ourselves |
|
526
|
|
|
|
|
|
|
* can cause crashes due to destruction order issues. */ |
|
527
|
53
|
50
|
|
|
|
|
if (PL_dirty) { |
|
528
|
53
|
|
|
|
|
|
g_callback_registry = NULL; |
|
529
|
53
|
|
|
|
|
|
return; |
|
530
|
|
|
|
|
|
|
} |
|
531
|
|
|
|
|
|
|
|
|
532
|
|
|
|
|
|
|
/* Normal cleanup (not during global destruction) */ |
|
533
|
0
|
|
|
|
|
|
hv_iterinit(g_callback_registry); |
|
534
|
0
|
0
|
|
|
|
|
while ((entry = hv_iternext(g_callback_registry))) { |
|
535
|
0
|
|
|
|
|
|
SV *sv = HeVAL(entry); |
|
536
|
0
|
0
|
|
|
|
|
if (sv && SvOK(sv)) { |
|
|
|
0
|
|
|
|
|
|
|
537
|
0
|
|
|
|
|
|
RegisteredCallback *cb = (RegisteredCallback*)SvIVX(sv); |
|
538
|
0
|
0
|
|
|
|
|
if (cb) { |
|
539
|
0
|
0
|
|
|
|
|
if (cb->perl_callback) { |
|
540
|
0
|
|
|
|
|
|
SvREFCNT_dec(cb->perl_callback); |
|
541
|
0
|
|
|
|
|
|
cb->perl_callback = NULL; |
|
542
|
|
|
|
|
|
|
} |
|
543
|
0
|
0
|
|
|
|
|
if (cb->name) { |
|
544
|
0
|
|
|
|
|
|
Safefree(cb->name); |
|
545
|
0
|
|
|
|
|
|
cb->name = NULL; |
|
546
|
|
|
|
|
|
|
} |
|
547
|
0
|
|
|
|
|
|
Safefree(cb); |
|
548
|
|
|
|
|
|
|
} |
|
549
|
|
|
|
|
|
|
} |
|
550
|
|
|
|
|
|
|
} |
|
551
|
0
|
|
|
|
|
|
SvREFCNT_dec((SV*)g_callback_registry); |
|
552
|
0
|
|
|
|
|
|
g_callback_registry = NULL; |
|
553
|
|
|
|
|
|
|
} |
|
554
|
|
|
|
|
|
|
|
|
555
|
41580
|
|
|
|
|
|
static RegisteredCallback* get_registered_callback(pTHX_ const char *name) { |
|
556
|
|
|
|
|
|
|
SV **svp; |
|
557
|
41580
|
50
|
|
|
|
|
if (!g_callback_registry) return NULL; |
|
558
|
41580
|
|
|
|
|
|
svp = hv_fetch(g_callback_registry, name, strlen(name), 0); |
|
559
|
41580
|
100
|
|
|
|
|
if (!svp || !SvOK(*svp)) return NULL; |
|
|
|
50
|
|
|
|
|
|
|
560
|
39550
|
|
|
|
|
|
return (RegisteredCallback*)SvIVX(*svp); |
|
561
|
|
|
|
|
|
|
} |
|
562
|
|
|
|
|
|
|
|
|
563
|
|
|
|
|
|
|
/* Register a built-in predicate */ |
|
564
|
901
|
|
|
|
|
|
static void register_builtin_predicate(pTHX_ const char *name, UtilPredicateFunc func) { |
|
565
|
|
|
|
|
|
|
RegisteredCallback *cb; |
|
566
|
|
|
|
|
|
|
SV *sv; |
|
567
|
|
|
|
|
|
|
|
|
568
|
901
|
|
|
|
|
|
init_callback_registry(aTHX); |
|
569
|
|
|
|
|
|
|
|
|
570
|
901
|
|
|
|
|
|
Newxz(cb, 1, RegisteredCallback); |
|
571
|
901
|
|
|
|
|
|
cb->name = savepv(name); |
|
572
|
901
|
|
|
|
|
|
cb->predicate = func; |
|
573
|
901
|
|
|
|
|
|
cb->mapper = NULL; |
|
574
|
901
|
|
|
|
|
|
cb->reducer = NULL; |
|
575
|
901
|
|
|
|
|
|
cb->perl_callback = NULL; |
|
576
|
|
|
|
|
|
|
|
|
577
|
901
|
|
|
|
|
|
sv = newSViv(PTR2IV(cb)); |
|
578
|
901
|
|
|
|
|
|
hv_store(g_callback_registry, name, strlen(name), sv, 0); |
|
579
|
901
|
|
|
|
|
|
} |
|
580
|
|
|
|
|
|
|
|
|
581
|
|
|
|
|
|
|
/* Public API for XS modules to register predicates */ |
|
582
|
0
|
|
|
|
|
|
PERL_CALLCONV void funcutil_register_predicate_xs(pTHX_ const char *name, |
|
583
|
|
|
|
|
|
|
UtilPredicateFunc func) { |
|
584
|
|
|
|
|
|
|
RegisteredCallback *cb; |
|
585
|
|
|
|
|
|
|
SV *sv; |
|
586
|
|
|
|
|
|
|
|
|
587
|
0
|
|
|
|
|
|
init_callback_registry(aTHX); |
|
588
|
|
|
|
|
|
|
|
|
589
|
|
|
|
|
|
|
/* Check if already registered */ |
|
590
|
0
|
0
|
|
|
|
|
if (get_registered_callback(aTHX_ name)) { |
|
591
|
0
|
|
|
|
|
|
croak("Callback '%s' is already registered", name); |
|
592
|
|
|
|
|
|
|
} |
|
593
|
|
|
|
|
|
|
|
|
594
|
0
|
|
|
|
|
|
Newxz(cb, 1, RegisteredCallback); |
|
595
|
0
|
|
|
|
|
|
cb->name = savepv(name); |
|
596
|
0
|
|
|
|
|
|
cb->predicate = func; |
|
597
|
0
|
|
|
|
|
|
cb->mapper = NULL; |
|
598
|
0
|
|
|
|
|
|
cb->reducer = NULL; |
|
599
|
0
|
|
|
|
|
|
cb->perl_callback = NULL; |
|
600
|
|
|
|
|
|
|
|
|
601
|
0
|
|
|
|
|
|
sv = newSViv(PTR2IV(cb)); |
|
602
|
0
|
|
|
|
|
|
hv_store(g_callback_registry, name, strlen(name), sv, 0); |
|
603
|
0
|
|
|
|
|
|
} |
|
604
|
|
|
|
|
|
|
|
|
605
|
|
|
|
|
|
|
/* Public API for XS modules to register mappers */ |
|
606
|
0
|
|
|
|
|
|
PERL_CALLCONV void funcutil_register_mapper_xs(pTHX_ const char *name, |
|
607
|
|
|
|
|
|
|
UtilMapFunc func) { |
|
608
|
|
|
|
|
|
|
RegisteredCallback *cb; |
|
609
|
|
|
|
|
|
|
SV *sv; |
|
610
|
|
|
|
|
|
|
|
|
611
|
0
|
|
|
|
|
|
init_callback_registry(aTHX); |
|
612
|
|
|
|
|
|
|
|
|
613
|
0
|
0
|
|
|
|
|
if (get_registered_callback(aTHX_ name)) { |
|
614
|
0
|
|
|
|
|
|
croak("Callback '%s' is already registered", name); |
|
615
|
|
|
|
|
|
|
} |
|
616
|
|
|
|
|
|
|
|
|
617
|
0
|
|
|
|
|
|
Newxz(cb, 1, RegisteredCallback); |
|
618
|
0
|
|
|
|
|
|
cb->name = savepv(name); |
|
619
|
0
|
|
|
|
|
|
cb->predicate = NULL; |
|
620
|
0
|
|
|
|
|
|
cb->mapper = func; |
|
621
|
0
|
|
|
|
|
|
cb->reducer = NULL; |
|
622
|
0
|
|
|
|
|
|
cb->perl_callback = NULL; |
|
623
|
|
|
|
|
|
|
|
|
624
|
0
|
|
|
|
|
|
sv = newSViv(PTR2IV(cb)); |
|
625
|
0
|
|
|
|
|
|
hv_store(g_callback_registry, name, strlen(name), sv, 0); |
|
626
|
0
|
|
|
|
|
|
} |
|
627
|
|
|
|
|
|
|
|
|
628
|
|
|
|
|
|
|
/* Public API for XS modules to register reducers */ |
|
629
|
0
|
|
|
|
|
|
PERL_CALLCONV void funcutil_register_reducer_xs(pTHX_ const char *name, |
|
630
|
|
|
|
|
|
|
UtilReduceFunc func) { |
|
631
|
|
|
|
|
|
|
RegisteredCallback *cb; |
|
632
|
|
|
|
|
|
|
SV *sv; |
|
633
|
|
|
|
|
|
|
|
|
634
|
0
|
|
|
|
|
|
init_callback_registry(aTHX); |
|
635
|
|
|
|
|
|
|
|
|
636
|
0
|
0
|
|
|
|
|
if (get_registered_callback(aTHX_ name)) { |
|
637
|
0
|
|
|
|
|
|
croak("Callback '%s' is already registered", name); |
|
638
|
|
|
|
|
|
|
} |
|
639
|
|
|
|
|
|
|
|
|
640
|
0
|
|
|
|
|
|
Newxz(cb, 1, RegisteredCallback); |
|
641
|
0
|
|
|
|
|
|
cb->name = savepv(name); |
|
642
|
0
|
|
|
|
|
|
cb->predicate = NULL; |
|
643
|
0
|
|
|
|
|
|
cb->mapper = NULL; |
|
644
|
0
|
|
|
|
|
|
cb->reducer = func; |
|
645
|
0
|
|
|
|
|
|
cb->perl_callback = NULL; |
|
646
|
|
|
|
|
|
|
|
|
647
|
0
|
|
|
|
|
|
sv = newSViv(PTR2IV(cb)); |
|
648
|
0
|
|
|
|
|
|
hv_store(g_callback_registry, name, strlen(name), sv, 0); |
|
649
|
0
|
|
|
|
|
|
} |
|
650
|
|
|
|
|
|
|
|
|
651
|
|
|
|
|
|
|
/* Check if a callback exists */ |
|
652
|
6018
|
|
|
|
|
|
static bool has_callback(pTHX_ const char *name) { |
|
653
|
6018
|
|
|
|
|
|
return get_registered_callback(aTHX_ name) != NULL; |
|
654
|
|
|
|
|
|
|
} |
|
655
|
|
|
|
|
|
|
|
|
656
|
|
|
|
|
|
|
/* List all registered callbacks */ |
|
657
|
1006
|
|
|
|
|
|
static AV* list_callbacks(pTHX) { |
|
658
|
|
|
|
|
|
|
AV *result; |
|
659
|
|
|
|
|
|
|
HE *entry; |
|
660
|
|
|
|
|
|
|
|
|
661
|
1006
|
|
|
|
|
|
result = newAV(); |
|
662
|
1006
|
50
|
|
|
|
|
if (!g_callback_registry) return result; |
|
663
|
|
|
|
|
|
|
|
|
664
|
1006
|
|
|
|
|
|
hv_iterinit(g_callback_registry); |
|
665
|
18116
|
100
|
|
|
|
|
while ((entry = hv_iternext(g_callback_registry))) { |
|
666
|
|
|
|
|
|
|
I32 klen; |
|
667
|
17110
|
|
|
|
|
|
char *key = hv_iterkey(entry, &klen); |
|
668
|
17110
|
|
|
|
|
|
av_push(result, newSVpvn(key, klen)); |
|
669
|
|
|
|
|
|
|
} |
|
670
|
1006
|
|
|
|
|
|
return result; |
|
671
|
|
|
|
|
|
|
} |
|
672
|
|
|
|
|
|
|
|
|
673
|
|
|
|
|
|
|
/* Initialize built-in callbacks (called from BOOT) */ |
|
674
|
53
|
|
|
|
|
|
static void init_builtin_callbacks(pTHX) { |
|
675
|
53
|
|
|
|
|
|
register_builtin_predicate(aTHX_ ":is_defined", builtin_is_defined); |
|
676
|
53
|
|
|
|
|
|
register_builtin_predicate(aTHX_ ":is_true", builtin_is_true); |
|
677
|
53
|
|
|
|
|
|
register_builtin_predicate(aTHX_ ":is_false", builtin_is_false); |
|
678
|
53
|
|
|
|
|
|
register_builtin_predicate(aTHX_ ":is_ref", builtin_is_ref); |
|
679
|
53
|
|
|
|
|
|
register_builtin_predicate(aTHX_ ":is_array", builtin_is_array); |
|
680
|
53
|
|
|
|
|
|
register_builtin_predicate(aTHX_ ":is_hash", builtin_is_hash); |
|
681
|
53
|
|
|
|
|
|
register_builtin_predicate(aTHX_ ":is_code", builtin_is_code); |
|
682
|
53
|
|
|
|
|
|
register_builtin_predicate(aTHX_ ":is_positive", builtin_is_positive); |
|
683
|
53
|
|
|
|
|
|
register_builtin_predicate(aTHX_ ":is_negative", builtin_is_negative); |
|
684
|
53
|
|
|
|
|
|
register_builtin_predicate(aTHX_ ":is_zero", builtin_is_zero); |
|
685
|
53
|
|
|
|
|
|
register_builtin_predicate(aTHX_ ":is_even", builtin_is_even); |
|
686
|
53
|
|
|
|
|
|
register_builtin_predicate(aTHX_ ":is_odd", builtin_is_odd); |
|
687
|
53
|
|
|
|
|
|
register_builtin_predicate(aTHX_ ":is_empty", builtin_is_empty); |
|
688
|
53
|
|
|
|
|
|
register_builtin_predicate(aTHX_ ":is_nonempty", builtin_is_nonempty); |
|
689
|
53
|
|
|
|
|
|
register_builtin_predicate(aTHX_ ":is_string", builtin_is_string); |
|
690
|
53
|
|
|
|
|
|
register_builtin_predicate(aTHX_ ":is_number", builtin_is_number); |
|
691
|
53
|
|
|
|
|
|
register_builtin_predicate(aTHX_ ":is_integer", builtin_is_integer); |
|
692
|
53
|
|
|
|
|
|
} |
|
693
|
|
|
|
|
|
|
|
|
694
|
|
|
|
|
|
|
/* ============================================ |
|
695
|
|
|
|
|
|
|
Custom OP implementations - fastest path |
|
696
|
|
|
|
|
|
|
============================================ */ |
|
697
|
|
|
|
|
|
|
|
|
698
|
|
|
|
|
|
|
/* identity: just return the top of stack */ |
|
699
|
0
|
|
|
|
|
|
static OP* pp_identity(pTHX) { |
|
700
|
|
|
|
|
|
|
/* Value already on stack, nothing to do */ |
|
701
|
0
|
|
|
|
|
|
return NORMAL; |
|
702
|
|
|
|
|
|
|
} |
|
703
|
|
|
|
|
|
|
|
|
704
|
|
|
|
|
|
|
/* always: push stored value from op_targ index */ |
|
705
|
0
|
|
|
|
|
|
static OP* pp_always(pTHX) { |
|
706
|
0
|
|
|
|
|
|
dSP; |
|
707
|
0
|
|
|
|
|
|
IV idx = PL_op->op_targ; |
|
708
|
0
|
0
|
|
|
|
|
XPUSHs(g_always_values[idx]); |
|
709
|
0
|
|
|
|
|
|
RETURN; |
|
710
|
|
|
|
|
|
|
} |
|
711
|
|
|
|
|
|
|
|
|
712
|
|
|
|
|
|
|
/* clamp: 3 values on stack, return clamped */ |
|
713
|
30
|
|
|
|
|
|
static OP* pp_clamp(pTHX) { |
|
714
|
30
|
|
|
|
|
|
dSP; dMARK; dORIGMARK; |
|
715
|
|
|
|
|
|
|
SV *val_sv, *min_sv, *max_sv; |
|
716
|
|
|
|
|
|
|
NV value, min, max, result; |
|
717
|
|
|
|
|
|
|
|
|
718
|
|
|
|
|
|
|
/* We get 3 args on stack after the mark */ |
|
719
|
30
|
50
|
|
|
|
|
if (SP - MARK != 3) { |
|
720
|
|
|
|
|
|
|
/* Fallback: just use direct POPs if no mark context */ |
|
721
|
0
|
|
|
|
|
|
SP = ORIGMARK; |
|
722
|
0
|
|
|
|
|
|
PUTBACK; |
|
723
|
|
|
|
|
|
|
/* Pop without mark - shouldn't happen in list context */ |
|
724
|
0
|
|
|
|
|
|
dSP; |
|
725
|
0
|
|
|
|
|
|
max_sv = POPs; |
|
726
|
0
|
|
|
|
|
|
min_sv = POPs; |
|
727
|
0
|
|
|
|
|
|
val_sv = POPs; |
|
728
|
|
|
|
|
|
|
} else { |
|
729
|
30
|
|
|
|
|
|
val_sv = MARK[1]; |
|
730
|
30
|
|
|
|
|
|
min_sv = MARK[2]; |
|
731
|
30
|
|
|
|
|
|
max_sv = MARK[3]; |
|
732
|
30
|
|
|
|
|
|
SP = ORIGMARK; /* reset stack to before args */ |
|
733
|
|
|
|
|
|
|
} |
|
734
|
|
|
|
|
|
|
|
|
735
|
30
|
|
|
|
|
|
value = SvNV(val_sv); |
|
736
|
30
|
|
|
|
|
|
min = SvNV(min_sv); |
|
737
|
30
|
|
|
|
|
|
max = SvNV(max_sv); |
|
738
|
|
|
|
|
|
|
|
|
739
|
30
|
100
|
|
|
|
|
if (value < min) { |
|
740
|
6
|
|
|
|
|
|
result = min; |
|
741
|
24
|
100
|
|
|
|
|
} else if (value > max) { |
|
742
|
7
|
|
|
|
|
|
result = max; |
|
743
|
|
|
|
|
|
|
} else { |
|
744
|
17
|
|
|
|
|
|
result = value; |
|
745
|
|
|
|
|
|
|
} |
|
746
|
|
|
|
|
|
|
|
|
747
|
30
|
|
|
|
|
|
PUSHs(sv_2mortal(newSVnv(result))); |
|
748
|
30
|
|
|
|
|
|
RETURN; |
|
749
|
|
|
|
|
|
|
} |
|
750
|
|
|
|
|
|
|
|
|
751
|
|
|
|
|
|
|
/* nvl: 2 values on stack, return first if defined */ |
|
752
|
0
|
|
|
|
|
|
static OP* pp_nvl(pTHX) { |
|
753
|
0
|
|
|
|
|
|
dSP; |
|
754
|
0
|
|
|
|
|
|
SV *def_sv = POPs; |
|
755
|
0
|
|
|
|
|
|
SV *val_sv = TOPs; |
|
756
|
|
|
|
|
|
|
|
|
757
|
0
|
0
|
|
|
|
|
if (!SvOK(val_sv)) { |
|
758
|
0
|
|
|
|
|
|
SETs(def_sv); |
|
759
|
|
|
|
|
|
|
} |
|
760
|
0
|
|
|
|
|
|
RETURN; |
|
761
|
|
|
|
|
|
|
} |
|
762
|
|
|
|
|
|
|
|
|
763
|
|
|
|
|
|
|
/* ============================================ |
|
764
|
|
|
|
|
|
|
Type predicate custom ops - blazing fast! |
|
765
|
|
|
|
|
|
|
These are the fastest possible type checks: |
|
766
|
|
|
|
|
|
|
single SV flag check, no function call overhead |
|
767
|
|
|
|
|
|
|
============================================ */ |
|
768
|
|
|
|
|
|
|
|
|
769
|
|
|
|
|
|
|
/* is_ref: check if value is a reference */ |
|
770
|
10
|
|
|
|
|
|
static OP* pp_is_ref(pTHX) { |
|
771
|
10
|
|
|
|
|
|
dSP; |
|
772
|
10
|
|
|
|
|
|
SV *sv = TOPs; |
|
773
|
10
|
100
|
|
|
|
|
SETs(SvROK(sv) ? &PL_sv_yes : &PL_sv_no); |
|
774
|
10
|
|
|
|
|
|
RETURN; |
|
775
|
|
|
|
|
|
|
} |
|
776
|
|
|
|
|
|
|
|
|
777
|
|
|
|
|
|
|
/* is_array: check if value is an arrayref */ |
|
778
|
4
|
|
|
|
|
|
static OP* pp_is_array(pTHX) { |
|
779
|
4
|
|
|
|
|
|
dSP; |
|
780
|
4
|
|
|
|
|
|
SV *sv = TOPs; |
|
781
|
4
|
100
|
|
|
|
|
SETs((SvROK(sv) && SvTYPE(SvRV(sv)) == SVt_PVAV) ? &PL_sv_yes : &PL_sv_no); |
|
|
|
100
|
|
|
|
|
|
|
782
|
4
|
|
|
|
|
|
RETURN; |
|
783
|
|
|
|
|
|
|
} |
|
784
|
|
|
|
|
|
|
|
|
785
|
|
|
|
|
|
|
/* is_hash: check if value is a hashref */ |
|
786
|
3
|
|
|
|
|
|
static OP* pp_is_hash(pTHX) { |
|
787
|
3
|
|
|
|
|
|
dSP; |
|
788
|
3
|
|
|
|
|
|
SV *sv = TOPs; |
|
789
|
3
|
100
|
|
|
|
|
SETs((SvROK(sv) && SvTYPE(SvRV(sv)) == SVt_PVHV) ? &PL_sv_yes : &PL_sv_no); |
|
|
|
100
|
|
|
|
|
|
|
790
|
3
|
|
|
|
|
|
RETURN; |
|
791
|
|
|
|
|
|
|
} |
|
792
|
|
|
|
|
|
|
|
|
793
|
|
|
|
|
|
|
/* is_code: check if value is a coderef */ |
|
794
|
3
|
|
|
|
|
|
static OP* pp_is_code(pTHX) { |
|
795
|
3
|
|
|
|
|
|
dSP; |
|
796
|
3
|
|
|
|
|
|
SV *sv = TOPs; |
|
797
|
3
|
100
|
|
|
|
|
SETs((SvROK(sv) && SvTYPE(SvRV(sv)) == SVt_PVCV) ? &PL_sv_yes : &PL_sv_no); |
|
|
|
100
|
|
|
|
|
|
|
798
|
3
|
|
|
|
|
|
RETURN; |
|
799
|
|
|
|
|
|
|
} |
|
800
|
|
|
|
|
|
|
|
|
801
|
|
|
|
|
|
|
/* is_defined: check if value is defined */ |
|
802
|
9
|
|
|
|
|
|
static OP* pp_is_defined(pTHX) { |
|
803
|
9
|
|
|
|
|
|
dSP; |
|
804
|
9
|
|
|
|
|
|
SV *sv = TOPs; |
|
805
|
9
|
100
|
|
|
|
|
SETs(SvOK(sv) ? &PL_sv_yes : &PL_sv_no); |
|
806
|
9
|
|
|
|
|
|
RETURN; |
|
807
|
|
|
|
|
|
|
} |
|
808
|
|
|
|
|
|
|
|
|
809
|
|
|
|
|
|
|
/* ============================================ |
|
810
|
|
|
|
|
|
|
String predicate custom ops - blazing fast! |
|
811
|
|
|
|
|
|
|
Direct SvPV/SvCUR access, no function overhead |
|
812
|
|
|
|
|
|
|
============================================ */ |
|
813
|
|
|
|
|
|
|
|
|
814
|
|
|
|
|
|
|
/* is_empty: check if string is undefined or empty */ |
|
815
|
4
|
|
|
|
|
|
static OP* pp_is_empty(pTHX) { |
|
816
|
4
|
|
|
|
|
|
dSP; |
|
817
|
4
|
|
|
|
|
|
SV *sv = TOPs; |
|
818
|
|
|
|
|
|
|
/* Empty if: undefined OR length is 0 */ |
|
819
|
4
|
100
|
|
|
|
|
if (!SvOK(sv)) { |
|
820
|
1
|
|
|
|
|
|
SETs(&PL_sv_yes); |
|
821
|
|
|
|
|
|
|
} else { |
|
822
|
|
|
|
|
|
|
STRLEN len; |
|
823
|
3
|
|
|
|
|
|
SvPV(sv, len); |
|
824
|
3
|
100
|
|
|
|
|
SETs(len == 0 ? &PL_sv_yes : &PL_sv_no); |
|
825
|
|
|
|
|
|
|
} |
|
826
|
4
|
|
|
|
|
|
RETURN; |
|
827
|
|
|
|
|
|
|
} |
|
828
|
|
|
|
|
|
|
|
|
829
|
|
|
|
|
|
|
/* starts_with: check if string starts with prefix */ |
|
830
|
13
|
|
|
|
|
|
static OP* pp_starts_with(pTHX) { |
|
831
|
13
|
|
|
|
|
|
dSP; |
|
832
|
13
|
|
|
|
|
|
SV *prefix_sv = POPs; |
|
833
|
13
|
|
|
|
|
|
SV *str_sv = TOPs; |
|
834
|
|
|
|
|
|
|
|
|
835
|
13
|
50
|
|
|
|
|
if (!SvOK(str_sv) || !SvOK(prefix_sv)) { |
|
|
|
50
|
|
|
|
|
|
|
836
|
0
|
|
|
|
|
|
SETs(&PL_sv_no); |
|
837
|
0
|
|
|
|
|
|
RETURN; |
|
838
|
|
|
|
|
|
|
} |
|
839
|
|
|
|
|
|
|
|
|
840
|
|
|
|
|
|
|
STRLEN str_len, prefix_len; |
|
841
|
13
|
|
|
|
|
|
const char *str = SvPV(str_sv, str_len); |
|
842
|
13
|
|
|
|
|
|
const char *prefix = SvPV(prefix_sv, prefix_len); |
|
843
|
|
|
|
|
|
|
|
|
844
|
13
|
50
|
|
|
|
|
if (prefix_len > str_len) { |
|
845
|
0
|
|
|
|
|
|
SETs(&PL_sv_no); |
|
846
|
13
|
100
|
|
|
|
|
} else if (prefix_len == 0) { |
|
847
|
1
|
|
|
|
|
|
SETs(&PL_sv_yes); /* Empty prefix always matches */ |
|
848
|
|
|
|
|
|
|
} else { |
|
849
|
12
|
100
|
|
|
|
|
SETs(memcmp(str, prefix, prefix_len) == 0 ? &PL_sv_yes : &PL_sv_no); |
|
850
|
|
|
|
|
|
|
} |
|
851
|
13
|
|
|
|
|
|
RETURN; |
|
852
|
|
|
|
|
|
|
} |
|
853
|
|
|
|
|
|
|
|
|
854
|
|
|
|
|
|
|
/* ends_with: check if string ends with suffix */ |
|
855
|
4
|
|
|
|
|
|
static OP* pp_ends_with(pTHX) { |
|
856
|
4
|
|
|
|
|
|
dSP; |
|
857
|
4
|
|
|
|
|
|
SV *suffix_sv = POPs; |
|
858
|
4
|
|
|
|
|
|
SV *str_sv = TOPs; |
|
859
|
|
|
|
|
|
|
|
|
860
|
4
|
50
|
|
|
|
|
if (!SvOK(str_sv) || !SvOK(suffix_sv)) { |
|
|
|
50
|
|
|
|
|
|
|
861
|
0
|
|
|
|
|
|
SETs(&PL_sv_no); |
|
862
|
0
|
|
|
|
|
|
RETURN; |
|
863
|
|
|
|
|
|
|
} |
|
864
|
|
|
|
|
|
|
|
|
865
|
|
|
|
|
|
|
STRLEN str_len, suffix_len; |
|
866
|
4
|
|
|
|
|
|
const char *str = SvPV(str_sv, str_len); |
|
867
|
4
|
|
|
|
|
|
const char *suffix = SvPV(suffix_sv, suffix_len); |
|
868
|
|
|
|
|
|
|
|
|
869
|
4
|
50
|
|
|
|
|
if (suffix_len > str_len) { |
|
870
|
0
|
|
|
|
|
|
SETs(&PL_sv_no); |
|
871
|
4
|
100
|
|
|
|
|
} else if (suffix_len == 0) { |
|
872
|
1
|
|
|
|
|
|
SETs(&PL_sv_yes); /* Empty suffix always matches */ |
|
873
|
|
|
|
|
|
|
} else { |
|
874
|
3
|
|
|
|
|
|
const char *str_end = str + str_len - suffix_len; |
|
875
|
3
|
100
|
|
|
|
|
SETs(memcmp(str_end, suffix, suffix_len) == 0 ? &PL_sv_yes : &PL_sv_no); |
|
876
|
|
|
|
|
|
|
} |
|
877
|
4
|
|
|
|
|
|
RETURN; |
|
878
|
|
|
|
|
|
|
} |
|
879
|
|
|
|
|
|
|
|
|
880
|
|
|
|
|
|
|
/* ============================================ |
|
881
|
|
|
|
|
|
|
Boolean/Truthiness custom ops - blazing fast! |
|
882
|
|
|
|
|
|
|
Direct SvTRUE check, minimal overhead |
|
883
|
|
|
|
|
|
|
============================================ */ |
|
884
|
|
|
|
|
|
|
|
|
885
|
|
|
|
|
|
|
/* is_true: check if value is truthy (Perl truth semantics) */ |
|
886
|
5
|
|
|
|
|
|
static OP* pp_is_true(pTHX) { |
|
887
|
5
|
|
|
|
|
|
dSP; |
|
888
|
5
|
|
|
|
|
|
SV *sv = TOPs; |
|
889
|
5
|
100
|
|
|
|
|
SETs(SvTRUE(sv) ? &PL_sv_yes : &PL_sv_no); |
|
890
|
5
|
|
|
|
|
|
RETURN; |
|
891
|
|
|
|
|
|
|
} |
|
892
|
|
|
|
|
|
|
|
|
893
|
|
|
|
|
|
|
/* is_false: check if value is falsy (Perl truth semantics) */ |
|
894
|
5
|
|
|
|
|
|
static OP* pp_is_false(pTHX) { |
|
895
|
5
|
|
|
|
|
|
dSP; |
|
896
|
5
|
|
|
|
|
|
SV *sv = TOPs; |
|
897
|
5
|
100
|
|
|
|
|
SETs(SvTRUE(sv) ? &PL_sv_no : &PL_sv_yes); |
|
898
|
5
|
|
|
|
|
|
RETURN; |
|
899
|
|
|
|
|
|
|
} |
|
900
|
|
|
|
|
|
|
|
|
901
|
|
|
|
|
|
|
/* bool: normalize to boolean (1 or empty string) */ |
|
902
|
7
|
|
|
|
|
|
static OP* pp_bool(pTHX) { |
|
903
|
7
|
|
|
|
|
|
dSP; |
|
904
|
7
|
|
|
|
|
|
SV *sv = TOPs; |
|
905
|
7
|
100
|
|
|
|
|
SETs(SvTRUE(sv) ? &PL_sv_yes : &PL_sv_no); |
|
906
|
7
|
|
|
|
|
|
RETURN; |
|
907
|
|
|
|
|
|
|
} |
|
908
|
|
|
|
|
|
|
|
|
909
|
|
|
|
|
|
|
/* ============================================ |
|
910
|
|
|
|
|
|
|
Extended type predicate custom ops - blazing fast! |
|
911
|
|
|
|
|
|
|
============================================ */ |
|
912
|
|
|
|
|
|
|
|
|
913
|
|
|
|
|
|
|
/* is_num: check if value is numeric (has numeric value or looks like number) */ |
|
914
|
6
|
|
|
|
|
|
static OP* pp_is_num(pTHX) { |
|
915
|
6
|
|
|
|
|
|
dSP; |
|
916
|
6
|
|
|
|
|
|
SV *sv = TOPs; |
|
917
|
|
|
|
|
|
|
/* SvNIOK: has numeric (NV or IV) value cached */ |
|
918
|
|
|
|
|
|
|
/* Also check looks_like_number for strings that can be numbers */ |
|
919
|
6
|
100
|
|
|
|
|
SETs((SvNIOK(sv) || looks_like_number(sv)) ? &PL_sv_yes : &PL_sv_no); |
|
|
|
100
|
|
|
|
|
|
|
920
|
6
|
|
|
|
|
|
RETURN; |
|
921
|
|
|
|
|
|
|
} |
|
922
|
|
|
|
|
|
|
|
|
923
|
|
|
|
|
|
|
/* is_int: check if value is an integer */ |
|
924
|
5
|
|
|
|
|
|
static OP* pp_is_int(pTHX) { |
|
925
|
5
|
|
|
|
|
|
dSP; |
|
926
|
5
|
|
|
|
|
|
SV *sv = TOPs; |
|
927
|
|
|
|
|
|
|
/* SvIOK: has integer value cached */ |
|
928
|
5
|
100
|
|
|
|
|
if (SvIOK(sv)) { |
|
929
|
3
|
|
|
|
|
|
SETs(&PL_sv_yes); |
|
930
|
2
|
100
|
|
|
|
|
} else if (SvNOK(sv)) { |
|
931
|
|
|
|
|
|
|
/* It's a float - check if it's a whole number */ |
|
932
|
1
|
|
|
|
|
|
NV nv = SvNV(sv); |
|
933
|
1
|
50
|
|
|
|
|
SETs((nv == (NV)(IV)nv) ? &PL_sv_yes : &PL_sv_no); |
|
934
|
1
|
50
|
|
|
|
|
} else if (looks_like_number(sv)) { |
|
935
|
|
|
|
|
|
|
/* String that looks like a number - check if integer */ |
|
936
|
|
|
|
|
|
|
STRLEN len; |
|
937
|
0
|
|
|
|
|
|
const char *pv = SvPV(sv, len); |
|
938
|
|
|
|
|
|
|
/* Simple check: no decimal point or exponent */ |
|
939
|
0
|
|
|
|
|
|
bool has_dot = FALSE; |
|
940
|
|
|
|
|
|
|
STRLEN i; |
|
941
|
0
|
0
|
|
|
|
|
for (i = 0; i < len; i++) { |
|
942
|
0
|
0
|
|
|
|
|
if (pv[i] == '.' || pv[i] == 'e' || pv[i] == 'E') { |
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
943
|
0
|
|
|
|
|
|
has_dot = TRUE; |
|
944
|
0
|
|
|
|
|
|
break; |
|
945
|
|
|
|
|
|
|
} |
|
946
|
|
|
|
|
|
|
} |
|
947
|
0
|
0
|
|
|
|
|
if (has_dot) { |
|
948
|
|
|
|
|
|
|
/* Has decimal - check if value is actually integer */ |
|
949
|
0
|
|
|
|
|
|
NV nv = SvNV(sv); |
|
950
|
0
|
0
|
|
|
|
|
SETs((nv == (NV)(IV)nv) ? &PL_sv_yes : &PL_sv_no); |
|
951
|
|
|
|
|
|
|
} else { |
|
952
|
0
|
|
|
|
|
|
SETs(&PL_sv_yes); |
|
953
|
|
|
|
|
|
|
} |
|
954
|
|
|
|
|
|
|
} else { |
|
955
|
1
|
|
|
|
|
|
SETs(&PL_sv_no); |
|
956
|
|
|
|
|
|
|
} |
|
957
|
5
|
|
|
|
|
|
RETURN; |
|
958
|
|
|
|
|
|
|
} |
|
959
|
|
|
|
|
|
|
|
|
960
|
|
|
|
|
|
|
/* is_blessed: check if value is a blessed reference */ |
|
961
|
3
|
|
|
|
|
|
static OP* pp_is_blessed(pTHX) { |
|
962
|
3
|
|
|
|
|
|
dSP; |
|
963
|
3
|
|
|
|
|
|
SV *sv = TOPs; |
|
964
|
3
|
100
|
|
|
|
|
SETs(sv_isobject(sv) ? &PL_sv_yes : &PL_sv_no); |
|
965
|
3
|
|
|
|
|
|
RETURN; |
|
966
|
|
|
|
|
|
|
} |
|
967
|
|
|
|
|
|
|
|
|
968
|
|
|
|
|
|
|
/* is_scalar_ref: check if value is a scalar reference (not array/hash/code/etc) */ |
|
969
|
3
|
|
|
|
|
|
static OP* pp_is_scalar_ref(pTHX) { |
|
970
|
3
|
|
|
|
|
|
dSP; |
|
971
|
3
|
|
|
|
|
|
SV *sv = TOPs; |
|
972
|
3
|
100
|
|
|
|
|
if (SvROK(sv)) { |
|
973
|
2
|
|
|
|
|
|
SV *rv = SvRV(sv); |
|
974
|
2
|
|
|
|
|
|
svtype type = SvTYPE(rv); |
|
975
|
|
|
|
|
|
|
/* Scalar refs are < SVt_PVAV (array) */ |
|
976
|
2
|
100
|
|
|
|
|
SETs((type < SVt_PVAV) ? &PL_sv_yes : &PL_sv_no); |
|
977
|
|
|
|
|
|
|
} else { |
|
978
|
1
|
|
|
|
|
|
SETs(&PL_sv_no); |
|
979
|
|
|
|
|
|
|
} |
|
980
|
3
|
|
|
|
|
|
RETURN; |
|
981
|
|
|
|
|
|
|
} |
|
982
|
|
|
|
|
|
|
|
|
983
|
|
|
|
|
|
|
/* is_regex: check if value is a compiled regex */ |
|
984
|
3
|
|
|
|
|
|
static OP* pp_is_regex(pTHX) { |
|
985
|
3
|
|
|
|
|
|
dSP; |
|
986
|
3
|
|
|
|
|
|
SV *sv = TOPs; |
|
987
|
|
|
|
|
|
|
/* SvRXOK: check if SV is a regex (qr//) - available since Perl 5.10 */ |
|
988
|
3
|
100
|
|
|
|
|
SETs(SvRXOK(sv) ? &PL_sv_yes : &PL_sv_no); |
|
989
|
3
|
|
|
|
|
|
RETURN; |
|
990
|
|
|
|
|
|
|
} |
|
991
|
|
|
|
|
|
|
|
|
992
|
|
|
|
|
|
|
/* is_glob: check if value is a glob (*foo) */ |
|
993
|
2
|
|
|
|
|
|
static OP* pp_is_glob(pTHX) { |
|
994
|
2
|
|
|
|
|
|
dSP; |
|
995
|
2
|
|
|
|
|
|
SV *sv = TOPs; |
|
996
|
2
|
100
|
|
|
|
|
SETs((SvTYPE(sv) == SVt_PVGV) ? &PL_sv_yes : &PL_sv_no); |
|
997
|
2
|
|
|
|
|
|
RETURN; |
|
998
|
|
|
|
|
|
|
} |
|
999
|
|
|
|
|
|
|
|
|
1000
|
|
|
|
|
|
|
/* is_string: check if value is a plain scalar (defined, not a reference) */ |
|
1001
|
0
|
|
|
|
|
|
static OP* pp_is_string(pTHX) { |
|
1002
|
0
|
|
|
|
|
|
dSP; |
|
1003
|
0
|
|
|
|
|
|
SV *sv = TOPs; |
|
1004
|
0
|
0
|
|
|
|
|
SETs((SvOK(sv) && !SvROK(sv)) ? &PL_sv_yes : &PL_sv_no); |
|
|
|
0
|
|
|
|
|
|
|
1005
|
0
|
|
|
|
|
|
RETURN; |
|
1006
|
|
|
|
|
|
|
} |
|
1007
|
|
|
|
|
|
|
|
|
1008
|
|
|
|
|
|
|
/* ============================================ |
|
1009
|
|
|
|
|
|
|
Numeric predicate custom ops - blazing fast! |
|
1010
|
|
|
|
|
|
|
Direct SvNV comparison, minimal overhead |
|
1011
|
|
|
|
|
|
|
============================================ */ |
|
1012
|
|
|
|
|
|
|
|
|
1013
|
|
|
|
|
|
|
/* is_positive: check if value is > 0 */ |
|
1014
|
8
|
|
|
|
|
|
static OP* pp_is_positive(pTHX) { |
|
1015
|
8
|
|
|
|
|
|
dSP; |
|
1016
|
8
|
|
|
|
|
|
SV *sv = TOPs; |
|
1017
|
8
|
50
|
|
|
|
|
if (SvNIOK(sv) || looks_like_number(sv)) { |
|
|
|
0
|
|
|
|
|
|
|
1018
|
8
|
|
|
|
|
|
NV nv = SvNV(sv); |
|
1019
|
8
|
100
|
|
|
|
|
SETs((nv > 0) ? &PL_sv_yes : &PL_sv_no); |
|
1020
|
|
|
|
|
|
|
} else { |
|
1021
|
0
|
|
|
|
|
|
SETs(&PL_sv_no); |
|
1022
|
|
|
|
|
|
|
} |
|
1023
|
8
|
|
|
|
|
|
RETURN; |
|
1024
|
|
|
|
|
|
|
} |
|
1025
|
|
|
|
|
|
|
|
|
1026
|
|
|
|
|
|
|
/* is_negative: check if value is < 0 */ |
|
1027
|
4
|
|
|
|
|
|
static OP* pp_is_negative(pTHX) { |
|
1028
|
4
|
|
|
|
|
|
dSP; |
|
1029
|
4
|
|
|
|
|
|
SV *sv = TOPs; |
|
1030
|
4
|
50
|
|
|
|
|
if (SvNIOK(sv) || looks_like_number(sv)) { |
|
|
|
0
|
|
|
|
|
|
|
1031
|
4
|
|
|
|
|
|
NV nv = SvNV(sv); |
|
1032
|
4
|
100
|
|
|
|
|
SETs((nv < 0) ? &PL_sv_yes : &PL_sv_no); |
|
1033
|
|
|
|
|
|
|
} else { |
|
1034
|
0
|
|
|
|
|
|
SETs(&PL_sv_no); |
|
1035
|
|
|
|
|
|
|
} |
|
1036
|
4
|
|
|
|
|
|
RETURN; |
|
1037
|
|
|
|
|
|
|
} |
|
1038
|
|
|
|
|
|
|
|
|
1039
|
|
|
|
|
|
|
/* is_zero: check if value is == 0 */ |
|
1040
|
4
|
|
|
|
|
|
static OP* pp_is_zero(pTHX) { |
|
1041
|
4
|
|
|
|
|
|
dSP; |
|
1042
|
4
|
|
|
|
|
|
SV *sv = TOPs; |
|
1043
|
4
|
50
|
|
|
|
|
if (SvNIOK(sv) || looks_like_number(sv)) { |
|
|
|
0
|
|
|
|
|
|
|
1044
|
4
|
|
|
|
|
|
NV nv = SvNV(sv); |
|
1045
|
4
|
100
|
|
|
|
|
SETs((nv == 0) ? &PL_sv_yes : &PL_sv_no); |
|
1046
|
|
|
|
|
|
|
} else { |
|
1047
|
0
|
|
|
|
|
|
SETs(&PL_sv_no); |
|
1048
|
|
|
|
|
|
|
} |
|
1049
|
4
|
|
|
|
|
|
RETURN; |
|
1050
|
|
|
|
|
|
|
} |
|
1051
|
|
|
|
|
|
|
|
|
1052
|
|
|
|
|
|
|
/* ============================================ |
|
1053
|
|
|
|
|
|
|
Numeric utility custom ops |
|
1054
|
|
|
|
|
|
|
============================================ */ |
|
1055
|
|
|
|
|
|
|
|
|
1056
|
|
|
|
|
|
|
/* is_even: check if integer is even (single bitwise AND) */ |
|
1057
|
5
|
|
|
|
|
|
static OP* pp_is_even(pTHX) { |
|
1058
|
5
|
|
|
|
|
|
dSP; |
|
1059
|
5
|
|
|
|
|
|
SV *sv = TOPs; |
|
1060
|
5
|
50
|
|
|
|
|
if (SvIOK(sv)) { |
|
1061
|
5
|
100
|
|
|
|
|
SETs((SvIVX(sv) & 1) == 0 ? &PL_sv_yes : &PL_sv_no); |
|
1062
|
0
|
0
|
|
|
|
|
} else if (SvNIOK(sv)) { |
|
1063
|
0
|
|
|
|
|
|
NV nv = SvNV(sv); |
|
1064
|
0
|
0
|
|
|
|
|
if (nv == (NV)(IV)nv) { |
|
1065
|
0
|
0
|
|
|
|
|
SETs(((IV)nv & 1) == 0 ? &PL_sv_yes : &PL_sv_no); |
|
1066
|
|
|
|
|
|
|
} else { |
|
1067
|
0
|
|
|
|
|
|
SETs(&PL_sv_no); |
|
1068
|
|
|
|
|
|
|
} |
|
1069
|
0
|
0
|
|
|
|
|
} else if (looks_like_number(sv)) { |
|
1070
|
0
|
0
|
|
|
|
|
SETs((SvIV(sv) & 1) == 0 ? &PL_sv_yes : &PL_sv_no); |
|
1071
|
|
|
|
|
|
|
} else { |
|
1072
|
0
|
|
|
|
|
|
SETs(&PL_sv_no); |
|
1073
|
|
|
|
|
|
|
} |
|
1074
|
5
|
|
|
|
|
|
RETURN; |
|
1075
|
|
|
|
|
|
|
} |
|
1076
|
|
|
|
|
|
|
|
|
1077
|
|
|
|
|
|
|
/* is_odd: check if integer is odd (single bitwise AND) */ |
|
1078
|
5
|
|
|
|
|
|
static OP* pp_is_odd(pTHX) { |
|
1079
|
5
|
|
|
|
|
|
dSP; |
|
1080
|
5
|
|
|
|
|
|
SV *sv = TOPs; |
|
1081
|
5
|
50
|
|
|
|
|
if (SvIOK(sv)) { |
|
1082
|
5
|
100
|
|
|
|
|
SETs((SvIVX(sv) & 1) == 1 ? &PL_sv_yes : &PL_sv_no); |
|
1083
|
0
|
0
|
|
|
|
|
} else if (SvNIOK(sv)) { |
|
1084
|
0
|
|
|
|
|
|
NV nv = SvNV(sv); |
|
1085
|
0
|
0
|
|
|
|
|
if (nv == (NV)(IV)nv) { |
|
1086
|
0
|
0
|
|
|
|
|
SETs(((IV)nv & 1) == 1 ? &PL_sv_yes : &PL_sv_no); |
|
1087
|
|
|
|
|
|
|
} else { |
|
1088
|
0
|
|
|
|
|
|
SETs(&PL_sv_no); |
|
1089
|
|
|
|
|
|
|
} |
|
1090
|
0
|
0
|
|
|
|
|
} else if (looks_like_number(sv)) { |
|
1091
|
0
|
0
|
|
|
|
|
SETs((SvIV(sv) & 1) == 1 ? &PL_sv_yes : &PL_sv_no); |
|
1092
|
|
|
|
|
|
|
} else { |
|
1093
|
0
|
|
|
|
|
|
SETs(&PL_sv_no); |
|
1094
|
|
|
|
|
|
|
} |
|
1095
|
5
|
|
|
|
|
|
RETURN; |
|
1096
|
|
|
|
|
|
|
} |
|
1097
|
|
|
|
|
|
|
|
|
1098
|
|
|
|
|
|
|
/* is_between: check if value is between min and max (inclusive) */ |
|
1099
|
0
|
|
|
|
|
|
static OP* pp_is_between(pTHX) { |
|
1100
|
0
|
|
|
|
|
|
dSP; |
|
1101
|
0
|
|
|
|
|
|
SV *max_sv = POPs; |
|
1102
|
0
|
|
|
|
|
|
SV *min_sv = POPs; |
|
1103
|
0
|
|
|
|
|
|
SV *val_sv = TOPs; |
|
1104
|
|
|
|
|
|
|
|
|
1105
|
0
|
0
|
|
|
|
|
if (SvNIOK(val_sv) || looks_like_number(val_sv)) { |
|
|
|
0
|
|
|
|
|
|
|
1106
|
0
|
|
|
|
|
|
NV val = SvNV(val_sv); |
|
1107
|
0
|
|
|
|
|
|
NV min = SvNV(min_sv); |
|
1108
|
0
|
|
|
|
|
|
NV max = SvNV(max_sv); |
|
1109
|
0
|
0
|
|
|
|
|
SETs((val >= min && val <= max) ? &PL_sv_yes : &PL_sv_no); |
|
|
|
0
|
|
|
|
|
|
|
1110
|
|
|
|
|
|
|
} else { |
|
1111
|
0
|
|
|
|
|
|
SETs(&PL_sv_no); |
|
1112
|
|
|
|
|
|
|
} |
|
1113
|
0
|
|
|
|
|
|
RETURN; |
|
1114
|
|
|
|
|
|
|
} |
|
1115
|
|
|
|
|
|
|
|
|
1116
|
|
|
|
|
|
|
/* ============================================ |
|
1117
|
|
|
|
|
|
|
Collection custom ops - direct AvFILL/HvKEYS access |
|
1118
|
|
|
|
|
|
|
============================================ */ |
|
1119
|
|
|
|
|
|
|
|
|
1120
|
|
|
|
|
|
|
/* is_empty_array: check if arrayref is empty - direct AvFILL */ |
|
1121
|
3
|
|
|
|
|
|
static OP* pp_is_empty_array(pTHX) { |
|
1122
|
3
|
|
|
|
|
|
dSP; |
|
1123
|
3
|
|
|
|
|
|
SV *sv = TOPs; |
|
1124
|
3
|
100
|
|
|
|
|
if (SvROK(sv) && SvTYPE(SvRV(sv)) == SVt_PVAV) { |
|
|
|
50
|
|
|
|
|
|
|
1125
|
2
|
|
|
|
|
|
AV *av = (AV*)SvRV(sv); |
|
1126
|
2
|
50
|
|
|
|
|
SETs(AvFILL(av) < 0 ? &PL_sv_yes : &PL_sv_no); |
|
|
|
100
|
|
|
|
|
|
|
1127
|
|
|
|
|
|
|
} else { |
|
1128
|
1
|
|
|
|
|
|
SETs(&PL_sv_no); /* Not an arrayref */ |
|
1129
|
|
|
|
|
|
|
} |
|
1130
|
3
|
|
|
|
|
|
RETURN; |
|
1131
|
|
|
|
|
|
|
} |
|
1132
|
|
|
|
|
|
|
|
|
1133
|
|
|
|
|
|
|
/* is_empty_hash: check if hashref is empty - direct HvKEYS */ |
|
1134
|
3
|
|
|
|
|
|
static OP* pp_is_empty_hash(pTHX) { |
|
1135
|
3
|
|
|
|
|
|
dSP; |
|
1136
|
3
|
|
|
|
|
|
SV *sv = TOPs; |
|
1137
|
3
|
100
|
|
|
|
|
if (SvROK(sv) && SvTYPE(SvRV(sv)) == SVt_PVHV) { |
|
|
|
50
|
|
|
|
|
|
|
1138
|
2
|
|
|
|
|
|
HV *hv = (HV*)SvRV(sv); |
|
1139
|
2
|
50
|
|
|
|
|
SETs(HvKEYS(hv) == 0 ? &PL_sv_yes : &PL_sv_no); |
|
|
|
100
|
|
|
|
|
|
|
1140
|
|
|
|
|
|
|
} else { |
|
1141
|
1
|
|
|
|
|
|
SETs(&PL_sv_no); /* Not a hashref */ |
|
1142
|
|
|
|
|
|
|
} |
|
1143
|
3
|
|
|
|
|
|
RETURN; |
|
1144
|
|
|
|
|
|
|
} |
|
1145
|
|
|
|
|
|
|
|
|
1146
|
|
|
|
|
|
|
/* array_len: get array length - direct AvFILL + 1 */ |
|
1147
|
3
|
|
|
|
|
|
static OP* pp_array_len(pTHX) { |
|
1148
|
3
|
|
|
|
|
|
dSP; |
|
1149
|
3
|
|
|
|
|
|
SV *sv = TOPs; |
|
1150
|
3
|
50
|
|
|
|
|
if (SvROK(sv) && SvTYPE(SvRV(sv)) == SVt_PVAV) { |
|
|
|
50
|
|
|
|
|
|
|
1151
|
3
|
|
|
|
|
|
AV *av = (AV*)SvRV(sv); |
|
1152
|
3
|
50
|
|
|
|
|
SV *len = sv_2mortal(newSViv(AvFILL(av) + 1)); |
|
1153
|
3
|
|
|
|
|
|
SETs(len); |
|
1154
|
|
|
|
|
|
|
} else { |
|
1155
|
0
|
|
|
|
|
|
SETs(&PL_sv_undef); /* Not an arrayref */ |
|
1156
|
|
|
|
|
|
|
} |
|
1157
|
3
|
|
|
|
|
|
RETURN; |
|
1158
|
|
|
|
|
|
|
} |
|
1159
|
|
|
|
|
|
|
|
|
1160
|
|
|
|
|
|
|
/* hash_size: get hash key count - direct HvKEYS */ |
|
1161
|
3
|
|
|
|
|
|
static OP* pp_hash_size(pTHX) { |
|
1162
|
3
|
|
|
|
|
|
dSP; |
|
1163
|
3
|
|
|
|
|
|
SV *sv = TOPs; |
|
1164
|
3
|
50
|
|
|
|
|
if (SvROK(sv) && SvTYPE(SvRV(sv)) == SVt_PVHV) { |
|
|
|
50
|
|
|
|
|
|
|
1165
|
3
|
|
|
|
|
|
HV *hv = (HV*)SvRV(sv); |
|
1166
|
3
|
50
|
|
|
|
|
SV *size = sv_2mortal(newSViv(HvKEYS(hv))); |
|
1167
|
3
|
|
|
|
|
|
SETs(size); |
|
1168
|
|
|
|
|
|
|
} else { |
|
1169
|
0
|
|
|
|
|
|
SETs(&PL_sv_undef); /* Not a hashref */ |
|
1170
|
|
|
|
|
|
|
} |
|
1171
|
3
|
|
|
|
|
|
RETURN; |
|
1172
|
|
|
|
|
|
|
} |
|
1173
|
|
|
|
|
|
|
|
|
1174
|
|
|
|
|
|
|
/* array_first: get first element without slice overhead */ |
|
1175
|
3
|
|
|
|
|
|
static OP* pp_array_first(pTHX) { |
|
1176
|
3
|
|
|
|
|
|
dSP; |
|
1177
|
3
|
|
|
|
|
|
SV *sv = TOPs; |
|
1178
|
6
|
50
|
|
|
|
|
if (SvROK(sv) && SvTYPE(SvRV(sv)) == SVt_PVAV) { |
|
|
|
50
|
|
|
|
|
|
|
1179
|
3
|
|
|
|
|
|
AV *av = (AV*)SvRV(sv); |
|
1180
|
3
|
50
|
|
|
|
|
if (AvFILL(av) >= 0) { |
|
|
|
100
|
|
|
|
|
|
|
1181
|
2
|
|
|
|
|
|
SV **elem = av_fetch(av, 0, 0); |
|
1182
|
2
|
50
|
|
|
|
|
SETs(elem ? *elem : &PL_sv_undef); |
|
1183
|
|
|
|
|
|
|
} else { |
|
1184
|
1
|
|
|
|
|
|
SETs(&PL_sv_undef); /* Empty array */ |
|
1185
|
|
|
|
|
|
|
} |
|
1186
|
|
|
|
|
|
|
} else { |
|
1187
|
0
|
|
|
|
|
|
SETs(&PL_sv_undef); /* Not an arrayref */ |
|
1188
|
|
|
|
|
|
|
} |
|
1189
|
3
|
|
|
|
|
|
RETURN; |
|
1190
|
|
|
|
|
|
|
} |
|
1191
|
|
|
|
|
|
|
|
|
1192
|
|
|
|
|
|
|
/* array_last: get last element without slice overhead */ |
|
1193
|
3
|
|
|
|
|
|
static OP* pp_array_last(pTHX) { |
|
1194
|
3
|
|
|
|
|
|
dSP; |
|
1195
|
3
|
|
|
|
|
|
SV *sv = TOPs; |
|
1196
|
6
|
50
|
|
|
|
|
if (SvROK(sv) && SvTYPE(SvRV(sv)) == SVt_PVAV) { |
|
|
|
50
|
|
|
|
|
|
|
1197
|
3
|
|
|
|
|
|
AV *av = (AV*)SvRV(sv); |
|
1198
|
3
|
50
|
|
|
|
|
IV last_idx = AvFILL(av); |
|
1199
|
3
|
100
|
|
|
|
|
if (last_idx >= 0) { |
|
1200
|
2
|
|
|
|
|
|
SV **elem = av_fetch(av, last_idx, 0); |
|
1201
|
2
|
50
|
|
|
|
|
SETs(elem ? *elem : &PL_sv_undef); |
|
1202
|
|
|
|
|
|
|
} else { |
|
1203
|
1
|
|
|
|
|
|
SETs(&PL_sv_undef); /* Empty array */ |
|
1204
|
|
|
|
|
|
|
} |
|
1205
|
|
|
|
|
|
|
} else { |
|
1206
|
0
|
|
|
|
|
|
SETs(&PL_sv_undef); /* Not an arrayref */ |
|
1207
|
|
|
|
|
|
|
} |
|
1208
|
3
|
|
|
|
|
|
RETURN; |
|
1209
|
|
|
|
|
|
|
} |
|
1210
|
|
|
|
|
|
|
|
|
1211
|
|
|
|
|
|
|
|
|
1212
|
|
|
|
|
|
|
/* ============================================ |
|
1213
|
|
|
|
|
|
|
String manipulation custom ops |
|
1214
|
|
|
|
|
|
|
============================================ */ |
|
1215
|
|
|
|
|
|
|
|
|
1216
|
|
|
|
|
|
|
/* trim: remove leading/trailing whitespace */ |
|
1217
|
5
|
|
|
|
|
|
static OP* pp_trim(pTHX) { |
|
1218
|
5
|
|
|
|
|
|
dSP; |
|
1219
|
5
|
|
|
|
|
|
SV *sv = TOPs; |
|
1220
|
|
|
|
|
|
|
|
|
1221
|
5
|
50
|
|
|
|
|
if (!SvOK(sv)) { |
|
1222
|
0
|
|
|
|
|
|
SETs(&PL_sv_undef); |
|
1223
|
0
|
|
|
|
|
|
RETURN; |
|
1224
|
|
|
|
|
|
|
} |
|
1225
|
|
|
|
|
|
|
|
|
1226
|
|
|
|
|
|
|
STRLEN len; |
|
1227
|
5
|
|
|
|
|
|
const char *str = SvPV(sv, len); |
|
1228
|
5
|
|
|
|
|
|
const char *start = str; |
|
1229
|
5
|
|
|
|
|
|
const char *end = str + len; |
|
1230
|
|
|
|
|
|
|
|
|
1231
|
|
|
|
|
|
|
/* Skip leading whitespace */ |
|
1232
|
11
|
50
|
|
|
|
|
while (start < end && isSPACE(*start)) { |
|
|
|
100
|
|
|
|
|
|
|
1233
|
6
|
|
|
|
|
|
start++; |
|
1234
|
|
|
|
|
|
|
} |
|
1235
|
|
|
|
|
|
|
|
|
1236
|
|
|
|
|
|
|
/* Skip trailing whitespace */ |
|
1237
|
11
|
50
|
|
|
|
|
while (end > start && isSPACE(*(end - 1))) { |
|
|
|
100
|
|
|
|
|
|
|
1238
|
6
|
|
|
|
|
|
end--; |
|
1239
|
|
|
|
|
|
|
} |
|
1240
|
|
|
|
|
|
|
|
|
1241
|
|
|
|
|
|
|
/* Create new SV with trimmed content */ |
|
1242
|
5
|
|
|
|
|
|
SV *result = sv_2mortal(newSVpvn(start, end - start)); |
|
1243
|
5
|
|
|
|
|
|
SETs(result); |
|
1244
|
5
|
|
|
|
|
|
RETURN; |
|
1245
|
|
|
|
|
|
|
} |
|
1246
|
|
|
|
|
|
|
|
|
1247
|
|
|
|
|
|
|
/* ltrim: remove leading whitespace only */ |
|
1248
|
3
|
|
|
|
|
|
static OP* pp_ltrim(pTHX) { |
|
1249
|
3
|
|
|
|
|
|
dSP; |
|
1250
|
3
|
|
|
|
|
|
SV *sv = TOPs; |
|
1251
|
|
|
|
|
|
|
|
|
1252
|
3
|
50
|
|
|
|
|
if (!SvOK(sv)) { |
|
1253
|
0
|
|
|
|
|
|
SETs(&PL_sv_undef); |
|
1254
|
0
|
|
|
|
|
|
RETURN; |
|
1255
|
|
|
|
|
|
|
} |
|
1256
|
|
|
|
|
|
|
|
|
1257
|
|
|
|
|
|
|
STRLEN len; |
|
1258
|
3
|
|
|
|
|
|
const char *str = SvPV(sv, len); |
|
1259
|
3
|
|
|
|
|
|
const char *start = str; |
|
1260
|
3
|
|
|
|
|
|
const char *end = str + len; |
|
1261
|
|
|
|
|
|
|
|
|
1262
|
|
|
|
|
|
|
/* Skip leading whitespace */ |
|
1263
|
7
|
50
|
|
|
|
|
while (start < end && isSPACE(*start)) { |
|
|
|
100
|
|
|
|
|
|
|
1264
|
4
|
|
|
|
|
|
start++; |
|
1265
|
|
|
|
|
|
|
} |
|
1266
|
|
|
|
|
|
|
|
|
1267
|
3
|
|
|
|
|
|
SV *result = sv_2mortal(newSVpvn(start, end - start)); |
|
1268
|
3
|
|
|
|
|
|
SETs(result); |
|
1269
|
3
|
|
|
|
|
|
RETURN; |
|
1270
|
|
|
|
|
|
|
} |
|
1271
|
|
|
|
|
|
|
|
|
1272
|
|
|
|
|
|
|
/* rtrim: remove trailing whitespace only */ |
|
1273
|
3
|
|
|
|
|
|
static OP* pp_rtrim(pTHX) { |
|
1274
|
3
|
|
|
|
|
|
dSP; |
|
1275
|
3
|
|
|
|
|
|
SV *sv = TOPs; |
|
1276
|
|
|
|
|
|
|
|
|
1277
|
3
|
50
|
|
|
|
|
if (!SvOK(sv)) { |
|
1278
|
0
|
|
|
|
|
|
SETs(&PL_sv_undef); |
|
1279
|
0
|
|
|
|
|
|
RETURN; |
|
1280
|
|
|
|
|
|
|
} |
|
1281
|
|
|
|
|
|
|
|
|
1282
|
|
|
|
|
|
|
STRLEN len; |
|
1283
|
3
|
|
|
|
|
|
const char *str = SvPV(sv, len); |
|
1284
|
3
|
|
|
|
|
|
const char *end = str + len; |
|
1285
|
|
|
|
|
|
|
|
|
1286
|
|
|
|
|
|
|
/* Skip trailing whitespace */ |
|
1287
|
7
|
50
|
|
|
|
|
while (end > str && isSPACE(*(end - 1))) { |
|
|
|
100
|
|
|
|
|
|
|
1288
|
4
|
|
|
|
|
|
end--; |
|
1289
|
|
|
|
|
|
|
} |
|
1290
|
|
|
|
|
|
|
|
|
1291
|
3
|
|
|
|
|
|
SV *result = sv_2mortal(newSVpvn(str, end - str)); |
|
1292
|
3
|
|
|
|
|
|
SETs(result); |
|
1293
|
3
|
|
|
|
|
|
RETURN; |
|
1294
|
|
|
|
|
|
|
} |
|
1295
|
|
|
|
|
|
|
|
|
1296
|
|
|
|
|
|
|
/* ============================================ |
|
1297
|
|
|
|
|
|
|
Conditional custom ops |
|
1298
|
|
|
|
|
|
|
============================================ */ |
|
1299
|
|
|
|
|
|
|
|
|
1300
|
|
|
|
|
|
|
/* maybe: return $then if $val is defined, else undef */ |
|
1301
|
2
|
|
|
|
|
|
static OP* pp_maybe(pTHX) { |
|
1302
|
2
|
|
|
|
|
|
dSP; |
|
1303
|
2
|
|
|
|
|
|
SV *then_sv = POPs; |
|
1304
|
2
|
|
|
|
|
|
SV *val_sv = TOPs; |
|
1305
|
|
|
|
|
|
|
|
|
1306
|
2
|
100
|
|
|
|
|
if (SvOK(val_sv)) { |
|
1307
|
1
|
|
|
|
|
|
SETs(then_sv); |
|
1308
|
|
|
|
|
|
|
} else { |
|
1309
|
1
|
|
|
|
|
|
SETs(&PL_sv_undef); |
|
1310
|
|
|
|
|
|
|
} |
|
1311
|
2
|
|
|
|
|
|
RETURN; |
|
1312
|
|
|
|
|
|
|
} |
|
1313
|
|
|
|
|
|
|
|
|
1314
|
|
|
|
|
|
|
/* ============================================ |
|
1315
|
|
|
|
|
|
|
Numeric custom ops |
|
1316
|
|
|
|
|
|
|
============================================ */ |
|
1317
|
|
|
|
|
|
|
|
|
1318
|
|
|
|
|
|
|
/* sign: return -1, 0, or 1 based on value */ |
|
1319
|
3
|
|
|
|
|
|
static OP* pp_sign(pTHX) { |
|
1320
|
3
|
|
|
|
|
|
dSP; |
|
1321
|
3
|
|
|
|
|
|
SV *sv = TOPs; |
|
1322
|
|
|
|
|
|
|
|
|
1323
|
3
|
50
|
|
|
|
|
if (!SvNIOK(sv) && !looks_like_number(sv)) { |
|
|
|
0
|
|
|
|
|
|
|
1324
|
0
|
|
|
|
|
|
SETs(&PL_sv_undef); |
|
1325
|
0
|
|
|
|
|
|
RETURN; |
|
1326
|
|
|
|
|
|
|
} |
|
1327
|
|
|
|
|
|
|
|
|
1328
|
3
|
|
|
|
|
|
NV nv = SvNV(sv); |
|
1329
|
3
|
100
|
|
|
|
|
if (nv > 0) { |
|
1330
|
1
|
|
|
|
|
|
SETs(sv_2mortal(newSViv(1))); |
|
1331
|
2
|
100
|
|
|
|
|
} else if (nv < 0) { |
|
1332
|
1
|
|
|
|
|
|
SETs(sv_2mortal(newSViv(-1))); |
|
1333
|
|
|
|
|
|
|
} else { |
|
1334
|
1
|
|
|
|
|
|
SETs(sv_2mortal(newSViv(0))); |
|
1335
|
|
|
|
|
|
|
} |
|
1336
|
3
|
|
|
|
|
|
RETURN; |
|
1337
|
|
|
|
|
|
|
} |
|
1338
|
|
|
|
|
|
|
|
|
1339
|
|
|
|
|
|
|
/* min2: return smaller of two values */ |
|
1340
|
4
|
|
|
|
|
|
static OP* pp_min2(pTHX) { |
|
1341
|
4
|
|
|
|
|
|
dSP; |
|
1342
|
4
|
|
|
|
|
|
SV *b_sv = POPs; |
|
1343
|
4
|
|
|
|
|
|
SV *a_sv = TOPs; |
|
1344
|
|
|
|
|
|
|
|
|
1345
|
4
|
|
|
|
|
|
NV a = SvNV(a_sv); |
|
1346
|
4
|
|
|
|
|
|
NV b = SvNV(b_sv); |
|
1347
|
|
|
|
|
|
|
|
|
1348
|
4
|
100
|
|
|
|
|
SETs(a <= b ? a_sv : b_sv); |
|
1349
|
4
|
|
|
|
|
|
RETURN; |
|
1350
|
|
|
|
|
|
|
} |
|
1351
|
|
|
|
|
|
|
|
|
1352
|
|
|
|
|
|
|
/* max2: return larger of two values */ |
|
1353
|
4
|
|
|
|
|
|
static OP* pp_max2(pTHX) { |
|
1354
|
4
|
|
|
|
|
|
dSP; |
|
1355
|
4
|
|
|
|
|
|
SV *b_sv = POPs; |
|
1356
|
4
|
|
|
|
|
|
SV *a_sv = TOPs; |
|
1357
|
|
|
|
|
|
|
|
|
1358
|
4
|
|
|
|
|
|
NV a = SvNV(a_sv); |
|
1359
|
4
|
|
|
|
|
|
NV b = SvNV(b_sv); |
|
1360
|
|
|
|
|
|
|
|
|
1361
|
4
|
100
|
|
|
|
|
SETs(a >= b ? a_sv : b_sv); |
|
1362
|
4
|
|
|
|
|
|
RETURN; |
|
1363
|
|
|
|
|
|
|
} |
|
1364
|
|
|
|
|
|
|
|
|
1365
|
|
|
|
|
|
|
|
|
1366
|
|
|
|
|
|
|
/* ============================================ |
|
1367
|
|
|
|
|
|
|
Call checkers - replace function calls with custom ops |
|
1368
|
|
|
|
|
|
|
============================================ */ |
|
1369
|
|
|
|
|
|
|
|
|
1370
|
|
|
|
|
|
|
/* |
|
1371
|
|
|
|
|
|
|
* Check if an op is accessing $_ (the default variable). |
|
1372
|
|
|
|
|
|
|
* Custom ops now properly handle list context with marks, |
|
1373
|
|
|
|
|
|
|
* but we still fall back to XS for $_ because of how map/grep |
|
1374
|
|
|
|
|
|
|
* set up the op tree with $_ - the argument evaluation is different. |
|
1375
|
|
|
|
|
|
|
* Returns TRUE if we should fall back to XS. |
|
1376
|
|
|
|
|
|
|
*/ |
|
1377
|
152
|
|
|
|
|
|
static bool op_is_dollar_underscore(pTHX_ OP *op) { |
|
1378
|
152
|
50
|
|
|
|
|
if (!op) return FALSE; |
|
1379
|
|
|
|
|
|
|
|
|
1380
|
|
|
|
|
|
|
/* Check for $_ access: rv2sv -> gv for "_" */ |
|
1381
|
152
|
100
|
|
|
|
|
if (op->op_type == OP_RV2SV) { |
|
1382
|
4
|
|
|
|
|
|
OP *gvop = cUNOPx(op)->op_first; |
|
1383
|
4
|
50
|
|
|
|
|
if (gvop && gvop->op_type == OP_GV) { |
|
|
|
50
|
|
|
|
|
|
|
1384
|
4
|
|
|
|
|
|
GV *gv = cGVOPx_gv(gvop); |
|
1385
|
4
|
50
|
|
|
|
|
if (gv && GvNAMELEN(gv) == 1 && GvNAME(gv)[0] == '_') { |
|
|
|
50
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
1386
|
4
|
|
|
|
|
|
return TRUE; |
|
1387
|
|
|
|
|
|
|
} |
|
1388
|
|
|
|
|
|
|
} |
|
1389
|
|
|
|
|
|
|
} |
|
1390
|
|
|
|
|
|
|
|
|
1391
|
148
|
|
|
|
|
|
return FALSE; |
|
1392
|
|
|
|
|
|
|
} |
|
1393
|
|
|
|
|
|
|
|
|
1394
|
|
|
|
|
|
|
/* identity call checker - replaces identity($x) with just $x */ |
|
1395
|
3
|
|
|
|
|
|
static OP* identity_call_checker(pTHX_ OP *entersubop, GV *namegv, SV *ckobj) { |
|
1396
|
|
|
|
|
|
|
OP *pushop, *argop, *cvop; |
|
1397
|
|
|
|
|
|
|
|
|
1398
|
|
|
|
|
|
|
PERL_UNUSED_ARG(namegv); |
|
1399
|
|
|
|
|
|
|
PERL_UNUSED_ARG(ckobj); |
|
1400
|
|
|
|
|
|
|
|
|
1401
|
|
|
|
|
|
|
/* Get the argument list */ |
|
1402
|
3
|
|
|
|
|
|
pushop = cUNOPx(entersubop)->op_first; |
|
1403
|
3
|
50
|
|
|
|
|
if (!OpHAS_SIBLING(pushop)) { |
|
1404
|
3
|
|
|
|
|
|
pushop = cUNOPx(pushop)->op_first; |
|
1405
|
|
|
|
|
|
|
} |
|
1406
|
|
|
|
|
|
|
|
|
1407
|
|
|
|
|
|
|
/* Find first real arg (skip pushmark) */ |
|
1408
|
3
|
50
|
|
|
|
|
argop = OpSIBLING(pushop); |
|
1409
|
|
|
|
|
|
|
|
|
1410
|
|
|
|
|
|
|
/* Find the cv op (last sibling) */ |
|
1411
|
3
|
|
|
|
|
|
cvop = argop; |
|
1412
|
6
|
100
|
|
|
|
|
while (OpHAS_SIBLING(cvop)) { |
|
1413
|
3
|
50
|
|
|
|
|
cvop = OpSIBLING(cvop); |
|
1414
|
|
|
|
|
|
|
} |
|
1415
|
|
|
|
|
|
|
|
|
1416
|
|
|
|
|
|
|
/* Check for exactly one argument */ |
|
1417
|
3
|
50
|
|
|
|
|
if (argop != cvop && OpSIBLING(argop) == cvop) { |
|
|
|
50
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
1418
|
|
|
|
|
|
|
/* Single arg - just return the arg itself */ |
|
1419
|
3
|
|
|
|
|
|
OP *arg = argop; |
|
1420
|
|
|
|
|
|
|
|
|
1421
|
|
|
|
|
|
|
/* If arg is $_, fall back to XS (map/grep context) */ |
|
1422
|
3
|
50
|
|
|
|
|
if (op_is_dollar_underscore(aTHX_ arg)) { |
|
1423
|
0
|
|
|
|
|
|
return entersubop; |
|
1424
|
|
|
|
|
|
|
} |
|
1425
|
|
|
|
|
|
|
|
|
1426
|
|
|
|
|
|
|
/* Detach arg from list */ |
|
1427
|
3
|
|
|
|
|
|
OpMORESIB_set(pushop, cvop); |
|
1428
|
3
|
|
|
|
|
|
OpLASTSIB_set(arg, NULL); |
|
1429
|
|
|
|
|
|
|
|
|
1430
|
3
|
|
|
|
|
|
op_free(entersubop); |
|
1431
|
3
|
|
|
|
|
|
return arg; /* Just return the argument op directly! */ |
|
1432
|
|
|
|
|
|
|
} |
|
1433
|
|
|
|
|
|
|
|
|
1434
|
|
|
|
|
|
|
/* Fall through to XS for edge cases */ |
|
1435
|
0
|
|
|
|
|
|
return entersubop; |
|
1436
|
|
|
|
|
|
|
} |
|
1437
|
|
|
|
|
|
|
|
|
1438
|
|
|
|
|
|
|
/* clamp call checker - replaces clamp($v, $min, $max) with custom op */ |
|
1439
|
8
|
|
|
|
|
|
static OP* clamp_call_checker(pTHX_ OP *entersubop, GV *namegv, SV *ckobj) { |
|
1440
|
|
|
|
|
|
|
OP *pushop, *arg1, *arg2, *arg3, *cvop; |
|
1441
|
|
|
|
|
|
|
OP *listop; |
|
1442
|
|
|
|
|
|
|
|
|
1443
|
|
|
|
|
|
|
PERL_UNUSED_ARG(namegv); |
|
1444
|
|
|
|
|
|
|
PERL_UNUSED_ARG(ckobj); |
|
1445
|
|
|
|
|
|
|
|
|
1446
|
|
|
|
|
|
|
/* Get the argument list */ |
|
1447
|
8
|
|
|
|
|
|
pushop = cUNOPx(entersubop)->op_first; |
|
1448
|
8
|
50
|
|
|
|
|
if (!OpHAS_SIBLING(pushop)) { |
|
1449
|
8
|
|
|
|
|
|
pushop = cUNOPx(pushop)->op_first; |
|
1450
|
|
|
|
|
|
|
} |
|
1451
|
|
|
|
|
|
|
|
|
1452
|
|
|
|
|
|
|
/* Find args (skip pushmark) */ |
|
1453
|
8
|
50
|
|
|
|
|
arg1 = OpSIBLING(pushop); /* value */ |
|
1454
|
8
|
50
|
|
|
|
|
if (!arg1) return entersubop; |
|
1455
|
|
|
|
|
|
|
|
|
1456
|
8
|
50
|
|
|
|
|
arg2 = OpSIBLING(arg1); /* min */ |
|
1457
|
8
|
50
|
|
|
|
|
if (!arg2) return entersubop; |
|
1458
|
|
|
|
|
|
|
|
|
1459
|
8
|
50
|
|
|
|
|
arg3 = OpSIBLING(arg2); /* max */ |
|
1460
|
8
|
50
|
|
|
|
|
if (!arg3) return entersubop; |
|
1461
|
|
|
|
|
|
|
|
|
1462
|
8
|
50
|
|
|
|
|
cvop = OpSIBLING(arg3); /* cv op (should be last) */ |
|
1463
|
8
|
50
|
|
|
|
|
if (!cvop || OpHAS_SIBLING(cvop)) return entersubop; |
|
|
|
50
|
|
|
|
|
|
|
1464
|
|
|
|
|
|
|
|
|
1465
|
|
|
|
|
|
|
/* |
|
1466
|
|
|
|
|
|
|
* If arg1 is accessing $_, we're likely in map/grep. |
|
1467
|
|
|
|
|
|
|
* The custom op doesn't work correctly in these contexts. |
|
1468
|
|
|
|
|
|
|
* Fall back to XS. |
|
1469
|
|
|
|
|
|
|
*/ |
|
1470
|
8
|
100
|
|
|
|
|
if (op_is_dollar_underscore(aTHX_ arg1)) { |
|
1471
|
2
|
|
|
|
|
|
return entersubop; |
|
1472
|
|
|
|
|
|
|
} |
|
1473
|
|
|
|
|
|
|
|
|
1474
|
|
|
|
|
|
|
/* Detach args from the entersub tree */ |
|
1475
|
6
|
|
|
|
|
|
OpMORESIB_set(pushop, cvop); |
|
1476
|
|
|
|
|
|
|
|
|
1477
|
|
|
|
|
|
|
/* Chain arg1 -> arg2 -> arg3 */ |
|
1478
|
6
|
|
|
|
|
|
OpMORESIB_set(arg1, arg2); |
|
1479
|
6
|
|
|
|
|
|
OpMORESIB_set(arg2, arg3); |
|
1480
|
6
|
|
|
|
|
|
OpLASTSIB_set(arg3, NULL); |
|
1481
|
|
|
|
|
|
|
|
|
1482
|
|
|
|
|
|
|
/* |
|
1483
|
|
|
|
|
|
|
* Create a LISTOP with 3 children for clamp. |
|
1484
|
|
|
|
|
|
|
* We use op_convert_list to properly set up a list context. |
|
1485
|
|
|
|
|
|
|
*/ |
|
1486
|
6
|
|
|
|
|
|
listop = op_convert_list(OP_LIST, OPf_STACKED, arg1); |
|
1487
|
6
|
|
|
|
|
|
listop->op_type = OP_CUSTOM; |
|
1488
|
6
|
|
|
|
|
|
listop->op_ppaddr = pp_clamp; |
|
1489
|
6
|
|
|
|
|
|
listop->op_flags = (listop->op_flags & ~OPf_WANT) | OPf_WANT_SCALAR | OPf_STACKED; |
|
1490
|
6
|
|
|
|
|
|
listop->op_targ = pad_alloc(OP_NULL, SVs_PADTMP); |
|
1491
|
|
|
|
|
|
|
|
|
1492
|
6
|
|
|
|
|
|
op_free(entersubop); |
|
1493
|
6
|
|
|
|
|
|
return listop; |
|
1494
|
|
|
|
|
|
|
} |
|
1495
|
|
|
|
|
|
|
|
|
1496
|
|
|
|
|
|
|
/* Generic call checker for single-arg type predicates */ |
|
1497
|
120
|
|
|
|
|
|
static OP* type_predicate_call_checker(pTHX_ OP *entersubop, GV *namegv, SV *ckobj, OP* (*pp_func)(pTHX)) { |
|
1498
|
|
|
|
|
|
|
OP *pushop, *argop, *cvop; |
|
1499
|
|
|
|
|
|
|
|
|
1500
|
|
|
|
|
|
|
PERL_UNUSED_ARG(namegv); |
|
1501
|
|
|
|
|
|
|
PERL_UNUSED_ARG(ckobj); |
|
1502
|
|
|
|
|
|
|
|
|
1503
|
|
|
|
|
|
|
/* Get the argument list */ |
|
1504
|
120
|
|
|
|
|
|
pushop = cUNOPx(entersubop)->op_first; |
|
1505
|
120
|
50
|
|
|
|
|
if (!OpHAS_SIBLING(pushop)) { |
|
1506
|
120
|
|
|
|
|
|
pushop = cUNOPx(pushop)->op_first; |
|
1507
|
|
|
|
|
|
|
} |
|
1508
|
|
|
|
|
|
|
|
|
1509
|
|
|
|
|
|
|
/* Find first real arg (skip pushmark) */ |
|
1510
|
120
|
50
|
|
|
|
|
argop = OpSIBLING(pushop); |
|
1511
|
|
|
|
|
|
|
|
|
1512
|
|
|
|
|
|
|
/* Find the cv op (last sibling) */ |
|
1513
|
120
|
|
|
|
|
|
cvop = argop; |
|
1514
|
240
|
100
|
|
|
|
|
while (OpHAS_SIBLING(cvop)) { |
|
1515
|
120
|
50
|
|
|
|
|
cvop = OpSIBLING(cvop); |
|
1516
|
|
|
|
|
|
|
} |
|
1517
|
|
|
|
|
|
|
|
|
1518
|
|
|
|
|
|
|
/* Check for exactly one argument */ |
|
1519
|
120
|
50
|
|
|
|
|
if (argop != cvop && OpSIBLING(argop) == cvop) { |
|
|
|
50
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
1520
|
120
|
|
|
|
|
|
OP *arg = argop; |
|
1521
|
|
|
|
|
|
|
|
|
1522
|
|
|
|
|
|
|
/* If arg is $_, fall back to XS (map/grep context) */ |
|
1523
|
120
|
100
|
|
|
|
|
if (op_is_dollar_underscore(aTHX_ arg)) { |
|
1524
|
1
|
|
|
|
|
|
return entersubop; |
|
1525
|
|
|
|
|
|
|
} |
|
1526
|
|
|
|
|
|
|
|
|
1527
|
|
|
|
|
|
|
/* Detach arg from list */ |
|
1528
|
119
|
|
|
|
|
|
OpMORESIB_set(pushop, cvop); |
|
1529
|
119
|
|
|
|
|
|
OpLASTSIB_set(arg, NULL); |
|
1530
|
|
|
|
|
|
|
|
|
1531
|
|
|
|
|
|
|
/* Create unary custom op with arg as child */ |
|
1532
|
119
|
|
|
|
|
|
OP *newop = newUNOP(OP_CUSTOM, 0, arg); |
|
1533
|
119
|
|
|
|
|
|
newop->op_ppaddr = pp_func; |
|
1534
|
|
|
|
|
|
|
|
|
1535
|
119
|
|
|
|
|
|
op_free(entersubop); |
|
1536
|
119
|
|
|
|
|
|
return newop; |
|
1537
|
|
|
|
|
|
|
} |
|
1538
|
|
|
|
|
|
|
|
|
1539
|
|
|
|
|
|
|
/* Fall through to XS for edge cases */ |
|
1540
|
0
|
|
|
|
|
|
return entersubop; |
|
1541
|
|
|
|
|
|
|
} |
|
1542
|
|
|
|
|
|
|
|
|
1543
|
|
|
|
|
|
|
/* Individual call checkers for each type predicate */ |
|
1544
|
6
|
|
|
|
|
|
static OP* is_ref_call_checker(pTHX_ OP *entersubop, GV *namegv, SV *ckobj) { |
|
1545
|
6
|
|
|
|
|
|
return type_predicate_call_checker(aTHX_ entersubop, namegv, ckobj, pp_is_ref); |
|
1546
|
|
|
|
|
|
|
} |
|
1547
|
|
|
|
|
|
|
|
|
1548
|
4
|
|
|
|
|
|
static OP* is_array_call_checker(pTHX_ OP *entersubop, GV *namegv, SV *ckobj) { |
|
1549
|
4
|
|
|
|
|
|
return type_predicate_call_checker(aTHX_ entersubop, namegv, ckobj, pp_is_array); |
|
1550
|
|
|
|
|
|
|
} |
|
1551
|
|
|
|
|
|
|
|
|
1552
|
3
|
|
|
|
|
|
static OP* is_hash_call_checker(pTHX_ OP *entersubop, GV *namegv, SV *ckobj) { |
|
1553
|
3
|
|
|
|
|
|
return type_predicate_call_checker(aTHX_ entersubop, namegv, ckobj, pp_is_hash); |
|
1554
|
|
|
|
|
|
|
} |
|
1555
|
|
|
|
|
|
|
|
|
1556
|
3
|
|
|
|
|
|
static OP* is_code_call_checker(pTHX_ OP *entersubop, GV *namegv, SV *ckobj) { |
|
1557
|
3
|
|
|
|
|
|
return type_predicate_call_checker(aTHX_ entersubop, namegv, ckobj, pp_is_code); |
|
1558
|
|
|
|
|
|
|
} |
|
1559
|
|
|
|
|
|
|
|
|
1560
|
5
|
|
|
|
|
|
static OP* is_defined_call_checker(pTHX_ OP *entersubop, GV *namegv, SV *ckobj) { |
|
1561
|
5
|
|
|
|
|
|
return type_predicate_call_checker(aTHX_ entersubop, namegv, ckobj, pp_is_defined); |
|
1562
|
|
|
|
|
|
|
} |
|
1563
|
|
|
|
|
|
|
|
|
1564
|
|
|
|
|
|
|
/* String predicate call checkers */ |
|
1565
|
4
|
|
|
|
|
|
static OP* is_empty_call_checker(pTHX_ OP *entersubop, GV *namegv, SV *ckobj) { |
|
1566
|
4
|
|
|
|
|
|
return type_predicate_call_checker(aTHX_ entersubop, namegv, ckobj, pp_is_empty); |
|
1567
|
|
|
|
|
|
|
} |
|
1568
|
|
|
|
|
|
|
|
|
1569
|
|
|
|
|
|
|
/* Generic two-arg string predicate call checker */ |
|
1570
|
21
|
|
|
|
|
|
static OP* two_arg_string_call_checker(pTHX_ OP *entersubop, GV *namegv, SV *ckobj, OP* (*pp_func)(pTHX)) { |
|
1571
|
|
|
|
|
|
|
OP *pushop, *arg1, *arg2, *cvop; |
|
1572
|
|
|
|
|
|
|
|
|
1573
|
|
|
|
|
|
|
PERL_UNUSED_ARG(namegv); |
|
1574
|
|
|
|
|
|
|
PERL_UNUSED_ARG(ckobj); |
|
1575
|
|
|
|
|
|
|
|
|
1576
|
|
|
|
|
|
|
/* Get the argument list */ |
|
1577
|
21
|
|
|
|
|
|
pushop = cUNOPx(entersubop)->op_first; |
|
1578
|
21
|
50
|
|
|
|
|
if (!OpHAS_SIBLING(pushop)) { |
|
1579
|
21
|
|
|
|
|
|
pushop = cUNOPx(pushop)->op_first; |
|
1580
|
|
|
|
|
|
|
} |
|
1581
|
|
|
|
|
|
|
|
|
1582
|
|
|
|
|
|
|
/* Find args (skip pushmark) */ |
|
1583
|
21
|
50
|
|
|
|
|
arg1 = OpSIBLING(pushop); /* string */ |
|
1584
|
21
|
50
|
|
|
|
|
if (!arg1) return entersubop; |
|
1585
|
|
|
|
|
|
|
|
|
1586
|
21
|
50
|
|
|
|
|
arg2 = OpSIBLING(arg1); /* prefix/suffix */ |
|
1587
|
21
|
50
|
|
|
|
|
if (!arg2) return entersubop; |
|
1588
|
|
|
|
|
|
|
|
|
1589
|
21
|
50
|
|
|
|
|
cvop = OpSIBLING(arg2); /* cv op (should be last) */ |
|
1590
|
21
|
50
|
|
|
|
|
if (!cvop || OpHAS_SIBLING(cvop)) return entersubop; |
|
|
|
50
|
|
|
|
|
|
|
1591
|
|
|
|
|
|
|
|
|
1592
|
|
|
|
|
|
|
/* If arg1 is $_, fall back to XS (map/grep context) */ |
|
1593
|
21
|
100
|
|
|
|
|
if (op_is_dollar_underscore(aTHX_ arg1)) { |
|
1594
|
1
|
|
|
|
|
|
return entersubop; |
|
1595
|
|
|
|
|
|
|
} |
|
1596
|
|
|
|
|
|
|
|
|
1597
|
|
|
|
|
|
|
/* Detach args from the entersub tree */ |
|
1598
|
20
|
|
|
|
|
|
OpMORESIB_set(pushop, cvop); |
|
1599
|
|
|
|
|
|
|
|
|
1600
|
|
|
|
|
|
|
/* Chain arg1 -> arg2 */ |
|
1601
|
20
|
|
|
|
|
|
OpMORESIB_set(arg1, arg2); |
|
1602
|
20
|
|
|
|
|
|
OpLASTSIB_set(arg2, NULL); |
|
1603
|
|
|
|
|
|
|
|
|
1604
|
|
|
|
|
|
|
/* |
|
1605
|
|
|
|
|
|
|
* Create a custom BINOP-style op. |
|
1606
|
|
|
|
|
|
|
* Use newBINOP to create a proper binary op structure where |
|
1607
|
|
|
|
|
|
|
* both arguments are children. The optimizer won't eliminate |
|
1608
|
|
|
|
|
|
|
* children of an op that's going to use them. |
|
1609
|
|
|
|
|
|
|
*/ |
|
1610
|
20
|
|
|
|
|
|
OP *binop = newBINOP(OP_NULL, 0, arg1, arg2); |
|
1611
|
20
|
|
|
|
|
|
binop->op_type = OP_CUSTOM; |
|
1612
|
20
|
|
|
|
|
|
binop->op_ppaddr = pp_func; |
|
1613
|
20
|
|
|
|
|
|
binop->op_flags = OPf_WANT_SCALAR | OPf_KIDS | OPf_STACKED; |
|
1614
|
|
|
|
|
|
|
|
|
1615
|
20
|
|
|
|
|
|
op_free(entersubop); |
|
1616
|
20
|
|
|
|
|
|
return binop; |
|
1617
|
|
|
|
|
|
|
} |
|
1618
|
|
|
|
|
|
|
|
|
1619
|
7
|
|
|
|
|
|
static OP* starts_with_call_checker(pTHX_ OP *entersubop, GV *namegv, SV *ckobj) { |
|
1620
|
7
|
|
|
|
|
|
return two_arg_string_call_checker(aTHX_ entersubop, namegv, ckobj, pp_starts_with); |
|
1621
|
|
|
|
|
|
|
} |
|
1622
|
|
|
|
|
|
|
|
|
1623
|
4
|
|
|
|
|
|
static OP* ends_with_call_checker(pTHX_ OP *entersubop, GV *namegv, SV *ckobj) { |
|
1624
|
4
|
|
|
|
|
|
return two_arg_string_call_checker(aTHX_ entersubop, namegv, ckobj, pp_ends_with); |
|
1625
|
|
|
|
|
|
|
} |
|
1626
|
|
|
|
|
|
|
|
|
1627
|
|
|
|
|
|
|
/* Boolean/Truthiness call checkers */ |
|
1628
|
5
|
|
|
|
|
|
static OP* is_true_call_checker(pTHX_ OP *entersubop, GV *namegv, SV *ckobj) { |
|
1629
|
5
|
|
|
|
|
|
return type_predicate_call_checker(aTHX_ entersubop, namegv, ckobj, pp_is_true); |
|
1630
|
|
|
|
|
|
|
} |
|
1631
|
|
|
|
|
|
|
|
|
1632
|
5
|
|
|
|
|
|
static OP* is_false_call_checker(pTHX_ OP *entersubop, GV *namegv, SV *ckobj) { |
|
1633
|
5
|
|
|
|
|
|
return type_predicate_call_checker(aTHX_ entersubop, namegv, ckobj, pp_is_false); |
|
1634
|
|
|
|
|
|
|
} |
|
1635
|
|
|
|
|
|
|
|
|
1636
|
7
|
|
|
|
|
|
static OP* bool_call_checker(pTHX_ OP *entersubop, GV *namegv, SV *ckobj) { |
|
1637
|
7
|
|
|
|
|
|
return type_predicate_call_checker(aTHX_ entersubop, namegv, ckobj, pp_bool); |
|
1638
|
|
|
|
|
|
|
} |
|
1639
|
|
|
|
|
|
|
|
|
1640
|
|
|
|
|
|
|
/* Extended type predicate call checkers */ |
|
1641
|
6
|
|
|
|
|
|
static OP* is_num_call_checker(pTHX_ OP *entersubop, GV *namegv, SV *ckobj) { |
|
1642
|
6
|
|
|
|
|
|
return type_predicate_call_checker(aTHX_ entersubop, namegv, ckobj, pp_is_num); |
|
1643
|
|
|
|
|
|
|
} |
|
1644
|
|
|
|
|
|
|
|
|
1645
|
5
|
|
|
|
|
|
static OP* is_int_call_checker(pTHX_ OP *entersubop, GV *namegv, SV *ckobj) { |
|
1646
|
5
|
|
|
|
|
|
return type_predicate_call_checker(aTHX_ entersubop, namegv, ckobj, pp_is_int); |
|
1647
|
|
|
|
|
|
|
} |
|
1648
|
|
|
|
|
|
|
|
|
1649
|
3
|
|
|
|
|
|
static OP* is_blessed_call_checker(pTHX_ OP *entersubop, GV *namegv, SV *ckobj) { |
|
1650
|
3
|
|
|
|
|
|
return type_predicate_call_checker(aTHX_ entersubop, namegv, ckobj, pp_is_blessed); |
|
1651
|
|
|
|
|
|
|
} |
|
1652
|
|
|
|
|
|
|
|
|
1653
|
3
|
|
|
|
|
|
static OP* is_scalar_ref_call_checker(pTHX_ OP *entersubop, GV *namegv, SV *ckobj) { |
|
1654
|
3
|
|
|
|
|
|
return type_predicate_call_checker(aTHX_ entersubop, namegv, ckobj, pp_is_scalar_ref); |
|
1655
|
|
|
|
|
|
|
} |
|
1656
|
|
|
|
|
|
|
|
|
1657
|
3
|
|
|
|
|
|
static OP* is_regex_call_checker(pTHX_ OP *entersubop, GV *namegv, SV *ckobj) { |
|
1658
|
3
|
|
|
|
|
|
return type_predicate_call_checker(aTHX_ entersubop, namegv, ckobj, pp_is_regex); |
|
1659
|
|
|
|
|
|
|
} |
|
1660
|
|
|
|
|
|
|
|
|
1661
|
2
|
|
|
|
|
|
static OP* is_glob_call_checker(pTHX_ OP *entersubop, GV *namegv, SV *ckobj) { |
|
1662
|
2
|
|
|
|
|
|
return type_predicate_call_checker(aTHX_ entersubop, namegv, ckobj, pp_is_glob); |
|
1663
|
|
|
|
|
|
|
} |
|
1664
|
|
|
|
|
|
|
|
|
1665
|
0
|
|
|
|
|
|
static OP* is_string_call_checker(pTHX_ OP *entersubop, GV *namegv, SV *ckobj) { |
|
1666
|
0
|
|
|
|
|
|
return type_predicate_call_checker(aTHX_ entersubop, namegv, ckobj, pp_is_string); |
|
1667
|
|
|
|
|
|
|
} |
|
1668
|
|
|
|
|
|
|
|
|
1669
|
|
|
|
|
|
|
/* Numeric predicate call checkers */ |
|
1670
|
5
|
|
|
|
|
|
static OP* is_positive_call_checker(pTHX_ OP *entersubop, GV *namegv, SV *ckobj) { |
|
1671
|
5
|
|
|
|
|
|
return type_predicate_call_checker(aTHX_ entersubop, namegv, ckobj, pp_is_positive); |
|
1672
|
|
|
|
|
|
|
} |
|
1673
|
|
|
|
|
|
|
|
|
1674
|
4
|
|
|
|
|
|
static OP* is_negative_call_checker(pTHX_ OP *entersubop, GV *namegv, SV *ckobj) { |
|
1675
|
4
|
|
|
|
|
|
return type_predicate_call_checker(aTHX_ entersubop, namegv, ckobj, pp_is_negative); |
|
1676
|
|
|
|
|
|
|
} |
|
1677
|
|
|
|
|
|
|
|
|
1678
|
4
|
|
|
|
|
|
static OP* is_zero_call_checker(pTHX_ OP *entersubop, GV *namegv, SV *ckobj) { |
|
1679
|
4
|
|
|
|
|
|
return type_predicate_call_checker(aTHX_ entersubop, namegv, ckobj, pp_is_zero); |
|
1680
|
|
|
|
|
|
|
} |
|
1681
|
|
|
|
|
|
|
|
|
1682
|
|
|
|
|
|
|
/* Numeric utility call checkers */ |
|
1683
|
6
|
|
|
|
|
|
static OP* is_even_call_checker(pTHX_ OP *entersubop, GV *namegv, SV *ckobj) { |
|
1684
|
6
|
|
|
|
|
|
return type_predicate_call_checker(aTHX_ entersubop, namegv, ckobj, pp_is_even); |
|
1685
|
|
|
|
|
|
|
} |
|
1686
|
|
|
|
|
|
|
|
|
1687
|
5
|
|
|
|
|
|
static OP* is_odd_call_checker(pTHX_ OP *entersubop, GV *namegv, SV *ckobj) { |
|
1688
|
5
|
|
|
|
|
|
return type_predicate_call_checker(aTHX_ entersubop, namegv, ckobj, pp_is_odd); |
|
1689
|
|
|
|
|
|
|
} |
|
1690
|
|
|
|
|
|
|
|
|
1691
|
|
|
|
|
|
|
/* is_between needs 3 args - use same pattern as clamp */ |
|
1692
|
5
|
|
|
|
|
|
static OP* is_between_call_checker(pTHX_ OP *entersubop, GV *namegv, SV *ckobj) { |
|
1693
|
|
|
|
|
|
|
/* 3-arg ops are complex to optimize with custom ops. |
|
1694
|
|
|
|
|
|
|
* Fall back to XS function for now. */ |
|
1695
|
|
|
|
|
|
|
PERL_UNUSED_ARG(namegv); |
|
1696
|
|
|
|
|
|
|
PERL_UNUSED_ARG(ckobj); |
|
1697
|
5
|
|
|
|
|
|
return entersubop; |
|
1698
|
|
|
|
|
|
|
} |
|
1699
|
|
|
|
|
|
|
|
|
1700
|
|
|
|
|
|
|
/* Collection call checkers */ |
|
1701
|
3
|
|
|
|
|
|
static OP* is_empty_array_call_checker(pTHX_ OP *entersubop, GV *namegv, SV *ckobj) { |
|
1702
|
3
|
|
|
|
|
|
return type_predicate_call_checker(aTHX_ entersubop, namegv, ckobj, pp_is_empty_array); |
|
1703
|
|
|
|
|
|
|
} |
|
1704
|
|
|
|
|
|
|
|
|
1705
|
3
|
|
|
|
|
|
static OP* is_empty_hash_call_checker(pTHX_ OP *entersubop, GV *namegv, SV *ckobj) { |
|
1706
|
3
|
|
|
|
|
|
return type_predicate_call_checker(aTHX_ entersubop, namegv, ckobj, pp_is_empty_hash); |
|
1707
|
|
|
|
|
|
|
} |
|
1708
|
|
|
|
|
|
|
|
|
1709
|
3
|
|
|
|
|
|
static OP* array_len_call_checker(pTHX_ OP *entersubop, GV *namegv, SV *ckobj) { |
|
1710
|
3
|
|
|
|
|
|
return type_predicate_call_checker(aTHX_ entersubop, namegv, ckobj, pp_array_len); |
|
1711
|
|
|
|
|
|
|
} |
|
1712
|
|
|
|
|
|
|
|
|
1713
|
3
|
|
|
|
|
|
static OP* hash_size_call_checker(pTHX_ OP *entersubop, GV *namegv, SV *ckobj) { |
|
1714
|
3
|
|
|
|
|
|
return type_predicate_call_checker(aTHX_ entersubop, namegv, ckobj, pp_hash_size); |
|
1715
|
|
|
|
|
|
|
} |
|
1716
|
|
|
|
|
|
|
|
|
1717
|
3
|
|
|
|
|
|
static OP* array_first_call_checker(pTHX_ OP *entersubop, GV *namegv, SV *ckobj) { |
|
1718
|
3
|
|
|
|
|
|
return type_predicate_call_checker(aTHX_ entersubop, namegv, ckobj, pp_array_first); |
|
1719
|
|
|
|
|
|
|
} |
|
1720
|
|
|
|
|
|
|
|
|
1721
|
3
|
|
|
|
|
|
static OP* array_last_call_checker(pTHX_ OP *entersubop, GV *namegv, SV *ckobj) { |
|
1722
|
3
|
|
|
|
|
|
return type_predicate_call_checker(aTHX_ entersubop, namegv, ckobj, pp_array_last); |
|
1723
|
|
|
|
|
|
|
} |
|
1724
|
|
|
|
|
|
|
|
|
1725
|
|
|
|
|
|
|
/* trim uses single-arg pattern */ |
|
1726
|
5
|
|
|
|
|
|
static OP* trim_call_checker(pTHX_ OP *entersubop, GV *namegv, SV *ckobj) { |
|
1727
|
5
|
|
|
|
|
|
return type_predicate_call_checker(aTHX_ entersubop, namegv, ckobj, pp_trim); |
|
1728
|
|
|
|
|
|
|
} |
|
1729
|
|
|
|
|
|
|
|
|
1730
|
3
|
|
|
|
|
|
static OP* ltrim_call_checker(pTHX_ OP *entersubop, GV *namegv, SV *ckobj) { |
|
1731
|
3
|
|
|
|
|
|
return type_predicate_call_checker(aTHX_ entersubop, namegv, ckobj, pp_ltrim); |
|
1732
|
|
|
|
|
|
|
} |
|
1733
|
|
|
|
|
|
|
|
|
1734
|
3
|
|
|
|
|
|
static OP* rtrim_call_checker(pTHX_ OP *entersubop, GV *namegv, SV *ckobj) { |
|
1735
|
3
|
|
|
|
|
|
return type_predicate_call_checker(aTHX_ entersubop, namegv, ckobj, pp_rtrim); |
|
1736
|
|
|
|
|
|
|
} |
|
1737
|
|
|
|
|
|
|
|
|
1738
|
|
|
|
|
|
|
/* maybe uses two-arg pattern */ |
|
1739
|
2
|
|
|
|
|
|
static OP* maybe_call_checker(pTHX_ OP *entersubop, GV *namegv, SV *ckobj) { |
|
1740
|
2
|
|
|
|
|
|
return two_arg_string_call_checker(aTHX_ entersubop, namegv, ckobj, pp_maybe); |
|
1741
|
|
|
|
|
|
|
} |
|
1742
|
|
|
|
|
|
|
|
|
1743
|
|
|
|
|
|
|
/* Numeric ops */ |
|
1744
|
3
|
|
|
|
|
|
static OP* sign_call_checker(pTHX_ OP *entersubop, GV *namegv, SV *ckobj) { |
|
1745
|
3
|
|
|
|
|
|
return type_predicate_call_checker(aTHX_ entersubop, namegv, ckobj, pp_sign); |
|
1746
|
|
|
|
|
|
|
} |
|
1747
|
|
|
|
|
|
|
|
|
1748
|
4
|
|
|
|
|
|
static OP* min2_call_checker(pTHX_ OP *entersubop, GV *namegv, SV *ckobj) { |
|
1749
|
4
|
|
|
|
|
|
return two_arg_string_call_checker(aTHX_ entersubop, namegv, ckobj, pp_min2); |
|
1750
|
|
|
|
|
|
|
} |
|
1751
|
|
|
|
|
|
|
|
|
1752
|
4
|
|
|
|
|
|
static OP* max2_call_checker(pTHX_ OP *entersubop, GV *namegv, SV *ckobj) { |
|
1753
|
4
|
|
|
|
|
|
return two_arg_string_call_checker(aTHX_ entersubop, namegv, ckobj, pp_max2); |
|
1754
|
|
|
|
|
|
|
} |
|
1755
|
|
|
|
|
|
|
|
|
1756
|
|
|
|
|
|
|
/* ============================================ |
|
1757
|
|
|
|
|
|
|
Memo implementation |
|
1758
|
|
|
|
|
|
|
============================================ */ |
|
1759
|
|
|
|
|
|
|
|
|
1760
|
213
|
|
|
|
|
|
XS_INTERNAL(xs_memo) { |
|
1761
|
213
|
|
|
|
|
|
dXSARGS; |
|
1762
|
213
|
50
|
|
|
|
|
if (items != 1) croak("Usage: Func::Util::memo(\\&func)"); |
|
1763
|
|
|
|
|
|
|
|
|
1764
|
213
|
|
|
|
|
|
SV *func = ST(0); |
|
1765
|
213
|
50
|
|
|
|
|
if (!SvROK(func) || SvTYPE(SvRV(func)) != SVt_PVCV) { |
|
|
|
50
|
|
|
|
|
|
|
1766
|
0
|
|
|
|
|
|
croak("Func::Util::memo requires a coderef"); |
|
1767
|
|
|
|
|
|
|
} |
|
1768
|
|
|
|
|
|
|
|
|
1769
|
|
|
|
|
|
|
/* Allocate memo slot */ |
|
1770
|
213
|
|
|
|
|
|
IV idx = g_memo_count++; |
|
1771
|
213
|
|
|
|
|
|
ensure_memo_capacity(idx); |
|
1772
|
|
|
|
|
|
|
|
|
1773
|
213
|
|
|
|
|
|
MemoizedFunc *mf = &g_memos[idx]; |
|
1774
|
213
|
|
|
|
|
|
mf->func = SvREFCNT_inc_simple_NN(func); |
|
1775
|
213
|
|
|
|
|
|
mf->cache = newHV(); |
|
1776
|
213
|
|
|
|
|
|
mf->hits = 0; |
|
1777
|
213
|
|
|
|
|
|
mf->misses = 0; |
|
1778
|
|
|
|
|
|
|
|
|
1779
|
|
|
|
|
|
|
/* Create wrapper CV */ |
|
1780
|
213
|
|
|
|
|
|
CV *wrapper = newXS(NULL, xs_memo_call, __FILE__); |
|
1781
|
213
|
|
|
|
|
|
CvXSUBANY(wrapper).any_iv = idx; |
|
1782
|
|
|
|
|
|
|
|
|
1783
|
|
|
|
|
|
|
/* Attach magic for cleanup when wrapper is freed */ |
|
1784
|
213
|
|
|
|
|
|
sv_magicext((SV*)wrapper, NULL, PERL_MAGIC_ext, &util_memo_vtbl, NULL, idx); |
|
1785
|
|
|
|
|
|
|
|
|
1786
|
213
|
|
|
|
|
|
ST(0) = sv_2mortal(newRV_noinc((SV*)wrapper)); |
|
1787
|
213
|
|
|
|
|
|
XSRETURN(1); |
|
1788
|
|
|
|
|
|
|
} |
|
1789
|
|
|
|
|
|
|
|
|
1790
|
849
|
|
|
|
|
|
XS_INTERNAL(xs_memo_call) { |
|
1791
|
849
|
|
|
|
|
|
dXSARGS; |
|
1792
|
849
|
|
|
|
|
|
IV idx = CvXSUBANY(cv).any_iv; |
|
1793
|
849
|
|
|
|
|
|
MemoizedFunc *mf = &g_memos[idx]; |
|
1794
|
|
|
|
|
|
|
|
|
1795
|
|
|
|
|
|
|
/* Build cache key from arguments */ |
|
1796
|
849
|
|
|
|
|
|
SV *key = build_cache_key(aTHX_ &ST(0), items); |
|
1797
|
|
|
|
|
|
|
STRLEN key_len; |
|
1798
|
849
|
|
|
|
|
|
const char *key_pv = SvPV(key, key_len); |
|
1799
|
|
|
|
|
|
|
|
|
1800
|
|
|
|
|
|
|
/* Check cache */ |
|
1801
|
849
|
|
|
|
|
|
SV **cached = hv_fetch(mf->cache, key_pv, key_len, 0); |
|
1802
|
849
|
100
|
|
|
|
|
if (cached && SvOK(*cached)) { |
|
|
|
50
|
|
|
|
|
|
|
1803
|
421
|
|
|
|
|
|
mf->hits++; |
|
1804
|
421
|
|
|
|
|
|
SvREFCNT_dec_NN(key); |
|
1805
|
421
|
50
|
|
|
|
|
if (SvROK(*cached) && SvTYPE(SvRV(*cached)) == SVt_PVAV) { |
|
|
|
0
|
|
|
|
|
|
|
1806
|
0
|
|
|
|
|
|
AV *av = (AV*)SvRV(*cached); |
|
1807
|
0
|
|
|
|
|
|
IV len = av_len(av) + 1; |
|
1808
|
|
|
|
|
|
|
IV i; |
|
1809
|
0
|
0
|
|
|
|
|
EXTEND(SP, len); |
|
|
|
0
|
|
|
|
|
|
|
1810
|
0
|
0
|
|
|
|
|
for (i = 0; i < len; i++) { |
|
1811
|
0
|
|
|
|
|
|
SV **elem = av_fetch(av, i, 0); |
|
1812
|
0
|
0
|
|
|
|
|
ST(i) = elem ? *elem : &PL_sv_undef; |
|
1813
|
|
|
|
|
|
|
} |
|
1814
|
0
|
|
|
|
|
|
XSRETURN(len); |
|
1815
|
|
|
|
|
|
|
} else { |
|
1816
|
421
|
|
|
|
|
|
ST(0) = *cached; |
|
1817
|
421
|
|
|
|
|
|
XSRETURN(1); |
|
1818
|
|
|
|
|
|
|
} |
|
1819
|
|
|
|
|
|
|
} |
|
1820
|
|
|
|
|
|
|
|
|
1821
|
428
|
|
|
|
|
|
mf->misses++; |
|
1822
|
|
|
|
|
|
|
|
|
1823
|
428
|
|
|
|
|
|
ENTER; |
|
1824
|
428
|
|
|
|
|
|
SAVETMPS; |
|
1825
|
428
|
50
|
|
|
|
|
PUSHMARK(SP); |
|
1826
|
|
|
|
|
|
|
|
|
1827
|
|
|
|
|
|
|
IV i; |
|
1828
|
428
|
50
|
|
|
|
|
EXTEND(SP, items); |
|
|
|
50
|
|
|
|
|
|
|
1829
|
857
|
100
|
|
|
|
|
for (i = 0; i < items; i++) { |
|
1830
|
429
|
|
|
|
|
|
PUSHs(ST(i)); |
|
1831
|
|
|
|
|
|
|
} |
|
1832
|
428
|
|
|
|
|
|
PUTBACK; |
|
1833
|
|
|
|
|
|
|
|
|
1834
|
428
|
|
|
|
|
|
IV count = call_sv(mf->func, G_ARRAY); |
|
1835
|
|
|
|
|
|
|
|
|
1836
|
428
|
|
|
|
|
|
SPAGAIN; |
|
1837
|
|
|
|
|
|
|
|
|
1838
|
428
|
50
|
|
|
|
|
if (count == 1) { |
|
1839
|
428
|
|
|
|
|
|
SV *result = SvREFCNT_inc(POPs); |
|
1840
|
428
|
|
|
|
|
|
hv_store(mf->cache, key_pv, key_len, result, 0); |
|
1841
|
428
|
|
|
|
|
|
PUTBACK; |
|
1842
|
428
|
50
|
|
|
|
|
FREETMPS; |
|
1843
|
428
|
|
|
|
|
|
LEAVE; |
|
1844
|
428
|
|
|
|
|
|
SvREFCNT_dec_NN(key); |
|
1845
|
428
|
|
|
|
|
|
ST(0) = result; |
|
1846
|
428
|
|
|
|
|
|
XSRETURN(1); |
|
1847
|
0
|
0
|
|
|
|
|
} else if (count > 0) { |
|
1848
|
0
|
|
|
|
|
|
AV *av = newAV(); |
|
1849
|
0
|
|
|
|
|
|
av_extend(av, count - 1); |
|
1850
|
0
|
0
|
|
|
|
|
for (i = count - 1; i >= 0; i--) { |
|
1851
|
0
|
|
|
|
|
|
av_store(av, i, SvREFCNT_inc(POPs)); |
|
1852
|
|
|
|
|
|
|
} |
|
1853
|
0
|
|
|
|
|
|
SV *result = newRV_noinc((SV*)av); |
|
1854
|
0
|
|
|
|
|
|
hv_store(mf->cache, key_pv, key_len, result, 0); |
|
1855
|
0
|
|
|
|
|
|
PUTBACK; |
|
1856
|
0
|
0
|
|
|
|
|
FREETMPS; |
|
1857
|
0
|
|
|
|
|
|
LEAVE; |
|
1858
|
0
|
|
|
|
|
|
SvREFCNT_dec_NN(key); |
|
1859
|
0
|
0
|
|
|
|
|
for (i = 0; i < count; i++) { |
|
1860
|
0
|
|
|
|
|
|
SV **elem = av_fetch(av, i, 0); |
|
1861
|
0
|
0
|
|
|
|
|
ST(i) = elem ? *elem : &PL_sv_undef; |
|
1862
|
|
|
|
|
|
|
} |
|
1863
|
0
|
|
|
|
|
|
XSRETURN(count); |
|
1864
|
|
|
|
|
|
|
} else { |
|
1865
|
0
|
|
|
|
|
|
hv_store(mf->cache, key_pv, key_len, &PL_sv_undef, 0); |
|
1866
|
0
|
|
|
|
|
|
PUTBACK; |
|
1867
|
0
|
0
|
|
|
|
|
FREETMPS; |
|
1868
|
0
|
|
|
|
|
|
LEAVE; |
|
1869
|
0
|
|
|
|
|
|
SvREFCNT_dec_NN(key); |
|
1870
|
0
|
|
|
|
|
|
XSRETURN_EMPTY; |
|
1871
|
|
|
|
|
|
|
} |
|
1872
|
|
|
|
|
|
|
} |
|
1873
|
|
|
|
|
|
|
|
|
1874
|
|
|
|
|
|
|
/* ============================================ |
|
1875
|
|
|
|
|
|
|
Pipe/Compose implementation |
|
1876
|
|
|
|
|
|
|
============================================ */ |
|
1877
|
|
|
|
|
|
|
|
|
1878
|
1011
|
|
|
|
|
|
XS_INTERNAL(xs_pipe) { |
|
1879
|
1011
|
|
|
|
|
|
dXSARGS; |
|
1880
|
1011
|
50
|
|
|
|
|
if (items < 2) croak("Usage: Func::Util::pipeline($value, \\&fn1, \\&fn2, ...)"); |
|
1881
|
|
|
|
|
|
|
|
|
1882
|
1011
|
|
|
|
|
|
SV *value = SvREFCNT_inc(ST(0)); |
|
1883
|
|
|
|
|
|
|
IV i; |
|
1884
|
|
|
|
|
|
|
|
|
1885
|
4036
|
100
|
|
|
|
|
for (i = 1; i < items; i++) { |
|
1886
|
3025
|
|
|
|
|
|
SV *func = ST(i); |
|
1887
|
3025
|
50
|
|
|
|
|
if (!SvROK(func) || SvTYPE(SvRV(func)) != SVt_PVCV) { |
|
|
|
50
|
|
|
|
|
|
|
1888
|
0
|
|
|
|
|
|
SvREFCNT_dec(value); |
|
1889
|
0
|
|
|
|
|
|
croak("Func::Util::pipeline: argument %d is not a coderef", (int)i); |
|
1890
|
|
|
|
|
|
|
} |
|
1891
|
|
|
|
|
|
|
|
|
1892
|
3025
|
|
|
|
|
|
ENTER; |
|
1893
|
3025
|
|
|
|
|
|
SAVETMPS; |
|
1894
|
3025
|
50
|
|
|
|
|
PUSHMARK(SP); |
|
1895
|
3025
|
50
|
|
|
|
|
XPUSHs(value); |
|
1896
|
3025
|
|
|
|
|
|
PUTBACK; |
|
1897
|
|
|
|
|
|
|
|
|
1898
|
3025
|
|
|
|
|
|
call_sv(func, G_SCALAR); |
|
1899
|
|
|
|
|
|
|
|
|
1900
|
3025
|
|
|
|
|
|
SPAGAIN; |
|
1901
|
3025
|
|
|
|
|
|
SV *new_value = POPs; |
|
1902
|
3025
|
|
|
|
|
|
SvREFCNT_inc(new_value); |
|
1903
|
3025
|
|
|
|
|
|
PUTBACK; |
|
1904
|
3025
|
100
|
|
|
|
|
FREETMPS; |
|
1905
|
3025
|
|
|
|
|
|
LEAVE; |
|
1906
|
|
|
|
|
|
|
|
|
1907
|
3025
|
|
|
|
|
|
SvREFCNT_dec(value); |
|
1908
|
3025
|
|
|
|
|
|
value = new_value; |
|
1909
|
|
|
|
|
|
|
} |
|
1910
|
|
|
|
|
|
|
|
|
1911
|
1011
|
|
|
|
|
|
ST(0) = sv_2mortal(value); |
|
1912
|
1011
|
|
|
|
|
|
XSRETURN(1); |
|
1913
|
|
|
|
|
|
|
} |
|
1914
|
|
|
|
|
|
|
|
|
1915
|
1010
|
|
|
|
|
|
XS_INTERNAL(xs_compose) { |
|
1916
|
1010
|
|
|
|
|
|
dXSARGS; |
|
1917
|
1010
|
50
|
|
|
|
|
if (items < 1) croak("Usage: Func::Util::compose(\\&fn1, \\&fn2, ...)"); |
|
1918
|
|
|
|
|
|
|
|
|
1919
|
1010
|
|
|
|
|
|
AV *funcs = newAV(); |
|
1920
|
1010
|
|
|
|
|
|
av_extend(funcs, items - 1); |
|
1921
|
|
|
|
|
|
|
IV i; |
|
1922
|
4030
|
100
|
|
|
|
|
for (i = 0; i < items; i++) { |
|
1923
|
3020
|
|
|
|
|
|
SV *func = ST(i); |
|
1924
|
3020
|
50
|
|
|
|
|
if (!SvROK(func) || SvTYPE(SvRV(func)) != SVt_PVCV) { |
|
|
|
50
|
|
|
|
|
|
|
1925
|
0
|
|
|
|
|
|
croak("Func::Util::compose: argument %d is not a coderef", (int)(i+1)); |
|
1926
|
|
|
|
|
|
|
} |
|
1927
|
3020
|
|
|
|
|
|
av_store(funcs, i, SvREFCNT_inc_simple_NN(func)); |
|
1928
|
|
|
|
|
|
|
} |
|
1929
|
|
|
|
|
|
|
|
|
1930
|
1010
|
|
|
|
|
|
CV *wrapper = newXS(NULL, xs_compose_call, __FILE__); |
|
1931
|
1010
|
|
|
|
|
|
CvXSUBANY(wrapper).any_ptr = (void*)funcs; |
|
1932
|
|
|
|
|
|
|
|
|
1933
|
|
|
|
|
|
|
/* Attach magic for cleanup when wrapper is freed - pass AV via mg_ptr */ |
|
1934
|
1010
|
|
|
|
|
|
sv_magicext((SV*)wrapper, NULL, PERL_MAGIC_ext, &util_compose_vtbl, (char*)funcs, 0); |
|
1935
|
|
|
|
|
|
|
|
|
1936
|
1010
|
|
|
|
|
|
ST(0) = sv_2mortal(newRV_noinc((SV*)wrapper)); |
|
1937
|
1010
|
|
|
|
|
|
XSRETURN(1); |
|
1938
|
|
|
|
|
|
|
} |
|
1939
|
|
|
|
|
|
|
|
|
1940
|
1011
|
|
|
|
|
|
XS_INTERNAL(xs_compose_call) { |
|
1941
|
1011
|
|
|
|
|
|
dXSARGS; |
|
1942
|
1011
|
|
|
|
|
|
AV *funcs = (AV*)CvXSUBANY(cv).any_ptr; |
|
1943
|
1011
|
|
|
|
|
|
IV func_count = av_len(funcs) + 1; |
|
1944
|
|
|
|
|
|
|
|
|
1945
|
1011
|
|
|
|
|
|
SV *value = NULL; |
|
1946
|
|
|
|
|
|
|
|
|
1947
|
|
|
|
|
|
|
IV i; |
|
1948
|
4033
|
100
|
|
|
|
|
for (i = func_count - 1; i >= 0; i--) { |
|
1949
|
3022
|
|
|
|
|
|
SV **func_ptr = av_fetch(funcs, i, 0); |
|
1950
|
3022
|
50
|
|
|
|
|
if (!func_ptr) continue; |
|
1951
|
|
|
|
|
|
|
|
|
1952
|
3022
|
|
|
|
|
|
ENTER; |
|
1953
|
3022
|
|
|
|
|
|
SAVETMPS; |
|
1954
|
3022
|
50
|
|
|
|
|
PUSHMARK(SP); |
|
1955
|
|
|
|
|
|
|
|
|
1956
|
3022
|
100
|
|
|
|
|
if (i == func_count - 1) { |
|
1957
|
|
|
|
|
|
|
IV j; |
|
1958
|
1011
|
50
|
|
|
|
|
EXTEND(SP, items); |
|
|
|
50
|
|
|
|
|
|
|
1959
|
2022
|
100
|
|
|
|
|
for (j = 0; j < items; j++) { |
|
1960
|
1011
|
|
|
|
|
|
PUSHs(ST(j)); |
|
1961
|
|
|
|
|
|
|
} |
|
1962
|
|
|
|
|
|
|
} else { |
|
1963
|
2011
|
50
|
|
|
|
|
XPUSHs(value); |
|
1964
|
|
|
|
|
|
|
} |
|
1965
|
3022
|
|
|
|
|
|
PUTBACK; |
|
1966
|
|
|
|
|
|
|
|
|
1967
|
3022
|
|
|
|
|
|
call_sv(*func_ptr, G_SCALAR); |
|
1968
|
|
|
|
|
|
|
|
|
1969
|
3022
|
|
|
|
|
|
SPAGAIN; |
|
1970
|
3022
|
|
|
|
|
|
SV *new_value = POPs; |
|
1971
|
3022
|
|
|
|
|
|
SvREFCNT_inc(new_value); |
|
1972
|
3022
|
|
|
|
|
|
PUTBACK; |
|
1973
|
3022
|
100
|
|
|
|
|
FREETMPS; |
|
1974
|
3022
|
|
|
|
|
|
LEAVE; |
|
1975
|
|
|
|
|
|
|
|
|
1976
|
3022
|
100
|
|
|
|
|
if (value) SvREFCNT_dec(value); |
|
1977
|
3022
|
|
|
|
|
|
value = new_value; |
|
1978
|
|
|
|
|
|
|
} |
|
1979
|
|
|
|
|
|
|
|
|
1980
|
1011
|
50
|
|
|
|
|
ST(0) = value ? sv_2mortal(value) : &PL_sv_undef; |
|
1981
|
1011
|
|
|
|
|
|
XSRETURN(1); |
|
1982
|
|
|
|
|
|
|
} |
|
1983
|
|
|
|
|
|
|
|
|
1984
|
|
|
|
|
|
|
/* ============================================ |
|
1985
|
|
|
|
|
|
|
Lazy evaluation implementation |
|
1986
|
|
|
|
|
|
|
============================================ */ |
|
1987
|
|
|
|
|
|
|
|
|
1988
|
1008
|
|
|
|
|
|
XS_INTERNAL(xs_lazy) { |
|
1989
|
1008
|
|
|
|
|
|
dXSARGS; |
|
1990
|
1008
|
50
|
|
|
|
|
if (items != 1) croak("Usage: Func::Util::lazy(sub { ... })"); |
|
1991
|
|
|
|
|
|
|
|
|
1992
|
1008
|
|
|
|
|
|
SV *thunk = ST(0); |
|
1993
|
1008
|
50
|
|
|
|
|
if (!SvROK(thunk) || SvTYPE(SvRV(thunk)) != SVt_PVCV) { |
|
|
|
50
|
|
|
|
|
|
|
1994
|
0
|
|
|
|
|
|
croak("Func::Util::lazy requires a coderef"); |
|
1995
|
|
|
|
|
|
|
} |
|
1996
|
|
|
|
|
|
|
|
|
1997
|
1008
|
|
|
|
|
|
IV idx = g_lazy_count++; |
|
1998
|
1008
|
|
|
|
|
|
ensure_lazy_capacity(idx); |
|
1999
|
|
|
|
|
|
|
|
|
2000
|
1008
|
|
|
|
|
|
LazyValue *lv = &g_lazies[idx]; |
|
2001
|
1008
|
|
|
|
|
|
lv->thunk = SvREFCNT_inc_simple_NN(thunk); |
|
2002
|
1008
|
|
|
|
|
|
lv->value = NULL; |
|
2003
|
1008
|
|
|
|
|
|
lv->forced = FALSE; |
|
2004
|
|
|
|
|
|
|
|
|
2005
|
1008
|
|
|
|
|
|
SV *obj = newSViv(idx); |
|
2006
|
1008
|
|
|
|
|
|
SV *ref = newRV_noinc(obj); |
|
2007
|
1008
|
|
|
|
|
|
sv_bless(ref, gv_stashpv("Func::Util::Lazy", GV_ADD)); |
|
2008
|
|
|
|
|
|
|
|
|
2009
|
|
|
|
|
|
|
/* Attach magic for cleanup when lazy object is freed */ |
|
2010
|
1008
|
|
|
|
|
|
sv_magicext(obj, NULL, PERL_MAGIC_ext, &util_lazy_vtbl, NULL, idx); |
|
2011
|
|
|
|
|
|
|
|
|
2012
|
1008
|
|
|
|
|
|
ST(0) = sv_2mortal(ref); |
|
2013
|
1008
|
|
|
|
|
|
XSRETURN(1); |
|
2014
|
|
|
|
|
|
|
} |
|
2015
|
|
|
|
|
|
|
|
|
2016
|
2018
|
|
|
|
|
|
XS_INTERNAL(xs_force) { |
|
2017
|
2018
|
|
|
|
|
|
dXSARGS; |
|
2018
|
2018
|
50
|
|
|
|
|
if (items != 1) croak("Usage: Func::Util::force($lazy)"); |
|
2019
|
|
|
|
|
|
|
|
|
2020
|
2018
|
|
|
|
|
|
SV *lazy = ST(0); |
|
2021
|
|
|
|
|
|
|
|
|
2022
|
2018
|
100
|
|
|
|
|
if (!SvROK(lazy) || !sv_derived_from(lazy, "Func::Util::Lazy")) { |
|
|
|
100
|
|
|
|
|
|
|
2023
|
5
|
|
|
|
|
|
ST(0) = lazy; |
|
2024
|
5
|
|
|
|
|
|
XSRETURN(1); |
|
2025
|
|
|
|
|
|
|
} |
|
2026
|
|
|
|
|
|
|
|
|
2027
|
2013
|
|
|
|
|
|
IV idx = SvIV(SvRV(lazy)); |
|
2028
|
2013
|
50
|
|
|
|
|
if (idx < 0 || idx >= g_lazy_count) { |
|
|
|
50
|
|
|
|
|
|
|
2029
|
0
|
|
|
|
|
|
croak("Func::Util::force: invalid lazy value"); |
|
2030
|
|
|
|
|
|
|
} |
|
2031
|
|
|
|
|
|
|
|
|
2032
|
2013
|
|
|
|
|
|
LazyValue *lv = &g_lazies[idx]; |
|
2033
|
|
|
|
|
|
|
|
|
2034
|
2013
|
100
|
|
|
|
|
if (lv->forced) { |
|
2035
|
1005
|
|
|
|
|
|
ST(0) = lv->value; |
|
2036
|
1005
|
|
|
|
|
|
XSRETURN(1); |
|
2037
|
|
|
|
|
|
|
} |
|
2038
|
|
|
|
|
|
|
|
|
2039
|
1008
|
|
|
|
|
|
ENTER; |
|
2040
|
1008
|
|
|
|
|
|
SAVETMPS; |
|
2041
|
1008
|
50
|
|
|
|
|
PUSHMARK(SP); |
|
2042
|
1008
|
|
|
|
|
|
PUTBACK; |
|
2043
|
|
|
|
|
|
|
|
|
2044
|
1008
|
|
|
|
|
|
call_sv(lv->thunk, G_SCALAR); |
|
2045
|
|
|
|
|
|
|
|
|
2046
|
1008
|
|
|
|
|
|
SPAGAIN; |
|
2047
|
1008
|
|
|
|
|
|
lv->value = SvREFCNT_inc(POPs); |
|
2048
|
1008
|
|
|
|
|
|
lv->forced = TRUE; |
|
2049
|
1008
|
|
|
|
|
|
PUTBACK; |
|
2050
|
1008
|
50
|
|
|
|
|
FREETMPS; |
|
2051
|
1008
|
|
|
|
|
|
LEAVE; |
|
2052
|
|
|
|
|
|
|
|
|
2053
|
1008
|
|
|
|
|
|
SvREFCNT_dec(lv->thunk); |
|
2054
|
1008
|
|
|
|
|
|
lv->thunk = NULL; |
|
2055
|
|
|
|
|
|
|
|
|
2056
|
1008
|
|
|
|
|
|
ST(0) = lv->value; |
|
2057
|
1008
|
|
|
|
|
|
XSRETURN(1); |
|
2058
|
|
|
|
|
|
|
} |
|
2059
|
|
|
|
|
|
|
|
|
2060
|
|
|
|
|
|
|
/* ============================================ |
|
2061
|
|
|
|
|
|
|
Safe navigation (dig) implementation |
|
2062
|
|
|
|
|
|
|
============================================ */ |
|
2063
|
|
|
|
|
|
|
|
|
2064
|
19124
|
|
|
|
|
|
XS_INTERNAL(xs_dig) { |
|
2065
|
19124
|
|
|
|
|
|
dXSARGS; |
|
2066
|
19124
|
50
|
|
|
|
|
if (items < 2) croak("Usage: Func::Util::dig($hash, @keys)"); |
|
2067
|
|
|
|
|
|
|
|
|
2068
|
19124
|
|
|
|
|
|
SV *current = ST(0); |
|
2069
|
|
|
|
|
|
|
IV i; |
|
2070
|
|
|
|
|
|
|
|
|
2071
|
60471
|
100
|
|
|
|
|
for (i = 1; i < items; i++) { |
|
2072
|
47354
|
100
|
|
|
|
|
if (!SvROK(current) || SvTYPE(SvRV(current)) != SVt_PVHV) { |
|
|
|
100
|
|
|
|
|
|
|
2073
|
6007
|
|
|
|
|
|
XSRETURN_UNDEF; |
|
2074
|
|
|
|
|
|
|
} |
|
2075
|
|
|
|
|
|
|
|
|
2076
|
44353
|
|
|
|
|
|
HV *hv = (HV*)SvRV(current); |
|
2077
|
44353
|
|
|
|
|
|
SV *key = ST(i); |
|
2078
|
|
|
|
|
|
|
STRLEN key_len; |
|
2079
|
44353
|
|
|
|
|
|
const char *key_pv = SvPV(key, key_len); |
|
2080
|
|
|
|
|
|
|
|
|
2081
|
44353
|
|
|
|
|
|
SV **val = hv_fetch(hv, key_pv, key_len, 0); |
|
2082
|
44353
|
100
|
|
|
|
|
if (!val || !SvOK(*val)) { |
|
|
|
100
|
|
|
|
|
|
|
2083
|
3006
|
|
|
|
|
|
XSRETURN_UNDEF; |
|
2084
|
|
|
|
|
|
|
} |
|
2085
|
|
|
|
|
|
|
|
|
2086
|
41347
|
|
|
|
|
|
current = *val; |
|
2087
|
|
|
|
|
|
|
} |
|
2088
|
|
|
|
|
|
|
|
|
2089
|
13117
|
|
|
|
|
|
ST(0) = current; |
|
2090
|
13117
|
|
|
|
|
|
XSRETURN(1); |
|
2091
|
|
|
|
|
|
|
} |
|
2092
|
|
|
|
|
|
|
|
|
2093
|
|
|
|
|
|
|
/* ============================================ |
|
2094
|
|
|
|
|
|
|
Tap implementation |
|
2095
|
|
|
|
|
|
|
============================================ */ |
|
2096
|
|
|
|
|
|
|
|
|
2097
|
12105
|
|
|
|
|
|
XS_INTERNAL(xs_tap) { |
|
2098
|
12105
|
|
|
|
|
|
dXSARGS; |
|
2099
|
12105
|
50
|
|
|
|
|
if (items != 2) croak("Usage: Func::Util::tap(\\&block, $value)"); |
|
2100
|
|
|
|
|
|
|
|
|
2101
|
12105
|
|
|
|
|
|
SV *func = ST(0); |
|
2102
|
12105
|
|
|
|
|
|
SV *value = ST(1); |
|
2103
|
|
|
|
|
|
|
|
|
2104
|
12105
|
50
|
|
|
|
|
if (!SvROK(func) || SvTYPE(SvRV(func)) != SVt_PVCV) { |
|
|
|
50
|
|
|
|
|
|
|
2105
|
0
|
|
|
|
|
|
croak("Func::Util::tap: first argument must be a coderef"); |
|
2106
|
|
|
|
|
|
|
} |
|
2107
|
|
|
|
|
|
|
|
|
2108
|
12105
|
|
|
|
|
|
ENTER; |
|
2109
|
12105
|
|
|
|
|
|
SAVETMPS; |
|
2110
|
12105
|
|
|
|
|
|
SAVE_DEFSV; |
|
2111
|
12105
|
|
|
|
|
|
DEFSV_set(value); |
|
2112
|
|
|
|
|
|
|
|
|
2113
|
12105
|
50
|
|
|
|
|
PUSHMARK(SP); |
|
2114
|
12105
|
50
|
|
|
|
|
XPUSHs(value); |
|
2115
|
12105
|
|
|
|
|
|
PUTBACK; |
|
2116
|
|
|
|
|
|
|
|
|
2117
|
12105
|
|
|
|
|
|
call_sv(func, G_DISCARD | G_VOID); |
|
2118
|
|
|
|
|
|
|
|
|
2119
|
12105
|
|
|
|
|
|
SPAGAIN; |
|
2120
|
12105
|
50
|
|
|
|
|
FREETMPS; |
|
2121
|
12105
|
|
|
|
|
|
LEAVE; |
|
2122
|
|
|
|
|
|
|
|
|
2123
|
12105
|
|
|
|
|
|
ST(0) = value; |
|
2124
|
12105
|
|
|
|
|
|
XSRETURN(1); |
|
2125
|
|
|
|
|
|
|
} |
|
2126
|
|
|
|
|
|
|
|
|
2127
|
|
|
|
|
|
|
/* ============================================ |
|
2128
|
|
|
|
|
|
|
Clamp XS fallback |
|
2129
|
|
|
|
|
|
|
============================================ */ |
|
2130
|
|
|
|
|
|
|
|
|
2131
|
18161
|
|
|
|
|
|
XS_INTERNAL(xs_clamp) { |
|
2132
|
18161
|
|
|
|
|
|
dXSARGS; |
|
2133
|
|
|
|
|
|
|
NV value, min, max, result; |
|
2134
|
18161
|
50
|
|
|
|
|
if (items != 3) croak("Usage: Func::Util::clamp($value, $min, $max)"); |
|
2135
|
|
|
|
|
|
|
|
|
2136
|
18161
|
|
|
|
|
|
value = SvNV(ST(0)); |
|
2137
|
18161
|
|
|
|
|
|
min = SvNV(ST(1)); |
|
2138
|
18161
|
|
|
|
|
|
max = SvNV(ST(2)); |
|
2139
|
|
|
|
|
|
|
|
|
2140
|
18161
|
100
|
|
|
|
|
if (value < min) { |
|
2141
|
3014
|
|
|
|
|
|
result = min; |
|
2142
|
15147
|
100
|
|
|
|
|
} else if (value > max) { |
|
2143
|
3014
|
|
|
|
|
|
result = max; |
|
2144
|
|
|
|
|
|
|
} else { |
|
2145
|
12133
|
|
|
|
|
|
result = value; |
|
2146
|
|
|
|
|
|
|
} |
|
2147
|
|
|
|
|
|
|
|
|
2148
|
18161
|
|
|
|
|
|
ST(0) = sv_2mortal(newSVnv(result)); |
|
2149
|
18161
|
|
|
|
|
|
XSRETURN(1); |
|
2150
|
|
|
|
|
|
|
} |
|
2151
|
|
|
|
|
|
|
|
|
2152
|
|
|
|
|
|
|
/* ============================================ |
|
2153
|
|
|
|
|
|
|
Identity XS fallback |
|
2154
|
|
|
|
|
|
|
============================================ */ |
|
2155
|
|
|
|
|
|
|
|
|
2156
|
16119
|
|
|
|
|
|
XS_INTERNAL(xs_identity) { |
|
2157
|
16119
|
|
|
|
|
|
dXSARGS; |
|
2158
|
16119
|
50
|
|
|
|
|
if (items != 1) croak("Usage: Func::Util::identity($value)"); |
|
2159
|
16119
|
|
|
|
|
|
XSRETURN(1); |
|
2160
|
|
|
|
|
|
|
} |
|
2161
|
|
|
|
|
|
|
|
|
2162
|
|
|
|
|
|
|
/* ============================================ |
|
2163
|
|
|
|
|
|
|
Always implementation |
|
2164
|
|
|
|
|
|
|
============================================ */ |
|
2165
|
|
|
|
|
|
|
|
|
2166
|
8
|
|
|
|
|
|
XS_INTERNAL(xs_always) { |
|
2167
|
8
|
|
|
|
|
|
dXSARGS; |
|
2168
|
8
|
50
|
|
|
|
|
if (items != 1) croak("Usage: Func::Util::always($value)"); |
|
2169
|
|
|
|
|
|
|
|
|
2170
|
8
|
|
|
|
|
|
IV idx = g_always_count++; |
|
2171
|
8
|
|
|
|
|
|
ensure_always_capacity(idx); |
|
2172
|
|
|
|
|
|
|
|
|
2173
|
8
|
|
|
|
|
|
g_always_values[idx] = SvREFCNT_inc_simple_NN(ST(0)); |
|
2174
|
|
|
|
|
|
|
|
|
2175
|
8
|
|
|
|
|
|
CV *wrapper = newXS(NULL, xs_always_call, __FILE__); |
|
2176
|
8
|
|
|
|
|
|
CvXSUBANY(wrapper).any_iv = idx; |
|
2177
|
|
|
|
|
|
|
|
|
2178
|
|
|
|
|
|
|
/* Attach magic for cleanup when wrapper is freed */ |
|
2179
|
8
|
|
|
|
|
|
sv_magicext((SV*)wrapper, NULL, PERL_MAGIC_ext, &util_always_vtbl, NULL, idx); |
|
2180
|
|
|
|
|
|
|
|
|
2181
|
8
|
|
|
|
|
|
ST(0) = sv_2mortal(newRV_noinc((SV*)wrapper)); |
|
2182
|
8
|
|
|
|
|
|
XSRETURN(1); |
|
2183
|
|
|
|
|
|
|
} |
|
2184
|
|
|
|
|
|
|
|
|
2185
|
4014
|
|
|
|
|
|
XS_INTERNAL(xs_always_call) { |
|
2186
|
4014
|
|
|
|
|
|
dXSARGS; |
|
2187
|
|
|
|
|
|
|
PERL_UNUSED_VAR(items); |
|
2188
|
4014
|
|
|
|
|
|
IV idx = CvXSUBANY(cv).any_iv; |
|
2189
|
|
|
|
|
|
|
|
|
2190
|
4014
|
|
|
|
|
|
ST(0) = g_always_values[idx]; |
|
2191
|
4014
|
|
|
|
|
|
XSRETURN(1); |
|
2192
|
|
|
|
|
|
|
} |
|
2193
|
|
|
|
|
|
|
|
|
2194
|
|
|
|
|
|
|
/* ============================================ |
|
2195
|
|
|
|
|
|
|
Stub/noop functions - return constants |
|
2196
|
|
|
|
|
|
|
============================================ */ |
|
2197
|
|
|
|
|
|
|
|
|
2198
|
|
|
|
|
|
|
/* pp_noop - custom op that returns undef */ |
|
2199
|
2
|
|
|
|
|
|
static OP* pp_noop(pTHX) { |
|
2200
|
2
|
|
|
|
|
|
dSP; |
|
2201
|
2
|
50
|
|
|
|
|
XPUSHs(&PL_sv_undef); |
|
2202
|
2
|
|
|
|
|
|
RETURN; |
|
2203
|
|
|
|
|
|
|
} |
|
2204
|
|
|
|
|
|
|
|
|
2205
|
|
|
|
|
|
|
/* noop call checker - replace with ultra-fast custom op */ |
|
2206
|
2
|
|
|
|
|
|
static OP* noop_call_checker(pTHX_ OP *entersubop, GV *namegv, SV *ckobj) { |
|
2207
|
|
|
|
|
|
|
OP *newop; |
|
2208
|
|
|
|
|
|
|
PERL_UNUSED_ARG(namegv); |
|
2209
|
|
|
|
|
|
|
PERL_UNUSED_ARG(ckobj); |
|
2210
|
|
|
|
|
|
|
|
|
2211
|
2
|
|
|
|
|
|
op_free(entersubop); |
|
2212
|
|
|
|
|
|
|
|
|
2213
|
2
|
|
|
|
|
|
NewOp(1101, newop, 1, OP); |
|
2214
|
2
|
|
|
|
|
|
newop->op_type = OP_CUSTOM; |
|
2215
|
2
|
|
|
|
|
|
newop->op_ppaddr = pp_noop; |
|
2216
|
2
|
|
|
|
|
|
newop->op_flags = OPf_WANT_SCALAR; |
|
2217
|
2
|
|
|
|
|
|
newop->op_next = newop; |
|
2218
|
|
|
|
|
|
|
|
|
2219
|
2
|
|
|
|
|
|
return newop; |
|
2220
|
|
|
|
|
|
|
} |
|
2221
|
|
|
|
|
|
|
|
|
2222
|
|
|
|
|
|
|
/* noop() - does nothing, returns undef. Ignores all arguments. */ |
|
2223
|
4004
|
|
|
|
|
|
XS_INTERNAL(xs_noop) { |
|
2224
|
4004
|
|
|
|
|
|
dXSARGS; |
|
2225
|
|
|
|
|
|
|
PERL_UNUSED_VAR(items); |
|
2226
|
4004
|
|
|
|
|
|
XSRETURN_UNDEF; |
|
2227
|
|
|
|
|
|
|
} |
|
2228
|
|
|
|
|
|
|
|
|
2229
|
|
|
|
|
|
|
/* stub_true() - always returns true (1) */ |
|
2230
|
12105
|
|
|
|
|
|
XS_INTERNAL(xs_stub_true) { |
|
2231
|
12105
|
|
|
|
|
|
dXSARGS; |
|
2232
|
|
|
|
|
|
|
PERL_UNUSED_VAR(items); |
|
2233
|
12105
|
|
|
|
|
|
XSRETURN_YES; |
|
2234
|
|
|
|
|
|
|
} |
|
2235
|
|
|
|
|
|
|
|
|
2236
|
|
|
|
|
|
|
/* stub_false() - always returns false ('') */ |
|
2237
|
12105
|
|
|
|
|
|
XS_INTERNAL(xs_stub_false) { |
|
2238
|
12105
|
|
|
|
|
|
dXSARGS; |
|
2239
|
|
|
|
|
|
|
PERL_UNUSED_VAR(items); |
|
2240
|
12105
|
|
|
|
|
|
XSRETURN_NO; |
|
2241
|
|
|
|
|
|
|
} |
|
2242
|
|
|
|
|
|
|
|
|
2243
|
|
|
|
|
|
|
/* stub_array() - returns empty arrayref in scalar context, empty list in list context */ |
|
2244
|
12105
|
|
|
|
|
|
XS_INTERNAL(xs_stub_array) { |
|
2245
|
12105
|
|
|
|
|
|
dXSARGS; |
|
2246
|
|
|
|
|
|
|
PERL_UNUSED_VAR(items); |
|
2247
|
12105
|
100
|
|
|
|
|
if (GIMME_V == G_ARRAY) { |
|
2248
|
10102
|
|
|
|
|
|
XSRETURN_EMPTY; |
|
2249
|
|
|
|
|
|
|
} |
|
2250
|
2003
|
|
|
|
|
|
ST(0) = sv_2mortal(newRV_noinc((SV*)newAV())); |
|
2251
|
2003
|
|
|
|
|
|
XSRETURN(1); |
|
2252
|
|
|
|
|
|
|
} |
|
2253
|
|
|
|
|
|
|
|
|
2254
|
|
|
|
|
|
|
/* stub_hash() - returns empty hashref in scalar context, empty list in list context */ |
|
2255
|
12105
|
|
|
|
|
|
XS_INTERNAL(xs_stub_hash) { |
|
2256
|
12105
|
|
|
|
|
|
dXSARGS; |
|
2257
|
|
|
|
|
|
|
PERL_UNUSED_VAR(items); |
|
2258
|
12105
|
100
|
|
|
|
|
if (GIMME_V == G_ARRAY) { |
|
2259
|
10102
|
|
|
|
|
|
XSRETURN_EMPTY; |
|
2260
|
|
|
|
|
|
|
} |
|
2261
|
2003
|
|
|
|
|
|
ST(0) = sv_2mortal(newRV_noinc((SV*)newHV())); |
|
2262
|
2003
|
|
|
|
|
|
XSRETURN(1); |
|
2263
|
|
|
|
|
|
|
} |
|
2264
|
|
|
|
|
|
|
|
|
2265
|
|
|
|
|
|
|
/* stub_string() - always returns empty string '' */ |
|
2266
|
12103
|
|
|
|
|
|
XS_INTERNAL(xs_stub_string) { |
|
2267
|
12103
|
|
|
|
|
|
dXSARGS; |
|
2268
|
|
|
|
|
|
|
PERL_UNUSED_VAR(items); |
|
2269
|
|
|
|
|
|
|
/* Return shared empty string constant - XSRETURN_NO returns '' */ |
|
2270
|
12103
|
|
|
|
|
|
XSRETURN_NO; |
|
2271
|
|
|
|
|
|
|
} |
|
2272
|
|
|
|
|
|
|
|
|
2273
|
|
|
|
|
|
|
/* stub_zero() - always returns 0 */ |
|
2274
|
12104
|
|
|
|
|
|
XS_INTERNAL(xs_stub_zero) { |
|
2275
|
12104
|
|
|
|
|
|
dXSARGS; |
|
2276
|
|
|
|
|
|
|
PERL_UNUSED_VAR(items); |
|
2277
|
|
|
|
|
|
|
/* Return shared 0 SV */ |
|
2278
|
12104
|
|
|
|
|
|
ST(0) = &PL_sv_zero; |
|
2279
|
12104
|
|
|
|
|
|
XSRETURN(1); |
|
2280
|
|
|
|
|
|
|
} |
|
2281
|
|
|
|
|
|
|
|
|
2282
|
|
|
|
|
|
|
/* ============================================ |
|
2283
|
|
|
|
|
|
|
Functional combinators |
|
2284
|
|
|
|
|
|
|
============================================ */ |
|
2285
|
|
|
|
|
|
|
|
|
2286
|
|
|
|
|
|
|
/* negate(\&pred) - returns a function that returns the opposite */ |
|
2287
|
1008
|
|
|
|
|
|
XS_INTERNAL(xs_negate) { |
|
2288
|
1008
|
|
|
|
|
|
dXSARGS; |
|
2289
|
1008
|
50
|
|
|
|
|
if (items != 1) croak("Usage: Func::Util::negate(\\&predicate)"); |
|
2290
|
|
|
|
|
|
|
|
|
2291
|
1008
|
|
|
|
|
|
SV *pred = ST(0); |
|
2292
|
1008
|
50
|
|
|
|
|
if (!SvROK(pred) || SvTYPE(SvRV(pred)) != SVt_PVCV) { |
|
|
|
50
|
|
|
|
|
|
|
2293
|
0
|
|
|
|
|
|
croak("Func::Util::negate: argument must be a coderef"); |
|
2294
|
|
|
|
|
|
|
} |
|
2295
|
|
|
|
|
|
|
|
|
2296
|
1008
|
|
|
|
|
|
CV *wrapper = newXS(NULL, xs_negate_call, __FILE__); |
|
2297
|
1008
|
|
|
|
|
|
CvXSUBANY(wrapper).any_ptr = SvREFCNT_inc_simple_NN(pred); |
|
2298
|
|
|
|
|
|
|
|
|
2299
|
1008
|
|
|
|
|
|
ST(0) = sv_2mortal(newRV_noinc((SV*)wrapper)); |
|
2300
|
1008
|
|
|
|
|
|
XSRETURN(1); |
|
2301
|
|
|
|
|
|
|
} |
|
2302
|
|
|
|
|
|
|
|
|
2303
|
12113
|
|
|
|
|
|
XS_INTERNAL(xs_negate_call) { |
|
2304
|
12113
|
|
|
|
|
|
dXSARGS; |
|
2305
|
12113
|
|
|
|
|
|
SV *pred = (SV*)CvXSUBANY(cv).any_ptr; |
|
2306
|
|
|
|
|
|
|
|
|
2307
|
12113
|
|
|
|
|
|
ENTER; |
|
2308
|
12113
|
|
|
|
|
|
SAVETMPS; |
|
2309
|
12113
|
50
|
|
|
|
|
PUSHMARK(SP); |
|
2310
|
|
|
|
|
|
|
|
|
2311
|
|
|
|
|
|
|
IV i; |
|
2312
|
12113
|
50
|
|
|
|
|
EXTEND(SP, items); |
|
|
|
50
|
|
|
|
|
|
|
2313
|
24226
|
100
|
|
|
|
|
for (i = 0; i < items; i++) { |
|
2314
|
12113
|
|
|
|
|
|
PUSHs(ST(i)); |
|
2315
|
|
|
|
|
|
|
} |
|
2316
|
12113
|
|
|
|
|
|
PUTBACK; |
|
2317
|
|
|
|
|
|
|
|
|
2318
|
12113
|
|
|
|
|
|
call_sv(pred, G_SCALAR); |
|
2319
|
|
|
|
|
|
|
|
|
2320
|
12113
|
|
|
|
|
|
SPAGAIN; |
|
2321
|
12113
|
|
|
|
|
|
SV *result = POPs; |
|
2322
|
12113
|
|
|
|
|
|
bool val = SvTRUE(result); |
|
2323
|
12113
|
|
|
|
|
|
PUTBACK; |
|
2324
|
12113
|
50
|
|
|
|
|
FREETMPS; |
|
2325
|
12113
|
|
|
|
|
|
LEAVE; |
|
2326
|
|
|
|
|
|
|
|
|
2327
|
12113
|
100
|
|
|
|
|
ST(0) = val ? &PL_sv_no : &PL_sv_yes; |
|
2328
|
12113
|
|
|
|
|
|
XSRETURN(1); |
|
2329
|
|
|
|
|
|
|
} |
|
2330
|
|
|
|
|
|
|
|
|
2331
|
|
|
|
|
|
|
/* once(\&f) - execute once, cache forever */ |
|
2332
|
1005
|
|
|
|
|
|
XS_INTERNAL(xs_once) { |
|
2333
|
1005
|
|
|
|
|
|
dXSARGS; |
|
2334
|
1005
|
50
|
|
|
|
|
if (items != 1) croak("Usage: Func::Util::once(\\&func)"); |
|
2335
|
|
|
|
|
|
|
|
|
2336
|
1005
|
|
|
|
|
|
SV *func = ST(0); |
|
2337
|
1005
|
50
|
|
|
|
|
if (!SvROK(func) || SvTYPE(SvRV(func)) != SVt_PVCV) { |
|
|
|
50
|
|
|
|
|
|
|
2338
|
0
|
|
|
|
|
|
croak("Func::Util::once: argument must be a coderef"); |
|
2339
|
|
|
|
|
|
|
} |
|
2340
|
|
|
|
|
|
|
|
|
2341
|
1005
|
|
|
|
|
|
IV idx = g_once_count++; |
|
2342
|
1005
|
|
|
|
|
|
ensure_once_capacity(idx); |
|
2343
|
|
|
|
|
|
|
|
|
2344
|
1005
|
|
|
|
|
|
OnceFunc *of = &g_onces[idx]; |
|
2345
|
1005
|
|
|
|
|
|
of->func = SvREFCNT_inc_simple_NN(func); |
|
2346
|
1005
|
|
|
|
|
|
of->result = NULL; |
|
2347
|
1005
|
|
|
|
|
|
of->called = FALSE; |
|
2348
|
|
|
|
|
|
|
|
|
2349
|
1005
|
|
|
|
|
|
CV *wrapper = newXS(NULL, xs_once_call, __FILE__); |
|
2350
|
1005
|
|
|
|
|
|
CvXSUBANY(wrapper).any_iv = idx; |
|
2351
|
|
|
|
|
|
|
|
|
2352
|
|
|
|
|
|
|
/* Attach magic for cleanup when wrapper is freed */ |
|
2353
|
1005
|
|
|
|
|
|
sv_magicext((SV*)wrapper, NULL, PERL_MAGIC_ext, &util_once_vtbl, NULL, idx); |
|
2354
|
|
|
|
|
|
|
|
|
2355
|
1005
|
|
|
|
|
|
ST(0) = sv_2mortal(newRV_noinc((SV*)wrapper)); |
|
2356
|
1005
|
|
|
|
|
|
XSRETURN(1); |
|
2357
|
|
|
|
|
|
|
} |
|
2358
|
|
|
|
|
|
|
|
|
2359
|
3015
|
|
|
|
|
|
XS_INTERNAL(xs_once_call) { |
|
2360
|
3015
|
|
|
|
|
|
dXSARGS; |
|
2361
|
|
|
|
|
|
|
PERL_UNUSED_VAR(items); |
|
2362
|
3015
|
|
|
|
|
|
IV idx = CvXSUBANY(cv).any_iv; |
|
2363
|
3015
|
|
|
|
|
|
OnceFunc *of = &g_onces[idx]; |
|
2364
|
|
|
|
|
|
|
|
|
2365
|
3015
|
100
|
|
|
|
|
if (of->called) { |
|
2366
|
2010
|
50
|
|
|
|
|
ST(0) = of->result ? of->result : &PL_sv_undef; |
|
2367
|
2010
|
|
|
|
|
|
XSRETURN(1); |
|
2368
|
|
|
|
|
|
|
} |
|
2369
|
|
|
|
|
|
|
|
|
2370
|
1005
|
|
|
|
|
|
ENTER; |
|
2371
|
1005
|
|
|
|
|
|
SAVETMPS; |
|
2372
|
1005
|
50
|
|
|
|
|
PUSHMARK(SP); |
|
2373
|
1005
|
|
|
|
|
|
PUTBACK; |
|
2374
|
|
|
|
|
|
|
|
|
2375
|
1005
|
|
|
|
|
|
call_sv(of->func, G_SCALAR); |
|
2376
|
|
|
|
|
|
|
|
|
2377
|
1005
|
|
|
|
|
|
SPAGAIN; |
|
2378
|
1005
|
|
|
|
|
|
of->result = SvREFCNT_inc(POPs); |
|
2379
|
1005
|
|
|
|
|
|
of->called = TRUE; |
|
2380
|
1005
|
|
|
|
|
|
PUTBACK; |
|
2381
|
1005
|
50
|
|
|
|
|
FREETMPS; |
|
2382
|
1005
|
|
|
|
|
|
LEAVE; |
|
2383
|
|
|
|
|
|
|
|
|
2384
|
|
|
|
|
|
|
/* Free the original function, no longer needed */ |
|
2385
|
1005
|
|
|
|
|
|
SvREFCNT_dec(of->func); |
|
2386
|
1005
|
|
|
|
|
|
of->func = NULL; |
|
2387
|
|
|
|
|
|
|
|
|
2388
|
1005
|
|
|
|
|
|
ST(0) = of->result; |
|
2389
|
1005
|
|
|
|
|
|
XSRETURN(1); |
|
2390
|
|
|
|
|
|
|
} |
|
2391
|
|
|
|
|
|
|
|
|
2392
|
|
|
|
|
|
|
/* partial(\&f, @bound) - bind first N args */ |
|
2393
|
1012
|
|
|
|
|
|
XS_INTERNAL(xs_partial) { |
|
2394
|
1012
|
|
|
|
|
|
dXSARGS; |
|
2395
|
1012
|
50
|
|
|
|
|
if (items < 1) croak("Usage: Func::Util::partial(\\&func, @bound_args)"); |
|
2396
|
|
|
|
|
|
|
|
|
2397
|
1012
|
|
|
|
|
|
SV *func = ST(0); |
|
2398
|
1012
|
50
|
|
|
|
|
if (!SvROK(func) || SvTYPE(SvRV(func)) != SVt_PVCV) { |
|
|
|
50
|
|
|
|
|
|
|
2399
|
0
|
|
|
|
|
|
croak("Func::Util::partial: first argument must be a coderef"); |
|
2400
|
|
|
|
|
|
|
} |
|
2401
|
|
|
|
|
|
|
|
|
2402
|
1012
|
|
|
|
|
|
IV idx = g_partial_count++; |
|
2403
|
1012
|
|
|
|
|
|
ensure_partial_capacity(idx); |
|
2404
|
|
|
|
|
|
|
|
|
2405
|
1012
|
|
|
|
|
|
PartialFunc *pf = &g_partials[idx]; |
|
2406
|
1012
|
|
|
|
|
|
pf->func = SvREFCNT_inc_simple_NN(func); |
|
2407
|
1012
|
|
|
|
|
|
pf->bound_args = newAV(); |
|
2408
|
|
|
|
|
|
|
|
|
2409
|
|
|
|
|
|
|
/* Store bound arguments */ |
|
2410
|
|
|
|
|
|
|
IV i; |
|
2411
|
2024
|
100
|
|
|
|
|
for (i = 1; i < items; i++) { |
|
2412
|
1012
|
|
|
|
|
|
av_push(pf->bound_args, SvREFCNT_inc_simple_NN(ST(i))); |
|
2413
|
|
|
|
|
|
|
} |
|
2414
|
|
|
|
|
|
|
|
|
2415
|
1012
|
|
|
|
|
|
CV *wrapper = newXS(NULL, xs_partial_call, __FILE__); |
|
2416
|
1012
|
|
|
|
|
|
CvXSUBANY(wrapper).any_iv = idx; |
|
2417
|
|
|
|
|
|
|
|
|
2418
|
|
|
|
|
|
|
/* Attach magic for cleanup when wrapper is freed */ |
|
2419
|
1012
|
|
|
|
|
|
sv_magicext((SV*)wrapper, NULL, PERL_MAGIC_ext, &util_partial_vtbl, NULL, idx); |
|
2420
|
|
|
|
|
|
|
|
|
2421
|
1012
|
|
|
|
|
|
ST(0) = sv_2mortal(newRV_noinc((SV*)wrapper)); |
|
2422
|
1012
|
|
|
|
|
|
XSRETURN(1); |
|
2423
|
|
|
|
|
|
|
} |
|
2424
|
|
|
|
|
|
|
|
|
2425
|
2016
|
|
|
|
|
|
XS_INTERNAL(xs_partial_call) { |
|
2426
|
2016
|
|
|
|
|
|
dXSARGS; |
|
2427
|
2016
|
|
|
|
|
|
IV idx = CvXSUBANY(cv).any_iv; |
|
2428
|
2016
|
|
|
|
|
|
PartialFunc *pf = &g_partials[idx]; |
|
2429
|
|
|
|
|
|
|
|
|
2430
|
2016
|
|
|
|
|
|
IV bound_count = av_len(pf->bound_args) + 1; |
|
2431
|
2016
|
|
|
|
|
|
IV total = bound_count + items; |
|
2432
|
|
|
|
|
|
|
|
|
2433
|
2016
|
|
|
|
|
|
ENTER; |
|
2434
|
2016
|
|
|
|
|
|
SAVETMPS; |
|
2435
|
2016
|
50
|
|
|
|
|
PUSHMARK(SP); |
|
2436
|
|
|
|
|
|
|
|
|
2437
|
2016
|
50
|
|
|
|
|
EXTEND(SP, total); |
|
|
|
50
|
|
|
|
|
|
|
2438
|
|
|
|
|
|
|
|
|
2439
|
|
|
|
|
|
|
/* Push bound args first */ |
|
2440
|
|
|
|
|
|
|
IV i; |
|
2441
|
4032
|
100
|
|
|
|
|
for (i = 0; i < bound_count; i++) { |
|
2442
|
2016
|
|
|
|
|
|
SV **elem = av_fetch(pf->bound_args, i, 0); |
|
2443
|
2016
|
50
|
|
|
|
|
PUSHs(elem ? *elem : &PL_sv_undef); |
|
2444
|
|
|
|
|
|
|
} |
|
2445
|
|
|
|
|
|
|
|
|
2446
|
|
|
|
|
|
|
/* Push call-time args */ |
|
2447
|
4031
|
100
|
|
|
|
|
for (i = 0; i < items; i++) { |
|
2448
|
2015
|
|
|
|
|
|
PUSHs(ST(i)); |
|
2449
|
|
|
|
|
|
|
} |
|
2450
|
2016
|
|
|
|
|
|
PUTBACK; |
|
2451
|
|
|
|
|
|
|
|
|
2452
|
2016
|
|
|
|
|
|
IV count = call_sv(pf->func, G_SCALAR); |
|
2453
|
|
|
|
|
|
|
|
|
2454
|
2016
|
|
|
|
|
|
SPAGAIN; |
|
2455
|
2016
|
50
|
|
|
|
|
SV *result = count > 0 ? POPs : &PL_sv_undef; |
|
2456
|
2016
|
|
|
|
|
|
SvREFCNT_inc(result); |
|
2457
|
2016
|
|
|
|
|
|
PUTBACK; |
|
2458
|
2016
|
50
|
|
|
|
|
FREETMPS; |
|
2459
|
2016
|
|
|
|
|
|
LEAVE; |
|
2460
|
|
|
|
|
|
|
|
|
2461
|
2016
|
|
|
|
|
|
ST(0) = sv_2mortal(result); |
|
2462
|
2016
|
|
|
|
|
|
XSRETURN(1); |
|
2463
|
|
|
|
|
|
|
} |
|
2464
|
|
|
|
|
|
|
|
|
2465
|
|
|
|
|
|
|
/* ============================================ |
|
2466
|
|
|
|
|
|
|
Data extraction functions |
|
2467
|
|
|
|
|
|
|
============================================ */ |
|
2468
|
|
|
|
|
|
|
|
|
2469
|
|
|
|
|
|
|
/* pick($hash, @keys) - extract subset of keys |
|
2470
|
|
|
|
|
|
|
* Returns hashref in scalar context, flattened list in list context */ |
|
2471
|
12722
|
|
|
|
|
|
XS_INTERNAL(xs_pick) { |
|
2472
|
12722
|
|
|
|
|
|
dXSARGS; |
|
2473
|
12722
|
50
|
|
|
|
|
if (items < 1) croak("Usage: Func::Util::pick(\\%%hash, @keys)"); |
|
2474
|
|
|
|
|
|
|
|
|
2475
|
12722
|
|
|
|
|
|
SV *href = ST(0); |
|
2476
|
12722
|
50
|
|
|
|
|
if (!SvROK(href) || SvTYPE(SvRV(href)) != SVt_PVHV) { |
|
|
|
50
|
|
|
|
|
|
|
2477
|
0
|
|
|
|
|
|
croak("Func::Util::pick: first argument must be a hashref"); |
|
2478
|
|
|
|
|
|
|
} |
|
2479
|
|
|
|
|
|
|
|
|
2480
|
12722
|
|
|
|
|
|
HV *src = (HV*)SvRV(href); |
|
2481
|
12722
|
|
|
|
|
|
HV *dest = newHV(); |
|
2482
|
|
|
|
|
|
|
|
|
2483
|
|
|
|
|
|
|
IV i; |
|
2484
|
38566
|
100
|
|
|
|
|
for (i = 1; i < items; i++) { |
|
2485
|
25844
|
|
|
|
|
|
SV *key = ST(i); |
|
2486
|
|
|
|
|
|
|
STRLEN key_len; |
|
2487
|
25844
|
|
|
|
|
|
const char *key_pv = SvPV(key, key_len); |
|
2488
|
|
|
|
|
|
|
|
|
2489
|
25844
|
|
|
|
|
|
SV **val = hv_fetch(src, key_pv, key_len, 0); |
|
2490
|
25844
|
100
|
|
|
|
|
if (val && SvOK(*val)) { |
|
|
|
100
|
|
|
|
|
|
|
2491
|
24238
|
|
|
|
|
|
hv_store(dest, key_pv, key_len, SvREFCNT_inc(*val), 0); |
|
2492
|
|
|
|
|
|
|
} |
|
2493
|
|
|
|
|
|
|
} |
|
2494
|
|
|
|
|
|
|
|
|
2495
|
|
|
|
|
|
|
/* Check calling context */ |
|
2496
|
12722
|
100
|
|
|
|
|
if (GIMME_V == G_ARRAY) { |
|
2497
|
|
|
|
|
|
|
/* List context - return flattened key-value pairs */ |
|
2498
|
10100
|
50
|
|
|
|
|
IV n = HvUSEDKEYS(dest); |
|
2499
|
10100
|
|
|
|
|
|
SP -= items; /* Reset stack pointer */ |
|
2500
|
10100
|
50
|
|
|
|
|
EXTEND(SP, n * 2); |
|
|
|
50
|
|
|
|
|
|
|
2501
|
|
|
|
|
|
|
|
|
2502
|
10100
|
|
|
|
|
|
hv_iterinit(dest); |
|
2503
|
|
|
|
|
|
|
HE *he; |
|
2504
|
30300
|
100
|
|
|
|
|
while ((he = hv_iternext(dest)) != NULL) { |
|
2505
|
|
|
|
|
|
|
STRLEN klen; |
|
2506
|
20200
|
50
|
|
|
|
|
const char *key = HePV(he, klen); |
|
2507
|
20200
|
|
|
|
|
|
mPUSHp(key, klen); |
|
2508
|
20200
|
|
|
|
|
|
mPUSHs(SvREFCNT_inc(HeVAL(he))); |
|
2509
|
|
|
|
|
|
|
} |
|
2510
|
10100
|
|
|
|
|
|
SvREFCNT_dec((SV*)dest); /* Free the temp hash */ |
|
2511
|
10100
|
|
|
|
|
|
PUTBACK; |
|
2512
|
10100
|
|
|
|
|
|
return; |
|
2513
|
|
|
|
|
|
|
} |
|
2514
|
|
|
|
|
|
|
|
|
2515
|
|
|
|
|
|
|
/* Scalar context - return hashref */ |
|
2516
|
2622
|
|
|
|
|
|
ST(0) = sv_2mortal(newRV_noinc((SV*)dest)); |
|
2517
|
2622
|
|
|
|
|
|
XSRETURN(1); |
|
2518
|
|
|
|
|
|
|
} |
|
2519
|
|
|
|
|
|
|
|
|
2520
|
|
|
|
|
|
|
/* pluck(\@hashes, $field) - extract field from each hash */ |
|
2521
|
2211
|
|
|
|
|
|
XS_INTERNAL(xs_pluck) { |
|
2522
|
2211
|
|
|
|
|
|
dXSARGS; |
|
2523
|
2211
|
50
|
|
|
|
|
if (items != 2) croak("Usage: Func::Util::pluck(\\@array, $field)"); |
|
2524
|
|
|
|
|
|
|
|
|
2525
|
2211
|
|
|
|
|
|
SV *aref = ST(0); |
|
2526
|
2211
|
50
|
|
|
|
|
if (!SvROK(aref) || SvTYPE(SvRV(aref)) != SVt_PVAV) { |
|
|
|
50
|
|
|
|
|
|
|
2527
|
0
|
|
|
|
|
|
croak("Func::Util::pluck: first argument must be an arrayref"); |
|
2528
|
|
|
|
|
|
|
} |
|
2529
|
|
|
|
|
|
|
|
|
2530
|
2211
|
|
|
|
|
|
SV *field = ST(1); |
|
2531
|
|
|
|
|
|
|
STRLEN field_len; |
|
2532
|
2211
|
|
|
|
|
|
const char *field_pv = SvPV(field, field_len); |
|
2533
|
|
|
|
|
|
|
|
|
2534
|
2211
|
|
|
|
|
|
AV *src = (AV*)SvRV(aref); |
|
2535
|
2211
|
|
|
|
|
|
IV len = av_len(src) + 1; |
|
2536
|
2211
|
|
|
|
|
|
AV *dest = newAV(); |
|
2537
|
2211
|
|
|
|
|
|
av_extend(dest, len - 1); |
|
2538
|
|
|
|
|
|
|
|
|
2539
|
|
|
|
|
|
|
IV i; |
|
2540
|
8836
|
100
|
|
|
|
|
for (i = 0; i < len; i++) { |
|
2541
|
6625
|
|
|
|
|
|
SV **elem = av_fetch(src, i, 0); |
|
2542
|
13250
|
50
|
|
|
|
|
if (elem && SvROK(*elem) && SvTYPE(SvRV(*elem)) == SVt_PVHV) { |
|
|
|
50
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
2543
|
6625
|
|
|
|
|
|
HV *hv = (HV*)SvRV(*elem); |
|
2544
|
6625
|
|
|
|
|
|
SV **val = hv_fetch(hv, field_pv, field_len, 0); |
|
2545
|
6625
|
100
|
|
|
|
|
if (val && SvOK(*val)) { |
|
|
|
50
|
|
|
|
|
|
|
2546
|
5422
|
|
|
|
|
|
av_push(dest, SvREFCNT_inc(*val)); |
|
2547
|
|
|
|
|
|
|
} else { |
|
2548
|
1203
|
|
|
|
|
|
av_push(dest, &PL_sv_undef); |
|
2549
|
|
|
|
|
|
|
} |
|
2550
|
|
|
|
|
|
|
} else { |
|
2551
|
0
|
|
|
|
|
|
av_push(dest, &PL_sv_undef); |
|
2552
|
|
|
|
|
|
|
} |
|
2553
|
|
|
|
|
|
|
} |
|
2554
|
|
|
|
|
|
|
|
|
2555
|
2211
|
|
|
|
|
|
ST(0) = sv_2mortal(newRV_noinc((SV*)dest)); |
|
2556
|
2211
|
|
|
|
|
|
XSRETURN(1); |
|
2557
|
|
|
|
|
|
|
} |
|
2558
|
|
|
|
|
|
|
|
|
2559
|
|
|
|
|
|
|
/* omit($hash, @keys) - exclude subset of keys (inverse of pick) |
|
2560
|
|
|
|
|
|
|
* Returns hashref in scalar context, flattened list in list context */ |
|
2561
|
12710
|
|
|
|
|
|
XS_INTERNAL(xs_omit) { |
|
2562
|
12710
|
|
|
|
|
|
dXSARGS; |
|
2563
|
12710
|
50
|
|
|
|
|
if (items < 1) croak("Usage: Func::Util::omit(\\%%hash, @keys)"); |
|
2564
|
|
|
|
|
|
|
|
|
2565
|
12710
|
|
|
|
|
|
SV *href = ST(0); |
|
2566
|
12710
|
50
|
|
|
|
|
if (!SvROK(href) || SvTYPE(SvRV(href)) != SVt_PVHV) { |
|
|
|
50
|
|
|
|
|
|
|
2567
|
0
|
|
|
|
|
|
croak("Func::Util::omit: first argument must be a hashref"); |
|
2568
|
|
|
|
|
|
|
} |
|
2569
|
|
|
|
|
|
|
|
|
2570
|
12710
|
|
|
|
|
|
HV *src = (HV*)SvRV(href); |
|
2571
|
12710
|
|
|
|
|
|
HV *dest = newHV(); |
|
2572
|
|
|
|
|
|
|
|
|
2573
|
|
|
|
|
|
|
/* Build exclusion set for O(1) lookup */ |
|
2574
|
12710
|
|
|
|
|
|
HV *exclude = newHV(); |
|
2575
|
|
|
|
|
|
|
IV i; |
|
2576
|
28025
|
100
|
|
|
|
|
for (i = 1; i < items; i++) { |
|
2577
|
15315
|
|
|
|
|
|
SV *key = ST(i); |
|
2578
|
|
|
|
|
|
|
STRLEN key_len; |
|
2579
|
15315
|
|
|
|
|
|
const char *key_pv = SvPV(key, key_len); |
|
2580
|
15315
|
|
|
|
|
|
hv_store(exclude, key_pv, key_len, &PL_sv_yes, 0); |
|
2581
|
|
|
|
|
|
|
} |
|
2582
|
|
|
|
|
|
|
|
|
2583
|
|
|
|
|
|
|
/* Iterate source, copy non-excluded keys */ |
|
2584
|
12710
|
|
|
|
|
|
hv_iterinit(src); |
|
2585
|
|
|
|
|
|
|
HE *entry; |
|
2586
|
53838
|
100
|
|
|
|
|
while ((entry = hv_iternext(src)) != NULL) { |
|
2587
|
41128
|
|
|
|
|
|
SV *key = hv_iterkeysv(entry); |
|
2588
|
|
|
|
|
|
|
STRLEN key_len; |
|
2589
|
41128
|
|
|
|
|
|
const char *key_pv = SvPV(key, key_len); |
|
2590
|
|
|
|
|
|
|
|
|
2591
|
41128
|
100
|
|
|
|
|
if (!hv_exists(exclude, key_pv, key_len)) { |
|
2592
|
26219
|
|
|
|
|
|
SV *val = hv_iterval(src, entry); |
|
2593
|
26219
|
50
|
|
|
|
|
if (SvOK(val)) { |
|
2594
|
26219
|
|
|
|
|
|
hv_store(dest, key_pv, key_len, SvREFCNT_inc(val), 0); |
|
2595
|
|
|
|
|
|
|
} |
|
2596
|
|
|
|
|
|
|
} |
|
2597
|
|
|
|
|
|
|
} |
|
2598
|
|
|
|
|
|
|
|
|
2599
|
12710
|
|
|
|
|
|
SvREFCNT_dec((SV*)exclude); |
|
2600
|
|
|
|
|
|
|
|
|
2601
|
|
|
|
|
|
|
/* Check calling context */ |
|
2602
|
12710
|
100
|
|
|
|
|
if (GIMME_V == G_ARRAY) { |
|
2603
|
|
|
|
|
|
|
/* List context - return flattened key-value pairs */ |
|
2604
|
10100
|
50
|
|
|
|
|
IV n = HvUSEDKEYS(dest); |
|
2605
|
10100
|
|
|
|
|
|
SP -= items; /* Reset stack pointer */ |
|
2606
|
10100
|
50
|
|
|
|
|
EXTEND(SP, n * 2); |
|
|
|
50
|
|
|
|
|
|
|
2607
|
|
|
|
|
|
|
|
|
2608
|
10100
|
|
|
|
|
|
hv_iterinit(dest); |
|
2609
|
|
|
|
|
|
|
HE *he; |
|
2610
|
30300
|
100
|
|
|
|
|
while ((he = hv_iternext(dest)) != NULL) { |
|
2611
|
|
|
|
|
|
|
STRLEN klen; |
|
2612
|
20200
|
50
|
|
|
|
|
const char *key = HePV(he, klen); |
|
2613
|
20200
|
|
|
|
|
|
mPUSHp(key, klen); |
|
2614
|
20200
|
|
|
|
|
|
mPUSHs(SvREFCNT_inc(HeVAL(he))); |
|
2615
|
|
|
|
|
|
|
} |
|
2616
|
10100
|
|
|
|
|
|
SvREFCNT_dec((SV*)dest); /* Free the temp hash */ |
|
2617
|
10100
|
|
|
|
|
|
PUTBACK; |
|
2618
|
10100
|
|
|
|
|
|
return; |
|
2619
|
|
|
|
|
|
|
} |
|
2620
|
|
|
|
|
|
|
|
|
2621
|
|
|
|
|
|
|
/* Scalar context - return hashref */ |
|
2622
|
2610
|
|
|
|
|
|
ST(0) = sv_2mortal(newRV_noinc((SV*)dest)); |
|
2623
|
2610
|
|
|
|
|
|
XSRETURN(1); |
|
2624
|
|
|
|
|
|
|
} |
|
2625
|
|
|
|
|
|
|
|
|
2626
|
|
|
|
|
|
|
/* uniq(@list) - return unique elements (preserves order) */ |
|
2627
|
2626
|
|
|
|
|
|
XS_INTERNAL(xs_uniq) { |
|
2628
|
2626
|
|
|
|
|
|
dXSARGS; |
|
2629
|
|
|
|
|
|
|
|
|
2630
|
2626
|
100
|
|
|
|
|
if (items == 0) { |
|
2631
|
1
|
|
|
|
|
|
XSRETURN(0); |
|
2632
|
|
|
|
|
|
|
} |
|
2633
|
|
|
|
|
|
|
|
|
2634
|
2625
|
100
|
|
|
|
|
if (items == 1) { |
|
2635
|
1611
|
|
|
|
|
|
XSRETURN(1); |
|
2636
|
|
|
|
|
|
|
} |
|
2637
|
|
|
|
|
|
|
|
|
2638
|
|
|
|
|
|
|
/* For small lists, use simple O(n^2) - faster due to no hash overhead */ |
|
2639
|
1014
|
100
|
|
|
|
|
if (items <= 8) { |
|
2640
|
13
|
|
|
|
|
|
IV out = 0; |
|
2641
|
|
|
|
|
|
|
IV i, j; |
|
2642
|
77
|
100
|
|
|
|
|
for (i = 0; i < items; i++) { |
|
2643
|
64
|
|
|
|
|
|
SV *elem = ST(i); |
|
2644
|
|
|
|
|
|
|
STRLEN len_i; |
|
2645
|
64
|
100
|
|
|
|
|
const char *key_i = SvOK(elem) ? SvPV_const(elem, len_i) : "\x00UNDEF\x00"; |
|
2646
|
64
|
100
|
|
|
|
|
if (!SvOK(elem)) len_i = 7; |
|
2647
|
|
|
|
|
|
|
|
|
2648
|
64
|
|
|
|
|
|
bool dup = FALSE; |
|
2649
|
131
|
100
|
|
|
|
|
for (j = 0; j < out; j++) { |
|
2650
|
91
|
|
|
|
|
|
SV *prev = ST(j); |
|
2651
|
|
|
|
|
|
|
STRLEN len_j; |
|
2652
|
91
|
100
|
|
|
|
|
const char *key_j = SvOK(prev) ? SvPV_const(prev, len_j) : "\x00UNDEF\x00"; |
|
2653
|
91
|
100
|
|
|
|
|
if (!SvOK(prev)) len_j = 7; |
|
2654
|
|
|
|
|
|
|
|
|
2655
|
91
|
100
|
|
|
|
|
if (len_i == len_j && memcmp(key_i, key_j, len_i) == 0) { |
|
|
|
100
|
|
|
|
|
|
|
2656
|
24
|
|
|
|
|
|
dup = TRUE; |
|
2657
|
24
|
|
|
|
|
|
break; |
|
2658
|
|
|
|
|
|
|
} |
|
2659
|
|
|
|
|
|
|
} |
|
2660
|
64
|
100
|
|
|
|
|
if (!dup) ST(out++) = elem; |
|
2661
|
|
|
|
|
|
|
} |
|
2662
|
13
|
|
|
|
|
|
XSRETURN(out); |
|
2663
|
|
|
|
|
|
|
} |
|
2664
|
|
|
|
|
|
|
|
|
2665
|
1001
|
|
|
|
|
|
HV *seen = newHV(); |
|
2666
|
1001
|
|
|
|
|
|
IV out = 0; |
|
2667
|
1001
|
|
|
|
|
|
hv_ksplit(seen, items); |
|
2668
|
|
|
|
|
|
|
|
|
2669
|
|
|
|
|
|
|
IV i; |
|
2670
|
11011
|
100
|
|
|
|
|
for (i = 0; i < items; i++) { |
|
2671
|
10010
|
|
|
|
|
|
SV *elem = ST(i); |
|
2672
|
|
|
|
|
|
|
STRLEN len; |
|
2673
|
|
|
|
|
|
|
const char *key; |
|
2674
|
|
|
|
|
|
|
U32 hash; |
|
2675
|
|
|
|
|
|
|
|
|
2676
|
10010
|
50
|
|
|
|
|
key = SvOK(elem) ? SvPV_const(elem, len) : (len = 7, "\x00UNDEF\x00"); |
|
2677
|
|
|
|
|
|
|
|
|
2678
|
10010
|
50
|
|
|
|
|
PERL_HASH(hash, key, len); |
|
2679
|
|
|
|
|
|
|
|
|
2680
|
10010
|
100
|
|
|
|
|
if (!hv_common(seen, NULL, key, len, 0, HV_FETCH_ISEXISTS, NULL, hash)) { |
|
2681
|
4004
|
|
|
|
|
|
hv_common(seen, NULL, key, len, 0, HV_FETCH_ISSTORE, &PL_sv_yes, hash); |
|
2682
|
4004
|
|
|
|
|
|
ST(out++) = elem; |
|
2683
|
|
|
|
|
|
|
} |
|
2684
|
|
|
|
|
|
|
} |
|
2685
|
|
|
|
|
|
|
|
|
2686
|
1001
|
|
|
|
|
|
SvREFCNT_dec_NN((SV*)seen); |
|
2687
|
1001
|
|
|
|
|
|
XSRETURN(out); |
|
2688
|
|
|
|
|
|
|
} |
|
2689
|
|
|
|
|
|
|
|
|
2690
|
|
|
|
|
|
|
/* partition(\&pred, @list) - split into [matches], [non-matches] */ |
|
2691
|
2210
|
|
|
|
|
|
XS_INTERNAL(xs_partition) { |
|
2692
|
2210
|
|
|
|
|
|
dXSARGS; |
|
2693
|
2210
|
50
|
|
|
|
|
if (items < 1) croak("Usage: Func::Util::partition(\\&block, @list)"); |
|
2694
|
|
|
|
|
|
|
|
|
2695
|
2210
|
|
|
|
|
|
SV *block = ST(0); |
|
2696
|
2210
|
50
|
|
|
|
|
if (!SvROK(block) || SvTYPE(SvRV(block)) != SVt_PVCV) { |
|
|
|
50
|
|
|
|
|
|
|
2697
|
0
|
|
|
|
|
|
croak("Func::Util::partition: first argument must be a coderef"); |
|
2698
|
|
|
|
|
|
|
} |
|
2699
|
|
|
|
|
|
|
|
|
2700
|
2210
|
|
|
|
|
|
IV list_len = items - 1; |
|
2701
|
|
|
|
|
|
|
|
|
2702
|
2210
|
100
|
|
|
|
|
if (list_len == 0) { |
|
2703
|
1
|
|
|
|
|
|
AV *pass = newAV(); |
|
2704
|
1
|
|
|
|
|
|
AV *fail = newAV(); |
|
2705
|
1
|
|
|
|
|
|
AV *outer = newAV(); |
|
2706
|
1
|
|
|
|
|
|
av_push(outer, newRV_noinc((SV*)pass)); |
|
2707
|
1
|
|
|
|
|
|
av_push(outer, newRV_noinc((SV*)fail)); |
|
2708
|
1
|
|
|
|
|
|
ST(0) = sv_2mortal(newRV_noinc((SV*)outer)); |
|
2709
|
1
|
|
|
|
|
|
XSRETURN(1); |
|
2710
|
|
|
|
|
|
|
} |
|
2711
|
|
|
|
|
|
|
|
|
2712
|
2209
|
|
|
|
|
|
AV *pass = newAV(); |
|
2713
|
2209
|
|
|
|
|
|
AV *fail = newAV(); |
|
2714
|
2209
|
|
|
|
|
|
av_extend(pass, list_len >> 1); |
|
2715
|
2209
|
|
|
|
|
|
av_extend(fail, list_len >> 1); |
|
2716
|
|
|
|
|
|
|
|
|
2717
|
2209
|
50
|
|
|
|
|
SV *orig_defsv = DEFSV; |
|
2718
|
|
|
|
|
|
|
|
|
2719
|
|
|
|
|
|
|
IV i; |
|
2720
|
4452
|
100
|
|
|
|
|
for (i = 1; i < items; i++) { |
|
2721
|
2243
|
|
|
|
|
|
SV *elem = ST(i); |
|
2722
|
|
|
|
|
|
|
|
|
2723
|
2243
|
|
|
|
|
|
DEFSV_set(elem); |
|
2724
|
|
|
|
|
|
|
|
|
2725
|
2243
|
|
|
|
|
|
ENTER; |
|
2726
|
2243
|
|
|
|
|
|
SAVETMPS; |
|
2727
|
2243
|
50
|
|
|
|
|
PUSHMARK(SP); |
|
2728
|
2243
|
50
|
|
|
|
|
XPUSHs(elem); |
|
2729
|
2243
|
|
|
|
|
|
PUTBACK; |
|
2730
|
|
|
|
|
|
|
|
|
2731
|
2243
|
|
|
|
|
|
call_sv(block, G_SCALAR); |
|
2732
|
|
|
|
|
|
|
|
|
2733
|
2243
|
|
|
|
|
|
SPAGAIN; |
|
2734
|
2243
|
|
|
|
|
|
SV *result = POPs; |
|
2735
|
2243
|
|
|
|
|
|
bool matched = SvTRUE(result); |
|
2736
|
2243
|
|
|
|
|
|
PUTBACK; |
|
2737
|
2243
|
50
|
|
|
|
|
FREETMPS; |
|
2738
|
2243
|
|
|
|
|
|
LEAVE; |
|
2739
|
|
|
|
|
|
|
|
|
2740
|
2243
|
100
|
|
|
|
|
if (matched) { |
|
2741
|
2222
|
|
|
|
|
|
av_push(pass, SvREFCNT_inc_simple_NN(elem)); |
|
2742
|
|
|
|
|
|
|
} else { |
|
2743
|
21
|
|
|
|
|
|
av_push(fail, SvREFCNT_inc_simple_NN(elem)); |
|
2744
|
|
|
|
|
|
|
} |
|
2745
|
|
|
|
|
|
|
} |
|
2746
|
|
|
|
|
|
|
|
|
2747
|
2209
|
|
|
|
|
|
DEFSV_set(orig_defsv); |
|
2748
|
|
|
|
|
|
|
|
|
2749
|
2209
|
|
|
|
|
|
AV *outer = newAV(); |
|
2750
|
2209
|
|
|
|
|
|
av_push(outer, newRV_noinc((SV*)pass)); |
|
2751
|
2209
|
|
|
|
|
|
av_push(outer, newRV_noinc((SV*)fail)); |
|
2752
|
|
|
|
|
|
|
|
|
2753
|
2209
|
|
|
|
|
|
ST(0) = sv_2mortal(newRV_noinc((SV*)outer)); |
|
2754
|
2209
|
|
|
|
|
|
XSRETURN(1); |
|
2755
|
|
|
|
|
|
|
} |
|
2756
|
|
|
|
|
|
|
|
|
2757
|
|
|
|
|
|
|
/* defaults($hash, $defaults) - fill in missing keys from defaults |
|
2758
|
|
|
|
|
|
|
* Returns hashref in scalar context, flattened list in list context */ |
|
2759
|
11507
|
|
|
|
|
|
XS_INTERNAL(xs_defaults) { |
|
2760
|
11507
|
|
|
|
|
|
dXSARGS; |
|
2761
|
11507
|
50
|
|
|
|
|
if (items != 2) croak("Usage: Func::Util::defaults(\\%%hash, \\%%defaults)"); |
|
2762
|
|
|
|
|
|
|
|
|
2763
|
11507
|
|
|
|
|
|
SV *href = ST(0); |
|
2764
|
11507
|
|
|
|
|
|
SV *dref = ST(1); |
|
2765
|
|
|
|
|
|
|
|
|
2766
|
11507
|
50
|
|
|
|
|
if (!SvROK(href) || SvTYPE(SvRV(href)) != SVt_PVHV) { |
|
|
|
50
|
|
|
|
|
|
|
2767
|
0
|
|
|
|
|
|
croak("Func::Util::defaults: first argument must be a hashref"); |
|
2768
|
|
|
|
|
|
|
} |
|
2769
|
11507
|
50
|
|
|
|
|
if (!SvROK(dref) || SvTYPE(SvRV(dref)) != SVt_PVHV) { |
|
|
|
50
|
|
|
|
|
|
|
2770
|
0
|
|
|
|
|
|
croak("Func::Util::defaults: second argument must be a hashref"); |
|
2771
|
|
|
|
|
|
|
} |
|
2772
|
|
|
|
|
|
|
|
|
2773
|
11507
|
|
|
|
|
|
HV *src = (HV*)SvRV(href); |
|
2774
|
11507
|
|
|
|
|
|
HV *def = (HV*)SvRV(dref); |
|
2775
|
|
|
|
|
|
|
|
|
2776
|
|
|
|
|
|
|
/* Pre-size dest hash */ |
|
2777
|
11507
|
50
|
|
|
|
|
IV src_keys = HvUSEDKEYS(src); |
|
2778
|
11507
|
50
|
|
|
|
|
IV def_keys = HvUSEDKEYS(def); |
|
2779
|
11507
|
|
|
|
|
|
HV *dest = newHV(); |
|
2780
|
11507
|
|
|
|
|
|
hv_ksplit(dest, src_keys + def_keys); |
|
2781
|
|
|
|
|
|
|
|
|
2782
|
|
|
|
|
|
|
/* Copy all from source first */ |
|
2783
|
11507
|
|
|
|
|
|
hv_iterinit(src); |
|
2784
|
|
|
|
|
|
|
HE *entry; |
|
2785
|
24014
|
100
|
|
|
|
|
while ((entry = hv_iternext(src)) != NULL) { |
|
2786
|
|
|
|
|
|
|
STRLEN key_len; |
|
2787
|
12507
|
50
|
|
|
|
|
const char *key_pv = HePV(entry, key_len); |
|
2788
|
12507
|
|
|
|
|
|
SV *val = HeVAL(entry); |
|
2789
|
12507
|
|
|
|
|
|
hv_store(dest, key_pv, key_len, SvREFCNT_inc_simple_NN(val), HeHASH(entry)); |
|
2790
|
|
|
|
|
|
|
} |
|
2791
|
|
|
|
|
|
|
|
|
2792
|
|
|
|
|
|
|
/* Fill in missing from defaults - use pre-computed hash */ |
|
2793
|
11507
|
|
|
|
|
|
hv_iterinit(def); |
|
2794
|
35918
|
100
|
|
|
|
|
while ((entry = hv_iternext(def)) != NULL) { |
|
2795
|
|
|
|
|
|
|
STRLEN key_len; |
|
2796
|
24411
|
50
|
|
|
|
|
const char *key_pv = HePV(entry, key_len); |
|
2797
|
24411
|
|
|
|
|
|
U32 hash = HeHASH(entry); |
|
2798
|
|
|
|
|
|
|
|
|
2799
|
|
|
|
|
|
|
/* Check if exists and is defined in dest */ |
|
2800
|
24411
|
|
|
|
|
|
SV **existing = hv_fetch(dest, key_pv, key_len, 0); |
|
2801
|
24411
|
100
|
|
|
|
|
if (!existing || !SvOK(*existing)) { |
|
|
|
100
|
|
|
|
|
|
|
2802
|
12909
|
|
|
|
|
|
SV *val = HeVAL(entry); |
|
2803
|
12909
|
|
|
|
|
|
hv_store(dest, key_pv, key_len, SvREFCNT_inc_simple_NN(val), hash); |
|
2804
|
|
|
|
|
|
|
} |
|
2805
|
|
|
|
|
|
|
} |
|
2806
|
|
|
|
|
|
|
|
|
2807
|
|
|
|
|
|
|
/* Check calling context */ |
|
2808
|
11507
|
100
|
|
|
|
|
if (GIMME_V == G_ARRAY) { |
|
2809
|
|
|
|
|
|
|
/* List context - return flattened key-value pairs */ |
|
2810
|
10100
|
50
|
|
|
|
|
IV n = HvUSEDKEYS(dest); |
|
2811
|
10100
|
|
|
|
|
|
SP -= items; /* Reset stack pointer */ |
|
2812
|
10100
|
50
|
|
|
|
|
EXTEND(SP, n * 2); |
|
|
|
50
|
|
|
|
|
|
|
2813
|
|
|
|
|
|
|
|
|
2814
|
10100
|
|
|
|
|
|
hv_iterinit(dest); |
|
2815
|
|
|
|
|
|
|
HE *he; |
|
2816
|
30300
|
100
|
|
|
|
|
while ((he = hv_iternext(dest)) != NULL) { |
|
2817
|
|
|
|
|
|
|
STRLEN klen; |
|
2818
|
20200
|
50
|
|
|
|
|
const char *key = HePV(he, klen); |
|
2819
|
20200
|
|
|
|
|
|
mPUSHp(key, klen); |
|
2820
|
20200
|
|
|
|
|
|
mPUSHs(SvREFCNT_inc(HeVAL(he))); |
|
2821
|
|
|
|
|
|
|
} |
|
2822
|
10100
|
|
|
|
|
|
SvREFCNT_dec((SV*)dest); /* Free the temp hash */ |
|
2823
|
10100
|
|
|
|
|
|
PUTBACK; |
|
2824
|
10100
|
|
|
|
|
|
return; |
|
2825
|
|
|
|
|
|
|
} |
|
2826
|
|
|
|
|
|
|
|
|
2827
|
|
|
|
|
|
|
/* Scalar context - return hashref */ |
|
2828
|
1407
|
|
|
|
|
|
ST(0) = sv_2mortal(newRV_noinc((SV*)dest)); |
|
2829
|
1407
|
|
|
|
|
|
XSRETURN(1); |
|
2830
|
|
|
|
|
|
|
} |
|
2831
|
|
|
|
|
|
|
|
|
2832
|
|
|
|
|
|
|
/* ============================================ |
|
2833
|
|
|
|
|
|
|
Null coalescing functions |
|
2834
|
|
|
|
|
|
|
============================================ */ |
|
2835
|
|
|
|
|
|
|
|
|
2836
|
|
|
|
|
|
|
/* nvl($x, $default) - return $x if defined, else $default */ |
|
2837
|
20128
|
|
|
|
|
|
XS_INTERNAL(xs_nvl) { |
|
2838
|
20128
|
|
|
|
|
|
dXSARGS; |
|
2839
|
20128
|
50
|
|
|
|
|
if (items != 2) croak("Usage: Func::Util::nvl($value, $default)"); |
|
2840
|
|
|
|
|
|
|
|
|
2841
|
20128
|
|
|
|
|
|
SV *val = ST(0); |
|
2842
|
20128
|
100
|
|
|
|
|
if (SvOK(val)) { |
|
2843
|
8010
|
|
|
|
|
|
XSRETURN(1); /* Return first arg */ |
|
2844
|
|
|
|
|
|
|
} |
|
2845
|
12118
|
|
|
|
|
|
ST(0) = ST(1); |
|
2846
|
12118
|
|
|
|
|
|
XSRETURN(1); |
|
2847
|
|
|
|
|
|
|
} |
|
2848
|
|
|
|
|
|
|
|
|
2849
|
|
|
|
|
|
|
/* coalesce($a, $b, ...) - return first defined value */ |
|
2850
|
18116
|
|
|
|
|
|
XS_INTERNAL(xs_coalesce) { |
|
2851
|
18116
|
|
|
|
|
|
dXSARGS; |
|
2852
|
18116
|
50
|
|
|
|
|
if (items < 1) croak("Usage: Func::Util::coalesce($val, ...)"); |
|
2853
|
|
|
|
|
|
|
|
|
2854
|
|
|
|
|
|
|
IV i; |
|
2855
|
48332
|
100
|
|
|
|
|
for (i = 0; i < items; i++) { |
|
2856
|
47329
|
100
|
|
|
|
|
if (SvOK(ST(i))) { |
|
2857
|
17113
|
|
|
|
|
|
ST(0) = ST(i); |
|
2858
|
17113
|
|
|
|
|
|
XSRETURN(1); |
|
2859
|
|
|
|
|
|
|
} |
|
2860
|
|
|
|
|
|
|
} |
|
2861
|
|
|
|
|
|
|
/* All undefined, return undef */ |
|
2862
|
1003
|
|
|
|
|
|
ST(0) = &PL_sv_undef; |
|
2863
|
1003
|
|
|
|
|
|
XSRETURN(1); |
|
2864
|
|
|
|
|
|
|
} |
|
2865
|
|
|
|
|
|
|
|
|
2866
|
|
|
|
|
|
|
/* ============================================ |
|
2867
|
|
|
|
|
|
|
List functions (first, any, all, none) |
|
2868
|
|
|
|
|
|
|
|
|
2869
|
|
|
|
|
|
|
These use MULTICALL for pure Perl subs which is significantly |
|
2870
|
|
|
|
|
|
|
faster than call_sv() for repeated invocations. |
|
2871
|
|
|
|
|
|
|
|
|
2872
|
|
|
|
|
|
|
For XS subs, we fall back to call_sv(). |
|
2873
|
|
|
|
|
|
|
============================================ */ |
|
2874
|
|
|
|
|
|
|
|
|
2875
|
|
|
|
|
|
|
/* Inline CALLRUNOPS - experimental optimization to skip function call overhead. |
|
2876
|
|
|
|
|
|
|
Use cautiously - this inlines the runops loop directly. */ |
|
2877
|
|
|
|
|
|
|
#define INLINE_RUNOPS() \ |
|
2878
|
|
|
|
|
|
|
STMT_START { \ |
|
2879
|
|
|
|
|
|
|
OP *_inline_op = PL_op; \ |
|
2880
|
|
|
|
|
|
|
while ((_inline_op = _inline_op->op_ppaddr(aTHX))) ; \ |
|
2881
|
|
|
|
|
|
|
} STMT_END |
|
2882
|
|
|
|
|
|
|
|
|
2883
|
|
|
|
|
|
|
/* ============================================ |
|
2884
|
|
|
|
|
|
|
Specialized array predicates - pure C, no callback |
|
2885
|
|
|
|
|
|
|
These are blazing fast because they avoid all Perl callback overhead |
|
2886
|
|
|
|
|
|
|
============================================ */ |
|
2887
|
|
|
|
|
|
|
|
|
2888
|
|
|
|
|
|
|
/* first_gt(\@array, $threshold) or first_gt(\@array, $key, $threshold) |
|
2889
|
|
|
|
|
|
|
first element > threshold, pure C |
|
2890
|
|
|
|
|
|
|
With key: first hash where hash->{key} > threshold */ |
|
2891
|
3027
|
|
|
|
|
|
XS_INTERNAL(xs_first_gt) { |
|
2892
|
3027
|
|
|
|
|
|
dXSARGS; |
|
2893
|
3027
|
50
|
|
|
|
|
if (items < 2 || items > 3) croak("Usage: Func::Util::first_gt(\\@array, $threshold) or first_gt(\\@array, $key, $threshold)"); |
|
|
|
50
|
|
|
|
|
|
|
2894
|
|
|
|
|
|
|
|
|
2895
|
3027
|
|
|
|
|
|
SV *aref = ST(0); |
|
2896
|
3027
|
50
|
|
|
|
|
if (!SvROK(aref) || SvTYPE(SvRV(aref)) != SVt_PVAV) { |
|
|
|
50
|
|
|
|
|
|
|
2897
|
0
|
|
|
|
|
|
croak("Func::Util::first_gt: first argument must be an arrayref"); |
|
2898
|
|
|
|
|
|
|
} |
|
2899
|
|
|
|
|
|
|
|
|
2900
|
3027
|
|
|
|
|
|
AV *av = (AV *)SvRV(aref); |
|
2901
|
3027
|
|
|
|
|
|
SSize_t len = av_len(av) + 1; |
|
2902
|
|
|
|
|
|
|
SSize_t i; |
|
2903
|
|
|
|
|
|
|
|
|
2904
|
3027
|
100
|
|
|
|
|
if (items == 2) { |
|
2905
|
|
|
|
|
|
|
/* Simple array of scalars */ |
|
2906
|
2026
|
|
|
|
|
|
NV threshold = SvNV(ST(1)); |
|
2907
|
12105
|
100
|
|
|
|
|
for (i = 0; i < len; i++) { |
|
2908
|
11097
|
|
|
|
|
|
SV **elem = av_fetch(av, i, 0); |
|
2909
|
11097
|
50
|
|
|
|
|
if (elem && SvNV(*elem) > threshold) { |
|
|
|
100
|
|
|
|
|
|
|
2910
|
1018
|
|
|
|
|
|
ST(0) = *elem; |
|
2911
|
1018
|
|
|
|
|
|
XSRETURN(1); |
|
2912
|
|
|
|
|
|
|
} |
|
2913
|
|
|
|
|
|
|
} |
|
2914
|
|
|
|
|
|
|
} else { |
|
2915
|
|
|
|
|
|
|
/* Array of hashes with key */ |
|
2916
|
1001
|
|
|
|
|
|
char *key = SvPV_nolen(ST(1)); |
|
2917
|
1001
|
|
|
|
|
|
NV threshold = SvNV(ST(2)); |
|
2918
|
2002
|
50
|
|
|
|
|
for (i = 0; i < len; i++) { |
|
2919
|
2002
|
|
|
|
|
|
SV **elem = av_fetch(av, i, 0); |
|
2920
|
2002
|
50
|
|
|
|
|
if (elem && SvROK(*elem) && SvTYPE(SvRV(*elem)) == SVt_PVHV) { |
|
|
|
50
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
2921
|
2002
|
|
|
|
|
|
HV *hv = (HV *)SvRV(*elem); |
|
2922
|
2002
|
|
|
|
|
|
SV **val = hv_fetch(hv, key, strlen(key), 0); |
|
2923
|
2002
|
50
|
|
|
|
|
if (val && SvNV(*val) > threshold) { |
|
|
|
100
|
|
|
|
|
|
|
2924
|
1001
|
|
|
|
|
|
ST(0) = *elem; |
|
2925
|
1001
|
|
|
|
|
|
XSRETURN(1); |
|
2926
|
|
|
|
|
|
|
} |
|
2927
|
|
|
|
|
|
|
} |
|
2928
|
|
|
|
|
|
|
} |
|
2929
|
|
|
|
|
|
|
} |
|
2930
|
|
|
|
|
|
|
|
|
2931
|
1008
|
|
|
|
|
|
XSRETURN_UNDEF; |
|
2932
|
|
|
|
|
|
|
} |
|
2933
|
|
|
|
|
|
|
|
|
2934
|
|
|
|
|
|
|
/* first_lt(\@array, $threshold) or first_lt(\@array, $key, $threshold) |
|
2935
|
|
|
|
|
|
|
first element < threshold, pure C */ |
|
2936
|
3012
|
|
|
|
|
|
XS_INTERNAL(xs_first_lt) { |
|
2937
|
3012
|
|
|
|
|
|
dXSARGS; |
|
2938
|
3012
|
50
|
|
|
|
|
if (items < 2 || items > 3) croak("Usage: Func::Util::first_lt(\\@array, $threshold) or first_lt(\\@array, $key, $threshold)"); |
|
|
|
50
|
|
|
|
|
|
|
2939
|
|
|
|
|
|
|
|
|
2940
|
3012
|
|
|
|
|
|
SV *aref = ST(0); |
|
2941
|
3012
|
50
|
|
|
|
|
if (!SvROK(aref) || SvTYPE(SvRV(aref)) != SVt_PVAV) { |
|
|
|
50
|
|
|
|
|
|
|
2942
|
0
|
|
|
|
|
|
croak("Func::Util::first_lt: first argument must be an arrayref"); |
|
2943
|
|
|
|
|
|
|
} |
|
2944
|
|
|
|
|
|
|
|
|
2945
|
3012
|
|
|
|
|
|
AV *av = (AV *)SvRV(aref); |
|
2946
|
3012
|
|
|
|
|
|
SSize_t len = av_len(av) + 1; |
|
2947
|
|
|
|
|
|
|
SSize_t i; |
|
2948
|
|
|
|
|
|
|
|
|
2949
|
3012
|
100
|
|
|
|
|
if (items == 2) { |
|
2950
|
2011
|
|
|
|
|
|
NV threshold = SvNV(ST(1)); |
|
2951
|
9038
|
100
|
|
|
|
|
for (i = 0; i < len; i++) { |
|
2952
|
8034
|
|
|
|
|
|
SV **elem = av_fetch(av, i, 0); |
|
2953
|
8034
|
50
|
|
|
|
|
if (elem && SvNV(*elem) < threshold) { |
|
|
|
100
|
|
|
|
|
|
|
2954
|
1007
|
|
|
|
|
|
ST(0) = *elem; |
|
2955
|
1007
|
|
|
|
|
|
XSRETURN(1); |
|
2956
|
|
|
|
|
|
|
} |
|
2957
|
|
|
|
|
|
|
} |
|
2958
|
|
|
|
|
|
|
} else { |
|
2959
|
1001
|
|
|
|
|
|
char *key = SvPV_nolen(ST(1)); |
|
2960
|
1001
|
|
|
|
|
|
NV threshold = SvNV(ST(2)); |
|
2961
|
1001
|
50
|
|
|
|
|
for (i = 0; i < len; i++) { |
|
2962
|
1001
|
|
|
|
|
|
SV **elem = av_fetch(av, i, 0); |
|
2963
|
1001
|
50
|
|
|
|
|
if (elem && SvROK(*elem) && SvTYPE(SvRV(*elem)) == SVt_PVHV) { |
|
|
|
50
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
2964
|
1001
|
|
|
|
|
|
HV *hv = (HV *)SvRV(*elem); |
|
2965
|
1001
|
|
|
|
|
|
SV **val = hv_fetch(hv, key, strlen(key), 0); |
|
2966
|
1001
|
50
|
|
|
|
|
if (val && SvNV(*val) < threshold) { |
|
|
|
50
|
|
|
|
|
|
|
2967
|
1001
|
|
|
|
|
|
ST(0) = *elem; |
|
2968
|
1001
|
|
|
|
|
|
XSRETURN(1); |
|
2969
|
|
|
|
|
|
|
} |
|
2970
|
|
|
|
|
|
|
} |
|
2971
|
|
|
|
|
|
|
} |
|
2972
|
|
|
|
|
|
|
} |
|
2973
|
|
|
|
|
|
|
|
|
2974
|
1004
|
|
|
|
|
|
XSRETURN_UNDEF; |
|
2975
|
|
|
|
|
|
|
} |
|
2976
|
|
|
|
|
|
|
|
|
2977
|
|
|
|
|
|
|
/* first_eq(\@array, $value) or first_eq(\@array, $key, $value) |
|
2978
|
|
|
|
|
|
|
first element == value (numeric), pure C */ |
|
2979
|
3012
|
|
|
|
|
|
XS_INTERNAL(xs_first_eq) { |
|
2980
|
3012
|
|
|
|
|
|
dXSARGS; |
|
2981
|
3012
|
50
|
|
|
|
|
if (items < 2 || items > 3) croak("Usage: Func::Util::first_eq(\\@array, $value) or first_eq(\\@array, $key, $value)"); |
|
|
|
50
|
|
|
|
|
|
|
2982
|
|
|
|
|
|
|
|
|
2983
|
3012
|
|
|
|
|
|
SV *aref = ST(0); |
|
2984
|
3012
|
50
|
|
|
|
|
if (!SvROK(aref) || SvTYPE(SvRV(aref)) != SVt_PVAV) { |
|
|
|
50
|
|
|
|
|
|
|
2985
|
0
|
|
|
|
|
|
croak("Func::Util::first_eq: first argument must be an arrayref"); |
|
2986
|
|
|
|
|
|
|
} |
|
2987
|
|
|
|
|
|
|
|
|
2988
|
3012
|
|
|
|
|
|
AV *av = (AV *)SvRV(aref); |
|
2989
|
3012
|
|
|
|
|
|
SSize_t len = av_len(av) + 1; |
|
2990
|
|
|
|
|
|
|
SSize_t i; |
|
2991
|
|
|
|
|
|
|
|
|
2992
|
3012
|
100
|
|
|
|
|
if (items == 2) { |
|
2993
|
2011
|
|
|
|
|
|
NV target = SvNV(ST(1)); |
|
2994
|
11047
|
100
|
|
|
|
|
for (i = 0; i < len; i++) { |
|
2995
|
10043
|
|
|
|
|
|
SV **elem = av_fetch(av, i, 0); |
|
2996
|
10043
|
50
|
|
|
|
|
if (elem && SvNV(*elem) == target) { |
|
|
|
100
|
|
|
|
|
|
|
2997
|
1007
|
|
|
|
|
|
ST(0) = *elem; |
|
2998
|
1007
|
|
|
|
|
|
XSRETURN(1); |
|
2999
|
|
|
|
|
|
|
} |
|
3000
|
|
|
|
|
|
|
} |
|
3001
|
|
|
|
|
|
|
} else { |
|
3002
|
1001
|
|
|
|
|
|
char *key = SvPV_nolen(ST(1)); |
|
3003
|
1001
|
|
|
|
|
|
NV target = SvNV(ST(2)); |
|
3004
|
2003
|
50
|
|
|
|
|
for (i = 0; i < len; i++) { |
|
3005
|
2003
|
|
|
|
|
|
SV **elem = av_fetch(av, i, 0); |
|
3006
|
2003
|
50
|
|
|
|
|
if (elem && SvROK(*elem) && SvTYPE(SvRV(*elem)) == SVt_PVHV) { |
|
|
|
50
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
3007
|
2003
|
|
|
|
|
|
HV *hv = (HV *)SvRV(*elem); |
|
3008
|
2003
|
|
|
|
|
|
SV **val = hv_fetch(hv, key, strlen(key), 0); |
|
3009
|
2003
|
50
|
|
|
|
|
if (val && SvNV(*val) == target) { |
|
|
|
100
|
|
|
|
|
|
|
3010
|
1001
|
|
|
|
|
|
ST(0) = *elem; |
|
3011
|
1001
|
|
|
|
|
|
XSRETURN(1); |
|
3012
|
|
|
|
|
|
|
} |
|
3013
|
|
|
|
|
|
|
} |
|
3014
|
|
|
|
|
|
|
} |
|
3015
|
|
|
|
|
|
|
} |
|
3016
|
|
|
|
|
|
|
|
|
3017
|
1004
|
|
|
|
|
|
XSRETURN_UNDEF; |
|
3018
|
|
|
|
|
|
|
} |
|
3019
|
|
|
|
|
|
|
|
|
3020
|
|
|
|
|
|
|
/* first_ge(\@array, $threshold) or first_ge(\@array, $key, $threshold) |
|
3021
|
|
|
|
|
|
|
first element >= threshold, pure C */ |
|
3022
|
3014
|
|
|
|
|
|
XS_INTERNAL(xs_first_ge) { |
|
3023
|
3014
|
|
|
|
|
|
dXSARGS; |
|
3024
|
3014
|
50
|
|
|
|
|
if (items < 2 || items > 3) croak("Usage: Func::Util::first_ge(\\@array, $threshold) or first_ge(\\@array, $key, $threshold)"); |
|
|
|
50
|
|
|
|
|
|
|
3025
|
|
|
|
|
|
|
|
|
3026
|
3014
|
|
|
|
|
|
SV *aref = ST(0); |
|
3027
|
3014
|
50
|
|
|
|
|
if (!SvROK(aref) || SvTYPE(SvRV(aref)) != SVt_PVAV) { |
|
|
|
50
|
|
|
|
|
|
|
3028
|
0
|
|
|
|
|
|
croak("Func::Util::first_ge: first argument must be an arrayref"); |
|
3029
|
|
|
|
|
|
|
} |
|
3030
|
|
|
|
|
|
|
|
|
3031
|
3014
|
|
|
|
|
|
AV *av = (AV *)SvRV(aref); |
|
3032
|
3014
|
|
|
|
|
|
SSize_t len = av_len(av) + 1; |
|
3033
|
|
|
|
|
|
|
SSize_t i; |
|
3034
|
|
|
|
|
|
|
|
|
3035
|
3014
|
100
|
|
|
|
|
if (items == 2) { |
|
3036
|
2013
|
|
|
|
|
|
NV threshold = SvNV(ST(1)); |
|
3037
|
11051
|
100
|
|
|
|
|
for (i = 0; i < len; i++) { |
|
3038
|
10048
|
|
|
|
|
|
SV **elem = av_fetch(av, i, 0); |
|
3039
|
10048
|
50
|
|
|
|
|
if (elem && SvNV(*elem) >= threshold) { |
|
|
|
100
|
|
|
|
|
|
|
3040
|
1010
|
|
|
|
|
|
ST(0) = *elem; |
|
3041
|
1010
|
|
|
|
|
|
XSRETURN(1); |
|
3042
|
|
|
|
|
|
|
} |
|
3043
|
|
|
|
|
|
|
} |
|
3044
|
|
|
|
|
|
|
} else { |
|
3045
|
1001
|
|
|
|
|
|
char *key = SvPV_nolen(ST(1)); |
|
3046
|
1001
|
|
|
|
|
|
NV threshold = SvNV(ST(2)); |
|
3047
|
1002
|
50
|
|
|
|
|
for (i = 0; i < len; i++) { |
|
3048
|
1002
|
|
|
|
|
|
SV **elem = av_fetch(av, i, 0); |
|
3049
|
1002
|
50
|
|
|
|
|
if (elem && SvROK(*elem) && SvTYPE(SvRV(*elem)) == SVt_PVHV) { |
|
|
|
50
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
3050
|
1002
|
|
|
|
|
|
HV *hv = (HV *)SvRV(*elem); |
|
3051
|
1002
|
|
|
|
|
|
SV **val = hv_fetch(hv, key, strlen(key), 0); |
|
3052
|
1002
|
50
|
|
|
|
|
if (val && SvNV(*val) >= threshold) { |
|
|
|
100
|
|
|
|
|
|
|
3053
|
1001
|
|
|
|
|
|
ST(0) = *elem; |
|
3054
|
1001
|
|
|
|
|
|
XSRETURN(1); |
|
3055
|
|
|
|
|
|
|
} |
|
3056
|
|
|
|
|
|
|
} |
|
3057
|
|
|
|
|
|
|
} |
|
3058
|
|
|
|
|
|
|
} |
|
3059
|
|
|
|
|
|
|
|
|
3060
|
1003
|
|
|
|
|
|
XSRETURN_UNDEF; |
|
3061
|
|
|
|
|
|
|
} |
|
3062
|
|
|
|
|
|
|
|
|
3063
|
|
|
|
|
|
|
/* first_le(\@array, $threshold) or first_le(\@array, $key, $threshold) |
|
3064
|
|
|
|
|
|
|
first element <= threshold, pure C */ |
|
3065
|
3009
|
|
|
|
|
|
XS_INTERNAL(xs_first_le) { |
|
3066
|
3009
|
|
|
|
|
|
dXSARGS; |
|
3067
|
3009
|
50
|
|
|
|
|
if (items < 2 || items > 3) croak("Usage: Func::Util::first_le(\\@array, $threshold) or first_le(\\@array, $key, $threshold)"); |
|
|
|
50
|
|
|
|
|
|
|
3068
|
|
|
|
|
|
|
|
|
3069
|
3009
|
|
|
|
|
|
SV *aref = ST(0); |
|
3070
|
3009
|
50
|
|
|
|
|
if (!SvROK(aref) || SvTYPE(SvRV(aref)) != SVt_PVAV) { |
|
|
|
50
|
|
|
|
|
|
|
3071
|
0
|
|
|
|
|
|
croak("Func::Util::first_le: first argument must be an arrayref"); |
|
3072
|
|
|
|
|
|
|
} |
|
3073
|
|
|
|
|
|
|
|
|
3074
|
3009
|
|
|
|
|
|
AV *av = (AV *)SvRV(aref); |
|
3075
|
3009
|
|
|
|
|
|
SSize_t len = av_len(av) + 1; |
|
3076
|
|
|
|
|
|
|
SSize_t i; |
|
3077
|
|
|
|
|
|
|
|
|
3078
|
3009
|
100
|
|
|
|
|
if (items == 2) { |
|
3079
|
2009
|
|
|
|
|
|
NV threshold = SvNV(ST(1)); |
|
3080
|
9027
|
100
|
|
|
|
|
for (i = 0; i < len; i++) { |
|
3081
|
8024
|
|
|
|
|
|
SV **elem = av_fetch(av, i, 0); |
|
3082
|
8024
|
50
|
|
|
|
|
if (elem && SvNV(*elem) <= threshold) { |
|
|
|
100
|
|
|
|
|
|
|
3083
|
1006
|
|
|
|
|
|
ST(0) = *elem; |
|
3084
|
1006
|
|
|
|
|
|
XSRETURN(1); |
|
3085
|
|
|
|
|
|
|
} |
|
3086
|
|
|
|
|
|
|
} |
|
3087
|
|
|
|
|
|
|
} else { |
|
3088
|
1000
|
|
|
|
|
|
char *key = SvPV_nolen(ST(1)); |
|
3089
|
1000
|
|
|
|
|
|
NV threshold = SvNV(ST(2)); |
|
3090
|
1000
|
50
|
|
|
|
|
for (i = 0; i < len; i++) { |
|
3091
|
1000
|
|
|
|
|
|
SV **elem = av_fetch(av, i, 0); |
|
3092
|
1000
|
50
|
|
|
|
|
if (elem && SvROK(*elem) && SvTYPE(SvRV(*elem)) == SVt_PVHV) { |
|
|
|
50
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
3093
|
1000
|
|
|
|
|
|
HV *hv = (HV *)SvRV(*elem); |
|
3094
|
1000
|
|
|
|
|
|
SV **val = hv_fetch(hv, key, strlen(key), 0); |
|
3095
|
1000
|
50
|
|
|
|
|
if (val && SvNV(*val) <= threshold) { |
|
|
|
50
|
|
|
|
|
|
|
3096
|
1000
|
|
|
|
|
|
ST(0) = *elem; |
|
3097
|
1000
|
|
|
|
|
|
XSRETURN(1); |
|
3098
|
|
|
|
|
|
|
} |
|
3099
|
|
|
|
|
|
|
} |
|
3100
|
|
|
|
|
|
|
} |
|
3101
|
|
|
|
|
|
|
} |
|
3102
|
|
|
|
|
|
|
|
|
3103
|
1003
|
|
|
|
|
|
XSRETURN_UNDEF; |
|
3104
|
|
|
|
|
|
|
} |
|
3105
|
|
|
|
|
|
|
|
|
3106
|
|
|
|
|
|
|
/* first_ne(\@array, $value) or first_ne(\@array, $key, $value) |
|
3107
|
|
|
|
|
|
|
first element != value (numeric), pure C */ |
|
3108
|
2007
|
|
|
|
|
|
XS_INTERNAL(xs_first_ne) { |
|
3109
|
2007
|
|
|
|
|
|
dXSARGS; |
|
3110
|
2007
|
50
|
|
|
|
|
if (items < 2 || items > 3) croak("Usage: Func::Util::first_ne(\\@array, $value) or first_ne(\\@array, $key, $value)"); |
|
|
|
50
|
|
|
|
|
|
|
3111
|
|
|
|
|
|
|
|
|
3112
|
2007
|
|
|
|
|
|
SV *aref = ST(0); |
|
3113
|
2007
|
50
|
|
|
|
|
if (!SvROK(aref) || SvTYPE(SvRV(aref)) != SVt_PVAV) { |
|
|
|
50
|
|
|
|
|
|
|
3114
|
0
|
|
|
|
|
|
croak("Func::Util::first_ne: first argument must be an arrayref"); |
|
3115
|
|
|
|
|
|
|
} |
|
3116
|
|
|
|
|
|
|
|
|
3117
|
2007
|
|
|
|
|
|
AV *av = (AV *)SvRV(aref); |
|
3118
|
2007
|
|
|
|
|
|
SSize_t len = av_len(av) + 1; |
|
3119
|
|
|
|
|
|
|
SSize_t i; |
|
3120
|
|
|
|
|
|
|
|
|
3121
|
2007
|
100
|
|
|
|
|
if (items == 2) { |
|
3122
|
1007
|
|
|
|
|
|
NV target = SvNV(ST(1)); |
|
3123
|
2020
|
100
|
|
|
|
|
for (i = 0; i < len; i++) { |
|
3124
|
2018
|
|
|
|
|
|
SV **elem = av_fetch(av, i, 0); |
|
3125
|
2018
|
50
|
|
|
|
|
if (elem && SvNV(*elem) != target) { |
|
|
|
100
|
|
|
|
|
|
|
3126
|
1005
|
|
|
|
|
|
ST(0) = *elem; |
|
3127
|
1005
|
|
|
|
|
|
XSRETURN(1); |
|
3128
|
|
|
|
|
|
|
} |
|
3129
|
|
|
|
|
|
|
} |
|
3130
|
|
|
|
|
|
|
} else { |
|
3131
|
1000
|
|
|
|
|
|
char *key = SvPV_nolen(ST(1)); |
|
3132
|
1000
|
|
|
|
|
|
NV target = SvNV(ST(2)); |
|
3133
|
2000
|
50
|
|
|
|
|
for (i = 0; i < len; i++) { |
|
3134
|
2000
|
|
|
|
|
|
SV **elem = av_fetch(av, i, 0); |
|
3135
|
2000
|
50
|
|
|
|
|
if (elem && SvROK(*elem) && SvTYPE(SvRV(*elem)) == SVt_PVHV) { |
|
|
|
50
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
3136
|
2000
|
|
|
|
|
|
HV *hv = (HV *)SvRV(*elem); |
|
3137
|
2000
|
|
|
|
|
|
SV **val = hv_fetch(hv, key, strlen(key), 0); |
|
3138
|
2000
|
50
|
|
|
|
|
if (val && SvNV(*val) != target) { |
|
|
|
100
|
|
|
|
|
|
|
3139
|
1000
|
|
|
|
|
|
ST(0) = *elem; |
|
3140
|
1000
|
|
|
|
|
|
XSRETURN(1); |
|
3141
|
|
|
|
|
|
|
} |
|
3142
|
|
|
|
|
|
|
} |
|
3143
|
|
|
|
|
|
|
} |
|
3144
|
|
|
|
|
|
|
} |
|
3145
|
|
|
|
|
|
|
|
|
3146
|
2
|
|
|
|
|
|
XSRETURN_UNDEF; |
|
3147
|
|
|
|
|
|
|
} |
|
3148
|
|
|
|
|
|
|
|
|
3149
|
|
|
|
|
|
|
/* ============================================ |
|
3150
|
|
|
|
|
|
|
final_* - like first_* but iterates backwards |
|
3151
|
|
|
|
|
|
|
============================================ */ |
|
3152
|
|
|
|
|
|
|
|
|
3153
|
|
|
|
|
|
|
/* final_gt(\@array, $threshold) or final_gt(\@array, $key, $threshold) |
|
3154
|
|
|
|
|
|
|
last element > threshold, pure C, backwards iteration */ |
|
3155
|
3012
|
|
|
|
|
|
XS_INTERNAL(xs_final_gt) { |
|
3156
|
3012
|
|
|
|
|
|
dXSARGS; |
|
3157
|
3012
|
50
|
|
|
|
|
if (items < 2 || items > 3) croak("Usage: Func::Util::final_gt(\\@array, $threshold) or final_gt(\\@array, $key, $threshold)"); |
|
|
|
50
|
|
|
|
|
|
|
3158
|
|
|
|
|
|
|
|
|
3159
|
3012
|
|
|
|
|
|
SV *aref = ST(0); |
|
3160
|
3012
|
50
|
|
|
|
|
if (!SvROK(aref) || SvTYPE(SvRV(aref)) != SVt_PVAV) { |
|
|
|
50
|
|
|
|
|
|
|
3161
|
0
|
|
|
|
|
|
croak("Func::Util::final_gt: first argument must be an arrayref"); |
|
3162
|
|
|
|
|
|
|
} |
|
3163
|
|
|
|
|
|
|
|
|
3164
|
3012
|
|
|
|
|
|
AV *av = (AV *)SvRV(aref); |
|
3165
|
3012
|
|
|
|
|
|
SSize_t len = av_len(av) + 1; |
|
3166
|
|
|
|
|
|
|
SSize_t i; |
|
3167
|
|
|
|
|
|
|
|
|
3168
|
3012
|
100
|
|
|
|
|
if (items == 2) { |
|
3169
|
2011
|
|
|
|
|
|
NV threshold = SvNV(ST(1)); |
|
3170
|
9028
|
100
|
|
|
|
|
for (i = len - 1; i >= 0; i--) { |
|
3171
|
8023
|
|
|
|
|
|
SV **elem = av_fetch(av, i, 0); |
|
3172
|
8023
|
50
|
|
|
|
|
if (elem && SvNV(*elem) > threshold) { |
|
|
|
100
|
|
|
|
|
|
|
3173
|
1006
|
|
|
|
|
|
ST(0) = *elem; |
|
3174
|
1006
|
|
|
|
|
|
XSRETURN(1); |
|
3175
|
|
|
|
|
|
|
} |
|
3176
|
|
|
|
|
|
|
} |
|
3177
|
|
|
|
|
|
|
} else { |
|
3178
|
1001
|
|
|
|
|
|
char *key = SvPV_nolen(ST(1)); |
|
3179
|
1001
|
|
|
|
|
|
NV threshold = SvNV(ST(2)); |
|
3180
|
1001
|
50
|
|
|
|
|
for (i = len - 1; i >= 0; i--) { |
|
3181
|
1001
|
|
|
|
|
|
SV **elem = av_fetch(av, i, 0); |
|
3182
|
1001
|
50
|
|
|
|
|
if (elem && SvROK(*elem) && SvTYPE(SvRV(*elem)) == SVt_PVHV) { |
|
|
|
50
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
3183
|
1001
|
|
|
|
|
|
HV *hv = (HV *)SvRV(*elem); |
|
3184
|
1001
|
|
|
|
|
|
SV **val = hv_fetch(hv, key, strlen(key), 0); |
|
3185
|
1001
|
50
|
|
|
|
|
if (val && SvNV(*val) > threshold) { |
|
|
|
50
|
|
|
|
|
|
|
3186
|
1001
|
|
|
|
|
|
ST(0) = *elem; |
|
3187
|
1001
|
|
|
|
|
|
XSRETURN(1); |
|
3188
|
|
|
|
|
|
|
} |
|
3189
|
|
|
|
|
|
|
} |
|
3190
|
|
|
|
|
|
|
} |
|
3191
|
|
|
|
|
|
|
} |
|
3192
|
|
|
|
|
|
|
|
|
3193
|
1005
|
|
|
|
|
|
XSRETURN_UNDEF; |
|
3194
|
|
|
|
|
|
|
} |
|
3195
|
|
|
|
|
|
|
|
|
3196
|
|
|
|
|
|
|
/* final_lt(\@array, $threshold) or final_lt(\@array, $key, $threshold) */ |
|
3197
|
3011
|
|
|
|
|
|
XS_INTERNAL(xs_final_lt) { |
|
3198
|
3011
|
|
|
|
|
|
dXSARGS; |
|
3199
|
3011
|
50
|
|
|
|
|
if (items < 2 || items > 3) croak("Usage: Func::Util::final_lt(\\@array, $threshold) or final_lt(\\@array, $key, $threshold)"); |
|
|
|
50
|
|
|
|
|
|
|
3200
|
|
|
|
|
|
|
|
|
3201
|
3011
|
|
|
|
|
|
SV *aref = ST(0); |
|
3202
|
3011
|
50
|
|
|
|
|
if (!SvROK(aref) || SvTYPE(SvRV(aref)) != SVt_PVAV) { |
|
|
|
50
|
|
|
|
|
|
|
3203
|
0
|
|
|
|
|
|
croak("Func::Util::final_lt: first argument must be an arrayref"); |
|
3204
|
|
|
|
|
|
|
} |
|
3205
|
|
|
|
|
|
|
|
|
3206
|
3011
|
|
|
|
|
|
AV *av = (AV *)SvRV(aref); |
|
3207
|
3011
|
|
|
|
|
|
SSize_t len = av_len(av) + 1; |
|
3208
|
|
|
|
|
|
|
SSize_t i; |
|
3209
|
|
|
|
|
|
|
|
|
3210
|
3011
|
100
|
|
|
|
|
if (items == 2) { |
|
3211
|
2010
|
|
|
|
|
|
NV threshold = SvNV(ST(1)); |
|
3212
|
12036
|
100
|
|
|
|
|
for (i = len - 1; i >= 0; i--) { |
|
3213
|
11033
|
|
|
|
|
|
SV **elem = av_fetch(av, i, 0); |
|
3214
|
11033
|
50
|
|
|
|
|
if (elem && SvNV(*elem) < threshold) { |
|
|
|
100
|
|
|
|
|
|
|
3215
|
1007
|
|
|
|
|
|
ST(0) = *elem; |
|
3216
|
1007
|
|
|
|
|
|
XSRETURN(1); |
|
3217
|
|
|
|
|
|
|
} |
|
3218
|
|
|
|
|
|
|
} |
|
3219
|
|
|
|
|
|
|
} else { |
|
3220
|
1001
|
|
|
|
|
|
char *key = SvPV_nolen(ST(1)); |
|
3221
|
1001
|
|
|
|
|
|
NV threshold = SvNV(ST(2)); |
|
3222
|
2001
|
50
|
|
|
|
|
for (i = len - 1; i >= 0; i--) { |
|
3223
|
2001
|
|
|
|
|
|
SV **elem = av_fetch(av, i, 0); |
|
3224
|
2001
|
50
|
|
|
|
|
if (elem && SvROK(*elem) && SvTYPE(SvRV(*elem)) == SVt_PVHV) { |
|
|
|
50
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
3225
|
2001
|
|
|
|
|
|
HV *hv = (HV *)SvRV(*elem); |
|
3226
|
2001
|
|
|
|
|
|
SV **val = hv_fetch(hv, key, strlen(key), 0); |
|
3227
|
2001
|
50
|
|
|
|
|
if (val && SvNV(*val) < threshold) { |
|
|
|
100
|
|
|
|
|
|
|
3228
|
1001
|
|
|
|
|
|
ST(0) = *elem; |
|
3229
|
1001
|
|
|
|
|
|
XSRETURN(1); |
|
3230
|
|
|
|
|
|
|
} |
|
3231
|
|
|
|
|
|
|
} |
|
3232
|
|
|
|
|
|
|
} |
|
3233
|
|
|
|
|
|
|
} |
|
3234
|
|
|
|
|
|
|
|
|
3235
|
1003
|
|
|
|
|
|
XSRETURN_UNDEF; |
|
3236
|
|
|
|
|
|
|
} |
|
3237
|
|
|
|
|
|
|
|
|
3238
|
|
|
|
|
|
|
/* final_ge(\@array, $threshold) or final_ge(\@array, $key, $threshold) */ |
|
3239
|
2005
|
|
|
|
|
|
XS_INTERNAL(xs_final_ge) { |
|
3240
|
2005
|
|
|
|
|
|
dXSARGS; |
|
3241
|
2005
|
50
|
|
|
|
|
if (items < 2 || items > 3) croak("Usage: Func::Util::final_ge(\\@array, $threshold) or final_ge(\\@array, $key, $threshold)"); |
|
|
|
50
|
|
|
|
|
|
|
3242
|
|
|
|
|
|
|
|
|
3243
|
2005
|
|
|
|
|
|
SV *aref = ST(0); |
|
3244
|
2005
|
50
|
|
|
|
|
if (!SvROK(aref) || SvTYPE(SvRV(aref)) != SVt_PVAV) { |
|
|
|
50
|
|
|
|
|
|
|
3245
|
0
|
|
|
|
|
|
croak("Func::Util::final_ge: first argument must be an arrayref"); |
|
3246
|
|
|
|
|
|
|
} |
|
3247
|
|
|
|
|
|
|
|
|
3248
|
2005
|
|
|
|
|
|
AV *av = (AV *)SvRV(aref); |
|
3249
|
2005
|
|
|
|
|
|
SSize_t len = av_len(av) + 1; |
|
3250
|
|
|
|
|
|
|
SSize_t i; |
|
3251
|
|
|
|
|
|
|
|
|
3252
|
2005
|
100
|
|
|
|
|
if (items == 2) { |
|
3253
|
1005
|
|
|
|
|
|
NV threshold = SvNV(ST(1)); |
|
3254
|
1017
|
100
|
|
|
|
|
for (i = len - 1; i >= 0; i--) { |
|
3255
|
1015
|
|
|
|
|
|
SV **elem = av_fetch(av, i, 0); |
|
3256
|
1015
|
50
|
|
|
|
|
if (elem && SvNV(*elem) >= threshold) { |
|
|
|
100
|
|
|
|
|
|
|
3257
|
1003
|
|
|
|
|
|
ST(0) = *elem; |
|
3258
|
1003
|
|
|
|
|
|
XSRETURN(1); |
|
3259
|
|
|
|
|
|
|
} |
|
3260
|
|
|
|
|
|
|
} |
|
3261
|
|
|
|
|
|
|
} else { |
|
3262
|
1000
|
|
|
|
|
|
char *key = SvPV_nolen(ST(1)); |
|
3263
|
1000
|
|
|
|
|
|
NV threshold = SvNV(ST(2)); |
|
3264
|
1000
|
50
|
|
|
|
|
for (i = len - 1; i >= 0; i--) { |
|
3265
|
1000
|
|
|
|
|
|
SV **elem = av_fetch(av, i, 0); |
|
3266
|
1000
|
50
|
|
|
|
|
if (elem && SvROK(*elem) && SvTYPE(SvRV(*elem)) == SVt_PVHV) { |
|
|
|
50
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
3267
|
1000
|
|
|
|
|
|
HV *hv = (HV *)SvRV(*elem); |
|
3268
|
1000
|
|
|
|
|
|
SV **val = hv_fetch(hv, key, strlen(key), 0); |
|
3269
|
1000
|
50
|
|
|
|
|
if (val && SvNV(*val) >= threshold) { |
|
|
|
50
|
|
|
|
|
|
|
3270
|
1000
|
|
|
|
|
|
ST(0) = *elem; |
|
3271
|
1000
|
|
|
|
|
|
XSRETURN(1); |
|
3272
|
|
|
|
|
|
|
} |
|
3273
|
|
|
|
|
|
|
} |
|
3274
|
|
|
|
|
|
|
} |
|
3275
|
|
|
|
|
|
|
} |
|
3276
|
|
|
|
|
|
|
|
|
3277
|
2
|
|
|
|
|
|
XSRETURN_UNDEF; |
|
3278
|
|
|
|
|
|
|
} |
|
3279
|
|
|
|
|
|
|
|
|
3280
|
|
|
|
|
|
|
/* final_le(\@array, $threshold) or final_le(\@array, $key, $threshold) */ |
|
3281
|
2006
|
|
|
|
|
|
XS_INTERNAL(xs_final_le) { |
|
3282
|
2006
|
|
|
|
|
|
dXSARGS; |
|
3283
|
2006
|
50
|
|
|
|
|
if (items < 2 || items > 3) croak("Usage: Func::Util::final_le(\\@array, $threshold) or final_le(\\@array, $key, $threshold)"); |
|
|
|
50
|
|
|
|
|
|
|
3284
|
|
|
|
|
|
|
|
|
3285
|
2006
|
|
|
|
|
|
SV *aref = ST(0); |
|
3286
|
2006
|
50
|
|
|
|
|
if (!SvROK(aref) || SvTYPE(SvRV(aref)) != SVt_PVAV) { |
|
|
|
50
|
|
|
|
|
|
|
3287
|
0
|
|
|
|
|
|
croak("Func::Util::final_le: first argument must be an arrayref"); |
|
3288
|
|
|
|
|
|
|
} |
|
3289
|
|
|
|
|
|
|
|
|
3290
|
2006
|
|
|
|
|
|
AV *av = (AV *)SvRV(aref); |
|
3291
|
2006
|
|
|
|
|
|
SSize_t len = av_len(av) + 1; |
|
3292
|
|
|
|
|
|
|
SSize_t i; |
|
3293
|
|
|
|
|
|
|
|
|
3294
|
2006
|
100
|
|
|
|
|
if (items == 2) { |
|
3295
|
1006
|
|
|
|
|
|
NV threshold = SvNV(ST(1)); |
|
3296
|
3016
|
100
|
|
|
|
|
for (i = len - 1; i >= 0; i--) { |
|
3297
|
3015
|
|
|
|
|
|
SV **elem = av_fetch(av, i, 0); |
|
3298
|
3015
|
50
|
|
|
|
|
if (elem && SvNV(*elem) <= threshold) { |
|
|
|
100
|
|
|
|
|
|
|
3299
|
1005
|
|
|
|
|
|
ST(0) = *elem; |
|
3300
|
1005
|
|
|
|
|
|
XSRETURN(1); |
|
3301
|
|
|
|
|
|
|
} |
|
3302
|
|
|
|
|
|
|
} |
|
3303
|
|
|
|
|
|
|
} else { |
|
3304
|
1000
|
|
|
|
|
|
char *key = SvPV_nolen(ST(1)); |
|
3305
|
1000
|
|
|
|
|
|
NV threshold = SvNV(ST(2)); |
|
3306
|
2000
|
50
|
|
|
|
|
for (i = len - 1; i >= 0; i--) { |
|
3307
|
2000
|
|
|
|
|
|
SV **elem = av_fetch(av, i, 0); |
|
3308
|
2000
|
50
|
|
|
|
|
if (elem && SvROK(*elem) && SvTYPE(SvRV(*elem)) == SVt_PVHV) { |
|
|
|
50
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
3309
|
2000
|
|
|
|
|
|
HV *hv = (HV *)SvRV(*elem); |
|
3310
|
2000
|
|
|
|
|
|
SV **val = hv_fetch(hv, key, strlen(key), 0); |
|
3311
|
2000
|
50
|
|
|
|
|
if (val && SvNV(*val) <= threshold) { |
|
|
|
100
|
|
|
|
|
|
|
3312
|
1000
|
|
|
|
|
|
ST(0) = *elem; |
|
3313
|
1000
|
|
|
|
|
|
XSRETURN(1); |
|
3314
|
|
|
|
|
|
|
} |
|
3315
|
|
|
|
|
|
|
} |
|
3316
|
|
|
|
|
|
|
} |
|
3317
|
|
|
|
|
|
|
} |
|
3318
|
|
|
|
|
|
|
|
|
3319
|
1
|
|
|
|
|
|
XSRETURN_UNDEF; |
|
3320
|
|
|
|
|
|
|
} |
|
3321
|
|
|
|
|
|
|
|
|
3322
|
|
|
|
|
|
|
/* final_eq(\@array, $value) or final_eq(\@array, $key, $value) */ |
|
3323
|
2006
|
|
|
|
|
|
XS_INTERNAL(xs_final_eq) { |
|
3324
|
2006
|
|
|
|
|
|
dXSARGS; |
|
3325
|
2006
|
50
|
|
|
|
|
if (items < 2 || items > 3) croak("Usage: Func::Util::final_eq(\\@array, $value) or final_eq(\\@array, $key, $value)"); |
|
|
|
50
|
|
|
|
|
|
|
3326
|
|
|
|
|
|
|
|
|
3327
|
2006
|
|
|
|
|
|
SV *aref = ST(0); |
|
3328
|
2006
|
50
|
|
|
|
|
if (!SvROK(aref) || SvTYPE(SvRV(aref)) != SVt_PVAV) { |
|
|
|
50
|
|
|
|
|
|
|
3329
|
0
|
|
|
|
|
|
croak("Func::Util::final_eq: first argument must be an arrayref"); |
|
3330
|
|
|
|
|
|
|
} |
|
3331
|
|
|
|
|
|
|
|
|
3332
|
2006
|
|
|
|
|
|
AV *av = (AV *)SvRV(aref); |
|
3333
|
2006
|
|
|
|
|
|
SSize_t len = av_len(av) + 1; |
|
3334
|
|
|
|
|
|
|
SSize_t i; |
|
3335
|
|
|
|
|
|
|
|
|
3336
|
2006
|
100
|
|
|
|
|
if (items == 2) { |
|
3337
|
1006
|
|
|
|
|
|
NV target = SvNV(ST(1)); |
|
3338
|
3025
|
100
|
|
|
|
|
for (i = len - 1; i >= 0; i--) { |
|
3339
|
3023
|
|
|
|
|
|
SV **elem = av_fetch(av, i, 0); |
|
3340
|
3023
|
50
|
|
|
|
|
if (elem && SvNV(*elem) == target) { |
|
|
|
100
|
|
|
|
|
|
|
3341
|
1004
|
|
|
|
|
|
ST(0) = *elem; |
|
3342
|
1004
|
|
|
|
|
|
XSRETURN(1); |
|
3343
|
|
|
|
|
|
|
} |
|
3344
|
|
|
|
|
|
|
} |
|
3345
|
|
|
|
|
|
|
} else { |
|
3346
|
1000
|
|
|
|
|
|
char *key = SvPV_nolen(ST(1)); |
|
3347
|
1000
|
|
|
|
|
|
NV target = SvNV(ST(2)); |
|
3348
|
3000
|
50
|
|
|
|
|
for (i = len - 1; i >= 0; i--) { |
|
3349
|
3000
|
|
|
|
|
|
SV **elem = av_fetch(av, i, 0); |
|
3350
|
3000
|
50
|
|
|
|
|
if (elem && SvROK(*elem) && SvTYPE(SvRV(*elem)) == SVt_PVHV) { |
|
|
|
50
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
3351
|
3000
|
|
|
|
|
|
HV *hv = (HV *)SvRV(*elem); |
|
3352
|
3000
|
|
|
|
|
|
SV **val = hv_fetch(hv, key, strlen(key), 0); |
|
3353
|
3000
|
50
|
|
|
|
|
if (val && SvNV(*val) == target) { |
|
|
|
100
|
|
|
|
|
|
|
3354
|
1000
|
|
|
|
|
|
ST(0) = *elem; |
|
3355
|
1000
|
|
|
|
|
|
XSRETURN(1); |
|
3356
|
|
|
|
|
|
|
} |
|
3357
|
|
|
|
|
|
|
} |
|
3358
|
|
|
|
|
|
|
} |
|
3359
|
|
|
|
|
|
|
} |
|
3360
|
|
|
|
|
|
|
|
|
3361
|
2
|
|
|
|
|
|
XSRETURN_UNDEF; |
|
3362
|
|
|
|
|
|
|
} |
|
3363
|
|
|
|
|
|
|
|
|
3364
|
|
|
|
|
|
|
/* final_ne(\@array, $value) or final_ne(\@array, $key, $value) */ |
|
3365
|
2004
|
|
|
|
|
|
XS_INTERNAL(xs_final_ne) { |
|
3366
|
2004
|
|
|
|
|
|
dXSARGS; |
|
3367
|
2004
|
50
|
|
|
|
|
if (items < 2 || items > 3) croak("Usage: Func::Util::final_ne(\\@array, $value) or final_ne(\\@array, $key, $value)"); |
|
|
|
50
|
|
|
|
|
|
|
3368
|
|
|
|
|
|
|
|
|
3369
|
2004
|
|
|
|
|
|
SV *aref = ST(0); |
|
3370
|
2004
|
50
|
|
|
|
|
if (!SvROK(aref) || SvTYPE(SvRV(aref)) != SVt_PVAV) { |
|
|
|
50
|
|
|
|
|
|
|
3371
|
0
|
|
|
|
|
|
croak("Func::Util::final_ne: first argument must be an arrayref"); |
|
3372
|
|
|
|
|
|
|
} |
|
3373
|
|
|
|
|
|
|
|
|
3374
|
2004
|
|
|
|
|
|
AV *av = (AV *)SvRV(aref); |
|
3375
|
2004
|
|
|
|
|
|
SSize_t len = av_len(av) + 1; |
|
3376
|
|
|
|
|
|
|
SSize_t i; |
|
3377
|
|
|
|
|
|
|
|
|
3378
|
2004
|
100
|
|
|
|
|
if (items == 2) { |
|
3379
|
1004
|
|
|
|
|
|
NV target = SvNV(ST(1)); |
|
3380
|
2009
|
100
|
|
|
|
|
for (i = len - 1; i >= 0; i--) { |
|
3381
|
2008
|
|
|
|
|
|
SV **elem = av_fetch(av, i, 0); |
|
3382
|
2008
|
50
|
|
|
|
|
if (elem && SvNV(*elem) != target) { |
|
|
|
100
|
|
|
|
|
|
|
3383
|
1003
|
|
|
|
|
|
ST(0) = *elem; |
|
3384
|
1003
|
|
|
|
|
|
XSRETURN(1); |
|
3385
|
|
|
|
|
|
|
} |
|
3386
|
|
|
|
|
|
|
} |
|
3387
|
|
|
|
|
|
|
} else { |
|
3388
|
1000
|
|
|
|
|
|
char *key = SvPV_nolen(ST(1)); |
|
3389
|
1000
|
|
|
|
|
|
NV target = SvNV(ST(2)); |
|
3390
|
2000
|
50
|
|
|
|
|
for (i = len - 1; i >= 0; i--) { |
|
3391
|
2000
|
|
|
|
|
|
SV **elem = av_fetch(av, i, 0); |
|
3392
|
2000
|
50
|
|
|
|
|
if (elem && SvROK(*elem) && SvTYPE(SvRV(*elem)) == SVt_PVHV) { |
|
|
|
50
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
3393
|
2000
|
|
|
|
|
|
HV *hv = (HV *)SvRV(*elem); |
|
3394
|
2000
|
|
|
|
|
|
SV **val = hv_fetch(hv, key, strlen(key), 0); |
|
3395
|
2000
|
50
|
|
|
|
|
if (val && SvNV(*val) != target) { |
|
|
|
100
|
|
|
|
|
|
|
3396
|
1000
|
|
|
|
|
|
ST(0) = *elem; |
|
3397
|
1000
|
|
|
|
|
|
XSRETURN(1); |
|
3398
|
|
|
|
|
|
|
} |
|
3399
|
|
|
|
|
|
|
} |
|
3400
|
|
|
|
|
|
|
} |
|
3401
|
|
|
|
|
|
|
} |
|
3402
|
|
|
|
|
|
|
|
|
3403
|
1
|
|
|
|
|
|
XSRETURN_UNDEF; |
|
3404
|
|
|
|
|
|
|
} |
|
3405
|
|
|
|
|
|
|
|
|
3406
|
|
|
|
|
|
|
/* any_gt(\@array, $threshold) or any_gt(\@array, $key, $threshold) |
|
3407
|
|
|
|
|
|
|
true if any element > threshold, pure C */ |
|
3408
|
3015
|
|
|
|
|
|
XS_INTERNAL(xs_any_gt) { |
|
3409
|
3015
|
|
|
|
|
|
dXSARGS; |
|
3410
|
3015
|
50
|
|
|
|
|
if (items < 2 || items > 3) croak("Usage: Func::Util::any_gt(\\@array, $threshold) or any_gt(\\@array, $key, $threshold)"); |
|
|
|
50
|
|
|
|
|
|
|
3411
|
|
|
|
|
|
|
|
|
3412
|
3015
|
|
|
|
|
|
SV *aref = ST(0); |
|
3413
|
3015
|
50
|
|
|
|
|
if (!SvROK(aref) || SvTYPE(SvRV(aref)) != SVt_PVAV) { |
|
|
|
50
|
|
|
|
|
|
|
3414
|
0
|
|
|
|
|
|
croak("Func::Util::any_gt: first argument must be an arrayref"); |
|
3415
|
|
|
|
|
|
|
} |
|
3416
|
|
|
|
|
|
|
|
|
3417
|
3015
|
|
|
|
|
|
AV *av = (AV *)SvRV(aref); |
|
3418
|
3015
|
|
|
|
|
|
SSize_t len = av_len(av) + 1; |
|
3419
|
|
|
|
|
|
|
SSize_t i; |
|
3420
|
|
|
|
|
|
|
|
|
3421
|
3015
|
100
|
|
|
|
|
if (items == 2) { |
|
3422
|
2013
|
|
|
|
|
|
NV threshold = SvNV(ST(1)); |
|
3423
|
15056
|
100
|
|
|
|
|
for (i = 0; i < len; i++) { |
|
3424
|
14048
|
|
|
|
|
|
SV **elem = av_fetch(av, i, 0); |
|
3425
|
14048
|
50
|
|
|
|
|
if (elem && SvNV(*elem) > threshold) { |
|
|
|
100
|
|
|
|
|
|
|
3426
|
1005
|
|
|
|
|
|
XSRETURN_YES; |
|
3427
|
|
|
|
|
|
|
} |
|
3428
|
|
|
|
|
|
|
} |
|
3429
|
|
|
|
|
|
|
} else { |
|
3430
|
1002
|
|
|
|
|
|
char *key = SvPV_nolen(ST(1)); |
|
3431
|
1002
|
|
|
|
|
|
NV threshold = SvNV(ST(2)); |
|
3432
|
4005
|
100
|
|
|
|
|
for (i = 0; i < len; i++) { |
|
3433
|
4004
|
|
|
|
|
|
SV **elem = av_fetch(av, i, 0); |
|
3434
|
4004
|
50
|
|
|
|
|
if (elem && SvROK(*elem) && SvTYPE(SvRV(*elem)) == SVt_PVHV) { |
|
|
|
50
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
3435
|
4004
|
|
|
|
|
|
HV *hv = (HV *)SvRV(*elem); |
|
3436
|
4004
|
|
|
|
|
|
SV **val = hv_fetch(hv, key, strlen(key), 0); |
|
3437
|
4004
|
50
|
|
|
|
|
if (val && SvNV(*val) > threshold) { |
|
|
|
100
|
|
|
|
|
|
|
3438
|
1001
|
|
|
|
|
|
XSRETURN_YES; |
|
3439
|
|
|
|
|
|
|
} |
|
3440
|
|
|
|
|
|
|
} |
|
3441
|
|
|
|
|
|
|
} |
|
3442
|
|
|
|
|
|
|
} |
|
3443
|
|
|
|
|
|
|
|
|
3444
|
1009
|
|
|
|
|
|
XSRETURN_NO; |
|
3445
|
|
|
|
|
|
|
} |
|
3446
|
|
|
|
|
|
|
|
|
3447
|
|
|
|
|
|
|
/* any_lt(\@array, $threshold) or any_lt(\@array, $key, $threshold) */ |
|
3448
|
3021
|
|
|
|
|
|
XS_INTERNAL(xs_any_lt) { |
|
3449
|
3021
|
|
|
|
|
|
dXSARGS; |
|
3450
|
3021
|
50
|
|
|
|
|
if (items < 2 || items > 3) croak("Usage: Func::Util::any_lt(\\@array, $threshold) or any_lt(\\@array, $key, $threshold)"); |
|
|
|
50
|
|
|
|
|
|
|
3451
|
|
|
|
|
|
|
|
|
3452
|
3021
|
|
|
|
|
|
SV *aref = ST(0); |
|
3453
|
3021
|
50
|
|
|
|
|
if (!SvROK(aref) || SvTYPE(SvRV(aref)) != SVt_PVAV) { |
|
|
|
50
|
|
|
|
|
|
|
3454
|
0
|
|
|
|
|
|
croak("Func::Util::any_lt: first argument must be an arrayref"); |
|
3455
|
|
|
|
|
|
|
} |
|
3456
|
|
|
|
|
|
|
|
|
3457
|
3021
|
|
|
|
|
|
AV *av = (AV *)SvRV(aref); |
|
3458
|
3021
|
|
|
|
|
|
SSize_t len = av_len(av) + 1; |
|
3459
|
|
|
|
|
|
|
SSize_t i; |
|
3460
|
|
|
|
|
|
|
|
|
3461
|
3021
|
100
|
|
|
|
|
if (items == 2) { |
|
3462
|
2021
|
|
|
|
|
|
NV threshold = SvNV(ST(1)); |
|
3463
|
9049
|
100
|
|
|
|
|
for (i = 0; i < len; i++) { |
|
3464
|
8044
|
|
|
|
|
|
SV **elem = av_fetch(av, i, 0); |
|
3465
|
8044
|
50
|
|
|
|
|
if (elem && SvNV(*elem) < threshold) { |
|
|
|
100
|
|
|
|
|
|
|
3466
|
1016
|
|
|
|
|
|
XSRETURN_YES; |
|
3467
|
|
|
|
|
|
|
} |
|
3468
|
|
|
|
|
|
|
} |
|
3469
|
|
|
|
|
|
|
} else { |
|
3470
|
1000
|
|
|
|
|
|
char *key = SvPV_nolen(ST(1)); |
|
3471
|
1000
|
|
|
|
|
|
NV threshold = SvNV(ST(2)); |
|
3472
|
3000
|
50
|
|
|
|
|
for (i = 0; i < len; i++) { |
|
3473
|
3000
|
|
|
|
|
|
SV **elem = av_fetch(av, i, 0); |
|
3474
|
3000
|
50
|
|
|
|
|
if (elem && SvROK(*elem) && SvTYPE(SvRV(*elem)) == SVt_PVHV) { |
|
|
|
50
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
3475
|
3000
|
|
|
|
|
|
HV *hv = (HV *)SvRV(*elem); |
|
3476
|
3000
|
|
|
|
|
|
SV **val = hv_fetch(hv, key, strlen(key), 0); |
|
3477
|
3000
|
50
|
|
|
|
|
if (val && SvNV(*val) < threshold) { |
|
|
|
100
|
|
|
|
|
|
|
3478
|
1000
|
|
|
|
|
|
XSRETURN_YES; |
|
3479
|
|
|
|
|
|
|
} |
|
3480
|
|
|
|
|
|
|
} |
|
3481
|
|
|
|
|
|
|
} |
|
3482
|
|
|
|
|
|
|
} |
|
3483
|
|
|
|
|
|
|
|
|
3484
|
1005
|
|
|
|
|
|
XSRETURN_NO; |
|
3485
|
|
|
|
|
|
|
} |
|
3486
|
|
|
|
|
|
|
|
|
3487
|
|
|
|
|
|
|
/* any_ge(\@array, $threshold) or any_ge(\@array, $key, $threshold) */ |
|
3488
|
2007
|
|
|
|
|
|
XS_INTERNAL(xs_any_ge) { |
|
3489
|
2007
|
|
|
|
|
|
dXSARGS; |
|
3490
|
2007
|
50
|
|
|
|
|
if (items < 2 || items > 3) croak("Usage: Func::Util::any_ge(\\@array, $threshold) or any_ge(\\@array, $key, $threshold)"); |
|
|
|
50
|
|
|
|
|
|
|
3491
|
|
|
|
|
|
|
|
|
3492
|
2007
|
|
|
|
|
|
SV *aref = ST(0); |
|
3493
|
2007
|
50
|
|
|
|
|
if (!SvROK(aref) || SvTYPE(SvRV(aref)) != SVt_PVAV) { |
|
|
|
50
|
|
|
|
|
|
|
3494
|
0
|
|
|
|
|
|
croak("Func::Util::any_ge: first argument must be an arrayref"); |
|
3495
|
|
|
|
|
|
|
} |
|
3496
|
|
|
|
|
|
|
|
|
3497
|
2007
|
|
|
|
|
|
AV *av = (AV *)SvRV(aref); |
|
3498
|
2007
|
|
|
|
|
|
SSize_t len = av_len(av) + 1; |
|
3499
|
|
|
|
|
|
|
SSize_t i; |
|
3500
|
|
|
|
|
|
|
|
|
3501
|
2007
|
100
|
|
|
|
|
if (items == 2) { |
|
3502
|
1007
|
|
|
|
|
|
NV threshold = SvNV(ST(1)); |
|
3503
|
7037
|
100
|
|
|
|
|
for (i = 0; i < len; i++) { |
|
3504
|
7035
|
|
|
|
|
|
SV **elem = av_fetch(av, i, 0); |
|
3505
|
7035
|
50
|
|
|
|
|
if (elem && SvNV(*elem) >= threshold) { |
|
|
|
100
|
|
|
|
|
|
|
3506
|
1005
|
|
|
|
|
|
XSRETURN_YES; |
|
3507
|
|
|
|
|
|
|
} |
|
3508
|
|
|
|
|
|
|
} |
|
3509
|
|
|
|
|
|
|
} else { |
|
3510
|
1000
|
|
|
|
|
|
char *key = SvPV_nolen(ST(1)); |
|
3511
|
1000
|
|
|
|
|
|
NV threshold = SvNV(ST(2)); |
|
3512
|
4000
|
50
|
|
|
|
|
for (i = 0; i < len; i++) { |
|
3513
|
4000
|
|
|
|
|
|
SV **elem = av_fetch(av, i, 0); |
|
3514
|
4000
|
50
|
|
|
|
|
if (elem && SvROK(*elem) && SvTYPE(SvRV(*elem)) == SVt_PVHV) { |
|
|
|
50
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
3515
|
4000
|
|
|
|
|
|
HV *hv = (HV *)SvRV(*elem); |
|
3516
|
4000
|
|
|
|
|
|
SV **val = hv_fetch(hv, key, strlen(key), 0); |
|
3517
|
4000
|
50
|
|
|
|
|
if (val && SvNV(*val) >= threshold) { |
|
|
|
100
|
|
|
|
|
|
|
3518
|
1000
|
|
|
|
|
|
XSRETURN_YES; |
|
3519
|
|
|
|
|
|
|
} |
|
3520
|
|
|
|
|
|
|
} |
|
3521
|
|
|
|
|
|
|
} |
|
3522
|
|
|
|
|
|
|
} |
|
3523
|
|
|
|
|
|
|
|
|
3524
|
2
|
|
|
|
|
|
XSRETURN_NO; |
|
3525
|
|
|
|
|
|
|
} |
|
3526
|
|
|
|
|
|
|
|
|
3527
|
|
|
|
|
|
|
/* any_le(\@array, $threshold) or any_le(\@array, $key, $threshold) */ |
|
3528
|
2005
|
|
|
|
|
|
XS_INTERNAL(xs_any_le) { |
|
3529
|
2005
|
|
|
|
|
|
dXSARGS; |
|
3530
|
2005
|
50
|
|
|
|
|
if (items < 2 || items > 3) croak("Usage: Func::Util::any_le(\\@array, $threshold) or any_le(\\@array, $key, $threshold)"); |
|
|
|
50
|
|
|
|
|
|
|
3531
|
|
|
|
|
|
|
|
|
3532
|
2005
|
|
|
|
|
|
SV *aref = ST(0); |
|
3533
|
2005
|
50
|
|
|
|
|
if (!SvROK(aref) || SvTYPE(SvRV(aref)) != SVt_PVAV) { |
|
|
|
50
|
|
|
|
|
|
|
3534
|
0
|
|
|
|
|
|
croak("Func::Util::any_le: first argument must be an arrayref"); |
|
3535
|
|
|
|
|
|
|
} |
|
3536
|
|
|
|
|
|
|
|
|
3537
|
2005
|
|
|
|
|
|
AV *av = (AV *)SvRV(aref); |
|
3538
|
2005
|
|
|
|
|
|
SSize_t len = av_len(av) + 1; |
|
3539
|
|
|
|
|
|
|
SSize_t i; |
|
3540
|
|
|
|
|
|
|
|
|
3541
|
2005
|
100
|
|
|
|
|
if (items == 2) { |
|
3542
|
1005
|
|
|
|
|
|
NV threshold = SvNV(ST(1)); |
|
3543
|
1018
|
100
|
|
|
|
|
for (i = 0; i < len; i++) { |
|
3544
|
1016
|
|
|
|
|
|
SV **elem = av_fetch(av, i, 0); |
|
3545
|
1016
|
50
|
|
|
|
|
if (elem && SvNV(*elem) <= threshold) { |
|
|
|
100
|
|
|
|
|
|
|
3546
|
1003
|
|
|
|
|
|
XSRETURN_YES; |
|
3547
|
|
|
|
|
|
|
} |
|
3548
|
|
|
|
|
|
|
} |
|
3549
|
|
|
|
|
|
|
} else { |
|
3550
|
1000
|
|
|
|
|
|
char *key = SvPV_nolen(ST(1)); |
|
3551
|
1000
|
|
|
|
|
|
NV threshold = SvNV(ST(2)); |
|
3552
|
3000
|
50
|
|
|
|
|
for (i = 0; i < len; i++) { |
|
3553
|
3000
|
|
|
|
|
|
SV **elem = av_fetch(av, i, 0); |
|
3554
|
3000
|
50
|
|
|
|
|
if (elem && SvROK(*elem) && SvTYPE(SvRV(*elem)) == SVt_PVHV) { |
|
|
|
50
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
3555
|
3000
|
|
|
|
|
|
HV *hv = (HV *)SvRV(*elem); |
|
3556
|
3000
|
|
|
|
|
|
SV **val = hv_fetch(hv, key, strlen(key), 0); |
|
3557
|
3000
|
50
|
|
|
|
|
if (val && SvNV(*val) <= threshold) { |
|
|
|
100
|
|
|
|
|
|
|
3558
|
1000
|
|
|
|
|
|
XSRETURN_YES; |
|
3559
|
|
|
|
|
|
|
} |
|
3560
|
|
|
|
|
|
|
} |
|
3561
|
|
|
|
|
|
|
} |
|
3562
|
|
|
|
|
|
|
} |
|
3563
|
|
|
|
|
|
|
|
|
3564
|
2
|
|
|
|
|
|
XSRETURN_NO; |
|
3565
|
|
|
|
|
|
|
} |
|
3566
|
|
|
|
|
|
|
|
|
3567
|
|
|
|
|
|
|
/* any_eq(\@array, $value) or any_eq(\@array, $key, $value) */ |
|
3568
|
3009
|
|
|
|
|
|
XS_INTERNAL(xs_any_eq) { |
|
3569
|
3009
|
|
|
|
|
|
dXSARGS; |
|
3570
|
3009
|
50
|
|
|
|
|
if (items < 2 || items > 3) croak("Usage: Func::Util::any_eq(\\@array, $value) or any_eq(\\@array, $key, $value)"); |
|
|
|
50
|
|
|
|
|
|
|
3571
|
|
|
|
|
|
|
|
|
3572
|
3009
|
|
|
|
|
|
SV *aref = ST(0); |
|
3573
|
3009
|
50
|
|
|
|
|
if (!SvROK(aref) || SvTYPE(SvRV(aref)) != SVt_PVAV) { |
|
|
|
50
|
|
|
|
|
|
|
3574
|
0
|
|
|
|
|
|
croak("Func::Util::any_eq: first argument must be an arrayref"); |
|
3575
|
|
|
|
|
|
|
} |
|
3576
|
|
|
|
|
|
|
|
|
3577
|
3009
|
|
|
|
|
|
AV *av = (AV *)SvRV(aref); |
|
3578
|
3009
|
|
|
|
|
|
SSize_t len = av_len(av) + 1; |
|
3579
|
|
|
|
|
|
|
SSize_t i; |
|
3580
|
|
|
|
|
|
|
|
|
3581
|
3009
|
100
|
|
|
|
|
if (items == 2) { |
|
3582
|
2009
|
|
|
|
|
|
NV target = SvNV(ST(1)); |
|
3583
|
12037
|
100
|
|
|
|
|
for (i = 0; i < len; i++) { |
|
3584
|
11033
|
|
|
|
|
|
SV **elem = av_fetch(av, i, 0); |
|
3585
|
11033
|
50
|
|
|
|
|
if (elem && SvNV(*elem) == target) { |
|
|
|
100
|
|
|
|
|
|
|
3586
|
1005
|
|
|
|
|
|
XSRETURN_YES; |
|
3587
|
|
|
|
|
|
|
} |
|
3588
|
|
|
|
|
|
|
} |
|
3589
|
|
|
|
|
|
|
} else { |
|
3590
|
1000
|
|
|
|
|
|
char *key = SvPV_nolen(ST(1)); |
|
3591
|
1000
|
|
|
|
|
|
NV target = SvNV(ST(2)); |
|
3592
|
1000
|
50
|
|
|
|
|
for (i = 0; i < len; i++) { |
|
3593
|
1000
|
|
|
|
|
|
SV **elem = av_fetch(av, i, 0); |
|
3594
|
1000
|
50
|
|
|
|
|
if (elem && SvROK(*elem) && SvTYPE(SvRV(*elem)) == SVt_PVHV) { |
|
|
|
50
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
3595
|
1000
|
|
|
|
|
|
HV *hv = (HV *)SvRV(*elem); |
|
3596
|
1000
|
|
|
|
|
|
SV **val = hv_fetch(hv, key, strlen(key), 0); |
|
3597
|
1000
|
50
|
|
|
|
|
if (val && SvNV(*val) == target) { |
|
|
|
50
|
|
|
|
|
|
|
3598
|
1000
|
|
|
|
|
|
XSRETURN_YES; |
|
3599
|
|
|
|
|
|
|
} |
|
3600
|
|
|
|
|
|
|
} |
|
3601
|
|
|
|
|
|
|
} |
|
3602
|
|
|
|
|
|
|
} |
|
3603
|
|
|
|
|
|
|
|
|
3604
|
1004
|
|
|
|
|
|
XSRETURN_NO; |
|
3605
|
|
|
|
|
|
|
} |
|
3606
|
|
|
|
|
|
|
|
|
3607
|
|
|
|
|
|
|
/* any_ne(\@array, $value) or any_ne(\@array, $key, $value) */ |
|
3608
|
2004
|
|
|
|
|
|
XS_INTERNAL(xs_any_ne) { |
|
3609
|
2004
|
|
|
|
|
|
dXSARGS; |
|
3610
|
2004
|
50
|
|
|
|
|
if (items < 2 || items > 3) croak("Usage: Func::Util::any_ne(\\@array, $value) or any_ne(\\@array, $key, $value)"); |
|
|
|
50
|
|
|
|
|
|
|
3611
|
|
|
|
|
|
|
|
|
3612
|
2004
|
|
|
|
|
|
SV *aref = ST(0); |
|
3613
|
2004
|
50
|
|
|
|
|
if (!SvROK(aref) || SvTYPE(SvRV(aref)) != SVt_PVAV) { |
|
|
|
50
|
|
|
|
|
|
|
3614
|
0
|
|
|
|
|
|
croak("Func::Util::any_ne: first argument must be an arrayref"); |
|
3615
|
|
|
|
|
|
|
} |
|
3616
|
|
|
|
|
|
|
|
|
3617
|
2004
|
|
|
|
|
|
AV *av = (AV *)SvRV(aref); |
|
3618
|
2004
|
|
|
|
|
|
SSize_t len = av_len(av) + 1; |
|
3619
|
|
|
|
|
|
|
SSize_t i; |
|
3620
|
|
|
|
|
|
|
|
|
3621
|
2004
|
100
|
|
|
|
|
if (items == 2) { |
|
3622
|
1004
|
|
|
|
|
|
NV target = SvNV(ST(1)); |
|
3623
|
2010
|
100
|
|
|
|
|
for (i = 0; i < len; i++) { |
|
3624
|
2009
|
|
|
|
|
|
SV **elem = av_fetch(av, i, 0); |
|
3625
|
2009
|
50
|
|
|
|
|
if (elem && SvNV(*elem) != target) { |
|
|
|
100
|
|
|
|
|
|
|
3626
|
1003
|
|
|
|
|
|
XSRETURN_YES; |
|
3627
|
|
|
|
|
|
|
} |
|
3628
|
|
|
|
|
|
|
} |
|
3629
|
|
|
|
|
|
|
} else { |
|
3630
|
1000
|
|
|
|
|
|
char *key = SvPV_nolen(ST(1)); |
|
3631
|
1000
|
|
|
|
|
|
NV target = SvNV(ST(2)); |
|
3632
|
2000
|
50
|
|
|
|
|
for (i = 0; i < len; i++) { |
|
3633
|
2000
|
|
|
|
|
|
SV **elem = av_fetch(av, i, 0); |
|
3634
|
2000
|
50
|
|
|
|
|
if (elem && SvROK(*elem) && SvTYPE(SvRV(*elem)) == SVt_PVHV) { |
|
|
|
50
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
3635
|
2000
|
|
|
|
|
|
HV *hv = (HV *)SvRV(*elem); |
|
3636
|
2000
|
|
|
|
|
|
SV **val = hv_fetch(hv, key, strlen(key), 0); |
|
3637
|
2000
|
50
|
|
|
|
|
if (val && SvNV(*val) != target) { |
|
|
|
100
|
|
|
|
|
|
|
3638
|
1000
|
|
|
|
|
|
XSRETURN_YES; |
|
3639
|
|
|
|
|
|
|
} |
|
3640
|
|
|
|
|
|
|
} |
|
3641
|
|
|
|
|
|
|
} |
|
3642
|
|
|
|
|
|
|
} |
|
3643
|
|
|
|
|
|
|
|
|
3644
|
1
|
|
|
|
|
|
XSRETURN_NO; |
|
3645
|
|
|
|
|
|
|
} |
|
3646
|
|
|
|
|
|
|
|
|
3647
|
|
|
|
|
|
|
/* all_gt(\@array, $n) - true if all elements > n, pure C */ |
|
3648
|
|
|
|
|
|
|
/* all_gt(\@array, $threshold) or all_gt(\@array, $key, $threshold) |
|
3649
|
|
|
|
|
|
|
true if all elements > threshold, pure C */ |
|
3650
|
3013
|
|
|
|
|
|
XS_INTERNAL(xs_all_gt) { |
|
3651
|
3013
|
|
|
|
|
|
dXSARGS; |
|
3652
|
3013
|
50
|
|
|
|
|
if (items < 2 || items > 3) croak("Usage: Func::Util::all_gt(\\@array, $threshold) or all_gt(\\@array, $key, $threshold)"); |
|
|
|
50
|
|
|
|
|
|
|
3653
|
|
|
|
|
|
|
|
|
3654
|
3013
|
|
|
|
|
|
SV *aref = ST(0); |
|
3655
|
3013
|
50
|
|
|
|
|
if (!SvROK(aref) || SvTYPE(SvRV(aref)) != SVt_PVAV) { |
|
|
|
50
|
|
|
|
|
|
|
3656
|
0
|
|
|
|
|
|
croak("Func::Util::all_gt: first argument must be an arrayref"); |
|
3657
|
|
|
|
|
|
|
} |
|
3658
|
|
|
|
|
|
|
|
|
3659
|
3013
|
|
|
|
|
|
AV *av = (AV *)SvRV(aref); |
|
3660
|
3013
|
|
|
|
|
|
SSize_t len = av_len(av) + 1; |
|
3661
|
|
|
|
|
|
|
SSize_t i; |
|
3662
|
|
|
|
|
|
|
|
|
3663
|
3013
|
100
|
|
|
|
|
if (len == 0) XSRETURN_YES; /* vacuous truth */ |
|
3664
|
|
|
|
|
|
|
|
|
3665
|
3010
|
100
|
|
|
|
|
if (items == 2) { |
|
3666
|
2010
|
|
|
|
|
|
NV threshold = SvNV(ST(1)); |
|
3667
|
9038
|
100
|
|
|
|
|
for (i = 0; i < len; i++) { |
|
3668
|
8033
|
|
|
|
|
|
SV **elem = av_fetch(av, i, 0); |
|
3669
|
8033
|
50
|
|
|
|
|
if (!elem || SvNV(*elem) <= threshold) { |
|
|
|
100
|
|
|
|
|
|
|
3670
|
1005
|
|
|
|
|
|
XSRETURN_NO; |
|
3671
|
|
|
|
|
|
|
} |
|
3672
|
|
|
|
|
|
|
} |
|
3673
|
|
|
|
|
|
|
} else { |
|
3674
|
1000
|
|
|
|
|
|
char *key = SvPV_nolen(ST(1)); |
|
3675
|
1000
|
|
|
|
|
|
NV threshold = SvNV(ST(2)); |
|
3676
|
5000
|
100
|
|
|
|
|
for (i = 0; i < len; i++) { |
|
3677
|
4000
|
|
|
|
|
|
SV **elem = av_fetch(av, i, 0); |
|
3678
|
4000
|
50
|
|
|
|
|
if (!elem || !SvROK(*elem) || SvTYPE(SvRV(*elem)) != SVt_PVHV) { |
|
|
|
50
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
3679
|
0
|
|
|
|
|
|
XSRETURN_NO; |
|
3680
|
|
|
|
|
|
|
} |
|
3681
|
4000
|
|
|
|
|
|
HV *hv = (HV *)SvRV(*elem); |
|
3682
|
4000
|
|
|
|
|
|
SV **val = hv_fetch(hv, key, strlen(key), 0); |
|
3683
|
4000
|
50
|
|
|
|
|
if (!val || SvNV(*val) <= threshold) { |
|
|
|
50
|
|
|
|
|
|
|
3684
|
0
|
|
|
|
|
|
XSRETURN_NO; |
|
3685
|
|
|
|
|
|
|
} |
|
3686
|
|
|
|
|
|
|
} |
|
3687
|
|
|
|
|
|
|
} |
|
3688
|
|
|
|
|
|
|
|
|
3689
|
2005
|
|
|
|
|
|
XSRETURN_YES; |
|
3690
|
|
|
|
|
|
|
} |
|
3691
|
|
|
|
|
|
|
|
|
3692
|
|
|
|
|
|
|
/* all_lt(\@array, $threshold) or all_lt(\@array, $key, $threshold) */ |
|
3693
|
3009
|
|
|
|
|
|
XS_INTERNAL(xs_all_lt) { |
|
3694
|
3009
|
|
|
|
|
|
dXSARGS; |
|
3695
|
3009
|
50
|
|
|
|
|
if (items < 2 || items > 3) croak("Usage: Func::Util::all_lt(\\@array, $threshold) or all_lt(\\@array, $key, $threshold)"); |
|
|
|
50
|
|
|
|
|
|
|
3696
|
|
|
|
|
|
|
|
|
3697
|
3009
|
|
|
|
|
|
SV *aref = ST(0); |
|
3698
|
3009
|
50
|
|
|
|
|
if (!SvROK(aref) || SvTYPE(SvRV(aref)) != SVt_PVAV) { |
|
|
|
50
|
|
|
|
|
|
|
3699
|
0
|
|
|
|
|
|
croak("Func::Util::all_lt: first argument must be an arrayref"); |
|
3700
|
|
|
|
|
|
|
} |
|
3701
|
|
|
|
|
|
|
|
|
3702
|
3009
|
|
|
|
|
|
AV *av = (AV *)SvRV(aref); |
|
3703
|
3009
|
|
|
|
|
|
SSize_t len = av_len(av) + 1; |
|
3704
|
|
|
|
|
|
|
SSize_t i; |
|
3705
|
|
|
|
|
|
|
|
|
3706
|
3009
|
100
|
|
|
|
|
if (len == 0) XSRETURN_YES; |
|
3707
|
|
|
|
|
|
|
|
|
3708
|
3008
|
100
|
|
|
|
|
if (items == 2) { |
|
3709
|
2008
|
|
|
|
|
|
NV threshold = SvNV(ST(1)); |
|
3710
|
11044
|
100
|
|
|
|
|
for (i = 0; i < len; i++) { |
|
3711
|
10040
|
|
|
|
|
|
SV **elem = av_fetch(av, i, 0); |
|
3712
|
10040
|
50
|
|
|
|
|
if (!elem || SvNV(*elem) >= threshold) { |
|
|
|
100
|
|
|
|
|
|
|
3713
|
1004
|
|
|
|
|
|
XSRETURN_NO; |
|
3714
|
|
|
|
|
|
|
} |
|
3715
|
|
|
|
|
|
|
} |
|
3716
|
|
|
|
|
|
|
} else { |
|
3717
|
1000
|
|
|
|
|
|
char *key = SvPV_nolen(ST(1)); |
|
3718
|
1000
|
|
|
|
|
|
NV threshold = SvNV(ST(2)); |
|
3719
|
5000
|
100
|
|
|
|
|
for (i = 0; i < len; i++) { |
|
3720
|
4000
|
|
|
|
|
|
SV **elem = av_fetch(av, i, 0); |
|
3721
|
4000
|
50
|
|
|
|
|
if (!elem || !SvROK(*elem) || SvTYPE(SvRV(*elem)) != SVt_PVHV) { |
|
|
|
50
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
3722
|
0
|
|
|
|
|
|
XSRETURN_NO; |
|
3723
|
|
|
|
|
|
|
} |
|
3724
|
4000
|
|
|
|
|
|
HV *hv = (HV *)SvRV(*elem); |
|
3725
|
4000
|
|
|
|
|
|
SV **val = hv_fetch(hv, key, strlen(key), 0); |
|
3726
|
4000
|
50
|
|
|
|
|
if (!val || SvNV(*val) >= threshold) { |
|
|
|
50
|
|
|
|
|
|
|
3727
|
0
|
|
|
|
|
|
XSRETURN_NO; |
|
3728
|
|
|
|
|
|
|
} |
|
3729
|
|
|
|
|
|
|
} |
|
3730
|
|
|
|
|
|
|
} |
|
3731
|
|
|
|
|
|
|
|
|
3732
|
2004
|
|
|
|
|
|
XSRETURN_YES; |
|
3733
|
|
|
|
|
|
|
} |
|
3734
|
|
|
|
|
|
|
|
|
3735
|
|
|
|
|
|
|
/* all_ge(\@array, $threshold) or all_ge(\@array, $key, $threshold) */ |
|
3736
|
3019
|
|
|
|
|
|
XS_INTERNAL(xs_all_ge) { |
|
3737
|
3019
|
|
|
|
|
|
dXSARGS; |
|
3738
|
3019
|
50
|
|
|
|
|
if (items < 2 || items > 3) croak("Usage: Func::Util::all_ge(\\@array, $threshold) or all_ge(\\@array, $key, $threshold)"); |
|
|
|
50
|
|
|
|
|
|
|
3739
|
|
|
|
|
|
|
|
|
3740
|
3019
|
|
|
|
|
|
SV *aref = ST(0); |
|
3741
|
3019
|
50
|
|
|
|
|
if (!SvROK(aref) || SvTYPE(SvRV(aref)) != SVt_PVAV) { |
|
|
|
50
|
|
|
|
|
|
|
3742
|
0
|
|
|
|
|
|
croak("Func::Util::all_ge: first argument must be an arrayref"); |
|
3743
|
|
|
|
|
|
|
} |
|
3744
|
|
|
|
|
|
|
|
|
3745
|
3019
|
|
|
|
|
|
AV *av = (AV *)SvRV(aref); |
|
3746
|
3019
|
|
|
|
|
|
SSize_t len = av_len(av) + 1; |
|
3747
|
|
|
|
|
|
|
SSize_t i; |
|
3748
|
|
|
|
|
|
|
|
|
3749
|
3019
|
50
|
|
|
|
|
if (len == 0) XSRETURN_YES; |
|
3750
|
|
|
|
|
|
|
|
|
3751
|
3019
|
100
|
|
|
|
|
if (items == 2) { |
|
3752
|
2017
|
|
|
|
|
|
NV threshold = SvNV(ST(1)); |
|
3753
|
9102
|
100
|
|
|
|
|
for (i = 0; i < len; i++) { |
|
3754
|
8089
|
|
|
|
|
|
SV **elem = av_fetch(av, i, 0); |
|
3755
|
8089
|
50
|
|
|
|
|
if (!elem || SvNV(*elem) < threshold) { |
|
|
|
100
|
|
|
|
|
|
|
3756
|
1004
|
|
|
|
|
|
XSRETURN_NO; |
|
3757
|
|
|
|
|
|
|
} |
|
3758
|
|
|
|
|
|
|
} |
|
3759
|
|
|
|
|
|
|
} else { |
|
3760
|
1002
|
|
|
|
|
|
char *key = SvPV_nolen(ST(1)); |
|
3761
|
1002
|
|
|
|
|
|
NV threshold = SvNV(ST(2)); |
|
3762
|
5004
|
100
|
|
|
|
|
for (i = 0; i < len; i++) { |
|
3763
|
4003
|
|
|
|
|
|
SV **elem = av_fetch(av, i, 0); |
|
3764
|
4003
|
50
|
|
|
|
|
if (!elem || !SvROK(*elem) || SvTYPE(SvRV(*elem)) != SVt_PVHV) { |
|
|
|
50
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
3765
|
0
|
|
|
|
|
|
XSRETURN_NO; |
|
3766
|
|
|
|
|
|
|
} |
|
3767
|
4003
|
|
|
|
|
|
HV *hv = (HV *)SvRV(*elem); |
|
3768
|
4003
|
|
|
|
|
|
SV **val = hv_fetch(hv, key, strlen(key), 0); |
|
3769
|
4003
|
50
|
|
|
|
|
if (!val || SvNV(*val) < threshold) { |
|
|
|
100
|
|
|
|
|
|
|
3770
|
1
|
|
|
|
|
|
XSRETURN_NO; |
|
3771
|
|
|
|
|
|
|
} |
|
3772
|
|
|
|
|
|
|
} |
|
3773
|
|
|
|
|
|
|
} |
|
3774
|
|
|
|
|
|
|
|
|
3775
|
2014
|
|
|
|
|
|
XSRETURN_YES; |
|
3776
|
|
|
|
|
|
|
} |
|
3777
|
|
|
|
|
|
|
|
|
3778
|
|
|
|
|
|
|
/* all_le(\@array, $threshold) or all_le(\@array, $key, $threshold) */ |
|
3779
|
3004
|
|
|
|
|
|
XS_INTERNAL(xs_all_le) { |
|
3780
|
3004
|
|
|
|
|
|
dXSARGS; |
|
3781
|
3004
|
50
|
|
|
|
|
if (items < 2 || items > 3) croak("Usage: Func::Util::all_le(\\@array, $threshold) or all_le(\\@array, $key, $threshold)"); |
|
|
|
50
|
|
|
|
|
|
|
3782
|
|
|
|
|
|
|
|
|
3783
|
3004
|
|
|
|
|
|
SV *aref = ST(0); |
|
3784
|
3004
|
50
|
|
|
|
|
if (!SvROK(aref) || SvTYPE(SvRV(aref)) != SVt_PVAV) { |
|
|
|
50
|
|
|
|
|
|
|
3785
|
0
|
|
|
|
|
|
croak("Func::Util::all_le: first argument must be an arrayref"); |
|
3786
|
|
|
|
|
|
|
} |
|
3787
|
|
|
|
|
|
|
|
|
3788
|
3004
|
|
|
|
|
|
AV *av = (AV *)SvRV(aref); |
|
3789
|
3004
|
|
|
|
|
|
SSize_t len = av_len(av) + 1; |
|
3790
|
|
|
|
|
|
|
SSize_t i; |
|
3791
|
|
|
|
|
|
|
|
|
3792
|
3004
|
50
|
|
|
|
|
if (len == 0) XSRETURN_YES; |
|
3793
|
|
|
|
|
|
|
|
|
3794
|
3004
|
100
|
|
|
|
|
if (items == 2) { |
|
3795
|
2004
|
|
|
|
|
|
NV threshold = SvNV(ST(1)); |
|
3796
|
12025
|
100
|
|
|
|
|
for (i = 0; i < len; i++) { |
|
3797
|
11023
|
|
|
|
|
|
SV **elem = av_fetch(av, i, 0); |
|
3798
|
11023
|
50
|
|
|
|
|
if (!elem || SvNV(*elem) > threshold) { |
|
|
|
100
|
|
|
|
|
|
|
3799
|
1002
|
|
|
|
|
|
XSRETURN_NO; |
|
3800
|
|
|
|
|
|
|
} |
|
3801
|
|
|
|
|
|
|
} |
|
3802
|
|
|
|
|
|
|
} else { |
|
3803
|
1000
|
|
|
|
|
|
char *key = SvPV_nolen(ST(1)); |
|
3804
|
1000
|
|
|
|
|
|
NV threshold = SvNV(ST(2)); |
|
3805
|
5000
|
100
|
|
|
|
|
for (i = 0; i < len; i++) { |
|
3806
|
4000
|
|
|
|
|
|
SV **elem = av_fetch(av, i, 0); |
|
3807
|
4000
|
50
|
|
|
|
|
if (!elem || !SvROK(*elem) || SvTYPE(SvRV(*elem)) != SVt_PVHV) { |
|
|
|
50
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
3808
|
0
|
|
|
|
|
|
XSRETURN_NO; |
|
3809
|
|
|
|
|
|
|
} |
|
3810
|
4000
|
|
|
|
|
|
HV *hv = (HV *)SvRV(*elem); |
|
3811
|
4000
|
|
|
|
|
|
SV **val = hv_fetch(hv, key, strlen(key), 0); |
|
3812
|
4000
|
50
|
|
|
|
|
if (!val || SvNV(*val) > threshold) { |
|
|
|
50
|
|
|
|
|
|
|
3813
|
0
|
|
|
|
|
|
XSRETURN_NO; |
|
3814
|
|
|
|
|
|
|
} |
|
3815
|
|
|
|
|
|
|
} |
|
3816
|
|
|
|
|
|
|
} |
|
3817
|
|
|
|
|
|
|
|
|
3818
|
2002
|
|
|
|
|
|
XSRETURN_YES; |
|
3819
|
|
|
|
|
|
|
} |
|
3820
|
|
|
|
|
|
|
|
|
3821
|
|
|
|
|
|
|
/* all_eq(\@array, $value) or all_eq(\@array, $key, $value) */ |
|
3822
|
2007
|
|
|
|
|
|
XS_INTERNAL(xs_all_eq) { |
|
3823
|
2007
|
|
|
|
|
|
dXSARGS; |
|
3824
|
2007
|
50
|
|
|
|
|
if (items < 2 || items > 3) croak("Usage: Func::Util::all_eq(\\@array, $value) or all_eq(\\@array, $key, $value)"); |
|
|
|
50
|
|
|
|
|
|
|
3825
|
|
|
|
|
|
|
|
|
3826
|
2007
|
|
|
|
|
|
SV *aref = ST(0); |
|
3827
|
2007
|
50
|
|
|
|
|
if (!SvROK(aref) || SvTYPE(SvRV(aref)) != SVt_PVAV) { |
|
|
|
50
|
|
|
|
|
|
|
3828
|
0
|
|
|
|
|
|
croak("Func::Util::all_eq: first argument must be an arrayref"); |
|
3829
|
|
|
|
|
|
|
} |
|
3830
|
|
|
|
|
|
|
|
|
3831
|
2007
|
|
|
|
|
|
AV *av = (AV *)SvRV(aref); |
|
3832
|
2007
|
|
|
|
|
|
SSize_t len = av_len(av) + 1; |
|
3833
|
|
|
|
|
|
|
SSize_t i; |
|
3834
|
|
|
|
|
|
|
|
|
3835
|
2007
|
100
|
|
|
|
|
if (len == 0) XSRETURN_YES; |
|
3836
|
|
|
|
|
|
|
|
|
3837
|
2006
|
50
|
|
|
|
|
if (items == 2) { |
|
3838
|
2006
|
|
|
|
|
|
NV target = SvNV(ST(1)); |
|
3839
|
6018
|
100
|
|
|
|
|
for (i = 0; i < len; i++) { |
|
3840
|
5014
|
|
|
|
|
|
SV **elem = av_fetch(av, i, 0); |
|
3841
|
5014
|
50
|
|
|
|
|
if (!elem || SvNV(*elem) != target) { |
|
|
|
100
|
|
|
|
|
|
|
3842
|
1002
|
|
|
|
|
|
XSRETURN_NO; |
|
3843
|
|
|
|
|
|
|
} |
|
3844
|
|
|
|
|
|
|
} |
|
3845
|
|
|
|
|
|
|
} else { |
|
3846
|
0
|
|
|
|
|
|
char *key = SvPV_nolen(ST(1)); |
|
3847
|
0
|
|
|
|
|
|
NV target = SvNV(ST(2)); |
|
3848
|
0
|
0
|
|
|
|
|
for (i = 0; i < len; i++) { |
|
3849
|
0
|
|
|
|
|
|
SV **elem = av_fetch(av, i, 0); |
|
3850
|
0
|
0
|
|
|
|
|
if (!elem || !SvROK(*elem) || SvTYPE(SvRV(*elem)) != SVt_PVHV) { |
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
3851
|
0
|
|
|
|
|
|
XSRETURN_NO; |
|
3852
|
|
|
|
|
|
|
} |
|
3853
|
0
|
|
|
|
|
|
HV *hv = (HV *)SvRV(*elem); |
|
3854
|
0
|
|
|
|
|
|
SV **val = hv_fetch(hv, key, strlen(key), 0); |
|
3855
|
0
|
0
|
|
|
|
|
if (!val || SvNV(*val) != target) { |
|
|
|
0
|
|
|
|
|
|
|
3856
|
0
|
|
|
|
|
|
XSRETURN_NO; |
|
3857
|
|
|
|
|
|
|
} |
|
3858
|
|
|
|
|
|
|
} |
|
3859
|
|
|
|
|
|
|
} |
|
3860
|
|
|
|
|
|
|
|
|
3861
|
1004
|
|
|
|
|
|
XSRETURN_YES; |
|
3862
|
|
|
|
|
|
|
} |
|
3863
|
|
|
|
|
|
|
|
|
3864
|
|
|
|
|
|
|
/* all_ne(\@array, $value) or all_ne(\@array, $key, $value) */ |
|
3865
|
2004
|
|
|
|
|
|
XS_INTERNAL(xs_all_ne) { |
|
3866
|
2004
|
|
|
|
|
|
dXSARGS; |
|
3867
|
2004
|
50
|
|
|
|
|
if (items < 2 || items > 3) croak("Usage: Func::Util::all_ne(\\@array, $value) or all_ne(\\@array, $key, $value)"); |
|
|
|
50
|
|
|
|
|
|
|
3868
|
|
|
|
|
|
|
|
|
3869
|
2004
|
|
|
|
|
|
SV *aref = ST(0); |
|
3870
|
2004
|
50
|
|
|
|
|
if (!SvROK(aref) || SvTYPE(SvRV(aref)) != SVt_PVAV) { |
|
|
|
50
|
|
|
|
|
|
|
3871
|
0
|
|
|
|
|
|
croak("Func::Util::all_ne: first argument must be an arrayref"); |
|
3872
|
|
|
|
|
|
|
} |
|
3873
|
|
|
|
|
|
|
|
|
3874
|
2004
|
|
|
|
|
|
AV *av = (AV *)SvRV(aref); |
|
3875
|
2004
|
|
|
|
|
|
SSize_t len = av_len(av) + 1; |
|
3876
|
|
|
|
|
|
|
SSize_t i; |
|
3877
|
|
|
|
|
|
|
|
|
3878
|
2004
|
50
|
|
|
|
|
if (len == 0) XSRETURN_YES; |
|
3879
|
|
|
|
|
|
|
|
|
3880
|
2004
|
50
|
|
|
|
|
if (items == 2) { |
|
3881
|
2004
|
|
|
|
|
|
NV target = SvNV(ST(1)); |
|
3882
|
11020
|
100
|
|
|
|
|
for (i = 0; i < len; i++) { |
|
3883
|
10018
|
|
|
|
|
|
SV **elem = av_fetch(av, i, 0); |
|
3884
|
10018
|
50
|
|
|
|
|
if (!elem || SvNV(*elem) == target) { |
|
|
|
100
|
|
|
|
|
|
|
3885
|
1002
|
|
|
|
|
|
XSRETURN_NO; |
|
3886
|
|
|
|
|
|
|
} |
|
3887
|
|
|
|
|
|
|
} |
|
3888
|
|
|
|
|
|
|
} else { |
|
3889
|
0
|
|
|
|
|
|
char *key = SvPV_nolen(ST(1)); |
|
3890
|
0
|
|
|
|
|
|
NV target = SvNV(ST(2)); |
|
3891
|
0
|
0
|
|
|
|
|
for (i = 0; i < len; i++) { |
|
3892
|
0
|
|
|
|
|
|
SV **elem = av_fetch(av, i, 0); |
|
3893
|
0
|
0
|
|
|
|
|
if (!elem || !SvROK(*elem) || SvTYPE(SvRV(*elem)) != SVt_PVHV) { |
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
3894
|
0
|
|
|
|
|
|
XSRETURN_NO; |
|
3895
|
|
|
|
|
|
|
} |
|
3896
|
0
|
|
|
|
|
|
HV *hv = (HV *)SvRV(*elem); |
|
3897
|
0
|
|
|
|
|
|
SV **val = hv_fetch(hv, key, strlen(key), 0); |
|
3898
|
0
|
0
|
|
|
|
|
if (!val || SvNV(*val) == target) { |
|
|
|
0
|
|
|
|
|
|
|
3899
|
0
|
|
|
|
|
|
XSRETURN_NO; |
|
3900
|
|
|
|
|
|
|
} |
|
3901
|
|
|
|
|
|
|
} |
|
3902
|
|
|
|
|
|
|
} |
|
3903
|
|
|
|
|
|
|
|
|
3904
|
1002
|
|
|
|
|
|
XSRETURN_YES; |
|
3905
|
|
|
|
|
|
|
} |
|
3906
|
|
|
|
|
|
|
|
|
3907
|
|
|
|
|
|
|
/* none_gt(\@array, $threshold) or none_gt(\@array, $key, $threshold) |
|
3908
|
|
|
|
|
|
|
true if no element > threshold, pure C */ |
|
3909
|
3011
|
|
|
|
|
|
XS_INTERNAL(xs_none_gt) { |
|
3910
|
3011
|
|
|
|
|
|
dXSARGS; |
|
3911
|
3011
|
50
|
|
|
|
|
if (items < 2 || items > 3) croak("Usage: Func::Util::none_gt(\\@array, $threshold) or none_gt(\\@array, $key, $threshold)"); |
|
|
|
50
|
|
|
|
|
|
|
3912
|
|
|
|
|
|
|
|
|
3913
|
3011
|
|
|
|
|
|
SV *aref = ST(0); |
|
3914
|
3011
|
50
|
|
|
|
|
if (!SvROK(aref) || SvTYPE(SvRV(aref)) != SVt_PVAV) { |
|
|
|
50
|
|
|
|
|
|
|
3915
|
0
|
|
|
|
|
|
croak("Func::Util::none_gt: first argument must be an arrayref"); |
|
3916
|
|
|
|
|
|
|
} |
|
3917
|
|
|
|
|
|
|
|
|
3918
|
3011
|
|
|
|
|
|
AV *av = (AV *)SvRV(aref); |
|
3919
|
3011
|
|
|
|
|
|
SSize_t len = av_len(av) + 1; |
|
3920
|
|
|
|
|
|
|
SSize_t i; |
|
3921
|
|
|
|
|
|
|
|
|
3922
|
3011
|
100
|
|
|
|
|
if (items == 2) { |
|
3923
|
2011
|
|
|
|
|
|
NV threshold = SvNV(ST(1)); |
|
3924
|
12046
|
100
|
|
|
|
|
for (i = 0; i < len; i++) { |
|
3925
|
11039
|
|
|
|
|
|
SV **elem = av_fetch(av, i, 0); |
|
3926
|
11039
|
50
|
|
|
|
|
if (elem && SvNV(*elem) > threshold) { |
|
|
|
100
|
|
|
|
|
|
|
3927
|
1004
|
|
|
|
|
|
XSRETURN_NO; |
|
3928
|
|
|
|
|
|
|
} |
|
3929
|
|
|
|
|
|
|
} |
|
3930
|
|
|
|
|
|
|
} else { |
|
3931
|
1000
|
|
|
|
|
|
char *key = SvPV_nolen(ST(1)); |
|
3932
|
1000
|
|
|
|
|
|
NV threshold = SvNV(ST(2)); |
|
3933
|
5000
|
100
|
|
|
|
|
for (i = 0; i < len; i++) { |
|
3934
|
4000
|
|
|
|
|
|
SV **elem = av_fetch(av, i, 0); |
|
3935
|
4000
|
50
|
|
|
|
|
if (elem && SvROK(*elem) && SvTYPE(SvRV(*elem)) == SVt_PVHV) { |
|
|
|
50
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
3936
|
4000
|
|
|
|
|
|
HV *hv = (HV *)SvRV(*elem); |
|
3937
|
4000
|
|
|
|
|
|
SV **val = hv_fetch(hv, key, strlen(key), 0); |
|
3938
|
4000
|
50
|
|
|
|
|
if (val && SvNV(*val) > threshold) { |
|
|
|
50
|
|
|
|
|
|
|
3939
|
0
|
|
|
|
|
|
XSRETURN_NO; |
|
3940
|
|
|
|
|
|
|
} |
|
3941
|
|
|
|
|
|
|
} |
|
3942
|
|
|
|
|
|
|
} |
|
3943
|
|
|
|
|
|
|
} |
|
3944
|
|
|
|
|
|
|
|
|
3945
|
2007
|
|
|
|
|
|
XSRETURN_YES; |
|
3946
|
|
|
|
|
|
|
} |
|
3947
|
|
|
|
|
|
|
|
|
3948
|
|
|
|
|
|
|
/* none_lt(\@array, $threshold) or none_lt(\@array, $key, $threshold) */ |
|
3949
|
3010
|
|
|
|
|
|
XS_INTERNAL(xs_none_lt) { |
|
3950
|
3010
|
|
|
|
|
|
dXSARGS; |
|
3951
|
3010
|
50
|
|
|
|
|
if (items < 2 || items > 3) croak("Usage: Func::Util::none_lt(\\@array, $threshold) or none_lt(\\@array, $key, $threshold)"); |
|
|
|
50
|
|
|
|
|
|
|
3952
|
|
|
|
|
|
|
|
|
3953
|
3010
|
|
|
|
|
|
SV *aref = ST(0); |
|
3954
|
3010
|
50
|
|
|
|
|
if (!SvROK(aref) || SvTYPE(SvRV(aref)) != SVt_PVAV) { |
|
|
|
50
|
|
|
|
|
|
|
3955
|
0
|
|
|
|
|
|
croak("Func::Util::none_lt: first argument must be an arrayref"); |
|
3956
|
|
|
|
|
|
|
} |
|
3957
|
|
|
|
|
|
|
|
|
3958
|
3010
|
|
|
|
|
|
AV *av = (AV *)SvRV(aref); |
|
3959
|
3010
|
|
|
|
|
|
SSize_t len = av_len(av) + 1; |
|
3960
|
|
|
|
|
|
|
SSize_t i; |
|
3961
|
|
|
|
|
|
|
|
|
3962
|
3010
|
100
|
|
|
|
|
if (items == 2) { |
|
3963
|
2008
|
|
|
|
|
|
NV threshold = SvNV(ST(1)); |
|
3964
|
9036
|
100
|
|
|
|
|
for (i = 0; i < len; i++) { |
|
3965
|
8031
|
|
|
|
|
|
SV **elem = av_fetch(av, i, 0); |
|
3966
|
8031
|
50
|
|
|
|
|
if (elem && SvNV(*elem) < threshold) { |
|
|
|
100
|
|
|
|
|
|
|
3967
|
1003
|
|
|
|
|
|
XSRETURN_NO; |
|
3968
|
|
|
|
|
|
|
} |
|
3969
|
|
|
|
|
|
|
} |
|
3970
|
|
|
|
|
|
|
} else { |
|
3971
|
1002
|
|
|
|
|
|
char *key = SvPV_nolen(ST(1)); |
|
3972
|
1002
|
|
|
|
|
|
NV threshold = SvNV(ST(2)); |
|
3973
|
5004
|
100
|
|
|
|
|
for (i = 0; i < len; i++) { |
|
3974
|
4003
|
|
|
|
|
|
SV **elem = av_fetch(av, i, 0); |
|
3975
|
4003
|
50
|
|
|
|
|
if (elem && SvROK(*elem) && SvTYPE(SvRV(*elem)) == SVt_PVHV) { |
|
|
|
50
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
3976
|
4003
|
|
|
|
|
|
HV *hv = (HV *)SvRV(*elem); |
|
3977
|
4003
|
|
|
|
|
|
SV **val = hv_fetch(hv, key, strlen(key), 0); |
|
3978
|
4003
|
50
|
|
|
|
|
if (val && SvNV(*val) < threshold) { |
|
|
|
100
|
|
|
|
|
|
|
3979
|
1
|
|
|
|
|
|
XSRETURN_NO; |
|
3980
|
|
|
|
|
|
|
} |
|
3981
|
|
|
|
|
|
|
} |
|
3982
|
|
|
|
|
|
|
} |
|
3983
|
|
|
|
|
|
|
} |
|
3984
|
|
|
|
|
|
|
|
|
3985
|
2006
|
|
|
|
|
|
XSRETURN_YES; |
|
3986
|
|
|
|
|
|
|
} |
|
3987
|
|
|
|
|
|
|
|
|
3988
|
|
|
|
|
|
|
/* none_ge(\@array, $threshold) or none_ge(\@array, $key, $threshold) */ |
|
3989
|
2004
|
|
|
|
|
|
XS_INTERNAL(xs_none_ge) { |
|
3990
|
2004
|
|
|
|
|
|
dXSARGS; |
|
3991
|
2004
|
50
|
|
|
|
|
if (items < 2 || items > 3) croak("Usage: Func::Util::none_ge(\\@array, $threshold) or none_ge(\\@array, $key, $threshold)"); |
|
|
|
50
|
|
|
|
|
|
|
3992
|
|
|
|
|
|
|
|
|
3993
|
2004
|
|
|
|
|
|
SV *aref = ST(0); |
|
3994
|
2004
|
50
|
|
|
|
|
if (!SvROK(aref) || SvTYPE(SvRV(aref)) != SVt_PVAV) { |
|
|
|
50
|
|
|
|
|
|
|
3995
|
0
|
|
|
|
|
|
croak("Func::Util::none_ge: first argument must be an arrayref"); |
|
3996
|
|
|
|
|
|
|
} |
|
3997
|
|
|
|
|
|
|
|
|
3998
|
2004
|
|
|
|
|
|
AV *av = (AV *)SvRV(aref); |
|
3999
|
2004
|
|
|
|
|
|
SSize_t len = av_len(av) + 1; |
|
4000
|
|
|
|
|
|
|
SSize_t i; |
|
4001
|
|
|
|
|
|
|
|
|
4002
|
2004
|
50
|
|
|
|
|
if (items == 2) { |
|
4003
|
2004
|
|
|
|
|
|
NV threshold = SvNV(ST(1)); |
|
4004
|
9025
|
100
|
|
|
|
|
for (i = 0; i < len; i++) { |
|
4005
|
8023
|
|
|
|
|
|
SV **elem = av_fetch(av, i, 0); |
|
4006
|
8023
|
50
|
|
|
|
|
if (elem && SvNV(*elem) >= threshold) { |
|
|
|
100
|
|
|
|
|
|
|
4007
|
1002
|
|
|
|
|
|
XSRETURN_NO; |
|
4008
|
|
|
|
|
|
|
} |
|
4009
|
|
|
|
|
|
|
} |
|
4010
|
|
|
|
|
|
|
} else { |
|
4011
|
0
|
|
|
|
|
|
char *key = SvPV_nolen(ST(1)); |
|
4012
|
0
|
|
|
|
|
|
NV threshold = SvNV(ST(2)); |
|
4013
|
0
|
0
|
|
|
|
|
for (i = 0; i < len; i++) { |
|
4014
|
0
|
|
|
|
|
|
SV **elem = av_fetch(av, i, 0); |
|
4015
|
0
|
0
|
|
|
|
|
if (elem && SvROK(*elem) && SvTYPE(SvRV(*elem)) == SVt_PVHV) { |
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
4016
|
0
|
|
|
|
|
|
HV *hv = (HV *)SvRV(*elem); |
|
4017
|
0
|
|
|
|
|
|
SV **val = hv_fetch(hv, key, strlen(key), 0); |
|
4018
|
0
|
0
|
|
|
|
|
if (val && SvNV(*val) >= threshold) { |
|
|
|
0
|
|
|
|
|
|
|
4019
|
0
|
|
|
|
|
|
XSRETURN_NO; |
|
4020
|
|
|
|
|
|
|
} |
|
4021
|
|
|
|
|
|
|
} |
|
4022
|
|
|
|
|
|
|
} |
|
4023
|
|
|
|
|
|
|
} |
|
4024
|
|
|
|
|
|
|
|
|
4025
|
1002
|
|
|
|
|
|
XSRETURN_YES; |
|
4026
|
|
|
|
|
|
|
} |
|
4027
|
|
|
|
|
|
|
|
|
4028
|
|
|
|
|
|
|
/* none_le(\@array, $threshold) or none_le(\@array, $key, $threshold) */ |
|
4029
|
2004
|
|
|
|
|
|
XS_INTERNAL(xs_none_le) { |
|
4030
|
2004
|
|
|
|
|
|
dXSARGS; |
|
4031
|
2004
|
50
|
|
|
|
|
if (items < 2 || items > 3) croak("Usage: Func::Util::none_le(\\@array, $threshold) or none_le(\\@array, $key, $threshold)"); |
|
|
|
50
|
|
|
|
|
|
|
4032
|
|
|
|
|
|
|
|
|
4033
|
2004
|
|
|
|
|
|
SV *aref = ST(0); |
|
4034
|
2004
|
50
|
|
|
|
|
if (!SvROK(aref) || SvTYPE(SvRV(aref)) != SVt_PVAV) { |
|
|
|
50
|
|
|
|
|
|
|
4035
|
0
|
|
|
|
|
|
croak("Func::Util::none_le: first argument must be an arrayref"); |
|
4036
|
|
|
|
|
|
|
} |
|
4037
|
|
|
|
|
|
|
|
|
4038
|
2004
|
|
|
|
|
|
AV *av = (AV *)SvRV(aref); |
|
4039
|
2004
|
|
|
|
|
|
SSize_t len = av_len(av) + 1; |
|
4040
|
|
|
|
|
|
|
SSize_t i; |
|
4041
|
|
|
|
|
|
|
|
|
4042
|
2004
|
50
|
|
|
|
|
if (items == 2) { |
|
4043
|
2004
|
|
|
|
|
|
NV threshold = SvNV(ST(1)); |
|
4044
|
9017
|
100
|
|
|
|
|
for (i = 0; i < len; i++) { |
|
4045
|
8015
|
|
|
|
|
|
SV **elem = av_fetch(av, i, 0); |
|
4046
|
8015
|
50
|
|
|
|
|
if (elem && SvNV(*elem) <= threshold) { |
|
|
|
100
|
|
|
|
|
|
|
4047
|
1002
|
|
|
|
|
|
XSRETURN_NO; |
|
4048
|
|
|
|
|
|
|
} |
|
4049
|
|
|
|
|
|
|
} |
|
4050
|
|
|
|
|
|
|
} else { |
|
4051
|
0
|
|
|
|
|
|
char *key = SvPV_nolen(ST(1)); |
|
4052
|
0
|
|
|
|
|
|
NV threshold = SvNV(ST(2)); |
|
4053
|
0
|
0
|
|
|
|
|
for (i = 0; i < len; i++) { |
|
4054
|
0
|
|
|
|
|
|
SV **elem = av_fetch(av, i, 0); |
|
4055
|
0
|
0
|
|
|
|
|
if (elem && SvROK(*elem) && SvTYPE(SvRV(*elem)) == SVt_PVHV) { |
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
4056
|
0
|
|
|
|
|
|
HV *hv = (HV *)SvRV(*elem); |
|
4057
|
0
|
|
|
|
|
|
SV **val = hv_fetch(hv, key, strlen(key), 0); |
|
4058
|
0
|
0
|
|
|
|
|
if (val && SvNV(*val) <= threshold) { |
|
|
|
0
|
|
|
|
|
|
|
4059
|
0
|
|
|
|
|
|
XSRETURN_NO; |
|
4060
|
|
|
|
|
|
|
} |
|
4061
|
|
|
|
|
|
|
} |
|
4062
|
|
|
|
|
|
|
} |
|
4063
|
|
|
|
|
|
|
} |
|
4064
|
|
|
|
|
|
|
|
|
4065
|
1002
|
|
|
|
|
|
XSRETURN_YES; |
|
4066
|
|
|
|
|
|
|
} |
|
4067
|
|
|
|
|
|
|
|
|
4068
|
|
|
|
|
|
|
/* none_eq(\@array, $value) or none_eq(\@array, $key, $value) */ |
|
4069
|
2008
|
|
|
|
|
|
XS_INTERNAL(xs_none_eq) { |
|
4070
|
2008
|
|
|
|
|
|
dXSARGS; |
|
4071
|
2008
|
50
|
|
|
|
|
if (items < 2 || items > 3) croak("Usage: Func::Util::none_eq(\\@array, $value) or none_eq(\\@array, $key, $value)"); |
|
|
|
50
|
|
|
|
|
|
|
4072
|
|
|
|
|
|
|
|
|
4073
|
2008
|
|
|
|
|
|
SV *aref = ST(0); |
|
4074
|
2008
|
50
|
|
|
|
|
if (!SvROK(aref) || SvTYPE(SvRV(aref)) != SVt_PVAV) { |
|
|
|
50
|
|
|
|
|
|
|
4075
|
0
|
|
|
|
|
|
croak("Func::Util::none_eq: first argument must be an arrayref"); |
|
4076
|
|
|
|
|
|
|
} |
|
4077
|
|
|
|
|
|
|
|
|
4078
|
2008
|
|
|
|
|
|
AV *av = (AV *)SvRV(aref); |
|
4079
|
2008
|
|
|
|
|
|
SSize_t len = av_len(av) + 1; |
|
4080
|
|
|
|
|
|
|
SSize_t i; |
|
4081
|
|
|
|
|
|
|
|
|
4082
|
2008
|
50
|
|
|
|
|
if (items == 2) { |
|
4083
|
2008
|
|
|
|
|
|
NV target = SvNV(ST(1)); |
|
4084
|
11037
|
100
|
|
|
|
|
for (i = 0; i < len; i++) { |
|
4085
|
10032
|
|
|
|
|
|
SV **elem = av_fetch(av, i, 0); |
|
4086
|
10032
|
50
|
|
|
|
|
if (elem && SvNV(*elem) == target) { |
|
|
|
100
|
|
|
|
|
|
|
4087
|
1003
|
|
|
|
|
|
XSRETURN_NO; |
|
4088
|
|
|
|
|
|
|
} |
|
4089
|
|
|
|
|
|
|
} |
|
4090
|
|
|
|
|
|
|
} else { |
|
4091
|
0
|
|
|
|
|
|
char *key = SvPV_nolen(ST(1)); |
|
4092
|
0
|
|
|
|
|
|
NV target = SvNV(ST(2)); |
|
4093
|
0
|
0
|
|
|
|
|
for (i = 0; i < len; i++) { |
|
4094
|
0
|
|
|
|
|
|
SV **elem = av_fetch(av, i, 0); |
|
4095
|
0
|
0
|
|
|
|
|
if (elem && SvROK(*elem) && SvTYPE(SvRV(*elem)) == SVt_PVHV) { |
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
4096
|
0
|
|
|
|
|
|
HV *hv = (HV *)SvRV(*elem); |
|
4097
|
0
|
|
|
|
|
|
SV **val = hv_fetch(hv, key, strlen(key), 0); |
|
4098
|
0
|
0
|
|
|
|
|
if (val && SvNV(*val) == target) { |
|
|
|
0
|
|
|
|
|
|
|
4099
|
0
|
|
|
|
|
|
XSRETURN_NO; |
|
4100
|
|
|
|
|
|
|
} |
|
4101
|
|
|
|
|
|
|
} |
|
4102
|
|
|
|
|
|
|
} |
|
4103
|
|
|
|
|
|
|
} |
|
4104
|
|
|
|
|
|
|
|
|
4105
|
1005
|
|
|
|
|
|
XSRETURN_YES; |
|
4106
|
|
|
|
|
|
|
} |
|
4107
|
|
|
|
|
|
|
|
|
4108
|
|
|
|
|
|
|
/* none_ne(\@array, $value) or none_ne(\@array, $key, $value) */ |
|
4109
|
2005
|
|
|
|
|
|
XS_INTERNAL(xs_none_ne) { |
|
4110
|
2005
|
|
|
|
|
|
dXSARGS; |
|
4111
|
2005
|
50
|
|
|
|
|
if (items < 2 || items > 3) croak("Usage: Func::Util::none_ne(\\@array, $value) or none_ne(\\@array, $key, $value)"); |
|
|
|
50
|
|
|
|
|
|
|
4112
|
|
|
|
|
|
|
|
|
4113
|
2005
|
|
|
|
|
|
SV *aref = ST(0); |
|
4114
|
2005
|
50
|
|
|
|
|
if (!SvROK(aref) || SvTYPE(SvRV(aref)) != SVt_PVAV) { |
|
|
|
50
|
|
|
|
|
|
|
4115
|
0
|
|
|
|
|
|
croak("Func::Util::none_ne: first argument must be an arrayref"); |
|
4116
|
|
|
|
|
|
|
} |
|
4117
|
|
|
|
|
|
|
|
|
4118
|
2005
|
|
|
|
|
|
AV *av = (AV *)SvRV(aref); |
|
4119
|
2005
|
|
|
|
|
|
SSize_t len = av_len(av) + 1; |
|
4120
|
|
|
|
|
|
|
SSize_t i; |
|
4121
|
|
|
|
|
|
|
|
|
4122
|
2005
|
50
|
|
|
|
|
if (items == 2) { |
|
4123
|
2005
|
|
|
|
|
|
NV target = SvNV(ST(1)); |
|
4124
|
6015
|
100
|
|
|
|
|
for (i = 0; i < len; i++) { |
|
4125
|
5012
|
|
|
|
|
|
SV **elem = av_fetch(av, i, 0); |
|
4126
|
5012
|
50
|
|
|
|
|
if (elem && SvNV(*elem) != target) { |
|
|
|
100
|
|
|
|
|
|
|
4127
|
1002
|
|
|
|
|
|
XSRETURN_NO; |
|
4128
|
|
|
|
|
|
|
} |
|
4129
|
|
|
|
|
|
|
} |
|
4130
|
|
|
|
|
|
|
} else { |
|
4131
|
0
|
|
|
|
|
|
char *key = SvPV_nolen(ST(1)); |
|
4132
|
0
|
|
|
|
|
|
NV target = SvNV(ST(2)); |
|
4133
|
0
|
0
|
|
|
|
|
for (i = 0; i < len; i++) { |
|
4134
|
0
|
|
|
|
|
|
SV **elem = av_fetch(av, i, 0); |
|
4135
|
0
|
0
|
|
|
|
|
if (elem && SvROK(*elem) && SvTYPE(SvRV(*elem)) == SVt_PVHV) { |
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
4136
|
0
|
|
|
|
|
|
HV *hv = (HV *)SvRV(*elem); |
|
4137
|
0
|
|
|
|
|
|
SV **val = hv_fetch(hv, key, strlen(key), 0); |
|
4138
|
0
|
0
|
|
|
|
|
if (val && SvNV(*val) != target) { |
|
|
|
0
|
|
|
|
|
|
|
4139
|
0
|
|
|
|
|
|
XSRETURN_NO; |
|
4140
|
|
|
|
|
|
|
} |
|
4141
|
|
|
|
|
|
|
} |
|
4142
|
|
|
|
|
|
|
} |
|
4143
|
|
|
|
|
|
|
} |
|
4144
|
|
|
|
|
|
|
|
|
4145
|
1003
|
|
|
|
|
|
XSRETURN_YES; |
|
4146
|
|
|
|
|
|
|
} |
|
4147
|
|
|
|
|
|
|
|
|
4148
|
|
|
|
|
|
|
/* firstr(\&block, \@array) - first with arrayref, no stack flattening */ |
|
4149
|
3012
|
|
|
|
|
|
XS_INTERNAL(xs_firstr) { |
|
4150
|
3012
|
|
|
|
|
|
dXSARGS; |
|
4151
|
3012
|
50
|
|
|
|
|
if (items != 2) croak("Usage: Func::Util::firstr(\\&block, \\@array)"); |
|
4152
|
|
|
|
|
|
|
|
|
4153
|
3012
|
|
|
|
|
|
SV *block = ST(0); |
|
4154
|
3012
|
|
|
|
|
|
SV *aref = ST(1); |
|
4155
|
|
|
|
|
|
|
|
|
4156
|
3012
|
50
|
|
|
|
|
if (!SvROK(block) || SvTYPE(SvRV(block)) != SVt_PVCV) { |
|
|
|
50
|
|
|
|
|
|
|
4157
|
0
|
|
|
|
|
|
croak("Func::Util::firstr: first argument must be a coderef"); |
|
4158
|
|
|
|
|
|
|
} |
|
4159
|
3012
|
50
|
|
|
|
|
if (!SvROK(aref) || SvTYPE(SvRV(aref)) != SVt_PVAV) { |
|
|
|
50
|
|
|
|
|
|
|
4160
|
0
|
|
|
|
|
|
croak("Func::Util::firstr: second argument must be an arrayref"); |
|
4161
|
|
|
|
|
|
|
} |
|
4162
|
|
|
|
|
|
|
|
|
4163
|
3012
|
|
|
|
|
|
CV *block_cv = (CV *)SvRV(block); |
|
4164
|
3012
|
|
|
|
|
|
AV *av = (AV *)SvRV(aref); |
|
4165
|
3012
|
|
|
|
|
|
SSize_t len = av_len(av) + 1; |
|
4166
|
|
|
|
|
|
|
SSize_t i; |
|
4167
|
|
|
|
|
|
|
|
|
4168
|
3012
|
50
|
|
|
|
|
if (len == 0) { |
|
4169
|
0
|
|
|
|
|
|
XSRETURN_UNDEF; |
|
4170
|
|
|
|
|
|
|
} |
|
4171
|
|
|
|
|
|
|
|
|
4172
|
|
|
|
|
|
|
#ifdef dMULTICALL |
|
4173
|
3012
|
50
|
|
|
|
|
if (!CvISXSUB(block_cv)) { |
|
4174
|
|
|
|
|
|
|
dMULTICALL; |
|
4175
|
3012
|
|
|
|
|
|
I32 gimme = G_SCALAR; |
|
4176
|
|
|
|
|
|
|
|
|
4177
|
3012
|
|
|
|
|
|
SAVESPTR(GvSV(PL_defgv)); |
|
4178
|
3012
|
50
|
|
|
|
|
PUSH_MULTICALL(block_cv); |
|
4179
|
|
|
|
|
|
|
|
|
4180
|
11045
|
100
|
|
|
|
|
for (i = 0; i < len; i++) { |
|
4181
|
10045
|
|
|
|
|
|
SV **elem = av_fetch(av, i, 0); |
|
4182
|
10045
|
50
|
|
|
|
|
if (!elem) continue; |
|
4183
|
|
|
|
|
|
|
|
|
4184
|
10045
|
|
|
|
|
|
SV *def_sv = GvSV(PL_defgv) = *elem; |
|
4185
|
10045
|
|
|
|
|
|
SvTEMP_off(def_sv); |
|
4186
|
|
|
|
|
|
|
|
|
4187
|
10045
|
|
|
|
|
|
MULTICALL; |
|
4188
|
|
|
|
|
|
|
|
|
4189
|
10045
|
100
|
|
|
|
|
if (SvTRUE(*PL_stack_sp)) { |
|
4190
|
2012
|
50
|
|
|
|
|
POP_MULTICALL; |
|
4191
|
2012
|
|
|
|
|
|
ST(0) = *elem; |
|
4192
|
2012
|
|
|
|
|
|
XSRETURN(1); |
|
4193
|
|
|
|
|
|
|
} |
|
4194
|
|
|
|
|
|
|
} |
|
4195
|
|
|
|
|
|
|
|
|
4196
|
1000
|
50
|
|
|
|
|
POP_MULTICALL; |
|
4197
|
1000
|
|
|
|
|
|
XSRETURN_UNDEF; |
|
4198
|
|
|
|
|
|
|
} |
|
4199
|
|
|
|
|
|
|
#endif |
|
4200
|
|
|
|
|
|
|
|
|
4201
|
|
|
|
|
|
|
/* Fallback for XS subs */ |
|
4202
|
0
|
0
|
|
|
|
|
for (i = 0; i < len; i++) { |
|
4203
|
0
|
|
|
|
|
|
SV **elem = av_fetch(av, i, 0); |
|
4204
|
0
|
0
|
|
|
|
|
if (!elem) continue; |
|
4205
|
|
|
|
|
|
|
|
|
4206
|
0
|
|
|
|
|
|
dSP; |
|
4207
|
0
|
|
|
|
|
|
GvSV(PL_defgv) = *elem; |
|
4208
|
|
|
|
|
|
|
|
|
4209
|
0
|
0
|
|
|
|
|
PUSHMARK(SP); |
|
4210
|
0
|
|
|
|
|
|
call_sv((SV*)block_cv, G_SCALAR); |
|
4211
|
|
|
|
|
|
|
|
|
4212
|
0
|
0
|
|
|
|
|
if (SvTRUE(*PL_stack_sp)) { |
|
4213
|
0
|
|
|
|
|
|
ST(0) = *elem; |
|
4214
|
0
|
|
|
|
|
|
XSRETURN(1); |
|
4215
|
|
|
|
|
|
|
} |
|
4216
|
|
|
|
|
|
|
} |
|
4217
|
|
|
|
|
|
|
|
|
4218
|
0
|
|
|
|
|
|
XSRETURN_UNDEF; |
|
4219
|
|
|
|
|
|
|
} |
|
4220
|
|
|
|
|
|
|
|
|
4221
|
|
|
|
|
|
|
/* final(\&block, \@array) - last element where block returns true (backwards iteration) */ |
|
4222
|
2014
|
|
|
|
|
|
XS_INTERNAL(xs_final) { |
|
4223
|
2014
|
|
|
|
|
|
dXSARGS; |
|
4224
|
2014
|
50
|
|
|
|
|
if (items != 2) croak("Usage: Func::Util::final(\\&block, \\@array)"); |
|
4225
|
|
|
|
|
|
|
|
|
4226
|
2014
|
|
|
|
|
|
SV *block = ST(0); |
|
4227
|
2014
|
|
|
|
|
|
SV *aref = ST(1); |
|
4228
|
|
|
|
|
|
|
|
|
4229
|
2014
|
50
|
|
|
|
|
if (!SvROK(block) || SvTYPE(SvRV(block)) != SVt_PVCV) { |
|
|
|
50
|
|
|
|
|
|
|
4230
|
0
|
|
|
|
|
|
croak("Func::Util::final: first argument must be a coderef"); |
|
4231
|
|
|
|
|
|
|
} |
|
4232
|
2014
|
50
|
|
|
|
|
if (!SvROK(aref) || SvTYPE(SvRV(aref)) != SVt_PVAV) { |
|
|
|
50
|
|
|
|
|
|
|
4233
|
0
|
|
|
|
|
|
croak("Func::Util::final: second argument must be an arrayref"); |
|
4234
|
|
|
|
|
|
|
} |
|
4235
|
|
|
|
|
|
|
|
|
4236
|
2014
|
|
|
|
|
|
CV *block_cv = (CV *)SvRV(block); |
|
4237
|
2014
|
|
|
|
|
|
AV *av = (AV *)SvRV(aref); |
|
4238
|
2014
|
|
|
|
|
|
SSize_t len = av_len(av) + 1; |
|
4239
|
|
|
|
|
|
|
SSize_t i; |
|
4240
|
|
|
|
|
|
|
|
|
4241
|
2014
|
100
|
|
|
|
|
if (len == 0) { |
|
4242
|
2
|
|
|
|
|
|
XSRETURN_UNDEF; |
|
4243
|
|
|
|
|
|
|
} |
|
4244
|
|
|
|
|
|
|
|
|
4245
|
|
|
|
|
|
|
#ifdef dMULTICALL |
|
4246
|
2012
|
50
|
|
|
|
|
if (!CvISXSUB(block_cv)) { |
|
4247
|
|
|
|
|
|
|
dMULTICALL; |
|
4248
|
2012
|
|
|
|
|
|
I32 gimme = G_SCALAR; |
|
4249
|
|
|
|
|
|
|
|
|
4250
|
2012
|
|
|
|
|
|
SAVESPTR(GvSV(PL_defgv)); |
|
4251
|
2012
|
50
|
|
|
|
|
PUSH_MULTICALL(block_cv); |
|
4252
|
|
|
|
|
|
|
|
|
4253
|
|
|
|
|
|
|
/* Iterate backwards for speed */ |
|
4254
|
9029
|
100
|
|
|
|
|
for (i = len - 1; i >= 0; i--) { |
|
4255
|
8026
|
|
|
|
|
|
SV **elem = av_fetch(av, i, 0); |
|
4256
|
8026
|
50
|
|
|
|
|
if (!elem) continue; |
|
4257
|
|
|
|
|
|
|
|
|
4258
|
8026
|
|
|
|
|
|
SV *def_sv = GvSV(PL_defgv) = *elem; |
|
4259
|
8026
|
|
|
|
|
|
SvTEMP_off(def_sv); |
|
4260
|
|
|
|
|
|
|
|
|
4261
|
8026
|
|
|
|
|
|
MULTICALL; |
|
4262
|
|
|
|
|
|
|
|
|
4263
|
8026
|
100
|
|
|
|
|
if (SvTRUE(*PL_stack_sp)) { |
|
4264
|
1009
|
50
|
|
|
|
|
POP_MULTICALL; |
|
4265
|
1009
|
|
|
|
|
|
ST(0) = *elem; |
|
4266
|
1009
|
|
|
|
|
|
XSRETURN(1); |
|
4267
|
|
|
|
|
|
|
} |
|
4268
|
|
|
|
|
|
|
} |
|
4269
|
|
|
|
|
|
|
|
|
4270
|
1003
|
50
|
|
|
|
|
POP_MULTICALL; |
|
4271
|
1003
|
|
|
|
|
|
XSRETURN_UNDEF; |
|
4272
|
|
|
|
|
|
|
} |
|
4273
|
|
|
|
|
|
|
#endif |
|
4274
|
|
|
|
|
|
|
|
|
4275
|
|
|
|
|
|
|
/* Fallback for XS subs - backwards */ |
|
4276
|
0
|
0
|
|
|
|
|
for (i = len - 1; i >= 0; i--) { |
|
4277
|
0
|
|
|
|
|
|
SV **elem = av_fetch(av, i, 0); |
|
4278
|
0
|
0
|
|
|
|
|
if (!elem) continue; |
|
4279
|
|
|
|
|
|
|
|
|
4280
|
0
|
|
|
|
|
|
dSP; |
|
4281
|
0
|
|
|
|
|
|
GvSV(PL_defgv) = *elem; |
|
4282
|
|
|
|
|
|
|
|
|
4283
|
0
|
0
|
|
|
|
|
PUSHMARK(SP); |
|
4284
|
0
|
|
|
|
|
|
call_sv((SV*)block_cv, G_SCALAR); |
|
4285
|
|
|
|
|
|
|
|
|
4286
|
0
|
0
|
|
|
|
|
if (SvTRUE(*PL_stack_sp)) { |
|
4287
|
0
|
|
|
|
|
|
ST(0) = *elem; |
|
4288
|
0
|
|
|
|
|
|
XSRETURN(1); |
|
4289
|
|
|
|
|
|
|
} |
|
4290
|
|
|
|
|
|
|
} |
|
4291
|
|
|
|
|
|
|
|
|
4292
|
0
|
|
|
|
|
|
XSRETURN_UNDEF; |
|
4293
|
|
|
|
|
|
|
} |
|
4294
|
|
|
|
|
|
|
|
|
4295
|
|
|
|
|
|
|
/* first { block } @list - return first element where block returns true */ |
|
4296
|
13118
|
|
|
|
|
|
XS_INTERNAL(xs_first) { |
|
4297
|
13118
|
|
|
|
|
|
dXSARGS; |
|
4298
|
13118
|
50
|
|
|
|
|
if (items < 1) croak("Usage: Func::Util::first(\\&block, @list)"); |
|
4299
|
|
|
|
|
|
|
|
|
4300
|
13118
|
|
|
|
|
|
SV *block = ST(0); |
|
4301
|
13118
|
50
|
|
|
|
|
if (!SvROK(block) || SvTYPE(SvRV(block)) != SVt_PVCV) { |
|
|
|
50
|
|
|
|
|
|
|
4302
|
0
|
|
|
|
|
|
croak("Func::Util::first: first argument must be a coderef"); |
|
4303
|
|
|
|
|
|
|
} |
|
4304
|
|
|
|
|
|
|
|
|
4305
|
13118
|
|
|
|
|
|
CV *block_cv = (CV *)SvRV(block); |
|
4306
|
|
|
|
|
|
|
/* Store args from stack base before any stack manipulation */ |
|
4307
|
13118
|
|
|
|
|
|
SV **args = &PL_stack_base[ax]; |
|
4308
|
|
|
|
|
|
|
IV index; |
|
4309
|
|
|
|
|
|
|
|
|
4310
|
|
|
|
|
|
|
/* Empty list - return undef */ |
|
4311
|
13118
|
100
|
|
|
|
|
if (items <= 1) { |
|
4312
|
2
|
|
|
|
|
|
XSRETURN_UNDEF; |
|
4313
|
|
|
|
|
|
|
} |
|
4314
|
|
|
|
|
|
|
|
|
4315
|
|
|
|
|
|
|
/* Use MULTICALL for pure Perl subs - much faster than call_sv */ |
|
4316
|
|
|
|
|
|
|
#ifdef dMULTICALL |
|
4317
|
13116
|
50
|
|
|
|
|
if (!CvISXSUB(block_cv)) { |
|
4318
|
|
|
|
|
|
|
dMULTICALL; |
|
4319
|
13116
|
|
|
|
|
|
I32 gimme = G_SCALAR; |
|
4320
|
|
|
|
|
|
|
|
|
4321
|
13116
|
|
|
|
|
|
SAVESPTR(GvSV(PL_defgv)); |
|
4322
|
13116
|
50
|
|
|
|
|
PUSH_MULTICALL(block_cv); |
|
4323
|
|
|
|
|
|
|
|
|
4324
|
33340
|
100
|
|
|
|
|
for (index = 1; index < items; index++) { |
|
4325
|
33336
|
|
|
|
|
|
SV *def_sv = GvSV(PL_defgv) = args[index]; |
|
4326
|
33336
|
|
|
|
|
|
SvTEMP_off(def_sv); |
|
4327
|
|
|
|
|
|
|
|
|
4328
|
33336
|
|
|
|
|
|
MULTICALL; |
|
4329
|
|
|
|
|
|
|
|
|
4330
|
33336
|
100
|
|
|
|
|
if (SvTRUE(*PL_stack_sp)) { |
|
4331
|
13112
|
50
|
|
|
|
|
POP_MULTICALL; |
|
4332
|
13112
|
|
|
|
|
|
ST(0) = ST(index); |
|
4333
|
13112
|
|
|
|
|
|
XSRETURN(1); |
|
4334
|
|
|
|
|
|
|
} |
|
4335
|
|
|
|
|
|
|
} |
|
4336
|
|
|
|
|
|
|
|
|
4337
|
4
|
50
|
|
|
|
|
POP_MULTICALL; |
|
4338
|
4
|
|
|
|
|
|
XSRETURN_UNDEF; |
|
4339
|
|
|
|
|
|
|
} |
|
4340
|
|
|
|
|
|
|
#endif |
|
4341
|
|
|
|
|
|
|
|
|
4342
|
|
|
|
|
|
|
/* Fallback for XS subs */ |
|
4343
|
0
|
0
|
|
|
|
|
for (index = 1; index < items; index++) { |
|
4344
|
0
|
|
|
|
|
|
dSP; |
|
4345
|
0
|
|
|
|
|
|
GvSV(PL_defgv) = args[index]; |
|
4346
|
|
|
|
|
|
|
|
|
4347
|
0
|
0
|
|
|
|
|
PUSHMARK(SP); |
|
4348
|
0
|
|
|
|
|
|
call_sv((SV*)block_cv, G_SCALAR); |
|
4349
|
|
|
|
|
|
|
|
|
4350
|
0
|
0
|
|
|
|
|
if (SvTRUE(*PL_stack_sp)) { |
|
4351
|
0
|
|
|
|
|
|
ST(0) = ST(index); |
|
4352
|
0
|
|
|
|
|
|
XSRETURN(1); |
|
4353
|
|
|
|
|
|
|
} |
|
4354
|
|
|
|
|
|
|
} |
|
4355
|
|
|
|
|
|
|
|
|
4356
|
0
|
|
|
|
|
|
XSRETURN_UNDEF; |
|
4357
|
|
|
|
|
|
|
} |
|
4358
|
|
|
|
|
|
|
|
|
4359
|
|
|
|
|
|
|
/* any { block } @list - return true if any element matches */ |
|
4360
|
13126
|
|
|
|
|
|
XS_INTERNAL(xs_any) { |
|
4361
|
13126
|
|
|
|
|
|
dXSARGS; |
|
4362
|
13126
|
50
|
|
|
|
|
if (items < 1) croak("Usage: Func::Util::any(\\&block, @list)"); |
|
4363
|
|
|
|
|
|
|
|
|
4364
|
13126
|
|
|
|
|
|
SV *block = ST(0); |
|
4365
|
13126
|
50
|
|
|
|
|
if (!SvROK(block) || SvTYPE(SvRV(block)) != SVt_PVCV) { |
|
|
|
50
|
|
|
|
|
|
|
4366
|
0
|
|
|
|
|
|
croak("Func::Util::any: first argument must be a coderef"); |
|
4367
|
|
|
|
|
|
|
} |
|
4368
|
|
|
|
|
|
|
|
|
4369
|
13126
|
|
|
|
|
|
CV *block_cv = (CV *)SvRV(block); |
|
4370
|
13126
|
|
|
|
|
|
SV **args = &PL_stack_base[ax]; |
|
4371
|
|
|
|
|
|
|
IV index; |
|
4372
|
|
|
|
|
|
|
|
|
4373
|
|
|
|
|
|
|
/* Empty list returns false */ |
|
4374
|
13126
|
100
|
|
|
|
|
if (items <= 1) { |
|
4375
|
2
|
|
|
|
|
|
XSRETURN_NO; |
|
4376
|
|
|
|
|
|
|
} |
|
4377
|
|
|
|
|
|
|
|
|
4378
|
|
|
|
|
|
|
#ifdef dMULTICALL |
|
4379
|
13124
|
50
|
|
|
|
|
if (!CvISXSUB(block_cv)) { |
|
4380
|
|
|
|
|
|
|
dMULTICALL; |
|
4381
|
13124
|
|
|
|
|
|
I32 gimme = G_SCALAR; |
|
4382
|
|
|
|
|
|
|
|
|
4383
|
13124
|
|
|
|
|
|
SAVESPTR(GvSV(PL_defgv)); |
|
4384
|
13124
|
50
|
|
|
|
|
PUSH_MULTICALL(block_cv); |
|
4385
|
|
|
|
|
|
|
|
|
4386
|
33345
|
100
|
|
|
|
|
for (index = 1; index < items; index++) { |
|
4387
|
33341
|
|
|
|
|
|
SV *def_sv = GvSV(PL_defgv) = args[index]; |
|
4388
|
33341
|
|
|
|
|
|
SvTEMP_off(def_sv); |
|
4389
|
|
|
|
|
|
|
|
|
4390
|
33341
|
|
|
|
|
|
MULTICALL; |
|
4391
|
|
|
|
|
|
|
|
|
4392
|
33341
|
100
|
|
|
|
|
if (SvTRUE(*PL_stack_sp)) { |
|
4393
|
13120
|
50
|
|
|
|
|
POP_MULTICALL; |
|
4394
|
13120
|
|
|
|
|
|
XSRETURN_YES; |
|
4395
|
|
|
|
|
|
|
} |
|
4396
|
|
|
|
|
|
|
} |
|
4397
|
|
|
|
|
|
|
|
|
4398
|
4
|
50
|
|
|
|
|
POP_MULTICALL; |
|
4399
|
4
|
|
|
|
|
|
XSRETURN_NO; |
|
4400
|
|
|
|
|
|
|
} |
|
4401
|
|
|
|
|
|
|
#endif |
|
4402
|
|
|
|
|
|
|
|
|
4403
|
0
|
0
|
|
|
|
|
for (index = 1; index < items; index++) { |
|
4404
|
0
|
|
|
|
|
|
dSP; |
|
4405
|
0
|
|
|
|
|
|
GvSV(PL_defgv) = args[index]; |
|
4406
|
|
|
|
|
|
|
|
|
4407
|
0
|
0
|
|
|
|
|
PUSHMARK(SP); |
|
4408
|
0
|
|
|
|
|
|
call_sv((SV*)block_cv, G_SCALAR); |
|
4409
|
|
|
|
|
|
|
|
|
4410
|
0
|
0
|
|
|
|
|
if (SvTRUE(*PL_stack_sp)) { |
|
4411
|
0
|
|
|
|
|
|
XSRETURN_YES; |
|
4412
|
|
|
|
|
|
|
} |
|
4413
|
|
|
|
|
|
|
} |
|
4414
|
|
|
|
|
|
|
|
|
4415
|
0
|
|
|
|
|
|
XSRETURN_NO; |
|
4416
|
|
|
|
|
|
|
} |
|
4417
|
|
|
|
|
|
|
|
|
4418
|
|
|
|
|
|
|
/* all { block } @list - return true if all elements match */ |
|
4419
|
13116
|
|
|
|
|
|
XS_INTERNAL(xs_all) { |
|
4420
|
13116
|
|
|
|
|
|
dXSARGS; |
|
4421
|
13116
|
50
|
|
|
|
|
if (items < 1) croak("Usage: Func::Util::all(\\&block, @list)"); |
|
4422
|
|
|
|
|
|
|
|
|
4423
|
13116
|
|
|
|
|
|
SV *block = ST(0); |
|
4424
|
13116
|
50
|
|
|
|
|
if (!SvROK(block) || SvTYPE(SvRV(block)) != SVt_PVCV) { |
|
|
|
50
|
|
|
|
|
|
|
4425
|
0
|
|
|
|
|
|
croak("Func::Util::all: first argument must be a coderef"); |
|
4426
|
|
|
|
|
|
|
} |
|
4427
|
|
|
|
|
|
|
|
|
4428
|
13116
|
|
|
|
|
|
CV *block_cv = (CV *)SvRV(block); |
|
4429
|
13116
|
|
|
|
|
|
SV **args = &PL_stack_base[ax]; |
|
4430
|
|
|
|
|
|
|
IV index; |
|
4431
|
|
|
|
|
|
|
|
|
4432
|
|
|
|
|
|
|
/* Empty list returns true (vacuous truth) */ |
|
4433
|
13116
|
100
|
|
|
|
|
if (items <= 1) { |
|
4434
|
2
|
|
|
|
|
|
XSRETURN_YES; |
|
4435
|
|
|
|
|
|
|
} |
|
4436
|
|
|
|
|
|
|
|
|
4437
|
|
|
|
|
|
|
#ifdef dMULTICALL |
|
4438
|
13114
|
50
|
|
|
|
|
if (!CvISXSUB(block_cv)) { |
|
4439
|
|
|
|
|
|
|
dMULTICALL; |
|
4440
|
13114
|
|
|
|
|
|
I32 gimme = G_SCALAR; |
|
4441
|
|
|
|
|
|
|
|
|
4442
|
13114
|
|
|
|
|
|
SAVESPTR(GvSV(PL_defgv)); |
|
4443
|
13114
|
50
|
|
|
|
|
PUSH_MULTICALL(block_cv); |
|
4444
|
|
|
|
|
|
|
|
|
4445
|
66645
|
100
|
|
|
|
|
for (index = 1; index < items; index++) { |
|
4446
|
53537
|
|
|
|
|
|
SV *def_sv = GvSV(PL_defgv) = args[index]; |
|
4447
|
53537
|
|
|
|
|
|
SvTEMP_off(def_sv); |
|
4448
|
|
|
|
|
|
|
|
|
4449
|
53537
|
|
|
|
|
|
MULTICALL; |
|
4450
|
|
|
|
|
|
|
|
|
4451
|
53537
|
100
|
|
|
|
|
if (!SvTRUE(*PL_stack_sp)) { |
|
4452
|
6
|
50
|
|
|
|
|
POP_MULTICALL; |
|
4453
|
6
|
|
|
|
|
|
XSRETURN_NO; |
|
4454
|
|
|
|
|
|
|
} |
|
4455
|
|
|
|
|
|
|
} |
|
4456
|
|
|
|
|
|
|
|
|
4457
|
13108
|
50
|
|
|
|
|
POP_MULTICALL; |
|
4458
|
13108
|
|
|
|
|
|
XSRETURN_YES; |
|
4459
|
|
|
|
|
|
|
} |
|
4460
|
|
|
|
|
|
|
#endif |
|
4461
|
|
|
|
|
|
|
|
|
4462
|
0
|
0
|
|
|
|
|
for (index = 1; index < items; index++) { |
|
4463
|
0
|
|
|
|
|
|
dSP; |
|
4464
|
0
|
|
|
|
|
|
GvSV(PL_defgv) = args[index]; |
|
4465
|
|
|
|
|
|
|
|
|
4466
|
0
|
0
|
|
|
|
|
PUSHMARK(SP); |
|
4467
|
0
|
|
|
|
|
|
call_sv((SV*)block_cv, G_SCALAR); |
|
4468
|
|
|
|
|
|
|
|
|
4469
|
0
|
0
|
|
|
|
|
if (!SvTRUE(*PL_stack_sp)) { |
|
4470
|
0
|
|
|
|
|
|
XSRETURN_NO; |
|
4471
|
|
|
|
|
|
|
} |
|
4472
|
|
|
|
|
|
|
} |
|
4473
|
|
|
|
|
|
|
|
|
4474
|
0
|
|
|
|
|
|
XSRETURN_YES; |
|
4475
|
|
|
|
|
|
|
} |
|
4476
|
|
|
|
|
|
|
|
|
4477
|
|
|
|
|
|
|
/* none { block } @list - return true if no elements match */ |
|
4478
|
13114
|
|
|
|
|
|
XS_INTERNAL(xs_none) { |
|
4479
|
13114
|
|
|
|
|
|
dXSARGS; |
|
4480
|
13114
|
50
|
|
|
|
|
if (items < 1) croak("Usage: Func::Util::none(\\&block, @list)"); |
|
4481
|
|
|
|
|
|
|
|
|
4482
|
13114
|
|
|
|
|
|
SV *block = ST(0); |
|
4483
|
13114
|
50
|
|
|
|
|
if (!SvROK(block) || SvTYPE(SvRV(block)) != SVt_PVCV) { |
|
|
|
50
|
|
|
|
|
|
|
4484
|
0
|
|
|
|
|
|
croak("Func::Util::none: first argument must be a coderef"); |
|
4485
|
|
|
|
|
|
|
} |
|
4486
|
|
|
|
|
|
|
|
|
4487
|
13114
|
|
|
|
|
|
CV *block_cv = (CV *)SvRV(block); |
|
4488
|
13114
|
|
|
|
|
|
SV **args = &PL_stack_base[ax]; |
|
4489
|
|
|
|
|
|
|
IV index; |
|
4490
|
|
|
|
|
|
|
|
|
4491
|
|
|
|
|
|
|
/* Empty list returns true (no elements match = vacuous truth) */ |
|
4492
|
13114
|
100
|
|
|
|
|
if (items <= 1) { |
|
4493
|
2
|
|
|
|
|
|
XSRETURN_YES; |
|
4494
|
|
|
|
|
|
|
} |
|
4495
|
|
|
|
|
|
|
|
|
4496
|
|
|
|
|
|
|
#ifdef dMULTICALL |
|
4497
|
13112
|
50
|
|
|
|
|
if (!CvISXSUB(block_cv)) { |
|
4498
|
|
|
|
|
|
|
dMULTICALL; |
|
4499
|
13112
|
|
|
|
|
|
I32 gimme = G_SCALAR; |
|
4500
|
|
|
|
|
|
|
|
|
4501
|
13112
|
|
|
|
|
|
SAVESPTR(GvSV(PL_defgv)); |
|
4502
|
13112
|
50
|
|
|
|
|
PUSH_MULTICALL(block_cv); |
|
4503
|
|
|
|
|
|
|
|
|
4504
|
63638
|
100
|
|
|
|
|
for (index = 1; index < items; index++) { |
|
4505
|
53532
|
|
|
|
|
|
SV *def_sv = GvSV(PL_defgv) = args[index]; |
|
4506
|
53532
|
|
|
|
|
|
SvTEMP_off(def_sv); |
|
4507
|
|
|
|
|
|
|
|
|
4508
|
53532
|
|
|
|
|
|
MULTICALL; |
|
4509
|
|
|
|
|
|
|
|
|
4510
|
53532
|
100
|
|
|
|
|
if (SvTRUE(*PL_stack_sp)) { |
|
4511
|
3006
|
50
|
|
|
|
|
POP_MULTICALL; |
|
4512
|
3006
|
|
|
|
|
|
XSRETURN_NO; |
|
4513
|
|
|
|
|
|
|
} |
|
4514
|
|
|
|
|
|
|
} |
|
4515
|
|
|
|
|
|
|
|
|
4516
|
10106
|
50
|
|
|
|
|
POP_MULTICALL; |
|
4517
|
10106
|
|
|
|
|
|
XSRETURN_YES; |
|
4518
|
|
|
|
|
|
|
} |
|
4519
|
|
|
|
|
|
|
#endif |
|
4520
|
|
|
|
|
|
|
|
|
4521
|
0
|
0
|
|
|
|
|
for (index = 1; index < items; index++) { |
|
4522
|
0
|
|
|
|
|
|
dSP; |
|
4523
|
0
|
|
|
|
|
|
GvSV(PL_defgv) = args[index]; |
|
4524
|
|
|
|
|
|
|
|
|
4525
|
0
|
0
|
|
|
|
|
PUSHMARK(SP); |
|
4526
|
0
|
|
|
|
|
|
call_sv((SV*)block_cv, G_SCALAR); |
|
4527
|
|
|
|
|
|
|
|
|
4528
|
0
|
0
|
|
|
|
|
if (SvTRUE(*PL_stack_sp)) { |
|
4529
|
0
|
|
|
|
|
|
XSRETURN_NO; |
|
4530
|
|
|
|
|
|
|
} |
|
4531
|
|
|
|
|
|
|
} |
|
4532
|
|
|
|
|
|
|
|
|
4533
|
0
|
|
|
|
|
|
XSRETURN_YES; |
|
4534
|
|
|
|
|
|
|
} |
|
4535
|
|
|
|
|
|
|
|
|
4536
|
|
|
|
|
|
|
/* ============================================ |
|
4537
|
|
|
|
|
|
|
Experimental: Inlined MULTICALL versions for benchmarking |
|
4538
|
|
|
|
|
|
|
|
|
4539
|
|
|
|
|
|
|
These versions inline the runops loop to skip the CALLRUNOPS |
|
4540
|
|
|
|
|
|
|
function call overhead. For testing only. |
|
4541
|
|
|
|
|
|
|
============================================ */ |
|
4542
|
|
|
|
|
|
|
|
|
4543
|
|
|
|
|
|
|
/* first_inline - experimental version with inlined runops loop |
|
4544
|
|
|
|
|
|
|
* Requires MULTICALL API (5.11+) */ |
|
4545
|
|
|
|
|
|
|
#ifdef dMULTICALL |
|
4546
|
6
|
|
|
|
|
|
XS_INTERNAL(xs_first_inline) { |
|
4547
|
6
|
|
|
|
|
|
dXSARGS; |
|
4548
|
6
|
50
|
|
|
|
|
if (items < 1) croak("Usage: Func::Util::first_inline(\\&block, @list)"); |
|
4549
|
|
|
|
|
|
|
|
|
4550
|
6
|
|
|
|
|
|
SV *block = ST(0); |
|
4551
|
6
|
50
|
|
|
|
|
if (!SvROK(block) || SvTYPE(SvRV(block)) != SVt_PVCV) { |
|
|
|
50
|
|
|
|
|
|
|
4552
|
0
|
|
|
|
|
|
croak("Func::Util::first_inline: first argument must be a coderef"); |
|
4553
|
|
|
|
|
|
|
} |
|
4554
|
|
|
|
|
|
|
|
|
4555
|
6
|
|
|
|
|
|
CV *the_cv = (CV *)SvRV(block); |
|
4556
|
|
|
|
|
|
|
|
|
4557
|
6
|
100
|
|
|
|
|
if (items == 1) { |
|
4558
|
1
|
|
|
|
|
|
XSRETURN_UNDEF; |
|
4559
|
|
|
|
|
|
|
} |
|
4560
|
|
|
|
|
|
|
|
|
4561
|
|
|
|
|
|
|
/* Only works with pure Perl subs */ |
|
4562
|
5
|
50
|
|
|
|
|
if (CvISXSUB(the_cv)) { |
|
4563
|
0
|
|
|
|
|
|
croak("Func::Util::first_inline: only works with pure Perl subs"); |
|
4564
|
|
|
|
|
|
|
} |
|
4565
|
|
|
|
|
|
|
|
|
4566
|
5
|
|
|
|
|
|
SV **args = &ST(1); |
|
4567
|
5
|
|
|
|
|
|
IV num_args = items - 1; |
|
4568
|
|
|
|
|
|
|
IV i; |
|
4569
|
|
|
|
|
|
|
|
|
4570
|
|
|
|
|
|
|
/* Use standard MULTICALL API for compatibility */ |
|
4571
|
|
|
|
|
|
|
dMULTICALL; |
|
4572
|
5
|
|
|
|
|
|
I32 gimme = G_SCALAR; |
|
4573
|
|
|
|
|
|
|
|
|
4574
|
5
|
50
|
|
|
|
|
PUSH_MULTICALL(the_cv); |
|
4575
|
|
|
|
|
|
|
|
|
4576
|
|
|
|
|
|
|
/* Save and setup $_ */ |
|
4577
|
5
|
|
|
|
|
|
SAVESPTR(GvSV(PL_defgv)); |
|
4578
|
|
|
|
|
|
|
|
|
4579
|
25
|
100
|
|
|
|
|
for (i = 0; i < num_args; i++) { |
|
4580
|
24
|
|
|
|
|
|
SV *elem = args[i]; |
|
4581
|
24
|
|
|
|
|
|
GvSV(PL_defgv) = elem; |
|
4582
|
24
|
|
|
|
|
|
SvTEMP_off(elem); |
|
4583
|
|
|
|
|
|
|
|
|
4584
|
24
|
|
|
|
|
|
MULTICALL; |
|
4585
|
|
|
|
|
|
|
|
|
4586
|
24
|
100
|
|
|
|
|
if (SvTRUE(*PL_stack_sp)) { |
|
4587
|
|
|
|
|
|
|
/* Found it - cleanup and return */ |
|
4588
|
4
|
50
|
|
|
|
|
POP_MULTICALL; |
|
4589
|
4
|
|
|
|
|
|
SPAGAIN; |
|
4590
|
|
|
|
|
|
|
|
|
4591
|
4
|
|
|
|
|
|
ST(0) = elem; |
|
4592
|
4
|
|
|
|
|
|
XSRETURN(1); |
|
4593
|
|
|
|
|
|
|
} |
|
4594
|
|
|
|
|
|
|
} |
|
4595
|
|
|
|
|
|
|
|
|
4596
|
|
|
|
|
|
|
/* Cleanup */ |
|
4597
|
1
|
50
|
|
|
|
|
POP_MULTICALL; |
|
4598
|
1
|
|
|
|
|
|
SPAGAIN; |
|
4599
|
|
|
|
|
|
|
|
|
4600
|
1
|
|
|
|
|
|
XSRETURN_UNDEF; |
|
4601
|
|
|
|
|
|
|
} |
|
4602
|
|
|
|
|
|
|
#endif /* dMULTICALL */ |
|
4603
|
|
|
|
|
|
|
|
|
4604
|
|
|
|
|
|
|
|
|
4605
|
|
|
|
|
|
|
/* ============================================ |
|
4606
|
|
|
|
|
|
|
Type predicate XS fallbacks |
|
4607
|
|
|
|
|
|
|
============================================ */ |
|
4608
|
|
|
|
|
|
|
|
|
4609
|
17123
|
|
|
|
|
|
XS_INTERNAL(xs_is_ref) { |
|
4610
|
17123
|
|
|
|
|
|
dXSARGS; |
|
4611
|
17123
|
50
|
|
|
|
|
if (items != 1) croak("Usage: Func::Util::is_ref($value)"); |
|
4612
|
17123
|
100
|
|
|
|
|
ST(0) = SvROK(ST(0)) ? &PL_sv_yes : &PL_sv_no; |
|
4613
|
17123
|
|
|
|
|
|
XSRETURN(1); |
|
4614
|
|
|
|
|
|
|
} |
|
4615
|
|
|
|
|
|
|
|
|
4616
|
17132
|
|
|
|
|
|
XS_INTERNAL(xs_is_array) { |
|
4617
|
17132
|
|
|
|
|
|
dXSARGS; |
|
4618
|
17132
|
50
|
|
|
|
|
if (items != 1) croak("Usage: Func::Util::is_array($value)"); |
|
4619
|
17132
|
|
|
|
|
|
SV *sv = ST(0); |
|
4620
|
17132
|
100
|
|
|
|
|
ST(0) = (SvROK(sv) && SvTYPE(SvRV(sv)) == SVt_PVAV) ? &PL_sv_yes : &PL_sv_no; |
|
|
|
100
|
|
|
|
|
|
|
4621
|
17132
|
|
|
|
|
|
XSRETURN(1); |
|
4622
|
|
|
|
|
|
|
} |
|
4623
|
|
|
|
|
|
|
|
|
4624
|
17131
|
|
|
|
|
|
XS_INTERNAL(xs_is_hash) { |
|
4625
|
17131
|
|
|
|
|
|
dXSARGS; |
|
4626
|
17131
|
50
|
|
|
|
|
if (items != 1) croak("Usage: Func::Util::is_hash($value)"); |
|
4627
|
17131
|
|
|
|
|
|
SV *sv = ST(0); |
|
4628
|
17131
|
100
|
|
|
|
|
ST(0) = (SvROK(sv) && SvTYPE(SvRV(sv)) == SVt_PVHV) ? &PL_sv_yes : &PL_sv_no; |
|
|
|
100
|
|
|
|
|
|
|
4629
|
17131
|
|
|
|
|
|
XSRETURN(1); |
|
4630
|
|
|
|
|
|
|
} |
|
4631
|
|
|
|
|
|
|
|
|
4632
|
17117
|
|
|
|
|
|
XS_INTERNAL(xs_is_code) { |
|
4633
|
17117
|
|
|
|
|
|
dXSARGS; |
|
4634
|
17117
|
50
|
|
|
|
|
if (items != 1) croak("Usage: Func::Util::is_code($value)"); |
|
4635
|
17117
|
|
|
|
|
|
SV *sv = ST(0); |
|
4636
|
17117
|
100
|
|
|
|
|
ST(0) = (SvROK(sv) && SvTYPE(SvRV(sv)) == SVt_PVCV) ? &PL_sv_yes : &PL_sv_no; |
|
|
|
100
|
|
|
|
|
|
|
4637
|
17117
|
|
|
|
|
|
XSRETURN(1); |
|
4638
|
|
|
|
|
|
|
} |
|
4639
|
|
|
|
|
|
|
|
|
4640
|
17118
|
|
|
|
|
|
XS_INTERNAL(xs_is_defined) { |
|
4641
|
17118
|
|
|
|
|
|
dXSARGS; |
|
4642
|
17118
|
50
|
|
|
|
|
if (items != 1) croak("Usage: Func::Util::is_defined($value)"); |
|
4643
|
17118
|
100
|
|
|
|
|
ST(0) = SvOK(ST(0)) ? &PL_sv_yes : &PL_sv_no; |
|
4644
|
17118
|
|
|
|
|
|
XSRETURN(1); |
|
4645
|
|
|
|
|
|
|
} |
|
4646
|
|
|
|
|
|
|
|
|
4647
|
|
|
|
|
|
|
/* ============================================ |
|
4648
|
|
|
|
|
|
|
String predicate XS fallbacks |
|
4649
|
|
|
|
|
|
|
============================================ */ |
|
4650
|
|
|
|
|
|
|
|
|
4651
|
12016
|
|
|
|
|
|
XS_INTERNAL(xs_is_empty) { |
|
4652
|
12016
|
|
|
|
|
|
dXSARGS; |
|
4653
|
12016
|
50
|
|
|
|
|
if (items != 1) croak("Usage: Func::Util::is_empty($value)"); |
|
4654
|
12016
|
|
|
|
|
|
SV *sv = ST(0); |
|
4655
|
12016
|
100
|
|
|
|
|
if (!SvOK(sv)) { |
|
4656
|
2002
|
|
|
|
|
|
ST(0) = &PL_sv_yes; |
|
4657
|
|
|
|
|
|
|
} else { |
|
4658
|
|
|
|
|
|
|
STRLEN len; |
|
4659
|
10014
|
|
|
|
|
|
SvPV(sv, len); |
|
4660
|
10014
|
100
|
|
|
|
|
ST(0) = len == 0 ? &PL_sv_yes : &PL_sv_no; |
|
4661
|
|
|
|
|
|
|
} |
|
4662
|
12016
|
|
|
|
|
|
XSRETURN(1); |
|
4663
|
|
|
|
|
|
|
} |
|
4664
|
|
|
|
|
|
|
|
|
4665
|
19124
|
|
|
|
|
|
XS_INTERNAL(xs_starts_with) { |
|
4666
|
19124
|
|
|
|
|
|
dXSARGS; |
|
4667
|
19124
|
50
|
|
|
|
|
if (items != 2) croak("Usage: Func::Util::starts_with($string, $prefix)"); |
|
4668
|
|
|
|
|
|
|
|
|
4669
|
19124
|
|
|
|
|
|
SV *str_sv = ST(0); |
|
4670
|
19124
|
|
|
|
|
|
SV *prefix_sv = ST(1); |
|
4671
|
|
|
|
|
|
|
|
|
4672
|
19124
|
100
|
|
|
|
|
if (!SvOK(str_sv) || !SvOK(prefix_sv)) { |
|
|
|
100
|
|
|
|
|
|
|
4673
|
1003
|
|
|
|
|
|
ST(0) = &PL_sv_no; |
|
4674
|
1003
|
|
|
|
|
|
XSRETURN(1); |
|
4675
|
|
|
|
|
|
|
} |
|
4676
|
|
|
|
|
|
|
|
|
4677
|
|
|
|
|
|
|
STRLEN str_len, prefix_len; |
|
4678
|
18121
|
|
|
|
|
|
const char *str = SvPV(str_sv, str_len); |
|
4679
|
18121
|
|
|
|
|
|
const char *prefix = SvPV(prefix_sv, prefix_len); |
|
4680
|
|
|
|
|
|
|
|
|
4681
|
18121
|
100
|
|
|
|
|
if (prefix_len > str_len) { |
|
4682
|
3002
|
|
|
|
|
|
ST(0) = &PL_sv_no; |
|
4683
|
15119
|
100
|
|
|
|
|
} else if (prefix_len == 0) { |
|
4684
|
1002
|
|
|
|
|
|
ST(0) = &PL_sv_yes; |
|
4685
|
|
|
|
|
|
|
} else { |
|
4686
|
14117
|
100
|
|
|
|
|
ST(0) = memcmp(str, prefix, prefix_len) == 0 ? &PL_sv_yes : &PL_sv_no; |
|
4687
|
|
|
|
|
|
|
} |
|
4688
|
18121
|
|
|
|
|
|
XSRETURN(1); |
|
4689
|
|
|
|
|
|
|
} |
|
4690
|
|
|
|
|
|
|
|
|
4691
|
19122
|
|
|
|
|
|
XS_INTERNAL(xs_ends_with) { |
|
4692
|
19122
|
|
|
|
|
|
dXSARGS; |
|
4693
|
19122
|
50
|
|
|
|
|
if (items != 2) croak("Usage: Func::Util::ends_with($string, $suffix)"); |
|
4694
|
|
|
|
|
|
|
|
|
4695
|
19122
|
|
|
|
|
|
SV *str_sv = ST(0); |
|
4696
|
19122
|
|
|
|
|
|
SV *suffix_sv = ST(1); |
|
4697
|
|
|
|
|
|
|
|
|
4698
|
19122
|
100
|
|
|
|
|
if (!SvOK(str_sv) || !SvOK(suffix_sv)) { |
|
|
|
100
|
|
|
|
|
|
|
4699
|
1003
|
|
|
|
|
|
ST(0) = &PL_sv_no; |
|
4700
|
1003
|
|
|
|
|
|
XSRETURN(1); |
|
4701
|
|
|
|
|
|
|
} |
|
4702
|
|
|
|
|
|
|
|
|
4703
|
|
|
|
|
|
|
STRLEN str_len, suffix_len; |
|
4704
|
18119
|
|
|
|
|
|
const char *str = SvPV(str_sv, str_len); |
|
4705
|
18119
|
|
|
|
|
|
const char *suffix = SvPV(suffix_sv, suffix_len); |
|
4706
|
|
|
|
|
|
|
|
|
4707
|
18119
|
100
|
|
|
|
|
if (suffix_len > str_len) { |
|
4708
|
3002
|
|
|
|
|
|
ST(0) = &PL_sv_no; |
|
4709
|
15117
|
100
|
|
|
|
|
} else if (suffix_len == 0) { |
|
4710
|
1002
|
|
|
|
|
|
ST(0) = &PL_sv_yes; |
|
4711
|
|
|
|
|
|
|
} else { |
|
4712
|
14115
|
|
|
|
|
|
const char *str_end = str + str_len - suffix_len; |
|
4713
|
14115
|
100
|
|
|
|
|
ST(0) = memcmp(str_end, suffix, suffix_len) == 0 ? &PL_sv_yes : &PL_sv_no; |
|
4714
|
|
|
|
|
|
|
} |
|
4715
|
18119
|
|
|
|
|
|
XSRETURN(1); |
|
4716
|
|
|
|
|
|
|
} |
|
4717
|
|
|
|
|
|
|
|
|
4718
|
|
|
|
|
|
|
/* count: count occurrences of substring using memmem */ |
|
4719
|
2000
|
|
|
|
|
|
XS_INTERNAL(xs_count) { |
|
4720
|
2000
|
|
|
|
|
|
dXSARGS; |
|
4721
|
2000
|
50
|
|
|
|
|
if (items != 2) croak("Usage: Func::Util::count($string, $substring)"); |
|
4722
|
|
|
|
|
|
|
|
|
4723
|
2000
|
|
|
|
|
|
SV *str_sv = ST(0); |
|
4724
|
2000
|
|
|
|
|
|
SV *needle_sv = ST(1); |
|
4725
|
|
|
|
|
|
|
|
|
4726
|
2000
|
50
|
|
|
|
|
if (!SvOK(str_sv) || !SvOK(needle_sv)) { |
|
|
|
50
|
|
|
|
|
|
|
4727
|
0
|
|
|
|
|
|
ST(0) = sv_2mortal(newSViv(0)); |
|
4728
|
0
|
|
|
|
|
|
XSRETURN(1); |
|
4729
|
|
|
|
|
|
|
} |
|
4730
|
|
|
|
|
|
|
|
|
4731
|
|
|
|
|
|
|
STRLEN str_len, needle_len; |
|
4732
|
2000
|
|
|
|
|
|
const char *str = SvPV_const(str_sv, str_len); |
|
4733
|
2000
|
|
|
|
|
|
const char *needle = SvPV_const(needle_sv, needle_len); |
|
4734
|
|
|
|
|
|
|
|
|
4735
|
2000
|
50
|
|
|
|
|
if (needle_len == 0 || needle_len > str_len) { |
|
|
|
50
|
|
|
|
|
|
|
4736
|
2000
|
|
|
|
|
|
ST(0) = sv_2mortal(newSViv(0)); |
|
4737
|
2000
|
|
|
|
|
|
XSRETURN(1); |
|
4738
|
|
|
|
|
|
|
} |
|
4739
|
|
|
|
|
|
|
|
|
4740
|
0
|
|
|
|
|
|
IV count = 0; |
|
4741
|
0
|
|
|
|
|
|
const char *p = str; |
|
4742
|
0
|
|
|
|
|
|
const char *end = str + str_len; |
|
4743
|
0
|
|
|
|
|
|
STRLEN remaining = str_len; |
|
4744
|
|
|
|
|
|
|
|
|
4745
|
0
|
0
|
|
|
|
|
while (remaining >= needle_len) { |
|
4746
|
0
|
|
|
|
|
|
const char *found = (const char *)util_memmem(p, remaining, needle, needle_len); |
|
4747
|
0
|
0
|
|
|
|
|
if (!found) break; |
|
4748
|
0
|
|
|
|
|
|
count++; |
|
4749
|
|
|
|
|
|
|
/* Move past the match (non-overlapping) */ |
|
4750
|
0
|
|
|
|
|
|
p = found + needle_len; |
|
4751
|
0
|
|
|
|
|
|
remaining = end - p; |
|
4752
|
|
|
|
|
|
|
} |
|
4753
|
|
|
|
|
|
|
|
|
4754
|
0
|
|
|
|
|
|
ST(0) = sv_2mortal(newSViv(count)); |
|
4755
|
0
|
|
|
|
|
|
XSRETURN(1); |
|
4756
|
|
|
|
|
|
|
} |
|
4757
|
|
|
|
|
|
|
|
|
4758
|
|
|
|
|
|
|
/* replace_all: replace all occurrences of old with new using memmem */ |
|
4759
|
5026
|
|
|
|
|
|
XS_INTERNAL(xs_replace_all) { |
|
4760
|
5026
|
|
|
|
|
|
dXSARGS; |
|
4761
|
5026
|
50
|
|
|
|
|
if (items != 3) croak("Usage: Func::Util::replace_all($string, $old, $new)"); |
|
4762
|
|
|
|
|
|
|
|
|
4763
|
5026
|
|
|
|
|
|
SV *str_sv = ST(0); |
|
4764
|
5026
|
|
|
|
|
|
SV *old_sv = ST(1); |
|
4765
|
5026
|
|
|
|
|
|
SV *new_sv = ST(2); |
|
4766
|
|
|
|
|
|
|
|
|
4767
|
|
|
|
|
|
|
/* Handle undef - return undef */ |
|
4768
|
5026
|
50
|
|
|
|
|
if (!SvOK(str_sv)) { |
|
4769
|
0
|
|
|
|
|
|
ST(0) = &PL_sv_undef; |
|
4770
|
0
|
|
|
|
|
|
XSRETURN(1); |
|
4771
|
|
|
|
|
|
|
} |
|
4772
|
|
|
|
|
|
|
|
|
4773
|
|
|
|
|
|
|
STRLEN str_len, old_len, new_len; |
|
4774
|
5026
|
|
|
|
|
|
const char *str = SvPV_const(str_sv, str_len); |
|
4775
|
5026
|
|
|
|
|
|
const char *old = SvPV_const(old_sv, old_len); |
|
4776
|
5026
|
|
|
|
|
|
const char *replacement = SvPV_const(new_sv, new_len); |
|
4777
|
|
|
|
|
|
|
|
|
4778
|
|
|
|
|
|
|
/* Empty search string or not found - return original */ |
|
4779
|
5026
|
100
|
|
|
|
|
if (old_len == 0 || old_len > str_len) { |
|
|
|
100
|
|
|
|
|
|
|
4780
|
1002
|
|
|
|
|
|
ST(0) = sv_2mortal(newSVpvn(str, str_len)); |
|
4781
|
1002
|
|
|
|
|
|
XSRETURN(1); |
|
4782
|
|
|
|
|
|
|
} |
|
4783
|
|
|
|
|
|
|
|
|
4784
|
|
|
|
|
|
|
/* First pass: count occurrences to pre-size buffer */ |
|
4785
|
4024
|
|
|
|
|
|
IV count = 0; |
|
4786
|
4024
|
|
|
|
|
|
const char *p = str; |
|
4787
|
4024
|
|
|
|
|
|
const char *end = str + str_len; |
|
4788
|
4024
|
|
|
|
|
|
STRLEN remaining = str_len; |
|
4789
|
|
|
|
|
|
|
|
|
4790
|
11061
|
100
|
|
|
|
|
while (remaining >= old_len) { |
|
4791
|
9052
|
|
|
|
|
|
const char *found = (const char *)util_memmem(p, remaining, old, old_len); |
|
4792
|
9052
|
100
|
|
|
|
|
if (!found) break; |
|
4793
|
7037
|
|
|
|
|
|
count++; |
|
4794
|
7037
|
|
|
|
|
|
p = found + old_len; |
|
4795
|
7037
|
|
|
|
|
|
remaining = end - p; |
|
4796
|
|
|
|
|
|
|
} |
|
4797
|
|
|
|
|
|
|
|
|
4798
|
4024
|
100
|
|
|
|
|
if (count == 0) { |
|
4799
|
|
|
|
|
|
|
/* No matches - return copy of original */ |
|
4800
|
1002
|
|
|
|
|
|
ST(0) = sv_2mortal(newSVpvn(str, str_len)); |
|
4801
|
1002
|
|
|
|
|
|
XSRETURN(1); |
|
4802
|
|
|
|
|
|
|
} |
|
4803
|
|
|
|
|
|
|
|
|
4804
|
|
|
|
|
|
|
/* Calculate result size and allocate */ |
|
4805
|
3022
|
|
|
|
|
|
STRLEN result_len = str_len + count * (new_len - old_len); |
|
4806
|
3022
|
|
|
|
|
|
SV *result = sv_2mortal(newSV(result_len + 1)); |
|
4807
|
3022
|
|
|
|
|
|
SvPOK_on(result); |
|
4808
|
3022
|
|
|
|
|
|
char *out = SvPVX(result); |
|
4809
|
3022
|
|
|
|
|
|
char *out_ptr = out; |
|
4810
|
|
|
|
|
|
|
|
|
4811
|
|
|
|
|
|
|
/* Second pass: build result */ |
|
4812
|
3022
|
|
|
|
|
|
p = str; |
|
4813
|
3022
|
|
|
|
|
|
remaining = str_len; |
|
4814
|
|
|
|
|
|
|
|
|
4815
|
10059
|
100
|
|
|
|
|
while (remaining >= old_len) { |
|
4816
|
8050
|
|
|
|
|
|
const char *found = (const char *)util_memmem(p, remaining, old, old_len); |
|
4817
|
8050
|
100
|
|
|
|
|
if (!found) break; |
|
4818
|
|
|
|
|
|
|
|
|
4819
|
|
|
|
|
|
|
/* Copy text before match */ |
|
4820
|
7037
|
|
|
|
|
|
STRLEN before_len = found - p; |
|
4821
|
7037
|
100
|
|
|
|
|
if (before_len > 0) { |
|
4822
|
2025
|
|
|
|
|
|
memcpy(out_ptr, p, before_len); |
|
4823
|
2025
|
|
|
|
|
|
out_ptr += before_len; |
|
4824
|
|
|
|
|
|
|
} |
|
4825
|
|
|
|
|
|
|
|
|
4826
|
|
|
|
|
|
|
/* Copy replacement */ |
|
4827
|
7037
|
100
|
|
|
|
|
if (new_len > 0) { |
|
4828
|
5035
|
|
|
|
|
|
memcpy(out_ptr, replacement, new_len); |
|
4829
|
5035
|
|
|
|
|
|
out_ptr += new_len; |
|
4830
|
|
|
|
|
|
|
} |
|
4831
|
|
|
|
|
|
|
|
|
4832
|
7037
|
|
|
|
|
|
p = found + old_len; |
|
4833
|
7037
|
|
|
|
|
|
remaining = end - p; |
|
4834
|
|
|
|
|
|
|
} |
|
4835
|
|
|
|
|
|
|
|
|
4836
|
|
|
|
|
|
|
/* Copy remaining text after last match */ |
|
4837
|
3022
|
100
|
|
|
|
|
if (remaining > 0) { |
|
4838
|
1016
|
|
|
|
|
|
memcpy(out_ptr, p, remaining); |
|
4839
|
1016
|
|
|
|
|
|
out_ptr += remaining; |
|
4840
|
|
|
|
|
|
|
} |
|
4841
|
|
|
|
|
|
|
|
|
4842
|
3022
|
|
|
|
|
|
*out_ptr = '\0'; |
|
4843
|
3022
|
|
|
|
|
|
SvCUR_set(result, out_ptr - out); |
|
4844
|
|
|
|
|
|
|
|
|
4845
|
3022
|
|
|
|
|
|
ST(0) = result; |
|
4846
|
3022
|
|
|
|
|
|
XSRETURN(1); |
|
4847
|
|
|
|
|
|
|
} |
|
4848
|
|
|
|
|
|
|
|
|
4849
|
|
|
|
|
|
|
/* before: get text before first occurrence of delimiter */ |
|
4850
|
0
|
|
|
|
|
|
XS_INTERNAL(xs_before) { |
|
4851
|
0
|
|
|
|
|
|
dXSARGS; |
|
4852
|
0
|
0
|
|
|
|
|
if (items != 2) croak("Usage: Func::Util::before($string, $delimiter)"); |
|
4853
|
|
|
|
|
|
|
|
|
4854
|
0
|
|
|
|
|
|
SV *str_sv = ST(0); |
|
4855
|
0
|
|
|
|
|
|
SV *delim_sv = ST(1); |
|
4856
|
|
|
|
|
|
|
|
|
4857
|
0
|
0
|
|
|
|
|
if (!SvOK(str_sv)) { |
|
4858
|
0
|
|
|
|
|
|
ST(0) = &PL_sv_undef; |
|
4859
|
0
|
|
|
|
|
|
XSRETURN(1); |
|
4860
|
|
|
|
|
|
|
} |
|
4861
|
|
|
|
|
|
|
|
|
4862
|
|
|
|
|
|
|
STRLEN str_len, delim_len; |
|
4863
|
0
|
|
|
|
|
|
const char *str = SvPV_const(str_sv, str_len); |
|
4864
|
0
|
|
|
|
|
|
const char *delim = SvPV_const(delim_sv, delim_len); |
|
4865
|
|
|
|
|
|
|
|
|
4866
|
0
|
0
|
|
|
|
|
if (delim_len == 0 || delim_len > str_len) { |
|
|
|
0
|
|
|
|
|
|
|
4867
|
0
|
|
|
|
|
|
ST(0) = sv_2mortal(newSVpvn(str, str_len)); |
|
4868
|
0
|
|
|
|
|
|
XSRETURN(1); |
|
4869
|
|
|
|
|
|
|
} |
|
4870
|
|
|
|
|
|
|
|
|
4871
|
0
|
|
|
|
|
|
const char *found = (const char *)util_memmem(str, str_len, delim, delim_len); |
|
4872
|
0
|
0
|
|
|
|
|
if (found) { |
|
4873
|
0
|
|
|
|
|
|
ST(0) = sv_2mortal(newSVpvn(str, found - str)); |
|
4874
|
|
|
|
|
|
|
} else { |
|
4875
|
0
|
|
|
|
|
|
ST(0) = sv_2mortal(newSVpvn(str, str_len)); |
|
4876
|
|
|
|
|
|
|
} |
|
4877
|
0
|
|
|
|
|
|
XSRETURN(1); |
|
4878
|
|
|
|
|
|
|
} |
|
4879
|
|
|
|
|
|
|
|
|
4880
|
|
|
|
|
|
|
/* after: get text after first occurrence of delimiter */ |
|
4881
|
0
|
|
|
|
|
|
XS_INTERNAL(xs_after) { |
|
4882
|
0
|
|
|
|
|
|
dXSARGS; |
|
4883
|
0
|
0
|
|
|
|
|
if (items != 2) croak("Usage: Func::Util::after($string, $delimiter)"); |
|
4884
|
|
|
|
|
|
|
|
|
4885
|
0
|
|
|
|
|
|
SV *str_sv = ST(0); |
|
4886
|
0
|
|
|
|
|
|
SV *delim_sv = ST(1); |
|
4887
|
|
|
|
|
|
|
|
|
4888
|
0
|
0
|
|
|
|
|
if (!SvOK(str_sv)) { |
|
4889
|
0
|
|
|
|
|
|
ST(0) = &PL_sv_undef; |
|
4890
|
0
|
|
|
|
|
|
XSRETURN(1); |
|
4891
|
|
|
|
|
|
|
} |
|
4892
|
|
|
|
|
|
|
|
|
4893
|
|
|
|
|
|
|
STRLEN str_len, delim_len; |
|
4894
|
0
|
|
|
|
|
|
const char *str = SvPV_const(str_sv, str_len); |
|
4895
|
0
|
|
|
|
|
|
const char *delim = SvPV_const(delim_sv, delim_len); |
|
4896
|
|
|
|
|
|
|
|
|
4897
|
0
|
0
|
|
|
|
|
if (delim_len == 0 || delim_len > str_len) { |
|
|
|
0
|
|
|
|
|
|
|
4898
|
0
|
|
|
|
|
|
ST(0) = sv_2mortal(newSVpvn("", 0)); |
|
4899
|
0
|
|
|
|
|
|
XSRETURN(1); |
|
4900
|
|
|
|
|
|
|
} |
|
4901
|
|
|
|
|
|
|
|
|
4902
|
0
|
|
|
|
|
|
const char *found = (const char *)util_memmem(str, str_len, delim, delim_len); |
|
4903
|
0
|
0
|
|
|
|
|
if (found) { |
|
4904
|
0
|
|
|
|
|
|
const char *after_delim = found + delim_len; |
|
4905
|
0
|
|
|
|
|
|
ST(0) = sv_2mortal(newSVpvn(after_delim, str + str_len - after_delim)); |
|
4906
|
|
|
|
|
|
|
} else { |
|
4907
|
0
|
|
|
|
|
|
ST(0) = sv_2mortal(newSVpvn("", 0)); |
|
4908
|
|
|
|
|
|
|
} |
|
4909
|
0
|
|
|
|
|
|
XSRETURN(1); |
|
4910
|
|
|
|
|
|
|
} |
|
4911
|
|
|
|
|
|
|
|
|
4912
|
|
|
|
|
|
|
/* ============================================ |
|
4913
|
|
|
|
|
|
|
Boolean/Truthiness XS fallbacks |
|
4914
|
|
|
|
|
|
|
============================================ */ |
|
4915
|
|
|
|
|
|
|
|
|
4916
|
24129
|
|
|
|
|
|
XS_INTERNAL(xs_is_true) { |
|
4917
|
24129
|
|
|
|
|
|
dXSARGS; |
|
4918
|
24129
|
50
|
|
|
|
|
if (items != 1) croak("Usage: Func::Util::is_true($value)"); |
|
4919
|
24129
|
100
|
|
|
|
|
ST(0) = SvTRUE(ST(0)) ? &PL_sv_yes : &PL_sv_no; |
|
4920
|
24129
|
|
|
|
|
|
XSRETURN(1); |
|
4921
|
|
|
|
|
|
|
} |
|
4922
|
|
|
|
|
|
|
|
|
4923
|
22118
|
|
|
|
|
|
XS_INTERNAL(xs_is_false) { |
|
4924
|
22118
|
|
|
|
|
|
dXSARGS; |
|
4925
|
22118
|
50
|
|
|
|
|
if (items != 1) croak("Usage: Func::Util::is_false($value)"); |
|
4926
|
22118
|
100
|
|
|
|
|
ST(0) = SvTRUE(ST(0)) ? &PL_sv_no : &PL_sv_yes; |
|
4927
|
22118
|
|
|
|
|
|
XSRETURN(1); |
|
4928
|
|
|
|
|
|
|
} |
|
4929
|
|
|
|
|
|
|
|
|
4930
|
30119
|
|
|
|
|
|
XS_INTERNAL(xs_bool) { |
|
4931
|
30119
|
|
|
|
|
|
dXSARGS; |
|
4932
|
30119
|
50
|
|
|
|
|
if (items != 1) croak("Usage: Func::Util::bool($value)"); |
|
4933
|
30119
|
100
|
|
|
|
|
ST(0) = SvTRUE(ST(0)) ? &PL_sv_yes : &PL_sv_no; |
|
4934
|
30119
|
|
|
|
|
|
XSRETURN(1); |
|
4935
|
|
|
|
|
|
|
} |
|
4936
|
|
|
|
|
|
|
|
|
4937
|
|
|
|
|
|
|
/* ============================================ |
|
4938
|
|
|
|
|
|
|
Extended type predicate XS fallbacks |
|
4939
|
|
|
|
|
|
|
============================================ */ |
|
4940
|
|
|
|
|
|
|
|
|
4941
|
19155
|
|
|
|
|
|
XS_INTERNAL(xs_is_num) { |
|
4942
|
19155
|
|
|
|
|
|
dXSARGS; |
|
4943
|
19155
|
50
|
|
|
|
|
if (items != 1) croak("Usage: Func::Util::is_num($value)"); |
|
4944
|
19155
|
|
|
|
|
|
SV *sv = ST(0); |
|
4945
|
19155
|
100
|
|
|
|
|
ST(0) = (SvNIOK(sv) || looks_like_number(sv)) ? &PL_sv_yes : &PL_sv_no; |
|
|
|
100
|
|
|
|
|
|
|
4946
|
19155
|
|
|
|
|
|
XSRETURN(1); |
|
4947
|
|
|
|
|
|
|
} |
|
4948
|
|
|
|
|
|
|
|
|
4949
|
17120
|
|
|
|
|
|
XS_INTERNAL(xs_is_int) { |
|
4950
|
17120
|
|
|
|
|
|
dXSARGS; |
|
4951
|
17120
|
50
|
|
|
|
|
if (items != 1) croak("Usage: Func::Util::is_int($value)"); |
|
4952
|
17120
|
|
|
|
|
|
SV *sv = ST(0); |
|
4953
|
17120
|
100
|
|
|
|
|
if (SvIOK(sv)) { |
|
4954
|
14106
|
|
|
|
|
|
ST(0) = &PL_sv_yes; |
|
4955
|
3014
|
100
|
|
|
|
|
} else if (SvNOK(sv)) { |
|
4956
|
3009
|
|
|
|
|
|
NV nv = SvNV(sv); |
|
4957
|
3009
|
100
|
|
|
|
|
ST(0) = (nv == (NV)(IV)nv) ? &PL_sv_yes : &PL_sv_no; |
|
4958
|
5
|
100
|
|
|
|
|
} else if (looks_like_number(sv)) { |
|
4959
|
|
|
|
|
|
|
STRLEN len; |
|
4960
|
2
|
|
|
|
|
|
const char *pv = SvPV(sv, len); |
|
4961
|
2
|
|
|
|
|
|
bool has_dot = FALSE; |
|
4962
|
|
|
|
|
|
|
STRLEN i; |
|
4963
|
6
|
100
|
|
|
|
|
for (i = 0; i < len; i++) { |
|
4964
|
4
|
50
|
|
|
|
|
if (pv[i] == '.' || pv[i] == 'e' || pv[i] == 'E') { |
|
|
|
50
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
4965
|
0
|
|
|
|
|
|
has_dot = TRUE; |
|
4966
|
0
|
|
|
|
|
|
break; |
|
4967
|
|
|
|
|
|
|
} |
|
4968
|
|
|
|
|
|
|
} |
|
4969
|
2
|
50
|
|
|
|
|
if (has_dot) { |
|
4970
|
0
|
|
|
|
|
|
NV nv = SvNV(sv); |
|
4971
|
0
|
0
|
|
|
|
|
ST(0) = (nv == (NV)(IV)nv) ? &PL_sv_yes : &PL_sv_no; |
|
4972
|
|
|
|
|
|
|
} else { |
|
4973
|
2
|
|
|
|
|
|
ST(0) = &PL_sv_yes; |
|
4974
|
|
|
|
|
|
|
} |
|
4975
|
|
|
|
|
|
|
} else { |
|
4976
|
3
|
|
|
|
|
|
ST(0) = &PL_sv_no; |
|
4977
|
|
|
|
|
|
|
} |
|
4978
|
17120
|
|
|
|
|
|
XSRETURN(1); |
|
4979
|
|
|
|
|
|
|
} |
|
4980
|
|
|
|
|
|
|
|
|
4981
|
6011
|
|
|
|
|
|
XS_INTERNAL(xs_is_blessed) { |
|
4982
|
6011
|
|
|
|
|
|
dXSARGS; |
|
4983
|
6011
|
50
|
|
|
|
|
if (items != 1) croak("Usage: Func::Util::is_blessed($value)"); |
|
4984
|
6011
|
100
|
|
|
|
|
ST(0) = sv_isobject(ST(0)) ? &PL_sv_yes : &PL_sv_no; |
|
4985
|
6011
|
|
|
|
|
|
XSRETURN(1); |
|
4986
|
|
|
|
|
|
|
} |
|
4987
|
|
|
|
|
|
|
|
|
4988
|
4009
|
|
|
|
|
|
XS_INTERNAL(xs_is_scalar_ref) { |
|
4989
|
4009
|
|
|
|
|
|
dXSARGS; |
|
4990
|
4009
|
50
|
|
|
|
|
if (items != 1) croak("Usage: Func::Util::is_scalar_ref($value)"); |
|
4991
|
4009
|
|
|
|
|
|
SV *sv = ST(0); |
|
4992
|
4009
|
100
|
|
|
|
|
if (SvROK(sv)) { |
|
4993
|
4006
|
|
|
|
|
|
SV *rv = SvRV(sv); |
|
4994
|
4006
|
|
|
|
|
|
svtype type = SvTYPE(rv); |
|
4995
|
4006
|
100
|
|
|
|
|
ST(0) = (type < SVt_PVAV) ? &PL_sv_yes : &PL_sv_no; |
|
4996
|
|
|
|
|
|
|
} else { |
|
4997
|
3
|
|
|
|
|
|
ST(0) = &PL_sv_no; |
|
4998
|
|
|
|
|
|
|
} |
|
4999
|
4009
|
|
|
|
|
|
XSRETURN(1); |
|
5000
|
|
|
|
|
|
|
} |
|
5001
|
|
|
|
|
|
|
|
|
5002
|
6009
|
|
|
|
|
|
XS_INTERNAL(xs_is_regex) { |
|
5003
|
6009
|
|
|
|
|
|
dXSARGS; |
|
5004
|
6009
|
50
|
|
|
|
|
if (items != 1) croak("Usage: Func::Util::is_regex($value)"); |
|
5005
|
6009
|
100
|
|
|
|
|
ST(0) = SvRXOK(ST(0)) ? &PL_sv_yes : &PL_sv_no; |
|
5006
|
6009
|
|
|
|
|
|
XSRETURN(1); |
|
5007
|
|
|
|
|
|
|
} |
|
5008
|
|
|
|
|
|
|
|
|
5009
|
6008
|
|
|
|
|
|
XS_INTERNAL(xs_is_glob) { |
|
5010
|
6008
|
|
|
|
|
|
dXSARGS; |
|
5011
|
6008
|
50
|
|
|
|
|
if (items != 1) croak("Usage: Func::Util::is_glob($value)"); |
|
5012
|
6008
|
100
|
|
|
|
|
ST(0) = (SvTYPE(ST(0)) == SVt_PVGV) ? &PL_sv_yes : &PL_sv_no; |
|
5013
|
6008
|
|
|
|
|
|
XSRETURN(1); |
|
5014
|
|
|
|
|
|
|
} |
|
5015
|
|
|
|
|
|
|
|
|
5016
|
8017
|
|
|
|
|
|
XS_INTERNAL(xs_is_string) { |
|
5017
|
8017
|
|
|
|
|
|
dXSARGS; |
|
5018
|
8017
|
50
|
|
|
|
|
if (items != 1) croak("Usage: Func::Util::is_string($value)"); |
|
5019
|
8017
|
|
|
|
|
|
SV *sv = ST(0); |
|
5020
|
8017
|
100
|
|
|
|
|
ST(0) = (SvOK(sv) && !SvROK(sv)) ? &PL_sv_yes : &PL_sv_no; |
|
|
|
100
|
|
|
|
|
|
|
5021
|
8017
|
|
|
|
|
|
XSRETURN(1); |
|
5022
|
|
|
|
|
|
|
} |
|
5023
|
|
|
|
|
|
|
|
|
5024
|
|
|
|
|
|
|
/* ============================================ |
|
5025
|
|
|
|
|
|
|
Numeric predicate XS fallbacks |
|
5026
|
|
|
|
|
|
|
============================================ */ |
|
5027
|
|
|
|
|
|
|
|
|
5028
|
15123
|
|
|
|
|
|
XS_INTERNAL(xs_is_positive) { |
|
5029
|
15123
|
|
|
|
|
|
dXSARGS; |
|
5030
|
15123
|
50
|
|
|
|
|
if (items != 1) croak("Usage: Func::Util::is_positive($value)"); |
|
5031
|
15123
|
|
|
|
|
|
SV *sv = ST(0); |
|
5032
|
15123
|
100
|
|
|
|
|
if (SvNIOK(sv) || looks_like_number(sv)) { |
|
|
|
50
|
|
|
|
|
|
|
5033
|
15121
|
|
|
|
|
|
NV nv = SvNV(sv); |
|
5034
|
15121
|
100
|
|
|
|
|
ST(0) = (nv > 0) ? &PL_sv_yes : &PL_sv_no; |
|
5035
|
|
|
|
|
|
|
} else { |
|
5036
|
2
|
|
|
|
|
|
ST(0) = &PL_sv_no; |
|
5037
|
|
|
|
|
|
|
} |
|
5038
|
15123
|
|
|
|
|
|
XSRETURN(1); |
|
5039
|
|
|
|
|
|
|
} |
|
5040
|
|
|
|
|
|
|
|
|
5041
|
13122
|
|
|
|
|
|
XS_INTERNAL(xs_is_negative) { |
|
5042
|
13122
|
|
|
|
|
|
dXSARGS; |
|
5043
|
13122
|
50
|
|
|
|
|
if (items != 1) croak("Usage: Func::Util::is_negative($value)"); |
|
5044
|
13122
|
|
|
|
|
|
SV *sv = ST(0); |
|
5045
|
13122
|
100
|
|
|
|
|
if (SvNIOK(sv) || looks_like_number(sv)) { |
|
|
|
50
|
|
|
|
|
|
|
5046
|
13120
|
|
|
|
|
|
NV nv = SvNV(sv); |
|
5047
|
13120
|
100
|
|
|
|
|
ST(0) = (nv < 0) ? &PL_sv_yes : &PL_sv_no; |
|
5048
|
|
|
|
|
|
|
} else { |
|
5049
|
2
|
|
|
|
|
|
ST(0) = &PL_sv_no; |
|
5050
|
|
|
|
|
|
|
} |
|
5051
|
13122
|
|
|
|
|
|
XSRETURN(1); |
|
5052
|
|
|
|
|
|
|
} |
|
5053
|
|
|
|
|
|
|
|
|
5054
|
13123
|
|
|
|
|
|
XS_INTERNAL(xs_is_zero) { |
|
5055
|
13123
|
|
|
|
|
|
dXSARGS; |
|
5056
|
13123
|
50
|
|
|
|
|
if (items != 1) croak("Usage: Func::Util::is_zero($value)"); |
|
5057
|
13123
|
|
|
|
|
|
SV *sv = ST(0); |
|
5058
|
13123
|
100
|
|
|
|
|
if (SvNIOK(sv) || looks_like_number(sv)) { |
|
|
|
100
|
|
|
|
|
|
|
5059
|
13121
|
|
|
|
|
|
NV nv = SvNV(sv); |
|
5060
|
13121
|
100
|
|
|
|
|
ST(0) = (nv == 0) ? &PL_sv_yes : &PL_sv_no; |
|
5061
|
|
|
|
|
|
|
} else { |
|
5062
|
2
|
|
|
|
|
|
ST(0) = &PL_sv_no; |
|
5063
|
|
|
|
|
|
|
} |
|
5064
|
13123
|
|
|
|
|
|
XSRETURN(1); |
|
5065
|
|
|
|
|
|
|
} |
|
5066
|
|
|
|
|
|
|
|
|
5067
|
|
|
|
|
|
|
/* ============================================ |
|
5068
|
|
|
|
|
|
|
Numeric utility XS fallbacks |
|
5069
|
|
|
|
|
|
|
============================================ */ |
|
5070
|
|
|
|
|
|
|
|
|
5071
|
18156
|
|
|
|
|
|
XS_INTERNAL(xs_is_even) { |
|
5072
|
18156
|
|
|
|
|
|
dXSARGS; |
|
5073
|
18156
|
50
|
|
|
|
|
if (items != 1) croak("Usage: Func::Util::is_even($value)"); |
|
5074
|
18156
|
|
|
|
|
|
SV *sv = ST(0); |
|
5075
|
18156
|
100
|
|
|
|
|
if (SvIOK(sv)) { |
|
5076
|
18144
|
100
|
|
|
|
|
ST(0) = (SvIVX(sv) & 1) == 0 ? &PL_sv_yes : &PL_sv_no; |
|
5077
|
12
|
100
|
|
|
|
|
} else if (SvNIOK(sv)) { |
|
5078
|
5
|
|
|
|
|
|
NV nv = SvNV(sv); |
|
5079
|
5
|
100
|
|
|
|
|
if (nv == (NV)(IV)nv) { |
|
5080
|
3
|
100
|
|
|
|
|
ST(0) = ((IV)nv & 1) == 0 ? &PL_sv_yes : &PL_sv_no; |
|
5081
|
|
|
|
|
|
|
} else { |
|
5082
|
2
|
|
|
|
|
|
ST(0) = &PL_sv_no; |
|
5083
|
|
|
|
|
|
|
} |
|
5084
|
7
|
100
|
|
|
|
|
} else if (looks_like_number(sv)) { |
|
5085
|
2
|
50
|
|
|
|
|
ST(0) = (SvIV(sv) & 1) == 0 ? &PL_sv_yes : &PL_sv_no; |
|
5086
|
|
|
|
|
|
|
} else { |
|
5087
|
5
|
|
|
|
|
|
ST(0) = &PL_sv_no; |
|
5088
|
|
|
|
|
|
|
} |
|
5089
|
18156
|
|
|
|
|
|
XSRETURN(1); |
|
5090
|
|
|
|
|
|
|
} |
|
5091
|
|
|
|
|
|
|
|
|
5092
|
18155
|
|
|
|
|
|
XS_INTERNAL(xs_is_odd) { |
|
5093
|
18155
|
|
|
|
|
|
dXSARGS; |
|
5094
|
18155
|
50
|
|
|
|
|
if (items != 1) croak("Usage: Func::Util::is_odd($value)"); |
|
5095
|
18155
|
|
|
|
|
|
SV *sv = ST(0); |
|
5096
|
18155
|
100
|
|
|
|
|
if (SvIOK(sv)) { |
|
5097
|
18144
|
100
|
|
|
|
|
ST(0) = (SvIVX(sv) & 1) == 1 ? &PL_sv_yes : &PL_sv_no; |
|
5098
|
11
|
100
|
|
|
|
|
} else if (SvNIOK(sv)) { |
|
5099
|
5
|
|
|
|
|
|
NV nv = SvNV(sv); |
|
5100
|
5
|
100
|
|
|
|
|
if (nv == (NV)(IV)nv) { |
|
5101
|
3
|
100
|
|
|
|
|
ST(0) = ((IV)nv & 1) == 1 ? &PL_sv_yes : &PL_sv_no; |
|
5102
|
|
|
|
|
|
|
} else { |
|
5103
|
2
|
|
|
|
|
|
ST(0) = &PL_sv_no; |
|
5104
|
|
|
|
|
|
|
} |
|
5105
|
6
|
100
|
|
|
|
|
} else if (looks_like_number(sv)) { |
|
5106
|
2
|
50
|
|
|
|
|
ST(0) = (SvIV(sv) & 1) == 1 ? &PL_sv_yes : &PL_sv_no; |
|
5107
|
|
|
|
|
|
|
} else { |
|
5108
|
4
|
|
|
|
|
|
ST(0) = &PL_sv_no; |
|
5109
|
|
|
|
|
|
|
} |
|
5110
|
18155
|
|
|
|
|
|
XSRETURN(1); |
|
5111
|
|
|
|
|
|
|
} |
|
5112
|
|
|
|
|
|
|
|
|
5113
|
15070
|
|
|
|
|
|
XS_INTERNAL(xs_is_between) { |
|
5114
|
15070
|
|
|
|
|
|
dXSARGS; |
|
5115
|
15070
|
50
|
|
|
|
|
if (items != 3) croak("Usage: Func::Util::is_between($value, $min, $max)"); |
|
5116
|
15070
|
|
|
|
|
|
SV *val_sv = ST(0); |
|
5117
|
15070
|
|
|
|
|
|
SV *min_sv = ST(1); |
|
5118
|
15070
|
|
|
|
|
|
SV *max_sv = ST(2); |
|
5119
|
|
|
|
|
|
|
|
|
5120
|
15070
|
100
|
|
|
|
|
if (SvNIOK(val_sv) || looks_like_number(val_sv)) { |
|
|
|
100
|
|
|
|
|
|
|
5121
|
15066
|
|
|
|
|
|
NV val = SvNV(val_sv); |
|
5122
|
15066
|
|
|
|
|
|
NV min = SvNV(min_sv); |
|
5123
|
15066
|
|
|
|
|
|
NV max = SvNV(max_sv); |
|
5124
|
15066
|
100
|
|
|
|
|
ST(0) = (val >= min && val <= max) ? &PL_sv_yes : &PL_sv_no; |
|
|
|
100
|
|
|
|
|
|
|
5125
|
|
|
|
|
|
|
} else { |
|
5126
|
4
|
|
|
|
|
|
ST(0) = &PL_sv_no; |
|
5127
|
|
|
|
|
|
|
} |
|
5128
|
15070
|
|
|
|
|
|
XSRETURN(1); |
|
5129
|
|
|
|
|
|
|
} |
|
5130
|
|
|
|
|
|
|
|
|
5131
|
|
|
|
|
|
|
/* ============================================ |
|
5132
|
|
|
|
|
|
|
Collection XS fallbacks |
|
5133
|
|
|
|
|
|
|
============================================ */ |
|
5134
|
|
|
|
|
|
|
|
|
5135
|
4008
|
|
|
|
|
|
XS_INTERNAL(xs_is_empty_array) { |
|
5136
|
4008
|
|
|
|
|
|
dXSARGS; |
|
5137
|
4008
|
50
|
|
|
|
|
if (items != 1) croak("Usage: Func::Util::is_empty_array($arrayref)"); |
|
5138
|
4008
|
|
|
|
|
|
SV *sv = ST(0); |
|
5139
|
4008
|
100
|
|
|
|
|
if (SvROK(sv) && SvTYPE(SvRV(sv)) == SVt_PVAV) { |
|
|
|
100
|
|
|
|
|
|
|
5140
|
4004
|
|
|
|
|
|
AV *av = (AV*)SvRV(sv); |
|
5141
|
4004
|
50
|
|
|
|
|
ST(0) = AvFILL(av) < 0 ? &PL_sv_yes : &PL_sv_no; |
|
|
|
100
|
|
|
|
|
|
|
5142
|
|
|
|
|
|
|
} else { |
|
5143
|
4
|
|
|
|
|
|
ST(0) = &PL_sv_no; |
|
5144
|
|
|
|
|
|
|
} |
|
5145
|
4008
|
|
|
|
|
|
XSRETURN(1); |
|
5146
|
|
|
|
|
|
|
} |
|
5147
|
|
|
|
|
|
|
|
|
5148
|
5008
|
|
|
|
|
|
XS_INTERNAL(xs_is_empty_hash) { |
|
5149
|
5008
|
|
|
|
|
|
dXSARGS; |
|
5150
|
5008
|
50
|
|
|
|
|
if (items != 1) croak("Usage: Func::Util::is_empty_hash($hashref)"); |
|
5151
|
5008
|
|
|
|
|
|
SV *sv = ST(0); |
|
5152
|
5008
|
100
|
|
|
|
|
if (SvROK(sv) && SvTYPE(SvRV(sv)) == SVt_PVHV) { |
|
|
|
100
|
|
|
|
|
|
|
5153
|
5004
|
|
|
|
|
|
HV *hv = (HV*)SvRV(sv); |
|
5154
|
5004
|
50
|
|
|
|
|
ST(0) = HvKEYS(hv) == 0 ? &PL_sv_yes : &PL_sv_no; |
|
|
|
100
|
|
|
|
|
|
|
5155
|
|
|
|
|
|
|
} else { |
|
5156
|
4
|
|
|
|
|
|
ST(0) = &PL_sv_no; |
|
5157
|
|
|
|
|
|
|
} |
|
5158
|
5008
|
|
|
|
|
|
XSRETURN(1); |
|
5159
|
|
|
|
|
|
|
} |
|
5160
|
|
|
|
|
|
|
|
|
5161
|
14126
|
|
|
|
|
|
XS_INTERNAL(xs_array_len) { |
|
5162
|
14126
|
|
|
|
|
|
dXSARGS; |
|
5163
|
14126
|
50
|
|
|
|
|
if (items != 1) croak("Usage: Func::Util::array_len($arrayref)"); |
|
5164
|
14126
|
|
|
|
|
|
SV *sv = ST(0); |
|
5165
|
14126
|
100
|
|
|
|
|
if (SvROK(sv) && SvTYPE(SvRV(sv)) == SVt_PVAV) { |
|
|
|
100
|
|
|
|
|
|
|
5166
|
13123
|
|
|
|
|
|
AV *av = (AV*)SvRV(sv); |
|
5167
|
13123
|
50
|
|
|
|
|
ST(0) = sv_2mortal(newSViv(AvFILL(av) + 1)); |
|
5168
|
|
|
|
|
|
|
} else { |
|
5169
|
1003
|
|
|
|
|
|
ST(0) = &PL_sv_undef; |
|
5170
|
|
|
|
|
|
|
} |
|
5171
|
14126
|
|
|
|
|
|
XSRETURN(1); |
|
5172
|
|
|
|
|
|
|
} |
|
5173
|
|
|
|
|
|
|
|
|
5174
|
4018
|
|
|
|
|
|
XS_INTERNAL(xs_hash_size) { |
|
5175
|
4018
|
|
|
|
|
|
dXSARGS; |
|
5176
|
4018
|
50
|
|
|
|
|
if (items != 1) croak("Usage: Func::Util::hash_size($hashref)"); |
|
5177
|
4018
|
|
|
|
|
|
SV *sv = ST(0); |
|
5178
|
4018
|
100
|
|
|
|
|
if (SvROK(sv) && SvTYPE(SvRV(sv)) == SVt_PVHV) { |
|
|
|
100
|
|
|
|
|
|
|
5179
|
3015
|
|
|
|
|
|
HV *hv = (HV*)SvRV(sv); |
|
5180
|
3015
|
50
|
|
|
|
|
ST(0) = sv_2mortal(newSViv(HvKEYS(hv))); |
|
5181
|
|
|
|
|
|
|
} else { |
|
5182
|
1003
|
|
|
|
|
|
ST(0) = &PL_sv_undef; |
|
5183
|
|
|
|
|
|
|
} |
|
5184
|
4018
|
|
|
|
|
|
XSRETURN(1); |
|
5185
|
|
|
|
|
|
|
} |
|
5186
|
|
|
|
|
|
|
|
|
5187
|
13114
|
|
|
|
|
|
XS_INTERNAL(xs_array_first) { |
|
5188
|
13114
|
|
|
|
|
|
dXSARGS; |
|
5189
|
13114
|
50
|
|
|
|
|
if (items != 1) croak("Usage: Func::Util::array_first($arrayref)"); |
|
5190
|
13114
|
|
|
|
|
|
SV *sv = ST(0); |
|
5191
|
26225
|
100
|
|
|
|
|
if (SvROK(sv) && SvTYPE(SvRV(sv)) == SVt_PVAV) { |
|
|
|
100
|
|
|
|
|
|
|
5192
|
13111
|
|
|
|
|
|
AV *av = (AV*)SvRV(sv); |
|
5193
|
13111
|
50
|
|
|
|
|
if (AvFILL(av) >= 0) { |
|
|
|
100
|
|
|
|
|
|
|
5194
|
12110
|
|
|
|
|
|
SV **elem = av_fetch(av, 0, 0); |
|
5195
|
12110
|
50
|
|
|
|
|
ST(0) = elem ? *elem : &PL_sv_undef; |
|
5196
|
|
|
|
|
|
|
} else { |
|
5197
|
1001
|
|
|
|
|
|
ST(0) = &PL_sv_undef; |
|
5198
|
|
|
|
|
|
|
} |
|
5199
|
|
|
|
|
|
|
} else { |
|
5200
|
3
|
|
|
|
|
|
ST(0) = &PL_sv_undef; |
|
5201
|
|
|
|
|
|
|
} |
|
5202
|
13114
|
|
|
|
|
|
XSRETURN(1); |
|
5203
|
|
|
|
|
|
|
} |
|
5204
|
|
|
|
|
|
|
|
|
5205
|
13113
|
|
|
|
|
|
XS_INTERNAL(xs_array_last) { |
|
5206
|
13113
|
|
|
|
|
|
dXSARGS; |
|
5207
|
13113
|
50
|
|
|
|
|
if (items != 1) croak("Usage: Func::Util::array_last($arrayref)"); |
|
5208
|
13113
|
|
|
|
|
|
SV *sv = ST(0); |
|
5209
|
26223
|
100
|
|
|
|
|
if (SvROK(sv) && SvTYPE(SvRV(sv)) == SVt_PVAV) { |
|
|
|
100
|
|
|
|
|
|
|
5210
|
13110
|
|
|
|
|
|
AV *av = (AV*)SvRV(sv); |
|
5211
|
13110
|
50
|
|
|
|
|
IV last_idx = AvFILL(av); |
|
5212
|
13110
|
100
|
|
|
|
|
if (last_idx >= 0) { |
|
5213
|
12109
|
|
|
|
|
|
SV **elem = av_fetch(av, last_idx, 0); |
|
5214
|
12109
|
50
|
|
|
|
|
ST(0) = elem ? *elem : &PL_sv_undef; |
|
5215
|
|
|
|
|
|
|
} else { |
|
5216
|
1001
|
|
|
|
|
|
ST(0) = &PL_sv_undef; |
|
5217
|
|
|
|
|
|
|
} |
|
5218
|
|
|
|
|
|
|
} else { |
|
5219
|
3
|
|
|
|
|
|
ST(0) = &PL_sv_undef; |
|
5220
|
|
|
|
|
|
|
} |
|
5221
|
13113
|
|
|
|
|
|
XSRETURN(1); |
|
5222
|
|
|
|
|
|
|
} |
|
5223
|
|
|
|
|
|
|
|
|
5224
|
|
|
|
|
|
|
/* ============================================ |
|
5225
|
|
|
|
|
|
|
String manipulation XS fallbacks |
|
5226
|
|
|
|
|
|
|
============================================ */ |
|
5227
|
|
|
|
|
|
|
|
|
5228
|
20141
|
|
|
|
|
|
XS_INTERNAL(xs_trim) { |
|
5229
|
20141
|
|
|
|
|
|
dXSARGS; |
|
5230
|
20141
|
50
|
|
|
|
|
if (items != 1) croak("Usage: Func::Util::trim($string)"); |
|
5231
|
|
|
|
|
|
|
|
|
5232
|
20141
|
|
|
|
|
|
SV *sv = ST(0); |
|
5233
|
20141
|
100
|
|
|
|
|
if (!SvOK(sv)) { |
|
5234
|
1001
|
|
|
|
|
|
ST(0) = &PL_sv_undef; |
|
5235
|
1001
|
|
|
|
|
|
XSRETURN(1); |
|
5236
|
|
|
|
|
|
|
} |
|
5237
|
|
|
|
|
|
|
|
|
5238
|
|
|
|
|
|
|
STRLEN len; |
|
5239
|
19140
|
|
|
|
|
|
const char *str = SvPV(sv, len); |
|
5240
|
19140
|
|
|
|
|
|
const char *start = str; |
|
5241
|
19140
|
|
|
|
|
|
const char *end = str + len; |
|
5242
|
|
|
|
|
|
|
|
|
5243
|
|
|
|
|
|
|
/* Skip leading whitespace */ |
|
5244
|
50410
|
100
|
|
|
|
|
while (start < end && isSPACE(*start)) { |
|
|
|
100
|
|
|
|
|
|
|
5245
|
31270
|
|
|
|
|
|
start++; |
|
5246
|
|
|
|
|
|
|
} |
|
5247
|
|
|
|
|
|
|
|
|
5248
|
|
|
|
|
|
|
/* Skip trailing whitespace */ |
|
5249
|
47401
|
100
|
|
|
|
|
while (end > start && isSPACE(*(end - 1))) { |
|
|
|
100
|
|
|
|
|
|
|
5250
|
28261
|
|
|
|
|
|
end--; |
|
5251
|
|
|
|
|
|
|
} |
|
5252
|
|
|
|
|
|
|
|
|
5253
|
19140
|
|
|
|
|
|
ST(0) = sv_2mortal(newSVpvn(start, end - start)); |
|
5254
|
19140
|
|
|
|
|
|
XSRETURN(1); |
|
5255
|
|
|
|
|
|
|
} |
|
5256
|
|
|
|
|
|
|
|
|
5257
|
4013
|
|
|
|
|
|
XS_INTERNAL(xs_ltrim) { |
|
5258
|
4013
|
|
|
|
|
|
dXSARGS; |
|
5259
|
4013
|
50
|
|
|
|
|
if (items != 1) croak("Usage: Func::Util::ltrim($string)"); |
|
5260
|
|
|
|
|
|
|
|
|
5261
|
4013
|
|
|
|
|
|
SV *sv = ST(0); |
|
5262
|
4013
|
100
|
|
|
|
|
if (!SvOK(sv)) { |
|
5263
|
1
|
|
|
|
|
|
ST(0) = &PL_sv_undef; |
|
5264
|
1
|
|
|
|
|
|
XSRETURN(1); |
|
5265
|
|
|
|
|
|
|
} |
|
5266
|
|
|
|
|
|
|
|
|
5267
|
|
|
|
|
|
|
STRLEN len; |
|
5268
|
4012
|
|
|
|
|
|
const char *str = SvPV(sv, len); |
|
5269
|
4012
|
|
|
|
|
|
const char *start = str; |
|
5270
|
4012
|
|
|
|
|
|
const char *end = str + len; |
|
5271
|
|
|
|
|
|
|
|
|
5272
|
8031
|
100
|
|
|
|
|
while (start < end && isSPACE(*start)) { |
|
|
|
100
|
|
|
|
|
|
|
5273
|
4019
|
|
|
|
|
|
start++; |
|
5274
|
|
|
|
|
|
|
} |
|
5275
|
|
|
|
|
|
|
|
|
5276
|
4012
|
|
|
|
|
|
ST(0) = sv_2mortal(newSVpvn(start, end - start)); |
|
5277
|
4012
|
|
|
|
|
|
XSRETURN(1); |
|
5278
|
|
|
|
|
|
|
} |
|
5279
|
|
|
|
|
|
|
|
|
5280
|
4013
|
|
|
|
|
|
XS_INTERNAL(xs_rtrim) { |
|
5281
|
4013
|
|
|
|
|
|
dXSARGS; |
|
5282
|
4013
|
50
|
|
|
|
|
if (items != 1) croak("Usage: Func::Util::rtrim($string)"); |
|
5283
|
|
|
|
|
|
|
|
|
5284
|
4013
|
|
|
|
|
|
SV *sv = ST(0); |
|
5285
|
4013
|
100
|
|
|
|
|
if (!SvOK(sv)) { |
|
5286
|
1
|
|
|
|
|
|
ST(0) = &PL_sv_undef; |
|
5287
|
1
|
|
|
|
|
|
XSRETURN(1); |
|
5288
|
|
|
|
|
|
|
} |
|
5289
|
|
|
|
|
|
|
|
|
5290
|
|
|
|
|
|
|
STRLEN len; |
|
5291
|
4012
|
|
|
|
|
|
const char *str = SvPV(sv, len); |
|
5292
|
4012
|
|
|
|
|
|
const char *end = str + len; |
|
5293
|
|
|
|
|
|
|
|
|
5294
|
8026
|
100
|
|
|
|
|
while (end > str && isSPACE(*(end - 1))) { |
|
|
|
100
|
|
|
|
|
|
|
5295
|
4014
|
|
|
|
|
|
end--; |
|
5296
|
|
|
|
|
|
|
} |
|
5297
|
|
|
|
|
|
|
|
|
5298
|
4012
|
|
|
|
|
|
ST(0) = sv_2mortal(newSVpvn(str, end - str)); |
|
5299
|
4012
|
|
|
|
|
|
XSRETURN(1); |
|
5300
|
|
|
|
|
|
|
} |
|
5301
|
|
|
|
|
|
|
|
|
5302
|
|
|
|
|
|
|
/* ============================================ |
|
5303
|
|
|
|
|
|
|
Conditional XS fallbacks |
|
5304
|
|
|
|
|
|
|
============================================ */ |
|
5305
|
|
|
|
|
|
|
|
|
5306
|
10028
|
|
|
|
|
|
XS_INTERNAL(xs_maybe) { |
|
5307
|
10028
|
|
|
|
|
|
dXSARGS; |
|
5308
|
10028
|
50
|
|
|
|
|
if (items != 2) croak("Usage: Func::Util::maybe($value, $then)"); |
|
5309
|
|
|
|
|
|
|
|
|
5310
|
10028
|
|
|
|
|
|
SV *val = ST(0); |
|
5311
|
10028
|
100
|
|
|
|
|
if (SvOK(val)) { |
|
5312
|
8023
|
|
|
|
|
|
ST(0) = ST(1); |
|
5313
|
|
|
|
|
|
|
} else { |
|
5314
|
2005
|
|
|
|
|
|
ST(0) = &PL_sv_undef; |
|
5315
|
|
|
|
|
|
|
} |
|
5316
|
10028
|
|
|
|
|
|
XSRETURN(1); |
|
5317
|
|
|
|
|
|
|
} |
|
5318
|
|
|
|
|
|
|
|
|
5319
|
|
|
|
|
|
|
/* ============================================ |
|
5320
|
|
|
|
|
|
|
Numeric XS fallbacks |
|
5321
|
|
|
|
|
|
|
============================================ */ |
|
5322
|
|
|
|
|
|
|
|
|
5323
|
21131
|
|
|
|
|
|
XS_INTERNAL(xs_sign) { |
|
5324
|
21131
|
|
|
|
|
|
dXSARGS; |
|
5325
|
21131
|
50
|
|
|
|
|
if (items != 1) croak("Usage: Func::Util::sign($number)"); |
|
5326
|
|
|
|
|
|
|
|
|
5327
|
21131
|
|
|
|
|
|
SV *sv = ST(0); |
|
5328
|
21131
|
100
|
|
|
|
|
if (!SvNIOK(sv) && !looks_like_number(sv)) { |
|
|
|
100
|
|
|
|
|
|
|
5329
|
2
|
|
|
|
|
|
ST(0) = &PL_sv_undef; |
|
5330
|
2
|
|
|
|
|
|
XSRETURN(1); |
|
5331
|
|
|
|
|
|
|
} |
|
5332
|
|
|
|
|
|
|
|
|
5333
|
21129
|
|
|
|
|
|
NV nv = SvNV(sv); |
|
5334
|
21129
|
100
|
|
|
|
|
if (nv > 0) { |
|
5335
|
4011
|
|
|
|
|
|
ST(0) = sv_2mortal(newSViv(1)); |
|
5336
|
17118
|
100
|
|
|
|
|
} else if (nv < 0) { |
|
5337
|
14113
|
|
|
|
|
|
ST(0) = sv_2mortal(newSViv(-1)); |
|
5338
|
|
|
|
|
|
|
} else { |
|
5339
|
3005
|
|
|
|
|
|
ST(0) = sv_2mortal(newSViv(0)); |
|
5340
|
|
|
|
|
|
|
} |
|
5341
|
21129
|
|
|
|
|
|
XSRETURN(1); |
|
5342
|
|
|
|
|
|
|
} |
|
5343
|
|
|
|
|
|
|
|
|
5344
|
15116
|
|
|
|
|
|
XS_INTERNAL(xs_min2) { |
|
5345
|
15116
|
|
|
|
|
|
dXSARGS; |
|
5346
|
15116
|
50
|
|
|
|
|
if (items != 2) croak("Usage: Func::Util::min2($a, $b)"); |
|
5347
|
|
|
|
|
|
|
|
|
5348
|
15116
|
|
|
|
|
|
NV a = SvNV(ST(0)); |
|
5349
|
15116
|
|
|
|
|
|
NV b = SvNV(ST(1)); |
|
5350
|
|
|
|
|
|
|
|
|
5351
|
15116
|
100
|
|
|
|
|
ST(0) = a <= b ? ST(0) : ST(1); |
|
5352
|
15116
|
|
|
|
|
|
XSRETURN(1); |
|
5353
|
|
|
|
|
|
|
} |
|
5354
|
|
|
|
|
|
|
|
|
5355
|
15116
|
|
|
|
|
|
XS_INTERNAL(xs_max2) { |
|
5356
|
15116
|
|
|
|
|
|
dXSARGS; |
|
5357
|
15116
|
50
|
|
|
|
|
if (items != 2) croak("Usage: Func::Util::max2($a, $b)"); |
|
5358
|
|
|
|
|
|
|
|
|
5359
|
15116
|
|
|
|
|
|
NV a = SvNV(ST(0)); |
|
5360
|
15116
|
|
|
|
|
|
NV b = SvNV(ST(1)); |
|
5361
|
|
|
|
|
|
|
|
|
5362
|
15116
|
100
|
|
|
|
|
ST(0) = a >= b ? ST(0) : ST(1); |
|
5363
|
15116
|
|
|
|
|
|
XSRETURN(1); |
|
5364
|
|
|
|
|
|
|
} |
|
5365
|
|
|
|
|
|
|
|
|
5366
|
|
|
|
|
|
|
/* ============================================ |
|
5367
|
|
|
|
|
|
|
Named callback loop functions |
|
5368
|
|
|
|
|
|
|
These accept a callback name instead of coderef |
|
5369
|
|
|
|
|
|
|
============================================ */ |
|
5370
|
|
|
|
|
|
|
|
|
5371
|
|
|
|
|
|
|
/* any_cb(\@list, ':predicate') - true if any element matches */ |
|
5372
|
11130
|
|
|
|
|
|
XS_INTERNAL(xs_any_cb) { |
|
5373
|
11130
|
|
|
|
|
|
dXSARGS; |
|
5374
|
11130
|
50
|
|
|
|
|
if (items != 2) croak("Usage: Func::Util::any_cb(\\@list, $callback_name)"); |
|
5375
|
|
|
|
|
|
|
|
|
5376
|
11130
|
|
|
|
|
|
SV *list_sv = ST(0); |
|
5377
|
11130
|
100
|
|
|
|
|
if (!SvROK(list_sv) || SvTYPE(SvRV(list_sv)) != SVt_PVAV) { |
|
|
|
100
|
|
|
|
|
|
|
5378
|
3
|
|
|
|
|
|
croak("Func::Util::any_cb: first argument must be an arrayref"); |
|
5379
|
|
|
|
|
|
|
} |
|
5380
|
11127
|
|
|
|
|
|
AV *list = (AV*)SvRV(list_sv); |
|
5381
|
|
|
|
|
|
|
|
|
5382
|
|
|
|
|
|
|
STRLEN name_len; |
|
5383
|
11127
|
|
|
|
|
|
const char *name = SvPV(ST(1), name_len); |
|
5384
|
|
|
|
|
|
|
|
|
5385
|
11127
|
|
|
|
|
|
RegisteredCallback *cb = get_registered_callback(aTHX_ name); |
|
5386
|
11127
|
100
|
|
|
|
|
if (!cb) { |
|
5387
|
2
|
|
|
|
|
|
croak("Func::Util::any_cb: unknown callback '%s'", name); |
|
5388
|
|
|
|
|
|
|
} |
|
5389
|
11125
|
100
|
|
|
|
|
if (!cb->predicate && !cb->perl_callback) { |
|
|
|
50
|
|
|
|
|
|
|
5390
|
0
|
|
|
|
|
|
croak("Func::Util::any_cb: callback '%s' is not a predicate", name); |
|
5391
|
|
|
|
|
|
|
} |
|
5392
|
|
|
|
|
|
|
|
|
5393
|
11125
|
|
|
|
|
|
IV len = av_len(list) + 1; |
|
5394
|
|
|
|
|
|
|
IV i; |
|
5395
|
|
|
|
|
|
|
|
|
5396
|
11125
|
100
|
|
|
|
|
if (cb->predicate) { |
|
5397
|
|
|
|
|
|
|
/* Fast C path */ |
|
5398
|
35174
|
100
|
|
|
|
|
for (i = 0; i < len; i++) { |
|
5399
|
35172
|
|
|
|
|
|
SV **svp = av_fetch(list, i, 0); |
|
5400
|
35172
|
50
|
|
|
|
|
if (svp && cb->predicate(aTHX_ *svp)) { |
|
|
|
100
|
|
|
|
|
|
|
5401
|
10123
|
|
|
|
|
|
XSRETURN_YES; |
|
5402
|
|
|
|
|
|
|
} |
|
5403
|
|
|
|
|
|
|
} |
|
5404
|
1000
|
50
|
|
|
|
|
} else if (cb->perl_callback) { |
|
5405
|
|
|
|
|
|
|
/* Perl callback fallback - use isolated stack scope */ |
|
5406
|
7000
|
50
|
|
|
|
|
for (i = 0; i < len; i++) { |
|
5407
|
7000
|
|
|
|
|
|
SV **svp = av_fetch(list, i, 0); |
|
5408
|
7000
|
50
|
|
|
|
|
if (!svp) continue; |
|
5409
|
|
|
|
|
|
|
|
|
5410
|
7000
|
|
|
|
|
|
bool matches = FALSE; |
|
5411
|
|
|
|
|
|
|
{ |
|
5412
|
7000
|
|
|
|
|
|
dSP; |
|
5413
|
|
|
|
|
|
|
int count; |
|
5414
|
|
|
|
|
|
|
SV *result; |
|
5415
|
|
|
|
|
|
|
|
|
5416
|
7000
|
|
|
|
|
|
ENTER; |
|
5417
|
7000
|
|
|
|
|
|
SAVETMPS; |
|
5418
|
|
|
|
|
|
|
|
|
5419
|
7000
|
50
|
|
|
|
|
PUSHMARK(SP); |
|
5420
|
7000
|
50
|
|
|
|
|
XPUSHs(*svp); |
|
5421
|
7000
|
|
|
|
|
|
PUTBACK; |
|
5422
|
|
|
|
|
|
|
|
|
5423
|
7000
|
|
|
|
|
|
count = call_sv(cb->perl_callback, G_SCALAR); |
|
5424
|
|
|
|
|
|
|
|
|
5425
|
7000
|
|
|
|
|
|
SPAGAIN; |
|
5426
|
7000
|
50
|
|
|
|
|
if (count > 0) { |
|
5427
|
7000
|
|
|
|
|
|
result = POPs; |
|
5428
|
7000
|
|
|
|
|
|
matches = SvTRUE(result); |
|
5429
|
|
|
|
|
|
|
} |
|
5430
|
7000
|
|
|
|
|
|
PUTBACK; |
|
5431
|
|
|
|
|
|
|
|
|
5432
|
7000
|
50
|
|
|
|
|
FREETMPS; |
|
5433
|
7000
|
|
|
|
|
|
LEAVE; |
|
5434
|
|
|
|
|
|
|
} |
|
5435
|
|
|
|
|
|
|
|
|
5436
|
7000
|
100
|
|
|
|
|
if (matches) { |
|
5437
|
1000
|
|
|
|
|
|
XSRETURN_YES; |
|
5438
|
|
|
|
|
|
|
} |
|
5439
|
|
|
|
|
|
|
} |
|
5440
|
|
|
|
|
|
|
} |
|
5441
|
|
|
|
|
|
|
|
|
5442
|
2
|
|
|
|
|
|
XSRETURN_NO; |
|
5443
|
|
|
|
|
|
|
} |
|
5444
|
|
|
|
|
|
|
|
|
5445
|
|
|
|
|
|
|
/* all_cb(\@list, ':predicate') - true if all elements match */ |
|
5446
|
3128
|
|
|
|
|
|
XS_INTERNAL(xs_all_cb) { |
|
5447
|
3128
|
|
|
|
|
|
dXSARGS; |
|
5448
|
3128
|
50
|
|
|
|
|
if (items != 2) croak("Usage: Func::Util::all_cb(\\@list, $callback_name)"); |
|
5449
|
|
|
|
|
|
|
|
|
5450
|
3128
|
|
|
|
|
|
SV *list_sv = ST(0); |
|
5451
|
3128
|
50
|
|
|
|
|
if (!SvROK(list_sv) || SvTYPE(SvRV(list_sv)) != SVt_PVAV) { |
|
|
|
50
|
|
|
|
|
|
|
5452
|
0
|
|
|
|
|
|
croak("Func::Util::all_cb: first argument must be an arrayref"); |
|
5453
|
|
|
|
|
|
|
} |
|
5454
|
3128
|
|
|
|
|
|
AV *list = (AV*)SvRV(list_sv); |
|
5455
|
|
|
|
|
|
|
|
|
5456
|
|
|
|
|
|
|
STRLEN name_len; |
|
5457
|
3128
|
|
|
|
|
|
const char *name = SvPV(ST(1), name_len); |
|
5458
|
|
|
|
|
|
|
|
|
5459
|
3128
|
|
|
|
|
|
RegisteredCallback *cb = get_registered_callback(aTHX_ name); |
|
5460
|
3128
|
100
|
|
|
|
|
if (!cb) { |
|
5461
|
1
|
|
|
|
|
|
croak("Func::Util::all_cb: unknown callback '%s'", name); |
|
5462
|
|
|
|
|
|
|
} |
|
5463
|
3127
|
50
|
|
|
|
|
if (!cb->predicate && !cb->perl_callback) { |
|
|
|
0
|
|
|
|
|
|
|
5464
|
0
|
|
|
|
|
|
croak("Func::Util::all_cb: callback '%s' is not a predicate", name); |
|
5465
|
|
|
|
|
|
|
} |
|
5466
|
|
|
|
|
|
|
|
|
5467
|
3127
|
|
|
|
|
|
IV len = av_len(list) + 1; |
|
5468
|
|
|
|
|
|
|
IV i; |
|
5469
|
|
|
|
|
|
|
|
|
5470
|
|
|
|
|
|
|
/* Empty list returns true (vacuous truth) */ |
|
5471
|
3127
|
100
|
|
|
|
|
if (len == 0) { |
|
5472
|
5
|
|
|
|
|
|
XSRETURN_YES; |
|
5473
|
|
|
|
|
|
|
} |
|
5474
|
|
|
|
|
|
|
|
|
5475
|
3122
|
50
|
|
|
|
|
if (cb->predicate) { |
|
5476
|
1021228
|
100
|
|
|
|
|
for (i = 0; i < len; i++) { |
|
5477
|
1019114
|
|
|
|
|
|
SV **svp = av_fetch(list, i, 0); |
|
5478
|
1019114
|
50
|
|
|
|
|
if (!svp || !cb->predicate(aTHX_ *svp)) { |
|
|
|
100
|
|
|
|
|
|
|
5479
|
1008
|
|
|
|
|
|
XSRETURN_NO; |
|
5480
|
|
|
|
|
|
|
} |
|
5481
|
|
|
|
|
|
|
} |
|
5482
|
0
|
0
|
|
|
|
|
} else if (cb->perl_callback) { |
|
5483
|
0
|
0
|
|
|
|
|
for (i = 0; i < len; i++) { |
|
5484
|
0
|
|
|
|
|
|
SV **svp = av_fetch(list, i, 0); |
|
5485
|
0
|
0
|
|
|
|
|
if (!svp) { XSRETURN_NO; } |
|
5486
|
0
|
|
|
|
|
|
bool matches = FALSE; |
|
5487
|
|
|
|
|
|
|
{ |
|
5488
|
0
|
|
|
|
|
|
dSP; |
|
5489
|
|
|
|
|
|
|
int count; |
|
5490
|
|
|
|
|
|
|
SV *result; |
|
5491
|
0
|
|
|
|
|
|
ENTER; SAVETMPS; |
|
5492
|
0
|
0
|
|
|
|
|
PUSHMARK(SP); |
|
5493
|
0
|
0
|
|
|
|
|
XPUSHs(*svp); |
|
5494
|
0
|
|
|
|
|
|
PUTBACK; |
|
5495
|
0
|
|
|
|
|
|
count = call_sv(cb->perl_callback, G_SCALAR); |
|
5496
|
0
|
|
|
|
|
|
SPAGAIN; |
|
5497
|
0
|
0
|
|
|
|
|
if (count > 0) { |
|
5498
|
0
|
|
|
|
|
|
result = POPs; |
|
5499
|
0
|
|
|
|
|
|
matches = SvTRUE(result); |
|
5500
|
|
|
|
|
|
|
} |
|
5501
|
0
|
|
|
|
|
|
PUTBACK; |
|
5502
|
0
|
0
|
|
|
|
|
FREETMPS; LEAVE; |
|
5503
|
|
|
|
|
|
|
} |
|
5504
|
0
|
0
|
|
|
|
|
if (!matches) { |
|
5505
|
0
|
|
|
|
|
|
XSRETURN_NO; |
|
5506
|
|
|
|
|
|
|
} |
|
5507
|
|
|
|
|
|
|
} |
|
5508
|
|
|
|
|
|
|
} |
|
5509
|
|
|
|
|
|
|
|
|
5510
|
2114
|
|
|
|
|
|
XSRETURN_YES; |
|
5511
|
|
|
|
|
|
|
} |
|
5512
|
|
|
|
|
|
|
|
|
5513
|
|
|
|
|
|
|
/* none_cb(\@list, ':predicate') - true if no elements match */ |
|
5514
|
2012
|
|
|
|
|
|
XS_INTERNAL(xs_none_cb) { |
|
5515
|
2012
|
|
|
|
|
|
dXSARGS; |
|
5516
|
2012
|
50
|
|
|
|
|
if (items != 2) croak("Usage: Func::Util::none_cb(\\@list, $callback_name)"); |
|
5517
|
|
|
|
|
|
|
|
|
5518
|
2012
|
|
|
|
|
|
SV *list_sv = ST(0); |
|
5519
|
2012
|
50
|
|
|
|
|
if (!SvROK(list_sv) || SvTYPE(SvRV(list_sv)) != SVt_PVAV) { |
|
|
|
50
|
|
|
|
|
|
|
5520
|
0
|
|
|
|
|
|
croak("Func::Util::none_cb: first argument must be an arrayref"); |
|
5521
|
|
|
|
|
|
|
} |
|
5522
|
2012
|
|
|
|
|
|
AV *list = (AV*)SvRV(list_sv); |
|
5523
|
|
|
|
|
|
|
|
|
5524
|
|
|
|
|
|
|
STRLEN name_len; |
|
5525
|
2012
|
|
|
|
|
|
const char *name = SvPV(ST(1), name_len); |
|
5526
|
|
|
|
|
|
|
|
|
5527
|
2012
|
|
|
|
|
|
RegisteredCallback *cb = get_registered_callback(aTHX_ name); |
|
5528
|
2012
|
100
|
|
|
|
|
if (!cb) { |
|
5529
|
1
|
|
|
|
|
|
croak("Func::Util::none_cb: unknown callback '%s'", name); |
|
5530
|
|
|
|
|
|
|
} |
|
5531
|
2011
|
50
|
|
|
|
|
if (!cb->predicate && !cb->perl_callback) { |
|
|
|
0
|
|
|
|
|
|
|
5532
|
0
|
|
|
|
|
|
croak("Func::Util::none_cb: callback '%s' is not a predicate", name); |
|
5533
|
|
|
|
|
|
|
} |
|
5534
|
|
|
|
|
|
|
|
|
5535
|
2011
|
|
|
|
|
|
IV len = av_len(list) + 1; |
|
5536
|
|
|
|
|
|
|
IV i; |
|
5537
|
|
|
|
|
|
|
|
|
5538
|
2011
|
50
|
|
|
|
|
if (cb->predicate) { |
|
5539
|
14027
|
100
|
|
|
|
|
for (i = 0; i < len; i++) { |
|
5540
|
13021
|
|
|
|
|
|
SV **svp = av_fetch(list, i, 0); |
|
5541
|
13021
|
50
|
|
|
|
|
if (svp && cb->predicate(aTHX_ *svp)) { |
|
|
|
100
|
|
|
|
|
|
|
5542
|
1005
|
|
|
|
|
|
XSRETURN_NO; |
|
5543
|
|
|
|
|
|
|
} |
|
5544
|
|
|
|
|
|
|
} |
|
5545
|
0
|
0
|
|
|
|
|
} else if (cb->perl_callback) { |
|
5546
|
0
|
0
|
|
|
|
|
for (i = 0; i < len; i++) { |
|
5547
|
0
|
|
|
|
|
|
SV **svp = av_fetch(list, i, 0); |
|
5548
|
0
|
0
|
|
|
|
|
if (!svp) continue; |
|
5549
|
0
|
|
|
|
|
|
bool matches = FALSE; |
|
5550
|
|
|
|
|
|
|
{ |
|
5551
|
0
|
|
|
|
|
|
dSP; |
|
5552
|
|
|
|
|
|
|
int count; |
|
5553
|
|
|
|
|
|
|
SV *result; |
|
5554
|
0
|
|
|
|
|
|
ENTER; SAVETMPS; |
|
5555
|
0
|
0
|
|
|
|
|
PUSHMARK(SP); |
|
5556
|
0
|
0
|
|
|
|
|
XPUSHs(*svp); |
|
5557
|
0
|
|
|
|
|
|
PUTBACK; |
|
5558
|
0
|
|
|
|
|
|
count = call_sv(cb->perl_callback, G_SCALAR); |
|
5559
|
0
|
|
|
|
|
|
SPAGAIN; |
|
5560
|
0
|
0
|
|
|
|
|
if (count > 0) { |
|
5561
|
0
|
|
|
|
|
|
result = POPs; |
|
5562
|
0
|
|
|
|
|
|
matches = SvTRUE(result); |
|
5563
|
|
|
|
|
|
|
} |
|
5564
|
0
|
|
|
|
|
|
PUTBACK; |
|
5565
|
0
|
0
|
|
|
|
|
FREETMPS; LEAVE; |
|
5566
|
|
|
|
|
|
|
} |
|
5567
|
0
|
0
|
|
|
|
|
if (matches) { |
|
5568
|
0
|
|
|
|
|
|
XSRETURN_NO; |
|
5569
|
|
|
|
|
|
|
} |
|
5570
|
|
|
|
|
|
|
} |
|
5571
|
|
|
|
|
|
|
} |
|
5572
|
|
|
|
|
|
|
|
|
5573
|
1006
|
|
|
|
|
|
XSRETURN_YES; |
|
5574
|
|
|
|
|
|
|
} |
|
5575
|
|
|
|
|
|
|
|
|
5576
|
|
|
|
|
|
|
/* first_cb(\@list, ':predicate') - first matching element */ |
|
5577
|
5043
|
|
|
|
|
|
XS_INTERNAL(xs_first_cb) { |
|
5578
|
5043
|
|
|
|
|
|
dXSARGS; |
|
5579
|
5043
|
50
|
|
|
|
|
if (items != 2) croak("Usage: Func::Util::first_cb(\\@list, $callback_name)"); |
|
5580
|
|
|
|
|
|
|
|
|
5581
|
5043
|
|
|
|
|
|
SV *list_sv = ST(0); |
|
5582
|
5043
|
50
|
|
|
|
|
if (!SvROK(list_sv) || SvTYPE(SvRV(list_sv)) != SVt_PVAV) { |
|
|
|
50
|
|
|
|
|
|
|
5583
|
0
|
|
|
|
|
|
croak("Func::Util::first_cb: first argument must be an arrayref"); |
|
5584
|
|
|
|
|
|
|
} |
|
5585
|
5043
|
|
|
|
|
|
AV *list = (AV*)SvRV(list_sv); |
|
5586
|
|
|
|
|
|
|
|
|
5587
|
|
|
|
|
|
|
STRLEN name_len; |
|
5588
|
5043
|
|
|
|
|
|
const char *name = SvPV(ST(1), name_len); |
|
5589
|
|
|
|
|
|
|
|
|
5590
|
5043
|
|
|
|
|
|
RegisteredCallback *cb = get_registered_callback(aTHX_ name); |
|
5591
|
5043
|
100
|
|
|
|
|
if (!cb) { |
|
5592
|
1
|
|
|
|
|
|
croak("Func::Util::first_cb: unknown callback '%s'", name); |
|
5593
|
|
|
|
|
|
|
} |
|
5594
|
5042
|
100
|
|
|
|
|
if (!cb->predicate && !cb->perl_callback) { |
|
|
|
50
|
|
|
|
|
|
|
5595
|
0
|
|
|
|
|
|
croak("Func::Util::first_cb: callback '%s' is not a predicate", name); |
|
5596
|
|
|
|
|
|
|
} |
|
5597
|
|
|
|
|
|
|
|
|
5598
|
5042
|
|
|
|
|
|
IV len = av_len(list) + 1; |
|
5599
|
|
|
|
|
|
|
IV i; |
|
5600
|
|
|
|
|
|
|
|
|
5601
|
5042
|
100
|
|
|
|
|
if (cb->predicate) { |
|
5602
|
10082
|
100
|
|
|
|
|
for (i = 0; i < len; i++) { |
|
5603
|
10080
|
|
|
|
|
|
SV **svp = av_fetch(list, i, 0); |
|
5604
|
10080
|
50
|
|
|
|
|
if (svp && cb->predicate(aTHX_ *svp)) { |
|
|
|
100
|
|
|
|
|
|
|
5605
|
4030
|
|
|
|
|
|
ST(0) = *svp; |
|
5606
|
4030
|
|
|
|
|
|
XSRETURN(1); |
|
5607
|
|
|
|
|
|
|
} |
|
5608
|
|
|
|
|
|
|
} |
|
5609
|
1010
|
50
|
|
|
|
|
} else if (cb->perl_callback) { |
|
5610
|
7014
|
100
|
|
|
|
|
for (i = 0; i < len; i++) { |
|
5611
|
7012
|
|
|
|
|
|
SV **svp = av_fetch(list, i, 0); |
|
5612
|
7012
|
50
|
|
|
|
|
if (!svp) continue; |
|
5613
|
7012
|
|
|
|
|
|
bool matches = FALSE; |
|
5614
|
|
|
|
|
|
|
{ |
|
5615
|
7012
|
|
|
|
|
|
dSP; |
|
5616
|
|
|
|
|
|
|
int count; |
|
5617
|
|
|
|
|
|
|
SV *result; |
|
5618
|
7012
|
|
|
|
|
|
ENTER; SAVETMPS; |
|
5619
|
7012
|
50
|
|
|
|
|
PUSHMARK(SP); |
|
5620
|
7012
|
50
|
|
|
|
|
XPUSHs(*svp); |
|
5621
|
7012
|
|
|
|
|
|
PUTBACK; |
|
5622
|
7012
|
|
|
|
|
|
count = call_sv(cb->perl_callback, G_SCALAR); |
|
5623
|
7012
|
|
|
|
|
|
SPAGAIN; |
|
5624
|
7012
|
50
|
|
|
|
|
if (count > 0) { |
|
5625
|
7012
|
|
|
|
|
|
result = POPs; |
|
5626
|
7012
|
|
|
|
|
|
matches = SvTRUE(result); |
|
5627
|
|
|
|
|
|
|
} |
|
5628
|
7012
|
|
|
|
|
|
PUTBACK; |
|
5629
|
7012
|
50
|
|
|
|
|
FREETMPS; LEAVE; |
|
5630
|
|
|
|
|
|
|
} |
|
5631
|
7012
|
100
|
|
|
|
|
if (matches) { |
|
5632
|
1008
|
|
|
|
|
|
ST(0) = *svp; |
|
5633
|
1008
|
|
|
|
|
|
XSRETURN(1); |
|
5634
|
|
|
|
|
|
|
} |
|
5635
|
|
|
|
|
|
|
} |
|
5636
|
|
|
|
|
|
|
} |
|
5637
|
|
|
|
|
|
|
|
|
5638
|
4
|
|
|
|
|
|
XSRETURN_UNDEF; |
|
5639
|
|
|
|
|
|
|
} |
|
5640
|
|
|
|
|
|
|
|
|
5641
|
|
|
|
|
|
|
/* grep_cb(\@list, ':predicate') - all matching elements */ |
|
5642
|
3037
|
|
|
|
|
|
XS_INTERNAL(xs_grep_cb) { |
|
5643
|
3037
|
|
|
|
|
|
dXSARGS; |
|
5644
|
3037
|
50
|
|
|
|
|
if (items != 2) croak("Usage: Func::Util::grep_cb(\\@list, $callback_name)"); |
|
5645
|
|
|
|
|
|
|
|
|
5646
|
3037
|
|
|
|
|
|
SV *list_sv = ST(0); |
|
5647
|
3037
|
50
|
|
|
|
|
if (!SvROK(list_sv) || SvTYPE(SvRV(list_sv)) != SVt_PVAV) { |
|
|
|
50
|
|
|
|
|
|
|
5648
|
0
|
|
|
|
|
|
croak("Func::Util::grep_cb: first argument must be an arrayref"); |
|
5649
|
|
|
|
|
|
|
} |
|
5650
|
3037
|
|
|
|
|
|
AV *list = (AV*)SvRV(list_sv); |
|
5651
|
|
|
|
|
|
|
|
|
5652
|
|
|
|
|
|
|
STRLEN name_len; |
|
5653
|
3037
|
|
|
|
|
|
const char *name = SvPV(ST(1), name_len); |
|
5654
|
|
|
|
|
|
|
|
|
5655
|
3037
|
|
|
|
|
|
RegisteredCallback *cb = get_registered_callback(aTHX_ name); |
|
5656
|
3037
|
100
|
|
|
|
|
if (!cb) { |
|
5657
|
1
|
|
|
|
|
|
croak("Func::Util::grep_cb: unknown callback '%s'", name); |
|
5658
|
|
|
|
|
|
|
} |
|
5659
|
3036
|
100
|
|
|
|
|
if (!cb->predicate && !cb->perl_callback) { |
|
|
|
50
|
|
|
|
|
|
|
5660
|
0
|
|
|
|
|
|
croak("Func::Util::grep_cb: callback '%s' is not a predicate", name); |
|
5661
|
|
|
|
|
|
|
} |
|
5662
|
|
|
|
|
|
|
|
|
5663
|
3036
|
|
|
|
|
|
IV len = av_len(list) + 1; |
|
5664
|
|
|
|
|
|
|
IV i; |
|
5665
|
3036
|
|
|
|
|
|
IV count = 0; |
|
5666
|
|
|
|
|
|
|
|
|
5667
|
|
|
|
|
|
|
/* Collect matching elements in a temporary array first */ |
|
5668
|
3036
|
|
|
|
|
|
AV *results = newAV(); |
|
5669
|
3036
|
|
|
|
|
|
sv_2mortal((SV*)results); |
|
5670
|
|
|
|
|
|
|
|
|
5671
|
3036
|
100
|
|
|
|
|
if (cb->predicate) { |
|
5672
|
30175
|
100
|
|
|
|
|
for (i = 0; i < len; i++) { |
|
5673
|
27151
|
|
|
|
|
|
SV **svp = av_fetch(list, i, 0); |
|
5674
|
27151
|
50
|
|
|
|
|
if (svp && cb->predicate(aTHX_ *svp)) { |
|
|
|
100
|
|
|
|
|
|
|
5675
|
13075
|
|
|
|
|
|
av_push(results, SvREFCNT_inc(*svp)); |
|
5676
|
13075
|
|
|
|
|
|
count++; |
|
5677
|
|
|
|
|
|
|
} |
|
5678
|
|
|
|
|
|
|
} |
|
5679
|
12
|
50
|
|
|
|
|
} else if (cb->perl_callback) { |
|
5680
|
88
|
100
|
|
|
|
|
for (i = 0; i < len; i++) { |
|
5681
|
76
|
|
|
|
|
|
SV **svp = av_fetch(list, i, 0); |
|
5682
|
76
|
50
|
|
|
|
|
if (!svp) continue; |
|
5683
|
76
|
|
|
|
|
|
SV *elem = *svp; |
|
5684
|
76
|
|
|
|
|
|
bool matches = FALSE; |
|
5685
|
|
|
|
|
|
|
{ |
|
5686
|
76
|
|
|
|
|
|
dSP; |
|
5687
|
|
|
|
|
|
|
int call_count; |
|
5688
|
|
|
|
|
|
|
SV *result; |
|
5689
|
76
|
|
|
|
|
|
ENTER; SAVETMPS; |
|
5690
|
76
|
50
|
|
|
|
|
PUSHMARK(SP); |
|
5691
|
76
|
50
|
|
|
|
|
XPUSHs(elem); |
|
5692
|
76
|
|
|
|
|
|
PUTBACK; |
|
5693
|
76
|
|
|
|
|
|
call_count = call_sv(cb->perl_callback, G_SCALAR); |
|
5694
|
76
|
|
|
|
|
|
SPAGAIN; |
|
5695
|
76
|
50
|
|
|
|
|
if (call_count > 0) { |
|
5696
|
76
|
|
|
|
|
|
result = POPs; |
|
5697
|
76
|
|
|
|
|
|
matches = SvTRUE(result); |
|
5698
|
|
|
|
|
|
|
} |
|
5699
|
76
|
|
|
|
|
|
PUTBACK; |
|
5700
|
76
|
50
|
|
|
|
|
FREETMPS; LEAVE; |
|
5701
|
|
|
|
|
|
|
} |
|
5702
|
76
|
100
|
|
|
|
|
if (matches) { |
|
5703
|
39
|
|
|
|
|
|
av_push(results, SvREFCNT_inc(elem)); |
|
5704
|
39
|
|
|
|
|
|
count++; |
|
5705
|
|
|
|
|
|
|
} |
|
5706
|
|
|
|
|
|
|
} |
|
5707
|
|
|
|
|
|
|
} |
|
5708
|
|
|
|
|
|
|
|
|
5709
|
|
|
|
|
|
|
/* Now push all results to the stack */ |
|
5710
|
3036
|
|
|
|
|
|
SP -= items; |
|
5711
|
16150
|
100
|
|
|
|
|
for (i = 0; i < count; i++) { |
|
5712
|
13114
|
|
|
|
|
|
SV **svp = av_fetch(results, i, 0); |
|
5713
|
13114
|
50
|
|
|
|
|
if (svp) { |
|
5714
|
13114
|
50
|
|
|
|
|
XPUSHs(sv_2mortal(SvREFCNT_inc(*svp))); |
|
5715
|
|
|
|
|
|
|
} |
|
5716
|
|
|
|
|
|
|
} |
|
5717
|
|
|
|
|
|
|
|
|
5718
|
3036
|
|
|
|
|
|
PUTBACK; |
|
5719
|
3036
|
|
|
|
|
|
XSRETURN(count); |
|
5720
|
|
|
|
|
|
|
} |
|
5721
|
|
|
|
|
|
|
|
|
5722
|
|
|
|
|
|
|
/* count_cb(\@list, ':predicate') - count matching elements */ |
|
5723
|
6165
|
|
|
|
|
|
XS_INTERNAL(xs_count_cb) { |
|
5724
|
6165
|
|
|
|
|
|
dXSARGS; |
|
5725
|
6165
|
50
|
|
|
|
|
if (items != 2) croak("Usage: Func::Util::count_cb(\\@list, $callback_name)"); |
|
5726
|
|
|
|
|
|
|
|
|
5727
|
6165
|
|
|
|
|
|
SV *list_sv = ST(0); |
|
5728
|
6165
|
50
|
|
|
|
|
if (!SvROK(list_sv) || SvTYPE(SvRV(list_sv)) != SVt_PVAV) { |
|
|
|
50
|
|
|
|
|
|
|
5729
|
0
|
|
|
|
|
|
croak("Func::Util::count_cb: first argument must be an arrayref"); |
|
5730
|
|
|
|
|
|
|
} |
|
5731
|
6165
|
|
|
|
|
|
AV *list = (AV*)SvRV(list_sv); |
|
5732
|
|
|
|
|
|
|
|
|
5733
|
|
|
|
|
|
|
STRLEN name_len; |
|
5734
|
6165
|
|
|
|
|
|
const char *name = SvPV(ST(1), name_len); |
|
5735
|
|
|
|
|
|
|
|
|
5736
|
6165
|
|
|
|
|
|
RegisteredCallback *cb = get_registered_callback(aTHX_ name); |
|
5737
|
6165
|
100
|
|
|
|
|
if (!cb) { |
|
5738
|
1
|
|
|
|
|
|
croak("Func::Util::count_cb: unknown callback '%s'", name); |
|
5739
|
|
|
|
|
|
|
} |
|
5740
|
6164
|
100
|
|
|
|
|
if (!cb->predicate && !cb->perl_callback) { |
|
|
|
50
|
|
|
|
|
|
|
5741
|
0
|
|
|
|
|
|
croak("Func::Util::count_cb: callback '%s' is not a predicate", name); |
|
5742
|
|
|
|
|
|
|
} |
|
5743
|
|
|
|
|
|
|
|
|
5744
|
6164
|
|
|
|
|
|
IV len = av_len(list) + 1; |
|
5745
|
|
|
|
|
|
|
IV i; |
|
5746
|
6164
|
|
|
|
|
|
IV count = 0; |
|
5747
|
|
|
|
|
|
|
|
|
5748
|
6164
|
100
|
|
|
|
|
if (cb->predicate) { |
|
5749
|
1046549
|
100
|
|
|
|
|
for (i = 0; i < len; i++) { |
|
5750
|
1041389
|
|
|
|
|
|
SV **svp = av_fetch(list, i, 0); |
|
5751
|
1041389
|
50
|
|
|
|
|
if (svp && cb->predicate(aTHX_ *svp)) { |
|
|
|
100
|
|
|
|
|
|
|
5752
|
1022177
|
|
|
|
|
|
count++; |
|
5753
|
|
|
|
|
|
|
} |
|
5754
|
|
|
|
|
|
|
} |
|
5755
|
1004
|
50
|
|
|
|
|
} else if (cb->perl_callback) { |
|
5756
|
10033
|
100
|
|
|
|
|
for (i = 0; i < len; i++) { |
|
5757
|
9029
|
|
|
|
|
|
SV **svp = av_fetch(list, i, 0); |
|
5758
|
9029
|
50
|
|
|
|
|
if (!svp) continue; |
|
5759
|
9029
|
|
|
|
|
|
bool matches = FALSE; |
|
5760
|
|
|
|
|
|
|
{ |
|
5761
|
9029
|
|
|
|
|
|
dSP; |
|
5762
|
|
|
|
|
|
|
int call_count; |
|
5763
|
|
|
|
|
|
|
SV *result; |
|
5764
|
9029
|
|
|
|
|
|
ENTER; SAVETMPS; |
|
5765
|
9029
|
50
|
|
|
|
|
PUSHMARK(SP); |
|
5766
|
9029
|
50
|
|
|
|
|
XPUSHs(*svp); |
|
5767
|
9029
|
|
|
|
|
|
PUTBACK; |
|
5768
|
9029
|
|
|
|
|
|
call_count = call_sv(cb->perl_callback, G_SCALAR); |
|
5769
|
9029
|
|
|
|
|
|
SPAGAIN; |
|
5770
|
9029
|
50
|
|
|
|
|
if (call_count > 0) { |
|
5771
|
9029
|
|
|
|
|
|
result = POPs; |
|
5772
|
9029
|
|
|
|
|
|
matches = SvTRUE(result); |
|
5773
|
|
|
|
|
|
|
} |
|
5774
|
9029
|
|
|
|
|
|
PUTBACK; |
|
5775
|
9029
|
50
|
|
|
|
|
FREETMPS; LEAVE; |
|
5776
|
|
|
|
|
|
|
} |
|
5777
|
9029
|
100
|
|
|
|
|
if (matches) { |
|
5778
|
3010
|
|
|
|
|
|
count++; |
|
5779
|
|
|
|
|
|
|
} |
|
5780
|
|
|
|
|
|
|
} |
|
5781
|
|
|
|
|
|
|
} |
|
5782
|
|
|
|
|
|
|
|
|
5783
|
6164
|
|
|
|
|
|
XSRETURN_IV(count); |
|
5784
|
|
|
|
|
|
|
} |
|
5785
|
|
|
|
|
|
|
|
|
5786
|
|
|
|
|
|
|
/* partition_cb(\@list, ':predicate') - split into [matches], [non-matches] */ |
|
5787
|
2013
|
|
|
|
|
|
XS_INTERNAL(xs_partition_cb) { |
|
5788
|
2013
|
|
|
|
|
|
dXSARGS; |
|
5789
|
2013
|
50
|
|
|
|
|
if (items != 2) croak("Usage: Func::Util::partition_cb(\\@list, $callback_name)"); |
|
5790
|
|
|
|
|
|
|
|
|
5791
|
2013
|
|
|
|
|
|
SV *list_sv = ST(0); |
|
5792
|
2013
|
100
|
|
|
|
|
if (!SvROK(list_sv) || SvTYPE(SvRV(list_sv)) != SVt_PVAV) { |
|
|
|
50
|
|
|
|
|
|
|
5793
|
1
|
|
|
|
|
|
croak("Func::Util::partition_cb: first argument must be an arrayref"); |
|
5794
|
|
|
|
|
|
|
} |
|
5795
|
2012
|
|
|
|
|
|
AV *list = (AV*)SvRV(list_sv); |
|
5796
|
|
|
|
|
|
|
|
|
5797
|
|
|
|
|
|
|
STRLEN name_len; |
|
5798
|
2012
|
|
|
|
|
|
const char *name = SvPV(ST(1), name_len); |
|
5799
|
|
|
|
|
|
|
|
|
5800
|
2012
|
|
|
|
|
|
RegisteredCallback *cb = get_registered_callback(aTHX_ name); |
|
5801
|
2012
|
100
|
|
|
|
|
if (!cb) { |
|
5802
|
1
|
|
|
|
|
|
croak("Func::Util::partition_cb: unknown callback '%s'", name); |
|
5803
|
|
|
|
|
|
|
} |
|
5804
|
2011
|
100
|
|
|
|
|
if (!cb->predicate && !cb->perl_callback) { |
|
|
|
50
|
|
|
|
|
|
|
5805
|
0
|
|
|
|
|
|
croak("Func::Util::partition_cb: callback '%s' is not a predicate", name); |
|
5806
|
|
|
|
|
|
|
} |
|
5807
|
|
|
|
|
|
|
|
|
5808
|
2011
|
|
|
|
|
|
IV len = av_len(list) + 1; |
|
5809
|
2011
|
|
|
|
|
|
AV *pass = newAV(); |
|
5810
|
2011
|
|
|
|
|
|
AV *fail = newAV(); |
|
5811
|
2011
|
|
|
|
|
|
av_extend(pass, len >> 1); |
|
5812
|
2011
|
|
|
|
|
|
av_extend(fail, len >> 1); |
|
5813
|
|
|
|
|
|
|
|
|
5814
|
|
|
|
|
|
|
IV i; |
|
5815
|
2011
|
100
|
|
|
|
|
if (cb->predicate) { |
|
5816
|
20049
|
100
|
|
|
|
|
for (i = 0; i < len; i++) { |
|
5817
|
18040
|
|
|
|
|
|
SV **svp = av_fetch(list, i, 0); |
|
5818
|
18040
|
50
|
|
|
|
|
if (!svp) continue; |
|
5819
|
18040
|
100
|
|
|
|
|
if (cb->predicate(aTHX_ *svp)) { |
|
5820
|
11020
|
|
|
|
|
|
av_push(pass, SvREFCNT_inc_simple_NN(*svp)); |
|
5821
|
|
|
|
|
|
|
} else { |
|
5822
|
7020
|
|
|
|
|
|
av_push(fail, SvREFCNT_inc_simple_NN(*svp)); |
|
5823
|
|
|
|
|
|
|
} |
|
5824
|
|
|
|
|
|
|
} |
|
5825
|
2
|
50
|
|
|
|
|
} else if (cb->perl_callback) { |
|
5826
|
11
|
100
|
|
|
|
|
for (i = 0; i < len; i++) { |
|
5827
|
9
|
|
|
|
|
|
SV **svp = av_fetch(list, i, 0); |
|
5828
|
9
|
50
|
|
|
|
|
if (!svp) continue; |
|
5829
|
9
|
|
|
|
|
|
bool matches = FALSE; |
|
5830
|
|
|
|
|
|
|
{ |
|
5831
|
9
|
|
|
|
|
|
dSP; |
|
5832
|
|
|
|
|
|
|
int call_count; |
|
5833
|
|
|
|
|
|
|
SV *result; |
|
5834
|
|
|
|
|
|
|
|
|
5835
|
9
|
|
|
|
|
|
ENTER; |
|
5836
|
9
|
|
|
|
|
|
SAVETMPS; |
|
5837
|
|
|
|
|
|
|
|
|
5838
|
9
|
50
|
|
|
|
|
PUSHMARK(SP); |
|
5839
|
9
|
50
|
|
|
|
|
XPUSHs(*svp); |
|
5840
|
9
|
|
|
|
|
|
PUTBACK; |
|
5841
|
|
|
|
|
|
|
|
|
5842
|
9
|
|
|
|
|
|
call_count = call_sv(cb->perl_callback, G_SCALAR); |
|
5843
|
|
|
|
|
|
|
|
|
5844
|
9
|
|
|
|
|
|
SPAGAIN; |
|
5845
|
9
|
50
|
|
|
|
|
if (call_count > 0) { |
|
5846
|
9
|
|
|
|
|
|
result = POPs; |
|
5847
|
9
|
|
|
|
|
|
matches = SvTRUE(result); |
|
5848
|
|
|
|
|
|
|
} |
|
5849
|
9
|
|
|
|
|
|
PUTBACK; |
|
5850
|
|
|
|
|
|
|
|
|
5851
|
9
|
50
|
|
|
|
|
FREETMPS; |
|
5852
|
9
|
|
|
|
|
|
LEAVE; |
|
5853
|
|
|
|
|
|
|
} |
|
5854
|
9
|
100
|
|
|
|
|
if (matches) { |
|
5855
|
4
|
|
|
|
|
|
av_push(pass, SvREFCNT_inc_simple_NN(*svp)); |
|
5856
|
|
|
|
|
|
|
} else { |
|
5857
|
5
|
|
|
|
|
|
av_push(fail, SvREFCNT_inc_simple_NN(*svp)); |
|
5858
|
|
|
|
|
|
|
} |
|
5859
|
|
|
|
|
|
|
} |
|
5860
|
|
|
|
|
|
|
} |
|
5861
|
|
|
|
|
|
|
|
|
5862
|
|
|
|
|
|
|
/* Return list of two arrayrefs */ |
|
5863
|
2011
|
|
|
|
|
|
ST(0) = sv_2mortal(newRV_noinc((SV*)pass)); |
|
5864
|
2011
|
|
|
|
|
|
ST(1) = sv_2mortal(newRV_noinc((SV*)fail)); |
|
5865
|
2011
|
|
|
|
|
|
XSRETURN(2); |
|
5866
|
|
|
|
|
|
|
} |
|
5867
|
|
|
|
|
|
|
|
|
5868
|
|
|
|
|
|
|
/* final_cb(\@list, ':predicate') - find last matching element */ |
|
5869
|
3019
|
|
|
|
|
|
XS_INTERNAL(xs_final_cb) { |
|
5870
|
3019
|
|
|
|
|
|
dXSARGS; |
|
5871
|
3019
|
50
|
|
|
|
|
if (items != 2) croak("Usage: Func::Util::final_cb(\\@list, $callback_name)"); |
|
5872
|
|
|
|
|
|
|
|
|
5873
|
3019
|
|
|
|
|
|
SV *list_sv = ST(0); |
|
5874
|
3019
|
100
|
|
|
|
|
if (!SvROK(list_sv) || SvTYPE(SvRV(list_sv)) != SVt_PVAV) { |
|
|
|
50
|
|
|
|
|
|
|
5875
|
1
|
|
|
|
|
|
croak("Func::Util::final_cb: first argument must be an arrayref"); |
|
5876
|
|
|
|
|
|
|
} |
|
5877
|
3018
|
|
|
|
|
|
AV *list = (AV*)SvRV(list_sv); |
|
5878
|
|
|
|
|
|
|
|
|
5879
|
|
|
|
|
|
|
STRLEN name_len; |
|
5880
|
3018
|
|
|
|
|
|
const char *name = SvPV(ST(1), name_len); |
|
5881
|
|
|
|
|
|
|
|
|
5882
|
3018
|
|
|
|
|
|
RegisteredCallback *cb = get_registered_callback(aTHX_ name); |
|
5883
|
3018
|
100
|
|
|
|
|
if (!cb) { |
|
5884
|
1
|
|
|
|
|
|
croak("Func::Util::final_cb: unknown callback '%s'", name); |
|
5885
|
|
|
|
|
|
|
} |
|
5886
|
3017
|
100
|
|
|
|
|
if (!cb->predicate && !cb->perl_callback) { |
|
|
|
50
|
|
|
|
|
|
|
5887
|
0
|
|
|
|
|
|
croak("Func::Util::final_cb: callback '%s' is not a predicate", name); |
|
5888
|
|
|
|
|
|
|
} |
|
5889
|
|
|
|
|
|
|
|
|
5890
|
3017
|
|
|
|
|
|
IV len = av_len(list) + 1; |
|
5891
|
|
|
|
|
|
|
IV i; |
|
5892
|
|
|
|
|
|
|
|
|
5893
|
3017
|
100
|
|
|
|
|
if (cb->predicate) { |
|
5894
|
|
|
|
|
|
|
/* Search from end - C predicate path */ |
|
5895
|
10039
|
100
|
|
|
|
|
for (i = len - 1; i >= 0; i--) { |
|
5896
|
10033
|
|
|
|
|
|
SV **svp = av_fetch(list, i, 0); |
|
5897
|
10033
|
50
|
|
|
|
|
if (svp && cb->predicate(aTHX_ *svp)) { |
|
|
|
100
|
|
|
|
|
|
|
5898
|
3010
|
|
|
|
|
|
ST(0) = *svp; |
|
5899
|
3010
|
|
|
|
|
|
XSRETURN(1); |
|
5900
|
|
|
|
|
|
|
} |
|
5901
|
|
|
|
|
|
|
} |
|
5902
|
1
|
50
|
|
|
|
|
} else if (cb->perl_callback) { |
|
5903
|
|
|
|
|
|
|
/* Search from end - Perl callback path */ |
|
5904
|
1
|
50
|
|
|
|
|
for (i = len - 1; i >= 0; i--) { |
|
5905
|
1
|
|
|
|
|
|
SV **svp = av_fetch(list, i, 0); |
|
5906
|
1
|
50
|
|
|
|
|
if (!svp) continue; |
|
5907
|
1
|
|
|
|
|
|
bool matches = FALSE; |
|
5908
|
|
|
|
|
|
|
{ |
|
5909
|
1
|
|
|
|
|
|
dSP; |
|
5910
|
|
|
|
|
|
|
int count; |
|
5911
|
|
|
|
|
|
|
SV *result; |
|
5912
|
1
|
|
|
|
|
|
ENTER; SAVETMPS; |
|
5913
|
1
|
50
|
|
|
|
|
PUSHMARK(SP); |
|
5914
|
1
|
50
|
|
|
|
|
XPUSHs(*svp); |
|
5915
|
1
|
|
|
|
|
|
PUTBACK; |
|
5916
|
1
|
|
|
|
|
|
count = call_sv(cb->perl_callback, G_SCALAR); |
|
5917
|
1
|
|
|
|
|
|
SPAGAIN; |
|
5918
|
1
|
50
|
|
|
|
|
if (count > 0) { |
|
5919
|
1
|
|
|
|
|
|
result = POPs; |
|
5920
|
1
|
|
|
|
|
|
matches = SvTRUE(result); |
|
5921
|
|
|
|
|
|
|
} |
|
5922
|
1
|
|
|
|
|
|
PUTBACK; |
|
5923
|
1
|
50
|
|
|
|
|
FREETMPS; LEAVE; |
|
5924
|
|
|
|
|
|
|
} |
|
5925
|
1
|
50
|
|
|
|
|
if (matches) { |
|
5926
|
1
|
|
|
|
|
|
ST(0) = *svp; |
|
5927
|
1
|
|
|
|
|
|
XSRETURN(1); |
|
5928
|
|
|
|
|
|
|
} |
|
5929
|
|
|
|
|
|
|
} |
|
5930
|
|
|
|
|
|
|
} |
|
5931
|
|
|
|
|
|
|
|
|
5932
|
6
|
|
|
|
|
|
XSRETURN_UNDEF; |
|
5933
|
|
|
|
|
|
|
} |
|
5934
|
|
|
|
|
|
|
|
|
5935
|
|
|
|
|
|
|
/* Perl-level callback registration */ |
|
5936
|
20
|
|
|
|
|
|
XS_INTERNAL(xs_register_callback) { |
|
5937
|
20
|
|
|
|
|
|
dXSARGS; |
|
5938
|
20
|
50
|
|
|
|
|
if (items != 2) croak("Usage: Func::Util::register_callback($name, \\&coderef)"); |
|
5939
|
|
|
|
|
|
|
|
|
5940
|
|
|
|
|
|
|
STRLEN name_len; |
|
5941
|
20
|
|
|
|
|
|
const char *name = SvPV(ST(0), name_len); |
|
5942
|
|
|
|
|
|
|
|
|
5943
|
20
|
|
|
|
|
|
SV *coderef = ST(1); |
|
5944
|
20
|
50
|
|
|
|
|
if (!SvROK(coderef) || SvTYPE(SvRV(coderef)) != SVt_PVCV) { |
|
|
|
50
|
|
|
|
|
|
|
5945
|
0
|
|
|
|
|
|
croak("Func::Util::register_callback: second argument must be a coderef"); |
|
5946
|
|
|
|
|
|
|
} |
|
5947
|
|
|
|
|
|
|
|
|
5948
|
|
|
|
|
|
|
RegisteredCallback *cb; |
|
5949
|
|
|
|
|
|
|
SV *sv; |
|
5950
|
|
|
|
|
|
|
|
|
5951
|
20
|
|
|
|
|
|
init_callback_registry(aTHX); |
|
5952
|
|
|
|
|
|
|
|
|
5953
|
|
|
|
|
|
|
/* Check if already registered */ |
|
5954
|
20
|
100
|
|
|
|
|
if (get_registered_callback(aTHX_ name)) { |
|
5955
|
3
|
|
|
|
|
|
croak("Callback '%s' is already registered", name); |
|
5956
|
|
|
|
|
|
|
} |
|
5957
|
|
|
|
|
|
|
|
|
5958
|
17
|
|
|
|
|
|
Newxz(cb, 1, RegisteredCallback); |
|
5959
|
17
|
|
|
|
|
|
cb->name = savepv(name); |
|
5960
|
17
|
|
|
|
|
|
cb->predicate = NULL; |
|
5961
|
17
|
|
|
|
|
|
cb->mapper = NULL; |
|
5962
|
17
|
|
|
|
|
|
cb->reducer = NULL; |
|
5963
|
|
|
|
|
|
|
/* Store a copy of the coderef (RV to CV) */ |
|
5964
|
17
|
|
|
|
|
|
cb->perl_callback = newSVsv(coderef); |
|
5965
|
|
|
|
|
|
|
|
|
5966
|
17
|
|
|
|
|
|
sv = newSViv(PTR2IV(cb)); |
|
5967
|
17
|
|
|
|
|
|
hv_store(g_callback_registry, name, name_len, sv, 0); |
|
5968
|
|
|
|
|
|
|
|
|
5969
|
17
|
|
|
|
|
|
XSRETURN_YES; |
|
5970
|
|
|
|
|
|
|
} |
|
5971
|
|
|
|
|
|
|
|
|
5972
|
|
|
|
|
|
|
/* Check if callback exists */ |
|
5973
|
6018
|
|
|
|
|
|
XS_INTERNAL(xs_has_callback) { |
|
5974
|
6018
|
|
|
|
|
|
dXSARGS; |
|
5975
|
6018
|
50
|
|
|
|
|
if (items != 1) croak("Usage: Func::Util::has_callback($name)"); |
|
5976
|
|
|
|
|
|
|
|
|
5977
|
|
|
|
|
|
|
STRLEN name_len; |
|
5978
|
6018
|
|
|
|
|
|
const char *name = SvPV(ST(0), name_len); |
|
5979
|
|
|
|
|
|
|
|
|
5980
|
6018
|
100
|
|
|
|
|
if (has_callback(aTHX_ name)) { |
|
5981
|
4014
|
|
|
|
|
|
XSRETURN_YES; |
|
5982
|
|
|
|
|
|
|
} |
|
5983
|
2004
|
|
|
|
|
|
XSRETURN_NO; |
|
5984
|
|
|
|
|
|
|
} |
|
5985
|
|
|
|
|
|
|
|
|
5986
|
|
|
|
|
|
|
/* List all callbacks */ |
|
5987
|
1006
|
|
|
|
|
|
XS_INTERNAL(xs_list_callbacks) { |
|
5988
|
1006
|
|
|
|
|
|
dXSARGS; |
|
5989
|
|
|
|
|
|
|
PERL_UNUSED_ARG(items); |
|
5990
|
|
|
|
|
|
|
|
|
5991
|
1006
|
|
|
|
|
|
AV *result = list_callbacks(aTHX); |
|
5992
|
1006
|
|
|
|
|
|
ST(0) = sv_2mortal(newRV_noinc((SV*)result)); |
|
5993
|
1006
|
|
|
|
|
|
XSRETURN(1); |
|
5994
|
|
|
|
|
|
|
} |
|
5995
|
|
|
|
|
|
|
|
|
5996
|
|
|
|
|
|
|
/* ============================================ |
|
5997
|
|
|
|
|
|
|
Import function - O(1) hash-based lookup |
|
5998
|
|
|
|
|
|
|
============================================ */ |
|
5999
|
|
|
|
|
|
|
|
|
6000
|
|
|
|
|
|
|
/* Export entry: supports XS functions, Perl coderefs, or both */ |
|
6001
|
|
|
|
|
|
|
typedef struct { |
|
6002
|
|
|
|
|
|
|
XSUBADDR_t xs_func; /* XS function pointer (NULL for Perl-only) */ |
|
6003
|
|
|
|
|
|
|
Perl_call_checker call_checker; /* Optional call checker for XS */ |
|
6004
|
|
|
|
|
|
|
SV *perl_cv; /* Perl coderef (NULL for XS-only) */ |
|
6005
|
|
|
|
|
|
|
} ExportEntry; |
|
6006
|
|
|
|
|
|
|
|
|
6007
|
|
|
|
|
|
|
/* Global export hash - initialized at boot */ |
|
6008
|
|
|
|
|
|
|
static HV *g_export_hash = NULL; |
|
6009
|
|
|
|
|
|
|
|
|
6010
|
|
|
|
|
|
|
/* Register an XS export with optional call checker (internal) */ |
|
6011
|
6095
|
|
|
|
|
|
static void register_export(pTHX_ const char *name, XSUBADDR_t xs_func, Perl_call_checker checker) { |
|
6012
|
|
|
|
|
|
|
ExportEntry *entry; |
|
6013
|
6095
|
|
|
|
|
|
Newx(entry, 1, ExportEntry); |
|
6014
|
6095
|
|
|
|
|
|
entry->xs_func = xs_func; |
|
6015
|
6095
|
|
|
|
|
|
entry->call_checker = checker; |
|
6016
|
6095
|
|
|
|
|
|
entry->perl_cv = NULL; |
|
6017
|
6095
|
|
|
|
|
|
(void)hv_store(g_export_hash, name, strlen(name), newSViv(PTR2IV(entry)), 0); |
|
6018
|
6095
|
|
|
|
|
|
} |
|
6019
|
|
|
|
|
|
|
|
|
6020
|
|
|
|
|
|
|
/* ============================================ |
|
6021
|
|
|
|
|
|
|
Public API: Register custom exports |
|
6022
|
|
|
|
|
|
|
============================================ */ |
|
6023
|
|
|
|
|
|
|
|
|
6024
|
|
|
|
|
|
|
/* Register a Perl coderef as an export - called from Perl */ |
|
6025
|
15
|
|
|
|
|
|
XS_INTERNAL(xs_register_export) { |
|
6026
|
15
|
|
|
|
|
|
dXSARGS; |
|
6027
|
15
|
50
|
|
|
|
|
if (items != 2) |
|
6028
|
0
|
|
|
|
|
|
croak("Usage: Func::Util::register_export($name, \\&coderef)"); |
|
6029
|
|
|
|
|
|
|
|
|
6030
|
|
|
|
|
|
|
STRLEN name_len; |
|
6031
|
15
|
|
|
|
|
|
char *name = SvPV(ST(0), name_len); |
|
6032
|
15
|
|
|
|
|
|
SV *cv_sv = ST(1); |
|
6033
|
|
|
|
|
|
|
|
|
6034
|
|
|
|
|
|
|
/* Validate it's a coderef */ |
|
6035
|
15
|
100
|
|
|
|
|
if (!SvROK(cv_sv) || SvTYPE(SvRV(cv_sv)) != SVt_PVCV) |
|
|
|
100
|
|
|
|
|
|
|
6036
|
3
|
|
|
|
|
|
croak("Func::Util::register_export: second argument must be a coderef"); |
|
6037
|
|
|
|
|
|
|
|
|
6038
|
|
|
|
|
|
|
/* Check if name already exists */ |
|
6039
|
12
|
100
|
|
|
|
|
if (hv_exists(g_export_hash, name, name_len)) |
|
6040
|
1
|
|
|
|
|
|
croak("Func::Util::register_export: '%s' is already registered", name); |
|
6041
|
|
|
|
|
|
|
|
|
6042
|
|
|
|
|
|
|
/* Create entry for Perl coderef */ |
|
6043
|
|
|
|
|
|
|
ExportEntry *entry; |
|
6044
|
11
|
|
|
|
|
|
Newx(entry, 1, ExportEntry); |
|
6045
|
11
|
|
|
|
|
|
entry->xs_func = NULL; |
|
6046
|
11
|
|
|
|
|
|
entry->call_checker = NULL; |
|
6047
|
11
|
|
|
|
|
|
entry->perl_cv = SvREFCNT_inc(cv_sv); /* Keep a reference */ |
|
6048
|
|
|
|
|
|
|
|
|
6049
|
11
|
|
|
|
|
|
(void)hv_store(g_export_hash, name, name_len, newSViv(PTR2IV(entry)), 0); |
|
6050
|
|
|
|
|
|
|
|
|
6051
|
11
|
|
|
|
|
|
XSRETURN_YES; |
|
6052
|
|
|
|
|
|
|
} |
|
6053
|
|
|
|
|
|
|
|
|
6054
|
|
|
|
|
|
|
/* Check if an export name is registered */ |
|
6055
|
17
|
|
|
|
|
|
XS_INTERNAL(xs_has_export) { |
|
6056
|
17
|
|
|
|
|
|
dXSARGS; |
|
6057
|
17
|
50
|
|
|
|
|
if (items != 1) |
|
6058
|
0
|
|
|
|
|
|
croak("Usage: Func::Util::has_export($name)"); |
|
6059
|
|
|
|
|
|
|
|
|
6060
|
|
|
|
|
|
|
STRLEN name_len; |
|
6061
|
17
|
|
|
|
|
|
char *name = SvPV(ST(0), name_len); |
|
6062
|
|
|
|
|
|
|
|
|
6063
|
17
|
100
|
|
|
|
|
if (hv_exists(g_export_hash, name, name_len)) { |
|
6064
|
14
|
|
|
|
|
|
XSRETURN_YES; |
|
6065
|
|
|
|
|
|
|
} else { |
|
6066
|
3
|
|
|
|
|
|
XSRETURN_NO; |
|
6067
|
|
|
|
|
|
|
} |
|
6068
|
|
|
|
|
|
|
} |
|
6069
|
|
|
|
|
|
|
|
|
6070
|
|
|
|
|
|
|
/* List all registered export names */ |
|
6071
|
3
|
|
|
|
|
|
XS_INTERNAL(xs_list_exports) { |
|
6072
|
3
|
|
|
|
|
|
dXSARGS; |
|
6073
|
|
|
|
|
|
|
PERL_UNUSED_ARG(items); |
|
6074
|
|
|
|
|
|
|
|
|
6075
|
3
|
|
|
|
|
|
AV *result = newAV(); |
|
6076
|
|
|
|
|
|
|
HE *entry; |
|
6077
|
|
|
|
|
|
|
|
|
6078
|
3
|
|
|
|
|
|
hv_iterinit(g_export_hash); |
|
6079
|
357
|
100
|
|
|
|
|
while ((entry = hv_iternext(g_export_hash))) { |
|
6080
|
354
|
|
|
|
|
|
SV *key = hv_iterkeysv(entry); |
|
6081
|
354
|
|
|
|
|
|
av_push(result, SvREFCNT_inc(key)); |
|
6082
|
|
|
|
|
|
|
} |
|
6083
|
|
|
|
|
|
|
|
|
6084
|
3
|
|
|
|
|
|
ST(0) = sv_2mortal(newRV_noinc((SV*)result)); |
|
6085
|
3
|
|
|
|
|
|
XSRETURN(1); |
|
6086
|
|
|
|
|
|
|
} |
|
6087
|
|
|
|
|
|
|
|
|
6088
|
|
|
|
|
|
|
/* ============================================ |
|
6089
|
|
|
|
|
|
|
C API for XS modules to register exports |
|
6090
|
|
|
|
|
|
|
============================================ */ |
|
6091
|
|
|
|
|
|
|
|
|
6092
|
|
|
|
|
|
|
/* |
|
6093
|
|
|
|
|
|
|
* Register an XS function as a util export. |
|
6094
|
|
|
|
|
|
|
* Call this from your BOOT section: |
|
6095
|
|
|
|
|
|
|
* funcutil_register_export_xs(aTHX_ "my_func", xs_my_func); |
|
6096
|
|
|
|
|
|
|
*/ |
|
6097
|
7
|
|
|
|
|
|
void funcutil_register_export_xs(pTHX_ const char *name, XSUBADDR_t xs_func) { |
|
6098
|
7
|
50
|
|
|
|
|
if (!g_export_hash) { |
|
6099
|
0
|
|
|
|
|
|
croak("funcutil_register_export_xs: Func::Util module not yet loaded"); |
|
6100
|
|
|
|
|
|
|
} |
|
6101
|
|
|
|
|
|
|
|
|
6102
|
7
|
|
|
|
|
|
STRLEN name_len = strlen(name); |
|
6103
|
7
|
50
|
|
|
|
|
if (hv_exists(g_export_hash, name, name_len)) { |
|
6104
|
0
|
|
|
|
|
|
croak("funcutil_register_export_xs: '%s' is already registered", name); |
|
6105
|
|
|
|
|
|
|
} |
|
6106
|
|
|
|
|
|
|
|
|
6107
|
|
|
|
|
|
|
ExportEntry *entry; |
|
6108
|
7
|
|
|
|
|
|
Newx(entry, 1, ExportEntry); |
|
6109
|
7
|
|
|
|
|
|
entry->xs_func = xs_func; |
|
6110
|
7
|
|
|
|
|
|
entry->call_checker = NULL; |
|
6111
|
7
|
|
|
|
|
|
entry->perl_cv = NULL; |
|
6112
|
|
|
|
|
|
|
|
|
6113
|
7
|
|
|
|
|
|
(void)hv_store(g_export_hash, name, name_len, newSViv(PTR2IV(entry)), 0); |
|
6114
|
7
|
|
|
|
|
|
} |
|
6115
|
|
|
|
|
|
|
|
|
6116
|
|
|
|
|
|
|
/* Initialize export hash at boot - called once */ |
|
6117
|
53
|
|
|
|
|
|
static void init_export_hash(pTHX) { |
|
6118
|
53
|
|
|
|
|
|
g_export_hash = newHV(); |
|
6119
|
|
|
|
|
|
|
|
|
6120
|
|
|
|
|
|
|
/* Functional */ |
|
6121
|
53
|
|
|
|
|
|
register_export(aTHX_ "memo", xs_memo, NULL); |
|
6122
|
53
|
|
|
|
|
|
register_export(aTHX_ "pipeline", xs_pipe, NULL); |
|
6123
|
53
|
|
|
|
|
|
register_export(aTHX_ "compose", xs_compose, NULL); |
|
6124
|
53
|
|
|
|
|
|
register_export(aTHX_ "lazy", xs_lazy, NULL); |
|
6125
|
53
|
|
|
|
|
|
register_export(aTHX_ "force", xs_force, NULL); |
|
6126
|
53
|
|
|
|
|
|
register_export(aTHX_ "dig", xs_dig, NULL); |
|
6127
|
53
|
|
|
|
|
|
register_export(aTHX_ "clamp", xs_clamp, clamp_call_checker); |
|
6128
|
53
|
|
|
|
|
|
register_export(aTHX_ "tap", xs_tap, NULL); |
|
6129
|
53
|
|
|
|
|
|
register_export(aTHX_ "identity", xs_identity, identity_call_checker); |
|
6130
|
53
|
|
|
|
|
|
register_export(aTHX_ "always", xs_always, NULL); |
|
6131
|
53
|
|
|
|
|
|
register_export(aTHX_ "noop", xs_noop, noop_call_checker); |
|
6132
|
53
|
|
|
|
|
|
register_export(aTHX_ "partial", xs_partial, NULL); |
|
6133
|
53
|
|
|
|
|
|
register_export(aTHX_ "negate", xs_negate, NULL); |
|
6134
|
53
|
|
|
|
|
|
register_export(aTHX_ "once", xs_once, NULL); |
|
6135
|
|
|
|
|
|
|
|
|
6136
|
|
|
|
|
|
|
/* Stubs */ |
|
6137
|
53
|
|
|
|
|
|
register_export(aTHX_ "stub_true", xs_stub_true, NULL); |
|
6138
|
53
|
|
|
|
|
|
register_export(aTHX_ "stub_false", xs_stub_false, NULL); |
|
6139
|
53
|
|
|
|
|
|
register_export(aTHX_ "stub_array", xs_stub_array, NULL); |
|
6140
|
53
|
|
|
|
|
|
register_export(aTHX_ "stub_hash", xs_stub_hash, NULL); |
|
6141
|
53
|
|
|
|
|
|
register_export(aTHX_ "stub_string", xs_stub_string, NULL); |
|
6142
|
53
|
|
|
|
|
|
register_export(aTHX_ "stub_zero", xs_stub_zero, NULL); |
|
6143
|
|
|
|
|
|
|
|
|
6144
|
|
|
|
|
|
|
/* Null coalescing */ |
|
6145
|
53
|
|
|
|
|
|
register_export(aTHX_ "nvl", xs_nvl, NULL); |
|
6146
|
53
|
|
|
|
|
|
register_export(aTHX_ "coalesce", xs_coalesce, NULL); |
|
6147
|
|
|
|
|
|
|
|
|
6148
|
|
|
|
|
|
|
/* List operations */ |
|
6149
|
53
|
|
|
|
|
|
register_export(aTHX_ "first", xs_first, NULL); |
|
6150
|
53
|
|
|
|
|
|
register_export(aTHX_ "firstr", xs_firstr, NULL); |
|
6151
|
53
|
|
|
|
|
|
register_export(aTHX_ "any", xs_any, NULL); |
|
6152
|
53
|
|
|
|
|
|
register_export(aTHX_ "all", xs_all, NULL); |
|
6153
|
53
|
|
|
|
|
|
register_export(aTHX_ "none", xs_none, NULL); |
|
6154
|
53
|
|
|
|
|
|
register_export(aTHX_ "final", xs_final, NULL); |
|
6155
|
|
|
|
|
|
|
#ifdef dMULTICALL |
|
6156
|
53
|
|
|
|
|
|
register_export(aTHX_ "first_inline", xs_first_inline, NULL); |
|
6157
|
|
|
|
|
|
|
#endif |
|
6158
|
|
|
|
|
|
|
|
|
6159
|
|
|
|
|
|
|
/* Callback-based loop functions */ |
|
6160
|
53
|
|
|
|
|
|
register_export(aTHX_ "any_cb", xs_any_cb, NULL); |
|
6161
|
53
|
|
|
|
|
|
register_export(aTHX_ "all_cb", xs_all_cb, NULL); |
|
6162
|
53
|
|
|
|
|
|
register_export(aTHX_ "none_cb", xs_none_cb, NULL); |
|
6163
|
53
|
|
|
|
|
|
register_export(aTHX_ "first_cb", xs_first_cb, NULL); |
|
6164
|
53
|
|
|
|
|
|
register_export(aTHX_ "grep_cb", xs_grep_cb, NULL); |
|
6165
|
53
|
|
|
|
|
|
register_export(aTHX_ "count_cb", xs_count_cb, NULL); |
|
6166
|
53
|
|
|
|
|
|
register_export(aTHX_ "partition_cb", xs_partition_cb, NULL); |
|
6167
|
53
|
|
|
|
|
|
register_export(aTHX_ "final_cb", xs_final_cb, NULL); |
|
6168
|
53
|
|
|
|
|
|
register_export(aTHX_ "register_callback", xs_register_callback, NULL); |
|
6169
|
53
|
|
|
|
|
|
register_export(aTHX_ "has_callback", xs_has_callback, NULL); |
|
6170
|
53
|
|
|
|
|
|
register_export(aTHX_ "list_callbacks", xs_list_callbacks, NULL); |
|
6171
|
|
|
|
|
|
|
|
|
6172
|
|
|
|
|
|
|
/* Specialized predicates - first_* */ |
|
6173
|
53
|
|
|
|
|
|
register_export(aTHX_ "first_gt", xs_first_gt, NULL); |
|
6174
|
53
|
|
|
|
|
|
register_export(aTHX_ "first_lt", xs_first_lt, NULL); |
|
6175
|
53
|
|
|
|
|
|
register_export(aTHX_ "first_ge", xs_first_ge, NULL); |
|
6176
|
53
|
|
|
|
|
|
register_export(aTHX_ "first_le", xs_first_le, NULL); |
|
6177
|
53
|
|
|
|
|
|
register_export(aTHX_ "first_eq", xs_first_eq, NULL); |
|
6178
|
53
|
|
|
|
|
|
register_export(aTHX_ "first_ne", xs_first_ne, NULL); |
|
6179
|
|
|
|
|
|
|
|
|
6180
|
|
|
|
|
|
|
/* Specialized predicates - final_* */ |
|
6181
|
53
|
|
|
|
|
|
register_export(aTHX_ "final_gt", xs_final_gt, NULL); |
|
6182
|
53
|
|
|
|
|
|
register_export(aTHX_ "final_lt", xs_final_lt, NULL); |
|
6183
|
53
|
|
|
|
|
|
register_export(aTHX_ "final_ge", xs_final_ge, NULL); |
|
6184
|
53
|
|
|
|
|
|
register_export(aTHX_ "final_le", xs_final_le, NULL); |
|
6185
|
53
|
|
|
|
|
|
register_export(aTHX_ "final_eq", xs_final_eq, NULL); |
|
6186
|
53
|
|
|
|
|
|
register_export(aTHX_ "final_ne", xs_final_ne, NULL); |
|
6187
|
|
|
|
|
|
|
|
|
6188
|
|
|
|
|
|
|
/* Specialized predicates - any_* */ |
|
6189
|
53
|
|
|
|
|
|
register_export(aTHX_ "any_gt", xs_any_gt, NULL); |
|
6190
|
53
|
|
|
|
|
|
register_export(aTHX_ "any_lt", xs_any_lt, NULL); |
|
6191
|
53
|
|
|
|
|
|
register_export(aTHX_ "any_ge", xs_any_ge, NULL); |
|
6192
|
53
|
|
|
|
|
|
register_export(aTHX_ "any_le", xs_any_le, NULL); |
|
6193
|
53
|
|
|
|
|
|
register_export(aTHX_ "any_eq", xs_any_eq, NULL); |
|
6194
|
53
|
|
|
|
|
|
register_export(aTHX_ "any_ne", xs_any_ne, NULL); |
|
6195
|
|
|
|
|
|
|
|
|
6196
|
|
|
|
|
|
|
/* Specialized predicates - all_* */ |
|
6197
|
53
|
|
|
|
|
|
register_export(aTHX_ "all_gt", xs_all_gt, NULL); |
|
6198
|
53
|
|
|
|
|
|
register_export(aTHX_ "all_lt", xs_all_lt, NULL); |
|
6199
|
53
|
|
|
|
|
|
register_export(aTHX_ "all_ge", xs_all_ge, NULL); |
|
6200
|
53
|
|
|
|
|
|
register_export(aTHX_ "all_le", xs_all_le, NULL); |
|
6201
|
53
|
|
|
|
|
|
register_export(aTHX_ "all_eq", xs_all_eq, NULL); |
|
6202
|
53
|
|
|
|
|
|
register_export(aTHX_ "all_ne", xs_all_ne, NULL); |
|
6203
|
|
|
|
|
|
|
|
|
6204
|
|
|
|
|
|
|
/* Specialized predicates - none_* */ |
|
6205
|
53
|
|
|
|
|
|
register_export(aTHX_ "none_gt", xs_none_gt, NULL); |
|
6206
|
53
|
|
|
|
|
|
register_export(aTHX_ "none_lt", xs_none_lt, NULL); |
|
6207
|
53
|
|
|
|
|
|
register_export(aTHX_ "none_ge", xs_none_ge, NULL); |
|
6208
|
53
|
|
|
|
|
|
register_export(aTHX_ "none_le", xs_none_le, NULL); |
|
6209
|
53
|
|
|
|
|
|
register_export(aTHX_ "none_eq", xs_none_eq, NULL); |
|
6210
|
53
|
|
|
|
|
|
register_export(aTHX_ "none_ne", xs_none_ne, NULL); |
|
6211
|
|
|
|
|
|
|
|
|
6212
|
|
|
|
|
|
|
/* Collection functions */ |
|
6213
|
53
|
|
|
|
|
|
register_export(aTHX_ "pick", xs_pick, NULL); |
|
6214
|
53
|
|
|
|
|
|
register_export(aTHX_ "pluck", xs_pluck, NULL); |
|
6215
|
53
|
|
|
|
|
|
register_export(aTHX_ "omit", xs_omit, NULL); |
|
6216
|
53
|
|
|
|
|
|
register_export(aTHX_ "uniq", xs_uniq, NULL); |
|
6217
|
53
|
|
|
|
|
|
register_export(aTHX_ "partition", xs_partition, NULL); |
|
6218
|
53
|
|
|
|
|
|
register_export(aTHX_ "defaults", xs_defaults, NULL); |
|
6219
|
53
|
|
|
|
|
|
register_export(aTHX_ "count", xs_count, NULL); |
|
6220
|
53
|
|
|
|
|
|
register_export(aTHX_ "replace_all", xs_replace_all, NULL); |
|
6221
|
|
|
|
|
|
|
|
|
6222
|
|
|
|
|
|
|
/* Type predicates */ |
|
6223
|
53
|
|
|
|
|
|
register_export(aTHX_ "is_ref", xs_is_ref, is_ref_call_checker); |
|
6224
|
53
|
|
|
|
|
|
register_export(aTHX_ "is_array", xs_is_array, is_array_call_checker); |
|
6225
|
53
|
|
|
|
|
|
register_export(aTHX_ "is_hash", xs_is_hash, is_hash_call_checker); |
|
6226
|
53
|
|
|
|
|
|
register_export(aTHX_ "is_code", xs_is_code, is_code_call_checker); |
|
6227
|
53
|
|
|
|
|
|
register_export(aTHX_ "is_defined", xs_is_defined, is_defined_call_checker); |
|
6228
|
53
|
|
|
|
|
|
register_export(aTHX_ "is_string", xs_is_string, is_string_call_checker); |
|
6229
|
|
|
|
|
|
|
|
|
6230
|
|
|
|
|
|
|
/* String predicates */ |
|
6231
|
53
|
|
|
|
|
|
register_export(aTHX_ "is_empty", xs_is_empty, is_empty_call_checker); |
|
6232
|
53
|
|
|
|
|
|
register_export(aTHX_ "starts_with", xs_starts_with, starts_with_call_checker); |
|
6233
|
53
|
|
|
|
|
|
register_export(aTHX_ "ends_with", xs_ends_with, ends_with_call_checker); |
|
6234
|
53
|
|
|
|
|
|
register_export(aTHX_ "trim", xs_trim, trim_call_checker); |
|
6235
|
53
|
|
|
|
|
|
register_export(aTHX_ "ltrim", xs_ltrim, ltrim_call_checker); |
|
6236
|
53
|
|
|
|
|
|
register_export(aTHX_ "rtrim", xs_rtrim, rtrim_call_checker); |
|
6237
|
|
|
|
|
|
|
|
|
6238
|
|
|
|
|
|
|
/* Boolean predicates */ |
|
6239
|
53
|
|
|
|
|
|
register_export(aTHX_ "is_true", xs_is_true, is_true_call_checker); |
|
6240
|
53
|
|
|
|
|
|
register_export(aTHX_ "is_false", xs_is_false, is_false_call_checker); |
|
6241
|
53
|
|
|
|
|
|
register_export(aTHX_ "bool", xs_bool, bool_call_checker); |
|
6242
|
|
|
|
|
|
|
|
|
6243
|
|
|
|
|
|
|
/* Extended type predicates */ |
|
6244
|
53
|
|
|
|
|
|
register_export(aTHX_ "is_num", xs_is_num, is_num_call_checker); |
|
6245
|
53
|
|
|
|
|
|
register_export(aTHX_ "is_int", xs_is_int, is_int_call_checker); |
|
6246
|
53
|
|
|
|
|
|
register_export(aTHX_ "is_blessed", xs_is_blessed, is_blessed_call_checker); |
|
6247
|
53
|
|
|
|
|
|
register_export(aTHX_ "is_scalar_ref", xs_is_scalar_ref, is_scalar_ref_call_checker); |
|
6248
|
53
|
|
|
|
|
|
register_export(aTHX_ "is_regex", xs_is_regex, is_regex_call_checker); |
|
6249
|
53
|
|
|
|
|
|
register_export(aTHX_ "is_glob", xs_is_glob, is_glob_call_checker); |
|
6250
|
|
|
|
|
|
|
|
|
6251
|
|
|
|
|
|
|
/* Numeric predicates */ |
|
6252
|
53
|
|
|
|
|
|
register_export(aTHX_ "is_positive", xs_is_positive, is_positive_call_checker); |
|
6253
|
53
|
|
|
|
|
|
register_export(aTHX_ "is_negative", xs_is_negative, is_negative_call_checker); |
|
6254
|
53
|
|
|
|
|
|
register_export(aTHX_ "is_zero", xs_is_zero, is_zero_call_checker); |
|
6255
|
53
|
|
|
|
|
|
register_export(aTHX_ "is_even", xs_is_even, is_even_call_checker); |
|
6256
|
53
|
|
|
|
|
|
register_export(aTHX_ "is_odd", xs_is_odd, is_odd_call_checker); |
|
6257
|
53
|
|
|
|
|
|
register_export(aTHX_ "is_between", xs_is_between, is_between_call_checker); |
|
6258
|
|
|
|
|
|
|
|
|
6259
|
|
|
|
|
|
|
/* Collection predicates */ |
|
6260
|
53
|
|
|
|
|
|
register_export(aTHX_ "is_empty_array", xs_is_empty_array, is_empty_array_call_checker); |
|
6261
|
53
|
|
|
|
|
|
register_export(aTHX_ "is_empty_hash", xs_is_empty_hash, is_empty_hash_call_checker); |
|
6262
|
53
|
|
|
|
|
|
register_export(aTHX_ "array_len", xs_array_len, array_len_call_checker); |
|
6263
|
53
|
|
|
|
|
|
register_export(aTHX_ "hash_size", xs_hash_size, hash_size_call_checker); |
|
6264
|
53
|
|
|
|
|
|
register_export(aTHX_ "array_first", xs_array_first, array_first_call_checker); |
|
6265
|
53
|
|
|
|
|
|
register_export(aTHX_ "array_last", xs_array_last, array_last_call_checker); |
|
6266
|
|
|
|
|
|
|
|
|
6267
|
|
|
|
|
|
|
/* Conditional/numeric ops */ |
|
6268
|
53
|
|
|
|
|
|
register_export(aTHX_ "maybe", xs_maybe, maybe_call_checker); |
|
6269
|
53
|
|
|
|
|
|
register_export(aTHX_ "sign", xs_sign, sign_call_checker); |
|
6270
|
53
|
|
|
|
|
|
register_export(aTHX_ "min2", xs_min2, min2_call_checker); |
|
6271
|
53
|
|
|
|
|
|
register_export(aTHX_ "max2", xs_max2, max2_call_checker); |
|
6272
|
53
|
|
|
|
|
|
} |
|
6273
|
|
|
|
|
|
|
|
|
6274
|
85
|
|
|
|
|
|
static char* get_caller(pTHX) { |
|
6275
|
85
|
50
|
|
|
|
|
return HvNAME((HV*)CopSTASH(PL_curcop)); |
|
|
|
50
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
6276
|
|
|
|
|
|
|
} |
|
6277
|
|
|
|
|
|
|
|
|
6278
|
|
|
|
|
|
|
/* Fast O(1) import using hash lookup */ |
|
6279
|
85
|
|
|
|
|
|
XS_INTERNAL(xs_import) { |
|
6280
|
85
|
|
|
|
|
|
dXSARGS; |
|
6281
|
85
|
|
|
|
|
|
char *pkg = get_caller(aTHX); |
|
6282
|
|
|
|
|
|
|
IV i; |
|
6283
|
|
|
|
|
|
|
STRLEN name_len; |
|
6284
|
|
|
|
|
|
|
char full[512]; |
|
6285
|
|
|
|
|
|
|
|
|
6286
|
539
|
100
|
|
|
|
|
for (i = 1; i < items; i++) { |
|
6287
|
456
|
|
|
|
|
|
char *name = SvPV(ST(i), name_len); |
|
6288
|
456
|
|
|
|
|
|
SV **entry_sv = hv_fetch(g_export_hash, name, name_len, 0); |
|
6289
|
|
|
|
|
|
|
|
|
6290
|
456
|
100
|
|
|
|
|
if (!entry_sv || !*entry_sv) { |
|
|
|
50
|
|
|
|
|
|
|
6291
|
2
|
|
|
|
|
|
croak("util: unknown export '%s'", name); |
|
6292
|
|
|
|
|
|
|
} |
|
6293
|
|
|
|
|
|
|
|
|
6294
|
454
|
|
|
|
|
|
ExportEntry *entry = INT2PTR(ExportEntry*, SvIV(*entry_sv)); |
|
6295
|
454
|
|
|
|
|
|
snprintf(full, sizeof(full), "%s::%s", pkg, name); |
|
6296
|
|
|
|
|
|
|
|
|
6297
|
454
|
100
|
|
|
|
|
if (entry->xs_func) { |
|
6298
|
|
|
|
|
|
|
/* XS function: create XS stub in caller's namespace. |
|
6299
|
|
|
|
|
|
|
* Note: We intentionally do NOT install call checkers on exported |
|
6300
|
|
|
|
|
|
|
* functions. Call checkers are compile-time optimizations that work |
|
6301
|
|
|
|
|
|
|
* by transforming the op tree. They work on util::* functions because |
|
6302
|
|
|
|
|
|
|
* those are installed at boot time before any user code compiles. |
|
6303
|
|
|
|
|
|
|
* Users who want compile-time optimization should call util::func() |
|
6304
|
|
|
|
|
|
|
* directly instead of importing. */ |
|
6305
|
446
|
|
|
|
|
|
CV *cv = newXS(full, entry->xs_func, __FILE__); |
|
6306
|
|
|
|
|
|
|
PERL_UNUSED_VAR(cv); |
|
6307
|
8
|
50
|
|
|
|
|
} else if (entry->perl_cv) { |
|
6308
|
|
|
|
|
|
|
/* Perl coderef: create alias in caller's namespace */ |
|
6309
|
8
|
|
|
|
|
|
GV *gv = gv_fetchpv(full, GV_ADD, SVt_PVCV); |
|
6310
|
8
|
50
|
|
|
|
|
if (gv) { |
|
6311
|
|
|
|
|
|
|
/* Get the actual CV from the reference */ |
|
6312
|
8
|
|
|
|
|
|
CV *src_cv = (CV*)SvRV(entry->perl_cv); |
|
6313
|
|
|
|
|
|
|
/* Assign the CV to the glob's CODE slot */ |
|
6314
|
8
|
|
|
|
|
|
SvREFCNT_inc((SV*)src_cv); |
|
6315
|
8
|
|
|
|
|
|
GvCV_set(gv, src_cv); |
|
6316
|
|
|
|
|
|
|
} |
|
6317
|
|
|
|
|
|
|
} |
|
6318
|
|
|
|
|
|
|
} |
|
6319
|
|
|
|
|
|
|
|
|
6320
|
83
|
|
|
|
|
|
XSRETURN_EMPTY; |
|
6321
|
|
|
|
|
|
|
} |
|
6322
|
|
|
|
|
|
|
|
|
6323
|
|
|
|
|
|
|
/* ============================================ |
|
6324
|
|
|
|
|
|
|
Boot |
|
6325
|
|
|
|
|
|
|
============================================ */ |
|
6326
|
|
|
|
|
|
|
|
|
6327
|
53
|
|
|
|
|
|
XS_EXTERNAL(boot_Func__Util) { |
|
6328
|
53
|
|
|
|
|
|
dXSBOOTARGSXSAPIVERCHK; |
|
6329
|
|
|
|
|
|
|
PERL_UNUSED_VAR(items); |
|
6330
|
|
|
|
|
|
|
|
|
6331
|
|
|
|
|
|
|
/* Initialize built-in loop callbacks */ |
|
6332
|
53
|
|
|
|
|
|
init_builtin_callbacks(aTHX); |
|
6333
|
|
|
|
|
|
|
|
|
6334
|
|
|
|
|
|
|
/* Register custom ops */ |
|
6335
|
53
|
|
|
|
|
|
XopENTRY_set(&identity_xop, xop_name, "identity"); |
|
6336
|
53
|
|
|
|
|
|
XopENTRY_set(&identity_xop, xop_desc, "identity passthrough"); |
|
6337
|
53
|
|
|
|
|
|
Perl_custom_op_register(aTHX_ pp_identity, &identity_xop); |
|
6338
|
|
|
|
|
|
|
|
|
6339
|
53
|
|
|
|
|
|
XopENTRY_set(&always_xop, xop_name, "always"); |
|
6340
|
53
|
|
|
|
|
|
XopENTRY_set(&always_xop, xop_desc, "always return stored value"); |
|
6341
|
53
|
|
|
|
|
|
Perl_custom_op_register(aTHX_ pp_always, &always_xop); |
|
6342
|
|
|
|
|
|
|
|
|
6343
|
53
|
|
|
|
|
|
XopENTRY_set(&clamp_xop, xop_name, "clamp"); |
|
6344
|
53
|
|
|
|
|
|
XopENTRY_set(&clamp_xop, xop_desc, "clamp value between min and max"); |
|
6345
|
53
|
|
|
|
|
|
Perl_custom_op_register(aTHX_ pp_clamp, &clamp_xop); |
|
6346
|
|
|
|
|
|
|
|
|
6347
|
|
|
|
|
|
|
/* Register type predicate custom ops */ |
|
6348
|
53
|
|
|
|
|
|
XopENTRY_set(&is_ref_xop, xop_name, "is_ref"); |
|
6349
|
53
|
|
|
|
|
|
XopENTRY_set(&is_ref_xop, xop_desc, "check if value is a reference"); |
|
6350
|
53
|
|
|
|
|
|
Perl_custom_op_register(aTHX_ pp_is_ref, &is_ref_xop); |
|
6351
|
|
|
|
|
|
|
|
|
6352
|
53
|
|
|
|
|
|
XopENTRY_set(&is_array_xop, xop_name, "is_array"); |
|
6353
|
53
|
|
|
|
|
|
XopENTRY_set(&is_array_xop, xop_desc, "check if value is an arrayref"); |
|
6354
|
53
|
|
|
|
|
|
Perl_custom_op_register(aTHX_ pp_is_array, &is_array_xop); |
|
6355
|
|
|
|
|
|
|
|
|
6356
|
53
|
|
|
|
|
|
XopENTRY_set(&is_hash_xop, xop_name, "is_hash"); |
|
6357
|
53
|
|
|
|
|
|
XopENTRY_set(&is_hash_xop, xop_desc, "check if value is a hashref"); |
|
6358
|
53
|
|
|
|
|
|
Perl_custom_op_register(aTHX_ pp_is_hash, &is_hash_xop); |
|
6359
|
|
|
|
|
|
|
|
|
6360
|
53
|
|
|
|
|
|
XopENTRY_set(&is_code_xop, xop_name, "is_code"); |
|
6361
|
53
|
|
|
|
|
|
XopENTRY_set(&is_code_xop, xop_desc, "check if value is a coderef"); |
|
6362
|
53
|
|
|
|
|
|
Perl_custom_op_register(aTHX_ pp_is_code, &is_code_xop); |
|
6363
|
|
|
|
|
|
|
|
|
6364
|
53
|
|
|
|
|
|
XopENTRY_set(&is_defined_xop, xop_name, "is_defined"); |
|
6365
|
53
|
|
|
|
|
|
XopENTRY_set(&is_defined_xop, xop_desc, "check if value is defined"); |
|
6366
|
53
|
|
|
|
|
|
Perl_custom_op_register(aTHX_ pp_is_defined, &is_defined_xop); |
|
6367
|
|
|
|
|
|
|
|
|
6368
|
|
|
|
|
|
|
/* Register string predicate custom ops */ |
|
6369
|
53
|
|
|
|
|
|
XopENTRY_set(&is_empty_xop, xop_name, "is_empty"); |
|
6370
|
53
|
|
|
|
|
|
XopENTRY_set(&is_empty_xop, xop_desc, "check if string is empty"); |
|
6371
|
53
|
|
|
|
|
|
Perl_custom_op_register(aTHX_ pp_is_empty, &is_empty_xop); |
|
6372
|
|
|
|
|
|
|
|
|
6373
|
53
|
|
|
|
|
|
XopENTRY_set(&starts_with_xop, xop_name, "starts_with"); |
|
6374
|
53
|
|
|
|
|
|
XopENTRY_set(&starts_with_xop, xop_desc, "check if string starts with prefix"); |
|
6375
|
53
|
|
|
|
|
|
Perl_custom_op_register(aTHX_ pp_starts_with, &starts_with_xop); |
|
6376
|
|
|
|
|
|
|
|
|
6377
|
53
|
|
|
|
|
|
XopENTRY_set(&ends_with_xop, xop_name, "ends_with"); |
|
6378
|
53
|
|
|
|
|
|
XopENTRY_set(&ends_with_xop, xop_desc, "check if string ends with suffix"); |
|
6379
|
53
|
|
|
|
|
|
Perl_custom_op_register(aTHX_ pp_ends_with, &ends_with_xop); |
|
6380
|
|
|
|
|
|
|
|
|
6381
|
|
|
|
|
|
|
/* Register boolean/truthiness custom ops */ |
|
6382
|
53
|
|
|
|
|
|
XopENTRY_set(&is_true_xop, xop_name, "is_true"); |
|
6383
|
53
|
|
|
|
|
|
XopENTRY_set(&is_true_xop, xop_desc, "check if value is truthy"); |
|
6384
|
53
|
|
|
|
|
|
Perl_custom_op_register(aTHX_ pp_is_true, &is_true_xop); |
|
6385
|
|
|
|
|
|
|
|
|
6386
|
53
|
|
|
|
|
|
XopENTRY_set(&is_false_xop, xop_name, "is_false"); |
|
6387
|
53
|
|
|
|
|
|
XopENTRY_set(&is_false_xop, xop_desc, "check if value is falsy"); |
|
6388
|
53
|
|
|
|
|
|
Perl_custom_op_register(aTHX_ pp_is_false, &is_false_xop); |
|
6389
|
|
|
|
|
|
|
|
|
6390
|
53
|
|
|
|
|
|
XopENTRY_set(&bool_xop, xop_name, "bool"); |
|
6391
|
53
|
|
|
|
|
|
XopENTRY_set(&bool_xop, xop_desc, "normalize to boolean"); |
|
6392
|
53
|
|
|
|
|
|
Perl_custom_op_register(aTHX_ pp_bool, &bool_xop); |
|
6393
|
|
|
|
|
|
|
|
|
6394
|
|
|
|
|
|
|
/* Register extended type predicate custom ops */ |
|
6395
|
53
|
|
|
|
|
|
XopENTRY_set(&is_num_xop, xop_name, "is_num"); |
|
6396
|
53
|
|
|
|
|
|
XopENTRY_set(&is_num_xop, xop_desc, "check if value is numeric"); |
|
6397
|
53
|
|
|
|
|
|
Perl_custom_op_register(aTHX_ pp_is_num, &is_num_xop); |
|
6398
|
|
|
|
|
|
|
|
|
6399
|
53
|
|
|
|
|
|
XopENTRY_set(&is_int_xop, xop_name, "is_int"); |
|
6400
|
53
|
|
|
|
|
|
XopENTRY_set(&is_int_xop, xop_desc, "check if value is integer"); |
|
6401
|
53
|
|
|
|
|
|
Perl_custom_op_register(aTHX_ pp_is_int, &is_int_xop); |
|
6402
|
|
|
|
|
|
|
|
|
6403
|
53
|
|
|
|
|
|
XopENTRY_set(&is_blessed_xop, xop_name, "is_blessed"); |
|
6404
|
53
|
|
|
|
|
|
XopENTRY_set(&is_blessed_xop, xop_desc, "check if value is blessed"); |
|
6405
|
53
|
|
|
|
|
|
Perl_custom_op_register(aTHX_ pp_is_blessed, &is_blessed_xop); |
|
6406
|
|
|
|
|
|
|
|
|
6407
|
53
|
|
|
|
|
|
XopENTRY_set(&is_scalar_ref_xop, xop_name, "is_scalar_ref"); |
|
6408
|
53
|
|
|
|
|
|
XopENTRY_set(&is_scalar_ref_xop, xop_desc, "check if value is scalar reference"); |
|
6409
|
53
|
|
|
|
|
|
Perl_custom_op_register(aTHX_ pp_is_scalar_ref, &is_scalar_ref_xop); |
|
6410
|
|
|
|
|
|
|
|
|
6411
|
53
|
|
|
|
|
|
XopENTRY_set(&is_regex_xop, xop_name, "is_regex"); |
|
6412
|
53
|
|
|
|
|
|
XopENTRY_set(&is_regex_xop, xop_desc, "check if value is compiled regex"); |
|
6413
|
53
|
|
|
|
|
|
Perl_custom_op_register(aTHX_ pp_is_regex, &is_regex_xop); |
|
6414
|
|
|
|
|
|
|
|
|
6415
|
53
|
|
|
|
|
|
XopENTRY_set(&is_glob_xop, xop_name, "is_glob"); |
|
6416
|
53
|
|
|
|
|
|
XopENTRY_set(&is_glob_xop, xop_desc, "check if value is glob"); |
|
6417
|
53
|
|
|
|
|
|
Perl_custom_op_register(aTHX_ pp_is_glob, &is_glob_xop); |
|
6418
|
|
|
|
|
|
|
|
|
6419
|
53
|
|
|
|
|
|
XopENTRY_set(&is_string_xop, xop_name, "is_string"); |
|
6420
|
53
|
|
|
|
|
|
XopENTRY_set(&is_string_xop, xop_desc, "check if value is plain scalar"); |
|
6421
|
53
|
|
|
|
|
|
Perl_custom_op_register(aTHX_ pp_is_string, &is_string_xop); |
|
6422
|
|
|
|
|
|
|
|
|
6423
|
|
|
|
|
|
|
/* Register numeric predicate custom ops */ |
|
6424
|
53
|
|
|
|
|
|
XopENTRY_set(&is_positive_xop, xop_name, "is_positive"); |
|
6425
|
53
|
|
|
|
|
|
XopENTRY_set(&is_positive_xop, xop_desc, "check if value is positive"); |
|
6426
|
53
|
|
|
|
|
|
Perl_custom_op_register(aTHX_ pp_is_positive, &is_positive_xop); |
|
6427
|
|
|
|
|
|
|
|
|
6428
|
53
|
|
|
|
|
|
XopENTRY_set(&is_negative_xop, xop_name, "is_negative"); |
|
6429
|
53
|
|
|
|
|
|
XopENTRY_set(&is_negative_xop, xop_desc, "check if value is negative"); |
|
6430
|
53
|
|
|
|
|
|
Perl_custom_op_register(aTHX_ pp_is_negative, &is_negative_xop); |
|
6431
|
|
|
|
|
|
|
|
|
6432
|
53
|
|
|
|
|
|
XopENTRY_set(&is_zero_xop, xop_name, "is_zero"); |
|
6433
|
53
|
|
|
|
|
|
XopENTRY_set(&is_zero_xop, xop_desc, "check if value is zero"); |
|
6434
|
53
|
|
|
|
|
|
Perl_custom_op_register(aTHX_ pp_is_zero, &is_zero_xop); |
|
6435
|
|
|
|
|
|
|
|
|
6436
|
|
|
|
|
|
|
/* Register numeric utility custom ops */ |
|
6437
|
53
|
|
|
|
|
|
XopENTRY_set(&is_even_xop, xop_name, "is_even"); |
|
6438
|
53
|
|
|
|
|
|
XopENTRY_set(&is_even_xop, xop_desc, "check if integer is even"); |
|
6439
|
53
|
|
|
|
|
|
Perl_custom_op_register(aTHX_ pp_is_even, &is_even_xop); |
|
6440
|
|
|
|
|
|
|
|
|
6441
|
53
|
|
|
|
|
|
XopENTRY_set(&is_odd_xop, xop_name, "is_odd"); |
|
6442
|
53
|
|
|
|
|
|
XopENTRY_set(&is_odd_xop, xop_desc, "check if integer is odd"); |
|
6443
|
53
|
|
|
|
|
|
Perl_custom_op_register(aTHX_ pp_is_odd, &is_odd_xop); |
|
6444
|
|
|
|
|
|
|
|
|
6445
|
53
|
|
|
|
|
|
XopENTRY_set(&is_between_xop, xop_name, "is_between"); |
|
6446
|
53
|
|
|
|
|
|
XopENTRY_set(&is_between_xop, xop_desc, "check if value is between min and max"); |
|
6447
|
53
|
|
|
|
|
|
Perl_custom_op_register(aTHX_ pp_is_between, &is_between_xop); |
|
6448
|
|
|
|
|
|
|
|
|
6449
|
|
|
|
|
|
|
/* Register collection custom ops */ |
|
6450
|
53
|
|
|
|
|
|
XopENTRY_set(&is_empty_array_xop, xop_name, "is_empty_array"); |
|
6451
|
53
|
|
|
|
|
|
XopENTRY_set(&is_empty_array_xop, xop_desc, "check if arrayref is empty"); |
|
6452
|
53
|
|
|
|
|
|
Perl_custom_op_register(aTHX_ pp_is_empty_array, &is_empty_array_xop); |
|
6453
|
|
|
|
|
|
|
|
|
6454
|
53
|
|
|
|
|
|
XopENTRY_set(&is_empty_hash_xop, xop_name, "is_empty_hash"); |
|
6455
|
53
|
|
|
|
|
|
XopENTRY_set(&is_empty_hash_xop, xop_desc, "check if hashref is empty"); |
|
6456
|
53
|
|
|
|
|
|
Perl_custom_op_register(aTHX_ pp_is_empty_hash, &is_empty_hash_xop); |
|
6457
|
|
|
|
|
|
|
|
|
6458
|
53
|
|
|
|
|
|
XopENTRY_set(&array_len_xop, xop_name, "array_len"); |
|
6459
|
53
|
|
|
|
|
|
XopENTRY_set(&array_len_xop, xop_desc, "get array length"); |
|
6460
|
53
|
|
|
|
|
|
Perl_custom_op_register(aTHX_ pp_array_len, &array_len_xop); |
|
6461
|
|
|
|
|
|
|
|
|
6462
|
53
|
|
|
|
|
|
XopENTRY_set(&hash_size_xop, xop_name, "hash_size"); |
|
6463
|
53
|
|
|
|
|
|
XopENTRY_set(&hash_size_xop, xop_desc, "get hash key count"); |
|
6464
|
53
|
|
|
|
|
|
Perl_custom_op_register(aTHX_ pp_hash_size, &hash_size_xop); |
|
6465
|
|
|
|
|
|
|
|
|
6466
|
53
|
|
|
|
|
|
XopENTRY_set(&array_first_xop, xop_name, "array_first"); |
|
6467
|
53
|
|
|
|
|
|
XopENTRY_set(&array_first_xop, xop_desc, "get first array element"); |
|
6468
|
53
|
|
|
|
|
|
Perl_custom_op_register(aTHX_ pp_array_first, &array_first_xop); |
|
6469
|
|
|
|
|
|
|
|
|
6470
|
53
|
|
|
|
|
|
XopENTRY_set(&array_last_xop, xop_name, "array_last"); |
|
6471
|
53
|
|
|
|
|
|
XopENTRY_set(&array_last_xop, xop_desc, "get last array element"); |
|
6472
|
53
|
|
|
|
|
|
Perl_custom_op_register(aTHX_ pp_array_last, &array_last_xop); |
|
6473
|
|
|
|
|
|
|
|
|
6474
|
|
|
|
|
|
|
/* Register string manipulation custom ops */ |
|
6475
|
53
|
|
|
|
|
|
XopENTRY_set(&trim_xop, xop_name, "trim"); |
|
6476
|
53
|
|
|
|
|
|
XopENTRY_set(&trim_xop, xop_desc, "trim whitespace from string"); |
|
6477
|
53
|
|
|
|
|
|
Perl_custom_op_register(aTHX_ pp_trim, &trim_xop); |
|
6478
|
|
|
|
|
|
|
|
|
6479
|
53
|
|
|
|
|
|
XopENTRY_set(<rim_xop, xop_name, "ltrim"); |
|
6480
|
53
|
|
|
|
|
|
XopENTRY_set(<rim_xop, xop_desc, "trim leading whitespace"); |
|
6481
|
53
|
|
|
|
|
|
Perl_custom_op_register(aTHX_ pp_ltrim, <rim_xop); |
|
6482
|
|
|
|
|
|
|
|
|
6483
|
53
|
|
|
|
|
|
XopENTRY_set(&rtrim_xop, xop_name, "rtrim"); |
|
6484
|
53
|
|
|
|
|
|
XopENTRY_set(&rtrim_xop, xop_desc, "trim trailing whitespace"); |
|
6485
|
53
|
|
|
|
|
|
Perl_custom_op_register(aTHX_ pp_rtrim, &rtrim_xop); |
|
6486
|
|
|
|
|
|
|
|
|
6487
|
|
|
|
|
|
|
/* Register conditional custom ops */ |
|
6488
|
53
|
|
|
|
|
|
XopENTRY_set(&maybe_xop, xop_name, "maybe"); |
|
6489
|
53
|
|
|
|
|
|
XopENTRY_set(&maybe_xop, xop_desc, "return value if defined"); |
|
6490
|
53
|
|
|
|
|
|
Perl_custom_op_register(aTHX_ pp_maybe, &maybe_xop); |
|
6491
|
|
|
|
|
|
|
|
|
6492
|
|
|
|
|
|
|
/* Register numeric custom ops */ |
|
6493
|
53
|
|
|
|
|
|
XopENTRY_set(&sign_xop, xop_name, "sign"); |
|
6494
|
53
|
|
|
|
|
|
XopENTRY_set(&sign_xop, xop_desc, "return sign of number"); |
|
6495
|
53
|
|
|
|
|
|
Perl_custom_op_register(aTHX_ pp_sign, &sign_xop); |
|
6496
|
|
|
|
|
|
|
|
|
6497
|
53
|
|
|
|
|
|
XopENTRY_set(&min2_xop, xop_name, "min2"); |
|
6498
|
53
|
|
|
|
|
|
XopENTRY_set(&min2_xop, xop_desc, "return smaller of two values"); |
|
6499
|
53
|
|
|
|
|
|
Perl_custom_op_register(aTHX_ pp_min2, &min2_xop); |
|
6500
|
|
|
|
|
|
|
|
|
6501
|
53
|
|
|
|
|
|
XopENTRY_set(&max2_xop, xop_name, "max2"); |
|
6502
|
53
|
|
|
|
|
|
XopENTRY_set(&max2_xop, xop_desc, "return larger of two values"); |
|
6503
|
53
|
|
|
|
|
|
Perl_custom_op_register(aTHX_ pp_max2, &max2_xop); |
|
6504
|
|
|
|
|
|
|
|
|
6505
|
|
|
|
|
|
|
/* Initialize memo storage */ |
|
6506
|
53
|
|
|
|
|
|
g_memo_size = 16; |
|
6507
|
53
|
50
|
|
|
|
|
Newxz(g_memos, g_memo_size, MemoizedFunc); |
|
6508
|
|
|
|
|
|
|
|
|
6509
|
|
|
|
|
|
|
/* Initialize lazy storage */ |
|
6510
|
53
|
|
|
|
|
|
g_lazy_size = 16; |
|
6511
|
53
|
50
|
|
|
|
|
Newxz(g_lazies, g_lazy_size, LazyValue); |
|
6512
|
|
|
|
|
|
|
|
|
6513
|
|
|
|
|
|
|
/* Initialize always storage */ |
|
6514
|
53
|
|
|
|
|
|
g_always_size = 16; |
|
6515
|
53
|
50
|
|
|
|
|
Newxz(g_always_values, g_always_size, SV*); |
|
6516
|
|
|
|
|
|
|
|
|
6517
|
|
|
|
|
|
|
/* Initialize once storage */ |
|
6518
|
53
|
|
|
|
|
|
g_once_size = 16; |
|
6519
|
53
|
50
|
|
|
|
|
Newxz(g_onces, g_once_size, OnceFunc); |
|
6520
|
|
|
|
|
|
|
|
|
6521
|
|
|
|
|
|
|
/* Initialize partial storage */ |
|
6522
|
53
|
|
|
|
|
|
g_partial_size = 16; |
|
6523
|
53
|
50
|
|
|
|
|
Newxz(g_partials, g_partial_size, PartialFunc); |
|
6524
|
|
|
|
|
|
|
|
|
6525
|
|
|
|
|
|
|
/* Initialize export hash for O(1) import lookup */ |
|
6526
|
53
|
|
|
|
|
|
init_export_hash(aTHX); |
|
6527
|
|
|
|
|
|
|
|
|
6528
|
|
|
|
|
|
|
/* Export functions */ |
|
6529
|
53
|
|
|
|
|
|
newXS("Func::Util::import", xs_import, __FILE__); |
|
6530
|
|
|
|
|
|
|
|
|
6531
|
|
|
|
|
|
|
/* Export registry API */ |
|
6532
|
53
|
|
|
|
|
|
newXS("Func::Util::register_export", xs_register_export, __FILE__); |
|
6533
|
53
|
|
|
|
|
|
newXS("Func::Util::has_export", xs_has_export, __FILE__); |
|
6534
|
53
|
|
|
|
|
|
newXS("Func::Util::list_exports", xs_list_exports, __FILE__); |
|
6535
|
|
|
|
|
|
|
|
|
6536
|
53
|
|
|
|
|
|
newXS("Func::Util::memo", xs_memo, __FILE__); |
|
6537
|
53
|
|
|
|
|
|
newXS("Func::Util::pipeline", xs_pipe, __FILE__); |
|
6538
|
53
|
|
|
|
|
|
newXS("Func::Util::compose", xs_compose, __FILE__); |
|
6539
|
53
|
|
|
|
|
|
newXS("Func::Util::lazy", xs_lazy, __FILE__); |
|
6540
|
53
|
|
|
|
|
|
newXS("Func::Util::force", xs_force, __FILE__); |
|
6541
|
53
|
|
|
|
|
|
newXS("Func::Util::dig", xs_dig, __FILE__); |
|
6542
|
|
|
|
|
|
|
|
|
6543
|
|
|
|
|
|
|
{ |
|
6544
|
53
|
|
|
|
|
|
CV *cv = newXS("Func::Util::clamp", xs_clamp, __FILE__); |
|
6545
|
53
|
|
|
|
|
|
cv_set_call_checker(cv, clamp_call_checker, (SV*)cv); |
|
6546
|
|
|
|
|
|
|
} |
|
6547
|
|
|
|
|
|
|
|
|
6548
|
53
|
|
|
|
|
|
newXS("Func::Util::tap", xs_tap, __FILE__); |
|
6549
|
|
|
|
|
|
|
|
|
6550
|
|
|
|
|
|
|
{ |
|
6551
|
53
|
|
|
|
|
|
CV *cv = newXS("Func::Util::identity", xs_identity, __FILE__); |
|
6552
|
53
|
|
|
|
|
|
cv_set_call_checker(cv, identity_call_checker, (SV*)cv); |
|
6553
|
|
|
|
|
|
|
} |
|
6554
|
|
|
|
|
|
|
|
|
6555
|
53
|
|
|
|
|
|
newXS("Func::Util::always", xs_always, __FILE__); |
|
6556
|
|
|
|
|
|
|
{ |
|
6557
|
53
|
|
|
|
|
|
CV *cv = newXS("Func::Util::noop", xs_noop, __FILE__); |
|
6558
|
53
|
|
|
|
|
|
cv_set_call_checker(cv, noop_call_checker, (SV*)cv); |
|
6559
|
|
|
|
|
|
|
} |
|
6560
|
53
|
|
|
|
|
|
newXS("Func::Util::stub_true", xs_stub_true, __FILE__); |
|
6561
|
53
|
|
|
|
|
|
newXS("Func::Util::stub_false", xs_stub_false, __FILE__); |
|
6562
|
53
|
|
|
|
|
|
newXS("Func::Util::stub_array", xs_stub_array, __FILE__); |
|
6563
|
53
|
|
|
|
|
|
newXS("Func::Util::stub_hash", xs_stub_hash, __FILE__); |
|
6564
|
53
|
|
|
|
|
|
newXS("Func::Util::stub_string", xs_stub_string, __FILE__); |
|
6565
|
53
|
|
|
|
|
|
newXS("Func::Util::stub_zero", xs_stub_zero, __FILE__); |
|
6566
|
53
|
|
|
|
|
|
newXS("Func::Util::nvl", xs_nvl, __FILE__); |
|
6567
|
53
|
|
|
|
|
|
newXS("Func::Util::coalesce", xs_coalesce, __FILE__); |
|
6568
|
|
|
|
|
|
|
|
|
6569
|
|
|
|
|
|
|
/* List functions */ |
|
6570
|
53
|
|
|
|
|
|
newXS("Func::Util::first", xs_first, __FILE__); |
|
6571
|
53
|
|
|
|
|
|
newXS("Func::Util::firstr", xs_firstr, __FILE__); |
|
6572
|
53
|
|
|
|
|
|
newXS("Func::Util::any", xs_any, __FILE__); |
|
6573
|
53
|
|
|
|
|
|
newXS("Func::Util::all", xs_all, __FILE__); |
|
6574
|
53
|
|
|
|
|
|
newXS("Func::Util::none", xs_none, __FILE__); |
|
6575
|
|
|
|
|
|
|
#ifdef dMULTICALL |
|
6576
|
53
|
|
|
|
|
|
newXS("Func::Util::first_inline", xs_first_inline, __FILE__); /* experimental, 5.11+ only */ |
|
6577
|
|
|
|
|
|
|
#endif |
|
6578
|
|
|
|
|
|
|
|
|
6579
|
|
|
|
|
|
|
/* Named callback loop functions */ |
|
6580
|
53
|
|
|
|
|
|
newXS("Func::Util::any_cb", xs_any_cb, __FILE__); |
|
6581
|
53
|
|
|
|
|
|
newXS("Func::Util::all_cb", xs_all_cb, __FILE__); |
|
6582
|
53
|
|
|
|
|
|
newXS("Func::Util::none_cb", xs_none_cb, __FILE__); |
|
6583
|
53
|
|
|
|
|
|
newXS("Func::Util::first_cb", xs_first_cb, __FILE__); |
|
6584
|
53
|
|
|
|
|
|
newXS("Func::Util::grep_cb", xs_grep_cb, __FILE__); |
|
6585
|
53
|
|
|
|
|
|
newXS("Func::Util::count_cb", xs_count_cb, __FILE__); |
|
6586
|
53
|
|
|
|
|
|
newXS("Func::Util::partition_cb", xs_partition_cb, __FILE__); |
|
6587
|
53
|
|
|
|
|
|
newXS("Func::Util::final_cb", xs_final_cb, __FILE__); |
|
6588
|
53
|
|
|
|
|
|
newXS("Func::Util::register_callback", xs_register_callback, __FILE__); |
|
6589
|
53
|
|
|
|
|
|
newXS("Func::Util::has_callback", xs_has_callback, __FILE__); |
|
6590
|
53
|
|
|
|
|
|
newXS("Func::Util::list_callbacks", xs_list_callbacks, __FILE__); |
|
6591
|
|
|
|
|
|
|
|
|
6592
|
|
|
|
|
|
|
/* Specialized array predicates - pure C, no callback */ |
|
6593
|
53
|
|
|
|
|
|
newXS("Func::Util::first_gt", xs_first_gt, __FILE__); |
|
6594
|
53
|
|
|
|
|
|
newXS("Func::Util::first_lt", xs_first_lt, __FILE__); |
|
6595
|
53
|
|
|
|
|
|
newXS("Func::Util::first_ge", xs_first_ge, __FILE__); |
|
6596
|
53
|
|
|
|
|
|
newXS("Func::Util::first_le", xs_first_le, __FILE__); |
|
6597
|
53
|
|
|
|
|
|
newXS("Func::Util::first_eq", xs_first_eq, __FILE__); |
|
6598
|
53
|
|
|
|
|
|
newXS("Func::Util::first_ne", xs_first_ne, __FILE__); |
|
6599
|
53
|
|
|
|
|
|
newXS("Func::Util::final", xs_final, __FILE__); |
|
6600
|
53
|
|
|
|
|
|
newXS("Func::Util::final_gt", xs_final_gt, __FILE__); |
|
6601
|
53
|
|
|
|
|
|
newXS("Func::Util::final_lt", xs_final_lt, __FILE__); |
|
6602
|
53
|
|
|
|
|
|
newXS("Func::Util::final_ge", xs_final_ge, __FILE__); |
|
6603
|
53
|
|
|
|
|
|
newXS("Func::Util::final_le", xs_final_le, __FILE__); |
|
6604
|
53
|
|
|
|
|
|
newXS("Func::Util::final_eq", xs_final_eq, __FILE__); |
|
6605
|
53
|
|
|
|
|
|
newXS("Func::Util::final_ne", xs_final_ne, __FILE__); |
|
6606
|
53
|
|
|
|
|
|
newXS("Func::Util::any_gt", xs_any_gt, __FILE__); |
|
6607
|
53
|
|
|
|
|
|
newXS("Func::Util::any_lt", xs_any_lt, __FILE__); |
|
6608
|
53
|
|
|
|
|
|
newXS("Func::Util::any_ge", xs_any_ge, __FILE__); |
|
6609
|
53
|
|
|
|
|
|
newXS("Func::Util::any_le", xs_any_le, __FILE__); |
|
6610
|
53
|
|
|
|
|
|
newXS("Func::Util::any_eq", xs_any_eq, __FILE__); |
|
6611
|
53
|
|
|
|
|
|
newXS("Func::Util::any_ne", xs_any_ne, __FILE__); |
|
6612
|
53
|
|
|
|
|
|
newXS("Func::Util::all_gt", xs_all_gt, __FILE__); |
|
6613
|
53
|
|
|
|
|
|
newXS("Func::Util::all_lt", xs_all_lt, __FILE__); |
|
6614
|
53
|
|
|
|
|
|
newXS("Func::Util::all_ge", xs_all_ge, __FILE__); |
|
6615
|
53
|
|
|
|
|
|
newXS("Func::Util::all_le", xs_all_le, __FILE__); |
|
6616
|
53
|
|
|
|
|
|
newXS("Func::Util::all_eq", xs_all_eq, __FILE__); |
|
6617
|
53
|
|
|
|
|
|
newXS("Func::Util::all_ne", xs_all_ne, __FILE__); |
|
6618
|
53
|
|
|
|
|
|
newXS("Func::Util::none_gt", xs_none_gt, __FILE__); |
|
6619
|
53
|
|
|
|
|
|
newXS("Func::Util::none_lt", xs_none_lt, __FILE__); |
|
6620
|
53
|
|
|
|
|
|
newXS("Func::Util::none_ge", xs_none_ge, __FILE__); |
|
6621
|
53
|
|
|
|
|
|
newXS("Func::Util::none_le", xs_none_le, __FILE__); |
|
6622
|
53
|
|
|
|
|
|
newXS("Func::Util::none_eq", xs_none_eq, __FILE__); |
|
6623
|
53
|
|
|
|
|
|
newXS("Func::Util::none_ne", xs_none_ne, __FILE__); |
|
6624
|
|
|
|
|
|
|
|
|
6625
|
|
|
|
|
|
|
/* Functional combinators */ |
|
6626
|
53
|
|
|
|
|
|
newXS("Func::Util::negate", xs_negate, __FILE__); |
|
6627
|
53
|
|
|
|
|
|
newXS("Func::Util::once", xs_once, __FILE__); |
|
6628
|
53
|
|
|
|
|
|
newXS("Func::Util::partial", xs_partial, __FILE__); |
|
6629
|
|
|
|
|
|
|
|
|
6630
|
|
|
|
|
|
|
/* Data extraction */ |
|
6631
|
53
|
|
|
|
|
|
newXS("Func::Util::pick", xs_pick, __FILE__); |
|
6632
|
53
|
|
|
|
|
|
newXS("Func::Util::pluck", xs_pluck, __FILE__); |
|
6633
|
53
|
|
|
|
|
|
newXS("Func::Util::omit", xs_omit, __FILE__); |
|
6634
|
53
|
|
|
|
|
|
newXS("Func::Util::uniq", xs_uniq, __FILE__); |
|
6635
|
53
|
|
|
|
|
|
newXS("Func::Util::partition", xs_partition, __FILE__); |
|
6636
|
53
|
|
|
|
|
|
newXS("Func::Util::defaults", xs_defaults, __FILE__); |
|
6637
|
|
|
|
|
|
|
|
|
6638
|
|
|
|
|
|
|
/* Type predicates with call checkers */ |
|
6639
|
|
|
|
|
|
|
{ |
|
6640
|
53
|
|
|
|
|
|
CV *cv = newXS("Func::Util::is_ref", xs_is_ref, __FILE__); |
|
6641
|
53
|
|
|
|
|
|
cv_set_call_checker(cv, is_ref_call_checker, (SV*)cv); |
|
6642
|
|
|
|
|
|
|
} |
|
6643
|
|
|
|
|
|
|
{ |
|
6644
|
53
|
|
|
|
|
|
CV *cv = newXS("Func::Util::is_array", xs_is_array, __FILE__); |
|
6645
|
53
|
|
|
|
|
|
cv_set_call_checker(cv, is_array_call_checker, (SV*)cv); |
|
6646
|
|
|
|
|
|
|
} |
|
6647
|
|
|
|
|
|
|
{ |
|
6648
|
53
|
|
|
|
|
|
CV *cv = newXS("Func::Util::is_hash", xs_is_hash, __FILE__); |
|
6649
|
53
|
|
|
|
|
|
cv_set_call_checker(cv, is_hash_call_checker, (SV*)cv); |
|
6650
|
|
|
|
|
|
|
} |
|
6651
|
|
|
|
|
|
|
{ |
|
6652
|
53
|
|
|
|
|
|
CV *cv = newXS("Func::Util::is_code", xs_is_code, __FILE__); |
|
6653
|
53
|
|
|
|
|
|
cv_set_call_checker(cv, is_code_call_checker, (SV*)cv); |
|
6654
|
|
|
|
|
|
|
} |
|
6655
|
|
|
|
|
|
|
{ |
|
6656
|
53
|
|
|
|
|
|
CV *cv = newXS("Func::Util::is_defined", xs_is_defined, __FILE__); |
|
6657
|
53
|
|
|
|
|
|
cv_set_call_checker(cv, is_defined_call_checker, (SV*)cv); |
|
6658
|
|
|
|
|
|
|
} |
|
6659
|
|
|
|
|
|
|
|
|
6660
|
|
|
|
|
|
|
/* String predicates with call checkers */ |
|
6661
|
|
|
|
|
|
|
{ |
|
6662
|
53
|
|
|
|
|
|
CV *cv = newXS("Func::Util::is_empty", xs_is_empty, __FILE__); |
|
6663
|
53
|
|
|
|
|
|
cv_set_call_checker(cv, is_empty_call_checker, (SV*)cv); |
|
6664
|
|
|
|
|
|
|
} |
|
6665
|
|
|
|
|
|
|
{ |
|
6666
|
53
|
|
|
|
|
|
CV *cv = newXS("Func::Util::starts_with", xs_starts_with, __FILE__); |
|
6667
|
53
|
|
|
|
|
|
cv_set_call_checker(cv, starts_with_call_checker, (SV*)cv); |
|
6668
|
|
|
|
|
|
|
} |
|
6669
|
|
|
|
|
|
|
{ |
|
6670
|
53
|
|
|
|
|
|
CV *cv = newXS("Func::Util::ends_with", xs_ends_with, __FILE__); |
|
6671
|
53
|
|
|
|
|
|
cv_set_call_checker(cv, ends_with_call_checker, (SV*)cv); |
|
6672
|
|
|
|
|
|
|
} |
|
6673
|
53
|
|
|
|
|
|
newXS("Func::Util::count", xs_count, __FILE__); |
|
6674
|
53
|
|
|
|
|
|
newXS("Func::Util::replace_all", xs_replace_all, __FILE__); |
|
6675
|
|
|
|
|
|
|
|
|
6676
|
|
|
|
|
|
|
/* Boolean/Truthiness predicates with call checkers */ |
|
6677
|
|
|
|
|
|
|
{ |
|
6678
|
53
|
|
|
|
|
|
CV *cv = newXS("Func::Util::is_true", xs_is_true, __FILE__); |
|
6679
|
53
|
|
|
|
|
|
cv_set_call_checker(cv, is_true_call_checker, (SV*)cv); |
|
6680
|
|
|
|
|
|
|
} |
|
6681
|
|
|
|
|
|
|
{ |
|
6682
|
53
|
|
|
|
|
|
CV *cv = newXS("Func::Util::is_false", xs_is_false, __FILE__); |
|
6683
|
53
|
|
|
|
|
|
cv_set_call_checker(cv, is_false_call_checker, (SV*)cv); |
|
6684
|
|
|
|
|
|
|
} |
|
6685
|
|
|
|
|
|
|
{ |
|
6686
|
53
|
|
|
|
|
|
CV *cv = newXS("Func::Util::bool", xs_bool, __FILE__); |
|
6687
|
53
|
|
|
|
|
|
cv_set_call_checker(cv, bool_call_checker, (SV*)cv); |
|
6688
|
|
|
|
|
|
|
} |
|
6689
|
|
|
|
|
|
|
|
|
6690
|
|
|
|
|
|
|
/* Extended type predicates with call checkers */ |
|
6691
|
|
|
|
|
|
|
{ |
|
6692
|
53
|
|
|
|
|
|
CV *cv = newXS("Func::Util::is_num", xs_is_num, __FILE__); |
|
6693
|
53
|
|
|
|
|
|
cv_set_call_checker(cv, is_num_call_checker, (SV*)cv); |
|
6694
|
|
|
|
|
|
|
} |
|
6695
|
|
|
|
|
|
|
{ |
|
6696
|
53
|
|
|
|
|
|
CV *cv = newXS("Func::Util::is_int", xs_is_int, __FILE__); |
|
6697
|
53
|
|
|
|
|
|
cv_set_call_checker(cv, is_int_call_checker, (SV*)cv); |
|
6698
|
|
|
|
|
|
|
} |
|
6699
|
|
|
|
|
|
|
{ |
|
6700
|
53
|
|
|
|
|
|
CV *cv = newXS("Func::Util::is_blessed", xs_is_blessed, __FILE__); |
|
6701
|
53
|
|
|
|
|
|
cv_set_call_checker(cv, is_blessed_call_checker, (SV*)cv); |
|
6702
|
|
|
|
|
|
|
} |
|
6703
|
|
|
|
|
|
|
{ |
|
6704
|
53
|
|
|
|
|
|
CV *cv = newXS("Func::Util::is_scalar_ref", xs_is_scalar_ref, __FILE__); |
|
6705
|
53
|
|
|
|
|
|
cv_set_call_checker(cv, is_scalar_ref_call_checker, (SV*)cv); |
|
6706
|
|
|
|
|
|
|
} |
|
6707
|
|
|
|
|
|
|
{ |
|
6708
|
53
|
|
|
|
|
|
CV *cv = newXS("Func::Util::is_regex", xs_is_regex, __FILE__); |
|
6709
|
53
|
|
|
|
|
|
cv_set_call_checker(cv, is_regex_call_checker, (SV*)cv); |
|
6710
|
|
|
|
|
|
|
} |
|
6711
|
|
|
|
|
|
|
{ |
|
6712
|
53
|
|
|
|
|
|
CV *cv = newXS("Func::Util::is_glob", xs_is_glob, __FILE__); |
|
6713
|
53
|
|
|
|
|
|
cv_set_call_checker(cv, is_glob_call_checker, (SV*)cv); |
|
6714
|
|
|
|
|
|
|
} |
|
6715
|
|
|
|
|
|
|
|
|
6716
|
|
|
|
|
|
|
/* Numeric predicates with call checkers */ |
|
6717
|
|
|
|
|
|
|
{ |
|
6718
|
53
|
|
|
|
|
|
CV *cv = newXS("Func::Util::is_positive", xs_is_positive, __FILE__); |
|
6719
|
53
|
|
|
|
|
|
cv_set_call_checker(cv, is_positive_call_checker, (SV*)cv); |
|
6720
|
|
|
|
|
|
|
} |
|
6721
|
|
|
|
|
|
|
{ |
|
6722
|
53
|
|
|
|
|
|
CV *cv = newXS("Func::Util::is_negative", xs_is_negative, __FILE__); |
|
6723
|
53
|
|
|
|
|
|
cv_set_call_checker(cv, is_negative_call_checker, (SV*)cv); |
|
6724
|
|
|
|
|
|
|
} |
|
6725
|
|
|
|
|
|
|
{ |
|
6726
|
53
|
|
|
|
|
|
CV *cv = newXS("Func::Util::is_zero", xs_is_zero, __FILE__); |
|
6727
|
53
|
|
|
|
|
|
cv_set_call_checker(cv, is_zero_call_checker, (SV*)cv); |
|
6728
|
|
|
|
|
|
|
} |
|
6729
|
|
|
|
|
|
|
|
|
6730
|
|
|
|
|
|
|
/* Numeric utility ops with call checkers */ |
|
6731
|
|
|
|
|
|
|
{ |
|
6732
|
53
|
|
|
|
|
|
CV *cv = newXS("Func::Util::is_even", xs_is_even, __FILE__); |
|
6733
|
53
|
|
|
|
|
|
cv_set_call_checker(cv, is_even_call_checker, (SV*)cv); |
|
6734
|
|
|
|
|
|
|
} |
|
6735
|
|
|
|
|
|
|
{ |
|
6736
|
53
|
|
|
|
|
|
CV *cv = newXS("Func::Util::is_odd", xs_is_odd, __FILE__); |
|
6737
|
53
|
|
|
|
|
|
cv_set_call_checker(cv, is_odd_call_checker, (SV*)cv); |
|
6738
|
|
|
|
|
|
|
} |
|
6739
|
|
|
|
|
|
|
{ |
|
6740
|
53
|
|
|
|
|
|
CV *cv = newXS("Func::Util::is_between", xs_is_between, __FILE__); |
|
6741
|
53
|
|
|
|
|
|
cv_set_call_checker(cv, is_between_call_checker, (SV*)cv); |
|
6742
|
|
|
|
|
|
|
} |
|
6743
|
|
|
|
|
|
|
|
|
6744
|
|
|
|
|
|
|
/* Collection ops with call checkers */ |
|
6745
|
|
|
|
|
|
|
{ |
|
6746
|
53
|
|
|
|
|
|
CV *cv = newXS("Func::Util::is_empty_array", xs_is_empty_array, __FILE__); |
|
6747
|
53
|
|
|
|
|
|
cv_set_call_checker(cv, is_empty_array_call_checker, (SV*)cv); |
|
6748
|
|
|
|
|
|
|
} |
|
6749
|
|
|
|
|
|
|
{ |
|
6750
|
53
|
|
|
|
|
|
CV *cv = newXS("Func::Util::is_empty_hash", xs_is_empty_hash, __FILE__); |
|
6751
|
53
|
|
|
|
|
|
cv_set_call_checker(cv, is_empty_hash_call_checker, (SV*)cv); |
|
6752
|
|
|
|
|
|
|
} |
|
6753
|
|
|
|
|
|
|
{ |
|
6754
|
53
|
|
|
|
|
|
CV *cv = newXS("Func::Util::array_len", xs_array_len, __FILE__); |
|
6755
|
53
|
|
|
|
|
|
cv_set_call_checker(cv, array_len_call_checker, (SV*)cv); |
|
6756
|
|
|
|
|
|
|
} |
|
6757
|
|
|
|
|
|
|
{ |
|
6758
|
53
|
|
|
|
|
|
CV *cv = newXS("Func::Util::hash_size", xs_hash_size, __FILE__); |
|
6759
|
53
|
|
|
|
|
|
cv_set_call_checker(cv, hash_size_call_checker, (SV*)cv); |
|
6760
|
|
|
|
|
|
|
} |
|
6761
|
|
|
|
|
|
|
{ |
|
6762
|
53
|
|
|
|
|
|
CV *cv = newXS("Func::Util::array_first", xs_array_first, __FILE__); |
|
6763
|
53
|
|
|
|
|
|
cv_set_call_checker(cv, array_first_call_checker, (SV*)cv); |
|
6764
|
|
|
|
|
|
|
} |
|
6765
|
|
|
|
|
|
|
{ |
|
6766
|
53
|
|
|
|
|
|
CV *cv = newXS("Func::Util::array_last", xs_array_last, __FILE__); |
|
6767
|
53
|
|
|
|
|
|
cv_set_call_checker(cv, array_last_call_checker, (SV*)cv); |
|
6768
|
|
|
|
|
|
|
} |
|
6769
|
|
|
|
|
|
|
|
|
6770
|
|
|
|
|
|
|
/* String manipulation ops with call checkers */ |
|
6771
|
|
|
|
|
|
|
{ |
|
6772
|
53
|
|
|
|
|
|
CV *cv = newXS("Func::Util::trim", xs_trim, __FILE__); |
|
6773
|
53
|
|
|
|
|
|
cv_set_call_checker(cv, trim_call_checker, (SV*)cv); |
|
6774
|
|
|
|
|
|
|
} |
|
6775
|
|
|
|
|
|
|
{ |
|
6776
|
53
|
|
|
|
|
|
CV *cv = newXS("Func::Util::ltrim", xs_ltrim, __FILE__); |
|
6777
|
53
|
|
|
|
|
|
cv_set_call_checker(cv, ltrim_call_checker, (SV*)cv); |
|
6778
|
|
|
|
|
|
|
} |
|
6779
|
|
|
|
|
|
|
{ |
|
6780
|
53
|
|
|
|
|
|
CV *cv = newXS("Func::Util::rtrim", xs_rtrim, __FILE__); |
|
6781
|
53
|
|
|
|
|
|
cv_set_call_checker(cv, rtrim_call_checker, (SV*)cv); |
|
6782
|
|
|
|
|
|
|
} |
|
6783
|
|
|
|
|
|
|
|
|
6784
|
|
|
|
|
|
|
/* Conditional ops with call checkers */ |
|
6785
|
|
|
|
|
|
|
{ |
|
6786
|
53
|
|
|
|
|
|
CV *cv = newXS("Func::Util::maybe", xs_maybe, __FILE__); |
|
6787
|
53
|
|
|
|
|
|
cv_set_call_checker(cv, maybe_call_checker, (SV*)cv); |
|
6788
|
|
|
|
|
|
|
} |
|
6789
|
|
|
|
|
|
|
|
|
6790
|
|
|
|
|
|
|
/* Numeric ops with call checkers */ |
|
6791
|
|
|
|
|
|
|
{ |
|
6792
|
53
|
|
|
|
|
|
CV *cv = newXS("Func::Util::sign", xs_sign, __FILE__); |
|
6793
|
53
|
|
|
|
|
|
cv_set_call_checker(cv, sign_call_checker, (SV*)cv); |
|
6794
|
|
|
|
|
|
|
} |
|
6795
|
|
|
|
|
|
|
{ |
|
6796
|
53
|
|
|
|
|
|
CV *cv = newXS("Func::Util::min2", xs_min2, __FILE__); |
|
6797
|
53
|
|
|
|
|
|
cv_set_call_checker(cv, min2_call_checker, (SV*)cv); |
|
6798
|
|
|
|
|
|
|
} |
|
6799
|
|
|
|
|
|
|
{ |
|
6800
|
53
|
|
|
|
|
|
CV *cv = newXS("Func::Util::max2", xs_max2, __FILE__); |
|
6801
|
53
|
|
|
|
|
|
cv_set_call_checker(cv, max2_call_checker, (SV*)cv); |
|
6802
|
|
|
|
|
|
|
} |
|
6803
|
|
|
|
|
|
|
|
|
6804
|
|
|
|
|
|
|
/* Register cleanup for global destruction */ |
|
6805
|
53
|
|
|
|
|
|
Perl_call_atexit(aTHX_ cleanup_callback_registry, NULL); |
|
6806
|
|
|
|
|
|
|
|
|
6807
|
53
|
|
|
|
|
|
Perl_xs_boot_epilog(aTHX_ ax); |
|
6808
|
53
|
|
|
|
|
|
} |