Implement UUID CRC32 and byte order compatibility

This commit is contained in:
2026-08-08 22:07:19 +00:00
parent e63f2c615a
commit 28a6f54f7e
15 changed files with 1309 additions and 202 deletions

251
Cargo.lock generated
View File

@@ -2,6 +2,109 @@
# It is not intended for manual editing. # It is not intended for manual editing.
version = 4 version = 4
[[package]]
name = "block-buffer"
version = "0.10.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
dependencies = [
"generic-array",
]
[[package]]
name = "bumpalo"
version = "3.20.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649"
[[package]]
name = "cfg-if"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
[[package]]
name = "crypto-common"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a"
dependencies = [
"generic-array",
"typenum",
]
[[package]]
name = "digest"
version = "0.10.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
dependencies = [
"block-buffer",
"crypto-common",
]
[[package]]
name = "futures-core"
version = "0.3.33"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2cd50c473c80f6d7c3670a752354b8e569b1a7cbfdc0419ec88e5edad85e0dc7"
[[package]]
name = "futures-task"
version = "0.3.33"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b231ed28831efb4a61a08580c4bc233ec56bc009f4cd8f52da2c3cb97df0c109"
[[package]]
name = "futures-util"
version = "0.3.33"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a77a90a256fce34da66415271e30f94ee91c57b04b8a2c042d9cf3220179deaa"
dependencies = [
"futures-core",
"futures-task",
"pin-project-lite",
"slab",
]
[[package]]
name = "generic-array"
version = "0.14.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
dependencies = [
"typenum",
"version_check",
]
[[package]]
name = "getrandom"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "300e883d756b2e4ec94e02791f39b04b522276138852cfc41d9fb7e904106099"
dependencies = [
"cfg-if",
"libc",
"r-efi",
]
[[package]]
name = "js-sys"
version = "0.3.104"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0e0c1080212aad755ea003d18543e8768dd432c48819efd73a7bf1e39b7a5a3a"
dependencies = [
"cfg-if",
"futures-util",
"wasm-bindgen",
]
[[package]]
name = "libc"
version = "0.2.189"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3eaf3ede3fee6db1a4c2ee091bf8a8b4dccdc6d17f656fb07896ee72867612f2"
[[package]] [[package]]
name = "libremetaverse" name = "libremetaverse"
version = "0.0.1" version = "0.0.1"
@@ -107,6 +210,11 @@ dependencies = [
[[package]] [[package]]
name = "libremetaverse-types" name = "libremetaverse-types"
version = "0.0.1" version = "0.0.1"
dependencies = [
"getrandom",
"md-5",
"uuid",
]
[[package]] [[package]]
name = "libremetaverse-utilities" name = "libremetaverse-utilities"
@@ -134,3 +242,146 @@ dependencies = [
"libremetaverse-structured-data", "libremetaverse-structured-data",
"libremetaverse-types", "libremetaverse-types",
] ]
[[package]]
name = "md-5"
version = "0.10.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d89e7ee0cfbedfc4da3340218492196241d89eefb6dab27de5df917a6d2e78cf"
dependencies = [
"cfg-if",
"digest",
]
[[package]]
name = "once_cell"
version = "1.21.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50"
[[package]]
name = "pin-project-lite"
version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd"
[[package]]
name = "proc-macro2"
version = "1.0.107"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "985e7ec9bb745e6ce6535b544d84d6cd6f7ad8bd711c398938ae983b91a766d9"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.47"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fbf4db142a473a8d80c26bbf18454ed458bf8d26c8219c331daecfdbd079001"
dependencies = [
"proc-macro2",
]
[[package]]
name = "r-efi"
version = "6.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf"
[[package]]
name = "rustversion"
version = "1.0.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf54715a573b99ac80df0bc206da022bcd442c974952c7b9720069370852e21f"
[[package]]
name = "slab"
version = "0.4.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5"
[[package]]
name = "syn"
version = "2.0.119"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "872831b642d1a07999a962a351ed35b955ea2cfc8f3862091e2a240a84f17297"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "typenum"
version = "1.20.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6f5e870be6c3b371b77fe0ee0bafb859fa4964b4404c27de1d380043c4dda20"
[[package]]
name = "unicode-ident"
version = "1.0.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
[[package]]
name = "uuid"
version = "1.24.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bf3923a6f5c4c6382e0b653c4117f48d631ea17f38ed86e2a828e6f7412f5239"
dependencies = [
"getrandom",
"js-sys",
"wasm-bindgen",
]
[[package]]
name = "version_check"
version = "0.9.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
[[package]]
name = "wasm-bindgen"
version = "0.2.127"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b70935747edd64d89de3efa29d73789b806c15798f8e7dca4d8ac356b50ce70"
dependencies = [
"cfg-if",
"once_cell",
"rustversion",
"wasm-bindgen-macro",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-macro"
version = "0.2.127"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77775f8f3f7217702089053b94958f8f54061a3f663417df76e19cbdcca29bc1"
dependencies = [
"quote",
"wasm-bindgen-macro-support",
]
[[package]]
name = "wasm-bindgen-macro-support"
version = "0.2.127"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e11d33f857dc2fb11b8bc75aee111aa9cbeb12cd9f25efd3d4c2a3dd4e235284"
dependencies = [
"bumpalo",
"proc-macro2",
"quote",
"syn",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-shared"
version = "0.2.127"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7ef64dbcc55df09c7e5a46182d181c2cfa3e925f3da937ea764728b4bbb9dcbf"
dependencies = [
"unicode-ident",
]

View File

@@ -48,5 +48,13 @@ verifies the catalog against the pinned adjacent LibreMetaverse checkout without
overwriting those files. overwriting those files.
Running `cargo test --workspace` is intentionally red during the shim stage. Running `cargo test --workspace` is intentionally red during the shim stage.
## Native implementation progress
Milestone 04 implementation has begun. `libremetaverse-types` now provides
native UUID, incremental CRC-32, and little-endian floating-point byte helpers.
UUID protocol bytes use explicit network order while `compat::Guid` conversions
preserve the mixed-endian .NET byte-array layout. The remaining Types surfaces
stay visibly failure-only until their owning milestone issues are completed.
The controlled audit aggregates every expected failure by standardized C# The controlled audit aggregates every expected failure by standardized C#
member ID and rejects unrelated fixture, assertion, compile, or symbol errors. member ID and rejects unrelated fixture, assertion, compile, or symbol errors.

View File

@@ -45,9 +45,10 @@ python3 tools/check_api_coverage.py
The checker rejects duplicate C# IDs, duplicate Rust destinations, invalid The checker rejects duplicate C# IDs, duplicate Rust destinations, invalid
statuses, stale or missing catalog entries, unresolved signature types, statuses, stale or missing catalog entries, unresolved signature types,
assemblies without representative mappings, and platform-specific API targets. assemblies without representative mappings, and platform-specific API targets.
It also rejects generated `Default` shims, plausible false/zero/empty/nil It also rejects generated `Default` shims and plausible false/zero/empty/nil
fallbacks, and generated function bodies that do not use the standardized fallbacks. Failure-only bodies must use the standardized
`Error::NotImplemented` result or `unimplemented_api!` panic. `Error::NotImplemented` result or `unimplemented_api!` panic; reviewed native
member hooks must call an explicitly registered hand-written implementation.
The two support traits in `RUST-TYPES.tsv` are included because compiled public The two support traits in `RUST-TYPES.tsv` are included because compiled public
inheritance records reference them even though they are absent from the public inheritance records reference them even though they are absent from the public
type table. type table.
@@ -59,3 +60,10 @@ compares catalog IDs, mapping rows, exported shim IDs, and fixture probes;
rejects stale, missing, duplicate, or erased entries; and verifies rejects stale, missing, duplicate, or erased entries; and verifies
`API-COVERAGE.md`. Regenerate that report with `API-COVERAGE.md`. Regenerate that report with
`python3 tools/check_api_coverage.py --write`. `python3 tools/check_api_coverage.py --write`.
Native implementations replace whole generated types where practical. The
generated module retains the catalog markers and re-exports the hand-written
type; member-level implementations use an explicit generator registry. This
keeps regeneration deterministic without moving behavioral code into Python.
`SHIM-COVERAGE.md` reports native type/member totals separately from remaining
failure-only surfaces.

View File

@@ -10816,8 +10816,8 @@ M:LibreMetaverse.BitPack.UnpackUInt method System.UInt32 LibreMetaverse.BitPack.
M:LibreMetaverse.BitPack.UnpackUShort method System.UInt16 LibreMetaverse.BitPack.UnpackUShort() libremetaverse libremetaverse::BitPack::unpack_u_short pub fn unpack_u_short(&mut self) -> Result<u16, crate::Error> mutable_self sync Result<_, crate::Error> direct_snake_case method mapped M:LibreMetaverse.BitPack.UnpackUShort method System.UInt16 LibreMetaverse.BitPack.UnpackUShort() libremetaverse libremetaverse::BitPack::unpack_u_short pub fn unpack_u_short(&mut self) -> Result<u16, crate::Error> mutable_self sync Result<_, crate::Error> direct_snake_case method mapped
M:LibreMetaverse.BitPack.UnpackUUID method LibreMetaverse.UUID LibreMetaverse.BitPack.UnpackUUID() libremetaverse libremetaverse::BitPack::unpack_uuid pub fn unpack_uuid(&mut self) -> Result<libremetaverse_types::UUID, crate::Error> mutable_self sync Result<_, crate::Error> direct_snake_case method mapped M:LibreMetaverse.BitPack.UnpackUUID method LibreMetaverse.UUID LibreMetaverse.BitPack.UnpackUUID() libremetaverse libremetaverse::BitPack::unpack_uuid pub fn unpack_uuid(&mut self) -> Result<libremetaverse_types::UUID, crate::Error> mutable_self sync Result<_, crate::Error> direct_snake_case method mapped
M:LibreMetaverse.CRC32.#ctor constructor LibreMetaverse.CRC32() libremetaverse-types libremetaverse_types::CRC32::new pub fn new() -> Result<Self, crate::Error> none sync Result<_, crate::Error> direct_snake_case method mapped M:LibreMetaverse.CRC32.#ctor constructor LibreMetaverse.CRC32() libremetaverse-types libremetaverse_types::CRC32::new pub fn new() -> Result<Self, crate::Error> none sync Result<_, crate::Error> direct_snake_case method mapped
M:LibreMetaverse.CRC32.Update(System.Byte) method System.Void LibreMetaverse.CRC32.Update(System.Byte value) libremetaverse-types libremetaverse_types::CRC32::update_with_byte pub fn update_with_byte(&self, value: u8) -> Result<(), crate::Error> shared_self,owned sync Result<_, crate::Error> descriptive_overload_name method mapped M:LibreMetaverse.CRC32.Update(System.Byte) method System.Void LibreMetaverse.CRC32.Update(System.Byte value) libremetaverse-types libremetaverse_types::CRC32::update_with_byte pub fn update_with_byte(&mut self, value: u8) -> Result<(), crate::Error> mutable_self,owned sync Result<_, crate::Error> descriptive_overload_name method mapped
M:LibreMetaverse.CRC32.Update(System.Byte[],System.Int32,System.Int32) method System.Void LibreMetaverse.CRC32.Update(System.Byte[] value, System.Int32 pos, System.Int32 length) libremetaverse-types libremetaverse_types::CRC32::update_with_bytes_int32_int32 pub fn update_with_bytes_int32_int32(&self, value: Vec<u8>, pos: i32, length: i32) -> Result<(), crate::Error> shared_self,owned,owned,owned sync Result<_, crate::Error> descriptive_overload_name method mapped M:LibreMetaverse.CRC32.Update(System.Byte[],System.Int32,System.Int32) method System.Void LibreMetaverse.CRC32.Update(System.Byte[] value, System.Int32 pos, System.Int32 length) libremetaverse-types libremetaverse_types::CRC32::update_with_bytes_int32_int32 pub fn update_with_bytes_int32_int32(&mut self, value: Vec<u8>, pos: i32, length: i32) -> Result<(), crate::Error> mutable_self,owned,owned,owned sync Result<_, crate::Error> descriptive_overload_name method mapped
M:LibreMetaverse.CacheDictionary`2.#ctor(System.Int32,LibreMetaverse.ICacheDictionaryRemovalStrategy{`0}) constructor LibreMetaverse.CacheDictionary<TKey, TValue>(System.Int32 maxSize, LibreMetaverse.ICacheDictionaryRemovalStrategy<TKey> removalStrategy) libremetaverse-types libremetaverse_types::CacheDictionary::new pub fn new(max_size: i32, removal_strategy: Box<dyn libremetaverse_types::ICacheDictionaryRemovalStrategy<TKey>>) -> Result<Self, crate::Error> owned,owned sync Result<_, crate::Error> direct_snake_case method mapped M:LibreMetaverse.CacheDictionary`2.#ctor(System.Int32,LibreMetaverse.ICacheDictionaryRemovalStrategy{`0}) constructor LibreMetaverse.CacheDictionary<TKey, TValue>(System.Int32 maxSize, LibreMetaverse.ICacheDictionaryRemovalStrategy<TKey> removalStrategy) libremetaverse-types libremetaverse_types::CacheDictionary::new pub fn new(max_size: i32, removal_strategy: Box<dyn libremetaverse_types::ICacheDictionaryRemovalStrategy<TKey>>) -> Result<Self, crate::Error> owned,owned sync Result<_, crate::Error> direct_snake_case method mapped
M:LibreMetaverse.CacheDictionary`2.Add(System.Collections.Generic.KeyValuePair{`0,`1}) method System.Void LibreMetaverse.CacheDictionary<TKey, TValue>.Add(System.Collections.Generic.KeyValuePair<TKey, TValue> item) libremetaverse-types libremetaverse_types::CacheDictionary::add_with_key_value_pair pub fn add_with_key_value_pair(&self, item: (TKey, Option<TValue>)) -> Result<(), crate::Error> shared_self,owned sync Result<_, crate::Error> descriptive_overload_name method mapped M:LibreMetaverse.CacheDictionary`2.Add(System.Collections.Generic.KeyValuePair{`0,`1}) method System.Void LibreMetaverse.CacheDictionary<TKey, TValue>.Add(System.Collections.Generic.KeyValuePair<TKey, TValue> item) libremetaverse-types libremetaverse_types::CacheDictionary::add_with_key_value_pair pub fn add_with_key_value_pair(&self, item: (TKey, Option<TValue>)) -> Result<(), crate::Error> shared_self,owned sync Result<_, crate::Error> descriptive_overload_name method mapped
M:LibreMetaverse.CacheDictionary`2.Add(`0,`1) method System.Void LibreMetaverse.CacheDictionary<TKey, TValue>.Add(TKey key, TValue value) libremetaverse-types libremetaverse_types::CacheDictionary::add_with_t_key_t_value pub fn add_with_t_key_t_value(&self, key: TKey, value: Option<TValue>) -> Result<(), crate::Error> shared_self,owned,owned sync Result<_, crate::Error> descriptive_overload_name method mapped M:LibreMetaverse.CacheDictionary`2.Add(`0,`1) method System.Void LibreMetaverse.CacheDictionary<TKey, TValue>.Add(TKey key, TValue value) libremetaverse-types libremetaverse_types::CacheDictionary::add_with_t_key_t_value pub fn add_with_t_key_t_value(&self, key: TKey, value: Option<TValue>) -> Result<(), crate::Error> shared_self,owned,owned sync Result<_, crate::Error> descriptive_overload_name method mapped
@@ -25081,7 +25081,7 @@ M:LibreMetaverse.UUID.GetULong method System.UInt64 LibreMetaverse.UUID.GetULong
M:LibreMetaverse.UUID.Parse(System.String) method LibreMetaverse.UUID LibreMetaverse.UUID.Parse(System.String val) libremetaverse-types libremetaverse_types::UUID::parse pub fn parse(val: String) -> Result<libremetaverse_types::UUID, crate::Error> owned sync Result<_, crate::Error> direct_snake_case method mapped M:LibreMetaverse.UUID.Parse(System.String) method LibreMetaverse.UUID LibreMetaverse.UUID.Parse(System.String val) libremetaverse-types libremetaverse_types::UUID::parse pub fn parse(val: String) -> Result<libremetaverse_types::UUID, crate::Error> owned sync Result<_, crate::Error> direct_snake_case method mapped
M:LibreMetaverse.UUID.Random method LibreMetaverse.UUID LibreMetaverse.UUID.Random() libremetaverse-types libremetaverse_types::UUID::random pub fn random() -> Result<libremetaverse_types::UUID, crate::Error> none sync Result<_, crate::Error> direct_snake_case method mapped M:LibreMetaverse.UUID.Random method LibreMetaverse.UUID LibreMetaverse.UUID.Random() libremetaverse-types libremetaverse_types::UUID::random pub fn random() -> Result<libremetaverse_types::UUID, crate::Error> none sync Result<_, crate::Error> direct_snake_case method mapped
M:LibreMetaverse.UUID.SecureRandom method LibreMetaverse.UUID LibreMetaverse.UUID.SecureRandom() libremetaverse-types libremetaverse_types::UUID::secure_random pub fn secure_random() -> Result<libremetaverse_types::UUID, crate::Error> none sync Result<_, crate::Error> direct_snake_case method mapped M:LibreMetaverse.UUID.SecureRandom method LibreMetaverse.UUID LibreMetaverse.UUID.SecureRandom() libremetaverse-types libremetaverse_types::UUID::secure_random pub fn secure_random() -> Result<libremetaverse_types::UUID, crate::Error> none sync Result<_, crate::Error> direct_snake_case method mapped
M:LibreMetaverse.UUID.ToBytes(System.Byte[],System.Int32) method System.Void LibreMetaverse.UUID.ToBytes(System.Byte[] dest, System.Int32 pos) libremetaverse-types libremetaverse_types::UUID::to_bytes pub fn to_bytes(&self, dest: Vec<u8>, pos: i32) -> Result<(), crate::Error> shared_self,owned,owned sync Result<_, crate::Error> direct_snake_case method mapped M:LibreMetaverse.UUID.ToBytes(System.Byte[],System.Int32) method System.Void LibreMetaverse.UUID.ToBytes(System.Byte[] dest, System.Int32 pos) libremetaverse-types libremetaverse_types::UUID::to_bytes pub fn to_bytes(&self, dest: &mut [u8], pos: i32) -> Result<(), crate::Error> shared_self,mutable_borrow,owned sync Result<_, crate::Error> direct_snake_case method mapped
M:LibreMetaverse.UUID.ToString method System.String LibreMetaverse.UUID.ToString() libremetaverse-types libremetaverse_types::UUID::to_string pub fn to_string(&self) -> String shared_self sync infallible_placeholder direct_snake_case method mapped M:LibreMetaverse.UUID.ToString method System.String LibreMetaverse.UUID.ToString() libremetaverse-types libremetaverse_types::UUID::to_string pub fn to_string(&self) -> String shared_self sync infallible_placeholder direct_snake_case method mapped
M:LibreMetaverse.UUID.TryParse(System.String,LibreMetaverse.UUID@) method System.Boolean LibreMetaverse.UUID.TryParse(System.String val, out LibreMetaverse.UUID& result) libremetaverse-types libremetaverse_types::UUID::try_parse pub fn try_parse(val: String, result: &mut libremetaverse_types::UUID) -> bool owned,mutable_output sync infallible_placeholder direct_snake_case method mapped M:LibreMetaverse.UUID.TryParse(System.String,LibreMetaverse.UUID@) method System.Boolean LibreMetaverse.UUID.TryParse(System.String val, out LibreMetaverse.UUID& result) libremetaverse-types libremetaverse_types::UUID::try_parse pub fn try_parse(val: String, result: &mut libremetaverse_types::UUID) -> bool owned,mutable_output sync infallible_placeholder direct_snake_case method mapped
M:LibreMetaverse.UUID.op_Equality(LibreMetaverse.UUID,LibreMetaverse.UUID) method System.Boolean LibreMetaverse.UUID.op_Equality(LibreMetaverse.UUID left, LibreMetaverse.UUID right) libremetaverse-types libremetaverse_types::UUID::eq pub fn eq(left: libremetaverse_types::UUID, right: libremetaverse_types::UUID) -> bool owned,owned sync infallible_placeholder direct_snake_case method mapped M:LibreMetaverse.UUID.op_Equality(LibreMetaverse.UUID,LibreMetaverse.UUID) method System.Boolean LibreMetaverse.UUID.op_Equality(LibreMetaverse.UUID left, LibreMetaverse.UUID right) libremetaverse-types libremetaverse_types::UUID::eq pub fn eq(left: libremetaverse_types::UUID, right: libremetaverse_types::UUID) -> bool owned,owned sync infallible_placeholder direct_snake_case method mapped
@@ -25159,8 +25159,8 @@ M:LibreMetaverse.Utils.MD5(System.String) method System.String LibreMetaverse.Ut
M:LibreMetaverse.Utils.MD5String(System.String) method System.String LibreMetaverse.Utils.MD5String(System.String value) libremetaverse-types libremetaverse_types::Utils::md5_string pub fn md5_string(value: String) -> Result<String, crate::Error> owned sync Result<_, crate::Error> direct_snake_case method mapped M:LibreMetaverse.Utils.MD5String(System.String) method System.String LibreMetaverse.Utils.MD5String(System.String value) libremetaverse-types libremetaverse_types::Utils::md5_string pub fn md5_string(value: String) -> Result<String, crate::Error> owned sync Result<_, crate::Error> direct_snake_case method mapped
M:LibreMetaverse.Utils.PBKDF2(System.String) method System.String LibreMetaverse.Utils.PBKDF2(System.String str) libremetaverse-types libremetaverse_types::Utils::pbkdf2 pub fn pbkdf2(str: String) -> Result<String, crate::Error> owned sync Result<_, crate::Error> direct_snake_case method mapped M:LibreMetaverse.Utils.PBKDF2(System.String) method System.String LibreMetaverse.Utils.PBKDF2(System.String str) libremetaverse-types libremetaverse_types::Utils::pbkdf2 pub fn pbkdf2(str: String) -> Result<String, crate::Error> owned sync Result<_, crate::Error> direct_snake_case method mapped
M:LibreMetaverse.Utils.RandomDouble method System.Double LibreMetaverse.Utils.RandomDouble() libremetaverse-types libremetaverse_types::Utils::random_double pub fn random_double() -> Result<f64, crate::Error> none sync Result<_, crate::Error> direct_snake_case method mapped M:LibreMetaverse.Utils.RandomDouble method System.Double LibreMetaverse.Utils.RandomDouble() libremetaverse-types libremetaverse_types::Utils::random_double pub fn random_double() -> Result<f64, crate::Error> none sync Result<_, crate::Error> direct_snake_case method mapped
M:LibreMetaverse.Utils.ReadDoubleLittleEndian(System.Byte[],System.Int32) method System.Double LibreMetaverse.Utils.ReadDoubleLittleEndian(System.Byte[] src, System.Int32 pos) libremetaverse-types libremetaverse_types::Utils::read_double_little_endian pub fn read_double_little_endian(src: Vec<u8>, pos: i32) -> Result<f64, crate::Error> owned,owned sync Result<_, crate::Error> direct_snake_case method mapped M:LibreMetaverse.Utils.ReadDoubleLittleEndian(System.Byte[],System.Int32) method System.Double LibreMetaverse.Utils.ReadDoubleLittleEndian(System.Byte[] src, System.Int32 pos) libremetaverse-types libremetaverse_types::Utils::read_double_little_endian pub fn read_double_little_endian(src: &[u8], pos: i32) -> Result<f64, crate::Error> shared_borrow,owned sync Result<_, crate::Error> direct_snake_case method mapped
M:LibreMetaverse.Utils.ReadSingleLittleEndian(System.Byte[],System.Int32) method System.Single LibreMetaverse.Utils.ReadSingleLittleEndian(System.Byte[] src, System.Int32 pos) libremetaverse-types libremetaverse_types::Utils::read_single_little_endian pub fn read_single_little_endian(src: Vec<u8>, pos: i32) -> Result<f32, crate::Error> owned,owned sync Result<_, crate::Error> direct_snake_case method mapped M:LibreMetaverse.Utils.ReadSingleLittleEndian(System.Byte[],System.Int32) method System.Single LibreMetaverse.Utils.ReadSingleLittleEndian(System.Byte[] src, System.Int32 pos) libremetaverse-types libremetaverse_types::Utils::read_single_little_endian pub fn read_single_little_endian(src: &[u8], pos: i32) -> Result<f32, crate::Error> shared_borrow,owned sync Result<_, crate::Error> direct_snake_case method mapped
M:LibreMetaverse.Utils.Round(System.Single) method System.Int32 LibreMetaverse.Utils.Round(System.Single val) libremetaverse-types libremetaverse_types::Utils::round pub fn round(val: f32) -> Result<i32, crate::Error> owned sync Result<_, crate::Error> direct_snake_case method mapped M:LibreMetaverse.Utils.Round(System.Single) method System.Int32 LibreMetaverse.Utils.Round(System.Single val) libremetaverse-types libremetaverse_types::Utils::round pub fn round(val: f32) -> Result<i32, crate::Error> owned sync Result<_, crate::Error> direct_snake_case method mapped
M:LibreMetaverse.Utils.SHA1(System.Byte[]) method System.Byte[] LibreMetaverse.Utils.SHA1(System.Byte[] data) libremetaverse-types libremetaverse_types::Utils::sha1 pub fn sha1(data: Vec<u8>) -> Result<Vec<u8>, crate::Error> owned sync Result<_, crate::Error> direct_snake_case method mapped M:LibreMetaverse.Utils.SHA1(System.Byte[]) method System.Byte[] LibreMetaverse.Utils.SHA1(System.Byte[] data) libremetaverse-types libremetaverse_types::Utils::sha1 pub fn sha1(data: Vec<u8>) -> Result<Vec<u8>, crate::Error> owned sync Result<_, crate::Error> direct_snake_case method mapped
M:LibreMetaverse.Utils.SHA1String(System.String) method System.String LibreMetaverse.Utils.SHA1String(System.String value) libremetaverse-types libremetaverse_types::Utils::sha1_string pub fn sha1_string(value: String) -> Result<String, crate::Error> owned sync Result<_, crate::Error> direct_snake_case method mapped M:LibreMetaverse.Utils.SHA1String(System.String) method System.String LibreMetaverse.Utils.SHA1String(System.String value) libremetaverse-types libremetaverse_types::Utils::sha1_string pub fn sha1_string(value: String) -> Result<String, crate::Error> owned sync Result<_, crate::Error> direct_snake_case method mapped
@@ -25198,8 +25198,8 @@ M:LibreMetaverse.Utils.UIntToHexString(System.UInt32) method System.String Libre
M:LibreMetaverse.Utils.UIntsToLong(System.UInt32,System.UInt32) method System.UInt64 LibreMetaverse.Utils.UIntsToLong(System.UInt32 a, System.UInt32 b) libremetaverse-types libremetaverse_types::Utils::u_ints_to_long pub fn u_ints_to_long(a: u32, b: u32) -> Result<u64, crate::Error> owned,owned sync Result<_, crate::Error> direct_snake_case method mapped M:LibreMetaverse.Utils.UIntsToLong(System.UInt32,System.UInt32) method System.UInt64 LibreMetaverse.Utils.UIntsToLong(System.UInt32 a, System.UInt32 b) libremetaverse-types libremetaverse_types::Utils::u_ints_to_long pub fn u_ints_to_long(a: u32, b: u32) -> Result<u64, crate::Error> owned,owned sync Result<_, crate::Error> direct_snake_case method mapped
M:LibreMetaverse.Utils.UnixTimeToDateTime(System.Int32) method System.DateTime LibreMetaverse.Utils.UnixTimeToDateTime(System.Int32 timestamp) libremetaverse-types libremetaverse_types::Utils::unix_time_to_date_time_with_int32 pub fn unix_time_to_date_time_with_int32(timestamp: i32) -> Result<std::time::SystemTime, crate::Error> owned sync Result<_, crate::Error> descriptive_overload_name method mapped M:LibreMetaverse.Utils.UnixTimeToDateTime(System.Int32) method System.DateTime LibreMetaverse.Utils.UnixTimeToDateTime(System.Int32 timestamp) libremetaverse-types libremetaverse_types::Utils::unix_time_to_date_time_with_int32 pub fn unix_time_to_date_time_with_int32(timestamp: i32) -> Result<std::time::SystemTime, crate::Error> owned sync Result<_, crate::Error> descriptive_overload_name method mapped
M:LibreMetaverse.Utils.UnixTimeToDateTime(System.UInt32) method System.DateTime LibreMetaverse.Utils.UnixTimeToDateTime(System.UInt32 timestamp) libremetaverse-types libremetaverse_types::Utils::unix_time_to_date_time_with_u_int32 pub fn unix_time_to_date_time_with_u_int32(timestamp: u32) -> Result<std::time::SystemTime, crate::Error> owned sync Result<_, crate::Error> descriptive_overload_name method mapped M:LibreMetaverse.Utils.UnixTimeToDateTime(System.UInt32) method System.DateTime LibreMetaverse.Utils.UnixTimeToDateTime(System.UInt32 timestamp) libremetaverse-types libremetaverse_types::Utils::unix_time_to_date_time_with_u_int32 pub fn unix_time_to_date_time_with_u_int32(timestamp: u32) -> Result<std::time::SystemTime, crate::Error> owned sync Result<_, crate::Error> descriptive_overload_name method mapped
M:LibreMetaverse.Utils.WriteDoubleLittleEndian(System.Byte[],System.Int32,System.Double) method System.Void LibreMetaverse.Utils.WriteDoubleLittleEndian(System.Byte[] dest, System.Int32 pos, System.Double value) libremetaverse-types libremetaverse_types::Utils::write_double_little_endian pub fn write_double_little_endian(dest: Vec<u8>, pos: i32, value: f64) -> Result<(), crate::Error> owned,owned,owned sync Result<_, crate::Error> direct_snake_case method mapped M:LibreMetaverse.Utils.WriteDoubleLittleEndian(System.Byte[],System.Int32,System.Double) method System.Void LibreMetaverse.Utils.WriteDoubleLittleEndian(System.Byte[] dest, System.Int32 pos, System.Double value) libremetaverse-types libremetaverse_types::Utils::write_double_little_endian pub fn write_double_little_endian(dest: &mut [u8], pos: i32, value: f64) -> Result<(), crate::Error> mutable_borrow,owned,owned sync Result<_, crate::Error> direct_snake_case method mapped
M:LibreMetaverse.Utils.WriteSingleLittleEndian(System.Byte[],System.Int32,System.Single) method System.Void LibreMetaverse.Utils.WriteSingleLittleEndian(System.Byte[] dest, System.Int32 pos, System.Single value) libremetaverse-types libremetaverse_types::Utils::write_single_little_endian pub fn write_single_little_endian(dest: Vec<u8>, pos: i32, value: f32) -> Result<(), crate::Error> owned,owned,owned sync Result<_, crate::Error> direct_snake_case method mapped M:LibreMetaverse.Utils.WriteSingleLittleEndian(System.Byte[],System.Int32,System.Single) method System.Void LibreMetaverse.Utils.WriteSingleLittleEndian(System.Byte[] dest, System.Int32 pos, System.Single value) libremetaverse-types libremetaverse_types::Utils::write_single_little_endian pub fn write_single_little_endian(dest: &mut [u8], pos: i32, value: f32) -> Result<(), crate::Error> mutable_borrow,owned,owned sync Result<_, crate::Error> direct_snake_case method mapped
M:LibreMetaverse.Vector2.#ctor(LibreMetaverse.Vector2) constructor LibreMetaverse.Vector2(LibreMetaverse.Vector2 vector) libremetaverse-types libremetaverse_types::Vector2::new_with_vector2 pub fn new_with_vector2(vector: libremetaverse_types::Vector2) -> Result<Self, crate::Error> owned sync Result<_, crate::Error> descriptive_overload_name method mapped M:LibreMetaverse.Vector2.#ctor(LibreMetaverse.Vector2) constructor LibreMetaverse.Vector2(LibreMetaverse.Vector2 vector) libremetaverse-types libremetaverse_types::Vector2::new_with_vector2 pub fn new_with_vector2(vector: libremetaverse_types::Vector2) -> Result<Self, crate::Error> owned sync Result<_, crate::Error> descriptive_overload_name method mapped
M:LibreMetaverse.Vector2.#ctor(System.Single) constructor LibreMetaverse.Vector2(System.Single value) libremetaverse-types libremetaverse_types::Vector2::new_with_single pub fn new_with_single(value: f32) -> Result<Self, crate::Error> owned sync Result<_, crate::Error> descriptive_overload_name method mapped M:LibreMetaverse.Vector2.#ctor(System.Single) constructor LibreMetaverse.Vector2(System.Single value) libremetaverse-types libremetaverse_types::Vector2::new_with_single pub fn new_with_single(value: f32) -> Result<Self, crate::Error> owned sync Result<_, crate::Error> descriptive_overload_name method mapped
M:LibreMetaverse.Vector2.#ctor(System.Single,System.Single) constructor LibreMetaverse.Vector2(System.Single x, System.Single y) libremetaverse-types libremetaverse_types::Vector2::new_with_single_single pub fn new_with_single_single(x: f32, y: f32) -> Result<Self, crate::Error> owned,owned sync Result<_, crate::Error> descriptive_overload_name method mapped M:LibreMetaverse.Vector2.#ctor(System.Single,System.Single) constructor LibreMetaverse.Vector2(System.Single x, System.Single y) libremetaverse-types libremetaverse_types::Vector2::new_with_single_single pub fn new_with_single_single(x: f32, y: f32) -> Result<Self, crate::Error> owned,owned sync Result<_, crate::Error> descriptive_overload_name method mapped
Can't render this file because it is too large.

View File

@@ -13,7 +13,7 @@ Generated by `python3 tools/generate_api_shims.py`; do not edit by hand.
| `LibreMetaverse.Rendering.MeshFoundry` | 1 | 14 | callable failure-only shim | | `LibreMetaverse.Rendering.MeshFoundry` | 1 | 14 | callable failure-only shim |
| `LibreMetaverse.Rendering.Simple` | 1 | 6 | callable failure-only shim | | `LibreMetaverse.Rendering.Simple` | 1 | 6 | callable failure-only shim |
| `LibreMetaverse.StructuredData` | 16 | 295 | callable failure-only shim | | `LibreMetaverse.StructuredData` | 16 | 295 | callable failure-only shim |
| `LibreMetaverse.Types` | 45 | 942 | callable failure-only shim | | `LibreMetaverse.Types` | 45 | 942 | native implementation: 2 types / 34 members; remaining surface is callable failure-only shims |
| `LibreMetaverse.Utilities` | 3 | 13 | callable failure-only shim | | `LibreMetaverse.Utilities` | 3 | 13 | callable failure-only shim |
| `LibreMetaverse.Voice.Vivox` | 64 | 531 | callable failure-only shim | | `LibreMetaverse.Voice.Vivox` | 64 | 531 | callable failure-only shim |
| `LibreMetaverse.Voice.WebRTC` | 12 | 210 | callable failure-only shim | | `LibreMetaverse.Voice.WebRTC` | 12 | 210 | callable failure-only shim |

View File

@@ -9,3 +9,8 @@ description = "Core value-type shims for the MetaCrate LibreMetaverse rewrite"
[lints] [lints]
workspace = true workspace = true
[dependencies]
getrandom = "0.4"
md-5 = "0.10"
uuid = { version = "1.18", features = ["v4"] }

View File

@@ -0,0 +1,91 @@
//! Platform-independent byte-order helpers owned by `Utils`.
use crate::Error;
fn checked_range(pos: i32, width: usize, len: usize) -> Result<std::ops::Range<usize>, Error> {
let start = usize::try_from(pos).map_err(|_| Error::IndexOutOfRange)?;
let end = start.checked_add(width).ok_or(Error::IndexOutOfRange)?;
if end > len {
return Err(Error::IndexOutOfRange);
}
Ok(start..end)
}
pub(crate) fn read_single_little_endian(src: &[u8], pos: i32) -> Result<f32, Error> {
let range = checked_range(pos, 4, src.len())?;
let bytes: [u8; 4] = src[range]
.try_into()
.expect("range has the requested width");
Ok(f32::from_le_bytes(bytes))
}
pub(crate) fn write_single_little_endian(
dest: &mut [u8],
pos: i32,
value: f32,
) -> Result<(), Error> {
let range = checked_range(pos, 4, dest.len())?;
dest[range].copy_from_slice(&value.to_le_bytes());
Ok(())
}
pub(crate) fn read_double_little_endian(src: &[u8], pos: i32) -> Result<f64, Error> {
let range = checked_range(pos, 8, src.len())?;
let bytes: [u8; 8] = src[range]
.try_into()
.expect("range has the requested width");
Ok(f64::from_le_bytes(bytes))
}
pub(crate) fn write_double_little_endian(
dest: &mut [u8],
pos: i32,
value: f64,
) -> Result<(), Error> {
let range = checked_range(pos, 8, dest.len())?;
dest[range].copy_from_slice(&value.to_le_bytes());
Ok(())
}
#[cfg(test)]
mod tests {
#![allow(clippy::float_cmp)] // Exact bit-preserving round trips are the behavior under test.
use super::*;
#[test]
fn little_endian_float_golden_vectors_round_trip_at_offsets() {
let mut bytes = [0xaa; 14];
write_single_little_endian(&mut bytes, 1, -12.5).unwrap();
assert_eq!(&bytes[1..5], &(-12.5_f32).to_bits().to_le_bytes());
assert_eq!(
read_single_little_endian(&bytes, 1).unwrap().to_bits(),
(-12.5_f32).to_bits()
);
write_double_little_endian(&mut bytes, 5, std::f64::consts::PI).unwrap();
assert_eq!(&bytes[5..13], &std::f64::consts::PI.to_bits().to_le_bytes());
assert_eq!(
read_double_little_endian(&bytes, 5).unwrap().to_bits(),
std::f64::consts::PI.to_bits()
);
assert_eq!(bytes[0], 0xaa);
assert_eq!(bytes[13], 0xaa);
}
#[test]
fn byte_order_helpers_reject_invalid_ranges() {
assert_eq!(
read_single_little_endian(&[0; 4], -1),
Err(Error::IndexOutOfRange)
);
assert_eq!(
read_double_little_endian(&[0; 7], 0),
Err(Error::IndexOutOfRange)
);
assert_eq!(
write_single_little_endian(&mut [0; 4], 1, 0.0),
Err(Error::IndexOutOfRange)
);
}
}

View File

@@ -22,6 +22,7 @@ impl<T: std::io::Read + std::io::Write> ReadWrite for T {}
#[derive(Clone, Debug, Eq, Hash, PartialEq)] #[derive(Clone, Debug, Eq, Hash, PartialEq)]
pub enum Object { pub enum Object {
String(String), String(String),
UUID(crate::UUID),
} }
impl From<String> for Object { impl From<String> for Object {

View File

@@ -0,0 +1,119 @@
//! Native implementation of `LibreMetaverse`'s incremental CRC-32 accumulator.
#![allow(clippy::needless_pass_by_value)] // Public signature mirrors the mapped C# byte array.
use crate::Error;
/// Incremental reflected CRC-32 accumulator using polynomial `0xedb88320`.
///
/// `LibreMetaverse` exposes the running (not final-XORed) accumulator. A new
/// value therefore starts at `0xffff_ffff`, and callers may apply a final XOR
/// themselves if they need the conventional ISO-HDLC checksum.
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub struct CRC32 {
/// Current running CRC value.
pub crc: u32,
}
impl CRC32 {
/// Creates an accumulator initialized to `0xffff_ffff`.
///
/// # Errors
///
/// This compatibility signature is infallible and always returns `Ok`.
pub fn new() -> Result<Self, Error> {
Ok(Self { crc: u32::MAX })
}
/// Incorporates one byte into the running accumulator.
///
/// # Errors
///
/// This compatibility signature is infallible and always returns `Ok`.
pub fn update_with_byte(&mut self, value: u8) -> Result<(), Error> {
self.crc = update(self.crc, value);
Ok(())
}
/// Updates bytes in the half-open range `pos..length`.
///
/// The final argument intentionally preserves the pinned C# implementation:
/// it is an exclusive end index, despite being named `length`.
///
/// # Errors
///
/// Returns [`Error::IndexOutOfRange`] when an iterated index is outside
/// `value`. As in C#, bytes before the invalid index remain incorporated.
pub fn update_with_bytes_int32_int32(
&mut self,
value: Vec<u8>,
pos: i32,
length: i32,
) -> Result<(), Error> {
if pos >= length {
return Ok(());
}
for index in pos..length {
let index = usize::try_from(index).map_err(|_| Error::IndexOutOfRange)?;
let byte = *value.get(index).ok_or(Error::IndexOutOfRange)?;
self.crc = update(self.crc, byte);
}
Ok(())
}
}
fn update(mut crc: u32, byte: u8) -> u32 {
crc ^= u32::from(byte);
for _ in 0..8 {
let mask = 0_u32.wrapping_sub(crc & 1);
crc = (crc >> 1) ^ (0xedb8_8320 & mask);
}
crc
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn crc32_matches_standard_and_incremental_golden_vectors() {
let mut crc = CRC32::new().unwrap();
crc.update_with_bytes_int32_int32(b"123456789".to_vec(), 0, 9)
.unwrap();
assert_eq!(crc.crc, 0x340b_c6d9);
assert_eq!(crc.crc ^ u32::MAX, 0xcbf4_3926);
let mut incremental = CRC32::new().unwrap();
for byte in b"123456789" {
incremental.update_with_byte(*byte).unwrap();
}
assert_eq!(incremental, crc);
}
#[test]
fn range_argument_is_an_exclusive_end_index() {
let mut selected = CRC32::new().unwrap();
selected
.update_with_bytes_int32_int32(b"x123y".to_vec(), 1, 4)
.unwrap();
let mut direct = CRC32::new().unwrap();
direct
.update_with_bytes_int32_int32(b"123".to_vec(), 0, 3)
.unwrap();
assert_eq!(selected, direct);
}
#[test]
fn invalid_end_index_preserves_csharp_partial_update_behavior() {
let mut partial = CRC32::new().unwrap();
assert_eq!(
partial.update_with_bytes_int32_int32(b"12".to_vec(), 0, 3),
Err(Error::IndexOutOfRange)
);
let mut expected = CRC32::new().unwrap();
expected
.update_with_bytes_int32_int32(b"12".to_vec(), 0, 2)
.unwrap();
assert_eq!(partial, expected);
}
}

View File

@@ -184,31 +184,11 @@ pub enum AttachmentPoint {
} }
/// C# type: `T:LibreMetaverse.CRC32`. /// C# type: `T:LibreMetaverse.CRC32`.
pub struct CRC32 { /// C# member: `F:LibreMetaverse.CRC32.CRC`.
/// C# member: `F:LibreMetaverse.CRC32.CRC`. /// C# member: `M:LibreMetaverse.CRC32.#ctor`.
pub crc: u32, /// C# member: `M:LibreMetaverse.CRC32.Update(System.Byte)`.
} /// C# member: `M:LibreMetaverse.CRC32.Update(System.Byte[],System.Int32,System.Int32)`.
impl CRC32 { pub use crate::crc32::CRC32;
/// C# member: `M:LibreMetaverse.CRC32.#ctor`.
pub fn new() -> Result<Self, crate::Error> {
libremetaverse_types::not_implemented("M:LibreMetaverse.CRC32.#ctor")
}
/// C# member: `M:LibreMetaverse.CRC32.Update(System.Byte)`.
pub fn update_with_byte(&self, value: u8) -> Result<(), crate::Error> {
libremetaverse_types::not_implemented("M:LibreMetaverse.CRC32.Update(System.Byte)")
}
/// C# member: `M:LibreMetaverse.CRC32.Update(System.Byte[],System.Int32,System.Int32)`.
pub fn update_with_bytes_int32_int32(
&self,
value: Vec<u8>,
pos: i32,
length: i32,
) -> Result<(), crate::Error> {
libremetaverse_types::not_implemented(
"M:LibreMetaverse.CRC32.Update(System.Byte[],System.Int32,System.Int32)",
)
}
}
/// C# type: `T:LibreMetaverse.CacheDictionary`2`. /// C# type: `T:LibreMetaverse.CacheDictionary`2`.
pub struct CacheDictionary<TKey, TValue> { pub struct CacheDictionary<TKey, TValue> {
@@ -2658,154 +2638,33 @@ impl TokenBucket {
} }
/// C# type: `T:LibreMetaverse.UUID`. /// C# type: `T:LibreMetaverse.UUID`.
#[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)] /// C# member: `F:LibreMetaverse.UUID.Zero`.
pub struct UUID { /// C# member: `M:LibreMetaverse.UUID.#ctor(System.Byte[],System.Int32)`.
bytes: [u8; 16], /// C# member: `M:LibreMetaverse.UUID.#ctor(System.Guid)`.
} /// C# member: `M:LibreMetaverse.UUID.#ctor(System.String)`.
impl UUID { /// C# member: `M:LibreMetaverse.UUID.#ctor(System.UInt64)`.
/// C# member: `F:LibreMetaverse.UUID.Zero`. /// C# member: `M:LibreMetaverse.UUID.CRC`.
pub fn zero() -> libremetaverse_types::UUID { /// C# member: `M:LibreMetaverse.UUID.Combine(LibreMetaverse.UUID,LibreMetaverse.UUID)`.
libremetaverse_types::unimplemented_api!("F:LibreMetaverse.UUID.Zero") /// C# member: `M:LibreMetaverse.UUID.CompareTo(LibreMetaverse.UUID)`.
} /// C# member: `M:LibreMetaverse.UUID.Deconstruct(System.Guid@)`.
/// C# member: `M:LibreMetaverse.UUID.#ctor(System.Byte[],System.Int32)`. /// C# member: `M:LibreMetaverse.UUID.Equals(LibreMetaverse.UUID)`.
pub fn new_with_bytes_int32(source: Vec<u8>, pos: i32) -> Result<Self, crate::Error> { /// C# member: `M:LibreMetaverse.UUID.Equals(System.Object)`.
libremetaverse_types::not_implemented( /// C# member: `M:LibreMetaverse.UUID.FromBytes(System.Byte[],System.Int32)`.
"M:LibreMetaverse.UUID.#ctor(System.Byte[],System.Int32)", /// C# member: `M:LibreMetaverse.UUID.GetBytes`.
) /// C# member: `M:LibreMetaverse.UUID.GetHashCode`.
} /// C# member: `M:LibreMetaverse.UUID.GetULong`.
/// C# member: `M:LibreMetaverse.UUID.#ctor(System.Guid)`. /// C# member: `M:LibreMetaverse.UUID.Parse(System.String)`.
pub fn new_with_guid(guid: libremetaverse_types::compat::Guid) -> Result<Self, crate::Error> { /// C# member: `M:LibreMetaverse.UUID.Random`.
libremetaverse_types::not_implemented("M:LibreMetaverse.UUID.#ctor(System.Guid)") /// C# member: `M:LibreMetaverse.UUID.SecureRandom`.
} /// C# member: `M:LibreMetaverse.UUID.ToBytes(System.Byte[],System.Int32)`.
/// C# member: `M:LibreMetaverse.UUID.#ctor(System.String)`. /// C# member: `M:LibreMetaverse.UUID.ToString`.
pub fn new_with_string(val: String) -> Result<Self, crate::Error> { /// C# member: `M:LibreMetaverse.UUID.TryParse(System.String,LibreMetaverse.UUID@)`.
libremetaverse_types::not_implemented("M:LibreMetaverse.UUID.#ctor(System.String)") /// C# member: `M:LibreMetaverse.UUID.op_Equality(LibreMetaverse.UUID,LibreMetaverse.UUID)`.
} /// C# member: `M:LibreMetaverse.UUID.op_ExclusiveOr(LibreMetaverse.UUID,LibreMetaverse.UUID)`.
/// C# member: `M:LibreMetaverse.UUID.#ctor(System.UInt64)`. /// C# member: `M:LibreMetaverse.UUID.op_Explicit(System.String)~LibreMetaverse.UUID`.
pub fn new_with_u_int64(val: u64) -> Result<Self, crate::Error> { /// C# member: `M:LibreMetaverse.UUID.op_Inequality(LibreMetaverse.UUID,LibreMetaverse.UUID)`.
libremetaverse_types::not_implemented("M:LibreMetaverse.UUID.#ctor(System.UInt64)") /// C# member: `P:LibreMetaverse.UUID.Guid`.
} pub use crate::uuid::UUID;
/// C# member: `M:LibreMetaverse.UUID.CRC`.
pub fn crc(&mut self) -> Result<u32, crate::Error> {
libremetaverse_types::not_implemented("M:LibreMetaverse.UUID.CRC")
}
/// C# member: `M:LibreMetaverse.UUID.Combine(LibreMetaverse.UUID,LibreMetaverse.UUID)`.
pub fn combine(
first: libremetaverse_types::UUID,
second: libremetaverse_types::UUID,
) -> Result<libremetaverse_types::UUID, crate::Error> {
libremetaverse_types::not_implemented(
"M:LibreMetaverse.UUID.Combine(LibreMetaverse.UUID,LibreMetaverse.UUID)",
)
}
/// C# member: `M:LibreMetaverse.UUID.CompareTo(LibreMetaverse.UUID)`.
pub fn compare_to(&self, id: libremetaverse_types::UUID) -> Result<i32, crate::Error> {
libremetaverse_types::not_implemented(
"M:LibreMetaverse.UUID.CompareTo(LibreMetaverse.UUID)",
)
}
/// C# member: `M:LibreMetaverse.UUID.Deconstruct(System.Guid@)`.
pub fn deconstruct(
&mut self,
guid: &mut libremetaverse_types::compat::Guid,
) -> Result<(), crate::Error> {
libremetaverse_types::not_implemented("M:LibreMetaverse.UUID.Deconstruct(System.Guid@)")
}
/// C# member: `M:LibreMetaverse.UUID.Equals(LibreMetaverse.UUID)`.
pub fn equals_with_uuid(&self, other: libremetaverse_types::UUID) -> bool {
libremetaverse_types::unimplemented_api!(
"M:LibreMetaverse.UUID.Equals(LibreMetaverse.UUID)"
)
}
/// C# member: `M:LibreMetaverse.UUID.Equals(System.Object)`.
pub fn equals_with_object(&self, obj: libremetaverse_types::compat::Object) -> bool {
libremetaverse_types::unimplemented_api!("M:LibreMetaverse.UUID.Equals(System.Object)")
}
/// C# member: `M:LibreMetaverse.UUID.FromBytes(System.Byte[],System.Int32)`.
pub fn from_bytes(
source: Vec<u8>,
pos: i32,
) -> Result<libremetaverse_types::UUID, crate::Error> {
libremetaverse_types::not_implemented(
"M:LibreMetaverse.UUID.FromBytes(System.Byte[],System.Int32)",
)
}
/// C# member: `M:LibreMetaverse.UUID.GetBytes`.
pub fn get_bytes(&self) -> Result<Vec<u8>, crate::Error> {
libremetaverse_types::not_implemented("M:LibreMetaverse.UUID.GetBytes")
}
/// C# member: `M:LibreMetaverse.UUID.GetHashCode`.
pub fn get_hash_code(&self) -> i32 {
libremetaverse_types::unimplemented_api!("M:LibreMetaverse.UUID.GetHashCode")
}
/// C# member: `M:LibreMetaverse.UUID.GetULong`.
pub fn get_u_long(&self) -> Result<u64, crate::Error> {
libremetaverse_types::not_implemented("M:LibreMetaverse.UUID.GetULong")
}
/// C# member: `M:LibreMetaverse.UUID.Parse(System.String)`.
pub fn parse(val: String) -> Result<libremetaverse_types::UUID, crate::Error> {
libremetaverse_types::not_implemented("M:LibreMetaverse.UUID.Parse(System.String)")
}
/// C# member: `M:LibreMetaverse.UUID.Random`.
pub fn random() -> Result<libremetaverse_types::UUID, crate::Error> {
libremetaverse_types::not_implemented("M:LibreMetaverse.UUID.Random")
}
/// C# member: `M:LibreMetaverse.UUID.SecureRandom`.
pub fn secure_random() -> Result<libremetaverse_types::UUID, crate::Error> {
libremetaverse_types::not_implemented("M:LibreMetaverse.UUID.SecureRandom")
}
/// C# member: `M:LibreMetaverse.UUID.ToBytes(System.Byte[],System.Int32)`.
pub fn to_bytes(&self, dest: Vec<u8>, pos: i32) -> Result<(), crate::Error> {
libremetaverse_types::not_implemented(
"M:LibreMetaverse.UUID.ToBytes(System.Byte[],System.Int32)",
)
}
/// C# member: `M:LibreMetaverse.UUID.ToString`.
pub fn to_string(&self) -> String {
libremetaverse_types::unimplemented_api!("M:LibreMetaverse.UUID.ToString")
}
/// C# member: `M:LibreMetaverse.UUID.TryParse(System.String,LibreMetaverse.UUID@)`.
pub fn try_parse(val: String, result: &mut libremetaverse_types::UUID) -> bool {
libremetaverse_types::unimplemented_api!(
"M:LibreMetaverse.UUID.TryParse(System.String,LibreMetaverse.UUID@)"
)
}
/// C# member: `M:LibreMetaverse.UUID.op_Equality(LibreMetaverse.UUID,LibreMetaverse.UUID)`.
pub fn eq(left: libremetaverse_types::UUID, right: libremetaverse_types::UUID) -> bool {
libremetaverse_types::unimplemented_api!(
"M:LibreMetaverse.UUID.op_Equality(LibreMetaverse.UUID,LibreMetaverse.UUID)"
)
}
/// C# member: `M:LibreMetaverse.UUID.op_ExclusiveOr(LibreMetaverse.UUID,LibreMetaverse.UUID)`.
pub fn bitxor(
lhs: libremetaverse_types::UUID,
rhs: libremetaverse_types::UUID,
) -> libremetaverse_types::UUID {
libremetaverse_types::unimplemented_api!(
"M:LibreMetaverse.UUID.op_ExclusiveOr(LibreMetaverse.UUID,LibreMetaverse.UUID)"
)
}
/// C# member: `M:LibreMetaverse.UUID.op_Explicit(System.String)~LibreMetaverse.UUID`.
pub fn try_from(val: String) -> libremetaverse_types::UUID {
libremetaverse_types::unimplemented_api!(
"M:LibreMetaverse.UUID.op_Explicit(System.String)~LibreMetaverse.UUID"
)
}
/// C# member: `M:LibreMetaverse.UUID.op_Inequality(LibreMetaverse.UUID,LibreMetaverse.UUID)`.
pub fn ne(left: libremetaverse_types::UUID, right: libremetaverse_types::UUID) -> bool {
libremetaverse_types::unimplemented_api!(
"M:LibreMetaverse.UUID.op_Inequality(LibreMetaverse.UUID,LibreMetaverse.UUID)"
)
}
/// C# member: `P:LibreMetaverse.UUID.Guid`.
pub fn guid(&self) -> libremetaverse_types::compat::Guid {
libremetaverse_types::unimplemented_api!("P:LibreMetaverse.UUID.Guid")
}
/// Setter for C# member: `P:LibreMetaverse.UUID.Guid`.
pub fn set_guid(&mut self, value: libremetaverse_types::compat::Guid) {
libremetaverse_types::unimplemented_api!("P:LibreMetaverse.UUID.Guid")
}
}
/// C# type: `T:LibreMetaverse.Utils`. /// C# type: `T:LibreMetaverse.Utils`.
pub struct Utils; pub struct Utils;
@@ -3252,16 +3111,12 @@ impl Utils {
libremetaverse_types::not_implemented("M:LibreMetaverse.Utils.RandomDouble") libremetaverse_types::not_implemented("M:LibreMetaverse.Utils.RandomDouble")
} }
/// C# member: `M:LibreMetaverse.Utils.ReadDoubleLittleEndian(System.Byte[],System.Int32)`. /// C# member: `M:LibreMetaverse.Utils.ReadDoubleLittleEndian(System.Byte[],System.Int32)`.
pub fn read_double_little_endian(src: Vec<u8>, pos: i32) -> Result<f64, crate::Error> { pub fn read_double_little_endian(src: &[u8], pos: i32) -> Result<f64, crate::Error> {
libremetaverse_types::not_implemented( crate::byte_order::read_double_little_endian(src, pos)
"M:LibreMetaverse.Utils.ReadDoubleLittleEndian(System.Byte[],System.Int32)",
)
} }
/// C# member: `M:LibreMetaverse.Utils.ReadSingleLittleEndian(System.Byte[],System.Int32)`. /// C# member: `M:LibreMetaverse.Utils.ReadSingleLittleEndian(System.Byte[],System.Int32)`.
pub fn read_single_little_endian(src: Vec<u8>, pos: i32) -> Result<f32, crate::Error> { pub fn read_single_little_endian(src: &[u8], pos: i32) -> Result<f32, crate::Error> {
libremetaverse_types::not_implemented( crate::byte_order::read_single_little_endian(src, pos)
"M:LibreMetaverse.Utils.ReadSingleLittleEndian(System.Byte[],System.Int32)",
)
} }
/// C# member: `M:LibreMetaverse.Utils.Round(System.Single)`. /// C# member: `M:LibreMetaverse.Utils.Round(System.Single)`.
pub fn round(val: f32) -> Result<i32, crate::Error> { pub fn round(val: f32) -> Result<i32, crate::Error> {
@@ -3510,23 +3365,19 @@ impl Utils {
} }
/// C# member: `M:LibreMetaverse.Utils.WriteDoubleLittleEndian(System.Byte[],System.Int32,System.Double)`. /// C# member: `M:LibreMetaverse.Utils.WriteDoubleLittleEndian(System.Byte[],System.Int32,System.Double)`.
pub fn write_double_little_endian( pub fn write_double_little_endian(
dest: Vec<u8>, dest: &mut [u8],
pos: i32, pos: i32,
value: f64, value: f64,
) -> Result<(), crate::Error> { ) -> Result<(), crate::Error> {
libremetaverse_types::not_implemented( crate::byte_order::write_double_little_endian(dest, pos, value)
"M:LibreMetaverse.Utils.WriteDoubleLittleEndian(System.Byte[],System.Int32,System.Double)",
)
} }
/// C# member: `M:LibreMetaverse.Utils.WriteSingleLittleEndian(System.Byte[],System.Int32,System.Single)`. /// C# member: `M:LibreMetaverse.Utils.WriteSingleLittleEndian(System.Byte[],System.Int32,System.Single)`.
pub fn write_single_little_endian( pub fn write_single_little_endian(
dest: Vec<u8>, dest: &mut [u8],
pos: i32, pos: i32,
value: f32, value: f32,
) -> Result<(), crate::Error> { ) -> Result<(), crate::Error> {
libremetaverse_types::not_implemented( crate::byte_order::write_single_little_endian(dest, pos, value)
"M:LibreMetaverse.Utils.WriteSingleLittleEndian(System.Byte[],System.Int32,System.Single)",
)
} }
} }

View File

@@ -2,9 +2,12 @@
extern crate self as libremetaverse_types; extern crate self as libremetaverse_types;
mod byte_order;
pub mod compat; pub mod compat;
mod crc32;
mod generated; mod generated;
pub mod shim; pub mod shim;
mod uuid;
pub use generated::*; pub use generated::*;
pub use shim::{Error, NotImplemented, not_implemented}; pub use shim::{Error, NotImplemented, not_implemented};

View File

@@ -0,0 +1,448 @@
//! Native, byte-order-explicit UUID compatibility type.
#![allow(clippy::missing_errors_doc)] // Result shapes are fixed by the public compatibility map.
#![allow(clippy::needless_pass_by_value)] // Owned arguments mirror mapped C# value parameters.
use crate::Error;
use crate::compat::{Guid, Object};
use md5::{Digest, Md5};
use std::cmp::Ordering;
use std::fmt;
/// A 128-bit UUID stored in RFC/network byte order.
///
/// `System.Guid` uses a mixed-endian byte-array representation for its first
/// three fields. Conversions to and from [`Guid`] explicitly perform that
/// permutation; all protocol-facing byte APIs remain unambiguously big-endian.
#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct UUID(uuid::Uuid);
impl UUID {
#[must_use]
pub const fn zero() -> Self {
Self(uuid::Uuid::nil())
}
pub fn new_with_bytes_int32(source: Vec<u8>, pos: i32) -> Result<Self, Error> {
Self::from_slice_at(&source, pos)
}
pub fn new_with_guid(guid: Guid) -> Result<Self, Error> {
Ok(Self(uuid::Uuid::from_bytes(guid_raw_to_network(guid.0))))
}
pub fn new_with_string(val: String) -> Result<Self, Error> {
if val.is_empty() {
return Ok(Self::zero());
}
parse_guid_text(&val).map(Self).ok_or(Error::Argument)
}
pub fn new_with_u_int64(val: u64) -> Result<Self, Error> {
let mut bytes = [0_u8; 16];
bytes[8..].copy_from_slice(&val.to_le_bytes());
Ok(Self(uuid::Uuid::from_bytes(bytes)))
}
pub fn crc(&mut self) -> Result<u32, Error> {
let bytes = self.0.as_bytes();
Ok((0..4)
.map(|index| {
let offset = index * 4;
u32::from_be_bytes([
bytes[offset],
bytes[offset + 1],
bytes[offset + 2],
bytes[offset + 3],
])
})
.fold(0_u32, u32::wrapping_add))
}
pub fn combine(first: Self, second: Self) -> Result<Self, Error> {
let mut digest = Md5::new();
digest.update(first.0.as_bytes());
digest.update(second.0.as_bytes());
let bytes: [u8; 16] = digest.finalize().into();
Ok(Self(uuid::Uuid::from_bytes(bytes)))
}
pub fn compare_to(&self, id: Self) -> Result<i32, Error> {
Ok(match self.cmp(&id) {
Ordering::Less => -1,
Ordering::Equal => 0,
Ordering::Greater => 1,
})
}
pub fn deconstruct(&mut self, guid: &mut Guid) -> Result<(), Error> {
*guid = self.guid();
Ok(())
}
#[must_use]
pub fn equals_with_uuid(&self, other: Self) -> bool {
*self == other
}
#[must_use]
pub fn equals_with_object(&self, obj: Object) -> bool {
matches!(obj, Object::UUID(value) if *self == value)
}
pub fn from_bytes(source: Vec<u8>, pos: i32) -> Result<Self, Error> {
Self::from_slice_at(&source, pos)
}
pub fn get_bytes(&self) -> Result<Vec<u8>, Error> {
Ok(self.0.as_bytes().to_vec())
}
/// Returns the hash produced by `System.Guid.GetHashCode` for the wrapped
/// Guid value. This is deliberately distinct from Rust's randomized
/// [`Hash`](std::hash::Hash) use in collections.
#[must_use]
pub fn get_hash_code(&self) -> i32 {
let raw = network_to_guid_raw(*self.0.as_bytes());
let a = i32::from_le_bytes([raw[0], raw[1], raw[2], raw[3]]);
let b = i32::from(i16::from_le_bytes([raw[4], raw[5]]));
let c = u16::from_le_bytes([raw[6], raw[7]]);
a ^ ((b << 16) | i32::from(c)) ^ ((i32::from(raw[10])) << 24 | i32::from(raw[15]))
}
pub fn get_u_long(&self) -> Result<u64, Error> {
let bytes = self.0.as_bytes();
Ok(u64::from_le_bytes([
bytes[8], bytes[9], bytes[10], bytes[11], bytes[12], bytes[13], bytes[14], bytes[15],
]))
}
pub fn parse(val: String) -> Result<Self, Error> {
Self::new_with_string(val)
}
pub fn random() -> Result<Self, Error> {
Ok(Self(uuid::Uuid::new_v4()))
}
pub fn secure_random() -> Result<Self, Error> {
// Match the pinned implementation exactly: random bytes are first
// interpreted in Guid.ToByteArray layout, then its raw indices 6 and 8
// receive the version/variant masks.
let mut raw = [0_u8; 16];
getrandom::fill(&mut raw).map_err(|_| Error::InvalidOperation)?;
raw[6] = (raw[6] & 0x0f) | 0x40;
raw[8] = (raw[8] & 0x3f) | 0x80;
Ok(Self(uuid::Uuid::from_bytes(guid_raw_to_network(raw))))
}
pub fn to_bytes(&self, dest: &mut [u8], pos: i32) -> Result<(), Error> {
let start = usize::try_from(pos).map_err(|_| Error::IndexOutOfRange)?;
let end = start.checked_add(16).ok_or(Error::IndexOutOfRange)?;
let target = dest.get_mut(start..end).ok_or(Error::IndexOutOfRange)?;
target.copy_from_slice(self.0.as_bytes());
Ok(())
}
#[must_use]
#[allow(clippy::inherent_to_string_shadow_display)] // Required mapped C# member.
pub fn to_string(&self) -> String {
self.0.hyphenated().to_string()
}
pub fn try_parse(val: String, result: &mut Self) -> bool {
if val.is_empty() {
*result = Self::zero();
return false;
}
if let Some(value) = parse_guid_text(&val) {
*result = Self(value);
true
} else {
*result = Self::zero();
false
}
}
#[must_use]
#[allow(clippy::should_implement_trait)] // Required mapped operator name.
pub fn eq(left: Self, right: Self) -> bool {
left == right
}
#[must_use]
#[allow(clippy::should_implement_trait)] // Required mapped operator name.
pub fn bitxor(lhs: Self, rhs: Self) -> Self {
let mut bytes = [0_u8; 16];
for ((output, left), right) in bytes.iter_mut().zip(lhs.0.as_bytes()).zip(rhs.0.as_bytes())
{
*output = left ^ right;
}
Self(uuid::Uuid::from_bytes(bytes))
}
/// Preserves the mapped explicit conversion. Like the C# operator, invalid
/// input cannot be represented in its return type and therefore panics.
///
/// # Panics
///
/// Panics when `val` is not accepted by `System.Guid` parsing rules.
#[must_use]
pub fn try_from(val: String) -> Self {
Self::new_with_string(val).expect("invalid UUID string in explicit conversion")
}
#[must_use]
pub fn ne(left: Self, right: Self) -> bool {
left != right
}
#[must_use]
pub fn guid(&self) -> Guid {
Guid(network_to_guid_raw(*self.0.as_bytes()))
}
pub fn set_guid(&mut self, value: Guid) {
self.0 = uuid::Uuid::from_bytes(guid_raw_to_network(value.0));
}
fn from_slice_at(source: &[u8], pos: i32) -> Result<Self, Error> {
let start = usize::try_from(pos).map_err(|_| Error::IndexOutOfRange)?;
let end = start.checked_add(16).ok_or(Error::IndexOutOfRange)?;
let bytes: [u8; 16] = source
.get(start..end)
.ok_or(Error::IndexOutOfRange)?
.try_into()
.expect("UUID range has sixteen bytes");
Ok(Self(uuid::Uuid::from_bytes(bytes)))
}
}
impl fmt::Display for UUID {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
self.0.hyphenated().fmt(formatter)
}
}
impl From<UUID> for Object {
fn from(value: UUID) -> Self {
Self::UUID(value)
}
}
fn guid_raw_to_network(raw: [u8; 16]) -> [u8; 16] {
[
raw[3], raw[2], raw[1], raw[0], raw[5], raw[4], raw[7], raw[6], raw[8], raw[9], raw[10],
raw[11], raw[12], raw[13], raw[14], raw[15],
]
}
fn network_to_guid_raw(network: [u8; 16]) -> [u8; 16] {
[
network[3],
network[2],
network[1],
network[0],
network[5],
network[4],
network[7],
network[6],
network[8],
network[9],
network[10],
network[11],
network[12],
network[13],
network[14],
network[15],
]
}
fn parse_guid_text(value: &str) -> Option<uuid::Uuid> {
let value = value.trim();
if let Ok(parsed) = uuid::Uuid::parse_str(value) {
return Some(parsed);
}
if let Some(inner) = value
.strip_prefix('(')
.and_then(|inner| inner.strip_suffix(')'))
.or_else(|| {
value
.strip_prefix('{')
.and_then(|inner| inner.strip_suffix('}'))
})
&& let Ok(parsed) = uuid::Uuid::parse_str(inner)
{
return Some(parsed);
}
parse_guid_x_format(value)
}
fn parse_guid_x_format(value: &str) -> Option<uuid::Uuid> {
let compact: String = value
.chars()
.filter(|character| !character.is_whitespace())
.collect();
let outer = compact.strip_prefix('{')?.strip_suffix('}')?;
let (leading, trailing) = outer.split_once(",{")?;
let trailing = trailing.strip_suffix('}')?;
let fields: Vec<&str> = leading.split(',').chain(trailing.split(',')).collect();
if fields.len() != 11 {
return None;
}
let a = u32::from_str_radix(strip_hex_prefix(fields[0])?, 16).ok()?;
let b = u16::from_str_radix(strip_hex_prefix(fields[1])?, 16).ok()?;
let c = u16::from_str_radix(strip_hex_prefix(fields[2])?, 16).ok()?;
let mut bytes = [0_u8; 16];
bytes[..4].copy_from_slice(&a.to_be_bytes());
bytes[4..6].copy_from_slice(&b.to_be_bytes());
bytes[6..8].copy_from_slice(&c.to_be_bytes());
for (output, field) in bytes[8..].iter_mut().zip(&fields[3..]) {
*output = u8::from_str_radix(strip_hex_prefix(field)?, 16).ok()?;
}
Some(uuid::Uuid::from_bytes(bytes))
}
fn strip_hex_prefix(field: &str) -> Option<&str> {
field
.strip_prefix("0x")
.or_else(|| field.strip_prefix("0X"))
}
#[cfg(test)]
mod tests {
use super::*;
const NETWORK: [u8; 16] = [
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
0x0f,
];
const TEXT: &str = "00010203-0405-0607-0809-0a0b0c0d0e0f";
#[test]
fn network_bytes_string_and_guid_raw_bytes_are_unambiguous() {
let id = UUID::new_with_bytes_int32(NETWORK.to_vec(), 0).unwrap();
assert_eq!(id.to_string(), TEXT);
assert_eq!(id.get_bytes().unwrap(), NETWORK);
assert_eq!(
id.guid().0,
[
0x03, 0x02, 0x01, 0x00, 0x05, 0x04, 0x07, 0x06, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d,
0x0e, 0x0f,
]
);
assert_eq!(UUID::new_with_guid(id.guid()).unwrap(), id);
}
#[test]
fn parsing_offsets_and_malformed_inputs_match_contract() {
assert_eq!(UUID::new_with_string(String::new()).unwrap(), UUID::zero());
assert_eq!(
UUID::parse(TEXT.replace('-', "")).unwrap().to_string(),
TEXT
);
assert_eq!(UUID::parse(TEXT.to_uppercase()).unwrap().to_string(), TEXT);
assert_eq!(
UUID::parse(format!(" {TEXT}\t")).unwrap().to_string(),
TEXT
);
assert_eq!(UUID::parse(format!("({TEXT})")).unwrap().to_string(), TEXT);
assert_eq!(
UUID::parse(format!("{{{TEXT}}}")).unwrap().to_string(),
TEXT
);
assert_eq!(
UUID::parse(
"{0x00010203,0x0405,0x0607,{0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f}}".to_owned()
)
.unwrap()
.to_string(),
TEXT
);
assert_eq!(UUID::parse("bad".to_owned()), Err(Error::Argument));
let mut output = UUID::parse(TEXT.to_owned()).unwrap();
assert!(!UUID::try_parse(String::new(), &mut output));
assert_eq!(output, UUID::zero());
assert!(!UUID::try_parse("bad".to_owned(), &mut output));
assert_eq!(output, UUID::zero());
let mut with_offset = vec![0xff; 20];
with_offset[2..18].copy_from_slice(&NETWORK);
assert_eq!(UUID::from_bytes(with_offset, 2).unwrap().to_string(), TEXT);
assert_eq!(
UUID::from_bytes(vec![0; 15], 0),
Err(Error::IndexOutOfRange)
);
}
#[test]
fn crc_ulong_xor_and_combine_match_golden_values() {
let mut id = UUID::parse(TEXT.to_owned()).unwrap();
assert_eq!(id.crc().unwrap(), 0x181c_2024);
assert_eq!(id.get_u_long().unwrap(), 0x0f0e_0d0c_0b0a_0908);
assert_eq!(UUID::bitxor(id, id), UUID::zero());
let second = UUID::parse("ffffffff-ffff-ffff-ffff-ffffffffffff".to_owned()).unwrap();
assert_eq!(
UUID::combine(id, second).unwrap().to_string(),
"1145ee5c-de6d-592f-027e-3daf0424653e"
);
}
#[test]
fn destination_write_and_u64_constructor_preserve_exact_bytes() {
let id = UUID::parse(TEXT.to_owned()).unwrap();
let mut destination = [0xff; 20];
id.to_bytes(&mut destination, 2).unwrap();
assert_eq!(&destination[2..18], &NETWORK);
assert_eq!(destination[0], 0xff);
assert_eq!(destination[19], 0xff);
assert_eq!(
id.to_bytes(&mut destination, 5),
Err(Error::IndexOutOfRange)
);
let from_u64 = UUID::new_with_u_int64(0x0102_0304_0506_0708).unwrap();
assert_eq!(
from_u64.get_bytes().unwrap(),
[0, 0, 0, 0, 0, 0, 0, 0, 8, 7, 6, 5, 4, 3, 2, 1]
);
}
#[test]
fn generated_values_have_expected_uniqueness_and_bit_layout() {
let random = UUID::random().unwrap();
assert_ne!(random, UUID::zero());
assert_eq!(random.0.get_version_num(), 4);
assert_eq!(random.0.as_bytes()[8] & 0xc0, 0x80);
let secure = UUID::secure_random().unwrap();
let raw = secure.guid().0;
assert_eq!(raw[6] & 0xf0, 0x40);
assert_eq!(raw[8] & 0xc0, 0x80);
}
#[test]
fn comparison_hash_guid_mutation_and_mapped_operators_are_compatible() {
let id = UUID::parse(TEXT.to_owned()).unwrap();
let greater = UUID::parse("00010203-0405-0607-0809-0a0b0c0d0e10".to_owned()).unwrap();
assert_eq!(id.compare_to(id).unwrap(), 0);
assert_eq!(id.compare_to(greater).unwrap(), -1);
assert_eq!(greater.compare_to(id).unwrap(), 1);
assert_eq!(id.get_hash_code(), 0x0e04_040b);
assert!(UUID::eq(id, id));
assert!(!UUID::ne(id, id));
assert!(id.equals_with_object(Object::from(id)));
assert!(!id.equals_with_object(Object::from(TEXT)));
assert_eq!(UUID::try_from(TEXT.to_owned()), id);
let mut deconstructed = Guid([0xff; 16]);
let mut mutable = id;
mutable.deconstruct(&mut deconstructed).unwrap();
assert_eq!(deconstructed, id.guid());
mutable.set_guid(greater.guid());
assert_eq!(mutable, greater);
}
}

View File

@@ -2,6 +2,109 @@
# It is not intended for manual editing. # It is not intended for manual editing.
version = 4 version = 4
[[package]]
name = "block-buffer"
version = "0.10.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
dependencies = [
"generic-array",
]
[[package]]
name = "bumpalo"
version = "3.20.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649"
[[package]]
name = "cfg-if"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
[[package]]
name = "crypto-common"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a"
dependencies = [
"generic-array",
"typenum",
]
[[package]]
name = "digest"
version = "0.10.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
dependencies = [
"block-buffer",
"crypto-common",
]
[[package]]
name = "futures-core"
version = "0.3.33"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2cd50c473c80f6d7c3670a752354b8e569b1a7cbfdc0419ec88e5edad85e0dc7"
[[package]]
name = "futures-task"
version = "0.3.33"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b231ed28831efb4a61a08580c4bc233ec56bc009f4cd8f52da2c3cb97df0c109"
[[package]]
name = "futures-util"
version = "0.3.33"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a77a90a256fce34da66415271e30f94ee91c57b04b8a2c042d9cf3220179deaa"
dependencies = [
"futures-core",
"futures-task",
"pin-project-lite",
"slab",
]
[[package]]
name = "generic-array"
version = "0.14.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
dependencies = [
"typenum",
"version_check",
]
[[package]]
name = "getrandom"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "300e883d756b2e4ec94e02791f39b04b522276138852cfc41d9fb7e904106099"
dependencies = [
"cfg-if",
"libc",
"r-efi",
]
[[package]]
name = "js-sys"
version = "0.3.104"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0e0c1080212aad755ea003d18543e8768dd432c48819efd73a7bf1e39b7a5a3a"
dependencies = [
"cfg-if",
"futures-util",
"wasm-bindgen",
]
[[package]]
name = "libc"
version = "0.2.189"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3eaf3ede3fee6db1a4c2ee091bf8a8b4dccdc6d17f656fb07896ee72867612f2"
[[package]] [[package]]
name = "libremetaverse" name = "libremetaverse"
version = "0.0.1" version = "0.0.1"
@@ -78,6 +181,11 @@ dependencies = [
[[package]] [[package]]
name = "libremetaverse-types" name = "libremetaverse-types"
version = "0.0.1" version = "0.0.1"
dependencies = [
"getrandom",
"md-5",
"uuid",
]
[[package]] [[package]]
name = "libremetaverse-utilities" name = "libremetaverse-utilities"
@@ -106,6 +214,16 @@ dependencies = [
"libremetaverse-types", "libremetaverse-types",
] ]
[[package]]
name = "md-5"
version = "0.10.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d89e7ee0cfbedfc4da3340218492196241d89eefb6dab27de5df917a6d2e78cf"
dependencies = [
"cfg-if",
"digest",
]
[[package]] [[package]]
name = "metacrate-api-compile" name = "metacrate-api-compile"
version = "0.0.0" version = "0.0.0"
@@ -124,3 +242,136 @@ dependencies = [
"libremetaverse-voice-vivox", "libremetaverse-voice-vivox",
"libremetaverse-voice-webrtc", "libremetaverse-voice-webrtc",
] ]
[[package]]
name = "once_cell"
version = "1.21.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50"
[[package]]
name = "pin-project-lite"
version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd"
[[package]]
name = "proc-macro2"
version = "1.0.107"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "985e7ec9bb745e6ce6535b544d84d6cd6f7ad8bd711c398938ae983b91a766d9"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.47"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fbf4db142a473a8d80c26bbf18454ed458bf8d26c8219c331daecfdbd079001"
dependencies = [
"proc-macro2",
]
[[package]]
name = "r-efi"
version = "6.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf"
[[package]]
name = "rustversion"
version = "1.0.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf54715a573b99ac80df0bc206da022bcd442c974952c7b9720069370852e21f"
[[package]]
name = "slab"
version = "0.4.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5"
[[package]]
name = "syn"
version = "2.0.119"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "872831b642d1a07999a962a351ed35b955ea2cfc8f3862091e2a240a84f17297"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "typenum"
version = "1.20.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6f5e870be6c3b371b77fe0ee0bafb859fa4964b4404c27de1d380043c4dda20"
[[package]]
name = "unicode-ident"
version = "1.0.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
[[package]]
name = "uuid"
version = "1.24.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bf3923a6f5c4c6382e0b653c4117f48d631ea17f38ed86e2a828e6f7412f5239"
dependencies = [
"getrandom",
"js-sys",
"wasm-bindgen",
]
[[package]]
name = "version_check"
version = "0.9.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
[[package]]
name = "wasm-bindgen"
version = "0.2.127"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b70935747edd64d89de3efa29d73789b806c15798f8e7dca4d8ac356b50ce70"
dependencies = [
"cfg-if",
"once_cell",
"rustversion",
"wasm-bindgen-macro",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-macro"
version = "0.2.127"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77775f8f3f7217702089053b94958f8f54061a3f663417df76e19cbdcca29bc1"
dependencies = [
"quote",
"wasm-bindgen-macro-support",
]
[[package]]
name = "wasm-bindgen-macro-support"
version = "0.2.127"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e11d33f857dc2fb11b8bc75aee111aa9cbeb12cd9f25efd3d4c2a3dd4e235284"
dependencies = [
"bumpalo",
"proc-macro2",
"quote",
"syn",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-shared"
version = "0.2.127"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7ef64dbcc55df09c7e5a46182d181c2cfa3e925f3da937ea764728b4bbb9dcbf"
dependencies = [
"unicode-ident",
]

View File

@@ -34,6 +34,25 @@ TARGETS = {
"LibreMetaverse.Voice.WebRTC": ROOT / "crates/libremetaverse-voice-webrtc/src/generated.rs", "LibreMetaverse.Voice.WebRTC": ROOT / "crates/libremetaverse-voice-webrtc/src/generated.rs",
} }
# Types whose failure-only generated shells have been replaced by native Rust
# implementations. The generated module keeps catalog markers and re-exports
# the hand-written type so coverage remains deterministic.
NATIVE_TYPES = {
"T:LibreMetaverse.CRC32": "crate::crc32::CRC32",
"T:LibreMetaverse.UUID": "crate::uuid::UUID",
}
NATIVE_MEMBER_BODIES = {
"M:LibreMetaverse.Utils.ReadSingleLittleEndian(System.Byte[],System.Int32)":
"crate::byte_order::read_single_little_endian(src, pos)",
"M:LibreMetaverse.Utils.WriteSingleLittleEndian(System.Byte[],System.Int32,System.Single)":
"crate::byte_order::write_single_little_endian(dest, pos, value)",
"M:LibreMetaverse.Utils.ReadDoubleLittleEndian(System.Byte[],System.Int32)":
"crate::byte_order::read_double_little_endian(src, pos)",
"M:LibreMetaverse.Utils.WriteDoubleLittleEndian(System.Byte[],System.Int32,System.Double)":
"crate::byte_order::write_double_little_endian(dest, pos, value)",
}
MAIN_TARGET = ROOT / "crates/libremetaverse/src/generated.rs" MAIN_TARGET = ROOT / "crates/libremetaverse/src/generated.rs"
WIRE_NAMESPACES = ( WIRE_NAMESPACES = (
"LibreMetaverse.Assets", "LibreMetaverse.Assets",
@@ -539,6 +558,8 @@ def render_enum(item: dict, rust_name: str) -> list[str]:
def render_body(signature: str, member_id: str, error_model: str, asyncness: str, trait: bool) -> str: def render_body(signature: str, member_id: str, error_model: str, asyncness: str, trait: bool) -> str:
if trait: if trait:
signature = signature.removeprefix("pub ") signature = signature.removeprefix("pub ")
if member_id in NATIVE_MEMBER_BODIES:
return f" {signature} {{ {NATIVE_MEMBER_BODIES[member_id]} }}"
failure = ( failure = (
f"libremetaverse_types::not_implemented({json.dumps(member_id)})" f"libremetaverse_types::not_implemented({json.dumps(member_id)})"
if error_model.startswith("Result") if error_model.startswith("Result")
@@ -564,6 +585,11 @@ def rust_constant_value(item: dict) -> str:
def render_type(item: dict, type_row: dict, member_rows: dict[str, dict[str, str]], mapper: mapping.Mapper) -> str: def render_type(item: dict, type_row: dict, member_rows: dict[str, dict[str, str]], mapper: mapping.Mapper) -> str:
rust_name = type_name(type_row) rust_name = type_name(type_row)
if native_path := NATIVE_TYPES.get(item["doc_id"]):
lines = [f"/// C# type: `{item['doc_id']}`."]
lines.extend(f"/// C# member: `{member['doc_id']}`." for member in item["members"])
lines.append(f"pub use {native_path} as {rust_name};")
return "\n".join(lines)
if item["kind"] == "enum": if item["kind"] == "enum":
return "\n".join(render_enum(item, rust_name)) return "\n".join(render_enum(item, rust_name))
names = generic_names(item) names = generic_names(item)
@@ -803,7 +829,22 @@ def coverage_report(catalog: dict, coverage: dict[str, tuple[int, int, bool]]) -
got_types, got_members, complete = coverage[name] got_types, got_members, complete = coverage[name]
if complete and (types, members) != (got_types, got_members): if complete and (types, members) != (got_types, got_members):
raise ValueError(f"incomplete generated slice for {name}") raise ValueError(f"incomplete generated slice for {name}")
status = "callable failure-only shim" if complete else f"partial callable shim ({got_types:,} types / {got_members:,} members)" native_type_ids = {
item["doc_id"] for item in assembly["types"] if item["doc_id"] in NATIVE_TYPES
}
native_member_ids = {
member["doc_id"]
for item in assembly["types"]
for member in item["members"]
if item["doc_id"] in native_type_ids or member["doc_id"] in NATIVE_MEMBER_BODIES
}
if native_type_ids or native_member_ids:
status = (
f"native implementation: {len(native_type_ids):,} types / "
f"{len(native_member_ids):,} members; remaining surface is callable failure-only shims"
)
else:
status = "callable failure-only shim" if complete else f"partial callable shim ({got_types:,} types / {got_members:,} members)"
total_types += got_types total_types += got_types
total_members += got_members total_members += got_members
else: else:

View File

@@ -189,6 +189,26 @@ MEMBER_TYPE_OVERRIDES = {
), ),
} }
MEMBER_SIGNATURE_OVERRIDES = { MEMBER_SIGNATURE_OVERRIDES = {
"M:LibreMetaverse.Utils.ReadSingleLittleEndian(System.Byte[],System.Int32)": (
"pub fn read_single_little_endian(src: &[u8], pos: i32) -> Result<f32, crate::Error>",
"shared_borrow,owned",
),
"M:LibreMetaverse.Utils.WriteSingleLittleEndian(System.Byte[],System.Int32,System.Single)": (
"pub fn write_single_little_endian(dest: &mut [u8], pos: i32, value: f32) -> Result<(), crate::Error>",
"mutable_borrow,owned,owned",
),
"M:LibreMetaverse.Utils.ReadDoubleLittleEndian(System.Byte[],System.Int32)": (
"pub fn read_double_little_endian(src: &[u8], pos: i32) -> Result<f64, crate::Error>",
"shared_borrow,owned",
),
"M:LibreMetaverse.Utils.WriteDoubleLittleEndian(System.Byte[],System.Int32,System.Double)": (
"pub fn write_double_little_endian(dest: &mut [u8], pos: i32, value: f64) -> Result<(), crate::Error>",
"mutable_borrow,owned,owned",
),
"M:LibreMetaverse.UUID.ToBytes(System.Byte[],System.Int32)": (
"pub fn to_bytes(&self, dest: &mut [u8], pos: i32) -> Result<(), crate::Error>",
"shared_self,mutable_borrow,owned",
),
"M:LibreMetaverse.AppearanceManager.#ctor(LibreMetaverse.GridClient)": ( "M:LibreMetaverse.AppearanceManager.#ctor(LibreMetaverse.GridClient)": (
"pub fn new(client: Option<std::sync::Arc<libremetaverse::GridClient>>) " "pub fn new(client: Option<std::sync::Arc<libremetaverse::GridClient>>) "
"-> Result<Self, crate::Error>", "-> Result<Self, crate::Error>",
@@ -853,7 +873,15 @@ def validate_generated_shims() -> None:
for body in function_bodies(text): for body in function_bodies(text):
if FAKE_BODY.search(body): if FAKE_BODY.search(body):
raise ValueError(f"generated shim returns a plausible fallback: {path.relative_to(ROOT)}") raise ValueError(f"generated shim returns a plausible fallback: {path.relative_to(ROOT)}")
if not any(marker in body for marker in ("unimplemented_api!", "not_implemented(", "NotImplemented::new(")): if not any(
marker in body
for marker in (
"unimplemented_api!",
"not_implemented(",
"NotImplemented::new(",
"crate::byte_order::",
)
):
raise ValueError(f"generated shim function does not use the standardized failure: {path.relative_to(ROOT)}") raise ValueError(f"generated shim function does not use the standardized failure: {path.relative_to(ROOT)}")
@@ -983,6 +1011,8 @@ def generic_names(item: dict, owner: dict) -> set[str]:
def method_receiver(owner: dict, item: dict) -> tuple[str, str]: def method_receiver(owner: dict, item: dict) -> tuple[str, str]:
if item.get("static") or item["kind"] == "constructor": if item.get("static") or item["kind"] == "constructor":
return "", "none" return "", "none"
if owner["doc_id"] == "T:LibreMetaverse.CRC32" and item["name"] == "Update":
return "&mut self", "mutable_self"
if item["name"] == "Deserialize": if item["name"] == "Deserialize":
return "&mut self", "mutable_self" return "&mut self", "mutable_self"
if owner["doc_id"] == "T:LibreMetaverse.Assets.AssetMaterial" and item["name"].startswith( if owner["doc_id"] == "T:LibreMetaverse.Assets.AssetMaterial" and item["name"].startswith(