# crous

**A compact, canonical binary serializer and human-readable alternative to JSON — pure Python.**

[![PyPI](https://img.shields.io/pypi/v/crous)](https://pypi.org/project/crous/)
[![Python](https://img.shields.io/pypi/pyversions/crous)](https://pypi.org/project/crous/)
[![License](https://img.shields.io/pypi/l/crous)](https://github.com/axiomchronicles/crous/blob/main/LICENSE-MIT)

## Overview

Crous is a production-grade binary serialization format that provides:

- **Compact binary encoding** — 2×–5× smaller than equivalent JSON
- **Human-readable text notation** — unique syntax with deterministic binary mapping
- **Zero-copy decoding** — borrow directly from input buffers
- **Block-framed format** — per-block XXH64 checksums for data integrity
- **File I/O** — `load()` / `dump()` familiar API, like `json.load` / `json.dump`
- **Cross-language** — wire-compatible with the Rust implementation

## Installation

```bash
pip install crous
```

## Quick Start

```python
import crous

# Encode any Python object
data = crous.encode({"name": "Alice", "age": 30, "active": True})
print(data[:7])  # b'CROUSv1'

# Decode back to Python
obj = crous.decode(data)
print(obj)  # {'name': 'Alice', 'age': 30, 'active': True}
```

## File I/O

```python
import crous

# Write to a .crous file (like json.dump)
crous.dump({"name": "Alice", "age": 30}, "data.crous")

# Read from a .crous file (like json.load)
obj = crous.load("data.crous")
print(obj)  # {'name': 'Alice', 'age': 30}

# Append a value to an existing file
crous.append({"event": "stop"}, "log.crous")

# Lossless round-trip with Value objects
from crous.value import Value
crous.dump_values([Value.str_("hello"), Value.uint(42)], "typed.crous")
values = crous.load_values("typed.crous")
print(values[0].type)  # ValueType.STR
```

## Human-Readable Text Format

```crous
{
    name: "Alice";
    age: 30;
    tags: ["admin", "user"];
    config: {
        theme: "dark";
        notifications: true;
    };
    avatar: b64#iVBORw0KGgo=;
}
```

Parse and pretty-print:

```python
import crous

text = '{ name: "Alice"; age: 30; }'
value = crous.parse_text(text)
print(crous.pretty_print(value))
```

## Encoder / Decoder Classes

```python
from crous.encoder import Encoder
from crous.value import Value

enc = Encoder()
enc.enable_dedup()          # Enable string deduplication
enc.encode_value(Value.from_python({"key": "value"}))
data = enc.finish()
```

```python
from crous.decoder import Decoder

dec = Decoder(data)
values = dec.decode_all()
print(values[0].to_python())
```

## Wire Format

```
File:    [Header 8B] [Block]* [Trailer Block]
Header:  "CROUSv1" (7B) | flags (1B)
Block:   type(1B) | length(varint) | compression(1B) | checksum(8B) | payload
```

The format is deterministic: the same input always produces the same bytes.

## Cross-Language Compatibility

Data encoded by the pure-Python `crous` package is fully wire-compatible with the Rust implementation (`crous-core`). Files can be written in Python and read in Rust, or vice versa.

## License

Licensed under either of:

- MIT License ([LICENSE-MIT](https://github.com/axiomchronicles/crous/blob/main/LICENSE-MIT))
- Apache License, Version 2.0 ([LICENSE-APACHE](https://github.com/axiomchronicles/crous/blob/main/LICENSE-APACHE))

at your option.
