///|
/// Raised when a compressed Gateway stream cannot be initialized or decoded.
pub(all) suberror InflateError {
InflateError(status~ : Int)
InvalidUtf8
} derive(Debug)
///|
/// Stateful decoder seam for Gateway compression. Implementations retain one
/// inflate context across messages. `None` means the Z_SYNC_FLUSH suffix has
/// not arrived yet.
pub(open) trait Inflater {
fn push(Self, Bytes) -> String? raise
}
///|
priv type ZlibHandle
///|
extern "C" fn zlib_new() -> ZlibHandle = "discord_gateway_zlib_new"
///|
#borrow(handle)
extern "C" fn zlib_status(handle : ZlibHandle) -> Int = "discord_gateway_zlib_status"
///|
#borrow(handle, input)
extern "C" fn zlib_inflate(handle : ZlibHandle, input : Bytes) -> Bytes = "discord_gateway_zlib_inflate"
///|
priv struct ZlibStreamInflater {
handle : ZlibHandle
pending : Array[Byte]
}
///|
fn ZlibStreamInflater::ZlibStreamInflater() -> ZlibStreamInflater raise InflateError {
let handle = zlib_new()
let status = zlib_status(handle)
if status != 0 {
raise InflateError(status~)
}
{ handle, pending: [], }
}
///|
fn has_sync_flush_suffix(bytes : Array[Byte]) -> Bool {
let len = bytes.length()
len >= 4 &&
bytes[len - 4] == 0x00 &&
bytes[len - 3] == 0x00 &&
bytes[len - 2] == 0xff &&
bytes[len - 1] == 0xff
}
///|
impl Inflater for ZlibStreamInflater with fn push(self, bytes) {
for byte in bytes {
self.pending.push(byte)
}
if !has_sync_flush_suffix(self.pending) {
return None
}
let compressed = Bytes::from_array(self.pending)
self.pending.clear()
let plain = zlib_inflate(self.handle, compressed)
let status = zlib_status(self.handle)
if status != 0 {
raise InflateError(status~)
}
Some(@utf8.decode(plain) catch { _ => raise InflateError::InvalidUtf8 })
}
///|
fn new_zlib_stream_inflater() -> &Inflater raise {
ZlibStreamInflater()
}