View Javadoc
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 }