| /* |
| * 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]; |
| } |
| } |