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