@@ -26,6 +26,7 @@ def __init__(self, data_iter):
26
26
self .data_iter = data_iter
27
27
self .i = 0
28
28
self .chunk = b""
29
+ self .last_char = None
29
30
30
31
def read (self ):
31
32
"""Read the next character from the stream."""
@@ -39,16 +40,23 @@ def read(self):
39
40
self .i += 1
40
41
return char
41
42
42
- def fast_forward (self , closer ):
43
+ def fast_forward (self , closer , buffer = None ):
43
44
"""Read through the stream until the character is ``closer``, ``]``
44
45
(ending a list) or ``}`` (ending an object.) Intermediate lists and
45
46
objects are skipped."""
47
+
46
48
closer = ord (closer )
47
49
close_stack = [closer ]
48
50
count = 0
49
51
while close_stack :
50
52
char = self .read ()
51
53
count += 1
54
+ if buffer :
55
+ if count == len (buffer ):
56
+ new_buffer = bytearray (len (buffer ) + 32 )
57
+ new_buffer [: len (buffer )] = buffer
58
+ buffer = new_buffer
59
+ buffer [count ] = char
52
60
if char == close_stack [- 1 ]:
53
61
close_stack .pop ()
54
62
elif char == ord ('"' ):
@@ -63,6 +71,9 @@ def fast_forward(self, closer):
63
71
close_stack .append (ord ("}" ))
64
72
elif char == ord ("[" ):
65
73
close_stack .append (ord ("]" ))
74
+ if buffer :
75
+ value_string = bytes (memoryview (buffer )[: count + 1 ]).decode ("utf-8" )
76
+ return json .loads (value_string )
66
77
return False
67
78
68
79
def next_value (self , endswith = None ):
@@ -77,10 +88,10 @@ def next_value(self, endswith=None):
77
88
except EOFError :
78
89
char = endswith
79
90
if not in_string and (char == endswith or char in (ord ("]" ), ord ("}" ))):
91
+ self .last_char = char
80
92
if len (buf ) == 0 :
81
93
return None
82
94
value_string = bytes (buf ).decode ("utf-8" )
83
- # print(f"{repr(value_string)}, {endswith=}")
84
95
return json .loads (value_string )
85
96
if char == ord ("{" ):
86
97
return TransientObject (self )
@@ -94,40 +105,60 @@ def next_value(self, endswith=None):
94
105
buf .append (char )
95
106
96
107
97
- class Transient : # pylint: disable=too-few-public-methods
108
+ class Transient :
98
109
"""Transient object representing a JSON object."""
99
110
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
-
106
111
def __init__ (self , stream ):
112
+ self .active_child = None
107
113
self .data = stream
108
114
self .done = False
109
- self .active_child = None
115
+ self .has_read = False
116
+ self .finish_char = ""
117
+ self .start_char = ""
110
118
111
119
def finish (self ):
112
120
"""Consume all of the characters for this list from the stream."""
113
121
if not self .done :
114
122
if self .active_child :
115
123
self .active_child .finish ()
116
124
self .active_child = None
117
- self .data .fast_forward ("]" )
125
+ self .data .fast_forward (self . finish_char )
118
126
self .done = True
119
127
128
+ def as_object (self ):
129
+ """Consume all of the characters for this list from the stream and return as an object."""
130
+ if self .has_read :
131
+ raise BufferError ("Object has already been partly read." )
132
+
133
+ buffer = bytearray (32 )
134
+ buffer [0 ] = ord (self .start_char )
135
+ self .done = True
136
+ return self .data .fast_forward (self .finish_char , buffer )
137
+
138
+
139
+ class TransientList (Transient ):
140
+ """Transient object that acts like a list through the stream."""
141
+
142
+ def __init__ (self , stream ):
143
+ super ().__init__ (stream )
144
+ self .finish_char = "]"
145
+ self .start_char = "["
146
+
120
147
def __iter__ (self ):
121
148
return self
122
149
123
150
def __next__ (self ):
151
+ self .has_read = True
152
+
124
153
if self .active_child :
125
154
self .active_child .finish ()
126
155
self .done = self .data .fast_forward ("," )
127
156
self .active_child = None
128
157
if self .done :
129
158
raise StopIteration ()
130
159
next_value = self .data .next_value ("," )
160
+ if self .data .last_char == ord ("]" ):
161
+ self .done = True
131
162
if next_value is None :
132
163
self .done = True
133
164
raise StopIteration ()
@@ -140,42 +171,34 @@ class TransientObject(Transient):
140
171
"""Transient object that acts like a dictionary through the stream."""
141
172
142
173
def __init__ (self , stream ):
143
- self .data = stream
144
- self .done = False
145
- self .buf = array .array ("B" )
146
-
147
- self .active_child = None
148
-
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
174
+ super ().__init__ (stream )
175
+ self .finish_char = "}"
176
+ self .start_char = "{"
157
177
158
178
def __getitem__ (self , key ):
179
+ self .has_read = True
180
+
159
181
if self .active_child :
160
182
self .active_child .finish ()
161
183
self .done = self .data .fast_forward ("," )
162
184
self .active_child = None
163
185
if self .done :
164
- raise KeyError ()
186
+ raise KeyError (key )
165
187
166
- while True :
188
+ while not self . done :
167
189
current_key = self .data .next_value (":" )
168
190
if current_key is None :
169
- # print("object done", self)
170
191
self .done = True
171
192
break
172
193
if current_key == key :
173
194
next_value = self .data .next_value ("," )
195
+ if self .data .last_char in [ord ("}" ), ord ("]" )]:
196
+ self .done = True
174
197
if isinstance (next_value , Transient ):
175
198
self .active_child = next_value
176
199
return next_value
177
- self .data .fast_forward ("," )
178
- raise KeyError ()
200
+ self .done = self . data .fast_forward ("," )
201
+ raise KeyError (key )
179
202
180
203
181
204
def load (data_iter ):
0 commit comments