/* * 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.flink.graph.types.valuearray; import org.apache.flink.core.memory.DataInputView; import org.apache.flink.core.memory.DataOutputView; import org.apache.flink.core.memory.MemorySegment; import org.apache.flink.graph.utils.Murmur3_32; import org.apache.flink.types.IntValue; import org.apache.flink.types.LongValue; import org.apache.flink.util.Preconditions; import java.io.IOException; import java.util.Arrays; import java.util.Iterator; /** * An array of {@link LongValue}. */ public class LongValueArray implements ValueArray<LongValue> { protected static final int ELEMENT_LENGTH_IN_BYTES = 8; protected static final int DEFAULT_CAPACITY_IN_BYTES = 4096; // see note in ArrayList, HashTable, ... private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8; private boolean isBounded; private long[] data; // the number of elements currently stored private int position; // location of the bookmark used by mark() and reset() private transient int mark; // hasher used to generate the normalized key private Murmur3_32 hash = new Murmur3_32(0xdf099ea8); // hash result stored as normalized key private IntValue hashValue = new IntValue(); /** * Initializes an expandable array with default capacity. */ public LongValueArray() { isBounded = false; initialize(DEFAULT_CAPACITY_IN_BYTES); } /** * Initializes a fixed-size array with the provided number of bytes. * * @param bytes number of bytes of the encapsulated array */ public LongValueArray(int bytes) { isBounded = true; initialize(bytes); } /** * Initializes the array with the provided number of bytes. * * @param bytes initial size of the encapsulated array in bytes */ private void initialize(int bytes) { int capacity = bytes / ELEMENT_LENGTH_IN_BYTES; Preconditions.checkArgument(capacity > 0, "Requested array with zero capacity"); Preconditions.checkArgument(capacity <= MAX_ARRAY_SIZE, "Requested capacity exceeds limit of " + MAX_ARRAY_SIZE); data = new long[capacity]; } // -------------------------------------------------------------------------------------------- /** * If the size of the array is insufficient to hold the given capacity then * copy the array into a new, larger array. * * @param minCapacity minimum required number of elements */ private void ensureCapacity(int minCapacity) { long currentCapacity = data.length; if (minCapacity <= currentCapacity) { return; } // increase capacity by at least ~50% long expandedCapacity = Math.max(minCapacity, currentCapacity + (currentCapacity >> 1)); int newCapacity = (int) Math.min(MAX_ARRAY_SIZE, expandedCapacity); if (newCapacity < minCapacity) { // throw exception as unbounded arrays are not expected to fill throw new RuntimeException("Requested array size " + minCapacity + " exceeds limit of " + MAX_ARRAY_SIZE); } data = Arrays.copyOf(data, newCapacity); } @Override public String toString() { StringBuilder sb = new StringBuilder("["); for (int idx = 0 ; idx < this.position ; idx++) { sb.append(data[idx]); if (idx < position - 1) { sb.append(","); } } sb.append("]"); return sb.toString(); } // -------------------------------------------------------------------------------------------- // Iterable // -------------------------------------------------------------------------------------------- private final ReadIterator iterator = new ReadIterator(); @Override public Iterator<LongValue> iterator() { iterator.reset(); return iterator; } private class ReadIterator implements Iterator<LongValue> { private LongValue value = new LongValue(); private int pos; @Override public boolean hasNext() { return pos < position; } @Override public LongValue next() { value.setValue(data[pos++]); return value; } @Override public void remove() { throw new UnsupportedOperationException("remove"); } public void reset() { pos = 0; } } // -------------------------------------------------------------------------------------------- // IOReadableWritable // -------------------------------------------------------------------------------------------- @Override public void write(DataOutputView out) throws IOException { out.writeInt(position); for (int i = 0 ; i < position ; i++) { out.writeLong(data[i]); } } @Override public void read(DataInputView in) throws IOException { position = in.readInt(); mark = 0; ensureCapacity(position); for (int i = 0 ; i < position ; i++) { data[i] = in.readLong(); } } // -------------------------------------------------------------------------------------------- // NormalizableKey // -------------------------------------------------------------------------------------------- @Override public int getMaxNormalizedKeyLen() { return hashValue.getMaxNormalizedKeyLen(); } @Override public void copyNormalizedKey(MemorySegment target, int offset, int len) { hash.reset(); hash.hash(position); for (int i = 0 ; i < position ; i++) { hash.hash(data[i]); } hashValue.setValue(hash.hash()); hashValue.copyNormalizedKey(target, offset, len); } // -------------------------------------------------------------------------------------------- // Comparable // -------------------------------------------------------------------------------------------- @Override public int compareTo(ValueArray<LongValue> o) { LongValueArray other = (LongValueArray) o; int min = Math.min(position, other.position); for (int i = 0 ; i < min ; i++) { int cmp = Long.compare(data[i], other.data[i]); if (cmp != 0) { return cmp; } } return Integer.compare(position, other.position); } // -------------------------------------------------------------------------------------------- // Key // -------------------------------------------------------------------------------------------- @Override public int hashCode() { int hash = 1; for (int i = 0 ; i < position ; i++) { hash = 31 * hash + (int) (data[i] ^ data[i] >>> 32); } return hash; } @Override public boolean equals(Object obj) { if (obj instanceof LongValueArray) { LongValueArray other = (LongValueArray) obj; if (position != other.position) { return false; } for (int i = 0 ; i < position ; i++) { if (data[i] != other.data[i]) { return false; } } return true; } return false; } // -------------------------------------------------------------------------------------------- // ResettableValue // -------------------------------------------------------------------------------------------- @Override public void setValue(ValueArray<LongValue> value) { value.copyTo(this); } // -------------------------------------------------------------------------------------------- // CopyableValue // -------------------------------------------------------------------------------------------- @Override public int getBinaryLength() { return -1; } @Override public void copyTo(ValueArray<LongValue> target) { LongValueArray other = (LongValueArray) target; other.position = position; other.mark = mark; other.ensureCapacity(position); System.arraycopy(data, 0, other.data, 0, position); } @Override public ValueArray<LongValue> copy() { ValueArray<LongValue> copy = new LongValueArray(); this.copyTo(copy); return copy; } @Override public void copy(DataInputView source, DataOutputView target) throws IOException { copyInternal(source, target); } protected static void copyInternal(DataInputView source, DataOutputView target) throws IOException { int count = source.readInt(); target.writeInt(count); int bytes = ELEMENT_LENGTH_IN_BYTES * count; target.write(source, bytes); } // -------------------------------------------------------------------------------------------- // ValueArray // -------------------------------------------------------------------------------------------- @Override public int size() { return position; } @Override public boolean isFull() { if (isBounded) { return position == data.length; } else { return position == MAX_ARRAY_SIZE; } } @Override public boolean add(LongValue value) { int newPosition = position + 1; if (newPosition > data.length) { if (isBounded) { return false; } else { ensureCapacity(newPosition); } } data[position] = value.getValue(); position = newPosition; return true; } @Override public boolean addAll(ValueArray<LongValue> other) { LongValueArray source = (LongValueArray) other; int sourceSize = source.position; int newPosition = position + sourceSize; if (newPosition > data.length) { if (isBounded) { return false; } else { ensureCapacity(newPosition); } } System.arraycopy(source.data, 0, data, position, sourceSize); position = newPosition; return true; } @Override public void clear() { position = 0; } @Override public void mark() { mark = position; } @Override public void reset() { position = mark; } }