@@ -32,13 +32,16 @@ def __init__(self) -> None:
3232 self ._closed : bool = False
3333 #: Whether we've handled the first split segment yet
3434 self ._first : bool = True
35+ #: The index of ``_buff`` at which to start the search for the next
36+ #: separator
37+ self ._index : int = 0
3538
3639 @abstractmethod
37- def _find_separator (self , data : AnyStr ) -> tuple [int , int ] | None :
40+ def _find_separator (self , data : AnyStr , index : int ) -> tuple [int , int ] | int :
3841 """
39- Find the first occurrence of a separator in ``data`` and return the
40- separator's starting and ending indices; if no separator is found,
41- return `None` .
42+ Find the first occurrence of a separator in ``data[index:] `` and return
43+ the separator's starting and ending indices; if no separator is found,
44+ return the index to start search from at subsequent calls .
4245 """
4346 ...
4447
@@ -66,17 +69,20 @@ def _output(self, item: AnyStr) -> None:
6669 def _split (self ) -> None :
6770 """Split up the current contents of `_buff`"""
6871 while self ._buff :
69- span = self ._find_separator (self ._buff )
70- if span is None :
72+ span = self ._find_separator (self ._buff , self ._index )
73+ if isinstance (span , int ):
74+ self ._index = span
7175 break
7276 start , end = span
7377 self ._handle_segment (self ._buff [:start ], first = self ._first )
7478 self ._first = False
7579 self ._handle_separator (self ._buff [start :end ])
7680 self ._buff = self ._buff [end :]
81+ self ._index = 0
7782 if self ._closed and self ._buff is not None :
7883 self ._handle_segment (self ._buff , first = self ._first , last = True )
7984 self ._buff = None
85+ self ._index = 0
8086
8187 def feed (self , data : AnyStr ) -> None :
8288 """
@@ -163,6 +169,7 @@ def reset(self) -> None:
163169 self ._hold = None
164170 self ._closed = False
165171 self ._first = True
172+ self ._index = 0
166173
167174 def getstate (self ) -> SplitterState [AnyStr ]:
168175 """Retrieve a representation of the splitter's current state"""
@@ -172,6 +179,7 @@ def getstate(self) -> SplitterState[AnyStr]:
172179 hold = self ._hold ,
173180 closed = self ._closed ,
174181 first = self ._first ,
182+ index = self ._index ,
175183 )
176184
177185 def setstate (self , state : SplitterState [AnyStr ]) -> None :
@@ -185,6 +193,7 @@ def setstate(self, state: SplitterState[AnyStr]) -> None:
185193 self ._hold = state .hold
186194 self ._closed = state .closed
187195 self ._first = state .first
196+ self ._index = state .index
188197
189198 def itersplit (self , iterable : Iterable [AnyStr ]) -> Iterator [AnyStr ]:
190199 """
@@ -247,11 +256,14 @@ def __init__(self, separator: AnyStr, retain: bool = False) -> None:
247256 self ._separator : AnyStr = separator
248257 self ._retain : bool = retain
249258
250- def _find_separator (self , data : AnyStr ) -> tuple [int , int ] | None :
259+ def _find_separator (self , data : AnyStr , index : int ) -> tuple [int , int ] | int :
251260 try :
252- i = data .index (self ._separator )
261+ i = data .index (self ._separator , index )
253262 except ValueError :
254- return None
263+ if data :
264+ return len (data ) - len (self ._separator ) + 1
265+ else :
266+ return 0
255267 else :
256268 return (i , i + len (self ._separator ))
257269
@@ -365,10 +377,15 @@ def __init__(self, retain: bool = False, translate: bool = True) -> None:
365377 self ._translate = translate
366378 self ._strs : NewlineStrs [AnyStr ] | None = None
367379
368- def _find_separator (self , data : AnyStr ) -> tuple [int , int ] | None :
380+ def _find_separator (self , data : AnyStr , index : int ) -> tuple [int , int ] | int :
369381 if self ._strs is None :
370382 self ._strs = NewlineStrs .for_type (data )
371- return self ._strs .search (data , self .closed )
383+ if (span := self ._strs .search (data , self .closed , pos = index )) is not None :
384+ return span
385+ elif data :
386+ return len (data ) - 1
387+ else :
388+ return 0
372389
373390 def _handle_segment (
374391 self , item : AnyStr , first : bool = False , last : bool = False # noqa: U100
@@ -415,12 +432,15 @@ def __init__(self, retain: bool = False, translate: bool = True) -> None:
415432 self ._retain = retain
416433 self ._translate = translate
417434
418- def _find_separator (self , data : str ) -> tuple [int , int ] | None :
419- m = self .SEP_RGX .search (data )
420- if m and not (m .group () == "\r " and m .end () == len (data ) and not self .closed ):
421- return m .span ()
435+ def _find_separator (self , data : str , index : int ) -> tuple [int , int ] | int :
436+ m = self .SEP_RGX .search (data , pos = index )
437+ if m :
438+ if m .group () == "\r " and m .end () == len (data ) and not self .closed :
439+ return len (data ) - 1
440+ else :
441+ return m .span ()
422442 else :
423- return None
443+ return len ( data )
424444
425445 def _handle_segment (
426446 self , item : str , first : bool = False , last : bool = False # noqa: U100
@@ -467,12 +487,15 @@ def __init__(self, retain: bool = False, translate: bool = True) -> None:
467487 def _split (self ) -> None :
468488 if self ._strs is None and self ._buff is not None :
469489 self ._strs = NewlineStrs .for_type (self ._buff )
470- pos = 0
471490 while self ._buff :
472491 assert self ._strs is not None
473492 if self ._hold is None :
474- span = self ._strs .search (self ._buff , self .closed , pos = pos )
493+ span = self ._strs .search (self ._buff , self .closed , pos = self . _index )
475494 if span is None :
495+ if self ._buff :
496+ self ._index = len (self ._buff ) - 1
497+ else :
498+ self ._index = 0
476499 break
477500 start , end = span
478501 if (self ._first and start == 0 ) or self ._strs .match (
@@ -486,13 +509,14 @@ def _split(self) -> None:
486509 self ._handle_separator (self ._buff [start :end ])
487510 self ._first = False
488511 self ._buff = self ._buff [end :]
512+ self ._index = 0
489513 else :
490514 if self ._translate and self ._buff [start :end ] != self ._strs .LF :
491515 self ._buff = (
492516 self ._buff [:start ] + self ._strs .LF + self ._buff [end :]
493517 )
494518 end = start + 1
495- pos = end
519+ self . _index = end
496520 else :
497521 end2 = self ._strs .match (self ._buff , self .closed )
498522 if end2 is None :
@@ -504,7 +528,7 @@ def _split(self) -> None:
504528 else :
505529 self ._handle_separator (self ._buff [:end2 ])
506530 self ._buff = self ._buff [end2 :]
507- pos = 0
531+ self . _index = 0
508532 if self ._closed and self ._buff is not None :
509533 assert self ._strs is not None
510534 if self ._buff :
@@ -517,7 +541,7 @@ def _split(self) -> None:
517541 self ._output (self ._hold )
518542 self ._buff = None
519543
520- def _find_separator (self , data : AnyStr ) -> tuple [int , int ] | None :
544+ def _find_separator (self , data : AnyStr , index : int ) -> tuple [int , int ] | int :
521545 raise NotImplementedError ("Not used by this subclass" ) # pragma: no cover
522546
523547 def _handle_segment (
@@ -661,3 +685,4 @@ class SplitterState(Generic[AnyStr]):
661685 hold : AnyStr | None
662686 closed : bool
663687 first : bool
688+ index : int
0 commit comments