1*cda5da8dSAndroid Build Coastguard Worker# -*- Mode: Python; tab-width: 4 -*- 2*cda5da8dSAndroid Build Coastguard Worker# Id: asynchat.py,v 2.26 2000/09/07 22:29:26 rushing Exp 3*cda5da8dSAndroid Build Coastguard Worker# Author: Sam Rushing <[email protected]> 4*cda5da8dSAndroid Build Coastguard Worker 5*cda5da8dSAndroid Build Coastguard Worker# ====================================================================== 6*cda5da8dSAndroid Build Coastguard Worker# Copyright 1996 by Sam Rushing 7*cda5da8dSAndroid Build Coastguard Worker# 8*cda5da8dSAndroid Build Coastguard Worker# All Rights Reserved 9*cda5da8dSAndroid Build Coastguard Worker# 10*cda5da8dSAndroid Build Coastguard Worker# Permission to use, copy, modify, and distribute this software and 11*cda5da8dSAndroid Build Coastguard Worker# its documentation for any purpose and without fee is hereby 12*cda5da8dSAndroid Build Coastguard Worker# granted, provided that the above copyright notice appear in all 13*cda5da8dSAndroid Build Coastguard Worker# copies and that both that copyright notice and this permission 14*cda5da8dSAndroid Build Coastguard Worker# notice appear in supporting documentation, and that the name of Sam 15*cda5da8dSAndroid Build Coastguard Worker# Rushing not be used in advertising or publicity pertaining to 16*cda5da8dSAndroid Build Coastguard Worker# distribution of the software without specific, written prior 17*cda5da8dSAndroid Build Coastguard Worker# permission. 18*cda5da8dSAndroid Build Coastguard Worker# 19*cda5da8dSAndroid Build Coastguard Worker# SAM RUSHING DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, 20*cda5da8dSAndroid Build Coastguard Worker# INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN 21*cda5da8dSAndroid Build Coastguard Worker# NO EVENT SHALL SAM RUSHING BE LIABLE FOR ANY SPECIAL, INDIRECT OR 22*cda5da8dSAndroid Build Coastguard Worker# CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS 23*cda5da8dSAndroid Build Coastguard Worker# OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, 24*cda5da8dSAndroid Build Coastguard Worker# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN 25*cda5da8dSAndroid Build Coastguard Worker# CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 26*cda5da8dSAndroid Build Coastguard Worker# ====================================================================== 27*cda5da8dSAndroid Build Coastguard Worker 28*cda5da8dSAndroid Build Coastguard Workerr"""A class supporting chat-style (command/response) protocols. 29*cda5da8dSAndroid Build Coastguard Worker 30*cda5da8dSAndroid Build Coastguard WorkerThis class adds support for 'chat' style protocols - where one side 31*cda5da8dSAndroid Build Coastguard Workersends a 'command', and the other sends a response (examples would be 32*cda5da8dSAndroid Build Coastguard Workerthe common internet protocols - smtp, nntp, ftp, etc..). 33*cda5da8dSAndroid Build Coastguard Worker 34*cda5da8dSAndroid Build Coastguard WorkerThe handle_read() method looks at the input stream for the current 35*cda5da8dSAndroid Build Coastguard Worker'terminator' (usually '\r\n' for single-line responses, '\r\n.\r\n' 36*cda5da8dSAndroid Build Coastguard Workerfor multi-line output), calling self.found_terminator() on its 37*cda5da8dSAndroid Build Coastguard Workerreceipt. 38*cda5da8dSAndroid Build Coastguard Worker 39*cda5da8dSAndroid Build Coastguard Workerfor example: 40*cda5da8dSAndroid Build Coastguard WorkerSay you build an async nntp client using this class. At the start 41*cda5da8dSAndroid Build Coastguard Workerof the connection, you'll have self.terminator set to '\r\n', in 42*cda5da8dSAndroid Build Coastguard Workerorder to process the single-line greeting. Just before issuing a 43*cda5da8dSAndroid Build Coastguard Worker'LIST' command you'll set it to '\r\n.\r\n'. The output of the LIST 44*cda5da8dSAndroid Build Coastguard Workercommand will be accumulated (using your own 'collect_incoming_data' 45*cda5da8dSAndroid Build Coastguard Workermethod) up to the terminator, and then control will be returned to 46*cda5da8dSAndroid Build Coastguard Workeryou - by calling your self.found_terminator() method. 47*cda5da8dSAndroid Build Coastguard Worker""" 48*cda5da8dSAndroid Build Coastguard Workerimport asyncore 49*cda5da8dSAndroid Build Coastguard Workerfrom collections import deque 50*cda5da8dSAndroid Build Coastguard Worker 51*cda5da8dSAndroid Build Coastguard Workerfrom warnings import _deprecated 52*cda5da8dSAndroid Build Coastguard Worker 53*cda5da8dSAndroid Build Coastguard Worker_DEPRECATION_MSG = ('The {name} module is deprecated and will be removed in ' 54*cda5da8dSAndroid Build Coastguard Worker 'Python {remove}. The recommended replacement is asyncio') 55*cda5da8dSAndroid Build Coastguard Worker_deprecated(__name__, _DEPRECATION_MSG, remove=(3, 12)) 56*cda5da8dSAndroid Build Coastguard Worker 57*cda5da8dSAndroid Build Coastguard Worker 58*cda5da8dSAndroid Build Coastguard Worker 59*cda5da8dSAndroid Build Coastguard Workerclass async_chat(asyncore.dispatcher): 60*cda5da8dSAndroid Build Coastguard Worker """This is an abstract class. You must derive from this class, and add 61*cda5da8dSAndroid Build Coastguard Worker the two methods collect_incoming_data() and found_terminator()""" 62*cda5da8dSAndroid Build Coastguard Worker 63*cda5da8dSAndroid Build Coastguard Worker # these are overridable defaults 64*cda5da8dSAndroid Build Coastguard Worker 65*cda5da8dSAndroid Build Coastguard Worker ac_in_buffer_size = 65536 66*cda5da8dSAndroid Build Coastguard Worker ac_out_buffer_size = 65536 67*cda5da8dSAndroid Build Coastguard Worker 68*cda5da8dSAndroid Build Coastguard Worker # we don't want to enable the use of encoding by default, because that is a 69*cda5da8dSAndroid Build Coastguard Worker # sign of an application bug that we don't want to pass silently 70*cda5da8dSAndroid Build Coastguard Worker 71*cda5da8dSAndroid Build Coastguard Worker use_encoding = 0 72*cda5da8dSAndroid Build Coastguard Worker encoding = 'latin-1' 73*cda5da8dSAndroid Build Coastguard Worker 74*cda5da8dSAndroid Build Coastguard Worker def __init__(self, sock=None, map=None): 75*cda5da8dSAndroid Build Coastguard Worker # for string terminator matching 76*cda5da8dSAndroid Build Coastguard Worker self.ac_in_buffer = b'' 77*cda5da8dSAndroid Build Coastguard Worker 78*cda5da8dSAndroid Build Coastguard Worker # we use a list here rather than io.BytesIO for a few reasons... 79*cda5da8dSAndroid Build Coastguard Worker # del lst[:] is faster than bio.truncate(0) 80*cda5da8dSAndroid Build Coastguard Worker # lst = [] is faster than bio.truncate(0) 81*cda5da8dSAndroid Build Coastguard Worker self.incoming = [] 82*cda5da8dSAndroid Build Coastguard Worker 83*cda5da8dSAndroid Build Coastguard Worker # we toss the use of the "simple producer" and replace it with 84*cda5da8dSAndroid Build Coastguard Worker # a pure deque, which the original fifo was a wrapping of 85*cda5da8dSAndroid Build Coastguard Worker self.producer_fifo = deque() 86*cda5da8dSAndroid Build Coastguard Worker asyncore.dispatcher.__init__(self, sock, map) 87*cda5da8dSAndroid Build Coastguard Worker 88*cda5da8dSAndroid Build Coastguard Worker def collect_incoming_data(self, data): 89*cda5da8dSAndroid Build Coastguard Worker raise NotImplementedError("must be implemented in subclass") 90*cda5da8dSAndroid Build Coastguard Worker 91*cda5da8dSAndroid Build Coastguard Worker def _collect_incoming_data(self, data): 92*cda5da8dSAndroid Build Coastguard Worker self.incoming.append(data) 93*cda5da8dSAndroid Build Coastguard Worker 94*cda5da8dSAndroid Build Coastguard Worker def _get_data(self): 95*cda5da8dSAndroid Build Coastguard Worker d = b''.join(self.incoming) 96*cda5da8dSAndroid Build Coastguard Worker del self.incoming[:] 97*cda5da8dSAndroid Build Coastguard Worker return d 98*cda5da8dSAndroid Build Coastguard Worker 99*cda5da8dSAndroid Build Coastguard Worker def found_terminator(self): 100*cda5da8dSAndroid Build Coastguard Worker raise NotImplementedError("must be implemented in subclass") 101*cda5da8dSAndroid Build Coastguard Worker 102*cda5da8dSAndroid Build Coastguard Worker def set_terminator(self, term): 103*cda5da8dSAndroid Build Coastguard Worker """Set the input delimiter. 104*cda5da8dSAndroid Build Coastguard Worker 105*cda5da8dSAndroid Build Coastguard Worker Can be a fixed string of any length, an integer, or None. 106*cda5da8dSAndroid Build Coastguard Worker """ 107*cda5da8dSAndroid Build Coastguard Worker if isinstance(term, str) and self.use_encoding: 108*cda5da8dSAndroid Build Coastguard Worker term = bytes(term, self.encoding) 109*cda5da8dSAndroid Build Coastguard Worker elif isinstance(term, int) and term < 0: 110*cda5da8dSAndroid Build Coastguard Worker raise ValueError('the number of received bytes must be positive') 111*cda5da8dSAndroid Build Coastguard Worker self.terminator = term 112*cda5da8dSAndroid Build Coastguard Worker 113*cda5da8dSAndroid Build Coastguard Worker def get_terminator(self): 114*cda5da8dSAndroid Build Coastguard Worker return self.terminator 115*cda5da8dSAndroid Build Coastguard Worker 116*cda5da8dSAndroid Build Coastguard Worker # grab some more data from the socket, 117*cda5da8dSAndroid Build Coastguard Worker # throw it to the collector method, 118*cda5da8dSAndroid Build Coastguard Worker # check for the terminator, 119*cda5da8dSAndroid Build Coastguard Worker # if found, transition to the next state. 120*cda5da8dSAndroid Build Coastguard Worker 121*cda5da8dSAndroid Build Coastguard Worker def handle_read(self): 122*cda5da8dSAndroid Build Coastguard Worker 123*cda5da8dSAndroid Build Coastguard Worker try: 124*cda5da8dSAndroid Build Coastguard Worker data = self.recv(self.ac_in_buffer_size) 125*cda5da8dSAndroid Build Coastguard Worker except BlockingIOError: 126*cda5da8dSAndroid Build Coastguard Worker return 127*cda5da8dSAndroid Build Coastguard Worker except OSError: 128*cda5da8dSAndroid Build Coastguard Worker self.handle_error() 129*cda5da8dSAndroid Build Coastguard Worker return 130*cda5da8dSAndroid Build Coastguard Worker 131*cda5da8dSAndroid Build Coastguard Worker if isinstance(data, str) and self.use_encoding: 132*cda5da8dSAndroid Build Coastguard Worker data = bytes(str, self.encoding) 133*cda5da8dSAndroid Build Coastguard Worker self.ac_in_buffer = self.ac_in_buffer + data 134*cda5da8dSAndroid Build Coastguard Worker 135*cda5da8dSAndroid Build Coastguard Worker # Continue to search for self.terminator in self.ac_in_buffer, 136*cda5da8dSAndroid Build Coastguard Worker # while calling self.collect_incoming_data. The while loop 137*cda5da8dSAndroid Build Coastguard Worker # is necessary because we might read several data+terminator 138*cda5da8dSAndroid Build Coastguard Worker # combos with a single recv(4096). 139*cda5da8dSAndroid Build Coastguard Worker 140*cda5da8dSAndroid Build Coastguard Worker while self.ac_in_buffer: 141*cda5da8dSAndroid Build Coastguard Worker lb = len(self.ac_in_buffer) 142*cda5da8dSAndroid Build Coastguard Worker terminator = self.get_terminator() 143*cda5da8dSAndroid Build Coastguard Worker if not terminator: 144*cda5da8dSAndroid Build Coastguard Worker # no terminator, collect it all 145*cda5da8dSAndroid Build Coastguard Worker self.collect_incoming_data(self.ac_in_buffer) 146*cda5da8dSAndroid Build Coastguard Worker self.ac_in_buffer = b'' 147*cda5da8dSAndroid Build Coastguard Worker elif isinstance(terminator, int): 148*cda5da8dSAndroid Build Coastguard Worker # numeric terminator 149*cda5da8dSAndroid Build Coastguard Worker n = terminator 150*cda5da8dSAndroid Build Coastguard Worker if lb < n: 151*cda5da8dSAndroid Build Coastguard Worker self.collect_incoming_data(self.ac_in_buffer) 152*cda5da8dSAndroid Build Coastguard Worker self.ac_in_buffer = b'' 153*cda5da8dSAndroid Build Coastguard Worker self.terminator = self.terminator - lb 154*cda5da8dSAndroid Build Coastguard Worker else: 155*cda5da8dSAndroid Build Coastguard Worker self.collect_incoming_data(self.ac_in_buffer[:n]) 156*cda5da8dSAndroid Build Coastguard Worker self.ac_in_buffer = self.ac_in_buffer[n:] 157*cda5da8dSAndroid Build Coastguard Worker self.terminator = 0 158*cda5da8dSAndroid Build Coastguard Worker self.found_terminator() 159*cda5da8dSAndroid Build Coastguard Worker else: 160*cda5da8dSAndroid Build Coastguard Worker # 3 cases: 161*cda5da8dSAndroid Build Coastguard Worker # 1) end of buffer matches terminator exactly: 162*cda5da8dSAndroid Build Coastguard Worker # collect data, transition 163*cda5da8dSAndroid Build Coastguard Worker # 2) end of buffer matches some prefix: 164*cda5da8dSAndroid Build Coastguard Worker # collect data to the prefix 165*cda5da8dSAndroid Build Coastguard Worker # 3) end of buffer does not match any prefix: 166*cda5da8dSAndroid Build Coastguard Worker # collect data 167*cda5da8dSAndroid Build Coastguard Worker terminator_len = len(terminator) 168*cda5da8dSAndroid Build Coastguard Worker index = self.ac_in_buffer.find(terminator) 169*cda5da8dSAndroid Build Coastguard Worker if index != -1: 170*cda5da8dSAndroid Build Coastguard Worker # we found the terminator 171*cda5da8dSAndroid Build Coastguard Worker if index > 0: 172*cda5da8dSAndroid Build Coastguard Worker # don't bother reporting the empty string 173*cda5da8dSAndroid Build Coastguard Worker # (source of subtle bugs) 174*cda5da8dSAndroid Build Coastguard Worker self.collect_incoming_data(self.ac_in_buffer[:index]) 175*cda5da8dSAndroid Build Coastguard Worker self.ac_in_buffer = self.ac_in_buffer[index+terminator_len:] 176*cda5da8dSAndroid Build Coastguard Worker # This does the Right Thing if the terminator 177*cda5da8dSAndroid Build Coastguard Worker # is changed here. 178*cda5da8dSAndroid Build Coastguard Worker self.found_terminator() 179*cda5da8dSAndroid Build Coastguard Worker else: 180*cda5da8dSAndroid Build Coastguard Worker # check for a prefix of the terminator 181*cda5da8dSAndroid Build Coastguard Worker index = find_prefix_at_end(self.ac_in_buffer, terminator) 182*cda5da8dSAndroid Build Coastguard Worker if index: 183*cda5da8dSAndroid Build Coastguard Worker if index != lb: 184*cda5da8dSAndroid Build Coastguard Worker # we found a prefix, collect up to the prefix 185*cda5da8dSAndroid Build Coastguard Worker self.collect_incoming_data(self.ac_in_buffer[:-index]) 186*cda5da8dSAndroid Build Coastguard Worker self.ac_in_buffer = self.ac_in_buffer[-index:] 187*cda5da8dSAndroid Build Coastguard Worker break 188*cda5da8dSAndroid Build Coastguard Worker else: 189*cda5da8dSAndroid Build Coastguard Worker # no prefix, collect it all 190*cda5da8dSAndroid Build Coastguard Worker self.collect_incoming_data(self.ac_in_buffer) 191*cda5da8dSAndroid Build Coastguard Worker self.ac_in_buffer = b'' 192*cda5da8dSAndroid Build Coastguard Worker 193*cda5da8dSAndroid Build Coastguard Worker def handle_write(self): 194*cda5da8dSAndroid Build Coastguard Worker self.initiate_send() 195*cda5da8dSAndroid Build Coastguard Worker 196*cda5da8dSAndroid Build Coastguard Worker def handle_close(self): 197*cda5da8dSAndroid Build Coastguard Worker self.close() 198*cda5da8dSAndroid Build Coastguard Worker 199*cda5da8dSAndroid Build Coastguard Worker def push(self, data): 200*cda5da8dSAndroid Build Coastguard Worker if not isinstance(data, (bytes, bytearray, memoryview)): 201*cda5da8dSAndroid Build Coastguard Worker raise TypeError('data argument must be byte-ish (%r)', 202*cda5da8dSAndroid Build Coastguard Worker type(data)) 203*cda5da8dSAndroid Build Coastguard Worker sabs = self.ac_out_buffer_size 204*cda5da8dSAndroid Build Coastguard Worker if len(data) > sabs: 205*cda5da8dSAndroid Build Coastguard Worker for i in range(0, len(data), sabs): 206*cda5da8dSAndroid Build Coastguard Worker self.producer_fifo.append(data[i:i+sabs]) 207*cda5da8dSAndroid Build Coastguard Worker else: 208*cda5da8dSAndroid Build Coastguard Worker self.producer_fifo.append(data) 209*cda5da8dSAndroid Build Coastguard Worker self.initiate_send() 210*cda5da8dSAndroid Build Coastguard Worker 211*cda5da8dSAndroid Build Coastguard Worker def push_with_producer(self, producer): 212*cda5da8dSAndroid Build Coastguard Worker self.producer_fifo.append(producer) 213*cda5da8dSAndroid Build Coastguard Worker self.initiate_send() 214*cda5da8dSAndroid Build Coastguard Worker 215*cda5da8dSAndroid Build Coastguard Worker def readable(self): 216*cda5da8dSAndroid Build Coastguard Worker "predicate for inclusion in the readable for select()" 217*cda5da8dSAndroid Build Coastguard Worker # cannot use the old predicate, it violates the claim of the 218*cda5da8dSAndroid Build Coastguard Worker # set_terminator method. 219*cda5da8dSAndroid Build Coastguard Worker 220*cda5da8dSAndroid Build Coastguard Worker # return (len(self.ac_in_buffer) <= self.ac_in_buffer_size) 221*cda5da8dSAndroid Build Coastguard Worker return 1 222*cda5da8dSAndroid Build Coastguard Worker 223*cda5da8dSAndroid Build Coastguard Worker def writable(self): 224*cda5da8dSAndroid Build Coastguard Worker "predicate for inclusion in the writable for select()" 225*cda5da8dSAndroid Build Coastguard Worker return self.producer_fifo or (not self.connected) 226*cda5da8dSAndroid Build Coastguard Worker 227*cda5da8dSAndroid Build Coastguard Worker def close_when_done(self): 228*cda5da8dSAndroid Build Coastguard Worker "automatically close this channel once the outgoing queue is empty" 229*cda5da8dSAndroid Build Coastguard Worker self.producer_fifo.append(None) 230*cda5da8dSAndroid Build Coastguard Worker 231*cda5da8dSAndroid Build Coastguard Worker def initiate_send(self): 232*cda5da8dSAndroid Build Coastguard Worker while self.producer_fifo and self.connected: 233*cda5da8dSAndroid Build Coastguard Worker first = self.producer_fifo[0] 234*cda5da8dSAndroid Build Coastguard Worker # handle empty string/buffer or None entry 235*cda5da8dSAndroid Build Coastguard Worker if not first: 236*cda5da8dSAndroid Build Coastguard Worker del self.producer_fifo[0] 237*cda5da8dSAndroid Build Coastguard Worker if first is None: 238*cda5da8dSAndroid Build Coastguard Worker self.handle_close() 239*cda5da8dSAndroid Build Coastguard Worker return 240*cda5da8dSAndroid Build Coastguard Worker 241*cda5da8dSAndroid Build Coastguard Worker # handle classic producer behavior 242*cda5da8dSAndroid Build Coastguard Worker obs = self.ac_out_buffer_size 243*cda5da8dSAndroid Build Coastguard Worker try: 244*cda5da8dSAndroid Build Coastguard Worker data = first[:obs] 245*cda5da8dSAndroid Build Coastguard Worker except TypeError: 246*cda5da8dSAndroid Build Coastguard Worker data = first.more() 247*cda5da8dSAndroid Build Coastguard Worker if data: 248*cda5da8dSAndroid Build Coastguard Worker self.producer_fifo.appendleft(data) 249*cda5da8dSAndroid Build Coastguard Worker else: 250*cda5da8dSAndroid Build Coastguard Worker del self.producer_fifo[0] 251*cda5da8dSAndroid Build Coastguard Worker continue 252*cda5da8dSAndroid Build Coastguard Worker 253*cda5da8dSAndroid Build Coastguard Worker if isinstance(data, str) and self.use_encoding: 254*cda5da8dSAndroid Build Coastguard Worker data = bytes(data, self.encoding) 255*cda5da8dSAndroid Build Coastguard Worker 256*cda5da8dSAndroid Build Coastguard Worker # send the data 257*cda5da8dSAndroid Build Coastguard Worker try: 258*cda5da8dSAndroid Build Coastguard Worker num_sent = self.send(data) 259*cda5da8dSAndroid Build Coastguard Worker except OSError: 260*cda5da8dSAndroid Build Coastguard Worker self.handle_error() 261*cda5da8dSAndroid Build Coastguard Worker return 262*cda5da8dSAndroid Build Coastguard Worker 263*cda5da8dSAndroid Build Coastguard Worker if num_sent: 264*cda5da8dSAndroid Build Coastguard Worker if num_sent < len(data) or obs < len(first): 265*cda5da8dSAndroid Build Coastguard Worker self.producer_fifo[0] = first[num_sent:] 266*cda5da8dSAndroid Build Coastguard Worker else: 267*cda5da8dSAndroid Build Coastguard Worker del self.producer_fifo[0] 268*cda5da8dSAndroid Build Coastguard Worker # we tried to send some actual data 269*cda5da8dSAndroid Build Coastguard Worker return 270*cda5da8dSAndroid Build Coastguard Worker 271*cda5da8dSAndroid Build Coastguard Worker def discard_buffers(self): 272*cda5da8dSAndroid Build Coastguard Worker # Emergencies only! 273*cda5da8dSAndroid Build Coastguard Worker self.ac_in_buffer = b'' 274*cda5da8dSAndroid Build Coastguard Worker del self.incoming[:] 275*cda5da8dSAndroid Build Coastguard Worker self.producer_fifo.clear() 276*cda5da8dSAndroid Build Coastguard Worker 277*cda5da8dSAndroid Build Coastguard Worker 278*cda5da8dSAndroid Build Coastguard Workerclass simple_producer: 279*cda5da8dSAndroid Build Coastguard Worker 280*cda5da8dSAndroid Build Coastguard Worker def __init__(self, data, buffer_size=512): 281*cda5da8dSAndroid Build Coastguard Worker self.data = data 282*cda5da8dSAndroid Build Coastguard Worker self.buffer_size = buffer_size 283*cda5da8dSAndroid Build Coastguard Worker 284*cda5da8dSAndroid Build Coastguard Worker def more(self): 285*cda5da8dSAndroid Build Coastguard Worker if len(self.data) > self.buffer_size: 286*cda5da8dSAndroid Build Coastguard Worker result = self.data[:self.buffer_size] 287*cda5da8dSAndroid Build Coastguard Worker self.data = self.data[self.buffer_size:] 288*cda5da8dSAndroid Build Coastguard Worker return result 289*cda5da8dSAndroid Build Coastguard Worker else: 290*cda5da8dSAndroid Build Coastguard Worker result = self.data 291*cda5da8dSAndroid Build Coastguard Worker self.data = b'' 292*cda5da8dSAndroid Build Coastguard Worker return result 293*cda5da8dSAndroid Build Coastguard Worker 294*cda5da8dSAndroid Build Coastguard Worker 295*cda5da8dSAndroid Build Coastguard Worker# Given 'haystack', see if any prefix of 'needle' is at its end. This 296*cda5da8dSAndroid Build Coastguard Worker# assumes an exact match has already been checked. Return the number of 297*cda5da8dSAndroid Build Coastguard Worker# characters matched. 298*cda5da8dSAndroid Build Coastguard Worker# for example: 299*cda5da8dSAndroid Build Coastguard Worker# f_p_a_e("qwerty\r", "\r\n") => 1 300*cda5da8dSAndroid Build Coastguard Worker# f_p_a_e("qwertydkjf", "\r\n") => 0 301*cda5da8dSAndroid Build Coastguard Worker# f_p_a_e("qwerty\r\n", "\r\n") => <undefined> 302*cda5da8dSAndroid Build Coastguard Worker 303*cda5da8dSAndroid Build Coastguard Worker# this could maybe be made faster with a computed regex? 304*cda5da8dSAndroid Build Coastguard Worker# [answer: no; circa Python-2.0, Jan 2001] 305*cda5da8dSAndroid Build Coastguard Worker# new python: 28961/s 306*cda5da8dSAndroid Build Coastguard Worker# old python: 18307/s 307*cda5da8dSAndroid Build Coastguard Worker# re: 12820/s 308*cda5da8dSAndroid Build Coastguard Worker# regex: 14035/s 309*cda5da8dSAndroid Build Coastguard Worker 310*cda5da8dSAndroid Build Coastguard Workerdef find_prefix_at_end(haystack, needle): 311*cda5da8dSAndroid Build Coastguard Worker l = len(needle) - 1 312*cda5da8dSAndroid Build Coastguard Worker while l and not haystack.endswith(needle[:l]): 313*cda5da8dSAndroid Build Coastguard Worker l -= 1 314*cda5da8dSAndroid Build Coastguard Worker return l 315