--- /dev/null
+target
+test-output
+*.iml
+.project
+.classpath
+.idea
+.settings
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+
+ <modelVersion>4.0.0</modelVersion>
+
+ <groupId>de.republlb</groupId>
+ <artifactId>pinet</artifactId>
+ <packaging>jar</packaging>
+ <version>1.0-SNAPSHOT</version>
+
+ <name>Controlling a Raspberry Pi</name>
+ <url>https://sourcefourge.net</url>
+
+ <properties>
+ <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+ <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
+ <logback.version>1.1.7</logback.version>
+ <slf4j.version>1.7.21</slf4j.version>
+ </properties>
+
+ <dependencies>
+ <!-- logging -->
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-api</artifactId>
+ <version>${slf4j.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>ch.qos.logback</groupId>
+ <artifactId>logback-classic</artifactId>
+ <version>${logback.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>ch.qos.logback</groupId>
+ <artifactId>logback-core</artifactId>
+ <version>${logback.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.testng</groupId>
+ <artifactId>testng</artifactId>
+ <version>6.9.8</version>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <defaultGoal>install</defaultGoal>
+
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-compiler-plugin</artifactId>
+ <version>3.5.1</version>
+ <configuration>
+ <source>1.7</source>
+ <target>1.7</target>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-resources-plugin</artifactId>
+ <version>2.6</version>
+ <configuration>
+ <encoding>UTF-8</encoding>
+ </configuration>
+ </plugin>
+
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-surefire-plugin</artifactId>
+ <version>2.12.4</version>
+ <configuration>
+ <parallel>methods</parallel>
+ <threadCount>4</threadCount>
+ </configuration>
+ </plugin>
+ <!-- Allows the example to be run via 'mvn compile exec:java' -->
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>exec-maven-plugin</artifactId>
+ <version>1.4.0</version>
+ <configuration>
+ <mainClass>de.republlb.MainApp</mainClass>
+ <includePluginDependencies>false</includePluginDependencies>
+ </configuration>
+ </plugin>
+
+ </plugins>
+ </build>
+
+</project>
--- /dev/null
+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 <i>buffer</i> 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);
+ }
+ }
+ }
+}
--- /dev/null
+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);
+ }
+}
--- /dev/null
+/**
+ *
+ */
+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;
+ }
+}
--- /dev/null
+/**
+ *
+ */
+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;
+ }
+}
--- /dev/null
+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 <i>ensureSize()</i>.
+ *
+ * The storage space is managed with a <i>blocksize</i>: 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<br>
+ * otherwise: the content as byte hexadecimal dump<br>
+ * 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 <i>buffer</i> to add<br>
+ * <i>0 <= offset < buffer.length</i><br>
+ * If offset is out of range nothing will be done
+ * @param length
+ * the number of bytes to add<br>
+ * 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 <i>null</i>
+ * @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.
+ * <p>
+ * 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<br>
+ * otherwise: the content as byte hexadecimal dump<br>
+ * 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 <i>width</i> 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 <i>width</i> 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<br>
+ * 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 <i>null</i>: not convertable<br>
+ * 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 <i>null</i>: not convertible<br>
+ * 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;
+ }
+}
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<configuration>
+
+ <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
+ <layout class="ch.qos.logback.classic.PatternLayout">
+ <Pattern>%d{HH:mm:ss.SSS} %-5level %logger{36} - %msg%n</Pattern>
+ </layout>
+ </appender>
+
+ <logger name="com.base22" level="TRACE"/>
+
+
+ <root level="debug">
+ <appender-ref ref="STDOUT" />
+ </root>
+</configuration>
\ No newline at end of file
--- /dev/null
+package de.republib.util;
+
+import org.testng.Assert;
+
+/**
+ * Tests the class <i>DynBytes</i>.
+ */
+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