All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.named_data.jndn.util.Blob Maven / Gradle / Ivy

Go to download

jNDN is a new implementation of a Named Data Networking client library written in Java. It is wire format compatible with the new NDN-TLV encoding, with NDNx and PARC's CCNx.

There is a newer version: 0.25
Show newest version
/**
 * Copyright (C) 2013-2017 Regents of the University of California.
 * @author: Jeff Thompson 
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program.  If not, see .
 * A copy of the GNU Lesser General Public License is in the file COPYING.
 */

package net.named_data.jndn.util;

import java.io.UnsupportedEncodingException;
import java.nio.ByteBuffer;

/**
 * A Blob holds a pointer to an immutable ByteBuffer.  We use an immutable
 * buffer so that it is OK to pass the object into methods because the new or
 * old owner can’t change the bytes.
 * Note that  the pointer to the ByteBuffer can be null.
 */
public class Blob implements Comparable {
  /**
   * Create a new Blob with a null pointer.
   */
  public
  Blob()
  {
    buffer_ = null;
  }

  /**
   * Create a new Blob and take another pointer to the given blob's buffer.
   * @param blob The Blob from which we take another pointer to the same buffer.
   */
  public
  Blob(Blob blob)
  {
    if (blob != null)
      buffer_ = blob.buffer_;
    else
      buffer_ = null;
  }

  /**
   * Create a new Blob from an existing ByteBuffer.  IMPORTANT: If copy is
   * false, after calling this constructor, if you keep a pointer to the buffer
   * then you must treat it as immutable and promise not to change it.
   * @param buffer The existing ByteBuffer.  It is important that the buffer
   * position and limit are correct.
   * @param copy If true, copy the contents into a new byte array.  If false,
   * just take a slice which uses the existing byte array in buffer.
   */
  public
  Blob(ByteBuffer buffer, boolean copy)
  {
    if (buffer != null) {
      if (copy) {
        buffer_ = ByteBuffer.allocate(buffer.remaining());

        // Put updates buffer.position(), so save and restore it.
        int savePosition = buffer.position();
        buffer_.put(buffer);
        buffer.position(savePosition);

        buffer_.flip();
      }
      else
        buffer_ = buffer.slice();
    }
    else
      buffer_ = null;
  }

  /**
   * Create a new Blob from the the byte array. IMPORTANT: If copy is false,
   * after calling this constructor, if you keep a pointer to the buffer then
   * you must treat it as immutable and promise not to change it.
   * @param value The byte array. If copy is true, this makes a copy.
   * @param copy If true, copy the contents into a new byte array.  If false,
   * just use ByteBuffer.wrap which uses the existing byte array.
   */
  public
  Blob(byte[] value, boolean copy)
  {
    if (copy) {
      buffer_ = ByteBuffer.allocate(value.length);
      buffer_.put(value);
      buffer_.flip();
    }
    else
      buffer_ = ByteBuffer.wrap(value);
  }

  /**
   * Create a new Blob with a copy of the bytes in the array.
   * @param value The byte array to copy.
   */
  public
  Blob(byte[] value)
  {
    buffer_ = ByteBuffer.allocate(value.length);
    buffer_.put(value);
    buffer_.flip();
  }
  
  /**
   * Create a new Blob with a copy of the bytes in the array.
   * @param value The array of integer to copy where each integer is in
   * the range 0 to 255.
   */
  public
  Blob(int[] value)
  {
    buffer_ = ByteBuffer.allocate(value.length);
    for (int i = 0; i < value.length; ++i)
      buffer_.put((byte)value[i]);
    buffer_.flip();
  }

  /**
   * Create a new Blob from the UTF-8 encoding of the Unicode string.
   * @param value The Unicode string which is encoded as UTF-8.
   */
  public
  Blob(String value)
  {
    byte[] utf8;
    try {
      utf8 = value.getBytes("UTF-8");
    } catch (UnsupportedEncodingException ex) {
      // We don't expect this to happen.
      throw new Error("UTF-8 encoder not supported: " + ex.getMessage());
    }
    buffer_ = ByteBuffer.allocate(utf8.length);
    buffer_.put(utf8);
    buffer_.flip();
  }

  /**
   * Get the read-only ByteBuffer.
   * @return The read-only ByteBuffer using asReadOnlyBuffer(), or null if the
   * pointer is null.
   */
  public final ByteBuffer
  buf()
  {
    if (buffer_ != null)
      // We call asReadOnlyBuffer each time because it is still allowed to
      //   change the position and limit on a read-only buffer, and we don't
      //   want the caller to modify our buffer_.
      return buffer_.asReadOnlyBuffer();
    else
      return null;
  }

  /**
   * Get a byte array by calling ByteBuffer.array() if possible (because the
   * ByteBuffer slice covers the entire backing array). Otherwise return byte
   * array as a copy of the ByteBuffer. This is called immutableArray to remind
   * you not to change the contents of the returned array. This method is
   * necessary because the read-only ByteBuffer returned by buf() doesn't allow
   * you to call array().
   * @return A byte array which you should not modify, or null if the pointer
   * is null.
   */
  public final byte[]
  getImmutableArray()
  {
    if (buffer_ != null) {
      // We can't call array() on a read only ByteBuffer.
      if (!buffer_.isReadOnly()) {
        byte[] array =  buffer_.array();
        if (array.length == buffer_.remaining())
          // Assume the buffer_ covers the entire backing array, so just return.
          return array;
      }

      // We have to copy to a new byte array.
      ByteBuffer tempBuffer = ByteBuffer.allocate(buffer_.remaining());
      int savePosition = buffer_.position();
      tempBuffer.put(buffer_);
      buffer_.position(savePosition);
      tempBuffer.flip();
      return tempBuffer.array();
    }
    else
      return null;
  }

  /**
   * Get the size of the buffer.
   * @return The length (remaining) of the ByteBuffer, or 0 if the pointer is
   * null.
   */
  public final int
  size()
  {
    if (buffer_ != null)
      return buffer_.remaining();
    else
      return 0;
  }

  /**
   * Check if the buffer pointer is null.
   * @return True if the buffer pointer is null, otherwise false.
   */
  public final boolean
  isNull()
  {
    return buffer_ == null;
  }

  /**
   * Return a hex string of buf() from position to limit.
   * @return A string of hex bytes, or "" if the buffer is null.
   */
  public final String
  toHex()
  {
    if (buffer_ == null)
      return "";
    else
      return toHex(buffer_);
  }

  /**
   * Write a hex string of the contents of buffer from position to limit to the
   * output.
   * @param buffer The buffer.
   * @param output The StringBuffer to write to.
   */
  public static void
  toHex(ByteBuffer buffer, StringBuffer output)
  {
    for (int i = buffer.position(); i < buffer.limit(); ++i) {
      String hex = Integer.toHexString((int)buffer.get(i) & 0xff);
      if (hex.length() <= 1)
        // Append the leading zero.
        output.append("0");
      output.append(hex);
    }
  }

  /**
   * Return a hex string of the contents of buffer from position to limit.
   * @param buffer The buffer.
   * @return A string of hex bytes.
   */
  public static String
  toHex(ByteBuffer buffer)
  {
    StringBuffer output = new StringBuffer(buffer.remaining() * 2);
    toHex(buffer, output);
    return output.toString();
  }

  public final boolean equals(Blob other)
  {
    if (buffer_ == null)
      return other.buffer_ == null;
    else if (other.isNull())
      return false;
    else
      return buffer_.equals(other.buffer_);
  }

  public boolean equals(Object other)
  {
    if (!(other instanceof Blob))
      return false;

    return equals((Blob)other);
  }

  /**
   * Compare this to the other Blob using byte-by-byte comparison from their
   * position to their limit. If this and other are both isNull(), then this
   * returns 0. If this isNull() and the other is not, return -1. If this is not
   * isNull() and the other is, return 1. We compare explicitly because a Blob
   * uses a ByteBuffer which compares based on signed byte, not unsigned byte.
   * @param other The other Blob to compare with.
   * @return 0 If they compare equal, -1 if self is less than other, or 1 if
   * self is greater than other.  If both are equal up to the shortest, then
   * return -1 if self is shorter than other, or 1 of self is longer than other.
   */
  public final int
  compare(Blob other)
  {
    if (buffer_ == null && other.buffer_ == null)
        return 0;
    if (buffer_ == null && other.buffer_ != null)
        return -1;
    if (buffer_ != null && other.buffer_ == null)
        return 1;

    // Manually compare elements as unsigned.
    int r = Math.min(buffer_.remaining(), other.buffer_.remaining());
    for (int i = 0; i < r; ++i) {
      // b & 0xff makes the byte unsigned and returns an int.
      int xThis = buffer_.get(buffer_.position() + i) & 0xff;
      int xOther = other.buffer_.get(other.buffer_.position() + i) & 0xff;

      if (xThis < xOther)
        return -1;
      if (xThis > xOther)
        return 1;
    }

    // They are equal up to the shorter.
    if (buffer_.remaining() < other.buffer_.remaining())
        return -1;
    if (buffer_.remaining() > other.buffer_.remaining())
        return 1;
    return 0;
  }

  public final int
  compareTo(Object o) { return this.compare((Blob)o); }

  // Also include this version for portability.
  public final int
  CompareTo(Object o) { return this.compare((Blob)o); }

  /**
   * If the hash code is already computed then return it, otherwise compute and
   * return the hash code.
   * @return The hash code for the buffer, or 0 if the buffer is null.
   */
  public int hashCode()
  {
    if (!haveHashCode_) {
      if (buffer_ == null)
        hashCode_ = 0;
      else
        hashCode_ = buffer_.hashCode();

      haveHashCode_ = true;
    }

    return hashCode_;
  }

  /**
   * Decode the byte array as UTF8 and return the Unicode string.
   * @return A unicode string, or "" if the buffer is null.
   */
  public String
  toString()
  {
    if (buffer_ == null)
      return "";
    else {
      try {
        return new String(getImmutableArray(), "UTF-8");
      } catch (UnsupportedEncodingException ex) {
        // We don't expect this to happen.
        throw new Error("UTF-8 decoder not supported: " + ex.getMessage());
      }
    }
  }

  private final ByteBuffer buffer_;
  private boolean haveHashCode_ = false;
  private int hashCode_;
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy