summaryrefslogtreecommitdiff
path: root/ext/ByteLoader/ByteLoader.xs
blob: d8b15c102a21b1473ddc877c9cf7311936adf8af (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
#define PERL_NO_GET_CONTEXT
#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"
#include "byterun.h"

/* Something arbitary for a buffer size */
#define BYTELOADER_BUFFER 8096

int
bl_getc(struct byteloader_fdata *data)
{
    dTHX;
    if (SvCUR(data->datasv) <= data->next_out) {
      int result;
      /* Run out of buffered data, so attempt to read some more */
      *(SvPV_nolen (data->datasv)) = '\0';
      SvCUR_set (data->datasv, 0);
      data->next_out = 0;
      result = FILTER_READ (data->idx + 1, data->datasv, BYTELOADER_BUFFER);

      /* Filter returned error, or we got EOF and no data, then return EOF.
	 Not sure if filter is allowed to return EOF and add data simultaneously
	 Think not, but will bullet proof against it. */
      if (result < 0 || SvCUR(data->datasv) == 0)
	return EOF;
      /* Else there must be at least one byte present, which is good enough */
    }

    return *((char *) SvPV_nolen (data->datasv) + data->next_out++);
}

int
bl_read(struct byteloader_fdata *data, char *buf, size_t size, size_t n)
{
    dTHX;
    char *start;
    STRLEN len;
    size_t wanted = size * n;

    start = SvPV (data->datasv, len);
    if (len < (data->next_out + wanted)) {
      int result;

      /* Shuffle data to start of buffer */
      len -= data->next_out;
      if (len) {
	memmove (start, start + data->next_out, len + 1);
	SvCUR_set (data->datasv, len);
      } else {
	*start = '\0';	/* Avoid call to memmove. */
	SvCUR_set (data->datasv, 0);
      }
      data->next_out = 0;

      /* Attempt to read more data. */
      do {
	result = FILTER_READ (data->idx + 1, data->datasv, BYTELOADER_BUFFER);
	
	start = SvPV (data->datasv, len);
      } while (result > 0 && len < wanted);
      /* Loop while not (EOF || error) and short reads */

      /* If not enough data read, truncate copy */
      if (wanted > len)
	wanted = len;
    }

    if (wanted > 0) {
      memcpy (buf, start + data->next_out, wanted);
	data->next_out += wanted;
      wanted /= size;
    }
    return (int) wanted;
}

static I32
byteloader_filter(pTHXo_ int idx, SV *buf_sv, int maxlen)
{
    OP *saveroot = PL_main_root;
    OP *savestart = PL_main_start;
    struct byteloader_state bstate;
    struct byteloader_fdata data;

    data.next_out = 0;
    data.datasv = FILTER_DATA(idx);
    data.idx = idx;

    bstate.bs_fdata = &data;
    bstate.bs_obj_list = Null(void**);
    bstate.bs_obj_list_fill = -1;
    bstate.bs_sv = Nullsv;
    bstate.bs_iv_overflows = 0;

    byterun(aTHXo_ &bstate);

    if (PL_in_eval) {
        OP *o;

        PL_eval_start = PL_main_start;

        o = newSVOP(OP_CONST, 0, newSViv(1));
        PL_eval_root = newLISTOP(OP_LINESEQ, 0, PL_main_root, o);
        PL_main_root->op_next = o;
        PL_eval_root = newUNOP(OP_LEAVEEVAL, 0, PL_eval_root);
        o->op_next = PL_eval_root;
    
        PL_main_root = saveroot;
        PL_main_start = savestart;
    }

    return 0;
}

MODULE = ByteLoader		PACKAGE = ByteLoader

PROTOTYPES:	ENABLE

void
import(package="ByteLoader", ...)
  char *package
  PREINIT:
    SV *sv = newSVpvn ("", 0);
  PPCODE:
    if (!sv)
      croak ("Could not allocate ByteLoader buffers");
    filter_add(byteloader_filter, sv);

void
unimport(package="ByteLoader", ...)
  char *package
  PPCODE:
    filter_del(byteloader_filter);