#### libremiliacr
#### Copyright(C) 2020-2024 Remilia Scarlet <remilia@posteo.jp>
####
#### This program is free software: you can redistribute it and/or modify
#### it under the terms of the GNU General Public License as published
#### the Free Software Foundation, either version 3 of the License, or
#### (at your option) any later version.
####
#### This program is distributed in the hope that it will be useful,
#### but WITHOUT ANY WARRANTY; without even the implied warranty of
#### MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
#### GNU General Public License for more details.
####
#### You should have received a copy of the GNU General Public License
#### along with this program.If not, see<http:####www.gnu.org/licenses/.>
module RemiLib
# The `ArrayPool` class provides functionality to allow an array of a desired
# size to be 'rented' out so that buffers of the same size don't need to be
# constantly re-created.
#
# These are not thread safe. If you need to use array pools with multiple
# threads, create one `ArrayPool` instance directly per thread.
class ArrayPool(T)
@arrays : Array(Array(T)) = [] of Array(T)
@rented : Array(Bool) = [] of Bool
# Creates a new `ArrayPool` instance.
#
# `defaultVal` is the default value assigned to each index when creating
# _NEW_ arrays. Existing arrays are not re-initialized with `defaultVal`
# between rentings.
def initialize(@defaultVal : T)
end
# Requests an `Array(T)` of `size` elements from the pool. If an
# array of that size already exists and is not yet rented, it is returned
# and set as 'rented'. Otherwise a new array of that size is created, set
# as 'rented', and returned.
#
# IMPORTANT: No effort is made to clear out the contents of old arrays
# before returning one.
def rent(size) : Array(T)
@arrays.size.times do |i|
if !@rented.unsafe_fetch(i) && @arrays.unsafe_fetch(i).size == size
# Found one to rent
@rented.unsafe_put(i, true)
return @arrays.unsafe_fetch(i)
end
end
# None available, make a new array and rent it out.
ret = Array.new(size, @defaultVal)
@arrays << ret
@rented << true
ret
end
# Lets the array pool know that you are done using `array` so that it can
# mark it as not rented. After calling this, you should not use `array`
# anymore without first re-renting it with `ArrayPool.rent`.
def return(array)
@arrays.size.times do |i|
if @arrays.unsafe_fetch(i).same?(array)
@rented.unsafe_put(i, false)
return
end
end
raise "Attempted to return a buffer that was not part of the array pool"
end
# Rents out an array of `size` elements, then yields it to the block. The
# block is wrapped in a `begin..ensure` that will automatically return the
# array when the block exits.
def withRentedArray(size, &)
buf = rent(size)
begin
yield buf
ensure
self.return(buf)
end
end
# Removes all arrays from the pool and marks them as not rented.
def clear : Nil
@arrays.clear
@rented.clear
end
end
end