mirror of
https://github.com/bytecodealliance/wasm-micro-runtime.git
synced 2025-06-18 02:59:21 +00:00
add nn-cli example (#4373)
an example application with flexible cli options which aims to allow us to perform any wasi-nn operations. eg. ``` --load-graph=file=fixture/model.xml,file=fixture/model.bin,id=graph --init-execution-context=graph-id=graph,id=ctx --set-input=file=fixture/tensor.bgr,context-id=ctx,dim=1,dim=3,dim=224,dim=224 --compute=context-id=ctx --get-output=context-id=ctx,file=output.bin ```
This commit is contained in:
parent
c9b8c16088
commit
b3ce192e1a
12
wamr-wasi-extensions/samples/nn-cli/CMakeLists.txt
Normal file
12
wamr-wasi-extensions/samples/nn-cli/CMakeLists.txt
Normal file
|
@ -0,0 +1,12 @@
|
|||
# Copyright (C) 2025 Midokura Japan KK. All rights reserved.
|
||||
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
|
||||
cmake_minimum_required(VERSION 3.14)
|
||||
|
||||
set(CMAKE_C_STANDARD 99)
|
||||
set(CMAKE_C_STANDARD_REQUIRED YES)
|
||||
|
||||
project(nn-cli LANGUAGES C)
|
||||
add_executable(nn-cli main.c fileio.c map.c)
|
||||
find_package(wamr-wasi-nn REQUIRED)
|
||||
target_link_libraries(nn-cli wamr-wasi-nn)
|
73
wamr-wasi-extensions/samples/nn-cli/fileio.c
Normal file
73
wamr-wasi-extensions/samples/nn-cli/fileio.c
Normal file
|
@ -0,0 +1,73 @@
|
|||
/*
|
||||
* Copyright (C) 2025 Midokura Japan KK. All rights reserved.
|
||||
* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
*/
|
||||
|
||||
/*
|
||||
* modified copy-and-paste from:
|
||||
* https://github.com/yamt/toywasm/blob/0eaad8cacd0cc7692946ff19b25994f106113be8/lib/fileio.c
|
||||
*/
|
||||
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "fileio.h"
|
||||
|
||||
int
|
||||
map_file(const char *path, void **pp, size_t *sizep)
|
||||
{
|
||||
void *p;
|
||||
size_t size;
|
||||
ssize_t ssz;
|
||||
int fd;
|
||||
int ret;
|
||||
|
||||
fd = open(path, O_RDONLY);
|
||||
if (fd == -1) {
|
||||
ret = errno;
|
||||
assert(ret != 0);
|
||||
return ret;
|
||||
}
|
||||
struct stat st;
|
||||
ret = fstat(fd, &st);
|
||||
if (ret == -1) {
|
||||
ret = errno;
|
||||
assert(ret != 0);
|
||||
close(fd);
|
||||
return ret;
|
||||
}
|
||||
size = st.st_size;
|
||||
if (size > 0) {
|
||||
p = malloc(size);
|
||||
}
|
||||
else {
|
||||
/* Avoid a confusing error */
|
||||
p = malloc(1);
|
||||
}
|
||||
if (p == NULL) {
|
||||
close(fd);
|
||||
return ENOMEM;
|
||||
}
|
||||
ssz = read(fd, p, size);
|
||||
if (ssz != size) {
|
||||
ret = errno;
|
||||
assert(ret != 0);
|
||||
close(fd);
|
||||
return ret;
|
||||
}
|
||||
close(fd);
|
||||
*pp = p;
|
||||
*sizep = size;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
unmap_file(void *p, size_t sz)
|
||||
{
|
||||
free(p);
|
||||
}
|
14
wamr-wasi-extensions/samples/nn-cli/fileio.h
Normal file
14
wamr-wasi-extensions/samples/nn-cli/fileio.h
Normal file
|
@ -0,0 +1,14 @@
|
|||
/*
|
||||
* Copyright (C) 2025 Midokura Japan KK. All rights reserved.
|
||||
* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
*/
|
||||
|
||||
/*
|
||||
* modified copy-and-paste from:
|
||||
* https://github.com/yamt/toywasm/blob/0eaad8cacd0cc7692946ff19b25994f106113be8/lib/fileio.h
|
||||
*/
|
||||
|
||||
int
|
||||
map_file(const char *filename, void **pp, size_t *szp);
|
||||
void
|
||||
unmap_file(void *p, size_t sz);
|
496
wamr-wasi-extensions/samples/nn-cli/main.c
Normal file
496
wamr-wasi-extensions/samples/nn-cli/main.c
Normal file
|
@ -0,0 +1,496 @@
|
|||
/*
|
||||
* Copyright (C) 2025 Midokura Japan KK. All rights reserved.
|
||||
* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
*/
|
||||
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <getopt.h>
|
||||
#include <inttypes.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <wamr/wasi_ephemeral_nn.h>
|
||||
|
||||
#include "fileio.h"
|
||||
#include "map.h"
|
||||
|
||||
static struct map graphs;
|
||||
static struct map contexts;
|
||||
|
||||
static void
|
||||
load_graph(char *options)
|
||||
{
|
||||
int target = wasi_ephemeral_nn_target_cpu;
|
||||
int encoding = wasi_ephemeral_nn_encoding_openvino;
|
||||
const char *id = "default";
|
||||
wasi_ephemeral_nn_graph_builder *builders = NULL;
|
||||
size_t nbuilders = 0;
|
||||
enum {
|
||||
opt_id,
|
||||
opt_file,
|
||||
opt_encoding,
|
||||
opt_target,
|
||||
};
|
||||
static char *const keylistp[] = {
|
||||
[opt_id] = "id",
|
||||
[opt_file] = "file",
|
||||
[opt_encoding] = "encoding",
|
||||
[opt_target] = "target",
|
||||
NULL,
|
||||
};
|
||||
while (*options) {
|
||||
extern char *suboptarg;
|
||||
char *value;
|
||||
const char *saved = options;
|
||||
switch (getsubopt(&options, keylistp, &value)) {
|
||||
case opt_id:
|
||||
if (value == NULL) {
|
||||
fprintf(stderr, "no value for %s\n", saved);
|
||||
exit(2);
|
||||
}
|
||||
id = value;
|
||||
break;
|
||||
case opt_file:
|
||||
if (value == NULL) {
|
||||
fprintf(stderr, "no value for %s\n", saved);
|
||||
exit(2);
|
||||
}
|
||||
builders =
|
||||
realloc(builders, (nbuilders + 1) * sizeof(*builders));
|
||||
if (builders == NULL) {
|
||||
exit(1);
|
||||
}
|
||||
wasi_ephemeral_nn_graph_builder *b = &builders[nbuilders++];
|
||||
int ret = map_file(value, (void *)&b->buf, (void *)&b->size);
|
||||
if (ret != 0) {
|
||||
fprintf(stderr, "map_file \"%s\" failed: %s\n", value,
|
||||
strerror(ret));
|
||||
exit(1);
|
||||
}
|
||||
break;
|
||||
case opt_encoding:
|
||||
if (value == NULL) {
|
||||
fprintf(stderr, "no value for %s\n", saved);
|
||||
exit(2);
|
||||
}
|
||||
encoding = atoi(value);
|
||||
break;
|
||||
case opt_target:
|
||||
if (value == NULL) {
|
||||
fprintf(stderr, "no value for %s\n", saved);
|
||||
exit(2);
|
||||
}
|
||||
target = atoi(value);
|
||||
break;
|
||||
case -1:
|
||||
fprintf(stderr, "unknown subopt %s\n", saved);
|
||||
exit(2);
|
||||
}
|
||||
}
|
||||
|
||||
wasi_ephemeral_nn_error nnret;
|
||||
wasi_ephemeral_nn_graph g;
|
||||
nnret = wasi_ephemeral_nn_load(builders, nbuilders, encoding, target, &g);
|
||||
size_t i;
|
||||
for (i = 0; i < nbuilders; i++) {
|
||||
wasi_ephemeral_nn_graph_builder *b = &builders[i];
|
||||
unmap_file(b->buf, b->size);
|
||||
}
|
||||
if (nnret != wasi_ephemeral_nn_error_success) {
|
||||
fprintf(stderr, "load failed with %d\n", (int)nnret);
|
||||
exit(1);
|
||||
}
|
||||
map_set(&graphs, id, g);
|
||||
}
|
||||
|
||||
static void
|
||||
init_execution_context(char *options)
|
||||
{
|
||||
const char *id = "default";
|
||||
const char *graph_id = "default";
|
||||
enum {
|
||||
opt_id,
|
||||
opt_graph_id,
|
||||
};
|
||||
static char *const keylistp[] = {
|
||||
[opt_id] = "id",
|
||||
[opt_graph_id] = "graph-id",
|
||||
NULL,
|
||||
};
|
||||
while (*options) {
|
||||
extern char *suboptarg;
|
||||
char *value;
|
||||
const char *saved = options;
|
||||
switch (getsubopt(&options, keylistp, &value)) {
|
||||
case opt_id:
|
||||
if (value == NULL) {
|
||||
fprintf(stderr, "no value for %s\n", saved);
|
||||
exit(2);
|
||||
}
|
||||
id = value;
|
||||
break;
|
||||
case opt_graph_id:
|
||||
if (value == NULL) {
|
||||
fprintf(stderr, "no value for %s\n", saved);
|
||||
exit(2);
|
||||
}
|
||||
graph_id = value;
|
||||
break;
|
||||
case -1:
|
||||
fprintf(stderr, "unknown subopt %s\n", saved);
|
||||
exit(2);
|
||||
}
|
||||
}
|
||||
|
||||
wasi_ephemeral_nn_graph g = map_get(&graphs, graph_id);
|
||||
wasi_ephemeral_nn_graph_execution_context c;
|
||||
wasi_ephemeral_nn_error nnret;
|
||||
nnret = wasi_ephemeral_nn_init_execution_context(g, &c);
|
||||
if (nnret != wasi_ephemeral_nn_error_success) {
|
||||
fprintf(stderr, "init_execution_context failed with %d\n", (int)nnret);
|
||||
exit(1);
|
||||
}
|
||||
map_set(&contexts, id, c);
|
||||
}
|
||||
|
||||
static void
|
||||
set_input(char *options)
|
||||
{
|
||||
int ret;
|
||||
const char *context_id = "default";
|
||||
uint32_t idx = 0;
|
||||
wasi_ephemeral_nn_tensor tensor = {
|
||||
.dimensions = { .buf = NULL, .size = 0, },
|
||||
.type = wasi_ephemeral_nn_type_fp32,
|
||||
.data = NULL,
|
||||
};
|
||||
void *buf = NULL;
|
||||
size_t sz = 0;
|
||||
enum {
|
||||
opt_context_id,
|
||||
opt_dim,
|
||||
opt_type,
|
||||
opt_idx,
|
||||
opt_file,
|
||||
};
|
||||
static char *const keylistp[] = {
|
||||
[opt_context_id] = "context-id",
|
||||
[opt_dim] = "dim",
|
||||
[opt_type] = "type",
|
||||
[opt_idx] = "idx",
|
||||
[opt_file] = "file",
|
||||
NULL,
|
||||
};
|
||||
while (*options) {
|
||||
extern char *suboptarg;
|
||||
char *value;
|
||||
const char *saved = options;
|
||||
switch (getsubopt(&options, keylistp, &value)) {
|
||||
case opt_context_id:
|
||||
if (value == NULL) {
|
||||
fprintf(stderr, "no value for %s\n", saved);
|
||||
exit(2);
|
||||
}
|
||||
context_id = value;
|
||||
break;
|
||||
case opt_dim:
|
||||
if (value == NULL) {
|
||||
fprintf(stderr, "no value for %s\n", saved);
|
||||
exit(2);
|
||||
}
|
||||
wasi_ephemeral_nn_tensor_dimensions *dims = &tensor.dimensions;
|
||||
|
||||
dims->buf =
|
||||
realloc(dims->buf, (dims->size + 1) * sizeof(*dims->buf));
|
||||
if (dims->buf == NULL) {
|
||||
exit(1);
|
||||
}
|
||||
dims->buf[dims->size++] = atoi(value);
|
||||
break;
|
||||
case opt_type:
|
||||
if (value == NULL) {
|
||||
fprintf(stderr, "no value for %s\n", saved);
|
||||
exit(2);
|
||||
}
|
||||
tensor.type = atoi(value);
|
||||
break;
|
||||
case opt_file:
|
||||
if (value == NULL) {
|
||||
fprintf(stderr, "no value for %s\n", saved);
|
||||
exit(2);
|
||||
}
|
||||
if (buf != NULL) {
|
||||
fprintf(stderr, "duplicated tensor data\n");
|
||||
exit(2);
|
||||
}
|
||||
ret = map_file(value, &buf, &sz);
|
||||
if (ret != 0) {
|
||||
fprintf(stderr, "map_file \"%s\" failed: %s\n", value,
|
||||
strerror(ret));
|
||||
exit(1);
|
||||
}
|
||||
break;
|
||||
case opt_idx:
|
||||
if (value == NULL) {
|
||||
fprintf(stderr, "no value for %s\n", saved);
|
||||
exit(2);
|
||||
}
|
||||
idx = atoi(value);
|
||||
break;
|
||||
case -1:
|
||||
fprintf(stderr, "unknown subopt %s\n", saved);
|
||||
exit(2);
|
||||
}
|
||||
}
|
||||
|
||||
if (tensor.dimensions.size == 0) {
|
||||
fprintf(stderr, "no dimension is given\n");
|
||||
exit(2);
|
||||
}
|
||||
if (buf == NULL) {
|
||||
fprintf(stderr, "no tensor is given\n");
|
||||
exit(2);
|
||||
}
|
||||
|
||||
/*
|
||||
* REVISIT: we can check the tensor size against type/dimensions
|
||||
* and warn the user if unexpected.
|
||||
*/
|
||||
|
||||
wasi_ephemeral_nn_error nnret;
|
||||
wasi_ephemeral_nn_graph_execution_context c =
|
||||
map_get(&contexts, context_id);
|
||||
tensor.data = buf;
|
||||
nnret = wasi_ephemeral_nn_set_input(c, idx, &tensor);
|
||||
unmap_file(buf, sz);
|
||||
if (nnret != wasi_ephemeral_nn_error_success) {
|
||||
fprintf(stderr, "set_input failed with %d\n", (int)nnret);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
compute(char *options)
|
||||
{
|
||||
const char *context_id = "default";
|
||||
enum {
|
||||
opt_context_id,
|
||||
};
|
||||
static char *const keylistp[] = {
|
||||
[opt_context_id] = "context-id",
|
||||
NULL,
|
||||
};
|
||||
while (*options) {
|
||||
extern char *suboptarg;
|
||||
char *value;
|
||||
const char *saved = options;
|
||||
switch (getsubopt(&options, keylistp, &value)) {
|
||||
case opt_context_id:
|
||||
if (value == NULL) {
|
||||
fprintf(stderr, "no value for %s\n", saved);
|
||||
exit(2);
|
||||
}
|
||||
context_id = value;
|
||||
break;
|
||||
case -1:
|
||||
fprintf(stderr, "unknown subopt %s\n", saved);
|
||||
exit(2);
|
||||
}
|
||||
}
|
||||
|
||||
wasi_ephemeral_nn_graph_execution_context c =
|
||||
map_get(&contexts, context_id);
|
||||
wasi_ephemeral_nn_error nnret;
|
||||
nnret = wasi_ephemeral_nn_compute(c);
|
||||
if (nnret != wasi_ephemeral_nn_error_success) {
|
||||
fprintf(stderr, "compute failed with %d\n", (int)nnret);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
get_output(char *options)
|
||||
{
|
||||
int ret;
|
||||
const char *outfile = NULL;
|
||||
const char *context_id = "default";
|
||||
uint32_t idx = 0;
|
||||
enum {
|
||||
opt_context_id,
|
||||
opt_idx,
|
||||
opt_file,
|
||||
};
|
||||
static char *const keylistp[] = {
|
||||
[opt_context_id] = "context-id",
|
||||
[opt_idx] = "idx",
|
||||
[opt_file] = "file",
|
||||
NULL,
|
||||
};
|
||||
while (*options) {
|
||||
extern char *suboptarg;
|
||||
char *value;
|
||||
const char *saved = options;
|
||||
switch (getsubopt(&options, keylistp, &value)) {
|
||||
case opt_context_id:
|
||||
if (value == NULL) {
|
||||
fprintf(stderr, "no value for %s\n", saved);
|
||||
exit(2);
|
||||
}
|
||||
context_id = value;
|
||||
break;
|
||||
case opt_file:
|
||||
if (value == NULL) {
|
||||
fprintf(stderr, "no value for %s\n", saved);
|
||||
exit(2);
|
||||
}
|
||||
outfile = value;
|
||||
break;
|
||||
case opt_idx:
|
||||
if (value == NULL) {
|
||||
fprintf(stderr, "no value for %s\n", saved);
|
||||
exit(2);
|
||||
}
|
||||
idx = atoi(value);
|
||||
break;
|
||||
case -1:
|
||||
fprintf(stderr, "unknown subopt %s\n", saved);
|
||||
exit(2);
|
||||
}
|
||||
}
|
||||
|
||||
int outfd = -1;
|
||||
if (outfile != NULL) {
|
||||
outfd = open(outfile, O_CREAT | O_TRUNC | O_WRONLY);
|
||||
if (outfd == -1) {
|
||||
fprintf(stderr, "failed to open output file \"%s\": %s\n", outfile,
|
||||
strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
wasi_ephemeral_nn_error nnret;
|
||||
wasi_ephemeral_nn_graph_execution_context c =
|
||||
map_get(&contexts, context_id);
|
||||
void *resultbuf = NULL;
|
||||
size_t resultbufsz = 256;
|
||||
uint32_t resultsz;
|
||||
retry:
|
||||
resultbuf = realloc(resultbuf, resultbufsz);
|
||||
if (resultbuf == NULL) {
|
||||
exit(1);
|
||||
}
|
||||
nnret =
|
||||
wasi_ephemeral_nn_get_output(c, 0, resultbuf, resultbufsz, &resultsz);
|
||||
if (nnret == wasi_ephemeral_nn_error_too_large) {
|
||||
resultbufsz *= 2;
|
||||
goto retry;
|
||||
}
|
||||
if (nnret != wasi_ephemeral_nn_error_success) {
|
||||
fprintf(stderr, "get_output failed with %d\n", (int)nnret);
|
||||
exit(1);
|
||||
}
|
||||
if (outfd != -1) {
|
||||
ssize_t written = write(outfd, resultbuf, resultsz);
|
||||
if (written == -1) {
|
||||
fprintf(stderr, "failed to write: %s\n", strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
if (written == -1) {
|
||||
fprintf(stderr, "unexpetecd write length %zu (expected %zu)\n",
|
||||
written, (size_t)resultsz);
|
||||
exit(1);
|
||||
}
|
||||
ret = close(outfd);
|
||||
if (ret != 0) {
|
||||
fprintf(stderr, "failed to close: %s\n", strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
else {
|
||||
fprintf(stderr, "WARNING: discarding %zu bytes output\n",
|
||||
(size_t)resultsz);
|
||||
}
|
||||
}
|
||||
|
||||
enum longopt {
|
||||
opt_load_graph = 0x100,
|
||||
opt_init_execution_context,
|
||||
opt_set_input,
|
||||
opt_compute,
|
||||
opt_get_output,
|
||||
};
|
||||
|
||||
static const struct option longopts[] = {
|
||||
{
|
||||
"load-graph",
|
||||
required_argument,
|
||||
NULL,
|
||||
opt_load_graph,
|
||||
},
|
||||
{
|
||||
"init-execution-context",
|
||||
optional_argument,
|
||||
NULL,
|
||||
opt_init_execution_context,
|
||||
},
|
||||
{
|
||||
"set-input",
|
||||
required_argument,
|
||||
NULL,
|
||||
opt_set_input,
|
||||
},
|
||||
{
|
||||
"compute",
|
||||
optional_argument,
|
||||
NULL,
|
||||
opt_compute,
|
||||
},
|
||||
{
|
||||
"get-output",
|
||||
optional_argument,
|
||||
NULL,
|
||||
opt_get_output,
|
||||
},
|
||||
{
|
||||
NULL,
|
||||
0,
|
||||
NULL,
|
||||
0,
|
||||
},
|
||||
};
|
||||
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
extern char *optarg;
|
||||
int ch;
|
||||
int longidx;
|
||||
while ((ch = getopt_long(argc, argv, "", longopts, &longidx)) != -1) {
|
||||
switch (ch) {
|
||||
case opt_load_graph:
|
||||
load_graph(optarg);
|
||||
break;
|
||||
case opt_init_execution_context:
|
||||
init_execution_context(optarg ? optarg : "");
|
||||
break;
|
||||
case opt_set_input:
|
||||
set_input(optarg);
|
||||
break;
|
||||
case opt_compute:
|
||||
compute(optarg ? optarg : "");
|
||||
break;
|
||||
case opt_get_output:
|
||||
get_output(optarg ? optarg : "");
|
||||
break;
|
||||
default:
|
||||
exit(2);
|
||||
}
|
||||
}
|
||||
exit(0);
|
||||
}
|
58
wamr-wasi-extensions/samples/nn-cli/map.c
Normal file
58
wamr-wasi-extensions/samples/nn-cli/map.c
Normal file
|
@ -0,0 +1,58 @@
|
|||
/*
|
||||
* Copyright (C) 2025 Midokura Japan KK. All rights reserved.
|
||||
* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "map.h"
|
||||
|
||||
static uintmax_t *
|
||||
map_find_slot(struct map *m, const char *name)
|
||||
{
|
||||
size_t i;
|
||||
for (i = 0; i < m->nentries; i++) {
|
||||
if (!strcmp(m->entries[i].k, name)) {
|
||||
return &m->entries[i].v;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
map_append(struct map *m, const char *k, uintmax_t v)
|
||||
{
|
||||
m->entries = realloc(m->entries, (m->nentries + 1) * sizeof(*m->entries));
|
||||
if (m->entries == NULL) {
|
||||
exit(1);
|
||||
}
|
||||
struct map_entry *e = &m->entries[m->nentries++];
|
||||
e->k = k;
|
||||
e->v = v;
|
||||
}
|
||||
|
||||
void
|
||||
map_set(struct map *m, const char *k, uintmax_t v)
|
||||
{
|
||||
uintmax_t *p = map_find_slot(m, k);
|
||||
if (p != NULL) {
|
||||
fprintf(stderr, "duplicated id \"%s\"\n", k);
|
||||
exit(1);
|
||||
}
|
||||
map_append(m, k, v);
|
||||
}
|
||||
|
||||
uintmax_t
|
||||
map_get(struct map *m, const char *k)
|
||||
{
|
||||
uintmax_t *p = map_find_slot(m, k);
|
||||
if (p == NULL) {
|
||||
fprintf(stderr, "id \"%s\" not found\n", k);
|
||||
exit(1);
|
||||
}
|
||||
return *p;
|
||||
}
|
19
wamr-wasi-extensions/samples/nn-cli/map.h
Normal file
19
wamr-wasi-extensions/samples/nn-cli/map.h
Normal file
|
@ -0,0 +1,19 @@
|
|||
/*
|
||||
* Copyright (C) 2025 Midokura Japan KK. All rights reserved.
|
||||
* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
struct map {
|
||||
struct map_entry {
|
||||
const char *k;
|
||||
uintmax_t v;
|
||||
} * entries;
|
||||
size_t nentries;
|
||||
};
|
||||
|
||||
void
|
||||
map_set(struct map *m, const char *k, uintmax_t v);
|
||||
uintmax_t
|
||||
map_get(struct map *m, const char *k);
|
|
@ -20,6 +20,12 @@ cmake -B build-app-nn \
|
|||
samples/nn
|
||||
cmake --build build-app-nn
|
||||
|
||||
cmake -B build-app-nn-cli \
|
||||
-DCMAKE_TOOLCHAIN_FILE=${WASI_SDK}/share/cmake/wasi-sdk.cmake \
|
||||
-DCMAKE_PREFIX_PATH=${PREFIX} \
|
||||
samples/nn-cli
|
||||
cmake --build build-app-nn-cli
|
||||
|
||||
cmake -B build-app-socket-nslookup \
|
||||
-DCMAKE_TOOLCHAIN_FILE=${WASI_SDK}/share/cmake/wasi-sdk-pthread.cmake \
|
||||
-DCMAKE_PREFIX_PATH=${PREFIX} \
|
||||
|
|
Loading…
Reference in New Issue
Block a user