PageRenderTime 45ms CodeModel.GetById 13ms RepoModel.GetById 0ms app.codeStats 1ms

/projects/tomcat-7.0.2/java/org/apache/tomcat/util/buf/StringCache.java

https://gitlab.com/essere.lab.public/qualitas.class-corpus
Java | 692 lines | 426 code | 111 blank | 155 comment | 117 complexity | fe52dbb9aa73005d908f6154af87764f MD5 | raw file
  1. /*
  2. * Licensed to the Apache Software Foundation (ASF) under one or more
  3. * contributor license agreements. See the NOTICE file distributed with
  4. * this work for additional information regarding copyright ownership.
  5. * The ASF licenses this file to You under the Apache License, Version 2.0
  6. * (the "License"); you may not use this file except in compliance with
  7. * the License. You may obtain a copy of the License at
  8. *
  9. * http://www.apache.org/licenses/LICENSE-2.0
  10. *
  11. * Unless required by applicable law or agreed to in writing, software
  12. * distributed under the License is distributed on an "AS IS" BASIS,
  13. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. * See the License for the specific language governing permissions and
  15. * limitations under the License.
  16. */
  17. package org.apache.tomcat.util.buf;
  18. import java.util.ArrayList;
  19. import java.util.HashMap;
  20. import java.util.TreeMap;
  21. import java.util.Map.Entry;
  22. /**
  23. * This class implements a String cache for ByteChunk and CharChunk.
  24. *
  25. * @author Remy Maucherat
  26. */
  27. public class StringCache {
  28. private static final org.apache.juli.logging.Log log=
  29. org.apache.juli.logging.LogFactory.getLog( StringCache.class );
  30. // ------------------------------------------------------- Static Variables
  31. /**
  32. * Enabled ?
  33. */
  34. protected static boolean byteEnabled = ("true".equals(System.getProperty(
  35. "tomcat.util.buf.StringCache.byte.enabled", "false")));
  36. protected static boolean charEnabled = ("true".equals(System.getProperty(
  37. "tomcat.util.buf.StringCache.char.enabled", "false")));
  38. protected static int trainThreshold = Integer.parseInt(System.getProperty(
  39. "tomcat.util.buf.StringCache.trainThreshold", "20000"));
  40. protected static int cacheSize = Integer.parseInt(System.getProperty(
  41. "tomcat.util.buf.StringCache.cacheSize", "200"));
  42. protected static int maxStringSize = Integer.parseInt(System.getProperty(
  43. "tomcat.util.buf.StringCache.maxStringSize", "128"));
  44. /**
  45. * Statistics hash map for byte chunk.
  46. */
  47. protected static HashMap<ByteEntry,int[]> bcStats =
  48. new HashMap<ByteEntry,int[]>(cacheSize);
  49. /**
  50. * toString count for byte chunk.
  51. */
  52. protected static int bcCount = 0;
  53. /**
  54. * Cache for byte chunk.
  55. */
  56. protected static ByteEntry[] bcCache = null;
  57. /**
  58. * Statistics hash map for char chunk.
  59. */
  60. protected static HashMap<CharEntry,int[]> ccStats =
  61. new HashMap<CharEntry,int[]>(cacheSize);
  62. /**
  63. * toString count for char chunk.
  64. */
  65. protected static int ccCount = 0;
  66. /**
  67. * Cache for char chunk.
  68. */
  69. protected static CharEntry[] ccCache = null;
  70. /**
  71. * Access count.
  72. */
  73. protected static int accessCount = 0;
  74. /**
  75. * Hit count.
  76. */
  77. protected static int hitCount = 0;
  78. // ------------------------------------------------------------ Properties
  79. /**
  80. * @return Returns the cacheSize.
  81. */
  82. public int getCacheSize() {
  83. return cacheSize;
  84. }
  85. /**
  86. * @param cacheSize The cacheSize to set.
  87. */
  88. public void setCacheSize(int cacheSize) {
  89. StringCache.cacheSize = cacheSize;
  90. }
  91. /**
  92. * @return Returns the enabled.
  93. */
  94. public boolean getByteEnabled() {
  95. return byteEnabled;
  96. }
  97. /**
  98. * @param byteEnabled The enabled to set.
  99. */
  100. public void setByteEnabled(boolean byteEnabled) {
  101. StringCache.byteEnabled = byteEnabled;
  102. }
  103. /**
  104. * @return Returns the enabled.
  105. */
  106. public boolean getCharEnabled() {
  107. return charEnabled;
  108. }
  109. /**
  110. * @param charEnabled The enabled to set.
  111. */
  112. public void setCharEnabled(boolean charEnabled) {
  113. StringCache.charEnabled = charEnabled;
  114. }
  115. /**
  116. * @return Returns the trainThreshold.
  117. */
  118. public int getTrainThreshold() {
  119. return trainThreshold;
  120. }
  121. /**
  122. * @param trainThreshold The trainThreshold to set.
  123. */
  124. public void setTrainThreshold(int trainThreshold) {
  125. StringCache.trainThreshold = trainThreshold;
  126. }
  127. /**
  128. * @return Returns the accessCount.
  129. */
  130. public int getAccessCount() {
  131. return accessCount;
  132. }
  133. /**
  134. * @return Returns the hitCount.
  135. */
  136. public int getHitCount() {
  137. return hitCount;
  138. }
  139. // -------------------------------------------------- Public Static Methods
  140. public void reset() {
  141. hitCount = 0;
  142. accessCount = 0;
  143. synchronized (bcStats) {
  144. bcCache = null;
  145. bcCount = 0;
  146. }
  147. synchronized (ccStats) {
  148. ccCache = null;
  149. ccCount = 0;
  150. }
  151. }
  152. public static String toString(ByteChunk bc) {
  153. // If the cache is null, then either caching is disabled, or we're
  154. // still training
  155. if (bcCache == null) {
  156. String value = bc.toStringInternal();
  157. if (byteEnabled && (value.length() < maxStringSize)) {
  158. // If training, everything is synced
  159. synchronized (bcStats) {
  160. // If the cache has been generated on a previous invocation
  161. // while waiting for the lock, just return the toString
  162. // value we just calculated
  163. if (bcCache != null) {
  164. return value;
  165. }
  166. // Two cases: either we just exceeded the train count, in
  167. // which case the cache must be created, or we just update
  168. // the count for the string
  169. if (bcCount > trainThreshold) {
  170. long t1 = System.currentTimeMillis();
  171. // Sort the entries according to occurrence
  172. TreeMap<Integer,ArrayList<ByteEntry>> tempMap =
  173. new TreeMap<Integer,ArrayList<ByteEntry>>();
  174. for (Entry<ByteEntry,int[]> item : bcStats.entrySet()) {
  175. ByteEntry entry = item.getKey();
  176. int[] countA = item.getValue();
  177. Integer count = new Integer(countA[0]);
  178. // Add to the list for that count
  179. ArrayList<ByteEntry> list = tempMap.get(count);
  180. if (list == null) {
  181. // Create list
  182. list = new ArrayList<ByteEntry>();
  183. tempMap.put(count, list);
  184. }
  185. list.add(entry);
  186. }
  187. // Allocate array of the right size
  188. int size = bcStats.size();
  189. if (size > cacheSize) {
  190. size = cacheSize;
  191. }
  192. ByteEntry[] tempbcCache = new ByteEntry[size];
  193. // Fill it up using an alphabetical order
  194. // and a dumb insert sort
  195. ByteChunk tempChunk = new ByteChunk();
  196. int n = 0;
  197. while (n < size) {
  198. Object key = tempMap.lastKey();
  199. ArrayList<ByteEntry> list = tempMap.get(key);
  200. for (int i = 0; i < list.size() && n < size; i++) {
  201. ByteEntry entry = list.get(i);
  202. tempChunk.setBytes(entry.name, 0,
  203. entry.name.length);
  204. int insertPos = findClosest(tempChunk,
  205. tempbcCache, n);
  206. if (insertPos == n) {
  207. tempbcCache[n + 1] = entry;
  208. } else {
  209. System.arraycopy(tempbcCache, insertPos + 1,
  210. tempbcCache, insertPos + 2,
  211. n - insertPos - 1);
  212. tempbcCache[insertPos + 1] = entry;
  213. }
  214. n++;
  215. }
  216. tempMap.remove(key);
  217. }
  218. bcCount = 0;
  219. bcStats.clear();
  220. bcCache = tempbcCache;
  221. if (log.isDebugEnabled()) {
  222. long t2 = System.currentTimeMillis();
  223. log.debug("ByteCache generation time: " +
  224. (t2 - t1) + "ms");
  225. }
  226. } else {
  227. bcCount++;
  228. // Allocate new ByteEntry for the lookup
  229. ByteEntry entry = new ByteEntry();
  230. entry.value = value;
  231. int[] count = bcStats.get(entry);
  232. if (count == null) {
  233. int end = bc.getEnd();
  234. int start = bc.getStart();
  235. // Create byte array and copy bytes
  236. entry.name = new byte[bc.getLength()];
  237. System.arraycopy(bc.getBuffer(), start, entry.name,
  238. 0, end - start);
  239. // Set encoding
  240. entry.enc = bc.getEncoding();
  241. // Initialize occurrence count to one
  242. count = new int[1];
  243. count[0] = 1;
  244. // Set in the stats hash map
  245. bcStats.put(entry, count);
  246. } else {
  247. count[0] = count[0] + 1;
  248. }
  249. }
  250. }
  251. }
  252. return value;
  253. } else {
  254. accessCount++;
  255. // Find the corresponding String
  256. String result = find(bc);
  257. if (result == null) {
  258. return bc.toStringInternal();
  259. }
  260. // Note: We don't care about safety for the stats
  261. hitCount++;
  262. return result;
  263. }
  264. }
  265. public static String toString(CharChunk cc) {
  266. // If the cache is null, then either caching is disabled, or we're
  267. // still training
  268. if (ccCache == null) {
  269. String value = cc.toStringInternal();
  270. if (charEnabled && (value.length() < maxStringSize)) {
  271. // If training, everything is synced
  272. synchronized (ccStats) {
  273. // If the cache has been generated on a previous invocation
  274. // while waiting for the lock, just return the toString
  275. // value we just calculated
  276. if (ccCache != null) {
  277. return value;
  278. }
  279. // Two cases: either we just exceeded the train count, in
  280. // which case the cache must be created, or we just update
  281. // the count for the string
  282. if (ccCount > trainThreshold) {
  283. long t1 = System.currentTimeMillis();
  284. // Sort the entries according to occurrence
  285. TreeMap<Integer,ArrayList<CharEntry>> tempMap =
  286. new TreeMap<Integer,ArrayList<CharEntry>>();
  287. for (Entry<CharEntry,int[]> item : ccStats.entrySet()) {
  288. CharEntry entry = item.getKey();
  289. int[] countA = item.getValue();
  290. Integer count = new Integer(countA[0]);
  291. // Add to the list for that count
  292. ArrayList<CharEntry> list = tempMap.get(count);
  293. if (list == null) {
  294. // Create list
  295. list = new ArrayList<CharEntry>();
  296. tempMap.put(count, list);
  297. }
  298. list.add(entry);
  299. }
  300. // Allocate array of the right size
  301. int size = ccStats.size();
  302. if (size > cacheSize) {
  303. size = cacheSize;
  304. }
  305. CharEntry[] tempccCache = new CharEntry[size];
  306. // Fill it up using an alphabetical order
  307. // and a dumb insert sort
  308. CharChunk tempChunk = new CharChunk();
  309. int n = 0;
  310. while (n < size) {
  311. Object key = tempMap.lastKey();
  312. ArrayList<CharEntry> list = tempMap.get(key);
  313. for (int i = 0; i < list.size() && n < size; i++) {
  314. CharEntry entry = list.get(i);
  315. tempChunk.setChars(entry.name, 0,
  316. entry.name.length);
  317. int insertPos = findClosest(tempChunk,
  318. tempccCache, n);
  319. if (insertPos == n) {
  320. tempccCache[n + 1] = entry;
  321. } else {
  322. System.arraycopy(tempccCache, insertPos + 1,
  323. tempccCache, insertPos + 2,
  324. n - insertPos - 1);
  325. tempccCache[insertPos + 1] = entry;
  326. }
  327. n++;
  328. }
  329. tempMap.remove(key);
  330. }
  331. ccCount = 0;
  332. ccStats.clear();
  333. ccCache = tempccCache;
  334. if (log.isDebugEnabled()) {
  335. long t2 = System.currentTimeMillis();
  336. log.debug("CharCache generation time: " +
  337. (t2 - t1) + "ms");
  338. }
  339. } else {
  340. ccCount++;
  341. // Allocate new CharEntry for the lookup
  342. CharEntry entry = new CharEntry();
  343. entry.value = value;
  344. int[] count = ccStats.get(entry);
  345. if (count == null) {
  346. int end = cc.getEnd();
  347. int start = cc.getStart();
  348. // Create char array and copy chars
  349. entry.name = new char[cc.getLength()];
  350. System.arraycopy(cc.getBuffer(), start, entry.name,
  351. 0, end - start);
  352. // Initialize occurrence count to one
  353. count = new int[1];
  354. count[0] = 1;
  355. // Set in the stats hash map
  356. ccStats.put(entry, count);
  357. } else {
  358. count[0] = count[0] + 1;
  359. }
  360. }
  361. }
  362. }
  363. return value;
  364. } else {
  365. accessCount++;
  366. // Find the corresponding String
  367. String result = find(cc);
  368. if (result == null) {
  369. return cc.toStringInternal();
  370. }
  371. // Note: We don't care about safety for the stats
  372. hitCount++;
  373. return result;
  374. }
  375. }
  376. // ----------------------------------------------------- Protected Methods
  377. /**
  378. * Compare given byte chunk with byte array.
  379. * Return -1, 0 or +1 if inferior, equal, or superior to the String.
  380. */
  381. protected static final int compare(ByteChunk name, byte[] compareTo) {
  382. int result = 0;
  383. byte[] b = name.getBuffer();
  384. int start = name.getStart();
  385. int end = name.getEnd();
  386. int len = compareTo.length;
  387. if ((end - start) < len) {
  388. len = end - start;
  389. }
  390. for (int i = 0; (i < len) && (result == 0); i++) {
  391. if (b[i + start] > compareTo[i]) {
  392. result = 1;
  393. } else if (b[i + start] < compareTo[i]) {
  394. result = -1;
  395. }
  396. }
  397. if (result == 0) {
  398. if (compareTo.length > (end - start)) {
  399. result = -1;
  400. } else if (compareTo.length < (end - start)) {
  401. result = 1;
  402. }
  403. }
  404. return result;
  405. }
  406. /**
  407. * Find an entry given its name in the cache and return the associated
  408. * String.
  409. */
  410. protected static final String find(ByteChunk name) {
  411. int pos = findClosest(name, bcCache, bcCache.length);
  412. if ((pos < 0) || (compare(name, bcCache[pos].name) != 0)
  413. || !(name.getEncoding().equals(bcCache[pos].enc))) {
  414. return null;
  415. } else {
  416. return bcCache[pos].value;
  417. }
  418. }
  419. /**
  420. * Find an entry given its name in a sorted array of map elements.
  421. * This will return the index for the closest inferior or equal item in the
  422. * given array.
  423. */
  424. protected static final int findClosest(ByteChunk name, ByteEntry[] array,
  425. int len) {
  426. int a = 0;
  427. int b = len - 1;
  428. // Special cases: -1 and 0
  429. if (b == -1) {
  430. return -1;
  431. }
  432. if (compare(name, array[0].name) < 0) {
  433. return -1;
  434. }
  435. if (b == 0) {
  436. return 0;
  437. }
  438. int i = 0;
  439. while (true) {
  440. i = (b + a) / 2;
  441. int result = compare(name, array[i].name);
  442. if (result == 1) {
  443. a = i;
  444. } else if (result == 0) {
  445. return i;
  446. } else {
  447. b = i;
  448. }
  449. if ((b - a) == 1) {
  450. int result2 = compare(name, array[b].name);
  451. if (result2 < 0) {
  452. return a;
  453. } else {
  454. return b;
  455. }
  456. }
  457. }
  458. }
  459. /**
  460. * Compare given char chunk with char array.
  461. * Return -1, 0 or +1 if inferior, equal, or superior to the String.
  462. */
  463. protected static final int compare(CharChunk name, char[] compareTo) {
  464. int result = 0;
  465. char[] c = name.getBuffer();
  466. int start = name.getStart();
  467. int end = name.getEnd();
  468. int len = compareTo.length;
  469. if ((end - start) < len) {
  470. len = end - start;
  471. }
  472. for (int i = 0; (i < len) && (result == 0); i++) {
  473. if (c[i + start] > compareTo[i]) {
  474. result = 1;
  475. } else if (c[i + start] < compareTo[i]) {
  476. result = -1;
  477. }
  478. }
  479. if (result == 0) {
  480. if (compareTo.length > (end - start)) {
  481. result = -1;
  482. } else if (compareTo.length < (end - start)) {
  483. result = 1;
  484. }
  485. }
  486. return result;
  487. }
  488. /**
  489. * Find an entry given its name in the cache and return the associated
  490. * String.
  491. */
  492. protected static final String find(CharChunk name) {
  493. int pos = findClosest(name, ccCache, ccCache.length);
  494. if ((pos < 0) || (compare(name, ccCache[pos].name) != 0)) {
  495. return null;
  496. } else {
  497. return ccCache[pos].value;
  498. }
  499. }
  500. /**
  501. * Find an entry given its name in a sorted array of map elements.
  502. * This will return the index for the closest inferior or equal item in the
  503. * given array.
  504. */
  505. protected static final int findClosest(CharChunk name, CharEntry[] array,
  506. int len) {
  507. int a = 0;
  508. int b = len - 1;
  509. // Special cases: -1 and 0
  510. if (b == -1) {
  511. return -1;
  512. }
  513. if (compare(name, array[0].name) < 0 ) {
  514. return -1;
  515. }
  516. if (b == 0) {
  517. return 0;
  518. }
  519. int i = 0;
  520. while (true) {
  521. i = (b + a) / 2;
  522. int result = compare(name, array[i].name);
  523. if (result == 1) {
  524. a = i;
  525. } else if (result == 0) {
  526. return i;
  527. } else {
  528. b = i;
  529. }
  530. if ((b - a) == 1) {
  531. int result2 = compare(name, array[b].name);
  532. if (result2 < 0) {
  533. return a;
  534. } else {
  535. return b;
  536. }
  537. }
  538. }
  539. }
  540. // -------------------------------------------------- ByteEntry Inner Class
  541. public static class ByteEntry {
  542. public byte[] name = null;
  543. public String enc = null;
  544. public String value = null;
  545. @Override
  546. public String toString() {
  547. return value;
  548. }
  549. @Override
  550. public int hashCode() {
  551. return value.hashCode();
  552. }
  553. @Override
  554. public boolean equals(Object obj) {
  555. if (obj instanceof ByteEntry) {
  556. return value.equals(((ByteEntry) obj).value);
  557. }
  558. return false;
  559. }
  560. }
  561. // -------------------------------------------------- CharEntry Inner Class
  562. public static class CharEntry {
  563. public char[] name = null;
  564. public String value = null;
  565. @Override
  566. public String toString() {
  567. return value;
  568. }
  569. @Override
  570. public int hashCode() {
  571. return value.hashCode();
  572. }
  573. @Override
  574. public boolean equals(Object obj) {
  575. if (obj instanceof CharEntry) {
  576. return value.equals(((CharEntry) obj).value);
  577. }
  578. return false;
  579. }
  580. }
  581. }