ox 2.14.28 → 2.14.29
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +103 -0
- data/ext/ox/base64.c +24 -17
- data/ext/ox/base64.h +2 -1
- data/ext/ox/buf.h +4 -7
- data/ext/ox/builder.c +277 -122
- data/ext/ox/cache.c +48 -13
- data/ext/ox/cache.h +2 -0
- data/ext/ox/dump.c +374 -221
- data/ext/ox/extconf.rb +3 -0
- data/ext/ox/gen_load.c +10 -8
- data/ext/ox/hash_load.c +7 -2
- data/ext/ox/helper.h +10 -6
- data/ext/ox/intern.c +6 -1
- data/ext/ox/obj_load.c +207 -47
- data/ext/ox/ox.c +179 -38
- data/ext/ox/ox.h +11 -1
- data/ext/ox/parse.c +249 -106
- data/ext/ox/sax.c +22 -18
- data/ext/ox/sax_as.c +42 -10
- data/ext/ox/sax_buf.c +23 -8
- data/ext/ox/sax_stack.h +13 -0
- data/ext/ox/time_conv.h +62 -0
- data/ext/ox/xml_check.h +58 -0
- data/ext/ox/xml_str.h +247 -0
- data/lib/ox/version.rb +1 -1
- metadata +5 -2
data/ext/ox/sax_as.c
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
#include <errno.h>
|
|
7
|
+
#include <stdbool.h>
|
|
7
8
|
#include <stdio.h>
|
|
8
9
|
#include <stdlib.h>
|
|
9
10
|
#include <strings.h>
|
|
@@ -18,19 +19,26 @@
|
|
|
18
19
|
#include "ruby.h"
|
|
19
20
|
#include "ruby/version.h"
|
|
20
21
|
#include "sax.h"
|
|
22
|
+
#include "time_conv.h"
|
|
21
23
|
|
|
22
24
|
static VALUE parse_double_time(const char *text) {
|
|
23
|
-
|
|
25
|
+
time_t v = 0;
|
|
24
26
|
long v2 = 0;
|
|
25
27
|
const char *dot = 0;
|
|
26
28
|
char c;
|
|
29
|
+
bool neg = false;
|
|
27
30
|
|
|
31
|
+
// A time before the epoch is written as a sign followed by the magnitude.
|
|
32
|
+
if ('-' == *text) {
|
|
33
|
+
neg = true;
|
|
34
|
+
text++;
|
|
35
|
+
}
|
|
28
36
|
for (; '.' != *text; text++) {
|
|
29
37
|
c = *text;
|
|
30
38
|
if (c < '0' || '9' < c) {
|
|
31
39
|
return Qnil;
|
|
32
40
|
}
|
|
33
|
-
v = 10 * v + (
|
|
41
|
+
v = 10 * v + (time_t)(c - '0');
|
|
34
42
|
}
|
|
35
43
|
dot = text++;
|
|
36
44
|
for (; '\0' != *text && text - dot <= 6; text++) {
|
|
@@ -43,6 +51,17 @@ static VALUE parse_double_time(const char *text) {
|
|
|
43
51
|
for (; text - dot <= 9; text++) {
|
|
44
52
|
v2 *= 10;
|
|
45
53
|
}
|
|
54
|
+
if (neg) {
|
|
55
|
+
// rb_time_nano_new() wants a non negative nanosecond count, so the
|
|
56
|
+
// fraction goes back into the second count the way dump_time_thin()
|
|
57
|
+
// took it out.
|
|
58
|
+
if (0 == v2) {
|
|
59
|
+
v = -v;
|
|
60
|
+
} else {
|
|
61
|
+
v = -v - 1;
|
|
62
|
+
v2 = 1000000000L - v2;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
46
65
|
return rb_time_nano_new(v, v2);
|
|
47
66
|
}
|
|
48
67
|
|
|
@@ -69,7 +88,9 @@ static VALUE parse_xsd_time(const char *text) {
|
|
|
69
88
|
{2, '\0', '\0'},
|
|
70
89
|
{0, '\0', '\0'}};
|
|
71
90
|
Tp tp = tpa;
|
|
72
|
-
|
|
91
|
+
long nsec = 0;
|
|
92
|
+
long offset;
|
|
93
|
+
bool neg = false;
|
|
73
94
|
|
|
74
95
|
memset(cargs, 0, sizeof(cargs));
|
|
75
96
|
for (; 0 != tp->cnt; tp++) {
|
|
@@ -83,6 +104,15 @@ static VALUE parse_xsd_time(const char *text) {
|
|
|
83
104
|
}
|
|
84
105
|
v = 10 * v + (long)(c - '0');
|
|
85
106
|
}
|
|
107
|
+
// The fraction is the only field terminated by the offset sign, and the
|
|
108
|
+
// sign is the only place the offset's direction is written down.
|
|
109
|
+
if ('+' == tp->end) {
|
|
110
|
+
nsec = v;
|
|
111
|
+
for (; 0 < i; i--) {
|
|
112
|
+
nsec *= 10;
|
|
113
|
+
}
|
|
114
|
+
neg = ('-' == *text);
|
|
115
|
+
}
|
|
86
116
|
if ('\0' == c) {
|
|
87
117
|
break;
|
|
88
118
|
}
|
|
@@ -92,13 +122,15 @@ static VALUE parse_xsd_time(const char *text) {
|
|
|
92
122
|
}
|
|
93
123
|
*cp++ = v;
|
|
94
124
|
}
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
return rb_time_nano_new(
|
|
125
|
+
offset = cargs[7] * 3600 + cargs[8] * 60;
|
|
126
|
+
if (neg) {
|
|
127
|
+
offset = -offset;
|
|
128
|
+
}
|
|
129
|
+
// mktime() would read the wall clock as local time and throw the offset
|
|
130
|
+
// away, and returns -1 for everything before the epoch on Windows.
|
|
131
|
+
return rb_time_nano_new(
|
|
132
|
+
(time_t)(ox_epoch_from_civil(cargs[0], cargs[1], cargs[2], cargs[3], cargs[4], cargs[5]) - offset),
|
|
133
|
+
nsec);
|
|
102
134
|
}
|
|
103
135
|
|
|
104
136
|
/* call-seq: as_s()
|
data/ext/ox/sax_buf.c
CHANGED
|
@@ -133,14 +133,22 @@ static VALUE partial_io_cb(VALUE rbuf) {
|
|
|
133
133
|
VALUE args[1];
|
|
134
134
|
VALUE rstr;
|
|
135
135
|
char *str;
|
|
136
|
+
size_t max = (size_t)(buf->end - buf->tail);
|
|
136
137
|
size_t cnt;
|
|
137
138
|
|
|
138
|
-
args[0] = ULONG2NUM(
|
|
139
|
+
args[0] = ULONG2NUM(max);
|
|
139
140
|
rstr = rb_funcall2(buf->in.io, ox_readpartial_id, 1, args);
|
|
140
141
|
str = StringValuePtr(rstr);
|
|
141
|
-
|
|
142
|
+
// Clamp to the space actually requested. A misbehaving IO can return more
|
|
143
|
+
// than max bytes; copying it unclamped overflows the buffer (which starts
|
|
144
|
+
// on the C stack). Use the real byte length, not strlen(), so an embedded
|
|
145
|
+
// NUL does not cause a short copy that still leaves a longer string.
|
|
146
|
+
cnt = (size_t)RSTRING_LEN(rstr);
|
|
147
|
+
if (cnt > max) {
|
|
148
|
+
cnt = max;
|
|
149
|
+
}
|
|
142
150
|
// printf("*** read partial %lu bytes, str: '%s'\n", cnt, str);
|
|
143
|
-
|
|
151
|
+
memcpy(buf->tail, str, cnt);
|
|
144
152
|
buf->read_end = buf->tail + cnt;
|
|
145
153
|
|
|
146
154
|
return Qtrue;
|
|
@@ -151,14 +159,19 @@ static VALUE io_cb(VALUE rbuf) {
|
|
|
151
159
|
VALUE args[1];
|
|
152
160
|
VALUE rstr;
|
|
153
161
|
char *str;
|
|
162
|
+
size_t max = (size_t)(buf->end - buf->tail);
|
|
154
163
|
size_t cnt;
|
|
155
164
|
|
|
156
|
-
args[0] = ULONG2NUM(
|
|
165
|
+
args[0] = ULONG2NUM(max);
|
|
157
166
|
rstr = rb_funcall2(buf->in.io, ox_read_id, 1, args);
|
|
158
167
|
str = StringValuePtr(rstr);
|
|
159
|
-
|
|
168
|
+
// See partial_io_cb: clamp to the requested size to prevent overflow.
|
|
169
|
+
cnt = (size_t)RSTRING_LEN(rstr);
|
|
170
|
+
if (cnt > max) {
|
|
171
|
+
cnt = max;
|
|
172
|
+
}
|
|
160
173
|
// printf("*** read %lu bytes, str: '%s'\n", cnt, str);
|
|
161
|
-
|
|
174
|
+
memcpy(buf->tail, str, cnt);
|
|
162
175
|
buf->read_end = buf->tail + cnt;
|
|
163
176
|
|
|
164
177
|
return Qtrue;
|
|
@@ -180,9 +193,11 @@ static int read_from_fd(Buf buf) {
|
|
|
180
193
|
if (cnt < 0) {
|
|
181
194
|
ox_sax_drive_error(buf->dr, "failed to read from file");
|
|
182
195
|
return -1;
|
|
183
|
-
} else if (0
|
|
184
|
-
|
|
196
|
+
} else if (0 == cnt) { // EOF
|
|
197
|
+
return -1;
|
|
185
198
|
}
|
|
199
|
+
buf->read_end = buf->tail + cnt;
|
|
200
|
+
|
|
186
201
|
return 0;
|
|
187
202
|
}
|
|
188
203
|
|
data/ext/ox/sax_stack.h
CHANGED
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
|
|
14
14
|
#define STACK_INC 32
|
|
15
15
|
#define NV_BUF_MAX 64
|
|
16
|
+
#define MAX_ELEMENT_DEPTH 1000
|
|
16
17
|
|
|
17
18
|
typedef struct _nv {
|
|
18
19
|
char name_buf[NV_BUF_MAX];
|
|
@@ -40,6 +41,15 @@ inline static int stack_empty(NStack stack) {
|
|
|
40
41
|
}
|
|
41
42
|
|
|
42
43
|
inline static void stack_cleanup(NStack stack) {
|
|
44
|
+
Nv nv;
|
|
45
|
+
|
|
46
|
+
// Elements still on the stack were never popped, so their over-long names
|
|
47
|
+
// are still owned here.
|
|
48
|
+
for (nv = stack->head; nv < stack->tail; nv++) {
|
|
49
|
+
if (NULL != nv->name) {
|
|
50
|
+
xfree((char *)(nv->name));
|
|
51
|
+
}
|
|
52
|
+
}
|
|
43
53
|
if (stack->base != stack->head) {
|
|
44
54
|
xfree(stack->head);
|
|
45
55
|
}
|
|
@@ -50,6 +60,9 @@ inline static void stack_push(NStack stack, const char *name, size_t nlen, VALUE
|
|
|
50
60
|
size_t len = stack->end - stack->head;
|
|
51
61
|
size_t toff = stack->tail - stack->head;
|
|
52
62
|
|
|
63
|
+
if (MAX_ELEMENT_DEPTH < toff) {
|
|
64
|
+
rb_raise(ox_parse_error_class, "sax nesting depth exceeded.\n");
|
|
65
|
+
}
|
|
53
66
|
if (stack->base == stack->head) {
|
|
54
67
|
stack->head = ALLOC_N(struct _nv, len + STACK_INC);
|
|
55
68
|
memcpy(stack->head, stack->base, sizeof(struct _nv) * len);
|
data/ext/ox/time_conv.h
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/* time_conv.h
|
|
2
|
+
* Copyright (c) 2011, Peter Ohler
|
|
3
|
+
* All rights reserved.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
#ifndef OX_TIME_CONV_H
|
|
7
|
+
#define OX_TIME_CONV_H
|
|
8
|
+
|
|
9
|
+
#include <stdint.h>
|
|
10
|
+
#include <time.h>
|
|
11
|
+
|
|
12
|
+
// Howard Hinnant's civil_from_days and days_from_civil, which are inverses.
|
|
13
|
+
// Neither consults a timezone database, and that is the point: mktime() reads
|
|
14
|
+
// the wall clock as local time no matter what offset the document carries, and
|
|
15
|
+
// the Microsoft CRT returns -1 for anything before the epoch.
|
|
16
|
+
|
|
17
|
+
// Splits seconds since the epoch into a UTC date and wall clock.
|
|
18
|
+
inline static void
|
|
19
|
+
ox_civil_from_epoch(int64_t sec, long long *yearp, int *monp, int *dayp, int *hourp, int *minp, int *secp) {
|
|
20
|
+
long long days = (long long)sec / 86400;
|
|
21
|
+
long long rem = (long long)sec % 86400;
|
|
22
|
+
long long era, doe, yoe, doy, mp;
|
|
23
|
+
|
|
24
|
+
if (0 > rem) {
|
|
25
|
+
rem += 86400;
|
|
26
|
+
days--;
|
|
27
|
+
}
|
|
28
|
+
*hourp = (int)(rem / 3600);
|
|
29
|
+
*minp = (int)(rem % 3600 / 60);
|
|
30
|
+
*secp = (int)(rem % 60);
|
|
31
|
+
|
|
32
|
+
// The era starts in March so the leap day is last in the year and needs no
|
|
33
|
+
// special case.
|
|
34
|
+
days += 719468;
|
|
35
|
+
era = (0 <= days ? days : days - 146096) / 146097;
|
|
36
|
+
doe = days - era * 146097;
|
|
37
|
+
yoe = (doe - doe / 1460 + doe / 36524 - doe / 146096) / 365;
|
|
38
|
+
doy = doe - (365 * yoe + yoe / 4 - yoe / 100);
|
|
39
|
+
mp = (5 * doy + 2) / 153;
|
|
40
|
+
*dayp = (int)(doy - (153 * mp + 2) / 5 + 1);
|
|
41
|
+
*monp = (int)(10 > mp ? mp + 3 : mp - 9);
|
|
42
|
+
*yearp = yoe + era * 400 + (2 >= *monp);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Joins a UTC date and wall clock back into seconds since the epoch.
|
|
46
|
+
inline static int64_t ox_epoch_from_civil(long year, long mon, long day, long hour, long min, long sec) {
|
|
47
|
+
int64_t y = (int64_t)year;
|
|
48
|
+
int64_t era, yoe, doy, doe, days;
|
|
49
|
+
|
|
50
|
+
if (2 >= mon) {
|
|
51
|
+
y--;
|
|
52
|
+
}
|
|
53
|
+
era = (0 <= y ? y : y - 399) / 400;
|
|
54
|
+
yoe = y - era * 400;
|
|
55
|
+
doy = (153 * (mon + (2 < mon ? -3 : 9)) + 2) / 5 + day - 1;
|
|
56
|
+
doe = yoe * 365 + yoe / 4 - yoe / 100 + doy;
|
|
57
|
+
days = era * 146097 + doe - 719468;
|
|
58
|
+
|
|
59
|
+
return days * 86400 + (int64_t)hour * 3600 + (int64_t)min * 60 + (int64_t)sec;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
#endif /* OX_TIME_CONV_H */
|
data/ext/ox/xml_check.h
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/* xml_check.h
|
|
2
|
+
* Copyright (c) 2011, Peter Ohler
|
|
3
|
+
* All rights reserved.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
#ifndef OX_XML_CHECK_H
|
|
7
|
+
#define OX_XML_CHECK_H
|
|
8
|
+
|
|
9
|
+
#include <stdbool.h>
|
|
10
|
+
#include <stddef.h>
|
|
11
|
+
|
|
12
|
+
#include "err.h"
|
|
13
|
+
#include "xml_str.h"
|
|
14
|
+
|
|
15
|
+
/* What a writer asks before it writes anything, shared by dump.c and builder.c.
|
|
16
|
+
* The scans are in xml_str.h, which stays free of Ruby so it can be reasoned
|
|
17
|
+
* about on its own; these are the wrappers that turn an answer into a raise.
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
/* Raises on a character XML has no way to write, for a value that is copied
|
|
21
|
+
* through rather than escaped. The escape path answers the same question on its
|
|
22
|
+
* way past, but inside a CDATA section, a comment or an instruction a character
|
|
23
|
+
* reference is not expanded, so escaping is not an option for those.
|
|
24
|
+
*/
|
|
25
|
+
inline static void check_unescaped(const char *str, size_t size) {
|
|
26
|
+
const unsigned char *bad = xml_first_invalid((const unsigned char *)str, size);
|
|
27
|
+
|
|
28
|
+
if (NULL != bad) {
|
|
29
|
+
rb_raise(ox_syntax_error_class, "'\\#x%02x' is not a valid XML character.", *bad);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/* Refuses a name only for a character that could end it where it is written,
|
|
34
|
+
* the narrow rule #469 settled on, since anything else is odd but round trips.
|
|
35
|
+
* A byte XML can not hold at all keeps the message the values raise with.
|
|
36
|
+
*
|
|
37
|
+
* Escaping is not the answer here even where a table has an escape: a name is
|
|
38
|
+
* not a place a character reference is expanded, so < in a name reads back
|
|
39
|
+
* as four more characters rather than as the one that was asked for.
|
|
40
|
+
*/
|
|
41
|
+
inline static void check_name_chars(const char *str, size_t size, bool attr) {
|
|
42
|
+
const char ends = attr ? XML_NAME_ATTR : XML_NAME_ELEMENT;
|
|
43
|
+
const unsigned char *bad = xml_first_bad_name((const unsigned char *)str, size, ends);
|
|
44
|
+
const char *kind = attr ? "an attribute" : "an element";
|
|
45
|
+
|
|
46
|
+
if (NULL == bad) {
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
if (0x20 > *bad && '\t' != *bad && '\n' != *bad && '\r' != *bad) {
|
|
50
|
+
rb_raise(ox_syntax_error_class, "'\\#x%02x' is not a valid XML character.", *bad);
|
|
51
|
+
}
|
|
52
|
+
if (0x20 >= *bad) {
|
|
53
|
+
rb_raise(ox_syntax_error_class, "'\\#x%02x' can not be used in %s name.", *bad, kind);
|
|
54
|
+
}
|
|
55
|
+
rb_raise(ox_syntax_error_class, "'%c' can not be used in %s name.", *bad, kind);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
#endif /* OX_XML_CHECK_H */
|
data/ext/ox/xml_str.h
ADDED
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
/* xml_str.h
|
|
2
|
+
* Copyright (c) 2011, Peter Ohler
|
|
3
|
+
* All rights reserved.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
#ifndef OX_XML_STR_H
|
|
7
|
+
#define OX_XML_STR_H
|
|
8
|
+
|
|
9
|
+
#include <stdint.h>
|
|
10
|
+
#include <string.h>
|
|
11
|
+
|
|
12
|
+
#ifndef __has_builtin
|
|
13
|
+
#define __has_builtin(x) 0
|
|
14
|
+
#endif
|
|
15
|
+
|
|
16
|
+
#if __has_builtin(__builtin_memcpy)
|
|
17
|
+
#define HAVE_FAST_MEMCPY 1
|
|
18
|
+
|
|
19
|
+
inline static void fast_memcpy16(void *dest, const void *src, size_t n) {
|
|
20
|
+
char *d = (char *)dest;
|
|
21
|
+
const char *s = (const char *)src;
|
|
22
|
+
|
|
23
|
+
if (n >= 8) {
|
|
24
|
+
__builtin_memcpy(d, s, 8);
|
|
25
|
+
__builtin_memcpy(d + n - 8, s + n - 8, 8);
|
|
26
|
+
} else if (n >= 4) {
|
|
27
|
+
__builtin_memcpy(d, s, 4);
|
|
28
|
+
__builtin_memcpy(d + n - 4, s + n - 4, 4);
|
|
29
|
+
} else if (n >= 2) {
|
|
30
|
+
__builtin_memcpy(d, s, 2);
|
|
31
|
+
__builtin_memcpy(d + n - 2, s + n - 2, 2);
|
|
32
|
+
} else if (n >= 1) {
|
|
33
|
+
*d = *s;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
#endif
|
|
37
|
+
|
|
38
|
+
/* Shared helpers for the serializer escape path used by both dump.c and
|
|
39
|
+
* builder.c. The size scan and the escape loop both walk the source string a
|
|
40
|
+
* word at a time and only fall to a byte at a time when a word contains a byte
|
|
41
|
+
* that might need escaping.
|
|
42
|
+
*
|
|
43
|
+
* Each escape table maps a byte to the length of its serialized form held as an
|
|
44
|
+
* ASCII digit, '1' meaning the byte is copied through unchanged. The tables in
|
|
45
|
+
* use (xml_element_chars, xml_quote_chars / xml_attr_chars) escape only a
|
|
46
|
+
* subset of
|
|
47
|
+
*
|
|
48
|
+
* a byte below 0x20, '"', '\'', '&', '<', '>'
|
|
49
|
+
*
|
|
50
|
+
* so a word none of whose bytes are in that set is entirely pass-through and
|
|
51
|
+
* every one of its bytes is '1' in every table. The predicate below is that
|
|
52
|
+
* union. It is deliberately a superset: '"' and '\'' are pass-through in
|
|
53
|
+
* xml_element_chars, and 0x09, 0x0a and 0x0d are pass-through in every table,
|
|
54
|
+
* but flagging them only sends those bytes to the byte loop, which copies them
|
|
55
|
+
* through unchanged, so the output is identical. It must never miss a byte a
|
|
56
|
+
* table would escape, which is why the whole escaped union is covered even
|
|
57
|
+
* though no current table escapes '\''.
|
|
58
|
+
*/
|
|
59
|
+
|
|
60
|
+
#define XSTR_ONES 0x0101010101010101ULL
|
|
61
|
+
#define XSTR_HIGH 0x8080808080808080ULL
|
|
62
|
+
|
|
63
|
+
/* Sets the high bit of every byte of a word that is not guaranteed to be
|
|
64
|
+
* pass-through, that is a byte below 0x20, or a '"', '\'', '&', '<' or '>'.
|
|
65
|
+
*
|
|
66
|
+
* The byte below 0x20 test is the classic (v - ones*0x20) & ~v hasless, and the
|
|
67
|
+
* five character tests are haszero on v xored with the character broadcast to
|
|
68
|
+
* every byte. Bytes with the high bit already set, the UTF-8 lead and
|
|
69
|
+
* continuation bytes, are cleared by the & ~... term and so are never flagged.
|
|
70
|
+
*
|
|
71
|
+
* A borrow out of one byte can also flag the byte above it, but a borrow is
|
|
72
|
+
* only produced by a byte that already matched, so the lowest flagged byte is
|
|
73
|
+
* always a real match and there are never false negatives. That is what lets a
|
|
74
|
+
* zero result mean the whole word is pass-through, and the count of trailing
|
|
75
|
+
* zeros point at the first byte that has to go through the table.
|
|
76
|
+
*/
|
|
77
|
+
inline static uint64_t xml_bytes_of_interest(uint64_t v) {
|
|
78
|
+
uint64_t q = v ^ (XSTR_ONES * (uint64_t)'"');
|
|
79
|
+
uint64_t s = v ^ (XSTR_ONES * (uint64_t)'\'');
|
|
80
|
+
uint64_t a = v ^ (XSTR_ONES * (uint64_t)'&');
|
|
81
|
+
uint64_t l = v ^ (XSTR_ONES * (uint64_t)'<');
|
|
82
|
+
uint64_t g = v ^ (XSTR_ONES * (uint64_t)'>');
|
|
83
|
+
|
|
84
|
+
return (((v - XSTR_ONES * 0x20) & ~v) | ((q - XSTR_ONES) & ~q) | ((s - XSTR_ONES) & ~s) | ((a - XSTR_ONES) & ~a) |
|
|
85
|
+
((l - XSTR_ONES) & ~l) | ((g - XSTR_ONES) & ~g)) &
|
|
86
|
+
XSTR_HIGH;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/* Offset of the lowest flagged byte, which is the first byte of the word that
|
|
90
|
+
* has to go through the table. Only ever called with a non-zero mask.
|
|
91
|
+
*/
|
|
92
|
+
inline static int xml_first_of_interest(const unsigned char *s, uint64_t mask) {
|
|
93
|
+
#if defined(__GNUC__) || defined(__clang__)
|
|
94
|
+
#if defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__) && (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)
|
|
95
|
+
(void)s;
|
|
96
|
+
return (int)(__builtin_clzll(mask) >> 3);
|
|
97
|
+
#else
|
|
98
|
+
(void)s;
|
|
99
|
+
return (int)(__builtin_ctzll(mask) >> 3);
|
|
100
|
+
#endif
|
|
101
|
+
#else
|
|
102
|
+
int i;
|
|
103
|
+
|
|
104
|
+
(void)mask;
|
|
105
|
+
for (i = 0; i < 8; i++) {
|
|
106
|
+
unsigned char u = s[i];
|
|
107
|
+
|
|
108
|
+
if (u < 0x20 || '"' == u || '\'' == u || '&' == u || '<' == u || '>' == u) {
|
|
109
|
+
break;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
return i;
|
|
113
|
+
#endif
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/* Length of str once serialized through table, the sum of the table entries
|
|
117
|
+
* less len * '0' so that a pass-through byte counts as one. A word none of
|
|
118
|
+
* whose bytes need escaping contributes 8 * '1' with no table lookup at all;
|
|
119
|
+
* only words with a byte of interest are summed one byte at a time. The word
|
|
120
|
+
* loop is bounded by str + len so it never reads past the string.
|
|
121
|
+
*/
|
|
122
|
+
inline static size_t xml_str_len(const unsigned char *str, size_t len, const char *table) {
|
|
123
|
+
const unsigned char *end = str + len;
|
|
124
|
+
size_t size = 0;
|
|
125
|
+
|
|
126
|
+
while (str + 8 <= end) {
|
|
127
|
+
uint64_t v;
|
|
128
|
+
|
|
129
|
+
memcpy(&v, str, 8);
|
|
130
|
+
if (0 == xml_bytes_of_interest(v)) {
|
|
131
|
+
size += 8 * (size_t)'1';
|
|
132
|
+
} else {
|
|
133
|
+
int i;
|
|
134
|
+
|
|
135
|
+
for (i = 0; i < 8; i++) {
|
|
136
|
+
size += table[str[i]];
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
str += 8;
|
|
140
|
+
}
|
|
141
|
+
for (; str < end; str++) {
|
|
142
|
+
size += table[*str];
|
|
143
|
+
}
|
|
144
|
+
return size - len * (size_t)'0';
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/* First byte of str that no XML table lets through, or NULL.
|
|
148
|
+
*
|
|
149
|
+
* Every table marks the same set - the bytes under 0x20 other than 0x09, 0x0a
|
|
150
|
+
* and 0x0d - so this takes no table and answers for all of them. They are all
|
|
151
|
+
* under 0x20, which is the first term of xml_bytes_of_interest(), so a word
|
|
152
|
+
* that term does not flag holds none of them and is skipped whole.
|
|
153
|
+
*
|
|
154
|
+
* A caller that has to write a delimiter before the value it is escaping uses
|
|
155
|
+
* this to find out first, since the escape loop only reaches the byte after
|
|
156
|
+
* the delimiter is already out.
|
|
157
|
+
*/
|
|
158
|
+
inline static const unsigned char *xml_first_invalid(const unsigned char *str, size_t len) {
|
|
159
|
+
const unsigned char *end = str + len;
|
|
160
|
+
|
|
161
|
+
while (str + 8 <= end) {
|
|
162
|
+
uint64_t v;
|
|
163
|
+
|
|
164
|
+
memcpy(&v, str, 8);
|
|
165
|
+
if (0 != (((v - XSTR_ONES * 0x20) & ~v) & XSTR_HIGH)) {
|
|
166
|
+
int i;
|
|
167
|
+
|
|
168
|
+
for (i = 0; i < 8; i++) {
|
|
169
|
+
unsigned char c = str[i];
|
|
170
|
+
|
|
171
|
+
if (c < 0x20 && '\t' != c && '\n' != c && '\r' != c) {
|
|
172
|
+
return str + i;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
str += 8;
|
|
177
|
+
}
|
|
178
|
+
for (; str < end; str++) {
|
|
179
|
+
if (*str < 0x20 && '\t' != *str && '\n' != *str && '\r' != *str) {
|
|
180
|
+
return str;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
return NULL;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/* What a byte does to a name, in the same shape as the escape tables. '2' ends
|
|
187
|
+
* a name wherever one is written - a byte at or below a space, either white
|
|
188
|
+
* space or one XML can not hold at all, or one of the five characters a start
|
|
189
|
+
* tag is read with. '1' ends only an attribute name, the quote its value is
|
|
190
|
+
* written in. '0' is a byte a name can hold.
|
|
191
|
+
*/
|
|
192
|
+
#define XML_NAME_ELEMENT '2'
|
|
193
|
+
#define XML_NAME_ATTR '1'
|
|
194
|
+
|
|
195
|
+
static const char xml_name_chars[257] = "\
|
|
196
|
+
22222222222222222222222222222222\
|
|
197
|
+
20100020000000020000000000002220\
|
|
198
|
+
00000000000000000000000000000000\
|
|
199
|
+
00000000000000000000000000000000\
|
|
200
|
+
00000000000000000000000000000000\
|
|
201
|
+
00000000000000000000000000000000\
|
|
202
|
+
00000000000000000000000000000000\
|
|
203
|
+
00000000000000000000000000000000";
|
|
204
|
+
|
|
205
|
+
/* First byte of str that ends a name written where ends says it is written, or
|
|
206
|
+
* NULL. One load and one compare a byte, since every one of these is a name and
|
|
207
|
+
* a name is short enough that a word loop would spend more on its own setup
|
|
208
|
+
* than it saves.
|
|
209
|
+
*/
|
|
210
|
+
inline static const unsigned char *xml_first_bad_name(const unsigned char *str, size_t len, char ends) {
|
|
211
|
+
const unsigned char *end = str + len;
|
|
212
|
+
|
|
213
|
+
for (; str < end; str++) {
|
|
214
|
+
if (ends <= xml_name_chars[*str]) {
|
|
215
|
+
return str;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
return NULL;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/* A CDATA section ends at the first "]]>", so a value holding one closes its
|
|
222
|
+
* own section and the rest is read as markup. Both writers split each
|
|
223
|
+
* occurrence into "]]" + "]]>" + "<![CDATA[" + ">", which reads back as the
|
|
224
|
+
* same three characters, so this is what they scan with.
|
|
225
|
+
*/
|
|
226
|
+
#define CDATA_SPLIT_EXTRA 12
|
|
227
|
+
|
|
228
|
+
inline static const char *xml_cdata_end(const char *str, const char *end) {
|
|
229
|
+
for (; str + 3 <= end; str++) {
|
|
230
|
+
if (']' == *str && ']' == str[1] && '>' == str[2]) {
|
|
231
|
+
return str;
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
return NULL;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
inline static size_t xml_cdata_end_cnt(const char *str, const char *end) {
|
|
238
|
+
size_t cnt = 0;
|
|
239
|
+
|
|
240
|
+
while (NULL != (str = xml_cdata_end(str, end))) {
|
|
241
|
+
cnt++;
|
|
242
|
+
str += 2;
|
|
243
|
+
}
|
|
244
|
+
return cnt;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
#endif /* OX_XML_STR_H */
|
data/lib/ox/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: ox
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 2.14.
|
|
4
|
+
version: 2.14.29
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Peter Ohler
|
|
@@ -72,7 +72,10 @@ files:
|
|
|
72
72
|
- ext/ox/slotcache.h
|
|
73
73
|
- ext/ox/special.c
|
|
74
74
|
- ext/ox/special.h
|
|
75
|
+
- ext/ox/time_conv.h
|
|
75
76
|
- ext/ox/type.h
|
|
77
|
+
- ext/ox/xml_check.h
|
|
78
|
+
- ext/ox/xml_str.h
|
|
76
79
|
- lib/ox.rb
|
|
77
80
|
- lib/ox/bag.rb
|
|
78
81
|
- lib/ox/cdata.rb
|
|
@@ -122,7 +125,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
122
125
|
- !ruby/object:Gem::Version
|
|
123
126
|
version: '0'
|
|
124
127
|
requirements: []
|
|
125
|
-
rubygems_version: 4.0.
|
|
128
|
+
rubygems_version: 4.0.17
|
|
126
129
|
specification_version: 4
|
|
127
130
|
summary: A fast XML parser and object serializer.
|
|
128
131
|
test_files: []
|