#ifndef PLCB_UTIL_H_
#define PLCB_UTIL_H_
/* This file contains various conversion functions and macros */
/*this stuff converts from SVs to int64_t (signed and unsigned) depending
on the perl*/
/*XXX:
12:54 < LeoNerd> mordy: Namely, my ( $hi, $lo ) = unpack "L> L>", $packed_64bit;
my $num = Math::BigInt->new( $hi ); $num >>= 32; $num |= $lo;
return $num;
12:55 < mordy> what about vice versa? or would i change my C interface to accept 2
32 bit integers and piece them back together there?
12:55 < LeoNerd> mordy: That might be simplest.
*/
#ifdef PLCB_PERL64
#define plcb_sv_to_u64(sv) SvUV(sv)
#define plcb_sv_to_64(sv) (int64_t)(plcb_sv_to_u64(sv))
#define plcb_sv_from_u64(sv, num) (sv_setuv(sv, num))
#define plcb_sv_from_u64_new(nump) newSVuv( (*nump) )
#define plcb_sv2cas(sv) (\
(SvIOK(sv)) \
? (uint64_t)(SvIVX(sv)) \
: (die("Expected valid (UV) cas. IOK not true"),-1) \
)
#else
static PERL_UNUSED_DECL uint64_t plcb_sv_to_u64(SV *in)
{
char *sv_blob;
STRLEN blob_len;
uint64_t ret;
if (SvIOK(in)) {
/*Numeric*/
return SvUV(in);
}
sv_blob = SvPV(in, blob_len);
if (blob_len != 8) {
die("expected 8-byte data string. Got %d", (int)blob_len);
}
ret = *(uint64_t*)sv_blob;
return ret;
}
#define plcb_sv_to_64(sv) ((int64_t)(plcb_sv_to_u64(sv)))
#define plcb_sv_from_u64(sv, num) \
(sv_setpvn(sv, (const char const*)&(num), 8))
#define plcb_sv_from_u64_new(nump) \
newSVpv((const char* const)(nump), 8)
/*Extract a packed 8 byte blob from an SV into a CAS value*/
#define plcb_sv2cas plcb_sv_to_u64
#endif /*PLCB_PERL64*/
/*assertively extract a non-null key from an SV, together with its length*/
#define plcb_get_str_or_die(ksv, charvar, lenvar, diespec) \
(void)(((charvar = SvPV(ksv, lenvar))) \
? ( (lenvar) ? charvar : (void*)die("Got zero-length %s", diespec) ) \
: (void*)die("Got NULL %s", diespec))
#define PLCB_TIME_ABS_OFFSET
#ifdef PLCB_TIME_ABS_OFFSET
#define PLCB_UEXP2EXP(cbexp, uexp, now) \
cbexp = ((uexp) \
? ((now) \
? (now + uexp) \
: (time(NULL)) + uexp) \
: 0)
#else
/*Memcached protocol states that a time offset greater than 30 days is taken
to be an epoch time. We hide this from perl by simply generating our own
epoch time based on the user's time*/
#define PLCB_UEXP2EXP(cbexp, uexp, now) \
cbexp = ((uexp) \
? ((uexp > (30*24*60*60)) \
? ((now) \
? (now + uexp) \
: (time(NULL) + uexp)) \
: uexp) \
: 0)
#endif
/**
* Extract the expiry value from an SV
*/
static PERL_UNUSED_DECL UV plcb_exp_from_sv(SV *exp)
{
UV ret = 0;
if (SvTYPE(exp) == SVt_NULL) {
return 0;
}
if (SvIOK(exp)) {
IV expiv = SvIVX(exp);
if (expiv < 0) {
die("Expiry cannot be negative");
}
ret = expiv;
} else if (SvPOK(exp)) {
UV exptype = grok_number(SvPVX(exp), SvCUR(exp), &ret);
if (!exptype) {
die("Bad expiry value");
}
if (exptype == IS_NUMBER_NEG) {
die("Expiry cannot be negative");
}
} else {
die("Bad type for expiry");
}
return ret;
}
#define plcb_is_arrayref(sv) (SvROK(sv) && SvTYPE(SvRV(sv)) == SVt_PVAV)
#define plcb_is_simple_string(sv) ( SvPOK(sv) != 0 && SvROK(sv) == 0 )
/* Handy types for XS */
/* for easy passing */
typedef struct {
struct PLCB_st *ptr;
SV *sv;
} PLCB_XS_OBJPAIR_t;
typedef struct {
SV *origsv;
char *base;
STRLEN len;
} PLCB_XS_STRING_t;
typedef PLCB_XS_STRING_t PLCB_XS_STRING_NONULL_t;
#endif /* PLCB_UTIL_H_ */