code: /*
   * Copyright (c) 2019 the Eclipse Milo Authors
   *
   * This program and the accompanying materials are made
   * available under the terms of the Eclipse Public License 2.0
   * which is available at https://www.eclipse.org/legal/epl-2.0/
   *
   * SPDX-License-Identifier: EPL-2.0
   */
  
  package org.eclipse.milo.opcua.stack.core.types.builtin.unsigned;
  
  import java.io.ObjectStreamException;
  
  import org.jetbrains.annotations.NotNull;
  
  /**
   * The <code>unsigned int</code> type
   *
   * @author Lukas Eder
   * @author Ed Schaller
   * @author Jens Nerche
   */
  public final class UInteger extends UNumber implements Comparable<UInteger> {
      private static final Class<UInteger> CLASS = UInteger.class;
      private static final String CLASS_NAME = CLASS.getName();
  
      /**
       * System property name for the property to set the size of the pre-cache.
       */
      private static final String PRECACHE_PROPERTY = CLASS_NAME + ".precacheSize";
  
      /**
       * Default size for the value cache.
       */
      private static final int DEFAULT_PRECACHE_SIZE = 256;
  
      /**
       * Generated UID
       */
      private static final long serialVersionUID = -6821055240959745390L;
  
      /**
       * Cached values
       */
      private static final UInteger[] VALUES = mkValues();
  
      /**
       * A constant holding the minimum value an <code>unsigned int</code> can
       * have, 0.
       */
      public static final long MIN_VALUE = 0x00000000;
  
      /**
       * A constant holding the maximum value an <code>unsigned int</code> can
       * have, 2<sup>32</sup>-1.
       */
      public static final long MAX_VALUE = 0xffffffffL;
  
      /**
       * A constant holding the minimum value an <code>unsigned int</code> can
       * have as UInteger, 0.
       */
      public static final UInteger MIN = valueOf(MIN_VALUE);
  
      /**
       * A constant holding the maximum value an <code>unsigned int</code> can
       * have as UInteger, 2<sup>32</sup>-1.
       */
      public static final UInteger MAX = valueOf(MAX_VALUE);
  
      /**
       * The value modelling the content of this <code>unsigned int</code>
       */
      private final long value;
  
      /**
       * Figure out the size of the precache.
       *
       * @return The parsed value of the system property
       * {@link #PRECACHE_PROPERTY} or {@link #DEFAULT_PRECACHE_SIZE} if
       * the property is not set, not a number or retrieving results in a
       * {@link SecurityException}. If the parsed value is zero or
       * negative no cache will be created. If the value is larger than
       * {@link Integer#MAX_VALUE} then Integer#MAX_VALUE will be used.
       */
      private static int getPrecacheSize() {
          String prop = null;
          long propParsed;
  
          try {
              prop = System.getProperty(PRECACHE_PROPERTY);
          } catch (SecurityException e) {
              // security manager stopped us so use default
              // FIXME: should we log this somewhere?
              return DEFAULT_PRECACHE_SIZE;
          }
          if (prop == null)
              return DEFAULT_PRECACHE_SIZE;
          if (prop.length() <= 0) {
              // empty value
              // FIXME: should we log this somewhere?
              return DEFAULT_PRECACHE_SIZE;
          }
          try {
              propParsed = Long.parseLong(prop);
          } catch (NumberFormatException e) {
              // not a valid number
              // FIXME: should we log this somewhere?
              return DEFAULT_PRECACHE_SIZE;
          }
          // treat negative value as no cache...
          if (propParsed < 0)
              return 0;
          if (propParsed > Integer.MAX_VALUE) {
              // FIXME: should we log this somewhere?
              return Integer.MAX_VALUE;
          }
          return (int) propParsed;
      }
  
      /**
       * Generate a cached value for initial unsigned integer values.
       *
       * @return Array of cached values for UInteger
       */
      private static UInteger[] mkValues() {
          int precacheSize = getPrecacheSize();
          UInteger[] ret;
  
          if (precacheSize <= 0)
              return null;
          ret = new UInteger[precacheSize];
          for (int i = 0; i < precacheSize; i++)
              ret[i] = new UInteger(i);
          return ret;
      }
  
      /**
       * Unchecked internal constructor.
       * This serves to construct without unnecessary value checks.
       *
       * @param value  The value to wrap
       * @param unused Unused parameter to distinguish between this and the
       *               public constructor.
       */
      private UInteger(long value, boolean unused) {
          this.value = value;
      }
  
      /**
       * Retrieve a cached value.
       *
       * @param value Cached value to retrieve
       * @return Cached value if one exists. Null otherwise.
       */
      private static UInteger getCached(long value) {
          if (VALUES != null && value < VALUES.length)
              return VALUES[(int) value];
          return null;
      }
  
      /**
       * Get the value of a long without checking the value.
       */
      private static UInteger valueOfUnchecked(long value) {
          UInteger cached;
  
          if ((cached = getCached(value)) != null)
              return cached;
          return new UInteger(value, true);
      }
  
      /**
       * Create an <code>unsigned int</code>
       *
       * @throws NumberFormatException If <code>value</code> does not contain a
       *                               parsable <code>unsigned int</code>.
       */
      public static UInteger valueOf(String value) throws NumberFormatException {
          return valueOfUnchecked(rangeCheck(Long.parseLong(value)));
      }
  
      /**
       * Create an <code>unsigned int</code> by masking it with
       * <code>0xFFFFFFFF</code> i.e. <code>(int) -1</code> becomes
       * <code>(uint) 4294967295</code>
       */
      public static UInteger valueOf(int value) {
          return valueOfUnchecked(value & MAX_VALUE);
      }
  
      /**
       * Create an <code>unsigned int</code>
       *
       * @throws NumberFormatException If <code>value</code> is not in the range
       *                               of an <code>unsigned byte</code>
       */
      public static UInteger valueOf(long value) throws NumberFormatException {
          return valueOfUnchecked(rangeCheck(value));
      }
  
      /**
       * Create an <code>unsigned int</code>
       *
       * @throws NumberFormatException If <code>value</code> is not in the range
       *                               of an <code>unsigned int</code>
       */
      private UInteger(long value) throws NumberFormatException {
          this.value = rangeCheck(value);
      }
  
      /**
       * Create an <code>unsigned int</code> by masking it with
       * <code>0xFFFFFFFF</code> i.e. <code>(int) -1</code> becomes
       * <code>(uint) 4294967295</code>
       */
      private UInteger(int value) {
          this.value = value & MAX_VALUE;
      }
  
      /**
       * Create an <code>unsigned int</code>
       *
       * @throws NumberFormatException If <code>value</code> does not contain a
       *                               parsable <code>unsigned int</code>.
       */
      private UInteger(String value) throws NumberFormatException {
          this.value = rangeCheck(Long.parseLong(value));
      }
  
      /**
       * Throw exception if value out of range (long version)
       *
       * @param value Value to check
       * @return value if it is in range
       * @throws NumberFormatException if value is out of range
       */
      private static long rangeCheck(long value) throws NumberFormatException {
          if (value < MIN_VALUE || value > MAX_VALUE) {
              throw new NumberFormatException("Value is out of range : " + value);
          }
          return value;
      }
  
      /**
       * Replace version read through deserialization with cached version.
       *
       * @return cached instance of this object's value if one exists, otherwise
       * this object
       * @throws ObjectStreamException
       */
      private Object readResolve() throws ObjectStreamException {
          UInteger cached;
  
          // the value read could be invalid so check it
          rangeCheck(value);
          if ((cached = getCached(value)) != null)
              return cached;
          return this;
      }
  
      @Override
      public int intValue() {
          return (int) value;
      }
  
      @Override
      public long longValue() {
          return value;
      }
  
      @Override
      public float floatValue() {
          return value;
      }
  
      @Override
      public double doubleValue() {
          return value;
      }
  
      @Override
      public int hashCode() {
          return Long.valueOf(value).hashCode();
      }
  
      @Override
      public boolean equals(Object obj) {
          if (this == obj)
              return true;
          if (obj instanceof UInteger) {
              return value == ((UInteger) obj).value;
          }
  
          return false;
      }
  
      @Override
      public String toString() {
          return Long.valueOf(value).toString();
      }
  
      @Override
      public int compareTo(@NotNull UInteger o) {
          return (value < o.value ? -1 : (value == o.value ? 0 : 1));
      }
  
      public UInteger add(final UInteger val) {
          return valueOf(value + val.value);
      }
  
      public UInteger add(final int val) {
          return valueOf(value + val);
      }
  
      public UInteger subtract(final UInteger val) {
          return valueOf(value - val.value);
      }
  
      public UInteger subtract(final int val) {
          return valueOf(value - val);
      }
  }
  