Crypt-SecretBuffer
view release on metacpan or search on metacpan
SecretBuffer.xs view on Meta::CPAN
}
#elif defined(HAVE_MINCORE)
#define CAN_SCAN_MEMORY 1
#include <sys/mman.h>
static bool is_page_accessible(uintptr_t addr) {
unsigned char vec;
return mincore((void*)addr, 1, &vec) == 0;
}
#else
#define CAN_SCAN_MEMORY 0
#endif
/* The rest only works if we have is_page_accessible */
#if CAN_SCAN_MEMORY
IV scan_mapped_memory_in_range(uintptr_t p, uintptr_t lim, const char *needle, size_t needle_len) {
size_t pagesize= get_page_size();
size_t count= 0;
void *at;
uintptr_t run_start = p, run_lim;
p = (p & ~(pagesize - 1)); /* round to nearest page, from here out */
while (p < lim) {
/* Skip pages that aren't mapped */
while (p < lim && !is_page_accessible(p)) {
p += pagesize;
run_start= p;
}
/* This page is mapped. Find the end of this mapped range, if it comes before lim */
while (p < lim && is_page_accessible(p)) {
p += pagesize;
}
run_lim= p < lim? p : lim;
/* Scan memory from run_start to run_lim */
while (run_start < run_lim && (at= memmem((void*)run_start, run_lim - run_start, needle, needle_len))) {
++count;
run_start= ((intptr_t)at) + needle_len;
}
}
return count;
}
#else
IV scan_mapped_memory_in_range(uintptr_t p, uintptr_t lim, const char *needle, size_t needle_len) {
return -1;
}
#endif
/**********************************************************************************************\
* Crypt::SecretBuffer API
\**********************************************************************************************/
MODULE = Crypt::SecretBuffer PACKAGE = Crypt::SecretBuffer
PROTOTYPES: DISABLE
void
new(...)
ALIAS:
Crypt::SecretBuffer::Exports::secret = 1
Crypt::SecretBuffer::Exports::secret_buffer = 2
INIT:
SV *buf_ref= NULL;
secret_buffer *buf= secret_buffer_new(0, &buf_ref);
int i, next_arg= ix == 0? 1 : 0;
PPCODE:
if (items - next_arg == 1) {
secret_buffer_splice_sv(buf, 0, 0, ST(next_arg));
}
else {
if ((items - next_arg) & 1)
croak("Odd number of arguments; expected (key => value) pairs");
for (i= next_arg; i < items-1; i += 2) {
SV *key= ST(i), *val= ST(i+1);
{
dSP;
ENTER;
SAVETMPS;
PUSHMARK(SP);
EXTEND(SP, 2);
PUSHs(buf_ref);
PUSHs(val);
PUTBACK;
call_method(SvPV_nolen(key), G_DISCARD);
FREETMPS;
LEAVE;
}
}
}
PUSHs(buf_ref);
void
append(buf, ...)
auto_secret_buffer buf
ALIAS:
assign = 1
INIT:
int i;
size_t from_ofs= (ix? 0 : buf->len), len_sum= 0;
U8 *write_pos;
secret_buffer_byte_range stack_ranges[16], *ranges= stack_ranges;
if ((items-1) > 16) {
Newx(ranges, (items-1), secret_buffer_byte_range);
SAVEFREEPV(ranges);
}
PPCODE:
for (i= 0; i < items-1; i++) {
ranges[i].ptr= secret_buffer_SvPVbyte(ST(i+1), &ranges[i].len);
len_sum += ranges[i].len;
}
secret_buffer_set_len(buf, from_ofs + len_sum);
write_pos= buf->data + from_ofs;
for (i= 0; i < items-1; i++) {
memcpy(write_pos, ranges[i].ptr, ranges[i].len);
write_pos += ranges[i].len;
}
XSRETURN(1); /* return self for chaining */
void
length(buf, val=NULL)
auto_secret_buffer buf
SV *val
ALIAS:
len = 1
PPCODE:
if (val) { /* writing */
IV ival= SvIV(val);
if (ival < 0) ival= 0;
secret_buffer_set_len(buf, ival);
/* return self, for chaining */
}
else /* reading */
ST(0)= sv_2mortal(newSViv(buf->len));
XSRETURN(1);
void
capacity(buf, val=NULL, flags= 0)
auto_secret_buffer buf
SV *val
secret_buffer_alloc_flags flags
PPCODE:
if (val) { /* wiritng */
IV ival= SvIV(val);
if (ival < 0) ival= 0;
if (flags & SECRET_BUFFER_AT_LEAST)
secret_buffer_alloc_at_least(buf, ival);
else
secret_buffer_realloc(buf, ival);
/* return self, for chaining */
}
else /* reading */
ST(0)= sv_2mortal(newSViv(buf->capacity));
XSRETURN(1);
void
clear(buf)
auto_secret_buffer buf
PPCODE:
secret_buffer_realloc(buf, 0);
XSRETURN(1); /* self, for chaining */
IV
index(buf, pattern, ofs_sv= &PL_sv_undef)
secret_buffer *buf
SV *pattern
SV *ofs_sv
ALIAS:
rindex = 1
INIT:
secret_buffer_parse parse;
size_t pos= 0, lim;
int flags= 0;
if (ix == 0) { // index (forward)
pos= normalize_offset(SvOK(ofs_sv)? SvIV(ofs_sv) : 0, buf->len);
lim= buf->len;
} else { // rindex (reverse)
IV max= normalize_offset(SvOK(ofs_sv)? SvIV(ofs_sv) : -1, buf->len);
flags= SECRET_BUFFER_MATCH_REVERSE;
// The ofs specifies the *start* of the match, not the maximum byte pos
// that could be part of the match. If pattern is a charset, add one to get 'lim',
// and if pattern is a string, add string byte length to get 'lim'.
if (SvRX(pattern))
lim= max + 1;
else {
STRLEN len; // needs to be byte count, so can't SvCUR without converting to bytes first
(void) SvPVbyte(pattern, len);
lim= max + len;
}
// re-clamp lim to end of buffer
if (lim > buf->len) lim= buf->len;
}
if (!secret_buffer_parse_init(&parse, buf, pos, lim, 0))
croak("%s", parse.error);
CODE:
if (secret_buffer_match(&parse, pattern, flags))
RETVAL= parse.pos - (U8*) buf->data;
else {
if (parse.error)
croak("%s", parse.error);
RETVAL= -1;
}
OUTPUT:
RETVAL
void
scan(buf, pattern, flags= 0, ofs= 0, len_sv= &PL_sv_undef)
secret_buffer *buf
SV *pattern
IV flags
IV ofs
SV *len_sv
INIT:
secret_buffer_parse parse;
// lim was captured as an SV so that undef can be used to indicate
// end of the buffer.
IV len= !SvOK(len_sv)? buf->len : SvIV(len_sv);
ofs= normalize_offset(ofs, buf->len);
if (!secret_buffer_parse_init(&parse, buf,
ofs, ofs + normalize_offset(len, buf->len - ofs),
(flags & SECRET_BUFFER_ENCODING_MASK)
))
croak("%s", parse.error);
PPCODE:
if (secret_buffer_match(&parse, pattern, flags)) {
PUSHs(sv_2mortal(newSViv(parse.pos - (U8*) buf->data)));
PUSHs(sv_2mortal(newSViv(parse.lim - parse.pos)));
}
else if (parse.error)
croak("%s", parse.error);
void
splice(buf, ofs, len, replacement)
secret_buffer *buf
IV ofs
IV len
SV *replacement
PPCODE:
/* normalize negative offset, and clamp to valid range */
ofs= normalize_offset(ofs, buf->len);
/* normalize negative count, and clamp to valid range */
len= normalize_offset(len, buf->len - ofs);
secret_buffer_splice_sv(buf, ofs, len, replacement);
XSRETURN(1); /* return $self */
void
substr(buf, ofs, count_sv=NULL, replacement=NULL)
secret_buffer *buf
IV ofs
SV *count_sv
SV *replacement
INIT:
unsigned char *sub_start;
secret_buffer *sub_buf= NULL;
SV *sub_ref= NULL;
IV count= count_sv? SvIV(count_sv) : buf->len;
PPCODE:
/* normalize negative offset, and clamp to valid range */
ofs= normalize_offset(ofs, buf->len);
/* normalize negative count, and clamp to valid range */
count= normalize_offset(count, buf->len - ofs);
sub_start= (unsigned char*) buf->data + ofs;
/* If called in non-void context, construct new secret from this range */
if (GIMME_V != G_VOID) {
SV **el;
sub_buf= secret_buffer_new(count, &sub_ref);
if (count) {
Copy(sub_start, sub_buf->data, count, unsigned char);
sub_buf->len= count;
}
/* inherit the stringify_mask */
el= hv_fetchs((HV*) SvRV(ST(0)), "stringify_mask", 0);
if (el && *el)
/* we know the hv isn't tied because we just created it, so no need to check success */
hv_stores((HV*) SvRV(sub_ref), "stringify_mask", newSVsv(*el));
}
/* modifying string? */
if (replacement)
secret_buffer_splice_sv(buf, ofs, count, replacement);
/* If void context, return nothing. Else return the substr */
if (!sub_ref)
XSRETURN(0);
else {
ST(0)= sub_ref; /* already mortal */
XSRETURN(1);
}
void
append_asn1_der_length(buf, val)
secret_buffer *buf
UV val
PPCODE:
secret_buffer_append_uv_asn1_der_length(buf, val);
void
append_base128le(buf, val)
secret_buffer *buf
UV val
PPCODE:
secret_buffer_append_uv_base128le(buf, val);
void
append_base128be(buf, val)
secret_buffer *buf
UV val
PPCODE:
secret_buffer_append_uv_base128be(buf, val);
void
append_lenprefixed(buf, ...)
secret_buffer *buf
INIT:
size_t bytes_needed= 0;
IV i;
PPCODE:
/* Add up all the lengths and over-estimate 9 bytes for each length specifier */
for (i= 1; i < items; i++) {
STRLEN len;
secret_buffer_SvPVbyte(ST(i), &len);
bytes_needed += 9 + len;
}
/* ensure space with one reallocation */
secret_buffer_alloc_at_least(buf, buf->len + bytes_needed);
/* append each length and span to the buffer */
for (i= 1; i < items; i++) {
STRLEN len;
size_t buf_pos;
const char *data= secret_buffer_SvPVbyte(ST(i), &len);
secret_buffer_append_uv_base128be(buf, len);
buf_pos= buf->len;
secret_buffer_set_len(buf, buf_pos + len);
memcpy(buf->data + buf_pos, data, len);
}
IV
memcmp(lhs, rhs, reverse=false)
SV *lhs
SV *rhs
bool reverse
ALIAS:
Crypt::SecretBuffer::Span::memcmp = 1
Crypt::SecretBuffer::Exports::memcmp = 2
INIT:
STRLEN lhs_len, rhs_len;
const char *lhs_buf= secret_buffer_SvPVbyte(lhs, &lhs_len);
const char *rhs_buf= secret_buffer_SvPVbyte(rhs, &rhs_len);
PERL_UNUSED_VAR(ix);
CODE:
/* constant-time loop */
{
volatile int ret= 0;
int i, common_len= lhs_len < rhs_len? lhs_len : rhs_len;
for (i = 0; i < common_len; i++)
if (lhs_buf[i] != rhs_buf[i] && !ret)
ret= lhs_buf[i] < rhs_buf[i]? -1 : 1;
if (ret == 0 && lhs_len != rhs_len)
ret= lhs_len < rhs_len? -1 : 1;
RETVAL= ret;
}
if (reverse)
RETVAL= -RETVAL;
OUTPUT:
RETVAL
UV
append_random(buf, count, flags=0)
auto_secret_buffer buf
UV count
secret_buffer_io_flags flags
CODE:
RETVAL= secret_buffer_append_random(buf, count, flags);
OUTPUT:
RETVAL
void
append_sysread(buf, handle, count)
auto_secret_buffer buf
PerlIO *handle
IV count
INIT:
IV got;
PPCODE:
got= secret_buffer_append_sysread(buf, handle, count);
if (got < 0)
XSRETURN_UNDEF;
else
PUSHs(sv_2mortal(newSViv(got)));
void
append_read(buf, handle, count)
auto_secret_buffer buf
PerlIO *handle
IV count
INIT:
int got;
PPCODE:
got= secret_buffer_append_read(buf, handle, count);
if (got < 0)
XSRETURN_UNDEF;
else
PUSHs(sv_2mortal(newSViv(got)));
void
_append_console_line(buf, handle)
auto_secret_buffer buf
PerlIO *handle
INIT:
int got;
PPCODE:
got= secret_buffer_append_console_line(buf, handle);
ST(0)= got == SECRET_BUFFER_GOTLINE? &PL_sv_yes
: got == SECRET_BUFFER_EOF? &PL_sv_no
: &PL_sv_undef;
XSRETURN(1);
void
syswrite(buf, io, count=buf->len, ofs=0)
secret_buffer *buf
PerlIO *io
IV ofs
IV count
INIT:
IV wrote;
PPCODE:
wrote= secret_buffer_syswrite(buf, io, ofs, count);
ST(0)= (wrote < 0)? &PL_sv_undef : sv_2mortal(newSViv(wrote));
XSRETURN(1);
void
write_async(buf, io, count=buf->len, ofs=0)
secret_buffer *buf
PerlIO *io
IV ofs
IV count
INIT:
IV wrote;
SV *ref_out= NULL;
PPCODE:
wrote= secret_buffer_write_async(buf, io, ofs, count, GIMME_V == G_VOID? NULL : &ref_out);
/* wrote == 0 means that it supplied a result promise object, which is already mortal.
* but avoid creating one when called in void context. */
ST(0)= wrote? sv_2mortal(newSViv(wrote)) : ref_out? ref_out : &PL_sv_undef;
XSRETURN(1);
void
stringify(buf, ...)
auto_secret_buffer buf
INIT:
SV **field= hv_fetch((HV*)SvRV(ST(0)), "stringify_mask", 14, 0);
PPCODE:
if (!field || !*field) {
ST(0)= sv_2mortal(newSVpvn("[REDACTED]", 10));
} else if (SvOK(*field)) {
ST(0)= *field;
} else {
ST(0)= secret_buffer_get_stringify_sv(buf);
}
XSRETURN(1);
void
unmask_to(buf, coderef)
secret_buffer *buf
SV *coderef
INIT:
int count= 0;
PPCODE:
PUSHMARK(SP);
EXTEND(SP, 1);
PUSHs(secret_buffer_get_stringify_sv(buf));
PUTBACK;
count= call_sv(coderef, GIMME_V);
SPAGAIN;
XSRETURN(count);
bool
_can_count_copies_in_process_memory()
CODE:
RETVAL= false;
OUTPUT:
RETVAL
IV
_count_matches_in_mem(buf, addr0, addr1)
secret_buffer *buf
UV addr0
UV addr1
CODE:
if (!buf->len)
croak("Empty buffer");
RETVAL= scan_mapped_memory_in_range(addr0, addr1, buf->data, buf->len);
if (RETVAL < 0)
croak("Unimplemented");
OUTPUT:
RETVAL
MODULE = Crypt::SecretBuffer PACKAGE = Crypt::SecretBuffer::Exports
void
unmask_secrets_to(coderef, ...)
SV *coderef
INIT:
int count= 0, i;
secret_buffer *buf= NULL;
PPCODE:
PUSHMARK(SP);
EXTEND(SP, items);
for (i= 1; i < items; i++) {
if (SvOK(ST(i)) && SvROK(ST(i)) && (buf= secret_buffer_from_magic(ST(i), 0)))
PUSHs(secret_buffer_get_stringify_sv(buf));
else
PUSHs(ST(i));
}
PUTBACK;
count= call_sv(coderef, GIMME_V);
SPAGAIN;
XSRETURN(count);
bool
_wait_fh_readable(handle, timeout_sv)
PerlIO *handle
SV *timeout_sv
CODE:
RETVAL= sb_wait_fh_readable(aTHX_ handle, timeout_sv);
OUTPUT:
RETVAL
void
_debug_charset(cset)
secret_buffer_charset *cset
INIT:
HV *hv;
PPCODE:
PUSHs(sv_2mortal((SV*)newRV_noinc((SV*)(hv= newHV()))));
hv_stores(hv, "bitmap", newSVpvn((char*)cset->bitmap, sizeof(cset->bitmap)));
hv_stores(hv, "unicode_above_7F", newSViv(cset->unicode_above_7F));
MODULE = Crypt::SecretBuffer PACKAGE = Crypt::SecretBuffer::AsyncResult
void
wait(result, timeout=-1)
secret_buffer_async_result *result
NV timeout
INIT:
IV os_err, bytes_written;
PPCODE:
if (secret_buffer_async_result_recv(result, (IV)(timeout*1000), &bytes_written, &os_err)) {
EXTEND(sp, 2);
ST(0)= sv_2mortal(newSViv(bytes_written));
ST(1)= sv_2mortal(newSViv(os_err));
XSRETURN(2);
} else {
XSRETURN(0);
}
MODULE = Crypt::SecretBuffer PACKAGE = Crypt::SecretBuffer::ConsoleState
void
new(pkg, ...)
const char *pkg;
ALIAS:
maybe_new = 1
INIT:
sb_console_state cstate, *magic_cstate;
PerlIO *handle= NULL;
SV *auto_restore= NULL;
SV *set_echo= NULL;
SV *set_line_input= NULL;
SV *objref;
bool already_set= true;
if (items == 2) {
IO *io = sv_2io(ST(1));
handle= io? IoIFP(io) : NULL;
} else if (items > 2) {
int i= 1;
if ((items - 1) & 1)
croak("expected even-length key/value attribute list");
for (; i < items; i+= 2) {
STRLEN len;
const char *name= SvPV(ST(i), len);
SV *val= ST(i+1);
if (len == 4 && memcmp(name, "echo", 4) == 0) {
if (SvOK(val)) set_echo= val;
}
else if (len == 6 && memcmp(name, "handle", 6) == 0) {
IO *io = sv_2io(val);
handle= io? IoIFP(io) : NULL;
}
else if (len == 10 && memcmp(name, "line_input", 10) == 0) {
if (SvOK(val)) set_line_input= val;
}
else if (len == 12 && memcmp(name, "auto_restore", 12) == 0) {
if (SvOK(val)) auto_restore= val;
}
else {
croak("Unknown option '%s'", name);
}
}
}
if (!handle)
croak("'handle' is required");
PPCODE:
ST(0)= &PL_sv_undef;
/* if it fails to initialize, return undef for 'maybe_new', else die */
if (!sb_console_state_init(aTHX_ &cstate, handle)) {
if (ix == 0)
croak("Can't read console/tty state");
XSRETURN(1);
}
if (auto_restore)
cstate.auto_restore= SvTRUE(auto_restore);
if (set_echo) {
bool enable= SvTRUE(set_echo);
if (sb_console_state_get_echo(&cstate) != enable) {
already_set= false;
if (!sb_console_state_set_echo(&cstate, enable))
croak("set echo = %d failed", (int)enable);
}
}
if (set_line_input) {
bool enable= SvTRUE(set_line_input);
if (sb_console_state_get_line_input(&cstate) != enable) {
already_set= false;
if (!sb_console_state_set_line_input(&cstate, enable))
croak("set echo = %d failed", (int)enable);
}
}
/* if user called 'maybe_new' and echo state aready matches requested
state, return undef. */
if (ix == 1 && (set_echo || set_line_input) && already_set)
XSRETURN(1);
/* new blessed ConsoleState object */
ST(0)= objref= sv_2mortal(newRV_noinc(&PL_sv_yes));
newSVrv(objref, pkg);
/* move cstate into MAGIC on object */
magic_cstate= secret_buffer_console_state_from_magic(objref, SECRET_BUFFER_MAGIC_AUTOCREATE);
*magic_cstate= cstate;
/* duplicate file handle in case user closes it */
sb_console_state_dup_fd(magic_cstate);
XSRETURN(1);
bool
echo(cstate, enable=NULL)
sb_console_state *cstate
SV *enable
CODE:
if (enable != NULL)
sb_console_state_set_echo(cstate, SvTRUE(enable));
RETVAL= sb_console_state_get_echo(cstate);
OUTPUT:
RETVAL
bool
line_input(cstate, enable=NULL)
sb_console_state *cstate
SV *enable
CODE:
if (enable != NULL)
sb_console_state_set_line_input(cstate, SvTRUE(enable));
RETVAL= sb_console_state_get_line_input(cstate);
OUTPUT:
RETVAL
SecretBuffer.xs view on Meta::CPAN
cstate->auto_restore= SvTRUE(enable);
RETVAL= cstate->auto_restore;
OUTPUT:
RETVAL
bool
restore(cstate)
sb_console_state *cstate
CODE:
RETVAL= sb_console_state_restore(cstate);
cstate->auto_restore= false; /* no longer run restore on destructor */
OUTPUT:
RETVAL
bool
wait_char_readable(cstate, timeout_sv=&PL_sv_undef)
sb_console_state *cstate
SV *timeout_sv
CODE:
RETVAL= sb_console_state_wait_char_readable(aTHX_ cstate, timeout_sv);
OUTPUT:
RETVAL
bool
_append_console_char(cstate, buf)
sb_console_state *cstate
secret_buffer *buf;
CODE:
#ifdef WIN32
RETVAL= sb_append_console_char(aTHX_ cstate, buf);
#else
RETVAL= false;
#endif
OUTPUT:
RETVAL
MODULE = Crypt::SecretBuffer PACKAGE = Crypt::SecretBuffer::Span
void
new(class_or_obj, ...)
SV *class_or_obj
ALIAS:
clone = 1
subspan = 2
Crypt::SecretBuffer::span = 3
Crypt::SecretBuffer::Exports::span = 4
INIT:
secret_buffer_span *span= secret_buffer_span_from_magic(class_or_obj, SECRET_BUFFER_MAGIC_UNDEF_OK);
SV **buf_field= span && SvTYPE(SvRV(class_or_obj)) == SVt_PVHV
? hv_fetchs((HV*)SvRV(class_or_obj), "buf", 0)
: NULL;
secret_buffer *buf= secret_buffer_from_magic(
buf_field? *buf_field : class_or_obj, SECRET_BUFFER_MAGIC_UNDEF_OK
);
bool subspan= span && ix >= 2;
IV base_pos= subspan? span->pos : 0;
IV pos=0, lim=0, len=0, base_lim=0;
int encoding= span? span->encoding : 0, i;
SV *encoding_sv= NULL;
bool have_pos= false, have_lim= false, have_len= false;
PPCODE:
//warn("items=%d span=%p buf=%p base_pos=%d", (int)items, span, buf, (int)base_pos);
// 3-argument form, only usable when first arg is a buffer or refs a buffer
if (buf && items >= 2 && looks_like_number(ST(1))) {
pos= SvIV(ST(1));
have_pos= true;
if (items >= 3 && SvOK(ST(2))) {
len= SvIV(ST(2));
have_len= true;
if (items >= 4) {
encoding_sv= ST(3);
if (items > 4)
warn("unexpected 4th argument after encoding");
}
}
} else { // key => value form
if ((items - 1) & 1)
croak("Odd number of arguments; expected (key => value, ...)");
for (i= 1; i < items-1; i += 2) {
if (0 == strcmp("pos", SvPV_nolen(ST(i)))) {
pos= SvIV(ST(i+1));
have_pos= true;
}
else if (0 == strcmp("lim", SvPV_nolen(ST(i)))) {
lim= SvIV(ST(i+1));
have_lim= true;
}
else if (0 == strcmp("len", SvPV_nolen(ST(i)))) {
len= SvIV(ST(i+1));
have_len= true;
}
else if (0 == strcmp("encoding", SvPV_nolen(ST(i)))) {
encoding_sv= ST(i+1);
}
else if (0 == strcmp("buf", SvPV_nolen(ST(i)))) {
buf= secret_buffer_from_magic(ST(i+1), SECRET_BUFFER_MAGIC_OR_DIE);
}
}
}
if (have_len && have_lim && (lim != pos + len))
croak("Can't specify both 'len' and 'lim', make up your mind!");
// buffer is required
if (!buf) {
/* The 'span()' exported function can accept plain scalars and upgrade them to a span object */
if (ix != 4)
croak("Require 'buf' attribute");
buf= secret_buffer_new(0, NULL);
secret_buffer_splice_sv(buf, 0, 0, class_or_obj);
}
base_lim= subspan? span->lim : buf->len;
// pos is relative to base_pos, and needs truncated to (or count backward from) base_lim
pos= have_pos? normalize_offset(pos, base_lim-base_pos)+base_pos
: span ? span->pos
: base_pos;
// likewise for lim, but also might need calculated from 'len'
lim= have_lim? normalize_offset(lim, base_lim-base_pos)+base_pos
: have_len? normalize_offset(len, base_lim-pos)+pos
: span ? span->lim
: base_lim;
if (pos > lim)
croak("lim must be greater or equal to pos");
//warn(" base_lim=%d pos=%d lim=%d", (int) base_lim, (int)pos, (int)lim);
// check encoding
if (encoding_sv) {
if (!parse_encoding(aTHX_ encoding_sv, &encoding))
croak("Unknown encoding '%s'", SvPV_nolen(encoding_sv));
}
PUSHs(new_mortal_span_obj(aTHX_ buf, pos, lim, encoding));
UV
pos(span, newval_sv= NULL)
secret_buffer_span *span
SV *newval_sv
ALIAS:
lim = 1
len = 2
length = 2
CODE:
if (newval_sv) {
IV newval= SvIV(newval_sv);
if (newval < 0)
croak("pos, lim, and len cannot be negative");
switch (ix) {
case 0: span->pos= newval; break;
case 1: if (newval < span->pos) croak("lim must be >= pos");
span->lim= newval; break;
case 2: span->lim= span->pos + newval;
default: croak("BUG");
}
}
RETVAL= ix == 0? span->pos
: ix == 1? span->lim
: ix == 2? span->lim - span->pos
: -1;
OUTPUT:
RETVAL
void
encoding(span, newval_sv= NULL)
secret_buffer_span *span
SV *newval_sv
INIT:
SV *enc_const;
AV *encodings= get_av("Crypt::SecretBuffer::_encodings", 0);
if (!encodings) croak("BUG");
PPCODE:
if (newval_sv)
if (!parse_encoding(aTHX_ newval_sv, &span->encoding))
croak("Invalid encoding");
enc_const= *av_fetch(encodings, span->encoding, 1);
if (!enc_const || !SvOK(enc_const))
croak("BUG");
PUSHs(enc_const);
const char *
last_error(span)
secret_buffer_span *span
CODE:
RETVAL= span->last_error;
OUTPUT:
RETVAL
void
set_up_us_the_bom(self)
SV *self
ALIAS:
consume_bom = 1
INIT:
secret_buffer_span *span= secret_buffer_span_from_magic(self, SECRET_BUFFER_MAGIC_OR_DIE);
secret_buffer_parse p;
if (!secret_buffer_parse_init_from_sv(&p, self))
croak("%s", p.error);
PERL_UNUSED_VAR(ix);
PPCODE:
if (p.lim - p.pos >= 3 && p.pos[0] == 0xEF && p.pos[1] == 0xBB && p.pos[2] == 0xBF) {
span->encoding= SECRET_BUFFER_ENCODING_UTF8;
span->pos += 3;
}
else if (p.lim - p.pos >= 2 && p.pos[0] == 0xFF && p.pos[1] == 0xFE) {
span->encoding= SECRET_BUFFER_ENCODING_UTF16LE;
span->pos += 2;
}
else if (p.lim - p.pos >= 2 && p.pos[0] == 0xFE && p.pos[1] == 0xFF) {
span->encoding= SECRET_BUFFER_ENCODING_UTF16BE;
span->pos += 2;
}
XSRETURN(1);
void
scan(self, pattern=NULL, flags= 0)
SV *self
SV *pattern
IV flags
ALIAS:
parse = 0x102
rparse = 0x203
trim = 0x322
ltrim = 0x422
rtrim = 0x523
starts_with = 0x612
ends_with = 0x713
INIT:
secret_buffer_span *span= secret_buffer_span_from_magic(self, SECRET_BUFFER_MAGIC_OR_DIE);
SV **sb_sv= hv_fetchs((HV*)SvRV(self), "buf", 1);
secret_buffer *buf= secret_buffer_from_magic(*sb_sv, SECRET_BUFFER_MAGIC_OR_DIE);
secret_buffer_parse parse;
if (!secret_buffer_parse_init(&parse, buf, span->pos, span->lim, span->encoding))
croak("%s", parse.error);
// Bit 0 indicates an op from the end of the buffer
if (ix & 1)
flags |= SECRET_BUFFER_MATCH_REVERSE;
// Bit 1 indicates an anchored op
if (ix & 2)
flags |= SECRET_BUFFER_MATCH_ANCHORED;
// Bits 4..7 indicate return type,
// 0 == return a span
// 1 == return bool
// 2 == return self
int ret_type= (ix >> 4) & 0xF;
if (ret_type != 1 && parse.encoding == SECRET_BUFFER_ENCODING_BASE64)
croak("Cannot perform parse, trim, or scan on base64 (pos / lim of result would not be whole bytes)");
int op= (ix >> 8);
bool matched;
if (!pattern) {
if (op == 3 || op == 4 || op == 5)
pattern= get_sv("Crypt::SecretBuffer::Span::default_trim_regex", 0);
if (!pattern)
croak("pattern is required");
}
PPCODE:
matched= secret_buffer_match(&parse, pattern, flags);
if (parse.error)
croak("%s", parse.error);
switch (op) {
case 1: // parse
if (matched) span->pos= parse.lim - (U8*) buf->data;
break;
case 2: // rparse
if (matched) span->lim= parse.pos - (U8*) buf->data;
break;
case 3: // trim
case 4: // ltrim
if (matched) span->pos= parse.lim - (U8*) buf->data;
if (op == 4) break;
// reset the modified parse_state and run in reverse
parse.pos= (U8*) (buf->data + span->pos);
parse.lim= (U8*) (buf->data + span->lim);
flags |= SECRET_BUFFER_MATCH_REVERSE;
matched= secret_buffer_match(&parse, pattern, flags);
case 5: // rtrim, and trim
if (matched) span->lim= parse.pos - (U8*) buf->data;
break;
default:
(void)0; // suppress warning that not all values were handled
}
if (ret_type == 0) {
if (!matched)
XSRETURN_UNDEF;
if (parse.pos > parse.lim || parse.lim > (U8*) buf->data + buf->len)
croak("BUG: parse pos=%p lim=%p buf.data=%p buf.len=%ld",
parse.pos, parse.lim, buf->data, (long)buf->len);
PUSHs(new_mortal_span_obj(aTHX_ buf, parse.pos - (U8*) buf->data, parse.lim - (U8*) buf->data, span->encoding));
} else if (ret_type == 1) {
if (matched)
XSRETURN_YES;
else
XSRETURN_NO;
}
else {
XSRETURN(1); // use self pointer in ST(0)
}
void
parse_lenprefixed(self, count = 1)
SV *self
IV count
INIT:
secret_buffer_span *span= secret_buffer_span_from_magic(self, SECRET_BUFFER_MAGIC_OR_DIE);
secret_buffer_parse p;
UV len;
size_t ofs;
/* treat an invalid span as a bug, rather than returning it to the user in the err_out param */
if (!secret_buffer_parse_init_from_sv(&p, self))
croak("%s", p.error);
PPCODE:
while (count && p.pos < p.lim) {
if (!secret_buffer_parse_uv_base128be(&p, &len)) {
span->last_error= p.error;
XSRETURN_EMPTY;
}
if (len > p.lim - p.pos) {
span->last_error= "Length exceeds end of Span";
XSRETURN_EMPTY;
}
ofs= p.pos - (U8*) p.sbuf->data;
XPUSHs(secret_buffer_span_new_obj(p.sbuf, ofs, ofs + len, 0));
p.pos += len;
if (count > 0) --count;
}
span->pos= p.pos - (U8*) p.sbuf->data;
span->last_error= NULL;
SV*
parse_asn1_der_length(self)
SV *self
ALIAS:
parse_base128le = 1
parse_base128be = 2
INIT:
secret_buffer_span *span= secret_buffer_span_from_magic(self, SECRET_BUFFER_MAGIC_OR_DIE);
secret_buffer_parse p;
UV val_out;
bool success;
/* treat an invalid span as a bug, rather than returning it to the user as last_error */
if (!secret_buffer_parse_init_from_sv(&p, self))
croak("%s", p.error);
CODE:
switch (ix) {
case 0 : success= secret_buffer_parse_uv_asn1_der_length(&p, &val_out); break;
case 1 : success= secret_buffer_parse_uv_base128le(&p, &val_out); break;
case 2 : success= secret_buffer_parse_uv_base128be(&p, &val_out); break;
default: croak("BUG");
}
if (success) {
/* advance the span position */
span->pos= p.pos - (U8*) p.sbuf->data;
span->last_error= NULL;
RETVAL= newSVuv(val_out);
} else {
span->last_error= p.error;
RETVAL= &PL_sv_undef;
}
OUTPUT:
RETVAL
void
copy(self, ...)
SV *self
ALIAS:
copy_to = 1
append_to = 2
INIT:
SV *dst_sv= NULL;
int next_arg, dst_encoding= -1;
secret_buffer_parse src;
if (!secret_buffer_parse_init_from_sv(&src, self))
croak("%s", src.error);
PPCODE:
if (ix > 0) { /* called as 'copy_to' or 'append_to' */
if (items < 2)
croak("Missing copy/append destination");
dst_sv= ST(1);
next_arg= 2;
}
else { /* called as 'copy' */
secret_buffer_new(0, &dst_sv);
next_arg= 1;
}
// parse options
if ((items - next_arg) & 1)
croak("expected even-length list of (key => val)");
for (; next_arg < items; next_arg+= 2) {
if (0 == strcmp(SvPV_nolen(ST(next_arg)), "encoding")) {
if (!parse_encoding(aTHX_ ST(next_arg+1), &dst_encoding))
croak("Unknown encoding");
}
}
if (!secret_buffer_copy_to(&src, dst_sv, dst_encoding, ix == 2))
croak("copy failed: %s", src.error);
// copy returns the SecretBuffer, but copy_to returns empty list.
if (ix == 0)
PUSHs(dst_sv);
IV
cmp(lhs, rhs, reverse=false)
SV *lhs
SV *rhs
bool reverse
INIT:
secret_buffer_parse lhs_parse, rhs_parse;
if (!secret_buffer_parse_init_from_sv(&lhs_parse, lhs))
croak("%s", lhs_parse.error);
if (!secret_buffer_parse_init_from_sv(&rhs_parse, rhs))
croak("%s", rhs_parse.error);
CODE:
RETVAL= sb_parse_codepointcmp(&lhs_parse, &rhs_parse);
if (reverse)
RETVAL= -RETVAL;
OUTPUT:
RETVAL
BOOT:
int i;
HV *stash= gv_stashpvs("Crypt::SecretBuffer", 1);
HV *exports_stash= gv_stashpvs("Crypt::SecretBuffer::Exports", 1);
#define EXPORT_CONST(name, const) \
newCONSTSUB(stash, name, make_enum_dualvar(aTHX_ const, newSVpvs_share(name)));\
hv_stores(exports_stash, name, SvREFCNT_inc(*hv_fetchs(stash, name, 0)))
EXPORT_CONST("NONBLOCK", SECRET_BUFFER_NONBLOCK);
EXPORT_CONST("AT_LEAST", SECRET_BUFFER_AT_LEAST);
EXPORT_CONST("MATCH_MULTI", SECRET_BUFFER_MATCH_MULTI);
EXPORT_CONST("MATCH_REVERSE", SECRET_BUFFER_MATCH_REVERSE);
EXPORT_CONST("MATCH_NEGATE", SECRET_BUFFER_MATCH_NEGATE);
EXPORT_CONST("MATCH_ANCHORED",SECRET_BUFFER_MATCH_ANCHORED);
EXPORT_CONST("MATCH_CONST_TIME",SECRET_BUFFER_MATCH_CONST_TIME);
#undef EXPORT_CONST
SV *enc[SECRET_BUFFER_ENCODING_MAX+1];
( run in 0.937 second using v1.01-cache-2.11-cpan-71847e10f99 )