blob: 8eba8e321d1144cc0076133e200d8a751d20cf71 [file] [log] [blame]
/*
* 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 org.apache.cassandra.io.util;
import java.io.IOException;
public final class PageAware
{
public static final int PAGE_SIZE = 4096; // must be a power of two
public static final int PAGE_SIZE_SHIFT = Integer.numberOfTrailingZeros(PAGE_SIZE);
/**
* Calculate the end of the page identified by the given position.
* Equivalent to floor(dstPos / PAGE_SIZE + 1) * PAGE_SIZE.
* <p>
* When the argument is equal to the page boundary, returns the next page boundary. E.g. pageLimit(0) == PAGE_SIZE.
*/
public static long pageLimit(long dstPos)
{
return (dstPos | (PAGE_SIZE - 1)) + 1;
}
/**
* Calculate the start of the page that contains the given position.
* Equivalent to floor(dstPos / PAGE_SIZE) * PAGE_SIZE.
*/
public static long pageStart(long dstPos)
{
return dstPos & -PAGE_SIZE;
}
/**
* Calculate the earliest page boundary for the given position.
* Equivalent to ceil(dstPos / PAGE_SIZE) * PAGE_SIZE.
* <p>
* When the argument is equal to a page boundary, returns the argument.
*/
public static long padded(long dstPos)
{
return pageStart(dstPos + PAGE_SIZE - 1);
}
/**
* Calculate the number of bytes left in this page.
* Equivalent to pageLimit(position) - position.
* <p>
* When the argument is equal to a page boundary, returns PAGE_SIZE.
*/
public static int bytesLeftInPage(long dstPos)
{
return PAGE_SIZE - (int) (dstPos & (PAGE_SIZE - 1));
}
/**
* Calculate the number of pages that fit in the given size, rounded up to a page if the size is not an exact multiple.
*
* @param size the size that needs to cover a number of pages
* @return the number of pages, rounded up
*/
public static int numPages(int size)
{
return (size + PAGE_SIZE - 1) >> PAGE_SIZE_SHIFT;
}
/**
* Given a position relative to the start of a number of pages, determine the exact page number this
* position falls into. For example, positions from 0 to {@link #PAGE_SIZE} -1 will fall into page zero
* and so forth.
*
* @param dstPos the position
* @return the page number, indexed at zero
*/
public static int pageNum(long dstPos)
{
return Math.toIntExact(dstPos >> PAGE_SIZE_SHIFT);
}
/**
* Pad the given output stream with zeroes until the next page boundary.
* If the destination position is already at a page boundary, do not do anything.
*/
public static void pad(DataOutputPlus dest) throws IOException
{
long position = dest.position();
long bytesLeft = padded(position) - position;
// bytesLeft is provably within [0, pageSize - 1]
dest.write(EmptyPage.EMPTY_PAGE, 0, (int) bytesLeft);
}
static class EmptyPage
{
static final byte[] EMPTY_PAGE = new byte[PAGE_SIZE];
}
}