typebit 0.1.2

TypeBit: A universal download engine for torrent (v1/v2, DHT, PEX, web seeds), eD2k/eMule, Xunlei, FlashGet, QQDL, IPFS, Kad and direct HTTP(S)/FTP, with provable-download receipts and a semantic utility scheduler.
Documentation
# Implementing a Host


`typebit::Host` is the only interface you implement. It has ~18 methods, all
`no_std`-friendly. The engine calls them from `tick()` on **your** thread, so
they must not block for long (use non-blocking sockets).

## The trait


```rust
pub trait Host {
    fn now_ms(&self) -> u64;                        // clock (ms)
    fn fill_random(&mut self, buf: &mut [u8]);      // CSPRNG
    fn log(&mut self, level: LogLevel, msg: &str);

    // HTTP(S) GET → append body to `out` (trackers, web seeds, IPFS gateways)
    fn http_get(&mut self, url: &str, timeout_ms: u64, out: &mut Vec<u8>)
        -> Result<()>;

    // TCP (non-blocking)
    fn tcp_connect(&mut self, addr: &NetAddr) -> Result<ConnId>;
    fn tcp_connect_done(&mut self, id: ConnId) -> Result<()>;  // Ok=established
    fn tcp_send(&mut self, id: ConnId, data: &[u8]) -> Result<usize>;
    fn tcp_recv(&mut self, id: ConnId, buf: &mut [u8]) -> Result<usize>;
    fn tcp_close(&mut self, id: ConnId);

    // UDP (DHT, UDP trackers)
    fn udp_open(&mut self, port: u16) -> Result<()>;
    fn udp_send(&mut self, addr: &NetAddr, data: &[u8]) -> Result<()>;
    fn udp_recv(&mut self, buf: &mut [u8]) -> Result<(NetAddr, usize)>;

    // Files (piece data)
    fn disk_open(&mut self, path: &str) -> Result<DiskId>;
    fn disk_read(&mut self, id: DiskId, offset: u64, buf: &mut [u8]) -> Result<usize>;
    fn disk_write(&mut self, id: DiskId, offset: u64, data: &[u8]) -> Result<()>;
    fn disk_prealloc(&mut self, id: DiskId, size: u64) -> Result<()>;
    fn disk_flush(&mut self, id: DiskId) -> Result<()>;
    fn disk_close(&mut self, id: DiskId);
}
```

## Conventions the engine relies on


- `tcp_recv` / `udp_recv` return `Err(Error::WouldBlock)` when there is no
  data right now. Do **not** block in these.
- `tcp_connect` returns a handle immediately (non-blocking connect); the
  engine polls with `tcp_connect_done`.
- `http_get` may block up to `timeout_ms`; the engine uses it for trackers
  and web seeds.
- Disk offsets are absolute within one file (per-file handles).

## A real (std) host in ~60 lines

```rust
use std::net::TcpStream;
use typebit::platform::{ConnId, DiskId, Host, LogLevel, NetAddr};
use typebit::{Error, Result};

pub struct StdHost {
    tcp: HashMap<ConnId, TcpStream>,
    disks: HashMap<DiskId, std::fs::File>,
    next_id: u32,
}

impl Host for StdHost {
    fn now_ms(&self) -> u64 {
        std::time::SystemTime::now()
            .duration_since(std::time::UNIX_EPOCH)
            .unwrap_or_default()
            .as_millis() as u64
    }
    fn fill_random(&mut self, buf: &mut [u8]) {
        // os random (e.g. getrandom or /dev/urandom)
    }
    fn tcp_connect(&mut self, addr: &NetAddr) -> Result<ConnId> {
        let (ip, port) = match addr {
            NetAddr::V4(ip, p) => (std::net::Ipv4Addr::from(*ip), *p),
            NetAddr::V6(ip, p) => (std::net::Ipv6Addr::from(*ip), *p),
        };
        let stream = TcpStream::connect((ip, port)).map_err(|_| Error::Io)?;
        stream.set_nonblocking(true).map_err(|_| Error::Io)?;
        let id = self.next_id;
        self.next_id += 1;
        self.tcp.insert(id, stream);
        Ok(id)
    }
    fn tcp_recv(&mut self, id: ConnId, buf: &mut [u8]) -> Result<usize> {
        use std::io::Read;
        match self.tcp.get_mut(&id).unwrap().read(buf) {
            Ok(n) => Ok(n),
            Err(e) if e.kind() == std::io::ErrorKind::WouldBlock => Err(Error::WouldBlock),
            Err(_) => Err(Error::Io),
        }
    }
    // ... and the rest. See examples/minimal_host.rs for a complete mock.
}
```

## Integration checklist


1. `Engine::new(host, config)` — one per app
2. `add_torrent(&bytes, save_dir)` or `add_magnet(uri, save_dir)` → infohash
3. `start(&hash)`
4. on a timer: `tick()`, then `take_events()` and forward events to your UI
5. inbound sockets: `on_inbound_connection(conn, addr)`
6. persistence: `save_state()` / `load_state(&state, now)`

That's the whole integration surface. Everything else is internal.