Created
September 22, 2018 22:03
-
-
Save bew/b816c76613574389445ba43b8f7110eb to your computer and use it in GitHub Desktop.
Class pre-allocation in Crystal
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class Object | |
alias TypeIdType = Int32 | |
end | |
# | |
# This is unsafe to store any class created through the ObjectPool, as as | |
# soon the ObjectPool is GC-ed, all pointers to classes inside that pool | |
# will have unknown behavior if used. | |
class ObjectPool(T) | |
@buffer : Pointer(UInt8) | |
# Returns the size of the object pool. | |
getter size : Int32 | |
# Creates an object pool with *size* number of pre-allocated class T. | |
def initialize(size : Int) | |
{% unless T < Reference %} | |
{% raise "Object type must be a class" %} | |
{% end %} | |
@size = size.to_i | |
@buffer = Pointer(UInt8).malloc(instance_sizeof(T) * size) | |
# NOTE: memory buffer is all 0 | |
# Set type_id of each allocated T instances | |
@size.times do |index| | |
object_raw_ptr = unsafe_raw_ptr_at(index) | |
object_raw_ptr.as(Object::TypeIdType*).value = T.crystal_instance_type_id | |
end | |
end | |
# Returns the object at *index* as the given object type. | |
# | |
# Note that the object might not be initialized at this point, | |
# it is recommended to always initialize the instance when you | |
# get it for the first time. | |
def object_at(index) : T | |
object_raw_ptr = unsafe_raw_ptr_at(index) | |
object_raw_ptr.unsafe_as(T) | |
end | |
private def unsafe_raw_ptr_at(index) | |
# TODO: check *index* is in range | |
first_byte_index = instance_sizeof(T) * index | |
@buffer + first_byte_index | |
end | |
end | |
class Person | |
property name : String | |
property age : Int32 | |
def initialize(@name, @age) | |
end | |
def init(*args, **kwargs) | |
# This is needed because `initialize` is protected, | |
# thus I can't call it from the outside. | |
# | |
# NOTE for me: this is one way to initialize the instance, but | |
# this `init` method should be explicitely declared by | |
# the user, not generated by some macros. | |
initialize(*args, **kwargs) | |
end | |
end | |
# --------------------------------------------- | |
def init_person_by_ref(person : Person) | |
person.name = "bew" | |
person.age = 3 | |
end | |
def try_pre_alloc | |
pool = ObjectPool(Person).new 5 | |
person = pool.object_at 1 | |
person.init("init", -1) | |
p! person | |
init_person_by_ref(person) | |
p! person | |
end | |
try_pre_alloc |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Live first version: https://play.crystal-lang.org/#/r/559a