com.yahoo.slime.ObjectValue Maven / Gradle / Ivy
// Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.slime;
/**
* A Value holding a slime "Object", a dynamic collection of named
* value fields. Fields can be inspected or traversed using the
* {@link Inspector} interface, and you can add new fields by using the
* various "set" methods in the @ref Cursor interface.
*
* @author havardpe
*/
final class ObjectValue extends Value {
private int capacity = 16;
private int hashSize() { return (capacity + (capacity >> 1) - 1); }
private int used = 0;
private Value[] values = new Value[capacity];
private int[] hash = new int[capacity + hashSize() + (capacity << 1)];
private final SymbolTable names;
private void rehash() {
capacity = (capacity << 1);
Value[] v = values;
values = new Value[capacity];
System.arraycopy(v, 0, values, 0, used);
int[] h = hash;
hash = new int[capacity + hashSize() + (capacity << 1)];
System.arraycopy(h, 0, hash, 0, used);
for (int i = 0; i < used; i++) {
int prev = (capacity + (hash[i] % hashSize()));
int entry = hash[prev];
while (entry != 0) {
prev = entry + 1;
entry = hash[prev];
}
final int insertIdx = (capacity + hashSize() + (i << 1));
hash[prev] = insertIdx;
hash[insertIdx] = i;
}
}
private Value put(int sym, Value value) {
if (used == capacity) {
rehash();
}
int prev = (capacity + (sym % hashSize()));
int entry = hash[prev];
while (entry != 0) {
final int idx = hash[entry];
if (hash[idx] == sym) { // found entry
return NixValue.invalid();
}
prev = entry + 1;
entry = hash[prev];
}
final int insertIdx = (capacity + hashSize() + (used << 1));
hash[prev] = insertIdx;
hash[insertIdx] = used;
hash[used] = sym;
values[used++] = value;
return value;
}
private Value get(int sym) {
int entry = hash[capacity + (sym % hashSize())];
while (entry != 0) {
final int idx = hash[entry];
if (hash[idx] == sym) { // found entry
return values[idx];
}
entry = hash[entry + 1];
}
return NixValue.invalid();
}
public ObjectValue(SymbolTable names) { this.names = names; }
public ObjectValue(SymbolTable names, int sym, Value value) {
this.names = names;
put(sym, value);
}
public Type type() { return Type.OBJECT; }
public int children() { return used; }
public int fields() { return used; }
public Value field(int sym) { return get(sym); }
public Value field(String name) { return get(names.lookup(name)); }
public void accept(Visitor v) { v.visitObject(this); }
public void traverse(ObjectSymbolTraverser ot) {
for (int i = 0; i < used; ++i) {
ot.field(hash[i], values[i]);
}
}
public void traverse(ObjectTraverser ot) {
for (int i = 0; i < used; ++i) {
ot.field(names.inspect(hash[i]), values[i]);
}
}
protected Cursor setLeaf(int sym, Value value) { return put(sym, value); }
public Cursor setArray(int sym) { return put(sym, new ArrayValue(names)); }
public Cursor setObject(int sym) { return put(sym, new ObjectValue(names)); }
protected Cursor setLeaf(String name, Value value) { return put(names.insert(name), value); }
public Cursor setArray(String name) { return put(names.insert(name), new ArrayValue(names)); }
public Cursor setObject(String name) { return put(names.insert(name), new ObjectValue(names)); }
}