File Coverage

XS.xs
Criterion Covered Total %
statement 90 97 92.7
branch 34 50 68.0
condition n/a
subroutine n/a
pod n/a
total 124 147 84.3


line stmt bran cond sub pod time code
1             #include "EXTERN.h"
2             #include "perl.h"
3             #include "XSUB.h"
4              
5             #include "ppport.h"
6              
7 38           int char_to_num(char c, int pos)
8             {
9 38           int masks[] = {0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0x7c, 0x3e, 0x1f, 0x0f, 0x07, 0x03, 0x01};
10 38           unsigned num = c & masks[pos + 4];
11              
12 38 100         if (pos < 3) {
13 22           return num >> (3 - pos);
14             }
15             else {
16 38           return num << (pos - 3);
17             }
18             }
19              
20             #define ULID_LEN 16
21             #define ULID_TIME_LEN 6
22             #define ULID_RAND_LEN 10
23             #define RESULT_LEN 26
24              
25 1           SV* encode_ulid(SV *svstr)
26             {
27             unsigned long len;
28 1 50         char* str = SvPVbyte(svstr, len);
29 1 50         if (len != ULID_LEN) croak("invalid string length in encode_ulid: %d", len);
30              
31 1           char base32[] = "0123456789ABCDEFGHJKMNPQRSTVWXYZ";
32             char result[RESULT_LEN];
33 1           char *current = result;
34              
35 1           int i = 0;
36 1           unsigned num = 0;
37             int last_pos;
38 1           int last_len = 0;
39              
40 1           int parts[2] = { ULID_TIME_LEN, ULID_RAND_LEN };
41 1           int paddings[2] = { -2, 0 };
42             int part;
43              
44 3 100         for (part = 0; part < 2; ++part) {
45 2           last_pos = paddings[part];
46 2           len = last_len + parts[part];
47              
48 18 100         for (; i < len; ++i) {
49 54 100         while (last_pos < 8) {
50 38           num += char_to_num(str[i], last_pos);
51 38           last_pos += 5;
52              
53 38 100         if (last_pos <= 8) {
54 26           *current++ = base32[num];
55 26           num = 0;
56             }
57             }
58              
59 16           last_pos = (last_pos > 8) * (last_pos - 8 - 5);
60             }
61              
62 2           last_len = len;
63             }
64              
65 1           return newSVpv(result, RESULT_LEN);
66             }
67              
68 2           SV* build_binary_ulid (double time, const char *randomness, unsigned long len)
69             {
70             char result[ULID_LEN];
71             int i;
72              
73 2           unsigned long microtime = time * 1000;
74             unsigned char byte;
75              
76             // network byte order
77 14 100         for (i = ULID_TIME_LEN - 1; i >= 0; --i) {
78 12           byte = microtime & 0xff;
79 12           result[i] = (char) byte;
80 12           microtime = microtime >> 8;
81             }
82              
83 2           int j = 0;
84              
85 22 100         for (i = ULID_TIME_LEN; i < ULID_LEN; ++i) {
86 20 50         if (len < ULID_RAND_LEN) {
87 0           result[i] = '\0';
88 0           ++len;
89             }
90             else {
91 20           result[i] = randomness[j++];
92             }
93             }
94              
95 2           return newSVpv(result, ULID_LEN);
96             }
97              
98             // proper XS Code starts here
99              
100             MODULE = Data::ULID::XS PACKAGE = Data::ULID::XS
101              
102             PROTOTYPES: DISABLE
103              
104             SV*
105             ulid(...)
106             CODE:
107 2           dSP;
108              
109 2 50         PUSHMARK(SP);
110              
111 2 100         if (items == 0) {
112 1           int count = call_pv("Data::ULID::XS::binary_ulid", G_SCALAR);
113              
114 1           SPAGAIN;
115              
116 1 50         if (count != 1) {
117 0           croak("Calling Data::ULID::XS::binary_ulid went wrong in Data::ULID::XS::ulid");
118             }
119              
120 1           SV *ret = POPs;
121 1           RETVAL = encode_ulid(ret);
122             }
123             else {
124 1 50         EXTEND(SP, 1);
125 1           PUSHs(ST(0));
126 1           PUTBACK;
127              
128 1           int count = call_pv("Data::ULID::ulid", G_SCALAR);
129              
130 1           SPAGAIN;
131              
132 1 50         if (count != 1) {
133 0           croak("Calling Data::ULID::ulid went wrong in Data::ULID::XS::ulid");
134             }
135              
136 1           SV *ret = POPs;
137 1           SvREFCNT_inc(ret);
138 1           RETVAL = ret;
139             }
140              
141 2           PUTBACK;
142             OUTPUT:
143             RETVAL
144              
145             SV*
146             binary_ulid(...)
147             CODE:
148 3           dSP;
149              
150 3 50         PUSHMARK(SP);
151              
152 3 100         if (items == 0) {
153 2           ENTER;
154 2           SAVETMPS;
155              
156 2           int count = call_pv("Time::HiRes::time", G_SCALAR);
157              
158 2           SPAGAIN;
159              
160 2 50         if (count != 1) {
161 0           croak("Calling Time::HiRes::time went wrong in Data::ULID::XS::binary_ulid");
162             }
163              
164 2           SV *time_sv = POPs;
165 2 50         double time = SvNV(time_sv);
166              
167 2 50         EXTEND(SP, 2);
168 2           PUSHs(get_sv("Data::ULID::XS::RNG", 0));
169 2           PUSHs(sv_2mortal(newSViv(10)));
170 2           PUTBACK;
171              
172 2           count = call_method("bytes", G_SCALAR);
173              
174 2           SPAGAIN;
175              
176 2 50         if (count != 1) {
177 0           croak("Calling method bytes on Crypt::PRNG::* went wrong in Data::ULID::XS::binary_ulid");
178             }
179              
180 2           SV *randomness_sv = POPs;
181             unsigned long len;
182 2 50         char *randomness = SvPVbyte(randomness_sv, len);
183              
184 2 50         FREETMPS;
185 2           LEAVE;
186              
187 2           RETVAL = build_binary_ulid(time, randomness, len);
188             }
189             else {
190 1 50         EXTEND(SP, 1);
191 1           PUSHs(ST(0));
192 1           PUTBACK;
193              
194 1           int count = call_pv("Data::ULID::binary_ulid", G_SCALAR);
195              
196 1           SPAGAIN;
197              
198 1 50         if (count != 1) {
199 0           croak("Calling Data::ULID::binary_ulid went wrong in Data::ULID::XS::binary_ulid");
200             }
201              
202 1           SV *ret = POPs;
203 1           SvREFCNT_inc(ret);
204 1           RETVAL = ret;
205             }
206              
207 3           PUTBACK;
208             OUTPUT:
209             RETVAL
210