File Coverage

SecretBuffer.xs
Criterion Covered Total %
statement 467 588 79.4
branch 326 506 64.4
condition n/a
subroutine n/a
pod n/a
total 793 1094 72.4


line stmt bran cond sub pod time code
1             #define PERL_NO_GET_CONTEXT
2             #include "EXTERN.h"
3             #include "perl.h"
4             #include "XSUB.h"
5             #define NEED_mg_findext
6             #define NEED_newSVpvn_share
7             #define NEED_SvRX
8             #define NEED_RX_PRECOMP
9             #define NEED_RX_PRELEN
10             #include "ppport.h"
11             /* these weren't supplied by ppport.h */
12             #ifndef RX_PRECOMP
13             #define RX_PRECOMP(rx) ((rx)->precomp)
14             #define RX_PRELEN(rx) ((rx)->prelen)
15             #endif
16              
17             #include "SecretBuffer_config.h"
18              
19             #ifndef HAVE_BOOL
20             #define bool int
21             #define true 1
22             #define false 0
23             #endif
24              
25             #include "SecretBuffer.h"
26             #include "SecretBufferManualLinkage.h"
27              
28             typedef struct secret_buffer_span {
29             size_t pos, lim;
30             int encoding;
31             const char *last_error;
32             } secret_buffer_span;
33              
34             // For typemap
35             typedef secret_buffer_span *auto_secret_buffer_span;
36             int sb_parse_codepointcmp(secret_buffer_parse *lhs, secret_buffer_parse *rhs);
37              
38             /**********************************************************************************************\
39             * XS Utils
40             \**********************************************************************************************/
41              
42             /* Common perl idioms for negative offset or negative count meaning a position
43             * measured backward from the end.
44             */
45 402           static inline IV normalize_offset(IV ofs, IV len) {
46 402 100         if (ofs < 0) {
47 20           ofs += len;
48 20 50         if (ofs < 0)
49 0           ofs= 0;
50             }
51 382 100         else if (ofs > len)
52 10           ofs= len;
53 402           return ofs;
54             }
55              
56             /* For exported constant dualvars */
57             #define EXPORT_ENUM(x) newCONSTSUB(stash, #x, make_enum_dualvar(aTHX_ x, newSVpvs_share(#x)))
58 1150           static SV * make_enum_dualvar(pTHX_ IV ival, SV *name) {
59 1150 50         SvUPGRADE(name, SVt_PVNV);
60 1150           SvIV_set(name, ival);
61 1150           SvIOK_on(name);
62 1150           SvREADONLY_on(name);
63 1150           return name;
64             }
65              
66             /**********************************************************************************************\
67             * Platform compatibility stuff
68             \**********************************************************************************************/
69              
70             #ifdef WIN32
71              
72             static size_t get_page_size() {
73             SYSTEM_INFO sysInfo;
74             GetSystemInfo(&sysInfo);
75             return sysInfo.dwPageSize;
76             }
77              
78             typedef DWORD syserror_type;
79             #define GET_SYSERROR(x) ((x)= GetLastError())
80             #define SET_SYSERROR(x) SetLastError(x)
81              
82             static void croak_with_syserror(const char *prefix, DWORD error_code) {
83             char message_buffer[512];
84             DWORD length;
85              
86             length = FormatMessageA(
87             FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
88             NULL,
89             error_code,
90             MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
91             message_buffer,
92             sizeof(message_buffer),
93             NULL
94             );
95              
96             if (length)
97             croak("%s: %s (%lu)", prefix, message_buffer, error_code);
98             else
99             croak("%s: %lu", prefix, error_code);
100             }
101              
102             #else /* not WIN32 */
103              
104 125           static size_t get_page_size() {
105 125           long pagesize = sysconf(_SC_PAGESIZE);
106 125 50         return (pagesize < 0)? 4096 : pagesize;
107             }
108              
109             #define GET_SYSERROR(x) ((x)= errno)
110             #define SET_SYSERROR(x) (errno= (x))
111             typedef int syserror_type;
112              
113             #define croak_with_syserror(msg, err) croak("%s: %s", msg, strerror(err))
114              
115             #endif
116              
117             /* Shim for systems that lack memmem */
118             #ifndef HAVE_MEMMEM
119             static void* memmem(
120             const void *haystack, size_t haystacklen,
121             const void *needle, size_t needlelen
122             ) {
123             if (!needle || !needlelen) {
124             return (void*) haystack;
125             }
126             else if (!haystack || !haystacklen) {
127             return NULL;
128             }
129             else {
130             const char *p= (const char*) haystack;
131             const char *lim= p + haystacklen - (needlelen - 1);
132             char first_ch= *(char*)needle;
133             while (p < lim) {
134             if (*p == first_ch) {
135             if (memcmp(p, needle, needlelen) == 0)
136             return (void*)p;
137             }
138             ++p;
139             }
140             }
141             return NULL;
142             }
143             #endif /* HAVE_MEMMEM */
144              
145             /**********************************************************************************************\
146             * MAGIC vtables
147             \**********************************************************************************************/
148              
149             #ifdef USE_ITHREADS
150             static int secret_buffer_magic_dup(pTHX_ MAGIC *mg, CLONE_PARAMS *param);
151             static int secret_buffer_stringify_magic_dup(pTHX_ MAGIC *mg, CLONE_PARAMS *params);
152             static int secret_buffer_async_result_magic_dup(pTHX_ MAGIC *mg, CLONE_PARAMS *params);
153             static int secret_buffer_span_magic_dup(pTHX_ MAGIC *mg, CLONE_PARAMS *params);
154             #else
155             #define secret_buffer_magic_dup 0
156             #define secret_buffer_stringify_magic_dup 0
157             #define secret_buffer_async_result_magic_dup 0
158             #define secret_buffer_span_magic_dup 0
159             #endif
160              
161             static int secret_buffer_magic_free(pTHX_ SV *sv, MAGIC *mg);
162             static MGVTBL secret_buffer_magic_vtbl = {
163             NULL, NULL, NULL, NULL,
164             secret_buffer_magic_free,
165             NULL,
166             secret_buffer_magic_dup
167             #ifdef MGf_LOCAL
168             ,NULL
169             #endif
170             };
171              
172             static int secret_buffer_stringify_magic_get(pTHX_ SV *sv, MAGIC *mg);
173             static int secret_buffer_stringify_magic_set(pTHX_ SV *sv, MAGIC *mg);
174             static int secret_buffer_stringify_magic_free(pTHX_ SV *sv, MAGIC *mg);
175             static MGVTBL secret_buffer_stringify_magic_vtbl = {
176             secret_buffer_stringify_magic_get,
177             secret_buffer_stringify_magic_set,
178             NULL, NULL,
179             secret_buffer_stringify_magic_free,
180             NULL,
181             secret_buffer_stringify_magic_dup
182             #ifdef MGf_LOCAL
183             ,NULL
184             #endif
185             };
186              
187             static int secret_buffer_async_result_magic_free(pTHX_ SV *sv, MAGIC *mg);
188             static MGVTBL secret_buffer_async_result_magic_vtbl = {
189             NULL, NULL, NULL, NULL,
190             secret_buffer_async_result_magic_free,
191             NULL,
192             secret_buffer_async_result_magic_dup
193             #ifdef MGf_LOCAL
194             ,NULL
195             #endif
196             };
197              
198             static int secret_buffer_span_magic_free(pTHX_ SV *sv, MAGIC *mg);
199             static MGVTBL secret_buffer_span_magic_vtbl = {
200             NULL, NULL, NULL, NULL,
201             secret_buffer_span_magic_free,
202             NULL,
203             secret_buffer_span_magic_dup
204             #ifdef MGf_LOCAL
205             ,NULL
206             #endif
207             };
208              
209             typedef void* secret_buffer_X_auto_ctor(pTHX_ SV *owner);
210 11224           void* secret_buffer_X_from_magic(pTHX_ SV *obj, int flags,
211             const MGVTBL *mg_vtbl, const char *mg_desc,
212             secret_buffer_X_auto_ctor *auto_ctor
213             ) {
214             SV *sv;
215             MAGIC *magic;
216              
217 11224 50         if ((!obj || !SvOK(obj)) && (flags & SECRET_BUFFER_MAGIC_UNDEF_OK))
    50          
    0          
218 0           return NULL;
219              
220 11224 100         if (!sv_isobject(obj)) {
221 1510 50         if (flags & SECRET_BUFFER_MAGIC_OR_DIE)
222 0           croak("Not an object");
223 1510           return NULL;
224             }
225 9714           sv = SvRV(obj);
226 9714 100         if (SvMAGICAL(sv) && (magic = mg_findext(sv, PERL_MAGIC_ext, mg_vtbl)))
    100          
227 7836           return magic->mg_ptr;
228              
229 1878 100         if (flags & SECRET_BUFFER_MAGIC_AUTOCREATE && auto_ctor) {
    50          
230 1363           void *data= auto_ctor(aTHX_ sv);
231 1363           magic = sv_magicext(sv, NULL, PERL_MAGIC_ext, mg_vtbl, (const char*) data, 0);
232             #ifdef USE_ITHREADS
233             magic->mg_flags |= MGf_DUP;
234             #endif
235 1363           return data;
236             }
237 515 50         if (flags & SECRET_BUFFER_MAGIC_OR_DIE)
238 0           croak("Object lacks '%s' magic", mg_desc);
239 515           return NULL;
240             }
241              
242             static secret_buffer_span* secret_buffer_span_from_magic(SV *objref, int flags);
243              
244             #include "secret_buffer_base.c"
245             #include "secret_buffer_console.c"
246             #include "secret_buffer_async_write.c"
247             #include "secret_buffer_charset.c"
248             #include "secret_buffer_parse.c"
249             #include "secret_buffer_span.c"
250              
251             /**********************************************************************************************\
252             * SecretBuffer magic
253             \**********************************************************************************************/
254              
255             /*
256             * SecretBuffer Magic
257             */
258              
259 566           int secret_buffer_magic_free(pTHX_ SV *sv, MAGIC *mg) {
260 566           secret_buffer *buf= (secret_buffer*) mg->mg_ptr;
261 566 50         if (buf) {
262 566           secret_buffer_realloc(buf, 0);
263 566 100         if (buf->stringify_sv)
264 31           sv_2mortal(buf->stringify_sv);
265 566           Safefree(mg->mg_ptr);
266 566           mg->mg_ptr = NULL;
267             }
268 566           return 0;
269             }
270              
271             #ifdef USE_ITHREADS
272             int secret_buffer_magic_dup(pTHX_ MAGIC *mg, CLONE_PARAMS *param) {
273             secret_buffer *clone, *orig = (secret_buffer *)mg->mg_ptr;
274             PERL_UNUSED_VAR(param);
275             Newxz(clone, 1, secret_buffer);
276             clone->wrapper= mg->mg_obj;
277             mg->mg_ptr = (char *)clone;
278             secret_buffer_realloc(clone, orig->capacity);
279             if (orig->capacity)
280             memcpy(clone->data, orig->data, orig->capacity);
281             clone->len= orig->len;
282             return 0;
283             }
284             #endif
285              
286             /* Aliases for typemap */
287             typedef secret_buffer *auto_secret_buffer;
288             typedef secret_buffer *maybe_secret_buffer;
289              
290             /*
291             * SecretBuffer stringify SV magic
292             */
293              
294 65           int secret_buffer_stringify_magic_get(pTHX_ SV *sv, MAGIC *mg) {
295 65           secret_buffer *buf= (secret_buffer *)mg->mg_ptr;
296             assert(buf->stringify_sv == sv);
297 65 100         if (buf->data) {
298             /* Perl SvPV requires there to be a NUL byte beyond the reported length of the SV */
299 64 50         if (buf->capacity <= buf->len) {
300 0           secret_buffer_alloc_at_least(buf, buf->len+1);
301 0           buf->data[buf->len]= 0;
302             }
303 64           SvPVX(sv)= buf->data;
304 64           SvCUR(sv)= buf->len;
305             } else {
306 1           SvPVX(sv)= "";
307 1           SvCUR(sv)= 0;
308             }
309 65           SvPOK_on(sv);
310 65           SvUTF8_off(sv);
311 65           SvREADONLY_on(sv);
312 65           return 0;
313             }
314              
315 0           int secret_buffer_stringify_magic_set(pTHX_ SV *sv, MAGIC *mg) {
316 0           warn("Attempt to assign stringify scalar");
317 0           return 0;
318             }
319              
320 31           int secret_buffer_stringify_magic_free(pTHX_ SV *sv, MAGIC *mg) {
321             /* warn("Freeing stringify scalar"); */
322 31           return 0;
323             }
324              
325             #ifdef USE_ITHREADS
326             int secret_buffer_stringify_magic_dup(pTHX_ MAGIC *mg, CLONE_PARAMS *param) {
327             croak("Can't dup stringify_sv");
328             }
329             #endif
330              
331 54           SV* secret_buffer_get_stringify_sv(secret_buffer *buf) {
332             dTHX;
333             MAGIC mg;
334 54           SV *sv= buf->stringify_sv;
335 54 100         if (!sv) {
336 31           sv= buf->stringify_sv= newSV(0);
337 31           sv_magicext(sv, NULL, PERL_MAGIC_ext, &secret_buffer_stringify_magic_vtbl, (const char *)buf, 0);
338             #ifdef USE_ITHREADS
339             /* magic->mg_flags |= MGf_DUP; it doesn't support duplication, so does the flag need set? */
340             #endif
341             }
342             /* Run the get magic immediately in case caller forgets */
343 54           mg.mg_ptr= (char*) buf;
344 54           secret_buffer_stringify_magic_get(aTHX_ sv, &mg);
345 54           return sv;
346             }
347              
348             /* flag for capacity */
349             #define SECRET_BUFFER_AT_LEAST 1
350              
351             /* Convenience to convert string parameters to the corresponding integer so that Perl-side
352             * doesn't always need to import the flag constants.
353             */
354 1           static IV parse_io_flags(pTHX_ SV *sv) {
355 1 50         if (!sv || !SvOK(sv))
    50          
356 0           return 0;
357 1 50         if (SvIOK(sv))
358 1           return SvIV(sv);
359 0 0         if (SvPOK(sv)) {
360 0           const char *str= SvPV_nolen(sv);
361 0 0         if (!str[0]) return 0;
362 0 0         if (strcmp(str, "NONBLOCK") == 0) return SECRET_BUFFER_NONBLOCK;
363             }
364 0           croak("Unknown flag %s", SvPV_nolen(sv));
365             }
366              
367 2           static IV parse_alloc_flags(pTHX_ SV *sv) {
368 2 50         if (!sv || !SvOK(sv))
    50          
369 0           return 0;
370 2 50         if (SvIOK(sv))
371 0           return SvIV(sv);
372 2 50         if (SvPOK(sv)) {
373 2           const char *str= SvPV_nolen(sv);
374 2 50         if (!str[0]) return 0;
375 2 50         if (strcmp(str, "AT_LEAST") == 0) return SECRET_BUFFER_AT_LEAST;
376             }
377 0           croak("Unknown flag %s", SvPV_nolen(sv));
378             }
379              
380             /* encoding-type for Span->parse_lenprefixed */
381             #define BASE128LE 1
382             #define BASE128BE 2
383             #define ASN1_DER_LENGTH 3
384              
385 0           static IV parse_lenprefixed_type(pTHX_ SV *sv) {
386 0 0         if (sv && SvIOK(sv)) {
    0          
387 0           IV val= SvIV(sv);
388 0 0         if (val < 1 || val > 3)
    0          
389 0           croak("Unknown encoding type %ld", (long)val);
390 0           return val;
391 0 0         } else if (sv && SvOK(sv)) {
    0          
392             STRLEN len;
393 0           const char *str= SvPV(sv, len);
394 0 0         if (strcmp(str, "BASE128LE") == 0) return BASE128LE;
395 0 0         if (strcmp(str, "BASE128BE") == 0) return BASE128BE;
396 0 0         if (strcmp(str, "ASN1_DER_LENGTH") == 0) return ASN1_DER_LENGTH;
397 0           croak("Unknown encoding type '%s'", str);
398             }
399 0           }
400              
401             /* for typemap to automatically convert flags */
402             typedef int secret_buffer_io_flags;
403             typedef int secret_buffer_alloc_flags;
404             typedef int secret_buffer_lenprefixed_type;
405              
406             typedef sb_console_state maybe_console_state;
407             typedef sb_console_state auto_console_state;
408              
409             /**********************************************************************************************\
410             * Debug helpers
411             \**********************************************************************************************/
412              
413             /* Helper function to check if a memory page is accessible (committed and readable) */
414             #if defined(WIN32)
415             #define CAN_SCAN_MEMORY 1
416             static bool is_page_accessible(uintptr_t addr) {
417             MEMORY_BASIC_INFORMATION memInfo;
418             if (VirtualQuery((LPCVOID)addr, &memInfo, sizeof(memInfo)) == 0)
419             return FALSE;
420             return (memInfo.State == MEM_COMMIT) &&
421             (memInfo.Protect & (PAGE_READONLY | PAGE_READWRITE | PAGE_EXECUTE_READ | PAGE_EXECUTE_READWRITE));
422             }
423             #elif defined(HAVE_MINCORE)
424             #define CAN_SCAN_MEMORY 1
425             #include
426 28763           static bool is_page_accessible(uintptr_t addr) {
427             unsigned char vec;
428 28763           return mincore((void*)addr, 1, &vec) == 0;
429             }
430             #else
431             #define CAN_SCAN_MEMORY 0
432             #endif
433              
434             /* The rest only works if we have is_page_accessible */
435             #if CAN_SCAN_MEMORY
436 125           IV scan_mapped_memory_in_range(uintptr_t p, uintptr_t lim, const char *needle, size_t needle_len) {
437 125           size_t pagesize= get_page_size();
438 125           size_t count= 0;
439             void *at;
440 125           uintptr_t run_start = p, run_lim;
441 125           p = (p & ~(pagesize - 1)); /* round to nearest page, from here out */
442 249 100         while (p < lim) {
443             /* Skip pages that aren't mapped */
444 124 50         while (p < lim && !is_page_accessible(p)) {
    50          
445 0           p += pagesize;
446 0           run_start= p;
447             }
448             /* This page is mapped. Find the end of this mapped range, if it comes before lim */
449 28763 100         while (p < lim && is_page_accessible(p)) {
    50          
450 28639           p += pagesize;
451             }
452 124           run_lim= p < lim? p : lim;
453             /* Scan memory from run_start to run_lim */
454 131 50         while (run_start < run_lim && (at= memmem((void*)run_start, run_lim - run_start, needle, needle_len))) {
    100          
455 7           ++count;
456 7           run_start= ((intptr_t)at) + needle_len;
457             }
458             }
459 125           return count;
460             }
461             #else
462             IV scan_mapped_memory_in_range(uintptr_t p, uintptr_t lim, const char *needle, size_t needle_len) {
463             return -1;
464             }
465             #endif
466              
467             /**********************************************************************************************\
468             * Crypt::SecretBuffer API
469             \**********************************************************************************************/
470             MODULE = Crypt::SecretBuffer PACKAGE = Crypt::SecretBuffer
471             PROTOTYPES: DISABLE
472              
473             void
474             new(...)
475             ALIAS:
476             Crypt::SecretBuffer::Exports::secret = 1
477             Crypt::SecretBuffer::Exports::secret_buffer = 2
478             INIT:
479 533           SV *buf_ref= NULL;
480 533           secret_buffer *buf= secret_buffer_new(0, &buf_ref);
481 533           int i, next_arg= ix == 0? 1 : 0;
482             PPCODE:
483 533 100         if (items - next_arg == 1) {
484 110           secret_buffer_splice_sv(buf, 0, 0, ST(next_arg));
485             }
486             else {
487 423 50         if ((items - next_arg) & 1)
488 0           croak("Odd number of arguments; expected (key => value) pairs");
489 428 100         for (i= next_arg; i < items-1; i += 2) {
490 5           SV *key= ST(i), *val= ST(i+1);
491             {
492 5           dSP;
493 5           ENTER;
494 5           SAVETMPS;
495 5 50         PUSHMARK(SP);
496 5 50         EXTEND(SP, 2);
497 5           PUSHs(buf_ref);
498 5           PUSHs(val);
499 5           PUTBACK;
500 5           call_method(SvPV_nolen(key), G_DISCARD);
501 5 50         FREETMPS;
502 5           LEAVE;
503             }
504             }
505             }
506 533           PUSHs(buf_ref);
507              
508             void
509             append(buf, source= NULL)
510             auto_secret_buffer buf
511             SV *source;
512             ALIAS:
513             assign = 1
514             PPCODE:
515 30 100         secret_buffer_splice_sv(buf, ix? 0 : buf->len, ix? buf->len : 0, source);
    100          
516 30           XSRETURN(1); /* return self for chaining */
517              
518             void
519             length(buf, val=NULL)
520             auto_secret_buffer buf
521             SV *val
522             PPCODE:
523 71 100         if (val) { /* writing */
524 4           IV ival= SvIV(val);
525 4 50         if (ival < 0) ival= 0;
526 4           secret_buffer_set_len(buf, ival);
527             /* return self, for chaining */
528             }
529             else /* reading */
530 67           ST(0)= sv_2mortal(newSViv(buf->len));
531 71           XSRETURN(1);
532              
533             void
534             capacity(buf, val=NULL, flags= 0)
535             auto_secret_buffer buf
536             SV *val
537             secret_buffer_alloc_flags flags
538             PPCODE:
539 8 100         if (val) { /* wiritng */
540 4           IV ival= SvIV(val);
541 4 50         if (ival < 0) ival= 0;
542 4 100         if (flags & SECRET_BUFFER_AT_LEAST)
543 2           secret_buffer_alloc_at_least(buf, ival);
544             else
545 2           secret_buffer_realloc(buf, ival);
546             /* return self, for chaining */
547             }
548             else /* reading */
549 4           ST(0)= sv_2mortal(newSViv(buf->capacity));
550 8           XSRETURN(1);
551              
552             void
553             clear(buf)
554             auto_secret_buffer buf
555             PPCODE:
556 5           secret_buffer_realloc(buf, 0);
557 5           XSRETURN(1); /* self, for chaining */
558              
559             IV
560             index(buf, pattern, ofs_sv= &PL_sv_undef)
561             secret_buffer *buf
562             SV *pattern
563             SV *ofs_sv
564             ALIAS:
565             rindex = 1
566             INIT:
567             secret_buffer_parse parse;
568 29           size_t pos= 0, lim;
569 29           int flags= 0;
570 29 100         if (ix == 0) { // index (forward)
571 20 100         pos= normalize_offset(SvOK(ofs_sv)? SvIV(ofs_sv) : 0, buf->len);
572 20           lim= buf->len;
573             } else { // rindex (reverse)
574 9 100         IV max= normalize_offset(SvOK(ofs_sv)? SvIV(ofs_sv) : -1, buf->len);
575 9           flags= SECRET_BUFFER_MATCH_REVERSE;
576             // The ofs specifies the *start* of the match, not the maximum byte pos
577             // that could be part of the match. If pattern is a charset, add one to get 'lim',
578             // and if pattern is a string, add string byte length to get 'lim'.
579 9 100         if (SvRX(pattern))
580 2           lim= max + 1;
581             else {
582             STRLEN len; // needs to be byte count, so can't SvCUR without converting to bytes first
583 7           (void) SvPVbyte(pattern, len);
584 7           lim= max + len;
585             }
586             // re-clamp lim to end of buffer
587 9 100         if (lim > buf->len) lim= buf->len;
588             }
589 29 50         if (!secret_buffer_parse_init(&parse, buf, pos, lim, 0))
590 0           croak("%s", parse.error);
591             CODE:
592 29 100         if (secret_buffer_match(&parse, pattern, flags))
593 24           RETVAL= parse.pos - (U8*) buf->data;
594             else {
595 5 50         if (parse.error)
596 0           croak("%s", parse.error);
597 5           RETVAL= -1;
598             }
599             OUTPUT:
600             RETVAL
601              
602             void
603             scan(buf, pattern, flags= 0, ofs= 0, len_sv= &PL_sv_undef)
604             secret_buffer *buf
605             SV *pattern
606             IV flags
607             IV ofs
608             SV *len_sv
609             INIT:
610             secret_buffer_parse parse;
611             // lim was captured as an SV so that undef can be used to indicate
612             // end of the buffer.
613 106 50         IV len= !SvOK(len_sv)? buf->len : SvIV(len_sv);
614 106           ofs= normalize_offset(ofs, buf->len);
615 106 50         if (!secret_buffer_parse_init(&parse, buf,
616 106           ofs, ofs + normalize_offset(len, buf->len - ofs),
617             (flags & SECRET_BUFFER_ENCODING_MASK)
618             ))
619 0           croak("%s", parse.error);
620             PPCODE:
621 106 100         if (secret_buffer_match(&parse, pattern, flags)) {
622 82           PUSHs(sv_2mortal(newSViv(parse.pos - (U8*) buf->data)));
623 82           PUSHs(sv_2mortal(newSViv(parse.lim - parse.pos)));
624             }
625 24 50         else if (parse.error)
626 0           croak("%s", parse.error);
627              
628             void
629             splice(buf, ofs, len, replacement)
630             secret_buffer *buf
631             IV ofs
632             IV len
633             SV *replacement
634             PPCODE:
635             /* normalize negative offset, and clamp to valid range */
636 0           ofs= normalize_offset(ofs, buf->len);
637             /* normalize negative count, and clamp to valid range */
638 0           len= normalize_offset(len, buf->len - ofs);
639 0           secret_buffer_splice_sv(buf, ofs, len, replacement);
640 0           XSRETURN(1); /* return $self */
641              
642             void
643             substr(buf, ofs, count_sv=NULL, replacement=NULL)
644             secret_buffer *buf
645             IV ofs
646             SV *count_sv
647             SV *replacement
648             INIT:
649             unsigned char *sub_start;
650 18           secret_buffer *sub_buf= NULL;
651 18           SV *sub_ref= NULL;
652 18 100         IV count= count_sv? SvIV(count_sv) : buf->len;
653             PPCODE:
654             /* normalize negative offset, and clamp to valid range */
655 18           ofs= normalize_offset(ofs, buf->len);
656             /* normalize negative count, and clamp to valid range */
657 18           count= normalize_offset(count, buf->len - ofs);
658 18           sub_start= (unsigned char*) buf->data + ofs;
659             /* If called in non-void context, construct new secret from this range */
660 18 100         if (GIMME_V != G_VOID) {
661             SV **el;
662 11           sub_buf= secret_buffer_new(count, &sub_ref);
663 11 100         if (count) {
664 8           Copy(sub_start, sub_buf->data, count, unsigned char);
665 8           sub_buf->len= count;
666             }
667             /* inherit the stringify_mask */
668 11           el= hv_fetchs((HV*) SvRV(ST(0)), "stringify_mask", 0);
669 11 100         if (el && *el)
    50          
670             /* we know the hv isn't tied because we just created it, so no need to check success */
671 1           hv_stores((HV*) SvRV(sub_ref), "stringify_mask", newSVsv(*el));
672             }
673             /* modifying string? */
674 18 100         if (replacement)
675 7           secret_buffer_splice_sv(buf, ofs, count, replacement);
676             /* If void context, return nothing. Else return the substr */
677 18 100         if (!sub_ref)
678 7           XSRETURN(0);
679             else {
680 11           ST(0)= sub_ref; /* already mortal */
681 11           XSRETURN(1);
682             }
683              
684             void
685             append_asn1_der_length(buf, val)
686             secret_buffer *buf
687             UV val
688             PPCODE:
689 384           secret_buffer_append_uv_asn1_der_length(buf, val);
690              
691             void
692             append_base128le(buf, val)
693             secret_buffer *buf
694             UV val
695             PPCODE:
696 384           secret_buffer_append_uv_base128le(buf, val);
697              
698             void
699             append_base128be(buf, val)
700             secret_buffer *buf
701             UV val
702             PPCODE:
703 384           secret_buffer_append_uv_base128be(buf, val);
704              
705             void
706             append_lenprefixed(buf, ...)
707             secret_buffer *buf
708             INIT:
709 1           size_t bytes_needed= 0;
710             IV i;
711             PPCODE:
712             /* Add up all the lengths and over-estimate 9 bytes for each length specifier */
713 4 100         for (i= 1; i < items; i++) {
714             STRLEN len;
715 3           secret_buffer_SvPVbyte(ST(i), &len);
716 3           bytes_needed += 9 + len;
717             }
718             /* ensure space with one reallocation */
719 1           secret_buffer_alloc_at_least(buf, buf->len + bytes_needed);
720             /* append each length and span to the buffer */
721 4 100         for (i= 1; i < items; i++) {
722             STRLEN len;
723             size_t buf_pos;
724 3           const char *data= secret_buffer_SvPVbyte(ST(i), &len);
725 3           secret_buffer_append_uv_base128be(buf, len);
726 3           buf_pos= buf->len;
727 3           secret_buffer_set_len(buf, buf_pos + len);
728 3           memcpy(buf->data + buf_pos, data, len);
729             }
730              
731             IV
732             memcmp(lhs, rhs, reverse=false)
733             SV *lhs
734             SV *rhs
735             bool reverse
736             ALIAS:
737             Crypt::SecretBuffer::Span::memcmp = 1
738             Crypt::SecretBuffer::Exports::memcmp = 2
739             INIT:
740             STRLEN lhs_len, rhs_len;
741 163           const char *lhs_buf= secret_buffer_SvPVbyte(lhs, &lhs_len);
742 163           const char *rhs_buf= secret_buffer_SvPVbyte(rhs, &rhs_len);
743             PERL_UNUSED_VAR(ix);
744             CODE:
745             /* constant-time loop */
746             {
747 163           volatile int ret= 0;
748 163           int i, common_len= lhs_len < rhs_len? lhs_len : rhs_len;
749 1415 100         for (i = 0; i < common_len; i++)
750 1252 100         if (lhs_buf[i] != rhs_buf[i] && !ret)
    50          
751 8 100         ret= lhs_buf[i] < rhs_buf[i]? -1 : 1;
752 163 100         if (ret == 0 && lhs_len != rhs_len)
    100          
753 6 100         ret= lhs_len < rhs_len? -1 : 1;
754 163           RETVAL= ret;
755             }
756 163 100         if (reverse)
757 4           RETVAL= -RETVAL;
758             OUTPUT:
759             RETVAL
760              
761             UV
762             append_random(buf, count, flags=0)
763             auto_secret_buffer buf
764             UV count
765             secret_buffer_io_flags flags
766             CODE:
767 2 50         RETVAL= secret_buffer_append_random(buf, count, flags);
768             OUTPUT:
769             RETVAL
770              
771             void
772             append_sysread(buf, handle, count)
773             auto_secret_buffer buf
774             PerlIO *handle
775             IV count
776             INIT:
777             IV got;
778             PPCODE:
779 6           got= secret_buffer_append_sysread(buf, handle, count);
780 6 50         ST(0)= (got < 0)? &PL_sv_undef : sv_2mortal(newSViv(got));
781 6           XSRETURN(1);
782              
783             void
784             append_read(buf, handle, count)
785             auto_secret_buffer buf
786             PerlIO *handle
787             IV count
788             INIT:
789             int got;
790             PPCODE:
791 4           got= secret_buffer_append_read(buf, handle, count);
792 4 100         ST(0)= (got < 0)? &PL_sv_undef : sv_2mortal(newSViv(got));
793 4           XSRETURN(1);
794              
795             void
796             _append_console_line(buf, handle)
797             auto_secret_buffer buf
798             PerlIO *handle
799             INIT:
800             int got;
801             PPCODE:
802 22           got= secret_buffer_append_console_line(buf, handle);
803 22           ST(0)= got == SECRET_BUFFER_GOTLINE? &PL_sv_yes
804 27 100         : got == SECRET_BUFFER_EOF? &PL_sv_no
805 5 100         : &PL_sv_undef;
806 22           XSRETURN(1);
807              
808             void
809             syswrite(buf, io, count=buf->len, ofs=0)
810             secret_buffer *buf
811             PerlIO *io
812             IV ofs
813             IV count
814             INIT:
815             IV wrote;
816             PPCODE:
817 4           wrote= secret_buffer_syswrite(buf, io, ofs, count);
818 4 50         ST(0)= (wrote < 0)? &PL_sv_undef : sv_2mortal(newSViv(wrote));
819 4           XSRETURN(1);
820              
821             void
822             write_async(buf, io, count=buf->len, ofs=0)
823             secret_buffer *buf
824             PerlIO *io
825             IV ofs
826             IV count
827             INIT:
828             IV wrote;
829 3           SV *ref_out= NULL;
830             PPCODE:
831 3 100         wrote= secret_buffer_write_async(buf, io, ofs, count, GIMME_V == G_VOID? NULL : &ref_out);
832             /* wrote == 0 means that it supplied a result promise object, which is already mortal.
833             * but avoid creating one when called in void context. */
834 3 50         ST(0)= wrote? sv_2mortal(newSViv(wrote)) : ref_out? ref_out : &PL_sv_undef;
    0          
835 3           XSRETURN(1);
836              
837             void
838             stringify(buf, ...)
839             auto_secret_buffer buf
840             INIT:
841 55           SV **field= hv_fetch((HV*)SvRV(ST(0)), "stringify_mask", 14, 0);
842             PPCODE:
843 55 100         if (!field || !*field) {
    50          
844 12           ST(0)= sv_2mortal(newSVpvn("[REDACTED]", 10));
845 43 100         } else if (SvOK(*field)) {
846 3           ST(0)= *field;
847             } else {
848 40           ST(0)= secret_buffer_get_stringify_sv(buf);
849             }
850 55           XSRETURN(1);
851              
852             void
853             unmask_to(buf, coderef)
854             secret_buffer *buf
855             SV *coderef
856             INIT:
857 7           int count= 0;
858             PPCODE:
859 7 50         PUSHMARK(SP);
860 7 50         EXTEND(SP, 1);
861 7           PUSHs(secret_buffer_get_stringify_sv(buf));
862 7           PUTBACK;
863 7           count= call_sv(coderef, GIMME_V);
864 5           SPAGAIN;
865 5           XSRETURN(count);
866              
867             bool
868             _can_count_copies_in_process_memory()
869             CODE:
870 0 0         RETVAL= false;
871             OUTPUT:
872             RETVAL
873            
874             IV
875             _count_matches_in_mem(buf, addr0, addr1)
876             secret_buffer *buf
877             UV addr0
878             UV addr1
879             CODE:
880 125 50         if (!buf->len)
881 0           croak("Empty buffer");
882 125           RETVAL= scan_mapped_memory_in_range(addr0, addr1, buf->data, buf->len);
883 125 50         if (RETVAL < 0)
884 0           croak("Unimplemented");
885             OUTPUT:
886             RETVAL
887              
888             MODULE = Crypt::SecretBuffer PACKAGE = Crypt::SecretBuffer::Exports
889              
890             void
891             unmask_secrets_to(coderef, ...)
892             SV *coderef
893             INIT:
894 7           int count= 0, i;
895 7           secret_buffer *buf= NULL;
896             PPCODE:
897 7 50         PUSHMARK(SP);
898 7 50         EXTEND(SP, items);
    50          
899 18 100         for (i= 1; i < items; i++) {
900 11 50         if (SvOK(ST(i)) && SvROK(ST(i)) && (buf= secret_buffer_from_magic(ST(i), 0)))
    100          
    50          
901 7           PUSHs(secret_buffer_get_stringify_sv(buf));
902             else
903 4           PUSHs(ST(i));
904             }
905 7           PUTBACK;
906 7           count= call_sv(coderef, GIMME_V);
907 5           SPAGAIN;
908 5           XSRETURN(count);
909              
910             void
911             _debug_charset(cset)
912             secret_buffer_charset *cset
913             INIT:
914             HV *hv;
915             PPCODE:
916 14           PUSHs(sv_2mortal((SV*)newRV_noinc((SV*)(hv= newHV()))));
917 14           hv_stores(hv, "bitmap", newSVpvn((char*)cset->bitmap, sizeof(cset->bitmap)));
918 14           hv_stores(hv, "unicode_above_7F", newSViv(cset->unicode_above_7F));
919              
920             MODULE = Crypt::SecretBuffer PACKAGE = Crypt::SecretBuffer::AsyncResult
921              
922             void
923             wait(result, timeout=-1)
924             secret_buffer_async_result *result
925             NV timeout
926             INIT:
927             IV os_err, bytes_written;
928             PPCODE:
929 0 0         if (secret_buffer_async_result_recv(result, (IV)(timeout*1000), &bytes_written, &os_err)) {
930 0 0         EXTEND(sp, 2);
931 0           ST(0)= sv_2mortal(newSViv(bytes_written));
932 0           ST(1)= sv_2mortal(newSViv(os_err));
933 0           XSRETURN(2);
934             } else {
935 0           XSRETURN(0);
936             }
937              
938             MODULE = Crypt::SecretBuffer PACKAGE = Crypt::SecretBuffer::ConsoleState
939              
940             void
941             new(pkg, ...)
942             const char *pkg;
943             ALIAS:
944             maybe_new = 1
945             INIT:
946             sb_console_state cstate, *magic_cstate;
947 22           PerlIO *handle= NULL;
948 22           SV *auto_restore= NULL;
949 22           SV *set_echo= NULL;
950 22           SV *set_line_input= NULL;
951             SV *objref;
952 22           bool already_set= true;
953 22 50         if (items == 2) {
954 0           IO *io = sv_2io(ST(1));
955 0 0         handle= io? IoIFP(io) : NULL;
956 22 50         } else if (items > 2) {
957 22           int i= 1;
958 22 50         if ((items - 1) & 1)
959 0           croak("expected even-length key/value attribute list");
960 88 100         for (; i < items; i+= 2) {
961             STRLEN len;
962 66           const char *name= SvPV(ST(i), len);
963 66 100         if (len == 4 && memcmp(name, "echo", 4) == 0) {
    50          
964 22           set_echo= ST(i+1);
965             }
966 44 100         else if (len == 6 && memcmp(name, "handle", 6) == 0) {
    50          
967 22           IO *io = sv_2io(ST(i+1));
968 22 50         handle= io? IoIFP(io) : NULL;
969             }
970 22 50         else if (len == 10 && memcmp(name, "line_input", 10) == 0) {
    0          
971 0           set_line_input= ST(i+1);
972             }
973 22 50         else if (len == 12 && memcmp(name, "auto_restore", 12) == 0) {
    50          
974 22           auto_restore= ST(i+1);
975             }
976             else {
977 0           croak("Unknown option '%s'", name);
978             }
979             }
980             }
981 22 50         if (!handle)
982 0           croak("'handle' is required");
983             PPCODE:
984 22           ST(0)= &PL_sv_undef;
985             /* if it fails to initialize, return undef for 'maybe_new', else die */
986 22 50         if (!sb_console_state_init(aTHX_ &cstate, handle)) {
987 22 50         if (ix == 0)
988 0           croak("Can't read console/tty state");
989 22           XSRETURN(1);
990             }
991 0 0         if (auto_restore)
992 0           cstate.auto_restore= SvTRUE(auto_restore);
993 0 0         if (set_echo && SvOK(set_echo)) {
    0          
994 0           bool enable= SvTRUE(set_echo);
995 0 0         if (sb_console_state_get_echo(&cstate) != enable) {
996 0           already_set= false;
997 0 0         if (!sb_console_state_set_echo(&cstate, enable))
998 0           croak("set echo = %d failed", (int)enable);
999             }
1000             }
1001 0 0         if (set_line_input && SvOK(set_line_input)) {
    0          
1002 0           bool enable= SvTRUE(set_line_input);
1003 0 0         if (sb_console_state_get_line_input(&cstate) != enable) {
1004 0           already_set= false;
1005 0 0         if (!sb_console_state_set_line_input(&cstate, enable))
1006 0           croak("set echo = %d failed", (int)enable);
1007             }
1008             }
1009             /* if user called 'maybe_new' and echo state aready matches requested
1010             state, return undef. */
1011 0 0         if (ix == 1 && already_set)
    0          
1012 0           XSRETURN(1);
1013             /* new blessed ConsoleState object */
1014 0           ST(0)= objref= sv_2mortal(newRV_noinc(&PL_sv_yes));
1015 0           newSVrv(objref, pkg);
1016             /* move cstate into MAGIC on object */
1017 0           magic_cstate= secret_buffer_console_state_from_magic(objref, SECRET_BUFFER_MAGIC_AUTOCREATE);
1018 0           *magic_cstate= cstate;
1019             /* duplicate file handle in case user closes it */
1020 0           sb_console_state_dup_fd(magic_cstate);
1021 0           XSRETURN(1);
1022              
1023             bool
1024             echo(cstate, enable=NULL)
1025             sb_console_state *cstate
1026             SV *enable
1027             CODE:
1028 0 0         if (enable != NULL)
1029 0           sb_console_state_set_echo(cstate, SvTRUE(enable));
1030 0           RETVAL= sb_console_state_get_echo(cstate);
1031             OUTPUT:
1032             RETVAL
1033              
1034             bool
1035             line_input(cstate, enable=NULL)
1036             sb_console_state *cstate
1037             SV *enable
1038             CODE:
1039 0 0         if (enable != NULL)
1040 0           sb_console_state_set_line_input(cstate, SvTRUE(enable));
1041 0           RETVAL= sb_console_state_get_line_input(cstate);
1042             OUTPUT:
1043             RETVAL
1044              
1045             bool
1046             auto_restore(cstate, enable=NULL)
1047             sb_console_state *cstate
1048             SV *enable
1049             CODE:
1050 0 0         if (enable != NULL)
1051 0           cstate->auto_restore= SvTRUE(enable);
1052 0 0         RETVAL= cstate->auto_restore;
1053             OUTPUT:
1054             RETVAL
1055              
1056             bool
1057             restore(cstate)
1058             sb_console_state *cstate
1059             CODE:
1060 0           RETVAL= sb_console_state_restore(cstate);
1061 0 0         cstate->auto_restore= false; /* no longer run restore on destructor */
1062             OUTPUT:
1063             RETVAL
1064              
1065             MODULE = Crypt::SecretBuffer PACKAGE = Crypt::SecretBuffer::Span
1066              
1067             void
1068             new(class_or_obj, ...)
1069             SV *class_or_obj
1070             ALIAS:
1071             clone = 1
1072             subspan = 2
1073             Crypt::SecretBuffer::span = 3
1074             Crypt::SecretBuffer::Exports::span = 4
1075             INIT:
1076 554           secret_buffer_span *span= secret_buffer_span_from_magic(class_or_obj, SECRET_BUFFER_MAGIC_UNDEF_OK);
1077 56 50         SV **buf_field= span && SvTYPE(SvRV(class_or_obj)) == SVt_PVHV
1078 56           ? hv_fetchs((HV*)SvRV(class_or_obj), "buf", 0)
1079 554 100         : NULL;
1080 554 100         secret_buffer *buf= secret_buffer_from_magic(
1081             buf_field? *buf_field : class_or_obj, SECRET_BUFFER_MAGIC_UNDEF_OK
1082             );
1083 554 100         bool subspan= span && ix >= 2;
    100          
1084 554 100         IV base_pos= subspan? span->pos : 0;
1085 554           IV pos=0, lim=0, len=0, base_lim=0;
1086 554 100         int encoding= span? span->encoding : 0, i;
1087 554           SV *encoding_sv= NULL;
1088 554           bool have_pos= false, have_lim= false, have_len= false;
1089             PPCODE:
1090             //warn("items=%d span=%p buf=%p base_pos=%d", (int)items, span, buf, (int)base_pos);
1091             // 3-argument form, only usable when first arg is a buffer or refs a buffer
1092 554 100         if (buf && items >= 2 && looks_like_number(ST(1))) {
    100          
    100          
1093 29           pos= SvIV(ST(1));
1094 29           have_pos= true;
1095 29 100         if (items >= 3 && SvOK(ST(2))) {
    50          
1096 26           len= SvIV(ST(2));
1097 26           have_len= true;
1098 26 100         if (items >= 4) {
1099 2           encoding_sv= ST(3);
1100 2 50         if (items > 4)
1101 0           warn("unexpected 4th argument after encoding");
1102             }
1103             }
1104             } else { // key => value form
1105 525 50         if ((items - 1) & 1)
1106 0           croak("Odd number of arguments; expected (key => value, ...)");
1107 609 100         for (i= 1; i < items-1; i += 2) {
1108 84 100         if (0 == strcmp("pos", SvPV_nolen(ST(i)))) {
1109 22           pos= SvIV(ST(i+1));
1110 22           have_pos= true;
1111             }
1112 62 100         else if (0 == strcmp("lim", SvPV_nolen(ST(i)))) {
1113 30           lim= SvIV(ST(i+1));
1114 30           have_lim= true;
1115             }
1116 32 100         else if (0 == strcmp("len", SvPV_nolen(ST(i)))) {
1117 4           len= SvIV(ST(i+1));
1118 4           have_len= true;
1119             }
1120 28 100         else if (0 == strcmp("encoding", SvPV_nolen(ST(i)))) {
1121 26           encoding_sv= ST(i+1);
1122             }
1123 2 50         else if (0 == strcmp("buf", SvPV_nolen(ST(i)))) {
1124 2           buf= secret_buffer_from_magic(ST(i+1), SECRET_BUFFER_MAGIC_OR_DIE);
1125             }
1126             }
1127             }
1128 554 100         if (have_len && have_lim && (lim != pos + len))
    50          
    0          
1129 0           croak("Can't specify both 'len' and 'lim', make up your mind!");
1130             // buffer is required
1131 554 100         if (!buf) {
1132             /* The 'span()' exported function can accept plain scalars and upgrade them to a span object */
1133 2 50         if (ix != 4)
1134 0           croak("Require 'buf' attribute");
1135 2           buf= secret_buffer_new(0, NULL);
1136 2           secret_buffer_splice_sv(buf, 0, 0, class_or_obj);
1137             }
1138 554 100         base_lim= subspan? span->lim : buf->len;
1139             // pos is relative to base_pos, and needs truncated to (or count backward from) base_lim
1140 51           pos= have_pos? normalize_offset(pos, base_lim-base_pos)+base_pos
1141 605 100         : span ? span->pos
    100          
1142             : base_pos;
1143             // likewise for lim, but also might need calculated from 'len'
1144 30           lim= have_lim? normalize_offset(lim, base_lim-base_pos)+base_pos
1145 616 100         : have_len? normalize_offset(len, base_lim-pos)+pos
    100          
    100          
1146 32           : span ? span->lim
1147             : base_lim;
1148 554 50         if (pos > lim)
1149 0           croak("lim must be greater or equal to pos");
1150             //warn(" base_lim=%d pos=%d lim=%d", (int) base_lim, (int)pos, (int)lim);
1151             // check encoding
1152 554 100         if (encoding_sv) {
1153 28 50         if (!parse_encoding(aTHX_ encoding_sv, &encoding))
1154 0           croak("Unknown encoding '%s'", SvPV_nolen(encoding_sv));
1155             }
1156 554           PUSHs(new_mortal_span_obj(aTHX_ buf, pos, lim, encoding));
1157              
1158             UV
1159             pos(span, newval_sv= NULL)
1160             secret_buffer_span *span
1161             SV *newval_sv
1162             ALIAS:
1163             lim = 1
1164             len = 2
1165             length = 2
1166             CODE:
1167 442 100         if (newval_sv) {
1168 74           IV newval= SvIV(newval_sv);
1169 74 50         if (newval < 0)
1170 0           croak("pos, lim, and len cannot be negative");
1171 74           switch (ix) {
1172 64           case 0: span->pos= newval; break;
1173 10 50         case 1: if (newval < span->pos) croak("lim must be >= pos");
1174 10           span->lim= newval; break;
1175 0           case 2: span->lim= span->pos + newval;
1176 0           default: croak("BUG");
1177             }
1178             }
1179 442 100         RETVAL= ix == 0? span->pos
1180 703 100         : ix == 1? span->lim
1181 446 100         : ix == 2? span->lim - span->pos
1182 185 50         : -1;
1183             OUTPUT:
1184             RETVAL
1185              
1186             void
1187             encoding(span, newval_sv= NULL)
1188             secret_buffer_span *span
1189             SV *newval_sv
1190             INIT:
1191             SV *enc_const;
1192 26           AV *encodings= get_av("Crypt::SecretBuffer::_encodings", 0);
1193 26 50         if (!encodings) croak("BUG");
1194             PPCODE:
1195 26 100         if (newval_sv)
1196 15 50         if (!parse_encoding(aTHX_ newval_sv, &span->encoding))
1197 0           croak("Invalid encoding");
1198 26           enc_const= *av_fetch(encodings, span->encoding, 1);
1199 26 50         if (!enc_const || !SvOK(enc_const))
    50          
1200 0           croak("BUG");
1201 26           PUSHs(enc_const);
1202              
1203             const char *
1204             last_error(span)
1205             secret_buffer_span *span
1206             CODE:
1207 5           RETVAL= span->last_error;
1208             OUTPUT:
1209             RETVAL
1210              
1211             void
1212             set_up_us_the_bom(self)
1213             SV *self
1214             ALIAS:
1215             consume_bom = 1
1216             INIT:
1217 3           secret_buffer_span *span= secret_buffer_span_from_magic(self, SECRET_BUFFER_MAGIC_OR_DIE);
1218             secret_buffer_parse p;
1219 3 50         if (!secret_buffer_parse_init_from_sv(&p, self))
1220 0           croak("%s", p.error);
1221             PERL_UNUSED_VAR(ix);
1222             PPCODE:
1223 3 50         if (p.lim - p.pos >= 3 && p.pos[0] == 0xEF && p.pos[1] == 0xBB && p.pos[2] == 0xBF) {
    100          
    50          
    50          
1224 1           span->encoding= SECRET_BUFFER_ENCODING_UTF8;
1225 1           span->pos += 3;
1226             }
1227 2 50         else if (p.lim - p.pos >= 2 && p.pos[0] == 0xFF && p.pos[1] == 0xFE) {
    100          
    50          
1228 1           span->encoding= SECRET_BUFFER_ENCODING_UTF16LE;
1229 1           span->pos += 2;
1230             }
1231 1 50         else if (p.lim - p.pos >= 2 && p.pos[0] == 0xFE && p.pos[1] == 0xFF) {
    50          
    50          
1232 1           span->encoding= SECRET_BUFFER_ENCODING_UTF16BE;
1233 1           span->pos += 2;
1234             }
1235 3           XSRETURN(1);
1236              
1237             void
1238             scan(self, pattern=NULL, flags= 0)
1239             SV *self
1240             SV *pattern
1241             IV flags
1242             ALIAS:
1243             parse = 0x102
1244             rparse = 0x203
1245             trim = 0x322
1246             ltrim = 0x422
1247             rtrim = 0x523
1248             starts_with = 0x612
1249             ends_with = 0x713
1250             INIT:
1251 572           secret_buffer_span *span= secret_buffer_span_from_magic(self, SECRET_BUFFER_MAGIC_OR_DIE);
1252 572           SV **sb_sv= hv_fetchs((HV*)SvRV(self), "buf", 1);
1253 572           secret_buffer *buf= secret_buffer_from_magic(*sb_sv, SECRET_BUFFER_MAGIC_OR_DIE);
1254             secret_buffer_parse parse;
1255 572 50         if (!secret_buffer_parse_init(&parse, buf, span->pos, span->lim, span->encoding))
1256 0           croak("%s", parse.error);
1257             // Bit 0 indicates an op from the end of the buffer
1258 572 100         if (ix & 1)
1259 68           flags |= SECRET_BUFFER_MATCH_REVERSE;
1260             // Bit 1 indicates an anchored op
1261 572 100         if (ix & 2)
1262 526           flags |= SECRET_BUFFER_MATCH_ANCHORED;
1263             // Bits 4..7 indicate return type,
1264             // 0 == return a span
1265             // 1 == return bool
1266             // 2 == return self
1267 572           int ret_type= (ix >> 4) & 0xF;
1268 572 100         if (ret_type != 1 && parse.encoding == SECRET_BUFFER_ENCODING_BASE64)
    50          
1269 0           croak("Cannot perform parse, trim, or scan on base64 (pos / lim of result would not be whole bytes)");
1270 572           int op= (ix >> 8);
1271             bool matched;
1272 572 100         if (!pattern) {
1273 14 100         if (op == 3 || op == 4 || op == 5)
    100          
    50          
1274 14           pattern= get_sv("Crypt::SecretBuffer::Span::default_trim_regex", 0);
1275 14 50         if (!pattern)
1276 0           croak("pattern is required");
1277             }
1278             PPCODE:
1279 572           matched= secret_buffer_match(&parse, pattern, flags);
1280 572 100         if (parse.error)
1281 1           croak("%s", parse.error);
1282 571           switch (op) {
1283 264           case 1: // parse
1284 264 100         if (matched) span->pos= parse.lim - (U8*) buf->data;
1285 264           break;
1286 1           case 2: // rparse
1287 1 50         if (matched) span->lim= parse.pos - (U8*) buf->data;
1288 1           break;
1289 118           case 3: // trim
1290             case 4: // ltrim
1291 118 100         if (matched) span->pos= parse.lim - (U8*) buf->data;
1292 118 100         if (op == 4) break;
1293             // reset the modified parse_state and run in reverse
1294 78           parse.pos= (U8*) (buf->data + span->pos);
1295 78           parse.lim= (U8*) (buf->data + span->lim);
1296 78           flags |= SECRET_BUFFER_MATCH_REVERSE;
1297 78           matched= secret_buffer_match(&parse, pattern, flags);
1298 119           case 5: // rtrim, and trim
1299 119 100         if (matched) span->lim= parse.pos - (U8*) buf->data;
1300 119           break;
1301 571           default:
1302             (void)0; // suppress warning that not all values were handled
1303             }
1304 571 100         if (ret_type == 0) {
1305 311 100         if (!matched)
1306 77           XSRETURN_UNDEF;
1307 234 50         if (parse.pos > parse.lim || parse.lim > (U8*) buf->data + buf->len)
    50          
1308 0           croak("BUG: parse pos=%p lim=%p buf.data=%p buf.len=%ld",
1309             parse.pos, parse.lim, buf->data, (long)buf->len);
1310 234           PUSHs(new_mortal_span_obj(aTHX_ buf, parse.pos - (U8*) buf->data, parse.lim - (U8*) buf->data, span->encoding));
1311 260 100         } else if (ret_type == 1) {
1312 101 100         if (matched)
1313 63           XSRETURN_YES;
1314             else
1315 38           XSRETURN_NO;
1316             }
1317             else {
1318 159           XSRETURN(1); // use self pointer in ST(0)
1319             }
1320              
1321             void
1322             parse_lenprefixed(self, count = 1)
1323             SV *self
1324             IV count
1325             INIT:
1326 5           secret_buffer_span *span= secret_buffer_span_from_magic(self, SECRET_BUFFER_MAGIC_OR_DIE);
1327             secret_buffer_parse p;
1328             UV len;
1329             size_t ofs;
1330             /* treat an invalid span as a bug, rather than returning it to the user in the err_out param */
1331 5 50         if (!secret_buffer_parse_init_from_sv(&p, self))
1332 0           croak("%s", p.error);
1333             PPCODE:
1334 14 100         while (count && p.pos < p.lim) {
    100          
1335 11 50         if (!secret_buffer_parse_uv_base128be(&p, &len)) {
1336 0           span->last_error= p.error;
1337 0           XSRETURN_EMPTY;
1338             }
1339 11 100         if (len > p.lim - p.pos) {
1340 2           span->last_error= "Length exceeds end of Span";
1341 2           XSRETURN_EMPTY;
1342             }
1343 9           ofs= p.pos - (U8*) p.sbuf->data;
1344 9 50         XPUSHs(secret_buffer_span_new_obj(p.sbuf, ofs, ofs + len, 0));
1345 9           p.pos += len;
1346 9 100         if (count > 0) --count;
1347             }
1348 3           span->pos= p.pos - (U8*) p.sbuf->data;
1349 3           span->last_error= NULL;
1350              
1351             SV*
1352             parse_asn1_der_length(self)
1353             SV *self
1354             ALIAS:
1355             parse_base128le = 1
1356             parse_base128be = 2
1357             INIT:
1358 1152           secret_buffer_span *span= secret_buffer_span_from_magic(self, SECRET_BUFFER_MAGIC_OR_DIE);
1359             secret_buffer_parse p;
1360             UV val_out;
1361             bool success;
1362             /* treat an invalid span as a bug, rather than returning it to the user as last_error */
1363 1152 50         if (!secret_buffer_parse_init_from_sv(&p, self))
1364 0           croak("%s", p.error);
1365             CODE:
1366 1152           switch (ix) {
1367 384           case 0 : success= secret_buffer_parse_uv_asn1_der_length(&p, &val_out); break;
1368 384           case 1 : success= secret_buffer_parse_uv_base128le(&p, &val_out); break;
1369 384           case 2 : success= secret_buffer_parse_uv_base128be(&p, &val_out); break;
1370 0           default: croak("BUG");
1371             }
1372 1152 50         if (success) {
1373             /* advance the span position */
1374 1152           span->pos= p.pos - (U8*) p.sbuf->data;
1375 1152           span->last_error= NULL;
1376 1152           RETVAL= newSVuv(val_out);
1377             } else {
1378 0           span->last_error= p.error;
1379 0           RETVAL= &PL_sv_undef;
1380             }
1381             OUTPUT:
1382             RETVAL
1383              
1384             void
1385             copy(self, ...)
1386             SV *self
1387             ALIAS:
1388             copy_to = 1
1389             append_to = 2
1390             INIT:
1391 102           SV *dst_sv= NULL;
1392 102           int next_arg, dst_encoding= -1;
1393             secret_buffer_parse src;
1394 102 100         if (!secret_buffer_parse_init_from_sv(&src, self))
1395 1           croak("%s", src.error);
1396             PPCODE:
1397 101 100         if (ix > 0) { /* called as 'copy_to' or 'append_to' */
1398 89 50         if (items < 2)
1399 0           croak("Missing copy/append destination");
1400 89           dst_sv= ST(1);
1401 89           next_arg= 2;
1402             }
1403             else { /* called as 'copy' */
1404 12           secret_buffer_new(0, &dst_sv);
1405 12           next_arg= 1;
1406             }
1407            
1408             // parse options
1409 101 50         if ((items - next_arg) & 1)
1410 0           croak("expected even-length list of (key => val)");
1411 124 100         for (; next_arg < items; next_arg+= 2) {
1412 23 50         if (0 == strcmp(SvPV_nolen(ST(next_arg)), "encoding")) {
1413 23 50         if (!parse_encoding(aTHX_ ST(next_arg+1), &dst_encoding))
1414 0           croak("Unknown encoding");
1415             }
1416             }
1417 101 50         if (!secret_buffer_copy_to(&src, dst_sv, dst_encoding, ix == 2))
1418 0           croak("copy failed: %s", src.error);
1419             // copy returns the SecretBuffer, but copy_to returns empty list.
1420 101 100         if (ix == 0)
1421 12           PUSHs(dst_sv);
1422              
1423             IV
1424             cmp(lhs, rhs, reverse=false)
1425             SV *lhs
1426             SV *rhs
1427             bool reverse
1428             INIT:
1429             secret_buffer_parse lhs_parse, rhs_parse;
1430 18 50         if (!secret_buffer_parse_init_from_sv(&lhs_parse, lhs))
1431 0           croak("%s", lhs_parse.error);
1432 18 50         if (!secret_buffer_parse_init_from_sv(&rhs_parse, rhs))
1433 0           croak("%s", rhs_parse.error);
1434             CODE:
1435 18           RETVAL= sb_parse_codepointcmp(&lhs_parse, &rhs_parse);
1436 18 50         if (reverse)
1437 0           RETVAL= -RETVAL;
1438             OUTPUT:
1439             RETVAL
1440              
1441             BOOT:
1442             int i;
1443 23           HV *stash= gv_stashpvs("Crypt::SecretBuffer", 1);
1444 23           HV *exports_stash= gv_stashpvs("Crypt::SecretBuffer::Exports", 1);
1445             #define EXPORT_CONST(name, const) \
1446             newCONSTSUB(stash, name, make_enum_dualvar(aTHX_ const, newSVpvs_share(name)));\
1447             hv_stores(exports_stash, name, SvREFCNT_inc(*hv_fetchs(stash, name, 0)))
1448 23           EXPORT_CONST("NONBLOCK", SECRET_BUFFER_NONBLOCK);
1449 23           EXPORT_CONST("AT_LEAST", SECRET_BUFFER_AT_LEAST);
1450 23           EXPORT_CONST("MATCH_MULTI", SECRET_BUFFER_MATCH_MULTI);
1451 23           EXPORT_CONST("MATCH_REVERSE", SECRET_BUFFER_MATCH_REVERSE);
1452 23           EXPORT_CONST("MATCH_NEGATE", SECRET_BUFFER_MATCH_NEGATE);
1453 23           EXPORT_CONST("MATCH_ANCHORED",SECRET_BUFFER_MATCH_ANCHORED);
1454 23           EXPORT_CONST("MATCH_CONST_TIME",SECRET_BUFFER_MATCH_CONST_TIME);
1455             #undef EXPORT_CONST
1456             SV *enc[SECRET_BUFFER_ENCODING_MAX+1];
1457 23           memset(enc, 0, sizeof(enc));
1458             #define EXPORT_ENCODING(name, str, const) \
1459             newCONSTSUB(stash, name, (enc[const]= make_enum_dualvar(aTHX_ const, newSVpvs_share(str)))); \
1460             hv_stores(exports_stash, name, SvREFCNT_inc(*hv_fetchs(stash, name, 0)))
1461 23           EXPORT_ENCODING("ASCII", "ASCII", SECRET_BUFFER_ENCODING_ASCII);
1462 23           EXPORT_ENCODING("ISO8859_1","ISO-8859-1", SECRET_BUFFER_ENCODING_ISO8859_1);
1463 23           EXPORT_ENCODING("UTF8", "UTF-8", SECRET_BUFFER_ENCODING_UTF8);
1464 23           EXPORT_ENCODING("UTF16LE", "UTF-16LE", SECRET_BUFFER_ENCODING_UTF16LE);
1465 23           EXPORT_ENCODING("UTF16BE", "UTF-16BE", SECRET_BUFFER_ENCODING_UTF16BE);
1466 23           EXPORT_ENCODING("HEX", "HEX", SECRET_BUFFER_ENCODING_HEX);
1467 23           EXPORT_ENCODING("BASE64", "BASE64", SECRET_BUFFER_ENCODING_BASE64);
1468             #undef EXPORT_ENCODING
1469             // Set up an array of _encodings so that the accessor can return an existing SV
1470 23           AV *encodings= get_av("Crypt::SecretBuffer::_encodings", GV_ADD);
1471 23           av_fill(encodings, SECRET_BUFFER_ENCODING_MAX);
1472 207 100         for (i= 0; i <= SECRET_BUFFER_ENCODING_MAX; i++)
1473 184 100         if (enc[i] && av_store(encodings, i, enc[i]))
    50          
1474 161           SvREFCNT_inc(enc[i]);
1475 23           SECRET_BUFFER_EXPORT_FUNCTION_POINTERS