Improve segmented journal actor metrics
[controller.git] / atomix-storage / src / main / java / io / atomix / utils / serializer / Kryo505ByteBufferInput.java
1 /* Copyright (c) 2008, Nathan Sweet
2  * All rights reserved.
3  * 
4  * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following
5  * conditions are met:
6  * 
7  * - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
8  * - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following
9  * disclaimer in the documentation and/or other materials provided with the distribution.
10  * - Neither the name of Esoteric Software nor the names of its contributors may be used to endorse or promote products derived
11  * from this software without specific prior written permission.
12  * 
13  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
14  * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
15  * SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
16  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
17  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
18  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
19
20 package io.atomix.utils.serializer;
21
22 import com.esotericsoftware.kryo.io.ByteBufferInput;
23 import java.nio.ByteBuffer;
24
25 /**
26  * A Kryo-4.0.3 ByteBufferInput adapted to deal with
27  * <a href="https://github.com/EsotericSoftware/kryo/issues/505">issue 505</a>.
28  *
29  * @author Roman Levenstein &lt;romixlev@gmail.com&gt;
30  * @author Robert Varga
31  */
32 public final class Kryo505ByteBufferInput extends ByteBufferInput {
33         Kryo505ByteBufferInput (ByteBuffer buffer) {
34                 super(buffer);
35         }
36
37         @Override
38         public String readString () {
39                 niobuffer.position(position);
40                 int available = require(1);
41                 position++;
42                 int b = niobuffer.get();
43                 if ((b & 0x80) == 0) return readAscii(); // ASCII.
44                 // Null, empty, or UTF8.
45                 int charCount = available >= 5 ? readUtf8Length(b) : readUtf8Length_slow(b);
46                 switch (charCount) {
47                 case 0:
48                         return null;
49                 case 1:
50                         return "";
51                 }
52                 charCount--;
53                 if (chars.length < charCount) chars = new char[charCount];
54                 readUtf8(charCount);
55                 return new String(chars, 0, charCount);
56         }
57
58         private int readUtf8Length (int b) {
59                 int result = b & 0x3F; // Mask all but first 6 bits.
60                 if ((b & 0x40) != 0) { // Bit 7 means another byte, bit 8 means UTF8.
61                         position++;
62                         b = niobuffer.get();
63                         result |= (b & 0x7F) << 6;
64                         if ((b & 0x80) != 0) {
65                                 position++;
66                                 b = niobuffer.get();
67                                 result |= (b & 0x7F) << 13;
68                                 if ((b & 0x80) != 0) {
69                                         position++;
70                                         b = niobuffer.get();
71                                         result |= (b & 0x7F) << 20;
72                                         if ((b & 0x80) != 0) {
73                                                 position++;
74                                                 b = niobuffer.get();
75                                                 result |= (b & 0x7F) << 27;
76                                         }
77                                 }
78                         }
79                 }
80                 return result;
81         }
82
83         private int readUtf8Length_slow (int b) {
84                 int result = b & 0x3F; // Mask all but first 6 bits.
85                 if ((b & 0x40) != 0) { // Bit 7 means another byte, bit 8 means UTF8.
86                         require(1);
87                         position++;
88                         b = niobuffer.get();
89                         result |= (b & 0x7F) << 6;
90                         if ((b & 0x80) != 0) {
91                                 require(1);
92                                 position++;
93                                 b = niobuffer.get();
94                                 result |= (b & 0x7F) << 13;
95                                 if ((b & 0x80) != 0) {
96                                         require(1);
97                                         position++;
98                                         b = niobuffer.get();
99                                         result |= (b & 0x7F) << 20;
100                                         if ((b & 0x80) != 0) {
101                                                 require(1);
102                                                 position++;
103                                                 b = niobuffer.get();
104                                                 result |= (b & 0x7F) << 27;
105                                         }
106                                 }
107                         }
108                 }
109                 return result;
110         }
111
112         private void readUtf8 (int charCount) {
113                 char[] chars = this.chars;
114                 // Try to read 7 bit ASCII chars.
115                 int charIndex = 0;
116                 int count = Math.min(require(1), charCount);
117                 int position = this.position;
118                 int b;
119                 while (charIndex < count) {
120                         position++;
121                         b = niobuffer.get();
122                         if (b < 0) {
123                                 position--;
124                                 break;
125                         }
126                         chars[charIndex++] = (char)b;
127                 }
128                 this.position = position;
129                 // If buffer didn't hold all chars or any were not ASCII, use slow path for remainder.
130                 if (charIndex < charCount) {
131                         niobuffer.position(position);
132                         readUtf8_slow(charCount, charIndex);
133                 }
134         }
135
136         private void readUtf8_slow (int charCount, int charIndex) {
137                 char[] chars = this.chars;
138                 while (charIndex < charCount) {
139                         if (position == limit) require(1);
140                         position++;
141                         int b = niobuffer.get() & 0xFF;
142                         switch (b >> 4) {
143                         case 0:
144                         case 1:
145                         case 2:
146                         case 3:
147                         case 4:
148                         case 5:
149                         case 6:
150                         case 7:
151                                 chars[charIndex] = (char)b;
152                                 break;
153                         case 12:
154                         case 13:
155                                 if (position == limit) require(1);
156                                 position++;
157                                 chars[charIndex] = (char)((b & 0x1F) << 6 | niobuffer.get() & 0x3F);
158                                 break;
159                         case 14:
160                                 require(2);
161                                 position += 2;
162                                 int b2 = niobuffer.get();
163                                 int b3 = niobuffer.get();
164                                 chars[charIndex] = (char)((b & 0x0F) << 12 | (b2 & 0x3F) << 6 | b3 & 0x3F);
165                                 break;
166                         }
167                         charIndex++;
168                 }
169         }
170
171         private String readAscii () {
172                 int end = position;
173                 int start = end - 1;
174                 int limit = this.limit;
175                 int b;
176                 do {
177                         if (end == limit) return readAscii_slow();
178                         end++;
179                         b = niobuffer.get();
180                 } while ((b & 0x80) == 0);
181                 int count = end - start;
182                 byte[] tmp = new byte[count];
183                 niobuffer.position(start);
184                 niobuffer.get(tmp);
185                 tmp[count - 1] &= 0x7F;  // Mask end of ascii bit.
186                 String value = new String(tmp, 0, 0, count);
187                 position = end;
188                 niobuffer.position(position);
189                 return value;
190         }
191
192         private String readAscii_slow () {
193                 position--; // Re-read the first byte.
194                 // Copy chars currently in buffer.
195                 int charCount = limit - position;
196                 if (charCount > chars.length) chars = new char[charCount * 2];
197                 char[] chars = this.chars;
198                 for (int i = position, ii = 0, n = limit; i < n; i++, ii++)
199                         chars[ii] = (char)niobuffer.get(i);
200                 position = limit;
201                 // Copy additional chars one by one.
202                 while (true) {
203                         require(1);
204                         position++;
205                         int b = niobuffer.get();
206                         if (charCount == chars.length) {
207                                 char[] newChars = new char[charCount * 2];
208                                 System.arraycopy(chars, 0, newChars, 0, charCount);
209                                 chars = newChars;
210                                 this.chars = newChars;
211                         }
212                         if ((b & 0x80) == 0x80) {
213                                 chars[charCount++] = (char)(b & 0x7F);
214                                 break;
215                         }
216                         chars[charCount++] = (char)b;
217                 }
218                 return new String(chars, 0, charCount);
219         }
220
221         @Override
222         public StringBuilder readStringBuilder () {
223                 niobuffer.position(position);
224                 int available = require(1);
225                 position++;
226                 int b = niobuffer.get();
227                 if ((b & 0x80) == 0) return new StringBuilder(readAscii()); // ASCII.
228                 // Null, empty, or UTF8.
229                 int charCount = available >= 5 ? readUtf8Length(b) : readUtf8Length_slow(b);
230                 switch (charCount) {
231                 case 0:
232                         return null;
233                 case 1:
234                         return new StringBuilder("");
235                 }
236                 charCount--;
237                 if (chars.length < charCount) chars = new char[charCount];
238                 readUtf8(charCount);
239                 StringBuilder builder = new StringBuilder(charCount);
240                 builder.append(chars, 0, charCount);
241                 return builder;
242         }
243 }