Skip to content

Commit

Permalink
Implement traditional heap (malloc/free) for use in direct NIO buffer…
Browse files Browse the repository at this point in the history
…s implementation
  • Loading branch information
konsoletyper committed Feb 1, 2025
1 parent 2192b3a commit e8e734a
Show file tree
Hide file tree
Showing 4 changed files with 289 additions and 0 deletions.
19 changes: 19 additions & 0 deletions core/src/main/java/org/teavm/runtime/Heap.java
Original file line number Diff line number Diff line change
@@ -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 {
}
171 changes: 171 additions & 0 deletions core/src/main/java/org/teavm/runtime/heap/Heap.java
Original file line number Diff line number Diff line change
@@ -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;
}
}
76 changes: 76 additions & 0 deletions core/src/main/java/org/teavm/runtime/heap/HeapNode.java
Original file line number Diff line number Diff line change
@@ -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);
}
}
23 changes: 23 additions & 0 deletions core/src/main/java/org/teavm/runtime/heap/HeapRecord.java
Original file line number Diff line number Diff line change
@@ -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;
}

0 comments on commit e8e734a

Please sign in to comment.