/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache 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.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.ignite.internal.processors.hadoop.shuffle.collections;
import org.apache.ignite.IgniteCheckedException;
import org.apache.ignite.internal.processors.hadoop.HadoopJobInfo;
import org.apache.ignite.internal.processors.hadoop.HadoopTaskContext;
import org.apache.ignite.internal.util.offheap.unsafe.GridUnsafeMemory;
import org.apache.ignite.internal.util.typedef.internal.A;
import org.apache.ignite.internal.util.typedef.internal.U;
/**
* Hash multimap.
*/
public class HadoopHashMultimap extends HadoopHashMultimapBase {
/** */
private long[] tbl;
/** */
private int keys;
/**
* @param jobInfo Job info.
* @param mem Memory.
* @param cap Initial capacity.
*/
public HadoopHashMultimap(HadoopJobInfo jobInfo, GridUnsafeMemory mem, int cap) {
super(jobInfo, mem);
assert U.isPow2(cap) : cap;
tbl = new long[cap];
}
/** {@inheritDoc} */
@Override public Adder startAdding(HadoopTaskContext ctx) throws IgniteCheckedException {
return new AdderImpl(ctx);
}
/**
* Rehash.
*/
private void rehash() {
long[] newTbl = new long[tbl.length << 1];
int newMask = newTbl.length - 1;
for (long meta : tbl) {
while (meta != 0) {
long collision = collision(meta);
int idx = keyHash(meta) & newMask;
collision(meta, newTbl[idx]);
newTbl[idx] = meta;
meta = collision;
}
}
tbl = newTbl;
}
/**
* @return Keys count.
*/
public int keys() {
return keys;
}
/** {@inheritDoc} */
@Override public int capacity() {
return tbl.length;
}
/** {@inheritDoc} */
@Override protected long meta(int idx) {
return tbl[idx];
}
/**
* Adder.
*/
private class AdderImpl extends AdderBase {
/** */
private final Reader keyReader;
/**
* @param ctx Task context.
* @throws IgniteCheckedException If failed.
*/
protected AdderImpl(HadoopTaskContext ctx) throws IgniteCheckedException {
super(ctx);
keyReader = new Reader(keySer);
}
/**
* @param keyHash Key hash.
* @param keySize Key size.
* @param keyPtr Key pointer.
* @param valPtr Value page pointer.
* @param collisionPtr Pointer to meta with hash collision.
* @return Created meta page pointer.
*/
private long createMeta(int keyHash, int keySize, long keyPtr, long valPtr, long collisionPtr) {
long meta = allocate(32);
mem.writeInt(meta, keyHash);
mem.writeInt(meta + 4, keySize);
mem.writeLong(meta + 8, keyPtr);
mem.writeLong(meta + 16, valPtr);
mem.writeLong(meta + 24, collisionPtr);
return meta;
}
/** {@inheritDoc} */
@Override public void write(Object key, Object val) throws IgniteCheckedException {
A.notNull(val, "val");
int keyHash = U.hash(key.hashCode());
// Write value.
long valPtr = write(12, val, valSer);
int valSize = writtenSize() - 12;
valueSize(valPtr, valSize);
// Find position in table.
int idx = keyHash & (tbl.length - 1);
long meta = tbl[idx];
// Search for our key in collisions.
while (meta != 0) {
if (keyHash(meta) == keyHash && key.equals(keyReader.readKey(meta))) { // Found key.
nextValue(valPtr, value(meta));
value(meta, valPtr);
return;
}
meta = collision(meta);
}
// Write key.
long keyPtr = write(0, key, keySer);
int keySize = writtenSize();
nextValue(valPtr, 0);
tbl[idx] = createMeta(keyHash, keySize, keyPtr, valPtr, tbl[idx]);
if (++keys > (tbl.length >>> 2) * 3)
rehash();
}
}
}