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.hbase.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 }
91
92 private short put(byte[] array, int offset, int length) {
93
94
95 byte[] stored = new byte[length];
96 Bytes.putBytes(stored, 0, array, offset, length);
97
98 if (currSize < initSize) {
99
100 if (indexToNode[currSize] == null) {
101 indexToNode[currSize] = new Node();
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 for (int i = 0; i < currSize; i++) {
176 indexToNode[i].next = null;
177 indexToNode[i].prev = null;
178 indexToNode[i].container = null;
179 }
180 currSize = 0;
181 nodeToIndex.clear();
182 tail = null;
183 head = null;
184 }
185
186 private static class Node {
187 byte[] container;
188 int offset;
189 int length;
190 Node next;
191 Node prev;
192
193 public Node() {
194 }
195
196 private void setContents(byte[] container, int offset, int length) {
197 this.container = container;
198 this.offset = offset;
199 this.length = length;
200 }
201
202 @Override
203 public int hashCode() {
204 return Bytes.hashCode(container, offset, length);
205 }
206
207 @Override
208 public boolean equals(Object other) {
209 if (!(other instanceof Node)) {
210 return false;
211 }
212
213 Node casted = (Node) other;
214 return Bytes.equals(container, offset, length, casted.container,
215 casted.offset, casted.length);
216 }
217 }
218 }
219 }