mirror of
https://github.com/fluencelabs/fluid
synced 2025-04-24 14:22:18 +00:00
Merge pull request #4 from fluencelabs/backend_c
add backend example on C
This commit is contained in:
commit
619c944024
14
backend-c/Dockerfile
Normal file
14
backend-c/Dockerfile
Normal file
@ -0,0 +1,14 @@
|
||||
FROM ubuntu:19.04
|
||||
|
||||
RUN apt-get update \
|
||||
&& apt-get install -y ca-certificates \
|
||||
curl \
|
||||
git \
|
||||
make \
|
||||
libtinfo5
|
||||
|
||||
RUN curl -L https://github.com/CraneStation/wasi-sdk/releases/download/wasi-sdk-6/wasi-sdk-6.0-linux.tar.gz | tar xz --strip-components=1 -C /
|
||||
|
||||
VOLUME /code
|
||||
WORKDIR /code
|
||||
CMD make
|
201
backend-c/LICENSE
Normal file
201
backend-c/LICENSE
Normal file
@ -0,0 +1,201 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
23
backend-c/Makefile
Normal file
23
backend-c/Makefile
Normal file
@ -0,0 +1,23 @@
|
||||
TARGET = fluid
|
||||
CC = /opt/wasi-sdk/bin/clang
|
||||
SYSROOT = /opt/wasi-sdk/share/wasi-sysroot
|
||||
TARGET_TRIPLE = wasm32-unknown-wasi
|
||||
CFLAGS = -nostartfiles -fvisibility=hidden
|
||||
LDFLAGS = -Wl,--no-entry,--demangle,--allow-undefined
|
||||
EXPORT_FUNCS = --export=allocate,--export=deallocate,--export=invoke
|
||||
SDK = sdk/allocator.c sdk/logger.c
|
||||
SRC = src/main.c src/model.c
|
||||
LIBS = libs/tiny-json/tiny-json.c
|
||||
|
||||
.PHONY: default all clean
|
||||
|
||||
default: $(TARGET)
|
||||
all: default
|
||||
|
||||
$(TARGET): $(SRC) $(SDK) $(LIBS)
|
||||
$(CC) --sysroot=$(SYSROOT) --target=$(TARGET_TRIPLE) $(CFLAGS) $(LDFLAGS) -Wl,$(EXPORT_FUNCS) $^ -o $@.wasm
|
||||
|
||||
.PRECIOUS: $(TARGET)
|
||||
|
||||
clean:
|
||||
-rm -f $(TARGET).wasm
|
16
backend-c/README.md
Normal file
16
backend-c/README.md
Normal file
@ -0,0 +1,16 @@
|
||||
# Fluid
|
||||
|
||||
The twitter-like application for Fluence.
|
||||
|
||||
# How to build
|
||||
|
||||
This app could be built either with docker
|
||||
|
||||
```bash
|
||||
docker-compose up
|
||||
```
|
||||
|
||||
or by Makefile with [wasi-sdk](https://github.com/CraneStation/wasi-sdk) installed
|
||||
```bash
|
||||
make
|
||||
```
|
7
backend-c/docker-compose.yml
Normal file
7
backend-c/docker-compose.yml
Normal file
@ -0,0 +1,7 @@
|
||||
version: '3'
|
||||
services:
|
||||
hello_world:
|
||||
build:
|
||||
context: .
|
||||
volumes:
|
||||
- .:/code
|
5
backend-c/libs/tiny-json/.travis.yml
Normal file
5
backend-c/libs/tiny-json/.travis.yml
Normal file
@ -0,0 +1,5 @@
|
||||
language: c
|
||||
sudo: false
|
||||
script:
|
||||
- cd test
|
||||
- make test
|
463
backend-c/libs/tiny-json/tiny-json.c
Normal file
463
backend-c/libs/tiny-json/tiny-json.c
Normal file
@ -0,0 +1,463 @@
|
||||
|
||||
/*
|
||||
|
||||
<https://github.com/rafagafe/tiny-json>
|
||||
|
||||
Licensed under the MIT License <http://opensource.org/licenses/MIT>.
|
||||
SPDX-License-Identifier: MIT
|
||||
Copyright (c) 2016-2018 Rafa Garcia <rafagarcia77@gmail.com>.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include "tiny-json.h"
|
||||
|
||||
/** Structure to handle a heap of JSON properties. */
|
||||
typedef struct jsonStaticPool_s {
|
||||
json_t* const mem; /**< Pointer to array of json properties. */
|
||||
unsigned int const qty; /**< Length of the array of json properties. */
|
||||
unsigned int nextFree; /**< The index of the next free json property. */
|
||||
jsonPool_t pool;
|
||||
} jsonStaticPool_t;
|
||||
|
||||
/* Search a property by its name in a JSON object. */
|
||||
json_t const* json_getProperty( json_t const* obj, char const* property ) {
|
||||
json_t const* sibling;
|
||||
for( sibling = obj->u.c.child; sibling; sibling = sibling->sibling )
|
||||
if ( sibling->name && !strcmp( sibling->name, property ) )
|
||||
return sibling;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Search a property by its name in a JSON object and return its value. */
|
||||
char const* json_getPropertyValue( json_t const* obj, char const* property ) {
|
||||
json_t const* field = json_getProperty( obj, property );
|
||||
if ( !field ) return 0;
|
||||
jsonType_t type = json_getType( field );
|
||||
if ( JSON_ARRAY >= type ) return 0;
|
||||
return json_getValue( field );
|
||||
}
|
||||
|
||||
/* Internal prototypes: */
|
||||
static char* goBlank( char* str );
|
||||
static char* goNum( char* str );
|
||||
static json_t* poolInit( jsonPool_t* pool );
|
||||
static json_t* poolAlloc( jsonPool_t* pool );
|
||||
static char* objValue( char* ptr, json_t* obj, jsonPool_t* pool );
|
||||
static char* setToNull( char* ch );
|
||||
static bool isEndOfPrimitive( char ch );
|
||||
|
||||
/* Parse a string to get a json. */
|
||||
json_t const* json_createWithPool( char *str, jsonPool_t *pool ) {
|
||||
char* ptr = goBlank( str );
|
||||
if ( !ptr || *ptr != '{' ) return 0;
|
||||
json_t* obj = pool->init( pool );
|
||||
obj->name = 0;
|
||||
obj->sibling = 0;
|
||||
obj->u.c.child = 0;
|
||||
ptr = objValue( ptr, obj, pool );
|
||||
if ( !ptr ) return 0;
|
||||
return obj;
|
||||
}
|
||||
|
||||
/* Parse a string to get a json. */
|
||||
json_t const* json_create( char* str, json_t mem[], unsigned int qty ) {
|
||||
jsonStaticPool_t spool = {
|
||||
.mem = mem,
|
||||
.qty = qty,
|
||||
.pool = {
|
||||
.init = poolInit,
|
||||
.alloc = poolAlloc
|
||||
}
|
||||
};
|
||||
return json_createWithPool( str, &spool.pool );
|
||||
}
|
||||
|
||||
/** Get a special character with its escape character. Examples:
|
||||
* 'b' -> '\b', 'n' -> '\n', 't' -> '\t'
|
||||
* @param ch The escape character.
|
||||
* @return The character code. */
|
||||
static char getEscape( char ch ) {
|
||||
static struct { char ch; char code; } const pair[] = {
|
||||
{ '\"', '\"' }, { '\\', '\\' },
|
||||
{ '/', '/' }, { 'b', '\b' },
|
||||
{ 'f', '\f' }, { 'n', '\n' },
|
||||
{ 'r', '\r' }, { 't', '\t' },
|
||||
};
|
||||
unsigned int i;
|
||||
for( i = 0; i < sizeof pair / sizeof *pair; ++i )
|
||||
if ( pair[i].ch == ch )
|
||||
return pair[i].code;
|
||||
return '\0';
|
||||
}
|
||||
|
||||
/** Parse 4 characters.
|
||||
* @Param str Pointer to first digit.
|
||||
* @retval '?' If the four characters are hexadecimal digits.
|
||||
* @retcal '\0' In other cases. */
|
||||
static unsigned char getCharFromUnicode( unsigned char const* str ) {
|
||||
unsigned int i;
|
||||
for( i = 0; i < 4; ++i )
|
||||
if ( !isxdigit( str[i] ) )
|
||||
return '\0';
|
||||
return '?';
|
||||
}
|
||||
|
||||
/** Parse a string and replace the scape characters by their meaning characters.
|
||||
* This parser stops when finds the character '\"'. Then replaces '\"' by '\0'.
|
||||
* @param str Pointer to first character.
|
||||
* @retval Pointer to first non white space after the string. If success.
|
||||
* @retval Null pointer if any error occur. */
|
||||
static char* parseString( char* str ) {
|
||||
unsigned char* head = (unsigned char*)str;
|
||||
unsigned char* tail = (unsigned char*)str;
|
||||
for( ; *head >= ' '; ++head, ++tail ) {
|
||||
if ( *head == '\"' ) {
|
||||
*tail = '\0';
|
||||
return (char*)++head;
|
||||
}
|
||||
if ( *head == '\\' ) {
|
||||
if ( *++head == 'u' ) {
|
||||
char const ch = getCharFromUnicode( ++head );
|
||||
if ( ch == '\0' ) return 0;
|
||||
*tail = ch;
|
||||
head += 3;
|
||||
}
|
||||
else {
|
||||
char const esc = getEscape( *head );
|
||||
if ( esc == '\0' ) return 0;
|
||||
*tail = esc;
|
||||
}
|
||||
}
|
||||
else *tail = *head;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** Parse a string to get the name of a property.
|
||||
* @param str Pointer to first character.
|
||||
* @param property The property to assign the name.
|
||||
* @retval Pointer to first of property value. If success.
|
||||
* @retval Null pointer if any error occur. */
|
||||
static char* propertyName( char* ptr, json_t* property ) {
|
||||
property->name = ++ptr;
|
||||
ptr = parseString( ptr );
|
||||
if ( !ptr ) return 0;
|
||||
ptr = goBlank( ptr );
|
||||
if ( !ptr ) return 0;
|
||||
if ( *ptr++ != ':' ) return 0;
|
||||
return goBlank( ptr );
|
||||
}
|
||||
|
||||
/** Parse a string to get the value of a property when its type is JSON_TEXT.
|
||||
* @param str Pointer to first character ('\"').
|
||||
* @param property The property to assign the name.
|
||||
* @retval Pointer to first non white space after the string. If success.
|
||||
* @retval Null pointer if any error occur. */
|
||||
static char* textValue( char* ptr, json_t* property ) {
|
||||
++property->u.value;
|
||||
ptr = parseString( ++ptr );
|
||||
if ( !ptr ) return 0;
|
||||
property->type = JSON_TEXT;
|
||||
return ptr;
|
||||
}
|
||||
|
||||
/** Compare two strings until get the null character in the second one.
|
||||
* @param ptr sub string
|
||||
* @param str main string
|
||||
* @retval Pointer to next character.
|
||||
* @retval Null pointer if any error occur. */
|
||||
static char* checkStr( char* ptr, char const* str ) {
|
||||
while( *str )
|
||||
if ( *ptr++ != *str++ )
|
||||
return 0;
|
||||
return ptr;
|
||||
}
|
||||
|
||||
/** Parser a string to get a primitive value.
|
||||
* If the first character after the value is different of '}' or ']' is set to '\0'.
|
||||
* @param str Pointer to first character.
|
||||
* @param property Property handler to set the value and the type, (true, false or null).
|
||||
* @param value String with the primitive literal.
|
||||
* @param type The code of the type. ( JSON_BOOLEAN or JSON_NULL )
|
||||
* @retval Pointer to first non white space after the string. If success.
|
||||
* @retval Null pointer if any error occur. */
|
||||
static char* primitiveValue( char* ptr, json_t* property, char const* value, jsonType_t type ) {
|
||||
ptr = checkStr( ptr, value );
|
||||
if ( !ptr || !isEndOfPrimitive( *ptr ) ) return 0;
|
||||
ptr = setToNull( ptr );
|
||||
property->type = type;
|
||||
return ptr;
|
||||
}
|
||||
|
||||
/** Parser a string to get a true value.
|
||||
* If the first character after the value is different of '}' or ']' is set to '\0'.
|
||||
* @param str Pointer to first character.
|
||||
* @param property Property handler to set the value and the type, (true, false or null).
|
||||
* @retval Pointer to first non white space after the string. If success.
|
||||
* @retval Null pointer if any error occur. */
|
||||
static char* trueValue( char* ptr, json_t* property ) {
|
||||
return primitiveValue( ptr, property, "true", JSON_BOOLEAN );
|
||||
}
|
||||
|
||||
/** Parser a string to get a false value.
|
||||
* If the first character after the value is different of '}' or ']' is set to '\0'.
|
||||
* @param str Pointer to first character.
|
||||
* @param property Property handler to set the value and the type, (true, false or null).
|
||||
* @retval Pointer to first non white space after the string. If success.
|
||||
* @retval Null pointer if any error occur. */
|
||||
static char* falseValue( char* ptr, json_t* property ) {
|
||||
return primitiveValue( ptr, property, "false", JSON_BOOLEAN );
|
||||
}
|
||||
|
||||
/** Parser a string to get a null value.
|
||||
* If the first character after the value is different of '}' or ']' is set to '\0'.
|
||||
* @param str Pointer to first character.
|
||||
* @param property Property handler to set the value and the type, (true, false or null).
|
||||
* @retval Pointer to first non white space after the string. If success.
|
||||
* @retval Null pointer if any error occur. */
|
||||
static char* nullValue( char* ptr, json_t* property ) {
|
||||
return primitiveValue( ptr, property, "null", JSON_NULL );
|
||||
}
|
||||
|
||||
/** Analyze the exponential part of a real number.
|
||||
* @param str Pointer to first character.
|
||||
* @retval Pointer to first non numerical after the string. If success.
|
||||
* @retval Null pointer if any error occur. */
|
||||
static char* expValue( char* ptr ) {
|
||||
if ( *ptr == '-' || *ptr == '+' ) ++ptr;
|
||||
if ( !isdigit( *ptr ) ) return 0;
|
||||
ptr = goNum( ++ptr );
|
||||
return ptr;
|
||||
}
|
||||
|
||||
/** Analyze the decimal part of a real number.
|
||||
* @param str Pointer to first character.
|
||||
* @retval Pointer to first non numerical after the string. If success.
|
||||
* @retval Null pointer if any error occur. */
|
||||
static char* fraqValue( char* ptr ) {
|
||||
if ( !isdigit( *ptr ) ) return 0;
|
||||
ptr = goNum( ++ptr );
|
||||
if ( !ptr ) return 0;
|
||||
return ptr;
|
||||
}
|
||||
|
||||
/** Parser a string to get a numerical value.
|
||||
* If the first character after the value is different of '}' or ']' is set to '\0'.
|
||||
* @param str Pointer to first character.
|
||||
* @param property Property handler to set the value and the type: JSON_REAL or JSON_INTEGER.
|
||||
* @retval Pointer to first non white space after the string. If success.
|
||||
* @retval Null pointer if any error occur. */
|
||||
static char* numValue( char* ptr, json_t* property ) {
|
||||
if ( *ptr == '-' ) ++ptr;
|
||||
if ( !isdigit( *ptr ) ) return 0;
|
||||
if ( *ptr != '0' ) {
|
||||
ptr = goNum( ptr );
|
||||
if ( !ptr ) return 0;
|
||||
}
|
||||
else if ( isdigit( *++ptr ) ) return 0;
|
||||
property->type = JSON_INTEGER;
|
||||
if ( *ptr == '.' ) {
|
||||
ptr = fraqValue( ++ptr );
|
||||
if ( !ptr ) return 0;
|
||||
property->type = JSON_REAL;
|
||||
}
|
||||
if ( *ptr == 'e' || *ptr == 'E' ) {
|
||||
ptr = expValue( ++ptr );
|
||||
if ( !ptr ) return 0;
|
||||
property->type = JSON_REAL;
|
||||
}
|
||||
if ( !isEndOfPrimitive( *ptr ) ) return 0;
|
||||
if ( JSON_INTEGER == property->type ) {
|
||||
char const* value = property->u.value;
|
||||
bool const negative = *value == '-';
|
||||
static char const min[] = "-9223372036854775808";
|
||||
static char const max[] = "9223372036854775807";
|
||||
unsigned int const maxdigits = ( negative? sizeof min: sizeof max ) - 1;
|
||||
unsigned int const len = ptr - value;
|
||||
if ( len > maxdigits ) return 0;
|
||||
if ( len == maxdigits ) {
|
||||
char const tmp = *ptr;
|
||||
*ptr = '\0';
|
||||
char const* const threshold = negative ? min: max;
|
||||
if ( 0 > strcmp( threshold, value ) ) return 0;
|
||||
*ptr = tmp;
|
||||
}
|
||||
}
|
||||
ptr = setToNull( ptr );
|
||||
return ptr;
|
||||
}
|
||||
|
||||
/** Add a property to a JSON object or array.
|
||||
* @param obj The handler of the JSON object or array.
|
||||
* @param property The handler of the property to be added. */
|
||||
static void add( json_t* obj, json_t* property ) {
|
||||
property->sibling = 0;
|
||||
if ( !obj->u.c.child ){
|
||||
obj->u.c.child = property;
|
||||
obj->u.c.last_child = property;
|
||||
} else {
|
||||
obj->u.c.last_child->sibling = property;
|
||||
obj->u.c.last_child = property;
|
||||
}
|
||||
}
|
||||
|
||||
/** Parser a string to get a json object value.
|
||||
* @param str Pointer to first character.
|
||||
* @param pool The handler of a json pool for creating json instances.
|
||||
* @retval Pointer to first character after the value. If success.
|
||||
* @retval Null pointer if any error occur. */
|
||||
static char* objValue( char* ptr, json_t* obj, jsonPool_t* pool ) {
|
||||
obj->type = JSON_OBJ;
|
||||
obj->u.c.child = 0;
|
||||
obj->sibling = 0;
|
||||
ptr++;
|
||||
for(;;) {
|
||||
ptr = goBlank( ptr );
|
||||
if ( !ptr ) return 0;
|
||||
if ( *ptr == ',' ) {
|
||||
++ptr;
|
||||
continue;
|
||||
}
|
||||
char const endchar = ( obj->type == JSON_OBJ )? '}': ']';
|
||||
if ( *ptr == endchar ) {
|
||||
*ptr = '\0';
|
||||
json_t* parentObj = obj->sibling;
|
||||
if ( !parentObj ) return ++ptr;
|
||||
obj->sibling = 0;
|
||||
obj = parentObj;
|
||||
++ptr;
|
||||
continue;
|
||||
}
|
||||
json_t* property = pool->alloc( pool );
|
||||
if ( !property ) return 0;
|
||||
if( obj->type != JSON_ARRAY ) {
|
||||
if ( *ptr != '\"' ) return 0;
|
||||
ptr = propertyName( ptr, property );
|
||||
if ( !ptr ) return 0;
|
||||
}
|
||||
else property->name = 0;
|
||||
add( obj, property );
|
||||
property->u.value = ptr;
|
||||
switch( *ptr ) {
|
||||
case '{':
|
||||
property->type = JSON_OBJ;
|
||||
property->u.c.child = 0;
|
||||
property->sibling = obj;
|
||||
obj = property;
|
||||
++ptr;
|
||||
break;
|
||||
case '[':
|
||||
property->type = JSON_ARRAY;
|
||||
property->u.c.child = 0;
|
||||
property->sibling = obj;
|
||||
obj = property;
|
||||
++ptr;
|
||||
break;
|
||||
case '\"': ptr = textValue( ptr, property ); break;
|
||||
case 't': ptr = trueValue( ptr, property ); break;
|
||||
case 'f': ptr = falseValue( ptr, property ); break;
|
||||
case 'n': ptr = nullValue( ptr, property ); break;
|
||||
default: ptr = numValue( ptr, property ); break;
|
||||
}
|
||||
if ( !ptr ) return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/** Initialize a json pool.
|
||||
* @param pool The handler of the pool.
|
||||
* @return a instance of a json. */
|
||||
static json_t* poolInit( jsonPool_t* pool ) {
|
||||
jsonStaticPool_t *spool = json_containerOf( pool, jsonStaticPool_t, pool );
|
||||
spool->nextFree = 1;
|
||||
return spool->mem;
|
||||
}
|
||||
|
||||
/** Create an instance of a json from a pool.
|
||||
* @param pool The handler of the pool.
|
||||
* @retval The handler of the new instance if success.
|
||||
* @retval Null pointer if the pool was empty. */
|
||||
static json_t* poolAlloc( jsonPool_t* pool ) {
|
||||
jsonStaticPool_t *spool = json_containerOf( pool, jsonStaticPool_t, pool );
|
||||
if ( spool->nextFree >= spool->qty ) return 0;
|
||||
return spool->mem + spool->nextFree++;
|
||||
}
|
||||
|
||||
/** Checks whether an character belongs to set.
|
||||
* @param ch Character value to be checked.
|
||||
* @param set Set of characters. It is just a null-terminated string.
|
||||
* @return true or false there is membership or not. */
|
||||
static bool isOneOfThem( char ch, char const* set ) {
|
||||
while( *set != '\0' )
|
||||
if ( ch == *set++ )
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
/** Increases a pointer while it points to a character that belongs to a set.
|
||||
* @param str The initial pointer value.
|
||||
* @param set Set of characters. It is just a null-terminated string.
|
||||
* @return The final pointer value or null pointer if the null character was found. */
|
||||
static char* goWhile( char* str, char const* set ) {
|
||||
for(; *str != '\0'; ++str ) {
|
||||
if ( !isOneOfThem( *str, set ) )
|
||||
return str;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** Set of characters that defines a blank. */
|
||||
static char const* const blank = " \n\r\t\f";
|
||||
|
||||
/** Increases a pointer while it points to a white space character.
|
||||
* @param str The initial pointer value.
|
||||
* @return The final pointer value or null pointer if the null character was found. */
|
||||
static char* goBlank( char* str ) {
|
||||
return goWhile( str, blank );
|
||||
}
|
||||
|
||||
/** Increases a pointer while it points to a decimal digit character.
|
||||
* @param str The initial pointer value.
|
||||
* @return The final pointer value or null pointer if the null character was found. */
|
||||
static char* goNum( char* str ) {
|
||||
for( ; *str != '\0'; ++str ) {
|
||||
if ( !isdigit( *str ) )
|
||||
return str;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** Set of characters that defines the end of an array or a JSON object. */
|
||||
static char const* const endofblock = "}]";
|
||||
|
||||
/** Set a char to '\0' and increase its pointer if the char is different to '}' or ']'.
|
||||
* @param ch Pointer to character.
|
||||
* @return Final value pointer. */
|
||||
static char* setToNull( char* ch ) {
|
||||
if ( !isOneOfThem( *ch, endofblock ) ) *ch++ = '\0';
|
||||
return ch;
|
||||
}
|
||||
|
||||
/** Indicate if a character is the end of a primitive value. */
|
||||
static bool isEndOfPrimitive( char ch ) {
|
||||
return ch == ',' || isOneOfThem( ch, blank ) || isOneOfThem( ch, endofblock );
|
||||
}
|
176
backend-c/libs/tiny-json/tiny-json.h
Normal file
176
backend-c/libs/tiny-json/tiny-json.h
Normal file
@ -0,0 +1,176 @@
|
||||
|
||||
/*
|
||||
|
||||
<https://github.com/rafagafe/tiny-json>
|
||||
|
||||
Licensed under the MIT License <http://opensource.org/licenses/MIT>.
|
||||
SPDX-License-Identifier: MIT
|
||||
Copyright (c) 2016-2018 Rafa Garcia <rafagarcia77@gmail.com>.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
#ifndef _TINY_JSON_H_
|
||||
#define _TINY_JSON_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#define json_containerOf( ptr, type, member ) \
|
||||
((type*)( (char*)ptr - offsetof( type, member ) ))
|
||||
|
||||
/** @defgroup tinyJson Tiny JSON parser.
|
||||
* @{ */
|
||||
|
||||
/** Enumeration of codes of supported JSON properties types. */
|
||||
typedef enum {
|
||||
JSON_OBJ, JSON_ARRAY, JSON_TEXT, JSON_BOOLEAN,
|
||||
JSON_INTEGER, JSON_REAL, JSON_NULL
|
||||
} jsonType_t;
|
||||
|
||||
/** Structure to handle JSON properties. */
|
||||
typedef struct json_s {
|
||||
struct json_s* sibling;
|
||||
char const* name;
|
||||
union {
|
||||
char const* value;
|
||||
struct {
|
||||
struct json_s* child;
|
||||
struct json_s* last_child;
|
||||
} c;
|
||||
} u;
|
||||
jsonType_t type;
|
||||
} json_t;
|
||||
|
||||
/** Parse a string to get a json.
|
||||
* @param str String pointer with a JSON object. It will be modified.
|
||||
* @param mem Array of json properties to allocate.
|
||||
* @param qty Number of elements of mem.
|
||||
* @retval Null pointer if any was wrong in the parse process.
|
||||
* @retval If the parser process was successfully a valid handler of a json.
|
||||
* This property is always unnamed and its type is JSON_OBJ. */
|
||||
json_t const* json_create( char* str, json_t mem[], unsigned int qty );
|
||||
|
||||
/** Get the name of a json property.
|
||||
* @param json A valid handler of a json property.
|
||||
* @retval Pointer to null-terminated if property has name.
|
||||
* @retval Null pointer if the property is unnamed. */
|
||||
static inline char const* json_getName( json_t const* json ) {
|
||||
return json->name;
|
||||
}
|
||||
|
||||
/** Get the value of a json property.
|
||||
* The type of property cannot be JSON_OBJ or JSON_ARRAY.
|
||||
* @param json A valid handler of a json property.
|
||||
* @return Pointer to null-terminated string with the value. */
|
||||
static inline char const* json_getValue( json_t const* property ) {
|
||||
return property->u.value;
|
||||
}
|
||||
|
||||
/** Get the type of a json property.
|
||||
* @param json A valid handler of a json property.
|
||||
* @return The code of type.*/
|
||||
static inline jsonType_t json_getType( json_t const* json ) {
|
||||
return json->type;
|
||||
}
|
||||
|
||||
/** Get the next sibling of a JSON property that is within a JSON object or array.
|
||||
* @param json A valid handler of a json property.
|
||||
* @retval The handler of the next sibling if found.
|
||||
* @retval Null pointer if the json property is the last one. */
|
||||
static inline json_t const* json_getSibling( json_t const* json ) {
|
||||
return json->sibling;
|
||||
}
|
||||
|
||||
/** Search a property by its name in a JSON object.
|
||||
* @param obj A valid handler of a json object. Its type must be JSON_OBJ.
|
||||
* @param property The name of property to get.
|
||||
* @retval The handler of the json property if found.
|
||||
* @retval Null pointer if not found. */
|
||||
json_t const* json_getProperty( json_t const* obj, char const* property );
|
||||
|
||||
|
||||
/** Search a property by its name in a JSON object and return its value.
|
||||
* @param obj A valid handler of a json object. Its type must be JSON_OBJ.
|
||||
* @param property The name of property to get.
|
||||
* @retval If found a pointer to null-terminated string with the value.
|
||||
* @retval Null pointer if not found or it is an array or an object. */
|
||||
char const* json_getPropertyValue( json_t const* obj, char const* property );
|
||||
|
||||
/** Get the first property of a JSON object or array.
|
||||
* @param json A valid handler of a json property.
|
||||
* Its type must be JSON_OBJ or JSON_ARRAY.
|
||||
* @retval The handler of the first property if there is.
|
||||
* @retval Null pointer if the json object has not properties. */
|
||||
static inline json_t const* json_getChild( json_t const* json ) {
|
||||
return json->u.c.child;
|
||||
}
|
||||
|
||||
/** Get the value of a json boolean property.
|
||||
* @param property A valid handler of a json object. Its type must be JSON_BOOLEAN.
|
||||
* @return The value stdbool. */
|
||||
static inline bool json_getBoolean( json_t const* property ) {
|
||||
return *property->u.value == 't';
|
||||
}
|
||||
|
||||
/** Get the value of a json integer property.
|
||||
* @param property A valid handler of a json object. Its type must be JSON_INTEGER.
|
||||
* @return The value stdint. */
|
||||
static inline int64_t json_getInteger( json_t const* property ) {
|
||||
return atoll( property->u.value );
|
||||
}
|
||||
|
||||
/** Get the value of a json real property.
|
||||
* @param property A valid handler of a json object. Its type must be JSON_REAL.
|
||||
* @return The value. */
|
||||
static inline double json_getReal( json_t const* property ) {
|
||||
return atof( property->u.value );
|
||||
}
|
||||
|
||||
|
||||
|
||||
/** Structure to handle a heap of JSON properties. */
|
||||
typedef struct jsonPool_s jsonPool_t;
|
||||
struct jsonPool_s {
|
||||
json_t* (*init)( jsonPool_t* pool );
|
||||
json_t* (*alloc)( jsonPool_t* pool );
|
||||
};
|
||||
|
||||
/** Parse a string to get a json.
|
||||
* @param str String pointer with a JSON object. It will be modified.
|
||||
* @param pool Custom json pool pointer.
|
||||
* @retval Null pointer if any was wrong in the parse process.
|
||||
* @retval If the parser process was successfully a valid handler of a json.
|
||||
* This property is always unnamed and its type is JSON_OBJ. */
|
||||
json_t const* json_createWithPool( char* str, jsonPool_t* pool );
|
||||
|
||||
/** @ } */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _TINY_JSON_H_ */
|
73
backend-c/run.sh
Executable file
73
backend-c/run.sh
Executable file
@ -0,0 +1,73 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -e
|
||||
set -o pipefail
|
||||
|
||||
mkdir -p wasm
|
||||
|
||||
# Download SQLite
|
||||
SQLITE="sqlite3_0.2.0.wasm"
|
||||
if [ ! -f "wasm/$SQLITE" ]; then
|
||||
echo "Downloading $SQLITE..."
|
||||
wget -q https://github.com/fluencelabs/sqlite/releases/download/v0.2.0_w/$SQLITE -O ./wasm/$SQLITE
|
||||
fi
|
||||
|
||||
# Build fluid WASM module
|
||||
echo "Building to WASM..."
|
||||
docker-compose up --build
|
||||
cp fluid.wasm wasm/
|
||||
echo
|
||||
|
||||
# Run it all on 30000 port with default Fluence API
|
||||
echo "Running..."
|
||||
docker rm -f frun &>/dev/null || true
|
||||
echo 'docker run -d --name frun --rm -v "$(pwd)/wasm:/code" -p 30000:30000 fluencelabs/frun:latest'
|
||||
docker run -d --name frun --rm -v "$(pwd)/wasm:/code" -p 30000:30000 fluencelabs/frun:latest >/dev/null
|
||||
echo
|
||||
|
||||
# Wait for app to be initialized
|
||||
sleep 1 && (docker logs -f frun 2>&1 &) | grep -q initialized && sleep 1
|
||||
|
||||
|
||||
|
||||
############################################
|
||||
### *** --- === Sending post === --- *** ###
|
||||
############################################
|
||||
|
||||
# Assign json to a variable using heredoc technique
|
||||
JSON=$(cat <<JSON
|
||||
{"action":"Post","message":"I'm nice, you're nice, it's nice!","username":"random_joe"}
|
||||
JSON
|
||||
)
|
||||
|
||||
echo -e "Sending post: $JSON"
|
||||
# Send json as a request, and receive result
|
||||
RESPONSE=$(curl -s 'http://localhost:30000/apps/0/tx' --data $'sessionId/0\n'"$JSON" --compressed)
|
||||
RESPONSE=$(echo "$RESPONSE" | jq -r .result.data | base64 --decode 2>/dev/null || echo "$RESPONSE")
|
||||
# Parse json or print response as is
|
||||
echo "$RESPONSE" | jq . 2>/dev/null || echo "$RESPONSE"
|
||||
|
||||
|
||||
##############################################
|
||||
### *** --- === Fetching posts === --- *** ###
|
||||
##############################################
|
||||
|
||||
# Assign json to a variable using heredoc technique
|
||||
JSON=$(cat <<JSON
|
||||
{"action":"Fetch", "handle": "random_joe", "offset": 0, "count": 10}
|
||||
JSON
|
||||
)
|
||||
|
||||
echo -e "Fetching posts: $JSON"
|
||||
|
||||
# Send json as a request, and receive result
|
||||
RESPONSE=$(curl -s 'http://localhost:30000/apps/0/tx' --data $'sessionId/1\n'"$JSON" --compressed)
|
||||
RESPONSE=$(echo "$RESPONSE" | jq -r .result.data | base64 --decode 2>/dev/null || echo "$RESPONSE")
|
||||
|
||||
# Parse json or print response as is
|
||||
echo "$RESPONSE" | jq . 2>/dev/null || echo "$RESPONSE"
|
||||
|
||||
|
||||
# Remove frun container
|
||||
echo -e "Stopping..."
|
||||
docker rm -f frun >/dev/null
|
13
backend-c/sdk/allocator.c
Normal file
13
backend-c/sdk/allocator.c
Normal file
@ -0,0 +1,13 @@
|
||||
#include "allocator.h"
|
||||
#include <stdlib.h>
|
||||
|
||||
#define UNUSED(x) (void)(x)
|
||||
|
||||
void *allocate(size_t size) {
|
||||
return malloc(size);
|
||||
}
|
||||
|
||||
void deallocate(void *ptr, size_t size) {
|
||||
UNUSED(size);
|
||||
free(ptr);
|
||||
}
|
27
backend-c/sdk/allocator.h
Normal file
27
backend-c/sdk/allocator.h
Normal file
@ -0,0 +1,27 @@
|
||||
#ifndef FLUENCE_C_SDK_ALLOCATOR_H
|
||||
#define FLUENCE_C_SDK_ALLOCATOR_H
|
||||
|
||||
#include <stddef.h> // for size_t
|
||||
|
||||
/**
|
||||
* Allocates a memory region of a given size.
|
||||
*
|
||||
* Used by Wasm VM for byte array passing. Should be exported from module.
|
||||
*
|
||||
* @param size a size of allocated memory region.
|
||||
* @return a pointer to the allocated memory region.
|
||||
*/
|
||||
void *allocate(size_t size);
|
||||
|
||||
/**
|
||||
* Frees a memory region.
|
||||
*
|
||||
* Used by Wasm VM for freeing previous memory allocated by `allocate` function.
|
||||
* Should be exported from module.
|
||||
*
|
||||
* @param ptr a pointer to the previously allocated memory region.
|
||||
* @param size a size of the previously allocated memory region.
|
||||
*/
|
||||
void deallocate(void *ptr, size_t size);
|
||||
|
||||
#endif //FLUENCE_C_SDK_ALLOCATOR_H
|
15
backend-c/sdk/logger.c
Normal file
15
backend-c/sdk/logger.c
Normal file
@ -0,0 +1,15 @@
|
||||
#include "logger.h"
|
||||
|
||||
#define __LOGGER_IMPORT(name) \
|
||||
__attribute__((__import_module__("logger"), __import_name__(#name)))
|
||||
|
||||
void __write(char ch) __LOGGER_IMPORT(write);
|
||||
void __flush() __LOGGER_IMPORT(flush);
|
||||
|
||||
void wasm_log(const char *str, int len) {
|
||||
for(int byteId = 0; byteId < len; ++byteId) {
|
||||
__write(str[byteId]);
|
||||
}
|
||||
|
||||
__flush();
|
||||
}
|
12
backend-c/sdk/logger.h
Normal file
12
backend-c/sdk/logger.h
Normal file
@ -0,0 +1,12 @@
|
||||
#ifndef FLUENCE_C_SDK_LOGGER_H
|
||||
#define FLUENCE_C_SDK_LOGGER_H
|
||||
|
||||
/**
|
||||
* Writes provided string to Wasm VM logger.
|
||||
*
|
||||
* @param str a pointer to a message that should be logged.
|
||||
* @param len a size of a message that should be logged.
|
||||
*/
|
||||
void wasm_log(const char *str, int len);
|
||||
|
||||
#endif //FLUENCE_C_SDK_LOGGER_H
|
62
backend-c/sdk/side_module_api.h
Normal file
62
backend-c/sdk/side_module_api.h
Normal file
@ -0,0 +1,62 @@
|
||||
#ifndef C_TEMPLATE_SIDE_MODULE_API_H
|
||||
#define C_TEMPLATE_SIDE_MODULE_API_H
|
||||
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
/*
|
||||
* Concatenate preprocessor tokens A and B without expanding macro definitions
|
||||
* (however, if invoked from a macro, macro arguments are expanded).
|
||||
*/
|
||||
#define PPCAT_NX(A, B) A ## B
|
||||
|
||||
/*
|
||||
* Concatenate preprocessor tokens A and B after macro-expanding them.
|
||||
*/
|
||||
#define PPCAT(A, B) PPCAT_NX(A, B)
|
||||
|
||||
/*
|
||||
* Turn A into a string literal without expanding macro definitions
|
||||
* (however, if invoked from a macro, macro arguments are expanded).
|
||||
*/
|
||||
#define STRINGIZE_NX(A) #A
|
||||
|
||||
/*
|
||||
* Turn A into a string literal after macro-expanding it.
|
||||
*/
|
||||
#define STRINGIZE(A) STRINGIZE_NX(A)
|
||||
|
||||
|
||||
#define __MODULE_IMPORT(module_name) \
|
||||
void module_name ## _store(char *ptr, char byte) __attribute__((__import_module__(#module_name), __import_name__(STRINGIZE(PPCAT(module_name, _store))))); \
|
||||
unsigned char module_name ## _load(char *ptr) __attribute__((__import_module__(#module_name), __import_name__(STRINGIZE(PPCAT(module_name, _load))))); \
|
||||
char* module_name ## _allocate(unsigned int size) __attribute__((__import_module__(#module_name), __import_name__(STRINGIZE(PPCAT(module_name, _allocate))))); \
|
||||
void module_name ## _deallocate(char *ptr, unsigned int size) __attribute__((__import_module__(#module_name), __import_name__(STRINGIZE(PPCAT(module_name, _deallocate))))); \
|
||||
char* module_name ## _invoke(char *ptr, int size) __attribute__((__import_module__(#module_name), __import_name__(STRINGIZE(PPCAT(module_name, _invoke))))); \
|
||||
\
|
||||
char * module_name ## _call(const char *ptr, int length) { \
|
||||
char *request_ptr = module_name ## _allocate(length); \
|
||||
\
|
||||
for(int i = 0; i < length; ++i) { \
|
||||
module_name ## _store(request_ptr + i, ptr[i]); \
|
||||
} \
|
||||
char *result = module_name ## _invoke(request_ptr, length); \
|
||||
\
|
||||
unsigned int result_size = 0; \
|
||||
for (int i = 0; i < 4; ++i) { \
|
||||
result_size = result_size | (module_name ## _load(result + i) << 8*i); \
|
||||
} \
|
||||
\
|
||||
char *result_out = malloc(result_size + 1); \
|
||||
for(int i = 0; i < result_size; ++i) { \
|
||||
result_out[i] = module_name ## _load(result + 4 + i); \
|
||||
}\
|
||||
\
|
||||
result_out[result_size] = '\0'; \
|
||||
\
|
||||
module_name ## _deallocate(result, result_size); \
|
||||
\
|
||||
return result_out;\
|
||||
}
|
||||
|
||||
#endif //C_TEMPLATE_SIDE_MODULE_API_H
|
14
backend-c/sdk/syscalls_stubs.c
Normal file
14
backend-c/sdk/syscalls_stubs.c
Normal file
@ -0,0 +1,14 @@
|
||||
#include <stdio.h>
|
||||
|
||||
size_t __stdio_write(FILE *f, const unsigned char *buf, size_t len) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
int __stdio_close(FILE *f) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
off_t __stdio_seek(FILE *_f, off_t _offset, int _value) {
|
||||
return 1;
|
||||
}
|
||||
|
158
backend-c/src/main.c
Normal file
158
backend-c/src/main.c
Normal file
@ -0,0 +1,158 @@
|
||||
#include "model.h"
|
||||
#include "../sdk/allocator.h"
|
||||
#include "../sdk/logger.h"
|
||||
#include "../sdk/syscalls_stubs.c"
|
||||
#include "../libs/tiny-json/tiny-json.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
char *prepare_response(const char *response, int response_length) {
|
||||
const int RESPONSE_SIZE_BYTES = 4;
|
||||
char *result = (char *)allocate(response_length + RESPONSE_SIZE_BYTES);
|
||||
|
||||
for(int i = 0; i < RESPONSE_SIZE_BYTES; ++i) {
|
||||
result[i] = (response_length >> 8*i) & 0xFF;
|
||||
}
|
||||
|
||||
memcpy(result + RESPONSE_SIZE_BYTES, response, response_length);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
const char *add_post_request(const json_t *json);
|
||||
const char *fetch_posts_request(const json_t *json);
|
||||
|
||||
bool isInited = 0;
|
||||
|
||||
const char *invoke(char *str, int length) {
|
||||
// initialize SQLite by creating schema
|
||||
if(0 == isInited) {
|
||||
create_scheme();
|
||||
isInited = 1;
|
||||
}
|
||||
|
||||
json_t pool[10];
|
||||
const unsigned int pool_size = sizeof pool / sizeof *pool;
|
||||
|
||||
// try to parse json and extract action field
|
||||
const json_t *json = json_create(str, pool, pool_size);
|
||||
if(!json) {
|
||||
const char error[] = "Mailformed json given";
|
||||
return prepare_response(error, sizeof error);
|
||||
}
|
||||
|
||||
json_t const *action_json = json_getProperty(json, "action");
|
||||
if(0 == action_json) {
|
||||
const char error[] = "Given json doesn't contain action field";
|
||||
return prepare_response(error, sizeof error);
|
||||
}
|
||||
|
||||
if(JSON_TEXT != json_getType(action_json)) {
|
||||
const char error[] = "action field is mailformed";
|
||||
return prepare_response(error, sizeof error);
|
||||
}
|
||||
|
||||
const char *action = json_getValue(action_json);
|
||||
|
||||
// use action to determine the desired activity
|
||||
const char *result = "";
|
||||
if(0 == strcmp(action, "Post")) {
|
||||
result = add_post_request(json);
|
||||
} else if(0 == strcmp(action, "Fetch")) {
|
||||
result = fetch_posts_request(json);
|
||||
} else {
|
||||
// no suitable action given
|
||||
char *error = (char *)malloc(1024);
|
||||
const int error_size = snprintf(error, 1024, "%s given as the action field, but only `Post` and `Fetch` are supported", action);
|
||||
result = prepare_response(error, error_size);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
const char *add_post_request(const json_t *json) {
|
||||
// try to extract username and message properties
|
||||
const json_t *username_json = json_getProperty(json, "username");
|
||||
const json_t *message_json = json_getProperty(json, "message");
|
||||
if(0 == message_json || 0 == username_json) {
|
||||
const char error[] = "Given json doesn't contain message or username field";
|
||||
return prepare_response(error, sizeof error);
|
||||
}
|
||||
|
||||
if(JSON_TEXT != json_getType(message_json) || JSON_TEXT != json_getType(username_json)) {
|
||||
const char error[] = "message or action fields are mailformed";
|
||||
return prepare_response(error, sizeof error);
|
||||
}
|
||||
|
||||
const char *username = json_getValue(username_json);
|
||||
const char *message = json_getValue(message_json);
|
||||
|
||||
const char *add_post_result = add_post(username, strlen(username),
|
||||
message, strlen(message));
|
||||
if(0 == add_post_result) {
|
||||
const char error[] = "add_post failed";
|
||||
return prepare_response(error, sizeof error);
|
||||
}
|
||||
|
||||
// returns the updated post count to the client
|
||||
const char *count = get_posts_count();
|
||||
if(0 == count) {
|
||||
const char error[] = "get_posts_count failed";
|
||||
return prepare_response(error, sizeof error);
|
||||
}
|
||||
|
||||
char result[256];
|
||||
const int result_len = snprintf(result, 256, "{ count: \"%s\" }", count);
|
||||
|
||||
return prepare_response(result, result_len);
|
||||
}
|
||||
|
||||
const char *fetch_posts_request(const json_t *json) {
|
||||
// try to extract username, offset and count fields
|
||||
const json_t *username_json = json_getProperty(json, "username");
|
||||
const json_t *offset_json = json_getProperty(json, "offset");
|
||||
const json_t *count_json = json_getProperty(json, "count");
|
||||
|
||||
if((0 != username_json && JSON_TEXT != json_getType(username_json)) ||
|
||||
(0 != offset_json && JSON_INTEGER != json_getType(offset_json)) ||
|
||||
(0 != count_json && JSON_INTEGER != json_getType(count_json))
|
||||
) {
|
||||
const char error[] = "json fields are mailformed";
|
||||
return prepare_response(error, sizeof(error));
|
||||
}
|
||||
|
||||
int count = 100;
|
||||
if(0 != count_json) {
|
||||
count = json_getInteger(count_json);
|
||||
}
|
||||
|
||||
int offset = 0;
|
||||
if(0 != offset_json) {
|
||||
offset = json_getInteger(offset_json);
|
||||
}
|
||||
|
||||
char *result = "";
|
||||
if(0 == username_json) {
|
||||
// if no username specified, jsut return all posts
|
||||
result = get_all_posts(offset, count);
|
||||
if(0 == result) {
|
||||
const char error[] = "get_all_posts failed";
|
||||
return prepare_response(error, sizeof(error));
|
||||
}
|
||||
} else {
|
||||
const char *username = json_getValue(username_json);
|
||||
result = get_posts_by_username(username, strlen(username), offset, count);
|
||||
if(0 == result) {
|
||||
const char error[] = "get_posts_by_username failed";
|
||||
return prepare_response(error, sizeof(error));
|
||||
}
|
||||
}
|
||||
|
||||
const int result_len = strlen(result) + 100;
|
||||
char *result_out = malloc(result_len);
|
||||
|
||||
const int result_out_len = snprintf(result_out, result_len, "{ posts: \"%s\" }", result);
|
||||
|
||||
return prepare_response(result_out, result_out_len);
|
||||
}
|
68
backend-c/src/model.c
Normal file
68
backend-c/src/model.c
Normal file
@ -0,0 +1,68 @@
|
||||
#include "../sdk/side_module_api.h"
|
||||
#include "../sdk/logger.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
__MODULE_IMPORT(sqlite)
|
||||
|
||||
// TODO: introduce error codes and security checks
|
||||
|
||||
void create_scheme() {
|
||||
const char create_sql[] = "CREATE TABLE messages(message text, username text)";
|
||||
sqlite_call(create_sql, sizeof(create_sql));
|
||||
}
|
||||
|
||||
char *add_post(const char *username, int username_length, const char *message, int message_length) {
|
||||
// at now wasm-ld has 1024 bytes for stack permission by default - that why dynamic allocation here
|
||||
const int request_size = username_length + message_length + 50;
|
||||
char *add_sql = (char *)malloc(request_size);
|
||||
|
||||
const int add_sql_length = snprintf(add_sql, request_size, "INSERT INTO messages VALUES(\"%s\", \"%s\")", message, username);
|
||||
if(add_sql_length < 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return sqlite_call(add_sql, add_sql_length);
|
||||
}
|
||||
|
||||
char *get_all_posts(int offset, int count) {
|
||||
// at now wasm-ld has 1024 bytes for stack permission by default - that why dynamic allocation here
|
||||
char *get_sql = (char *)malloc(256);
|
||||
|
||||
const int get_sql_length = snprintf(get_sql, 256,
|
||||
"SELECT json_group_array("
|
||||
" json_object('message', message, 'username', username)"
|
||||
" ) AS json_result FROM ("
|
||||
" SELECT * FROM messages LIMIT %d OFFSET %d"
|
||||
" )", count, offset);
|
||||
if(get_sql_length < 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return sqlite_call(get_sql, get_sql_length);
|
||||
}
|
||||
|
||||
char *get_posts_by_username(const char *username, int username_length, int offset, int count) {
|
||||
// at now wasm-ld has 1024 bytes for stack permission by default - that why dynamic allocation here
|
||||
const int request_size = username_length + 300;
|
||||
char *get_sql = (char *)malloc(request_size);
|
||||
|
||||
const int add_sql_length = snprintf(get_sql, request_size,
|
||||
"SELECT json_group_array("
|
||||
" json_object('message', message, 'username', username)"
|
||||
" ) AS json_result FROM ("
|
||||
" SELECT * FROM messages where username = '%s' LIMIT %d OFFSET %d"
|
||||
" )",
|
||||
username, count, offset);
|
||||
if(add_sql_length < 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return sqlite_call(get_sql, add_sql_length);
|
||||
}
|
||||
|
||||
char *get_posts_count() {
|
||||
const char count_sql[] = "SELECT COUNT(*) from messages";
|
||||
return sqlite_call(count_sql, sizeof(count_sql));
|
||||
}
|
14
backend-c/src/model.h
Normal file
14
backend-c/src/model.h
Normal file
@ -0,0 +1,14 @@
|
||||
#ifndef FLUID_MODEL_H
|
||||
#define FLUID_MODEL_H
|
||||
|
||||
void create_scheme();
|
||||
|
||||
char *add_post(const char *username, int username_length, const char *message, int message_length);
|
||||
|
||||
char *get_all_posts(int offset, int count);
|
||||
|
||||
char *get_posts_by_username(const char *username, int username_length, int offset, int count);
|
||||
|
||||
char *get_posts_count();
|
||||
|
||||
#endif //FLUID_MODEL_H
|
Loading…
x
Reference in New Issue
Block a user