1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 package org.apache.hadoop.hbase.client;
22
23 import java.io.DataInput;
24 import java.io.DataOutput;
25 import java.io.IOException;
26 import java.util.ArrayList;
27 import java.util.Arrays;
28 import java.util.Comparator;
29 import java.util.List;
30 import java.util.Map;
31 import java.util.NavigableMap;
32 import java.util.TreeMap;
33
34 import org.apache.hadoop.hbase.KeyValue;
35 import org.apache.hadoop.hbase.KeyValue.SplitKeyValue;
36 import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
37 import org.apache.hadoop.hbase.io.WritableWithSize;
38 import org.apache.hadoop.hbase.util.Bytes;
39 import org.apache.hadoop.io.Writable;
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68 public class Result implements Writable, WritableWithSize {
69 private static final byte RESULT_VERSION = (byte)1;
70
71 private KeyValue [] kvs = null;
72 private NavigableMap<byte[],
73 NavigableMap<byte[], NavigableMap<Long, byte[]>>> familyMap = null;
74
75
76 private transient byte [] row = null;
77 private ImmutableBytesWritable bytes = null;
78
79
80
81
82 public Result() {}
83
84
85
86
87
88 public Result(KeyValue [] kvs) {
89 if(kvs != null && kvs.length > 0) {
90 this.kvs = kvs;
91 }
92 }
93
94
95
96
97
98 public Result(List<KeyValue> kvs) {
99 this(kvs.toArray(new KeyValue[kvs.size()]));
100 }
101
102
103
104
105
106 public Result(ImmutableBytesWritable bytes) {
107 this.bytes = bytes;
108 }
109
110
111
112
113
114
115 public byte [] getRow() {
116 if (this.row == null) {
117 if(this.kvs == null) {
118 readFields();
119 }
120 this.row = this.kvs.length == 0? null: this.kvs[0].getRow();
121 }
122 return this.row;
123 }
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145 public KeyValue[] raw() {
146 if(this.kvs == null) {
147 readFields();
148 }
149 return kvs;
150 }
151
152
153
154
155
156
157
158
159 public List<KeyValue> list() {
160 if(this.kvs == null) {
161 readFields();
162 }
163 return isEmpty()? null: Arrays.asList(raw());
164 }
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181 public List<KeyValue> getColumn(byte [] family, byte [] qualifier) {
182 List<KeyValue> result = new ArrayList<KeyValue>();
183
184 KeyValue [] kvs = raw();
185
186 if (kvs == null || kvs.length == 0) {
187 return result;
188 }
189 int pos = binarySearch(kvs, family, qualifier);
190 if (pos == -1) {
191 return result;
192 }
193
194 for (int i = pos ; i < kvs.length ; i++ ) {
195 KeyValue kv = kvs[i];
196 if (kv.matchingColumn(family,qualifier)) {
197 result.add(kv);
198 } else {
199 break;
200 }
201 }
202
203 return result;
204 }
205
206 protected int binarySearch(final KeyValue [] kvs,
207 final byte [] family,
208 final byte [] qualifier) {
209 KeyValue searchTerm =
210 KeyValue.createFirstOnRow(kvs[0].getRow(),
211 family, qualifier);
212
213
214 int pos = Arrays.binarySearch(kvs, searchTerm, KeyValue.COMPARATOR);
215
216 if (pos < 0) {
217 pos = (pos+1) * -1;
218
219 }
220 if (pos == kvs.length) {
221 return -1;
222 }
223 return pos;
224 }
225
226
227
228
229
230
231
232
233
234
235 public KeyValue getColumnLatest(byte [] family, byte [] qualifier) {
236 KeyValue [] kvs = raw();
237 if (kvs == null || kvs.length == 0) {
238 return null;
239 }
240 int pos = binarySearch(kvs, family, qualifier);
241 if (pos == -1) {
242 return null;
243 }
244 KeyValue kv = kvs[pos];
245 if (kv.matchingColumn(family, qualifier)) {
246 return kv;
247 }
248 return null;
249 }
250
251
252
253
254
255
256
257 public byte[] getValue(byte [] family, byte [] qualifier) {
258 KeyValue kv = getColumnLatest(family, qualifier);
259 if (kv == null) {
260 return null;
261 }
262 return kv.getValue();
263 }
264
265
266
267
268
269
270
271 public boolean containsColumn(byte [] family, byte [] qualifier) {
272 KeyValue kv = getColumnLatest(family, qualifier);
273 return kv != null;
274 }
275
276
277
278
279
280
281
282
283
284
285 public NavigableMap<byte[], NavigableMap<byte[], NavigableMap<Long, byte[]>>> getMap() {
286 if(this.familyMap != null) {
287 return this.familyMap;
288 }
289 if(isEmpty()) {
290 return null;
291 }
292 this.familyMap =
293 new TreeMap<byte[], NavigableMap<byte[], NavigableMap<Long, byte[]>>>
294 (Bytes.BYTES_COMPARATOR);
295 for(KeyValue kv : this.kvs) {
296 SplitKeyValue splitKV = kv.split();
297 byte [] family = splitKV.getFamily();
298 NavigableMap<byte[], NavigableMap<Long, byte[]>> columnMap =
299 familyMap.get(family);
300 if(columnMap == null) {
301 columnMap = new TreeMap<byte[], NavigableMap<Long, byte[]>>
302 (Bytes.BYTES_COMPARATOR);
303 familyMap.put(family, columnMap);
304 }
305 byte [] qualifier = splitKV.getQualifier();
306 NavigableMap<Long, byte[]> versionMap = columnMap.get(qualifier);
307 if(versionMap == null) {
308 versionMap = new TreeMap<Long, byte[]>(new Comparator<Long>() {
309 public int compare(Long l1, Long l2) {
310 return l2.compareTo(l1);
311 }
312 });
313 columnMap.put(qualifier, versionMap);
314 }
315 Long timestamp = Bytes.toLong(splitKV.getTimestamp());
316 byte [] value = splitKV.getValue();
317 versionMap.put(timestamp, value);
318 }
319 return this.familyMap;
320 }
321
322
323
324
325
326
327
328
329
330 public NavigableMap<byte[], NavigableMap<byte[], byte[]>> getNoVersionMap() {
331 if(this.familyMap == null) {
332 getMap();
333 }
334 if(isEmpty()) {
335 return null;
336 }
337 NavigableMap<byte[], NavigableMap<byte[], byte[]>> returnMap =
338 new TreeMap<byte[], NavigableMap<byte[], byte[]>>(Bytes.BYTES_COMPARATOR);
339 for(Map.Entry<byte[], NavigableMap<byte[], NavigableMap<Long, byte[]>>>
340 familyEntry : familyMap.entrySet()) {
341 NavigableMap<byte[], byte[]> qualifierMap =
342 new TreeMap<byte[], byte[]>(Bytes.BYTES_COMPARATOR);
343 for(Map.Entry<byte[], NavigableMap<Long, byte[]>> qualifierEntry :
344 familyEntry.getValue().entrySet()) {
345 byte [] value =
346 qualifierEntry.getValue().get(qualifierEntry.getValue().firstKey());
347 qualifierMap.put(qualifierEntry.getKey(), value);
348 }
349 returnMap.put(familyEntry.getKey(), qualifierMap);
350 }
351 return returnMap;
352 }
353
354
355
356
357
358
359
360
361 public NavigableMap<byte[], byte[]> getFamilyMap(byte [] family) {
362 if(this.familyMap == null) {
363 getMap();
364 }
365 if(isEmpty()) {
366 return null;
367 }
368 NavigableMap<byte[], byte[]> returnMap =
369 new TreeMap<byte[], byte[]>(Bytes.BYTES_COMPARATOR);
370 NavigableMap<byte[], NavigableMap<Long, byte[]>> qualifierMap =
371 familyMap.get(family);
372 if(qualifierMap == null) {
373 return returnMap;
374 }
375 for(Map.Entry<byte[], NavigableMap<Long, byte[]>> entry :
376 qualifierMap.entrySet()) {
377 byte [] value =
378 entry.getValue().get(entry.getValue().firstKey());
379 returnMap.put(entry.getKey(), value);
380 }
381 return returnMap;
382 }
383
384
385
386
387
388 public byte [] value() {
389 if (isEmpty()) {
390 return null;
391 }
392 return kvs[0].getValue();
393 }
394
395
396
397
398
399
400
401
402
403 public ImmutableBytesWritable getBytes() {
404 return this.bytes;
405 }
406
407
408
409
410
411 public boolean isEmpty() {
412 if(this.kvs == null) {
413 readFields();
414 }
415 return this.kvs == null || this.kvs.length == 0;
416 }
417
418
419
420
421 public int size() {
422 if(this.kvs == null) {
423 readFields();
424 }
425 return this.kvs == null? 0: this.kvs.length;
426 }
427
428
429
430
431 @Override
432 public String toString() {
433 StringBuilder sb = new StringBuilder();
434 sb.append("keyvalues=");
435 if(isEmpty()) {
436 sb.append("NONE");
437 return sb.toString();
438 }
439 sb.append("{");
440 boolean moreThanOne = false;
441 for(KeyValue kv : this.kvs) {
442 if(moreThanOne) {
443 sb.append(", ");
444 } else {
445 moreThanOne = true;
446 }
447 sb.append(kv.toString());
448 }
449 sb.append("}");
450 return sb.toString();
451 }
452
453
454 public void readFields(final DataInput in)
455 throws IOException {
456 familyMap = null;
457 row = null;
458 kvs = null;
459 int totalBuffer = in.readInt();
460 if(totalBuffer == 0) {
461 bytes = null;
462 return;
463 }
464 byte [] raw = new byte[totalBuffer];
465 readChunked(in, raw, 0, totalBuffer);
466 bytes = new ImmutableBytesWritable(raw, 0, totalBuffer);
467 }
468
469 private void readChunked(final DataInput in, byte[] dest, int ofs, int len)
470 throws IOException {
471 int maxRead = 8192;
472
473 for (; ofs < len; ofs += maxRead)
474 in.readFully(dest, ofs, Math.min(len - ofs, maxRead));
475 }
476
477
478 private void readFields() {
479 if (bytes == null) {
480 this.kvs = new KeyValue[0];
481 return;
482 }
483 byte [] buf = bytes.get();
484 int offset = bytes.getOffset();
485 int finalOffset = bytes.getSize() + offset;
486 List<KeyValue> kvs = new ArrayList<KeyValue>();
487 while(offset < finalOffset) {
488 int keyLength = Bytes.toInt(buf, offset);
489 offset += Bytes.SIZEOF_INT;
490 kvs.add(new KeyValue(buf, offset, keyLength));
491 offset += keyLength;
492 }
493 this.kvs = kvs.toArray(new KeyValue[kvs.size()]);
494 }
495
496 public long getWritableSize() {
497 if (isEmpty())
498 return Bytes.SIZEOF_INT;
499
500 long size = Bytes.SIZEOF_INT;
501
502 for (KeyValue kv : kvs) {
503 size += kv.getLength();
504 size += Bytes.SIZEOF_INT;
505 }
506
507 return size;
508 }
509
510 public void write(final DataOutput out)
511 throws IOException {
512 if(isEmpty()) {
513 out.writeInt(0);
514 } else {
515 int totalLen = 0;
516 for(KeyValue kv : kvs) {
517 totalLen += kv.getLength() + Bytes.SIZEOF_INT;
518 }
519 out.writeInt(totalLen);
520 for(KeyValue kv : kvs) {
521 out.writeInt(kv.getLength());
522 out.write(kv.getBuffer(), kv.getOffset(), kv.getLength());
523 }
524 }
525 }
526
527 public static long getWriteArraySize(Result [] results) {
528 long size = Bytes.SIZEOF_BYTE;
529 if (results == null || results.length == 0) {
530 size += Bytes.SIZEOF_INT;
531 return size;
532 }
533
534 size += Bytes.SIZEOF_INT;
535 size += Bytes.SIZEOF_INT;
536 for (Result result : results) {
537 size += Bytes.SIZEOF_INT;
538 if (result == null || result.isEmpty())
539 continue;
540
541 for (KeyValue kv : result.raw()) {
542 size += Bytes.SIZEOF_INT;
543 size += kv.getLength();
544 }
545 }
546
547 return size;
548 }
549
550 public static void writeArray(final DataOutput out, Result [] results)
551 throws IOException {
552
553
554
555
556 out.writeByte(RESULT_VERSION);
557 if(results == null || results.length == 0) {
558 out.writeInt(0);
559 return;
560 }
561 out.writeInt(results.length);
562 int bufLen = 0;
563 for(Result result : results) {
564 bufLen += Bytes.SIZEOF_INT;
565 if(result == null || result.isEmpty()) {
566 continue;
567 }
568 for(KeyValue key : result.raw()) {
569 bufLen += key.getLength() + Bytes.SIZEOF_INT;
570 }
571 }
572 out.writeInt(bufLen);
573 for(Result result : results) {
574 if(result == null || result.isEmpty()) {
575 out.writeInt(0);
576 continue;
577 }
578 out.writeInt(result.size());
579 for(KeyValue kv : result.raw()) {
580 out.writeInt(kv.getLength());
581 out.write(kv.getBuffer(), kv.getOffset(), kv.getLength());
582 }
583 }
584 }
585
586 public static Result [] readArray(final DataInput in)
587 throws IOException {
588
589
590
591
592 int version = in.readByte();
593 if (version > RESULT_VERSION) {
594 throw new IOException("version not supported");
595 }
596 int numResults = in.readInt();
597 if(numResults == 0) {
598 return new Result[0];
599 }
600 Result [] results = new Result[numResults];
601 int bufSize = in.readInt();
602 byte [] buf = new byte[bufSize];
603 int offset = 0;
604 for(int i=0;i<numResults;i++) {
605 int numKeys = in.readInt();
606 offset += Bytes.SIZEOF_INT;
607 if(numKeys == 0) {
608 results[i] = new Result((ImmutableBytesWritable)null);
609 continue;
610 }
611 int initialOffset = offset;
612 for(int j=0;j<numKeys;j++) {
613 int keyLen = in.readInt();
614 Bytes.putInt(buf, offset, keyLen);
615 offset += Bytes.SIZEOF_INT;
616 in.readFully(buf, offset, keyLen);
617 offset += keyLen;
618 }
619 int totalLength = offset - initialOffset;
620 results[i] = new Result(new ImmutableBytesWritable(buf, initialOffset,
621 totalLength));
622 }
623 return results;
624 }
625
626
627
628
629
630
631
632 public static void compareResults(Result res1, Result res2)
633 throws Exception {
634 if (res2 == null) {
635 throw new Exception("There wasn't enough rows, we stopped at "
636 + Bytes.toStringBinary(res1.getRow()));
637 }
638 if (res1.size() != res2.size()) {
639 throw new Exception("This row doesn't have the same number of KVs: "
640 + res1.toString() + " compared to " + res2.toString());
641 }
642 KeyValue[] ourKVs = res1.raw();
643 KeyValue[] replicatedKVs = res2.raw();
644 for (int i = 0; i < res1.size(); i++) {
645 if (!ourKVs[i].equals(replicatedKVs[i]) ||
646 !Bytes.equals(ourKVs[i].getValue(), replicatedKVs[i].getValue())) {
647 throw new Exception("This result was different: "
648 + res1.toString() + " compared to " + res2.toString());
649 }
650 }
651 }
652
653
654
655
656
657 public void copyFrom(Result other) {
658 this.row = other.row;
659 this.bytes = other.bytes;
660 this.familyMap = other.familyMap;
661 this.kvs = other.kvs;
662 }
663 }