aboutsummaryrefslogtreecommitdiff
path: root/s2storage/src/write/java/com/android/timezone/location/storage/io/write/TypedOutputStream.java
blob: 36e658c321eed33ca4f0a7f2cedb8dd5e3355dfc (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
/*
 * Copyright (C) 2020 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.timezone.location.storage.io.write;

import com.android.timezone.location.storage.util.BitwiseUtils;

import java.io.BufferedOutputStream;
import java.io.Closeable;
import java.io.DataOutputStream;
import java.io.Flushable;
import java.io.IOException;
import java.io.OutputStream;

/**
 * A stream-based writer of typed data that can be read back by
 * {@link com.android.timezone.location.storage.block.read.BlockData} and
 * {@link com.android.timezone.location.storage.io.read.TypedInputStream}
 */
public final class TypedOutputStream implements Flushable, Closeable {

    private final DataOutputStream mDataOutputStream;

    /** Creates an instance, wrapping the supplied stream. */
    public TypedOutputStream(OutputStream out) {
        mDataOutputStream = new DataOutputStream(new BufferedOutputStream(out, 8192));
    }

    /**
     * Writes {@code value} as a sequence of network-ordered bytes. {@code byteCount} must be
     * between 1 and 8 inclusive. All bits not to be written in {@code value} <em>must</em> be
     * zero'd otherwise an {@link IllegalArgumentException} will be thrown.
     */
    public void writeVarByteValue(int byteCount, long value) throws IOException {
        if (byteCount < 1 || byteCount > 8) {
            throw new IllegalArgumentException("byteCount " + byteCount + " out of range");
        }

        // To avoid errors, we check for zeros in the bytes we're not writing.
        if (byteCount < 8) {
            long unusedBits = value & BitwiseUtils.getHighBitsMask((8 - byteCount) * 8);
            if (unusedBits != 0) {
                throw new IllegalArgumentException("Bits not to be written should be zero,"
                        + " unusedBits=" + unusedBits);
            }
        }

        // Write the high bytes first.
        for (int i = byteCount - 1; i >= 0; i--) {
            byte b = (byte) (value >>> (i * 8));
            mDataOutputStream.write(b);
        }
    }

    /**
     * Writes {@code unsignedValue} as an unsigned byte. If {@code unsignedValue} is outside of the
     * range 0-255 inclusive, an {@link IllegalArgumentException} is thrown.
     */
    public void writeUnsignedByte(int unsignedValue) throws IOException {
        if (unsignedValue < 0 || unsignedValue > 255) {
            throw new IllegalArgumentException("unsignedValue=" + unsignedValue + " is negative");
        }
        mDataOutputStream.writeByte(unsignedValue);
    }

    /**
     * Writes {@code b} as a signed byte. If {@code b} is outside of the range -128-127 inclusive,
     * an {@link IllegalArgumentException} is thrown.
     */
    public void writeByte(int b) throws IOException {
        BitwiseUtils.checkSignedValueInRange(Byte.SIZE, b);
        mDataOutputStream.writeByte(b);
    }

    /**
     * Writes {@code bytes}.
     */
    public void writeBytes(byte[] bytes) throws IOException {
        mDataOutputStream.write(bytes);
    }

    /**
     * Writes {@code len} {@code bytes} starting at {@code off}.
     */
    public void writeBytes(byte[] bytes, int off, int len) throws IOException {
        mDataOutputStream.write(bytes, off, len);
    }

    /**
     * Writes a tiny (<= 255 entry) byte array as an unsigned byte (length) followed by the bytes.
     */
    public void writeTinyByteArray(byte[] bytes) throws IOException {
        writeUnsignedByte(bytes.length);
        mDataOutputStream.write(bytes);
    }

    /**
     * Writes a tiny (<= 255 entry) char array as an unsigned byte (length) followed by the chars.
     */
    public void writeTinyCharArray(char[] chars) throws IOException {
        writeUnsignedByte(chars.length);
        for (int i = 0; i < chars.length; i++) {
            mDataOutputStream.writeChar(chars[i]);
        }
    }

    /**
     * Writes {@code v} as an 16-bit value in network byte order. If {@code v} is outside of the
     * char range, an {@link IllegalArgumentException} is thrown.
     */
    public void writeChar(int v) throws IOException {
        BitwiseUtils.checkUnsignedValueInRange(Character.SIZE, v);
        mDataOutputStream.writeChar(v);
    }

    /**
     * Writes {@code v} as an 32-bit value in network byte order.
     */
    public void writeInt(int v) throws IOException {
        mDataOutputStream.writeInt(v);
    }

    /**
     * Writes {@code v} as an 64-bit value in network byte order.
     */
    public void writeLong(long v) throws IOException {
        mDataOutputStream.writeLong(v);
    }

    @Override
    public void close() throws IOException {
        mDataOutputStream.close();
    }

    @Override
    public void flush() throws IOException {
        mDataOutputStream.flush();
    }
}