1 /* 2 * Copyright (C) 2020 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.storage.block.read; 18 19 import com.android.storage.io.read.TypedInputStream; 20 import com.android.storage.util.BitwiseUtils; 21 22 import java.nio.ByteBuffer; 23 import java.nio.ByteOrder; 24 import java.util.Objects; 25 26 /** 27 * Provides typed, absolute position, random access to a block's data. 28 * 29 * <p>See also {@link TypedInputStream} for a streamed 30 * equivalent. 31 */ 32 public final class BlockData { 33 34 private final ByteBuffer mDataBytes; 35 36 /** Wraps a read-only, big-endian {@link ByteBuffer}. */ BlockData(ByteBuffer dataBytes)37 public BlockData(ByteBuffer dataBytes) { 38 if (!dataBytes.isReadOnly()) { 39 throw new IllegalArgumentException("dataBytes must be readonly"); 40 } 41 if (dataBytes.order() != ByteOrder.BIG_ENDIAN) { 42 throw new IllegalArgumentException("dataBytes must be big-endian"); 43 } 44 mDataBytes = Objects.requireNonNull(dataBytes); 45 } 46 47 /** Returns a copy of the underlying {@link ByteBuffer}. */ getByteBuffer()48 public ByteBuffer getByteBuffer() { 49 return mDataBytes.duplicate(); 50 } 51 52 /** Returns the value of the byte at the specified position. */ getByte(int byteOffset)53 public byte getByte(int byteOffset) { 54 return mDataBytes.get(byteOffset); 55 } 56 57 /** Returns the value of the byte at the specified position as an unsigned value. */ getUnsignedByte(int byteOffset)58 public int getUnsignedByte(int byteOffset) { 59 return mDataBytes.get(byteOffset) & 0xFF; 60 } 61 62 /** Returns the value of the 16-bit char at the specified position as an unsigned value. */ getChar(int byteOffset)63 public char getChar(int byteOffset) { 64 return mDataBytes.getChar(byteOffset); 65 } 66 67 /** Returns the value of the 32-bit int at the specified position as an signed value. */ getInt(int byteOffset)68 public int getInt(int byteOffset) { 69 return mDataBytes.getInt(byteOffset); 70 } 71 72 /** Returns the value of the 64-bit long at the specified position as an signed value. */ getLong(int byteOffset)73 public long getLong(int byteOffset) { 74 return mDataBytes.getLong(byteOffset); 75 } 76 77 /** 78 * Returns a tiny (<= 255 entries) array of signed bytes starting at the specified position, 79 * where the length is encoded in the data. 80 */ getTinyByteArray(int byteOffset)81 public byte[] getTinyByteArray(int byteOffset) { 82 int size = getUnsignedByte(byteOffset); 83 return getBytes(byteOffset + 1, size); 84 } 85 86 /** 87 * Returns an array of signed bytes starting at the specified position, where the 4-byte length 88 * is encoded in the data. 89 */ getByteArray(int byteOffset)90 public byte[] getByteArray(int byteOffset) { 91 int size = getInt(byteOffset); 92 return getBytes(byteOffset + Integer.BYTES, size); 93 } 94 95 /** 96 * Returns an array of signed bytes starting at the specified position. 97 */ getBytes(int byteOffset, int byteCount)98 public byte[] getBytes(int byteOffset, int byteCount) { 99 byte[] bytes = new byte[byteCount]; 100 for (int i = 0; i < byteCount; i++) { 101 bytes[i] = mDataBytes.get(byteOffset + i); 102 } 103 return bytes; 104 } 105 106 /** 107 * Returns a tiny (<= 255 entries) array of chars starting at the specified position, where the 108 * length is encoded in the data. 109 */ getTinyCharArray(int byteOffset)110 public char[] getTinyCharArray(int byteOffset) { 111 int size = getUnsignedByte(byteOffset); 112 return getChars(byteOffset + 1, size); 113 } 114 115 /** 116 * Returns an array of chars starting at the specified position. 117 */ getChars(int byteOffset, int charCount)118 public char[] getChars(int byteOffset, int charCount) { 119 char[] array = new char[charCount]; 120 for (int i = 0; i < charCount; i++) { 121 array[i] = getChar(byteOffset); 122 byteOffset += Character.BYTES; 123 } 124 return array; 125 } 126 127 /** 128 * Returns 1-8 bytes ({@code valueSizeBytes}) starting as the specified position as a 129 * {@code long}. The value can be interpreted as signed or unsigned depending on 130 * {@code signExtend}. 131 */ getValueAsLong(int valueSizeBytes, int byteOffset, boolean signExtend)132 public long getValueAsLong(int valueSizeBytes, int byteOffset, boolean signExtend) { 133 if (valueSizeBytes < 0 || valueSizeBytes > Long.BYTES) { 134 throw new IllegalArgumentException("valueSizeBytes must be <= 8 bytes"); 135 } 136 return getValueInternal(valueSizeBytes, byteOffset, signExtend); 137 } 138 139 /** Returns the size of the block data. */ getSize()140 public int getSize() { 141 return mDataBytes.limit(); 142 } 143 getValueInternal(int valueSizeBytes, int byteOffset, boolean signExtend)144 private long getValueInternal(int valueSizeBytes, int byteOffset, boolean signExtend) { 145 if (byteOffset < 0) { 146 throw new IllegalArgumentException( 147 "byteOffset=" + byteOffset + " must not be negative"); 148 } 149 150 // High bytes read first. 151 long value = 0; 152 int bytesRead = 0; 153 while (bytesRead++ < valueSizeBytes) { 154 value <<= 8; 155 value |= (mDataBytes.get(byteOffset++) & 0xFF); 156 } 157 if (valueSizeBytes < 8 && signExtend) { 158 int entrySizeBits = valueSizeBytes * Byte.SIZE; 159 value = BitwiseUtils.signExtendToLong(entrySizeBits, value); 160 } 161 return value; 162 } 163 } 164