All Downloads are FREE. Search and download functionalities are using the official Maven repository.

fm.common.IPMap.scala Maven / Gradle / Ivy

/*
 * Copyright 2015 Frugal Mechanic (http://frugalmechanic.com)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package fm.common

import it.unimi.dsi.fastutil.ints.{Int2IntAVLTreeMap, IntComparator, IntIterator}
import it.unimi.dsi.fastutil.longs.{LongIterator, Long2ObjectOpenHashMap}

object IPMap {
  def newBuilder[T]: IPMapMutable[T] = IPMapMutable()

  def empty[T]: IPMapImmutable[T] = IPMapImmutable.empty[T]

  def apply[T](ips: TraversableOnce[(IPOrSubnet, T)]): IPMapImmutable[T] = IPMapImmutable(ips)

  private[common] val leadingBitsFirstComparator: IntComparator = new IntComparator() {
    def compare(a: Int, b: Int): Int = {
      // Note:
      //  - masks should only have leading ones so Integer.bitCount should be fine
      //  - We want a higher number of leading 1's to be sorted first
      Integer.compare(Integer.bitCount(b), Integer.bitCount(a))
    }
  }
}

sealed trait IPMap[T] {
  // This maps the ip and mask to whatever object we are storing
  private[common] def ipsWithMaskMap: Long2ObjectOpenHashMap[T]

  // This maps the mask to a count of how many items have this mask in the ipsWithMaskMap
  private[common] def maskToCountMap: Int2IntAVLTreeMap

  def toImmutable: IPMapImmutable[T]
  def toMutable: IPMapMutable[T]

  final def apply(ip: String): T = apply(IP(ip))

  final def apply(ip: IP): T = {
    val value: T = getOrNull(ip)
    if (null == value) throw new NoSuchElementException(ip.toString)
    value
  }

  final def get(ip: String): Option[T] = get(IP(ip))

  final def get(ip: IP): Option[T] = Option(getOrNull(ip))

  private def getOrNull(ip: IP): T = {
    val it: IntIterator = maskToCountMap.keySet.iterator()
    while (it.hasNext) {
      val mask: Int = it.nextInt
      val value: T = ipsWithMaskMap.get(makeIPWithMask(ip.intValue, mask))
      if (null != value) return value
    }

    null.asInstanceOf[T]
  }

  final def exact(ip: IPOrSubnet): T = {
    val value: T = getExactOrNull(ip)
    if (null == value) throw new NoSuchElementException(ip.toString)
    value
  }

  final def getExact(ip: IPOrSubnet): Option[T] = Option(getExactOrNull(ip))

  private def getExactOrNull(ip: IPOrSubnet): T = ipsWithMaskMap.get(makeIPWithMask(ip))

  final def contains(ip: String): Boolean = contains(IP(ip))

  final def contains(ip: IP): Boolean = {
    val it: IntIterator = maskToCountMap.keySet.iterator()
    while (it.hasNext) {
      val mask: Int = it.nextInt
      if (ipsWithMaskMap.containsKey(makeIPWithMask(ip.intValue, mask))) return true
    }

    false
  }

  /**
   * Does this IPMap exactly contain the given subnet
   */
  final def containsExact(subnet: IPOrSubnet): Boolean = ipsWithMaskMap.containsKey(makeIPWithMask(subnet))

  final def isEmpty: Boolean = ipsWithMaskMap.isEmpty()

  protected def makeIPWithMask(subnet: IPOrSubnet): Long = makeIPWithMask(subnet.start.intValue, subnet.mask)

  // [UPPER 32 BITS IS IP ADDRESS][LOWER 32 BITS IS THE BITMASK]
  protected def makeIPWithMask(ip: Int, mask: Int): Long = BitUtils.makeLong(ip & mask, mask)
}

object IPMapMutable {
  def apply[T](): IPMapMutable[T] = new IPMapMutable[T]()
}

final class IPMapMutable[T] extends IPMap[T] with BuilderCompat[(IPOrSubnet,T), IPMapImmutable[T]] {
  private[common] val ipsWithMaskMap: Long2ObjectOpenHashMap[T] = new Long2ObjectOpenHashMap()
  private[common] val maskToCountMap: Int2IntAVLTreeMap = new Int2IntAVLTreeMap(IPMap.leadingBitsFirstComparator)

  def toImmutable: IPMapImmutable[T] = result()
  def toMutable: IPMapMutable[T] = this

  def addOne(ip: String, value: T): this.type = addOne(IPSubnet.parse(ip), value)

  override def addOne(ipAndValue: (IPOrSubnet, T)): this.type = addOne(ipAndValue._1, ipAndValue._2)

  def addOne(ip: IPOrSubnet, value: T): this.type = {
    add(ip.start.intValue, ip.mask, value)
    this
  }

  def ++=(other: IPMap[T]): this.type = {
    val ipIT: LongIterator = other.ipsWithMaskMap.keySet().iterator()
    while (ipIT.hasNext) {
      val key: Long = ipIT.nextLong
      add(BitUtils.getUpper(key), BitUtils.getLower(key), other.ipsWithMaskMap.get(key))
    }

    this
  }

  private def add(ip: Int, mask: Int, value: T): Unit = {
    val key: Long = makeIPWithMask(ip, mask)

    if (!ipsWithMaskMap.containsKey(key)) {
      // Key doesn't exist which means we need to modify the maskToCountMap
      maskToCountMap.put(mask, maskToCountMap.get(mask) + 1)
    }

    ipsWithMaskMap.put(key, value)
  }

  override def clear(): Unit = {
    ipsWithMaskMap.clear()
    maskToCountMap.clear()
  }

  override def result(): IPMapImmutable[T] = new IPMapImmutable(this)
}

object IPMapImmutable {
  def apply[T](ips: TraversableOnce[(IPOrSubnet, T)]): IPMapImmutable[T] = {
    val builder: IPMapMutable[T] = IPMap.newBuilder
    ips.foreach{ builder += _ }
    builder.result()
  }

  def empty[T]: IPMapImmutable[T] = _empty.asInstanceOf[IPMapImmutable[T]]

  private val _empty: IPMapImmutable[AnyRef] = IPMap.newBuilder[AnyRef].result()
}

final class IPMapImmutable[T](map: IPMapMutable[T]) extends IPMap[T] {
  private[common] val ipsWithMaskMap: Long2ObjectOpenHashMap[T] = new Long2ObjectOpenHashMap(map.ipsWithMaskMap)
  private[common] val maskToCountMap: Int2IntAVLTreeMap = new Int2IntAVLTreeMap(map.maskToCountMap)

  def toImmutable: IPMapImmutable[T] = this

  def toMutable: IPMapMutable[T] = {
    IPMap.newBuilder ++= this
  }

  def ++(other: IPMap[T]): IPMapImmutable[T] = (toMutable ++= other).result()
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy