BUG-5280: optimize identifier serialization format
[controller.git] / opendaylight / md-sal / cds-access-api / src / main / java / org / opendaylight / controller / cluster / access / concepts / WritableObjects.java
1 /*
2  * Copyright (c) 2016 Cisco Systems, Inc. and others.  All rights reserved.
3  *
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
7  */
8 package org.opendaylight.controller.cluster.access.concepts;
9
10 import java.io.DataInput;
11 import java.io.DataOutput;
12 import java.io.IOException;
13
14 //FIXME: this really should go into yangtools/common/concepts.
15 public final class WritableObjects {
16     private WritableObjects() {
17         throw new UnsupportedOperationException();
18     }
19
20     /**
21      * Write a long value into a {@link DataOutput}, compressing potential zero bytes. This method is useful for
22      * serializing counters and similar, which have a wide range, but typically do not use it. The value provided is
23      * treated as unsigned.
24      *
25      * This methods writes the number of trailing non-zero in the value. It then writes the minimum required bytes
26      * to reconstruct the value by left-padding zeroes. Inverse operation is performed by {@link #readLong(DataInput)}.
27      *
28      * @param out Output
29      * @param value long value to write
30      */
31     public static void writeLong(final DataOutput out, final long value) throws IOException {
32         final int bytes = valueBytes(value);
33         out.writeByte(bytes);
34         writeValue(out, value, bytes);
35     }
36
37     public static long readLong(final DataInput in) throws IOException {
38         return readValue(in, in.readByte());
39     }
40
41     private static long readValue(final DataInput in, final int flags) throws IOException {
42         int bytes = flags & 0xf;
43         if (bytes < 8) {
44             if (bytes > 0) {
45                 long value = 0;
46                 if (bytes >= 4) {
47                     bytes -= 4;
48                     value = (in.readInt() & 0xFFFFFFFFL) << (bytes * Byte.SIZE);
49                 }
50                 if (bytes >= 2) {
51                     bytes -= 2;
52                     value |= in.readUnsignedShort() << (bytes * Byte.SIZE);
53                 }
54                 if (bytes > 0) {
55                     value |= in.readUnsignedByte();
56                 }
57                 return value;
58             } else {
59                 return 0;
60             }
61         } else {
62             return in.readLong();
63         }
64     }
65
66     private static void writeValue(final DataOutput out, final long value, final int bytes) throws IOException {
67         if (bytes < 8) {
68             int left = bytes;
69             if (left >= 4) {
70                 left -= 4;
71                 out.writeInt((int)(value >>> (left * Byte.SIZE)));
72             }
73             if (left >= 2) {
74                 left -= 2;
75                 out.writeShort((int)(value >>> (left * Byte.SIZE)));
76             }
77             if (left > 0) {
78                 out.writeByte((int)(value & 0xFF));
79             }
80         } else {
81             out.writeLong(value);
82         }
83     }
84
85     private static int valueBytes(final long value) {
86         // This is a binary search for the first match. Note that we need to mask bits from the most significant one
87         if ((value & 0xFFFFFFFF00000000L) != 0) {
88             if ((value & 0xFFFF000000000000L) != 0) {
89                 return (value & 0xFF00000000000000L) != 0 ? 8 : 7;
90             } else {
91                 return (value & 0xFF0000000000L) != 0 ? 6 : 5;
92             }
93         } else if ((value & 0xFFFFFFFFL) != 0) {
94             if ((value & 0xFFFF0000L) != 0) {
95                 return (value & 0xFF000000L) != 0 ? 4 : 3;
96             } else {
97                 return (value & 0xFF00L) != 0 ? 2 : 1;
98             }
99         } else {
100             return 0;
101         }
102     }
103 }