blob: 284abac440c27ef5f190464164d6ae2e623f9066 [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
Carmelo Cascone6b32c992016-04-13 11:53:09 -070019import com.google.common.base.Objects;
20
21import java.nio.ByteBuffer;
22import java.nio.ByteOrder;
Carmelo Casconeaa8b6292016-04-13 14:27:06 -070023import java.util.Arrays;
Carmelo Cascone6b32c992016-04-13 11:53:09 -070024
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
Carmelo Casconeaa8b6292016-04-13 14:27:06 -070065 * @return a new immutable byte sequence
Carmelo Cascone6b32c992016-04-13 11:53:09 -070066 */
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 /**
Carmelo Cascone17fc9e42016-05-31 11:29:21 -070075 * Creates a new immutable byte sequence with the same content and order of
76 * the passed byte array, from/to the given indexes (inclusive).
77 *
78 * @param original a byte array value
79 * @return a new immutable byte sequence
80 */
81 public static ImmutableByteSequence copyFrom(byte[] original, int fromIdx, int toIdx) {
82 checkArgument(original != null && original.length > 0,
83 "Cannot copy from an empty or null array");
84 checkArgument(toIdx >= fromIdx && toIdx < original.length, "invalid indexes");
85 ByteBuffer buffer = ByteBuffer.allocate((toIdx - fromIdx) + 1);
86 for (int i = fromIdx; i <= toIdx; i++) {
87 buffer.put(original[i]);
88 }
89 return new ImmutableByteSequence(buffer);
90 }
91
92 /**
Carmelo Cascone6b32c992016-04-13 11:53:09 -070093 * Creates a new immutable byte sequence copying bytes from the given
94 * ByteBuffer {@link ByteBuffer}. If the byte buffer order is not big-endian
95 * bytes will be copied in reverse order.
96 *
97 * @param original a byte buffer
98 * @return a new byte buffer object
99 */
100 public static ImmutableByteSequence copyFrom(ByteBuffer original) {
101 checkArgument(original != null && original.capacity() > 0,
102 "Cannot copy from an empty or null byte buffer");
103
104 byte[] bytes = new byte[original.capacity()];
105
106 // copy bytes from original buffer
107 original.rewind();
108 original.get(bytes);
109
110 if (original.order() == ByteOrder.LITTLE_ENDIAN) {
111 // FIXME: this can be improved, e.g. read bytes in reverse order from original
112 reverse(bytes);
113 }
114
115 return new ImmutableByteSequence(ByteBuffer.wrap(bytes));
116 }
117
118 /**
119 * Creates a new byte sequence of 8 bytes containing the given long value.
120 *
121 * @param original a long value
Carmelo Casconeaa8b6292016-04-13 14:27:06 -0700122 * @return a new immutable byte sequence
Carmelo Cascone6b32c992016-04-13 11:53:09 -0700123 */
124 public static ImmutableByteSequence copyFrom(long original) {
125 return new ImmutableByteSequence(
126 ByteBuffer.allocate(Long.BYTES).putLong(original));
127 }
128
129 /**
130 * Creates a new byte sequence of 4 bytes containing the given int value.
131 *
132 * @param original an int value
Carmelo Casconeaa8b6292016-04-13 14:27:06 -0700133 * @return a new immutable byte sequence
Carmelo Cascone6b32c992016-04-13 11:53:09 -0700134 */
135 public static ImmutableByteSequence copyFrom(int original) {
136 return new ImmutableByteSequence(
137 ByteBuffer.allocate(Integer.BYTES).putInt(original));
138 }
139
140 /**
141 * Creates a new byte sequence of 2 bytes containing the given short value.
142 *
143 * @param original a short value
Carmelo Casconeaa8b6292016-04-13 14:27:06 -0700144 * @return a new immutable byte sequence
Carmelo Cascone6b32c992016-04-13 11:53:09 -0700145 */
146 public static ImmutableByteSequence copyFrom(short original) {
147 return new ImmutableByteSequence(
148 ByteBuffer.allocate(Short.BYTES).putShort(original));
149 }
150
151 /**
152 * Creates a new byte sequence of 1 byte containing the given value.
153 *
154 * @param original a byte value
Carmelo Casconeaa8b6292016-04-13 14:27:06 -0700155 * @return a new immutable byte sequence
Carmelo Cascone6b32c992016-04-13 11:53:09 -0700156 */
157 public static ImmutableByteSequence copyFrom(byte original) {
158 return new ImmutableByteSequence(
159 ByteBuffer.allocate(Byte.BYTES).put(original));
160 }
161
162 /**
Carmelo Casconeaa8b6292016-04-13 14:27:06 -0700163 * Creates a new byte sequence of the given size where alla bits are 0.
164 *
165 * @param size number of bytes
166 * @return a new immutable byte sequence
167 */
168 public static ImmutableByteSequence ofZeros(int size) {
169 byte[] bytes = new byte[size];
170 Arrays.fill(bytes, (byte) 0);
171 return new ImmutableByteSequence(ByteBuffer.wrap(bytes));
172 }
173
174 /**
175 * Creates a new byte sequence of the given size where alla bits are 1.
176 *
177 * @param size number of bytes
178 * @return a new immutable byte sequence
179 */
180 public static ImmutableByteSequence ofOnes(int size) {
181 byte[] bytes = new byte[size];
182 Arrays.fill(bytes, (byte) 0xFF);
183 return new ImmutableByteSequence(ByteBuffer.wrap(bytes));
184 }
185
186 /**
Carmelo Cascone6b32c992016-04-13 11:53:09 -0700187 * Returns a view of this sequence as a read-only {@link ByteBuffer}.
188 * <p>
189 * The returned buffer will have position 0, while limit and capacity will
190 * be set to this sequence {@link #size()}. The buffer order will be
191 * big-endian.
192 *
193 * @return a read-only byte buffer
194 */
195 public ByteBuffer asReadOnlyBuffer() {
196 // position, limit and capacity set rewind at constructor
197 return value.asReadOnlyBuffer();
198 }
199
200 /**
201 * Gets the number of bytes in this sequence.
202 *
203 * @return an integer value
204 */
205 public int size() {
206 return this.value.capacity();
207 }
208
209 /**
210 * Creates a new byte array view of this sequence.
211 *
212 * @return a new byte array
213 */
214 public byte[] asArray() {
215 ByteBuffer bb = asReadOnlyBuffer();
216 byte[] bytes = new byte[size()];
217 bb.get(bytes);
218 return bytes;
219 }
220
221 @Override
222 public int hashCode() {
223 return value.hashCode();
224 }
225
226 @Override
227 public boolean equals(Object obj) {
228 if (this == obj) {
229 return true;
230 }
231 if (obj == null || getClass() != obj.getClass()) {
232 return false;
233 }
234 final ImmutableByteSequence other = (ImmutableByteSequence) obj;
235 return Objects.equal(this.value, other.value);
236 }
237
238 @Override
239 public String toString() {
Carmelo Cascone804c0012016-06-23 13:05:40 -0700240 return HexString.toHexString(value.array());
Carmelo Cascone6b32c992016-04-13 11:53:09 -0700241 }
242}