]> gitweb.hamatoma.de Git - jpinet/commitdiff
initial state
authorhama <hama@siduction.net>
Sun, 3 Jul 2016 06:36:27 +0000 (08:36 +0200)
committerhama <hama@siduction.net>
Sun, 3 Jul 2016 06:40:49 +0000 (08:40 +0200)
.gitignore [new file with mode: 0644]
pom.xml [new file with mode: 0644]
src/main/java/de/republib/net/TcpClient.java [new file with mode: 0644]
src/main/java/de/republib/pinet/Client.java [new file with mode: 0644]
src/main/java/de/republib/pinet/GpioClient.java [new file with mode: 0644]
src/main/java/de/republib/pinet/PinNumber.java [new file with mode: 0644]
src/main/java/de/republib/util/DynBytes.java [new file with mode: 0644]
src/main/resources/logback.xml [new file with mode: 0644]
src/test/java/de/republib/util/DynBytesTest.java [new file with mode: 0644]

diff --git a/.gitignore b/.gitignore
new file mode 100644 (file)
index 0000000..ff41283
--- /dev/null
@@ -0,0 +1,7 @@
+target
+test-output
+*.iml
+.project
+.classpath
+.idea
+.settings
diff --git a/pom.xml b/pom.xml
new file mode 100644 (file)
index 0000000..20b04d8
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,92 @@
+<?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>
diff --git a/src/main/java/de/republib/net/TcpClient.java b/src/main/java/de/republib/net/TcpClient.java
new file mode 100644 (file)
index 0000000..87c7130
--- /dev/null
@@ -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 <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);
+                       }
+               }
+       }
+}
diff --git a/src/main/java/de/republib/pinet/Client.java b/src/main/java/de/republib/pinet/Client.java
new file mode 100644 (file)
index 0000000..55030c9
--- /dev/null
@@ -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 (file)
index 0000000..010fd7f
--- /dev/null
@@ -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 (file)
index 0000000..09b2260
--- /dev/null
@@ -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 (file)
index 0000000..2ab43a5
--- /dev/null
@@ -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 <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;
+       }
+}
diff --git a/src/main/resources/logback.xml b/src/main/resources/logback.xml
new file mode 100644 (file)
index 0000000..dd6a690
--- /dev/null
@@ -0,0 +1,16 @@
+<?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
diff --git a/src/test/java/de/republib/util/DynBytesTest.java b/src/test/java/de/republib/util/DynBytesTest.java
new file mode 100644 (file)
index 0000000..194ccfb
--- /dev/null
@@ -0,0 +1,196 @@
+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