@@ -26,6 +26,7 @@ def __init__(self, data_iter):
2626 self .data_iter = data_iter
2727 self .i = 0
2828 self .chunk = b""
29+ self .last_char = None
2930
3031 def read (self ):
3132 """Read the next character from the stream."""
@@ -39,16 +40,37 @@ def read(self):
3940 self .i += 1
4041 return char
4142
42- def fast_forward (self , closer ):
43- """Read through the stream until the character is ``closer``, ``]``
43+ def fast_forward (self , closer , * , return_object = False ):
44+ """
45+ Read through the stream until the character is ``closer``, ``]``
4446 (ending a list) or ``}`` (ending an object.) Intermediate lists and
45- objects are skipped."""
47+ objects are skipped.
48+
49+ :param str closer: the character to read until
50+ :param bool return_object: read until the closer,
51+ and then parse the data and return as an object
52+ """
53+
4654 closer = ord (closer )
4755 close_stack = [closer ]
4856 count = 0
57+
58+ buffer = None
59+ if return_object :
60+ buffer = bytearray (32 )
61+ # ] = 93, [ = 91
62+ # } = 125, { = 123
63+ buffer [0 ] = closer - 2
64+
4965 while close_stack :
5066 char = self .read ()
5167 count += 1
68+ if buffer :
69+ if count == len (buffer ):
70+ new_buffer = bytearray (len (buffer ) + 32 )
71+ new_buffer [: len (buffer )] = buffer
72+ buffer = new_buffer
73+ buffer [count ] = char
5274 if char == close_stack [- 1 ]:
5375 close_stack .pop ()
5476 elif char == ord ('"' ):
@@ -63,6 +85,9 @@ def fast_forward(self, closer):
6385 close_stack .append (ord ("}" ))
6486 elif char == ord ("[" ):
6587 close_stack .append (ord ("]" ))
88+ if buffer :
89+ value_string = bytes (memoryview (buffer )[: count + 1 ]).decode ("utf-8" )
90+ return json .loads (value_string )
6691 return False
6792
6893 def next_value (self , endswith = None ):
@@ -77,10 +102,10 @@ def next_value(self, endswith=None):
77102 except EOFError :
78103 char = endswith
79104 if not in_string and (char == endswith or char in (ord ("]" ), ord ("}" ))):
105+ self .last_char = char
80106 if len (buf ) == 0 :
81107 return None
82108 value_string = bytes (buf ).decode ("utf-8" )
83- # print(f"{repr(value_string)}, {endswith=}")
84109 return json .loads (value_string )
85110 if char == ord ("{" ):
86111 return TransientObject (self )
@@ -94,40 +119,56 @@ def next_value(self, endswith=None):
94119 buf .append (char )
95120
96121
97- class Transient : # pylint: disable=too-few-public-methods
122+ class Transient :
98123 """Transient object representing a JSON object."""
99124
100- # This is helpful for checking that something is a TransientList or TransientObject.
101-
102-
103- class TransientList (Transient ):
104- """Transient object that acts like a list through the stream."""
105-
106125 def __init__ (self , stream ):
126+ self .active_child = None
107127 self .data = stream
108128 self .done = False
109- self .active_child = None
129+ self .has_read = False
130+ self .finish_char = ""
110131
111132 def finish (self ):
112133 """Consume all of the characters for this list from the stream."""
113134 if not self .done :
114135 if self .active_child :
115136 self .active_child .finish ()
116137 self .active_child = None
117- self .data .fast_forward ("]" )
138+ self .data .fast_forward (self .finish_char )
139+ self .done = True
140+
141+ def as_object (self ):
142+ """Consume all of the characters for this list from the stream and return as an object."""
143+ if self .has_read :
144+ raise BufferError ("Object has already been partly read." )
145+
118146 self .done = True
147+ return self .data .fast_forward (self .finish_char , return_object = True )
148+
149+
150+ class TransientList (Transient ):
151+ """Transient object that acts like a list through the stream."""
152+
153+ def __init__ (self , stream ):
154+ super ().__init__ (stream )
155+ self .finish_char = "]"
119156
120157 def __iter__ (self ):
121158 return self
122159
123160 def __next__ (self ):
161+ self .has_read = True
162+
124163 if self .active_child :
125164 self .active_child .finish ()
126165 self .done = self .data .fast_forward ("," )
127166 self .active_child = None
128167 if self .done :
129168 raise StopIteration ()
130169 next_value = self .data .next_value ("," )
170+ if self .data .last_char == ord ("]" ):
171+ self .done = True
131172 if next_value is None :
132173 self .done = True
133174 raise StopIteration ()
@@ -140,42 +181,39 @@ class TransientObject(Transient):
140181 """Transient object that acts like a dictionary through the stream."""
141182
142183 def __init__ (self , stream ):
143- self . data = stream
144- self .done = False
145- self .buf = array . array ( "B" )
184+ super (). __init__ ( stream )
185+ self .finish_char = "}"
186+ self .active_child_key = None
146187
147- self .active_child = None
188+ def __getitem__ (self , key ):
189+ if self .active_child and self .active_child_key == key :
190+ return self .active_child
148191
149- def finish (self ):
150- """Consume all of the characters for this object from the stream."""
151- if not self .done :
152- if self .active_child :
153- self .active_child .finish ()
154- self .active_child = None
155- self .data .fast_forward ("}" )
156- self .done = True
192+ self .has_read = True
157193
158- def __getitem__ (self , key ):
159194 if self .active_child :
160195 self .active_child .finish ()
161196 self .done = self .data .fast_forward ("," )
162197 self .active_child = None
198+ self .active_child_key = None
163199 if self .done :
164- raise KeyError ()
200+ raise KeyError (key )
165201
166- while True :
202+ while not self . done :
167203 current_key = self .data .next_value (":" )
168204 if current_key is None :
169- # print("object done", self)
170205 self .done = True
171206 break
172207 if current_key == key :
173208 next_value = self .data .next_value ("," )
209+ if self .data .last_char == ord ("}" ):
210+ self .done = True
174211 if isinstance (next_value , Transient ):
175212 self .active_child = next_value
213+ self .active_child_key = key
176214 return next_value
177- self .data .fast_forward ("," )
178- raise KeyError ()
215+ self .done = self . data .fast_forward ("," )
216+ raise KeyError (key )
179217
180218
181219def load (data_iter ):
0 commit comments