collabora-online/wasm/base64.hpp

127 lines
4.9 KiB
C++
Raw Normal View History

Make the temporary "WASM app" work Now I hope things are initialised in the right order and the plumbing gets set up so that messages are passed as expected. It seems to work most of the time. Main changes are: - The online WASM executable is built using the -s MODULARIZE -s EXPORT_NAME=createOnlineModule options. This means that the WASM runtime is not automatically initialized and the main() function is not automatically started. Only when the createOnlineModule() function is called is that done. Calling exported C/C++ functions is a little bit more complicated. - Code to actually Base64-encode strings to be executed as JavaScript when expected is now present in wasmapp.cpp. (After being passed through the Base64ToArrayBuffer function on the JS side.) Whether this is actually necessary is not fully clear, but to keep the code similar to that in the GTK, iOS, and Android apps, this is kept as such for now. It would probably work fine to just directly create the ArrayBuffer in the C++ (using the EM_ASM magic). - The COOLWSD::run() function is now run in a separate thread so that main() can return. - The FakeWebSocket's onopen() function is now called from innerMain(), where the HULLO message is sent. It remains a bit unclear if this really is the ideal place. In the mobile apps the HULLO message is sent and the onopen() function is called in the window.socket.onopen() function in global.js. But note that despite that the WASM app and the mobile apps are largely quite similarly constructed and the FakeSocket and FakeWebSocket plumbing is the same, there is an important difference. In a mobile app the C++ code is what runs first, and that then loads the HTML page into WebKit, in which the JS runs. In the WASM app it is the other way around. The web page is naturaly the one that is loaded and the JS code then starts running the C++ code as WASM. Finally, note that the whole concept that there is a separate "WASM app" is temporary. What we eventually want to achieve is that the COOL webpage upon loading will connect a COOL server. As it does currently. The COOL server runs the online and core C++ code to load a document, and renders document tiles and sends those to the client JS code to dispay. The new thing will be that, if enabled, in addition to the HTML and JS resources, the client will also download the WASM code and data resources. Also, the document and updates to it will be downloaded while being edited so that a copy can be kept in client memory. But the WASM code and the downloaded document will remain unused most of the time. Only if the connection to the COOL server breaks will the JS start running the WASM code and the JS will talk to online code running locally as WASM instead of to a COOL server. Obviously there are still lots of things hanging in the air here regarding how exactly this will work. Signed-off-by: Tor Lillqvist <tml@collabora.com> Change-Id: Ib1786a0b485d51797b0f2302d4296aa1ff9df5c1
2023-01-05 19:11:01 +01:00
// Base64 encoder from https://gist.github.com/tomykaira/f0fd86b6c73063283afe550bc5d77594
// Just the first one I found.
#pragma once
/**
* SPDX-License-Identifier: MIT
*
Make the temporary "WASM app" work Now I hope things are initialised in the right order and the plumbing gets set up so that messages are passed as expected. It seems to work most of the time. Main changes are: - The online WASM executable is built using the -s MODULARIZE -s EXPORT_NAME=createOnlineModule options. This means that the WASM runtime is not automatically initialized and the main() function is not automatically started. Only when the createOnlineModule() function is called is that done. Calling exported C/C++ functions is a little bit more complicated. - Code to actually Base64-encode strings to be executed as JavaScript when expected is now present in wasmapp.cpp. (After being passed through the Base64ToArrayBuffer function on the JS side.) Whether this is actually necessary is not fully clear, but to keep the code similar to that in the GTK, iOS, and Android apps, this is kept as such for now. It would probably work fine to just directly create the ArrayBuffer in the C++ (using the EM_ASM magic). - The COOLWSD::run() function is now run in a separate thread so that main() can return. - The FakeWebSocket's onopen() function is now called from innerMain(), where the HULLO message is sent. It remains a bit unclear if this really is the ideal place. In the mobile apps the HULLO message is sent and the onopen() function is called in the window.socket.onopen() function in global.js. But note that despite that the WASM app and the mobile apps are largely quite similarly constructed and the FakeSocket and FakeWebSocket plumbing is the same, there is an important difference. In a mobile app the C++ code is what runs first, and that then loads the HTML page into WebKit, in which the JS runs. In the WASM app it is the other way around. The web page is naturaly the one that is loaded and the JS code then starts running the C++ code as WASM. Finally, note that the whole concept that there is a separate "WASM app" is temporary. What we eventually want to achieve is that the COOL webpage upon loading will connect a COOL server. As it does currently. The COOL server runs the online and core C++ code to load a document, and renders document tiles and sends those to the client JS code to dispay. The new thing will be that, if enabled, in addition to the HTML and JS resources, the client will also download the WASM code and data resources. Also, the document and updates to it will be downloaded while being edited so that a copy can be kept in client memory. But the WASM code and the downloaded document will remain unused most of the time. Only if the connection to the COOL server breaks will the JS start running the WASM code and the JS will talk to online code running locally as WASM instead of to a COOL server. Obviously there are still lots of things hanging in the air here regarding how exactly this will work. Signed-off-by: Tor Lillqvist <tml@collabora.com> Change-Id: Ib1786a0b485d51797b0f2302d4296aa1ff9df5c1
2023-01-05 19:11:01 +01:00
* The MIT License (MIT)
* Copyright (c) 2016 tomykaira
*
* 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>
namespace macaron {
class Base64 {
public:
static std::string Encode(const std::string data) {
static constexpr char sEncodingTable[] = {
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
'w', 'x', 'y', 'z', '0', '1', '2', '3',
'4', '5', '6', '7', '8', '9', '+', '/'
};
size_t in_len = data.size();
size_t out_len = 4 * ((in_len + 2) / 3);
std::string ret(out_len, '\0');
size_t i;
char *p = const_cast<char*>(ret.c_str());
for (i = 0; i < in_len - 2; i += 3) {
*p++ = sEncodingTable[(data[i] >> 2) & 0x3F];
*p++ = sEncodingTable[((data[i] & 0x3) << 4) | ((int) (data[i + 1] & 0xF0) >> 4)];
*p++ = sEncodingTable[((data[i + 1] & 0xF) << 2) | ((int) (data[i + 2] & 0xC0) >> 6)];
*p++ = sEncodingTable[data[i + 2] & 0x3F];
}
if (i < in_len) {
*p++ = sEncodingTable[(data[i] >> 2) & 0x3F];
if (i == (in_len - 1)) {
*p++ = sEncodingTable[((data[i] & 0x3) << 4)];
*p++ = '=';
}
else {
*p++ = sEncodingTable[((data[i] & 0x3) << 4) | ((int) (data[i + 1] & 0xF0) >> 4)];
*p++ = sEncodingTable[((data[i + 1] & 0xF) << 2)];
}
*p++ = '=';
}
return ret;
}
static std::string Decode(const std::string& input, std::string& out) {
static constexpr unsigned char kDecodingTable[] = {
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 62, 64, 64, 64, 63,
52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 64, 64, 64, 64, 64, 64,
64, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 64, 64, 64, 64, 64,
64, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 64, 64, 64, 64, 64,
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64
};
size_t in_len = input.size();
if (in_len % 4 != 0) return "Input data size is not a multiple of 4";
size_t out_len = in_len / 4 * 3;
if (input[in_len - 1] == '=') out_len--;
if (input[in_len - 2] == '=') out_len--;
out.resize(out_len);
for (size_t i = 0, j = 0; i < in_len;) {
uint32_t a = input[i] == '=' ? 0 & i++ : kDecodingTable[static_cast<int>(input[i++])];
uint32_t b = input[i] == '=' ? 0 & i++ : kDecodingTable[static_cast<int>(input[i++])];
uint32_t c = input[i] == '=' ? 0 & i++ : kDecodingTable[static_cast<int>(input[i++])];
uint32_t d = input[i] == '=' ? 0 & i++ : kDecodingTable[static_cast<int>(input[i++])];
uint32_t triple = (a << 3 * 6) + (b << 2 * 6) + (c << 1 * 6) + (d << 0 * 6);
if (j < out_len) out[j++] = (triple >> 2 * 8) & 0xFF;
if (j < out_len) out[j++] = (triple >> 1 * 8) & 0xFF;
if (j < out_len) out[j++] = (triple >> 0 * 8) & 0xFF;
}
return "";
}
};
}