/**
* 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 com.facebook.infrastructure.db;
import java.io.*;
import java.nio.ByteBuffer;
import java.util.*;
import com.facebook.infrastructure.io.*;
import org.apache.commons.lang.ArrayUtils;
/**
* Author : Avinash Lakshman ( alakshman@facebook.com) & Prashant Malik ( pmalik@facebook.com )
*/
class CommitLogHeader
{
private static ICompactSerializer<CommitLogHeader> serializer_;
static
{
serializer_ = new CommitLogHeaderSerializer();
}
static ICompactSerializer<CommitLogHeader> serializer()
{
return serializer_;
}
static int size(int columnFamilyCount)
{
/*
* We serialize the CommitLogHeader as a byte[] and write it
* to disk. So we first write an "int" to specify the length
* of the byte[] which is why we first have a 4 in the sum.
* We then have size which is the number of bits to track who
* has been flushed and then the rest is the position[]
* size = #of column families
* +
* size of the bitset
* +
* size of position array
*/
return 4 + columnFamilyCount + (4 * columnFamilyCount);
}
static int getLowestPosition(CommitLogHeader clHeader)
{
int[] positions = clHeader.getPositions();
int minPosition = Integer.MAX_VALUE;
for ( int position : positions )
{
if ( position < minPosition && position > 0)
{
minPosition = position;
}
}
if(minPosition == Integer.MAX_VALUE)
minPosition = 0;
return minPosition;
}
/*
* Bitwise & of each byte in the two arrays.
* Both arrays are of same length. In order
* to be memory efficient the result is in
* the third parameter.
*/
static byte[] and(byte[] bytes, byte[] bytes2) throws IOException
{
DataInputBuffer bufIn = new DataInputBuffer();
bufIn.reset(bytes, 0, bytes.length);
CommitLogHeader clHeader = CommitLogHeader.serializer().deserialize(bufIn);
byte[] clh = clHeader.getBitSet();
bufIn.reset(bytes2, 0, bytes2.length);
CommitLogHeader clHeader2 = CommitLogHeader.serializer().deserialize(bufIn);
byte[] clh2 = clHeader2.getBitSet();
byte[] result = new byte[clh.length];
for ( int i = 0; i < clh.length; ++i )
{
result[i] = (byte)(clh[i] & clh2[i]);
}
return result;
}
static boolean isZero(byte[] bytes)
{
for ( byte b : bytes )
{
if ( b == 1 )
return false;
}
return true;
}
private byte[] header_ = ArrayUtils.EMPTY_BYTE_ARRAY;
private int[] position_ = new int[0];
CommitLogHeader(int size)
{
header_ = new byte[size];
position_ = new int[size];
}
/*
* This ctor is used while deserializing. This ctor
* also builds an index of position to column family
* Id.
*/
CommitLogHeader(byte[] header, int[] position)
{
header_ = header;
position_ = position;
}
CommitLogHeader(CommitLogHeader clHeader)
{
header_ = new byte[clHeader.header_.length];
System.arraycopy(clHeader.header_, 0, header_, 0, header_.length);
position_ = new int[clHeader.position_.length];
System.arraycopy(clHeader.position_, 0, position_, 0, position_.length);
}
byte get(int index)
{
return header_[index];
}
int getPosition(int index)
{
return position_[index];
}
void turnOn(int index, long position)
{
turnOn(header_, index, position);
}
void turnOn(byte[] bytes, int index, long position)
{
bytes[index] = (byte)1;
position_[index] = (int)position;
}
void turnOff(int index)
{
turnOff(header_, index);
}
void turnOff(byte[] bytes, int index)
{
bytes[index] = (byte)0;
position_[index] = 0;
}
boolean isSafeToDelete() throws IOException
{
return isSafeToDelete(header_);
}
boolean isSafeToDelete(byte[] bytes) throws IOException
{
for ( byte b : bytes )
{
if ( b == 1 )
return false;
}
return true;
}
byte[] getBitSet()
{
return header_;
}
int[] getPositions()
{
return position_;
}
void zeroPositions()
{
int size = position_.length;
position_ = new int[size];
}
void and (CommitLogHeader commitLogHeader)
{
byte[] clh2 = commitLogHeader.header_;
for ( int i = 0; i < header_.length; ++i )
{
header_[i] = (byte)(header_[i] & clh2[i]);
}
}
byte[] toByteArray() throws IOException
{
ByteArrayOutputStream bos = new ByteArrayOutputStream();
DataOutputStream dos = new DataOutputStream(bos);
CommitLogHeader.serializer().serialize(this, dos);
return bos.toByteArray();
}
public String toString()
{
StringBuilder sb = new StringBuilder("");
for ( byte b : header_ )
{
sb.append(b);
sb.append(" ");
}
sb.append(" | " );
for ( int position : position_ )
{
sb.append(position);
sb.append(" ");
}
return sb.toString();
}
}
class CommitLogHeaderSerializer implements ICompactSerializer<CommitLogHeader>
{
public void serialize(CommitLogHeader clHeader, DataOutputStream dos) throws IOException
{
dos.writeInt(clHeader.getBitSet().length);
dos.write(clHeader.getBitSet());
int[] positions = clHeader.getPositions();
for ( int position : positions )
{
dos.writeInt(position);
}
}
public CommitLogHeader deserialize(DataInputStream dis) throws IOException
{
int size = dis.readInt();
byte[] bitFlags = new byte[size];
dis.readFully(bitFlags);
int[] position = new int[size];
for ( int i = 0; i < size; ++i )
{
position[i] = dis.readInt();
}
return new CommitLogHeader(bitFlags, position);
}
}