/* * 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.TObjectIntHashMap; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.io.IOException; import java.io.OutputStream; import java.io.UnsupportedEncodingException; /** * An encoder of various primitive types. * The encoding format is documented at the following link: * https://android.googlesource.com/platform/tools/gpu/+/master/binary/doc.go */ public class Encoder { @NotNull private final OutputStream mOutputStream; @NotNull private final TObjectIntHashMap<BinaryObject> mEncodedMap; @NotNull private final byte[] mBuffer; public Encoder(@NotNull OutputStream out) { mEncodedMap = new TObjectIntHashMap<BinaryObject>(); mOutputStream = out; mBuffer = new byte[9]; } public void bool(boolean v) throws IOException { mBuffer[0] = (byte)(v ? 1 : 0); mOutputStream.write(mBuffer, 0, 1); } public void int8(byte v) throws IOException { mBuffer[0] = v; mOutputStream.write(mBuffer, 0, 1); } public void uint8(short v) throws IOException { mBuffer[0] = (byte)(v & 0xff); mOutputStream.write(mBuffer, 0, 1); } private void intv(long v) throws IOException { long uv = v << 1; if (v < 0) uv = ~uv; uintv(uv); } private void uintv(long v) throws IOException { long space = ~0x7fL; int tag = 0; for (int o = 8; true; o--) { if ((v & space) == 0) { mBuffer[o] = (byte)(v | tag); mOutputStream.write(mBuffer, o, 9 - o); return; } mBuffer[o] = (byte)(v&0xff); v >>>= 8; space >>= 1; tag =(tag >> 1) | 0x80; } } public void int16(short v) throws IOException { intv(v); } public void uint16(int v) throws IOException { uintv(v); } public void int32(int v) throws IOException { intv(v); } public void uint32(long v) throws IOException { uintv(v); } public void int64(long v) throws IOException { intv(v); } public void uint64(long v) throws IOException { uintv(v); } public void float32(float v) throws IOException { int bits = Float.floatToIntBits(v); int shuffled = ((bits & 0x000000ff) << 24) | ((bits & 0x0000ff00) << 8) | ((bits & 0x00ff0000) >> 8) | ((bits & 0xff000000) >>> 24); uintv(shuffled); } public void float64(double v) throws IOException { long bits = Double.doubleToLongBits(v); 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); uintv(shuffled); } public void string(@Nullable String v) throws IOException { try { if (v == null) { uint32(0); return; } byte[] bytes = v.getBytes("UTF-8"); uint32(bytes.length); for (byte b : bytes) { int8(b); } } catch (UnsupportedEncodingException e) { throw new RuntimeException(e); // Should never happen } } public void object(@Nullable BinaryObject obj) throws IOException { if (obj == null) { uint32(BinaryObject.NULL_ID); return; } if (mEncodedMap.containsKey(obj)) { int key = mEncodedMap.get(obj); uint16(key); return; } int key = mEncodedMap.size() + 1; mEncodedMap.put(obj, key); uint32(key); obj.type().encode(this); obj.encode(this); } public OutputStream stream() { return mOutputStream; } }