Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add precise registry types and allocation-free queries and updates #3184

Merged
merged 4 commits into from
Aug 1, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
340 changes: 114 additions & 226 deletions crates/libs/registry/src/key.rs

Large diffs are not rendered by default.

1 change: 0 additions & 1 deletion crates/libs/registry/src/key_iterator.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
use super::*;
use core::ptr::{null, null_mut};

/// An iterator of registry key names.
pub struct KeyIterator<'a> {
Expand Down
33 changes: 17 additions & 16 deletions crates/libs/registry/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ Learn more about Rust for Windows here: <https://github.com/microsoft/windows-rs
extern crate alloc;

use alloc::{string::String, vec::Vec};
use core::ops::Deref;
use core::ptr::{null, null_mut};

mod bindings;
use bindings::*;
Expand All @@ -19,6 +21,12 @@ pub use key::Key;
mod value;
pub use value::Value;

mod value_bytes;
use value_bytes::ValueBytes;

mod pcwstr;
use pcwstr::*;

mod key_iterator;
pub use key_iterator::KeyIterator;

Expand All @@ -32,7 +40,7 @@ pub use windows_result::Result;
use windows_result::*;

pub use windows_strings::HSTRING;
use windows_strings::*;
use windows_strings::{PCWSTR, *};

/// The predefined `HKEY_CLASSES_ROOT` registry key.
pub const CLASSES_ROOT: &Key = &Key(HKEY_CLASSES_ROOT);
Expand All @@ -49,21 +57,6 @@ pub const LOCAL_MACHINE: &Key = &Key(HKEY_LOCAL_MACHINE);
/// The predefined `HKEY_USERS` registry key.
pub const USERS: &Key = &Key(HKEY_USERS);

fn pcwstr<T: AsRef<str>>(value: T) -> Vec<u16> {
value
.as_ref()
.encode_utf16()
.chain(core::iter::once(0))
.collect()
}

fn trim(mut value: &[u16]) -> &[u16] {
while value.last() == Some(&0) {
value = &value[..value.len() - 1];
}
value
}

fn win32_error(result: u32) -> Result<()> {
if result == 0 {
Ok(())
Expand All @@ -75,3 +68,11 @@ fn win32_error(result: u32) -> Result<()> {
fn invalid_data() -> Error {
Error::from_hresult(HRESULT::from_win32(ERROR_INVALID_DATA))
}

fn from_le_bytes(ty: Type, from: &[u8]) -> Result<u64> {
match ty {
Type::U32 if from.len() == 4 => Ok(u32::from_le_bytes(from.try_into().unwrap()).into()),
Type::U64 if from.len() == 8 => Ok(u64::from_le_bytes(from.try_into().unwrap())),
_ => Err(invalid_data()),
}
}
41 changes: 41 additions & 0 deletions crates/libs/registry/src/pcwstr.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
use super::*;

pub struct OwnedPcwstr(Vec<u16>);

pub fn pcwstr<T: AsRef<str>>(value: T) -> OwnedPcwstr {
OwnedPcwstr(
value
.as_ref()
.encode_utf16()
.chain(core::iter::once(0))
.collect(),
)
}

pub fn multi_pcwstr<T: AsRef<str>>(value: &[T]) -> OwnedPcwstr {
OwnedPcwstr(
value
.iter()
.flat_map(|value| value.as_ref().encode_utf16().chain(core::iter::once(0)))
.chain(core::iter::once(0))
.collect(),
)
}

impl OwnedPcwstr {
pub fn as_ptr(&self) -> *const u16 {
debug_assert!(
self.0.last() == Some(&0),
"`OwnedPcwstr` isn't null-terminated"
);
self.0.as_ptr()
}

pub fn as_bytes(&self) -> &[u8] {
unsafe { core::slice::from_raw_parts(self.as_ptr() as *const _, self.0.len() * 2) }
}

pub fn as_raw(&self) -> PCWSTR {
PCWSTR(self.as_ptr())
}
}
43 changes: 38 additions & 5 deletions crates/libs/registry/src/type.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
use super::*;

/// The possible types that a registry value could have.
#[derive(Clone, PartialEq, Eq, Debug)]
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub enum Type {
/// A 32-bit unsigned integer value.
U32,
Expand All @@ -10,12 +12,43 @@ pub enum Type {
/// A string value.
String,

/// An array u8 bytes.
Bytes,
/// A string value that may contain unexpanded environment variables.
ExpandString,

/// An array of string values.
MultiString,

/// An unknown or unsupported type.
Unknown(u32),
/// An array u8 bytes.
Bytes,

/// An unknown type.
Other(u32),
}

impl From<u32> for Type {
fn from(ty: u32) -> Self {
match ty {
REG_DWORD => Self::U32,
REG_QWORD => Self::U64,
REG_SZ => Self::String,
REG_EXPAND_SZ => Self::ExpandString,
REG_MULTI_SZ => Self::MultiString,
REG_BINARY => Self::Bytes,
rest => Self::Other(rest),
}
}
}

impl From<Type> for u32 {
fn from(ty: Type) -> Self {
match ty {
Type::U32 => REG_DWORD,
Type::U64 => REG_QWORD,
Type::String => REG_SZ,
Type::ExpandString => REG_EXPAND_SZ,
Type::MultiString => REG_MULTI_SZ,
Type::Bytes => REG_BINARY,
Type::Other(other) => other,
}
}
}
106 changes: 93 additions & 13 deletions crates/libs/registry/src/value.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,102 @@ use super::*;

/// A registry value.
#[derive(Clone, PartialEq, Eq, Debug)]
pub enum Value {
/// A 32-bit unsigned integer value.
U32(u32),
pub struct Value {
pub(crate) value: ValueBytes,
kennykerr marked this conversation as resolved.
Show resolved Hide resolved
pub(crate) ty: Type,
}

/// A 64-bit unsigned integer value.
U64(u64),
impl Value {
/// Gets the type of the registry value.
pub fn ty(&self) -> Type {
self.ty
}

/// A string value.
String(String),
/// Sets the type of the registry value. This does not change the value.
pub fn set_ty(&mut self, ty: Type) {
self.ty = ty;
}
}

/// An array u8 bytes.
Bytes(Vec<u8>),
impl core::ops::Deref for Value {
type Target = [u8];

/// An array of string values.
MultiString(Vec<String>),
fn deref(&self) -> &[u8] {
&self.value
}
}

impl AsRef<[u8]> for Value {
fn as_ref(&self) -> &[u8] {
&self.value
}
}

impl TryFrom<u32> for Value {
type Error = Error;
fn try_from(from: u32) -> Result<Self> {
Ok(Self {
value: from.to_le_bytes().try_into()?,
ty: Type::U32,
})
}
}

impl TryFrom<Value> for u32 {
type Error = Error;
fn try_from(from: Value) -> Result<Self> {
Ok(from_le_bytes(from.ty, &from)?.try_into()?)
}
}

impl TryFrom<u64> for Value {
type Error = Error;
fn try_from(from: u64) -> Result<Self> {
Ok(Self {
value: from.to_le_bytes().try_into()?,
ty: Type::U64,
})
}
}

impl TryFrom<Value> for u64 {
type Error = Error;
fn try_from(from: Value) -> Result<Self> {
from_le_bytes(from.ty, &from)
}
}

impl TryFrom<Value> for String {
type Error = Error;
fn try_from(from: Value) -> Result<Self> {
match from.ty {
Type::String | Type::ExpandString => Ok(Self::from_utf16(from.value.as_wide())?),
_ => Err(invalid_data()),
}
}
}

impl TryFrom<&str> for Value {
type Error = Error;
fn try_from(from: &str) -> Result<Self> {
Ok(Self {
value: ValueBytes::from_slice(pcwstr(from).as_bytes())?,
ty: Type::String,
})
}
}

/// An unknown or unsupported type.
Unknown(u32),
impl TryFrom<Value> for Vec<String> {
type Error = Error;
fn try_from(from: Value) -> Result<Self> {
match from.ty {
Type::MultiString => Ok(from
.value
.as_wide()
.split(|c| *c == 0)
.map(String::from_utf16_lossy)
.collect()),
_ => Ok(vec![String::try_from(from)?]),
}
}
}
Loading