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.timezone.location.storage.block.read; 18 19 import com.android.timezone.location.storage.util.BitwiseUtils; 20 21 import java.nio.ByteBuffer; 22 import java.nio.ByteOrder; 23 import java.util.Objects; 24 25 /** 26 * Provides typed, absolute position, random access to a block's data. 27 * 28 * <p>See also {@link com.android.timezone.location.storage.io.read.TypedInputStream} for a streamed 29 * equivalent. 30 */ 31 public final class BlockData { 32 33 private final ByteBuffer mDataBytes; 34 35 /** Wraps a read-only, big-endian {@link ByteBuffer}. */ BlockData(ByteBuffer dataBytes)36 public BlockData(ByteBuffer dataBytes) { 37 if (!dataBytes.isReadOnly()) { 38 throw new IllegalArgumentException("dataBytes must be readonly"); 39 } 40 if (dataBytes.order() != ByteOrder.BIG_ENDIAN) { 41 throw new IllegalArgumentException("dataBytes must be big-endian"); 42 } 43 mDataBytes = Objects.requireNonNull(dataBytes); 44 } 45 46 /** Returns a copy of the underlying {@link ByteBuffer}. */ getByteBuffer()47 public ByteBuffer getByteBuffer() { 48 return mDataBytes.duplicate(); 49 } 50 51 /** Returns the value of the byte at the specified position. */ getByte(int byteOffset)52 public byte getByte(int byteOffset) { 53 return mDataBytes.get(byteOffset); 54 } 55 56 /** Returns the value of the byte at the specified position as an unsigned value. */ getUnsignedByte(int byteOffset)57 public int getUnsignedByte(int byteOffset) { 58 return mDataBytes.get(byteOffset) & 0xFF; 59 } 60 61 /** Returns the value of the 16-bit char at the specified position as an unsigned value. */ getChar(int byteOffset)62 public char getChar(int byteOffset) { 63 return mDataBytes.getChar(byteOffset); 64 } 65 66 /** Returns the value of the 32-bit int at the specified position as an signed value. */ getInt(int byteOffset)67 public int getInt(int byteOffset) { 68 return mDataBytes.getInt(byteOffset); 69 } 70 71 /** Returns the value of the 64-bit long at the specified position as an signed value. */ getLong(int byteOffset)72 public long getLong(int byteOffset) { 73 return mDataBytes.getLong(byteOffset); 74 } 75 76 /** 77 * Returns a tiny (<= 255 entries) array of signed bytes starting at the specified position, 78 * where the length is encoded in the data. 79 */ getTinyByteArray(int byteOffset)80 public byte[] getTinyByteArray(int byteOffset) { 81 int size = getUnsignedByte(byteOffset); 82 return getBytes(byteOffset + 1, size); 83 } 84 85 /** 86 * Returns an array of signed bytes starting at the specified position. 87 */ getBytes(int byteOffset, int byteCount)88 public byte[] getBytes(int byteOffset, int byteCount) { 89 byte[] bytes = new byte[byteCount]; 90 for (int i = 0; i < byteCount; i++) { 91 bytes[i] = mDataBytes.get(byteOffset + i); 92 } 93 return bytes; 94 } 95 96 /** 97 * Returns a tiny (<= 255 entries) array of chars starting at the specified position, where the 98 * length is encoded in the data. 99 */ getTinyCharArray(int byteOffset)100 public char[] getTinyCharArray(int byteOffset) { 101 int size = getUnsignedByte(byteOffset); 102 return getChars(byteOffset + 1, size); 103 } 104 105 /** 106 * Returns an array of chars starting at the specified position. 107 */ getChars(int byteOffset, int charCount)108 public char[] getChars(int byteOffset, int charCount) { 109 char[] array = new char[charCount]; 110 for (int i = 0; i < charCount; i++) { 111 array[i] = getChar(byteOffset); 112 byteOffset += Character.BYTES; 113 } 114 return array; 115 } 116 117 /** 118 * Returns 1-8 bytes ({@code valueSizeBytes}) starting as the specified position as a 119 * {@code long}. The value can be interpreted as signed or unsigned depending on 120 * {@code signExtend}. 121 */ getValueAsLong(int valueSizeBytes, int byteOffset, boolean signExtend)122 public long getValueAsLong(int valueSizeBytes, int byteOffset, boolean signExtend) { 123 if (valueSizeBytes < 0 || valueSizeBytes > Long.BYTES) { 124 throw new IllegalArgumentException("valueSizeBytes must be <= 8 bytes"); 125 } 126 return getValueInternal(valueSizeBytes, byteOffset, signExtend); 127 } 128 129 /** Returns the size of the block data. */ getSize()130 public int getSize() { 131 return mDataBytes.limit(); 132 } 133 getValueInternal(int valueSizeBytes, int byteOffset, boolean signExtend)134 private long getValueInternal(int valueSizeBytes, int byteOffset, boolean signExtend) { 135 if (byteOffset < 0) { 136 throw new IllegalArgumentException( 137 "byteOffset=" + byteOffset + " must not be negative"); 138 } 139 140 // High bytes read first. 141 long value = 0; 142 int bytesRead = 0; 143 while (bytesRead++ < valueSizeBytes) { 144 value <<= 8; 145 value |= (mDataBytes.get(byteOffset++) & 0xFF); 146 } 147 if (valueSizeBytes < 8 && signExtend) { 148 int entrySizeBits = valueSizeBytes * Byte.SIZE; 149 value = BitwiseUtils.signExtendToLong(entrySizeBits, value); 150 } 151 return value; 152 } 153 } 154