mirror of
https://github.com/lighttransport/tinyusdz.git
synced 2026-01-18 01:11:17 +01:00
mcp w.i.p.
This commit is contained in:
@@ -419,6 +419,9 @@ set(TINYUSDZ_SOURCES
|
||||
${PROJECT_SOURCE_DIR}/src/pprinter.cc
|
||||
${PROJECT_SOURCE_DIR}/src/stage.cc
|
||||
${PROJECT_SOURCE_DIR}/src/stage.hh
|
||||
${PROJECT_SOURCE_DIR}/src/uuid-gen.cc
|
||||
${PROJECT_SOURCE_DIR}/src/uuid-gen.hh
|
||||
|
||||
)
|
||||
|
||||
if (TINYUSDZ_WITH_TYDRA)
|
||||
|
||||
1117
src/external/civetweb/handle_form.inl
vendored
Normal file
1117
src/external/civetweb/handle_form.inl
vendored
Normal file
File diff suppressed because it is too large
Load Diff
262
src/external/civetweb/match.inl
vendored
Normal file
262
src/external/civetweb/match.inl
vendored
Normal file
@@ -0,0 +1,262 @@
|
||||
/* Reimplementation of pattern matching */
|
||||
/* This file is part of the CivetWeb web server.
|
||||
* See https://github.com/civetweb/civetweb/
|
||||
*/
|
||||
|
||||
|
||||
/* Initialize structure with 0 matches */
|
||||
static void
|
||||
match_context_reset(struct mg_match_context *mcx)
|
||||
{
|
||||
mcx->num_matches = 0;
|
||||
memset(mcx->match, 0, sizeof(mcx->match));
|
||||
}
|
||||
|
||||
|
||||
/* Add a new match to the list of matches */
|
||||
static void
|
||||
match_context_push(const char *str, size_t len, struct mg_match_context *mcx)
|
||||
{
|
||||
if (mcx->num_matches < MG_MATCH_CONTEXT_MAX_MATCHES) {
|
||||
mcx->match[mcx->num_matches].str = str;
|
||||
mcx->match[mcx->num_matches].len = len;
|
||||
mcx->num_matches++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static ptrdiff_t
|
||||
mg_match_impl(const char *pat,
|
||||
size_t pat_len,
|
||||
const char *str,
|
||||
struct mg_match_context *mcx)
|
||||
{
|
||||
/* Parse string */
|
||||
size_t i_pat = 0; /* Pattern index */
|
||||
size_t i_str = 0; /* Pattern index */
|
||||
|
||||
int case_sensitive = ((mcx != NULL) ? mcx->case_sensitive : 0); /* 0 or 1 */
|
||||
|
||||
while (i_pat < pat_len) {
|
||||
|
||||
/* Pattern ? matches one character, except / and NULL character */
|
||||
if ((pat[i_pat] == '?') && (str[i_str] != '\0')
|
||||
&& (str[i_str] != '/')) {
|
||||
size_t i_str_start = i_str;
|
||||
do {
|
||||
/* Advance as long as there are ? */
|
||||
i_pat++;
|
||||
i_str++;
|
||||
} while ((i_pat < pat_len) && (pat[i_pat] == '?')
|
||||
&& (str[i_str] != '\0') && (str[i_str] != '/'));
|
||||
|
||||
/* If we have a match context, add the substring we just found */
|
||||
if (mcx) {
|
||||
match_context_push(str + i_str_start, i_str - i_str_start, mcx);
|
||||
}
|
||||
|
||||
/* Reached end of pattern ? */
|
||||
if (i_pat == pat_len) {
|
||||
return (ptrdiff_t)i_str;
|
||||
}
|
||||
}
|
||||
|
||||
/* Pattern $ matches end of string */
|
||||
if (pat[i_pat] == '$') {
|
||||
return (str[i_str] == '\0') ? (ptrdiff_t)i_str : -1;
|
||||
}
|
||||
|
||||
/* Pattern * or ** matches multiple characters */
|
||||
if (pat[i_pat] == '*') {
|
||||
size_t len; /* length matched by "*" or "**" */
|
||||
ptrdiff_t ret;
|
||||
|
||||
i_pat++;
|
||||
if ((i_pat < pat_len) && (pat[i_pat] == '*')) {
|
||||
/* Pattern ** matches all */
|
||||
i_pat++;
|
||||
len = strlen(str + i_str);
|
||||
} else {
|
||||
/* Pattern * matches all except / character */
|
||||
len = strcspn(str + i_str, "/");
|
||||
}
|
||||
|
||||
if (i_pat == pat_len) {
|
||||
/* End of pattern reached. Add all to match context. */
|
||||
if (mcx) {
|
||||
match_context_push(str + i_str, len, mcx);
|
||||
}
|
||||
return ((ptrdiff_t)(i_str + len));
|
||||
}
|
||||
|
||||
/* This loop searches for the longest possible match */
|
||||
do {
|
||||
ret = mg_match_impl(pat + i_pat,
|
||||
(pat_len - (size_t)i_pat),
|
||||
str + i_str + len,
|
||||
mcx);
|
||||
} while ((ret == -1) && (len-- > 0));
|
||||
|
||||
/* If we have a match context, add the substring we just found */
|
||||
if (ret >= 0) {
|
||||
if (mcx) {
|
||||
match_context_push(str + i_str, len, mcx);
|
||||
}
|
||||
return ((ptrdiff_t)i_str + ret + (ptrdiff_t)len);
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
/* Single character compare */
|
||||
if (case_sensitive) {
|
||||
if (pat[i_pat] != str[i_str]) {
|
||||
/* case sensitive compare: mismatch */
|
||||
return -1;
|
||||
}
|
||||
} else if (lowercase(&pat[i_pat]) != lowercase(&str[i_str])) {
|
||||
/* case insensitive compare: mismatch */
|
||||
return -1;
|
||||
}
|
||||
|
||||
i_pat++;
|
||||
i_str++;
|
||||
}
|
||||
return (ptrdiff_t)i_str;
|
||||
}
|
||||
|
||||
|
||||
static ptrdiff_t
|
||||
mg_match_alternatives(const char *pat,
|
||||
size_t pat_len,
|
||||
const char *str,
|
||||
struct mg_match_context *mcx)
|
||||
{
|
||||
const char *match_alternative = (const char *)memchr(pat, '|', pat_len);
|
||||
|
||||
if (mcx != NULL) {
|
||||
match_context_reset(mcx);
|
||||
}
|
||||
|
||||
while (match_alternative != NULL) {
|
||||
/* Split at | for alternative match */
|
||||
size_t left_size = (size_t)(match_alternative - pat);
|
||||
|
||||
/* Try left string first */
|
||||
ptrdiff_t ret = mg_match_impl(pat, left_size, str, mcx);
|
||||
if (ret >= 0) {
|
||||
/* A 0-byte match is also valid */
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Reset possible incomplete match data */
|
||||
if (mcx != NULL) {
|
||||
match_context_reset(mcx);
|
||||
}
|
||||
|
||||
/* If no match: try right side */
|
||||
pat += left_size + 1;
|
||||
pat_len -= left_size + 1;
|
||||
match_alternative = (const char *)memchr(pat, '|', pat_len);
|
||||
}
|
||||
|
||||
/* Handled all | operators. This is the final string. */
|
||||
return mg_match_impl(pat, pat_len, str, mcx);
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
match_compare(const void *p1, const void *p2, void *user)
|
||||
{
|
||||
const struct mg_match_element *e1 = (const struct mg_match_element *)p1;
|
||||
const struct mg_match_element *e2 = (const struct mg_match_element *)p2;
|
||||
|
||||
/* unused */
|
||||
(void)user;
|
||||
|
||||
if (e1->str > e2->str) {
|
||||
return +1;
|
||||
}
|
||||
if (e1->str < e2->str) {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
#if defined(MG_EXPERIMENTAL_INTERFACES)
|
||||
CIVETWEB_API
|
||||
#else
|
||||
static
|
||||
#endif
|
||||
ptrdiff_t
|
||||
mg_match(const char *pat, const char *str, struct mg_match_context *mcx)
|
||||
{
|
||||
size_t pat_len = strlen(pat);
|
||||
ptrdiff_t ret = mg_match_alternatives(pat, pat_len, str, mcx);
|
||||
if (mcx != NULL) {
|
||||
if (ret < 0) {
|
||||
/* Remove possible incomplete data */
|
||||
match_context_reset(mcx);
|
||||
} else {
|
||||
/* Join "?*" to one pattern. */
|
||||
size_t i, j;
|
||||
|
||||
/* Use difference of two array elements instead of sizeof, since
|
||||
* there may be some additional padding bytes. */
|
||||
size_t elmsize =
|
||||
(size_t)(&mcx->match[1]) - (size_t)(&mcx->match[0]);
|
||||
|
||||
/* First sort the matches by address ("str" begin to end) */
|
||||
mg_sort(mcx->match, mcx->num_matches, elmsize, match_compare, NULL);
|
||||
|
||||
/* Join consecutive matches */
|
||||
i = 1;
|
||||
while (i < mcx->num_matches) {
|
||||
if ((mcx->match[i - 1].str + mcx->match[i - 1].len)
|
||||
== mcx->match[i].str) {
|
||||
/* Two matches are consecutive. Join length. */
|
||||
mcx->match[i - 1].len += mcx->match[i].len;
|
||||
|
||||
/* Shift all list elements. */
|
||||
for (j = i + 1; j < mcx->num_matches; j++) {
|
||||
mcx->match[j - 1].len = mcx->match[j].len;
|
||||
mcx->match[j - 1].str = mcx->match[j].str;
|
||||
}
|
||||
|
||||
/* Remove/blank last list element. */
|
||||
mcx->num_matches--;
|
||||
mcx->match[mcx->num_matches].str = NULL;
|
||||
mcx->match[mcx->num_matches].len = 0;
|
||||
|
||||
} else {
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
static ptrdiff_t
|
||||
match_prefix(const char *pattern, size_t pattern_len, const char *str)
|
||||
{
|
||||
if (pattern == NULL) {
|
||||
return -1;
|
||||
}
|
||||
return mg_match_alternatives(pattern, pattern_len, str, NULL);
|
||||
}
|
||||
|
||||
|
||||
static ptrdiff_t
|
||||
match_prefix_strlen(const char *pattern, const char *str)
|
||||
{
|
||||
if (pattern == NULL) {
|
||||
return -1;
|
||||
}
|
||||
return mg_match_alternatives(pattern, strlen(pattern), str, NULL);
|
||||
}
|
||||
|
||||
/* End of match.inl */
|
||||
342
src/external/civetweb/response.inl
vendored
Normal file
342
src/external/civetweb/response.inl
vendored
Normal file
@@ -0,0 +1,342 @@
|
||||
/* response.inl
|
||||
*
|
||||
* Bufferring for HTTP headers for HTTP response.
|
||||
* This function are only intended to be used at the server side.
|
||||
* Optional for HTTP/1.0 and HTTP/1.1, mandatory for HTTP/2.
|
||||
*
|
||||
* This file is part of the CivetWeb project.
|
||||
*/
|
||||
|
||||
#if defined(NO_RESPONSE_BUFFERING) && defined(USE_HTTP2)
|
||||
#error "HTTP2 works only if NO_RESPONSE_BUFFERING is not set"
|
||||
#endif
|
||||
|
||||
|
||||
/* Internal function to free header list */
|
||||
static void
|
||||
free_buffered_response_header_list(struct mg_connection *conn)
|
||||
{
|
||||
#if !defined(NO_RESPONSE_BUFFERING)
|
||||
while (conn->response_info.num_headers > 0) {
|
||||
conn->response_info.num_headers--;
|
||||
mg_free((void *)conn->response_info
|
||||
.http_headers[conn->response_info.num_headers]
|
||||
.name);
|
||||
conn->response_info.http_headers[conn->response_info.num_headers].name =
|
||||
0;
|
||||
mg_free((void *)conn->response_info
|
||||
.http_headers[conn->response_info.num_headers]
|
||||
.value);
|
||||
conn->response_info.http_headers[conn->response_info.num_headers]
|
||||
.value = 0;
|
||||
}
|
||||
#else
|
||||
(void)conn; /* Nothing to do */
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
/* Send first line of HTTP/1.x response */
|
||||
static int
|
||||
send_http1_response_status_line(struct mg_connection *conn)
|
||||
{
|
||||
const char *status_txt;
|
||||
const char *http_version = conn->request_info.http_version;
|
||||
int status_code = conn->status_code;
|
||||
|
||||
if ((status_code < 100) || (status_code > 999)) {
|
||||
/* Set invalid status code to "500 Internal Server Error" */
|
||||
status_code = 500;
|
||||
}
|
||||
if (!http_version) {
|
||||
http_version = "1.0";
|
||||
}
|
||||
|
||||
/* mg_get_response_code_text will never return NULL */
|
||||
status_txt = mg_get_response_code_text(conn, conn->status_code);
|
||||
|
||||
if (mg_printf(
|
||||
conn, "HTTP/%s %i %s\r\n", http_version, status_code, status_txt)
|
||||
< 10) {
|
||||
/* Network sending failed */
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
/* Initialize a new HTTP response
|
||||
* Parameters:
|
||||
* conn: Current connection handle.
|
||||
* status: HTTP status code (e.g., 200 for "OK").
|
||||
* Return:
|
||||
* 0: ok
|
||||
* -1: parameter error
|
||||
* -2: invalid connection type
|
||||
* -3: invalid connection status
|
||||
* -4: network error (only if built with NO_RESPONSE_BUFFERING)
|
||||
*/
|
||||
int
|
||||
mg_response_header_start(struct mg_connection *conn, int status)
|
||||
{
|
||||
int ret = 0;
|
||||
if ((conn == NULL) || (status < 100) || (status > 999)) {
|
||||
/* Parameter error */
|
||||
return -1;
|
||||
}
|
||||
if ((conn->connection_type != CONNECTION_TYPE_REQUEST)
|
||||
|| (conn->protocol_type == PROTOCOL_TYPE_WEBSOCKET)) {
|
||||
/* Only allowed in server context */
|
||||
return -2;
|
||||
}
|
||||
if (conn->request_state != 0) {
|
||||
/* only allowed if nothing was sent up to now */
|
||||
return -3;
|
||||
}
|
||||
conn->status_code = status;
|
||||
conn->request_state = 1;
|
||||
|
||||
/* Buffered response is stored, unbuffered response will be sent directly,
|
||||
* but we can only send HTTP/1.x response here */
|
||||
#if !defined(NO_RESPONSE_BUFFERING)
|
||||
free_buffered_response_header_list(conn);
|
||||
#else
|
||||
if (!send_http1_response_status_line(conn)) {
|
||||
ret = -4;
|
||||
};
|
||||
conn->request_state = 1; /* Reset from 10 to 1 */
|
||||
#endif
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/* Add a new HTTP response header line
|
||||
* Parameters:
|
||||
* conn: Current connection handle.
|
||||
* header: Header name.
|
||||
* value: Header value.
|
||||
* value_len: Length of header value, excluding the terminating zero.
|
||||
* Use -1 for "strlen(value)".
|
||||
* Return:
|
||||
* 0: ok
|
||||
* -1: parameter error
|
||||
* -2: invalid connection type
|
||||
* -3: invalid connection status
|
||||
* -4: too many headers
|
||||
* -5: out of memory
|
||||
*/
|
||||
int
|
||||
mg_response_header_add(struct mg_connection *conn,
|
||||
const char *header,
|
||||
const char *value,
|
||||
int value_len)
|
||||
{
|
||||
#if !defined(NO_RESPONSE_BUFFERING)
|
||||
int hidx;
|
||||
#endif
|
||||
|
||||
if ((conn == NULL) || (header == NULL) || (value == NULL)) {
|
||||
/* Parameter error */
|
||||
return -1;
|
||||
}
|
||||
if ((conn->connection_type != CONNECTION_TYPE_REQUEST)
|
||||
|| (conn->protocol_type == PROTOCOL_TYPE_WEBSOCKET)) {
|
||||
/* Only allowed in server context */
|
||||
return -2;
|
||||
}
|
||||
if (conn->request_state != 1) {
|
||||
/* only allowed if mg_response_header_start has been called before */
|
||||
return -3;
|
||||
}
|
||||
|
||||
#if !defined(NO_RESPONSE_BUFFERING)
|
||||
hidx = conn->response_info.num_headers;
|
||||
if (hidx >= MG_MAX_HEADERS) {
|
||||
/* Too many headers */
|
||||
return -4;
|
||||
}
|
||||
|
||||
/* Alloc new element */
|
||||
conn->response_info.http_headers[hidx].name =
|
||||
mg_strdup_ctx(header, conn->phys_ctx);
|
||||
if (value_len >= 0) {
|
||||
char *hbuf =
|
||||
(char *)mg_malloc_ctx((unsigned)value_len + 1, conn->phys_ctx);
|
||||
if (hbuf) {
|
||||
memcpy(hbuf, value, (unsigned)value_len);
|
||||
hbuf[value_len] = 0;
|
||||
}
|
||||
conn->response_info.http_headers[hidx].value = hbuf;
|
||||
} else {
|
||||
conn->response_info.http_headers[hidx].value =
|
||||
mg_strdup_ctx(value, conn->phys_ctx);
|
||||
}
|
||||
|
||||
if ((conn->response_info.http_headers[hidx].name == 0)
|
||||
|| (conn->response_info.http_headers[hidx].value == 0)) {
|
||||
/* Out of memory */
|
||||
mg_free((void *)conn->response_info.http_headers[hidx].name);
|
||||
conn->response_info.http_headers[hidx].name = 0;
|
||||
mg_free((void *)conn->response_info.http_headers[hidx].value);
|
||||
conn->response_info.http_headers[hidx].value = 0;
|
||||
return -5;
|
||||
}
|
||||
|
||||
/* OK, header stored */
|
||||
conn->response_info.num_headers++;
|
||||
|
||||
#else
|
||||
if (value_len >= 0) {
|
||||
mg_printf(conn, "%s: %.*s\r\n", header, (int)value_len, value);
|
||||
} else {
|
||||
mg_printf(conn, "%s: %s\r\n", header, value);
|
||||
}
|
||||
conn->request_state = 1; /* Reset from 10 to 1 */
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* forward */
|
||||
static int parse_http_headers(char **buf, struct mg_header hdr[MG_MAX_HEADERS]);
|
||||
|
||||
|
||||
/* Add a complete header string (key + value).
|
||||
* Parameters:
|
||||
* conn: Current connection handle.
|
||||
* http1_headers: Header line(s) in the form "name: value".
|
||||
* Return:
|
||||
* >=0: no error, number of header lines added
|
||||
* -1: parameter error
|
||||
* -2: invalid connection type
|
||||
* -3: invalid connection status
|
||||
* -4: too many headers
|
||||
* -5: out of memory
|
||||
*/
|
||||
int
|
||||
mg_response_header_add_lines(struct mg_connection *conn,
|
||||
const char *http1_headers)
|
||||
{
|
||||
struct mg_header add_hdr[MG_MAX_HEADERS];
|
||||
int num_hdr, i, ret;
|
||||
char *workbuffer, *parse;
|
||||
|
||||
/* We need to work on a copy of the work buffer, sice parse_http_headers
|
||||
* will modify */
|
||||
workbuffer = mg_strdup_ctx(http1_headers, conn->phys_ctx);
|
||||
if (!workbuffer) {
|
||||
/* Out of memory */
|
||||
return -5;
|
||||
}
|
||||
|
||||
/* Call existing method to split header buffer */
|
||||
parse = workbuffer;
|
||||
num_hdr = parse_http_headers(&parse, add_hdr);
|
||||
ret = num_hdr;
|
||||
|
||||
for (i = 0; i < num_hdr; i++) {
|
||||
int lret =
|
||||
mg_response_header_add(conn, add_hdr[i].name, add_hdr[i].value, -1);
|
||||
if ((ret > 0) && (lret < 0)) {
|
||||
/* Store error return value */
|
||||
ret = lret;
|
||||
}
|
||||
}
|
||||
|
||||
/* mg_response_header_add created a copy, so we can free the original */
|
||||
mg_free(workbuffer);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
#if defined(USE_HTTP2)
|
||||
static int http2_send_response_headers(struct mg_connection *conn);
|
||||
#endif
|
||||
|
||||
|
||||
/* Send http response
|
||||
* Parameters:
|
||||
* conn: Current connection handle.
|
||||
* Return:
|
||||
* 0: ok
|
||||
* -1: parameter error
|
||||
* -2: invalid connection type
|
||||
* -3: invalid connection status
|
||||
* -4: network send failed
|
||||
*/
|
||||
int
|
||||
mg_response_header_send(struct mg_connection *conn)
|
||||
{
|
||||
#if !defined(NO_RESPONSE_BUFFERING)
|
||||
int i;
|
||||
int has_date = 0;
|
||||
int has_connection = 0;
|
||||
#endif
|
||||
|
||||
if (conn == NULL) {
|
||||
/* Parameter error */
|
||||
return -1;
|
||||
}
|
||||
if ((conn->connection_type != CONNECTION_TYPE_REQUEST)
|
||||
|| (conn->protocol_type == PROTOCOL_TYPE_WEBSOCKET)) {
|
||||
/* Only allowed in server context */
|
||||
return -2;
|
||||
}
|
||||
if (conn->request_state != 1) {
|
||||
/* only allowed if mg_response_header_start has been called before */
|
||||
return -3;
|
||||
}
|
||||
|
||||
/* State: 2 */
|
||||
conn->request_state = 2;
|
||||
|
||||
#if !defined(NO_RESPONSE_BUFFERING)
|
||||
#if defined(USE_HTTP2)
|
||||
if (conn->protocol_type == PROTOCOL_TYPE_HTTP2) {
|
||||
int ret = http2_send_response_headers(conn);
|
||||
free_buffered_response_header_list(conn);
|
||||
return (ret ? 0 : -4);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Send */
|
||||
if (!send_http1_response_status_line(conn)) {
|
||||
free_buffered_response_header_list(conn);
|
||||
return -4;
|
||||
};
|
||||
for (i = 0; i < conn->response_info.num_headers; i++) {
|
||||
mg_printf(conn,
|
||||
"%s: %s\r\n",
|
||||
conn->response_info.http_headers[i].name,
|
||||
conn->response_info.http_headers[i].value);
|
||||
|
||||
/* Check for some special headers */
|
||||
if (!mg_strcasecmp("Date", conn->response_info.http_headers[i].name)) {
|
||||
has_date = 1;
|
||||
}
|
||||
if (!mg_strcasecmp("Connection",
|
||||
conn->response_info.http_headers[i].name)) {
|
||||
has_connection = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (!has_date) {
|
||||
time_t curtime = time(NULL);
|
||||
char date[64];
|
||||
gmt_time_string(date, sizeof(date), &curtime);
|
||||
mg_printf(conn, "Date: %s\r\n", date);
|
||||
}
|
||||
if (!has_connection) {
|
||||
mg_printf(conn, "Connection: %s\r\n", suggest_connection_header(conn));
|
||||
}
|
||||
#endif
|
||||
|
||||
mg_write(conn, "\r\n", 2);
|
||||
conn->request_state = 3;
|
||||
|
||||
/* ok */
|
||||
free_buffered_response_header_list(conn);
|
||||
return 0;
|
||||
}
|
||||
@@ -365,7 +365,7 @@ bool LoadUSDAFromMemory(const uint8_t *addr, const size_t length,
|
||||
///
|
||||
/// @return true upon success
|
||||
///
|
||||
bool LoadLayerFromFile(const std::string &filename, Layer *stage,
|
||||
bool LoadLayerFromFile(const std::string &filename, Layer *layer,
|
||||
std::string *warn, std::string *err,
|
||||
const USDLoadOptions &options = USDLoadOptions());
|
||||
|
||||
|
||||
32
src/tydra/mcp-context.hh
Normal file
32
src/tydra/mcp-context.hh
Normal file
@@ -0,0 +1,32 @@
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <map>
|
||||
|
||||
#include "prim-types.hh"
|
||||
|
||||
namespace tinyusdz {
|
||||
|
||||
namespace tydra {
|
||||
namespace mcp {
|
||||
|
||||
struct USDLayer
|
||||
{
|
||||
std::string uri;
|
||||
Layer layer;
|
||||
};
|
||||
|
||||
struct Context
|
||||
{
|
||||
|
||||
// loaded USD assets
|
||||
// key = UUID
|
||||
std::map<std::string, USDLayer> layers;
|
||||
|
||||
};
|
||||
|
||||
} // namespace mcp
|
||||
} // namespace tydra
|
||||
} // namespace tinyusdz
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "mcp-server.hh"
|
||||
#include "mcp-tools.hh"
|
||||
#include "mcp-resources.hh"
|
||||
#include "mcp-context.hh"
|
||||
|
||||
#if defined(TINYUSDZ_WITH_MCP_SERVER)
|
||||
|
||||
@@ -118,6 +119,8 @@ class MCPServer::Impl {
|
||||
|
||||
// Create JSON-RPC error response
|
||||
JsonRpcResponse create_error_response(int code, const std::string& message, const nlohmann::json& id = nullptr);
|
||||
|
||||
Context mcp_ctx_;
|
||||
};
|
||||
|
||||
int MCPServer::Impl::mcp_handler(struct mg_connection *conn, void *user_data) {
|
||||
@@ -348,9 +351,8 @@ bool MCPServer::Impl::init(int port, const std::string &host) {
|
||||
|
||||
});
|
||||
|
||||
register_method("tools/call",[](const nlohmann::json ¶ms, std::string &err) -> nlohmann::json {
|
||||
register_method("tools/call",[this](const nlohmann::json ¶ms, std::string &err) -> nlohmann::json {
|
||||
|
||||
(void)err;
|
||||
|
||||
if (!params.contains("name")) {
|
||||
err = "`name` is missing in params.";
|
||||
@@ -361,7 +363,8 @@ bool MCPServer::Impl::init(int port, const std::string &host) {
|
||||
nlohmann::json empty{};
|
||||
nlohmann::json result;
|
||||
|
||||
bool ret = mcp::CallTool(tool_name, params.contains("arguments") ? params["arguments"] : empty, result);
|
||||
std::string err_;
|
||||
bool ret = mcp::CallTool(mcp_ctx_, tool_name, params.contains("arguments") ? params["arguments"] : empty, result, err_);
|
||||
|
||||
if (!ret) {
|
||||
err = "Unknown tool: " + tool_name;
|
||||
|
||||
@@ -2,8 +2,10 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <map>
|
||||
|
||||
namespace tinyusdz {
|
||||
|
||||
namespace tydra {
|
||||
namespace mcp {
|
||||
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
#include "mcp-tools.hh"
|
||||
#include "mcp-server.hh"
|
||||
#include "mcp-context.hh"
|
||||
#include <string>
|
||||
#include "tinyusdz.hh"
|
||||
#include "uuid-gen.hh"
|
||||
|
||||
#ifdef __clang__
|
||||
#pragma clang diagnostic push
|
||||
@@ -18,6 +22,8 @@ namespace mcp {
|
||||
|
||||
namespace {
|
||||
bool GetVersion(nlohmann::json &result);
|
||||
bool LoadUSDLayerFromFile(Context &ctx, const nlohmann::json &args, nlohmann::json &result, std::string &err);
|
||||
|
||||
|
||||
bool GetVersion(nlohmann::json &result) {
|
||||
|
||||
@@ -39,6 +45,45 @@ bool GetVersion(nlohmann::json &result) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
bool LoadUSDLayerFromFile(Context &ctx, const nlohmann::json &args, nlohmann::json &result, std::string &err) {
|
||||
if (!args.contains("uri")) {
|
||||
err = "`uri` param not found.\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string uri = args["uri"];
|
||||
|
||||
|
||||
Layer layer;
|
||||
std::string warn;
|
||||
USDLoadOptions options;
|
||||
if (!LoadLayerFromFile(uri, &layer, &warn, &err, options)) {
|
||||
err = "Failed to load layer from file: " + err + "\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!warn.empty()) {
|
||||
result["warnings"] = warn;
|
||||
}
|
||||
|
||||
std::string uuid = generateUUID();
|
||||
|
||||
if (ctx.layers.count(uuid)) {
|
||||
// This should not be happen.
|
||||
err = "Internal error. UUID conflict\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
USDLayer usd_layer;
|
||||
usd_layer.uri = uri;
|
||||
usd_layer.layer = std::move(layer);
|
||||
|
||||
ctx.layers.emplace(uuid, std::move(usd_layer));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
bool GetToolsList(nlohmann::json &result) {
|
||||
@@ -64,11 +109,13 @@ bool GetToolsList(nlohmann::json &result) {
|
||||
}
|
||||
|
||||
|
||||
bool CallTool(const std::string &tool_name, const nlohmann::json &args, nlohmann::json &result) {
|
||||
bool CallTool(Context &ctx, const std::string &tool_name, const nlohmann::json &args, nlohmann::json &result, std::string &err) {
|
||||
(void)args;
|
||||
|
||||
if (tool_name == "get_version") {
|
||||
return GetVersion(result);
|
||||
} else if (tool_name == "load_usd_layer") {
|
||||
return LoadUSDLayerFromFile(ctx, args, result, err);
|
||||
}
|
||||
|
||||
// tool not found.
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
#include "mcp-context.hh"
|
||||
|
||||
#ifdef __clang__
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Weverything"
|
||||
@@ -24,7 +26,7 @@ bool GetToolsList(
|
||||
|
||||
|
||||
// TODO: Batch call tools
|
||||
bool CallTool(const std::string &tool_name, const nlohmann::json &args, nlohmann::json &result);
|
||||
bool CallTool(Context &ctx, const std::string &tool_name, const nlohmann::json &args, nlohmann::json &result, std::string &err);
|
||||
|
||||
} // namespace mcp
|
||||
} // namespace tydra
|
||||
|
||||
96
src/uuid-gen.cc
Normal file
96
src/uuid-gen.cc
Normal file
@@ -0,0 +1,96 @@
|
||||
// SPDX-License-Identifier: Apache 2.0
|
||||
// Copyright 2025-Present Light Transport Entertainment, Inc.
|
||||
//
|
||||
// Simple UUID Version 4 generator implementation
|
||||
//
|
||||
#include "uuid-gen.hh"
|
||||
|
||||
#include <cstdint>
|
||||
#include <random>
|
||||
#include <sstream>
|
||||
#include <iomanip>
|
||||
|
||||
namespace tinyusdz {
|
||||
|
||||
UUIDGenerator::UUIDGenerator()
|
||||
: rd_(), gen_(rd_()), dis_(0, 0xFFFFFFFF) {
|
||||
}
|
||||
|
||||
std::string UUIDGenerator::generate() {
|
||||
// Generate 4 32-bit random numbers (128 bits total)
|
||||
uint32_t data[4];
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
data[i] = dis_(gen_);
|
||||
}
|
||||
|
||||
// Convert to bytes for easier manipulation
|
||||
uint8_t bytes[16];
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
bytes[i * 4 + 0] = (data[i] >> 24) & 0xFF;
|
||||
bytes[i * 4 + 1] = (data[i] >> 16) & 0xFF;
|
||||
bytes[i * 4 + 2] = (data[i] >> 8) & 0xFF;
|
||||
bytes[i * 4 + 3] = data[i] & 0xFF;
|
||||
}
|
||||
|
||||
// Set version (4) in the most significant 4 bits of the 7th byte
|
||||
bytes[6] = (bytes[6] & 0x0F) | 0x40;
|
||||
|
||||
// Set variant (10) in the most significant 2 bits of the 9th byte
|
||||
bytes[8] = (bytes[8] & 0x3F) | 0x80;
|
||||
|
||||
// Format as UUID string: xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx
|
||||
std::ostringstream oss;
|
||||
oss << std::hex << std::setfill('0');
|
||||
|
||||
// First group: 8 hex digits
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
oss << std::setw(2) << static_cast<int>(bytes[i]);
|
||||
}
|
||||
oss << '-';
|
||||
|
||||
// Second group: 4 hex digits
|
||||
for (int i = 4; i < 6; ++i) {
|
||||
oss << std::setw(2) << static_cast<int>(bytes[i]);
|
||||
}
|
||||
oss << '-';
|
||||
|
||||
// Third group: 4 hex digits (with version)
|
||||
for (int i = 6; i < 8; ++i) {
|
||||
oss << std::setw(2) << static_cast<int>(bytes[i]);
|
||||
}
|
||||
oss << '-';
|
||||
|
||||
// Fourth group: 4 hex digits (with variant)
|
||||
for (int i = 8; i < 10; ++i) {
|
||||
oss << std::setw(2) << static_cast<int>(bytes[i]);
|
||||
}
|
||||
oss << '-';
|
||||
|
||||
// Fifth group: 12 hex digits
|
||||
for (int i = 10; i < 16; ++i) {
|
||||
oss << std::setw(2) << static_cast<int>(bytes[i]);
|
||||
}
|
||||
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
std::string UUIDGenerator::generateUUID() {
|
||||
#ifdef __clang__
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wexit-time-destructors"
|
||||
#endif
|
||||
|
||||
static UUIDGenerator generator;
|
||||
|
||||
#ifdef __clang__
|
||||
#pragma clang diagnostic pop
|
||||
#endif
|
||||
|
||||
return generator.generate();
|
||||
}
|
||||
|
||||
std::string generateUUID() {
|
||||
return UUIDGenerator::generateUUID();
|
||||
}
|
||||
|
||||
} // namespace tinyusdz
|
||||
45
src/uuid-gen.hh
Normal file
45
src/uuid-gen.hh
Normal file
@@ -0,0 +1,45 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <random>
|
||||
#include <sstream>
|
||||
#include <iomanip>
|
||||
|
||||
namespace tinyusdz {
|
||||
|
||||
///
|
||||
/// Simple UUID Version 4 generator
|
||||
///
|
||||
/// Generates random UUIDs in the format: xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx
|
||||
/// where x is a random hexadecimal digit and y is one of 8, 9, A, or B
|
||||
///
|
||||
class UUIDGenerator {
|
||||
public:
|
||||
UUIDGenerator();
|
||||
|
||||
///
|
||||
/// Generate a new UUID v4 string
|
||||
/// @return UUID string in standard format (e.g., "550e8400-e29b-41d4-a716-446655440000")
|
||||
///
|
||||
std::string generate();
|
||||
|
||||
///
|
||||
/// Generate a new UUID v4 string (static version)
|
||||
/// @return UUID string in standard format
|
||||
///
|
||||
static std::string generateUUID();
|
||||
|
||||
private:
|
||||
std::random_device rd_;
|
||||
std::mt19937 gen_;
|
||||
std::uniform_int_distribution<uint32_t> dis_;
|
||||
};
|
||||
|
||||
///
|
||||
/// Generate a UUID v4 string (convenience function)
|
||||
/// @return UUID string in standard format
|
||||
///
|
||||
std::string generateUUID();
|
||||
|
||||
} // namespace tinyusdz
|
||||
|
||||
Reference in New Issue
Block a user