Skip to content

feat: update API spec and C Libs #109

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 1 commit into from
Jul 14, 2024
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
10 changes: 10 additions & 0 deletions c/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
*.swp
*.*~
.DS_Store

.vs/
.vscode

build/
examples/ping_api
examples/exec_api
15 changes: 15 additions & 0 deletions c/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
[package]
name = "kcl-lib-c"
version = "0.9.1"
edition = "2021"
publish = false

[lib]
crate-type = ["cdylib", "staticlib"]
doc = false

[build-dependencies]
cbindgen = "0.26.0"

[dependencies]
kclvm-api = { git = "https://github.com/kcl-lang/kcl", version = "0.9.1" }
65 changes: 65 additions & 0 deletions c/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
# Directories
KCL_LIB_PATH = $(PWD)/target/release
KCL_LIB = -L$(KCL_LIB_PATH) -Wl,-rpath,$(KCL_LIB_PATH) -lkcl_lib_c
INCLUDE_DIR = include
LIB_DIR = lib
SRC_DIR = src
BUILD_DIR = build

# Example directories
EXAMPLES=$(wildcard ./examples/*.c)
EXAMPLE_OBJECTS=$(EXAMPLES:.c=.o)
EXAMPLE_TARGETS=$(EXAMPLES:.c=)

# Source Files
LIB_SOURCES = $(LIB_DIR)/pb_common.c $(LIB_DIR)/pb_decode.c $(LIB_DIR)/pb_encode.c $(LIB_DIR)/spec.pb.c
LIB_OBJECTS = $(addprefix $(BUILD_DIR)/, $(notdir $(LIB_SOURCES:.c=.o)))

# Static lib output
STATIC_LIB = $(LIB_DIR)/libkcl_lib_c.a

# CC flags
CCFLAGS=-I./include
CXXFLAGS=-I./include -std=c++14

all: cargo-build $(STATIC_LIB)

debug: CFLAGS += -g
debug: all

.PHONY: fmt
fmt:
cargo fmt
find . -name '*.cpp' -exec clang-format -i --style=WebKit --verbose {} \;
find . -name '*.c' -exec clang-format -i --style=WebKit --verbose {} \;

.PHONY: cargo-build
cargo-cargo:
cargo build -r

$(BUILD_DIR):
mkdir -p $(BUILD_DIR)

$(BUILD_DIR)/%.o: $(LIB_DIR)/%.c | $(BUILD_DIR)
$(CC) $(CCFLAGS) -c $< -o $@

$(STATIC_LIB): $(LIB_OBJECTS)
ar rcs $@ $^

.PHONY: examples
examples: $(STATIC_LIB) $(EXAMPLE_TARGETS)

$(EXAMPLE_TARGETS): % : %.o
$(CC) $(CCFLAGS) -o $@ $^ $(KCL_LIB) $(STATIC_LIB)

%.o: %.c
$(CC) $(CCFLAGS) -c $< -o $@

run: examples
./examples/exec_api

.PHONY: clean
clean:
rm -rf $(BUILD_DIR) *.o $(EXECUTABLE) $(STATIC_LIB)
rm -rf $(EXAMPLE_OBJECTS)
rm -rf $(EXAMPLE_TARGETS)
28 changes: 28 additions & 0 deletions c/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# KCL Artifact Library for C

This repo is under development, PRs welcome!

## Developing

```shell
cargo build --release
make
```

## Build Examples

```shell
make examples
```

## Run Examples

```shell
./examples/exec_api
```

## Formatting

```shell
make fmt
```
67 changes: 67 additions & 0 deletions c/examples/exec_api.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
#include <kcl_lib.h>

int main()
{
uint8_t buffer[BUFFER_SIZE];
uint8_t result_buffer[BUFFER_SIZE];
size_t message_length;
bool status;
const char* file_str = "./test_data/schema.k";
struct Buffer file = {
.buffer = file_str,
.len = strlen(file_str),
};
struct Buffer* files[] = { &file };
struct RepeatedString strs = { .repeated = &files[0], .index = 0, .max_size = 1 };
ExecProgram_Args args = ExecProgram_Args_init_zero;
args.k_filename_list.funcs.encode = encode_str_list;
args.k_filename_list.arg = &strs;

pb_ostream_t stream = pb_ostream_from_buffer(buffer, sizeof(buffer));
status = pb_encode(&stream, ExecProgram_Args_fields, &args);
message_length = stream.bytes_written;

if (!status) {
printf("Encoding failed: %s\n", PB_GET_ERROR(&stream));
return 1;
}

const char* api_str = "KclvmService.ExecProgram";
size_t result_length = call_native((const uint8_t*)api_str, strlen(api_str), buffer, message_length, result_buffer);
if (check_error_prefix(result_buffer)) {
printf("%s", result_buffer);
return 1;
}
pb_istream_t istream = pb_istream_from_buffer(result_buffer, result_length);

ExecProgram_Result result = ExecProgram_Result_init_default;
result.yaml_result.funcs.decode = decode_string;
result.json_result.funcs.decode = decode_string;
result.err_message.funcs.decode = decode_string;
result.log_message.funcs.decode = decode_string;

uint8_t yaml_value_buffer[BUFFER_SIZE] = { 0 };
result.yaml_result.arg = yaml_value_buffer;

uint8_t json_value_buffer[BUFFER_SIZE] = { 0 };
result.json_result.arg = json_value_buffer;

uint8_t err_value_buffer[BUFFER_SIZE] = { 0 };
result.err_message.arg = err_value_buffer;

uint8_t log_value_buffer[BUFFER_SIZE] = { 0 };
result.log_message.arg = log_value_buffer;

status = pb_decode(&istream, ExecProgram_Result_fields, &result);

if (!status) {
printf("Decoding failed: %s\n", PB_GET_ERROR(&istream));
return 1;
}

if (result.yaml_result.arg) {
printf("%s\n", (char*)result.yaml_result.arg);
}

return 0;
}
47 changes: 47 additions & 0 deletions c/examples/ping_api.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
#include <kcl_lib.h>

int main()
{
uint8_t buffer[BUFFER_SIZE];
uint8_t result_buffer[BUFFER_SIZE];
size_t message_length;
bool status;

Ping_Args ping_args = Ping_Args_init_zero;
pb_ostream_t stream = pb_ostream_from_buffer(buffer, sizeof(buffer));
ping_args.value.funcs.encode = encode_string;
const char* hello_str = "hello";
ping_args.value.arg = (void*)hello_str;

status = pb_encode(&stream, Ping_Args_fields, &ping_args);
message_length = stream.bytes_written;

if (!status) {
printf("Encoding failed: %s\n", PB_GET_ERROR(&stream));
return 1;
}

const char* api_str = "KclvmService.Ping";
size_t result_length = call_native((const uint8_t*)api_str, strlen(api_str), buffer, message_length, result_buffer);

pb_istream_t istream = pb_istream_from_buffer(result_buffer, result_length);
Ping_Args decoded_ping_args = Ping_Args_init_default;
decoded_ping_args.value.funcs.decode = decode_string;
uint8_t value_buffer[BUFFER_SIZE] = { 0 };
decoded_ping_args.value.arg = value_buffer;

status = pb_decode(&istream, Ping_Args_fields, &decoded_ping_args);

if (!status) {
printf("Decoding failed: %s\n", PB_GET_ERROR(&istream));
return 1;
}

if (decoded_ping_args.value.arg) {
printf("Decoded message: %s\n", (char*)decoded_ping_args.value.arg);
} else {
printf("No decoded message\n");
}

return 0;
}
90 changes: 90 additions & 0 deletions c/include/kcl_lib.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
#ifndef _API_H
#define _API_H

#ifdef __cplusplus
extern "C" {
#endif

#include <pb_decode.h>
#include <pb_encode.h>
#include <spec.pb.h>
#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>

#define BUFFER_SIZE 1024

struct Buffer {
const char* buffer;
size_t len;
};

struct RepeatedString {
struct Buffer** repeated;
int index;
size_t max_size;
};

// Encode callback function for setting string
bool encode_string(pb_ostream_t* stream, const pb_field_t* field, void* const* arg)
{
if (!pb_encode_tag_for_field(stream, field))
return false;
return pb_encode_string(stream, (const uint8_t*)(*arg), strlen((const char*)*arg));
}

bool encode_str_list(pb_ostream_t* stream, const pb_field_t* field, void* const* arg)
{
struct RepeatedString* req = *arg;
while (req->index < req->max_size) {
struct Buffer* sreq = req->repeated[req->index];
++req->index;
if (!pb_encode_tag(stream, PB_WT_STRING, field->tag)) {
return false;
}

if (!pb_encode_string(stream, (const uint8_t*)sreq->buffer, sreq->len)) {
return false;
}
}

return true;
}

// Decode callback function for getting string
bool decode_string(pb_istream_t* stream, const pb_field_t* field, void** arg)
{
uint8_t buffer[BUFFER_SIZE] = { 0 };

/* We could read block-by-block to avoid the large buffer... */
if (stream->bytes_left > sizeof(buffer) - 1)
return false;

if (!pb_read(stream, buffer, stream->bytes_left))
return false;

memcpy((char*)*arg, buffer, BUFFER_SIZE);
return true;
}

bool check_error_prefix(uint8_t result_buffer[])
{
if (result_buffer[0] == 'E' && result_buffer[1] == 'R' && result_buffer[2] == 'R' && result_buffer[3] == 'O' && result_buffer[4] == 'R') {
return true;
}
return false;
}

uintptr_t call_native(const uint8_t *name_ptr,
uintptr_t name_len,
const uint8_t *args_ptr,
uintptr_t args_len,
uint8_t *result_ptr);

#ifdef __cplusplus
} /* extern "C" */
#endif

#endif
Loading
Loading