blob: 2ea26ccd7eb86313c84a7080362815f0775e1903 [file] [log] [blame]
/*=============================================================================#
# Copyright (c) 2009, 2021 Stephan Wahlbrink and others.
#
# This program and the accompanying materials are made available under the
# terms of the Eclipse Public License 2.0 which is available at
# https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
# which is available at https://www.apache.org/licenses/LICENSE-2.0.
#
# SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
#
# Contributors:
# Stephan Wahlbrink <sw@wahlbrink.eu> - initial API and implementation
#=============================================================================*/
package org.eclipse.statet.rj.data.impl;
import java.util.Arrays;
import org.eclipse.statet.jcommons.lang.NonNull;
import org.eclipse.statet.jcommons.lang.NonNullByDefault;
import org.eclipse.statet.rj.data.RDataUtils;
import org.eclipse.statet.rj.data.RStore;
@NonNullByDefault
public abstract class AbstractRStore<P> implements RStore<P> {
protected static final long NA_numeric_LONG= 0x7ff80000000007a2L;
protected static final long NA_numeric_LONG_MASK= 0x7ff00000ffffffffL;
protected static final long NA_numeric_LONG_MATCH= 0x7ff00000000007a2L;
protected static final int NA_numeric_INT_MATCH= 0x000007a2;
protected static final double NA_numeric_DOUBLE= Double.longBitsToDouble(NA_numeric_LONG);
protected static final long NaN_numeric_LONG= Double.doubleToLongBits(Double.NaN);
protected static final double NaN_numeric_DOUBLE= Double.NaN;
protected static final int NA_integer_INT= Integer.MIN_VALUE;
protected static final byte NA_byte_BYTE= 0x0; // no real NA, used e.g. for new values
protected static final byte NA_logical_BYTE= 2;
protected static final int NA_logical_INT= Integer.MIN_VALUE;
protected static final int WITH_NAMES= 0x1;
protected static final byte[] EMPTY_BYTE_ARRAY= new byte[0];
protected static final byte [] @NonNull[] EMPTY_BYTE_2dARRAY= new byte[0][];
protected static final int[] EMPTY_INT_ARRAY= new int[0];
protected static final int [] @NonNull[] EMPTY_INT_2dARRAY= new int[0][];
protected static final double[] EMPTY_DOUBLE_ARRAY= new double[0];
protected static final double [] @NonNull[] EMPTY_DOUBLE_2dARRAY= new double[0][];
protected static final String[] EMPTY_STRING_ARRAY= new String[0];
protected static final String [] @NonNull[] EMPTY_STRING_2dARRAY= new String[0][];
protected static final int DEFAULT_LONG_DATA_SEGMENT_LENGTH= 1 << 28;
private final static boolean gIsBSupported;
static {
gIsBSupported= checkIsBSupported();
}
public static boolean isBforNASupported() {
return gIsBSupported;
}
private static boolean checkIsBSupported() {
double d= 0;
long l= 0;
d= NA_numeric_DOUBLE;
l= Double.doubleToRawLongBits(d);
if (l != NA_numeric_LONG) {
return false;
}
d= Double.longBitsToDouble(l);
l= Double.doubleToRawLongBits(d);
if (l != NA_numeric_LONG) {
return false;
}
return true;
}
protected static final long check2dArrayLength(final boolean [] @NonNull[] array, final int segmentLength) {
long length= 0;
if (array.length > 0) {
final int last= array.length - 1;
for (int i= 0; i < last; i++) {
if (array[i].length != segmentLength) {
throw new IllegalArgumentException("Unexpected data segment length (" + array[i].length + ", but " + segmentLength + " expected)");
}
}
length= last * (long)segmentLength;
if (array[last].length > segmentLength) {
throw new IllegalArgumentException("Unexpected data segment length (" + array[last].length + ", but max " + segmentLength + " expected)");
}
length+= array[last].length;
}
return length;
}
protected static final long check2dArrayLength(final int [] @NonNull[] array, final int segmentLength) {
long length= 0;
if (array.length > 0) {
final int last= array.length - 1;
for (int i= 0; i < last; i++) {
if (array[i].length != segmentLength) {
throw new IllegalArgumentException("Unexpected data segment length (" + array[i].length + ", but " + segmentLength + " expected)");
}
}
length= last * (long) segmentLength;
if (array[last].length > segmentLength) {
throw new IllegalArgumentException("Unexpected data segment length (" + array[last].length + ", but max " + segmentLength + " expected)");
}
length+= array[last].length;
}
return length;
}
protected static final long check2dArrayLength(final double[] @NonNull[] array, final int segmentLength) {
long length= 0;
if (array.length > 0) {
final int last= array.length - 1;
for (int i= 0; i < last; i++) {
if (array[i].length != segmentLength) {
throw new IllegalArgumentException("Unexpected data segment length (" + array[i].length + ", but " + segmentLength + " expected)");
}
}
length= last * (long) segmentLength;
if (array[last].length > segmentLength) {
throw new IllegalArgumentException("Unexpected data segment length (" + array[last].length + ", but max " + segmentLength + " expected)");
}
length+= array[last].length;
}
return length;
}
protected static final long check2dArrayLength(final byte [] @NonNull[] array, final int segmentLength) {
long length= 0;
if (array.length > 0) {
final int last= array.length - 1;
for (int i= 0; i < last; i++) {
if (array[i].length != segmentLength) {
throw new IllegalArgumentException("Unexpected data segment length (" + array[i].length + ", but " + segmentLength + " expected)");
}
}
length= last * (long) segmentLength;
if (array[last].length > segmentLength) {
throw new IllegalArgumentException("Unexpected data segment length (" + array[last].length + ", but max " + segmentLength + " expected)");
}
length+= array[last].length;
}
return length;
}
protected static final long check2dArrayLength(final Object [] @NonNull[] array, final int segmentLength) {
long length= 0;
if (array.length > 0) {
final int last= array.length - 1;
for (int i= 0; i < last; i++) {
if (array[i].length != segmentLength) {
throw new IllegalArgumentException("Unexpected data segment length (" + array[i].length + ", but " + segmentLength + " expected)");
}
}
length= last * (long) segmentLength;
if (array[last].length > segmentLength) {
throw new IllegalArgumentException("Unexpected data segment length (" + array[last].length + ", but max " + segmentLength + " expected)");
}
length+= array[last].length;
}
return length;
}
protected static final int [] @NonNull[] new2dIntArray(final long length, final int segmentLength) {
if (length == 0) {
return EMPTY_INT_2dARRAY;
}
final var array= new int [1 + (int)((length - 1) / segmentLength)] @NonNull[];
final int last= array.length - 1;
for (int i= 0; i < last; i++) {
array[i]= new int[segmentLength];
}
{ final int restLength= (int)(length % segmentLength);
array[last]= new int[(restLength == 0) ? segmentLength : restLength];
}
return array;
}
protected static final double [] @NonNull[] new2dDoubleArray(final long length, final int segmentLength) {
if (length == 0) {
return EMPTY_DOUBLE_2dARRAY;
}
final var array= new double [1 + (int)((length - 1) / segmentLength)] @NonNull[];
final int last= array.length - 1;
for (int i= 0; i < last; i++) {
array[i]= new double[segmentLength];
}
{ final int restLength= (int)(length % segmentLength);
array[last]= new double[(restLength == 0) ? segmentLength : restLength];
}
return array;
}
protected static final String [] @NonNull[] new2dStringArray(final long length, final int segmentLength) {
if (length == 0) {
return EMPTY_STRING_2dARRAY;
}
final var array= new String [1 + (int)((length - 1) / segmentLength)] @NonNull[];
final int last= array.length - 1;
for (int i= 0; i < last; i++) {
array[i]= new String[segmentLength];
}
{ final int restLength= (int)(length % segmentLength);
array[last]= new String[(restLength == 0) ? segmentLength : restLength];
}
return array;
}
protected static final byte [] @NonNull[] new2dByteArray(final long length, final int segmentLength) {
if (length == 0) {
return EMPTY_BYTE_2dARRAY;
}
final var array= new byte [1 + (int)((length - 1) / segmentLength)] @NonNull[];
final int last= array.length - 1;
for (int i= 0; i < last; i++) {
array[i]= new byte[segmentLength];
}
{ final int restLength= (int)(length % segmentLength);
array[last]= new byte[(restLength == 0) ? segmentLength : restLength];
}
return array;
}
protected static final int[] addIdx(final int[] idxs, final int newIdx) {
int i= Arrays.binarySearch(idxs, newIdx);
if (i >= 0) {
return idxs;
}
final int[] newIdxs= new int[idxs.length+1];
i= -1-i;
System.arraycopy(idxs, 0, newIdxs, 0, i);
newIdxs[i]= newIdx;
System.arraycopy(idxs, i, newIdxs, i+1, idxs.length-i);
return newIdxs;
}
protected static final int[] insertIdx(final int[] currentIdxs, final int[] insertedIdxs) {
final int[] pos= new int[insertedIdxs.length];
for (int i= 0; i < pos.length; i++) {
pos[i]= Arrays.binarySearch(currentIdxs, insertedIdxs[i]);
if (pos[i] < 0) {
pos[i]= -1-pos[i];
}
}
final int[] newIdxs= new int[currentIdxs.length+insertedIdxs.length];
int l= pos.length;
int ci= currentIdxs.length-1;
while(l > 1) {
final int stop= pos[l-1];
for (; ci >= stop; ci--) {
newIdxs[ci+l]= currentIdxs[ci] + l;
}
l--;
newIdxs[ci+l]= insertedIdxs[l] + l;
}
System.arraycopy(currentIdxs, 0, newIdxs, 0, pos[0]);
return newIdxs;
}
protected static final void updateIdxInserted(final int[] currentIdxs, final int[] insertedIdxs) {
int pos= Arrays.binarySearch(currentIdxs, insertedIdxs[0]);
if (pos < 0) {
pos= -1-pos;
}
for (int i= 0; i < insertedIdxs.length; ) {
i++;
int stop= (i < insertedIdxs.length) ? Arrays.binarySearch(currentIdxs, insertedIdxs[i]) : currentIdxs.length;
if (stop < 0) {
stop= -1-stop;
}
while (pos < stop) {
currentIdxs[pos]+= i;
}
pos= stop;
}
}
protected static final int[] updateIdxRemoved(final int[] currentIdxs, final int[] removedIdxs) {
final int[] pos= new int[removedIdxs.length];
int naCount= 0;
for (int i= 0; i < pos.length; i++) {
pos[i]= Arrays.binarySearch(currentIdxs, removedIdxs[i]);
if (pos[i] < 0) {
pos[i]= -1-pos[i];
}
else {
naCount++;
}
}
final int[] newIdxs= (naCount == 0) ? currentIdxs : new int[currentIdxs.length-naCount];
int l= 0;
int ci= 0;
int ni= 0;
if (newIdxs != currentIdxs) {
System.arraycopy(currentIdxs, 0, newIdxs, 0, pos[0]);
}
while(l < removedIdxs.length) {
if (currentIdxs[pos[l]] == removedIdxs[l]) {
ci++;
}
l++;
final int stop= (l < removedIdxs.length) ? pos[l] : currentIdxs.length;
for (; ci < stop; ci++, ni++) {
newIdxs[ni]= currentIdxs[ci] - l;
}
}
return newIdxs;
}
protected static final int[] deleteIdx(final int[] idxs, final int removeIdx) {
final int i= Arrays.binarySearch(idxs, removeIdx);
if (i < 0) {
return idxs;
}
final int[] newIdxs= new int[idxs.length-1];
System.arraycopy(idxs, 0, newIdxs, 0, i);
System.arraycopy(idxs, i+1, newIdxs, i, idxs.length-i-1);
return newIdxs;
}
protected static final int getNewArraySize(final int length) {
if (length >= 0xfffffff) {
return Integer.MAX_VALUE;
}
return ((length+0x7) | 0xf) + 1;
}
protected static final double[] ensureCapacity(final double[] currentValues, final int length) {
if (currentValues.length >= length) {
return currentValues;
}
return new double[getNewArraySize(length)];
}
protected static final int[] ensureCapacity(final int[] currentValues, final int length) {
final int diff= currentValues.length - length;
if (diff >= 0 && diff <= 512) {
return currentValues;
}
return new int[getNewArraySize(length)];
}
protected static final byte[] ensureCapacity(final byte[] currentValues, final int length) {
if (currentValues.length >= length) {
return currentValues;
}
return new byte[getNewArraySize(length)];
}
protected static final String[] ensureCapacity(final String[] currentValues, final int length) {
if (currentValues.length >= length) {
return currentValues;
}
return new String[getNewArraySize(length)];
}
protected static final double[] prepareInsert(final double[] currentValues, final int currentLength, final int[] idxs) {
final double[] newValues= ensureCapacity(currentValues, currentLength+idxs.length);
int i= idxs.length-1;
System.arraycopy(currentValues, idxs[i], newValues, idxs[i]+i+1, currentLength-idxs[i]);
for (i--; i >= 0; i--) {
System.arraycopy(currentValues, idxs[i], newValues, idxs[i]+i+1, idxs[i+1]-idxs[i]);
}
if (newValues != currentValues) {
System.arraycopy(currentValues, 0, newValues, 0, idxs[0]);
}
return newValues;
}
protected static final int[] prepareInsert(final int[] currentValues, final int currentLength, final int[] idxs) {
final int[] newValues= ensureCapacity(currentValues, currentLength+idxs.length);
int i= idxs.length-1;
System.arraycopy(currentValues, idxs[i], newValues, idxs[i]+i+1, currentLength-idxs[i]);
for (i--; i >= 0; i--) {
System.arraycopy(currentValues, idxs[i], newValues, idxs[i]+i+1, idxs[i+1]-idxs[i]);
}
if (newValues != currentValues) {
System.arraycopy(currentValues, 0, newValues, 0, idxs[0]);
}
return newValues;
}
protected static final byte[] prepareInsert(final byte[] currentValues, final int currentLength, final int[] idxs) {
final byte[] newValues= ensureCapacity(currentValues, currentLength+idxs.length);
int i= idxs.length-1;
System.arraycopy(currentValues, idxs[i], newValues, idxs[i]+i+1, currentLength-idxs[i]);
for (i--; i >= 0; i--) {
System.arraycopy(currentValues, idxs[i], newValues, idxs[i]+i+1, idxs[i+1]-idxs[i]);
}
if (currentValues != newValues) {
System.arraycopy(currentValues, 0, newValues, 0, idxs[0]);
}
return newValues;
}
protected static final String[] prepareInsert(final String[] currentValues, final int currentLength, final int[] idxs) {
final String[] newValues= ensureCapacity(currentValues, currentLength+idxs.length);
int i= idxs.length-1;
System.arraycopy(currentValues, idxs[i], newValues, idxs[i]+i+1, currentLength-idxs[i]);
for (i--; i >= 0; i--) {
System.arraycopy(currentValues, idxs[i], newValues, idxs[i]+i+1, idxs[i+1]-idxs[i]);
}
if (currentValues != newValues) {
System.arraycopy(currentValues, 0, newValues, 0, idxs[0]);
}
return newValues;
}
protected static final double[] remove(final double[] currentValues, final int currentLength, final int[] idxs) {
final double[] newValues= ensureCapacity(currentValues, currentLength-idxs.length);
if (currentValues != newValues) {
System.arraycopy(currentValues, 0, newValues, 0, idxs[0]);
}
int i= 0;
for (; i < idxs.length-1; i++) {
System.arraycopy(currentValues, idxs[i]+1, newValues, idxs[i]-i, idxs[i+1]-idxs[i]);
}
System.arraycopy(currentValues, idxs[i]+1, newValues, idxs[i]-i, currentLength-idxs[i]-1);
return newValues;
}
protected static final int[] remove(final int[] currentValues, final int currentLength, final int[] idxs) {
final int[] newValues= ensureCapacity(currentValues, currentLength-idxs.length);
if (currentValues != newValues) {
System.arraycopy(currentValues, 0, newValues, 0, idxs[0]);
}
int i= 0;
for (; i < idxs.length-1; i++) {
System.arraycopy(currentValues, idxs[i]+1, newValues, idxs[i]-i, idxs[i+1]-idxs[i]);
}
System.arraycopy(currentValues, idxs[i]+1, newValues, idxs[i]-i, currentLength-idxs[i]-1);
return newValues;
}
protected static final byte[] remove(final byte[] currentValues, final int currentLength, final int[] idxs) {
final byte[] newValues= ensureCapacity(currentValues, currentLength-idxs.length);
if (currentValues != newValues) {
System.arraycopy(currentValues, 0, newValues, 0, idxs[0]);
}
int i= 0;
for (; i < idxs.length-1; i++) {
System.arraycopy(currentValues, idxs[i]+1, newValues, idxs[i]-i, idxs[i+1]-idxs[i]);
}
System.arraycopy(currentValues, idxs[i]+1, newValues, idxs[i]-i, currentLength-idxs[i]-1);
return newValues;
}
protected static final String[] remove(final String[] currentValues, final int currentLength, final int[] idxs) {
final String[] newValues= ensureCapacity(currentValues, currentLength-idxs.length);
if (currentValues != newValues) {
System.arraycopy(currentValues, 0, newValues, 0, idxs[0]);
}
int i= 0;
for (; i < idxs.length-1; i++) {
System.arraycopy(currentValues, idxs[i]+1, newValues, idxs[i]-i, idxs[i+1]-idxs[i]);
}
System.arraycopy(currentValues, idxs[i]+1, newValues, idxs[i]-i, currentLength-idxs[i]-1);
return newValues;
}
@Override
public void setNA(final int idx) {
throw new UnsupportedOperationException();
}
@Override
public void setNA(final long idx) {
throw new UnsupportedOperationException();
}
@Override
public boolean getLogi(final int idx) {
throw new UnsupportedOperationException();
}
@Override
public boolean getLogi(final long idx) {
throw new UnsupportedOperationException();
}
@Override
public void setLogi(final int idx, final boolean logi) {
throw new UnsupportedOperationException();
}
@Override
public void setLogi(final long idx, final boolean logi) {
throw new UnsupportedOperationException();
}
@Override
public int getInt(final int idx) {
throw new UnsupportedOperationException();
}
@Override
public int getInt(final long idx) {
throw new UnsupportedOperationException();
}
@Override
public void setInt(final int idx, final int integer) {
throw new UnsupportedOperationException();
}
@Override
public void setInt(final long idx, final int integer) {
throw new UnsupportedOperationException();
}
@Override
public double getNum(final int idx) {
throw new UnsupportedOperationException();
}
@Override
public double getNum(final long idx) {
throw new UnsupportedOperationException();
}
@Override
public void setNum(final int idx, final double real) {
throw new UnsupportedOperationException();
}
@Override
public void setNum(final long idx, final double real) {
throw new UnsupportedOperationException();
}
@Override
public String getChar(final int idx) {
throw new UnsupportedOperationException();
}
@Override
public String getChar(final long idx) {
throw new UnsupportedOperationException();
}
@Override
public void setChar(final int idx, final String character) {
throw new UnsupportedOperationException();
}
@Override
public void setChar(final long idx, final String character) {
throw new UnsupportedOperationException();
}
@Override
public byte getRaw(final int idx) {
throw new UnsupportedOperationException();
}
@Override
public byte getRaw(final long idx) {
throw new UnsupportedOperationException();
}
@Override
public void setRaw(final int idx, final byte raw) {
throw new UnsupportedOperationException();
}
@Override
public void setRaw(final long idx, final byte raw) {
throw new UnsupportedOperationException();
}
@Override
public double getCplxIm(final int idx) {
throw new UnsupportedOperationException();
}
@Override
public double getCplxIm(final long idx) {
throw new UnsupportedOperationException();
}
@Override
public double getCplxRe(final int idx) {
throw new UnsupportedOperationException();
}
@Override
public double getCplxRe(final long idx) {
throw new UnsupportedOperationException();
}
@Override
public void setCplx(final int idx, final double real, final double imaginary) {
throw new UnsupportedOperationException();
}
@Override
public void setCplx(final long idx, final double real, final double imaginary) {
throw new UnsupportedOperationException();
}
protected abstract boolean isStructOnly();
@Override
public boolean containsNA() {
return (indexOfNA(0) >= 0);
}
@Override
public long indexOfNA() {
return indexOfNA(0);
}
@Override
public long indexOfNA(long fromIdx) {
if (fromIdx < 0) {
fromIdx= 0;
}
final long length= getLength();
while (fromIdx < length) {
if (isNA(fromIdx)) {
return fromIdx;
}
fromIdx++;
}
return -1;
}
@Override
public boolean contains(final int integer) {
return (indexOf(integer, 0) >= 0);
}
@Override
public final long indexOf(final int integer) {
return indexOf(integer, 0);
}
@Override
public long indexOf(final int integer, long fromIdx) {
if (fromIdx < 0) {
fromIdx= 0;
}
final long l= getLength();
while (fromIdx < l) {
if (!isNA(fromIdx) && integer == getInt(fromIdx)) {
return fromIdx;
}
fromIdx++;
}
return -1;
}
@Override
public boolean contains(final String character) {
return (indexOf(character, 0) >= 0);
}
@Override
public final long indexOf(final String character) {
return indexOf(character, 0);
}
@Override
public long indexOf(final String character, long fromIdx) {
if (character == null) {
return -1;
}
if (fromIdx < 0) {
fromIdx= 0;
}
final long l= getLength();
while (fromIdx < l) {
if (!isNA(fromIdx) && character.equals(getChar(fromIdx))) {
return fromIdx;
}
fromIdx++;
}
return -1;
}
protected int checkToArrayLength() {
final long length= getLength();
if (length > Integer.MAX_VALUE) {
throw new UnsupportedOperationException("Not supported for long data");
}
return (int)length;
}
@Override
public String toString() {
final StringBuilder sb= new StringBuilder();
sb.append(RDataUtils.getStoreAbbr(this));
sb.append(' ');
final long length;
if (isStructOnly()) {
sb.append("<struct only>");
}
else if ((length= getLength()) > 0) {
long end= (length <= 25) ? length : 10;
if (getStoreType() == CHARACTER) {
for (long idx= 0; true;) {
if (isNA(idx)) {
sb.append("NA");
}
else {
sb.append('"');
sb.append(getChar(idx));
sb.append('"');
}
idx++;
if (idx < end) {
sb.append(", ");
continue;
}
else {
if (end == length) {
break;
}
else {
sb.append(", .., ");
idx= length - 10;
end= length;
continue;
}
}
}
}
else {
for (long idx= 0; true;) {
if (isNA(idx)) {
sb.append("NA");
}
else {
sb.append(getChar(idx));
}
idx++;
if (idx < end) {
sb.append(", ");
continue;
}
else {
if (end == length) {
break;
}
else {
sb.append(", .., ");
idx= length - 10;
end= length;
continue;
}
}
}
}
}
return sb.toString();
}
}