001/*
002 * Copyright (C) 2007 The Guava Authors
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
005 * in compliance with the License. You may obtain a copy of the License at
006 *
007 * http://www.apache.org/licenses/LICENSE-2.0
008 *
009 * Unless required by applicable law or agreed to in writing, software distributed under the License
010 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
011 * or implied. See the License for the specific language governing permissions and limitations under
012 * the License.
013 */
014
015package com.google.common.collect;
016
017import static com.google.common.base.Preconditions.checkNotNull;
018
019import com.google.common.annotations.GwtIncompatible;
020import com.google.common.annotations.J2ktIncompatible;
021import com.google.common.annotations.VisibleForTesting;
022import com.google.common.base.Equivalence;
023import com.google.common.base.Function;
024import com.google.common.collect.MapMaker.Dummy;
025import com.google.common.collect.MapMakerInternalMap.InternalEntry;
026import com.google.errorprone.annotations.CanIgnoreReturnValue;
027import org.jspecify.annotations.Nullable;
028
029/**
030 * Contains static methods pertaining to instances of {@link Interner}.
031 *
032 * @author Kevin Bourrillion
033 * @since 3.0
034 */
035@J2ktIncompatible
036@GwtIncompatible
037public final class Interners {
038  private Interners() {}
039
040  /**
041   * Builder for {@link Interner} instances.
042   *
043   * @since 21.0
044   */
045  public static class InternerBuilder {
046    private final MapMaker mapMaker = new MapMaker();
047    private boolean strong = true;
048
049    private InternerBuilder() {}
050
051    /**
052     * Instructs the {@link InternerBuilder} to build a strong interner.
053     *
054     * @see Interners#newStrongInterner()
055     */
056    @CanIgnoreReturnValue
057    public InternerBuilder strong() {
058      this.strong = true;
059      return this;
060    }
061
062    /**
063     * Instructs the {@link InternerBuilder} to build a weak interner.
064     *
065     * @see Interners#newWeakInterner()
066     */
067    @CanIgnoreReturnValue
068    @GwtIncompatible("java.lang.ref.WeakReference")
069    public InternerBuilder weak() {
070      this.strong = false;
071      return this;
072    }
073
074    /**
075     * Sets the concurrency level that will be used by the to-be-built {@link Interner}.
076     *
077     * @see MapMaker#concurrencyLevel(int)
078     */
079    @CanIgnoreReturnValue
080    public InternerBuilder concurrencyLevel(int concurrencyLevel) {
081      this.mapMaker.concurrencyLevel(concurrencyLevel);
082      return this;
083    }
084
085    public <E> Interner<E> build() {
086      if (!strong) {
087        mapMaker.weakKeys();
088      }
089      return new InternerImpl<>(mapMaker);
090    }
091  }
092
093  /** Returns a fresh {@link InternerBuilder} instance. */
094  public static InternerBuilder newBuilder() {
095    return new InternerBuilder();
096  }
097
098  /**
099   * Returns a new thread-safe interner which retains a strong reference to each instance it has
100   * interned, thus preventing these instances from being garbage-collected. If this retention is
101   * acceptable, this implementation may perform better than {@link #newWeakInterner}.
102   */
103  public static <E> Interner<E> newStrongInterner() {
104    return newBuilder().strong().build();
105  }
106
107  /**
108   * Returns a new thread-safe interner which retains a weak reference to each instance it has
109   * interned, and so does not prevent these instances from being garbage-collected. This most
110   * likely does not perform as well as {@link #newStrongInterner}, but is the best alternative when
111   * the memory usage of that implementation is unacceptable.
112   */
113  @GwtIncompatible("java.lang.ref.WeakReference")
114  public static <E> Interner<E> newWeakInterner() {
115    return newBuilder().weak().build();
116  }
117
118  @VisibleForTesting
119  static final class InternerImpl<E> implements Interner<E> {
120    // MapMaker is our friend, we know about this type
121    @VisibleForTesting final MapMakerInternalMap<E, Dummy, ?, ?> map;
122
123    private InternerImpl(MapMaker mapMaker) {
124      this.map =
125          MapMakerInternalMap.createWithDummyValues(mapMaker.keyEquivalence(Equivalence.equals()));
126    }
127
128    @Override
129    public E intern(E sample) {
130      while (true) {
131        // trying to read the canonical...
132        @SuppressWarnings("rawtypes") // using raw types to avoid a bug in our nullness checker :(
133        InternalEntry entry = map.getEntry(sample);
134        if (entry != null) {
135          Object canonical = entry.getKey();
136          if (canonical != null) { // only matters if weak/soft keys are used
137            // The compiler would know this is safe if not for our use of raw types (see above).
138            @SuppressWarnings("unchecked")
139            E result = (E) canonical;
140            return result;
141          }
142        }
143
144        // didn't see it, trying to put it instead...
145        Dummy sneaky = map.putIfAbsent(sample, Dummy.VALUE);
146        if (sneaky == null) {
147          return sample;
148        } else {
149          /* Someone beat us to it! Trying again...
150           *
151           * Technically this loop not guaranteed to terminate, so theoretically (extremely
152           * unlikely) this thread might starve, but even then, there is always going to be another
153           * thread doing progress here.
154           */
155        }
156      }
157    }
158  }
159
160  /**
161   * Returns a function that delegates to the {@link Interner#intern} method of the given interner.
162   *
163   * @since 8.0
164   */
165  public static <E> Function<E, E> asFunction(Interner<E> interner) {
166    return new InternerFunction<>(checkNotNull(interner));
167  }
168
169  private static class InternerFunction<E> implements Function<E, E> {
170
171    private final Interner<E> interner;
172
173    public InternerFunction(Interner<E> interner) {
174      this.interner = interner;
175    }
176
177    @Override
178    public E apply(E input) {
179      return interner.intern(input);
180    }
181
182    @Override
183    public int hashCode() {
184      return interner.hashCode();
185    }
186
187    @Override
188    public boolean equals(@Nullable Object other) {
189      if (other instanceof InternerFunction) {
190        InternerFunction<?> that = (InternerFunction<?>) other;
191        return interner.equals(that.interner);
192      }
193
194      return false;
195    }
196  }
197}