philiprehberger-queue_stack 0.1.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 +7 -0
- data/CHANGELOG.md +20 -0
- data/LICENSE +21 -0
- data/README.md +117 -0
- data/lib/philiprehberger/queue_stack/queue.rb +95 -0
- data/lib/philiprehberger/queue_stack/stack.rb +95 -0
- data/lib/philiprehberger/queue_stack/version.rb +7 -0
- data/lib/philiprehberger/queue_stack.rb +11 -0
- metadata +57 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: bb3c47dcfc192e615cba4fd45499c4795e9e75709dc5c2e3f49558e24f66079f
|
|
4
|
+
data.tar.gz: 47622c2ace206565ca68368c15c8b8145a825138cae4125b8fe65781938a36b2
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: a022329cdb118ca730b1ff1587bd3347cc5c42a5531f946e28a4cd660cce234ab38b0436afa3cdcb8d956afd571d97f7aeaf4db7465cad4df0bac33a90df5e54
|
|
7
|
+
data.tar.gz: 0b8843ed669a90e5e3f906fe64e5df242298d6a8d2e33a8206e2830dc59da164a112af8b2c1c0fb072b0c7c38a945777e826729aac2c4ef79ffbc9600494f02d
|
data/CHANGELOG.md
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this gem will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [Unreleased]
|
|
9
|
+
|
|
10
|
+
## [0.1.0] - 2026-03-22
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
- Initial release
|
|
14
|
+
- Thread-safe FIFO Queue with enqueue, dequeue, peek, and size
|
|
15
|
+
- Thread-safe LIFO Stack with push, pop, peek, and size
|
|
16
|
+
- Configurable capacity limits for both Queue and Stack
|
|
17
|
+
- Blocking operations that wait when empty or at capacity
|
|
18
|
+
- Timeout-based try_dequeue and try_pop operations
|
|
19
|
+
- Empty and full status checks
|
|
20
|
+
- Mutex and ConditionVariable for thread-safe access
|
data/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 philiprehberger
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
# philiprehberger-queue_stack
|
|
2
|
+
|
|
3
|
+
[](https://github.com/philiprehberger/rb-queue-stack/actions/workflows/ci.yml)
|
|
4
|
+
[](https://rubygems.org/gems/philiprehberger-queue_stack)
|
|
5
|
+
[](LICENSE)
|
|
6
|
+
|
|
7
|
+
Thread-safe Queue and Stack with capacity limits and blocking operations
|
|
8
|
+
|
|
9
|
+
## Requirements
|
|
10
|
+
|
|
11
|
+
- Ruby >= 3.1
|
|
12
|
+
|
|
13
|
+
## Installation
|
|
14
|
+
|
|
15
|
+
Add to your Gemfile:
|
|
16
|
+
|
|
17
|
+
```ruby
|
|
18
|
+
gem 'philiprehberger-queue_stack'
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Or install directly:
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
gem install philiprehberger-queue_stack
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Usage
|
|
28
|
+
|
|
29
|
+
```ruby
|
|
30
|
+
require 'philiprehberger/queue_stack'
|
|
31
|
+
|
|
32
|
+
q = Philiprehberger::QueueStack::Queue.new(capacity: 100)
|
|
33
|
+
q.enqueue('task')
|
|
34
|
+
item = q.dequeue # => 'task'
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
### Queue (FIFO)
|
|
38
|
+
|
|
39
|
+
```ruby
|
|
40
|
+
q = Philiprehberger::QueueStack::Queue.new(capacity: 10)
|
|
41
|
+
q.enqueue('first')
|
|
42
|
+
q.enqueue('second')
|
|
43
|
+
q.dequeue # => 'first'
|
|
44
|
+
q.peek # => 'second'
|
|
45
|
+
q.size # => 1
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### Stack (LIFO)
|
|
49
|
+
|
|
50
|
+
```ruby
|
|
51
|
+
s = Philiprehberger::QueueStack::Stack.new(capacity: 10)
|
|
52
|
+
s.push('first')
|
|
53
|
+
s.push('second')
|
|
54
|
+
s.pop # => 'second'
|
|
55
|
+
s.peek # => 'first'
|
|
56
|
+
s.size # => 1
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### Blocking with Timeout
|
|
60
|
+
|
|
61
|
+
```ruby
|
|
62
|
+
q = Philiprehberger::QueueStack::Queue.new
|
|
63
|
+
item = q.try_dequeue(timeout: 5) # waits up to 5 seconds
|
|
64
|
+
|
|
65
|
+
s = Philiprehberger::QueueStack::Stack.new
|
|
66
|
+
item = s.try_pop(timeout: 5) # waits up to 5 seconds
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### Capacity Limits
|
|
70
|
+
|
|
71
|
+
```ruby
|
|
72
|
+
q = Philiprehberger::QueueStack::Queue.new(capacity: 3)
|
|
73
|
+
q.full? # => false
|
|
74
|
+
3.times { |i| q.enqueue(i) }
|
|
75
|
+
q.full? # => true
|
|
76
|
+
# enqueue blocks until space is available
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## API
|
|
80
|
+
|
|
81
|
+
### `Queue`
|
|
82
|
+
|
|
83
|
+
| Method | Description |
|
|
84
|
+
|--------|-------------|
|
|
85
|
+
| `.new(capacity:)` | Create a queue with optional capacity limit |
|
|
86
|
+
| `#enqueue(item)` | Add item to back (blocks if full) |
|
|
87
|
+
| `#dequeue` | Remove and return front item (blocks if empty) |
|
|
88
|
+
| `#try_dequeue(timeout:)` | Dequeue with timeout, returns nil on timeout |
|
|
89
|
+
| `#peek` | View front item without removing |
|
|
90
|
+
| `#size` | Number of items |
|
|
91
|
+
| `#empty?` | Whether the queue is empty |
|
|
92
|
+
| `#full?` | Whether the queue is at capacity |
|
|
93
|
+
|
|
94
|
+
### `Stack`
|
|
95
|
+
|
|
96
|
+
| Method | Description |
|
|
97
|
+
|--------|-------------|
|
|
98
|
+
| `.new(capacity:)` | Create a stack with optional capacity limit |
|
|
99
|
+
| `#push(item)` | Push item on top (blocks if full) |
|
|
100
|
+
| `#pop` | Remove and return top item (blocks if empty) |
|
|
101
|
+
| `#try_pop(timeout:)` | Pop with timeout, returns nil on timeout |
|
|
102
|
+
| `#peek` | View top item without removing |
|
|
103
|
+
| `#size` | Number of items |
|
|
104
|
+
| `#empty?` | Whether the stack is empty |
|
|
105
|
+
| `#full?` | Whether the stack is at capacity |
|
|
106
|
+
|
|
107
|
+
## Development
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
bundle install
|
|
111
|
+
bundle exec rspec # Run tests
|
|
112
|
+
bundle exec rubocop # Check code style
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
## License
|
|
116
|
+
|
|
117
|
+
MIT
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Philiprehberger
|
|
4
|
+
module QueueStack
|
|
5
|
+
# Thread-safe FIFO queue with optional capacity limit and blocking operations.
|
|
6
|
+
#
|
|
7
|
+
# @example
|
|
8
|
+
# q = Queue.new(capacity: 10)
|
|
9
|
+
# q.enqueue('item')
|
|
10
|
+
# q.dequeue # => 'item'
|
|
11
|
+
class Queue
|
|
12
|
+
# Create a new queue.
|
|
13
|
+
#
|
|
14
|
+
# @param capacity [Integer, nil] maximum number of items (nil for unlimited)
|
|
15
|
+
def initialize(capacity: nil)
|
|
16
|
+
@items = []
|
|
17
|
+
@capacity = capacity
|
|
18
|
+
@mutex = Mutex.new
|
|
19
|
+
@not_empty = ConditionVariable.new
|
|
20
|
+
@not_full = ConditionVariable.new
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# Add an item to the back of the queue. Blocks if at capacity.
|
|
24
|
+
#
|
|
25
|
+
# @param item [Object] the item to enqueue
|
|
26
|
+
# @return [void]
|
|
27
|
+
def enqueue(item)
|
|
28
|
+
@mutex.synchronize do
|
|
29
|
+
@not_full.wait(@mutex) while @capacity && @items.length >= @capacity
|
|
30
|
+
@items.push(item)
|
|
31
|
+
@not_empty.signal
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# Remove and return the front item. Blocks if empty.
|
|
36
|
+
#
|
|
37
|
+
# @return [Object] the dequeued item
|
|
38
|
+
def dequeue
|
|
39
|
+
@mutex.synchronize do
|
|
40
|
+
@not_empty.wait(@mutex) while @items.empty?
|
|
41
|
+
item = @items.shift
|
|
42
|
+
@not_full.signal
|
|
43
|
+
item
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# Try to dequeue an item with a timeout.
|
|
48
|
+
#
|
|
49
|
+
# @param timeout [Numeric] seconds to wait
|
|
50
|
+
# @return [Object, nil] the dequeued item or nil on timeout
|
|
51
|
+
def try_dequeue(timeout:)
|
|
52
|
+
deadline = Time.now + timeout
|
|
53
|
+
@mutex.synchronize do
|
|
54
|
+
while @items.empty?
|
|
55
|
+
remaining = deadline - Time.now
|
|
56
|
+
return nil if remaining <= 0
|
|
57
|
+
|
|
58
|
+
@not_empty.wait(@mutex, remaining)
|
|
59
|
+
end
|
|
60
|
+
item = @items.shift
|
|
61
|
+
@not_full.signal
|
|
62
|
+
item
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
# Peek at the front item without removing it.
|
|
67
|
+
#
|
|
68
|
+
# @return [Object, nil] the front item or nil if empty
|
|
69
|
+
def peek
|
|
70
|
+
@mutex.synchronize { @items.first }
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
# Return the number of items in the queue.
|
|
74
|
+
#
|
|
75
|
+
# @return [Integer]
|
|
76
|
+
def size
|
|
77
|
+
@mutex.synchronize { @items.length }
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
# Whether the queue is empty.
|
|
81
|
+
#
|
|
82
|
+
# @return [Boolean]
|
|
83
|
+
def empty?
|
|
84
|
+
@mutex.synchronize { @items.empty? }
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# Whether the queue is at capacity.
|
|
88
|
+
#
|
|
89
|
+
# @return [Boolean]
|
|
90
|
+
def full?
|
|
91
|
+
@mutex.synchronize { @capacity ? @items.length >= @capacity : false }
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
end
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Philiprehberger
|
|
4
|
+
module QueueStack
|
|
5
|
+
# Thread-safe LIFO stack with optional capacity limit and blocking operations.
|
|
6
|
+
#
|
|
7
|
+
# @example
|
|
8
|
+
# s = Stack.new(capacity: 10)
|
|
9
|
+
# s.push('item')
|
|
10
|
+
# s.pop # => 'item'
|
|
11
|
+
class Stack
|
|
12
|
+
# Create a new stack.
|
|
13
|
+
#
|
|
14
|
+
# @param capacity [Integer, nil] maximum number of items (nil for unlimited)
|
|
15
|
+
def initialize(capacity: nil)
|
|
16
|
+
@items = []
|
|
17
|
+
@capacity = capacity
|
|
18
|
+
@mutex = Mutex.new
|
|
19
|
+
@not_empty = ConditionVariable.new
|
|
20
|
+
@not_full = ConditionVariable.new
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# Push an item onto the top of the stack. Blocks if at capacity.
|
|
24
|
+
#
|
|
25
|
+
# @param item [Object] the item to push
|
|
26
|
+
# @return [void]
|
|
27
|
+
def push(item)
|
|
28
|
+
@mutex.synchronize do
|
|
29
|
+
@not_full.wait(@mutex) while @capacity && @items.length >= @capacity
|
|
30
|
+
@items.push(item)
|
|
31
|
+
@not_empty.signal
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# Pop and return the top item. Blocks if empty.
|
|
36
|
+
#
|
|
37
|
+
# @return [Object] the popped item
|
|
38
|
+
def pop
|
|
39
|
+
@mutex.synchronize do
|
|
40
|
+
@not_empty.wait(@mutex) while @items.empty?
|
|
41
|
+
item = @items.pop
|
|
42
|
+
@not_full.signal
|
|
43
|
+
item
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# Try to pop an item with a timeout.
|
|
48
|
+
#
|
|
49
|
+
# @param timeout [Numeric] seconds to wait
|
|
50
|
+
# @return [Object, nil] the popped item or nil on timeout
|
|
51
|
+
def try_pop(timeout:)
|
|
52
|
+
deadline = Time.now + timeout
|
|
53
|
+
@mutex.synchronize do
|
|
54
|
+
while @items.empty?
|
|
55
|
+
remaining = deadline - Time.now
|
|
56
|
+
return nil if remaining <= 0
|
|
57
|
+
|
|
58
|
+
@not_empty.wait(@mutex, remaining)
|
|
59
|
+
end
|
|
60
|
+
item = @items.pop
|
|
61
|
+
@not_full.signal
|
|
62
|
+
item
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
# Peek at the top item without removing it.
|
|
67
|
+
#
|
|
68
|
+
# @return [Object, nil] the top item or nil if empty
|
|
69
|
+
def peek
|
|
70
|
+
@mutex.synchronize { @items.last }
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
# Return the number of items in the stack.
|
|
74
|
+
#
|
|
75
|
+
# @return [Integer]
|
|
76
|
+
def size
|
|
77
|
+
@mutex.synchronize { @items.length }
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
# Whether the stack is empty.
|
|
81
|
+
#
|
|
82
|
+
# @return [Boolean]
|
|
83
|
+
def empty?
|
|
84
|
+
@mutex.synchronize { @items.empty? }
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# Whether the stack is at capacity.
|
|
88
|
+
#
|
|
89
|
+
# @return [Boolean]
|
|
90
|
+
def full?
|
|
91
|
+
@mutex.synchronize { @capacity ? @items.length >= @capacity : false }
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: philiprehberger-queue_stack
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Philip Rehberger
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2026-03-22 00:00:00.000000000 Z
|
|
12
|
+
dependencies: []
|
|
13
|
+
description: Thread-safe queue and stack data structures with configurable capacity
|
|
14
|
+
limits, blocking enqueue/dequeue with timeouts, and peek operations. Uses Mutex
|
|
15
|
+
and ConditionVariable for safe concurrent access.
|
|
16
|
+
email:
|
|
17
|
+
- me@philiprehberger.com
|
|
18
|
+
executables: []
|
|
19
|
+
extensions: []
|
|
20
|
+
extra_rdoc_files: []
|
|
21
|
+
files:
|
|
22
|
+
- CHANGELOG.md
|
|
23
|
+
- LICENSE
|
|
24
|
+
- README.md
|
|
25
|
+
- lib/philiprehberger/queue_stack.rb
|
|
26
|
+
- lib/philiprehberger/queue_stack/queue.rb
|
|
27
|
+
- lib/philiprehberger/queue_stack/stack.rb
|
|
28
|
+
- lib/philiprehberger/queue_stack/version.rb
|
|
29
|
+
homepage: https://github.com/philiprehberger/rb-queue-stack
|
|
30
|
+
licenses:
|
|
31
|
+
- MIT
|
|
32
|
+
metadata:
|
|
33
|
+
homepage_uri: https://github.com/philiprehberger/rb-queue-stack
|
|
34
|
+
source_code_uri: https://github.com/philiprehberger/rb-queue-stack
|
|
35
|
+
changelog_uri: https://github.com/philiprehberger/rb-queue-stack/blob/main/CHANGELOG.md
|
|
36
|
+
bug_tracker_uri: https://github.com/philiprehberger/rb-queue-stack/issues
|
|
37
|
+
rubygems_mfa_required: 'true'
|
|
38
|
+
post_install_message:
|
|
39
|
+
rdoc_options: []
|
|
40
|
+
require_paths:
|
|
41
|
+
- lib
|
|
42
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
43
|
+
requirements:
|
|
44
|
+
- - ">="
|
|
45
|
+
- !ruby/object:Gem::Version
|
|
46
|
+
version: 3.1.0
|
|
47
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
48
|
+
requirements:
|
|
49
|
+
- - ">="
|
|
50
|
+
- !ruby/object:Gem::Version
|
|
51
|
+
version: '0'
|
|
52
|
+
requirements: []
|
|
53
|
+
rubygems_version: 3.5.22
|
|
54
|
+
signing_key:
|
|
55
|
+
specification_version: 4
|
|
56
|
+
summary: Thread-safe Queue and Stack with capacity limits and blocking operations
|
|
57
|
+
test_files: []
|