@@ -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