HashSlotRanges.java

package redis.clients.jedis;

import java.util.function.IntConsumer;

/**
 * An immutable set of hash slots expressed as comma-separated slots and/or inclusive
 * {@code from-to} ranges, e.g. {@code "123,456,789-1000"} ��� the slots-or-ranges wire format of
 * cluster maintenance pushes.
 */
final class HashSlotRanges {

  /** Inclusive bounds, flattened as {@code [from0, to0, from1, to1, ...]} in wire order. */
  private final int[] bounds;
  private final String source;

  private HashSlotRanges(int[] bounds, String source) {
    this.bounds = bounds;
    this.source = source;
  }

  /**
   * Parses the slots-or-ranges wire format.
   * @throws IllegalArgumentException on an empty string, an empty or non-numeric token, an inverted
   *           range, or a slot outside {@code [0, 16384)}
   */
  static HashSlotRanges parse(String s) {
    if (s == null || s.isEmpty()) {
      throw new IllegalArgumentException("Empty slot ranges");
    }
    String[] tokens = s.split(",", -1); // keep trailing empties so "1," and "," are rejected
    int[] bounds = new int[tokens.length * 2];
    for (int t = 0; t < tokens.length; t++) {
      String token = tokens[t];
      int dash = token.indexOf('-');
      int from, to;
      try {
        if (dash < 0) {
          from = to = Integer.parseInt(token);
        } else {
          from = Integer.parseInt(token.substring(0, dash));
          to = Integer.parseInt(token.substring(dash + 1));
        }
      } catch (NumberFormatException e) {
        throw new IllegalArgumentException("Unparseable slot range token: " + token, e);
      }
      if (from < 0 || to < from || to >= Protocol.CLUSTER_HASHSLOTS) {
        throw new IllegalArgumentException("Slot range out of bounds: " + token);
      }
      bounds[t * 2] = from;
      bounds[t * 2 + 1] = to;
    }
    return new HashSlotRanges(bounds, s);
  }

  boolean contains(int slot) {
    for (int i = 0; i < bounds.length; i += 2) {
      if (slot >= bounds[i] && slot <= bounds[i + 1]) {
        return true;
      }
    }
    return false;
  }

  int slotCount() {
    int count = 0;
    for (int i = 0; i < bounds.length; i += 2) {
      count += bounds[i + 1] - bounds[i] + 1;
    }
    return count;
  }

  void forEachSlot(IntConsumer action) {
    for (int i = 0; i < bounds.length; i += 2) {
      for (int slot = bounds[i]; slot <= bounds[i + 1]; slot++) {
        action.accept(slot);
      }
    }
  }

  @Override
  public String toString() {
    return source;
  }
}