File Coverage

XS.xs
Criterion Covered Total %
statement 76 89 85.3
branch 33 60 55.0
condition n/a
subroutine n/a
pod n/a
total 109 149 73.1


line stmt bran cond sub pod time code
1             #include "EXTERN.h"
2             #include "perl.h"
3             #include "XSUB.h"
4              
5             #ifndef sv_setrv
6             #define sv_setrv(sv, rv) sv_rvweaken(sv); SvRV_set((sv), (rv)); SvROK_on(sv)
7             #endif
8              
9             typedef struct {
10             int base;
11             AV *symbols;
12             HV *symbol_value_map;
13             } MathNumberBaseXS;
14              
15             /* Free allocated memory for the C structure */
16 10           static void free_data(void *ptr) {
17 10 50         if (!ptr) return;
18              
19 10           MathNumberBaseXS *data = (MathNumberBaseXS *)ptr;
20              
21             /* Avoid interfering with Perl's reference management */
22 10           data->symbols = NULL;
23 10           data->symbol_value_map = NULL;
24              
25 10           Safefree(data);
26             }
27              
28             /* XS Definitions */
29             MODULE = Math::NumberBase::XS PACKAGE = Math::NumberBase::XS
30              
31             PROTOTYPES: ENABLE
32              
33             void
34             DESTROY(self)
35             SV *self;
36             CODE:
37 10           MAGIC *mg = mg_find(SvRV(self), 'P');
38 10 50         if (mg && mg->mg_ptr) {
    50          
39 10           free_data(mg->mg_ptr);
40 10           mg->mg_ptr = NULL;
41             }
42              
43             void
44             _init(self, base, symbols, symbol_value_map)
45             SV *self;
46             int base;
47             AV *symbols;
48             HV *symbol_value_map;
49             CODE:
50             MathNumberBaseXS *data;
51              
52             /* Check and clean up existing MAGIC */
53 10           MAGIC *mg = mg_find(SvRV(self), 'P');
54 10 50         if (mg && mg->mg_ptr) {
    0          
55 0           free_data(mg->mg_ptr);
56 0           mg->mg_ptr = NULL;
57             }
58              
59             /* Allocate new memory for the C structure */
60 10           Newxz(data, 1, MathNumberBaseXS);
61 10           data->base = base;
62              
63             /* Increment reference counts for Perl-managed objects */
64 10           SvREFCNT_inc((SV *)symbols);
65 10           data->symbols = symbols;
66              
67 10           SvREFCNT_inc((SV *)symbol_value_map);
68 10           data->symbol_value_map = symbol_value_map;
69              
70             /* Attach the new C structure to the Perl object via MAGIC */
71 10           sv_magic(SvRV(self), NULL, 'P', (const char *)data, 0);
72 10           SvMAGIC(SvRV(self))->mg_flags |= MGf_DUP;
73              
74             int
75             _get_base(self)
76             SV *self;
77             CODE:
78 2           MAGIC *mg = mg_find(SvRV(self), 'P');
79 2 50         if (!mg || !mg->mg_ptr) croak("Object not initialized");
    50          
80              
81 2           MathNumberBaseXS *data = (MathNumberBaseXS *)mg->mg_ptr;
82 2 100         RETVAL = data->base;
83             OUTPUT:
84             RETVAL
85              
86             AV *
87             _get_symbols(self)
88             SV *self;
89             CODE:
90 2           MAGIC *mg = mg_find(SvRV(self), 'P');
91 2 50         if (!mg || !mg->mg_ptr) croak("Object not initialized");
    50          
92              
93 2           MathNumberBaseXS *data = (MathNumberBaseXS *)mg->mg_ptr;
94              
95             /* Increment reference count before returning to Perl */
96 2           RETVAL = data->symbols;
97 2           SvREFCNT_inc((SV *)RETVAL);
98             OUTPUT:
99             RETVAL
100              
101             HV *
102             _get_symbol_value_map(self)
103             SV *self;
104             CODE:
105 1           MAGIC *mg = mg_find(SvRV(self), 'P');
106 1 50         if (!mg || !mg->mg_ptr) croak("Object not initialized");
    50          
107              
108 1           MathNumberBaseXS *data = (MathNumberBaseXS *)mg->mg_ptr;
109              
110             /* Increment reference count before returning to Perl */
111 1           RETVAL = data->symbol_value_map;
112 1           SvREFCNT_inc((SV *)RETVAL);
113             OUTPUT:
114             RETVAL
115              
116             UV
117             _to_decimal(self, string)
118             SV *self;
119             SV *string;
120             CODE:
121 16           MAGIC *mg = mg_find(SvRV(self), 'P');
122 16 50         if (!mg || !mg->mg_ptr) croak("Object not initialized");
    50          
123              
124 16           MathNumberBaseXS *data = (MathNumberBaseXS *)mg->mg_ptr;
125              
126 16           int base = data->base;
127 16           HV *symbol_value_map = data->symbol_value_map;
128              
129 16           UV result = 0;
130 16           UV power = 1; /* Start with base^0 = 1 */
131              
132             /* Ensure the input is defined */
133 16 50         if (!SvOK(string)) {
134 0           croak("Input string is undefined");
135             }
136              
137             /* Convert input to a string */
138             STRLEN len;
139 16           const char *input = SvPVutf8(string, len);
140              
141 16 50         if (len == 0) {
142 0           croak("Input string is empty");
143             }
144              
145             /* Process the string from right to left */
146 122 100         for (int i = len - 1; i >= 0; i--) {
147 106           char char_at_pos = input[i];
148              
149             /* Fetch the character's value from the symbol_value_map */
150 106           SV **value_sv = hv_fetch(symbol_value_map, &char_at_pos, 1, 0);
151 106 50         if (!value_sv || !SvOK(*value_sv)) {
    50          
152 0           croak("Invalid character '%c' in input string", char_at_pos);
153             }
154              
155 106           int value = SvIV(*value_sv); /* Get the integer value for the character */
156              
157             /* Accumulate the result */
158 106           result += value * power;
159              
160             /* Update power = power * base */
161 106 100         if (i > 0) { /* Avoid overflow on the last iteration */
162 90           UV new_power = power * base;
163 90 50         if (new_power < power) { /* Detect overflow */
164 0           croak("Overflow occurred while calculating power");
165             }
166 90           power = new_power;
167             }
168             }
169              
170 16 100         RETVAL = result;
171             OUTPUT:
172             RETVAL
173              
174             const char *
175             _from_decimal(self, in_decimal)
176             SV *self;
177             UV in_decimal;
178             CODE:
179 12           MAGIC *mg = mg_find(SvRV(self), 'P');
180 12 50         if (!mg || !mg->mg_ptr) croak("Object not initialized");
    50          
181              
182 12           MathNumberBaseXS *data = (MathNumberBaseXS *)mg->mg_ptr;
183              
184 12           int base = data->base;
185 12           AV *symbols = data->symbols;
186              
187 12           STRLEN result_len = 0;
188 12           STRLEN buffer_size = 64;
189 12           char *buffer = (char *)malloc(buffer_size);
190              
191 12 50         if (!buffer) {
192 0           croak("Memory allocation failed");
193             }
194              
195 12           buffer[0] = '\0'; // Ensure buffer starts null-terminated
196              
197 89 100         while (in_decimal > 0) {
198 77           int index = in_decimal % base;
199 77           in_decimal /= base;
200              
201 77           SV **symbol_sv = av_fetch(symbols, index, 0);
202 77 50         if (!symbol_sv || !SvOK(*symbol_sv)) {
    50          
203 0           free(buffer);
204 0           croak("Invalid symbol for index %d", index);
205             }
206              
207             STRLEN symbol_len;
208 77           const char *symbol = SvPV(*symbol_sv, symbol_len);
209              
210 77 50         while (result_len + symbol_len + 1 >= buffer_size) {
211 0           buffer_size *= 2;
212 0           buffer = (char *)realloc(buffer, buffer_size);
213 0 0         if (!buffer) {
214 0           croak("Memory reallocation failed");
215             }
216             }
217              
218 77           memmove(buffer + symbol_len, buffer, result_len + 1);
219 77           memcpy(buffer, symbol, symbol_len);
220 77           result_len += symbol_len;
221             }
222              
223 12           RETVAL = buffer;
224             OUTPUT:
225             RETVAL