content: /*
   * Copyright (c) 2022, 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 javac.internal.jimage.decompressor;
  
  import java.nio.ByteBuffer;
  import java.nio.ByteOrder;
  import java.util.Objects;
  
  import javac.internal.jimage.decompressor.ResourceDecompressor.StringsProvider;
  
  /**
   *
   * A resource header for compressed resource. This class is handled internally,
   * you don't have to add header to the resource, headers are added automatically
   * for compressed resources.
   *
   * @implNote This class needs to maintain JDK 8 source compatibility.
   *
   * It is used internally in the JDK to implement jimage/jrtfs access,
   * but also compiled and delivered as part of the jrtfs.jar to support access
   * to the jimage file provided by the shipped JDK by tools running on JDK 8.
   */
  public final class CompressedResourceHeader {
  
      private static final int SIZE = 29;
      public static final int MAGIC = 0xCAFEFAFA;
      private final long uncompressedSize;
      private final long compressedSize;
      private final int decompressorNameOffset;
      private final int contentOffset;
      private final boolean isTerminal;
  
      public CompressedResourceHeader(long compressedSize,
              long uncompressedSize, int decompressorNameOffset, int contentOffset,
              boolean isTerminal) {
          this.compressedSize = compressedSize;
          this.uncompressedSize = uncompressedSize;
          this.decompressorNameOffset = decompressorNameOffset;
          this.contentOffset = contentOffset;
          this.isTerminal = isTerminal;
      }
  
      public boolean isTerminal() {
          return isTerminal;
      }
  
      public int getDecompressorNameOffset() {
          return decompressorNameOffset;
      }
  
      public int getContentOffset() {
          return contentOffset;
      }
  
      public String getStoredContent(StringsProvider provider) {
          Objects.requireNonNull(provider);
          if(contentOffset == -1) {
              return null;
          }
          return provider.getString(contentOffset);
      }
  
      public long getUncompressedSize() {
          return uncompressedSize;
      }
  
      public long getResourceSize() {
          return compressedSize;
      }
  
      public byte[] getBytes(ByteOrder order) {
          Objects.requireNonNull(order);
          ByteBuffer buffer = ByteBuffer.allocate(SIZE);
          buffer.order(order);
          buffer.putInt(MAGIC);
          buffer.putLong(compressedSize);
          buffer.putLong(uncompressedSize);
          buffer.putInt(decompressorNameOffset);
          buffer.putInt(contentOffset);
          buffer.put(isTerminal ? (byte)1 : (byte)0);
          return buffer.array();
      }
  
      public static int getSize() {
          return SIZE;
      }
  
      public static CompressedResourceHeader readFromResource(ByteOrder order,
              byte[] resource) {
          Objects.requireNonNull(order);
          Objects.requireNonNull(resource);
          if (resource.length < getSize()) {
              return null;
          }
          ByteBuffer buffer = ByteBuffer.wrap(resource, 0, SIZE);
          buffer.order(order);
          int magic = buffer.getInt();
          if(magic != MAGIC) {
              return null;
          }
          long size = buffer.getLong();
          long uncompressedSize = buffer.getLong();
          int decompressorNameOffset = buffer.getInt();
          int contentIndex = buffer.getInt();
          byte isTerminal = buffer.get();
          return new CompressedResourceHeader(size, uncompressedSize,
                  decompressorNameOffset, contentIndex, isTerminal == 1);
      }
  }
  