/* * Copyright 2014-2017 Real Logic Ltd. * * 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 io.aeron.logbuffer; import org.agrona.*; import org.agrona.concurrent.UnsafeBuffer; import org.agrona.concurrent.status.Position; import static io.aeron.logbuffer.FrameDescriptor.*; import static io.aeron.protocol.DataHeaderFlyweight.HEADER_LENGTH; /** * A term buffer reader. * * <b>Note:</b> Reading from the term is thread safe, but each thread needs its own instance of this class. */ public class TermReader { /** * Reads data from a term in a log buffer and updates a passed {@link Position} so progress is not lost in the * event of an exception. * * If a fragmentsLimit of 0 or less is passed then at least one read will be attempted. * * @param termBuffer to be read for fragments. * @param termOffset within the buffer that the read should begin. * @param handler the handler for data that has been read * @param fragmentsLimit limit the number of fragments read. * @param header to be used for mapping over the header for a given fragment. * @param errorHandler to be notified if an error occurs during the callback. * @param currentPosition prior to reading further fragments * @param subscriberPosition to be updated after reading with new position * @return the number of fragments read */ public static int read( final UnsafeBuffer termBuffer, final int termOffset, final FragmentHandler handler, final int fragmentsLimit, final Header header, final ErrorHandler errorHandler, final long currentPosition, final Position subscriberPosition) { int fragmentsRead = 0; int offset = termOffset; final int capacity = termBuffer.capacity(); header.buffer(termBuffer); try { do { final int frameLength = frameLengthVolatile(termBuffer, offset); if (frameLength <= 0) { break; } final int frameOffset = offset; offset += BitUtil.align(frameLength, FRAME_ALIGNMENT); if (!isPaddingFrame(termBuffer, frameOffset)) { header.offset(frameOffset); handler.onFragment(termBuffer, frameOffset + HEADER_LENGTH, frameLength - HEADER_LENGTH, header); ++fragmentsRead; } } while (fragmentsRead < fragmentsLimit && offset < capacity); } catch (final Throwable t) { errorHandler.onError(t); } finally { final long newPosition = currentPosition + (offset - termOffset); if (newPosition > currentPosition) { subscriberPosition.setOrdered(newPosition); } } return fragmentsRead; } /** * Reads data from a term in a log buffer. * * If a fragmentsLimit of 0 or less is passed then at least one read will be attempted. * Note: this method has users outside of Aeron * * @param termBuffer to be read for fragments. * @param termOffset within the buffer that the read should begin. * @param handler the handler for data that has been read * @param fragmentsLimit limit the number of fragments read. * @param header to be used for mapping over the header for a given fragment. * @param errorHandler to be notified if an error occurs during the callback. * @return the number of fragments read */ public static long read( final UnsafeBuffer termBuffer, final int termOffset, final FragmentHandler handler, final int fragmentsLimit, final Header header, final ErrorHandler errorHandler) { int fragmentsRead = 0; int offset = termOffset; final int capacity = termBuffer.capacity(); try { do { final int frameLength = frameLengthVolatile(termBuffer, offset); if (frameLength <= 0) { break; } final int frameOffset = offset; offset += BitUtil.align(frameLength, FRAME_ALIGNMENT); if (!isPaddingFrame(termBuffer, frameOffset)) { header.buffer(termBuffer); header.offset(frameOffset); handler.onFragment(termBuffer, frameOffset + HEADER_LENGTH, frameLength - HEADER_LENGTH, header); ++fragmentsRead; } } while (fragmentsRead < fragmentsLimit && offset < capacity); } catch (final Throwable t) { errorHandler.onError(t); } return pack(offset, fragmentsRead); } /** * Pack the values for fragmentsRead and offset into a long for returning on the stack. * * @param offset value to be packed. * @param fragmentsRead value to be packed. * @return a long with both ints packed into it. */ public static long pack(final int offset, final int fragmentsRead) { return ((long)offset << 32) | fragmentsRead; } /** * The number of fragments that have been read. * * @param readOutcome into which the fragments read value has been packed. * @return the number of fragments that have been read. */ public static int fragmentsRead(final long readOutcome) { return (int)readOutcome; } /** * The offset up to which the term has progressed. * * @param readOutcome into which the offset value has been packed. * @return the offset up to which the term has progressed. */ public static int offset(final long readOutcome) { return (int)(readOutcome >>> 32); } }