/** * 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.yarn.server.timeline.util; import org.apache.hadoop.io.WritableComparator; import java.io.IOException; import static java.nio.charset.StandardCharsets.UTF_8; import static org.apache.hadoop.yarn.server.timeline.GenericObjectMapper.readReverseOrderedLong; public class LeveldbUtils { /** A string builder utility for building timeline server leveldb keys. */ public static class KeyBuilder { /** Maximum subkeys that can be added to construct a key. */ private static final int MAX_NUMBER_OF_KEY_ELEMENTS = 10; private byte[][] b; private boolean[] useSeparator; private int index; private int length; public KeyBuilder(int size) { b = new byte[size][]; useSeparator = new boolean[size]; index = 0; length = 0; } public static KeyBuilder newInstance() { return new KeyBuilder(MAX_NUMBER_OF_KEY_ELEMENTS); } /** Instantiate a new key build with the given maximum subkes. * @param size maximum subkeys that can be added to this key builder * @return a newly constructed key builder */ public static KeyBuilder newInstance(final int size) { return new KeyBuilder(size); } public KeyBuilder add(String s) { return add(s.getBytes(UTF_8), true); } public KeyBuilder add(byte[] t) { return add(t, false); } public KeyBuilder add(byte[] t, boolean sep) { b[index] = t; useSeparator[index] = sep; length += t.length; if (sep) { length++; } index++; return this; } /** Builds a byte array without the final string delimiter. */ public byte[] getBytes() { // check the last valid entry to see the final length int bytesLength = length; if (useSeparator[index - 1]) { bytesLength = length - 1; } byte[] bytes = new byte[bytesLength]; int curPos = 0; for (int i = 0; i < index; i++) { System.arraycopy(b[i], 0, bytes, curPos, b[i].length); curPos += b[i].length; if (i < index - 1 && useSeparator[i]) { bytes[curPos++] = 0x0; } } return bytes; } /** Builds a byte array including the final string delimiter. */ public byte[] getBytesForLookup() { byte[] bytes = new byte[length]; int curPos = 0; for (int i = 0; i < index; i++) { System.arraycopy(b[i], 0, bytes, curPos, b[i].length); curPos += b[i].length; if (useSeparator[i]) { bytes[curPos++] = 0x0; } } return bytes; } } public static class KeyParser { private final byte[] b; private int offset; public KeyParser(final byte[] b, final int offset) { this.b = b; this.offset = offset; } /** Returns a string from the offset until the next string delimiter. */ public String getNextString() throws IOException { if (offset >= b.length) { throw new IOException( "tried to read nonexistent string from byte array"); } int i = 0; while (offset + i < b.length && b[offset + i] != 0x0) { i++; } String s = new String(b, offset, i, UTF_8); offset = offset + i + 1; return s; } /** Moves current position until after the next end of string marker. */ public void skipNextString() throws IOException { if (offset >= b.length) { throw new IOException("tried to read nonexistent string from byte array"); } while (offset < b.length && b[offset] != 0x0) { ++offset; } ++offset; } /** Read the next 8 bytes in the byte buffer as a long. */ public long getNextLong() throws IOException { if (offset + 8 >= b.length) { throw new IOException("byte array ran out when trying to read long"); } long value = readReverseOrderedLong(b, offset); offset += 8; return value; } public int getOffset() { return offset; } /** Returns a copy of the remaining bytes. */ public byte[] getRemainingBytes() { byte[] bytes = new byte[b.length - offset]; System.arraycopy(b, offset, bytes, 0, b.length - offset); return bytes; } } /** * Returns true if the byte array begins with the specified prefix. */ public static boolean prefixMatches(byte[] prefix, int prefixlen, byte[] b) { if (b.length < prefixlen) { return false; } return WritableComparator.compareBytes(prefix, 0, prefixlen, b, 0, prefixlen) == 0; } }