jdk/src/java.base/share/classes/jdk/internal/jimage/BasicImageReader.java
author chegar
Wed, 03 Dec 2014 14:22:58 +0000
changeset 27565 729f9700483a
child 31673 135283550686
permissions -rw-r--r--
8049367: Modular Run-Time Images Reviewed-by: chegar, dfuchs, ihse, joehw, mullan, psandoz, wetmore Contributed-by: alan.bateman@oracle.com, alex.buckley@oracle.com, bradford.wetmore@oracle.com, chris.hegarty@oracle.com, erik.joelsson@oracle.com, james.laskey@oracle.com, jonathan.gibbons@oracle.com, karen.kinnear@oracle.com, magnus.ihse.bursie@oracle.com, mandy.chung@oracle.com, mark.reinhold@oracle.com, paul.sandoz@oracle.com, sundararajan.athijegannathan@oracle.com

/*
 * Copyright (c) 2014, 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 jdk.internal.jimage;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.IntBuffer;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class BasicImageReader {
    private final String imagePath;
    private final PReader preader;
    private final ByteOrder byteOrder;
    private final ImageHeader header;
    private final int indexSize;
    private final IntBuffer redirectBuffer;
    private final IntBuffer offsetsBuffer;
    private final ByteBuffer locationsBuffer;
    private final ByteBuffer stringsBuffer;
    private final ImageStrings strings;

    protected BasicImageReader(String imagePath, ByteOrder byteOrder) throws IOException {
        this.imagePath = imagePath;
        this.preader = PReader.open(imagePath);
        this.byteOrder = byteOrder;
        this.header = ImageHeader.readFrom(byteOrder, getIntBuffer(0, ImageHeader.getHeaderSize()));
        this.indexSize = header.getIndexSize();
        this.redirectBuffer = getIntBuffer(header.getRedirectOffset(), header.getRedirectSize());
        this.offsetsBuffer = getIntBuffer(header.getOffsetsOffset(), header.getOffsetsSize());
        this.locationsBuffer = getByteBuffer(header.getLocationsOffset(), header.getLocationsSize());
        this.stringsBuffer = getByteBuffer(header.getStringsOffset(), header.getStringsSize());
        this.strings = new ImageStrings(new ImageStream(stringsBuffer));
    }

    protected BasicImageReader(String imagePath) throws IOException {
        this(imagePath, ByteOrder.nativeOrder());
    }

    public static BasicImageReader open(String imagePath) throws IOException {
        return new BasicImageReader(imagePath, ByteOrder.nativeOrder());
    }

    public String imagePath() {
        return imagePath;
    }

    public boolean isOpen() {
        return preader.isOpen();
    }

    public void close() throws IOException {
        preader.close();
    }

    public ImageHeader getHeader() {
        return header;
    }

    public ImageLocation findLocation(String name) {
        return findLocation(new UTF8String(name));
    }

    public ImageLocation findLocation(byte[] name) {
        return findLocation(new UTF8String(name));
    }

    public synchronized ImageLocation findLocation(UTF8String name) {
        int count = header.getLocationCount();
        int hash = name.hashCode() % count;
        int redirect = getRedirect(hash);

        if (redirect == 0) {
            return null;
        }

        int index;

        if (redirect < 0) {
            // If no collision.
            index = -redirect - 1;
        } else {
            // If collision, recompute hash code.
            index = name.hashCode(redirect) % count;
        }

        int offset = getOffset(index);

        if (offset == 0) {
            return null;
        }

        ImageLocation location = getLocation(offset);

        return location.verify(name) ? location : null;
    }

    public String[] getEntryNames() {
        return getEntryNames(true);
    }

    public String[] getEntryNames(boolean sorted) {
        int count = header.getLocationCount();
        List<String> list = new ArrayList<>();

        for (int i = 0; i < count; i++) {
            int offset = getOffset(i);

            if (offset != 0) {
                ImageLocation location = ImageLocation.readFrom(locationsBuffer, offset, strings);
                list.add(location.getFullnameString());
            }
        }

        String[] array = list.toArray(new String[0]);

        if (sorted) {
            Arrays.sort(array);
        }

        return array;
    }

    protected ImageLocation[] getAllLocations(boolean sorted) {
        int count = header.getLocationCount();
        List<ImageLocation> list = new ArrayList<>();

        for (int i = 0; i < count; i++) {
            int offset = getOffset(i);

            if (offset != 0) {
                ImageLocation location = ImageLocation.readFrom(locationsBuffer, offset, strings);
                list.add(location);
            }
        }

        ImageLocation[] array = list.toArray(new ImageLocation[0]);

        if (sorted) {
            Arrays.sort(array, (ImageLocation loc1, ImageLocation loc2) ->
                    loc1.getFullnameString().compareTo(loc2.getFullnameString()));
        }

        return array;
    }

    private IntBuffer getIntBuffer(long offset, long size) throws IOException {
        MappedByteBuffer buffer = preader.channel().map(FileChannel.MapMode.READ_ONLY, offset, size);
        buffer.order(byteOrder);

        return buffer.asIntBuffer();
    }

    private ByteBuffer getByteBuffer(long offset, long size) throws IOException {
        MappedByteBuffer buffer = preader.channel().map(FileChannel.MapMode.READ_ONLY, offset, size);
        // order is not copied into the readonly copy.
        ByteBuffer readOnly = buffer.asReadOnlyBuffer();
        readOnly.order(byteOrder);
        return readOnly;
    }

    private int getRedirect(int index) {
        return redirectBuffer.get(index);
    }

    private int getOffset(int index) {
        return offsetsBuffer.get(index);
    }

    private ImageLocation getLocation(int offset) {
        return ImageLocation.readFrom(locationsBuffer, offset, strings);
    }

    public String getString(int offset) {
        return strings.get(offset).toString();
    }

    public byte[] getResource(ImageLocation loc) throws IOException {
        long compressedSize = loc.getCompressedSize();
        assert compressedSize < Integer.MAX_VALUE;

        if (compressedSize == 0) {
            return preader.read((int)loc.getUncompressedSize(),
                                indexSize + loc.getContentOffset());
        } else {
            byte[] buf = preader.read((int)compressedSize,
                                      indexSize + loc.getContentOffset());
            return ImageFile.Compressor.decompress(buf);
        }
    }

    public byte[] getResource(String name) throws IOException {
        ImageLocation location = findLocation(name);

        return location != null ? getResource(location) : null;
    }

    public List<String> getNames(String name) throws IOException {
        return getNames(getResource(name));
    }

    public List<String> getNames(byte[] bytes) {
        IntBuffer buffer = ByteBuffer.wrap(bytes).asIntBuffer();
        List<String> names = new ArrayList<>();

        while (buffer.hasRemaining()) {
            int offset = buffer.get();
            names.add(getString(offset));
        }

        return names;
    }
}