1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.hadoop.hbase.io.util;
20
21 import java.util.HashMap;
22
23 import org.apache.hadoop.classification.InterfaceAudience;
24 import org.apache.hadoop.hbase.util.Bytes;
25
26 import com.google.common.base.Preconditions;
27
28
29
30
31
32
33
34
35
36
37 @InterfaceAudience.Private
38 public class LRUDictionary implements Dictionary {
39
40 BidirectionalLRUMap backingStore;
41 @Override
42 public byte[] getEntry(short idx) {
43 return backingStore.get(idx);
44 }
45
46 @Override
47 public void init(int initialSize) {
48 backingStore = new BidirectionalLRUMap(initialSize);
49 }
50 @Override
51 public short findEntry(byte[] data, int offset, int length) {
52 short ret = backingStore.findIdx(data, offset, length);
53 if (ret == NOT_IN_DICTIONARY) {
54 addEntry(data, offset, length);
55 }
56 return ret;
57 }
58
59 @Override
60 public short addEntry(byte[] data, int offset, int length) {
61 if (length <= 0) return NOT_IN_DICTIONARY;
62 return backingStore.put(data, offset, length);
63 }
64
65 @Override
66 public void clear() {
67 backingStore.clear();
68 }
69
70
71
72
73
74
75
76 static class BidirectionalLRUMap {
77 private int currSize = 0;
78
79
80 private Node head;
81 private Node tail;
82
83 private HashMap<Node, Short> nodeToIndex = new HashMap<Node, Short>();
84 private Node[] indexToNode;
85 private int initSize = 0;
86
87 public BidirectionalLRUMap(int initialSize) {
88 initSize = initialSize;
89 indexToNode = new Node[initialSize];
90 for (int i = 0; i < initialSize; i++) {
91 indexToNode[i] = new Node();
92 }
93 }
94
95 private short put(byte[] array, int offset, int length) {
96
97
98 byte[] stored = new byte[length];
99 Bytes.putBytes(stored, 0, array, offset, length);
100
101 if (currSize < initSize) {
102
103 indexToNode[currSize].setContents(stored, 0, stored.length);
104 setHead(indexToNode[currSize]);
105 short ret = (short) currSize++;
106 nodeToIndex.put(indexToNode[ret], ret);
107 return ret;
108 } else {
109 short s = nodeToIndex.remove(tail);
110 tail.setContents(stored, 0, stored.length);
111
112 nodeToIndex.put(tail, s);
113 moveToHead(tail);
114 return s;
115 }
116 }
117
118 private short findIdx(byte[] array, int offset, int length) {
119 Short s;
120 final Node comparisonNode = new Node();
121 comparisonNode.setContents(array, offset, length);
122 if ((s = nodeToIndex.get(comparisonNode)) != null) {
123 moveToHead(indexToNode[s]);
124 return s;
125 } else {
126 return -1;
127 }
128 }
129
130 private byte[] get(short idx) {
131 Preconditions.checkElementIndex(idx, currSize);
132 moveToHead(indexToNode[idx]);
133 return indexToNode[idx].container;
134 }
135
136 private void moveToHead(Node n) {
137 if (head == n) {
138
139 return;
140 }
141
142 assert n.prev != null;
143
144 n.prev.next = n.next;
145
146
147 if (n.next != null) {
148 n.next.prev = n.prev;
149 } else {
150 assert n == tail;
151 tail = n.prev;
152 }
153
154 setHead(n);
155 }
156
157 private void setHead(Node n) {
158
159 n.prev = null;
160 n.next = head;
161 if (head != null) {
162 assert head.prev == null;
163 head.prev = n;
164 }
165
166 head = n;
167
168
169 if (tail == null) {
170 tail = n;
171 }
172 }
173
174 private void clear() {
175 currSize = 0;
176 nodeToIndex.clear();
177 tail = null;
178 head = null;
179
180 for (Node n : indexToNode) {
181 n.container = null;
182 }
183
184 for (int i = 0; i < initSize; i++) {
185 indexToNode[i].next = null;
186 indexToNode[i].prev = null;
187 }
188 }
189
190 private static class Node {
191 byte[] container;
192 int offset;
193 int length;
194 Node next;
195 Node prev;
196
197 public Node() {
198 }
199
200 private void setContents(byte[] container, int offset, int length) {
201 this.container = container;
202 this.offset = offset;
203 this.length = length;
204 }
205
206 @Override
207 public int hashCode() {
208 return Bytes.hashCode(container, offset, length);
209 }
210
211 @Override
212 public boolean equals(Object other) {
213 if (!(other instanceof Node)) {
214 return false;
215 }
216
217 Node casted = (Node) other;
218 return Bytes.equals(container, offset, length, casted.container,
219 casted.offset, casted.length);
220 }
221 }
222 }
223 }