We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
request.files access became slower
The access to `request.files['file']` became slower when I upgraded to Werkzeug 2.3. A 12.1 MB file used to take a couple of ms, but now it takes about 8 seconds.

```python
from werkzeug import Request, Response, run_simple

@Request.application
def app(request):
    if request.method == "POST":
        return Response(request.files["file"].filename)

    return Response("""<!doctype html>
<meta charset=utf-8>
<form method=post enctype=multipart/form-data>
<input type=file name=file>
<input type=submit>
</form>
""", mimetype="text/html")

run_simple("127.0.0.1", 5000, app)
```

Environment:

- Python version: 3.10
- Werkzeug version: 2.3.0

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `src/werkzeug/sansio/multipart.py`
Content:
```
1 from __future__ import annotations
2 
3 import re
4 import typing as t
5 from dataclasses import dataclass
6 from enum import auto
7 from enum import Enum
8 
9 from ..datastructures import Headers
10 from ..exceptions import RequestEntityTooLarge
11 from ..http import parse_options_header
12 
13 
14 class Event:
15     pass
16 
17 
18 @dataclass(frozen=True)
19 class Preamble(Event):
20     data: bytes
21 
22 
23 @dataclass(frozen=True)
24 class Field(Event):
25     name: str
26     headers: Headers
27 
28 
29 @dataclass(frozen=True)
30 class File(Event):
31     name: str
32     filename: str
33     headers: Headers
34 
35 
36 @dataclass(frozen=True)
37 class Data(Event):
38     data: bytes
39     more_data: bool
40 
41 
42 @dataclass(frozen=True)
43 class Epilogue(Event):
44     data: bytes
45 
46 
47 class NeedData(Event):
48     pass
49 
50 
51 NEED_DATA = NeedData()
52 
53 
54 class State(Enum):
55     PREAMBLE = auto()
56     PART = auto()
57     DATA = auto()
58     DATA_START = auto()
59     EPILOGUE = auto()
60     COMPLETE = auto()
61 
62 
63 # Multipart line breaks MUST be CRLF (\r\n) by RFC-7578, except that
64 # many implementations break this and either use CR or LF alone.
65 LINE_BREAK = b"(?:\r\n|\n|\r)"
66 BLANK_LINE_RE = re.compile(b"(?:\r\n\r\n|\r\r|\n\n)", re.MULTILINE)
67 LINE_BREAK_RE = re.compile(LINE_BREAK, re.MULTILINE)
68 # Header values can be continued via a space or tab after the linebreak, as
69 # per RFC2231
70 HEADER_CONTINUATION_RE = re.compile(b"%s[ \t]" % LINE_BREAK, re.MULTILINE)
71 # This must be long enough to contain any line breaks plus any
72 # additional boundary markers (--) such that they will be found in a
73 # subsequent search
74 SEARCH_EXTRA_LENGTH = 8
75 
76 
77 class MultipartDecoder:
78     """Decodes a multipart message as bytes into Python events.
79 
80     The part data is returned as available to allow the caller to save
81     the data from memory to disk, if desired.
82     """
83 
84     def __init__(
85         self,
86         boundary: bytes,
87         max_form_memory_size: int | None = None,
88         *,
89         max_parts: int | None = None,
90     ) -> None:
91         self.buffer = bytearray()
92         self.complete = False
93         self.max_form_memory_size = max_form_memory_size
94         self.max_parts = max_parts
95         self.state = State.PREAMBLE
96         self.boundary = boundary
97 
98         # Note in the below \h i.e. horizontal whitespace is used
99         # as [^\S\n\r] as \h isn't supported in python.
100 
101         # The preamble must end with a boundary where the boundary is
102         # prefixed by a line break, RFC2046. Except that many
103         # implementations including Werkzeug's tests omit the line
104         # break prefix. In addition the first boundary could be the
105         # epilogue boundary (for empty form-data) hence the matching
106         # group to understand if it is an epilogue boundary.
107         self.preamble_re = re.compile(
108             rb"%s?--%s(--[^\S\n\r]*%s?|[^\S\n\r]*%s)"
109             % (LINE_BREAK, re.escape(boundary), LINE_BREAK, LINE_BREAK),
110             re.MULTILINE,
111         )
112         # A boundary must include a line break prefix and suffix, and
113         # may include trailing whitespace. In addition the boundary
114         # could be the epilogue boundary hence the matching group to
115         # understand if it is an epilogue boundary.
116         self.boundary_re = re.compile(
117             rb"%s--%s(--[^\S\n\r]*%s?|[^\S\n\r]*%s)"
118             % (LINE_BREAK, re.escape(boundary), LINE_BREAK, LINE_BREAK),
119             re.MULTILINE,
120         )
121         self._search_position = 0
122         self._parts_decoded = 0
123 
124     def last_newline(self) -> int:
125         try:
126             last_nl = self.buffer.rindex(b"\n")
127         except ValueError:
128             last_nl = len(self.buffer)
129         try:
130             last_cr = self.buffer.rindex(b"\r")
131         except ValueError:
132             last_cr = len(self.buffer)
133 
134         return min(last_nl, last_cr)
135 
136     def receive_data(self, data: bytes | None) -> None:
137         if data is None:
138             self.complete = True
139         elif (
140             self.max_form_memory_size is not None
141             and len(self.buffer) + len(data) > self.max_form_memory_size
142         ):
143             raise RequestEntityTooLarge()
144         else:
145             self.buffer.extend(data)
146 
147     def next_event(self) -> Event:
148         event: Event = NEED_DATA
149 
150         if self.state == State.PREAMBLE:
151             match = self.preamble_re.search(self.buffer, self._search_position)
152             if match is not None:
153                 if match.group(1).startswith(b"--"):
154                     self.state = State.EPILOGUE
155                 else:
156                     self.state = State.PART
157                 data = bytes(self.buffer[: match.start()])
158                 del self.buffer[: match.end()]
159                 event = Preamble(data=data)
160                 self._search_position = 0
161             else:
162                 # Update the search start position to be equal to the
163                 # current buffer length (already searched) minus a
164                 # safe buffer for part of the search target.
165                 self._search_position = max(
166                     0, len(self.buffer) - len(self.boundary) - SEARCH_EXTRA_LENGTH
167                 )
168 
169         elif self.state == State.PART:
170             match = BLANK_LINE_RE.search(self.buffer, self._search_position)
171             if match is not None:
172                 headers = self._parse_headers(self.buffer[: match.start()])
173                 # The final header ends with a single CRLF, however a
174                 # blank line indicates the start of the
175                 # body. Therefore the end is after the first CRLF.
176                 headers_end = (match.start() + match.end()) // 2
177                 del self.buffer[:headers_end]
178 
179                 if "content-disposition" not in headers:
180                     raise ValueError("Missing Content-Disposition header")
181 
182                 disposition, extra = parse_options_header(
183                     headers["content-disposition"]
184                 )
185                 name = t.cast(str, extra.get("name"))
186                 filename = extra.get("filename")
187                 if filename is not None:
188                     event = File(
189                         filename=filename,
190                         headers=headers,
191                         name=name,
192                     )
193                 else:
194                     event = Field(
195                         headers=headers,
196                         name=name,
197                     )
198                 self.state = State.DATA_START
199                 self._search_position = 0
200                 self._parts_decoded += 1
201 
202                 if self.max_parts is not None and self._parts_decoded > self.max_parts:
203                     raise RequestEntityTooLarge()
204             else:
205                 # Update the search start position to be equal to the
206                 # current buffer length (already searched) minus a
207                 # safe buffer for part of the search target.
208                 self._search_position = max(0, len(self.buffer) - SEARCH_EXTRA_LENGTH)
209 
210         elif self.state == State.DATA_START:
211             data, del_index, more_data = self._parse_data(self.buffer, start=True)
212             del self.buffer[:del_index]
213             event = Data(data=data, more_data=more_data)
214             if more_data:
215                 self.state = State.DATA
216 
217         elif self.state == State.DATA:
218             data, del_index, more_data = self._parse_data(self.buffer, start=False)
219             del self.buffer[:del_index]
220             if data or not more_data:
221                 event = Data(data=data, more_data=more_data)
222 
223         elif self.state == State.EPILOGUE and self.complete:
224             event = Epilogue(data=bytes(self.buffer))
225             del self.buffer[:]
226             self.state = State.COMPLETE
227 
228         if self.complete and isinstance(event, NeedData):
229             raise ValueError(f"Invalid form-data cannot parse beyond {self.state}")
230 
231         return event
232 
233     def _parse_headers(self, data: bytes) -> Headers:
234         headers: list[tuple[str, str]] = []
235         # Merge the continued headers into one line
236         data = HEADER_CONTINUATION_RE.sub(b" ", data)
237         # Now there is one header per line
238         for line in data.splitlines():
239             line = line.strip()
240 
241             if line != b"":
242                 name, _, value = line.decode().partition(":")
243                 headers.append((name.strip(), value.strip()))
244         return Headers(headers)
245 
246     def _parse_data(self, data: bytes, *, start: bool) -> tuple[bytes, int, bool]:
247         # Body parts must start with CRLF (or CR or LF)
248         if start:
249             match = LINE_BREAK_RE.match(data)
250             data_start = t.cast(t.Match[bytes], match).end()
251         else:
252             data_start = 0
253 
254         match = self.boundary_re.search(data)
255         if match is not None:
256             if match.group(1).startswith(b"--"):
257                 self.state = State.EPILOGUE
258             else:
259                 self.state = State.PART
260             data_end = match.start()
261             del_index = match.end()
262         else:
263             data_end = del_index = self.last_newline()
264         more_data = match is None
265 
266         return bytes(data[data_start:data_end]), del_index, more_data
267 
268 
269 class MultipartEncoder:
270     def __init__(self, boundary: bytes) -> None:
271         self.boundary = boundary
272         self.state = State.PREAMBLE
273 
274     def send_event(self, event: Event) -> bytes:
275         if isinstance(event, Preamble) and self.state == State.PREAMBLE:
276             self.state = State.PART
277             return event.data
278         elif isinstance(event, (Field, File)) and self.state in {
279             State.PREAMBLE,
280             State.PART,
281             State.DATA,
282         }:
283             data = b"\r\n--" + self.boundary + b"\r\n"
284             data += b'Content-Disposition: form-data; name="%s"' % event.name.encode()
285             if isinstance(event, File):
286                 data += b'; filename="%s"' % event.filename.encode()
287             data += b"\r\n"
288             for name, value in t.cast(Field, event).headers:
289                 if name.lower() != "content-disposition":
290                     data += f"{name}: {value}\r\n".encode()
291             self.state = State.DATA_START
292             return data
293         elif isinstance(event, Data) and self.state == State.DATA_START:
294             self.state = State.DATA
295             if len(event.data) > 0:
296                 return b"\r\n" + event.data
297             else:
298                 return event.data
299         elif isinstance(event, Data) and self.state == State.DATA:
300             return event.data
301         elif isinstance(event, Epilogue):
302             self.state = State.COMPLETE
303             return b"\r\n--" + self.boundary + b"--\r\n" + event.data
304         else:
305             raise ValueError(f"Cannot generate {event} in state: {self.state}")
306
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/src/werkzeug/sansio/multipart.py b/src/werkzeug/sansio/multipart.py
--- a/src/werkzeug/sansio/multipart.py
+++ b/src/werkzeug/sansio/multipart.py
@@ -121,15 +121,15 @@
         self._search_position = 0
         self._parts_decoded = 0
 
-    def last_newline(self) -> int:
+    def last_newline(self, data: bytes) -> int:
         try:
-            last_nl = self.buffer.rindex(b"\n")
+            last_nl = data.rindex(b"\n")
         except ValueError:
-            last_nl = len(self.buffer)
+            last_nl = len(data)
         try:
-            last_cr = self.buffer.rindex(b"\r")
+            last_cr = data.rindex(b"\r")
         except ValueError:
-            last_cr = len(self.buffer)
+            last_cr = len(data)
 
         return min(last_nl, last_cr)
 
@@ -251,17 +251,25 @@
         else:
             data_start = 0
 
-        match = self.boundary_re.search(data)
-        if match is not None:
-            if match.group(1).startswith(b"--"):
-                self.state = State.EPILOGUE
-            else:
-                self.state = State.PART
-            data_end = match.start()
-            del_index = match.end()
+        if self.buffer.find(b"--" + self.boundary) == -1:
+            # No complete boundary in the buffer, but there may be
+            # a partial boundary at the end. As the boundary
+            # starts with either a nl or cr find the earliest and
+            # return up to that as data.
+            data_end = del_index = self.last_newline(data[data_start:])
+            more_data = True
         else:
-            data_end = del_index = self.last_newline()
-        more_data = match is None
+            match = self.boundary_re.search(data)
+            if match is not None:
+                if match.group(1).startswith(b"--"):
+                    self.state = State.EPILOGUE
+                else:
+                    self.state = State.PART
+                data_end = match.start()
+                del_index = match.end()
+            else:
+                data_end = del_index = self.last_newline(data[data_start:])
+            more_data = match is None
 
         return bytes(data[data_start:data_end]), del_index, more_data

{"golden_diff": "diff --git a/src/werkzeug/sansio/multipart.py b/src/werkzeug/sansio/multipart.py\n--- a/src/werkzeug/sansio/multipart.py\n+++ b/src/werkzeug/sansio/multipart.py\n@@ -121,15 +121,15 @@\n         self._search_position = 0\n         self._parts_decoded = 0\n \n-    def last_newline(self) -> int:\n+    def last_newline(self, data: bytes) -> int:\n         try:\n-            last_nl = self.buffer.rindex(b\"\\n\")\n+            last_nl = data.rindex(b\"\\n\")\n         except ValueError:\n-            last_nl = len(self.buffer)\n+            last_nl = len(data)\n         try:\n-            last_cr = self.buffer.rindex(b\"\\r\")\n+            last_cr = data.rindex(b\"\\r\")\n         except ValueError:\n-            last_cr = len(self.buffer)\n+            last_cr = len(data)\n \n         return min(last_nl, last_cr)\n \n@@ -251,17 +251,25 @@\n         else:\n             data_start = 0\n \n-        match = self.boundary_re.search(data)\n-        if match is not None:\n-            if match.group(1).startswith(b\"--\"):\n-                self.state = State.EPILOGUE\n-            else:\n-                self.state = State.PART\n-            data_end = match.start()\n-            del_index = match.end()\n+        if self.buffer.find(b\"--\" + self.boundary) == -1:\n+            # No complete boundary in the buffer, but there may be\n+            # a partial boundary at the end. As the boundary\n+            # starts with either a nl or cr find the earliest and\n+            # return up to that as data.\n+            data_end = del_index = self.last_newline(data[data_start:])\n+            more_data = True\n         else:\n-            data_end = del_index = self.last_newline()\n-        more_data = match is None\n+            match = self.boundary_re.search(data)\n+            if match is not None:\n+                if match.group(1).startswith(b\"--\"):\n+                    self.state = State.EPILOGUE\n+                else:\n+                    self.state = State.PART\n+                data_end = match.start()\n+                del_index = match.end()\n+            else:\n+                data_end = del_index = self.last_newline(data[data_start:])\n+            more_data = match is None\n \n         return bytes(data[data_start:data_end]), del_index, more_data\n", "issue": "request.files access became slower\nThe access to `request.files['file']` became slower when I upgraded to Werkzeug 2.3. A 12.1 MB file used to take a couple of ms, but now it takes about 8 seconds.\r\n\r\n```python\r\nfrom werkzeug import Request, Response, run_simple\r\n\r\n@Request.application\r\ndef app(request):\r\n    if request.method == \"POST\":\r\n        return Response(request.files[\"file\"].filename)\r\n\r\n    return Response(\"\"\"<!doctype html>\r\n<meta charset=utf-8>\r\n<form method=post enctype=multipart/form-data>\r\n<input type=file name=file>\r\n<input type=submit>\r\n</form>\r\n\"\"\", mimetype=\"text/html\")\r\n\r\nrun_simple(\"127.0.0.1\", 5000, app)\r\n```\r\n\r\nEnvironment:\r\n\r\n- Python version: 3.10\r\n- Werkzeug version: 2.3.0\n", "before_files": [{"content": "from __future__ import annotations\n\nimport re\nimport typing as t\nfrom dataclasses import dataclass\nfrom enum import auto\nfrom enum import Enum\n\nfrom ..datastructures import Headers\nfrom ..exceptions import RequestEntityTooLarge\nfrom ..http import parse_options_header\n\n\nclass Event:\n    pass\n\n\n@dataclass(frozen=True)\nclass Preamble(Event):\n    data: bytes\n\n\n@dataclass(frozen=True)\nclass Field(Event):\n    name: str\n    headers: Headers\n\n\n@dataclass(frozen=True)\nclass File(Event):\n    name: str\n    filename: str\n    headers: Headers\n\n\n@dataclass(frozen=True)\nclass Data(Event):\n    data: bytes\n    more_data: bool\n\n\n@dataclass(frozen=True)\nclass Epilogue(Event):\n    data: bytes\n\n\nclass NeedData(Event):\n    pass\n\n\nNEED_DATA = NeedData()\n\n\nclass State(Enum):\n    PREAMBLE = auto()\n    PART = auto()\n    DATA = auto()\n    DATA_START = auto()\n    EPILOGUE = auto()\n    COMPLETE = auto()\n\n\n# Multipart line breaks MUST be CRLF (\\r\\n) by RFC-7578, except that\n# many implementations break this and either use CR or LF alone.\nLINE_BREAK = b\"(?:\\r\\n|\\n|\\r)\"\nBLANK_LINE_RE = re.compile(b\"(?:\\r\\n\\r\\n|\\r\\r|\\n\\n)\", re.MULTILINE)\nLINE_BREAK_RE = re.compile(LINE_BREAK, re.MULTILINE)\n# Header values can be continued via a space or tab after the linebreak, as\n# per RFC2231\nHEADER_CONTINUATION_RE = re.compile(b\"%s[ \\t]\" % LINE_BREAK, re.MULTILINE)\n# This must be long enough to contain any line breaks plus any\n# additional boundary markers (--) such that they will be found in a\n# subsequent search\nSEARCH_EXTRA_LENGTH = 8\n\n\nclass MultipartDecoder:\n    \"\"\"Decodes a multipart message as bytes into Python events.\n\n    The part data is returned as available to allow the caller to save\n    the data from memory to disk, if desired.\n    \"\"\"\n\n    def __init__(\n        self,\n        boundary: bytes,\n        max_form_memory_size: int | None = None,\n        *,\n        max_parts: int | None = None,\n    ) -> None:\n        self.buffer = bytearray()\n        self.complete = False\n        self.max_form_memory_size = max_form_memory_size\n        self.max_parts = max_parts\n        self.state = State.PREAMBLE\n        self.boundary = boundary\n\n        # Note in the below \\h i.e. horizontal whitespace is used\n        # as [^\\S\\n\\r] as \\h isn't supported in python.\n\n        # The preamble must end with a boundary where the boundary is\n        # prefixed by a line break, RFC2046. Except that many\n        # implementations including Werkzeug's tests omit the line\n        # break prefix. In addition the first boundary could be the\n        # epilogue boundary (for empty form-data) hence the matching\n        # group to understand if it is an epilogue boundary.\n        self.preamble_re = re.compile(\n            rb\"%s?--%s(--[^\\S\\n\\r]*%s?|[^\\S\\n\\r]*%s)\"\n            % (LINE_BREAK, re.escape(boundary), LINE_BREAK, LINE_BREAK),\n            re.MULTILINE,\n        )\n        # A boundary must include a line break prefix and suffix, and\n        # may include trailing whitespace. In addition the boundary\n        # could be the epilogue boundary hence the matching group to\n        # understand if it is an epilogue boundary.\n        self.boundary_re = re.compile(\n            rb\"%s--%s(--[^\\S\\n\\r]*%s?|[^\\S\\n\\r]*%s)\"\n            % (LINE_BREAK, re.escape(boundary), LINE_BREAK, LINE_BREAK),\n            re.MULTILINE,\n        )\n        self._search_position = 0\n        self._parts_decoded = 0\n\n    def last_newline(self) -> int:\n        try:\n            last_nl = self.buffer.rindex(b\"\\n\")\n        except ValueError:\n            last_nl = len(self.buffer)\n        try:\n            last_cr = self.buffer.rindex(b\"\\r\")\n        except ValueError:\n            last_cr = len(self.buffer)\n\n        return min(last_nl, last_cr)\n\n    def receive_data(self, data: bytes | None) -> None:\n        if data is None:\n            self.complete = True\n        elif (\n            self.max_form_memory_size is not None\n            and len(self.buffer) + len(data) > self.max_form_memory_size\n        ):\n            raise RequestEntityTooLarge()\n        else:\n            self.buffer.extend(data)\n\n    def next_event(self) -> Event:\n        event: Event = NEED_DATA\n\n        if self.state == State.PREAMBLE:\n            match = self.preamble_re.search(self.buffer, self._search_position)\n            if match is not None:\n                if match.group(1).startswith(b\"--\"):\n                    self.state = State.EPILOGUE\n                else:\n                    self.state = State.PART\n                data = bytes(self.buffer[: match.start()])\n                del self.buffer[: match.end()]\n                event = Preamble(data=data)\n                self._search_position = 0\n            else:\n                # Update the search start position to be equal to the\n                # current buffer length (already searched) minus a\n                # safe buffer for part of the search target.\n                self._search_position = max(\n                    0, len(self.buffer) - len(self.boundary) - SEARCH_EXTRA_LENGTH\n                )\n\n        elif self.state == State.PART:\n            match = BLANK_LINE_RE.search(self.buffer, self._search_position)\n            if match is not None:\n                headers = self._parse_headers(self.buffer[: match.start()])\n                # The final header ends with a single CRLF, however a\n                # blank line indicates the start of the\n                # body. Therefore the end is after the first CRLF.\n                headers_end = (match.start() + match.end()) // 2\n                del self.buffer[:headers_end]\n\n                if \"content-disposition\" not in headers:\n                    raise ValueError(\"Missing Content-Disposition header\")\n\n                disposition, extra = parse_options_header(\n                    headers[\"content-disposition\"]\n                )\n                name = t.cast(str, extra.get(\"name\"))\n                filename = extra.get(\"filename\")\n                if filename is not None:\n                    event = File(\n                        filename=filename,\n                        headers=headers,\n                        name=name,\n                    )\n                else:\n                    event = Field(\n                        headers=headers,\n                        name=name,\n                    )\n                self.state = State.DATA_START\n                self._search_position = 0\n                self._parts_decoded += 1\n\n                if self.max_parts is not None and self._parts_decoded > self.max_parts:\n                    raise RequestEntityTooLarge()\n            else:\n                # Update the search start position to be equal to the\n                # current buffer length (already searched) minus a\n                # safe buffer for part of the search target.\n                self._search_position = max(0, len(self.buffer) - SEARCH_EXTRA_LENGTH)\n\n        elif self.state == State.DATA_START:\n            data, del_index, more_data = self._parse_data(self.buffer, start=True)\n            del self.buffer[:del_index]\n            event = Data(data=data, more_data=more_data)\n            if more_data:\n                self.state = State.DATA\n\n        elif self.state == State.DATA:\n            data, del_index, more_data = self._parse_data(self.buffer, start=False)\n            del self.buffer[:del_index]\n            if data or not more_data:\n                event = Data(data=data, more_data=more_data)\n\n        elif self.state == State.EPILOGUE and self.complete:\n            event = Epilogue(data=bytes(self.buffer))\n            del self.buffer[:]\n            self.state = State.COMPLETE\n\n        if self.complete and isinstance(event, NeedData):\n            raise ValueError(f\"Invalid form-data cannot parse beyond {self.state}\")\n\n        return event\n\n    def _parse_headers(self, data: bytes) -> Headers:\n        headers: list[tuple[str, str]] = []\n        # Merge the continued headers into one line\n        data = HEADER_CONTINUATION_RE.sub(b\" \", data)\n        # Now there is one header per line\n        for line in data.splitlines():\n            line = line.strip()\n\n            if line != b\"\":\n                name, _, value = line.decode().partition(\":\")\n                headers.append((name.strip(), value.strip()))\n        return Headers(headers)\n\n    def _parse_data(self, data: bytes, *, start: bool) -> tuple[bytes, int, bool]:\n        # Body parts must start with CRLF (or CR or LF)\n        if start:\n            match = LINE_BREAK_RE.match(data)\n            data_start = t.cast(t.Match[bytes], match).end()\n        else:\n            data_start = 0\n\n        match = self.boundary_re.search(data)\n        if match is not None:\n            if match.group(1).startswith(b\"--\"):\n                self.state = State.EPILOGUE\n            else:\n                self.state = State.PART\n            data_end = match.start()\n            del_index = match.end()\n        else:\n            data_end = del_index = self.last_newline()\n        more_data = match is None\n\n        return bytes(data[data_start:data_end]), del_index, more_data\n\n\nclass MultipartEncoder:\n    def __init__(self, boundary: bytes) -> None:\n        self.boundary = boundary\n        self.state = State.PREAMBLE\n\n    def send_event(self, event: Event) -> bytes:\n        if isinstance(event, Preamble) and self.state == State.PREAMBLE:\n            self.state = State.PART\n            return event.data\n        elif isinstance(event, (Field, File)) and self.state in {\n            State.PREAMBLE,\n            State.PART,\n            State.DATA,\n        }:\n            data = b\"\\r\\n--\" + self.boundary + b\"\\r\\n\"\n            data += b'Content-Disposition: form-data; name=\"%s\"' % event.name.encode()\n            if isinstance(event, File):\n                data += b'; filename=\"%s\"' % event.filename.encode()\n            data += b\"\\r\\n\"\n            for name, value in t.cast(Field, event).headers:\n                if name.lower() != \"content-disposition\":\n                    data += f\"{name}: {value}\\r\\n\".encode()\n            self.state = State.DATA_START\n            return data\n        elif isinstance(event, Data) and self.state == State.DATA_START:\n            self.state = State.DATA\n            if len(event.data) > 0:\n                return b\"\\r\\n\" + event.data\n            else:\n                return event.data\n        elif isinstance(event, Data) and self.state == State.DATA:\n            return event.data\n        elif isinstance(event, Epilogue):\n            self.state = State.COMPLETE\n            return b\"\\r\\n--\" + self.boundary + b\"--\\r\\n\" + event.data\n        else:\n            raise ValueError(f\"Cannot generate {event} in state: {self.state}\")\n", "path": "src/werkzeug/sansio/multipart.py"}], "after_files": [{"content": "from __future__ import annotations\n\nimport re\nimport typing as t\nfrom dataclasses import dataclass\nfrom enum import auto\nfrom enum import Enum\n\nfrom ..datastructures import Headers\nfrom ..exceptions import RequestEntityTooLarge\nfrom ..http import parse_options_header\n\n\nclass Event:\n    pass\n\n\n@dataclass(frozen=True)\nclass Preamble(Event):\n    data: bytes\n\n\n@dataclass(frozen=True)\nclass Field(Event):\n    name: str\n    headers: Headers\n\n\n@dataclass(frozen=True)\nclass File(Event):\n    name: str\n    filename: str\n    headers: Headers\n\n\n@dataclass(frozen=True)\nclass Data(Event):\n    data: bytes\n    more_data: bool\n\n\n@dataclass(frozen=True)\nclass Epilogue(Event):\n    data: bytes\n\n\nclass NeedData(Event):\n    pass\n\n\nNEED_DATA = NeedData()\n\n\nclass State(Enum):\n    PREAMBLE = auto()\n    PART = auto()\n    DATA = auto()\n    DATA_START = auto()\n    EPILOGUE = auto()\n    COMPLETE = auto()\n\n\n# Multipart line breaks MUST be CRLF (\\r\\n) by RFC-7578, except that\n# many implementations break this and either use CR or LF alone.\nLINE_BREAK = b\"(?:\\r\\n|\\n|\\r)\"\nBLANK_LINE_RE = re.compile(b\"(?:\\r\\n\\r\\n|\\r\\r|\\n\\n)\", re.MULTILINE)\nLINE_BREAK_RE = re.compile(LINE_BREAK, re.MULTILINE)\n# Header values can be continued via a space or tab after the linebreak, as\n# per RFC2231\nHEADER_CONTINUATION_RE = re.compile(b\"%s[ \\t]\" % LINE_BREAK, re.MULTILINE)\n# This must be long enough to contain any line breaks plus any\n# additional boundary markers (--) such that they will be found in a\n# subsequent search\nSEARCH_EXTRA_LENGTH = 8\n\n\nclass MultipartDecoder:\n    \"\"\"Decodes a multipart message as bytes into Python events.\n\n    The part data is returned as available to allow the caller to save\n    the data from memory to disk, if desired.\n    \"\"\"\n\n    def __init__(\n        self,\n        boundary: bytes,\n        max_form_memory_size: int | None = None,\n        *,\n        max_parts: int | None = None,\n    ) -> None:\n        self.buffer = bytearray()\n        self.complete = False\n        self.max_form_memory_size = max_form_memory_size\n        self.max_parts = max_parts\n        self.state = State.PREAMBLE\n        self.boundary = boundary\n\n        # Note in the below \\h i.e. horizontal whitespace is used\n        # as [^\\S\\n\\r] as \\h isn't supported in python.\n\n        # The preamble must end with a boundary where the boundary is\n        # prefixed by a line break, RFC2046. Except that many\n        # implementations including Werkzeug's tests omit the line\n        # break prefix. In addition the first boundary could be the\n        # epilogue boundary (for empty form-data) hence the matching\n        # group to understand if it is an epilogue boundary.\n        self.preamble_re = re.compile(\n            rb\"%s?--%s(--[^\\S\\n\\r]*%s?|[^\\S\\n\\r]*%s)\"\n            % (LINE_BREAK, re.escape(boundary), LINE_BREAK, LINE_BREAK),\n            re.MULTILINE,\n        )\n        # A boundary must include a line break prefix and suffix, and\n        # may include trailing whitespace. In addition the boundary\n        # could be the epilogue boundary hence the matching group to\n        # understand if it is an epilogue boundary.\n        self.boundary_re = re.compile(\n            rb\"%s--%s(--[^\\S\\n\\r]*%s?|[^\\S\\n\\r]*%s)\"\n            % (LINE_BREAK, re.escape(boundary), LINE_BREAK, LINE_BREAK),\n            re.MULTILINE,\n        )\n        self._search_position = 0\n        self._parts_decoded = 0\n\n    def last_newline(self, data: bytes) -> int:\n        try:\n            last_nl = data.rindex(b\"\\n\")\n        except ValueError:\n            last_nl = len(data)\n        try:\n            last_cr = data.rindex(b\"\\r\")\n        except ValueError:\n            last_cr = len(data)\n\n        return min(last_nl, last_cr)\n\n    def receive_data(self, data: bytes | None) -> None:\n        if data is None:\n            self.complete = True\n        elif (\n            self.max_form_memory_size is not None\n            and len(self.buffer) + len(data) > self.max_form_memory_size\n        ):\n            raise RequestEntityTooLarge()\n        else:\n            self.buffer.extend(data)\n\n    def next_event(self) -> Event:\n        event: Event = NEED_DATA\n\n        if self.state == State.PREAMBLE:\n            match = self.preamble_re.search(self.buffer, self._search_position)\n            if match is not None:\n                if match.group(1).startswith(b\"--\"):\n                    self.state = State.EPILOGUE\n                else:\n                    self.state = State.PART\n                data = bytes(self.buffer[: match.start()])\n                del self.buffer[: match.end()]\n                event = Preamble(data=data)\n                self._search_position = 0\n            else:\n                # Update the search start position to be equal to the\n                # current buffer length (already searched) minus a\n                # safe buffer for part of the search target.\n                self._search_position = max(\n                    0, len(self.buffer) - len(self.boundary) - SEARCH_EXTRA_LENGTH\n                )\n\n        elif self.state == State.PART:\n            match = BLANK_LINE_RE.search(self.buffer, self._search_position)\n            if match is not None:\n                headers = self._parse_headers(self.buffer[: match.start()])\n                # The final header ends with a single CRLF, however a\n                # blank line indicates the start of the\n                # body. Therefore the end is after the first CRLF.\n                headers_end = (match.start() + match.end()) // 2\n                del self.buffer[:headers_end]\n\n                if \"content-disposition\" not in headers:\n                    raise ValueError(\"Missing Content-Disposition header\")\n\n                disposition, extra = parse_options_header(\n                    headers[\"content-disposition\"]\n                )\n                name = t.cast(str, extra.get(\"name\"))\n                filename = extra.get(\"filename\")\n                if filename is not None:\n                    event = File(\n                        filename=filename,\n                        headers=headers,\n                        name=name,\n                    )\n                else:\n                    event = Field(\n                        headers=headers,\n                        name=name,\n                    )\n                self.state = State.DATA_START\n                self._search_position = 0\n                self._parts_decoded += 1\n\n                if self.max_parts is not None and self._parts_decoded > self.max_parts:\n                    raise RequestEntityTooLarge()\n            else:\n                # Update the search start position to be equal to the\n                # current buffer length (already searched) minus a\n                # safe buffer for part of the search target.\n                self._search_position = max(0, len(self.buffer) - SEARCH_EXTRA_LENGTH)\n\n        elif self.state == State.DATA_START:\n            data, del_index, more_data = self._parse_data(self.buffer, start=True)\n            del self.buffer[:del_index]\n            event = Data(data=data, more_data=more_data)\n            if more_data:\n                self.state = State.DATA\n\n        elif self.state == State.DATA:\n            data, del_index, more_data = self._parse_data(self.buffer, start=False)\n            del self.buffer[:del_index]\n            if data or not more_data:\n                event = Data(data=data, more_data=more_data)\n\n        elif self.state == State.EPILOGUE and self.complete:\n            event = Epilogue(data=bytes(self.buffer))\n            del self.buffer[:]\n            self.state = State.COMPLETE\n\n        if self.complete and isinstance(event, NeedData):\n            raise ValueError(f\"Invalid form-data cannot parse beyond {self.state}\")\n\n        return event\n\n    def _parse_headers(self, data: bytes) -> Headers:\n        headers: list[tuple[str, str]] = []\n        # Merge the continued headers into one line\n        data = HEADER_CONTINUATION_RE.sub(b\" \", data)\n        # Now there is one header per line\n        for line in data.splitlines():\n            line = line.strip()\n\n            if line != b\"\":\n                name, _, value = line.decode().partition(\":\")\n                headers.append((name.strip(), value.strip()))\n        return Headers(headers)\n\n    def _parse_data(self, data: bytes, *, start: bool) -> tuple[bytes, int, bool]:\n        # Body parts must start with CRLF (or CR or LF)\n        if start:\n            match = LINE_BREAK_RE.match(data)\n            data_start = t.cast(t.Match[bytes], match).end()\n        else:\n            data_start = 0\n\n        if self.buffer.find(b\"--\" + self.boundary) == -1:\n            # No complete boundary in the buffer, but there may be\n            # a partial boundary at the end. As the boundary\n            # starts with either a nl or cr find the earliest and\n            # return up to that as data.\n            data_end = del_index = self.last_newline(data[data_start:])\n            more_data = True\n        else:\n            match = self.boundary_re.search(data)\n            if match is not None:\n                if match.group(1).startswith(b\"--\"):\n                    self.state = State.EPILOGUE\n                else:\n                    self.state = State.PART\n                data_end = match.start()\n                del_index = match.end()\n            else:\n                data_end = del_index = self.last_newline(data[data_start:])\n            more_data = match is None\n\n        return bytes(data[data_start:data_end]), del_index, more_data\n\n\nclass MultipartEncoder:\n    def __init__(self, boundary: bytes) -> None:\n        self.boundary = boundary\n        self.state = State.PREAMBLE\n\n    def send_event(self, event: Event) -> bytes:\n        if isinstance(event, Preamble) and self.state == State.PREAMBLE:\n            self.state = State.PART\n            return event.data\n        elif isinstance(event, (Field, File)) and self.state in {\n            State.PREAMBLE,\n            State.PART,\n            State.DATA,\n        }:\n            data = b\"\\r\\n--\" + self.boundary + b\"\\r\\n\"\n            data += b'Content-Disposition: form-data; name=\"%s\"' % event.name.encode()\n            if isinstance(event, File):\n                data += b'; filename=\"%s\"' % event.filename.encode()\n            data += b\"\\r\\n\"\n            for name, value in t.cast(Field, event).headers:\n                if name.lower() != \"content-disposition\":\n                    data += f\"{name}: {value}\\r\\n\".encode()\n            self.state = State.DATA_START\n            return data\n        elif isinstance(event, Data) and self.state == State.DATA_START:\n            self.state = State.DATA\n            if len(event.data) > 0:\n                return b\"\\r\\n\" + event.data\n            else:\n                return event.data\n        elif isinstance(event, Data) and self.state == State.DATA:\n            return event.data\n        elif isinstance(event, Epilogue):\n            self.state = State.COMPLETE\n            return b\"\\r\\n--\" + self.boundary + b\"--\\r\\n\" + event.data\n        else:\n            raise ValueError(f\"Cannot generate {event} in state: {self.state}\")\n", "path": "src/werkzeug/sansio/multipart.py"}]}