Correct cache sizing for Uint types
[yangtools.git] / yang / yang-common / src / main / java / org / opendaylight / yangtools / yang / common / Uint64.java
1 /*
2  * Copyright (c) 2015 Pantheon Technologies s.r.o. and others.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8 package org.opendaylight.yangtools.yang.common;
9
10 import static com.google.common.base.Preconditions.checkArgument;
11
12 import com.google.common.annotations.Beta;
13 import com.google.common.cache.CacheBuilder;
14 import com.google.common.cache.CacheLoader;
15 import com.google.common.cache.LoadingCache;
16 import com.google.common.primitives.UnsignedLong;
17 import java.math.BigInteger;
18 import org.eclipse.jdt.annotation.NonNullByDefault;
19 import org.eclipse.jdt.annotation.Nullable;
20 import org.kohsuke.MetaInfServices;
21 import org.opendaylight.yangtools.concepts.Variant;
22
23 /**
24  * Dedicated type for YANG's 'type uint64' type.
25  *
26  * @author Robert Varga
27  */
28 @Beta
29 @NonNullByDefault
30 public class Uint64 extends Number implements CanonicalValue<Uint64> {
31     @MetaInfServices(value = CanonicalValueSupport.class)
32     public static final class Support extends AbstractCanonicalValueSupport<Uint64> {
33         public Support() {
34             super(Uint64.class);
35         }
36
37         @Override
38         public Variant<Uint64, CanonicalValueViolation> fromString(final String str) {
39             try {
40                 return Variant.ofFirst(Uint64.valueOf(str));
41             } catch (IllegalArgumentException e) {
42                 return CanonicalValueViolation.variantOf(e);
43             }
44         }
45     }
46
47     private static final CanonicalValueSupport<Uint64> SUPPORT = new Support();
48     private static final long serialVersionUID = 1L;
49     private static final long MIN_VALUE_LONG = 0;
50
51     /**
52      * Cache of first 256 values.
53      */
54     private static final Uint64[] CACHE = new Uint64[256];
55     /**
56      * Commonly encountered values.
57      */
58     private static final Uint64[] COMMON = {
59         new Uint64(Short.MAX_VALUE + 1L),
60         new Uint64(32768),
61         new Uint64(65535),
62         new Uint64(65536),
63         new Uint64(Integer.MAX_VALUE),
64         new Uint64(Integer.MAX_VALUE + 1L),
65         new Uint64(Long.MAX_VALUE),
66         new Uint64(-1L)
67     };
68
69     public static final Uint64 MIN_VALUE = valueOf(MIN_VALUE_LONG);
70     public static final Uint64 MAX_VALUE = fromLongBits(-1);
71
72     /**
73      * Tunable weak LRU cache for other values. By default it holds {@value #DEFAULT_LRU_SIZE} entries. This can be
74      * changed via {@value #LRU_SIZE_PROPERTY} system property.
75      */
76     private static final int DEFAULT_LRU_SIZE = 1024;
77     private static final String LRU_SIZE_PROPERTY = "org.opendaylight.yangtools.yang.common.Uint64.LRU.size";
78     private static final int MAX_LRU_SIZE = 0xffffff;
79     private static final int LRU_SIZE;
80
81     static {
82         final int p = Integer.getInteger(LRU_SIZE_PROPERTY, DEFAULT_LRU_SIZE);
83         LRU_SIZE = p >= 0 ? Math.min(p, MAX_LRU_SIZE) : DEFAULT_LRU_SIZE;
84     }
85
86     private static final LoadingCache<Long, Uint64> LRU = CacheBuilder.newBuilder().weakValues().maximumSize(LRU_SIZE)
87             .build(new CacheLoader<Long, Uint64>() {
88                 @Override
89                 public Uint64 load(final Long key) {
90                     return new Uint64(key);
91                 }
92             });
93
94     private final long value;
95
96     Uint64(final long value) {
97         this.value = value;
98     }
99
100     protected Uint64(final Uint64 other) {
101         this.value = other.value;
102     }
103
104     private static Uint64 instanceFor(final long value) {
105         final int slot = (int)value;
106         return slot >= 0 && slot < CACHE.length ? fromCache(slot, value) : fromCommon(value);
107     }
108
109     private static Uint64 fromCommon(final long value) {
110         for (Uint64 c : COMMON) {
111             if (c.value == value) {
112                 return c;
113             }
114         }
115         return LRU.getUnchecked(value);
116     }
117
118     private static Uint64 fromCache(final int slot, final long value) {
119         // FIXME: 4.0.0: use VarHandles here
120         Uint64 ret = CACHE[slot];
121         if (ret == null) {
122             synchronized (CACHE) {
123                 ret = CACHE[slot];
124                 if (ret == null) {
125                     ret = new Uint64(value);
126                     CACHE[slot] = ret;
127                 }
128             }
129         }
130         return ret;
131     }
132
133     public static Uint64 fromLongBits(final long bits) {
134         return instanceFor(bits);
135     }
136
137     public static Uint64 fromUnsignedLong(final UnsignedLong ulong) {
138         return instanceFor(ulong.longValue());
139     }
140
141     public static Uint64 valueOf(final byte byteVal) {
142         checkArgument(byteVal >= MIN_VALUE_LONG, "Negative values are not allowed");
143         return instanceFor(byteVal);
144     }
145
146     public static Uint64 valueOf(final short shortVal) {
147         checkArgument(shortVal >= MIN_VALUE_LONG, "Negative values are not allowed");
148         return instanceFor(shortVal);
149     }
150
151     public static Uint64 valueOf(final int intVal) {
152         checkArgument(intVal >= MIN_VALUE_LONG, "Value %s is outside of allowed range", intVal);
153         return instanceFor(intVal);
154     }
155
156     public static Uint64 valueOf(final long longVal) {
157         checkArgument(longVal >= MIN_VALUE_LONG, "Value %s is outside of allowed range", longVal);
158         return instanceFor(longVal);
159     }
160
161     public static Uint64 valueOf(final Uint8 uint) {
162         return instanceFor(uint.shortValue());
163     }
164
165     public static Uint64 valueOf(final Uint16 uint) {
166         return instanceFor(uint.intValue());
167     }
168
169     public static Uint64 valueOf(final Uint32 uint) {
170         return instanceFor(uint.longValue());
171     }
172
173     public static Uint64 valueOf(final String string) {
174         return valueOf(string, 10);
175     }
176
177     public static Uint64 valueOf(final String string, final int radix) {
178         return instanceFor(Long.parseUnsignedLong(string, radix));
179     }
180
181     public static Uint64 valueOf(final BigInteger bigInt) {
182         checkArgument(bigInt.signum() >= 0, "Negative values not allowed");
183         checkArgument(bigInt.bitLength() <= Long.SIZE, "Value %s is outside of allowed range", bigInt);
184
185         return instanceFor(bigInt.longValue());
186     }
187
188     @Override
189     public final int intValue() {
190         return (int)value;
191     }
192
193     @Override
194     public final long longValue() {
195         return value;
196     }
197
198     @Override
199     public final float floatValue() {
200         // TODO: ditch Guava
201         return UnsignedLong.fromLongBits(value).floatValue();
202     }
203
204     @Override
205     public final double doubleValue() {
206         // TODO: ditch Guava
207         return UnsignedLong.fromLongBits(value).doubleValue();
208     }
209
210     public final UnsignedLong toUnsignedLong() {
211         return UnsignedLong.fromLongBits(value);
212     }
213
214     @Override
215     @SuppressWarnings("checkstyle:parameterName")
216     public final int compareTo(final Uint64 o) {
217         return Long.compareUnsigned(value, o.value);
218     }
219
220     @Override
221     public final String toCanonicalString() {
222         return Long.toUnsignedString(value);
223     }
224
225     @Override
226     public final CanonicalValueSupport<Uint64> support() {
227         return SUPPORT;
228     }
229
230     @Override
231     public final int hashCode() {
232         return Long.hashCode(value);
233     }
234
235     @Override
236     public final boolean equals(final @Nullable Object obj) {
237         return this == obj || obj instanceof Uint64 && value == ((Uint64)obj).value;
238     }
239
240     @Override
241     public final String toString() {
242         return toCanonicalString();
243     }
244
245     private Object readResolve() {
246         return instanceFor(value);
247     }
248 }