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
35 public static void writeLong(final DataOutput out, final long value) throws IOException {
36 writeLong(out, value, 0);
40 * Write a long value into a {@link DataOutput}, compressing potential zero bytes. This method is useful for
41 * serializing counters and similar, which have a wide range, but typically do not use it. The value provided is
42 * treated as unsigned.
44 * This methods writes the number of trailing non-zero in the value. It then writes the minimum required bytes
45 * to reconstruct the value by left-padding zeroes. Inverse operation is performed by {@link #readLong(DataInput)}
46 * or a combination of {@link #readLongHeader(DataInput)} and {@link #readLongBody(DataInput, byte)}.
48 * Additionally the caller can use the top four bits (i.e. 0xF0) for caller-specific flags. These will be ignored
49 * by {@link #readLong(DataInput)}, but can be extracted via {@link #readLongHeader(DataInput)}.
51 * @param out Data output
52 * @param value long value to write
53 * @param flags flags to store
54 * @throws IOException if an I/O error occurs
56 public static void writeLong(final DataOutput out, final long value, final int flags) throws IOException {
57 Preconditions.checkArgument((flags & 0xFFFFFF0F) == 0, "Invalid flags {}", flags);
58 final int bytes = valueBytes(value);
59 out.writeByte(bytes | flags);
60 writeValue(out, value, bytes);
64 * Read a long value from a {@link DataInput} which was previously written via {@link #writeLong(DataOutput, long)}.
66 * @param in Data input
67 * @return long value extracted from the data input
68 * @throws IOException if an I/O error occurs
70 public static long readLong(final @Nonnull DataInput in) throws IOException {
71 return readLongBody(in, readLongHeader(in));
75 * Read the header of a compressed long value. The header may contain user-defined flags, which can be extracted
76 * via {@link #longHeaderFlags(byte)}.
78 * @param in Data input
79 * @return Header of next value
80 * @throws IOException if an I/O error occurs
82 public static byte readLongHeader(final @Nonnull DataInput in) throws IOException {
87 * Extract user-defined flags from a compressed long header. This will return 0 if the long value originates from
88 * {@link #writeLong(DataOutput, long)}.
90 * @param header Value header, as returned by {@link #readLongHeader(DataInput)}
91 * @return User-defined flags
93 public static int longHeaderFlags(final byte header) {
98 * Read a long value from a {@link DataInput} as hinted by the result of {@link #readLongHeader(DataInput)}.
100 * @param in Data input
101 * @param header Value header, as returned by {@link #readLongHeader(DataInput)}
102 * @throws IOException if an I/O error occurs
104 public static long readLongBody(final @Nonnull DataInput in, final byte header) throws IOException {
105 int bytes = header & 0xF;
111 value = (in.readInt() & 0xFFFFFFFFL) << (bytes * Byte.SIZE);
115 value |= in.readUnsignedShort() << (bytes * Byte.SIZE);
118 value |= in.readUnsignedByte();
125 return in.readLong();
129 private static void writeValue(final DataOutput out, final long value, final int bytes) throws IOException {
134 out.writeInt((int)(value >>> (left * Byte.SIZE)));
138 out.writeShort((int)(value >>> (left * Byte.SIZE)));
141 out.writeByte((int)(value & 0xFF));
144 out.writeLong(value);
148 private static int valueBytes(final long value) {
149 // This is a binary search for the first match. Note that we need to mask bits from the most significant one
150 if ((value & 0xFFFFFFFF00000000L) != 0) {
151 if ((value & 0xFFFF000000000000L) != 0) {
152 return (value & 0xFF00000000000000L) != 0 ? 8 : 7;
154 return (value & 0x0000FF0000000000L) != 0 ? 6 : 5;
156 } else if ((value & 0x00000000FFFFFFFFL) != 0) {
157 if ((value & 0x00000000FFFF0000L) != 0) {
158 return (value & 0x00000000FF000000L) != 0 ? 4 : 3;
160 return (value & 0x000000000000FF00L) != 0 ? 2 : 1;