import { AbstractIPRange , IPv4CidrRange , IPv6CidrRange , RangedSet } from "./IPRange" ; import { AbstractIPNum , IPv4 , IPv6 } from "./IPNumber" ; import { IPv4Prefix , IPv6Prefix , isIPv4Prefix , Prefix } from "./Prefix" ; import * as bigInt from "big-integer" ; type RangeType = RangedSet < IPv4 > | RangedSet < IPv6 > ; export type IPCidrRange < T > = T extends IPv4Prefix ? IPv4CidrRange : IPv6CidrRange ; export type IPCidrRangeArray < T > = T extends IPv4Prefix ? Array < IPv4CidrRange > : Array < IPv6CidrRange > ; /**
 * Represents a collection of IP  RangedSet 's
  export class Pool < T extends RangeType > { private backingSet : SortedSet = new SortedSet ( ) ; /**
     * Convenient method for creating an instance from arrays of  IPv4 or  IPv6 param ipNumbers the arrays of  IPv4 or  IPv6 that will make up the pool.
      public static fromIPNumbers ( ipNumbers ) { let ranges = ( ipNumbers as Array < AbstractIPNum > ) . map ( ( ip ) => { return RangedSet . fromSingleIP ( ip ) ; } ) ; return new Pool ( ranges ) ; } /**
     * Convenient method for creating an instance from arrays of  RangedSet .
     *
     *  param ipRanges the arrays of  RangedSet 's that will make up the pool.
      public static fromRangeSet ( ipRanges ) { return new Pool ( ipRanges ) ; } /**
     * Convenient method for creating an instance from arrays of  IPv4CidrRange or  IPv6CidrRange .
     *
     *  param cidrRanges the arrays of  IPv4CidrRange or  IPv6CidrRange that will make up the pool.
      public static fromCidrRanges ( cidrRanges ) { let cidr = cidrRanges as ( IPv4CidrRange | IPv6CidrRange ) [ ] ; let rangeSet = cidr . map ( ( range ) => { return range . toRangeSet ( ) ; } ) ; return new Pool ( rangeSet ) ; } /**
     * Constructor for an IP pool.
     *
     * Creates a Pool of IP ranges from supplied  RangedSet 's
     *
     *  param ranges constructor ( ranges ) { ranges . forEach ( range => { this . backingSet . add ( range ) ; } ) ; } /**
     * Returns an array of  RangedSet 's that is contained within the pool
      public getRanges ( ) { return this . backingSet . asArray ( ) ; } /**
     * Returns an new  Pool with all the IP ranges aggregated
      public aggregate ( ) { let sortedRanges = this . backingSet . asArray ( ) ; let mergedRanges = sortedRanges . reduce < Array < RangedSet < AbstractIPNum > > > ( ( accumulator , currentRange , currentIndex , array ) => { if ( accumulator . length == 0 ) { accumulator . push ( currentRange ) ; return accumulator ; } else { let previous = accumulator . pop ( ) ! ; let previousCidrRange = previous . toCidrRange ( ) ; let currentCidrRange = currentRange . toCidrRange ( ) ; if ( previousCidrRange . isCidrMergeable ( currentCidrRange ) ) { let merged = previousCidrRange . merge ( currentCidrRange ) ; accumulator . push ( merged . toRangeSet ( ) ) ; return accumulator ; } else { if ( ! previous . contains ( currentRange ) ) { accumulator . push ( previous ) ; accumulator . push ( currentRange ) ; } else { accumulator . push ( previous ) ; } return accumulator ; } } } , [ ] ) ; let aggregatedPool = Pool . fromRangeSet ( mergedRanges ) ; if ( aggregatedPool . getRanges ( ) . length !== this . getRanges ( ) . length ) { return aggregatedPool . aggregate ( ) ; } else { return aggregatedPool ; } } param prefix public getCidrRange < T extends IPv4Prefix | IPv6Prefix > ( prefix ) { if ( prefix . toRangeSize ( ) . gt ( this . getSize ( ) ) ) { throw new Error ( `Not enough IP number in the pool for requested prefix: ${ prefix }` ) ; } let selectedCidrRange ; let error ; loop : for ( let range of this . getRanges ( ) ) { for ( var offset = bigInt . zero ; offset . plus ( prefix . toRangeSize ( ) ) . lesserOrEquals ( range . getSize ( ) ) ; offset = offset . plus ( bigInt . one ) ) try { let selectedRange = range . takeSubRange ( offset , prefix . toRangeSize ( ) ) ; selectedCidrRange = selectedRange . toCidrRange ( ) ; let remaining = range . difference ( selectedRange ) ; this . removeExact ( range ) ; this . add ( remaining ) ; break loop ; } catch ( e ) { if ( e instanceof RangeError ) { continue loop ; } error = e ; } } if ( selectedCidrRange ) { return selectedCidrRange as IPCidrRange < T > ; } else { throw ( error === undefined ? new Error ( `No range big enough in the pool for requested prefix: ${ prefix }` ) : error ) ; } } param reqprefix public getCidrRanges < T extends IPv4Prefix | IPv6Prefix > ( reqprefix ) { if ( reqprefix . toRangeSize ( ) . greater ( this . getSize ( ) ) ) { throw new Error ( "Prefix greater than pool" ) ; } let go = ( reqprefix , prefix , accummulated ) : AbstractIPRange < AbstractIPNum , IPv4Prefix | IPv6Prefix > [ ] => { try { let singleCidrRange = this . getCidrRange ( prefix ) ; accummulated . push ( singleCidrRange ) ; let currentSize = accummulated . reduce ( ( previous , current ) => { return previous . plus ( current . getSize ( ) ) ; } , bigInt . zero ) ; if ( reqprefix . toRangeSize ( ) . equals ( currentSize ) ) { return accummulated as IPCidrRangeArray < T > ; } else { return go ( reqprefix , prefix , accummulated ) ; } } catch ( e ) { let lowerPrefix = isIPv4Prefix ( prefix ) ? IPv4Prefix . fromNumber ( prefix . getValue ( ) + 1 ) : IPv6Prefix . fromNumber ( prefix . getValue ( ) + 1 ) ; return go ( reqprefix , lowerPrefix , accummulated ) ; } } ; return go ( reqprefix , reqprefix , [ ] ) as IPCidrRangeArray < T > ; } /**
     * Returns the size of IP numbers in the pool
     */ public getSize ( ) { return this . aggregate ( ) . getRanges ( ) . reduce ( ( previous , current ) => { return previous . plus ( current . getSize ( ) ) ; } , bigInt . zero ) ; } param ipRanges public resetWith ( ipRanges ) { this . backingSet . clear ( ) ; this . backingSet = this . backingSet . add ( ipRanges ) ; } param rangeToRemove public removeExact ( rangeToRemove ) { let updatedSet = this . backingSet . removeExact ( rangeToRemove ) ; let isUpdated = ! this . backingSet . isEquals ( updatedSet ) ; this . backingSet = updatedSet ; return isUpdated ; } param rangeToRemove public removeOverlapping ( rangeToRemove ) { let updatedSet = this . backingSet . removeOverlapping ( rangeToRemove ) ; let isUpdated = ! this . backingSet . isEquals ( updatedSet ) ; this . backingSet = updatedSet ; return isUpdated ; } param range public add ( range ) { this . backingSet = this . backingSet . add ( range ) ; } /**
     * Removes all ranges from pool
     */ public clear ( ) { this . backingSet . clear ( ) ; } } type T = RangedSet < AbstractIPNum > ; class SortedSet { public backingArray : Array < T > ; private sortArray ( array ) { array . sort ( ( a , b ) => { if ( a . isLessThan ( b ) ) { return - 1 ; } if ( a . isGreaterThan ( b ) ) { return 1 ; } return 0 ; } ) ; return array ; } constructor ( ) ; constructor ( array ) ; constructor ( array ? ) { if ( array ) { this . backingArray = this . sortArray ( array ) ; } else { this . backingArray = new Array < T > ( ) ; } } public asArray ( ) { return this . backingArray ; } public isEquals ( other ) { if ( this . backingArray . length !== other . asArray ( ) . length ) { return false ; } return this . backingArray . every ( ( value , index ) => { return value . getSize ( ) . equals ( other . asArray ( ) [ index ] . getSize ( ) ) ; } ) ; } public add ( item ) ; public add ( item ) ; public add ( item ) { let array = this . backingArray ; if ( "push" in item ) { array = array . concat ( item ) ; } else { array . push ( item ) ; } return new SortedSet ( this . sortArray ( array ) ) ; } public removeExact ( items ) ; public removeExact ( items ) ; public removeExact ( items ) { let filtered = this . backingArray . filter ( currentItem => { if ( "push" in items ) { return items . find ( item => item . isEquals ( currentItem ) ) !== undefined ; } else { return ! items . isEquals ( currentItem ) ; } } ) ; return new SortedSet ( this . sortArray ( filtered ) ) ; } public removeOverlapping ( items ) ; public removeOverlapping ( items ) ; public removeOverlapping ( items ) { let filtered = this . backingArray . flatMap ( backingItem => { if ( "push" in items ) { return items . flatMap ( item => { if ( backingItem . contains ( item ) ) { return backingItem . difference ( item ) ; } else if ( backingItem . inside ( item ) ) { return new Array < RangedSet < IPv4 | IPv6 > > ( ) ; } else if ( backingItem . isOverlapping ( item ) ) { return [ backingItem . subtract ( item ) ] ; } else { return [ item ] ; } } ) ; } else { try { return backingItem . difference ( items ) ; } catch ( e ) { return backingItem ; } } } ) ; return new SortedSet ( this . sortArray ( filtered ) ) ; } public clear ( ) { this . backingArray = [ ] ; } }