Class: Ratomic::Map
- Inherits:
-
Object
- Object
- Ratomic::Map
- Defined in:
- lib/ratomic/map.rb
Overview
A Ractor-shareable concurrent Hash backed by Rust’s DashMap.
Map gives Ruby code a small, Ruby-shaped API over DashMap’s concurrent storage. It is suitable for runtime state with shareable keys and values that are safe to access from multiple Ractors, such as integer counters or immutable offsets.
This is not a full Hash replacement. Iteration and arbitrary mutable object borrowing are intentionally absent.
Some methods on this type hold an internal entry guard while the block runs or while a reference is live. Do not call back into the same map from inside those blocks, and do not hold a reference from #get or #[] while mutating the same key. That can deadlock the underlying DashMap bucket.
Instance Method Summary collapse
-
#[](key) ⇒ Object?
Read a value by
key. -
#[]=(key, value) ⇒ Object
Set a value for
key. -
#add_to_set(key, value) ⇒ Set
Atomically add
valueto a Set bucket forkey. -
#append(key, value) ⇒ Array
Atomically append
valueto an Array bucket forkey. -
#clear ⇒ Ratomic::Map
Remove all entries from the map.
-
#compute(key) {|value| ... } ⇒ Object
Atomically compute and store a value for
key. -
#decrement(key, by = 1) ⇒ Numeric
Atomically decrement the numeric value for
key. -
#delete(key) ⇒ Object?
Remove
keyand return its previous value. -
#empty? ⇒ Boolean
Check whether the map currently has no entries.
-
#fetch(key, default = UNDEFINED) {|key| ... } ⇒ Object
Fetch a value by
key. -
#fetch_and_modify(key) {|value| ... } ⇒ void
Replace the existing value for
keywith the block return value. -
#fetch_or_store(key) ⇒ Object
Return the existing value for
key, or atomically store the block result. -
#get(key) ⇒ Object?
Read a value by
key. -
#include?(key) ⇒ Boolean
(also: #member?)
Alias for #key?.
-
#increment(key, by = 1) ⇒ Numeric
Atomically increment the numeric value for
key. -
#key?(key) ⇒ Boolean
Check whether
keycurrently exists in the map. -
#length ⇒ Integer
Alias for #size.
-
#set(key, value) ⇒ Object
Set a value for
key. -
#size ⇒ Integer
Return the current number of entries.
-
#upsert(key, initial) {|value| ... } ⇒ Object
Atomically insert
initialfor a missing key, or update an existing value.
Instance Method Details
#[](key) ⇒ Object?
Read a value by key.
Missing keys currently return nil, so storing nil is ambiguous.
229 230 231 |
# File 'lib/ratomic/map.rb', line 229 def [](key) get(key) end |
#[]=(key, value) ⇒ Object
Set a value for key.
In assignment form, Ruby returns the assigned value.
211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 |
# File 'lib/ratomic/map.rb', line 211 class Map # Set a value for +key+. # # In assignment form, Ruby returns the assigned value. # # @param key [Object] key to write # @param value [Object] value to store # @return [Object] the assigned value def []=(key, value) set(key, value) end # Read a value by +key+. # # Missing keys currently return nil, so storing nil is ambiguous. # # @param key [Object] lookup key # @return [Object, nil] the stored value, or nil when the key is missing def [](key) get(key) end # Fetch a value by +key+. # # Unlike #[], this distinguishes missing keys from explicit nil values. # # @param key [Object] lookup key # @param default [Object] fallback value to return when the key is missing # @yieldparam key [Object] the missing key # @return [Object] the found value, default, or block result # @raise [KeyError] if +key+ is missing and no default or block is provided def fetch(key, default = UNDEFINED) return get(key) if key?(key) return yield key if block_given? return default unless default.equal?(UNDEFINED) raise KeyError, "key not found: #{key.inspect}" end # Atomically increment the numeric value for +key+. # # Missing keys start at zero. Existing non-numeric values raise TypeError # and are left unchanged. This uses a native update path and is the preferred # counter primitive for Ractor-heavy workloads. # # @param key [Object] counter key to increment # @param by [Numeric] amount to add # @return [Numeric] the newly stored value # @raise [TypeError] if +by+ or the existing value is not numeric def increment(key, by = 1) raise TypeError, "amount must be numeric: #{by.inspect}" unless by.is_a?(Numeric) __increment_numeric(key, by) end # Atomically decrement the numeric value for +key+. # # Missing keys start at zero. # # @param key [Object] counter key to decrement # @param by [Numeric] amount to subtract # @return [Numeric] the newly stored value # @raise [TypeError] if +by+ or the existing value is not numeric def decrement(key, by = 1) raise TypeError, "amount must be numeric: #{by.inspect}" unless by.is_a?(Numeric) increment(key, -by) end # Atomically append +value+ to an Array bucket for +key+. # # The stored Array is replaced rather than mutated in place. # # @param key [Object] bucket key to append into # @param value [Object] value to append # @return [Array] the newly stored frozen Array # @raise [TypeError] if the existing value is not an Array def append(key, value) missing = !key?(key) compute(key) do |old_value| if old_value.nil? && missing [value].freeze else unless old_value.is_a?(Array) raise TypeError, "existing value for #{key.inspect} must be an Array: #{old_value.inspect}" end (old_value + [value]).freeze end end end # Atomically add +value+ to a Set bucket for +key+. # # The stored Set is replaced rather than mutated in place. # # @param key [Object] bucket key to update # @param value [Object] value to add to the set # @return [Set] the newly stored frozen Set # @raise [TypeError] if the existing value is not a Set def add_to_set(key, value) missing = !key?(key) compute(key) do |old_value| if old_value.nil? && missing Set[value].freeze else unless old_value.is_a?(Set) raise TypeError, "existing value for #{key.inspect} must be a Set: #{old_value.inspect}" end (old_value | [value]).freeze end end end # Alias for #size. # # @return [Integer] the current number of entries def length size end # Check whether the map currently has no entries. # # @return [Boolean] true when the map currently has no entries def empty? size.zero? end # Alias for #key?. # # @param key [Object] lookup key # @return [Boolean] true when the key currently exists def include?(key) key?(key) end alias member? include? end |
#add_to_set(key, value) ⇒ Set
Atomically add value to a Set bucket for key.
The stored Set is replaced rather than mutated in place.
211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 |
# File 'lib/ratomic/map.rb', line 211 class Map # Set a value for +key+. # # In assignment form, Ruby returns the assigned value. # # @param key [Object] key to write # @param value [Object] value to store # @return [Object] the assigned value def []=(key, value) set(key, value) end # Read a value by +key+. # # Missing keys currently return nil, so storing nil is ambiguous. # # @param key [Object] lookup key # @return [Object, nil] the stored value, or nil when the key is missing def [](key) get(key) end # Fetch a value by +key+. # # Unlike #[], this distinguishes missing keys from explicit nil values. # # @param key [Object] lookup key # @param default [Object] fallback value to return when the key is missing # @yieldparam key [Object] the missing key # @return [Object] the found value, default, or block result # @raise [KeyError] if +key+ is missing and no default or block is provided def fetch(key, default = UNDEFINED) return get(key) if key?(key) return yield key if block_given? return default unless default.equal?(UNDEFINED) raise KeyError, "key not found: #{key.inspect}" end # Atomically increment the numeric value for +key+. # # Missing keys start at zero. Existing non-numeric values raise TypeError # and are left unchanged. This uses a native update path and is the preferred # counter primitive for Ractor-heavy workloads. # # @param key [Object] counter key to increment # @param by [Numeric] amount to add # @return [Numeric] the newly stored value # @raise [TypeError] if +by+ or the existing value is not numeric def increment(key, by = 1) raise TypeError, "amount must be numeric: #{by.inspect}" unless by.is_a?(Numeric) __increment_numeric(key, by) end # Atomically decrement the numeric value for +key+. # # Missing keys start at zero. # # @param key [Object] counter key to decrement # @param by [Numeric] amount to subtract # @return [Numeric] the newly stored value # @raise [TypeError] if +by+ or the existing value is not numeric def decrement(key, by = 1) raise TypeError, "amount must be numeric: #{by.inspect}" unless by.is_a?(Numeric) increment(key, -by) end # Atomically append +value+ to an Array bucket for +key+. # # The stored Array is replaced rather than mutated in place. # # @param key [Object] bucket key to append into # @param value [Object] value to append # @return [Array] the newly stored frozen Array # @raise [TypeError] if the existing value is not an Array def append(key, value) missing = !key?(key) compute(key) do |old_value| if old_value.nil? && missing [value].freeze else unless old_value.is_a?(Array) raise TypeError, "existing value for #{key.inspect} must be an Array: #{old_value.inspect}" end (old_value + [value]).freeze end end end # Atomically add +value+ to a Set bucket for +key+. # # The stored Set is replaced rather than mutated in place. # # @param key [Object] bucket key to update # @param value [Object] value to add to the set # @return [Set] the newly stored frozen Set # @raise [TypeError] if the existing value is not a Set def add_to_set(key, value) missing = !key?(key) compute(key) do |old_value| if old_value.nil? && missing Set[value].freeze else unless old_value.is_a?(Set) raise TypeError, "existing value for #{key.inspect} must be a Set: #{old_value.inspect}" end (old_value | [value]).freeze end end end # Alias for #size. # # @return [Integer] the current number of entries def length size end # Check whether the map currently has no entries. # # @return [Boolean] true when the map currently has no entries def empty? size.zero? end # Alias for #key?. # # @param key [Object] lookup key # @return [Boolean] true when the key currently exists def include?(key) key?(key) end alias member? include? end |
#append(key, value) ⇒ Array
Atomically append value to an Array bucket for key.
The stored Array is replaced rather than mutated in place.
211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 |
# File 'lib/ratomic/map.rb', line 211 class Map # Set a value for +key+. # # In assignment form, Ruby returns the assigned value. # # @param key [Object] key to write # @param value [Object] value to store # @return [Object] the assigned value def []=(key, value) set(key, value) end # Read a value by +key+. # # Missing keys currently return nil, so storing nil is ambiguous. # # @param key [Object] lookup key # @return [Object, nil] the stored value, or nil when the key is missing def [](key) get(key) end # Fetch a value by +key+. # # Unlike #[], this distinguishes missing keys from explicit nil values. # # @param key [Object] lookup key # @param default [Object] fallback value to return when the key is missing # @yieldparam key [Object] the missing key # @return [Object] the found value, default, or block result # @raise [KeyError] if +key+ is missing and no default or block is provided def fetch(key, default = UNDEFINED) return get(key) if key?(key) return yield key if block_given? return default unless default.equal?(UNDEFINED) raise KeyError, "key not found: #{key.inspect}" end # Atomically increment the numeric value for +key+. # # Missing keys start at zero. Existing non-numeric values raise TypeError # and are left unchanged. This uses a native update path and is the preferred # counter primitive for Ractor-heavy workloads. # # @param key [Object] counter key to increment # @param by [Numeric] amount to add # @return [Numeric] the newly stored value # @raise [TypeError] if +by+ or the existing value is not numeric def increment(key, by = 1) raise TypeError, "amount must be numeric: #{by.inspect}" unless by.is_a?(Numeric) __increment_numeric(key, by) end # Atomically decrement the numeric value for +key+. # # Missing keys start at zero. # # @param key [Object] counter key to decrement # @param by [Numeric] amount to subtract # @return [Numeric] the newly stored value # @raise [TypeError] if +by+ or the existing value is not numeric def decrement(key, by = 1) raise TypeError, "amount must be numeric: #{by.inspect}" unless by.is_a?(Numeric) increment(key, -by) end # Atomically append +value+ to an Array bucket for +key+. # # The stored Array is replaced rather than mutated in place. # # @param key [Object] bucket key to append into # @param value [Object] value to append # @return [Array] the newly stored frozen Array # @raise [TypeError] if the existing value is not an Array def append(key, value) missing = !key?(key) compute(key) do |old_value| if old_value.nil? && missing [value].freeze else unless old_value.is_a?(Array) raise TypeError, "existing value for #{key.inspect} must be an Array: #{old_value.inspect}" end (old_value + [value]).freeze end end end # Atomically add +value+ to a Set bucket for +key+. # # The stored Set is replaced rather than mutated in place. # # @param key [Object] bucket key to update # @param value [Object] value to add to the set # @return [Set] the newly stored frozen Set # @raise [TypeError] if the existing value is not a Set def add_to_set(key, value) missing = !key?(key) compute(key) do |old_value| if old_value.nil? && missing Set[value].freeze else unless old_value.is_a?(Set) raise TypeError, "existing value for #{key.inspect} must be a Set: #{old_value.inspect}" end (old_value | [value]).freeze end end end # Alias for #size. # # @return [Integer] the current number of entries def length size end # Check whether the map currently has no entries. # # @return [Boolean] true when the map currently has no entries def empty? size.zero? end # Alias for #key?. # # @param key [Object] lookup key # @return [Boolean] true when the key currently exists def include?(key) key?(key) end alias member? include? end |
#clear ⇒ Ratomic::Map
Remove all entries from the map.
211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 |
# File 'lib/ratomic/map.rb', line 211 class Map # Set a value for +key+. # # In assignment form, Ruby returns the assigned value. # # @param key [Object] key to write # @param value [Object] value to store # @return [Object] the assigned value def []=(key, value) set(key, value) end # Read a value by +key+. # # Missing keys currently return nil, so storing nil is ambiguous. # # @param key [Object] lookup key # @return [Object, nil] the stored value, or nil when the key is missing def [](key) get(key) end # Fetch a value by +key+. # # Unlike #[], this distinguishes missing keys from explicit nil values. # # @param key [Object] lookup key # @param default [Object] fallback value to return when the key is missing # @yieldparam key [Object] the missing key # @return [Object] the found value, default, or block result # @raise [KeyError] if +key+ is missing and no default or block is provided def fetch(key, default = UNDEFINED) return get(key) if key?(key) return yield key if block_given? return default unless default.equal?(UNDEFINED) raise KeyError, "key not found: #{key.inspect}" end # Atomically increment the numeric value for +key+. # # Missing keys start at zero. Existing non-numeric values raise TypeError # and are left unchanged. This uses a native update path and is the preferred # counter primitive for Ractor-heavy workloads. # # @param key [Object] counter key to increment # @param by [Numeric] amount to add # @return [Numeric] the newly stored value # @raise [TypeError] if +by+ or the existing value is not numeric def increment(key, by = 1) raise TypeError, "amount must be numeric: #{by.inspect}" unless by.is_a?(Numeric) __increment_numeric(key, by) end # Atomically decrement the numeric value for +key+. # # Missing keys start at zero. # # @param key [Object] counter key to decrement # @param by [Numeric] amount to subtract # @return [Numeric] the newly stored value # @raise [TypeError] if +by+ or the existing value is not numeric def decrement(key, by = 1) raise TypeError, "amount must be numeric: #{by.inspect}" unless by.is_a?(Numeric) increment(key, -by) end # Atomically append +value+ to an Array bucket for +key+. # # The stored Array is replaced rather than mutated in place. # # @param key [Object] bucket key to append into # @param value [Object] value to append # @return [Array] the newly stored frozen Array # @raise [TypeError] if the existing value is not an Array def append(key, value) missing = !key?(key) compute(key) do |old_value| if old_value.nil? && missing [value].freeze else unless old_value.is_a?(Array) raise TypeError, "existing value for #{key.inspect} must be an Array: #{old_value.inspect}" end (old_value + [value]).freeze end end end # Atomically add +value+ to a Set bucket for +key+. # # The stored Set is replaced rather than mutated in place. # # @param key [Object] bucket key to update # @param value [Object] value to add to the set # @return [Set] the newly stored frozen Set # @raise [TypeError] if the existing value is not a Set def add_to_set(key, value) missing = !key?(key) compute(key) do |old_value| if old_value.nil? && missing Set[value].freeze else unless old_value.is_a?(Set) raise TypeError, "existing value for #{key.inspect} must be a Set: #{old_value.inspect}" end (old_value | [value]).freeze end end end # Alias for #size. # # @return [Integer] the current number of entries def length size end # Check whether the map currently has no entries. # # @return [Boolean] true when the map currently has no entries def empty? size.zero? end # Alias for #key?. # # @param key [Object] lookup key # @return [Boolean] true when the key currently exists def include?(key) key?(key) end alias member? include? end |
#compute(key) {|value| ... } ⇒ Object
Atomically compute and store a value for key.
If key exists, yields the current value. If key is missing, yields nil. The block return value is stored and returned.
The operation is atomic for the key. The block runs while the map entry is locked, so avoid using this method for Ractor-hot loops or calling back into the same map from inside the block. Prefer native update helpers such as #increment when they fit the workflow.
Never call this method from inside another live reference to the same entry or from a block that already holds the same map’s guard. That can deadlock the bucket.
If the block raises, the previous value is preserved. If the key was missing, no entry is inserted.
211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 |
# File 'lib/ratomic/map.rb', line 211 class Map # Set a value for +key+. # # In assignment form, Ruby returns the assigned value. # # @param key [Object] key to write # @param value [Object] value to store # @return [Object] the assigned value def []=(key, value) set(key, value) end # Read a value by +key+. # # Missing keys currently return nil, so storing nil is ambiguous. # # @param key [Object] lookup key # @return [Object, nil] the stored value, or nil when the key is missing def [](key) get(key) end # Fetch a value by +key+. # # Unlike #[], this distinguishes missing keys from explicit nil values. # # @param key [Object] lookup key # @param default [Object] fallback value to return when the key is missing # @yieldparam key [Object] the missing key # @return [Object] the found value, default, or block result # @raise [KeyError] if +key+ is missing and no default or block is provided def fetch(key, default = UNDEFINED) return get(key) if key?(key) return yield key if block_given? return default unless default.equal?(UNDEFINED) raise KeyError, "key not found: #{key.inspect}" end # Atomically increment the numeric value for +key+. # # Missing keys start at zero. Existing non-numeric values raise TypeError # and are left unchanged. This uses a native update path and is the preferred # counter primitive for Ractor-heavy workloads. # # @param key [Object] counter key to increment # @param by [Numeric] amount to add # @return [Numeric] the newly stored value # @raise [TypeError] if +by+ or the existing value is not numeric def increment(key, by = 1) raise TypeError, "amount must be numeric: #{by.inspect}" unless by.is_a?(Numeric) __increment_numeric(key, by) end # Atomically decrement the numeric value for +key+. # # Missing keys start at zero. # # @param key [Object] counter key to decrement # @param by [Numeric] amount to subtract # @return [Numeric] the newly stored value # @raise [TypeError] if +by+ or the existing value is not numeric def decrement(key, by = 1) raise TypeError, "amount must be numeric: #{by.inspect}" unless by.is_a?(Numeric) increment(key, -by) end # Atomically append +value+ to an Array bucket for +key+. # # The stored Array is replaced rather than mutated in place. # # @param key [Object] bucket key to append into # @param value [Object] value to append # @return [Array] the newly stored frozen Array # @raise [TypeError] if the existing value is not an Array def append(key, value) missing = !key?(key) compute(key) do |old_value| if old_value.nil? && missing [value].freeze else unless old_value.is_a?(Array) raise TypeError, "existing value for #{key.inspect} must be an Array: #{old_value.inspect}" end (old_value + [value]).freeze end end end # Atomically add +value+ to a Set bucket for +key+. # # The stored Set is replaced rather than mutated in place. # # @param key [Object] bucket key to update # @param value [Object] value to add to the set # @return [Set] the newly stored frozen Set # @raise [TypeError] if the existing value is not a Set def add_to_set(key, value) missing = !key?(key) compute(key) do |old_value| if old_value.nil? && missing Set[value].freeze else unless old_value.is_a?(Set) raise TypeError, "existing value for #{key.inspect} must be a Set: #{old_value.inspect}" end (old_value | [value]).freeze end end end # Alias for #size. # # @return [Integer] the current number of entries def length size end # Check whether the map currently has no entries. # # @return [Boolean] true when the map currently has no entries def empty? size.zero? end # Alias for #key?. # # @param key [Object] lookup key # @return [Boolean] true when the key currently exists def include?(key) key?(key) end alias member? include? end |
#decrement(key, by = 1) ⇒ Numeric
Atomically decrement the numeric value for key.
Missing keys start at zero.
211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 |
# File 'lib/ratomic/map.rb', line 211 class Map # Set a value for +key+. # # In assignment form, Ruby returns the assigned value. # # @param key [Object] key to write # @param value [Object] value to store # @return [Object] the assigned value def []=(key, value) set(key, value) end # Read a value by +key+. # # Missing keys currently return nil, so storing nil is ambiguous. # # @param key [Object] lookup key # @return [Object, nil] the stored value, or nil when the key is missing def [](key) get(key) end # Fetch a value by +key+. # # Unlike #[], this distinguishes missing keys from explicit nil values. # # @param key [Object] lookup key # @param default [Object] fallback value to return when the key is missing # @yieldparam key [Object] the missing key # @return [Object] the found value, default, or block result # @raise [KeyError] if +key+ is missing and no default or block is provided def fetch(key, default = UNDEFINED) return get(key) if key?(key) return yield key if block_given? return default unless default.equal?(UNDEFINED) raise KeyError, "key not found: #{key.inspect}" end # Atomically increment the numeric value for +key+. # # Missing keys start at zero. Existing non-numeric values raise TypeError # and are left unchanged. This uses a native update path and is the preferred # counter primitive for Ractor-heavy workloads. # # @param key [Object] counter key to increment # @param by [Numeric] amount to add # @return [Numeric] the newly stored value # @raise [TypeError] if +by+ or the existing value is not numeric def increment(key, by = 1) raise TypeError, "amount must be numeric: #{by.inspect}" unless by.is_a?(Numeric) __increment_numeric(key, by) end # Atomically decrement the numeric value for +key+. # # Missing keys start at zero. # # @param key [Object] counter key to decrement # @param by [Numeric] amount to subtract # @return [Numeric] the newly stored value # @raise [TypeError] if +by+ or the existing value is not numeric def decrement(key, by = 1) raise TypeError, "amount must be numeric: #{by.inspect}" unless by.is_a?(Numeric) increment(key, -by) end # Atomically append +value+ to an Array bucket for +key+. # # The stored Array is replaced rather than mutated in place. # # @param key [Object] bucket key to append into # @param value [Object] value to append # @return [Array] the newly stored frozen Array # @raise [TypeError] if the existing value is not an Array def append(key, value) missing = !key?(key) compute(key) do |old_value| if old_value.nil? && missing [value].freeze else unless old_value.is_a?(Array) raise TypeError, "existing value for #{key.inspect} must be an Array: #{old_value.inspect}" end (old_value + [value]).freeze end end end # Atomically add +value+ to a Set bucket for +key+. # # The stored Set is replaced rather than mutated in place. # # @param key [Object] bucket key to update # @param value [Object] value to add to the set # @return [Set] the newly stored frozen Set # @raise [TypeError] if the existing value is not a Set def add_to_set(key, value) missing = !key?(key) compute(key) do |old_value| if old_value.nil? && missing Set[value].freeze else unless old_value.is_a?(Set) raise TypeError, "existing value for #{key.inspect} must be a Set: #{old_value.inspect}" end (old_value | [value]).freeze end end end # Alias for #size. # # @return [Integer] the current number of entries def length size end # Check whether the map currently has no entries. # # @return [Boolean] true when the map currently has no entries def empty? size.zero? end # Alias for #key?. # # @param key [Object] lookup key # @return [Boolean] true when the key currently exists def include?(key) key?(key) end alias member? include? end |
#delete(key) ⇒ Object?
Remove key and return its previous value.
Missing keys return nil. Stored nil values also return nil; use #key? before deleting if that distinction matters.
211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 |
# File 'lib/ratomic/map.rb', line 211 class Map # Set a value for +key+. # # In assignment form, Ruby returns the assigned value. # # @param key [Object] key to write # @param value [Object] value to store # @return [Object] the assigned value def []=(key, value) set(key, value) end # Read a value by +key+. # # Missing keys currently return nil, so storing nil is ambiguous. # # @param key [Object] lookup key # @return [Object, nil] the stored value, or nil when the key is missing def [](key) get(key) end # Fetch a value by +key+. # # Unlike #[], this distinguishes missing keys from explicit nil values. # # @param key [Object] lookup key # @param default [Object] fallback value to return when the key is missing # @yieldparam key [Object] the missing key # @return [Object] the found value, default, or block result # @raise [KeyError] if +key+ is missing and no default or block is provided def fetch(key, default = UNDEFINED) return get(key) if key?(key) return yield key if block_given? return default unless default.equal?(UNDEFINED) raise KeyError, "key not found: #{key.inspect}" end # Atomically increment the numeric value for +key+. # # Missing keys start at zero. Existing non-numeric values raise TypeError # and are left unchanged. This uses a native update path and is the preferred # counter primitive for Ractor-heavy workloads. # # @param key [Object] counter key to increment # @param by [Numeric] amount to add # @return [Numeric] the newly stored value # @raise [TypeError] if +by+ or the existing value is not numeric def increment(key, by = 1) raise TypeError, "amount must be numeric: #{by.inspect}" unless by.is_a?(Numeric) __increment_numeric(key, by) end # Atomically decrement the numeric value for +key+. # # Missing keys start at zero. # # @param key [Object] counter key to decrement # @param by [Numeric] amount to subtract # @return [Numeric] the newly stored value # @raise [TypeError] if +by+ or the existing value is not numeric def decrement(key, by = 1) raise TypeError, "amount must be numeric: #{by.inspect}" unless by.is_a?(Numeric) increment(key, -by) end # Atomically append +value+ to an Array bucket for +key+. # # The stored Array is replaced rather than mutated in place. # # @param key [Object] bucket key to append into # @param value [Object] value to append # @return [Array] the newly stored frozen Array # @raise [TypeError] if the existing value is not an Array def append(key, value) missing = !key?(key) compute(key) do |old_value| if old_value.nil? && missing [value].freeze else unless old_value.is_a?(Array) raise TypeError, "existing value for #{key.inspect} must be an Array: #{old_value.inspect}" end (old_value + [value]).freeze end end end # Atomically add +value+ to a Set bucket for +key+. # # The stored Set is replaced rather than mutated in place. # # @param key [Object] bucket key to update # @param value [Object] value to add to the set # @return [Set] the newly stored frozen Set # @raise [TypeError] if the existing value is not a Set def add_to_set(key, value) missing = !key?(key) compute(key) do |old_value| if old_value.nil? && missing Set[value].freeze else unless old_value.is_a?(Set) raise TypeError, "existing value for #{key.inspect} must be a Set: #{old_value.inspect}" end (old_value | [value]).freeze end end end # Alias for #size. # # @return [Integer] the current number of entries def length size end # Check whether the map currently has no entries. # # @return [Boolean] true when the map currently has no entries def empty? size.zero? end # Alias for #key?. # # @param key [Object] lookup key # @return [Boolean] true when the key currently exists def include?(key) key?(key) end alias member? include? end |
#empty? ⇒ Boolean
Check whether the map currently has no entries.
338 339 340 |
# File 'lib/ratomic/map.rb', line 338 def empty? size.zero? end |
#fetch(key, default = UNDEFINED) {|key| ... } ⇒ Object
Fetch a value by key.
Unlike #[], this distinguishes missing keys from explicit nil values.
242 243 244 245 246 247 248 |
# File 'lib/ratomic/map.rb', line 242 def fetch(key, default = UNDEFINED) return get(key) if key?(key) return yield key if block_given? return default unless default.equal?(UNDEFINED) raise KeyError, "key not found: #{key.inspect}" end |
#fetch_and_modify(key) {|value| ... } ⇒ void
This method returns an undefined value.
Replace the existing value for key with the block return value.
TODO: Revisit this name once the Map API settles. Prefer public method names that stay as close as possible to Ruby Hash semantics.
The key must already exist. The operation is atomic for the key. The block runs while the map entry is locked, so avoid using this method for Ractor-hot loops or calling back into the same map from inside the block. If the block raises, the previous value is preserved.
211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 |
# File 'lib/ratomic/map.rb', line 211 class Map # Set a value for +key+. # # In assignment form, Ruby returns the assigned value. # # @param key [Object] key to write # @param value [Object] value to store # @return [Object] the assigned value def []=(key, value) set(key, value) end # Read a value by +key+. # # Missing keys currently return nil, so storing nil is ambiguous. # # @param key [Object] lookup key # @return [Object, nil] the stored value, or nil when the key is missing def [](key) get(key) end # Fetch a value by +key+. # # Unlike #[], this distinguishes missing keys from explicit nil values. # # @param key [Object] lookup key # @param default [Object] fallback value to return when the key is missing # @yieldparam key [Object] the missing key # @return [Object] the found value, default, or block result # @raise [KeyError] if +key+ is missing and no default or block is provided def fetch(key, default = UNDEFINED) return get(key) if key?(key) return yield key if block_given? return default unless default.equal?(UNDEFINED) raise KeyError, "key not found: #{key.inspect}" end # Atomically increment the numeric value for +key+. # # Missing keys start at zero. Existing non-numeric values raise TypeError # and are left unchanged. This uses a native update path and is the preferred # counter primitive for Ractor-heavy workloads. # # @param key [Object] counter key to increment # @param by [Numeric] amount to add # @return [Numeric] the newly stored value # @raise [TypeError] if +by+ or the existing value is not numeric def increment(key, by = 1) raise TypeError, "amount must be numeric: #{by.inspect}" unless by.is_a?(Numeric) __increment_numeric(key, by) end # Atomically decrement the numeric value for +key+. # # Missing keys start at zero. # # @param key [Object] counter key to decrement # @param by [Numeric] amount to subtract # @return [Numeric] the newly stored value # @raise [TypeError] if +by+ or the existing value is not numeric def decrement(key, by = 1) raise TypeError, "amount must be numeric: #{by.inspect}" unless by.is_a?(Numeric) increment(key, -by) end # Atomically append +value+ to an Array bucket for +key+. # # The stored Array is replaced rather than mutated in place. # # @param key [Object] bucket key to append into # @param value [Object] value to append # @return [Array] the newly stored frozen Array # @raise [TypeError] if the existing value is not an Array def append(key, value) missing = !key?(key) compute(key) do |old_value| if old_value.nil? && missing [value].freeze else unless old_value.is_a?(Array) raise TypeError, "existing value for #{key.inspect} must be an Array: #{old_value.inspect}" end (old_value + [value]).freeze end end end # Atomically add +value+ to a Set bucket for +key+. # # The stored Set is replaced rather than mutated in place. # # @param key [Object] bucket key to update # @param value [Object] value to add to the set # @return [Set] the newly stored frozen Set # @raise [TypeError] if the existing value is not a Set def add_to_set(key, value) missing = !key?(key) compute(key) do |old_value| if old_value.nil? && missing Set[value].freeze else unless old_value.is_a?(Set) raise TypeError, "existing value for #{key.inspect} must be a Set: #{old_value.inspect}" end (old_value | [value]).freeze end end end # Alias for #size. # # @return [Integer] the current number of entries def length size end # Check whether the map currently has no entries. # # @return [Boolean] true when the map currently has no entries def empty? size.zero? end # Alias for #key?. # # @param key [Object] lookup key # @return [Boolean] true when the key currently exists def include?(key) key?(key) end alias member? include? end |
#fetch_or_store(key) ⇒ Object
Return the existing value for key, or atomically store the block result.
If key exists, returns the current value and does not yield. If key is missing, yields once, stores the block return value, and returns it.
The operation is atomic for the key. Under contention, only one stored value wins for a missing key. The block runs while the map entry is locked, so avoid using this method for Ractor-hot loops or calling back into the same map from inside the block.
If the block raises, no entry is inserted.
211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 |
# File 'lib/ratomic/map.rb', line 211 class Map # Set a value for +key+. # # In assignment form, Ruby returns the assigned value. # # @param key [Object] key to write # @param value [Object] value to store # @return [Object] the assigned value def []=(key, value) set(key, value) end # Read a value by +key+. # # Missing keys currently return nil, so storing nil is ambiguous. # # @param key [Object] lookup key # @return [Object, nil] the stored value, or nil when the key is missing def [](key) get(key) end # Fetch a value by +key+. # # Unlike #[], this distinguishes missing keys from explicit nil values. # # @param key [Object] lookup key # @param default [Object] fallback value to return when the key is missing # @yieldparam key [Object] the missing key # @return [Object] the found value, default, or block result # @raise [KeyError] if +key+ is missing and no default or block is provided def fetch(key, default = UNDEFINED) return get(key) if key?(key) return yield key if block_given? return default unless default.equal?(UNDEFINED) raise KeyError, "key not found: #{key.inspect}" end # Atomically increment the numeric value for +key+. # # Missing keys start at zero. Existing non-numeric values raise TypeError # and are left unchanged. This uses a native update path and is the preferred # counter primitive for Ractor-heavy workloads. # # @param key [Object] counter key to increment # @param by [Numeric] amount to add # @return [Numeric] the newly stored value # @raise [TypeError] if +by+ or the existing value is not numeric def increment(key, by = 1) raise TypeError, "amount must be numeric: #{by.inspect}" unless by.is_a?(Numeric) __increment_numeric(key, by) end # Atomically decrement the numeric value for +key+. # # Missing keys start at zero. # # @param key [Object] counter key to decrement # @param by [Numeric] amount to subtract # @return [Numeric] the newly stored value # @raise [TypeError] if +by+ or the existing value is not numeric def decrement(key, by = 1) raise TypeError, "amount must be numeric: #{by.inspect}" unless by.is_a?(Numeric) increment(key, -by) end # Atomically append +value+ to an Array bucket for +key+. # # The stored Array is replaced rather than mutated in place. # # @param key [Object] bucket key to append into # @param value [Object] value to append # @return [Array] the newly stored frozen Array # @raise [TypeError] if the existing value is not an Array def append(key, value) missing = !key?(key) compute(key) do |old_value| if old_value.nil? && missing [value].freeze else unless old_value.is_a?(Array) raise TypeError, "existing value for #{key.inspect} must be an Array: #{old_value.inspect}" end (old_value + [value]).freeze end end end # Atomically add +value+ to a Set bucket for +key+. # # The stored Set is replaced rather than mutated in place. # # @param key [Object] bucket key to update # @param value [Object] value to add to the set # @return [Set] the newly stored frozen Set # @raise [TypeError] if the existing value is not a Set def add_to_set(key, value) missing = !key?(key) compute(key) do |old_value| if old_value.nil? && missing Set[value].freeze else unless old_value.is_a?(Set) raise TypeError, "existing value for #{key.inspect} must be a Set: #{old_value.inspect}" end (old_value | [value]).freeze end end end # Alias for #size. # # @return [Integer] the current number of entries def length size end # Check whether the map currently has no entries. # # @return [Boolean] true when the map currently has no entries def empty? size.zero? end # Alias for #key?. # # @param key [Object] lookup key # @return [Boolean] true when the key currently exists def include?(key) key?(key) end alias member? include? end |
#get(key) ⇒ Object?
Read a value by key.
Missing keys return nil, so use #key? or #fetch when stored nil values need to be distinguished from missing entries.
If you keep the returned reference alive and then mutate the same key, you can deadlock the underlying DashMap bucket. Copy out what you need and let the reference go out of scope before mutating.
211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 |
# File 'lib/ratomic/map.rb', line 211 class Map # Set a value for +key+. # # In assignment form, Ruby returns the assigned value. # # @param key [Object] key to write # @param value [Object] value to store # @return [Object] the assigned value def []=(key, value) set(key, value) end # Read a value by +key+. # # Missing keys currently return nil, so storing nil is ambiguous. # # @param key [Object] lookup key # @return [Object, nil] the stored value, or nil when the key is missing def [](key) get(key) end # Fetch a value by +key+. # # Unlike #[], this distinguishes missing keys from explicit nil values. # # @param key [Object] lookup key # @param default [Object] fallback value to return when the key is missing # @yieldparam key [Object] the missing key # @return [Object] the found value, default, or block result # @raise [KeyError] if +key+ is missing and no default or block is provided def fetch(key, default = UNDEFINED) return get(key) if key?(key) return yield key if block_given? return default unless default.equal?(UNDEFINED) raise KeyError, "key not found: #{key.inspect}" end # Atomically increment the numeric value for +key+. # # Missing keys start at zero. Existing non-numeric values raise TypeError # and are left unchanged. This uses a native update path and is the preferred # counter primitive for Ractor-heavy workloads. # # @param key [Object] counter key to increment # @param by [Numeric] amount to add # @return [Numeric] the newly stored value # @raise [TypeError] if +by+ or the existing value is not numeric def increment(key, by = 1) raise TypeError, "amount must be numeric: #{by.inspect}" unless by.is_a?(Numeric) __increment_numeric(key, by) end # Atomically decrement the numeric value for +key+. # # Missing keys start at zero. # # @param key [Object] counter key to decrement # @param by [Numeric] amount to subtract # @return [Numeric] the newly stored value # @raise [TypeError] if +by+ or the existing value is not numeric def decrement(key, by = 1) raise TypeError, "amount must be numeric: #{by.inspect}" unless by.is_a?(Numeric) increment(key, -by) end # Atomically append +value+ to an Array bucket for +key+. # # The stored Array is replaced rather than mutated in place. # # @param key [Object] bucket key to append into # @param value [Object] value to append # @return [Array] the newly stored frozen Array # @raise [TypeError] if the existing value is not an Array def append(key, value) missing = !key?(key) compute(key) do |old_value| if old_value.nil? && missing [value].freeze else unless old_value.is_a?(Array) raise TypeError, "existing value for #{key.inspect} must be an Array: #{old_value.inspect}" end (old_value + [value]).freeze end end end # Atomically add +value+ to a Set bucket for +key+. # # The stored Set is replaced rather than mutated in place. # # @param key [Object] bucket key to update # @param value [Object] value to add to the set # @return [Set] the newly stored frozen Set # @raise [TypeError] if the existing value is not a Set def add_to_set(key, value) missing = !key?(key) compute(key) do |old_value| if old_value.nil? && missing Set[value].freeze else unless old_value.is_a?(Set) raise TypeError, "existing value for #{key.inspect} must be a Set: #{old_value.inspect}" end (old_value | [value]).freeze end end end # Alias for #size. # # @return [Integer] the current number of entries def length size end # Check whether the map currently has no entries. # # @return [Boolean] true when the map currently has no entries def empty? size.zero? end # Alias for #key?. # # @param key [Object] lookup key # @return [Boolean] true when the key currently exists def include?(key) key?(key) end alias member? include? end |
#include?(key) ⇒ Boolean Also known as: member?
Alias for #key?.
346 347 348 |
# File 'lib/ratomic/map.rb', line 346 def include?(key) key?(key) end |
#increment(key, by = 1) ⇒ Numeric
Atomically increment the numeric value for key.
Missing keys start at zero. Existing non-numeric values raise TypeError and are left unchanged. This uses a native update path and is the preferred counter primitive for Ractor-heavy workloads.
211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 |
# File 'lib/ratomic/map.rb', line 211 class Map # Set a value for +key+. # # In assignment form, Ruby returns the assigned value. # # @param key [Object] key to write # @param value [Object] value to store # @return [Object] the assigned value def []=(key, value) set(key, value) end # Read a value by +key+. # # Missing keys currently return nil, so storing nil is ambiguous. # # @param key [Object] lookup key # @return [Object, nil] the stored value, or nil when the key is missing def [](key) get(key) end # Fetch a value by +key+. # # Unlike #[], this distinguishes missing keys from explicit nil values. # # @param key [Object] lookup key # @param default [Object] fallback value to return when the key is missing # @yieldparam key [Object] the missing key # @return [Object] the found value, default, or block result # @raise [KeyError] if +key+ is missing and no default or block is provided def fetch(key, default = UNDEFINED) return get(key) if key?(key) return yield key if block_given? return default unless default.equal?(UNDEFINED) raise KeyError, "key not found: #{key.inspect}" end # Atomically increment the numeric value for +key+. # # Missing keys start at zero. Existing non-numeric values raise TypeError # and are left unchanged. This uses a native update path and is the preferred # counter primitive for Ractor-heavy workloads. # # @param key [Object] counter key to increment # @param by [Numeric] amount to add # @return [Numeric] the newly stored value # @raise [TypeError] if +by+ or the existing value is not numeric def increment(key, by = 1) raise TypeError, "amount must be numeric: #{by.inspect}" unless by.is_a?(Numeric) __increment_numeric(key, by) end # Atomically decrement the numeric value for +key+. # # Missing keys start at zero. # # @param key [Object] counter key to decrement # @param by [Numeric] amount to subtract # @return [Numeric] the newly stored value # @raise [TypeError] if +by+ or the existing value is not numeric def decrement(key, by = 1) raise TypeError, "amount must be numeric: #{by.inspect}" unless by.is_a?(Numeric) increment(key, -by) end # Atomically append +value+ to an Array bucket for +key+. # # The stored Array is replaced rather than mutated in place. # # @param key [Object] bucket key to append into # @param value [Object] value to append # @return [Array] the newly stored frozen Array # @raise [TypeError] if the existing value is not an Array def append(key, value) missing = !key?(key) compute(key) do |old_value| if old_value.nil? && missing [value].freeze else unless old_value.is_a?(Array) raise TypeError, "existing value for #{key.inspect} must be an Array: #{old_value.inspect}" end (old_value + [value]).freeze end end end # Atomically add +value+ to a Set bucket for +key+. # # The stored Set is replaced rather than mutated in place. # # @param key [Object] bucket key to update # @param value [Object] value to add to the set # @return [Set] the newly stored frozen Set # @raise [TypeError] if the existing value is not a Set def add_to_set(key, value) missing = !key?(key) compute(key) do |old_value| if old_value.nil? && missing Set[value].freeze else unless old_value.is_a?(Set) raise TypeError, "existing value for #{key.inspect} must be a Set: #{old_value.inspect}" end (old_value | [value]).freeze end end end # Alias for #size. # # @return [Integer] the current number of entries def length size end # Check whether the map currently has no entries. # # @return [Boolean] true when the map currently has no entries def empty? size.zero? end # Alias for #key?. # # @param key [Object] lookup key # @return [Boolean] true when the key currently exists def include?(key) key?(key) end alias member? include? end |
#key?(key) ⇒ Boolean
Check whether key currently exists in the map.
Unlike #get and #[], this distinguishes missing keys from stored nil values.
211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 |
# File 'lib/ratomic/map.rb', line 211 class Map # Set a value for +key+. # # In assignment form, Ruby returns the assigned value. # # @param key [Object] key to write # @param value [Object] value to store # @return [Object] the assigned value def []=(key, value) set(key, value) end # Read a value by +key+. # # Missing keys currently return nil, so storing nil is ambiguous. # # @param key [Object] lookup key # @return [Object, nil] the stored value, or nil when the key is missing def [](key) get(key) end # Fetch a value by +key+. # # Unlike #[], this distinguishes missing keys from explicit nil values. # # @param key [Object] lookup key # @param default [Object] fallback value to return when the key is missing # @yieldparam key [Object] the missing key # @return [Object] the found value, default, or block result # @raise [KeyError] if +key+ is missing and no default or block is provided def fetch(key, default = UNDEFINED) return get(key) if key?(key) return yield key if block_given? return default unless default.equal?(UNDEFINED) raise KeyError, "key not found: #{key.inspect}" end # Atomically increment the numeric value for +key+. # # Missing keys start at zero. Existing non-numeric values raise TypeError # and are left unchanged. This uses a native update path and is the preferred # counter primitive for Ractor-heavy workloads. # # @param key [Object] counter key to increment # @param by [Numeric] amount to add # @return [Numeric] the newly stored value # @raise [TypeError] if +by+ or the existing value is not numeric def increment(key, by = 1) raise TypeError, "amount must be numeric: #{by.inspect}" unless by.is_a?(Numeric) __increment_numeric(key, by) end # Atomically decrement the numeric value for +key+. # # Missing keys start at zero. # # @param key [Object] counter key to decrement # @param by [Numeric] amount to subtract # @return [Numeric] the newly stored value # @raise [TypeError] if +by+ or the existing value is not numeric def decrement(key, by = 1) raise TypeError, "amount must be numeric: #{by.inspect}" unless by.is_a?(Numeric) increment(key, -by) end # Atomically append +value+ to an Array bucket for +key+. # # The stored Array is replaced rather than mutated in place. # # @param key [Object] bucket key to append into # @param value [Object] value to append # @return [Array] the newly stored frozen Array # @raise [TypeError] if the existing value is not an Array def append(key, value) missing = !key?(key) compute(key) do |old_value| if old_value.nil? && missing [value].freeze else unless old_value.is_a?(Array) raise TypeError, "existing value for #{key.inspect} must be an Array: #{old_value.inspect}" end (old_value + [value]).freeze end end end # Atomically add +value+ to a Set bucket for +key+. # # The stored Set is replaced rather than mutated in place. # # @param key [Object] bucket key to update # @param value [Object] value to add to the set # @return [Set] the newly stored frozen Set # @raise [TypeError] if the existing value is not a Set def add_to_set(key, value) missing = !key?(key) compute(key) do |old_value| if old_value.nil? && missing Set[value].freeze else unless old_value.is_a?(Set) raise TypeError, "existing value for #{key.inspect} must be a Set: #{old_value.inspect}" end (old_value | [value]).freeze end end end # Alias for #size. # # @return [Integer] the current number of entries def length size end # Check whether the map currently has no entries. # # @return [Boolean] true when the map currently has no entries def empty? size.zero? end # Alias for #key?. # # @param key [Object] lookup key # @return [Boolean] true when the key currently exists def include?(key) key?(key) end alias member? include? end |
#length ⇒ Integer
Alias for #size.
331 332 333 |
# File 'lib/ratomic/map.rb', line 331 def length size end |
#set(key, value) ⇒ Object
Set a value for key.
This is the method behind ‘#[]=` and follows Ruby setter semantics by returning the assigned value.
211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 |
# File 'lib/ratomic/map.rb', line 211 class Map # Set a value for +key+. # # In assignment form, Ruby returns the assigned value. # # @param key [Object] key to write # @param value [Object] value to store # @return [Object] the assigned value def []=(key, value) set(key, value) end # Read a value by +key+. # # Missing keys currently return nil, so storing nil is ambiguous. # # @param key [Object] lookup key # @return [Object, nil] the stored value, or nil when the key is missing def [](key) get(key) end # Fetch a value by +key+. # # Unlike #[], this distinguishes missing keys from explicit nil values. # # @param key [Object] lookup key # @param default [Object] fallback value to return when the key is missing # @yieldparam key [Object] the missing key # @return [Object] the found value, default, or block result # @raise [KeyError] if +key+ is missing and no default or block is provided def fetch(key, default = UNDEFINED) return get(key) if key?(key) return yield key if block_given? return default unless default.equal?(UNDEFINED) raise KeyError, "key not found: #{key.inspect}" end # Atomically increment the numeric value for +key+. # # Missing keys start at zero. Existing non-numeric values raise TypeError # and are left unchanged. This uses a native update path and is the preferred # counter primitive for Ractor-heavy workloads. # # @param key [Object] counter key to increment # @param by [Numeric] amount to add # @return [Numeric] the newly stored value # @raise [TypeError] if +by+ or the existing value is not numeric def increment(key, by = 1) raise TypeError, "amount must be numeric: #{by.inspect}" unless by.is_a?(Numeric) __increment_numeric(key, by) end # Atomically decrement the numeric value for +key+. # # Missing keys start at zero. # # @param key [Object] counter key to decrement # @param by [Numeric] amount to subtract # @return [Numeric] the newly stored value # @raise [TypeError] if +by+ or the existing value is not numeric def decrement(key, by = 1) raise TypeError, "amount must be numeric: #{by.inspect}" unless by.is_a?(Numeric) increment(key, -by) end # Atomically append +value+ to an Array bucket for +key+. # # The stored Array is replaced rather than mutated in place. # # @param key [Object] bucket key to append into # @param value [Object] value to append # @return [Array] the newly stored frozen Array # @raise [TypeError] if the existing value is not an Array def append(key, value) missing = !key?(key) compute(key) do |old_value| if old_value.nil? && missing [value].freeze else unless old_value.is_a?(Array) raise TypeError, "existing value for #{key.inspect} must be an Array: #{old_value.inspect}" end (old_value + [value]).freeze end end end # Atomically add +value+ to a Set bucket for +key+. # # The stored Set is replaced rather than mutated in place. # # @param key [Object] bucket key to update # @param value [Object] value to add to the set # @return [Set] the newly stored frozen Set # @raise [TypeError] if the existing value is not a Set def add_to_set(key, value) missing = !key?(key) compute(key) do |old_value| if old_value.nil? && missing Set[value].freeze else unless old_value.is_a?(Set) raise TypeError, "existing value for #{key.inspect} must be a Set: #{old_value.inspect}" end (old_value | [value]).freeze end end end # Alias for #size. # # @return [Integer] the current number of entries def length size end # Check whether the map currently has no entries. # # @return [Boolean] true when the map currently has no entries def empty? size.zero? end # Alias for #key?. # # @param key [Object] lookup key # @return [Boolean] true when the key currently exists def include?(key) key?(key) end alias member? include? end |
#size ⇒ Integer
Return the current number of entries.
Since this is a concurrent map, the value is a moment-in-time observation.
211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 |
# File 'lib/ratomic/map.rb', line 211 class Map # Set a value for +key+. # # In assignment form, Ruby returns the assigned value. # # @param key [Object] key to write # @param value [Object] value to store # @return [Object] the assigned value def []=(key, value) set(key, value) end # Read a value by +key+. # # Missing keys currently return nil, so storing nil is ambiguous. # # @param key [Object] lookup key # @return [Object, nil] the stored value, or nil when the key is missing def [](key) get(key) end # Fetch a value by +key+. # # Unlike #[], this distinguishes missing keys from explicit nil values. # # @param key [Object] lookup key # @param default [Object] fallback value to return when the key is missing # @yieldparam key [Object] the missing key # @return [Object] the found value, default, or block result # @raise [KeyError] if +key+ is missing and no default or block is provided def fetch(key, default = UNDEFINED) return get(key) if key?(key) return yield key if block_given? return default unless default.equal?(UNDEFINED) raise KeyError, "key not found: #{key.inspect}" end # Atomically increment the numeric value for +key+. # # Missing keys start at zero. Existing non-numeric values raise TypeError # and are left unchanged. This uses a native update path and is the preferred # counter primitive for Ractor-heavy workloads. # # @param key [Object] counter key to increment # @param by [Numeric] amount to add # @return [Numeric] the newly stored value # @raise [TypeError] if +by+ or the existing value is not numeric def increment(key, by = 1) raise TypeError, "amount must be numeric: #{by.inspect}" unless by.is_a?(Numeric) __increment_numeric(key, by) end # Atomically decrement the numeric value for +key+. # # Missing keys start at zero. # # @param key [Object] counter key to decrement # @param by [Numeric] amount to subtract # @return [Numeric] the newly stored value # @raise [TypeError] if +by+ or the existing value is not numeric def decrement(key, by = 1) raise TypeError, "amount must be numeric: #{by.inspect}" unless by.is_a?(Numeric) increment(key, -by) end # Atomically append +value+ to an Array bucket for +key+. # # The stored Array is replaced rather than mutated in place. # # @param key [Object] bucket key to append into # @param value [Object] value to append # @return [Array] the newly stored frozen Array # @raise [TypeError] if the existing value is not an Array def append(key, value) missing = !key?(key) compute(key) do |old_value| if old_value.nil? && missing [value].freeze else unless old_value.is_a?(Array) raise TypeError, "existing value for #{key.inspect} must be an Array: #{old_value.inspect}" end (old_value + [value]).freeze end end end # Atomically add +value+ to a Set bucket for +key+. # # The stored Set is replaced rather than mutated in place. # # @param key [Object] bucket key to update # @param value [Object] value to add to the set # @return [Set] the newly stored frozen Set # @raise [TypeError] if the existing value is not a Set def add_to_set(key, value) missing = !key?(key) compute(key) do |old_value| if old_value.nil? && missing Set[value].freeze else unless old_value.is_a?(Set) raise TypeError, "existing value for #{key.inspect} must be a Set: #{old_value.inspect}" end (old_value | [value]).freeze end end end # Alias for #size. # # @return [Integer] the current number of entries def length size end # Check whether the map currently has no entries. # # @return [Boolean] true when the map currently has no entries def empty? size.zero? end # Alias for #key?. # # @param key [Object] lookup key # @return [Boolean] true when the key currently exists def include?(key) key?(key) end alias member? include? end |
#upsert(key, initial) {|value| ... } ⇒ Object
Atomically insert initial for a missing key, or update an existing value.
If key is missing, stores and returns initial without yielding. If key exists, yields the current value, stores the block return value, and returns it.
The operation is atomic for the key. The block runs while the map entry is locked, so avoid using this method for Ractor-hot loops or calling back into the same map from inside the block. Prefer native update helpers such as #increment when they fit the workflow.
Never call this method from inside another live reference to the same entry or from a block that already holds the same map’s guard. That can deadlock the bucket.
If the block raises, the previous value is preserved.
211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 |
# File 'lib/ratomic/map.rb', line 211 class Map # Set a value for +key+. # # In assignment form, Ruby returns the assigned value. # # @param key [Object] key to write # @param value [Object] value to store # @return [Object] the assigned value def []=(key, value) set(key, value) end # Read a value by +key+. # # Missing keys currently return nil, so storing nil is ambiguous. # # @param key [Object] lookup key # @return [Object, nil] the stored value, or nil when the key is missing def [](key) get(key) end # Fetch a value by +key+. # # Unlike #[], this distinguishes missing keys from explicit nil values. # # @param key [Object] lookup key # @param default [Object] fallback value to return when the key is missing # @yieldparam key [Object] the missing key # @return [Object] the found value, default, or block result # @raise [KeyError] if +key+ is missing and no default or block is provided def fetch(key, default = UNDEFINED) return get(key) if key?(key) return yield key if block_given? return default unless default.equal?(UNDEFINED) raise KeyError, "key not found: #{key.inspect}" end # Atomically increment the numeric value for +key+. # # Missing keys start at zero. Existing non-numeric values raise TypeError # and are left unchanged. This uses a native update path and is the preferred # counter primitive for Ractor-heavy workloads. # # @param key [Object] counter key to increment # @param by [Numeric] amount to add # @return [Numeric] the newly stored value # @raise [TypeError] if +by+ or the existing value is not numeric def increment(key, by = 1) raise TypeError, "amount must be numeric: #{by.inspect}" unless by.is_a?(Numeric) __increment_numeric(key, by) end # Atomically decrement the numeric value for +key+. # # Missing keys start at zero. # # @param key [Object] counter key to decrement # @param by [Numeric] amount to subtract # @return [Numeric] the newly stored value # @raise [TypeError] if +by+ or the existing value is not numeric def decrement(key, by = 1) raise TypeError, "amount must be numeric: #{by.inspect}" unless by.is_a?(Numeric) increment(key, -by) end # Atomically append +value+ to an Array bucket for +key+. # # The stored Array is replaced rather than mutated in place. # # @param key [Object] bucket key to append into # @param value [Object] value to append # @return [Array] the newly stored frozen Array # @raise [TypeError] if the existing value is not an Array def append(key, value) missing = !key?(key) compute(key) do |old_value| if old_value.nil? && missing [value].freeze else unless old_value.is_a?(Array) raise TypeError, "existing value for #{key.inspect} must be an Array: #{old_value.inspect}" end (old_value + [value]).freeze end end end # Atomically add +value+ to a Set bucket for +key+. # # The stored Set is replaced rather than mutated in place. # # @param key [Object] bucket key to update # @param value [Object] value to add to the set # @return [Set] the newly stored frozen Set # @raise [TypeError] if the existing value is not a Set def add_to_set(key, value) missing = !key?(key) compute(key) do |old_value| if old_value.nil? && missing Set[value].freeze else unless old_value.is_a?(Set) raise TypeError, "existing value for #{key.inspect} must be a Set: #{old_value.inspect}" end (old_value | [value]).freeze end end end # Alias for #size. # # @return [Integer] the current number of entries def length size end # Check whether the map currently has no entries. # # @return [Boolean] true when the map currently has no entries def empty? size.zero? end # Alias for #key?. # # @param key [Object] lookup key # @return [Boolean] true when the key currently exists def include?(key) key?(key) end alias member? include? end |