1   /*
2    * RDFpro - An extensible tool for building stream-oriented RDF processing libraries.
3    * 
4    * Written in 2015 by Francesco Corcoglioniti with support by Alessio Palmero Aprosio and Marco
5    * Rospocher. Contact info on http://rdfpro.fbk.eu/
6    * 
7    * To the extent possible under law, the authors have dedicated all copyright and related and
8    * neighboring rights to this software to the public domain worldwide. This software is
9    * distributed without any warranty.
10   * 
11   * You should have received a copy of the CC0 Public Domain Dedication along with this software.
12   * If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.
13   */
14  package eu.fbk.rdfpro.util;
15  
16  import java.util.Arrays;
17  
18  abstract class Buffer {
19  
20      public static Buffer newFixedBuffer(final byte[] bytes) {
21          return new FixedBuffer(bytes);
22      }
23  
24      public static Buffer newResizableBuffer() {
25          return new ResizableBuffer();
26      }
27  
28      public abstract byte read(long offset);
29  
30      public abstract short readShort(final long offset);
31  
32      public abstract int readInt(long offset);
33  
34      public abstract long readNumber(final long offset, final int length);
35  
36      public abstract String readString(final long offset, final int length);
37  
38      public abstract void write(final long offset, final byte b);
39  
40      public abstract void writeShort(final long offset, final short n);
41  
42      public abstract void writeInt(final long offset, final int n);
43  
44      public abstract void writeLong(final long offset, final long n);
45  
46      public abstract void writeNumber(final long offset, final int length, final long n);
47  
48      public abstract void writeBytes(long offset, final byte[] bytes, int index, int length);
49  
50      public abstract void writeBuffer(long offset, final Buffer buffer, long bufferOffset,
51              long length);
52  
53      public abstract int writeString(long offset, String s);
54  
55      public abstract boolean equalString(final long offset, final int length, final String s);
56  
57      private static final class FixedBuffer extends Buffer {
58  
59          private final byte[] buffer;
60  
61          FixedBuffer(final byte[] buffer) {
62              this.buffer = buffer;
63          }
64  
65          @Override
66          public byte read(final long offset) {
67              return this.buffer[(int) offset];
68          }
69  
70          @Override
71          public short readShort(final long offset) {
72              final int index = (int) offset;
73              return (short) (this.buffer[index] << 8 | this.buffer[index + 1] & 0xFF);
74          }
75  
76          @Override
77          public int readInt(final long offset) {
78              final int index = (int) offset;
79              return this.buffer[index] << 24 | (this.buffer[index + 1] & 0xFF) << 16
80                      | (this.buffer[index + 2] & 0xFF) << 8 | this.buffer[index + 3] & 0xFF;
81          }
82  
83          @Override
84          public long readNumber(final long offset, final int length) {
85              int index = (int) offset;
86              long result = 0;
87              for (int i = 0; i < length; ++i) {
88                  result = result << 8 | this.buffer[index++] & 0xFFL;
89              }
90              return result;
91          }
92  
93          @Override
94          public String readString(final long offset, final int length) {
95              final StringBuilder builder = new StringBuilder();
96              int index = (int) offset;
97              int temp = 0;
98              for (int i = 0; i < length; ++i) {
99                  final int b = this.buffer[index++] & 0xFF;
100                 if (temp != 0) {
101                     if (temp < 0) {
102                         temp = b;
103                     } else {
104                         builder.append((char) (temp << 8 | b));
105                         temp = 0;
106                     }
107                 } else if (b == 0) {
108                     temp = -1;
109                 } else {
110                     builder.append((char) b);
111                 }
112             }
113             return builder.toString();
114         }
115 
116         @Override
117         public void write(final long offset, final byte b) {
118             this.buffer[(int) offset] = b;
119         }
120 
121         @Override
122         public void writeShort(final long offset, final short n) {
123             final int index = (int) offset;
124             this.buffer[index] = (byte) (n >>> 8);
125             this.buffer[index + 1] = (byte) n;
126         }
127 
128         @Override
129         public void writeInt(final long offset, final int n) {
130             final int index = (int) offset;
131             this.buffer[index] = (byte) (n >>> 24);
132             this.buffer[index + 1] = (byte) (n >>> 16);
133             this.buffer[index + 2] = (byte) (n >>> 8);
134             this.buffer[index + 3] = (byte) n;
135         }
136 
137         @Override
138         public void writeLong(final long offset, final long n) {
139             final int index = (int) offset;
140             this.buffer[index] = (byte) (n >>> 56);
141             this.buffer[index + 1] = (byte) (n >>> 48);
142             this.buffer[index + 2] = (byte) (n >>> 40);
143             this.buffer[index + 3] = (byte) (n >>> 32);
144             this.buffer[index + 4] = (byte) (n >>> 24);
145             this.buffer[index + 5] = (byte) (n >>> 16);
146             this.buffer[index + 6] = (byte) (n >>> 8);
147             this.buffer[index + 7] = (byte) n;
148         }
149 
150         @Override
151         public void writeNumber(final long offset, final int length, final long n) {
152             int index = (int) offset;
153             for (int i = 1; i <= length; ++i) {
154                 this.buffer[index++] = (byte) (n >>> (length - i << 3));
155             }
156         }
157 
158         @Override
159         public void writeBytes(final long offset, final byte[] bytes, final int index,
160                 final int length) {
161             System.arraycopy(bytes, index, this.buffer, (int) offset, length);
162         }
163 
164         @Override
165         public void writeBuffer(final long thisOffset, final Buffer buffer,
166                 final long bufferOffset, long length) {
167 
168             if (buffer instanceof FixedBuffer) {
169                 writeBytes(thisOffset, ((FixedBuffer) buffer).buffer, (int) bufferOffset,
170                         (int) length);
171             }
172 
173             final ResizableBuffer rbuffer = (ResizableBuffer) buffer;
174             int thisIndex = (int) thisOffset;
175             int bufferIndex = (int) (bufferOffset >>> 16);
176             int byteIndex = (int) bufferOffset & 0xFFFF;
177             while (true) {
178                 final int len = (int) Math.min(length, 0x10000 - byteIndex);
179                 final byte[] bytes = rbuffer.buffer(bufferIndex);
180                 System.arraycopy(bytes, byteIndex, this.buffer, thisIndex, len);
181                 length -= len;
182                 if (length == 0) {
183                     break;
184                 }
185                 thisIndex += len;
186                 byteIndex = 0;
187                 ++bufferIndex;
188             }
189         }
190 
191         @Override
192         public int writeString(final long offset, final String s) {
193             int index = (int) offset;
194             final int length = s.length();
195             int byteLength = length;
196             for (int i = 0; i < length; ++i) {
197                 final char ch = s.charAt(i);
198                 if (ch > 0 && ch <= 127) {
199                     this.buffer[index++] = (byte) ch;
200                 } else {
201                     byteLength += 2;
202                     this.buffer[index++] = 0;
203                     this.buffer[index++] = (byte) (ch >>> 8);
204                     this.buffer[index++] = (byte) ch;
205                 }
206             }
207             return byteLength;
208         }
209 
210         @Override
211         public boolean equalString(final long offset, final int length, final String s) {
212 
213             final int strLength = s.length();
214             if (strLength == 0) {
215                 return length == 0;
216             } else if (strLength > length) {
217                 return false;
218             }
219 
220             int index = (int) offset;
221             int temp = -1;
222             int strIndex = 0;
223             for (int i = 0; i < length; ++i) {
224                 final byte b = this.buffer[index++];
225                 if (temp >= 0) {
226                     if (temp == Integer.MAX_VALUE) {
227                         temp = b & 0xFF;
228                     } else {
229                         final char ch = (char) (temp << 8 | b & 0xFF);
230                         if (strIndex == strLength || s.charAt(strIndex++) != ch) {
231                             return false;
232                         }
233                         temp = -1;
234                     }
235                 } else if (b == 0) {
236                     temp = Integer.MAX_VALUE;
237                 } else {
238                     final char ch = (char) b;
239                     if (strIndex == strLength || s.charAt(strIndex++) != ch) {
240                         return false;
241                     }
242                 }
243             }
244 
245             return strIndex == strLength;
246         }
247 
248     }
249 
250     private static final class ResizableBuffer extends Buffer {
251 
252         private byte[][] buffers;
253 
254         ResizableBuffer() {
255             this.buffers = new byte[4][];
256         }
257 
258         @Override
259         public byte read(final long offset) {
260             final int bufferIndex = (int) (offset >>> 16);
261             final int byteIndex = (int) offset & 0xFFFF;
262             return buffer(bufferIndex)[byteIndex];
263         }
264 
265         @Override
266         public short readShort(final long offset) {
267             final int bufferIndex = (int) (offset >>> 16);
268             final int byteIndex = (int) offset & 0xFFFF;
269             if (byteIndex < 0x10000 - 1) {
270                 final byte[] buffer = buffer(bufferIndex);
271                 return (short) (buffer[byteIndex] << 8 | buffer[byteIndex + 1] & 0xFF);
272             } else {
273                 return (short) readNumber(offset, 2);
274             }
275         }
276 
277         @Override
278         public int readInt(final long offset) {
279             final int bufferIndex = (int) (offset >>> 16);
280             final int byteIndex = (int) offset & 0xFFFF;
281             if (byteIndex < 0x10000 - 3) {
282                 final byte[] buffer = buffer(bufferIndex);
283                 return buffer[byteIndex] << 24 | (buffer[byteIndex + 1] & 0xFF) << 16
284                         | (buffer[byteIndex + 2] & 0xFF) << 8 | buffer[byteIndex + 3] & 0xFF;
285             } else {
286                 return (int) readNumber(offset, 4);
287             }
288         }
289 
290         @Override
291         public long readNumber(final long offset, final int length) {
292             int bufferIndex = (int) (offset >>> 16);
293             int byteIndex = (int) offset & 0xFFFF;
294             byte[] buffer = buffer(bufferIndex);
295             long result = 0;
296             for (int i = 0; i < length; ++i) {
297                 result = result << 8 | buffer[byteIndex++] & 0xFFL;
298                 if (byteIndex == 0x10000) {
299                     buffer = buffer(++bufferIndex);
300                     byteIndex = 0;
301                 }
302             }
303             return result;
304         }
305 
306         @Override
307         public String readString(final long offset, final int length) {
308 
309             assert offset >= 0;
310             assert length >= 0;
311 
312             final StringBuilder builder = new StringBuilder();
313             int bufferIndex = (int) (offset >>> 16);
314             int byteIndex = (int) offset & 0xFFFF;
315             byte[] buffer = buffer(bufferIndex);
316             int temp = 0;
317             for (int i = 0; i < length; ++i) {
318                 final int b = buffer[byteIndex++] & 0xFF;
319                 if (byteIndex == 0x10000) {
320                     buffer = buffer(++bufferIndex);
321                     byteIndex = 0;
322                 }
323                 if (temp != 0) {
324                     if (temp < 0) {
325                         temp = b;
326                     } else {
327                         builder.append((char) (temp << 8 | b));
328                         temp = 0;
329                     }
330                 } else if (b == 0) {
331                     temp = -1;
332                 } else {
333                     builder.append((char) b);
334                 }
335             }
336 
337             return builder.toString();
338         }
339 
340         @Override
341         public void write(final long offset, final byte b) {
342             final int bufferIndex = (int) (offset >>> 16);
343             final int byteIndex = (int) offset & 0xFFFF;
344             final byte[] buffer = buffer(bufferIndex);
345             buffer[byteIndex] = b;
346         }
347 
348         @Override
349         public void writeShort(final long offset, final short n) {
350             final int bufferIndex = (int) (offset >>> 16);
351             final int byteIndex = (int) offset & 0xFFFF;
352             if (byteIndex < 0x10000 - 1) {
353                 final byte[] buffer = buffer(bufferIndex);
354                 buffer[byteIndex] = (byte) (n >>> 8);
355                 buffer[byteIndex + 1] = (byte) n;
356             } else {
357                 writeNumber(offset, 2, n);
358             }
359         }
360 
361         @Override
362         public void writeInt(final long offset, final int n) {
363             final int bufferIndex = (int) (offset >>> 16);
364             final int byteIndex = (int) offset & 0xFFFF;
365             if (byteIndex < 0x10000 - 3) {
366                 final byte[] buffer = buffer(bufferIndex);
367                 buffer[byteIndex] = (byte) (n >>> 24);
368                 buffer[byteIndex + 1] = (byte) (n >>> 16);
369                 buffer[byteIndex + 2] = (byte) (n >>> 8);
370                 buffer[byteIndex + 3] = (byte) n;
371             } else {
372                 writeNumber(offset, 4, n);
373             }
374         }
375 
376         @Override
377         public void writeLong(final long offset, final long n) {
378             final int bufferIndex = (int) (offset >>> 16);
379             final int byteIndex = (int) offset & 0xFFFF;
380             if (byteIndex < 0x10000 - 7) {
381                 final byte[] buffer = buffer(bufferIndex);
382                 buffer[byteIndex] = (byte) (n >>> 56);
383                 buffer[byteIndex + 1] = (byte) (n >>> 48);
384                 buffer[byteIndex + 2] = (byte) (n >>> 40);
385                 buffer[byteIndex + 3] = (byte) (n >>> 32);
386                 buffer[byteIndex + 4] = (byte) (n >>> 24);
387                 buffer[byteIndex + 5] = (byte) (n >>> 16);
388                 buffer[byteIndex + 6] = (byte) (n >>> 8);
389                 buffer[byteIndex + 7] = (byte) n;
390             } else {
391                 writeNumber(offset, 8, n);
392             }
393         }
394 
395         @Override
396         public void writeNumber(final long offset, final int length, final long n) {
397             int bufferIndex = (int) (offset >>> 16);
398             int byteIndex = (int) offset & 0xFFFF;
399             byte[] buffer = buffer(bufferIndex);
400             for (int i = 1; i <= length; ++i) {
401                 buffer[byteIndex++] = (byte) (n >>> (length - i << 3));
402                 if (byteIndex == 0x10000) {
403                     buffer = buffer(++bufferIndex);
404                     byteIndex = 0;
405                 }
406             }
407         }
408 
409         @Override
410         public void writeBytes(final long offset, final byte[] bytes, int index, int length) {
411             int bufferIndex = (int) (offset >>> 16);
412             int byteIndex = (int) offset & 0xFFFF;
413             while (true) {
414                 final int len = Math.min(length, 0x10000 - byteIndex);
415                 final byte[] buffer = buffer(bufferIndex);
416                 System.arraycopy(bytes, index, buffer, byteIndex, len);
417                 length -= len;
418                 if (length == 0) {
419                     break;
420                 }
421                 index += len;
422                 byteIndex = 0;
423                 ++bufferIndex;
424             }
425         }
426 
427         @Override
428         public void writeBuffer(final long thisOffset, final Buffer buffer,
429                 final long bufferOffset, long length) {
430 
431             if (buffer instanceof FixedBuffer) {
432                 writeBytes(thisOffset, ((FixedBuffer) buffer).buffer, (int) bufferOffset,
433                         (int) length);
434 
435             } else {
436                 int thisBufferIndex = (int) (thisOffset >>> 16);
437                 int thisByteIndex = (int) thisOffset & 0xFFFF;
438                 int otherBufferIndex = (int) (bufferOffset >>> 16);
439                 int otherByteIndex = (int) bufferOffset & 0xFFFF;
440 
441                 while (true) {
442                     final int len = (int) Math.min(length,
443                             Math.min(0x10000 - otherByteIndex, 0x10000 - thisByteIndex));
444                     final byte[] thisBuffer = buffer(thisBufferIndex);
445                     final byte[] otherBuffer = ((ResizableBuffer) buffer).buffer(otherBufferIndex);
446                     System.arraycopy(otherBuffer, otherByteIndex, thisBuffer, thisByteIndex, len);
447                     length -= len;
448                     if (length == 0) {
449                         break;
450                     }
451                     thisByteIndex += len;
452                     if (thisByteIndex == 0x10000) {
453                         ++thisBufferIndex;
454                         thisByteIndex = 0;
455                     }
456                     otherByteIndex += len;
457                     if (otherByteIndex == 0x10000) {
458                         ++otherBufferIndex;
459                         otherByteIndex = 0;
460                     }
461                 }
462             }
463         }
464 
465         @Override
466         public int writeString(final long offset, final String s) {
467 
468             assert offset >= 0L;
469             assert s != null;
470 
471             int bufferIndex = (int) (offset >>> 16);
472             int byteIndex = (int) offset & 0xFFFF;
473             byte[] buffer = buffer(bufferIndex);
474             final int length = s.length();
475             int byteLength = length;
476             for (int i = 0; i < length; ++i) {
477                 final char ch = s.charAt(i);
478                 if (ch > 0 && ch <= 127) {
479                     buffer[byteIndex++] = (byte) ch;
480                     if (byteIndex == 0x10000) {
481                         buffer = buffer(++bufferIndex);
482                         byteIndex = 0;
483                     }
484                 } else {
485                     byteLength += 2;
486                     for (final byte b : new byte[] { 0, (byte) (ch >>> 8), (byte) ch }) {
487                         buffer[byteIndex++] = b;
488                         if (byteIndex == 0x10000) {
489                             buffer = buffer(++bufferIndex);
490                             byteIndex = 0;
491                         }
492                     }
493                 }
494             }
495 
496             return byteLength;
497         }
498 
499         @Override
500         public boolean equalString(final long offset, final int length, final String s) {
501 
502             assert offset >= 0;
503             assert length >= 0;
504             assert s != null;
505 
506             final int strLength = s.length();
507             if (strLength == 0) {
508                 return length == 0;
509             } else if (strLength > length) {
510                 return false;
511             }
512 
513             int bufferIndex = (int) (offset >>> 16);
514             int byteIndex = (int) offset & 0xFFFF;
515             byte[] buffer = buffer(bufferIndex);
516             int temp = -1;
517             int strIndex = 0;
518             for (int i = 0; i < length; ++i) {
519                 final byte b = buffer[byteIndex++];
520                 if (byteIndex == 0x10000) {
521                     buffer = buffer(++bufferIndex);
522                     byteIndex = 0;
523                 }
524                 if (temp >= 0) {
525                     if (temp == Integer.MAX_VALUE) {
526                         temp = b & 0xFF;
527                     } else {
528                         final char ch = (char) (temp << 8 | b & 0xFF);
529                         if (strIndex == strLength || s.charAt(strIndex++) != ch) {
530                             return false;
531                         }
532                         temp = -1;
533                     }
534                 } else if (b == 0) {
535                     temp = Integer.MAX_VALUE;
536                 } else {
537                     final char ch = (char) b;
538                     if (strIndex == strLength || s.charAt(strIndex++) != ch) {
539                         return false;
540                     }
541                 }
542             }
543 
544             return strIndex == strLength;
545         }
546 
547         private byte[] buffer(final int index) {
548             assert index >= 0;
549             final byte[][] buffers = this.buffers;
550             if (index < buffers.length) {
551                 final byte[] buffer = buffers[index];
552                 if (buffer != null) {
553                     return buffer;
554                 }
555             }
556             return bufferHelper(index);
557         }
558 
559         private synchronized byte[] bufferHelper(final int index) {
560             if (index >= this.buffers.length) {
561                 this.buffers = Arrays.copyOf(this.buffers, this.buffers.length << 1);
562             }
563             byte[] buffer = this.buffers[index];
564             if (buffer == null) {
565                 buffer = new byte[64 * 1024];
566                 this.buffers[index] = buffer;
567             }
568             return buffer;
569         }
570 
571     }
572 
573 }