package org.apache.cassandra.utils;
/*
 * 
 * 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.
 * 
 */


import java.nio.ByteBuffer;

import org.github.jamm.MemoryLayoutSpecification;
import org.github.jamm.MemoryMeter;

/**
 * A convenience class for wrapping access to MemoryMeter
 */
public class ObjectSizes
{
    private static final MemoryMeter meter = new MemoryMeter()
                                             .omitSharedBufferOverhead()
                                             .withGuessing(MemoryMeter.Guess.FALLBACK_UNSAFE);

    private static final long BUFFER_EMPTY_SIZE = measure(ByteBufferUtil.EMPTY_BYTE_BUFFER);
    private static final long STRING_EMPTY_SIZE = measure("");

    /**
     * Memory a byte array consumes
     * @param bytes byte array to get memory size
     * @return heap-size of the array
     */
    public static long sizeOfArray(byte[] bytes)
    {
        return sizeOfArray(bytes.length, 1);
    }

    /**
     * Memory a long array consumes
     * @param longs byte array to get memory size
     * @return heap-size of the array
     */
    public static long sizeOfArray(long[] longs)
    {
        return sizeOfArray(longs.length, 8);
    }

    /**
     * Memory an int array consumes
     * @param ints byte array to get memory size
     * @return heap-size of the array
     */
    public static long sizeOfArray(int[] ints)
    {
        return sizeOfArray(ints.length, 4);
    }

    /**
     * Memory a reference array consumes
     * @param length the length of the reference array
     * @return heap-size of the array
     */
    public static long sizeOfReferenceArray(int length)
    {
        return sizeOfArray(length, MemoryLayoutSpecification.SPEC.getReferenceSize());
    }

    /**
     * Memory a reference array consumes itself only
     * @param objects the array to size
     * @return heap-size of the array (excluding memory retained by referenced objects)
     */
    public static long sizeOfArray(Object[] objects)
    {
        return sizeOfReferenceArray(objects.length);
    }

    private static long sizeOfArray(int length, long elementSize)
    {
        return MemoryLayoutSpecification.sizeOfArray(length, elementSize);
    }

    /**
     * Memory a ByteBuffer array consumes.
     */
    public static long sizeOnHeapOf(ByteBuffer[] array)
    {
        long allElementsSize = 0;
        for (int i = 0; i < array.length; i++)
            if (array[i] != null)
                allElementsSize += sizeOnHeapOf(array[i]);

        return allElementsSize + sizeOfArray(array);
    }

    public static long sizeOnHeapExcludingData(ByteBuffer[] array)
    {
        return BUFFER_EMPTY_SIZE * array.length + sizeOfArray(array);
    }
    /**
     * Memory a byte buffer consumes
     * @param buffer ByteBuffer to calculate in memory size
     * @return Total in-memory size of the byte buffer
     */
    public static long sizeOnHeapOf(ByteBuffer buffer)
    {
        if (buffer.isDirect())
            return BUFFER_EMPTY_SIZE;
        // if we're only referencing a sub-portion of the ByteBuffer, don't count the array overhead (assume it's slab
        // allocated, so amortized over all the allocations the overhead is negligible and better to undercount than over)
        if (buffer.capacity() > buffer.remaining())
            return buffer.remaining();
        return BUFFER_EMPTY_SIZE + sizeOfArray(buffer.capacity(), 1);
    }

    public static long sizeOnHeapExcludingData(ByteBuffer buffer)
    {
        return BUFFER_EMPTY_SIZE;
    }

    /**
     * Memory a String consumes
     * @param str String to calculate memory size of
     * @return Total in-memory size of the String
     */
    public static long sizeOf(String str)
    {
        return STRING_EMPTY_SIZE + sizeOfArray(str.length(), 2);
    }

    /**
     * @param pojo the object to measure
     * @return the size on the heap of the instance and all retained heap referenced by it, excluding portions of
     * ByteBuffer that are not directly referenced by it but including any other referenced that may also be retained
     * by other objects.
     */
    public static long measureDeep(Object pojo)
    {
        return meter.measureDeep(pojo);
    }

    /**
     * @param pojo the object to measure
     * @return the size on the heap of the instance only, excluding any referenced objects
     */
    public static long measure(Object pojo)
    {
        return meter.measure(pojo);
    }
}
