Skip to content

Add a few C APIs #2047

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Apr 25, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
75 changes: 75 additions & 0 deletions xls/public/c_api.cc
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

#include <string.h> // NOLINT(modernize-deprecated-headers)

#include <algorithm>
#include <cstddef>
#include <cstdint>
#include <cstdlib>
Expand Down Expand Up @@ -347,6 +348,32 @@ struct xls_value* xls_value_clone(const struct xls_value* value) {
return reinterpret_cast<xls_value*>(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<const xls::Value*>(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);
Expand Down Expand Up @@ -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<const xls::Bits*>(bits);
std::vector<uint8_t> 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<const xls::Bits*>(bits);
absl::StatusOr<uint64_t> 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<const xls::Bits*>(bits);
absl::StatusOr<int64_t> 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);
Expand Down Expand Up @@ -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);
Expand Down
36 changes: 36 additions & 0 deletions xls/public/c_api.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);

Expand Down Expand Up @@ -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);

Expand Down Expand Up @@ -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);

Expand Down
5 changes: 5 additions & 0 deletions xls/public/c_api_symbols.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
86 changes: 86 additions & 0 deletions xls/public/c_api_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

#include <cstddef>
#include <cstdint>
#include <cstdlib>
#include <filesystem> // NOLINT
#include <functional>
#include <initializer_list>
Expand Down Expand Up @@ -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));
Expand Down Expand Up @@ -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));
Expand Down Expand Up @@ -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); });
Expand Down Expand Up @@ -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;
Expand Down