From 6aea7c7f7097ab2a04f5dc21dd3f73e988397ffd Mon Sep 17 00:00:00 2001 From: Ian Shehadeh Date: Fri, 4 Mar 2022 08:48:54 -0500 Subject: [PATCH] add int/to-bytes int/to-bytes unpacks the bytes of a 64-bit integer into a tuple. --- src/core/inttypes.c | 38 ++++++++++++++++++++++++++++++++++++++ test/suite0006.janet | 21 +++++++++++++++++++++ 2 files changed, 59 insertions(+) diff --git a/src/core/inttypes.c b/src/core/inttypes.c index 5de40112..d985c5c7 100644 --- a/src/core/inttypes.c +++ b/src/core/inttypes.c @@ -238,6 +238,43 @@ JANET_CORE_FN(cfun_to_number, janet_panicf("expected int/u64 or int/s64, got %q", argv[0]); } +JANET_CORE_FN(cfun_to_bytes, + "(int/to-bytes value &opt endianness)", + "Convert an `int/s64` or `int/u64` into an 8-element tuple of bytes.\n" + "the endianness paramater indicates the byte order:\n" + "- `nil` (unset): system byte order\n" + "- `:le`: little-endian, least significant byte first\n" + "- `:be`: big-endian, most significant byte first\n") { + janet_arity(argc, 1, 2); + if (janet_is_int(argv[0]) == JANET_INT_NONE) { + janet_panicf("int/to-bytes: expected an int/s64 or int/u64, got %q", argv[0]); + } + + int reverse = 0; + if (argc > 1 && !janet_checktype(argv[1], JANET_NIL)) { + JanetKeyword endianness_kw = janet_getkeyword(argv, 1); + if (!janet_cstrcmp(endianness_kw, "le")) { +#if JANET_BIG_ENDIAN + reverse = 1; +#endif + } else if (!janet_cstrcmp(endianness_kw, "be")) { +#if JANET_LITTLE_ENDIAN + reverse = 1; +#endif + } else { + janet_panicf("int/to-bytes: expected endianness :le, :be or nil, got %v", argv[1]); + } + } + + uint8_t *bytes = janet_unwrap_abstract(argv[0]); + Janet *bytes_tuple = janet_tuple_begin(8); + for (int i = 0; i < 8; ++i) { + bytes_tuple[i] = janet_wrap_integer(bytes[reverse ? 7 - i : i]); + } + + return janet_wrap_tuple(janet_tuple_end(bytes_tuple)); +} + /* * Code to support polymorphic comparison. * int/u64 and int/s64 support a "compare" method that allows @@ -546,6 +583,7 @@ void janet_lib_inttypes(JanetTable *env) { JANET_CORE_REG("int/s64", cfun_it_s64_new), JANET_CORE_REG("int/u64", cfun_it_u64_new), JANET_CORE_REG("int/to-number", cfun_to_number), + JANET_CORE_REG("int/to-bytes", cfun_to_bytes), JANET_REG_END }; janet_core_cfuns_ext(env, NULL, it_cfuns); diff --git a/test/suite0006.janet b/test/suite0006.janet index 3c4cd551..52309c01 100644 --- a/test/suite0006.janet +++ b/test/suite0006.janet @@ -91,6 +91,27 @@ "trap INT64_MIN / -1" (:/ (int/s64 "-0x8000_0000_0000_0000") -1)) +# int/s64 and int/u64 serialization +(assert (= (int/to-bytes (u64 0)) [0 0 0 0 0 0 0 0])) + +(assert (= (int/to-bytes (i64 1) :le) [1 0 0 0 0 0 0 0])) +(assert (= (int/to-bytes (i64 1) :be) [0 0 0 0 0 0 0 1])) +(assert (= (int/to-bytes (i64 -1)) [0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF])) +(assert (= (int/to-bytes (i64 -5) :be) [0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF (- 0xFF 4)])) + +(assert (= (int/to-bytes (u64 1) :le) [1 0 0 0 0 0 0 0])) +(assert (= (int/to-bytes (u64 1) :be) [0 0 0 0 0 0 0 1])) +(assert (= (int/to-bytes (u64 300) :be) [0 0 0 0 0 0 0x01 0x2C])) + +(assert-error + "bad value passed to int/to-bytes" + (int/to-bytes 1)) + +(assert-error + "invalid endianness passed to int/to-bytes" + (int/to-bytes (u64 0) :little)) + + # Dynamic bindings (setdyn :a 10) (assert (= 40 (with-dyns [:a 25 :b 15] (+ (dyn :a) (dyn :b)))) "dyn usage 1")