| | 1 | | // Copyright (c) ZeroC, Inc. |
| | 2 | |
|
| | 3 | | using System.Diagnostics; |
| | 4 | |
|
| | 5 | | namespace Ice.Internal; |
| | 6 | |
|
| | 7 | | // An instance of ByteBuffer cannot grow beyond its initial capacity. |
| | 8 | | // This class wraps a ByteBuffer and supports reallocation. |
| | 9 | | public class Buffer |
| | 10 | | { |
| | 11 | | public Buffer() |
| 1 | 12 | | : this(ByteBuffer.ByteOrder.LittleEndian) |
| | 13 | | { |
| 1 | 14 | | } |
| | 15 | |
|
| 1 | 16 | | public Buffer(ByteBuffer.ByteOrder order) |
| | 17 | | { |
| 1 | 18 | | b = _emptyBuffer; |
| 1 | 19 | | _size = 0; |
| 1 | 20 | | _capacity = 0; |
| 1 | 21 | | _order = order; |
| 1 | 22 | | } |
| | 23 | |
|
| | 24 | | public Buffer(byte[] data) |
| 1 | 25 | | : this(data, ByteBuffer.ByteOrder.LittleEndian) |
| | 26 | | { |
| 1 | 27 | | } |
| | 28 | |
|
| 1 | 29 | | public Buffer(byte[] data, ByteBuffer.ByteOrder order) |
| | 30 | | { |
| 1 | 31 | | b = ByteBuffer.wrap(data); |
| 1 | 32 | | b.order(order); |
| 1 | 33 | | _size = data.Length; |
| 1 | 34 | | _capacity = b.capacity(); |
| 1 | 35 | | _order = order; |
| 1 | 36 | | } |
| | 37 | |
|
| | 38 | | public Buffer(ByteBuffer data) |
| 0 | 39 | | : this(data, ByteBuffer.ByteOrder.LittleEndian) |
| | 40 | | { |
| 0 | 41 | | } |
| | 42 | |
|
| 0 | 43 | | public Buffer(ByteBuffer data, ByteBuffer.ByteOrder order) |
| | 44 | | { |
| 0 | 45 | | b = data; |
| 0 | 46 | | b.order(order); |
| 0 | 47 | | _size = data.remaining(); |
| 0 | 48 | | _capacity = 0; |
| 0 | 49 | | _order = order; |
| 0 | 50 | | } |
| | 51 | |
|
| 1 | 52 | | public Buffer(Buffer buf, bool adopt) |
| | 53 | | { |
| 1 | 54 | | b = buf.b; |
| 1 | 55 | | _size = buf._size; |
| 1 | 56 | | _capacity = buf._capacity; |
| 1 | 57 | | _shrinkCounter = buf._shrinkCounter; |
| 1 | 58 | | _order = buf._order; |
| | 59 | |
|
| 1 | 60 | | if (adopt) |
| | 61 | | { |
| 1 | 62 | | buf.clear(); |
| | 63 | | } |
| 1 | 64 | | } |
| | 65 | |
|
| 1 | 66 | | public int size() => _size; |
| | 67 | |
|
| 1 | 68 | | public bool empty() => _size == 0; |
| | 69 | |
|
| | 70 | | public void clear() |
| | 71 | | { |
| 1 | 72 | | b = _emptyBuffer; |
| 1 | 73 | | _size = 0; |
| 1 | 74 | | _capacity = 0; |
| 1 | 75 | | _shrinkCounter = 0; |
| 1 | 76 | | } |
| | 77 | |
|
| | 78 | | // |
| | 79 | | // Call expand(n) to add room for n additional bytes. Note that expand() |
| | 80 | | // examines the current position of the buffer first; we don't want to |
| | 81 | | // expand the buffer if the caller is writing to a location that is |
| | 82 | | // already in the buffer. |
| | 83 | | // |
| | 84 | | public void expand(int n) |
| | 85 | | { |
| 1 | 86 | | int sz = (b == _emptyBuffer) ? n : b.position() + n; |
| 1 | 87 | | if (sz > _size) |
| | 88 | | { |
| 1 | 89 | | resize(sz, false); |
| | 90 | | } |
| 1 | 91 | | } |
| | 92 | |
|
| | 93 | | public void resize(int n, bool reading) |
| | 94 | | { |
| | 95 | | Debug.Assert(b == _emptyBuffer || _capacity > 0); |
| | 96 | |
|
| 1 | 97 | | if (n == 0) |
| | 98 | | { |
| 1 | 99 | | clear(); |
| | 100 | | } |
| 1 | 101 | | else if (n > _capacity) |
| | 102 | | { |
| 1 | 103 | | reserve(n); |
| | 104 | | } |
| 1 | 105 | | _size = n; |
| | 106 | |
|
| | 107 | | // |
| | 108 | | // When used for reading, we want to set the buffer's limit to the new size. |
| | 109 | | // |
| 1 | 110 | | if (reading) |
| | 111 | | { |
| 1 | 112 | | b.limit(_size); |
| | 113 | | } |
| 1 | 114 | | } |
| | 115 | |
|
| | 116 | | public void reset() |
| | 117 | | { |
| 1 | 118 | | if (_size > 0 && _size * 2 < _capacity) |
| | 119 | | { |
| | 120 | | // |
| | 121 | | // If the current buffer size is smaller than the |
| | 122 | | // buffer capacity, we shrink the buffer memory to the |
| | 123 | | // current size. This is to avoid holding on to too much |
| | 124 | | // memory if it's not needed anymore. |
| | 125 | | // |
| 1 | 126 | | if (++_shrinkCounter > 2) |
| | 127 | | { |
| 1 | 128 | | reserve(_size); |
| 1 | 129 | | _shrinkCounter = 0; |
| | 130 | | } |
| | 131 | | } |
| | 132 | | else |
| | 133 | | { |
| 1 | 134 | | _shrinkCounter = 0; |
| | 135 | | } |
| 1 | 136 | | _size = 0; |
| 1 | 137 | | if (b != _emptyBuffer) |
| | 138 | | { |
| 1 | 139 | | b.limit(b.capacity()); |
| 1 | 140 | | b.position(0); |
| | 141 | | } |
| 1 | 142 | | } |
| | 143 | |
|
| | 144 | | private void reserve(int n) |
| | 145 | | { |
| | 146 | | Debug.Assert(_capacity == b.capacity()); |
| | 147 | |
|
| 1 | 148 | | if (n > _capacity) |
| | 149 | | { |
| 1 | 150 | | _capacity = System.Math.Max(n, 2 * _capacity); |
| 1 | 151 | | _capacity = System.Math.Max(240, _capacity); |
| | 152 | | } |
| 1 | 153 | | else if (n < _capacity) |
| | 154 | | { |
| 1 | 155 | | _capacity = n; |
| | 156 | | } |
| | 157 | | else |
| | 158 | | { |
| 0 | 159 | | return; |
| | 160 | | } |
| | 161 | |
|
| | 162 | | try |
| | 163 | | { |
| 1 | 164 | | var buf = ByteBuffer.allocate(_capacity); |
| | 165 | |
|
| 1 | 166 | | if (b == _emptyBuffer) |
| | 167 | | { |
| 1 | 168 | | b = buf; |
| | 169 | | } |
| | 170 | | else |
| | 171 | | { |
| 1 | 172 | | int pos = b.position(); |
| 1 | 173 | | b.position(0); |
| 1 | 174 | | b.limit(System.Math.Min(_capacity, b.capacity())); |
| 1 | 175 | | buf.put(b); |
| 1 | 176 | | b = buf; |
| 1 | 177 | | b.limit(b.capacity()); |
| 1 | 178 | | b.position(pos); |
| | 179 | | } |
| | 180 | |
|
| 1 | 181 | | b.order(_order); |
| 1 | 182 | | } |
| 0 | 183 | | catch (System.OutOfMemoryException) |
| | 184 | | { |
| 0 | 185 | | _capacity = b.capacity(); // Restore the previous capacity |
| 0 | 186 | | throw; |
| | 187 | | } |
| 0 | 188 | | catch (System.Exception ex) |
| | 189 | | { |
| 0 | 190 | | _capacity = b.capacity(); // Restore the previous capacity. |
| 0 | 191 | | throw new MarshalException("unexpected exception while trying to allocate a ByteBuffer", ex); |
| | 192 | | } |
| | 193 | | finally |
| | 194 | | { |
| | 195 | | Debug.Assert(_capacity == b.capacity()); |
| 1 | 196 | | } |
| 1 | 197 | | } |
| | 198 | |
|
| | 199 | | public ByteBuffer b; |
| | 200 | | // Sentinel used for null buffer. |
| 1 | 201 | | private static readonly ByteBuffer _emptyBuffer = new(); |
| | 202 | |
|
| | 203 | | private int _size; |
| | 204 | | private int _capacity; // Cache capacity to avoid excessive method calls. |
| | 205 | | private int _shrinkCounter; |
| | 206 | | private readonly ByteBuffer.ByteOrder _order; |
| | 207 | | } |