#ifdef __cplusplus
extern "C" {
#endif
#define PERL_NO_GET_CONTEXT
#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"
#ifdef __cplusplus
}
#endif
// Hack to work around "error: declaration of 'Perl___notused' has a different
// language linkage" error on Clang
#ifdef dNOOP
# undef dNOOP
# define dNOOP
#endif
#ifdef do_open
#undef do_open
#endif
#ifdef do_close
#undef do_close
#endif
#define NEED_sv_2pvbyte
#define NEED_newCONSTSUB
#define NEED_newRV_noinc
#define NEED_newSVpvn_flags
#define NEED_sv_2pv_flags
#include "ppport.h"
#include <sstream>
#include "ux.hpp"
#define CHECK_RESULT(ret) STMT_START { \
int rc = ret; \
if (rc != 0) { \
std::string what = THIS->what(rc); \
if (what.empty()) { \
what = "An error occured"; \
} \
croak("%s", what.c_str()); \
} \
} STMT_END
static SV *
do_callback(pTHX_ SV *callback, SV *str) {
dSP;
int count;
SV *retval;
ENTER;
SAVETMPS;
PUSHMARK(SP);
XPUSHs(sv_2mortal(str));
PUTBACK;
count = call_sv(callback, G_SCALAR);
SPAGAIN;
if (count != 1) {
croak("callback sub must return scalar!");
}
retval = newSVsv(POPs);
PUTBACK;
FREETMPS;
LEAVE;
return retval;
}
MODULE = Text::Ux PACKAGE = Text::Ux
PROTOTYPES: DISABLE
BOOT:
{
HV* ux = gv_stashpv("Text::Ux", 1);
newCONSTSUB(ux, "LIMIT_DEFAULT", newSViv(ux::LIMIT_DEFAULT));
}
ux::Trie*
ux::Trie::new()
CODE:
RETVAL = new ux::Trie();
OUTPUT:
RETVAL
void
ux::Trie::build(AV* key_list, bool is_tail_ux = true)
CODE:
std::vector<std::string> keys;
I32 keys_len = av_len(key_list);
for (I32 i = 0; i <= keys_len; i++) {
STRLEN len;
char* key = SvPV(*av_fetch(key_list, i, 0), len);
keys.push_back(std::string(key, len));
}
THIS->build(keys, is_tail_ux);
void
ux::Trie::save(SV* stuff)
CODE:
if (SvROK(stuff) && strcmp(sv_reftype(SvRV(stuff), TRUE), "SCALAR") == 0) {
std::ostringstream os;
CHECK_RESULT(THIS->save(os));
std::string str = os.str();
sv_setpvn(SvRV(stuff), str.c_str(), str.length());
} else {
CHECK_RESULT(THIS->save(SvPV_nolen(stuff)));
}
void
ux::Trie::load(SV* stuff)
CODE:
if (SvROK(stuff) && strcmp(sv_reftype(SvRV(stuff), TRUE), "SCALAR") == 0) {
STRLEN len;
char* str = SvPVbyte(SvRV(stuff), len);
std::istringstream is(std::string(str, len));
CHECK_RESULT(THIS->load(is));
} else {
CHECK_RESULT(THIS->load(SvPV_nolen(stuff)));
}
SV*
ux::Trie::prefix_search(SV* query)
CODE:
if (!SvOK(query)) {
XSRETURN_UNDEF;
}
STRLEN len;
char* str = SvPV(query, len);
size_t ret_len;
ux::id_t id = THIS->prefixSearch(str, len, ret_len);
if (id == ux::NOTFOUND) {
XSRETURN_UNDEF;
}
std::string key = THIS->decodeKey(id);
RETVAL = newSVpvn_utf8(key.c_str(), key.length(), SvUTF8(query));
OUTPUT:
RETVAL
void
ux::Trie::common_prefix_search(SV* query, size_t limit = ux::LIMIT_DEFAULT)
PPCODE:
if (!SvOK(query)) {
XSRETURN_EMPTY;
}
STRLEN len;
char* str = SvPV(query, len);
std::vector<ux::id_t> ret_ids;
size_t num_keys = THIS->commonPrefixSearch(str, len, ret_ids, limit);
if (num_keys == 0) {
XSRETURN_EMPTY;
}
EXTEND(SP, num_keys);
bool is_utf8 = SvUTF8(query);
for (size_t i = 0; i < num_keys; i++) {
std::string key = THIS->decodeKey(ret_ids[i]);
PUSHs(sv_2mortal(newSVpvn_utf8(key.c_str(), key.length(), is_utf8)));
}
XSRETURN(num_keys);
void
ux::Trie::predictive_search(SV* query, size_t limit = ux::LIMIT_DEFAULT)
PPCODE:
if (!SvOK(query)) {
XSRETURN_EMPTY;
}
STRLEN len;
char* str = SvPV(query, len);
std::vector<ux::id_t> ret_ids;
size_t num_keys = THIS->predictiveSearch(str, len, ret_ids, limit);
if (num_keys == 0) {
XSRETURN_EMPTY;
}
EXTEND(SP, num_keys);
bool is_utf8 = SvUTF8(query);
for (size_t i = 0; i < num_keys; i++) {
std::string key = THIS->decodeKey(ret_ids[i]);
PUSHs(sv_2mortal(newSVpvn_utf8(key.c_str(), key.length(), is_utf8)));
}
XSRETURN(num_keys);
size_t
ux::Trie::size()
void
ux::Trie::clear()
size_t
ux::Trie::alloc_size()
CODE:
RETVAL = THIS->getAllocSize();
OUTPUT:
RETVAL
std::string
ux::Trie::alloc_stat(size_t alloc_size)
CODE:
std::ostringstream os;
THIS->allocStat(alloc_size, os);
RETVAL = os.str();
OUTPUT:
RETVAL
std::string
ux::Trie::stat()
CODE:
std::ostringstream os;
THIS->stat(os);
RETVAL = os.str();
OUTPUT:
RETVAL
SV*
ux::Trie::gsub(SV* query, SV* callback)
CODE:
if (!SvOK(query)) {
XSRETURN_UNDEF;
}
if (!SvROK(callback) || SvTYPE(SvRV(callback)) != SVt_PVCV) {
croak("callback must be a CODE reference");
}
bool is_utf8 = SvUTF8(query);
SV* result = newSVpvs("");
char* head = SvPV_nolen(query);
char* tail = head + SvCUR(query);
size_t ret_len;
while (head < tail) {
if (THIS->prefixSearch(head, tail - head, ret_len) != ux::NOTFOUND) {
SV* str = newSVpvn_utf8(head, ret_len, is_utf8);
SV* ret = do_callback(aTHX_ callback, str);
if (SvOK(ret)) {
sv_catsv(result, ret);
}
SvREFCNT_dec(ret);
head += ret_len;
} else {
I32 len = is_utf8 ? UTF8SKIP(head) : 1;
sv_catpvn(result, head, len);
head += len;
}
}
RETVAL = result;
OUTPUT:
RETVAL
SV*
ux::Trie::decode_key(ux::id_t id)
CODE:
if (id >= THIS->size()) {
XSRETURN_UNDEF;
}
std::string key = THIS->decodeKey(id);
if (key.empty()) {
XSRETURN_UNDEF;
}
RETVAL = newSVpvn(key.c_str(), key.length());
OUTPUT:
RETVAL
SV*
ux::Trie::decode_key_utf8(ux::id_t id)
CODE:
if (id >= THIS->size()) {
XSRETURN_UNDEF;
}
std::string key = THIS->decodeKey(id);
if (key.empty()) {
XSRETURN_UNDEF;
}
RETVAL = newSVpvn_utf8(key.c_str(), key.length(), 1);
OUTPUT:
RETVAL
void
ux::Trie::DESTROY()