com.antsdb.saltedfish.nosql.Row.java Source code

Java tutorial

Introduction

Here is the source code for com.antsdb.saltedfish.nosql.Row.java

Source

/*-------------------------------------------------------------------------------------------------
 _______ __   _ _______ _______ ______  ______
 |_____| | \  |    |    |______ |     \ |_____]
 |     | |  \_|    |    ______| |_____/ |_____]
    
 Copyright (c) 2016, antsdb.com and/or its affiliates. All rights reserved. *-xguo0<@
    
 This program is free software: you can redistribute it and/or modify it under the terms of the
 GNU Affero General Public License, version 3, as published by the Free Software Foundation.
    
 You should have received a copy of the GNU Affero General Public License along with this program.
 If not, see <https://www.gnu.org/licenses/agpl-3.0.txt>
-------------------------------------------------------------------------------------------------*/
package com.antsdb.saltedfish.nosql;

import java.util.Collection;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;

import org.apache.commons.lang.NotImplementedException;

import com.antsdb.saltedfish.cpp.BluntHeap;
import com.antsdb.saltedfish.cpp.FishObject;
import com.antsdb.saltedfish.cpp.KeyBytes;
import com.antsdb.saltedfish.cpp.Unsafe;
import com.antsdb.saltedfish.cpp.Value;
import com.antsdb.saltedfish.util.BytesUtil;
import com.antsdb.saltedfish.util.CodingError;
import com.antsdb.saltedfish.util.UberObject;

/**
 * Row is a read-only view of the raw data stored in the file
 * 
 * data structure of a raw row
 * 
 * 0: byte, version
 * 1: byte, tomb stone indicator
 * 2-3: reserved
 * 4: int8, transaction time stamp/transaction id
 * 12: int2, number of columns
 * 16: key offset
 * 20: field offset vector
 * @author xinyi
 *
 */
public class Row extends UberObject implements Map<Integer, Object> {

    protected static final int OFFSET_FILE_VERSION = 0;
    protected static final int OFFSET_LENGTH = 1;
    protected static final int OFFSET_TABLE_ID = 4;
    protected static final int OFFSET_MAX_COLUMN_ID = 0x8;
    protected static final int OFFSET_TRX_TS = 0xa;
    protected static final int OFFSET_KEY_OFFSET = 0x12;
    protected static final int OFFSET_VALUES_OFFSETS = 0x16;
    protected static final Row TOMB_STONE = new Row(Unsafe.allocateMemory(100));

    protected static final byte FILE_VERSION = Value.FORMAT_ROW;

    transient int tableId;

    long addr;
    int maxColumnid;
    long version;

    Row(long addr, long version) {
        if (Unsafe.getByte(addr + OFFSET_FILE_VERSION) != FILE_VERSION) {
            throw new IllegalArgumentException();
        }
        this.version = version;
        this.addr = addr;
        this.maxColumnid = getMaxColumnId();
    }

    protected Row(long addr) {
        this.addr = addr;
        this.version = 0;
        this.maxColumnid = -1;
    }

    public boolean isTombStone() {
        return this == TOMB_STONE;
    }

    final static long from(long p, VaporizingRow from, int tableId) {
        int size = from.getSize();
        long pRow = p;
        long pData = pRow + getHeaderSize(from.getMaxColumnId());
        Unsafe.putByte(pRow, FILE_VERSION);
        Unsafe.putInt3(pRow + OFFSET_LENGTH, size);
        Unsafe.putInt(pRow + OFFSET_TABLE_ID, tableId);
        Unsafe.putLong(pRow + OFFSET_TRX_TS, from.getTrxTimestamp());
        Unsafe.putShort(pRow + OFFSET_MAX_COLUMN_ID, (short) from.getMaxColumnId());
        Unsafe.putInt(pRow + OFFSET_KEY_OFFSET, (int) (pData - pRow));
        pData = copyValue(from.getKeyAddress(), pData);
        for (int i = 0; i <= from.getMaxColumnId(); i++) {
            long pValue = from.getFieldAddress(i);
            if (pValue == 0) {
                Unsafe.putInt(pRow + OFFSET_VALUES_OFFSETS + i * 4, 0);
                continue;
            }
            int offset = (int) (pData - pRow);
            Unsafe.putInt(pRow + OFFSET_VALUES_OFFSETS + i * 4, offset);
            pData = copyValue(pValue, pData);
        }
        if ((pData - pRow) != size) {
            throw new CodingError();
        }
        return pRow;
    }

    final static long from(SpaceManager spaceman, VaporizingRow from) {
        int size = from.getSize();
        long spRow = spaceman.alloc(size);
        long pRow = spaceman.toMemory(spRow);
        long pData = pRow + getHeaderSize(from.getMaxColumnId());
        Unsafe.putByte(pRow, FILE_VERSION);
        Unsafe.putInt3(pRow + OFFSET_LENGTH, size);
        Unsafe.putLong(pRow + OFFSET_TRX_TS, from.getTrxTimestamp());
        Unsafe.putShort(pRow + OFFSET_MAX_COLUMN_ID, (short) from.getMaxColumnId());
        Unsafe.putInt(pRow + OFFSET_KEY_OFFSET, (int) (pData - pRow));
        pData = copyValue(from.getKeyAddress(), pData);
        for (int i = 0; i <= from.getMaxColumnId(); i++) {
            long pValue = from.getFieldAddress(i);
            if (pValue == 0) {
                Unsafe.putInt(pRow + OFFSET_VALUES_OFFSETS + i * 4, 0);
                continue;
            }
            int offset = (int) (pData - pRow);
            Unsafe.putInt(pRow + OFFSET_VALUES_OFFSETS + i * 4, offset);
            pData = copyValue(pValue, pData);
        }
        if ((pData - pRow) != size) {
            throw new CodingError();
        }
        return spRow;
    }

    private static long copyValue(long pValue, long pData) {
        if (pValue == 0) {
            return pData;
        }
        int size = FishObject.getSize(pValue);
        Unsafe.copyMemory(pValue, pData, size);
        return pData + size;
    }

    /**
      * 
      * @param maxSequence maximum possible value of the column id.
      */
    public static Row fromMemoryPointer(long pRow, long version) {
        if (pRow == 0) {
            return null;
        }
        if (pRow == 1) {
            return TOMB_STONE;
        }
        Row row = new Row(pRow, version);
        return row;
    }

    public static Row fromSpacePointer(SpaceManager memman, long spRow, long version) {
        if (spRow == 0) {
            return null;
        }
        if (spRow == 1) {
            return TOMB_STONE;
        }
        long pRow = memman.toMemory(spRow);
        return fromMemoryPointer(pRow, version);
    }

    public int getKeyOffset() {
        int offset = Unsafe.getIntVolatile(this.addr + OFFSET_KEY_OFFSET);
        return offset;
    }

    public long getKeyAddress() {
        int offset = getKeyOffset() & 0xffff;
        return this.addr + offset;
    }

    public byte[] getKey() {
        int offset = Unsafe.getInt(this.addr + OFFSET_KEY_OFFSET);
        if (offset == 0) {
            return null;
        }
        if (offset >= 1000) {
            throw new IllegalArgumentException();
        }
        byte[] bytes = KeyBytes.create(this.addr + offset).get();
        return bytes;
    }

    public Object get(int field) {
        long addr = getFieldAddress(field);
        Object val = FishObject.get(null, addr);
        return val;
    }

    @Override
    public int size() {
        int count = 0;
        for (int i = 0; i <= this.maxColumnid; i++) {
            long addr = getFieldAddress(i);
            if (addr != 0) {
                count++;
            }
        }
        return count;
    }

    @Override
    public boolean isEmpty() {
        return size() == 0;
    }

    @Override
    public boolean containsKey(Object key) {
        return keySet().contains(key);
    }

    @Override
    public boolean containsValue(Object value) {
        return values().contains(value);
    }

    @Override
    public Object get(Object key) {
        return get((int) key);
    }

    @Override
    public Object put(Integer key, Object value) {
        throw new NotImplementedException();
    }

    @Override
    public Object remove(Object key) {
        throw new NotImplementedException();
    }

    @Override
    public void putAll(Map<? extends Integer, ? extends Object> m) {
        throw new NotImplementedException();
    }

    @Override
    public void clear() {
        throw new NotImplementedException();
    }

    @Override
    public Set<Integer> keySet() {
        Set<Integer> set = new LinkedHashSet<>();
        int maxColumnId = getMaxColumnId();
        for (int i = 0; i < maxColumnId; i++) {
            if (getFieldAddress(i) != 0) {
                set.add(i);
            }
        }
        return set;
    }

    @Override
    public Collection<Object> values() {
        Set<Object> set = new LinkedHashSet<>();
        int maxColumnId = getMaxColumnId();
        for (int i = 0; i < maxColumnId; i++) {
            Object value = get(i);
            if (value != null) {
                set.add(value);
            }
        }
        return set;
    }

    private static class MyEntry implements Map.Entry<Integer, Object> {
        Integer key;
        Object value;

        public MyEntry(Integer key, Object value) {
            this.key = key;
            this.value = value;
        }

        @Override
        public Integer getKey() {
            return this.key;
        }

        @Override
        public Object getValue() {
            return this.value;
        }

        @Override
        public Object setValue(Object value) {
            throw new NotImplementedException();
        }
    }

    @Override
    public Set<Map.Entry<Integer, Object>> entrySet() {
        Set<Map.Entry<Integer, Object>> set = new LinkedHashSet<>();
        int maxColumnId = getMaxColumnId();
        for (int i = 0; i < maxColumnId; i++) {
            Object value = get(i);
            if (value != null) {
                MyEntry entry = new MyEntry(i, value);
                set.add(entry);
            }
        }

        return set;
    }

    /**
     * transaction time stamp is a unique incremental number used to version a record
     * 
     * @return
     */
    public long getTrxTimestamp() {
        return this.version;
    }

    public void setVersion(long version) {
        this.version = version;
    }

    /**
     * calculate header size
     * 
     * @param maxColumnId maximum possible column id
     * @return number of bytes
     */
    static int getHeaderSize(int maxColumnId) {
        int size = OFFSET_VALUES_OFFSETS + (maxColumnId + 1) * 4;
        return size;
    }

    public int getMaxColumnId() {
        int value = Unsafe.getUnsignedShort(this.addr + OFFSET_MAX_COLUMN_ID);
        return value;
    }

    public final static boolean isTombStone(long pRow) {
        return pRow == 1;
    }

    public long getFieldAddress(int n) {
        if ((n < -1) || (n > getMaxColumnId())) {
            return 0;
        }
        int offset = Unsafe.getInt(this.addr + OFFSET_VALUES_OFFSETS + n * 4);
        if (offset == 0) {
            return 0;
        }
        return this.addr + offset;
    }

    @Override
    public String toString() {
        StringBuilder buf = new StringBuilder();
        buf.append("version:");
        buf.append((int) Unsafe.getByte(this.addr));
        buf.append('\n');
        buf.append("max column id:");
        buf.append(getMaxColumnId());
        buf.append('\n');
        buf.append("trx timestamp:");
        buf.append(getTrxTimestamp());
        buf.append('\n');
        buf.append("key:");
        buf.append(BytesUtil.toHex8(getKey()));
        buf.append('\n');
        for (int i = 0; i <= getMaxColumnId(); i++) {
            Object value = get(i);
            if (value != null) {
                buf.append(i);
                buf.append(":");
                if (value instanceof byte[]) {
                    buf.append(BytesUtil.toHex((byte[]) value));
                } else {
                    buf.append(value);
                }
                buf.append("\n");
            }
        }
        return buf.toString();
    }

    public long getAddress() {
        return this.addr;
    }

    public int getTableId() {
        return Row.getTableId(this.addr);
    }

    public static long getVersion(long pRow) {
        long result = Unsafe.getLong(pRow + OFFSET_TRX_TS);
        return result;
    }

    public static void setVersion(long pRow, long version) {
        Unsafe.putLong(pRow + OFFSET_TRX_TS, version);
    }

    public static int getTableId(long pRow) {
        return Unsafe.getInt(pRow + OFFSET_TABLE_ID);
    }

    public static long getKeyAddress(long pRow) {
        int offset = Unsafe.getInt(pRow + OFFSET_KEY_OFFSET);
        return pRow + offset;
    }

    /**
     * get the number of bytes used by the row including header
     */
    public int getLength() {
        int size = Unsafe.getInt3(this.addr + OFFSET_LENGTH);
        return size;
    }

    public long getVersion() {
        return this.version;
    }

    /**
     * clone the row in the target heap
     * @param heap
     * @return offset in the specified heap
     */
    public int clone(BluntHeap heap) {
        int size = getLength();
        int offset = heap.allocOffset(size());
        long addr = heap.getAddress(offset);
        Unsafe.copyMemory(getAddress(), addr, size);
        Row.setVersion(addr, getVersion());
        return offset;
    }
}