map = new LinkedHashMap<>();
+
+ map.put("logger", new LoggingHandler(LogLevel.DEBUG));
+ map.put("protocol_splitter", new ProtocolSplitter());
+ map.put("protocol_decoder", new ProtocolDecoder(true));
+ map.put("protocol_encoder", new ProtocolEncoder());
+ map.put("handshake_handler", new HandshakeHandler(statusHandlerProvider));
+
+ return map;
+ }
+
+ @Provides
+ StatusHandler provideStatusHandler() {
+ return new StatusHandler();
}
}
diff --git a/src/main/java/mc/server/network/Server.java b/src/main/java/mc/server/network/Server.java
index f1464f8..b346a17 100644
--- a/src/main/java/mc/server/network/Server.java
+++ b/src/main/java/mc/server/network/Server.java
@@ -2,5 +2,5 @@ package mc.server.network;
public interface Server {
- void start(String host, int port);
+ void bind(String host, int port);
}
diff --git a/src/main/java/mc/server/network/netty/AbstractPacketHandler.java b/src/main/java/mc/server/network/netty/AbstractPacketHandler.java
new file mode 100644
index 0000000..733e255
--- /dev/null
+++ b/src/main/java/mc/server/network/netty/AbstractPacketHandler.java
@@ -0,0 +1,17 @@
+package mc.server.network.netty;
+
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.SimpleChannelInboundHandler;
+import mc.protocol.packets.Packet;
+
+public abstract class AbstractPacketHandler extends SimpleChannelInboundHandler {
+
+ @SuppressWarnings("unchecked")
+ @Override
+ protected void channelRead0(ChannelHandlerContext ctx, Packet msg) throws Exception {
+ channelRead1(ctx, (P) msg);
+ }
+
+ @SuppressWarnings("java:S112")
+ protected abstract void channelRead1(ChannelHandlerContext ctx, P packet) throws Exception;
+}
diff --git a/src/main/java/mc/server/network/netty/HandshakeHandler.java b/src/main/java/mc/server/network/netty/HandshakeHandler.java
new file mode 100644
index 0000000..8ceb9a1
--- /dev/null
+++ b/src/main/java/mc/server/network/netty/HandshakeHandler.java
@@ -0,0 +1,24 @@
+package mc.server.network.netty;
+
+import io.netty.channel.ChannelHandlerContext;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import mc.protocol.NetworkAttributes;
+import mc.protocol.packets.client.HandshakePacket;
+
+import javax.inject.Provider;
+
+@Slf4j
+@RequiredArgsConstructor
+public class HandshakeHandler extends AbstractPacketHandler {
+
+ private final Provider statusHandlerProvider;
+
+ @Override
+ protected void channelRead1(ChannelHandlerContext ctx, HandshakePacket packet) {
+ log.info("{}", packet);
+
+ ctx.channel().attr(NetworkAttributes.STATE).set(packet.getNextState());
+ ctx.pipeline().replace("handshake_handler", "status_handler", statusHandlerProvider.get());
+ }
+}
diff --git a/src/main/java/mc/server/network/netty/NettyServer.java b/src/main/java/mc/server/network/netty/NettyServer.java
index 4c3d19e..881914c 100644
--- a/src/main/java/mc/server/network/netty/NettyServer.java
+++ b/src/main/java/mc/server/network/netty/NettyServer.java
@@ -12,12 +12,11 @@ public class NettyServer implements Server {
private final ServerBootstrap serverBootstrap;
@Override
- public void start(String host, int port) {
+ public void bind(String host, int port) {
log.info("Network starting: {}:{}", host, port);
try {
- serverBootstrap.bind(host, port)
- .sync().channel().closeFuture().sync();
+ serverBootstrap.bind(host, port).sync().channel().closeFuture().sync();
} catch (InterruptedException e) {
if (log.isTraceEnabled()) {
log.trace("{}: {}", e.getClass().getSimpleName(), e.getMessage(), e);
diff --git a/src/main/java/mc/server/network/netty/StatusHandler.java b/src/main/java/mc/server/network/netty/StatusHandler.java
new file mode 100644
index 0000000..2d898ff
--- /dev/null
+++ b/src/main/java/mc/server/network/netty/StatusHandler.java
@@ -0,0 +1,33 @@
+package mc.server.network.netty;
+
+import io.netty.channel.ChannelHandlerContext;
+import lombok.extern.slf4j.Slf4j;
+import mc.protocol.packets.client.StatusServerRequest;
+import mc.protocol.packets.server.StatusServerResponse;
+
+@Slf4j
+public class StatusHandler extends AbstractPacketHandler {
+
+ @Override
+ protected void channelRead1(ChannelHandlerContext ctx, StatusServerRequest packet) {
+ log.info("{}", packet);
+
+ StatusServerResponse response = new StatusServerResponse();
+ response.setInfo("{\n" +
+ " \"version\": {\n" +
+ " \"name\": \"1.12.2\",\n" +
+ " \"protocol\": 340\n" +
+ " },\n" +
+ " \"players\": {\n" +
+ " \"max\": 0,\n" +
+ " \"online\": 0,\n" +
+ " \"sample\": []\n" +
+ " },\n" +
+ " \"description\": {\n" +
+ " \"text\": \"Hello world\"\n" +
+ " }\n" +
+ "}");
+
+ ctx.channel().writeAndFlush(response).channel().disconnect();
+ }
+}
diff --git a/src/main/resources/logback.xml b/src/main/resources/logback.xml
index 9f1a1a5..192c634 100644
--- a/src/main/resources/logback.xml
+++ b/src/main/resources/logback.xml
@@ -4,11 +4,15 @@
- %d{HH:mm:ss.SSS} %-5level [%t] [%logger{36}] -- %msg%n
+ %d{HH:mm:ss.SSS} %-5level [%35.35logger{34}] -- %msg%n
+
+
+
+
\ No newline at end of file
diff --git a/src/test/java/mc/protocol/io/NetByteBufReadTest.java b/src/test/java/mc/protocol/io/NetByteBufReadTest.java
new file mode 100644
index 0000000..c48b6b4
--- /dev/null
+++ b/src/test/java/mc/protocol/io/NetByteBufReadTest.java
@@ -0,0 +1,307 @@
+package mc.protocol.io;
+
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.Unpooled;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+
+import java.io.ByteArrayOutputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.util.Random;
+import java.util.UUID;
+import java.util.stream.Stream;
+
+import static org.junit.jupiter.api.Assertions.*;
+import static org.junit.jupiter.api.Assertions.assertArrayEquals;
+
+class NetByteBufReadTest {
+
+ private static Random random;
+ private ByteArrayOutputStream baos;
+
+ @BeforeEach
+ void setUp() {
+ random = new Random(System.currentTimeMillis());
+ baos = new ByteArrayOutputStream();
+ }
+
+ @ParameterizedTest
+ @MethodSource("paramsReadBoolean")
+ void readBoolean(byte sourceByte, boolean expectedValue) {
+ baos.write(sourceByte);
+
+ NetByteBuf netByteBuf = new NetByteBuf(Unpooled.wrappedBuffer(baos.toByteArray()));
+
+ assertEquals(expectedValue, netByteBuf.readBoolean());
+ }
+
+ @Test
+ void readByte() {
+ byte[] bytes = new byte[1];
+ random.nextBytes(bytes);
+ baos.write(bytes[0]);
+
+ NetByteBuf netByteBuf = new NetByteBuf(Unpooled.wrappedBuffer(baos.toByteArray()));
+
+ assertEquals(bytes[0], netByteBuf.readByte());
+ }
+
+ @ParameterizedTest
+ @MethodSource("paramsReadUnsignedByte")
+ void readUnsignedByte(byte sourceByte, int expectedValue) {
+ baos.write(sourceByte);
+
+ NetByteBuf netByteBuf = new NetByteBuf(Unpooled.wrappedBuffer(baos.toByteArray()));
+
+ assertEquals(expectedValue, netByteBuf.readUnsignedByte());
+ }
+
+ @Test
+ void readShort() throws IOException {
+ int value = Integer.valueOf(random.nextInt()).shortValue();
+ new DataOutputStream(baos).writeShort(value);
+
+ NetByteBuf netByteBuf = new NetByteBuf(Unpooled.wrappedBuffer(baos.toByteArray()));
+
+ assertEquals(value, netByteBuf.readShort());
+ }
+
+ @Test
+ void readUnsignedShort() throws IOException {
+ int value = 32768;
+ new DataOutputStream(baos).writeShort(value);
+
+ NetByteBuf netByteBuf = new NetByteBuf(Unpooled.wrappedBuffer(baos.toByteArray()));
+
+ assertEquals(value, netByteBuf.readUnsignedShort());
+ }
+
+ @ParameterizedTest
+ @MethodSource("paramsReadString")
+ void readString(String string) throws IOException {
+ final byte[] strBytes = string.getBytes(StandardCharsets.UTF_8);
+ final byte[] bytes = new byte[strBytes.length + 1];
+ bytes[0] = (byte) string.codePoints().count(); // допустим, что размер поместился в один байт
+ System.arraycopy(strBytes, 0, bytes, 1, strBytes.length);
+
+ baos.write(bytes);
+
+ NetByteBuf netByteBuf = new NetByteBuf(Unpooled.wrappedBuffer(baos.toByteArray()));
+
+ assertEquals(string, netByteBuf.readString());
+ }
+
+ @Test
+ void readString_overSize() throws IOException {
+ String string = "123";
+ final byte[] strBytes = string.getBytes(StandardCharsets.UTF_8);
+ final byte[] bytes = new byte[strBytes.length + 1];
+ final int length = string.length();
+ bytes[0] = (byte) (length + 1); // допустим, что размер поместился в один байт
+ System.arraycopy(strBytes, 0, bytes, 1, strBytes.length);
+
+ baos.write(bytes);
+
+ NetByteBuf netByteBuf = new NetByteBuf(Unpooled.wrappedBuffer(baos.toByteArray()));
+
+ assertThrows(DecoderException.class, () -> netByteBuf.readString(length));
+ }
+
+ @Test
+ void readString_lessZero() throws IOException {
+ String string = "123";
+ final byte[] strBytes = string.getBytes(StandardCharsets.UTF_8);
+ final byte[] bytes = new byte[strBytes.length + 5];
+ bytes[0] = (byte) 0xFF;
+ bytes[1] = (byte) 0xFF;
+ bytes[2] = (byte) 0xFF;
+ bytes[3] = (byte) 0xFF;
+ bytes[4] = (byte) 0x0F;
+ System.arraycopy(strBytes, 0, bytes, 5, strBytes.length);
+
+ baos.write(bytes);
+
+ NetByteBuf netByteBuf = new NetByteBuf(Unpooled.wrappedBuffer(baos.toByteArray()));
+
+ assertThrows(DecoderException.class, () -> netByteBuf.readString(-1));
+ }
+
+ @ParameterizedTest
+ @MethodSource("paramsReadVarInt")
+ void readVarInt(byte[] sourceBytes, int expectedValue) throws IOException {
+ baos.write(sourceBytes);
+
+ NetByteBuf netByteBuf = new NetByteBuf(Unpooled.wrappedBuffer(baos.toByteArray()));
+
+ assertEquals(expectedValue, netByteBuf.readVarInt());
+ }
+
+ @Test
+ void readVarInt_tooBig() throws IOException {
+ baos.write(new byte[]{ (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0x0F });
+
+ NetByteBuf netByteBuf = new NetByteBuf(Unpooled.wrappedBuffer(baos.toByteArray()));
+
+ assertEquals(-1, netByteBuf.readVarInt());
+ }
+
+ @ParameterizedTest
+ @MethodSource({"paramsReadVarInt", "paramsReadVarLong"})
+ void readVarLong(byte[] sourceBytes, long expectedValue) throws IOException {
+ baos.write(sourceBytes);
+
+ NetByteBuf netByteBuf = new NetByteBuf(Unpooled.wrappedBuffer(baos.toByteArray()));
+
+ assertEquals(expectedValue, netByteBuf.readVarLong());
+ }
+
+ @Test
+ void readVarLong_tooBig() throws IOException {
+ baos.write(new byte[]{ (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
+ (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
+ (byte) 0xFF, (byte) 0x0F });
+
+ NetByteBuf netByteBuf = new NetByteBuf(Unpooled.wrappedBuffer(baos.toByteArray()));
+
+ assertEquals(-1, netByteBuf.readVarLong());
+ }
+
+ @Test
+ void readUUID() throws IOException {
+ final UUID uuid = UUID.randomUUID();
+ final long mostSignificantBits = uuid.getMostSignificantBits();
+ final long leastSignificantBits = uuid.getLeastSignificantBits();
+
+ baos.write(new byte[]{
+ (byte) ((mostSignificantBits >>> 56) & 0xFF),
+ (byte) ((mostSignificantBits >>> 48) & 0xFF),
+ (byte) ((mostSignificantBits >>> 40) & 0xFF),
+ (byte) ((mostSignificantBits >>> 32) & 0xFF),
+ (byte) ((mostSignificantBits >>> 24) & 0xFF),
+ (byte) ((mostSignificantBits >>> 16) & 0xFF),
+ (byte) ((mostSignificantBits >>> 8) & 0xFF),
+ (byte) (mostSignificantBits & 0xFF)
+ });
+ baos.write(new byte[]{
+ (byte) ((leastSignificantBits >>> 56) & 0xFF),
+ (byte) ((leastSignificantBits >>> 48) & 0xFF),
+ (byte) ((leastSignificantBits >>> 40) & 0xFF),
+ (byte) ((leastSignificantBits >>> 32) & 0xFF),
+ (byte) ((leastSignificantBits >>> 24) & 0xFF),
+ (byte) ((leastSignificantBits >>> 16) & 0xFF),
+ (byte) ((leastSignificantBits >>> 8) & 0xFF),
+ (byte) (leastSignificantBits & 0xFF)
+ });
+
+ NetByteBuf netByteBuf = new NetByteBuf(Unpooled.wrappedBuffer(baos.toByteArray()));
+
+ assertEquals(uuid, netByteBuf.readUUID());
+ }
+
+ @Test
+ void readBytes() throws IOException {
+ byte[] bytes = new byte[128];
+ random.nextBytes(bytes);
+ baos.write(bytes);
+
+ byte[] actualBytes = new byte[128];
+ NetByteBuf netByteBuf = new NetByteBuf(Unpooled.wrappedBuffer(baos.toByteArray()));
+
+ assertEquals(bytes.length, netByteBuf.readableBytes());
+
+ netByteBuf.readBytes(actualBytes);
+
+ assertArrayEquals(bytes, actualBytes);
+ assertEquals(0, netByteBuf.readableBytes());
+ }
+
+ @Test
+ void read_offset() throws IOException {
+ byte[] bytes = new byte[128];
+ random.nextBytes(bytes);
+ baos.write(bytes);
+
+ byte[] buff = new byte[128];
+ NetByteBuf netByteBuf = new NetByteBuf(Unpooled.wrappedBuffer(baos.toByteArray()));
+ netByteBuf.readBytes(buff, 3, 11);
+
+ byte[] expectedBytes = new byte[11];
+ System.arraycopy(bytes, 0, expectedBytes, 0, 11);
+ byte[] actualBytes = new byte[11];
+ System.arraycopy(buff, 3, actualBytes, 0, 11);
+
+ assertArrayEquals(expectedBytes, actualBytes);
+ }
+
+ @SuppressWarnings("unused")
+ private static Stream paramsReadBoolean() {
+ return Stream.of(
+ Arguments.of((byte) 0x00, false),
+ Arguments.of((byte) 0x01, true)
+ );
+ }
+
+ @SuppressWarnings("unused")
+ private static Stream paramsReadUnsignedByte() {
+ return Stream.of(
+ Arguments.of((byte) 30, 30),
+ Arguments.of((byte) (0xFF & 130), 130)
+ );
+ }
+
+ @SuppressWarnings("unused")
+ private static Stream paramsReadString() {
+ return Stream.of(
+ Arguments.of(""),
+ Arguments.of("Latin"),
+ Arguments.of("Кириллица"),
+ Arguments.of("العربية"),
+ Arguments.of("ﬦﬣﬡ"), // Алфавитные формы представления
+ Arguments.of("\uD800\uDD07") // Эгейские цифры, [один]
+ );
+ }
+
+ @SuppressWarnings("unused")
+ private static Stream paramsReadVarInt() {
+ return Stream.of(
+ Arguments.of(new byte[]{ 0x78 }, 120),
+ Arguments.of(new byte[]{ (byte) 0xE0, 0x5D }, 12000),
+ Arguments.of(new byte[]{ (byte) 0xC0, (byte) 0xA9, 0x07 }, 120000),
+ Arguments.of(new byte[]{ (byte) 0x80, (byte) 0x9C, (byte) 0x9C, (byte) 0x39 }, 120_000_000),
+ Arguments.of(new byte[]{ (byte) 0x80, (byte) 0x98, (byte) 0x9A, (byte) 0xBC, 0x04 }, 1_200_000_000)
+ );
+ }
+
+ @SuppressWarnings("unused")
+ private static Stream paramsReadVarLong() {
+ return Stream.of(
+ Arguments.of(
+ new byte[]{ (byte) 0x80, (byte) 0xF0, (byte) 0x85, (byte) 0xDA, 0x2C },
+ 12_000_000_000L),
+ Arguments.of(
+ new byte[]{ (byte) 0x80, (byte) 0xE0, (byte) 0xBA, (byte) 0x84, (byte) 0xBF, 0x03 },
+ 120_000_000_000L),
+ Arguments.of(
+ new byte[]{ (byte) 0x80, (byte) 0x80, (byte) 0xF3, (byte) 0xBD, (byte) 0x9F, (byte) 0xDD,
+ 0x02 },
+ 12_000_000_000_000L),
+ Arguments.of(
+ new byte[]{ (byte) 0x80, (byte) 0x80, (byte) 0xEC, (byte) 0xAD, (byte) 0xCC, (byte) 0xEC,
+ (byte) 0x90, 0x02},
+ 1_200_000_000_000_000L),
+ Arguments.of(
+ new byte[]{ (byte) 0x80, (byte) 0x80, (byte) 0xB0, (byte) 0xE8, (byte) 0xD3, (byte) 0xEB,
+ (byte) 0x94, (byte) 0xD5, 0x01 },
+ 120_000_000_000_000_000L),
+ Arguments.of(
+ new byte[]{ (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80,
+ (byte) 0x80, (byte) 0x80, (byte) 0x80, 0x01 },
+ Long.MIN_VALUE)
+ );
+ }
+}
\ No newline at end of file
diff --git a/src/test/java/mc/protocol/io/NetByteBufWriteTest.java b/src/test/java/mc/protocol/io/NetByteBufWriteTest.java
new file mode 100644
index 0000000..74b6c5a
--- /dev/null
+++ b/src/test/java/mc/protocol/io/NetByteBufWriteTest.java
@@ -0,0 +1,246 @@
+package mc.protocol.io;
+
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.Unpooled;
+import org.apache.commons.lang3.RandomStringUtils;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+
+import java.nio.charset.StandardCharsets;
+import java.util.Random;
+import java.util.UUID;
+import java.util.stream.Stream;
+
+import static org.junit.jupiter.api.Assertions.assertArrayEquals;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+class NetByteBufWriteTest {
+
+ private static Random random;
+
+ @BeforeAll
+ static void setUp() {
+ random = new Random(System.currentTimeMillis());
+ }
+
+ @ParameterizedTest
+ @MethodSource("paramsWriteBoolean")
+ void writeBoolean(boolean sourceValue, byte expectedByte) {
+ ByteBuf byteBuf = Unpooled.buffer();
+ NetByteBuf netByteBuf = new NetByteBuf(byteBuf);
+
+ netByteBuf.writeBoolean(sourceValue);
+
+ assertEquals(expectedByte, byteBuf.array()[0]);
+ }
+
+ @ParameterizedTest
+ @MethodSource("paramsWriteByte")
+ void writeByte(byte sourceValue, byte expectedByte) {
+ ByteBuf byteBuf = Unpooled.buffer();
+ NetByteBuf netByteBuf = new NetByteBuf(byteBuf);
+
+ netByteBuf.writeByte(sourceValue);
+
+ assertEquals(expectedByte, byteBuf.array()[0]);
+ }
+
+ @ParameterizedTest
+ @MethodSource("paramsWriteString")
+ void writeString(String string) {
+ ByteBuf byteBuf = Unpooled.buffer();
+ NetByteBuf netByteBuf = new NetByteBuf(byteBuf);
+
+ netByteBuf.writeString(string);
+
+ byte[] actualArray = netByteBuf.copy(0, netByteBuf.readableBytes()).array();
+ int length = actualArray[0]; // допустим, что размер поместился в один байт
+ assertEquals(string.codePoints().count(), length);
+
+ byte[] dataBytes = new byte[actualArray.length - 1];
+ System.arraycopy(actualArray, 1, dataBytes, 0, dataBytes.length);
+ assertEquals(string, new String(dataBytes, StandardCharsets.UTF_8));
+ }
+
+ //возможно этот тест нужно перенести в NetByteBufReadTest
+ @Test
+ void writeString_overSize() {
+ String overSizeString = RandomStringUtils.randomAscii(Short.MAX_VALUE + Short.MAX_VALUE);
+
+ ByteBuf byteBuf = Unpooled.buffer();
+ NetByteBuf netByteBuf = new NetByteBuf(byteBuf);
+
+ netByteBuf.writeString(overSizeString);
+
+ NetByteBuf netByteBuf2 = new NetByteBuf(byteBuf.copy());
+ String actualString = netByteBuf2.readString();
+
+ String expectedString = overSizeString.substring(0, Short.MAX_VALUE);
+
+ assertEquals(expectedString, actualString);
+ }
+
+ @ParameterizedTest
+ @MethodSource("paramsWriteVarInt")
+ void writeVarInt(int sourceValue, byte[] expectedBytes) {
+ ByteBuf byteBuf = Unpooled.buffer();
+ NetByteBuf netByteBuf = new NetByteBuf(byteBuf);
+
+ netByteBuf.writeVarInt(sourceValue);
+ byte[] actualArray = netByteBuf.copy(0, netByteBuf.readableBytes()).array();
+
+ assertArrayEquals(expectedBytes, actualArray);
+ }
+
+ @ParameterizedTest
+ @MethodSource({ "paramsWriteVarInt", "paramsWriteVarLong" })
+ void writeVarLong(long sourceValue, byte[] expectedBytes) {
+ ByteBuf byteBuf = Unpooled.buffer();
+ NetByteBuf netByteBuf = new NetByteBuf(byteBuf);
+
+ netByteBuf.writeVarLong(sourceValue);
+ byte[] actualArray = netByteBuf.copy(0, netByteBuf.readableBytes()).array();
+
+ assertArrayEquals(expectedBytes, actualArray);
+ }
+
+ @Test
+ void writeUUID() {
+ final UUID uuid = UUID.randomUUID();
+
+ ByteBuf byteBuf = Unpooled.buffer();
+ NetByteBuf netByteBuf = new NetByteBuf(byteBuf);
+
+ netByteBuf.writeUUID(uuid);
+
+ final long mostSignificantBits = uuid.getMostSignificantBits();
+ final long leastSignificantBits = uuid.getLeastSignificantBits();
+
+ byte[] actualArray = netByteBuf.copy(0, netByteBuf.readableBytes()).array();
+
+ assertArrayEquals(new byte[]{
+ (byte) ((mostSignificantBits >>> 56) & 0xFF),
+ (byte) ((mostSignificantBits >>> 48) & 0xFF),
+ (byte) ((mostSignificantBits >>> 40) & 0xFF),
+ (byte) ((mostSignificantBits >>> 32) & 0xFF),
+ (byte) ((mostSignificantBits >>> 24) & 0xFF),
+ (byte) ((mostSignificantBits >>> 16) & 0xFF),
+ (byte) ((mostSignificantBits >>> 8) & 0xFF),
+ (byte) (mostSignificantBits & 0xFF),
+
+ (byte) ((leastSignificantBits >>> 56) & 0xFF),
+ (byte) ((leastSignificantBits >>> 48) & 0xFF),
+ (byte) ((leastSignificantBits >>> 40) & 0xFF),
+ (byte) ((leastSignificantBits >>> 32) & 0xFF),
+ (byte) ((leastSignificantBits >>> 24) & 0xFF),
+ (byte) ((leastSignificantBits >>> 16) & 0xFF),
+ (byte) ((leastSignificantBits >>> 8) & 0xFF),
+ (byte) (leastSignificantBits & 0xFF) },
+ actualArray);
+ }
+
+ @Test
+ void writeBytes() {
+ byte[] bytes = new byte[128];
+ random.nextBytes(bytes);
+
+ ByteBuf byteBuf = Unpooled.buffer();
+ NetByteBuf netByteBuf = new NetByteBuf(byteBuf);
+
+ netByteBuf.writeBytes(bytes);
+ byte[] actualArray = netByteBuf.copy(0, netByteBuf.readableBytes()).array();
+
+ assertArrayEquals(bytes, actualArray);
+ }
+
+ @Test
+ void write_offset() {
+ byte[] bytes = new byte[128];
+ random.nextBytes(bytes);
+
+ ByteBuf byteBuf = Unpooled.buffer();
+ NetByteBuf netByteBuf = new NetByteBuf(byteBuf);
+
+ netByteBuf.writeBytes(bytes, 3, 11);
+
+ byte[] actualBytes = new byte[11];
+ System.arraycopy(byteBuf.array(), 0, actualBytes, 0, 11);
+
+ byte[] expectedBytes = new byte[11];
+ System.arraycopy(bytes, 3, expectedBytes, 0, 11);
+
+ assertArrayEquals(expectedBytes, actualBytes);
+ }
+
+ @SuppressWarnings("unused")
+ private static Stream paramsWriteBoolean() {
+ return Stream.of(
+ Arguments.of(false, (byte) 0x00),
+ Arguments.of(true, (byte) 0x01)
+ );
+ }
+
+ @SuppressWarnings("unused")
+ private static Stream paramsWriteByte() {
+ byte b = Integer.valueOf(random.nextInt()).byteValue();
+
+ return Stream.of(
+ Arguments.of(b, b),
+ Arguments.of((byte) 128, (byte) -128)
+ );
+ }
+
+ @SuppressWarnings("unused")
+ private static Stream paramsWriteString() {
+ return Stream.of(
+ Arguments.of(""),
+ Arguments.of("Latin"),
+ Arguments.of("Кириллица"),
+ Arguments.of("العربية"),
+ Arguments.of("ﬦﬣﬡ"), // Алфавитные формы представления
+ Arguments.of("\uD800\uDD07") // Эгейские цифры, [один]
+ );
+ }
+
+ @SuppressWarnings("unused")
+ private static Stream paramsWriteVarInt() {
+ return Stream.of(
+ Arguments.of(120, new byte[]{ 0x78 }),
+ Arguments.of(12000, new byte[]{ (byte) 0xE0, 0x5D }),
+ Arguments.of(120000, new byte[]{ (byte) 0xC0, (byte) 0xA9, 0x07 }),
+ Arguments.of(120000000, new byte[]{ (byte) 0x80, (byte) 0x9C, (byte) 0x9C, (byte) 0x39 }),
+ Arguments.of(1200000000, new byte[]{ (byte) 0x80, (byte) 0x98, (byte) 0x9A, (byte) 0xBC, 0x04 })
+ );
+ }
+
+ @SuppressWarnings("unused")
+ private static Stream paramsWriteVarLong() {
+ return Stream.of(
+ Arguments.of(
+ 12_000_000_000L,
+ new byte[]{ (byte) 0x80, (byte) 0xF0, (byte) 0x85, (byte) 0xDA, 0x2C }),
+ Arguments.of(
+ 120_000_000_000L,
+ new byte[]{ (byte) 0x80, (byte) 0xE0, (byte) 0xBA, (byte) 0x84, (byte) 0xBF, 0x03 }),
+ Arguments.of(
+ 12_000_000_000_000L,
+ new byte[]{ (byte) 0x80, (byte) 0x80, (byte) 0xF3, (byte) 0xBD, (byte) 0x9F, (byte) 0xDD,
+ 0x02 }),
+ Arguments.of(
+ 1_200_000_000_000_000L,
+ new byte[]{ (byte) 0x80, (byte) 0x80, (byte) 0xEC, (byte) 0xAD, (byte) 0xCC, (byte) 0xEC,
+ (byte) 0x90, 0x02 }),
+ Arguments.of(
+ 120_000_000_000_000_000L,
+ new byte[]{ (byte) 0x80, (byte) 0x80, (byte) 0xB0, (byte) 0xE8, (byte) 0xD3, (byte) 0xEB,
+ (byte) 0x94, (byte) 0xD5, 0x01 }),
+ Arguments.of(
+ Long.MIN_VALUE,
+ new byte[]{ (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80,
+ (byte) 0x80, (byte) 0x80, (byte) 0x80, 0x01 })
+ );
+ }
+}