1 /* $Id: /mirror/Senna-Perl/lib/Senna.xs 6103 2007-03-16T16:45:50.914799Z daisuke $
2 *
3 * Copyright (c) 2005-2007 Daisuke Maki <dmaki@cpan.org>
4 * All rights reserved.
5 */
6
7 /* TODO
8 *
9 * - Add more tests
10 *
11 */
12
13 #include "EXTERN.h"
14 #include "perl.h"
15 #include "XSUB.h"
16 #define NEED_newCONSTSUB
17 #define NEED_newRV_noinc
18 #include "ppport.h"
19
20 #include <senna/senna.h>
21
22 /* This is defined in senna's sym.h, so don't forget to change accordingly */
23 #ifndef SEN_SYM_MAX_KEY_LENGTH
24 #define SEN_SYM_MAX_KEY_LENGTH 0xffff
25 #endif
26
27 typedef uint8_t byte;
28
29 #ifndef SEN_SYM_MAX_SEGMENT
30 #define SEN_SYM_MAX_SEGMENT 0x400
31 #endif
32
33 typedef enum {
34 sen_io_auto,
35 sen_io_manual
36 } sen_io_mode;
37
38 typedef struct {
39 void *map;
40 uint32_t nref;
41 uint32_t count;
42 #if defined(WIN32) && defined(WIN32_FMO_EACH)
43 HANDLE fmo;
44 #endif /* defined(WIN32) && defined(WIN32_FMO_EACH) */
45 } sen_io_mapinfo;
46
47 typedef struct _sen_io sen_io;
48
49 struct _sen_io {
50 char path[PATH_MAX];
51 struct _sen_io_header *header;
52 byte *user_header;
53 sen_io_mapinfo *maps;
54 uint32_t *nrefs;
55 uint32_t base;
56 uint32_t base_seg;
57 sen_io_mode mode;
58 uint32_t cache_size;
59 struct _sen_io_fileinfo *fis;
60 uint32_t nmaps;
61 uint32_t count;
62 uint8_t flags;
63 };
64
65 struct _sen_sym {
66 uint8_t v08p;
67 sen_io *io;
68 struct sen_sym_header *header;
69 uint32_t flags;
70 sen_encoding encoding;
71 uint32_t key_size;
72 uint32_t nref;
73 uint32_t *lock;
74 void *keyaddrs[SEN_SYM_MAX_SEGMENT];
75 void *pataddrs[SEN_SYM_MAX_SEGMENT];
76 void *sisaddrs[SEN_SYM_MAX_SEGMENT];
77 };
78
79 #define SEN_SET_MAX_CHUNK 22
80
81 typedef pthread_mutex_t sen_mutex;
82
83 typedef struct _sen_array sen_array;
84 // #define SEN_ARRAY_W 2
85 #define SEN_ARRAY_W 0
86 #define SEN_ARRAY_R(i) (1<<((i)<<SEN_ARRAY_W))
87 #define SEN_ARRAY_S (SEN_ARRAY_R(1)-1)
88 #define SEN_ARRAY_N (32>>SEN_ARRAY_W)
89
90 struct _sen_array {
91 sen_ctx *ctx;
92 sen_id max;
93 uint16_t element_size;
94 uint16_t flags;
95 sen_mutex lock;
96 void *elements[SEN_ARRAY_N];
97 };
98
99 struct _sen_set {
100 uint32_t key_size;
101 uint32_t value_size;
102 uint32_t entry_size;
103 uint32_t max_offset;
104 int32_t n_entries;
105 uint32_t n_garbages;
106 // uint32_t curr_entry;
107 // uint32_t curr_chunk;
108 unsigned int max_n_subrecs;
109 unsigned int record_size;
110 unsigned int subrec_size;
111 sen_rec_unit record_unit;
112 sen_rec_unit subrec_unit;
113 uint8_t arrayp;
114 sen_set_eh garbages;
115 sen_set_eh *index;
116 sen_ctx *ctx;
117 sen_sym *keys;
118 sen_recordh *curr_rec;
119 sen_set_cursor *cursor;
120 int limit;
121 sen_recordh *sorted;
122 void *userdata;
123 sen_id subrec_id;
124 // byte *chunks[SEN_SET_MAX_CHUNK + 1];
125
126 sen_array a;
127 };
128
129 /* This is defined in senna's snip.h. */
130 #ifndef MAX_SNIP_RESULT_COUNT
131 #define MAX_SNIP_RESULT_COUNT 8U
132 #endif
133
134 /* XXX -
135 * I can never get this straight.
136 * Senna's key_size element, even if you set it to 0 at creation time,
137 * returns the actual length of the key.
138 */
139 #define SEN_INT_KEY sizeof(int)
140 #define SEN_VARCHAR_KEY 0
141 #define SEN_MAX_KEY_SIZE 8192
142 #define SEN_MAX_PATH_SIZE 1024
143
144 #define XS_STATE(type, x) \
145 INT2PTR(type, SvROK(x) ? SvIV(SvRV(x)) : SvIV(x))
146
147 #define XS_STRUCT2OBJ(sv, class, obj) \
148 sv = newSViv(PTR2IV(obj)); \
149 sv = newRV_noinc(sv); \
150 sv_bless(sv, gv_stashpv(class, 1)); \
151 SvREADONLY_on(sv);
152
153 typedef struct sen_perl_snip {
154 sen_snip *snip; /* the snip object */
155 char **open_tags;
156 size_t open_tags_size;
157 char **close_tags;
158 size_t close_tags_size;
159 } sen_perl_snip;
160
161 SV *
sen_rc2obj(sen_rc rc)162 sen_rc2obj(sen_rc rc)
163 {
164 SV *sv;
165
166 if (GIMME_V == G_VOID) {
167 sv = &PL_sv_undef;
168 } else {
169 dSP;
170 ENTER;
171 SAVETMPS;
172 PUSHMARK(SP);
173 XPUSHs(sv_2mortal(newSVpv("Senna::RC", 9)));
174 XPUSHs(sv_2mortal(newSViv(rc)));
175 PUTBACK;
176 if (call_method("Senna::RC::new", G_SCALAR) <= 0) {
177 croak ("Senna::RC::new did not return object ");
178 }
179 SPAGAIN;
180 sv = POPs;
181
182 if (! sv_isobject(sv) || ! sv_isa(sv, "Senna::RC")) {
183 croak ("Senna::RC::new did not return a proper object");
184 }
185 sv = newSVsv(sv);
186
187 FREETMPS;
188 LEAVE;
189 }
190
191 return sv;
192 }
193
194 void
senna_bootstrap()195 senna_bootstrap()
196 {
197 HV *stash;
198 sen_rc rc;
199
200 rc = sen_init();
201 if (rc != sen_success)
202 croak("Failed to call sen_init(). sen_init() returned %d", rc);
203
204 stash = gv_stashpv("Senna::Constants", 1);
205
206 /* miscellany */
207 newCONSTSUB(stash, "LIBSENNA_VERSION", newSVpvf("%d.%d.%d", SENNA_MAJOR_VERSION, SENNA_MINOR_VERSION, SENNA_MICRO_VERSION));
208
209 /* key_size */
210 newCONSTSUB(stash, "SEN_VARCHAR_KEY", newSViv(SEN_VARCHAR_KEY));
211 newCONSTSUB(stash, "SEN_INT_KEY", newSViv(SEN_INT_KEY));
212
213 /* sen_index_create flags */
214 newCONSTSUB(stash, "SEN_INDEX_NORMALIZE", newSViv(SEN_INDEX_NORMALIZE));
215 newCONSTSUB(stash, "SEN_INDEX_SPLIT_ALPHA", newSViv(SEN_INDEX_SPLIT_ALPHA));
216 newCONSTSUB(stash, "SEN_INDEX_SPLIT_DIGIT", newSViv(SEN_INDEX_SPLIT_DIGIT));
217 newCONSTSUB(stash, "SEN_INDEX_SPLIT_SYMBOL", newSViv(SEN_INDEX_SPLIT_SYMBOL));
218 newCONSTSUB(stash, "SEN_INDEX_MORPH_ANALYSE", newSViv(SEN_INDEX_MORPH_ANALYSE));
219 newCONSTSUB(stash, "SEN_INDEX_NGRAM", newSViv(SEN_INDEX_NGRAM));
220 newCONSTSUB(stash, "SEN_INDEX_DELIMITED", newSViv(SEN_INDEX_DELIMITED));
221 newCONSTSUB(stash, "SEN_INDEX_ENABLE_SUFFIX_SEARCH", newSViv(SEN_INDEX_ENABLE_SUFFIX_SEARCH));
222 newCONSTSUB(stash, "SEN_INDEX_DISABLE_SUFFIX_SEARCH", newSViv(SEN_INDEX_DISABLE_SUFFIX_SEARCH));
223 newCONSTSUB(stash, "SEN_INDEX_WITH_VACUUM", newSViv(SEN_INDEX_WITH_VACUUM));
224
225 /* sen_query */
226 newCONSTSUB(stash, "SEN_QUERY_AND", newSVpvf("%c", SEN_QUERY_AND));
227 newCONSTSUB(stash, "SEN_QUERY_BUT", newSVpvf("%c", SEN_QUERY_BUT));
228 newCONSTSUB(stash, "SEN_QUERY_ADJ_INC", newSVpvf("%c", SEN_QUERY_ADJ_INC));
229 newCONSTSUB(stash, "SEN_QUERY_ADJ_DEC", newSVpvf("%c", SEN_QUERY_ADJ_DEC));
230 newCONSTSUB(stash, "SEN_QUERY_ADJ_NEG", newSVpvf("%c", SEN_QUERY_ADJ_NEG));
231 newCONSTSUB(stash, "SEN_QUERY_PREFIX", newSVpvf("%c", SEN_QUERY_PREFIX));
232 newCONSTSUB(stash, "SEN_QUERY_PARENL", newSVpvf("%c", SEN_QUERY_PARENL));
233 newCONSTSUB(stash, "SEN_QUERY_PARENR", newSVpvf("%c", SEN_QUERY_PARENR));
234 newCONSTSUB(stash, "SEN_QUERY_QUOTEL", newSVpvf("%c", SEN_QUERY_QUOTEL));
235 newCONSTSUB(stash, "SEN_QUERY_QUOTER", newSVpvf("%c", SEN_QUERY_QUOTER));
236
237 /* sen_rc */
238 newCONSTSUB(stash, "SEN_RC_SUCCESS", newSViv(sen_success));
239 newCONSTSUB(stash, "SEN_RC_MEMORY_EXHAUSTED", newSViv(sen_memory_exhausted));
240 newCONSTSUB(stash, "SEN_RC_INVALID_FORMAT", newSViv(sen_invalid_format));
241 newCONSTSUB(stash, "SEN_RC_FILE_ERR", newSViv(sen_file_operation_error));
242 newCONSTSUB(stash, "SEN_RC_INVALID_ARG", newSViv(sen_invalid_argument));
243 newCONSTSUB(stash, "SEN_RC_OTHER", newSViv(sen_other_error));
244
245 /* sen_encoding */
246 newCONSTSUB(stash, "SEN_ENC_DEFAULT", newSViv(sen_enc_default));
247 newCONSTSUB(stash, "SEN_ENC_NONE", newSViv(sen_enc_none));
248 newCONSTSUB(stash, "SEN_ENC_EUCJP", newSViv(sen_enc_euc_jp));
249 newCONSTSUB(stash, "SEN_ENC_UTF8", newSViv(sen_enc_utf8));
250 newCONSTSUB(stash, "SEN_ENC_SJIS", newSViv(sen_enc_sjis));
251
252 /* sen_rec_unit */
253 newCONSTSUB(stash, "SEN_REC_DOCUMENT", newSViv(sen_rec_document));
254 newCONSTSUB(stash, "SEN_REC_SECTION", newSViv(sen_rec_section));
255 newCONSTSUB(stash, "SEN_REC_POSITION", newSViv(sen_rec_position));
256 newCONSTSUB(stash, "SEN_REC_USERDEF", newSViv(sen_rec_userdef));
257 newCONSTSUB(stash, "SEN_REC_NONE", newSViv(sen_rec_none));
258
259 /* sen_sel_operator */
260 newCONSTSUB(stash, "SEN_SELOP_OR", newSViv(sen_sel_or));
261 newCONSTSUB(stash, "SEN_SELOP_AND", newSViv(sen_sel_and));
262 newCONSTSUB(stash, "SEN_SELOP_BUT", newSViv(sen_sel_but));
263 newCONSTSUB(stash, "SEN_SELOP_ADJUST", newSViv(sen_sel_adjust));
264
265 /* sen_sel_mode */
266 newCONSTSUB(stash, "SEN_SELMODE_EXACT", newSViv(sen_sel_exact));
267 newCONSTSUB(stash, "SEN_SELMODE_PARTIAL", newSViv(sen_sel_partial));
268 newCONSTSUB(stash, "SEN_SELMODE_UNSPLIT", newSViv(sen_sel_unsplit));
269 newCONSTSUB(stash, "SEN_SELMODE_NEAR", newSViv(sen_sel_near));
270 newCONSTSUB(stash, "SEN_SELMODE_SIMILAR", newSViv(sen_sel_similar));
271 newCONSTSUB(stash, "SEN_SELMODE_TERM_EXTRACT", newSViv(sen_sel_term_extract));
272
273 /* sen_sort_mode */
274 newCONSTSUB(stash, "SEN_SORT_DESC", newSViv(sen_sort_descending));
275 newCONSTSUB(stash, "SEN_SORT_ASC", newSViv(sen_sort_ascending));
276
277 /* sen_log_level */
278 newCONSTSUB(stash, "SEN_LOG_NONE", newSViv(sen_log_none));
279 newCONSTSUB(stash, "SEN_LOG_EMERG", newSViv(sen_log_emerg));
280 newCONSTSUB(stash, "SEN_LOG_ALERT", newSViv(sen_log_alert));
281 newCONSTSUB(stash, "SEN_LOG_CRIT", newSViv(sen_log_crit));
282 newCONSTSUB(stash, "SEN_LOG_ERROR", newSViv(sen_log_error));
283 newCONSTSUB(stash, "SEN_LOG_WARNING", newSViv(sen_log_warning));
284 newCONSTSUB(stash, "SEN_LOG_NOTICE", newSViv(sen_log_notice));
285 newCONSTSUB(stash, "SEN_LOG_INFO", newSViv(sen_log_info));
286 newCONSTSUB(stash, "SEN_LOG_DEBUG", newSViv(sen_log_debug));
287 newCONSTSUB(stash, "SEN_LOG_DUMP", newSViv(sen_log_dump));
288 }
289
290 static void
sv2senna_key(sen_index * index,SV * key,void ** ret_key)291 sv2senna_key(sen_index *index, SV *key, void **ret_key)
292 {
293 long *int_key;
294 long int_tmp;
295 char *char_key;
296 STRLEN len;
297 int key_size;
298
299 sen_index_info(index, &key_size, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
300
301 if (key_size == SEN_INT_KEY) {
302 if (! SvIOK(key)) {
303 croak("index is created with integer keys, but was passed a non-integer key");
304 }
305
306 int_tmp = SvIV(key);
307 *ret_key = &int_tmp;
308 } else {
309 char_key = SvPV(key, len);
310 if (len >= SEN_MAX_KEY_SIZE) {
311 croak("key length must be less than SENNA_MAX_KEY_SIZE bytes");
312 }
313 *ret_key = (void *) char_key;
314 }
315 }
316
317 struct _sen_set;
318 /* typedef struct _sen_set sen_set; */
319
320 static int
sen_sort_optarg_cb(sen_records * r1,const sen_recordh * a,sen_records * r2,const sen_recordh * b,void * args)321 sen_sort_optarg_cb(sen_records *r1, const sen_recordh *a,
322 sen_records *r2, const sen_recordh *b, void *args)
323 {
324 dSP;
325 int key_size;
326 void **compar_args = (void *) args;
327 int i, section, pos, score, n_subrecs;
328 SV *sr1_obj;
329 SV *sr2_obj;
330 AV *cb_args;
331 SV *sv_a;
332 SV *sv_b;
333 SV *sv_key;
334 SV *sv;
335 SV **svr;
336
337 cb_args = (AV *) compar_args[1];
338
339 /* sen_rc sen_sym_info(sen_sym *sym, int *key_size, unsigned *flags,
340 sen_encoding *encoding, unsigned *nrecords, unsigned *file_size); */
341 /* typedef struct _sen_sym sen_sym; */
342 /* typedef sen_set sen_records; */
343 /* sen_set.sen_sym* keys */
344 sen_sym_info(r1->keys, &key_size, NULL, NULL, NULL, NULL);
345 if (key_size == SEN_VARCHAR_KEY) {
346 char key[SEN_MAX_KEY_SIZE];
347 sen_record_info(r1, a, key, SEN_MAX_KEY_SIZE, NULL,
348 §ion, &pos, &score, &n_subrecs);
349
350 ENTER;
351 SAVETMPS;
352 PUSHMARK(SP);
353 XPUSHs(sv_2mortal(newSVpv("Senna::Record", 13)));
354 XPUSHs(sv_2mortal(newSVpv("key", 3)));
355 XPUSHs(sv_2mortal(newSVpv(key, 0)));
356 XPUSHs(sv_2mortal(newSVpv("score", 5)));
357 XPUSHs(sv_2mortal(newSViv(score)));
358 XPUSHs(sv_2mortal(newSVpv("pos", 3)));
359 XPUSHs(sv_2mortal(newSViv(pos)));
360 XPUSHs(sv_2mortal(newSVpv("section", 7)));
361 XPUSHs(sv_2mortal(newSViv(section)));
362 XPUSHs(sv_2mortal(newSVpv("n_subrecs", 8)));
363 XPUSHs(sv_2mortal(newSViv(n_subrecs)));
364
365 PUTBACK;
366 if (! call_method("Senna::Record::new", G_SCALAR) <= 0) {
367 croak ("Senna::Record::new did not return object");
368 }
369 SPAGAIN;
370 sv_a = POPs;
371 FREETMPS;
372 LEAVE;
373
374 if (! sv_isobject(sv) || ! sv_isa(sv, "Senna::Record")) {
375 croak ("Senna::Record::new did not return a proper object");
376 }
377
378
379 sen_record_info(r2, b, key, SEN_MAX_KEY_SIZE, NULL,
380 §ion, &pos, &score, &n_subrecs);
381
382 ENTER;
383 SAVETMPS;
384 PUSHMARK(SP);
385 XPUSHs(sv_2mortal(newSVpv("Senna::Record", 13)));
386 XPUSHs(sv_2mortal(newSVpv("key", 3)));
387 XPUSHs(sv_2mortal(newSVpv(key, 0)));
388 XPUSHs(sv_2mortal(newSVpv("score", 5)));
389 XPUSHs(sv_2mortal(newSViv(score)));
390 XPUSHs(sv_2mortal(newSVpv("pos", 3)));
391 XPUSHs(sv_2mortal(newSViv(pos)));
392 XPUSHs(sv_2mortal(newSVpv("section", 7)));
393 XPUSHs(sv_2mortal(newSViv(section)));
394 XPUSHs(sv_2mortal(newSVpv("n_subrecs", 8)));
395 XPUSHs(sv_2mortal(newSViv(n_subrecs)));
396
397 PUTBACK;
398 if (! call_method("Senna::Record::new", G_SCALAR) <= 0) {
399 croak ("Senna::Record::new did not return object");
400 }
401 SPAGAIN;
402 sv_b = POPs;
403 FREETMPS;
404 LEAVE;
405
406 if (! sv_isobject(sv) || ! sv_isa(sv, "Senna::Record")) {
407 croak ("Senna::Record::new did not return a proper object");
408 }
409 } else {
410 long key;
411 sen_record_info(r1, a, (void *) &key, SEN_INT_KEY, NULL,
412 §ion, &pos, &score, &n_subrecs);
413 ENTER;
414 SAVETMPS;
415 PUSHMARK(SP);
416 XPUSHs(sv_2mortal(newSVpv("Senna::Record", 13)));
417 XPUSHs(sv_2mortal(newSVpv("key", 3)));
418 XPUSHs(sv_2mortal(newSViv(key)));
419 XPUSHs(sv_2mortal(newSVpv("score", 5)));
420 XPUSHs(sv_2mortal(newSViv(score)));
421 XPUSHs(sv_2mortal(newSVpv("pos", 3)));
422 XPUSHs(sv_2mortal(newSViv(pos)));
423 XPUSHs(sv_2mortal(newSVpv("section", 7)));
424 XPUSHs(sv_2mortal(newSViv(section)));
425 XPUSHs(sv_2mortal(newSVpv("n_subrecs", 8)));
426 XPUSHs(sv_2mortal(newSViv(n_subrecs)));
427
428 PUTBACK;
429 if (! call_method("Senna::Record::new", G_SCALAR) <= 0) {
430 croak ("Senna::Record::new did not return object");
431 }
432 SPAGAIN;
433 sv_a = POPs;
434 FREETMPS;
435 LEAVE;
436
437 if (! sv_isobject(sv) || ! sv_isa(sv, "Senna::Record")) {
438 croak ("Senna::Record::new did not return a proper object");
439 }
440
441 sen_record_info(r2, b, (void *) &key, SEN_INT_KEY, NULL,
442 §ion, &pos, &score, &n_subrecs);
443 ENTER;
444 SAVETMPS;
445 PUSHMARK(SP);
446 XPUSHs(sv_2mortal(newSVpv("Senna::Record", 13)));
447 XPUSHs(sv_2mortal(newSVpv("key", 3)));
448 XPUSHs(sv_2mortal(newSViv(key)));
449 XPUSHs(sv_2mortal(newSVpv("score", 5)));
450 XPUSHs(sv_2mortal(newSViv(score)));
451 XPUSHs(sv_2mortal(newSVpv("pos", 3)));
452 XPUSHs(sv_2mortal(newSViv(pos)));
453 XPUSHs(sv_2mortal(newSVpv("section", 7)));
454 XPUSHs(sv_2mortal(newSViv(section)));
455 XPUSHs(sv_2mortal(newSVpv("n_subrecs", 8)));
456 XPUSHs(sv_2mortal(newSViv(n_subrecs)));
457
458 PUTBACK;
459 if (! call_method("Senna::Record::new", G_SCALAR) <= 0) {
460 croak ("Senna::Record::new did not return object");
461 }
462 SPAGAIN;
463 sv_b = POPs;
464 FREETMPS;
465 LEAVE;
466
467 if (! sv_isobject(sv) || ! sv_isa(sv, "Senna::Record")) {
468 croak ("Senna::Record::new did not return a proper object");
469 }
470
471 }
472
473 ENTER;
474 SAVETMPS;
475 PUSHMARK(SP);
476
477 sr1_obj = XS_STRUCT2OBJ(sv, "Senna::Results", r1);
478 sr2_obj = XS_STRUCT2OBJ(sv, "Senna::Results", r2);
479 XPUSHs(sr1_obj);
480 XPUSHs(sv_a);
481 XPUSHs(sr2_obj);
482 XPUSHs(sv_b);
483 for(i = 0; i <= av_len(cb_args); i++) {
484 svr = av_fetch(cb_args, i, 0);
485 if (svr == NULL)
486 XPUSHs(&PL_sv_undef);
487 else
488 XPUSHs(sv_2mortal(newSVsv(*svr)));
489 }
490
491 PUTBACK;
492 if (! call_sv((SV *) compar_args[0], G_EVAL|G_SCALAR) <= 0) {
493 return 0;
494 }
495
496 SPAGAIN;
497 sv = POPs;
498 FREETMPS;
499 LEAVE;
500
501 return SvTRUE(sv) ? 1 : 0;
502 }
503
504 static int
sen_select_optarg_cb(sen_records * r,const void * key,int pid,void * args)505 sen_select_optarg_cb(sen_records *r, const void *key, int pid, void *args)
506 {
507 SV *sr_obj;
508 SV *cb_pid;
509 SV *cb_key;
510 AV *cb_args;
511 SV *sv;
512 SV **svr;
513 int key_size;
514 int i;
515 void **func_args = (void **) args;
516
517 dSP;
518
519 sr_obj = XS_STRUCT2OBJ(sv, "Senna::Results", r);
520 cb_pid = newSViv(pid);
521 cb_args = (AV *) func_args[1];
522
523 sen_records_rewind(r);
524 sen_record_info(r, sen_records_curr_rec(r), NULL, 0, &key_size,
525 NULL, NULL, NULL, NULL);
526 if (key_size == SEN_INT_KEY) {
527 cb_key = newSViv(*((long *) key));
528 } else {
529 cb_key = newSVpv((char *) key, 0);
530 }
531
532 ENTER;
533 SAVETMPS;
534 PUSHMARK(SP);
535
536 XPUSHs(sr_obj);
537 XPUSHs(cb_key);
538 XPUSHs(cb_pid);
539 for(i = 0; i <= av_len(cb_args); i++) {
540 svr = av_fetch(cb_args, i, 0);
541 if (svr == NULL)
542 XPUSHs(&PL_sv_undef);
543 else
544 XPUSHs(sv_2mortal(newSVsv(*svr)));
545 }
546
547 PUTBACK;
548 if (! call_sv((SV *) func_args[0], G_EVAL|G_SCALAR) <= 0) {
549 return 0;
550 }
551
552 SPAGAIN;
553 sv = POPs;
554 FREETMPS;
555 LEAVE;
556
557 return SvTRUE(sv) ? 1 : 0;
558 }
559
560 MODULE = Senna PACKAGE = Senna
561
562 BOOT:
563 senna_bootstrap();
564
565 MODULE = Senna PACKAGE = Senna::Index PREFIX=SIndex_
566
567 SV *
568 SIndex_xs_create(class, path, key_size = SEN_VARCHAR_KEY, flags = 0, initial_n_segments = 0, encoding = sen_enc_default)
569 char *class;
570 char *path;
571 int key_size;
572 int flags;
573 int initial_n_segments;
574 sen_encoding encoding;
575 PREINIT:
576 sen_index *index;
577 SV *sv;
578 CODE:
579 index = sen_index_create(path, key_size, flags, initial_n_segments, encoding);
580 if (index == NULL)
581 croak ("Failed to create senna index");
582
583 XS_STRUCT2OBJ(sv, class, index);
584 RETVAL = sv;
585 OUTPUT:
586 RETVAL
587
588 SV *
589 SIndex_xs_open(class, path)
590 char *class;
591 char *path;
592 PREINIT:
593 sen_index *index;
594 int key_size;
595 SV *sv;
596 CODE:
597 index = sen_index_open(path);
598 if (index == NULL)
599 croak ("Failed to open senna index");
600
601 /* Make sure that index does not have some unsupported key_size
602 */
603 sen_index_info(index, &key_size, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
604 if (key_size != SEN_VARCHAR_KEY && key_size != SEN_INT_KEY)
605 croak("Senna::Index does not support key_size other than 0 or 4");
606
607 XS_STRUCT2OBJ(sv, class, index);
608 SvREADONLY_on(sv);
609
610 RETVAL = sv;
611 OUTPUT:
612 RETVAL
613
614 void
615 SIndex_info(self)
616 SV *self;
617 PREINIT:
618 sen_index *index;
619 int key_size;
620 int flags;
621 int initial_n_segments;
622 sen_encoding encoding;
623 unsigned int nrecords_keys;
624 unsigned int file_size_keys;
625 unsigned int nrecords_lexicon;
626 unsigned int file_size_lexicon;
627 unsigned int inv_seg_size;
628 unsigned int inv_chunk_size;
629 sen_rc rc;
630 PPCODE:
631 index = XS_STATE(sen_index *, self);
632 /*
633 sen_rc sen_index_info(sen_index *i, int *key_size, int *flags,
634 int *initial_n_segments, sen_encoding *encoding,
635 unsigned *nrecords_keys, unsigned *file_size_keys,
636 unsigned *nrecords_lexicon, unsigned *file_size_lexicon,
637 unsigned long long *inv_seg_size,
638 unsigned long long *inv_chunk_size);
639 */
640 rc = sen_index_info(index,
641 &key_size, &flags, &initial_n_segments, &encoding,
642 &nrecords_keys, &file_size_keys, &nrecords_lexicon,
643 &file_size_lexicon, (unsigned long long*)&inv_seg_size, (unsigned long long*)&inv_chunk_size
644 );
645
646 if (rc != sen_success)
647 croak("Failed to call sen_index_info: %d", rc);
648
649 EXTEND(SP, 10);
650 mPUSHi(key_size);
651 mPUSHi(flags);
652 mPUSHi(initial_n_segments);
653 mPUSHi(encoding);
654 mPUSHi(nrecords_keys);
655 mPUSHi(file_size_keys);
656 mPUSHi(nrecords_lexicon);
657 mPUSHi(file_size_lexicon);
658 mPUSHi(inv_seg_size);
659 mPUSHi(inv_chunk_size);
660
661 SV *
662 SIndex_path(self)
663 SV *self;
664 PREINIT:
665 sen_index *index;
666 char path[SEN_MAX_PATH_SIZE];
667 CODE:
668 index = XS_STATE(sen_index *, self);
669 sen_index_path(index, path, SEN_MAX_PATH_SIZE);
670 RETVAL = newSVpv(path, 0);
671 OUTPUT:
672 RETVAL
673
674 SV *
675 SIndex_close(obj)
676 SV *obj;
677 PREINIT:
678 sen_index *index;
679 CODE:
680 index = XS_STATE(sen_index *, obj);
681 RETVAL = sen_rc2obj(sen_index_close(index));
682 OUTPUT:
683 RETVAL
684
685 SV *
686 SIndex_remove(self)
687 SV *self;
688 PREINIT:
689 sen_index *index;
690 char path[SEN_MAX_PATH_SIZE];
691 CODE:
692 index = XS_STATE(sen_index *, self);
693 if (! sen_index_path(index, path, SEN_MAX_PATH_SIZE))
694 croak("sen_index_path did not return a proper path");
695 RETVAL = sen_rc2obj(sen_index_remove(path));
696 OUTPUT:
697 RETVAL
698
699 SV *
700 SIndex_xs_rename(self, new)
701 SV *self;
702 char *new;
703 PREINIT:
704 sen_index *index;
705 char path[SEN_MAX_PATH_SIZE];
706 CODE:
707 index = XS_STATE(sen_index *, self);
708 if (! sen_index_path(index, path, SEN_MAX_PATH_SIZE))
709 croak("sen_index_path did not return a proper path");
710 RETVAL = sen_rc2obj(sen_index_rename(path, new));
711 OUTPUT:
712 RETVAL
713
714 void
715 SIndex_xs_select(self, query_sv, records, op_sv, optarg_sv)
716 SV *self;
717 SV *query_sv;
718 SV *records;
719 SV *op_sv;
720 SV *optarg_sv;
721 PREINIT:
722 SV *sv;
723 STRLEN query_len = 0;
724 sen_index *index;
725 sen_rc rc;
726 sen_records *r;
727 sen_select_optarg *optarg = NULL;
728 sen_sel_operator op = SvOK(op_sv) ? SvIV(op_sv) : 0;
729 char *query = NULL;
730 int need_optarg_free = 0;
731 int need_records_free = 0;
732 PPCODE:
733 index = XS_STATE(sen_index *, self);
734
735 if ( SvOK(query_sv) ) {
736 query = SvPV(query_sv, query_len);
737 }
738
739 if (! SvOK(records)) {
740 r = sen_records_open(sen_rec_document, sen_rec_none, 0);
741 need_records_free = 1;
742 } else {
743 r = XS_STATE(sen_records *, records);
744 }
745
746 if (SvOK(optarg_sv)) {
747 optarg = XS_STATE(sen_select_optarg *, optarg_sv);
748 if (optarg != NULL) {
749 Newz(1234, optarg, 1, sen_select_optarg);
750 optarg->mode = sen_sel_exact;
751 need_optarg_free = 1;
752 }
753 }
754
755 rc = sen_index_select(
756 index,
757 query,
758 #if (SENNA_MAJOR_VERSION >= 1)
759 query_len,
760 #endif
761 r,
762 op,
763 optarg
764 );
765
766 if (need_optarg_free) {
767 Safefree(optarg);
768 }
769
770 if (rc != sen_success) {
771 Safefree(r);
772 croak ("Failed to execute sen_index_select: rc = %d", rc);
773 }
774
775 if (GIMME_V == G_VOID) {
776 if (need_records_free) sen_records_close(r);
777 /* no op */;
778 } else if (GIMME_V == G_SCALAR) {
779 XS_STRUCT2OBJ(sv, "Senna::Records", r);
780 XPUSHs(sv);
781 } else {
782 char keybuf[SEN_MAX_KEY_SIZE];
783 int score;
784 int hits;
785
786 hits = sen_records_nhits(r);
787 if (hits <= 0)
788 return;
789
790 EXTEND(SP, hits);
791 while (sen_records_next(r, &keybuf, SEN_MAX_KEY_SIZE, &score)) {
792 /* Construct Senna::Record object */
793 dSP;
794
795 ENTER;
796 SAVETMPS;
797 PUSHMARK(SP);
798 XPUSHs(sv_2mortal(newSVpv("Senna::Record", 13)));
799 XPUSHs(sv_2mortal(newSVpv("key", 3)));
800
801 SPAGAIN;
802 sv = POPs;
803 if (! SvROK(sv) || SvTYPE(SvRV(sv)) != SVt_PVHV ) {
804 croak ("Senna::Record::new did not return a proper object");
805 }
806 sv = newSVsv(sv);
807
808 PUTBACK;
809 FREETMPS;
810 LEAVE;
811
812 XPUSHs(sv);
813 }
814 if (need_records_free) sen_records_close(r);
815 }
816
817 SV *
818 SIndex_xs_upd(self, key, old_sv, new_sv)
819 SV *self;
820 SV *key;
821 SV *old_sv;
822 SV *new_sv;
823 PREINIT:
824 sen_index *index;
825 int key_size;
826 void *void_key;
827 char *old = NULL;
828 char *new = NULL;
829 STRLEN old_len, new_len;
830 CODE:
831 index = XS_STATE(sen_index *, self);
832 sv2senna_key(index, key, &void_key);
833
834 if ( SvOK(old_sv) ) {
835 old = SvPV(old_sv, old_len);
836 }
837
838 if ( SvOK(new_sv) ) {
839 new = SvPV(new_sv, new_len);
840 }
841
842 RETVAL = sen_rc2obj(sen_index_upd(
843 index,
844 void_key,
845 old,
846 #if (SENNA_MAJOR_VERSION >= 1)
847 old_len,
848 #endif
849 new,
850 #if (SENNA_MAJOR_VERSION >= 1)
851 new_len
852 #endif
853 ));
854
855 OUTPUT:
856 RETVAL
857
858 SV *
859 SIndex_xs_update(self, key, section, old, new)
860 SV *self;
861 SV *key;
862 unsigned int section;
863 SV *old;
864 SV *new;
865 PREINIT:
866 sen_index *index;
867 int key_size;
868 void *void_key;
869 sen_values *old_values;
870 sen_values *new_values;
871 CODE:
872 if (section < 1)
873 croak("section must be >= 1");
874
875 index = XS_STATE(sen_index *, self);
876 old_values = SvOK(old) ? XS_STATE(sen_values *, old) : NULL;
877 new_values = SvOK(new) ? XS_STATE(sen_values *, new) : NULL;
878
879 sv2senna_key(index, key, &void_key);
880 RETVAL = sen_rc2obj(sen_index_update(index, key, section, old_values, new_values));
881 OUTPUT:
882 RETVAL
883
884 SV *
885 SIndex_xs_query_exec(self, query, op = sen_sel_or)
886 SV *self;
887 SV *query;
888 sen_sel_operator op;
889 PREINIT:
890 sen_index *i;
891 sen_query *q;
892 sen_records *r;
893 sen_rc rc;
894 SV *sv;
895 CODE:
896 i = XS_STATE(sen_index *, self);
897 q = XS_STATE(sen_query *, query);
898
899 Newz(1234, r, 1, sen_records);
900
901 rc = sen_query_exec(i, q, r, op);
902 if (rc != sen_success)
903 croak("sen_query_exec failed: rc = %d", rc);
904
905 XS_STRUCT2OBJ(sv, "Senna::Records", r);
906 RETVAL = sv;
907 OUTPUT:
908 RETVAL
909
910 MODULE = Senna PACKAGE = Senna::Records PREFIX=SRecords_
911
912 SV *
913 SRecords_xs_open(class, record_unit, subrec_unit, max_n_subrecs)
914 char *class;
915 sen_rec_unit record_unit;
916 sen_rec_unit subrec_unit;
917 unsigned int max_n_subrecs;
918 PREINIT:
919 sen_records *r;
920 SV *sv;
921 CODE:
922 r = sen_records_open(record_unit, subrec_unit, max_n_subrecs);
923 if (r == NULL)
924 croak("Failed to open sen_records");
925
926 XS_STRUCT2OBJ(sv, class, r);
927 RETVAL = sv;
928 OUTPUT:
929 RETVAL
930
931 void
932 SRecords_xs_next(self)
933 SV *self;
934 PREINIT:
935 sen_records *r;
936 sen_rc rc;
937 PPCODE:
938 r = XS_STATE(sen_records *, self);
939
940 if (GIMME_V == G_SCALAR) {
941 /* If we're being called in scalar context, then just return if
942 * we have a next record or not
943 */
944 rc = sen_records_next(r, NULL, 0, NULL);
945 XPUSHs(rc == 0 ? &PL_sv_no : &PL_sv_yes);
946 } else {
947 /* Otherwise, grab the next entry along with other metadata */
948 SV *key_sv;
949 int score = 0;
950 int key_size = 0;
951 int section = 0;
952 int pos = 0;
953 int n_subrecs = 0;
954
955 sen_sym_info(r->keys, &key_size, NULL, NULL, NULL, NULL);
956
957 if (key_size == SEN_INT_KEY) {
958 long key;
959
960 rc = sen_records_next(r, &key, 0, &score);
961 sen_record_info(r, sen_records_curr_rec(r),
962 NULL, 0, NULL,
963 §ion, &pos, NULL, &n_subrecs);
964 key_sv = newSViv(key);
965 } else {
966 char key[SEN_MAX_KEY_SIZE];
967
968 rc = sen_records_next(r, &key, SEN_MAX_KEY_SIZE, &score);
969 sen_record_info(r, sen_records_curr_rec(r),
970 NULL, 0, NULL,
971 §ion, &pos, NULL, &n_subrecs);
972 key_sv = newSVpv(key, 0);
973 }
974
975 if (rc != 0) {
976 XPUSHs(key_sv);
977 XPUSHs(sv_2mortal(newSViv(score)));
978 XPUSHs(sv_2mortal(newSViv(section)));
979 XPUSHs(sv_2mortal(newSViv(pos)));
980 XPUSHs(sv_2mortal(newSViv(n_subrecs)));
981 }
982 }
983
984 SV *
985 SRecords_rewind(self)
986 SV *self;
987 PREINIT:
988 sen_records *r;
989 CODE:
990 r = XS_STATE(sen_records *, self);
991 RETVAL = sen_rc2obj(sen_records_rewind(r));
992 OUTPUT:
993 RETVAL
994
995 int
996 SRecords_nhits(self)
997 SV *self;
998 PREINIT:
999 sen_records *r;
1000 CODE:
1001 r = XS_STATE(sen_records *, self);
1002 RETVAL = sen_records_nhits(r);
1003 OUTPUT:
1004 RETVAL
1005
1006 int
1007 SRecords_curr_score(self)
1008 SV *self;
1009 PREINIT:
1010 sen_records *r;
1011 CODE:
1012 r = XS_STATE(sen_records *, self);
1013 RETVAL = sen_records_curr_score(r);
1014 OUTPUT:
1015 RETVAL
1016
1017 int
1018 SRecords_find(self, key)
1019 SV *self;
1020 SV *key;
1021 PREINIT:
1022 sen_records *r;
1023 int key_size;
1024 STRLEN len;
1025 CODE:
1026 r = XS_STATE(sen_records *, self);
1027
1028 sen_records_rewind(r);
1029 sen_record_info(r, sen_records_curr_rec(r), NULL, 0, &key_size,
1030 NULL, NULL, NULL, NULL);
1031 /* int sen_records_find(sen_records *r, const void *key);
1032 */
1033 if (key_size == SEN_INT_KEY) {
1034 RETVAL = sen_records_find(r, (const void *) SvIV(key));
1035 } else {
1036 RETVAL = sen_records_find(r, (const void *) SvPV(key, len));
1037 }
1038 sen_records_rewind(r);
1039 OUTPUT:
1040 RETVAL
1041
1042 SV *
1043 SRecords_curr_key(self)
1044 SV *self;
1045 PREINIT:
1046 int key_size;
1047 sen_records *r;
1048 CODE:
1049 r = XS_STATE(sen_records *, self);
1050
1051 /* if we've already reached the end, don't even bother */
1052 if( ! r->curr_rec)
1053 XSRETURN_UNDEF;
1054
1055 sen_record_info(r, sen_records_curr_rec(r), NULL, 0, &key_size,
1056 NULL, NULL, NULL, NULL);
1057
1058 if (key_size == SEN_INT_KEY) {
1059 long int_key;
1060 if (! sen_records_curr_key(r, &int_key, 1))
1061 XSRETURN_UNDEF;
1062
1063 RETVAL = newSViv(int_key);
1064 } else {
1065 char char_key[SEN_MAX_KEY_SIZE];
1066
1067 if (! sen_records_curr_key(r, &char_key, SEN_MAX_KEY_SIZE) )
1068 XSRETURN_UNDEF;
1069 RETVAL = newSVpv(char_key, 0);
1070 }
1071 OUTPUT:
1072 RETVAL
1073
1074 SV *
1075 SRecords_close(self)
1076 SV *self;
1077 PREINIT:
1078 sen_records *r;
1079 CODE:
1080 r = XS_STATE(sen_records *, self);
1081 RETVAL = sen_rc2obj(sen_records_close(r));
1082 OUTPUT:
1083 RETVAL
1084
1085 SV *
1086 SRecords_union(self, other)
1087 SV *self;
1088 SV *other;
1089 PREINIT:
1090 sen_records *r;
1091 SV *sv;
1092 CODE:
1093 r = sen_records_union(XS_STATE(sen_records *, self),
1094 XS_STATE(sen_records *, other));
1095 XS_STRUCT2OBJ(sv, "Senna::Records", r);
1096 RETVAL = sv;
1097 OUTPUT:
1098 RETVAL
1099
1100 SV *
1101 SRecords_subtract(self, other)
1102 SV *self;
1103 SV *other;
1104 PREINIT:
1105 sen_records *r;
1106 SV *sv;
1107 CODE:
1108 r = sen_records_subtract(XS_STATE(sen_records *, self),
1109 XS_STATE(sen_records *, other));
1110 XS_STRUCT2OBJ(sv, "Senna::Records", r);
1111 RETVAL = sv;
1112 OUTPUT:
1113 RETVAL
1114
1115 SV *
1116 SRecords_intersect(self, other)
1117 SV *self;
1118 SV *other;
1119 PREINIT:
1120 sen_records *r;
1121 SV *sv;
1122 CODE:
1123 r = sen_records_intersect(XS_STATE(sen_records *, self),
1124 XS_STATE(sen_records *, other));
1125 XS_STRUCT2OBJ(sv, "Senna::Records", r);
1126 RETVAL = sv;
1127 OUTPUT:
1128 RETVAL
1129
1130 int
1131 SRecords_difference(self, other)
1132 SV *self;
1133 SV *other;
1134 PREINIT:
1135 SV *sv;
1136 CODE:
1137 RETVAL = sen_records_difference(
1138 XS_STATE(sen_records *, self),
1139 XS_STATE(sen_records *, other));
1140 OUTPUT:
1141 RETVAL
1142
1143 SV *
1144 SRecords_xs_sort(self, limit, optarg)
1145 SV *self;
1146 int limit;
1147 SV *optarg;
1148 PREINIT:
1149 sen_records *r;
1150 sen_sort_optarg *o;
1151 CODE:
1152 r = XS_STATE(sen_records *, self);
1153 o = XS_STATE(sen_sort_optarg *, optarg);
1154 RETVAL = sen_rc2obj(sen_records_sort(r, limit, o));
1155 OUTPUT:
1156 RETVAL
1157
1158 MODULE = Senna PACKAGE = Senna::Query PREFIX=SQuery_
1159
1160 SV *
1161 SQuery_xs_open(class, str, default_op, max_exprs, encoding)
1162 char *class;
1163 char *str;
1164 sen_sel_operator default_op;
1165 int max_exprs;
1166 sen_encoding encoding;
1167 PREINIT:
1168 SV *sv;
1169 sen_query *q;
1170 CODE:
1171 q = sen_query_open(
1172 str,
1173 #if (SENNA_MAJOR_VERSION >= 1)
1174 strlen(str),
1175 #endif
1176 default_op,
1177 max_exprs,
1178 encoding
1179 );
1180 if (q == NULL)
1181 croak("Failed to open query");
1182
1183 XS_STRUCT2OBJ(sv, class, q);
1184 RETVAL = sv;
1185 OUTPUT:
1186 RETVAL
1187
1188 char *
1189 SQuery_rest(self)
1190 SV *self;
1191 PREINIT:
1192 sen_query *q;
1193 #if (SENNA_MAJOR_VERSION >= 1)
1194 char *rest;
1195 unsigned int len;
1196 #endif
1197 CODE:
1198 q = XS_STATE(sen_query *, self);
1199 #if (SENNA_MAJOR_VERSION >= 1)
1200 len = sen_query_rest(q, (const char**) &rest);
1201 RETVAL = rest;
1202 #else
1203 RETVAL = (char *) sen_query_rest(q);
1204 #endif
1205 OUTPUT:
1206 RETVAL
1207
1208 SV *
1209 SQuery_close(self)
1210 SV *self;
1211 PREINIT:
1212 sen_query *q;
1213 CODE:
1214 q = XS_STATE(sen_query *, self);
1215 RETVAL = sen_rc2obj(sen_query_close(q));
1216 OUTPUT:
1217 RETVAL
1218
1219 MODULE = Senna PACKAGE = Senna::Symbol PREFIX=SSymbol_
1220
1221 SV *
1222 SSymbol_xs_create(class, path, key_size, flags, encoding)
1223 char *class;
1224 char *path;
1225 unsigned int key_size;
1226 unsigned int flags;
1227 sen_encoding encoding;
1228 PREINIT:
1229 SV *sv;
1230 sen_sym *sym;
1231 CODE:
1232 sym = sen_sym_create(path, key_size, flags, encoding);
1233 if (sym == NULL)
1234 croak("Failed to create sym");
1235 XS_STRUCT2OBJ(sv, class, sym);
1236
1237 RETVAL = sv;
1238 OUTPUT:
1239 RETVAL
1240
1241 SV *
1242 SSymbol_xs_open(class, path)
1243 char *class;
1244 char *path;
1245 PREINIT:
1246 SV *sv;
1247 sen_sym *sym;
1248 CODE:
1249 sym = sen_sym_open(path);
1250 if(sym == NULL)
1251 croak("Failed to open sen_sym");
1252 XS_STRUCT2OBJ(sv, class, sym);
1253 RETVAL = sv;
1254 OUTPUT:
1255 RETVAL
1256
1257 SV *
1258 SSymbol_close(self)
1259 SV *self;
1260 PREINIT:
1261 sen_sym *sym;
1262 CODE:
1263 sym = XS_STATE(sen_sym *, self);
1264 RETVAL = sen_rc2obj(sen_sym_close(sym));
1265 OUTPUT:
1266 RETVAL
1267
1268 sen_id
1269 SSymbol_xs_get(self, key)
1270 SV *self;
1271 char *key;
1272 PREINIT:
1273 sen_sym *sym;
1274 CODE:
1275 sym = XS_STATE(sen_sym *, self);
1276 RETVAL = sen_sym_get(sym, key);
1277 OUTPUT:
1278 RETVAL
1279
1280 sen_id
1281 SSymbol_xs_at(self, key)
1282 SV *self;
1283 char *key;
1284 PREINIT:
1285 sen_sym *sym;
1286 CODE:
1287 sym = XS_STATE(sen_sym *, self);
1288 RETVAL = sen_sym_at(sym, key);
1289 OUTPUT:
1290 RETVAL
1291
1292 SV *
1293 SSymbol_xs_del(self, key)
1294 SV *self;
1295 char *key;
1296 PREINIT:
1297 sen_sym *sym;
1298 CODE:
1299 sym = XS_STATE(sen_sym *, self);
1300 RETVAL = sen_rc2obj(sen_sym_del(sym, key));
1301 OUTPUT:
1302 RETVAL
1303
1304 unsigned int
1305 SSymbol_size(self)
1306 SV *self;
1307 PREINIT:
1308 sen_sym *sym;
1309 CODE:
1310 sym = XS_STATE(sen_sym *, self);
1311 RETVAL = sen_sym_size(sym);
1312 OUTPUT:
1313 RETVAL
1314
1315 char *
1316 SSymbol_xs_key(self, id)
1317 SV *self;
1318 sen_id id;
1319 PREINIT:
1320 sen_sym *sym;
1321 char keybuf[SEN_SYM_MAX_KEY_LENGTH];
1322 sen_rc rc;
1323 CODE:
1324 sym = XS_STATE(sen_sym *, self);
1325 rc = sen_sym_key(sym, id, keybuf, SEN_SYM_MAX_KEY_LENGTH);
1326 if (rc != sen_success)
1327 croak("Failed to call sen_sym_key: %d", rc);
1328
1329 RETVAL = keybuf;
1330 OUTPUT:
1331 RETVAL
1332
1333 sen_id
1334 SSymbol_xs_common_prefix_search(self, key)
1335 SV *self;
1336 char *key;
1337 PREINIT:
1338 sen_sym *sym;
1339 CODE:
1340 sym = XS_STATE(sen_sym *, self);
1341 RETVAL = sen_sym_common_prefix_search(sym, key);
1342 OUTPUT:
1343 RETVAL
1344
1345 SV *
1346 SSymbol_xs_prefix_search(self, key);
1347 SV *self;
1348 char *key;
1349 PREINIT:
1350 sen_sym *sym;
1351 SV *sv;
1352 CODE:
1353 sym = XS_STATE(sen_sym *, self);
1354 XS_STRUCT2OBJ(sv, "Senna::Set", sen_sym_prefix_search(sym, key));
1355 RETVAL = sv;
1356 OUTPUT:
1357 RETVAL
1358
1359 SV *
1360 SSymbol_xs_suffix_search(self, key);
1361 SV *self;
1362 char *key;
1363 PREINIT:
1364 sen_sym *sym;
1365 SV *sv;
1366 CODE:
1367 sym = XS_STATE(sen_sym *, self);
1368 XS_STRUCT2OBJ(sv, "Senna::Set", sen_sym_suffix_search(sym, key));
1369 RETVAL = sv;
1370 OUTPUT:
1371 RETVAL
1372
1373 int
1374 SSymbol_xs_pocket_get(self, id)
1375 SV *self;
1376 sen_id id;
1377 PREINIT:
1378 sen_sym *sym;
1379 CODE:
1380 sym = XS_STATE(sen_sym *, self);
1381 RETVAL = sen_sym_pocket_get(sym, id);
1382 OUTPUT:
1383 RETVAL
1384
1385 SV *
1386 SSymbol_xs_pocket_set(self, id, value)
1387 SV *self;
1388 sen_id id;
1389 unsigned int value;
1390 PREINIT:
1391 sen_sym *sym;
1392 CODE:
1393 sym = XS_STATE(sen_sym *, self);
1394 RETVAL = sen_rc2obj(sen_sym_pocket_set(sym, id, value));
1395 OUTPUT:
1396 RETVAL
1397
1398 sen_id
1399 SSymbol_xs_next(self, id)
1400 SV *self;
1401 sen_id id;
1402 PREINIT:
1403 sen_sym *sym;
1404 CODE:
1405 sym = XS_STATE(sen_sym *, self);
1406 RETVAL = sen_sym_next(sym, id);
1407 OUTPUT:
1408 RETVAL
1409
1410 MODULE = Senna PACKAGE = Senna::Set PREFIX=SSet_
1411
1412 SV *
1413 SSet_xs_open(class, key_size = SEN_VARCHAR_KEY, value_size = 0, n_entries = 0)
1414 char *class;
1415 unsigned int key_size;
1416 unsigned int value_size;
1417 unsigned int n_entries;
1418 PREINIT:
1419 SV *sv;
1420 sen_set *set;
1421 CODE:
1422 set = sen_set_open(key_size, value_size, n_entries);
1423 XS_STRUCT2OBJ(sv, class, set);
1424 RETVAL = sv;
1425 OUTPUT:
1426 RETVAL
1427
1428 SV *
1429 SSet_close(self)
1430 SV *self;
1431 PREINIT:
1432 sen_set *set;
1433 CODE:
1434 set = XS_STATE(sen_set *, self);
1435 RETVAL = sen_rc2obj(sen_set_close(set));
1436 OUTPUT:
1437 RETVAL
1438
1439 void
1440 SSet_info(self)
1441 SV *self;
1442 PREINIT:
1443 sen_rc rc;
1444 sen_set *set;
1445 unsigned int key_size;
1446 unsigned int value_size;
1447 unsigned int n_entries;
1448 PPCODE:
1449 set = XS_STATE(sen_set *, self);
1450 rc = sen_set_info(set, &key_size, &value_size, &n_entries);
1451 if (rc != sen_success)
1452 croak ("Failed to call sen_set_info: %d", rc);
1453
1454 EXTEND(SP, 3);
1455 PUSHs(sv_2mortal(newSViv(key_size)));
1456 PUSHs(sv_2mortal(newSViv(value_size)));
1457 PUSHs(sv_2mortal(newSViv(n_entries)));
1458
1459 MODULE = Senna PACKAGE = Senna::Snippet PREFIX=SSnip_
1460
1461 SV *
1462 SSnip_xs_open(class, encoding, flags, width, max_results, default_open_tag_sv, default_close_tag_sv, mapping_sv)
1463 char* class;
1464 sen_encoding encoding;
1465 int flags;
1466 size_t width;
1467 unsigned int max_results;
1468 SV* default_open_tag_sv;
1469 SV* default_close_tag_sv;
1470 SV* mapping_sv;
1471 PREINIT:
1472 int mapping;
1473 sen_snip *snip;
1474 sen_perl_snip *perl_snip;
1475 char *default_open_tag = NULL;
1476 char *default_close_tag = NULL;
1477 STRLEN default_open_tag_len = 0;
1478 STRLEN default_close_tag_len = 0;
1479 SV *sv;
1480 CODE:
1481 if (max_results > MAX_SNIP_RESULT_COUNT)
1482 croak("Senna::Snippet::new(): max_results exceeds MAX_SNIP_RESULT_COUNT (%d)", MAX_SNIP_RESULT_COUNT);
1483
1484 if (SvPOK(default_open_tag_sv) && sv_len(default_open_tag_sv))
1485 default_open_tag = SvPV(default_open_tag_sv, default_open_tag_len);
1486
1487 if (SvPOK(default_close_tag_sv) && sv_len(default_close_tag_sv))
1488 default_close_tag = SvPV(default_close_tag_sv, default_close_tag_len);
1489
1490 mapping = SvTRUE(mapping_sv) ? -1 : 0;
1491
1492 Newz(1234, perl_snip, 1, sen_perl_snip);
1493
1494 if (default_open_tag == NULL)
1495 croak("Senna::Snippet::new(): default_open_tag must be specified");
1496
1497 if (default_close_tag == NULL)
1498 croak("Senna::Snippet::new(): default_close_tag must be specified");
1499
1500
1501 perl_snip->open_tags_size = 1;
1502 Newz(1234, perl_snip->open_tags, 1, char *);
1503 Newz(1234, perl_snip->open_tags[perl_snip->open_tags_size - 1], default_open_tag_len + 1, char);
1504 Copy(default_open_tag, perl_snip->open_tags[perl_snip->open_tags_size - 1], default_open_tag_len, char);
1505 default_open_tag = perl_snip->open_tags[perl_snip->open_tags_size - 1];
1506
1507 perl_snip->close_tags_size = 1;
1508 Newz(1234, perl_snip->close_tags, 1, char *);
1509 Newz(1234, (perl_snip->close_tags[perl_snip->close_tags_size - 1]), default_close_tag_len + 1, char);
1510 Copy(default_close_tag, perl_snip->close_tags[0], default_close_tag_len, char);
1511 default_close_tag = perl_snip->close_tags[perl_snip->close_tags_size - 1];
1512
1513 /* mapping is written as a struct, but the docs say that you should
1514 * specify NULL or -1
1515 */
1516 snip = sen_snip_open(
1517 encoding,
1518 flags,
1519 width,
1520 max_results,
1521 default_open_tag,
1522 #if (SENNA_MAJOR_VERSION >= 1)
1523 default_open_tag_len,
1524 #endif
1525 default_close_tag,
1526 #if (SENNA_MAJOR_VERSION >= 1)
1527 default_close_tag_len,
1528 #endif
1529 (sen_snip_mapping *) mapping
1530 );
1531 if (snip == NULL)
1532 croak("Failed to create snip");
1533
1534 perl_snip->snip = snip;
1535
1536 XS_STRUCT2OBJ(sv, class, perl_snip);
1537 RETVAL = sv;
1538 OUTPUT:
1539 RETVAL
1540
1541 SV *
1542 SSnip_xs_add_cond(self, keyword, opentag_sv, closetag_sv)
1543 SV *self;
1544 char *keyword;
1545 SV *opentag_sv;
1546 SV *closetag_sv;
1547 PREINIT:
1548 char *opentag = NULL;
1549 char *closetag = NULL;
1550 sen_perl_snip *snip;
1551 STRLEN opentag_len = 0;
1552 STRLEN closetag_len = 0;
1553 CODE:
1554 snip = XS_STATE(sen_perl_snip *, self);
1555
1556 if (SvPOK(opentag_sv) && sv_len(opentag_sv) > 0) {
1557 opentag = SvPV(opentag_sv, opentag_len);
1558 snip->open_tags_size++;
1559 Renew(snip->open_tags, snip->open_tags_size, char *);
1560 Newz(1234, snip->open_tags[snip->open_tags_size - 1], opentag_len + 1, char);
1561 Copy(opentag, snip->open_tags[snip->open_tags_size - 1], opentag_len, char);
1562 opentag = snip->open_tags[snip->open_tags_size - 1];
1563 }
1564
1565 if (SvPOK(closetag_sv) && sv_len(closetag_sv) > 0) {
1566 closetag = SvPV(closetag_sv, closetag_len);
1567 snip->close_tags_size++;
1568 Renew(snip->close_tags, snip->close_tags_size, char *);
1569 Newz(1234, snip->close_tags[snip->close_tags_size - 1], closetag_len + 1, char);
1570 Copy(closetag, snip->close_tags[snip->close_tags_size - 1], closetag_len, char);
1571 closetag = snip->close_tags[snip->close_tags_size - 1];
1572 }
1573
1574 RETVAL = sen_rc2obj(
1575 sen_snip_add_cond(
1576 snip->snip,
1577 keyword,
1578 #if (SENNA_MAJOR_VERSION >= 1)
1579 strlen(keyword),
1580 #endif
1581 opentag,
1582 #if (SENNA_MAJOR_VERSION >= 1)
1583 opentag_len,
1584 #endif
1585 closetag
1586 #if (SENNA_MAJOR_VERSION >= 1)
1587 ,
1588 closetag_len
1589 #endif
1590 )
1591 );
1592 OUTPUT:
1593 RETVAL
1594
1595 void
1596 SSnip_xs_exec(self, string)
1597 SV *self;
1598 char *string;
1599 PREINIT:
1600 sen_perl_snip *snip;
1601 unsigned int nresults;
1602 char *result;
1603 #if (SENNA_MAJOR_VERSION >= 1)
1604 unsigned int max_tagged_len;
1605 #else
1606 size_t max_tagged_len;
1607 #endif
1608 int i;
1609 sen_rc rc;
1610 PPCODE:
1611 snip = XS_STATE(sen_perl_snip *, self);
1612 sen_snip_exec(
1613 snip->snip,
1614 string,
1615 #if (SENNA_MAJOR_VERSION >= 1)
1616 strlen(string),
1617 #endif
1618 &nresults,
1619 &max_tagged_len
1620 );
1621
1622 EXTEND(SP, nresults);
1623 Newz(1234, result, max_tagged_len, char);
1624 for(i = 0; i < nresults; i++) {
1625 rc = sen_snip_get_result(
1626 snip->snip,
1627 i,
1628 result
1629 #if (SENNA_MAJOR_VERSION >= 1)
1630 ,
1631 &max_tagged_len
1632 #endif
1633 );
1634 if (rc != sen_success)
1635 croak("Call to sen_snip_get_result returned %d", rc);
1636 PUSHs(sv_2mortal(newSVpv(result, 0)));
1637 }
1638 Safefree(result);
1639
1640 void
1641 DESTROY(self)
1642 SV *self;
1643 PREINIT:
1644 sen_perl_snip *snip;
1645 int i;
1646 PPCODE:
1647 snip = XS_STATE(sen_perl_snip *, self);
1648 sen_snip_close(snip->snip);
1649
1650 for(i = 0; i < snip->open_tags_size; i++) {
1651 Safefree(snip->open_tags[i]);
1652 }
1653 Safefree(snip->open_tags);
1654
1655 for(i = 0; i < snip->close_tags_size; i++) {
1656 Safefree(snip->close_tags[i]);
1657 }
1658 Safefree(snip->close_tags);
1659
1660
1661
1662
1663 MODULE = Senna PACKAGE = Senna::OptArg::Sort PREFIX=SOSort_
1664
1665 SV *
1666 SOSort_xs_new(class, mode, compar = NULL, compar_arg = NULL)
1667 char *class;
1668 sen_sort_mode mode;
1669 CV *compar;
1670 AV *compar_arg;
1671 PREINIT:
1672 sen_sort_optarg *optarg;
1673 SV *sv;
1674 CODE:
1675 Newz(1234, optarg, 1, sen_sort_optarg);
1676 optarg->mode = mode;
1677
1678 if (SvOK(compar)) {
1679 optarg->compar = &sen_sort_optarg_cb;
1680
1681 /* The callback arguments are always the CV of the callback,
1682 * the AV of the argument list, and key_size of the index.
1683 */
1684 Newz(1234, optarg->compar_arg, 2, void *);
1685 ((void **)optarg->compar_arg)[0] = compar;
1686 if (SvOK(compar_arg) && SvTYPE(compar_arg) == SVt_PVCV)
1687 ((void **)optarg->compar_arg)[1] = SvREFCNT_inc(compar_arg);
1688 }
1689
1690 XS_STRUCT2OBJ(sv, class, optarg);
1691 RETVAL = sv;
1692 OUTPUT:
1693 RETVAL
1694
1695 sen_sort_mode
1696 SOSort_mode(self)
1697 SV *self;
1698 PREINIT:
1699 sen_sort_optarg *optarg;
1700 CODE:
1701 optarg = XS_STATE(sen_sort_optarg *, self);
1702 RETVAL = optarg->mode;
1703 OUTPUT:
1704 RETVAL
1705
1706 CV *
1707 SOSort_compar(self)
1708 SV *self;
1709 PREINIT:
1710 sen_sort_optarg *optarg;
1711 void **args;
1712 CODE:
1713 optarg = XS_STATE(sen_sort_optarg *, self);
1714 /* The CV is always placed in the first element of ->compar_arg */
1715 args = (void **) optarg->compar_arg;
1716 if (args[0] == NULL)
1717 XSRETURN_UNDEF;
1718
1719 RETVAL = (CV *) args[0];
1720 OUTPUT:
1721 RETVAL
1722
1723 void
1724 SOSort_compar_arg(self)
1725 SV *self;
1726 PREINIT:
1727 sen_sort_optarg *optarg;
1728 void **args;
1729 PPCODE:
1730 optarg = XS_STATE(sen_sort_optarg *, self);
1731 /* The CV is always placed in the second element of ->func_arg */
1732 args = (void **) optarg->compar_arg;
1733 if (GIMME_V == G_SCALAR) {
1734 AV *av;
1735 if (args[1] == NULL)
1736 return;
1737
1738 av = (AV *) args[1];
1739 EXTEND(SP, 1);
1740 PUSHs(newRV_noinc((SV *) av));
1741 } else {
1742 AV *av;
1743 int i;
1744 int len;
1745 SV **svr;
1746
1747 av = (AV *) args[1];
1748 len = av_len(av) + 1;
1749 if (len <= 0)
1750 return;
1751
1752 EXTEND(SP, len);
1753 for (i = 0; i < len; i++) {
1754 svr = av_fetch(av, i - 1, 0);
1755 if (*svr != NULL && SvOK(*svr)) {
1756 PUSHs(*svr);
1757 }
1758 }
1759 }
1760
1761 void
1762 SOSort_DESTROY(self)
1763 SV *self;
1764 PREINIT:
1765 sen_sort_optarg *optarg;
1766 CODE:
1767 optarg = XS_STATE(sen_sort_optarg *, self);
1768 if (optarg->compar_arg != NULL) {
1769 void **args = (void **) optarg->compar_arg;
1770
1771 if (args[0] != NULL)
1772 SvREFCNT_dec((CV *) args[0]);
1773
1774 if (args[1] != NULL)
1775 SvREFCNT_dec((AV *) args[1]);
1776
1777 Safefree(args);
1778 }
1779
1780 Safefree(optarg);
1781
1782 MODULE = Senna PACKAGE = Senna::OptArg::Select PREFIX=SOSelect_
1783
1784 SV *
1785 SOSelect_xs_new(class, mode, similarity_threshold, max_interval, weight_vector, func = NULL, func_args = NULL)
1786 char *class;
1787 sen_sel_mode mode;
1788 int similarity_threshold;
1789 int max_interval;
1790 AV *weight_vector;
1791 CV *func;
1792 AV *func_args;
1793 PREINIT:
1794 sen_select_optarg *optarg;
1795 SV **svr;
1796 int i;
1797 SV *sv;
1798 CODE:
1799 Newz(1234, optarg, 1, sen_select_optarg);
1800 optarg->mode = mode;
1801 optarg->similarity_threshold = similarity_threshold;
1802 optarg->vector_size = av_len(weight_vector) + 1;
1803 optarg->max_interval = max_interval;
1804
1805 if (optarg->vector_size > 0) {
1806 Newz(1234, optarg->weight_vector, optarg->vector_size, int);
1807 for(i = 0; i < optarg->vector_size; i++) {
1808 svr = av_fetch(weight_vector, i, 0);
1809 if (svr != NULL && SvIOK(*svr)) {
1810 optarg->weight_vector[i] = SvIV(*svr);
1811 }
1812 }
1813 }
1814
1815 if (SvOK(func)) {
1816 int key_size;
1817 void **args;
1818
1819 optarg->func = &sen_select_optarg_cb;
1820
1821 /* The callback arguments are always the CV of the callback,
1822 * the AV of the argument list, and key_size of the index.
1823 */
1824 Newz(1234, args, 2, void *);
1825 args[0] = (void *) func;
1826 if (SvOK(func_args))
1827 args[1] = (void *) func_args;
1828 optarg->func_arg = (void *) args;
1829 }
1830
1831 XS_STRUCT2OBJ(sv, class, optarg);
1832 RETVAL = sv;
1833
1834 OUTPUT:
1835 RETVAL
1836
1837 sen_sel_mode
1838 SOSelect_mode(self)
1839 SV *self;
1840 PREINIT:
1841 sen_select_optarg *optarg;
1842 CODE:
1843 optarg = XS_STATE(sen_select_optarg *, self);
1844 RETVAL = optarg->mode;
1845 OUTPUT:
1846 RETVAL
1847
1848 int
1849 SOSelect_similarity_threshold(self)
1850 SV *self;
1851 PREINIT:
1852 sen_select_optarg *optarg;
1853 CODE:
1854 optarg = XS_STATE(sen_select_optarg *, self);
1855 RETVAL = optarg->similarity_threshold;
1856 OUTPUT:
1857 RETVAL
1858
1859 int
1860 SOSelect_max_interval(self)
1861 SV *self;
1862 PREINIT:
1863 sen_select_optarg *optarg;
1864 CODE:
1865 optarg = XS_STATE(sen_select_optarg *, self);
1866 RETVAL = optarg->max_interval;
1867 OUTPUT:
1868 RETVAL
1869
1870 void
1871 SOSelect_weight_vector(self)
1872 SV *self;
1873 PREINIT:
1874 sen_select_optarg *optarg;
1875 PPCODE:
1876 optarg = XS_STATE(sen_select_optarg *, self);
1877 if (optarg->vector_size <= 0)
1878 return;
1879
1880 if (GIMME_V == G_SCALAR) {
1881 AV *av = newAV();
1882 int i;
1883
1884 EXTEND(SP, 1);
1885 av_extend(av, optarg->vector_size - 1);
1886 for (i = 0; i < optarg->vector_size; i++) {
1887 av_push(av, newSViv(optarg->weight_vector[i]));
1888 }
1889 PUSHs(newRV_inc((SV *) av));
1890 } else {
1891 int i;
1892
1893 EXTEND(SP, optarg->vector_size);
1894 for (i = 0; i < optarg->vector_size; i++) {
1895 PUSHs(newSViv(optarg->weight_vector[i]));
1896 }
1897 }
1898
1899 CV *
1900 SOSelect_func(self)
1901 SV *self;
1902 PREINIT:
1903 sen_select_optarg *optarg;
1904 void **args;
1905 CODE:
1906 optarg = XS_STATE(sen_select_optarg *, self);
1907 /* The CV is always placed in the first element of ->func_arg */
1908 args = (void **) optarg->func_arg;
1909 if (args[0] == NULL)
1910 XSRETURN_UNDEF;
1911
1912 RETVAL = (CV *) args[0];
1913 OUTPUT:
1914 RETVAL
1915
1916 void
1917 SOSelect_func_arg(self)
1918 SV *self;
1919 PREINIT:
1920 sen_select_optarg *optarg;
1921 void **args;
1922 PPCODE:
1923 optarg = XS_STATE(sen_select_optarg *, self);
1924 /* The CV is always placed in the second element of ->func_arg */
1925 args = (void **) optarg->func_arg;
1926 if (GIMME_V == G_SCALAR) {
1927 AV *av;
1928 if (args[1] == NULL)
1929 return;
1930
1931 av = (AV *) args[1];
1932 EXTEND(SP, 1);
1933 PUSHs(newRV_noinc((SV *) av));
1934 } else {
1935 AV *av;
1936 int i;
1937 int len;
1938 SV **svr;
1939
1940 av = (AV *) args[1];
1941 len = av_len(av) + 1;
1942 if (len <= 0)
1943 return;
1944
1945 EXTEND(SP, len);
1946 for (i = 0; i < len; i++) {
1947 svr = av_fetch(av, i - 1, 0);
1948 if (*svr != NULL && SvOK(*svr)) {
1949 PUSHs(*svr);
1950 }
1951 }
1952 }
1953
1954 SV *
1955 SOSelect_DESTROY(self)
1956 SV *self;
1957 PREINIT:
1958 sen_select_optarg *optarg;
1959 CODE:
1960 optarg = XS_STATE(sen_select_optarg *, self);
1961 if (optarg->weight_vector != NULL)
1962 Safefree(optarg->weight_vector);
1963
1964 if (optarg->func_arg != NULL) {
1965 void **args = (void **) optarg->func_arg;
1966 if (args[0] != NULL)
1967 SvREFCNT_dec((CV *) args[0]);
1968
1969 if (args[1] != NULL)
1970 SvREFCNT_dec((AV *) args[1]);
1971
1972 Safefree(optarg->func_arg);
1973 }
1974
1975 Safefree(optarg);
1976
1977 MODULE = Senna PACKAGE = Senna::Values PREFIX=SValues_
1978
1979 SV *
1980 SValues_open(class)
1981 char *class;
1982 PREINIT:
1983 sen_values *values;
1984 SV *sv;
1985 CODE:
1986 values = sen_values_open();
1987 XS_STRUCT2OBJ(sv, class, values);
1988 RETVAL = sv;
1989 OUTPUT:
1990 RETVAL
1991
1992 SV *
1993 SValues_close(self)
1994 SV *self;
1995 PREINIT:
1996 sen_values *values;
1997 CODE:
1998 values = XS_STATE(sen_values *, self);
1999 RETVAL = sen_rc2obj(sen_values_close(values));
2000 OUTPUT:
2001 RETVAL
2002
2003 SV *
2004 SValues_xs_add(self, str, weight)
2005 SV *self;
2006 char *str;
2007 unsigned int weight;
2008 PREINIT:
2009 sen_values *values;
2010 CODE:
2011 values = XS_STATE(sen_values *, self);
2012 RETVAL = sen_rc2obj(
2013 sen_values_add(
2014 values,
2015 str,
2016 #if (SENNA_MAJOR_VERSION >= 1)
2017 strlen(str),
2018 #endif
2019 weight
2020 )
2021 );
2022 OUTPUT:
2023 RETVAL
2024
2025