WIP network switch statistics
This commit is contained in:
parent
b64a05e0ad
commit
4effe2f1b4
|
@ -55,6 +55,10 @@ object RenderHelper: DrawableHelper() {
|
||||||
if (disabled) return
|
if (disabled) return
|
||||||
|
|
||||||
RenderSystem.lineWidth(width)
|
RenderSystem.lineWidth(width)
|
||||||
|
RenderSystem.enableBlend()
|
||||||
|
RenderSystem.disableTexture()
|
||||||
|
RenderSystem.defaultBlendFunc()
|
||||||
|
RenderSystem.setShader(GameRenderer::getPositionColorShader)
|
||||||
val tessellator = Tessellator.getInstance()
|
val tessellator = Tessellator.getInstance()
|
||||||
val buffer = tessellator.buffer
|
val buffer = tessellator.buffer
|
||||||
buffer.begin(VertexFormat.DrawMode.LINES, VertexFormats.POSITION_COLOR)
|
buffer.begin(VertexFormat.DrawMode.LINES, VertexFormats.POSITION_COLOR)
|
||||||
|
|
|
@ -7,6 +7,7 @@ import net.fabricmc.fabric.api.client.screenhandler.v1.ScreenRegistry
|
||||||
import net.fabricmc.fabric.api.renderer.v1.RendererAccess
|
import net.fabricmc.fabric.api.renderer.v1.RendererAccess
|
||||||
import net.fabricmc.fabric.api.renderer.v1.material.RenderMaterial
|
import net.fabricmc.fabric.api.renderer.v1.material.RenderMaterial
|
||||||
import net.shadowfacts.phycon.block.inserter.InserterScreen
|
import net.shadowfacts.phycon.block.inserter.InserterScreen
|
||||||
|
import net.shadowfacts.phycon.block.netswitch.SwitchConsoleScreen
|
||||||
import net.shadowfacts.phycon.block.redstone_emitter.RedstoneEmitterScreen
|
import net.shadowfacts.phycon.block.redstone_emitter.RedstoneEmitterScreen
|
||||||
import net.shadowfacts.phycon.block.terminal.CraftingTerminalScreen
|
import net.shadowfacts.phycon.block.terminal.CraftingTerminalScreen
|
||||||
import net.shadowfacts.phycon.init.PhyScreens
|
import net.shadowfacts.phycon.init.PhyScreens
|
||||||
|
@ -44,6 +45,7 @@ object PhysicalConnectivityClient: ClientModInitializer {
|
||||||
ScreenRegistry.register(PhyScreens.CRAFTING_TERMINAL, ::CraftingTerminalScreen)
|
ScreenRegistry.register(PhyScreens.CRAFTING_TERMINAL, ::CraftingTerminalScreen)
|
||||||
ScreenRegistry.register(PhyScreens.INSERTER, ::InserterScreen)
|
ScreenRegistry.register(PhyScreens.INSERTER, ::InserterScreen)
|
||||||
ScreenRegistry.register(PhyScreens.REDSTONE_EMITTER, ::RedstoneEmitterScreen)
|
ScreenRegistry.register(PhyScreens.REDSTONE_EMITTER, ::RedstoneEmitterScreen)
|
||||||
|
ScreenRegistry.register(PhyScreens.SWITCH_CONSOLE, ::SwitchConsoleScreen)
|
||||||
|
|
||||||
registerGlobalReceiver(S2CTerminalUpdateDisplayedItems)
|
registerGlobalReceiver(S2CTerminalUpdateDisplayedItems)
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,6 +20,7 @@ import net.shadowfacts.phycon.frame.BasePacketFrame
|
||||||
import net.shadowfacts.phycon.frame.NetworkSplitFrame
|
import net.shadowfacts.phycon.frame.NetworkSplitFrame
|
||||||
import net.shadowfacts.phycon.init.PhyBlockEntities
|
import net.shadowfacts.phycon.init.PhyBlockEntities
|
||||||
import net.shadowfacts.phycon.packet.ItemStackPacket
|
import net.shadowfacts.phycon.packet.ItemStackPacket
|
||||||
|
import net.shadowfacts.phycon.util.IntRingBuffer
|
||||||
import net.shadowfacts.phycon.util.NetworkUtil
|
import net.shadowfacts.phycon.util.NetworkUtil
|
||||||
import java.lang.ref.WeakReference
|
import java.lang.ref.WeakReference
|
||||||
import java.util.Deque
|
import java.util.Deque
|
||||||
|
@ -38,9 +39,13 @@ class SwitchBlockEntity(pos: BlockPos, state: BlockState): BlockEntity(PhyBlockE
|
||||||
|
|
||||||
private val macTable = mutableMapOf<MACAddress, Direction>()
|
private val macTable = mutableMapOf<MACAddress, Direction>()
|
||||||
private val destinationCache = Array<WeakReference<Interface>?>(6) { null }
|
private val destinationCache = Array<WeakReference<Interface>?>(6) { null }
|
||||||
|
val packetStatistics = IntRingBuffer(60) // 1 minute's worth
|
||||||
|
private val currentSecondPacketStatistics = IntRingBuffer(20)
|
||||||
private var packetsHandledThisTick = 0
|
private var packetsHandledThisTick = 0
|
||||||
private var delayedPackets: Deque<Pair<PacketFrame, SwitchInterface>> = LinkedList()
|
private var delayedPackets: Deque<Pair<PacketFrame, SwitchInterface>> = LinkedList()
|
||||||
|
|
||||||
|
var statisticsObserver: (() -> Unit)? = null
|
||||||
|
|
||||||
fun interfaceForSide(side: Direction): SwitchInterface {
|
fun interfaceForSide(side: Direction): SwitchInterface {
|
||||||
return interfaces.find { it.side == side }!!
|
return interfaces.find { it.side == side }!!
|
||||||
}
|
}
|
||||||
|
@ -98,6 +103,16 @@ class SwitchBlockEntity(pos: BlockPos, state: BlockState): BlockEntity(PhyBlockE
|
||||||
}
|
}
|
||||||
|
|
||||||
fun tick() {
|
fun tick() {
|
||||||
|
if (statisticsObserver != null) {
|
||||||
|
if (currentSecondPacketStatistics.size == 20) {
|
||||||
|
packetStatistics.add(currentSecondPacketStatistics.sum())
|
||||||
|
currentSecondPacketStatistics.clear()
|
||||||
|
statisticsObserver?.invoke()
|
||||||
|
} else {
|
||||||
|
currentSecondPacketStatistics.add(packetsHandledThisTick)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
packetsHandledThisTick = 0
|
packetsHandledThisTick = 0
|
||||||
|
|
||||||
while (delayedPackets.isNotEmpty() && packetsHandledThisTick <= SWITCHING_CAPACITY) {
|
while (delayedPackets.isNotEmpty() && packetsHandledThisTick <= SWITCHING_CAPACITY) {
|
||||||
|
@ -147,6 +162,11 @@ class SwitchBlockEntity(pos: BlockPos, state: BlockState): BlockEntity(PhyBlockE
|
||||||
delayedPackets.addLast(frame to fromItf)
|
delayedPackets.addLast(frame to fromItf)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
tag.getIntArray("PacketStatistics")?.also { statistics ->
|
||||||
|
if (statistics.isNotEmpty()) {
|
||||||
|
packetStatistics.replace(statistics)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun toUpdatePacket(): Packet<ClientPlayPacketListener>? {
|
override fun toUpdatePacket(): Packet<ClientPlayPacketListener>? {
|
||||||
|
@ -156,6 +176,7 @@ class SwitchBlockEntity(pos: BlockPos, state: BlockState): BlockEntity(PhyBlockE
|
||||||
override fun toInitialChunkDataNbt(): NbtCompound {
|
override fun toInitialChunkDataNbt(): NbtCompound {
|
||||||
val tag = NbtCompound()
|
val tag = NbtCompound()
|
||||||
tag.putLongArray("InterfaceAddresses", interfaces.map { it.macAddress.address })
|
tag.putLongArray("InterfaceAddresses", interfaces.map { it.macAddress.address })
|
||||||
|
tag.putIntArray("PacketStatistics", packetStatistics.asContiguousArray())
|
||||||
return tag
|
return tag
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,94 @@
|
||||||
|
package net.shadowfacts.phycon.block.netswitch
|
||||||
|
|
||||||
|
import net.minecraft.client.util.math.MatrixStack
|
||||||
|
import net.minecraft.entity.player.PlayerInventory
|
||||||
|
import net.minecraft.text.LiteralText
|
||||||
|
import net.minecraft.text.Text
|
||||||
|
import net.shadowfacts.cacao.CacaoHandledScreen
|
||||||
|
import net.shadowfacts.cacao.geometry.Point
|
||||||
|
import net.shadowfacts.cacao.geometry.Rect
|
||||||
|
import net.shadowfacts.cacao.geometry.Size
|
||||||
|
import net.shadowfacts.cacao.util.Color
|
||||||
|
import net.shadowfacts.cacao.util.RenderHelper
|
||||||
|
import net.shadowfacts.cacao.view.Label
|
||||||
|
import net.shadowfacts.cacao.view.View
|
||||||
|
import net.shadowfacts.cacao.viewcontroller.ViewController
|
||||||
|
import net.shadowfacts.cacao.window.ScreenHandlerWindow
|
||||||
|
import net.shadowfacts.kiwidsl.dsl
|
||||||
|
import org.lwjgl.glfw.GLFW
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author shadowfacts
|
||||||
|
*/
|
||||||
|
class SwitchConsoleScreen(
|
||||||
|
handler: SwitchConsoleScreenHandler,
|
||||||
|
playerInventory: PlayerInventory,
|
||||||
|
title: Text,
|
||||||
|
): CacaoHandledScreen<SwitchConsoleScreenHandler>(
|
||||||
|
handler,
|
||||||
|
playerInventory,
|
||||||
|
title,
|
||||||
|
) {
|
||||||
|
|
||||||
|
val root = SwitchConsoleViewController(handler.switch)
|
||||||
|
|
||||||
|
init {
|
||||||
|
addWindow(ScreenHandlerWindow(handler, root))
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun shouldPause() = false
|
||||||
|
|
||||||
|
override fun keyPressed(keyCode: Int, scanCode: Int, modifiers: Int): Boolean {
|
||||||
|
if (keyCode == GLFW.GLFW_KEY_E) {
|
||||||
|
close()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return super.keyPressed(keyCode, scanCode, modifiers)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
class SwitchConsoleViewController(val switch: SwitchBlockEntity): ViewController() {
|
||||||
|
override fun viewDidLoad() {
|
||||||
|
super.viewDidLoad()
|
||||||
|
|
||||||
|
val stats = SwitchPacketStatisticsView(switch)
|
||||||
|
view.addSubview(stats)
|
||||||
|
view.solver.dsl {
|
||||||
|
stats.centerXAnchor equalTo (view.centerXAnchor + 50)
|
||||||
|
stats.centerYAnchor equalTo (view.centerYAnchor + 50)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class SwitchPacketStatisticsView(val switch: SwitchBlockEntity): View() {
|
||||||
|
|
||||||
|
init {
|
||||||
|
intrinsicContentSize = Size(180.0, 90.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun drawContent(matrixStack: MatrixStack, mouse: Point, delta: Float) {
|
||||||
|
RenderHelper.fill(matrixStack, bounds, Color.BLACK)
|
||||||
|
if (switch.packetStatistics.size == 0) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// TODO: drawLine isn't working for some reason
|
||||||
|
RenderHelper.drawLine(Point(bounds.left, bounds.top), Point(bounds.right, bounds.bottom), 1.0, 2f, Color.MAGENTA)
|
||||||
|
return
|
||||||
|
val maxPackets = switch.packetStatistics.maxOf { it }
|
||||||
|
val maxDataPointsCount = 60
|
||||||
|
var lastPoint: Point? = null
|
||||||
|
val size = Size(3.0, 3.0)
|
||||||
|
for ((index, packets) in switch.packetStatistics.withIndex()) {
|
||||||
|
val x = (1 - (switch.packetStatistics.size - index).toDouble() / maxDataPointsCount) * bounds.width
|
||||||
|
val y = (1 - (packets.toDouble() / maxPackets)) * (bounds.height)
|
||||||
|
val point = Point(x, y)
|
||||||
|
if (lastPoint != null) {
|
||||||
|
// RenderHelper.fill(matrixStack, Rect(lastPoint, 3.0, 3.0), Color.RED)
|
||||||
|
RenderHelper.drawLine(lastPoint, point, 1.0, 2f, Color.RED)
|
||||||
|
}
|
||||||
|
lastPoint = point
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,46 @@
|
||||||
|
package net.shadowfacts.phycon.block.netswitch
|
||||||
|
|
||||||
|
import net.minecraft.entity.player.PlayerEntity
|
||||||
|
import net.minecraft.entity.player.PlayerInventory
|
||||||
|
import net.minecraft.network.PacketByteBuf
|
||||||
|
import net.minecraft.screen.ScreenHandler
|
||||||
|
import net.minecraft.util.Identifier
|
||||||
|
import net.shadowfacts.phycon.PhysicalConnectivity
|
||||||
|
import net.shadowfacts.phycon.init.PhyBlocks
|
||||||
|
import net.shadowfacts.phycon.init.PhyScreens
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author shadowfacts
|
||||||
|
*/
|
||||||
|
class SwitchConsoleScreenHandler(
|
||||||
|
syncId: Int,
|
||||||
|
val switch: SwitchBlockEntity,
|
||||||
|
): ScreenHandler(PhyScreens.SWITCH_CONSOLE, syncId) {
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
val ID = Identifier(PhysicalConnectivity.MODID, "switch_console")
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(syncId: Int, playerInv: PlayerInventory, buf: PacketByteBuf):
|
||||||
|
this(
|
||||||
|
syncId,
|
||||||
|
PhyBlocks.SWITCH.getBlockEntity(playerInv.player.world, buf.readBlockPos())!!
|
||||||
|
)
|
||||||
|
|
||||||
|
init {
|
||||||
|
switch.statisticsObserver = {
|
||||||
|
switch.world!!.updateListeners(switch.pos, switch.cachedState, switch.cachedState, 3)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun canUse(player: PlayerEntity): Boolean {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun close(player: PlayerEntity) {
|
||||||
|
super.close(player)
|
||||||
|
|
||||||
|
switch.statisticsObserver = null
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -1,8 +1,14 @@
|
||||||
package net.shadowfacts.phycon.init
|
package net.shadowfacts.phycon.init
|
||||||
|
|
||||||
|
import net.fabricmc.fabric.api.screenhandler.v1.ExtendedScreenHandlerType
|
||||||
|
import net.fabricmc.fabric.api.screenhandler.v1.ExtendedScreenHandlerType.ExtendedFactory
|
||||||
import net.fabricmc.fabric.api.screenhandler.v1.ScreenHandlerRegistry
|
import net.fabricmc.fabric.api.screenhandler.v1.ScreenHandlerRegistry
|
||||||
|
import net.minecraft.screen.ScreenHandler
|
||||||
import net.minecraft.screen.ScreenHandlerType
|
import net.minecraft.screen.ScreenHandlerType
|
||||||
|
import net.minecraft.util.Identifier
|
||||||
|
import net.minecraft.util.registry.Registry
|
||||||
import net.shadowfacts.phycon.block.inserter.InserterScreenHandler
|
import net.shadowfacts.phycon.block.inserter.InserterScreenHandler
|
||||||
|
import net.shadowfacts.phycon.block.netswitch.SwitchConsoleScreenHandler
|
||||||
import net.shadowfacts.phycon.block.redstone_emitter.RedstoneEmitterScreenHandler
|
import net.shadowfacts.phycon.block.redstone_emitter.RedstoneEmitterScreenHandler
|
||||||
import net.shadowfacts.phycon.block.terminal.CraftingTerminalBlock
|
import net.shadowfacts.phycon.block.terminal.CraftingTerminalBlock
|
||||||
import net.shadowfacts.phycon.block.terminal.CraftingTerminalScreenHandler
|
import net.shadowfacts.phycon.block.terminal.CraftingTerminalScreenHandler
|
||||||
|
@ -19,12 +25,19 @@ object PhyScreens {
|
||||||
private set
|
private set
|
||||||
lateinit var REDSTONE_EMITTER: ScreenHandlerType<RedstoneEmitterScreenHandler>
|
lateinit var REDSTONE_EMITTER: ScreenHandlerType<RedstoneEmitterScreenHandler>
|
||||||
private set
|
private set
|
||||||
|
lateinit var SWITCH_CONSOLE: ScreenHandlerType<SwitchConsoleScreenHandler>
|
||||||
|
private set
|
||||||
|
|
||||||
fun init() {
|
fun init() {
|
||||||
TERMINAL = ScreenHandlerRegistry.registerExtended(TerminalBlock.ID, ::TerminalScreenHandler)
|
TERMINAL = register(TerminalBlock.ID, ::TerminalScreenHandler)
|
||||||
CRAFTING_TERMINAL = ScreenHandlerRegistry.registerExtended(CraftingTerminalBlock.ID, ::CraftingTerminalScreenHandler)
|
CRAFTING_TERMINAL = register(CraftingTerminalBlock.ID, ::CraftingTerminalScreenHandler)
|
||||||
INSERTER = ScreenHandlerRegistry.registerExtended(InserterScreenHandler.ID, ::InserterScreenHandler)
|
INSERTER = register(InserterScreenHandler.ID, ::InserterScreenHandler)
|
||||||
REDSTONE_EMITTER = ScreenHandlerRegistry.registerExtended(RedstoneEmitterScreenHandler.ID, ::RedstoneEmitterScreenHandler)
|
REDSTONE_EMITTER = register(RedstoneEmitterScreenHandler.ID, ::RedstoneEmitterScreenHandler)
|
||||||
|
SWITCH_CONSOLE = register(SwitchConsoleScreenHandler.ID, ::SwitchConsoleScreenHandler)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun <T: ScreenHandler> register(id: Identifier, factory: ExtendedFactory<T>): ScreenHandlerType<T> {
|
||||||
|
return Registry.register(Registry.SCREEN_HANDLER, id, ExtendedScreenHandlerType(factory))
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,14 +1,25 @@
|
||||||
package net.shadowfacts.phycon.item
|
package net.shadowfacts.phycon.item
|
||||||
|
|
||||||
|
import net.fabricmc.fabric.api.screenhandler.v1.ExtendedScreenHandlerFactory
|
||||||
import net.minecraft.client.MinecraftClient
|
import net.minecraft.client.MinecraftClient
|
||||||
|
import net.minecraft.entity.player.PlayerEntity
|
||||||
|
import net.minecraft.entity.player.PlayerInventory
|
||||||
import net.minecraft.item.Item
|
import net.minecraft.item.Item
|
||||||
import net.minecraft.item.ItemUsageContext
|
import net.minecraft.item.ItemUsageContext
|
||||||
|
import net.minecraft.network.PacketByteBuf
|
||||||
|
import net.minecraft.screen.ScreenHandler
|
||||||
|
import net.minecraft.server.network.ServerPlayerEntity
|
||||||
|
import net.minecraft.text.Text
|
||||||
import net.minecraft.util.ActionResult
|
import net.minecraft.util.ActionResult
|
||||||
import net.minecraft.util.Identifier
|
import net.minecraft.util.Identifier
|
||||||
import net.shadowfacts.phycon.PhysicalConnectivity
|
import net.shadowfacts.phycon.PhysicalConnectivity
|
||||||
import net.shadowfacts.phycon.block.DeviceBlock
|
import net.shadowfacts.phycon.block.DeviceBlock
|
||||||
import net.shadowfacts.phycon.block.DeviceBlockEntity
|
import net.shadowfacts.phycon.block.DeviceBlockEntity
|
||||||
|
import net.shadowfacts.phycon.block.netswitch.SwitchBlockEntity
|
||||||
import net.shadowfacts.phycon.client.screen.console.DeviceConsoleScreen
|
import net.shadowfacts.phycon.client.screen.console.DeviceConsoleScreen
|
||||||
|
import net.shadowfacts.phycon.block.netswitch.SwitchConsoleScreen
|
||||||
|
import net.shadowfacts.phycon.block.netswitch.SwitchConsoleScreenHandler
|
||||||
|
import net.shadowfacts.phycon.init.PhyBlocks
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author shadowfacts
|
* @author shadowfacts
|
||||||
|
@ -31,6 +42,14 @@ class ConsoleItem: Item(Settings().maxCount(1)) {
|
||||||
}
|
}
|
||||||
return ActionResult.SUCCESS
|
return ActionResult.SUCCESS
|
||||||
}
|
}
|
||||||
|
} else if (block === PhyBlocks.SWITCH) {
|
||||||
|
val be = block.getBlockEntity(context.world, context.blockPos)
|
||||||
|
if (be != null) {
|
||||||
|
if (!context.world.isClient && context.player != null) {
|
||||||
|
openScreen(be, context.player!!)
|
||||||
|
}
|
||||||
|
return ActionResult.SUCCESS
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return ActionResult.PASS
|
return ActionResult.PASS
|
||||||
}
|
}
|
||||||
|
@ -41,4 +60,19 @@ class ConsoleItem: Item(Settings().maxCount(1)) {
|
||||||
MinecraftClient.getInstance().setScreen(screen)
|
MinecraftClient.getInstance().setScreen(screen)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun openScreen(be: SwitchBlockEntity, player: PlayerEntity) {
|
||||||
|
val factory = object: ExtendedScreenHandlerFactory {
|
||||||
|
override fun createMenu(syncId: Int, playerInv: PlayerInventory, player: PlayerEntity): ScreenHandler {
|
||||||
|
return SwitchConsoleScreenHandler(syncId, be)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getDisplayName() = PhyBlocks.SWITCH.name
|
||||||
|
|
||||||
|
override fun writeScreenOpeningData(player: ServerPlayerEntity, buf: PacketByteBuf) {
|
||||||
|
buf.writeBlockPos(be.pos)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
player.openHandledScreen(factory)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,69 @@
|
||||||
|
package net.shadowfacts.phycon.util
|
||||||
|
|
||||||
|
import kotlin.math.min
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author shadowfacts
|
||||||
|
*/
|
||||||
|
class IntRingBuffer(size: Int): Iterable<Int> {
|
||||||
|
private val array = IntArray(size)
|
||||||
|
private var next = 0
|
||||||
|
private var wrapped = false
|
||||||
|
|
||||||
|
val size: Int
|
||||||
|
get() = if (wrapped) array.size else next
|
||||||
|
|
||||||
|
fun add(value: Int) {
|
||||||
|
array[next] = value
|
||||||
|
next = (next + 1) % array.size
|
||||||
|
wrapped = wrapped || next == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
fun asContiguousArray(): IntArray {
|
||||||
|
if (wrapped) {
|
||||||
|
val array = IntArray(array.size)
|
||||||
|
this.array.copyInto(array, destinationOffset = array.size - next, startIndex = 0, endIndex = next)
|
||||||
|
this.array.copyInto(array, destinationOffset = 0, startIndex = next, endIndex = array.size)
|
||||||
|
return array
|
||||||
|
} else {
|
||||||
|
return this.array.copyOfRange(0, next)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun replace(array: IntArray) {
|
||||||
|
val count = min(array.size, this.array.size)
|
||||||
|
array.copyInto(this.array, destinationOffset = 0, startIndex = array.size - count, endIndex = count)
|
||||||
|
next = (count + 1) % this.array.size
|
||||||
|
wrapped = array.size >= this.array.size
|
||||||
|
}
|
||||||
|
|
||||||
|
fun clear() {
|
||||||
|
next = 0
|
||||||
|
wrapped = false
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun iterator() = Iterator(this)
|
||||||
|
|
||||||
|
class Iterator(private val ringBuffer: IntRingBuffer): IntIterator() {
|
||||||
|
private var nextIndex = if (ringBuffer.wrapped) ringBuffer.next else 0
|
||||||
|
private var wrapped = false
|
||||||
|
|
||||||
|
override fun nextInt(): Int {
|
||||||
|
val res = ringBuffer.array[nextIndex]
|
||||||
|
nextIndex += 1
|
||||||
|
if (nextIndex >= ringBuffer.array.size) {
|
||||||
|
wrapped = true
|
||||||
|
nextIndex = 0
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun hasNext(): Boolean {
|
||||||
|
return if (ringBuffer.wrapped) {
|
||||||
|
!wrapped || nextIndex < ringBuffer.next
|
||||||
|
} else {
|
||||||
|
nextIndex < ringBuffer.next
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,48 @@
|
||||||
|
package net.shadowfacts.phycon.util
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test
|
||||||
|
import org.junit.jupiter.api.Assertions.assertArrayEquals
|
||||||
|
import org.junit.jupiter.api.Assertions.assertFalse
|
||||||
|
import org.junit.jupiter.api.Assertions.assertEquals
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author shadowfacts
|
||||||
|
*/
|
||||||
|
class IntRingBufferTests {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testEmpty() {
|
||||||
|
assertArrayEquals(intArrayOf(), IntRingBuffer(4).asContiguousArray())
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testAsContiguousArray() {
|
||||||
|
val buffer = IntRingBuffer(4)
|
||||||
|
buffer.add(0)
|
||||||
|
buffer.add(1)
|
||||||
|
assertArrayEquals(intArrayOf(0, 1), buffer.asContiguousArray())
|
||||||
|
|
||||||
|
buffer.add(2)
|
||||||
|
buffer.add(3)
|
||||||
|
buffer.add(4)
|
||||||
|
assertArrayEquals(intArrayOf(1, 2, 3, 4), buffer.asContiguousArray())
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testIterator() {
|
||||||
|
val buffer = IntRingBuffer(4)
|
||||||
|
|
||||||
|
val iterator = buffer.iterator()
|
||||||
|
assertFalse(iterator.hasNext())
|
||||||
|
|
||||||
|
buffer.add(0)
|
||||||
|
buffer.add(1)
|
||||||
|
assertEquals(listOf(0, 1), buffer.toList())
|
||||||
|
|
||||||
|
buffer.add(2)
|
||||||
|
buffer.add(3)
|
||||||
|
buffer.add(4)
|
||||||
|
assertEquals(listOf(1, 2, 3, 4), buffer.toList())
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue