All Downloads are FREE. Search and download functionalities are using the official Maven repository.

ucar.nc2.iosp.hdf5.BTree2 Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (c) 1998-2018 John Caron and University Corporation for Atmospheric Research/Unidata
 * See LICENSE for license information.
 */

package ucar.nc2.iosp.hdf5;

import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import ucar.nc2.util.Misc;
import ucar.unidata.io.RandomAccessFile;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
 * // Level 1A2
 *
 * These are used for symbols, not data i think. Version 1 is H5header.GroupBTree.
 *
 * Version 2 B-trees are "traditional" B-trees, with one major difference. Instead of just using a simple pointer
 * (or address in the file) to a child of an internal node, the pointer to the child node contains two additional
 * pieces of information: the number of records in the child node itself, and the total number of records in the child
 * node and all its descendents. Storing this additional information allows fast array-like indexing to locate the n'th
 * record in the B-tree.
 * 
 * The entry into a version 2 B-tree is a header which contains global information about the structure of the B-tree.
 * The root node address field in the header points to the B-tree root node, which is either an internal or leaf node,
 * depending on the value in the header's depth field. An internal node consists of records plus pointers to further
 * leaf
 * or internal nodes in the tree. A leaf node consists of solely of records. The format of the records depends on the
 * B-tree type (stored in the header).
 *
 * 
 * @author caron
 * @since 6/27/12
 */
public class BTree2 {
  private boolean debugBtree2, debugPos;
  private java.io.PrintStream debugOut = System.out;

  public final byte btreeType;
  private int nodeSize; // size in bytes of btree nodes
  private short recordSize; // size in bytes of btree records

  private String owner;
  private H5headerIF h5;
  private RandomAccessFile raf;

  public List entryList = new ArrayList<>();

  public BTree2(H5headerIF h5, String owner, long address) throws IOException {
    this.h5 = h5;
    this.raf = h5.getRandomAccessFile();
    this.owner = owner;

    raf.seek(h5.getFileOffset(address));

    // header
    byte[] heapname = new byte[4];
    raf.readFully(heapname);
    String magic = new String(heapname, StandardCharsets.UTF_8);
    if (!magic.equals("BTHD"))
      throw new IllegalStateException(magic + " should equal BTHD");

    byte version = raf.readByte();
    btreeType = raf.readByte();
    nodeSize = raf.readInt();
    recordSize = raf.readShort();
    short treeDepth = raf.readShort();
    byte split = raf.readByte();
    byte merge = raf.readByte();
    long rootNodeAddress = h5.readOffset();
    short numRecordsRootNode = raf.readShort();
    long totalRecords = h5.readLength(); // total in entire btree
    int checksum = raf.readInt();

    if (debugBtree2) {
      debugOut.printf(
          "BTree2 (%s) version=%d type=%d treeDepth=%d nodeSize=%d recordSize=%d numRecordsRootNode=%d totalRecords=%d rootNodeAddress=%d%n",
          owner, version, btreeType, treeDepth, nodeSize, recordSize, numRecordsRootNode, totalRecords,
          rootNodeAddress);
    }

    if (treeDepth > 0) {
      InternalNode node = new InternalNode(rootNodeAddress, numRecordsRootNode, recordSize, treeDepth);
      node.recurse();
    } else {
      LeafNode leaf = new LeafNode(rootNodeAddress, numRecordsRootNode);
      leaf.addEntries(entryList);
    }
  }

  BTree2.Record1 getEntry1(int hugeObjectID) {
    for (Entry2 entry : entryList) {
      BTree2.Record1 record1 = (BTree2.Record1) entry.record;
      if (record1.hugeObjectID == hugeObjectID)
        return record1;
    }
    return null;
  }

  // these are part of the level 1A data structure, type = 0
  public static class Entry2 {
    long childAddress, nrecords, totNrecords;
    public Object record;
  }

  class InternalNode {
    Entry2[] entries;
    int depth;

    InternalNode(long address, short nrecords, short recordSize, int depth) throws IOException {
      this.depth = depth;
      raf.seek(h5.getFileOffset(address));

      if (debugPos)
        debugOut.println("--Btree2 InternalNode position=" + raf.getFilePointer());

      // header
      byte[] sig = new byte[4];
      raf.readFully(sig);
      String magic = new String(sig, StandardCharsets.UTF_8);
      if (!magic.equals("BTIN"))
        throw new IllegalStateException(magic + " should equal BTIN");

      byte version = raf.readByte();
      byte nodeType = raf.readByte();
      if (nodeType != btreeType)
        throw new IllegalStateException();

      if (debugBtree2)
        debugOut.println("   BTree2 InternalNode version=" + version + " type=" + nodeType + " nrecords=" + nrecords);

      entries = new Entry2[nrecords + 1]; // did i mention theres actually n+1 children?
      for (int i = 0; i < nrecords; i++) {
        entries[i] = new Entry2();
        entries[i].record = readRecord(btreeType);
      }
      entries[nrecords] = new Entry2();

      int maxNumRecords = nodeSize / recordSize; // LOOK ?? guessing
      int maxNumRecordsPlusDesc = nodeSize / recordSize; // LOOK ?? guessing
      for (int i = 0; i < nrecords + 1; i++) {
        Entry2 e = entries[i];
        e.childAddress = h5.readOffset();
        e.nrecords = h5.readVariableSizeUnsigned(1); // readVariableSizeMax(maxNumRecords);
        if (depth > 1)
          e.totNrecords = h5.readVariableSizeUnsigned(2); // readVariableSizeMax(maxNumRecordsPlusDesc);

        if (debugBtree2)
          debugOut.println(" BTree2 entry childAddress=" + e.childAddress + " nrecords=" + e.nrecords + " totNrecords="
              + e.totNrecords);
      }

      // skip
      raf.readInt();
    }

    void recurse() throws IOException {
      for (Entry2 e : entries) {
        if (depth > 1) {
          InternalNode node = new InternalNode(e.childAddress, (short) e.nrecords, recordSize, depth - 1);
          node.recurse();
        } else {
          long nrecs = e.nrecords;
          LeafNode leaf = new LeafNode(e.childAddress, (short) nrecs);
          leaf.addEntries(entryList);
        }
        if (e.record != null) // last one is null
          entryList.add(e);
      }
    }
  }

  class LeafNode {
    Entry2[] entries;

    LeafNode(long address, short nrecords) throws IOException {
      raf.seek(h5.getFileOffset(address));

      if (debugPos)
        debugOut.println("--Btree2 InternalNode position=" + raf.getFilePointer());

      // header
      byte[] sig = new byte[4];
      raf.readFully(sig);
      String magic = new String(sig, StandardCharsets.UTF_8);
      if (!magic.equals("BTLF"))
        throw new IllegalStateException(magic + " should equal BTLF");

      byte version = raf.readByte();
      byte nodeType = raf.readByte();
      if (nodeType != btreeType)
        throw new IllegalStateException();

      if (debugBtree2)
        debugOut.println("   BTree2 LeafNode version=" + version + " type=" + nodeType + " nrecords=" + nrecords);

      entries = new Entry2[nrecords];
      for (int i = 0; i < nrecords; i++) {
        entries[i] = new Entry2();
        entries[i].record = readRecord(btreeType);
      }

      // skip
      raf.readInt();
    }

    void addEntries(List list) {
      Collections.addAll(list, entries);
    }
  }

  Object readRecord(int type) throws IOException {
    switch (type) {
      case 1:
        return new Record1();
      case 2:
        return new Record2();
      case 3:
        return new Record3();
      case 4:
        return new Record4();
      case 5:
        return new Record5();
      case 6:
        return new Record6();
      case 7: {
        return new Record70(); // LOOK wrong
      }
      case 8:
        return new Record8();
      case 9:
        return new Record9();
      default:
        throw new IllegalStateException();
    }
  }

  class Record1 {
    long hugeObjectAddress, hugeObjectLength, hugeObjectID;

    Record1() throws IOException {
      hugeObjectAddress = h5.readOffset();
      hugeObjectLength = h5.readLength();
      hugeObjectID = h5.readLength();
    }
  }

  class Record2 {
    long hugeObjectAddress, hugeObjectLength, hugeObjectID, hugeObjectSize;
    int filterMask;

    Record2() throws IOException {
      hugeObjectAddress = h5.readOffset();
      hugeObjectLength = h5.readLength();
      filterMask = raf.readInt();
      hugeObjectSize = h5.readLength();
      hugeObjectID = h5.readLength();
    }
  }

  class Record3 {
    long hugeObjectAddress, hugeObjectLength;

    Record3() throws IOException {
      hugeObjectAddress = h5.readOffset();
      hugeObjectLength = h5.readLength();
    }
  }

  class Record4 {
    long hugeObjectAddress, hugeObjectLength, hugeObjectID, hugeObjectSize;
    int filterMask;

    Record4() throws IOException {
      hugeObjectAddress = h5.readOffset();
      hugeObjectLength = h5.readLength();
      filterMask = raf.readInt();
      hugeObjectSize = h5.readLength();
    }
  }

  public class Record5 {
    int nameHash;
    byte[] heapId = new byte[7];

    Record5() throws IOException {
      nameHash = raf.readInt();
      raf.readFully(heapId);

      if (debugBtree2)
        debugOut.println("  record5 nameHash=" + nameHash + " heapId=" + Arrays.toString(heapId));
    }

    public byte[] getHeapId() {
      return heapId;
    }
  }

  public class Record6 {
    long creationOrder;
    byte[] heapId = new byte[7];

    Record6() throws IOException {
      creationOrder = raf.readLong();
      raf.readFully(heapId);
      if (debugBtree2)
        debugOut.println("  record6 creationOrder=" + creationOrder + " heapId=" + Arrays.toString(heapId));
    }

    public byte[] getHeapId() {
      return heapId;
    }
  }

  class Record70 {
    byte location;
    int refCount;
    byte[] id = new byte[8];

    Record70() throws IOException {
      location = raf.readByte();
      refCount = raf.readInt();
      raf.readFully(id);
    }
  }

  class Record71 {
    byte location, messtype;
    short index;
    long address;

    Record71() throws IOException {
      location = raf.readByte();
      raf.readByte(); // skip a byte
      messtype = raf.readByte();
      index = raf.readShort();
      address = h5.readOffset();
    }
  }

  public class Record8 {
    byte flags;
    int creationOrder, nameHash;
    byte[] heapId = new byte[8];

    Record8() throws IOException {
      raf.readFully(heapId);
      flags = raf.readByte();
      creationOrder = raf.readInt();
      nameHash = raf.readInt();
      if (debugBtree2)
        debugOut.println("  record8 creationOrder=" + creationOrder + " heapId=" + Arrays.toString(heapId));
    }

    public byte[] getHeapId() {
      return heapId;
    }
  }

  public class Record9 {
    byte flags;
    int creationOrder;
    byte[] heapId = new byte[8];

    Record9() throws IOException {
      raf.readFully(heapId);
      flags = raf.readByte();
      creationOrder = raf.readInt();
    }

    public byte[] getHeapId() {
      return heapId;
    }
  }

} // BTree2




© 2015 - 2025 Weber Informatics LLC | Privacy Policy