File Coverage

SecretBuffer.xs
Criterion Covered Total %
statement 477 600 79.5
branch 329 510 64.5
condition n/a
subroutine n/a
pod n/a
total 806 1110 72.6


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