View Javadoc

1   /**
2    * Copyright 2007 The Apache Software Foundation
3    *
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *     http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing, software
15   * distributed under the License is distributed on an "AS IS" BASIS,
16   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17   * See the License for the specific language governing permissions and
18   * limitations under the License.
19   */
20  package org.apache.hadoop.hbase;
21  
22  import java.io.DataInput;
23  import java.io.DataOutput;
24  import java.io.EOFException;
25  import java.io.IOException;
26  import java.util.Arrays;
27  
28  import org.apache.commons.logging.Log;
29  import org.apache.commons.logging.LogFactory;
30  import org.apache.hadoop.conf.Configuration;
31  import org.apache.hadoop.fs.FileSystem;
32  import org.apache.hadoop.fs.Path;
33  import org.apache.hadoop.hbase.KeyValue.KVComparator;
34  import org.apache.hadoop.hbase.migration.HRegionInfo090x;
35  import org.apache.hadoop.hbase.util.Bytes;
36  import org.apache.hadoop.hbase.util.FSTableDescriptors;
37  import org.apache.hadoop.hbase.util.JenkinsHash;
38  import org.apache.hadoop.hbase.util.MD5Hash;
39  import org.apache.hadoop.io.VersionedWritable;
40  import org.apache.hadoop.io.WritableComparable;
41  
42  /**
43   * HRegion information.
44   * Contains HRegion id, start and end keys, a reference to this
45   * HRegions' table descriptor, etc.
46   */
47  public class HRegionInfo extends VersionedWritable
48  implements WritableComparable<HRegionInfo> {
49    // VERSION == 0 when HRegionInfo had an HTableDescriptor inside it.
50    public static final byte VERSION_PRE_092 = 0;
51    public static final byte VERSION = 1;
52    private static final Log LOG = LogFactory.getLog(HRegionInfo.class);
53  
54    /**
55     * The new format for a region name contains its encodedName at the end.
56     * The encoded name also serves as the directory name for the region
57     * in the filesystem.
58     *
59     * New region name format:
60     *    &lt;tablename>,,&lt;startkey>,&lt;regionIdTimestamp>.&lt;encodedName>.
61     * where,
62     *    &lt;encodedName> is a hex version of the MD5 hash of
63     *    &lt;tablename>,&lt;startkey>,&lt;regionIdTimestamp>
64     * 
65     * The old region name format:
66     *    &lt;tablename>,&lt;startkey>,&lt;regionIdTimestamp>
67     * For region names in the old format, the encoded name is a 32-bit
68     * JenkinsHash integer value (in its decimal notation, string form). 
69     *<p>
70     * **NOTE**
71     *
72     * ROOT, the first META region, and regions created by an older
73     * version of HBase (0.20 or prior) will continue to use the
74     * old region name format.
75     */
76  
77    /** Separator used to demarcate the encodedName in a region name
78     * in the new format. See description on new format above. 
79     */ 
80    private static final int ENC_SEPARATOR = '.';
81    public  static final int MD5_HEX_LENGTH   = 32;
82  
83    /**
84     * Does region name contain its encoded name?
85     * @param regionName region name
86     * @return boolean indicating if this a new format region
87     *         name which contains its encoded name.
88     */
89    private static boolean hasEncodedName(final byte[] regionName) {
90      // check if region name ends in ENC_SEPARATOR
91      if ((regionName.length >= 1)
92          && (regionName[regionName.length - 1] == ENC_SEPARATOR)) {
93        // region name is new format. it contains the encoded name.
94        return true; 
95      }
96      return false;
97    }
98    
99    /**
100    * @param regionName
101    * @return the encodedName
102    */
103   public static String encodeRegionName(final byte [] regionName) {
104     String encodedName;
105     if (hasEncodedName(regionName)) {
106       // region is in new format:
107       // <tableName>,<startKey>,<regionIdTimeStamp>/encodedName/
108       encodedName = Bytes.toString(regionName,
109           regionName.length - MD5_HEX_LENGTH - 1,
110           MD5_HEX_LENGTH);
111     } else {
112       // old format region name. ROOT and first META region also 
113       // use this format.EncodedName is the JenkinsHash value.
114       int hashVal = Math.abs(JenkinsHash.getInstance().hash(regionName,
115         regionName.length, 0));
116       encodedName = String.valueOf(hashVal);
117     }
118     return encodedName;
119   }
120 
121   /**
122    * Use logging.
123    * @param encodedRegionName The encoded regionname.
124    * @return <code>-ROOT-</code> if passed <code>70236052</code> or
125    * <code>.META.</code> if passed </code>1028785192</code> else returns
126    * <code>encodedRegionName</code>
127    */
128   public static String prettyPrint(final String encodedRegionName) {
129     if (encodedRegionName.equals("70236052")) {
130       return encodedRegionName + "/-ROOT-";
131     } else if (encodedRegionName.equals("1028785192")) {
132       return encodedRegionName + "/.META.";
133     }
134     return encodedRegionName;
135   }
136 
137   /** delimiter used between portions of a region name */
138   public static final int DELIMITER = ',';
139 
140   /** HRegionInfo for root region */
141   public static final HRegionInfo ROOT_REGIONINFO =
142     new HRegionInfo(0L, Bytes.toBytes("-ROOT-"));
143 
144   /** HRegionInfo for first meta region */
145   public static final HRegionInfo FIRST_META_REGIONINFO =
146     new HRegionInfo(1L, Bytes.toBytes(".META."));
147 
148   private byte [] endKey = HConstants.EMPTY_BYTE_ARRAY;
149   // This flag is in the parent of a split while the parent is still referenced
150   // by daughter regions.  We USED to set this flag when we disabled a table
151   // but now table state is kept up in zookeeper as of 0.90.0 HBase.
152   private boolean offLine = false;
153   private long regionId = -1;
154   private transient byte [] regionName = HConstants.EMPTY_BYTE_ARRAY;
155   private String regionNameStr = "";
156   private boolean split = false;
157   private byte [] startKey = HConstants.EMPTY_BYTE_ARRAY;
158   private int hashCode = -1;
159   //TODO: Move NO_HASH to HStoreFile which is really the only place it is used.
160   public static final String NO_HASH = null;
161   private volatile String encodedName = NO_HASH;
162   private byte [] encodedNameAsBytes = null;
163 
164   // Current TableName
165   private byte[] tableName = null;
166 
167   private void setHashCode() {
168     int result = Arrays.hashCode(this.regionName);
169     result ^= this.regionId;
170     result ^= Arrays.hashCode(this.startKey);
171     result ^= Arrays.hashCode(this.endKey);
172     result ^= Boolean.valueOf(this.offLine).hashCode();
173     result ^= Arrays.hashCode(this.tableName);
174     this.hashCode = result;
175   }
176 
177 
178   /**
179    * Private constructor used constructing HRegionInfo for the catalog root and
180    * first meta regions
181    */
182   private HRegionInfo(long regionId, byte[] tableName) {
183     super();
184     this.regionId = regionId;
185     this.tableName = tableName.clone();
186     // Note: Root & First Meta regions names are still in old format
187     this.regionName = createRegionName(tableName, null,
188                                        regionId, false);
189     this.regionNameStr = Bytes.toStringBinary(this.regionName);
190     setHashCode();
191   }
192 
193   /** Default constructor - creates empty object */
194   public HRegionInfo() {
195     super();
196   }
197 
198   /**
199    * Used only for migration
200    * @param other HRegionInfoForMigration
201    */
202   public HRegionInfo(HRegionInfo090x other) {
203     super();
204     this.endKey = other.getEndKey();
205     this.offLine = other.isOffline();
206     this.regionId = other.getRegionId();
207     this.regionName = other.getRegionName();
208     this.regionNameStr = Bytes.toStringBinary(this.regionName);
209     this.split = other.isSplit();
210     this.startKey = other.getStartKey();
211     this.hashCode = other.hashCode();
212     this.encodedName = other.getEncodedName();
213     this.tableName = other.getTableDesc().getName();
214   }
215 
216   public HRegionInfo(final byte[] tableName) {
217     this(tableName, null, null);
218   }
219 
220   /**
221    * Construct HRegionInfo with explicit parameters
222    *
223    * @param tableName the table name
224    * @param startKey first key in region
225    * @param endKey end of key range
226    * @throws IllegalArgumentException
227    */
228   public HRegionInfo(final byte[] tableName, final byte[] startKey,
229                      final byte[] endKey)
230   throws IllegalArgumentException {
231     this(tableName, startKey, endKey, false);
232   }
233 
234 
235   /**
236    * Construct HRegionInfo with explicit parameters
237    *
238    * @param tableName the table descriptor
239    * @param startKey first key in region
240    * @param endKey end of key range
241    * @param split true if this region has split and we have daughter regions
242    * regions that may or may not hold references to this region.
243    * @throws IllegalArgumentException
244    */
245   public HRegionInfo(final byte[] tableName, final byte[] startKey,
246                      final byte[] endKey, final boolean split)
247   throws IllegalArgumentException {
248     this(tableName, startKey, endKey, split, System.currentTimeMillis());
249   }
250 
251 
252   /**
253    * Construct HRegionInfo with explicit parameters
254    *
255    * @param tableName the table descriptor
256    * @param startKey first key in region
257    * @param endKey end of key range
258    * @param split true if this region has split and we have daughter regions
259    * regions that may or may not hold references to this region.
260    * @param regionid Region id to use.
261    * @throws IllegalArgumentException
262    */
263   public HRegionInfo(final byte[] tableName, final byte[] startKey,
264                      final byte[] endKey, final boolean split, final long regionid)
265   throws IllegalArgumentException {
266 
267     super();
268     if (tableName == null) {
269       throw new IllegalArgumentException("tableName cannot be null");
270     }
271     this.tableName = tableName.clone();
272     this.offLine = false;
273     this.regionId = regionid;
274 
275     this.regionName = createRegionName(this.tableName, startKey, regionId, true);
276 
277     this.regionNameStr = Bytes.toStringBinary(this.regionName);
278     this.split = split;
279     this.endKey = endKey == null? HConstants.EMPTY_END_ROW: endKey.clone();
280     this.startKey = startKey == null?
281       HConstants.EMPTY_START_ROW: startKey.clone();
282     this.tableName = tableName.clone();
283     setHashCode();
284   }
285 
286   /**
287    * Costruct a copy of another HRegionInfo
288    *
289    * @param other
290    */
291   public HRegionInfo(HRegionInfo other) {
292     super();
293     this.endKey = other.getEndKey();
294     this.offLine = other.isOffline();
295     this.regionId = other.getRegionId();
296     this.regionName = other.getRegionName();
297     this.regionNameStr = Bytes.toStringBinary(this.regionName);
298     this.split = other.isSplit();
299     this.startKey = other.getStartKey();
300     this.hashCode = other.hashCode();
301     this.encodedName = other.getEncodedName();
302     this.tableName = other.tableName;
303   }
304 
305 
306   /**
307    * Make a region name of passed parameters.
308    * @param tableName
309    * @param startKey Can be null
310    * @param regionid Region id (Usually timestamp from when region was created).
311    * @param newFormat should we create the region name in the new format
312    *                  (such that it contains its encoded name?).
313    * @return Region name made of passed tableName, startKey and id
314    */
315   public static byte [] createRegionName(final byte [] tableName,
316       final byte [] startKey, final long regionid, boolean newFormat) {
317     return createRegionName(tableName, startKey, Long.toString(regionid), newFormat);
318   }
319 
320   /**
321    * Make a region name of passed parameters.
322    * @param tableName
323    * @param startKey Can be null
324    * @param id Region id (Usually timestamp from when region was created).
325    * @param newFormat should we create the region name in the new format
326    *                  (such that it contains its encoded name?).
327    * @return Region name made of passed tableName, startKey and id
328    */
329   public static byte [] createRegionName(final byte [] tableName,
330       final byte [] startKey, final String id, boolean newFormat) {
331     return createRegionName(tableName, startKey, Bytes.toBytes(id), newFormat);
332   }
333 
334   /**
335    * Make a region name of passed parameters.
336    * @param tableName
337    * @param startKey Can be null
338    * @param id Region id (Usually timestamp from when region was created).
339    * @param newFormat should we create the region name in the new format
340    *                  (such that it contains its encoded name?).
341    * @return Region name made of passed tableName, startKey and id
342    */
343   public static byte [] createRegionName(final byte [] tableName,
344       final byte [] startKey, final byte [] id, boolean newFormat) {
345     byte [] b = new byte [tableName.length + 2 + id.length +
346        (startKey == null? 0: startKey.length) +
347        (newFormat ? (MD5_HEX_LENGTH + 2) : 0)];
348 
349     int offset = tableName.length;
350     System.arraycopy(tableName, 0, b, 0, offset);
351     b[offset++] = DELIMITER;
352     if (startKey != null && startKey.length > 0) {
353       System.arraycopy(startKey, 0, b, offset, startKey.length);
354       offset += startKey.length;
355     }
356     b[offset++] = DELIMITER;
357     System.arraycopy(id, 0, b, offset, id.length);
358     offset += id.length;
359 
360     if (newFormat) {
361       //
362       // Encoded name should be built into the region name.
363       //
364       // Use the region name thus far (namely, <tablename>,<startKey>,<id>)
365       // to compute a MD5 hash to be used as the encoded name, and append
366       // it to the byte buffer.
367       //
368       String md5Hash = MD5Hash.getMD5AsHex(b, 0, offset);
369       byte [] md5HashBytes = Bytes.toBytes(md5Hash);
370 
371       if (md5HashBytes.length != MD5_HEX_LENGTH) {
372         LOG.error("MD5-hash length mismatch: Expected=" + MD5_HEX_LENGTH +
373                   "; Got=" + md5HashBytes.length); 
374       }
375 
376       // now append the bytes '.<encodedName>.' to the end
377       b[offset++] = ENC_SEPARATOR;
378       System.arraycopy(md5HashBytes, 0, b, offset, MD5_HEX_LENGTH);
379       offset += MD5_HEX_LENGTH;
380       b[offset++] = ENC_SEPARATOR;
381     }
382     
383     return b;
384   }
385 
386   /**
387    * Gets the table name from the specified region name.
388    * @param regionName
389    * @return Table name.
390    */
391   public static byte [] getTableName(byte [] regionName) {
392     int offset = -1;
393     for (int i = 0; i < regionName.length; i++) {
394       if (regionName[i] == DELIMITER) {
395         offset = i;
396         break;
397       }
398     }
399     byte [] tableName = new byte[offset];
400     System.arraycopy(regionName, 0, tableName, 0, offset);
401     return tableName;
402   }
403 
404   /**
405    * Separate elements of a regionName.
406    * @param regionName
407    * @return Array of byte[] containing tableName, startKey and id
408    * @throws IOException
409    */
410   public static byte [][] parseRegionName(final byte [] regionName)
411   throws IOException {
412     int offset = -1;
413     for (int i = 0; i < regionName.length; i++) {
414       if (regionName[i] == DELIMITER) {
415         offset = i;
416         break;
417       }
418     }
419     if(offset == -1) throw new IOException("Invalid regionName format");
420     byte [] tableName = new byte[offset];
421     System.arraycopy(regionName, 0, tableName, 0, offset);
422     offset = -1;
423     for (int i = regionName.length - 1; i > 0; i--) {
424       if(regionName[i] == DELIMITER) {
425         offset = i;
426         break;
427       }
428     }
429     if(offset == -1) throw new IOException("Invalid regionName format");
430     byte [] startKey = HConstants.EMPTY_BYTE_ARRAY;
431     if(offset != tableName.length + 1) {
432       startKey = new byte[offset - tableName.length - 1];
433       System.arraycopy(regionName, tableName.length + 1, startKey, 0,
434           offset - tableName.length - 1);
435     }
436     byte [] id = new byte[regionName.length - offset - 1];
437     System.arraycopy(regionName, offset + 1, id, 0,
438         regionName.length - offset - 1);
439     byte [][] elements = new byte[3][];
440     elements[0] = tableName;
441     elements[1] = startKey;
442     elements[2] = id;
443     return elements;
444   }
445 
446   /** @return the regionId */
447   public long getRegionId(){
448     return regionId;
449   }
450 
451   /**
452    * @return the regionName as an array of bytes.
453    * @see #getRegionNameAsString()
454    */
455   public byte [] getRegionName(){
456     return regionName;
457   }
458 
459   /**
460    * @return Region name as a String for use in logging, etc.
461    */
462   public String getRegionNameAsString() {
463     if (hasEncodedName(this.regionName)) {
464       // new format region names already have their encoded name.
465       return this.regionNameStr;
466     }
467 
468     // old format. regionNameStr doesn't have the region name.
469     //
470     //
471     return this.regionNameStr + "." + this.getEncodedName();
472   }
473 
474   /** @return the encoded region name */
475   public synchronized String getEncodedName() {
476     if (this.encodedName == NO_HASH) {
477       this.encodedName = encodeRegionName(this.regionName);
478     }
479     return this.encodedName;
480   }
481 
482   public synchronized byte [] getEncodedNameAsBytes() {
483     if (this.encodedNameAsBytes == null) {
484       this.encodedNameAsBytes = Bytes.toBytes(getEncodedName());
485     }
486     return this.encodedNameAsBytes;
487   }
488 
489   /** @return the startKey */
490   public byte [] getStartKey(){
491     return startKey;
492   }
493   
494   /** @return the endKey */
495   public byte [] getEndKey(){
496     return endKey;
497   }
498 
499   /**
500    * Get current table name of the region
501    * @return byte array of table name
502    */
503   public byte[] getTableName() {
504     if (tableName == null || tableName.length == 0) {
505       tableName = getTableName(getRegionName());
506     }
507     return tableName;
508   }
509 
510   /**
511    * Get current table name as string
512    * @return string representation of current table
513    */
514   public String getTableNameAsString() {
515     return Bytes.toString(tableName);
516   }
517 
518   /**
519    * Returns true if the given inclusive range of rows is fully contained
520    * by this region. For example, if the region is foo,a,g and this is
521    * passed ["b","c"] or ["a","c"] it will return true, but if this is passed
522    * ["b","z"] it will return false.
523    * @throws IllegalArgumentException if the range passed is invalid (ie end < start)
524    */
525   public boolean containsRange(byte[] rangeStartKey, byte[] rangeEndKey) {
526     if (Bytes.compareTo(rangeStartKey, rangeEndKey) > 0) {
527       throw new IllegalArgumentException(
528       "Invalid range: " + Bytes.toStringBinary(rangeStartKey) +
529       " > " + Bytes.toStringBinary(rangeEndKey));
530     }
531 
532     boolean firstKeyInRange = Bytes.compareTo(rangeStartKey, startKey) >= 0;
533     boolean lastKeyInRange =
534       Bytes.compareTo(rangeEndKey, endKey) < 0 ||
535       Bytes.equals(endKey, HConstants.EMPTY_BYTE_ARRAY);
536     return firstKeyInRange && lastKeyInRange;
537   }
538   
539   /**
540    * Return true if the given row falls in this region.
541    */
542   public boolean containsRow(byte[] row) {
543     return Bytes.compareTo(row, startKey) >= 0 &&
544       (Bytes.compareTo(row, endKey) < 0 ||
545        Bytes.equals(endKey, HConstants.EMPTY_BYTE_ARRAY));
546   }
547 
548   /**
549    * @return the tableDesc
550    * @deprecated Do not use; expensive call
551    *         use HRegionInfo.getTableNameAsString() in place of
552    *         HRegionInfo.getTableDesc().getNameAsString()
553    */
554    @Deprecated
555   public HTableDescriptor getTableDesc() {
556     Configuration c = HBaseConfiguration.create();
557     FileSystem fs;
558     try {
559       fs = FileSystem.get(c);
560     } catch (IOException e) {
561       throw new RuntimeException(e);
562     }
563     FSTableDescriptors fstd =
564       new FSTableDescriptors(fs, new Path(c.get(HConstants.HBASE_DIR)));
565     try {
566       return fstd.get(this.tableName);
567     } catch (IOException e) {
568       throw new RuntimeException(e);
569     }
570   }
571 
572   /**
573    * @param newDesc new table descriptor to use
574    * @deprecated Do not use; expensive call
575    */
576   @Deprecated
577   public void setTableDesc(HTableDescriptor newDesc) {
578     Configuration c = HBaseConfiguration.create();
579     FileSystem fs;
580     try {
581       fs = FileSystem.get(c);
582     } catch (IOException e) {
583       throw new RuntimeException(e);
584     }
585     FSTableDescriptors fstd =
586       new FSTableDescriptors(fs, new Path(c.get(HConstants.HBASE_DIR)));
587     try {
588       fstd.add(newDesc);
589     } catch (IOException e) {
590       throw new RuntimeException(e);
591     }
592   }
593 
594   /** @return true if this is the root region */
595   public boolean isRootRegion() {
596     return Bytes.equals(tableName, HRegionInfo.ROOT_REGIONINFO.getTableName());
597   }
598 
599   /** @return true if this region is from a table that is a meta table,
600    * either <code>.META.</code> or <code>-ROOT-</code>
601    */
602   public boolean isMetaTable() {
603     return isRootRegion() || isMetaRegion();
604   }
605 
606   /** @return true if this region is a meta region */
607   public boolean isMetaRegion() {
608      return Bytes.equals(tableName, HRegionInfo.FIRST_META_REGIONINFO.getTableName());
609   }
610 
611   /**
612    * @return True if has been split and has daughters.
613    */
614   public boolean isSplit() {
615     return this.split;
616   }
617 
618   /**
619    * @param split set split status
620    */
621   public void setSplit(boolean split) {
622     this.split = split;
623   }
624 
625   /**
626    * @return True if this region is offline.
627    */
628   public boolean isOffline() {
629     return this.offLine;
630   }
631 
632   /**
633    * The parent of a region split is offline while split daughters hold
634    * references to the parent. Offlined regions are closed.
635    * @param offLine Set online/offline status.
636    */
637   public void setOffline(boolean offLine) {
638     this.offLine = offLine;
639   }
640 
641 
642   /**
643    * @return True if this is a split parent region.
644    */
645   public boolean isSplitParent() {
646     if (!isSplit()) return false;
647     if (!isOffline()) {
648       LOG.warn("Region is split but NOT offline: " + getRegionNameAsString());
649     }
650     return true;
651   }
652 
653   /**
654    * @see java.lang.Object#toString()
655    */
656   @Override
657   public String toString() {
658     return "{" + HConstants.NAME + " => '" +
659       this.regionNameStr
660       + "', STARTKEY => '" +
661       Bytes.toStringBinary(this.startKey) + "', ENDKEY => '" +
662       Bytes.toStringBinary(this.endKey) +
663       "', ENCODED => " + getEncodedName() + "," +
664       (isOffline()? " OFFLINE => true,": "") +
665       (isSplit()? " SPLIT => true,": "") + "}";
666   }
667 
668   /**
669    * @see java.lang.Object#equals(java.lang.Object)
670    */
671   @Override
672   public boolean equals(Object o) {
673     if (this == o) {
674       return true;
675     }
676     if (o == null) {
677       return false;
678     }
679     if (!(o instanceof HRegionInfo)) {
680       return false;
681     }
682     return this.compareTo((HRegionInfo)o) == 0;
683   }
684 
685   /**
686    * @see java.lang.Object#hashCode()
687    */
688   @Override
689   public int hashCode() {
690     return this.hashCode;
691   }
692 
693   /** @return the object version number */
694   @Override
695   public byte getVersion() {
696     return VERSION;
697   }
698 
699   //
700   // Writable
701   //
702 
703   @Override
704   public void write(DataOutput out) throws IOException {
705     super.write(out);
706     Bytes.writeByteArray(out, endKey);
707     out.writeBoolean(offLine);
708     out.writeLong(regionId);
709     Bytes.writeByteArray(out, regionName);
710     out.writeBoolean(split);
711     Bytes.writeByteArray(out, startKey);
712     Bytes.writeByteArray(out, tableName);
713     out.writeInt(hashCode);
714   }
715 
716   @Override
717   public void readFields(DataInput in) throws IOException {
718     // Read the single version byte.  We don't ask the super class do it
719     // because freaks out if its not the current classes' version.  This method
720     // can deserialize version 0 and version 1 of HRI.
721     byte version = in.readByte();
722     if (version == 0) {
723       // This is the old HRI that carried an HTD.  Migrate it.  The below
724       // was copied from the old 0.90 HRI readFields.
725       this.endKey = Bytes.readByteArray(in);
726       this.offLine = in.readBoolean();
727       this.regionId = in.readLong();
728       this.regionName = Bytes.readByteArray(in);
729       this.regionNameStr = Bytes.toStringBinary(this.regionName);
730       this.split = in.readBoolean();
731       this.startKey = Bytes.readByteArray(in);
732       try {
733         HTableDescriptor htd = new HTableDescriptor();
734         htd.readFields(in);
735         this.tableName = htd.getName();
736       } catch(EOFException eofe) {
737          throw new IOException("HTD not found in input buffer", eofe);
738       }
739       this.hashCode = in.readInt();
740     } else if (getVersion() == VERSION) {
741       this.endKey = Bytes.readByteArray(in);
742       this.offLine = in.readBoolean();
743       this.regionId = in.readLong();
744       this.regionName = Bytes.readByteArray(in);
745       this.regionNameStr = Bytes.toStringBinary(this.regionName);
746       this.split = in.readBoolean();
747       this.startKey = Bytes.readByteArray(in);
748       this.tableName = Bytes.readByteArray(in);
749       this.hashCode = in.readInt();
750     } else {
751       throw new IOException("Non-migratable/unknown version=" + getVersion());
752     }
753   }
754 
755   //
756   // Comparable
757   //
758 
759   public int compareTo(HRegionInfo o) {
760     if (o == null) {
761       return 1;
762     }
763 
764     // Are regions of same table?
765     int result = Bytes.compareTo(this.tableName, o.tableName);
766     if (result != 0) {
767       return result;
768     }
769 
770     // Compare start keys.
771     result = Bytes.compareTo(this.startKey, o.startKey);
772     if (result != 0) {
773       return result;
774     }
775 
776     // Compare end keys.
777     result = Bytes.compareTo(this.endKey, o.endKey);
778     if (result != 0) {
779       return result;
780     }
781     if (this.offLine == o.offLine)
782         return 0;
783     if (this.offLine == true) return -1;
784         
785     return 1;
786   }
787 
788   /**
789    * @return Comparator to use comparing {@link KeyValue}s.
790    */
791   public KVComparator getComparator() {
792     return isRootRegion()? KeyValue.ROOT_COMPARATOR: isMetaRegion()?
793       KeyValue.META_COMPARATOR: KeyValue.COMPARATOR;
794   }
795 }