0
# Buffer Management
1
2
gRPC Core provides memory-efficient buffer abstractions for handling data in gRPC streams. These interfaces allow different transport implementations to optimize memory usage while providing a consistent API for reading and writing data.
3
4
## Capabilities
5
6
### Readable Buffer Interface
7
8
Interface for reading data from buffers in a streaming fashion.
9
10
```java { .api }
11
/**
12
* Interface for readable byte buffers
13
* Located: io.grpc.internal.ReadableBuffer
14
*/
15
interface ReadableBuffer {
16
/**
17
* Gets the number of readable bytes remaining in this buffer
18
* @return Number of bytes that can be read
19
*/
20
int readableBytes();
21
22
/**
23
* Reads a single byte as an unsigned integer (0-255)
24
* @return Unsigned byte value
25
* @throws IndexOutOfBoundsException if no bytes are readable
26
*/
27
int readUnsignedByte();
28
29
/**
30
* Reads bytes into the destination array
31
* @param dest Destination byte array
32
* @param destOffset Starting offset in destination array
33
* @param length Number of bytes to read
34
* @throws IndexOutOfBoundsException if not enough bytes are readable
35
*/
36
void readBytes(byte[] dest, int destOffset, int length);
37
38
/**
39
* Reads bytes into a newly allocated byte array
40
* @param length Number of bytes to read
41
* @return Byte array containing the read data
42
* @throws IndexOutOfBoundsException if not enough bytes are readable
43
*/
44
void readBytes(byte[] dest);
45
46
/**
47
* Reads bytes into a new readable buffer
48
* @param length Number of bytes to read
49
* @return New ReadableBuffer containing the read data
50
* @throws IndexOutOfBoundsException if not enough bytes are readable
51
*/
52
ReadableBuffer readBytes(int length);
53
54
/**
55
* Skips the specified number of bytes
56
* @param length Number of bytes to skip
57
* @throws IndexOutOfBoundsException if not enough bytes are readable
58
*/
59
void skipBytes(int length);
60
61
/**
62
* Checks if this buffer has a backing byte array
63
* @return true if hasArray() and array() are supported
64
*/
65
boolean hasArray();
66
67
/**
68
* Gets the backing byte array (if available)
69
* @return Backing byte array
70
* @throws UnsupportedOperationException if hasArray() returns false
71
*/
72
byte[] array();
73
74
/**
75
* Gets the offset in the backing array where readable data starts
76
* @return Array offset
77
* @throws UnsupportedOperationException if hasArray() returns false
78
*/
79
int arrayOffset();
80
81
/**
82
* Closes the buffer and releases any associated resources
83
*/
84
void close();
85
}
86
```
87
88
### Writable Buffer Interface
89
90
Interface for writing data to buffers.
91
92
```java { .api }
93
/**
94
* Interface for writable byte buffers
95
* Located: io.grpc.internal.WritableBuffer
96
*/
97
interface WritableBuffer {
98
/**
99
* Writes bytes from the source array to this buffer
100
* @param src Source byte array
101
* @param srcOffset Starting offset in source array
102
* @param length Number of bytes to write
103
* @throws IndexOutOfBoundsException if buffer doesn't have enough space
104
*/
105
void write(byte[] src, int srcOffset, int length);
106
107
/**
108
* Writes all bytes from the source array to this buffer
109
* @param src Source byte array
110
* @throws IndexOutOfBoundsException if buffer doesn't have enough space
111
*/
112
void write(byte[] src);
113
114
/**
115
* Writes a single byte to this buffer
116
* @param b Byte value to write
117
* @throws IndexOutOfBoundsException if buffer doesn't have enough space
118
*/
119
void write(int b);
120
121
/**
122
* Gets the number of bytes that can still be written to this buffer
123
* @return Number of writable bytes remaining
124
*/
125
int writableBytes();
126
127
/**
128
* Gets the number of bytes that have been written and can be read
129
* @return Number of readable bytes
130
*/
131
int readableBytes();
132
133
/**
134
* Releases the buffer and returns it as a ReadableBuffer
135
* After calling this method, the WritableBuffer should not be used
136
* @return ReadableBuffer containing the written data
137
*/
138
ReadableBuffer readableBuffer();
139
}
140
```
141
142
### Writable Buffer Allocator Interface
143
144
Interface for allocating writable buffers.
145
146
```java { .api }
147
/**
148
* Interface for allocating writable buffers
149
* Located: io.grpc.internal.WritableBufferAllocator
150
*/
151
interface WritableBufferAllocator {
152
/**
153
* Allocates a new writable buffer with the specified capacity hint
154
* @param capacityHint Hint for the desired buffer capacity
155
* @return New WritableBuffer instance
156
*/
157
WritableBuffer allocate(int capacityHint);
158
}
159
```
160
161
## Buffer Implementations
162
163
### Composite Readable Buffer
164
165
Implementation that combines multiple readable buffers into a single logical buffer.
166
167
```java { .api }
168
/**
169
* Composite readable buffer implementation
170
* Located: io.grpc.internal.CompositeReadableBuffer
171
*/
172
class CompositeReadableBuffer implements ReadableBuffer {
173
/**
174
* Creates a new composite buffer
175
*/
176
public CompositeReadableBuffer();
177
178
/**
179
* Adds a buffer to the end of this composite buffer
180
* @param buffer Buffer to add
181
* @throws IllegalArgumentException if buffer is null
182
*/
183
public void addBuffer(ReadableBuffer buffer);
184
185
@Override
186
public int readableBytes();
187
188
@Override
189
public int readUnsignedByte();
190
191
@Override
192
public void readBytes(byte[] dest, int destOffset, int length);
193
194
@Override
195
public void readBytes(byte[] dest);
196
197
@Override
198
public ReadableBuffer readBytes(int length);
199
200
@Override
201
public void skipBytes(int length);
202
203
@Override
204
public boolean hasArray();
205
206
@Override
207
public byte[] array();
208
209
@Override
210
public int arrayOffset();
211
212
@Override
213
public void close();
214
}
215
```
216
217
### Array-Based Buffer Implementations
218
219
Simple implementations using byte arrays as backing storage.
220
221
```java { .api }
222
/**
223
* ReadableBuffer implementation backed by a byte array
224
* Located: io.grpc.internal.ReadableBuffers
225
*/
226
class ReadableBuffers {
227
/**
228
* Creates a readable buffer wrapping the entire byte array
229
* @param bytes Byte array to wrap
230
* @return ReadableBuffer wrapping the array
231
*/
232
public static ReadableBuffer wrap(byte[] bytes);
233
234
/**
235
* Creates a readable buffer wrapping part of a byte array
236
* @param bytes Byte array to wrap
237
* @param offset Starting offset in the array
238
* @param length Number of bytes to include
239
* @return ReadableBuffer wrapping the specified array section
240
*/
241
public static ReadableBuffer wrap(byte[] bytes, int offset, int length);
242
243
/**
244
* Creates an empty readable buffer
245
* @return Empty ReadableBuffer
246
*/
247
public static ReadableBuffer empty();
248
}
249
250
/**
251
* WritableBuffer implementation backed by a byte array
252
* Located: io.grpc.internal.WritableBuffers
253
*/
254
class WritableBuffers {
255
/**
256
* Creates a writable buffer with the specified initial capacity
257
* @param capacity Initial capacity in bytes
258
* @return WritableBuffer with the specified capacity
259
*/
260
public static WritableBuffer allocate(int capacity);
261
}
262
```
263
264
## Buffer Usage Patterns
265
266
### Reading Data from Streams
267
268
```java
269
import io.grpc.internal.ReadableBuffer;
270
import io.grpc.internal.CompositeReadableBuffer;
271
272
// Reading from a single buffer
273
ReadableBuffer buffer = getMessageBuffer();
274
byte[] messageData = new byte[buffer.readableBytes()];
275
buffer.readBytes(messageData);
276
buffer.close();
277
278
// Reading from composite buffer (multiple chunks)
279
CompositeReadableBuffer composite = new CompositeReadableBuffer();
280
composite.addBuffer(chunk1);
281
composite.addBuffer(chunk2);
282
composite.addBuffer(chunk3);
283
284
// Read the entire composite as one logical buffer
285
byte[] fullMessage = new byte[composite.readableBytes()];
286
composite.readBytes(fullMessage);
287
composite.close();
288
```
289
290
### Writing Data to Streams
291
292
```java
293
import io.grpc.internal.WritableBuffer;
294
import io.grpc.internal.WritableBufferAllocator;
295
296
// Allocate buffer for writing
297
WritableBufferAllocator allocator = getBufferAllocator();
298
WritableBuffer buffer = allocator.allocate(1024);
299
300
// Write data to buffer
301
byte[] messageData = serializeMessage(message);
302
buffer.write(messageData);
303
304
// Convert to readable buffer for transmission
305
ReadableBuffer readable = buffer.readableBuffer();
306
sendBuffer(readable);
307
```
308
309
### Custom Buffer Allocator
310
311
```java
312
import io.grpc.internal.WritableBufferAllocator;
313
import io.grpc.internal.WritableBuffer;
314
315
// Custom allocator implementation
316
public class PooledBufferAllocator implements WritableBufferAllocator {
317
private final BufferPool pool;
318
319
public PooledBufferAllocator(BufferPool pool) {
320
this.pool = pool;
321
}
322
323
@Override
324
public WritableBuffer allocate(int capacityHint) {
325
// Get buffer from pool based on capacity hint
326
return pool.borrowBuffer(capacityHint);
327
}
328
}
329
```
330
331
## Memory Management
332
333
### Buffer Lifecycle
334
335
1. **Allocation**: Buffers are allocated through allocator interfaces
336
2. **Writing**: Data is written to WritableBuffer instances
337
3. **Conversion**: WritableBuffers are converted to ReadableBuffers
338
4. **Reading**: Data is read from ReadableBuffer instances
339
5. **Cleanup**: Buffers are closed to release resources
340
341
### Resource Management Best Practices
342
343
```java
344
// Always close buffers to prevent memory leaks
345
ReadableBuffer buffer = null;
346
try {
347
buffer = receiveBuffer();
348
processBuffer(buffer);
349
} finally {
350
if (buffer != null) {
351
buffer.close();
352
}
353
}
354
355
// Or use try-with-resources if buffer implements AutoCloseable
356
try (ReadableBuffer buffer = receiveBuffer()) {
357
processBuffer(buffer);
358
}
359
```
360
361
### Performance Considerations
362
363
- **Buffer Reuse**: Implement buffer pooling for high-throughput scenarios
364
- **Copy Avoidance**: Use `hasArray()` to access backing arrays directly when possible
365
- **Composite Buffers**: Use for scatter-gather operations to avoid copying
366
- **Capacity Hints**: Provide accurate capacity hints to reduce allocations
367
- **Early Release**: Close buffers as soon as reading is complete
368
369
## Error Handling
370
371
Buffer operations handle errors through:
372
373
- **IndexOutOfBoundsException**: Thrown when reading beyond buffer capacity
374
- **IllegalArgumentException**: Thrown for invalid parameters (null buffers, negative lengths)
375
- **UnsupportedOperationException**: Thrown when operations are not supported (e.g., `array()` when `hasArray()` is false)
376
- **Resource Cleanup**: Buffers implement proper cleanup in `close()` methods
377
- **State Validation**: Operations check buffer state and throw appropriate exceptions