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}