Skip to content
This repository was archived by the owner on Mar 8, 2018. It is now read-only.

Commit 85f3aa5

Browse files
author
R. Tyler Ballance
committed
Properly handle multiple unicode keys inside of a dictionary
Previously, py-yajl used `yajl_gen_number()` in order to print a non-escaped buffer to the output stream. The ENSURE_NOT_KEY macro inside of yajl_gen.c would cause entries to be dropped and an unchecked `yajl_gen_keys_must_be_strings` status would be returned. In order to work around this, a portion of code has been lifted from yajl_gen.c and added to yajl_hacks.c which adds the function `yajl_gen_raw_string()` which prints the buffer, unescaped, to the output stream without calling ENSURE_NOT_KEY. http://github.com/rtyler/py-yajl/issues/#issue/12 Change-Id: I75a71573c4949d04ad4d532c27f2b64486db906e
1 parent c619aea commit 85f3aa5

File tree

5 files changed

+142
-5
lines changed

5 files changed

+142
-5
lines changed

encoder.c

+7-5
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,10 @@
4040

4141
static const char *hexdigit = "0123456789abcdef";
4242

43+
/* Located in yajl_hacks.c */
44+
extern yajl_gen_status yajl_gen_raw_string(yajl_gen g,
45+
const unsigned char * str, unsigned int len);
46+
4347
static yajl_gen_status ProcessObject(_YajlEncoder *self, PyObject *object)
4448
{
4549
yajl_gen handle = (yajl_gen)(self->_generator);
@@ -62,10 +66,9 @@ static yajl_gen_status ProcessObject(_YajlEncoder *self, PyObject *object)
6266
* Create a buffer with enough space for code-points, preceeding and
6367
* following quotes and a null termination character
6468
*/
65-
char *buffer = (char *)(malloc(sizeof(char) * (3 + length * 6)));
69+
char *buffer = (char *)(malloc(sizeof(char) * (1 + length * 6)));
6670
unsigned int offset = 0;
6771

68-
buffer[offset++] = '\"';
6972
while (length-- > 0) {
7073
Py_UNICODE ch = *raw_unicode++;
7174

@@ -138,9 +141,8 @@ static yajl_gen_status ProcessObject(_YajlEncoder *self, PyObject *object)
138141
continue;
139142
}
140143
}
141-
buffer[offset++] = '\"';
142-
buffer[offset + 1] = '\0';
143-
return yajl_gen_number(handle, (const char *)(buffer), (unsigned int)(offset));
144+
buffer[offset] = '\0';
145+
return yajl_gen_raw_string(handle, (const unsigned char *)(buffer), (unsigned int)(offset));
144146
}
145147
#ifdef IS_PYTHON3
146148
if (PyBytes_Check(object)) {

setup.py

+1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
'yajl.c',
1717
'encoder.c',
1818
'decoder.c',
19+
'yajl_hacks.c',
1920
'yajl/src/yajl_alloc.c',
2021
'yajl/src/yajl_buf.c',
2122
'yajl/src/yajl.c',

tests/python2.py

+2
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,5 @@
99
IssueSevenTest_latin1_char = u'f\xe9in'
1010
# u'早安, 爸爸' # Good morning!
1111
IssueSevenTest_chinese_char = u'\u65e9\u5b89, \u7238\u7238'
12+
13+
IssueTwelveTest_dict = {u'a' : u'b', u'c' : u'd'}

tests/unit.py

+9
Original file line numberDiff line numberDiff line change
@@ -285,6 +285,15 @@ def testLong(self):
285285
result = yajl.loads(yajl.dumps(data))
286286
self.assertEquals({'1': 2}, result)
287287

288+
class IssueTwelveTest(unittest.TestCase):
289+
def runTest(self):
290+
normal = {'a' : 'b', 'c' : 'd'}
291+
self.assertEquals(yajl.dumps(normal), '{"a":"b","c":"d"}')
292+
293+
if not is_python3():
294+
from tests import python2
295+
self.assertEquals(yajl.dumps(python2.IssueTwelveTest_dict), '{"a":"b","c":"d"}')
296+
288297
if __name__ == '__main__':
289298
verbosity = '-v' in sys.argv and 2 or 1
290299
runner = unittest.TextTestRunner(verbosity=verbosity)

yajl_hacks.c

+123
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
/*
2+
* Copyright 2010, R. Tyler Ballance <[email protected]>
3+
*
4+
* Redistribution and use in source and binary forms, with or without
5+
* modification, are permitted provided that the following conditions are
6+
* met:
7+
*
8+
* 1. Redistributions of source code must retain the above copyright
9+
* notice, this list of conditions and the following disclaimer.
10+
*
11+
* 2. Redistributions in binary form must reproduce the above copyright
12+
* notice, this list of conditions and the following disclaimer in
13+
* the documentation and/or other materials provided with the
14+
* distribution.
15+
*
16+
* 3. Neither the name of R. Tyler Ballance nor the names of its
17+
* contributors may be used to endorse or promote products derived
18+
* from this software without specific prior written permission.
19+
*
20+
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
21+
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
22+
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
23+
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
24+
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
25+
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
26+
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27+
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
28+
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
29+
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30+
* POSSIBILITY OF SUCH DAMAGE.
31+
*/
32+
#include <yajl_encode.h>
33+
34+
35+
/*
36+
* This code was yanked largely from yajl_gen.c
37+
* it is merely a set of hacks
38+
*/
39+
40+
typedef enum {
41+
yajl_gen_start,
42+
yajl_gen_map_start,
43+
yajl_gen_map_key,
44+
yajl_gen_map_val,
45+
yajl_gen_array_start,
46+
yajl_gen_in_array,
47+
yajl_gen_complete,
48+
yajl_gen_error
49+
} yajl_gen_state;
50+
51+
struct yajl_gen_t
52+
{
53+
unsigned int depth;
54+
unsigned int pretty;
55+
const char * indentString;
56+
yajl_gen_state state[YAJL_MAX_DEPTH];
57+
yajl_print_t print;
58+
void * ctx; /* yajl_buf */
59+
/* memory allocation routines */
60+
yajl_alloc_funcs alloc;
61+
};
62+
63+
#define INSERT_SEP \
64+
if (g->state[g->depth] == yajl_gen_map_key || \
65+
g->state[g->depth] == yajl_gen_in_array) { \
66+
g->print(g->ctx, ",", 1); \
67+
if (g->pretty) g->print(g->ctx, "\n", 1); \
68+
} else if (g->state[g->depth] == yajl_gen_map_val) { \
69+
g->print(g->ctx, ":", 1); \
70+
if (g->pretty) g->print(g->ctx, " ", 1); \
71+
}
72+
73+
#define INSERT_WHITESPACE \
74+
if (g->pretty) { \
75+
if (g->state[g->depth] != yajl_gen_map_val) { \
76+
unsigned int _i; \
77+
for (_i=0;_i<g->depth;_i++) \
78+
g->print(g->ctx, g->indentString, \
79+
strlen(g->indentString)); \
80+
} \
81+
}
82+
/* check that we're not complete, or in error state. in a valid state
83+
* to be generating */
84+
#define ENSURE_VALID_STATE \
85+
if (g->state[g->depth] == yajl_gen_error) { \
86+
return yajl_gen_in_error_state;\
87+
} else if (g->state[g->depth] == yajl_gen_complete) { \
88+
return yajl_gen_generation_complete; \
89+
}
90+
91+
#define APPENDED_ATOM \
92+
switch (g->state[g->depth]) { \
93+
case yajl_gen_start: \
94+
g->state[g->depth] = yajl_gen_complete; \
95+
break; \
96+
case yajl_gen_map_start: \
97+
case yajl_gen_map_key: \
98+
g->state[g->depth] = yajl_gen_map_val; \
99+
break; \
100+
case yajl_gen_array_start: \
101+
g->state[g->depth] = yajl_gen_in_array; \
102+
break; \
103+
case yajl_gen_map_val: \
104+
g->state[g->depth] = yajl_gen_map_key; \
105+
break; \
106+
default: \
107+
break; \
108+
} \
109+
110+
#define FINAL_NEWLINE \
111+
if (g->pretty && g->state[g->depth] == yajl_gen_complete) \
112+
g->print(g->ctx, "\n", 1);
113+
114+
yajl_gen_status yajl_gen_raw_string(yajl_gen g, const unsigned char * str, unsigned int len)
115+
{
116+
ENSURE_VALID_STATE; INSERT_SEP; INSERT_WHITESPACE;
117+
g->print(g->ctx, "\"", 1);
118+
g->print(g->ctx, str, len);
119+
g->print(g->ctx, "\"", 1);
120+
APPENDED_ATOM;
121+
FINAL_NEWLINE;
122+
return yajl_gen_status_ok;
123+
}

0 commit comments

Comments
 (0)