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