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.io.write; 18 19 import com.android.timezone.location.storage.util.BitwiseUtils; 20 21 import java.io.BufferedOutputStream; 22 import java.io.Closeable; 23 import java.io.DataOutputStream; 24 import java.io.Flushable; 25 import java.io.IOException; 26 import java.io.OutputStream; 27 28 /** 29 * A stream-based writer of typed data that can be read back by 30 * {@link com.android.timezone.location.storage.block.read.BlockData} and 31 * {@link com.android.timezone.location.storage.io.read.TypedInputStream} 32 */ 33 public final class TypedOutputStream implements Flushable, Closeable { 34 35 private final DataOutputStream mDataOutputStream; 36 37 /** Creates an instance, wrapping the supplied stream. */ TypedOutputStream(OutputStream out)38 public TypedOutputStream(OutputStream out) { 39 mDataOutputStream = new DataOutputStream(new BufferedOutputStream(out, 8192)); 40 } 41 42 /** 43 * Writes {@code value} as a sequence of network-ordered bytes. {@code byteCount} must be 44 * between 1 and 8 inclusive. All bits not to be written in {@code value} <em>must</em> be 45 * zero'd otherwise an {@link IllegalArgumentException} will be thrown. 46 */ writeVarByteValue(int byteCount, long value)47 public void writeVarByteValue(int byteCount, long value) throws IOException { 48 if (byteCount < 1 || byteCount > 8) { 49 throw new IllegalArgumentException("byteCount " + byteCount + " out of range"); 50 } 51 52 // To avoid errors, we check for zeros in the bytes we're not writing. 53 if (byteCount < 8) { 54 long unusedBits = value & BitwiseUtils.getHighBitsMask((8 - byteCount) * 8); 55 if (unusedBits != 0) { 56 throw new IllegalArgumentException("Bits not to be written should be zero," 57 + " unusedBits=" + unusedBits); 58 } 59 } 60 61 // Write the high bytes first. 62 for (int i = byteCount - 1; i >= 0; i--) { 63 byte b = (byte) (value >>> (i * 8)); 64 mDataOutputStream.write(b); 65 } 66 } 67 68 /** 69 * Writes {@code unsignedValue} as an unsigned byte. If {@code unsignedValue} is outside of the 70 * range 0-255 inclusive, an {@link IllegalArgumentException} is thrown. 71 */ writeUnsignedByte(int unsignedValue)72 public void writeUnsignedByte(int unsignedValue) throws IOException { 73 if (unsignedValue < 0 || unsignedValue > 255) { 74 throw new IllegalArgumentException("unsignedValue=" + unsignedValue + " is negative"); 75 } 76 mDataOutputStream.writeByte(unsignedValue); 77 } 78 79 /** 80 * Writes {@code b} as a signed byte. If {@code b} is outside of the range -128-127 inclusive, 81 * an {@link IllegalArgumentException} is thrown. 82 */ writeByte(int b)83 public void writeByte(int b) throws IOException { 84 BitwiseUtils.checkSignedValueInRange(Byte.SIZE, b); 85 mDataOutputStream.writeByte(b); 86 } 87 88 /** 89 * Writes {@code bytes}. 90 */ writeBytes(byte[] bytes)91 public void writeBytes(byte[] bytes) throws IOException { 92 mDataOutputStream.write(bytes); 93 } 94 95 /** 96 * Writes {@code len} {@code bytes} starting at {@code off}. 97 */ writeBytes(byte[] bytes, int off, int len)98 public void writeBytes(byte[] bytes, int off, int len) throws IOException { 99 mDataOutputStream.write(bytes, off, len); 100 } 101 102 /** 103 * Writes a tiny (<= 255 entry) byte array as an unsigned byte (length) followed by the bytes. 104 */ writeTinyByteArray(byte[] bytes)105 public void writeTinyByteArray(byte[] bytes) throws IOException { 106 writeUnsignedByte(bytes.length); 107 mDataOutputStream.write(bytes); 108 } 109 110 /** 111 * Writes a tiny (<= 255 entry) char array as an unsigned byte (length) followed by the chars. 112 */ writeTinyCharArray(char[] chars)113 public void writeTinyCharArray(char[] chars) throws IOException { 114 writeUnsignedByte(chars.length); 115 for (int i = 0; i < chars.length; i++) { 116 mDataOutputStream.writeChar(chars[i]); 117 } 118 } 119 120 /** 121 * Writes {@code v} as an 16-bit value in network byte order. If {@code v} is outside of the 122 * char range, an {@link IllegalArgumentException} is thrown. 123 */ writeChar(int v)124 public void writeChar(int v) throws IOException { 125 BitwiseUtils.checkUnsignedValueInRange(Character.SIZE, v); 126 mDataOutputStream.writeChar(v); 127 } 128 129 /** 130 * Writes {@code v} as an 32-bit value in network byte order. 131 */ writeInt(int v)132 public void writeInt(int v) throws IOException { 133 mDataOutputStream.writeInt(v); 134 } 135 136 /** 137 * Writes {@code v} as an 64-bit value in network byte order. 138 */ writeLong(long v)139 public void writeLong(long v) throws IOException { 140 mDataOutputStream.writeLong(v); 141 } 142 143 @Override close()144 public void close() throws IOException { 145 mDataOutputStream.close(); 146 } 147 148 @Override flush()149 public void flush() throws IOException { 150 mDataOutputStream.flush(); 151 } 152 } 153