001// ***************************************************************************************************************************
002// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements.  See the NOTICE file *
003// * distributed with this work for additional information regarding copyright ownership.  The ASF licenses this file        *
004// * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance            *
005// * 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 is distributed on an  *
010// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the License for the        *
011// * specific language governing permissions and limitations under the License.                                              *
012// ***************************************************************************************************************************
013package org.apache.juneau.internal;
014
015import java.io.*;
016import java.util.*;
017import java.util.concurrent.*;
018
019/**
020 * A utility class for caching byte arrays in memory so that duplicate arrays can be reused.
021 */
022public class ByteArrayCache {
023
024   /**
025    * Default global byte array cache.
026    *
027    * <p>
028    * Note that this can't ever get garbage collected so don't add really large arrays!
029    */
030   public static final ByteArrayCache DEFAULT = new ByteArrayCache();
031
032   private final ConcurrentHashMap<ByteArray,byte[]> cache = new ConcurrentHashMap<>();
033
034   /**
035    * Add the specified byte array to this cache.
036    *
037    * @param contents The byte array to add to this cache.
038    * @return
039    *    Either the same byte array or a previously cached byte array depending on whether the byte array already
040    *    exists in the cache.
041    */
042   public byte[] cache(byte[] contents) {
043      if (contents == null)
044         return null;
045      ByteArray ba = new ByteArray(contents);
046      cache.putIfAbsent(ba, ba.contents);
047      return cache.get(ba);
048   }
049
050   /**
051    * Add the specified input stream to this cache.
052    *
053    * @param contents The input stream whose contents are to be added to this cache.
054    * @return
055    *    Either the same byte array or a previously cached byte array depending on whether the byte array already
056    *    exists in the cache.
057    * @throws IOException
058    */
059   public byte[] cache(InputStream contents) throws IOException {
060      if (contents == null)
061         return null;
062      ByteArray ba = new ByteArray(IOUtils.readBytes(contents, 1024));
063      cache.putIfAbsent(ba, ba.contents);
064      return cache.get(ba);
065   }
066
067   /**
068    * Returns the number of byte arrays in this cache.
069    *
070    * @return The number of byte arrays in this cache.
071    */
072   public int size() {
073      return cache.size();
074   }
075
076   static final class ByteArray {
077      int hashCode;
078      byte[] contents;
079
080      ByteArray(byte[] contents) {
081         this.contents = contents;
082         int multiplier = 1;
083         for (int i = 0; i < contents.length; i++) {
084            hashCode += contents[i] * multiplier;
085            int shifted = multiplier << 5;
086            multiplier = shifted - multiplier;
087         }
088      }
089
090      @Override /* Object */
091      public int hashCode() {
092         if (hashCode == 0) {
093         }
094         return hashCode;
095      }
096
097      @Override /* Object */
098      public boolean equals(Object o) {
099         if (o instanceof ByteArray) {
100            ByteArray ba = (ByteArray)o;
101            if (ba.hashCode == hashCode)
102               return Arrays.equals(ba.contents, contents);
103         }
104         return false;
105      }
106   }
107}