diff --git a/core/src/main/java/org/teavm/runtime/Heap.java b/core/src/main/java/org/teavm/runtime/Heap.java new file mode 100644 index 000000000..6221ca64b --- /dev/null +++ b/core/src/main/java/org/teavm/runtime/Heap.java @@ -0,0 +1,19 @@ +/* + * Copyright 2025 Alexey Andreev. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.teavm.runtime; + +public class Heap { +} diff --git a/core/src/main/java/org/teavm/runtime/heap/Heap.java b/core/src/main/java/org/teavm/runtime/heap/Heap.java new file mode 100644 index 000000000..bbe5a9d8f --- /dev/null +++ b/core/src/main/java/org/teavm/runtime/heap/Heap.java @@ -0,0 +1,171 @@ +/* + * Copyright 2025 Alexey Andreev. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.teavm.runtime.heap; + +import org.teavm.interop.Address; +import org.teavm.interop.StaticInit; +import org.teavm.interop.Structure; +import org.teavm.interop.Unmanaged; + +@Unmanaged +@StaticInit +public class Heap { + private static HeapNode root; + + public static void init(Address start, int size) { + root = start.toStructure(); + root.size = size - Structure.sizeOf(HeapRecord.class); + root.left = null; + root.right = null; + root.height = 1; + root.flags = 0; + } + + public static Address alloc(int bytes) { + if (root == null) { + return null; + } + + bytes = (Integer.divideUnsigned(bytes - 1, Address.sizeOf()) + 1) * Address.sizeOf(); + bytes = Math.max(bytes, Structure.sizeOf(HeapNode.class) - Structure.sizeOf(HeapRecord.class)); + var free = findFree(root, bytes); + if (free == null) { + return null; + } + delete(free); + if (free.size - bytes >= Structure.sizeOf(HeapNode.class) + Structure.sizeOf(HeapRecord.class)) { + HeapNode nextFree = free.toAddress().add(Structure.sizeOf(HeapRecord.class) + bytes).toStructure(); + nextFree.size = free.size - bytes - Structure.sizeOf(HeapRecord.class); + nextFree.previousSize = free.size; + root = insert(root, nextFree); + } + + return free.toAddress().add(Structure.sizeOf(HeapRecord.class)); + } + + public static void release(Address address) { + HeapRecord record = address.add(-Structure.sizeOf(HeapRecord.class)).toStructure(); + HeapRecord nextRecord = record.toAddress().add(Structure.sizeOf(HeapRecord.class) + record.size).toStructure(); + + } + + private static HeapNode insert(HeapNode into, HeapNode node) { + if (into == null) { + node.left = null; + node.right = null; + node.flags &= ~HeapNode.FLAG_LIST; + return node; + } + if (node.size < into.size) { + into.left = insert(into.left, node); + } else if (node.size > into.size) { + into.right = insert(into.right, node); + } else { + node.left = into; + node.flags |= HeapNode.FLAG_LIST; + node.right = into.list; + if (into.list != null) { + into.list.left = node; + } + into.list = node; + return into; + } + HeapNode.fix(into); + return HeapNode.balance(into); + } + + private static void delete(HeapNode node) { + if ((node.flags & HeapNode.FLAG_LIST) != 0) { + root = delete(root, node); + } else if (node.list != null) { + var newHead = node.list; + updateListHolder(root, node); + newHead.list = newHead.right; + newHead.left = node.left; + newHead.right = node.right; + newHead.flags &= ~HeapNode.FLAG_LIST; + } else { + root = delete(root, node); + } + } + + private static void updateListHolder(HeapNode at, HeapNode node) { + if (at.left == node) { + at.left = node.list; + } else if (at.right == node) { + at.right = node.list; + } else if (node.size < at.size) { + updateListHolder(at.left, node); + } else { + updateListHolder(at.right, node); + } + } + + private static HeapNode delete(HeapNode root, HeapNode node) { + if (root == null) { + return null; + } + if (node.size < root.size) { + root.left = delete(root.left, node); + } else if (node.size > root.size) { + root.right = delete(root.right, node); + } else if (root.left == null) { + return root.right; + } else if (root.right == null) { + return root.left; + } else { + var left = root.left; + var min = findMinimum(root.right); + min.right = removeMinimum(root.right); + min.left = left; + root = min; + } + HeapNode.fix(root); + return HeapNode.balance(root); + } + + private static HeapNode removeMinimum(HeapNode node) { + if (node.left == null) { + return node.right; + } else { + node.left = removeMinimum(node.left); + HeapNode.fix(node); + return HeapNode.balance(node); + } + } + + private static HeapNode findMinimum(HeapNode node) { + while (node.left != null) { + node = node.left; + } + return node; + } + + private static HeapNode findFree(HeapNode node, int bytes) { + while (node != null) { + if (node.size < bytes) { + node = node.right; + } else if (node.size > bytes) { + if (node.left == null || node.left.size < bytes) { + break; + } + } else { + break; + } + } + return node; + } +} diff --git a/core/src/main/java/org/teavm/runtime/heap/HeapNode.java b/core/src/main/java/org/teavm/runtime/heap/HeapNode.java new file mode 100644 index 000000000..a8851a1d7 --- /dev/null +++ b/core/src/main/java/org/teavm/runtime/heap/HeapNode.java @@ -0,0 +1,76 @@ +/* + * Copyright 2025 Alexey Andreev. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.teavm.runtime.heap; + +import org.teavm.interop.StaticInit; +import org.teavm.interop.Unmanaged; + +@Unmanaged +@StaticInit +class HeapNode extends HeapRecord { + static final int FLAG_LIST = 1; + + HeapNode left; + HeapNode right; + HeapNode list; + short height; + short flags; + + static HeapNode balance(HeapNode node) { + int factor = factor(node); + if (factor == 2) { + if (factor(node.right) < 0) { + node.right = rotateRight(node.right); + } + return rotateLeft(node); + } else if (factor == -2) { + if (factor(node.left) > 0) { + node.left = rotateLeft(node.left); + } + return rotateRight(node); + } else { + return node; + } + } + + private static int factor(HeapNode node) { + return (node.right != null ? node.right.height : 0) - (node.left != null ? node.left.height : 0); + } + + private static HeapNode rotateRight(HeapNode node) { + var left = node.left; + node.left = left.right; + left.right = node; + fix(node); + fix(left); + return left; + } + + private static HeapNode rotateLeft(HeapNode node) { + var right = node.right; + node.right = right.left; + right.left = node; + fix(node); + fix(right); + return right; + } + + static void fix(HeapNode node) { + var leftHeight = node.right != null ? node.right.height : 0; + var rightHeight = node.left != null ? node.left.height : 0; + node.height = (short) (Math.max(leftHeight, rightHeight) + 1); + } +} diff --git a/core/src/main/java/org/teavm/runtime/heap/HeapRecord.java b/core/src/main/java/org/teavm/runtime/heap/HeapRecord.java new file mode 100644 index 000000000..451e94815 --- /dev/null +++ b/core/src/main/java/org/teavm/runtime/heap/HeapRecord.java @@ -0,0 +1,23 @@ +/* + * Copyright 2025 Alexey Andreev. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.teavm.runtime.heap; + +import org.teavm.interop.Structure; + +public class HeapRecord extends Structure { + public int size; + public int previousSize; +}