Skip to content
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

Add swss::Table to c api #964

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
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
1 change: 1 addition & 0 deletions common/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ common_libswsscommon_la_SOURCES = \
common/c-api/zmqserver.cpp \
common/c-api/zmqconsumerstatetable.cpp \
common/c-api/zmqproducerstatetable.cpp \
common/c-api/table.cpp \
common/performancetimer.cpp

common_libswsscommon_la_CXXFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(LIBNL_CFLAGS) $(CODE_COVERAGE_CXXFLAGS)
Expand Down
71 changes: 71 additions & 0 deletions common/c-api/table.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
#include <string>
#include <vector>

#include "../dbconnector.h"
#include "../table.h"
#include "table.h"
#include "util.h"

using namespace swss;
using namespace std;

SWSSTable SWSSTable_new(SWSSDBConnector db, const char *tableName) {
SWSSTry(return (SWSSTable) new Table((DBConnector *)db, string(tableName)));
}

void SWSSTable_free(SWSSTable tbl) {
SWSSTry(delete (Table *)tbl);
}

int8_t SWSSTable_get(SWSSTable tbl, const char *key, SWSSFieldValueArray *outValues) {
SWSSTry({
vector<FieldValueTuple> fvs;
bool exists = ((Table *)tbl)->get(string(key), fvs);
if (exists) {
*outValues = makeFieldValueArray(fvs);
return 1;
} else {
return 0;
}
});
}

int8_t SWSSTable_hget(SWSSTable tbl, const char *key, const char *field, SWSSString *outValue) {
SWSSTry({
string s;
bool exists = ((Table *)tbl)->hget(string(key), string(field), s);
if (exists) {
*outValue = makeString(move(s));
return 1;
} else {
return 0;
}
});
}

void SWSSTable_set(SWSSTable tbl, const char *key, SWSSFieldValueArray values) {
SWSSTry({
vector<FieldValueTuple> fvs = takeFieldValueArray(values);
((Table *)tbl)->set(string(key), fvs);
});
}

void SWSSTable_hset(SWSSTable tbl, const char *key, const char *field, SWSSStrRef value) {
SWSSTry({ ((Table *)tbl)->hset(string(key), string(field), takeStrRef(value)); });
}

void SWSSTable_del(SWSSTable tbl, const char *key) {
SWSSTry({ ((Table *)tbl)->del(string(key)); });
}

void SWSSTable_hdel(SWSSTable tbl, const char *key, const char *field) {
SWSSTry({ ((Table *)tbl)->hdel(string(key), string(field)); });
}

SWSSStringArray SWSSTable_getKeys(SWSSTable tbl) {
SWSSTry({
vector<string> keys;
((Table *)tbl)->getKeys(keys);
return makeStringArray(move(keys));
})
}
41 changes: 41 additions & 0 deletions common/c-api/table.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
#ifndef SWSS_COMMON_C_API_TABLE_H
#define SWSS_COMMON_C_API_TABLE_H

#include "dbconnector.h"
#include "util.h"

#ifdef __cplusplus
extern "C" {
#endif

#include <stdint.h>

typedef struct SWSSTableOpaque *SWSSTable;

SWSSTable SWSSTable_new(SWSSDBConnector db, const char *tableName);

void SWSSTable_free(SWSSTable tbl);

// If the key exists, populates outValues with the table's values and returns 1.
// If the key doesn't exist, returns 0.
int8_t SWSSTable_get(SWSSTable tbl, const char *key, SWSSFieldValueArray *outValues);

// If the key and field exist, populates outValue with the field's value and returns 1.
// If the key doesn't exist, returns 0.
int8_t SWSSTable_hget(SWSSTable tbl, const char *key, const char *field, SWSSString *outValue);

void SWSSTable_set(SWSSTable tbl, const char *key, SWSSFieldValueArray values);

void SWSSTable_hset(SWSSTable tbl, const char *key, const char *field, SWSSStrRef value);

void SWSSTable_del(SWSSTable tbl, const char *key);

void SWSSTable_hdel(SWSSTable tbl, const char *key, const char *field);

SWSSStringArray SWSSTable_getKeys(SWSSTable tbl);

#ifdef __cplusplus
}
#endif

#endif
4 changes: 4 additions & 0 deletions common/c-api/util.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,7 @@ void SWSSFieldValueArray_free(SWSSFieldValueArray arr) {
void SWSSKeyOpFieldValuesArray_free(SWSSKeyOpFieldValuesArray kfvs) {
SWSSTry(delete[] kfvs.data);
}

void SWSSStringArray_free(SWSSStringArray arr) {
SWSSTry(delete[] arr.data);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a "const char **data;", do you need delete all string pointer in this array first?

Copy link
Contributor Author

@erer1243 erer1243 Jan 9, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The c api free functions don't do that because the data is sometimes user-owned. The behavior is expressed in the header file comments.

}
25 changes: 25 additions & 0 deletions common/c-api/util.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ typedef struct SWSSStringOpaque *SWSSString;
typedef struct SWSSStrRefOpaque *SWSSStrRef;

// FFI version of swss::FieldValueTuple
// field should be freed with libc's free()
typedef struct {
const char *field;
SWSSString value;
Expand Down Expand Up @@ -59,6 +60,13 @@ typedef enum {
SWSSSelectResult_SIGNAL = 2,
} SWSSSelectResult;

// FFI version of std::vector<std::string>
// data strings should be freed with libc's free()
typedef struct {
uint64_t len;
const char **data;
} SWSSStringArray;

// data should not include a null terminator
SWSSString SWSSString_new(const char *data, uint64_t length);

Expand All @@ -85,6 +93,10 @@ void SWSSFieldValueArray_free(SWSSFieldValueArray arr);
// grained control of ownership).
void SWSSKeyOpFieldValuesArray_free(SWSSKeyOpFieldValuesArray kfvs);

// arr.data may be null. This is not recursive - elements must be freed separately (for finer
// grained control of ownership).
void SWSSStringArray_free(SWSSStringArray arr);

#ifdef __cplusplus
}
#endif
Expand Down Expand Up @@ -214,6 +226,19 @@ template <class T> static inline SWSSKeyOpFieldValuesArray makeKeyOpFieldValuesA
return out;
}

static inline SWSSStringArray makeStringArray(std::vector<std::string> &&in) {
const char **data = new const char*[in.size()];

size_t i = 0;
for (std::string &s : in)
data[i++] = strdup(s.c_str());

SWSSStringArray out;
out.len = (uint64_t)in.size();
out.data = data;
return out;
}

static inline std::string takeString(SWSSString s) {
return std::string(std::move(*((std::string *)s)));
}
Expand Down
81 changes: 69 additions & 12 deletions tests/c_api_ut.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include "common/c-api/dbconnector.h"
#include "common/c-api/producerstatetable.h"
#include "common/c-api/subscriberstatetable.h"
#include "common/c-api/table.h"
#include "common/c-api/util.h"
#include "common/c-api/zmqclient.h"
#include "common/c-api/zmqconsumerstatetable.h"
Expand Down Expand Up @@ -40,14 +41,18 @@ static void sortKfvs(vector<KeyOpFieldsValuesTuple> &kfvs) {

#define free(x) std::free(const_cast<void *>(reinterpret_cast<const void *>(x)));

static void freeFieldValuesArray(SWSSFieldValueArray arr) {
for (uint64_t i = 0; i < arr.len; i++) {
free(arr.data[i].field);
SWSSString_free(arr.data[i].value);
}
SWSSFieldValueArray_free(arr);
}

static void freeKeyOpFieldValuesArray(SWSSKeyOpFieldValuesArray arr) {
for (uint64_t i = 0; i < arr.len; i++) {
free(arr.data[i].key);
for (uint64_t j = 0; j < arr.data[i].fieldValues.len; j++) {
free(arr.data[i].fieldValues.data[j].field);
SWSSString_free(arr.data[i].fieldValues.data[j].value);
}
SWSSFieldValueArray_free(arr.data[i].fieldValues);
freeFieldValuesArray(arr.data[i].fieldValues);
}
SWSSKeyOpFieldValuesArray_free(arr);
}
Expand Down Expand Up @@ -105,6 +110,57 @@ TEST(c_api, DBConnector) {
SWSSDBConnector_free(db);
}

TEST(c_api, Table) {
clearDB();
SWSSStringManager sm;

SWSSDBConnector db = SWSSDBConnector_new_named("TEST_DB", 1000, true);
SWSSTable tbl = SWSSTable_new(db, "mytable");

SWSSFieldValueArray fvs;
SWSSString ss;
EXPECT_FALSE(SWSSTable_get(tbl, "mykey", &fvs));
EXPECT_FALSE(SWSSTable_hget(tbl, "mykey", "myfield", &ss));

SWSSStringArray keys = SWSSTable_getKeys(tbl);
EXPECT_EQ(keys.len, 0);
SWSSStringArray_free(keys);

SWSSTable_hset(tbl, "mykey", "myfield", sm.makeStrRef("myvalue"));
keys = SWSSTable_getKeys(tbl);
ASSERT_EQ(keys.len, 1);
EXPECT_STREQ(keys.data[0], "mykey");
free(keys.data[0]);
SWSSStringArray_free(keys);

ASSERT_TRUE(SWSSTable_hget(tbl, "mykey", "myfield", &ss));
EXPECT_STREQ(SWSSStrRef_c_str((SWSSStrRef)ss), "myvalue");
SWSSString_free(ss);

SWSSTable_hdel(tbl, "mykey", "myfield");
EXPECT_FALSE(SWSSTable_hget(tbl, "mykey", "myfield", &ss));

SWSSFieldValueTuple data[2] = {{.field = "myfield1", .value = sm.makeString("myvalue1")},
{.field = "myfield2", .value = sm.makeString("myvalue2")}};
fvs.len = 2;
fvs.data = data;
SWSSTable_set(tbl, "mykey", fvs);

ASSERT_TRUE(SWSSTable_get(tbl, "mykey", &fvs));
EXPECT_EQ(fvs.len, 2);
EXPECT_STREQ(data[0].field, fvs.data[0].field);
EXPECT_STREQ(data[1].field, fvs.data[1].field);
freeFieldValuesArray(fvs);

SWSSTable_del(tbl, "mykey");
keys = SWSSTable_getKeys(tbl);
EXPECT_EQ(keys.len, 0);
SWSSStringArray_free(keys);

SWSSTable_free(tbl);
SWSSDBConnector_free(db);
}

TEST(c_api, ConsumerProducerStateTables) {
clearDB();
SWSSStringManager sm;
Expand All @@ -119,9 +175,8 @@ TEST(c_api, ConsumerProducerStateTables) {
ASSERT_EQ(arr.len, 0);
freeKeyOpFieldValuesArray(arr);

SWSSFieldValueTuple data[2] = {
{.field = "myfield1", .value = sm.makeString("myvalue1")},
{.field = "myfield2", .value = sm.makeString("myvalue2")}};
SWSSFieldValueTuple data[2] = {{.field = "myfield1", .value = sm.makeString("myvalue1")},
{.field = "myfield2", .value = sm.makeString("myvalue2")}};
SWSSFieldValueArray values = {
.len = 2,
.data = data,
Expand Down Expand Up @@ -154,7 +209,7 @@ TEST(c_api, ConsumerProducerStateTables) {
ASSERT_EQ(fieldValues1.size(), 1);
EXPECT_EQ(fieldValues1[0].first, "myfield3");
EXPECT_EQ(fieldValues1[0].second, "myvalue3");

arr = SWSSConsumerStateTable_pops(cst);
EXPECT_EQ(arr.len, 0);
freeKeyOpFieldValuesArray(arr);
Expand Down Expand Up @@ -244,14 +299,16 @@ TEST(c_api, ZmqConsumerProducerStateTable) {
// On flag = 0, we use the ZmqProducerStateTable
// On flag = 1, we use the ZmqClient directly
for (int flag = 0; flag < 2; flag++) {
SWSSFieldValueTuple values_key1_data[2] = {{.field = "myfield1", .value = sm.makeString("myvalue1")},
{.field = "myfield2", .value = sm.makeString("myvalue2")}};
SWSSFieldValueTuple values_key1_data[2] = {
{.field = "myfield1", .value = sm.makeString("myvalue1")},
{.field = "myfield2", .value = sm.makeString("myvalue2")}};
SWSSFieldValueArray values_key1 = {
.len = 2,
.data = values_key1_data,
};

SWSSFieldValueTuple values_key2_data[1] = {{.field = "myfield3", .value = sm.makeString("myvalue3")}};
SWSSFieldValueTuple values_key2_data[1] = {
{.field = "myfield3", .value = sm.makeString("myvalue3")}};
SWSSFieldValueArray values_key2 = {
.len = 1,
.data = values_key2_data,
Expand Down
Loading