2 * Copyright (c) 2016 Cisco Systems, Inc. and others. All rights reserved.
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6 * and is available at http://www.eclipse.org/legal/epl-v10.html
8 package org.opendaylight.yangtools.concepts;
10 import com.google.common.annotations.Beta;
11 import com.google.common.base.Preconditions;
12 import java.io.DataInput;
13 import java.io.DataOutput;
14 import java.io.IOException;
15 import javax.annotation.Nonnull;
18 * Utility methods for working with {@link WritableObject}s.
20 * @author Robert Varga
23 public final class WritableObjects {
24 private WritableObjects() {
25 throw new UnsupportedOperationException();
29 * Shorthand for {@link #writeLong(DataOutput, long, int)} with zero flags.
31 * @param out Data output
32 * @param value long value to write
33 * @throws IOException if an I/O error occurs
34 * @throws NullPointerException if output is null
36 public static void writeLong(final DataOutput out, final long value) throws IOException {
37 writeLong(out, value, 0);
41 * Write a long value into a {@link DataOutput}, compressing potential zero bytes. This method is useful for
42 * serializing counters and similar, which have a wide range, but typically do not use it. The value provided is
43 * treated as unsigned.
45 * This methods writes the number of trailing non-zero in the value. It then writes the minimum required bytes
46 * to reconstruct the value by left-padding zeroes. Inverse operation is performed by {@link #readLong(DataInput)}
47 * or a combination of {@link #readLongHeader(DataInput)} and {@link #readLongBody(DataInput, byte)}.
49 * Additionally the caller can use the top four bits (i.e. 0xF0) for caller-specific flags. These will be ignored
50 * by {@link #readLong(DataInput)}, but can be extracted via {@link #readLongHeader(DataInput)}.
52 * @param out Data output
53 * @param value long value to write
54 * @param flags flags to store
55 * @throws IOException if an I/O error occurs
56 * @throws NullPointerException if output is null
58 public static void writeLong(final DataOutput out, final long value, final int flags) throws IOException {
59 Preconditions.checkArgument((flags & 0xFFFFFF0F) == 0, "Invalid flags {}", flags);
60 final int bytes = valueBytes(value);
61 out.writeByte(bytes | flags);
62 writeValue(out, value, bytes);
66 * Read a long value from a {@link DataInput} which was previously written via {@link #writeLong(DataOutput, long)}.
68 * @param in Data input
69 * @return long value extracted from the data input
70 * @throws IOException if an I/O error occurs
71 * @throws NullPointerException if input is null
73 public static long readLong(final @Nonnull DataInput in) throws IOException {
74 return readLongBody(in, readLongHeader(in));
78 * Read the header of a compressed long value. The header may contain user-defined flags, which can be extracted
79 * via {@link #longHeaderFlags(byte)}.
81 * @param in Data input
82 * @return Header of next value
83 * @throws IOException if an I/O error occurs
84 * @throws NullPointerException if input is null
86 public static byte readLongHeader(final @Nonnull DataInput in) throws IOException {
91 * Extract user-defined flags from a compressed long header. This will return 0 if the long value originates from
92 * {@link #writeLong(DataOutput, long)}.
94 * @param header Value header, as returned by {@link #readLongHeader(DataInput)}
95 * @return User-defined flags
97 public static int longHeaderFlags(final byte header) {
102 * Read a long value from a {@link DataInput} as hinted by the result of {@link #readLongHeader(DataInput)}.
104 * @param in Data input
105 * @param header Value header, as returned by {@link #readLongHeader(DataInput)}
106 * @throws IOException if an I/O error occurs
107 * @throws NullPointerException if input is null
109 public static long readLongBody(final @Nonnull DataInput in, final byte header) throws IOException {
110 int bytes = header & 0xF;
116 value = (in.readInt() & 0xFFFFFFFFL) << (bytes * Byte.SIZE);
120 value |= in.readUnsignedShort() << (bytes * Byte.SIZE);
123 value |= in.readUnsignedByte();
130 return in.readLong();
135 * Write two consecutive long values. These values can be read back using {@link #readLongHeader(DataInput)},
136 * {@link #readFirstLong(DataInput, byte)} and {@link #readSecondLong(DataInput, byte)}.
138 * This is a more efficient way of serializing two longs than {@link #writeLong(DataOutput, long)}. This is achieved
139 * by using the flags field to hold the length of the second long -- hence saving one byte.
141 * @param out Data output
142 * @param value0 first long value to write
143 * @param value1 second long value to write
144 * @throws IOException if an I/O error occurs
145 * @throws NullPointerException if output is null
147 public static void writeLongs(final @Nonnull DataOutput out, final long value0, final long value1) throws IOException {
148 final int clen = WritableObjects.valueBytes(value1);
149 writeLong(out, value0, clen << 4);
150 WritableObjects.writeValue(out, value1, clen);
154 * Read first long value from an input.
156 * @param in Data input
157 * @param header Value header, as returned by {@link #readLongHeader(DataInput)}
158 * @return First long specified in {@link #writeLongs(DataOutput, long, long)}
159 * @throws IOException if an I/O error occurs
160 * @throws NullPointerException if input is null
162 public static long readFirstLong(final @Nonnull DataInput in, final byte header) throws IOException {
163 return WritableObjects.readLongBody(in, header);
167 * Read second long value from an input.
169 * @param in Data input
170 * @param header Value header, as returned by {@link #readLongHeader(DataInput)}
171 * @return Second long specified in {@link #writeLongs(DataOutput, long, long)}
172 * @throws IOException if an I/O error occurs
173 * @throws NullPointerException if input is null
175 public static long readSecondLong(final @Nonnull DataInput in, final byte header) throws IOException {
176 return WritableObjects.readLongBody(in, (byte)(header >>> 4));
179 private static void writeValue(final DataOutput out, final long value, final int bytes) throws IOException {
184 out.writeInt((int)(value >>> (left * Byte.SIZE)));
188 out.writeShort((int)(value >>> (left * Byte.SIZE)));
191 out.writeByte((int)(value & 0xFF));
194 out.writeLong(value);
198 private static int valueBytes(final long value) {
199 // This is a binary search for the first match. Note that we need to mask bits from the most significant one.
200 // It completes completes in three to four mask-and-compare operations.
201 if ((value & 0xFFFFFFFF00000000L) != 0) {
202 if ((value & 0xFFFF000000000000L) != 0) {
203 return (value & 0xFF00000000000000L) != 0 ? 8 : 7;
205 return (value & 0x0000FF0000000000L) != 0 ? 6 : 5;
207 } else if ((value & 0x00000000FFFFFFFFL) != 0) {
208 if ((value & 0x00000000FFFF0000L) != 0) {
209 return (value & 0x00000000FF000000L) != 0 ? 4 : 3;
211 return (value & 0x000000000000FF00L) != 0 ? 2 : 1;