diff --git a/src/main/java/li/cil/oc2/common/bus/device/MemoryDevice.java b/src/main/java/li/cil/oc2/common/bus/device/MemoryDevice.java new file mode 100644 index 00000000..70a00748 --- /dev/null +++ b/src/main/java/li/cil/oc2/common/bus/device/MemoryDevice.java @@ -0,0 +1,153 @@ +package li.cil.oc2.common.bus.device; + +import li.cil.oc2.Constants; +import li.cil.oc2.api.bus.device.vm.*; +import li.cil.oc2.common.bus.device.provider.util.AbstractObjectProxy; +import li.cil.oc2.common.serialization.BlobStorage; +import li.cil.oc2.common.util.NBTTagIds; +import li.cil.sedna.api.device.PhysicalMemory; +import li.cil.sedna.device.memory.Memory; +import li.cil.sedna.memory.PhysicalMemoryInputStream; +import li.cil.sedna.memory.PhysicalMemoryOutputStream; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.CompoundNBT; + +import java.util.Optional; +import java.util.OptionalLong; +import java.util.UUID; + +public final class MemoryDevice extends AbstractObjectProxy implements VMDevice, VMDeviceLifecycleListener { + private static final UUID SERIALIZATION_KEY = UUID.fromString("c82f8c1c-d7ff-43b0-ab44-989e1fe818bb"); + + private static final String BLOB_HANDLE_NBT_TAG_NAME = "blob"; + private static final String ADDRESS_NBT_TAG_NAME = "address"; + + final int RAM_SIZE = 8 * Constants.MEGABYTE; + + /////////////////////////////////////////////////////////////// + + private BlobStorage.JobHandle jobHandle; + private PhysicalMemory device; + + /////////////////////////////////////////////////////////////// + + private UUID blobHandle; + private Long address; + + /////////////////////////////////////////////////////////////// + + public MemoryDevice(final ItemStack value) { + super(value); + } + + @Override + public VMDeviceLoadResult load(final VMContext context) { + if (!allocateDevice(context)) { + return VMDeviceLoadResult.fail(); + } + + if (!claimAddress(context)) { + return VMDeviceLoadResult.fail(); + } + + loadPersistedState(); + + return VMDeviceLoadResult.success(); + } + + @Override + public void handleLifecycleEvent(final VMDeviceLifecycleEventType event) { + switch (event) { + case RESUME_RUNNING: + awaitStorageOperation(); + break; + case UNLOAD: + unload(); + break; + } + } + + @Override + public Optional getSerializationKey() { + return Optional.of(SERIALIZATION_KEY); + } + + @Override + public CompoundNBT serializeNBT() { + final CompoundNBT nbt = new CompoundNBT(); + + if (device != null) { + blobHandle = BlobStorage.validateHandle(blobHandle); + nbt.putUniqueId(BLOB_HANDLE_NBT_TAG_NAME, blobHandle); + + jobHandle = BlobStorage.submitSave(blobHandle, new PhysicalMemoryInputStream(device)); + } + if (address != null) { + nbt.putLong(ADDRESS_NBT_TAG_NAME, address); + } + + return nbt; + } + + @Override + public void deserializeNBT(final CompoundNBT nbt) { + if (nbt.hasUniqueId(BLOB_HANDLE_NBT_TAG_NAME)) { + blobHandle = nbt.getUniqueId(BLOB_HANDLE_NBT_TAG_NAME); + } + if (nbt.contains(ADDRESS_NBT_TAG_NAME, NBTTagIds.TAG_LONG)) { + address = nbt.getLong(ADDRESS_NBT_TAG_NAME); + } + } + + /////////////////////////////////////////////////////////////// + + private boolean allocateDevice(final VMContext context) { + if (!context.getMemoryAllocator().claimMemory(RAM_SIZE)) { + return false; + } + + device = Memory.create(RAM_SIZE); + + return true; + } + + private boolean claimAddress(final VMContext context) { + final OptionalLong claimedAddress; + if (this.address != null) { + claimedAddress = context.getMemoryRangeAllocator().claimMemoryRange(this.address, device); + } else { + claimedAddress = context.getMemoryRangeAllocator().claimMemoryRange(device); + } + + if (!claimedAddress.isPresent()) { + return false; + } + + this.address = claimedAddress.getAsLong(); + + return true; + } + + private void loadPersistedState() { + if (blobHandle != null) { + jobHandle = BlobStorage.submitLoad(blobHandle, new PhysicalMemoryOutputStream(device)); + } + } + + private void awaitStorageOperation() { + if (jobHandle != null) { + jobHandle.await(); + jobHandle = null; + } + } + + private void unload() { + // RAM is volatile, so free up our persisted blob when device is unloaded. + BlobStorage.freeHandle(blobHandle); + blobHandle = null; + + device = null; + address = null; + jobHandle = null; + } +} diff --git a/src/main/java/li/cil/oc2/common/bus/device/provider/MemoryItemDeviceProvider.java b/src/main/java/li/cil/oc2/common/bus/device/provider/MemoryItemDeviceProvider.java index d454faf9..17906a95 100644 --- a/src/main/java/li/cil/oc2/common/bus/device/provider/MemoryItemDeviceProvider.java +++ b/src/main/java/li/cil/oc2/common/bus/device/provider/MemoryItemDeviceProvider.java @@ -1,26 +1,12 @@ package li.cil.oc2.common.bus.device.provider; -import li.cil.oc2.Constants; import li.cil.oc2.api.bus.device.Device; import li.cil.oc2.api.bus.device.provider.ItemDeviceQuery; -import li.cil.oc2.api.bus.device.vm.*; +import li.cil.oc2.common.bus.device.MemoryDevice; import li.cil.oc2.common.bus.device.provider.util.AbstractItemDeviceProvider; -import li.cil.oc2.common.bus.device.provider.util.AbstractObjectProxy; import li.cil.oc2.common.init.Items; -import li.cil.oc2.common.serialization.BlobStorage; -import li.cil.oc2.common.util.NBTTagIds; -import li.cil.sedna.api.device.PhysicalMemory; -import li.cil.sedna.device.memory.Memory; -import li.cil.sedna.memory.PhysicalMemoryInputStream; -import li.cil.sedna.memory.PhysicalMemoryOutputStream; -import net.minecraft.item.ItemStack; -import net.minecraft.nbt.CompoundNBT; import net.minecraftforge.common.util.LazyOptional; -import java.util.Optional; -import java.util.OptionalLong; -import java.util.UUID; - public final class MemoryItemDeviceProvider extends AbstractItemDeviceProvider { public MemoryItemDeviceProvider() { super(Items.RAM_8M_ITEM.get()); @@ -35,139 +21,4 @@ public final class MemoryItemDeviceProvider extends AbstractItemDeviceProvider { /////////////////////////////////////////////////////////////////// - private static final class MemoryDevice extends AbstractObjectProxy implements VMDevice, VMDeviceLifecycleListener { - private static final UUID SERIALIZATION_KEY = UUID.fromString("c82f8c1c-d7ff-43b0-ab44-989e1fe818bb"); - - private static final String BLOB_HANDLE_NBT_TAG_NAME = "blob"; - private static final String ADDRESS_NBT_TAG_NAME = "address"; - - final int RAM_SIZE = 8 * Constants.MEGABYTE; - - /////////////////////////////////////////////////////////////// - - private BlobStorage.JobHandle jobHandle; - private PhysicalMemory device; - - /////////////////////////////////////////////////////////////// - - private UUID blobHandle; - private Long address; - - /////////////////////////////////////////////////////////////// - - public MemoryDevice(final ItemStack value) { - super(value); - } - - @Override - public VMDeviceLoadResult load(final VMContext context) { - if (!allocateDevice(context)) { - return VMDeviceLoadResult.fail(); - } - - if (!claimAddress(context)) { - return VMDeviceLoadResult.fail(); - } - - loadPersistedState(); - - return VMDeviceLoadResult.success(); - } - - @Override - public void handleLifecycleEvent(final VMDeviceLifecycleEventType event) { - switch (event) { - case RESUME_RUNNING: - awaitStorageOperation(); - break; - case UNLOAD: - unload(); - break; - } - } - - @Override - public Optional getSerializationKey() { - return Optional.of(SERIALIZATION_KEY); - } - - @Override - public CompoundNBT serializeNBT() { - final CompoundNBT nbt = new CompoundNBT(); - - if (device != null) { - blobHandle = BlobStorage.validateHandle(blobHandle); - nbt.putUniqueId(BLOB_HANDLE_NBT_TAG_NAME, blobHandle); - - jobHandle = BlobStorage.submitSave(blobHandle, new PhysicalMemoryInputStream(device)); - } - if (address != null) { - nbt.putLong(ADDRESS_NBT_TAG_NAME, address); - } - - return nbt; - } - - @Override - public void deserializeNBT(final CompoundNBT nbt) { - if (nbt.hasUniqueId(BLOB_HANDLE_NBT_TAG_NAME)) { - blobHandle = nbt.getUniqueId(BLOB_HANDLE_NBT_TAG_NAME); - } - if (nbt.contains(ADDRESS_NBT_TAG_NAME, NBTTagIds.TAG_LONG)) { - address = nbt.getLong(ADDRESS_NBT_TAG_NAME); - } - } - - /////////////////////////////////////////////////////////////// - - private boolean allocateDevice(final VMContext context) { - if (!context.getMemoryAllocator().claimMemory(RAM_SIZE)) { - return false; - } - - device = Memory.create(RAM_SIZE); - - return true; - } - - private boolean claimAddress(final VMContext context) { - final OptionalLong claimedAddress; - if (this.address != null) { - claimedAddress = context.getMemoryRangeAllocator().claimMemoryRange(this.address, device); - } else { - claimedAddress = context.getMemoryRangeAllocator().claimMemoryRange(device); - } - - if (!claimedAddress.isPresent()) { - return false; - } - - this.address = claimedAddress.getAsLong(); - - return true; - } - - private void loadPersistedState() { - if (blobHandle != null) { - jobHandle = BlobStorage.submitLoad(blobHandle, new PhysicalMemoryOutputStream(device)); - } - } - - private void awaitStorageOperation() { - if (jobHandle != null) { - jobHandle.await(); - jobHandle = null; - } - } - - private void unload() { - // RAM is volatile, so free up our persisted blob when device is unloaded. - BlobStorage.freeHandle(blobHandle); - blobHandle = null; - - device = null; - address = null; - jobHandle = null; - } - } }