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 static com.google.common.base.Preconditions.checkArgument;
12 import com.google.common.annotations.Beta;
13 import java.io.DataInput;
14 import java.io.DataOutput;
15 import java.io.IOException;
16 import org.eclipse.jdt.annotation.NonNullByDefault;
19 * Utility methods for working with {@link WritableObject}s.
21 * @author Robert Varga
25 public final class WritableObjects {
26 private WritableObjects() {
31 * Shorthand for {@link #writeLong(DataOutput, long, int)} with zero flags.
33 * @param out Data output
34 * @param value long value to write
35 * @throws IOException if an I/O error occurs
36 * @throws NullPointerException if output is null
38 public static void writeLong(final DataOutput out, final long value) throws IOException {
39 writeLong(out, value, 0);
43 * Write a long value into a {@link DataOutput}, compressing potential zero bytes. This method is useful for
44 * serializing counters and similar, which have a wide range, but typically do not use it. The value provided is
45 * treated as unsigned.
47 * <p>This methods writes the number of trailing non-zero in the value. It then writes the minimum required bytes
48 * to reconstruct the value by left-padding zeroes. Inverse operation is performed by {@link #readLong(DataInput)}
49 * or a combination of {@link #readLongHeader(DataInput)} and {@link #readLongBody(DataInput, byte)}.
51 * <p>Additionally the caller can use the top four bits (i.e. 0xF0) for caller-specific flags. These will be
52 * ignored by {@link #readLong(DataInput)}, but can be extracted via {@link #readLongHeader(DataInput)}.
54 * @param out Data output
55 * @param value long value to write
56 * @param flags flags to store
57 * @throws IOException if an I/O error occurs
58 * @throws NullPointerException if output is null
60 public static void writeLong(final DataOutput out, final long value, final int flags) throws IOException {
61 checkArgument((flags & 0xFFFFFF0F) == 0, "Invalid flags %s", flags);
62 final int bytes = valueBytes(value);
63 out.writeByte(bytes | flags);
64 writeValue(out, value, bytes);
68 * Read a long value from a {@link DataInput} which was previously written via {@link #writeLong(DataOutput, long)}.
70 * @param in Data input
71 * @return long value extracted from the data input
72 * @throws IOException if an I/O error occurs
73 * @throws NullPointerException if input is null
75 public static long readLong(final DataInput in) throws IOException {
76 return readLongBody(in, readLongHeader(in));
80 * Read the header of a compressed long value. The header may contain user-defined flags, which can be extracted
81 * via {@link #longHeaderFlags(byte)}.
83 * @param in Data input
84 * @return Header of next value
85 * @throws IOException if an I/O error occurs
86 * @throws NullPointerException if input is null
88 public static byte readLongHeader(final DataInput in) throws IOException {
93 * Extract user-defined flags from a compressed long header. This will return 0 if the long value originates from
94 * {@link #writeLong(DataOutput, long)}.
96 * @param header Value header, as returned by {@link #readLongHeader(DataInput)}
97 * @return User-defined flags
99 public static int longHeaderFlags(final byte header) {
100 return header & 0xF0;
104 * Read a long value from a {@link DataInput} as hinted by the result of {@link #readLongHeader(DataInput)}.
106 * @param in Data input
107 * @param header Value header, as returned by {@link #readLongHeader(DataInput)}
109 * @throws IOException if an I/O error occurs
110 * @throws NullPointerException if input is null
112 public static long readLongBody(final DataInput in, final byte header) throws IOException {
113 int bytes = header & 0xF;
115 return in.readLong();
125 value = (in.readInt() & 0xFFFFFFFFL) << bytes * Byte.SIZE;
129 value |= in.readUnsignedShort() << bytes * Byte.SIZE;
132 value |= in.readUnsignedByte();
138 * Write two consecutive long values. These values can be read back using {@link #readLongHeader(DataInput)},
139 * {@link #readFirstLong(DataInput, byte)} and {@link #readSecondLong(DataInput, byte)}.
141 * <p>This is a more efficient way of serializing two longs than {@link #writeLong(DataOutput, long)}. This is
142 * achieved by using the flags field to hold the length of the second long -- hence saving one byte.
144 * @param out Data output
145 * @param value0 first long value to write
146 * @param value1 second long value to write
147 * @throws IOException if an I/O error occurs
148 * @throws NullPointerException if output is null
150 public static void writeLongs(final DataOutput out, final long value0, final long value1) throws IOException {
151 final int clen = WritableObjects.valueBytes(value1);
152 writeLong(out, value0, clen << 4);
153 WritableObjects.writeValue(out, value1, clen);
157 * Read first long value from an input.
159 * @param in Data input
160 * @param header Value header, as returned by {@link #readLongHeader(DataInput)}
161 * @return First long specified in {@link #writeLongs(DataOutput, long, long)}
162 * @throws IOException if an I/O error occurs
163 * @throws NullPointerException if input is null
165 public static long readFirstLong(final DataInput in, final byte header) throws IOException {
166 return WritableObjects.readLongBody(in, header);
170 * Read second long value from an input.
172 * @param in Data input
173 * @param header Value header, as returned by {@link #readLongHeader(DataInput)}
174 * @return Second long specified in {@link #writeLongs(DataOutput, long, long)}
175 * @throws IOException if an I/O error occurs
176 * @throws NullPointerException if input is null
178 public static long readSecondLong(final DataInput in, final byte header) throws IOException {
179 return WritableObjects.readLongBody(in, (byte)(header >> 4));
182 private static void writeValue(final DataOutput out, final long value, final int bytes) throws IOException {
187 out.writeInt((int)(value >>> left * Byte.SIZE));
191 out.writeShort((int)(value >>> left * Byte.SIZE));
194 out.writeByte((int)(value & 0xFF));
197 out.writeLong(value);
201 private static int valueBytes(final long value) {
202 // This is a binary search for the first match. Note that we need to mask bits from the most significant one.
203 // It completes completes in three to four mask-and-compare operations.
204 if ((value & 0xFFFFFFFF00000000L) != 0) {
205 if ((value & 0xFFFF000000000000L) != 0) {
206 return (value & 0xFF00000000000000L) != 0 ? 8 : 7;
208 return (value & 0x0000FF0000000000L) != 0 ? 6 : 5;
209 } else if ((value & 0x00000000FFFFFFFFL) != 0) {
210 if ((value & 0x00000000FFFF0000L) != 0) {
211 return (value & 0x00000000FF000000L) != 0 ? 4 : 3;
213 return (value & 0x000000000000FF00L) != 0 ? 2 : 1;