【算法】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 . . . */ }
欢迎拍砖讨论。