/* * Copyright (C) 2015 The Android Open Source Project * * Licensed 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 com.android.tools.rpclib.binary; import gnu.trove.TIntObjectHashMap; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.io.IOException; import java.io.InputStream; import java.io.UnsupportedEncodingException; /** * A decoder of various RPC primitive types. * The encoding format is documented at the following link: * https://android.googlesource.com/platform/tools/gpu/+/master/binary/doc.go */ public class Decoder { @NotNull private final TIntObjectHashMap<BinaryObject> mDecodedMap; @NotNull private final InputStream mInputStream; @NotNull private final byte[] mBuffer; public Decoder(@NotNull InputStream in) { mDecodedMap = new TIntObjectHashMap<BinaryObject>(); mInputStream = in; mBuffer = new byte[9]; } public void read(byte[] buf, int count) throws IOException { int off = 0; while (off < count) { off += mInputStream.read(buf, off, count - off); } } private void read(int count) throws IOException { read(mBuffer, count); } public boolean bool() throws IOException { read(1); return mBuffer[0] != 0; } public byte int8() throws IOException { read(1); return mBuffer[0]; } public byte uint8() throws IOException { return int8(); } private long intv() throws IOException { long uv = uintv(); long v = uv >>> 1; if ((uv & 1) != 0) { v = ~v; } return v; } private long uintv() throws IOException { read(1); int count = 0; while (((0x80 >> count) & mBuffer[0]) != 0) count++; long v = mBuffer[0] & (0xff >> count); if (count == 0) { return v; } read(count); for (int i = 0; i < count; i++) { v = (v << 8) | (mBuffer[i] & 0xffL); } return v; } public short int16() throws IOException { return (short)intv(); } public short uint16() throws IOException { return (short)uintv(); } public int int32() throws IOException { return (int)intv(); } public int uint32() throws IOException { return (int)uintv(); } public long int64() throws IOException { return intv(); } public long uint64() throws IOException { return uintv(); } public float float32() throws IOException { int bits = (int)uintv(); int shuffled = ((bits & 0x000000ff) << 24) | ((bits & 0x0000ff00) << 8) | ((bits & 0x00ff0000) >> 8) | ((bits & 0xff000000) >>> 24); return Float.intBitsToFloat(shuffled); } public double float64() throws IOException { long bits = uintv(); long shuffled = ((bits & 0x00000000000000ffL) << 56) | ((bits & 0x000000000000ff00L) << 40) | ((bits & 0x0000000000ff0000L) << 24) | ((bits & 0x00000000ff000000L) << 8) | ((bits & 0x000000ff00000000L) >> 8) | ((bits & 0x0000ff0000000000L) >> 24) | ((bits & 0x00ff000000000000L) >> 40) | ((bits & 0xff00000000000000L) >>> 56); return Double.longBitsToDouble(shuffled); } public String string() throws IOException { int size = uint32(); byte[] bytes = new byte[size]; for (int i = 0; i < size; i++) { bytes[i] = int8(); } try { return new String(bytes, "UTF-8"); } catch (UnsupportedEncodingException e) { throw new RuntimeException(e); // Should never happen } } @Nullable public BinaryObject object() throws IOException { int key = uint32(); if (key == BinaryObject.NULL_ID) { return null; } BinaryObject obj = mDecodedMap.get(key); if (obj != null) { return obj; } ObjectTypeID type = new ObjectTypeID(this); BinaryObjectCreator creator = ObjectTypeID.lookup(type); if (creator == null) { throw new RuntimeException("Unknown type id encountered: " + type); } obj = creator.create(); obj.decode(this); mDecodedMap.put(key, obj); return obj; } public InputStream stream() { return mInputStream; } }