Skip to content

Commit a7dd2a5

Browse files
committed
Updates for v2 arch
1 parent 904e357 commit a7dd2a5

20 files changed

Lines changed: 1627 additions & 395 deletions
Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
1+
package org.hdf5javalib.hdffile.infrastructure.fractalheap.archive.gemini;
2+
3+
import java.io.IOException;
4+
import java.nio.channels.SeekableByteChannel;
5+
import java.util.Arrays;
6+
import java.util.HashMap;
7+
import java.util.Map;
8+
9+
// package org.hdf5javalib.hdffile.infrastructure.fractalheap.gemini;
10+
// ... (imports)
11+
12+
public class FractalHeap {
13+
// ... (fields are mostly the same)
14+
public final FractalHeapHeader header;
15+
private final SeekableByteChannel channel;
16+
private final int sizeOfOffsets;
17+
private final int sizeOfLengths;
18+
private final Map<Long, Object> blockCache;
19+
20+
// ### REASONING FOR CHANGE ###
21+
// The DirectBlockInfo needs to return not just the address and size of the located
22+
// block, but also its starting offset within the heap's linear address space. This
23+
// is essential for calculating the object's relative position inside the block.
24+
private static class DirectBlockInfo {
25+
final long address; // File address of the block
26+
final long size; // Size of the block
27+
final long startOffset; // The block's starting offset in the heap's linear address space
28+
29+
DirectBlockInfo(long address, long size, long startOffset) {
30+
this.address = address;
31+
this.size = size;
32+
this.startOffset = startOffset;
33+
}
34+
}
35+
36+
public FractalHeap(FractalHeapHeader header, SeekableByteChannel channel, int sizeOfOffsets, int sizeOfLengths) {
37+
this.header = header;
38+
this.channel = channel;
39+
this.sizeOfOffsets = sizeOfOffsets;
40+
this.sizeOfLengths = sizeOfLengths;
41+
this.blockCache = new HashMap<>();
42+
}
43+
44+
// ... (getObject method is fine)
45+
public byte[] getObject(byte[] rawId) throws IOException {
46+
// ... (existing implementation is good)
47+
if (header.addressOfRootBlock == Hdf5Utils.UNDEFINED_ADDRESS) {
48+
throw new IOException("Cannot get object; fractal heap has no root block.");
49+
}
50+
51+
ParsedHeapId id = new ParsedHeapId(rawId, this.header);
52+
53+
54+
switch (id.type) {
55+
case 0: // Managed Object
56+
return findManagedObject(id);
57+
case 1: // Huge Object
58+
throw new UnsupportedOperationException("Huge object retrieval not implemented.");
59+
case 2: // Tiny Object
60+
// ... (existing tiny object logic seems plausible)
61+
return null;
62+
default:
63+
throw new IOException("Unknown heap object type: " + id.type);
64+
}
65+
}
66+
67+
68+
private byte[] findManagedObject(ParsedHeapId id) throws IOException {
69+
DirectBlockInfo blockInfo = findDirectBlockForOffset(id.offset);
70+
if (blockInfo.address == Hdf5Utils.UNDEFINED_ADDRESS) {
71+
throw new IOException("Could not locate direct block for heap offset: " + id.offset);
72+
}
73+
74+
FractalHeapDirectBlock directBlock = getDirectBlock(blockInfo.address, blockInfo.size);
75+
76+
// ### REASONING FOR CHANGE ###
77+
// The object's offset is absolute (from the start of the heap). The block's
78+
// offset is also absolute. To find the object's position *inside* the block's
79+
// data array, we must subtract the block's starting offset.
80+
// OLD: long offsetInBlock = id.offset;
81+
long offsetInBlock = id.offset - blockInfo.startOffset;
82+
83+
if (offsetInBlock < 0 || offsetInBlock + id.length > directBlock.objectData.length) {
84+
throw new IOException("Heap ID points outside of the located direct block's bounds. HeapID offset: " + id.offset +
85+
", Block Start Offset: " + blockInfo.startOffset + ", Calculated Offset in Block: " + offsetInBlock +
86+
", Block Data Length: " + directBlock.objectData.length);
87+
}
88+
89+
return Arrays.copyOfRange(directBlock.objectData, (int) offsetInBlock, (int) (offsetInBlock + id.length));
90+
}
91+
92+
private DirectBlockInfo findDirectBlockForOffset(long targetOffset) throws IOException {
93+
if (header.currentRowsInRootIndirectBlock == 0) {
94+
if (targetOffset < header.startingBlockSize) {
95+
// The root is a direct block. Its starting offset in the heap is 0.
96+
return new DirectBlockInfo(header.addressOfRootBlock, header.startingBlockSize, 0);
97+
} else {
98+
throw new IOException("Target offset is out of bounds for a direct root block.");
99+
}
100+
} else {
101+
// The root is an indirect block.
102+
return findAddressInIndirectBlock(header.addressOfRootBlock, header.currentRowsInRootIndirectBlock, targetOffset);
103+
}
104+
}
105+
106+
private DirectBlockInfo findAddressInIndirectBlock(long indirectBlockAddr, int rows, long targetOffset) throws IOException {
107+
FractalHeapIndirectBlock indirectBlock = getIndirectBlock(indirectBlockAddr, rows);
108+
109+
// ### REASONING FOR CHANGE ###
110+
// This is the core logic fix. We start with the single known offset for the
111+
// indirect block, which covers its first child. Then, we loop and cumulatively
112+
// add the size of each child block to find the starting offset of the next one.
113+
long currentBlockStartOffset = indirectBlock.blockOffset;
114+
115+
for (int i = 0; i < indirectBlock.childDirectBlockAddresses.length; i++) {
116+
// The row determines the size of the block this entry points to.
117+
int row = i / header.tableWidth;
118+
long childBlockSize = header.startingBlockSize * (1L << row);
119+
120+
// Check if our target offset falls within the range of THIS child block.
121+
if (targetOffset >= currentBlockStartOffset && targetOffset < (currentBlockStartOffset + childBlockSize)) {
122+
long childAddress = indirectBlock.childDirectBlockAddresses[i];
123+
if (childAddress == Hdf5Utils.UNDEFINED_ADDRESS) {
124+
// This space in the heap is allocated but the block hasn't been written to disk.
125+
throw new IOException("Found entry for offset " + targetOffset + " but the block address is undefined.");
126+
}
127+
// We found it! Return the address, size, and calculated starting offset.
128+
return new DirectBlockInfo(childAddress, childBlockSize, currentBlockStartOffset);
129+
}
130+
131+
// If not, add this block's size to the running offset and check the next entry.
132+
currentBlockStartOffset += childBlockSize;
133+
}
134+
135+
if (indirectBlock.childIndirectBlockAddresses.length > 0) {
136+
// TODO: The same cumulative logic needs to be applied to find which child
137+
// indirect block to descend into. The `currentBlockStartOffset` would continue
138+
// to accumulate across the direct block section first.
139+
throw new UnsupportedOperationException("Searching for objects in nested indirect blocks is not yet implemented.");
140+
}
141+
142+
throw new IOException("Failed to find a direct block containing offset " + targetOffset);
143+
}
144+
145+
// --- Caching Helper Methods ---
146+
147+
private FractalHeapDirectBlock getDirectBlock(long address, long size) throws IOException {
148+
return (FractalHeapDirectBlock) blockCache.computeIfAbsent(address, addr -> {
149+
try {
150+
return FractalHeapDirectBlock.read(channel, (Long) addr, size, header, sizeOfOffsets);
151+
} catch (IOException e) {
152+
// Lambda expressions can't throw checked exceptions directly, so we wrap them.
153+
throw new RuntimeException(e);
154+
}
155+
});
156+
}
157+
158+
private FractalHeapIndirectBlock getIndirectBlock(long address, int rows) throws IOException {
159+
try {
160+
return (FractalHeapIndirectBlock) blockCache.computeIfAbsent(address, addr -> {
161+
try {
162+
return FractalHeapIndirectBlock.read(channel, (Long) addr, rows, header, sizeOfOffsets, sizeOfLengths);
163+
} catch (IOException e) {
164+
throw new RuntimeException(e);
165+
}
166+
});
167+
} catch(RuntimeException e) {
168+
// Unwrap the original IOException
169+
if (e.getCause() instanceof IOException) {
170+
throw (IOException) e.getCause();
171+
}
172+
throw e;
173+
}
174+
}
175+
}

src/main/java/org/hdf5javalib/hdffile/infrastructure/fractalheap/gemini/FractalHeapDirectBlock.java renamed to src/main/java/org/hdf5javalib/hdffile/infrastructure/fractalheap/archive/gemini/FractalHeapDirectBlock.java

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,45 @@
1-
package org.hdf5javalib.hdffile.infrastructure.fractalheap.gemini;
1+
package org.hdf5javalib.hdffile.infrastructure.fractalheap.archive.gemini;
22

33
import java.io.IOException;
44
import java.nio.ByteBuffer;
55
import java.nio.ByteOrder;
66
import java.nio.channels.SeekableByteChannel;
77

8+
// package org.hdf5javalib.hdffile.infrastructure.fractalheap.gemini;
9+
// ... (imports)
10+
811
class FractalHeapDirectBlock {
912
public final String signature;
1013
public final short version;
1114
public final long heapHeaderAddress;
1215
public final long blockOffset;
13-
public final byte[] objectData;
16+
public final byte[] objectData; // Now contains ONLY the object data
1417
public final Integer checksum;
1518

1619
private FractalHeapDirectBlock(ByteBuffer bb, FractalHeapHeader header, int sizeOfOffsets) throws IOException {
1720
bb.order(ByteOrder.LITTLE_ENDIAN);
1821
this.signature = Hdf5Utils.readSignature(bb, "FHDB");
1922
this.version = bb.get();
2023
this.heapHeaderAddress = Hdf5Utils.readOffset(bb, sizeOfOffsets);
21-
int blockOffsetSize = Hdf5Utils.ceilLog2(1L << header.maximumHeapSize) / 8;
24+
int blockOffsetSize = (Hdf5Utils.ceilLog2(1L << header.maximumHeapSize) + 7) / 8;
2225
this.blockOffset = Hdf5Utils.readVariableSizeUnsigned(bb, blockOffsetSize);
26+
27+
// ### REASONING FOR CHANGE ###
28+
// The checksum appears at the END of the data. We need to handle this carefully.
29+
// We will copy the data between the header and the potential checksum into our objectData array.
30+
int dataSize = bb.remaining();
2331
if ((header.flags & 2) != 0) {
24-
// dataSize -= 4;
25-
// bb.position(bb.position() + dataSize);
26-
this.checksum = bb.getInt();
27-
// bb.position(bb.position() - dataSize - 4);
32+
// If checksum exists, it's the last 4 bytes.
33+
dataSize -= 4;
34+
this.checksum = bb.getInt(bb.position() + dataSize);
2835
} else {
2936
this.checksum = null;
3037
}
31-
this.objectData = bb.array();
38+
39+
// OLD: this.objectData = bb.array();
40+
// NEW: Copy only the remaining bytes (the actual data payload) into the objectData field.
41+
this.objectData = new byte[dataSize];
42+
bb.get(this.objectData);
3243
}
3344

3445
public static FractalHeapDirectBlock read(SeekableByteChannel channel, long blockAddress, long expectedBlockSize, FractalHeapHeader header, int sizeOfOffsets) throws IOException {
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
package org.hdf5javalib.hdffile.infrastructure.fractalheap.archive.gemini;
2+
3+
import java.io.IOException;
4+
import java.nio.ByteBuffer;
5+
import java.nio.ByteOrder;
6+
import java.nio.channels.SeekableByteChannel;
7+
8+
class FractalHeapHeader {
9+
public final String signature;
10+
public final short version;
11+
public final int heapIdLength;
12+
public final int ioFiltersEncodedLength;
13+
public final short flags;
14+
public final long maximumSizeOfManagedObjects;
15+
public final long nextHugeObjectId;
16+
public final long v2BtreeAddressOfHugeObjects;
17+
public final long amountOfFreeSpaceInManagedBlocks;
18+
public final long addressOfManagedBlockFreeSpaceManager;
19+
public final long amountOfManagedSpaceInHeap;
20+
public final long amountOfAllocatedManagedSpaceInHeap;
21+
public final long offsetOfDirectBlockAllocationIterator;
22+
public final long numberOfManagedObjectsInHeap;
23+
public final long sizeOfHugeObjectsInHeap;
24+
public final long numberOfHugeObjectsInHeap;
25+
public final long sizeOfTinyObjectsInHeap;
26+
public final long numberOfTinyObjectsInHeap;
27+
public final int tableWidth;
28+
public final long startingBlockSize;
29+
public final long maximumDirectBlockSize;
30+
public final int maximumHeapSize;
31+
public final int startingRowsInRootIndirectBlock;
32+
public final long addressOfRootBlock;
33+
public final int currentRowsInRootIndirectBlock;
34+
public final Long sizeOfFilteredRootDirectBlock;
35+
public final Integer ioFilterMask;
36+
public final byte[] ioFilterInformation;
37+
public final int checksum;
38+
39+
// Main constructor for reading
40+
private FractalHeapHeader(ByteBuffer bb, int sizeOfOffsets, int sizeOfLengths) throws IOException {
41+
bb.order(ByteOrder.LITTLE_ENDIAN);
42+
43+
this.signature = Hdf5Utils.readSignature(bb, "FRHP");
44+
this.version = bb.get();
45+
this.heapIdLength = bb.getShort() & 0xFFFF;
46+
this.ioFiltersEncodedLength = bb.getShort() & 0xFFFF;
47+
this.flags = bb.get();
48+
this.maximumSizeOfManagedObjects = bb.getInt() & 0xFFFFFFFFL;
49+
this.nextHugeObjectId = Hdf5Utils.readLength(bb, sizeOfLengths);
50+
this.v2BtreeAddressOfHugeObjects = Hdf5Utils.readOffset(bb, sizeOfOffsets);
51+
this.amountOfFreeSpaceInManagedBlocks = Hdf5Utils.readLength(bb, sizeOfLengths);
52+
this.addressOfManagedBlockFreeSpaceManager = Hdf5Utils.readOffset(bb, sizeOfOffsets);
53+
this.amountOfManagedSpaceInHeap = Hdf5Utils.readLength(bb, sizeOfLengths);
54+
this.amountOfAllocatedManagedSpaceInHeap = Hdf5Utils.readLength(bb, sizeOfLengths);
55+
this.offsetOfDirectBlockAllocationIterator = Hdf5Utils.readLength(bb, sizeOfLengths);
56+
this.numberOfManagedObjectsInHeap = Hdf5Utils.readLength(bb, sizeOfLengths);
57+
this.sizeOfHugeObjectsInHeap = Hdf5Utils.readLength(bb, sizeOfLengths);
58+
this.numberOfHugeObjectsInHeap = Hdf5Utils.readLength(bb, sizeOfLengths);
59+
this.sizeOfTinyObjectsInHeap = Hdf5Utils.readLength(bb, sizeOfLengths);
60+
this.numberOfTinyObjectsInHeap = Hdf5Utils.readLength(bb, sizeOfLengths);
61+
this.tableWidth = bb.getShort() & 0xFFFF;
62+
this.startingBlockSize = Hdf5Utils.readLength(bb, sizeOfLengths);
63+
this.maximumDirectBlockSize = Hdf5Utils.readLength(bb, sizeOfLengths);
64+
this.maximumHeapSize = bb.getShort() & 0xFFFF;
65+
this.startingRowsInRootIndirectBlock = bb.getShort() & 0xFFFF;
66+
this.addressOfRootBlock = Hdf5Utils.readOffset(bb, sizeOfOffsets);
67+
this.currentRowsInRootIndirectBlock = bb.getShort() & 0xFFFF;
68+
69+
if (this.ioFiltersEncodedLength > 0) {
70+
this.sizeOfFilteredRootDirectBlock = Hdf5Utils.readLength(bb, sizeOfLengths);
71+
this.ioFilterMask = bb.getInt();
72+
this.ioFilterInformation = new byte[this.ioFiltersEncodedLength];
73+
bb.get(this.ioFilterInformation);
74+
} else {
75+
this.sizeOfFilteredRootDirectBlock = null;
76+
this.ioFilterMask = null;
77+
this.ioFilterInformation = null;
78+
}
79+
this.checksum = Hdf5Utils.readChecksum(bb);
80+
}
81+
82+
// Constructor for mocking
83+
FractalHeapHeader(String sig, short v, int hIdLen, int ioLen, short fl, long maxObj, long nxtHuge, long btree, long free, long freeMgr, long manSpace, long allocSpace, long iter, long nMan, long szHuge, long nHuge, long szTiny, long nTiny, int tW, long startBlk, long maxBlk, int maxHp, int startRow, long rootAddr, int currRow, Long filtSz, Integer filtMsk, byte[] filtInfo, int cs) {
84+
signature = sig; version = v; heapIdLength = hIdLen; ioFiltersEncodedLength = ioLen; flags = fl; maximumSizeOfManagedObjects = maxObj; nextHugeObjectId = nxtHuge; v2BtreeAddressOfHugeObjects = btree; amountOfFreeSpaceInManagedBlocks = free; addressOfManagedBlockFreeSpaceManager = freeMgr; amountOfManagedSpaceInHeap = manSpace; amountOfAllocatedManagedSpaceInHeap = allocSpace; offsetOfDirectBlockAllocationIterator = iter; numberOfManagedObjectsInHeap = nMan; sizeOfHugeObjectsInHeap = szHuge; numberOfHugeObjectsInHeap = nHuge; sizeOfTinyObjectsInHeap = szTiny; numberOfTinyObjectsInHeap = nTiny; tableWidth = tW; startingBlockSize = startBlk; maximumDirectBlockSize = maxBlk; maximumHeapSize = maxHp; startingRowsInRootIndirectBlock = startRow; addressOfRootBlock = rootAddr; currentRowsInRootIndirectBlock = currRow; sizeOfFilteredRootDirectBlock = filtSz; ioFilterMask = filtMsk; ioFilterInformation = filtInfo; checksum = cs;
85+
}
86+
87+
public static FractalHeapHeader read(SeekableByteChannel channel, int sizeOfOffsets, int sizeOfLengths) throws IOException {
88+
long originalPos = channel.position();
89+
channel.position(originalPos + 4 + 1 + 2); // sig + ver + heapIdLen
90+
ByteBuffer filterLenBuf = ByteBuffer.allocate(2).order(ByteOrder.LITTLE_ENDIAN);
91+
channel.read(filterLenBuf);
92+
filterLenBuf.flip();
93+
int ioFilterLength = filterLenBuf.getShort() & 0xFFFF;
94+
channel.position(originalPos);
95+
96+
int headerSize = 101 + (11 * sizeOfLengths) + (3 * sizeOfOffsets) - 48; // Base size for 8/8 offsets/lengths
97+
if (ioFilterLength > 0) {
98+
headerSize += sizeOfLengths + 4 + ioFilterLength;
99+
}
100+
101+
ByteBuffer headerBuffer = ByteBuffer.allocate(headerSize);
102+
Hdf5Utils.readBytes(channel, headerBuffer);
103+
return new FractalHeapHeader(headerBuffer, sizeOfOffsets, sizeOfLengths);
104+
}
105+
106+
// public byte[] toByteArray() {
107+
// // Simplified for mocking. A real implementation would be more robust.
108+
// ByteBuffer bb = ByteBuffer.allocate(256).order(ByteOrder.LITTLE_ENDIAN);
109+
// bb.put(signature.getBytes()).put(version).putShort((short)heapIdLength).putShort((short)ioFiltersEncodedLength).put(flags).putInt((int)maximumSizeOfManagedObjects);
110+
// bb.putLong(nextHugeObjectId).putLong(v2BtreeAddressOfHugeObjects).putLong(amountOfFreeSpaceInManagedBlocks).putLong(addressOfManagedBlockFreeSpaceManager).putLong(amountOfManagedSpaceInHeap).putLong(amountOfAllocatedManagedSpaceInHeap).putLong(offsetOfDirectBlockAllocationIterator).putLong(numberOfManagedObjectsInHeap).putLong(sizeOfHugeObjectsInHeap).putLong(numberOfHugeObjectsInHeap).putLong(sizeOfTinyObjectsInHeap).putLong(numberOfTinyObjectsInHeap);
111+
// bb.putShort((short)tableWidth).putLong(startingBlockSize).putLong(maximumDirectBlockSize).putShort((short)maximumHeapSize).putShort((short)startingRowsInRootIndirectBlock).putLong(addressOfRootBlock).putShort((short)currentRowsInRootIndirectBlock);
112+
// if (ioFiltersEncodedLength > 0) { /* Omitted for mock */ }
113+
// bb.putInt(checksum);
114+
// byte[] result = new byte[bb.position()];
115+
// bb.flip();
116+
// bb.get(result);
117+
// return result;
118+
// }
119+
}

0 commit comments

Comments
 (0)