From 827010f79dda1f03ffc7a8d205a1ad9e64aeb8de Mon Sep 17 00:00:00 2001 From: Kevin Watkins Date: Fri, 20 Feb 2026 16:37:52 +0000 Subject: [PATCH 1/7] Added documentation. --- .github/workflows/release.yml | 25 + ReadMe.md | 86 ++++ doc/.gitignore | 171 +++++++ doc/.idea/.gitignore | 10 + doc/.idea/doc.iml | 10 + doc/.idea/encodings.xml | 4 + .../inspectionProfiles/Project_Default.xml | 19 + .../inspectionProfiles/profiles_settings.xml | 6 + doc/.idea/misc.xml | 7 + doc/.idea/modules.xml | 8 + doc/.idea/runConfigurations/Build.xml | 17 + doc/.idea/runConfigurations/Generate_API.xml | 17 + doc/.idea/runConfigurations/Serve.xml | 17 + doc/.idea/vcs.xml | 6 + doc/docs/.gitignore | 1 + doc/docs/bit-operations.md | 200 ++++++++ doc/docs/index.md | 36 ++ doc/docs/reading-and-writing.md | 165 ++++++ doc/docs/streams.md | 92 ++++ doc/docs/stylesheets/overrides.css | 3 + doc/global_state.py | 2 + doc/hooks.py | 9 + doc/main.py | 70 +++ doc/mkdocs.yml | 39 ++ doc/pyproject.toml | 11 + doc/requirements.txt | 4 + doc/uv.lock | 470 ++++++++++++++++++ src/Directory.Build.props | 1 + .../ByteIReadOnlyListExtensions.cs | 4 +- 29 files changed, 1508 insertions(+), 2 deletions(-) create mode 100644 doc/.gitignore create mode 100644 doc/.idea/.gitignore create mode 100644 doc/.idea/doc.iml create mode 100644 doc/.idea/encodings.xml create mode 100644 doc/.idea/inspectionProfiles/Project_Default.xml create mode 100644 doc/.idea/inspectionProfiles/profiles_settings.xml create mode 100644 doc/.idea/misc.xml create mode 100644 doc/.idea/modules.xml create mode 100644 doc/.idea/runConfigurations/Build.xml create mode 100644 doc/.idea/runConfigurations/Generate_API.xml create mode 100644 doc/.idea/runConfigurations/Serve.xml create mode 100644 doc/.idea/vcs.xml create mode 100644 doc/docs/.gitignore create mode 100644 doc/docs/bit-operations.md create mode 100644 doc/docs/index.md create mode 100644 doc/docs/reading-and-writing.md create mode 100644 doc/docs/streams.md create mode 100644 doc/docs/stylesheets/overrides.css create mode 100644 doc/global_state.py create mode 100644 doc/hooks.py create mode 100644 doc/main.py create mode 100644 doc/mkdocs.yml create mode 100644 doc/pyproject.toml create mode 100644 doc/requirements.txt create mode 100644 doc/uv.lock diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index a0412bf..4b82de2 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -26,3 +26,28 @@ jobs: directory: src nuget-api-key: ${{ secrets.NUGET_API_KEY }} version: ${{ inputs.version }} + + - name: Build + uses: MrKWatkins/dotnet-build@main + with: + name: MrKWatkins.BinaryPrimitives + directory: src + + - name: Restore .NET tools + run: dotnet tool restore + + - name: Install Sesharp + run: dotnet tool install MrKWatkins.Sesharp.Tool + + - name: Make API Docs Directory + run: mkdir doc/docs/API + + - name: Generate API Docs + run: dotnet sesharp src/MrKWatkins.BinaryPrimitives/bin/Release/net10.0/MrKWatkins.BinaryPrimitives.dll doc/docs/API + + - name: Deploy docs + uses: mhausenblas/mkdocs-deploy-gh-pages@master + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + CONFIG_FILE: doc/mkdocs.yml + REQUIREMENTS: doc/requirements.txt diff --git a/ReadMe.md b/ReadMe.md index 032ac96..b750cb8 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -8,6 +8,92 @@ The name is based on the [BinaryPrimitives](https://learn.microsoft.com/en-us/dotnet/api/system.buffers.binary.binaryprimitives) class in the .NET Framework which provides similar methods. +Full documentation is available at [mrkwatkins.github.io/BinaryPrimitives](https://mrkwatkins.github.io/BinaryPrimitives/). Source code and issue tracking are on [GitHub](https://github.com/MrKWatkins/BinaryPrimitives). + +## Installation + +``` +dotnet add package MrKWatkins.BinaryPrimitives +``` + +## Usage + +### Reading and Writing + +Read and write multi-byte integers from `byte[]`, `Span`, `Stream`, and other byte +container types. All methods support an optional `Endian` parameter (defaulting to +`Endian.Little`): + +```csharp +byte[] data = new byte[8]; +data.SetInt32(0, 42); +data.SetUInt16(4, 1000, Endian.Big); + +int a = data.GetInt32(0); // 42 +ushort b = data.GetUInt16(4, Endian.Big); // 1000 +``` + +Collections can have values appended directly: + +```csharp +var buffer = new List(); +buffer.AddUInt32(0xDEADBEEF); +buffer.AddInt16(-1, Endian.Big); +``` + +Streams support typed reading and writing, with async overloads for all methods: + +```csharp +stream.WriteInt32(42); +int value = stream.ReadInt32OrThrow(); +``` + +### Bit Operations + +Get, set, and clear individual bits or ranges of bits on `byte`, `ushort`, `int`, `uint`, +`long`, and `ulong`: + +```csharp +byte b = 0b00001010; +bool set = b.GetBit(1); // true +byte with0 = b.SetBit(0); // 0b00001011 +byte clear = b.ResetBit(1); // 0b00001000 +byte range = b.GetBits(1, 3); // bits 1..3, shifted to position 0 +``` + +`byte` also provides nibble access, parity, and CPU flag detection: + +```csharp +byte high = b.HighNibble(); // bits 4-7 +bool even = b.Parity(); // true if an even number of bits are set +``` + +All supported integer types can be formatted as a binary string: + +```csharp +string s = ((byte)0x5A).ToBinaryString(); // "0b01011010" +``` + +### Streams + +`ReadOnlyListStream` wraps an `IReadOnlyList` as a seekable, read-only `Stream`: + +```csharp +IReadOnlyList bytes = LoadBytes(); +using var stream = new ReadOnlyListStream(bytes); +``` + +`PeekableStream` wraps any readable `Stream` to add one-byte lookahead without consuming the +byte: + +```csharp +using var stream = new PeekableStream(inner); +if (stream.Peek() == 0xFF) +{ + stream.ReadByteOrThrow(); // consume it +} +``` + ## Licencing Licensed under GPL v3.0. \ No newline at end of file diff --git a/doc/.gitignore b/doc/.gitignore new file mode 100644 index 0000000..867b516 --- /dev/null +++ b/doc/.gitignore @@ -0,0 +1,171 @@ +copilot.data.*.xml +data/ +node_modules/ +docs/assets/javascripts/ +docs/assets/vocabulary/categories.json + +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +logs/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +test_results.xml +test-results.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# poetry +# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control +#poetry.lock + +# pdm +# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. +#pdm.lock +# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it +# in version control. +# https://pdm.fming.dev/latest/usage/project/#working-with-version-control +.pdm.toml +.pdm-python +.pdm-build/ + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# PyCharm +# JetBrains specific template is maintained in a separate JetBrains.gitignore that can +# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore +# and can be added to the global gitignore or merged into this file. For a more nuclear +# option (not recommended) you can uncomment the following to ignore the entire idea folder. +#.idea/ diff --git a/doc/.idea/.gitignore b/doc/.idea/.gitignore new file mode 100644 index 0000000..ab1f416 --- /dev/null +++ b/doc/.idea/.gitignore @@ -0,0 +1,10 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Ignored default folder with query files +/queries/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml +# Editor-based HTTP Client requests +/httpRequests/ diff --git a/doc/.idea/doc.iml b/doc/.idea/doc.iml new file mode 100644 index 0000000..2c2e0f6 --- /dev/null +++ b/doc/.idea/doc.iml @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/doc/.idea/encodings.xml b/doc/.idea/encodings.xml new file mode 100644 index 0000000..df87cf9 --- /dev/null +++ b/doc/.idea/encodings.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/doc/.idea/inspectionProfiles/Project_Default.xml b/doc/.idea/inspectionProfiles/Project_Default.xml new file mode 100644 index 0000000..90c935d --- /dev/null +++ b/doc/.idea/inspectionProfiles/Project_Default.xml @@ -0,0 +1,19 @@ + + + + \ No newline at end of file diff --git a/doc/.idea/inspectionProfiles/profiles_settings.xml b/doc/.idea/inspectionProfiles/profiles_settings.xml new file mode 100644 index 0000000..105ce2d --- /dev/null +++ b/doc/.idea/inspectionProfiles/profiles_settings.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/doc/.idea/misc.xml b/doc/.idea/misc.xml new file mode 100644 index 0000000..c05ee9f --- /dev/null +++ b/doc/.idea/misc.xml @@ -0,0 +1,7 @@ + + + + + + \ No newline at end of file diff --git a/doc/.idea/modules.xml b/doc/.idea/modules.xml new file mode 100644 index 0000000..989d897 --- /dev/null +++ b/doc/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/doc/.idea/runConfigurations/Build.xml b/doc/.idea/runConfigurations/Build.xml new file mode 100644 index 0000000..5618cf6 --- /dev/null +++ b/doc/.idea/runConfigurations/Build.xml @@ -0,0 +1,17 @@ + + + + \ No newline at end of file diff --git a/doc/.idea/runConfigurations/Generate_API.xml b/doc/.idea/runConfigurations/Generate_API.xml new file mode 100644 index 0000000..bbab76d --- /dev/null +++ b/doc/.idea/runConfigurations/Generate_API.xml @@ -0,0 +1,17 @@ + + + + \ No newline at end of file diff --git a/doc/.idea/runConfigurations/Serve.xml b/doc/.idea/runConfigurations/Serve.xml new file mode 100644 index 0000000..44e8d27 --- /dev/null +++ b/doc/.idea/runConfigurations/Serve.xml @@ -0,0 +1,17 @@ + + + + \ No newline at end of file diff --git a/doc/.idea/vcs.xml b/doc/.idea/vcs.xml new file mode 100644 index 0000000..6c0b863 --- /dev/null +++ b/doc/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/doc/docs/.gitignore b/doc/docs/.gitignore new file mode 100644 index 0000000..4bb2df2 --- /dev/null +++ b/doc/docs/.gitignore @@ -0,0 +1 @@ +API/**/*.md \ No newline at end of file diff --git a/doc/docs/bit-operations.md b/doc/docs/bit-operations.md new file mode 100644 index 0000000..e7677fa --- /dev/null +++ b/doc/docs/bit-operations.md @@ -0,0 +1,200 @@ +# Bit Operations + +The library provides extension methods for common bit-level operations on primitive integer +types. These cover individual bit access, bit ranges, nibble manipulation, parity, overflow +detection, and binary string formatting. + +## Supported Types + +Bit operations are available on the following types: + +| Type | Bit Width | API Reference | +| -------- | --------- | ------------- | +| `byte` | 8 | [`ByteExtensions`](API/MrKWatkins.BinaryPrimitives/ByteExtensions/index.md) | +| `ushort` | 16 | [`UInt16Extensions`](API/MrKWatkins.BinaryPrimitives/UInt16Extensions/index.md) | +| `int` | 32 | [`Int32Extensions`](API/MrKWatkins.BinaryPrimitives/Int32Extensions/index.md) | +| `uint` | 32 | [`UInt32Extensions`](API/MrKWatkins.BinaryPrimitives/UInt32Extensions/index.md) | +| `long` | 64 | [`Int64Extensions`](API/MrKWatkins.BinaryPrimitives/Int64Extensions/index.md) | +| `ulong` | 64 | [`UInt64Extensions`](API/MrKWatkins.BinaryPrimitives/UInt64Extensions/index.md) | + +## Individual Bit Access + +`GetBit(index)` returns `true` if the bit at the given zero-based index is set. +`SetBit(index)` returns a new value with that bit set. +`ResetBit(index)` returns a new value with that bit cleared. + +Bits are numbered from 0 (least significant) to N-1 (most significant). + +```csharp +byte b = 0b00001010; // bits 1 and 3 are set + +bool bit1 = b.GetBit(1); // true +bool bit0 = b.GetBit(0); // false + +byte set = b.SetBit(0); // 0b00001011 +byte reset = b.ResetBit(1); // 0b00001000 +``` + +## Bit Ranges + +`GetBits(startInclusive, endInclusive)` extracts a contiguous range of bits, shifting the +result right so that the start bit is at position 0. `SetBits(value, startInclusive, +endInclusive)` replaces a range of bits with the low bits of the supplied value. + +Both methods validate that the range indices are within bounds and that `endInclusive` is not +less than `startInclusive`. + +```csharp +byte b = 0b01101100; + +byte nibble = b.GetBits(2, 5); // bits 2..5 = 0b1011, shifted to 0b1011 (11) + +byte updated = b.SetBits(0b1111, 2, 5); // sets bits 2..5 to 1111 -> 0b01111100 +``` + +## Convenience Bit Properties + +Three convenience methods provide named access to commonly used bit positions: + +- `LeftMostBit()` returns the value of the most significant bit. +- `RightMostBit()` returns the value of the least significant bit (bit 0). +- `SignBit()` returns the value of the sign bit (same as `LeftMostBit()`). + +```csharp +byte b = 0b10000001; + +bool left = b.LeftMostBit(); // true (bit 7) +bool right = b.RightMostBit(); // true (bit 0) +bool sign = b.SignBit(); // true (bit 7) +``` + +## Nibble Operations (byte only) + +`HighNibble()` returns bits 4-7 of a byte shifted down to bits 0-3. +`LowNibble()` returns bits 0-3 of a byte. +`SetHighNibble(nibble)` returns a new byte with the upper four bits replaced. +`SetLowNibble(nibble)` returns a new byte with the lower four bits replaced. + +```csharp +byte b = 0xAB; + +byte high = b.HighNibble(); // 0x0A +byte low = b.LowNibble(); // 0x0B + +byte updated = b.SetHighNibble(0x05); // 0x5B +``` + +## Copying Bits with a Mask (byte only) + +[`CopyBitsFrom(toCopyFrom, mask)`](API/MrKWatkins.BinaryPrimitives/ByteExtensions/CopyBitsFrom.md) +copies bits from one byte to another selectively. Bits where the mask is set are taken from +`toCopyFrom`; bits where the mask is clear are taken from the receiver. + +```csharp +byte target = 0b11110000; +byte source = 0b10101010; +byte mask = 0b00001111; + +byte result = target.CopyBitsFrom(source, mask); // 0b11101010 +``` + +## Parity (byte only) + +[`Parity()`](API/MrKWatkins.BinaryPrimitives/ByteExtensions/Parity.md) returns `true` if the +number of set bits in the byte is even (even parity). + +```csharp +byte b = 0b00001111; // 4 set bits +bool even = b.Parity(); // true + +byte c = 0b00000111; // 3 set bits +bool odd = c.Parity(); // false +``` + +## Byte Decomposition (ushort only) + +`MostSignificantByte()` returns the high byte, and `LeastSignificantByte()` returns the low +byte. `ToBytes()` returns both as a `(byte Msb, byte Lsb)` tuple. + +```csharp +ushort value = 0x1234; + +byte msb = value.MostSignificantByte(); // 0x12 +byte lsb = value.LeastSignificantByte(); // 0x34 + +(byte m, byte l) = value.ToBytes(); // m = 0x12, l = 0x34 +``` + +## Binary String Representation + +`ToBinaryString()` converts any supported integer value to a human-readable binary string +prefixed with `"0b"`. + +| Type | Output length | Example | +| -------- | ------------- | ------------------------------------ | +| `byte` | 10 chars | `"0b01011010"` | +| `ushort` | 18 chars | `"0b0001001000110100"` | +| `int` | 34 chars | `"0b00000000000000000000000001011010"` | +| `uint` | 34 chars | `"0b00000000000000000000000001011010"` | +| `long` | 66 chars | (64 digits prefixed with `"0b"`) | +| `ulong` | 66 chars | (64 digits prefixed with `"0b"`) | + +```csharp +byte b = 0x5A; +string s = b.ToBinaryString(); // "0b01011010" + +int i = 90; +string t = i.ToBinaryString(); // "0b00000000000000000000000001011010" +``` + +[`BoolExtensions.ToBitChar()`](API/MrKWatkins.BinaryPrimitives/BoolExtensions/ToBitChar.md) +converts a boolean to the character `'1'` for `true` or `'0'` for `false`. This is useful when +building binary string representations character by character. + +```csharp +char c = true.ToBitChar(); // '1' +char d = false.ToBitChar(); // '0' +``` + +## Arithmetic Overflow Detection + +These methods detect whether an arithmetic operation on two values produced an overflow or a +half-carry/half-borrow. They are primarily useful when emulating processors that expose status +flags, such as Z80 or 6502 CPUs. + +### Addition + +`DidAdditionHalfCarry(left, right)` is called on the sum and returns `true` if the addition of +the low nibble (for `byte`) or low byte (for `ushort`) produced a carry into the next nibble or +byte. On `byte`, this is a carry from bit 3 to bit 4. On `ushort`, it is a carry from bit 11 to +bit 12. + +`DidAdditionOverflow(left, right)` is called on the sum and returns `true` if a signed addition +overflowed, meaning the result has the wrong sign relative to both operands. + +```csharp +byte left = 120; +byte right = 20; +byte sum = (byte)(left + right); // 140, wraps in signed interpretation + +bool overflow = sum.DidAdditionOverflow(left, right); // true (sign changed) +bool halfCarry = sum.DidAdditionHalfCarry(left, right); // depends on nibbles +``` + +### Subtraction + +`DidSubtractionHalfBorrow(left, right)` is called on the difference and returns `true` if a +borrow occurred from the upper nibble into the lower nibble (for `byte`), or from the upper byte +into the lower byte (for `ushort`). + +`DidSubtractionOverflow(left, right)` is called on the difference and returns `true` if a signed +subtraction overflowed. + +```csharp +byte left = 10; +byte right = 20; +byte difference = (byte)(left - right); // 246 (wraps) + +bool overflow = difference.DidSubtractionOverflow(left, right); // true +bool halfBorrow = difference.DidSubtractionHalfBorrow(left, right); // depends on nibbles +``` diff --git a/doc/docs/index.md b/doc/docs/index.md new file mode 100644 index 0000000..8343d49 --- /dev/null +++ b/doc/docs/index.md @@ -0,0 +1,36 @@ +# Home + +[![Build Status](https://github.com/MrKWatkins/BinaryPrimitives/actions/workflows/build.yml/badge.svg)](https://github.com/MrKWatkins/BinaryPrimitives/actions/workflows/build.yml) +[![NuGet Version](https://img.shields.io/nuget/v/MrKWatkins.BinaryPrimitives)](https://www.nuget.org/packages/MrKWatkins.BinaryPrimitives) +[![NuGet Downloads](https://img.shields.io/nuget/dt/MrKWatkins.BinaryPrimitives)](https://www.nuget.org/packages/MrKWatkins.BinaryPrimitives) + +> Extension methods on primitive types for various binary operations, such as reading and writing values from bytes and calculating parity. + +The name is based on the [BinaryPrimitives](https://learn.microsoft.com/en-us/dotnet/api/system.buffers.binary.binaryprimitives) class in the .NET Framework which provides similar methods. + +## Reading and Writing + +Extension methods on byte container types (`byte[]`, `Span`, `Stream`, and others) for +reading and writing multi-byte primitive values with configurable endianness. + +[Read more](reading-and-writing.md) + +## Bit Operations + +Extension methods on integer types (`byte`, `ushort`, `int`, `uint`, `long`, `ulong`) for +common bit-level operations: individual bit access, bit range extraction and replacement, nibble +manipulation, parity, overflow detection, and binary string formatting. + +[Read more](bit-operations.md) + +## Streams + +Two `Stream` implementations for byte-oriented work: `ReadOnlyListStream` wraps an +`IReadOnlyList` as a seekable read-only stream, and `PeekableStream` adds +peek-without-consuming to any readable stream. + +[Read more](streams.md) + +## Licencing + +Licensed under GPL v3.0. diff --git a/doc/docs/reading-and-writing.md b/doc/docs/reading-and-writing.md new file mode 100644 index 0000000..93a2342 --- /dev/null +++ b/doc/docs/reading-and-writing.md @@ -0,0 +1,165 @@ +# Reading and Writing Primitive Values + +The library provides extension methods for reading and writing multi-byte primitive values from a +variety of C# byte container types. All methods use a consistent naming convention and support +configurable byte ordering via the [`Endian`](API/MrKWatkins.BinaryPrimitives/Endian/index.md) +enum. + +## Supported Types + +The following primitive types can be read and written: + +| Method Suffix | .NET Type | Size | +| ------------- | --------- | ---- | +| `Int16` | `short` | 2 bytes | +| `UInt16` | `ushort` | 2 bytes | +| `UInt24` | `int` | 3 bytes (value is stored in the low 24 bits) | +| `Int32` | `int` | 4 bytes | +| `UInt32` | `uint` | 4 bytes | +| `Int64` | `long` | 8 bytes | +| `UInt64` | `ulong` | 8 bytes | + +## Endianness + +All multi-byte methods accept an optional `Endian` parameter. The default is `Endian.Little` +(least significant byte first). Pass `Endian.Big` to use big-endian (most significant byte first) +byte ordering. + +```csharp +byte[] bytes = [0x01, 0x02, 0x03, 0x04]; + +int littleEndian = bytes.GetInt32(0); // 0x04030201 +int bigEndian = bytes.GetInt32(0, Endian.Big); // 0x01020304 +``` + +## Byte Containers + +### byte[] and Span<byte> + +[`ByteArrayExtensions`](API/MrKWatkins.BinaryPrimitives/ByteArrayExtensions/index.md) and +[`ByteSpanExtensions`](API/MrKWatkins.BinaryPrimitives/ByteSpanExtensions/index.md) support +both reading (`Get*`) and writing (`Set*`). All methods take a zero-based index indicating +where in the array or span to read from or write to. + +```csharp +byte[] data = new byte[8]; + +data.SetInt32(0, 42); +data.SetUInt16(4, 1000, Endian.Big); + +int value = data.GetInt32(0); // 42 +ushort word = data.GetUInt16(4, Endian.Big); // 1000 +``` + +### ReadOnlySpan<byte> + +[`ByteReadOnlySpanExtensions`](API/MrKWatkins.BinaryPrimitives/ByteReadOnlySpanExtensions/index.md) +supports reading only. The index parameter specifies the start position. + +```csharp +ReadOnlySpan span = stackalloc byte[] { 0xFF, 0x00, 0x00, 0x00 }; +int value = span.GetInt32(0); // -1 (little-endian signed int32) +``` + +### IList<byte> and List<byte> + +[`ByteIListExtensions`](API/MrKWatkins.BinaryPrimitives/ByteIListExtensions/index.md) and +[`ByteListExtensions`](API/MrKWatkins.BinaryPrimitives/ByteListExtensions/index.md) support +both `Get*` and `Set*` methods. `List` has optimised overloads that avoid virtual +dispatch. + +```csharp +var list = new List { 0x0A, 0x00, 0x00, 0x00 }; +int value = list.GetInt32(0); // 10 +``` + +### IReadOnlyList<byte> + +[`ByteIReadOnlyListExtensions`](API/MrKWatkins.BinaryPrimitives/ByteIReadOnlyListExtensions/index.md) +supports reading only. It also provides a `CopyTo` method for copying the entire list into a +`Span`. + +```csharp +IReadOnlyList source = new List { 1, 2, 3, 4 }; +uint value = source.GetUInt32(0); + +Span destination = stackalloc byte[4]; +source.CopyTo(destination); +``` + +### ICollection<byte> + +[`ByteICollectionExtensions`](API/MrKWatkins.BinaryPrimitives/ByteICollectionExtensions/index.md) +provides `Add*` methods that append the bytes of a value to the collection in the specified byte +order. These are useful for building up byte sequences. + +```csharp +var buffer = new List(); +buffer.AddUInt16(0x0102); // appends 0x02, 0x01 (little-endian) +buffer.AddUInt16(0x0102, Endian.Big); // appends 0x01, 0x02 (big-endian) +buffer.AddInt32(-1); // appends 0xFF, 0xFF, 0xFF, 0xFF +``` + +### ReadOnlyMemory<byte> + +[`ReadOnlyMemoryExtensions`](API/MrKWatkins.BinaryPrimitives/ReadOnlyMemoryExtensions/index.md) +supports reading only, with the same `Get*` methods and index parameter as other read-only +containers. + +```csharp +ReadOnlyMemory memory = new byte[] { 0x78, 0x56, 0x34, 0x12 }; +int value = memory.GetInt32(0); // 0x12345678 +``` + +### Stream + +[`StreamExtensions`](API/MrKWatkins.BinaryPrimitives/StreamExtensions/index.md) provides +`Read*OrThrow` and `Write*` methods for sequential reading and writing. Read methods throw +`EndOfStreamException` if the stream ends before enough bytes are available. All methods have +asynchronous counterparts suffixed with `Async`. + +```csharp +using var stream = new MemoryStream(); +stream.WriteInt32(42); +stream.WriteUInt16(1000, Endian.Big); + +stream.Position = 0; +int a = stream.ReadInt32OrThrow(); +ushort b = stream.ReadUInt16OrThrow(Endian.Big); +``` + +Asynchronous versions follow the same pattern: + +```csharp +await stream.WriteInt32Async(42); +int value = await stream.ReadInt32OrThrowAsync(); +``` + +Additional helper methods are available for reading raw bytes: + +- `ReadByteOrThrow()` reads a single byte or throws `EndOfStreamException`. +- `ReadExactly(length)` reads a specific number of bytes or throws `EndOfStreamException`. +- `ReadAllBytes()` reads all remaining bytes into a new array. + +## Composing Values from Individual Bytes + +[`EndianExtensions`](API/MrKWatkins.BinaryPrimitives/EndianExtensions/index.md) provides +methods for composing values from individual bytes when working byte-by-byte: + +```csharp +byte b0 = 0x01, b1 = 0x02; +ushort value = Endian.Little.ToUInt16(b0, b1); // 0x0201 +ushort big = Endian.Big.ToUInt16(b0, b1); // 0x0102 +``` + +[`UInt16Extensions`](API/MrKWatkins.BinaryPrimitives/UInt16Extensions/index.md) provides +`ToBytes()` for decomposing a `ushort` into its most and least significant bytes, and +[`ByteByteTupleExtensions`](API/MrKWatkins.BinaryPrimitives/ByteByteTupleExtensions/index.md) +provides `ToUInt16()` for the reverse: + +```csharp +ushort value = 0x1234; +(byte msb, byte lsb) = value.ToBytes(); // msb = 0x12, lsb = 0x34 + +ushort reconstructed = (msb, lsb).ToUInt16(); // 0x1234 +``` diff --git a/doc/docs/streams.md b/doc/docs/streams.md new file mode 100644 index 0000000..963f15a --- /dev/null +++ b/doc/docs/streams.md @@ -0,0 +1,92 @@ +# Streams + +The library provides two `Stream` implementations for working with byte sequences, plus +extension methods on `Stream` for typed reading and writing. See the +[Reading and Writing](reading-and-writing.md) page for full details of the typed `Stream` +extension methods. + +## ReadOnlyListStream + +[`ReadOnlyListStream`](API/MrKWatkins.BinaryPrimitives/ReadOnlyListStream/index.md) wraps an +`IReadOnlyList` as a seekable, read-only `Stream`. It adapts any list of bytes for use +with APIs that consume a `Stream`, without copying the underlying data. + +```csharp +IReadOnlyList bytes = new List { 0x01, 0x00, 0x00, 0x00 }; +using var stream = new ReadOnlyListStream(bytes); + +int value = stream.ReadInt32OrThrow(); // 1 +``` + +The stream supports seeking via `Seek` and the `Position` property. `CanRead` and `CanSeek` +return `true` while the stream is open. `CanWrite` always returns `false`; any attempt to +write or resize the stream throws `NotSupportedException`. + +## PeekableStream + +[`PeekableStream`](API/MrKWatkins.BinaryPrimitives/PeekableStream/index.md) wraps any readable +`Stream` and adds the ability to inspect the next byte without consuming it. This is useful for +parsers that need to look ahead one byte before deciding how to proceed. + +```csharp +using var inner = new MemoryStream(new byte[] { 0x01, 0x02, 0x03 }); +using var stream = new PeekableStream(inner); + +int next = stream.Peek(); // 1, position stays at 0 +int read = stream.ReadByte(); // 1, position advances to 1 +``` + +`Peek()` returns the next byte as an integer in the range 0..255, or -1 if the end of the +stream has been reached. Calling `Peek()` multiple times without reading returns the same byte +each time. Any read or seek operation that moves the position will reset the peeked value. + +The `EndOfStream` property is a convenience that calls `Peek()` and returns `true` if the +result is -1: + +```csharp +while (!stream.EndOfStream) +{ + byte b = stream.ReadByteOrThrow(); + // process b +} +``` + +### Construction and Ownership + +The constructor accepts a `leaveOpen` parameter (default `true`) that controls whether the +underlying stream is disposed when the `PeekableStream` is disposed. Set it to `false` to +transfer ownership: + +```csharp +// The underlying stream will be disposed when the PeekableStream is disposed. +using var stream = new PeekableStream(inner, leaveOpen: false); +``` + +The stream must be readable; passing a non-readable stream throws `ArgumentException`. + +`CanRead` and `CanSeek` reflect the underlying stream's capabilities while the wrapper is +open. `CanWrite` always returns `false`. + +## StreamExtensions + +[`StreamExtensions`](API/MrKWatkins.BinaryPrimitives/StreamExtensions/index.md) adds typed +read and write methods directly to `Stream`, covering all the multi-byte primitive types +supported by the library. See +[Reading and Writing: Stream](reading-and-writing.md#stream) for full details and examples. + +The methods available are: + +**Reading (synchronous):** `ReadByteOrThrow`, `ReadExactly`, `ReadAllBytes`, +`ReadInt16OrThrow`, `ReadUInt16OrThrow`, `ReadUInt24OrThrow`, `ReadInt32OrThrow`, +`ReadUInt32OrThrow`, `ReadInt64OrThrow`, `ReadUInt64OrThrow`. + +**Writing (synchronous):** `WriteInt16`, `WriteUInt16`, `WriteUInt24`, `WriteInt32`, +`WriteUInt32`, `WriteInt64`, `WriteUInt64`. + +**Reading (asynchronous):** `ReadByteOrThrowAsync`, `ReadExactlyAsync`, `ReadAllBytesAsync`, +`ReadInt16OrThrowAsync`, `ReadUInt16OrThrowAsync`, `ReadUInt24OrThrowAsync`, +`ReadInt32OrThrowAsync`, `ReadUInt32OrThrowAsync`, `ReadInt64OrThrowAsync`, +`ReadUInt64OrThrowAsync`. + +**Writing (asynchronous):** `WriteInt16Async`, `WriteUInt16Async`, `WriteUInt24Async`, +`WriteInt32Async`, `WriteUInt32Async`, `WriteInt64Async`, `WriteUInt64Async`. diff --git a/doc/docs/stylesheets/overrides.css b/doc/docs/stylesheets/overrides.css new file mode 100644 index 0000000..f025d58 --- /dev/null +++ b/doc/docs/stylesheets/overrides.css @@ -0,0 +1,3 @@ +.md-grid { + max-width: 100%; +} \ No newline at end of file diff --git a/doc/global_state.py b/doc/global_state.py new file mode 100644 index 0000000..42aefe1 --- /dev/null +++ b/doc/global_state.py @@ -0,0 +1,2 @@ +# To share state between macros and hooks. +global_state = {"nav_tree": None} \ No newline at end of file diff --git a/doc/hooks.py b/doc/hooks.py new file mode 100644 index 0000000..d843cd6 --- /dev/null +++ b/doc/hooks.py @@ -0,0 +1,9 @@ +import logging + +from global_state import global_state + +logger = logging.getLogger("mkdocs") + +def on_nav(nav, config, files): + global_state["nav_tree"] = nav.items + logger.log(logging.INFO, "Captured navigation tree.") \ No newline at end of file diff --git a/doc/main.py b/doc/main.py new file mode 100644 index 0000000..8056441 --- /dev/null +++ b/doc/main.py @@ -0,0 +1,70 @@ +import logging +import csv +import os + +from global_state import global_state + +logger = logging.getLogger("mkdocs") + + +def define_env(env): + @env.macro + def global_nav(): + logger.log(logging.INFO, "Rendering global navigation tree...") + nav_tree = global_state.get("nav_tree") + if not nav_tree: + logger.log(logging.ERROR, "No navigation tree in global state.") + return "Unable to render navigation tree." + + result = "\n".join(render(env.conf, nav_tree)) + logger.log(logging.INFO, result) + return result + + @env.macro + def vocabulary_table(category): + filename = f"{category}.csv" + path = os.path.join(env.conf['docs_dir'], 'assets', 'vocabulary', filename) + if not os.path.exists(path): + logger.log(logging.ERROR, f"Vocabulary file not found: {path}") + return f"**Error: Vocabulary file '{filename}' not found.**" + + try: + with open(path, mode='r', encoding='utf-8') as f: + reader = csv.DictReader(f) + rows = list(reader) + + if not rows: + return "_No words available._" + + headers = reader.fieldnames + header_row = "| " + " | ".join(headers) + " |" + separator_row = "| " + " | ".join(["---"] * len(headers)) + " |" + + md_rows = [] + for row in rows: + md_rows.append("| " + " | ".join(row.get(h, "") for h in headers) + " |") + + return "\n".join([header_row, separator_row] + md_rows) + except Exception as e: + logger.log(logging.ERROR, f"Error rendering vocabulary table: {e}") + return f"**Error rendering vocabulary table: {e}**" + + +def render(conf, items, indent=0): + lines = [] + for item in items: + if item.is_section: + lines.append(" " * indent + f"- {item.title}") + + # Recursively render children. + if item.children: + lines.extend(render(conf, item.children, indent + 1)) + elif item.is_page: + if not item.is_homepage: + # Read the source for the page so we can get the title. + item.read_source(conf) + lines.append(" " * indent + f"- [{item.title}]({item.file.src_uri})") + else: + raise Exception(f"Unexpected item type: {type(item)}") + + return lines \ No newline at end of file diff --git a/doc/mkdocs.yml b/doc/mkdocs.yml new file mode 100644 index 0000000..7e0dba4 --- /dev/null +++ b/doc/mkdocs.yml @@ -0,0 +1,39 @@ +site_name: BinaryPrimitives +theme: + name: material + features: + - navigation.sections + palette: + - media: "(prefers-color-scheme)" + primary: green + accent: green + toggle: + icon: material/brightness-auto + name: Switch to light mode + + - media: "(prefers-color-scheme: light)" + primary: green + accent: green + scheme: default + toggle: + icon: material/brightness-7 + name: Switch to dark mode + + - media: "(prefers-color-scheme: dark)" + primary: green + accent: green + scheme: slate + toggle: + icon: material/brightness-4 + name: Switch to system preference +plugins: + - search + - macros + - mkdocs-simple-hooks: + hooks: + on_nav: "hooks:on_nav" +extra_css: + - stylesheets/overrides.css +markdown_extensions: + - attr_list + diff --git a/doc/pyproject.toml b/doc/pyproject.toml new file mode 100644 index 0000000..5932847 --- /dev/null +++ b/doc/pyproject.toml @@ -0,0 +1,11 @@ +[project] +name = "russian" +version = "0.1.0" +description = "Russian notes" +requires-python = ">=3.13" +dependencies = [ + "mkdocs>=1.6.1", + "mkdocs-macros-plugin>=1.5.0", + "mkdocs-material>=9.7.1", + "mkdocs-simple-hooks>=0.1.5", +] diff --git a/doc/requirements.txt b/doc/requirements.txt new file mode 100644 index 0000000..b016746 --- /dev/null +++ b/doc/requirements.txt @@ -0,0 +1,4 @@ +mkdocs>=1.6.1 +mkdocs-macros-plugin>=1.3.9 +mkdocs-material>=9.6.19 +mkdocs-simple-hooks>=0.1.5 diff --git a/doc/uv.lock b/doc/uv.lock new file mode 100644 index 0000000..501508c --- /dev/null +++ b/doc/uv.lock @@ -0,0 +1,470 @@ +version = 1 +revision = 3 +requires-python = ">=3.13" + +[[package]] +name = "babel" +version = "2.17.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/7d/6b/d52e42361e1aa00709585ecc30b3f9684b3ab62530771402248b1b1d6240/babel-2.17.0.tar.gz", hash = "sha256:0c54cffb19f690cdcc52a3b50bcbf71e07a808d1c80d549f2459b9d2cf0afb9d", size = 9951852, upload-time = "2025-02-01T15:17:41.026Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b7/b8/3fe70c75fe32afc4bb507f75563d39bc5642255d1d94f1f23604725780bf/babel-2.17.0-py3-none-any.whl", hash = "sha256:4d0b53093fdfb4b21c92b5213dba5a1b23885afa8383709427046b21c366e5f2", size = 10182537, upload-time = "2025-02-01T15:17:37.39Z" }, +] + +[[package]] +name = "backrefs" +version = "5.9" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/eb/a7/312f673df6a79003279e1f55619abbe7daebbb87c17c976ddc0345c04c7b/backrefs-5.9.tar.gz", hash = "sha256:808548cb708d66b82ee231f962cb36faaf4f2baab032f2fbb783e9c2fdddaa59", size = 5765857, upload-time = "2025-06-22T19:34:13.97Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/19/4d/798dc1f30468134906575156c089c492cf79b5a5fd373f07fe26c4d046bf/backrefs-5.9-py310-none-any.whl", hash = "sha256:db8e8ba0e9de81fcd635f440deab5ae5f2591b54ac1ebe0550a2ca063488cd9f", size = 380267, upload-time = "2025-06-22T19:34:05.252Z" }, + { url = "https://files.pythonhosted.org/packages/55/07/f0b3375bf0d06014e9787797e6b7cc02b38ac9ff9726ccfe834d94e9991e/backrefs-5.9-py311-none-any.whl", hash = "sha256:6907635edebbe9b2dc3de3a2befff44d74f30a4562adbb8b36f21252ea19c5cf", size = 392072, upload-time = "2025-06-22T19:34:06.743Z" }, + { url = "https://files.pythonhosted.org/packages/9d/12/4f345407259dd60a0997107758ba3f221cf89a9b5a0f8ed5b961aef97253/backrefs-5.9-py312-none-any.whl", hash = "sha256:7fdf9771f63e6028d7fee7e0c497c81abda597ea45d6b8f89e8ad76994f5befa", size = 397947, upload-time = "2025-06-22T19:34:08.172Z" }, + { url = "https://files.pythonhosted.org/packages/10/bf/fa31834dc27a7f05e5290eae47c82690edc3a7b37d58f7fb35a1bdbf355b/backrefs-5.9-py313-none-any.whl", hash = "sha256:cc37b19fa219e93ff825ed1fed8879e47b4d89aa7a1884860e2db64ccd7c676b", size = 399843, upload-time = "2025-06-22T19:34:09.68Z" }, + { url = "https://files.pythonhosted.org/packages/fc/24/b29af34b2c9c41645a9f4ff117bae860291780d73880f449e0b5d948c070/backrefs-5.9-py314-none-any.whl", hash = "sha256:df5e169836cc8acb5e440ebae9aad4bf9d15e226d3bad049cf3f6a5c20cc8dc9", size = 411762, upload-time = "2025-06-22T19:34:11.037Z" }, + { url = "https://files.pythonhosted.org/packages/41/ff/392bff89415399a979be4a65357a41d92729ae8580a66073d8ec8d810f98/backrefs-5.9-py39-none-any.whl", hash = "sha256:f48ee18f6252b8f5777a22a00a09a85de0ca931658f1dd96d4406a34f3748c60", size = 380265, upload-time = "2025-06-22T19:34:12.405Z" }, +] + +[[package]] +name = "certifi" +version = "2025.8.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/dc/67/960ebe6bf230a96cda2e0abcf73af550ec4f090005363542f0765df162e0/certifi-2025.8.3.tar.gz", hash = "sha256:e564105f78ded564e3ae7c923924435e1daa7463faeab5bb932bc53ffae63407", size = 162386, upload-time = "2025-08-03T03:07:47.08Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e5/48/1549795ba7742c948d2ad169c1c8cdbae65bc450d6cd753d124b17c8cd32/certifi-2025.8.3-py3-none-any.whl", hash = "sha256:f6c12493cfb1b06ba2ff328595af9350c65d6644968e5d3a2ffd78699af217a5", size = 161216, upload-time = "2025-08-03T03:07:45.777Z" }, +] + +[[package]] +name = "charset-normalizer" +version = "3.4.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/83/2d/5fd176ceb9b2fc619e63405525573493ca23441330fcdaee6bef9460e924/charset_normalizer-3.4.3.tar.gz", hash = "sha256:6fce4b8500244f6fcb71465d4a4930d132ba9ab8e71a7859e6a5d59851068d14", size = 122371, upload-time = "2025-08-09T07:57:28.46Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/65/ca/2135ac97709b400c7654b4b764daf5c5567c2da45a30cdd20f9eefe2d658/charset_normalizer-3.4.3-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:14c2a87c65b351109f6abfc424cab3927b3bdece6f706e4d12faaf3d52ee5efe", size = 205326, upload-time = "2025-08-09T07:56:24.721Z" }, + { url = "https://files.pythonhosted.org/packages/71/11/98a04c3c97dd34e49c7d247083af03645ca3730809a5509443f3c37f7c99/charset_normalizer-3.4.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:41d1fc408ff5fdfb910200ec0e74abc40387bccb3252f3f27c0676731df2b2c8", size = 146008, upload-time = "2025-08-09T07:56:26.004Z" }, + { url = "https://files.pythonhosted.org/packages/60/f5/4659a4cb3c4ec146bec80c32d8bb16033752574c20b1252ee842a95d1a1e/charset_normalizer-3.4.3-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:1bb60174149316da1c35fa5233681f7c0f9f514509b8e399ab70fea5f17e45c9", size = 159196, upload-time = "2025-08-09T07:56:27.25Z" }, + { url = "https://files.pythonhosted.org/packages/86/9e/f552f7a00611f168b9a5865a1414179b2c6de8235a4fa40189f6f79a1753/charset_normalizer-3.4.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:30d006f98569de3459c2fc1f2acde170b7b2bd265dc1943e87e1a4efe1b67c31", size = 156819, upload-time = "2025-08-09T07:56:28.515Z" }, + { url = "https://files.pythonhosted.org/packages/7e/95/42aa2156235cbc8fa61208aded06ef46111c4d3f0de233107b3f38631803/charset_normalizer-3.4.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:416175faf02e4b0810f1f38bcb54682878a4af94059a1cd63b8747244420801f", size = 151350, upload-time = "2025-08-09T07:56:29.716Z" }, + { url = "https://files.pythonhosted.org/packages/c2/a9/3865b02c56f300a6f94fc631ef54f0a8a29da74fb45a773dfd3dcd380af7/charset_normalizer-3.4.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:6aab0f181c486f973bc7262a97f5aca3ee7e1437011ef0c2ec04b5a11d16c927", size = 148644, upload-time = "2025-08-09T07:56:30.984Z" }, + { url = "https://files.pythonhosted.org/packages/77/d9/cbcf1a2a5c7d7856f11e7ac2d782aec12bdfea60d104e60e0aa1c97849dc/charset_normalizer-3.4.3-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:fdabf8315679312cfa71302f9bd509ded4f2f263fb5b765cf1433b39106c3cc9", size = 160468, upload-time = "2025-08-09T07:56:32.252Z" }, + { url = "https://files.pythonhosted.org/packages/f6/42/6f45efee8697b89fda4d50580f292b8f7f9306cb2971d4b53f8914e4d890/charset_normalizer-3.4.3-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:bd28b817ea8c70215401f657edef3a8aa83c29d447fb0b622c35403780ba11d5", size = 158187, upload-time = "2025-08-09T07:56:33.481Z" }, + { url = "https://files.pythonhosted.org/packages/70/99/f1c3bdcfaa9c45b3ce96f70b14f070411366fa19549c1d4832c935d8e2c3/charset_normalizer-3.4.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:18343b2d246dc6761a249ba1fb13f9ee9a2bcd95decc767319506056ea4ad4dc", size = 152699, upload-time = "2025-08-09T07:56:34.739Z" }, + { url = "https://files.pythonhosted.org/packages/a3/ad/b0081f2f99a4b194bcbb1934ef3b12aa4d9702ced80a37026b7607c72e58/charset_normalizer-3.4.3-cp313-cp313-win32.whl", hash = "sha256:6fb70de56f1859a3f71261cbe41005f56a7842cc348d3aeb26237560bfa5e0ce", size = 99580, upload-time = "2025-08-09T07:56:35.981Z" }, + { url = "https://files.pythonhosted.org/packages/9a/8f/ae790790c7b64f925e5c953b924aaa42a243fb778fed9e41f147b2a5715a/charset_normalizer-3.4.3-cp313-cp313-win_amd64.whl", hash = "sha256:cf1ebb7d78e1ad8ec2a8c4732c7be2e736f6e5123a4146c5b89c9d1f585f8cef", size = 107366, upload-time = "2025-08-09T07:56:37.339Z" }, + { url = "https://files.pythonhosted.org/packages/8e/91/b5a06ad970ddc7a0e513112d40113e834638f4ca1120eb727a249fb2715e/charset_normalizer-3.4.3-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:3cd35b7e8aedeb9e34c41385fda4f73ba609e561faedfae0a9e75e44ac558a15", size = 204342, upload-time = "2025-08-09T07:56:38.687Z" }, + { url = "https://files.pythonhosted.org/packages/ce/ec/1edc30a377f0a02689342f214455c3f6c2fbedd896a1d2f856c002fc3062/charset_normalizer-3.4.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b89bc04de1d83006373429975f8ef9e7932534b8cc9ca582e4db7d20d91816db", size = 145995, upload-time = "2025-08-09T07:56:40.048Z" }, + { url = "https://files.pythonhosted.org/packages/17/e5/5e67ab85e6d22b04641acb5399c8684f4d37caf7558a53859f0283a650e9/charset_normalizer-3.4.3-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:2001a39612b241dae17b4687898843f254f8748b796a2e16f1051a17078d991d", size = 158640, upload-time = "2025-08-09T07:56:41.311Z" }, + { url = "https://files.pythonhosted.org/packages/f1/e5/38421987f6c697ee3722981289d554957c4be652f963d71c5e46a262e135/charset_normalizer-3.4.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:8dcfc373f888e4fb39a7bc57e93e3b845e7f462dacc008d9749568b1c4ece096", size = 156636, upload-time = "2025-08-09T07:56:43.195Z" }, + { url = "https://files.pythonhosted.org/packages/a0/e4/5a075de8daa3ec0745a9a3b54467e0c2967daaaf2cec04c845f73493e9a1/charset_normalizer-3.4.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:18b97b8404387b96cdbd30ad660f6407799126d26a39ca65729162fd810a99aa", size = 150939, upload-time = "2025-08-09T07:56:44.819Z" }, + { url = "https://files.pythonhosted.org/packages/02/f7/3611b32318b30974131db62b4043f335861d4d9b49adc6d57c1149cc49d4/charset_normalizer-3.4.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:ccf600859c183d70eb47e05a44cd80a4ce77394d1ac0f79dbd2dd90a69a3a049", size = 148580, upload-time = "2025-08-09T07:56:46.684Z" }, + { url = "https://files.pythonhosted.org/packages/7e/61/19b36f4bd67f2793ab6a99b979b4e4f3d8fc754cbdffb805335df4337126/charset_normalizer-3.4.3-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:53cd68b185d98dde4ad8990e56a58dea83a4162161b1ea9272e5c9182ce415e0", size = 159870, upload-time = "2025-08-09T07:56:47.941Z" }, + { url = "https://files.pythonhosted.org/packages/06/57/84722eefdd338c04cf3030ada66889298eaedf3e7a30a624201e0cbe424a/charset_normalizer-3.4.3-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:30a96e1e1f865f78b030d65241c1ee850cdf422d869e9028e2fc1d5e4db73b92", size = 157797, upload-time = "2025-08-09T07:56:49.756Z" }, + { url = "https://files.pythonhosted.org/packages/72/2a/aff5dd112b2f14bcc3462c312dce5445806bfc8ab3a7328555da95330e4b/charset_normalizer-3.4.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:d716a916938e03231e86e43782ca7878fb602a125a91e7acb8b5112e2e96ac16", size = 152224, upload-time = "2025-08-09T07:56:51.369Z" }, + { url = "https://files.pythonhosted.org/packages/b7/8c/9839225320046ed279c6e839d51f028342eb77c91c89b8ef2549f951f3ec/charset_normalizer-3.4.3-cp314-cp314-win32.whl", hash = "sha256:c6dbd0ccdda3a2ba7c2ecd9d77b37f3b5831687d8dc1b6ca5f56a4880cc7b7ce", size = 100086, upload-time = "2025-08-09T07:56:52.722Z" }, + { url = "https://files.pythonhosted.org/packages/ee/7a/36fbcf646e41f710ce0a563c1c9a343c6edf9be80786edeb15b6f62e17db/charset_normalizer-3.4.3-cp314-cp314-win_amd64.whl", hash = "sha256:73dc19b562516fc9bcf6e5d6e596df0b4eb98d87e4f79f3ae71840e6ed21361c", size = 107400, upload-time = "2025-08-09T07:56:55.172Z" }, + { url = "https://files.pythonhosted.org/packages/8a/1f/f041989e93b001bc4e44bb1669ccdcf54d3f00e628229a85b08d330615c5/charset_normalizer-3.4.3-py3-none-any.whl", hash = "sha256:ce571ab16d890d23b5c278547ba694193a45011ff86a9162a71307ed9f86759a", size = 53175, upload-time = "2025-08-09T07:57:26.864Z" }, +] + +[[package]] +name = "click" +version = "8.2.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/60/6c/8ca2efa64cf75a977a0d7fac081354553ebe483345c734fb6b6515d96bbc/click-8.2.1.tar.gz", hash = "sha256:27c491cc05d968d271d5a1db13e3b5a184636d9d930f148c50b038f0d0646202", size = 286342, upload-time = "2025-05-20T23:19:49.832Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/85/32/10bb5764d90a8eee674e9dc6f4db6a0ab47c8c4d0d83c27f7c39ac415a4d/click-8.2.1-py3-none-any.whl", hash = "sha256:61a3265b914e850b85317d0b3109c7f8cd35a670f963866005d6ef1d5175a12b", size = 102215, upload-time = "2025-05-20T23:19:47.796Z" }, +] + +[[package]] +name = "colorama" +version = "0.4.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" }, +] + +[[package]] +name = "ghp-import" +version = "2.1.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "python-dateutil" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/d9/29/d40217cbe2f6b1359e00c6c307bb3fc876ba74068cbab3dde77f03ca0dc4/ghp-import-2.1.0.tar.gz", hash = "sha256:9c535c4c61193c2df8871222567d7fd7e5014d835f97dc7b7439069e2413d343", size = 10943, upload-time = "2022-05-02T15:47:16.11Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f7/ec/67fbef5d497f86283db54c22eec6f6140243aae73265799baaaa19cd17fb/ghp_import-2.1.0-py3-none-any.whl", hash = "sha256:8337dd7b50877f163d4c0289bc1f1c7f127550241988d568c1db512c4324a619", size = 11034, upload-time = "2022-05-02T15:47:14.552Z" }, +] + +[[package]] +name = "hjson" +version = "3.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/82/e5/0b56d723a76ca67abadbf7fb71609fb0ea7e6926e94fcca6c65a85b36a0e/hjson-3.1.0.tar.gz", hash = "sha256:55af475a27cf83a7969c808399d7bccdec8fb836a07ddbd574587593b9cdcf75", size = 40541, upload-time = "2022-08-13T02:53:01.919Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1f/7f/13cd798d180af4bf4c0ceddeefba2b864a63c71645abc0308b768d67bb81/hjson-3.1.0-py3-none-any.whl", hash = "sha256:65713cdcf13214fb554eb8b4ef803419733f4f5e551047c9b711098ab7186b89", size = 54018, upload-time = "2022-08-13T02:52:59.899Z" }, +] + +[[package]] +name = "idna" +version = "3.10" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f1/70/7703c29685631f5a7590aa73f1f1d3fa9a380e654b86af429e0934a32f7d/idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9", size = 190490, upload-time = "2024-09-15T18:07:39.745Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", size = 70442, upload-time = "2024-09-15T18:07:37.964Z" }, +] + +[[package]] +name = "jinja2" +version = "3.1.6" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "markupsafe" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/df/bf/f7da0350254c0ed7c72f3e33cef02e048281fec7ecec5f032d4aac52226b/jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d", size = 245115, upload-time = "2025-03-05T20:05:02.478Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67", size = 134899, upload-time = "2025-03-05T20:05:00.369Z" }, +] + +[[package]] +name = "markdown" +version = "3.9" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/8d/37/02347f6d6d8279247a5837082ebc26fc0d5aaeaf75aa013fcbb433c777ab/markdown-3.9.tar.gz", hash = "sha256:d2900fe1782bd33bdbbd56859defef70c2e78fc46668f8eb9df3128138f2cb6a", size = 364585, upload-time = "2025-09-04T20:25:22.885Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/70/ae/44c4a6a4cbb496d93c6257954260fe3a6e91b7bed2240e5dad2a717f5111/markdown-3.9-py3-none-any.whl", hash = "sha256:9f4d91ed810864ea88a6f32c07ba8bee1346c0cc1f6b1f9f6c822f2a9667d280", size = 107441, upload-time = "2025-09-04T20:25:21.784Z" }, +] + +[[package]] +name = "markupsafe" +version = "3.0.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b2/97/5d42485e71dfc078108a86d6de8fa46db44a1a9295e89c5d6d4a06e23a62/markupsafe-3.0.2.tar.gz", hash = "sha256:ee55d3edf80167e48ea11a923c7386f4669df67d7994554387f84e7d8b0a2bf0", size = 20537, upload-time = "2024-10-18T15:21:54.129Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/83/0e/67eb10a7ecc77a0c2bbe2b0235765b98d164d81600746914bebada795e97/MarkupSafe-3.0.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ba9527cdd4c926ed0760bc301f6728ef34d841f405abf9d4f959c478421e4efd", size = 14274, upload-time = "2024-10-18T15:21:24.577Z" }, + { url = "https://files.pythonhosted.org/packages/2b/6d/9409f3684d3335375d04e5f05744dfe7e9f120062c9857df4ab490a1031a/MarkupSafe-3.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f8b3d067f2e40fe93e1ccdd6b2e1d16c43140e76f02fb1319a05cf2b79d99430", size = 12352, upload-time = "2024-10-18T15:21:25.382Z" }, + { url = "https://files.pythonhosted.org/packages/d2/f5/6eadfcd3885ea85fe2a7c128315cc1bb7241e1987443d78c8fe712d03091/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:569511d3b58c8791ab4c2e1285575265991e6d8f8700c7be0e88f86cb0672094", size = 24122, upload-time = "2024-10-18T15:21:26.199Z" }, + { url = "https://files.pythonhosted.org/packages/0c/91/96cf928db8236f1bfab6ce15ad070dfdd02ed88261c2afafd4b43575e9e9/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15ab75ef81add55874e7ab7055e9c397312385bd9ced94920f2802310c930396", size = 23085, upload-time = "2024-10-18T15:21:27.029Z" }, + { url = "https://files.pythonhosted.org/packages/c2/cf/c9d56af24d56ea04daae7ac0940232d31d5a8354f2b457c6d856b2057d69/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f3818cb119498c0678015754eba762e0d61e5b52d34c8b13d770f0719f7b1d79", size = 22978, upload-time = "2024-10-18T15:21:27.846Z" }, + { url = "https://files.pythonhosted.org/packages/2a/9f/8619835cd6a711d6272d62abb78c033bda638fdc54c4e7f4272cf1c0962b/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:cdb82a876c47801bb54a690c5ae105a46b392ac6099881cdfb9f6e95e4014c6a", size = 24208, upload-time = "2024-10-18T15:21:28.744Z" }, + { url = "https://files.pythonhosted.org/packages/f9/bf/176950a1792b2cd2102b8ffeb5133e1ed984547b75db47c25a67d3359f77/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:cabc348d87e913db6ab4aa100f01b08f481097838bdddf7c7a84b7575b7309ca", size = 23357, upload-time = "2024-10-18T15:21:29.545Z" }, + { url = "https://files.pythonhosted.org/packages/ce/4f/9a02c1d335caabe5c4efb90e1b6e8ee944aa245c1aaaab8e8a618987d816/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:444dcda765c8a838eaae23112db52f1efaf750daddb2d9ca300bcae1039adc5c", size = 23344, upload-time = "2024-10-18T15:21:30.366Z" }, + { url = "https://files.pythonhosted.org/packages/ee/55/c271b57db36f748f0e04a759ace9f8f759ccf22b4960c270c78a394f58be/MarkupSafe-3.0.2-cp313-cp313-win32.whl", hash = "sha256:bcf3e58998965654fdaff38e58584d8937aa3096ab5354d493c77d1fdd66d7a1", size = 15101, upload-time = "2024-10-18T15:21:31.207Z" }, + { url = "https://files.pythonhosted.org/packages/29/88/07df22d2dd4df40aba9f3e402e6dc1b8ee86297dddbad4872bd5e7b0094f/MarkupSafe-3.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:e6a2a455bd412959b57a172ce6328d2dd1f01cb2135efda2e4576e8a23fa3b0f", size = 15603, upload-time = "2024-10-18T15:21:32.032Z" }, + { url = "https://files.pythonhosted.org/packages/62/6a/8b89d24db2d32d433dffcd6a8779159da109842434f1dd2f6e71f32f738c/MarkupSafe-3.0.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:b5a6b3ada725cea8a5e634536b1b01c30bcdcd7f9c6fff4151548d5bf6b3a36c", size = 14510, upload-time = "2024-10-18T15:21:33.625Z" }, + { url = "https://files.pythonhosted.org/packages/7a/06/a10f955f70a2e5a9bf78d11a161029d278eeacbd35ef806c3fd17b13060d/MarkupSafe-3.0.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:a904af0a6162c73e3edcb969eeeb53a63ceeb5d8cf642fade7d39e7963a22ddb", size = 12486, upload-time = "2024-10-18T15:21:34.611Z" }, + { url = "https://files.pythonhosted.org/packages/34/cf/65d4a571869a1a9078198ca28f39fba5fbb910f952f9dbc5220afff9f5e6/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4aa4e5faecf353ed117801a068ebab7b7e09ffb6e1d5e412dc852e0da018126c", size = 25480, upload-time = "2024-10-18T15:21:35.398Z" }, + { url = "https://files.pythonhosted.org/packages/0c/e3/90e9651924c430b885468b56b3d597cabf6d72be4b24a0acd1fa0e12af67/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0ef13eaeee5b615fb07c9a7dadb38eac06a0608b41570d8ade51c56539e509d", size = 23914, upload-time = "2024-10-18T15:21:36.231Z" }, + { url = "https://files.pythonhosted.org/packages/66/8c/6c7cf61f95d63bb866db39085150df1f2a5bd3335298f14a66b48e92659c/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d16a81a06776313e817c951135cf7340a3e91e8c1ff2fac444cfd75fffa04afe", size = 23796, upload-time = "2024-10-18T15:21:37.073Z" }, + { url = "https://files.pythonhosted.org/packages/bb/35/cbe9238ec3f47ac9a7c8b3df7a808e7cb50fe149dc7039f5f454b3fba218/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:6381026f158fdb7c72a168278597a5e3a5222e83ea18f543112b2662a9b699c5", size = 25473, upload-time = "2024-10-18T15:21:37.932Z" }, + { url = "https://files.pythonhosted.org/packages/e6/32/7621a4382488aa283cc05e8984a9c219abad3bca087be9ec77e89939ded9/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:3d79d162e7be8f996986c064d1c7c817f6df3a77fe3d6859f6f9e7be4b8c213a", size = 24114, upload-time = "2024-10-18T15:21:39.799Z" }, + { url = "https://files.pythonhosted.org/packages/0d/80/0985960e4b89922cb5a0bac0ed39c5b96cbc1a536a99f30e8c220a996ed9/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:131a3c7689c85f5ad20f9f6fb1b866f402c445b220c19fe4308c0b147ccd2ad9", size = 24098, upload-time = "2024-10-18T15:21:40.813Z" }, + { url = "https://files.pythonhosted.org/packages/82/78/fedb03c7d5380df2427038ec8d973587e90561b2d90cd472ce9254cf348b/MarkupSafe-3.0.2-cp313-cp313t-win32.whl", hash = "sha256:ba8062ed2cf21c07a9e295d5b8a2a5ce678b913b45fdf68c32d95d6c1291e0b6", size = 15208, upload-time = "2024-10-18T15:21:41.814Z" }, + { url = "https://files.pythonhosted.org/packages/4f/65/6079a46068dfceaeabb5dcad6d674f5f5c61a6fa5673746f42a9f4c233b3/MarkupSafe-3.0.2-cp313-cp313t-win_amd64.whl", hash = "sha256:e444a31f8db13eb18ada366ab3cf45fd4b31e4db1236a4448f68778c1d1a5a2f", size = 15739, upload-time = "2024-10-18T15:21:42.784Z" }, +] + +[[package]] +name = "mergedeep" +version = "1.3.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/3a/41/580bb4006e3ed0361b8151a01d324fb03f420815446c7def45d02f74c270/mergedeep-1.3.4.tar.gz", hash = "sha256:0096d52e9dad9939c3d975a774666af186eda617e6ca84df4c94dec30004f2a8", size = 4661, upload-time = "2021-02-05T18:55:30.623Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2c/19/04f9b178c2d8a15b076c8b5140708fa6ffc5601fb6f1e975537072df5b2a/mergedeep-1.3.4-py3-none-any.whl", hash = "sha256:70775750742b25c0d8f36c55aed03d24c3384d17c951b3175d898bd778ef0307", size = 6354, upload-time = "2021-02-05T18:55:29.583Z" }, +] + +[[package]] +name = "mkdocs" +version = "1.6.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "click" }, + { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "ghp-import" }, + { name = "jinja2" }, + { name = "markdown" }, + { name = "markupsafe" }, + { name = "mergedeep" }, + { name = "mkdocs-get-deps" }, + { name = "packaging" }, + { name = "pathspec" }, + { name = "pyyaml" }, + { name = "pyyaml-env-tag" }, + { name = "watchdog" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/bc/c6/bbd4f061bd16b378247f12953ffcb04786a618ce5e904b8c5a01a0309061/mkdocs-1.6.1.tar.gz", hash = "sha256:7b432f01d928c084353ab39c57282f29f92136665bdd6abf7c1ec8d822ef86f2", size = 3889159, upload-time = "2024-08-30T12:24:06.899Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/22/5b/dbc6a8cddc9cfa9c4971d59fb12bb8d42e161b7e7f8cc89e49137c5b279c/mkdocs-1.6.1-py3-none-any.whl", hash = "sha256:db91759624d1647f3f34aa0c3f327dd2601beae39a366d6e064c03468d35c20e", size = 3864451, upload-time = "2024-08-30T12:24:05.054Z" }, +] + +[[package]] +name = "mkdocs-get-deps" +version = "0.2.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "mergedeep" }, + { name = "platformdirs" }, + { name = "pyyaml" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/98/f5/ed29cd50067784976f25ed0ed6fcd3c2ce9eb90650aa3b2796ddf7b6870b/mkdocs_get_deps-0.2.0.tar.gz", hash = "sha256:162b3d129c7fad9b19abfdcb9c1458a651628e4b1dea628ac68790fb3061c60c", size = 10239, upload-time = "2023-11-20T17:51:09.981Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9f/d4/029f984e8d3f3b6b726bd33cafc473b75e9e44c0f7e80a5b29abc466bdea/mkdocs_get_deps-0.2.0-py3-none-any.whl", hash = "sha256:2bf11d0b133e77a0dd036abeeb06dec8775e46efa526dc70667d8863eefc6134", size = 9521, upload-time = "2023-11-20T17:51:08.587Z" }, +] + +[[package]] +name = "mkdocs-macros-plugin" +version = "1.5.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "hjson" }, + { name = "jinja2" }, + { name = "mkdocs" }, + { name = "packaging" }, + { name = "pathspec" }, + { name = "python-dateutil" }, + { name = "pyyaml" }, + { name = "requests" }, + { name = "super-collections" }, + { name = "termcolor" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/92/15/e6a44839841ebc9c5872fa0e6fad1c3757424e4fe026093b68e9f386d136/mkdocs_macros_plugin-1.5.0.tar.gz", hash = "sha256:12aa45ce7ecb7a445c66b9f649f3dd05e9b92e8af6bc65e4acd91d26f878c01f", size = 37730, upload-time = "2025-11-13T08:08:55.545Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/51/62/9fffba5bb9ed3d31a932ad35038ba9483d59850256ee0fea7f1187173983/mkdocs_macros_plugin-1.5.0-py3-none-any.whl", hash = "sha256:c10fabd812bf50f9170609d0ed518e54f1f0e12c334ac29141723a83c881dd6f", size = 44626, upload-time = "2025-11-13T08:08:53.878Z" }, +] + +[[package]] +name = "mkdocs-material" +version = "9.7.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "babel" }, + { name = "backrefs" }, + { name = "colorama" }, + { name = "jinja2" }, + { name = "markdown" }, + { name = "mkdocs" }, + { name = "mkdocs-material-extensions" }, + { name = "paginate" }, + { name = "pygments" }, + { name = "pymdown-extensions" }, + { name = "requests" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/27/e2/2ffc356cd72f1473d07c7719d82a8f2cbd261666828614ecb95b12169f41/mkdocs_material-9.7.1.tar.gz", hash = "sha256:89601b8f2c3e6c6ee0a918cc3566cb201d40bf37c3cd3c2067e26fadb8cce2b8", size = 4094392, upload-time = "2025-12-18T09:49:00.308Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3e/32/ed071cb721aca8c227718cffcf7bd539620e9799bbf2619e90c757bfd030/mkdocs_material-9.7.1-py3-none-any.whl", hash = "sha256:3f6100937d7d731f87f1e3e3b021c97f7239666b9ba1151ab476cabb96c60d5c", size = 9297166, upload-time = "2025-12-18T09:48:56.664Z" }, +] + +[[package]] +name = "mkdocs-material-extensions" +version = "1.3.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/79/9b/9b4c96d6593b2a541e1cb8b34899a6d021d208bb357042823d4d2cabdbe7/mkdocs_material_extensions-1.3.1.tar.gz", hash = "sha256:10c9511cea88f568257f960358a467d12b970e1f7b2c0e5fb2bb48cab1928443", size = 11847, upload-time = "2023-11-22T19:09:45.208Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5b/54/662a4743aa81d9582ee9339d4ffa3c8fd40a4965e033d77b9da9774d3960/mkdocs_material_extensions-1.3.1-py3-none-any.whl", hash = "sha256:adff8b62700b25cb77b53358dad940f3ef973dd6db797907c49e3c2ef3ab4e31", size = 8728, upload-time = "2023-11-22T19:09:43.465Z" }, +] + +[[package]] +name = "mkdocs-simple-hooks" +version = "0.1.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "mkdocs" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f1/93/565f98d6810e3b493e61160aea1cceb8653331576e7fa7f048ac7e7cdf62/mkdocs-simple-hooks-0.1.5.tar.gz", hash = "sha256:dddbdf151a18723c9302a133e5cf79538be8eb9d274e8e07d2ac3ac34890837c", size = 4037, upload-time = "2022-01-07T09:11:18.345Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7a/e9/7bf0f928f5b6cdd602d4a01d52e5fc1eba8d3ba6d97619a88fc271a625f8/mkdocs_simple_hooks-0.1.5-py3-none-any.whl", hash = "sha256:efeabdbb98b0850a909adee285f3404535117159d5cb3a34f541d6eaa644d50a", size = 4596, upload-time = "2022-01-07T09:11:17.022Z" }, +] + +[[package]] +name = "packaging" +version = "25.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a1/d4/1fc4078c65507b51b96ca8f8c3ba19e6a61c8253c72794544580a7b6c24d/packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f", size = 165727, upload-time = "2025-04-19T11:48:59.673Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/20/12/38679034af332785aac8774540895e234f4d07f7545804097de4b666afd8/packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484", size = 66469, upload-time = "2025-04-19T11:48:57.875Z" }, +] + +[[package]] +name = "paginate" +version = "0.5.7" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ec/46/68dde5b6bc00c1296ec6466ab27dddede6aec9af1b99090e1107091b3b84/paginate-0.5.7.tar.gz", hash = "sha256:22bd083ab41e1a8b4f3690544afb2c60c25e5c9a63a30fa2f483f6c60c8e5945", size = 19252, upload-time = "2024-08-25T14:17:24.139Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/90/96/04b8e52da071d28f5e21a805b19cb9390aa17a47462ac87f5e2696b9566d/paginate-0.5.7-py2.py3-none-any.whl", hash = "sha256:b885e2af73abcf01d9559fd5216b57ef722f8c42affbb63942377668e35c7591", size = 13746, upload-time = "2024-08-25T14:17:22.55Z" }, +] + +[[package]] +name = "pathspec" +version = "0.12.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ca/bc/f35b8446f4531a7cb215605d100cd88b7ac6f44ab3fc94870c120ab3adbf/pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712", size = 51043, upload-time = "2023-12-10T22:30:45Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cc/20/ff623b09d963f88bfde16306a54e12ee5ea43e9b597108672ff3a408aad6/pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08", size = 31191, upload-time = "2023-12-10T22:30:43.14Z" }, +] + +[[package]] +name = "platformdirs" +version = "4.4.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/23/e8/21db9c9987b0e728855bd57bff6984f67952bea55d6f75e055c46b5383e8/platformdirs-4.4.0.tar.gz", hash = "sha256:ca753cf4d81dc309bc67b0ea38fd15dc97bc30ce419a7f58d13eb3bf14c4febf", size = 21634, upload-time = "2025-08-26T14:32:04.268Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/40/4b/2028861e724d3bd36227adfa20d3fd24c3fc6d52032f4a93c133be5d17ce/platformdirs-4.4.0-py3-none-any.whl", hash = "sha256:abd01743f24e5287cd7a5db3752faf1a2d65353f38ec26d98e25a6db65958c85", size = 18654, upload-time = "2025-08-26T14:32:02.735Z" }, +] + +[[package]] +name = "pygments" +version = "2.19.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b0/77/a5b8c569bf593b0140bde72ea885a803b82086995367bf2037de0159d924/pygments-2.19.2.tar.gz", hash = "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887", size = 4968631, upload-time = "2025-06-21T13:39:12.283Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c7/21/705964c7812476f378728bdf590ca4b771ec72385c533964653c68e86bdc/pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b", size = 1225217, upload-time = "2025-06-21T13:39:07.939Z" }, +] + +[[package]] +name = "pymdown-extensions" +version = "10.16.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "markdown" }, + { name = "pyyaml" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/55/b3/6d2b3f149bc5413b0a29761c2c5832d8ce904a1d7f621e86616d96f505cc/pymdown_extensions-10.16.1.tar.gz", hash = "sha256:aace82bcccba3efc03e25d584e6a22d27a8e17caa3f4dd9f207e49b787aa9a91", size = 853277, upload-time = "2025-07-28T16:19:34.167Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e4/06/43084e6cbd4b3bc0e80f6be743b2e79fbc6eed8de9ad8c629939fa55d972/pymdown_extensions-10.16.1-py3-none-any.whl", hash = "sha256:d6ba157a6c03146a7fb122b2b9a121300056384eafeec9c9f9e584adfdb2a32d", size = 266178, upload-time = "2025-07-28T16:19:31.401Z" }, +] + +[[package]] +name = "python-dateutil" +version = "2.9.0.post0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "six" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/66/c0/0c8b6ad9f17a802ee498c46e004a0eb49bc148f2fd230864601a86dcf6db/python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", size = 342432, upload-time = "2024-03-01T18:36:20.211Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892, upload-time = "2024-03-01T18:36:18.57Z" }, +] + +[[package]] +name = "pyyaml" +version = "6.0.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/54/ed/79a089b6be93607fa5cdaedf301d7dfb23af5f25c398d5ead2525b063e17/pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e", size = 130631, upload-time = "2024-08-06T20:33:50.674Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ef/e3/3af305b830494fa85d95f6d95ef7fa73f2ee1cc8ef5b495c7c3269fb835f/PyYAML-6.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba", size = 181309, upload-time = "2024-08-06T20:32:43.4Z" }, + { url = "https://files.pythonhosted.org/packages/45/9f/3b1c20a0b7a3200524eb0076cc027a970d320bd3a6592873c85c92a08731/PyYAML-6.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1", size = 171679, upload-time = "2024-08-06T20:32:44.801Z" }, + { url = "https://files.pythonhosted.org/packages/7c/9a/337322f27005c33bcb656c655fa78325b730324c78620e8328ae28b64d0c/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133", size = 733428, upload-time = "2024-08-06T20:32:46.432Z" }, + { url = "https://files.pythonhosted.org/packages/a3/69/864fbe19e6c18ea3cc196cbe5d392175b4cf3d5d0ac1403ec3f2d237ebb5/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484", size = 763361, upload-time = "2024-08-06T20:32:51.188Z" }, + { url = "https://files.pythonhosted.org/packages/04/24/b7721e4845c2f162d26f50521b825fb061bc0a5afcf9a386840f23ea19fa/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5", size = 759523, upload-time = "2024-08-06T20:32:53.019Z" }, + { url = "https://files.pythonhosted.org/packages/2b/b2/e3234f59ba06559c6ff63c4e10baea10e5e7df868092bf9ab40e5b9c56b6/PyYAML-6.0.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc", size = 726660, upload-time = "2024-08-06T20:32:54.708Z" }, + { url = "https://files.pythonhosted.org/packages/fe/0f/25911a9f080464c59fab9027482f822b86bf0608957a5fcc6eaac85aa515/PyYAML-6.0.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652", size = 751597, upload-time = "2024-08-06T20:32:56.985Z" }, + { url = "https://files.pythonhosted.org/packages/14/0d/e2c3b43bbce3cf6bd97c840b46088a3031085179e596d4929729d8d68270/PyYAML-6.0.2-cp313-cp313-win32.whl", hash = "sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183", size = 140527, upload-time = "2024-08-06T20:33:03.001Z" }, + { url = "https://files.pythonhosted.org/packages/fa/de/02b54f42487e3d3c6efb3f89428677074ca7bf43aae402517bc7cca949f3/PyYAML-6.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563", size = 156446, upload-time = "2024-08-06T20:33:04.33Z" }, +] + +[[package]] +name = "pyyaml-env-tag" +version = "1.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pyyaml" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/eb/2e/79c822141bfd05a853236b504869ebc6b70159afc570e1d5a20641782eaa/pyyaml_env_tag-1.1.tar.gz", hash = "sha256:2eb38b75a2d21ee0475d6d97ec19c63287a7e140231e4214969d0eac923cd7ff", size = 5737, upload-time = "2025-05-13T15:24:01.64Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/04/11/432f32f8097b03e3cd5fe57e88efb685d964e2e5178a48ed61e841f7fdce/pyyaml_env_tag-1.1-py3-none-any.whl", hash = "sha256:17109e1a528561e32f026364712fee1264bc2ea6715120891174ed1b980d2e04", size = 4722, upload-time = "2025-05-13T15:23:59.629Z" }, +] + +[[package]] +name = "requests" +version = "2.32.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "certifi" }, + { name = "charset-normalizer" }, + { name = "idna" }, + { name = "urllib3" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c9/74/b3ff8e6c8446842c3f5c837e9c3dfcfe2018ea6ecef224c710c85ef728f4/requests-2.32.5.tar.gz", hash = "sha256:dbba0bac56e100853db0ea71b82b4dfd5fe2bf6d3754a8893c3af500cec7d7cf", size = 134517, upload-time = "2025-08-18T20:46:02.573Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1e/db/4254e3eabe8020b458f1a747140d32277ec7a271daf1d235b70dc0b4e6e3/requests-2.32.5-py3-none-any.whl", hash = "sha256:2462f94637a34fd532264295e186976db0f5d453d1cdd31473c85a6a161affb6", size = 64738, upload-time = "2025-08-18T20:46:00.542Z" }, +] + +[[package]] +name = "russian" +version = "0.1.0" +source = { virtual = "." } +dependencies = [ + { name = "mkdocs" }, + { name = "mkdocs-macros-plugin" }, + { name = "mkdocs-material" }, + { name = "mkdocs-simple-hooks" }, +] + +[package.metadata] +requires-dist = [ + { name = "mkdocs", specifier = ">=1.6.1" }, + { name = "mkdocs-macros-plugin", specifier = ">=1.5.0" }, + { name = "mkdocs-material", specifier = ">=9.7.1" }, + { name = "mkdocs-simple-hooks", specifier = ">=0.1.5" }, +] + +[[package]] +name = "six" +version = "1.17.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/94/e7/b2c673351809dca68a0e064b6af791aa332cf192da575fd474ed7d6f16a2/six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81", size = 34031, upload-time = "2024-12-04T17:35:28.174Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050, upload-time = "2024-12-04T17:35:26.475Z" }, +] + +[[package]] +name = "super-collections" +version = "0.6.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "hjson" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/e0/de/a0c3d1244912c260638f0f925e190e493ccea37ecaea9bbad7c14413b803/super_collections-0.6.2.tar.gz", hash = "sha256:0c8d8abacd9fad2c7c1c715f036c29f5db213f8cac65f24d45ecba12b4da187a", size = 31315, upload-time = "2025-09-30T00:37:08.067Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/17/43/47c7cf84b3bd74a8631b02d47db356656bb8dff6f2e61a4c749963814d0d/super_collections-0.6.2-py3-none-any.whl", hash = "sha256:291b74d26299e9051d69ad9d89e61b07b6646f86a57a2f5ab3063d206eee9c56", size = 16173, upload-time = "2025-09-30T00:37:07.104Z" }, +] + +[[package]] +name = "termcolor" +version = "3.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ca/6c/3d75c196ac07ac8749600b60b03f4f6094d54e132c4d94ebac6ee0e0add0/termcolor-3.1.0.tar.gz", hash = "sha256:6a6dd7fbee581909eeec6a756cff1d7f7c376063b14e4a298dc4980309e55970", size = 14324, upload-time = "2025-04-30T11:37:53.791Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4f/bd/de8d508070629b6d84a30d01d57e4a65c69aa7f5abe7560b8fad3b50ea59/termcolor-3.1.0-py3-none-any.whl", hash = "sha256:591dd26b5c2ce03b9e43f391264626557873ce1d379019786f99b0c2bee140aa", size = 7684, upload-time = "2025-04-30T11:37:52.382Z" }, +] + +[[package]] +name = "urllib3" +version = "2.5.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/15/22/9ee70a2574a4f4599c47dd506532914ce044817c7752a79b6a51286319bc/urllib3-2.5.0.tar.gz", hash = "sha256:3fc47733c7e419d4bc3f6b3dc2b4f890bb743906a30d56ba4a5bfa4bbff92760", size = 393185, upload-time = "2025-06-18T14:07:41.644Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a7/c2/fe1e52489ae3122415c51f387e221dd0773709bad6c6cdaa599e8a2c5185/urllib3-2.5.0-py3-none-any.whl", hash = "sha256:e6b01673c0fa6a13e374b50871808eb3bf7046c4b125b216f6bf1cc604cff0dc", size = 129795, upload-time = "2025-06-18T14:07:40.39Z" }, +] + +[[package]] +name = "watchdog" +version = "6.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/db/7d/7f3d619e951c88ed75c6037b246ddcf2d322812ee8ea189be89511721d54/watchdog-6.0.0.tar.gz", hash = "sha256:9ddf7c82fda3ae8e24decda1338ede66e1c99883db93711d8fb941eaa2d8c282", size = 131220, upload-time = "2024-11-01T14:07:13.037Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/68/98/b0345cabdce2041a01293ba483333582891a3bd5769b08eceb0d406056ef/watchdog-6.0.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:490ab2ef84f11129844c23fb14ecf30ef3d8a6abafd3754a6f75ca1e6654136c", size = 96480, upload-time = "2024-11-01T14:06:42.952Z" }, + { url = "https://files.pythonhosted.org/packages/85/83/cdf13902c626b28eedef7ec4f10745c52aad8a8fe7eb04ed7b1f111ca20e/watchdog-6.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:76aae96b00ae814b181bb25b1b98076d5fc84e8a53cd8885a318b42b6d3a5134", size = 88451, upload-time = "2024-11-01T14:06:45.084Z" }, + { url = "https://files.pythonhosted.org/packages/fe/c4/225c87bae08c8b9ec99030cd48ae9c4eca050a59bf5c2255853e18c87b50/watchdog-6.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a175f755fc2279e0b7312c0035d52e27211a5bc39719dd529625b1930917345b", size = 89057, upload-time = "2024-11-01T14:06:47.324Z" }, + { url = "https://files.pythonhosted.org/packages/a9/c7/ca4bf3e518cb57a686b2feb4f55a1892fd9a3dd13f470fca14e00f80ea36/watchdog-6.0.0-py3-none-manylinux2014_aarch64.whl", hash = "sha256:7607498efa04a3542ae3e05e64da8202e58159aa1fa4acddf7678d34a35d4f13", size = 79079, upload-time = "2024-11-01T14:06:59.472Z" }, + { url = "https://files.pythonhosted.org/packages/5c/51/d46dc9332f9a647593c947b4b88e2381c8dfc0942d15b8edc0310fa4abb1/watchdog-6.0.0-py3-none-manylinux2014_armv7l.whl", hash = "sha256:9041567ee8953024c83343288ccc458fd0a2d811d6a0fd68c4c22609e3490379", size = 79078, upload-time = "2024-11-01T14:07:01.431Z" }, + { url = "https://files.pythonhosted.org/packages/d4/57/04edbf5e169cd318d5f07b4766fee38e825d64b6913ca157ca32d1a42267/watchdog-6.0.0-py3-none-manylinux2014_i686.whl", hash = "sha256:82dc3e3143c7e38ec49d61af98d6558288c415eac98486a5c581726e0737c00e", size = 79076, upload-time = "2024-11-01T14:07:02.568Z" }, + { url = "https://files.pythonhosted.org/packages/ab/cc/da8422b300e13cb187d2203f20b9253e91058aaf7db65b74142013478e66/watchdog-6.0.0-py3-none-manylinux2014_ppc64.whl", hash = "sha256:212ac9b8bf1161dc91bd09c048048a95ca3a4c4f5e5d4a7d1b1a7d5752a7f96f", size = 79077, upload-time = "2024-11-01T14:07:03.893Z" }, + { url = "https://files.pythonhosted.org/packages/2c/3b/b8964e04ae1a025c44ba8e4291f86e97fac443bca31de8bd98d3263d2fcf/watchdog-6.0.0-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:e3df4cbb9a450c6d49318f6d14f4bbc80d763fa587ba46ec86f99f9e6876bb26", size = 79078, upload-time = "2024-11-01T14:07:05.189Z" }, + { url = "https://files.pythonhosted.org/packages/62/ae/a696eb424bedff7407801c257d4b1afda455fe40821a2be430e173660e81/watchdog-6.0.0-py3-none-manylinux2014_s390x.whl", hash = "sha256:2cce7cfc2008eb51feb6aab51251fd79b85d9894e98ba847408f662b3395ca3c", size = 79077, upload-time = "2024-11-01T14:07:06.376Z" }, + { url = "https://files.pythonhosted.org/packages/b5/e8/dbf020b4d98251a9860752a094d09a65e1b436ad181faf929983f697048f/watchdog-6.0.0-py3-none-manylinux2014_x86_64.whl", hash = "sha256:20ffe5b202af80ab4266dcd3e91aae72bf2da48c0d33bdb15c66658e685e94e2", size = 79078, upload-time = "2024-11-01T14:07:07.547Z" }, + { url = "https://files.pythonhosted.org/packages/07/f6/d0e5b343768e8bcb4cda79f0f2f55051bf26177ecd5651f84c07567461cf/watchdog-6.0.0-py3-none-win32.whl", hash = "sha256:07df1fdd701c5d4c8e55ef6cf55b8f0120fe1aef7ef39a1c6fc6bc2e606d517a", size = 79065, upload-time = "2024-11-01T14:07:09.525Z" }, + { url = "https://files.pythonhosted.org/packages/db/d9/c495884c6e548fce18a8f40568ff120bc3a4b7b99813081c8ac0c936fa64/watchdog-6.0.0-py3-none-win_amd64.whl", hash = "sha256:cbafb470cf848d93b5d013e2ecb245d4aa1c8fd0504e863ccefa32445359d680", size = 79070, upload-time = "2024-11-01T14:07:10.686Z" }, + { url = "https://files.pythonhosted.org/packages/33/e8/e40370e6d74ddba47f002a32919d91310d6074130fe4e17dabcafc15cbf1/watchdog-6.0.0-py3-none-win_ia64.whl", hash = "sha256:a1914259fa9e1454315171103c6a30961236f508b9b623eae470268bbcc6a22f", size = 79067, upload-time = "2024-11-01T14:07:11.845Z" }, +] diff --git a/src/Directory.Build.props b/src/Directory.Build.props index 533d2d5..3d49b80 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -61,6 +61,7 @@ ReadMe.md true true + true diff --git a/src/MrKWatkins.BinaryPrimitives/ByteIReadOnlyListExtensions.cs b/src/MrKWatkins.BinaryPrimitives/ByteIReadOnlyListExtensions.cs index b9ac865..996e3d5 100644 --- a/src/MrKWatkins.BinaryPrimitives/ByteIReadOnlyListExtensions.cs +++ b/src/MrKWatkins.BinaryPrimitives/ByteIReadOnlyListExtensions.cs @@ -18,14 +18,14 @@ public static class ByteIReadOnlyListExtensions /// /// The destination span. /// The zero-based index in to start copying to. - /// does not have enough space to copy . + /// does not have enough space to copy bytes. public void CopyTo(Span destination, int start) => bytes.CopyTo(destination[start..]); /// /// Copies the contents of a read-only list to a span. /// /// The destination span. - /// does not have enough space to copy . + /// does not have enough space to copy bytes. public void CopyTo(Span destination) { if (bytes.Count > destination.Length) From f95b811013c54a949f6ef32a18b277d9ca2e9854 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 20 Feb 2026 17:57:26 +0000 Subject: [PATCH 2/7] Initial plan From 6f273c627f5eac5b5d46d3e805ecfa72b2f0b78f Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 20 Feb 2026 18:13:31 +0000 Subject: [PATCH 3/7] Add UInt24 readonly struct implementing generic math interfaces Implements IBinaryInteger, IMinMaxValue, and IUnsignedNumber with all required sub-interfaces including arithmetic, comparison, bitwise, shift, parsing, formatting, and conversion operators. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- src/MrKWatkins.BinaryPrimitives/UInt24.cs | 1512 +++++++++++++++++++++ 1 file changed, 1512 insertions(+) create mode 100644 src/MrKWatkins.BinaryPrimitives/UInt24.cs diff --git a/src/MrKWatkins.BinaryPrimitives/UInt24.cs b/src/MrKWatkins.BinaryPrimitives/UInt24.cs new file mode 100644 index 0000000..66548e6 --- /dev/null +++ b/src/MrKWatkins.BinaryPrimitives/UInt24.cs @@ -0,0 +1,1512 @@ +using System.Globalization; +using System.Numerics; +using System.Runtime.CompilerServices; + +namespace MrKWatkins.BinaryPrimitives; + +/// +/// Represents a 24-bit unsigned integer. +/// +public readonly struct UInt24 : + IBinaryInteger, + IMinMaxValue, + IUnsignedNumber +{ + internal const uint MaxValueUInt32 = 0x00FFFFFFu; + + private readonly uint _value; + + /// + /// Initializes a new instance of the struct from a raw value without validation. + /// + /// The raw value. + internal UInt24(uint value) => _value = value; + + private UInt24(uint value, bool @checked) + { + if (@checked && value > MaxValueUInt32) + { + throw new OverflowException(); + } + + _value = value; + } + + /// + /// Represents the largest possible value of a . + /// + public static UInt24 MaxValue => new(MaxValueUInt32); + + /// + /// Represents the smallest possible value of a . + /// + public static UInt24 MinValue => new(0u); + + /// + public static UInt24 One => new(1u); + + /// + public static int Radix => 2; + + /// + public static UInt24 Zero => new(0u); + + /// + public static UInt24 AdditiveIdentity => Zero; + + /// + public static UInt24 MultiplicativeIdentity => One; + + #region IComparable + + /// + [Pure] + public int CompareTo(object? obj) + { + if (obj is null) + { + return 1; + } + + if (obj is UInt24 other) + { + return CompareTo(other); + } + + throw new ArgumentException($"Object must be of type {nameof(UInt24)}.", nameof(obj)); + } + + /// + [Pure] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public int CompareTo(UInt24 other) => _value.CompareTo(other._value); + + #endregion + + #region IEquatable + + /// + [Pure] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool Equals(UInt24 other) => _value == other._value; + + /// + [Pure] + public override bool Equals(object? obj) => obj is UInt24 other && Equals(other); + + /// + [Pure] + public override int GetHashCode() => _value.GetHashCode(); + + #endregion + + #region Formatting + + /// + [Pure] + public override string ToString() => _value.ToString(CultureInfo.CurrentCulture); + + /// + [Pure] + public string ToString(string? format, IFormatProvider? formatProvider) => _value.ToString(format, formatProvider); + + /// + [Pure] + public bool TryFormat(Span destination, out int charsWritten, ReadOnlySpan format, IFormatProvider? provider) => + _value.TryFormat(destination, out charsWritten, format, provider); + + /// + [Pure] + public bool TryFormat(Span utf8Destination, out int bytesWritten, ReadOnlySpan format, IFormatProvider? provider) => + _value.TryFormat(utf8Destination, out bytesWritten, format, provider); + + #endregion + + #region Parsing + + /// + [Pure] + public static UInt24 Parse(string s, IFormatProvider? provider) => Parse(s, NumberStyles.Integer, provider); + + /// + [Pure] + public static UInt24 Parse(string s, NumberStyles style, IFormatProvider? provider) + { + var value = uint.Parse(s, style, provider); + return new UInt24(value, true); + } + + /// + [Pure] + public static UInt24 Parse(ReadOnlySpan s, IFormatProvider? provider) => Parse(s, NumberStyles.Integer, provider); + + /// + [Pure] + public static UInt24 Parse(ReadOnlySpan s, NumberStyles style, IFormatProvider? provider) + { + var value = uint.Parse(s, style, provider); + return new UInt24(value, true); + } + + /// + [Pure] + public static UInt24 Parse(ReadOnlySpan utf8Text, IFormatProvider? provider) => Parse(utf8Text, NumberStyles.Integer, provider); + + /// + [Pure] + public static UInt24 Parse(ReadOnlySpan utf8Text, NumberStyles style, IFormatProvider? provider) + { + var value = uint.Parse(utf8Text, style, provider); + return new UInt24(value, true); + } + + /// + public static bool TryParse(string? s, IFormatProvider? provider, out UInt24 result) => + TryParse(s, NumberStyles.Integer, provider, out result); + + /// + public static bool TryParse(string? s, NumberStyles style, IFormatProvider? provider, out UInt24 result) + { + if (uint.TryParse(s, style, provider, out var value) && value <= MaxValueUInt32) + { + result = new UInt24(value); + return true; + } + + result = default; + return false; + } + + /// + public static bool TryParse(ReadOnlySpan s, IFormatProvider? provider, out UInt24 result) => + TryParse(s, NumberStyles.Integer, provider, out result); + + /// + public static bool TryParse(ReadOnlySpan s, NumberStyles style, IFormatProvider? provider, out UInt24 result) + { + if (uint.TryParse(s, style, provider, out var value) && value <= MaxValueUInt32) + { + result = new UInt24(value); + return true; + } + + result = default; + return false; + } + + /// + public static bool TryParse(ReadOnlySpan utf8Text, IFormatProvider? provider, out UInt24 result) => + TryParse(utf8Text, NumberStyles.Integer, provider, out result); + + /// + public static bool TryParse(ReadOnlySpan utf8Text, NumberStyles style, IFormatProvider? provider, out UInt24 result) + { + if (uint.TryParse(utf8Text, style, provider, out var value) && value <= MaxValueUInt32) + { + result = new UInt24(value); + return true; + } + + result = default; + return false; + } + + #endregion + + #region Arithmetic Operators + + /// + [Pure] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static UInt24 operator +(UInt24 left, UInt24 right) => new((left._value + right._value) & MaxValueUInt32); + + /// + [Pure] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static UInt24 operator checked +(UInt24 left, UInt24 right) + { + var result = left._value + right._value; + if (result > MaxValueUInt32) + { + throw new OverflowException(); + } + + return new UInt24(result); + } + + /// + [Pure] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static UInt24 operator -(UInt24 left, UInt24 right) => new((left._value - right._value) & MaxValueUInt32); + + /// + [Pure] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static UInt24 operator checked -(UInt24 left, UInt24 right) + { + if (left._value < right._value) + { + throw new OverflowException(); + } + + return new UInt24(left._value - right._value); + } + + /// + [Pure] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static UInt24 operator *(UInt24 left, UInt24 right) => new((left._value * right._value) & MaxValueUInt32); + + /// + [Pure] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static UInt24 operator checked *(UInt24 left, UInt24 right) + { + var result = (ulong)left._value * right._value; + if (result > MaxValueUInt32) + { + throw new OverflowException(); + } + + return new UInt24((uint)result); + } + + /// + [Pure] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static UInt24 operator /(UInt24 left, UInt24 right) => new(left._value / right._value); + + /// + [Pure] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static UInt24 operator %(UInt24 left, UInt24 right) => new(left._value % right._value); + + #endregion + + #region Increment/Decrement Operators + + /// + [Pure] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static UInt24 operator ++(UInt24 value) => new((value._value + 1u) & MaxValueUInt32); + + /// + [Pure] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static UInt24 operator checked ++(UInt24 value) + { + if (value._value >= MaxValueUInt32) + { + throw new OverflowException(); + } + + return new UInt24(value._value + 1u); + } + + /// + [Pure] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static UInt24 operator --(UInt24 value) => new((value._value - 1u) & MaxValueUInt32); + + /// + [Pure] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static UInt24 operator checked --(UInt24 value) + { + if (value._value == 0u) + { + throw new OverflowException(); + } + + return new UInt24(value._value - 1u); + } + + #endregion + + #region Unary Operators + + /// + [Pure] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static UInt24 operator +(UInt24 value) => value; + + /// + [Pure] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static UInt24 operator -(UInt24 value) => new((~value._value + 1u) & MaxValueUInt32); + + /// + [Pure] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static UInt24 operator checked -(UInt24 value) + { + if (value._value != 0u) + { + throw new OverflowException(); + } + + return Zero; + } + + #endregion + + #region Comparison Operators + + /// + [Pure] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator ==(UInt24 left, UInt24 right) => left._value == right._value; + + /// + [Pure] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator !=(UInt24 left, UInt24 right) => left._value != right._value; + + /// + [Pure] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator <(UInt24 left, UInt24 right) => left._value < right._value; + + /// + [Pure] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator >(UInt24 left, UInt24 right) => left._value > right._value; + + /// + [Pure] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator <=(UInt24 left, UInt24 right) => left._value <= right._value; + + /// + [Pure] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator >=(UInt24 left, UInt24 right) => left._value >= right._value; + + #endregion + + #region Bitwise Operators + + /// + [Pure] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static UInt24 operator &(UInt24 left, UInt24 right) => new(left._value & right._value); + + /// + [Pure] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static UInt24 operator |(UInt24 left, UInt24 right) => new(left._value | right._value); + + /// + [Pure] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static UInt24 operator ^(UInt24 left, UInt24 right) => new(left._value ^ right._value); + + /// + [Pure] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static UInt24 operator ~(UInt24 value) => new(~value._value & MaxValueUInt32); + + #endregion + + #region Shift Operators + + /// + [Pure] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static UInt24 operator <<(UInt24 value, int shiftAmount) => new((value._value << shiftAmount) & MaxValueUInt32); + + /// + [Pure] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static UInt24 operator >>(UInt24 value, int shiftAmount) => new(value._value >> shiftAmount); + + /// + [Pure] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static UInt24 operator >>>(UInt24 value, int shiftAmount) => new(value._value >>> shiftAmount); + + #endregion + + #region Conversion Operators - Implicit TO UInt24 + + /// + /// Implicitly converts a to a . + /// + /// The value to convert. + [Pure] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator UInt24(byte value) => new((uint)value); + + /// + /// Implicitly converts a to a . + /// + /// The value to convert. + [Pure] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator UInt24(ushort value) => new((uint)value); + + #endregion + + #region Conversion Operators - Implicit FROM UInt24 + + /// + /// Implicitly converts a to a . + /// + /// The value to convert. + [Pure] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator uint(UInt24 value) => value._value; + + /// + /// Implicitly converts a to an . + /// + /// The value to convert. + [Pure] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator int(UInt24 value) => (int)value._value; + + /// + /// Implicitly converts a to a . + /// + /// The value to convert. + [Pure] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator long(UInt24 value) => value._value; + + /// + /// Implicitly converts a to a . + /// + /// The value to convert. + [Pure] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator ulong(UInt24 value) => value._value; + + #endregion + + #region Conversion Operators - Explicit TO UInt24 + + /// + /// Explicitly converts an to a . + /// + /// The value to convert. + /// is negative. + [Pure] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static explicit operator UInt24(sbyte value) => new(checked((uint)value), false); + + /// + /// Explicitly converts a to a . + /// + /// The value to convert. + /// is negative. + [Pure] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static explicit operator UInt24(short value) => new(checked((uint)value), false); + + /// + /// Explicitly converts an to a . + /// + /// The value to convert. + /// is negative or greater than . + [Pure] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static explicit operator UInt24(int value) => new(checked((uint)value), true); + + /// + /// Explicitly converts a to a . + /// + /// The value to convert. + /// is greater than . + [Pure] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static explicit operator UInt24(uint value) => new(value, true); + + /// + /// Explicitly converts a to a . + /// + /// The value to convert. + /// is negative or greater than . + [Pure] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static explicit operator UInt24(long value) => new(checked((uint)value), true); + + /// + /// Explicitly converts a to a . + /// + /// The value to convert. + /// is greater than . + [Pure] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static explicit operator UInt24(ulong value) => new(checked((uint)value), true); + + /// + /// Explicitly converts a to a . + /// + /// The value to convert. + /// is negative or greater than . + [Pure] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static explicit operator UInt24(nint value) => new(checked((uint)value), true); + + /// + /// Explicitly converts a to a . + /// + /// The value to convert. + /// is greater than . + [Pure] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static explicit operator UInt24(nuint value) => new(checked((uint)value), true); + + /// + /// Explicitly converts a to a . + /// + /// The value to convert. + /// is negative, greater than , NaN or infinity. + [Pure] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static explicit operator UInt24(Half value) => new(checked((uint)value), true); + + /// + /// Explicitly converts a to a . + /// + /// The value to convert. + /// is negative, greater than , NaN or infinity. + [Pure] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static explicit operator UInt24(float value) => new(checked((uint)value), true); + + /// + /// Explicitly converts a to a . + /// + /// The value to convert. + /// is negative, greater than , NaN or infinity. + [Pure] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static explicit operator UInt24(double value) => new(checked((uint)value), true); + + /// + /// Explicitly converts a to a . + /// + /// The value to convert. + /// is negative or greater than . + [Pure] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static explicit operator UInt24(decimal value) => new(checked((uint)value), true); + + #endregion + + #region Conversion Operators - Explicit FROM UInt24 + + /// + /// Explicitly converts a to a . + /// + /// The value to convert. + /// is greater than . + [Pure] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static explicit operator byte(UInt24 value) => checked((byte)value._value); + + /// + /// Explicitly converts a to an . + /// + /// The value to convert. + /// is greater than . + [Pure] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static explicit operator sbyte(UInt24 value) => checked((sbyte)value._value); + + /// + /// Explicitly converts a to a . + /// + /// The value to convert. + /// is greater than . + [Pure] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static explicit operator short(UInt24 value) => checked((short)value._value); + + /// + /// Explicitly converts a to a . + /// + /// The value to convert. + /// is greater than . + [Pure] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static explicit operator ushort(UInt24 value) => checked((ushort)value._value); + + #endregion + + #region INumberBase + + /// + [Pure] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsCanonical(UInt24 value) => true; + + /// + [Pure] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsComplexNumber(UInt24 value) => false; + + /// + [Pure] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsEvenInteger(UInt24 value) => (value._value & 1u) == 0u; + + /// + [Pure] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsFinite(UInt24 value) => true; + + /// + [Pure] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsImaginaryNumber(UInt24 value) => false; + + /// + [Pure] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsInfinity(UInt24 value) => false; + + /// + [Pure] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsInteger(UInt24 value) => true; + + /// + [Pure] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsNaN(UInt24 value) => false; + + /// + [Pure] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsNegative(UInt24 value) => false; + + /// + [Pure] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsNormal(UInt24 value) => value._value != 0u; + + /// + [Pure] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsOddInteger(UInt24 value) => (value._value & 1u) != 0u; + + /// + [Pure] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsPositive(UInt24 value) => true; + + /// + [Pure] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsRealNumber(UInt24 value) => true; + + /// + [Pure] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsNegativeInfinity(UInt24 value) => false; + + /// + [Pure] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsPositiveInfinity(UInt24 value) => false; + + /// + [Pure] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsSubnormal(UInt24 value) => false; + + /// + [Pure] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsZero(UInt24 value) => value._value == 0u; + + /// + [Pure] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static UInt24 MaxMagnitude(UInt24 x, UInt24 y) => Max(x, y); + + /// + [Pure] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static UInt24 MaxMagnitudeNumber(UInt24 x, UInt24 y) => Max(x, y); + + /// + [Pure] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static UInt24 MinMagnitude(UInt24 x, UInt24 y) => Min(x, y); + + /// + [Pure] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static UInt24 MinMagnitudeNumber(UInt24 x, UInt24 y) => Min(x, y); + + /// + [Pure] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static UInt24 Abs(UInt24 value) => value; + + /// + static bool INumberBase.TryConvertFromChecked(TOther value, out UInt24 result) + { + if (typeof(TOther) == typeof(uint)) + { + var v = (uint)(object)value; + if (v > MaxValueUInt32) + { + throw new OverflowException(); + } + + result = new UInt24(v); + return true; + } + + if (typeof(TOther) == typeof(ulong)) + { + var v = (ulong)(object)value; + if (v > MaxValueUInt32) + { + throw new OverflowException(); + } + + result = new UInt24((uint)v); + return true; + } + + if (typeof(TOther) == typeof(nuint)) + { + var v = (nuint)(object)value; + if (v > MaxValueUInt32) + { + throw new OverflowException(); + } + + result = new UInt24((uint)v); + return true; + } + + result = default; + return false; + } + + /// + static bool INumberBase.TryConvertFromSaturating(TOther value, out UInt24 result) + { + if (typeof(TOther) == typeof(uint)) + { + var v = (uint)(object)value; + result = new UInt24(v > MaxValueUInt32 ? MaxValueUInt32 : v); + return true; + } + + if (typeof(TOther) == typeof(ulong)) + { + var v = (ulong)(object)value; + result = new UInt24(v > MaxValueUInt32 ? MaxValueUInt32 : (uint)v); + return true; + } + + if (typeof(TOther) == typeof(nuint)) + { + var v = (nuint)(object)value; + result = new UInt24(v > MaxValueUInt32 ? MaxValueUInt32 : (uint)v); + return true; + } + + result = default; + return false; + } + + /// + static bool INumberBase.TryConvertFromTruncating(TOther value, out UInt24 result) + { + if (typeof(TOther) == typeof(uint)) + { + result = new UInt24((uint)(object)value & MaxValueUInt32); + return true; + } + + if (typeof(TOther) == typeof(ulong)) + { + result = new UInt24((uint)((ulong)(object)value & MaxValueUInt32)); + return true; + } + + if (typeof(TOther) == typeof(nuint)) + { + result = new UInt24((uint)((nuint)(object)value & MaxValueUInt32)); + return true; + } + + result = default; + return false; + } + + /// + static bool INumberBase.TryConvertToChecked(UInt24 value, [MaybeNullWhen(false)] out TOther result) + { + if (typeof(TOther) == typeof(byte)) + { + result = (TOther)(object)checked((byte)value._value); + return true; + } + + if (typeof(TOther) == typeof(sbyte)) + { + result = (TOther)(object)checked((sbyte)value._value); + return true; + } + + if (typeof(TOther) == typeof(short)) + { + result = (TOther)(object)checked((short)value._value); + return true; + } + + if (typeof(TOther) == typeof(ushort)) + { + result = (TOther)(object)checked((ushort)value._value); + return true; + } + + if (typeof(TOther) == typeof(int)) + { + result = (TOther)(object)(int)value._value; + return true; + } + + if (typeof(TOther) == typeof(uint)) + { + result = (TOther)(object)value._value; + return true; + } + + if (typeof(TOther) == typeof(long)) + { + result = (TOther)(object)(long)value._value; + return true; + } + + if (typeof(TOther) == typeof(ulong)) + { + result = (TOther)(object)(ulong)value._value; + return true; + } + + result = default; + return false; + } + + /// + static bool INumberBase.TryConvertToSaturating(UInt24 value, [MaybeNullWhen(false)] out TOther result) + { + if (typeof(TOther) == typeof(byte)) + { + result = (TOther)(object)(value._value > byte.MaxValue ? byte.MaxValue : (byte)value._value); + return true; + } + + if (typeof(TOther) == typeof(sbyte)) + { + result = (TOther)(object)(value._value > (uint)sbyte.MaxValue ? sbyte.MaxValue : (sbyte)value._value); + return true; + } + + if (typeof(TOther) == typeof(short)) + { + result = (TOther)(object)(value._value > (uint)short.MaxValue ? short.MaxValue : (short)value._value); + return true; + } + + if (typeof(TOther) == typeof(ushort)) + { + result = (TOther)(object)(value._value > ushort.MaxValue ? ushort.MaxValue : (ushort)value._value); + return true; + } + + if (typeof(TOther) == typeof(int)) + { + result = (TOther)(object)(int)value._value; + return true; + } + + if (typeof(TOther) == typeof(uint)) + { + result = (TOther)(object)value._value; + return true; + } + + if (typeof(TOther) == typeof(long)) + { + result = (TOther)(object)(long)value._value; + return true; + } + + if (typeof(TOther) == typeof(ulong)) + { + result = (TOther)(object)(ulong)value._value; + return true; + } + + result = default; + return false; + } + + /// + static bool INumberBase.TryConvertToTruncating(UInt24 value, [MaybeNullWhen(false)] out TOther result) + { + if (typeof(TOther) == typeof(byte)) + { + result = (TOther)(object)(byte)value._value; + return true; + } + + if (typeof(TOther) == typeof(sbyte)) + { + result = (TOther)(object)(sbyte)value._value; + return true; + } + + if (typeof(TOther) == typeof(short)) + { + result = (TOther)(object)(short)value._value; + return true; + } + + if (typeof(TOther) == typeof(ushort)) + { + result = (TOther)(object)(ushort)value._value; + return true; + } + + if (typeof(TOther) == typeof(int)) + { + result = (TOther)(object)(int)value._value; + return true; + } + + if (typeof(TOther) == typeof(uint)) + { + result = (TOther)(object)value._value; + return true; + } + + if (typeof(TOther) == typeof(long)) + { + result = (TOther)(object)(long)value._value; + return true; + } + + if (typeof(TOther) == typeof(ulong)) + { + result = (TOther)(object)(ulong)value._value; + return true; + } + + result = default; + return false; + } + + #endregion + + #region INumber + + /// + [Pure] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static UInt24 Clamp(UInt24 value, UInt24 min, UInt24 max) => + new(Math.Clamp(value._value, min._value, max._value)); + + /// + [Pure] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static UInt24 Max(UInt24 x, UInt24 y) => new(Math.Max(x._value, y._value)); + + /// + [Pure] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static UInt24 Min(UInt24 x, UInt24 y) => new(Math.Min(x._value, y._value)); + + /// + [Pure] + public static int Sign(UInt24 value) => value._value == 0u ? 0 : 1; + + #endregion + + #region IBinaryNumber + + /// + [Pure] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsPow2(UInt24 value) => uint.IsPow2(value._value); + + /// + [Pure] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static UInt24 Log2(UInt24 value) => new((uint)uint.Log2(value._value)); + + #endregion + + #region IBinaryInteger + + /// + [Pure] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public int GetByteCount() => 3; + + /// + [Pure] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public int GetShortestBitLength() + { + // Delegate to the uint value's calculation. + return 32 - int.LeadingZeroCount((int)_value); + } + + /// + [Pure] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static UInt24 LeadingZeroCount(UInt24 value) => + new((uint)(uint.LeadingZeroCount(value._value) - 8)); + + /// + [Pure] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static UInt24 TrailingZeroCount(UInt24 value) + { + // If value is 0, TrailingZeroCount on uint returns 32, but for 24-bit we want 24. + var count = uint.TrailingZeroCount(value._value); + return new(count > 24u ? 24u : count); + } + + /// + [Pure] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static UInt24 PopCount(UInt24 value) => new((uint)uint.PopCount(value._value)); + + /// + [Pure] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static UInt24 RotateLeft(UInt24 value, int rotateAmount) + { + rotateAmount = ((rotateAmount % 24) + 24) % 24; + return new(((value._value << rotateAmount) | (value._value >> (24 - rotateAmount))) & MaxValueUInt32); + } + + /// + [Pure] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static UInt24 RotateRight(UInt24 value, int rotateAmount) + { + rotateAmount = ((rotateAmount % 24) + 24) % 24; + return new(((value._value >> rotateAmount) | (value._value << (24 - rotateAmount))) & MaxValueUInt32); + } + + /// + public bool TryWriteBigEndian(Span destination, out int bytesWritten) + { + if (destination.Length < 3) + { + bytesWritten = 0; + return false; + } + + destination[0] = (byte)(_value >> 16); + destination[1] = (byte)(_value >> 8); + destination[2] = (byte)_value; + bytesWritten = 3; + return true; + } + + /// + public bool TryWriteLittleEndian(Span destination, out int bytesWritten) + { + if (destination.Length < 3) + { + bytesWritten = 0; + return false; + } + + destination[0] = (byte)_value; + destination[1] = (byte)(_value >> 8); + destination[2] = (byte)(_value >> 16); + bytesWritten = 3; + return true; + } + + /// + static bool IBinaryInteger.TryReadBigEndian(ReadOnlySpan source, bool isUnsigned, out UInt24 value) + { + if (source.Length == 0) + { + value = default; + return false; + } + + // If signed and high bit set, it's negative which is out of range for unsigned. + if (!isUnsigned && (source[0] & 0x80) != 0) + { + value = default; + return false; + } + + uint result; + + if (source.Length >= 4) + { + // Check that any leading bytes beyond the 3 we need are all zero. + for (var i = 0; i < source.Length - 3; i++) + { + if (source[i] != 0) + { + value = default; + return false; + } + } + + var offset = source.Length - 3; + result = (uint)source[offset] << 16 | (uint)source[offset + 1] << 8 | source[offset + 2]; + } + else if (source.Length == 3) + { + result = (uint)source[0] << 16 | (uint)source[1] << 8 | source[2]; + } + else if (source.Length == 2) + { + result = (uint)source[0] << 8 | source[1]; + } + else + { + result = source[0]; + } + + if (result > MaxValueUInt32) + { + value = default; + return false; + } + + value = new UInt24(result); + return true; + } + + /// + static bool IBinaryInteger.TryReadLittleEndian(ReadOnlySpan source, bool isUnsigned, out UInt24 value) + { + if (source.Length == 0) + { + value = default; + return false; + } + + // If signed and high bit of the last byte is set, it's negative. + if (!isUnsigned && (source[^1] & 0x80) != 0) + { + value = default; + return false; + } + + uint result; + + if (source.Length >= 4) + { + // Check that any trailing bytes beyond the 3 we need are all zero. + for (var i = 3; i < source.Length; i++) + { + if (source[i] != 0) + { + value = default; + return false; + } + } + + result = source[0] | (uint)source[1] << 8 | (uint)source[2] << 16; + } + else if (source.Length == 3) + { + result = source[0] | (uint)source[1] << 8 | (uint)source[2] << 16; + } + else if (source.Length == 2) + { + result = source[0] | (uint)source[1] << 8; + } + else + { + result = source[0]; + } + + if (result > MaxValueUInt32) + { + value = default; + return false; + } + + value = new UInt24(result); + return true; + } + + #endregion + + #region CreateChecked/CreateSaturating/CreateTruncating + + /// + [Pure] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static UInt24 CreateChecked(TOther value) + where TOther : INumberBase + { + if (typeof(TOther) == typeof(byte)) + { + return new UInt24((uint)(byte)(object)value); + } + + if (typeof(TOther) == typeof(ushort)) + { + return new UInt24((uint)(ushort)(object)value); + } + + if (typeof(TOther) == typeof(uint)) + { + return new UInt24((uint)(object)value, true); + } + + if (typeof(TOther) == typeof(ulong)) + { + var v = (ulong)(object)value; + if (v > MaxValueUInt32) + { + throw new OverflowException(); + } + + return new UInt24((uint)v); + } + + if (typeof(TOther) == typeof(sbyte)) + { + return new UInt24(checked((uint)(sbyte)(object)value), false); + } + + if (typeof(TOther) == typeof(short)) + { + return new UInt24(checked((uint)(short)(object)value), false); + } + + if (typeof(TOther) == typeof(int)) + { + return new UInt24(checked((uint)(int)(object)value), true); + } + + if (typeof(TOther) == typeof(long)) + { + return new UInt24(checked((uint)(long)(object)value), true); + } + + if (typeof(TOther) == typeof(Half)) + { + return new UInt24(checked((uint)(Half)(object)value), true); + } + + if (typeof(TOther) == typeof(float)) + { + return new UInt24(checked((uint)(float)(object)value), true); + } + + if (typeof(TOther) == typeof(double)) + { + return new UInt24(checked((uint)(double)(object)value), true); + } + + if (typeof(TOther) == typeof(decimal)) + { + return new UInt24(checked((uint)(decimal)(object)value), true); + } + + if (typeof(TOther) == typeof(UInt24)) + { + return (UInt24)(object)value; + } + + throw new NotSupportedException(); + } + + /// + [Pure] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static UInt24 CreateSaturating(TOther value) + where TOther : INumberBase + { + if (typeof(TOther) == typeof(byte)) + { + return new UInt24((uint)(byte)(object)value); + } + + if (typeof(TOther) == typeof(ushort)) + { + return new UInt24((uint)(ushort)(object)value); + } + + if (typeof(TOther) == typeof(uint)) + { + var v = (uint)(object)value; + return new UInt24(v > MaxValueUInt32 ? MaxValueUInt32 : v); + } + + if (typeof(TOther) == typeof(ulong)) + { + var v = (ulong)(object)value; + return new UInt24(v > MaxValueUInt32 ? MaxValueUInt32 : (uint)v); + } + + if (typeof(TOther) == typeof(sbyte)) + { + var v = (sbyte)(object)value; + return new UInt24(v < 0 ? 0u : (uint)v); + } + + if (typeof(TOther) == typeof(short)) + { + var v = (short)(object)value; + return new UInt24(v < 0 ? 0u : (uint)v); + } + + if (typeof(TOther) == typeof(int)) + { + var v = (int)(object)value; + return new UInt24(v < 0 ? 0u : v > (int)MaxValueUInt32 ? MaxValueUInt32 : (uint)v); + } + + if (typeof(TOther) == typeof(long)) + { + var v = (long)(object)value; + return new UInt24(v < 0L ? 0u : v > MaxValueUInt32 ? MaxValueUInt32 : (uint)v); + } + + if (typeof(TOther) == typeof(Half)) + { + var v = (Half)(object)value; + if (Half.IsNaN(v) || v < Half.Zero) + { + return Zero; + } + + return (float)v >= MaxValueUInt32 ? MaxValue : new UInt24((uint)(float)v); + } + + if (typeof(TOther) == typeof(float)) + { + var v = (float)(object)value; + if (float.IsNaN(v) || v < 0f) + { + return Zero; + } + + return v >= MaxValueUInt32 ? MaxValue : new UInt24((uint)v); + } + + if (typeof(TOther) == typeof(double)) + { + var v = (double)(object)value; + if (double.IsNaN(v) || v < 0.0) + { + return Zero; + } + + return v >= MaxValueUInt32 ? MaxValue : new UInt24((uint)v); + } + + if (typeof(TOther) == typeof(decimal)) + { + var v = (decimal)(object)value; + if (v < 0m) + { + return Zero; + } + + return v >= MaxValueUInt32 ? MaxValue : new UInt24((uint)v); + } + + if (typeof(TOther) == typeof(UInt24)) + { + return (UInt24)(object)value; + } + + throw new NotSupportedException(); + } + + /// + [Pure] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static UInt24 CreateTruncating(TOther value) + where TOther : INumberBase + { + if (typeof(TOther) == typeof(byte)) + { + return new UInt24((uint)(byte)(object)value); + } + + if (typeof(TOther) == typeof(ushort)) + { + return new UInt24((uint)(ushort)(object)value); + } + + if (typeof(TOther) == typeof(uint)) + { + return new UInt24((uint)(object)value & MaxValueUInt32); + } + + if (typeof(TOther) == typeof(ulong)) + { + return new UInt24((uint)((ulong)(object)value & MaxValueUInt32)); + } + + if (typeof(TOther) == typeof(sbyte)) + { + return new UInt24((uint)(sbyte)(object)value & MaxValueUInt32); + } + + if (typeof(TOther) == typeof(short)) + { + return new UInt24((uint)(short)(object)value & MaxValueUInt32); + } + + if (typeof(TOther) == typeof(int)) + { + return new UInt24((uint)(int)(object)value & MaxValueUInt32); + } + + if (typeof(TOther) == typeof(long)) + { + return new UInt24((uint)(long)(object)value & MaxValueUInt32); + } + + if (typeof(TOther) == typeof(Half)) + { + return new UInt24((uint)(float)(Half)(object)value & MaxValueUInt32); + } + + if (typeof(TOther) == typeof(float)) + { + return new UInt24((uint)(float)(object)value & MaxValueUInt32); + } + + if (typeof(TOther) == typeof(double)) + { + return new UInt24((uint)(double)(object)value & MaxValueUInt32); + } + + if (typeof(TOther) == typeof(decimal)) + { + return new UInt24((uint)(decimal)(object)value & MaxValueUInt32); + } + + if (typeof(TOther) == typeof(UInt24)) + { + return (UInt24)(object)value; + } + + throw new NotSupportedException(); + } + + #endregion +} \ No newline at end of file From 71eee958a0358743995c7e5b55863a987f98a660 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 20 Feb 2026 18:22:30 +0000 Subject: [PATCH 4/7] Update test files for UInt24 type changes Cast int literals to UInt24 in SetUInt24/WriteUInt24/AddUInt24 calls. Cast UInt24 results to int before Should().Equal() assertions. Fix overflow value 0x78654321 to valid UInt24 value 0x654321. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../ByteArrayExtensionsTests.cs | 24 +++++----- .../ByteICollectionExtensionsTests.cs | 4 +- .../ByteIListExtensionsTests.cs | 24 +++++----- .../ByteIReadOnlyListExtensionsTests.cs | 12 ++--- .../ByteListExtensionsTests.cs | 18 ++++---- .../ByteReadOnlySpanExtensionsTests.cs | 6 +-- .../ByteSpanExtensionsTests.cs | 14 +++--- .../EndianExtensionsTests.cs | 2 +- .../ReadOnlyMemoryExtensionsTests.cs | 12 ++--- .../StreamExtensionsTests.cs | 16 +++---- .../ByteArrayExtensions.cs | 44 +++++++++---------- .../ByteICollectionExtensions.cs | 6 +-- .../ByteIListExtensions.cs | 41 ++++++++--------- .../ByteIReadOnlyListExtensions.cs | 14 +++--- .../ByteListExtensions.cs | 24 +++++----- .../ByteReadOnlySpanExtensions.cs | 14 +++--- .../ByteSpanExtensions.cs | 44 +++++++++---------- .../EndianExtensions.cs | 10 ++--- .../ReadOnlyMemoryExtensions.cs | 12 ++--- .../StreamExtensions.cs | 24 +++++----- 20 files changed, 181 insertions(+), 184 deletions(-) diff --git a/src/MrKWatkins.BinaryPrimitives.Tests/ByteArrayExtensionsTests.cs b/src/MrKWatkins.BinaryPrimitives.Tests/ByteArrayExtensionsTests.cs index 354c105..3503d6c 100644 --- a/src/MrKWatkins.BinaryPrimitives.Tests/ByteArrayExtensionsTests.cs +++ b/src/MrKWatkins.BinaryPrimitives.Tests/ByteArrayExtensionsTests.cs @@ -145,8 +145,8 @@ public void GetUInt24_Array() { byte[] bytes = [0x78, 0x56, 0x34, 0x12]; - bytes.GetUInt24(0).Should().Equal(0x345678); - bytes.GetUInt24(1).Should().Equal(0x123456); + ((int)bytes.GetUInt24(0)).Should().Equal(0x345678); + ((int)bytes.GetUInt24(1)).Should().Equal(0x123456); } @@ -155,10 +155,10 @@ public void GetUInt24_Array_Endian() { byte[] bytes = [0x78, 0x56, 0x34, 0x12]; - bytes.GetUInt24(0, Endian.Little).Should().Equal(0x345678); - bytes.GetUInt24(1, Endian.Little).Should().Equal(0x123456); - bytes.GetUInt24(0, Endian.Big).Should().Equal(0x785634); - bytes.GetUInt24(1, Endian.Big).Should().Equal(0x563412); + ((int)bytes.GetUInt24(0, Endian.Little)).Should().Equal(0x345678); + ((int)bytes.GetUInt24(1, Endian.Little)).Should().Equal(0x123456); + ((int)bytes.GetUInt24(0, Endian.Big)).Should().Equal(0x785634); + ((int)bytes.GetUInt24(1, Endian.Big)).Should().Equal(0x563412); } @@ -167,10 +167,10 @@ public void SetUInt24_Array() { byte[] bytes = [0x00, 0x00, 0x00, 0x00]; - bytes.SetUInt24(1, 0x123456); + bytes.SetUInt24(1, (UInt24)0x123456); bytes.Should().SequenceEqual(0x00, 0x56, 0x34, 0x12); - bytes.SetUInt24(0, 0x654321); + bytes.SetUInt24(0, (UInt24)0x654321); bytes.Should().SequenceEqual(0x21, 0x43, 0x65, 0x12); } @@ -180,16 +180,16 @@ public void SetUInt24_Array_Endian() { byte[] bytes = [0x00, 0x00, 0x00, 0x00]; - bytes.SetUInt24(1, 0x123456, Endian.Little); + bytes.SetUInt24(1, (UInt24)0x123456, Endian.Little); bytes.Should().SequenceEqual(0x00, 0x56, 0x34, 0x12); - bytes.SetUInt24(0, 0x654321, Endian.Little); + bytes.SetUInt24(0, (UInt24)0x654321, Endian.Little); bytes.Should().SequenceEqual(0x21, 0x43, 0x65, 0x12); - bytes.SetUInt24(1, 0x123456, Endian.Big); + bytes.SetUInt24(1, (UInt24)0x123456, Endian.Big); bytes.Should().SequenceEqual(0x21, 0x12, 0x34, 0x56); - bytes.SetUInt24(0, 0x654321, Endian.Big); + bytes.SetUInt24(0, (UInt24)0x654321, Endian.Big); bytes.Should().SequenceEqual(0x65, 0x43, 0x21, 0x56); } diff --git a/src/MrKWatkins.BinaryPrimitives.Tests/ByteICollectionExtensionsTests.cs b/src/MrKWatkins.BinaryPrimitives.Tests/ByteICollectionExtensionsTests.cs index 4dc147d..81b310e 100644 --- a/src/MrKWatkins.BinaryPrimitives.Tests/ByteICollectionExtensionsTests.cs +++ b/src/MrKWatkins.BinaryPrimitives.Tests/ByteICollectionExtensionsTests.cs @@ -31,10 +31,10 @@ public void AddUInt24() { List bytes = [0x01]; - bytes.AddUInt24(0x123456); + bytes.AddUInt24((UInt24)0x123456); bytes.Should().SequenceEqual(0x01, 0x56, 0x34, 0x12); - bytes.AddUInt24(0x789ABC, Endian.Big); + bytes.AddUInt24((UInt24)0x789ABC, Endian.Big); bytes.Should().SequenceEqual(0x01, 0x56, 0x34, 0x12, 0x78, 0x9A, 0xBC); } diff --git a/src/MrKWatkins.BinaryPrimitives.Tests/ByteIListExtensionsTests.cs b/src/MrKWatkins.BinaryPrimitives.Tests/ByteIListExtensionsTests.cs index e5d53a4..9464566 100644 --- a/src/MrKWatkins.BinaryPrimitives.Tests/ByteIListExtensionsTests.cs +++ b/src/MrKWatkins.BinaryPrimitives.Tests/ByteIListExtensionsTests.cs @@ -133,8 +133,8 @@ public void GetUInt24_IList() { IList bytes = [0x78, 0x56, 0x34, 0x12]; - bytes.GetUInt24(0).Should().Equal(0x345678); - bytes.GetUInt24(1).Should().Equal(0x123456); + ((int)bytes.GetUInt24(0)).Should().Equal(0x345678); + ((int)bytes.GetUInt24(1)).Should().Equal(0x123456); } @@ -143,10 +143,10 @@ public void GetUInt24_IList_Endian() { IList bytes = [0x78, 0x56, 0x34, 0x12]; - bytes.GetUInt24(0, Endian.Little).Should().Equal(0x345678); - bytes.GetUInt24(1, Endian.Little).Should().Equal(0x123456); - bytes.GetUInt24(0, Endian.Big).Should().Equal(0x785634); - bytes.GetUInt24(1, Endian.Big).Should().Equal(0x563412); + ((int)bytes.GetUInt24(0, Endian.Little)).Should().Equal(0x345678); + ((int)bytes.GetUInt24(1, Endian.Little)).Should().Equal(0x123456); + ((int)bytes.GetUInt24(0, Endian.Big)).Should().Equal(0x785634); + ((int)bytes.GetUInt24(1, Endian.Big)).Should().Equal(0x563412); } @@ -155,10 +155,10 @@ public void SetUInt24_IList() { IList bytes = [0x00, 0x00, 0x00, 0x00]; - bytes.SetUInt24(1, 0x123456); + bytes.SetUInt24(1, (UInt24)0x123456); bytes.Should().SequenceEqual(0x00, 0x56, 0x34, 0x12); - bytes.SetUInt24(0, 0x78654321); + bytes.SetUInt24(0, (UInt24)0x654321); bytes.Should().SequenceEqual(0x21, 0x43, 0x65, 0x12); } @@ -168,16 +168,16 @@ public void SetUInt24_IList_Endian() { IList bytes = [0x00, 0x00, 0x00, 0x00]; - bytes.SetUInt24(1, 0x123456, Endian.Little); + bytes.SetUInt24(1, (UInt24)0x123456, Endian.Little); bytes.Should().SequenceEqual(0x00, 0x56, 0x34, 0x12); - bytes.SetUInt24(0, 0x78654321, Endian.Little); + bytes.SetUInt24(0, (UInt24)0x654321, Endian.Little); bytes.Should().SequenceEqual(0x21, 0x43, 0x65, 0x12); - bytes.SetUInt24(1, 0x123456, Endian.Big); + bytes.SetUInt24(1, (UInt24)0x123456, Endian.Big); bytes.Should().SequenceEqual(0x21, 0x12, 0x34, 0x56); - bytes.SetUInt24(0, 0x78654321, Endian.Big); + bytes.SetUInt24(0, (UInt24)0x654321, Endian.Big); bytes.Should().SequenceEqual(0x65, 0x43, 0x21, 0x56); } diff --git a/src/MrKWatkins.BinaryPrimitives.Tests/ByteIReadOnlyListExtensionsTests.cs b/src/MrKWatkins.BinaryPrimitives.Tests/ByteIReadOnlyListExtensionsTests.cs index 6654e32..6991855 100644 --- a/src/MrKWatkins.BinaryPrimitives.Tests/ByteIReadOnlyListExtensionsTests.cs +++ b/src/MrKWatkins.BinaryPrimitives.Tests/ByteIReadOnlyListExtensionsTests.cs @@ -64,8 +64,8 @@ public void GetUInt24_IReadOnlyList() { IReadOnlyList bytes = [0x78, 0x56, 0x34, 0x12]; - bytes.GetUInt24(0).Should().Equal(0x345678); - bytes.GetUInt24(1).Should().Equal(0x123456); + ((int)bytes.GetUInt24(0)).Should().Equal(0x345678); + ((int)bytes.GetUInt24(1)).Should().Equal(0x123456); } @@ -74,10 +74,10 @@ public void GetUInt24_IReadOnlyList_Endian() { IReadOnlyList bytes = [0x78, 0x56, 0x34, 0x12]; - bytes.GetUInt24(0, Endian.Little).Should().Equal(0x345678); - bytes.GetUInt24(1, Endian.Little).Should().Equal(0x123456); - bytes.GetUInt24(0, Endian.Big).Should().Equal(0x785634); - bytes.GetUInt24(1, Endian.Big).Should().Equal(0x563412); + ((int)bytes.GetUInt24(0, Endian.Little)).Should().Equal(0x345678); + ((int)bytes.GetUInt24(1, Endian.Little)).Should().Equal(0x123456); + ((int)bytes.GetUInt24(0, Endian.Big)).Should().Equal(0x785634); + ((int)bytes.GetUInt24(1, Endian.Big)).Should().Equal(0x563412); } diff --git a/src/MrKWatkins.BinaryPrimitives.Tests/ByteListExtensionsTests.cs b/src/MrKWatkins.BinaryPrimitives.Tests/ByteListExtensionsTests.cs index f2d904e..0973bec 100644 --- a/src/MrKWatkins.BinaryPrimitives.Tests/ByteListExtensionsTests.cs +++ b/src/MrKWatkins.BinaryPrimitives.Tests/ByteListExtensionsTests.cs @@ -64,8 +64,8 @@ public void GetUInt24_List() { List bytes = [0x78, 0x56, 0x34, 0x12]; - bytes.GetUInt24(0).Should().Equal(0x345678); - bytes.GetUInt24(1).Should().Equal(0x123456); + ((int)bytes.GetUInt24(0)).Should().Equal(0x345678); + ((int)bytes.GetUInt24(1)).Should().Equal(0x123456); } @@ -74,10 +74,10 @@ public void GetUInt24_List_Endian() { List bytes = [0x78, 0x56, 0x34, 0x12]; - bytes.GetUInt24(0, Endian.Little).Should().Equal(0x345678); - bytes.GetUInt24(1, Endian.Little).Should().Equal(0x123456); - bytes.GetUInt24(0, Endian.Big).Should().Equal(0x785634); - bytes.GetUInt24(1, Endian.Big).Should().Equal(0x563412); + ((int)bytes.GetUInt24(0, Endian.Little)).Should().Equal(0x345678); + ((int)bytes.GetUInt24(1, Endian.Little)).Should().Equal(0x123456); + ((int)bytes.GetUInt24(0, Endian.Big)).Should().Equal(0x785634); + ((int)bytes.GetUInt24(1, Endian.Big)).Should().Equal(0x563412); } @@ -209,7 +209,7 @@ public void SetUInt24_List() { List bytes = [0x78, 0x00, 0x00, 0x00, 0x12]; - bytes.SetUInt24(1, 0x123456); + bytes.SetUInt24(1, (UInt24)0x123456); bytes.Should().SequenceEqual(0x78, 0x56, 0x34, 0x12, 0x12); } @@ -218,10 +218,10 @@ public void SetUInt24_List_Endian() { List bytes = [0x78, 0x00, 0x00, 0x00, 0x12]; - bytes.SetUInt24(1, 0x123456, Endian.Little); + bytes.SetUInt24(1, (UInt24)0x123456, Endian.Little); bytes.Should().SequenceEqual(0x78, 0x56, 0x34, 0x12, 0x12); - bytes.SetUInt24(1, 0x123456, Endian.Big); + bytes.SetUInt24(1, (UInt24)0x123456, Endian.Big); bytes.Should().SequenceEqual(0x78, 0x12, 0x34, 0x56, 0x12); } diff --git a/src/MrKWatkins.BinaryPrimitives.Tests/ByteReadOnlySpanExtensionsTests.cs b/src/MrKWatkins.BinaryPrimitives.Tests/ByteReadOnlySpanExtensionsTests.cs index 6eeb709..bd542a0 100644 --- a/src/MrKWatkins.BinaryPrimitives.Tests/ByteReadOnlySpanExtensionsTests.cs +++ b/src/MrKWatkins.BinaryPrimitives.Tests/ByteReadOnlySpanExtensionsTests.cs @@ -64,7 +64,7 @@ public void GetUInt24_ReadOnlySpan() { ReadOnlySpan bytes = [0x78, 0x56, 0x34]; - bytes.GetUInt24().Should().Equal(0x345678); + ((int)bytes.GetUInt24()).Should().Equal(0x345678); } @@ -73,8 +73,8 @@ public void GetUInt24_ReadOnlySpan_Endian() { ReadOnlySpan bytes = [0x78, 0x56, 0x34]; - bytes.GetUInt24(Endian.Little).Should().Equal(0x345678); - bytes.GetUInt24(Endian.Big).Should().Equal(0x785634); + ((int)bytes.GetUInt24(Endian.Little)).Should().Equal(0x345678); + ((int)bytes.GetUInt24(Endian.Big)).Should().Equal(0x785634); } diff --git a/src/MrKWatkins.BinaryPrimitives.Tests/ByteSpanExtensionsTests.cs b/src/MrKWatkins.BinaryPrimitives.Tests/ByteSpanExtensionsTests.cs index e256eff..2b8508a 100644 --- a/src/MrKWatkins.BinaryPrimitives.Tests/ByteSpanExtensionsTests.cs +++ b/src/MrKWatkins.BinaryPrimitives.Tests/ByteSpanExtensionsTests.cs @@ -133,7 +133,7 @@ public void GetUInt24_Span() { Span bytes = [0x78, 0x56, 0x34]; - bytes.GetUInt24().Should().Equal(0x345678); + ((int)bytes.GetUInt24()).Should().Equal(0x345678); } @@ -142,8 +142,8 @@ public void GetUInt24_Span_Endian() { Span bytes = [0x78, 0x56, 0x34]; - bytes.GetUInt24(Endian.Little).Should().Equal(0x345678); - bytes.GetUInt24(Endian.Big).Should().Equal(0x785634); + ((int)bytes.GetUInt24(Endian.Little)).Should().Equal(0x345678); + ((int)bytes.GetUInt24(Endian.Big)).Should().Equal(0x785634); } @@ -152,10 +152,10 @@ public void SetUInt24_Span() { Span bytes = [0x00, 0x00, 0x00]; - bytes.SetUInt24(0x123456); + bytes.SetUInt24((UInt24)0x123456); bytes.ToArray().Should().SequenceEqual(0x56, 0x34, 0x12); - bytes.SetUInt24(0x654321); + bytes.SetUInt24((UInt24)0x654321); bytes.ToArray().Should().SequenceEqual(0x21, 0x43, 0x65); } @@ -165,10 +165,10 @@ public void SetUInt24_Span_Endian() { Span bytes = [0x00, 0x00, 0x00]; - bytes.SetUInt24(0x123456, Endian.Little); + bytes.SetUInt24((UInt24)0x123456, Endian.Little); bytes.ToArray().Should().SequenceEqual(0x56, 0x34, 0x12); - bytes.SetUInt24(0x123456, Endian.Big); + bytes.SetUInt24((UInt24)0x123456, Endian.Big); bytes.ToArray().Should().SequenceEqual(0x12, 0x34, 0x56); } diff --git a/src/MrKWatkins.BinaryPrimitives.Tests/EndianExtensionsTests.cs b/src/MrKWatkins.BinaryPrimitives.Tests/EndianExtensionsTests.cs index b0de397..ad21939 100644 --- a/src/MrKWatkins.BinaryPrimitives.Tests/EndianExtensionsTests.cs +++ b/src/MrKWatkins.BinaryPrimitives.Tests/EndianExtensionsTests.cs @@ -5,7 +5,7 @@ public sealed class EndianExtensionsTests [TestCase(Endian.Big, 0x12, 0x34, 0x56, 0x00123456)] [TestCase(Endian.Little, 0x12, 0x34, 0x56, 0x00563412)] public void ToUInt24(Endian endian, byte byte0, byte byte1, byte byte2, int expected) => - endian.ToUInt24(byte0, byte1, byte2).Should().Equal(expected); + ((int)endian.ToUInt24(byte0, byte1, byte2)).Should().Equal(expected); [Test] diff --git a/src/MrKWatkins.BinaryPrimitives.Tests/ReadOnlyMemoryExtensionsTests.cs b/src/MrKWatkins.BinaryPrimitives.Tests/ReadOnlyMemoryExtensionsTests.cs index 90231e8..9dc7011 100644 --- a/src/MrKWatkins.BinaryPrimitives.Tests/ReadOnlyMemoryExtensionsTests.cs +++ b/src/MrKWatkins.BinaryPrimitives.Tests/ReadOnlyMemoryExtensionsTests.cs @@ -100,8 +100,8 @@ public void GetUInt24_ReadOnlyMemory() { ReadOnlyMemory bytes = new byte[] { 0x78, 0x56, 0x34, 0x12 }; - bytes.GetUInt24(0).Should().Equal(0x345678); - bytes.GetUInt24(1).Should().Equal(0x123456); + ((int)bytes.GetUInt24(0)).Should().Equal(0x345678); + ((int)bytes.GetUInt24(1)).Should().Equal(0x123456); } [Test] @@ -109,10 +109,10 @@ public void GetUInt24_ReadOnlyMemory_Endian() { ReadOnlyMemory bytes = new byte[] { 0x78, 0x56, 0x34, 0x12 }; - bytes.GetUInt24(0, Endian.Little).Should().Equal(0x345678); - bytes.GetUInt24(1, Endian.Little).Should().Equal(0x123456); - bytes.GetUInt24(0, Endian.Big).Should().Equal(0x785634); - bytes.GetUInt24(1, Endian.Big).Should().Equal(0x563412); + ((int)bytes.GetUInt24(0, Endian.Little)).Should().Equal(0x345678); + ((int)bytes.GetUInt24(1, Endian.Little)).Should().Equal(0x123456); + ((int)bytes.GetUInt24(0, Endian.Big)).Should().Equal(0x785634); + ((int)bytes.GetUInt24(1, Endian.Big)).Should().Equal(0x563412); } diff --git a/src/MrKWatkins.BinaryPrimitives.Tests/StreamExtensionsTests.cs b/src/MrKWatkins.BinaryPrimitives.Tests/StreamExtensionsTests.cs index 1b34af7..a3ac576 100644 --- a/src/MrKWatkins.BinaryPrimitives.Tests/StreamExtensionsTests.cs +++ b/src/MrKWatkins.BinaryPrimitives.Tests/StreamExtensionsTests.cs @@ -113,14 +113,14 @@ public void WriteUInt16() public void ReadUInt24OrThrow() { using var stream = new MemoryStream([0x56, 0x34, 0x12]); - stream.ReadUInt24OrThrow().Should().Equal(0x123456); + ((int)stream.ReadUInt24OrThrow()).Should().Equal(0x123456); } [Test] public void ReadUInt24OrThrow_BigEndian() { using var stream = new MemoryStream([0x12, 0x34, 0x56]); - stream.ReadUInt24OrThrow(Endian.Big).Should().Equal(0x123456); + ((int)stream.ReadUInt24OrThrow(Endian.Big)).Should().Equal(0x123456); } [Test] @@ -129,10 +129,10 @@ public void WriteUInt24() using var bytes = new MemoryStream(); bytes.WriteByte(0x01); - bytes.WriteUInt24(0x123456); + bytes.WriteUInt24((UInt24)0x123456); bytes.ToArray().Should().SequenceEqual(0x01, 0x56, 0x34, 0x12); - bytes.WriteUInt24(0x789ABC, Endian.Big); + bytes.WriteUInt24((UInt24)0x789ABC, Endian.Big); bytes.ToArray().Should().SequenceEqual(0x01, 0x56, 0x34, 0x12, 0x78, 0x9A, 0xBC); } @@ -353,14 +353,14 @@ public async Task WriteUInt16Async() public async Task ReadUInt24OrThrowAsync() { using var stream = new MemoryStream([0x56, 0x34, 0x12]); - (await stream.ReadUInt24OrThrowAsync()).Should().Equal(0x123456); + ((int)await stream.ReadUInt24OrThrowAsync()).Should().Equal(0x123456); } [Test] public async Task ReadUInt24OrThrowAsync_BigEndian() { using var stream = new MemoryStream([0x12, 0x34, 0x56]); - (await stream.ReadUInt24OrThrowAsync(Endian.Big)).Should().Equal(0x123456); + ((int)await stream.ReadUInt24OrThrowAsync(Endian.Big)).Should().Equal(0x123456); } [Test] @@ -369,10 +369,10 @@ public async Task WriteUInt24Async() using var bytes = new MemoryStream(); bytes.WriteByte(0x01); - await bytes.WriteUInt24Async(0x123456); + await bytes.WriteUInt24Async((UInt24)0x123456); bytes.ToArray().Should().SequenceEqual(0x01, 0x56, 0x34, 0x12); - await bytes.WriteUInt24Async(0x789ABC, Endian.Big); + await bytes.WriteUInt24Async((UInt24)0x789ABC, Endian.Big); bytes.ToArray().Should().SequenceEqual(0x01, 0x56, 0x34, 0x12, 0x78, 0x9A, 0xBC); } diff --git a/src/MrKWatkins.BinaryPrimitives/ByteArrayExtensions.cs b/src/MrKWatkins.BinaryPrimitives/ByteArrayExtensions.cs index 8ec3fe4..8101d1c 100644 --- a/src/MrKWatkins.BinaryPrimitives/ByteArrayExtensions.cs +++ b/src/MrKWatkins.BinaryPrimitives/ByteArrayExtensions.cs @@ -1,4 +1,3 @@ -using System.Diagnostics; using System.Runtime.CompilerServices; namespace MrKWatkins.BinaryPrimitives; @@ -147,54 +146,53 @@ public long GetInt64(int index, Endian endian) => /// - /// Reads a little-endian unsigned 24-bit integer from a byte array at the specified index. + /// Reads a little-endian from a byte array at the specified index. /// /// The zero-based index to read from. - /// The 24-bit value stored in an . + /// The value. [Pure] [OverloadResolutionPriority(ConcreteTypePriority)] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public int GetUInt24(int index) => Unsafe.ReadUnaligned(ref bytes[index]) | bytes[index + 2] << 16; + public UInt24 GetUInt24(int index) => new((uint)(Unsafe.ReadUnaligned(ref bytes[index]) | bytes[index + 2] << 16)); /// - /// Reads an unsigned 24-bit integer from a byte array at the specified index using the specified endianness. + /// Reads a from a byte array at the specified index using the specified endianness. /// /// The zero-based index to read from. /// The endianness to use. - /// The 24-bit value stored in an . + /// The value. [Pure] [OverloadResolutionPriority(ConcreteTypePriority)] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public int GetUInt24(int index, Endian endian) => + public UInt24 GetUInt24(int index, Endian endian) => endian == Endian.Little ? bytes.GetUInt24(index) - : bytes[index] << 16 | bytes[index + 1] << 8 | bytes[index + 2]; + : new((uint)(bytes[index] << 16 | bytes[index + 1] << 8 | bytes[index + 2])); /// - /// Writes a little-endian unsigned 24-bit integer to a byte array at the specified index. + /// Writes a little-endian to a byte array at the specified index. /// /// The zero-based index to write to. - /// The 24-bit value to write. Only the lower 24 bits are used. + /// The value to write. [OverloadResolutionPriority(ConcreteTypePriority)] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void SetUInt24(int index, int value) + public void SetUInt24(int index, UInt24 value) { - Debug.Assert((value & ~0xFFFFFF) == 0, "Value exceeds 24 bits."); - value &= 0xFFFFFF; - bytes[index] = (byte)value; - bytes[index + 1] = (byte)(value >> 8); - bytes[index + 2] = (byte)(value >> 16); + uint v = value; + bytes[index] = (byte)v; + bytes[index + 1] = (byte)(v >> 8); + bytes[index + 2] = (byte)(v >> 16); } /// - /// Writes an unsigned 24-bit integer to a byte array at the specified index using the specified endianness. + /// Writes a to a byte array at the specified index using the specified endianness. /// /// The zero-based index to write to. - /// The 24-bit value to write. Only the lower 24 bits are used. + /// The value to write. /// The endianness to use. [OverloadResolutionPriority(ConcreteTypePriority)] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void SetUInt24(int index, int value, Endian endian) + public void SetUInt24(int index, UInt24 value, Endian endian) { if (endian == Endian.Little) { @@ -202,10 +200,10 @@ public void SetUInt24(int index, int value, Endian endian) } else { - Debug.Assert((value & ~0xFFFFFF) == 0, "Value exceeds 24 bits."); - bytes[index] = (byte)(value >> 16); - bytes[index + 1] = (byte)(value >> 8); - bytes[index + 2] = (byte)value; + uint v = value; + bytes[index] = (byte)(v >> 16); + bytes[index + 1] = (byte)(v >> 8); + bytes[index + 2] = (byte)v; } } diff --git a/src/MrKWatkins.BinaryPrimitives/ByteICollectionExtensions.cs b/src/MrKWatkins.BinaryPrimitives/ByteICollectionExtensions.cs index 703e52d..ac769ed 100644 --- a/src/MrKWatkins.BinaryPrimitives/ByteICollectionExtensions.cs +++ b/src/MrKWatkins.BinaryPrimitives/ByteICollectionExtensions.cs @@ -42,11 +42,11 @@ public void AddInt16(short value, Endian endian = Endian.Little) } /// - /// Adds an unsigned 24-bit integer to a byte collection. + /// Adds a to a byte collection. /// - /// The 24-bit value to add. Only the lower 24 bits are used. + /// The value to add. /// The endianness to use. - public void AddUInt24(int value, Endian endian = Endian.Little) + public void AddUInt24(UInt24 value, Endian endian = Endian.Little) { Span buffer = stackalloc byte[3]; buffer.SetUInt24(value, endian); diff --git a/src/MrKWatkins.BinaryPrimitives/ByteIListExtensions.cs b/src/MrKWatkins.BinaryPrimitives/ByteIListExtensions.cs index a4a40ae..1ab11d4 100644 --- a/src/MrKWatkins.BinaryPrimitives/ByteIListExtensions.cs +++ b/src/MrKWatkins.BinaryPrimitives/ByteIListExtensions.cs @@ -200,49 +200,49 @@ public void SetInt64(int index, long value, Endian endian) /// - /// Reads a little-endian unsigned 24-bit integer from a list of bytes at the specified index. + /// Reads a little-endian from a list of bytes at the specified index. /// /// The zero-based index to read from. - /// The 24-bit value stored in an . + /// The value. [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public int GetUInt24(int index) => bytes[index] | bytes[index + 1] << 8 | bytes[index + 2] << 16; + public UInt24 GetUInt24(int index) => new((uint)(bytes[index] | bytes[index + 1] << 8 | bytes[index + 2] << 16)); /// - /// Reads an unsigned 24-bit integer from a list of bytes at the specified index using the specified endianness. + /// Reads a from a list of bytes at the specified index using the specified endianness. /// /// The zero-based index to read from. /// The endianness to use. - /// The 24-bit value stored in an . + /// The value. [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public int GetUInt24(int index, Endian endian) => + public UInt24 GetUInt24(int index, Endian endian) => endian == Endian.Little ? bytes.GetUInt24(index) - : bytes[index] << 16 | bytes[index + 1] << 8 | bytes[index + 2]; + : new((uint)(bytes[index] << 16 | bytes[index + 1] << 8 | bytes[index + 2])); /// - /// Writes a little-endian unsigned 24-bit integer to a list of bytes at the specified index. + /// Writes a little-endian to a list of bytes at the specified index. /// /// The zero-based index to write to. - /// The 24-bit value to write. Only the lower 24 bits are used. + /// The value to write. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void SetUInt24(int index, int value) + public void SetUInt24(int index, UInt24 value) { - value &= 0xFFFFFF; - bytes[index] = (byte)value; - bytes[index + 1] = (byte)(value >> 8); - bytes[index + 2] = (byte)(value >> 16); + uint v = value; + bytes[index] = (byte)v; + bytes[index + 1] = (byte)(v >> 8); + bytes[index + 2] = (byte)(v >> 16); } /// - /// Writes an unsigned 24-bit integer to a list of bytes at the specified index using the specified endianness. + /// Writes a to a list of bytes at the specified index using the specified endianness. /// /// The zero-based index to write to. - /// The 24-bit value to write. Only the lower 24 bits are used. + /// The value to write. /// The endianness to use. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void SetUInt24(int index, int value, Endian endian) + public void SetUInt24(int index, UInt24 value, Endian endian) { if (endian == Endian.Little) { @@ -250,9 +250,10 @@ public void SetUInt24(int index, int value, Endian endian) } else { - bytes[index] = (byte)(value >> 16); - bytes[index + 1] = (byte)(value >> 8); - bytes[index + 2] = (byte)value; + uint v = value; + bytes[index] = (byte)(v >> 16); + bytes[index + 1] = (byte)(v >> 8); + bytes[index + 2] = (byte)v; } } diff --git a/src/MrKWatkins.BinaryPrimitives/ByteIReadOnlyListExtensions.cs b/src/MrKWatkins.BinaryPrimitives/ByteIReadOnlyListExtensions.cs index 996e3d5..74142d0 100644 --- a/src/MrKWatkins.BinaryPrimitives/ByteIReadOnlyListExtensions.cs +++ b/src/MrKWatkins.BinaryPrimitives/ByteIReadOnlyListExtensions.cs @@ -128,28 +128,28 @@ public long GetInt64(int index, Endian endian) => /// - /// Reads a little-endian unsigned 24-bit integer from a read-only list of bytes at the specified index. + /// Reads a little-endian from a read-only list of bytes at the specified index. /// /// The zero-based index to read from. - /// The 24-bit value stored in an . + /// The value. [Pure] [OverloadResolutionPriority(ReadOnlyPriority)] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public int GetUInt24(int index) => bytes[index] | bytes[index + 1] << 8 | bytes[index + 2] << 16; + public UInt24 GetUInt24(int index) => new((uint)(bytes[index] | bytes[index + 1] << 8 | bytes[index + 2] << 16)); /// - /// Reads an unsigned 24-bit integer from a read-only list of bytes at the specified index using the specified endianness. + /// Reads a from a read-only list of bytes at the specified index using the specified endianness. /// /// The zero-based index to read from. /// The endianness to use. - /// The 24-bit value stored in an . + /// The value. [Pure] [OverloadResolutionPriority(ReadOnlyPriority)] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public int GetUInt24(int index, Endian endian) => + public UInt24 GetUInt24(int index, Endian endian) => endian == Endian.Little ? bytes.GetUInt24(index) - : bytes[index] << 16 | bytes[index + 1] << 8 | bytes[index + 2]; + : new((uint)(bytes[index] << 16 | bytes[index + 1] << 8 | bytes[index + 2])); /// diff --git a/src/MrKWatkins.BinaryPrimitives/ByteListExtensions.cs b/src/MrKWatkins.BinaryPrimitives/ByteListExtensions.cs index d80f9f0..1288b83 100644 --- a/src/MrKWatkins.BinaryPrimitives/ByteListExtensions.cs +++ b/src/MrKWatkins.BinaryPrimitives/ByteListExtensions.cs @@ -87,26 +87,26 @@ public long GetInt64(int index, Endian endian) => /// - /// Reads a little-endian unsigned 24-bit integer from a of bytes at the specified index. + /// Reads a little-endian from a of bytes at the specified index. /// /// The zero-based index to read from. - /// The 24-bit value stored in an . + /// The value. [Pure] [OverloadResolutionPriority(ConcreteTypePriority)] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public int GetUInt24(int index) => + public UInt24 GetUInt24(int index) => CollectionsMarshal.AsSpan(bytes)[index..].GetUInt24(); /// - /// Reads an unsigned 24-bit integer from a of bytes at the specified index using the specified endianness. + /// Reads a from a of bytes at the specified index using the specified endianness. /// /// The zero-based index to read from. /// The endianness to use. - /// The 24-bit value stored in an . + /// The value. [Pure] [OverloadResolutionPriority(ConcreteTypePriority)] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public int GetUInt24(int index, Endian endian) => + public UInt24 GetUInt24(int index, Endian endian) => CollectionsMarshal.AsSpan(bytes)[index..].GetUInt24(endian); @@ -248,24 +248,24 @@ public void SetInt64(int index, long value, Endian endian) => /// - /// Writes a little-endian unsigned 24-bit integer to a of bytes at the specified index. + /// Writes a little-endian to a of bytes at the specified index. /// /// The zero-based index to write to. - /// The 24-bit value to write. Only the lower 24 bits are used. + /// The value to write. [OverloadResolutionPriority(ConcreteTypePriority)] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void SetUInt24(int index, int value) => + public void SetUInt24(int index, UInt24 value) => CollectionsMarshal.AsSpan(bytes)[index..].SetUInt24(value); /// - /// Writes an unsigned 24-bit integer to a of bytes at the specified index using the specified endianness. + /// Writes a to a of bytes at the specified index using the specified endianness. /// /// The zero-based index to write to. - /// The 24-bit value to write. Only the lower 24 bits are used. + /// The value to write. /// The endianness to use. [OverloadResolutionPriority(ConcreteTypePriority)] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void SetUInt24(int index, int value, Endian endian) => + public void SetUInt24(int index, UInt24 value, Endian endian) => CollectionsMarshal.AsSpan(bytes)[index..].SetUInt24(value, endian); diff --git a/src/MrKWatkins.BinaryPrimitives/ByteReadOnlySpanExtensions.cs b/src/MrKWatkins.BinaryPrimitives/ByteReadOnlySpanExtensions.cs index 51efb80..65de23a 100644 --- a/src/MrKWatkins.BinaryPrimitives/ByteReadOnlySpanExtensions.cs +++ b/src/MrKWatkins.BinaryPrimitives/ByteReadOnlySpanExtensions.cs @@ -76,24 +76,24 @@ public long GetInt64(Endian endian) => /// - /// Reads a little-endian unsigned 24-bit integer from a read-only span of bytes. + /// Reads a little-endian from a read-only span of bytes. /// - /// The 24-bit value stored in an . + /// The value. [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public int GetUInt24() => bytes[0] | bytes[1] << 8 | bytes[2] << 16; + public UInt24 GetUInt24() => new((uint)(bytes[0] | bytes[1] << 8 | bytes[2] << 16)); /// - /// Reads an unsigned 24-bit integer from a read-only span of bytes using the specified endianness. + /// Reads a from a read-only span of bytes using the specified endianness. /// /// The endianness to use. - /// The 24-bit value stored in an . + /// The value. [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public int GetUInt24(Endian endian) => + public UInt24 GetUInt24(Endian endian) => endian == Endian.Little ? bytes.GetUInt24() - : bytes[0] << 16 | bytes[1] << 8 | bytes[2]; + : new((uint)(bytes[0] << 16 | bytes[1] << 8 | bytes[2])); /// diff --git a/src/MrKWatkins.BinaryPrimitives/ByteSpanExtensions.cs b/src/MrKWatkins.BinaryPrimitives/ByteSpanExtensions.cs index 6e2a972..5a46aa5 100644 --- a/src/MrKWatkins.BinaryPrimitives/ByteSpanExtensions.cs +++ b/src/MrKWatkins.BinaryPrimitives/ByteSpanExtensions.cs @@ -1,4 +1,3 @@ -using System.Diagnostics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; @@ -152,46 +151,45 @@ public void SetInt64(long value, Endian endian) /// - /// Reads a little-endian unsigned 24-bit integer from a span of bytes. + /// Reads a little-endian from a span of bytes. /// - /// The 24-bit value stored in an . + /// The value. [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public int GetUInt24() => bytes[0] | bytes[1] << 8 | bytes[2] << 16; + public UInt24 GetUInt24() => new((uint)(bytes[0] | bytes[1] << 8 | bytes[2] << 16)); /// - /// Reads an unsigned 24-bit integer from a span of bytes using the specified endianness. + /// Reads a from a span of bytes using the specified endianness. /// /// The endianness to use. - /// The 24-bit value stored in an . + /// The value. [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public int GetUInt24(Endian endian) => + public UInt24 GetUInt24(Endian endian) => endian == Endian.Little ? bytes.GetUInt24() - : bytes[0] << 16 | bytes[1] << 8 | bytes[2]; + : new((uint)(bytes[0] << 16 | bytes[1] << 8 | bytes[2])); /// - /// Writes a little-endian unsigned 24-bit integer to a span of bytes. + /// Writes a little-endian to a span of bytes. /// - /// The 24-bit value to write. Only the lower 24 bits are used. + /// The value to write. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void SetUInt24(int value) + public void SetUInt24(UInt24 value) { - Debug.Assert((value & ~0xFFFFFF) == 0, "Value exceeds 24 bits."); - value &= 0xFFFFFF; - bytes[0] = (byte)value; - bytes[1] = (byte)(value >> 8); - bytes[2] = (byte)(value >> 16); + uint v = value; + bytes[0] = (byte)v; + bytes[1] = (byte)(v >> 8); + bytes[2] = (byte)(v >> 16); } /// - /// Writes an unsigned 24-bit integer to a span of bytes using the specified endianness. + /// Writes a to a span of bytes using the specified endianness. /// - /// The 24-bit value to write. Only the lower 24 bits are used. + /// The value to write. /// The endianness to use. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void SetUInt24(int value, Endian endian) + public void SetUInt24(UInt24 value, Endian endian) { if (endian == Endian.Little) { @@ -199,10 +197,10 @@ public void SetUInt24(int value, Endian endian) } else { - Debug.Assert((value & ~0xFFFFFF) == 0, "Value exceeds 24 bits."); - bytes[0] = (byte)(value >> 16); - bytes[1] = (byte)(value >> 8); - bytes[2] = (byte)value; + uint v = value; + bytes[0] = (byte)(v >> 16); + bytes[1] = (byte)(v >> 8); + bytes[2] = (byte)v; } } diff --git a/src/MrKWatkins.BinaryPrimitives/EndianExtensions.cs b/src/MrKWatkins.BinaryPrimitives/EndianExtensions.cs index 9aa9dbe..14da255 100644 --- a/src/MrKWatkins.BinaryPrimitives/EndianExtensions.cs +++ b/src/MrKWatkins.BinaryPrimitives/EndianExtensions.cs @@ -11,22 +11,22 @@ public static class EndianExtensions extension(Endian endian) { /// - /// Composes an unsigned 24-bit integer from three bytes. + /// Composes a from three bytes. /// /// The first byte. /// The second byte. /// The third byte. - /// The composed 24-bit value stored in an . + /// The composed value. [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public int ToUInt24(byte byte0, byte byte1, byte byte2) + public UInt24 ToUInt24(byte byte0, byte byte1, byte byte2) { if (endian == Endian.Little) { - return byte0 | byte1 << 8 | byte2 << 16; + return new UInt24((uint)(byte0 | byte1 << 8 | byte2 << 16)); } - return byte2 | byte1 << 8 | byte0 << 16; + return new UInt24((uint)(byte2 | byte1 << 8 | byte0 << 16)); } /// diff --git a/src/MrKWatkins.BinaryPrimitives/ReadOnlyMemoryExtensions.cs b/src/MrKWatkins.BinaryPrimitives/ReadOnlyMemoryExtensions.cs index cab3535..43c0d3d 100644 --- a/src/MrKWatkins.BinaryPrimitives/ReadOnlyMemoryExtensions.cs +++ b/src/MrKWatkins.BinaryPrimitives/ReadOnlyMemoryExtensions.cs @@ -88,23 +88,23 @@ public static ReadOnlySequence CreateWrappedSequence(this ReadOnlyMemory - /// Reads a little-endian unsigned 24-bit integer from read-only memory at the specified index. + /// Reads a little-endian from read-only memory at the specified index. /// /// The zero-based index to read from. - /// The 24-bit value stored in an . + /// The value. [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public int GetUInt24(int index) => bytes.Span[index..].GetUInt24(); + public UInt24 GetUInt24(int index) => bytes.Span[index..].GetUInt24(); /// - /// Reads an unsigned 24-bit integer from read-only memory at the specified index using the specified endianness. + /// Reads a from read-only memory at the specified index using the specified endianness. /// /// The zero-based index to read from. /// The endianness to use. - /// The 24-bit value stored in an . + /// The value. [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public int GetUInt24(int index, Endian endian) => bytes.Span[index..].GetUInt24(endian); + public UInt24 GetUInt24(int index, Endian endian) => bytes.Span[index..].GetUInt24(endian); /// diff --git a/src/MrKWatkins.BinaryPrimitives/StreamExtensions.cs b/src/MrKWatkins.BinaryPrimitives/StreamExtensions.cs index 4c1985e..e8b6a38 100644 --- a/src/MrKWatkins.BinaryPrimitives/StreamExtensions.cs +++ b/src/MrKWatkins.BinaryPrimitives/StreamExtensions.cs @@ -94,20 +94,20 @@ public void WriteUInt16(ushort value, Endian endian = Endian.Little) } /// - /// Reads an unsigned 24-bit integer from the stream, throwing if the end of the stream has been reached. + /// Reads a from the stream, throwing if the end of the stream has been reached. /// /// The endianness to use. - /// The 24-bit value stored in an . + /// The value read from the stream. /// The end of the stream has been reached. [MustUseReturnValue] - public int ReadUInt24OrThrow(Endian endian = Endian.Little) => stream.ReadExactly(3).GetUInt24(0, endian); + public UInt24 ReadUInt24OrThrow(Endian endian = Endian.Little) => stream.ReadExactly(3).GetUInt24(0, endian); /// - /// Writes an unsigned 24-bit integer to the stream. + /// Writes a to the stream. /// - /// The 24-bit value to write. Only the lower 24 bits are used. + /// The value to write. /// The endianness to use. - public void WriteUInt24(int value, Endian endian = Endian.Little) + public void WriteUInt24(UInt24 value, Endian endian = Endian.Little) { Span bytes = stackalloc byte[3]; bytes.SetUInt24(value, endian); @@ -295,26 +295,26 @@ public async ValueTask WriteUInt16Async(ushort value, Endian endian = Endian.Lit } /// - /// Reads an unsigned 24-bit integer from the stream asynchronously, throwing if the end of the stream has been reached. + /// Reads a from the stream asynchronously, throwing if the end of the stream has been reached. /// /// The endianness to use. /// A cancellation token. - /// The 24-bit value stored in an . + /// The value read from the stream. /// The end of the stream has been reached. [MustUseReturnValue] - public async ValueTask ReadUInt24OrThrowAsync(Endian endian = Endian.Little, CancellationToken cancellationToken = default) + public async ValueTask ReadUInt24OrThrowAsync(Endian endian = Endian.Little, CancellationToken cancellationToken = default) { var buffer = await stream.ReadExactlyAsync(3, cancellationToken).ConfigureAwait(false); return buffer.GetUInt24(0, endian); } /// - /// Writes an unsigned 24-bit integer to the stream asynchronously. + /// Writes a to the stream asynchronously. /// - /// The 24-bit value to write. Only the lower 24 bits are used. + /// The value to write. /// The endianness to use. /// A cancellation token. - public async ValueTask WriteUInt24Async(int value, Endian endian = Endian.Little, CancellationToken cancellationToken = default) + public async ValueTask WriteUInt24Async(UInt24 value, Endian endian = Endian.Little, CancellationToken cancellationToken = default) { var bytes = new byte[3]; bytes.SetUInt24(0, value, endian); From 3c58d354a6665c3d7d412ee27ad4bd03dbafd847 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 20 Feb 2026 18:28:35 +0000 Subject: [PATCH 5/7] Add comprehensive tests for UInt24 struct Cover constants, equality, comparison, arithmetic (checked/unchecked), bitwise/shift operators, conversion operators, formatting, parsing, INumber, IBinaryNumber, IBinaryInteger, and INumberBase methods. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../UInt24Tests.cs | 637 ++++++++++++++++++ 1 file changed, 637 insertions(+) create mode 100644 src/MrKWatkins.BinaryPrimitives.Tests/UInt24Tests.cs diff --git a/src/MrKWatkins.BinaryPrimitives.Tests/UInt24Tests.cs b/src/MrKWatkins.BinaryPrimitives.Tests/UInt24Tests.cs new file mode 100644 index 0000000..9218b97 --- /dev/null +++ b/src/MrKWatkins.BinaryPrimitives.Tests/UInt24Tests.cs @@ -0,0 +1,637 @@ +using System.Globalization; + +namespace MrKWatkins.BinaryPrimitives.Tests; + +public sealed class UInt24Tests +{ + #region Constants and Properties + + [Test] + public void MinValue() => ((int)UInt24.MinValue).Should().Equal(0); + + [Test] + public void MaxValue() => ((int)UInt24.MaxValue).Should().Equal(16777215); + + [Test] + public void Zero() => ((int)UInt24.Zero).Should().Equal(0); + + [Test] + public void One() => ((int)UInt24.One).Should().Equal(1); + + [Test] + public void Radix() => UInt24.Radix.Should().Equal(2); + + [Test] + public void AdditiveIdentity() => ((int)UInt24.AdditiveIdentity).Should().Equal(0); + + [Test] + public void MultiplicativeIdentity() => ((int)UInt24.MultiplicativeIdentity).Should().Equal(1); + + #endregion + + #region Equality + + [TestCase(0, 0, true)] + [TestCase(100, 100, true)] + [TestCase(100, 200, false)] + [TestCase(0, 1, false)] + public void Equals_UInt24(int left, int right, bool expected) + => ((UInt24)left).Equals((UInt24)right).Should().Equal(expected); + + [TestCase(100, true)] + [TestCase(200, false)] + public void Equals_Object(int other, bool expected) + => ((UInt24)100).Equals((object)(UInt24)other).Should().Equal(expected); + + [Test] + public void Equals_Object_Null() + => ((UInt24)100).Equals(null).Should().Equal(false); + + [Test] + public void Equals_Object_WrongType() + => ((UInt24)100).Equals((object)100).Should().Equal(false); + + [TestCase(100, 100)] + [TestCase(0, 0)] + [TestCase(16777215, 16777215)] + public void GetHashCode_EqualValues(int left, int right) + => ((UInt24)left).GetHashCode().Should().Equal(((UInt24)right).GetHashCode()); + + #endregion + + #region CompareTo + + [TestCase(100, 200, -1)] + [TestCase(200, 200, 0)] + [TestCase(200, 100, 1)] + public void CompareTo_UInt24(int left, int right, int expectedSign) + => Math.Sign(((UInt24)left).CompareTo((UInt24)right)).Should().Equal(expectedSign); + + [TestCase(100, 200, -1)] + [TestCase(200, 200, 0)] + [TestCase(200, 100, 1)] + public void CompareTo_Object(int left, int right, int expectedSign) + => Math.Sign(((UInt24)left).CompareTo((object)(UInt24)right)).Should().Equal(expectedSign); + + [Test] + public void CompareTo_Object_Null() + => ((UInt24)100).CompareTo(null).Should().Equal(1); + + [Test] + public void CompareTo_Object_WrongType() + => AssertThat.Invoking(() => ((UInt24)100).CompareTo((object)100)).Should().Throw(); + + #endregion + + #region Comparison Operators + + [TestCase(100, 100, true)] + [TestCase(100, 200, false)] + public void EqualityOperator(int left, int right, bool expected) + => ((UInt24)left == (UInt24)right).Should().Equal(expected); + + [TestCase(100, 100, false)] + [TestCase(100, 200, true)] + public void InequalityOperator(int left, int right, bool expected) + => ((UInt24)left != (UInt24)right).Should().Equal(expected); + + [TestCase(100, 200, true)] + [TestCase(200, 200, false)] + [TestCase(200, 100, false)] + public void LessThanOperator(int left, int right, bool expected) + => ((UInt24)left < (UInt24)right).Should().Equal(expected); + + [TestCase(200, 100, true)] + [TestCase(200, 200, false)] + [TestCase(100, 200, false)] + public void GreaterThanOperator(int left, int right, bool expected) + => ((UInt24)left > (UInt24)right).Should().Equal(expected); + + [TestCase(100, 200, true)] + [TestCase(200, 200, true)] + [TestCase(200, 100, false)] + public void LessThanOrEqualOperator(int left, int right, bool expected) + => ((UInt24)left <= (UInt24)right).Should().Equal(expected); + + [TestCase(200, 100, true)] + [TestCase(200, 200, true)] + [TestCase(100, 200, false)] + public void GreaterThanOrEqualOperator(int left, int right, bool expected) + => ((UInt24)left >= (UInt24)right).Should().Equal(expected); + + #endregion + + #region Arithmetic Operators + + [TestCase(0, 1, 1)] + [TestCase(100, 200, 300)] + [TestCase(16777215, 1, 0)] + [TestCase(16777215, 2, 1)] + public void Addition(int left, int right, int expected) + => ((int)((UInt24)left + (UInt24)right)).Should().Equal(expected); + + [TestCase(1, 0, 1)] + [TestCase(300, 200, 100)] + [TestCase(0, 1, 16777215)] + public void Subtraction(int left, int right, int expected) + => ((int)((UInt24)left - (UInt24)right)).Should().Equal(expected); + + [TestCase(100, 200, 20000)] + [TestCase(5000, 5000, 8222784)] + public void Multiplication(int left, int right, int expected) + => ((int)((UInt24)left * (UInt24)right)).Should().Equal(expected); + + [TestCase(200, 100, 2)] + [TestCase(10, 3, 3)] + [TestCase(0, 1, 0)] + public void Division(int left, int right, int expected) + => ((int)((UInt24)left / (UInt24)right)).Should().Equal(expected); + + [TestCase(10, 3, 1)] + [TestCase(200, 100, 0)] + [TestCase(7, 4, 3)] + public void Modulus(int left, int right, int expected) + => ((int)((UInt24)left % (UInt24)right)).Should().Equal(expected); + + [TestCase(0, 1)] + [TestCase(100, 101)] + [TestCase(16777215, 0)] + public void Increment(int value, int expected) + { + var v = (UInt24)value; + v++; + ((int)v).Should().Equal(expected); + } + + [TestCase(1, 0)] + [TestCase(100, 99)] + [TestCase(0, 16777215)] + public void Decrement(int value, int expected) + { + var v = (UInt24)value; + v--; + ((int)v).Should().Equal(expected); + } + + [TestCase(0)] + [TestCase(100)] + [TestCase(16777215)] + public void UnaryPlus(int value) + => ((int)(+(UInt24)value)).Should().Equal(value); + + [TestCase(0, 0)] + [TestCase(1, 16777215)] + public void UnaryMinus(int value, int expected) + => ((int)(-(UInt24)value)).Should().Equal(expected); + + #endregion + + #region Checked Arithmetic + + [Test] + public void CheckedAddition() + => AssertThat.Invoking(() => checked((UInt24)16777215 + (UInt24)1)).Should().Throw(); + + [TestCase(0, 1, 1)] + [TestCase(100, 200, 300)] + public void CheckedAddition_NoOverflow(int left, int right, int expected) + => ((int)checked((UInt24)left + (UInt24)right)).Should().Equal(expected); + + [Test] + public void CheckedSubtraction() + => AssertThat.Invoking(() => checked((UInt24)0 - (UInt24)1)).Should().Throw(); + + [TestCase(300, 200, 100)] + [TestCase(1, 0, 1)] + public void CheckedSubtraction_NoOverflow(int left, int right, int expected) + => ((int)checked((UInt24)left - (UInt24)right)).Should().Equal(expected); + + [Test] + public void CheckedMultiplication() + => AssertThat.Invoking(() => checked((UInt24)16777215 * (UInt24)2)).Should().Throw(); + + [TestCase(100, 200, 20000)] + public void CheckedMultiplication_NoOverflow(int left, int right, int expected) + => ((int)checked((UInt24)left * (UInt24)right)).Should().Equal(expected); + + [Test] + public void CheckedIncrement() + { + var v = UInt24.MaxValue; + AssertThat.Invoking(() => checked(v++)).Should().Throw(); + } + + [Test] + public void CheckedDecrement() + { + var v = UInt24.MinValue; + AssertThat.Invoking(() => checked(v--)).Should().Throw(); + } + + [Test] + public void CheckedUnaryMinus() + => AssertThat.Invoking(() => checked(-(UInt24)1)).Should().Throw(); + + [Test] + public void CheckedUnaryMinus_Zero() + => ((int)checked(-(UInt24)0)).Should().Equal(0); + + #endregion + + #region Bitwise Operators + + [TestCase(0xFF00FF, 0x00FF00, 0x000000)] + [TestCase(0xFFFFFF, 0x00FF00, 0x00FF00)] + public void BitwiseAnd(int left, int right, int expected) + => ((int)((UInt24)left & (UInt24)right)).Should().Equal(expected); + + [TestCase(0xFF0000, 0x00FF00, 0xFFFF00)] + [TestCase(0x000000, 0x00FF00, 0x00FF00)] + public void BitwiseOr(int left, int right, int expected) + => ((int)((UInt24)left | (UInt24)right)).Should().Equal(expected); + + [TestCase(0xFF00FF, 0xFF0000, 0x0000FF)] + [TestCase(0xFFFFFF, 0x000000, 0xFFFFFF)] + public void BitwiseXor(int left, int right, int expected) + => ((int)((UInt24)left ^ (UInt24)right)).Should().Equal(expected); + + [TestCase(0x000000, 0xFFFFFF)] + [TestCase(0xFF00FF, 0x00FF00)] + [TestCase(0xFFFFFF, 0x000000)] + public void BitwiseNot(int value, int expected) + => ((int)(~(UInt24)value)).Should().Equal(expected); + + [TestCase(1, 1, 2)] + [TestCase(1, 8, 256)] + [TestCase(0x800000, 1, 0)] + public void LeftShift(int value, int shift, int expected) + => ((int)((UInt24)value << shift)).Should().Equal(expected); + + [TestCase(256, 1, 128)] + [TestCase(256, 8, 1)] + [TestCase(0xFFFFFF, 12, 0xFFF)] + public void RightShift(int value, int shift, int expected) + => ((int)((UInt24)value >> shift)).Should().Equal(expected); + + [TestCase(256, 1, 128)] + [TestCase(0xFFFFFF, 12, 0xFFF)] + public void UnsignedRightShift(int value, int shift, int expected) + => ((int)((UInt24)value >>> shift)).Should().Equal(expected); + + #endregion + + #region Conversion Operators - Implicit TO UInt24 + + [TestCase(0)] + [TestCase(255)] + public void ImplicitFromByte(int value) + { + UInt24 result = (byte)value; + ((int)result).Should().Equal(value); + } + + [TestCase(0)] + [TestCase(65535)] + public void ImplicitFromUInt16(int value) + { + UInt24 result = (ushort)value; + ((int)result).Should().Equal(value); + } + + #endregion + + #region Conversion Operators - Implicit FROM UInt24 + + [TestCase(0)] + [TestCase(16777215)] + public void ImplicitToUInt32(int value) + { + uint result = (UInt24)value; + result.Should().Equal((uint)value); + } + + [TestCase(0)] + [TestCase(16777215)] + public void ImplicitToInt32(int value) + { + int result = (UInt24)value; + result.Should().Equal(value); + } + + [TestCase(0)] + [TestCase(16777215)] + public void ImplicitToInt64(int value) + { + long result = (UInt24)value; + result.Should().Equal((long)value); + } + + [TestCase(0)] + [TestCase(16777215)] + public void ImplicitToUInt64(int value) + { + ulong result = (UInt24)value; + result.Should().Equal((ulong)value); + } + + #endregion + + #region Conversion Operators - Explicit TO UInt24 + + [Test] + public void ExplicitFromInt32_Negative() + => AssertThat.Invoking(() => _ = (UInt24)(-1)).Should().Throw(); + + [Test] + public void ExplicitFromInt32_TooLarge() + => AssertThat.Invoking(() => _ = (UInt24)16777216).Should().Throw(); + + [TestCase(0)] + [TestCase(16777215)] + public void ExplicitFromInt32(int value) + => ((int)(UInt24)value).Should().Equal(value); + + [Test] + public void ExplicitFromUInt32_TooLarge() + => AssertThat.Invoking(() => _ = (UInt24)16777216u).Should().Throw(); + + [Test] + public void ExplicitFromInt64_Negative() + => AssertThat.Invoking(() => _ = (UInt24)(-1L)).Should().Throw(); + + [Test] + public void ExplicitFromInt64_TooLarge() + => AssertThat.Invoking(() => _ = (UInt24)16777216L).Should().Throw(); + + [Test] + public void ExplicitFromSByte_Negative() + => AssertThat.Invoking(() => _ = (UInt24)(sbyte)-1).Should().Throw(); + + [Test] + public void ExplicitFromInt16_Negative() + => AssertThat.Invoking(() => _ = (UInt24)(short)-1).Should().Throw(); + + #endregion + + #region Conversion Operators - Explicit FROM UInt24 + + [Test] + public void ExplicitToByte_TooLarge() + => AssertThat.Invoking(() => _ = (byte)(UInt24)256).Should().Throw(); + + [TestCase(0)] + [TestCase(255)] + public void ExplicitToByte(int value) + => ((byte)(UInt24)value).Should().Equal((byte)value); + + [Test] + public void ExplicitToSByte_TooLarge() + => AssertThat.Invoking(() => _ = (sbyte)(UInt24)128).Should().Throw(); + + [TestCase(0)] + [TestCase(127)] + public void ExplicitToSByte(int value) + => ((sbyte)(UInt24)value).Should().Equal((sbyte)value); + + [Test] + public void ExplicitToInt16_TooLarge() + => AssertThat.Invoking(() => _ = (short)(UInt24)32768).Should().Throw(); + + [TestCase(0)] + [TestCase(32767)] + public void ExplicitToInt16(int value) + => ((short)(UInt24)value).Should().Equal((short)value); + + [Test] + public void ExplicitToUInt16_TooLarge() + => AssertThat.Invoking(() => _ = (ushort)(UInt24)65536).Should().Throw(); + + [TestCase(0)] + [TestCase(65535)] + public void ExplicitToUInt16(int value) + => ((ushort)(UInt24)value).Should().Equal((ushort)value); + + #endregion + + #region Formatting + + [TestCase(0, "0")] + [TestCase(12345, "12345")] + [TestCase(16777215, "16777215")] + public void ToString_NoArgs(int value, string expected) + => ((UInt24)value).ToString().Should().Equal(expected); + + [TestCase(255, "X4", "00FF")] + [TestCase(16777215, "X6", "FFFFFF")] + public void ToString_FormatProvider(int value, string format, string expected) + => ((UInt24)value).ToString(format, CultureInfo.InvariantCulture).Should().Equal(expected); + + #endregion + + #region Parsing + + [TestCase("0", 0)] + [TestCase("12345", 12345)] + [TestCase("16777215", 16777215)] + public void Parse_String(string input, int expected) + => ((int)UInt24.Parse(input, CultureInfo.InvariantCulture)).Should().Equal(expected); + + [Test] + public void Parse_String_Overflow() + => AssertThat.Invoking(() => UInt24.Parse("16777216", CultureInfo.InvariantCulture)).Should().Throw(); + + [TestCase("0", true, 0)] + [TestCase("12345", true, 12345)] + [TestCase("16777215", true, 16777215)] + [TestCase("16777216", false, 0)] + [TestCase("abc", false, 0)] + public void TryParse_String(string? input, bool expectedResult, int expectedValue) + { + UInt24.TryParse(input, CultureInfo.InvariantCulture, out var result).Should().Equal(expectedResult); + ((int)result).Should().Equal(expectedValue); + } + + #endregion + + #region INumber + + [TestCase(50, 0, 100, 50)] + [TestCase(0, 10, 100, 10)] + [TestCase(200, 10, 100, 100)] + public void Clamp(int value, int min, int max, int expected) + => ((int)UInt24.Clamp((UInt24)value, (UInt24)min, (UInt24)max)).Should().Equal(expected); + + [TestCase(100, 200, 200)] + [TestCase(200, 100, 200)] + [TestCase(100, 100, 100)] + public void Max(int x, int y, int expected) + => ((int)UInt24.Max((UInt24)x, (UInt24)y)).Should().Equal(expected); + + [TestCase(100, 200, 100)] + [TestCase(200, 100, 100)] + [TestCase(100, 100, 100)] + public void Min(int x, int y, int expected) + => ((int)UInt24.Min((UInt24)x, (UInt24)y)).Should().Equal(expected); + + [TestCase(0, 0)] + [TestCase(1, 1)] + [TestCase(16777215, 1)] + public void Sign(int value, int expected) + => UInt24.Sign((UInt24)value).Should().Equal(expected); + + [TestCase(0, 0)] + [TestCase(100, 100)] + [TestCase(16777215, 16777215)] + public void Abs(int value, int expected) + => ((int)UInt24.Abs((UInt24)value)).Should().Equal(expected); + + #endregion + + #region IBinaryNumber + + [TestCase(0, false)] + [TestCase(1, true)] + [TestCase(2, true)] + [TestCase(3, false)] + [TestCase(4, true)] + [TestCase(256, true)] + [TestCase(100, false)] + public void IsPow2(int value, bool expected) + => UInt24.IsPow2((UInt24)value).Should().Equal(expected); + + [TestCase(1, 0)] + [TestCase(2, 1)] + [TestCase(4, 2)] + [TestCase(256, 8)] + [TestCase(16777215, 23)] + public void Log2(int value, int expected) + => ((int)UInt24.Log2((UInt24)value)).Should().Equal(expected); + + #endregion + + #region IBinaryInteger + + [Test] + public void GetByteCount() + => ((UInt24)0).GetByteCount().Should().Equal(3); + + [TestCase(0, 24)] + [TestCase(1, 23)] + [TestCase(0xFF, 16)] + [TestCase(0xFFFF, 8)] + [TestCase(0xFFFFFF, 0)] + public void LeadingZeroCount(int value, int expected) + => ((int)UInt24.LeadingZeroCount((UInt24)value)).Should().Equal(expected); + + [TestCase(0, 24)] + [TestCase(1, 0)] + [TestCase(2, 1)] + [TestCase(4, 2)] + [TestCase(0x100, 8)] + public void TrailingZeroCount(int value, int expected) + => ((int)UInt24.TrailingZeroCount((UInt24)value)).Should().Equal(expected); + + [TestCase(0, 0)] + [TestCase(1, 1)] + [TestCase(0xFF, 8)] + [TestCase(0xFFFFFF, 24)] + [TestCase(0xAAAAAA, 12)] + public void PopCount(int value, int expected) + => ((int)UInt24.PopCount((UInt24)value)).Should().Equal(expected); + + [TestCase(1, 1, 2)] + [TestCase(1, 8, 256)] + [TestCase(0x800000, 1, 1)] + [TestCase(0x000001, 24, 1)] + public void RotateLeft(int value, int amount, int expected) + => ((int)UInt24.RotateLeft((UInt24)value, amount)).Should().Equal(expected); + + [TestCase(1, 1, 0x800000)] + [TestCase(2, 1, 1)] + [TestCase(0x800000, 1, 0x400000)] + [TestCase(0x000001, 24, 1)] + public void RotateRight(int value, int amount, int expected) + => ((int)UInt24.RotateRight((UInt24)value, amount)).Should().Equal(expected); + + [Test] + public void TryWriteLittleEndian() + { + var value = (UInt24)0x123456; + var buffer = new byte[3]; + value.TryWriteLittleEndian(buffer, out var bytesWritten).Should().Equal(true); + bytesWritten.Should().Equal(3); + buffer[0].Should().Equal((byte)0x56); + buffer[1].Should().Equal((byte)0x34); + buffer[2].Should().Equal((byte)0x12); + } + + [Test] + public void TryWriteLittleEndian_BufferTooSmall() + { + var value = (UInt24)0x123456; + var buffer = new byte[2]; + value.TryWriteLittleEndian(buffer, out var bytesWritten).Should().Equal(false); + bytesWritten.Should().Equal(0); + } + + [Test] + public void TryWriteBigEndian() + { + var value = (UInt24)0x123456; + var buffer = new byte[3]; + value.TryWriteBigEndian(buffer, out var bytesWritten).Should().Equal(true); + bytesWritten.Should().Equal(3); + buffer[0].Should().Equal((byte)0x12); + buffer[1].Should().Equal((byte)0x34); + buffer[2].Should().Equal((byte)0x56); + } + + [Test] + public void TryWriteBigEndian_BufferTooSmall() + { + var value = (UInt24)0x123456; + var buffer = new byte[2]; + value.TryWriteBigEndian(buffer, out var bytesWritten).Should().Equal(false); + bytesWritten.Should().Equal(0); + } + + #endregion + + #region INumberBase Boolean Methods + + [TestCase(0, true)] + [TestCase(1, false)] + [TestCase(16777215, false)] + public void IsZero(int value, bool expected) + => UInt24.IsZero((UInt24)value).Should().Equal(expected); + + [TestCase(0, true)] + [TestCase(1, true)] + [TestCase(16777215, true)] + public void IsPositive(int value, bool expected) + => UInt24.IsPositive((UInt24)value).Should().Equal(expected); + + [TestCase(0, false)] + [TestCase(1, false)] + [TestCase(16777215, false)] + public void IsNegative(int value, bool expected) + => UInt24.IsNegative((UInt24)value).Should().Equal(expected); + + [TestCase(0, true)] + [TestCase(1, false)] + [TestCase(2, true)] + [TestCase(3, false)] + [TestCase(16777214, true)] + public void IsEvenInteger(int value, bool expected) + => UInt24.IsEvenInteger((UInt24)value).Should().Equal(expected); + + [TestCase(0, false)] + [TestCase(1, true)] + [TestCase(2, false)] + [TestCase(3, true)] + [TestCase(16777215, true)] + public void IsOddInteger(int value, bool expected) + => UInt24.IsOddInteger((UInt24)value).Should().Equal(expected); + + #endregion +} \ No newline at end of file From cac4c14d4bd39dc4b5de6a2c3911e238d7fabb0a Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 20 Feb 2026 20:00:34 +0000 Subject: [PATCH 6/7] Add descriptive messages to all OverflowException throws in UInt24 Co-authored-by: MrKWatkins <345796+MrKWatkins@users.noreply.github.com> --- src/MrKWatkins.BinaryPrimitives/UInt24.cs | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/MrKWatkins.BinaryPrimitives/UInt24.cs b/src/MrKWatkins.BinaryPrimitives/UInt24.cs index 66548e6..e405d0e 100644 --- a/src/MrKWatkins.BinaryPrimitives/UInt24.cs +++ b/src/MrKWatkins.BinaryPrimitives/UInt24.cs @@ -26,7 +26,7 @@ private UInt24(uint value, bool @checked) { if (@checked && value > MaxValueUInt32) { - throw new OverflowException(); + throw new OverflowException("Value was either too large or too small for a UInt24."); } _value = value; @@ -228,7 +228,7 @@ public static bool TryParse(ReadOnlySpan utf8Text, NumberStyles style, IFo var result = left._value + right._value; if (result > MaxValueUInt32) { - throw new OverflowException(); + throw new OverflowException("UInt24 addition overflowed."); } return new UInt24(result); @@ -246,7 +246,7 @@ public static bool TryParse(ReadOnlySpan utf8Text, NumberStyles style, IFo { if (left._value < right._value) { - throw new OverflowException(); + throw new OverflowException("UInt24 subtraction overflowed."); } return new UInt24(left._value - right._value); @@ -265,7 +265,7 @@ public static bool TryParse(ReadOnlySpan utf8Text, NumberStyles style, IFo var result = (ulong)left._value * right._value; if (result > MaxValueUInt32) { - throw new OverflowException(); + throw new OverflowException("UInt24 multiplication overflowed."); } return new UInt24((uint)result); @@ -297,7 +297,7 @@ public static bool TryParse(ReadOnlySpan utf8Text, NumberStyles style, IFo { if (value._value >= MaxValueUInt32) { - throw new OverflowException(); + throw new OverflowException("UInt24 increment overflowed."); } return new UInt24(value._value + 1u); @@ -315,7 +315,7 @@ public static bool TryParse(ReadOnlySpan utf8Text, NumberStyles style, IFo { if (value._value == 0u) { - throw new OverflowException(); + throw new OverflowException("UInt24 decrement overflowed."); } return new UInt24(value._value - 1u); @@ -342,7 +342,7 @@ public static bool TryParse(ReadOnlySpan utf8Text, NumberStyles style, IFo { if (value._value != 0u) { - throw new OverflowException(); + throw new OverflowException("UInt24 negation overflowed."); } return Zero; @@ -755,7 +755,7 @@ static bool INumberBase.TryConvertFromChecked(TOther value, out var v = (uint)(object)value; if (v > MaxValueUInt32) { - throw new OverflowException(); + throw new OverflowException("Value was either too large or too small for a UInt24."); } result = new UInt24(v); @@ -767,7 +767,7 @@ static bool INumberBase.TryConvertFromChecked(TOther value, out var v = (ulong)(object)value; if (v > MaxValueUInt32) { - throw new OverflowException(); + throw new OverflowException("Value was either too large or too small for a UInt24."); } result = new UInt24((uint)v); @@ -779,7 +779,7 @@ static bool INumberBase.TryConvertFromChecked(TOther value, out var v = (nuint)(object)value; if (v > MaxValueUInt32) { - throw new OverflowException(); + throw new OverflowException("Value was either too large or too small for a UInt24."); } result = new UInt24((uint)v); @@ -1276,7 +1276,7 @@ public static UInt24 CreateChecked(TOther value) var v = (ulong)(object)value; if (v > MaxValueUInt32) { - throw new OverflowException(); + throw new OverflowException("Value was either too large or too small for a UInt24."); } return new UInt24((uint)v); From cd627ae7e523e512c6257ec02ce0735125439f40 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 21 Feb 2026 00:16:31 +0000 Subject: [PATCH 7/7] Include actual values in OverflowException messages for UInt24 Co-authored-by: MrKWatkins <345796+MrKWatkins@users.noreply.github.com> --- src/MrKWatkins.BinaryPrimitives/UInt24.cs | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/MrKWatkins.BinaryPrimitives/UInt24.cs b/src/MrKWatkins.BinaryPrimitives/UInt24.cs index e405d0e..7c89738 100644 --- a/src/MrKWatkins.BinaryPrimitives/UInt24.cs +++ b/src/MrKWatkins.BinaryPrimitives/UInt24.cs @@ -26,7 +26,7 @@ private UInt24(uint value, bool @checked) { if (@checked && value > MaxValueUInt32) { - throw new OverflowException("Value was either too large or too small for a UInt24."); + throw new OverflowException($"The value {value} is outside the range of a UInt24 (0 to {MaxValueUInt32})."); } _value = value; @@ -228,7 +228,7 @@ public static bool TryParse(ReadOnlySpan utf8Text, NumberStyles style, IFo var result = left._value + right._value; if (result > MaxValueUInt32) { - throw new OverflowException("UInt24 addition overflowed."); + throw new OverflowException($"{left._value} + {right._value} = {result} which exceeds the maximum UInt24 value of {MaxValueUInt32}."); } return new UInt24(result); @@ -246,7 +246,7 @@ public static bool TryParse(ReadOnlySpan utf8Text, NumberStyles style, IFo { if (left._value < right._value) { - throw new OverflowException("UInt24 subtraction overflowed."); + throw new OverflowException($"{left._value} - {right._value} would result in a negative value which is outside the range of a UInt24."); } return new UInt24(left._value - right._value); @@ -265,7 +265,7 @@ public static bool TryParse(ReadOnlySpan utf8Text, NumberStyles style, IFo var result = (ulong)left._value * right._value; if (result > MaxValueUInt32) { - throw new OverflowException("UInt24 multiplication overflowed."); + throw new OverflowException($"{left._value} * {right._value} = {result} which exceeds the maximum UInt24 value of {MaxValueUInt32}."); } return new UInt24((uint)result); @@ -297,7 +297,7 @@ public static bool TryParse(ReadOnlySpan utf8Text, NumberStyles style, IFo { if (value._value >= MaxValueUInt32) { - throw new OverflowException("UInt24 increment overflowed."); + throw new OverflowException($"{value._value} + 1 = {value._value + 1} which exceeds the maximum UInt24 value of {MaxValueUInt32}."); } return new UInt24(value._value + 1u); @@ -315,7 +315,7 @@ public static bool TryParse(ReadOnlySpan utf8Text, NumberStyles style, IFo { if (value._value == 0u) { - throw new OverflowException("UInt24 decrement overflowed."); + throw new OverflowException("0 - 1 would result in a negative value which is outside the range of a UInt24."); } return new UInt24(value._value - 1u); @@ -342,7 +342,7 @@ public static bool TryParse(ReadOnlySpan utf8Text, NumberStyles style, IFo { if (value._value != 0u) { - throw new OverflowException("UInt24 negation overflowed."); + throw new OverflowException($"Negating {value._value} would result in a negative value which is outside the range of a UInt24."); } return Zero; @@ -755,7 +755,7 @@ static bool INumberBase.TryConvertFromChecked(TOther value, out var v = (uint)(object)value; if (v > MaxValueUInt32) { - throw new OverflowException("Value was either too large or too small for a UInt24."); + throw new OverflowException($"The value {v} is outside the range of a UInt24 (0 to {MaxValueUInt32})."); } result = new UInt24(v); @@ -767,7 +767,7 @@ static bool INumberBase.TryConvertFromChecked(TOther value, out var v = (ulong)(object)value; if (v > MaxValueUInt32) { - throw new OverflowException("Value was either too large or too small for a UInt24."); + throw new OverflowException($"The value {v} is outside the range of a UInt24 (0 to {MaxValueUInt32})."); } result = new UInt24((uint)v); @@ -779,7 +779,7 @@ static bool INumberBase.TryConvertFromChecked(TOther value, out var v = (nuint)(object)value; if (v > MaxValueUInt32) { - throw new OverflowException("Value was either too large or too small for a UInt24."); + throw new OverflowException($"The value {v} is outside the range of a UInt24 (0 to {MaxValueUInt32})."); } result = new UInt24((uint)v); @@ -1276,7 +1276,7 @@ public static UInt24 CreateChecked(TOther value) var v = (ulong)(object)value; if (v > MaxValueUInt32) { - throw new OverflowException("Value was either too large or too small for a UInt24."); + throw new OverflowException($"The value {v} is outside the range of a UInt24 (0 to {MaxValueUInt32})."); } return new UInt24((uint)v);