# C#快速分配可用IP

假设有一个192.168.54.0/24网络,如何快速分配一个可用ip,查找,添加,删除

192.168.54.0/24 可以使用 192.168.54.1-162.168.54.255 范围的ip,那么,就可以使用256个bit来表示某个ip是否已经被使用,在C#中,可以使用4个ulong来表示,于是

示例

C#
class FindIPTest
{
    public ulong[] array = new ulong[4] { ulong.MaxValue, ulong.MaxValue, ulong.MaxValue, 0 };
    /// <summary>
    /// 查找网段内可用ip(/24)
    /// </summary>
    /// <param name="array">缓存数组</param>
    /// <param name="value">找到的值</param>
    /// <returns></returns>
    /// <exception cref="Exception">array length must be 4</exception>
    public bool Find(ulong[] array, out byte value)
    {
        value = 0;
        if (array.Length != 4) throw new Exception("array length must be 4");
        //排除 .1 .255 .256
        array[0] |= 0b1;
        array[3] |= (ulong)0b11 << 62;

        if (array[0] < ulong.MaxValue) value = Find(array[0], 0);
        else if (array[1] < ulong.MaxValue) value = Find(array[1], 1);
        else if (array[2] < ulong.MaxValue) value = Find(array[2], 2);
        else if (array[3] < ulong.MaxValue) value = Find(array[3], 3);
        return value > 0;
    }
    private byte Find(ulong group, byte index)
    {
        byte value = (byte)(index * 64);
        //每次对半开,也可以循环,循环稍微会慢3-4ns,常量值快一点
        ulong _group = (group & uint.MaxValue);
        if (_group >= uint.MaxValue) { _group = group >> 32; value += 32; }
        group = _group;

        _group = (group & ushort.MaxValue);
        if (_group >= ushort.MaxValue) { _group = group >> 16; value += 16; }
        group = _group;

        _group = (group & byte.MaxValue);
        if (_group >= byte.MaxValue) { _group = group >> 8; value += 8; }
        group = _group;

        _group = (group & 0b1111);
        if (_group >= 0b1111) { _group = group >> 4; value += 4; }
        group = _group;

        _group = (group & 0b11);
        if (_group >= 0b11) { _group = group >> 2; value += 2; }
        group = _group;

        _group = (group & 0b1);
        if (_group >= 0b1) { value += 1; }
        value += 1;

        return value;
    }
    /// <summary>
    /// 是否存在一个ip
    /// </summary>
    /// <param name="group"></param>
    /// <param name="value"></param>
    /// <returns></returns>
    /// <exception cref="Exception"></exception>
    public bool Exists(ulong[] array, byte value)
    {
        if (array.Length != 4) throw new Exception("array length must be 4");
        int arrayIndex = value / 64;
        int length = value - arrayIndex * 64;

        return (array[arrayIndex] >> (length - 1) & 1) == 1;
    }
    /// <summary>
    /// 将一个ip(/24)设为已使用
    /// </summary>
    /// <param name="array">缓存数组</param>
    /// <param name="value"></param>
    public void Add(ulong[] array, byte value)
    {
        if (array.Length != 4) throw new Exception("array length must be 4");
        int arrayIndex = value / 64;
        int length = value - arrayIndex * 64;
        array[arrayIndex] |= (ulong)1 << (length - 1);
    }
    /// <summary>
    /// 删除一个ip(/24),设为未使用
    /// </summary>
    /// <param name="array"></param>
    /// <param name="value"></param>
    /// <exception cref="Exception"></exception>
    public void Delete(ulong[] array, byte value)
    {
        if (array.Length != 4) throw new Exception("array length must be 4");
        int arrayIndex = value / 64;
        int length = value - arrayIndex * 64;
        array[arrayIndex] &= ~((ulong)1 << (length - 1));
    }
}

测试

C#
[MemoryDiagnoser]
public partial class Test
{
    [GlobalSetup]
    public void Startup(){}

    FindPoerTest t = new FindPoerTest();
    [Benchmark]
    public void FindIP()
    {
        t.Find(t.array, out byte point);
    }
    [Benchmark]
    public void AddIP()
    {
        t.Add(t.array, 253);
    }
    [Benchmark]
    public void DeleteIP()
    {
        t.Delete(t.array, 253);
    } 
}

基准

C#
BenchmarkDotNet=v0.13.5, OS=Windows 10 (10.0.19045.3208/22H2/2022Update)
AMD Ryzen 3 2200G with Radeon Vega Graphics, 1 CPU, 4 logical and 4 physical cores
.NET SDK=7.0.100
  [Host]     : .NET 7.0.0 (7.0.22.51805), X64 RyuJIT AVX2 [AttachedDebugger]
  DefaultJob : .NET 7.0.0 (7.0.22.51805), X64 RyuJIT AVX2

|   Method |     Mean |     Error |    StdDev |   Median | Allocated |
|--------- |---------:|----------:|----------:|---------:|----------:|
|   FindIP | 8.246 ns | 0.1799 ns | 0.1595 ns | 8.303 ns |         - |
|    AddIP | 4.604 ns | 0.2598 ns | 0.7067 ns | 4.923 ns |         - |
| DeleteIP | 4.837 ns | 0.1308 ns | 0.1701 ns | 4.873 ns |         - |