package cz.crcs.ectester.common.ec; import cz.crcs.ectester.common.util.ByteUtil; import java.io.*; import java.util.*; import java.util.regex.Pattern; /** * A list of byte arrays for holding EC data. *

* The data can be read from a byte array via readBytes(), from a CSV via readCSV(). * The data can be exported to a byte array via flatten() or to a string array via expand(). * * @author Jan Jancar johny@neuromancer.sk */ public abstract class EC_Data { String id; int count; byte[][] data; private static final Pattern HEX = Pattern.compile("(0x|0X)?[a-fA-F\\d]+"); EC_Data() { } EC_Data(int count) { this.count = count; this.data = new byte[count][]; } EC_Data(byte[][] data) { this.count = data.length; this.data = data; } EC_Data(String id, int count) { this(count); this.id = id; } EC_Data(String id, byte[][] data) { this(data); this.id = id; } public String getId() { return id; } public int getCount() { return count; } public byte[][] getData() { return data; } public byte[] getData(int index) { return data[index]; } public boolean hasData() { return data != null; } public byte[] flatten() { ByteArrayOutputStream out = new ByteArrayOutputStream(); for (byte[] param : data) { byte[] length = new byte[2]; ByteUtil.setShort(length, 0, (short) param.length); out.write(length, 0, 2); out.write(param, 0, param.length); } return out.toByteArray(); } public String[] expand() { List out = new ArrayList<>(count); for (byte[] param : data) { out.add(ByteUtil.bytesToHex(param, false)); } return out.toArray(new String[out.size()]); } private static byte[] pad(byte[] data) { if (data.length == 1) { return new byte[]{(byte) 0, data[0]}; } else if (data.length == 0 || data.length > 2) { return data; } return null; } private static byte[] parse(String param) { byte[] data; if (param.startsWith("0x") || param.startsWith("0X")) { data = ByteUtil.hexToBytes(param.substring(2)); } else { data = ByteUtil.hexToBytes(param); } if (data == null) return new byte[0]; if (data.length < 2) return pad(data); return data; } private boolean readHex(String[] hex) { if (hex.length != count) { return false; } for (int i = 0; i < count; ++i) { this.data[i] = parse(hex[i]); } return true; } public boolean readCSV(InputStream in) { Scanner s = new Scanner(in); s.useDelimiter(",|;"); List data = new LinkedList<>(); while (s.hasNext()) { String field = s.next(); data.add(field.replaceAll("\\s+", "")); } if (data.isEmpty()) { return false; } for (String param : data) { if (!HEX.matcher(param).matches()) { return false; } } return readHex(data.toArray(new String[data.size()])); } public boolean readBytes(byte[] bytes) { if (bytes == null) { return false; } int offset = 0; for (int i = 0; i < count; i++) { if (bytes.length - offset < 2) { return false; } short paramLength = ByteUtil.getShort(bytes, offset); offset += 2; if (bytes.length < offset + paramLength) { return false; } data[i] = new byte[paramLength]; System.arraycopy(bytes, offset, data[i], 0, paramLength); offset += paramLength; } return true; } public boolean readByteArray(byte[][] bytes) { if (bytes == null || count != bytes.length) { return false; } for (int i = 0; i < count; ++i) { data[i] = new byte[bytes[i].length]; System.arraycopy(bytes[i], 0, data[i], 0, bytes[i].length); } return true; } public void writeCSV(OutputStream out) throws IOException { Writer w = new OutputStreamWriter(out); w.write(String.join(",", expand())); w.flush(); } @Override public String toString() { return String.join(",", expand()); } @Override public boolean equals(Object obj) { if (obj instanceof EC_Data) { EC_Data other = (EC_Data) obj; if (this.id != null || other.id != null) { return Objects.equals(this.id, other.id); } if (this.count != other.count) return false; for (int i = 0; i < this.count; ++i) { if (!Arrays.equals(this.data[i], other.data[i])) { return false; } } return true; } else { return false; } } @Override public int hashCode() { if (this.id != null) { return this.id.hashCode(); } return Arrays.deepHashCode(this.data); } }