| /** |
| * 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.hadoop.io.erasurecode.rawcoder; |
| |
| import org.apache.hadoop.HadoopIllegalArgumentException; |
| import org.apache.hadoop.classification.InterfaceAudience; |
| import org.apache.hadoop.io.erasurecode.ECChunk; |
| |
| import java.nio.ByteBuffer; |
| import java.util.Arrays; |
| |
| /** |
| * Helpful utilities for implementing some raw erasure coders. |
| */ |
| @InterfaceAudience.Private |
| final class CoderUtil { |
| |
| private CoderUtil() { |
| // No called |
| } |
| |
| private static byte[] emptyChunk = new byte[4096]; |
| |
| /** |
| * Make sure to return an empty chunk buffer for the desired length. |
| * @param leastLength |
| * @return empty chunk of zero bytes |
| */ |
| static byte[] getEmptyChunk(int leastLength) { |
| if (emptyChunk.length >= leastLength) { |
| return emptyChunk; // In most time |
| } |
| |
| synchronized (CoderUtil.class) { |
| emptyChunk = new byte[leastLength]; |
| } |
| |
| return emptyChunk; |
| } |
| |
| /** |
| * Ensure a buffer filled with ZERO bytes from current readable/writable |
| * position. |
| * @param buffer a buffer ready to read / write certain size bytes |
| * @return the buffer itself, with ZERO bytes written, the position and limit |
| * are not changed after the call |
| */ |
| static ByteBuffer resetBuffer(ByteBuffer buffer, int len) { |
| int pos = buffer.position(); |
| buffer.put(getEmptyChunk(len), 0, len); |
| buffer.position(pos); |
| |
| return buffer; |
| } |
| |
| /** |
| * Ensure the buffer (either input or output) ready to read or write with ZERO |
| * bytes fully in specified length of len. |
| * @param buffer bytes array buffer |
| * @return the buffer itself |
| */ |
| static byte[] resetBuffer(byte[] buffer, int offset, int len) { |
| byte[] empty = getEmptyChunk(len); |
| System.arraycopy(empty, 0, buffer, offset, len); |
| |
| return buffer; |
| } |
| |
| /** |
| * Initialize the output buffers with ZERO bytes. |
| */ |
| static void resetOutputBuffers(ByteBuffer[] buffers, int dataLen) { |
| for (ByteBuffer buffer : buffers) { |
| resetBuffer(buffer, dataLen); |
| } |
| } |
| |
| /** |
| * Initialize the output buffers with ZERO bytes. |
| */ |
| static void resetOutputBuffers(byte[][] buffers, int[] offsets, |
| int dataLen) { |
| for (int i = 0; i < buffers.length; i++) { |
| resetBuffer(buffers[i], offsets[i], dataLen); |
| } |
| } |
| |
| /** |
| * Convert an array of this chunks to an array of ByteBuffers |
| * @param chunks chunks to convertToByteArrayState into buffers |
| * @return an array of ByteBuffers |
| */ |
| static ByteBuffer[] toBuffers(ECChunk[] chunks) { |
| ByteBuffer[] buffers = new ByteBuffer[chunks.length]; |
| |
| ECChunk chunk; |
| for (int i = 0; i < chunks.length; i++) { |
| chunk = chunks[i]; |
| if (chunk == null) { |
| buffers[i] = null; |
| } else { |
| buffers[i] = chunk.getBuffer(); |
| } |
| } |
| |
| return buffers; |
| } |
| |
| /** |
| * Clone an input bytes array as direct ByteBuffer. |
| */ |
| static ByteBuffer cloneAsDirectByteBuffer(byte[] input, int offset, int len) { |
| if (input == null) { // an input can be null, if erased or not to read |
| return null; |
| } |
| |
| ByteBuffer directBuffer = ByteBuffer.allocateDirect(len); |
| directBuffer.put(input, offset, len); |
| directBuffer.flip(); |
| return directBuffer; |
| } |
| |
| /** |
| * Get indexes array for items marked as null, either erased or |
| * not to read. |
| * @return indexes array |
| */ |
| static <T> int[] getNullIndexes(T[] inputs) { |
| int[] nullIndexes = new int[inputs.length]; |
| int idx = 0; |
| for (int i = 0; i < inputs.length; i++) { |
| if (inputs[i] == null) { |
| nullIndexes[idx++] = i; |
| } |
| } |
| |
| return Arrays.copyOf(nullIndexes, idx); |
| } |
| |
| /** |
| * Find the valid input from all the inputs. |
| * @param inputs input buffers to look for valid input |
| * @return the first valid input |
| */ |
| static <T> T findFirstValidInput(T[] inputs) { |
| for (T input : inputs) { |
| if (input != null) { |
| return input; |
| } |
| } |
| |
| throw new HadoopIllegalArgumentException( |
| "Invalid inputs are found, all being null"); |
| } |
| |
| /** |
| * Picking up indexes of valid inputs. |
| * @param inputs decoding input buffers |
| * @param <T> |
| */ |
| static <T> int[] getValidIndexes(T[] inputs) { |
| int[] validIndexes = new int[inputs.length]; |
| int idx = 0; |
| for (int i = 0; i < inputs.length; i++) { |
| if (inputs[i] != null) { |
| validIndexes[idx++] = i; |
| } |
| } |
| |
| return Arrays.copyOf(validIndexes, idx); |
| } |
| } |