Skip to content

Commit ce6bddf

Browse files
committed
Fix parsing RS-separated streams
Fix the parsing loop to use the proper interface, thus fixing parsing RS-separated streams (RFC 7464). Without this, the program would abort on assertion failure, like this: python3: src/jv_parse.c:684: jv_parser_set_buf: Assertion `(p->curr_buf == 0 || p->curr_buf_pos == p->curr_buf_length) && "previous buffer not exhausted"' failed.
1 parent 8f212d8 commit ce6bddf

File tree

2 files changed

+76
-10
lines changed

2 files changed

+76
-10
lines changed

jq.pyx

+9-10
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ cdef extern from "jv.h":
4444
jv_parser* jv_parser_new(int)
4545
void jv_parser_free(jv_parser*)
4646
void jv_parser_set_buf(jv_parser*, const char*, int, int)
47+
int jv_parser_remaining(jv_parser*)
4748
jv jv_parser_next(jv_parser*)
4849

4950
jv jv_parse(const char*)
@@ -175,11 +176,11 @@ cdef class _JSONParser(object):
175176
"""
176177
cdef jv value
177178
while True:
178-
# If we have no bytes to parse
179-
if self._bytes is None:
180-
# Ready some more
179+
# If the parser has no buffer set/left
180+
if not jv_parser_remaining(self._parser):
181+
# Supply it with some bytes
181182
self._ready_next_bytes()
182-
# Parse whatever we've readied, if any
183+
# Get next value from the parser
183184
value = jv_parser_next(self._parser)
184185
if jv_is_valid(value):
185186
if self._packed:
@@ -193,12 +194,10 @@ cdef class _JSONParser(object):
193194
message = jv_string_value(error_message).decode("utf8")
194195
jv_free(error_message)
195196
raise JSONParseError(message)
196-
else:
197-
jv_free(value)
198-
# If we didn't ready any bytes
199-
if self._bytes is None:
200-
raise StopIteration
201-
self._bytes = None
197+
jv_free(value)
198+
# If we supplied no bytes last time
199+
if self._bytes is None:
200+
raise StopIteration
202201

203202
cdef bint _ready_next_bytes(self) except 1:
204203
cdef char* cbytes

tests/jq_tests.py

+67
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,58 @@ def unicode_strings_can_be_used_as_input():
177177
)
178178

179179

180+
@istest
181+
def record_separator_character_accepted_in_input():
182+
assert_equal(
183+
[],
184+
list(jq.compile(".").input(text='\x1e'))
185+
)
186+
assert_equal(
187+
[],
188+
list(jq.compile(".").input(text='\x1e\x1e'))
189+
)
190+
assert_equal(
191+
[{}],
192+
list(jq.compile(".").input(text='\x1e{}'))
193+
)
194+
assert_equal(
195+
[{}],
196+
list(jq.compile(".").input(text='\x1e\x1e{}'))
197+
)
198+
assert_equal(
199+
[{}],
200+
list(jq.compile(".").input(text='{}\x1e'))
201+
)
202+
assert_equal(
203+
[{}],
204+
list(jq.compile(".").input(text='{}\x1e\x1e'))
205+
)
206+
assert_equal(
207+
[{}],
208+
list(jq.compile(".").input(text='\x1e{}\x1e'))
209+
)
210+
assert_equal(
211+
[{},[]],
212+
list(jq.compile(".").input(text='{}\x1e[]'))
213+
)
214+
assert_equal(
215+
[{},[]],
216+
list(jq.compile(".").input(text='{}\x1e\x1e[]'))
217+
)
218+
assert_equal(
219+
[{},[]],
220+
list(jq.compile(".").input(text='\x1e{}\x1e[]'))
221+
)
222+
assert_equal(
223+
[{},[]],
224+
list(jq.compile(".").input(text='{}\x1e[]\x1e'))
225+
)
226+
assert_equal(
227+
[{},[]],
228+
list(jq.compile(".").input(text='\x1e{}\x1e[]\x1e'))
229+
)
230+
231+
180232
@istest
181233
def unicode_strings_can_be_used_as_programs():
182234
assert_equal(
@@ -212,6 +264,21 @@ def parse_json_all_inputs_accepted():
212264
assert_equal(True, next(jq.parse_json(text=b"true")))
213265
assert_equal(True, next(jq.parse_json(text_iter=iter([b"true"]))))
214266

267+
@istest
268+
def parse_json_record_separator_character_accepted():
269+
assert_equal([], list(jq.parse_json(text='\x1e')))
270+
assert_equal([], list(jq.parse_json(text='\x1e\x1e')))
271+
assert_equal([{}], list(jq.parse_json(text='\x1e{}')))
272+
assert_equal([{}], list(jq.parse_json(text='\x1e\x1e{}')))
273+
assert_equal([{}], list(jq.parse_json(text='{}\x1e')))
274+
assert_equal([{}], list(jq.parse_json(text='{}\x1e\x1e')))
275+
assert_equal([{}], list(jq.parse_json(text='\x1e{}\x1e')))
276+
assert_equal([{},[]], list(jq.parse_json(text='{}\x1e[]')))
277+
assert_equal([{},[]], list(jq.parse_json(text='{}\x1e\x1e[]')))
278+
assert_equal([{},[]], list(jq.parse_json(text='\x1e{}\x1e[]')))
279+
assert_equal([{},[]], list(jq.parse_json(text='{}\x1e[]\x1e')))
280+
assert_equal([{},[]], list(jq.parse_json(text='\x1e{}\x1e[]\x1e')))
281+
215282
@istest
216283
def parse_json_file_works():
217284
fp = io.StringIO('{"abc": "def"}')

0 commit comments

Comments
 (0)