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

org.wildfly.common.net.CidrAddressTable Maven / Gradle / Ivy

Go to download

This artifact provides a single jar that contains all classes required to use remote EJB and JMS, including all dependencies. It is intended for use by those not using maven, maven users should just import the EJB and JMS BOM's instead (shaded JAR's cause lots of problems with maven, as it is very easy to inadvertently end up with different versions on classes on the class path).

The newest version!
/*
 * JBoss, Home of Professional Open Source.
 * Copyright 2017 Red Hat, Inc., and individual contributors
 * as indicated by the @author tags.
 *
 * 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 org.wildfly.common.net;

import java.net.Inet4Address;
import java.net.InetAddress;
import java.util.Arrays;
import java.util.Iterator;
import java.util.NoSuchElementException;
import java.util.Objects;
import java.util.Spliterator;
import java.util.Spliterators;
import java.util.concurrent.atomic.AtomicReference;

import org.wildfly.common.Assert;

/**
 * A table for mapping IP addresses to objects using {@link CidrAddress} instances for matching.
 *
 * @author David M. Lloyd
 */
public final class CidrAddressTable implements Iterable> {

    @SuppressWarnings("rawtypes")
    private static final Mapping[] NO_MAPPINGS = new Mapping[0];

    private final AtomicReference[]> mappingsRef;

    public CidrAddressTable() {
        mappingsRef = new AtomicReference<>(empty());
    }

    private CidrAddressTable(Mapping[] mappings) {
        mappingsRef = new AtomicReference<>(mappings);
    }

    public T getOrDefault(InetAddress address, T defVal) {
        Assert.checkNotNullParam("address", address);
        final Mapping mapping = doGet(mappingsRef.get(), address.getAddress(), address instanceof Inet4Address ? 32 : 128, Inet.getScopeId(address));
        return mapping == null ? defVal : mapping.value;
    }

    public T get(InetAddress address) {
        return getOrDefault(address, null);
    }

    public T put(CidrAddress block, T value) {
        Assert.checkNotNullParam("block", block);
        Assert.checkNotNullParam("value", value);
        return doPut(block, null, value, true, true);
    }

    public T putIfAbsent(CidrAddress block, T value) {
        Assert.checkNotNullParam("block", block);
        Assert.checkNotNullParam("value", value);
        return doPut(block, null, value, true, false);
    }

    public T replaceExact(CidrAddress block, T value) {
        Assert.checkNotNullParam("block", block);
        Assert.checkNotNullParam("value", value);
        return doPut(block, null, value, false, true);
    }

    public boolean replaceExact(CidrAddress block, T expect, T update) {
        Assert.checkNotNullParam("block", block);
        Assert.checkNotNullParam("expect", expect);
        Assert.checkNotNullParam("update", update);
        return doPut(block, expect, update, false, true) == expect;
    }

    public T removeExact(CidrAddress block) {
        Assert.checkNotNullParam("block", block);
        return doPut(block, null, null, false, true);
    }

    public boolean removeExact(CidrAddress block, T expect) {
        Assert.checkNotNullParam("block", block);
        return doPut(block, expect, null, false, true) == expect;
    }

    private T doPut(final CidrAddress block, final T expect, final T update, final boolean putIfAbsent, final boolean putIfPresent) {
        assert putIfAbsent || putIfPresent;
        final AtomicReference[]> mappingsRef = this.mappingsRef;
        final byte[] bytes = block.getNetworkAddress().getAddress();
        Mapping[] oldVal, newVal;
        int idx;
        T existing;
        boolean matchesExpected;
        do {
            oldVal = mappingsRef.get();
            idx = doFind(oldVal, bytes, block.getNetmaskBits(), block.getScopeId());
            if (idx < 0) {
                if (! putIfAbsent) {
                    return null;
                }
                existing = null;
            } else {
                existing = oldVal[idx].value;
            }
            if (expect != null) {
                matchesExpected = Objects.equals(expect, existing);
                if (! matchesExpected) {
                    return existing;
                }
            } else {
                matchesExpected = false;
            }
            if (idx >= 0 && ! putIfPresent) {
                return existing;
            }
            // now construct the new mapping
            final int oldLen = oldVal.length;
            if (update == null) {
                assert idx >= 0;
                // removal
                if (oldLen == 1) {
                    newVal = empty();
                } else {
                    final Mapping removing = oldVal[idx];
                    newVal = Arrays.copyOf(oldVal, oldLen - 1);
                    System.arraycopy(oldVal, idx + 1, newVal, idx, oldLen - idx - 1);
                    // now reparent any children that I was a parent of with my old parent
                    for (int i = 0; i < oldLen - 1; i ++) {
                        if (newVal[i].parent == removing) {
                            newVal[i] = newVal[i].withNewParent(removing.parent);
                        }
                    }
                }
            } else if (idx >= 0) {
                // replace
                newVal = oldVal.clone();
                final Mapping oldMapping = oldVal[idx];
                final Mapping newMapping = new Mapping<>(block, update, oldVal[idx].parent);
                newVal[idx] = newMapping;
                // now reparent any child to me
                for (int i = 0; i < oldLen; i ++) {
                    if (i != idx && newVal[i].parent == oldMapping) {
                        newVal[i] = newVal[i].withNewParent(newMapping);
                    }
                }
            } else {
                // add
                newVal = Arrays.copyOf(oldVal, oldLen + 1);
                final Mapping newMappingParent = doGet(oldVal, bytes, block.getNetmaskBits(), block.getScopeId());
                final Mapping newMapping = new Mapping<>(block, update, newMappingParent);
                newVal[-idx - 1] = newMapping;
                System.arraycopy(oldVal, -idx - 1, newVal, -idx, oldLen + idx + 1);
                // now reparent any children who have a parent of my (possibly null) parent but match me
                for (int i = 0; i <= oldLen; i++) {
                    if (newVal[i] != newMapping && newVal[i].parent == newMappingParent && block.matches(newVal[i].range)) {
                        newVal[i] = newVal[i].withNewParent(newMapping);
                    }
                }
            }
        } while (! mappingsRef.compareAndSet(oldVal, newVal));
        return matchesExpected ? expect : existing;
    }

    @SuppressWarnings("unchecked")
    private static  Mapping[] empty() {
        return NO_MAPPINGS;
    }

    public void clear() {
        mappingsRef.set(empty());
    }

    public int size() {
        return mappingsRef.get().length;
    }

    public boolean isEmpty() {
        return size() == 0;
    }

    public CidrAddressTable clone() {
        return new CidrAddressTable<>(mappingsRef.get());
    }

    public Iterator> iterator() {
        final Mapping[] mappings = mappingsRef.get();
        return new Iterator>() {
            int idx;

            public boolean hasNext() {
                return idx < mappings.length;
            }

            public Mapping next() {
                if (! hasNext()) throw new NoSuchElementException();
                return mappings[idx++];
            }
        };
    }

    public Spliterator> spliterator() {
        final Mapping[] mappings = mappingsRef.get();
        return Spliterators.spliterator(mappings, Spliterator.IMMUTABLE | Spliterator.ORDERED);
    }

    public String toString() {
        StringBuilder b = new StringBuilder();
        final Mapping[] mappings = mappingsRef.get();
        b.append(mappings.length).append(" mappings");
        for (final Mapping mapping : mappings) {
            b.append(System.lineSeparator()).append('\t').append(mapping.range);
            if (mapping.parent != null) {
                b.append(" (parent ").append(mapping.parent.range).append(')');
            }
            b.append(" -> ").append(mapping.value);
        }
        return b.toString();
    }

    private int doFind(Mapping[] table, byte[] bytes, int maskBits, final int scopeId) {
        int low = 0;
        int high = table.length - 1;

        while (low <= high) {
            // bisect the range
            int mid = low + high >>> 1;

            // compare the mapping at this location
            Mapping mapping = table[mid];
            int cmp = mapping.range.compareAddressBytesTo(bytes, maskBits, scopeId);

            if (cmp < 0) {
                // move to the latter half
                low = mid + 1;
            } else if (cmp > 0) {
                // move to the former half
                high = mid - 1;
            } else {
                // exact match is the best case
                return mid;
            }
        }
        // return the point we would insert at (plus one, negated)
        return -(low + 1);
    }

    private Mapping doGet(Mapping[] table, byte[] bytes, final int netmaskBits, final int scopeId) {
        int idx = doFind(table, bytes, netmaskBits, scopeId);
        if (idx >= 0) {
            // exact match
            assert table[idx].range.matches(bytes, scopeId);
            return table[idx];
        }
        // check immediate predecessor if there is one
        int pre = -idx - 2;
        if (pre >= 0) {
            if (table[pre].range.matches(bytes, scopeId)) {
                return table[pre];
            }
            // try parent
            Mapping parent = table[pre].parent;
            while (parent != null) {
                if (parent.range.matches(bytes, scopeId)) {
                    return parent;
                }
                parent = parent.parent;
            }
        }
        return null;
    }

    /**
     * A single mapping in the table.
     *
     * @param  the value type
     */
    public static final class Mapping {
        final CidrAddress range;
        final T value;
        final Mapping parent;

        Mapping(final CidrAddress range, final T value, final Mapping parent) {
            this.range = range;
            this.value = value;
            this.parent = parent;
        }

        Mapping withNewParent(Mapping newParent) {
            return new Mapping(range, value, newParent);
        }

        /**
         * Get the address range of this entry.
         *
         * @return the address range of this entry (not {@code null})
         */
        public CidrAddress getRange() {
            return range;
        }

        /**
         * Get the stored value of this entry.
         *
         * @return the stored value of this entry
         */
        public T getValue() {
            return value;
        }

        /**
         * Get the parent of this entry, if any.
         *
         * @return the parent of this entry, or {@code null} if there is no parent
         */
        public Mapping getParent() {
            return parent;
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy