/*
 * Copyright (c) 2003, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code 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 General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */

package sun.management.counter.perf;

import sun.management.counter.*;
import java.nio.*;

class Prologue {
    // these constants should match their #define counterparts in vmdata.hpp
    private final static byte PERFDATA_BIG_ENDIAN    = 0;
    private final static byte PERFDATA_LITTLE_ENDIAN = 1;
    private final static int  PERFDATA_MAGIC         = 0xcafec0c0;

    private class PrologueFieldOffset {
        private final static int SIZEOF_BYTE = 1;
        private final static int SIZEOF_INT  = 4;
        private final static int SIZEOF_LONG = 8;

        private final static int MAGIC_SIZE            = SIZEOF_INT;
        private final static int BYTE_ORDER_SIZE       = SIZEOF_BYTE;
        private final static int MAJOR_SIZE            = SIZEOF_BYTE;
        private final static int MINOR_SIZE            = SIZEOF_BYTE;
        private final static int ACCESSIBLE_SIZE       = SIZEOF_BYTE;
        private final static int USED_SIZE             = SIZEOF_INT;
        private final static int OVERFLOW_SIZE         = SIZEOF_INT;
        private final static int MOD_TIMESTAMP_SIZE    = SIZEOF_LONG;
        private final static int ENTRY_OFFSET_SIZE     = SIZEOF_INT;
        private final static int NUM_ENTRIES_SIZE      = SIZEOF_INT;

        // these constants must match the field offsets and sizes
        // in the PerfDataPrologue structure in perfMemory.hpp
        final static int MAGIC          = 0;
        final static int BYTE_ORDER     = MAGIC + MAGIC_SIZE;
        final static int MAJOR_VERSION  = BYTE_ORDER + BYTE_ORDER_SIZE;
        final static int MINOR_VERSION  = MAJOR_VERSION + MAJOR_SIZE;
        final static int ACCESSIBLE     = MINOR_VERSION + MINOR_SIZE;
        final static int USED           = ACCESSIBLE + ACCESSIBLE_SIZE;
        final static int OVERFLOW       = USED + USED_SIZE;
        final static int MOD_TIMESTAMP  = OVERFLOW + OVERFLOW_SIZE;
        final static int ENTRY_OFFSET   = MOD_TIMESTAMP + MOD_TIMESTAMP_SIZE;
        final static int NUM_ENTRIES    = ENTRY_OFFSET + ENTRY_OFFSET_SIZE;
        final static int PROLOGUE_2_0_SIZE = NUM_ENTRIES + NUM_ENTRIES_SIZE;
    }


    private ByteBuffer header;
    private int magic;

    Prologue(ByteBuffer b) {
        this.header = b.duplicate();

        // the magic number is always stored in big-endian format
        // save and restore the buffer's initial byte order around
        // the fetch of the data.
        header.order(ByteOrder.BIG_ENDIAN);
        header.position(PrologueFieldOffset.MAGIC);
        magic = header.getInt();

        // the magic number is always stored in big-endian format
        if (magic != PERFDATA_MAGIC) {
            throw new InstrumentationException("Bad Magic: " +
                                               Integer.toHexString(getMagic()));
        }


        // set the buffer's byte order according to the value of its
        // byte order field.
        header.order(getByteOrder());

        // Check version
        int major = getMajorVersion();
        int minor = getMinorVersion();

        if (major < 2) {
            throw new InstrumentationException("Unsupported version: " +
                                               major + "." + minor);
        }

        // Currently, only support 2.0 version.
        header.limit(PrologueFieldOffset.PROLOGUE_2_0_SIZE);
    }

    public int getMagic() {
        return magic;
    }

    public int getMajorVersion() {
        header.position(PrologueFieldOffset.MAJOR_VERSION);
        return (int)header.get();
    }

    public int getMinorVersion() {
        header.position(PrologueFieldOffset.MINOR_VERSION);
        return (int)header.get();
    }

    public ByteOrder getByteOrder() {
        header.position(PrologueFieldOffset.BYTE_ORDER);

        byte byte_order = header.get();
        if (byte_order == PERFDATA_BIG_ENDIAN) {
            return ByteOrder.BIG_ENDIAN;
        }
        else {
            return ByteOrder.LITTLE_ENDIAN;
        }
    }

    public int getEntryOffset() {
        header.position(PrologueFieldOffset.ENTRY_OFFSET);
        return header.getInt();
    }

    // The following fields are updated asynchronously
    // while they are accessed by these methods.
    public int getUsed() {
        header.position(PrologueFieldOffset.USED);
        return header.getInt();
    }

    public int getOverflow() {
        header.position(PrologueFieldOffset.OVERFLOW);
        return header.getInt();
    }

    public long getModificationTimeStamp() {
        header.position(PrologueFieldOffset.MOD_TIMESTAMP);
        return header.getLong();
    }

    public int getNumEntries() {
        header.position(PrologueFieldOffset.NUM_ENTRIES);
        return header.getInt();
    }

    public boolean isAccessible() {
        header.position(PrologueFieldOffset.ACCESSIBLE);
        byte b = header.get();
        return (b == 0 ? false : true);
    }
}