1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27 package org.apache.hc.client5.http.utils;
28
29 import java.nio.ByteBuffer;
30 import java.nio.CharBuffer;
31 import java.nio.charset.CharacterCodingException;
32 import java.nio.charset.Charset;
33 import java.nio.charset.CharsetEncoder;
34 import java.nio.charset.CoderResult;
35 import java.nio.charset.CodingErrorAction;
36 import java.nio.charset.StandardCharsets;
37
38
39
40
41
42
43 public final class ByteArrayBuilder {
44
45 private CharsetEncoder charsetEncoder;
46 private ByteBuffer buffer;
47
48 public ByteArrayBuilder() {
49 }
50
51 public ByteArrayBuilder(final int initialCapacity) {
52 this.buffer = ByteBuffer.allocate(initialCapacity);
53 }
54
55 public int capacity() {
56 return this.buffer != null ? this.buffer.capacity() : 0;
57 }
58
59 static ByteBuffer ensureFreeCapacity(final ByteBuffer buffer, final int capacity) {
60 if (buffer == null) {
61 return ByteBuffer.allocate(capacity);
62 }
63 if (buffer.remaining() < capacity) {
64 final ByteBuffer newBuffer = ByteBuffer.allocate(buffer.position() + capacity);
65 buffer.flip();
66 newBuffer.put(buffer);
67 return newBuffer;
68 }
69 return buffer;
70 }
71
72 static ByteBuffer encode(
73 final ByteBuffer buffer, final CharBuffer in, final CharsetEncoder encoder) throws CharacterCodingException {
74
75 final int capacity = (int) (in.remaining() * encoder.averageBytesPerChar());
76 ByteBuffer out = ensureFreeCapacity(buffer, capacity);
77 while (in.hasRemaining()) {
78 CoderResult result = encoder.encode(in, out, true);
79 if (result.isError()) {
80 result.throwException();
81 }
82 if (result.isUnderflow()) {
83 result = encoder.flush(out);
84 }
85 if (result.isUnderflow()) {
86 break;
87 }
88 if (result.isOverflow()) {
89 out = ensureFreeCapacity(out, capacity);
90 }
91 }
92 return out;
93 }
94
95 public void ensureFreeCapacity(final int freeCapacity) {
96 this.buffer = ensureFreeCapacity(this.buffer, freeCapacity);
97 }
98
99 private void doAppend(final CharBuffer charBuffer) {
100 if (this.charsetEncoder == null) {
101 this.charsetEncoder = StandardCharsets.US_ASCII.newEncoder()
102 .onMalformedInput(CodingErrorAction.IGNORE)
103 .onUnmappableCharacter(CodingErrorAction.REPLACE);
104 }
105 this.charsetEncoder.reset();
106 try {
107 this.buffer = encode(this.buffer, charBuffer, this.charsetEncoder);
108 } catch (final CharacterCodingException ex) {
109
110 throw new IllegalStateException("Unexpected character coding error", ex);
111 }
112 }
113
114 public ByteArrayBuilder charset(final Charset charset) {
115 if (charset == null) {
116 this.charsetEncoder = null;
117 } else {
118 this.charsetEncoder = charset.newEncoder()
119 .onMalformedInput(CodingErrorAction.IGNORE)
120 .onUnmappableCharacter(CodingErrorAction.REPLACE);
121 }
122 return this;
123 }
124
125 public ByteArrayBuilder append(final byte[] b, final int off, final int len) {
126 if (b == null) {
127 return this;
128 }
129 if ((off < 0) || (off > b.length) || (len < 0) ||
130 ((off + len) < 0) || ((off + len) > b.length)) {
131 throw new IndexOutOfBoundsException("off: " + off + " len: " + len + " b.length: " + b.length);
132 }
133 ensureFreeCapacity(len);
134 this.buffer.put(b, off, len);
135 return this;
136 }
137
138 public ByteArrayBuilder append(final byte[] b) {
139 if (b == null) {
140 return this;
141 }
142 return append(b, 0, b.length);
143 }
144
145 public ByteArrayBuilder append(final CharBuffer charBuffer) {
146 if (charBuffer == null) {
147 return this;
148 }
149 doAppend(charBuffer);
150 return this;
151 }
152
153 public ByteArrayBuilder append(final char[] b, final int off, final int len) {
154 if (b == null) {
155 return this;
156 }
157 if ((off < 0) || (off > b.length) || (len < 0) ||
158 ((off + len) < 0) || ((off + len) > b.length)) {
159 throw new IndexOutOfBoundsException("off: " + off + " len: " + len + " b.length: " + b.length);
160 }
161 return append(CharBuffer.wrap(b, off, len));
162 }
163
164 public ByteArrayBuilder append(final char[] b) {
165 if (b == null) {
166 return this;
167 }
168 return append(b, 0, b.length);
169 }
170
171 public ByteArrayBuilder append(final String s) {
172 if (s == null) {
173 return this;
174 }
175 return append(CharBuffer.wrap(s));
176 }
177
178 public ByteBuffer toByteBuffer() {
179 return this.buffer != null ? this.buffer.duplicate() : ByteBuffer.allocate(0);
180 }
181
182 public byte[] toByteArray() {
183 if (this.buffer == null) {
184 return new byte[] {};
185 }
186 this.buffer.flip();
187 final byte[] b = new byte[this.buffer.remaining()];
188 this.buffer.get(b);
189 this.buffer.clear();
190 return b;
191 }
192
193 public void reset() {
194 if (this.charsetEncoder != null) {
195 this.charsetEncoder.reset();
196 }
197 if (this.buffer != null) {
198 this.buffer.clear();
199 }
200 }
201
202 @Override
203 public String toString() {
204 return this.buffer != null ? this.buffer.toString() : "null";
205 }
206
207 }