philiprehberger-queue_stack 0.5.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 +4 -4
- data/CHANGELOG.md +11 -0
- data/README.md +41 -0
- data/lib/philiprehberger/queue_stack/queue.rb +62 -0
- data/lib/philiprehberger/queue_stack/stack.rb +61 -0
- data/lib/philiprehberger/queue_stack/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: dbcea69e9c62dd59232e450824070497d480a78224b1f04894664832e1e5c019
|
|
4
|
+
data.tar.gz: b9c91c0ff26af13c51ce30667c5eee0ad0bedce13c807164ddcd273e147d2513
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 8db38faf1a4c7090ee405fededf09f3f939fd11217d46d0220641ff12b3ef286ca915c845b98847e3c22e7f4e0a4ab55d5c71a14d18db9af7b99859e24b454ff
|
|
7
|
+
data.tar.gz: 748285864c78717094e6f34a8c0cf9b4fc2e9c7bde05c17d14b8bd60821790bca8687b9076e2f4d24b9f5bb3cab3a23c131e1dd87cd64f7a4b6da1b30985df38
|
data/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,17 @@ 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
|
+
|
|
16
|
+
## [0.6.0] - 2026-04-27
|
|
17
|
+
|
|
18
|
+
### Added
|
|
19
|
+
- `Queue#capacity`, `Queue#remaining_capacity`, `Stack#capacity`, `Stack#remaining_capacity` — capacity introspection. `capacity` returns the configured limit (or `nil` for unlimited); `remaining_capacity` returns the number of additional items that can still be accepted (or `nil` for unlimited, `0` when full). Mutex-synchronized for consistent reads.
|
|
20
|
+
|
|
10
21
|
## [0.5.0] - 2026-04-16
|
|
11
22
|
|
|
12
23
|
### Added
|
data/README.md
CHANGED
|
@@ -4,6 +4,8 @@
|
|
|
4
4
|
[](https://rubygems.org/gems/philiprehberger-queue_stack)
|
|
5
5
|
[](https://github.com/philiprehberger/rb-queue-stack/commits/main)
|
|
6
6
|
|
|
7
|
+

|
|
8
|
+
|
|
7
9
|
Thread-safe Queue and Stack with capacity limits and blocking operations
|
|
8
10
|
|
|
9
11
|
## Requirements
|
|
@@ -159,6 +161,37 @@ q.full? # => true
|
|
|
159
161
|
# enqueue blocks until space is available
|
|
160
162
|
```
|
|
161
163
|
|
|
164
|
+
### Capacity
|
|
165
|
+
|
|
166
|
+
Read the configured capacity and the number of additional items that can be
|
|
167
|
+
accepted. Both return `nil` for unbounded containers; `remaining_capacity`
|
|
168
|
+
returns `0` when full. Useful for sizing batches or backpressure decisions.
|
|
169
|
+
|
|
170
|
+
```ruby
|
|
171
|
+
q = Philiprehberger::QueueStack::Queue.new(capacity: 100)
|
|
172
|
+
q.capacity # => 100
|
|
173
|
+
q.remaining_capacity # => 100
|
|
174
|
+
50.times { |i| q.enqueue(i) }
|
|
175
|
+
q.remaining_capacity # => 50
|
|
176
|
+
|
|
177
|
+
batch_size = [items.length, q.remaining_capacity].min
|
|
178
|
+
```
|
|
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
|
+
|
|
162
195
|
## API
|
|
163
196
|
|
|
164
197
|
### `Queue`
|
|
@@ -167,8 +200,10 @@ q.full? # => true
|
|
|
167
200
|
|--------|-------------|
|
|
168
201
|
| `.new(capacity:)` | Create a queue with optional capacity limit |
|
|
169
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) |
|
|
170
204
|
| `#try_enqueue(item, timeout: nil)` | Non-blocking enqueue, returns true/false (waits up to timeout if given) |
|
|
171
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) |
|
|
172
207
|
| `#dequeue_if { \|item\| ... }` | Remove and return the front item only if the block is truthy (non-blocking) |
|
|
173
208
|
| `#try_dequeue(timeout:)` | Dequeue with timeout, returns nil on timeout |
|
|
174
209
|
| `#clear` | Remove all items without returning them |
|
|
@@ -182,6 +217,8 @@ q.full? # => true
|
|
|
182
217
|
| `#size` | Number of items |
|
|
183
218
|
| `#empty?` | Whether the queue is empty |
|
|
184
219
|
| `#full?` | Whether the queue is at capacity |
|
|
220
|
+
| `#capacity` | Configured capacity, or `nil` for an unlimited queue |
|
|
221
|
+
| `#remaining_capacity` | Items the queue can still accept (`nil` for unlimited, `0` when full) |
|
|
185
222
|
|
|
186
223
|
### `Stack`
|
|
187
224
|
|
|
@@ -189,8 +226,10 @@ q.full? # => true
|
|
|
189
226
|
|--------|-------------|
|
|
190
227
|
| `.new(capacity:)` | Create a stack with optional capacity limit |
|
|
191
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) |
|
|
192
230
|
| `#try_push(item, timeout: nil)` | Non-blocking push, returns true/false (waits up to timeout if given) |
|
|
193
231
|
| `#pop` | Remove and return top item (blocks if empty) |
|
|
232
|
+
| `#pop_batch(max)` | Pop up to `max` items, top first (non-blocking) |
|
|
194
233
|
| `#pop_if { \|item\| ... }` | Remove and return the top item only if the block is truthy (non-blocking) |
|
|
195
234
|
| `#try_pop(timeout:)` | Pop with timeout, returns nil on timeout |
|
|
196
235
|
| `#clear` | Remove all items without returning them |
|
|
@@ -203,6 +242,8 @@ q.full? # => true
|
|
|
203
242
|
| `#size` | Number of items |
|
|
204
243
|
| `#empty?` | Whether the stack is empty |
|
|
205
244
|
| `#full?` | Whether the stack is at capacity |
|
|
245
|
+
| `#capacity` | Configured capacity, or `nil` for an unlimited stack |
|
|
246
|
+
| `#remaining_capacity` | Items the stack can still accept (`nil` for unlimited, `0` when full) |
|
|
206
247
|
|
|
207
248
|
## Development
|
|
208
249
|
|
|
@@ -225,6 +225,68 @@ module Philiprehberger
|
|
|
225
225
|
def full?
|
|
226
226
|
@mutex.synchronize { @capacity ? @items.length >= @capacity : false }
|
|
227
227
|
end
|
|
228
|
+
|
|
229
|
+
# The configured capacity, or +nil+ for an unlimited queue.
|
|
230
|
+
#
|
|
231
|
+
# @return [Integer, nil]
|
|
232
|
+
def capacity
|
|
233
|
+
@mutex.synchronize { @capacity }
|
|
234
|
+
end
|
|
235
|
+
|
|
236
|
+
# Number of additional items the queue can accept before it is full.
|
|
237
|
+
#
|
|
238
|
+
# Returns +nil+ for unlimited queues. For bounded queues returns
|
|
239
|
+
# +capacity - size+, clamped to a minimum of 0.
|
|
240
|
+
#
|
|
241
|
+
# @return [Integer, nil]
|
|
242
|
+
def remaining_capacity
|
|
243
|
+
@mutex.synchronize do
|
|
244
|
+
next nil unless @capacity
|
|
245
|
+
|
|
246
|
+
[@capacity - @items.length, 0].max
|
|
247
|
+
end
|
|
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
|
|
228
290
|
end
|
|
229
291
|
end
|
|
230
292
|
end
|
|
@@ -213,6 +213,67 @@ module Philiprehberger
|
|
|
213
213
|
def full?
|
|
214
214
|
@mutex.synchronize { @capacity ? @items.length >= @capacity : false }
|
|
215
215
|
end
|
|
216
|
+
|
|
217
|
+
# The configured capacity, or +nil+ for an unlimited stack.
|
|
218
|
+
#
|
|
219
|
+
# @return [Integer, nil]
|
|
220
|
+
def capacity
|
|
221
|
+
@mutex.synchronize { @capacity }
|
|
222
|
+
end
|
|
223
|
+
|
|
224
|
+
# Number of additional items the stack can accept before it is full.
|
|
225
|
+
#
|
|
226
|
+
# Returns +nil+ for unlimited stacks. For bounded stacks returns
|
|
227
|
+
# +capacity - size+, clamped to a minimum of 0.
|
|
228
|
+
#
|
|
229
|
+
# @return [Integer, nil]
|
|
230
|
+
def remaining_capacity
|
|
231
|
+
@mutex.synchronize do
|
|
232
|
+
next nil unless @capacity
|
|
233
|
+
|
|
234
|
+
[@capacity - @items.length, 0].max
|
|
235
|
+
end
|
|
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
|
|
216
277
|
end
|
|
217
278
|
end
|
|
218
279
|
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.
|
|
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-
|
|
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
|