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

util.RangeSet Maven / Gradle / Ivy

Go to download

A tool to perform static analysis on regexes to determine whether they are vulnerable to ReDoS.

There is a newer version: 1.0.8
Show newest version
package util;

import java.util.*;

import util.RangeSet.Range;

public class RangeSet implements Iterable {
	
	private final int rangesLowerBound; /* inclusive */
	private final int rangesUpperBound; /* exclusive */
	
	private TreeSet ranges;
	
	public RangeSet(int rangesLowerBound, int rangesUpperBound) {
		this.rangesLowerBound = rangesLowerBound;
		this.rangesUpperBound = rangesUpperBound;
		
		ranges = new TreeSet();
	}
	
	public RangeSet(RangeSet rs) {
		rangesLowerBound = rs.rangesLowerBound;
		rangesUpperBound = rs.rangesUpperBound;
		ranges = new TreeSet(rs.ranges);
	}
	
	public Range createRange(int num) {
		return new Range(num);
	}
	
	public Range createRange(int low, int high) {
		return new Range(low, high);
	}
	
	public void union(RangeSet rs) {
		ranges.addAll(rs.ranges);	
		mergeRanges();
	}
	
	public void union(Range r) {
		ranges.add(r);
		mergeRanges();	
	}
	
	public void union(List rangesToAdd) {
		ranges.addAll(rangesToAdd);	
		mergeRanges();		
	}
	
	public void intersection(RangeSet rs) {
		complement();
		rs.complement();	
		union(rs);
		complement();
	}
	
	public void complement() {
		if (ranges.isEmpty()) {
			union(new Range(rangesLowerBound, rangesUpperBound));
			return;
		}
		TreeSet rangesComplement = new TreeSet();
		Iterator i0 = ranges.iterator();
		Range currentRange = i0.next();
		if (currentRange.low != rangesLowerBound) {
			rangesComplement.add(new Range(rangesLowerBound, currentRange.low));
		}
		
		while (i0.hasNext()) {
			int newLow = currentRange.high;
			currentRange = i0.next();
			int newHigh = currentRange.low;
			Range newRange = new Range(newLow, newHigh);
			rangesComplement.add(newRange);
		}
		
		if (currentRange.high != rangesUpperBound) {
			rangesComplement.add(new Range(currentRange.high, rangesUpperBound));
		}
		ranges = rangesComplement;
	}
	
	public Set discretize() {
		Set values = new HashSet();
		for (Range r : ranges) {
			for (int i = r.low; i < r.high; i++) {
				values.add(i);
			}
		}
		
		return values;
	}
	
	private void mergeRanges() {
		List l = new LinkedList(ranges);
		for (int i = 0; i < l.size() - 1; ) {
			Range r1 = l.get(i);
			Range r2 = l.get(i + 1);
			if (r1.overlapsAdjacent(r2)) {
				l.set(i, r1.merge(r2));
				l.remove(i + 1);
			} else {
				i++;
			}
		}
		ranges = new TreeSet(l);
	}
	
	public int sampleRangeSet() {
		if (ranges.isEmpty()) {
			throw new IllegalStateException("Cannot sample from empty range set.");
		}
		return ranges.first().low;
		
	}
	
	public boolean isEmpty() {
		return ranges.isEmpty();
	}
	
	@Override
	public boolean equals(Object o) {
		if (o == null) {
			return false;
		}
	    if (o == this) {
	    	return true;
	    }
	    if (!(o instanceof RangeSet)) {
	    	return false;
	    }
	    
	    RangeSet rs = (RangeSet) o;
	    /* TODO are two Range sets with different bounds, but equal ranges, equal?*/
	    if (ranges.size() != rs.ranges.size()) {
	    	return false;
	    }
	    
	    Iterator i0 = ranges.iterator();
	    Iterator i1 = rs.ranges.iterator();
	    while (i0.hasNext()) {
	    	Range r0 = i0.next();
	    	Range r1 = i1.next();
	    	if (!r0.equals(r1)) {
	    		return false;
	    	}
	    }
	    return true;
	}
	
	@Override
	public int hashCode() {
		return 17 * rangesLowerBound + 33 * rangesUpperBound + ranges.hashCode();
	}
	
	public boolean contains(int num) {
		Range numRange = new Range(num);
		for (Range r : ranges) {
			if (r.overlaps(numRange)) {
				return true;
			}
		}
		return false;
	}
	
	/*
	@Override
	public String toString() {
		StringBuilder sb = new StringBuilder();
		int previous = rangesLowerBound;
		for (Range r : ranges) {
			for (int i = previous; i < r.low; i++) {
				sb.append(i + ",");
			}
			sb.append(r + ",");
			previous = r.high;
		}
		for (int i = previous; i < rangesUpperBound; i++) {
			sb.append(i + ",");
		}
		return sb.toString();
	}
	*/
	
	@Override
	public String toString() {
		StringBuilder sb = new StringBuilder();
		for (Range r : ranges) {
			sb.append("[" + r.low + "," + r.high + ") ");
			
		}
		return sb.toString();
	}

	public class Range implements Comparable {
		public final int low; /* inclusive */
		public final int high; /* exclusive */
		
		public Range(int low, int high) {
			if (low < rangesLowerBound || high > rangesUpperBound + 1) {
				throw new IllegalArgumentException("Range exceeds bounds.");
			}
			if (low >= high) {
				throw new IllegalArgumentException("Low must less than high.");
			}
			this.low = low;
			this.high = high;
		}

		public Range(int num) {
			if (num < rangesLowerBound || num > rangesUpperBound + 1) {
				throw new IllegalArgumentException("Range exceeds bounds.");
			}
			this.low = num;
			this.high = num + 1;
		}
		
		public boolean overlaps(Range r) {
			return low < r.high && r.low < high;
		}
		
		public boolean overlapsAdjacent(Range r) {
			return low <= r.high && r.low <= high;
		}
		
		
		
		public Range merge(Range r) {
			int newLow = 0;
			int newHigh = 0;
			if (!overlapsAdjacent(r)) {
				throw new RuntimeException("Cannot merge non-overlapping ranges");
			}
			
			if (low <= r.low) {
				newLow = low;
			} else {
				newLow = r.low;
			}
			
			if (high >= r.high) {
				newHigh = high;
			} else {
				newHigh = r.high;
			}
			
			return new Range(newLow, newHigh);
		}
		
		public RangeSet complement() {
			if (low == rangesLowerBound && high == rangesUpperBound + 1) {
				/* empty range set */
				return new RangeSet(rangesLowerBound, rangesUpperBound);
			}
			if (low == rangesLowerBound) {
				/* only one range */
				Range rComplement = new Range(high, rangesUpperBound + 1);
				RangeSet rs = new RangeSet(rangesLowerBound, rangesUpperBound);
				rs.union(rComplement);
				return rs;
			}
			if (high == rangesUpperBound + 1) {
				/* only one range */
				Range rComplement = new Range(rangesLowerBound, low);
				RangeSet rs = new RangeSet(rangesLowerBound, rangesUpperBound);
				rs.union(rComplement);
				return rs;
			}
			
			Range r1 = new Range(rangesLowerBound, low);
			Range r2 = new Range(high, rangesUpperBound + 1);
			/* TODO there might be a better way to do this... */
			RangeSet comp1 = new RangeSet(rangesLowerBound, rangesUpperBound);
			comp1.union(r1);
			RangeSet comp2 = new RangeSet(rangesLowerBound, rangesUpperBound);
			comp2.union(r2);
			comp1.union(comp2);
			return comp1;
		}

		@Override
		public int compareTo(Range r) {
			if (low != r.low) {
				return low - r.low;
			}
			return high - r.high;
		}
		
		@Override
		public String toString() {
			return "[" + low + ", " + (high - 1) + "]";
		}
		
		@Override
		public boolean equals(Object o) {
			if (o == null) {
				return false;
			}
		    if (o == this) {
		    	return true;
		    }
		    if (!(o instanceof Range)) {
		    	return false;
		    }
		    
		    Range r = (Range) o;
		    return low == r.low && high == r.high;
		}
		
		@Override
		public int hashCode() {
			return 5 * low + 7 * high;
		}
	}

	
	public static void main(String [] args) {
		RangeSet rs1 =  new RangeSet(0, 256);
		Range r11 = rs1.createRange(0, 10);
		rs1.union(r11);
		Range r12 = rs1.createRange(20, 30);
		rs1.union(r12);
		Range r13 = rs1.createRange(40, 50);
		rs1.union(r13);
		
		RangeSet rs2 =  new RangeSet(0, 256);
		Range r21 = rs2.createRange(10, 20);
		rs2.union(r21);
		Range r22 = rs2.createRange(30, 40);
		rs1.union(r22);
		Range r23 = rs2.createRange(50, 60);
		rs2.union(r23);
		
		//rs1.union(rs2);
		
		System.out.println(rs1);
		System.out.println(rs2);
		rs1.intersection(rs2);
		System.out.println(rs1);
		
		RangeSet rs3 =  new RangeSet(0, 256);
		Range r31 = rs3.createRange(0, 256);
		rs3.union(r31);
		System.out.println(rs3);
		rs3.complement();
		System.out.println(rs3);
		rs3.complement();
		System.out.println(rs3);
	}

	@Override
	public Iterator iterator() {
		return ranges.iterator();
	}

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy