1 package org.metricshub.ipmi.core.common;
2
3 /*-
4 * ╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲
5 * IPMI Java Client
6 * ჻჻჻჻჻჻
7 * Copyright 2023 Verax Systems, MetricsHub
8 * ჻჻჻჻჻჻
9 * This program is free software: you can redistribute it and/or modify
10 * it under the terms of the GNU Lesser General Public License as
11 * published by the Free Software Foundation, either version 3 of the
12 * License, or (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Lesser Public License for more details.
18 *
19 * You should have received a copy of the GNU General Lesser Public
20 * License along with this program. If not, see
21 * <http://www.gnu.org/licenses/lgpl-3.0.html>.
22 * ╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱
23 */
24
25 /**
26 * {@link ByteBuffer} is a wrapper for byte[], and allows to safely read and write from the buffer.
27 * {@link ByteBuffer} is thread safe and guarantees proper handling of subsequent reads and writes, if they are within availabla buffer size.
28 */
29 public class ByteBuffer {
30
31 /**
32 * Main buffer, in which all data is stored.
33 */
34 private byte[] buffer;
35
36 /**
37 * Current position of read marker.
38 */
39 private int readMarker;
40
41 /**
42 * Current position of write marker.
43 */
44 private int writeMarker;
45
46 /**
47 * Allocate new {@link ByteBuffer} with given size.
48 *
49 * @param capacity
50 * max capacity of constructed buffer.
51 */
52 public ByteBuffer(int capacity) {
53 if (capacity <= 0) {
54 throw new IllegalArgumentException("Buffer must have positive capacity");
55 }
56
57 buffer = new byte[capacity];
58 readMarker = 0;
59 writeMarker = 0;
60 }
61
62 /**
63 * Attempts to write given byte array to this {@link ByteBuffer}.
64 * Writes as many bytes as it can, so that partial data from given array can be written
65 * if not available space for whole array is found in buffer.
66 *
67 * @param bytes
68 * bytes to write
69 *
70 * @return number of bytes that were actualy written
71 */
72 public synchronized int write(byte[] bytes) {
73 if (shouldRewindBuffer(bytes)) {
74 rewind();
75 }
76
77 int actualBytesToWrite = Math.min(bytes.length, buffer.length - writeMarker);
78
79 System.arraycopy(bytes, 0, buffer, writeMarker, actualBytesToWrite);
80
81 writeMarker += actualBytesToWrite;
82
83 return actualBytesToWrite;
84 }
85
86 /**
87 * Attempts to read given number of bytes from this {@link ByteBuffer}.
88 * If buffer currently contains less bytes than requested, this method reads only available number of bytes.
89 *
90 * @param numberOfBytes
91 * requested number of bytes to read
92 * @return actual bytes that could be read from this buffer.
93 */
94 public synchronized byte[] read(int numberOfBytes) {
95 int actualNumberOfBytes = Math.min(numberOfBytes, size());
96 byte[] result = new byte[actualNumberOfBytes];
97
98 System.arraycopy(buffer, readMarker, result, 0, result.length);
99
100 readMarker += actualNumberOfBytes;
101
102 return result;
103 }
104
105 /**
106 * Returns current size of the buffer (number of available data to read).
107 *
108 * @return size of the buffer in bytes
109 */
110 public synchronized int size() {
111 return writeMarker - readMarker;
112 }
113
114 /**
115 * Returns max capacity of the buffer (number of total data that can be stored in the buffer).
116 *
117 * @return capacity of the buffer in bytes
118 */
119 public synchronized int capacity() {
120 return buffer.length;
121 }
122
123 /**
124 * Returns remainig space in the buffer (number of bytes that can still be written to this buffer until it gets full).
125 *
126 * @return remaining free space in this buffer
127 */
128 public synchronized int remainingSpace() {
129 return capacity() - size();
130 }
131
132 /**
133 * Check if buffer should be rewind in order to write given byte array.
134 *
135 * @param bytesToWrite
136 * byte array that we want to write to a buffer
137 * @return true if buffer can and should be rewind, false otherwise
138 */
139 private boolean shouldRewindBuffer(byte[] bytesToWrite) {
140 return bytesToWrite.length > buffer.length - writeMarker && readMarker > 0;
141 }
142
143 /**
144 * Moves all unread bytes to the beginning of the buffer, removing bytes that are already read and freeing space for new writes.
145 */
146 private void rewind() {
147 int currentSize = size();
148
149 byte[] newBuffer = new byte[capacity()];
150 System.arraycopy(buffer, readMarker, newBuffer, 0, currentSize);
151 buffer = newBuffer;
152
153 readMarker = 0;
154 writeMarker = currentSize;
155 }
156 }