webassembly

parent e668f7cf
[submodule "webassembly/emsdk"]
path = webassembly/emsdk
url = https://github.com/emscripten-core/emsdk
### Отчет по улучшению приложения Budget Planner
#### Введение
В рамках данного задания было реализовано сохранение данных приложения в
localStorage браузера и небольшое изменение **makefile**.
#### Реализованные изменения
1. **Добавленные функции в C коде**:
```C
void saveToLocalStorage() {
// Get all expenses as JSON
char* json = (char*)malloc(MAX_STRING_LENGTH * expenseCount);
if (!json) return;
// Create array of expenses
sprintf(json, "[");
for (int i = 0; i < expenseCount; i++) {
if (i > 0) strcat(json, ",");
char* expense_json = getExpenseJSON(i);
strcat(json, expense_json);
freeMemory(expense_json);
}
strcat(json, "]");
// Call JavaScript to save to localStorage
EM_ASM({
localStorage.setItem('budgetPlanner_expenses', UTF8ToString($0));
}, json);
free(json);
}
void loadFromLocalStorage() {
// Call JavaScript to load data and parse it
EM_ASM({
var stored = localStorage.getItem('budgetPlanner_expenses');
if (stored) {
var expenses = JSON.parse(stored);
Module.ccall('jsClearAllExpenses', 'number', [], []); // Clear existing data
for (var i = 0; i < expenses.length; i++) {
var expense = expenses[i];
Module.ccall('jsAddExpense', 'number',
['string', 'string', 'number', 'string'],
[expense.date, expense.category, expense.amount, expense.description]
);
}
}
});
}
```
2. **Изменения makefile**:
```makefile
# Default target
all: build server
build:
$(CC) $(SOURCES) -s WASM=1 -s ASSERTIONS=1 -s MODULARIZE=1 -s EXPORT_NAME="'BudgetPlanner'" \
-s EXPORTED_FUNCTIONS='["_main","_showHelloMessage","_jsAddExpense","_jsDeleteExpense","_jsClearAllExpenses","_jsGetTotalExpenses","_jsGetExpenseCount","_jsGetCategoryCount","_getExpenseJSON","_getCategoryTotalJSON","_freeMemory","_malloc","_free","_saveToLocalStorage","_loadFromLocalStorage"]' \
-s EXPORTED_RUNTIME_METHODS='["ccall", "cwrap", "stringToUTF8", "UTF8ToString"]' \
$(LDFLAGS) -o index.js
server:
python3 -m http.server 8000
```
Теперь запуск сервера с приложением совершается по умолчанию и **build**
это отдельный таргет.
Помимо этого был изменен список **EXPORTED_FUNCTIONS**, чтобы он включал
добавленные функции.
#### Принцип работы
1. **Сохранение данных**:
- Создание JSON-строки со всеми расходами
- Использование Emscripten для вызова JavaScript функции сохранения
- Корректная работа с памятью (выделение и освобождение)
2. **Загрузка данных**:
- Получение данных из localStorage
- Парсинг JSON-строки
- Очистка существующих данных
- Постраниное добавление сохраненных расходов
#### Результат
Реализованное улучшение обеспечивает:
- Постоянное хранение данных между сессиями
- Автоматическую загрузку при старте
- Сохранение всех функциональных возможностей приложения
- Эффективное использование памяти
File moved
all: compile
compile:
emcc main.c -o index.js -s WASM=1 -O2 -s EXPORTED_RUNTIME_METHODS='["stringToUTF8","UTF8ToString"]' -s EXPORTED_FUNCTIONS='["_main","_jsAddExpense","_jsDeleteExpense","_jsClearAllExpenses","_jsGetTotalExpenses","_jsGetExpenseCount","_jsGetCategoryCount","_getExpenseJSON","_getCategoryTotalJSON","_freeMemory","_malloc","_free"]' --shell-file index.html -s ALLOW_MEMORY_GROWTH=1
serve:
python3 -m http.server 8000
......@@ -71,6 +71,11 @@ This command:
- Enables memory growth for dynamic memory allocation
- Optimizes the code with `-O2`
Ensure that the following files are in the same directory as `index.html`:
- `app.js`
- `index.js`
- The WebAssembly file (`.wasm`) generated during compilation
## Running the Application
To run the application, you need to serve the compiled files using a web server. You can use Python's built-in HTTP server:
......
......@@ -255,7 +255,7 @@
</div>
<!-- Script to load the WebAssembly module -->
<script>
<!-- <script>
// This script will be replaced by the Emscripten-generated JavaScript
// when the C code is compiled. However, we need to define some functions
// that will be called from the C code.
......@@ -270,7 +270,7 @@
updateCategoryTotals();
}
};
</script>
</script> -->
<!-- Custom JavaScript for the Budget Planner application -->
<script>
......@@ -292,6 +292,32 @@
console.log("updateCategoryTotals function called");
}
</script>
<script src="index.js"></script>
<script>
// var Module = BudgetPlanner(Module);
</script>
<script src="app.js"></script>
<button onclick="Module.ccall('showHelloMessage', null, [], [])">Say Hello</button>
<script>
// Define the Module object with custom configurations
var Module = {
onRuntimeInitialized: function() {
console.log("WebAssembly module initialized");
// Initialize the UI
updateExpenseTable();
updateTotalExpenses(0);
updateCategoryTotals();
}
};
// Instantiate the WebAssembly module
BudgetPlanner(Module).then(function(initializedModule) {
console.log("Module is ready:", initializedModule);
// Optionally, assign the initialized module back to `Module`
Module = initializedModule;
}).catch(function(error) {
console.error("Failed to initialize the WebAssembly module:", error);
});
</script>
</body>
</html>
<script src="app.js.script"></script>
This diff is collapsed.
......@@ -30,7 +30,7 @@
/**
* Maximum length for string fields in the expense entry
*/
#define MAX_STRING_LENGTH 100
#define MAX_STRING_LENGTH 1000
/**
* Maximum number of different expense categories
......@@ -40,6 +40,8 @@
// Forward declarations of functions
void updateCategoryTotals();
void updateUI();
void saveToLocalStorage();
void loadFromLocalStorage();
/**
* Structure to represent an individual expense entry
......@@ -118,6 +120,9 @@ int addExpense(const char* date, const char* category, double amount, const char
// Update the UI
updateUI();
// Save data
saveToLocalStorage();
return 1; // Success
}
......@@ -147,6 +152,9 @@ int deleteExpense(int index) {
// Update the UI
updateUI();
// Save data
saveToLocalStorage();
return 1; // Success
}
......@@ -165,6 +173,9 @@ int clearAllExpenses() {
// Update the UI
updateUI();
// Save data
saveToLocalStorage();
return 1; // Success
}
......@@ -360,6 +371,48 @@ int EMSCRIPTEN_KEEPALIVE jsAddExpense(const char* date, const char* category, do
return addExpense(date, category, amount, description);
}
void saveToLocalStorage() {
// Get all expenses as JSON
char* json = (char*)malloc(MAX_STRING_LENGTH * expenseCount);
if (!json) return;
// Create array of expenses
sprintf(json, "[");
for (int i = 0; i < expenseCount; i++) {
if (i > 0) strcat(json, ",");
char* expense_json = getExpenseJSON(i);
strcat(json, expense_json);
freeMemory(expense_json);
}
strcat(json, "]");
// Call JavaScript to save to localStorage
EM_ASM({
localStorage.setItem('budgetPlanner_expenses', UTF8ToString($0));
}, json);
free(json);
}
void loadFromLocalStorage() {
// Call JavaScript to load data and parse it
EM_ASM({
var stored = localStorage.getItem('budgetPlanner_expenses');
if (stored) {
var expenses = JSON.parse(stored);
Module.ccall('jsClearAllExpenses', 'number', [], []); // Clear existing data
for (var i = 0; i < expenses.length; i++) {
var expense = expenses[i];
Module.ccall('jsAddExpense', 'number',
['string', 'string', 'number', 'string'],
[expense.date, expense.category, expense.amount, expense.description]
);
}
}
});
}
/**
* Function to delete an expense from JavaScript
* This function is exported to JavaScript to delete an expense
......@@ -411,6 +464,16 @@ int EMSCRIPTEN_KEEPALIVE jsGetCategoryCount() {
return getCategoryCount();
}
/**
* Function to display a "Hello" alert message
* This function is exported to JavaScript
*/
void EMSCRIPTEN_KEEPALIVE showHelloMessage() {
EM_ASM({
alert("Hello from C!");
});
}
/**
* Main function - entry point of the application
* In a WebAssembly context, this function is called when the module is loaded
......@@ -424,6 +487,9 @@ int main() {
// Initialize the category count
categoryCount = 0;
// Load saved data on startup
loadFromLocalStorage();
// Update the UI
updateUI();
......
# Compiler and source file
CC=emcc
SOURCES=main.c
# Optimization and linker flags
LDFLAGS=-O3 --llvm-opts 2 --llvm-lto 1 -s ALLOW_MEMORY_GROWTH=1
# Default target
all: build server
build:
$(CC) $(SOURCES) -s WASM=1 -s ASSERTIONS=1 -s MODULARIZE=1 -s EXPORT_NAME="'BudgetPlanner'" \
-s EXPORTED_FUNCTIONS='["_main","_showHelloMessage","_jsAddExpense","_jsDeleteExpense","_jsClearAllExpenses","_jsGetTotalExpenses","_jsGetExpenseCount","_jsGetCategoryCount","_getExpenseJSON","_getCategoryTotalJSON","_freeMemory","_malloc","_free","_saveToLocalStorage","_loadFromLocalStorage"]' \
-s EXPORTED_RUNTIME_METHODS='["ccall", "cwrap", "stringToUTF8", "UTF8ToString"]' \
$(LDFLAGS) -o index.js
server:
python3 -m http.server 8000
# Clean target
clean:
rm -f index.js
rm -f index.wasm
CC=emcc
SOURCES=main.cpp
LDFLAGS=-O3 --llvm-opts 2 --llvm-lto 1
all:
$(CC) $(SOURCES) --bind -s ASSERTIONS=1 -s MODULARIZE=1 -s EXPORT_NAME="'APPCpp'" -s WASM=1 -s AGGRESSIVE_VARIABLE_ELIMINATION=1 -s INLINING_LIMIT=1 -s NO_EXIT_RUNTIME=1 -s EXPORTED_FUNCTIONS="['_malloc','_free']" -s EXTRA_EXPORTED_RUNTIME_METHODS='["ccall", "cwrap"]' -std=c++11 $(LDFLAGS) -o main.wasm.js
$(CC) $(SOURCES) --bind -s ASSERTIONS=1 -s MODULARIZE=1 -s EXPORT_NAME="'APPCpp'" -s AGGRESSIVE_VARIABLE_ELIMINATION=1 -s INLINING_LIMIT=1 -s NO_EXIT_RUNTIME=1 -s EXPORTED_FUNCTIONS="['_malloc','_free']" -s EXTRA_EXPORTED_RUNTIME_METHODS='["ccall", "cwrap"]' -std=c++11 $(LDFLAGS) -o main.asm.js
clean:
rm -f main.wasm.js
rm -f main.asm.js
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment