/* * Copyright 2013 Jive Software, Inc * * Licensed 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 com.jivesoftware.os.amza.service.storage.binary; import com.jivesoftware.os.amza.api.filer.UIO; import com.jivesoftware.os.amza.api.stream.FpKeyValueStream; import com.jivesoftware.os.amza.api.stream.RowType; import com.jivesoftware.os.amza.api.stream.TxKeyValueStream; import com.jivesoftware.os.amza.api.stream.UnprefixedTxKeyValueStream; import com.jivesoftware.os.amza.api.wal.PrimaryRowMarshaller; import com.jivesoftware.os.amza.api.wal.WALKey; import java.io.IOException; import org.xerial.snappy.Snappy; public class BinaryPrimaryRowMarshaller implements PrimaryRowMarshaller { @Override public byte[] toRow(RowType rowType, byte[] pk, byte[] value, long timestamp, boolean tombstoned, long version) throws Exception { return toRowBytes(compress(rowType, pk), compress(rowType, value), timestamp, tombstoned, version); } private byte[] toRowBytes(byte[] pk, byte[] value, long timestamp, boolean tombstoned, long version) throws IOException { byte[] bytes = new byte[8 + 1 + 8 + 4 + (value != null ? value.length : 0) + 4 + pk.length]; int o = 0; UIO.longBytes(timestamp, bytes, o); o += 8; bytes[o] = tombstoned ? (byte) 1 : (byte) 0; // tombstone o++; UIO.longBytes(version, bytes, o); o += 8; UIO.intBytes(value == null ? -1 : value.length, bytes, o); o += 4; if (value != null) { UIO.writeBytes(value, bytes, o); o += value.length; } UIO.intBytes(pk.length, bytes, o); o += 4; UIO.writeBytes(pk, bytes, o); return bytes; } @Override public byte[] convert(RowType fromType, byte[] row, RowType toType) throws Exception { if (fromType == toType) { return row; } int o = 0; long timestamp = UIO.bytesLong(row, o); o += 8; boolean tombstone = row[o] == 1; o++; long version = UIO.bytesLong(row, o); o += 8; int valueLength = UIO.bytesInt(row, o); o += 4; byte[] value = null; if (valueLength >= 0) { value = new byte[valueLength]; UIO.readBytes(row, o, value); o += valueLength; } int pkLength = UIO.bytesInt(row, o); o += 4; byte[] pk = new byte[pkLength]; UIO.readBytes(row, o, pk); return toRow(toType, uncompress(fromType, pk), uncompress(fromType, value), timestamp, tombstone, version); } @Override public int maximumSizeInBytes(RowType rowType, int pkSizeInBytes, int valueSizeInBytes) { if (rowType == RowType.snappy_primary) { return 8 + 1 + 8 + 4 + Snappy.maxCompressedLength(valueSizeInBytes) + 4 + Snappy.maxCompressedLength(pkSizeInBytes); } else { return 8 + 1 + 8 + 4 + valueSizeInBytes + 4 + pkSizeInBytes; } } @Override public boolean fromRows(FpRows fpRows, FpKeyValueStream fpKeyValueStream) throws Exception { byte[] intLongBuffer = new byte[8]; return WALKey.decompose( stream -> fpRows.consume((fp, rowType, row) -> { int o = 0; long timestamp = UIO.bytesLong(row, o); o += 8; boolean tombstone = row[o] == 1; o++; long version = UIO.bytesLong(row, o); o += 8; int valueLength = UIO.bytesInt(row, o); o += 4; byte[] value = null; if (valueLength >= 0) { value = new byte[valueLength]; UIO.readBytes(row, o, value); o += valueLength; } int pkLength = UIO.bytesInt(row, o); o += 4; byte[] pk = new byte[pkLength]; UIO.readBytes(row, o, pk); return stream.stream(-1, fp, rowType, uncompress(rowType, pk), true, uncompress(rowType, value), timestamp, tombstone, version, row); }), (txId, fp, rowType, prefix, key, hasValue, value, valueTimestamp, valueTombstoned, valueVersion, row) -> fpKeyValueStream.stream(fp, rowType, prefix, key, value, valueTimestamp, valueTombstoned, valueVersion)); } @Override public boolean fromRows(TxFpRows txFpRows, TxKeyValueStream txKeyValueStream) throws Exception { byte[] intLongBuffer = new byte[8]; return WALKey.decompose( stream -> txFpRows.consume((txId, fp, rowType, row) -> { int o = 0; long timestamp = UIO.bytesLong(row, o); o += 8; boolean tombstone = row[o] == 1; o++; long version = UIO.bytesLong(row, o); o += 8; int valueLength = UIO.bytesInt(row, o); o += 4; byte[] value = null; if (valueLength >= 0) { value = new byte[valueLength]; UIO.readBytes(row, o, value); o += valueLength; } int pkLength = UIO.bytesInt(row, o); o += 4; byte[] pk = new byte[pkLength]; UIO.readBytes(row, o, pk); return stream.stream(txId, fp, rowType, uncompress(rowType, pk), true, uncompress(rowType, value), timestamp, tombstone, version, row); }), (txId, fp, rowType, prefix, key, hasValue, value, valueTimestamp, valueTombstoned, valueVersion, row) -> txKeyValueStream.stream(txId, prefix, key, value, valueTimestamp, valueTombstoned, valueVersion).wantsMore()); } @Override public boolean fromRows(TxFpRows txFpRows, UnprefixedTxKeyValueStream txKeyValueStream) throws Exception { byte[] intLongBuffer = new byte[8]; return WALKey.decompose( stream -> txFpRows.consume((txId, fp, rowType, row) -> { int o = 0; long timestamp = UIO.bytesLong(row, o); o += 8; boolean tombstone = row[o] == 1; o++; long version = UIO.bytesLong(row, o); o += 8; int valueLength = UIO.bytesInt(row, o); o += 4; byte[] value = null; if (valueLength >= 0) { value = new byte[valueLength]; UIO.readBytes(row, o, value); o += valueLength; } int pkLength = UIO.bytesInt(row, o); o += 4; byte[] pk = new byte[pkLength]; UIO.readBytes(row, o, pk); return stream.stream(txId, fp, rowType, uncompress(rowType, pk), true, uncompress(rowType, value), timestamp, tombstone, version, row); }), (txId, fp, rowType, prefix, key, hasValue, value, valueTimestamp, valueTombstoned, valueVersion, row) -> txKeyValueStream.row(txId, key, value, valueTimestamp, valueTombstoned, valueVersion)); } @Override public boolean fromRows(TxFpRows txFpRows, WALKey.TxFpKeyValueEntryStream<byte[]> txFpKeyValueStream) throws Exception { byte[] intLongBuffer = new byte[8]; return WALKey.decompose( txFpRawKeyValueEntryStream -> txFpRows.consume((txId, fp, rowType, row) -> { int o = 0; long timestamp = UIO.bytesLong(row, o); o += 8; boolean tombstone = row[o] == 1; o++; long version = UIO.bytesLong(row, o); o += 8; int valueLength = UIO.bytesInt(row, o); o += 4; byte[] value = null; if (valueLength >= 0) { value = new byte[valueLength]; UIO.readBytes(row, o, value); o += valueLength; } int pkLength = UIO.bytesInt(row, o); o += 4; byte[] pk = new byte[pkLength]; UIO.readBytes(row, o, pk); return txFpRawKeyValueEntryStream.stream(txId, fp, rowType, uncompress(rowType, pk), true, uncompress(rowType, value), timestamp, tombstone, version, row); }), txFpKeyValueStream); } @Override public byte[] valueFromRow(RowType rowType, byte[] row, int offset) throws Exception { int o = offset + 8 + 1 + 8; int valueLength = UIO.bytesInt(row, o); o += 4; byte[] value = null; if (valueLength >= 0) { value = new byte[valueLength]; UIO.readBytes(row, o, value); } return uncompress(rowType, value); } @Override public long timestampFromRow(byte[] row, int offset) throws Exception { return UIO.bytesLong(row, offset); } @Override public boolean tombstonedFromRow(byte[] row, int offset) throws Exception { return row[offset + 8] == 1; } @Override public long versionFromRow(byte[] row, int offset) throws Exception { return UIO.bytesLong(row, offset + 8 + 1); } private byte[] compress(RowType rowType, byte[] bytes) throws IOException { if (rowType == RowType.primary) { return bytes; } else if (rowType == RowType.snappy_primary) { if (bytes != null) { return Snappy.compress(bytes); } return null; } else { throw new IllegalArgumentException("compressing rowType:" + rowType + " is not supported."); } } private byte[] uncompress(RowType rowType, byte[] bytes) throws IOException { if (rowType == RowType.primary) { return bytes; } else if (rowType == RowType.snappy_primary) { if (bytes != null) { return Snappy.uncompress(bytes); } return null; } else { throw new IllegalArgumentException("uncompressing rowType:" + rowType + " is not supported."); } } }