philiprehberger-queue_stack 0.6.0 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: aeb1d7901c33280074ac91777f24b5dcc3704f9d3144dac214a4e8baba9b8bf0
4
- data.tar.gz: 21a0f6c879d8d1f95270b4d34497e5905064ee96738b30ae9a7bb870c6e3d820
3
+ metadata.gz: dbcea69e9c62dd59232e450824070497d480a78224b1f04894664832e1e5c019
4
+ data.tar.gz: b9c91c0ff26af13c51ce30667c5eee0ad0bedce13c807164ddcd273e147d2513
5
5
  SHA512:
6
- metadata.gz: 28ba1990bc0e94d4c79b414d1a62a69d022f4091d76bd3bdc092e6c377cc0fd7fd79d1a265126aed15bf3a705fa34ac05c05d084990ea867022e03220f23ed7d
7
- data.tar.gz: d6fc9a7f47a18010cf5b1f7e096861cca61bf565f604c5687a10165cc2074196457e918b3564fe8f80a233ca2e9ad4584929af11b465db4481b3e35f40e0a6ca
6
+ metadata.gz: 8db38faf1a4c7090ee405fededf09f3f939fd11217d46d0220641ff12b3ef286ca915c845b98847e3c22e7f4e0a4ab55d5c71a14d18db9af7b99859e24b454ff
7
+ data.tar.gz: 748285864c78717094e6f34a8c0cf9b4fc2e9c7bde05c17d14b8bd60821790bca8687b9076e2f4d24b9f5bb3cab3a23c131e1dd87cd64f7a4b6da1b30985df38
data/CHANGELOG.md CHANGED
@@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.7.0] - 2026-05-29
11
+
12
+ ### Added
13
+ - `Queue#enqueue_all(items)` and `Stack#push_all(items)` — enqueue/push an array of items under a single mutex acquisition; each item respects capacity and `ClosedError` semantics
14
+ - `Queue#dequeue_batch(max)` and `Stack#pop_batch(max)` — remove and return up to `max` items in one synchronized step; non-blocking; signals waiting producers
15
+
10
16
  ## [0.6.0] - 2026-04-27
11
17
 
12
18
  ### Added
data/README.md CHANGED
@@ -4,6 +4,8 @@
4
4
  [![Gem Version](https://badge.fury.io/rb/philiprehberger-queue_stack.svg)](https://rubygems.org/gems/philiprehberger-queue_stack)
5
5
  [![Last updated](https://img.shields.io/github/last-commit/philiprehberger/rb-queue-stack)](https://github.com/philiprehberger/rb-queue-stack/commits/main)
6
6
 
7
+ ![philiprehberger-queue_stack](https://raw.githubusercontent.com/philiprehberger/rb-queue-stack/main/package-card.webp)
8
+
7
9
  Thread-safe Queue and Stack with capacity limits and blocking operations
8
10
 
9
11
  ## Requirements
@@ -175,6 +177,21 @@ q.remaining_capacity # => 50
175
177
  batch_size = [items.length, q.remaining_capacity].min
176
178
  ```
177
179
 
180
+ ### Batch Insertion and Draining
181
+
182
+ `enqueue_all` / `push_all` insert an array of items under a single mutex acquisition. `dequeue_batch` / `pop_batch` remove up to `max` items at once and signal waiting producers. All four respect capacity and `ClosedError` semantics.
183
+
184
+ ```ruby
185
+ q = Philiprehberger::QueueStack::Queue.new
186
+ q.enqueue_all(%w[a b c d])
187
+ q.dequeue_batch(2) # => ["a", "b"]
188
+ q.dequeue_batch(99) # => ["c", "d"] (clamped to available)
189
+
190
+ s = Philiprehberger::QueueStack::Stack.new
191
+ s.push_all(%w[a b c])
192
+ s.pop_batch(2) # => ["c", "b"] (LIFO: top first)
193
+ ```
194
+
178
195
  ## API
179
196
 
180
197
  ### `Queue`
@@ -183,8 +200,10 @@ batch_size = [items.length, q.remaining_capacity].min
183
200
  |--------|-------------|
184
201
  | `.new(capacity:)` | Create a queue with optional capacity limit |
185
202
  | `#enqueue(item)` | Add item to back (blocks if full) |
203
+ | `#enqueue_all(items)` | Enqueue an array of items in FIFO order (blocks per-element if full) |
186
204
  | `#try_enqueue(item, timeout: nil)` | Non-blocking enqueue, returns true/false (waits up to timeout if given) |
187
205
  | `#dequeue` | Remove and return front item (blocks if empty) |
206
+ | `#dequeue_batch(max)` | Remove and return up to `max` items in FIFO order (non-blocking) |
188
207
  | `#dequeue_if { \|item\| ... }` | Remove and return the front item only if the block is truthy (non-blocking) |
189
208
  | `#try_dequeue(timeout:)` | Dequeue with timeout, returns nil on timeout |
190
209
  | `#clear` | Remove all items without returning them |
@@ -207,8 +226,10 @@ batch_size = [items.length, q.remaining_capacity].min
207
226
  |--------|-------------|
208
227
  | `.new(capacity:)` | Create a stack with optional capacity limit |
209
228
  | `#push(item)` | Push item on top (blocks if full) |
229
+ | `#push_all(items)` | Push an array of items in order; last becomes the top (blocks per-element if full) |
210
230
  | `#try_push(item, timeout: nil)` | Non-blocking push, returns true/false (waits up to timeout if given) |
211
231
  | `#pop` | Remove and return top item (blocks if empty) |
232
+ | `#pop_batch(max)` | Pop up to `max` items, top first (non-blocking) |
212
233
  | `#pop_if { \|item\| ... }` | Remove and return the top item only if the block is truthy (non-blocking) |
213
234
  | `#try_pop(timeout:)` | Pop with timeout, returns nil on timeout |
214
235
  | `#clear` | Remove all items without returning them |
@@ -246,6 +246,47 @@ module Philiprehberger
246
246
  [@capacity - @items.length, 0].max
247
247
  end
248
248
  end
249
+
250
+ # Enqueue many items in FIFO order. Each item is enqueued under the
251
+ # same Mutex acquisition; the call blocks while waiting for capacity
252
+ # exactly as +enqueue+ would for the individual elements that cannot
253
+ # fit immediately.
254
+ #
255
+ # @param items [Array] the items to enqueue, in order
256
+ # @return [void]
257
+ # @raise [ClosedError] if the queue has been closed
258
+ def enqueue_all(items)
259
+ @mutex.synchronize do
260
+ raise ClosedError, 'cannot enqueue on a closed queue' if @closed
261
+
262
+ items.each do |item|
263
+ @not_full.wait(@mutex) while @capacity && @items.length >= @capacity
264
+ raise ClosedError, 'cannot enqueue on a closed queue' if @closed
265
+
266
+ @items.push(item)
267
+ @not_empty.signal
268
+ end
269
+ end
270
+ end
271
+
272
+ # Remove and return up to +max+ items from the front of the queue.
273
+ # Non-blocking: returns an empty array if the queue is empty.
274
+ #
275
+ # @param max [Integer] maximum number of items to remove (must be a non-negative Integer)
276
+ # @return [Array] up to +max+ items in FIFO order
277
+ # @raise [ArgumentError] if +max+ is not a non-negative Integer
278
+ def dequeue_batch(max)
279
+ raise ArgumentError, 'max must be a non-negative Integer' unless max.is_a?(Integer) && max >= 0
280
+
281
+ @mutex.synchronize do
282
+ count = [max, @items.length].min
283
+ next [] if count.zero?
284
+
285
+ batch = @items.shift(count)
286
+ @not_full.broadcast
287
+ batch
288
+ end
289
+ end
249
290
  end
250
291
  end
251
292
  end
@@ -234,6 +234,46 @@ module Philiprehberger
234
234
  [@capacity - @items.length, 0].max
235
235
  end
236
236
  end
237
+
238
+ # Push many items onto the stack in order. The last element of +items+
239
+ # ends up on top. Each push respects capacity exactly as +push+ would.
240
+ #
241
+ # @param items [Array] the items to push, in order
242
+ # @return [void]
243
+ # @raise [ClosedError] if the stack has been closed
244
+ def push_all(items)
245
+ @mutex.synchronize do
246
+ raise ClosedError, 'cannot push on a closed stack' if @closed
247
+
248
+ items.each do |item|
249
+ @not_full.wait(@mutex) while @capacity && @items.length >= @capacity
250
+ raise ClosedError, 'cannot push on a closed stack' if @closed
251
+
252
+ @items.push(item)
253
+ @not_empty.signal
254
+ end
255
+ end
256
+ end
257
+
258
+ # Pop up to +max+ items from the top of the stack in LIFO order
259
+ # (top first). Non-blocking: returns an empty array if the stack is
260
+ # empty.
261
+ #
262
+ # @param max [Integer] maximum number of items to pop (must be a non-negative Integer)
263
+ # @return [Array] up to +max+ items, top first
264
+ # @raise [ArgumentError] if +max+ is not a non-negative Integer
265
+ def pop_batch(max)
266
+ raise ArgumentError, 'max must be a non-negative Integer' unless max.is_a?(Integer) && max >= 0
267
+
268
+ @mutex.synchronize do
269
+ count = [max, @items.length].min
270
+ next [] if count.zero?
271
+
272
+ batch = Array.new(count) { @items.pop }
273
+ @not_full.broadcast
274
+ batch
275
+ end
276
+ end
237
277
  end
238
278
  end
239
279
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Philiprehberger
4
4
  module QueueStack
5
- VERSION = '0.6.0'
5
+ VERSION = '0.7.0'
6
6
  end
7
7
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: philiprehberger-queue_stack
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.0
4
+ version: 0.7.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Philip Rehberger
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2026-04-28 00:00:00.000000000 Z
11
+ date: 2026-05-30 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: Thread-safe queue and stack data structures with configurable capacity
14
14
  limits, blocking enqueue/dequeue with timeouts, and peek operations. Uses Mutex