From 48fcffbe35eeefae83eb66835bf03823f5cd9a80 Mon Sep 17 00:00:00 2001 From: hama Date: Sun, 3 Jul 2016 08:36:27 +0200 Subject: [PATCH] initial state --- .gitignore | 7 + pom.xml | 92 ++++ src/main/java/de/republib/net/TcpClient.java | 89 ++++ src/main/java/de/republib/pinet/Client.java | 36 ++ .../java/de/republib/pinet/GpioClient.java | 60 +++ .../java/de/republib/pinet/PinNumber.java | 92 ++++ src/main/java/de/republib/util/DynBytes.java | 392 ++++++++++++++++++ src/main/resources/logback.xml | 16 + .../java/de/republib/util/DynBytesTest.java | 196 +++++++++ 9 files changed, 980 insertions(+) create mode 100644 .gitignore create mode 100644 pom.xml create mode 100644 src/main/java/de/republib/net/TcpClient.java create mode 100644 src/main/java/de/republib/pinet/Client.java create mode 100644 src/main/java/de/republib/pinet/GpioClient.java create mode 100644 src/main/java/de/republib/pinet/PinNumber.java create mode 100644 src/main/java/de/republib/util/DynBytes.java create mode 100644 src/main/resources/logback.xml create mode 100644 src/test/java/de/republib/util/DynBytesTest.java diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ff41283 --- /dev/null +++ b/.gitignore @@ -0,0 +1,7 @@ +target +test-output +*.iml +.project +.classpath +.idea +.settings diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..20b04d8 --- /dev/null +++ b/pom.xml @@ -0,0 +1,92 @@ + + + + 4.0.0 + + de.republlb + pinet + jar + 1.0-SNAPSHOT + + Controlling a Raspberry Pi + https://sourcefourge.net + + + UTF-8 + UTF-8 + 1.1.7 + 1.7.21 + + + + + + org.slf4j + slf4j-api + ${slf4j.version} + + + ch.qos.logback + logback-classic + ${logback.version} + + + ch.qos.logback + logback-core + ${logback.version} + + + org.testng + testng + 6.9.8 + test + + + + + install + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.5.1 + + 1.7 + 1.7 + + + + org.apache.maven.plugins + maven-resources-plugin + 2.6 + + UTF-8 + + + + + org.apache.maven.plugins + maven-surefire-plugin + 2.12.4 + + methods + 4 + + + + + org.codehaus.mojo + exec-maven-plugin + 1.4.0 + + de.republlb.MainApp + false + + + + + + + diff --git a/src/main/java/de/republib/net/TcpClient.java b/src/main/java/de/republib/net/TcpClient.java new file mode 100644 index 0000000..87c7130 --- /dev/null +++ b/src/main/java/de/republib/net/TcpClient.java @@ -0,0 +1,89 @@ +package de.republib.net; + +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.net.Socket; +import java.net.UnknownHostException; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import de.republib.util.DynBytes; + +/** + * @author hm + * + */ +public class TcpClient { + Logger logger = LoggerFactory.getLogger(TcpClient.class); + Socket socket = null; + DataOutputStream outStream = null; + DataInputStream inStream = null; + String host = "127.0.0.1"; + int port; + boolean active; + + /** + * Constructor. + * + * @param host + * server address + * @param port + * server port + */ + public TcpClient(String host, int port) { + this.port = port; + this.host = host; + this.active = false; + try { + this.socket = new Socket(host, port); + this.outStream = new DataOutputStream(new BufferedOutputStream(this.socket.getOutputStream())); + this.inStream = new DataInputStream(new BufferedInputStream(this.socket.getInputStream())); + this.active = true; + } catch (final UnknownHostException e) { + this.logger.error(String.format("unknown server: %s:%d", host, port)); + } catch (final IOException e) { + this.logger.error(String.format("cannot connect to %s:%d", host, port), e); + } + } + + public DynBytes receive(DynBytes buffer) { + int length; + try { + buffer.setLength(0); + length = this.inStream.read(buffer.getBuffer(), 0, buffer.getLength()); + if (this.logger.isDebugEnabled()) { + this.logger.debug(String.format("received: %s[%d]", buffer.toUtf8(4), buffer.getLength())); + } + buffer.setLength(length); + } catch (final IOException e) { + this.logger.error("TcpClient.receive():", e); + } + return buffer; + } + + /** + * + * @param buffer + * bytes to send + * @param offset + * first index in buffer to send + * @param length + * number of bytes to send + */ + public void send(DynBytes message) { + if (this.active) { + if (this.logger.isDebugEnabled()) { + this.logger.debug(String.format("send(%s, %d)", message.toUtf8(4), message.getLength())); + } + try { + this.outStream.write(message.getBuffer(), 0, message.getLength()); + } catch (final IOException e) { + this.logger.error(String.format("send(%s, %d)", message.toUtf8(4), message.getLength()), e); + } + } + } +} diff --git a/src/main/java/de/republib/pinet/Client.java b/src/main/java/de/republib/pinet/Client.java new file mode 100644 index 0000000..55030c9 --- /dev/null +++ b/src/main/java/de/republib/pinet/Client.java @@ -0,0 +1,36 @@ +package de.republlb; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import de.republib.pinet.GpioClient; +import de.republib.pinet.PinNumber; + +/** + * A Camel Application + */ +public class MainApp { + static Logger logger = LoggerFactory.getLogger(MainApp.class); + + /** + * Evaluates the options and start the program. + * + * @param args + * program arguments + */ + public static void main(String... args) throws Exception { + MainApp.logger.info("start"); + int port = 15000; + final String host = "127.0.0.1"; + final int ix = 0; + String arg; + while (ix < args.length && (arg = args[ix]).startsWith("-")) { + if (arg.startsWith("-p") || arg.startsWith("--port=")) { + arg = arg.substring(arg.charAt(1) == '-' ? 2 : 7); + port = Integer.parseInt(arg); + } + } + final GpioClient client = new GpioClient(host, port); + client.blink(PinNumber.PIN_RPi2_12, 10, 500, 500); + } +} diff --git a/src/main/java/de/republib/pinet/GpioClient.java b/src/main/java/de/republib/pinet/GpioClient.java new file mode 100644 index 0000000..010fd7f --- /dev/null +++ b/src/main/java/de/republib/pinet/GpioClient.java @@ -0,0 +1,60 @@ +/** + * + */ +package de.republib.pinet; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import de.republib.net.TcpClient; +import de.republib.util.DynBytes; + +/** + * Requests services for a GPIO server. + * + * @author hm + * + */ +public class GpioClient extends TcpClient { + Logger logger = LoggerFactory.getLogger(GpioClient.class); + + DynBytes buffer = new DynBytes(64 * 1014, 64 * 1014); + + /** + * Constructor. + * + * @param host + * @param port + */ + public GpioClient(String host, int port) { + super(host, port); + } + + /** + * Toggles an output pin between high and low. + * + * @param pin + * pin for output + * @param count + * number of blinks (toggles between low and high) + * @param high + * duration of the high state in milliseconds + * @param low + * duration of the low state in milliseconds + * @return the answer from the server + */ + public DynBytes blink(PinNumber pin, int count, int high, int low) { + final DynBytes rc = null; + if (this.logger.isDebugEnabled()) { + this.logger.debug( + String.format("blink: pin: %d count: %d high: %d low: %d", pin.getNumber(), count, high, low)); + } + this.buffer.setLength(0).append("BLNK"); + this.buffer.append((byte) pin.getNumber()); + this.buffer.appendLittleEndian(count, 4); + this.buffer.appendLittleEndian(high * 1000, 4); + this.buffer.appendLittleEndian(low * 1000, 4); + send(this.buffer); + return rc; + } +} diff --git a/src/main/java/de/republib/pinet/PinNumber.java b/src/main/java/de/republib/pinet/PinNumber.java new file mode 100644 index 0000000..09b2260 --- /dev/null +++ b/src/main/java/de/republib/pinet/PinNumber.java @@ -0,0 +1,92 @@ +/** + * + */ +package de.republib.pinet; + +/** + * Pins of the SystemOnChip BM2835 (used in raspberry pi). + * + * @author hm + * + */ +public enum PinNumber { + PIN_RPi2_03("RPi2_03", 2), // + PIN_RPi2_05("RPi2_05", 3), // + PIN_RPi2_07("RPi2_07", 4), // + PIN_RPi2_08("RPi2_08", 14), // + PIN_RPi2_10("RPi2_10", 15), // + PIN_RPi2_11("RPi2_11", 17), // + PIN_RPi2_12("RPi2_12", 18), // + PIN_RPi2_13("RPi2_13", 27), // + PIN_RPi2_15("RPi2_15", 22), // + PIN_RPi2_16("RPi2_16", 23), // + PIN_RPi2_18("RPi2_18", 24), // + PIN_RPi2_19("RPi2_19", 10), // + PIN_RPi2_21("RPi2_21", 9), // + PIN_RPi2_22("RPi2_22", 25), // + PIN_RPi2_23("RPi2_23", 11), // + PIN_RPi2_24("RPi2_24", 8), // + PIN_RPi2_26("RPi2_26", 7), // + PIN_RPi2_29("RPi2_29", 5), // + PIN_RPi2_31("RPi2_31", 6), // + PIN_RPi2_32("RPi2_32", 12), // + PIN_RPi2_33("RPi2_33", 13), // + PIN_RPi2_35("RPi2_35", 19), // + PIN_RPi2_36("RPi2_36", 16), // + PIN_RPi2_37("RPi2_37", 26), // + PIN_RPi2_38("RPi2_38", 20), // + PIN_RPi2_40("RPi2_40", 21), // + + PIN_SpiCE1("SpiCE1", 7), // + PIN_SpiCE0("SpiCE0", 8), // + PIN_SpiMISO("SpiMISO", 9), // + PIN_SpiMOSI("SpiMOSI", 10), // + PIN_SpiCLK("SpiCLK", 11); + + /** + * Finds the item by name + * + * @param name + * @return + */ + public static PinNumber find(String name) { + PinNumber rc = null; + for (final PinNumber pin : PinNumber.values()) { + if (pin.name.equals(name)) { + rc = pin; + break; + } + } + return rc; + } + + private int number; + private String name; + + /** + * Constructor. + * + * @param name + * the name of the pin + * @param number + * the number (0..31) + */ + PinNumber(String name, int number) { + this.name = name; + this.number = number; + } + + /** + * @return the name + */ + public String getName() { + return this.name; + } + + /** + * @return the number + */ + public int getNumber() { + return this.number; + } +} diff --git a/src/main/java/de/republib/util/DynBytes.java b/src/main/java/de/republib/util/DynBytes.java new file mode 100644 index 0000000..2ab43a5 --- /dev/null +++ b/src/main/java/de/republib/util/DynBytes.java @@ -0,0 +1,392 @@ +package de.republib.util; + +import java.io.UnsupportedEncodingException; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * A byte array with an adaptive size/length. + * + * The length of the array is dynamic between 0 and the current size. + * + * The size of the array is dynamic: it can be increased by ensureSize(). + * + * The storage space is managed with a blocksize: If the space is not + * enough it will reallocated with at least a size which is larger or equal than + * the old size plus the blocksize. + * + */ +public class DynBytes { + static Logger logger = LoggerFactory.getLogger(DynBytes.class); + + /** + * Convert a (binary) buffer content into a readable form. + * + * @param offset + * first index to dump: 0..length-1 + * @param length + * number of bytes to dump. May be too large + * @param bytePerLine + * number of bytes dumped into one output line + * @return "": empty buffer or parameter out of range
+ * otherwise: the content as byte hexadecimal dump
+ * e.g. "07 05 ab 03\n07 00\n" + */ + public static String hexDump(byte[] buffer, int offset, int length, int bytePerLine) { + String rc; + if (offset < 0 || offset >= buffer.length || bytePerLine < 1) { + rc = ""; + } else { + // buffer.length = 2, max_offset = 1, length => 1: + if (length > buffer.length - offset) { + length = buffer.length - offset; + } + final StringBuilder rc2 = new StringBuilder(3 * length); + int ix = offset; + for (int ii = 1; ii <= length; ii++) { + rc2.append(DynBytes.toHex((buffer[ix] >> 4) & 0xf)); + rc2.append(DynBytes.toHex(buffer[ix] & 0xf)); + rc2.append(ii % bytePerLine == 0 ? '\n' : ' '); + ix++; + } + if (length % bytePerLine != 0) { + rc2.deleteCharAt(rc2.length() - 1); + rc2.append('\n'); + } + rc = rc2.toString(); + } + return rc; + } + + /** + * Converts a number 0..15 into a hex digit. + * + * @param value + * value to convert + * @return the hexadecimal digit + */ + public static char toHex(int value) { + return (char) (value < 10 ? '0' + value : 'a' + value - 10); + } + + protected int blocksize; + + protected byte[] buffer; + + protected int length; + + /** + * Constructor. + * + * @param capacity + * the initial capacity of the buffer. If <= 0 it will be set to + * 16 + * @param blocksize + * the initial blocksize. If <= 0 it will be set to 1 + */ + public DynBytes(int capacity, int blocksize) { + if (capacity <= 0) { + capacity = 16; + } + this.buffer = new byte[capacity]; + this.blocksize = Math.max(1, blocksize); + this.length = 0; + } + + /** + * Appends a byte to the instance. + * + * @param value + * the byte to append + * @return the instance (for chaining) + */ + public DynBytes append(byte value) { + ensureSize(this.length + 1); + this.buffer[this.length++] = value; + return this; + } + + /** + * Appends a part of a byte array to the instance. + * + * @param buffer + * the byte array to add + * @param offset + * the first index of buffer to add
+ * 0 <= offset < buffer.length
+ * If offset is out of range nothing will be done + * @param length + * the number of bytes to add
+ * Is automatically reduced to a valid value + * @return the instance (for chaining) + */ + public DynBytes append(byte[] buffer, int offset, int length) { + if (offset >= 0 && offset < buffer.length) { + if (length > buffer.length - offset) { + length = buffer.length - offset; + } + ensureSize(this.length + length); + System.arraycopy(buffer, offset, this.buffer, this.length, length); + this.length += length; + } + return this; + } + + /** + * Appends a string to the instance (as UTF-8). + * + * @param string + * the string to add. May be null + * @return the instance (for chaining) + */ + public DynBytes append(String string) { + if (string != null) { + byte[] value = null; + try { + value = string.getBytes("UTF-8"); + append(value, 0, value.length); + } catch (final UnsupportedEncodingException e) { + DynBytes.logger.error("unsupported encoding: UTF-8"); + } + } + return this; + } + + /** + * Appends a number as little endian byte array. + * + * @param number + * the number to append + * @param width + * the number of bytes to append (1..4) + * @return the instance (for chaining) + */ + public DynBytes appendLittleEndian(int number, int width) { + if (width >= 1 && width <= 4) { + ensureSize(this.length + width); + for (int ix = 0; ix < width; ix++) { + this.buffer[this.length++] = (byte) (number & 0xff); + number >>= 8; + } + } + return this; + } + + /** + * Appends a number as little endian byte array. + * + * @param number + * the number to append + * @param width + * the number of bytes to append (1..8) + * @return the instance (for chaining) + */ + public DynBytes appendLittleEndian(long number, int width) { + if (width >= 1 && width <= 8) { + ensureSize(this.length + width); + for (int ix = 0; ix < width; ix++) { + this.buffer[this.length++] = (byte) (number & 0xff); + number >>= 8; + } + } + return this; + } + + /** + * Ensures that the size of the buffer is larger or equal to a given size. + *

+ * If the buffer is to small it will be enlarged. + * + * @param size + * minimal size of the buffer + * @return the instance (for chaining) + */ + public DynBytes ensureSize(int size) { + if (this.buffer.length < size) { + if (this.buffer.length + this.blocksize > size) { + size = this.buffer.length + this.blocksize; + } + final byte[] newBuffer = new byte[size]; + if (this.length > 0) { + System.arraycopy(this.buffer, 0, newBuffer, 0, this.length); + } + this.buffer = newBuffer; + } + return this; + } + + /** + * Returns the blocksize. + * + * @return the blocksize + */ + public int getBlocksize() { + return this.blocksize; + } + + /** + * @return the buffer + */ + public byte[] getBuffer() { + return this.buffer; + } + + /** + * Returns the buffer content as byte array. + * + * @return the buffer content + */ + public byte[] getBytes() { + byte[] rc = this.buffer; + if (this.length < this.buffer.length) { + rc = new byte[this.length]; + System.arraycopy(this.buffer, 0, rc, 0, this.length); + } + return rc; + } + + /** + * Returns the current capacity of the buffer. + * + * @return the current size + */ + public int getCapacity() { + return this.buffer.length; + } + + /** + * Returns the current length of the buffer. + * + * @return the current length + */ + public int getLength() { + return this.length; + } + + /** + * Convert a (binary) buffer content into a readable form. + * + * @param offset + * first index to dump: 0..length-1 + * @param length + * number of bytes to dump. May be too large + * @param bytePerLine + * number of bytes dumped into one output line + * @return "": empty buffer or parameter out of range
+ * otherwise: the content as byte hexadecimal dump
+ * e.g. "07 05 ab 03\n07 00\n" + */ + public String hexDump(int offset, int length, int bytePerLine) { + if (length > this.length - offset) { + length = this.length - offset; + } + final String rc = DynBytes.hexDump(this.buffer, offset, length, bytePerLine); + return rc; + } + + /** + * Extract an integer with width bytes from the buffer. + * + * @param index + * the first index of the integer + * @param width + * the number of bytes of the integer + * @return the value of the integer stored as little endian + */ + public int intAsLittleEndian(int index, int width, int defaultValue) { + int rc = defaultValue; + if (index >= 0 && index + width <= this.length && width > 0 && width <= 4) { + rc = 0; + for (int ii = width - 1; ii >= 0; ii--) { + rc = (rc << 8) | ((this.buffer[index + ii]) & 0xff); + } + } + return rc; + } + + /** + * Extract an integer with width bytes from the buffer. + * + * @param index + * the first index of the integer + * @param width + * the number of bytes of the integer + * @return the value of the integer stored as little endian + */ + public long longAsLittleEndian(int index, int width, long defaultValue) { + long rc = defaultValue; + if (index >= 0 && index + width <= this.length && width > 0 && width <= 8) { + rc = 0; + for (int ii = width - 1; ii >= 0; ii--) { + rc = (rc << 8) | ((this.buffer[index + ii]) & 0xff); + } + } + return rc; + } + + /** + * Sets the blocksize. + * + * @param blocksize + * the new blocksize. Values < 1 will be set to 1 + */ + public void setBlocksize(int blocksize) { + this.blocksize = blocksize; + } + + /** + * Sets the current length. + * + * @param length + * < 0: the length is set to 0
+ * otherwise: the new length + * @return the instance (for chaining) + */ + public DynBytes setLength(int length) { + if (length > this.buffer.length) { + ensureSize(length); + } + this.length = length; + return this; + } + + /** + * Converts the content into an UTF-8 string. + * + * @return null: not convertable
+ * otherwise: the content as UTF-8 string + */ + public String toUtf8() { + String rc = null; + try { + rc = new String(this.buffer, 0, this.length, "UTF-8"); + } catch (final UnsupportedEncodingException e) { + DynBytes.logger.error("DynBytes.toUtf8(): ", e); + } + return rc; + } + + /** + * Converts the content into an UTF-8 string. + * + * @param maxLength + * the maximal count of bytes to convert + * @return null: not convertible
+ * otherwise: the content as UTF-8 string + */ + public String toUtf8(int maxLength) { + String rc = null; + if (maxLength <= 0) { + rc = ""; + } else { + try { + final int length = Math.min(this.length, maxLength); + rc = new String(this.buffer, 0, length, "UTF-8"); + } catch (final UnsupportedEncodingException e) { + DynBytes.logger.error("DynBytes.toUtf8(): ", e); + } + } + return rc; + } +} diff --git a/src/main/resources/logback.xml b/src/main/resources/logback.xml new file mode 100644 index 0000000..dd6a690 --- /dev/null +++ b/src/main/resources/logback.xml @@ -0,0 +1,16 @@ + + + + + + %d{HH:mm:ss.SSS} %-5level %logger{36} - %msg%n + + + + + + + + + + \ No newline at end of file diff --git a/src/test/java/de/republib/util/DynBytesTest.java b/src/test/java/de/republib/util/DynBytesTest.java new file mode 100644 index 0000000..194ccfb --- /dev/null +++ b/src/test/java/de/republib/util/DynBytesTest.java @@ -0,0 +1,196 @@ +package de.republib.util; + +import org.testng.Assert; + +/** + * Tests the class DynBytes. + */ +public class DynBytesTest { + @org.testng.annotations.BeforeMethod + public void setUp() throws Exception { + + } + + @org.testng.annotations.AfterMethod + public void tearDown() throws Exception { + + } + + @org.testng.annotations.Test + public void testAppend() throws Exception { + // append(buffer, offset, length): + final DynBytes buffer = new DynBytes(16, 16); + final byte[] buffer2 = { 0x41, 0x31, 0x61 }; + // offset and length in limits, chaining: + buffer.append(buffer2, 0, 3).append(buffer2, 1, 1); + Assert.assertEquals(buffer.toUtf8(), "A1a1"); + + // length to large: + buffer.append(buffer2, 2, 2); + Assert.assertEquals(buffer.toUtf8(), "A1a1a"); + + // offset out of range: + // too small: + buffer.append(buffer2, -1, 2); + Assert.assertEquals(buffer.toUtf8(), "A1a1a"); + // too large: + buffer.append(buffer2, 3, 2); + Assert.assertEquals(buffer.toUtf8(), "A1a1a"); + } + + @org.testng.annotations.Test + public void testAppend2() throws Exception { + // append(String): + final DynBytes buffer = new DynBytes(16, 16); + // standard use, chaining: + buffer.append("Hi").append(" ").append("world"); + Assert.assertEquals(buffer.toUtf8(), "Hi world"); + // umlauts: + buffer.append(": ").append("äöü").append("ß"); + Assert.assertEquals(buffer.toUtf8(), "Hi world: äöüß"); + + // string out of range: + buffer.append(null); + Assert.assertEquals(buffer.toUtf8(), "Hi world: äöüß"); + } + + @org.testng.annotations.Test + public void testAppend3() throws Exception { + final DynBytes buffer = new DynBytes(16, 16); + buffer.append((byte) 0x32).append((byte) 0x43); + Assert.assertEquals(buffer.toUtf8(), "2C"); + } + + @org.testng.annotations.Test + public void testAppendLittleEndian() throws Exception { + // appendLittleEndian(int value): + final DynBytes buffer = new DynBytes(16, 16); + buffer.appendLittleEndian(0x87654321, 4); + Assert.assertEquals(buffer.hexDump(0, 99, 99), "21 43 65 87\n"); + } + + @org.testng.annotations.Test + public void testAppendLittleEndian2() throws Exception { + // appendLittleEndian(long value): + final DynBytes buffer = new DynBytes(16, 16); + buffer.appendLittleEndian(0xfedcba8765432190L, 8).appendLittleEndian(0x7788112233L, 3); + Assert.assertEquals(buffer.hexDump(0, 99, 99), "90 21 43 65 87 ba dc fe 33 22 11\n"); + } + + @org.testng.annotations.Test + public void testBasic() throws Exception { + final DynBytes buffer = new DynBytes(10, 16); + Assert.assertEquals(buffer.getCapacity(), 10); + Assert.assertEquals(buffer.getBlocksize(), 16); + Assert.assertEquals(buffer.length, 0); + buffer.setBlocksize(17); + Assert.assertEquals(buffer.getBlocksize(), 17); + buffer.setBlocksize(17); + Assert.assertEquals(buffer.getBlocksize(), 17); + } + + @org.testng.annotations.Test + public void testEnsureSize() throws Exception { + final DynBytes buffer = new DynBytes(2, 2); + Assert.assertEquals(buffer.getCapacity(), 2); + Assert.assertEquals(buffer.getBlocksize(), 2); + + buffer.append("."); + Assert.assertEquals(buffer.getLength(), 1); + Assert.assertEquals(buffer.getCapacity(), 2); + + buffer.append(":"); + Assert.assertEquals(buffer.getLength(), 2); + Assert.assertEquals(buffer.getCapacity(), 2); + + buffer.append("!"); + Assert.assertEquals(buffer.getLength(), 3); + Assert.assertEquals(buffer.getCapacity(), 4); + + buffer.setBlocksize(4); + buffer.append("4"); + Assert.assertEquals(buffer.getLength(), 4); + Assert.assertEquals(buffer.getCapacity(), 4); + + buffer.append("5678"); + Assert.assertEquals(buffer.getLength(), 8); + Assert.assertEquals(buffer.getCapacity(), 8); + + buffer.append("."); + Assert.assertEquals(buffer.getLength(), 9); + Assert.assertEquals(buffer.getCapacity(), 12); + } + + @org.testng.annotations.Test + public void testHexDump() { + final byte[] bytes = { 0x1, (byte) 0xae, 0x20, (byte) 0xbc, 0x12 }; + Assert.assertEquals(DynBytes.hexDump(bytes, 1, 1, 2), "ae\n"); + // 2 full lines: + Assert.assertEquals(DynBytes.hexDump(bytes, 0, 4, 2), "01 ae\n20 bc\n"); + // 2 full and a half line: + Assert.assertEquals(DynBytes.hexDump(bytes, 0, 5, 2), "01 ae\n20 bc\n12\n"); + // too long length: + Assert.assertEquals(DynBytes.hexDump(bytes, 4, 99, 2), "12\n"); + } + + @org.testng.annotations.Test + public void testIntAsLittleEndian() { + final DynBytes buffer = new DynBytes(16, 2); + buffer.appendLittleEndian(0x1234567890abcdefL, 8); + Assert.assertEquals(buffer.intAsLittleEndian(0, 1, -1), 0xef); + Assert.assertEquals(buffer.intAsLittleEndian(0, 3, -1), 0xabcdef); + Assert.assertEquals(buffer.intAsLittleEndian(1, 4, -1), 0x7890abcd); + Assert.assertEquals(buffer.intAsLittleEndian(6, 2, -1), 0x1234); + + // wrong index: + Assert.assertEquals(buffer.intAsLittleEndian(-1, 3, -2), -2); + Assert.assertEquals(buffer.intAsLittleEndian(6, 4, -3), -3); + Assert.assertEquals(buffer.intAsLittleEndian(8, 1, -4), -4); + + // wrong width: + Assert.assertEquals(buffer.intAsLittleEndian(0, 0, -5), -5); + Assert.assertEquals(buffer.intAsLittleEndian(0, 5, -6), -6); + } + + @org.testng.annotations.Test + public void testLongAsLittleEndian() { + final DynBytes buffer = new DynBytes(16, 2); + buffer.appendLittleEndian(0x1234567890abcdefL, 8); + Assert.assertEquals(buffer.longAsLittleEndian(0, 1, -1), 0xefL); + Assert.assertEquals(buffer.longAsLittleEndian(0, 3, -1), 0xabcdefL); + Assert.assertEquals(buffer.longAsLittleEndian(1, 4, -1), 0x7890abcdL); + Assert.assertEquals(buffer.longAsLittleEndian(6, 2, -1), 0x1234L); + + // wrong index: + Assert.assertEquals(buffer.intAsLittleEndian(-1, 3, -2), -2); + Assert.assertEquals(buffer.intAsLittleEndian(6, 4, -3), -3); + Assert.assertEquals(buffer.intAsLittleEndian(8, 1, -4), -4); + + // wrong width: + Assert.assertEquals(buffer.intAsLittleEndian(0, 0, -5), -5); + Assert.assertEquals(buffer.intAsLittleEndian(0, 5, -6), -6); + } + + @org.testng.annotations.Test + public void testToHex() { + Assert.assertEquals(DynBytes.toHex(0), '0'); + Assert.assertEquals(DynBytes.toHex(9), '9'); + Assert.assertEquals(DynBytes.toHex(10), 'a'); + Assert.assertEquals(DynBytes.toHex(15), 'f'); + } + + @org.testng.annotations.Test + public void testUtf8() { + final DynBytes buffer = new DynBytes(2, 2); + final String text = "Öfter über Äpfel"; + final String text2 = " gießen"; + buffer.append(text).append(text2); + Assert.assertEquals(buffer.toUtf8(), text + text2); + Assert.assertEquals(buffer.toUtf8(text.length() + 3 /* 3 Umlaut */), text); + Assert.assertEquals(buffer.toUtf8(99), text + text2); + // wrong maxLength: + Assert.assertEquals(buffer.toUtf8(-1), ""); + Assert.assertEquals(buffer.toUtf8(0), ""); + } + +} \ No newline at end of file -- 2.39.5