Streaming

Crous supports stream-based I/O for writing to and reading from any file-like object, including files, sockets, io.BytesIO, and network streams.

Stream Writing with dump_to_stream

Write serialized binary data to any writable stream (any object with a write() method).

stream_write.py
import crous
import io

data = {"name": "Alice", "scores": [98, 95, 100]}

# Write to BytesIO
buffer = io.BytesIO()
crous.dump_to_stream(data, buffer)

# Check the result
buffer.seek(0)
print(f"Written {len(buffer.getvalue())} bytes")

Stream Reading with load_from_stream

Read and deserialize from any readable stream (any object with a read() method).

stream_read.py
import crous
import io

# Write data
data = {"key": "value"}
buffer = io.BytesIO()
crous.dump_to_stream(data, buffer)

# Read it back
buffer.seek(0)
result = crous.load_from_stream(buffer)
assert result == data

File Streams

The dump and load functions accept both file paths and file objects:

file_streams.py
import crous

data = {"users": [{"name": "Alice"}, {"name": "Bob"}]}

# String path — Crous handles opening/closing
crous.dump(data, "output.crous")
result = crous.load("output.crous")

# File object — you manage the lifecycle
with open("output.crous", "wb") as f:
    crous.dump(data, f)

with open("output.crous", "rb") as f:
    result = crous.load(f)

Multi-Record Streaming

You can write multiple records to a single stream. Each record includes its own header, so they can be read back sequentially.

multi_record.py
import crous
import io

buffer = io.BytesIO()

# Write multiple records
records = [
    {"id": 1, "name": "Alice"},
    {"id": 2, "name": "Bob"},
    {"id": 3, "name": "Charlie"},
]

for record in records:
    crous.dump_to_stream(record, buffer)

# Read them back
buffer.seek(0)
for _ in range(len(records)):
    record = crous.load_from_stream(buffer)
    print(record)

Stream Position

When reading multiple records, the stream position advances after each load_from_stream call. Make sure to seek to position 0 if you want to re-read from the beginning.

Network Sockets

Since Crous works with any file-like object, you can use it with sockets wrapped in socket.makefile():

socket_example.py
import crous
import socket

# Server side
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.bind(('localhost', 9999))
server.listen(1)
conn, addr = server.accept()

# Wrap socket as file object
stream = conn.makefile('rwb')

# Read a message
data = crous.load_from_stream(stream)
print(f"Received: {data}")

# Send a response
crous.dump_to_stream({"status": "ok"}, stream)
stream.flush()  # important for sockets!

Buffering

When using sockets, remember to call flush() after writing to ensure data is actually sent. Crous does not automatically flush the stream.

BytesIO Convenience

For in-memory operations, io.BytesIO works seamlessly:

import crous
import io

# dumps/loads is simpler for pure in-memory use
binary = crous.dumps(data)        # returns bytes directly
result = crous.loads(binary)      # accepts bytes directly

# dump_to_stream/load_from_stream for stream-based use
buf = io.BytesIO()
crous.dump_to_stream(data, buf)   # writes to stream
buf.seek(0)
result = crous.load_from_stream(buf)  # reads from stream