View Javadoc

1   /**
2    *
3    * Licensed to the Apache Software Foundation (ASF) under one
4    * or more contributor license agreements.  See the NOTICE file
5    * distributed with this work for additional information
6    * regarding copyright ownership.  The ASF licenses this file
7    * to you under the Apache License, Version 2.0 (the
8    * "License"); you may not use this file except in compliance
9    * with the License.  You may obtain a copy of the License at
10   *
11   *     http://www.apache.org/licenses/LICENSE-2.0
12   *
13   * Unless required by applicable law or agreed to in writing, software
14   * distributed under the License is distributed on an "AS IS" BASIS,
15   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16   * See the License for the specific language governing permissions and
17   * limitations under the License.
18   */
19  package org.apache.hadoop.hbase;
20  
21  import java.io.ByteArrayInputStream;
22  import java.io.DataInput;
23  import java.io.DataInputStream;
24  import java.io.DataOutput;
25  import java.io.EOFException;
26  import java.io.IOException;
27  import java.io.SequenceInputStream;
28  import java.util.ArrayList;
29  import java.util.Arrays;
30  import java.util.List;
31  
32  import org.apache.hadoop.hbase.util.ByteStringer;
33  import org.apache.commons.logging.Log;
34  import org.apache.commons.logging.LogFactory;
35  import org.apache.hadoop.hbase.classification.InterfaceAudience;
36  import org.apache.hadoop.hbase.classification.InterfaceStability;
37  import org.apache.hadoop.hbase.KeyValue.KVComparator;
38  import org.apache.hadoop.hbase.client.RegionReplicaUtil;
39  import org.apache.hadoop.hbase.client.Result;
40  import org.apache.hadoop.hbase.exceptions.DeserializationException;
41  import org.apache.hadoop.hbase.protobuf.ProtobufUtil;
42  import org.apache.hadoop.hbase.protobuf.generated.HBaseProtos;
43  import org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.RegionInfo;
44  import org.apache.hadoop.hbase.util.Bytes;
45  import org.apache.hadoop.hbase.util.JenkinsHash;
46  import org.apache.hadoop.hbase.util.MD5Hash;
47  import org.apache.hadoop.hbase.util.Pair;
48  import org.apache.hadoop.hbase.util.PairOfSameType;
49  import org.apache.hadoop.io.DataInputBuffer;
50  
51  /**
52   * Information about a region. A region is a range of keys in the whole keyspace of a table, an
53   * identifier (a timestamp) for differentiating between subset ranges (after region split)
54   * and a replicaId for differentiating the instance for the same range and some status information
55   * about the region.
56   *
57   * The region has a unique name which consists of the following fields:
58   * <li> tableName   : The name of the table </li>
59   * <li> startKey    : The startKey for the region. </li>
60   * <li> regionId    : A timestamp when the region is created. </li>
61   * <li> replicaId   : An id starting from 0 to differentiate replicas of the same region range
62   * but hosted in separated servers. The same region range can be hosted in multiple locations.</li>
63   * <li> encodedName : An MD5 encoded string for the region name.</li>
64   *
65   * <br> Other than the fields in the region name, region info contains:
66   * <li> endKey      : the endKey for the region (exclusive) </li>
67   * <li> split       : Whether the region is split </li>
68   * <li> offline     : Whether the region is offline </li>
69   *
70   * In 0.98 or before, a list of table's regions would fully cover the total keyspace, and at any
71   * point in time, a row key always belongs to a single region, which is hosted in a single server.
72   * In 0.99+, a region can have multiple instances (called replicas), and thus a range (or row) can
73   * correspond to multiple HRegionInfo's. These HRI's share the same fields however except the
74   * replicaId field. If the replicaId is not set, it defaults to 0, which is compatible with the
75   * previous behavior of a range corresponding to 1 region.
76   */
77  @InterfaceAudience.Public
78  @InterfaceStability.Evolving
79  public class HRegionInfo implements Comparable<HRegionInfo> {
80    /*
81     * There are two versions associated with HRegionInfo: HRegionInfo.VERSION and
82     * HConstants.META_VERSION. HRegionInfo.VERSION indicates the data structure's versioning
83     * while HConstants.META_VERSION indicates the versioning of the serialized HRIs stored in
84     * the hbase:meta table.
85     *
86     * Pre-0.92:
87     *   HRI.VERSION == 0 and HConstants.META_VERSION does not exist
88      *  (is not stored at hbase:meta table)
89     *   HRegionInfo had an HTableDescriptor reference inside it.
90     *   HRegionInfo is serialized as Writable to hbase:meta table.
91     * For 0.92.x and 0.94.x:
92     *   HRI.VERSION == 1 and HConstants.META_VERSION == 0
93     *   HRI no longer has HTableDescriptor in it.
94     *   HRI is serialized as Writable to hbase:meta table.
95     * For 0.96.x:
96     *   HRI.VERSION == 1 and HConstants.META_VERSION == 1
97     *   HRI data structure is the same as 0.92 and 0.94
98     *   HRI is serialized as PB to hbase:meta table.
99     *
100    * Versioning of HRegionInfo is deprecated. HRegionInfo does protobuf
101    * serialization using RegionInfo class, which has it's own versioning.
102    */
103   @Deprecated
104   public static final byte VERSION = 1;
105   private static final Log LOG = LogFactory.getLog(HRegionInfo.class);
106 
107   /**
108    * The new format for a region name contains its encodedName at the end.
109    * The encoded name also serves as the directory name for the region
110    * in the filesystem.
111    *
112    * New region name format:
113    *    &lt;tablename>,,&lt;startkey>,&lt;regionIdTimestamp>.&lt;encodedName>.
114    * where,
115    *    &lt;encodedName> is a hex version of the MD5 hash of
116    *    &lt;tablename>,&lt;startkey>,&lt;regionIdTimestamp>
117    *
118    * The old region name format:
119    *    &lt;tablename>,&lt;startkey>,&lt;regionIdTimestamp>
120    * For region names in the old format, the encoded name is a 32-bit
121    * JenkinsHash integer value (in its decimal notation, string form).
122    *<p>
123    * **NOTE**
124    *
125    * The first hbase:meta region, and regions created by an older
126    * version of HBase (0.20 or prior) will continue to use the
127    * old region name format.
128    */
129 
130   /** Separator used to demarcate the encodedName in a region name
131    * in the new format. See description on new format above.
132    */
133   private static final int ENC_SEPARATOR = '.';
134   public  static final int MD5_HEX_LENGTH   = 32;
135 
136   /** A non-capture group so that this can be embedded. */
137   public static final String ENCODED_REGION_NAME_REGEX = "(?:[a-f0-9]+)";
138 
139   // to keep appended int's sorted in string format. Only allows 2 bytes to be
140   // sorted for replicaId
141   public static final String REPLICA_ID_FORMAT = "%04X";
142 
143   public static final byte REPLICA_ID_DELIMITER = (byte)'_';
144 
145   private static final int MAX_REPLICA_ID = 0xFFFF;
146   public static final int DEFAULT_REPLICA_ID = 0;
147   /**
148    * Does region name contain its encoded name?
149    * @param regionName region name
150    * @return boolean indicating if this a new format region
151    *         name which contains its encoded name.
152    */
153   private static boolean hasEncodedName(final byte[] regionName) {
154     // check if region name ends in ENC_SEPARATOR
155     if ((regionName.length >= 1)
156         && (regionName[regionName.length - 1] == ENC_SEPARATOR)) {
157       // region name is new format. it contains the encoded name.
158       return true;
159     }
160     return false;
161   }
162 
163   /**
164    * @param regionName
165    * @return the encodedName
166    */
167   public static String encodeRegionName(final byte [] regionName) {
168     String encodedName;
169     if (hasEncodedName(regionName)) {
170       // region is in new format:
171       // <tableName>,<startKey>,<regionIdTimeStamp>/encodedName/
172       encodedName = Bytes.toString(regionName,
173           regionName.length - MD5_HEX_LENGTH - 1,
174           MD5_HEX_LENGTH);
175     } else {
176       // old format region name. First hbase:meta region also
177       // use this format.EncodedName is the JenkinsHash value.
178       int hashVal = Math.abs(JenkinsHash.getInstance().hash(regionName,
179         regionName.length, 0));
180       encodedName = String.valueOf(hashVal);
181     }
182     return encodedName;
183   }
184 
185   /**
186    * @return Return a short, printable name for this region (usually encoded name) for us logging.
187    */
188   public String getShortNameToLog() {
189     return prettyPrint(this.getEncodedName());
190   }
191 
192   /**
193    * Use logging.
194    * @param encodedRegionName The encoded regionname.
195    * @return <code>hbase:meta</code> if passed <code>1028785192</code> else returns
196    * <code>encodedRegionName</code>
197    */
198   public static String prettyPrint(final String encodedRegionName) {
199     if (encodedRegionName.equals("1028785192")) {
200       return encodedRegionName + "/hbase:meta";
201     }
202     return encodedRegionName;
203   }
204 
205   private byte [] endKey = HConstants.EMPTY_BYTE_ARRAY;
206   // This flag is in the parent of a split while the parent is still referenced
207   // by daughter regions.  We USED to set this flag when we disabled a table
208   // but now table state is kept up in zookeeper as of 0.90.0 HBase.
209   private boolean offLine = false;
210   private long regionId = -1;
211   private transient byte [] regionName = HConstants.EMPTY_BYTE_ARRAY;
212   private boolean split = false;
213   private byte [] startKey = HConstants.EMPTY_BYTE_ARRAY;
214   private int hashCode = -1;
215   //TODO: Move NO_HASH to HStoreFile which is really the only place it is used.
216   public static final String NO_HASH = null;
217   private String encodedName = null;
218   private byte [] encodedNameAsBytes = null;
219   private int replicaId = DEFAULT_REPLICA_ID;
220 
221   // Current TableName
222   private TableName tableName = null;
223 
224   /** HRegionInfo for first meta region */
225   public static final HRegionInfo FIRST_META_REGIONINFO =
226       new HRegionInfo(1L, TableName.META_TABLE_NAME);
227 
228   private void setHashCode() {
229     int result = Arrays.hashCode(this.regionName);
230     result ^= this.regionId;
231     result ^= Arrays.hashCode(this.startKey);
232     result ^= Arrays.hashCode(this.endKey);
233     result ^= Boolean.valueOf(this.offLine).hashCode();
234     result ^= Arrays.hashCode(this.tableName.getName());
235     result ^= this.replicaId;
236     this.hashCode = result;
237   }
238 
239 
240   /**
241    * Private constructor used constructing HRegionInfo for the
242    * first meta regions
243    */
244   private HRegionInfo(long regionId, TableName tableName) {
245     this(regionId, tableName, DEFAULT_REPLICA_ID);
246   }
247 
248   public HRegionInfo(long regionId, TableName tableName, int replicaId) {
249     super();
250     this.regionId = regionId;
251     this.tableName = tableName;
252     this.replicaId = replicaId;
253     // Note: First Meta region replicas names are in old format
254     this.regionName = createRegionName(tableName, null, regionId, replicaId, false);
255     setHashCode();
256   }
257 
258   /** Default constructor - creates empty object
259    * @deprecated As of release 0.96
260    *             (<a href="https://issues.apache.org/jira/browse/HBASE-5453">HBASE-5453</a>).
261    *             This will be removed in HBase 2.0.0.
262    *             Used by Writables and Writables are going away.
263    */
264   @Deprecated
265   public HRegionInfo() {
266     super();
267   }
268 
269   public HRegionInfo(final TableName tableName) {
270     this(tableName, null, null);
271   }
272 
273   /**
274    * Construct HRegionInfo with explicit parameters
275    *
276    * @param tableName the table name
277    * @param startKey first key in region
278    * @param endKey end of key range
279    * @throws IllegalArgumentException
280    */
281   public HRegionInfo(final TableName tableName, final byte[] startKey, final byte[] endKey)
282   throws IllegalArgumentException {
283     this(tableName, startKey, endKey, false);
284   }
285 
286   /**
287    * Construct HRegionInfo with explicit parameters
288    *
289    * @param tableName the table descriptor
290    * @param startKey first key in region
291    * @param endKey end of key range
292    * @param split true if this region has split and we have daughter regions
293    * regions that may or may not hold references to this region.
294    * @throws IllegalArgumentException
295    */
296   public HRegionInfo(final TableName tableName, final byte[] startKey, final byte[] endKey,
297       final boolean split)
298   throws IllegalArgumentException {
299     this(tableName, startKey, endKey, split, System.currentTimeMillis());
300   }
301 
302   /**
303    * Construct HRegionInfo with explicit parameters
304    *
305    * @param tableName the table descriptor
306    * @param startKey first key in region
307    * @param endKey end of key range
308    * @param split true if this region has split and we have daughter regions
309    * regions that may or may not hold references to this region.
310    * @param regionid Region id to use.
311    * @throws IllegalArgumentException
312    */
313   public HRegionInfo(final TableName tableName, final byte[] startKey,
314                      final byte[] endKey, final boolean split, final long regionid)
315   throws IllegalArgumentException {
316     this(tableName, startKey, endKey, split, regionid, DEFAULT_REPLICA_ID);
317   }
318 
319   /**
320    * Construct HRegionInfo with explicit parameters
321    *
322    * @param tableName the table descriptor
323    * @param startKey first key in region
324    * @param endKey end of key range
325    * @param split true if this region has split and we have daughter regions
326    * regions that may or may not hold references to this region.
327    * @param regionid Region id to use.
328    * @param replicaId the replicaId to use
329    * @throws IllegalArgumentException
330    */
331   public HRegionInfo(final TableName tableName, final byte[] startKey,
332                      final byte[] endKey, final boolean split, final long regionid,
333                      final int replicaId)
334     throws IllegalArgumentException {
335     super();
336     if (tableName == null) {
337       throw new IllegalArgumentException("TableName cannot be null");
338     }
339     this.tableName = tableName;
340     this.offLine = false;
341     this.regionId = regionid;
342     this.replicaId = replicaId;
343     if (this.replicaId > MAX_REPLICA_ID) {
344       throw new IllegalArgumentException("ReplicaId cannot be greater than" + MAX_REPLICA_ID);
345     }
346 
347     this.regionName = createRegionName(this.tableName, startKey, regionId, replicaId, true);
348 
349     this.split = split;
350     this.endKey = endKey == null? HConstants.EMPTY_END_ROW: endKey.clone();
351     this.startKey = startKey == null?
352       HConstants.EMPTY_START_ROW: startKey.clone();
353     this.tableName = tableName;
354     setHashCode();
355   }
356 
357   /**
358    * Costruct a copy of another HRegionInfo
359    *
360    * @param other
361    */
362   public HRegionInfo(HRegionInfo other) {
363     super();
364     this.endKey = other.getEndKey();
365     this.offLine = other.isOffline();
366     this.regionId = other.getRegionId();
367     this.regionName = other.getRegionName();
368     this.split = other.isSplit();
369     this.startKey = other.getStartKey();
370     this.hashCode = other.hashCode();
371     this.encodedName = other.getEncodedName();
372     this.tableName = other.tableName;
373     this.replicaId = other.replicaId;
374   }
375 
376   public HRegionInfo(HRegionInfo other, int replicaId) {
377     this(other);
378     this.replicaId = replicaId;
379     this.setHashCode();
380   }
381 
382   /**
383    * Make a region name of passed parameters.
384    * @param tableName
385    * @param startKey Can be null
386    * @param regionid Region id (Usually timestamp from when region was created).
387    * @param newFormat should we create the region name in the new format
388    *                  (such that it contains its encoded name?).
389    * @return Region name made of passed tableName, startKey and id
390    */
391   public static byte [] createRegionName(final TableName tableName,
392       final byte [] startKey, final long regionid, boolean newFormat) {
393     return createRegionName(tableName, startKey, Long.toString(regionid), newFormat);
394   }
395 
396   /**
397    * Make a region name of passed parameters.
398    * @param tableName
399    * @param startKey Can be null
400    * @param id Region id (Usually timestamp from when region was created).
401    * @param newFormat should we create the region name in the new format
402    *                  (such that it contains its encoded name?).
403    * @return Region name made of passed tableName, startKey and id
404    */
405   public static byte [] createRegionName(final TableName tableName,
406       final byte [] startKey, final String id, boolean newFormat) {
407     return createRegionName(tableName, startKey, Bytes.toBytes(id), newFormat);
408   }
409 
410   /**
411    * Make a region name of passed parameters.
412    * @param tableName
413    * @param startKey Can be null
414    * @param regionid Region id (Usually timestamp from when region was created).
415    * @param replicaId
416    * @param newFormat should we create the region name in the new format
417    *                  (such that it contains its encoded name?).
418    * @return Region name made of passed tableName, startKey, id and replicaId
419    */
420   public static byte [] createRegionName(final TableName tableName,
421       final byte [] startKey, final long regionid, int replicaId, boolean newFormat) {
422     return createRegionName(tableName, startKey, Bytes.toBytes(Long.toString(regionid)),
423         replicaId, newFormat);
424   }
425 
426   /**
427    * Make a region name of passed parameters.
428    * @param tableName
429    * @param startKey Can be null
430    * @param id Region id (Usually timestamp from when region was created).
431    * @param newFormat should we create the region name in the new format
432    *                  (such that it contains its encoded name?).
433    * @return Region name made of passed tableName, startKey and id
434    */
435   public static byte [] createRegionName(final TableName tableName,
436       final byte [] startKey, final byte [] id, boolean newFormat) {
437     return createRegionName(tableName, startKey, id, DEFAULT_REPLICA_ID, newFormat);
438   }
439   /**
440    * Make a region name of passed parameters.
441    * @param tableName
442    * @param startKey Can be null
443    * @param id Region id (Usually timestamp from when region was created).
444    * @param replicaId
445    * @param newFormat should we create the region name in the new format
446    * @return Region name made of passed tableName, startKey, id and replicaId
447    */
448   public static byte [] createRegionName(final TableName tableName,
449       final byte [] startKey, final byte [] id, final int replicaId, boolean newFormat) {
450     int len = tableName.getName().length + 2 + id.length +
451         (startKey == null? 0: startKey.length);
452     if (newFormat) {
453       len += MD5_HEX_LENGTH + 2;
454     }
455     byte[] replicaIdBytes = null;
456     // Special casing: replicaId is only appended if replicaId is greater than
457     // 0. This is because all regions in meta would have to be migrated to the new
458     // name otherwise
459     if (replicaId > 0) {
460       // use string representation for replica id
461       replicaIdBytes = Bytes.toBytes(String.format(REPLICA_ID_FORMAT, replicaId));
462       len += 1 + replicaIdBytes.length;
463     }
464 
465     byte [] b = new byte [len];
466 
467     int offset = tableName.getName().length;
468     System.arraycopy(tableName.getName(), 0, b, 0, offset);
469     b[offset++] = HConstants.DELIMITER;
470     if (startKey != null && startKey.length > 0) {
471       System.arraycopy(startKey, 0, b, offset, startKey.length);
472       offset += startKey.length;
473     }
474     b[offset++] = HConstants.DELIMITER;
475     System.arraycopy(id, 0, b, offset, id.length);
476     offset += id.length;
477 
478     if (replicaIdBytes != null) {
479       b[offset++] = REPLICA_ID_DELIMITER;
480       System.arraycopy(replicaIdBytes, 0, b, offset, replicaIdBytes.length);
481       offset += replicaIdBytes.length;
482     }
483 
484     if (newFormat) {
485       //
486       // Encoded name should be built into the region name.
487       //
488       // Use the region name thus far (namely, <tablename>,<startKey>,<id>_<replicaId>)
489       // to compute a MD5 hash to be used as the encoded name, and append
490       // it to the byte buffer.
491       //
492       String md5Hash = MD5Hash.getMD5AsHex(b, 0, offset);
493       byte [] md5HashBytes = Bytes.toBytes(md5Hash);
494 
495       if (md5HashBytes.length != MD5_HEX_LENGTH) {
496         LOG.error("MD5-hash length mismatch: Expected=" + MD5_HEX_LENGTH +
497                   "; Got=" + md5HashBytes.length);
498       }
499 
500       // now append the bytes '.<encodedName>.' to the end
501       b[offset++] = ENC_SEPARATOR;
502       System.arraycopy(md5HashBytes, 0, b, offset, MD5_HEX_LENGTH);
503       offset += MD5_HEX_LENGTH;
504       b[offset++] = ENC_SEPARATOR;
505     }
506 
507     return b;
508   }
509 
510   /**
511    * Gets the table name from the specified region name.
512    * @param regionName
513    * @return Table name.
514    * @deprecated As of release 0.96
515    *             (<a href="https://issues.apache.org/jira/browse/HBASE-9508">HBASE-9508</a>).
516    *             This will be removed in HBase 2.0.0. Use {@link #getTable(byte[])}.
517    */
518   @Deprecated
519   public static byte [] getTableName(byte[] regionName) {
520     int offset = -1;
521     for (int i = 0; i < regionName.length; i++) {
522       if (regionName[i] == HConstants.DELIMITER) {
523         offset = i;
524         break;
525       }
526     }
527     byte[] buff  = new byte[offset];
528     System.arraycopy(regionName, 0, buff, 0, offset);
529     return buff;
530   }
531 
532 
533   /**
534    * Gets the table name from the specified region name.
535    * Like {@link #getTableName(byte[])} only returns a {@link TableName} rather than a byte array.
536    * @param regionName
537    * @return Table name
538    * @see #getTableName(byte[])
539    */
540   public static TableName getTable(final byte [] regionName) {
541     return TableName.valueOf(getTableName(regionName));
542   }
543 
544   /**
545    * Gets the start key from the specified region name.
546    * @param regionName
547    * @return Start key.
548    */
549   public static byte[] getStartKey(final byte[] regionName) throws IOException {
550     return parseRegionName(regionName)[1];
551   }
552 
553   /**
554    * Separate elements of a regionName.
555    * @param regionName
556    * @return Array of byte[] containing tableName, startKey and id
557    * @throws IOException
558    */
559   public static byte [][] parseRegionName(final byte [] regionName)
560   throws IOException {
561     // Region name is of the format:
562     // tablename,startkey,regionIdTimestamp[_replicaId][.encodedName.]
563     // startkey can contain the delimiter (',') so we parse from the start and end
564 
565     // parse from start
566     int offset = -1;
567     for (int i = 0; i < regionName.length; i++) {
568       if (regionName[i] == HConstants.DELIMITER) {
569         offset = i;
570         break;
571       }
572     }
573     if (offset == -1) {
574       throw new IOException("Invalid regionName format: " + Bytes.toStringBinary(regionName));
575     }
576     byte[] tableName = new byte[offset];
577     System.arraycopy(regionName, 0, tableName, 0, offset);
578     offset = -1;
579 
580     int endOffset = regionName.length;
581     // check whether regionName contains encodedName
582     if (regionName.length > MD5_HEX_LENGTH + 2
583         && regionName[regionName.length-1] == ENC_SEPARATOR
584         && regionName[regionName.length-MD5_HEX_LENGTH-2] == ENC_SEPARATOR) {
585       endOffset = endOffset - MD5_HEX_LENGTH - 2;
586     }
587 
588     // parse from end
589     byte[] replicaId = null;
590     int idEndOffset = endOffset;
591     for (int i = endOffset - 1; i > 0; i--) {
592       if (regionName[i] == REPLICA_ID_DELIMITER) { //replicaId may or may not be present
593         replicaId = new byte[endOffset - i - 1];
594         System.arraycopy(regionName, i + 1, replicaId, 0,
595           endOffset - i - 1);
596         idEndOffset = i;
597         // do not break, continue to search for id
598       }
599       if (regionName[i] == HConstants.DELIMITER) {
600         offset = i;
601         break;
602       }
603     }
604     if (offset == -1) {
605       throw new IOException("Invalid regionName format: " + Bytes.toStringBinary(regionName));
606     }
607     byte [] startKey = HConstants.EMPTY_BYTE_ARRAY;
608     if(offset != tableName.length + 1) {
609       startKey = new byte[offset - tableName.length - 1];
610       System.arraycopy(regionName, tableName.length + 1, startKey, 0,
611           offset - tableName.length - 1);
612     }
613     byte [] id = new byte[idEndOffset - offset - 1];
614     System.arraycopy(regionName, offset + 1, id, 0,
615       idEndOffset - offset - 1);
616     byte [][] elements = new byte[replicaId == null ? 3 : 4][];
617     elements[0] = tableName;
618     elements[1] = startKey;
619     elements[2] = id;
620     if (replicaId != null) {
621       elements[3] = replicaId;
622     }
623 
624     return elements;
625   }
626 
627   /** @return the regionId */
628   public long getRegionId(){
629     return regionId;
630   }
631 
632   /**
633    * @return the regionName as an array of bytes.
634    * @see #getRegionNameAsString()
635    */
636   public byte [] getRegionName(){
637     return regionName;
638   }
639 
640   /**
641    * @return Region name as a String for use in logging, etc.
642    */
643   public String getRegionNameAsString() {
644     if (hasEncodedName(this.regionName)) {
645       // new format region names already have their encoded name.
646       return Bytes.toStringBinary(this.regionName);
647     }
648 
649     // old format. regionNameStr doesn't have the region name.
650     //
651     //
652     return Bytes.toStringBinary(this.regionName) + "." + this.getEncodedName();
653   }
654 
655   /** @return the encoded region name */
656   public synchronized String getEncodedName() {
657     if (this.encodedName == null) {
658       this.encodedName = encodeRegionName(this.regionName);
659     }
660     return this.encodedName;
661   }
662 
663   public synchronized byte [] getEncodedNameAsBytes() {
664     if (this.encodedNameAsBytes == null) {
665       this.encodedNameAsBytes = Bytes.toBytes(getEncodedName());
666     }
667     return this.encodedNameAsBytes;
668   }
669 
670   /** @return the startKey */
671   public byte [] getStartKey(){
672     return startKey;
673   }
674 
675   /** @return the endKey */
676   public byte [] getEndKey(){
677     return endKey;
678   }
679 
680   /**
681    * Get current table name of the region
682    * @return byte array of table name
683    * @deprecated As of release 0.96
684    *             (<a href="https://issues.apache.org/jira/browse/HBASE-9508">HBASE-9508</a>).
685    *             This will be removed in HBase 2.0.0. Use {@link #getTable()}.
686    */
687   @Deprecated
688   public byte [] getTableName() {
689     return getTable().toBytes();
690   }
691 
692   /**
693    * Get current table name of the region
694    * @return TableName
695    * @see #getTableName()
696    */
697   public TableName getTable() {
698     // This method name should be getTableName but there was already a method getTableName
699     // that returned a byte array.  It is unfortunate given everwhere else, getTableName returns
700     // a TableName instance.
701     if (tableName == null || tableName.getName().length == 0) {
702       tableName = getTable(getRegionName());
703     }
704     return this.tableName;
705   }
706 
707   /**
708    * Returns true if the given inclusive range of rows is fully contained
709    * by this region. For example, if the region is foo,a,g and this is
710    * passed ["b","c"] or ["a","c"] it will return true, but if this is passed
711    * ["b","z"] it will return false.
712    * @throws IllegalArgumentException if the range passed is invalid (ie end < start)
713    */
714   public boolean containsRange(byte[] rangeStartKey, byte[] rangeEndKey) {
715     if (Bytes.compareTo(rangeStartKey, rangeEndKey) > 0) {
716       throw new IllegalArgumentException(
717       "Invalid range: " + Bytes.toStringBinary(rangeStartKey) +
718       " > " + Bytes.toStringBinary(rangeEndKey));
719     }
720 
721     boolean firstKeyInRange = Bytes.compareTo(rangeStartKey, startKey) >= 0;
722     boolean lastKeyInRange =
723       Bytes.compareTo(rangeEndKey, endKey) < 0 ||
724       Bytes.equals(endKey, HConstants.EMPTY_BYTE_ARRAY);
725     return firstKeyInRange && lastKeyInRange;
726   }
727 
728   /**
729    * Return true if the given row falls in this region.
730    */
731   public boolean containsRow(byte[] row) {
732     return Bytes.compareTo(row, startKey) >= 0 &&
733       (Bytes.compareTo(row, endKey) < 0 ||
734        Bytes.equals(endKey, HConstants.EMPTY_BYTE_ARRAY));
735   }
736 
737   /**
738    * @return true if this region is from hbase:meta
739    */
740   public boolean isMetaTable() {
741     return isMetaRegion();
742   }
743 
744   /** @return true if this region is a meta region */
745   public boolean isMetaRegion() {
746      return tableName.equals(HRegionInfo.FIRST_META_REGIONINFO.getTable());
747   }
748 
749   /**
750    * @return true if this region is from a system table
751    */
752   public boolean isSystemTable() {
753     return tableName.isSystemTable();
754   }
755 
756   /**
757    * @return True if has been split and has daughters.
758    */
759   public boolean isSplit() {
760     return this.split;
761   }
762 
763   /**
764    * @param split set split status
765    */
766   public void setSplit(boolean split) {
767     this.split = split;
768   }
769 
770   /**
771    * @return True if this region is offline.
772    */
773   public boolean isOffline() {
774     return this.offLine;
775   }
776 
777   /**
778    * The parent of a region split is offline while split daughters hold
779    * references to the parent. Offlined regions are closed.
780    * @param offLine Set online/offline status.
781    */
782   public void setOffline(boolean offLine) {
783     this.offLine = offLine;
784   }
785 
786   /**
787    * @return True if this is a split parent region.
788    */
789   public boolean isSplitParent() {
790     if (!isSplit()) return false;
791     if (!isOffline()) {
792       LOG.warn("Region is split but NOT offline: " + getRegionNameAsString());
793     }
794     return true;
795   }
796 
797   /**
798    * Returns the region replica id
799    * @return returns region replica id
800    */
801   public int getReplicaId() {
802     return replicaId;
803   }
804 
805   /**
806    * @see java.lang.Object#toString()
807    */
808   @Override
809   public String toString() {
810     return "{ENCODED => " + getEncodedName() + ", " +
811       HConstants.NAME + " => '" + Bytes.toStringBinary(this.regionName)
812       + "', STARTKEY => '" +
813       Bytes.toStringBinary(this.startKey) + "', ENDKEY => '" +
814       Bytes.toStringBinary(this.endKey) + "'" +
815       (isOffline()? ", OFFLINE => true": "") +
816       (isSplit()? ", SPLIT => true": "") +
817       ((replicaId > 0)? ", REPLICA_ID => " + replicaId : "") + "}";
818   }
819 
820   /**
821    * @see java.lang.Object#equals(java.lang.Object)
822    */
823   @Override
824   public boolean equals(Object o) {
825     if (this == o) {
826       return true;
827     }
828     if (o == null) {
829       return false;
830     }
831     if (!(o instanceof HRegionInfo)) {
832       return false;
833     }
834     return this.compareTo((HRegionInfo)o) == 0;
835   }
836 
837   /**
838    * @see java.lang.Object#hashCode()
839    */
840   @Override
841   public int hashCode() {
842     return this.hashCode;
843   }
844 
845   /** @return the object version number
846    * @deprecated HRI is no longer a VersionedWritable */
847   @Deprecated
848   public byte getVersion() {
849     return VERSION;
850   }
851 
852   /**
853    * @deprecated Use protobuf serialization instead.  See {@link #toByteArray()} and
854    * {@link #toDelimitedByteArray()}
855    */
856   @Deprecated
857   public void write(DataOutput out) throws IOException {
858     out.writeByte(getVersion());
859     Bytes.writeByteArray(out, endKey);
860     out.writeBoolean(offLine);
861     out.writeLong(regionId);
862     Bytes.writeByteArray(out, regionName);
863     out.writeBoolean(split);
864     Bytes.writeByteArray(out, startKey);
865     Bytes.writeByteArray(out, tableName.getName());
866     out.writeInt(hashCode);
867   }
868 
869   /**
870    * @deprecated Use protobuf deserialization instead.
871    * @see #parseFrom(byte[])
872    */
873   @Deprecated
874   public void readFields(DataInput in) throws IOException {
875     // Read the single version byte.  We don't ask the super class do it
876     // because freaks out if its not the current classes' version.  This method
877     // can deserialize version 0 and version 1 of HRI.
878     byte version = in.readByte();
879     if (version == 0) {
880       // This is the old HRI that carried an HTD.  Migrate it.  The below
881       // was copied from the old 0.90 HRI readFields.
882       this.endKey = Bytes.readByteArray(in);
883       this.offLine = in.readBoolean();
884       this.regionId = in.readLong();
885       this.regionName = Bytes.readByteArray(in);
886       this.split = in.readBoolean();
887       this.startKey = Bytes.readByteArray(in);
888       try {
889         HTableDescriptor htd = new HTableDescriptor();
890         htd.readFields(in);
891         this.tableName = htd.getTableName();
892       } catch(EOFException eofe) {
893          throw new IOException("HTD not found in input buffer", eofe);
894       }
895       this.hashCode = in.readInt();
896     } else if (getVersion() == version) {
897       this.endKey = Bytes.readByteArray(in);
898       this.offLine = in.readBoolean();
899       this.regionId = in.readLong();
900       this.regionName = Bytes.readByteArray(in);
901       this.split = in.readBoolean();
902       this.startKey = Bytes.readByteArray(in);
903       this.tableName = TableName.valueOf(Bytes.readByteArray(in));
904       this.hashCode = in.readInt();
905     } else {
906       throw new IOException("Non-migratable/unknown version=" + getVersion());
907     }
908   }
909 
910   @Deprecated
911   private void readFields(byte[] bytes, int offset, int len) throws IOException {
912     if (bytes == null || len <= 0) {
913       throw new IllegalArgumentException("Can't build a writable with empty " +
914           "bytes array");
915     }
916     DataInputBuffer in = new DataInputBuffer();
917     try {
918       in.reset(bytes, offset, len);
919       this.readFields(in);
920     } finally {
921       in.close();
922     }
923   }
924 
925   //
926   // Comparable
927   //
928 
929   @Override
930   public int compareTo(HRegionInfo o) {
931     if (o == null) {
932       return 1;
933     }
934 
935     // Are regions of same table?
936     int result = this.tableName.compareTo(o.tableName);
937     if (result != 0) {
938       return result;
939     }
940 
941     // Compare start keys.
942     result = Bytes.compareTo(this.startKey, o.startKey);
943     if (result != 0) {
944       return result;
945     }
946 
947     // Compare end keys.
948     result = Bytes.compareTo(this.endKey, o.endKey);
949 
950     if (result != 0) {
951       if (this.getStartKey().length != 0
952               && this.getEndKey().length == 0) {
953           return 1; // this is last region
954       }
955       if (o.getStartKey().length != 0
956               && o.getEndKey().length == 0) {
957           return -1; // o is the last region
958       }
959       return result;
960     }
961 
962     // regionId is usually milli timestamp -- this defines older stamps
963     // to be "smaller" than newer stamps in sort order.
964     if (this.regionId > o.regionId) {
965       return 1;
966     } else if (this.regionId < o.regionId) {
967       return -1;
968     }
969 
970     int replicaDiff = this.getReplicaId() - o.getReplicaId();
971     if (replicaDiff != 0) return replicaDiff;
972 
973     if (this.offLine == o.offLine)
974       return 0;
975     if (this.offLine == true) return -1;
976 
977     return 1;
978   }
979 
980   /**
981    * @return Comparator to use comparing {@link KeyValue}s.
982    */
983   public KVComparator getComparator() {
984     return isMetaRegion()?
985       KeyValue.META_COMPARATOR: KeyValue.COMPARATOR;
986   }
987 
988   /**
989    * Convert a HRegionInfo to a RegionInfo
990    *
991    * @return the converted RegionInfo
992    */
993   RegionInfo convert() {
994     return convert(this);
995   }
996 
997   /**
998    * Convert a HRegionInfo to a RegionInfo
999    *
1000    * @param info the HRegionInfo to convert
1001    * @return the converted RegionInfo
1002    */
1003   public static RegionInfo convert(final HRegionInfo info) {
1004     if (info == null) return null;
1005     RegionInfo.Builder builder = RegionInfo.newBuilder();
1006     builder.setTableName(ProtobufUtil.toProtoTableName(info.getTable()));
1007     builder.setRegionId(info.getRegionId());
1008     if (info.getStartKey() != null) {
1009       builder.setStartKey(ByteStringer.wrap(info.getStartKey()));
1010     }
1011     if (info.getEndKey() != null) {
1012       builder.setEndKey(ByteStringer.wrap(info.getEndKey()));
1013     }
1014     builder.setOffline(info.isOffline());
1015     builder.setSplit(info.isSplit());
1016     builder.setReplicaId(info.getReplicaId());
1017     return builder.build();
1018   }
1019 
1020   /**
1021    * Convert a RegionInfo to a HRegionInfo
1022    *
1023    * @param proto the RegionInfo to convert
1024    * @return the converted HRegionInfho
1025    */
1026   public static HRegionInfo convert(final RegionInfo proto) {
1027     if (proto == null) return null;
1028     TableName tableName =
1029         ProtobufUtil.toTableName(proto.getTableName());
1030     if (tableName.equals(TableName.META_TABLE_NAME)) {
1031       return RegionReplicaUtil.getRegionInfoForReplica(FIRST_META_REGIONINFO,
1032           proto.getReplicaId());
1033     }
1034     long regionId = proto.getRegionId();
1035     int replicaId = proto.hasReplicaId() ? proto.getReplicaId() : DEFAULT_REPLICA_ID;
1036     byte[] startKey = null;
1037     byte[] endKey = null;
1038     if (proto.hasStartKey()) {
1039       startKey = proto.getStartKey().toByteArray();
1040     }
1041     if (proto.hasEndKey()) {
1042       endKey = proto.getEndKey().toByteArray();
1043     }
1044     boolean split = false;
1045     if (proto.hasSplit()) {
1046       split = proto.getSplit();
1047     }
1048     HRegionInfo hri = new HRegionInfo(
1049         tableName,
1050         startKey,
1051         endKey, split, regionId, replicaId);
1052     if (proto.hasOffline()) {
1053       hri.setOffline(proto.getOffline());
1054     }
1055     return hri;
1056   }
1057 
1058   /**
1059    * @return This instance serialized as protobuf w/ a magic pb prefix.
1060    * @see #parseFrom(byte[])
1061    */
1062   public byte [] toByteArray() {
1063     byte [] bytes = convert().toByteArray();
1064     return ProtobufUtil.prependPBMagic(bytes);
1065   }
1066 
1067   /**
1068    * @return A deserialized {@link HRegionInfo}
1069    * or null if we failed deserialize or passed bytes null
1070    * @see #toByteArray()
1071    */
1072   public static HRegionInfo parseFromOrNull(final byte [] bytes) {
1073     if (bytes == null) return null;
1074     return parseFromOrNull(bytes, 0, bytes.length);
1075   }
1076 
1077   /**
1078    * @return A deserialized {@link HRegionInfo} or null
1079    *  if we failed deserialize or passed bytes null
1080    * @see #toByteArray()
1081    */
1082   public static HRegionInfo parseFromOrNull(final byte [] bytes, int offset, int len) {
1083     if (bytes == null || len <= 0) return null;
1084     try {
1085       return parseFrom(bytes, offset, len);
1086     } catch (DeserializationException e) {
1087       return null;
1088     }
1089   }
1090 
1091   /**
1092    * @param bytes A pb RegionInfo serialized with a pb magic prefix.
1093    * @return A deserialized {@link HRegionInfo}
1094    * @throws DeserializationException
1095    * @see #toByteArray()
1096    */
1097   public static HRegionInfo parseFrom(final byte [] bytes) throws DeserializationException {
1098     if (bytes == null) return null;
1099     return parseFrom(bytes, 0, bytes.length);
1100   }
1101 
1102   /**
1103    * @param bytes A pb RegionInfo serialized with a pb magic prefix.
1104    * @param offset starting point in the byte array
1105    * @param len length to read on the byte array
1106    * @return A deserialized {@link HRegionInfo}
1107    * @throws DeserializationException
1108    * @see #toByteArray()
1109    */
1110   public static HRegionInfo parseFrom(final byte [] bytes, int offset, int len)
1111       throws DeserializationException {
1112     if (ProtobufUtil.isPBMagicPrefix(bytes, offset, len)) {
1113       int pblen = ProtobufUtil.lengthOfPBMagic();
1114       try {
1115         HBaseProtos.RegionInfo.Builder builder = HBaseProtos.RegionInfo.newBuilder();
1116         ProtobufUtil.mergeFrom(builder, bytes, pblen + offset, len - pblen);
1117         HBaseProtos.RegionInfo ri = builder.build();
1118         return convert(ri);
1119       } catch (IOException e) {
1120         throw new DeserializationException(e);
1121       }
1122     } else {
1123       try {
1124         HRegionInfo hri = new HRegionInfo();
1125         hri.readFields(bytes, offset, len);
1126         return hri;
1127       } catch (IOException e) {
1128         throw new DeserializationException(e);
1129       }
1130     }
1131   }
1132 
1133   /**
1134    * Use this instead of {@link #toByteArray()} when writing to a stream and you want to use
1135    * the pb mergeDelimitedFrom (w/o the delimiter, pb reads to EOF which may not be what you want).
1136    * @return This instance serialized as a delimited protobuf w/ a magic pb prefix.
1137    * @throws IOException
1138    * @see #toByteArray()
1139    */
1140   public byte [] toDelimitedByteArray() throws IOException {
1141     return ProtobufUtil.toDelimitedByteArray(convert());
1142   }
1143 
1144   /**
1145    * Extract a HRegionInfo and ServerName from catalog table {@link Result}.
1146    * @param r Result to pull from
1147    * @return A pair of the {@link HRegionInfo} and the {@link ServerName}
1148    * (or null for server address if no address set in hbase:meta).
1149    * @throws IOException
1150    * @deprecated use MetaTableAccessor methods for interacting with meta layouts
1151    */
1152   @Deprecated
1153   public static Pair<HRegionInfo, ServerName> getHRegionInfoAndServerName(final Result r) {
1154     HRegionInfo info =
1155       getHRegionInfo(r, HConstants.REGIONINFO_QUALIFIER);
1156     ServerName sn = getServerName(r);
1157     return new Pair<HRegionInfo, ServerName>(info, sn);
1158   }
1159 
1160   /**
1161    * Returns HRegionInfo object from the column
1162    * HConstants.CATALOG_FAMILY:HConstants.REGIONINFO_QUALIFIER of the catalog
1163    * table Result.
1164    * @param data a Result object from the catalog table scan
1165    * @return HRegionInfo or null
1166    * @deprecated use MetaTableAccessor methods for interacting with meta layouts
1167    */
1168   @Deprecated
1169   public static HRegionInfo getHRegionInfo(Result data) {
1170     return getHRegionInfo(data, HConstants.REGIONINFO_QUALIFIER);
1171   }
1172 
1173   /**
1174    * Returns the daughter regions by reading the corresponding columns of the catalog table
1175    * Result.
1176    * @param data a Result object from the catalog table scan
1177    * @return a pair of HRegionInfo or PairOfSameType(null, null) if the region is not a split
1178    * parent
1179    * @deprecated use MetaTableAccessor methods for interacting with meta layouts
1180    */
1181   @Deprecated
1182   public static PairOfSameType<HRegionInfo> getDaughterRegions(Result data) throws IOException {
1183     HRegionInfo splitA = getHRegionInfo(data, HConstants.SPLITA_QUALIFIER);
1184     HRegionInfo splitB = getHRegionInfo(data, HConstants.SPLITB_QUALIFIER);
1185 
1186     return new PairOfSameType<HRegionInfo>(splitA, splitB);
1187   }
1188 
1189   /**
1190    * Returns the merge regions by reading the corresponding columns of the catalog table
1191    * Result.
1192    * @param data a Result object from the catalog table scan
1193    * @return a pair of HRegionInfo or PairOfSameType(null, null) if the region is not a split
1194    * parent
1195    * @deprecated use MetaTableAccessor methods for interacting with meta layouts
1196    */
1197   @Deprecated
1198   public static PairOfSameType<HRegionInfo> getMergeRegions(Result data) throws IOException {
1199     HRegionInfo mergeA = getHRegionInfo(data, HConstants.MERGEA_QUALIFIER);
1200     HRegionInfo mergeB = getHRegionInfo(data, HConstants.MERGEB_QUALIFIER);
1201 
1202     return new PairOfSameType<HRegionInfo>(mergeA, mergeB);
1203   }
1204 
1205   /**
1206    * Returns the HRegionInfo object from the column {@link HConstants#CATALOG_FAMILY} and
1207    * <code>qualifier</code> of the catalog table result.
1208    * @param r a Result object from the catalog table scan
1209    * @param qualifier Column family qualifier -- either
1210    * {@link HConstants#SPLITA_QUALIFIER}, {@link HConstants#SPLITB_QUALIFIER} or
1211    * {@link HConstants#REGIONINFO_QUALIFIER}.
1212    * @return An HRegionInfo instance or null.
1213    * @deprecated use MetaTableAccessor methods for interacting with meta layouts
1214    */
1215   @Deprecated
1216   public static HRegionInfo getHRegionInfo(final Result r, byte [] qualifier) {
1217     Cell cell = r.getColumnLatestCell(
1218         HConstants.CATALOG_FAMILY, qualifier);
1219     if (cell == null) return null;
1220     return parseFromOrNull(cell.getValueArray(), cell.getValueOffset(), cell.getValueLength());
1221   }
1222 
1223   /**
1224    * @deprecated use MetaTableAccessor methods for interacting with meta layouts
1225    */
1226   @Deprecated
1227   public static ServerName getServerName(final Result r) {
1228     Cell cell = r.getColumnLatestCell(HConstants.CATALOG_FAMILY, HConstants.SERVER_QUALIFIER);
1229     if (cell == null || cell.getValueLength() == 0) return null;
1230     String hostAndPort = Bytes.toString(
1231         cell.getValueArray(), cell.getValueOffset(), cell.getValueLength());
1232     cell = r.getColumnLatestCell(HConstants.CATALOG_FAMILY,
1233       HConstants.STARTCODE_QUALIFIER);
1234     if (cell == null || cell.getValueLength() == 0) return null;
1235     try {
1236       return ServerName.valueOf(hostAndPort,
1237           Bytes.toLong(cell.getValueArray(), cell.getValueOffset(), cell.getValueLength()));
1238     } catch (IllegalArgumentException e) {
1239       LOG.error("Ignoring invalid region for server " + hostAndPort + "; cell=" + cell, e);
1240       return null;
1241     }
1242   }
1243 
1244   /**
1245    * The latest seqnum that the server writing to meta observed when opening the region.
1246    * E.g. the seqNum when the result of {@link #getServerName(Result)} was written.
1247    * @param r Result to pull the seqNum from
1248    * @return SeqNum, or HConstants.NO_SEQNUM if there's no value written.
1249    * @deprecated use MetaTableAccessor methods for interacting with meta layouts
1250    */
1251   @Deprecated
1252   public static long getSeqNumDuringOpen(final Result r) {
1253     Cell cell = r.getColumnLatestCell(HConstants.CATALOG_FAMILY, HConstants.SEQNUM_QUALIFIER);
1254     if (cell == null || cell.getValueLength() == 0) return HConstants.NO_SEQNUM;
1255     return Bytes.toLong(cell.getValueArray(), cell.getValueOffset(), cell.getValueLength());
1256   }
1257 
1258   /**
1259    * Parses an HRegionInfo instance from the passed in stream.  Presumes the HRegionInfo was
1260    * serialized to the stream with {@link #toDelimitedByteArray()}
1261    * @param in
1262    * @return An instance of HRegionInfo.
1263    * @throws IOException
1264    */
1265   public static HRegionInfo parseFrom(final DataInputStream in) throws IOException {
1266     // I need to be able to move back in the stream if this is not a pb serialization so I can
1267     // do the Writable decoding instead.
1268     int pblen = ProtobufUtil.lengthOfPBMagic();
1269     byte [] pbuf = new byte[pblen];
1270     if (in.markSupported()) { //read it with mark()
1271       in.mark(pblen);
1272     }
1273 
1274     //assumption: if Writable serialization, it should be longer than pblen.
1275     int read = in.read(pbuf);
1276     if (read != pblen) throw new IOException("read=" + read + ", wanted=" + pblen);
1277     if (ProtobufUtil.isPBMagicPrefix(pbuf)) {
1278       return convert(HBaseProtos.RegionInfo.parseDelimitedFrom(in));
1279     } else {
1280         // Presume Writables.  Need to reset the stream since it didn't start w/ pb.
1281       if (in.markSupported()) {
1282         in.reset();
1283         HRegionInfo hri = new HRegionInfo();
1284         hri.readFields(in);
1285         return hri;
1286       } else {
1287         //we cannot use BufferedInputStream, it consumes more than we read from the underlying IS
1288         ByteArrayInputStream bais = new ByteArrayInputStream(pbuf);
1289         SequenceInputStream sis = new SequenceInputStream(bais, in); //concatenate input streams
1290         HRegionInfo hri = new HRegionInfo();
1291         hri.readFields(new DataInputStream(sis));
1292         return hri;
1293       }
1294     }
1295   }
1296 
1297   /**
1298    * Serializes given HRegionInfo's as a byte array. Use this instead of {@link #toByteArray()} when
1299    * writing to a stream and you want to use the pb mergeDelimitedFrom (w/o the delimiter, pb reads
1300    * to EOF which may not be what you want). {@link #parseDelimitedFrom(byte[], int, int)} can
1301    * be used to read back the instances.
1302    * @param infos HRegionInfo objects to serialize
1303    * @return This instance serialized as a delimited protobuf w/ a magic pb prefix.
1304    * @throws IOException
1305    * @see #toByteArray()
1306    */
1307   public static byte[] toDelimitedByteArray(HRegionInfo... infos) throws IOException {
1308     byte[][] bytes = new byte[infos.length][];
1309     int size = 0;
1310     for (int i = 0; i < infos.length; i++) {
1311       bytes[i] = infos[i].toDelimitedByteArray();
1312       size += bytes[i].length;
1313     }
1314 
1315     byte[] result = new byte[size];
1316     int offset = 0;
1317     for (byte[] b : bytes) {
1318       System.arraycopy(b, 0, result, offset, b.length);
1319       offset += b.length;
1320     }
1321     return result;
1322   }
1323 
1324   /**
1325    * Parses all the HRegionInfo instances from the passed in stream until EOF. Presumes the
1326    * HRegionInfo's were serialized to the stream with {@link #toDelimitedByteArray()}
1327    * @param bytes serialized bytes
1328    * @param offset the start offset into the byte[] buffer
1329    * @param length how far we should read into the byte[] buffer
1330    * @return All the hregioninfos that are in the byte array. Keeps reading till we hit the end.
1331    */
1332   public static List<HRegionInfo> parseDelimitedFrom(final byte[] bytes, final int offset,
1333       final int length) throws IOException {
1334     if (bytes == null) {
1335       throw new IllegalArgumentException("Can't build an object with empty bytes array");
1336     }
1337     DataInputBuffer in = new DataInputBuffer();
1338     List<HRegionInfo> hris = new ArrayList<HRegionInfo>();
1339     try {
1340       in.reset(bytes, offset, length);
1341       while (in.available() > 0) {
1342         HRegionInfo hri = parseFrom(in);
1343         hris.add(hri);
1344       }
1345     } finally {
1346       in.close();
1347     }
1348     return hris;
1349   }
1350 
1351   /**
1352    * Check whether two regions are adjacent
1353    * @param regionA
1354    * @param regionB
1355    * @return true if two regions are adjacent
1356    */
1357   public static boolean areAdjacent(HRegionInfo regionA, HRegionInfo regionB) {
1358     if (regionA == null || regionB == null) {
1359       throw new IllegalArgumentException(
1360           "Can't check whether adjacent for null region");
1361     }
1362     HRegionInfo a = regionA;
1363     HRegionInfo b = regionB;
1364     if (Bytes.compareTo(a.getStartKey(), b.getStartKey()) > 0) {
1365       a = regionB;
1366       b = regionA;
1367     }
1368     if (Bytes.compareTo(a.getEndKey(), b.getStartKey()) == 0) {
1369       return true;
1370     }
1371     return false;
1372   }
1373 }