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