Przeglądaj źródła

ncd: Implement struct_encode function.

Ambroz Bizjak 11 lat temu
rodzic
commit
126eaf9576
2 zmienionych plików z 139 dodań i 0 usunięć
  1. 90 0
      ncd/modules/basic_functions.c
  2. 49 0
      ncd/tests/struct.ncd

+ 90 - 0
ncd/modules/basic_functions.c

@@ -461,6 +461,93 @@ DEFINE_PERCHAR(tolower, b_ascii_tolower(ch))
 DEFINE_PERCHAR(toupper, b_ascii_toupper(ch))
 
 
+// struct_encode
+
+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 ((big = NCDVal_StringEquals(encoding, "u8"))) {
+        size = 1;
+    } else if ((big = NCDVal_StringEquals(encoding, "u16b")) || NCDVal_StringEquals(encoding, "u16l")) {
+        size = 2;
+    } else if ((big = NCDVal_StringEquals(encoding, "u32b")) || NCDVal_StringEquals(encoding, "u32l")) {
+        size = 4;
+    } else if ((big = NCDVal_StringEquals(encoding, "u64b")) || NCDVal_StringEquals(encoding, "u64l")) {
+        size = 8;
+    } else {
+        FunctionLog(&call, BLOG_ERROR, "struct_encode: invalid encoding specified");
+        return 0;
+    }
+    uint8_t results[8];
+    for (int i = 0; i < size; i++) {
+        results[big ? (size - 1 - i) : i] = val_int;
+        val_int >>= 8;
+    }
+    if (val_int > 0) {
+        FunctionLog(&call, BLOG_ERROR, "struct_encode: value is out of range");
+        return 0;
+    }
+    if (!ExpString_AppendBinaryMr(estr, MemRef_Make((char const *)results, size))) {
+        FunctionLog(&call, BLOG_ERROR, "ExpString_AppendBinaryMr failed");
+        return 0;
+    }
+    return 1;
+}
+
+static void struct_encode_eval (NCDCall call)
+{
+    if (NCDCall_ArgCount(&call) != 1) {
+        FunctionLog(&call, BLOG_ERROR, "struct_encode: need one argument");
+        goto fail0;
+    }
+    NCDValRef arg = NCDCall_EvalArg(&call, 0, NCDCall_ResMem(&call));
+    if (NCDVal_IsInvalid(arg)) {
+        goto fail0;
+    }
+    if (!NCDVal_IsList(arg)) {
+        FunctionLog(&call, BLOG_ERROR, "struct_encode: argument must be a list");
+        goto fail0;
+    }
+    ExpString estr;
+    if (!ExpString_Init(&estr)) {
+        FunctionLog(&call, BLOG_ERROR, "ExpString_Init failed");
+        goto fail0;
+    }
+    size_t count = NCDVal_ListCount(arg);
+    for (size_t i = 0; i < count; i++) {
+        NCDValRef elem = NCDVal_ListGet(arg, i);
+        if (!NCDVal_IsList(elem)) {
+            FunctionLog(&call, BLOG_ERROR, "struct_encode: element must be a list");
+            goto fail1;
+        }
+        NCDValRef encoding;
+        NCDValRef value;
+        if (!NCDVal_ListRead(elem, 2, &encoding, &value)) {
+            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;
+        }
+    }
+    NCDCall_SetResult(&call, NCDVal_NewStringBinMr(NCDCall_ResMem(&call), ExpString_GetMr(&estr)));
+fail1:
+    ExpString_Free(&estr);
+fail0:
+    return;
+}
+
+
 static struct NCDModuleFunction const functions[] = {
     {
         .func_name = "error",
@@ -564,6 +651,9 @@ static struct NCDModuleFunction const functions[] = {
     }, {
         .func_name = "toupper",
         .func_eval = perchar_toupper_eval
+    }, {
+        .func_name = "struct_encode",
+        .func_eval = struct_encode_eval
     }, {
         .func_name = NULL
     }

+ 49 - 0
ncd/tests/struct.ncd

@@ -0,0 +1,49 @@
+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"));
+
+    exit("0");
+}