Implement SEQUENCE encoding and decoding in client
This commit is contained in:
parent
5df58e9d28
commit
ef9e578e25
@ -7,24 +7,26 @@ namespace StudySystemClient.Der {
|
|||||||
|
|
||||||
private const uint BASE_HEADER_SIZE = 2;
|
private const uint BASE_HEADER_SIZE = 2;
|
||||||
|
|
||||||
public static Datum decode(uint8[] bytes) throws DecodeError {
|
public static Datum decode(uint8[] bytes, out uint? size = null)
|
||||||
|
throws DecodeError {
|
||||||
if (bytes.length < BASE_HEADER_SIZE) {
|
if (bytes.length < BASE_HEADER_SIZE) {
|
||||||
throw new DecodeError.INCOMPLETE(
|
throw new DecodeError.INCOMPLETE(
|
||||||
"Message is fewer than %u bytes", BASE_HEADER_SIZE);
|
"Message is fewer than %u bytes", BASE_HEADER_SIZE);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint header_size = 0;
|
uint header_size;
|
||||||
var length = decode_length(bytes, ref header_size);
|
var length = decode_length(bytes, out header_size);
|
||||||
if (header_size + length > bytes.length) {
|
if (header_size + length > bytes.length) {
|
||||||
throw new DecodeError.INCOMPLETE(
|
throw new DecodeError.INCOMPLETE(
|
||||||
"Length %u but only %u bytes available", length,
|
"Length %u but only %u bytes available", length,
|
||||||
bytes.length - header_size);
|
bytes.length - header_size);
|
||||||
}
|
}
|
||||||
var content = bytes[header_size:header_size + length];
|
var content = bytes[header_size:header_size + length];
|
||||||
|
size = header_size + length;
|
||||||
return decode_datum(bytes[0], content);
|
return decode_datum(bytes[0], content);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static uint decode_length(uint8[] bytes, ref uint header_size)
|
private static uint decode_length(uint8[] bytes, out uint header_size)
|
||||||
throws DecodeError {
|
throws DecodeError {
|
||||||
if ((bytes[1] & 0x80) != 0) {
|
if ((bytes[1] & 0x80) != 0) {
|
||||||
var length_size = bytes[1] & 0x7f;
|
var length_size = bytes[1] & 0x7f;
|
||||||
@ -53,6 +55,8 @@ namespace StudySystemClient.Der {
|
|||||||
return new Integer.from_content(content);
|
return new Integer.from_content(content);
|
||||||
case Utf8String.TYPE:
|
case Utf8String.TYPE:
|
||||||
return new Utf8String.from_content(content);
|
return new Utf8String.from_content(content);
|
||||||
|
case Sequence.TYPE:
|
||||||
|
return new Sequence.from_content(content);
|
||||||
default:
|
default:
|
||||||
throw new DecodeError.UNKNOWN_TYPE("Unsupported type: %02x",
|
throw new DecodeError.UNKNOWN_TYPE("Unsupported type: %02x",
|
||||||
type);
|
type);
|
||||||
@ -214,11 +218,48 @@ namespace StudySystemClient.Der {
|
|||||||
value = decode_string(bytes);
|
value = decode_string(bytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static string decode_string(uint8[] bytes) {
|
private static string decode_string(uint8[] bytes) {
|
||||||
var buffer = new uint8[bytes.length + 1];
|
var buffer = new uint8[bytes.length + 1];
|
||||||
Memory.copy(buffer, bytes, bytes.length);
|
Memory.copy(buffer, bytes, bytes.length);
|
||||||
buffer[bytes.length] = 0;
|
buffer[bytes.length] = 0;
|
||||||
return (string)buffer;
|
return (string)buffer;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class Sequence : Datum {
|
||||||
|
internal const uint8 TYPE = 0x30;
|
||||||
|
|
||||||
|
public Datum[] value { get; private set; }
|
||||||
|
|
||||||
|
public Sequence(Datum[] val) {
|
||||||
|
type = TYPE;
|
||||||
|
content = encode_array(val);
|
||||||
|
value = val;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal Sequence.from_content(uint8[] bytes) throws DecodeError {
|
||||||
|
type = TYPE;
|
||||||
|
content = bytes;
|
||||||
|
value = decode_array(bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static uint8[] encode_array(Datum[] val) {
|
||||||
|
var buffer = new ByteArray();
|
||||||
|
foreach (var datum in val)
|
||||||
|
buffer.append(datum.encode());
|
||||||
|
return buffer.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Datum[] decode_array(uint8[] bytes)
|
||||||
|
throws DecodeError {
|
||||||
|
var elems = new GenericArray<Datum>();
|
||||||
|
uint offset = 0;
|
||||||
|
uint size;
|
||||||
|
while (offset < bytes.length) {
|
||||||
|
elems.add(decode(bytes[offset:], out size));
|
||||||
|
offset += size;
|
||||||
|
}
|
||||||
|
return elems.data;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
using StudySystemClient;
|
||||||
using StudySystemClient.Der;
|
using StudySystemClient.Der;
|
||||||
|
|
||||||
static bool bytes_equal(uint8[] expected, uint8[] actual) {
|
static bool bytes_equal(uint8[] expected, uint8[] actual) {
|
||||||
@ -46,15 +47,31 @@ static void test_decode_boolean(uint8[] bytes, bool expected) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void test_decode_integer(uint8[] bytes, int64 expected) {
|
static void test_decode_integer(uint8[] bytes, int64 expected) {
|
||||||
Integer integer;
|
Datum datum;
|
||||||
try {
|
try {
|
||||||
integer = decode(bytes) as Integer;
|
datum = decode(bytes);
|
||||||
|
} catch (DecodeError err) {
|
||||||
|
Test.message("Decoding failed: %s", err.message);
|
||||||
|
Test.fail();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
test_integer_value(expected, datum);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_decode_utf8string(uint8[] bytes, string expected) {
|
||||||
|
Datum datum;
|
||||||
|
try {
|
||||||
|
datum = decode(bytes);
|
||||||
} catch (DecodeError err) {
|
} catch (DecodeError err) {
|
||||||
Test.message("Decoding failed: %s", err.message);
|
Test.message("Decoding failed: %s", err.message);
|
||||||
Test.fail();
|
Test.fail();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
test_utf8string_value(expected, datum);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_integer_value(int64 expected, Datum datum) {
|
||||||
|
var integer = datum as Integer;
|
||||||
if (integer == null) {
|
if (integer == null) {
|
||||||
Test.message("Bytes were not decoded as a INTEGER");
|
Test.message("Bytes were not decoded as a INTEGER");
|
||||||
Test.fail();
|
Test.fail();
|
||||||
@ -67,16 +84,8 @@ static void test_decode_integer(uint8[] bytes, int64 expected) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void test_decode_utf8string(uint8[] bytes, string expected) {
|
static void test_utf8string_value(string expected, Datum datum) {
|
||||||
Utf8String utf8string;
|
var utf8string = datum as Utf8String;
|
||||||
try {
|
|
||||||
utf8string = decode(bytes) as Utf8String;
|
|
||||||
} catch (DecodeError err) {
|
|
||||||
Test.message("Decoding failed: %s", err.message);
|
|
||||||
Test.fail();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (utf8string == null) {
|
if (utf8string == null) {
|
||||||
Test.message("Bytes were not decoded as a UTF8String");
|
Test.message("Bytes were not decoded as a UTF8String");
|
||||||
Test.fail();
|
Test.fail();
|
||||||
@ -154,6 +163,15 @@ void main(string[] args) {
|
|||||||
test_encode(new Utf8String(string.nfill(128, 'x')), expected);
|
test_encode(new Utf8String(string.nfill(128, 'x')), expected);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Test.add_func("/encode/sequence/foo,42", () => {
|
||||||
|
var sequence = new Der.Sequence(
|
||||||
|
{new Utf8String("foo"), new Integer(42)});
|
||||||
|
var expected = new uint8[] {
|
||||||
|
0x30, 0x08, 0x0c, 0x03, 0x66, 0x6f, 0x6f, 0x02, 0x01, 0x2a
|
||||||
|
};
|
||||||
|
test_encode(sequence, expected);
|
||||||
|
});
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Decoding
|
* Decoding
|
||||||
*/
|
*/
|
||||||
@ -208,5 +226,34 @@ void main(string[] args) {
|
|||||||
test_decode_utf8string(bytes, string.nfill(128, 'x'));
|
test_decode_utf8string(bytes, string.nfill(128, 'x'));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Test.add_func("/decode/sequence/foo,42", () => {
|
||||||
|
var bytes = new uint8[] {
|
||||||
|
0x30, 0x08, 0x0c, 0x03, 0x66, 0x6f, 0x6f, 0x02, 0x01, 0x2a
|
||||||
|
};
|
||||||
|
var expected = 2;
|
||||||
|
Der.Sequence sequence;
|
||||||
|
try {
|
||||||
|
sequence = decode(bytes) as Der.Sequence;
|
||||||
|
} catch (DecodeError err) {
|
||||||
|
Test.message("Decoding failed: %s", err.message);
|
||||||
|
Test.fail();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (sequence == null) {
|
||||||
|
Test.message("Bytes were not decoded as a SEQUENCE");
|
||||||
|
Test.fail();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Datum[] elems = sequence.value;
|
||||||
|
if (elems.length != expected) {
|
||||||
|
Test.message(
|
||||||
|
@"Expected $expected elements, got $(elems.length)");
|
||||||
|
Test.fail();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
test_utf8string_value("foo", elems[0]);
|
||||||
|
test_integer_value(42, elems[1]);
|
||||||
|
});
|
||||||
|
|
||||||
Test.run();
|
Test.run();
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user