zoukankan      html  css  js  c++  java
  • 【算法】CIDR集合的算法

        CIDR(Classless Inter-Domain Routing)是“无类别域间路由”的缩写。是当前用来表示路由的一种方式。由于在做某产品的时候需要用到CIDR集合的一些算法,例如集合的交集、并集、补集运算。下面给出相应的实现代码。

        首先是CIDR的结构:

        public sealed class CIDR : IXmlSerializable, IComparable<CIDR>
        {
            private byte[] bytes = null;
    
            public IPAddress IPAddress { get; private set; }
            public int Subnet { get; private set; }
    
            public override bool Equals(object obj)
            {
                CIDR cidr = obj as CIDR;
                if (cidr == null)
                    return false;
                return this.Subnet == cidr.Subnet && this.IPAddress.Equals(cidr.IPAddress);
            }
            public override int GetHashCode()
            {
                return this.IPAddress.GetHashCode() ^ this.Subnet.GetHashCode();
            }
            public override string ToString()
            {
                return string.Format("{0}/{1}", IPAddress, Subnet);
            }
    
            public static CIDR Parse(string str)
            {
                CIDR cidr = new CIDR();
                cidr.SetFromString(str);
                return cidr;
            }
            private void SetFromString(string str)
            {
                int i = str.IndexOf('/');
                this.IPAddress = IPAddress.Parse(str.Substring(0, i));
                this.Subnet = int.Parse(str.Substring(i + 1));
                this.bytes = this.IPAddress.GetAddressBytes();
    
                int length = this.bytes.Length << 3;
                if (this.Subnet < 0 || this.Subnet > length)
                    throw new ArgumentException("", "str");
                // can optimize
                for (i = this.Subnet + 1; i <= length; i++)
                {
                    int bit = (bytes[(i - 1) >> 3] >> ((8 - i) & 7)) & 1;
                    if (bit != 0)
                        throw new ArgumentException("", "str");
                }
            }
    
            public bool IsIPv6
            {
                get { return this.IPAddress.AddressFamily == AddressFamily.InterNetworkV6; }
            }
    
            #region IXmlSerializable
            XmlSchema IXmlSerializable.GetSchema()
            {
                return null;
            }
    
            void IXmlSerializable.ReadXml(XmlReader reader)
            {
                this.SetFromString(reader.ReadElementContentAsString());
            }
    
            void IXmlSerializable.WriteXml(XmlWriter writer)
            {
                writer.WriteString(this.ToString());
            }
            #endregion
    
            #region CIDR Algorithm
            public CIDR GetPaired()
            {
                if (this.Subnet == 0)
                    throw new NotSupportedException("Subnet=0");
                CIDR cidr = new CIDR();
                cidr.Subnet = this.Subnet;
                cidr.bytes = new byte[this.bytes.Length];
                this.bytes.CopyTo(cidr.bytes, 0);
    
                int k = this.Subnet - 1;
                int i = k >> 3;
                int j = (7 - k) & 7;
                cidr.bytes[i] = (byte)(cidr.bytes[i] ^ (1 << j));
                cidr.IPAddress = new IPAddress(cidr.bytes);
    
                return cidr;
            }
    
            public CIDR GetBiggerSubnet()
            {
                if (this.Subnet == 0)
                    throw new NotSupportedException("Subnet=0");
                CIDR cidr = new CIDR();
                cidr.Subnet = this.Subnet - 1;
                cidr.bytes = new byte[this.bytes.Length];
                this.bytes.CopyTo(cidr.bytes, 0);
    
                int k = this.Subnet - 1;
                int i = k >> 3;
                int j = (7 - k) & 7;
                cidr.bytes[i] = (byte)(cidr.bytes[i] & ~(1 << j));
                cidr.IPAddress = new IPAddress(cidr.bytes);
    
                return cidr;
            }
    
            public CIDR GetSmallerSubnet()
            {
                if (this.Subnet == this.bytes.Length << 3)
                    throw new NotSupportedException("Subnet=max");
                CIDR cidr = new CIDR();
                cidr.Subnet = this.Subnet + 1;
                cidr.bytes = this.bytes;
                cidr.IPAddress = this.IPAddress;
                return cidr;
            }
    
            public bool ExistsIntersection(CIDR other)
            {
                if (other == null)
                    throw new ArgumentNullException("other");
    
                if (this.IsIPv6 != other.IsIPv6)
                    throw new NotSupportedException("Different address family");
    
                if (this.Subnet == 0 || other.Subnet == 0)
                    return true;
    
                int min = Math.Min(this.Subnet, other.Subnet) - 1;
                int length = min >> 3;
    
                int k;
                for (k = 0; k < length; k++)
                    if (this.bytes[k] != other.bytes[k])
                        return false;
    
                //int m = (0xff << (7 - min & 7)) & 0xff;
                int m = (0xff80 >> (min & 7));
                if (k < this.bytes.Length && (this.bytes[k] & m) != (other.bytes[k] & m))
                    return false;
    
                return true;
            }
    
            public CIDR Intersect(CIDR other)
            {
                if (other == null)
                    throw new ArgumentNullException("other");
    
                if (this.IsIPv6 != other.IsIPv6)
                    throw new NotSupportedException("Different address family");
    
                if (this.ExistsIntersection(other))
                    if (this.Subnet > other.Subnet)
                        return this;
                    else
                        return other;
                else
                    return null;
            }
    
            public CIDR[] Union(CIDR other)
            {
                if (other == null)
                    throw new ArgumentNullException("other");
    
                if (this.IsIPv6 != other.IsIPv6)
                    throw new NotSupportedException("Different address family");
    
                if (this.ExistsIntersection(other))
                    if (this.Subnet < other.Subnet)
                        return new[] { this };
                    else
                        return new[] { other };
                else
                    if (this.Subnet == other.Subnet && this.Equals(other.GetPaired()))
                        return new[] { this.GetBiggerSubnet() };
                    else
                        return new[] { this, other };
            }
            #endregion
    
            public int CompareTo(CIDR other)
            {
                if (other == null)
                    throw new ArgumentNullException("other");
    
                if (this.IsIPv6 != other.IsIPv6)
                    if (this.IsIPv6)
                        return 1;
                    else
                        return -1;
                else if (this.Subnet > other.Subnet)
                    return 1;
                else if (this.Subnet < other.Subnet)
                    return -1;
                else
                    for (int i = 0; i < this.bytes.Length; i++)
                        if (this.bytes[i] > other.bytes[i])
                            return 1;
                        else if (this.bytes[i] < other.bytes[i])
                            return -1;
                return 0;
            }
        }

        这个类实现了序列化接口(IXmlSerializable)可比较接口(IComparable<T>)。方便进行存储等操作。

        接下来是对CIDR集合的操作。由于在产品中叫做RouteGroup,所以在这里也就没改名字了:

        public class RouteGroup
        {
            public RouteGroup()
            {
                this.CIDRs = new HashSet<CIDR>();
            }
    
            [XmlAttribute]
            public string GroupName { get; set; }
            [XmlAttribute]
            public Guid GroupID { get; set; }
            public HashSet<CIDR> CIDRs { get; set; }
    
            public static RouteGroup Create(string groupName)
            {
                return new RouteGroup { GroupName = groupName, GroupID = Guid.NewGuid() };
            }
    
            public void Union(CIDR cidr)
            {
                if (cidr == null)
                    throw new ArgumentNullException("cidr");
    
                var list = this.CIDRs
                    .Where(s => s.IsIPv6 == cidr.IsIPv6 && s.ExistsIntersection(cidr))
                    .ToList();
                if (list.Count == 0)
                {
                    if (cidr.Subnet > 0 && this.CIDRs.Remove(cidr.GetPaired()))
                        this.Union(cidr.GetBiggerSubnet());
                    else
                        this.CIDRs.Add(cidr);
                }
                else
                {
                    foreach (var item in list)
                        this.CIDRs.Remove(item);
    
                    var max = list.Max();
                    if (max.Subnet < cidr.Subnet)
                        this.CIDRs.Add(max);
                    else if (max.Subnet > cidr.Subnet)
                        this.CIDRs.Add(cidr);
                }
            }
    
            public void Subtract(CIDR cidr)
            {
                if (cidr == null)
                    throw new ArgumentNullException("cidr");
    
                var list = this.CIDRs
                   .Where(s => s.IsIPv6 == cidr.IsIPv6 && s.ExistsIntersection(cidr))
                   .ToList();
                if (list.Count > 0)
                {
                    foreach (var item in list)
                        this.CIDRs.Remove(item);
    
                    var min = list.Min();
                    CIDR other = cidr;
                    for (int i = cidr.Subnet; i > min.Subnet; i--)
                    {
                        this.CIDRs.Add(other.GetPaired());
                        other = other.GetBiggerSubnet();
                    }
                }
            }
    
            public void Union(string cidr)
            {
                this.Union(CIDR.Parse(cidr));
            }
    
            public void Subtract(string cidr)
            {
                this.Subtract(CIDR.Parse(cidr));
            }
    
            public void Reverse()
            {
                var list = this.CIDRs.ToList();
                this.CIDRs.Clear();
                this.Union("0.0.0.0/0");
                foreach (var item in list)
                    this.Subtract(item);
            }
    
            public void Sort()
            {
                var list = this.CIDRs.OrderBy(s => s).ToList();
                this.CIDRs.Clear();
                foreach (var item in list)
                {
                    this.CIDRs.Add(item);
                }
            }
    
            public void Rebuild()
            {
                var list = this.CIDRs.OrderBy(s => s).ToList();
                this.CIDRs.Clear();
                foreach (var item in list)
                {
                    this.Union(item);
                }
            }
        }

        样例代码:

       static void Main(string[] args)
            {
                var rg = RouteGroup.Create("test");
                rg.Union("1.2.0.0/16");
                rg.Union("1.3.0.0/16");
                rg.Union("2.2.0.0/16");
                rg.Union("3.2.0.0/16");
                rg.Union("4.2.5.0/24");
                rg.Union("5.2.8.0/21");
                rg.Union("4.2.12.0/22");
                rg.Sort();
                foreach (var item in rg.CIDRs)
                    Console.WriteLine(item);
                Console.WriteLine();
    
                rg.Reverse();
                rg.Sort();
                foreach (var item in rg.CIDRs)
                    Console.WriteLine(item);
                Console.WriteLine();
    
                rg.Subtract("0.0.0.0/2");
                rg.Sort();
                foreach (var item in rg.CIDRs)
                    Console.WriteLine(item);
    
                /*
                 * 1.2.0.0/15
                 * 2.2.0.0/16
                 * 3.2.0.0/16
                 * 5.2.8.0/21
                 * 4.2.12.0/22
                 * 4.2.5.0/24
                 * 
                 * 128.0.0.0/1
                 * 64.0.0.0/2
                 * 32.0.0.0/3
                 * 16.0.0.0/4
                 * 8.0.0.0/5
                 * 6.0.0.0/7
                 * 0.0.0.0/8
                 * 1.128.0.0/9
                 * 2.128.0.0/9
                 * 3.128.0.0/9
                 * 4.128.0.0/9
                 * 5.128.0.0/9
                 * 1.64.0.0/10
                 * 2.64.0.0/10
                 * 3.64.0.0/10
                 * 4.64.0.0/10
                 * 5.64.0.0/10
                 * 1.32.0.0/11
                 * 2.32.0.0/11
                 * 3.32.0.0/11
                 * 4.32.0.0/11
                 * 5.32.0.0/11
                 * 1.16.0.0/12
                 * 2.16.0.0/12
                 * 3.16.0.0/12
                 * 4.16.0.0/12
                 * 5.16.0.0/12
                 * 1.8.0.0/13
                 * 2.8.0.0/13
                 * 3.8.0.0/13
                 * 4.8.0.0/13
                 * 5.8.0.0/13
                 * 1.4.0.0/14
                 * 2.4.0.0/14
                 * 3.4.0.0/14
                 * 4.4.0.0/14
                 * 5.4.0.0/14
                 * 1.0.0.0/15
                 * 2.0.0.0/15
                 * 3.0.0.0/15
                 * 4.0.0.0/15
                 * 5.0.0.0/15
                 * 2.3.0.0/16
                 * 3.3.0.0/16
                 * 4.3.0.0/16
                 * 5.3.0.0/16
                 * 4.2.128.0/17
                 * 5.2.128.0/17
                 * 4.2.64.0/18
                 * 5.2.64.0/18
                 * 4.2.32.0/19
                 * 5.2.32.0/19
                 * 4.2.16.0/20
                 * 5.2.16.0/20
                 * 5.2.0.0/21
                 * 4.2.0.0/22
                 * 4.2.8.0/22
                 * 4.2.6.0/23
                 * 4.2.4.0/24
                 * 
                 * 128.0.0.0/1
                 * 64.0.0.0/2
                 * Press any key to continue . . .
                 */
            }

        欢迎拍砖讨论。

  • 相关阅读:
    IDEA工具-快捷键整理
    Intellij热部署插件JRebel_转载
    [刘阳Java]_为什么要前后端分离
    [刘阳Java]_程序员Java编程进阶的5个注意点,别编程两三年还是增删改查
    [刘阳Java]_Web前端入门级练习_迅雷首页第一屏设计
    [刘阳Java]_Web前端入门级练习_迅雷官宣网设计
    [刘阳Java]_MySQL数据优化总结_查询备忘录
    Mybatis总结(一)
    java.io.IOException: Could not find resource com/xxx/xxxMapper.xml
    push to origin/master was rejected错误解决方案
  • 原文地址:https://www.cnblogs.com/Aimeast/p/2290841.html
Copyright © 2011-2022 走看看