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             &section, &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             &section, &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             &section, &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             &section, &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                     &section, &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                     &section, &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