소스 검색

ncd: Implement struct_decode.

Ambroz Bizjak 11 년 전
부모
커밋
0a3e683ce9
2개의 변경된 파일140개의 추가작업 그리고 56개의 파일을 삭제
  1. 101 10
      ncd/modules/basic_functions.c
  2. 39 46
      ncd/tests/struct.ncd

+ 101 - 10
ncd/modules/basic_functions.c

@@ -461,18 +461,17 @@ DEFINE_PERCHAR(tolower, b_ascii_tolower(ch))
 DEFINE_PERCHAR(toupper, b_ascii_toupper(ch))
 
 
-// struct_encode
+// struct_encode, struct_decode
 
-static int struct_encode_single (NCDCall call, ExpString *estr, NCDValRef encoding, NCDValRef value)
+static int read_integer_encoding (NCDValRef encoding, int *out_big, int *out_size)
 {
-    uintmax_t val_int;
-    if (!ncd_read_uintmax(value, &val_int)) {
-        FunctionLog(&call, BLOG_ERROR, "struct_encode: value must be an integer");
+    if (!NCDVal_IsString(encoding)) {
         return 0;
     }
     int big;
     int size;
-    if ((big = NCDVal_StringEquals(encoding, "u8"))) {
+    if (NCDVal_StringEquals(encoding, "u8")) {
+        big = 0;
         size = 1;
     } else if ((big = NCDVal_StringEquals(encoding, "u16b")) || NCDVal_StringEquals(encoding, "u16l")) {
         size = 2;
@@ -481,6 +480,23 @@ static int struct_encode_single (NCDCall call, ExpString *estr, NCDValRef encodi
     } else if ((big = NCDVal_StringEquals(encoding, "u64b")) || NCDVal_StringEquals(encoding, "u64l")) {
         size = 8;
     } else {
+        return 0;
+    }
+    *out_big = big;
+    *out_size = size;
+    return 1;
+}
+
+static int struct_encode_single (NCDCall call, ExpString *estr, NCDValRef encoding, NCDValRef value)
+{
+    uintmax_t val_int;
+    if (!ncd_read_uintmax(value, &val_int)) {
+        FunctionLog(&call, BLOG_ERROR, "struct_encode: value must be an integer");
+        return 0;
+    }
+    int big;
+    int size;
+    if (!read_integer_encoding(encoding, &big, &size)) {
         FunctionLog(&call, BLOG_ERROR, "struct_encode: invalid encoding specified");
         return 0;
     }
@@ -532,10 +548,6 @@ static void struct_encode_eval (NCDCall call)
             FunctionLog(&call, BLOG_ERROR, "struct_encode: element list must have two elements");
             goto fail1;
         }
-        if (!NCDVal_IsString(encoding)) {
-            FunctionLog(&call, BLOG_ERROR, "struct_encode: encoding must be a string");
-            goto fail1;
-        }
         if (!struct_encode_single(call, &estr, encoding, value)) {
             goto fail1;
         }
@@ -547,6 +559,82 @@ fail0:
     return;
 }
 
+static int struct_decode_single (NCDCall call, MemRef *data, NCDValRef encoding, NCDValRef result_list)
+{
+    int big;
+    int size;
+    if (!read_integer_encoding(encoding, &big, &size)) {
+        FunctionLog(&call, BLOG_ERROR, "struct_decode: invalid encoding specified");
+        return 0;
+    }
+    if (data->len < size) {
+        FunctionLog(&call, BLOG_ERROR, "struct_decode: insufficient data available");
+        return 0;
+    }
+    uintmax_t val_int = 0;
+    for (int i = 0; i < size; i++) {
+        val_int <<= 8;
+        val_int |= *(uint8_t const *)(data->ptr + (big ? i : (size - 1 - i)));
+    }
+    *data = MemRef_SubFrom(*data, size);
+    NCDValRef value = ncd_make_uintmax(NCDCall_ResMem(&call), val_int);
+    if (NCDVal_IsInvalid(value)) {
+        return 0;
+    }
+    if (!NCDVal_ListAppend(result_list, value)) {
+        return 0;
+    }
+    return 1;
+}
+
+static void struct_decode_eval (NCDCall call)
+{
+    if (NCDCall_ArgCount(&call) != 2) {
+        FunctionLog(&call, BLOG_ERROR, "struct_decode: need two arguments");
+        goto fail0;
+    }
+    NCDValRef format_arg = NCDCall_EvalArg(&call, 0, NCDCall_ResMem(&call));
+    if (NCDVal_IsInvalid(format_arg)) {
+        goto fail0;
+    }
+    if (!NCDVal_IsList(format_arg)) {
+        FunctionLog(&call, BLOG_ERROR, "struct_decode: format argument must be a list");
+        goto fail0;
+    }
+    // Evaluate the data string to temp mem, so the pointer doesn't change.
+    NCDValMem temp_mem;
+    NCDValMem_Init(&temp_mem);
+    NCDValRef data_arg = NCDCall_EvalArg(&call, 1, &temp_mem);
+    if (NCDVal_IsInvalid(data_arg)) {
+        goto fail1;
+    }
+    if (!NCDVal_IsString(data_arg)) {
+        FunctionLog(&call, BLOG_ERROR, "struct_decode: data argument must be a string");
+        goto fail1;
+    }
+    size_t count = NCDVal_ListCount(format_arg);
+    NCDValRef result_list = NCDVal_NewList(NCDCall_ResMem(&call), count);
+    if (NCDVal_IsInvalid(result_list)) {
+        goto fail1;
+    }
+    MemRef data = NCDVal_StringMemRef(data_arg);
+    for (size_t i = 0; i < count; i++) {
+        NCDValRef encoding = NCDVal_ListGet(format_arg, i);
+        if (!struct_decode_single(call, &data, encoding, result_list)) {
+            goto fail1;
+        }
+    }
+    if (data.len > 0) {
+        FunctionLog(&call, BLOG_ERROR, "struct_decode: not all data was consumed");
+        goto fail1;
+    }
+    NCDCall_SetResult(&call, result_list);
+fail1:
+    NCDValMem_Free(&temp_mem);
+fail0:
+    return;
+}
+
 
 static struct NCDModuleFunction const functions[] = {
     {
@@ -654,6 +742,9 @@ static struct NCDModuleFunction const functions[] = {
     }, {
         .func_name = "struct_encode",
         .func_eval = struct_encode_eval
+    }, {
+        .func_name = "struct_decode",
+        .func_eval = struct_decode_eval
     }, {
         .func_name = NULL
     }

+ 39 - 46
ncd/tests/struct.ncd

@@ -1,49 +1,42 @@
-process main {
-    var(@struct_encode({{@u8, "0"}})) x;
-    assert(@val_equal(x, "\x00"));
-    var(@struct_encode({{@u8, "254"}})) x;
-    assert(@val_equal(x, "\xFE"));
-    var(@struct_encode({{@u8, "255"}})) x;
-    assert(@val_equal(x, "\xFF"));
-
-    var(@struct_encode({{@u16b, "0"}})) x;
-    assert(@val_equal(x, "\x00\x00"));
-    var(@struct_encode({{@u16l, "0"}})) x;
-    assert(@val_equal(x, "\x00\x00"));
-    var(@struct_encode({{@u16b, "65534"}})) x;
-    assert(@val_equal(x, "\xFF\xFE"));
-    var(@struct_encode({{@u16l, "65534"}})) x;
-    assert(@val_equal(x, "\xFE\xFF"));
-    var(@struct_encode({{@u16b, "65535"}})) x;
-    assert(@val_equal(x, "\xFF\xFF"));
-    var(@struct_encode({{@u16l, "65535"}})) x;
-    assert(@val_equal(x, "\xFF\xFF"));
-
-    var(@struct_encode({{@u32b, "0"}})) x;
-    assert(@val_equal(x, "\x00\x00\x00\x00"));
-    var(@struct_encode({{@u32l, "0"}})) x;
-    assert(@val_equal(x, "\x00\x00\x00\x00"));
-    var(@struct_encode({{@u32b, "4294967294"}})) x;
-    assert(@val_equal(x, "\xFF\xFF\xFF\xFE"));
-    var(@struct_encode({{@u32l, "4294967294"}})) x;
-    assert(@val_equal(x, "\xFE\xFF\xFF\xFF"));
-    var(@struct_encode({{@u32b, "4294967295"}})) x;
-    assert(@val_equal(x, "\xFF\xFF\xFF\xFF"));
-    var(@struct_encode({{@u32l, "4294967295"}})) x;
-    assert(@val_equal(x, "\xFF\xFF\xFF\xFF"));
-
-    var(@struct_encode({{@u64b, "0"}})) x;
-    assert(@val_equal(x, "\x00\x00\x00\x00\x00\x00\x00\x00"));
-    var(@struct_encode({{@u64l, "0"}})) x;
-    assert(@val_equal(x, "\x00\x00\x00\x00\x00\x00\x00\x00"));
-    var(@struct_encode({{@u64b, "18446744073709551614"}})) x;
-    assert(@val_equal(x, "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFE"));
-    var(@struct_encode({{@u64l, "18446744073709551614"}})) x;
-    assert(@val_equal(x, "\xFE\xFF\xFF\xFF\xFF\xFF\xFF\xFF"));
-    var(@struct_encode({{@u64b, "18446744073709551615"}})) x;
-    assert(@val_equal(x, "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"));
-    var(@struct_encode({{@u64l, "18446744073709551615"}})) x;
-    assert(@val_equal(x, "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"));
+template test_one {
+    var(@struct_encode({{_arg0, _arg1}})) encoded;
+    assert(@val_equal(encoded, _arg2));
+    
+    var(@struct_decode({_arg0}, encoded)) decoded;
+    assert(@val_equal(decoded, {_arg1}));
+}
 
+process main {
+    call(@test_one, {@u8, "0", "\x00"});
+    call(@test_one, {@u8, "254", "\xFE"});
+    call(@test_one, {@u8, "255", "\xFF"});
+    
+    call(@test_one, {@u16b, "0", "\x00\x00"});
+    call(@test_one, {@u16l, "0", "\x00\x00"});
+    call(@test_one, {@u16b, "65534", "\xFF\xFE"});
+    call(@test_one, {@u16l, "65534", "\xFE\xFF"});
+    call(@test_one, {@u16b, "65535", "\xFF\xFF"});
+    call(@test_one, {@u16l, "65535", "\xFF\xFF"});
+    
+    call(@test_one, {@u32b, "0", "\x00\x00\x00\x00"});
+    call(@test_one, {@u32l, "0", "\x00\x00\x00\x00"});
+    call(@test_one, {@u32b, "4294967294", "\xFF\xFF\xFF\xFE"});
+    call(@test_one, {@u32l, "4294967294", "\xFE\xFF\xFF\xFF"});
+    call(@test_one, {@u32b, "4294967295", "\xFF\xFF\xFF\xFF"});
+    call(@test_one, {@u32l, "4294967295", "\xFF\xFF\xFF\xFF"});
+    
+    call(@test_one, {@u64b, "0", "\x00\x00\x00\x00\x00\x00\x00\x00"});
+    call(@test_one, {@u64l, "0", "\x00\x00\x00\x00\x00\x00\x00\x00"});
+    call(@test_one, {@u64b, "18446744073709551614", "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFE"});
+    call(@test_one, {@u64l, "18446744073709551614", "\xFE\xFF\xFF\xFF\xFF\xFF\xFF\xFF"});
+    call(@test_one, {@u64b, "18446744073709551615", "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"});
+    call(@test_one, {@u64l, "18446744073709551615", "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"});
+    
+    var(@struct_encode({{@u8, "4"}, {@u16l, "1000"}})) encoded;
+    assert(@val_equal(encoded, "\x04\xE8\x03"));
+    
+    var(@struct_decode({@u8, @u16l}, "\x05\xE9\x03")) decoded;
+    assert(@val_equal(decoded, {"5", "1001"}));
+    
     exit("0");
 }