diff --git a/xls/public/c_api.cc b/xls/public/c_api.cc index dce1f99aa3..2d505e666d 100644 --- a/xls/public/c_api.cc +++ b/xls/public/c_api.cc @@ -16,6 +16,7 @@ #include // NOLINT(modernize-deprecated-headers) +#include #include #include #include @@ -347,6 +348,32 @@ struct xls_value* xls_value_clone(const struct xls_value* value) { return reinterpret_cast(new xls::Value(*cpp_value)); } +bool xls_value_get_kind(const struct xls_value* value, char** error_out, + xls_value_kind* kind_out) { + CHECK(value != nullptr); + const auto* cpp_value = reinterpret_cast(value); + *error_out = nullptr; + switch (cpp_value->kind()) { + case xls::ValueKind::kBits: + *kind_out = xls_value_kind_bits; + break; + case xls::ValueKind::kToken: + *kind_out = xls_value_kind_token; + break; + case xls::ValueKind::kArray: + *kind_out = xls_value_kind_array; + break; + case xls::ValueKind::kTuple: + *kind_out = xls_value_kind_tuple; + break; + case xls::ValueKind::kInvalid: + *error_out = xls::ToOwnedCString( + absl::InvalidArgumentError("Value is invalid").ToString()); + return false; + } + return true; +} + bool xls_value_make_ubits(int64_t bit_count, uint64_t value, char** error_out, struct xls_value** xls_value_out) { CHECK(error_out != nullptr); @@ -428,6 +455,50 @@ bool xls_bits_get_bit(const struct xls_bits* bits, int64_t index) { return cpp_bits->Get(index); } +bool xls_bits_to_bytes(const struct xls_bits* bits, char** error_out, + uint8_t** bytes_out, size_t* byte_count_out) { + CHECK(bits != nullptr); + CHECK(bytes_out != nullptr); + CHECK(error_out != nullptr); + CHECK(byte_count_out != nullptr); + const auto* cpp_bits = reinterpret_cast(bits); + std::vector bytes = cpp_bits->ToBytes(); + *byte_count_out = bytes.size(); + *bytes_out = new uint8_t[bytes.size()]; + std::copy(bytes.begin(), bytes.end(), *bytes_out); + return true; +} + +bool xls_bits_to_uint64(const struct xls_bits* bits, char** error_out, + uint64_t* value_out) { + CHECK(bits != nullptr); + CHECK(value_out != nullptr); + CHECK(error_out != nullptr); + const auto* cpp_bits = reinterpret_cast(bits); + absl::StatusOr result = cpp_bits->ToUint64(); + if (!result.ok()) { + *error_out = xls::ToOwnedCString(result.status().ToString()); + return false; + } + *value_out = result.value(); + return true; +} + +bool xls_bits_to_int64(const struct xls_bits* bits, char** error_out, + int64_t* value_out) { + CHECK(bits != nullptr); + CHECK(value_out != nullptr); + CHECK(error_out != nullptr); + const auto* cpp_bits = reinterpret_cast(bits); + absl::StatusOr result = cpp_bits->ToInt64(); + if (!result.ok()) { + *error_out = xls::ToOwnedCString(result.status().ToString()); + return false; + } + *value_out = result.value(); + return true; +} + struct xls_bits* xls_bits_width_slice(const struct xls_bits* bits, int64_t start, int64_t width) { CHECK(bits != nullptr); @@ -678,6 +749,10 @@ void xls_c_strs_free(char** c_strs, size_t count) { delete[] c_strs; } +void xls_bytes_free(uint8_t *bytes) { + delete[] bytes; +} + bool xls_value_to_string(const struct xls_value* v, char** string_out) { CHECK(v != nullptr); CHECK(string_out != nullptr); diff --git a/xls/public/c_api.h b/xls/public/c_api.h index ab27d2bd3d..f9afdb4271 100644 --- a/xls/public/c_api.h +++ b/xls/public/c_api.h @@ -43,6 +43,15 @@ extern "C" { +typedef int32_t xls_value_kind; +enum { + xls_value_kind_invalid, + xls_value_kind_bits, + xls_value_kind_array, + xls_value_kind_tuple, + xls_value_kind_token, +}; + // Opaque structs. struct xls_bits; struct xls_function; @@ -130,6 +139,11 @@ bool xls_parse_typed_value(const char* input, char** error_out, bool xls_value_make_ubits(int64_t bit_count, uint64_t value, char** error_out, struct xls_value** xls_value_out); +// Returns the kind of the given value. The caller must free the returned +// `error_out` string via `xls_c_str_free` if an error is returned. +bool xls_value_get_kind(const struct xls_value* value, char** error_out, + xls_value_kind* kind_out); + bool xls_value_make_sbits(int64_t bit_count, int64_t value, char** error_out, struct xls_value** xls_value_out); @@ -199,6 +213,23 @@ bool xls_bits_eq(const struct xls_bits* a, const struct xls_bits* b); // returns 1, `xls_bits_get_bit(bits, 1)` returns 0. bool xls_bits_get_bit(const struct xls_bits* bits, int64_t index); +// Converts the given bits value to a byte array, whose length is saved in +// byte_count_out. The caller must free the returned `bytes_out` pointer +// via `xls_bytes_free` if xls_bits_to_bytes returns true. +// If the conversion fails and it returns false, error_out is populated +// with an error message and the caller must free the +// `error_out` pointer with `xls_c_str_free`. +bool xls_bits_to_bytes(const struct xls_bits* bits, char** error_out, + uint8_t** bytes_out, size_t* byte_count_out); + +// Converts the given bits value to a signed or unsigned integer 64-bit integer +// value. If the conversion is not possible, false is returned, `error_out` +// contains the error message and the caller must free the `error_out` pointer +// with `xls_c_str_free`. +bool xls_bits_to_uint64(const struct xls_bits* bits, char** error_out, + uint64_t* value_out); +bool xls_bits_to_int64(const struct xls_bits* bits, char** error_out, + int64_t* value_out); struct xls_bits* xls_bits_width_slice(const struct xls_bits* bits, int64_t start, int64_t width); @@ -305,6 +336,11 @@ void xls_c_str_free(char* c_str); // Frees an array of C strings that were provided by this XLS public API. void xls_c_strs_free(char** c_strs, size_t count); + +// Frees the given `bytes` -- the bytes should have been allocated by the +// XLS library where ownership was passed back to the caller. +void xls_bytes_free(uint8_t* bytes); + // Returns a string representation of the given IR package `p`. bool xls_package_to_string(const struct xls_package* p, char** string_out); diff --git a/xls/public/c_api_symbols.txt b/xls/public/c_api_symbols.txt index ad692e30e1..79a8483ead 100644 --- a/xls/public/c_api_symbols.txt +++ b/xls/public/c_api_symbols.txt @@ -15,8 +15,11 @@ xls_bits_shift_right_arithmetic xls_bits_shift_right_logical xls_bits_smul xls_bits_sub +xls_bits_to_bytes xls_bits_to_debug_string +xls_bits_to_int64 xls_bits_to_string +xls_bits_to_uint64 xls_bits_umul xls_bits_width_slice xls_bits_xor @@ -56,6 +59,7 @@ xls_builder_base_add_xor xls_builder_base_add_xor_reduce xls_builder_base_add_zero_extend xls_bvalue_free +xls_bytes_free xls_c_str_free xls_c_strs_free xls_convert_dslx_path_to_ir @@ -172,6 +176,7 @@ xls_value_from_bits_owned xls_value_get_bits xls_value_get_element xls_value_get_element_count +xls_value_get_kind xls_value_make_array xls_value_make_false xls_value_make_sbits diff --git a/xls/public/c_api_test.cc b/xls/public/c_api_test.cc index 8cc0d38604..ca66ff1089 100644 --- a/xls/public/c_api_test.cc +++ b/xls/public/c_api_test.cc @@ -16,6 +16,7 @@ #include #include +#include #include // NOLINT #include #include @@ -304,6 +305,11 @@ TEST(XlsCApiTest, FlattenBitsValueToBits) { ASSERT_TRUE(xls_parse_typed_value("bits[32]:0x42", &error_out, &value)); absl::Cleanup free_value([value] { xls_value_free(value); }); + xls_value_kind kind = xls_value_kind_invalid; + ASSERT_TRUE(xls_value_get_kind(value, &error_out, &kind)); + EXPECT_EQ(kind, xls_value_kind_bits); + ASSERT_EQ(error_out, nullptr); + // Get the bits from within the value. Note that it's owned by the caller. xls_bits* value_bits = nullptr; ASSERT_TRUE(xls_value_get_bits(value, &error_out, &value_bits)); @@ -336,6 +342,10 @@ TEST(XlsCApiTest, FlattenTupleValueToBits) { xls_value* tuple = xls_value_make_tuple(/*element_count=*/2, elements); absl::Cleanup free_tuple([tuple] { xls_value_free(tuple); }); + xls_value_kind kind = xls_value_kind_invalid; + ASSERT_TRUE(xls_value_get_kind(tuple, &error_out, &kind)); + EXPECT_EQ(kind, xls_value_kind_tuple); + // Get the elements and check they are equal to the originals. xls_value* u3_7_extracted = nullptr; ASSERT_TRUE(xls_value_get_element(tuple, 0, &error_out, &u3_7_extracted)); @@ -390,6 +400,10 @@ TEST(XlsCApiTest, MakeArrayValue) { /*element_count=*/2, elements, &error_out, &array)); absl::Cleanup free_array([array] { xls_value_free(array); }); + xls_value_kind kind = xls_value_kind_invalid; + ASSERT_TRUE(xls_value_get_kind(array, &error_out, &kind)); + EXPECT_EQ(kind, xls_value_kind_array); + char* value_str = nullptr; ASSERT_TRUE(xls_value_to_string(array, &value_str)); absl::Cleanup free_value_str([value_str] { xls_c_str_free(value_str); }); @@ -422,6 +436,78 @@ TEST(XlsCApiTest, MakeBitsFromUint8DataWithMsbPadding) { EXPECT_EQ(xls_bits_get_bit(bits, 5), 1); } +TEST(XlsCApiTest, BitsToBytes) { + char* error_out = nullptr; + xls_bits* bits = nullptr; + ASSERT_TRUE(xls_bits_make_ubits(32, 0x0A0B0C0D, &error_out, &bits)); + uint8_t* bytes = nullptr; + size_t byte_count = 0; + + absl::Cleanup free_memory([&error_out, &bits, &bytes] { + xls_c_str_free(error_out); + xls_bits_free(bits); + xls_bytes_free(bytes); + }); + + ASSERT_TRUE(xls_bits_to_bytes(bits, &error_out, &bytes, &byte_count)); + EXPECT_EQ(byte_count, 4); + EXPECT_EQ(bytes[0], 0x0D); + EXPECT_EQ(bytes[1], 0x0C); + EXPECT_EQ(bytes[2], 0x0B); + EXPECT_EQ(bytes[3], 0x0A); +} + +TEST(XlsCApiTest, BitsToBytesWithPadding) { + char* error_out = nullptr; + xls_bits* bits = nullptr; + ASSERT_TRUE(xls_bits_make_ubits( + 34, 0b11'0000'0000'0000'0000'0000'0000'0000'0000, &error_out, &bits)); + uint8_t* bytes = nullptr; + size_t byte_count = 0; + + absl::Cleanup free_memory([&error_out, &bits, &bytes] { + xls_c_str_free(error_out); + xls_bits_free(bits); + xls_bytes_free(bytes); + }); + + ASSERT_TRUE(xls_bits_to_bytes(bits, &error_out, &bytes, &byte_count)); + EXPECT_EQ(byte_count, 5); + EXPECT_EQ(bytes[0], 0x00); + EXPECT_EQ(bytes[1], 0x00); + EXPECT_EQ(bytes[2], 0x00); + EXPECT_EQ(bytes[3], 0x00); + EXPECT_EQ(bytes[4], 0b11); +} + +TEST(XlsCApiTest, BitsToUint64Fit) { + char* error_out = nullptr; + xls_bits* bits = nullptr; + ASSERT_TRUE(xls_bits_make_ubits(64, 0x0A0B0C0D, &error_out, &bits)); + absl::Cleanup free_memory([&error_out, &bits] { + xls_c_str_free(error_out); + xls_bits_free(bits); + }); + + uint64_t value = 0; + ASSERT_TRUE(xls_bits_to_uint64(bits, &error_out, &value)); + EXPECT_EQ(value, 0x0A0B0C0D); +} + +TEST(XlsCApiTest, BitsToInt64Fit) { + char* error_out = nullptr; + xls_bits* bits = nullptr; + ASSERT_TRUE(xls_bits_make_ubits(64, 0x0A0B0C0D, &error_out, &bits)); + absl::Cleanup free_memory([&error_out, &bits] { + xls_c_str_free(error_out); + xls_bits_free(bits); + }); + + int64_t value = 0; + ASSERT_TRUE(xls_bits_to_int64(bits, &error_out, &value)); + EXPECT_EQ(value, 0x0A0B0C0D); +} + TEST(XlsCApiTest, MakeSbitsDoesNotFit) { char* error_out = nullptr; xls_bits* bits = nullptr;