blob: 2d10bfb507744424ae2e96e7241d5f97616601f4 [file] [log] [blame]
Carmelo Cascone6b32c992016-04-13 11:53:09 -07001/*
2 * Copyright 2016-present Open Networking Laboratory
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package org.onlab.util;
18
19import com.google.common.base.MoreObjects;
20import com.google.common.base.Objects;
21
22import java.nio.ByteBuffer;
23import java.nio.ByteOrder;
24
25import static com.google.common.base.Preconditions.checkArgument;
26import static org.apache.commons.lang3.ArrayUtils.reverse;
27
28/**
29 * Immutable sequence of bytes, assumed to represent a value in
30 * {@link ByteOrder#BIG_ENDIAN BIG_ENDIAN} order.
31 * <p>
32 * Sequences can be created copying from an already existing representation of a
33 * sequence of bytes, such as {@link ByteBuffer} or {@code byte[]}; or by
34 * copying bytes from a primitive data type, such as {@code long}, {@code int}
35 * or {@code short}. In the first case, bytes are assumed to be already given in
36 * big-endian order, while in the second case big-endianness is enforced by this
37 * class.
38 */
39public final class ImmutableByteSequence {
40
41 /*
42 Actual bytes are backed by a byte buffer.
43 The order of a newly-created byte buffer is always BIG_ENDIAN.
44 */
45 private ByteBuffer value;
46
47 /**
48 * Private constructor.
49 * Creates a new byte sequence object backed by the passed ByteBuffer.
50 *
51 * @param value a byte buffer
52 */
53 private ImmutableByteSequence(ByteBuffer value) {
54 this.value = value;
55 // Rewind buffer so it's ready to be read.
56 // No write operation should be performed on it from now on.
57 this.value.rewind();
58 }
59
60 /**
61 * Creates a new immutable byte sequence with the same content and order of
62 * the passed byte array.
63 *
64 * @param original a byte array value
65 * @return a new immutable byte buffer
66 */
67 public static ImmutableByteSequence copyFrom(byte[] original) {
68 checkArgument(original != null && original.length > 0,
69 "Cannot copy from an empty or null array");
70 return new ImmutableByteSequence(
71 ByteBuffer.allocate(original.length).put(original));
72 }
73
74 /**
75 * Creates a new immutable byte sequence copying bytes from the given
76 * ByteBuffer {@link ByteBuffer}. If the byte buffer order is not big-endian
77 * bytes will be copied in reverse order.
78 *
79 * @param original a byte buffer
80 * @return a new byte buffer object
81 */
82 public static ImmutableByteSequence copyFrom(ByteBuffer original) {
83 checkArgument(original != null && original.capacity() > 0,
84 "Cannot copy from an empty or null byte buffer");
85
86 byte[] bytes = new byte[original.capacity()];
87
88 // copy bytes from original buffer
89 original.rewind();
90 original.get(bytes);
91
92 if (original.order() == ByteOrder.LITTLE_ENDIAN) {
93 // FIXME: this can be improved, e.g. read bytes in reverse order from original
94 reverse(bytes);
95 }
96
97 return new ImmutableByteSequence(ByteBuffer.wrap(bytes));
98 }
99
100 /**
101 * Creates a new byte sequence of 8 bytes containing the given long value.
102 *
103 * @param original a long value
104 * @return a new immutable byte buffer
105 */
106 public static ImmutableByteSequence copyFrom(long original) {
107 return new ImmutableByteSequence(
108 ByteBuffer.allocate(Long.BYTES).putLong(original));
109 }
110
111 /**
112 * Creates a new byte sequence of 4 bytes containing the given int value.
113 *
114 * @param original an int value
115 * @return a new immutable byte buffer
116 */
117 public static ImmutableByteSequence copyFrom(int original) {
118 return new ImmutableByteSequence(
119 ByteBuffer.allocate(Integer.BYTES).putInt(original));
120 }
121
122 /**
123 * Creates a new byte sequence of 2 bytes containing the given short value.
124 *
125 * @param original a short value
126 * @return a new immutable byte buffer
127 */
128 public static ImmutableByteSequence copyFrom(short original) {
129 return new ImmutableByteSequence(
130 ByteBuffer.allocate(Short.BYTES).putShort(original));
131 }
132
133 /**
134 * Creates a new byte sequence of 1 byte containing the given value.
135 *
136 * @param original a byte value
137 * @return a new immutable byte buffer
138 */
139 public static ImmutableByteSequence copyFrom(byte original) {
140 return new ImmutableByteSequence(
141 ByteBuffer.allocate(Byte.BYTES).put(original));
142 }
143
144 /**
145 * Returns a view of this sequence as a read-only {@link ByteBuffer}.
146 * <p>
147 * The returned buffer will have position 0, while limit and capacity will
148 * be set to this sequence {@link #size()}. The buffer order will be
149 * big-endian.
150 *
151 * @return a read-only byte buffer
152 */
153 public ByteBuffer asReadOnlyBuffer() {
154 // position, limit and capacity set rewind at constructor
155 return value.asReadOnlyBuffer();
156 }
157
158 /**
159 * Gets the number of bytes in this sequence.
160 *
161 * @return an integer value
162 */
163 public int size() {
164 return this.value.capacity();
165 }
166
167 /**
168 * Creates a new byte array view of this sequence.
169 *
170 * @return a new byte array
171 */
172 public byte[] asArray() {
173 ByteBuffer bb = asReadOnlyBuffer();
174 byte[] bytes = new byte[size()];
175 bb.get(bytes);
176 return bytes;
177 }
178
179 @Override
180 public int hashCode() {
181 return value.hashCode();
182 }
183
184 @Override
185 public boolean equals(Object obj) {
186 if (this == obj) {
187 return true;
188 }
189 if (obj == null || getClass() != obj.getClass()) {
190 return false;
191 }
192 final ImmutableByteSequence other = (ImmutableByteSequence) obj;
193 return Objects.equal(this.value, other.value);
194 }
195
196 @Override
197 public String toString() {
198 return MoreObjects.toStringHelper(this)
199 .addValue(HexString.toHexString(asArray()))
200 .toString();
201 }
202}