001// Copyright (c) FIRST and other WPILib contributors. 002// Open Source Software; you can modify and/or share it under the terms of 003// the WPILib BSD license file in the root directory of this project. 004 005package edu.wpi.first.units; 006 007import edu.wpi.first.units.collections.LongToObjectHashMap; 008import java.util.Objects; 009import java.util.function.BiFunction; 010 011/** 012 * A helper class for creating and caching combined unit objects. This helps to reduce unnecessary 013 * object allocation by reusing already-created units. 014 * 015 * @param <A> the type of the first unit to be combined 016 * @param <B> the type of the second unit to be combined 017 * @param <Out> the type of the combinatorial unit 018 */ 019public final class CombinatoryUnitCache<A extends Unit, B extends Unit, Out extends Unit> { 020 /** 021 * Keep a cache of created instances so expressions like Volts.per(Meter) don't do any allocations 022 * after the first. 023 */ 024 private final LongToObjectHashMap<Out> m_cache = new LongToObjectHashMap<>(); 025 026 private final BiFunction<? super A, ? super B, ? extends Out> m_constructor; 027 028 /** 029 * Creates a new combinatory unit cache. The cache is initially empty and is not shared across 030 * instances. 031 * 032 * @param constructor the constructor function to use to create new combined units 033 */ 034 public CombinatoryUnitCache(BiFunction<? super A, ? super B, ? extends Out> constructor) { 035 this.m_constructor = 036 Objects.requireNonNull(constructor, "Cache unit constructor must be provided"); 037 } 038 039 /** 040 * Combines two units together and returns the result. The resulting units are cached and will be 041 * returned on successive calls to avoid allocating many duplicate objects. The combination output 042 * type is determined by the factory function passed into the cache's constructor. 043 * 044 * @param a the first unit 045 * @param b the second unit 046 * @return the combined unit 047 */ 048 public Out combine(A a, B b) { 049 final long key = ((long) a.hashCode()) << 32L | (b.hashCode() & 0xFFFFFFFFL); 050 051 var existing = m_cache.get(key); 052 if (existing != null) { 053 return existing; 054 } 055 056 var newUnit = m_constructor.apply(a, b); 057 m_cache.put(key, newUnit); 058 return newUnit; 059 } 060}