Skip to content

Commit 995081c

Browse files
committed
Update docs
1 parent 2e348e6 commit 995081c

1 file changed

Lines changed: 8 additions & 4 deletions

File tree

AGENTS.md

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,8 @@ vespera/
3636
│ ├── vespera_core/ # OpenAPI types, route/schema abstractions
3737
│ ├── vespera_macro/ # Proc-macros (main logic lives here)
3838
│ ├── vespera_inprocess/ # In-process dispatch (transport-agnostic)
39-
│ │ └── src/lib.rs # dispatch(), register_app(), dispatch_from_bytes()
39+
│ │ ├── src/lib.rs # dispatch(), register_app(), dispatch_from_bytes()
40+
│ │ └── src/wire/ # hand-rolled wire-header parse (header_read) + serialize (header_write)
4041
│ └── vespera_jni/ # JNI bridge (depends on vespera_inprocess)
4142
│ ├── src/jni_impl.rs # RUNTIME, jni_app! macro, JNI symbol export
4243
│ └── src/streaming_closures.rs # Streaming closure factories + JMethodID cache
@@ -64,6 +65,7 @@ vespera/
6465
| Add core types | `crates/vespera_core/src/` | OpenAPI spec types |
6566
| Test new features | `examples/axum-example/` | Add route, run example |
6667
| In-process dispatch | `crates/vespera_inprocess/src/dispatch.rs` | RequestEnvelope → Router → ResponseEnvelope; wire + direct-write entry points |
68+
| Wire header parse/serialize | `crates/vespera_inprocess/src/wire/` | Hand-rolled `header_read` (request parse) + `header_write` (response serialize); byte-identical to serde_json, whose twins are kept private (`*_serde`) for the criterion A/B |
6769
| App factory (FFI pattern) | `crates/vespera_inprocess/src/registry.rs` | register_app(), resolve_app_router() |
6870
| JNI integration | `crates/vespera_jni/src/jni_impl.rs` | RUNTIME, jni_app! macro, JNI symbol export |
6971
| Java bridge library | `libs/vespera-bridge/` | com.devfive.vespera.bridge package |
@@ -80,8 +82,10 @@ vespera/
8082
| `vespera_macro/src/parser/parameters.rs` | ~845 | Extract path/query params from handlers |
8183
| `vespera_macro/src/openapi_generator.rs` | ~808 | OpenAPI doc assembly |
8284
| `vespera_macro/src/collector.rs` | ~707 | Filesystem route scanning |
83-
| `vespera_inprocess/src/lib.rs` | ~85 | Crate root: module wiring + public re-exports (modularized — logic lives in the files below) |
84-
| `vespera_inprocess/src/wire.rs` | ~429 | Binary wire encode/decode: split/parse, `Cow` borrowing request header, `HeaderMap`-direct response serialization, 422 validation-error hoisting |
85+
| `vespera_inprocess/src/lib.rs` | ~115 | Crate root: module wiring + public re-exports + `#[doc(hidden)]` `bench_support` (modularized — logic lives in the files below) |
86+
| `vespera_inprocess/src/wire.rs` | ~910 | Binary wire frame split/parse + 422 validation-error hoisting; `parse_wire_header` / `write_wire_header_into{,_slice}` delegate to the hand-rolled `wire/` submodules (serde_json twins retained private as `*_serde` for the criterion A/B) |
87+
| `vespera_inprocess/src/wire/header_read.rs` | ~489 | Hand-rolled request-header JSON reader → `WireRequestHeader<'a>`: borrow-when-plain / own-when-escaped `Cow`, UTF-16 surrogate decode, any key order + unknown-skip + dup-reject, never panics (byte-behaviour-identical to the serde derive) |
88+
| `vespera_inprocess/src/wire/header_write.rs` | ~268 | Hand-rolled response-header JSON serializer: `serde_json`-exact escape table + `\u00XX`, sorted `HeaderMap`, metadata, `validation_errors`; one `JsonSink` serves the `Vec` and overflow-counting `&mut [u8]` paths (byte-identical to `serde_json`) |
8589
| `vespera_inprocess/src/dispatch.rs` | ~290 | Public dispatch entry points: text envelope API, binary wire API, direct-write (`dispatch_into`) API |
8690
| `vespera_inprocess/src/internal.rs` | ~335 | Request building + router oneshot + response collection (malformed path/header → 400) |
8791
| `vespera_inprocess/src/streaming.rs` | ~462 | Response / header-callback / bidirectional streaming; `RequestChunk`/`StreamAbort` error-aware request body; bounded `ChannelBody` |
@@ -193,7 +197,7 @@ bytes 4+N.. : raw body bytes (UTF-8 text or binary —
193197

194198
All share the same wire format, registered router, and panic-safe `catch_unwind` discipline. **`DecodedResponse` (vespera-bridge 0.2.0, BREAKING):** `body()` now returns a read-only `java.nio.ByteBuffer` (zero-copy view over the wire bytes); `bodyBytes()` materialises an owned `byte[]` copy on demand — callers that previously used `body()` as `byte[]` must switch to `bodyBytes()`. The direct-buffer path (`dispatchDirect` + pooled `dispatchDirectPooled`, per-thread 64 KiB→4 MiB buffers via `vespera.direct.maxBufferBytes`) removes the two JNI region copies of `dispatchBytes`; on response overflow it returns `-(requiredSize)` and a retry **re-runs the handler**, so the Java side only auto-retries idempotent requests (`BufferTooSmallException` otherwise). Spring autoconfigured default since vespera-bridge 0.2.0: `SmartDispatchModeResolver` (small/bodyless idempotent → `DIRECT` ~2.2µs, small non-idempotent → `SYNC` ~3.2µs, else streaming ~24µs). Opt out with `vespera.bridge.dispatch-mode=bidirectional-streaming` to restore the pre-0.2.0 default (`BidirectionalStreamingDispatchModeResolver`: provably bodyless requests — CL:0, or GET/HEAD/OPTIONS without CL/TE — downgrade to response-only `STREAMING` ~3x, 24.1→7.7µs; everything else streams both ways). `dispatchAsync` spawns the dispatch on Rust's shared Tokio runtime via `tokio::spawn` (panic → `JoinError` → `error_wire(500)`) and completes the `CompletableFuture` from a daemon-attached cached Tokio worker thread (`with_async_daemon_env` in `jni_impl.rs`: raw `AttachCurrentThreadAsDaemon` + TLS env cache + per-completion local frame + unconditional pending-exception cleanup) — ~1.3µs/op faster than scoped attach per completion. `dispatchStreaming` drains the response body chunk-by-chunk via `http_body::Body::frame()` and writes each chunk to the Java `OutputStream`. `dispatchFullStreaming` adds request-side streaming: a `tokio::task::spawn_blocking` thread pulls chunks (default 256 KiB) from `InputStream.read(byte[])` and feeds them into axum via an `mpsc::channel`-backed `http_body::Body`, giving natural backpressure (bounded channel, default 16 slots) so 1 GiB uploads run in `O(chunk_size)` RAM.
195199

196-
**Streaming tuning (process-fixed after first dispatch):** chunk size via system property `vespera.streaming.chunkBytes` / env `VESPERA_STREAMING_CHUNK_BYTES` (default 256 KiB, clamped 4 KiB–8 MiB); channel capacity via `vespera.streaming.channelCapacity` / `VESPERA_STREAMING_CHANNEL_CAPACITY` (default 16, clamped 1–1024). Java API: `VesperaBridge.configureStreaming(chunkBytes, channelCapacity)` — pending-config pattern (call before `init()`; values stored pending and applied right after native load, before any dispatch; programmatic > sysprops > env > defaults). Rust-side setters: `vespera_inprocess::set_streaming_chunk_bytes` / `set_streaming_channel_capacity` (precedence: setter > env > default). The shared Tokio runtime's worker count is tunable the same way: `vespera.runtime.workerThreads` / `VESPERA_RUNTIME_WORKERS` (default: logical CPUs, clamped 1–1024) — cap it when JVM thread pools compete for the same cores. `_default`-app dispatch resolves through a lock-free `OnceLock<Router>` fast path; named apps go through the `RwLock<HashMap>`. The response wire header serializes straight from `http::HeaderMap` (zero per-header allocation) and request wire headers deserialize borrowing from the input buffer (`Cow`) — the wire byte layout is locked by `crates/vespera_inprocess/tests/wire_contract.rs`.
200+
**Streaming tuning (process-fixed after first dispatch):** chunk size via system property `vespera.streaming.chunkBytes` / env `VESPERA_STREAMING_CHUNK_BYTES` (default 256 KiB, clamped 4 KiB–8 MiB); channel capacity via `vespera.streaming.channelCapacity` / `VESPERA_STREAMING_CHANNEL_CAPACITY` (default 16, clamped 1–1024). Java API: `VesperaBridge.configureStreaming(chunkBytes, channelCapacity)` — pending-config pattern (call before `init()`; values stored pending and applied right after native load, before any dispatch; programmatic > sysprops > env > defaults). Rust-side setters: `vespera_inprocess::set_streaming_chunk_bytes` / `set_streaming_channel_capacity` (precedence: setter > env > default). The shared Tokio runtime's worker count is tunable the same way: `vespera.runtime.workerThreads` / `VESPERA_RUNTIME_WORKERS` (default: logical CPUs, clamped 1–1024) — cap it when JVM thread pools compete for the same cores. `_default`-app dispatch resolves through a lock-free `OnceLock<Router>` fast path; named apps go through the `RwLock<HashMap>`. The response wire header serializes straight from `http::HeaderMap` (zero per-header allocation) and request wire headers deserialize borrowing from the input buffer (`Cow`); both the response serialize and the request parse use **hand-rolled** writers/parsers (`wire/header_write.rs` / `wire/header_read.rs`) byte-identical to the prior `serde_json` path — the serde twins are retained private as `*_serde` for the same-run criterion A/B in `benches/dispatch.rs` (group `wire_header_serde`). The wire byte layout is locked by `crates/vespera_inprocess/tests/wire_contract.rs` and the hand-vs-serde round-trip property test in `wire.rs`.
197201

198202
### Rust Public API (vespera_inprocess)
199203

0 commit comments

Comments
 (0)