/*
 * Licensed to the Apache Software Foundation (ASF) under one or more contributor license
 * agreements. See the NOTICE file distributed with this work for additional information regarding
 * copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance with the License. You may obtain a
 * copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software distributed under the License
 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
 * or implied. See the License for the specific language governing permissions and limitations under
 * the License.
 */
package org.apache.geode.internal.offheap;

import java.io.DataOutput;
import java.io.IOException;
import java.nio.ByteBuffer;

import org.apache.geode.internal.Sendable;
import org.apache.geode.internal.cache.CachedDeserializable;
import org.apache.geode.internal.offheap.annotations.Retained;
import org.apache.geode.internal.offheap.annotations.Unretained;

/**
 * Represents an object stored in the cache. Currently this interface is only used for values stored
 * in off-heap regions. This interface provides methods that let you read and write the bytes of
 * addressable memory used to store the bytes of the object. A reference count is used to determine
 * if the object is still allocated. To increment the count call {@link #retain()}. To decrement the
 * count call {@link #release()}. At some point in the future it may also be used for values stored
 * in heap regions.
 *
 * @since Geode 1.0
 */
public interface StoredObject extends Sendable, CachedDeserializable, Releasable {
  /**
   * Returns true if the value stored in this memory chunk is compressed. Returns false if it is
   * uncompressed.
   */
  boolean isCompressed();

  /**
   * Returns the data stored in this object as a deserialized heap object. If it is not serialized
   * then the result will be a byte[]. Otherwise the deserialized heap form of the stored object is
   * returned.
   *
   * @return the data stored in this object as a deserialized heap object.
   */
  Object getValueAsDeserializedHeapObject();

  /**
   * Returns the data stored in this object as a heap byte array. If it is not serialized then the
   * result will only contain the raw bytes stored in this object. Otherwise the serialized heap
   * form of the stored object is returned.
   *
   * @return the data stored in this object as a heap byte array.
   */
  byte[] getValueAsHeapByteArray();

  /**
   * Take all the bytes in the object and write them to the data output as a byte array. If the
   * StoredObject is not serialized then its raw byte array is sent. But if it is serialized then
   * the serialized byte array is sent. The corresponding de-serialization will need to call
   * readByteArray.
   *
   * @param out the data output to send this object to
   */
  void sendAsByteArray(DataOutput out) throws IOException;

  /**
   * Take all the bytes in the object and write them to the data output as a byte array. If the
   * StoredObject is not serialized then an exception will be thrown. The corresponding
   * deserialization will need to call readObject and will get an instance of
   * VMCachedDeserializable.
   *
   * @param out the data output to send this object to
   */
  void sendAsCachedDeserializable(DataOutput out) throws IOException;

  /**
   * Call to indicate that this object's memory is in use by the caller. The memory will stay
   * allocated until {@link #release()} is called. It is ok for a thread other than the one that
   * called this method to call release. This method is called implicitly at the time the chunk is
   * allocated. Note: @Retained tells you that "this" is retained by this method.
   *
   * @throws IllegalStateException if the max ref count is exceeded.
   * @return true if we are able to retain this chunk; false if we need to retry
   */
  @Retained
  boolean retain();

  /**
   * Returns true if this type of StoredObject uses a references count; false otherwise.
   */
  boolean hasRefCount();

  /**
   * Returns the number of users of this memory. If this type of StoredObject does not have a
   * reference count then -1 is returned.
   */
  int getRefCount();

  /**
   * Returns the address of the memory used to store this object. This address may not be to the
   * first byte of stored data since the implementation may store some internal data in the first
   * bytes of the memory. This address can be used with AddressableMemoryManager.
   */
  long getAddress();

  /**
   * Returns the number of bytes of memory used by this object to store an object. This size
   * includes any bytes used for padding and meta-information.
   */
  int getSize();

  /**
   * Returns the number of bytes of memory used to store the object. This size does not include any
   * bytes used for padding.
   */
  int getDataSize();

  byte readDataByte(int offset);

  void writeDataByte(int offset, byte value);

  void readDataBytes(int offset, byte[] bytes);

  void writeDataBytes(int offset, byte[] bytes);

  void readDataBytes(int offset, byte[] bytes, int bytesOffset, int size);

  void writeDataBytes(int offset, byte[] bytes, int bytesOffset, int size);

  /**
   * Returns an address that can read data from this StoredObject at the given offset.
   */
  long getAddressForReadingData(int offset, int size);

  /**
   * Returns a StoredObject that acts as if its data is our data starting at the given offset and
   * limited to the given number of bytes.
   */
  StoredObject slice(int offset, int limit);

  /**
   * Returns true if our data is equal to other's data; false otherwise.
   */
  boolean checkDataEquals(StoredObject other);

  /**
   * Returns true if the given bytes are equal to our data bytes; false otherwise
   */
  boolean checkDataEquals(byte[] serializedObj);

  /**
   * Creates and returns a direct ByteBuffer that contains the data of this stored object. Note that
   * the returned ByteBuffer has a reference to the address of this stored object so it can only be
   * used while this stored object is retained.
   *
   * @return the created direct byte buffer or null if it could not be created.
   */
  @Unretained
  ByteBuffer createDirectByteBuffer();

  /**
   * Returns true if the data is serialized with PDX
   */
  boolean isSerializedPdxInstance();

  /**
   * Returns a StoredObject that does not cache the heap form. If a StoredObject is going to be kept
   * around for a while then it is good to call this so that it will not also keep the heap form in
   * memory.
   */
  StoredObject getStoredObjectWithoutHeapForm();

  /**
   * Return true if the given "o" is reference to off-heap memory.
   */
  static boolean isOffHeapReference(Object o) {
    return (o instanceof StoredObject) && ((StoredObject) o).hasRefCount();
  }
}
