Login
arraypool.cr at tip
Login

File src/remilib/arraypool.cr from the latest check-in


     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
#### 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