refactor: add as_conversions clippy correctness lint (#1021)
Some checks failed
CI / Check formatting (push) Has been cancelled
CI / Check typos (push) Has been cancelled
Coverage / Coverage Report (push) Has been cancelled
Release crates / Open release PR (push) Has been cancelled
Release crates / Release crates (push) Has been cancelled
CI / Checks [linux] (push) Has been cancelled
CI / Checks [macos] (push) Has been cancelled
CI / Checks [windows] (push) Has been cancelled
CI / Fuzzing (push) Has been cancelled
CI / Web Client (push) Has been cancelled
CI / FFI (push) Has been cancelled
CI / Success (push) Has been cancelled

Co-authored-by: Benoît CORTIER <git.divisible626@passmail.com>
This commit is contained in:
Alex Yusiuk 2025-11-06 00:23:22 +02:00 committed by GitHub
parent d3e0cb17e1
commit 9622619e8c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
52 changed files with 526 additions and 264 deletions

View file

@ -92,7 +92,7 @@ fn_to_numeric_cast_any = "warn"
ptr_cast_constness = "warn"
# == Correctness == #
#as_conversions = "warn"
as_conversions = "warn"
cast_lossless = "warn"
cast_possible_truncation = "warn"
cast_possible_wrap = "warn"

View file

@ -68,7 +68,7 @@ async fn main() -> Result<(), anyhow::Error> {
let mut updates = DisplayUpdates::new(file, DesktopSize { width, height }, fps);
while let Some(up) = updates.next_update().await? {
if let DisplayUpdate::Bitmap(ref up) = up {
total_raw += up.data.len() as u64;
total_raw += u64::try_from(up.data.len())?;
} else {
eprintln!("Invalid update");
break;
@ -78,7 +78,7 @@ async fn main() -> Result<(), anyhow::Error> {
let Some(frag) = iter.next().await else {
break;
};
let len = frag?.data.len() as u64;
let len = u64::try_from(frag?.data.len())?;
total_enc += len;
}
n_updates += 1;
@ -87,6 +87,7 @@ async fn main() -> Result<(), anyhow::Error> {
}
println!();
#[expect(clippy::as_conversions, reason = "casting u64 to f64")]
let ratio = total_enc as f64 / total_raw as f64;
let percent = 100.0 - ratio * 100.0;
println!("Encoder: {encoder:?}");

View file

@ -1,4 +1,4 @@
msrv = "1.84"
msrv = "1.87"
semicolon-outside-block-ignore-multiline = true
accept-comment-above-statement = true
accept-comment-above-attributes = true

View file

@ -66,6 +66,7 @@ impl App {
let Some((window, _)) = self.window.as_mut() else {
return;
};
#[expect(clippy::as_conversions, reason = "casting f64 to u32")]
let scale_factor = (window.scale_factor() * 100.0) as u32;
let width = u16::try_from(size.width).expect("reasonable width");
@ -222,8 +223,10 @@ impl ApplicationHandler<RdpOutputEvent> for App {
}
WindowEvent::CursorMoved { position, .. } => {
let win_size = window.inner_size();
let x = (position.x / win_size.width as f64 * self.buffer_size.0 as f64) as u16;
let y = (position.y / win_size.height as f64 * self.buffer_size.1 as f64) as u16;
#[expect(clippy::as_conversions, reason = "casting f64 to u16")]
let x = (position.x / f64::from(win_size.width) * f64::from(self.buffer_size.0)) as u16;
#[expect(clippy::as_conversions, reason = "casting f64 to u16")]
let y = (position.y / f64::from(win_size.height) * f64::from(self.buffer_size.1)) as u16;
let operation = ironrdp::input::Operation::MouseMove(ironrdp::input::MousePosition { x, y });
let input_events = self.input_database.apply(core::iter::once(operation));
@ -239,6 +242,7 @@ impl ApplicationHandler<RdpOutputEvent> for App {
operations.push(ironrdp::input::Operation::WheelRotations(
ironrdp::input::WheelRotations {
is_vertical: false,
#[expect(clippy::as_conversions, reason = "casting f32 to i16")]
rotation_units: (delta_x * 100.) as i16,
},
));
@ -248,6 +252,7 @@ impl ApplicationHandler<RdpOutputEvent> for App {
operations.push(ironrdp::input::Operation::WheelRotations(
ironrdp::input::WheelRotations {
is_vertical: true,
#[expect(clippy::as_conversions, reason = "casting f32 to i16")]
rotation_units: (delta_y * 100.) as i16,
},
));
@ -258,6 +263,7 @@ impl ApplicationHandler<RdpOutputEvent> for App {
operations.push(ironrdp::input::Operation::WheelRotations(
ironrdp::input::WheelRotations {
is_vertical: false,
#[expect(clippy::as_conversions, reason = "casting f64 to i16")]
rotation_units: delta.x as i16,
},
));
@ -267,6 +273,7 @@ impl ApplicationHandler<RdpOutputEvent> for App {
operations.push(ironrdp::input::Operation::WheelRotations(
ironrdp::input::WheelRotations {
is_vertical: true,
#[expect(clippy::as_conversions, reason = "casting f64 to i16")]
rotation_units: delta.y as i16,
},
));

View file

@ -235,7 +235,7 @@ async fn connect(
let upgraded = ironrdp_tokio::mark_as_upgraded(should_upgrade, &mut connector);
let erased_stream = Box::new(upgraded_stream) as Box<dyn AsyncReadWrite + Unpin + Send + Sync>;
let erased_stream: Box<dyn AsyncReadWrite + Unpin + Send + Sync> = Box::new(upgraded_stream);
let mut upgraded_framed = ironrdp_tokio::TokioFramed::new_with_leftover(erased_stream, leftover_bytes);
let connection_result = ironrdp_tokio::connect_finalize(
@ -336,7 +336,7 @@ async fn connect_ws(
.await?;
let (ws, leftover_bytes) = framed.into_inner();
let erased_stream = Box::new(ws) as Box<dyn AsyncReadWrite + Unpin + Send + Sync>;
let erased_stream: Box<dyn AsyncReadWrite + Unpin + Send + Sync> = Box::new(ws);
let upgraded_framed = ironrdp_tokio::TokioFramed::new_with_leftover(erased_stream, leftover_bytes);
Ok((connection_result, upgraded_framed))

View file

@ -27,7 +27,7 @@ impl<'a> ClipboardDataRef<'a> {
};
// SAFETY: It is safe to call `GlobalLock` on the valid handle.
let data = unsafe { GlobalLock(handle) } as *const u8;
let data = unsafe { GlobalLock(handle) }.cast::<u8>().cast_const();
if data.is_null() {
// Can't lock data handle, handle is not valid anymore (e.g. clipboard has changed)

View file

@ -1,3 +1,4 @@
use core::ptr::with_exposed_provenance_mut;
use core::time::Duration;
use std::collections::HashSet;
use std::sync::mpsc;
@ -320,17 +321,19 @@ pub(crate) unsafe extern "system" fn clipboard_subproc(
// SAFETY: `data` is a valid pointer, returned by `Box::into_raw`, transferred to OS earlier
// via `SetWindowSubclass` call.
let _ = unsafe { Box::from_raw(data as *mut WinClipboardImpl) };
let _ = unsafe { Box::from_raw(with_exposed_provenance_mut::<WinClipboardImpl>(data)) };
return LRESULT(0);
}
// SAFETY: `data` is a valid pointer, returned by `Box::into_raw`, transferred to OS earlier
// via `SetWindowSubclass` call.
let ctx = unsafe { &mut *(data as *mut WinClipboardImpl) };
let ctx = unsafe { &mut *(with_exposed_provenance_mut::<WinClipboardImpl>(data)) };
match msg {
// We need to keep track of window state to distinguish between local and remote copy
WM_ACTIVATE | WM_ACTIVATEAPP => ctx.window_is_active = wparam.0 != WA_INACTIVE as usize, // `as` conversion is fine for constants
WM_ACTIVATE | WM_ACTIVATEAPP => {
ctx.window_is_active = wparam.0 != usize::try_from(WA_INACTIVE).expect("WA_INACTIVE fits into usize")
}
// Sent by the OS when OS clipboard content is changed
WM_CLIPBOARDUPDATE => {
// SAFETY: `GetClipboardOwner` is always safe to call.
@ -347,8 +350,9 @@ pub(crate) unsafe extern "system" fn clipboard_subproc(
}
// Sent by the OS when delay-rendered data is requested for rendering.
WM_RENDERFORMAT => {
#[expect(clippy::cast_possible_truncation)] // should never truncate in practice
ctx.handle_event(BackendEvent::RenderFormat(ClipboardFormatId::new(wparam.0 as u32)));
ctx.handle_event(BackendEvent::RenderFormat(ClipboardFormatId::new(
u32::try_from(wparam.0).expect("should never truncate in practice"),
)));
}
// Sent by the OS when all delay-rendered data is requested for rendering.
WM_RENDERALLFORMATS => {

View file

@ -200,8 +200,14 @@ impl WinClipboard {
//
// SAFETY: `window` is a valid window handle, `clipboard_subproc` is in the static memory,
// `ctx` is valid and its ownership is transferred to the subclass via `into_raw`.
let winapi_result =
unsafe { SetWindowSubclass(window, Some(clipboard_subproc), 0, Box::into_raw(ctx) as usize) };
let winapi_result = unsafe {
SetWindowSubclass(
window,
Some(clipboard_subproc),
0,
Box::into_raw(ctx).expose_provenance(),
)
};
if winapi_result == FALSE {
return Err(WinCliprdrError::WindowSubclass);

View file

@ -26,7 +26,7 @@ impl GlobalMemoryBuffer {
// - `dst` is valid for writes of `data.len()` bytes, we allocated enough above.
// - Both `data` and `dst` are properly aligned: u8 alignment is 1
// - Memory regions are not overlapping, `dst` was allocated by us just above.
unsafe { core::ptr::copy_nonoverlapping(data.as_ptr(), dst as *mut u8, data.len()) };
unsafe { core::ptr::copy_nonoverlapping(data.as_ptr(), dst.cast::<u8>(), data.len()) };
// SAFETY: We called `GlobalLock` on this handle just above.
if let Err(error) = unsafe { GlobalUnlock(handle) } {

View file

@ -1,10 +1,6 @@
#![cfg_attr(doc, doc = include_str!("../README.md"))]
#![doc(html_logo_url = "https://cdnweb.devolutions.net/images/projects/devolutions/logos/devolutions-icon-shadow.svg")]
#![allow(clippy::arithmetic_side_effects)] // FIXME: remove
#![allow(clippy::cast_lossless)] // FIXME: remove
#![allow(clippy::cast_possible_truncation)] // FIXME: remove
#![allow(clippy::cast_possible_wrap)] // FIXME: remove
#![allow(clippy::cast_sign_loss)] // FIXME: remove
pub mod backend;
pub mod pdu;

View file

@ -699,9 +699,9 @@ fn create_gcc_blocks<'a>(
desktop_physical_width: Some(0), // 0 per FreeRDP
desktop_physical_height: Some(0), // 0 per FreeRDP
desktop_orientation: if config.desktop_size.width > config.desktop_size.height {
Some(MonitorOrientation::Landscape as u16)
Some(MonitorOrientation::Landscape.as_u16())
} else {
Some(MonitorOrientation::Portrait as u16)
Some(MonitorOrientation::Portrait.as_u16())
},
desktop_scale_factor: Some(config.desktop_scale_factor),
device_scale_factor: if config.desktop_scale_factor >= 100 && config.desktop_scale_factor <= 500 {

View file

@ -200,7 +200,7 @@ impl Header {
fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> {
ensure_fixed_part_size!(in: dst);
dst.write_u8(((self.cmd as u8) << 4) | (Into::<u8>::into(self.sp) << 2) | Into::<u8>::into(self.cb_id));
dst.write_u8(((self.cmd.as_u8()) << 4) | (Into::<u8>::into(self.sp) << 2) | Into::<u8>::into(self.cb_id));
Ok(())
}
@ -235,6 +235,16 @@ enum Cmd {
SoftSyncResponse = 0x09,
}
impl Cmd {
#[expect(
clippy::as_conversions,
reason = "guarantees discriminant layout, and as is the only way to cast enum -> primitive"
)]
fn as_u8(self) -> u8 {
self as u8
}
}
impl TryFrom<u8> for Cmd {
type Error = DecodeError;
@ -702,7 +712,7 @@ impl CapsVersion {
fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> {
ensure_size!(in: dst, size: Self::size());
dst.write_u16(*self as u16);
dst.write_u16(u16::from(*self));
Ok(())
}
@ -725,6 +735,10 @@ impl TryFrom<u16> for CapsVersion {
}
impl From<CapsVersion> for u16 {
#[expect(
clippy::as_conversions,
reason = "guarantees discriminant layout, and as is the only way to cast enum -> primitive"
)]
fn from(version: CapsVersion) -> Self {
version as u16
}

View file

@ -114,8 +114,8 @@ pub fn rdp6_decode_bitmap_stream_to_rgb24(input: &BitmapInput<'_>) {
let _ = BitmapStreamDecoder::default().decode_bitmap_stream_to_rgb24(
input.src,
&mut out,
input.width as usize,
input.height as usize,
usize::from(input.width),
usize::from(input.height),
);
}

View file

@ -83,10 +83,15 @@ pub fn to_64x64_ycbcr_tile(
/// Convert a 16-bit RDP color to RGB representation. Input value should be represented in
/// little-endian format.
pub fn rdp_16bit_to_rgb(color: u16) -> [u8; 3] {
let r = (((((color >> 11) & 0x1f) * 527) + 23) >> 6) as u8;
let g = (((((color >> 5) & 0x3f) * 259) + 33) >> 6) as u8;
let b = ((((color & 0x1f) * 527) + 23) >> 6) as u8;
[r, g, b]
#[expect(clippy::missing_panics_doc, reason = "unreachable panic (checked integer underflow)")]
let out = {
let r = u8::try_from(((((color >> 11) & 0x1f) * 527) + 23) >> 6).expect("max possible value is 255");
let g = u8::try_from(((((color >> 5) & 0x3f) * 259) + 33) >> 6).expect("max possible value is 255");
let b = u8::try_from((((color & 0x1f) * 527) + 23) >> 6).expect("max possible value is 255");
[r, g, b]
};
out
}
#[derive(Debug)]

View file

@ -22,17 +22,21 @@ fn dwt_vertical<const SUBBAND_WIDTH: usize>(buffer: &[i16], dwt: &mut [i16]) {
let h_index = l_index + SUBBAND_WIDTH * total_width;
let src_index = y * total_width + x;
dwt[h_index] = ((i32::from(buffer[src_index + total_width])
- ((i32::from(buffer[src_index])
+ i32::from(buffer[src_index + if n < SUBBAND_WIDTH - 1 { 2 * total_width } else { 0 }]))
>> 1))
>> 1) as i16;
dwt[l_index] = (i32::from(buffer[src_index])
+ if n == 0 {
i32::from(dwt[h_index])
} else {
(i32::from(dwt[h_index - total_width]) + i32::from(dwt[h_index])) >> 1
}) as i16;
dwt[h_index] = i32_to_i16_possible_truncation(
(i32::from(buffer[src_index + total_width])
- ((i32::from(buffer[src_index])
+ i32::from(buffer[src_index + if n < SUBBAND_WIDTH - 1 { 2 * total_width } else { 0 }]))
>> 1))
>> 1,
);
dwt[l_index] = i32_to_i16_possible_truncation(
i32::from(buffer[src_index])
+ if n == 0 {
i32::from(dwt[h_index])
} else {
(i32::from(dwt[h_index - total_width]) + i32::from(dwt[h_index])) >> 1
},
);
}
}
}
@ -57,16 +61,20 @@ fn dwt_horizontal<const SUBBAND_WIDTH: usize>(mut buffer: &mut [i16], dwt: &[i16
let x = n * 2;
// HL
hl[n] = ((i32::from(l_src[x + 1])
- ((i32::from(l_src[x]) + i32::from(l_src[if n < SUBBAND_WIDTH - 1 { x + 2 } else { x }])) >> 1))
>> 1) as i16;
hl[n] = i32_to_i16_possible_truncation(
(i32::from(l_src[x + 1])
- ((i32::from(l_src[x]) + i32::from(l_src[if n < SUBBAND_WIDTH - 1 { x + 2 } else { x }])) >> 1))
>> 1,
);
// LL
ll[n] = (i32::from(l_src[x])
+ if n == 0 {
i32::from(hl[n])
} else {
(i32::from(hl[n - 1]) + i32::from(hl[n])) >> 1
}) as i16;
ll[n] = i32_to_i16_possible_truncation(
i32::from(l_src[x])
+ if n == 0 {
i32::from(hl[n])
} else {
(i32::from(hl[n - 1]) + i32::from(hl[n])) >> 1
},
);
}
// H
@ -74,16 +82,20 @@ fn dwt_horizontal<const SUBBAND_WIDTH: usize>(mut buffer: &mut [i16], dwt: &[i16
let x = n * 2;
// HH
hh[n] = ((i32::from(h_src[x + 1])
- ((i32::from(h_src[x]) + i32::from(h_src[if n < SUBBAND_WIDTH - 1 { x + 2 } else { x }])) >> 1))
>> 1) as i16;
hh[n] = i32_to_i16_possible_truncation(
(i32::from(h_src[x + 1])
- ((i32::from(h_src[x]) + i32::from(h_src[if n < SUBBAND_WIDTH - 1 { x + 2 } else { x }])) >> 1))
>> 1,
);
// LH
lh[n] = (i32::from(h_src[x])
+ if n == 0 {
i32::from(hh[n])
} else {
(i32::from(hh[n - 1]) + i32::from(hh[n])) >> 1
}) as i16;
lh[n] = i32_to_i16_possible_truncation(
i32::from(h_src[x])
+ if n == 0 {
i32::from(hh[n])
} else {
(i32::from(hh[n - 1]) + i32::from(hh[n])) >> 1
},
);
}
hl = &mut hl[SUBBAND_WIDTH..];
@ -124,24 +136,30 @@ fn inverse_horizontal(mut buffer: &[i16], temp_buffer: &mut [i16], subband_width
for _ in 0..subband_width {
// Even coefficients
l_dst[0] = (i32::from(ll[0]) - ((i32::from(hl[0]) + i32::from(hl[0]) + 1) >> 1)) as i16;
h_dst[0] = (i32::from(lh[0]) - ((i32::from(hh[0]) + i32::from(hh[0]) + 1) >> 1)) as i16;
l_dst[0] = i32_to_i16_possible_truncation(i32::from(ll[0]) - ((i32::from(hl[0]) + i32::from(hl[0]) + 1) >> 1));
h_dst[0] = i32_to_i16_possible_truncation(i32::from(lh[0]) - ((i32::from(hh[0]) + i32::from(hh[0]) + 1) >> 1));
for n in 1..subband_width {
let x = n * 2;
l_dst[x] = (i32::from(ll[n]) - ((i32::from(hl[n - 1]) + i32::from(hl[n]) + 1) >> 1)) as i16;
h_dst[x] = (i32::from(lh[n]) - ((i32::from(hh[n - 1]) + i32::from(hh[n]) + 1) >> 1)) as i16;
l_dst[x] =
i32_to_i16_possible_truncation(i32::from(ll[n]) - ((i32::from(hl[n - 1]) + i32::from(hl[n]) + 1) >> 1));
h_dst[x] =
i32_to_i16_possible_truncation(i32::from(lh[n]) - ((i32::from(hh[n - 1]) + i32::from(hh[n]) + 1) >> 1));
}
// Odd coefficients
for n in 0..subband_width - 1 {
let x = n * 2;
l_dst[x + 1] = (i32::from(hl[n] << 1) + ((i32::from(l_dst[x]) + i32::from(l_dst[x + 2])) >> 1)) as i16;
h_dst[x + 1] = (i32::from(hh[n] << 1) + ((i32::from(h_dst[x]) + i32::from(h_dst[x + 2])) >> 1)) as i16;
l_dst[x + 1] = i32_to_i16_possible_truncation(
i32::from(hl[n] << 1) + ((i32::from(l_dst[x]) + i32::from(l_dst[x + 2])) >> 1),
);
h_dst[x + 1] = i32_to_i16_possible_truncation(
i32::from(hh[n] << 1) + ((i32::from(h_dst[x]) + i32::from(h_dst[x + 2])) >> 1),
);
}
let n = subband_width - 1;
let x = n * 2;
l_dst[x + 1] = (i32::from(hl[n] << 1) + i32::from(l_dst[x])) as i16;
h_dst[x + 1] = (i32::from(hh[n] << 1) + i32::from(h_dst[x])) as i16;
l_dst[x + 1] = i32_to_i16_possible_truncation(i32::from(hl[n] << 1) + i32::from(l_dst[x]));
h_dst[x + 1] = i32_to_i16_possible_truncation(i32::from(hh[n] << 1) + i32::from(h_dst[x]));
hl = &hl[subband_width..];
lh = &lh[subband_width..];
@ -157,8 +175,9 @@ fn inverse_vertical(mut buffer: &mut [i16], mut temp_buffer: &[i16], subband_wid
let total_width = subband_width * 2;
for _ in 0..total_width {
buffer[0] =
(i32::from(temp_buffer[0]) - ((i32::from(temp_buffer[subband_width * total_width]) * 2 + 1) >> 1)) as i16;
buffer[0] = i32_to_i16_possible_truncation(
i32::from(temp_buffer[0]) - ((i32::from(temp_buffer[subband_width * total_width]) * 2 + 1) >> 1),
);
let mut l = temp_buffer;
let mut lh = &temp_buffer[(subband_width - 1) * total_width..];
@ -171,18 +190,28 @@ fn inverse_vertical(mut buffer: &mut [i16], mut temp_buffer: &[i16], subband_wid
h = &h[total_width..];
// Even coefficients
dst[2 * total_width] = (i32::from(l[0]) - ((i32::from(lh[0]) + i32::from(h[0]) + 1) >> 1)) as i16;
dst[2 * total_width] =
i32_to_i16_possible_truncation(i32::from(l[0]) - ((i32::from(lh[0]) + i32::from(h[0]) + 1) >> 1));
// Odd coefficients
dst[total_width] =
(i32::from(lh[0] << 1) + ((i32::from(dst[0]) + i32::from(dst[2 * total_width])) >> 1)) as i16;
dst[total_width] = i32_to_i16_possible_truncation(
i32::from(lh[0] << 1) + ((i32::from(dst[0]) + i32::from(dst[2 * total_width])) >> 1),
);
dst = &mut dst[2 * total_width..];
}
dst[total_width] = (i32::from(lh[total_width] << 1) + ((i32::from(dst[0]) + i32::from(dst[0])) >> 1)) as i16;
dst[total_width] = i32_to_i16_possible_truncation(
i32::from(lh[total_width] << 1) + ((i32::from(dst[0]) + i32::from(dst[0])) >> 1),
);
temp_buffer = &temp_buffer[1..];
buffer = &mut buffer[1..];
}
}
#[expect(clippy::as_conversions)]
#[expect(clippy::cast_possible_truncation)]
fn i32_to_i16_possible_truncation(value: i32) -> i16 {
value as i16
}

View file

@ -1,10 +1,6 @@
#![cfg_attr(doc, doc = include_str!("../README.md"))]
#![doc(html_logo_url = "https://cdnweb.devolutions.net/images/projects/devolutions/logos/devolutions-icon-shadow.svg")]
#![allow(clippy::arithmetic_side_effects)] // FIXME: remove
#![allow(clippy::cast_lossless)] // FIXME: remove
#![allow(clippy::cast_possible_truncation)] // FIXME: remove
#![allow(clippy::cast_possible_wrap)] // FIXME: remove
#![allow(clippy::cast_sign_loss)] // FIXME: remove
pub mod color_conversion;
pub mod diff;

View file

@ -260,9 +260,12 @@ impl DecodedPointer {
} else if target.should_premultiply_alpha() {
// Calculate premultiplied alpha via integer arithmetic
let with_premultiplied_alpha = [
((color[0] as u16 * color[0] as u16) >> 8) as u8,
((color[1] as u16 * color[1] as u16) >> 8) as u8,
((color[2] as u16 * color[2] as u16) >> 8) as u8,
u8::try_from((u16::from(color[0]) * u16::from(color[0])) >> 8)
.expect("(u16 >> 8) fits into u8"),
u8::try_from((u16::from(color[1]) * u16::from(color[1])) >> 8)
.expect("(u16 >> 8) fits into u8"),
u8::try_from((u16::from(color[2]) * u16::from(color[2])) >> 8)
.expect("(u16 >> 8) fits into u8"),
color[3],
];
bitmap_data.extend_from_slice(&with_premultiplied_alpha);

View file

@ -11,7 +11,7 @@ pub fn decode(buffer: &mut [i16], quant: &Quant) {
let (first_level, buffer) = buffer.split_at_mut(FIRST_LEVEL_SUBBANDS_COUNT * FIRST_LEVEL_SIZE);
let (second_level, third_level) = buffer.split_at_mut(SECOND_LEVEL_SUBBANDS_COUNT * SECOND_LEVEL_SIZE);
let decode_chunk = |a: (&mut [i16], u8)| decode_block(a.0, a.1 as i16 - 1);
let decode_chunk = |a: (&mut [i16], u8)| decode_block(a.0, i16::from(a.1) - 1);
first_level
.chunks_mut(FIRST_LEVEL_SIZE)
@ -49,7 +49,7 @@ pub fn encode(buffer: &mut [i16], quant: &Quant) {
let (first_level, buffer) = buffer.split_at_mut(FIRST_LEVEL_SUBBANDS_COUNT * FIRST_LEVEL_SIZE);
let (second_level, third_level) = buffer.split_at_mut(SECOND_LEVEL_SUBBANDS_COUNT * SECOND_LEVEL_SIZE);
let encode_chunk = |a: (&mut [i16], u8)| encode_block(a.0, a.1 as i16 - 1);
let encode_chunk = |a: (&mut [i16], u8)| encode_block(a.0, i16::from(a.1) - 1);
first_level
.chunks_mut(FIRST_LEVEL_SIZE)

View file

@ -195,8 +195,9 @@ impl<'a> BitmapStreamDecoderImpl<'a> {
}
fn write_aycocg_planes_to_rgb24(&self, params: AYCoCgParams, planes: &[u8], dst: &mut Vec<u8>) {
#![allow(clippy::similar_names)] // Its hard to find better names for co, cg, etc.
let sample_shift = params.chroma_subsampling as usize;
#![allow(clippy::similar_names, reason = "its hard to find better names for co, cg, etc")]
let sample_shift = usize::from(params.chroma_subsampling);
let (y_offset, co_offset, cg_offset) = (
self.color_plane_offsets[0],
@ -265,12 +266,13 @@ fn ycocg_with_cll_to_rgb(cll: u8, y: u8, co: u8, cg: u8) -> Rgb {
// |R| |1 1/2 -1/2| |Y |
// |G| = |1 0 1/2| * |Co|
// |B| |1 -1/2 -1/2| |Cg|
let chroma_shift = (cll - 1) as usize;
let chroma_shift = cll - 1;
let clip_i16 = |v: i16| v.clamp(0, 255) as u8;
let clip_i16 =
|v: i16| u8::try_from(v.clamp(0, 255)).expect("fits into u8 because the value is clamped to [0..256]");
let co_signed = (co << chroma_shift) as i8;
let cg_signed = (cg << chroma_shift) as i8;
let co_signed = (co << chroma_shift).cast_signed();
let cg_signed = (cg << chroma_shift).cast_signed();
let y = i16::from(y);
let co = i16::from(co_signed);

View file

@ -93,9 +93,9 @@ impl RlePlaneDecoder {
let raw_bytes_field = (control_byte >> 4) & 0x0F;
let (run_length, raw_bytes_count) = match rle_bytes_field {
1 => (16 + raw_bytes_field as usize, 0),
2 => (32 + raw_bytes_field as usize, 0),
rle_control => (rle_control as usize, raw_bytes_field as usize),
1 => (16 + usize::from(raw_bytes_field), 0),
2 => (32 + usize::from(raw_bytes_field), 0),
rle_control => (usize::from(rle_control), usize::from(raw_bytes_field)),
};
self.decoded_data_len = raw_bytes_count + run_length;
@ -207,7 +207,8 @@ impl<I: Iterator> RleEncoderScanlineIterator<I> {
}
fn delta_value(prev: u8, next: u8) -> u8 {
let mut result = (next as i16 - prev as i16) as u8;
let mut result = u8::try_from((i16::from(next) - i16::from(prev)) & 0xFF)
.expect("masking with 0xFF ensures that the value fits into u8");
// bit magic from 3.1.9.2.1 of [MS-RDPEGDI].
if result < 128 {
@ -326,7 +327,10 @@ impl RlePlaneEncoder {
raw = &raw[15..];
}
let control = ((raw.len() as u8) << 4) + cmp::min(run, 15) as u8;
let raw_len = u8::try_from(raw.len()).expect("max value is guaranteed to be 15 due to the prior while loop");
let run_capped = u8::try_from(cmp::min(run, 15)).expect("max value is guaranteed to be 15");
let control = (raw_len << 4) + run_capped;
ensure_size!(dst: dst, size: raw.len() + 1);
@ -352,7 +356,8 @@ impl RlePlaneEncoder {
while run >= 16 {
ensure_size!(dst: dst, size: 1);
let current = cmp::min(run, MAX_DECODED_SEGMENT_SIZE) as u8;
let current = u8::try_from(cmp::min(run, MAX_DECODED_SEGMENT_SIZE))
.expect("max value is guaranteed to be MAX_DECODED_SEGMENT_SIZE (47)");
let c_raw_bytes = cmp::min(current / 16, 2);
let n_run_length = current - c_raw_bytes * 16;
@ -361,7 +366,7 @@ impl RlePlaneEncoder {
dst.write_u8(control);
written += 1;
run -= current as usize;
run -= usize::from(current);
}
if run > 0 {

View file

@ -63,17 +63,23 @@ impl<'a> BitStream<'a> {
}
pub fn encode(mode: EntropyAlgorithm, input: &[i16], tile: &mut [u8]) -> Result<usize, RlgrError> {
let mut k: u32 = 1;
let kr: u32 = 1;
let mut kp: u32 = k << LS_GR;
let mut krp: u32 = kr << LS_GR;
#![expect(
clippy::as_conversions,
reason = "u32-to-usize and usize-to-u32 conversions, mostly fine, and hot loop"
)]
if input.is_empty() {
return Err(RlgrError::EmptyTile);
}
let mut k: u32 = 1;
let kr: u32 = 1;
let mut kp: u32 = k << LS_GR;
let mut krp: u32 = kr << LS_GR;
let mut bits = BitStream::new(tile);
let mut input = input.iter().peekable();
while input.peek().is_some() {
match CompressionMode::from(k) {
CompressionMode::RunLength => {
@ -98,7 +104,7 @@ pub fn encode(mode: EntropyAlgorithm, input: &[i16], tile: &mut [u8]) -> Result<
bits.output_bits(k as usize, nz);
if let Some(val) = input.next() {
let mag = val.unsigned_abs() as u32;
let mag = u32::from(val.unsigned_abs());
bits.output_bit(1, *val < 0);
code_gr(&mut bits, &mut krp, mag - 1);
}
@ -152,37 +158,53 @@ pub fn encode(mode: EntropyAlgorithm, input: &[i16], tile: &mut [u8]) -> Result<
fn get_2magsign(val: i16) -> u32 {
let sign = if val < 0 { 1 } else { 0 };
(val.unsigned_abs() as u32) * 2 - sign
(u32::from(val.unsigned_abs())) * 2 - sign
}
fn code_gr(bits: &mut BitStream<'_>, krp: &mut u32, val: u32) {
let kr = (*krp >> LS_GR) as usize;
let vk = (val >> kr) as usize;
#![expect(
clippy::as_conversions,
reason = "u32-to-usize and usize-to-u32 conversions, mostly fine, and hot loop"
)]
bits.output_bit(vk, true);
let kr = (*krp >> LS_GR) as usize;
let vk = val >> kr;
let vk_usize = vk as usize;
bits.output_bit(vk_usize, true);
bits.output_bit(1, false);
if kr != 0 {
let remainder = val & ((1 << kr) - 1);
bits.output_bits(kr, remainder);
}
if vk == 0 {
*krp = krp.saturating_sub(2);
} else if vk > 1 {
*krp = min(*krp + vk as u32, KP_MAX);
*krp = min(*krp + vk, KP_MAX);
}
}
pub fn decode(mode: EntropyAlgorithm, tile: &[u8], mut output: &mut [i16]) -> Result<(), RlgrError> {
let mut k: u32 = 1;
let mut kr: u32 = 1;
let mut kp: u32 = k << LS_GR;
let mut krp: u32 = kr << LS_GR;
#![expect(
clippy::as_conversions,
clippy::cast_possible_truncation,
reason = "u32-to-usize and usize-to-u32 conversions, mostly fine, and hot loop"
)]
if tile.is_empty() {
return Err(RlgrError::EmptyTile);
}
let mut k: u32 = 1;
let mut kr: u32 = 1;
let mut kp: u32 = k << LS_GR;
let mut krp: u32 = kr << LS_GR;
let mut bits = Bits::new(BitSlice::from_slice(tile));
while !bits.is_empty() && !output.is_empty() {
match CompressionMode::from(k) {
CompressionMode::RunLength => {
@ -201,7 +223,7 @@ pub fn decode(mode: EntropyAlgorithm, tile: &[u8], mut output: &mut [i16]) -> Re
kp = kp.saturating_sub(DN_GR);
k = kp >> LS_GR;
let magnitude = compute_rl_magnitude(sign_bit, code_remainder);
let magnitude = compute_rl_magnitude(sign_bit, code_remainder)?;
let size = min(run as usize, output.len());
fill(&mut output[..size], 0);
@ -218,7 +240,7 @@ pub fn decode(mode: EntropyAlgorithm, tile: &[u8], mut output: &mut [i16]) -> Re
match mode {
EntropyAlgorithm::Rlgr1 => {
let magnitude = compute_rlgr1_magnitude(code_remainder, &mut k, &mut kp);
let magnitude = compute_rlgr1_magnitude(code_remainder, &mut k, &mut kp)?;
write_byte!(output, magnitude);
}
EntropyAlgorithm::Rlgr3 => {
@ -234,10 +256,10 @@ pub fn decode(mode: EntropyAlgorithm, tile: &[u8], mut output: &mut [i16]) -> Re
k = kp >> LS_GR;
}
let magnitude = compute_rlgr3_magnitude(val1);
let magnitude = compute_rlgr3_magnitude(val1)?;
write_byte!(output, magnitude);
let magnitude = compute_rlgr3_magnitude(val2);
let magnitude = compute_rlgr3_magnitude(val2)?;
write_byte!(output, magnitude);
}
}
@ -245,7 +267,7 @@ pub fn decode(mode: EntropyAlgorithm, tile: &[u8], mut output: &mut [i16]) -> Re
}
}
// fill remaining buffer with zeros
// Fill remaining buffer with zeros.
fill(output, 0);
Ok(())
@ -288,37 +310,41 @@ fn count_run(number_of_zeros: usize, k: &mut u32, kp: &mut u32) -> u32 {
.sum()
}
fn compute_rl_magnitude(sign_bit: u8, code_remainder: u32) -> i16 {
fn compute_rl_magnitude(sign_bit: u8, code_remainder: u32) -> Result<i16, RlgrError> {
let rl_magnitude =
i16::try_from(code_remainder + 1).map_err(|_| RlgrError::InvalidIntegralConversion("code remainder + 1"))?;
if sign_bit != 0 {
-((code_remainder + 1) as i16)
Ok(-rl_magnitude)
} else {
(code_remainder + 1) as i16
Ok(rl_magnitude)
}
}
fn compute_rlgr1_magnitude(code_remainder: u32, k: &mut u32, kp: &mut u32) -> i16 {
fn compute_rlgr1_magnitude(code_remainder: u32, k: &mut u32, kp: &mut u32) -> Result<i16, RlgrError> {
if code_remainder == 0 {
*kp = min(*kp + UQ_GR, KP_MAX);
*k = *kp >> LS_GR;
0
Ok(0)
} else {
*kp = kp.saturating_sub(DQ_GR);
*k = *kp >> LS_GR;
if code_remainder % 2 != 0 {
-(((code_remainder + 1) >> 1) as i16)
Ok(-i16::try_from((code_remainder + 1) >> 1)
.map_err(|_| RlgrError::InvalidIntegralConversion("(code remainder + 1) >> 1"))?)
} else {
(code_remainder >> 1) as i16
i16::try_from(code_remainder >> 1).map_err(|_| RlgrError::InvalidIntegralConversion("code remainder >> 1"))
}
}
}
fn compute_rlgr3_magnitude(val: u32) -> i16 {
fn compute_rlgr3_magnitude(val: u32) -> Result<i16, RlgrError> {
if val % 2 != 0 {
-(((val + 1) >> 1) as i16)
Ok(-i16::try_from((val + 1) >> 1).map_err(|_| RlgrError::InvalidIntegralConversion("(val + 1) >> 1"))?)
} else {
(val >> 1) as i16
i16::try_from(val >> 1).map_err(|_| RlgrError::InvalidIntegralConversion("val >> 1"))
}
}
@ -335,11 +361,17 @@ fn compute_n_index(code_remainder: u32) -> usize {
}
fn update_parameters_according_to_number_of_ones(number_of_ones: usize, kr: &mut u32, krp: &mut u32) {
#![expect(
clippy::as_conversions,
clippy::cast_possible_truncation,
reason = "usize-to-u32 conversions, hot loop"
)]
if number_of_ones == 0 {
*krp = (*krp).saturating_sub(2);
*kr = *krp >> LS_GR;
} else if number_of_ones > 1 {
*krp = min(*krp + number_of_ones as u32, KP_MAX);
*krp = min(*krp + (number_of_ones as u32), KP_MAX);
*kr = *krp >> LS_GR;
}
}
@ -365,6 +397,7 @@ pub enum RlgrError {
Io(io::Error),
Yuv(YuvError),
EmptyTile,
InvalidIntegralConversion(&'static str),
}
impl core::fmt::Display for RlgrError {
@ -373,6 +406,7 @@ impl core::fmt::Display for RlgrError {
Self::Io(_) => write!(f, "IO error"),
Self::Yuv(_) => write!(f, "YUV error"),
Self::EmptyTile => write!(f, "the input tile is empty"),
Self::InvalidIntegralConversion(s) => write!(f, "invalid `{s}`: out of range integral type conversion"),
}
}
}
@ -383,6 +417,7 @@ impl core::error::Error for RlgrError {
Self::Io(error) => Some(error),
Self::Yuv(error) => Some(error),
Self::EmptyTile => None,
Self::InvalidIntegralConversion(_) => None,
}
}
}

View file

@ -23,12 +23,14 @@ impl<'a> SegmentedDataPdu<'a> {
match descriptor {
SegmentedDescriptor::Single => Ok(SegmentedDataPdu::Single(BulkEncodedData::from_buffer(buffer)?)),
SegmentedDescriptor::Multipart => {
let segment_count = buffer.read_u16::<LittleEndian>()? as usize;
let uncompressed_size = buffer.read_u32::<LittleEndian>()? as usize;
let segment_count = usize::from(buffer.read_u16::<LittleEndian>()?);
let uncompressed_size = usize::try_from(buffer.read_u32::<LittleEndian>()?)
.map_err(|_| ZgfxError::InvalidIntegralConversion("segments uncompressed size"))?;
let mut segments = Vec::with_capacity(segment_count);
for _ in 0..segment_count {
let size = buffer.read_u32::<LittleEndian>()? as usize;
let size = usize::try_from(buffer.read_u32::<LittleEndian>()?)
.map_err(|_| ZgfxError::InvalidIntegralConversion("segment data size"))?;
let (segment_data, new_buffer) = buffer.split_at(size);
buffer = new_buffer;

View file

@ -79,8 +79,8 @@ impl Decompressor {
let mut bits = BitSlice::from_slice(encoded_data);
// The value of the last byte indicates the number of unused bits in the final byte
bits =
&bits[..8 * (encoded_data.len() - 1) - *encoded_data.last().expect("encoded_data is not empty") as usize];
bits = &bits
[..8 * (encoded_data.len() - 1) - usize::from(*encoded_data.last().expect("encoded_data is not empty"))];
let mut bits = Bits::new(bits);
let mut bytes_written = 0;
@ -135,14 +135,15 @@ fn handle_match(
distance_base: u32,
history: &mut FixedCircularBuffer,
output: &mut Vec<u8>,
) -> io::Result<usize> {
) -> Result<usize, ZgfxError> {
// Each token has been assigned a different base distance
// and number of additional value bits to be added to compute the full distance.
let distance = (distance_base + bits.split_to(distance_value_size).load_be::<u32>()) as usize;
let distance = usize::try_from(distance_base + bits.split_to(distance_value_size).load_be::<u32>())
.map_err(|_| ZgfxError::InvalidIntegralConversion("token's full distance"))?;
if distance == 0 {
read_unencoded_bytes(bits, history, output)
read_unencoded_bytes(bits, history, output).map_err(ZgfxError::from)
} else {
read_encoded_bytes(bits, distance, history, output)
}
@ -156,7 +157,7 @@ fn read_unencoded_bytes(
// A match distance of zero is a special case,
// which indicates that an unencoded run of bytes follows.
// The count of bytes is encoded as a 15-bit value
let length = bits.split_to(15).load_be::<u32>() as usize;
let length = bits.split_to(15).load_be::<usize>();
if bits.remaining_bits_of_last_byte() > 0 {
let pad_to_byte_boundary = 8 - bits.remaining_bits_of_last_byte();
@ -179,7 +180,7 @@ fn read_encoded_bytes(
distance: usize,
history: &mut FixedCircularBuffer,
output: &mut Vec<u8>,
) -> io::Result<usize> {
) -> Result<usize, ZgfxError> {
// A match length prefix follows the token and indicates
// how many additional bits will be needed to get the full length
// (the number of bytes to be copied).
@ -192,9 +193,12 @@ fn read_encoded_bytes(
3
} else {
let length = bits.split_to(length_token_size + 1).load_be::<u32>() as usize;
let length = bits.split_to(length_token_size + 1).load_be::<usize>();
let base = 2u32.pow(length_token_size as u32 + 1) as usize;
let length_token_size = u32::try_from(length_token_size)
.map_err(|_| ZgfxError::InvalidIntegralConversion("length of the token size"))?;
let base = 2usize.pow(length_token_size + 1);
base + length
};
@ -441,6 +445,7 @@ pub enum ZgfxError {
uncompressed_size: usize,
},
TokenBitsNotFound,
InvalidIntegralConversion(&'static str),
}
impl core::fmt::Display for ZgfxError {
@ -457,6 +462,7 @@ impl core::fmt::Display for ZgfxError {
"decompressed size of segments ({decompressed_size}) does not equal to uncompressed size ({uncompressed_size})",
),
Self::TokenBitsNotFound => write!(f, "token bits not found"),
Self::InvalidIntegralConversion(type_name) => write!(f, "invalid `{type_name}`: out of range integral type conversion"),
}
}
}
@ -469,6 +475,7 @@ impl core::error::Error for ZgfxError {
Self::InvalidSegmentedDescriptor => None,
Self::InvalidDecompressedSize { .. } => None,
Self::TokenBitsNotFound => None,
Self::InvalidIntegralConversion(_) => None,
}
}
}

View file

@ -24,6 +24,10 @@ pub enum MouseButton {
}
impl MouseButton {
#[expect(
clippy::as_conversions,
reason = "guarantees discriminant layout, and as is the only way to cast enum -> primitive"
)]
pub fn as_idx(self) -> usize {
self as usize
}
@ -78,7 +82,11 @@ impl Scancode {
pub const fn from_u16(scancode: u16) -> Self {
let extended = scancode & 0xE000 == 0xE000;
#[expect(clippy::cast_possible_truncation)] // truncating on purpose
#[expect(
clippy::as_conversions,
clippy::cast_possible_truncation,
reason = "truncating on purpose"
)]
let code = scancode as u8;
Self { code, extended }

View file

@ -221,7 +221,8 @@ impl GwClient {
let mut cur = ReadCursor::new(&msg);
let hdr = PktHdr::decode(&mut cur).map_err(|e| custom_err!("Header Decode", e))?;
assert!(cur.len() >= hdr.length as usize - hdr.size());
let header_length = usize::try_from(hdr.length).map_err(|_| Error::new("PktHdr too big", GwErrorKind::Decode))?;
assert!(cur.len() >= header_length - hdr.size());
match hdr.ty {
PktTy::Keepalive => {
continue;
@ -287,7 +288,10 @@ impl GwConn {
let mut cur = ReadCursor::new(&msg);
let hdr = PktHdr::decode(&mut cur).map_err(|_| Error::new("PktHdr", GwErrorKind::Decode))?;
if cur.len() != hdr.length as usize - hdr.size() {
let header_length =
usize::try_from(hdr.length).map_err(|_| Error::new("PktHdr too big", GwErrorKind::Decode))?;
if cur.len() != header_length - hdr.size() {
return Err(Error::new("read_packet", GwErrorKind::PacketEof));
}
@ -315,7 +319,7 @@ impl GwConn {
async fn tunnel(&mut self) -> Result<(), Error> {
let req = TunnelReqPkt {
// Havent seen any server working without this.
caps: HttpCapsTy::MessagingConsentSign as u32,
caps: HttpCapsTy::MessagingConsentSign.as_u32(),
fields_present: 0,
..TunnelReqPkt::default()
};

View file

@ -37,6 +37,16 @@ pub(crate) enum PktTy {
Keepalive = 0x0D,
}
impl PktTy {
#[expect(
clippy::as_conversions,
reason = "guarantees discriminant layout, and as is the only way to cast enum -> primitive"
)]
fn as_u16(self) -> u16 {
self as u16
}
}
impl TryFrom<u16> for PktTy {
type Error = ();
@ -78,7 +88,7 @@ impl Encode for PktHdr {
fn encode(&self, dst: &mut WriteCursor<'_>) -> ironrdp_core::EncodeResult<()> {
ensure_size!(in: dst, size: self.size());
dst.write_u16(self.ty as u16);
dst.write_u16(self.ty.as_u16());
dst.write_u16(self._reserved);
dst.write_u32(self.length);
@ -215,6 +225,7 @@ impl Encode for TunnelReqPkt {
/// 2.2.5.3.9 HTTP_CAPABILITY_TYPE Enumeration
#[repr(u32)]
#[expect(dead_code)]
#[derive(Copy, Clone)]
pub(crate) enum HttpCapsTy {
QuarSOH = 1,
IdleTimeout = 2,
@ -224,8 +235,19 @@ pub(crate) enum HttpCapsTy {
UdpTransport = 0x20,
}
impl HttpCapsTy {
#[expect(
clippy::as_conversions,
reason = "guarantees discriminant layout, and as is the only way to cast enum -> primitive"
)]
pub(crate) fn as_u32(self) -> u32 {
self as u32
}
}
/// 2.2.5.3.8 HTTP_TUNNEL_RESPONSE_FIELDS_PRESENT_FLAGS
#[repr(u16)]
#[derive(Copy, Clone)]
enum HttpTunnelResponseFields {
TunnelID = 1,
Caps = 2,
@ -234,6 +256,16 @@ enum HttpTunnelResponseFields {
Consent = 0x10,
}
impl HttpTunnelResponseFields {
#[expect(
clippy::as_conversions,
reason = "guarantees discriminant layout, and as is the only way to cast enum -> primitive"
)]
fn as_u16(self) -> u16 {
self as u16
}
}
/// 2.2.10.20 HTTP_TUNNEL_RESPONSE Structure
#[derive(Debug, Default)]
pub(crate) struct TunnelRespPkt {
@ -266,26 +298,26 @@ impl Decode<'_> for TunnelRespPkt {
..TunnelRespPkt::default()
};
if pkt.fields_present & (HttpTunnelResponseFields::TunnelID as u16) != 0 {
if pkt.fields_present & (HttpTunnelResponseFields::TunnelID.as_u16()) != 0 {
ensure_size!(in: src, size: 4);
pkt.tunnel_id = Some(src.read_u32());
}
if pkt.fields_present & (HttpTunnelResponseFields::Caps as u16) != 0 {
if pkt.fields_present & (HttpTunnelResponseFields::Caps.as_u16()) != 0 {
ensure_size!(in: src, size: 4);
pkt.caps_flags = Some(src.read_u32());
}
if pkt.fields_present & (HttpTunnelResponseFields::Soh as u16) != 0 {
if pkt.fields_present & (HttpTunnelResponseFields::Soh.as_u16()) != 0 {
ensure_size!(in: src, size: 2 + 2);
pkt.nonce = Some(src.read_u16());
let len = src.read_u16();
ensure_size!(in: src, size: len as usize);
pkt.server_cert = src.read_slice(len as usize).to_vec();
let len = usize::from(src.read_u16());
ensure_size!(in: src, size: len);
pkt.server_cert = src.read_slice(len).to_vec();
}
if pkt.fields_present & (HttpTunnelResponseFields::Consent as u16) != 0 {
if pkt.fields_present & (HttpTunnelResponseFields::Consent.as_u16()) != 0 {
ensure_size!(in: src, size: 2);
let len = src.read_u16();
ensure_size!(in: src, size: len as usize);
pkt.consent_msg = src.read_slice(len as usize).to_vec();
let len = usize::from(src.read_u16());
ensure_size!(in: src, size: len);
pkt.consent_msg = src.read_slice(len).to_vec();
}
Ok(pkt)
@ -330,12 +362,12 @@ impl Decode<'_> for ExtendedAuthPkt {
fn decode(src: &mut ReadCursor<'_>) -> ironrdp_core::DecodeResult<Self> {
ensure_size!(in: src, size: 4 + 2);
let error_code = src.read_u32();
let len = src.read_u16();
ensure_size!(in: src, size: len as usize);
let len = usize::from(src.read_u16());
ensure_size!(in: src, size: len);
Ok(ExtendedAuthPkt {
error_code,
blob: src.read_slice(len as usize).to_vec(),
blob: src.read_slice(len).to_vec(),
})
}
}
@ -497,9 +529,9 @@ impl Decode<'_> for ChannelResp {
}
if resp.fields_present & 4 != 0 {
ensure_size!(in: src, size: 2);
let len = src.read_u16();
ensure_size!(in: src, size: len as usize);
resp.authn_cookie = src.read_slice(len as usize).to_vec();
let len = usize::from(src.read_u16());
ensure_size!(in: src, size: len);
resp.authn_cookie = src.read_slice(len).to_vec();
}
Ok(resp)
}
@ -538,10 +570,10 @@ impl Encode for DataPkt<'_> {
impl<'a> Decode<'a> for DataPkt<'a> {
fn decode(src: &mut ReadCursor<'a>) -> ironrdp_core::DecodeResult<Self> {
ensure_size!(in: src, size: 2);
let len = src.read_u16();
ensure_size!(in: src, size: len as usize);
let len = usize::from(src.read_u16());
ensure_size!(in: src, size: len);
Ok(DataPkt {
data: src.read_slice(len as usize),
data: src.read_slice(len),
})
}
}

View file

@ -95,7 +95,7 @@ impl Encode for ExtendedMonitorInfo {
dst.write_u32(self.physical_width);
dst.write_u32(self.physical_height);
dst.write_u32(self.orientation.as_u32());
dst.write_u32(u32::from(self.orientation.as_u16()));
dst.write_u32(self.desktop_scale_factor);
dst.write_u32(self.device_scale_factor);
@ -132,7 +132,7 @@ impl<'de> Decode<'de> for ExtendedMonitorInfo {
}
}
#[repr(u32)]
#[repr(u16)]
#[derive(Debug, Copy, Clone, PartialEq, Eq, FromPrimitive)]
pub enum MonitorOrientation {
Landscape = 0,
@ -146,7 +146,7 @@ impl MonitorOrientation {
clippy::as_conversions,
reason = "guarantees discriminant layout, and as is the only way to cast enum -> primitive"
)]
fn as_u32(self) -> u32 {
self as u32
pub fn as_u16(self) -> u16 {
self as u16
}
}

View file

@ -637,6 +637,10 @@ pub enum EntropyBits {
}
impl EntropyBits {
#[expect(
clippy::as_conversions,
reason = "guarantees discriminant layout, and as is the only way to cast enum -> primitive"
)]
fn as_u8(self) -> u8 {
self as u8
}

View file

@ -412,6 +412,10 @@ impl RdpSpecificCode {
}
}
#[expect(
clippy::as_conversions,
reason = "guarantees discriminant layout, and as is the only way to cast enum -> primitive"
)]
fn as_u32(self) -> u32 {
self as u32
}

View file

@ -976,6 +976,10 @@ pub enum PixelFormat {
}
impl PixelFormat {
#[expect(
clippy::as_conversions,
reason = "guarantees discriminant layout, and as is the only way to cast enum -> primitive"
)]
fn as_u8(self) -> u8 {
self as u8
}

View file

@ -1,10 +1,6 @@
#![cfg_attr(doc, doc = include_str!("../README.md"))]
#![doc(html_logo_url = "https://cdnweb.devolutions.net/images/projects/devolutions/logos/devolutions-icon-shadow.svg")]
#![allow(clippy::arithmetic_side_effects)] // FIXME: remove
#![allow(clippy::cast_lossless)] // FIXME: remove
#![allow(clippy::cast_possible_truncation)] // FIXME: remove
#![allow(clippy::cast_possible_wrap)] // FIXME: remove
#![allow(clippy::cast_sign_loss)] // FIXME: remove
use ironrdp_core::{decode_cursor, impl_as_any, ReadCursor};
use ironrdp_pdu::gcc::ChannelName;

View file

@ -154,9 +154,15 @@ impl ClientNameRequest {
pub fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> {
ensure_size!(in: dst, size: self.size());
let encoded_computer_name_length = cast_length!(
"encoded computer name length",
encoded_str_len(self.computer_name(), self.unicode_flag().into(), true)
)?;
dst.write_u32(self.unicode_flag().into());
dst.write_u32(0); // // CodePage (4 bytes): it MUST be set to 0
dst.write_u32(encoded_str_len(self.computer_name(), self.unicode_flag().into(), true) as u32);
dst.write_u32(encoded_computer_name_length);
write_string_to_cursor(dst, self.computer_name(), self.unicode_flag().into(), true)
}
@ -186,6 +192,10 @@ impl From<ClientNameRequestUnicodeFlag> for CharacterSet {
}
impl From<ClientNameRequestUnicodeFlag> for u32 {
#[expect(
clippy::as_conversions,
reason = "guarantees discriminant layout, and as is the only way to cast enum -> primitive"
)]
fn from(val: ClientNameRequestUnicodeFlag) -> Self {
val as u32
}
@ -431,7 +441,7 @@ impl CapabilityHeader {
fn new_general() -> Self {
Self {
cap_type: CapabilityType::General,
length: (Self::SIZE + GeneralCapabilitySet::SIZE) as u16,
length: u16::try_from(Self::SIZE + GeneralCapabilitySet::SIZE).expect("value fits into u16"),
version: GENERAL_CAPABILITY_VERSION_02,
}
}
@ -439,7 +449,7 @@ impl CapabilityHeader {
fn new_smartcard() -> Self {
Self {
cap_type: CapabilityType::Smartcard,
length: Self::SIZE as u16,
length: u16::try_from(Self::SIZE).expect("value fits into u16"),
version: SMARTCARD_CAPABILITY_VERSION_01,
}
}
@ -447,7 +457,7 @@ impl CapabilityHeader {
fn new_drive() -> Self {
Self {
cap_type: CapabilityType::Drive,
length: Self::SIZE as u16,
length: u16::try_from(Self::SIZE).expect("value fits into u16"),
version: DRIVE_CAPABILITY_VERSION_02,
}
}
@ -490,6 +500,10 @@ enum CapabilityType {
}
impl From<CapabilityType> for u16 {
#[expect(
clippy::as_conversions,
reason = "guarantees discriminant layout, and as is the only way to cast enum -> primitive"
)]
fn from(cap_type: CapabilityType) -> Self {
cap_type as u16
}
@ -990,6 +1004,10 @@ pub enum DeviceType {
}
impl From<DeviceType> for u32 {
#[expect(
clippy::as_conversions,
reason = "guarantees discriminant layout, and as is the only way to cast enum -> primitive"
)]
fn from(device_type: DeviceType) -> Self {
device_type as u32
}
@ -1211,6 +1229,10 @@ impl TryFrom<u32> for MajorFunction {
}
impl From<MajorFunction> for u32 {
#[expect(
clippy::as_conversions,
reason = "guarantees discriminant layout, and as is the only way to cast enum -> primitive"
)]
fn from(major_function: MajorFunction) -> Self {
major_function as u32
}
@ -1253,12 +1275,6 @@ impl From<MinorFunction> for u32 {
}
}
impl From<MinorFunction> for u8 {
fn from(minor_function: MinorFunction) -> Self {
minor_function.0 as u8
}
}
/// [2.2.1.4.5] Device Control Request (DR_CONTROL_REQ)
///
/// [2.2.1.4.5]: https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpefs/30662c80-ec6e-4ed1-9004-2e6e367bb59f

View file

@ -574,6 +574,10 @@ impl ReturnCode {
}
impl From<ReturnCode> for u32 {
#[expect(
clippy::as_conversions,
reason = "guarantees discriminant layout, and as is the only way to cast enum -> primitive"
)]
fn from(val: ReturnCode) -> Self {
val as u32
}
@ -1244,7 +1248,7 @@ impl rpce::HeaderlessDecode for TransmitCall {
#[derive(Debug, PartialEq, Clone)]
pub struct SCardIORequest {
pub protocol: CardProtocol,
pub extra_bytes_length: u32,
pub extra_bytes_length: usize,
pub extra_bytes: Vec<u8>,
}
@ -1255,7 +1259,7 @@ impl ndr::Decode for SCardIORequest {
{
ensure_size!(in: src, size: size_of::<u32>() * 2);
let protocol = CardProtocol::from_bits_retain(src.read_u32());
let extra_bytes_length = src.read_u32();
let extra_bytes_length = cast_length!("SCardIORequest", "extra_bytes_length", src.read_u32())?;
let _extra_bytes_ptr = ndr::decode_ptr(src, index)?;
let extra_bytes = Vec::new();
Ok(Self {
@ -1267,9 +1271,8 @@ impl ndr::Decode for SCardIORequest {
fn decode_value(&mut self, src: &mut ReadCursor<'_>, charset: Option<CharacterSet>) -> DecodeResult<()> {
expect_no_charset(charset)?;
let extra_bytes_length: usize = cast_length!("TransmitCall", "extra_bytes_length", self.extra_bytes_length)?;
ensure_size!(in: src, size: extra_bytes_length);
self.extra_bytes = src.read_slice(extra_bytes_length).to_vec();
ensure_size!(in: src, size: self.extra_bytes_length);
self.extra_bytes = src.read_slice(self.extra_bytes_length).to_vec();
Ok(())
}
}
@ -1277,8 +1280,11 @@ impl ndr::Decode for SCardIORequest {
impl ndr::Encode for SCardIORequest {
fn encode_ptr(&self, index: &mut u32, dst: &mut WriteCursor<'_>) -> EncodeResult<()> {
ensure_size!(in: dst, size: self.size_ptr());
let extra_bytes_length = cast_length!("SCardIORequest", "extra_bytes_length", self.extra_bytes_length)?;
dst.write_u32(self.protocol.bits());
ndr::encode_ptr(Some(self.extra_bytes_length), index, dst)
ndr::encode_ptr(Some(extra_bytes_length), index, dst)
}
fn encode_value(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> {
@ -1292,7 +1298,7 @@ impl ndr::Encode for SCardIORequest {
}
fn size_value(&self) -> usize {
self.extra_bytes_length as usize
self.extra_bytes_length
}
}
@ -1485,6 +1491,10 @@ pub enum CardState {
}
impl From<CardState> for u32 {
#[expect(
clippy::as_conversions,
reason = "guarantees discriminant layout, and as is the only way to cast enum -> primitive"
)]
fn from(val: CardState) -> Self {
val as u32
}

View file

@ -244,6 +244,10 @@ impl TryFrom<u8> for Endianness {
}
impl From<Endianness> for u8 {
#[expect(
clippy::as_conversions,
reason = "guarantees discriminant layout, and as is the only way to cast enum -> primitive"
)]
fn from(endianness: Endianness) -> Self {
endianness as u8
}

View file

@ -374,6 +374,10 @@ impl TryFrom<u16> for Component {
}
impl From<Component> for u16 {
#[expect(
clippy::as_conversions,
reason = "guarantees discriminant layout, and as is the only way to cast enum -> primitive"
)]
fn from(component: Component) -> Self {
component as u16
}
@ -454,6 +458,10 @@ impl Display for PacketId {
}
impl From<PacketId> for u16 {
#[expect(
clippy::as_conversions,
reason = "guarantees discriminant layout, and as is the only way to cast enum -> primitive"
)]
fn from(packet_id: PacketId) -> Self {
packet_id as u16
}

View file

@ -160,6 +160,10 @@ impl DecodeStream {
}
};
#[expect(
clippy::as_conversions,
reason = "opus::Channels has no conversions to usize implemented"
)]
let mut pcm = vec![0u8; nb_samples * chan as usize * size_of::<i16>()];
if let Err(error) = dec.decode(&pkt, bytemuck::cast_slice_mut(pcm.as_mut_slice()), false) {
error!(?error, "Failed to decode an Opus packet");

View file

@ -80,7 +80,7 @@ impl Rdpsnd {
server_format
.formats
.get(format_no as usize)
.get(usize::from(format_no))
.ok_or_else(|| pdu_other_err!("invalid format"))
}
@ -196,7 +196,7 @@ impl SvcProcessor for Rdpsnd {
match pdu {
// TODO: handle WaveInfo for < v8
pdu::ServerAudioOutputPdu::Wave2(pdu) => {
let format_no = pdu.format_no as usize;
let format_no = usize::from(pdu.format_no);
let ts = pdu.audio_timestamp;
self.handler.wave(format_no, ts, pdu.data);
return Ok(self.wave_confirm(pdu.timestamp, pdu.block_no)?.into());

View file

@ -51,6 +51,10 @@ impl TryFrom<u16> for Version {
}
impl From<Version> for u16 {
#[expect(
clippy::as_conversions,
reason = "guarantees discriminant layout, and as is the only way to cast enum -> primitive"
)]
fn from(version: Version) -> Self {
version as u16
}
@ -442,9 +446,8 @@ impl<'de> Decode<'de> for ClientAudioFormatPdu {
ensure_fixed_part_size!(in: src);
let flags = AudioFormatFlags::from_bits_truncate(src.read_u32());
let volume = src.read_u32();
let volume_left = (volume & 0xFFFF) as u16;
let volume_right = (volume >> 16) as u16;
let volume_left = src.read_u16();
let volume_right = src.read_u16();
let pitch = src.read_u32();
let dgram_port = src.read_u16_be();
let n_formats = usize::from(src.read_u16());
@ -489,6 +492,10 @@ impl TryFrom<u16> for QualityMode {
}
impl From<QualityMode> for u16 {
#[expect(
clippy::as_conversions,
reason = "guarantees discriminant layout, and as is the only way to cast enum -> primitive"
)]
fn from(mode: QualityMode) -> Self {
mode as u16
}
@ -626,7 +633,7 @@ impl<'de> Decode<'de> for TrainingPdu {
ensure_fixed_part_size!(in: src);
let timestamp = src.read_u16();
let len = src.read_u16() as usize;
let len = usize::from(src.read_u16());
let data = if len != 0 {
if len < Self::FIXED_PART_SIZE + ServerAudioOutputPdu::FIXED_PART_SIZE {
return Err(invalid_field_err!("TrainingPdu::wPackSize", "too small"));
@ -839,7 +846,7 @@ impl Encode for WavePdu<'_> {
impl WavePdu<'_> {
fn decode(src: &mut ReadCursor<'_>, body_size: u16) -> DecodeResult<Self> {
let info = WaveInfoPdu::decode(src)?;
let body_size = body_size as usize;
let body_size = usize::from(body_size);
let data_len = body_size
.checked_sub(info.size())
.ok_or_else(|| invalid_field_err!("Length", "WaveInfo body_size is too small"))?;
@ -1090,9 +1097,8 @@ impl<'de> Decode<'de> for VolumePdu {
fn decode(src: &mut ReadCursor<'de>) -> DecodeResult<Self> {
ensure_fixed_part_size!(in: src);
let volume = src.read_u32();
let volume_left = (volume & 0xFFFF) as u16;
let volume_right = (volume >> 16) as u16;
let volume_left = src.read_u16();
let volume_right = src.read_u16();
Ok(Self {
volume_left,

View file

@ -19,7 +19,7 @@ pub(crate) struct BitmapEncoder {
impl BitmapEncoder {
pub(crate) fn new() -> Self {
Self {
buffer: vec![0; u16::MAX as usize],
buffer: vec![0; usize::from(u16::MAX)],
}
}

View file

@ -31,6 +31,16 @@ enum CodecId {
None = 0x0,
}
impl CodecId {
#[expect(
clippy::as_conversions,
reason = "guarantees discriminant layout, and as is the only way to cast enum -> primitive"
)]
fn as_u8(self) -> u8 {
self as u8
}
}
#[cfg_attr(feature = "__bench", visibility::make(pub))]
#[derive(Debug)]
pub(crate) struct UpdateEncoderCodecs {
@ -389,7 +399,7 @@ impl BitmapUpdateHandler for NoneHandler {
for row in bitmap.data.chunks(bitmap.stride.get()).rev() {
data.extend_from_slice(&row[..stride]);
}
set_surface(bitmap, CodecId::None as u8, &data)
set_surface(bitmap, CodecId::None.as_u8(), &data)
}
}

View file

@ -97,9 +97,14 @@ impl From<(u16, fast_path::KeyboardFlags)> for KeyboardEvent {
}
impl From<(u16, scan_code::KeyboardFlags)> for KeyboardEvent {
#[expect(clippy::cast_possible_truncation)] // we are actually truncating the value
#[expect(
clippy::as_conversions,
clippy::cast_possible_truncation,
reason = "we are truncating the value on purpose"
)]
fn from((key, flags): (u16, scan_code::KeyboardFlags)) -> Self {
let extended = flags.contains(scan_code::KeyboardFlags::EXTENDED);
if flags.contains(scan_code::KeyboardFlags::RELEASE) {
KeyboardEvent::Released {
code: key as u8,
@ -131,7 +136,11 @@ impl From<SynchronizeFlags> for KeyboardEvent {
}
impl From<SyncToggleFlags> for KeyboardEvent {
#[expect(clippy::cast_possible_truncation)] // we are actually truncating the value
#[expect(
clippy::as_conversions,
clippy::cast_possible_truncation,
reason = "we are truncating the value on purpose"
)]
fn from(value: SyncToggleFlags) -> Self {
KeyboardEvent::Synchronize(SynchronizeFlags::from_bits_truncate(value.bits() as u8))
}

View file

@ -72,7 +72,6 @@ struct PointerRenderingState {
}
#[expect(clippy::too_many_arguments)]
#[expect(clippy::cast_lossless)] // FIXME
fn copy_cursor_data(
from: &[u8],
from_pos: (usize, usize),
@ -124,11 +123,17 @@ fn copy_cursor_data(
continue;
}
// Integer alpha blending, source represented as premultiplied alpha color, calculation in floating point
to[to_start + pixel * PIXEL_SIZE] = src_r + (((dest_r as u16) * (255 - src_a) as u16) >> 8) as u8;
to[to_start + pixel * PIXEL_SIZE + 1] = src_g + (((dest_g as u16) * (255 - src_a) as u16) >> 8) as u8;
to[to_start + pixel * PIXEL_SIZE + 2] = src_b + (((dest_b as u16) * (255 - src_a) as u16) >> 8) as u8;
// Framebuffer is always opaque, so we can skip alpha channel change
#[expect(clippy::as_conversions, reason = "(u16 >> 8) fits into u8 + hot loop")]
{
// Integer alpha blending, source represented as premultiplied alpha color, calculation in floating point
to[to_start + pixel * PIXEL_SIZE] =
src_r + ((u16::from(dest_r) * u16::from(255 - src_a)) >> 8) as u8;
to[to_start + pixel * PIXEL_SIZE + 1] =
src_g + ((u16::from(dest_g) * u16::from(255 - src_a)) >> 8) as u8;
to[to_start + pixel * PIXEL_SIZE + 2] =
src_b + ((u16::from(dest_b) * u16::from(255 - src_a)) >> 8) as u8;
// Framebuffer is always opaque, so we can skip alpha channel change
}
}
} else {
to[to_start..to_start + width * PIXEL_SIZE]
@ -227,6 +232,13 @@ impl DecodedImage {
return Ok(None);
}
let pointer_src_rect_width = usize::from(self.pointer_src_rect.width());
let pointer_src_rect_height = usize::from(self.pointer_src_rect.height());
let pointer_draw_x = usize::from(self.pointer_draw_x);
let pointer_draw_y = usize::from(self.pointer_draw_y);
let width = usize::from(self.width);
let height = usize::from(self.height);
match &layer {
PointerLayer::Background => {
if self.pointer_backbuffer.is_empty() {
@ -237,15 +249,12 @@ impl DecodedImage {
copy_cursor_data(
&self.pointer_backbuffer,
(0, 0),
self.pointer_src_rect.width() as usize * 4,
pointer_src_rect_width * 4,
&mut self.data,
self.width as usize * 4,
(self.pointer_draw_x as usize, self.pointer_draw_y as usize),
(
self.pointer_src_rect.width() as usize,
self.pointer_src_rect.height() as usize,
),
(self.width as usize, self.height as usize),
width * 4,
(pointer_draw_x, pointer_draw_y),
(pointer_src_rect_width, pointer_src_rect_height),
(width, height),
false,
);
}
@ -254,37 +263,34 @@ impl DecodedImage {
let buffer_size = self
.pointer_backbuffer
.len()
.max(self.pointer_src_rect.width() as usize * self.pointer_src_rect.height() as usize * 4);
.max(pointer_src_rect_width * pointer_src_rect_height * 4);
self.pointer_backbuffer.resize(buffer_size, 0);
copy_cursor_data(
&self.data,
(self.pointer_draw_x as usize, self.pointer_draw_y as usize),
self.width as usize * 4,
(pointer_draw_x, pointer_draw_y),
width * 4,
&mut self.pointer_backbuffer,
self.pointer_src_rect.width() as usize * 4,
pointer_src_rect_width * 4,
(0, 0),
(
self.pointer_src_rect.width() as usize,
self.pointer_src_rect.height() as usize,
),
(self.width as usize, self.height as usize),
(pointer_src_rect_width, pointer_src_rect_height),
(width, height),
false,
);
// Draw pointer (with compositing)
copy_cursor_data(
pointer.bitmap_data.as_slice(),
(self.pointer_src_rect.left as usize, self.pointer_src_rect.top as usize),
(
usize::from(self.pointer_src_rect.left),
usize::from(self.pointer_src_rect.top),
),
usize::from(pointer.width) * 4,
&mut self.data,
self.width as usize * 4,
(self.pointer_draw_x as usize, self.pointer_draw_y as usize),
(
self.pointer_src_rect.width() as usize,
self.pointer_src_rect.height() as usize,
),
(self.width as usize, self.height as usize),
width * 4,
(pointer_draw_x, pointer_draw_y),
(pointer_src_rect_width, pointer_src_rect_height),
(width, height),
true,
);
}
@ -312,7 +318,6 @@ impl DecodedImage {
}
}
#[expect(clippy::cast_possible_wrap)] // FIXME
fn recalculate_pointer_geometry(&mut self) {
let x = self.pointer_x;
let y = self.pointer_y;
@ -322,10 +327,10 @@ impl DecodedImage {
_ => return,
};
let left_virtual = x as i16 - pointer.hotspot_x as i16;
let top_virtual = y as i16 - pointer.hotspot_y as i16;
let right_virtual = left_virtual + pointer.width as i16 - 1;
let bottom_virtual = top_virtual + pointer.height as i16 - 1;
let left_virtual = i32::from(x) - i32::from(pointer.hotspot_x);
let top_virtual = i32::from(y) - i32::from(pointer.hotspot_y);
let right_virtual = left_virtual + i32::from(pointer.width) - 1;
let bottom_virtual = top_virtual + i32::from(pointer.height) - 1;
let (left, draw_x) = if left_virtual < 0 {
// Cut left side if required
@ -342,7 +347,7 @@ impl DecodedImage {
};
// Cut right side if required
let right = if right_virtual >= (self.width - 1) as i16 {
let right = if right_virtual >= i32::from(self.width - 1) {
if draw_x + 1 >= self.width {
// Pointer is completely out of bounds horizontally
self.pointer_visible_on_screen = false;
@ -355,7 +360,7 @@ impl DecodedImage {
};
// Cut bottom side if required
let bottom = if bottom_virtual >= (self.height - 1) as i16 {
let bottom = if bottom_virtual >= i32::from(self.height - 1) {
if (draw_y + 1) >= self.height {
// Pointer is completely out of bounds vertically
self.pointer_visible_on_screen = false;
@ -539,7 +544,7 @@ impl DecodedImage {
const SRC_COLOR_DEPTH: usize = 2;
const DST_COLOR_DEPTH: usize = 4;
let image_width = self.width as usize;
let image_width = usize::from(self.width);
let rectangle_width = usize::from(update_rectangle.width());
let top = usize::from(update_rectangle.top);
let left = usize::from(update_rectangle.left);
@ -586,7 +591,7 @@ impl DecodedImage {
const SRC_COLOR_DEPTH: usize = 3;
const DST_COLOR_DEPTH: usize = 4;
let image_width = self.width as usize;
let image_width = usize::from(self.width);
let top = usize::from(update_rectangle.top);
let left = usize::from(update_rectangle.left);
@ -636,7 +641,7 @@ impl DecodedImage {
const SRC_COLOR_DEPTH: usize = 4;
const DST_COLOR_DEPTH: usize = 4;
let image_width = self.width as usize;
let image_width = usize::from(self.width);
let rectangle_width = usize::from(update_rectangle.width());
let top = usize::from(update_rectangle.top);
let left = usize::from(update_rectangle.left);

View file

@ -187,10 +187,11 @@ struct DecodingTileContext {
impl DecodingTileContext {
fn new() -> Self {
let tile_size = usize::from(TILE_SIZE);
Self {
tile_output: vec![0; TILE_SIZE as usize * TILE_SIZE as usize * 4],
ycbcr_buffer: vec![vec![0; TILE_SIZE as usize * TILE_SIZE as usize]; 3],
ycbcr_temp_buffer: vec![0; TILE_SIZE as usize * TILE_SIZE as usize],
tile_output: vec![0; tile_size * tile_size * 4],
ycbcr_buffer: vec![vec![0; tile_size * tile_size]; 3],
ycbcr_temp_buffer: vec![0; tile_size * tile_size],
}
}
}

View file

@ -42,6 +42,7 @@ const fn make_gcc_block_buffer<const N: usize>(data_type: u16, buffer: &[u8]) ->
let array = copy_slice(&data_type.to_le_bytes(), [0; N], 0);
#[expect(clippy::as_conversions, reason = "must be const casts")]
let length = (buffer.len() + USER_HEADER_LEN) as u16;
let array = copy_slice(&length.to_le_bytes(), array, 2);
@ -161,6 +162,7 @@ pub static SERVER_GCC_WITH_OPTIONAL_FIELDS: LazyLock<ServerGccBlocks> = LazyLock
data
});
#[expect(clippy::as_conversions, reason = "must be const casts")]
pub const CLIENT_GCC_CORE_BLOCK_BUFFER: [u8; gcc_block_size(
CLIENT_OPTIONAL_CORE_DATA_TO_SERVER_SELECTED_PROTOCOL_BUFFER,
)] = make_gcc_block_buffer(
@ -168,18 +170,22 @@ pub const CLIENT_GCC_CORE_BLOCK_BUFFER: [u8; gcc_block_size(
&CLIENT_OPTIONAL_CORE_DATA_TO_SERVER_SELECTED_PROTOCOL_BUFFER,
);
#[expect(clippy::as_conversions, reason = "must be const casts")]
pub const CLIENT_GCC_SECURITY_BLOCK_BUFFER: [u8; gcc_block_size(CLIENT_SECURITY_DATA_BUFFER)] =
make_gcc_block_buffer(ClientGccType::SecurityData as u16, &CLIENT_SECURITY_DATA_BUFFER);
#[expect(clippy::as_conversions, reason = "must be const casts")]
pub const CLIENT_GCC_NETWORK_BLOCK_BUFFER: [u8; gcc_block_size(CLIENT_NETWORK_DATA_WITH_CHANNELS_BUFFER)] =
make_gcc_block_buffer(
ClientGccType::NetworkData as u16,
&CLIENT_NETWORK_DATA_WITH_CHANNELS_BUFFER,
);
#[expect(clippy::as_conversions, reason = "must be const casts")]
pub const CLIENT_GCC_CLUSTER_BLOCK_BUFFER: [u8; gcc_block_size(CLUSTER_DATA_BUFFER)] =
make_gcc_block_buffer(ClientGccType::ClusterData as u16, &CLUSTER_DATA_BUFFER);
#[expect(clippy::as_conversions, reason = "must be const casts")]
pub const CLIENT_GCC_MONITOR_BLOCK_BUFFER: [u8; gcc_block_size(
crate::monitor_data::MONITOR_DATA_WITH_MONITORS_BUFFER,
)] = make_gcc_block_buffer(
@ -187,6 +193,7 @@ pub const CLIENT_GCC_MONITOR_BLOCK_BUFFER: [u8; gcc_block_size(
&crate::monitor_data::MONITOR_DATA_WITH_MONITORS_BUFFER,
);
#[expect(clippy::as_conversions, reason = "must be const casts")]
pub const CLIENT_GCC_MONITOR_EXTENDED_BLOCK_BUFFER: [u8; gcc_block_size(
crate::monitor_extended_data::MONITOR_DATA_WITH_MONITORS_BUFFER,
)] = make_gcc_block_buffer(
@ -194,24 +201,28 @@ pub const CLIENT_GCC_MONITOR_EXTENDED_BLOCK_BUFFER: [u8; gcc_block_size(
&crate::monitor_extended_data::MONITOR_DATA_WITH_MONITORS_BUFFER,
);
#[expect(clippy::as_conversions, reason = "must be const casts")]
pub const SERVER_GCC_CORE_BLOCK_BUFFER: [u8; gcc_block_size(SERVER_CORE_DATA_TO_REQUESTED_PROTOCOL_BUFFER)] =
make_gcc_block_buffer(
ServerGccType::CoreData as u16,
&SERVER_CORE_DATA_TO_REQUESTED_PROTOCOL_BUFFER,
);
#[expect(clippy::as_conversions, reason = "must be const casts")]
pub const SERVER_GCC_NETWORK_BLOCK_BUFFER: [u8; gcc_block_size(SERVER_NETWORK_DATA_WITH_CHANNELS_ID_BUFFER)] =
make_gcc_block_buffer(
ServerGccType::NetworkData as u16,
&SERVER_NETWORK_DATA_WITH_CHANNELS_ID_BUFFER,
);
#[expect(clippy::as_conversions, reason = "must be const casts")]
pub const SERVER_GCC_SECURITY_BLOCK_BUFFER: [u8; gcc_block_size(SERVER_SECURITY_DATA_WITH_OPTIONAL_FIELDS_BUFFER)] =
make_gcc_block_buffer(
ServerGccType::SecurityData as u16,
&SERVER_SECURITY_DATA_WITH_OPTIONAL_FIELDS_BUFFER,
);
#[expect(clippy::as_conversions, reason = "must be const casts")]
pub const SERVER_GCC_MESSAGE_CHANNEL_BLOCK_BUFFER: [u8; gcc_block_size(
crate::message_channel_data::SERVER_GCC_MESSAGE_CHANNEL_BLOCK_BUFFER,
)] = make_gcc_block_buffer(
@ -219,6 +230,7 @@ pub const SERVER_GCC_MESSAGE_CHANNEL_BLOCK_BUFFER: [u8; gcc_block_size(
&crate::message_channel_data::SERVER_GCC_MESSAGE_CHANNEL_BLOCK_BUFFER,
);
#[expect(clippy::as_conversions, reason = "must be const casts")]
pub const SERVER_GCC_MULTI_TRANSPORT_CHANNEL_BLOCK_BUFFER: [u8; gcc_block_size(
crate::multi_transport_channel_data::SERVER_GCC_MULTI_TRANSPORT_CHANNEL_BLOCK_BUFFER,
)] = make_gcc_block_buffer(

View file

@ -180,7 +180,7 @@ pub static SERVER_LICENSE_PDU: LazyLock<LicensePdu> = LazyLock::new(|| {
state_transition: LicensingStateTransition::NoTransition,
error_info: Vec::new(),
};
pdu.license_header.preamble_message_size = pdu.size() as u16;
pdu.license_header.preamble_message_size = u16::try_from(pdu.size()).unwrap();
pdu.into()
});
pub static SERVER_DEMAND_ACTIVE_PDU: LazyLock<ShareControlHeader> = LazyLock::new(|| ShareControlHeader {

View file

@ -65,6 +65,7 @@ pub static SERVER_SECURITY_DATA_WITH_MISMATCH_OF_REQUIRED_AND_OPTIONAL_FIELDS: L
server_cert: SERVER_CERT_BUFFER.to_vec(),
});
#[expect(clippy::as_conversions, reason = "must be const casts")]
pub const SERVER_SECURITY_DATA_WITH_OPTIONAL_FIELDS_BUFFER: [u8; 232] = concat_arrays!(
SERVER_SECURITY_DATA_WITH_OPTIONAL_FIELDS_PREFIX_BUFFER,
(SERVER_RANDOM_BUFFER.len() as u32).to_le_bytes(),
@ -73,6 +74,7 @@ pub const SERVER_SECURITY_DATA_WITH_OPTIONAL_FIELDS_BUFFER: [u8; 232] = concat_a
SERVER_CERT_BUFFER
);
#[expect(clippy::as_conversions, reason = "must be const casts")]
pub const SERVER_SECURITY_DATA_WITH_INVALID_SERVER_RANDOM_BUFFER: [u8; 233] = concat_arrays!(
SERVER_SECURITY_DATA_WITH_OPTIONAL_FIELDS_PREFIX_BUFFER,
(SERVER_RANDOM_BUFFER.len() as u32 + 1).to_le_bytes(),

View file

@ -9,6 +9,7 @@ const DATA: [u8; 12] = [0x71, 0x71, 0x71, 0x71, 0x71, 0x71, 0x71, 0x71, 0x71, 0x
const EDGE_CASE_LENGTH: u32 = 0x639;
const EDGE_CASE_CHANNEL_ID: u32 = 0x07;
const EDGE_CASE_PREFIX: [u8; 4] = [0x24, 0x7, 0x39, 0x6];
#[expect(clippy::as_conversions)]
const EDGE_CASE_DATA: [u8; EDGE_CASE_LENGTH as usize] = [
0xe0, 0x24, 0xa9, 0xba, 0xe0, 0x68, 0xa9, 0xba, 0x8a, 0x73, 0x41, 0x25, 0x12, 0x12, 0x1c, 0x28, 0x3b, 0xa6, 0x34,
0x8, 0x8, 0x7a, 0x38, 0x34, 0x2c, 0xe8, 0xf8, 0xd0, 0xef, 0x18, 0xc2, 0xc, 0x27, 0x1f, 0xb1, 0x83, 0x3c, 0x58,

View file

@ -2,7 +2,7 @@ use core::future::Future;
use core::net::{IpAddr, Ipv4Addr};
use core::pin::Pin;
use ironrdp_connector::{custom_err, ConnectorResult};
use ironrdp_connector::{custom_err, general_err, ConnectorResult};
use reqwest::Client;
use sspi::{Error, ErrorKind};
use tokio::io::{AsyncReadExt as _, AsyncWriteExt as _};
@ -62,7 +62,10 @@ impl ReqwestNetworkClient {
.map_err(|e| Error::new(ErrorKind::NoAuthenticatingAuthority, format!("{e:?}")))
.map_err(|e| custom_err!("failed to send KDC request over TCP", e))?;
let mut buf = vec![0; len as usize + 4];
let len = usize::try_from(len)
.map_err(|_| general_err!("invalid buffer length: out of range integral type conversion"))?;
let mut buf = vec![0; len + 4];
buf[0..4].copy_from_slice(&(len.to_be_bytes()));
stream

View file

@ -1,5 +1,6 @@
use core::num::NonZeroU32;
use anyhow::Context as _;
use ironrdp::pdu::geometry::{InclusiveRectangle, Rectangle as _};
use softbuffer::{NoDisplayHandle, NoWindowHandle};
use web_sys::HtmlCanvasElement;
@ -61,7 +62,7 @@ impl Canvas {
let region_width_usize = usize::from(region_width);
for dst_row in dst
.chunks_exact_mut(self.width.get() as usize)
.chunks_exact_mut(usize::try_from(self.width.get()).context("canvas width")?)
.skip(region_top_usize)
.take(region_height_usize)
{

View file

@ -69,7 +69,7 @@ struct SessionBuilderInner {
use_display_control: bool,
enable_credssp: bool,
outbound_message_size_limit: Option<u32>,
outbound_message_size_limit: Option<usize>,
}
impl Default for SessionBuilderInner {
@ -216,8 +216,8 @@ impl iron_remote_desktop::SessionBuilder for SessionBuilder {
|enable_credssp: bool| { self.0.borrow_mut().enable_credssp = enable_credssp };
|outbound_message_size_limit: f64| {
let limit = if outbound_message_size_limit >= 0.0 && outbound_message_size_limit <= f64::from(u32::MAX) {
#[expect(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
{ outbound_message_size_limit as u32 }
#[expect(clippy::as_conversions, clippy::cast_possible_truncation, clippy::cast_sign_loss)]
{ outbound_message_size_limit as usize }
} else {
warn!(outbound_message_size_limit, "Invalid outbound message size limit; fallback to unlimited");
0 // Fallback to no limit for invalid values.
@ -899,20 +899,20 @@ fn build_config(
async fn writer_task(
rx: mpsc::UnboundedReceiver<Vec<u8>>,
rdp_writer: WriteHalf<WebSocket>,
outbound_limit: Option<u32>,
outbound_limit: Option<usize>,
) {
debug!("writer task started");
async fn inner(
mut rx: mpsc::UnboundedReceiver<Vec<u8>>,
mut rdp_writer: WriteHalf<WebSocket>,
outbound_limit: Option<u32>,
outbound_limit: Option<usize>,
) -> anyhow::Result<()> {
while let Some(frame) = rx.next().await {
match outbound_limit {
Some(max_size) if frame.len() > max_size as usize => {
Some(max_size) if frame.len() > max_size => {
// Send in chunks.
for chunk in frame.chunks(max_size as usize) {
for chunk in frame.chunks(max_size) {
rdp_writer.write_all(chunk).await.context("couldn't write chunk")?;
rdp_writer.flush().await.context("couldn't flush chunk")?;
}
@ -1153,8 +1153,7 @@ where
}
}
#[expect(clippy::cast_sign_loss)]
#[expect(clippy::cast_possible_truncation)]
#[expect(clippy::as_conversions, clippy::cast_sign_loss, clippy::cast_possible_truncation)]
fn f64_to_u16_saturating_cast(value: f64) -> u16 {
value as u16
}

View file

@ -364,19 +364,23 @@ fn generate_sine_wave(sample_rate: u32, frequency: f32, duration_ms: u64, phase:
use core::f32::consts::PI;
let total_samples = (u64::from(sample_rate) * duration_ms) / 1000;
#[expect(clippy::as_conversions)]
let delta_phase = 2.0 * PI * frequency / sample_rate as f32;
let amplitude = 32767.0; // Max amplitude for 16-bit audio
let capacity = (total_samples as usize) * 2; // 2 channels
let capacity = usize::try_from(total_samples).expect("u64-to-usize") * 2; // 2 channels
let mut samples = Vec::with_capacity(capacity);
for _ in 0..total_samples {
let sample = (*phase).sin();
*phase += delta_phase;
// Wrap phase to maintain precision and avoid overflow
// Wrap phase to maintain precision and avoid overflow.
*phase %= 2.0 * PI;
#[expect(clippy::cast_possible_truncation)]
#[expect(clippy::as_conversions, clippy::cast_possible_truncation)]
let sample_i16 = (sample * amplitude) as i16;
// Write same sample to both channels (stereo)

View file

@ -328,8 +328,11 @@ fn get_json_float(value: &tinyjson::JsonValue, key: &str) -> anyhow::Result<f64>
}
fn get_json_int(value: &tinyjson::JsonValue, key: &str) -> anyhow::Result<u64> {
// tinyjson does not expose any integers at all, so we need the f64 to u64 as casting
#[expect(clippy::cast_sign_loss)]
#[expect(clippy::cast_possible_truncation)]
#[expect(
clippy::as_conversions,
clippy::cast_sign_loss,
clippy::cast_possible_truncation,
reason = "tinyjson does not expose any integers at all, so we need the f64 to u64 as casting"
)]
get_json_float(value, key).map(|value| value as u64)
}