File Coverage

lib/Data/Pond.xs
Criterion Covered Total %
statement 332 373 89.0
branch 268 366 73.2
condition n/a
subroutine n/a
pod n/a
total 600 739 81.1


line stmt bran cond sub pod time code
1             #define PERL_NO_GET_CONTEXT 1
2             #include "EXTERN.h"
3             #include "perl.h"
4             #include "XSUB.h"
5              
6             #define PERL_VERSION_DECIMAL(r,v,s) (r*1000000 + v*1000 + s)
7             #define PERL_DECIMAL_VERSION \
8             PERL_VERSION_DECIMAL(PERL_REVISION,PERL_VERSION,PERL_SUBVERSION)
9             #define PERL_VERSION_GE(r,v,s) \
10             (PERL_DECIMAL_VERSION >= PERL_VERSION_DECIMAL(r,v,s))
11              
12             #ifndef cBOOL
13             # define cBOOL(x) ((bool)!!(x))
14             #endif /* !cBOOL */
15              
16             #ifndef hv_fetchs
17             # define hv_fetchs(hv, keystr, lval) \
18             hv_fetch(hv, ""keystr"", sizeof(keystr)-1, lval)
19             #endif /* !hv_fetchs */
20              
21             #ifndef newSVpvs
22             # define newSVpvs(string) newSVpvn(""string"", sizeof(string)-1)
23             #endif /* !newSVpvs */
24              
25             #ifndef sv_catpvs_nomg
26             # define sv_catpvs_nomg(sv, string) \
27             sv_catpvn_nomg(sv, ""string"", sizeof(string)-1)
28             #endif /* !sv_catpvs_nomg */
29              
30             #if PERL_VERSION_GE(5,19,4)
31             typedef SSize_t array_ix_t;
32             #else /* <5.19.4 */
33             typedef I32 array_ix_t;
34             #endif /* <5.19.4 */
35              
36             #ifndef uvchr_to_utf8_flags
37             #define uvchr_to_utf8_flags(d, uv, flags) uvuni_to_utf8_flags(d, uv, flags);
38             #endif
39              
40             /* parameter classification */
41              
42             #define sv_is_glob(sv) (SvTYPE(sv) == SVt_PVGV)
43              
44             #if PERL_VERSION_GE(5,11,0)
45             # define sv_is_regexp(sv) (SvTYPE(sv) == SVt_REGEXP)
46             #else /* <5.11.0 */
47             # define sv_is_regexp(sv) 0
48             #endif /* <5.11.0 */
49              
50             #define sv_is_undef(sv) (!sv_is_glob(sv) && !sv_is_regexp(sv) && !SvOK(sv))
51              
52             #define sv_is_string(sv) \
53             (!sv_is_glob(sv) && !sv_is_regexp(sv) && \
54             (SvFLAGS(sv) & (SVf_IOK|SVf_NOK|SVf_POK|SVp_IOK|SVp_NOK|SVp_POK)))
55              
56             /* exceptions */
57              
58             #define throw_utf8_error() croak("broken internal UTF-8 encoding\n")
59             #define throw_syntax_error(p) croak("Pond syntax error\n")
60             #define throw_constraint_error(MSG) croak("Pond constraint error: "MSG"\n")
61             #define throw_data_error(MSG) croak("Pond data error: "MSG"\n")
62              
63             /*
64             * string walking
65             *
66             * The parser deals with strings that are internally encoded using Perl's
67             * extended form of UTF-8. It is not assumed that the encoding is
68             * well-formed; encoding errors will result in an exception. The encoding
69             * octets are treated as U8 type.
70             *
71             * Characters that are known to be in the ASCII range are in some places
72             * processed as U8. General Unicode characters are processed as U32, with
73             * the intent that the entire ISO-10646 31-bit range be handleable. Any
74             * codepoint is accepted for processing, even the surrogates (which are
75             * not legal in true UTF-8 encoding). Perl's extended UTF-8 extends to
76             * 72-bit codepoints; encodings beyond the 31-bit range are translated to
77             * codepoint U+80000000, whereby they are all treated as invalid.
78             *
79             * char_unicode() returns the codepoint represented by the character being
80             * pointed at, or throws an exception if the encoding is malformed.
81             *
82             * To move on to the character following the one pointed at, use the core
83             * macro UTF8SKIP(), as in (p + UTF8SKIP(p)). It assumes that the character
84             * is properly encoded, so it is essential that char_unicode() has been
85             * called on it first.
86             *
87             * Given an input SV (that is meant to be a string), pass it through
88             * upgrade_sv() to return an SV that contains the string in UTF-8. This
89             * could be either the same SV (if it is already UTF-8-encoded or contains
90             * no non-ASCII characters) or a mortal upgraded copy.
91             */
92              
93             #define char_unicode(p) THX_char_unicode(aTHX_ p)
94 439           static U32 THX_char_unicode(pTHX_ U8 *p)
95             {
96 439           U32 val = *p;
97             U8 req_c1;
98             int ncont;
99             int i;
100 439 100         if(!(val & 0x80)) return val;
101 38 50         if(!(val & 0x40)) throw_utf8_error();
102 38 50         if(!(val & 0x20)) {
103 38 50         if(!(val & 0x1e)) throw_utf8_error();
104 38           val &= 0x1f;
105             ncont = 1;
106             req_c1 = 0x00;
107 0 0         } else if(!(val & 0x10)) {
108 0           val &= 0x0f;
109             ncont = 2;
110             req_c1 = 0x20;
111 0 0         } else if(!(val & 0x08)) {
112 0           val &= 0x07;
113             ncont = 3;
114             req_c1 = 0x30;
115 0 0         } else if(!(val & 0x04)) {
116 0           val &= 0x03;
117             ncont = 4;
118             req_c1 = 0x38;
119 0 0         } else if(!(val & 0x02)) {
120 0           val &= 0x01;
121             ncont = 5;
122             req_c1 = 0x3c;
123 0 0         } else if(!(val & 0x01)) {
124 0 0         if(!(p[1] & 0x3e)) throw_utf8_error();
125 0 0         for(i = 6; i--; )
126 0 0         if((*++p & 0xc0) != 0x80)
127 0           throw_utf8_error();
128             return 0x80000000;
129             } else {
130             U8 first_six = 0;
131 0 0         for(i = 6; i--; ) {
132 0           U8 ext = *++p;
133 0 0         if((ext & 0xc0) != 0x80)
134 0           throw_utf8_error();
135 0           first_six |= ext;
136             }
137 0 0         if(!(first_six & 0x3f))
138 0           throw_utf8_error();
139 0 0         for(i = 6; i--; )
140 0 0         if((*++p & 0xc0) != 0x80)
141 0           throw_utf8_error();
142             return 0x80000000;
143             }
144 38 50         if(val == 0 && !(p[1] & req_c1))
    0          
145 0           throw_utf8_error();
146 76 100         for(i = ncont; i--; ) {
147 38           U8 ext = *++p;
148 38 50         if((ext & 0xc0) != 0x80)
149 0           throw_utf8_error();
150 38           val = UTF8_ACCUMULATE(val, ext);
151             }
152             return val;
153             }
154              
155             #define sv_cat_unichar(str, val) THX_sv_cat_unichar(aTHX_ str, val)
156 33           static void THX_sv_cat_unichar(pTHX_ SV *str, U32 val)
157             {
158             STRLEN vlen;
159             U8 *vstart, *voldend, *vnewend;
160 33           vlen = SvCUR(str);
161 33 50         vstart = (U8*)SvGROW(str, vlen+6+1);
    50          
162 33           voldend = vstart + vlen;
163 33           vnewend = uvchr_to_utf8_flags(voldend, val, UNICODE_ALLOW_ANY);
164 33           *vnewend = 0;
165 33           SvCUR_set(str, vnewend - vstart);
166 33           }
167              
168             #define upgrade_sv(input) THX_upgrade_sv(aTHX_ input)
169 408           static SV *THX_upgrade_sv(pTHX_ SV *input)
170             {
171             U8 *p, *end;
172             STRLEN len;
173 408 100         if(SvUTF8(input)) return input;
174 250           p = (U8*)SvPV(input, len);
175 2064 100         for(end = p + len; p != end; p++) {
176 1820 100         if(*p & 0x80) {
177 6           SV *output = sv_mortalcopy(input);
178 6           sv_utf8_upgrade(output);
179 6           return output;
180             }
181             }
182             return input;
183             }
184              
185             /*
186             * Pond reading
187             */
188              
189             #define CHARATTR_WSP 0x01
190             #define CHARATTR_DQSPECIAL 0x02
191             #define CHARATTR_CONTROL 0x04
192             #define CHARATTR_HEXDIGIT 0x08
193             #define CHARATTR_WORDSTART 0x10
194             #define CHARATTR_WORDCONT 0x20
195             #define CHARATTR_DECDIGIT 0x40
196             #define CHARATTR_OCTDIGIT 0x80
197              
198             static U8 const asciichar_attr[128] = {
199             0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, /* NUL to BEL */
200             0x04, 0x05, 0x05, 0x04, 0x05, 0x05, 0x04, 0x04, /* BS to SI */
201             0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, /* DLE to ETB */
202             0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, /* CAN to US */
203             0x01, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00, /* SP to ' */
204             0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* ( to / */
205             0xe8, 0xe8, 0xe8, 0xe8, 0xe8, 0xe8, 0xe8, 0xe8, /* 0 to 7 */
206             0x68, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 8 to ? */
207             0x02, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x30, /* @ to G */
208             0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, /* H to O */
209             0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, /* P to W */
210             0x30, 0x30, 0x30, 0x00, 0x02, 0x00, 0x00, 0x30, /* X to _ */
211             0x00, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x30, /* ` to g */
212             0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, /* h to o */
213             0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, /* p to w */
214             0x30, 0x30, 0x30, 0x00, 0x00, 0x00, 0x00, 0x04, /* x to DEL */
215             };
216              
217             static int char_is_wsp(U8 c)
218             {
219 1201 100         return !(c & 0x80) && (asciichar_attr[c] & CHARATTR_WSP);
    100          
    100          
    100          
    100          
220             }
221              
222             static int char_is_dqspecial(U8 c)
223             {
224 783 100         return !(c & 0x80) && (asciichar_attr[c] & CHARATTR_DQSPECIAL);
    100          
    100          
    100          
225             }
226              
227             static int char_is_control(U8 c)
228             {
229 433 100         return !(c & 0x80) && (asciichar_attr[c] & CHARATTR_CONTROL);
    100          
    100          
    100          
230             }
231              
232             static int unichar_is_control(U32 c)
233             {
234 415 100         return (c >= 0x80) ? c <= 0xa0 : (asciichar_attr[c] & CHARATTR_CONTROL);
    100          
    0          
235             }
236              
237             static int char_is_wordstart(U8 c)
238             {
239 50 100         return !(c & 0x80) && (asciichar_attr[c] & CHARATTR_WORDSTART);
240             }
241              
242             static int char_is_wordcont(U8 c)
243             {
244 55 0         return !(c & 0x80) && (asciichar_attr[c] & CHARATTR_WORDCONT);
    100          
245             }
246              
247             static int char_is_decdigit(U8 c)
248             {
249 242 100         return !(c & 0x80) && (asciichar_attr[c] & CHARATTR_DECDIGIT);
    50          
    100          
250             }
251              
252             static int char_is_octdigit(U8 c)
253             {
254 5 100         return !(c & 0x80) && (asciichar_attr[c] & CHARATTR_OCTDIGIT);
    100          
255             }
256              
257             static int char_is_hexdigit(U8 c)
258             {
259 85 100         return !(c & 0x80) && (asciichar_attr[c] & CHARATTR_HEXDIGIT);
    50          
    50          
    100          
260             }
261              
262             static int hexdigit_value(U8 c)
263             {
264 67 50         return c <= '9' ? c - '0' : c <= 'F' ? c - 'A' + 10 : c - 'a' + 10;
    50          
    50          
265             }
266              
267             static U8 *parse_opt_wsp(U8 *p)
268             {
269 1201 50         while(char_is_wsp(*p))
    50          
    50          
    50          
    50          
270 351           p++;
271             return p;
272             }
273              
274             static U8 const asciichar_backslash[128] = {
275             0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, /* NUL to BEL */
276             0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, /* BS to SI */
277             0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, /* DLE to ETB */
278             0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, /* CAN to US */
279             0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, /* SP to ' */
280             0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, /* ( to / */
281             0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0 to 7 */
282             0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, /* 8 to ? */
283             0x40, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, /* @ to G */
284             0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, /* H to O */
285             0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, /* P to W */
286             0xfd, 0xfd, 0xfd, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, /* X to _ */
287             0x60, 0x07, 0x08, 0xfd, 0xfd, 0x1b, 0x0c, 0xfd, /* ` to g */
288             0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0x0a, 0xfd, /* h to o */
289             0xfd, 0xfd, 0x0d, 0xfd, 0x09, 0xfd, 0xfd, 0xfd, /* p to w */
290             0xfe, 0xfd, 0xfd, 0x7b, 0x7c, 0x7d, 0x7e, 0xfd, /* x to DEL */
291             };
292              
293             #define parse_dqstring(end, pp) THX_parse_dqstring(aTHX_ end, pp)
294 148           static SV *THX_parse_dqstring(pTHX_ U8 *end, U8 **pp)
295             {
296 148           U8 *p = *pp;
297 148           SV *datum = sv_2mortal(newSVpvs(""));
298 148           SvUTF8_on(datum);
299             while(1) {
300 402           U8 c = *p, e;
301 406 50         if(p == end || char_is_control(c)) throw_syntax_error(p);
302 195           if(!char_is_dqspecial(c)) {
303             U8 *q = p;
304             do {
305 387           U32 val = char_unicode(q);
306 387 100         if(unichar_is_control(val))
307 2           throw_syntax_error(q);
308 385           q += UTF8SKIP(q);
309 385           c = *q;
310 770 50         } while(q != end && !char_is_dqspecial(c));
    100          
311 195           sv_catpvn_nomg(datum, (char*)p, q-p);
312             p = q;
313 195           continue;
314             }
315 201 100         if(c == '"') break;
316 60 50         if(c != '\\') throw_syntax_error(p);
317 60           c = *++p;
318 60 50         if(p == end) throw_syntax_error(p);
319 60 50         if(c & 0x80) {
320 0           U32 val = char_unicode(p);
321 0 0         if(unichar_is_control(val)) throw_syntax_error(q);
322             /* character will be treated as literal anyway */
323 0           continue;
324             }
325 60           e = asciichar_backslash[c];
326 60 100         if(e == 0xff) {
327 3           U32 val = c & 7;
328 3 50         c = *++p;
329             if(char_is_octdigit(c)) {
330 2           p++;
331 2           val = (val << 3) | (c & 7);
332 2 50         c = *p;
333             if(char_is_octdigit(c)) {
334 1           p++;
335 1           val = (val << 3) | (c & 7);
336             }
337             }
338 3           sv_cat_unichar(datum, val);
339 57 100         } else if(e == 0xfe) {
340             U32 val;
341 30 50         c = *++p;
342             if(char_is_hexdigit(c)) {
343 21 100         p++;
344 21           val = hexdigit_value(c);
345 21 50         c = *p;
346             if(char_is_hexdigit(c)) {
347 21           p++;
348 42 100         val = (val << 4) | hexdigit_value(c);
349             }
350 9 50         } else if(c == '{') {
351 9           p++;
352 9 50         c = *p;
353             if(!char_is_hexdigit(c))
354 0           throw_syntax_error(p);
355             val = 0;
356             do {
357 25 50         if(val & 0x78000000)
358 0           throw_constraint_error(
359             "invalid character");
360 25 100         val = (val << 4) | hexdigit_value(c);
361 25 50         c = *++p;
362 25 100         } while(char_is_hexdigit(c));
363 9 50         if(c != '}') throw_syntax_error(p);
364 9           p++;
365             } else {
366 0           throw_syntax_error(p);
367             }
368 30           sv_cat_unichar(datum, val);
369 27 100         } else if(e == 0xfd) {
370 1           throw_syntax_error(p);
371             } else {
372 26           p++;
373 26           sv_catpvn_nomg(datum, (char*)&e, 1);
374             }
375             }
376 141           *pp = p+1;
377 141           return datum;
378             }
379              
380             #define parse_sqstring(end, pp) THX_parse_sqstring(aTHX_ end, pp)
381 15           static SV *THX_parse_sqstring(pTHX_ U8 *end, U8 **pp)
382             {
383 15           U8 *p = *pp;
384 15           SV *datum = sv_2mortal(newSVpvs(""));
385 15           SvUTF8_on(datum);
386             while(1) {
387 31           U8 c = *p;
388 35 50         if(p == end || char_is_control(c)) throw_syntax_error(p);
389 27 100         if(c == '\'') break;
390 18 100         if(c != '\\') {
391             U8 *q = p;
392             do {
393 28           U32 val = char_unicode(q);
394 28 100         if(unichar_is_control(val))
395 2           throw_syntax_error(q);
396 26           q += UTF8SKIP(q);
397 26           c = *q;
398 26 100         } while(q != end && c != '\'' && c != '\\');
    100          
399 12           sv_catpvn_nomg(datum, (char*)p, q-p);
400             p = q;
401             } else {
402 4           c = p[1];
403 4 100         if(c == '\\' || c == '\'')
404 2           p++;
405 4           sv_catpvn_nomg(datum, (char*)p, 1);
406 4           p++;
407             }
408             }
409 9           *pp = p+1;
410 9           return datum;
411             }
412              
413             #define array_to_hash(array) THX_array_to_hash(aTHX_ array)
414 45           static SV *THX_array_to_hash(pTHX_ AV *array)
415             {
416             HV *hash;
417             SV *href;
418             array_ix_t alen, i;
419 45           alen = av_len(array);
420 45 50         if(!(alen & 1))
421 0           throw_constraint_error(
422             "odd number of elements in hash constructor");
423 45           hash = newHV();
424 45           href = sv_2mortal(newRV_noinc((SV*)hash));
425 103 100         for(i = 0; i <= alen; i += 2) {
426 58           SV **key_ptr = av_fetch(array, i, 0);
427             STRLEN key_len;
428             char *key_str;
429             SV *value;
430 58 50         if(!key_ptr || !sv_is_string(*key_ptr))
    50          
    50          
431 0           throw_constraint_error("non-string hash key");
432 58           key_str = SvPV(*key_ptr, key_len);
433 58 50         value = *av_fetch(array, i+1, 0);
434 58 50         if(!hv_store(hash, key_str, -key_len, SvREFCNT_inc(value), 0))
435 0           SvREFCNT_dec(value);
436             }
437 45           return href;
438             }
439              
440             #define parse_datum(end, pp) THX_parse_datum(aTHX_ end, pp)
441             static SV *THX_parse_datum(pTHX_ U8 *end, U8 **pp);
442 382           static SV *THX_parse_datum(pTHX_ U8 *end, U8 **pp)
443             {
444 382           U8 *p = *pp;
445 382           U8 c = *p;
446             SV *datum;
447 382 100         if(c == '"') {
448 148           p++;
449 148           datum = parse_dqstring(end, &p);
450 234 100         } else if(c == '\'') {
451 15           p++;
452 15           datum = parse_sqstring(end, &p);
453 219 100         } else if(c == '[' || c == '{') {
454             int is_hash = c == '{';
455 102 100         U8 close = is_hash ? '}' : ']';
456 102           AV *array = newAV();
457 102           sv_2mortal((SV*)array);
458 102           p++;
459             while(1) {
460 228           p = parse_opt_wsp(p);
461 228 100         if(*p == close) break;
462 324           av_push(array, SvREFCNT_inc(parse_datum(end, &p)));
463 160           p = parse_opt_wsp(p);
464 160 100         if(*p == close) break;
465 126 100         if(*p == ',') {
466 70           p++;
467 56 50         } else if(p[0] == '=' && p[1] == '>') {
    50          
468 56           p += 2;
469             } else {
470 0           throw_syntax_error(p);
471             }
472             }
473 98           p++;
474 98 100         datum = is_hash ? array_to_hash(array) :
475 53           sv_2mortal(newRV_inc((SV*)array));
476 117 50         } else if(c & 0x80) {
477 0           throw_syntax_error(p);
478             } else {
479 117           U8 attr = asciichar_attr[c];
480 117 100         if(attr & CHARATTR_WORDSTART) {
481 47           U8 *start = p++;
482             U8 *q;
483 55 50         while(char_is_wordcont(*p))
484 8           p++;
485             q = parse_opt_wsp(p);
486 47 100         if(!(q[0] == '=' && q[1] == '>'))
    50          
487 2           throw_syntax_error(q);
488 45           datum = sv_2mortal(newSVpvn((char*)start, p-start));
489 70 100         } else if(attr & CHARATTR_DECDIGIT) {
490 64           U8 *start = p++;
491 64 100         if(c == '0') {
492 5 50         if(char_is_decdigit(*p)) throw_syntax_error(p);
493             } else {
494 93 50         while(char_is_decdigit(*p))
495 34           p++;
496             }
497 64           datum = sv_2mortal(newSVpvn((char*)start, p-start));
498             } else {
499 6           throw_syntax_error(p);
500             }
501             }
502 357           *pp = p;
503 357           return datum;
504             }
505              
506             /*
507             * Pond writing
508             */
509              
510             struct writer_options {
511             int indent;
512             int undef_is_empty, unicode;
513             };
514              
515 152           static int pvn_is_integer(U8 *p, STRLEN len)
516             {
517 152           U8 *e = p + len;
518 152 100         if(len == 0 || len > 9) return 0;
519 140 100         if(*p == '0') return len == 1;
520 180 100         for(; p != e; p++) {
521 144 50         if(!char_is_decdigit(*p)) return 0;
522             }
523             return 1;
524             }
525              
526             #define ASCIICHAR_QUOTE_LITERAL 0x00
527             #define ASCIICHAR_QUOTE_HEXPAIR 0x01
528              
529             static U8 const asciichar_quote[128] = {
530             0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, /* NUL to BEL */
531             0x01, 0x74, 0x6e, 0x01, 0x01, 0x01, 0x01, 0x01, /* BS to SI */
532             0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, /* DLE to ETB */
533             0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, /* CAN to US */
534             0x00, 0x00, 0x22, 0x00, 0x24, 0x00, 0x00, 0x00, /* SP to ' */
535             0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* ( to / */
536             0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0 to 7 */
537             0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 8 to ? */
538             0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* @ to G */
539             0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* H to O */
540             0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* P to W */
541             0x00, 0x00, 0x00, 0x00, 0x5c, 0x00, 0x00, 0x00, /* X to _ */
542             0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* ` to g */
543             0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* h to o */
544             0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* p to w */
545             0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, /* x to DEL */
546             };
547              
548             static char const hexdig[16] = "0123456789abcdef";
549              
550             #define serialise_as_string(wo, out, datum) \
551             THX_serialise_as_string(aTHX_ wo, out, datum)
552 152           static void THX_serialise_as_string(pTHX_ struct writer_options *wo,
553             SV *out, SV *datum)
554             {
555             U8 *p;
556             STRLEN len;
557 152           p = (U8*)SvPV(datum, len);
558 152 100         if(pvn_is_integer(p, len)) {
559 40           sv_catpvn_nomg(out, (char *)p, len);
560             } else {
561 112           U8 *e = p + len;
562             U8 *lstart = p;
563 112           sv_catpvs_nomg(out, "\"");
564 472 100         while(p != e) {
565 360           U8 c = *p;
566 360 100         if(c & 0x80) {
567 24           U32 val = char_unicode(p);
568 24 50         if(val == 0x80000000)
569 0           throw_data_error("invalid character");
570 24 100         if(val <= 0xa0 || !wo->unicode) {
    100          
571 16 50         if(lstart != p)
572 16           sv_catpvn_nomg(out,
573             (char*)lstart,
574             p-lstart);
575             }
576 24           p += UTF8SKIP(p);
577 24 100         if(val <= 0xa0) {
578 8           c = val;
579 8           p--;
580 8           goto hexpair;
581             }
582 16 100         if(!wo->unicode) {
583             char hexbuf[12];
584             sprintf(hexbuf, "\\x{%02x}",
585             (unsigned)val);
586 8           sv_catpvn_nomg(out, hexbuf,
587             strlen(hexbuf));
588             lstart = p;
589             }
590             } else {
591 336           U8 quote = asciichar_quote[c];
592 336 100         if(quote == ASCIICHAR_QUOTE_LITERAL) {
593 308           p++;
594 308           continue;
595             }
596 28 50         if(lstart != p)
597 28           sv_catpvn_nomg(out, (char*)lstart,
598             p-lstart);
599 28 100         if(quote == ASCIICHAR_QUOTE_HEXPAIR) {
600             char hexbuf[4];
601 8           hexpair:
602 16           hexbuf[0] = '\\';
603 16           hexbuf[1] = 'x';
604 16           hexbuf[2] = hexdig[c >> 4];
605 16           hexbuf[3] = hexdig[c & 0xf];
606 16           sv_catpvn_nomg(out, hexbuf, 4);
607             } else {
608             char bsbuf[2];
609 20           bsbuf[0] = '\\';
610 20           bsbuf[1] = (char)quote;
611 20           sv_catpvn_nomg(out, bsbuf, 2);
612             }
613 36           lstart = ++p;
614             }
615             }
616 112 100         if(lstart != p) sv_catpvn_nomg(out, (char*)lstart, p-lstart);
617 112           sv_catpvs_nomg(out, "\"");
618             }
619 152           }
620              
621 50           static int pvn_is_bareword(U8 *p, STRLEN len)
622             {
623 50           U8 *e = p + len;
624 50 50         if(!char_is_wordstart(*p)) return 0;
625 42 100         while(++p != e) {
626 8 50         if(!char_is_wordcont(*p)) return 0;
    50          
627             }
628             return 1;
629             }
630              
631             #define serialise_as_bareword(wo, out, datum) \
632             THX_serialise_as_bareword(aTHX_ wo, out, datum)
633 50           static void THX_serialise_as_bareword(pTHX_ struct writer_options *wo,
634             SV *out, SV *datum)
635             {
636             U8 *p;
637             STRLEN len;
638 50           p = (U8*)SvPV(datum, len);
639 50 100         if(pvn_is_bareword(p, len)) {
640 38           sv_catpvn_nomg(out, (char *)p, len);
641             } else {
642 12           serialise_as_string(wo, out, datum);
643             }
644 50           }
645              
646             #define serialise_newline(wo, out) THX_serialise_newline(aTHX_ wo, out)
647 135           static void THX_serialise_newline(pTHX_ struct writer_options *wo, SV *out)
648             {
649 135           int indent = wo->indent;
650 135 100         if(indent != -1) {
651 64           STRLEN cur = SvCUR(out);
652 64 50         char *p = SvGROW(out, cur+indent+2) + cur;
    50          
653 64           *p++ = '\n';
654 64           memset(p, ' ', indent);
655 64           p[indent] = 0;
656 64           SvCUR_set(out, cur+1+indent);
657             }
658 135           }
659              
660             #define serialise_datum(wo, out, datum) \
661             THX_serialise_datum(aTHX_ wo, out, datum)
662             static void THX_serialise_datum(pTHX_ struct writer_options *wo,
663             SV *out, SV *datum);
664              
665             #define serialise_array(wo, out, adatum) \
666             THX_serialise_array(aTHX_ wo, out, adatum)
667 43           static void THX_serialise_array(pTHX_ struct writer_options *wo,
668             SV *out, AV *adatum)
669             {
670 43           array_ix_t alen = av_len(adatum), pos;
671 43 100         if(alen == -1) {
672 20           sv_catpvs_nomg(out, "[]");
673 20           return;
674             }
675 23           sv_catpvs_nomg(out, "[");
676 23 100         if(wo->indent != -1) wo->indent += 4;
677 23           serialise_newline(wo, out);
678 8           for(pos = 0; ; pos++) {
679 31           serialise_datum(wo, out,
680             *av_fetch(adatum, pos, 0));
681 29 100         if(pos == alen && wo->indent == -1)
    100          
682             break;
683 18           sv_catpvs_nomg(out, ",");
684 18 100         if(pos == alen)
685             break;
686 8           serialise_newline(wo, out);
687             }
688 21 100         if(wo->indent != -1) wo->indent -= 4;
689 21           serialise_newline(wo, out);
690 21           sv_catpvs_nomg(out, "]");
691             }
692              
693             #define serialise_hash(wo, out, hdatum) \
694             THX_serialise_hash(aTHX_ wo, out, hdatum)
695 38           static void THX_serialise_hash(pTHX_ struct writer_options *wo,
696             SV *out, HV *hdatum)
697             {
698             AV *keys;
699 38           U32 nelem = hv_iterinit(hdatum), pos;
700 38 100         if(nelem == 0) {
701 4           sv_catpvs_nomg(out, "{}");
702 4           return;
703             }
704 34           keys = newAV();
705 34           sv_2mortal((SV*)keys);
706 34           av_extend(keys, nelem-1);
707 84 100         for(pos = nelem; pos--; ) {
708 50           SV *keysv = upgrade_sv(
709             hv_iterkeysv(hv_iternext(hdatum)));
710 50           av_push(keys, SvREFCNT_inc(keysv));
711             }
712 34           sortsv(AvARRAY(keys), nelem, Perl_sv_cmp);
713 34           sv_catpvs_nomg(out, "{");
714 34 100         if(wo->indent != -1) wo->indent += 4;
715 34           serialise_newline(wo, out);
716 16           for(pos = 0; ; pos++) {
717 50           SV *keysv = *av_fetch(keys, pos, 0);
718             STRLEN klen;
719             char *key;
720 50           serialise_as_bareword(wo, out, keysv);
721 50 100         if(wo->indent == -1) {
722 26           sv_catpvs_nomg(out, "=>");
723             } else {
724 24           sv_catpvs_nomg(out, " => ");
725             }
726 50           key = SvPV(keysv, klen);
727 50           serialise_datum(wo, out, *hv_fetch(hdatum, key, -klen, 0));
728 49 100         if(pos == nelem-1 && wo->indent == -1)
    100          
729             break;
730 32           sv_catpvs_nomg(out, ",");
731 32 100         if(pos == nelem-1)
732             break;
733 16           serialise_newline(wo, out);
734             }
735 33 100         if(wo->indent != -1) wo->indent -= 4;
736 33           serialise_newline(wo, out);
737 33           sv_catpvs_nomg(out, "}");
738             }
739              
740 235           static void THX_serialise_datum(pTHX_ struct writer_options *wo,
741             SV *out, SV *datum)
742             {
743 235 100         if(sv_is_undef(datum) && wo->undef_is_empty) {
    100          
    100          
744 3           sv_catpvs_nomg(out, "\"\"");
745 232 100         } else if(sv_is_string(datum)) {
    100          
746 140           datum = upgrade_sv(datum);
747 140           serialise_as_string(wo, out, datum);
748             } else {
749 92 100         if(!SvROK(datum))
750 4           throw_data_error("unsupported data type");
751 88           datum = SvRV(datum);
752 88 100         if(SvOBJECT(datum))
753 4           throw_data_error("unsupported data type");
754 84 100         if(SvTYPE(datum) == SVt_PVAV) {
755 43           serialise_array(wo, out, (AV*)datum);
756 41 100         } else if(SvTYPE(datum) == SVt_PVHV) {
757 38           serialise_hash(wo, out, (HV*)datum);
758             } else {
759 3           throw_data_error("unsupported data type");
760             }
761             }
762 221           }
763              
764             MODULE = Data::Pond PACKAGE = Data::Pond
765              
766             PROTOTYPES: DISABLE
767              
768             SV *
769             pond_read_datum(SV *text_sv)
770             PROTOTYPE: $
771             PREINIT:
772             STRLEN text_len;
773             U8 *p, *end;
774             CODE:
775 229 100         if(!sv_is_string(text_sv)) throw_data_error("text isn't a string");
    100          
776 218           text_sv = upgrade_sv(text_sv);
777 218           p = (U8*)SvPV(text_sv, text_len);
778 218           end = p + text_len;
779 218           p = parse_opt_wsp(p);
780 218           RETVAL = parse_datum(end, &p);
781 197           p = parse_opt_wsp(p);
782 197 100         if(p != end) throw_syntax_error(p);
783             SvREFCNT_inc(RETVAL);
784             OUTPUT:
785             RETVAL
786              
787             SV *
788             pond_write_datum(SV *datum, SV *options = 0)
789             PROTOTYPE: $;$
790             PREINIT:
791 154 50         struct writer_options wo = { -1, 0, 0 };
792             CODE:
793 154 50         if(options) {
794             HV *opthash;
795             SV **item_ptr;
796 154 50         if(!SvROK(options))
797 0           throw_data_error("option hash isn't a hash");
798 154           options = SvRV(options);
799 154 50         if(SvOBJECT(options) || SvTYPE(options) != SVt_PVHV)
800 0           throw_data_error("option hash isn't a hash");
801             opthash = (HV*)options;
802 154 100         if((item_ptr = hv_fetchs(opthash, "indent", 0))) {
803 70           SV *item = *item_ptr;
804 70 50         if(!sv_is_undef(item)) {
    50          
805 70 50         if(!sv_is_string(item))
    50          
806 0           throw_data_error(
807             "indent option isn't a number");
808 70           wo.indent = SvIV(item);
809 70 50         if(wo.indent < 0)
810 0           throw_data_error(
811             "indent option is negative");
812             }
813             }
814 154 100         if((item_ptr = hv_fetchs(opthash, "undef_is_empty", 0))) {
815 3           SV *item = *item_ptr;
816 3           wo.undef_is_empty = cBOOL(SvTRUE(item));
817             }
818 154 100         if((item_ptr = hv_fetchs(opthash, "unicode", 0))) {
819 70           SV *item = *item_ptr;
820 70           wo.unicode = cBOOL(SvTRUE(item));
821             }
822             }
823 154           RETVAL = sv_2mortal(newSVpvs(""));
824 154           SvUTF8_on(RETVAL);
825 154           serialise_datum(&wo, RETVAL, datum);
826             SvREFCNT_inc(RETVAL);
827             OUTPUT:
828             RETVAL