package com.linkedin.databus.core.util; /* * * Copyright 2013 LinkedIn Corp. All rights reserved * * 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. * */ import java.nio.ByteBuffer; import org.apache.log4j.Logger; /** * "Addresses" used in EventBuffer are encoded with 3 elements: * <pre> * 1. GenId : The rotation level on which this address is relevant * 2. Index : The buffer in the List of ByteBuffers where the address is present * 3. Offset : Offset into the buffer. * * 63 n m 0 * ----------------------------------------------------------------------------- * | | | | * | GenId | Index | Offset | * | | | | * ----------------------------------------------------------------------------- * </pre> * * In the above diagram, * m bits are assigned to Offset, which implies the size of individual buffer <= 2^m; * n-m bits are assigned to Index, implying the number of buffers <= 2 ^(n-m); * and the rest of the bits (except the sign bit) are for GenId. * * More info on GenId: * Each time the event buffer wraps around, we increment the global genId. So, * by keeping the genId encoded in the most significant bit positions, we can get * the natural temporal ordering of events in the address-space. * * <pre> * ================ Important NOTE ======================= * * BufferPosition values are not resilient to event buffer restarts. So DO NOT use * the raw values directly in checkpointing and other places that are expected to have * a longer lifetime than one run of the relay or client containing the event buffer. * * ========================================================= * </pre> */ public class BufferPosition { public static final String MODULE = BufferPosition.class.getName(); public static final Logger LOG = Logger.getLogger(MODULE); private volatile long _address; private final BufferPositionParser _parser; private final ByteBuffer[] _buffers; /** * @return true if the position is negative */ public boolean init() { return _parser.init(_address); } /** * Assigns this position from another position. * * @param rhs the position from which to copy */ public void copy (BufferPosition rhs ) { _address = rhs.getPosition(); } /** * Removes the genId and returns the address part (index + offset). * * @return the address (with the genId stripped) */ public long getRealPosition() { return _parser.address(_address); } /** * @return the index encoded in the contained position */ public int bufferIndex() { return _parser.bufferIndex(_address); } /** * @return the offset encoded in the contained position */ public int bufferOffset() { return _parser.bufferOffset(_address); } /** * @return the genId encoded in the contained position */ public long bufferGenId() { return 0 <= _address ? _parser.bufferGenId(_address) : 0; } /** * Sets the position (including the genId). * * @param the position to be set */ public void setPosition(long address) { _address = address; } /** * Sets the position with a given genId, buffer index, and buffer offset. */ public void setPosition(long genId, int index, int offset) { _address = _parser.encode(genId, index, offset); } /** * @return the position in the eventBuffer, including the genId */ public long getPosition() { return _address; } /** * @param parser BufferPositionParser * @param buffers list of ByteBuffers in the event buffer */ public BufferPosition(BufferPositionParser parser, ByteBuffer[] buffers) { _parser = parser; _buffers = buffers; } /** * @param pos genId position * @param parser BufferPositionParser * @param buffers list of ByteBuffers in the event buffer */ public BufferPosition(long pos, BufferPositionParser parser, ByteBuffer[] buffers) { _parser = parser; _buffers = buffers; setPosition(pos); } @Override public String toString() { return _parser.toString(_address, _buffers); } /** * Increments the genId in the stored address by "increment". * * @return the position with the incremented genId */ public long incrementGenId() { _address = _parser.incrementGenId(_address); return _address; } /** * Moves the position to the beginning of the next ByteBuffer. * If we reach the end of the event buffer, the genId will be incremented by * one and the index and offset reset to zero. * * @return the position with the incremented index */ public long incrementIndex() { _address = _parser.incrementIndex(_address, _buffers); return _address; } /** * Increments the offset by "increment". * If we reach the end of the current bytebuffer, the index will be * incremented and the offset reset to zero. * * @param the increment value * @return the position with the incremented offset */ public long incrementOffset(int increment) { _address = _parser.incrementOffset(_address, increment, _buffers); return _address; } /** * Checks if the stored address refers to valid position in the buffer (<= limit). * If the address is at the limit of one buffer, the index gets incremented. * * @return position pointing to a valid location */ public long sanitize() { _address = _parser.sanitize(_address, _buffers); return _address; } /** * Checks if the stored address refers to valid position in the buffer (<= limit). * * If okToRegress and position >= limit, increments index. * If !okToRegress and position > limit, throws runtime exception. * * @return the position pointing to a valid location */ public long sanitize(boolean okToRegress) { _address = _parser.sanitize(_address, _buffers, okToRegress); return _address; } @Override public int hashCode() { return (int)_address; } @Override public boolean equals(Object obj) { return (obj instanceof BufferPosition) && (_address == ((BufferPosition)obj).getPosition()); } /** * Compares for equality between two positions. * * @param the position to check for equality * @return true if the underlying position is equal else false */ public boolean equals(BufferPosition position) { return (_address == position.getPosition()); } /** * Compares for equality between two positions, ignoring genId. * * @param position to check for equality * @return true if the underlying position (without genId) is equal else false */ public boolean equalsIgnoreGenId(BufferPosition position) { return (getRealPosition() == position.getRealPosition()); } /** * Ensures the position points to the start of some data in the ByteBuffers or reaches the end of * the data. */ public void skipOverFreeSpace() { ByteBuffer curBuf = _buffers[bufferIndex()]; for(int iterNum = 0;iterNum < _buffers.length && bufferOffset() >= curBuf.limit(); ++iterNum) { incrementIndex(); curBuf = _buffers[bufferIndex()]; } } }