Class: Lich::Common::LimitedArray

Inherits:
Array
  • Object
show all
Defined in:
documented/common/limitedarray.rb

Overview

Bounded script buffer with condition-variable-backed blocking reads.

Constant Summary collapse

SYNCHRONIZED_ARRAY_MUTATORS =

Mutating Array methods that require synchronization in Lich::Common::LimitedArray.

Returns:

  • (Array<Symbol>)

    list of method names that modify the array

%i[
  collect! compact! delete delete_at delete_if filter! keep_if map! pop
  reject! reverse! rotate! select! shuffle! slice! sort! sort_by! uniq!
].freeze
ENUMERATOR_MUTATORS =

Subset of SYNCHRONIZED_ARRAY_MUTATORS that return an Enumerator when called without a block.

Returns:

  • (Array<Symbol>)

    list of method names that support lazy evaluation

%i[collect! delete_if filter! keep_if map! reject! select! sort_by!].freeze
RAW_PUSH =

Unbound reference to Array#push, used to bypass overrides in synchronized operations.

Returns:

  • (UnboundMethod)
Array.instance_method(:push)
RAW_UNSHIFT =

Unbound reference to Array#unshift, used to bypass overrides in synchronized operations.

Returns:

  • (UnboundMethod)
Array.instance_method(:unshift)
RAW_SHIFT =

Unbound reference to Array#shift, used to bypass overrides in synchronized operations.

Returns:

  • (UnboundMethod)
Array.instance_method(:shift)
RAW_POP =

Unbound reference to Array#pop, used to bypass overrides in synchronized operations.

Returns:

  • (UnboundMethod)
Array.instance_method(:pop)
RAW_EMPTY =

Unbound reference to Array#empty?, used to bypass overrides in synchronized operations.

Returns:

  • (UnboundMethod)
Array.instance_method(:empty?)
RAW_LENGTH =

Unbound reference to Array#length, used to bypass overrides in synchronized operations.

Returns:

  • (UnboundMethod)
Array.instance_method(:length)
RAW_CLEAR =

Unbound reference to Array#clear, used to bypass overrides in synchronized operations.

Returns:

  • (UnboundMethod)
Array.instance_method(:clear)
RAW_DUP =

Unbound reference to Array#dup, used to bypass overrides in synchronized operations.

Returns:

  • (UnboundMethod)
Array.instance_method(:dup)
INIT_MUTEX =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

Global mutex protecting initialization of per-instance synchronization primitives.

Returns:

  • (Mutex)
Mutex.new

Instance Method Summary collapse

Constructor Details

#initialize(size = 0, obj = nil) ⇒ LimitedArray

Initializes a bounded array with a default size of 200 elements.

Examples:

buf = Lich::Common::LimitedArray.new(5)
buf.max_size #=> 200

Parameters:

  • size (Integer) (defaults to: 0)

    initial capacity; excess elements are trimmed from the front

  • obj (Object) (defaults to: nil)

    initial value to fill; optional



68
69
70
71
72
# File 'documented/common/limitedarray.rb', line 68

def initialize(size = 0, obj = nil)
  @max_size = 200
  super
  trim_front_locked
end

Instance Method Details

#[]=(*args) ⇒ Object

Sets elements by index or range and trims the front if it exceeds max_size.

Parameters:

  • args (Object)

    index/range and value(s) to assign

Returns:

  • (Object)

    the assigned value(s)



165
166
167
# File 'documented/common/limitedarray.rb', line 165

def []=(*args)
  bounded_mutation(:[]=, *args)
end

#clearself

Removes all elements from the array.

Returns:

  • (self)


220
221
222
# File 'documented/common/limitedarray.rb', line 220

def clear
  synchronize { RAW_CLEAR.bind_call(self) }
end

#clear_snapshotArray

Returns a snapshot of the current array and clears it in one atomic operation.

Examples:

buf = Lich::Common::LimitedArray.new
buf.push(1, 2, 3)
snapshot = buf.clear_snapshot
snapshot #=> [1, 2, 3]
buf.empty? #=> true

Returns:

  • (Array)

    a shallow copy of the array before clearing



265
266
267
268
269
270
271
# File 'documented/common/limitedarray.rb', line 265

def clear_snapshot
  synchronize do
    snapshot = RAW_DUP.bind_call(self)
    RAW_CLEAR.bind_call(self)
    snapshot
  end
end

#concat(other) ⇒ self

Concatenates another array and trims the front if it exceeds max_size.

Parameters:

  • other (Array)

    the array to concatenate

Returns:

  • (self)


140
141
142
# File 'documented/common/limitedarray.rb', line 140

def concat(other)
  bounded_mutation(:concat, other)
end

#dupArray

Returns a shallow copy of the array.

Returns:

  • (Array)

    a new unsynchronized copy



227
228
229
# File 'documented/common/limitedarray.rb', line 227

def dup
  synchronize { RAW_DUP.bind_call(self) }
end

#empty?Boolean

Returns whether the array is empty.

Returns:

  • (Boolean)

    true if the array has no elements



213
214
215
# File 'documented/common/limitedarray.rb', line 213

def empty?
  synchronize { RAW_EMPTY.bind_call(self) }
end

#fill(*args) { ... } ⇒ self

Fills the array with a value or block result and trims the front if it exceeds max_size.

Parameters:

  • args (Object)

    value or range and value

Yields:

  • block to generate values

Returns:

  • (self)


174
175
176
# File 'documented/common/limitedarray.rb', line 174

def fill(*args, &block)
  bounded_mutation(:fill, *args, &block)
end

#flatten!(*args) ⇒ self?

Recursively flattens nested arrays and trims the front if it exceeds max_size.

Parameters:

  • args (Integer)

    optional depth limit

Returns:

  • (self, nil)

    self if the array changed, nil otherwise



182
183
184
# File 'documented/common/limitedarray.rb', line 182

def flatten!(*args)
  bounded_mutation(:flatten!, *args)
end

#historyArray

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Returns an empty array (placeholder for compatibility).

Returns:

  • (Array)

    always returns an empty array



198
199
200
# File 'documented/common/limitedarray.rb', line 198

def history
  Array.new
end

#insert(index, *objects) ⇒ self

Inserts elements at the given index and trims the front if it exceeds max_size.

Parameters:

  • index (Integer)

    the insertion point

  • objects (Object)

    one or more elements to insert

Returns:

  • (self)


157
158
159
# File 'documented/common/limitedarray.rb', line 157

def insert(index, *objects)
  bounded_mutation(:insert, index, *objects)
end

#max_sizeInteger

Returns the maximum number of elements this array will hold.

Returns:

  • (Integer)

    the size limit



77
78
79
# File 'documented/common/limitedarray.rb', line 77

def max_size
  synchronize { @max_size }
end

#max_size=(value) ⇒ Integer

Sets the maximum number of elements, trimming the front if necessary.

Parameters:

  • value (Integer)

    the new size limit, must be positive

Returns:

  • (Integer)

    the new max_size

Raises:

  • (ArgumentError)

    if value is not a positive Integer



86
87
88
89
90
91
92
93
94
95
# File 'documented/common/limitedarray.rb', line 86

def max_size=(value)
  unless value.is_a?(Integer) && value.positive?
    raise ArgumentError, 'max_size must be a positive Integer'
  end

  synchronize do
    @max_size = value
    trim_front_locked
  end
end

#push(*lines) ⇒ self Also known as: <<, append

Appends elements to the end of the array and trims the front if it exceeds max_size.

Broadcasts to any threads waiting in #wait_shift.

Examples:

buf = Lich::Common::LimitedArray.new
buf.push("line 1", "line 2")

Parameters:

  • lines (Object)

    one or more elements to append

Returns:

  • (self)


106
107
108
109
110
111
112
113
# File 'documented/common/limitedarray.rb', line 106

def push(*lines)
  synchronize do
    result = RAW_PUSH.bind_call(self, *lines)
    trim_front_locked
    condition.broadcast unless lines.empty?
    result
  end
end

#replace(other) ⇒ self

Replaces the entire array contents and trims the front if it exceeds max_size.

Parameters:

  • other (Array)

    the replacement contents

Returns:

  • (self)


148
149
150
# File 'documented/common/limitedarray.rb', line 148

def replace(other)
  bounded_mutation(:replace, other)
end

#shift(*args) ⇒ Object, ...

Removes and returns the first element(s) from the front of the array.

Parameters:

  • args (Integer)

    optional count of elements to remove

Returns:

  • (Object, Array, nil)

    the removed element(s), or nil if empty



206
207
208
# File 'documented/common/limitedarray.rb', line 206

def shift(*args)
  synchronize { RAW_SHIFT.bind_call(self, *args) }
end

#shove(line) ⇒ self

Appends an element to the end of the array (alias for push).

Parameters:

  • line (Object)

    the element to append

Returns:

  • (self)


190
191
192
# File 'documented/common/limitedarray.rb', line 190

def shove(line)
  push(line)
end

#try_shiftObject?

Removes and returns the first element, or nil if the array is empty (non-blocking).

Returns:

  • (Object, nil)

    the removed element or nil



248
249
250
251
252
253
254
# File 'documented/common/limitedarray.rb', line 248

def try_shift
  synchronize do
    return nil if RAW_EMPTY.bind_call(self)

    RAW_SHIFT.bind_call(self)
  end
end

#unshift(*lines) ⇒ self Also known as: prepend

Prepends elements to the beginning of the array and trims the back if it exceeds max_size.

Broadcasts to any threads waiting in #wait_shift.

Examples:

buf = Lich::Common::LimitedArray.new
buf.unshift("first", "second")

Parameters:

  • lines (Object)

    one or more elements to prepend

Returns:

  • (self)


126
127
128
129
130
131
132
133
# File 'documented/common/limitedarray.rb', line 126

def unshift(*lines)
  synchronize do
    result = RAW_UNSHIFT.bind_call(self, *lines)
    RAW_POP.bind_call(self) while RAW_LENGTH.bind_call(self) > @max_size
    condition.broadcast unless lines.empty?
    result
  end
end

#wait_shift(timeout = nil) ⇒ Object

Wait for and remove the next item, returning nil when timeout expires.



232
233
234
235
236
237
238
239
240
241
242
243
# File 'documented/common/limitedarray.rb', line 232

def wait_shift(timeout = nil)
  mutex.synchronize do
    deadline = monotonic_time + timeout.to_f if timeout
    while RAW_EMPTY.bind_call(self)
      remaining = deadline && deadline - monotonic_time
      return nil if remaining && remaining <= 0

      condition.wait(mutex, remaining)
    end
    RAW_SHIFT.bind_call(self)
  end
end