Skip to content

Commit

Permalink
Adds (de)serialization of DataPoints to byte array.
Browse files Browse the repository at this point in the history
  • Loading branch information
RobAtticus committed Aug 4, 2015
1 parent 28bbf4b commit 67a0ea3
Show file tree
Hide file tree
Showing 2 changed files with 204 additions and 112 deletions.
65 changes: 65 additions & 0 deletions src/main/java/com/iobeam/api/resource/DataPoint.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import org.json.JSONObject;

import java.io.Serializable;
import java.nio.ByteBuffer;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.List;
Expand All @@ -16,6 +17,14 @@ public class DataPoint implements Serializable {
private static final String KEY_TIME = "time";
private static final String KEY_VALUE = "value";

public static final byte TYPE_LONG = 0x0;
public static final byte TYPE_DOUBLE = TYPE_LONG + 1;
public static final byte TYPE_STRING = TYPE_DOUBLE + 1;
/* Byte array format, all start with [time (8)][type (1)] (9 bytes), followed by:
- for int/long/float/double: [value (8)] (17 total bytes)
- for strings: [len (4)][value (X)] (13 + X total bytes, X = len of str)
*/

private final long time;
private final Object data;

Expand Down Expand Up @@ -162,6 +171,62 @@ public JSONObject toJson() {
return ret;
}

public static DataPoint fromByteArray(byte[] array) {
ByteBuffer buf = ByteBuffer.wrap(array);
long timestamp = buf.getLong();
byte type = buf.get();
if (type == TYPE_LONG) {
return new DataPoint(timestamp, buf.getLong());
} else if (type == TYPE_DOUBLE) {
return new DataPoint(timestamp, buf.getDouble());
} else {
int len = buf.getInt();
byte[] bytes = new byte[len];
buf.get(bytes, 0, len);
return new DataPoint(timestamp, new String(bytes));
}
}

/**
* Returns the byte array representation of this data point. Byte array format are as follows:
*
* Starts with 8 bytes for time and 1 byte for type for a total of 9 bytes. Then, based on value
* type, it does the following:
* - int/long/float/double: 8 bytes for the value, for 17 total bytes.
* - strings: 4 bytes for string length, then X bytes for string for a total of 13 + X bytes.
*
* @return Byte array representation
*/
public byte[] toByteArray() {
int arrSize = 1 + Long.BYTES; // first byte specifies type of data
byte type;
if (data instanceof Long) {
arrSize += Long.BYTES;
type = TYPE_LONG;
} else if (data instanceof Double) {
arrSize += Double.BYTES;
type = TYPE_DOUBLE;
} else {
arrSize += Integer.BYTES + ((String) data).length();
type = TYPE_STRING;
}

ByteBuffer buf = ByteBuffer.allocate(arrSize);
buf.putLong(time);
buf.put(type);
if (type == TYPE_LONG) {
buf.putLong((Long) data);
} else if (type == TYPE_DOUBLE) {
buf.putDouble((Double) data);
} else {
String temp = (String) data;
buf.putInt(temp.length());
buf.put(temp.getBytes());
}
buf.flip();
return buf.array();
}

@Override
public int hashCode() {
return (int) this.time;
Expand Down
251 changes: 139 additions & 112 deletions src/test/java/com/iobeam/api/resource/DataPointTest.java
Original file line number Diff line number Diff line change
@@ -1,112 +1,139 @@
package com.iobeam.api.resource;

import org.json.JSONObject;
import org.junit.Test;

import java.util.List;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;

public class DataPointTest {

private static final String jsonDataPoint = "{\n"
+ " \"time\": 123456789,\n"
+ " \"value\": 100\n"
+ "}";


private static final String jsonDataPoint2 = "{\n"
+ " \"time\": 123456789,\n"
+ " \"value\": 100.51\n"
+ "}";


private static final String jsonDataPoint3 = "{\n"
+ " \"time\": 123456789,\n"
+ " \"value\": \"110\"\n"
+ "}";

private static final String csvInts = "1,10,100,1000,";
private static final String otherReals = "1.0&2&5.0&10.";

@Test
public void testFromJson() throws Exception {
DataPoint dataPoint = DataPoint.fromJson(new JSONObject(jsonDataPoint));
assertEquals(123456789, dataPoint.getTime());
Object val = dataPoint.getValue();
assertTrue(val instanceof Long);
assertEquals(100, ((Long) val).longValue());

dataPoint = DataPoint.fromJson(new JSONObject(jsonDataPoint2));
assertEquals(123456789, dataPoint.getTime());
val = dataPoint.getValue();
assertTrue(val instanceof Double);
assertEquals(100.51, ((Double) val).doubleValue(), .05);

dataPoint = DataPoint.fromJson(new JSONObject(jsonDataPoint3));
assertEquals(123456789, dataPoint.getTime());
val = dataPoint.getValue();
assertTrue(val instanceof String);
assertEquals("110", val);

}

@Test
public void testToJson() throws Exception {
long now = System.currentTimeMillis();

DataPoint dataPointInt = new DataPoint(now, 10);
JSONObject json = dataPointInt.toJson();
assertEquals(now, json.getLong("time"));
assertEquals(10, json.getInt("value"));

DataPoint dataPointDouble = new DataPoint(now, 10.5);
json = dataPointDouble.toJson();
assertEquals(now, json.getLong("time"));
assertEquals(10.5, json.getDouble("value"), .1);

DataPoint dataPointString = new DataPoint(now, "11.2");
json = dataPointString.toJson();
assertEquals(now, json.getLong("time"));
assertEquals("11.2", json.getString("value"));
}

// REMOVE IN NEXT RELEASE

@Test
public void testCsvIntParse() throws Exception {
long ts = 1001;
List<DataPoint> list = DataPoint.parseDataPoints(csvInts, ",", Long.class, ts);
assertEquals(4, list.size());
for (DataPoint d : list) {
assertEquals(ts, d.getTime());
}
assertEquals(1l, list.get(0).getValue());
assertEquals(10l, list.get(1).getValue());
assertEquals(100l, list.get(2).getValue());
assertEquals(1000l, list.get(3).getValue());
}

@Test
public void testRealParse() throws Exception {
long ts = 2001;
List<DataPoint> list = DataPoint.parseDataPoints(otherReals, "&", Double.class, ts);
assertEquals(4, list.size());
for (DataPoint d : list) {
assertEquals(ts, d.getTime());
}
assertEquals(1d, list.get(0).getValue());
assertEquals(2d, list.get(1).getValue());
assertEquals(5d, list.get(2).getValue());
assertEquals(10d, list.get(3).getValue());
}

@Test
public void testInvalidTypeParse() throws Exception {
// Ideally, this call would be rejected as List is not an appropriate type, but we'll
// leave that as a TODO.
List<DataPoint> list = DataPoint.parseDataPoints(csvInts, ",", List.class);
assertEquals(0, list.size());
}
}
package com.iobeam.api.resource;

import org.json.JSONObject;
import org.junit.Test;

import java.nio.ByteBuffer;
import java.util.List;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;

public class DataPointTest {

private static final String jsonDataPoint = "{\n"
+ " \"time\": 123456789,\n"
+ " \"value\": 100\n"
+ "}";


private static final String jsonDataPoint2 = "{\n"
+ " \"time\": 123456789,\n"
+ " \"value\": 100.51\n"
+ "}";


private static final String jsonDataPoint3 = "{\n"
+ " \"time\": 123456789,\n"
+ " \"value\": \"110\"\n"
+ "}";

private static final String csvInts = "1,10,100,1000,";
private static final String otherReals = "1.0&2&5.0&10.";

@Test
public void testFromJson() throws Exception {
DataPoint dataPoint = DataPoint.fromJson(new JSONObject(jsonDataPoint));
assertEquals(123456789, dataPoint.getTime());
Object val = dataPoint.getValue();
assertTrue(val instanceof Long);
assertEquals(100, ((Long) val).longValue());

dataPoint = DataPoint.fromJson(new JSONObject(jsonDataPoint2));
assertEquals(123456789, dataPoint.getTime());
val = dataPoint.getValue();
assertTrue(val instanceof Double);
assertEquals(100.51, ((Double) val).doubleValue(), .05);

dataPoint = DataPoint.fromJson(new JSONObject(jsonDataPoint3));
assertEquals(123456789, dataPoint.getTime());
val = dataPoint.getValue();
assertTrue(val instanceof String);
assertEquals("110", val);

}

@Test
public void testToJson() throws Exception {
long now = System.currentTimeMillis();

DataPoint dataPointInt = new DataPoint(now, 10);
JSONObject json = dataPointInt.toJson();
assertEquals(now, json.getLong("time"));
assertEquals(10, json.getInt("value"));

DataPoint dataPointDouble = new DataPoint(now, 10.5);
json = dataPointDouble.toJson();
assertEquals(now, json.getLong("time"));
assertEquals(10.5, json.getDouble("value"), .1);

DataPoint dataPointString = new DataPoint(now, "11.2");
json = dataPointString.toJson();
assertEquals(now, json.getLong("time"));
assertEquals("11.2", json.getString("value"));
}

@Test
public void testToByteArray() throws Exception {
long now = System.currentTimeMillis();

DataPoint dataPointInt = new DataPoint(now, 10);
ByteBuffer buf = ByteBuffer.wrap(dataPointInt.toByteArray());
assertEquals(now, buf.getLong());
buf.get(); // skip type
assertEquals(10, buf.getLong());

DataPoint dataPointDouble = new DataPoint(now, 10.5);
buf = ByteBuffer.wrap(dataPointDouble.toByteArray());
assertEquals(now, buf.getLong());
buf.get(); // skip type
assertEquals(10.5, buf.getDouble(), .1);

DataPoint dataPointString = new DataPoint(now, "11.2");
buf = ByteBuffer.wrap(dataPointString.toByteArray());
assertEquals(now, buf.getLong());
buf.get(); // skip type
int strSize = buf.getInt();
byte[] temp = new byte[strSize];
buf.get(temp, 0, strSize);
assertEquals("11.2", new String(temp));
}

// REMOVE IN NEXT RELEASE

@Test
public void testCsvIntParse() throws Exception {
long ts = 1001;
List<DataPoint> list = DataPoint.parseDataPoints(csvInts, ",", Long.class, ts);
assertEquals(4, list.size());
for (DataPoint d : list) {
assertEquals(ts, d.getTime());
}
assertEquals(1l, list.get(0).getValue());
assertEquals(10l, list.get(1).getValue());
assertEquals(100l, list.get(2).getValue());
assertEquals(1000l, list.get(3).getValue());
}

@Test
public void testRealParse() throws Exception {
long ts = 2001;
List<DataPoint> list = DataPoint.parseDataPoints(otherReals, "&", Double.class, ts);
assertEquals(4, list.size());
for (DataPoint d : list) {
assertEquals(ts, d.getTime());
}
assertEquals(1d, list.get(0).getValue());
assertEquals(2d, list.get(1).getValue());
assertEquals(5d, list.get(2).getValue());
assertEquals(10d, list.get(3).getValue());
}

@Test
public void testInvalidTypeParse() throws Exception {
// Ideally, this call would be rejected as List is not an appropriate type, but we'll
// leave that as a TODO.
List<DataPoint> list = DataPoint.parseDataPoints(csvInts, ",", List.class);
assertEquals(0, list.size());
}
}

0 comments on commit 67a0ea3

Please sign in to comment.