/***** BEGIN LICENSE BLOCK *****
* Version: EPL 2.0/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Eclipse Public
* License Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.eclipse.org/legal/epl-v20.html
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* Copyright (C) 2001 Chad Fowler
* Copyright (C) 2001 Alan Moore
* Copyright (C) 2001-2002 Benoit Cerrina
* Copyright (C) 2001-2004 Jan Arne Petersen
* Copyright (C) 2002-2004 Anders Bengtsson
* Copyright (C) 2004-2006 Thomas E Enebo
* Copyright (C) 2004 Stefan Matthias Aust
* Copyright (C) 2005 Charles O Nutter
* Copyright (C) 2006 Ola Bini
* Copyright (C) 2006 Tim Azzopardi
* Copyright (C) 2006 Miguel Covarrubias
* Copyright (C) 2007 MenTaLguY
*
* Alternatively, the contents of this file may be used under the terms of
* either of the GNU General Public License Version 2 or later (the "GPL"),
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the EPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the EPL, the GPL or the LGPL.
***** END LICENSE BLOCK *****/
package org.jruby;
import org.jruby.anno.JRubyClass;
import org.jruby.anno.JRubyMethod;
import org.jruby.api.Create;
import org.jruby.api.Error;
import org.jruby.runtime.Block;
import org.jruby.runtime.ClassIndex;
import org.jruby.runtime.ThreadContext;
import org.jruby.runtime.builtin.IRubyObject;
import org.jruby.runtime.marshal.MarshalDumper;
import org.jruby.runtime.marshal.MarshalLoader;
import org.jruby.util.TypeConverter;
import org.jruby.util.io.RubyInputStream;
import org.jruby.util.io.RubyOutputStream;
import java.io.IOException;
import java.util.Collection;
import java.util.Map;
import java.util.Set;
import static org.jruby.api.Access.arrayClass;
import static org.jruby.api.Access.hashClass;
import static org.jruby.api.Convert.asBoolean;
import static org.jruby.api.Define.defineClass;
import static org.jruby.api.Error.*;
import static org.jruby.runtime.Visibility.PRIVATE;
// Design overview:
//
// RubyHash is implemented as hash table with a singly-linked list of
// RubyHash.RubyHashEntry objects for each bucket. RubyHashEntry objects
// are also kept in a doubly-linked list which reflects their insertion
// order and is used for iteration. For simplicity, this latter list is
// circular; a dummy RubyHashEntry, RubyHash.head, is used to mark the
// ends of the list.
//
// When an entry is removed from the table, it is also removed from the
// doubly-linked list. However, while the reference to the previous
// RubyHashEntry is cleared (to mark the entry as dead), the reference
// to the next RubyHashEntry is preserved so that iterators are not
// invalidated: any iterator with a reference to a dead entry can climb
// back up into the list of live entries by chasing next references until
// it finds a live entry (or head).
//
// Ordinarily, this scheme would require O(N) time to clear a hash (since
// each RubyHashEntry would need to be visited and unlinked from the
// iteration list), but RubyHash also maintains a generation count. Every
// time the hash is cleared, the doubly-linked list is simply discarded and
// the generation count incremented. Iterators check to see whether the
// generation count has changed; if it has, they reset themselves back to
// the new start of the list.
//
// This design means that iterators are never invalidated by changes to the
// hashtable, and they do not need to modify the structure during their
// lifecycle.
//
/** Implementation of the Hash class.
*
* Concurrency: no synchronization is required among readers, but
* all users must synchronize externally with writers.
*
*/
@JRubyClass(name = "Hash", include="Enumerable",
overrides = {RubyHashLinkedBuckets.class})
public class RubyHash extends RubyObject implements Map {
public static final int DEFAULT_INSPECT_STR_SIZE = 20;
/**
* This field is used as part of the state of RubyHashLinkedBuckets, but when supporting legacy constructors it will
* point at a delegated instance. Once all consumers of the deprecated constructor have been migrated, we move this
* field back into the subclasses and eliminate all delegated methods.
*/
protected Object state;
protected static final byte COMPARE_BY_IDENTITY = 0b00000001;
protected static final byte RUBY2_KEYWORD = 0b00000010;
protected static final byte PROCDEFAULT_HASH = 0b00000100;
protected static final RubyHashEntry NULL_ENTRY = new RubyHashEntry();
@Deprecated(since = "10.0.3.0")
public static final RubyHashEntry NO_ENTRY = NULL_ENTRY;
@SuppressWarnings("removal")
public static RubyClass createHashClass(ThreadContext context, RubyClass Object, RubyModule Enumerable) {
return defineClass(context, "Hash", Object, (runtime, klass) -> new RubyHashLinkedBuckets(runtime, klass)).
reifiedClass(RubyHash.class).
kindOf(new RubyModule.JavaClassKindOf(RubyHash.class)).
classIndex(ClassIndex.HASH).
include(context, Enumerable).
defineMethods(context, RubyHash.class);
}
@Override
public ClassIndex getNativeClassIndex() {
return ClassIndex.HASH;
}
/** rb_hash_s_create
*
*/
@JRubyMethod(name = "[]", rest = true, meta = true)
public static IRubyObject create(ThreadContext context, IRubyObject recv, IRubyObject[] args, Block block) {
if (args.length == 1) {
IRubyObject tmp = TypeConverter.convertToTypeWithCheck(args[0], hashClass(context), "to_hash");
if (!tmp.isNil()) return new RubyHashLinkedBuckets(context.runtime, (RubyClass) recv, (RubyHash) tmp);
var Array = arrayClass(context);
final IRubyObject nil = context.nil;
tmp = TypeConverter.convertToTypeWithCheck(args[0], Array, "to_ary");
if (tmp != nil) {
RubyHash hash = (RubyHash) ((RubyClass) recv).allocate(context);
var arr = (RubyArray) tmp;
for (int i = 0, j = arr.getLength(); i) v).getLength() + " for 1..2)");
case 2:
val = ((RubyArray) v).entry(1);
case 1:
key = ((RubyArray) v).entry(0);
hash.fastASetCheckString(context.runtime, key, val);
}
}
return hash;
}
}
if ((args.length & 1) != 0) throw argumentError(context, "odd number of arguments for Hash");
RubyHash hash = (RubyHash) ((RubyClass) recv).allocate(context);
for (int i=0; i < args.length; i+=2) hash.fastASetCheckString(context.runtime, args[i], args[i+1]);
return hash;
}
@JRubyMethod(name = "try_convert", meta = true)
public static IRubyObject try_convert(ThreadContext context, IRubyObject recv, IRubyObject args) {
return TypeConverter.convertToTypeWithCheck(args, hashClass(context), "to_hash");
}
/** rb_hash_new
*
*/
@Deprecated(since = "10.1.1.0")
public static final RubyHash newHash(Ruby runtime) {
return RubyHashLinkedBuckets.newLBHash(runtime);
}
/** rb_hash_new
*
*/
@SuppressWarnings("deprecation")
public static final RubyHash newSmallHash(Ruby runtime) {
return RubyHashLinkedBuckets.newLBHash(runtime, 1);
}
public static RubyHash newKwargs(Ruby runtime, String key, IRubyObject value) {
return newHash(runtime, runtime.newSymbol(key), value);
}
public static RubyHash newHash(Ruby runtime, IRubyObject key, IRubyObject value) {
RubyHash kwargs = newSmallHash(runtime);
kwargs.fastASetSmall(key, value);
return kwargs;
}
@SuppressWarnings("deprecation")
public static RubyHash newHash(Ruby runtime, IRubyObject defaultValue) {
return RubyHashLinkedBuckets.newLBHash(runtime, defaultValue);
}
@SuppressWarnings("deprecation")
public static RubyHash newHash(Ruby runtime, int buckets) {
return RubyHashLinkedBuckets.newLBHash(runtime, buckets);
}
@SuppressWarnings("deprecation")
public static RubyHash newHash(Ruby runtime, IRubyObject defaultValue, int buckets) {
return RubyHashLinkedBuckets.newLBHash(runtime, defaultValue, buckets);
}
/** rb_hash_new
*
*/
public static final RubyHash newHash(Ruby runtime, Map valueMap, IRubyObject defaultValue) {
assert defaultValue != null;
return new RubyHashLinkedBuckets(runtime, valueMap, defaultValue);
}
// Only constructor that does not delegate
protected RubyHash(Ruby runtime, RubyClass klass, boolean objectSpace, int unused) {
super(runtime, klass, objectSpace);
// ensure only subclasses call this constructor
assert this.getClass() != RubyHash.class;
}
// Delegated constructor, to be hidden and returned to normal super constructor once no longer in use
@Deprecated(since = "10.0.6.0", forRemoval = true)
public RubyHash(Ruby runtime, RubyClass klass) {
super(runtime, klass);
// ensure no subclasses call this constructor
assert getClass() == RubyHash.class;
setDelegate(this);
}
// Delegated constructor, to be hidden and returned to normal super constructor once no longer in use
@Deprecated(since = "10.0.3.0")
public RubyHash(Ruby runtime, RubyClass klass, boolean objectSpace) {
super(runtime, klass, objectSpace);
// ensure no subclasses call this constructor
assert getClass() == RubyHash.class;
this.setDelegate(new RubyHashLinkedBuckets(runtime, klass, objectSpace));
}
// Delegated constructor, to be hidden and returned to normal super constructor once no longer in use
@Deprecated(since = "10.0.6.0", forRemoval = true)
public RubyHash(Ruby runtime, int buckets) {
super(runtime, runtime.getHash());
// ensure no subclasses call this constructor
assert getClass() == RubyHash.class;
this.setDelegate(RubyHashLinkedBuckets.newLBHash(runtime, buckets));
}
// Delegated constructor, to be hidden and returned to normal super constructor once no longer in use
@Deprecated(since = "10.0.6.0", forRemoval = true)
public RubyHash(Ruby runtime) {
super(runtime, runtime.getHash());
// ensure no subclasses call this constructor
assert getClass() == RubyHash.class;
this.setDelegate(RubyHashLinkedBuckets.newLBHash(runtime));
}
// Delegated constructor, to be hidden and returned to normal super constructor once no longer in use
@Deprecated(since = "10.0.6.0", forRemoval = true)
public RubyHash(Ruby runtime, IRubyObject defaultValue) {
super(runtime, runtime.getHash());
// ensure no subclasses call this constructor
assert getClass() == RubyHash.class;
this.setDelegate(RubyHashLinkedBuckets.newLBHash(runtime, defaultValue));
}
// Delegated constructor, to be hidden and returned to normal super constructor once no longer in use
@Deprecated(since = "10.0.6.0", forRemoval = true)
public RubyHash(Ruby runtime, IRubyObject defaultValue, int buckets) {
super(runtime, runtime.getHash());
// ensure no subclasses call this constructor
assert getClass() == RubyHash.class;
this.setDelegate(RubyHashLinkedBuckets.newLBHash(runtime, defaultValue, buckets));
}
// Delegated constructor, to be hidden and returned to normal super constructor once no longer in use
@Deprecated(since = "10.0.3.0", forRemoval = true)
public RubyHash(Ruby runtime, Map valueMap, IRubyObject defaultValue) {
super(runtime, runtime.getHash());
// ensure no subclasses call this constructor
assert getClass() == RubyHash.class;
this.setDelegate(RubyHashLinkedBuckets.newHash(runtime, valueMap, defaultValue));
}
/* ============================
* Here are hash internals
* (This could be extracted to a separate class but it's not too large though)
* ============================
*/
@Deprecated(since = "10.0.3.0")
public static final int MRI_PRIMES[] = {
8 + 3, 16 + 3, 32 + 5, 64 + 3, 128 + 3, 256 + 27, 512 + 9, 1024 + 9, 2048 + 5, 4096 + 3,
8192 + 27, 16384 + 43, 32768 + 3, 65536 + 45, 131072 + 29, 262144 + 3, 524288 + 21, 1048576 + 7,
2097152 + 17, 4194304 + 15, 8388608 + 9, 16777216 + 43, 33554432 + 35, 67108864 + 15,
134217728 + 29, 268435456 + 3, 536870912 + 11, 1073741824 + 85, 0
};
private RubyHash getDelegate() {
return (RubyHash) state;
}
private void setDelegate(RubyHash state) {
this.state = state;
}
public static class RubyHashEntryLink {
RubyHash.RubyHashEntryLink prevAdded;
RubyHash.RubyHashEntryLink nextAdded;
int hash() {
// should never be called
throw new RuntimeException("hash() should never be called on RubyHashEntryLink");
}
public IRubyObject key() {
// should never be called
throw new RuntimeException("key() should never be called on RubyHashEntryLink");
}
IRubyObject value() {
// should never be called
throw new RuntimeException("value() should never be called on RubyHashEntryLink");
}
IRubyObject value(IRubyObject value) {
// should never be called
throw new RuntimeException("value(IRubyObject) should never be called on RubyHashEntryLink");
}
public boolean isLive() {
// should never be called
throw new RuntimeException("value() should never be called on RubyHashEntryLink");
}
}
public static final class RubyHashEntry extends RubyHashEntryLink implements Map.Entry {
private final IRubyObject key;
private IRubyObject value;
private RubyHashEntry next;
private final int hash;
RubyHashEntry() {
key = NEVER;
hash = -1;
}
public RubyHashEntry(int h, IRubyObject k, IRubyObject v, RubyHashEntry e, RubyHashEntryLink head) {
key = k;
value(v);
next(e);
hash = h;
if (head != null) {
RubyHashEntryLink prevAdded = head.prevAdded;
RubyHashEntryLink nextAdded = head;
this.prevAdded = prevAdded;
prevAdded.nextAdded = this;
this.nextAdded = nextAdded;
nextAdded.prevAdded = this;
}
}
public RubyHashEntry(RubyHashEntry oldEntry, int newHash) {
this.hash = newHash;
this.key = oldEntry.key();
this.value(oldEntry.value());
this.next(oldEntry.next());
// prevAdded is never null
RubyHashEntryLink prevAdded = oldEntry.prevAdded;
this.prevAdded = prevAdded;
prevAdded.nextAdded = this;
RubyHashEntryLink nextAdded = oldEntry.nextAdded;
if (nextAdded != null) {
this.nextAdded = nextAdded;
nextAdded.prevAdded = this;
}
}
public void detach() {
if (prevAdded != null) {
prevAdded.nextAdded = nextAdded;
nextAdded.prevAdded = prevAdded;
prevAdded = null;
}
}
public boolean isLive() {
return prevAdded != null;
}
@Override
public Object getKey() {
return key();
}
@Override
public Object getValue() {
return value();
}
@Override
public Object setValue(Object value) {
IRubyObject oldValue = this.value();
if (value instanceof IRubyObject) {
this.value((IRubyObject)value);
} else {
throw new UnsupportedOperationException("directEntrySet() doesn't support setValue for non IRubyObject instance entries, convert them manually or use entrySet() instead");
}
return oldValue;
}
@Override
public boolean equals(Object other){
if(!(other instanceof RubyHashEntry)) return false;
RubyHashEntry otherEntry = (RubyHashEntry)other;
return (key() == otherEntry.key() || key().eql(otherEntry.key())) &&
(value() == otherEntry.value() || value().equals(otherEntry.value()));
}
@Override
public int hashCode(){
return key().hashCode() ^ value().hashCode();
}
public IRubyObject key() {
return key;
}
IRubyObject value() {
return value;
}
IRubyObject value(IRubyObject value) {
IRubyObject oldValue = this.value;
this.value = value;
return oldValue;
}
RubyHashEntry next() {
return next;
}
void next(RubyHashEntry next) {
this.next = next;
}
int hash() {
return hash;
}
@Deprecated(since = "10.1.1.0")
public Object getJavaifiedKey(){
return key.toJava(Object.class);
}
@Deprecated(since = "10.1.1.0")
public Object getJavaifiedValue() {
return value.toJava(Object.class);
}
}
// put implementation
public IRubyObject internalPut(final IRubyObject key, final IRubyObject value) {
return getDelegate().internalPut(key, value);
}
boolean internalPutIfNoKey(final IRubyObject key, final IRubyObject value) {
return getDelegate().internalPutIfNoKey(key, value);
}
// get implementation
protected IRubyObject internalGet(IRubyObject key) {
return getDelegate().internalGet(key);
}
@Deprecated(since = "10.0.3.0")
RubyHashEntry getEntry(IRubyObject key) {
return getDelegate().getEntry(key);
}
public interface VisitorWithStateI {
void visit(ThreadContext context, RubyHash self, IRubyObject key, IRubyObject value, int index);
}
public static abstract class VisitorWithState {
public abstract void visit(ThreadContext context, RubyHash self, IRubyObject key, IRubyObject value, int index, T state);
}
public static abstract class Visitor extends VisitorWithState {
public void visit(ThreadContext context, RubyHash self, IRubyObject key, IRubyObject value, int index, Object state) {
visit(key, value);
}
public abstract void visit(IRubyObject key, IRubyObject value);
}
public void visitAll(ThreadContext context, VisitorWithStateI visitor) {
getDelegate().visitAll(context, visitor);
}
public void visitAll(ThreadContext context, VisitorWithState visitor, T state) {
this.getDelegate().visitAll(context, visitor, state);
}
protected void visitLimited(ThreadContext context, RubyHash.VisitorWithState visitor, long size, T state) {
this.getDelegate().visitLimited(context, visitor, size, state);
}
public boolean allSymbols() {
return getDelegate().allSymbols();
}
/* ============================
* End of hash internals
* ============================
*/
/* ================
* Instance Methods
* ================
*/
@JRubyMethod(visibility = PRIVATE, keywords = true)
public IRubyObject initialize(ThreadContext context, final Block block) {
return getDelegate().initialize(context, block);
}
@JRubyMethod(visibility = PRIVATE, keywords = true)
public IRubyObject initialize(ThreadContext context, IRubyObject _default, final Block block) {
return getDelegate().initialize(context, _default, block);
}
@JRubyMethod(visibility = PRIVATE, keywords = true)
public IRubyObject initialize(ThreadContext context, IRubyObject _default, IRubyObject hash, final Block block) {
return getDelegate().initialize(context, _default, hash, block);
}
@JRubyMethod(name = "default")
public IRubyObject default_value_get(ThreadContext context) {
return getDelegate().default_value_get(context);
}
@JRubyMethod(name = "default")
public IRubyObject default_value_get(ThreadContext context, IRubyObject arg) {
return getDelegate().default_value_get(context, arg);
}
@JRubyMethod(name = "default=")
public IRubyObject default_value_set(ThreadContext context, final IRubyObject defaultValue) {
return getDelegate().default_value_set(context, defaultValue);
}
@JRubyMethod
public IRubyObject default_proc(ThreadContext context) {
return getDelegate().default_proc(context);
}
@JRubyMethod(name = "default_proc=")
public IRubyObject set_default_proc(ThreadContext context, IRubyObject proc) {
return getDelegate().set_default_proc(context, proc);
}
public void modify() {
getDelegate().modify();
}
@JRubyMethod(name = "inspect")
public IRubyObject inspect(ThreadContext context) {
return getDelegate().inspect(context);
}
@JRubyMethod(name = {"size", "length"})
public RubyFixnum rb_size(ThreadContext context) {
return getDelegate().rb_size(context);
}
@JRubyMethod(name = "empty?")
public RubyBoolean empty_p(ThreadContext context) {
return getDelegate().empty_p(context);
}
@JRubyMethod(name = "to_a")
@Override
public RubyArray to_a(ThreadContext context) {
return getDelegate().to_a(context);
}
@JRubyMethod(name = "to_s")
public IRubyObject to_s(ThreadContext context) {
return getDelegate().to_s(context);
}
@JRubyMethod(name = "rehash")
public RubyHash rehash(ThreadContext context) {
return getDelegate().rehash(context);
}
@JRubyMethod(name = "to_hash")
public RubyHash to_hash(ThreadContext context) {
return getDelegate().to_hash(context);
}
@JRubyMethod
public RubyHash to_h(ThreadContext context, Block block) {
return getDelegate().to_h(context, block);
}
protected RubyHash to_h_block(ThreadContext context, Block block) {
return getDelegate().to_h_block(context, block);
}
@Override
public RubyHash convertToHash() {
return getDelegate().convertToHash();
}
public void fastASet(IRubyObject key, IRubyObject value) {
getDelegate().fastASet(key, value);
}
public void fastASetSmall(IRubyObject key, IRubyObject value) {
getDelegate().fastASetSmall(key, value);
}
// MRI: rb_hash_set_pair, fast/small version
public void fastASetSmallPair(ThreadContext context, IRubyObject _pair) {
getDelegate().fastASetSmallPair(context, _pair);
}
public void fastASetCheckString(Ruby runtime, IRubyObject key, IRubyObject value) {
getDelegate().fastASetCheckString(runtime, key, value);
}
public void fastASetSmallCheckString(Ruby runtime, IRubyObject key, IRubyObject value) {
getDelegate().fastASetSmallCheckString(runtime, key, value);
}
public void fastASet(Ruby runtime, IRubyObject key, IRubyObject value, boolean prepareString) {
getDelegate().fastASet(runtime, key, value, prepareString);
}
public void fastASetSmall(Ruby runtime, IRubyObject key, IRubyObject value, boolean prepareString) {
getDelegate().fastASetSmall(runtime, key, value, prepareString);
}
/**
* Set a key/value pair into this hash.
*
* @param context the current thread context
* @param key the key
* @param value the value
* @return the value set
*/
@JRubyMethod(name = {"[]=", "store"})
public IRubyObject op_aset(ThreadContext context, IRubyObject key, IRubyObject value) {
return getDelegate().op_aset(context, key, value);
}
// returns null when not found to avoid unnecessary getRuntime().getNil() call
public IRubyObject fastARef(IRubyObject key) {
return getDelegate().fastARef(key);
}
public RubyBoolean compare(final ThreadContext context, VisitorWithState visitor, IRubyObject other, boolean eql) {
return getDelegate().compare(context, visitor, other, eql);
}
@Override
@JRubyMethod(name = "==")
public IRubyObject op_equal(final ThreadContext context, IRubyObject other) {
return getDelegate().op_equal(context, other);
}
@JRubyMethod(name = "eql?")
public IRubyObject op_eql(final ThreadContext context, IRubyObject other) {
return getDelegate().op_eql(context, other);
}
@JRubyMethod(name = "[]")
public IRubyObject op_aref(ThreadContext context, IRubyObject key) {
return getDelegate().op_aref(context, key);
}
@JRubyMethod(name = "=")
public IRubyObject op_ge(ThreadContext context, IRubyObject other) {
return getDelegate().op_ge(context, other);
}
// MRI: rb_hash_hash
@JRubyMethod(name = "hash")
public RubyFixnum hash(ThreadContext context) {
return getDelegate().hash(context);
}
public IRubyObject fetch(ThreadContext context, IRubyObject[] args, Block block) {
return getDelegate().fetch(context, args, block);
}
@JRubyMethod(rest = true)
public IRubyObject except(ThreadContext context, IRubyObject[] keys) {
return getDelegate().except(context, keys);
}
@JRubyMethod
public IRubyObject fetch(ThreadContext context, IRubyObject key, Block block) {
return getDelegate().fetch(context, key, block);
}
@JRubyMethod
public IRubyObject fetch(ThreadContext context, IRubyObject key, IRubyObject _default, Block block) {
return getDelegate().fetch(context, key, _default, block);
}
@JRubyMethod(name = {"has_key?", "key?", "include?", "member?"})
public RubyBoolean has_key_p(ThreadContext context, IRubyObject key) {
return getDelegate().has_key_p(context, key);
}
/**
* A Java API to test the presence of a (Ruby) key in the Hash
*
* @param key the native (Ruby) key
* @return true if the hash contains the provided key
*/
public boolean hasKey(IRubyObject key) {
return getDelegate().hasKey(key);
}
@JRubyMethod(name = {"has_value?", "value?"})
public RubyBoolean has_value_p(ThreadContext context, IRubyObject expected) {
return getDelegate().has_value_p(context, expected);
}
public RubyHash eachCommon(final ThreadContext context, final Block block) {
return getDelegate().eachCommon(context, block);
}
@JRubyMethod(name = {"each", "each_pair"})
public IRubyObject each(final ThreadContext context, final Block block) {
return getDelegate().each(context, block);
}
public RubyHash each_pairCommon(final ThreadContext context, final Block block) {
return getDelegate().each_pairCommon(context, block);
}
public RubyHash each_valueCommon(final ThreadContext context, final Block block) {
return getDelegate().each_valueCommon(context, block);
}
@JRubyMethod
public IRubyObject each_value(final ThreadContext context, final Block block) {
return getDelegate().each_value(context, block);
}
public RubyHash each_keyCommon(final ThreadContext context, final Block block) {
return getDelegate().each_keyCommon(context, block);
}
@JRubyMethod
public IRubyObject each_key(final ThreadContext context, final Block block) {
return getDelegate().each_key(context, block);
}
@JRubyMethod(name = "transform_keys", rest = true)
public IRubyObject transform_keys(final ThreadContext context, IRubyObject[] args, final Block block) {
return getDelegate().transform_keys(context, args, block);
}
@JRubyMethod(name = "transform_values")
public IRubyObject transform_values(final ThreadContext context, final Block block) {
return getDelegate().transform_values(context, block);
}
@JRubyMethod(name = "transform_keys!", rest = true)
public IRubyObject transform_keys_bang(final ThreadContext context, IRubyObject[] args, final Block block) {
return getDelegate().transform_keys_bang(context, args, block);
}
@JRubyMethod(name = "transform_values!")
public IRubyObject transform_values_bang(final ThreadContext context, final Block block) {
return getDelegate().transform_values_bang(context, block);
}
@JRubyMethod(name = "select!", alias = "filter!")
public IRubyObject select_bang(final ThreadContext context, final Block block) {
return getDelegate().select_bang(context, block);
}
@JRubyMethod
public IRubyObject keep_if(final ThreadContext context, final Block block) {
return getDelegate().keep_if(context, block);
}
public boolean keep_ifCommon(final ThreadContext context, final Block block) {
return getDelegate().keep_ifCommon(context, block);
}
@JRubyMethod
public IRubyObject key(ThreadContext context, IRubyObject expected) {
return getDelegate().key(context, expected);
}
@JRubyMethod(name = "keys")
public RubyArray keys(final ThreadContext context) {
return getDelegate().keys(context);
}
public RubyArray keys() {
return getDelegate().keys();
}
@JRubyMethod(name = "values")
public RubyArray values(final ThreadContext context) {
return getDelegate().values(context);
}
public RubyArray rb_values(ThreadContext context) {
return getDelegate().rb_values(context);
}
public static final VisitorWithState StoreValueVisitor = new VisitorWithState() {
@Override
public void visit(ThreadContext context, RubyHash self, IRubyObject key, IRubyObject value, int index, RubyArray values) {
values.storeInternal(context, index, value);
}
};
// like RubyHash.StoreValueVisitor but 'unsafe' - user needs to assure array capacity and adjust length
static final VisitorWithState SetValueVisitor = new VisitorWithState() {
public void visit(ThreadContext context, RubyHash self, IRubyObject key, IRubyObject val, int index, RubyArray target) {
target.eltInternalSet(index, val);
}
};
@JRubyMethod(name = "shift")
public IRubyObject shift(ThreadContext context) {
return getDelegate().shift(context);
}
public boolean fastDelete(IRubyObject key) {
return getDelegate().fastDelete(key);
}
@JRubyMethod
public IRubyObject delete(ThreadContext context, IRubyObject key, Block block) {
return getDelegate().delete(context, key, block);
}
public IRubyObject delete(IRubyObject key) {
return getDelegate().delete(key);
}
public IRubyObject delete(ThreadContext context, IRubyObject key) {
return getDelegate().delete(context, key);
}
@JRubyMethod(name = "select", alias = "filter")
public IRubyObject select(final ThreadContext context, final Block block) {
return getDelegate().select(context, block);
}
@JRubyMethod(name = "slice", rest = true)
public RubyHash slice(final ThreadContext context, final IRubyObject[] args) {
return getDelegate().slice(context, args);
}
public RubyHash delete_ifInternal(ThreadContext context, Block block) {
return getDelegate().delete_ifInternal(context, block);
}
@JRubyMethod
public IRubyObject delete_if(final ThreadContext context, final Block block) {
return getDelegate().delete_if(context, block);
}
public RubyHash rejectInternal(ThreadContext context, Block block) {
return getDelegate().rejectInternal(context, block);
}
@JRubyMethod
public IRubyObject reject(final ThreadContext context, final Block block) {
return getDelegate().reject(context, block);
}
public IRubyObject reject_bangInternal(ThreadContext context, Block block) {
return getDelegate().reject_bangInternal(context, block);
}
@JRubyMethod(name = "reject!")
public IRubyObject reject_bang(final ThreadContext context, final Block block) {
return getDelegate().reject_bang(context, block);
}
@JRubyMethod(name = "clear")
public RubyHash rb_clear(ThreadContext context) {
return getDelegate().rb_clear(context);
}
@JRubyMethod(name = "invert")
public RubyHash invert(final ThreadContext context) {
return getDelegate().invert(context);
}
@JRubyMethod(name = {"merge!", "update"}, rest = true)
public RubyHash merge_bang(ThreadContext context, IRubyObject[] others, Block block) {
return getDelegate().merge_bang(context, others, block);
}
public void addAll(ThreadContext context, RubyHash otherHash) {
getDelegate().addAll(context, otherHash);
}
@JRubyMethod(rest = true)
public RubyHash merge(ThreadContext context, IRubyObject[] others, Block block) {
return getDelegate().merge(context, others, block);
}
@JRubyMethod(name = "initialize_copy", visibility = PRIVATE)
public RubyHash initialize_copy(ThreadContext context, IRubyObject other) {
return getDelegate().initialize_copy(context, other);
}
@JRubyMethod(name = "replace")
public RubyHash replace(final ThreadContext context, IRubyObject other) {
return getDelegate().replace(context, other);
}
@JRubyMethod(name = "values_at", rest = true)
public RubyArray values_at(ThreadContext context, IRubyObject[] args) {
return getDelegate().values_at(context, args);
}
@JRubyMethod(name = "fetch_values", rest = true)
public RubyArray fetch_values(ThreadContext context, IRubyObject[] args, Block block) {
return getDelegate().fetch_values(context, args, block);
}
@JRubyMethod(name = "assoc")
public IRubyObject assoc(final ThreadContext context, final IRubyObject obj) {
return getDelegate().assoc(context, obj);
}
@JRubyMethod(name = "rassoc")
public IRubyObject rassoc(final ThreadContext context, final IRubyObject obj) {
return getDelegate().rassoc(context, obj);
}
@JRubyMethod
public IRubyObject flatten(ThreadContext context) {
return getDelegate().flatten(context);
}
@JRubyMethod
public IRubyObject flatten(ThreadContext context, IRubyObject level) {
return getDelegate().flatten(context, level);
}
@JRubyMethod(name = "compact")
public IRubyObject compact(ThreadContext context) {
return getDelegate().compact(context);
}
@JRubyMethod(name = "compact!")
public IRubyObject compact_bang(ThreadContext context) {
return getDelegate().compact_bang(context);
}
@JRubyMethod(name = "compare_by_identity")
public IRubyObject compare_by_identity(ThreadContext context) {
return getDelegate().compare_by_identity(context);
}
@JRubyMethod(name = "compare_by_identity?")
public IRubyObject compare_by_identity_p(ThreadContext context) {
return getDelegate().compare_by_identity_p(context);
}
@JRubyMethod
public IRubyObject dup(ThreadContext context) {
return getDelegate().dup(context);
}
public IRubyObject rbClone(ThreadContext context, IRubyObject opts) {
return super.rbClone(context, opts);
}
public IRubyObject rbClone(ThreadContext context) {
return super.rbClone(context, context.nil);
}
@JRubyMethod(name = "any?")
public IRubyObject any_p(ThreadContext context, Block block) {
return getDelegate().any_p(context, block);
}
@JRubyMethod(name = "any?")
public IRubyObject any_p(ThreadContext context, IRubyObject pattern, Block block) {
return getDelegate().any_p(context, pattern, block);
}
/**
* A lightweight dup for internal use that does not dispatch to initialize_copy nor rehash the keys. Intended for
* use in dup'ing keyword args for processing.
*
* @param context
* @return
*/
public RubyHash dupFast(final ThreadContext context) {
return getDelegate().dupFast(context);
}
public RubyHash withRuby2Keywords(boolean ruby2Keywords) {
return getDelegate().withRuby2Keywords(ruby2Keywords);
}
public boolean hasDefaultProc() {
return getDelegate().hasDefaultProc();
}
public IRubyObject getIfNone() {
return getDelegate().getIfNone();
}
@JRubyMethod
public IRubyObject deconstruct_keys(ThreadContext context, IRubyObject _arg1) {
return getDelegate().deconstruct_keys(context, _arg1);
}
@JRubyMethod(name = "dig")
public IRubyObject dig(ThreadContext context, IRubyObject arg0) {
return getDelegate().dig(context, arg0);
}
@JRubyMethod(name = "dig")
public IRubyObject dig(ThreadContext context, IRubyObject arg0, IRubyObject arg1) {
return getDelegate().dig(context, arg0, arg1);
}
@JRubyMethod(name = "dig")
public IRubyObject dig(ThreadContext context, IRubyObject arg0, IRubyObject arg1, IRubyObject arg2) {
return getDelegate().dig(context, arg0, arg1, arg2);
}
@JRubyMethod(name = "dig", required = 1, rest = true, checkArity = false)
public IRubyObject dig(ThreadContext context, IRubyObject[] args) {
return getDelegate().dig(context, args);
}
@JRubyMethod
public IRubyObject to_proc(ThreadContext context) {
return getDelegate().to_proc(context);
}
@JRubyMethod(meta = true)
public static IRubyObject ruby2_keywords_hash(ThreadContext context, IRubyObject _self, IRubyObject arg) {
TypeConverter.checkType(context, arg, hashClass(context));
RubyHash hash = (RubyHash) arg.dup();
hash.setRuby2KeywordHash(true);
return hash;
}
@JRubyMethod(meta = true, name = "ruby2_keywords_hash?")
public static IRubyObject ruby2_keywords_hash_p(ThreadContext context, IRubyObject _self, IRubyObject arg) {
TypeConverter.checkType(context, arg, hashClass(context));
return asBoolean(context, ((RubyHash) arg).isRuby2KeywordHash());
}
protected static class VisitorIOException extends RuntimeException {
VisitorIOException(Throwable cause) {
super(cause);
}
}
// FIXME: Total hack to get flash in Rails marshalling/unmarshalling in session ok...We need
// to totally change marshalling to work with overridden core classes.
@Deprecated(since = "10.0.0.0", forRemoval = true)
@SuppressWarnings("removal")
public static void marshalTo(final RubyHash hash, final org.jruby.runtime.marshal.MarshalStream output) throws IOException {
var context = hash.getRuntime().getCurrentContext();
output.registerLinkTarget(context, hash);
int hashSize = hash.size();
output.writeInt(hashSize);
try {
hash.visitLimited(context, MarshalDumpVisitor, hashSize, output);
} catch (VisitorIOException e) {
throw (IOException)e.getCause();
}
if (hash.getIfNone() != UNDEF) output.dumpObject(hash.getIfNone());
}
public static void marshalTo(ThreadContext context, RubyOutputStream out, final RubyHash hash, final MarshalDumper output) {
output.registerObject(hash);
int hashSize = hash.size();
output.writeInt(out, hashSize);
try {
hash.visitLimited(context, new VisitorWithState() {
@Override
public void visit(ThreadContext context, RubyHash self, IRubyObject key, IRubyObject value, int index, MarshalDumper state) {
state.dumpObject(context, out, key);
state.dumpObject(context, out, value);
}
}, hashSize, output);
} catch (VisitorIOException e) {
throw Error.toRubyException(context, (IOException) e.getCause());
}
if (hash.getIfNone() != UNDEF) output.dumpObject(context, out, hash.getIfNone());
}
@Deprecated(since = "10.0.0.0", forRemoval = true)
@SuppressWarnings("removal")
private static final VisitorWithState MarshalDumpVisitor = new VisitorWithState() {
@Override
public void visit(ThreadContext context, RubyHash self, IRubyObject key, IRubyObject value, int index, org.jruby.runtime.marshal.MarshalStream output) {
try {
output.dumpObject(key);
output.dumpObject(value);
} catch (IOException e) {
throw new VisitorIOException(e);
}
}
};
@Deprecated(since = "10.0.0.0", forRemoval = true)
@SuppressWarnings("removal")
public static RubyHash unmarshalFrom(org.jruby.runtime.marshal.UnmarshalStream input, boolean defaultValue) throws IOException {
Ruby runtime = input.getRuntime();
RubyHash result = (RubyHash) input.entry(newHash(runtime));
int size = input.unmarshalInt();
for (int i = 0; i < size; i++) {
result.fastASetCheckString(runtime, input.unmarshalObject(), input.unmarshalObject());
}
if (defaultValue) result.default_value_set(runtime.getCurrentContext(), input.unmarshalObject());
return result;
}
public static RubyHash unmarshalFrom(ThreadContext context, RubyInputStream in, MarshalLoader input, boolean defaultValue, boolean identity) {
RubyHash result = Create.newHash(context);
if (identity) result.setComparedByIdentity(true);
result = (RubyHash) input.entry(result);
int size = input.unmarshalInt(context, in);
for (int i = 0; i < size; i++) {
result.fastASetCheckString(context.runtime, input.unmarshalObject(context, in), input.unmarshalObject(context, in));
}
if (defaultValue) result.default_value_set(context, input.unmarshalObject(context, in));
return result;
}
@Override
public Class getJavaClass() {
return Map.class;
}
// Satisfy java.util.Map interface (for Java integration)
@Override
public int size() {
return getDelegate().size();
}
@Override
public boolean isEmpty() {
return getDelegate().isEmpty();
}
@Override
public boolean containsKey(Object key) {
return getDelegate().containsKey(key);
}
@Override
public boolean containsValue(Object value) {
return getDelegate().containsValue(value);
}
@Override
public Object get(Object key) {
return getDelegate().get(key);
}
@Override
public Object put(Object key, Object value) {
return getDelegate().put(key, value);
}
@Override
public Object remove(Object key) {
return getDelegate().remove(key);
}
@Override
public void putAll(Map map) {
getDelegate().putAll(map);
}
@Override
public void clear() {
getDelegate().clear();
}
@Override
public boolean equals(Object other) {
return getDelegate().equals(other);
}
@Override
public Set keySet() {
return getDelegate().keySet();
}
@Override
public Collection values() {
return getDelegate().values();
}
@Override
public Set entrySet() {
return getDelegate().entrySet();
}
public Set directKeySet() {
return getDelegate().directKeySet();
}
public Collection directValues() {
return getDelegate().directValues();
}
public Set directEntrySet() {
return getDelegate().directEntrySet();
}
public boolean isComparedByIdentity() {
return getDelegate().isComparedByIdentity();
}
public void setComparedByIdentity(boolean comparedByIdentity) {
getDelegate().setComparedByIdentity(comparedByIdentity);
}
public boolean isRuby2KeywordHash() {
return getDelegate().isRuby2KeywordHash();
}
public void setRuby2KeywordHash(boolean value) {
getDelegate().setRuby2KeywordHash(value);
}
protected void setHashFlag(int flag, boolean set) {
getDelegate().setHashFlag(flag, set);
}
protected boolean hasHashFlag(int flag) {
return getDelegate().hasHashFlag(flag);
}
// Still used by jruby-openssl
@Deprecated(since = "9.1.3.0")
public final void visitAll(Visitor visitor) {
// use -1 to disable concurrency checks
visitLimited(getRuntime().getCurrentContext(), visitor, -1, null);
}
}