(self, cmd, name, rtype)
| 276 | assert nbytes == len(msg), f"{nbytes=} != {len(msg)=}" |
| 277 | |
| 278 | def _send(self, cmd, name, rtype): |
| 279 | if self._use_simple_format and '\n' not in name: |
| 280 | msg = f"{cmd}:{name}:{rtype}\n".encode("ascii") |
| 281 | if len(msg) > 512: |
| 282 | # posix guarantees that writes to a pipe of less than PIPE_BUF |
| 283 | # bytes are atomic, and that PIPE_BUF >= 512 |
| 284 | raise ValueError('msg too long') |
| 285 | self._ensure_running_and_write(msg) |
| 286 | return |
| 287 | |
| 288 | # POSIX guarantees that writes to a pipe of less than PIPE_BUF (512 on Linux) |
| 289 | # bytes are atomic. Therefore, we want the message to be shorter than 512 bytes. |
| 290 | # POSIX shm_open() and sem_open() require the name, including its leading slash, |
| 291 | # to be at most NAME_MAX bytes (255 on Linux) |
| 292 | # With json.dump(..., ensure_ascii=True) every non-ASCII byte becomes a 6-char |
| 293 | # escape like \uDC80. |
| 294 | # As we want the overall message to be kept atomic and therefore smaller than 512, |
| 295 | # we encode encode the raw name bytes with URL-safe Base64 - so a 255 long name |
| 296 | # will not exceed 340 bytes. |
| 297 | b = name.encode('utf-8', 'surrogateescape') |
| 298 | if len(b) > 255: |
| 299 | raise ValueError('shared memory name too long (max 255 bytes)') |
| 300 | b64 = base64.urlsafe_b64encode(b).decode('ascii') |
| 301 | |
| 302 | payload = {"cmd": cmd, "rtype": rtype, "base64_name": b64} |
| 303 | msg = (json.dumps(payload, ensure_ascii=True, separators=(",", ":")) + "\n").encode("ascii") |
| 304 | |
| 305 | # The entire JSON message is guaranteed < PIPE_BUF (512 bytes) by construction. |
| 306 | assert len(msg) <= 512, f"internal error: message too long ({len(msg)} bytes)" |
| 307 | assert msg.startswith(b'{') |
| 308 | |
| 309 | self._ensure_running_and_write(msg) |
| 310 | |
| 311 | _resource_tracker = ResourceTracker() |
| 312 | ensure_running = _resource_tracker.ensure_running |
no test coverage detected