Skip to content

Commit 71c3a9e

Browse files
committed
0.5.1: more utils and optimizations
1 parent 86b6ae3 commit 71c3a9e

File tree

6 files changed

+222
-15
lines changed

6 files changed

+222
-15
lines changed

gradle.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
group = com.trivago
22

3-
version = 0.5
3+
version = 0.5.1
44

55
org.gradle.jvmargs = -ea -Dfile.encoding=UTF-8 -XX:+UseG1GC -Xms512m -Xmx1G -Djava.net.preferIPv4Stack=true
66
systemProp.file.encoding = UTF-8

src/main/java/com/trivago/fastutilconcurrentwrapper/io/BAIS.java

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import org.jspecify.annotations.Nullable;
99

1010
import java.io.ByteArrayInputStream;
11+
import java.io.ByteArrayOutputStream;
1112
import java.io.DataInputStream;
1213
import java.io.IOException;
1314
import java.io.ObjectInput;
@@ -16,6 +17,8 @@
1617
import java.util.PrimitiveIterator;
1718
import java.util.UUID;
1819

20+
import static java.nio.charset.StandardCharsets.*;
21+
1922
/**
2023
@see java.io.ByteArrayInputStream
2124
@@ -39,8 +42,8 @@ public BAIS (byte[] array, @PositiveOrZero int offset, @PositiveOrZero int maxLe
3942

4043
public BAIS (byte[] array){ super(array); }//new
4144

42-
public BAIS (BAOS baos) {
43-
super(baos.array(), 0, baos.size());
45+
public BAIS (ByteArrayOutputStream baos) {
46+
super(baos instanceof BAOS us ? us.array() : baos.toByteArray(), 0, baos.size());
4447
}//new
4548

4649
/// The array backing the input stream. capacity = array().length
@@ -280,15 +283,23 @@ public Object readObject () throws ClassNotFoundException, IOException {
280283
/// @see BAOS#writeBytes(String)
281284
/// @see java.nio.charset.StandardCharsets#ISO_8859_1
282285
public String readLatin1String (@PositiveOrZero int strLen) {
286+
strLen = Math.min(strLen, available());
283287
String s = new String(buf, 0, pos, strLen);
284288
pos += strLen;
285289
return s;
286290
}
287291

292+
/// @see java.nio.charset.StandardCharsets#UTF_16BE
293+
public String readUTF16String (@PositiveOrZero int strLen) {
294+
int byteLen = Math.min(strLen << 1, available());//*2
295+
String s = new String(buf, pos, byteLen, UTF_16BE);
296+
pos += byteLen;
297+
return s;
298+
}
299+
288300
@Override
289301
public boolean hasNext () {
290-
// available() > 0 == (length - position) > 0
291-
return pos < count;
302+
return pos < count;// position < length: available() > 0 == (length - position) > 0
292303
}
293304

294305
@Override

src/main/java/com/trivago/fastutilconcurrentwrapper/io/BAOS.java

Lines changed: 59 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919
import java.util.Objects;
2020
import java.util.UUID;
2121

22+
import static java.nio.charset.StandardCharsets.*;
23+
2224
/**
2325
Simple, fast byte-array output stream that exposes the backing array.
2426
@@ -80,6 +82,24 @@ public BAOS (byte[] a) {
8082
buf = a;
8183
}//new
8284

85+
public BAOS (byte[] a, @PositiveOrZero int length) {
86+
super(0);
87+
buf = a;
88+
count = Math.min(length, a.length);
89+
}//new
90+
91+
public BAOS (ByteArrayOutputStream baos) {
92+
super(0);
93+
count = baos.size();
94+
if (baos instanceof BAOS us){
95+
buf = us.array();
96+
position = us.writerIndex();
97+
} else {
98+
buf = baos.toByteArray();
99+
position = count;
100+
}
101+
}//new ~ clone
102+
83103
/// Marks this array output stream as empty.
84104
@Override public void reset (){ count = 0; position = 0; }
85105

@@ -318,23 +338,23 @@ public void writeObject(Object obj) throws IOException {
318338

319339
@Override
320340
public Appendable append (CharSequence csq) {
321-
int len;
322-
if (csq != null && (len = csq.length()) > 0)
323-
append(csq, 0, len);
341+
append(csq, 0, csq.length());
324342
return this;
325343
}
326344

327345
/// @see #write(byte[], int, int)
328346
/// @see #writeBytes(byte[])
329347
@Override
330348
public Appendable append (CharSequence csq, int start, int end) {
331-
int len = end - start;
349+
final int len = end - start;
332350
Objects.checkFromIndexSize(start, len, csq.length());
333-
if (position + len > buf.length)
334-
buf = ByteArrays.grow(buf, position + len, position);
335-
for (int i = 0; i < len; i++)
336-
buf[position + i] = (byte) csq.charAt(start + i);
337-
position += len;
351+
final int len2 = len << 1;
352+
if (position + len2 > buf.length)
353+
buf = ByteArrays.grow(buf, position + len2, position);
354+
355+
JBytes.DirectByteArrayAccess.copyCharsToByteArray(csq, start, buf, position, len);
356+
357+
position += len2;
338358
if (count < position) count = position;
339359
return this;
340360
}
@@ -348,4 +368,34 @@ public Appendable append (char c) {
348368
if (count < position) count = position;
349369
return this;
350370
}
371+
372+
@Override
373+
public boolean equals (Object obj) {
374+
if (this == obj){
375+
return true;
376+
} else if (obj instanceof BAOS baos){
377+
return java.util.Arrays.equals(buf,0,count, baos.array(),0,baos.size());
378+
} else if (obj instanceof ByteArrayOutputStream baos){
379+
return java.util.Arrays.equals(buf,0,count, baos.toByteArray(),0,baos.size());
380+
} else if (obj instanceof byte[] a){
381+
return java.util.Arrays.equals(buf,0,count, a,0,a.length);
382+
} else if (obj instanceof CharSequence cs){
383+
byte[] a = cs.toString().getBytes(UTF_16BE);
384+
return java.util.Arrays.equals(buf,0,count, a,0,a.length);
385+
} else {
386+
return false;
387+
}
388+
}
389+
390+
/// @see java.util.Arrays#hashCode(byte[])
391+
/// @see jdk.internal.util.ArraysSupport#hashCode(int, byte[], int, int)
392+
/// @see java.lang.String#hashCode
393+
@Override
394+
public int hashCode () {
395+
int result = 1;
396+
for (int i = 0; i < count; i++){
397+
result = 31 * result + buf[i];
398+
}
399+
return result;
400+
}
351401
}

src/main/java/com/trivago/fastutilconcurrentwrapper/util/JBytes.java

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,7 @@ public static long bytesToLong (byte[] bytes) {
225225
* @see java.io.DataInputStream
226226
* @see jdk.internal.util.ByteArray
227227
* @see jdk.internal.util.ByteArrayLittleEndian
228+
* @see com.dynatrace.hash4j.internal.ByteArrayUtil
228229
*/
229230
@NoArgsConstructor(access = AccessLevel.PRIVATE)
230231
public static final class DirectByteArrayAccess {
@@ -601,5 +602,68 @@ public static void setDoubleRaw(byte[] array, int offset, double value) {
601602
private static VarHandle create(Class<?> viewArrayClass) {
602603
return MethodHandles.byteArrayViewVarHandle(viewArrayClass, ByteOrder.BIG_ENDIAN);
603604
}
605+
606+
/**
607+
* Reads a {@code long} value from a {@link CharSequence} with given offset.
608+
*
609+
* @param cs a char sequence
610+
* @param off an offset
611+
* @return the value
612+
*/
613+
public static long getLong (CharSequence cs, int off) {
614+
return ((long) cs.charAt(off) << 48)
615+
| ((long) cs.charAt(off + 1) << 32)
616+
| ((long) cs.charAt(off + 2) << 16)
617+
| cs.charAt(off + 3);
618+
}
619+
620+
/**
621+
* Reads an {@code int} value from a {@link CharSequence} with given offset.
622+
*
623+
* @param cs a char sequence
624+
* @param off an offset
625+
* @return the value
626+
*/
627+
public static int getInt (CharSequence cs, int off) {
628+
return ((int) cs.charAt(off) << 16)
629+
| (int) cs.charAt(off + 1);
630+
}
631+
632+
/**
633+
* Copies a given number of characters from a {@link CharSequence} into a byte array.
634+
*
635+
* @param cs a char sequence
636+
* @param offsetCharSequence an offset for the char sequence
637+
* @param toByteArray a byte array
638+
* @param offsetByteArray an offset for the byte array
639+
* @param numChars the number of characters to copy
640+
*/
641+
public static void copyCharsToByteArray (
642+
CharSequence cs,
643+
int offsetCharSequence,
644+
byte[] toByteArray,
645+
int offsetByteArray,
646+
int numChars
647+
){
648+
for (int charIdx = 0; charIdx <= numChars - 4; charIdx += 4){
649+
setLong(toByteArray,
650+
offsetByteArray + (charIdx << 1),
651+
getLong(cs, offsetCharSequence + charIdx));
652+
}
653+
654+
if ((numChars & 2) != 0){
655+
int charIdx = numChars & 0xFFFF_FFFC;
656+
setInt(toByteArray,
657+
offsetByteArray + (charIdx << 1),
658+
getInt(cs, offsetCharSequence + charIdx));
659+
}
660+
661+
if ((numChars & 1) != 0){
662+
int charIdx = numChars & 0xFFFF_FFFE;
663+
setChar(toByteArray,
664+
offsetByteArray + (charIdx << 1),
665+
cs.charAt(offsetCharSequence + charIdx));
666+
}
667+
}
604668
}
605669
}

src/test/java/com/trivago/fastutilconcurrentwrapper/io/BAISTest.java

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,12 @@
55
import org.junit.jupiter.api.DisplayName;
66
import org.junit.jupiter.api.Test;
77

8+
import java.io.IOException;
89
import java.io.UTFDataFormatException;
910
import java.util.Arrays;
1011
import java.util.UUID;
1112

13+
import static java.nio.charset.StandardCharsets.*;
1214
import static org.junit.jupiter.api.Assertions.*;
1315

1416
/// @see BAIS
@@ -326,4 +328,35 @@ void testSkipBytes() {
326328
assertEquals(3, skipped);
327329
assertEquals(3, stream.position());
328330
}
331+
332+
@Test
333+
void _transferTo () throws IOException {
334+
String s = "Hello World!";
335+
var is = new BAIS(s.getBytes(ISO_8859_1));
336+
var w = new BAOS();
337+
is.transferTo(w);
338+
assertEquals(s, w.toString());
339+
assertEquals(0, is.available());
340+
is.reset();
341+
assertEquals(is.available(), w.length());
342+
}
343+
344+
@Test
345+
void _iterator () throws IOException {
346+
String s = "Hello World!";
347+
var is = new BAIS(s.getBytes(ISO_8859_1));
348+
var w = new BAOS(is.array(), is.limit());
349+
assertEquals(s, w.toString());
350+
assertEquals(is.available(), w.length());
351+
352+
assertTrue(is.hasNext());
353+
assertEquals('H', is.nextInt());
354+
assertTrue(is.hasNext());
355+
assertEquals('e', is.nextInt());
356+
357+
is.reset();
358+
val sb = new StringBuilder();
359+
is.forEachRemaining((int ch)->sb.append((char) ch));
360+
assertEquals(s, sb.toString());
361+
}
329362
}

src/test/java/com/trivago/fastutilconcurrentwrapper/io/BAOSTest.java

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package com.trivago.fastutilconcurrentwrapper.io;
22

33
import com.trivago.fastutilconcurrentwrapper.util.JBytes;
4+
import it.unimi.dsi.fastutil.bytes.ByteArrays;
45
import lombok.val;
56
import org.junit.jupiter.api.BeforeEach;
67
import org.junit.jupiter.api.DisplayName;
@@ -22,6 +23,7 @@
2223
import java.util.UUID;
2324
import java.util.stream.IntStream;
2425

26+
import static java.nio.charset.StandardCharsets.*;
2527
import static org.junit.jupiter.api.Assertions.*;
2628

2729
///@see BAOS
@@ -607,7 +609,7 @@ void asStr () {
607609

608610
x.reset();
609611
x.writeChars("Я$€");
610-
assertEquals("Я$€", x.toString(StandardCharsets.UTF_16BE));
612+
assertEquals("Я$€", x.toString(UTF_16BE));
611613
}
612614

613615
@Test
@@ -1077,4 +1079,51 @@ void _ascii () {
10771079
assertEquals(sss, baos.toString());
10781080
assertEquals(sss, baos.toString(StandardCharsets.ISO_8859_1));
10791081
}
1082+
1083+
@Test
1084+
@SuppressWarnings({"ConstantValue", "EqualsBetweenInconvertibleTypes", "EqualsWithItself"})
1085+
void _appendable () {
1086+
var w = new BAOS();
1087+
assertEquals(1, w.hashCode());
1088+
assertEquals(1, Arrays.hashCode(ByteArrays.EMPTY_ARRAY));
1089+
1090+
var s1 = "Проверка связи!";
1091+
w.append(s1);
1092+
w.append("");// nop
1093+
w.append("012 test 012", 3, 9);
1094+
w.append("012 test 012", 3, 3);// nop
1095+
w.append('!');
1096+
assertEquals(s1+" test !", w.toString(UTF_16BE));
1097+
assertEquals(44, w.length());
1098+
1099+
val s3 = "🚀".repeat(50_000);
1100+
w.append(s3);
1101+
String total = s1 + " test !" + s3;
1102+
assertEquals(total, w.toString(UTF_16BE));
1103+
assertEquals(200_044, w.length());// 44+
1104+
assertEquals(2, "🚀".length());// 2 chars == 4 bytes
1105+
1106+
assertEquals(542738558, w.hashCode());
1107+
assertEquals(w.hashCode(), Arrays.hashCode(w.toByteArray()));
1108+
assertEquals(w.hashCode(), Arrays.hashCode(total.getBytes(UTF_16BE)));
1109+
1110+
boolean eq = w.equals(total);
1111+
assertTrue(eq);
1112+
eq = w.equals(total.getBytes(UTF_16BE));
1113+
assertTrue(eq);
1114+
eq = w.equals(w);
1115+
assertTrue(eq);
1116+
eq = w.equals(null);
1117+
assertFalse(eq);
1118+
1119+
var clone = new BAOS(w);
1120+
assertEquals(clone, w);
1121+
assertEquals(w.hashCode(), clone.hashCode());
1122+
// verify with BAIS
1123+
var is = new BAIS(w);
1124+
1125+
assertEquals(s1, is.readUTF16String(s1.length()));
1126+
assertEquals(" test !", is.readUTF16String(7));
1127+
assertEquals(s3, is.readUTF16String(s3.length()));
1128+
}
10801129
}

0 commit comments

Comments
 (0)