View Javadoc
1   /*
2    * #%L
3    * StringEncoder.java - mongodb-async-driver - Allanbank Consulting, Inc.
4    * %%
5    * Copyright (C) 2011 - 2014 Allanbank Consulting, Inc.
6    * %%
7    * Licensed under the Apache License, Version 2.0 (the "License");
8    * you may not use this file except in compliance with the License.
9    * You may obtain a copy of the License at
10   * 
11   *      http://www.apache.org/licenses/LICENSE-2.0
12   * 
13   * Unless required by applicable law or agreed to in writing, software
14   * distributed under the License is distributed on an "AS IS" BASIS,
15   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16   * See the License for the specific language governing permissions and
17   * limitations under the License.
18   * #L%
19   */
20  
21  package com.allanbank.mongodb.bson.io;
22  
23  import java.util.Collections;
24  import java.util.HashMap;
25  import java.util.List;
26  import java.util.Map;
27  import java.util.SortedMap;
28  
29  /**
30   * StringEncoderCache provides the ability to cache the encoding of a string to
31   * speed the writing of strings.
32   * <p>
33   * This class is thread safe. Thread safety is achieved by maintaining two data
34   * structures. The first is a map of seen strings to the number of times the
35   * string has been seen. The map is maintained by the base class:
36   * {@link AbstractStringCache}. The second structure is a simple map of the
37   * cached {@link String} to the encoded {@code byte[]}. The map has no locking
38   * or synchronization since it is read-only after construction.
39   * </p>
40   * 
41   * @api.no This class is <b>NOT</b> part of the drivers API. This class may be
42   *         mutated in incompatible ways between any two releases of the driver.
43   * @copyright 2014, Allanbank Consulting, Inc., All Rights Reserved
44   */
45  public class StringEncoderCache extends AbstractStringCache {
46  
47      /** The cache of strings to bytes. */
48      private Map<String, byte[]> myCache;
49  
50      /**
51       * Creates a new StringEncoder.
52       */
53      public StringEncoderCache() {
54          myCache = Collections.emptyMap();
55  
56          myMaxCacheLength = DEFAULT_MAX_CACHE_LENGTH;
57          myMaxCachEntries = DEFAULT_MAX_CACHE_ENTRIES;
58      }
59  
60      /**
61       * Looks in the cache for encoded bytes for the specified string.
62       * 
63       * @param string
64       *            The string value to find the cached bytes for.
65       * @return The cached bytes for the string. May be <code>null</code>.
66       */
67      public byte[] find(final String string) {
68          return myCache.get(string);
69      }
70  
71      /**
72       * Clears the cache.
73       */
74      @Override
75      protected void clear() {
76          myCache = Collections.emptyMap();
77          super.clear();
78      }
79  
80      /**
81       * Rebuilds the cache from the current collection of seen entries.
82       */
83      @Override
84      protected void rebuildCache() {
85          final SortedMap<Integer, List<SeenString>> order = buildCacheGroups();
86  
87          // Rebuild the cache.
88          int count = 0;
89          final Map<String, byte[]> cache = new HashMap<String, byte[]>(
90                  (int) Math.ceil(Math.min(order.size(), myMaxCachEntries) / 0.75));
91          for (final List<SeenString> seenAtCount : order.values()) {
92              for (final SeenString seen : seenAtCount) {
93                  if (count < myMaxCachEntries) {
94                      cache.put(seen.getValue(), seen.getBytes());
95                      count += 1;
96                  }
97                  else {
98                      mySeen.remove(seen.getValue());
99                  }
100             }
101         }
102 
103         myCache = cache;
104     }
105 }