chore: assorted refactors

pull/26/head
Sean McBride 5 years ago
parent 2220cf34a0
commit 4a80e6dd70

@ -12,16 +12,18 @@ struct http_header {
char *val; char *val;
}; };
struct http_resp_header { struct http_response_header {
char *hdr; char *header;
int len; int length;
}; };
struct http_request { struct http_request {
struct http_header headers[HTTP_HEADERS_MAX]; struct http_header headers[HTTP_HEADERS_MAX];
int nheaders; int header_count;
char * body; char * body;
int bodylen, bodyrlen; int body_length;
// TODO: What does bodyrlen mean? Does this suggest that I've misunderstood what bodylen was?
int bodyrlen;
// additional for http-parser // additional for http-parser
int last_was_value; int last_was_value;
int header_end; int header_end;
@ -29,12 +31,12 @@ struct http_request {
}; };
struct http_response { struct http_response {
struct http_resp_header headers[HTTP_HEADERS_MAX]; struct http_response_header headers[HTTP_HEADERS_MAX];
int nheaders; int header_count;
char * body; char * body;
int bodylen; int body_length;
char * status; char * status;
int stlen; int status_length;
#ifdef USE_HTTP_UVIO #ifdef USE_HTTP_UVIO
uv_buf_t bufs[HTTP_HEADERS_MAX * 2 + 3]; // max headers, one line for status code, remaining for body! uv_buf_t bufs[HTTP_HEADERS_MAX * 2 + 3]; // max headers, one line for status code, remaining for body!
#else #else

@ -5,47 +5,79 @@
int http_request_body_get_sb(struct sandbox *sandbox, char **body); int http_request_body_get_sb(struct sandbox *sandbox, char **body);
int http_request_parse_sb(struct sandbox *sandbox, size_t l); int http_request_parse_sb(struct sandbox *sandbox, size_t l);
int http_response_header_set_sb(struct sandbox *sandbox, char *h, int len); int http_response_header_set_sb(struct sandbox *sandbox, char *h, int length);
int http_response_body_set_sb(struct sandbox *sandbox, char *body, int len); int http_response_body_set_sb(struct sandbox *sandbox, char *body, int length);
int http_response_status_set_sb(struct sandbox *sandbox, char *status, int len); int http_response_status_set_sb(struct sandbox *sandbox, char *status, int length);
int http_response_vector_sb(struct sandbox *sandbox); int http_response_vector_sb(struct sandbox *sandbox);
void http_init(void); void http_init(void);
/**
* Gets the request of the body
* @param body pointer of pointer that we want to set to the http_request's body
* @returns the length of the http_request's body
**/
static inline int static inline int
http_request_body_get(char **b) http_request_body_get(char **body)
{ {
return http_request_body_get_sb(sandbox_current(), b); return http_request_body_get_sb(sandbox_current(), body);
} }
/**
* Set an HTTP Response Header on the current sandbox
* @param header string of the header that we want to set
* @param length the length of the header string
* @returns 0 (abends program in case of error)
**/
static inline int static inline int
http_response_header_set(char *key, int len) http_response_header_set(char *header, int length)
{ {
return http_response_header_set_sb(sandbox_current(), key, len); return http_response_header_set_sb(sandbox_current(), header, length);
} }
/**
* Set an HTTP Response Body on the current sandbox
* @param body string of the body that we want to set
* @param length the length of the body string
* @returns 0 (abends program in case of error)
**/
static inline int static inline int
http_response_body_set(char *body, int len) http_response_body_set(char *body, int length)
{ {
return http_response_body_set_sb(sandbox_current(), body, len); return http_response_body_set_sb(sandbox_current(), body, length);
} }
/**
* Set an HTTP Response Status on the current sandbox
* @param status string of the status we want to set
* @param length the length of the status
* @returns 0 (abends program in case of error)
**/
static inline int static inline int
http_response_status_set(char *status, int len) http_response_status_set(char *status, int length)
{ {
return http_response_status_set_sb(sandbox_current(), status, len); return http_response_status_set_sb(sandbox_current(), status, length);
} }
/**
* ??? on the current sandbox
* @returns ???
**/
static inline int static inline int
http_response_vector(void) http_response_vector(void)
{ {
return http_response_vector_sb(sandbox_current()); return http_response_vector_sb(sandbox_current());
} }
/**
* ???
* @param length ????
* @returns 0
**/
static inline int static inline int
http_request_parse(size_t l) http_request_parse(size_t length)
{ {
return http_request_parse_sb(sandbox_current(), l); return http_request_parse_sb(sandbox_current(), length);
} }
#endif /* SRFT_HTTP_API_H */ #endif /* SRFT_HTTP_API_H */

@ -1,29 +1,44 @@
#ifndef SFRT_RUNTIME_H #ifndef SFRT_RUNTIME_H
#define SFRT_RUNTIME_H #define SFRT_RUNTIME_H
#include "types.h"
#include <sys/epoll.h> // for epoll_create1(), epoll_ctl(), struct epoll_event
#include <uv.h> #include <uv.h>
#include "sandbox.h"
#include "module.h" #include "module.h"
#include <sys/epoll.h> // for epoll_create1(), epoll_ctl(), struct epoll_event #include "sandbox.h"
#include "types.h"
// global queue for stealing (work-stealing-deque) extern int epoll_file_descriptor;
extern struct deque_sandbox *global_deque; extern struct deque_sandbox *global_deque;
extern pthread_mutex_t global_deque_mutex; extern pthread_mutex_t global_deque_mutex;
extern int epoll_file_descriptor; extern __thread uv_loop_t uvio;
void alloc_linear_memory(void); void alloc_linear_memory(void);
void expand_memory(void); void expand_memory(void);
void free_linear_memory(void *base, u32 bound, u32 max); void free_linear_memory(void *base, u32 bound, u32 max);
// Assumption: bounds_check < WASM_PAGE_SIZE INLINE char *get_function_from_table(u32 idx, u32 type_id);
INLINE char *get_memory_ptr_for_runtime(u32 offset, u32 bounds_check); INLINE char *get_memory_ptr_for_runtime(u32 offset, u32 bounds_check);
void runtime_init(void);
void runtime_thd_init(void);
void stub_init(i32 offset);
/**
* ???
* @param offset ????
* @param bounds_check ???
* @return ???
**/
static inline void * static inline void *
get_memory_ptr_void(u32 offset, u32 bounds_check) get_memory_ptr_void(u32 offset, u32 bounds_check)
{ {
return (void *)get_memory_ptr_for_runtime(offset, bounds_check); return (void *)get_memory_ptr_for_runtime(offset, bounds_check);
} }
/**
* ???
* @param offset ????
* @return ???
**/
static inline char * static inline char *
get_memory_string(u32 offset) get_memory_string(u32 offset)
{ {
@ -40,31 +55,29 @@ get_memory_string(u32 offset)
} }
} }
INLINE char *get_function_from_table(u32 idx, u32 type_id); /**
* Get uvio
// libc/* might need to do some setup for the libc setup **/
void stub_init(i32 offset);
void runtime_init(void);
void runtime_thd_init(void);
extern __thread uv_loop_t uvio;
static inline uv_loop_t * static inline uv_loop_t *
runtime_uvio(void) runtime_uvio(void)
{ {
return &uvio; return &uvio;
} }
/**
* Get CPU time in cycles using the Intel instruction rdtsc
* @return CPU time in cycles
**/
static unsigned long long int static unsigned long long int
rdtsc(void) rdtsc(void)
{ {
unsigned long long int ret = 0; unsigned long long int cpu_time_in_cycles = 0;
unsigned int cycles_lo; unsigned int cycles_lo;
unsigned int cycles_hi; unsigned int cycles_hi;
__asm__ volatile("RDTSC" : "=a"(cycles_lo), "=d"(cycles_hi)); __asm__ volatile("RDTSC" : "=a"(cycles_lo), "=d"(cycles_hi));
ret = (unsigned long long int)cycles_hi << 32 | cycles_lo; cpu_time_in_cycles = (unsigned long long int)cycles_hi << 32 | cycles_lo;
return ret; return cpu_time_in_cycles;
} }
#endif /* SFRT_RUNTIME_H */ #endif /* SFRT_RUNTIME_H */

@ -12,7 +12,7 @@ http_parser_settings settings;
static inline int static inline int
http_on_msg_begin(http_parser *parser) http_on_msg_begin(http_parser *parser)
{ {
struct sandbox * sandbox = parser->data; struct sandbox * sandbox = parser->data;
struct http_request *http_request = &sandbox->http_request; struct http_request *http_request = &sandbox->http_request;
http_request->message_begin = 1; http_request->message_begin = 1;
@ -27,7 +27,7 @@ http_on_msg_begin(http_parser *parser)
static inline int static inline int
http_on_msg_end(http_parser *parser) http_on_msg_end(http_parser *parser)
{ {
struct sandbox * sandbox = parser->data; struct sandbox * sandbox = parser->data;
struct http_request *http_request = &sandbox->http_request; struct http_request *http_request = &sandbox->http_request;
http_request->message_end = 1; http_request->message_end = 1;
@ -41,7 +41,7 @@ http_on_msg_end(http_parser *parser)
static inline int static inline int
http_on_header_end(http_parser *parser) http_on_header_end(http_parser *parser)
{ {
struct sandbox * sandbox = parser->data; struct sandbox * sandbox = parser->data;
struct http_request *http_request = &sandbox->http_request; struct http_request *http_request = &sandbox->http_request;
http_request->header_end = 1; http_request->header_end = 1;
@ -58,7 +58,7 @@ http_on_header_end(http_parser *parser)
static inline int static inline int
http_on_url(http_parser *parser, const char *at, size_t length) http_on_url(http_parser *parser, const char *at, size_t length)
{ {
struct sandbox * sandbox = parser->data; struct sandbox *sandbox = parser->data;
assert(strncmp(sandbox->module->name, (at + 1), length - 1) == 0); assert(strncmp(sandbox->module->name, (at + 1), length - 1) == 0);
return 0; return 0;
@ -74,15 +74,16 @@ http_on_url(http_parser *parser, const char *at, size_t length)
static inline int static inline int
http_on_header_field(http_parser *parser, const char *at, size_t length) http_on_header_field(http_parser *parser, const char *at, size_t length)
{ {
struct sandbox * sandbox = parser->data; struct sandbox * sandbox = parser->data;
struct http_request *http_request = &sandbox->http_request; struct http_request *http_request = &sandbox->http_request;
if (http_request->last_was_value) http_request->nheaders++; if (http_request->last_was_value) http_request->header_count++;
assert(http_request->nheaders <= HTTP_HEADERS_MAX); assert(http_request->header_count <= HTTP_HEADERS_MAX);
assert(length < HTTP_HEADER_MAXSZ); assert(length < HTTP_HEADER_MAXSZ);
http_request->last_was_value = 0; http_request->last_was_value = 0;
http_request->headers[http_request->nheaders - 1].key = (char *)at; // it is from the sandbox's req_resp_data, should persist. http_request->headers[http_request->header_count - 1].key = (char *)
at; // it is from the sandbox's req_resp_data, should persist.
return 0; return 0;
} }
@ -97,14 +98,15 @@ http_on_header_field(http_parser *parser, const char *at, size_t length)
static inline int static inline int
http_on_header_value(http_parser *parser, const char *at, size_t length) http_on_header_value(http_parser *parser, const char *at, size_t length)
{ {
struct sandbox * sandbox = parser->data; struct sandbox * sandbox = parser->data;
struct http_request *http_request = &sandbox->http_request; struct http_request *http_request = &sandbox->http_request;
http_request->last_was_value = 1; http_request->last_was_value = 1;
assert(http_request->nheaders <= HTTP_HEADERS_MAX); assert(http_request->header_count <= HTTP_HEADERS_MAX);
assert(length < HTTP_HEADERVAL_MAXSZ); assert(length < HTTP_HEADERVAL_MAXSZ);
http_request->headers[http_request->nheaders - 1].val = (char *)at; // it is from the sandbox's req_resp_data, should persist. http_request->headers[http_request->header_count - 1].val = (char *)
at; // it is from the sandbox's req_resp_data, should persist.
return 0; return 0;
} }
@ -119,16 +121,16 @@ http_on_header_value(http_parser *parser, const char *at, size_t length)
static inline int static inline int
http_on_body(http_parser *parser, const char *at, size_t length) http_on_body(http_parser *parser, const char *at, size_t length)
{ {
struct sandbox * sandbox = parser->data; struct sandbox * sandbox = parser->data;
struct http_request *http_request = &sandbox->http_request; struct http_request *http_request = &sandbox->http_request;
assert(http_request->bodylen + length <= sandbox->module->max_request_size); assert(http_request->body_length + length <= sandbox->module->max_request_size);
if (!http_request->body) if (!http_request->body)
http_request->body = (char *)at; http_request->body = (char *)at;
else else
assert(http_request->body + http_request->bodylen == at); assert(http_request->body + http_request->body_length == at);
http_request->bodylen += length; http_request->body_length += length;
return 0; return 0;
} }
@ -136,93 +138,121 @@ http_on_body(http_parser *parser, const char *at, size_t length)
/** /**
* Gets the HTTP Request body from a Sandbox * Gets the HTTP Request body from a Sandbox
* @param sandbox the sandbox we want the body from * @param sandbox the sandbox we want the body from
* @param b pointer that we'll assign to the http_request body * @param body pointer that we'll assign to the http_request body
* @returns the length of the http_request's body * @returns the length of the http_request's body
**/ **/
int int
http_request_body_get_sb(struct sandbox *sandbox, char **b) http_request_body_get_sb(struct sandbox *sandbox, char **body)
{ {
struct http_request *http_request = &sandbox->http_request; struct http_request *http_request = &sandbox->http_request;
*b = http_request->body; *body = http_request->body;
return http_request->bodylen; return http_request->body_length;
} }
/**
* Set an HTTP Response Header on a Sandbox
* @param sandbox the sandbox we want to set the request header on
* @param header string of the header that we want to set
* @param length the length of the header string
* @returns 0 (abends program in case of error)
**/
int int
http_response_header_set_sb(struct sandbox *sandbox, char *key, int len) http_response_header_set_sb(struct sandbox *sandbox, char *header, int length)
{ {
// by now, req_resp_data should only be containing response! // by now, req_resp_data should only be containing response!
struct http_response *http_response = &sandbox->http_response; struct http_response *http_response = &sandbox->http_response;
assert(http_response->nheaders < HTTP_HEADERS_MAX); assert(http_response->header_count < HTTP_HEADERS_MAX);
http_response->nheaders++; http_response->header_count++;
http_response->headers[http_response->nheaders - 1].hdr = key; http_response->headers[http_response->header_count - 1].header = header;
http_response->headers[http_response->nheaders - 1].len = len; http_response->headers[http_response->header_count - 1].length = length;
return 0; return 0;
} }
/**
* Set an HTTP Response Body on a Sandbox
* @param sandbox the sandbox we want to set the request header on
* @param body string of the body that we want to set
* @param length the length of the header string
* @returns 0 (abends program in case of error)
**/
int int
http_response_body_set_sb(struct sandbox *sandbox, char *body, int len) http_response_body_set_sb(struct sandbox *sandbox, char *body, int length)
{ {
struct http_response *http_response = &sandbox->http_response; struct http_response *http_response = &sandbox->http_response;
assert(len <= sandbox->module->max_response_size); assert(length <= sandbox->module->max_response_size);
http_response->body = body; http_response->body = body;
http_response->bodylen = len; http_response->body_length = length;
return 0; return 0;
} }
/**
* Set an HTTP Response Status on a Sandbox
* @param sandbox the sandbox we want to set the request status on
* @param status string of the status we want to set
* @param length the length of the status
* @returns 0 (abends program in case of error)
**/
int int
http_response_status_set_sb(struct sandbox *sandbox, char *status, int len) http_response_status_set_sb(struct sandbox *sandbox, char *status, int length)
{ {
struct http_response *http_response = &sandbox->http_response; struct http_response *http_response = &sandbox->http_response;
http_response->status = status; http_response->status = status;
http_response->stlen = len; http_response->status_length = length;
return 0; return 0;
} }
/**
* ???
* @param sandbox the sandbox we want to set the request status on
* @returns ???
**/
int int
http_response_vector_sb(struct sandbox *sandbox) http_response_vector_sb(struct sandbox *sandbox)
{ {
int nb = 0; int nb = 0;
struct http_response *http_response = &sandbox->http_response; struct http_response *http_response = &sandbox->http_response;
#ifdef USE_HTTP_UVIO #ifdef USE_HTTP_UVIO
http_response->bufs[nb] = uv_buf_init(http_response->status, http_response->stlen); http_response->bufs[nb] = uv_buf_init(http_response->status, http_response->status_length);
nb++; nb++;
for (int i = 0; i < http_response->nheaders; i++) { for (int i = 0; i < http_response->header_count; i++) {
http_response->bufs[nb] = uv_buf_init(http_response->headers[i].hdr, http_response->headers[i].len); http_response->bufs[nb] = uv_buf_init(http_response->headers[i].header,
http_response->headers[i].length);
nb++; nb++;
} }
if (http_response->body) { if (http_response->body) {
http_response->bufs[nb] = uv_buf_init(http_response->body, http_response->bodylen); http_response->bufs[nb] = uv_buf_init(http_response->body, http_response->body_length);
nb++; nb++;
http_response->bufs[nb] = uv_buf_init(http_response->status + http_response->stlen - 2, 2); // for crlf http_response->bufs[nb] = uv_buf_init(http_response->status + http_response->status_length - 2,
2); // for crlf
nb++; nb++;
} }
#else #else
http_response->bufs[nb].iov_base = http_response->status; http_response->bufs[nb].iov_base = http_response->status;
http_response->bufs[nb].iov_len = http_response->stlen; http_response->bufs[nb].iov_len = http_response->status_length;
nb++; nb++;
for (int i = 0; i < http_response->nheaders; i++) { for (int i = 0; i < http_response->header_count; i++) {
http_response->bufs[nb].iov_base = http_response->headers[i].hdr; http_response->bufs[nb].iov_base = http_response->headers[i].header;
http_response->bufs[nb].iov_len = http_response->headers[i].len; http_response->bufs[nb].iov_len = http_response->headers[i].length;
nb++; nb++;
} }
if (http_response->body) { if (http_response->body) {
http_response->bufs[nb].iov_base = http_response->body; http_response->bufs[nb].iov_base = http_response->body;
http_response->bufs[nb].iov_len = http_response->bodylen; http_response->bufs[nb].iov_len = http_response->body_length;
nb++; nb++;
http_response->bufs[nb].iov_base = http_response->status + http_response->stlen - 2; http_response->bufs[nb].iov_base = http_response->status + http_response->status_length - 2;
http_response->bufs[nb].iov_len = 2; http_response->bufs[nb].iov_len = 2;
nb++; nb++;
} }
@ -231,10 +261,18 @@ http_response_vector_sb(struct sandbox *sandbox)
return nb; return nb;
} }
/**
* ???
* @param sandbox the sandbox we want to set the request status on
* @param length
* @returns 0
*
* Global State: settings
**/
int int
http_request_parse_sb(struct sandbox *sandbox, size_t l) http_request_parse_sb(struct sandbox *sandbox, size_t length)
{ {
http_parser_execute(&sandbox->http_parser, &settings, sandbox->req_resp_data + sandbox->rr_data_len, l); http_parser_execute(&sandbox->http_parser, &settings, sandbox->req_resp_data + sandbox->rr_data_len, length);
return 0; return 0;
} }

@ -107,11 +107,11 @@ wasm_read(i32 filedes, i32 buf_offset, i32 nbyte)
char * buffer = get_memory_ptr_void(buf_offset, nbyte); char * buffer = get_memory_ptr_void(buf_offset, nbyte);
struct sandbox * s = sandbox_current(); struct sandbox * s = sandbox_current();
struct http_request *r = &s->http_request; struct http_request *r = &s->http_request;
if (r->bodylen <= 0) return 0; if (r->body_length <= 0) return 0;
int l = nbyte > r->bodylen ? r->bodylen : nbyte; int l = nbyte > r->body_length ? r->body_length : nbyte;
memcpy(buffer, r->body + r->bodyrlen, l); memcpy(buffer, r->body + r->bodyrlen, l);
r->bodyrlen += l; r->bodyrlen += l;
r->bodylen -= l; r->body_length -= l;
return l; return l;
} }
int f = io_handle_fd(filedes); int f = io_handle_fd(filedes);
@ -500,15 +500,15 @@ wasm_readv(i32 fd, i32 iov_offset, i32 iovcnt)
struct wasm_iovec * iov = get_memory_ptr_void(iov_offset, iovcnt * sizeof(struct wasm_iovec)); struct wasm_iovec * iov = get_memory_ptr_void(iov_offset, iovcnt * sizeof(struct wasm_iovec));
struct sandbox * s = sandbox_current(); struct sandbox * s = sandbox_current();
struct http_request *r = &s->http_request; struct http_request *r = &s->http_request;
if (r->bodylen <= 0) return 0; if (r->body_length <= 0) return 0;
for (int i = 0; i < iovcnt; i++) { for (int i = 0; i < iovcnt; i++) {
int l = iov[i].len > r->bodylen ? r->bodylen : iov[i].len; int l = iov[i].len > r->body_length ? r->body_length : iov[i].len;
if (l <= 0) break; if (l <= 0) break;
char *b = get_memory_ptr_void(iov[i].base_offset, iov[i].len); char *b = get_memory_ptr_void(iov[i].base_offset, iov[i].len);
// http request body! // http request body!
memcpy(b, r->body + r->bodyrlen + len, l); memcpy(b, r->body + r->bodyrlen + len, l);
len += l; len += l;
r->bodylen -= l; r->body_length -= l;
} }
r->bodyrlen += len; r->bodyrlen += len;

@ -168,14 +168,14 @@ sandbox_client_response_set(void)
int sndsz = 0; int sndsz = 0;
struct sandbox *curr = sandbox_current(); struct sandbox *curr = sandbox_current();
int rsp_hdr_len = strlen(HTTP_RESP_200OK) + strlen(HTTP_RESP_CONTTYPE) + strlen(HTTP_RESP_CONTLEN); int rsp_hdr_len = strlen(HTTP_RESP_200OK) + strlen(HTTP_RESP_CONTTYPE) + strlen(HTTP_RESP_CONTLEN);
int bodylen = curr->rr_data_len - rsp_hdr_len; int body_length = curr->rr_data_len - rsp_hdr_len;
memset(curr->req_resp_data, 0, memset(curr->req_resp_data, 0,
strlen(HTTP_RESP_200OK) + strlen(HTTP_RESP_CONTTYPE) + strlen(HTTP_RESP_CONTLEN)); strlen(HTTP_RESP_200OK) + strlen(HTTP_RESP_CONTTYPE) + strlen(HTTP_RESP_CONTLEN));
strncpy(curr->req_resp_data, HTTP_RESP_200OK, strlen(HTTP_RESP_200OK)); strncpy(curr->req_resp_data, HTTP_RESP_200OK, strlen(HTTP_RESP_200OK));
sndsz += strlen(HTTP_RESP_200OK); sndsz += strlen(HTTP_RESP_200OK);
if (bodylen == 0) goto done; if (body_length == 0) goto done;
strncpy(curr->req_resp_data + sndsz, HTTP_RESP_CONTTYPE, strlen(HTTP_RESP_CONTTYPE)); strncpy(curr->req_resp_data + sndsz, HTTP_RESP_CONTTYPE, strlen(HTTP_RESP_CONTTYPE));
if (strlen(curr->module->response_content_type) <= 0) { if (strlen(curr->module->response_content_type) <= 0) {
strncpy(curr->req_resp_data + sndsz + strlen("Content-type: "), HTTP_RESP_CONTTYPE_PLAIN, strncpy(curr->req_resp_data + sndsz + strlen("Content-type: "), HTTP_RESP_CONTTYPE_PLAIN,
@ -186,11 +186,11 @@ sandbox_client_response_set(void)
} }
sndsz += strlen(HTTP_RESP_CONTTYPE); sndsz += strlen(HTTP_RESP_CONTTYPE);
char len[10] = { 0 }; char len[10] = { 0 };
sprintf(len, "%d", bodylen); sprintf(len, "%d", body_length);
strncpy(curr->req_resp_data + sndsz, HTTP_RESP_CONTLEN, strlen(HTTP_RESP_CONTLEN)); strncpy(curr->req_resp_data + sndsz, HTTP_RESP_CONTLEN, strlen(HTTP_RESP_CONTLEN));
strncpy(curr->req_resp_data + sndsz + strlen("Content-length: "), len, strlen(len)); strncpy(curr->req_resp_data + sndsz + strlen("Content-length: "), len, strlen(len));
sndsz += strlen(HTTP_RESP_CONTLEN); sndsz += strlen(HTTP_RESP_CONTLEN);
sndsz += bodylen; sndsz += body_length;
done: done:
assert(sndsz == curr->rr_data_len); assert(sndsz == curr->rr_data_len);

Loading…
Cancel
Save