6 package com.google.appinventor.components.runtime.util;
8 import java.util.ArrayList;
9 import java.nio.ByteBuffer;
10 import java.nio.ByteOrder;
11 import java.io.UnsupportedEncodingException;
22 private static byte PRIMPAR_SHORT = (byte) 0x00;
23 private static byte PRIMPAR_LONG = (byte) 0x80;
25 private static byte PRIMPAR_CONST = (byte) 0x00;
26 private static byte PRIMPAR_VARIABEL = (byte) 0x40;
27 private static byte PRIMPAR_LOCAL = (byte) 0x00;
28 private static byte PRIMPAR_GLOBAL = (byte) 0x20;
29 private static byte PRIMPAR_HANDLE = (byte) 0x10;
30 private static byte PRIMPAR_ADDR = (byte) 0x08;
32 private static byte PRIMPAR_INDEX = (byte) 0x1F;
33 private static byte PRIMPAR_CONST_SIGN = (byte) 0x20;
34 private static byte PRIMPAR_VALUE = (byte) 0x3F;
36 private static byte PRIMPAR_BYTES = (byte) 0x07;
38 private static byte PRIMPAR_STRING_OLD = (byte) 0;
39 private static byte PRIMPAR_1_BYTE = (byte) 1;
40 private static byte PRIMPAR_2_BYTES = (byte) 2;
41 private static byte PRIMPAR_4_BYTES = (byte) 3;
42 private static byte PRIMPAR_STRING = (byte) 4;
44 private static class FormatLiteral {
48 public FormatLiteral(
char symbol,
int size) {
54 public static byte[]
pack(String format, Object... values)
throws IllegalArgumentException {
56 String[] formatTokens = format.split(
"(?<=\\D)");
57 FormatLiteral[] literals =
new FormatLiteral[formatTokens.length];
59 int bufferCapacity = 0;
62 for (
int i = 0; i < formatTokens.length; i++) {
63 String token = formatTokens[i];
64 char symbol = token.charAt(token.length() - 1);
66 boolean sizeSpecified =
false;
68 if (token.length() != 1)
70 size = Integer.parseInt(token.substring(0, token.length() - 1));
74 throw new IllegalArgumentException(
"Illegal format string");
79 bufferCapacity += size;
83 bufferCapacity += size;
88 bufferCapacity += size;
93 bufferCapacity += size * 2;
98 bufferCapacity += size * 2;
103 bufferCapacity += size * 4;
108 bufferCapacity += size * 4;
113 bufferCapacity += size * 8;
118 bufferCapacity += size * 8;
123 bufferCapacity += size * 4;
128 bufferCapacity += size * 4;
133 if (size != ((String) values[index]).length())
134 throw new IllegalArgumentException(
"Illegal format string");
136 bufferCapacity += size;
142 throw new IllegalArgumentException(
"Illegal format string");
144 bufferCapacity += ((String) values[index]).length() + 1;
149 throw new IllegalArgumentException(
"Illegal format string");
152 literals[i] =
new FormatLiteral(symbol, size);
155 if (index != values.length)
156 throw new IllegalArgumentException(
"Illegal format string");
160 ByteBuffer buffer = ByteBuffer.allocate(bufferCapacity);
161 buffer.order(ByteOrder.LITTLE_ENDIAN);
163 for (FormatLiteral literal: literals) {
164 switch (literal.symbol) {
166 for (
int i = 0; i < literal.size; i++)
167 buffer.put((
byte) 0x00);
171 for (
int i = 0; i < literal.size; i++) {
172 buffer.put((Byte) values[index]);
178 buffer.put((
byte[]) values[index]);
183 for (
int i = 0; i < literal.size; i++) {
184 buffer.putShort((Short) values[index]);
190 for (
int i = 0; i < literal.size; i++) {
191 buffer.putShort(((
short[]) values[index])[i]);
197 for (
int i = 0; i < literal.size; i++) {
198 buffer.putInt((Integer) values[index]);
204 for (
int i = 0; i < literal.size; i++) {
205 buffer.putInt(((
int[]) values[index])[i]);
211 for (
int i = 0; i < literal.size; i++) {
212 buffer.putLong((Long) values[index]);
218 for (
int i = 0; i < literal.size; i++) {
219 buffer.putLong(((
long[]) values[index])[i]);
225 for (
int i = 0; i < literal.size; i++) {
226 buffer.putFloat((Float) values[index]);
232 for (
int i = 0; i < literal.size; i++) {
233 buffer.putFloat(((
float[]) values[index])[i]);
240 buffer.put(((String) values[index]).getBytes(
"US-ASCII"));
241 }
catch (UnsupportedEncodingException e) {
242 throw new IllegalArgumentException();
249 buffer.put(((String) values[index]).getBytes(
"US-ASCII"));
250 }
catch (UnsupportedEncodingException e) {
251 throw new IllegalArgumentException();
253 buffer.put((
byte) 0x00);
258 return buffer.array();
261 public static Object[]
unpack(String format,
byte[] bytes)
throws IllegalArgumentException {
262 String[] formatTokens = format.split(
"(?<=\\D)");
263 ArrayList<Object> decodedObjects =
new ArrayList<Object>();
264 ByteBuffer buffer = ByteBuffer.wrap(bytes);
265 buffer.order(ByteOrder.LITTLE_ENDIAN);
267 for (String token: formatTokens) {
268 boolean sizeSpecified =
false;
270 char symbol = token.charAt(token.length() - 1);
272 if (token.length() > 1) {
273 sizeSpecified =
true;
274 size = Integer.parseInt(token.substring(0, token.length() - 1));
277 throw new IllegalArgumentException(
"Illegal format string");
282 for (
int i = 0; i < size; i++)
287 for (
int i = 0; i < size; i++)
288 decodedObjects.add(buffer.get());
292 byte[] byteArray =
new byte[size];
293 buffer.get(byteArray, 0, size);
294 decodedObjects.add(byteArray);
298 for (
int i = 0; i < size; i++)
299 decodedObjects.add(buffer.getShort());
303 short[] shorts =
new short[size];
304 for (
short i = 0; i < size; i++)
305 shorts[i] = buffer.getShort();
306 decodedObjects.add(shorts);
310 for (
int i = 0; i < size; i++)
311 decodedObjects.add(buffer.getInt());
315 int[] integers =
new int[size];
316 for (
int i = 0; i < size; i++)
317 integers[i] = buffer.getInt();
318 decodedObjects.add(integers);
322 for (
int i = 0; i < size; i++)
323 decodedObjects.add(buffer.getLong());
327 long[] longs =
new long[size];
328 for (
int i = 0; i < size; i++)
329 longs[i] = buffer.getLong();
330 decodedObjects.add(longs);
334 for (
int i = 0; i < size; i++)
335 decodedObjects.add(buffer.getFloat());
339 float[] floats =
new float[size];
340 for (
int i = 0; i < size; i++)
341 floats[i] = buffer.getFloat();
342 decodedObjects.add(floats);
346 byte[] byteString =
new byte[size];
347 buffer.get(byteString, 0, size);
349 decodedObjects.add(
new String(byteString,
"US-ASCII"));
350 }
catch (UnsupportedEncodingException e) {
351 throw new IllegalArgumentException();
357 throw new IllegalArgumentException(
"Illegal format string");
359 StringBuffer stringBuffer =
new StringBuffer();
362 byte b = buffer.get();
363 if (b != (
byte) 0x00)
364 stringBuffer.append((
char) b);
369 decodedObjects.add(stringBuffer.toString());
374 throw new IllegalArgumentException(
"Illegal format string");
376 if (buffer.hasRemaining())
377 throw new IllegalArgumentException(
"Illegal format string");
380 throw new IllegalArgumentException(
"Illegal format string");
384 return decodedObjects.toArray();
388 if (v < -31 || v > 31)
389 throw new IllegalArgumentException(
"Encoded value must be in range [0, 127]");
391 return new byte[] { (byte) (v & PRIMPAR_VALUE) };
395 return new byte[] {(byte) ((
byte) (PRIMPAR_LONG | PRIMPAR_CONST) | PRIMPAR_1_BYTE),
400 return new byte[] {(byte) ((
byte) (PRIMPAR_LONG | PRIMPAR_CONST) | PRIMPAR_2_BYTES),
402 (byte) ((v >>> 8) & 0xFF)};
406 return new byte[] {(byte) ((
byte) (PRIMPAR_LONG | PRIMPAR_CONST) | PRIMPAR_4_BYTES),
408 (byte) ((v >>> 8) & 0xFF),
409 (
byte) ((v >>> 16) & 0xFF),
410 (byte) ((v >>> 24) & 0xFF)};
415 return new byte[] {(byte) ((i & PRIMPAR_INDEX) | PRIMPAR_SHORT | PRIMPAR_VARIABEL | PRIMPAR_LOCAL)};
419 return new byte[] {(byte) (PRIMPAR_LONG | PRIMPAR_VARIABEL | PRIMPAR_LOCAL | PRIMPAR_1_BYTE),
424 return new byte[] {(byte) (PRIMPAR_LONG | PRIMPAR_VARIABEL | PRIMPAR_LOCAL | PRIMPAR_2_BYTES),
426 (byte) ((i >>> 8) & 0xFF)};
430 return new byte[] {(byte) (PRIMPAR_LONG | PRIMPAR_VARIABEL | PRIMPAR_LOCAL | PRIMPAR_4_BYTES),
432 (byte) ((i >>> 8) & 0xFF),
433 (
byte) ((i >>> 16) & 0xFF),
434 (byte) ((i >>> 24) & 0xFF)};
438 return new byte[] {(byte)((i & PRIMPAR_INDEX) | PRIMPAR_SHORT | PRIMPAR_VARIABEL | PRIMPAR_GLOBAL)};
442 return new byte[] {(byte) (PRIMPAR_LONG | PRIMPAR_VARIABEL | PRIMPAR_GLOBAL | PRIMPAR_1_BYTE),
447 return new byte[] {(byte) (PRIMPAR_LONG | PRIMPAR_VARIABEL | PRIMPAR_GLOBAL | PRIMPAR_2_BYTES),
449 (byte) ((i >>> 8) & 0xFF)};
453 return new byte[] {(byte) (PRIMPAR_LONG | PRIMPAR_VARIABEL | PRIMPAR_GLOBAL | PRIMPAR_4_BYTES),
455 (byte) ((i >>> 8) & 0xFF),
456 (
byte) ((i >>> 16) & 0xFF),
457 (byte) ((i >>> 24) & 0xFF)};
462 int bufferCapacity = 2;
465 for (Object obj : parameters) {
466 if (obj instanceof Byte)
468 else if (obj instanceof Short)
470 else if (obj instanceof Integer)
472 else if (obj instanceof String)
473 bufferCapacity += ((String) obj).length() + 1;
475 throw new IllegalArgumentException(
"Parameters should be one of the class types: Byte, Short, Integer, String");
479 ByteBuffer buffer = ByteBuffer.allocate(bufferCapacity);
480 buffer.order(ByteOrder.LITTLE_ENDIAN);
485 for (Object obj : parameters) {
486 if (obj instanceof Byte)
487 buffer.put((Byte) obj);
488 else if (obj instanceof Short)
489 buffer.putShort((Short) obj);
490 else if (obj instanceof Integer)
491 buffer.putInt((Integer) obj);
492 else if (obj instanceof String) {
494 buffer.put(((String) obj).getBytes(
"US-ASCII"));
495 }
catch (UnsupportedEncodingException e) {
496 throw new IllegalArgumentException(
"Non-ASCII string encoding is not supported");
498 buffer.put((
byte) 0);
500 throw new IllegalArgumentException(
"Parameters should be one of the class types: Byte, Short, Integer, String");
503 return buffer.array();
506 public static byte[]
encodeDirectCommand(
byte opcode,
boolean needReply,
int globalAllocation,
int localAllocation, String paramFormat, Object... parameters) {
507 if (globalAllocation < 0 || globalAllocation > 0x3ff || localAllocation < 0 || localAllocation > 0x3f || paramFormat.length() != parameters.length)
508 throw new IllegalArgumentException();
512 ArrayList<byte[]> payloads =
new ArrayList<byte[]>();
514 for (
int i = 0; i < paramFormat.length(); i++) {
515 char letter = paramFormat.charAt(i);
516 Object obj = parameters[i];
520 if (obj instanceof Byte) {
521 if ((((Byte) obj) <= 31) && (((Byte) obj) >= -31))
526 else if (obj instanceof Short)
528 else if (obj instanceof Integer)
531 throw new IllegalArgumentException();
535 if (obj instanceof Byte) {
536 if ((((Byte) obj) <= 31) && (((Byte) obj) >= -31))
541 else if (obj instanceof Short)
543 else if (obj instanceof Integer)
546 throw new IllegalArgumentException();
550 if (obj instanceof Byte) {
551 if ((((Byte) obj) <= 31) && (((Byte) obj) >= -31))
556 else if (obj instanceof Short)
558 else if (obj instanceof Integer)
561 throw new IllegalArgumentException();
565 if (!(obj instanceof String))
566 throw new IllegalArgumentException();
569 payloads.add((((String) obj) +
'\0').getBytes(
"US-ASCII"));
570 }
catch (UnsupportedEncodingException e) {
571 throw new IllegalArgumentException();
576 throw new IllegalArgumentException(
"Illegal format string");
581 int bufferCapacity = 4;
582 for (
byte[] array : payloads)
583 bufferCapacity += array.length;
586 ByteBuffer buffer = ByteBuffer.allocate(bufferCapacity);
587 buffer.order(ByteOrder.LITTLE_ENDIAN);
590 buffer.put(
new byte[] {(byte) (globalAllocation & 0xff),
591 (byte) (((globalAllocation >>> 8) & 0x3) | (localAllocation << 2))});
594 for (
byte[] array : payloads)
597 return buffer.array();