/**
    * Reads the area code entries from the provided input stream and stores them to the internal byte
    * buffers.
    */
 private void readEntries(ObjectInput objectInput)
 {
     numOfEntries = objectInput.readInt();
     if (phoneNumberPrefixes == null || phoneNumberPrefixes.capacity() < numOfEntries) {
       phoneNumberPrefixes = ByteBuffer.allocate(numOfEntries * prefixSizeInBytes);
     }
     if (descriptionIndexes == null || descriptionIndexes.capacity() < numOfEntries) {
       descriptionIndexes = ByteBuffer.allocate(numOfEntries * descIndexSizeInBytes);
     }
     for (int i = 0; i < numOfEntries; i++) {
       readExternalWord(objectInput, prefixSizeInBytes, phoneNumberPrefixes, i);
       readExternalWord(objectInput, descIndexSizeInBytes, descriptionIndexes, i);
     }
 }
        public override void readFromSortedMap(SortedMap<Integer, String> areaCodeMap)
        {
            SortedSet<String> descriptionsSet = new TreeSet<String>();
            numOfEntries = areaCodeMap.size();
            prefixSizeInBytes = getOptimalNumberOfBytesForValue(areaCodeMap.lastKey());
            phoneNumberPrefixes = ByteBuffer.allocate(numOfEntries * prefixSizeInBytes);

            // Fill the phone number prefixes byte buffer, the set of possible lengths of prefixes and the
            // description set.
            int index = 0;
            foreach (Entry<Integer, String> entry in areaCodeMap.entrySet()) {
              int prefix = entry.getKey();
              storeWordInBuffer(phoneNumberPrefixes, prefixSizeInBytes, index, prefix);
              possibleLengths.add((int) Math.log10(prefix) + 1);
              descriptionsSet.add(entry.getValue());
              ++index;
            }
            createDescriptionPool(descriptionsSet, areaCodeMap);
        }
        /**
           * Creates the description pool from the provided set of string descriptions and area code map.
           */
        private void createDescriptionPool(SortedSet<String> descriptionsSet,
      SortedMap<Integer, String> areaCodeMap)
        {
            descIndexSizeInBytes = getOptimalNumberOfBytesForValue(descriptionsSet.size() - 1);
            descriptionIndexes = ByteBuffer.allocate(numOfEntries * descIndexSizeInBytes);
            descriptionPool = new String[descriptionsSet.size()];
            descriptionsSet.toArray(descriptionPool);

            // Map the phone number prefixes to the descriptions.
            int index = 0;
            for (int i = 0; i < numOfEntries; i++) {
              int prefix = readWordFromBuffer(phoneNumberPrefixes, prefixSizeInBytes, i);
              String description = areaCodeMap.get(prefix);
              int positionInDescriptionPool = Arrays.binarySearch(descriptionPool, description);
              storeWordInBuffer(descriptionIndexes, descIndexSizeInBytes, index, positionInDescriptionPool);
              ++index;
            }
        }
   /**
      * Writes the value read from the provided byte {@code buffer} at the specified {@code index} to
      * the provided {@code objectOutput}.
      *
      * @param objectOutput  the object output stream to which the value is written
      * @param wordSize  the number of bytes used to store the value
      * @param inputBuffer  the byte buffer from which the value is read
      * @param index  the index of the value in the the byte buffer
      * @throws IOException if an error occurred writing to the provided object output stream
      */
   private static void writeExternalWord(ObjectOutput objectOutput, int wordSize,
 ByteBuffer inputBuffer, int index)
   {
       int wordIndex = index * wordSize;
       if (wordSize == SHORT_NUM_BYTES) {
         objectOutput.writeShort(inputBuffer.getShort(wordIndex));
       } else {
         objectOutput.writeInt(inputBuffer.getInt(wordIndex));
       }
   }
 /**
    * Stores the provided {@code value} to the provided byte {@code buffer} at the specified {@code
    * index} using the provided {@code wordSize} in bytes. Note that only integer and short sizes are
    * supported.
    *
    * @param buffer  the byte buffer to which the value is stored
    * @param wordSize  the number of bytes used to store the provided value
    * @param index  the index to which the value is stored
    * @param value  the value that is stored assuming it does not require more than the specified
    *    number of bytes.
    */
 private static void storeWordInBuffer(ByteBuffer buffer, int wordSize, int index, int value)
 {
     int wordIndex = index * wordSize;
     if (wordSize == SHORT_NUM_BYTES) {
       buffer.putShort(wordIndex, (short) value);
     } else {
       buffer.putInt(wordIndex, value);
     }
 }
 /**
    * Reads the {@code value} at the specified {@code index} from the provided byte {@code buffer}.
    * Note that only integer and short sizes are supported.
    *
    * @param buffer  the byte buffer from which the value is read
    * @param wordSize  the number of bytes used to store the value
    * @param index  the index where the value is read from
    *
    * @return  the value read from the buffer
    */
 private static int readWordFromBuffer(ByteBuffer buffer, int wordSize, int index)
 {
     int wordIndex = index * wordSize;
     return wordSize == SHORT_NUM_BYTES ? buffer.getShort(wordIndex) : buffer.getInt(wordIndex);
 }
   /**
      * Stores a value which is read from the provided {@code objectInput} to the provided byte {@code
      * buffer} at the specified {@code index}.
      *
      * @param objectInput  the object input stream from which the value is read
      * @param wordSize  the number of bytes used to store the value read from the stream
      * @param outputBuffer  the byte buffer to which the value is stored
      * @param index  the index where the value is stored in the buffer
      * @throws IOException  if an error occurred reading from the object input stream
      */
   private static void readExternalWord(ObjectInput objectInput, int wordSize,
 ByteBuffer outputBuffer, int index)
   {
       int wordIndex = index * wordSize;
       if (wordSize == SHORT_NUM_BYTES) {
         outputBuffer.putShort(wordIndex, objectInput.readShort());
       } else {
         outputBuffer.putInt(wordIndex, objectInput.readInt());
       }
   }